Make not reevaluating dependency variable - makefile

I have a Makefile designed to build a few different programmes. My SOURCES and OBJECTS depend on PRODUCT, which should be the product being built. At the moment, I set PRODUCT before listing the dependency on OBJECTS on each rule:
CORE_LIB := libcore
SCANNER := scanner
FLETCHER := fletcher
PRODUCT := $(SCANNER)
SOURCE_DIR := ./src
BUILD_DIR := ./build
OBJECTS_DIR := $(BUILD_DIR)/intermediate
PRODUCT_DIR := $(BUILD_DIR)/product
SOURCES = $(wildcard $(SOURCE_DIR)/$(PRODUCT)/*.c) $(wildcard $(SOURCE_DIR)/$(PRODUCT)/*/*.c)
OBJECTS = $(SOURCES:$(SOURCE_DIR)/%.c=$(OBJECTS_DIR)/%.o)
# change PRODUCT based on rule being ran
$(SCANNER): PRODUCT := $(SCANNER)
$(SCANNER): $(OBJECTS)
# link stuff
$(CORE_LIB): PRODUCT := $(CORE_LIB)
$(CORE_LIB): $(OBJECTS)
# link stuff
However, OBJECTS keep being evaluated before PRODUCT is set, and never changes based on the rule. Is there anything I'm missing?

