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

Cross-platform justfiles #531

Closed
casey opened this issue Nov 13, 2019 · 44 comments
Closed

Cross-platform justfiles #531

casey opened this issue Nov 13, 2019 · 44 comments

Comments

@casey
Copy link
Owner

casey commented Nov 13, 2019

It would be useful to be able to write single justfiles that work on a variety of systems.

Concretely, justfile authors should be able to write a single justfile that can be deployed on different platforms, where the same command (i.e. just build) can be used to invoke the justfile and run a particular recipe or set of recipes.

I just released version v0.5.0 (see #530), which allows setting the shell used to interpret backticks and recipe lines on a per-justfile basis. This allows writing justfiles that work on platforms where sh is not available, but it still requires writing a different justfile for each different platform.

This feature has been discussed before, in particular in #326 and #161, however those issues have been addressed and closed, so I'm opening this issue to track the feature more directly.

The design which I currently favor is to add annotations to recipes that control:

  • Which shell is used: e.g "this recipe should be run with cmd.exe /c instead of sh -cu"
  • In which circumstances a recipe should run: e.g. "this recipe should be run when sh is not available"

Example:

[sh]
foo:
  ls /

[powershell]
foo:
  dir C:\\

Since this feature is primarily intended to make just usable when sh is not available, it seems like different versions of the same recipe should be selected not based on which OS just is running on, but rather whether or a particular shell is available. (Since windows machines may have sh, and if sh is available it is probably desirable to use it.)

I suggest initially starting with three annotations, sh, powershell, and cmd. Each annotation has an associated test, which, if multiple recipes have the same name but different annotations, just will use to select which version should run.

annotation test shell
sh sh -cu echo OK sh -cu
cmd cmd.exe /c Echo OK cmd.exe /c
powershell powershell -c "Write-Host OK" powershell -c

Recipes with no annotation will default to sh, to preserve backwards compatibility.

I think the above design is solid, but there are still a couple of unresolved questions:

  • Should it be possible for the user to override recipe selection from the command line, e.g. to run a powershell-annotated recipe even if sh is available? If so how?
  • Should it be possible to specify that recipe selection should be based on OS, and not shell availability? If so, how is OS-detection performed? If a recipe must run OS-specific commands, for example, it might be desirable to select recipe variants based on OS, and not shell availability, and even have a windows-specific recipe that still uses sh.
@runeimp
Copy link

runeimp commented Nov 13, 2019

Thank you for all the work your putting into cross-platform support! I think the annotations option above it a good solution that doesn't require writing your own scripting language. The update in v0.5.0 is great for people in organizations that may need to use work cross-platform and have the option of specifying organization wide to use something specific like REXX, or Python, etc. Also for individual developers that work cross-platform but don't need users to utilize the Justfile.

I think it would be reasonable and legible to specify the annotation as-is affixed to the recipe name. So something like just build would trigger version with the first annotated recipe with system support. While something like just build[cmd] on my Mac (probably within DosBox or similar) would use the [cmd] annotated version of the build recipe. Now how you display that there are annotated versions in just --list might be funky.

1. Sloppy Left (current default)

$ just --list
Available recipes:
    build [sh][cmd] # Build this code!
    help # Things about the project to remember
    test target="defaul" +args="" [sh][cmd][powershell] # Test this code!

2. Comments aligned

$ just --list
Available recipes:
    build [sh][cmd]                                     # Build this code!
    help                                                # Things about the project to remember
    test target="defaul" +args="" [sh][cmd][powershell] # Test this code!

3. Three Columns

$ just --list
Available recipes:
    build                         [sh][cmd]             # Build this code!
    help                                                # Things about the project to remember
    test target="defaul" +args="" [sh][cmd][powershell] # Test this code!

4. Stacked Annotations

$ just --list
Available recipes:
    [sh][cmd]
    build # Build this code!
    help # Things about the project to remember
    [sh][cmd][powershell]
    test target="defaul" +args="" # Test this code!

5. Stacked Annotations with Comments Aligned (my favorite compact)

$ just --list
Available recipes:
    [sh][cmd]
    build                         # Build this code!
    help                          # Things about the project to remember
    [sh][cmd][powershell]
    test target="defaul" +args="" # Test this code!

Number 3 is my favorite. But I always have wide terminals setup. Number 5 is my favorite for compact display.

BTW, thanks for adding cmd.exe support! I'll take it's lightweight over PowerShell for minor scripting any day.

@runeimp
Copy link

runeimp commented Nov 13, 2019

BTW, how hard would it be to just embed JavaScript, Lua, or Neko at this point? I know it was not really an option prior.

@casey
Copy link
Owner Author

casey commented Nov 13, 2019

Thank you for all the work your putting into cross-platform support!

You are most welcome!

…something like just build[cmd] on my Mac (probably within DosBox or similar) would use the [cmd] annotated version of the build recipe…

I'm hesitant to use RECIPE[ANNOTATION], because some shells (for example zsh) interpret [ as a special character. But, there are a lot of other options. Currently, recipe names cannot contain : or ., so another possibility is to use just build.cmd or just build:cmd.

I definitely like the three column display for --list, but agree that it could be problematic for

BTW, thanks for adding cmd.exe support! I'll take it's lightweight over PowerShell for minor scripting any day.

You bet. I think powershell should work too, using set shell := ["poweshell", "-c"], so I'm curious what people use.

BTW, how hard would it be to just embed JavaScript, Lua, or Neko at this point? I know it was not really an option prior.

I can definitely see the value of embedding a scripting language:

  • Scripting languages are less error prone than sh.
  • Scripting languages don't depend on the userland (cp, rm, grep, sed, etc) which is inconsistent between systems.
  • It makes just more of a one-stop-shop, if just is on a machine, you don't also need to think about installing a scripting language in order to run basic scripts on that machine.

The two main things that would stop me are:

  • I wouldn't be sure which language to choose. I think I would choose python, since it's very common, and language is a good choice to switch to when a shell script get's too large. Lua and JavaScript might be easier to embed, but I'm not entirely sure what the value proposition would be, since Lua and JS aren't as big an upgrade over sh for scripting.

  • I would need to be sure that there was a suitable implementation available. Just is a pure-Rust, statically linked binary, so the implementation would need to bundle the language and standard library into a statically linked library. As far as I know, there aren't any python implementations out there which meet this requirement. Also, it shouldn't bloat the binary too much. (Which might disqualify python, given that the standard library is quite large.)

@casey casey added this to the eventually milestone Nov 13, 2019
@runeimp
Copy link

runeimp commented Nov 14, 2019

JavaScript, Lua, Neko, and Python all have good embedding options. Lua and Neko were specifically designed with embedding in mind so have very small VMs.

Language CLI Size Minimum StdLib Notes
JavaScript - Duktape duk ~335 KB libduktape.so 300.6 KB Designed for portability. ECMAScript 5.1+
JavaScript - MuJS mujs ~208 KB libmujs.o 300.1 KB Designed to be embedded. ECMAScript via ECMA-262
JavaScript - QuickJS qjs ~1.2 MB quickjs.o 5.1 MB Designed to be embedded and supports ES2020
JavaScript - V8 ? ? Designed for speed. ECMAScript and WebAssembly
Lua lua ~20 KB liblua.dylib 166.4 KB Designed to be embedded
Neko neko ~20 KB libneko.dylib 164.0 KB Designed to be embedded
Python python ~14 KB libpython3.dylib 2.2 MB Designed for general scripting and supports embedding

Note the numbers above are just for general reference. I'm not even remotely certain of what is needed to implement any of those interpreters.

  • JavaScript
    • Well know to most programmers. Though only Node programmers (and a few other) will have any experience using JS for file IO.
    • C like. Easy to learn by C, C++, ObjectiveC, C#, and most other programmers.
    • Several interpreters to choose from.
  • Lua
    • Like what I've seen of the syntax.
    • Design goal of being very easy for newbies.
    • Lua's standard library is very small. See https://www.lua.org/manual/5.3/manual.html#6 for the latest.
    • The only thing I don't like about it (I have not done a deep dive at all yet) is it uses 1 based indexes for arrays instead of zero based. Also that it's arrays are actually a modified version of their hashtable type. Which can be problematic. Unless that was changed recently.
  • Neko
    • Like what I've seen of the syntax.
    • Design goal of being easy but well conceived for integration with other languages to use the VM.
    • The major part of the Neko standard library (std.ndll) is 208.4 KB and includes everything in https://nekovm.org/doc/libs/ except Mysql, Mod_neko, Regexp, Sqlite, Ui, and ZLib. Unless I'm incorrect in my review of lib/neko
    • Haxe can compile to Neko bytecode.
  • Python
    • Love the syntax.
    • Designed to be easy and extensible. Embedding is not a goal but is supported.

Lots of options but really for light weight and overall performance I can't see beating Neko. Though Lua is a close second with the caveats mentioned. Followed by MuJS, Duktape, etc. simply because of size.

@casey
Copy link
Owner Author

casey commented Nov 14, 2019

Thanks for this comparison! I think that a scripting language wouldn't fully obviate the need for a shell, since most people would probably want to write most recipes in shell, so an embedded scripting language would probably be a separate feature.

That being said, I think Python is the only reasonable choice, since it blows the other languages away in terms of ease of shell-scripting type tasks, and it's widely known. (JavaScript is definitely widely known, but not great for scripting, and Neko and Lua are popular but still somewhat niche.)

@runeimp
Copy link

runeimp commented Nov 14, 2019

No prob. It's something important to me for the project and something I'm trying to learn more about in general. I love Python 3 so no problem with that. I don't know how you can implement it without doubling or tripling the size of just in the process. And that may not matter to you at all, which is completely fine. Just thinking out loud. 👼

@brandonkal
Copy link

brandonkal commented Mar 17, 2020

I don't believe embedding makes sense as it will bloat the binary.

I'll just point out for people looking here that for scripting with TypeScript/JavaScript deno really makes a lot of sense. It handles dependency fetching via normal url imports and handles TypeScript compilation. All you need to do is make sure the deno binary is in the path.

For anything that requires a language runtime, it would just make sense to make the install of that a dependency of the other targets.

@rjsberry
Copy link
Contributor

rjsberry commented Jun 3, 2020

It would be useful to be able to write single justfiles that work on a variety of systems.

I would approach this exactly as I would in Rust with cfg attributes. We should provide attributes to tag recipes that dictate exactly when and how they should be run. Fundamentally, recipe names should be treated as identifiers and must be unique for a given invocation of just to be valid.

In the original example:

[sh]
foo:
  ls /

[powershell]
foo:
  dir C:\\

This should be a syntax error as the recipe names are the same (and there is no conditional attribute to select the recipe based on some precondition). There is no logical way to select which recipe to run.

If there are two versions of some recipe that require different shells, I would argue that they are in fact different recipes. If this was not the case, why would they require different shells? The way to differentiate here should be in name only. By introducing a conditional attribute, recipe selection becomes automatic and is entirely in the control of the user.

Since this feature is primarily intended to make just usable when sh is not available, it seems like different versions of the same recipe should be selected not based on which OS just is running on, but rather whether or a particular shell is available. (Since windows machines may have sh, and if sh is available it is probably desirable to use it.)

I partially agree with this. I don't necessarily think it is always be desirable to use sh if it is available on Windows. This needs to be completely down to the user, and not automatically selected by just.


My preferred design for cross-platform justfiles would look something like:

[conditional(os_family = "unix")]
[shell(sh)]
foo:
  ls /

[conditional(os_family = "windows")]
[shell(powershell)]
foo:
  dir C:\\

This makes it clear that:

  • On Unix-like OS, the first recipe is run
  • On Windows, the second recipe is run
  • Certain versions of recipes can use different shells
  • foo as a recipe is a unique, and depends on the OS family - the target for just foo is non-ambiguous
  • Recipes can contain OS-specific instructions which cannot be accessed from the wrong OS

Technically the [shell(sh)] attribute is not required as this is the default shell. Removing the line should have no effect.

Of course this could ultimately be extended to global variables to override defaults:

[conditional(os_family = "windows")]
set shell := ["powershell.exe", "-c"]

[conditional(os_family = "unix")]
foo:
  ls /

[conditional(os_family = "windows")]
foo:
  dir C:\\

@casey casey removed this from the eventually milestone Jul 2, 2020
@sagiegurari
Copy link

sagiegurari commented Jan 20, 2021

i wonder if duckscript might help you to resolve this issue.
its a really simplified shell like language i wrote and embedded inside cargo-make to give cross platform scripting solution.
it has few advantages:

  • written in rust so can be embedded really easily. just few lines. you can even pass variables/state to the script from outside.
  • its a shell like syntax but MUCH simpler so there is a 0 learning curve.
  • you can embed just the runtime or also the SDK which gives you access to a file system api, http/ftp clients, collection api and more.

disadvantages:

  • maybe too simple for some people. however most scripts inside makefiles are simple.
  • no pipe/redirect capability that people are used to
  • If you include the SDK you will get a LOT of dependencies and increase your binary size as all these capabilities (such as http client with ssl support) require these dependencies. The runtime itself is super small but... just a runtime won't give you much.

As i see it, the binary size issue is the biggest risk here.

@casey
Copy link
Owner Author

casey commented Jan 23, 2021

Duckscript looks very cool, and I definitely like the idea of embedding something like it in Just. I'm unsure about embedding something that isn't sh, and isn't very widely used. Even though duckscript is very simple, I'm not sure if people would want to learn a scripting language just for use with Just.

And even though sh has a lot of sharp corners, it's very nice that the scripts in Just recipes use the same language as the command line, and existing shell scripts, so you can move code between them easily. (I.e. copy commands from a justfile to the command line, or from a shell script into a justfile.)

@sagiegurari
Copy link

makes sense. so I think the only solution to solve cross platform is to make sh env setup easier on windows.
Maybe automate installation of git for windows for example by exposing it as a just sub command (on just for windows version only of course).

@kotx
Copy link

kotx commented Aug 15, 2021

pwsh/PowerShell is good at being cross-platform, but it might have a slightly alien or overly-verbose syntax to users of sh etc. And Linux users most likely don't have it anyway. But it might be an option?

@casey
Copy link
Owner Author

casey commented Aug 15, 2021

pwsh/PowerShell is good at being cross-platform, but it might have a slightly alien or overly-verbose syntax to users of sh etc. And Linux users most likely don't have it anyway. But it might be an option?

PowerShell would definitely be a reasonable option, although the unfamiliarity of most users is a negative. A big part of this, though, is also shipping cross-platform versions of the binaries that people call, since that's a big part of most scripts. So I'm basically waiting for a pure-rust version of something like busybox that includes a built-in sh.

@runeimp
Copy link

runeimp commented Aug 23, 2021

Are you currently aware of any such projects Casey? Even if they are just starting and may not see the light of day?

@casey
Copy link
Owner Author

casey commented Aug 23, 2021

@runeimp I think the closest thing I know of is uutils/coreutils. It's a reimplementation, in rust, of much of the unix userland, and can be built into a busybox-style binary. I don't think they have a shell yet though.

@markmmm
Copy link

markmmm commented Nov 13, 2021

I am mostly aligned with @rjsberry but I also think I am missing something :)
We already have the ability to specify shell's using a shebang - or setting the default interpreter - so the [sh] attribute seems to overlap with that and potentially confusing.

