GNU Make prerequisite rule not executed and dependent libs not found - makefile

My problem is that a prerequisite (set by a variable) is not executed and even if executed manually before, required libraries are not found when linking. My Makefiles are non recursive, but the prerequisite is the external Software googletest and therefore called recursively, as the sources are not changed between several "make" runs.
My environment is:
$ make --version
GNU Make 4.2.1
Built for x86_64-unknown-linux-gnu
Copyright (C) 1988-2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
$ uname -a
Linux archlinux 4.17.3-1-ARCH #1 SMP PREEMPT Tue Jun 26 04:42:36 UTC 2018 x86_64 GNU/Linux
This is the relevant directory structure:
|-- ExampleTrunk
| |-- BUILD
| | |-- BIN
| | |-- LIB
| | `-- OBJ
| |-- buildenv
| | |-- ...
| | |-- GTEST.mk
| | |-- Makefile
| | |-- commons.mk
| | |-- module_commons.mk
| | |-- pathdefs.mk
| |-- src
| `-- HelloWorld
| `-- module.mk
| `-- test
| `-- HelloWorld
| `-- module.mk
`-- Tools
`-- GoogleTest
`-- googletest
|-- make
|-- Makefile
The folder ExampleTrunk/BUILD and its subdirectories are immediately created in pathdefs.mk and therefore exist before further rule execution.
The start Makefile is ExampleTrunk/buildenv/Makefile:
...
export SHELL := $(shell which bash)
...
.DEFAULT_GOAL := all
#some common function definitions
include commons.mk
#get $(PROJECT_ROOT), $(REL_PROJECT_ROOT); immediately create $(GLOBAL_OBJ_PATH), $(GLOBAL_LIB_PATH), $(GLOBAL_BIN_PATH)
include pathdefs.mk
export BUILD_DIRS := $(GLOBAL_BIN_PATH) $(GLOBAL_LIB_PATH) $(GLOBAL_OBJ_PATH)
include $(REL_PROJECT_ROOT)/src/HelloWorld/module.mk
...
#only include test stuff, when tests shall be built or cleaned to save dependency calculation time in all other cases
ifeq "$(call givenStringEndsWithTest,$(MAKECMDGOALS))" "0"
include GTEST.mk
include $(REL_PROJECT_ROOT)/test/HelloWorld/module.mk
endif
all : $(programs) | $(BUILD_DIRS);
all_Test: $(testPrograms) | $(BUILD_DIRS);
$(GLOBAL_OBJ_PATH)/%.o: %.cpp | $(BUILD_DIRS)
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $#
clean:
$(RM) $(addprefix $(GLOBAL_BIN_PATH)/,$(programs)) $(addprefix $(GLOBAL_BIN_PATH)/,$(testPrograms))
$(RM) $(objectsToClean)
$(RM) $(objectsToClean:.o=.d)
clean_Test: clean gtest_clean gmock_clean;
The rules "all", "clean" and "clean_Test" resolve and execute their prerequisites as expected and run without errors. (evaluated by make ... -p) The rule "all_Test" produces errors as described below.
GTest.mk contains the following recursive rules:
gtest: | $(BUILD_DIRS)
[ -d "$(REL_GTEST_MAKEFILE_DIR)" ] && cd "$(REL_GTEST_MAKEFILE_DIR)" && $(MAKE)
gtest_clean:
[ -d "$(REL_GTEST_MAKEFILE_DIR)" ] && cd "$(REL_GTEST_MAKEFILE_DIR)" && $(MAKE) clean
The directory "$(REL_GTEST_MAKEFILE_DIR)" exists, because it is only another path representation to the ExampleTrunk/BUILD/* directories that were immediately created before by pathdefs.mk. When I call any of both rules directly they can be executed without errors. Because I adapted the googletest Makefile, the outputs are created in the ExampleTrunk/BUILD/* directories as required.
The module.mk files are very short, as they include the module_commons.mk which contains most of the things to be executed:
ExampleTrunk/src/HelloWorld/module.mk:
additionalPrograms :=
gmockIsRequired := no
include $(PROJECT_ROOT)/buildenv/module_commons.mk
ExampleTrunk/test/HelloWorld/module.mk:
additionalPrograms := HelloWorld
gmockIsRequired := no
include $(PROJECT_ROOT)/buildenv/module_commons.mk
ExampleTrunk/buildenv/module_commons.mk generates most of the variables automatically from the module.mk file location and settings.
When I exectue
[.....ExampleTrunk/buildenv]$ make HelloWorld
everythink works fine, but with
[.....ExampleTrunk/buildenv]$ make HelloWorld_Test
it fails with a linker error (shown below). Here are the relevant *.mk parts for HelloWorld_Test with already resolved variables as shown from make HelloWorld_Test -p (variables with files/directories or flags are not written in resolved form to keep it short):
...
LDFLAGS_TEST := -L../BUILD/LIB -lgtest -lgtest_main
CPPFLAGS_TEST := $(googleStuffCPPFLAGS) # these are -I arguments for GCC, generated in GTEST.mk
moduleObj := "this variable is generated correctly by functions..."
...
HelloWorld_Test: ../BUILD/BIN/HelloWorld_Test gtest | $(BUILD_DIRS);
../BUILD/BIN/HelloWorld_Test: $(moduleObj)
g++ $(CPPFLAGS) $(CPPFLAGS_TEST) $(CXXFLAGS) $(LDFLAGS_TEST) $^ -o $#
When I exectue
[.....ExampleTrunk/buildenv]$ make HelloWorld_Test -p>log.txt
the log.txt shows that the rule variables are resolved correctly, but I get the error
/usr/bin/ld: cannot find -lgtest
/usr/bin/ld: cannot find -lgtest_main
collect2: error: ld returned 1 exit status
This is because the rule "gtest" is not run before, but even if I run it manually by
[.....ExampleTrunk/buildenv]$ make gtest
and all required files are created in the correct ../BUILD/* directory, the error still remains.
What could be the reason, why
1. The prerequisite gtest is not executed for make HelloWorld_Test and
2. even if the libraries are created manually and therefore exist, they are not found by the linker
Thanks for your help
Jasmin

It would help people answer your question if it were a short, self-contained, correct example. There's too much information here which is likely not relevant to the problem and it takes a lot of effort to read through it all. Instead, start with a simple version of what you want to do with a cut-down makefile. If it doesn't fail, then keep adding parts of your real environment until it does.
I suspect your problem is right here at the end of your question:
HelloWorld_Test: ../BUILD/BIN/HelloWorld_Test gtest | $(BUILD_DIRS);
That's not what you want because it means first ../BUILD/BIN/HelloWorld_Test is built, then gtest is built.
In fact what you want is for gtest to be built before ../BUILD/BIN/HelloWorld_Test otherwise the gtest libraries are not available. I think you need this instead:
HelloWorld_Test: ../BUILD/BIN/HelloWorld_Test
../BUILD/BIN/HelloWorld_Test: $(moduleObj) | gtest $(BUILD_DIRS)
g++ $(CPPFLAGS) $(CPPFLAGS_TEST) $(CXXFLAGS) $(LDFLAGS_TEST) $^ -o $#
This ensures that gtest is built before you try to link your program. Ideally you'd want to have this target also list the gtest libraries as prerequisites so that if they're modified, your program is re-built.

As stated in the comment before, the answer of MadScientist solved the first problem of building gtest before the test code itself, but linking still needed some changes to work. To get gtest to be built before the test sources I changed the build rules from:
HelloWorld_Test: ../BUILD/BIN/HelloWorld_Test gtest | $(BUILD_DIRS);
../BUILD/BIN/HelloWorld_Test: $(moduleObj)
g++ $(CPPFLAGS) $(CPPFLAGS_TEST) $(CXXFLAGS) $(LDFLAGS_TEST) $^ -o $#
to
HelloWorld_Test: ../BUILD/BIN/HelloWorld_Test;
../BUILD/BIN/HelloWorld_Test: $(moduleObj) | gtest $(BUILD_DIRS)
g++ $(CXXFLAGS) $(LDFLAGS_TEST) $^ -o $#
The linking process doesn't need any header files. The required gtest libs were now built to ../BUILD/LIB before the *.o files are linked, but the libs still could not be found during the linking process:
[.....ExampleTrunk/buildenv]$ make HelloWorld_Test -p>log.txt
still gave the error
/usr/bin/ld: cannot find -lgtest
/usr/bin/ld: cannot find -lgtest_main
collect2: error: ld returned 1 exit status
The problem could be revealed by getting verbose output of the linking process by changing the rule to:
../BUILD/BIN/HelloWorld_Test: $(moduleObj) | gtest $(BUILD_DIRS)
ld -o $# $(LDFLAGS_TEST) $^ --verbose
The verbose output was:
GNU ld (GNU Binutils) 2.30
...
==================================================
attempt to open ../BUILD/LIB/libgtest.so failed
attempt to open ../BUILD/LIB/libgtest.a failed
...
so the problem was, that the parameter "-l..." adds the prefix "lib" to the library name. As the library names were gtest.a and gtest_main.a they could not be found. The solution was to change the variable from:
LDFLAGS_TEST := -L../BUILD/LIB/ -lgtest -lgtest_main
to the full path representation
LDFLAGS_TEST := ../BUILD/LIB/gtest.a ../BUILD/LIB/gtest_main
Now everything works fine :D
If more libs with "special" names have to be linked, it would be a better solution to change the corresponding Makefile to add the prefix "lib" to the library's output names. This would keep the command line shorter, as the path ../BUILD/LIB only appears once with the "-L" switch. Nevertheless I think for my case the full paths are okay, because the parameter length is not so much more and the manipulations to the 3rd party Makefile of googletest are less.
Thanks for your help,
Jasmin

Related

Use Makefile and sdcc to build a perfect automation for MCS-51 development

Since sdcc have some limitations, like compiling one file at a time, I've tried hard to write a Makefile to perfect the automation of MCS-51 development, which have some requirements:
Source file (.c) expect main.c are stored in ProjectFolder/Sources/, while main.c are stored at the root of project folder.
Headers are stored in ProjectFolder/Includes/.
Outputs through compiling, linking and locating should be stored at ProjectFolder/Builds/
Makefile should be smart enough to find all source files, instead of type their file name by hand.
Makefile should be smart enough to if there are some files in Sources/, or there's only main.c in the project.
The file structure can be depicted like:
Project Folder
|
|- Sources
| |
| |(some source files, but OPTIONAL)
|
|- Includes
| |
| |(some headers, but OPTIONAL)
|
|- Builds
| |
| |(some .rel .o .hex files. OUTPUT here)
|
|- main.c
|
|- Makefile
Here's my solution but still have a problem. It cannot be used for project only have one file main.c which means no source file in Sources/.
INCLUDES = Includes/
SOURCES = Sources/
BUILDS = Builds/
CC = sdcc
CFLAGS = -o $(BUILDS)
LOADER = stcgal
LOADER_FLAGS = -P stc89
$(BUILDS)main.ihx: main.c $(BUILDS)main.rel
# Link
#$(CC) main.c $(shell find $(BUILDS) -name "*.rel" -not -name "main.rel" -maxdepth 1) $(CFLAGS)
#echo Link & Locate Succeeded
$(BUILDS)main.rel: $(SOURCES) $(BUILDS)
# Compile
#for f in $(shell ls $(SOURCES)*.c) ; do \
$(CC) -c $${f} $(CFLAGS) ; \
done
#echo Compile Succeeded
$(SOURCES):
#mkdir $(SOURCES)
$(BUILDS):
#mkdir $(BUILDS)
clean:
# Remove all files in build folder
#rm $(BUILDS)*
#echo Build Folder Cleaned
load: $(BUILDS)main.ihx
# Load data to MCU via USB port
#$(LOADER) $(LOADER_FLAGS) -p $(shell ls /dev/tty.usbserial*) $(BUILDS)main.ihx
Let's try something. First note that I've not looked at the load target.
Let's start with the same definition as you:
INCLUDES = Includes/
SOURCES = Sources/
BUILDS = Builds/
CC = sdcc
We need a variable with the source files from Sources. GNU Make has a wildcard functions which does the same thing as your find. See that I'm using := to have an immediate expansion of the value, so the wildcard will not be executed several times.
SRCFILES := $(wildcard $(SOURCES)*.c)
Now a variable with the .rel files. It is build from main.rel and the SRCFILES value:
RELFILES := $(BUILDS)main.rel $(SRCFILES:$(SOURCES)%.c=$(BUILDS)%.rel)
Let's define another variable with the flag to pass so the Includes directory is searched:
CPPFLAGS = -I$(INCLUDES)
Now we can define pattern rules to describe how to build .rel files from .c files. I'm using an order-only prerequisite for the build directory:
$(BUILDS)%.rel: $(SOURCES)%.c | $(BUILDS)
$(CC) $(CPPFLAGS) $(CFLAGS) -c -o $# -c $<
$(BUILDS)%.rel: %.c | $(BUILDS)
$(CC) $(CPPFLAGS) $(CFLAGS) -o $# -c $<
Let's define some usability targets:
.PHONY: all clean
all: $(BUILDS)main.ihx
clean:
rm $(BUILDS)*
And finally define how to build the targets which aren't handled by the pattern rules:
$(BUILDS)main.ihx: $(RELFILES) | $(BUILDS)
$(CC) $(LDFLAGS) -o $# $^ $(LOADLIBES) $(LDLIBS)
$(BUILDS):
mkdir $(BUILDS)
I've used a few variables (CC, CPPFLAGS, CFLAGS, LDFLAGS, LOADLIBES, LDLIBS) in the same way as they are used by the built-in rules of GNU Make.
I've kept your makefile behavior. There are good reasons to have Makefiles targeting to build in the current directory. Explaining them and modifying the Makefile for that is out of scope for this answer, you may look at MadScientist's GNU Make White Papers and the GNU Make Manual.

Calling subdir.mk from Makefile with multiple code directories

Below is the folder structure for my code.
This is a very small example to understand the concept of multiple makefiles based on which I have to create makefile for bigger code structure.
work
├── code
| |
| └── main.h and test.h files here
│ └── main.c and test.c files here
| └── subdir.mk
|
├── _Build/
│ └── Makefile here
I am keeping both Makefile and subdir.mk files to be very basic and simple to grasp the concept.
Below is the code for subdir.mk
#subdir.mk
#============================================
test.o : test.c test.h
#echo Building test.c ...
gcc -Werror -Wall -c test.c -o test.o
main.o : main.c main.h test.h
#echo Building main.c ...
gcc -Werror -Wall -c main.c -o main.o
#============================================
Below is the code for main file ... Makefile
#Makefile
#============================================
include ../code/subdir.mk
main : ../code/main.o ..code/test.o
#echo Building ...
make subdir.mk # <--- What is the correct way to perform this code
#echo Linking files ...
gcc -Llib ../code/main.o ../code/test.o -lm -o main
clean:
rm -rv ../code/*.o
#============================================
The error I am getting is
make: *** No rule to make target 'test.c', needed by 'test.o'. Stop.
In subdir.mk I am trying to generate object files.
In Makefile I am trying to link the object files generated in subdir.mk
The way I am trying to execute is correct way or some different steps are followed when we have multiple subdir.mk and main Makefile.
Share your valuable comments please.
You cannot both include the subdir.mk file and also invoke it recursively.
You need to decide whether you want to use non-recursive make (which means you'd use include) or recursive make (which means you'd run a sub-make command).
If you want to use non-recursive make then your subdir.mk makefile needs to be prepared to be run when the current working directory is different than the directory that the subdir.mk file appears in.
If you want to use recursive make then you need a separate rule to build the objects, and you should not include subdirs.mk. Something like this:
main : ../code/main.o ..code/test.o
#echo Linking files ...
gcc -Llib ../code/main.o ../code/test.o -lm -o main
../code/main.o ..code/test.o : subdir ;
subdir:
#echo Building ...
cd ../code && $(MAKE) -f subdir.mk
.PHONY: subdir
Be sure to include the semicolon after the subdir in the .o rule.
When invoking sub-makes you should always use the $(MAKE) variable, never use the literal string make.
You will probably be better off having your subdir.mk build a single library out of the objects rather than having to repeat all the object files in multiple places. Then replace the list of object files in this makefile with the library.
Contrary to Andreas's assertion, this will not always rebuild main. It will only be rebuilt when one of the object files was changed.
subdir.mk has to use paths relative to the main makefile. E.g.
../code/test.o : ../code/test.c ../code/test.h
...
What you need is a recipe for creating the object files. In Makefile you'll need to remove include ../code/subdir.mk and add something like this:
.PHONY: ../code/main.o ..code/test.o
../code/main.o ..code/test.o:
$(MAKE) -C ../code/ -f subdir.mk $(#F)
When you build target main, make sees you need the object files. Then make find the above recipe for creating the object files and so runs it.
Having them .PHONY is because the top Makefile can´t tell whether or not they´re up to date. Without it the object files would never be rebuilt. This has the unfortunate consequence that main will always be rebuilt, even if subdir.mk determines the object files were indeed up to date. The solution is either to have it all in a single make (can still split into files and include), or change to a build tool that can get it right.

Makefile dependency error after using flags $(#:.o=.d)

I have created a Makefile for the below code structure
work
├── code
| |
| ├──inc/
| | └── main.h and test.h files here
| |
| ├──src/
│ └── main.c and test.c files here
├── _Build/
│ └── Makefile here
Here is the Makefile
# All path are referenced with the directory path of Makefile
# Directory Path for workspace
WORKSPACE = ..
# Directory path for code
PATH_DIR_CODE = $(WORKSPACE)/code
# Directory path for c source files
PATH_DIR_C_SOURCES = $(PATH_DIR_CODE)/src
# Directory path for c header files
DIR_PATH_C_HEADERS = $(PATH_DIR_CODE)/inc
# Directory path for obj files
DIR_PATH_OBJ = $(WORKSPACE)/obj
# Directory path for executables
DIR_PATH_BIN = $(WORKSPACE)/bin
# Executable name declaration
FILE_PATH_EXE = $(DIR_PATH_BIN)/main
# Command mkdir
MKDIR = mkdir
FILE_PATH_C_HEADER = $(shell find $(PATH_DIR_CODE) -name *.h)
DIR_PATH_C_HEADER = $(patsubst %/,%,$(sort $(dir $(FILE_PATH_C_HEADER))))
FILE_PATH_C_SRC = $(shell find $(PATH_DIR_CODE) -name *.c)
DIR_PATH_C_SRC = $(patsubst %/,%,$(sort $(dir $(FILE_PATH_C_SRC))))
INC_FILE_C_HEADER = $(addprefix -I, $(DIR_PATH_C_HEADER))
FILE_PATH_OBJ = $(patsubst $(DIR_PATH_C_SRC)/%.c, $(DIR_PATH_OBJ)/%.o, $(FILE_PATH_C_SRC))
CC = gcc
CFLAGS = -Werror -Wall
CDEPS = -MMD -MP -MF $(#:.o=.d)
LDFLAGS = -Llib
LDLIBS = -lm
MKDIR = mkdir
-include $(FILE_PATH_OBJ:.o=.d)
all : $(FILE_PATH_EXE)
.PHONY : all
$(FILE_PATH_EXE) : $(FILE_PATH_OBJ) | $(DIR_PATH_BIN)
$(CC) $(LDFLAGS) $^ $(LDLIBS) -o $#
$(DIR_PATH_OBJ)/%.o : $(DIR_PATH_C_SRC)/%.c | $(DIR_PATH_OBJ)
$(CC) $(CFLAGS) -c $< $(CDEPS) -o $#
$(DIR_PATH_BIN) $(DIR_PATH_OBJ):
$(MKDIR) -p $#
clean :
$(RM) -rv $(DIR_PATH_BIN) $(DIR_PATH_OBJ)
Based on tutorial for dependencies I have used the flags
CDEPS = -MMD -MP -MF $(#:.o=.d)
and
-include $(FILE_PATH_OBJ:.o=.d)
still I am getting the following error
mkdir -p ../obj
gcc -Werror -Wall -c ../code/src/main.c -MMD -MP -MF ../obj/main.d -o ../obj/main.o
../code/src/main.c:4:10: fatal error: test.h: No such file or directory
#include "test.h"
^~~~~~~~
compilation terminated.
make: *** [Makefile:56: ../obj/main.o] Error 1
To remove this error what should be included in the Makefile?
Dependencies should be removed by this logic or some other logic should be used?
You are conflating two different things.
The .d files tell make where to look for prerequisites of the target. In this case the target is an object file (.o) and the prerequisite is a header file, but to make they're just "targets" and "prerequisites". Make is not restricted to just compiling C programs: it can do essentially any task where changing some files means that some other files need to be updated: compiling programs (not just C programs) is one common use but it can build documentation, web sites, run tests, etc. Make does its job by running commands, just as you would do it yourself from the command line (except make never forgets to add an option and doesn't make typos). It doesn't really know anything about "compilers" and "linkers", internally.
The error you are getting is from the compiler (or to be pedantic, the preprocessor), not make, and the compiler has to be told where to look for the header files it needs to include. Those are two completely different things and require different operations: the compiler requires that you provide the directories to search using the -I command line option.
I suppose it might be nice if the compiler could parse make's .d files and figure out where to look for headers, but it can't. You have to specify the flags yourself.
In your situation it's even more clear: you are actually using the compiler to generate the .d files! So there's a chicken-and-egg problem: if the compiler could get the paths from the .d files, but the .d files are being created from the compiler, then where do the paths come from in the first place?

Makefile with Objects in Separate Directory Tree

I have been trying to devise a makefile example which can build object files to a different directory structure than the associated source. The directory tree, both with source and targets, is as follows:
/repo-root
|-- /build
|-- /example_lib
|-- makefile
|-- /Release*
|-- libexample_lib.a*
|-- /obj*
|-- example_lib.o*
|-- example_lib.d*
|-- /source
|-- /example_lib
|-- /inc
|-- example_lib.h
|-- /src
|-- example_lib.cpp
The asterisk folders/files are those the makefile should be generating.
I have seen other questions and answers (Makefile : Build in a separate directory tree), (Makefile with directory for object files), etc., but if I understand their source-to-object rules correctly these appear to create a subdirectory tree within the output directory matching that of the source.
The current makefile I am using is as follows, run using GNU Make 3.82:
SHELL = /bin/sh
.SUFFIXES:
.SUFFIXES: .cpp .o
# #todo Variables passed to make.
BUILD_CONFIG=Release
# Makefile specific variables
REPO_ROOT=../..
LIBRARY_NAME=example_lib
#-------------------------------------------------------------------
# Derived variables
#-------------------------------------------------------------------
LIBRARY_FILENAME=lib$(LIBRARY_NAME).a
LIBRARY_FILEPATH=$(BUILD_CONFIG)/$(LIBRARY_FILENAME)
# Source directories
CPP_SRC_DIRS=$(REPO_ROOT)/source/example_lib/src
# Source files
vpath %.cpp $(CPP_SRC_DIRS)
CPP_SRCS=$(foreach cpp_src_dir, $(CPP_SRC_DIRS), $(wildcard $(cpp_src_dir)/*.cpp))
# Object/dependencies directory
OBJ_DEPS_DIR=./$(BUILD_CONFIG)/obj
# Object files
OBJS=$(CPP_SRCS:%.cpp=$(OBJ_DEPS_DIR)/%.o)
# Dependency files (built with objects)
DEPS=$(CPP_SRCS:%.cpp=$(OBJ_DEPS_DIR)/%.d)
#-------------------------------------------------------------------
# C++ compiler settings
#-------------------------------------------------------------------
CPP_COMMAND=g++
CPP_OPTIONS_INC_PATHS=-I"$(REPO_ROOT)/source/example_lib/inc"
CPP_OPTIONS_OPTIM=-O3
CPP_OPTIONS_WARN=-Wall
CPP_OPTIONS_MISC=-c -fmessage-length=0
CPP_OPTIONS=$(CPP_OPTIONS_INC_PATHS) $(CPP_OPTIONS_OPTIM) $(CPP_OPTIONS_WARN) $(CPP_OPTIONS_MISC)
#-------------------------------------------------------------------
# Archiver settings
#-------------------------------------------------------------------
AR_COMMAND=ar
AR_OPTIONS=-r
#-------------------------------------------------------------------
# Targets
#-------------------------------------------------------------------
# Object/dependency directory target
$(OBJS): | $(OBJ_DEPS_DIR)
$(OBJ_DEPS_DIR):
mkdir -p $(OBJ_DEPS_DIR)
# Object targets
$(OBJ_DEPS_DIR)/%.o: %.cpp
#echo 'Building file: $<'
#echo 'Invoking: GCC C++ Compiler'
$(CPP_COMMAND) $(CPP_OPTIONS) -MMD -MP -MF"$(#:%.o=%.d)" -MT"$(#)" -o "$#" "$<"
#echo 'Finished building: $<'
#echo ' '
# 'all' target
all: $(LIBRARY_FILEPATH)
# Output library target
$(LIBRARY_FILEPATH): $(OBJS)
#echo 'Building target: $#'
#echo 'Invoking: GCC Archiver'
$(AR_COMMAND) $(AR_OPTIONS) "$#" $(OBJS)
#echo 'Finished building target: $#'
#echo ' '
# 'clean' target
clean:
#echo 'Cleaning targets'
rm -rf $(OBJS) $(DEPS) $(LIBRARY_FILENAME)
#echo ' '
# 'PHONY' target
.PHONY: all clean
What 'make' outputs, which makes sense, is:
make all
Building file: ../../source/example_lib/src/example_lib.cpp
Invoking: GCC C++ Compiler
g++ -I"../../source/example_lib/inc" -O3 -Wall -c -fmessage-length=0 -MMD -MP -MF"Release/obj/../../source/example_lib/src/example_lib.d" -MT"Release/obj/../../source/example_lib/src/example_lib.o" -o "Release/obj/../../source/example_lib/src/example_lib.o" "../../source/example_lib/src/example_lib.cpp"
../../source/example_lib/src/example_lib.cpp:6:1: fatal error: opening dependency file Release/obj/../../source/example_lib/src/example_lib.d: No such file or directory
}
^
compilation terminated.
make: *** [Release/obj/../../source/example_lib/src/example_lib.o] Error 1
The source-to-object rule is substituting the full path of the %.cpp file into the %.o target, which results in an object path of:
Release/obj/../../source/example_lib/src/example_lib.o
What I don't understand is how to get the implicit rules for a source and object file to match when they are in different trees. I used vpath for this to help with source resolution, but the object (target) portion doesn't match up. I also tried changing the source-to-object rule to:
$(OBJ_DEPS_DIR)/$(notdir %.o): %.cpp
But this resulted in the same path (it appears commands like $(notdir ...) aren't supported for wildcard matching?). I also realize that dropping all object files from potentially different source directories could result in a name collision if the two source directories happen to have a file with the same name - this isn't a problem for the code I am working with.
Any help is appreciated, and thanks in advance!
The trick is to remove the unneeded paths to the source files, before constructing the names of the object files:
CPP_SRCS=$(foreach cpp_src_dir, $(CPP_SRC_DIRS), $(wildcard $(cpp_src_dir)/*.cpp))
# CPP_SRCS now contains "../../source/example_lib/src/example_lib.cpp"
SRCS := $(notdir $(CPP_SRCS))
# SRCS now contains "example_lib.cpp"
BAD_OBJS := $(CPP_SRCS:%.cpp=$(OBJ_DEPS_DIR)/%.o)
# BAD_OBJS now contains "./Release/obj/../../source/example_lib/src/example_lib.o"
OBJS := $(patsubst %.cpp,$(OBJ_DEPS_DIR)/%.o,$(SRCS))
# OBJS now contains "./Release/obj/example_lib.o"
Some small adjustments to your rules are possible, once you have this much working.

Recursive Makefile with Shared Libs

I'm creating a Makefile for a project. I have the following structure of Makefiles:
./Makefile
./classification/Makefile
./misc/Makefile
./APP/Makefile
./qr/libs/Makefile
I'm doing a recursive make. In each directory, I have a Makefile which generates a Shared Library. So, in the ./classification folder, I'm going to generate classification.so, and thus to the other directories. In general, they have the following structure:
include ../standard_defs.mk
xCFLAGS=$(CFLAGS) -fPIC
SOURCES=help.cpp
OBJECTS=$(SOURCES:.cpp=.o)
EXECUTABLE=misc.so
xxDET=detection/$(EXECUTABLE)
export xxDET;
all: $(SOURCES) $(EXECUTABLE)
$(EXECUTABLE): $(OBJECTS)
$(CC) $(OBJECTS) -shared -o $#
.cpp.o:
$(CC) $(xCFLAGS) $< -c
clean:
rm -f $(OBJECTS) $(EXECUTABLE)
And the main Makefile (./Makefile) has the following structure:
CFLAGS=`pkg-config opencv --cflags`
LDFLAGS=`pkg-config opencv --libs`
include standard_defs.mk
SOURCES=DataFormatDetResult.cpp InputDataFiles.cpp InputImage.cpp \
InputManager.cpp main.cpp maths.cpp misc.cpp
OBJECTS=$(SOURCES:.cpp=.o)
EXECUTABLE=featureExtractor
all: $(LIBS) $(SOURCES) $(EXECUTABLE)
$(EXECUTABLE): $(OBJECTS)
make -C misc
make -C qr/libs
make -C classification
make -C APP
$(CC) $(CFLAGS) $(OBJECTS) -o $# $(LDFLAGS) misc/misc.so qr/libs/ap.so classification/classification.so APP/app.so
.cpp.o:
$(CC) $(CFLAGS) $< -c
clean:
make -C misc clean
make -C qr/libs clean
make -C APP clean
make -C classification clean
rm -f *.o $(EXECUTABLE)
Errors
When I try to compile the main Makefile, I got the following linking error:
classification/classification.so: undefined reference to `Help::InsertHelpType(std::basic_string<char, std::char_traits<char>, std::allocator<char> >)'
classification/classification.so: undefined reference to `Help::Help()'
collect2: ld returned 1 exit status
make: ** [featureExtractor] Erro 1
misc.so contains the class Help and it seems that classification.so does not find it in the final compiling. However, if I run make -C ./classification/, I don't get any errors.
Question 1
How can I solve this linkage problem?
Failed Solutions
I've tried to link misc.so to classification.so, doing this in ./classification/Makefile:
$(EXECUTABLE): $(OBJECTS)
$(CC) $(OBJECTS) -shared -o $# $(LDFLAGS) ../misc/misc.so
But it didn't work. All I've got was a new warning
/usr/bin/ld: warning: ../misc/misc.so, needed by classification/classification.so, not found (try using -rpath or -rpath-link)
Question 2
Is there a better way to do create a Makefile to this project?
EDIT 1: If I run make -C ./classification/, it generates ./classification/classification.so without errors. Then, if I run make in ./, I get the same error.
EDIT 2: When I ran nm --format sysv misc.so | grep Help, I've got:
$ nm --format sysv misc.so | grep Help
HelpTypes |0000000000205120| D | OBJECT|0000000000000038| |.data
_ZN11HelpControl9PrintHelpEv|0000000000002180| T | FUNC|000000000000082e| |.text
_ZN11HelpControlC1Ev|00000000000029b0| T | FUNC|0000000000000187| |.text
_ZN11HelpControlC2Ev|00000000000029b0| T | FUNC|0000000000000187| |.text
_ZN4Help14InsertHelpTypeESs|0000000000001ac0| T | FUNC|0000000000000126| |.text
_ZN4Help9PrintHelpEi|0000000000001bf0| T | FUNC|000000000000020d| |.text
_ZN4HelpC1Ev |0000000000001e00| T | FUNC|000000000000021a| |.text
_ZN4HelpC2Ev |0000000000001e00| T | FUNC|000000000000021a| |.text
_ZN4HelpD1Ev |0000000000003090| W | FUNC|000000000000039b| |.text
_ZN4HelpD2Ev |0000000000003090| W | FUNC|000000000000039b| |.text
_ZNSt8_Rb_treeISsSt4pairIKSs4HelpESt10_Select1stIS3_ESt4lessISsESaIS3_EE8_M_eraseEPSt13_Rb_tree_nodeIS3_E|0000000000003430| W | FUNC|0000000000000526| |.text
Try to get rid of the recursive Makefiles. Although they are somewhat common, recursive make is inherently broken. The main reason, that they are common is because that is what is build when you have automake and autoconf. However autoconf and automake both go to a great length to get the recursive make structure right and I have yet to seem anyone who get's a recursive make structure right without these tools.
There are only a few uses where recursive make is not inherently broken, for example in the way Cmake uses recursive makefiles. But again, these makefiles are build by automated tool, so this too is hard to get right manually.
EDIT: Here is a short summary of the article.
The main problem with recursive makefiles is, that it keeps make from building the full dependency graph, which it needs for building stuff in the right order. Recursive make was originally meant for cases where you would need to build multiple projects at once, without any dependencies between them. As long as there is a dependency across the recursive structure, make will make it very hard to fix the order. In the best case one then is left with a system which can be build, but which break when one tries to rebuild it after editing some of the files. I've seen a lot of cases, where recursive make messed up, so that one part of the application was linked against the libraries compiled from sources before the edit, and another part was linked against libraries compiled from sources after the edit. This leads to sudden breakage of the application, which will miraculously appear after a make clean && make.
In other cases recursive make can mess up the build altogether. This is the case, when the targets are executed in the absolutely wrong order to begin with. This means an ordinary build is not possible. Your example seems like one of those cases. Although I haven't looked at it too closely, it seems that make is not building all needed libraries correctly, because it does not fully know the dependencies.
In almost all cases a parallel make is completely rendered impossible when a recursive make is used. At least I have yet to see a manual recursive make structure which can reliably used with a make -j X.
There are two solutions:
Have the makefiles constructed by one of the tools, such as autotools or cmake. However this requires learning one more tool. Also the usability of these tools is highly debated (at least for autotools).
Get rid of the recursive structure by providing one makefile from which make can derive the full dependency graph. I've seen some people do this actually in a single file even with multiple libraries and automatic source detection, but I wouldn't so I cannot recommend that. Better yet is to have multiple files, one for each subdirectory and then use include to combine them into a large file at the root of the directory tree. This way make can only be called at the root of the tree, but it will always know the full set of dependencies. This way is also recommended by the article.
You haven't given us enough to reproduce the error, so this may take a couple of iterations.
In misc/, we need a way to test the Help class. If you don't have one already, write some simple code to that purpose in misc/:
//test_help.cpp
#include "help.h"
int main()
{
Help H;
return(0);
}
Try it:
make test_help.o help.o
g++ test_help.o help.o -o test_help
./test_help
Then with the library:
make misc.so
g++ test_help.o misc.so -o test_help
./test_help
Then move test_help.cpp to the upper directory and try from there:
make test_help.o
g++ test_help.o misc/misc.so -o test_help
./test_help
Then add a rule to the main Makefile:
test_help: test_help.o
make -C misc
$(CC) $(CFLAGS) $< -o $# misc/misc.so
./$#
and try make clean ; make test_help.

Resources