With the Makefile I'm working on, I convert pdf files into txt files.
I've implemented a clean target that would remove all .txt files. However, I do not wish to delete the source files, only those that have been generated.
Example:
I have following files in my folder:
pdfsource.pdf and donotharm.txt
Running my makefile would create following file:
pdfsource.txt
For now, my clean looks like this:
rm -f *.txt
Using make clean would not only delete pdfsource.txt, which is desired, but also donotharm.txt.
I think I could use: .PRECIOUS: donotharm.txt, but this is really specific. I'd like to have a general solution to this.
Thanks in advance!
You can list the generated files in a make variable and use it to clean only these:
PDF := $(wildcard *.pdf)
TEXT := $(patsubst %.pdf,%.txt,$(PDF))
...
clean:
rm -f $(TEXT)
Or, if you prefer a more compact (but a bit less readable) form:
clean:
rm -f $(patsubst %.pdf,%.txt,$(wildcard *.pdf))
Of course, this works only if there is no {foo.pdf,foo.txt} pair for which you want to preserve foo.txt from deletion by make clean.
Note: using make variables, in such a case, is usually a good idea because they can be shared among various rules. Example:
PDF := $(wildcard *.pdf)
TEXT := $(patsubst %.pdf,%.txt,$(PDF))
.PHONY: all clean
all: $(TEXT)
$(TEXT): %.txt: %.pdf
pdftotext $< $#
clean:
rm -f $(TEXT)
Another approach: "make -nps" gives you all make's metadata about dependencies. For any intermediate file, it prints
filename: ...
So you can exactly delete such files with a generic "clean" rule:
clean:; MAKEFLAGS= ${MAKE} -j1 -spinf $(word 1,${MAKEFILE_LIST}) \
| sed -n '/^# I/,$${/^[^\#\[%.][^ %]*: /s/:.*//p;}; 1s|.*|${clean}|p' | xargs rm -rf
The first line handles use of makefiles other than the defaults (makefile, GNUmakefile, Makefile)
In the "sed" command:
/^# I/,$
... selects the zone of make metadata with dependencies.
/^[^\#\[%.][^ %]*: /
... filters out comments, implicit rules, and files with no dependencies (the trailing space). It doesn't filter out phony targets; oh well.
Finally:
1s|.*|${clean}|p
adds any explicit targets for "clean" -- what you know that make does not; e.g.
clean += tmpdir/* *.gcda
Related
I know it is not optimal at all to rely on make's implicit rules but
my goal is to understand why they are not working in this case.
I want to write the simplest makefile one can write for a C project
without having to specify the sources.
I have tried to run make -d but the ouput is too big and verbose to
really be helpful.
I have written makefiles for some time and I believe I am familiar with how it
works. I am pretty sure I have managed to get implicit rules to work for me both
compiling and linking in the past but apparently I am forgetting something.
Here's what I have tried :
SRCS = $(wildcard *.c)
OBJS = ${SRCS:.c=.o}
NAME=exe
${NAME}: ${OBJS}
clean:
rm -rf *.o
fclean: clean
rm -rf ${NAME}
re: fclean ${NAME}
.PHONY: clean fclean re
It almost works but it doesn't link.
I am using gnu make version 4.3
Your Makefile doesn't execute the link step because there is only a very simple implicit rule for linking. From the documentation:
Linking a single object file
n is made automatically from n.o by running the C compiler to link the program. The precise recipe used is $(CC) $(LDFLAGS) n.o $(LOADLIBES) $(LDLIBS).
This rule does the right thing for a simple program with only one source file. It will also do the right thing if there are multiple object files (presumably coming from various other source files), one of which has a name matching that of the executable file. Thus,
x: y.o z.o
In other words, for your Makefile to work, NAME needs to match the basename of one of your object files.
For example, if I have your Makefile and a single source file named hello.c, I can run:
make NAME=hello
And see the result:
cc -c -o hello.o hello.c
cc hello.o -o hello
I am trying to copy files befoe compilation (I have two source files with same name so I copy the files to a files with a different name) and delete them at the end of the MAKEFILE.
I am trying to do the folliwng but probably there is mismatch in the execution order.
How can I do it correctly?
all: copy_dup_files $(dst_dir) $(APP_TARGET_LIB) delete_dup_files
copy_dup_files:
#echo "COPYING DUP FILES"
$(shell cp /aaa/hmac.c /aaa/hmac1.c )
$(shell cp /bbb/hmac.c /bbb/hmac2.c )
delete_dup_files:
#echo "DELETING DUP FILES"
$(shell rm /aaa/hmac1.c )
$(shell rm /bbb/hmac2.c )
Thanks
The purpose of $(shell) is to produce an output which Make reads. The recipe lines should not have this construct at all.
# this is evaluated when the Makefile is read
value := $(shell echo "Use the shell to produce a value for a variable")
# this is evaluated when you say "make foo"
foo:
echo 'No $$(shell ...) stuff here'
So, all the $(shell ...) stuff in your attempt gets evaluated when the Makefile is read, but before any actual target is executed.
Your makefile is trying to say /aaa/hmac1.c depends on /aaa/hmac.c.
Thus we have:
/aaa/hmac1.c: /aaa/hmac.c
cp $< $#
/bbb/hmac2.c: /bbb/hmac.c
cp $< $#
/aaa/hmac1.o /bbb/hmac2.o: %.o: %.c
gcc $< -o $#
myprog: /aaa/hmac1.o /bbb/hmac2.o
gcc $^ -o $#
This is clean and parallel safe (a good test of any makefile).
There are innumerable style improvements you could make, like
Get rid of the absolute paths
Use symbolic links instead of copying
Automatic dependency generation (for .h files, etc.)
Don't besmirch the source tree — put all the intermediate files (the .os and the temporary .cs) in their own build folder
&c. &c.
My project has a directory called tests/ which contains an arbitrary number of C source files, each one is a self-contained program designed to test a library. For each of these sourcefiles, I want to create an executable of the same name in my build/ directory.
E.g. tests/test_init.c would compile to an executable file build/test_init.
Currently, my Makefile snippet looks like the following:
BUILD_DIR = build
TEST_DIR = tests
test_sources:= $(TEST_DIR)/*.c
test_executables:= $(patsubst %.c, %, $(test_sources))
.PHONY: tests
tests: $(test_executables)
$(CC) $^ -o $# -g
But this fails to produce to desired result. Any help would be much appreciated.
First you need the wildcard function to find the sources:
test_sources:= $(wildcard $(TEST_DIR)/*.c)
Then the correct names for the executables:
test_executables:= $(patsubst $(TEST_DIR)/%.c, $(BUILD_DIR)/%, $(test_sources))
Then a pattern rule to build a test executable from the corresponding source:
$(BUILD_DIR)/%: $(TEST_DIR)/%.c
$(CC) $< -o $# -g
(A static pattern rule might be a little tidier, but it's a more advanced method.)
Finally a phony target to build all of the tests:
.PHONY: tests
tests: $(test_executables)
If you wanted Make to run all of these tests, you could make a phony pattern rule run_test_%, but that can wait for another day.
This Makefile will detect all files in test/*.c and provides tasks build_tests, run_tests, and clean_tests.
all: build_tests
define test_template
build_tests: test/$(1)
run_tests: build_tests run_test_$(1)
test/$(1) : test/$(1).c
$$(CC) $$^ -o $$#
.PHONY : run_test_$(1)
run_test_$(1) : test/$(1)
test/$(1)
endef
clean: clean_tests
clean_tests:
rm -fv $(foreach test, $(tests),test/$(test))
# Auto detect the tests (any .c file in the test directory),
# and store the list of tests names.
tests := $(foreach test, $(wildcard test/*.c),$(patsubst %.c,%,$(notdir $(test))))
# Add information about each test to the Makefile.
$(foreach test, $(tests), $(eval $(call test_template,$(test))))
Note: I'm not sure how to get tabs working inside a code block in markdown, so you'll need to replace spaces with a single tab on each indented line if you copy and paste this.
Say I have a list of source files and each are to be compiled to separate binaries:
SRCS = abcd.c efgh.c ijkl.c
And I want output files in separate subdirectories based on the file names like this:
build/abcd/abcd
build/efgh/efgh
build/ijkl/ijkl
I'm thinking a static pattern rule is the way to go. The pseudo-make-rule can be something like:
$(TARGETS): build/%/%: %.c
# stuff ...
I started by making a list of the subdirectories based on the filenames:
DIRS = $(SRCS:%.c=build/%)
So now we have DIRS = build/abcd build/efgh build/ijkl. I thought I can make the list of targets now with something like:
BLDS = $(DIRS:%=%/$(basename %))
But of course this doesn't work since the wildcard can not be used multiple times within a pattern. Therefore I'm now stuck at BLDS = build/abcd/% build/efgh/% build/ijkl/%.
Obviously I'm totally going about this the wrong way. How would you go about this?
For now I'm writing each rule explicitly, which is starting to get a bit tedious:
compile = # command to do stuff
BD = build
all: $(BD)/abcd/abcd $(BD)/efgh/efgh $(BD)/ijkl/ijkl
$(BD)/abcd/abcd: abcd.c
$(call compile)
$(BD)/efgh/efgh: efgh.c
$(call compile)
$(BD)/ijkl/ijkl: ijkl.c
$(call compile)
clean:
rm -rf build/*
.PHONY: all
I believe this does what you want:
SRCS:=abcd.c efgh.c ijkl.c
# We could fold NAMES into BLDS's definition if NAMES is not used elsewhere.
NAMES:=$(SRCS:%.c=%)
BLDS:=$(foreach name,$(NAMES),$(subst foo,$(name),build/foo/foo))
# We don't use DIRS below but the question had this variable.
DIRS:=$(dir $(BLDS))
TARGETS:=$(BLDS)
.PHONY: all
all: $(TARGETS)
.SECONDEXPANSION:
$(TARGETS): $$(notdir $$#).c
#echo Build $# from $^
mkdir -p $(dir $#)
touch $#
There are two important changes. The first is to reorder how the variables are created, and use subst, which allows replacing a matched string multiple times. The second is to use secondary expansion so that make builds rules for each of your targets. You initially a pattern with two %, but the docs say:
A pattern rule looks like an ordinary rule, except that its target contains the character `%' (exactly one of them).
(Emphasis added.)
I've tested the above with fake files abcd.c efgh.c and ijkl.c and get the following output:
$ make
Build build/abcd/abcd from abcd.c
mkdir -p build/abcd/
touch build/abcd/abcd
Build build/efgh/efgh from efgh.c
mkdir -p build/efgh/
touch build/efgh/efgh
Build build/ijkl/ijkl from ijkl.c
mkdir -p build/ijkl/
touch build/ijkl/ijkl
I am trying to write a makefile for a small scale application I wrote in C under Linux. Currently all my source files .c are in the top level directory and all header files in
an include directory. Here is the makefile I used for this.
IDIR =include
CC=gcc
CFLAGS=-I$(IDIR)
ODIR=obj
_OBJ = main.o kernel.o user_app.o myargs.o ofp_msgs.o pkt_ip.o pkt_ether.o pkt_tcp.o pkt_udp.o pkt_icmp.o
OBJ = $(patsubst %,$(ODIR)/%,$(_OBJ))
#DEPS = ofp_msgs.h
$(ODIR)/%.o: %.c
$(CC) -c -o $# $< $(CFLAGS)
all: jam
jam: $(OBJ)
gcc -o $# $^ $(CFLAGS) -lpthread
.PHONY: clean
clean:
rm -f $(ODIR)/*.o *~ jam
It works fine but what I want is that for example I make a sub directory called "Packet" and all my packet parsing files i-e "pkt_ip.c, pkt_tcp.c etc" should be in that directory where as their header files should still be in the top level directory i-t "toplevel/include". I did a bit of search and the most common way was to use recursive make. Then I see a lots of pages complaining about recursive make. Can anyone please help me in this as how to do this right ?
Thanks
I recommend checking out the method described in Recursive Make Considered Harmful.
I have used it on several projects (small to medium-size), and find it simpler to use, and easier to wrap ones head around, than the recursive approach.
Basically, you have a Makefile in the root directory which includes a (partial) makefile from each of the subdirectories:
SRC := main.c
MODULES := Packet lib etc
-include $(patsubst %, %/module.mk, $(MODULES))
OBJ := $(patsubst %.c, %.o, $(filter %.c,$(SRC)))
# (...)
Packet/module.mk:
SRC += Packet/pkt_ip.c Packet/pkt_tcp.c
LIBS += -lsome_library
These module makefiles can of course also define their own module targets, or special build requirements.
Unlike recursive make, "make" will only be invoked once, which for most use cases will lead to a faster build.
However, for most smaller projects neither build time nor complexity will be a major concern, so use what feels most natural.
there are several ways to do this but you can certainly use VPATH:=Packet to tell make to look for source files inside the 'Packet' directory. see make manual