Personally the requirements I think of are:

  1. Be able to implement a recipe differently for different OS's (which shell I use is either not related - or based on convenience)
  2. Be able to have different recipes on different OS's

For example I currently have the following justfile:

sanity-tests: && sanity-tests-win
    pytest sanity_tests --confcutdir sanity_tests 
    python modules/paths/paths_b/path.py

sanity-tests-win:
	#!/usr/bin/env python 
	import sys, subprocess
	if sys.platform == "win32":
		subprocess.check_call("py -3 modules/windows_registry/test_reg2.py")

I was thinking that maybe this could look like

sanity-tests: && sanity-tests-win
    pytest sanity_tests --confcutdir sanity_tests 
    python modules/paths/paths_b/path.py

[conditional(os_family = "windows")]
sanity-tests-win:
    py -3 modules/windows_registry/test_reg2.py

Though I haven't thought carefully whether && sanity-tests-win should be an error on non-Window's platforms so maybe "safer" would be the following

_sanity-tests-common:
    pytest sanity_tests --confcutdir sanity_tests 
    python modules/paths/paths_b/path.py

[conditional(os_family != "windows")]
sanity-tests: sanity-tests-common
    pass  # can I create an empty recipe?

[conditional(os_family = "windows")]
sanity-tests: sanity-tests-common
    py -3 modules/windows_registry/test_reg2.py

