Update linux binary expectations (#12622)

Conrad Irwin and Mikayla created

Fixes #12585

This changes the expectations for installed binaries on linux based on
work
that @jirutka has done for Alpine.

In particular, we now put the cli in place as `bin/zed` and the zed
binary as
`libexec/zed-editor`, and assume that packagers do the same.

cc @someone13574

Release notes:

- N/A

---------

Co-authored-by: Mikayla <mikayla@zed.dev>

Change summary

crates/cli/src/main.rs                              | 21 +++++-----
crates/zed/Cargo.toml                               |  2 
crates/zed/resources/flatpak/manifest-template.json |  4 +-
docs/src/development/linux.md                       | 30 ++++++++++++++
script/bundle-linux                                 | 10 ++--
script/bundle-mac                                   | 22 +++++------
script/install.sh                                   |  7 +++
script/zed-local                                    |  4 +-
8 files changed, 65 insertions(+), 35 deletions(-)

Detailed changes

crates/cli/src/main.rs 🔗

@@ -191,14 +191,15 @@ mod linux {
                 let cli = env::current_exe()?;
                 let dir = cli
                     .parent()
+                    .and_then(Path::parent)
                     .ok_or_else(|| anyhow!("no parent path for cli"))?;
 
-                match dir.join("zed").canonicalize() {
+                match dir.join("libexec").join("zed-editor").canonicalize() {
                     Ok(path) => Ok(path),
-                    // development builds have Zed capitalized
-                    Err(e) => match dir.join("Zed").canonicalize() {
-                        Ok(path) => Ok(path),
-                        Err(_) => Err(e),
+                    // In development cli and zed are in the ./target/ directory together
+                    Err(e) => match cli.parent().unwrap().join("zed").canonicalize() {
+                        Ok(path) if path != cli => Ok(path),
+                        _ => Err(e),
                     },
                 }
             }?;
@@ -254,10 +255,8 @@ mod linux {
                         eprintln!("failed to setsid: {}", std::io::Error::last_os_error());
                         process::exit(1);
                     }
-                    if std::env::var("ZED_KEEP_FD").is_err() {
-                        if let Err(_) = fork::close_fd() {
-                            eprintln!("failed to close_fd: {}", std::io::Error::last_os_error());
-                        }
+                    if let Err(_) = fork::close_fd() {
+                        eprintln!("failed to close_fd: {}", std::io::Error::last_os_error());
                     }
                     let error =
                         exec::execvp(path.clone(), &[path.as_os_str(), &OsString::from(ipc_url)]);
@@ -333,7 +332,7 @@ mod flatpak {
 
             if !is_app_location_set {
                 args.push("--zed".into());
-                args.push(flatpak_dir.join("bin").join("zed-app").into());
+                args.push(flatpak_dir.join("libexec").join("zed-editor").into());
             }
 
             let error = exec::execvp("/usr/bin/flatpak-spawn", args);
@@ -347,7 +346,7 @@ mod flatpak {
             && env::var("FLATPAK_ID").map_or(false, |id| id.starts_with("dev.zed.Zed"))
         {
             if args.zed.is_none() {
-                args.zed = Some("/app/bin/zed-app".into());
+                args.zed = Some("/app/libexec/zed-editor".into());
                 env::set_var("ZED_IS_FLATPAK_INSTALL", "1");
             }
         }

crates/zed/Cargo.toml 🔗

@@ -11,7 +11,7 @@ authors = ["Zed Team <hi@zed.dev>"]
 workspace = true
 
 [[bin]]
-name = "Zed"
+name = "zed"
 path = "src/main.rs"
 
 [dependencies]

crates/zed/resources/flatpak/manifest-template.json 🔗

@@ -40,8 +40,8 @@
         "envsubst < zed.desktop.in > zed.desktop && install -Dm644 zed.desktop /app/share/applications/$APP_ID.desktop",
         "envsubst < flatpak/zed.metainfo.xml.in > zed.metainfo.xml && install -Dm644 zed.metainfo.xml /app/share/metainfo/$APP_ID.metainfo.xml",
         "sed -i -e '/@release_info@/{r flatpak/release-info/$CHANNEL' -e 'd}' /app/share/metainfo/$APP_ID.metainfo.xml",
-        "install -Dm755 bin/cli /app/bin/zed",
-        "install -Dm755 bin/zed /app/bin/zed-app",
+        "install -Dm755 bin/zed /app/bin/zed",
+        "install -Dm755 libexec/zed-editor /app/libexec/zed-editor",
         "install -Dm755 lib/* -t /app/lib"
       ],
       "sources": [

docs/src/development/linux.md 🔗

@@ -70,10 +70,38 @@ cargo test --workspace
 
 Zed has basic support for both modes. The mode is selected at runtime. If you're on wayland and want to run in X11 mode, you can set `WAYLAND_DISPLAY='' cargo run` to do so.
 
+## Notes for packaging Zed
+
+Thank you for taking on the task of packaging Zed!
+
+### Technical requirements
+
+Zed has two main binaries:
+
+* You will need to build `crates/cli` and make it's binary available in `$PATH` with the name `zed`.
+* You will need to build `crates/zed` and put it at `$PATH/to/cli/../../libexec/zed-editor`. For example, if you are going to put the cli at `~/.local/bin/zed` put zed at `~/.local/libexec/zed-editor`.
+* If you are going to provide a `.desktop` file you can find a template in `crates/zed/resources/zed.desktop.in`, and use `envsubst` to populate it with the values required.
+* You will need to ensure that the necessary libraries are installed. You can get the current list by [inspecting the built binary](https://github.com/zed-industries/zed/blob/059a4141b756cf4afac4c977afc488539aec6470/script/bundle-linux#L65-L70) on your system.
+* For an example of a complete build script, see [script/bundle-linux](https://github.com/zed-industries/zed/blob/main/script/bundle-linux).
+
+### Other things to note
+
+At Zed, our priority has been to move fast and bring the latest technology to our users. We've long been frustrated at having software that is slow, out of date, or hard to configure, and so we've built our editor to those tastes.
+
+However, we realize that many distros have other priorities. We want to work with everyone to bring Zed to their favorite platforms. But there is a long way to go:
+
+* Zed is a fast moving early-phase project. We typically release 2-3 builds a week to fix user-reported issues and release major features.
+* There are a couple of other `zed` binaries that may be present on linux systems ([1](https://openzfs.github.io/openzfs-docs/man/v2.2/8/zed.8.html), [2](https://zed.brimdata.io/docs/commands/zed)).
+* We automatically install updates to Zed by default (though we do need a way for [package managers to opt out](https://github.com/zed-industries/zed/issues/12588)).
+* Zed automatically installs the correct version of common developer tools in the same way as rustup/rbenv/pyenv, etc. We understand that this is contentious, [see here](https://github.com/zed-industries/zed/issues/12589).
+* We allow users to install extensions on their own and from [zed-industries/extensions](https://github.com/zed-industries/extensions). These extensions may install further tooling as needed, such as language servers. In the long run we would like to make this safer, [see here](https://github.com/zed-industries/zed/issues/12358).
+* Zed connects to a number of online services by default (AI, telemetry, collaboration). AI and our telemetry can be disabled by your users with their own zed settings or by patching our [default settings file](https://github.com/zed-industries/zed/blob/main/assets/settings/default.json).
+* As a result, zed currently does not play nice with sandboxes, [see here](https://github.com/zed-industries/zed/pull/12006#issuecomment-2130421220)
+
 ## Flatpak
 
 > [!WARNING]
-> Zed's current Flatpak integration simply exits the sandbox on startup. Workflows that rely on Flatpak's sandboxing may not work as expected. 
+> Zed's current Flatpak integration simply exits the sandbox on startup. Workflows that rely on Flatpak's sandboxing may not work as expected.
 
 To build & install the Flatpak package locally follow the steps below:
 

script/bundle-linux 🔗

@@ -43,7 +43,7 @@ cargo build --release --target "${target_triple}" --package zed --package cli
 
 # Strip the binary of all debug symbols
 # Later, we probably want to do something like this: https://github.com/GabrielMajeri/separate-symbols
-strip "target/${target_triple}/release/Zed"
+strip "target/${target_triple}/release/zed"
 strip "target/${target_triple}/release/cli"
 
 suffix=""
@@ -57,13 +57,13 @@ temp_dir=$(mktemp -d)
 zed_dir="${temp_dir}/zed$suffix.app"
 
 # Binary
-mkdir -p "${zed_dir}/bin"
-cp "target/${target_triple}/release/Zed" "${zed_dir}/bin/zed"
-cp "target/${target_triple}/release/cli" "${zed_dir}/bin/cli"
+mkdir -p "${zed_dir}/bin" "${zed_dir}/libexec"
+cp "target/${target_triple}/release/zed" "${zed_dir}/libexec/zed-editor"
+cp "target/${target_triple}/release/cli" "${zed_dir}/bin/zed"
 
 # Libs
 find_libs() {
-    ldd target/${target_triple}/release/Zed |\
+    ldd target/${target_triple}/release/zed |\
     cut -d' ' -f3 |\
     grep -v '\<\(libstdc++.so\|libc.so\|libgcc_s.so\|libm.so\|libpthread.so\|libdl.so\)'
 }

script/bundle-mac 🔗

@@ -10,8 +10,6 @@ local_arch=false
 local_only=false
 local_install=false
 bundle_name=""
-zed_crate="zed"
-binary_name="Zed"
 
 # This must match the team in the provisioning profile.
 APPLE_NOTORIZATION_TEAM="MQ55VZLNZQ"
@@ -80,10 +78,10 @@ local_target_triple=${host_line#*: }
 
 if [ "$local_arch" = true ]; then
     echo "Building for local target only."
-    cargo build ${build_flag} --package ${zed_crate} --package cli
+    cargo build ${build_flag} --package zed --package cli
 else
     echo "Compiling zed binaries"
-    cargo build ${build_flag} --package ${zed_crate} --package cli --target aarch64-apple-darwin --target x86_64-apple-darwin
+    cargo build ${build_flag} --package zed --package cli --target aarch64-apple-darwin --target x86_64-apple-darwin
 fi
 
 echo "Creating application bundle"
@@ -91,7 +89,7 @@ pushd crates/zed
 channel=$(<RELEASE_CHANNEL)
 popd
 
-pushd crates/${zed_crate}
+pushd crates/zed
 cp Cargo.toml Cargo.toml.backup
 sed \
     -i .backup \
@@ -186,7 +184,7 @@ function prepare_binaries() {
         target/${architecture}/${target_dir}/Zed.dwarf.gz \
         "$channel/Zed-$version-${architecture}.dwarf.gz"
 
-    cp target/${architecture}/${target_dir}/${binary_name} "${app_path}/Contents/MacOS/${zed_crate}"
+    cp target/${architecture}/${target_dir}/zed "${app_path}/Contents/MacOS/zed"
     cp target/${architecture}/${target_dir}/cli "${app_path}/Contents/MacOS/cli"
 }
 
@@ -207,7 +205,7 @@ function sign_binaries() {
     download_git "${architecture}" "${app_path}/Contents/MacOS/git"
 
     # Note: The app identifier for our development builds is the same as the app identifier for nightly.
-    cp crates/${zed_crate}/contents/$channel/embedded.provisionprofile "${app_path}/Contents/"
+    cp crates/zed/contents/$channel/embedded.provisionprofile "${app_path}/Contents/"
 
     if [[ -n "${MACOS_CERTIFICATE:-}" && -n "${MACOS_CERTIFICATE_PASSWORD:-}" && -n "${APPLE_NOTARIZATION_USERNAME:-}" && -n "${APPLE_NOTARIZATION_PASSWORD:-}" ]]; then
         echo "Signing bundle with Apple-issued certificate"
@@ -223,8 +221,8 @@ function sign_binaries() {
         /usr/bin/codesign --deep --force --timestamp --sign "Zed Industries, Inc." "${app_path}/Contents/Frameworks/WebRTC.framework" -v
         /usr/bin/codesign --deep --force --timestamp --options runtime --sign "Zed Industries, Inc." "${app_path}/Contents/MacOS/cli" -v
         /usr/bin/codesign --deep --force --timestamp --options runtime --sign "Zed Industries, Inc." "${app_path}/Contents/MacOS/git" -v
-        /usr/bin/codesign --deep --force --timestamp --options runtime --entitlements crates/${zed_crate}/resources/zed.entitlements --sign "Zed Industries, Inc." "${app_path}/Contents/MacOS/${zed_crate}" -v
-        /usr/bin/codesign --force --timestamp --options runtime --entitlements crates/${zed_crate}/resources/zed.entitlements --sign "Zed Industries, Inc." "${app_path}" -v
+        /usr/bin/codesign --deep --force --timestamp --options runtime --entitlements crates/zed/resources/zed.entitlements --sign "Zed Industries, Inc." "${app_path}/Contents/MacOS/zed" -v
+        /usr/bin/codesign --force --timestamp --options runtime --entitlements crates/zed/resources/zed.entitlements --sign "Zed Industries, Inc." "${app_path}" -v
 
         security default-keychain -s login.keychain
     else
@@ -243,7 +241,7 @@ function sign_binaries() {
         # - get a signing key for the MQ55VZLNZQ team from Nathan.
         # - create your own signing key, and update references to MQ55VZLNZQ to your own team ID
         # then comment out this line.
-        cat crates/${zed_crate}/resources/zed.entitlements | sed '/com.apple.developer.associated-domains/,+1d' > "${app_path}/Contents/Resources/zed.entitlements"
+        cat crates/zed/resources/zed.entitlements | sed '/com.apple.developer.associated-domains/,+1d' > "${app_path}/Contents/Resources/zed.entitlements"
 
         codesign --force --deep --entitlements "${app_path}/Contents/Resources/zed.entitlements" --sign ${MACOS_SIGNING_KEY:- -} "${app_path}" -v
     fi
@@ -364,9 +362,9 @@ else
     app_path=target/release/$(basename "$app_path_x64")
     lipo \
         -create \
-        target/{x86_64-apple-darwin,aarch64-apple-darwin}/${target_dir}/${binary_name} \
+        target/{x86_64-apple-darwin,aarch64-apple-darwin}/${target_dir}/zed \
         -output \
-        "${app_path}/Contents/MacOS/${zed_crate}"
+        "${app_path}/Contents/MacOS/zed"
     lipo \
         -create \
         target/{x86_64-apple-darwin,aarch64-apple-darwin}/${target_dir}/cli \

script/install.sh 🔗

@@ -80,7 +80,12 @@ linux() {
     mkdir -p "$HOME/.local/bin" "$HOME/.local/share/applications"
 
     # Link the binary
-    ln -sf ~/.local/zed$suffix.app/bin/cli "$HOME/.local/bin/zed"
+    if [ -f ~/.local/zed$suffix.app/bin/zed ]; then
+        ln -sf ~/.local/zed$suffix.app/bin/zed "$HOME/.local/bin/zed"
+    else
+        # support for versions before 0.139.x.
+        ln -sf ~/.local/zed$suffix.app/bin/cli "$HOME/.local/bin/zed"
+    fi
 
     # Copy .desktop file
     desktop_file_path="$HOME/.local/share/applications/${appid}.desktop"

script/zed-local 🔗

@@ -121,10 +121,10 @@ if (user) {
 }
 
 let buildArgs = ["build"];
-let zedBinary = "target/debug/Zed";
+let zedBinary = "target/debug/zed";
 if (isReleaseMode) {
   buildArgs.push("--release");
-  zedBinary = "target/release/Zed";
+  zedBinary = "target/release/zed";
 }
 
 try {