PageRenderTime 37ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 1ms

/Miranda IM - CK Release/Miranda/boost/tools/build/v2/build_system.py

http://miranda-dev.googlecode.com/
Python | 881 lines | 336 code | 156 blank | 389 comment | 119 complexity | 4c8a4f05dad2d5da1689adce6a8002f5 MD5 | raw file
Possible License(s): GPL-2.0, MPL-2.0-no-copyleft-exception, LGPL-3.0, AGPL-1.0, BSD-3-Clause, LGPL-2.1
  1. # Status: mostly ported. Missing is --out-xml support, 'configure' integration
  2. # and some FIXME.
  3. # Base revision: 64351
  4. # Copyright 2003, 2005 Dave Abrahams
  5. # Copyright 2006 Rene Rivera
  6. # Copyright 2003, 2004, 2005, 2006, 2007 Vladimir Prus
  7. # Distributed under the Boost Software License, Version 1.0.
  8. # (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
  9. from b2.build.engine import Engine
  10. from b2.manager import Manager
  11. from b2.util.path import glob
  12. from b2.build import feature, property_set
  13. import b2.build.virtual_target
  14. from b2.build.targets import ProjectTarget
  15. from b2.util.sequence import unique
  16. import b2.build.build_request
  17. from b2.build.errors import ExceptionWithUserContext
  18. import b2.tools.common
  19. from b2.build.toolset import using
  20. import b2.build.project as project
  21. import b2.build.virtual_target as virtual_target
  22. import b2.build.build_request as build_request
  23. import b2.util.regex
  24. from b2.manager import get_manager
  25. from b2.util import cached
  26. from b2.util import option
  27. import bjam
  28. import os
  29. import sys
  30. import re
  31. ################################################################################
  32. #
  33. # Module global data.
  34. #
  35. ################################################################################
  36. # Flag indicating we should display additional debugging information related to
  37. # locating and loading Boost Build configuration files.
  38. debug_config = False
  39. # Legacy option doing too many things, some of which are not even documented.
  40. # Should be phased out.
  41. # * Disables loading site and user configuration files.
  42. # * Disables auto-configuration for toolsets specified explicitly on the
  43. # command-line.
  44. # * Causes --toolset command-line options to be ignored.
  45. # * Prevents the default toolset from being used even if no toolset has been
  46. # configured at all.
  47. legacy_ignore_config = False
  48. # The cleaning is tricky. Say, if user says 'bjam --clean foo' where 'foo' is a
  49. # directory, then we want to clean targets which are in 'foo' as well as those
  50. # in any children Jamfiles under foo but not in any unrelated Jamfiles. To
  51. # achieve this we collect a list of projects under which cleaning is allowed.
  52. project_targets = []
  53. # Virtual targets obtained when building main targets references on the command
  54. # line. When running 'bjam --clean main_target' we want to clean only files
  55. # belonging to that main target so we need to record which targets are produced
  56. # for it.
  57. results_of_main_targets = []
  58. # Was an XML dump requested?
  59. out_xml = False
  60. # Default toolset & version to be used in case no other toolset has been used
  61. # explicitly by either the loaded configuration files, the loaded project build
  62. # scripts or an explicit toolset request on the command line. If not specified,
  63. # an arbitrary default will be used based on the current host OS. This value,
  64. # while not strictly necessary, has been added to allow testing Boost-Build's
  65. # default toolset usage functionality.
  66. default_toolset = None
  67. default_toolset_version = None
  68. ################################################################################
  69. #
  70. # Public rules.
  71. #
  72. ################################################################################
  73. # Returns the property set with the free features from the currently processed
  74. # build request.
  75. #
  76. def command_line_free_features():
  77. return command_line_free_features
  78. # Sets the default toolset & version to be used in case no other toolset has
  79. # been used explicitly by either the loaded configuration files, the loaded
  80. # project build scripts or an explicit toolset request on the command line. For
  81. # more detailed information see the comment related to used global variables.
  82. #
  83. def set_default_toolset(toolset, version=None):
  84. default_toolset = toolset
  85. default_toolset_version = version
  86. pre_build_hook = []
  87. def add_pre_build_hook(callable):
  88. pre_build_hook.append(callable)
  89. post_build_hook = None
  90. def set_post_build_hook(callable):
  91. post_build_hook = callable
  92. ################################################################################
  93. #
  94. # Local rules.
  95. #
  96. ################################################################################
  97. # Returns actual Jam targets to be used for executing a clean request.
  98. #
  99. def actual_clean_targets(targets):
  100. # Construct a list of projects explicitly detected as targets on this build
  101. # system run. These are the projects under which cleaning is allowed.
  102. for t in targets:
  103. if isinstance(t, b2.build.targets.ProjectTarget):
  104. project_targets.append(t.project_module())
  105. # Construct a list of targets explicitly detected on this build system run
  106. # as a result of building main targets.
  107. targets_to_clean = set()
  108. for t in results_of_main_targets:
  109. # Do not include roots or sources.
  110. targets_to_clean.update(virtual_target.traverse(t))
  111. to_clean = []
  112. for t in get_manager().virtual_targets().all_targets():
  113. # Remove only derived targets.
  114. if t.action():
  115. p = t.project()
  116. if t in targets_to_clean or should_clean_project(p.project_module()):
  117. to_clean.append(t)
  118. return [t.actualize() for t in to_clean]
  119. _target_id_split = re.compile("(.*)//(.*)")
  120. # Given a target id, try to find and return the corresponding target. This is
  121. # only invoked when there is no Jamfile in ".". This code somewhat duplicates
  122. # code in project-target.find but we can not reuse that code without a
  123. # project-targets instance.
  124. #
  125. def find_target(target_id):
  126. projects = get_manager().projects()
  127. m = _target_id_split.match(target_id)
  128. if m:
  129. pm = projects.find(m.group(1), ".")
  130. else:
  131. pm = projects.find(target_id, ".")
  132. if pm:
  133. result = projects.target(pm)
  134. if m:
  135. result = result.find(m.group(2))
  136. return result
  137. def initialize_config_module(module_name, location=None):
  138. get_manager().projects().initialize(module_name, location)
  139. # Helper rule used to load configuration files. Loads the first configuration
  140. # file with the given 'filename' at 'path' into module with name 'module-name'.
  141. # Not finding the requested file may or may not be treated as an error depending
  142. # on the must-find parameter. Returns a normalized path to the loaded
  143. # configuration file or nothing if no file was loaded.
  144. #
  145. def load_config(module_name, filename, paths, must_find=False):
  146. if debug_config:
  147. print "notice: Searching '%s' for '%s' configuration file '%s." \
  148. % (paths, module_name, filename)
  149. where = None
  150. for path in paths:
  151. t = os.path.join(path, filename)
  152. if os.path.exists(t):
  153. where = t
  154. break
  155. if where:
  156. where = os.path.realpath(where)
  157. if debug_config:
  158. print "notice: Loading '%s' configuration file '%s' from '%s'." \
  159. % (module_name, filename, where)
  160. # Set source location so that path-constant in config files
  161. # with relative paths work. This is of most importance
  162. # for project-config.jam, but may be used in other
  163. # config files as well.
  164. attributes = get_manager().projects().attributes(module_name) ;
  165. attributes.set('source-location', os.path.dirname(where), True)
  166. get_manager().projects().load_standalone(module_name, where)
  167. else:
  168. msg = "Configuration file '%s' not found in '%s'." % (filename, path)
  169. if must_find:
  170. get_manager().errors()(msg)
  171. elif debug_config:
  172. print msg
  173. return where
  174. # Loads all the configuration files used by Boost Build in the following order:
  175. #
  176. # -- test-config --
  177. # Loaded only if specified on the command-line using the --test-config
  178. # command-line parameter. It is ok for this file not to exist even if specified.
  179. # If this configuration file is loaded, regular site and user configuration
  180. # files will not be. If a relative path is specified, file is searched for in
  181. # the current folder.
  182. #
  183. # -- site-config --
  184. # Always named site-config.jam. Will only be found if located on the system
  185. # root path (Windows), /etc (non-Windows), user's home folder or the Boost Build
  186. # path, in that order. Not loaded in case the test-config configuration file is
  187. # loaded or either the --ignore-site-config or the --ignore-config command-line
  188. # option is specified.
  189. #
  190. # -- user-config --
  191. # Named user-config.jam by default or may be named explicitly using the
  192. # --user-config command-line option or the BOOST_BUILD_USER_CONFIG environment
  193. # variable. If named explicitly the file is looked for from the current working
  194. # directory and if the default one is used then it is searched for in the
  195. # user's home directory and the Boost Build path, in that order. Not loaded in
  196. # case either the test-config configuration file is loaded, --ignore-config
  197. # command-line option is specified or an empty file name is explicitly
  198. # specified. If the file name has been given explicitly then the file must
  199. # exist.
  200. #
  201. # Test configurations have been added primarily for use by Boost Build's
  202. # internal unit testing system but may be used freely in other places as well.
  203. #
  204. def load_configuration_files():
  205. # Flag indicating that site configuration should not be loaded.
  206. ignore_site_config = "--ignore-site-config" in sys.argv
  207. if legacy_ignore_config and debug_config:
  208. print "notice: Regular site and user configuration files will be ignored"
  209. print "notice: due to the --ignore-config command-line option."
  210. initialize_config_module("test-config")
  211. test_config = None
  212. for a in sys.argv:
  213. m = re.match("--test-config=(.*)$", a)
  214. if m:
  215. test_config = b2.util.unquote(m.group(1))
  216. break
  217. if test_config:
  218. where = load_config("test-config", os.path.basename(test_config), [os.path.dirname(test_config)])
  219. if where:
  220. if debug_config and not legacy_ignore_config:
  221. print "notice: Regular site and user configuration files will"
  222. print "notice: be ignored due to the test configuration being loaded."
  223. user_path = [os.path.expanduser("~")] + bjam.variable("BOOST_BUILD_PATH")
  224. site_path = ["/etc"] + user_path
  225. if os.name in ["nt"]:
  226. site_path = [os.getenv("SystemRoot")] + user_path
  227. if ignore_site_config and not legacy_ignore_config:
  228. print "notice: Site configuration files will be ignored due to the"
  229. print "notice: --ignore-site-config command-line option."
  230. initialize_config_module("site-config")
  231. if not test_config and not ignore_site_config and not legacy_ignore_config:
  232. load_config('site-config', 'site-config.jam', site_path)
  233. initialize_config_module('user-config')
  234. if not test_config and not legacy_ignore_config:
  235. # Here, user_config has value of None if nothing is explicitly
  236. # specified, and value of '' if user explicitly does not want
  237. # to load any user config.
  238. user_config = None
  239. for a in sys.argv:
  240. m = re.match("--user-config=(.*)$", a)
  241. if m:
  242. user_config = m.group(1)
  243. break
  244. if user_config is None:
  245. user_config = os.getenv("BOOST_BUILD_USER_CONFIG")
  246. # Special handling for the case when the OS does not strip the quotes
  247. # around the file name, as is the case when using Cygwin bash.
  248. user_config = b2.util.unquote(user_config)
  249. explicitly_requested = user_config
  250. if user_config is None:
  251. user_config = "user-config.jam"
  252. if user_config:
  253. if explicitly_requested:
  254. user_config = os.path.abspath(user_config)
  255. if debug_config:
  256. print "notice: Loading explicitly specified user configuration file:"
  257. print " " + user_config
  258. load_config('user-config', os.path.basename(user_config), [os.path.dirname(user_config)], True)
  259. else:
  260. load_config('user-config', os.path.basename(user_config), user_path)
  261. else:
  262. if debug_config:
  263. print "notice: User configuration file loading explicitly disabled."
  264. # We look for project-config.jam from "." upward.
  265. # I am not sure this is 100% right decision, we might as well check for
  266. # it only alonside the Jamroot file. However:
  267. #
  268. # - We need to load project-root.jam before Jamroot
  269. # - We probably would need to load project-root.jam even if there's no
  270. # Jamroot - e.g. to implement automake-style out-of-tree builds.
  271. if os.path.exists("project-config.jam"):
  272. file = ["project-config.jam"]
  273. else:
  274. file = b2.util.path.glob_in_parents(".", ["project-config.jam"])
  275. if file:
  276. initialize_config_module('project-config', os.path.dirname(file[0]))
  277. load_config('project-config', "project-config.jam", [os.path.dirname(file[0])], True)
  278. # Autoconfigure toolsets based on any instances of --toolset=xx,yy,...zz or
  279. # toolset=xx,yy,...zz in the command line. May return additional properties to
  280. # be processed as if they had been specified by the user.
  281. #
  282. def process_explicit_toolset_requests():
  283. extra_properties = []
  284. option_toolsets = [e for option in b2.util.regex.transform(sys.argv, "^--toolset=(.*)$")
  285. for e in option.split(',')]
  286. feature_toolsets = [e for option in b2.util.regex.transform(sys.argv, "^toolset=(.*)$")
  287. for e in option.split(',')]
  288. for t in option_toolsets + feature_toolsets:
  289. # Parse toolset-version/properties.
  290. (toolset_version, toolset, version) = re.match("(([^-/]+)-?([^/]+)?)/?.*", t).groups()
  291. if debug_config:
  292. print "notice: [cmdline-cfg] Detected command-line request for '%s': toolset= %s version=%s" \
  293. % (toolset_version, toolset, version)
  294. # If the toolset is not known, configure it now.
  295. known = False
  296. if toolset in feature.values("toolset"):
  297. known = True
  298. if known and version and not feature.is_subvalue("toolset", toolset, "version", version):
  299. known = False
  300. # TODO: we should do 'using $(toolset)' in case no version has been
  301. # specified and there are no versions defined for the given toolset to
  302. # allow the toolset to configure its default version. For this we need
  303. # to know how to detect whether a given toolset has any versions
  304. # defined. An alternative would be to do this whenever version is not
  305. # specified but that would require that toolsets correctly handle the
  306. # case when their default version is configured multiple times which
  307. # should be checked for all existing toolsets first.
  308. if not known:
  309. if debug_config:
  310. print "notice: [cmdline-cfg] toolset '%s' not previously configured; attempting to auto-configure now" % toolset_version
  311. if version is not None:
  312. using(toolset, version)
  313. else:
  314. using(toolset)
  315. else:
  316. if debug_config:
  317. print "notice: [cmdline-cfg] toolset '%s' already configured" % toolset_version
  318. # Make sure we get an appropriate property into the build request in
  319. # case toolset has been specified using the "--toolset=..." command-line
  320. # option form.
  321. if not t in sys.argv and not t in feature_toolsets:
  322. if debug_config:
  323. print "notice: [cmdline-cfg] adding toolset=%s) to the build request." % t ;
  324. extra_properties += "toolset=%s" % t
  325. return extra_properties
  326. # Returns 'true' if the given 'project' is equal to or is a (possibly indirect)
  327. # child to any of the projects requested to be cleaned in this build system run.
  328. # Returns 'false' otherwise. Expects the .project-targets list to have already
  329. # been constructed.
  330. #
  331. @cached
  332. def should_clean_project(project):
  333. if project in project_targets:
  334. return True
  335. else:
  336. parent = get_manager().projects().attribute(project, "parent-module")
  337. if parent and parent != "user-config":
  338. return should_clean_project(parent)
  339. else:
  340. return False
  341. ################################################################################
  342. #
  343. # main()
  344. # ------
  345. #
  346. ################################################################################
  347. def main():
  348. sys.argv = bjam.variable("ARGV")
  349. # FIXME: document this option.
  350. if "--profiling" in sys.argv:
  351. import cProfile
  352. r = cProfile.runctx('main_real()', globals(), locals(), "stones.prof")
  353. import pstats
  354. stats = pstats.Stats("stones.prof")
  355. stats.strip_dirs()
  356. stats.sort_stats('time', 'calls')
  357. stats.print_callers(20)
  358. return r
  359. else:
  360. try:
  361. return main_real()
  362. except ExceptionWithUserContext, e:
  363. e.report()
  364. def main_real():
  365. global debug_config, legacy_ignore_config, out_xml
  366. debug_config = "--debug-configuration" in sys.argv
  367. legacy_ignore_config = "--ignore_config" in sys.argv
  368. out_xml = any(re.match("^--out-xml=(.*)$", a) for a in sys.argv)
  369. engine = Engine()
  370. global_build_dir = option.get("build-dir")
  371. manager = Manager(engine, global_build_dir)
  372. import b2.build.configure as configure
  373. if "--version" in sys.argv:
  374. version.report()
  375. return
  376. # This module defines types and generator and what not,
  377. # and depends on manager's existence
  378. import b2.tools.builtin
  379. b2.tools.common.init(manager)
  380. load_configuration_files()
  381. extra_properties = []
  382. # Note that this causes --toolset options to be ignored if --ignore-config
  383. # is specified.
  384. if not legacy_ignore_config:
  385. extra_properties = process_explicit_toolset_requests()
  386. # We always load project in "." so that 'use-project' directives have any
  387. # chance of being seen. Otherwise, we would not be able to refer to
  388. # subprojects using target ids.
  389. current_project = None
  390. projects = get_manager().projects()
  391. if projects.find(".", "."):
  392. current_project = projects.target(projects.load("."))
  393. # In case there are no toolsets currently defined makes the build run using
  394. # the default toolset.
  395. if not legacy_ignore_config and not feature.values("toolset"):
  396. dt = default_toolset
  397. dtv = None
  398. if default_toolset:
  399. dtv = default_toolset_version
  400. else:
  401. dt = "gcc"
  402. if os.name == 'nt':
  403. dt = "msvc"
  404. # FIXME:
  405. #else if [ os.name ] = MACOSX
  406. #{
  407. # default-toolset = darwin ;
  408. #}
  409. print "warning: No toolsets are configured."
  410. print "warning: Configuring default toolset '%s'." % dt
  411. print "warning: If the default is wrong, your build may not work correctly."
  412. print "warning: Use the \"toolset=xxxxx\" option to override our guess."
  413. print "warning: For more configuration options, please consult"
  414. print "warning: http://boost.org/boost-build2/doc/html/bbv2/advanced/configuration.html"
  415. using(dt, dtv)
  416. # Parse command line for targets and properties. Note that this requires
  417. # that all project files already be loaded.
  418. (target_ids, properties) = build_request.from_command_line(sys.argv[1:] + extra_properties)
  419. # Expand properties specified on the command line into multiple property
  420. # sets consisting of all legal property combinations. Each expanded property
  421. # set will be used for a single build run. E.g. if multiple toolsets are
  422. # specified then requested targets will be built with each of them.
  423. if properties:
  424. expanded = build_request.expand_no_defaults(properties)
  425. else:
  426. expanded = [property_set.empty()]
  427. # Check that we actually found something to build.
  428. if not current_project and not target_ids:
  429. get_manager().errors()("no Jamfile in current directory found, and no target references specified.")
  430. # FIXME:
  431. # EXIT
  432. # Flags indicating that this build system run has been started in order to
  433. # clean existing instead of create new targets. Note that these are not the
  434. # final flag values as they may get changed later on due to some special
  435. # targets being specified on the command line.
  436. clean = "--clean" in sys.argv
  437. cleanall = "--clean-all" in sys.argv
  438. # List of explicitly requested files to build. Any target references read
  439. # from the command line parameter not recognized as one of the targets
  440. # defined in the loaded Jamfiles will be interpreted as an explicitly
  441. # requested file to build. If any such files are explicitly requested then
  442. # only those files and the targets they depend on will be built and they
  443. # will be searched for among targets that would have been built had there
  444. # been no explicitly requested files.
  445. explicitly_requested_files = []
  446. # List of Boost Build meta-targets, virtual-targets and actual Jam targets
  447. # constructed in this build system run.
  448. targets = []
  449. virtual_targets = []
  450. actual_targets = []
  451. explicitly_requested_files = []
  452. # Process each target specified on the command-line and convert it into
  453. # internal Boost Build target objects. Detect special clean target. If no
  454. # main Boost Build targets were explictly requested use the current project
  455. # as the target.
  456. for id in target_ids:
  457. if id == "clean":
  458. clean = 1
  459. else:
  460. t = None
  461. if current_project:
  462. t = current_project.find(id, no_error=1)
  463. else:
  464. t = find_target(id)
  465. if not t:
  466. print "notice: could not find main target '%s'" % id
  467. print "notice: assuming it's a name of file to create " ;
  468. explicitly_requested_files.append(id)
  469. else:
  470. targets.append(t)
  471. if not targets:
  472. targets = [projects.target(projects.module_name("."))]
  473. # FIXME: put this BACK.
  474. ## if [ option.get dump-generators : : true ]
  475. ## {
  476. ## generators.dump ;
  477. ## }
  478. # We wish to put config.log in the build directory corresponding
  479. # to Jamroot, so that the location does not differ depending on
  480. # directory where we do build. The amount of indirection necessary
  481. # here is scary.
  482. first_project = targets[0].project()
  483. first_project_root_location = first_project.get('project-root')
  484. first_project_root_module = manager.projects().load(first_project_root_location)
  485. first_project_root = manager.projects().target(first_project_root_module)
  486. first_build_build_dir = first_project_root.build_dir()
  487. configure.set_log_file(os.path.join(first_build_build_dir, "config.log"))
  488. virtual_targets = []
  489. global results_of_main_targets
  490. # Now that we have a set of targets to build and a set of property sets to
  491. # build the targets with, we can start the main build process by using each
  492. # property set to generate virtual targets from all of our listed targets
  493. # and any of their dependants.
  494. for p in expanded:
  495. manager.set_command_line_free_features(property_set.create(p.free()))
  496. for t in targets:
  497. try:
  498. g = t.generate(p)
  499. if not isinstance(t, ProjectTarget):
  500. results_of_main_targets.extend(g.targets())
  501. virtual_targets.extend(g.targets())
  502. except ExceptionWithUserContext, e:
  503. e.report()
  504. except Exception:
  505. raise
  506. # Convert collected virtual targets into actual raw Jam targets.
  507. for t in virtual_targets:
  508. actual_targets.append(t.actualize())
  509. # FIXME: restore
  510. ## # If XML data output has been requested prepare additional rules and targets
  511. ## # so we can hook into Jam to collect build data while its building and have
  512. ## # it trigger the final XML report generation after all the planned targets
  513. ## # have been built.
  514. ## if $(.out-xml)
  515. ## {
  516. ## # Get a qualified virtual target name.
  517. ## rule full-target-name ( target )
  518. ## {
  519. ## local name = [ $(target).name ] ;
  520. ## local project = [ $(target).project ] ;
  521. ## local project-path = [ $(project).get location ] ;
  522. ## return $(project-path)//$(name) ;
  523. ## }
  524. ## # Generate an XML file containing build statistics for each constituent.
  525. ## #
  526. ## rule out-xml ( xml-file : constituents * )
  527. ## {
  528. ## # Prepare valid XML header and footer with some basic info.
  529. ## local nl = "
  530. ## " ;
  531. ## local jam = [ version.jam ] ;
  532. ## local os = [ modules.peek : OS OSPLAT JAMUNAME ] "" ;
  533. ## local timestamp = [ modules.peek : JAMDATE ] ;
  534. ## local cwd = [ PWD ] ;
  535. ## local command = $(.sys.argv) ;
  536. ## local bb-version = [ version.boost-build ] ;
  537. ## .header on $(xml-file) =
  538. ## "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
  539. ## "$(nl)<build format=\"1.0\" version=\"$(bb-version)\">"
  540. ## "$(nl) <jam version=\"$(jam:J=.)\" />"
  541. ## "$(nl) <os name=\"$(os[1])\" platform=\"$(os[2])\"><![CDATA[$(os[3-]:J= )]]></os>"
  542. ## "$(nl) <timestamp><![CDATA[$(timestamp)]]></timestamp>"
  543. ## "$(nl) <directory><![CDATA[$(cwd)]]></directory>"
  544. ## "$(nl) <command><![CDATA[\"$(command:J=\" \")\"]]></command>"
  545. ## ;
  546. ## .footer on $(xml-file) =
  547. ## "$(nl)</build>" ;
  548. ## # Generate the target dependency graph.
  549. ## .contents on $(xml-file) +=
  550. ## "$(nl) <targets>" ;
  551. ## for local t in [ virtual-target.all-targets ]
  552. ## {
  553. ## local action = [ $(t).action ] ;
  554. ## if $(action)
  555. ## # If a target has no action, it has no dependencies.
  556. ## {
  557. ## local name = [ full-target-name $(t) ] ;
  558. ## local sources = [ $(action).sources ] ;
  559. ## local dependencies ;
  560. ## for local s in $(sources)
  561. ## {
  562. ## dependencies += [ full-target-name $(s) ] ;
  563. ## }
  564. ## local path = [ $(t).path ] ;
  565. ## local jam-target = [ $(t).actual-name ] ;
  566. ## .contents on $(xml-file) +=
  567. ## "$(nl) <target>"
  568. ## "$(nl) <name><![CDATA[$(name)]]></name>"
  569. ## "$(nl) <dependencies>"
  570. ## "$(nl) <dependency><![CDATA[$(dependencies)]]></dependency>"
  571. ## "$(nl) </dependencies>"
  572. ## "$(nl) <path><![CDATA[$(path)]]></path>"
  573. ## "$(nl) <jam-target><![CDATA[$(jam-target)]]></jam-target>"
  574. ## "$(nl) </target>"
  575. ## ;
  576. ## }
  577. ## }
  578. ## .contents on $(xml-file) +=
  579. ## "$(nl) </targets>" ;
  580. ## # Build $(xml-file) after $(constituents). Do so even if a
  581. ## # constituent action fails and regenerate the xml on every bjam run.
  582. ## INCLUDES $(xml-file) : $(constituents) ;
  583. ## ALWAYS $(xml-file) ;
  584. ## __ACTION_RULE__ on $(xml-file) = build-system.out-xml.generate-action ;
  585. ## out-xml.generate $(xml-file) ;
  586. ## }
  587. ## # The actual build actions are here; if we did this work in the actions
  588. ## # clause we would have to form a valid command line containing the
  589. ## # result of @(...) below (the name of the XML file).
  590. ## #
  591. ## rule out-xml.generate-action ( args * : xml-file
  592. ## : command status start end user system : output ? )
  593. ## {
  594. ## local contents =
  595. ## [ on $(xml-file) return $(.header) $(.contents) $(.footer) ] ;
  596. ## local f = @($(xml-file):E=$(contents)) ;
  597. ## }
  598. ## # Nothing to do here; the *real* actions happen in
  599. ## # out-xml.generate-action.
  600. ## actions quietly out-xml.generate { }
  601. ## # Define the out-xml file target, which depends on all the targets so
  602. ## # that it runs the collection after the targets have run.
  603. ## out-xml $(.out-xml) : $(actual-targets) ;
  604. ## # Set up a global __ACTION_RULE__ that records all the available
  605. ## # statistics about each actual target in a variable "on" the --out-xml
  606. ## # target.
  607. ## #
  608. ## rule out-xml.collect ( xml-file : target : command status start end user
  609. ## system : output ? )
  610. ## {
  611. ## local nl = "
  612. ## " ;
  613. ## # Open the action with some basic info.
  614. ## .contents on $(xml-file) +=
  615. ## "$(nl) <action status=\"$(status)\" start=\"$(start)\" end=\"$(end)\" user=\"$(user)\" system=\"$(system)\">" ;
  616. ## # If we have an action object we can print out more detailed info.
  617. ## local action = [ on $(target) return $(.action) ] ;
  618. ## if $(action)
  619. ## {
  620. ## local action-name = [ $(action).action-name ] ;
  621. ## local action-sources = [ $(action).sources ] ;
  622. ## local action-props = [ $(action).properties ] ;
  623. ## # The qualified name of the action which we created the target.
  624. ## .contents on $(xml-file) +=
  625. ## "$(nl) <name><![CDATA[$(action-name)]]></name>" ;
  626. ## # The sources that made up the target.
  627. ## .contents on $(xml-file) +=
  628. ## "$(nl) <sources>" ;
  629. ## for local source in $(action-sources)
  630. ## {
  631. ## local source-actual = [ $(source).actual-name ] ;
  632. ## .contents on $(xml-file) +=
  633. ## "$(nl) <source><![CDATA[$(source-actual)]]></source>" ;
  634. ## }
  635. ## .contents on $(xml-file) +=
  636. ## "$(nl) </sources>" ;
  637. ## # The properties that define the conditions under which the
  638. ## # target was built.
  639. ## .contents on $(xml-file) +=
  640. ## "$(nl) <properties>" ;
  641. ## for local prop in [ $(action-props).raw ]
  642. ## {
  643. ## local prop-name = [ MATCH ^<(.*)>$ : $(prop:G) ] ;
  644. ## .contents on $(xml-file) +=
  645. ## "$(nl) <property name=\"$(prop-name)\"><![CDATA[$(prop:G=)]]></property>" ;
  646. ## }
  647. ## .contents on $(xml-file) +=
  648. ## "$(nl) </properties>" ;
  649. ## }
  650. ## local locate = [ on $(target) return $(LOCATE) ] ;
  651. ## locate ?= "" ;
  652. ## .contents on $(xml-file) +=
  653. ## "$(nl) <jam-target><![CDATA[$(target)]]></jam-target>"
  654. ## "$(nl) <path><![CDATA[$(target:G=:R=$(locate))]]></path>"
  655. ## "$(nl) <command><![CDATA[$(command)]]></command>"
  656. ## "$(nl) <output><![CDATA[$(output)]]></output>" ;
  657. ## .contents on $(xml-file) +=
  658. ## "$(nl) </action>" ;
  659. ## }
  660. ## # When no __ACTION_RULE__ is set "on" a target, the search falls back to
  661. ## # the global module.
  662. ## module
  663. ## {
  664. ## __ACTION_RULE__ = build-system.out-xml.collect
  665. ## [ modules.peek build-system : .out-xml ] ;
  666. ## }
  667. ## IMPORT
  668. ## build-system :
  669. ## out-xml.collect
  670. ## out-xml.generate-action
  671. ## : :
  672. ## build-system.out-xml.collect
  673. ## build-system.out-xml.generate-action
  674. ## ;
  675. ## }
  676. j = option.get("jobs")
  677. if j:
  678. bjam.call("set-variable", PARALLELISM, j)
  679. k = option.get("keep-going", "true", "true")
  680. if k in ["on", "yes", "true"]:
  681. bjam.call("set-variable", "KEEP_GOING", "1")
  682. elif k in ["off", "no", "false"]:
  683. bjam.call("set-variable", "KEEP_GOING", "0")
  684. else:
  685. print "error: Invalid value for the --keep-going option"
  686. sys.exit()
  687. # The 'all' pseudo target is not strictly needed expect in the case when we
  688. # use it below but people often assume they always have this target
  689. # available and do not declare it themselves before use which may cause
  690. # build failures with an error message about not being able to build the
  691. # 'all' target.
  692. bjam.call("NOTFILE", "all")
  693. # And now that all the actual raw Jam targets and all the dependencies
  694. # between them have been prepared all that is left is to tell Jam to update
  695. # those targets.
  696. if explicitly_requested_files:
  697. # Note that this case can not be joined with the regular one when only
  698. # exact Boost Build targets are requested as here we do not build those
  699. # requested targets but only use them to construct the dependency tree
  700. # needed to build the explicitly requested files.
  701. # FIXME: add $(.out-xml)
  702. bjam.call("UPDATE", ["<e>%s" % x for x in explicitly_requested_files])
  703. elif cleanall:
  704. bjam.call("UPDATE", "clean-all")
  705. elif clean:
  706. manager.engine().set_update_action("common.Clean", "clean",
  707. actual_clean_targets(targets))
  708. bjam.call("UPDATE", "clean")
  709. else:
  710. # FIXME:
  711. #configure.print-configure-checks-summary ;
  712. if pre_build_hook:
  713. for h in pre_build_hook:
  714. h()
  715. bjam.call("DEPENDS", "all", actual_targets)
  716. ok = bjam.call("UPDATE_NOW", "all") # FIXME: add out-xml
  717. if post_build_hook:
  718. post_build_hook(ok)
  719. # Prevent automatic update of the 'all' target, now that
  720. # we have explicitly updated what we wanted.
  721. bjam.call("UPDATE")
  722. if manager.errors().count() == 0:
  723. return ["ok"]
  724. else:
  725. return []