@runeimp
Copy link

runeimp commented Nov 13, 2021

@markmmm I believe the whole point of the shell support issue is largely that Windows does not support Bourne shell (sh) in any form out-of-the box. Yet Bourne shell itself or another shell that can emulate it (ash, bash, ksh, zsh, etc.) is supported on almost every other OS that vaguely resembles common usage. So basic flow control and logic scripted in Bourne shell is (other than Windows) extremely portable. So if we could embed sh into Just we could all just write Bourne shell script logic once in Just and utilize them everywhere without the need for anyone to install Linux Subsystem for Windows, Git for BASH, Cygwin, MSYS, etc. And if you don't like Bourne shell script you can already always use something else. You'll have have to ensure support is available on every OS your Justfile is deployed too or write two entirely different recipes per task you want just to handle. One for Windows, and one for everything else. Or possibly one for each OS if you really have some very special needs per OS.

@casey
Copy link
Owner Author

casey commented Nov 13, 2021

One problem though that needs to be kept in mind is that even if we embed a shell in just, the userlands on different OSs are totally different, so the commands that you're running aren't cross platform. We'd probably have to embed something like busybox.

@markmmm
Copy link

markmmm commented Nov 15, 2021

Example requirement: I want to delete a file (e.g. user-configuration.json) that is located in different locations on Win/Mac/Linux, on Windows I would like to additionally remove a registry entry

  • of course there are workarounds, like env-vars etc - but now a just-file stands on it's own - which is one of benefits if you ask me.
  • while the difference between rm / rd will be solved by the shell - the separate paths won't be. I don't know much about busy-box - but I assume that can only help when the paths in question are relatively 'standard')
  • By having the ability to specify a rule as OS/OS Family specific I could handle those differences.

