1

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.

asked Oct 2, 2024 at 17:53
1
  • (off topic) @Eljay Thank you for reaching out. I have reposted the question so you can move your comment there. In any event, this is a solution but it completely breaks the encapsulation. You could use public inheritance than, no need to write the explicit method. In my opinion, better way would be what I have written in the original post: here Commented Oct 16, 2024 at 17:25

2 Answers 2

2

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.

answered Oct 2, 2024 at 18:14
Sign up to request clarification or add additional context in comments.

2 Comments

If this doesn't help, you should use make -d and allow make to tell you why it decided to not use that pattern rule.
Also you didn't mention which version of GNU Make you're using; there are some subtle differences in the algorithm for matching pattern rules in some versions.
0

You can have make generate a makefile that explicitly overrides your general rule for the cases that this applies to.

SFILES := $(wildcard *.s)
DEPDIR := .dep
DEPFILES := $(SFILES:%.s=$(DEPDIR)/%.d)
include $(DEPFILES)
$(DEPDIR): ; @mkdir $@
.PRECIOUS: $(DEPDIR)/%.d
$(DEPDIR)/%.d: %.hpp | $(DEPDIR)
 echo "$*.out: $*.hpp ./asm/$*.o $$(SRC_FILE)" > $@
 echo "g++ $$(CXXFLAGS) $$(SRC_FILE) $$(word 2, $^) -o $$@ -I. -include $$<" >> $@

But, this is probably a bigger tool than you need to solve this.

answered Oct 2, 2024 at 18:13

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.