Make seeks dependencies in a directory before switching to it? - makefile

I tried to write a make rule of this form:
lib%.so: computations/%.h
make -C computations/ -f makefile $<
Output:
make -C computations/ -f makefile computations/test.h
make[1]: Entering directory `/home/shai/csm/csm2/src/computations'
make[1]: *** No rule to make target `computations/test.h'. Stop.
make[1]: Leaving directory `/home/shai/csm/csm2/src/computations'
make: *** [libtest.so] Error 2
Well, it seems that it looks for %.h after switching library. No biggie, I'll just try removing
lib%.so: %.h
make -C computations/ -f makefile $<
but now it doesn't recognize the rule at all!
Output:
shai#ubuntu:~/csm/csm2/src$ make libtest.so
`make: *** No rule to make target `libtest.so'. Stop.
Is make playing with my mind? How could a change in the dependencies make it stop recognizing the target?

How could a change in the dependencies make it stop recognizing the target?
Most likely because it can't find a file that fits the pattern of the dependency, because test.h is in another directory. You could use VPATH to have it search for files in other directories.
Edit: but this still won't fix your problem completely, because $< will be substituted by computations/test.h, which won't be found in the directory computations (as in the first error you got). You might try $(notdir $<), but I think it's an ugly hack (if it works; I haven't tried it). This results from the design of your makefile and project structure.

You're using make wrong.
Make's design is actually fairly simple at heart: you tell it
how to create targets (The body of the rule).
which targets it needs to create first (The dependency list).
Make then topological sorts the dependency information so it executes the bodies in the right order.
Your rule with $< (which expands to the list of dependencies) means you're telling the invoked make to create the dependency %.h not the target lib%.so.
The rule has to create the target. If the dependency needs to be created, you need to tell make how to do so with another, separate rule, not try to ensure it is up to date in this rule.
As you're trying to coordinate across directories, I'll recommend the paper Recursive make considered harmful, which can often be a nice way to organize things, though not the only possible way to do things.

It might just be the way you copy/pasted it, but you're missing a tab in the second form.

Related

Makefile warning: pattern recipe did not update peer target

Background:
We have a Makefile that sits in the middle of a dev-ops pipeline that does things in unusual ways, for legacy reasons not worth describing. This has worked fine for years, but upon upgrading to GNU Make 4.4, it started generating a new warning of the form:
Makefile:X: warning: pattern recipe did not update peer target 'Y'.
I'm 99% sure this warning is harmless for our use case, but the new warning output is tripping CI failures in our pipeline. This Makefile is invoked by an automated script and the output is also parsed by an automated script, neither of which can easily be changed.
Minimal reproducer:
Makefile:
%-seq %-par :: %.cpp force
$(MAKE) do_complicated_stuff SRC=$< TGT=$#
do_complicated_stuff:
#echo doing complicated stuff with SRC=$(SRC) TGT=$(TGT)
touch $(TGT)
%-seq :: %.c force
echo Error: this rule should not be run in this MRE
exit 1
.PHONY: force
Command:
$ rm -f *-{seq,par} ; touch foo.cpp ; make --no-print-directory foo-seq
make do_complicated_stuff SRC=foo.cpp TGT=foo-seq
doing complicated stuff with SRC=foo.cpp TGT=foo-seq
touch foo-seq
Makefile:2: warning: pattern recipe did not update peer target 'foo-par'.
Here make was invoked to build the foo-seq target, but it's complaining about the foo-par target which does not exist and was not even mentioned. A given make command will be invoked to build exactly one target at a time, and the .PHONY dependency ensures the rule will be run (regardless of whether or not Make considers dependencies to be up-to-date). Make will be invoked (by the script) many times in the same directory to build each test.
Question:
Why is GNU Make 4.4 suddenly generating this new warning for an idiom that has always silently worked correctly in the past, and what is the minimal change to silence this harmless warning?
Constraints/Requirements:
The solution probably needs to involve a pattern rule of some kind, because the set of possible source file names cannot be encoded in the Makefile. The name is provided only on the command line (which cannot be changed), but the rule needs to match it against the existence of the source file to ensure the correct rule is selected and executed.
In the real Makefile both of the rules modeled above are much more complicated than shown here, so we'd like to avoid duplicating the first rule for each of the target patterns (which does silence the warning, but causes maintainability problems).
Finally, for portability reasons the solution needs to continue functioning correctly without new warnings for all versions of GNU Make back to v3.80 (possibly negotiable if there's really no better solution).
The current plan is that in the next release of GNU make, this will become an error not a warning. So you should address it now. The reason for the change is that there are other errors in how GNU make handles patterns that cannot be fixed without changing this.
The problem is this rule:
%-seq %-par :: %.cpp force
$(MAKE) do_complicated_stuff SRC=$< TGT=$#
I'm not sure what you intend for this rule to actually do, but what it tells make it will do is a single invocation of this recipe will create both targets %-seq and %-par. That's what multiple patterns in a single rule means, and has always meant.
If your recipe does not actually build both of those targets then you will see this issue.
The simplest thing to do is write the rule twice, once for each target, which will work on all versions of GNU make and was always the correct way to write it:
%-seq :: %.cpp force
$(MAKE) do_complicated_stuff SRC=$< TGT=$#
%-par :: %.cpp force
$(MAKE) do_complicated_stuff SRC=$< TGT=$#
ETA
Wanting to avoid "duplicating the recipe" is trivial enough. Just put it into a variable:
define COMPLEX_RECIPE
$(MAKE) do_complicated_stuff SRC=$< TGT=$#
endef
%-seq :: %.cpp force ; $(COMPLEX_RECIPE)
%-par :: %.cpp force ; $(COMPLEX_RECIPE)

How to let Makefile see target from another file

I have such Makefile with a content for creating a script:
.PHONY cluster-run
cluster-run:
make $(TARGET) --just-print >> tmp_script.sh;
And another one nn.mk:
.PHONY nn-model
include Makefile
nn-model:
python run-nn.py
I have two separate Makefiles for readability, because their content is big and I have another '*.mk' files, like nn-lstm.mk, nn-conv.mk, etc.
I launch as follows:
make -f nn.mk cluster-run TARGET=nn-model
But make gives an error:
make nn-model --just-print >> tmp_script.sh;
make[1]: *** No rule to make target `nn-model'. Stop.
make: *** [cluster-run] Error 2
For me such behaviour is strange because target nn-model actually exists. How can I fix this problem?
First you should never use raw make in recipes. Always use the $(MAKE) variable.
Second, the problem is because when you run the sub-make you don't provide the -f option:
make nn-model --just-print >> tmp_script.sh;
Because of that, it reads Makefile but not nn.mk, and so there's no rule to build the target nn-model.
Remember if you run a sub-make like this it's starting an entirely new make process with a clean slate: none of the targets defined in the parent make process are known to the sub-make when it starts.
I don't know what you mean by target nn_model actually exists but there's definitely no file named nn_model or you wouldn't get that error.
So what's happening is that when you build cluster-run it invokes a recursive make, which reads Makefile, and asks it to build $(TARGET) (which will include nn-model).
Notice that the recursive make is a new make and does not inherit variables or rules from the parent make, so this make instance has no clue how to build nn-model If you want the child make to see this, then the child make must include the parent one...

