• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright Pedro Ferreira 2005.
2# Copyright Vladimir Prus 2007.
3# Distributed under the Boost
4# Software License, Version 1.0. (See accompanying
5# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6
7bjam_interface = __import__('bjam')
8
9import operator
10import re
11
12import b2.build.property_set as property_set
13
14from b2.util import set_jam_action, is_iterable
15
16class BjamAction(object):
17    """Class representing bjam action defined from Python."""
18
19    def __init__(self, action_name, function, has_command=False):
20        assert isinstance(action_name, basestring)
21        assert callable(function) or function is None
22        self.action_name = action_name
23        self.function = function
24        self.has_command = has_command
25
26    def __call__(self, targets, sources, property_set_):
27        assert is_iterable(targets)
28        assert is_iterable(sources)
29        assert isinstance(property_set_, property_set.PropertySet)
30        if self.has_command:
31            # Bjam actions defined from Python have only the command
32            # to execute, and no associated jam procedural code. So
33            # passing 'property_set' to it is not necessary.
34            bjam_interface.call("set-update-action", self.action_name,
35                                targets, sources, [])
36        if self.function:
37            self.function(targets, sources, property_set_)
38
39class BjamNativeAction(BjamAction):
40    """Class representing bjam action defined by Jam code.
41
42    We still allow to associate a Python callable that will
43    be called when this action is installed on any target.
44    """
45
46    def __call__(self, targets, sources, property_set_):
47        assert is_iterable(targets)
48        assert is_iterable(sources)
49        assert isinstance(property_set_, property_set.PropertySet)
50        if self.function:
51            self.function(targets, sources, property_set_)
52
53        p = []
54        if property_set:
55            p = property_set_.raw()
56
57        set_jam_action(self.action_name, targets, sources, p)
58
59action_modifiers = {"updated": 0x01,
60                    "together": 0x02,
61                    "ignore": 0x04,
62                    "quietly": 0x08,
63                    "piecemeal": 0x10,
64                    "existing": 0x20}
65
66class Engine:
67    """ The abstract interface to a build engine.
68
69    For now, the naming of targets, and special handling of some
70    target variables like SEARCH and LOCATE make this class coupled
71    to bjam engine.
72    """
73    def __init__ (self):
74        self.actions = {}
75
76    def add_dependency (self, targets, sources):
77        """Adds a dependency from 'targets' to 'sources'
78
79        Both 'targets' and 'sources' can be either list
80        of target names, or a single target name.
81        """
82        if isinstance (targets, str):
83            targets = [targets]
84        if isinstance (sources, str):
85            sources = [sources]
86        assert is_iterable(targets)
87        assert is_iterable(sources)
88
89        for target in targets:
90            for source in sources:
91                self.do_add_dependency (target, source)
92
93    def get_target_variable(self, targets, variable):
94        """Gets the value of `variable` on set on the first target in `targets`.
95
96        Args:
97            targets (str or list): one or more targets to get the variable from.
98            variable (str): the name of the variable
99
100        Returns:
101             the value of `variable` set on `targets` (list)
102
103        Example:
104
105            >>> ENGINE = get_manager().engine()
106            >>> ENGINE.set_target_variable(targets, 'MY-VAR', 'Hello World')
107            >>> ENGINE.get_target_variable(targets, 'MY-VAR')
108            ['Hello World']
109
110        Equivalent Jam code:
111
112            MY-VAR on $(targets) = "Hello World" ;
113            echo [ on $(targets) return $(MY-VAR) ] ;
114            "Hello World"
115        """
116        if isinstance(targets, str):
117            targets = [targets]
118        assert is_iterable(targets)
119        assert isinstance(variable, basestring)
120
121        return bjam_interface.call('get-target-variable', targets, variable)
122
123    def set_target_variable (self, targets, variable, value, append=0):
124        """ Sets a target variable.
125
126        The 'variable' will be available to bjam when it decides
127        where to generate targets, and will also be available to
128        updating rule for that 'taret'.
129        """
130        if isinstance (targets, str):
131            targets = [targets]
132        if isinstance(value, str):
133            value = [value]
134
135        assert is_iterable(targets)
136        assert isinstance(variable, basestring)
137        assert is_iterable(value)
138
139        if targets:
140            if append:
141                bjam_interface.call("set-target-variable", targets, variable, value, "true")
142            else:
143                bjam_interface.call("set-target-variable", targets, variable, value)
144
145    def set_update_action (self, action_name, targets, sources, properties=None):
146        """ Binds a target to the corresponding update action.
147            If target needs to be updated, the action registered
148            with action_name will be used.
149            The 'action_name' must be previously registered by
150            either 'register_action' or 'register_bjam_action'
151            method.
152        """
153        if isinstance(targets, str):
154            targets = [targets]
155        if isinstance(sources, str):
156            sources = [sources]
157        if properties is None:
158            properties = property_set.empty()
159        assert isinstance(action_name, basestring)
160        assert is_iterable(targets)
161        assert is_iterable(sources)
162        assert(isinstance(properties, property_set.PropertySet))
163
164        self.do_set_update_action (action_name, targets, sources, properties)
165
166    def register_action (self, action_name, command='', bound_list = [], flags = [],
167                         function = None):
168        """Creates a new build engine action.
169
170        Creates on bjam side an action named 'action_name', with
171        'command' as the command to be executed, 'bound_variables'
172        naming the list of variables bound when the command is executed
173        and specified flag.
174        If 'function' is not None, it should be a callable taking three
175        parameters:
176            - targets
177            - sources
178            - instance of the property_set class
179        This function will be called by set_update_action, and can
180        set additional target variables.
181        """
182        assert isinstance(action_name, basestring)
183        assert isinstance(command, basestring)
184        assert is_iterable(bound_list)
185        assert is_iterable(flags)
186        assert function is None or callable(function)
187
188        bjam_flags = reduce(operator.or_,
189                            (action_modifiers[flag] for flag in flags), 0)
190
191        # We allow command to be empty so that we can define 'action' as pure
192        # python function that would do some conditional logic and then relay
193        # to other actions.
194        assert command or function
195        if command:
196            bjam_interface.define_action(action_name, command, bound_list, bjam_flags)
197
198        self.actions[action_name] = BjamAction(
199            action_name, function, has_command=bool(command))
200
201    def register_bjam_action (self, action_name, function=None):
202        """Informs self that 'action_name' is declared in bjam.
203
204        From this point, 'action_name' is a valid argument to the
205        set_update_action method. The action_name should be callable
206        in the global module of bjam.
207        """
208
209        # We allow duplicate calls to this rule for the same
210        # action name.  This way, jamfile rules that take action names
211        # can just register them without specially checking if
212        # action is already registered.
213        assert isinstance(action_name, basestring)
214        assert function is None or callable(function)
215        if action_name not in self.actions:
216            self.actions[action_name] = BjamNativeAction(action_name, function)
217
218    # Overridables
219
220
221    def do_set_update_action (self, action_name, targets, sources, property_set_):
222        assert isinstance(action_name, basestring)
223        assert is_iterable(targets)
224        assert is_iterable(sources)
225        assert isinstance(property_set_, property_set.PropertySet)
226        action = self.actions.get(action_name)
227        if not action:
228            raise Exception("No action %s was registered" % action_name)
229        action(targets, sources, property_set_)
230
231    def do_set_target_variable (self, target, variable, value, append):
232        assert isinstance(target, basestring)
233        assert isinstance(variable, basestring)
234        assert is_iterable(value)
235        assert isinstance(append, int)  # matches bools
236        if append:
237            bjam_interface.call("set-target-variable", target, variable, value, "true")
238        else:
239            bjam_interface.call("set-target-variable", target, variable, value)
240
241    def do_add_dependency (self, target, source):
242        assert isinstance(target, basestring)
243        assert isinstance(source, basestring)
244        bjam_interface.call("DEPENDS", target, source)
245
246
247