I currently have a project with two files server.py and config.json. I created a Makefile this way:
all: config server
config: config.json
echo "config modified"
server: server.py
echo "server modified"
.PHONY: all config server
When I run make, I expect it to display "config modified" or "server modified" only if the specified files were modified. However, both strings are displayed each time I call make, even if config.json and server.py have not been modified before.
I used Makefiles for other projects in C and it seemed to always work well. What did I do wrong?
Thanks.
In order to decide if a prerequisite (e.g. server.py) is newer or not than the target (e.g. server) make needs two timestamps to compare: one for the prerequisite and one for the target. This means that your prerequisites and targets must be files. If your targets are not files, make has only one timestamp left. How do you want it to decide if this single timestamp is new or not? By comparing it to what? Current time? It would always be old. What else?
Moreover, you declared config and server as phony targets. In other words you told make that these targets shall always be remade, even if a file with that name exists.
If you want make to decide alone whether a target must be remade or not you must stick to the canonical make rule:
target-file: prerequisite-file...
recipe
This way make compares the last modification times of the target file and its prerequisite files and knows whether the target file is older than some prerequisites or not, that is, whether it shall be remade or not. In your case this could be something like:
all: config server
config: config.json
echo "config modified"
touch $#
server: server.py
echo "server modified"
touch $#
.PHONY: all
The touch $# recipe updates the last modification time of the target file (or creates it) and now, each time it runs, make can do its job properly. And yes, it forces you to accept the existence of these two files, but really, think about it, there is no other solution. These two files are a kind of make memory: they record when each recipe was last run.
If you don't want to see these files you can hide them (e.g. by naming them .config and .server if you are under some UNIX-like OS).
You can even create them in a dedicated sub-directory for easier cleaning:
TMPDIR := .tmpdir
all: $(TMPDIR)/config $(TMPDIR)/server
$(TMPDIR)/config: config.json
echo "config modified"
mkdir -p $(#D); touch $#
$(TMPDIR)/server: server.py
echo "server modified"
mkdir -p $(#D); touch $#
.PHONY: all clean
clean:
rm -rf $(TMPDIR)
Related
I'm a fairly new user of GNU Make. I want to get a list of Golang files and build each one of them using Make.
I want to create a target that will receive the apt Go file as a param.
In the snippet I've written the control of the program never reaches the %.go target.
Here is a snippet of a file.
EXECUTABLES := $(wildcard cmd/*/*/*.go)
%.go:
echo "Build the go file"
build: $(EXECUTABLES)
echo $<
Output:
echo cmd/abc/handler/main.go
cmd/abc/handler/main.go
I modified the script to this but I'm facing the same issue. Also tried replacing %.go with *.go and with cmd/abc/handler/main.go
Here is one of the variants mentioned above.
%.go:
echo "Hello world"
build: $(wildcard cmd/*/*/*.go)
echo $<
Anything I might be missing here?
You have a rule that tells make how to build a .go file. But, you already HAVE a .go file. Make doesn't have to build it, it's a source file that you wrote. That rule has no prerequisites, so as far as make is concerned if the file exists, it's up to date (it doesn't depend on any other file).
So, when you ask make to build that file make looks and says "this file exists already, and it doesn't depend on anything, so the thing you asked me to do is already done and I don't need to run any commands".
When you write a makefile you have to think of it from the end first back to the beginning. What is the thing you want to end up with? That is the first target you write. Then what files are used as inputs to that final thing? Those are the prerequisites of that target. And what commands are needed to create the target from the prerequisites? That is the recipe.
Then once you have that, you think about those prerequisites, considering them as targets. What prerequisites do they have? And what commands do you need to turn those prerequisites into that target?
And you keep going backwards like that until you come to a place where all the prerequisites are original source files that can't be built from anything, then you're done.
My makefile has a target for .env, which will simply create a .env file. When the file already exists, I will receive make: `.env' is up to date.
The makefile is like:
.env: .env.sample
#echo " ⚠️ Preparing local configuration..."
#test -f .env && echo "$$DOTENV_ERROR" && exit 1 || true
#cp .env.sample .env
However, I would like the same behaviour for a file inside a folder. Like:
./config/config.json: ./config/config.json.sample
#echo " ⚠️ Preparing local configuration..."
#test -f ./config/config.json && echo "$$CONFIGJSON_ERROR" && exit 1 || true
#cp config/config.json.sample config/config.json
Instead of receiving make: './config/config.json' is up to date. I receive my CONFIGJSON_ERROR, I would like to receive the same .env message, saying that the file is up to date.
I think there's a misunderstanding: make will invoke a recipe if either of two situations happens. The first is that the target file does not exist. That seems to be the one you're relying on above.
The second is if at least one of the prerequisites is newer than the target. In your recipe you'll fail if that happens because the target exists already but the target is out of date. That appears to be what's happening here: the target config/config.json exists but config/config.json.sample is newer than it.
I don't really understand why you're trying to throw an error in this situation so it's hard to suggest an alternative.
I'm not completely sure of what behavior you're after, but I'll take a guess...
Make will rebuild your target if the target does not exist, or any of its dependencies are newer than the target. So in your case, I'm guessing ./config/config.json.sample is likely newer than ./config/config.json. If you want to not copy, even if the .sample file is newer, then you could use an order only prerequisite:
./config/config.json: | ./config/config.json.sample
...
The result would be that .config/config.json would only be rebuilt if it does not exist (assuming it has no other dependencies). If neither file existed, then ./config/config.json.sample would be built before ./config/config.json.
I know this has been asked before, but none of the solutions I've found work for me because they're anti-DRY.
I have a number of targets that depend on things that can't readily be timestamped -- such as files copied from another system. What I'd like to be able to do is list dependencies in a variable, like nobuild=this,that, and have those targets be assumed to be up-to-date. Since I have a lot of these, I don't want to ifdef around each one; what would be pseudocodibly preferable would be something like
ignorable-target: dependencies
$(call ifnobuild,$#)
.. rest of normal build steps ..
where the ifnobuild macro expanded to some sort of exit-from-this-recipe-with-success gmake instruction if ignorable-target was mentioned in the nobuild variable.
I also don't want to get into multi-line continued shell commands in order to defer the conditional to the recipe itself; I want to be able to tell make "Assume these targets are up-to-date and don't try to build them," so I can test other aspects with the local copies already obtained from the problematic recipes.
There isn't any sort of exit-recipe-with-success mechanism in gmake, is there?
[Edited to hopefully make the situation more clear.]
Here's an example. Targets remote1 and remote2 each involve using ssh to do something time-consuming on a remote system, and then copying the results locally. Target local1 is built locally, and isn't a time sink. target-under-work depends on all three of the above.
local1: local1.c Makefile
remote1: local1
scp local1 remote-host:/tmp/
ssh remote-host /tmp/local1 some-args # takes a long time
scp remote-host:/tmp/local1.out remote1
remote2: local1
scp local1 other-host:/tmp/
ssh other-host /tmp/local1 other-args # takes a long time
scp other-host:/tmp/local1.out remote2
target-under-work: local1 remote1 remote2
do-something-with remote1,remote2
Now, when I just run make target-under-work, it's going to run the recipes for remote1 and remote2. However, the local copies of those files are 'good enough' for my testing, so I don't want them run every time. Once things go into production, they will be run every time, but while I'm developing target-under-work, I just want to use the copies already built, and I can rebuild them daily (or whatever) for the necessary testing granularity.
The above is over-simplified; there are multiple steps and targets that depend on remote1 and/or remote2. I see how I can get the effect I want by making them order-only prerequisites -- but that would mean changing the dependency list of every target that has them as prerequisites, rather than making a single change to remote1 and remote2 so I can use some variable from the command line to tell their recipes 'pretend this has been built, don't actually build it if there's already a copy.'
I hope this makes my question more clear.
No, this early exit make feature does not exist.
Note that your problem is probably under-specified because you don't explain what behaviour you want when a slow target does not exist yet.
Let's assume that the slow targets listed in nobuild shall be rebuilt if and only if they don't exist. Instead of using make functions to early exit their recipe you could use make functions to "hide" their list of prerequisites. This way, if they already exist, they will not be rebuilt, even if they are outdated. The only subtlety is that you will need the second expansion to use the $# automatic variable in the lists of prerequisites. In the following example slow (your remoteX) depends on fast1 (your local1). fast2 (your target-under-work) depends on fast1 and slow:
host> cat Makefile
# Expands as empty string if $(1) exists and
# listed in $(nobuild). Else expands as $(2).
# $(1): target
# $(2): prerequisites
define HIDE_IF_NOBUILD
$(if $(wildcard $(1)),$(if $(filter $(1),$(nobuild)),,$(2)),$(2))
endef
nobuild :=
fast1:
#echo 'build $#'
#touch $#
fast2: fast1 slow
#echo 'build $#'
#touch $#
.SECONDEXPANSION:
slow: $$(call HIDE_IF_NOBUILD,$$#,fast1)
#echo 'build $#'
#touch $#
# Case 1: slow target not listed in nobuild and not existing
host> rm -f slow; touch fast1; make fast2
build slow
build fast2
# Case 2: slow target not listed in nobuild and existing and outdated
host> touch slow; sleep 2; touch fast1; make fast2
build slow
build fast2
# Case 3: slow target listed in nobuild and not existing
host> rm -f slow; touch fast1; make nobuild="slow" fast2
build slow
build fast2
# Case 4: slow target listed in nobuild and existing and outdated
host> touch slow; sleep 2; touch fast1; make nobuild="slow" fast2
build fast2
I'd like a set of makefile rules that create a symlink to one of several code modules before building the project. The name of the make target would determine the file to which the symlink points. For example:
The user invokes 'make R3000'
Make sees that 'data.asm' doesn't exist yet, so a symlink is created from 'data_R3000.asm' to 'data.asm'
The build process continues, using data.asm
How can I set up make rules to do this?
Maybe something like:
MODULES := $(patsubst data_%.asm,%,$(wildcard data_*.asm))
all:
...
data.asm:
[ -n "$(filter $(MAKECMDGOALS),$(MODULES))" ] || { echo unknown module: $(MAKECMDGOALS) ; exit 1; }
ln -s $(filter $(MAKECMDGOALS),$(MODULES)) $#
Then make sure data.asm is listed as a prerequisite in the appropriate rules.
I would do something like this:
.PHONY mklink
mklink:
test -e data_$(MAKECMDGOALS).asm || exit 1
ln -s data_$(MAKECMDGOALS).asm data.asm
and then make all (and other targets) dependent on mklink. The reason you shouldn't make data.asm your target in the rule is that if you run make R3000, then data.asm will be created, and then if you run make L2000, the data.asm file will be pointing to the wrong directory, and will not be overwritten (I'll assuming this is not what you want). The test line checks if the link target exists, and if not, it exits with 1, causing the target to fail. You should also add a check that MAKECMDGOALS is exactly one element as well.
If I have a makefile that calls another makefile, how to I get the master makefile to correctly check if the dependencies of the subordinate makefile have changed?
For example, if I have the rule
server:
#cd $(SERVERDIR) && $(MAKE)
That invokes make in the subdirectory in which I build an executable "server". However, if I change one of the files that make up server, the parent make doesn't see the changes and refuses to rebuild server - "make: `server' is up to date."
How can I get the master makefile to correctly detect when there's a change in one of the dependent files (something like $(SERVERDIR)/server.c, for example?
It looks like you want to use a phony target
.PHONY: server
server:
#cd $(SERVERDIR) && $(MAKE)
There's a detailed description of the Phony target here, but the short description is you're telling the makefile that there will never be a file that corresponds with this server target, and therefore it won't consider server up to date if there is a file named server in the directory.
Your target name matches the name of one of the files or directories in your main Makefile directory.
Assuming you need to build everything in a subdirectory called server, this rule:
server:
$(MAKE) -C server
will not work, as the target server is a directory, has no source files and doesn't need to be built then.
This one:
srv:
$(MAKE) -C server
will work, as long as there is no file or directory called srv.
You don't:
Recursive Make Considered Harmful
Implementing non-recursive make
But yes, if you have no choice, e.g. because you don't control the sub-makefile, a .PHONY target is what you are looking for.