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

instead of `run` subcommand, treat first positional argument as the zig run target #1505

Open
andrewrk opened this Issue Sep 12, 2018 · 3 comments

Comments

Projects
None yet
4 participants
@andrewrk
Member

andrewrk commented Sep 12, 2018

Here's one use case:

#!/usr/bin/env zig

const std = @import("std");

pub fn main() !void {
    // If this program is run without stdout attached, exit with an error.
    var stdout_file = try std.io.getStdOut();
    // If this program encounters pipe failure when printing to stdout, exit
    // with an error.
    try stdout_file.write("Hello, world!\n");
}

Using env is the portable way to use an interpreter for a shebang line, and Linux passes everything after the space as a single argument, which means we can't do something like #!/usr/bin/env zig run.

As an example, on my home OS - NixOS - the /usr/bin directory is empty except for env:

andy@xps ~> ls /usr/bin/
env

The other use case is that Zig now enters the playing field, alongside commands like these:

  • python foo.py [args]
  • node foo.js [args]
  • ruby foo.rb [args]
  • perl foo.pl [args]
  • etc

This command would be unambiguous: args would be parsed normally until the first positional argument. If the first positional argument does not have a / in it then it is treated as a subcommand. Otherwise it is treated as a zig run target, and the rest of the arguments are forwarded to the program. If the file does not exist, a helpful error message could suggest that perhaps the user meant to try one of the subcommands, or they meant to create the file and execute it.

Here is a demonstration that it would be unambiguous:

  • Here we are currently printing the usage text, because it is currently not meaningful to pass a positional argument with no subcommand:
$ ./zig test.zig 
Unrecognized command: test.zig
Usage: ./zig [command] [options]
...
  • Here we try to make a file the same name as a subcommand, but we cannot get linux to pass us the path without the /:
andy@xps ~/tmp> cat build-obj
#!/usr/bin/env echo
andy@xps ~/tmp> chmod +x build-obj
andy@xps ~/tmp> build-obj
build-obj: command not found
andy@xps ~/tmp> ./build-obj
./build-obj

So as long as no zig subcommands have a / in them, this would be unambiguous. This seems like a reasonable restriction.

cc @tiehuis - I think we discussed this before and ended up on status quo, but I'd like to bring it up one more time.

@andrewrk andrewrk added this to the 0.4.0 milestone Sep 12, 2018

@tgschultz

This comment has been minimized.

Contributor

tgschultz commented Sep 12, 2018

Another option: symlink zig-run to zig, have zig check arg[0] to see if it was called as zig-run.

@thejoshwolfe

This comment has been minimized.

Member

thejoshwolfe commented Sep 12, 2018

If the first positional argument does not have a / in it then it is treated as a subcommand. Otherwise it is treated as a zig run target

This is unambiguous in most but not all cases.

Invoking shebang scripts on the PATH uses absolute paths, so this is good:

~/tmp$ echo '#!/usr/bin/env echo' > build-obj
~/tmp$ chmod +x build-obj 
~/tmp$ export PATH=$PATH:$(pwd)
~/tmp$ build-obj 
/home/josh/tmp/build-obj
~/tmp$ cd ..
~$ build-obj 
/home/josh/tmp/build-obj

However, we can get ambiguity by calling execv directly:

~/tmp$ python -c 'import os; os.execv("build-obj", ["blah"])'
build-obj

(I think the ./ prefix to invoke a program in the current directory is just a convention that shell languages follow.)

@mesbahamin

This comment has been minimized.

mesbahamin commented Nov 13, 2018

#!/usr/bin/env zig

As of GNU coreutils v8.30, you can do:

#!/usr/bin/env -S zig run

This feature was added in a recent (2018-04-20) commit to env.c in the
GNU coreutils package, which added the -S or --split-string option. I'm
pretty sure this feature was adopted from FreeBSD's version of env.

From the env man page:

OPTIONS
-S/--split-string usage in scripts
    The  -S  option allows specifing multiple parameters in a script.
    Running a script named 1.pl containing the following first line:

            #!/usr/bin/env -S perl -w -T

    Will execute perl -w -T 1.pl .

    Without the '-S' parameter the script will likely fail with:

            /usr/bin/env: 'perl -w -T': No such file or directory

    See the full documentation for more details.

More examples are available in the GNU coreutils manual.

Note: This only applies to shebangs that use /usr/bin/env, as
--split-string is a feature of env specifically.

@andrewrk andrewrk added the accepted label Nov 21, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment