GNU make: build all sources in a different directory - makefile

Given:
programs := apps/prog1 apps/prog2 # the actual list is quite long
sources := src/prog1.cpp src/prog2.cpp # showing only 2 files
Make file has 2 targets release and debug. Each target should build every program in bin/ directory and appends target name to the file name.
For example, building release should create bin/prog1_release and bin/prog2_release.
How to write static pattern rule to do it?
Thanks.

This will do it (in GNUMake 3.81):
BINS := $(patsubst apps/%,bin/%,$(programs)) # bin/prog1 bin/prog2 ...
release_bins := $(addsuffix _release,$(BINS)) # bin/prog1_release ...
debug_bins := $(addsuffix _debug,$(BINS)) # bin/prog1_debug ...
$(release_bins): bin/%_release: src/%.cpp
#build the binaries according to the release rule
$(debug_bins): bin/%_debug: src/%.cpp
#build the binaries according to the debug rule
release: $(release_bins)
debug: $(debug_bins)
.PHONY: release debug
# If it turns out that one of the progs needs something else too:
bin/prog20_debug: somethingElse.cpp
(There are ways to make this slightly more concise, but at the cost of clarity.)

Related

makefile target-specific changing srcs files

I'm trying to write a Makefile with a rule to make the project with another main.cpp file, because I'm testing my code with different options
I have different versions of the main function, that I put inside differents files : main.cpp, main_1.cpp, main_2.cpp, ..., to test different versions of my code, and they all have the same dependencies
first I was just commenting and un-commenting the Makefile variable MAIN that define the main.cpp file, but I was hoping there is a way to choose the one I want to try with a specific rule ?
I tried something with target-specific variables but it didn't work :
# # # # # # #
# VARIABLES #
# # # # # # #
NAME = my_program
VPATH = srcs
CXX = c++
CXXFLAGS = -I ./headers
OBJS = $(SRCS:%.cpp=%.o)
MAIN = main.cpp
#MAIN = main_1.cpp
SRCS = $(MAIN) file.cpp
# # # # #
# RULES #
# # # # #
all: $(NAME)
# target-specific variables
test-1: MAIN = main_1.cpp
test-1: re
$(NAME) : $(OBJS)
$(CXX) $(OBJS) -o $(NAME)
clean:
rm -f $(OBJS)
fclean: clean
rm -f $(NAME)
re: fclean all
.PHONY : all clean fclean re
the error output for main test_1 is :
c++ -I ./headers -c -o main.o srcs/main.cpp
c++ -I ./headers -c -o file.o srcs/file.cpp
c++ main_1.o Webserv.o -o my_program
c++: error: main_1.o: No such file or directory
Makefile:21: recipe for target 'my_program' failed
make: *** [my_program] Error 1
I think, then, that target-specific is not the right tool for what I'm trying to do.
Does Make provide a way to accomplish that (modifying the list of srcs files when calling a specific rule, and having the compilation working great with the new srcs files) ?
I'm vaguely thinking something like this.
test-%: main_%.cpp file.cpp
Now, make test-1 will produce an executable with that name from main_1.cpp instead of main.cpp, and similarly test-2 from main_2.cpp, etc.
If you have subsequent targets which hardcode my_program which should actually depend on which version you made, this might not be suitable, or at a minimum, you'd have to refactor those to use the current output executable. Similarly, you might want to add test-[1-9] to the files to remove in the clean target (or perhaps add a realclean target to remove them too).
Tangentially, several of your make variables don't seem to serve any immediate purpose. Putting stuff in variables makes sense for things you want to be able to override at compile time, or vaguely for making a general-purpose Makefile which can be applied with only minor modifications across several projects; but in isolation, these seem like unnecessary complexities you should probably avoid for the time being.
Your immediate problem could perhaps be solved by refactoring the dependency chain, but on the whole, I'd recommend keeping it as simple as possible. make already knows how to compile common source formats; all you really need to put in the Makefile are the dependencies which are not trivially obvious and any .PHONY targets, and overrides to select e.g. a specific default action.

multiple Makefile for a single project