Embedding shell solves one part of the problem for people who want to use shell across all platforms - but it wouldn't support the full title of this issue.
I would also say that users who want to use sh have a route available to them right now - they can ensure that sh is installed. But I would question whether just should "bless" (and integrate) any particular shell.

@runeimp
Copy link

runeimp commented Nov 16, 2021

@markmmm I like the conditional logic tags. Though you can already do this with {{os()}} as part of the conditional logic within a recipe. For example

sanity-tests-common:
	pytest sanity_tests --confcutdir sanity_tests 
	python modules/paths/paths_b/path.py

sanity-tests: sanity-tests-common
	#!/bin/sh
	if [ '{{os()}}' == 'windows' ]; then
		py -3 modules/windows_registry/test_reg2.py
	fi

@runeimp
Copy link

runeimp commented Nov 16, 2021

You may even be able to do it with with the conditional logic built into just. I've not been able to figure out how to use it that way myself, sorry @casey, but if it's possible that would be a cross-platform option as well.

@hustcer
Copy link
Contributor

hustcer commented Nov 16, 2021

I'm using just and nushell to run cross-platform scripts, they work pretty well

@runeimp
Copy link

runeimp commented Nov 17, 2021

@hustcer do you mean nushell? If so that looks rather interesting. @casey might that be a good candidate for an embed shell/scripting language as it is written in Rust? Looks to be heavily inspired by PowerShell which many POSIX types will likely distain. But maybe not?

