As someone who uses neither Just nor Make, I'm trying to understand the value proposition.
From what I can tell, Just accomplishes what a set of scripts in a directory could also do, but in a more ergonomic fashion. Since the justfile is a single file, you're not cluttering up your directory. You don't have to look for all *.sh files, but can instead do a "just --list". And using "just target" is probably easier to type than "./target.sh" since the just version has no punctuation.
What are some of the other benefits of Just that makes it superior to "a set of scripts in a directory"?
I also love just, but I try to restrict my usage to projects that don't have larger communities or user populations, as the getting it installed aspect is nowhere near universal as make. my favorite is mixing scripting languages and shell in the same file, albeit its got some rope.. but its productive and intuitive. https://github.com/kapilt/aws-sdk-api-changes/blob/master/ju...
You don’t need to use all of make’s features (I’ve been using it for something like 30 years and still can’t fathom sets of it). But TBH I just don’t see anything in those Justfiles I couldn’t do with make without needing to do anything special…
I've been using Just since at least mid-2018 (that's the oldest commit I can find), and we're using it on almost every single project at $WORK. It's easier to comprehend than make, doesn't have random GNUisms or BSDisms, it's easier to work with than a collection of random 5-10 line scripts, and despite being a bespoke tool, it's intuitive enough to a point where it immediately feels familiar.
Adding .PHONY targets and so forth is a bit inelegant, but I can share a makefile with confidence that any Linux/Mac OS/BSD user can use it without needing additional software, and I will never have to worry about make becoming unavailable or no longer maintained. Just my personal opinion.
> but I can share a makefile with confidence that any Linux/Mac OS/BSD can use it without needing any additional software
I'm sure you're kidding, but in the case that you're not: Make portability is gross.
We have ./configure steps precisely because Make is difficult w.r.t. portability, but even if that wasn't the case and you were just using make as a command executor: you still have enormous warts.
Oh, and yeah, you'd need whatever additional software too.
Be it: headers, linters, formatters, libraries or test suites that you've bundled.
Valgrind is a popular make target, but "Make" does not bring in valgrind (for example).
Honestly one of the most backwards things the Go community did was adopt "Make", it's so kludgey even as a pure command executor that I can't really take anyone seriously who argues for it's use.
I'm not saying "Just" is a replacement, I don't know what is.
Most of the arguments for using Make boil down to "I enjoy typing `make <something>`" and "you probably have it installed already?".
> I can share a makefile with confidence that any Linux/Mac OS/BSD user can use it without needing additional software
Makefile portability can be tricky. Especially if you try to do something fancy with the makefiles.
GNU Make has features and syntax that the other Makes don’t. Likewise there are features and syntax that some Make programs have that GNU Make doesn’t.
Make is not _that_ portable. If you're using high level languages and only need a task runner to kick off your compiler, watch rules or similar and need portability, you could write an executable bash script with functions that serve as your commands;
#!/bin/bash
set -e
function build() {
echo "Your build steps"
}
function clean() {
echo "Your clean steps"
}
eval $1 $@
Which you can run with
$ ./task clean
$ ./task build
You can also write these kinds of task scripts in JavaScript or Python - which might be easier to manage compatibility with Windows
Actually, I did a hack that does that too (this one is nicer, though). I can’t find mine offhand (too many makefiles), but it is also a Makefile target that runs grep -B1 on the Makefile itself and spits out each target’s name and the comment I usually add to it… And my Makefile template uses .env files too… I think I cover most of the list in my day-to-day use.
I like similar tricks. Another trick I like to use is being able to do a `make showconfig` and have it print the list of variables & their values that I care about.
You can see that here. You can also see my Make+BASH solution for documenting targets.
If you're going to use it like Make without the build system parts, why not just have a directory of tiny scripts? More portable than either, the only boilerplate is a shebang line, and you can use static analysis tools (shellcheck), formatters (shfmt) and the like.
Take the example they've screenshotted in the readme[1]:
alias b := build
host := `uname -a`
# build main
build:
cc *.c -o main
# test everything
test-all: build
./test --all
# run a specific test
test TEST: build
./test --test {{TEST}}
The equivalent would just be something like the following:
build.bash:
#!/usr/bin/env bash
set -o errexit
cc *.c -o main
test-all.bash:
#!/usr/bin/env bash
set -o errexit
"$(dirname "${BASH_SOURCE[0]}")/build.bash"
./test --all
test.bash:
#!/usr/bin/env bash
set -o errexit
"$(dirname "${BASH_SOURCE[0]}")/build.bash"
./test --test "$@"
If you want to get really fancy you could make a common.bash with safety pragmas and things like the host string.
Huge fan of just; I add a Justfile to pretty muc hevery new repo I create regardless of language or stack.
My personal favorite feature is the ability to load environment variables from a `.env` file and set them for all commands run. Just have to add this to the top of your `Justfile` to make it happen:
As someone who _extensively_ uses Makefiles everywhere to speed up things (why bother remembering how to start a server in a particular language when “make serve” will work anywhere), I almost understand why this exists, but then I remember that make is available everywhere and has tab auto completion and I have to wonder why…
Because make is dog shit if you need to intertwine make with bash. You have to remember various escaping rules (double $$ signs or not depending on whether you want to refer to a make variable or interpolate a bash variable), tabs instead of spaces that new devs often (quite rightly) get tripped up by, and various other idiosyncracies you can waste hours on.
Just is built in Rust rather than C. Rust is generally nicer to work with and maintain than C libs so huge win there development wise. Feature wise, only see a couple things with Just right now that differentiate it from Make.
This is actually very different than `make`, since it will always run the tasks even if their inputs haven't changed[0].
It's a bit bizarre to me that their example involves building code, since that application generally benefits from this "idiosyncratic" behavior of `make`.
You probably would want the behavior of `make` for the `test` command too, to avoid running potentially time-consuming tests unnecessarily. Bazel caches test results to avoid this, for example.
Caching test results sounds like a horrible idea, since the test could rely on external state or dependencies that weren't checked. For example, if you're testing something that opens a socket.
I do love me some make, and have since forever.
but we do have to look for a successor
as cleaning out all of make's historical baggage
would be a disservice to too many (really any is too many).
If you are not sure of what I am talking about, try typing
`make -p`
those builtin rules can be disabled if you are not building
ancient artifacts but that we have to is why one of these
work-a-likes is going to win some day.
I dig the general idea, but question the value add over a directory of `scripts` that follow sane conventions (ie `script/test`, `script/build` etc). Is the main thing that you can do `just -l` to see available commands? I have never really reached for `make` when I've had a choice, as I've done mostly ruby, JS, or java where you have more sane, humane tools (i.e. Rake, Yarn, Maven though that one is never fun).
My general approach is every repo should have something that follows https://github.com/github/scripts-to-rule-them-all, written in sh (maybe bash, its 2023), linted with shellcheck. When you need something fancy Rake is great or grab some nice bash command line helper and source it from all your scripts. Is a command listing really worth another dependency over what you get from `ls script` or `cat script/README` ?
I use https://github.com/davetron5000/gli for this, since I work in ruby. Adding something like just or gli to your project is a huge win. Every dev can just `just update_db` to refresh their dev db, `just update_secrets` to update dev secrets. Whatever. So much better than putting snippets in a wiki or whatever.
I like gli because it gives you subcommands, like `gli database refresh` etc.
Another simple tool similar to this is makesure[1]. It’s written in shell so the idea is you include makesure itself in your repo, which avoids needing to install another tool to run commands on your project.
It’s very simple so isn’t good for everything, but works well as a simple command runner.
Looks cool! Including the script in the project directory is the way, and I created on makesh[1] with that in mind. Since it's a submodule it can be easily pulled around with a repo and updated.
Glad to see people are finally going back to ahell scripts since they are very ergonomic and with a little portability in mind, they can be cross platform too.
I've been using it for a while and it's pretty flexible:
- dependencies
- parallelism
- programmatically generated tasks (since the config file is just a Python file)
- "udf" to specify when a task is up-to-date and can be skipped
I like the idea of trying to rethink the Make interface, but it just seems like most projects could actually benefit from build targets (conditional execution based on file existence/age), even if it's not the first thing you need to automate. I don't want to give that up because PHONY is confusing.
Nah, just does not support automatic parallelization of the dependency graph. This is a deal breaker for me. Also I'm not a huge fun of a yet another built-in language that tries to mimic a full-featured programming language.
I have been using Just for 3 months and it is such a fantastic tool. I never looked back at make. I don't even look at npm scripts anymore. I love Just.
That looks cool but I fundamentally hate yaml so it’s a no go.
I would rather lose my hair screwing with a makefile like thing than add more yaml to my day to day; I currently say “I hate yaml” at least 4 times a day.
If it’s not too much trouble and I don’t need comments I just write json as yaml.
Tools like these are handy, for sure. Problem is: If you're collaborating in a project, then you're requiring a new dependency to be installed in everyone's machine.
I feel like this a pretty non-issue so long as you document both that the dependency is required, and how to install it. Just is much easier to install than Python!
These issues are part of my daily work. I’ve started converting the make targets/commands to shell scripts because the hacks and ugliness that you have to do to provide make with arguments isn’t worth it. It seems like the more advanced shell features you want to use in a makefile, the more make gets in your way.
Not that I fault it. It’s supposed to be for making programs hence the name. We’re abusing it by turning into a script collection.
My personal favorite for small projects is invoke: https://www.pyinvoke.org/. I prefer it with python because it is just another lean dependency I can directly install along with other dependencies. Works pretty well unless you wanna chain and run long shell commands.
My biggest pet peeve with pyinvoke is that you can’t pass arbitrary arguments through to the underlying task. For something like invoking pytest you need to replicate the arguments you use in the task definition.
[+] [-] simonw|3 years ago|reply
Make never stuck for me - I couldn't quite get it to fit inside my head.
Just has the exact set of features I want.
Here's one example of one of my Justfiles: https://github.com/simonw/sqlite-utils/blob/fc221f9b62ed8624... - documented here: https://sqlite-utils.datasette.io/en/stable/contributing.htm...
I also wrote about using Just with Django in this TIL: https://til.simonwillison.net/django/just-with-django
[+] [-] justin_oaks|3 years ago|reply
From what I can tell, Just accomplishes what a set of scripts in a directory could also do, but in a more ergonomic fashion. Since the justfile is a single file, you're not cluttering up your directory. You don't have to look for all *.sh files, but can instead do a "just --list". And using "just target" is probably easier to type than "./target.sh" since the just version has no punctuation.
What are some of the other benefits of Just that makes it superior to "a set of scripts in a directory"?
[+] [-] kapilvt|3 years ago|reply
[+] [-] rcarmo|3 years ago|reply
[+] [-] rollcat|3 years ago|reply
[+] [-] velcrovan|3 years ago|reply
There is a nice trick that gives makefiles this ability: http://marmelab.com/blog/2016/02/29/auto-documented-makefile...
Adding .PHONY targets and so forth is a bit inelegant, but I can share a makefile with confidence that any Linux/Mac OS/BSD user can use it without needing additional software, and I will never have to worry about make becoming unavailable or no longer maintained. Just my personal opinion.
[+] [-] dijit|3 years ago|reply
I'm sure you're kidding, but in the case that you're not: Make portability is gross.
We have ./configure steps precisely because Make is difficult w.r.t. portability, but even if that wasn't the case and you were just using make as a command executor: you still have enormous warts.
Oh, and yeah, you'd need whatever additional software too.
Be it: headers, linters, formatters, libraries or test suites that you've bundled.
Valgrind is a popular make target, but "Make" does not bring in valgrind (for example).
Honestly one of the most backwards things the Go community did was adopt "Make", it's so kludgey even as a pure command executor that I can't really take anyone seriously who argues for it's use.
I'm not saying "Just" is a replacement, I don't know what is.
Most of the arguments for using Make boil down to "I enjoy typing `make <something>`" and "you probably have it installed already?".
[+] [-] codetrotter|3 years ago|reply
Makefile portability can be tricky. Especially if you try to do something fancy with the makefiles.
GNU Make has features and syntax that the other Makes don’t. Likewise there are features and syntax that some Make programs have that GNU Make doesn’t.
[+] [-] apatheticonion|3 years ago|reply
Make is not _that_ portable. If you're using high level languages and only need a task runner to kick off your compiler, watch rules or similar and need portability, you could write an executable bash script with functions that serve as your commands;
Which you can run with You can also write these kinds of task scripts in JavaScript or Python - which might be easier to manage compatibility with Windows[+] [-] rcarmo|3 years ago|reply
[+] [-] oso2k|3 years ago|reply
https://gist.github.com/prwhite/8168133
The linked blog's solution is also mentioned in the gist comments.
https://gist.github.com/prwhite/8168133?permalink_comment_id...
[+] [-] oso2k|3 years ago|reply
https://github.com/lpsantil/oop0/blob/master/Makefile#L87-L1...
[+] [-] rgoulter|3 years ago|reply
In my experience, `make` also needs to be installed. (On ubuntu it's part of `build-essential`).
I guess more precisely, `just` might not be available in all package managers?
[+] [-] dgunay|3 years ago|reply
[+] [-] unknown|3 years ago|reply
[deleted]
[+] [-] l0b0|3 years ago|reply
Take the example they've screenshotted in the readme[1]:
The equivalent would just be something like the following:build.bash:
test-all.bash: test.bash: If you want to get really fancy you could make a common.bash with safety pragmas and things like the host string.[1]: https://github.com/casey/just/blob/master/examples/screensho...
[+] [-] pletnes|3 years ago|reply
[+] [-] Ameo|3 years ago|reply
My personal favorite feature is the ability to load environment variables from a `.env` file and set them for all commands run. Just have to add this to the top of your `Justfile` to make it happen:
`set dotenv-load := true`
[+] [-] efxhoy|3 years ago|reply
make can do that too. Put
at the top of the Makefile. Works for me on GNU Make 3.81 on MacOS.[+] [-] rcarmo|3 years ago|reply
[+] [-] rosszurowski|3 years ago|reply
I wrote a post about that here: https://rosszurowski.com/log/2022/makefiles
[+] [-] nprateem|3 years ago|reply
[+] [-] stevenhuang|3 years ago|reply
[+] [-] chungy|3 years ago|reply
[+] [-] nsonha|3 years ago|reply
https://github.com/adriancooney/Taskfile
[+] [-] iepathos|3 years ago|reply
[+] [-] xigoi|3 years ago|reply
[+] [-] kjgkjhfkjf|3 years ago|reply
It's a bit bizarre to me that their example involves building code, since that application generally benefits from this "idiosyncratic" behavior of `make`.
You probably would want the behavior of `make` for the `test` command too, to avoid running potentially time-consuming tests unnecessarily. Bazel caches test results to avoid this, for example.
[0] https://github.com/casey/just#what-are-the-idiosyncrasies-of...
[+] [-] kevingadd|3 years ago|reply
[+] [-] tejtm|3 years ago|reply
If you are not sure of what I am talking about, try typing
`make -p`
those builtin rules can be disabled if you are not building ancient artifacts but that we have to is why one of these work-a-likes is going to win some day.
[+] [-] rsanheim|3 years ago|reply
My general approach is every repo should have something that follows https://github.com/github/scripts-to-rule-them-all, written in sh (maybe bash, its 2023), linted with shellcheck. When you need something fancy Rake is great or grab some nice bash command line helper and source it from all your scripts. Is a command listing really worth another dependency over what you get from `ls script` or `cat script/README` ?
[+] [-] elliotshep|3 years ago|reply
[+] [-] jacobsenscott|3 years ago|reply
I like gli because it gives you subcommands, like `gli database refresh` etc.
[+] [-] oakesm9|3 years ago|reply
It’s very simple so isn’t good for everything, but works well as a simple command runner.
[1] https://github.com/xonixx/makesure
[+] [-] baldomo|3 years ago|reply
Glad to see people are finally going back to ahell scripts since they are very ergonomic and with a little portability in mind, they can be cross platform too.
[1] https://github.com/Baldomo/makesh
[+] [-] JakaJancar|3 years ago|reply
I've been using it for a while and it's pretty flexible:
[+] [-] cortesoft|3 years ago|reply
There are a lot of language specific tools like this. I love Ruby's rake.
[+] [-] letmeinhere|3 years ago|reply
[+] [-] garganzol|3 years ago|reply
[+] [-] psuedo_uuh|3 years ago|reply
[+] [-] cute_boi|3 years ago|reply
[+] [-] gempir|3 years ago|reply
The alternative Taskfile can do that https://taskfile.dev/usage/#including-other-taskfiles
[+] [-] jkrubin|3 years ago|reply
I would rather lose my hair screwing with a makefile like thing than add more yaml to my day to day; I currently say “I hate yaml” at least 4 times a day.
If it’s not too much trouble and I don’t need comments I just write json as yaml.
[+] [-] brabel|3 years ago|reply
Just doesn't seem to even support caching task results (by declaring inputs/outputs)?
[+] [-] _jplc|3 years ago|reply
Stuff like `make` is already there, always. In my case, I assume everyone has Python 3 and include a `task` script using this template, which does something similar: https://gist.github.com/sirikon/d4327b6cc3de5cc244dbe5529d8f...
[+] [-] nicoburns|3 years ago|reply
[+] [-] user3939382|3 years ago|reply
Not that I fault it. It’s supposed to be for making programs hence the name. We’re abusing it by turning into a script collection.
[+] [-] rochak|3 years ago|reply
[+] [-] jsmeaton|3 years ago|reply