Unable to find headers with make - c++

I have a (fairly simple) makefile adapted from here that I am attempting to use to build a project on Ubuntu. The project tree is fairly simple: Makefile is in the root project directory, and there are src/, include/, build/, and bin/, where source code, header files, object files, and executables are stored, respectively.
When I run make from the root directory of the project, I get the following error message:
Linking...
g++ src/Main.cpp src/Foo.cpp -o bin/runner
src/Main.cpp:1:19: fatal error: Foo.hpp: No such file or directory
#include "Foo.hpp"
^
compilation terminated.
src/Foo.cpp:1:19: fatal error: Foo.hpp: No such file or directory
#include "Foo.hpp"
^
compilation terminated.
make: *** [bin/runner] Error 1
All that's currently in the project is Main.cpp. which calls two test functions Foo() and Bar() from Foo.cpp, which references a header file Foo.hpp. Here is the makefile:
CC := g++ # This is the main compiler
SRCDIR := src # Directory for source code
BUILDDIR := build # Directory containing all object files, which are removed on "make clean"
TARGET := bin/runner # bin/runner contains the main executable for project
# bin/ contains all other executables in the project (such as tests)
SRCEXT := cpp # File extension of source code
# Look for all the source files in SRCDIR with the file extension specified above
SOURCES := $(shell find $(SRCDIR) -type f -name *.$(SRCEXT))
# Name all object files the same root name as the source files from which they came, but add a .o extension to the end
OBJECTS := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(SOURCES:.$(SRCEXT)=.o))
# The -g flag specifies that debugging information should be produced in the native format of the OS
CFLAGS := -g -Wall
# Various flags for libraries that might need to be linked
INC := -I include # Ensures that all header files (in the include/ folder) are accessible for build
# Show the components that are currently being compiled/linked
# Also, this is the main procedure for make: The TARGET is built from the objects, and
# object files are built from source
$(TARGET): $(OBJECTS)
#echo " Linking..."
#echo " $(CC) $^ -o $(TARGET)"; $(CC) $^ -o $(TARGET)
$(BUILDDIR)/%.o: $(SRCDIR)/%.$(SRCEXT)
#mkdir -p $(BUILDDIR)
#echo " $(CC) $(CFLAGS) $(INC) -c -o $# $<"; $(CC) $(CFLAGS) $(INC) -c -o $# $<
# Directives for "make clean" which cleans all object files out of the build/ folder
clean:
#echo " Cleaning...";
#echo " $(RM) -r $(BUILDDIR) $(TARGET)"; $(RM) -r $(BUILDDIR) $(TARGET)
# Destroys everything in the build/ and bin/runner/ folders. Does not clean test executables.
.PHONY: clean
What am I missing here in order to get the header files to be properly linked?
EDIT: Here is the new makefile, and the current output:
# This is the main compiler
CC := g++
# Directory for source code
SRCDIR := src
# Directory containing all object files, which are removed on "make clean"
BUILDDIR := build
# bin/runner contains the main executable for project
# bin/ contains all other executables in the project (such as tests)
TARGET := bin/runner
# File extension of source code
SRCEXT := cpp
# Ensures that all header files (in the include/ folder) are accessible for build
INC := -I/include
# Look for all the source files in SRCDIR with the file extension specified above
# SOURCES := $(shell find $(SRCDIR) -type f -name *.$(SRCEXT))
SOURCES := $(wildcard $(SRCDIR)/*.$(SRCEXT))
# Name all object files the same root name as the source files from which they came, but add a .o extension to the end
# OBJECTS := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(SOURCES:.$(SRCEXT)=.o))
OBJECTS := $(addprefix $(TARGET)/, $(notdir $(SOURCES:.cpp=.o)))
# The -g flag specifies that debugging information should be produced in the native format of the OS
CFLAGS := -g -Wall
# Various flags for libraries that might need to be linked
LIB := #-pthread -lmongoclient -L lib -lboost_thread-mt -lboost_filesystem-mt -lboost_system-mt
# Show the components that are currently being compiled/linked
# Also, this is the main procedure for make: The TARGET is built from the objects, and
# object files are built from source
$(TARGET): $(OBJECTS)
#echo " Linking..."
$(CC) $^ -o $(TARGET)
# #echo " $(CC) $^ -o $(TARGET) $(LIB)"; $(CC) $^ -o $(TARGET) $(LIB)
$(BUILDDIR)/%.o: $(SRCDIR)/%.$(SRCEXT)
#mkdir -p $(BUILDDIR)
$(CC) $(CFLAGS) $(INC) -c -o $# $<
# Directives for "make clean" which cleans all object files out of the build/ folder
clean:
#echo " Cleaning...";
#echo " $(RM) -r $(BUILDDIR) $(TARGET)"; $(RM) -r $(BUILDDIR) $(TARGET)
# Tests
# tester:
# $(CC) $(CFLAGS) test/tester.cpp $(INC) $(LIB) -o bin/tester
# Spikes
# ticket:
# $(CC) $(CFLAGS) spikes/ticket.cpp $(INC) $(LIB) -o bin/ticket
# Destroys everything in the build/ and bin/runner/ folders. Does not clean test executables.
.PHONY: clean
Output:
[scott]> make
make: *** No rule to make target `bin/runner/Foo.o', needed by `bin/runner'. Stop.

tl;dr
Don't put end-of-line comments on variable assignments in make. It doesn't work the way you might expect.
Explanation
Your makefile isn't running the steps you expect it is.
You shouldn't be seeing Linking... for the compilation step.
make shouldn't be attempting to create the target from the source .cpp files.
You should be seeing your INC and CFLAGS values on the compilation line (but you are getting linking output so obviously aren't seeing them).
That's why your header can't be found by the way, your linking line doesn't have -I on it anywhere.
The reason that's happening is because make is applying the wrong rule.
make is applying the wrong rule because your variables are being set incorrectly.
Your variables are being set incorrectly because your variables have values you don't expect.
The makefile you started from had errors the author wasn't aware of.
make is not always very smart.
When you write
FOO := some value # comment
you expect FOO to have the value some value but make sees things differently.
make gives it the value some value since it can't tell the difference between the space between some and value and the space after value and before the comment.
So when you run your shell command (with *.$(SRCEXT) unquoted) the shell just ignores the trailing spaces). (Try quoting *.'$(SRCEXT)' and see what you get.)
However when you then try to $(SOURCES:=.$(SRCEXT)=.o) make doesn't drop the spaces and you have actually written $(src/Main.cpp src/Foo.cpp:=cpp =.o) which, you may notice, is a pattern that doesn't actually match.
As a result $(OBJECTS) gets the unmodified value of $(SOURCES) and "confuses" the $(TARGET): $(OBJECTS) line later causing make to skip your compilation target.
(Oh, also, that's why your linking line has a million spaces between g++ and the first source file.)
Oh, also, you don't need to shell out for find there unless your src/ directory has sub-directories of its own (and even then not with some make magic) because $(wildcard $(SRCDIR)/*.$(SRCEXT)) will work just fine (and also would have failed earlier I believe given this problem).

Define an environment variable that has . (current working directory) first, then ./include (not just include subdirectory but as ,/include and rest of the INCLUDE dirs that you might already have because of the compiler or other software requirement)
set INCLUDE :=.:./include:$INCLUDE
Alternately, use:
INC := -I. -I./include
gcc -I option is as: -I dir
Adds the directory dir to the list of directories to be searched for header files. Directories named by '-I' are searched before the standard system include directories. If the directory dir is a standard system include directory, the option is ignored to ensure that the default search order for system directories and the special treatment of system headers are not defeated

Related

How to create a Makefile for a C++ project with multiple directories?

I want to create a Makefile for a project with the following layout:
Source files (.cpp, potentially .c) in /src, with potential subdirectories
Header files (.h, .hpp...) in /inc, with potential subdirectories
Object files (.o) in /obj, with potential subdirectories
External libraries in /lib
Compiled program in /bin
So far, I've managed to write together this Makefile, but with some issues:
SRC_DIR := src
BIN_DIR := bin
LIB_DIR := lib
INC_DIR := inc
OBJ_DIR := obj
SRCEXTS := .c .C .cc .cpp .CPP .c++ .cxx .cp
HDREXTS := .h .H .hh .hpp .HPP .h++ .hxx .hp
TARGETS := $(BIN_DIR)/program
SOURCES := $(wildcard $(addprefix $(SRC_DIR)/*,$(SRCEXTS)))
HEADERS := $(wildcard $(addprefix $(LIB_DIR)/*,$(HDREXTS)))
OBJECTS := $(addsuffix .o, $(basename $(SOURCES)))
CXX = g++
CXXFLAGS = -std=c++17 -c -g -Wall
.PHONY: all clean
all: $(TARGETS)
$(TARGETS): $(OBJECTS)
$(CXX) $^ -o $#
$(OBJ_DIR)%$(OBJECTS): $(SRC_DIR)%$(SOURCES)
$(CXX) $(CXXFLAGS) $< -o $#
clean:
rm -f $(OBJECTS) $(TARGETS)
I've tried to make it as "generic" as possible, so future projects could be started with this layout and makefile as a template. Currently, it creates the .o-files inside the src-directory alongisde the source code. It also fails when trying to compile the program with
g++ src/main.o -o bin/program
/usr/bin/ld: src/main.o: _ZSt4cout: invalid version 3 (max 0)
/usr/bin/ld: src/main.o: error adding symbols: bad value
collect2: error: ld returned 1 exit status
make: *** [Makefile:23: bin/program] Error 1
Very new to C++ development. Been on a wild goose-chase for a while, trying to get a clear image of how it all works. My code is basically a weird Frankenstein monster of several code snippets I've stumbled upon. Hopefully my intentions are clear enough, this is my last ditch effort! Thanks in advance :)
As #JohnBollinger points out, you are attempting too much at once. I will suggest a few changes to get your makefile off the ground.
I can't explain the error you get when you try to build the executable (you haven't given us enough information to reproduce the error), but it doesn't look like a Make problem. I suggest you try to build it without Make, using the command line, and see what happens.
I will assume that the names of your sources end in ".cpp" (such as src/sailboat/foo.cpp), the names of your headers end in ".hpp", and the directory tree under obj/ is already present and correct. These restrictions are temporary training wheels; you can remove them when you have more skill.
First, finding the source files. This:
SOURCES := $(wildcard $(addprefix $(SRC_DIR)/*,$(SRCEXTS)))
will not work if src/ has subdirectories. To recurse into subdirectories, we will use find. (There is a shortcut available to GNUMake, but for now we'll do things the slow and careful way).
SOURCES := $(shell find src -name "*.cpp")
Now to construct the names of the desired object files, such as obj/sailboat/foo.o. This:
OBJECTS := $(addsuffix .o, $(basename $(SOURCES)))
will give you src/sailboat/foo.o. We need a different command to change the leading directory as well as the suffix:
OBJECTS := $(patsubst src/%.cpp,obj/%.o,$(SOURCES))
Some of the source files refer to header files, so before we can start building objects, we must be able to supply them. The compiler can find the needed headers, but we must tell it where to search. So we need the directories, not the full paths:
HEADERS := $(shell find inc -name "*.hpp")
HEADERDIRS := $(sort $(dir $(HEADERS)))
(The sort is just to remove duplicates. Not necessary, but tidy.)
Now the rule to build the objects. This is incorrect:
$(OBJ_DIR)%$(OBJECTS): $(SRC_DIR)%$(SOURCES)
$(CXX) $(CXXFLAGS) $< -o $#
Remember that OBJECTS can contain several space-separated words. So if it contains foo bar, the target will be obj/%foo bar, which is clearly not what you intended. Likewise the prerequisite list is wrong, and the recipe too. Junk it and start over.
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.cpp
$(CXX) $< -c -o $#
Then remember the header files, and add flags to tell the compiler where to look for them:
INCLUDEFLAGS := $(addprefix -I,$(HEADERDIRS))
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.cpp
$(CXX) $< -c $(INCLUDEFLAGS) -o $#
That should be enough to get your makefile working; further refinements can wait.
I'm not mastering makefiles but one thing for sure : target "all" is definitively not a PHONY target because it often depends on other targets (in fact, it always depends on other targets, by definition !).
Also, as said John Bollinger, it is obviously possible to have only one makefile at the root.
"all" target is not mandatory : make will look for the first target fisrt.
The folder structure is like this :
----root
----src
----inc
----obj
----lib
Here's a simple one of mine (rules to build static libraries have been commented out) :
CC=g++
SRCDIR=./src/
INCDIR=./inc/
INCFLAG=-I$(INCDIR)
OBJDIR=./obj/
LIB=./lib/
LIBFLAG=-lstdc++
#-lmystaticlibrary -lmyclasses
#$(LIBFLAG) -L$(LIB)
#STATICLIBRARIES= mystaticlibrary myclasses
OBJECTS = $(OBJDIR)apprendre.o $(OBJDIR)myfunctions.o $(OBJDIR)myclasses.o
apprendre: $(OBJECTS)
# echo
# echo --------------------- Edition des liens
$(CC) $(OBJECTS) -o $# $(INCFLAG)
# mystaticlibrary: mystaticlibrary.o
# # echo
# # echo --------------------- Compilation librairie statique $#
# ar cr $(LIB)lib$#.a $(OBJ)$#.o
# ranlib $(LIB)lib$#.a
# myclasses: myclasses.o
# # echo
# # echo --------------------- Compilation librairie statique $#
# ar cr $(LIB)lib$#.a $(OBJ)$#.o
# ranlib $(LIB)lib$#.a
$(OBJDIR)%.o: $(SRCDIR)%.cpp $(INCDIR)myfunctions.h $(INCDIR)myclasses.h
# echo
# echo --------------------- Compilation $<
$(CC) -c $< $(INCFLAG) -o $#
run:
# echo
# echo "-----------------------> GO !!! : apprendre"
# ./apprendre

fatal error: jni.h: No such file or directory. though I have included correct folder

I am trying to build a so file in my ubuntu 20.04 machine. I have created a Makefile to do the task. The content of the Makefile is given below
#
# 'make depend' uses makedepend to automatically generate dependencies
# (dependencies are added to end of Makefile)
# 'make' build so file 'libwebrtc-aec.so'
# 'make clean' removes all .o and so files
#
JAVA_HOME="/opt/jdk/jdk1.8.0_291"
# define the C++ compiler to use
CC = g++
# define any compile-time flags
CFLAGS = -Wall -g
# define any directories containing header files other than /usr/include
#
INCLUDES = -I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/linux
# define library paths in addition to /usr/lib
# if I wanted to include libraries not in /usr/lib I'd specify
# their path using -Lpath, something like:
#LFLAGS = -L/home/newhall/lib -L../lib
# define any libraries to link into executable:
# if I want to link in libraries (libx.so or libx.a) I use the -llibname
# option, something like (this will link in libmylib.so and libm.so:
#LIBS = -lmylib -lm
LIBS = -lm
# define the C source files
# $(wildcard *.cpp /xxx/xxx/*.cpp): get all .cpp files from the current directory and dir "/xxx/xxx/"
SRCS := $(wildcard *.cpp)
# define the C object files
#
# This uses Suffix Replacement within a macro:
# $(name:string1=string2)
# For each word in 'name' replace 'string1' with 'string2'
# Below we are replacing the suffix .c of all words in the macro SRCS
# with the .o suffix
#
# $(patsubst %.cpp,%.o,$(SRCS)): substitute all ".cpp" file name strings to ".o" file name strings
OBJS := $(patsubst %.cpp,%.o,$(SRCS))
# define the executable file
MAIN = libwebrtc-aec
#
# The following part of the makefile is generic; it can be used to
# build any executable just by changing the definitions above and by
# deleting dependencies appended to the file from 'make depend'
#
.PHONY: depend clean
all: $(MAIN)
#echo Simple compiler named libwebrtc-ace has been compiled
$(MAIN): $(OBJS)
$(CC) $(CFLAGS) $(INCLUDES) -shared -o $(MAIN).so $(OBJS) $(LIBS)
# $(CC) $(CFLAGS) $(INCLUDES) -o $(MAIN) $(OBJS) $(LFLAGS) $(LIBS)
# this is a suffix replacement rule for building .o's from .c's
# it uses automatic variables $<: the name of the prerequisite of
# the rule(a .c file) and $#: the name of the target of the rule (a .o file)
# (see the gnu make manual section about automatic variables)
#.c++.o:
# $(CC) $(CFLAGS) $(INCLUDES) -c $< -o $#
clean:
$(RM) *.o *~ $(MAIN).so
depend: $(SRCS)
makedepend $(INCLUDES) $^
# DO NOT DELETE THIS LINE -- make depend needs it
I can assure that my JAVA_HOME variable is pointing to correct folder and I have manually checked and found jni.h file is present in java home include directory. Still I am getting error like below
fatal error: jni.h: No such file or directory
2 | #include <jni.h>
| ^~~~~~~
compilation terminated.
I am not sure what did I do wrong. Need help from someone here.
You are using the .cpp.o implicit rule, which is (roughly) defined as follows:
.cpp.o:
$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c $<
Your Makefile never sets CXXFLAGS or CPPFLAGS, so your INCLUDES variable is not used when compiling .cpp files. To fix it, do:
CXXFLAGS = $(INCLUDES)

Makefile working in Linux but not under Windows, can't find files in subdirectory

I have a makefile (gnu make) working under linux, but when porting it to windows it does not work. The goal of the makefile is to make all the *.cpp file that reside in different subdirectories and compile them into *.obj files in a single BUILD_DIR.
Between linux and windows I only adjusted the SOURCES variable, the linux line is shown but commented. When I check all the names and directories they show the same (relative paths in their perspective notation) and what I expect.
The "error message" I get on windows is: make: *** No rule to make target 'DEM.cpp', needed by 'DEM.obj'. Stop.
In debug mode it says: File 'DEM.cpp' does not exist. (which it obviously does)
On linux it finds the file via the VPATH, in debug mode it says: No need to remake target 'DEM.cpp'; using VPATH name './Code/DEM.cpp'.
The subdirectory structure in both linux and windows is the same and the makefile is run from the same location.
Question: What is going wrong here, I have the feeling it has something to do with VPATH being handled differently on windows but I am not certain.
My makefile is:
# MAKEFILE
# Some makefile settings
SHELL = /bin/sh #Make sure the shell is the standard shell
.SUFFIXES: #Clear al implicit suffixes
.SUFFIXES: .cpp .obj #Set used suffixes
# Variable Declaration
CXX := g++
BUILD_DIR = .\bin\Release
PROGRAM_NAME := DEM.exe
#SOURCES := $(shell find . -name '*.cpp') #All *.cpp files with directory (LINUX style)
SOURCES := $(shell FORFILES /S /M *.cpp /C "CMD /C ECHO #relpath") #All *.cpp files with directory
NAMES := $(notdir $(basename $(SOURCES))) #Get all files names of the *.cpp files without extensions
OBJECTS := $(addsuffix .obj, $(NAMES)) #Get the to be generate *.o files without directory
SRC_DIRS := $(dir $(SOURCES)) #All the directory in which the sources are found
VPATH := $(SRC_DIRS) $(BUILD_DIR)
.PHONY: all
all: build #Standard entry point, run release
#echo "BUILD DONE"
.PHONY: build
build: $(PROGRAM_NAME)
#echo "BUILD DONE"
$(PROGRAM_NAME): $(OBJECTS)
#echo "Link executable"
$(CXX) -o $(BUILD_DIR)/$(PROGRAM_NAME) $(addprefix $(BUILD_DIR)/,$(OBJECTS))
$(OBJECTS): %.obj: %.cpp
#echo "Compile into object code: $<"
${CXX} $(CXXFLAGS) -c $< -o $(BUILD_DIR)/$# #Compiles object code and places it in the $(BUILD_DIR)
Update 1: Based on the comment of some programmer dude, I ran it with -p, and got the following interesting result:
# Not a target:
DEM.cpp:
# Implicit rule search has been done.
# File does not exist.
# File has not been updated.
# Not a target:
World.cpp:
# Implicit rule search has not been done.
# Modification time never checked.
# File has not been updated.
# Lot more targets below
It seems only to not find DEM.cpp but it finds everything else.
DEM.cpp resides in C:\Users\dklomp\Documents\Programming\C++ Source\DEM\DEM\Code, but should resolve to .\Code\
Most other files reside in subdirectories in Code.
But stdafx.cpp also resides in .\Code\ and there is no problem finding it.
Can it be a name clash with the directory name DEM being similar to DEM.cpp
Update 2: For information and closure.
I had already check the VPATH by reading the variable by printing it this seemed to give the correct information. However if i read it with #(info $(VPATH)) it seemet empty:
#echo "VPATH print variable: "
#echo "$(VPATH)"
#echo "VPATH print info: "
$(info VPATH)
gave:
"VPATH print variable: "
"./Code/InputOutput/ ./Code/InputOutput/ ./Code/InputOutput/
./Code/InputOutput/ ./Code/ ./Code/ ./Code/Models/ ./Code/Models/
./Code/Forces/ ./Code/Forces/ ./Code/Forces/ ./Code/Forces/ ./Code/Forces/
./Code/Forces/ ./Code/Forces/ ./Code/Forces/ ./Code/Forces/ ./Code/Tools/
./Code/Tools/ ./Code/Tools/ ./Code/Tools/ ./Code/Solvers/ ./Code/Solvers/
./Code/Solvers/ ./Code/World/ ./Code/World/ ./Code/World/ ./Code/World/
./Code/World/ ./Code/Interactions/ ./Code/Interactions/ ./Code/Interactions/
./Code/Interactions/ ./Code/Interactions/ ./Code/Interactions/
bin\Release"
"VPATH print info: "
Indeed with a lot of repetitions (will use the sort suggestion of hardcoreHenry) but it seemed oke. However the VPATH info is empty for some reason.
However implementing the proposed solution by code_fodder and removing all the inline code comments works. The inline code comments on the variable declaration do not seem to matter, those in the rules section do for windows, linux seems to handle inline code comments fine everywhere.
As usually thanks for the help and suggestions.
I tested this in a path that had spaces before the current location (e.g. like d:\path with spaces\myfolder\sub_folder1\sub_folder2\).
I think there is some issue with the FORFILES syntax/output - at least I could not get it to work. As soon as I used the more generic rwildcard from second answer from here it started to work quite well. Then I added rules to create/clean your output folder.
Also I removed any inline comments, since I am not 100% sure they are valid because they leave space characters everywhere and makefile is not very tolerant of that... I.E. make-sure you don't leave any trailing white space on any lines.
Also - this all assumes its gnu-make and not nmake
# MAKEFILE
# Some makefile settings
SHELL = /bin/sh
.SUFFIXES:
.SUFFIXES: .cpp .obj
# Variable Declaration
CXX := g++
BUILD_DIR = .\bin\Release
PROGRAM_NAME := DEM.exe
# Define a recursive wild card function that is portable.
rwildcard=$(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2) $(filter $(subst *,%,$2),$d))
#SOURCES := $(shell FORFILES /S /M *.cpp /C "CMD /C ECHO #relpath")
SOURCES := $(call rwildcard, ./, *.cpp)
$(info SOURCES: $(SOURCES))
NAMES := $(notdir $(basename $(SOURCES)))
OBJECTS := $(addsuffix .obj, $(NAMES))
SRC_DIRS := $(dir $(SOURCES))
VPATH := $(SRC_DIRS) $(BUILD_DIR)
.PHONY: all
all: create_dirs build
#echo "BUILD DONE"
# Has an order dependency on create_dirs to ensure that happens first, and so
# it works with parallel build (e.g. `make -j4`)
.PHONY: build
build: $(PROGRAM_NAME) | create_dirs
#echo "BUILD DONE"
$(PROGRAM_NAME): $(OBJECTS)
#echo "Link executable"
$(CXX) -o $(BUILD_DIR)/$(PROGRAM_NAME) $(addprefix $(BUILD_DIR)/,$(OBJECTS))
$(OBJECTS): %.obj: %.cpp
#echo "Compile into object code: $<"
${CXX} $(CXXFLAGS) -c $< -o $(BUILD_DIR)/$#
.PHONY: create_dirs
create_dirs:
#echo "creating dirs"
mkdir -p $(BUILD_DIR)
.PHONY: clean
clean:
#echo "cleaning"
rm -rf $(BUILD_DIR)

Make and c++ files, "includes" in the cpp

This is my current makefile:
# Compiler #
CXX = g++
DEBUG = -g
LFLAGS =
CXXFLAGS = -Wall
# Directories #
SRCDIR = src/
INCDIR = include/
BUILDDIR = build/
BINDIR = bin/
# Objects #
OBJ_NAMES = main.o dfa.o dfaException.o state.o
OBJS = $(addprefix $(BUILDDIR), $(OBJ_NAMES))
# Output #
NAME = pract3
TARGET = $(BINDIR)pract3
# Clean #
ifeq ($(OS),Windows_NT)
RM = del /q /s $(BUILDDIR:/=\)*.o $(BINDIR:/=\)$(NAME)*
else
RM = rm -rf $(BUILDDIR)*.o $(TARGET)*
endif
# Files #
$(TARGET): $(OBJS)
$(CXX) $(CXXFLAGS) $(LFLAGS) $(OBJS) -o $(TARGET)
$(BUILDDIR)%.o: $(SRCDIR)%.cpp
$(CXX) $(CXXFLAGS) $(LFLAGS) -c $< -o $#
clean:
$(RM)
And this is my project tree:
Porject/
bin/
build/
doc/
...
include/
connection.hpp
dfa.hpp
dfaException.hpp
state.hpp
src/
dfa.cpp
dfaException.cpp
main.cpp
state.cpp
Makefile
Readme
Now, I'm having three "problems".
First I want my makefile to create the bin and build directories just in case they aren't. I guess I just have to use:
mkdir $(BUILDDIR)
mkdir $(BINDIR)
But where should I put them ? And also, how can I prevent mkdir and rm (or del in windows) messages like "Cant find ..." or "x directory already exists ... "
Second, I'm guessing I can read the objects name from src/ (converting the .cpp to .o), but how can I read the file names from a directory ?
Last, I have one template class: connection.hpp (all functions are in the header). This file is included in state.hpp using: #include "../include/connection.hpp". I ran make one time with all correct and then I purposely make a syntax error in connection.hpp. Then I ran make again, but It only compiled the target file using the .o files in build without any error. Everytime I want to edit connection.hpp I have to use make clean and then make. Is there any better way to do this ?
If you need a directory to exist before you can proceed, simply put
mkdir -p ${DIRECTORY}
before you need it in your rule. If the directory already exists, mkdir -p will happily do nothing.
Likewise, if you use rm -f FILE, it should not complain if FILE does not exist in the first place.
There is no portable way to create a variable that holds the names of all files in a directory. But you are already using GNU Make features anyway, so you can just use
SOURCES = $(wildcard ${SRCDIR}/*.cpp)
and then
OBJECTS = $(SOURCES:.cpp=.o)
to transform them into object file names. You'll probably want to replace the leading directory name as well, I guess.
You didn't list any *.hpp files as prerequisites in your make file. You can either manually add them like
foo.o: foo.cpp bar.hpp baz.hpp
but that becomes unpleasant very quickly. Another trick is to use the compiler to tell you the headers (transitively) #included by a file. If you are using GCC, you can run
gcc -MM foo.cpp
to have it output above make-file snippet. You can put a pattern rule like the following
%.deps: %.cpp
${CXX} -MM ${CPPFLAGS} $< > $#
into your make-file and then include the generated *.deps files.
include $(SOURCES:.cpp=.deps)
GNU Make will be smart enough to first parse the make-file, recognize that the *.deps files don't exist and therefore cannot be included but figure out that there is a rule to generate them. So it will execute that rule and then continue parsing the make-file.
I have learned that trick from Peter Miller's great article Recursive Make Considered Harmful which is a good read if you want to learn how to write good make-files.

How do I write a smart makefile?

I'm new to linux and new to makefiles so I'm a little stumped at this point. I've been staring the make manual for an hour now and figured I would just ask:
I want to create a makefile for a relatively small project, whose source files are in proj/src/, and within subdirectories of that location. I have written a makefile at proj/makefile and want to use it to gather up all the source files, find their dependencies, and compile the result into proj/build/. Here is what I've written:
# here are my files and directories (there are also header files that are not
# listed here, but these are referred to from within the .cpp files with
# respect to the proj/src directory)
# proj/makefile
# proj/src/
# proj/src/main.cpp
# proj/src/dir1/
# proj/src/dir1/source1.cpp
# proj/src/dir2/
# proj/src/dir2/source2.cpp
# proj/build/
srcDir = src/
buildDir = build/
# This is a list of all the object files (can I get this programatically?)
objects = main.o source1.o source2.o
all: prog
# Here I want to compile all source (.cpp) files from src/ and all of its subdirectories, and to find the dependencies I want to call g++ -MM to automatically generate the list:
%.o: %.cpp
$(CXX) $(CXXFLAGS) -MM -I $(srcDir)
clean:
rm -rf *o $(buildDir)/prog
This is far from functional but I wanted to give it ago before asking. Running this gives
make: * No rule to make target main.o', needed byprog'. Stop.
So presumably my attempt at automatically writing the object files failed. If someone could give me some direction that would be great, I'm hoping that between the comments and code you can make out the intention.
EDIT: I have now tried the following:
SHELL := /bin/bash
srcDir = src
buildDir = bin
sources := $(shell find $(srcDir) -name *.cpp)
objects := $(sources:%.cpp=%.o)
-include $(sources:%.cpp=%.d)
# This is a list of all the object files (can I get this programatically?)
all: prog
prog: $(objects)
g++ $(objects) -o $(buildDir)/prog
# Here I want to compile all source (.cpp) files from src/ and all of its subdirectories, and to find the dependencies I want to call g++ -MM to automatically generate the list:
%.o: %.cpp
$(CXX) $(CXXFLAGS) -MMD -MP -c -I $(srcDir)
clean:
rm -rf *o $(buildDir)/hello
But when I run this I get the following error:
g++ -MMD -MP -I src
g++: fatal error: no input files
compilation terminated.
make: *** [src/core/Cell.o] Error 1
It wants to build main.o from main.cpp, but there is no main.cpp at the top level. You could specify the full paths of the object files, if they are to be built alongside the source files:
objects = $(srcDir)/main.o $(srcDir)/dir1/source1.o $(srcDir)/dir2/source2.o
can I get this programatically?
I do it using gnumake's shell function:
SHELL := /bin/bash
sources := $(shell find $(srcDir) -name *.cpp)
objects := $(sources:%.cpp=%.o)
to find the dependencies I want to call g++ -MM to automatically generate the list
Use -MMD to generate dependency files as well as compiling. It's also a good idea to specify -MP here - that makes sure the dependencies are correctly updated when you remove a header. Given the variables defined above, you can include the dependency files with
-include $(sources:%.cpp=%.d)