Multiple rules for one target - makefile

I have large project and it's compiled on-demand on several different machines with different OS'es.
One of targets has foo.res in it's dependencies. This resource file can be compiled using either one of 2 different rules:
First rule requires text file foo.txt to be present. This rule also requires some of the non-cross platform software to be installed in the system. This rule can not be used in-field by the our engineers, as it won't run under old MacOS.
Second rule uses pre-compiled resource file foo.res in resources directory and simply copies it to build/. Obviously, this is unwanted workaround, but i have no other choice.
I've tried to describe 2 rules in one Makefile, but i've got such error:
Makefile:78: warning: overriding recipe for target 'build/foo.res'
Makefile:75: warning: ignoring old recipe for target 'build/foo.res'
Currently I use Makefile with one of the rules commented-out:
.PHONY: compile
build/app.bin: some_files... build/foo.res
(Here is compiler call)
build/:
-mkdir build/
.PHONY: clear
clear:
-rm -rf ./build
... Lots of stuff here ...
# build/foo.res: res/foo.res | build/.
# cp res/foo.res build/foo.res
build/foo.res: res/foo.txt | build/.
some_app -o build/foo.res res/foo.txt
I deploy pre-compiled res/foo.res to some machines and then I swap commented rules, so regular cp is used. This works fine, until some changes are introduced to Makefile in some of commits. Then it blocks fast-forward update in git.
How can I configure make to fire only one of the rules, based on matched dependencies?

You can't do this with explicit rules. An explicit rule tells make THIS is how you build this target. You can create two different pattern rules that can build the same target. A pattern rule says this is one way you can build that target.
So, change your makefile to:
build/%.res: res/%.res | build/.
cp $< $#
build/%.res: res/%.txt | build/.
some_app -o $# $<

Related

Make file Doesn't detect changes in source files

I am very much new to make files , I am facing very basic problem , My Makefile doesn't detect changes I made to source files . The problem is , when I first time generate consoleapp binary from my source file i get expected output . But When I change source file again and when I run make again it says
make: 'consoleapp' is up to date , So what changes I have to give to make file so that it detects my changes
Below is my Makefile :
consoleapp:
g++ consoleapp.cpp -o consoleapp
clean:
rm -rf *.o consoleapp
This is my Source File :
#include <iostream>
using namespace std;
int main()
{
cout<<"I am ok \n"; // I am changing this line again after giving make
return 0;
}
make relies on the makefile author to tell it what each target's prerequisites are -- that is, which other targets or files affect the construction of the target in question, so that if they are newer or themselves out of date then the target is out of date and should be rebuilt. As your other answer already indicates, you do not designate any prerequisites for your targets, so make considers them out of date if and only if they don't exist at all.
That's actually problematic for both targets, albeit in different ways. For target consoleapp, which represents an actual file that you want to build, the failure to specify any prerequisites yields the problem you ask about: make does not recognize that changes to the source file necessitate a rebuild. The easiest way to fix that would be to just add the source file name to the recipe's header line, after the colon:
consoleapp: consoleapp.cpp
g++ consoleapp.cpp -o consoleapp
Generally speaking, however, it is wise to minimize duplication in your makefile code, and to that end you can use some of make's automatic variables to avoid repeating target and prerequisite names in your rule's recipe. In particular, I recommend always using $# to designate the rule's target inside its recipe:
consoleapp: consoleapp.cpp
g++ consoleapp.cpp -o $#
It's a bit more situational for prerequisites. In this case, all the prerequisites are source files to be compiled, and furthermore there is only one. If you are willing to rely on GNU extensions then in the recipe you might represent the sources via either $< (which represents the first prerequisite), or as $^ (which represents the whole prerequisite list, with any duplicates removed). For example,
consoleapp: consoleapp.cpp
g++ $^ -o $#
If you are not using GNU make, however, or if you want to support other people who don't, then you are stuck with some repetition here. You can still save yourself some effort, especially in the event of a change to the source list, by creating a make variable for the sources and duplicating that instead of duplicating the source list itself:
consoleapp_SRCS = consoleapp.cpp
consoleapp: $(consoleapp_SRCS)
g++ $(consoleapp_SRCS) -o $#
I mentioned earlier that there are problems with both of your rules. But what could be wrong with the clean rule, you may ask? It does not create a file named "clean", so its recipe will be run every time you execute make clean, just as you want, right? Not necessarily. Although that rule does not create a file named "clean", if such a file is created by some other means then suddenly your clean rule will stop working, as that file will be found already up to date with respect to its (empty) list of prerequisites.
POSIX standard make has no solution for that, but GNU make provides for it with the special target .PHONY. With GNU make, any targets designated as prerequisites of .PHONY are always considered out of date, and the filesystem is not even checked for them. This is exactly to support targets such as clean, which are used to designate actions to perform that do not produce persistent artifacts on the file system. Although that's a GNU extension, it is portable in the sense that it uses standard make syntax and the target's form is reserved for extensions, so a make that does not support .PHONY in the GNU sense is likely either to just ignore it or to treat it as an ordinary rule:
.PHONY: clean
clean:
rm -rf *.o consoleapp
because your target has no dependence. Please use this codes that rely to all cpp file in current dir to update binary.
SRCS=consoleapp.cpp
consoleapp: $(SRCS)
g++ $< -o $#

