Tricky "make" target requirements - makefile

I have a situation where I need to compile some source files from a library into my own program. The directories the source files are in are not writeable by me. Instead I have a local "build" directory where all the work is done.
The problem I am having the the translation of the paths. The source files are named, say xxxx.cpp and yyyy.cpp, and they are in /path/to/source/xxxx/xxxx.cpp and /path/to/source/yyyy/yyyy.cpp.
Using $(patsubst ...) I can happily convert those paths to build/xxxx/xxxx.cpp etc, but I can't get it to strip the first xxxx off.
I could do with crafting a target that would match something like this:
build/%.o: /path/to/source/%/%.cpp
$(CXX) ...
...but I can't get that to work at all. I guess it doesn't like the double wildcard in the latter part of the target.
The "source" for the names is a single variable with just the "xxxx" and "yyyy" in:
SYS_LIBS = xxxx yyyy
Any suggestions on how to get something like this to work?
Oh, I need it to be a "generic" solution - this will be an included makefile in many projects that use this library of files, so hand-crafting a target per file is not an option. I cannot predict what files will be in the library.

One easy way is to use vpath to let make find the files itself. You just define
vpath %.cpp /path/to/source
vpath %.cpp /path/to/source2
vpath %.cpp /path/to/source3
build/%.o : %.cpp
$(CXX)
You can define more than one path to source that way, but be careful if you have the same file in more than one folder (e.g. a.cpp in both path_to_source1 and path_to_source2)

Personally I'd use vpath, as Bruce suggests. If there are many directories under /path/to/source/ you can use
SRCS = $(wildcard /path/to/source/*)
vpath %.cpp $(SRCS)
(This works as long as the directories go only one level down from there. If there are things like /path/to/source/foo/bar/zzzz/zzzz.cpp, then you'll have to fall back on something like find.)
If you really want to do path translation, this will do it:
X := $(patsubst %.cpp,build/%.o,$(notdir $(X)))
Or (I don't know why you'd want to do it this way, but you could):
X := $(shell echo $(X) | sed 's|.*/\(.*\)\.cpp|build/\1\.o|')

Related

How do I read source files from a directory and create object files into another folder in a makefile?

I have the following source files:
% ls
data_lexicon.c data_lexicon.h lex.l makefile
And the following makefile:
% cat makefile
CC = cc
CFLAGS = -Wall -std=c89
LDFLAGS = -ll
OBJFILES = lex.o data_lexicon.o
TARGET = lexical_analyzer_1
all: $(TARGET) lex.c
lex.c: lex.l data_lexicon.h
lex -olex.c lex.l
$(TARGET): $(OBJFILES)
$(CC) $(CFLAGS) -o $(TARGET) $(OBJFILES) $(LDFLAGS)
clean:
rm -f $(OBJFILES) lex.c $(TARGET)
If I do make all I get:
% ls
data_lexicon.c data_lexicon.o lex.l
lexical_analyzer_1 data_lexicon.h lex.c
lex.o makefile
So far so good.
However, I would like to move the source files (data_lexicon.c, data_lexicon.h, lex.l) to a folder src and generate the intermediate files (data_lexicon.o lex.c, lex.o) into a obj folder.
I create both folders but I do not understand how the makefile file shall be configured.
I am using FreeBSD make, so the more portable the solution given the better.
However, I would like to move the source files (data_lexicon.c,
data_lexicon.h, lex.l) to a folder src and generate the intermediate
files (data_lexicon.o lex.c, lex.o) into a obj folder.
It never ceases to amaze me how people insist on making extra work for themselves. You can certainly do what you describe, but it will require writing explicit rules for the object files.
First of all, however, you need to understand that make itself doesn't really know anything about directories. (Traditional make doesn't, anyway. GNU make and perhaps others know a little about them.) That is, it doesn't have any sense of varying directories against which it resolves file names. Rather, every target name is resolved against make's working directory. If you want to refer to something in a subdirectory, then you must say so. To begin with:
OBJFILES = obj/lex.o obj/data_lexicon.o
Similar goes for target and prerequisite names in rules:
obj/lex.c: src/lex.l src/data_lexicon.h
lex -o$# src/lex.l
That's also one reason to favor make's automatic variables, such as the $# in the above rule representing the name of the target being built.
Your makefile presently relies on make's built-in rule for building object files from corresponding C source files, but "corresponding" means target and prerequisite names are identical, including any path components, except for the suffixes (.c vs .o). You will no longer have that correspondence for data_lexicon.o, so you will need to write an explicit rule for it building it. This part is left as an exercise.

Makefile's 'vpath' doesn't work when searching prerequisites with wildcards