@kotx
Copy link

kotx commented Nov 17, 2021

Looks good to me but I'm a PowerShell user :^)
Edit: also this feature looks cool

@runeimp
Copy link

runeimp commented Nov 17, 2021

I've grown to appreciate PowerShell over the last year. I think that in general it's verbose as hell. But I like many of the ideas behind it. And yes, that feature in nushell is awesome! 😀

@hustcer
Copy link
Contributor

hustcer commented Nov 17, 2021

@hustcer do you mean nushell? If so that looks rather interesting. @casey might that be a good candidate for an embed shell/scripting language as it is written in Rust? Looks to be heavily inspired by PowerShell which many POSIX types will likely distain. But maybe not?

Yes! That's it. It's modern, elegant and powerful, I have use it for a few months. The latest version 0.40.0 is compressed
to about 50M with a lot of pluggins, and nushell without pluggins could be much more smaller. However, nushell is not production ready, and will have a breaking change after using the new engine-q in the future.

@hustcer
Copy link
Contributor

hustcer commented Nov 17, 2021

I've grown to appreciate PowerShell over the last year. I think that in general it's verbose as hell. But I like many of the ideas behind it. And yes, that feature in nushell is awesome! 😀

@runeimp Try it and you will love it, I enjoy it very much! Actually, I have change all my scripts from sh to nushell.

