1# Status: ported.
2# Base revision: 64488
3#
4# Copyright (c) 2010 Vladimir Prus.
5#
6# Use, modification and distribution is subject to the Boost Software
7# License Version 1.0. (See accompanying file LICENSE_1_0.txt or
8# http://www.boost.org/LICENSE_1_0.txt)
9
10# This module defines function to help with two main tasks:
11#
12# - Discovering build-time configuration for the purposes of adjusting
13#   build process.
14# - Reporting what is built, and how it is configured.
15
16import b2.build.property as property
17import b2.build.property_set as property_set
18
19from b2.build import targets as targets_
20
21from b2.manager import get_manager
22from b2.util.sequence import unique
23from b2.util import bjam_signature, value_to_jam, is_iterable
24
25import bjam
26import os
27
28__width = 30
29
30def set_width(width):
31    global __width
32    __width = 30
33
34__components = []
35__built_components = []
36__component_logs = {}
37__announced_checks = False
38
39__log_file = None
40__log_fd = -1
41
42def register_components(components):
43    """Declare that the components specified by the parameter exist."""
44    assert is_iterable(components)
45    __components.extend(components)
46
47def components_building(components):
48    """Declare that the components specified by the parameters will be build."""
49    assert is_iterable(components)
50    __built_components.extend(components)
51
52def log_component_configuration(component, message):
53    """Report something about component configuration that the user should better know."""
54    assert isinstance(component, basestring)
55    assert isinstance(message, basestring)
56    __component_logs.setdefault(component, []).append(message)
57
58def log_check_result(result):
59    assert isinstance(result, basestring)
60    global __announced_checks
61    if not __announced_checks:
62        print "Performing configuration checks"
63        __announced_checks = True
64
65    print result
66
67def log_library_search_result(library, result):
68    assert isinstance(library, basestring)
69    assert isinstance(result, basestring)
70    log_check_result(("    - %(library)s : %(result)s" % locals()).rjust(__width))
71
72
73def print_component_configuration():
74
75    print "\nComponent configuration:"
76    for c in __components:
77        if c in __built_components:
78            s = "building"
79        else:
80            s = "not building"
81        message = "    - %s)" % c
82        message = message.rjust(__width)
83        message += " : " + s
84        for m in __component_logs.get(c, []):
85            print "        -" + m
86    print ""
87
88__builds_cache = {}
89
90def builds(metatarget_reference, project, ps, what):
91    # Attempt to build a metatarget named by 'metatarget-reference'
92    # in context of 'project' with properties 'ps'.
93    # Returns non-empty value if build is OK.
94    assert isinstance(metatarget_reference, basestring)
95    assert isinstance(project, targets_.ProjectTarget)
96    assert isinstance(ps, property_set.PropertySet)
97    assert isinstance(what, basestring)
98
99    result = []
100
101    existing = __builds_cache.get((what, ps), None)
102    if existing is None:
103
104        result = False
105        __builds_cache[(what, ps)] = False
106
107        targets = targets_.generate_from_reference(
108            metatarget_reference, project, ps).targets()
109        jam_targets = []
110        for t in targets:
111            jam_targets.append(t.actualize())
112
113        x = ("    - %s" % what).rjust(__width)
114        if bjam.call("UPDATE_NOW", jam_targets, str(__log_fd), "ignore-minus-n"):
115            __builds_cache[(what, ps)] = True
116            result = True
117            log_check_result("%s: yes" % x)
118        else:
119            log_check_result("%s: no" % x)
120
121        return result
122    else:
123        return existing
124
125def set_log_file(log_file_name):
126    assert isinstance(log_file_name, basestring)
127    # Called by Boost.Build startup code to specify name of a file
128    # that will receive results of configure checks.  This
129    # should never be called by users.
130    global __log_file, __log_fd
131    dirname = os.path.dirname(log_file_name)
132    if not os.path.exists(dirname):
133        os.makedirs(dirname)
134    # Make sure to keep the file around, so that it's not
135    # garbage-collected and closed
136    __log_file = open(log_file_name, "w")
137    __log_fd = __log_file.fileno()
138
139# Frontend rules
140
141class CheckTargetBuildsWorker:
142
143    def __init__(self, target, true_properties, false_properties):
144        self.target = target
145        self.true_properties = property.create_from_strings(true_properties, True)
146        self.false_properties = property.create_from_strings(false_properties, True)
147
148    def check(self, ps):
149        assert isinstance(ps, property_set.PropertySet)
150        # FIXME: this should not be hardcoded. Other checks might
151        # want to consider different set of features as relevant.
152        toolset = ps.get('toolset')[0]
153        toolset_version_property = "<toolset-" + toolset + ":version>" ;
154        relevant = ps.get_properties('target-os') + \
155                   ps.get_properties("toolset") + \
156                   ps.get_properties(toolset_version_property) + \
157                   ps.get_properties("address-model") + \
158                   ps.get_properties("architecture")
159        rps = property_set.create(relevant)
160        t = get_manager().targets().current()
161        p = t.project()
162        if builds(self.target, p, rps, "%s builds" % self.target):
163            choosen = self.true_properties
164        else:
165            choosen = self.false_properties
166        return property.evaluate_conditionals_in_context(choosen, ps)
167
168@bjam_signature((["target"], ["true_properties", "*"], ["false_properties", "*"]))
169def check_target_builds(target, true_properties, false_properties):
170    worker = CheckTargetBuildsWorker(target, true_properties, false_properties)
171    value = value_to_jam(worker.check)
172    return "<conditional>" + value
173
174get_manager().projects().add_rule("check-target-builds", check_target_builds)
175
176
177