No rule to make target but the rule exists - makefile

I wanted to write some Makefile for testing C code via Unity test framework.
However when I want to test it I get the message make: *** No rule to make target 'unity_build/results/test_unity_dumb_example.unity_res', needed by 'unity_test'. Stop. . I can not figure out what is wrong because there is the rule for files that match the pattern: $(UNITY_PATHR)%.unity_res:.
Here is beginning of my Makefile:
UNITY_PATHU = ${UNITY_INSTALL_DIR}/
UNITY_PATHS = src/
UNITY_PATHS += src/unity_dumb_example/
UNITY_PATHT = unity_test/
UNITY_PATHB = unity_build/
UNITY_PATHD = unity_build/depends/
UNITY_PATHO = unity_build/objs/
UNITY_PATHR = unity_build/results/
UNITY_BUILD_PATHS = $(UNITY_PATHB) $(UNITY_PATHD) $(UNITY_PATHO) $(UNITY_PATHR)
# Tell compiler where to look for all test files
UNITY_SRCT = $(wildcard $(UNITY_PATHT)*.c)
UNITY_COMPILER=gcc -c
UNITY_LINKER=gcc
UNITY_DEPEND=gcc -MM -MG -MF
UNITY_CFLAGS=-I. -I$(UNITY_PATHU) -I$(UNITY_PATHS) -DTEST
UNITY_RESULTS = $(patsubst $(UNITY_PATHT)test_%.c,$(UNITY_PATHR)test_%.unity_res,$(UNITY_SRCT))
unity_test: $(UNITY_BUILD_PATHS) $(UNITY_RESULTS)
#echo "-----------------------\nIGNORES:\n-----------------------"
#echo `grep -s IGNORE $(UNITY_PATHR)*.unity_res`
#echo "-----------------------\nFAILURES:\n----------------------"
#echo `grep -s FAIL $(UNITY_PATHR)*.unity_res`
#echo "\nDONE"
$(UNITY_PATHR)%.unity_res: $(UNITY_PATHB)%.out
./$< > $# 2>&1
In GNU make manual I have read
The target is a pattern for matching file names; the ‘%’
matches any nonempty substring, while other characters match only themselves.
I do not understand why make complains because there is no misspell.
EDIT:
After full clean the output is as follows:
mkdir -p unity_build/
mkdir -p unity_build/depends/
mkdir -p unity_build/objs/
mkdir -p unity_build/results/
make: *** No rule to make target 'unity_build/results/test_unity_dumb_example.unity_res', needed by 'unity_test'. Stop.
All the needed paths exist.
When I run make in debug mode -d I can see that make is trying pattern rule with stem test_test_unity_dumb_example instead of test_unity_dumb_example, for example:
Trying pattern rule with stem 'test_test_unity_dumb_example'
test_test_ was my mistake but I have fixed it. I still can't make it work.
When I run with -p I can find something like this in the output:
# Implicit Rules
unity_build/results/%.unity_res: unity_build/%.out
# recipe to execute (from 'Makefile.unity', line 30):
./$< > $# 2>&1
SOLVED
The problem was with the prerequisite of the prerequisite of the $(UNITY_PATHB)%.out. Precisely, path to one crucial source file was ${UNITY_INSTALL_DIR}/src instead of ${UNITY_INSTALL_DIR}/ .
However I still find it weird that make was complaining about the rule to target that is 2 level above the target that could not be built.

Related

Makefile Target Pattern with %

