Object files generation and best practices for linking using makefiles - C++ - c++

Background
I am just getting started with C++ programming on LINUX. In my last question, I asked about best practices of using makefiles for a big application. "SO" users suggested to read Miller's paper on recursive makefiles and avoid makefile recursion (I was using recursive makefiles).
I have followed miller and created a makefile like the below. Following is the project structure
root
...makefile
...main.cpp
...foo
......foo.cpp
......foo.h
......module.mk
My makefile looks like the below
#Main makefile which does the build
CFLAGS =
CC = g++
PROG = fooexe
#each module will append the source files to here
SRC :=
#including the description
include foo/module.mk
OBJ := $(patsubst %.cpp, %.o, $(filter %.cpp,$(SRC))) main.o
#linking the program
fooexe: $(OBJ)
$(CC) -o $(PROG) $(OBJ)
%.o:
$(CC) -c $(SRC)
main.o:
$(CC) -c main.cpp
depend:
makedepend -- $(CFLAGS) -- $(SRC)
.PHONY:clean
clean:
rm -f *.o
Here is the module.mk in foo directory.
SRC += foo/foo.cpp
When I run make -n, I get the following output.
g++ -c foo/foo.cpp
g++ -c main.cpp
g++ -o fooexe foo/foo.o main.o
Questions
Where should I create the object(.o) files? All object files in a single directory or each object files in it's own modules directory? I mean which is the best place to generate foo.o? Is it in foo directory or the root (My example generates in the root)?
In the provided example, g++ -c foo/foo.cpp command generates the .o file in the root directory. But when linking(g++ -o fooexe foo/foo.o main.o) it is looking for the foo/foo.o. How can I correct this?
Any help would be great

Where should I create the object(.o) files? All object files in a single directory or each object files in it's own modules directory? I mean which is the best place to generate foo.o? Is it in foo directory or the root (My example generates in the root)?
I find it easier for investigating failed builds to localize object files in a separate directory under the module level directory.
foo
|_ build
|_ src
Depending on the size of the project, these object files are grouped to form a component at a higher level and so on. All components go to a main build directory which is where the main application can be run from (has all dependent libraries etc).
In the provided example, g++ -c foo/foo.cpp command generates the .o file in the root directory. But when linking(g++ -o fooexe foo/foo.o main.o) it is looking for the foo/foo.o. How can I correct this?
Use:
g++ -o fooexe foo.o main.o

+1 for SCons.
I am using SCons, too. It scans the dependencies for you and it only rebuilds when source has changed as it uses cryptographic hash sums instead of timestamps.
In my SCons build the objects live in parallel directories to the source (to enable multiple builds like combinations of 32bit and 64bit, release and debug):
src
.build
linux
i686
debug
release
x86_64
debug
release

With regards to object and other generated interim files, I put these in a directory completely separate from the sources (I.e. under a directory that is excluded from backup and revision control). It may be slightly more bother to setup in projects or makefiles, but it saves time packaging up sources, and it is easier to have clean backups and revision control.
I create a subdirectory structure for the object files that matches the subdirectory structure for sources. Typically I have a separate subdirectory for each of my libraries and programs.
Additionally I also use multiple compilers (and versions) and multiple operating systems, so I will reproduce the object file directory structure under a directory for each of these compilers (which have newer versions of the standard and vendor libraries) to prevent object files with mismatched included header file versions.

The best thing you can do for yourself is to use something better than Make. SCons is my tool of choice on POSIX systems. Boost also has a build tool that is very flexible, but I had a hard time wrapping my head around it.
Oh, and if you want to use make, go ahead and build recursive makefiles. It really isn't that big a deal. I worked on a gigantic project using tons of recursive makefiles over the last three years, and it worked just fine.

Related

Compiling larger C++ projects in VSCode

