I've got the following directory structure:
root/
SConstruct
item0/
SConstruct
unit0/
unit0.h
private/
unit0.c
test/
test_unit0.c
SConstruct
SConscript
unit1/
(repeat unit0 structure)
item1/
(repeat item0 structure)
I want to be able to run scons from root
to build all things below it, and from root/item0
to build all things below it, etc.
The top-level SConstruct looks like:
SConscript('item0/SConstruct', duplicate=0)
SConscript('item1/SConstruct', duplicate=0)
The item-level SConstruct:
SConscript('unit0/SConstruct', duplicate=0)
SConscript('unit1/SConstruct', duplicate=0)
The unit-level SConstruct:
import os
proj_root = os.path.abspath('../../../../..')
proj_shared_path = os.path.join(proj_root,
os.path.normpath('trunk/sys/shared'))
env = Environment()
SConscript(
'SConscript',
variant_dir='output',
exports=['env', 'proj_shared_path'],
duplicate=0)
The unit-level SConscript (important stuff only):
Import('*')
# Setup Repositories to access out-of-current-directory-tree files.
lib1_path = os.path.join(proj_shared_path, 'lib1')
env.Repository('#/../private', lib1_path)
# Set general compilation and link flags.
env.Append(
CFLAGS = '-Wall -Wextra -std=c99 -pedantic',
CPPPATH = ['#/../..', proj_shared_path])
# These are the files we don't need to run gcovr on.
non_cov_srcs = [
'testit.c',
'lib1.c'
]
# Create a 'coverage' environment to build files that we will run gcovr on.
cov = env.Clone()
cov.Append(CFLAGS = '--coverage -O0')
cov_srcs = cov.Object('unit0.c')
SideEffect('unit0.gcno', cov_srcs)
# Our unit test program combines all the sources we've listed so far.
test_exe = env.Program(
'test_unit0',
[cov_srcs, non_cov_srcs],
LIBS='gcov', LINKFLAGS='--coverage')
Now, this all works fine when I run scons from root/item0/unit0/test
. However, when I run from root
or root/item0
, I get various types of errors, like below (run from root/item0
):
scons: done reading SConscript files.
scons: Building targets ...
scons: *** [unit0\test\output\unit0.obj] Source 'unit0\test\unit0.c' not found, needed by target 'unit0\test\output\unit0.obj'.
scons: building terminated because of errors.
Clearly unit0\test\unit0.c
won't be found because it doesn't exist, but I'm not sure why scons thinks it has to be there since the Repository is defined as '#/../private'
and there is no private shown in the error message.
edit I've figured out that it's probably looking for unit0\test\unit0.c
because unit0\test
is the "root" directory for SConscript
. I'm also guessing that the #/../private
Repository
location is only correct when scons
is run from unit0\test
. But, when I try to specify the Repository
in any other way (including an absolute path built with os.path.abspath
(and verified to be correct)), scons
still doesn't seem to want to look there for unit0.c
. Why is that?
With further help from @bdbaddog in the #scons IRC channel, I was able to determine the following:
variant_dir
SConstruct
and SConscript
in the same directory, Repository
ended up achieving this when building from the unit0/test
directory. But, I didn't know why.To properly achieve (1), Repository
is not the right tool. Instead, SConscript
should look like this:
Import('*')
# Set general compilation and link flags.
env.Append(
CFLAGS = '-Wall -Wextra -std=c99 -pedantic',
CPPPATH = ['#/item0', '#/lib1'])
# These are the files we don't need to run gcovr on.
non_cov_objs = [
Object(target='testit', source='testit.c'),
Object(target='lib1', source='#/lib1/lib1.c'
]
# Create a 'coverage' environment to build files that we will run gcovr on.
cov = env.Clone()
cov.Append(CFLAGS = '--coverage -O0')
cov_objs = cov.Object(
target='unit0',
source='#/item0/unit0/private/unit0.c')
SideEffect('unit0.gcno', cov_srcs)
# Our unit test program combines all the sources we've listed so far.
test_exe = env.Program(
'test_unit0',
[cov_objs, non_cov_objs],
LIBS='gcov', LINKFLAGS='--coverage')
By specifying target
and source
for each Object
, I can achieve what I want in terms of the object files (and other side-effects) being generated in the variant_dir
.