ToolFinder

Description

ToolFinder is a configurable functor used to search for executable files within predefined search paths. Once created (configured), an instance of ToolFinder is expected to return same search result, every time it’s applied to a given SCons environment.

python = ToolFinder('python')
def generate(env):
   env['PYTHON'] = python(env)   # same result here ...
def exists(env):
   return python(env)            # ... and here

The program being searched for is identified by a name. By default, tool name is used ('python' in the above example). This may be overwritten with the name parameter

python = ToolFinder('python', name='python3')

The above finder will search for 'python3' file for the purpose of SCons tool named 'python'. Cumbersome strings may be used for name, including variable substitutions, like name="$PYTHONNAME".

python = ToolFinder('python', name='$PYTHONNAME')

A sequence of file names may also be passed in as name (alternative names for the program).

# Will search for 'python3', then 'python', then 'python2' in each path
python = ToolFinder('python', name=['python3', 'python', 'python2'])

By default, ToolFinder searches within the standard SCons PATH (env['ENV']['PATH']). This can be changed, by providing path argument to the constructor

# Will search in custom path, instead of env['ENV']['PATH']
python = ToolFinder('python', path=['/home/user/.local/virtualenvs/foo/bin',
                                    '/home/user/.local/bin'])
prog = python(env)

If file is found in path (or SCons PATH, if path not given), its name is returned. By setting strip_path to False, the object is being told to return absolute path instead

# Will return absolute path to the file found
python = ToolFinder('python', strip_path=False)
prog = python(env)    # '/usr/bin/python', for example

ToolFinder accepts two extra search paths: priority_path, and fallback_path. The priority_path is searched prior to the SCons PATH, the fallback_path is examined after the SCons PATH.

# Will search in priority_path, then env['ENV']['PATH'], then fallback_path.
python = ToolFinder('python', priority_path=['/home/user/.local/bin'],
                              fallback_path=['/opt/bin'])
prog = python(env)

The priority-/fallback- paths are used by ToolFinder at the tool configuration time, but it’s assumed that they’re not used by SCons during the build phase (later), when the program has to be invoked. Following this assumption, to ensure that same program will be picked up at the build stage, ToolFinder returns an absolute path to any program found within either priority_path or fallback_path. This behavior may be changed with strip_priority_path and strip_fallback_path, respectively

# Will return 'python', instead of '/home/user/.local/bin/python'
python = ToolFinder('python', priority_path=['/home/user/.local/bin'],
                              strip_priority_path=True)
prog = python(env)

Examples

Iconv tool

The iconv(1) command translates texts between encodings. The following simple tool makes use of iconv(1) installed in standard location (within SCons search PATH).

Example:Tool implementation
Tool module: site_scons/site_tools/iconv.py
# -*- coding: utf-8 -*-
from sconstool.util import *
from SCons.Builder import Builder

_iconv = ToolFinder('iconv')


def generate(env):
    env.SetDefault(ICONVFROM='UTF-8', ICONVTO='UTF-8')
    env.SetDefault(ICONV=_iconv(env))
    env['ICONVCOM'] = '$ICONV -f $ICONVFROM -t $ICONVTO $SOURCE > $TARGET'
    env['BUILDERS']['Iconv'] = Builder(action='$ICONVCOM')


def exists(env):
    return _iconv(env)
Example:A project using the iconv tool
Project file: SConstruct
# SConstruct
import os
env = Environment(tools=['iconv'], ENV={'PATH': os.environ['PATH']})
env.Iconv('utf8.txt', 'latin2.txt', ICONVFROM='LATIN2')
Input file: latin2.txt
Zaźółć  gęślą jaźń

Hammer tool

This tool will use our own python script hammer.py stored in a non-standard path. The hammer.py will replace all the occurrences of "nail" with "drived in nail".

Example:The hammer command
Command implmementation: bin/hammer.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
with open(sys.argv[2], 'rt') as ifile, open(sys.argv[1], 'wt') as ofile:
    ofile.write(ifile.read().replace('nail', 'drived in nail'))
Example:Tool implementation
The tool module: site_scons/site_tool/hammer.py
# -*- coding: utf-8 -*-
from sconstool.util import *
from SCons.Builder import Builder
import sys

_hammer = ToolFinder('hammer', name='hammer.py', pathext=['.py'], priority_path='bin')


def generate(env):
    env.SetDefault(PYTHON=sys.executable)
    env.SetDefault(HAMMER=_hammer(env))
    env['HAMMERCOM'] = '$PYTHON $HAMMER $TARGET $SOURCE'
    env['BUILDERS']['Hammer'] = Builder(action='$HAMMERCOM')


def exists(env):
    return _hammer(env)
Example:A project using the hammer tool.
Project file: SConstruct
# SConstruct
env = Environment(tools=['hammer'])
env.Hammer('output.txt', 'input.txt')
Input file: input.txt
We have twenty nails.