I searched on the inet but I did not find any clear answer. Could you point me in the right direction on how to convert a Makefile into a CMakeLists?
I want to do that because I am new both to makefile and to cmake. In my job CMake is more used and since I need to start using one of them I prefer having everything in CMake. I know CMake is generating a Makefile but for me CMake is way easier to read than a Makefile.
I have the following Makefile:
PREFIX ?= /usr/local
CC = gcc
AR = ar
CFLAGS = -std=gnu99 -fPIC -Wall -Wno-unused-parameter -Wno-unused-function -I. -O4
APRILTAG_SRCS := $(shell ls *.c common/*.c)
APRILTAG_HEADERS := $(shell ls *.h common/*.h)
APRILTAG_OBJS := $(APRILTAG_SRCS:%.c=%.o)
TARGETS := libapriltag.a libapriltag.so
# LIBS := -Lusr/include/flycapture
.PHONY: all
all: $(TARGETS)
#$(MAKE) -C example all
.PHONY: install
install: libapriltag.so
#chmod +x install.sh
#./install.sh $(PREFIX)/lib libapriltag.so #this should be the line that install the library
#./install.sh $(PREFIX)/include/apriltag $(APRILTAG_HEADERS)
#sed 's:^prefix=$$:prefix=$(PREFIX):' < apriltag.pc.in > apriltag.pc
#./install.sh $(PREFIX)/lib/pkgconfig apriltag.pc
#rm apriltag.pc
#ldconfig
libapriltag.a: $(APRILTAG_OBJS)
#echo " [$#]"
#$(AR) -cq $# $(APRILTAG_OBJS)
libapriltag.so: $(APRILTAG_OBJS)
#echo " [$#]"
#$(CC) -fPIC -shared -o $# $^
%.o: %.c
#echo " $#"
#$(CC) -o $# -c $< $(CFLAGS)
.PHONY: clean
clean:
#rm -rf *.o common/*.o $(TARGETS)
#$(MAKE) -C example clean
I am not asking you to do my job but I would like to have some kind of guide or a good link where to look.
The project contains both C and C++ programming languages.
I started creating a new CMakeLists.txt file, but it is still not working. It gives me the following errors:
You have called ADD_LIBRARY for library librapriltag.a without any source files. This typically indicates a problem with your CMakeLists.txt file
-- Configuring done
CMake Error: Cannot determine link language for target "librapriltag.a".
CMake Error: CMake can not determine linker language for target: librapriltag.a
-- Generating done
-- Build files have been written to: .....
The CMakeLists.txt I started creating is the following:
project( apriltag2 C CXX)
cmake_minimum_required(VERSION 2.8)
set(CMAKE_C_FLAGS "-std=gnu99 -fPIC -Wall -Wno-unused-parameter -Wno-unused-function -I. -O4")
include_directories("/home/fschiano/Repositories/apriltag2")
include_directories("/home/fschiano/Repositories/apriltag2/common")
add_library( librapriltag.a )
The CMakeLists.txt which works is the following:
project( apriltag2 )
cmake_minimum_required(VERSION 2.8)
set(CMAKE_C_FLAGS "-std=gnu99 -fPIC -Wall -Wno-unused-parameter -Wno-unused-function -I. -O4")
message("CMAKE_SOURCE_DIR=${CMAKE_SOURCE_DIR}")
file(GLOB apriltag_SRC "*.c")
file(GLOB apriltag_HEADERS "*.h")
set(APRILTAG_SRCS ${apriltag_SRC})
set(APRILTAG_HEADERS ${apriltag_HEADERS})
message(STATUS "CMAKE_CURRENT_LIST_DIR=${CMAKE_CURRENT_LIST_DIR}")
add_library(apriltag STATIC ${APRILTAG_SRCS})
target_include_directories(apriltag PUBLIC ${CMAKE_SOURCE_DIR})
target_compile_options(apriltag PUBLIC -fPIC -Wall -Wno-unused-parameter -Wno-unused-function -O4)
install(TARGETS apriltag
ARCHIVE DESTINATION lib
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib)
install(DIRECTORY CMAKE_CURRENT_LIST_DIR/include/
DESTINATION CMAKE_CURRENT_LIST_DIR/include/
FILES_MATCHING PATTERN *.h)
EDIT:
Something is still not right. If I want to change something in my library, like something which is in /home/fschiano/Repositories/apriltag2/common
If I use the Makefile which I had before doing all these modifications and I do:
make
do some modifications in the files I wanted to modify
sudo make install, which would give me the following output:
/usr/local/lib/libapriltag.so
/usr/local/include/apriltag/apriltag.h
/usr/local/include/apriltag/common/g2d.h
/usr/local/include/apriltag/common/getopt.h
/usr/local/include/apriltag/common/homography.h
/usr/local/include/apriltag/common/image_f32.h
/usr/local/include/apriltag/common/image_u8.h
/usr/local/include/apriltag/common/image_u8x3.h
/usr/local/include/apriltag/common/matd.h
/usr/local/include/apriltag/common/math_util.h
/usr/local/include/apriltag/common/pnm.h
/usr/local/include/apriltag/common/postscript_utils.h
/usr/local/include/apriltag/common/string_util.h
/usr/local/include/apriltag/common/svd22.h
/usr/local/include/apriltag/common/thash_impl.h
/usr/local/include/apriltag/common/timeprofile.h
/usr/local/include/apriltag/common/time_util.h
/usr/local/include/apriltag/common/unionfind.h
/usr/local/include/apriltag/common/workerpool.h
/usr/local/include/apriltag/common/zarray.h
/usr/local/include/apriltag/common/zhash.h
/usr/local/include/apriltag/common/zmaxheap.h
/usr/local/include/apriltag/tag16h5.h
/usr/local/include/apriltag/tag25h7.h
/usr/local/include/apriltag/tag25h9.h
/usr/local/include/apriltag/tag36artoolkit.h
/usr/local/include/apriltag/tag36h10.h
/usr/local/include/apriltag/tag36h11.h
/usr/local/lib/pkgconfig/apriltag.pc
/sbin/ldconfig.real: /usr/lib/libstdc++.so.5 is not a symbolic link
and the modifications would take effect.
Now, if I remove the Makefile and I do:
cmake .
make
do some modifications in the files I wanted to modify
sudo make install, it gives me the following output:
Install the project...
-- Install configuration: ""
-- Up-to-date: /usr/local/lib/libapriltag.a
So it seems that the install part of the CMakeLists.txt is not right!
The file install.sh is the following.
#!/bin/sh -e
# Usage: install.sh TARGET [RELATIVE PATHS ...]
#
# e.g. ./install.sh /usr/local foo/file1 foo/file2 ...
# This creates the files /usr/local/foo/file1 and /usr/local/foo/file2
TARGETDIR=$1
shift
for src in "$#"; do
dest=$TARGETDIR/$src
mkdir -p $(dirname $dest)
cp $src $dest
echo $dest
done
Could you try to help me?
Thanks
Let's go through that step-by-step:
PREFIX ?= /usr/local
We ignore that, as it's the default. Can be overwritten by CMAKE_INSTALL_PREFIX.
CC = gcc
AR = ar
Ignore these as well. Use CMAKE_C_COMPILER and CMAKE_CXX_COMPILER to forcibly switch the compiler.
CFLAGS = -std=gnu99 -fPIC -Wall -Wno-unused-parameter -Wno-unused-function -I. -O4
They are pretty special for gcc-like compilers. Set them conditionally for CMAKE_C_COMPILER_ID MATCHES GNU further down after defining the target.
The standard is set by set(C_STANDARD 98) and set(CXX_STANDARD 98).
APRILTAG_SRCS := $(shell ls *.c common/*.c)
Define a variable listing all the source files individually: set(APRILTAG_SRCS ...)
APRILTAG_HEADERS := $(shell ls *.h common/*.h)
Define a variable listing all the header file individually: set(APRILTAG_HEADERS ...). However, you don't really need them anywhere (unless you want Visual Studio to list them).
APRILTAG_OBJS := $(APRILTAG_SRCS:%.c=%.o)
In most cases, you don't need that. For those rare cases there are Object Libraries.
TARGETS := libapriltag.a libapriltag.so
# LIBS := -Lusr/include/flycapture
We define our libraries here with add_library:
add_library(apriltag ${APRILTAG_SRCS})
target_include_directories(apriltag PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include/apriltag)
target_compile_options(apriltag PUBLIC -fPIC -Wall -Wno-unused-parameter -Wno-unused-function -O4)
The switch between static and shared is done via BUILD_SHARED_LIBS on invocation of CMake.
.PHONY: all
all: $(TARGETS)
#$(MAKE) -C example all
Nothing to do here. CMake will automatically create that.
.PHONY: install
install: libapriltag.so
#chmod +x install.sh
#./install.sh $(PREFIX)/lib libapriltag.so #this should be the line that install the library
#./install.sh $(PREFIX)/include/apriltag $(APRILTAG_HEADERS)
#sed 's:^prefix=$$:prefix=$(PREFIX):' < apriltag.pc.in > apriltag.pc
#./install.sh $(PREFIX)/lib/pkgconfig apriltag.pc
#rm apriltag.pc
#ldconfig
CMake will ease this up by a magnitude:
install(TARGETS apriltag
ARCHIVE DESTINATION lib
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib)
install(DIRECTORY include/
DESTINATION include/
FILES_MATCHING PATTERN *.h)
That will install the library static and shared library (whatever exists) and the header files.
libapriltag.a: $(APRILTAG_OBJS)
#echo " [$#]"
#$(AR) -cq $# $(APRILTAG_OBJS)
libapriltag.so: $(APRILTAG_OBJS)
#echo " [$#]"
#$(CC) -fPIC -shared -o $# $^
%.o: %.c
#echo " $#"
#$(CC) -o $# -c $< $(CFLAGS)
All this is not needed.
.PHONY: clean
clean:
#rm -rf *.o common/*.o $(TARGETS)
#$(MAKE) -C example clean
You don't need that. CMake will generate a clean target automatically.
Judging from TARGETS := libapriltag.a libapriltag.so, you'll defintely need add_library command to create targets.
Instead of gathering souces to be compiled using wildcards like APRILTAG_SRCS := $(shell ls *.c common/*.c) it is recommended to list them explicitly in add_library call. But if you really want to list them automatically, see file(GLOB ...) command. (There are some important things to be aware of, though, see Specify source files globally with GLOB?).
The clean target would be generated automatically by CMake.
Finally, see the documentation for install() command to create install rules.
Compiler flags are set using set(CMAKE_C_FLAGS "blabla"), or appended using set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} blabla").
Related
Makefile specified in this question, compiling all the cpp programs in a folder but not with python embedded cpp programs.
all: myUB
sourcesC := $(wildcard ../src/*.cpp)
objectsC := $(patsubst %.cpp,%.o,$(sourcesC))
INPATH=-I"C:/Python27/include"
LIBPATH=-L"C:/Python27/libs"-lpython27
myUB:
#echo 'Building target $#'
g++ -O0 -Wall -c -g3 -fmessage-length=0 \
$(sourcesC)
del *.o
clean:
Your final makefile could look somthing like:
all: myUB
sourcesC := $(wildcard ../src/*.cpp)
# Not used
#objectsC := $(patsubst %.cpp,%.o,$(sourcesC))
INC = -IC:\Python27\include
LIBS = -LC:\Python27\libs -lpython27
myUB:
#echo 'Building target $#'
g++ -O0 -Wall -g3 -fmessage-length=0 -o myprog.out $(sourcesC) $(INC) $(LIBS)
clean:
rm myprog.out
update
For the undefined ref to WinMain(), it means the linker can't find this function in your code. Either you need to include a library/object that contains it or you can define it yourself in a cpp file like:
#include <windows.h>
int WINAPI (*MyDummyReferenceToWinMain)(HINSTANCE hInstance, ..., int
nShowCmd ) = &WinMain;
I got the function template from here.
But this seems to mean that you are creating a windows application instead of a console app which uses int main(...) entry point.
Update2
I have made a new makefile to do what you have asked for in your latest comment which seems to be to create one executable per source file - I am assuming each source file has its own main.
# Build vars
CXX = g++
CXX_FLAGS = -O0 -Wall -g3
INC = -IC:\Python27\includ
LIBS = -LC:\Python27\libs -lpython27
# Sources
SRC_DIR=src
SOURCES = $(wildcard $(SRC_DIR)/*.cpp)
$(info SOURCES: $(SOURCES))
# Executables
EXE_DIR=bin
EXECUTABLES = $(subst $(SRC_DIR)/,$(EXE_DIR)/,$(subst cpp,out,$(SOURCES)))
$(info EXECUTABLES: $(EXECUTABLES))
$(info ----)
# Directories
DIRS = $(EXE_DIR)
# Rule to create folders and compile executables
all: $(DIRS) $(EXECUTABLES)
# Pattern rule to build each executable
$(EXE_DIR)/%.out : $(SRC_DIR)/%.cpp
#echo "compiling $< --> $#"
#$(CXX) $(CXX_FLAGS) -o $# $< $(INC) $(LIBS)
# Rule to create output dirs
$(DIRS):
#echo "Creating output folders"
#mkdir -p $(EXE_DIR)
# Rule to clean up
clean:
#echo "Cleaning"
#rm -rf $(EXE_DIR)
This should create one executable in the folder bin/ for each source file (.cpp) in folder src/.
Pardon my question, I am a beginner to GCC. I have a framework project that holds source code for multiple subcomponents.
The structure is below:
Framework/
makefile //Master makefile in root
Component1/
src/
bin/
makefile
Component2/
src/
bin/
makefile
...
...
...
ComponentN/
src/
bin/
makefile
Now each makefiles in ComponentN/ each of directories will compile the code in its respective src/ and output .o to bin/ directory.
The root makefile however searches all the .o files recursively and links them all into one executable named 'framework'
Problem:
For code dependencies like glib,gdbus,gio I have to link them once when creating .o objects, in each of the component projects.
Plus I have to link the dependencies again when linking all the .o into one executable at root level.
Why do I have to do it twice? I am interested in understanding the internal mechanics.
As per request I am putting in makefile of the individual component libs that products *.o files
CC = gcc
CFLAGS = -g3
LIBS = `pkg-config --cflags --libs glib-2.0`
BINDIR = bin
OUTOBJ = $(addprefix $(BINDIR)/, objex.o)
$(BINDIR)/%.o : %.c
$(CC) -c $< $(CFLAGS) -o $# $(LIBS)
all: $(OUTOBJ)
$(OUTOBJ): | $(BINDIR)
$(BINDIR):
mkdir $(BINDIR)
.PHONY: clean
clean:
rm bin/*
Object files (.o) are created by compilation commands, e.g.
gcc -c -o foo.o foo.c ...
g++ -c -o baz.o baz.cpp ...
-c means compile; don't link. No linkage happens in the creation of
object files by the compiler. Any linkage options that you add to a compilation
command, e.g.
gcc -c -o foo.o foo.c -L/my/libs -lbar -lgum
are simply ignored.
Linkage options are acted on by a linkage command, which creates a program, or shared/dynamic
library, by linking together object files and libraries, e.g.
gcc -o prog foo.o baz.o -L/my/libs -lbar -lgum
gcc -shared -o libfoobaz.so foo.o baz.o -L/my/libs -lbar -lgum
So:
For code dependencies like glib,gdbus,gio I have to link them once when creating .o objects, in each of the component projects.
No you don't, and you can't.
Later
With sight of the problem makefile it is quite clear how to eliminate
the $(LIBS) reference from the compilation recipe, and what has been stopping you. The makefile defines:
LIBS = `pkg-config --cflags --libs glib-2.0`
which is a mistake. That makes $(LIBS) expand to the standard output of the
command:
pkg-config --cflags --libs glib-2.0
which is a single string containing both the compilation options required
for compiling source that #include-s the glib-2.0 API (on account of --cflags)
and also the linkage options required for linking a program or shared library
against libglib-2.0 (on account of --libs). On my system that is:
$ pkg-config --cflags --libs glib-2.0
-I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -lglib-2.0
of which the compilation options alone would be output by:
$ pkg-config --cflags glib-2.0
-I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include
and the linkage options alone would be output by:
$ pkg-config --libs glib-2.0
-lglib-2.0
But because both sets of options are available only together through the expansion
of $(LIBS) you cannot successfully compile without passing the
linkage option -lglib-2.0, which is redundant and ignored.
As your make tool is evidently GNU Make, the makefile (which BTW is not that bad!) would be better written as:
Makefile
CC := gcc
CFLAGS := -g3 $(shell pkg-config --cflags glib-2.0)
BINDIR := bin
SRCS := objex.c
OUTOBJ := $(addprefix $(BINDIR)/, $(SRCS:.c=.o))
.PHONY: all clean
all: $(OUTOBJ)
$(BINDIR)/%.o : %.c
$(CC) -c $< $(CFLAGS) -o $#
$(OUTOBJ): | $(BINDIR)
$(BINDIR):
mkdir -p $(BINDIR)
clean:
$(RM) $(OUTOBJ)
which dispenses with LIBS and runs from scratch like:
$ make
mkdir -p bin
gcc -c objex.c -g3 -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -o bin/objex.o
Note a few other improvements:-
The use of immediate expansion (:=) wherever applicable in preference to unnecessary
recursive expansion (=). See 6.2 The Two Flavors of Variables
The use of direct shell substitution by make - $(shell command) - in preference to backtick-expansion in
recipe execution. See 8.13 The shell Function.
all, like clean is a phony target
and you need to tell make that it is, to avoid the booby-trap in which something creates a file called all in
the project directory without you noticing and make mysteriously stops detecting any work for it to do.
With your clean receipe:
clean:
rm bin/*
make clean will fail if ever run except following a successful build. The recipe
is replaced with $(RM) $(OUTOBJ), using GNU Make's predefined delete macro, which
won't fail.
Finally, remember that your linkage recipe, wherever it is, does need the library options for glib-2.0,
which you should provide in its makefile with:
LIBS := $(shell pkg-config --libs glib-2.0) # ...and any more library options required
for use in a recipe similar to:
prog: $(OBJS)
$(CC) -o $# $(LDFLAGS) $^ $(LIBS)
[1] Strictly, preprocessor options should appear in the definition of CPPFLAGS
(C PreProcessor Flags), not to be confused with CXXFLAGS (C++ compilation options).
[2] Strictly, linkage options other than libraries should appear in the definition
of LDFLAGS.
I don't understand the dependencies very well
TARGET = prog1.exe
SRC = src
# dir. with *.cpp
INC = inc
# dir. with *.hpp
BIN = bin
# dir. with *.o, *.out,*.exe
SOURCE = $(wildcard $(SRC)/*.cpp)
OBJECT = $(patsubst %, $(BIN)/%, $(notdir $(SOURCE:.cpp=.o)))
CXX = g++
CXXFLAGS = -g -I$(INC)
ifneq ($(ARGS),'')
GDBI := --args $(BIN)/$(TARGET) $(ARGS)
else
GDBI := $(BIN)/$(TARGET)
endif
$(BIN)/$(TARGET): $(OBJECT)
$(CXX) -o $# $^
$(BIN)/%.o: $(SRC)/%.cpp
$(CXX) $(CXXFLAGS) -c $< -o $#
.PHONY: help run clean depurate
run: $(BIN)/
$(BIN)/$(TARGET) ${ARGS}
clean:
rm -f $(OBJECT) $(BIN)/$(TARGET)
depurate:
gdb $(GDBI)
help:
#echo "Para ejecutar: \`make run' o \`make ARGS=\"<argumentos>\" run'."
Why is Make not checking changes with the headers files .hpp in the directory inc/?
Is right this solution for headers without source files?
I mean: x.cpp includes x.hpp, and x.hpp includes a.hpp and b.hpp.
$(BIN)/%.o: $(SRC)/%.cpp $(INC)/%.hpp
$(CXX) $(CXXFLAGS) -c $< -o $#
Make isn't doing dependency checking on your header files because they're not explicitly listed as a dependency anywhere.
If you want to make your source files depend on all of the header files, you can do something like this:
HEADERS:=$(wildcard $(INC)/*.hpp)
$(BIN)/%.o: $(SRC)/%.cpp $(HEADERS)
That will rebuild all of your source files whenever any header changes, which works but is probably overkill. If you only want a source file to depend on those header files it actually uses, you'll want to use the compiler's automatic dependency generation capabilities:
CXXFLAGS = -g -I$(INC) -MMD -MF $(<:.cpp=.d)
include $(wildcard $(SRC)/*.d)
$(BIN)/%.o: $(SRC)/%.cpp
$(CXX) $(CXXFLAGS) -c $< -o $#
clean:
rm -f $(OBJECT) $(BIN)/$(TARGET) $(SRC)/*.d
Now, when the compiler compiles a .cpp file, it will create a matching .d file alongside it. This file is a tiny fragment of a makefile that lists the headers that .cpp file uses. The next time you build, the include line pulls in all of those .d files and uses that information to help calculate dependencies. The blog post mentioned by Tim in the comments has a great detailed explanation of how this works.
I have been working on this makefile for quite some time now and I can't find the solution to my problem. Here is the makefile:
# Compiler:
CPPFLAGS = $(OPT_FLAGS) $(DEBUG_FLAGS) $(STANDARD_FLAGS) \
$(WARN_AS_ERRORS_FLAGS)
# Source files, headers, etc.:
OBJ_DIR = $(CX_BUILD_ROOT)/tests/unit
OUT_DIR = $(CX_BUILD_ROOT)/tests/unit
INCLUDES = -I$(CX_SRC_ROOT)/cXbase/publicAPI
LIBINCLUDES = -L$(CX_BUILD_ROOT)/connectx/libs
VPATH = tests
SRCS = cxUnitTests.cpp\
test_Player.cpp\
test_Name.cpp\
test_Game.cpp\
test_GameBoard.cpp\
test_Disc.cpp\
test_Color.cpp\
test_AsciiColorCode.cpp\
OBJS = test_Player.o\
test_Name.o\
test_Game.o\
test_GameBoard.o\
test_Disc.o\
test_Color.o\
test_AsciiColorCode.o\
LIBS = -lgtest\
-lgtest_main\
-lpthread\
-lcXbase
# Product:
MAIN = cxUnitTests.out
all: make_dir $(MAIN)
$(MAIN): $(OBJS)
#echo Invoquing GCC...
$(CPPC) $(LIBINCLUDES) -o $(OUT_DIR)/$(MAIN) $(OBJS) $(LIBS)
#echo $(MAIN) has been compiled and linked!
$(OBJ_DIR)/%.o: %.cpp
#echo Invoquing GCC...
$(CPPC) $(CPPFLAGS) $(INCLUDES) -c $< -o $#
#echo Object files created!
make_dir:
mkdir -p $(OBJ_DIR)
mkdir -p $(OUT_DIR)
clean:
#echo Removing object files...
$(RM) $(OBJ_DIR)/*.o
#echo Object files removed!
mrproper: clean
#echo Cleaning project...
$(RM) $(OUT_DIR)/$(MAIN)
#echo Project cleaned!
depend: $(SRCS)
#echo Finding dependencies...
makedepend $(INCLUDES) $^
#echo Dependencies found!
All values in the "Source files, headers, etc" section are defined in other makefiles from which this makefile is invoked with the $(MAKE) -C option They can all be #echoed and the resultant values are good. When I run make, I get:
g++ -g3 -std=c++0x -pedantic-errors -Wall -Wextra -Werror -Wconversion -c -o test_Player.o tests/test_Player.cpp
and
tests/test_Player.cpp:36:30: fatal error: publicAPI/Player.h: No such file or directory
It seems that make cannot access the content of the INCLUDES variable for some reason. I use Gnu-make.
Can you see what is wrong?
Regards
Make is using its built-in rule for compiling C++ files because your pattern rule $(OBJ_DIR)/%.o: %.cpp doesn't match your list of objects. By coincidence you've used one of the variables that the built-in recipe uses (CPPFLAGS), but make makes no use of INCLUDES.
One way to fix it would be to put something like the following after your list of objects
OBJS := $(addprefix $(OBJ_DIR)/,$(OBJS))
I finally found the problem. In fact, there were two:
The INCLUDES variable should have been set to
$(CX_SRC_ROOT)/cXbase instead of $(CX_SRC_ROOT)/cXbase/publicAPI
since, like the error message shows, the include file for Player
is looked for in publicAPI/Player.h so publicAPI was there
twice, but didn't show twice in the #echo.
My object list should have been in the form: $(OBJ_DIR)/objectFile.o.
I'm trying to create a generic makefile to build static libraries that my project uses. I've used the expertise on this site, as well as the GNU Make manual to help write the following makefile. It is working well apart from one annoying problem.
# Generic makefile to build/install a static library (zlib example)
ARCH = linux
CFLAGS = -O3 -Wall
# List of source code directories
SOURCES = src test utils
# List of header files to install
INCLUDES = src/zlib.h src/zconf.h
# Library to create
LIBNAME = libz.a
############################################################
BUILD_DIR = build/$(ARCH)
# Full path to the built library
TARGET = $(BUILD_DIR)/$(LIBNAME)
prefix = ../..
exec_prefix = prefix
libdir = $(prefix)/lib/$(ARCH)
includedir = $(prefix)/include
INSTALL_PROGRAM = install -D
INSTALL_DATA = $(INSTALL_PROGRAM) -m 644
CFILES := $(foreach dir,$(SOURCES),$(wildcard $(dir)/*.c))
OBJECTS := $(addprefix $(BUILD_DIR)/,$(CFILES:.c=.o))
DEPENDS := $(OBJECTS:.o=.d)
.PHONY: all installdirs install uninstall clean
# Default
all: $(DEPENDS) $(TARGET)
# Build the dependency files
# (GNU Make Manual 4.14 Generating Prerequisites Automatically)
$(BUILD_DIR)/%.d: $(BUILD_DIR)
#echo "build dep for $*.c as $#"
#$(CC) -M $(CFLAGS) $*.c > $#.tmp
#sed s~.*:~"$(BUILD_DIR)/$*.o $#:"~ $#.tmp > $#
#rm $#.tmp
# Link all changed object files into static library
$(TARGET): $(OBJECTS)
$(AR) -rc $(TARGET) $?
# Compile C source to object code
$(BUILD_DIR)/%.o: %.c
$(CC) $(CFLAGS) -c $< -o $#
# Create the necessary directory tree for the build
$(BUILD_DIR):
#for p in $(SOURCES); do mkdir -p $(BUILD_DIR)/$$p; done
# Create the necessary directory tree for installation
installdirs:
#mkdir -p $(libdir)
#mkdir -p $(includedir)
# Install the library and headers
install: all installdirs
$(INSTALL_DATA) $(TARGET) $(libdir)
for p in $(INCLUDES); do $(INSTALL_DATA) $$p $(includedir); done
# Remove the library and headers
uninstall:
rm -f $(libdir)/$(LIBNAME)
for p in $(notdir $(INCLUDES)); do rm -f $(includedir)/$$p; done
# Remove all build files
clean:
rm -fr $(BUILD_DIR)
# Pull in the dependencies if they exist
# http://scottmcpeak.com/autodepend/autodepend.html
-include $(DEPENDS)
The problem is that the dependency files are built when they don't need to be. e.g. make install shown below rebuilds the .d files before installing.
$make --version
GNU Make 3.81
$make
build dep for utils/utils.c as build/linux/utils/utils.d
build dep for test/test.c as build/linux/test/test.d
build dep for src/zutil.c as build/linux/src/zutil.d
...
{ continues on making the other .d files, then the .o files }
...
cc -O3 -Wall -c src/zutil.c -o build/linux/src/zutil.o
cc -O3 -Wall -c test/test.c -o build/linux/test/test.o
cc -O3 -Wall -c utils/utils.c -o build/linux/utils/utils.o
ar rc { ... .o files ... }
All good up to this point! But a 'make install' now will rebuild the .d files
$make install
build dep for utils/utils.c as build/linux/utils/utils.d
build dep for test/test.c as build/linux/test/test.d
build dep for src/zutil.c as build/linux/src/zutil.d
{ ... }
install -D -m 644 build/linux/libz.a ../../lib/linux
for p in src/zlib.h src/zconf.h; do install -D -m 644 $p ../../include; done
I tried to 'touch' the .d files when the objects are built, so the update time is newer than the .o files, but that had no effect. What's wrong with my makefile?
The problem is that you include the dependency files (whatever.d), and you also have a rule for building these files. So every time you use this makefile, no matter what target you specify, it will rebuild them, include them, and execute again.
Try this for an approach to dependency handling that solves this problem. Basically, you don't need a separate rule for foo.d, just make it a side effect of the foo.o rule (it takes some thought to realize that this will work).