πŸ‘€beardicusπŸ•‘2yπŸ”Ό104πŸ—¨οΈ92

(Replying to PARENT post)

One big reason I rely on Make: its old. Make has been well maintained for nearly 50 years, and is deeply integrated into the programming ecosystem as a whole.

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.

πŸ‘€dahfizzπŸ•‘2yπŸ”Ό0πŸ—¨οΈ0

(Replying to PARENT post)

The best thing about make is that it’s available everywhere on POSIX systems. macOS comes with it pre-installed, and every Linux distro or *BSD does too, or offers a package for it that’s often depended on by everything else devtools-wise. This means make skills are super transferable; possibly more so than bash/shell skills.

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`?

πŸ‘€jitlπŸ•‘2yπŸ”Ό0πŸ—¨οΈ0

(Replying to PARENT post)

Make is a great parable of our industry.

* 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.

πŸ‘€asveikauπŸ•‘2yπŸ”Ό0πŸ—¨οΈ0

(Replying to PARENT post)

I'd love to look into a parallel universe where Make didn't make so many really basic mistakes by 2023 standards. For instance, it has a lot of the "was there an error? eh, just keep going" philosophy from early days. I'd like it to be an error if a make rule claims to make a certain dependency and it fails to do so. That one change in a single stroke would eliminate a lot of Make's hostility when trying to first understand it. There's a series of similar things that could be done.

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.

πŸ‘€jerfπŸ•‘2yπŸ”Ό0πŸ—¨οΈ0

(Replying to PARENT post)

I imagine many of Mike's points would be addressed just as well by Just or most any other task runner... but I thought his main point of "Makefile as documentation" was valuable.

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.

πŸ‘€beardicusπŸ•‘2yπŸ”Ό0πŸ—¨οΈ0

(Replying to PARENT post)

The article has the following near the end:

"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?

πŸ‘€peatfreakπŸ•‘2yπŸ”Ό0πŸ—¨οΈ0

(Replying to PARENT post)

I always put a Makefile in all my projects, especially because I change language often and tooling.

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.

πŸ‘€kuonπŸ•‘2yπŸ”Ό0πŸ—¨οΈ0

(Replying to PARENT post)

> Note: use tabs rather than spaces to indent the commands in your makefile. Otherwise Make will crash with a cryptic error

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

πŸ‘€barbazooπŸ•‘2yπŸ”Ό0πŸ—¨οΈ0

(Replying to PARENT post)

If make is so great, why did people create systems like CMake, QMake, SCons, Bazel, etc.?

Or how about: https://news.ycombinator.com/item?id=21571559

Fabricate looks nice and simple!

πŸ‘€ameliusπŸ•‘2yπŸ”Ό0πŸ—¨οΈ0

(Replying to PARENT post)

An additional reason that drives me to use make on most new projects is the polyglot nature of many code repos. There are language/ecosystem-specific build tools: grunt, rake, etc. but often real world projects are a mix if different languages and to double down on just one language-specific tool feels unnecessarily constraining.

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.

πŸ‘€jondevalπŸ•‘2yπŸ”Ό0πŸ—¨οΈ0

(Replying to PARENT post)

The Makefile syntax is beyond cryptic. And I'm speaking as someone who used the autoconf/automake chain for years to build software. The author only made a simple example that requires downloading, transforming and uploading a file. Try and do something a bit more complex, deal with m4 macros, maybe the autoconf syntax, and you'll feel like you're back in the 1970s.

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.

πŸ‘€blacklightπŸ•‘2yπŸ”Ό0πŸ—¨οΈ0

(Replying to PARENT post)

There are also some other 'interesting' ways to use Make.

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...

πŸ‘€Mister_SnugglesπŸ•‘2yπŸ”Ό0πŸ—¨οΈ0

(Replying to PARENT post)

Why not just bash: https://github.com/gnat/doit

Honorable mention: single python script.

πŸ‘€geenatπŸ•‘2yπŸ”Ό0πŸ—¨οΈ0

(Replying to PARENT post)

πŸ‘€ameliusπŸ•‘2yπŸ”Ό0πŸ—¨οΈ0

(Replying to PARENT post)

A fast guide to make:

     git clone git://bitreich.org/english_knight

     cd  english_knight

     less english_knight
Enjoy.
πŸ‘€anthkπŸ•‘2yπŸ”Ό0πŸ—¨οΈ0

(Replying to PARENT post)

Make is old enough to have in it's implementation a great number of the ideas that are needed for build systems work or describe various kinds of build tasks. If you invent another tool you're probably going to reinvent those ideas eventually as you hit the problems they were invented for in make and if you haven't designed for them in advance your solutions might end up being just as unsatisfactory.

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:

https://gittup.org/tup/

πŸ‘€t43562πŸ•‘2yπŸ”Ό0πŸ—¨οΈ0

(Replying to PARENT post)

I haven't used make since college and will probably never use it again.
πŸ‘€debacleπŸ•‘2yπŸ”Ό0πŸ—¨οΈ0