Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(186)

Side by Side Diff: site_scons/site_tools/component_builders.py

Issue 6329: Dropping in software construction toolkit. (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: Created 12 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « site_scons/site_tools/component_bits.py ('k') | site_scons/site_tools/component_setup.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #!/usr/bin/python2.4
2 # Copyright 2008, Google Inc.
3 # All rights reserved.
4 #
5 # Redistribution and use in source and binary forms, with or without
6 # modification, are permitted provided that the following conditions are
7 # met:
8 #
9 # * Redistributions of source code must retain the above copyright
10 # notice, this list of conditions and the following disclaimer.
11 # * Redistributions in binary form must reproduce the above
12 # copyright notice, this list of conditions and the following disclaimer
13 # in the documentation and/or other materials provided with the
14 # distribution.
15 # * Neither the name of Google Inc. nor the names of its
16 # contributors may be used to endorse or promote products derived from
17 # this software without specific prior written permission.
18 #
19 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31 """Software construction toolkit builders for SCons."""
32
33
34 import SCons
35
36
37 __component_list = {}
38
39
40 def _InitializeComponentBuilders(self):
41 """Re-initializes component builders module.
42
43 Args:
44 self: Parent environment.
45 """
46 self = self # Silence gpylint
47
48 __component_list.clear()
49
50
51 def _RetrieveComponents(component_name, filter_components=None):
52 """Get the list of all components required by the specified component.
53
54 Args:
55 component_name: Name of the base component.
56 filter_components: List of components NOT to include.
57
58 Returns:
59 A list of the transitive closure of all components required by the base
60 component. That is, if A requires B and B requires C, this returns [B, C].
61
62 """
63 if filter_components:
64 filter_components = set(filter_components)
65 else:
66 filter_components = set()
67
68 components = set([component_name]) # Components always require themselves
69 new_components = set(components)
70 while new_components:
71 # Take next new component and add it to the list we've already scanned.
72 c = new_components.pop()
73 components.add(c)
74 # Add to the list of new components any of c's components that we haven't
75 # seen before.
76 new_components.update(__component_list.get(c, set())
77 - components - filter_components)
78
79 return list(components)
80
81
82 def _StoreComponents(self, component_name):
83 """Stores the list of child components for the specified component.
84
85 Args:
86 self: Environment containing component.
87 component_name: Name of the component.
88
89 Adds component references based on the LIBS and COMPONENTS variables in the
90 current environment. Should be called at primary SConscript execution time;
91 use _RetrieveComponents() to get the final components lists in a Defer()'d
92 function.
93 """
94
95 components = set()
96 for clist in ('LIBS', 'COMPONENTS'):
97 components.update(map(self.subst, self.Flatten(self[clist])))
98
99 if component_name not in __component_list:
100 __component_list[component_name] = set()
101 __component_list[component_name].update(components)
102
103
104 def _ComponentPlatformSetup(env, builder_name, **kwargs):
105 """Modify an environment to work with a component builder.
106
107 Args:
108 env: Environment to clone.
109 builder_name: Name of the builder.
110 kwargs: Keyword arguments.
111
112 Returns:
113 A modified clone of the environment.
114 """
115 # Clone environment so we can modify it
116 env = env.Clone()
117
118 # Add all keyword arguments to the environment
119 for k, v in kwargs.items():
120 env[k] = v
121
122 # Call platform-specific component setup function, if any
123 if env.get('COMPONENT_PLATFORM_SETUP'):
124 env['COMPONENT_PLATFORM_SETUP'](env, builder_name)
125
126 # Return the modified environment
127 return env
128
129 #------------------------------------------------------------------------------
130
131 # TODO(rspangler): Should be possible to refactor programs, test programs,
132 # libs to all publish as packages, for simplicity and code reuse.
133
134
135 def ComponentPackageDeferred(env):
136 """Deferred build steps for component package.
137
138 Args:
139 env: Environment from ComponentPackage().
140
141 Sets up the aliases to build the package.
142 """
143 package_name = env['PACKAGE_NAME']
144
145 # Install program and resources
146 all_outputs = []
147 components = _RetrieveComponents(package_name,
148 env.get('COMPONENT_PACKAGE_FILTER'))
149 for resource, dest_dir in env.get('COMPONENT_PACKAGE_RESOURCES').items():
150 all_outputs += env.ReplicatePublished(dest_dir, components, resource)
151
152 # Add installed program and resources to the alias
153 env.Alias(package_name, all_outputs)
154
155
156 def ComponentPackage(self, package_name, dest_dir, **kwargs):
157 """Pseudo-builder for package containing other components.
158
159 Args:
160 self: Environment in which we were called.
161 package_name: Name of package.
162 dest_dir: Destination directory for package.
163 args: Positional arguments.
164 kwargs: Keyword arguments.
165
166 Returns:
167 The alias node for the package.
168 """
169 # Clone and modify environment
170 env = _ComponentPlatformSetup(self, 'ComponentPackage', **kwargs)
171
172 env.Replace(
173 PACKAGE_NAME=package_name,
174 PACKAGE_DIR=dest_dir,
175 )
176
177 # Add an empty alias for the package and add it to the right groups
178 a = env.Alias(package_name, [])
179 for group in env['COMPONENT_PACKAGE_GROUPS']:
180 SCons.Script.Alias(group, a)
181
182 # Store list of components for this program
183 env._StoreComponents(package_name)
184
185 # Set up deferred call to replicate resources
186 env.Defer(ComponentPackageDeferred)
187
188 # Return the alias, since it's the only node we have
189 return a
190
191 #------------------------------------------------------------------------------
192
193
194 def ComponentObject(self, *args, **kwargs):
195 """Pseudo-builder for object to handle platform-dependent type.
196
197 Args:
198 self: Environment in which we were called.
199 args: Positional arguments.
200 kwargs: Keyword arguments.
201
202 Returns:
203 Passthrough return code from env.StaticLibrary() or env.SharedLibrary().
204
205 TODO(rspangler): Perhaps this should be a generator builder, so it can take
206 a list of inputs and return a list of outputs?
207 """
208 # Clone and modify environment
209 env = _ComponentPlatformSetup(self, 'ComponentObject', **kwargs)
210
211 # Make appropriate object type
212 if env.get('COMPONENT_STATIC'):
213 return env.StaticObject(*args, **kwargs)
214 else:
215 return env.SharedObject(*args, **kwargs)
216
217 #------------------------------------------------------------------------------
218
219
220 def ComponentLibrary(self, lib_name, *args, **kwargs):
221 """Pseudo-builder for library to handle platform-dependent type.
222
223 Args:
224 self: Environment in which we were called.
225 lib_name: Library name.
226 args: Positional arguments.
227 kwargs: Keyword arguments.
228
229 Returns:
230 Passthrough return code from env.StaticLibrary() or env.SharedLibrary().
231 """
232 # Clone and modify environment
233 env = _ComponentPlatformSetup(self, 'ComponentLibrary', **kwargs)
234
235 # Make appropriate library type
236 if env.get('COMPONENT_STATIC'):
237 lib_outputs = env.StaticLibrary(lib_name, *args, **kwargs)
238 else:
239 lib_outputs = env.SharedLibrary(lib_name, *args, **kwargs)
240
241 # Scan library outputs for files we need to link against this library, and
242 # files we need to run executables linked against this library.
243 need_for_link = []
244 need_for_debug = []
245 need_for_run = []
246 for o in lib_outputs:
247 if o.suffix in env['COMPONENT_LIBRARY_LINK_SUFFIXES']:
248 need_for_link.append(o)
249 if o.suffix in env['COMPONENT_LIBRARY_DEBUG_SUFFIXES']:
250 need_for_debug.append(o)
251 if o.suffix == env['SHLIBSUFFIX']:
252 need_for_run.append(o)
253 all_outputs = lib_outputs
254
255 # Install library in intermediate directory, so other libs and programs can
256 # link against it
257 all_outputs += env.Replicate('$COMPONENT_LIBRARY_DIR', need_for_link)
258
259 # Publish output
260 env.Publish(lib_name, 'run', need_for_run)
261 env.Publish(lib_name, 'debug', need_for_debug)
262
263 # Add an alias to build and copy the library, and add it to the right groups
264 a = self.Alias(lib_name, all_outputs)
265 for group in env['COMPONENT_LIBRARY_GROUPS']:
266 SCons.Script.Alias(group, a)
267
268 # Store list of components for this library
269 env._StoreComponents(lib_name)
270
271 # If library should publish itself, publish as if it was a program
272 if env.get('COMPONENT_LIBRARY_PUBLISH'):
273 env['PROGRAM_BASENAME'] = lib_name
274 env.Defer(ComponentProgramDeferred)
275
276 # Return the library outputs
277 return lib_outputs
278
279 #------------------------------------------------------------------------------
280
281
282 def ComponentTestProgramDeferred(env):
283 """Deferred build steps for test program.
284
285 Args:
286 env: Environment from ComponentTestProgram().
287
288 Sets up the aliases to compile and run the test program.
289 """
290 prog_name = env['PROGRAM_BASENAME']
291
292 # Install program and resources
293 all_outputs = []
294 components = _RetrieveComponents(prog_name)
295 for resource, dest_dir in env.get('COMPONENT_TEST_RESOURCES').items():
296 all_outputs += env.ReplicatePublished(dest_dir, components, resource)
297
298 # Add installed program and resources to the alias
299 env.Alias(prog_name, all_outputs)
300
301 # Add an alias for running the test in the test directory, if there's a test
302 # command line.
303 if env.get('COMPONENT_TEST_CMDLINE'):
304 # Test program is the first run resource we replicated.
305 test_program = env.ReplicatePublished('$TESTS_DIR', prog_name, 'run')
306 env.Replace(
307 COMMAND_OUTPUT_CMDLINE=env['COMPONENT_TEST_CMDLINE'],
308 COMMAND_OUTPUT_RUN_DIR='$TESTS_DIR',
309 )
310 test_out = env.CommandOutput(
311 '$TEST_OUTPUT_DIR/${PROGRAM_BASENAME}.out.txt', test_program)
312 # Running the test requires the test and its libs copied to the tests dir
313 env.Depends(test_out, all_outputs)
314 env.ComponentTestOutput('run_' + prog_name, test_out)
315
316
317 def ComponentTestProgram(self, prog_name, *args, **kwargs):
318 """Pseudo-builder for test program to handle platform-dependent type.
319
320 Args:
321 self: Environment in which we were called.
322 prog_name: Test program name.
323 args: Positional arguments.
324 kwargs: Keyword arguments.
325
326 Returns:
327 Output node list from env.Program().
328
329 TODO(rspangler): Should have some sort of support for S/M/L categorization
330 """
331 # Clone and modify environment
332 env = _ComponentPlatformSetup(self, 'ComponentTestProgram', **kwargs)
333
334 env['PROGRAM_BASENAME'] = prog_name
335 env['PROGRAM_NAME'] = '$PROGPREFIX$PROGRAM_BASENAME$PROGSUFFIX'
336
337 # Call env.Program()
338 out_nodes = env.Program(prog_name, *args, **kwargs)
339
340 # Publish output
341 env.Publish(prog_name, 'run', out_nodes[0])
342 env.Publish(prog_name, 'debug', out_nodes[1:])
343
344 # Add an alias to build the program to the right groups
345 a = env.Alias(prog_name, out_nodes)
346 for group in env['COMPONENT_TEST_PROGRAM_GROUPS']:
347 SCons.Script.Alias(group, a)
348
349 # Store list of components for this program
350 env._StoreComponents(prog_name)
351
352 # Set up deferred call to replicate resources and run test
353 env.Defer(ComponentTestProgramDeferred)
354
355 # Return the output node
356 return out_nodes
357
358 #------------------------------------------------------------------------------
359
360
361 def ComponentProgramDeferred(env):
362 """Deferred build steps for program.
363
364 Args:
365 env: Environment from ComponentProgram().
366
367 Sets up the aliases to compile the program.
368 """
369 prog_name = env['PROGRAM_BASENAME']
370
371 # Install program and resources
372 all_outputs = []
373 components = _RetrieveComponents(prog_name)
374 for resource, dest_dir in env.get('COMPONENT_PROGRAM_RESOURCES').items():
375 all_outputs += env.ReplicatePublished(dest_dir, components, resource)
376
377 # Add installed program and resources to the alias
378 env.Alias(prog_name, all_outputs)
379
380
381 def ComponentProgram(self, prog_name, *args, **kwargs):
382 """Pseudo-builder for program to handle platform-dependent type.
383
384 Args:
385 self: Environment in which we were called.
386 prog_name: Test program name.
387 args: Positional arguments.
388 kwargs: Keyword arguments.
389
390 Returns:
391 Output node list from env.Program().
392 """
393 # Clone and modify environment
394 env = _ComponentPlatformSetup(self, 'ComponentProgram', **kwargs)
395
396 env['PROGRAM_BASENAME'] = prog_name
397
398 # Call env.Program()
399 out_nodes = env.Program(prog_name, *args, **kwargs)
400
401 # Publish output
402 env.Publish(prog_name, 'run', out_nodes[0])
403 env.Publish(prog_name, 'debug', out_nodes[1:])
404
405 # Add an alias to build the program to the right groups
406 a = env.Alias(prog_name, out_nodes)
407 for group in env['COMPONENT_PROGRAM_GROUPS']:
408 SCons.Script.Alias(group, a)
409
410 # Store list of components for this program
411 env._StoreComponents(prog_name)
412
413 # Set up deferred call to replicate resources
414 env.Defer(ComponentProgramDeferred)
415
416 # Return the output nodes
417 return out_nodes
418
419 #------------------------------------------------------------------------------
420
421
422 def ComponentTestOutput(self, test_name, nodes):
423 """Pseudo-builder for test output.
424
425 Args:
426 self: Environment in which we were called.
427 test_name: Test name.
428 nodes: List of files/Nodes output by the test.
429
430 Returns:
431 Passthrough return code from env.Alias().
432 """
433
434 # Add an alias for the test outputs, and add it to the right groups
435 a = self.Alias(test_name, nodes)
436 for group in self['COMPONENT_TEST_OUTPUT_GROUPS']:
437 SCons.Script.Alias(group, a)
438
439 # Return the output node
440 return a
441
442 #------------------------------------------------------------------------------
443
444
445 def generate(env):
446 # NOTE: SCons requires the use of this name, which fails gpylint.
447 """SCons entry point for this tool."""
448
449 env.Replace(
450 COMPONENT_LIBRARY_DIR='$TARGET_ROOT/lib',
451 STAGING_DIR='$TARGET_ROOT/staging',
452 TESTS_DIR='$TARGET_ROOT/tests',
453 TEST_OUTPUT_DIR='$TARGET_ROOT/test_output',
454 # Default command line for a test is just the name of the file.
455 # TODO(rspangler): Why doesn't the following work:
456 # COMPONENT_TEST_CMDLINE='${SOURCE.abspath}',
457 # (it generates a SCons error)
458 COMPONENT_TEST_CMDLINE='${PROGRAM_NAME}',
459 COMPONENT_STATIC=True, # Static linking is a sensible default.
460 # Don't publish libraries to the staging dir by themselves by default.
461 COMPONENT_LIBRARY_PUBLISH=False,
462 )
463 env.Append(
464 LIBPATH=['$COMPONENT_LIBRARY_DIR'],
465 RPATH=['$COMPONENT_LIBRARY_DIR'],
466
467 # Default alias groups for component builders
468 COMPONENT_PACKAGE_GROUPS=['all_packages'],
469 COMPONENT_LIBRARY_GROUPS=['all_libraries'],
470 COMPONENT_PROGRAM_GROUPS=['all_programs'],
471 COMPONENT_TEST_PROGRAM_GROUPS=['all_test_programs'],
472 COMPONENT_TEST_OUTPUT_GROUPS=['run_all_tests'],
473
474 # Additional components whose resources should be copied into program
475 # directories, in addition to those from LIBS and the program itself.
476 LIBS=[],
477 COMPONENTS=[],
478
479 # Dicts of what resources should go in each destination directory for
480 # programs and test programs.
481 COMPONENT_PACKAGE_RESOURCES={
482 'run': '$PACKAGE_DIR',
483 'debug': '$PACKAGE_DIR',
484 },
485 COMPONENT_PROGRAM_RESOURCES={
486 'run': '$STAGING_DIR',
487 'debug': '$STAGING_DIR',
488 },
489 COMPONENT_TEST_RESOURCES={
490 'run': '$TESTS_DIR',
491 'debug': '$TESTS_DIR',
492 'test_input': '$TESTS_DIR',
493 },
494 )
495
496 # Add our pseudo-builder methods
497 env.AddMethod(_InitializeComponentBuilders)
498 env.AddMethod(_StoreComponents)
499 env.AddMethod(ComponentPackage)
500 env.AddMethod(ComponentObject)
501 env.AddMethod(ComponentLibrary)
502 env.AddMethod(ComponentProgram)
503 env.AddMethod(ComponentTestProgram)
504 env.AddMethod(ComponentTestOutput)
505
506 # Add our target groups
507 AddTargetGroup('all_libraries', 'libraries can be built')
508 AddTargetGroup('all_programs', 'programs can be built')
509 AddTargetGroup('all_test_programs', 'tests can be built')
510 AddTargetGroup('all_packages', 'packages can be built')
511 AddTargetGroup('run_all_tests', 'tests can be run')
OLDNEW
« no previous file with comments | « site_scons/site_tools/component_bits.py ('k') | site_scons/site_tools/component_setup.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698