I am new to makefile.
All I want is, when a specific C file will be changed, I want to run one command. And finally from one folder, any of the C file will be changed then I want to run the same command with that filename.
.e.g.
ceedling test:filename
I have simple file called unittest.mk. I am not sure the following approach is correct or not.
I am ruinning the following command to run this file.
make -f unittest.mk StartUnitTest
Here is the unittest.mk file:
TEST_OBJS += \
D:\ModApp\Apps\Paymark\ModApp_Paymark\build\test\out\test_txn_admin.o
D:\ModApp\Apps\Paymark\ModApp_Paymark\build\test\out\test_txn_admin.o: D:\ModApp\Apps\Paymark\ModApp_Paymark\test\test_txn_admin.c
echo $(*F)
echo $#
echo $<
StartUnitTest:
#echo Start Unit Test
$(TEST_OBJS)
#echo End Unit Test
When I run this file, it is giving the following error.
Start Unit Test
D:\ModApp\Apps\Paymark\ModApp_Paymark\build\test\out\test_txn_admin.o
D:\ModApp\Apps\Paymark\ModApp_Paymark\build\test\out\test_txn_admin.o
process_begin: CreateProcess(D:\ModApp\Apps\Paymark\ModApp_Paymark\build\test\out\test_txn_admin.o, D:\ModApp\Apps\Paymark\ModApp_Paymark\build\test\out\test_txn_admin.o, ...) failed.
make (e=193): Error 193
make: *** [StartUnitTest] Error 193
Finally once this will work, actually I want a target pattern with % as the following:
D:\ModApp\Apps\Paymark\ModApp_Paymark\build\test\out\%.o: D:\ModApp\Apps\Paymark\ModApp_Paymark\test\%.c
echo $(*F)
echo $#
echo $<
I have found the issue. I have changed the target "StartUnitTest" to the following and it is working now. Removed the echo messages.
StartUnitTest: $(TEST_OBJS)
Thank you MadScientist. the "make -d" does helped me to find the issue.

Why is make complaining "Nothing to be done for 'clean' "?

I want make to remove all files except the source files and the make rule file (i.e. the file named makefile), so I added a phony rule at the end of my makefile:
.PHONY:clean
clean:
$(shell ls | grep -v "[.][ch]" | grep -v makefile | xargs rm)
This does what I intend. But make always complains
make: Nothing to be done for 'clean'.
After I run make clean. Why does this message appear? And how can I make it disappear?
The use of $(shell ...) is unnecessary. It runs the command, then the output is used as if it was part of the Makefile. There is no output, so the resulting rule is:
clean:
i.e. the actual list of commands to update the clean target is empty.

Makefile cutting out variables in for loop

I'm writing my first complex Makefile for a highly-modularized project.
I have various sub-directories, each one has its own Makefile which supports at least the all and the clean targets.
These sub-Makefiles work just fine, however I have a problem with the main Makefile, that should call all the sub-Makefiles automatically from the list contained in the variable COMPONENTS.
I tried with the following Makefile:
OUTFILE = diskimage.bin
export NASM = nasm
COMPONENTS = bootloader
.PHONY = all clean FORCE $(OUTFILE) $(COMPONENTS)
all: $(OUTFILE)
$(OUTFILE): $(COMPONENTS)
./make_image
$(COMPONENTS): FORCE
for component in $(COMPONENTS); do \
make -C $component; \
done
FORCE:
clean:
for component in $(COMPONENTS); do \
make -C $component clean; \
done
This results in the following error message:
for component in bootloader; do \
make -C omponent; \
done
make: *** omponent: No such file or directory. Stop.
make: *** [bootloader] Error 2
as if the $component expression was only parsed as $c. I don't understand why that happens and how to fix it.
Just double the dollar sign:
$(COMPONENTS): FORCE
for component in $(COMPONENTS); do \
make -C $$component; \
done
The trouble is that with your makefile, Make expands $component before executing the rule. And since $c has no value (there is no such variable), it expands to nothing, leaving "omponent", which it passes to she shell, which complains that there's no such directory. (If you had written $(component), Make would have expanded it to nothing, since Make knows of no such variable, and then the shell would have complained that you were not specifying a directory at all.)
With the double dollar sign, Make expands $$component to $component, which it then passes to the shell, which interprets it as the loop variable, and everything proceeds as planned.
You really should have played around with a simple loop in a command, before attempting to do actual work with one.
Several issues.
.PHONY should be written as a dependency, not a macro definition
Don't write shell loops, use make syntax instead
When you call make recursively, you must do it via the ${MAKE} macro invocation
Leading to
OUTFILE = diskimage.bin
export NASM = nasm
COMPONENTS = bootloader
.PHONY: all
all: ${OUTFILE}
.PHONY: ${OUTFILE}
${OUTFILE}: ${COMPONENTS}
./make_image
.PHONY: ${COMPONENTS}
${COMPONENTS}:
${MAKE} -C $#
The advantage of this formulation is that it is parallel make friendly.
Always a test of a good Makefile.
Here make -j5 all will cause make to keep 5 commands running at once,
across all invocations of make.
Nice if you have 4 CPUs.
What about clean?
(Personally I hate clean targets—it's a sign of dodgy dependencies,
and of unhygienic mixing of source and target folders.)
Just add -clean (say) to each of the component names,
and repeat the pattern above.
CLEANS := $(addsuxffix -clean,${COMPONENTS})
.PHONY: clean
clean: ${CLEANS} ; #echo Clean succesful
.PHONY: ${CLEANS}
${CLEANS}: %-clean:
${MAKE} -C $* clean
These two sections can tidied up and combined into one if you feel so inclined.
Tip
Always run make with --warn (or --warn-undefined-variables to give it its full name) to catch inadvertent expansion of $c in things like $component.

