I used make heavily in the 80s and 90s, but haven't much since then. Recently I started a project that had source files getting processed into PDF files, for use by humans. Since this is the 21st century, those files have spaces in their names. At a certain point, I realized that I should be managing this processing somehow, so I thought of using a simple Makefile. A little searching reveals that the consensus on using make with files with spaces in their names is simply "don't even try." In the 21st century, this is not an acceptable answer.
Demanding the support of spaces in filenames significantly complicates code as simple space delimination no longer works and other delimination schemes are much more error prone -- forgetting balancing quotes, any one? While you are allowing spaces, you probabaly are allowing all possible code points or maybe even a null byte? Thinking about it gives me headaches.
I hate hearing people using 21st century or modern as reasons for inflating complexities. Without rein on complexity (whether it is hidden or not), the future is doomed, whatever you are building. While I am not saying we should avoid complexity at all cost, I am insisting that all complexity should be balanced with merits.
The merits of filenames with spaces is they read better in a GUI explorer. Whether that merit balances out all the complexity it brings is individual dependent. For me, that merit ranks very low and I avoid spaces in my filenames at all opportunities. For some, they need those filenames to be readable. And there are solutions. One solution, from those who don't code, seems to demand ("developers", paid or not) that every tool that deal with files should handle this additional complexity, regardless of their context and what they think. Another solution would be to add an additional pre-step of copying/renaming/linking/aliasing. With the latter solution, the complexity is confined.
I guess for some, it only matters with "I do work" or "they do work" rather than the big picture. That is fine. However given the context of you are working with Makefiles, then you are a developer at some level, you are supposed to do some work.
> And there are solutions. One solution, from those who don't code, seems to demand ("developers", paid or not) that every tool that deal with files should handle this additional complexity, regardless of their context and what they think.
Users expect computers to work in a non-surprising ways.
It isn't natural to use dashes or underscore in file names. Training users to be afraid of spaces is just teaching them one more way that computers are scary and unpredictable.
Meanwhile over in Windows land, all tools have been expected to deal with spaces in them for what is approaching 20 years.
Human beings who name things are going to use spaces. That spaces were used as delimiters for computers is somewhere between unfortunate and a colossal mistake.
But to use that as evidence of why spaces should not be supported in filenames is putting the cart before the horse.
The goal of software is not to perpetuate whatever mistakes have been made in the past. It's to solve problems for human beings.
And human beings have been using spaces to delimit words since long before computers existed.
What does the merit of spaces in file names matter? You still will need to deal with files with spaces in the real world. If your tooling doesn't support it, it's a non-starter for many.
You solution is a “boil the oceans” one typically proposed by engineers. You can re-program computers. You can’t re-program millions of people. Every natural language uses spaces to separate things.
You can accept that or you can keep tilting at windmills.
In every branch of science, reality wins. If your model can’t accomodate reality it’s either completely wrong or it needs adjustments, at least.
Not handling spaces is a symptom of a much deeper problem in common with a lot of 'unix' utilities: not actually structuring data apart from in ad-hoc string encodings. The fact that data can be so easy conflated with program structure is the cause of so many obscure bugs and overhead in using utilities that suffer from it it's a wonder anyone is defending this approach going forward.
The fact that Unix tools have trouble with spaces in filenames is absolutely a problem with Unix. If the Unix ecosystem had better support for this, then it wouldn't be a problem.
You're talking about implementation complexity. The only argument you can throw at the user is feature complexity. Handling spaces in filename isn't a complex feature at all, and users don't care that the simplistic implementation you're using makes it a problem.
> At a certain point, I realized that I should be managing this processing somehow, so I thought of using a simple Makefile.
I don't understand why one would think that make would be a good tool for this...
> Demanding the support of spaces in filenames significantly complicates code as simple space delimination no longer works and other delimination schemes are much more error prone -- forgetting balancing quotes, any one?
Yes this is a limitation of make, but not a million other tools out there.
Make is not a panacea, no tool is - pressing a tool into a job it is un-suited for because you understand it is "Not good" (trade mark and copy right pending).
This is the classic case of having a hammer and a screw -
I hate hearing people using 21st century or modern as reasons for inflating complexities.
No. He's right, you're wrong, hzhou321. We want spaces in filenames. We even want UTF-8 if possible. We don't want crude tools that cannot handle the most basic names. You can argue all you want, this is a very very basic demand that could be met with very very basic tools but make is just too crude.
People like you are exactly the cancer in the developer community that argues away reasonable demands like spaces in filenames and perpetuates the garbage legacy tools we have.
Solution: create the pdfs with spaces replaced by underscores. Then as the very last command in the relevant makefile section, insert a bash command to replace those underscores with spaces.
I deal with this shape of problem quite a bit. After using scons and make in the past I recently tried using ninja, and it really works well.
Specifically, a python configure script using the ninja_syntax.py module. This seems like it's a bit more complicated, but has a lot of nice attributes.
File names with spaces should just work (unlike make). The amount of hidden complexity is very low (unlike make or SCons); all the complexity lives in your configure script. It's driven by a real, non-arcane language (unlike make). Targets are automatically rebuilt when their rules/dependencies change (unlike make).
It's more difficult to install than make, but only marginally.
Perhaps you just chose the wrong tool for the job. Just because you are able to do similar things with make, doesn't mean it is has to be suited to your chosen use case. It's a tool that was created with a specific purpose in mind, with specific constraints, and it works fine for thousands (I assume) of people every day. You can't blame it for not being a general-purpose programming language. Make isn't beyond building other tools that you can write yourself - and use in the very same makefiles - to assist in handling cases like this, however.
Spaces in filenames create troubles with almost every command line tool. cut(1), awk(1), find(1), xargs(1), what not. How do you quote them, do you need to use \0 as a separator instead, do other commands on the pipeline support \0 separators? What happens after a couple expansions, passing stuff from one script to another?
And what the heck happened on 31 dec 1999 that the world became a different place where suddenly people realised: there were these space things, quite useful they were, why don't we tuck them into every file name and URL and who knows what?
People have better things to do than dealing with these things.
> Spaces in filenames create troubles with almost every command line tool
Then that's a shortcoming which should be addressed with the tools, because humans everywhere use spaces in filenames.
For every command-line tool I make (Windows & Linux), I ensure it handles such trivial use-cases. I can't see why such a simple task is seemingly impossible to get done in GNU coreutils.
I think the reason make is both so controversial and also long-lived is that despite how everyone thinks of it, it isn't really a build tool. It actually doesn't know anything at all about how to build C, C++, or any other kind of code. (I know this is obvious to those of us that know make, but I often get the impression that a lot of people think of make as gradle or maven for C, which it really isn't.) It's really a workflow automation tool, and the UX for that is actually pretty close to what you would want. You can pretty trivially just copy tiresome sequences of shell commands that you started out typing manually into a Makefile and automate your workflow really easily without thinking too much. Of course that's what shell scripts are for too, but make has an understanding of file based dependencies that lets you much more naturally express the automated steps in a way that's a lot more efficient to run. A lot of more modern build tools mix up the workflow element with the build element (and in some cases with packaging and distribution as well), and so they are "better than make", but only for a specific language and a specific workflow.
> and the UX for that is actually pretty close to what you would want.
That is so not true. Make has deeply woven into it the assumption that the product of workflows are files, and that the way you can tell the state of a file is by its last modification date. That's often true for builds (which is why make works reasonably well for builds), but often not true for other kinds of workflows.
But regardless of that, a tool that makes a semantic distinction between tabs and spaces is NEVER the UX you want unless you're a masochist.
> Make has deeply woven into it the assumption that the product of workflows are files, and that the way you can tell the state of a file is by its last modification date.
I've always wondered whether Make would be seen as less of a grudging necessity, and more of an elegant panacea, if operating systems had gone the route of Plan 9, where everything is—symbolically—a file, even if it's not a file in the sense of "a byte-stream persisted on disk."
Or, to put that another way: have you ever considered writing a FUSE filesystem to expose workflow inputs as readable files, and expect outputs as file creation/write calls—and then just throw Make at that?
> but often not true for other kinds of workflows.
Examples? I mean, there are some broken tools (EDA toolchains are famous for this) that generate multiple files with a single program run, which make can handle only with subtlety and care.
But actual tasks that make manages are things that are "expensive" and require checkpointing of state in some sense (if the build was cheap, no one would bother with build tooling). And the filesystem, with its monotonic date stamping of modifications, is the way we checkpoint state in almost all cases.
That's an argument that only makes sense when you state it in the abstract as you did. When it comes down to naming a real world tool or problem that has requirements that can't be solved with files, it's a much harder sell (and one not treated by most "make replacements", FWIW).
A language that uses lots of parens to delimit expressions is incredibly bad UX, especially when you try to balance a complex expression, but hopefully there are tools like Paredit to deal with that, so that I can write my Emacs Lisp with pleasure about every day. Similarly, any decent editor will help you out with using correct indentation with Makefiles.
Last modification date is not always a correct heuristic to use, but it's quite cheap compared to hashing things all the time.
Make is a tool for transforming files. I wonder how it's not quite natural and correct for it to assume it's working with files?
Yeah. There is a metric crap-ton of the design of Make that is solely for the purpose of compiling and linking and document processing. That's actually part of what makes it annoying to use it for projects other than C or C++, when you don't need to compile or transform or depend on different formats.
Thank you. It's nice to know that I'm not alone in this dark, dark world.
It's so depressing when people use arguments like "it's old", "it uses tabs", and "it's hard to learn". As described by one Peter Miller, "Make is an expert system". If a tool is the most powerful, standard, and expressive among its peers, its age or the fact that it uses tabs should be inconsequential.
If anything, the fact that it's decades old and used in every major software project is a testament to its effectiveness, not a drawback.
And if "learning Make" is a barrier, that to me is a sign that someone cares more about complaining than about their project. The same way people learn Git when it's clear that it's the best tool, people learn Make. It really isn't that hard. Even the basics are enough to reap huge benefits immediately.
> If anything, the fact that it's decades old and used in every major software project is a testament to its effectiveness, not a drawback.
Part of the reason is because people see the superficial issues (like the discussion regarding spaces) before they see the value of years (or decades) of work. It doesn't help that when folks bring this up many times you get "you're holding it wrong" type responses.
I sympathise with both sides of the argument. I don't know the solution but it's unfortunate seeing folks reinventing the wheel and struggling with problems solved in the past.
I definitely think Gulp and Webpack have a place. Where you have to write a Makefile fresh every time for every type of project, Gulp and Webpack come prepared with JS-specific stuff. That's perfect for a throwaway project or a small codebase where build performance and maintenance really don't matter.
My issue is that people who need serious solutions forgo Make because "tabs, man", or because Webpack has pretty console output.
The tools are not the same on every platform. That’s reason enough for me to not use it with my JavaScript projects.
The bigger reason though is that it’s not very idiomatic for JavaScript projects to use Make. It sounds like the only reason that some people go out of their way to use it is because they actually don’t want to learn something.
> "It’s not very idiomatic for JavaScript projects to use Make."
While I agree that popularity is a factor in picking a tool, it shouldn't be a deciding factor. Going by popularity is precisely how we end up with a new Build System of the Year(TM) every few years. The fact that we've gone through 4 fairly prominent tools (Gulp, Grunt, Broccoli, Webpack), all of which contending to "fix" the previous, and none of which have proper DAG or incremental builds (which Make has had for decades) is damning evidence.
In other words, I think Make could be (and I wish it was) idiomatic for JS.
Make's interface is horrible. Significant tabs. Syntax which relies on bizarre punctuation... If only whoever authored Make 40 years ago had had the design acumen of a Ken Thompson or a Dennis Ritchie!
We're stuck with Make because of network effects. I wish that it could just become "lost" forever and a different dependency-based-programming build tool could replace it... but that's just wishful thinking. The pace of our progress is doomed to be held back by the legacy of those poor design decisions for a long time to come.
Maybe I'm in the minority, but I've always found its syntax to be quite nice (though admittedly a departure from most modern languages). Then again, I find using JSON or not-quite-ruby to configure a build incredibly bizarre and confusing, so I guess I'm just set in my ways...
In all seriousness, what's wrong with it? Significant tabs aren't great, but I feel like that's a relatively minor wart. The simple things are _very_ simple and straightforward. The more complex things are more complex, but usually still manageable...
I've seen plenty of unmanageable Makefiles, but I haven't seen another system that would make them inherently cleaner. (I love CMake, but it's a beast, and even harder to debug than make. If it weren't for its nice cross-platform capabilities, I'm not sure it would see much use. It's also too specialized for a generic build tool. Then again, I definitely prefer it to raw Makefiles for a large C++ project.)
1. Claiming a rule makes a target, but then fails to make that target, ought to be a runtime fatal error in the makefile. I can hardly even guess at how much time this one change alone would have saved people.
2. String concatenation as the fundamental composition method is a cute hack for the 1970s... no sarcasm, it really is... but there's better known ways to make "templates" nowadays. It's hard to debug template-based code, it's hard to build a non-trivial system without templates.
3. Debugging makefiles is made much more difficult than necessary by make's default expansion of every target to about 30 different extensions for specific C-based tools (many of which nobody uses anymore), so make -d output is really hard to use. Technically once you learn to read the output it tends to have all the details you need to figure out what's going wrong, but it is simply buried in piles of files that have never and will never be found in my project.
4. The distinction between runtime variables and template-time variables is really difficult and annoying.
5. I have read the description of what INTERMEDIATE does at least a dozen times and I still don't really get it. I'm pretty sure it's basically a hack on the fact the underlying model isn't rich enough to do what people want.
6. Sort of related to 2, but the only datatype being strings makes a lot of things harder than it needs to be.
7. Make really needs a debugger so I can step through the build, see the final expansions of templates and commands, etc. It's a great example of a place where printf debugging can be very difficult to make work, but it's your only choice.
That said, I'd sort of like "a fixed-up make" myself, but there's an effect I wish I had a name for where new techs that are merely improvements on an old one almost never succeed, as if they are overshadowed by the original. Make++ is probably impossible to get anybody to buy in to, so if you don't want make you pretty much have to make something substantially different just to get people to look at you at all.
Also, obviously, many of the preceding comments still apply to a lot of other build tools, too.
$< $> $* $^ ... Not particularly explicit. You also have the very useful substitution rules, like $(SRC:.c=.o) which are probably more arcane than they ought to be. You can make similar complaints about POSIX shell syntax but at least the shell has the excuse of being used interactively so it makes sense to save on the typing I suppose.
That's my major qualm with it however, the rest of the syntax is mostly straightforward in my opinion, at least for basic Makefiles.
> I've seen plenty of unmanageable Makefiles, but I haven't seen another system that would make them inherently cleaner.
Not to push the particular product, but the approach:
FAKE (https://fake.build/), is an F# "Make" system that takes a fundamentally different tack to handling the complexity. Instead of having a new, restricted, purpose built language they've implemented a build DSL in F# scripts.
That yields build scripts that are strongly typed, just-in-time compiled, have full access to the .Net ecosystem and all your own code libraries, and are implemented in a first class functional language. That is to say: you can bring the full force of programming and abstraction to handle arbitrarily complex situations using the same competencies that one uses for coding.
As the build file grows in complexity and scope it can be refactored, and use functionality integrated into your infrastructure code, in the same way programs are refactored and improved to make them manageable. The result is something highly accessible, supportive, and aggressively positioned for modern build chains... If you can do it in .Net, you can do it in FAKE.
I don't like the syntax much, but I love the programming model. I think people who are used to imperative languages are put off by the declarative programming model of make.
Make was created by Stuart Feldman at Bell Labs in 1976. The fact that it is still in use in any form and still being discussed here is a testament to what an amazingly good job he did at the time. Whether it is the right tool for any given modern use case is up to the people who decide to use it or pass it by. I still work with almost daily, and it's in wide use by backend system engineers if my experience is any guide. Yes, it's quite clunky but also quite powerful and reliable at the few things it does. Its also pretty much guaranteed to already be installed and working on every nix system, and that's not nothing.
True, one should not edit makefiles with notepad. Proper editors have support for editing them, though.
> Syntax which relies on bizarre punctuation...
Well documented, though.[1]
> a different dependency-based-programming build tool could replace it... but that's just wishful thinking
Use prolog[2], you should be able to write this in about 42 lines. But you'll end up with the same complaints ("the syntax, the magic variables!") because, in my experience, those are just superficial: The real problem, imho, is that declarative and rule based programming are simply not part of the curriculum, especially not for auto-didactic web developers. OTOH, it only takes an hour or two to grok it, when somebody "who knows" is around and explains. It really is dead simple.
Make is such a horrifically awful thing to work with that I just end up using a regular scripting language for building. Why learn another language with all its eccentricities and footguns when I already know several others?
Because, like many other things in programming, you'll end up with a half-baked and buggy implementation of make anyways.
Incremental builds by looking for changed dependencies, a configuration file with its own significant identifiers (i.e. a build DSL shoehorned into JSON or YAML), generalized target rules, shelling out commands, sub-project builds, dry runs, dependencies for your own script, parallelization, and a unique tool with (making an generalization here) insufficient documentation.
If you're really unlucky, you'll even end up with the equivalent of a configure.sh to transpile one DSL and run environment into the DSL for your custom tool.
Incremental builds. It's a pain in the ass to write this in a good, generic way yourself. If your build tools don't already understand it, then make (and similar tools) makes for a nice addition versus just a script that invokes everything every time.
EDIT: Oh, and parallel execution, but smart parallel execution. Independent tasks can be allowed to run simultaneously. Very useful when you have a lot of IO bound tasks. Like in compilation, or if you set it up to retrieve or transmit data over a network.
It's not too hard to do that in your custom script, but more care is required because until you custom script reaches make level internal complexity you will have to manually track dependencies or make sub-optimal assumptions.
I very much prefer Rake for orchestrating multiple build systems in a web project or dependency installation or just any sort of scripting. It comes with a simplified interface to shelling out that will print out the commands you are calling.
If for some reason the project is ruby allergic, I'll try to use Invoke [0].
Sometimes I feel like people's usage of Make in web projects is akin to someone taking an axe and hand saw out to break down a tree for firewood when there are multiple perfectly functioning chainsaws in the garage.
Do you honestly think Make has no advantages over conventional scripting languages when it comes to building software? I suspect you know that it’s designed for that task and has been used for that task for several decades. Presumably you respect the community over those decades sufficiently to have a strong prior belief that there are good arguments for Make (as well as downsides), even if you can’t be bothered to research them.
Another advantage of using a scripting language is that the hard work of portability will already have been done for you by the authors of the scripting language.
Make, in contrast, works within a shell and invokes programs which may be either wildly or subtly incompatible across platforms. Add in the lacking support for conditionals in POSIX make and portability is a nightmare.
because then people new to your project can see a makefile and know that “make” and “make test” and “make install” probably work without having to learn your homebrew, one-off build system.
Compared to the autotools I'd say make is fairly decent! But yeah, it's a mess. I wonder if it's really possible to improve significantly over the status quo if you're not willing to compromise on flexibility and design the build system alongside the language itself, like Rust does with cargo for instance.
Make on the other hand is completely language agnostic, you can use it to compile C, Java, LaTeX, or make your taxes. Make is a bit like shell scripts, it's great when you have a small project and you just want to build a few C files for instance[1] but when it starts growing there always comes a point where it becomes hell.
[1] And even then if you want to do it right you need compiler support to figure header dependencies out, like GCC's various -M flags.
I'm really glad software development has largely moved away from the terse names and symbols that used to be so common. I mean patsubst? What a terrible function name! At the very least I would have added the 'h' in path.
After going through a few build systems for Javascript, I realized they were all reinventing the wheel in one way or the other, and pulled out venerable Make from the closet. It turned out to be way more expressive and easy to read.
One target to build (prod), another to run with fsevents doing auto-rebuild when a file is saved (instant gratification during dev), then a few targets for cleaup & housekeeping. All said, the file is 1/4 the size of any other JS-based build systems.
The reason I don't like doing this is portability. Since the steps within the makefile are going to be run through a shell, it is going to behave differently on different systems.
If your makefile fixes up a file using sed and your system has gnu sed, your makefile may fail on a system with BSD sed (e.g., a mac). If you rely on bash-isms, your makefile may not work on a debian system where it will be run with dash instead of bash. And so on.
If you look in your package.json, you'll surely see a dozen or so "scripts" lines that run through the same shell that make does and have all the problems you just mentioned.
I'd also like to point out that Linux and almost certainly your production environment (because it's most likely *ix) will be case sensitive. Your macOS or Windows file system? Not so much. Point is, you're already up to your neck in portability issues. My macOS coworkers often forget this detail.
It's certainly better than the 200+ lines of JavaScript something like Webpack spits out when you statr a new project. I had to debug some of our project's build files and realized halfway through that my teammates didn't write this Webpack config monstrosity, Webpack generated it by /default/.
Not to mention that gulp, grunt (do people still use this?) and webpack configs are written in JavaScript, so they're likely to have very hard to debug errors in them on the first 5 revisions.
* Parallel execution of build rules comes for free in a lot of implementations. This is really noticeable when you do heavy asset pre-processing.
* Cleanly written build rules are re-usable across projects as long as those projects have the same structure (directory layout).
* Cleanly written build rules provide incremental compilation/assembly for free: You express intermediate steps as targets and those are "cached". I put the "cached" in quotes here, because you essentially define a target file which is regenerated when it's dependencies are updated. Additional benefit: Inspection of intermediate results is easy - they are sitting there as files right in your build's output tree.
Yes! I recently used Make on some JS projects and my coworkers looked at me like I was insane. Even if you don't know advanced Make-fu it's a really good way to run all of your build steps in the right order without some crazy JSON config format.
The only build systems that I'm aware of that are monadic are redo, SCons and Shake-inspired build systems (including Shake itself, Jenga in OCaml, and several Haskell alternatives).
One realistic example (from the original Shake paper), is building a .tar file from the list of files contained in a file. Using Shake we can write the Action:
There are at least two aspects I'm aware of that increase the power of Make:
- Using `$(shell cat list.txt)` I can splice the contents of list.txt into the Makefile, reading the contents of list.txt before the dependencies are parsed.
- Using `-include file.d` I can include additional rules that are themselves produced by the build system.
It seems every "applicative" build system contains some mechanism for extending its power. I believe some are strictly less powerful than monadic systems, while others may turn out to be an encoding of monadic rules. However, I think that an explicitly monadic definition provides a clearer foundation.
I hate hearing people using 21st century or modern as reasons for inflating complexities. Without rein on complexity (whether it is hidden or not), the future is doomed, whatever you are building. While I am not saying we should avoid complexity at all cost, I am insisting that all complexity should be balanced with merits.
The merits of filenames with spaces is they read better in a GUI explorer. Whether that merit balances out all the complexity it brings is individual dependent. For me, that merit ranks very low and I avoid spaces in my filenames at all opportunities. For some, they need those filenames to be readable. And there are solutions. One solution, from those who don't code, seems to demand ("developers", paid or not) that every tool that deal with files should handle this additional complexity, regardless of their context and what they think. Another solution would be to add an additional pre-step of copying/renaming/linking/aliasing. With the latter solution, the complexity is confined.
I guess for some, it only matters with "I do work" or "they do work" rather than the big picture. That is fine. However given the context of you are working with Makefiles, then you are a developer at some level, you are supposed to do some work.
Users expect computers to work in a non-surprising ways.
It isn't natural to use dashes or underscore in file names. Training users to be afraid of spaces is just teaching them one more way that computers are scary and unpredictable.
Meanwhile over in Windows land, all tools have been expected to deal with spaces in them for what is approaching 20 years.
But to use that as evidence of why spaces should not be supported in filenames is putting the cart before the horse. The goal of software is not to perpetuate whatever mistakes have been made in the past. It's to solve problems for human beings.
And human beings have been using spaces to delimit words since long before computers existed.
You can accept that or you can keep tilting at windmills.
In every branch of science, reality wins. If your model can’t accomodate reality it’s either completely wrong or it needs adjustments, at least.
Wow.
So human beings should stop using allowed file names because it's too hard for you?
I've even worked with variables with spaces and special characters.
I don't see why filenames are so much more special, except a lot of old tools never got updated to world beyond ASCII
I don't understand why one would think that make would be a good tool for this...
> Demanding the support of spaces in filenames significantly complicates code as simple space delimination no longer works and other delimination schemes are much more error prone -- forgetting balancing quotes, any one?
Yes this is a limitation of make, but not a million other tools out there.
Make is not a panacea, no tool is - pressing a tool into a job it is un-suited for because you understand it is "Not good" (trade mark and copy right pending).
This is the classic case of having a hammer and a screw -
Deleted Comment
Even this merit is debatable. Is foo bar two things or one? I know foo-bar is one.
No. He's right, you're wrong, hzhou321. We want spaces in filenames. We even want UTF-8 if possible. We don't want crude tools that cannot handle the most basic names. You can argue all you want, this is a very very basic demand that could be met with very very basic tools but make is just too crude.
People like you are exactly the cancer in the developer community that argues away reasonable demands like spaces in filenames and perpetuates the garbage legacy tools we have.
How?
EDIT: Ok, now I got it. Boy that was a wild ride.
Being incapable of handling spaces is a bug that's been marked Minor since 2002 - https://savannah.gnu.org/bugs/?712
(plus I find double quotation marks easier to read and write than escaping every space in a path)
Edit: https://stackoverflow.com/questions/66800/promising-alternat... (they aren't really clones tho)
Specifically, a python configure script using the ninja_syntax.py module. This seems like it's a bit more complicated, but has a lot of nice attributes.
File names with spaces should just work (unlike make). The amount of hidden complexity is very low (unlike make or SCons); all the complexity lives in your configure script. It's driven by a real, non-arcane language (unlike make). Targets are automatically rebuilt when their rules/dependencies change (unlike make).
It's more difficult to install than make, but only marginally.
And what the heck happened on 31 dec 1999 that the world became a different place where suddenly people realised: there were these space things, quite useful they were, why don't we tuck them into every file name and URL and who knows what?
People have better things to do than dealing with these things.
Then that's a shortcoming which should be addressed with the tools, because humans everywhere use spaces in filenames.
For every command-line tool I make (Windows & Linux), I ensure it handles such trivial use-cases. I can't see why such a simple task is seemingly impossible to get done in GNU coreutils.
That's true.
> and the UX for that is actually pretty close to what you would want.
That is so not true. Make has deeply woven into it the assumption that the product of workflows are files, and that the way you can tell the state of a file is by its last modification date. That's often true for builds (which is why make works reasonably well for builds), but often not true for other kinds of workflows.
But regardless of that, a tool that makes a semantic distinction between tabs and spaces is NEVER the UX you want unless you're a masochist.
I've always wondered whether Make would be seen as less of a grudging necessity, and more of an elegant panacea, if operating systems had gone the route of Plan 9, where everything is—symbolically—a file, even if it's not a file in the sense of "a byte-stream persisted on disk."
Or, to put that another way: have you ever considered writing a FUSE filesystem to expose workflow inputs as readable files, and expect outputs as file creation/write calls—and then just throw Make at that?
Examples? I mean, there are some broken tools (EDA toolchains are famous for this) that generate multiple files with a single program run, which make can handle only with subtlety and care.
But actual tasks that make manages are things that are "expensive" and require checkpointing of state in some sense (if the build was cheap, no one would bother with build tooling). And the filesystem, with its monotonic date stamping of modifications, is the way we checkpoint state in almost all cases.
That's an argument that only makes sense when you state it in the abstract as you did. When it comes down to naming a real world tool or problem that has requirements that can't be solved with files, it's a much harder sell (and one not treated by most "make replacements", FWIW).
Last modification date is not always a correct heuristic to use, but it's quite cheap compared to hashing things all the time.
Make is a tool for transforming files. I wonder how it's not quite natural and correct for it to assume it's working with files?
You're referring to a standard Unix tool, an operating system where EVERYTHING is a file.
Dead Comment
Dead Comment
I guess it depends on how you define "know", but there are implicit rules.
Deleted Comment
It's so depressing when people use arguments like "it's old", "it uses tabs", and "it's hard to learn". As described by one Peter Miller, "Make is an expert system". If a tool is the most powerful, standard, and expressive among its peers, its age or the fact that it uses tabs should be inconsequential.
If anything, the fact that it's decades old and used in every major software project is a testament to its effectiveness, not a drawback.
And if "learning Make" is a barrier, that to me is a sign that someone cares more about complaining than about their project. The same way people learn Git when it's clear that it's the best tool, people learn Make. It really isn't that hard. Even the basics are enough to reap huge benefits immediately.
Part of the reason is because people see the superficial issues (like the discussion regarding spaces) before they see the value of years (or decades) of work. It doesn't help that when folks bring this up many times you get "you're holding it wrong" type responses.
I sympathise with both sides of the argument. I don't know the solution but it's unfortunate seeing folks reinventing the wheel and struggling with problems solved in the past.
My issue is that people who need serious solutions forgo Make because "tabs, man", or because Webpack has pretty console output.
The bigger reason though is that it’s not very idiomatic for JavaScript projects to use Make. It sounds like the only reason that some people go out of their way to use it is because they actually don’t want to learn something.
Any common examples?
> "It’s not very idiomatic for JavaScript projects to use Make."
While I agree that popularity is a factor in picking a tool, it shouldn't be a deciding factor. Going by popularity is precisely how we end up with a new Build System of the Year(TM) every few years. The fact that we've gone through 4 fairly prominent tools (Gulp, Grunt, Broccoli, Webpack), all of which contending to "fix" the previous, and none of which have proper DAG or incremental builds (which Make has had for decades) is damning evidence.
In other words, I think Make could be (and I wish it was) idiomatic for JS.
Just dealing with breaking changes in any one of those is a full time job!
We're stuck with Make because of network effects. I wish that it could just become "lost" forever and a different dependency-based-programming build tool could replace it... but that's just wishful thinking. The pace of our progress is doomed to be held back by the legacy of those poor design decisions for a long time to come.
In all seriousness, what's wrong with it? Significant tabs aren't great, but I feel like that's a relatively minor wart. The simple things are _very_ simple and straightforward. The more complex things are more complex, but usually still manageable...
I've seen plenty of unmanageable Makefiles, but I haven't seen another system that would make them inherently cleaner. (I love CMake, but it's a beast, and even harder to debug than make. If it weren't for its nice cross-platform capabilities, I'm not sure it would see much use. It's also too specialized for a generic build tool. Then again, I definitely prefer it to raw Makefiles for a large C++ project.)
1. Claiming a rule makes a target, but then fails to make that target, ought to be a runtime fatal error in the makefile. I can hardly even guess at how much time this one change alone would have saved people.
2. String concatenation as the fundamental composition method is a cute hack for the 1970s... no sarcasm, it really is... but there's better known ways to make "templates" nowadays. It's hard to debug template-based code, it's hard to build a non-trivial system without templates.
3. Debugging makefiles is made much more difficult than necessary by make's default expansion of every target to about 30 different extensions for specific C-based tools (many of which nobody uses anymore), so make -d output is really hard to use. Technically once you learn to read the output it tends to have all the details you need to figure out what's going wrong, but it is simply buried in piles of files that have never and will never be found in my project.
4. The distinction between runtime variables and template-time variables is really difficult and annoying.
5. I have read the description of what INTERMEDIATE does at least a dozen times and I still don't really get it. I'm pretty sure it's basically a hack on the fact the underlying model isn't rich enough to do what people want.
6. Sort of related to 2, but the only datatype being strings makes a lot of things harder than it needs to be.
7. Make really needs a debugger so I can step through the build, see the final expansions of templates and commands, etc. It's a great example of a place where printf debugging can be very difficult to make work, but it's your only choice.
That said, I'd sort of like "a fixed-up make" myself, but there's an effect I wish I had a name for where new techs that are merely improvements on an old one almost never succeed, as if they are overshadowed by the original. Make++ is probably impossible to get anybody to buy in to, so if you don't want make you pretty much have to make something substantially different just to get people to look at you at all.
Also, obviously, many of the preceding comments still apply to a lot of other build tools, too.
$< $> $* $^ ... Not particularly explicit. You also have the very useful substitution rules, like $(SRC:.c=.o) which are probably more arcane than they ought to be. You can make similar complaints about POSIX shell syntax but at least the shell has the excuse of being used interactively so it makes sense to save on the typing I suppose.
That's my major qualm with it however, the rest of the syntax is mostly straightforward in my opinion, at least for basic Makefiles.
Not to push the particular product, but the approach:
FAKE (https://fake.build/), is an F# "Make" system that takes a fundamentally different tack to handling the complexity. Instead of having a new, restricted, purpose built language they've implemented a build DSL in F# scripts.
That yields build scripts that are strongly typed, just-in-time compiled, have full access to the .Net ecosystem and all your own code libraries, and are implemented in a first class functional language. That is to say: you can bring the full force of programming and abstraction to handle arbitrarily complex situations using the same competencies that one uses for coding.
As the build file grows in complexity and scope it can be refactored, and use functionality integrated into your infrastructure code, in the same way programs are refactored and improved to make them manageable. The result is something highly accessible, supportive, and aggressively positioned for modern build chains... If you can do it in .Net, you can do it in FAKE.
Make was created by Stuart Feldman at Bell Labs in 1976. The fact that it is still in use in any form and still being discussed here is a testament to what an amazingly good job he did at the time. Whether it is the right tool for any given modern use case is up to the people who decide to use it or pass it by. I still work with almost daily, and it's in wide use by backend system engineers if my experience is any guide. Yes, it's quite clunky but also quite powerful and reliable at the few things it does. Its also pretty much guaranteed to already be installed and working on every nix system, and that's not nothing.
You know what you have to do to build git? Type make. It's amazing.
Not necessarily.
> It’s also pretty much guaranteed to already be installed and working on every nix system, and that's not nothing.
First mover advantage.
The fact that no modern language, basically nothing outside of C/C++ uses it, says a lot. And even those are moving away, see Cmake & co.
True, one should not edit makefiles with notepad. Proper editors have support for editing them, though.
> Syntax which relies on bizarre punctuation...
Well documented, though.[1]
> a different dependency-based-programming build tool could replace it... but that's just wishful thinking
Use prolog[2], you should be able to write this in about 42 lines. But you'll end up with the same complaints ("the syntax, the magic variables!") because, in my experience, those are just superficial: The real problem, imho, is that declarative and rule based programming are simply not part of the curriculum, especially not for auto-didactic web developers. OTOH, it only takes an hour or two to grok it, when somebody "who knows" is around and explains. It really is dead simple.
[1] http://pubs.opengroup.org/onlinepubs/009695399/utilities/mak...
[2] https://en.wikipedia.org/wiki/Prolog
$@ is target, because @ looks sort of like a bullseye.
significant tabs are gross, yes, but this is a one-time edit to your vimrc then you can forget about it forever.
it’s also installed everywhere and has minimal dependencies. it could be a lot worse. (see also: m4, autoconf, sendmail.cf)
Incremental builds by looking for changed dependencies, a configuration file with its own significant identifiers (i.e. a build DSL shoehorned into JSON or YAML), generalized target rules, shelling out commands, sub-project builds, dry runs, dependencies for your own script, parallelization, and a unique tool with (making an generalization here) insufficient documentation.
If you're really unlucky, you'll even end up with the equivalent of a configure.sh to transpile one DSL and run environment into the DSL for your custom tool.
EDIT: Oh, and parallel execution, but smart parallel execution. Independent tasks can be allowed to run simultaneously. Very useful when you have a lot of IO bound tasks. Like in compilation, or if you set it up to retrieve or transmit data over a network.
It's not too hard to do that in your custom script, but more care is required because until you custom script reaches make level internal complexity you will have to manually track dependencies or make sub-optimal assumptions.
If for some reason the project is ruby allergic, I'll try to use Invoke [0].
Sometimes I feel like people's usage of Make in web projects is akin to someone taking an axe and hand saw out to break down a tree for firewood when there are multiple perfectly functioning chainsaws in the garage.
[0] http://www.pyinvoke.org/
Make, in contrast, works within a shell and invokes programs which may be either wildly or subtly incompatible across platforms. Add in the lacking support for conditionals in POSIX make and portability is a nightmare.
Deleted Comment
https://9fans.github.io/plan9port/
http://www.vitanuova.com/inferno/papers/mk.html
Make on the other hand is completely language agnostic, you can use it to compile C, Java, LaTeX, or make your taxes. Make is a bit like shell scripts, it's great when you have a small project and you just want to build a few C files for instance[1] but when it starts growing there always comes a point where it becomes hell.
[1] And even then if you want to do it right you need compiler support to figure header dependencies out, like GCC's various -M flags.
Plus, to me C syntax is particularly good. You're writing real words and the computers does the things you tell it to. To the letter.
The "pat" in "patsubst" means pattern, not path, so adding an h would be incorrect. (https://www.gnu.org/software/make/manual/html_node/Text-Func...)
Make one that's generic and provides significant enough improvements that people care.
Deleted Comment
.RECIPEPREFIX option has been available for 7 years. Stop whining about non-issues.
One target to build (prod), another to run with fsevents doing auto-rebuild when a file is saved (instant gratification during dev), then a few targets for cleaup & housekeeping. All said, the file is 1/4 the size of any other JS-based build systems.
If your makefile fixes up a file using sed and your system has gnu sed, your makefile may fail on a system with BSD sed (e.g., a mac). If you rely on bash-isms, your makefile may not work on a debian system where it will be run with dash instead of bash. And so on.
If you look in your package.json, you'll surely see a dozen or so "scripts" lines that run through the same shell that make does and have all the problems you just mentioned.
I'd also like to point out that Linux and almost certainly your production environment (because it's most likely *ix) will be case sensitive. Your macOS or Windows file system? Not so much. Point is, you're already up to your neck in portability issues. My macOS coworkers often forget this detail.
Also, I use javascript where it makes sense: I rely on Browserify to resolve the web of `require`d files.
* Parallel execution of build rules comes for free in a lot of implementations. This is really noticeable when you do heavy asset pre-processing.
* Cleanly written build rules are re-usable across projects as long as those projects have the same structure (directory layout).
* Cleanly written build rules provide incremental compilation/assembly for free: You express intermediate steps as targets and those are "cached". I put the "cached" in quotes here, because you essentially define a target file which is regenerated when it's dependencies are updated. Additional benefit: Inspection of intermediate results is easy - they are sitting there as files right in your build's output tree.
One realistic example (from the original Shake paper), is building a .tar file from the list of files contained in a file. Using Shake we can write the Action:
There are at least two aspects I'm aware of that increase the power of Make:- Using `$(shell cat list.txt)` I can splice the contents of list.txt into the Makefile, reading the contents of list.txt before the dependencies are parsed.
- Using `-include file.d` I can include additional rules that are themselves produced by the build system.
It seems every "applicative" build system contains some mechanism for extending its power. I believe some are strictly less powerful than monadic systems, while others may turn out to be an encoding of monadic rules. However, I think that an explicitly monadic definition provides a clearer foundation.
http://neilmitchell.blogspot.com/2014/07/applicative-vs-mona...