Phony Targets(gnu.org)
gnu.org
Phony Targets
https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html
18 comments
Years ago, make would have been a bizarre and old-fashioned tooling choice for a software project, at least by people not writing C.
I strongly suspect that Golang choosing make is responsible for its reintroduction into the broad mainstream in the last 5-10 years. A lot of repos I interact with at work are full of makefiles.
Make is the bell bottoms of build tools.
I strongly suspect that Golang choosing make is responsible for its reintroduction into the broad mainstream in the last 5-10 years. A lot of repos I interact with at work are full of makefiles.
Make is the bell bottoms of build tools.
There's a lot of C around there.
These days, I require any project we work on to have a “one command bootstrap” or run command. Make let’s you sprinkle in just enough magic to do that. You can easily have a full library of generic targets to share too.
https://github.com/mintel/build-harness
https://github.com/mintel/build-harness
I believe most makefiles I see these days — pretty much all except the ones I write myself — are the monstrosities that come out of automake. If that is the first impression new programmers get of make, then make is dead, because nobody is going to want to touch it with a bargepole.
shake is a Haskell Make-like build system. I found it useful for benchmarking C code over lots of different combinations of compilers and compiler options. The initial Make solution I came up with was unwieldy and bug-prone.
It was very easy to update code, add new compilers and compiler options without rerunning benchmarks for everything. Only for what was changed.
Check it out: https://hackage.haskell.org/package/shake
It was very easy to update code, add new compilers and compiler options without rerunning benchmarks for everything. Only for what was changed.
Check it out: https://hackage.haskell.org/package/shake
i am an enthusiastic user of the probable antipattern of a makefile that is 90% phony targets.
For whatever reason, a makefile full of little baby shell scripts that are wired together seems cleaner and tidier and overall nicer to me.
Even ones that in no way at all ever depend on anything like a build state or looking to see if files have changed or whatever.
`make <action>` as the front-end to automate common tasks in my projects seems nicer than 15 shell scripts littering the top level of the repo, even if it is grossly underusing what make is capable of.
For whatever reason, a makefile full of little baby shell scripts that are wired together seems cleaner and tidier and overall nicer to me.
Even ones that in no way at all ever depend on anything like a build state or looking to see if files have changed or whatever.
`make <action>` as the front-end to automate common tasks in my projects seems nicer than 15 shell scripts littering the top level of the repo, even if it is grossly underusing what make is capable of.
I was "converted" away from shell script to Makefile project management years ago by a formet co-worker and I'm still thankful he persisted in convincing me.
Makefiles can be horrendous when you encounter them in C projects, with all the templating and generation and what not. But just a single Makefile at the project root with phony targets for all common tasks is a godsend.
One pet peeve of mine is to make `make` the only requirement to bootstrap a project dev environment from scratch. Since make is so ubiquitous you can pull in just about any requirement and dependency with clever use of targets, order only prerequisites, and files.
Makefiles can be horrendous when you encounter them in C projects, with all the templating and generation and what not. But just a single Makefile at the project root with phony targets for all common tasks is a godsend.
One pet peeve of mine is to make `make` the only requirement to bootstrap a project dev environment from scratch. Since make is so ubiquitous you can pull in just about any requirement and dependency with clever use of targets, order only prerequisites, and files.
I've just started learning makefiles - how would you say they are better from a regular bash file full of fumctions? I.e is it the "declarativeness" and it being a convention.
If you just want to build something, perhaps it doesn't matter much.
But when you are developing, typing "make" will rebuild the file you just edited plus anything that depends on the contents of that file (assuming a well written Makefile, which is not that hard to write for most cases). Trying to make that work in a bash script would involve lots of manually written tests, which would be easy to get wrong / get out of sync.
But when you are developing, typing "make" will rebuild the file you just edited plus anything that depends on the contents of that file (assuming a well written Makefile, which is not that hard to write for most cases). Trying to make that work in a bash script would involve lots of manually written tests, which would be easy to get wrong / get out of sync.
Indeed, declerativeness and conventions but also dependency resolving and ubiquity are the main plusses for me.
Bash scripts used for these tasks tend to accrue a lot of boilerplate and every script has it's own conventions. So every time you need to debug them you need to debug a lot of code.
With make every target can be it's own little isolated piece of code with input and output which makes reasoning about them a lot easier.
A few tips when learning makefiles: terminology[0]: it's a lot easier to find answers if you know what it's called what you are searching for. Automatic Variables[1]: learn these by heart, and the way they allow DRY targets. If you have a dependecy that is not a file you can use a dot prefixed file as a proxy.
[0] https://www.gnu.org/software/make/manual/html_node/Rule-Synt... [1] https://www.gnu.org/software/make/manual/html_node/Automatic...
Bash scripts used for these tasks tend to accrue a lot of boilerplate and every script has it's own conventions. So every time you need to debug them you need to debug a lot of code.
With make every target can be it's own little isolated piece of code with input and output which makes reasoning about them a lot easier.
A few tips when learning makefiles: terminology[0]: it's a lot easier to find answers if you know what it's called what you are searching for. Automatic Variables[1]: learn these by heart, and the way they allow DRY targets. If you have a dependecy that is not a file you can use a dot prefixed file as a proxy.
[0] https://www.gnu.org/software/make/manual/html_node/Rule-Synt... [1] https://www.gnu.org/software/make/manual/html_node/Automatic...
You might like justfiles[1]. They’re designed pretty much on this philosophy.
`just` doesn’t have the ubiquitous availability of `make`, but I’ve found it’s much more user friendly for this use case, especially with the documentation features and ability for commands to accept arguments.
[1] https://github.com/casey/just
`just` doesn’t have the ubiquitous availability of `make`, but I’ve found it’s much more user friendly for this use case, especially with the documentation features and ability for commands to accept arguments.
[1] https://github.com/casey/just
Looks interesting, just gave it a try converting a simple Makefile. But it seems like Just does away with file targets and file dependencies as a whole? As far as I can tell there is no way to skip dependencies that are already fulfilled?
The problem with the en-vogue style of phony-only Makefile is when you start to add dependencies. When both your "unittests" and "run" target depend on the "virtualenv" target but your Makefile author never grasped the technique (and limitations) of Make dependencies, things start to become brittle.
Can you elaborate on this? As a Make newbie, what you wrote seems like something that I would do...
So you want to populate your virtual environment before you run the unit tests, right?
The command to do so is trivial, but when do you need to actually execute it?
Normal make targets have prerequisites and outcomes (I think they are called goals). These must be files and make works by comparing their modification timestamps to see if a recipe has to be (re-)executed.
Now what is the prerequisite for your virtual environment?
What is the output? venv/bin/python ? What if you need to pip install some stuff (maybe only for testing)? What happens when the installation of a single package fails?
I think having a script (or a phony target for people with strange tastes ;) ) makes at least clear that there is no half-assed dependency tracking ongoing.
I think one way to figure this out would be to have a test script that runs inside the venv and outputs some kind of checksum.
The command to do so is trivial, but when do you need to actually execute it?
Normal make targets have prerequisites and outcomes (I think they are called goals). These must be files and make works by comparing their modification timestamps to see if a recipe has to be (re-)executed.
Now what is the prerequisite for your virtual environment?
What is the output? venv/bin/python ? What if you need to pip install some stuff (maybe only for testing)? What happens when the installation of a single package fails?
I think having a script (or a phony target for people with strange tastes ;) ) makes at least clear that there is no half-assed dependency tracking ongoing.
I think one way to figure this out would be to have a test script that runs inside the venv and outputs some kind of checksum.
That's actually really easy. Your target here is your venv site-packages, while requirements file is the prerequisite. The idea is that your site-packages folder has to be recreated/updated (pip install) when it is older than the requirements file. One thing you have to do is to simply update the directory timestamp each time you add a dependency, because adding a file into a folder does not change the folder timestamp. Having that it would be like
venv/site-packages: requirements.txt pip install -r $< touch $@
venv/site-packages: requirements.txt pip install -r $< touch $@
I am pretty sure that this does not work. Maybe GNU Make accepts folders as targets (but that would be news to me). But even then, the folder itself does not inform you whether your pip install was successful or not.
> Mike Bostock
https://bost.ocks.org/mike/make/