gnu make - recipe to keep installed version of file aligned with a master version of file

So here's a Makefile to install foo.conf, based on a master copy called foo.conf.master. It installs it to the current directory rather than /etc, just for testing purposes:
all: foo.conf.copied
foo.conf.copied: foo.conf.master foo.conf
cp foo.conf.master foo.conf
touch $#
# Recipe to tell make that it is okay for foo.conf not to exist beforehand.
foo.conf:
So then create foo.conf.master:
$ touch foo.conf.master
$
and you're ready to test:
$ make
cp foo.conf.master foo.conf
touch foo.conf.copied
$
The point is that if I (with my "trusted" sysadmin hat on) modify foo.conf.master then make (possibly called by cron) will roll out the update:
$ touch foo.conf.master
$ make
cp foo.conf.master foo.conf
touch foo.conf.copied
$
But equally important: if I (with my "rogue" sysadmin hat on) modify the installed version then make will back out the update:
$ touch foo.conf
$ make
cp foo.conf.master foo.conf
touch foo.conf.copied
$
Woohoo.
Okay, so now the problem: obviously foo.conf isn't the only file I want do this for, so I need to change my static rules to pattern rules. Okay, that's easy: substitute foo.conf for % in targets and dependencies, substitute foo.conf for $* in the commands, and make a minor modification to the last recipe (which would otherwise become only '%:') so that it doesn't look like I'm trying to cancel a builtin pattern rule.
So clean up and create this Makefile:
all: foo.conf.copied
%.copied: %.master %
cp $*.master $*
touch $#
# Recipe to tell make that it is okay for foo.conf not to exist beforehand.
# Nop tells make that I'm not *cancelling* a pattern rule here
# (see http://stackoverflow.com/questions/34315150/make-implicit-rules-dont-work-without-command).
%: ;
But this doesn't work:
$ make
make: *** No rule to make target `foo.conf.copied', needed by `all'. Stop.
$
The error message is misleading; it is really foo.conf that it doesn't know how to make, which can be demonstrated by adding the following at the bottom of the Makefile:
foo.conf:
touch $#
But then that's a static rule again, which I don't want.
There are a couple more requirements I would also like to satisfy, which the above example doesn't demonstrate. These are:
foo.conf should be installable anywhere in the filesystem (e.g. /etc/foo/server/foo.conf)
foo.conf.master should be in a central directory, or subdirectly thereof, for all master versions, preferably without the '.master' extension (e.g. ~/poor-mans-puppet/master-files/etc/foo/foo.conf)
foo.conf.copied should be in a central directory, not in the same directory as foo.conf (e.g. ~/poor-mans-puppet/timestamp-files/etc/foo/foo.conf)
After much googling, hair pulling, I'm asking here! Any ideas please? (PS: if copying Makefiles from here, remember to change indentation back to tabs.)
Mad Scientist below suggested an elegant static rule, but I really need it to be a pattern rule. The reason is that I need to hook extra dependencies in using rules:
all: <new-dependency>
rather than hooking them in using variables:
STUFF_ALL_SHOULD_DEPEND_ON += <new-dependency>
The reason for this requirement is for consistency with how other (non-%.copied) targets are handled in my very large Makefile.
However, based on Mad Scientist's idea, I tried the following, which didn't work, but perhaps helps somebody to help me:
all: foo.conf.copied
%.copied: %.master %
$(eval FILES_FOR_WHICH_AN_EMPTY_RECIPE_ARE_NEEDED += $$*)
cp $*.master $*
touch $#
define GENERATE_STATIC_EMPTY_RULE
$(1):
endef
$(foreach X,$(FILES_FOR_WHICH_AN_EMPTY_RECIPE_ARE_NEEDED),$(eval $(call GENERATE_STATIC_EMPTY_RULE,$(X))))
Can you explain why you're using this extra ".copied" file? Why don't you just use:
%: %.master ; cp $< $#
?
Anyway, you're running afoul of make's special rules related to match-anything rules (pattern rules like % that can build everything). If you change your pattern so it's not match-anything, like %.conf: ; then it will work. However you probably don't want to assume that all files end in .conf.
Alternatively you can use static pattern rules, like this:
FILES_TO_COPY = foo.conf bar.conf biz.baz
all: $(FILES_TO_COPY:%=%.copied)
$(FILES_TO_COPY:%=%.copied): %.copied : %.master %
cp $*.master $*
touch $#
and you don't need the extra pattern rule.
In the end, I dynamically generated static rules. The following pseudo-code hopefully makes the actual Makefile easier to understand:
if flag not set # flag won't be set on first call
prepare static rules
set flag # prevent running this clause again
recurse! # make invokes make
else
include static rules
do the normal thing
endif
Here's the real Makefile:
ifeq ($(MAKELEVEL),0)
all:
for X in $(patsubst %.copied,%,$^); do \
echo "$$X.copied: $$X.master $$X"; \
echo " cp $$X.master $$X"; \
echo " touch \$$#"; \
echo "$$X: ;"; \
done > Makefile.include
$(MAKE)
# The real dependencies on all are defined below, but while creating
# Makefile.include, we don't want make to digress and start making
# the dependencies; this pattern rule will stop it from doing that.
%.copied: ;
else
include Makefile.include
endif
all: foo.conf.copied
The outer make can be silenced by use of .SILENT and the --no-print-directory option (not shown above for clarity).
Here's the output:
$ touch foo.conf.master
$ make
cp foo.conf.master foo.conf
touch foo.conf.copied
$ touch foo.conf
$ make
cp foo.conf.master foo.conf
touch foo.conf.copied
$

