Targets in the form of {a,b} are not recognized in my Makefile - bash

I have the following makefile:
SHELL:/bin/bash
all: euro.n.{a,b}
euro.{a,b}:
touch euro.{a,b}
euro.n.{a,b}: euro.{a,b}
cat euro.a > euro.n.a
cat euro.b > euro.n.b
Now if I run make twice, the makefile won't recognize in the second run that the files euro.n.a and euro.n.b have already been created (and it will be executed again).
What is the problem?

What is the problem?
{a,b} is not syntax recognized by GNU make.
SHELL := /bin/bash (you missed = there) only affects the syntax of recipes.
One alternative solution:
SHELL := /bin/bash
all: $(addprefix euro.n.,a b)
euro.%:
touch $#
euro.n.% : euro.%
cp $< $#

Related

Read variable from file in target recipe enclosed in foreach

I have a top level Makefile that can create multiple targets, for which I have a for loop in place. In this file I am trying to read a text file with a version number that is created only when a sub makefile runs. I have got it to work but it looks very ugly, so I'm thinking there is possibly a better way to do it. Here is the top level Makefile:
T1=_release
T2=_debug
T3=_test
all: bin/mybin_release.bin
debug: bin/mybin_debug.bin
debug: export DBG:=1
test: bin/mybin_test.bin
test: export TST:=1
define build_foo
bin/mybin$$($(1)).bin: bin/abc$$($(1)).bin bin/xyz$$($(1)).bin
cat $$^ > $$#
VER=$$$$(cat bin/rev.txt) && mv bin/mybin$$($(1)).bin bin/mybin$$($(1))_$$$${VER}.bin
bin/abc$$($(1)).bin:
$(MAKE) -C mod1 # <--- rev.txt is produced here
bin/xyz$$($(1)).bin:
$(MAKE) -C mod2
endef
$(foreach suffix, T1 T2 T3, $(eval $(call build_foo,$(suffix))))
clean:
rm bin/*.bin bin/*.txt
Notice the attempt to grab file contents in VER. Is there a better/right way to do this? I cannot use eval since it runs at the beginning and rev.txt only gets created once the sub make runs.
Also, is this a decent way to build multiple targets(using foreach)? I use the exported variables to modify the target built by the sub makefile.
As far as I understand, this bin/rev.txt file, and all the bin/abcXXX.bin are produced when running $(MAKE) -C mod1. So, it is the same for all. What about:
include version.mk
version.mk: bin/rev.txt
{ printf 'VER = '; cat $<; } > $#
bin/rev.txt:
$(MAKE) -C mod1
Demo:
$ ls
Makefile
$ cat Makefile
include version.mk
all:
touch $(VER).txt
version.mk: rev.txt
{ printf 'VER = '; cat $<; } > $#
rev.txt:
echo "1.2.3" > $#
$ make --quiet
Makefile:1: version.mk: No such file or directory
$ ls
1.2.3.txt Makefile rev.txt version.mk
Explanation:
version.mk is a second makefile that make will look for. As per GNU make documentation, section 3.5:
To this end, after reading in all makefiles, make will consider each
as a goal target and attempt to update it. If a makefile has a rule
which says how to update it (found either in that very makefile or in
another one) or if an implicit rule applies to it (see Using Implicit
Rules), it will be updated if necessary. After all makefiles have been
checked, if any have actually been changed, make starts with a clean
slate and reads all the makefiles over again. (It will also attempt to
update each of them over again, but normally this will not change them
again, since they are already up to date.)
Note: as you wrote your makefile, $(MAKE) -C mod1 will be run several times, which is a waste. You could instead exploit a specificity of GNU make pattern rules: when they have multiple targets, make considers that all targets are produced by one single invocation of the recipe. Example:
$ cat Makefile
all: a.bin b.bin c.bin
a.%in b.%in c.%in:
#echo 'building a.bin b.bin c.bin'
$ make all
building a.bin b.bin c.bin
See? The recipe is executed only once to build the 3 targets. The only problem is that the % wildcard must match at least one character. So, in your case you could do something like (the character that % matches is the b of bin/):
T1 := _release
T2 := _debug
T3 := _test
suffixes := T1 T2 T3
pattern := $(foreach suffix,$(suffixes),%in/abc$($(suffix))) %in/rev.txt
$(pattern):
$(MAKE) -C mod1
This will tell make that $(MAKE) -C mod1 builds all the bin/abcXXX.bin and bin/rev.txt at once. Same with $(MAKE) -C mod2.
All in all, you could probably get completely rid of your build_foo generic rule by gluing all these features together. Something like:
T1 := _release
T2 := _debug
T3 := _test
suffixes := T1 T2 T3
patternabc := $(foreach suffix,$(suffixes),%in/abc$($(suffix)).bin) %in/rev.txt
patternxyz := $(foreach suffix,$(suffixes),%in/xyz$($(suffix)).bin)
include version.mk
all: bin/mybin_release_$(VER).bin
debug: bin/mybin_debug_$(VER).bin
debug: export DBG:=1
test: bin/mybin_test_$(VER).bin
test: export TST:=1
version.mk: bin/rev.txt
{ printf 'VER = '; cat $<; } > $#
$(patternabc):
$(MAKE) -C mod1
$(patternxyz):
$(MAKE) -C mod2
bin/mybin%_$(VER).bin: bin/abc%.bin bin/xyz%.bin
cat $^ > $#

makefile pattern with multiple variants

I tried to make a makefile with patterns.
I want to have two variants:
when i write make it should just compile the list of files.
when i write make run it should run the respective files.
this is my current makefile:
files = test test1 test2
all: $(files)
$(files): % : %.scala
scalac $<
run: $(files)
$(files): % : %.scala
scala $<
now, regardless of whether i do make or make run it always executes the scala command and never scalac
You have a duplicate target:
$(files): % : %.scala
remove one of the targets (probably the second line was added by you)

makefile : applying a single rule to a bunch of target

I'm starting to learn how to write makefiles and I can't find an existing topic related to my question, so apologies if it already exists.
As of now, it looks like this (and it works) :
INSTALL_DIR = $(realpath /home/$(USER)/bin/)
SRC = $(realpath ./)
script = $(SRC)/foo.sh
TAR = $(INSTALL_DIR)/foo
all : $(TAR)
$(TAR) : $(script)
ln -s $^ $#
What I would like to do is to use a minimum number of lines to generate symbolic links (that is, applying the existing rule for building TAR) to every bash script (that is $(SRC)*.sh) in the same fashion as I did for the first one ('foo.sh') here.
I could just make 'script' and 'TAR' like variable for every script manually but I'm sure there must be a better way. Little help ?
First we must find the scripts:
INSTALL_DIR = bin # there is no need for a full path here
SRC = $(realpath ./)
SCRIPTS := $(wildcard $(SRC)/*.sh) # /some/path/foo.sh /some/path/bar.sh /some/path/baz.sh
SCRIPTS := $(notdir $(SCRIPTS)) # foo.sh bar.sh baz.sh
Note my use of ":=" rather than "=". You can read about the difference here, but usually ":=" is better.
Now for the targets, the links we're trying to construct:
TARGETS := $(basename $(SCRIPTS)) # foo bar baz
TARGETS := $(addprefix $(INSTALL_DIR)/, $(TARGETS)) # bin/foo bin/bar bin/baz
The rules:
all: $(TARGETS)
$(INSTALL_DIR)/%: $(SRC)/%.sh
ln -s $< $#
That last rule is quite general. We could restrict it to the target list we defined:
$(TARGETS): $(INSTALL_DIR)/%: $(SRC)/%.sh
ln -s $< $#
Further refinements are possible, once this is working perfectly.
You're looking for a pattern rule. Something like:
SCRIPTS := foo bar xxx yyy
all: $(SCRIPTS)
% : %.sh
ln -s $< $#
Note that $(SCRIPTS) can also be populated by $(wildcard ...) if none of the scripts are generated by make (otherwise, $(SCRIPTS) would be missing any file that did not exist when make was first invoked).

make: automatic execution of targets

In my project, I have a set of programs that are build from sources:
SRC_FILES = $(wildcard $(SRC_DIR)/*.cpp)
TARGETS = $(patsubst $(SRC_DIR)/%.cpp,$(BIN_DIR)/%,$(SRC_FILES))
My build target is simple, and works fine:
all: $(TARGETS)
#echo "- Done target $#"
Now, I want a run target so that all these programs are run from the shell on request. Say, if I have 3 files, I want make to run automatically:
>$ ./test1
>$ ./test2
>$ ./test3
Or
>$ ./test1 && ./test2 && ./test3
I tried this:
run: $(TARGETS)
$(addsuffix && ,$(TARGETS))
That generates the following command:
./test1&& ./test2&&
but it fails, due to the trailing &&
(Of course, I want these to be generated automatically as there can be 3... or 30.)
Edit: actually, the && separator is not required, so something like this:
>$ ./test1; ./test2; ./test3;
will be fine too.
Have some .PHONY line near start of Makefile with
.PHONY: all run
You might have
run: $(TARGETS)
$(addsuffix && ,$(TARGETS)) true
but it is a dirty trick.
Maybe you want to produce the output of test2 into test2.out then you might have
TESTSCRIPTS= $(wildcard test*[0-9])
run: $(patsubst %, %.out, $(TESTSCRIPTS))
test%.out: test%
# here some command to run the test%
As alternatives to Basile Starynkevitch's entirely correct answer here there are (at least) two other options as well.
You can avoid the need to run an unnecessary command (builtin though it might be) to end the list by manually pulling off the first entry (this may in fact be more costly then the shell builtin though).
run: $(TARGETS)
$< $(addprefix &&,$(wordlist 2,$(words $^),$^))
A better option I think, assuming that connecting the commands with && isn't a necessity would be to use $(foreach) to generate the command to be run.
run: $(TARGETS)
$(foreach t,$^,$t;)
The trailing ; in that is crucial as the output from $(foreach) is a single line and you need ; to terminate each shell command (or it is seen as one long command with arguments).

Makefile is skipping certain dependencies

So I am writing a makefile that will take some files (*.in) as input to my C++ program and compare their output (results.out) to given correct output (*.out).
Specifically I have files t01.in, t02.in, t03.in, t04.in, and t05.in.
I have verified that $TESTIN = t01.in t02.in t03.in t04.in t05.in.
The problem is that it seems to run the %.in: %.out block only for three of these files, 1,3, and 4. Why is it doing this?
OUTPUT = chart
COMPILER = g++
SOURCES = chart.cpp
HEADERS =
OBJS = $(SOURCES:.cpp=.o)
TESTIN = tests/*.in
all: $(OUTPUT)
$(OUTPUT): $(OBJS)
$(COMPILER) *.o -o $(OUTPUT)
%.o: %.cpp
clear
$(COMPILER) -c $< -o $#
test: $(TESTIN)
%.in: %.out
./$(OUTPUT) < $# > tests/results.out
printf "\n"
ifeq ($(diff $< tests/results.out), )
printf "\tTest of "$#" succeeded for stdout.\n"
else
printf "\tTest of "$#" FAILED for stdout!\n"
endif
Additionally, if there is a better way of accomplishing what I am trying to do, or any other improvements I could make to this makefile (as I am rather new at this), suggestions would be greatly appreciated.
EDIT: If I add a second dependency to the block (%.in: %.out %.err), it runs the block for all five files. Still no idea why it works this way but not the way before.
First, I don't see how TESTIN can be correct. This line:
TESTIN = tests/*.in
is not a valid wildcard statement in Make; it should give the variable TESTIN the value tests/*.in. But let's suppose it has the value t01.in t02.in t03.in t04.in t05.in or tests/t01.in tests/t02.in tests/t03.in tests/t04.in tests/t05.in, or wherever these files actually are.
Second, as #OliCharlesworth points out, this rule:
%.in: %.out
...
is a rule for building *.in files, which is not what you intend. As for why it runs some tests and not others, here is my theory:
The timestamp of t01.out is later than that of t01.in, so Make decides that it must "rebuild" t01.in; likewise t03.in and t04.in. But the timestamp of t02.out is earlier than that of t02.in, so Make does not attempt to "rebuild" t02.in; likewise t05.in. The timestamps of t02.err and t05.err are later than those of t02.in and t05.in, respectively, so when you add the %.err prerequisite, Make runs all tests. You can test this theory by checking the timestamps and experimenting with touch.
Anyway, let's rewrite it. We need a new target for a new rule:
TESTS := $(patsubst %.in,test_%,$(TESTIN)) # test_t01 test_t02 ...
.PHONY: $(TESTS) # because there will be no files called test_t01, test_t02,...
$(TESTS): test_%: %.in %.out
./$(OUTPUT) < $< > tests/results.out
Now for the conditional. Your attempted conditional is in Make syntax; Make will evaluate it before executing any rule, so tests/result.out will not yet exist, and variables like $< will not yet be defined. We must put the conditional inside the command, in shell syntax:
$(TESTS): test_%: %.in %.out
./$(OUTPUT) < $< > tests/results.out
if diff $*.out tests/results.out >/dev/null; then \
echo Test of $* succeeded for stdout.; \
else echo Test of $* FAILED for stdout!; \
fi
(Note that only the first line of the conditional must begin with a TAB.)

Resources