How to enable a makefile to escape wildcard braces - shell

Consider following makefile snippet:
CXX := g++
test := sort{1..4} # i.e. sort1 sort2 sort3 sort4
#all: $(test)
$(test): %: %.cpp
$(CXX) -std=c++17 -Wall -O3 -o $# $<
clean:
rm -f $(test)
where sort1, sort2, sort3, and sort4 are expected to be the output (executable) files. However, when executing in shell:make $test or make clean, the wildcard { } seem NOT to expand, and the command can't remove those four files. How can I expand wildcard { } in a makefile?
Additionally, I've noticed a interesting phenomena: first let's use the non-expanded version (before I figure out above problem), with test := sort1 sort2 sort3 sort4 instead of line 3. When there is no target using the variable test (i.e. comment line 5), then if we try: make $test we will memerly compile sort1. However, when we try to use a target with prerequisites on this variable, for example, uncomment line 5, then make $test is equivalent to make all, thus generating four output files. How to explain this?
Thanks in advance for taking time for this trivial question, but it would mean a lot to me ;)

Although make does not expand sequence expressions internally, a shell that does expand them could be run from make; try the examples below.
Running make $test from a shell likely finds the shell variable $test not set and shell variable substitution yields nothing, so the command-line is reduced to only make, which goes about building the first target. When the #all: line is commented-out, make finds sort1 as the first target (provided test is set as expected); after removing the comment from #all, the first target will be all, so then make would handle the provided prerequisites.
CXX := g++
# straight assignment
test := sort1 sort2 sort3 sort4
$(info plain: "$(test)")
# foreach built-in
nset := 1 2 3 4
test := $(foreach n,$(nset),sort$(n))
$(info foreach: "$(test)")
# run a shell for sequence expression
test := $(shell echo sort{1..4})
$(info shell: "$(test)")
.PHONY: all
all: $(test)
$(test): %: %.cpp
$(CXX) -std=c++17 -Wall -O3 -o $# $<
clean:
rm -f $(test)

Related

Makefile 'missing separator' for ifneq

