Stop relying on binary location to be in `libexec` on Linux (#13374)

Josef Zoller and Conrad Irwin created

This fixes #13360 by adding fallback directories that are searched by
the CLI if the main executable cannot be found in the `libexec`
directory.

Release Notes:

- Added the fallback directories `lib/zed` and `lib/zed-editor` for the
main executable search in the CLI
([#13360](https://github.com/zed-industries/zed/issues/13360)).

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>

Change summary

crates/cli/src/main.rs        | 23 ++++++++++++-----------
docs/src/development/linux.md |  2 +-
2 files changed, 13 insertions(+), 12 deletions(-)

Detailed changes

crates/cli/src/main.rs 🔗

@@ -196,23 +196,24 @@ mod linux {
     impl Detect {
         pub fn detect(path: Option<&Path>) -> anyhow::Result<impl InstalledApp> {
             let path = if let Some(path) = path {
-                path.to_path_buf().canonicalize()
+                path.to_path_buf().canonicalize()?
             } else {
                 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("libexec").join("zed-editor").canonicalize() {
-                    Ok(path) => Ok(path),
-                    // 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),
-                    },
-                }
-            }?;
+                // libexec is the standard, lib/zed is for Arch (and other non-libexec distros),
+                // ./zed is for the target directory in development builds.
+                let possible_locations =
+                    ["../libexec/zed-editor", "../lib/zed/zed-editor", "./zed"];
+                possible_locations
+                    .iter()
+                    .find_map(|p| dir.join(p).canonicalize().ok().filter(|path| path != &cli))
+                    .ok_or_else(|| {
+                        anyhow!("could not find any of: {}", possible_locations.join(", "))
+                    })?
+            };
 
             Ok(App(path))
         }

docs/src/development/linux.md 🔗

@@ -89,7 +89,7 @@ Thank you for taking on the task of packaging Zed!
 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`.
+* 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`. As some linux distributions (notably Arch) discourage the use of `libexec`, you can also put this binary at `$PATH/to/cli/../../lib/zed/zed-editor` (e.g. `~/.local/lib/zed/zed-editor`) instead.
 * 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. This file should also be renamed to `$APP_ID.desktop`, so that the file [follows the FreeDesktop standards](https://github.com/zed-industries/zed/issues/12707#issuecomment-2168742761).
 * 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).