I am trying build a project with several modules included. the file tree looks like this:
the individual Makefiles for lib_one, lib_two and Main.cpp works fine, generating the 2 DLL and the EXE for the project, but requires enter at each directory and execute the command 'make' for each module.
I want now a Makefile in the top level directory ('/project') which triggers the building for all the other modules (DLLs and EXE) once. Based on some search results I got something like that:
lib_one := ./src/lib_one
lib_two := ./src/lib_two
libraries := $(lib_one) $(lib_two)
player := ./src
.PHONY: all $(player) $(libraries)
all: $(player)
$(player) $(libraries):
$(MAKE) --directory=$#
$(player): $(libraries)
when I execute the command 'make' I got this error:
Makefile:10: ***. Stop.
(the line 10 is this one: $(MAKE) --directory=$#). Besides a way to fix this error, I am looking to a way to accomplish this 3 things:
1)
the object files (*.o) from all the modules (DLLs and EXE) should be stores on a directory build in the same place from the directory src.
2)
the final files (*.dll and *.exe) should be placed in a directory reelease alongside the directories src and build.
3) if item 2 was possible, would be nice if each type of file being placed in a specific directory inside release (bin for *.exe, lib for *.dll and shared for other types). also, even with the exe and dlls in different directories, would be possible run the executable from this directory (making it search for the necessary libraries in ../lib alongside the other usual places?).
#Maxim is likely right for the cause of the error -- spaces vs tabs. As far as a your other questions go, without using non-recursive make, you could do something like:
Makefile:
export base_dir := ${CURDIR}
export obj_dir := ${base_dir}/build
export release_dir := ${base_dir}/release
lib_one := ${base_dir}/src/lib_one
lib_two := ${base_dir}/src/lib_two
libraries := $(lib_one) $(lib_two)
player := ${base_dir}/src
.PHONY: all $(player) $(libraries)
all: $(player)
$(player) $(libraries): | ${obj_dir} ${release_dir}
$(MAKE) --directory=$#
$(player): $(libraries)
${obj_dir} ${release_dir} :
#mkdir -f $#
The sub-makefiles would have access to any exported variable from the parent (see here), so in these you could do stuff like:
${obj_dir}/%.o : %.c
#echo compiling "$^ ==> $#"
$(CC) -c -o $# $^ $(CFLAGS)
which would compile the objects to the right directory.
Beware though -- if you have two components that produce an object file with the same name, because they're being produced in the same directory, you will end up with a potentially hard to debug race condition. Typically each component produces object files in its own unique directory to avoid this very thing.

Using Makefiles In Nested Directories