@runeimp
Copy link

runeimp commented Nov 17, 2021

Yeah, it would likely need to reach version 1 before @casey would consider using it. Looks promising though.

@torkleyy
Copy link

Regarding an embedded scripting language, have you considered rhai? It seems the easiest option to embed in Rust.

@casey
Copy link
Owner Author

casey commented Dec 22, 2021

@torkleyy rhai looks good, although the fact that it's not popular is a big of a downside, since most developers using just would have to learn it. Currently I think using python via https://github.com/RustPython/RustPython is the best bet.

@r12f
Copy link

r12f commented Jul 10, 2022

i wonder if there is any more update on this support. currently, my solution is set default shell to powershell 7.

@casey
Copy link
Owner Author

casey commented Jul 10, 2022

Nothing new to report, I've been working on other projects, so I'm not tackling major new features, although if someone wants to tackle this, I'm happy to help.

@hustcer
Copy link
Contributor

hustcer commented Jul 11, 2022

Nushell has introduced the new engine in v0.60, and just released v0.65, It's good enough for me currently. I had use it for more then half an year, and found no compatible issue for mac / Linux and Windows.

@r12f
Copy link

r12f commented Jul 11, 2022

Yea! Using a cross platform shell sounds like a plan! Since I am mostly using azure pipeline for build etc, Powershell 7 seems to be working good everywhere, so I will stick on that for now. Thanks for the quick check and help! @casey @hustcer

@runeimp
Copy link

runeimp commented Aug 2, 2022

Starlark maybe? #537 (comment)

@casey
Copy link
Owner Author

casey commented Nov 3, 2022

Now that #1387 has landed, and shells are fully configurable, I think this issue can probably be closed. Truly cross platform justfiles would depend on two things:

  • A cross-platform scripting language.
  • A cross-platform userland, i.e., cross-platform versions of all the commands that you might want to call in a jusfile.

These two things are ultimately outside of the scope of the project. I'm hoping that eventually, they'll appear, ideally in the form of sh and busybox clones written in Rust that can be integrated into the just binary.

@casey casey closed this as completed Nov 3, 2022
@MostHated
Copy link

MostHated commented Feb 16, 2023

I know this is an older issue technically, but PowerShell core is cross-platform and has built-in if ($isWindows) and if ($isLinux), etc. I use it heavily on linux, then also Windows for work, and a large majority of my scripts are x-platform, considering you can also utilize .Net classes for directory/paths like the following, which will work on most any system:

 # --| Example -----
if ($IsLinux) { & cp ./target/release/myapp ~/.local/bin/ } 

