1# Status: being ported by Steven Watanabe 2# Base revision: 47077 3# 4# Copyright (C) Andre Hentz 2003. Permission to copy, use, modify, sell and 5# distribute this software is granted provided this copyright notice appears in 6# all copies. This software is provided "as is" without express or implied 7# warranty, and with no claim as to its suitability for any purpose. 8# 9# Copyright (c) 2006 Rene Rivera. 10# 11# Copyright (c) 2008 Steven Watanabe 12# 13# Use, modification and distribution is subject to the Boost Software 14# License Version 1.0. (See accompanying file LICENSE_1_0.txt or 15# http://www.boost.org/LICENSE_1_0.txt) 16 17##import type ; 18##import generators ; 19##import feature ; 20##import errors ; 21##import scanner ; 22##import toolset : flags ; 23 24import os.path 25import re 26 27import bjam 28 29from b2.build import type, toolset, generators, scanner, feature 30from b2.exceptions import AlreadyDefined 31from b2.tools import builtin 32from b2.util import regex 33from b2.build.toolset import flags 34from b2.manager import get_manager 35from b2.util import utility 36 37__debug = None 38 39def debug(): 40 global __debug 41 if __debug is None: 42 __debug = "--debug-configuration" in bjam.variable("ARGV") 43 return __debug 44 45type.register('RC', ['rc']) 46 47def init(): 48 pass 49 50def configure (command = None, condition = None, options = None): 51 """ 52 Configures a new resource compilation command specific to a condition, 53 usually a toolset selection condition. The possible options are: 54 55 * <rc-type>(rc|windres) - Indicates the type of options the command 56 accepts. 57 58 Even though the arguments are all optional, only when a command, condition, 59 and at minimum the rc-type option are given will the command be configured. 60 This is so that callers don't have to check auto-configuration values 61 before calling this. And still get the functionality of build failures when 62 the resource compiler can't be found. 63 """ 64 rc_type = feature.get_values('<rc-type>', options) 65 if rc_type: 66 assert(len(rc_type) == 1) 67 rc_type = rc_type[0] 68 69 if command and condition and rc_type: 70 flags('rc.compile.resource', '.RC', condition, command) 71 flags('rc.compile.resource', '.RC_TYPE', condition, [rc_type.lower()]) 72 flags('rc.compile.resource', 'DEFINES', [], ['<define>']) 73 flags('rc.compile.resource', 'INCLUDES', [], ['<include>']) 74 if debug(): 75 print 'notice: using rc compiler ::', condition, '::', command 76 77engine = get_manager().engine() 78 79class RCAction: 80 """Class representing bjam action defined from Python. 81 The function must register the action to execute.""" 82 83 def __init__(self, action_name, function): 84 self.action_name = action_name 85 self.function = function 86 87 def __call__(self, targets, sources, property_set): 88 if self.function: 89 self.function(targets, sources, property_set) 90 91# FIXME: What is the proper way to dispatch actions? 92def rc_register_action(action_name, function = None): 93 global engine 94 if action_name in engine.actions: 95 raise AlreadyDefined("Bjam action %s is already defined" % action_name) 96 engine.actions[action_name] = RCAction(action_name, function) 97 98def rc_compile_resource(targets, sources, properties): 99 rc_type = bjam.call('get-target-variable', targets, '.RC_TYPE') 100 rc_type = rc_type[0] if rc_type else '' 101 global engine 102 engine.set_update_action('rc.compile.resource.' + rc_type, targets, sources, properties) 103 104rc_register_action('rc.compile.resource', rc_compile_resource) 105 106 107engine.register_action( 108 'rc.compile.resource.rc', 109 '"$(.RC)" -l 0x409 "-U$(UNDEFS)" "-D$(DEFINES)" -I"$(>:D)" -I"$(<:D)" -I"$(INCLUDES)" -fo "$(<)" "$(>)"') 110 111engine.register_action( 112 'rc.compile.resource.windres', 113 '"$(.RC)" "-U$(UNDEFS)" "-D$(DEFINES)" -I"$(>:D)" -I"$(<:D)" -I"$(INCLUDES)" -o "$(<)" -i "$(>)"') 114 115# FIXME: this was originally declared quietly 116engine.register_action( 117 'compile.resource.null', 118 'as /dev/null -o "$(<)"') 119 120# Since it's a common practice to write 121# exe hello : hello.cpp hello.rc 122# we change the name of object created from RC file, to 123# avoid conflict with hello.cpp. 124# The reason we generate OBJ and not RES, is that gcc does not 125# seem to like RES files, but works OK with OBJ. 126# See http://article.gmane.org/gmane.comp.lib.boost.build/5643/ 127# 128# Using 'register-c-compiler' adds the build directory to INCLUDES 129# FIXME: switch to generators 130builtin.register_c_compiler('rc.compile.resource', ['RC'], ['OBJ(%_res)'], []) 131 132__angle_include_re = "#include[ ]*<([^<]+)>" 133 134# Register scanner for resources 135class ResScanner(scanner.Scanner): 136 137 def __init__(self, includes): 138 scanner.__init__ ; 139 self.includes = includes 140 141 def pattern(self): 142 return "(([^ ]+[ ]+(BITMAP|CURSOR|FONT|ICON|MESSAGETABLE|RT_MANIFEST)" +\ 143 "[ ]+([^ \"]+|\"[^\"]+\"))|(#include[ ]*(<[^<]+>|\"[^\"]+\")))" ; 144 145 def process(self, target, matches, binding): 146 binding = binding[0] 147 angle = regex.transform(matches, "#include[ ]*<([^<]+)>") 148 quoted = regex.transform(matches, "#include[ ]*\"([^\"]+)\"") 149 res = regex.transform(matches, 150 "[^ ]+[ ]+(BITMAP|CURSOR|FONT|ICON|MESSAGETABLE|RT_MANIFEST)" +\ 151 "[ ]+(([^ \"]+)|\"([^\"]+)\")", [3, 4]) 152 153 # Icons and other includes may referenced as 154 # 155 # IDR_MAINFRAME ICON "res\\icon.ico" 156 # 157 # so we have to replace double backslashes to single ones. 158 res = [ re.sub(r'\\\\', '/', match) for match in res if match is not None ] 159 160 # CONSIDER: the new scoping rule seem to defeat "on target" variables. 161 g = bjam.call('get-target-variable', target, 'HDRGRIST')[0] 162 b = os.path.normpath(os.path.dirname(binding)) 163 164 # Attach binding of including file to included targets. 165 # When target is directly created from virtual target 166 # this extra information is unnecessary. But in other 167 # cases, it allows to distinguish between two headers of the 168 # same name included from different places. 169 # We don't need this extra information for angle includes, 170 # since they should not depend on including file (we can't 171 # get literal "." in include path). 172 g2 = g + "#" + b 173 174 g = "<" + g + ">" 175 g2 = "<" + g2 + ">" 176 angle = [g + x for x in angle] 177 quoted = [g2 + x for x in quoted] 178 res = [g2 + x for x in res] 179 180 all = angle + quoted 181 182 bjam.call('mark-included', target, all) 183 184 engine = get_manager().engine() 185 186 engine.add_dependency(target, res) 187 bjam.call('NOCARE', all + res) 188 engine.set_target_variable(angle, 'SEARCH', [utility.get_value(inc) for inc in self.includes]) 189 engine.set_target_variable(quoted, 'SEARCH', [b + utility.get_value(inc) for inc in self.includes]) 190 engine.set_target_variable(res, 'SEARCH', [b + utility.get_value(inc) for inc in self.includes]) 191 192 # Just propagate current scanner to includes, in a hope 193 # that includes do not change scanners. 194 get_manager().scanners().propagate(self, angle + quoted) 195 196scanner.register(ResScanner, 'include') 197type.set_scanner('RC', ResScanner) 198