copying only changed files between directories using Makefile

I read this: Makefile: Copying files with a rule but couldn't do it.
To make it simple, suppose I have two directories dir1 and dir2. Under dir1 I have three files: rabbit.c, tiger.c and bus.c .
I made Makefile like this:
dir2/rabbit.c:dir1/rabbit.c
dir2/tiger.c:dir1/tiger.c
dir2/bike.c:dir1/bike.c
dir2/%:
cp -f $< $#
I specified the prerequisites in three separate lines and specified the unified recipe for the three targets. I expected when I touch any file under dir1, make will copy that file to dir2. But this happend only for rabbit.c. What is wrong?
ADD(after selecting an answer) :
After realizing what's wrong by Takkat's answer, I fixed it and later modified it further and I think this is the correct simplest Makefile for this case.
.PHONY:all
LIST:=rabbit.c tiger.c bike.c
DSTFILES:=$(addprefix dir2/, $(LIST))
all: $(DSTFILES)
dir2/%:dir1/%
cp -f $< $#
Make chooses a default target in your makefile and, unless you specify differently on the command line, it builds just that target (and all prerequisites required to build that target).
The default target in a makefile is, by default, the first explicit target listed.
So in your makefile the first rule is:
dir2/rabbit.c:dir1/rabbit.c
so the first explicit target is dir2/rabbit.c, so that's all make builds.
If you want to build multiple targets by default, you need a first target that lists all the "real" targets as prerequisites; put this line first in your makefile:
all: dir2/rabbit.c dir2/tiger.c dir2/bike.c
and it will work. It's often considered good practice to declare targets like this, which don't relate to real files on the disk, as phony:
.PHONY: all

Makefile applies a rule recursively even if it shouldn't