# --| Create directory x-platform ------
# Edit -- Just tested on a Windows VM
# Running 'echo $HOME' from a pwsh.exe prompt returned 'C:\Users\<myUser>'
# So that makes it even easier to just use $HOME in scripts or other vars possibly.

$targetDir = ""
if ($IsWindows) { $targetDir = $env:HOMEPATH } # returns C:\Users\<user>\ 
else { $targetDir = $env:HOME }  # returns /home/<user>

$myApp = "${targetDir}/.myApp"
if (!([System.IO.Directory]::Exists($myApp))) {
    [system.io.directory]::CreateDirectory($myApp)
} 

# This will work on any system Pwsh Core installs on, which is most any. 
# (Win, Mac, Linux: Deb, Ubuntu, Rhel, Alpine, Raspi (Has Arm support), then also via Docker)

So either utilizing that in a justfile, or if a justfile simply had a command annotation of

#[if(linux)]
install: build
    echo linux > stuff

#[if(windows)]
install: build
    cmd /c myWindowCommand.stuff

Not sure id that information is useful at all, but I just wanted to make sure it was known

@r12f
Copy link

r12f commented Feb 17, 2023

Yep, this is what I ended up doing previously as the workaround too. Then later I switched to nushell. I highly recommend to give it a try. The problem with Powershell Core is that on linux and mac, the first launch can be crazily slow from my experience, (as well as the second, maybe the third, but it gets better every time). And on the other side, nushell is quite fast and usually can do most of the work too, unless you need certain specific features that uses powershell modules is way easier.

(don't get me wrong. I love powershell and wrote many powershell modules by myself too. : D i just don't like to wait when running frequently used commands.)

@MostHated
Copy link

MostHated commented Feb 17, 2023

Yep, this is what I ended up doing previously as the workaround too. Then later I switched to nushell. I highly recommend to give it a try. The problem with Powershell Core is that on linux and mac, the first launch can be crazily slow from my experience, (as well as the second, maybe the third, but it gets better every time). And on the other side, nushell is quite fast and usually can do most of the work too, unless you need certain specific features that uses powershell modules is way easier.

(don't get me wrong. I love powershell and wrote many powershell modules by myself too. : D i just don't like to wait when running frequently used commands.)

Edit: I have been playing around with nushell. Seems interesting, and for the most part capable. Are you just listing it as a build dependency then along with 'just', and then that is that, they have everything needed, more or less?

Thats interesting. On linux, I find Pwsh to be not only way faster than on Window, but faster than zsh (both are setup with the same imports and scripts, minus zsh itself, things like starship, my update display, etc) I try and use as few shell specific plugins as possible so I can set both up the same.

In this below, zsh is what I am loading in the first window, then pwsh in the second, both with full profiles. It's even faster if you launch with -noprofile -nologo, etc.

