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:

   1 prefix = "/usr/local"
   2 
   3 someshlib = env.SharedLibrary('foo', "foo.c")
   4 someprogram = env.Program("fooprog", "fooprog.c")
   5 
   6 # the install target
   7 env.Alias("install", env.Install(os.path.join(prefix, "lib"), someshlib))
   8 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:

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

Permissions

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

   1 import SCons
   2 
   3 # define the custom function
   4 from SCons.Script.SConscript import SConsEnvironment
   5 SConsEnvironment.Chmod = SCons.Action.ActionFactory(os.chmod,
   6         lambda dest, mode: 'Chmod("%s", 0%o)' % (dest, mode))
   7 
   8 def InstallPerm(env, dest, files, perm):
   9     obj = env.Install(dest, files)
  10     for i in obj:
  11         env.AddPostAction(i, env.Chmod(str(i), perm))
  12     return dest
  13 
  14 # put this function "in" scons
  15 SConsEnvironment.InstallPerm = InstallPerm
  16 
  17 # great, we're ready to use it!
  18 env.InstallPerm(bindir, ['fooprog', 'barprog'], 0755)
  19 
  20 # but let's say we're not happy yet, we'd prefer nicer names.
  21 SConsEnvironment.InstallProgram = lambda env, dest, files: InstallPerm(env, dest, files, 0755)
  22 SConsEnvironment.InstallHeader = lambda env, dest, files: InstallPerm(env, dest, files, 0644)
  23 
  24 # great, now you can also install by calling a method named 'InstallHeader' or 'InstallProgram'!
  25 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:

   1 try:
   2     umask = os.umask(022)
   3     print 'setting umask to 022 (was 0%o)' % umask
   4 except OSError:     # ignore on systems that don't support umask
   5     pass

   1 source="./data/icon.png"
   2 target="/usr/local/share/X/icon.png"
   3 
   4 env.Alias("install", target)
   5 env.Command( target, source,
   6 [
   7 Copy("$TARGET","$SOURCE"),
   8 Chmod("$TARGET", 0664),
   9 ])

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

   1 # where you need to implement 'RecursiveGlob' yourself 
   2 for file in RecursiveGlob("./data", "*"):
   3     # strip 'data/' out to have the filepath relative to data dir
   4     index = file.find("data/") + len("data/")
   5     filename_relative = file[index:]
   6     source = os.path.join("./data", filename_relative)
   7     target = os.path.join(data_dir, filename_relative)
   8             
   9     env.Alias("install", target)
  10     env.Command( target, source,
  11     [
  12     Copy("$TARGET","$SOURCE"),
  13     Chmod("$TARGET", 0664),
  14     ])

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]/app_name.mo.

   1 # install .mo files
   2 locale_dir = "/usr/local/share/locale"
   3 
   4 mo_files = Glob("./po/*/app_name.mo",strings=True)
   5 for mo in mo_files:
   6     # extract language code
   7     index_lo = mo.find("po/") + len("po/")
   8     index_hi = mo.find("/app_name.mo")
   9     lang_name = mo[index_lo:index_hi]
  10     # copy file
  11     install_location = locale_dir + "/" + lang_name + "/LC_MESSAGES/app_name.mo"
  12     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 :

   1 def create_uninstall_target(env, path, is_glob):
   2     if is_glob:
   3         all_files = Glob(path,strings=True)
   4         for filei in all_files:
   5             env.Command( "uninstall-"+filei, filei,
   6             [
   7             Delete("$SOURCE"),
   8             ])
   9             env.Alias("uninstall", "uninstall-"+filei)   
  10     else:
  11         env.Command( "uninstall-"+path, path,
  12         [
  13         Delete("$SOURCE"),
  14         ])
  15         env.Alias("uninstall", "uninstall-"+path)  

You can use it like this :

   1 if 'uninstall' in COMMAND_LINE_TARGETS:
   2     # create uninstall targets
   3     create_uninstall_target(env, "/usr/local/bin/myapp", False)
   4     create_uninstall_target(env, "/usr/local/share/myapp/", False)
   5     create_uninstall_target(env, "/usr/local/share/locale/*/LC_MESSAGES/myapp.mo", True)

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

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

InstallTargets (last edited 2010-10-30 19:47:24 by david.garcia)