7.4. Using the toolpath for external Tools

7.4.1. The default tool search path

Normally when using a tool from the construction environment, several different search locations are checked by default. This includes the Scons/Tools/ directory inbuilt to scons and the directory site_scons/site_tools relative to the root SConstruct file.

# Builtin tool or tool located within site_tools
env = Environment(tools = ['SomeTool'])
env.SomeTool(targets, sources)

# The search locations would include by default
SCons/Tool/SomeTool.py
SCons/Tool/SomeTool/__init__.py
./site_scons/site_tools/SomeTool.py
./site_scons/site_tools/SomeTool/__init__.py
      

7.4.2. Providing an external directory to toolpath

In some cases you may want to specify a different location to search for tools. The Environment constructor contains an option for this called toolpath This can be used to add additional search directories.

# Tool located within the toolpath directory option
env = Environment(tools = ['SomeTool'], toolpath = ['/opt/SomeToolPath', '/opt/SomeToolPath2'])
env.SomeTool(targets, sources)

# The search locations in this example would include:
/opt/SomeToolPath/SomeTool.py
/opt/SomeToolPath/SomeTool/__init__.py
/opt/SomeToolPath2/SomeTool.py
/opt/SomeToolPath2/SomeTool/__init__.py
SCons/Tool/SomeTool.py
SCons/Tool/SomeTool/__init__.py
./site_scons/site_tools/SomeTool.py
./site_scons/site_tools/SomeTool/__init__.py
      

7.4.3. Nested Tools within a toolpath

SCons 3.0 now supports the ability for a Builder to be located within a sub-directory / sub-package of the toolpath. This is similar to namespacing within python. With nested or namespaced tools we can use the dot notation to specify a sub-directory that the tool is located under.

# namespaced target
env = Environment(tools = ['SubDir1.SubDir2.SomeTool'], toolpath = ['/opt/SomeToolPath'])
env.SomeTool(targets, sources)

# With this example the search locations would include
/opt/SomeToolPath/SubDir1/SubDir2/SomeTool.py
/opt/SomeToolPath/SubDir1/SubDir2/SomeTool/__init__.py
SCons/Tool/SubDir1/SubDir2/SomeTool.py
SCons/Tool/SubDir1/SubDir2/SomeTool/__init__.py
./site_scons/site_tools/SubDir1/SubDir2/SomeTool.py
./site_scons/site_tools/SubDir1/SubDir2/SomeTool/__init__.py
      

For python2 It's important to note when creating tools within sub-directories, there needs to be a __init__.py file within each directory. This file can just be empty. This is the same constraint used by python when loading modules from within sub-directories (packages). For python3 this appears to be no longer a requirement.

7.4.4. Using sys.path within the toolpath

If we want to access tools externally to scons on the sys.path (one example would be tools installed via the pip package manager) One way to do this is to use sys.path with the toolpath. One thing to watch out for with this approach is that sys.path can sometimes contains paths to .egg files instead of directories. So we need to filter those out with this approach.

# namespaced target using sys.path within toolpath

searchpaths = []
for item in sys.path:
    if os.path.isdir(item): searchpaths.append(item)

env = Environment(tools = ['someinstalledpackage.SomeTool'], toolpath = searchpaths)
env.SomeTool(targets, sources)
      

By using sys.path with the toolpath argument and by using the nested syntax we can have scons search packages installed via pip for Tools.

# For Windows based on the python version and install directory, this may be something like
C:\Python35\Lib\site-packages\someinstalledpackage\SomeTool.py
C:\Python35\Lib\site-packages\someinstalledpackage\SomeTool\__init__.py

# For Linux this could be something like:
/usr/lib/python3/dist-packages/someinstalledpackage/SomeTool.py
/usr/lib/python3/dist-packages/someinstalledpackage/SomeTool/__init__.py

7.4.5. Using the PyPackageDir function to add to the toolpath

In some cases you may want to use a tool located within a installed external pip package. This is possible by the use of sys.path with the toolpath. However in that situation you need to provide a prefix to the toolname to indicate where it is located within sys.path

searchpaths = []
for item in sys.path:
    if os.path.isdir(item): searchpaths.append(item)
env = Environment(tools = ['tools_example.subdir1.subdir2.SomeTool'], toolpath = searchpaths)
env.SomeTool(targets, sources)
      

To avoid the use of a prefix within the name of the tool or filtering sys.path for directories, we can use the PyPackageDir(modulename) function to locate the directory of the python package. PyPackageDir returns a Dir object which represents the path of the directory for the python package / module specified as a parameter.

# namespaced target using sys.path
env = Environment(tools = ['SomeTool'], toolpath = [PyPackageDir('tools_example.subdir1.subdir2')])
env.SomeTool(targets, sources)