Directory wildcard in Makefile pattern rule

I'm trying to create a Makefile that will compile terminfo files residing in a directory via tic. tic also copies the termcap files it creates automatically to a system- or user-specific destination folder. For a regular user if the terminfo file is e.g. screen-256color-bce-s.terminfo, it will be compiled and copied to ~/.terminfo/s/screen-256color-bce-s. So it will look something like this:
terminfo/screen-256color-bce-s.terminfo => /home/user/.terminfo/s/screen-256color-bce-s
terminfo/screen-256color-s.terminfo => /home/user/.terminfo/s/screen-256color-s
If I put something like this into my Makefile:
TISRC = $(wildcard terminfo/*.terminfo)
TIDST = $(foreach x, $(TISRC), $(HOME)/.terminfo/$(shell basename $x|cut -c 1)/$(shell basename $x .terminfo))
$(HOME)/.terminfo/s/%: terminfo/%.terminfo
#echo "$< => $#"
#tic $<
install: $(TIDST)
it works. However, I'd like to make it general, and use a wildcard in the target, i.e.:
$(HOME)/.terminfo/**/%: terminfo/%.terminfo
#echo "$< => $#"
#tic $<
to be able to add terminfo files to my local repository. The above, however, does not work. How can I specify a wildcard directory in a pattern rule?
You can do that with GNU Make Secondary Expansion feature:
all : ${HOME}/.terminfo/x/a
all : ${HOME}/.terminfo/y/b
.SECONDEXPANSION:
${HOME}/.terminfo/%: terminfo/$$(notdir $$*).terminfo
#echo "$< ---> $#"
Output:
[~/tmp] $ make
terminfo/a.terminfo ---> /home/max/.terminfo/x/a
terminfo/b.terminfo ---> /home/max/.terminfo/y/b
As a side note, make provides some path manipulation functions, so that you don't really need to invoke the shell for that.
I don't think you can use wildcards the way you're trying to, but if you don't mind using eval trickery, you can get the effect you're shooting for without having to spell out all the directory paths explicitly:
TISRC = $(wildcard terminfo/*.terminfo)
BASENAMES = $(notdir $(basename ${TISRC}))
MKDST = ${HOME}/.terminfo/$(shell echo $1 | cut -c 1)/$1
TIDST := $(foreach s,${BASENAMES},$(call MKDST,$s))
DIRLTRS = $(notdir $(patsubst %/,%,$(sort $(dir ${TIDST}))))
install: ${TIDST}
# $1 - Directory Name
# $2 - File name
define T
${HOME}/.terminfo/$1/$2 : terminfo/$2.terminfo
#echo "$$< => $$#"
tic $$<
endef
# This is the tricky part: use template T to make the rules you need.
$(foreach d,${DIRLTRS},$(foreach f,${BASENAMES},$(eval $(call T,$d,$f))))

Resources