Find the specific source file from file list - makefile

I have following makefile
target: debug
debug: $(BIN_DEBUG)/$(TARGET_NAME).lib
#BIN_DEBUG is the path of debug folder
#TARGET_NAME is the library name.
#Rule for .libs
$(BIN_DEBUG)/$(TARGET_NAME).lib: $(DEBUG_OBJECT_LIST)
$(LIB) /OUT:$# $^
#DEBUG_OBJECT_LIST is the list of .obj files in BIN_DEBUG folder.
#Rule for obj files
$(DEBUG_OBJECT_LIST):$(BIN_DEBUG)/%.obj:%.cpp
#cl /c /Z7 /W3 /Od /D "_UNICODE" /D "UNICODE" /Gm /EHsc /Fo$# /Gd /analyze- /errorReport:queue $(CFLAGS) $^
#In the rule for each obj file, I want to pick the corresponding file for the obj file. I have file list in SOURCE_LIST.
#It shows how DEBUG_OBJECT_LIST is derived.
DEBUG_OBJECT_LIST1 = $(patsubst %.cpp, %.obj,$(SOURCE_LIST))
DEBUG_OBJECT_LIST2 = $(notdir $(DEBUG_OBJECT_LIST1))
DEBUG_OBJECT_LIST = $(patsubst %, $(BIN_DEBUG)/%, $(DEBUG_OBJECT_LIST2))
I need help in Rule for obj files. How to pickup the exact file for the given obj file?

Okay, general points first make is tricky beast and is very sensitive to whitespace.
Your patsubst command should not include a space after the comma. Now, in this case, it shouldn't make a difference, as all you'll do is add an extra space between items, but I can guarantee that if you don't stamp-out the habit, it will come back to bite you when you're doing a larger, more complex build...
The rule for your obj files is unnecessarily complex.
$(DEBUG_OBJECT_LIST):$(BIN_DEBUG)/%.obj:%.cpp
#cl ... $^
A more simple pattern rule will do the job:
$(BIN_DEBUG)/%.obj: %.cpp
#cl ... $<
This will means: "whenever I need a .obj file under $(BIN_DEBUG), look for the corresponding .cpp file relative to the current directory". Also note that I use $< and not $^ for the compile line. This will ensure that only the .cpp file is passed to the compiler, which is useful if you wanted to add a Makefile or a header to the implicit rule.
Finally, the DEBUG_OBJECT_LIST should probably be:
DEBUG_OBJECT_LIST = $(patsubst %.cpp,$(BIN_DEBUG)/%.obj,$(SOURCE_LIST))
Filtering-out directories is much easier to do by simply not adding them to the list in the first place. This is easy, if you don't use wildcards (either wildcard or *.* constructs). Wildcards seem like a good idea; a convenience, but they actually create more problems than they solve (including directories, non-source files, files you don't want to compile at the moment) - just bite the bullet and add source files to your sources list manually.
Edit: Correct error wrt. static pattern rule. Correct typo.

Problem is solved by setting VPATH variable. Please find more about VPATH.
SRC_DIR_LIST = $(dir $(SOURCE_LIST))
VPATH = $(sort $(SRC_DIR_LIST))

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.

Compile multiple executables from multiple source directories to single bin directory using makefile

I'm trying to create a makefile for a suite of programs that I am working on. The programs are all written in fortran and the source files are contained in different directories. I can't seem how to figure out how to get things to work. My current sumfile is
#Compiler and compiler flag variables
FCOMP=/usr/local/bin/gfortran
F_FLAGS=-O2 -fbounds-check -Wall
F_FLAGSDB=-g -fbounds-check -Wall
#paths to libraries
COMMON_LIB=/usr/local/lib/libspc_common.a
SPICE_LIB=/usr/local/lib/spicelib.a
# Paths to directories
BIN_DIR=BIN
# Get file names of component source files
#get names of files in src1
FORT_FILES=$(wildcard ./SRC1/*.f)
#get names of files in src2
FORTFILES+=$(wildcard ./SRC2/*.f)
#get names of files in src3
FORTFILES+=$(wildcard ./SRC3/*.f)
#get file names for output
EXE_FILES=$(addprefix $(BIN_DIR),$(notdir $(patsubst %.f, % , $(FORTFILES))))
# make commands
# Set the default option to compile the library with optimization
default: all
# create all command
all: $(EXE_FILES)
#echo toolkit has been built with optimization
#If compiling for debugging replace the compiler flags to remove optimization and add debugging
debug: F_FLAGS=$(F_FLAGSDB)
#Run compiler with debugging flags
debug: $(EXE_FILES)
#echo toolkit has been built with debugging
# Compile all of the source files into executables
$(EXE_FILES): % : %.f
$(FCOMP) $(F_FLAGS) $^ $(COMMON_LIB) $(SPICE_LIB) -o $(BIN_DIR)/$#
# install the library in /usr/local/lib
install:
cp -p $(BIN_DIR)* /usr/local/bin/toolkit/
# remove executable files for a clean build
clean:
rm $(BIN_DIR)*
The problem I am running into is that I get the following error when I try to run make:
make: *** No rule to make target `Display.f', needed by `Display'. Stop.
which I am assuming is because I have lost the directory that the source file comes from. Can someone help me here? I am totally stuck and don't know how to proceed.
In addition (this is more a general question about make), is there a way to tell make to recompile everything if the COMMON_LIB changes?
Thanks for your help!
Suppose your source files are
SRC1/alpha.f
SRC1/beta.f
SRC2/gamma.f
SRC3/delta.f
1) There is a flaw here:
EXE_FILES=$(addprefix $(BIN_DIR),$(notdir $(patsubst %.f, % , $(FORTFILES))))
This will produce
BINalpha BINbeta BINgamma BINdelta
when I think you intended
BIN/alpha BIN/beta BIN/gamma BIN/delta
A simple fix:
EXE_FILES=$(addprefix $(BIN_DIR)/,$(notdir $(patsubst %.f, % , $(FORTFILES))))
2) Now look at the static pattern rule:
$(EXE_FILES): % : %.f
...
So to build BIN/alpha, Make must first find BIN/alpha.f, which doesn't exist. To make it look for alpha.f, do this:
$(EXE_FILES): $(BIN_DIR)/% : %.f
...
3) How to find the sources?
You could do some delicate coding to help Make remember where it found alpha.f, but there's no need when we can use the vpath directive:
vpath %.f SRC1 SRC2 SRC3
4) One last look at that rule:
This command:
$(FCOMP) $(F_FLAGS) $^ $(COMMON_LIB) $(SPICE_LIB) -o $(BIN_DIR)/$#
Will produce e.g. BIN/BIN/alpha, which is silly. A non-PHONY Make rule should produce a file whose name is the target of the rule. It prevents a lot of trouble.
$(FCOMP) $(F_FLAGS) $^ $(COMMON_LIB) $(SPICE_LIB) -o $#
A few further refinements may be possible, once you have this working perfectly.

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.

Tricky "make" target requirements

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|')

Resources