Target dependency: Makefile no rule to make target error

Here is the make file that I am running,
.PHONY: build
build: pre_build_script $(OUTPUTDIR)/%.cpp
$(OUTPUTDIR)/%.cpp: $(INTXTDIR)/%.txt
python.exe $(SOMEDIR)/somepythonscript.py $(INTXTDIR) $(OUTPUTDIR)
.PHONY: pre_build_script
pre_build_script:
pythonscript.exe $(PREBUILDDIR)
This is the output that I get:
$ make build
pythonscript.exe $(SAMPLEDIR)
make: *** No rule to make target '../obj/CPP/%.cpp', needed by 'build'. Stop.
Looks like I'm missing on some sytanx as I get this error inspite of declaring the target dependency. Any suggestions?
This means make cannot find a file named $(OUTPUTDIR)/%.cpp, a prerequisite for the first rule.
You cannot use % as a wildcard anywhere in a rules like this:
build: pre_build_script $(OUTPUTDIR)/%.cpp
it needs to be a part of pattern rule or a static pattern rule.
You can use $(wildcard $(OUTPUTDIR)/*.cpp) to get a complete list of files, but it's an anti-pattern (pun intended). You are supposed to either exactly know what files are used in what rules, or (know it even better and) create a generic pattern rule.
The second pattern rule (one using somepythonscript.py) is supposed to work on a single source-target file pair, $(INTXTDIR)/%.txt -> $(OUTPUTDIR)/%.cpp. The command seems to process all the files in the directory, which is not incremental: it will redo all the work even if only one file was updated.

makefile - make: *** No rule to make target all'. Stop

i have been trying to make a correct makefile for a while now but i keep getting the error "make: *** No rule to make target `all'. Stop."
i have one main program: mpasswdsort.c
and the c file wich is used by mpasswdsort, it comes with a header as well:
list.c and list.h
my makefile:
CC=gcc
CFLAGS=-Wall -pedantic -ansi
all: mpasswdsort
server: mpasswdsort.o list.o
$(CC) mpasswdsort.o list.o -o mpasswdsort
mpasswdsort.o: mpasswdsort.cpp
$(CC) $(CFLAGS) mpasswdsort.cpp
list.o: list.cpp
$(CC) $(CFLAGS) list.cpp
clean:
rm -f server client *.o core
I am unsure if its wrong in the makefile or if the makefil isnt supposed to be a .txt file.
The error message you present indicates that make does not see any rule for building target "all". It is exactly the same diagnostic that GNU make emits when there is no makefile at all. Since the makefile contents you present do contain a rule for target "all", I conclude that make is not seeing them.
That may be because the makefile is in a different directory, because its name is different from the ones make tries by default (Makefile or makefile is conventional; GNU's version of make also checks for GNUmakefile), or because an access-control issue prevents make from reading the file. Since you remark
I am unsure if its wrong in the makefile or if the makefil isnt
supposed to be a .txt file.
, the most likely conclusion is that (at least) the filename is wrong. Makefiles are text files, but text file names don't necessarily end with ".txt". In fact, on Linux and other UNIXes, most of them don't. Makefiles shouldn't have such an extension, though, technically, you can use the -f option to tell make the name of the makefile to use.
For me, quite simply, I was initiating the make init command outside of the target directory that I wished to create the makefile. Hope this helps someone.
I'm using a MAC so renaming the "MakeFile" to "Makefile" did the trick for me.
And one more thing, I got this error after fixing the previous one:
Makefile:3: *** missing separator. Stop.
Replacing the four spaces with tabs solved this problem for me! Simply just delete the space before the commands in your "Makefile" and put a tab behind them.

Makefile pattern rules not working

I am learning makefiles, and can't just wrap my head around this problem i am having, and would like to understand how/why this fail.
I have half a dozen erlang files in a src directory. I want to compile these into a ebin directory, without having to define a rule for each and every one of them. According to the Gnu make documentation, pattern rules should be right up my alley.
However, with the following makefile, all I get from make is make: *** No targets. Stop. Why is that?
ebin/%.beam: src/%.erl
mkdir -p ebin
erlc -o ebin $<
Edit: Based on this answer, I now understand that i would have to explicitly declare the targets, for instance by using make ebin/cmplx.beam. However, i still do not understand how i should write my makefile to get my desired behaviour - since I have half a dozen targets (and in other projects even more), this seems like an unnecessary hassle. Is there not a way to define targets based on the source file names?
The target rule tells make that whenever it needs to produce a beam file in the ebin directory, and there exists a corresponding erl file in the src directory, it can use erlc.
However, this doesn't tell make that this is what it needs to do. You could explicitly tell make what it needs to do by giving it a target on the command line:
make ebin/foo.beam
If you don't give a target on the command line, make will pick the first non-pattern rule in the makefile as its target. However, your makefile doesn't have any non-pattern rules, so there is no target.
What you probably want is that for each existing erl file in src, make should consider the corresponding beam file in ebin to be a target. You can achieve that by calling wildcard and patsubst:
erl_files=$(wildcard src/*.erl)
beam_files=$(patsubst src/%.erl,ebin/%.beam,$(erl_files))
ebin/%.beam: src/%.erl
mkdir -p ebin
erlc -o ebin $<
all: $(beam_files)
(The indented lines need to be actual physical tabs, not spaces.)
That way, running make will rebuild all beam files that are out of date. all gets chosen as the default target, and it in turn depends on all beam existing or potential, each of which in turn depends on the corresponding erl file.
This trick is described in the GNU make manual.

Resources