Repository: a-scie/jump
Pull request #76 (2023-01-26 19:43:02)

Expose a `SCIE_ARGV0` env var to scies.
This complements the existing `SCIE` env var and allows detection of scies that are symlinked.

jsirois: This will be used by scie-pants to implement `pants_from_sources` support via symlink to support the status quo instead of having to use `PANTS_SOURCE=... pants`, although that can be used too.
jsirois: I've got https://github.com/pantsbuild/scie-pants/issues/30 working well with this; so I'll probably charge ahead here within the hour.

Patch:
diff --git a/CHANGES.md b/CHANGES.md
index 54f3113..f7def83 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,5 +1,14 @@
 # Release Notes
 
+## 0.10.0
+
+In addition to the `SCIE` environment variable being exposed to scies, `SCIE_ARGV0` is now exposed
+as well. On Unix systems this value can differ from `SCIE` and can be used to detect the name of the
+scie executable launched by the user. Although the `scie-jump` uses this internally to allow for
+BusyBox style dispatch based on symlinks, exposing `SCIE_ARGV0` allows non BusyBox scies to do the
+same.  See the [packaging guide](docs/packaging.md) for more details on environment variables
+supported by `scie-jump`.
+
 ## 0.9.0
 
 Support is added for specifying an alternate `scie-jump` binary to embed in the scie tip when
