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).
Contents
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:
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.
