I have been given a completed source archive (call it legacyProject
) that has a SConstruct
build script. This build script does a lot of work to create a nice customized Environment
that understands a custom toolchain for a microcontroller. It also has a couple of helper functions that simplify the generation of Program
statements for a rather large build matrix.
I am starting a related project that could reuse this Environment
and related code almost verbatim. I don't want to just copy-paste everything into a new SConstruct file, because the original might receive patches (not to mention that it's simply duplication of code). Currently these projects are side-by-side in the filesystem:
myProject/
SConstruct
legacyProject/
SConstruct
I'll probably rearrange them so that legacyProject
is a subdirectory of myProject
, so I can track exact revisions with version control.
myProject/
SConstruct
legacyProject/
SConstruct
Is there a way to import all the code from legacyProject/SConstruct
? With Python modules, this is trivial with import
, but I don't know if this is possible with Scons. My attempt:
SConscript('legacyProject/SConstruct')
just returns None.
Without refactoring some of the functionality from the legacy SConstruct into a helper file, I don't believe there is a way to do this.
If you can refactor the code out, then there are three possible alternatives approaches available - one is to use the existing SConscript mechanism built into SCons, another is to use python modules and the final approach is to use the site-dir option.
Reuse Via SConscripts
Assuming a project structure like:
.
├── common
│ └── SConscript
├── legacyProject
│ └── SConstruct
└── myProject
└── SConstruct
Your SConstruct files will create an environment, then execute the common/SConscript
, which returns a modified environment. For example, if you wanted to collect some options in the common directory you might end up with something like:
# common/SConscript
Import('env')
env['CCFLAGS'] = '-Wall -Wextra -pedantic'
Return('env')
And
# myProject/SConstruct (similar for legacy/SConstruct)
env = Environment()
print 'before:', env['CCFLAGS']
env = SConscript('../common/SConscript', exports = 'env')
print 'after:', env['CCFLAGS']
pick this approach if:
Reuse via Python Modules
If you're going to package scons utilities as python packages, there are two routes you can take.
The first is to use traditional Python packaging techniques. There are some good guides around - I recommend the Python Packaging User Guide. To get this working you'll need to write a setup.py, and installing using either pip
or python setup.py install
What makes this tricky is that you can have multiple side-by-side installations of Python and SCons, which are subtly coupled through your PATH environment variable. This may mean something that works on one machine may not work on another machine.
A more common approach is to modify the path python uses to search for modules in your SConstructs - poor practice from a Python perspective, but slightly more maintainable, particularly if your build machines are complex.
Assuming a project structure like:
.
├── common
│ └── __init__.py
├── legacyProject
│ └── SConstruct
└── myProject
└── SConscript
Your python module can be quite simple:
# common/__init__.py
def set_warning_flags(env):
env['CCFLAGS'] = '-Wall -Wextra -pedantic'
And:
# myProject/SConstruct
import sys
sys.path.insert(0, '..')
import common
env = Environment()
print 'before:', env['CCFLAGS']
common.set_warning_flags(env)
print 'after:', env['CCFLAGS']
Pick this approach if:
Reuse Via site-dir / site-init
If you want to add functionality on the fly to multiple SConstructs without changing them, you can use the in-built extensibility supported by SCons (see the sections in the user manual Where To Put Your Custom Builders and Tools or search for --site-dir=dir
in the SCons Man Page
In this case you would have:
.
├── common
│ └── site_init.py
├── legacyProject
│ └── SConstruct
└── myProject
└── SConstruct
Where site_init.py
was:
# common/site_init.py
def set_warning_flags(env):
env['CCFLAGS'] = '-Wall -Wextra -pedantic'
And the SConstruct was:
env = Environment()
print 'before:', env['CCFLAGS']
set_warning_flags(env)
print 'after:', env['CCFLAGS']
But you need to invoke SCons (from the myProject / legacyProject directories) with:
scons --site-dir=../common
Pick this approach if: