mirror of
https://github.com/boostorg/build.git
synced 2026-02-13 00:12:11 +00:00
438 lines
13 KiB
Python
438 lines
13 KiB
Python
# Status: being ported by Vladimir Prus.
|
|
|
|
# Copyright 2003, 2005 Dave Abrahams
|
|
# Copyright 2006 Rene Rivera
|
|
# Copyright 2003, 2004, 2005, 2006, 2007 Vladimir Prus
|
|
# Distributed under the Boost Software License, Version 1.0.
|
|
# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
|
|
|
|
from b2.build.engine import Engine
|
|
from b2.manager import Manager
|
|
from b2.util.path import glob
|
|
from b2.build import feature, property_set
|
|
import b2.build.virtual_target
|
|
from b2.build.targets import ProjectTarget
|
|
from b2.util.sequence import unique
|
|
import b2.build.build_request
|
|
from b2.build.errors import ExceptionWithUserContext
|
|
import b2.tools.common
|
|
|
|
import bjam
|
|
|
|
import os
|
|
import sys
|
|
|
|
# FIXME:
|
|
# Returns the location of the build system. The primary use case
|
|
# is building Boost, where it's sometimes needed to get location
|
|
# of other components (like BoostBook files), and it's convenient
|
|
# to use location relatively to Boost.Build path.
|
|
#rule location ( )
|
|
#{
|
|
# local r = [ modules.binding build-system ] ;
|
|
# return $(r:P) ;
|
|
#}
|
|
|
|
# FIXME:
|
|
|
|
def get_boolean_option(name):
|
|
match = "--" + name
|
|
if match in argv:
|
|
return 1
|
|
else:
|
|
return 0
|
|
|
|
def get_string_option(name):
|
|
match = "--" + name + "="
|
|
|
|
for arg in argv:
|
|
if arg.startswith(match):
|
|
return arg[len(match):]
|
|
return None
|
|
|
|
def home_directories():
|
|
if os.name == "nt":
|
|
result = set()
|
|
try:
|
|
result.add(os.environ['HOMEDRIVE'] + os.environ['HOMEPATH'])
|
|
result.add(os.environ['HOME'])
|
|
result.add(os.environ['USERPROFILE'])
|
|
except KeyError:
|
|
pass
|
|
return list(result)
|
|
else:
|
|
return [os.environ['HOME']]
|
|
|
|
ignore_config = 0
|
|
debug_config = 0
|
|
|
|
def load_config(manager, basename, path):
|
|
"""Unless ignore-config is set, search configuration
|
|
basename.jam in path and loads it. The jamfile module
|
|
for that file will be loaded 'basename'."""
|
|
|
|
if not ignore_config:
|
|
found = glob(path, [basename + ".jam"])
|
|
if found:
|
|
found = found[0]
|
|
if debug_config:
|
|
print "notice: searching '%s' for '%s.jam'" % (path, basename)
|
|
if found:
|
|
print "notice: loading %s.jam from %s" % (basename, found)
|
|
|
|
manager.projects().load_standalone(basename, found)
|
|
|
|
def main():
|
|
|
|
global argv
|
|
argv = bjam.variable("ARGV")
|
|
|
|
# FIXME: document this option.
|
|
if "--profiling" in argv:
|
|
import cProfile
|
|
import pstats
|
|
cProfile.runctx('main_real()', globals(), locals(), "stones.prof")
|
|
|
|
stats = pstats.Stats("stones.prof")
|
|
stats.strip_dirs()
|
|
stats.sort_stats('time', 'calls')
|
|
stats.print_callers(20)
|
|
else:
|
|
main_real()
|
|
|
|
def main_real():
|
|
|
|
global ignore_config
|
|
global debug_config
|
|
|
|
boost_build_path = bjam.variable("BOOST_BUILD_PATH")
|
|
|
|
engine = Engine()
|
|
|
|
global_build_dir = get_string_option("build-dir")
|
|
debug_config = get_boolean_option("debug-configuration")
|
|
|
|
manager = Manager(engine, global_build_dir)
|
|
|
|
# This module defines types and generator and what not,
|
|
# and depends on manager's existence
|
|
import b2.tools.builtin
|
|
|
|
|
|
# Check if we can load 'test-config.jam'. If we can, load it and
|
|
# ignore user configs.
|
|
|
|
test_config = glob(boost_build_path, ["test-config.jam"])
|
|
if test_config:
|
|
test_config = test_config[0]
|
|
|
|
if test_config:
|
|
if debug_config:
|
|
print "notice: loading testing-config.jam from '%s'" % test_config
|
|
print "notice: user-config.jam and site-config.jam will be ignored"
|
|
|
|
manager.projects().load_standalone("test-config", test_config)
|
|
|
|
|
|
ignore_config = test_config or get_boolean_option("ignore-config")
|
|
user_path = home_directories() + boost_build_path
|
|
|
|
site_path = ["/etc"] + user_path
|
|
if bjam.variable("OS") in ["NT", "CYGWIN"]:
|
|
site_path = [os.environ("SystemRoot")] + user_path
|
|
|
|
load_config(manager, "site-config", site_path)
|
|
|
|
user_config_path = get_string_option("user-config")
|
|
if not user_config_path:
|
|
user_config_path = os.environ.get("BOOST_BUILD_USER_CONFIG")
|
|
|
|
if user_config_path:
|
|
if debug_config:
|
|
print "Loading explicitly specifier user configuration file:"
|
|
print " %s" % user_config_path
|
|
|
|
manager.projects().load_standalone("user-config", user_config_path)
|
|
|
|
else:
|
|
load_config(manager, "user-config", user_path)
|
|
|
|
|
|
# FIXME:
|
|
## #
|
|
## # Autoconfigure toolsets based on any instances of --toolset=xx,yy,...zz or
|
|
## # toolset=xx,yy,...zz in the command line
|
|
## #
|
|
## local option-toolsets = [ regex.split-list [ MATCH ^--toolset=(.*) : $(argv) ] : "," ] ;
|
|
## local feature-toolsets = [ regex.split-list [ MATCH ^toolset=(.*) : $(argv) ] : "," ] ;
|
|
|
|
## # if the user specified --toolset=..., we need to add toolset=... to
|
|
## # the build request
|
|
## local extra-build-request ;
|
|
|
|
extra_build_request = []
|
|
|
|
## if ! $(ignore-config)
|
|
## {
|
|
## for local t in $(option-toolsets) $(feature-toolsets)
|
|
## {
|
|
## # Parse toolset-version/properties
|
|
## local (t-v,t,v) = [ MATCH (([^-/]+)-?([^/]+)?)/?.* : $(t) ] ;
|
|
## local toolset-version = $((t-v,t,v)[1]) ;
|
|
## local toolset = $((t-v,t,v)[2]) ;
|
|
## local version = $((t-v,t,v)[3]) ;
|
|
|
|
## if $(debug-config)
|
|
## {
|
|
## ECHO notice: [cmdline-cfg] Detected command-line request for
|
|
## $(toolset-version): toolset= \"$(toolset)\" "version= \""$(version)\" ;
|
|
## }
|
|
|
|
## local known ;
|
|
|
|
## # if the toolset isn't known, configure it now.
|
|
## if $(toolset) in [ feature.values <toolset> ]
|
|
## {
|
|
## known = true ;
|
|
## }
|
|
|
|
## if $(known) && $(version)
|
|
## && ! [ feature.is-subvalue toolset : $(toolset) : version : $(version) ]
|
|
## {
|
|
## known = ;
|
|
## }
|
|
|
|
## if ! $(known)
|
|
## {
|
|
## if $(debug-config)
|
|
## {
|
|
## ECHO notice: [cmdline-cfg] toolset $(toolset-version)
|
|
## not previously configured; configuring now ;
|
|
## }
|
|
## toolset.using $(toolset) : $(version) ;
|
|
## }
|
|
## else
|
|
## {
|
|
## if $(debug-config)
|
|
## {
|
|
## ECHO notice: [cmdline-cfg] toolset $(toolset-version) already configured ;
|
|
## }
|
|
## }
|
|
|
|
## # make sure we get an appropriate property into the build request in
|
|
## # case the user used the "--toolset=..." form
|
|
## if ! $(t) in $(argv)
|
|
## && ! $(t) in $(feature-toolsets)
|
|
## {
|
|
## if $(debug-config)
|
|
## {
|
|
## ECHO notice: [cmdline-cfg] adding toolset=$(t) "to build request." ;
|
|
## }
|
|
## extra-build-request += toolset=$(t) ;
|
|
## }
|
|
## }
|
|
## }
|
|
|
|
|
|
# FIXME:
|
|
## if USER_MODULE in [ RULENAMES ]
|
|
## {
|
|
## USER_MODULE site-config user-config ;
|
|
## }
|
|
|
|
if get_boolean_option("version"):
|
|
# FIXME: Move to a separate module. Include bjam
|
|
# verision.
|
|
print "Boost.Build M15 (Python port in development)"
|
|
sys.exit(0)
|
|
|
|
b2.tools.common.init(manager)
|
|
|
|
# We always load project in "." so that 'use-project' directives has
|
|
# any chance of been seen. Otherwise, we won't be able to refer to
|
|
# subprojects using target ids.
|
|
|
|
current_project = None
|
|
projects = manager.projects()
|
|
if projects.find(".", "."):
|
|
current_project = projects.target(projects.load("."))
|
|
|
|
# FIXME: revive this logic, when loading of gcc works
|
|
if not feature.values("<toolset>") and not ignore_config and 0:
|
|
default_toolset = "gcc" ;
|
|
if bjam.variable("OS") == "NT":
|
|
default_toolset = "msvc"
|
|
|
|
print "warning: No toolsets are configured." ;
|
|
print "warning: Configuring default toolset '%s'" % default_toolset
|
|
print "warning: If the default is wrong, you may not be able to build C++ programs."
|
|
print "warning: Use the \"--toolset=xxxxx\" option to override our guess."
|
|
print "warning: For more configuration options, please consult"
|
|
print "warning: http://boost.org/boost-build2/doc/html/bbv2/advanced/configuration.html"
|
|
|
|
projects.project_rules().using([default_toolset])
|
|
|
|
(target_ids, properties) = b2.build.build_request.from_command_line(
|
|
argv[1:] + extra_build_request)
|
|
|
|
if properties:
|
|
expanded = b2.build.build_request.expand_no_defaults(properties)
|
|
xexpanded = []
|
|
for e in expanded:
|
|
xexpanded.append(property_set.create(feature.split(e)))
|
|
expanded = xexpanded
|
|
else:
|
|
expanded = [property_set.empty()]
|
|
|
|
targets = []
|
|
|
|
clean = get_boolean_option("clean")
|
|
clean_all = get_boolean_option("clean-all")
|
|
|
|
|
|
bjam_targets = []
|
|
|
|
# Given a target id, try to find and return corresponding target.
|
|
# This is only invoked when there's no Jamfile in "."
|
|
# This code somewhat duplicates code in project-target.find but we can't reuse
|
|
# that code without project-targets instance.
|
|
def find_target (target_id):
|
|
split = target_id.split("//")
|
|
pm = None
|
|
if len(split) > 1:
|
|
pm = projects.find(split[0], ".")
|
|
else:
|
|
pm = projects.find(target_id, ".")
|
|
|
|
result = None
|
|
if pm:
|
|
result = projects.target(pm)
|
|
|
|
if len(split) > 1:
|
|
result = result.find(split[1])
|
|
|
|
if not current_project and not target_ids:
|
|
print "error: no Jamfile in current directory found, and no target references specified."
|
|
sys.exit(1)
|
|
|
|
for id in target_ids:
|
|
if id == "clean":
|
|
clean = 1
|
|
else:
|
|
t = None
|
|
if current_project:
|
|
t = current_project.find(id, no_error=1)
|
|
else:
|
|
t = find_target(id)
|
|
|
|
if not t:
|
|
print "notice: could not find main target '%s'" % id
|
|
print "notice: assuming it's a name of file to create " ;
|
|
bjam_targets.append(id)
|
|
else:
|
|
targets.append(t)
|
|
|
|
if not targets:
|
|
targets = [projects.target(projects.module_name("."))]
|
|
|
|
virtual_targets = []
|
|
|
|
# Virtual targets obtained when building main targets references on
|
|
# the command line. When running
|
|
#
|
|
# bjam --clean main_target
|
|
#
|
|
# we want to clean the files that belong only to that main target,
|
|
# so we need to record which targets are produced.
|
|
results_of_main_targets = []
|
|
|
|
for p in expanded:
|
|
manager.set_command_line_free_features(property_set.create(p.free()))
|
|
|
|
for t in targets:
|
|
try:
|
|
g = t.generate(p)
|
|
if not isinstance(t, ProjectTarget):
|
|
results_of_main_targets.extend(g.targets())
|
|
virtual_targets.extend(g.targets())
|
|
except ExceptionWithUserContext, e:
|
|
e.report()
|
|
except Exception:
|
|
raise
|
|
|
|
# The cleaning is tricky. Say, if
|
|
# user says:
|
|
#
|
|
# bjam --clean foo
|
|
#
|
|
# where 'foo' is a directory, then we want to clean targets
|
|
# which are in 'foo' or in any children Jamfiles, but not in any
|
|
# unrelated Jamfiles. So, we collect the list of project under which
|
|
# cleaning is allowed.
|
|
#
|
|
projects_to_clean = []
|
|
targets_to_clean = []
|
|
if clean or clean_all:
|
|
for t in targets:
|
|
if isinstance(t, ProjectTarget):
|
|
projects_to_clean.append(t.project_module())
|
|
|
|
for t in results_of_main_targets:
|
|
# Don't include roots or sources.
|
|
targets_to_clean += b2.build.virtual_target.traverse(t)
|
|
|
|
targets_to_clean = unique(targets_to_clean)
|
|
|
|
is_child_cache_ = {}
|
|
|
|
# Returns 'true' if 'project' is a child of 'current-project',
|
|
# possibly indirect, or is equal to 'project'.
|
|
# Returns 'false' otherwise.
|
|
def is_child (project):
|
|
|
|
r = is_child_cache_.get(project, None)
|
|
if not r:
|
|
if project in projects_to_clean:
|
|
r = 1
|
|
else:
|
|
parent = manager.projects().attribute(project, "parent-module")
|
|
if parent and parent != "user-config":
|
|
r = is_child(parent)
|
|
else:
|
|
r = 0
|
|
|
|
is_child_cache_[project] = r
|
|
|
|
return r
|
|
|
|
actual_targets = []
|
|
for t in virtual_targets:
|
|
actual_targets.append(t.actualize())
|
|
|
|
|
|
bjam.call("NOTFILE", "all")
|
|
bjam.call("DEPENDS", "all", actual_targets)
|
|
|
|
if bjam_targets:
|
|
bjam.call("UPDATE", ["<e>%s" % x for x in bjam_targets])
|
|
elif clean_all:
|
|
bjam.call("UPDATE", "clean-all")
|
|
elif clean:
|
|
to_clean = []
|
|
for t in manager.virtual_targets().all_targets():
|
|
p = t.project()
|
|
|
|
# Remove only derived targets.
|
|
if t.action() and \
|
|
(t in targets_to_clean or is_child(p.project_module())):
|
|
to_clean.append(t)
|
|
|
|
to_clean_actual = [t.actualize() for t in to_clean]
|
|
manager.engine().set_update_action('common.Clean', 'clean',
|
|
to_clean_actual, None)
|
|
|
|
bjam.call("UPDATE", "clean")
|
|
|
|
else:
|
|
bjam.call("UPDATE", "all")
|