(I realize this is purely anecdotal, but it's more to vouch for the fact that I would no longer consider it "slow", compared to how pwsh was a year, and especially two years ago. Dotnet 6-7 had brought huge speed improvements to both startup and overall runtime performance)
Kooha-2023-02-16-18-04-42

I started implementing some things in Just with pwsh last night and it has been working pretty well, minus some small things here and there being my first try using it with just.

That said, I have nothing against nushell, I just personally am a huge fan of pwsh on linux, being I get the crazy power of objects seamlessly mixed with bash, without having to pick up something else completely new.

What would be super nice is to be able to use more than one language in a single command (if there is no chance of the one example in my prior post #[if(linux)])

This is more just throwing things against the wall, but really, the main "want" is to simply have one command to run, regardless of the system it is on, even if I have to write the execution differently.

install: build
    ({pwsh
        # Items here are ran in pwsh, if it exists, else fall back to next shell 
        echo "Using pwsh: $PSVersionTable"
    }),
    ({sh
        # Items here are ran with sh, if pwsh was not ran, and sh exists.
        TEXTTEST="using sh!" && echo $TEXTTEST
    })

Something along those lines, then no matter which system, the same command can be ran from a user point of view to build/install, without having to have separate commands for each system, which it seems like might be necessary with the `shell := [""] commands?

@r12f
Copy link

r12f commented Feb 17, 2023

Wow! That's good for you! Somehow pwsh needs about 1-2 minutes to launch on my work mbp, so I switched to nushell in later projects. But I am still using it on my personal fun project (w/ -noprofile and etc). And yes, I am a huge fan as well, the object pipeline and all the bash aliases just makes things so much easier and less chance to wrong.

And having a fallback will be great indeed. What I am doing right now is that I have a init command for each system just to get things installed, e.g. just init-win, just init-linux and just init-mac. It uses whatever the shell that runs on those os, but the problem is that, the users has to remember to which command to run, instead of blindly run just init. If certain fallback mechanism is there, it will make this experience even better.

@runeimp
Copy link

runeimp commented Feb 17, 2023

@r12f you could do something like

# Initialize OS Setup
init:
	just _init-{{os()}}

_init-linux:
	# Linux init

_init-macos:
	# macOS init

_init-windows:
	# Windows init

@r12f
Copy link

r12f commented Feb 17, 2023

Wow! This is a really nice trick! Thanks a lot for sharing! 👍👍👍

@MostHated
Copy link

MostHated commented Feb 17, 2023

Edit: Oh... @runeimp way is much easier, lol.


I got way off track from what I wanted to accomplish this evening, but I wanted to see if I could create a single command that might work for both pwsh and sh, and it looks like I have something figured out. It is in no way practical, or even worth the effort outside of my need to accomplish this, lol, but this is essentially what I came up with:

shebang := if os() == 'windows' {
  'pwsh.exe'
} else {
  '/usr/bin/bash'
}

set shell := ["/usr/bin/env", "bash" ,"-c"]
set windows-shell := ["pwsh.exe","-NoLogo", "-noprofile", "-c"]

sh-pwsh:
    #!{{shebang}}
    #!/bin/bash
    # ---------
    # - Shell -
    # ---------
    echo --% >/dev/null;: ' | out-null
    <#'
    function ShellRunner {
      echo -e "\n\nRunning: ShellRunner\n\n" || true
      just install
    }
    # Pwsh ignores this whole block, but bash will run it
    "ShellRunner"
    exit #> # Bash exits here
    # ---------
    # - Pwsh --
    # ---------
    echo "`n`nRunning PwshRunner`n`n"
    function PwshRunner {
      $homeDir = $HOME
      $originalExe = "ncm-rs.exe"
      $newExe = "ncm.exe"
      $targetPath = "./target/release/"
      $releasePath = "./target/release/ncm.exe"
      $installPath =  "/.local/bin/"
      # --
      if (test-path $releasePath) { rm $releasePath }
      $(cargo test)
      $(cargo build --release)
      mv "$targetPath/$originalExe" "$targetPath$newExe"
      echo "Moving $releasePath to $homeDir/$installPath"
      cp "$releasePath" "${homeDir}/$installPath"
    }
    # --------
    PwshRunner
2023-02-16.22-28-51.mp4

@runeimp
Copy link

runeimp commented Feb 17, 2023

@MostHated I often need cross-platform compatibility to simplify Justfile maintenance. I work on Mac but most of the systems I deploy code to are Windows so I simplified things by installing PowerShell Core on my Mac and default to PowerShell Desktop on the Windows systems as they always have that version installed.

set windows-shell := ["powershell", "-c"] # To use PowerShell Desktop instead of Core on Windows
set shell := ["pwsh", "-c"] # PowerShell Core (Multi-Platform)

# The .exe extension is not required
shebang := if os() == 'windows' {
	'powershell'
} else {
	'/usr/bin/env pwsh'
} # Switch 'powershell' with 'pwsh' for PowerShell Core

# PS Hello World
@hello:
	#!{{shebang}}
	Write-Host "Shebang: {{shebang}}"
	Write-Host "Hello, world from PowerShell!"
	# Do other scripty things...

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

No branches or pull requests