Eldritch Cheese
Eldritch Cheese

Reputation: 1237

scons, CPPPATH, and multiple libraries

Problem Description

I am attempting to build a modular system using scons to compile multiple shared libraries, some of which depend on the others. While I can get this to compile with a few different work-arounds, each has some disadvantages, as described in the Attempted Solutions section.

.
├── SConstruct
└── src
    ├── libA
    │   ├── a.cc
    │   ├── a.hh
    │   └── SConscript
    ├── libB
    │   ├── b.cc
    │   ├── b.hh
    │   └── SConscript
    └── SConscript

Here, b.cc includes a.hh, which is the dependency between the two libraries.

The contents of each of the files are shown below.

# In SConstruct
VariantDir('build', 'src', duplicate=False)
SConscript('build/SConscript')

# In src/SConscript
env = Environment()
SConscript(['libA/SConscript', 'libB/SConscript'],
           exports='env')

# In src/libA/SConscript
Import('env')

env.Append(CPPPATH=['.'])
env.SharedLibrary('a.cc')

# In src/libB/SConscript
Import('env')

env.Append(CPPPATH=['.'])
env.SharedLibrary('b.cc')

Here, I run into an issue. When I run scons, I have the following result.

scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
g++ -o build/libA/a.os -c -fPIC -Ibuild/libA -Isrc/libA -Ibuild/libA -Isrc/libA src/libA/a.cc
g++ -o build/libA/liba.so -shared build/libA/a.os
g++ -o build/libB/b.os -c -fPIC -Ibuild/libB -Isrc/libB -Ibuild/libB -Isrc/libB src/libB/b.cc
src/libB/b.cc:3:16: fatal error: a.hh: No such file or directory
 #include "a.hh"
                ^
compilation terminated.
scons: *** [build/libB/b.os] Error 1
scons: building terminated because of errors.

I know from this older question that the duplication of -Ibuild/libA and -Isrc/libA makes sense, and is intended. However, -Isrc/libA is passed twice when compiling a.cc, and not at all when compiling b.cc.


Attempted Solutions

This seems to be caused by passing a string into CPPPATH, rather than a Dir() node. The string is then expanded with the path to the current SConscript later, rather than expanding with the current SConscript. To avoid this, I modified CPPPATH=['.'] to CPPPATH=[Dir('.')] in src/libA/SConscript and src/libB/SConscript. This does not work, because then only build/libA is included in the CPPPATH, not src/libA, as shown below.

scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
g++ -o build/libA/a.os -c -fPIC -Ibuild/libA -Ibuild/libB src/libA/a.cc
g++ -o build/libA/liba.so -shared build/libA/a.os
g++ -o build/libB/b.os -c -fPIC -Ibuild/libA -Ibuild/libB src/libB/b.cc
src/libB/b.cc:3:16: fatal error: a.hh: No such file or directory
 #include "a.hh"
                ^
compilation terminated.
scons: *** [build/libB/b.os] Error 1
scons: building terminated because of errors.

Second, I tested this with duplicate=False. In combination with the first test, using Dir('.'), this successfully compiles the libraries. However, this is not ideal, because any debug symbols then point to source files within the build directory. For large projects, after compiling, I like to delete the build directory to save space, which then makes debugging harder, since gdb can no longer find the source files.

Next, I tried using abolute paths. That is, in libA, I add CPPPATH=['#/src/libA'], and in libB, I add CPPPATH=['#/src/libB']. This successfully compiles, with duplicate=False. However, this is intended as a library that could potentially be included from many different top-level SConstructs. By hard-coding the path into the library's SConscript, it restricts the usage of this library.

Finally, I tried not using VariantDir at all. In addition to using Dir('.') as the path for each library, this works, but leaves all of the intermediate files in the src directory. This clutters up the directory structure, and makes it very tricky to maintain multiple builds (e.g. debug/release).

Is there a design usage that would avoid these issues?

Upvotes: 1

Views: 2886

Answers (1)

dirkbaechle
dirkbaechle

Reputation: 4052

You state that "By hard-coding the path into the library's SConscript, it restricts the usage of this library.". But what's the alternative? At some point in your build description you have to get specific about which paths should get searched for implicit dependencies (like headers or libs). And if you then move your folders around, the build will break...independent of your build tool choice.

Using SCons "#" paths, specified starting from the top-level SConstruct, works fine and is one solution to your problem. The other alternative would be to use relative paths, starting from your current SConstruct upwards or downwards. So for src/libB/SConscript you could use:

Import('env')

env.AppendUnique(CPPPATH=['.', '../libA'])
env.SharedLibrary('b.cc')

This would then at least allow you to move "libA" and "libB" simultaneously to a new place. But I don't think you can do better than that...

Upvotes: 1

Related Questions