I have a very bizzare problem with GNU make. I have the following files:
a/x.html
b/Makefile
b/c/Makefile
The contents of a/x.html are irrelevant. The contents of b/Makefile are as follows:
SRC=../a
all: x.html
%.html: ${SRC}/%.html
rsync $< $#
The contents of b/c/Makefile are the same, except for the definition of SRC:
SRC=../../a
If I run make in b/c/ the result is as expected:
rsync ../../a/x.html x.html
and x.html gets copied from a/ to b/c/.
However, if I run make in b/ the output I get is several lines of:
make: stat: ../a/../a/.. (repeated many times) ../a/x.html: File name too long
It seems that make is applying the rule for %.html recursively, but why? Is there something obvious I am missing?
To build a target that matches the pattern %.html (i.e. any target name that ends in .html), make applies the rule if it can build the dependency (target built from the original target with ../a/ prepended).
You ask to build x.html. This matches the pattern %.html, so the rule applies: make sees if it can build ../a/x.html.
../a/x.html matches the pattern %.html, so the rule applies: make sees if it can build ../a/../a/x.html.
../../a/x.html matches the pattern %.html, so the rule applies, etc.
The stem character can match any part of a path, including directory separators.
You can see what make is trying by running make -r -d (-d to show debugging output, -r to turn off built-in rules which would cause a huge amount of noise).
When you're in b/c, this stops at step 2 because ../../a/x.html exists but ../../../../a/x.html doesn't.
One way to fix this is to list the files on which you want to act. You can build that list from the list of files that already exist in ../a:
$(notdir $(wildcard ${SRC}/*.html)): %.html: ${SRC}/%.html
rsync $< $#
This has the downside that if the HTML files in ../a are themselves built by a rule in b/Makefile, then running make in b won't built them in a pristine source directory. This shouldn't be a problem though: it would be unusual to have a makefile in b build things outside b.
Another approach which doesn't have this defect is to use an absolute path.
%.html: $(abspath ${SRC})/%.html
rsync $< $#

Make file possible accidental order-only dependency?

I am pulling out my hair trying to debug an issue with make. It seems like make is randomly treating certain prerequisites as order-only prerequisites, resulting in them being left out of the static library target that depends on them. Most of the time the build works find but occasionaly some .cpp files are built but not included in the .a. When i run Make with --debug I see the following output for the suspect prerequisites.
Prerequisite `blah.o' is newer than target `/path/to/foo.a`
Prerequisite `blah1.o' is newer than target `/path/to/foo.a`
Prerequisite `blah2.o' is newer than target `/path/to/foo.a`
No need to remake target `/path/to/foo.a'
For all of the prereqs that do make it into the .a the last line is "Must remate target /path/to/foo.a" as I would expect.
Because make is invoked in several subdirectories, target /path/to/foo.a is updated several times. We are not running make in parallel so I don't think updates to the file are stomping each other. It seems that make is deliberately not updating the .a file despite the fact that the .o's are newer. The recipe to make foo.a is as follows:
$(OBJLIB): $(OBJS)
$(AR) $(ARFLAGS) $(OBJLIB) $?
Where ARFLAGS=rv and OBJLIB would be /path/to/foo.a.
Am i right in thinking that the .o files are being treated as order-only dependencies? Is there something else I'm missing here? I am using $(info) to output the contents of OBJLIB and OBJS and there are no errant pipe ('|') characters making their way into the variable contents that would induce order-only dependencies.
Unfortunately the answer had nothing to do with make. As far as I can tell the filesystem is the real culprit. Several people were experiencing success with the build but I was not. The difference between our systems which were using a common build environment was that I was building on an ext3 filesystem while they were using an ext4 filesystem.
Since ext3 does not support sub-1s timestamps (ext4 does) in some cases when the rule was invoked with only a few CPP files they were being compiled in the same second that the archive was updated by a previous invocation and everything was ending up with the same timestamps. Copying the directory over to an ext4 filesystem fixed the issue.
The real fix is to write a proper set of make rules but at least we have an answer as to why it was working for everyone but me.
You mentioned several updates to the .a file because make is invoked in different subdirectories. Probably the message
No need to remake target `/path/to/foo.a'
comes from one subdirectory, and is newer- from another. Consider building the lib out of all objects in one step.
Try this instead.
$(OBJLIB): $(OBJS)
$(AR) $(ARFLAGS) $(OBJLIB) $^
Your problem is that the variable $? is a list of dependencies that are newer than the target, while $^ is a list of all dependencies.
Also, you can use $# for to be more idiomatic.
$(OBJLIB): $(OBJS)
$(AR) $(ARFLAGS) $# $^

Code generation and make rule expansion

Assume I have a make rule:
.PHONY:gen
gen: auto.template
generate-sources auto.template
that creates a bunch of files, for example auto1.src, auto2.src, auto3.src and so on.
If I now have rules to build targets from *.src files, like this:
$(patsubst %.src,%.target,$(wildcard *.src)): %.target: %.src
build $< > $#
How can I tell make to first execute the gen rule and then expand the preconditions for the second rule template? GNU extensions are welcome.
Note: I would like to keep it in one make invocation; A trivial solution to this would be to put the second rule in a secondary Makefile.secondrun and call $(MAKE) -f Makefile.secondrun after gen was processed. But I was wondering if there is a better option.
Building off Beta's answer, here's how you can do it using makefile remaking in GNU make, which is not the same thing as recursive make. Rather, it updates an included makefile using a rule in the main makefile, then restarts the original make instance. This is how *.d dependency files are typically generated and used.
# Get the list of auto-generated sources. If this file doesn't exist, or if it is older
# than auto.template, it will get built using the rule defined below, according to the
# standard behavior of GNU make. If autosrcs.mk is rebuilt, GNU make will automatically
# restart itself after autosrcs.mk is updated.
include autosrcs.mk
# Once we have the list of auto-generated sources, getting the list of targets to build
# from them is a simple pattern substitution.
TARGETS=$(patsubst %.src,%.target,$(AUTO_SRCS))
all: $(TARGETS)
# Rule describing how to build autosrcs.mk. This generates the sources, then computes
# the list of autogenerated sources and writes that to autosrcs.mk in the form of a
# make variable. Note that we use *shell* constructs to get the list of sources, not
# make constructs like $(wildcard), which could be expanded at the wrong time relative
# to when the source files are actually created.
autosrcs.mk: auto.template
./generate-sources auto.template
echo "AUTO_SRCS=`echo *.src`" > autosrcs.mk
# How to build *.target files from *.src files.
%.target: %.src
#echo 'build $< > $#'
Short answer: you can't. Make determines all of the rules it will have to execute before it executes any rule.
Longer answer: maybe you can. As you say, you can use recursive Make explicitly, or surreptitiously by, say, building a file which your makefile will include (I'm looking at you, Jack Kelly). Or if you could somehow obtain a list of the files which gen will build, you could write a rule around that. Or you could take a leap of faith like this:
%.target: %.src
build $< > $#
%.src: gen;

Resources