Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Building multiple targets off same source tree with different preprocessor macros

My knowledge of make and autotools (which I'm not yet using for this project) is rudimentary at best despite plenty of googling and experimenting over a long period of time. I have a source hierarchy like below that I'm trying to find way to build has seamlessly as possible.

The application is made up of a main application with source in various subfolders under app/src. These are built with the respective Makefile in the root of that folder.

Then I have multiple other utilities that reside different folders under app/tools that each have their 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 app/src source folder, but with a preprocess macro EXTERNAL_TOOL enabled. So the object files generated from compiling the main app and the varous utilities are not compatible.

Currently to build each portion of the project I'm having to clean the source tree in between. This is painful and certainly not what I want in the end. What would be the best way to go about solving this? Ideas I've had that I've no been able to put into practice are:

  1. Separate build directory for each portion of the project
  2. When building the external tools, tagging their object files in the main app source tree somehow (util.file1.o?)

I'm not too sure I have the time and patience needed to master make / autotools. Might one of the other build tools (scons? cmake?) make this kind of task easier to accomplish? If so which one?

UPDATE: This is what I've got 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 about this after extensive googling SO browsing. This seems to work, but this part doesn't really seem particular nice (feels like a bit of a hack):

$(OBJECTS): | mkdir
    $(CXX) -c $(CXXFLAGS) -o $@ $(patsubst %.o,%.cpp,$(subst .util/,,$@))

In particular I'm not too keen on the fact I'm creating the list of objects from sources earlier on and adding the suffix, only to do the reverse down here. I couldn't seem to get it working any other way.

like image 617
goji Avatar asked Nov 14 '25 12:11

goji


2 Answers

CMake has add_definitions and remove_definitions commands. 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(...)
like image 155
hank Avatar answered Nov 17 '25 09:11

hank


This can be done with SCons rather painlessly. You will definitely need a build directory hierarchy for the objects built with different preprocessor macros. In SCons terms, creating build directories like this is called variant_dir. I would recomend the following SCons Hierarchical build 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 build the same source files with different preprocessor macros, you will need to build the same file with several different Environments. These env's could be setup in the src/module SConscript scripts, or from the root SConstruct and passed down. I prefer the second option, since it will make the src/module SCons scripts modular, and unaware (agnostic) of the preprocessor macros.

Here is the root build script, which creates the different env's and orchestrates the sub-directory build scripts:

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 build script for main app/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 module libs, it will be called twice, each time with a different env app/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)
like image 28
Brady Avatar answered Nov 17 '25 10:11

Brady



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!