1#!/bin/bash
2
3set -e
4
5build_flag="--release"
6target_dir="release"
7open_result=false
8local_arch=false
9local_only=false
10overwrite_local_app=false
11bundle_name=""
12
13# Function for displaying help info
14help_info() {
15 echo "
16Usage: ${0##*/} [options] [bundle_name]
17Build the application bundle.
18
19Options:
20 -d Compile in debug mode
21 -l Compile for local architecture and copy bundle to /Applications, implies -d.
22 -o Open the resulting DMG or the app itself in local mode.
23 -f Overwrite the local app bundle if it exists.
24 -h Display this help and exit.
25 "
26}
27
28# If -o option is specified, the folder of the resulting dmg will be opened in finder
29# If -d is specified, Zed will be compiled in debug mode and the application's path printed
30# If -od or -do is specified Zed will be bundled in debug and the application will be run.
31while getopts 'dlfoh' flag
32do
33 case "${flag}" in
34 o) open_result=true;;
35 d)
36 export CARGO_INCREMENTAL=true
37 export CARGO_BUNDLE_SKIP_BUILD=true
38 build_flag="";
39 local_arch=true
40 target_dir="debug"
41 ;;
42 l)
43 export CARGO_INCREMENTAL=true
44 export CARGO_BUNDLE_SKIP_BUILD=true
45 build_flag=""
46 local_arch=true
47 local_only=true
48 target_dir="debug"
49 ;;
50 f) overwrite_local_app=true;;
51 h)
52 help_info
53 exit 0
54 ;;
55 esac
56done
57
58shift $((OPTIND-1))
59
60if [ "$1" ]; then
61 bundle_name=$1
62fi
63
64export ZED_BUNDLE=true
65export MACOSX_DEPLOYMENT_TARGET=10.15.7
66
67cargo_bundle_version=$(cargo -q bundle --help 2>&1 | head -n 1 || echo "")
68if [ "$cargo_bundle_version" != "cargo-bundle v0.6.0-zed" ]; then
69 cargo install cargo-bundle --git https://github.com/zed-industries/cargo-bundle.git --branch zed-deploy
70fi
71
72rustup target add wasm32-wasi
73
74# Deal with versions of macOS that don't include libstdc++ headers
75export CXXFLAGS="-stdlib=libc++"
76
77version_info=$(rustc --version --verbose)
78host_line=$(echo "$version_info" | grep host)
79local_target_triple=${host_line#*: }
80
81if [ "$local_arch" = true ]; then
82 echo "Building for local target only."
83 cargo build ${build_flag} --package zed
84 cargo build ${build_flag} --package cli
85else
86 echo "Compiling zed binary for aarch64-apple-darwin"
87 cargo build ${build_flag} --package zed --target aarch64-apple-darwin
88 echo "Compiling zed binary for x86_64-apple-darwin"
89 cargo build ${build_flag} --package zed --target x86_64-apple-darwin
90 echo "Compiling cli binary for aarch64-apple-darwin"
91 cargo build ${build_flag} --package cli --target aarch64-apple-darwin
92 echo "Compiling cli binary for x86_64-apple-darwin"
93 cargo build ${build_flag} --package cli --target x86_64-apple-darwin
94fi
95
96echo "Creating application bundle"
97pushd crates/zed
98channel=$(<RELEASE_CHANNEL)
99cp Cargo.toml Cargo.toml.backup
100sed \
101 -i .backup \
102 "s/package.metadata.bundle-${channel}/package.metadata.bundle/" \
103 Cargo.toml
104
105if [ "$local_arch" = true ]; then
106 app_path=$(cargo bundle ${build_flag} --select-workspace-root | xargs)
107else
108 app_path=$(cargo bundle ${build_flag} --target x86_64-apple-darwin --select-workspace-root | xargs)
109fi
110
111mv Cargo.toml.backup Cargo.toml
112popd
113echo "Bundled ${app_path}"
114
115if [ "$local_arch" = false ]; then
116 echo "Creating fat binaries"
117 lipo \
118 -create \
119 target/{x86_64-apple-darwin,aarch64-apple-darwin}/${target_dir}/Zed \
120 -output \
121 "${app_path}/Contents/MacOS/zed"
122 lipo \
123 -create \
124 target/{x86_64-apple-darwin,aarch64-apple-darwin}/${target_dir}/cli \
125 -output \
126 "${app_path}/Contents/MacOS/cli"
127fi
128
129echo "Copying WebRTC.framework into the frameworks folder"
130mkdir "${app_path}/Contents/Frameworks"
131if [ "$local_arch" = false ]; then
132 cp -R target/${local_target_triple}/${target_dir}/WebRTC.framework "${app_path}/Contents/Frameworks/"
133else
134 cp -R target/${target_dir}/WebRTC.framework "${app_path}/Contents/Frameworks/"
135fi
136
137cp crates/zed/contents/embedded.provisionprofile "${app_path}/Contents/"
138
139if [[ -n $MACOS_CERTIFICATE && -n $MACOS_CERTIFICATE_PASSWORD && -n $APPLE_NOTARIZATION_USERNAME && -n $APPLE_NOTARIZATION_PASSWORD ]]; then
140 echo "Signing bundle with Apple-issued certificate"
141 security create-keychain -p "$MACOS_CERTIFICATE_PASSWORD" zed.keychain || echo ""
142 security default-keychain -s zed.keychain
143 security unlock-keychain -p "$MACOS_CERTIFICATE_PASSWORD" zed.keychain
144 echo "$MACOS_CERTIFICATE" | base64 --decode > /tmp/zed-certificate.p12
145 security import /tmp/zed-certificate.p12 -k zed.keychain -P "$MACOS_CERTIFICATE_PASSWORD" -T /usr/bin/codesign
146 rm /tmp/zed-certificate.p12
147 security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$MACOS_CERTIFICATE_PASSWORD" zed.keychain
148
149 # sequence of codesign commands modeled after this example: https://developer.apple.com/forums/thread/701514
150 /usr/bin/codesign --force --timestamp --sign "Zed Industries, Inc." "${app_path}/Contents/Frameworks" -v
151 /usr/bin/codesign --force --timestamp --options runtime --sign "Zed Industries, Inc." "${app_path}/Contents/MacOS/cli" -v
152 /usr/bin/codesign --force --timestamp --options runtime --entitlements crates/zed/resources/zed.entitlements --sign "Zed Industries, Inc." "${app_path}/Contents/MacOS/zed" -v
153
154 security default-keychain -s login.keychain
155else
156 echo "One or more of the following variables are missing: MACOS_CERTIFICATE, MACOS_CERTIFICATE_PASSWORD, APPLE_NOTARIZATION_USERNAME, APPLE_NOTARIZATION_PASSWORD"
157 echo "Performing an ad-hoc signature, but this bundle should not be distributed"
158 echo "If you see 'The application cannot be opened for an unexpected reason,' you likely don't have the necessary entitlements to run the application in your signing keychain"
159 echo "You will need to download a new signing key from developer.apple.com, add it to keychain, and export MACOS_SIGNING_KEY=<email address of signing key>"
160 codesign --force --deep --entitlements crates/zed/resources/zed.entitlements --sign ${MACOS_SIGNING_KEY:- -} "${app_path}" -v
161fi
162
163if [[ "$target_dir" = "debug" && "$local_only" = false ]]; then
164 if [ "$open_result" = true ]; then
165 open "$app_path"
166 else
167 echo "Created application bundle:"
168 echo "$app_path"
169 fi
170 exit 0
171fi
172
173if [ "$local_only" = true ]; then
174 # If bundle_name is not set or empty, use the basename of $app_path
175 if [ -z "$bundle_name" ]; then
176 bundle_name=$(basename "$app_path")
177 else
178 # If bundle_name doesn't end in .app, append it
179 if [[ "$bundle_name" != *.app ]]; then
180 bundle_name="$bundle_name.app"
181 fi
182 fi
183
184 if [ "$overwrite_local_app" = true ]; then
185 rm -rf "/Applications/$bundle_name"
186 fi
187 mv "$app_path" "/Applications/$bundle_name"
188
189 if [ "$open_result" = true ]; then
190 open "/Applications/$bundle_name"
191 else
192 echo "Installed application bundle:"
193 echo "/Applications/$bundle_name"
194 fi
195else
196 echo "Creating DMG"
197 dmg_target_directory="target/${target_dir}"
198 dmg_source_directory="${dmg_target_directory}/dmg"
199 dmg_file_path="${dmg_target_directory}/Zed.dmg"
200
201 rm -rf ${dmg_source_directory}
202 mkdir -p ${dmg_source_directory}
203 mv "${app_path}" "${dmg_source_directory}"
204
205 ln -s /Applications ${dmg_source_directory}
206 hdiutil create -volname Zed -srcfolder "${dmg_source_directory}" -ov -format UDZO "${dmg_file_path}"
207 # If someone runs this bundle script locally, a symlink will be placed in `dmg_source_directory`.
208 # This symlink causes CPU issues with Zed if the Zed codebase is the project being worked on, so we simply remove it for now.
209 rm ${dmg_source_directory}/Applications
210
211 echo "Adding license agreement to DMG"
212 npm install --global dmg-license minimist
213 dmg-license script/eula/eula.json "${dmg_file_path}"
214
215 if [[ -n $MACOS_CERTIFICATE && -n $MACOS_CERTIFICATE_PASSWORD && -n $APPLE_NOTARIZATION_USERNAME && -n $APPLE_NOTARIZATION_PASSWORD ]]; then
216 echo "Notarizing DMG with Apple"
217 npm install -g notarize-cli
218 npx notarize-cli --file "${dmg_file_path}" --bundle-id dev.zed.Zed --username "$APPLE_NOTARIZATION_USERNAME" --password "$APPLE_NOTARIZATION_PASSWORD"
219 fi
220
221 if [ "$open_result" = true ]; then
222 open $dmg_target_directory
223 fi
224fi