makefile: performing include to a .mak file after certain action on it - makefile

I have a large project I'm working on, in which I want to perform include to some .mak file, but only after I make change to this file content via a command inside the original makefile. Since it's a large project it will be hard to write code, so I will give this ridiculous example instead:
I have some small C project that all it's C and header files are in the same directory, and I need to write a makefile. I'm not allowed to use clean rule in the makefile I write, but I have a file named file.mak that I can include in my makefile. Content of file.mak:
.PHONY: clean
cleam:
$(RM) $(objs) test
The problem here is that the rule is cleam and not clean. I'm also not allowed to change manually file.mak , but I'm allowed to do this with a command inside the original makefile. This can be done easily by:
sed -i 's/cleam/clean/g' file.mak
So I thought of writing the makefile like this:
CC = gcc
srcs = $(wildcard ./*.c)
objs = $(srcs:.c=.o)
test: $(objs) change_file include_file
$(CC) $^ -o $#
%.o: %.c
$(CC) -c $< -o $#
change_file:
$(shell sed -i 's/cleam/clean/g' file.mak)
include_file: change_file
include file.mak
But I get the following error:
include: Command not found
So I understand that there is a problem of using include inside a rule, so is there a way to achieve what I want?

(GNU) make has a feature Remaking Makefiles that can be used for scenarios like this, but your approach is wrong. include is a directive and can't be used in a recipe.
Instead, when you include a file, make first checks for rules creating this exact file and executes them. As in your case, the file you want to include already exists, you have to make this rule .PHONY to force its execution. It would look like this:
.PHONY: file.mak
file.mak:
sed -i 's/cleam/clean/g' file.mak
include file.mak
As a more robust alternative (without the need for a phony rule), consider creating a fixed version (copy) and include this:
file_fixed.mak: file.mak
sed -e 's/cleam/clean/g' <file.mak >file_fixed.mak
include file_fixed.mak

Related

Why does make recompile all files?

My goal is the following: I have a directory src which contains markdown files (.md). I want to run a command on each of these files so that the comments are removed and the edited files are stored in a separate directory. For this I want to use make.
This is the Makefile I have:
.PHONY: clean all
BUILD_DIR := build
SRC_DIRS := src
SRCS := $(shell find $(SRC_DIRS) -name *.md)
DSTS := $(patsubst $(SRC_DIRS)/%.md,$(BUILD_DIR)/%.md,$(SRCS))
all: $(DSTS)
# The aim of this is to remove all my comments from the final documents
$(DSTS): $(SRCS)
pandoc --strip-comments -f markdown -i $< -t markdown -o $#
clean:
rm $(BUILD_DIR)/*.md
While this works in general, I noticed that the command is executed on all files, even though I changed only one single file.
Example: I have 3 Files src/a.md, src/b.md and src/c.md. Now I run make and all files are correctly generated in the build folder. Now I only edit c.md and run make again. I would expect that make only "compiles" src/c.md anew but instead all three files are compiled again. What am I doing wrong?
Your line
$(DSTS): $(SRCS)
is saying ‘All of the DSTS depend on all of the SRCS’, so whenever any one of the $(SRCS) is newer than any of the $(DSTS), this pandoc action will be run.
That's not what you want to express. What you want is something more like
$(BUILD_DIR)/%.md: $(SRC_DIRS)/%.md
pandoc --strip-comments -f markdown -i $< -t markdown -o $#
all: $(DSTS)
That says that all of the $(DSTS) should be up to date, and the pattern rule teaches Make what each one depends on, and how to build it, if it is out of date.
(As a general point, looking your original rule, it's rarely the right thing to do to have multiple targets in a rule, as you have with $(DSTS); also note that in your original, $< always refers only to the first of the dependencies in $(SRCS))

GNU make generate assembly first, them compile them to .o and link

SOURCE=a.c b.c c.c
ASM=$(patsubst %.c,%.s, $(SOURCE))
all:%.o
gcc -o test $^
$(ASM):%.c
gcc -S -o $# $<
%.o:%.s
gcc -c -o$# $<
I want to generate assembly code (.s) first, then compile the assembly code to object (.o), then link them.
But it seems above makefile code does not work. What is the correct code?
When asking questions, does not work is never very useful... if it worked you probably wouldn't be asking a question! :-) Instead you should always show the command you ran and the output you received (or at least the failing part of the output if it's long). Please cut and paste the actual text rather than paraphrasing messages. Also, including the version of the make program you're using (make --version) and the platform you're running on is often helpful.
Luckily this time we can figure out the problem without this information:
This:
$(ASM):%.c
gcc -S -o $# $<
where ASM is a.s b.s c.s, is not a pattern rule because the targets don't contain a pattern character %. That means the prerequisite %.c is not treated as a pattern, but as an actual file name, literally %.c which obviously doesn't exist.
Similarly, this:
all: %.o
has the same problem: all is a target, so this depends on the literal file named %.o which doesn't exist, and can't be created.
Also as a general rule every recipe that creates a target must create the actual target you told make it would, so this all rule is wrong because the target name is all but the recipe creates the target test.
Finally, it's a very bad idea to name your program test because test is a common UNIX program and a shell built-in, so if you run test it won't do the right thing (if you run ./test it will work).
You want to have all depend on the program you want to build, say mytest, and mytest should depend on the actual .o files:
all: mytest
mytest: $(SOURCE:.c=.o)
gcc -o $# $^
Next, you need to define a pattern rule that knows how to create an assembly file from a source file:
%.s : %.c
gcc -S -o $# $<
That, along with your other pattern rules, is all you need: make will figure it all out from that.
Finally, make has a built-in rule that tells it how to build object files directly from source files. It's best to get rid of this to force make to use your rules; add this to your makefile to delete it:
%.o : %.c

Makefile stops running at the middle [duplicate]

Hopefully this is a very simple question. I have a makefile pattern rule that looks like this:
%.so : %.f %.pyf
f2py -c -L${LAPACK_DIR} ${GRASPLIBS} -m $* $^ ${SOURCES} --opt='-02' --f77flags='-fcray-pointer' >> silent.txt
I want the makefile to build a number of .so files, so I tried to get it to build two files (radgrd_py.so and lodiso_py.so) by doing this:
radgrd_py.so lodiso_py.so:
%.so : %.f %.pyf
f2py -c -L${LAPACK_DIR} ${GRASPLIBS} -m $* $^ ${SOURCES} --opt='-02' --f77flags='-fcray-pointer' >> silent.txt
and then tried this:
radgrd_py.so:
lodiso_py.so:
%.so : %.f %.pyf
f2py -c -L${LAPACK_DIR} ${GRASPLIBS} -m $* $^ ${SOURCES} --opt='-02' --f77flags='-fcray-pointer' >> silent.txt
But in each case, it only builds the first target that I specify. If I run 'make radgrd_py.so' it works fine, I'm just not sure how to specify a list of files that need to be built so I can just run 'make'.
The usual trick is to add a 'dummy' target as the first that depends on all targets you want to build when running a plain make:
all: radgrd_py.so lodiso_py.so
It is a convention to call this target 'all' or 'default'. For extra correctness, let make know that this is not a real file by adding this line to your Makefile:
.PHONY: all
Best way is to add:
.PHONY: all
.DEFAULT: all
all: radgrd_py.so lodiso_py.so
Explanations:
make uses the first target appearing when no .DEFAULT is specified.
.PHONY informs make that the targets (a coma-separated list, in fact) don't create any file or folder.
all: as proposed by schot

How can I make a pattern rule dependency optional in a Makefile?

I would like make to reference the timestamp of a dependency if and only if the file already exists. I have a pattern rule like this:
%.pdf: %.sil
sile $< -o $#
This works great in normal situations, but the .sil file makes an external reference to a lua file of the same name if it exists. How do I make make aware of this so it checks the timestamps and regenerates the PDF if the lua file is newer but ignores the dependency if the file doesn't exist at all?
This:
%.pdf: %.sil %.lua
sile $< -o $#
…only works for cases where the file exists and causes an error if it doesn't.
With a sufficiently new version of GNU make you can use:
.SECONDEXPANSION:
%.pdf: %.sil $$(wildcard $$*.lua)
sile $< -o $#
See the manual section for SECONDEXPANSION targets and the wildcard function.

separate builds in separate directories

I'm sure this is a totally normal thing to do, but I can't figure out how to get make to do this.
I have a compiler that generates make dependencies of the usual form:
M/A.o : M/A.hs
M/B.o : M/A.o
So I write a rule to compile %.hs into %.o, add a rule to link the binary, include the dependencies file, and all is well. But I want to have several binary targets with different flags. E.g. I want build/test built with -DTESTING and build/profile built with -prof. So I need to keep the .o files in a separate tree, where they will be compiled with special flags.
The straightforward way I can think of would be to have dependencies that look something like this:
build/test/M/A.o : M/A.hs
build/test/M/B.o : build/test/M/A.o
build/profile/M/A.o : M/A.hs
... etc.
And then rules so that %.hs to build/test/%.o compiles with -DTESTING, etc. I think this would work, but it's clumsy, means preprocessing the deps file to add all that build/whatever/ prefix stuff, and would multiply its size by however many kinds of builds.
VPATH appears to be designed for this sort of thing and my idea was that I could set the VPATH and compiler flags depending on the target, and it almost works, but:
%.o: %.hs
#mkdir -p build/M
cp $< build/$#
VPATH = build
main: M/A.o M/B.o
cat $^ >$#
M/A.o : M/A.hs
M/B.o : M/B.hs
The first time the main target wants to run 'cat M/A.o M/B.o >main' which seems contrary to the gnu make documentation that says $^ should include the include the VPATH directory in which the dependency was found. Curiously, if I remove 'main' and make again, this time it uses the correct path. This is GNU make, 3.81.
What's going on here? Is there a better way to build with different flags? VPATH seems like a clumsy tool, surely there is a better way?
Make is working correctly. It tries cat M/A.o M/B.o >main the first time because it can't find the prerequisites it needs, but it knows a rule for M/A.o' andM/B.o(<em>not</em>build/M/A.o' and build/M/B.o) and expects that that is what the rule will produce. If you remove main and try again, it will find build/M/A.o' andbuild/M/B.o` via VPATH.
Let's modify this makefile in stages. First we change the VPATH so that it can find the .hs files (Make is good at using things there to build things here, not vise-versa, and that's what VPATH is good for), and change the rules slightly:
build/%.o: %.hs
cp $< $#
VPATH = M
main: build/A.o build/B.o
cat $^ > $#
Now for the different object directories.
build/test/%.o build/project/%.o: %.hs
cp $< $#
VPATH = M
test: build/test/A.o build/test/B.o
cat $^ > $#
project: build/project/A.o build/project/B.o
cat $^ > $#
Then we simplify those last two rules, so that it's easy to add more object files and binary targets:
OBJECTS = A.o B.o
test: $(addprefix build/test/,$(OBJECTS))
project: $(addprefix build/project/,$(OBJECTS))
test project:
cat $^ > $#
Now for the different compiler flags:
build/test/%.o: FLAGS += test_flags
build/project/%.o: FLAGS += proj_flags
build/test/%.o build/project/%.o: %.hs
#echo building $# from $^ using flags $(FLAGS)
cp $< $#
Finally the dependencies. This is a little tricky. Suppose you want the dependency B.o : A.hs to apply to however many object you have. This is one approach:
OBJECT_PATHS = build/test/ build/project/
# The following is from the included file generated by the compiler
$(addsuffix B.o,$(OBJECT_PATHS)) : A.hs
To generate lines like that, I'd pipe the raw lines (e.g. B.o: A.hs) through sed 's/\(.*\):\(.*\)/\1:\2/', and note that if you want to put this in a makefile command, don't forget to double the $ signs to preserve them for the shell.
I know that's a lot to absorb. Take it one step at a time and let us know how it works out.
If you haven't solved your problem by now or are experiencing further problems, best give the autotools (automake and autoconf) a chance. They'll quickly build you a Makefile that supports more configurable and flexible out-of-tree builds.

Resources