I know there are other issues with similar titles, but they don't seem to hold the solution.
Heres my makefile:
# Compiler Command
CC = mpiCC
CFLAGS = -c -I./header
# collecting object file names
src = $(wildcard source/*.cpp)
src1 = $(src:.cpp=.o)
objects := $(src1:source/%=bin/%)
# Compile object files into binary
all : $(objects)
$(CC) -o run $(objects)
ifneq($(n),) // <- error location , line 15
mpirun -np $(n) run
endif
# Generate object files by compiling .cpp and .h files
bin/%.o : source/%.cpp
$(CC) $(CFLAGS) $?
mv *.o bin
# Clean Recipe
.PHONY : clean
clean :
rm -rf all $(objects)
The goal of the ifneq is to have the binary run whenever it finishes compiling.
for example, a user runs the command:
make <- builds without running
make n=5 <- builds and runs on 5 processes
Whenever I use either of these, I get the error:
makefile:15: *** missing separator. Stop.
I've used cat -e -t -v to verify everything is tabbed instead of spaced. according to (https://www.gnu.org/software/make/manual/make.html#Conditional-Example) this conditional should function.
#MadScientist solved it. You need to put a space in between ifneq and its argument. For example:
ifneq($(n),0) is invalid.
ifneq ($(n),0) is valid.

makefile conditional recipe not executing

I have a makefile which produces an executable from several object files and I include a version number in each object file as it is compiled. However, I want the version number to be incremented only when a particular object file is created (ptarget below, the one containing main). I tried to do this using a conditional statement in the recipe:
ptarget:=$(addsuffix .obj,$(ouf))
%.obj : %.cpp
$(CXX) $(CXXFLAGS) $< -Fo$#
$(info $(ptarget))
$(info $#)
ifeq ($#, $(ptarget))
perl $(perlDir)versionBump/bump.pl -inc -f $(versionFile)
endif
I can see from the info commands that only when ptarget is built that $# == $(ptarget) -- I also tried using strip command to make sure no hidden whitespace, but the perl command to increment the version is never executed, it starts with a tab.
Just to add, this all works just fine without the conditional but the increment happens multiple times during a build, which is what I am trying to avoid. This example suggests it should work but...
This is a very common misunderstanding about make. ifeq is a make statement and is evaluated when make parses the Makefile. What you need is a conditional that gets evaluated when the recipe is executed by the shell, that is a shell if:
%.obj : %.cpp
$(CXX) $(CXXFLAGS) $< -Fo$#
$(info $(ptarget))
$(info $#)
if [ "$#" = "$(ptarget)" ]; then \
perl $(perlDir)versionBump/bump.pl -inc -f $(versionFile); \
fi
Do not forget the line continuations (the trailing \).

filter function in Makefile

Is there a way to pick up the target name using automatic variable.
SOURCES = $(wildcard *.c)
dummytgt: $(OBJ)/tier.o
$(GCC) $(CFLAGS) -c $(filter $#,$(SOURCES)).c -o $#
I do not want to mention the filename as input but would want to use the filter function to get the .c file which is same as target name. make throws an error no input files
It's helpful to have a look at how make parses this:
SOURCES = $(wildcard *.c)
dummytgt: $(OBJ)/tier.o
$(GCC) $(CFLAGS) -c $(filter $#,$(SOURCES)).c -o $#
First off,
it will read the makefile, defining and expanding macros as it goes.
SOURCES = $(wildcard *.c) means that ${SOURCES} is a lazy variable with definition $(wildcard *.c).
Lazy? Yes, these recursive variables (as the make manual has it) only expand their right-hand side when they are themselves expanded.
Make needs the dependencies as it reads the file, so $(OBJ) is expanded.
Let's assume that the expansion of ${OBJ} is objs (say).
The shell command block remains as a single lazy variable.
It's important to note that these are not expanded until make decides that it wants to build dmmytgt.
You could have written this to exactly the same effect:
dummytgt: objs/tier.o
$(GCC) $(CFLAGS) -c $(filter $#,$(wildcard *.c)).c -o $#
For this fragment to work,
the file objs/tier.o must already exist.
Let's assume it does.
Make now has all it needs to build dummytgt (according to your makefile),
so now it expands the command block.
$(wildcard *.c) expands to 1.c 2.c (say).
$# is dummytgt
$(filter dummytgt,1.c 2.c) is of course empty (and always will be!)
${GCC} is gcc (say)
${CFLAGS} is empty (say)
Thus the shell gets
gcc -c .c -o dummytgt
Presumably gcc complains that there is no file called .c.
The resulting error stops make's execution.
A few thing not to like here:
$(wildcard ) is only for hacky one-liner makefiles IMHO.
dummytgt requires objs/tier.o, but its build instructions never reference it.
Your $(filter ) always expands to nothing.
$(filter $#.c,$(SOURCES))
But I don't see why you don't use
$#.c
Or better still, make it a prerequisite.

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.)

Make: Setting Target Specific Variables With Different Goals

I'm building a makefile that will be used to build a release or debug target library. I want to place the object and auto-generated dependancy files into either a debug or release directory structure, depending on the requested makefile goal. I don't want to specify a testable make command-line argument (i.e. DBG=1), but would prefer to run make -f Makefile, or make -f Makefiel dbg for release and debug target goals, respectively. Got that part down. I understand that I can't assign a target-specific variable containing the name of the object dir (either release or debug) that can be used as part of the Target specification in a rule, like I did in the example shown below. In this example, OBJDIR is the target-specific variable I would like to set depending on the build goal. For that reason, in this example, $(OBJDIR) is empty in the target rule $(OBJDIR)/%.o. Any recommendations on how to perform the suggested steps nicely? (The example shown is simply a copy/paste unverified example...syntax is not verified...in fact, I can't get the tabs to appear correctly...I'm hoping to get some implementation ideas). (Also, $(OBJDIR) is not set in the clean target as shown...since it is not in the dbg/all target dependancy heirarchy...thoughts?) Thanks in advance.
Makefile:
OBJS := a.o b.o c.o
SRCS := $(OBJS:.o=.c)
-- Set up the release and the debug directory paths and object filenames
RELEASE_DIR := ./release
RELEASE_OBJ := $(OBJS:%=$(RELEASE_DIR)/%)
DEBUG_DIR := ./debug
DEBUG_OBJ := $(OBJS:%=$(DEBUG_DIR)/%)
.PHONY : all dbg
all: CFLAGS = -O3
all: OBJDIR := RELEASE_DIR
all: df := $(RELEASE_DIR)/$(*F)
all: init_release lib1.so
dbg: CFLAGS = -g -O0
dbg: OBJDIR := DEBUG_DIR
dbg: df := $(DEBUG_DIR)/$(*F)
dbg: init_debug lib1.so
Lib1.so: $(OBJ)
init_release:
-#mkdir -p $(RELEASE_DIR)
init_debug:
-#mkdir -p $(DEBUG_DIR)
lib1.so: $(OBJ)
#echo '--------------------------------------------------------------'
#echo linking $#
#gcc -shared -o lib1.so $(OBJ)
-Compile including advance dependancy generation alg, per Tom Tromey:
# http://make.paulandlesley.org/autodep.html
$(OBJDIR)/%.o: %.c
echo $#
echo $(OBJDIR)
echo compiling $#
$(COMPILE.c) -MD -o $# $<
cp $(df).d $(df).P; \
sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
-e '/^$$/ d' -e 's/$$/ :/' < $(df).d >> $(df).P; \
rm -f $(df)/$*.d
# If the goal is "clean", don't include these to avoid trying to build them
ifneq($(MAKECMDGOALS),clean)
-include $(SRCS:%.c=$(OBJDIR)/%.P)
endif
clean:
-#rm -f $(OBJDIR)/*.[Pdo] lib1.so
Target specific variables can be tricky. Use indirection instead. Make has lots of syntax to cut-down on boilerplate text. .SECONDEXPANSION is often good. A sketch:
.SECONDEXPANSION:
${DEBUG_OBJ} ${RELEASE_OBJ}: $$(patsubst %.o,%.c,$${#F})
gcc ${copts-${#D}} -c $< -o $#
Here we tell make that ./release/a.o depends on a.c. When make decides to build ./release/a.o it expands the shell line. As it does so, ${#D} is naturally release, so make carries on and expands ${copts-release} which you will have defined usefully.
Similarly, when producing ./debug/a.o make expands ${copts-debug}.
Copious use of $(warning [blah]), $(error [blah blah]) and the mandatory --warn-undefined-variables will help you get this right.
The Makefile you wrote is not valid, and it will not generate your targets as you expect. For instance, you cannot set the CFLAGS variable in the targets definitions all and dbg.
The only solution I can think of is to call make with the same Makefile defining the DBG variable as you wish. E.g.:
ifdef DBG
CFLAGS = -O0 -ggdb
OBJDIR = dbgdir
else
CFLAGS = -O2
OBJDIR = reldir
endif
all: $(OBJDIR)/target
#Your commands here
dbg:
$(MAKE) DBG=1
With this, if you call make, you have the release build. If you call make dbg you have the release build.

Resources