1# Status: ported.
2# Base revison: 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
19import b2.build.targets
20
21from b2.manager import get_manager
22from b2.util.sequence import unique
23from b2.util import bjam_signature, value_to_jam
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    __components.extend(components)
45
46def components_building(components):
47    """Declare that the components specified by the parameters will be build."""
48    __built_components.extend(components)
49
50def log_component_configuration(component, message):
51    """Report something about component configuration that the user should better know."""
52    __component_logs.setdefault(component, []).append(message)
53
54def log_check_result(result):
55    global __announced_checks
56    if not __announced_checks:
57        print "Performing configuration checks"
58        __announced_checks = True
59
60    print result
61
62def log_library_search_result(library, result):
63    log_check_result(("    - %(library)s : %(result)s" % locals()).rjust(width))
64
65
66def print_component_configuration():
67
68    print "\nComponent configuration:"
69    for c in __components:
70        if c in __built_components:
71            s = "building"
72        else:
73            s = "not building"
74        message = "    - %s)" % c
75        message = message.rjust(__width)
76        message += " : " + s
77        for m in __component_logs.get(c, []):
78            print "        -" + m
79    print ""
80
81__builds_cache = {}
82
83def builds(metatarget_reference, project, ps, what):
84    # Attempt to build a metatarget named by 'metatarget-reference'
85    # in context of 'project' with properties 'ps'.
86    # Returns non-empty value if build is OK.
87
88    result = []
89
90    existing = __builds_cache.get((what, ps), None)
91    if existing is None:
92
93        result = False
94        __builds_cache[(what, ps)] = False
95
96        targets = b2.build.targets.generate_from_reference(
97            metatarget_reference, project, ps).targets()
98        jam_targets = []
99        for t in targets:
100            jam_targets.append(t.actualize())
101
102        x = ("    - %s" % what).rjust(__width)
103        if bjam.call("UPDATE_NOW", jam_targets, str(__log_fd), "ignore-minus-n"):
104            __builds_cache[(what, ps)] = True
105            result = True
106            log_check_result("%s: yes" % x)
107        else:
108            log_check_result("%s: no" % x)
109
110        return result
111    else:
112        return existing
113
114def set_log_file(log_file_name):
115    # Called by Boost.Build startup code to specify name of a file
116    # that will receive results of configure checks.  This
117    # should never be called by users.
118    global __log_file, __log_fd
119    dirname = os.path.dirname(log_file_name)
120    if not os.path.exists(dirname):
121        os.makedirs(dirname)
122    # Make sure to keep the file around, so that it's not
123    # garbage-collected and closed
124    __log_file = open(log_file_name, "w")
125    __log_fd = __log_file.fileno()
126
127# Frontend rules
128
129class CheckTargetBuildsWorker:
130
131    def __init__(self, target, true_properties, false_properties):
132        self.target = target
133        self.true_properties = property.create_from_strings(true_properties, True)
134        self.false_properties = property.create_from_strings(false_properties, True)
135
136    def check(self, ps):
137
138        # FIXME: this should not be hardcoded. Other checks might
139        # want to consider different set of features as relevant.
140        toolset = ps.get('toolset')[0]
141        toolset_version_property = "<toolset-" + toolset + ":version>" ;
142        relevant = ps.get_properties('target-os') + \
143                   ps.get_properties("toolset") + \
144                   ps.get_properties(toolset_version_property) + \
145                   ps.get_properties("address-model") + \
146                   ps.get_properties("architecture")
147        rps = property_set.create(relevant)
148        t = get_manager().targets().current()
149        p = t.project()
150        if builds(self.target, p, rps, "%s builds" % self.target):
151            choosen = self.true_properties
152        else:
153            choosen = self.false_properties
154        return property.evaluate_conditionals_in_context(choosen, ps)
155
156@bjam_signature((["target"], ["true_properties", "*"], ["false_properties", "*"]))
157def check_target_builds(target, true_properties, false_properties):
158    worker = CheckTargetBuildsWorker(target, true_properties, false_properties)
159    value = value_to_jam(worker.check)
160    return "<conditional>" + value
161
162get_manager().projects().add_rule("check-target-builds", check_target_builds)
163
164
165