(Replying to PARENT post)
Sure there are more user-friendly tools written in the programming language du jour, but those will always need some special snowflake setup process for all of your developers. If youβre cross-platform, now you need encode that in a setup script before your project can buildβ¦ Maybe you can offer a Makefile just to install `better-make`?
(Replying to PARENT post)
* It's dead simple. It encodes dependency graphs and does stat() calls to check and compare timestamps. From that, you can have desirable features: minimal rebuilds of changed files, parallel build, etc ... Sure it's not perfect at this, timestamps can skew or whatever, dependencies can be improperly specified. But it does enormous heavy lifting with a very "dumb" implementation.
* It's extremely influential. Even if you're not using makefiles, chances are some build tool is stat()ing files based on some representation of dependencies. In some form or another they probably got that expectation from make.
* The original version was written by one guy over a weekend. Shows that our industry can have enormous, industry defining contributions from a small team.
(Replying to PARENT post)
All of its replacements generally involve such a paradigm shift that it's no longer a comparable, to use a real estate term.
I've got a whole list; $ being used for both make and shell replacements, leading to $$$$(var) abominations and the general lack of clarity as to which variables are for which things, many places where lists were clearly bodged in as after thoughts when they should be designed in from the beginning, tabs as meaningful whitespace is a classic but still a problem, .PHONY being a rather ugly hack when there should be a clear distinction between "a command I want to provide" versus "this is how to make a dependency", and while this may not be a user-visible behavior change, taking away all the C defaults so the strace is no longer a nightmare of trying every implicit C rule before actually trying the rule I want it to try. At least a mode of invoking the shell that looks more like a programming language where I pass a clean array of strings rather than the shell language, already a nightmare of string interpolation on its own terms, buried in another string interpolation language sitting on top of it. A thought-out solution to recursive versus included makefiles. And I never became a make master, but by my 21st century standards, trying to use make is just a fractal of bad 1970s ideas hitting me at every turn, so I'm quite sure if I had to work with it a lot more I could go on for quite a while. As it is I think I've forgotten some.
I think people, not entirely illegitimately, have trouble separating the essential concept of make, which I think was quite solid, from the accidental comedy of errors that we have as a received standard. So a lot of make replacements end up running from both of them, and often end up with a much more annoying essential concept at their core, such as a super-heavy-duty enumeration of "these are the exact things you may ever be interested in doing", which puts them behind the eight ball no matter how good their accidental choices may be.
(Replying to PARENT post)
After reading this way back in 2015 I decided to give it a try for a not-code-related task: downloading a book from the internet archive, copying out all the images, and running some adjustments and conversions on them with ImageMagick:
https://github.com/beardicus/bk-fig-phillips
It was fairly ridiculous but works well and I learned a lot.
(Replying to PARENT post)
"To see more real-world examples of makefiles, see my [World Atlas](https://github.com/mbostock/world-atlas) and [U.S. Atlas](https://github.com/topojson/us-atlas) projects, which contain makefiles for generating TopoJSON from Natural Earth, the National Atlas, the Census Bureau, and other sources."
I checked those repositories because the descriptions of the makefiles sound interesting, but I couldn't find the makefiles. Am I looking wrong?
(Replying to PARENT post)
I always create a make help task which gives me a list of commands and I have a few conventions, make build, make release, make server (dev server). Most of the time the Makefile is very simple and just call npm, cargo, webpack, ...
Being able to enter my blog written in hugo or a phoenix project or a ruby on rails project and just hit make server has been a real help to me.
That being said, I would not use make alone as a build tool for a complex project, it gets very cryptic when complexity is added. But as a "entry point" it serves me well.
(Replying to PARENT post)
Using this as an example, are there more modern equivalent tools that may be a bit more user friendly? I appreciate make and I get its age and the complexity and all, it's just sometimes I need something that's simple and explicit, without the historic baggage.
Edit: It's not about spaces/tabs obviously, but about "Otherwise Make will crash with a cryptic error" which I used as an example
(Replying to PARENT post)
Or how about: https://news.ycombinator.com/item?id=21571559
Fabricate looks nice and simple!
(Replying to PARENT post)
Having a build tool like make that is more closely aligned with the system level feels more natural for orchestrating build/test/deploy tasks that by their very nature contain more cross-cutting concerns.
(Replying to PARENT post)
Don't get me wrong: make has been a loyal friend for most of my life. I've built a lot of software using make and the whole auto* build chain. And the author doesn't even mention it's biggest strength point: portability. Every single machine with a UNIX-like system has make. But I have to admit when some technology is ready for retirement. More modern alternatives exist today. Cmake is the most obvious, Bazel is also a promising one. Even simple shell scripts can do the job for easy things.
(Replying to PARENT post)
As a batch runner: https://news.ycombinator.com/item?id=32441602
As an init system (which inspired my use of it as a batch runner): https://web.archive.org/web/20110606144530/http://www.ibm.co...
(Replying to PARENT post)
Honorable mention: single python script.
(Replying to PARENT post)
(Replying to PARENT post)
git clone git://bitreich.org/english_knight
cd english_knight
less english_knight
Enjoy.(Replying to PARENT post)
Examples just for the sake of it:
- phony targets - we want something to happen that has no result as a file or we want to group a lot of targets so we can refer to them easily in other lists of dependencies.
- deferred expansion assignment (you're able to say that something is X where you don't know what X is yet and will only know at the time that commands are being executed)
- pattern rules - a way to not have to write rules for every single object
- order-only prerequisites - X must happen before Y if it's happening but a change in X doesn't trigger Y
This is just a small selection and there are missing things (like how to handle rules that affect multiple targets).
It's all horrible and complex because like a lot of languages there's a manual listing the features but not much in the way of motivations for how or why you'd use them so you have to find that out by painful experience. e.g. deferred expansion is horrible in small makefiles and freaks most people out but ends up being essential in big ones quite often - especially where you're generating the makefile and don't at "this moment" know what the precise location or name of some file will be until later on in parsing.
It's also very difficult to address the warts and problems in (GNU) make because it's so critical to the build systems of so many packages that any breaking change could end up being a disaster for 1000s of packages used in your favorite Linux distribution or even bits of Android and so on.
I find pattern rules useless because I cannot limit their scope in the ways I need. There's no chance of changing that though, as it would break other makefiles.
So it's in a very constrained situation BECAUSE of it's "popularity".
Make is also not a good way to logically describe your build/work - something like Meson would be better - where you can describe on the one hand what a "program" model was as a kind of class or interface and on the other an implementation of the many nasty operating system specific details of how to build an item of that class or type.
Make has so many complex possible ways of operating (sometimes not all needed) that it can be hard to think about. You have to develop a mental model of it and in the end it can come down purely to how GNU make operates at the code level. It doesn't feel very well defined - you're essentially learning the specific implementation rather than a standard.
The things that Make can do end up slowing it down as a parser such that for large builds the time to parse the makefile becomes significant.
Make models a top down dependency tree - when builds get large one starts to want an Inverted Dependency Tree. i.e. instead of working out what the aim of the build is and therefore what sub-components need to be checked for changes we start with what changed and that gives us a list of actions that have to be taken. This sidesteps parsing of a huge makefile with a lot of build information in it that is mostly not relevant at all to the things that have changed. TUP is the first tool I know about that used this approach and having been burned hard by make and ninja when it comes to parsing huge makefiles (ninja is better but still slow) I think TUP's answer is the best:
(Replying to PARENT post)
I can start a new project that relies on Make, and be extremely confident that Make will continue to work and be maintained for the lifetime of my project. 20+ years from now, Make will still work. My Make knowledge will be relevant for my entire career.
New and shiny replacements like Just are tempting, but you have to consider the real cost of:
1) Learning a new build system
2) Onboarding new devs with an unfamiliar build system
3) Dealing with the eventual deprecation of the new shiny, once something newer and shinier comes out. Rewrite your build scripts, GOTO 1.