Author probably wants to use `private` for those target-local variables, though.
For example,
R: .SHELLFLAGS := -e
R: SHELL := Rscript
R:
greeting = "bonjour"
message(paste0(greeting, ", R!"))
Everything that target `R` depends on will also have SHELL and .SHELLFLAGS over-ridden. If `R` depends on some data generated by another program, it probably wants to be built and executed with the default SHELL (or another shell, perhaps). R: private .SHELLFLAGS := -e
R: private SHELL := Rscript
R:
greeting = "bonjour"
message(paste0(greeting, ", R!"))
Now, `R`'s dependencies will be generated with the makefile's defaults.Usually I prefer to build up richer stages like this using the system shell anyway, though. Build a target which in turn is executed by the shell normally to traverse the next edge in the graph. But I can see how this mechanism has its uses.
See also https://www.gnu.org/software/make/manual/html_node/Target_00...
[just](https://github.com/casey/just) is a tool that feels similar to make but is designed explicitly for the purpose of running commands.
I think of this as a simple CLI for your project workflow. You still really want to avoid putting code into a Justfile and put it into scripts. But the Justfile helps provide a slightly nicer UX and automatically invoke dependencies.
Most people forget to mark their targets .PHONY, so they have a subtle bug in their build (touch build; touch test).
----
But shell also suffers from the problem where it doesn't list the commands. I filed a bug for Oil shell here:
https://github.com/oilshell/oil/issues/751
I mentioned a couple other "frameworks" there like just, go, Taskfile, etc.
But it should really just be built into the shell, since it's so common. And there should be command completion too, which I think bash-completion has for Makefiles on many distros.
Apparently there is no standard name for this kind of "task runner". But I think shell makes a lot more sense than a custom format, because there are many instances where you need a simple loop or conditional. It scales better. (And Oil also fixes bad shell syntax while remaining compatible: http://www.oilshell.org/blog/2020/01/simplest-explanation.ht...)
If anyone wants to help let me know :) The code is plain Python and pretty hackable. However it generates fast C++, so you get the best of both worlds (in progress)
This is GNU Make + a few patches. So it's 100% compatible. And you get an interactive debugger, and lots more stuff. For instance, to list out the commands:
remake --targets
No idea why this hasn't been merged upstream.Your larger point really stands, though: if you're just running commands, you shouldn't be using Make. But it is abused in that way often, so...
I would think because it's useless. The targets in a Makefile are very often just internal and aren't always meant to be run by the user.
The lack of a standard argument for getting help doesn't make it problematic for use as a command runner. You can't get atomatically all available commands from a Makefile just like you can't get all command-line flags from an executable. The program/Makefile has to provide it by itself.
#!/usr/bin/make -f
And then putting them in my $PATH. I run them with arguments like: $ process-data.mk INTSV=a.tsv DB=largefile.gz OUTDIR=finished
This makes me feel like I've sold my soul to the devil, and that I'm just living on borrowed time until it all fails and falls apart. It hasn't yet, however...FWIW, using make -f in the shebang is also done for debian/rules in Debian package builds. I don't know if it serves any real purpose. I suppose it permits one to write a bespoke script for building targets without using make.[2] I guess I wouldn't be surprised if someone, somewhere depended on that capability, given how old and widespread Debian packages are.
[1] /usr/bin/env make -f would be better, but then you run afoul of the problem that you can't portably pass more than a single explicit shebang command argument.
[2] Which I see now is a bonus to your process-data.mk script. It could be replaced with a non-make version without effecting callers.
I might put #!/usr/bin/env make -f in case it's somewhere in else in PATH.
Also some systems (BSD, old commercial Unix) have non-gnu-compatible make and sometimes call their gnu make port "gmake" or "gnumake".
#!/bin/sh
# make ignores next line \
set -e
# make ignores next line \
exec make -f "$0" "$@"
Make treats slash-escaped new lines as a continuation even for comments, shell does not.There's also a special bonus papercut you might hit when /usr/bin/env is in the shebang with extra arguments: an infinite loop!
Sorry for the plug: I wrote about it here. https://www.crystae.net/posts/2019/11/08/two-shebang-papercu...
Personally, I try to use POSIX Makefiles, but I often find that they're most useful as a target for Makefile generators (in my case, these are usually a shell script called configure).
The most common mistake of that sort that I've seen is people trying to do complex conditionals inside their makefiles when they clearly would be better off in a Shell script. (I'm looking at you, fans of ifeq.)
Sometimes it's just simpler to bite the ancient bullet and go with a Makefile, with all its included pains and gotchas rather than try to figure out how to get the fancy new makefile replacement installed in all the relevant environments.
However, SnakeMake, Nextflow, etc feels excessively verbose compared to standard make. And the prior workflow managers of last decades were far worse. With standard make, you type pretty much exactly what you would for shell commands, and not too much more.
All other alternatives are going to be more verbose than make, and to me that's a negative.
Alas, I think the developers have in fact done just that. Make is not a build system. Make is a batch shell.
Build2 didn't produce a better make, they produced yet another C++ build system.
Pull requests are welcome.
Edit: I'm the author.