Create multiple targets from the same source tree using different preprocessor macros
My knowledge of make and autotools (which I am not yet using for this project) is rudimentary at best, despite a lot of searches and experimentation over a long period of time. I have an original hierarchy as shown below that I am trying to find a way to build as much as possible.
The app is made up of the main app, with the source in different subfolders in app / src. They are created with an appropriate Makefile in the root folder of that folder.
Then I have several other utilities that are in different folders in the / tools application, each with its own Makefile.
app/src/module1/file1.cpp app/src/module1/file1.hpp app/src/module2/file2.cpp app/src/module2/file2.hpp app/src/module3/file3.cpp app/src/module3/file3.hpp app/src/main.cpp app/src/main.hpp app/src/Makefile app/tools/util1/file1.cpp app/tools/util1/file1.hpp app/tools/util1/Makefile app/tools/util2/file2.cpp app/tools/util2/file2.hpp app/tools/util2/Makefile
The problem for me is that some of these tools depend on source files inside the source app / src folder, but with the EXTERNAL_TOOL preprocess macro enabled. Thus, the object files generated by compiling the main application and the varous utility are not compatible.
Currently, to create each part of the project, I need to clear the source tree in between. This is painful and certainly not what I want in the end. What would be the best way to solve this? Ideas I had that I could not put into practice:
- Split build directory for each part of the project
- When creating external tools, tagging their object files in the main tree of the source application somehow (util.file1.o?)
I'm not too sure if I have the time and patience required to master make / autotools. Could one of the other build tools (scons? Cmake?) Make this task easier to accomplish? If so, which one?
UPDATE: This is what I have now
SOURCES := util1.cpp util2.cpp util3.cpp \
../../src/module1/file1.cpp \
../../src/module1/file2.cpp \
../../src/module1/file3.cpp \
../../src/module2/file4.cpp \
../../src/module3/file5.cpp \
../../src/module3/file6.cpp \
../../src/module4/file7.cpp \
../../src/module4/file8.cpp \
../../src/module3/file9.cpp \
../../src/module4/file10.cpp \
../../src/module5/file11.cpp \
../../src/module3/file12.cpp \
../../src/module1/file13.cpp \
../../src/module3/file14.cpp \
../../src/module3/file15.cpp
OBJECTS = $(join $(addsuffix .util/, $(dir $(SOURCES))), $(notdir $(SOURCES:.cpp=.o)))
.PHONY: all mkdir
all: util
util: $(OBJECTS)
$(CXX) $(CXXFLAGS) $(OBJECTS) $(LIBS) -o util
$(OBJECTS): | mkdir
$(CXX) -c $(CXXFLAGS) -o $@ $(patsubst %.o,%.cpp,$(subst .util/,,$@))
mkdir:
@mkdir -p $(sort $(dir $(OBJECTS)))
clean:
-@rm -f $(OBJECTS) util
-@rmdir $(sort $(dir $(OBJECTS))) 2>/dev/null
I came to this after doing extensive internet searching. It seems to work, but this part doesn't really seem particularly nice (seems a bit hacky):
$(OBJECTS): | mkdir
$(CXX) -c $(CXXFLAGS) -o $@ $(patsubst %.o,%.cpp,$(subst .util/,,$@))
In particular, I'm not too keen on the fact that I was creating a list of objects from sources earlier and adding a suffix, only to do the opposite here. I couldn't get it to work in any other way.
source to share
CMake has commands add_definitions
and remove_definitions
. You can use them to define macros for different parts of your project:
# building tools #
add_definitions(-DEXTERNAL_TOOL)
add_subdirectory($TOOL1$ $BUILD_DIR$)
add_subdirectory($TOOL2$ $BUILD_DIR$)
...
# building main app #
remove_definitions(-DEXTERNAL_TOOL)
add_executable(...)
source to share
This can be done painlessly with SCons. You will definitely need a build directory hierarchy for objects built using various preprocessor macros. In SCons terms, creating directories like this is called variant_dir. I would recommend the following SCons assembly hierarchical structure:
app/SConstruct
app/src/module1/file1.cpp
app/src/module1/file1.hpp
app/src/module2/file2.cpp
app/src/module2/file2.hpp
app/src/module3/file3.cpp
app/src/module3/file3.hpp
app/src/main.cpp
app/src/main.hpp
app/src/SConscript_modules
app/src/SConscript_main
app/tools/util1/file1.cpp
app/tools/util1/file1.hpp
app/tools/util2/file2.cpp
app/tools/util2/file2.hpp
app/tools/SConscript
app/build/main/
app/build/target1/modules/
app/build/target2/modules/
app/build/tools/utils/
To be able to create the same source files with different preprocessor macros, you will need to create the same file with several different environments. These envs can be configured in src / module SConscript scripts or from root SConstruct and piped down. I prefer the second option as it makes the src / module SCons modular and is not aware (agnostic) of preprocessor macros.
Here is a root build script that creates various envs and organizes the build scripts for the subdirectory:
app / SConstruct
defines1 = ['MACRO1']
defines2 = ['MACRO2']
env1 = Environment(CPPDEFINES = defines1)
env2 = Environment(CPPDEFINES = defines2)
includePaths = [
'src/module1',
'src/module2',
'src/module3',
]
env1.Append(CPPPATH = includePaths)
env2.Append(CPPPATH = includePaths)
# Build different versions of the module libs
SConscript('src/SConscript_modules',
variant_dir = '#build/target1/modules',
exports = {'env':env1},
duplicate=0)
SConscript('src/SConscript_modules',
variant_dir = '#build/target2/modules',
exports = {'env':env2},
duplicate=0)
# Build main with env1
SConscript('src/SConscript_main',
variant_dir = '#build/main',
exports = {'env':env2},
duplicate=0)
# Build tools with env2
SConscript('tools/SConscript',
variant_dir = '#build/utils',
exports = {'env':env2},
duplicate=0)
This is the assembly script for the main application / SRC / SConscript _main
Import('env')
sourceFiles = ['main.cpp']
# If you want to modify the env here, Clone() it first, otherwise
# the changes will be visible to all other SConscripts
env.Program(target = 'main', source = sourceFiles)
This is the build script for the libs modules, it will be called twice, each time with a different env application / SRC / SConscript _modules
Import('env')
module1SourceFiles = ['file1.cpp']
module2SourceFiles = ['file2.cpp']
module3SourceFiles = ['file3.cpp']
# If you want to modify the env here, Clone() it first, otherwise
# the changes will be visible to all other SConscripts
env.Library(target = 'module1', source = module1SourceFiles)
env.Library(target = 'module2', source = module2SourceFiles)
env.Library(target = 'module3', source = module3SourceFiles)
source to share