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