You can't do that. The GNU make manual states, regarding target-specific variables:
As with automatic variables, these values are only available within the context of a target’s recipe (and in other target-specific assignments).
This means that you can't use target-specific variable values during the expansion of prerequisites, like $(OBJECTS). Or rather, you can use any variable there but the target-specific value is not available. Since you set the value of PRODUCT globally to be $(SCANNER), which is scanner, that's the value that will be used in all prerequisites.
You have multiple options. You can just define multiple variables, like this:
$(SCANNER)_SOURCES = $(wildcard $(SOURCE_DIR)/$(SCANNER)/*.c) $(wildcard $(SOURCE_DIR)/$(SCANNER)/*/*.c)
$(CORE_LIB)_SOURCES = $(wildcard $(SOURCE_DIR)/$(CORE_LIB)/*.c) $(wildcard $(SOURCE_DIR)/$(CORE_LIB)/*/*.c)
Then create rules like this:
$(SCANNER): $($(SCANNER)_SOURCES:$(SOURCE_DIR)/%.c=$(OBJECTS_DIR)/%.o)
# link stuff
$(CORE_LIB): $($(CORE_LIB)_SOURCES:$(SOURCE_DIR)/%.c=$(OBJECTS_DIR)/%.o)
# link stuff
If you have more of these, you could create a macro and use eval but I wouldn't bother with the complexity unless you really are going to have a bunch of them.
You can simplify the target definitions a bit using Secondary Expansion, like this:
OBJECTS = $$($$#_SOURCES:$(SOURCE_DIR)/%.c=$(OBJECTS_DIR)/%.o)
.SECONDEXPANSION:
$(SCANNER): $(OBJECTS)
# link stuff
$(CORE_LIB): $(OBJECTS)
# link stuff

Related

Makefile wildcard issue

I am trying to write a makefile to selectively include a certain file.
I have manage to get it working with the following:
OBJS := $(filter-out ./build/./src/class_1.cpp.o, $(OBJS))
OBJS := $(filter-out ./build/./src/class_2.cpp.o, $(OBJS))
OBJS_1 :=./build/./src/class_1.cpp.o $(OBJS)
OBJS_2 := ./build/./src/class_2.cpp.o $(OBJS)
But I would like to make it more generic and use a wildcard.
My understanding is that the following should work:
OBJS := $(filter-out $(wildcard ./build/./src/*), $(OBJS))
I also tried
OBJS := $(filter-out $(wildcard \./build/\./src/*), $(OBJS))
but not sure if the problem is with the special symbols or just a basic understanding.
however it then complains about having too many main functions (class 1 and class 2).
Is there something that I am missing with my understanding of wildcard and filters in make?
If I print out the value of $(OBJS) with the two different approaches, the values are identical, so I am not sure why one solution could work while the other one fails.
However, for some reason OBJS_1 doesn't seem to get the filter that was applied to $(OBJS)
$(wildcard ...) function finds which files actually exist in the filesystem (and some or all .o files might be created during make's execution, thus the result will be different depending on the last command). This is most certainly not what you want.
In your case you need only to perform the string substitution, and the 'match-all' pattern for strings in make is a percent-sign. So it should be:
OBJS:=$(filter-out ./build/./src/%,$(OBJS))
Also note that spaces in the makefile may have significance. For example, $(filter a, b) means that the second argument is " b", not just "b". Even though it wouldn't matter in your case, you should be more cautious with spaces.

Universal make-based build system design

I am aware of tools like CMake and GNU Autotools but I'm trying to write a universal build system myself, to use for my C and C++ projects. I'll briefly explain how it works and hopefully, someone can suggest either improvements or a better design altogether.
The build system proper lives in one of the project's subdirectories (I import it as a Git submodule). The project's root directory has a wrapper makefile that defines a couple of macros and includes the main makefile from said subdirectory. That does most of the work: it follows the directory organization scheme (i.e., it outputs libraries in lib, binaries in bin, etc.), it handles automatic dependencies for the source code and the DocBook documentation, and provides the de facto standard targets: all, test, clean, install, as well as others.
Here's what a wrapper makefile that builds two binaries, foo and bar, might look like:
# foo-specific macros
FOO_SRC_FILES = foo1.c foo2.c foo3.c
FOO_OBJ_FILES = $(FOO_SRC_FILES:.c=.o)
FOO_BIN_FILE = foo
# bar-specific macros
BAR_SRC_FILES = bar1.c bar2.c
BAR_OBJ_FILES = $(BAR_SRC_FILES:.c=.o)
BAR_BIN_FILE = bar
# Inform the build system about them
SRC_FILES = $(FOO_SRC_FILES) $(BAR_SRC_FILES)
OBJ_FILES = R(BAR_OBJ_FILES) $(BAR_OBJ_FILES)
BIN_FILES = $(FOO_BIN_FILE) $(BAR_BIN_FILE)
# Only install the binaries. If I were building a library, I would instead
# select the "lib" and perhaps "include" directories.
INSTALL = bin
INSTALL_DIR = /usr/share
# Use the build system
include build/build.mk
Now here's the problem. While build.mk can use pattern rules to create dependency and object files, there's only one OBJ_FILES and only one BIN_FILES. So if I put a pattern rule like the following in the build system that looks like this:
$(BIN_DIR)/$(BIN_FILES): $(OBJ_FILES:%=$(OBJ_DIR)/%) $(LIB_FILES:%=$(LIB_DIR)/%) | $(BIN_DIR)
$(CC) $(LDFLAGS) -o $# $(OBJ_FILES:%=$(OBJ_DIR)/%) -L $(LIB_DIR) $(LIB_FILES:lib%.a=-l %)
then foo would depend on and link with everything that bar does and vice versa. So what I end up doing is asking the user to put these rules in the wrapper makefile, even though they feel like they belong in build.mk:
$(BIN_DIR)/$(FOO_BIN_FILE): $(FOO_OBJ_FILES:%=$(OBJ_DIR)/%) $(FOO_LIB_FILES:%=$(LIB_DIR)/%) | $(BIN_DIR)
$(CC) $(LDFLAGS) -o $# $(FOO_OBJ_FILES:%=$(OBJ_DIR)/%) -L $(LIB_DIR) $(FOO_LIB_FILES:lib%.a=-l %)
$(BIN_DIR)/$(BAR_BIN_FILE): $(BAR_OBJ_FILES:%=$(OBJ_DIR)/%) $(BAR_LIB_FILES:%=$(LIB_DIR)/%) | $(BIN_DIR)
$(CC) $(LDFLAGS) -o $# $(BAR_OBJ_FILES:%=$(OBJ_DIR)/%) -L $(LIB_DIR) $(BAR_LIB_FILES:lib%.a=-l %)
The same issue applies to libraries as well, of course. The upside is that these rules can be copied and pasted almost verbatim; only the prefixes need to be changed (e.g., FOO or BAR).
Ideas to fix this include:
Asking the user to have separate wrapper makefiles for separate things (e.g., one for foo and another for bar) but that is just terrible.
Changing things up a little bit and then using m4 to do some preprocessing but I don't want to go through that unless a more elegant solution doesn't exist.
I would really appreciate some ideas.
PS: I know that the pattern matching expressions in the last two code samples can be replaced with text functions but those are GNU Make-specific. The style I used is more portable and is in fact on the list of additions for the next version of the POSIX standard.
I have begin to develop a similar system for my own C projects, but the logic I use does rely on some features which I believe are specific to GNU Make.
The main idea is to use a combinaison of $(eval) and $(call), by defining the logic of the build system, and then applying to the project tree.
To do so, I have in each of my directories and subdirectories a piece of Makefile of the following form, which I name Srcs.mk:
SRC := foo.c foo_bar.c bar.c
TARGET := foo_bar
SRC_DIR := src
OBJ_DIR := obj
I define a variable, which is in fact a macro, which is expanded with $(call) and then passed to $(eval). It's defined this way:
define get_local_variables
include Srcs.mk
$1SRC := $(SRC)
$1SRC_DIR := $(SRC_DIR)
$1OBJ_DIR := $(OBJ_DIR)
$1TARGET := $(TARGET)
TARGET :=
SRC :=
SRC_DIR :=
OBJ_DIR :=
$(call get_local_variables, $(DIR)) will expand to the above, with $1 replaced by the content of $(DIR). Then it will be treated as a Makefile fragment by $(eval)
This way, I fill per-directory variables for each of my directory.
I have then a handful or other rules which use this variables, using the same principles.
### Macros ###
obj = $(patsubst %.c,$($1OBJ_DIR)/%.o,$($1SRC))
define standard_rules
$($1TARGET): $(obj)
$$(LINK)
$(obj): $($1OBJ_DIR)/%.o:$($1SRC_DIR)/%.c | $($1OBJ_DIR)
$$(COMPILE)
endef
The variable are computed $(call), then expanded and read as makefile fragments by $(eval).
(I use static pattern rules but that it not intrinsic to the idea).
The whole idea is basically to define directories as a kind of namespace, with data attached to them, and then run function over them.
My actual system is a bit more complicated, but that the whole idea.
If you have a way to emulate $(eval) and $(call) (I think these are specific to GNU make, but not sure), you could try that approach.
You can also implement non recursive make this way, by adding a SUBDIRS variables in each directory and running recursively the same macro which is run on the current one. But it should been done carefully, not to mess it up with the order of expansion and evaluation in make.
So get_local_variables need to be evaluated before the rest of the macros are expanded.
(My project is visible on my Github account if you want to take a look, under make-build-system. But it is far from be complete enough^).
Be aware, though, that this is quite painful to debug when things go wrong. Make (at least, GNU) basically catch the error (when there is one) on the higher $(call) or $(eval) expansion.
I have developed my own non-recursive build system for GNU make, called prorab, where I solved the problem you described as follows.
The approach to solve your problem is somewhat similar to what #VannTen described in his answer, except that I use a macro to clean all state variables before defining build rules for the next binary.
For example, a makefile which builds two binaries could look like this:
include prorab.mk
this_name := AppName
this_ldlibs += -lsomelib1
this_cxxflags += -I../src -DDEBUG
this_srcs := main1.cpp MyClass1.cpp
$(eval $(prorab-build-app))
$(eval $(prorab-clear-this-vars))
this_name := AnotherppName
this_ldlibs += -lsomelib1
this_cxxflags += -I../src -DDEBUG
this_srcs := main2.cpp MyClass2.cpp
$(eval $(prorab-build-app))
So, in this example it will build two binaries: AppName and AnotherppName.
As you can see the build is configured by setting a number of this_-prefixed variables and the calling the $(eval $(prorab-build-app)) which expands to defining all the build, install, clean etc. rules.
Then a call to $(eval $(prorab-clear-this-vars)) clears all this_-prefixed variables, so that those can be defined again from scratch for the next binary, and so on.
Also, the very first line which includes the prorab.mk also cleans all this_-prefixed variables of course, so that the makefiles can be safely included into each other.
You can read more about that build system concepts here https://github.com/cppfw/prorab/blob/master/wiki/HomePage.adoc

How can I write a makefile rule with dependencies that depend on parts of the target?

I want to write a rule that looks something like this:
A_vs_B.txt : A.txt B.txt
but a general rule. The problem is I can't have two %s (as far as I know). I was thinking of just making the target %.txt and then using string functions to parse out the A and B in the dependencies, but that will be fairly complicated. I'm wondering if there's a better way to write a rule like this.
Two choices:
Secondary expansion (your idea of string munging in the dependencies). Something like this (might be better/shorter ways but this is what came to me first).
A_v_B.txt: $$(addsuffix $$(suffix $$(lastword $$(subst _, ,$$#))),$$(firstword $$(subst _, ,$$#)) $$(lastword $$(basename $$(subst _, ,$$#))))
#echo $^
Generated targets/prerequisites. Though how you generate the targets/prerequisites you need depends on where/how the pairs are generated/etc.
H1 := $(REP1)
H2 := $(REP2)
SEP := _vs_
JOIN := $(SEP)
define mktgt
H1 += $R.pr1
H2 += $R.pr2
JOIN += $(SEP)
endef
$(foreach R,$(REP1) $(REP2) $(REP1)$(REP2),$(eval $(mktgt)))
PAIRS := $(join $(join $(H1),$(JOIN)),$(H2))
$(foreach P,$(PAIRS),$(eval $P.txt: $(addsuffix .txt,$(subst _vs_, ,$P))))
# Debugging output
$(foreach P,$(PAIRS),$(info $P.txt: $(addsuffix .txt,$(subst _vs_, ,$P))))
That creates (as you'll see from the debugging output) the target/prerequisite mappings. It doesn't give the targets any recipe. I assume you have a recipe already and that you've assigned it to all the appropriate targets.
If not then adding something like:
$(PAIRS):
#echo 'Use $^ to generate $#'
should work.

Make target specific variables

I am writing small makefile, and I have a problem with target specific variables, I have this piece of code:
FILE_SOURCE := pliki.c wczytaj_plik.c wypisz_plik.c
CONSOLE_SOURCE := wczytaj_konsola.c wypisz_konsola.c
OTHER_SOURCE := suma.c roznica.c iloczyn.c macierz.c
HEADERS := suma.h roznica.h iloczyn.h wypisz.h wczytaj.h macierz.h
DEFINE_OPT =
NAME=macierze
FILE_OBJECTS := $(FILE_SOURCE:.c=.o)
CONSOLE_OBJECTS := $(CONSOLE_SOURCE:.c=.o)
OTHER_OBJECTS := $(OTHER_SOURCE:.c=.o)
finput: HEADERS+=pliki.h
finput: DEFINE_OPT+=-D WEWY_PLIKI
finput: OTHER_OBJECTS+=$(FILE_OBJECTS)
finput cinput: debug $(NAME)
$(NAME): $(OTHER_OBJECTS) main.o
#echo $^
gcc $(CFLAGS) -o $(NAME) $^
debug:
#echo $(OTHER_OBJECTS)
this is a piece that is relevant, when I invoke
make finput
in target debug I get all the .o files but
#echo $^
only produces
suma.o roznica.o iloczyn.o macierz.o main.o
so it is like FILE_OBJECTS were not added, but in gnu make manual:
There is one more special feature of target-specific variables: when you define a target-specific variable that variable value is also in effect for all prerequisites of this target, and all their prerequisites, etc. (unless those prerequisites override that variable with their own target-specific variable value).
So it is a bit weird that $(OTHER_OBJECTS) in $(NAME) don't include $(FILE_OBJECTS), how can I fix this problem?
If you look a couple lines up in the same section of the GNU make manual you quoted from, you will find the following.
As with automatic variables, these values are only available within the context of a target's recipe
This means the target specific value of OTHER_OBJECTS is not available in the prerequisites. (I haven't tried, but perhaps you can use the same workaround as with automatic variables, namely secondary expansion.)

How do I make an easy-to-update makefile?

My makefile looks something like this:
FOO_OBJECT_FILES := $(OBJDIR)/Foo.cpp.o
BAR_OBJECT_FILES := $(OBJDIR)/Bar.cpp.o $(OBJDIR)Bar.c.o
ALL_OBJECT_FILES := $(FOO_OBJECT_FILES) $(BAR_OBJECT_FILES)
$(BINDIR)/Foo.a: $(FOO_OBJECT_FILES)
# Rules for making a static library out of Foo's object files go here.
$(BINDIR)/Bar.a: $(BAR_OBJECT_FILES)
# This uses the exact same command sequence as the previous rule.
$(BINDIR)/All.a: $(ALL_OBJECT_FILES)
# Ditto.
# ...
When (not if) more targets are added to the project, the developer will have to update at least three things:
The list of the new target's object files
The list of all object files
Targets for making the new target, even if it uses the same rules as the others
Is there a way to simplify this process, or am I stuck with it?
I tried using wildcard rules, but it doesn't look like they work with macros.
$(BINDIR)/%.a: $(%_OBJECT_FILES)
# ...
You could treat the lists of object files as rules, but then the final target rules can't access them directly.
OBJECT_FILES_Foo: $(OBJDIR)/Foo.cpp.o
OBJECT_FILES_Bar: $(OBJDIR)/Bar.cpp.o $(OBJDIR)Bar.c.o
OBJECT_FILES_All: FOO_OBJECT_FILES BAR_OBJECT_FILES
$(BINDIR)/%.a: OBJECT_FILES_%
# This rule can't see into the object file lists to use them to build.
Is there no better way?
There are probably plenty of ways to do this. One such way is the following. All that needs to be done for a new target is add its name to the list of modules, and give the list of dependencies for it.
BINDIR := bin
OBJDIR := obj
MODULES := Foo Bar
Foo_OBJS := $(OBJDIR)/Foo.cpp.o
Bar_OBJS := $(OBJDIR)/Bar.cpp.o $(OBJDIR)/Bar.c.o
#####################################################
# #
# Nothing below here should need to be altered. #
# #
#####################################################
All_OBJS := $(foreach mod, $(MODULES),$($(mod)_OBJS))
define rule
$(BINDIR)/$(1).a: $($(1)_OBJS)
#echo
#echo 'Target: $$#'
#echo 'Deps : $$^'
endef
$(foreach lib, All $(MODULES), $(eval $(call rule,$(lib))))
###########################################
# #
# The next part is just here for testing. #
# #
###########################################
.PHONY: all
all: $(foreach lib, All $(MODULES),$(BINDIR)/$(lib).a)
%.o:
#echo Making $#
You can't do much about 1 and 2, those are arbitrary things that Make cannot possibly deduce. You can improve 3 slightly:
$(BINDIR)/%.a:
# commands for making a static library
# adding a new target:
QUARTZ_OBJECT_FILES := $(OBJDIR)/Quartz.cpp.o $(OBJDIR)Arbitrary.o
ALL_OBJECT_FILES += $(QUARTZ_OBJECT_FILES)
$(BINDIR)/Quartz.a: $(QUARTZ_OBJECT_FILES)
You could use a template to reduce those three lines to one:
$(eval $(call template, QUARTZ_OBJECT_FILES, $(OBJDIR)/Quartz.cpp.o $(OBJDIR)Arbitrary.o))
but is it really worth it?
While the other answers have provided good solutions for manual makefile writing, you could simply use automake to ease the build process.

Resources