Home > Blockchain >  Is it possible to use Makefile "define" to define a target plus its recipes?
Is it possible to use Makefile "define" to define a target plus its recipes?

Time:11-14

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 defined as variables and -included 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)
  • Related