Im trying to compile a C++ project using MinGW and can compile a simple main.cpp file with hello world without problems using g++ main.cpp -o main and also with external libraries using main.cpp extlib.cpp -o main.
But say im working on a rather large project with 10s of .cpp files organised inside of different files, how can I get the compiler to find all the cpp files that are needed? I know i can use main.cpp libs/*.cpp -o main but this will only compile all the source files inside of libs but not inside folders in libs.
Ive looked into make and cmake but dont understand how those automate the process if you still have to manually enter the directories. Is there no way to simply hit compile or at least a command line command to compile all the needed files inside a directory? This seems to work with #include without issues?
If you want to stick with MinGW and GNU Make I would probably use a Makefile that looks something like this to start with. You basically only need to maintain the srcs-variable by adding your source-files there. Usually you can use the wildcard-function for this if you have sub dirs. The rest of the Makefile (which can be left alone) sets up a build of an executable main.exe that depends on all the object-files. I also included dependency-handling via the deps-variable and the compiler flag -MMD which comes in handy when the project grows.
srcs := $(wildcard *.cpp) $(wildcard dir1/*.cpp) $(wildcard dir2/*.cpp)
objs := $(srcs:.cpp=.o)
deps := $(objs:.o=.d)
app := main.exe
CXXFLAGS := -MMD -Og -g -Wall -Werror -Wpedantic -std=c++2a
$(app): $(objs)
$(CXX) $(LDFLAGS) -o $# $^ $(LDLIBS)
-include $(deps)
.PHONY: clean
clean:
rm -f $(objs) $(deps)
I use CMake for simple projects.
Here's the simplest example I came with (CMakeLists.txt to put along your main.cpp in the root of your project):
cmake_minimum_required(VERSION 3.1)
SET(CMAKE_APP_NAME "Project")
project (${CMAKE_APP_NAME})
# list here your directories
INCLUDE_DIRECTORIES(dir1)
INCLUDE_DIRECTORIES(dir2)
# add an executable and list all files to compile
add_executable(${CMAKE_APP_NAME} main.cpp
dir1/file1.cpp
dir1/file1.h
dir2/file2.h
dir2/file2.cpp
)
Once your project becomes more complex, you could use file(GLOB*) to avoid writing all the files.
Overall, the most "automated" way to build a larger project is to use CMake. Keep learning it. You can use file(GLOB) to avoid listing every file in CMakeLists.txt. This is not recommended (see discussion here), but I do it anyway and never had any issues.

netbeans more then one main function

Currently I am working with Netbeans on Cygwin. I want to introduce a second main function, so after the build Netbeans should produce two exe files.
Is there a possibility to configure this in the IDE?
I also took a look into the make file, which includes
# include project implementation makefile
include nbproject/Makefile-impl.mk
# include project make variables
include nbproject/Makefile-variables.mk
I guess I should edit this file, or the included files to achieve what i am looking for. But obviously i would prefer to set it inside the IDE.
Since the main function is the entry point for the executable, the compiler must be fed with only one instance of it.
In order to do what you intend to do, you would have to write two separate Makefiles. One which compiles and links with main1.cpp and the other one that compiles and links main2.cpp
I think the IDE can only help you with the ability to create personalized Build configurations.
You can use NetBeans C/C++ Project With Existing Sources. This type of project relies on your own Makefile, so you are free in what you can do there. However, it's good to have predefined target names like build and clean - then it will be easier to call these targets from the NetBeans.
Your build target can create any number of executables you need, however it's not a good practice. Typically you should have targets main1 and main2, and your build should depend on both of them:
build: main1 main2
Each target main1 and main2 should create its own executable:
main1: main1.cpp
$(CXX) $(FLAGS) -o $# $^
main2: main2.cpp
$(CXX) $(FLAGS) -o $# $^
BTW, this setup will allow you to build just a single executable if you want:
make main1

Changing file paths in included makefile

I have project and few subproject
Some of subprojects are very independent. They works without any knowledge about whole project, they are build differently and have its own Makefile. This Makefile is located in subproject root dir.
Whole subproject is built to single object file.
Is there any way to properly include such object file to main Makefile? By "properly" I mean preserving all dependencies and building this file using method in subproject Makefile.
I tried to create rule for building this object file:
path_to_subproject/some_object.o:
$(MAKE) -C path_to_subproject
But that way I cannot preserve any dependencies.
What I need is to convert all relative path in subproject Makefile and include it to main Makefile.
Another way could be telling main Makefile that some_object.o is built using different Makefile, so make should use it to check dependencies etc.
Suppose the makefile in subproject/ looks like this:
some_object.o: some_source.cc some_header.h
$(CXX) -c $< -o $# -I.
You can modify it like so:
HERE:=/full/path/to/subproject
$(HERE)/some_object.o: $(HERE)/some_source.cc $(HERE)/some_header.h
$(CXX) -c $< -o $# -I$(HERE)
This will act the same as the original when used by itself (except that you can no longer make some_object.o from subproject/, you must make /full/path/to/subproject/some_object.o-- that can be fixed, but you must make a design decision or two).
Now you can add a line to the bottom of the master makefile:
include path_to_subproject/Makefile
and then the master makefile can use that object:
some_executable: local_object.o /full/path/to/subproject/some_object.o
whatever
More sophisticated variations are possible, once you have this working.

how to write makefile to take care of changes in the header file

Actually i have a library 'cryptopp' and what i want is that when i make any change to a file and issue the make command it should take care of the changes made in any file in the source directory. well, the GNUMakefile of cryptoopp takes care of the changes 'if' made in the '.cpp' files but not for the changes made in a '.h' file.
So what changes can i make in the 'GNUMakefile' of cryptopp so that it looks at all the modified header files and recompiles all the files dependent on the 'modified' header file.
If you are building with g++ you can let g++ generate dependancy makefiles.
You can include these in your main makefile.
Use the -M and -M* arguments to use this feature. (see http://gcc.gnu.org/onlinedocs/gcc-4.6.1/gcc/Preprocessor-Options.html#Preprocessor-Options)
You have to add all the dependencies to your Makefile:
mycode.o: mycode.cpp mycode.h somelib.h resources.h
$(CXX) -c -o $# $< $(CXXFLAGS) $(INCLUDES)
If you already have a generic pattern matching command line, you don't have to say the command again, you can just list the dependencies:
%o: %.cpp
$(CXX) -c -o $# $< $(CXXFLAGS) $(INCLUDES)
mycode.o: mycode.cpp mycode.h somelib.h resources.h
yourcode.o: yourcode.cpp yourcode.h mycode.h somethingelse.h
# ...
In general, this is a terrible and unscalable mess. You'll almost definitely want a higher-level build system to generate the Makefile for you. Even for very small projects keeping the header dependencies up to date in the Makefile is such a pain that it is simply not worth it.
There are several popular portable build environments. I personally like cmake a lot, which includes discovery if you changed the build settings (say from Debug to Release) and will always build all the necessary files (for example, if you change the cmake master file and type "make" it'll automatically run cmake again for you first).
For a Unix-only solution you could try makedepend, or the infamous autotools, though that's a whole other headache...
You might try 'makedepend' if it's installed on your system. The easiest way is to add a target to your makefile. Something like:
depend:
makedepend *.cc
You might have to replace the '*.cc' with a list of your source files. Then you can regenerate all the dependencies with 'make depend' command. You might want to redirect error messages to /dev/null since it always seems to generate a lot of noise.

Building multiple executables with similar rules

I am writing something like an interactive tutorial for C++. The tutorial will consist of two parts: one is compiled into a library (I'm using Scons to build that), and the other (the lessons) is shipped with the tutorial to be compiled by the end user. I'm currently looking for a good, easy way for people to build these lessons.
Basically, the second part is a directory with all the lessons in it, each in its own directory. Each lesson will have at least a lesson.cpp and a main.cpp file, there may be also other files, the existence of which I will not know until after it is shipped -- the end user will create these. It will look something like this:
all_lessons/
helloworld/
lesson.cpp
main.cpp
even_or_odd/
lesson.cpp
main.cpp
calculator/
lesson.cpp
main.cpp
user_created_add.cpp
Each of these will need to be compiled according to almost the same rules, and the command for compiling should be possible to run from one of the lesson directories (helloworld/, etc.).
Seeing as the rest of the project is built with Scons, it would make sense to use it for this part, too. However, Scons searches for the SConstruct file in the directory it is run from: would it be acceptable to put a SConstruct file in each lesson directory, plus a SConscript in the all_lessons/ directory that gives the general rules? This seems to go against the typical way Scons expects projects to be organised: what are the potential pitfalls of this approach? Could I put a SConstruct file instead of the SConscript one, and thereby make it possible to build from either directory (using exports to avoid endless recursion, I'm guessing)?
Also, I may at some point want to replace the lesson.cpp with a lesson.py that generates the necessary files; will Scons allow me to do this easily with builders, or is there a framework that would be more convenient?
In the end, I want to end up with the following (or equivalent with different build systems):
all_lessons/
SConstruct
helloworld/
SConstruct
lesson.cpp
main.cpp
even_or_odd/
SConstruct
lesson.py
main.cpp
calculator/
SConstruct
lesson.cpp
main.cpp
user_created_add.cpp
Running scons all in the all_lessons directory would need to:
Run even_or_odd/lesson.py to generate even_or_odd/lesson.cpp.
Realise that user_created_add.cpp also needs to be compiled.
Produce an executable for each lesson.
Running scons in even_or_odd/, or scons even_or_odd in all_lessons/ should produce an executable identical to the one above (same compile flags).
Summary:
Is Scons suitable for/capable of this?
Does Scons work well when SConscript files are above SConstruct files?
Does Scons work well with multiple SConstrcut files for one project, SConscripting each other?
Is the Scons builder system suitable for using Python scripts to generate C++ files?
Is there any advantage of using a different build system/writing my own build framework that I'm missing?
Any further comments are, of course, welcome.
Thanks.
You can actually do this with a few lines of GNU Make.
Below are two makefiles that allow building and cleaning from all_lessons directory and individual project directories. It assumes that all C++ sources in that directory comprise an executable file which gets named after its directory. When building and cleaning from the top level source directory (all_lessons) it builds and cleans all the projects. When building and cleaning from a project's directory it only builds and cleans the project's binaries.
These makefiles also automatically generate dependencies and are fully parallelizable (make -j friendly).
For the following example I used the same source file structure as you have:
$ find all_lessons
all_lessons
all_lessons/even_or_odd
all_lessons/even_or_odd/main.cpp
all_lessons/Makefile
all_lessons/helloworld
all_lessons/helloworld/lesson.cpp
all_lessons/helloworld/main.cpp
all_lessons/project.mk
all_lessons/calculator
all_lessons/calculator/lesson.cpp
all_lessons/calculator/user_created_add.cpp
all_lessons/calculator/main.cpp
To be able to build from individial project directories project.mk must be symlinked as project/Makefile first
[all_lessons]$ cd all_lessons/calculator/
[calculator]$ ln -s ../project.mk Makefile
[helloworld]$ cd ../helloworld/
[helloworld]$ ln -s ../project.mk Makefile
[even_or_odd]$ cd ../even_or_odd/
[even_or_odd]$ ln -s ../project.mk Makefile
Let's build one project:
[even_or_odd]$ make
make -C .. project_dirs=even_or_odd all
make[1]: Entering directory `/home/max/src/all_lessons'
g++ -c -o even_or_odd/main.o -Wall -Wextra -MD -MP -MF even_or_odd/main.d even_or_odd/main.cpp
g++ -o even_or_odd/even_or_odd even_or_odd/main.o
make[1]: Leaving directory `/home/max/src/all_lessons'
[even_or_odd]$ ./even_or_odd
hello, even_or_odd
Now build all projects:
[even_or_odd]$ cd ..
[all_lessons]$ make
g++ -c -o calculator/lesson.o -Wall -Wextra -MD -MP -MF calculator/lesson.d calculator/lesson.cpp
g++ -c -o calculator/user_created_add.o -Wall -Wextra -MD -MP -MF calculator/user_created_add.d calculator/user_created_add.cpp
g++ -c -o calculator/main.o -Wall -Wextra -MD -MP -MF calculator/main.d calculator/main.cpp
g++ -o calculator/calculator calculator/lesson.o calculator/user_created_add.o calculator/main.o
g++ -c -o helloworld/lesson.o -Wall -Wextra -MD -MP -MF helloworld/lesson.d helloworld/lesson.cpp
g++ -c -o helloworld/main.o -Wall -Wextra -MD -MP -MF helloworld/main.d helloworld/main.cpp
g++ -o helloworld/helloworld helloworld/lesson.o helloworld/main.o
[all_lessons]$ calculator/calculator
hello, calculator
[all_lessons]$ helloworld/helloworld
hello, world
Clean one project:
[all_lessons]$ cd helloworld/
[helloworld]$ make clean
make -C .. project_dirs=helloworld clean
make[1]: Entering directory `/home/max/src/all_lessons'
rm -f helloworld/lesson.o helloworld/main.o helloworld/main.d helloworld/lesson.d helloworld/helloworld
make[1]: Leaving directory `/home/max/src/all_lessons'
Clean all projects:
[helloworld]$ cd ..
[all_lessons]$ make clean
rm -f calculator/lesson.o calculator/user_created_add.o calculator/main.o even_or_odd/main.o helloworld/lesson.o helloworld/main.o calculator/user_created_add.d calculator/main.d calculator/lesson.d even_or_odd/main.d calculator/calculator even_or_odd/even_or_odd helloworld/helloworld
The makefiles:
[all_lessons]$ cat project.mk
all :
% : forward_ # build any target by forwarding to the main makefile
$(MAKE) -C .. project_dirs=$(notdir ${CURDIR}) $#
.PHONY : forward_
[all_lessons]$ cat Makefile
# one directory per project, one executable per directory
project_dirs := $(shell find * -maxdepth 0 -type d )
# executables are named after its directory and go into the same directory
exes := $(foreach dir,${project_dirs},${dir}/${dir})
all : ${exes}
# the rules
.SECONDEXPANSION:
objects = $(patsubst %.cpp,%.o,$(wildcard $(dir ${1})*.cpp))
# link
${exes} : % : $$(call objects,$$*) Makefile
g++ -o $# $(filter-out Makefile,$^) ${LDFLAGS} ${LDLIBS}
# compile .o and generate dependencies
%.o : %.cpp Makefile
g++ -c -o $# -Wall -Wextra ${CPPFLAGS} ${CXXFLAGS} -MD -MP -MF ${#:.o=.d} $<
.PHONY: clean
clean :
rm -f $(foreach exe,${exes},$(call objects,${exe})) $(foreach dir,${project_dirs},$(wildcard ${dir}/*.d)) ${exes}
# include auto-generated dependency files
-include $(foreach dir,${project_dirs},$(wildcard ${dir}/*.d))
As an exercise in learning scons, I've tried to answer your question. Unfortunately, I'm no expert, so I can't tell you what's the best/ideal way, but here's a way that works.
Scons is suitable for/capable of this. (This is exactly what build tools are for.)
Not applicable. (I don't know.)
Scons seems to work well with multiple SConstrcut files for one project, SConscripting each other.
The Scons builder system can use Python scripts to generate C++ files.
A different build system? To each his own.
Using the hierarchy you defined, there's a SConstruct file in each folder. You can run scons in a subfolder to build that project or at the top level to build all projects (not sure how you'd alias "all" to the default build). You can run scons -c to clean the project and scons automatically figures out which files it created and cleans them (including the generated lesson.cpp).
However, if you want compiler flags to propagate from the top-level file down, I think it's better to use SConscript files -- except I'm not sure about making these compile on their own.
./SConstruct
env = Environment()
env.SConscript(dirs=['calculator', 'even_or_odd', 'helloworld'], name='SConstruct')
./calculator/SConstruct and ./calculator/helloworld
env = Environment()
env.Program('program', Glob('*.cpp'))
./even_or_odd/SConstruct
env = Environment()
def add_compiler_builder(env):
# filename transformation
suffix = '.cpp'
src_suffix = '.py'
# define the build method
rule = 'python $SOURCE $TARGET'
bld = Builder(action = rule,
suffix = suffix,
src_suffix = src_suffix)
env.Append(BUILDERS = {'Lesson' : bld})
return env
add_compiler_builder(env)
env.Lesson('lesson.py')
env.Program('program', Glob('*.cpp'))
Using SConscripts
I convert the subfolder's SConstructs to SConscripts and can lift the code build specifics out of the subfolders, but then you need to run scons -u to build in a subfolder (to search upwards for the root SConstruct).
./SConstruct
def default_build(env):
env.Program('program', Glob('*.cpp'))
env = Environment()
env.default_build = default_build
Export('env')
env.SConscript(dirs=['calculator', 'even_or_odd', 'helloworld'])
./helloworld/SConscript, etc...
Import('env')
env.default_build(env)
Is it essential that the command for compiling be run from the lesson directory? If not then I would personally create all_lessons/makefile with the following contents:
lessons = helloworld even_or_odd calculator
all: $(lessons)
# for each $lesson, the target is $lesson/main built from $lesson/main.cpp and $lesson/lesson.cpp
# NB: the leading space on the second line *must* be a tab character
$(lessons:%=%/main): %/main: %/main.cpp %/lesson.cpp
g++ -W -Wall $+ -o $#
All lessons could then be built with "make" or "make all" in the all_lessons directory, or a specific lesson with e.g. "make helloworld/main".
As far as I have found, this is the best solution available:
The directory is structured in the same way, but instead of having multiple SConstruct files, the lessons have a SConscript file each, where defaults are overridden as necessary. The SConstruct files are generated by an external script as necessary, and SCons is invoked.
An overview:
all_lessons/
helloworld/
SConscript
lesson.cpp
main.cpp
even_or_odd/
SConscript
lesson.py
main.cpp
calculator/
SConscript
lesson.cpp
main.cpp
user_created_add.cpp
Using Glob, the SConscript file can make all files with the cpp extension be compiled. It can also use a builder (either one invoking a simple command, or a fully-fledged one) that will generate the lesson, meaning it's possible to even just store the lesson as metadata and have it generated on the spot.
So, to answer the questions:
Yes.
I don't know, but it is not required for the purpose of this.
As far as I have seen, no (issues with paths relative to SConstruct, amongst other things).
Yes, with several options being available.
I don't know.
Downsides to the suggested approach: this does require making a meta-build system separately. The number of files where options can be specified is higher, and the SConscript files give a lot of room for error.
Here is my way.
# SConstruct or SConscript
def getSubdirs(dir) :
lst = [ name for name in os.listdir(dir) if os.path.isdir(os.path.join(dir, name)) and name[0] != '.' ]
return lst
env = Environment()
path_to_lessons = '' # path to lessons
# configure your environment, set common rules and parameters for all lessons
for lesson in getSubdirs(path_to_lessons) :
lessonEnv = env.Clone()
# configure specific lesson, for example i'h ve checked SConscript file in lesson dir
# and if it exist, execute it with lessonEnv and append env specific settings
if File(os.path.join(path_to_lessons, lesson, 'lesson.scons')).exists() :
SConscript(os.path.join(lesson, 'lesson.scons', export = ['lessonEnv'])
# add lesson directory to include path
lessonEnv.Append(CPPPATH = os.path.join(path_to_lessons, lesson));
lessonEnv.Program(lesson, Glob(os.path.join(path_to_lessons, lesson, '*.cpp'))
Now you have :
env - core Environment that contain common rules and parameters for
all lessons
lessonEnv - clone of core env, but if you have
lesson.scons in specific lesson dir, you can additional configure
that environment or rewrite some parameters.