Please note:The SCons wiki is now restored from the attack in March 2013. All old passwords have been invalidated. Please reset your password if you have an account. If you note missing pages, please report them to webmaster@scons.org. Also, new account creation is currently disabled due to an ongoing spam flood (2013/08/27).

Here is a Builder that runs make. It's intended to be used along with UnTarBuilder and AutoConfigBuilder to extract some source code, configure it, and build it.

The Builder accepts several options to control build targets, execution environment, and make command-line options. It will also pass the SCons -j parameter to the make command (but there's an option to prevent that).

Definition

   1 # Make Builder: Runs make.
   2 #
   3 # Parameters:
   4 #    MakePath -- SCons Dir node representing the directory in which to run make.  REQUIRED.
   5 #    MakeCmd -- The 'make' executable to run.
   6 #               Default: make
   7 #    MakeEnv -- Dictionary of variables to set in the make execution environment.
   8 #               Default: none
   9 #    MakeOpts -- Options to pass on the make command line.
  10 #                Default: none
  11 #    MakeOneThread -- Don't pass any -j option to make.
  12 #                     Default: False
  13 #    MakeTargets -- String of space-seperated targets to pass to make
  14 #                   Default: ""
  15 
  16 import os
  17 import subprocess
  18 
  19 from SCons.Script import *
  20 
  21 def parms(target, source, env):
  22     """Assemble various Make parameters."""
  23 
  24     if 'MakePath' in env.Dictionary().keys():
  25         make_path = env.subst(str(env['MakePath']))
  26     else:
  27         print "Make builder requires MakePath variable"
  28         Exit(1)
  29 
  30     make_cmd = 'make'
  31     if 'MakeCmd' in env.Dictionary().keys():
  32         make_cmd = env.subst(env['MakeCmd'])
  33     elif 'MAKE' in env.Dictionary().keys():
  34         make_cmd = env.subst(env['MAKE'])
  35 
  36     make_env = None
  37     if env['CROSS_BUILD']:
  38         make_env = env['CROSS_ENV']
  39     if 'MakeEnv' in env.Dictionary().keys():
  40         if make_env == None:
  41             make_env = {}
  42         else:
  43             # We're appending to an existing dictionary, so create a copy
  44             # instead of appending to the original env['CROSS_ENV']
  45             make_env = env['CROSS_ENV'][:]
  46         for (k,v) in env['MakeEnv'].items():
  47             make_env[k] = v
  48 
  49     make_opts = None
  50     if 'MakeOpts' in env.Dictionary().keys():
  51         make_opts = env.subst(env['MakeOpts'])
  52 
  53     make_jobs = GetOption('num_jobs')
  54     if 'MakeOneThread' in env.Dictionary().keys() and env['MakeOneThread']:
  55         make_jobs = 1
  56 
  57     make_targets = None
  58     if 'MakeTargets' in env.Dictionary().keys():
  59         make_targets = env.subst(env['MakeTargets'])
  60 
  61     return (make_path, make_env, make_targets, make_cmd, make_jobs, make_opts)
  62 
  63 def message(target, source, env):
  64     """Return a pretty Make message"""
  65 
  66     (make_path,
  67      make_env,
  68      make_targets,
  69      make_cmd,
  70      make_jobs,
  71      make_opts) = parms(target, source, env)
  72 
  73     myenv = env.Clone()
  74     # Want to use MakeTargets in the MAKECOMSTR, but make it pretty first.
  75     if 'MakeTargets' in myenv.Dictionary().keys():
  76         myenv['MakeTargets'] += ' '
  77     else:
  78         myenv['MakeTargets'] = ''
  79 
  80     if 'MAKECOMSTR' in myenv.Dictionary().keys():
  81         return myenv.subst(myenv['MAKECOMSTR'],
  82                             target = target, source = source, raw = 1)
  83 
  84     msg = 'cd ' + make_path + ' &&'
  85     if make_env != None:
  86         for k, v in make_env.iteritems():
  87             msg += ' ' + k + '=' + v
  88     msg += ' ' + make_cmd
  89     if make_jobs > 1:
  90         msg += ' -j %d' % make_jobs
  91     if make_opts != None:
  92         msg += ' ' + ' '.join(make_opts)
  93     if make_targets != None:
  94         msg += ' ' + make_targets
  95     return msg
  96 
  97 def builder(target, source, env):
  98     """Run make in a directory."""
  99 
 100     (make_path,
 101      make_env,
 102      make_targets,
 103      make_cmd,
 104      make_jobs,
 105      make_opts) = parms(target, source, env)
 106 
 107     # Make sure there's a directory to run make in
 108     if len(make_path) == 0:
 109         print 'No path specified'
 110     if not os.path.exists(make_path):
 111         print 'Path %s not found' % make_path
 112 
 113     # Build up the command and its arguments in a list
 114     fullcmd = [ make_cmd ]
 115 
 116     if make_jobs > 1:
 117         fullcmd += [ '-j', str(make_jobs) ]
 118 
 119     if make_opts:
 120         fullcmd += make_opts
 121 
 122     if make_targets:
 123         fullcmd += make_targets.split()
 124 
 125     # Capture the make command's output, unless we're verbose
 126     real_stdout = subprocess.PIPE
 127     if 'MAKECOMSTR' not in env.Dictionary().keys():
 128         real_stdout = None
 129 
 130     # Make!
 131     make = subprocess.Popen(fullcmd,
 132                             cwd = make_path,
 133                             stdout = real_stdout,
 134                             env = make_env)
 135 
 136     # Some subprocesses don't terminate unless we communicate with them
 137     output = make.communicate()[0]
 138     return make.returncode
 139 
 140 def generate(env, **kwargs):
 141     env['BUILDERS']['Make'] = env.Builder(
 142         action = env.Action(builder, message))
 143 
 144 def exists(env):
 145     if env.WhereIs(env.subst('$MAKE')) != None:
 146         return True
 147     return False

Installation

To load this Builder into your environment, save the above as Make.py in your site_scons/site_tools/ directory, then add it as a tool to your environment:

   1 env = Environment(tools = ['default', 'Make'])
   2 # or
   3 otherenv.Tool('Make')

Usage

You use this builder by giving it source and target parameters as usual, but also specifying MakePath to tell it in which directory to run make.

   1 # Define the sources for the make build, so SCons can track dependencies
   2 # (or just set source=None to have SCons run make all the time and
   3 # let make track dependencies).
   4 src = Glob('foo-1.2.3/*.c')
   5 
   6 # Run make inside the foo-1.2.3/ directory
   7 foolib = env.Make(source = src,
   8                   target = [ 'foo-1.2.3/.libs/libfoo.a',
   9                              'foo-1.2.3/.libs/libfoo.so' ],
  10                   MakePath = Dir('foo-1.2.3'))

If your environment has MAKECOMSTR defined, the builder uses that string to print its command message and also hides the command's output. Without MAKECOMSTR the builder prints the full make command as well as the command's output.

Parameters

* MakePath -- (Required) SCons Dir node representing the directory in which to run make.

* MakeCmd -- (Default: make) The 'make' executable to run.

* MakeEnv -- (Default: none) Dictionary of variables to set in the make execution environment.

* MakeOpts -- (Default: none) Options to pass on the make command line.

* MakeOneThread -- (Default: False) Don't pass any -j options to make.

* MakeTargets -- (Default: none) String of space-separated targets for make to build.

MakeBuilder (last edited 2009-07-15 19:34:46 by MarcBranchaud)