Writing makefile for source files across different directories - makefile

I am stuck in writing a Makefile when my source code files are across different directories.
The directory structure is as follows :
I have my source files (.cc) in the folders FOLDER1 and FOLDER2 and the header files are in folder named INCLUDE. My makefile is present in FOLDER1.
program_NAME := myprogram
program_C_SRCS := $(wildcard *.cc)
program_C_OBJS := ${program_C_SRCS:.cc=.o}
program_OBJS := $(program_C_OBJS)
program_INCLUDE_DIRS := ../INCLUDE
program_LIBRARY_DIRS :=
program_LIBRARIES :=
CPPFLAGS += $(foreach includedir,$(program_INCLUDE_DIRS),-I$(includedir))
LDFLAGS += $(foreach librarydir,$(program_LIBRARY_DIRS),-L$(librarydir))
LDFLAGS += $(foreach library,$(program_LIBRARIES),-l$(library))
.PHONY: all clean distclean
all: $(program_NAME)
$(program_NAME): $(program_OBJS)
$(LINK.cc) $(program_OBJS) -o $(program_NAME)
clean:
#- $(RM) $(program_NAME)
#- $(RM) $(program_OBJS)
distclean: clean
Here if I keep all my source (.cc) files in FOLDER1 then it works but on moving some files to FOLDER2 it gives errors of undefined reference.
Please help me understand how to modify my makefile so that I can keep all my header files in one directory say INCLUDE and distribute my source files across different directories.
Thanks !!!

The problem is, that
program_C_SRCS := $(wildcard *.cc)
only adds source files in the same directory. So when linking you don't have the object files of your second folder. You probably can solve the problem by simply adding the source files of the other folder to your program_C_SRCS:
program_C_SRCS += $(wildcard ../FOLDER2/*.cc)
Include directorys
I guess you are using the gcc/g++, in this case http://gcc.gnu.org/onlinedocs/cpp/Search-Path.html says, that header paths are searched from left to right, meaning the first path given is searched first. Therefore you only have to add -I INCLUDE2 before -I INCLUDE1 option to achive what you want.
Your starting makefile already has some transformation for more then one include path build in:
CPPFLAGS += $(foreach includedir,$(program_INCLUDE_DIRS),-I$(includedir))
So you only have to rewrite your include paths:
program_INCLUDE_DIRS := ../INCLUDE2
program_INCLUDE_DIRS += ../INCLUDE1

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.

How to compile files that reside in different directory in Makefile?

I have seen this questions asked before but was not able to decipher those answers.
Lets say I reside in working directory, lets call it proj and this proj directory contains src folder which contains all the *.cpp files. I want to compile those file staying on the proj directory because in future I will be creating bin directory and placing the *.o and binary in bin.
So my proj directory currently contains : Makefile and src
What I have done so far is :
SOURCE = src
# This gives the path to the proj directory
CURRENT_DIR = $(shell pwd)
# This gives list of all the *.cpp files
SRC = $(shell cd $(SOURCE) && echo *.cpp)
# Here all the name of the files stored in SRC are converted from *.cpp to *.o
OBJS = $(SRC:.cpp=.o)
.PHONY: all
all: $(TARGE)
# use the content of SRC to compile
$(TARGET): $(OBJS)
$(info $(OBJS))
$(OBJS): $(SRC)
$(CC) $(FLAGS) -c $?
When I try to run the make command it says
make: *** No rule to make target 'xxx.cpp', needed by 'xxx.o'. Stop
Now I know what it is trying to say. It gives error because although it knows the name of the file, since the file is not in the current directory makefile does not know about src folder and hence have no clue about the *.cpp files.
So my question is: Is there any macros or trick to use in makefile to make sure makefile see the xxx.cpp in src folder while staying in the current directory( I don't want to specify the folder by hand here)?

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

How to define a pattern which is valid for all files in the directory including subdirectories?

I'm writing a pattern for compiling all .c file in the test directory.
Details of the directory is as follows:
./prj/makefile
./prj/test
./prj/test/test1/a.c
./prj/test/test1/b.c
./prj/test/test2/c.c
./prj/test/test2/d.c
./prj/test/e.c
...
Just a demo. This is my content of makefile:
# Find all files
rwildcard := $(wildcard $1$2) $(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2))
# All .c files
SRC_FILES := $(call rwildcard,test,*.c)
# All .o files
OBJ_FILES := $(SRC_FILES:.o=.c)
all : $(OBJ_FILES)
echo $(OBJ_FILES)
%.o : %.c
echo $# $<
Make prints No rule to make target '...'. I think make need know path of .o files and .c files. But I don't know how to setting the path, Since there is so many .c files in my prj.
Because OBJ_FILES has includes all .o files. Then I guess the pattern should be like this:
$(output_dir)/%.o : $(input_dir)/%.c
echo $# $
Since here may have many directories in ./prj/test, I cann't hardcoded it in makefile
Thanks for another friend, the above approach is right. since % can match many Multi-level directories。
We can't really solve your problem because you still have not specified where the object files should go. All in a specific directory? Always in the parent directory of the source file? Somewhere else?
Regardless of how you resolve that, you can add all your source directories to VPATH and have Make resolve the precise location while figuring out the dependencies.
VPATH=test:test/test1:test/test2
experiment: a.c d.c
echo $^
will echo test/test1/a.c test/test2/d.c with full paths.
So with this you can remove the hard-coded directory names from your %.o rule and simply specify which .o files you want built for the all target.
You can use this to get all c files in subdirectories:
$(wildcard */*.c)
Or also this to get all c files in subdirectories at any depth:
$(wildcard **/*.c)

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 $# $<

Resources