diff --git a/Cargo.lock b/Cargo.lock
index ddf47b3..0c061ad 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -370,7 +370,7 @@ dependencies = [
 
 [[package]]
 name = "jump"
-version = "0.9.0"
+version = "0.10.0"
 dependencies = [
  "bstr",
  "byteorder",
@@ -713,7 +713,7 @@ dependencies = [
 
 [[package]]
 name = "scie-jump"
-version = "0.9.0"
+version = "0.10.0"
 dependencies = [
  "bstr",
  "env_logger",
diff --git a/Cargo.toml b/Cargo.toml
index 6ebd1aa..7268ca7 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -5,7 +5,7 @@ members = [
 
 [package]
 name = "scie-jump"
-version = "0.9.0"
+version = "0.10.0"
 description = "The self contained interpreted executable launcher."
 authors = [
     "John Sirois <john.sirois@gmail.com>",
diff --git a/docs/packaging.md b/docs/packaging.md
index b36fc6e..b3ed136 100644
--- a/docs/packaging.md
+++ b/docs/packaging.md
@@ -330,6 +330,27 @@ gains the ability to blindly issue `tail -1 coursier | jq .` to inspect the lift
 scie. Either way though, since the scie is powered by a `scie-jump` in its tip, you can also issue
 `SCIE=inspect coursier` as in the boot-pack example.
 
+## Environment variables
+
+Environment variables can be used both to control a scie from the outside and by code embedded in a
+scie.
+
+Runtime external control variables:
+
++ `SCIE=<command>`: This can be used to execute built-in `scie-jump` utilities like `inspect` and
+  `boot-pack`. Use `SCIE=help <scie path>` to see the built-in `sice-jump` utilities supported.
++ `SCIE_BOOT=<command>`: This can be used to select a non-default command for execution in a scie
+  that defines named commands. One way to discover these is via invoking `SCIE=list <scie path>`.
++ `SCIE_BASE`: This can be used to override the default scie base of `~/.cache/nce` on Linux,
+   `~/Library/Caches/nce` on Mac and `~\AppData\Local\nce` on Windows.
+
+Runtime read-only variables:
+
++ `SCIE`: The absolute path of the scie executable. This can be used to re-execute the scie.
++ `SCIE_ARGV0`: The value of the 1st command line argument. If the scie was launched via a symlink
+  The symlink name will be preserved here whereas it would be resolved in `SCIE` on Unix systems.
+  This can be used to implement BusyBox-style re-direction.
+
 ## Advanced placeholders
 
 Further placeholders you can use in command "exe", "args" and "env" values include:
diff --git a/jump/Cargo.toml b/jump/Cargo.toml
index bddc57c..168b275 100644
--- a/jump/Cargo.toml
+++ b/jump/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "jump"
-version = "0.9.0"
+version = "0.10.0"
 description = "The bulk of the scie-jump binary logic."
 authors = [
     "John Sirois <john.sirois@gmail.com>",
diff --git a/jump/src/lib.rs b/jump/src/lib.rs
index 5a87e0f..c2a05d6 100644
--- a/jump/src/lib.rs
+++ b/jump/src/lib.rs
@@ -171,6 +171,7 @@ pub fn prepare_boot() -> Result<BootAction, String> {
         let process = selected_command.process;
         trace!("Prepared {process:#?}");
         env::set_var("SCIE", current_exe.exe.as_os_str());
+        env::set_var("SCIE_ARGV0", current_exe.invoked_as.as_os_str());
         Ok(BootAction::Execute((
             process,
             selected_command.argv1_consumed,
diff --git a/src/boot.rs b/src/boot.rs
index 04732dd..02314ec 100644
--- a/src/boot.rs
+++ b/src/boot.rs
@@ -19,7 +19,7 @@ pub(crate) fn help(message: String, exit_code: i32) -> ExitResult {
     if code.is_err() {
         Err(code.with_message(message))
     } else {
-        print!("{}", message);
+        print!("{message}");
         code.ok()
     }
 }
diff --git a/src/boot/pack.rs b/src/boot/pack.rs
index 2d674f0..3bcb452 100644
--- a/src/boot/pack.rs
+++ b/src/boot/pack.rs
@@ -37,10 +37,23 @@ fn load_manifest(path: &Path, jump: &Jump) -> Result<(Lift, PathBuf), String> {
     Ok((lift, manifest_path))
 }
 
-#[cfg(target_family = "windows")]
+#[cfg(windows)]
 fn finalize_executable(path: &Path) -> Result<PathBuf, String> {
-    if path.extension().is_none() {
-        let exe = path.with_extension(env::consts::EXE_EXTENSION);
+    use std::ffi::OsStr;
+    if let Some(env::consts::EXE_EXTENSION) = path.extension().and_then(OsStr::to_str) {
+        Ok(path.to_path_buf())
+    } else {
+        let exe = path.with_file_name(format!(
+            "{file_name}.{ext}",
+            file_name = path
+                .file_name()
+                .and_then(OsStr::to_str)
+                .ok_or_else(|| format!(
+                    "Failed to determine the file name of {path}",
+                    path = path.display()
+                ))?,
+            ext = env::consts::EXE_EXTENSION
+        ));
         std::fs::rename(path, &exe).map_err(|e| {
             format!(
                 "Failed to rename executable from {path} to {exe}: {e}",
@@ -48,12 +61,11 @@ fn finalize_executable(path: &Path) -> Result<PathBuf, String> {
                 exe = exe.display()
             )
         })?;
-        return Ok(exe);
+        Ok(exe)
     }
-    Ok(path.to_path_buf())
 }
 
-#[cfg(not(target_family = "windows"))]
+#[cfg(unix)]
 fn finalize_executable(path: &Path) -> Result<PathBuf, String> {
     use std::os::unix::fs::PermissionsExt;
     let mut perms = std::fs::metadata(path)


Test patch:
diff --git a/examples/node/test.sh b/examples/node/test.sh
index 259ffb4..edcbc4e 100644
--- a/examples/node/test.sh
+++ b/examples/node/test.sh
@@ -31,6 +31,15 @@ for applet in ${applets[*]}; do
   gc "${PWD}/${applet}"
 done
 
+cat <<EOF | ./node --input-type=module -
+import { strict as assert } from 'node:assert';
+import fs from 'node:fs';
+import path from 'node:path';
+
+assert.equal(path.basename(fs.realpathSync(process.env.SCIE)), 'node.js${EXE_EXT}');
+assert.equal(path.basename(process.env.SCIE_ARGV0), 'node${EXE_EXT}');
+EOF
+
 ./npm install cowsay
 gc "${PWD}/node_modules" "${PWD}/package.json" "${PWD}/package-lock.json"