patsubst did not translate *.c to *.o - makefile

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

Related

GNU make path substitution (directory flattening)

I have a makefile that gives me the source files in a hierarchy.
SRCS := $(wildcard $(SRC_DIR)/*.c $(SRC_DIR)/*/*.c)
gives me
./src/main.c ./src/add/add.c ./src/sub/sub.c
I want to flatten the object files into a single "obj" directory.
Of course
OBJS := $(SRCS:$(SRC_DIR)/%.c=$(OBJ_DIR)/%.obj)
gives me
./obj/main.obj ./obj/add/add.obj ./obj/sub/sub.obj
instead of desired
./obj/main.obj ./obj/add.obj ./obj/sub.obj
Question: How do I get rid of additional source directory levels?
Never had to use complex substitution so far. My intuitive try with additional "/%" in pattern
# won't work as expected:
OBJS := $(SRCS:$(SRC_DIR)/%/%.c=$(OBJ_DIR)/%.obj)
produces no meaningful result (${OBJS} becomes same as ${SRCS}).
All examples I found so far only have single occurance of "%" in match pattern.
MAK_DIR := $(dir $(lastword $(MAKEFILE_LIST)))
SRC_DIR = $(MAK_DIR)./src
OBJ_DIR = $(MAK_DIR)./obj
# gives: ./src/main.c ./src/add/add.c ./src/sub/sub.c
SRCS := $(wildcard $(SRC_DIR)/*.c $(SRC_DIR)/*/*.c)
# gives: ./obj/main.obj ./obj/add/add.obj ./obj/sub/sub.obj
OBJS := $(SRCS:$(SRC_DIR)/%.c=$(OBJ_DIR)/%.obj)
.PHONY : all
all :
#echo $(SRCS)
#echo $(OBJS)
Just use the notdir function, and also patsubst, more powerful (and that I find easier to understand than the shorthand):
OBJS := $(patsubst %.c,$(OBJ_DIR)/%.obj,$(notdir $(SRCS)))
But it is not the whole story because later on you will probably want to do something like:
$(OBJ_DIR)/%.obj: $(SRC_DIR)/%.c
which will not work any more. Not mentioning the fact that you could have several source files with the same name in different directories. Assuming you do not have such names conflicts you can generate all your dependencies using foreach-eval-call:
MAK_DIR := $(dir $(lastword $(MAKEFILE_LIST)))
SRC_DIR := $(MAK_DIR)./src
OBJ_DIR := $(MAK_DIR)./obj
SRCS := $(shell find $(MAK_DIR) -type f -name '*.c')
OBJS := $(patsubst %.c,$(OBJ_DIR)/%.obj,$(notdir $(SRCS)))
.PHONY: objs
objs: $(OBJS)
# $(1): source file
define DEP_rule
$(1)-obj := $$(patsubst %.c,$$(OBJ_DIR)/%.obj,$$(notdir $(1)))
$(1)-dep := $$(patsubst %.c,$$(OBJ_DIR)/%.d,$$(notdir $(1)))
$$($(1)-obj): $(1) $$($(1)-dep)
endef
$(foreach src,$(SRCS),$(eval $(call DEP_rule,$(src))))
Just remember that the DEP_rule macro gets expanded twice, thus the $$.

Makefile with subdirectories with sequential numbering

I have, in the main repository some subdirectories called assignment_1, assignment_2, ..., assignment_n.
I'm tring to write a Makefile that compiles the all TeX files inside those subdirectories.
This is what I have so far, but it doesn't work:
.PHONY: papers clean
PUBLISH_DIR := publish
TEX_DIR := .tex
SRC_DIR := assignment_$(wildcard *)
SRC_FILES := $(wildcard $(SRC_DIR)/*.tex)
CC := xelatex
FLAGS := -shell-escape -output-directory=$(TEX_DIR)
all: $(patsubst $(SRC_DIR)/%.tex, $(PUBLISH_DIR)/%.pdf, $(SRC_FILES))
$(PUBLISH_DIR)/%.pdf: $(SRC_DIR)/%.tex
mkdir -p $(TEX_DIR)
$(CC) $(FLAGS) $<
$(CC) $(FLAGS) $<
mkdir -p $(PUBLISH_DIR)
mv $(TEX_DIR)/*.pdf $(PUBLISH_DIR)/
clean:
rm -rf $(PUBLISH_DIR) $(TEX_DIR)
If I change this line
SRC_DIR := assignment_$(wildcard *)
with
SRC_DIR := assignment_1
it works beautifully but (obviously) only with the TeX file inside assignment_1.
Beside traversing the subdirectories, is there anything else I can improove in this Makefile?
I think you should modify your wildcard:
SRC_DIR := assignment_$(wildcard *)
to
SRC_DIR := $(wildcard assignment_*)
If $(wildcard *) expands to 1 2 3 then assignment_$(wildcard *) will expand to assignment_1 2 3 which is clearly not what you want.
Try this:
SRC_DIR := $(addprefix assignment_,$(wildcard *))
to add the assignment_ prefix to the start of each word.

makefile to zip all folders in current directory

I am trying to write a makefile which will compress (zip) all the folders in my current working directory. What I got so far is:
SUBDIRS := $(wildcard */)
ZIPS := $(addsuffix .zip,$(subst /,,$(SUBDIRS)))
$(ZIPS) : %.zip : | %
zip $# $*/*
dist: $(ZIPS)
(from Makefile: Generating zip files of all sub folders)
this does however only generate a zip file of the first folder in my current working directory (I would like to generate zips of all).
Just had to add all: $(ZIPS) as follows:
print-% : ; #echo $* = $($*)
SUBDIRS := $(wildcard */)
ZIPS := $(addsuffix .zip,$(subst /,,$(SUBDIRS)))
all: $(ZIPS)
$(ZIPS) : %.zip : | %
zip $# $*/*
dist: $(ZIPS)

will $# work with vpath

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

For Loop in GNU Makefile -- Gather all Object Files into one Variable Across Mutliple Directories

The general idea of what I am trying to be accomplished can hopefully be summed up by this small script.
DIRS = dir1 dir2 dir3 dir4 ...
OBJS =
all: GENERATE_OBJECT_FILES
GENERATE_OBJECT_FILES:
for curr_dir in $(DIRS); \
do \
$(join $(OBJS), `ls $${curr_dir}/*.o`); \
done
echo $(OBJS);
How could I accomplish this with a script within a Makefile?
I'd do it this way:
DIRS = dir1 dir2 dir3 dir4 ...
OBJS = $(wildcard $(DIRS:=/*.o))
GENERATE_OBJECT_FILES:
#echo $(OBJS);
I would use wildcard function outside the recipe, like this:
DIRS := dir1 dir2 dir3 dir4 ...
OBJS := $(foreach dir,$(DIRS),$(wildcard $(dir)/*.o))
all : $(OBJS)
#echo $^

Resources