Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[script] annotation #1479

Closed
casey opened this issue Jan 6, 2023 · 12 comments · Fixed by #2259
Closed

[script] annotation #1479

casey opened this issue Jan 6, 2023 · 12 comments · Fixed by #2259

Comments

@casey
Copy link
Owner

casey commented Jan 6, 2023

Shebangs are not supported on all platforms, and a fair amount of hackery to get working. Including calling cygpath on windows if the path contains forward slashes. To work around this, we could add a [script] annotation, which would take a command and arguments. When a [script] recipe was executed, it would be passed as the final argument to the command.

So this recipe:

[script("bash")]
foo:
  echo foo

Would be run like this recipe:

foo:
  #!/usr/bin/env bash
  echo foo

Eventually, we could add a script-interpreter setting, and allow the command to be omitted:

set script-interpreter := ["bash"]

[script]
foo:
  echo foo
@casey casey mentioned this issue Jan 6, 2023
@nk9
Copy link
Contributor

nk9 commented Jan 18, 2023

I can easily see people writing a recipe with conflicting [script] and shebang directives. I guess the shebang would win? Consistency/simplicity seems like a worthwhile goal in the language syntax design.

I don't understand the complexities of using (or maybe it's just implementing) the feature on Windows, but it's really appealing to me to have the language of the recipe content right next to the code itself. Is the current implementation on Windows something that will break?

Just my £.02.

@runeimp
Copy link

runeimp commented Jan 18, 2023

@casey does some magic on Windows systems where you can have a shebang like

recipe:
	#!powershell.exe
	Clear-Host
	Write-Output "Hello from PowerShell"

This is absolutely not acceptable shebang syntax on *nix systems. There the full path to the interpreter or whatever must be included. Unless you have some very special setup like @casey apparently does. 😉

This feature has been part of the Windows version of Just for forever. It will likely never break unless Windows itself does something "special". 😃

@drmacdon
Copy link

@casey The script syntax your suggesting is definitely cleaner than parsing the recipe to extract the shebang interpeter.

But, I was thinking and testing this more after studying the code and wondered why the Unix make_shebang_command can't function more like the Windows make_shebang_command and invoke the shebang.interpreter instead of the script?

The syntax #!nu works on Windows today because it passing nu as the shebang.interpretter and invoking it as the command. But this syntax fails on Unix because it is the script that is invoked falling back to standard process shebang handling forcing the full path to be provided. But does that really add any value if we already know the interpreter?

@casey
Copy link
Owner Author

casey commented Jul 15, 2024

But, I was thinking and testing this more after studying the code and wondered why the Unix make_shebang_command can't function more like the Windows make_shebang_command and invoke the shebang.interpreter instead of the script?

It certainly could function like Windows, and this would fix some cross-platform inconsistencies on Unix OSs, for example, different OSs split shebang lines differently. I would be worried that there would be some change in behavior, so it wouldn't be backwards compatible, but that could be tested for.

@laniakea64
Copy link
Contributor

I would be worried that there would be some change in behavior, so it wouldn't be backwards compatible,

One example -

[unix]
foo:
  #!foo
  foo

Currently, testing on Xubuntu 22.04, this will run a script foo located in the same directory as the justfile. With just's Windows-style shebang handling, this recipe would run whatever foo executable is in $PATH: in most cases there probably won't be such executable, so the recipe would become an error, but if there does happen to be a foo executable on $PATH the recipe could behave quite differently from intended.

Also not sure if/how env shebangs would be affected?

@drmacdon
Copy link

The intent was to run whatever it found on PATH which is similar to how the set shell := ['nu', '-c'] and Window's shebangs both resolve today.

I did miss that #!foo (relative path) works on linux today. It appears to fail on macos unless I include . in the PATH but that was not necessary on Ubuntu to resolve a local executable.

On macos, I did do a few tests with env:

test1: (test2 "witharg1") (test3 "witharg2")
    #!nu
    let var = "Hello, world!"
    print $var

test2 arg="default":
    #!env nu
    let var = "Hello, world! {{arg}}"
    print $var

test3 arg="default":
    #!/usr/bin/env nu
    let var = "Hello, world! {{arg}}"
    print $var

On the current version the first two fail (as expected) and the last one succeeds. When using Window's shebang approach they all succeed (as expected) since nu is in my PATH.

It is closer to how a theoretical [shell("nu")] would end up necessarily functioning and would be less complex if they all shared the same PATH resolution approach. Script using an env shebang today are likely doing so largely to achieve PATH resolution.

@old-pigeon
Copy link

Coming over from #2253 I would love this change, especially with the set script-interpreter thing to avoid duplicating the invocation everywhere.

@drmacdon
Copy link

drmacdon commented Jul 17, 2024

This might have been plausible work around which works on Macos,

default:
    #!{{just_executable()}} --command nu
    let var = "Hello World"
    print $var

but fails on Windows because of the current Windows shebang argument handling. The argument passed to just.exe ends up being "--command nu" and not ["--command", "nu"].

@drmacdon
Copy link

drmacdon commented Jul 17, 2024

Just saw @runeimp's approach to this from #1549 (comment) which works. Not elegant, but way nicer then escaping multiline scripts to pretend they are one line.

For nu cross-platform this is sufficient

set shell := ['nu', '-c']
shebang := `$nu | get current-exe`

default:
  #!{{shebang}}
  let var = "Hello World"
  print $var

@laniakea64
Copy link
Contributor

The intent was to run whatever it found on PATH

Sorry I wasn't clear. Was referring to this - https://just.systems/man/en/chapter_43.html#writing-recipes-in-other-languages

Shebang line splitting is operating system dependent. When passing a command with arguments, you may need to tell env to split them explicitly by using the -S flag:

Not sure if/how just using its Windows-style shebang handling on all platforms would affect when -S option of env is needed, what part of the shebang -S splits and the resulting effect,...?

@casey
Copy link
Owner Author

casey commented Jul 18, 2024

Done in #2259! I made it unstable, so you'll need --unstable, set unstable or export JUST_UNSTABLE=1.

I created #2260 to track stabilization and additions.

@casey
Copy link
Owner Author

casey commented Jul 18, 2024

Released in 1.32.0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants