• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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