I have a list of header files which I want to compile at once using Makefile. Some of them, however, have an externally defined function in assembly. If that is the case, there is a file with the same name (but with the extension .s
) in the directory ./asm
. For example, if ./r1.hpp
requires assembly file to be linked into it, there will also be ./asm/r1.s
. Therefore, I have come up with the following Makefile:
CXXFLAGS := @compile_flags.txt
SRC_FILE:= ./test.cpp
HDR_DIRS:= $(wildcard ./v[0-9]*)
.PHONY: help clean $(HDR_DIRS)
.DEFAULT_GOAL := help
help:
@echo "Please specify a target to run."
%.o: %.s
as $< -o $@
# rule 1
%.out: %.hpp ./asm/%.o $(SRC_FILE)
g++ $(CXXFLAGS) $(SRC_FILE) $(word 2, $^) -o $@ -I. -include $<
# rule 2
%.out: %.hpp $(SRC_FILE)
g++ $(CXXFLAGS) $(SRC_FILE) -o $@ -I. -include $<
$(HDR_DIRS): %:
$(MAKE) $(patsubst %.hpp,%.out,$(wildcard $@/r[0-9]*.hpp))
clean:
@if [ -z "$(DIR)" ]; then \
echo "Error: DIR is not specified. Please set DIR to the target directory."; \
exit 1; \
fi; \
echo "Cleaning files in $(DIR) ..."
@rm -f $(DIR)/*.out $(DIR)/asm/*.o
Here, I want rule1
to be applied if possible, and rule2
if there is no corresponding assembly file (meaning that .s
file does not need to be linked it). However, rule2
is called always, no matter what I try.
The only way I managed to make it work was by changing .out
in rule1
to .obj
but I need the same extensions for further processing. I also want .s
files to be tracked as dependencies since they can be changed and files then need to be recompiled.
How can I achieve this?
Note: I have also tried to use conditional dependency:
%.out: %.hpp $(wildcard $(patsubst %.hpp,%.s,$(dir $<)asm/$(notdir $<)))
@echo $(word 2, $^)
but this wildcard always expands to empty string and I do not understand why.
The version is:
GNU Make 4.4.1.
This won't work:
%.out: %.hpp $(wildcard $(patsubst %.hpp,%.s,$(dir $<)asm/$(notdir $<)))
because make expands functions and variables in prerequisite lists immediately when the makefile is parsed, but automatic variables are not set until make has already decided it needs to rebuild the target and wants to invoke the recipe. This means automatic variables like $<
are not set when the patsubst
function is invoked, so it expands to the empty string.
If you want the recipe to depend on the .s
file then why don't you just add this to the pattern rule? If you write it like this then I think it will work:
%.out: %.hpp ./asm/%.s $(SRC_FILE)
g++ $(CXXFLAGS) $(SRC_FILE) $(word 2, $^) -o $@ -I. -include $<
The reason it doesn't work if you use %.o
is because make always chooses the pattern which requires the least amount of work, if there are multiple patterns that match "equally". In your example rule 2 requires less work since it doesn't need to build the object file. If you use the source file then that's equal amounts of work and the first matching recipe should be used.