You are not wrong, but if the world where not full with even worse build systems it'd be a bit saying I prefer to drink from puddles because I find them wherever I go.
Make is everywhere, but apart from UX issues such as the dumb syntax or the different flavors you'll encounter, the fundamental problem with it is that it just can't build things properly (the existence of make clean is the dead give away here). That's fine for trivial stuff, but for real work you really owe it to yourself (and society at large) to use a build system which can correctly build things.
Some examples applying to the above:
- if your paths have white space in them, you are generally fucked
- if your change your Makefile, nothing will be rebuilt
- if inkscape writes out a partial file because full disk and you fix the problem, the partial svg will no be rebuilt
- if you install a new version of inkscape nothing will be rebuilt
This is probably my second biggest complaint about Make.
> - if you change your Makefile, nothing will be rebuilt
Heh, there’s a common trick for that: just slap Makefile on the end of prerequisite lists. I’ve worked with more complex projects that extend this to versioning the makefile with a `build/.makefile-v${MAKEFILE_VERSION}` common prerequisite so that you can bump the version if the change is such that you’ll need to rebuild everything.
> - if inkscape writes out a partial file because full disk and you fix the problem, the partial svg will no be rebuilt
Or the more common form, you run a fallible program and it produces errors but still produced a file (e.g. if you used piping, `> $@` instead of a `--output=$@` command line argument that only creates the file after processing has succeeded).
I think “build errored but produced a file” is probably my biggest complaint about Make. You can work around it in a few ways; `|| (touch --date=@0 $@; false)` is probably my favourite, which I used in https://chrismorgan.info/blog/make-and-git-diff-test-harness....
> - if you install a new version of inkscape nothing will be rebuilt
I’m curious: would Ninja rebuild on a new version of Inkscape? I’d be pleasantly surprised and view it much more favourably if it does.
In the scope of Make, prerequisite satisfaction is all reckoned with timestamps, which is a thing I’m not always fond of, so you definitely can’t do this properly. I think the closest you could get is generating a file containing the versions of all your prerequisites, and arrange for it to only be updated when they’ve changed. Basically a slight variant of the MAKEFILE_VERSION trick discussed earlier, though definitely much more involved, especially if you want it to have one filename always rather than just being a hash of all the versions.
And yes, all this is supporting your point that Make is not the best thing in this space, which I quite agree with. But for simple things I reckon it’s often still worth using despite this.
Re a new version of inkscape: Ninja does not force fully-correct builds, so it's on the author of the build.ninja file to either model this or not. (This is part of Ninja's objective of not mandating policy.)
To get this fully correct, note that you would need to model all inputs that Inkscape might read, which includes not only binaries but also configuration files or dynamically-loaded plugins. Some systems (I think tup?) track this by tracing executed binaries to see which file system operations they do.
Even with tracing it can be really subtle: for example if a C file has an #include "foo.h", creating a new file named foo.h earlier in the include search path is a meaningful change, which means a file-access-tracing build system also needs to track file-not-found results from previous executions.
(For C programs, also note that getting header dependencies correct includes similarly tracking all the headers in /usr/include that your program transitively includes. See the -MD vs -MMD flags to gcc.)
The correct way of doing this is not tracing but a hermetic build. If you do a sandboxed build with nix, you will get a specific version of inkscape with a specific hash, which includes will include any inkscape extensions you include. And since the build will only be able to read files in the sandbox, none of the problems you list above can happen -- you can't pull random stuff from the network either.
What you wrote is true, but it also seems to me unlikely that the OP is going to set up a sandboxed hermetic build to process a handful of pngs for her zine. I was instead explaining why Ninja doesn't attempt clever tricks to try to better approximate correctness.
> What you wrote is true, but it also seems to me unlikely that the OP is going to set up a sandboxed hermetic build to process a handful of pngs for her zine.
But it's trivial!
Assuming you fix the OP's Makefile (so much for Makefile syntax usability):
# put into Makefile
THINGS_TO_CONVERT := $(wildcard *.svg)
all: $(patsubst %.pdf,%.svg,$(THINGS_TO_CONVERT))
inkscape $< --export-text-to-path --export-pdf=$@
# put into default.nix
with import (fetchTarball "https://github.com/NixOS/nixpkgs/archive/7ad5e816faba3f808e2a8815b14d0f023a4e2160.tar.gz") {};
stdenv.mkDerivation {
name = "zine-images";
src = ./.;
buildInputs = [ inkscape ];
installPhase = "mkdir $out && cp *.pdf $out";
}
Now make sure you have sandbox=true in /etc/nix.conf, run `nix build` in the directory, and boom done: hermetic build of zine images. Good thing too, I got a warning about --export-pdf being deprecated when I ran this. Note that running make or the dependency on make is not explicitly specified, mkDerivation has some basic smarts to figure out from the presence of a Makefile that it should run make.
(Hardcoding the version of nixpkgs directly into the default.nix just for demo purposes, better to use niv or similar to manage a json file with versions for you).
> I’m curious: would Ninja rebuild on a new version of Inkscape? I’d be pleasantly surprised and view it much more favourably if it does.
I haven't used ninja, but nix and (unless memory fails me) bazel derived build systems will. Nix basically has a completely hermetic world, and everything is referred to by hash that includes the recursive dependency tree.
I've never seen the `touch` version of that safety versus broken output files before. I've only seen the `rm` version. Is `touch` advantageous? Edit: oh, oops, I should've followed the link; the output file in the example seems to be test output which would be more distinctly useful to inspect after the fact on partial failure. That makes sense.
> - if your paths have white space in them, you are generally fucked
Paths containing ':' are also fun. That make doesn't handle paths - and most likely other things - as a unit, but just substitutes their string representation into the makefile, is just asking for trouble.
But linux filename basically allows any byte sequence except EOF and /, so I think it is "make" fault to not being able to support it. Other proper build tools support it correctly.
> But linux filename basically allows any byte sequence except EOF and / (...)
Could/should/would...
Even though UNIX doesn't impose many restrictions on file names that doesn't mean it's in your best interests to use any weird character in them.
I mean, do you honestly believe it's a good idea to have file names that are 256-characters long, have line breaks in their name, and have substrings such as "sudo rm -rf /"?
> I mean, do you honestly believe it's a good idea to have file names that are 256-characters long, have line breaks in their name, and have substrings such as "sudo rm -rf /"?
Yes. It is usually some badly written programs or scripts choke on space, newline other things.
BTW, I would like to impose a restriction that filename must be valid UTF-8 string instead of random bytes.
Well, just because make is faulty, doesn't mean linux isn't too ;)
Spaces -- ok. But there is absolutely no use case for allowing near arbitrary byte sequences in filenames, it causes no end of trouble, and it would have been trivial to fix in a non-disruptive manner ages ago (mount -o allowreallydumbfilenames, off by default).
> You are not wrong, but if the world where not full with even worse build systems it'd be a bit saying I prefer to drink from puddles because I find them wherever I go.
That assertion is quite wrong. If you really want to go with that metaphor them the alternative to drinking from all the puddles you have readily available is to ignore them all and instead resort to waste time digging your own hole in the ground each and every single time you feel a little thirsty.
Then, in the only practical and conceivable advantage you get from all the extra work is to allow you to enjoy a particular earthy flavour that in the end can't be objectively described as an improvement.
Make is everywhere, but apart from UX issues such as the dumb syntax or the different flavors you'll encounter, the fundamental problem with it is that it just can't build things properly (the existence of make clean is the dead give away here). That's fine for trivial stuff, but for real work you really owe it to yourself (and society at large) to use a build system which can correctly build things.
Some examples applying to the above:
- if your paths have white space in them, you are generally fucked
- if your change your Makefile, nothing will be rebuilt
- if inkscape writes out a partial file because full disk and you fix the problem, the partial svg will no be rebuilt
- if you install a new version of inkscape nothing will be rebuilt