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