My project includes .c and .s (asm) files. I compile both types with 'gcc' and put output .o files to separate directory './bin'. To do that I'm using single makefile rule like this
bin/%.o: %.[cs]
$(CC) $(CFLAGS) -o $# -c $<
(As far as I understand, using square brackets wildcard in such context is a little bit unconventional, but it's working and it looks neat, so...)
The other day I decided to move some of my .c files to dedicated directory './common', so I added
vpath %.c common
at the beginning of the makefile. And now each time I try to 'make', it stops and throws an error on a file I had moved. For example, for 'common/foo.c' I get
"*** No rule to make target bin/foo.o, needed by..."
as if I haven't specified 'vpath'. But when I modify the rule to compile only .c files
bin/%.o: %.c
... ...
magically it starts to operate properly again and checks './common' for sources.
Looks like 'vpath' mechanism and wildcards can not work together, but I'm still new to 'make' and eager to learn what's the exact reason of such behavior. Any ideas anyone? Thanks in advance.
(Tested with make–3.81 and make–4.1.)
UPD: Having all the files and 'bin' directory reside on the same level like so
|-bin/
|-foo.c
|-bar.s
|-baz.c
|-Makefile
here's MWE
ROOTS = foo.o bar.o
OBJS = baz.o
SS = $(addprefix bin/,$(ROOTS) $(OBJS))
all: ff.out
ff.out: $(SS)
ld -o $# $^
bin/%.o: %.[cs]
gcc -o $# -c $<
Now if I move, say, 'foo.c' to separate directory and specify 'vpath', build stops with "No rule to make target bin/foo.o, needed by ff.out".
I suggest careful reading of How Not to Use VPATH as you seem to be at Step Three of that by having the OBJDIR in some places but not others.
To be explict, using a static pattern rule doesn't get you away from needing either at least one rule per source directory, or at least one make invocation per source directory. So, the simple answer is add a new rule for the new common/ directory that's the same as the other one:
bin/%.o: common/%.[cs]
gcc -o $# -c $<
There are lots of more comprehensive, but complex, answers, see the followon article for some of them.
For simple projects, there is no reason not to just track what directories you have in your main Makefile by adding extra rules. Also, there's a reasonable case for not having that bin/ dir and splitting .o and .out locations. Distributors and others expect to be able to control where files are created running from a seperate directory anyway.
I've thrown up a git repo with branches based on your cut down example that may clarify things.

How does make pass 'VPATH' to the sub-make

Thanks a lot for the comments. In this case actually VPATH is not used.
I write some makefiles to recursively build my project.
Part of the makefile in the root directory looks like this,
VPATH += $(shell find -type d)
export VPATH
#Codes to generate $(ARXIVS), i.e. the list of all local archives,
#is omitted here to save spaces
all : main.o $(ARXIVS)
$(ARXIVS) :
$(MAKE) --directory $(#D)
.PHONY : $(ARXIVS) all
As you can see, I pass the variable VPATH to sub-makes, in order to save some computational cost. And the whole system works.
But the searching paths in a sub-directory is different from the ones in the root directory.
I have print the values of VAPTH in sub-makes, the paths are indeed the ones with respect to the root directory. So VPATH in the sub-makes should be wrong.
But the whole system works. How did this happen?Any one give the suggestion to me. Thanks in advance

GNU Make: Using the filter function in prerequisites

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.

GNU make substitution command for automatic list of object files

A common type of makefile has a line like this:
OBJS=something.o other.o third.o progname.o
progname: $(OBJS)
Then you would run make progname. But GNU Make can also generate the list of o-files itself from all the c-files it sees in the current directory. How is this done?
Basically, I want to be able to add C files to the directory without having to change the makefile.
(Is it for instance through some shell-magic, or is there is a built-in function for this?)
It can also be done like this:
SRCS = $(wildcard *.c)
OBJS = $(SRCS:.c=.o)
progname: $(OBJS)
Which works just fine if the object file with main() in it is "progname.o".
To view all the defined rules (include the implicit ones), issue make -p.
However the fact that make knows how to generate object files from source files, does not mean that it should do this. Make will try to do the bare minimum in order to satisfy the target you ask it to build.
If you want make to compile all the sources into object in the current directory you will need a rule that will depend on all the objects, e.g.:
all: $(patsubst %.c,%.o,$(wildcard *.c))
You can expand a shell command to give you a list of files. You can also use implicit rules.
It can be done like this:
$(ODIR)/%.o: %.cpp
$(CC) -c $(CFLAGS) -o $# $<
make generates .o file names from .cpp file names it found in the current directory.

Resources