Makefile wildcard and variable expansion - makefile

I have this Makefile (abbreviated):
COMPILE = armcc
LINK = armlink
SRCDIR := ./src1 \
./src2
INCLUDES := -I ./inc
CSRC := $(wildcard $(SRCDIR)/*.c)
# CSRC := ./src1/*.c ./src2/*.c
OBJS := $(CSRC:.c=.o)
.PHONY: clean
clean:
#echo "Clean Finished"
%.o: %.c
#echo Compiling $<
#$(COMPILE) $(INCLUDES) $< -o $#
mktest: $(OBJS) Makefile
#echo $(CSRC)
#echo $(OBJS)
#echo building mktest
#$(LINK) -o mktest.axf
When I run it the wildcard only expanded for the last entry in the SRCDIR variable, which is ./src2. The output shown below.
c:> make mktest
./src1 ./src2/file2.c
./src1 ./src2/file2.o
building mktest
If I replace the line where CSRC defined, with the line below it. It works fine, and the output shown below.
c:> make mktest
./src1/*.c ./src2/*.c
./src1/*.o ./src2/*.o
building mktest
This is OK if I only have a few sub-directories I want to include. But if I want to include more, the Makefile will become ugly. Am I not using the wildcard function properly here?

What you would need your CSRC definition to be is:
CSRC:= $(foreach dir,$(SRCDIR),$(wildcard $(dir)/*))
If you look at the documentation:
$(wildcard pattern…)
This string, used anywhere in a makefile, is replaced by a space-separated list of names of existing files that match one of the given file name patterns…
This means your original line actually reads as:
CSRC := $(wildcard src1/ src2/*.c)
That is files whose names are matching against src1/ or src2/*.c.

Related

GNU make: What does '*** missing separator. Stop' mean for a source code file?

I'm fairly new to Makefiles, and I am trying to create one for a C project I am building using gcc.
I am familiar with the error Makefile:<col>: *** missing separator. Stop. It has popped up before when I used spaces instead of tabs to precede rules in the Makefile. I just tried writing a Makefile for this particular project (being sure to use TAB character instead of spaces) and when I run the make command, I get a very nondescript error I do not understand how to fix: src/main.c:7: *** missing separator. Stop
My directory structure looks like this:
- projectfolder/
- Makefile
- bin/
- build/
- inc/
- src/
- main.c
- otherfolder/
- inc/
- common.h
- io.h
- src/
- io.c
main.c, which includes the main function, has the following imports:
#include <stdio.h>
#include <stdlib.h>
#include "common.h"
#include "io.h"
My goal is to make a bunch of .o and .d files from the files in both src/ and inc/ directories and put those into projectfolder/build/, so that they can then be used to make the final executable in projectfolder/bin/
Finally, here is the Makefile that is causing the issue:
# Compiler and linker
CC := gcc
# Target binary
TARGET := the-program
# File extensions
SRCEXT := c
# Directories
TARGETDIR := bin
BUILDDIR := build
SRCDIRS := src /home/myusername/otherfolder/src
INCDIRS := inc /home/myusername/otherfolder/inc
# gcc options
CCFLAGS := -Wall -Wextra -O3
#---------------------------------------------------------------------------------
#DO NOT EDIT BELOW THIS LINE
#---------------------------------------------------------------------------------
vpath %.$(SRCEXT) $(SRCDIRS)
vpath %.h $(INCDIRS)
CCFLAGS += $(addprefix -I,$(INCDIRS)) -MMD -MP
SRC := $(shell find $(SRCDIRS) -name *.$(SRCEXT))
OBJ := $(SRC:$(SRCDIRS)/%.$(SRCEXT)=$(BUILDDIR)/%.o)
DEP := $(OBJ:.o=.d)
TARGET := $(TARGETDIR)/$(TARGET)
# RULE: Default make
all: makedirs $(TARGET) clean
# RULE: Remake
remake: fullclean all
# RULE: Clean
clean:
rm -rf $(BUILDDIR)
# RULE: Full clean (objects and binaries)
fullclean: clean
rm -rf $(TARGETDIR)
# RULE: Make dirs
makedirs:
mkdir -p $(BUILDDIR)
mkdir -p $(TARGETDIR)
# RULE: Link
$(TARGET): $(OBJ)
$(CC) $(OBJ) -o $#
# RULE: Compile
$(OBJ): $(SRC)
$(CC) $(CCFLAGS) -c $< -o $#
# RULE: Non-file targets
.PHONY: all remake clean fullclean makedirs
# include dependencies
-include $(DEP)
What about this file would cause the described error? I am assuming that it is related to the Makefile itself rather than the C code actually contained in src/main.c since this does not appear to be a compilation error, but if I am wrong, let me know and I can change the post.
This error:
src/main.c:7: *** missing separator. Stop
is clearly being printed by make. Since we know the format of these messages is <makefile>:<linenumber>: <error>, we can see that make is trying to parse the file src/main.c as a makefile and obviously this cannot work.
How could make be doing such a thing? The obvious culprit is this line:
-include $(DEP)
If the variable DEP contains the file src/main.c, then make would try to include that as a makefile and parse it. So how is DEP set?
DEP := $(OBJ:.o=.d)
This changes all words in OBJ that end with .o to end with .d. Crucially, it should be noted that this makes no changes to words that do not end in .o. So, if OBJS contained src/main.c, this would pass it through unmodified to DEPS.
So how is OBJ set? Here are the relevant variables:
SRCDIRS := src /home/myusername/otherfolder/src
SRC := $(shell find $(SRCDIRS) -name *.$(SRCEXT))
OBJ := $(SRC:$(SRCDIRS)/%.$(SRCEXT)=$(BUILDDIR)/%.o)
Let's expand this: the SRC variable runs:
find src /home/myusername/otherfolder/src -name *.c
(you really should escape the *, either with backslash or quotes: it's very dangerous how you have this).
Now we'll say that SRC gets the value:
SRC := src/main.c /home/myusername/otherfolder/src/other.c
Now what does OBJ contain?
OBJ := $(SRC:src /home/myusername/otherfolder/src/%.$(SRCEXT)=build/%.o)
This clearly cannot work: you can't put multiple directories into SRCDIRS, but then treat it as if it contained only one word.
The problem is in the line
OBJ := $(SRC:$(SRCDIRS)/%.$(SRCEXT)=$(BUILDDIR)/%.o)
The pattern substitution works only where the pattern matches and leaves all other strings alone.
As a simple demonstration see this makefile (no rules, just variable assignments):
x := foo bar baz
y := $(x:ba%=gu%)
$(info $(x))
$(info $(y))
Output:
foo bar baz
foo gur guz
As you can see, foo still is there, unchanged although it didn't match the pattern ba%.
In your case you are passing an impossible to substitute pattern, the content of $(SRCDIRS), which is the two words: src /home/myusername/otherfolder/src to the substitution call, therefore main.c - which is either src/main.c or /home/myusername/otherfolder/src/main.c remains unchanged in your list, gets inadvertently flushed further into $(DEP) and is finally included as text with your last line -include $(DEP).
As a recommendation I want to point you to VPATH (see manual and here: https://www.cmcrossroads.com/article/basics-vpath-and-vpath), which is the better alternative to indicate the location of your sources to make.

makefile how to output to separate build directory

I have this directory structure:
makefile
src
foo.c
build
My goal is simply to build foo.c and output the build files to the build directory.
I have the following makefile:
SRCS_DIR := ./src
BUILD_DIR := ./build
SRCS := $(shell find $(SRCS_DIR) -name "*.c")
OBJS := $(subst $(SRCS_DIR),$(BUILD_DIR),$(SRCS))
OBJS := $(OBJS:.c=.o)
test.exe: $(OBJS)
gcc $(OBJS) -o $#
%.o: %.c
gcc -c $< -o $#
The problem is the pattern rule. One of the object files is build/foo.o. The problem is that %.c gets turned into build/foo.c, which doesn't exist. What I want %.c to be is src/foo.c instead, but I have no idea how to do that.
The stem of the pattern must match exactly. So if you want a pattern that will put things into a different directory, you have to modify the pattern so that the non-matching parts are not part of the stem. So you can write:
build/%.o : src/%.o
gcc -c $< -o $#
so that the % matches only the common string.

substituting folder path in $(SOURCES) of a makefile

I am looking for a way to substitute the folder on a list of source files variable within makefile.
Is there something that works here?
I start off by finding my source files
program_C_SRCS := $(wildcard $(SRCDIR)/*.c)
program_CXX_SRCS := $(wildcard $(SRCDIR)/*.cpp)
the results (if I understand GNU makefiles correctly) look typically like
src/main.cpp
src/sensor.cpp
then I build by object files
program_C_OBJS := ${program_C_SRCS:.c=.o}
program_CXX_OBJS := ${program_CXX_SRCS:.cpp=.opp}
this replaces the extensions of my source files as per expected.
Finally, I want to replace "src/" with "obj/"
program_C_OBJPATH := ${subst $(SRCDIR) , $(OBJDIR) , $(program_C_OBJS)}
program_CXX_OBJPATH := ${subst $(SRCDIR) , $(OBJDIR) , $(program_CXX_OBJS)}
However, this does not work.
I have gone through the GNU makefile website to no avail. This solution Makefile to put object files from source files different directories into a single, separate directory? comes close but the objects directory must be explicitly included everywhere and the sources directory does not include source path information.
In my makefile the list of source files include the path and I would prefer that the list of object files include the corresponding object directory, too.
The rest of the makefile tries also to use variables
linking stage
$(program_NAME): $(program_OBJS)
$(CXX) $(CFLAGS) $(CXXFLAGS) $(LDFLAGS) $(program_OBJS) -o "$(program_NAME)"
compile stage
%.opp : %.cpp | mkdirobjdir
$(CXX) $(CFLAGS) $(CXXFLAGS) $(CPPFLAGS) -c -o "$#" "$<"
and finally the rules
main_enose.opp : main_enose.cpp core_enose.cpp core_enose.h
$(OBJDIR)/core_enose.opp : core_enose.cpp core_enose.h
$(OBJDIR)/core_enose.h :
Your problem here is the spaces around the commas in the $(subst) calls. make isn't ignoring them the way you expect it might. It is seeing them as literal values in the string to find, the string to replace and the to do the replacement in.
Remove them.
program_C_OBJPATH := ${subst $(SRCDIR),$(OBJDIR),$(program_C_OBJS)}
program_CXX_OBJPATH := ${subst $(SRCDIR),$(OBJDIR),$(program_CXX_OBJS)}
That said you probably either want to use $(patsubst) to limit where the replacement happens:
program_C_OBJPATH := $(patsubst $(SRCDIR)/%,$(OBJDIR)/%,$(program_C_OBJS))
program_CXX_OBJPATH := $(patsubst $(SRCDIR)/%,$(OBJDIR)/%,$(program_CXX_OBJS))
or you want to use $(notdir) and $(addprefix) to handle stripping all the directory information and adding it back:
program_C_OBJPATH := $(addprefix $(OBJDIR)/,$(notdir $(program_C_OBJS)))
program_CXX_OBJPATH := $(addprefix $(OBJDIR)/,$(notdir $(program_CXX_OBJS)))

makefile: lint multiple source files and out put to corresponding txt files

I'm trying to run a makefile on a directory such that it runs lint on all the cpp files in that directory and save the output in multiple files with the same names as source files.
e.g. in other words I want to save lint out put for abc.cpp to abc.txt and def.cpp to def.txt and so on for all the unknown number of files to a sub-directory lintfiles.
some thing like the following:-
*lint .ALWAYS:*
--lint-nt $(PATHS) $(OPTIONS) *.cpp > ./lintfiles/%f.txt
make util is not understanding Lint's %f option. I also tried the following but it generates no output.
lintfiles/%.txt: %.cpp
-lint-nt $(PATHS) $(OPTIONS) $# $^
please suggest.
I'm not familiar with pc-lint, but if the syntax is something like
lint-nt -o abc.lint abc.cpp
then this makefile should do the job:
SRC_DIR := src # or whatever
SRCS := $(wildcard $(SRC_DIR)/*.cpp)
LINTS := $(patsubst $(SRC_DIR)/%.cpp, $(SRC_DIR)/lint/%.lint, $(SRCS))
all: $(LINTS)
$(LINTS): $(SRC_DIR)/lint/%.lint : $(SRC_DIR)/%.cpp
lint-nt $(PATHS) $(OPTIONS) -o $# $<

Makefile, Pattern-Rules and Directories

I want to write a (gmake) makefile for a compiler that - unlike gcc - puts all output files into a specific directory. Unfortunately this behavior cannot be changed.
My sources are in multiple directories. How do I write a pattern-rule that lets me compile the sources.
Okay, that's a bit unclear. Here is an example. My sources look may like this:
./folder1/foo.c
./folder2/bar.c
and the output files will end up like this:
./obj/foo.obj
./obj/bar.obj
How should my rule to compile my sources look like?
%.obj : %.c
$(COMPILER) -c $<
will not work.
Any ideas? I'd like to avoid an implicit rule for each source file...
Extracted from some Makefile of mine:
OBJS := $(sort $(patsubst %.cpp,$(OBJECT_DIRECTORY)/%.o,$(patsubst %.c,$(OBJECT_DIRECTORY)/%.o,$(notdir $(SRCS)))))
Where OBJECT_DIRECTORY points to the object directory and SRCS is the list of source files (which you can even populate using $(wildcard)).
Then in the Makefile, I have:
define define_compile_rules
$(OBJECT_DIRECTORY)/%.o: $(1)%.c
#echo " + Compiling '$$<'"
#mkdir -p $$(#D)
$(CC) $$(CFLAGS) -o $$# -c $$<
endef
$(foreach directory,$(sort $(dir $(SRCS))),$(eval $(call define_compile_rules,$(directory))))
See the $(eval) function.

Resources