zotero/app/build.sh
Dan Stillman ce20a5f228 Ship legacy Safari App Extension alongside the web extension on Mac
Developer ID-signed Safari web extensions load only in Safari 18.4 and
later -- older Safari blocks them at the code-signing layer -- so macOS
11 and 12 users, whose Safari versions top out at 16.6 and 17.6, lost
the connector with the web extension conversion.

Embed the prebuilt legacy App Extension ($SAFARI_APP_EXTENSION, set by
the deploy scripts) at Contents/PlugIns/ZoteroSafariExtension.appex with
the historical bundle identifier, and move the web extension to
Contents/PlugIns/ZoteroSafariWebExtension.appex with a new
.SafariWebExtension identifier. The web extension's
SFSafariAppExtensionBundleIdentifiersToReplace key causes Safari
versions that can load it to hide the App Extension and migrate its
enabled state, while older Safari shows only the App Extension.

Also remove the removed-files entries for the App Extension's
resources, which would otherwise delete them on update.

https://forums.zotero.org/discussion/132925/
2026-07-28 07:24:27 -04:00

1185 lines
43 KiB
Bash
Executable file

#!/bin/bash -e
# Copyright (c) 2011 Zotero
# Center for History and New Media
# George Mason University, Fairfax, Virginia, USA
# http://zotero.org
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
CALLDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
. "$CALLDIR/config.sh"
. "$CALLDIR/scripts/utils.sh"
if [ "`uname`" = "Darwin" ]; then
MAC_NATIVE=1
else
MAC_NATIVE=0
fi
if [ "`uname -o 2> /dev/null`" = "Cygwin" ]; then
WIN_NATIVE=1
else
WIN_NATIVE=0
fi
function usage {
cat >&2 <<DONE
Usage: $0 [-d DIR] [-f FILE] -p PLATFORMS [-a ARCH] [-c CHANNEL] [options]
Options
-d DIR build directory to build from (from build_xpi; cannot be used with -f)
-f FILE ZIP file to build from (cannot be used with -d)
-t add devtools
-p PLATFORMS build for platforms PLATFORMS (m=Mac, w=Windows, l=Linux)
-a ARCH architecture to build (arm64, x64, i686, win32)
* Ignored for Mac (always universal)
* If omitted on Windows/Linux, all standard archs are built
-c CHANNEL use update channel CHANNEL
-e enforce signing
-s don't package; only build binaries in staging/ directory
-q quick build (skip compression and other optional steps for faster restarts during development)
DONE
exit 1
}
BUILD_DIR=`mktemp -d`
function cleanup {
rm -rf $BUILD_DIR
}
trap cleanup EXIT
function abspath {
echo $(cd $(dirname $1); pwd)/$(basename $1);
}
function check_lfs_file {
if [ "$(head --bytes 5 "$1")" = "versi" ]; then
echo "$1 not checked out -- install Git LFS and run 'git lfs pull'" >&2
exit 1
fi
}
SOURCE_DIR=""
ZIP_FILE=""
BUILD_MAC=0
BUILD_WIN=0
BUILD_LINUX=0
PACKAGE=1
DEVTOOLS=0
quick_build=0
arch=""
while getopts "d:f:p:a:c:tseq" opt; do
case $opt in
d)
SOURCE_DIR="$OPTARG"
;;
f)
ZIP_FILE="$OPTARG"
;;
p)
for (( i=0; i<${#OPTARG}; i++ )); do
case ${OPTARG:i:1} in
m) BUILD_MAC=1 ;;
w) BUILD_WIN=1 ;;
l) BUILD_LINUX=1 ;;
*)
echo "$0: Invalid platform option ${OPTARG:i:1}" >&2
usage
;;
esac
done
;;
a)
arch="$OPTARG"
;;
c)
UPDATE_CHANNEL="$OPTARG"
;;
t)
DEVTOOLS=1
;;
e)
SIGN=1
;;
s)
PACKAGE=0
;;
q)
quick_build=1
;;
*)
usage
;;
esac
shift $((OPTIND-1)); OPTIND=1
done
xul_hash_file() {
local _platform=$1
local _arch=$2
case $_platform in
m)
echo "hash-mac";;
w)
[[ $_arch == x64 ]] && _arch="win-x64"
[[ $_arch == arm64 ]] && _arch="win-arm64"
echo "hash-${_arch}";;
l)
[[ $_arch == x64 ]] && _arch="x86_64"
echo "hash-linux-${_arch}";;
*)
echo "Invalid platform '$_platform'" >&2
exit 1
esac
}
function check_xulrunner_hash {
local _platform=$1
local _arch=$2
local platform_hash_file=$(xul_hash_file "$_platform" "${_arch:-universal}")
# TEMP: If previous Windows hash file without architecture is present, force a refresh,
# because the Firefox folder might have been changed without the new hash file being
# updated, resulting in a broken build (e.g, fx140 using fx128). We'll want to keep this
# as long as we're switching back to <=fx128. This isn't an issue on macOS because there's
# no architecture and isn't an issue on Linux because the runtime folder names changed.
if [[ $_platform = 'w' ]] && [[ -e "$CALLDIR/xulrunner/hash-win" ]]; then
echo "Found old hash-win -- forcing redownload"
rm "$CALLDIR"/xulrunner/hash-win*
fi
if [ ! -e "$CALLDIR/xulrunner/$platform_hash_file" ]; then
echo "$platform_hash_file not found -- downloading"
echo
$CALLDIR/scripts/fetch_xulrunner -p $_platform ${_arch:+-a $_arch}
else
recalculated_xulrunner_hash=$("$CALLDIR/scripts/xulrunner_hash" -p $_platform ${_arch:+-a $_arch})
current_xulrunner_hash=$(< "$CALLDIR/xulrunner/$platform_hash_file")
if [ "$current_xulrunner_hash" != "$recalculated_xulrunner_hash" ]; then
echo "xulrunner hashes don't match -- redownloading"
echo
"$CALLDIR/scripts/fetch_xulrunner" -p $_platform ${_arch:+-a $_arch}
current_xulrunner_hash=$(< "$CALLDIR/xulrunner/$platform_hash_file")
if [ "$current_xulrunner_hash" != "$recalculated_xulrunner_hash" ]; then
echo "xulrunner hashes don't match after running fetch_xulrunner!"
exit 1
fi
fi
fi
}
# Derive platform flags from host if none provided
if [[ $BUILD_MAC == 0 ]] && [[ $BUILD_WIN == 0 ]] && [[ $BUILD_LINUX == 0 ]]; then
case "$(uname -s)" in
Darwin) BUILD_MAC=1 ;;
Linux) BUILD_LINUX=1 ;;
CYGWIN*|MINGW*) BUILD_WIN=1 ;;
esac
fi
# Check xulrunner hashes for requested builds
if [ $BUILD_MAC == 1 ]; then
check_xulrunner_hash m
fi
if [ $BUILD_WIN == 1 ]; then
if [[ -n $arch ]]; then
check_xulrunner_hash w $(get_canonical_arch w $arch)
else
for _a in win-x64 win-arm64 win32; do
check_xulrunner_hash w "$_a"
done
fi
fi
if [ $BUILD_LINUX == 1 ]; then
if [[ -n $arch ]]; then
check_xulrunner_hash l $(get_canonical_arch l $arch)
else
for _a in x64 arm64 i686; do
check_xulrunner_hash l "$_a"
done
fi
fi
# Require source dir or ZIP file
if [[ -z "$SOURCE_DIR" ]] && [[ -z "$ZIP_FILE" ]]; then
usage
elif [[ -n "$SOURCE_DIR" ]] && [[ -n "$ZIP_FILE" ]]; then
usage
fi
# Require at least one platform
if [[ $BUILD_MAC == 0 ]] && [[ $BUILD_WIN == 0 ]] && [[ $BUILD_LINUX == 0 ]]; then
usage
fi
if [[ -z "${ZOTERO_TEST:-}" ]] || [[ $ZOTERO_TEST == "0" ]]; then
include_tests=0
else
include_tests=1
fi
# Bundle devtools with dev builds
if [ "$UPDATE_CHANNEL" == "beta" ] || [ "$UPDATE_CHANNEL" == "dev" ] || [ "$UPDATE_CHANNEL" == "test" ]; then
DEVTOOLS=1
fi
if [ -z "$UPDATE_CHANNEL" ]; then UPDATE_CHANNEL="default"; fi
BUILD_ID=`date +%Y%m%d%H%M%S`
# Paths to Gecko runtimes
MAC_RUNTIME_PATH="$CALLDIR/xulrunner/Firefox.app"
WIN_RUNTIME_PATH_PREFIX="$CALLDIR/xulrunner/firefox-"
LINUX_RUNTIME_PATH_PREFIX="$CALLDIR/xulrunner/firefox-linux-"
base_dir="$BUILD_DIR/base"
app_dir="$BUILD_DIR/base/app"
omni_dir="$BUILD_DIR/base/app/omni"
shopt -s extglob
mkdir -p "$app_dir"
rm -rf "$STAGE_DIR"
mkdir "$STAGE_DIR"
rm -rf "$DIST_DIR"
mkdir "$DIST_DIR"
# Save build id, which is needed for updates manifest
echo $BUILD_ID > "$DIST_DIR/build_id"
cd "$app_dir"
# Copy 'browser' files from Firefox
#
# omni.ja is left uncompressed within the Firefox application files by fetch_xulrunner
#
# TEMP: Also extract .hyf hyphenation files from the outer (still compressed) omni.ja
# This works around https://bugzilla.mozilla.org/show_bug.cgi?id=1772900
set +e
if [ $BUILD_MAC == 1 ]; then
cp -Rp "$MAC_RUNTIME_PATH"/Contents/Resources/browser/omni "$app_dir"
unzip -qj "$MAC_RUNTIME_PATH"/Contents/Resources/omni.ja "hyphenation/*" -d "$app_dir"/hyphenation/
elif [[ $BUILD_WIN == 1 ]]; then
# These are non-arch-specific files, so just find a runtime we fetched:
# - If the caller specified -a, that arch is guaranteed present.
# - Otherwise prefer win-x64 → win-arm64 → win32, falling back to the first
# directory that exists under xulrunner/.
if [[ -n $arch ]]; then
omni_arch=$(get_canonical_arch w $arch)
else
for cand in win-x64 win-arm64 win32; do
[[ -d "${WIN_RUNTIME_PATH_PREFIX}${cand}" ]] && { omni_arch="$cand"; break; }
done
fi
cp -Rp "${WIN_RUNTIME_PATH_PREFIX}${omni_arch}"/browser/omni "$app_dir"
unzip -qj "${WIN_RUNTIME_PATH_PREFIX}${omni_arch}"/omni.ja "hyphenation/*" \
-d "$app_dir"/hyphenation/
elif [[ $BUILD_LINUX == 1 ]]; then
# Same as above
if [[ -n $arch ]]; then
omni_arch=$(get_canonical_arch l $arch)
else
for cand in x86_64 arm64 i686; do
[[ -d "${LINUX_RUNTIME_PATH_PREFIX}${cand}" ]] && { omni_arch="$cand"; break; }
done
fi
cp -Rp "${LINUX_RUNTIME_PATH_PREFIX}${omni_arch}"/browser/omni "$app_dir"
unzip -qj "${LINUX_RUNTIME_PATH_PREFIX}${omni_arch}"/omni.ja "hyphenation/*" \
-d "$app_dir"/hyphenation/
fi
set -e
cd $omni_dir
# Remove Firefox files that we don't need
rm actors/AboutLogins{Parent,Child}.sys.mjs
rm actors/AboutMessagePreview{Parent,Child}.sys.mjs
rm actors/AboutNewTab{Parent,Child}.sys.mjs
rm actors/AboutPocket{Parent,Child}.sys.mjs
rm actors/AboutPrivateBrowsing{Parent,Child}.sys.mjs
rm actors/AboutProtections{Parent,Child}.sys.mjs
rm actors/AboutReader{Parent,Child}.sys.mjs
rm actors/AboutTabCrashed{Parent,Child}.sys.mjs
rm actors/AboutWelcome{Parent,Child}.sys.mjs
rm actors/ASRouter{Parent,Child}.sys.mjs
rm actors/BackupUI{Parent,Child}.sys.mjs
rm actors/BlockedSite{Parent,Child}.sys.mjs
rm actors/BrowserProcessChild.sys.mjs
rm actors/BrowserTabChild.sys.mjs
rm actors/ClickHandler{Parent,Child}.sys.mjs
rm actors/ContentSearch{Parent,Child}.sys.mjs
rm actors/DecoderDoctor{Parent,Child}.sys.mjs
rm actors/DOMFullscreen{Parent,Child}.sys.mjs
rm actors/EncryptedMedia{Parent,Child}.sys.mjs
rm actors/FormValidation{Parent,Child}.sys.mjs
rm actors/LinkHandler{Parent,Child}.sys.mjs
rm actors/MigrationWizard{Parent,Child}.sys.mjs
rm actors/PageData{Parent,Child}.sys.mjs # Not our PageData actor
rm actors/PageInfoChild.sys.mjs
rm actors/PageStyle{Parent,Child}.sys.mjs
rm actors/Plugin{Parent,Child}.sys.mjs # GMP plugins, which we remove
rm actors/PointerLock{Parent,Child}.sys.mjs
#rm actors/RFPHelper{Parent,Child}.sys.mjs
rm actors/ScreenshotsComponentChild.sys.mjs
#rm actors/SearchSERPTelemetry{Parent,Child}.sys.mjs
#rm actors/ShoppingSidebar{Parent,Child}.sys.mjs
rm actors/SpeechDispatcher{Parent,Child}.sys.mjs
rm actors/WebRTC{Parent,Child}.sys.mjs
# Keep some essential files from chrome/browser/
mkdir chrome/browser-fx
browser_keep=(
content/browser/license.html
# For web-ext
content/browser/parent/ext-browser.js
# For spellchecking
content/browser/built_in_addons.json
)
if [ $BUILD_WIN == 1 ]; then
# Windows window controls
browser_keep+=(skin/classic/browser/window-controls)
fi
for file in "${browser_keep[@]}"; do
mkdir -p "$(dirname "chrome/browser-fx/$file")"
mv "chrome/browser/$file" "chrome/browser-fx/$file"
done
# Delete everything else
rm -r chrome/browser
# Move essential files back
mv chrome/browser-fx chrome/browser
#rm modules/SearchSERPTelemetry.sys.mjs
# Move some Firefox files that would be overwritten out of the way
mv chrome.manifest chrome.manifest-fx
mv defaults defaults-fx
# Extract Zotero files
if [ -n "$ZIP_FILE" ]; then
ZIP_FILE="`abspath $ZIP_FILE`"
echo "Building from $ZIP_FILE"
unzip -q $ZIP_FILE -d "$omni_dir"
else
rsync_params=""
if [ $include_tests -eq 0 ]; then
rsync_params="--exclude /test"
fi
rsync -a $rsync_params "$SOURCE_DIR/" ./
fi
mv defaults defaults-z
mv defaults-fx defaults
prefs_file=defaults/preferences/zotero.js
# Transfer Firefox prefs, omitting some with undesirable overrides from the base prefs
#
# - network.captive-portal-service.enabled
# Disable the captive portal check against Mozilla servers
# - extensions.systemAddon.update.url
grep -E -v '(network.captive-portal-service.enabled|extensions.systemAddon.update.url)' defaults/preferences/firefox.js > $prefs_file
rm defaults/preferences/firefox.js
# Combine app and "extension" Zotero prefs
echo "" >> $prefs_file
echo "#" >> $prefs_file
echo "# Zotero app prefs" >> $prefs_file
echo "#" >> $prefs_file
echo "" >> $prefs_file
cat "$CALLDIR/assets/prefs.js" >> $prefs_file
echo "" >> $prefs_file
echo "# Zotero extension prefs" >> $prefs_file
echo "" >> $prefs_file
cat defaults-z/preferences/zotero.js >> $prefs_file
rm -rf defaults-z
# Platform-specific prefs
if [ $BUILD_MAC == 1 ]; then
perl -pi -e 's/pref\("browser\.preferences\.instantApply", false\);/pref\("browser\.preferences\.instantApply", true);/' $prefs_file
perl -pi -e 's/%GECKO_VERSION%/'"$GECKO_VERSION_MAC"'/g' $prefs_file
# Fix horizontal mousewheel scrolling (this is set to 4 in the Fx60 .app greprefs.js, but
# defaults to 1 in later versions of Firefox, and needs to be 1 to work on macOS)
echo 'pref("mousewheel.with_shift.action", 1);' >> $prefs_file
elif [ $BUILD_WIN == 1 ]; then
perl -pi -e 's/%GECKO_VERSION%/'"$GECKO_VERSION_WIN"'/g' $prefs_file
elif [ $BUILD_LINUX == 1 ]; then
# Modify platform-specific prefs
perl -pi -e 's/pref\("browser\.preferences\.instantApply", false\);/pref\("browser\.preferences\.instantApply", true);/' $prefs_file
perl -pi -e 's/%GECKO_VERSION%/'"$GECKO_VERSION_LINUX"'/g' $prefs_file
# Auto-scroll is disabled by default on Linux
# https://bugzilla.mozilla.org/show_bug.cgi?id=1747208
perl -pi -e 's/pref\("general\.autoScroll", false\);/pref\("general.autoScroll", true);/' $prefs_file
fi
# Clear list of built-in add-ons, but leave the en-US dictionary
echo '{"dictionaries": {"en-US": "dictionaries/en-US.dic"}, "system": []}' > chrome/browser/content/browser/built_in_addons.json
# chrome.manifest
mv chrome.manifest zotero.manifest
mv chrome.manifest-fx chrome.manifest
# TEMP
#echo "manifest zotero.manifest" >> "$base_dir/chrome.manifest"
cat zotero.manifest >> chrome.manifest
rm zotero.manifest
# Update channel
perl -pi -e 's/pref\("app\.update\.channel", "[^"]*"\);/pref\("app\.update\.channel", "'"$UPDATE_CHANNEL"'");/' $prefs_file
echo -n "Channel: "
grep app.update.channel $prefs_file
echo
# Add devtools prefs
if [ $DEVTOOLS -eq 1 ]; then
echo >> $prefs_file
echo "// Dev Tools" >> $prefs_file
echo 'pref("devtools.debugger.remote-enabled", true);' >> $prefs_file
if [ $UPDATE_CHANNEL != "beta" ]; then
echo 'pref("devtools.debugger.prompt-connection", false);' >> $prefs_file
fi
# Use 'zotero' instead of 'zotero-bin' when passing -jsdebugger flag on Linux, since we need
# that to pass application.ini
replace_line 'let command = Services.dirsvc.get\("XREExeF", Ci.nsIFile\).path;' \
'let command = Services.dirsvc.get("XREExeF", Ci.nsIFile).path; command = command.replace("zotero-bin", "zotero");' \
chrome/devtools/modules/devtools/client/framework/browser-toolbox/Launcher.sys.mjs
# Fix source display breaking when switching windows in the Browser Toolbox.
# When the user switches which chrome window is being inspected (via the frame
# picker), the thread actor's _onWindowReady handler clears all debuggees and
# source actors, then re-adds debuggees. But it never calls addAllSources() to
# re-emit the existing scripts — it only does that for BFCache navigations.
# Since the scripts already exist (they're not new), onNewScript won't fire,
# so the debugger's source list stays empty. Fix: also call addAllSources()
# after frame switching, just like BFCache.
replace_line '_onWindowReady\(\{ isTopLevel, isBFCache \}\)' \
'_onWindowReady({ isTopLevel, isBFCache, isFrameSwitching })' \
chrome/devtools/modules/devtools/server/actors/thread.js
replace_line 'if \(isBFCache\) {' \
'if (isBFCache || isFrameSwitching) {' \
chrome/devtools/modules/devtools/server/actors/thread.js
# Suppress "Failed to get the active browserId" error when profile recording starts
replace_line 'console\.error\(' \
'if (false) console.error(' \
chrome/devtools/modules/devtools/shared/performance-new/recording-utils.sys.mjs
# Stub out profile favicon lookup since we don't have PlacesUtils
replace_line 'return getPageFavicons\(pageUrls\);' \
'return pageUrls ? pageUrls.map(() => null) : [];' \
chrome/devtools/modules/devtools/client/performance-new/shared/background.sys.mjs
# Use our profilerViewer.xhtml to display profiles.
# We can't use basicViewer because that loads Zotero scripts, and profiling
# runs in the Browser Toolbox child process, where a second Zotero instance
# would collide with the locked database.
replace_line 'const contentBrowser = await new Promise\(resolveOnContentBrowserCreated =>' \
'if (typeof win.openWebLinkIn !== "function") {
const ww = Cc["\@mozilla.org\/embedcomp\/window-watcher;1"].getService(
Ci.nsIWindowWatcher
);
const arg = { uri: urlToLoad };
arg.wrappedJSObject = arg;
const viewerWin = ww.openWindow(
null,
"chrome:\/\/zotero\/content\/standalone\/profilerViewer.xhtml",
null,
"chrome,dialog=no,resizable,centerscreen,scrollbars",
arg
);
return await new Promise(resolve => {
viewerWin.addEventListener("load", () => {
resolve(viewerWin.document.getElementById("content"));
}, { once: true });
});
}
const contentBrowser = await new Promise(resolveOnContentBrowserCreated =>' \
chrome/devtools/modules/devtools/client/performance-new/shared/browser.js
fi
# 5.0.96.3 / 5.0.97-beta.37+ddc7be75c
VERSION=`cat version`
# 5.0.96 / 5.0.97
VERSION_NUMERIC=`perl -ne 'print and last if s/^(\d+\.\d+(\.\d+)?).*/\1/;' version`
if [ -z "$VERSION" ]; then
echo "Version number not found in version file"
exit 1
fi
rm version
echo
echo "Version: $VERSION"
# Delete Mozilla signing info if present
rm -rf META-INF
# Copy branding
cp -R "$CALLDIR"/assets/branding/locale/brand.{dtd,properties} chrome/en-US/locale/branding/
# Copy browser localization .ftl files
for locale in `ls chrome/locale/`; do
mkdir -p localization/$locale/branding
cp "$CALLDIR/assets/branding/locale/brand.ftl" localization/$locale/branding/brand.ftl
mkdir -p localization/$locale/toolkit/global
cp chrome/locale/$locale/zotero/mozilla/arrowscrollbox.ftl localization/$locale/toolkit/global
cp chrome/locale/$locale/zotero/mozilla/textActions.ftl localization/$locale/toolkit/global
cp chrome/locale/$locale/zotero/mozilla/wizard.ftl localization/$locale/toolkit/global
mkdir -p localization/$locale/browser
cp chrome/locale/$locale/zotero/mozilla/browserSets.ftl localization/$locale/browser
cp chrome/locale/$locale/zotero/mozilla/menubar.ftl localization/$locale/browser
mkdir -p localization/$locale/devtools/client
cp chrome/locale/$locale/zotero/mozilla/toolbox.ftl localization/$locale/devtools/client
# TEMP: Until we've created zotero.ftl in all locales
touch chrome/locale/$locale/zotero/zotero.ftl
cp chrome/locale/$locale/zotero/*.ftl localization/$locale/
done
# Add to chrome manifest
echo "" >> chrome.manifest
cat "$CALLDIR/assets/chrome.manifest" >> chrome.manifest
replace_line 'handle: function bch_handle\(cmdLine\) {' 'handle: function bch_handle(cmdLine) {
\/\/ TEST_OPTIONS_PLACEHOLDER
' modules/BrowserContentHandler.sys.mjs
export CALLDIR && perl -pi -e 'BEGIN { local $/; open $fh, "$ENV{CALLDIR}/assets/commandLineHandler.js"; $replacement = <$fh>; close $fh; } s/\/\/ TEST_OPTIONS_PLACEHOLDER/$replacement/' modules/BrowserContentHandler.sys.mjs
# Don't try to initialize Places, since we've removed its files
#replace_line 'this._placesInitialized = true;' 'if (true) return; this._placesInitialized = true;' components/places/PlacesBrowserStartup.sys.mjs
# Move test files to root directory
if [ $include_tests -eq 1 ]; then
cat test/chrome.manifest >> chrome.manifest
rm test/chrome.manifest
cp -R test/tests "$base_dir/tests"
fi
# Copy platform-specific assets
if [ $BUILD_MAC == 1 ]; then
rsync -a "$CALLDIR/assets/mac/" ./
elif [ $BUILD_WIN == 1 ]; then
rsync -a "$CALLDIR/assets/win/" ./
elif [ $BUILD_LINUX == 1 ]; then
rsync -a "$CALLDIR/assets/unix/" ./
fi
# Add word processor plug-ins
echo >> chrome.manifest
if [ $BUILD_MAC == 1 ]; then
pluginDir="$CALLDIR/modules/zotero-word-for-mac-integration"
mkdir -p "integration/word-for-mac"
cp -RH "$pluginDir/components" \
"$pluginDir/resource" \
"$pluginDir/chrome.manifest" \
"integration/word-for-mac"
echo -n "Word for Mac plugin version: "
cat "integration/word-for-mac/resource/version.txt"
echo
echo >> $prefs_file
cat "$CALLDIR/modules/zotero-word-for-mac-integration/defaults/preferences/zoteroMacWordIntegration.js" >> $prefs_file
echo >> $prefs_file
echo "manifest integration/word-for-mac/chrome.manifest" >> chrome.manifest
elif [ $BUILD_WIN == 1 ]; then
pluginDir="$CALLDIR/modules/zotero-word-for-windows-integration"
mkdir -p "integration/word-for-windows"
cp -RH "$pluginDir/components" \
"$pluginDir/resource" \
"$pluginDir/chrome.manifest" \
"integration/word-for-windows"
echo -n "Word for Windows plugin version: "
cat "integration/word-for-windows/resource/version.txt"
echo
echo >> $prefs_file
cat "$CALLDIR/modules/zotero-word-for-windows-integration/defaults/preferences/zoteroWinWordIntegration.js" >> $prefs_file
echo >> $prefs_file
echo "manifest integration/word-for-windows/chrome.manifest" >> chrome.manifest
fi
# Libreoffice plugin for all platforms
pluginDir="$CALLDIR/modules/zotero-libreoffice-integration"
mkdir -p "integration/libreoffice"
cp -RH "$pluginDir/chrome" \
"$pluginDir/components" \
"$pluginDir/resource" \
"$pluginDir/chrome.manifest" \
"integration/libreoffice"
echo -n "LibreOffice plugin version: "
cat "integration/libreoffice/resource/version.txt"
echo
echo >> $prefs_file
cat "$CALLDIR/modules/zotero-libreoffice-integration/defaults/preferences/zoteroLibreOfficeIntegration.js" >> $prefs_file
echo >> $prefs_file
echo "manifest integration/libreoffice/chrome.manifest" >> chrome.manifest
# Delete files that shouldn't be distributed
find chrome -name .DS_Store -exec rm -f {} \;
# Zip browser and Zotero files into omni.ja
if [ $quick_build -eq 1 ]; then
# If quick build, don't compress or optimize
zip -qrXD omni.ja *
else
zip -qr9XD omni.ja *
python3 "$CALLDIR/scripts/optimizejars.py" --optimize ./ ./ ./
fi
mv omni.ja ..
cd "$CALLDIR"
rm -rf "$omni_dir"
# Copy updater.ini
cp "$CALLDIR/assets/updater.ini" "$base_dir"
# Adjust chrome.manifest
#perl -pi -e 's^(chrome|resource)/^jar:zotero.jar\!/$1/^g' "$BUILD_DIR/zotero/chrome.manifest"
# Copy application.ini and modify
cp "$CALLDIR/assets/application.ini" "$app_dir/application.ini"
perl -pi -e "s/\{\{VERSION}}/$VERSION/" "$app_dir/application.ini"
perl -pi -e "s/\{\{BUILDID}}/$BUILD_ID/" "$app_dir/application.ini"
# Remove unnecessary files
find "$BUILD_DIR" -name .DS_Store -exec rm -f {} \;
# Mac
if [ $BUILD_MAC == 1 ]; then
echo 'Building Zotero.app'
# Set up directory structure
APPDIR="$STAGE_DIR/Zotero.app"
rm -rf "$APPDIR"
mkdir "$APPDIR"
chmod 755 "$APPDIR"
cp -r "$CALLDIR/mac/Contents" "$APPDIR"
CONTENTSDIR="$APPDIR/Contents"
# Merge relevant assets from Firefox
mkdir "$CONTENTSDIR/MacOS"
cp -r "$MAC_RUNTIME_PATH/Contents/MacOS/"!(firefox|firefox-bin|crashreporter.app|minidump-analyzer|nmhproxy|pingsender|updater.app) "$CONTENTSDIR/MacOS"
cp -r "$MAC_RUNTIME_PATH/Contents/Resources/"!(application.ini|browser|defaults|precomplete|removed-files|updater.ini|update-settings.ini|webapprt*|*.icns|*.lproj) "$CONTENTSDIR/Resources"
# Add our custom ChannelPrefs.framework and change channel if not a source build
mkdir "$CONTENTSDIR/Frameworks"
tar xvf "$CALLDIR/mac/ChannelPrefs.framework.tar.xz" -C "$CONTENTSDIR/Frameworks"
if [ "$UPDATE_CHANNEL" != "source" ]; then
"$CALLDIR/mac/set-channel-prefs-channel" "$CONTENTSDIR/Frameworks/ChannelPrefs.framework/ChannelPrefs" source $UPDATE_CHANNEL
fi
# Use our own launcher
check_lfs_file "$CALLDIR/mac/zotero.xz"
xz -d --stdout "$CALLDIR/mac/zotero.xz" > "$CONTENTSDIR/MacOS/zotero"
chmod 755 "$CONTENTSDIR/MacOS/zotero"
# TEMP: Custom version of XUL with some backported Mozilla bug fixes
if [ -n "$custom_components_hash_mac" ]; then
cp "$MAC_RUNTIME_PATH/../MacOS/XUL" "$CONTENTSDIR/MacOS/"
fi
# Use our own updater, because Mozilla's requires updates signed by Mozilla
cd "$CONTENTSDIR/MacOS"
check_lfs_file "$CALLDIR/mac/updater.tar.xz"
tar xf "$CALLDIR/mac/updater.tar.xz"
# Modify Info.plist
perl -pi -e "s/\{\{VERSION\}\}/$VERSION/" "$CONTENTSDIR/Info.plist"
perl -pi -e "s/\{\{VERSION_NUMERIC\}\}/$VERSION_NUMERIC/" "$CONTENTSDIR/Info.plist"
if [ $UPDATE_CHANNEL == "beta" ] || [ $UPDATE_CHANNEL == "dev" ] || [ $UPDATE_CHANNEL == "source" ]; then
perl -pi -e "s/org\.zotero\.zotero/org.zotero.zotero-$UPDATE_CHANNEL/" "$CONTENTSDIR/Info.plist"
fi
perl -pi -e "s/\{\{VERSION\}\}/$VERSION/" "$CONTENTSDIR/Info.plist"
# Needed for "monkeypatch" Windows builds:
# http://www.nntp.perl.org/group/perl.perl5.porters/2010/08/msg162834.html
rm -f "$CONTENTSDIR/Info.plist.bak"
echo
grep -B 1 org.zotero.zotero "$CONTENTSDIR/Info.plist"
echo
grep -A 1 CFBundleShortVersionString "$CONTENTSDIR/Info.plist"
echo
grep -A 1 CFBundleVersion "$CONTENTSDIR/Info.plist"
echo
# Copy app files
rsync -a "$base_dir/" "$CONTENTSDIR/Resources/"
# Add word processor plug-ins
mkdir "$CONTENTSDIR/Resources/integration"
cp -RH "$CALLDIR/modules/zotero-libreoffice-integration/install" "$CONTENTSDIR/Resources/integration/libreoffice"
cp -RH "$CALLDIR/modules/zotero-word-for-mac-integration/install" "$CONTENTSDIR/Resources/integration/word-for-mac"
# Delete extraneous files
find "$CONTENTSDIR" -depth -type d -name .git -exec rm -rf {} \;
find "$CONTENTSDIR" \( -name .DS_Store -or -name update.rdf \) -exec rm -f {} \;
# Add Safari extensions -- this depends on signing but needs to be done before generating
# the precomplete file
#
# $SAFARI_APPEX is a stub appex built from the safari-web-extension wrapper project. The web
# extension itself comes from $SAFARI_EXT_RESOURCES (a zotero-connectors build/safari
# directory), which replaces the stub's placeholder resources here before signing.
#
# $SAFARI_APP_EXTENSION is an optional prebuilt legacy Safari App Extension, embedded
# alongside the web extension for Safari versions that can't load Developer ID web
# extensions (supported in Safari 18.4 and later). On Safari versions that can load the
# web extension, the SFSafariAppExtensionBundleIdentifiersToReplace key causes it to
# replace the App Extension.
if [[ $SIGN == 1 ]] && [[ -n "$SAFARI_APPEX" ]] && [[ -d "$SAFARI_APPEX" ]]; then
if [[ -z "${SAFARI_EXT_RESOURCES:-}" ]] || [[ ! -f "$SAFARI_EXT_RESOURCES/manifest.json" ]]; then
echo "SAFARI_EXT_RESOURCES doesn't contain a web extension -- aborting" 2>&1
exit 1
fi
bundle_identifier=$(/usr/libexec/PlistBuddy -c "Print CFBundleIdentifier" "$APPDIR/Contents/Info.plist")
mkdir "$APPDIR/Contents/PlugIns"
webext_appex="$APPDIR/Contents/PlugIns/ZoteroSafariWebExtension.appex"
cp -R "$SAFARI_APPEX" "$webext_appex"
appex_resources="$webext_appex/Contents/Resources"
rm -rf "$appex_resources"
mkdir "$appex_resources"
cp -R "$SAFARI_EXT_RESOURCES/." "$appex_resources/"
# Show the connector version in Safari
connector_version=$(python3 -c "import json, sys; print(json.load(open(sys.argv[1]))['version'])" "$appex_resources/manifest.json")
if [[ $connector_version == *999* ]] && [ "$UPDATE_CHANNEL" != "test" ]; then
echo "Placeholder connector version $connector_version not allowed for '$UPDATE_CHANNEL' channel -- aborting" 2>&1
exit 1
fi
/usr/libexec/PlistBuddy -c "Set CFBundleShortVersionString $connector_version" \
"$webext_appex/Contents/Info.plist"
/usr/libexec/PlistBuddy -c "Set CFBundleVersion $connector_version" \
"$webext_appex/Contents/Info.plist"
# Give the appex the same bundle identifier prefix as the parent app
/usr/libexec/PlistBuddy -c "Set CFBundleIdentifier $bundle_identifier.SafariWebExtension" \
"$webext_appex/Contents/Info.plist"
# Replace the legacy App Extension on Safari versions that can load the web extension
/usr/libexec/PlistBuddy -c "Add :NSExtension:SFSafariAppExtensionBundleIdentifiersToReplace array" \
"$webext_appex/Contents/Info.plist"
/usr/libexec/PlistBuddy -c "Add :NSExtension:SFSafariAppExtensionBundleIdentifiersToReplace:0 string $bundle_identifier.SafariExtension" \
"$webext_appex/Contents/Info.plist"
# Add legacy Safari App Extension
if [[ -n "${SAFARI_APP_EXTENSION:-}" ]]; then
if [[ ! -d "$SAFARI_APP_EXTENSION" ]]; then
echo "SAFARI_APP_EXTENSION not found at $SAFARI_APP_EXTENSION -- aborting" 2>&1
exit 1
fi
appext_appex="$APPDIR/Contents/PlugIns/ZoteroSafariExtension.appex"
cp -R "$SAFARI_APP_EXTENSION" "$appext_appex"
rm -rf "$appext_appex/Contents/Resources/safari/test"
/usr/libexec/PlistBuddy -c "Set CFBundleIdentifier $bundle_identifier.SafariExtension" \
"$appext_appex/Contents/Info.plist"
fi
fi
# Copy over removed-files and make a precomplete file
pushd "$CONTENTSDIR/Resources/"
cp "$CALLDIR/update-packaging/removed-files_mac" removed-files
python3 "$CALLDIR/scripts/createprecomplete.py"
if [ ! -s precomplete ]; then
echo "precomplete file not created -- aborting" 2>&1
exit 1
fi
popd
# Sign
if [ $SIGN == 1 ]; then
# Unlock keychain if a password is provided (necessary for building from a shell)
if [ -n "$KEYCHAIN_PASSWORD" ]; then
security -v unlock-keychain -p "$KEYCHAIN_PASSWORD" ~/Library/Keychains/$KEYCHAIN.keychain-db
fi
# Clear extended attributes, which can cause codesign to fail
/usr/bin/xattr -cr "$APPDIR"
# Sign libraries and updater without entitlements
/usr/bin/codesign --force --options runtime --sign "$DEVELOPER_ID" \
"$APPDIR/Contents/MacOS/XUL" \
"$APPDIR/Contents/MacOS/updater.app/Contents/Frameworks/UpdateSettings.framework" \
"$APPDIR/Contents/MacOS/updater.app" \
"$APPDIR/Contents/Frameworks/ChannelPrefs.framework"
find "$APPDIR/Contents" -name '*.dylib' -print0 | xargs -0 /usr/bin/codesign --force --options runtime --sign "$DEVELOPER_ID"
# Sign bundled apps and main app bundle
entitlements_file="$CALLDIR/mac/entitlements.xml"
find "$APPDIR/Contents" -name '*.app' -not -name "updater.app" -print0 | xargs -0 /usr/bin/codesign --force --options runtime --entitlements "$entitlements_file" --sign "$DEVELOPER_ID"
# Sign .jnilib (Java native shared library) within LibreOffice extension, since notarization
# started failing without this. The .jnilib is within a .jar within the .oxt, so we have to
# extract both, sign the libraries, and then update each ZIP.
#
# TODO: Remove this block once the plugin ships a jna.jar without the macOS native
# libraries, which are never loaded (JNA is used only on Windows)
pushd "$BUILD_DIR"
mkdir libreoffice-repack
cd libreoffice-repack
unzip -q "$APPDIR/Contents/Resources/integration/libreoffice/Zotero_LibreOffice_Integration.oxt" external_jars/jna.jar
unzip -q external_jars/jna.jar 'com/sun/jna/darwin*/libjnidispatch.jnilib'
/usr/bin/codesign --force --options runtime --sign "$DEVELOPER_ID" com/sun/jna/darwin*/libjnidispatch.jnilib
zip -u external_jars/jna.jar com/sun/jna/darwin*/libjnidispatch.jnilib
zip -u "$APPDIR/Contents/Resources/integration/libreoffice/Zotero_LibreOffice_Integration.oxt" external_jars/jna.jar
cd ..
rm -rf libreoffice-repack
popd
# Sign Safari extensions
#
# Even though they're signed by Xcode, we sign them again to make sure they match the parent app signature
for appex in "$APPDIR"/Contents/PlugIns/*.appex; do
if [ ! -d "$appex" ]; then
continue
fi
echo
# Extract entitlements, which differ from parent app
/usr/bin/codesign -d --entitlements "$BUILD_DIR/safari-entitlements.plist" --xml "$appex"
find "$appex/Contents" -name '*.dylib' -exec /usr/bin/codesign --force --options runtime --entitlements "$entitlements_file" --sign "$DEVELOPER_ID" {} \;
/usr/bin/codesign --force --options runtime --entitlements "$BUILD_DIR/safari-entitlements.plist" --sign "$DEVELOPER_ID" "$appex"
done
# Sign final app package
echo
/usr/bin/codesign --force --options runtime --entitlements "$entitlements_file" --sign "$DEVELOPER_ID" "$APPDIR"
# Verify app
/usr/bin/codesign --verify -vvvv "$APPDIR"
# Verify Safari extensions
for appex in "$APPDIR"/Contents/PlugIns/*.appex; do
if [ ! -d "$appex" ]; then
continue
fi
echo
/usr/bin/codesign --verify -vvvv "$appex"
done
fi
# Build and notarize disk image
if [ $PACKAGE == 1 ]; then
if [ $MAC_NATIVE == 1 ]; then
echo "Creating Mac installer"
dmg="$DIST_DIR/Zotero-$VERSION.dmg"
"$CALLDIR/mac/pkg-dmg" --source "$STAGE_DIR/Zotero.app" \
--target "$dmg" \
--sourcefile --volname Zotero --copy "$CALLDIR/mac/DSStore:/.DS_Store" \
--symlink /Applications:"/Drag Here to Install" > /dev/null
if [ "$UPDATE_CHANNEL" != "test" ]; then
# Upload disk image to Apple
"$CALLDIR/scripts/notarize_mac_app" "$dmg"
echo
# Staple notarization info to disk image
"$CALLDIR/scripts/notarization_stapler" "$dmg"
echo "Notarization complete"
else
echo "Test build -- skipping notarization"
fi
echo
else
echo 'Not building on Mac; creating Mac distribution as a zip file'
rm -f "$DIST_DIR/Zotero_mac.zip"
cd "$STAGE_DIR" && zip -rqX "$DIST_DIR/Zotero-${VERSION}_mac.zip" Zotero.app
fi
fi
fi
# Windows
if [ $BUILD_WIN == 1 ]; then
echo "Building Windows common"
COMMON_APPDIR="$STAGE_DIR/Zotero_common"
mkdir "$COMMON_APPDIR"
# Package non-arch-specific components
if [ $PACKAGE -eq 1 ]; then
# Copy installer files
cp -r "$CALLDIR/win/installer" "$BUILD_DIR/win_installer"
perl -pi -e "s/\{\{VERSION}}/$VERSION/" "$BUILD_DIR/win_installer/defines.nsi"
mkdir "$COMMON_APPDIR/uninstall"
# Compress 7zSD.sfx
upx --best -o "`cygpath -w \"$BUILD_DIR/7zSD.sfx\"`" \
"`cygpath -w \"$CALLDIR/win/installer/7zstub/firefox/7zSD.sfx\"`" > /dev/null
fi
if [[ -n $arch ]]; then
archs=("$(get_canonical_arch w $arch)");
else
archs=(win-x64 win-arm64 win32);
fi
for arch in "${archs[@]}"; do
echo "Building Zotero_$arch"
runtime_path="${WIN_RUNTIME_PATH_PREFIX}$arch"
# Set up directory
APPDIR="$STAGE_DIR/Zotero_$arch"
mkdir "$APPDIR"
# Copy relevant assets from Firefox
#
# 'i686' is a huge directory containing x86 versions of xul.dll and other files in
# Firefox ARM64 builds for use with the EME DRM plugins
cp -R "$runtime_path"/!(application.ini|browser|crashreporter*|default-browser-agent.exe|defaultagent*|defaults|devtools-files|firefox*|i686|maintenanceservice*|minidump-analyzer.exe|pingsender.exe|private_browsing*|precomplete|removed-files|uninstall|update*) "$APPDIR"
# Copy zotero.exe, which is built directly from Firefox source and then modified by
# ResourceHacker to add icons
check_lfs_file "$CALLDIR/win/zotero.exe.tar.xz"
tar xf "$CALLDIR/win/zotero.exe.tar.xz" --to-stdout zotero_$arch.exe > "$APPDIR/zotero.exe"
# Use our own updater, because Mozilla's requires updates signed by Mozilla
check_lfs_file "$CALLDIR/win/updater.exe.tar.xz"
tar xf "$CALLDIR/win/updater.exe.tar.xz" --to-stdout updater-$arch.exe > "$APPDIR/updater.exe"
# Update .exe version numbers (only possible on Windows)
if [ $WIN_NATIVE == 1 ]; then
# FileVersion is limited to four integers, so it won't be properly updated for non-release
# builds (e.g., we'll show 5.0.97.0 for 5.0.97-beta.37). ProductVersion will be the full
# version string.
rcedit "`cygpath -w \"$APPDIR/zotero.exe\"`" \
--set-file-version "$VERSION_NUMERIC" \
--set-product-version "$VERSION"
rcedit "`cygpath -w \"$APPDIR/updater.exe\"`" \
--set-file-version "$VERSION_NUMERIC" \
--set-product-version "$VERSION"
fi
# Copy app files
rsync -a "$base_dir/" "$APPDIR/"
#mv "$APPDIR/app/application.ini" "$APPDIR/"
# Copy in common files
rsync -a "$COMMON_APPDIR/" "$APPDIR/"
cat "$CALLDIR/win/installer/updater_append.ini" >> "$APPDIR/updater.ini"
# Add devtools
#if [ $DEVTOOLS -eq 1 ]; then
# # Create devtools.jar
# cd "$BUILD_DIR"
# mkdir -p devtools/locale
# cp -r "$runtime_path"/devtools-files/chrome/devtools/* devtools/
# cp -r "$runtime_path"/devtools-files/chrome/locale/* devtools/locale/
# cd devtools
# zip -r -q ../devtools.jar *
# cd ..
# rm -rf devtools
# mv devtools.jar "$APPDIR"
#
# cp "$runtime_path/devtools-files/components/interfaces.xpt" "$APPDIR/components/"
#fi
# Add word processor plug-ins
mkdir -p "$APPDIR/integration"
cp -RH "$CALLDIR/modules/zotero-libreoffice-integration/install" "$APPDIR/integration/libreoffice"
cp -RH "$CALLDIR/modules/zotero-word-for-windows-integration/install" "$APPDIR/integration/word-for-windows"
if [ $arch = 'win32' ]; then
rm "$APPDIR/integration/word-for-windows/libzoteroWinWordIntegration_x64.dll"
rm "$APPDIR/integration/word-for-windows/libzoteroWinWordIntegration_ARM64.dll"
elif [ $arch = 'win-x64' ]; then
mv "$APPDIR/integration/word-for-windows/libzoteroWinWordIntegration_x64.dll" \
"$APPDIR/integration/word-for-windows/libzoteroWinWordIntegration.dll"
rm "$APPDIR/integration/word-for-windows/libzoteroWinWordIntegration_ARM64.dll"
elif [ $arch = 'win-arm64' ]; then
mv "$APPDIR/integration/word-for-windows/libzoteroWinWordIntegration_ARM64.dll" \
"$APPDIR/integration/word-for-windows/libzoteroWinWordIntegration.dll"
rm "$APPDIR/integration/word-for-windows/libzoteroWinWordIntegration_x64.dll"
fi
# Delete extraneous files
find "$APPDIR" -depth -type d -name .git -exec rm -rf {} \;
find "$APPDIR" \( -name .DS_Store -or -name '.git*' -or -name '.travis.yml' -or -name update.rdf -or -name '*.bak' \) -exec rm -f {} \;
find "$APPDIR" \( -name '*.exe' -or -name '*.dll' \) -exec chmod 755 {} \;
if [[ $PACKAGE -eq 1 ]] && [[ $SIGN -eq 1 ]]; then
"$CALLDIR/win/codesign" "$APPDIR/zotero.exe" "$SIGNATURE_DESC"
sleep $SIGNTOOL_DELAY
"$CALLDIR/win/codesign" "$APPDIR/updater.exe" "$SIGNATURE_DESC Updater"
sleep $SIGNTOOL_DELAY
# Re-sign modified xul.dll
"$CALLDIR/win/codesign" "$APPDIR/xul.dll" "$SIGNATURE_DESC"
sleep $SIGNTOOL_DELAY
"$CALLDIR/win/codesign" "$APPDIR/integration/word-for-windows/libzoteroWinWordIntegration.dll" "$SIGNATURE_DESC Word Plugin"
fi
# Copy over removed-files and make a precomplete file
pushd "$APPDIR"
cp "$CALLDIR/update-packaging/removed-files_$arch" removed-files
python3 "$CALLDIR/scripts/createprecomplete.py"
if [ ! -s precomplete ]; then
echo "precomplete file not created -- aborting" 2>&1
exit 1
fi
popd
if [ $PACKAGE -eq 1 ]; then
if [ $WIN_NATIVE -eq 1 ]; then
echo "Creating Windows installer"
# Build uninstaller
if [ "$arch" = "win32" ]; then
"`cygpath -u \"${NSIS_DIR}makensis.exe\"`" /V1 "`cygpath -w \"$BUILD_DIR/win_installer/uninstaller.nsi\"`"
elif [ "$arch" = "win-x64" ]; then
"`cygpath -u \"${NSIS_DIR}makensis.exe\"`" /DHAVE_64BIT_BUILD /V1 "`cygpath -w \"$BUILD_DIR/win_installer/uninstaller.nsi\"`"
elif [ "$arch" = "win-arm64" ]; then
"`cygpath -u \"${NSIS_DIR}makensis.exe\"`" /DHAVE_64BIT_BUILD /D_ARM64_ /V1 "`cygpath -w \"$BUILD_DIR/win_installer/uninstaller.nsi\"`"
fi
mv "$BUILD_DIR/win_installer/helper.exe" "$APPDIR/uninstall"
if [ $SIGN -eq 1 ]; then
"$CALLDIR/win/codesign" "$APPDIR/uninstall/helper.exe" "$SIGNATURE_DESC Uninstaller"
sleep $SIGNTOOL_DELAY
fi
if [ "$arch" = "win32" ]; then
INSTALLER_PATH="$DIST_DIR/Zotero-${VERSION}_win32_setup.exe"
elif [ "$arch" = "win-x64" ]; then
INSTALLER_PATH="$DIST_DIR/Zotero-${VERSION}_x64_setup.exe"
elif [ "$arch" = "win-arm64" ]; then
INSTALLER_PATH="$DIST_DIR/Zotero-${VERSION}_arm64_setup.exe"
fi
# Stage installer
INSTALLER_STAGE_DIR="$BUILD_DIR/win_installer/staging"
rm -rf "$INSTALLER_STAGE_DIR"
mkdir "$INSTALLER_STAGE_DIR"
cp -r "$APPDIR" "$INSTALLER_STAGE_DIR/core"
# Build and sign setup.exe
if [ "$arch" = "win32" ]; then
"`cygpath -u \"${NSIS_DIR}makensis.exe\"`" /V1 "`cygpath -w \"$BUILD_DIR/win_installer/installer.nsi\"`"
elif [ "$arch" = "win-x64" ]; then
"`cygpath -u \"${NSIS_DIR}makensis.exe\"`" /DHAVE_64BIT_BUILD /V1 "`cygpath -w \"$BUILD_DIR/win_installer/installer.nsi\"`"
elif [ "$arch" = "win-arm64" ]; then
"`cygpath -u \"${NSIS_DIR}makensis.exe\"`" /DHAVE_64BIT_BUILD /D_ARM64_ /V1 "`cygpath -w \"$BUILD_DIR/win_installer/installer.nsi\"`"
fi
mv "$BUILD_DIR/win_installer/setup.exe" "$INSTALLER_STAGE_DIR"
if [ $SIGN == 1 ]; then
"$CALLDIR/win/codesign" "$INSTALLER_STAGE_DIR/setup.exe" "$SIGNATURE_DESC Setup"
sleep $SIGNTOOL_DELAY
fi
# Compress application
cd "$INSTALLER_STAGE_DIR" && 7z a -r -t7z "`cygpath -w \"$BUILD_DIR/app_$arch.7z\"`" \
-mx -m0=BCJ2 -m1=LZMA:d24 -m2=LZMA:d19 -m3=LZMA:d19 -mb0:1 -mb0s1:2 -mb0s2:3 > /dev/null
# Combine 7zSD.sfx and app.tag into setup.exe
cat "$BUILD_DIR/7zSD.sfx" "$CALLDIR/win/installer/app.tag" \
"$BUILD_DIR/app_$arch.7z" > "$INSTALLER_PATH"
# Sign installer .exe
if [ $SIGN == 1 ]; then
"$CALLDIR/win/codesign" "$INSTALLER_PATH" "$SIGNATURE_DESC Installer"
fi
chmod 755 "$INSTALLER_PATH"
else
echo 'Not building on Windows; only building zip file'
fi
cd "$STAGE_DIR"
zip -rqX "$DIST_DIR/Zotero-${VERSION}_$arch.zip" Zotero_$arch
fi
done
rm -rf "$COMMON_APPDIR"
fi
# Linux
if [ $BUILD_LINUX == 1 ]; then
if [[ -n $arch ]]; then
archs=("$(get_canonical_arch l $arch)")
else
archs=(x64 arm64 i686)
fi
for arch in "${archs[@]}"; do
[[ $arch == x64 ]] && arch="x86_64"
runtime_path="${LINUX_RUNTIME_PATH_PREFIX}${arch}"
# Set up directory
echo 'Building Zotero_linux-'$arch
APPDIR="$STAGE_DIR/Zotero_linux-$arch"
rm -rf "$APPDIR"
mkdir "$APPDIR"
# Merge relevant assets from Firefox
cp -r "$runtime_path/"!(application.ini|browser|defaults|devtools-files|crashreporter|crashreporter.ini|firefox|pingsender|precomplete|removed-files|run-mozilla.sh|update-settings.ini|updater|updater.ini) "$APPDIR"
# Use our own launcher that calls the original Firefox executable with -app
mv "$APPDIR"/firefox-bin "$APPDIR"/zotero-bin
cp "$CALLDIR/linux/zotero" "$APPDIR"/zotero
# Copy Ubuntu launcher files
cp "$CALLDIR/linux/zotero.desktop" "$APPDIR"
cp "$CALLDIR/linux/set_launcher_icon" "$APPDIR"
# Use our own updater, because Mozilla's requires updates signed by Mozilla
check_lfs_file "$CALLDIR/linux/updater.tar.xz"
tar xf "$CALLDIR/linux/updater.tar.xz" --to-stdout updater-$arch > "$APPDIR/updater"
chmod 755 "$APPDIR/updater"
# Copy app files
rsync -a "$base_dir/" "$APPDIR/"
# Add word processor plug-ins
mkdir "$APPDIR/integration"
cp -RH "$CALLDIR/modules/zotero-libreoffice-integration/install" "$APPDIR/integration/libreoffice"
# Firefox includes an 'icons' folder with updater.png, but some Linux distro builds omit it
mkdir -p "$APPDIR/icons"
# Copy icons
cp "$CALLDIR/linux/icons/icon32.png" "$APPDIR/icons/"
cp "$CALLDIR/linux/icons/icon64.png" "$APPDIR/icons/"
cp "$CALLDIR/linux/icons/icon128.png" "$APPDIR/icons/"
cp "$CALLDIR/linux/icons/symbolic.svg" "$APPDIR/icons/"
# Delete extraneous files
find "$APPDIR" -depth -type d -name .git -exec rm -rf {} \;
find "$APPDIR" \( -name .DS_Store -or -name update.rdf \) -exec rm -f {} \;
# Copy over removed-files and make a precomplete file
pushd "$APPDIR"
cp "$CALLDIR/update-packaging/removed-files_linux-$arch" removed-files
python3 "$CALLDIR/scripts/createprecomplete.py"
if [ ! -s precomplete ]; then
echo "precomplete file not created -- aborting" 2>&1
exit 1
fi
popd
if [ $PACKAGE == 1 ]; then
# Create tar
rm -f "$DIST_DIR/Zotero-${VERSION}_linux-$arch.tar.xz"
cd "$STAGE_DIR"
tar -cJf "$DIST_DIR/Zotero-${VERSION}_linux-$arch.tar.xz" "Zotero_linux-$arch"
fi
done
fi
rm -rf $BUILD_DIR