I have a C/C project that contains different directories, each containing a set of objects executables to build from C/C source code.
To enable automatic dependency tracking (generating .d dependency files whenever my #include
header files change), I have defined the following variables in a common Makefile:
# automatic prerequisite generation
# source: http://web.archive.org/web/20210820223028/http://make.mad-scientist.net/papers/advanced-auto-dependency-generation/
DEPFLAGS = -MT "$@" -MMD -MP -MF "$(@:.o=.d)"
CC_WRITE_DEP = $(CC) $(CFLAGS) -c "$<" -o "$@" $(DEPFLAGS)
CXX_WRITE_DEP = $(CXX) $(CXXFLAGS) -c "$<" -o "$@" $(DEPFLAGS)
So that when I write directory-specific Makefiles I can write:
# common compile options
common := common/Makefile
-include $(common)
# automatic dependency tracking
deps = $(objs:.o=.d)
-include $(deps)
# compile all .cpp source code files into .o object files
%.o: %.cpp
$(CXX_WRITE_DEP)
# compile all .c source code files into .o object files
%.o: %.c
$(CC_WRITE_DEP)
Where objs
refers to the object files needed to build each executable.
But I found that the block of lines I have presented above must be repeated for every Makefile that I use to build executables in each directory, which could be a hassle if there are many of them.
I then have tried to write this in the common Makefile:
define CC_OBJ_COMPILE =
%.o: %.c
$(CC_WRITE_DEP)
endef
define CXX_OBJ_COMPILE =
%.o: %.cpp
$(CXX_WRITE_DEP)
endef
and to include them in building executables:
common := common/Makefile
-include $(common)
$(CC_OBJ_COMPILE)
$(CXX_OBJ_COMPILE)
But this does not work. When I ran make -p --dry-run
in one directory for executables to see how those variables expanded, I saw these lines:
# ...
# makefile (from 'common/Makefile', line 16)
define CC_OBJ_COMPILE
%.o: %.c
$(CC_WRITE_DEP)
endef
# ...
# makefile (from 'common/Makefile', line 21)
define CXX_OBJ_COMPILE
%.o: %.cpp
$(CXX_WRITE_DEP)
endef
# ...
This means that the text variables are properly included into my executable-specific Makefiles.
However the implicit rules are expanded as:
# Implicit Rules
%.o: %.c
cc -Wall -Werror -c "" -o "" -MT "" -MMD -MP -MF ""
%.o: %.cpp
g -Wall -Werror -c "" -o "" -MT "" -MMD -MP -MF ""
Which means that they fail to include the automatic $<
and $@
variables for targets.
So is it possible to create reusable rules that can be define
d as variables and -include
d in other Makefiles using variable references? Did I miss something here?
CodePudding user response:
Make is an old grandpa - it is 45 years old. Consider moving to something newer - CMake, Scons, Meson, etc. Such tools will take care of dependencies automatically, will be portable, will come with a lot more features and will save you from endless (and pointless) hours of reinventing the wheel.
Is it possible to use Makefile "define" to define a target plus its recipes?
You have to eval the call.
define CXX_OBJ_COMPILE =
%.o: %.cpp
$(CXX_WRITE_DEP)
endef
$(eval $(CXX_OBJ_COMPILE))
Which means that they fail to include the automatic $< and $@ variables for targets.
Sure it does - $@
is like "expanded first", they need to be left for expansion.
CC_WRITE_DEP = $(CC) $(CFLAGS) -c "$$<" -o "$$@" $(DEPFLAGS)
CodePudding user response:
As I see it you have three options.
You could use the correct syntax for using "canned" code, as @KamilCuk directs.
Or you could put the rules in the common file rather than assigning them to variables, as @thebusybee suggested.
Or you could omit the rules entirely and let Make use the default rule with your extra flags:
CPPFLAGS = $(DEPFLAGS)