#!/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 .
CALLDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
. "$CALLDIR/config.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 <&1
exit 1
fi
}
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
while getopts "d:f:p:c:tseq" opt; do
case $opt in
d)
SOURCE_DIR="$OPTARG"
;;
f)
ZIP_FILE="$OPTARG"
;;
p)
for i in `seq 0 1 $((${#OPTARG}-1))`
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}"
usage
;;
esac
done
;;
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
function check_xulrunner_hash {
platform=$1
if [ $platform == "m" ]; then
platform_hash_file="hash-mac"
elif [ $platform == "w" ]; then
platform_hash_file="hash-win"
elif [ $platform == "l" ]; then
platform_hash_file="hash-linux"
else
echo "Platform parameter incorrect. Acceptable values: m/w/l"
exit 1
fi
if [ ! -e "$CALLDIR/xulrunner/$platform_hash_file" ]; then
echo "xulrunner not found -- downloading"
echo
$CALLDIR/scripts/fetch_xulrunner -p $platform
else
recalculated_xulrunner_hash=$("$CALLDIR/scripts/xulrunner_hash" -p $platform)
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
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
}
#Check if xulrunner and GECKO_VERSION for each platform match
if [ $BUILD_MAC == 1 ]; then
check_xulrunner_hash m
fi
if [ $BUILD_WIN == 1 ]; then
check_xulrunner_hash w
fi
if [ $BUILD_LINUX == 1 ]; then
check_xulrunner_hash l
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-"
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
# Non-arch-specific files, so just use 64-bit version
cp -Rp "${WIN_RUNTIME_PATH_PREFIX}win-x64"/browser/omni "$app_dir"
unzip -qj "${WIN_RUNTIME_PATH_PREFIX}win-x64"/omni.ja "hyphenation/*" -d "$app_dir"/hyphenation/
elif [ $BUILD_LINUX == 1 ]; then
# Non-arch-specific files, so just use 64-bit version
cp -Rp "${LINUX_RUNTIME_PATH_PREFIX}x86_64"/browser/omni "$app_dir"
unzip -qj "${LINUX_RUNTIME_PATH_PREFIX}x86_64"/omni.ja "hyphenation/*" -d "$app_dir"/hyphenation/
fi
set -e
cd $omni_dir
# 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
egrep -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
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
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/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
# 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|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"
# 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
cp "$MAC_RUNTIME_PATH/../MacOS/XUL" "$CONTENTSDIR/MacOS/"
# 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 App Extension -- this depends on signing but needs to be done before generating
# the precomplete file
if [[ $SIGN == 1 ]] && [[ -n "$SAFARI_APPEX" ]] && [[ -d "$SAFARI_APPEX" ]]; then
mkdir "$APPDIR/Contents/PlugIns"
cp -R $SAFARI_APPEX "$APPDIR/Contents/PlugIns/ZoteroSafariExtension.appex"
rm -rf "$APPDIR/Contents/PlugIns/ZoteroSafariExtension.appex/Contents/Resources/safari/test/"
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 app
entitlements_file="$CALLDIR/mac/entitlements.xml"
/usr/bin/codesign --force --options runtime --entitlements "$entitlements_file" --sign "$DEVELOPER_ID" \
"$APPDIR/Contents/MacOS/XUL" \
"$APPDIR/Contents/MacOS/updater.app/Contents/MacOS/org.mozilla.updater"
find "$APPDIR/Contents" -name '*.dylib' -exec /usr/bin/codesign --force --options runtime --entitlements "$entitlements_file" --sign "$DEVELOPER_ID" {} \;
find "$APPDIR/Contents" -name '*.app' -exec /usr/bin/codesign --force --options runtime --entitlements "$entitlements_file" --sign "$DEVELOPER_ID" {} \;
/usr/bin/codesign --force --options runtime --entitlements "$entitlements_file" --sign "$DEVELOPER_ID" "$APPDIR/Contents/MacOS/zotero"
# Sign Safari App Extension
#
# Even though it's signed by Xcode, we sign it again to make sure it matches the parent app signature
if [ -d "$APPDIR/Contents/PlugIns/ZoteroSafariExtension.appex" ]; then
echo
# Extract entitlements, which differ from parent app
/usr/bin/codesign -d --entitlements "$BUILD_DIR/safari-entitlements.plist" --xml "$SAFARI_APPEX"
# Add suffix to appex bundle identifier
if [ $UPDATE_CHANNEL == "beta" ] || [ $UPDATE_CHANNEL == "dev" ] || [ $UPDATE_CHANNEL == "source" ]; then
perl -pi -e "s/org\.zotero\.SafariExtensionApp\.SafariExtension/org.zotero.SafariExtensionApp.SafariExtension-$UPDATE_CHANNEL/" "$APPDIR/Contents/PlugIns/ZoteroSafariExtension.appex/Contents/Info.plist"
fi
find "$APPDIR/Contents/PlugIns/ZoteroSafariExtension.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" "$APPDIR/Contents/PlugIns/ZoteroSafariExtension.appex"
fi
# 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 App Extension
if [[ -n "$SAFARI_APPEX" ]] && [[ -d "$SAFARI_APPEX" ]]; then
echo
/usr/bin/codesign --verify -vvvv "$APPDIR/Contents/PlugIns/ZoteroSafariExtension.appex"
fi
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
for arch in win32 win-x64 win-arm64; 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
# Sign updater
if [ $SIGN -eq 1 ]; then
"$CALLDIR/win/codesign" "$APPDIR/updater.exe" "$SIGNATURE_DESC Updater"
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 {} \;
# 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
if [ $SIGN -eq 1 ]; then
"$CALLDIR/win/codesign" "$APPDIR/zotero.exe" "$SIGNATURE_DESC"
sleep $SIGNTOOL_DELAY
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
# Skip 32-bit build in tests
if [[ "${ZOTERO_TEST:-}" = "1" ]] || [[ "${SKIP_32:-}" = "1" ]]; then
archs="x86_64"
else
archs="i686 x86_64"
fi
for arch in $archs; do
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"
# 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.bz2"
cd "$STAGE_DIR"
tar -cjf "$DIST_DIR/Zotero-${VERSION}_linux-$arch.tar.bz2" "Zotero_linux-$arch"
fi
done
fi
rm -rf $BUILD_DIR