I am writing a Makefile for a project where all the cpp files are in subdirectories and some of them contains spaces:
└── L0/
│ └──── Commodities API/
│ │ └──── commodities_api.h
│ └──── Communication API/
│ │ └──── communication_api.h
│ └──── L0 Base/
│ │ ├──── L0_base.cpp
│ │ └──── L0_base.h
│ └──── Provision API/
│ │ └──── provision_api.h
│ ├──── L0_commodities.cpp
│ ├──── L0_communication.cpp
│ ├──── L0.cpp
│ ├──── L0_enumerations.h
│ ├──── L0_error_manager.h
│ ├──── L0.h
│ ├──── L0_provision.cpp
This is my Makefile 'til now:
CPP = g++
LFLAGS +=
CFLAGS += -g -MMD
INCLUDES +=
CPP_SRCS := $(shell find sources -name '*.cpp' | sort | uniq | sed 's/ /\\\\ /g')
CPP_DIRS := $(shell find sources -name '*.cpp' -printf '%h\n' | sort | uniq | sed 's/ /\\\\ /g')
OBJS := $(CPP_SRCS:.cpp=.o)
OUT_DIR := out
all:
#echo $(CPP_SRCS)
#echo
#echo $(OBJS)
#echo
#echo $(CPP_DIRS)
lib.so: $(OBJS)
%.o: %.cpp Makefile
#echo "[C] $<"
$(CPP) -c $(CFLAGS) $(INCLUDES) "$<"
This is the output from the "all" target, used to print the three variables:
sources/L0/L0\ Base/L0_base.cpp sources/L0/L0_commodities.cpp sources/L0/L0_communication.cpp sources/L0/L0.cpp sources/L0/L0_provision.cpp sources/L1/Crypto\ Libraries/aes256.cpp sources/L1/L1\ Base/L1_base.cpp sources/L1/L1.cpp sources/L1/L1_login_logout.cpp sources/L1/L1_security.cpp sources/L1/L1_sekey.cpp
sources/L0/L0\ Base/L0_base.o sources/L0/L0_commodities.o sources/L0/L0_communication.o sources/L0/L0.o sources/L0/L0_provision.o sources/L1/Crypto\ Libraries/aes256.o sources/L1/L1\ Base/L1_base.o sources/L1/L1.o sources/L1/L1_login_logout.o sources/L1/L1_security.o sources/L1/L1_sekey.o
sources/L0 sources/L0/L0\ Base sources/L1 sources/L1/Crypto\ Libraries sources/L1/L1\ Base
But when I try to launch make lib.so
make: *** No rule to make target 'sources/L0/L0\', needed by 'lib.so'. Stop.
What can I do to solve this problem? Thanks!
Make use spaces as list delimiter, and there is no good way to escape it (pun intended). Your options include:
Replace the spaces with something else, like dash or underscore
Migrate from Make to e.g. CMake or SCons
For Windows you can reference any file or directory by its short path which is guaranteed not having spaces, see https://superuser.com/questions/348079/how-can-i-find-the-short-path-of-a-windows-directory-file.
Related
I need a Makefile can be used to build a project with this kind of structure :
Multiple source folders.
all objects in a output dir.
Multiple corresponding target folders for object and dependency files.
│─inc
└─src
│ ├─lib1
│ │ ├─inc
│ │ └─src
│ ├─lib2
│ │ ├─inc
│ │ └─src
│ └─main.c
│
└─output
└─src
├─lib1
│ └─src
│ ├─ lib1_file1.o
│ └─ lib1_file2.o
├─lib2
│ └─src
│ ├─ lib2_file1.o
│ └─ lib2_file2.o
└─main.o
I tried this one, but not work.
LIBNAME=libhello.a
OUTDIR=output
CC = gcc
CFLAGS = -Wall -MD -O2 -c
LFLAGS =
INCS = -Iinc \
-Isrc/lib1/inc \
-Isrc/lib2/inc
SOURCES = src/main.c \
src/lib1/lib1_file1.c \
src/lib1/lib1_file2.c \
src/lib2/lib2_file1.c \
src/lib2/lib2_file2.c
OBJS=$(SOURCES:%.c=${OUTDIR}/%.o)
all: ${LIBNAME}
${LIBNAME}: ${OBJS}
ar crs ${OUTDIR}/$# $^
.PYONY: clean
clean:
#rm -rf ${OUTDIR}
${OUTDIR}/%.o:%.c
#mkdir -p $(#D)
#${CC} ${CFLAGS} ${INCS} $< -o $# ${LFLAGS}
Your makefile will work correctly if you give it the correct source paths. You gave it this:
SOURCES = ... src/lib1/lib1_file1.c ...
but the actual path in your directory structure is:
SOURCES = ... src/lib1/src/lib1_file1.c ...
I have a library which until now was just a static library, and I used it as a submodule of my programs.
Now I want to be able to install it and be able to use it both as a static and a shared library.
I installed (apart from the header files) the following files:
$ tree /usr/local/lib/
/usr/local/lib/
├── libalx
│ ├── libalx-base.a
│ ├── libalx-base.so
│ ├── libalx-cv.a
│ ├── libalx-cv.so
│ ├── libalx-gsl.a
│ ├── libalx-gsl.so
│ ├── libalx-ncurses.a
│ ├── libalx-ncurses.so
│ ├── libalx-ocr.a
│ └── libalx-ocr.so
├── pkgconfig
│ ├── libalx-base.pc
│ ├── libalx-cv.pc
│ ├── libalx-gsl.pc
│ ├── libalx-ncurses.pc
│ └── libalx-ocr.pc
The contents of (some of) the pkg-config files are the following:
libalx-base.pc:
Name: libalx-base
Description: The libalx C/C++ library (base module)
URL: https://github.com/alejandro-colomar/libalx
Version: 1~b4
Requires: libbsd-overlay
Requires.private:
prefix=/usr/local/
libdir=${prefix}/lib/
Cflags: -D _GNU_SOURCE -D _POSIX_C_SOURCE=200809L
Libs: -L ${libdir}/libalx/ -l alx-base
libalx-cv.pc:
Name: libalx-cv
Description: The libalx C/C++ library (openCV extension)
URL: https://github.com/alejandro-colomar/libalx
Version: 1~b4
Requires: libalx-gsl
Requires.private: opencv libalx-base
prefix=/usr/local/
libdir=${prefix}/lib/
Cflags: -D _GNU_SOURCE -D _POSIX_C_SOURCE=200809L
Libs: -L ${libdir}/libalx/ -l alx-cv
libalx-gsl.pc:
Name: libalx-gsl
Description: The libalx C/C++ library (GSL extension)
URL: https://github.com/alejandro-colomar/libalx
Version: 1~b4
Requires:
Requires.private: gsl libalx-base
prefix=/usr/local/
libdir=${prefix}/lib/
Cflags: -D _GNU_SOURCE -D _POSIX_C_SOURCE=200809L
Libs: -L ${libdir}/libalx/ -l alx-gsl
I'm using the library in a program that makes use of only two of the modules: base & cv.
I use the following CFLAGS and LIBS (LDFLAGS) in its Makefile:
################################################################################
# cflags
CFLAGS_STD = -std=gnu17
CFLAGS_OPT = -O3
CFLAGS_OPT += -march=native
CFLAGS_OPT += -flto
CFLAGS_W = -Wall
CFLAGS_W += -Wextra
CFLAGS_W += -Wstrict-prototypes
CFLAGS_W += -Werror
CFLAGS_PKG = `pkg-config --cflags opencv`
CFLAGS_PKG += `pkg-config --cflags libalx-cv`
CFLAGS_PKG += `pkg-config --cflags libalx-base`
CFLAGS_D = -D PROG_VERSION=\"$(PROGRAMVERSION)\"
CFLAGS_D += -D INSTALL_SHARE_DIR=\"$(INSTALL_SHARE_DIR)\"
CFLAGS_D += -D INSTALL_VAR_DIR=\"$(INSTALL_VAR_DIR)\"
CFLAGS = $(CFLAGS_STD)
CFLAGS += $(CFLAGS_OPT)
CFLAGS += $(CFLAGS_W)
CFLAGS += $(CFLAGS_PKG)
CFLAGS += $(CFLAGS_D)
export CFLAGS
################################################################################
# libs
LIBS_OPT = -O3
LIBS_OPT += -march=native
LIBS_OPT += -flto
LIBS_OPT += -fuse-linker-plugin
LIBS_PKG = `pkg-config --libs libalx-cv`
LIBS_PKG += `pkg-config --libs libalx-base`
LIBS = $(LIBS_OPT)
LIBS += $(LIBS_PKG)
export LIBS
################################################################################
And I have the following error:
gcc tmp/coins.o tmp/main.o tmp/parse.o -o cv-coins -O3 -march=native -flto -fuse-linker-plugin `pkg-config --libs libalx-cv` `pkg-config --libs libalx-base`
/usr/bin/ld: cannot find /usr/local//lib//libalx/: file format not recognized
collect2: error: ld returned 1 exit status
What is wrong? I assume the problem is in the .pc files, but I tried moving dependencies from requires to requires.private, the other way around, and other things, and none worked. For example, if I move libbsd-overlay to Requires.private, then the linker complains about not finding strnstr.
I got a directory where store my project assets, say:
+-- assets
| +-- styles
| | +-- child
| | | +-- child.css
| | +-- main.css
| +-- font.ttf
| +-- image.png
As you can see, they are several file types (this is only a sample, may have many more types) and the tree depth may vary.
How can i copy all this files into a single (flat) location? Something like this:
+-- assets
| +-- child.css
| +-- font.ttf
| +-- image.png
| +-- main.css
I tried use pattern rules but i'm pretty new in GNU Make, so nothing seems to work.
I got the recursive list of source files using the Bash globstar functionality, and modify it by Make's string manipulation functions:
BR_ASSETS := /home/user/project/assets/
BR_APP_ASSETS := /home/user/project/build/assets/
SOURCES := $(shell ls $(BR_ASSETS)**/*.*)
TARGETS := $(addprefix $(BR_APP_ASSETS),$(notdir $(SOURCES)))
What next?
Many thanks in advance.
all:
.PHONY: all
BR_ASSETS := /home/user/project/assets/
BR_APP_ASSETS := /home/user/project/build/assets/
SOURCES := $(shell find $(BR_ASSETS) -type f)
$(info SOURCES=$(SOURCES))
TARGETS := $(addprefix $(BR_APP_ASSETS),$(notdir $(SOURCES)))
# Generate rules to map sources into targets
$(foreach s,$(SOURCES),$(foreach t,$(filter %$(notdir $s),$(TARGETS)),$(info New rule: $t: $s)$(eval $t: $s)))
# All targets have the same recipe.
$(TARGETS):; $(if $(wildcard $(#D)),,mkdir -p $(#D) &&) cp $^ $#
all: $(TARGETS)
clean:; rm -rf $(BR_APP_ASSETS)
.PHONY: clean
Environment before running this Makefile:
$ find /home/user/project
/home/user/project
/home/user/project/assets
/home/user/project/assets/font.ttf
/home/user/project/assets/img.png
/home/user/project/assets/styles
/home/user/project/assets/styles/child
/home/user/project/assets/styles/child/child.css
/home/user/project/assets/styles/main.css
/home/user/project/build
Run this Makefile:
$ make -f Makefile.sample
SOURCES=/home/user/project/assets/font.ttf /home/user/project/assets/img.png /home/user/project/assets/styles/child/child.css /home/user/project/asset
s/styles/main.css
New rule: /home/user/project/build/assets/font.ttf: /home/user/project/assets/font.ttf
New rule: /home/user/project/build/assets/img.png: /home/user/project/assets/img.png
New rule: /home/user/project/build/assets/child.css: /home/user/project/assets/styles/child/child.css
New rule: /home/user/project/build/assets/main.css: /home/user/project/assets/styles/main.css
mkdir -p /home/user/project/build/assets && cp /home/user/project/assets/font.ttf /home/user/project/build/assets/font.ttf
cp /home/user/project/assets/img.png /home/user/project/build/assets/img.png
cp /home/user/project/assets/styles/child/child.css /home/user/project/build/assets/child.css
cp /home/user/project/assets/styles/main.css /home/user/project/build/assets/main.css
Environment after running this Makefile:
$ find /home/user/project
/home/user/project
/home/user/project/assets
/home/user/project/assets/font.ttf
/home/user/project/assets/img.png
/home/user/project/assets/styles
/home/user/project/assets/styles/child
/home/user/project/assets/styles/child/child.css
/home/user/project/assets/styles/main.css
/home/user/project/build
/home/user/project/build/assets
/home/user/project/build/assets/child.css
/home/user/project/build/assets/font.ttf
/home/user/project/build/assets/img.png
/home/user/project/build/assets/main.css
SRC_DIRS += $(CMM_DIR)
SRC_DIRS += $(ABC_DIR)
VPATH = $(SRC_FILES)
tear_abc123: $(OBJ)/ur23.o
$(CC) $(CFLAGS) -c $(subst tear_,,$#.c) $#.c $#_compiletime.c -o $#
In this, $#_compiletime.c (say tear_abc123_compiletime.c) gets generated before $(CC). make throws an error that abc123.c is not found. where abc123.c is present in $(ABC_DIR). Why vpath is could not find the c file.
The main problem is that VPATH works only for Prerequisites. And it does not work from the 'command' part of the target.
To make VPATH work you should specify list of sources in Prerequisites and reference it in the command using $^.
Please see the following example (for file tree as below)
.
|-- abc
| |-- abc123.c
| |-- tear_abc123.c
| `-- tear_abc123_compiletime.c
`-- Makefile
Makefile contains
SRC_DIRS := abc
VPATH := $(SRC_DIRS)
TARGET := tear_abc123
SOURCE_FILES := $(TARGET).c $(TARGET)_compiletime.c $(subst tear_,,$(TARGET).c)
tear_abc123: $(SOURCE_FILES)
#echo "SOURCE_FILES := $(SOURCE_FILES)"
#echo "prerequisites = $^"
The output will be
$ make
SOURCE_FILES := tear_abc123.c tear_abc123_compiletime.c abc123.c
prerequisites = abc/tear_abc123.c abc/tear_abc123_compiletime.c abc/abc123.c
As you can see
the SOURCE_FILES really contains only source files names
the prerequisites is automatically substituted with correct path
My test project folder structure looks like:
TOPDIR
├── a
│ └── a.c
├── b
│ └── b.c
├── c
│ └── c.mk
└── makefile
I wrote a test makefile:
MAKE_DIR = $(PWD)
MODULES := a b c
SRC_DIR := $(addprefix ${MAKE_DIR}/,$(MODULES))
BUILD_DIR := $(addprefix ${MAKE_DIR}/build/,$(MODULES))
SRC := $(foreach sdir,$(SRC_DIR),$(wildcard $(sdir)/*.c))
OBJ := $(patsubst ${SRC_DIR}/%.c,${BUILD_DIR}/%.o,$(SRC))
INCLUDES := $(addprefix -I,$(SRC_DIR))
vpath %.c $(SRC_DIR)
default:
#echo "SRC DIR: ${SRC_DIR}"
#echo "Build DIR: ${BUILD_DIR}"
#echo "Source: ${SRC}"
#echo "Obj: ${OBJ}"
#echo "Includes: ${INCLUDES}"
and it output:
[GNU-GCC]howchen#linux:~/Work/c/c/test/test_make
-> make
SRC DIR: /home/howchen/Work/c/c/test/test_make/a /home/howchen/Work/c/c/test/test_make/b /home/howchen/Work/c/c/test/test_make/c
Build DIR: /home/howchen/Work/c/c/test/test_make/build/a /home/howchen/Work/c/c/test/test_make/build/b /home/howchen/Work/c/c/test/test_make/build/c
Source: /home/howchen/Work/c/c/test/test_make/a/a.c /home/howchen/Work/c/c/test/test_make/b/b.c /home/howchen/Work/c/c/test/test_make/c/c.c
Obj: /home/howchen/Work/c/c/test/test_make/a/a.c /home/howchen/Work/c/c/test/test_make/b/b.c /home/howchen/Work/c/c/test/test_make/c/c.c
Includes: -I/home/howchen/Work/c/c/test/test_make/a -I/home/howchen/Work/c/c/test/test_make/b -I/home/howchen/Work/c/c/test/test_make/c
The ${Obj} variables are NOT on *.o format, why? any problem in my makefile?
UPDATE
regarding Magnus Reftel's help, I first try:
OBJ := $(foreach sdir,$(SRC_DIR),$(patsubst $(sdir)/%.c,$(BUILD_DIR)/%.o,$(filter $(sdir)/%.c,$(SRC))))
and it output like:
Obj:
/home/howchen/Work/c/c/test/test_make/build/a
/home/howchen/Work/c/c/test/test_make/build/b
/home/howchen/Work/c/c/test/test_make/build/c/a.o
/home/howchen/Work/c/c/test/test_make/build/a
/home/howchen/Work/c/c/test/test_make/build/b
/home/howchen/Work/c/c/test/test_make/build/c/b.o
/home/howchen/Work/c/c/test/test_make/build/a
/home/howchen/Work/c/c/test/test_make/build/b
/home/howchen/Work/c/c/test/test_make/build/c/c.o
The output contain both PATH and PATH/*.c these two things, seems still NOT correct because ALL obj files go to folder c ONLY
I think I already got the source file list, which stored in $(SRC), therefore I try:
OBJ := $(patsubst %.c,%.o, $(SRC))
and it output:
Obj: /home/howchen/Work/c/c/test/test_make/a/a.o /home/howchen/Work/c/c/test/test_make/b/b.o /home/howchen/Work/c/c/test/test_make/c/c.o
which seems correct, but not because I need locate the output obj file in my build folder not source folder.
If my first try statement is not correct, where is the problem?
If second way can be improved? which way to get $(OBJ) is best for my case?
Because SRC_DIR holds a list of directories, not just one. The pattern you're matching is therefore /home/howchen/Work/c/c/test/test_make/a /home/howchen/Work/c/c/test/test_make/b /home/howchen/Work/c/c/test/test_make/c/%.c which is surely not what you want. Try combining patsubst with the foreach function. Something along the lines of
OBJ := $(foreach dir,$(SRC_DIR),$(patsubst $(dir)/%.c,getting/the/correct/build/dir/here/is/left/as/an/excercise/to/the/reader%.o,$(filter $(dir)/%,$(SRC))))