Some system have GNU, some BSD.
> at least trivial to install
Then same for `just`, package manager have it.
I should specifically mention their docs. The docs are easily approachable with plenty of clear and concise examples. I even have a pdf copy of the doc book for quick reference.
https://marmelab.com/blog/2016/02/29/auto-documented-makefil...
help:
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
I stumbled over .*? since ? usually means optional, but turns out it means lazy in this context. The $$ would be makefile escaping. Dropping the grep and changing the regex slightly, I just appended this to an overengineered makefile: # Help idea derived from https://marmelab.com/blog/2016/02/29/auto-documented-makefile.html
# Prints the help-text from `target: ## help-text`, slightly reformatted and sorted
.PHONY: help
help: ## Write this help
awk 'BEGIN {FS = ":.*#+"}; /^[a-zA-Z_*.-]+:.*## .*$$/ {printf "%-30s %s\n", $$1, $$2}' $(MAKEFILE_LIST) | sort
Very nice idea, thanks for sharing it! sed -rn 's/^([^:]+):.*[ ]##[ ](.+)/\1:\2/p' $(MAKEFILE_LIST) | column -ts: -l2 | sort @awk '/^[a-zA-Z_-]+:[^#]*## .*$$/ {match($$0, /## (.*)$$/, arr); printf "\033[36m%-30s\033[0m %s\n", $$1, substr($$0, RSTART+3)}' $(MAKEFILE_LIST)It appears to give the benefits of autotools with the simplicity of make.
Its a code smell to me when your build system starts to become too complicated and not fit on a screen.
The main issue I have is that it goes from dead simple to pit of what the hell is happening with various things interacting with each others.
When `make` is used as a glorified front-end to `bash` scriptlets, why not use `bash` directly instead of having two-level of scripting?
See: https://blog.aloni.org/posts/bash-functional-command-relay/
You can do that in Bash. And now you've reinvented Makefile, but poorly.
I made a bash script that takes your Makefile and gives you a nice dialog menu for the targets that have comments. Works nicely as a self documenting project command menu.
https://gist.github.com/oppianmatt/dcc6f19542b080973e6164c71...
https://private-user-images.githubusercontent.com/48596/3262...
- tab completion of targets
- automatic dependency execution
- automatic entry points between every task
- result caching
- parallel execution
Yes, it’s possible to do all of this by hand in shell scripts. But why would I, when Make is ubiquitous and battle-tested?Sure, you can do it in bash, or python, or whatever. But then you have a cumbersome, not particularly interesting piece of code full of boiler plate. Of course, you can design it a bit, organise things neatly, and then use a config file because fiddling with the code in each project is unsustainable in the long run. At this point, you’ve just made a poor copy of make and thrown away all the good bits that result from decades of experience and weird corner cases.
The syntax of Makefiles is terrible, but make itself is very useful and versatile.
And that pattern is not abuse, it’s the sort of things Make was designed for. It’s just that we’re used to think of make as this old thing that just runs a compiler and that’s such a pain to deal with that we need Makefile generators to do it properly. And certainly that’s true for complex software compilation, but make is more versatile than that.
Certainly abuse, but hey.
This (ab)use of `make` runs multiple times a day, every day, and works perfectly every time.
The inspiration of this was an (ab)use of `make` as a way to paralellize Linux system startup: https://web.archive.org/web/20110606144530/http://www.ibm.co...
https://github.com/matheusmoreira/.files/blob/master/GNUmake...
I'm surprised at how well this thing works every time I use it. I even blogged about it.
https://www.matheusmoreira.com/articles/managing-dotfiles-wi...
Recently made a tool that processes the make database and prints the phony targets and their dependencies:
https://github.com/matheusmoreira/.files/blob/master/~/.loca...
I use it as a sort of makefile help command. Works surprisingly well too.
A task runner's tasks could be arbitrarily complicated, pulling in all sorts of dependencies of their own. This is less true for the traditional compile targets make was designed for.
Because the things we do in a Makefile are pretty much always project local and don't get reused, it limits how much heavy lifting these tasks are likely to do for us. Whereas if you built your our CLI in Python with Click or something, you would be able to make it a development dependency of your project. You can afford to invest in those tasks more because they'll be reused.
The Just command runner has the same problem, but at least it's designed to be a task runner.
I think that's called CMake.
I've never used eval.
Make's use of Bash can lead to hairy quoting/escaping. I use Make as a "dumb high-level runner" and put any sort of intelligence, like conditionals or loops or networking, in lower-level scripts or programs.
Make is an orchestrator.
Not for compiling, just as script runner.
I see this practice often and I haven't found a good reason
I use it quite a lot for automating deployments - if you want to Terraform up a VM:
make colo1-fooserver01.vm
Then if you want to run Ansible against it: make colo1-fooserver01
You don’t have remember or type all of the flags or arguments - just type make and hit tab for a list of the targets that you can buildI think the real reason is you have it all in one file rather than multiple scripts which makes it easier to edit and maintain.
... and if your scripts do all that, they've basically rebuilt make, but it's undocumented and worse.
(I say this as someone with LOTS of experience with make, and am not really a fan because I know too much and it's horrifying. But I dislike custom crippled versions even more.)
For script-running just is great, for full dep tracking build tool something like buck2 is an improvement.
The one place where make shines is when your workflow is truly file-based, so all steps can really be described as some variations of "transform file A to file B".
Make is a powerful tool. You just have to understand how it thinks about the world, and adjust your own thinking a bit if needed.
If you just want to have tasks that depend on other tasks, you don't need stamps, phony, or anything else.
But what happens when you want to say "only rebuild my venv if requirements.txt changed"? that's a file dependency that you can reasonably express between requirements.txt and venv/bin/activate. And then all of a sudden, you're squarely in Make's wheelhouse.
However, even after 10+ years of professional dev work during which I've regularly crossed paths with Makefiles, they still scare me, and I've still never written one from scratch myself. I cling to bash scripts, which I'm also a rookie at (or, for more complex cases, I write Python scripts, which I'm much more comfortable with).
I guess one day I'll read the manual, and digest some tutorials, and actually learn make. But I've made it this far...
Make has its warts (look at when and what it was designed for, after all!), but I've found it much easier to write Makefiles than YAML-for-github-runners.
If you're used to YAML, you get pleasantly surprised when using Make, which does execution of dependency trees in a more readable manner than most CI/CD YAML files do.
If the source code is kept tight, it doesn't yield much of a speed-up, though - precompiling hundreds of unnecessary headers can take a lot of time. Better to put some effort into moving unnecessary #includes out of header files. In fact, you can use your log of cache-hits to guide that work.
My opinion may be a bit skewed by the fact that I write code that gets built on a variety of different platforms, though, and make is essentially universal. It lets me have a consistent build process regardless of platform.
It's also very useful for automation that isn't related to building code.
.PHONY: foo
foo:
echo foo
bar: barin
cp barin bar
.PHONY: baz
baz: bar
echo bazhttps://marketplace.visualstudio.com/items?itemName=lfm.vsco...
It allows you to click above target to run target.
I've found Earthly [0] to be the _perfect_ tool to replace Make. It's a familiar syntax (combination of Dockerfiles + Makefiles). Every target is run in an isolated Docker container, and each target can copy files from other targets. This allows Earthly to perform caching and parallelization for free, and in addition you get lots of safety with containerization. I've been using Earthly for a couple of years now and I love it.
Some things I've built with it:
* At work [1], we use it to build Docker images for E2E testing. This includes building a Go project, our mkdocs documentation, our Vue UI, and a ton of little scripts all over the place for generating documentation, release notes, dependency information (like the licenses of our deps), etc.
* I used it to create my macOS cross compiler project [2].
* A project for playing a collaborative game of Pokemon on Discord [3]
IMO Makefiles are great if you have a few small targets. If you're looking at more than >50 lines, if your project uses many languages, or you need to run targets in a Docker container, then Earthly is a great choice.
[0]: https://earthly.dev/
[1]: https://p3m.dev/
[2]: https://github.com/shepherdjerred/macos-cross-compiler
[3]: https://github.com/shepherdjerred/discord-plays-pokemon
For anything needing light-to-moderate software configuration management, cmake is readily available and simple.
If you're running a Google then you're using a build and DVCS that make use of extensive caching, synthetic filesystems, and tools that wrap other tools.
I won't say what not to use. ;)
I don't believe that.
make: ** No rule to make target `love'. Stop.
I much prefer Make to alternatives like Just or Taskfile. Besides the fact that more people know Make, Make actually has incredibly useful functionality that alternatives remove 'for simplicity', but then later you realize you want that functionality and go back to Make. Sometimes old tricks are the best tricks.