A project that I am working on deals with multiple architectures. The code is divided into architecture specific and general modules (which can be shared between the architectures).
I am planning to organize the code as below
TOP
|
|---- Makefile
|
|---- src (arch independent)
|---- inc (arch independent)
|
|----arch1 (arch specific)
|---- src
|---- inc
|---- Makefile
|----arch2 (arch specific)
|---- src
|---- src
|---- Makefile
The arch specific project would be compiled by the Makefile within the arch directory. This makefile includes the Makefile at the TOP level.
The idea was that in the arch specific makefile will have all the src code from within the arch directory and the top level Makefile defines a variable which is simply the path of the TOP level directory, so that the arch specific Makefile can include code from the src folder in the top level.
However, when including the top level Makefile, the defined path variable (through shell pwd command) is evaluuated at the arch specific directory level.
How can I solve this? Is their a better way of doing this?
Right now it's a 2 level structure. But in future I plan top make it multi level with innermost level being arch specific and the layers become more and more general as one moves out to the top level.
What is your top-level makefile doing? - I mean, can you run make on it standalone? If not then all you really appear to gain from it is the list of source files and header locations - it would be easier to just specify those in a common_make.mk or somthing which only contains:
SOURCES += ../src/test1.c
SOURCES += ../src/test2.c
SOURCES += ../src/test3.c
INC += ../inc
then both of your lower level file include this very basic makefile "snippet" and you don't have to worry about the paths.
Usually I am putting common elements into separate config controlled repos and I have my projects the other way around:
arch1/
|--src
|--inc
|--common (repo)
|--inc
|--src
arch2/
|--src
|--inc
|--common (repo)
|--inc
|--src
So here I have two separate repo's with a shared sub-repo. If you wanted to tie the two archx projects together you can add those to into a super-repo with it's own makefile that just does:
make -C arch1
make -C arch2
I see two options for you
Common makefile template for archX/ directories
# ./Makefile.template
ARCH_DIR := $(PWD)
TOP_DIR := $(ARCH_DIR)/..
BUILD_DIR := $(TOP_DIR)/build
# non-architecture specific source code
NOARCH_SRC_DIR := $(TOP_DIR)/src
NOARCH_INC_DIR := $(TOP_DIR)/inc
# non-architecture specific source code
ARCH_SRC_DIR := $(ARCH_DIR)/src
ARCH_INC_DIR := $(ARCH_DIR)/inc
# other stuff...
CFLAGS := -I$(ARCH_INC_DIR) -I$(NOARCH_INC_DIR)
# directory specific variables, e.g. XXX_SRCS
include $(NOARCH_SRC_DIR)/Makefile.include
include $(ARCH_SRC_DIR)/Makefile.include
# generate object file lists
NOARCH_OBJS := $(NOARCH_SRCS:%.c=$(BUILD_DIR)/%.o)
ARCH_OBJS := $(ARCH_SRCS:%.c=$(BUILD_DIR)/%.o)
# add paths to source files
NOARCH_SRCS := $(addprefix $(NOARCH_SRC_DIR)/, $(NOARCH_SRC_DIR))
ARCH_SRCS := $(addprefix $(ARCH_SRC_DIR)/, $(ARCH_SRC_DIR))
# target-specific pattern rules for C files
$(NOARCH_OBJS): %.o: $(NOARCH_SRC_DIR)/%.c | $(BUILD_DIR)
$(CC) $(CFLAGS) -o $# -c $<
$(ARCH_OBJS): %.o: $(ARCH_SRC_DIR)/%.c | $(BUILD_DIR)
$(CC) $(CFLAGS) -o $# -c $<
... and so ...
ArchX/ directory makefile
# archX/Makefile
# common stuff
include $(PWD)/../Makefile.template
# arch specific targets follow here...
Now you can already run the build using
$ make -C arch1
Top-level directory Makefile
# ./Makefile
ifndef ARCH
$(error you must specifiy ARCH=)
endif
# forward all targets to arch directory
%:
$(MAKE) -C $(ARCH) $(MAKECMDGOAL)
Now you can run the build using
$ make ARCH=arch1
Personally I would not write the top-level makefile at all and use only the former approach.
IMHO you should avoid things like $(shell). But maybe that is just my personal view, take it with a grain of salt...

Different set of files and flags for different builds

Using GNU make for a project in Linux. I'd like to have a test and a prod build, and trying to implement it with conditional directives. Test build has different source files and flags etc. from the prod build.
First I found that if block only works when it's following a target.
# More variable definition skipped.
SRCS := some source files
CXXFLAGS := some complile flags
test: ${myBinary}
ifeq (${BUILD}, UNIT)
#echo BUILD == ${BUILD}
SRCS += ${TEST_SRCS}
CXXFLAGS += some test flags
endif
I use this way so that later I can use a pattern to build .o files, instead of listing all source files for 2 different builds. Basically I try to find a way to use patterns to build .o files, yet still have different files, flags, etc. for test/prod builds.
${BUILD_DIR}/%.o : %.cpp
${CXX} -c ${CXXFLAGS} ${INCS} $< -o $#
But after I put it next to a target, still got this error:
make: SRCS: Command not found
make: *** [test] Error 127
How to fix it to meet my goal?
Other advice on organizing the makefile to meet the goal is most welcomed too.
Edit:
The 2 executables produced can have different names but can be in same location.
I wanna put object files are in ${BUILD_DIR} to separate them from source files.
"Command not found" error is gone after unindenting SRCS line.

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