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 that is part of the scons distribution 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 function 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

Since SCons 3.0, a Builder may 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
      

7.4.4. Using sys.path within the toolpath

If we want to access tools external to scons which are findable via sys.path (for example, tools installed via Python's pip package manager), it is possible 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 PyPackageDir 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)