I have a bunch of libraries that I want to build as dependencies:
LIBS = libs/foo libs/bar
Each library has an object that I need to compile against, all in a predictable spot. They are:
libs/foo/lib/libfoo-O3.a
libs/bar/lib/libbar-O3.a
I would like to effectively transform my LIBS string into these rules:
build : libs/foo/lib/libfoo-O3.a libs/bar/lib/libbar-O3.a
libs/foo/lib/libfoo-O3.a:
$(MAKE) -C libs/foo
libs/bar/lib/libbar-O3.a:
$(MAKE) -C libs/bar
I know you can't use % twice, so unfortunately libs/%/lib/lib%-O3.a is a non-starter as a target. Is there another way to do this? Something with a define template?
If you can change the LIBS variable to just contain the name, you can do it easily:
LIBS = foo bar
LIBPATHS := $(foreach L,$(LIBS),libs/$L/lib/lib$L-O3.a)
If you can't you can still do this, only slightly less readable:
LIBS = libs/foo libs/bar
LIBPATHS := $(foreach L,$(LIBS),$L/lib/lib$(notdir $L)-O3.a)
Then just add something like:
$(LIBPATHS):
$(MAKE) -C $(firstword $(subst /lib/, ,$#))
Related
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.
I'm trying to get a top-level makefile to call make in a number of subfolders. The top-level has several targets and the important bit is shown below:
MAKE_DIRS := $(dir $(wildcard apps/**/Makefile))
.PHONY: clean_apps apps $(MAKE_DIRS)
clean_apps: TARGET_INFO := clean
apps clean_aps: $(MAKE_DIRS)
$(MAKE_DIRS):
$(MAKE) -C $# $(TARGET_INFO)
Now this works fine when I call the targets independently:
make apps; make clean_apps
However if I call them on the same commandline with:
make clean_apps apps
Then the apps target justs say nothing to do. I guess it's something to do with the dependency on the directories not having changed between invocations, but I thought the .PHONY command would avoid that problem...
I'm happy to know if there's a better way to deal with this.
Thanks,
bob
It is something much more simpler :
SUBDIRS := $(dir $(shell find apps -name "Makefile"))
.PHONY: all clean
all clean:
$(foreach DIR, $(SUBDIRS), $(MAKE) $(MAKEFLAGS) -C $(DIR) $#;)
I want to compile some C++ files and I absolutely have to put all object files in a separate build directory, but stored completely flat, i.e., without any further subdirectories. I know the common solution using VPATH, which goes something like this:
SOURCES = foo/one.cpp \
foo/bar/two.cpp \
foo/bar/sub/three.cpp
OBJDIR = obj
VPATH=$(dir $(SOURCES))
OBJECTS = $(addprefix $(OBJDIR)/, $(notdir $(SOURCES:%.cpp=%.o)))
$(OBJDIR)/%.o : %.cpp
#echo Should compile: $(filter %/$*.cpp, $(SOURCES))
#echo Compiling $<
all: $(OBJECTS)
This example pretty much works: I get three object files one.o, two.o, three.o in the 'obj' subdirectory (you can assume it just exists).
Now here's the catch when using VPATH: If there happens to be a file 'foo/three.cpp', then this will be compiled instead of the 'foo/bar/sub/three.cpp' which is named in the SOURCES variable. And no, I cannot rename either file; this name clash simply exists and I cannot do anything about that.
So my question is: How can I tell Make to only use '.cpp' files which appear in the SOURCES variable? I think the best solution would be to use that 'filter' statement in the target's prerequisite. I think this should be possible using secondary expansion, but I don't know what to do with the '%'. For example, I tried
.SECONDEXPANSION:
$(OBJDIR)/%.o : $$(filter %/$$*.cpp, $(SOURCES))
but that doesn't work.
UPDATE: With the help of tripleee, I managed to get this working using the following:
define make-deps
$(OBJDIR)/$(notdir $(1:%.cpp=%.o)): $1
endef
$(foreach d, $(SOURCES), $(eval $(call make-deps,$d)))
%.o :
#echo Should compile $^ into $#
#echo Compiling $^
I suspect the easiest solution to your problem would be to get rid of VPATH and document each dependency explicitly. This can easily be obtained from your SOURCES definition; perhaps you want to define a function, but it really boils down to this:
obj/one.o: foo/one.cpp
obj/two.o: foo/bar/two.cpp
obj/three.o: foo/bar/sub/three.cpp
The actual rule can remain, only it should no longer contain the dependencies in-line, and you can skip the obj/ subdirectory, because it's declared explicitly in each dependency:
%.o : # Dependencies declared above
#echo Should compile $^ into $#
#echo Compiling $^
I changed the rule to use $^ instead of $< in case you ever have more than a single dependency. This may be right or wrong for your circumstances; revert the change if it's not what you need.
In order to not need to maintain the dependencies by hand, you might want to generate %.d for each %.cpp file. See the GNU Make manual. (I tried to do this by using a define, but it seems you cannot declare dependencies with a foreach loop.)
In response to the question in the comment, this should not affect parallel builds in any way; it merely disambiguates the dependencies where your original Makefile was ambiguous when there were multiple biuld candidates with the same name in the VPATH. There are no new dependencies and no new rules.
I am trying to do this:
From a directory, pick all the C (.c) files, generate .o and add it to
my final target executable. The C files can be added or removed at anytime, so when I
run make for my target, the available C files from the directory has to be picked
to compile and link with my target.
So far, I have the following:
define test_tgt =
DIR = full/path/to/dir
FILES = $(wildcard $(DIR)/*.c)
OBJS = <rule-to-convert-C-to-O>
endef
get_new_files:
$(eval $(test_tgt))
final-target: get_new_files
$(CC) <other-objs> $(OBJS)
Somehow this doesn't seem to work. I see a lot of similar examples, but not sure what
is wrong here. If this approach is not correct, can anyone suggest a better way to
accomplish this.
TIA.
You are trying to program a check that make does by itself.
Just list $(OBJS) as dependencies of final-target.
Something like this should work under GNU make:
DIR = full/path/to/dir
FILES = $(wildcard $(DIR)/*.c)
OBJS = $(subst .c,.o,$(FILES))
final-target: $(OBJS)
$(LD) -o $# $+ # or similar
Full documentation is here: https://www.gnu.org/software/make/manual/html_node/Text-Functions.html
I have a Makefile which includes makefiles from sub-directories.
However, what I want is to include these "sub"-makefiles on base of a selected target.
Background is, that the sub-makefiles define different object files and depending on these object files the target executable should be created.
Assuming sub-makefile1 sets the variable
OBJECTS := foo.o foo1.o
sub-makefile2 sets
OBJECTS := bar.o bar1.o
And the generic rule would be:
lib/%.so: $(OBJECTS)
link $^ -o $#
The targets are (for example):
foo: lib/foo.so
bar: lib/bar.so
whereas target foo should include the foo makefile, target bar the bar-makefile.
Any idea how to handle this situation?
Thanks,
Christian
Beta has mentioned the $(MAKECMDGOALS), but not described it:
ifeq ($(MAKECMDGOALS),foo)
include sub-makefile1
endif
ifeq ($(MAKECMDGOALS),bar)
include sub-makefile2
endif
# Rest of Makefile follows...
This isn't such a great idea, as it will only work when make is called interactively. You can hack around this by making rules for foo and bar that recursively invoke make:
ifeq ($(MAKECMDGOALS),foo)
include sub-makefile1
foo: # ...
# Normal commands to build foo
else
foo:
$(MAKE) $<
endif
ifeq ($(MAKECMDGOALS),bar)
include sub-makefile2
bar: # ...
# Normal commands to build bar
else
bar:
$(MAKE) $<
endif
The specific thing you're asking for -- conditional inclusion -- is difficult in Make. It can be done, but it's not elegant.
There are several ways to get the effect you want. You could use a conditional on MAKECMDGOALS. You could could have your makefile invoke a second makefile, and pass it the name of the subdirectory to use. But (without knowing more about the situation) I think this way is the tidiest:
include sub-makefile1
FOO_OBJECTS := $(OBJECTS)
include sub-makefile2
BAR_OBJECTS := $(OBJECTS)
lib/%.so:
link $^ -o $#
lib/foo.so: $(FOO_OBJECTS)
lib/bar.so: $(BAR_OBJECTS)
foo bar : % : lib/%.so
(You could be clever with variable names like foo_OBJECTS to save a line or two, but I advise against that.)