Clone wiki

SCons / InstallTargets

The SCons wiki has moved to

It's often useful to be able to run "scons install" to copy the programs and shared libraries to their correct locations. Here's one way to do that:

prefix = "/usr/local"

someshlib = env.SharedLibrary('foo', "foo.c")
someprogram = env.Program("fooprog", "fooprog.c")

# the install target
env.Alias("install", env.Install(os.path.join(prefix, "lib"), someshlib))
env.Alias("install", env.Install(os.path.join(prefix, "bin"), someprogram))

Basically we alias 'install' to a couple of Install nodes, returned by the Install() method. That's all.

NB: There is no need to add something like 'Depends(install, someshlib)', since SCons does find this dependency automatically.

If you need some fine-grained install targets, you may use something like this:

Alias('install-lib', Install(os.path.join(prefix, "lib"), ...))
Alias('install-bin', Install(os.path.join(prefix, "bin"), ...))
Alias('install', ['install-bin', 'install-lib'])


Question: how to set permissions properly (binaries get 755, headers get 644, etc.) after an install?

  • One way to do it is to create a method that acts like Install() but has an additional permission argument. Wrappers with predefined permissions are useful for cleaner markup:
import SCons

# define the custom function
from SCons.Script.SConscript import SConsEnvironment
SConsEnvironment.Chmod = SCons.Action.ActionFactory(os.chmod,
        lambda dest, mode: 'Chmod("%s", 0%o)' % (dest, mode))

def InstallPerm(env, dest, files, perm):
    obj = env.Install(dest, files)
    for i in obj:
        env.AddPostAction(i, env.Chmod(str(i), perm))
    return dest

# put this function "in" scons
SConsEnvironment.InstallPerm = InstallPerm

# great, we're ready to use it!
env.InstallPerm(bindir, ['fooprog', 'barprog'], 0755)

# but let's say we're not happy yet, we'd prefer nicer names.
SConsEnvironment.InstallProgram = lambda env, dest, files: InstallPerm(env, dest, files, 0755)
SConsEnvironment.InstallHeader = lambda env, dest, files: InstallPerm(env, dest, files, 0644)

# great, now you can also install by calling a method named 'InstallHeader' or 'InstallProgram'!
env.InstallHeader(incdir, ['foo.h', 'bar.h'])

Don't forget to set the umask, or created directories might get wrong permissions on Unix and Windows:

    umask = os.umask(022)
    print 'setting umask to 022 (was 0%o)' % umask
except OSError:     # ignore on systems that don't support umask
  • Another similar method to install data with correct permissions is to use a Command :

env.Alias("install", target)
env.Command( target, source,
Chmod("$TARGET", 0664),

where target and source could be set in a directory parsing loop for conveniance :

# where you need to implement 'RecursiveGlob' yourself 
for file in RecursiveGlob("./data", "*"):
    # strip 'data/' out to have the filepath relative to data dir
    index = file.find("data/") + len("data/")
    filename_relative = file[index:]
    source = os.path.join("./data", filename_relative)
    target = os.path.join(data_dir, filename_relative)

    env.Alias("install", target)
    env.Command( target, source,
    Chmod("$TARGET", 0664),

For best results, also make sure the umask is set like described above.

Locale files

Installing locale files on UNIX systems can be a little tricky :

This is an example that will handle installing .mo files for a source layout of /po/[language code]/

# install .mo files
locale_dir = "/usr/local/share/locale"

mo_files = Glob("./po/*/",strings=True)
for mo in mo_files:
    # extract language code
    index_lo = mo.find("po/") + len("po/")
    index_hi = mo.find("/")
    lang_name = mo[index_lo:index_hi]
    # copy file
    install_location = locale_dir + "/" + lang_name + "/LC_MESSAGES/"
    env.Alias("install", env.InstallAs( install_location, mo ) )

It should be simple enough to adapt this code to layouts like /po/[language code].mo or any other.

Uninstall targets

Here's a sample uninstall function :

def create_uninstall_target(env, path, is_glob):
    if is_glob:
        all_files = Glob(path,strings=True)
        for filei in all_files:
            env.Command( "uninstall-"+filei, filei,
            env.Alias("uninstall", "uninstall-"+filei)   
        env.Command( "uninstall-"+path, path,
        env.Alias("uninstall", "uninstall-"+path)  

You can use it like this :

if 'uninstall' in COMMAND_LINE_TARGETS:
    # create uninstall targets
    create_uninstall_target(env, "/usr/local/bin/myapp", False)
    create_uninstall_target(env, "/usr/local/share/myapp/", False)
    create_uninstall_target(env, "/usr/local/share/locale/*/LC_MESSAGES/", True)

If you want to uninstall all the files installed using Install or InstallAs, there is a more expeditive way:

env.Command("uninstall", None, Delete(FindInstalledFiles()))