summaryrefslogtreecommitdiff
path: root/jam-files/boost-build/build/modifiers.jam
blob: 6b009343331dee093b15e5fa99fb7abeaea1e531 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
# Copyright 2003 Rene Rivera
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)

# Modifiers are generalized generators that mutate targets in specific ways.
# This structure allows for grouping a variety of functionality in an
# orthogonal way to the functionality in toolsets, and without specifying
# more target variations. In turn the modifiers can be used as building
# blocks to implement simple requests, like the <version> feature.

import modules ;
import feature ;
import errors ;
import type ;
import "class" : new ;
import generators ;
import property ;
import virtual-target ;
import numbers ;
import sequence ;
import symlink ;
import property-set ;

# Base generator for creating targets that are modifications of existing
# targets.
#
class modifier : generator
{
    rule __init__ (
        id
        composing ?
        : source-types *
        : target-types-and-names +
        : requirements *
        )
    {
        generator.__init__ $(id) $(composing)
            : $(source-types)
            : $(target-types-and-names)
            : $(requirements) ;

        self.targets-in-progress = ;
    }

    # Wraps the generation of the target to call before and after rules to
    # affect the real target.
    #
    rule run ( project name ? : property-set : sources + )
    {
        local result ;
        local current-target = $(project)^$(name) ;
        if ! $(current-target) in $(self.targets-in-progress)
        {
            # Before modifications...
            local project_ =
                [ modify-project-before
                    $(project) $(name) : $(property-set) : $(sources) ] ;
            local name_ =
                [ modify-name-before
                    $(project) $(name) : $(property-set) : $(sources) ] ;
            local property-set_ =
                [ modify-properties-before
                    $(project) $(name) : $(property-set) : $(sources) ] ;
            local sources_ =
                [ modify-sources-before
                    $(project) $(name) : $(property-set) : $(sources) ] ;
            project = $(project_) ;
            name = $(name_) ;
            property-set = $(property-set_) ;
            sources = $(sources_) ;

            # Generate the real target...
            local target-type-p =
                [ property.select <main-target-type> : [ $(property-set).raw ] ] ;
            self.targets-in-progress += $(current-target) ;
            result =
                [ generators.construct $(project) $(name)
                    : $(target-type-p:G=)
                    : $(property-set)
                    : $(sources) ] ;
            self.targets-in-progress = $(self.targets-in-progress[1--2]) ;

            # After modifications...
            result =
                [ modify-target-after $(result)
                    : $(project) $(name)
                    : $(property-set)
                    : $(sources) ] ;
        }
        return $(result) ;
    }

    rule modify-project-before ( project name ? : property-set : sources + )
    {
        return $(project) ;
    }

    rule modify-name-before ( project name ? : property-set : sources + )
    {
        return $(name) ;
    }

    rule modify-properties-before ( project name ? : property-set : sources + )
    {
        return $(property-set) ;
    }

    rule modify-sources-before ( project name ? : property-set : sources + )
    {
        return $(sources) ;
    }

    rule modify-target-after ( target : project name ? : property-set : sources + )
    {
        return $(target) ;
    }

    # Utility, clones a file-target with optional changes to the name, type and
    # project of the target.
    # NOTE: This functionality should be moved, and generalized, to
    # virtual-targets.
    #
    rule clone-file-target ( target : new-name ? : new-type ? : new-project ? )
    {
        # Need a MUTCH better way to clone a target...
        new-name ?= [ $(target).name ] ;
        new-type ?= [ $(target).type ] ;
        new-project ?= [ $(target).project ] ;
        local result = [ new file-target $(new-name) : $(new-type) : $(new-project) ] ;

        if [ $(target).dependencies ] { $(result).depends [ $(target).dependencies ] ; }
        $(result).root [ $(target).root ] ;
        $(result).set-usage-requirements [ $(target).usage-requirements ] ;

        local action = [ $(target).action ] ;
        local action-class = [ modules.peek $(action) : __class__ ] ;

        local ps = [ $(action).properties ] ;
        local cloned-action = [ new $(action-class) $(result) :
          [ $(action).sources ] : [ $(action).action-name ] : $(ps) ] ;
        $(result).action $(cloned-action) ;

        return $(result) ;
    }
}


# A modifier that changes the name of a target, after it's generated, given a
# regular expression to split the name, and a set of token to insert between the
# split tokens of the name. This also exposes the target for other uses with a
# symlink to the original name (optionally).
#
class name-modifier : modifier
{
    rule __init__ ( )
    {
        # Apply ourselves to EXE targets, for now.
        modifier.__init__ name.modifier : : EXE LIB : <name-modify>yes ;
    }

    # Modifies the name, by cloning the target with the new name.
    #
    rule modify-target-after ( target : project name ? : property-set : sources + )
    {
        local result = $(target) ;

        local name-mod-p = [ property.select <name-modifier> : [ $(property-set).raw ] ] ;
        if $(name-mod-p)
        {
            local new-name = [ modify-name [ $(target).name ] : $(name-mod-p:G=) ] ;
            if $(new-name) != [ $(target).name ]
            {
                result = [ clone-file-target $(target) : $(new-name) ] ;
            }
            local expose-original-as-symlink = [ MATCH "<symlink>(.*)" : $(name-mod-p) ] ;
            if $(expose-original-as-symlink)
            {
                local symlink-t = [ new symlink-targets $(project) : $(name) : [ $(result).name ] ] ;
                result = [ $(symlink-t).construct $(result)
                    : [ property-set.create [ $(property-set).raw ] <symlink-location>build-relative ] ] ;
            }
        }

        return $(result) ;
    }

    # Do the transformation of the name.
    #
    rule modify-name ( name : modifier-spec + )
    {
        local match = [ MATCH "<match>(.*)" : $(modifier-spec) ] ;
        local name-parts = [ MATCH $(match) : $(name) ] ;
        local insertions = [ sequence.insertion-sort [ MATCH "(<[0123456789]+>.*)" : $(modifier-spec) ] ] ;
        local new-name-parts ;
        local insert-position = 1 ;
        while $(insertions)
        {
            local insertion = [ MATCH "<$(insert-position)>(.*)" : $(insertions[1]) ] ;
            if $(insertion)
            {
                new-name-parts += $(insertion) ;
                insertions = $(insertions[2-]) ;
            }
            new-name-parts += $(name-parts[1]) ;
            name-parts = $(name-parts[2-]) ;
            insert-position = [ numbers.increment $(insert-position) ] ;
        }
        new-name-parts += $(name-parts) ;
        return [ sequence.join $(new-name-parts) ] ;
    }

    rule optional-properties ( )
    {
        return <name-modify>yes ;
    }
}
feature.feature name-modifier : : free ;
feature.feature name-modify : no yes : incidental optional ;
generators.register [ new name-modifier ] ;

# Translates <version> property to a set of modification properties
# that are applied by the name-modifier, and symlink-modifier.
#
rule version-to-modifier ( property : properties * )
{
    return
        <name-modify>yes
            <name-modifier><match>"^([^.]*)(.*)" <name-modifier><2>.$(property:G=)
            <name-modifier><symlink>yes
        ;
}
feature.action <version> : version-to-modifier ;