File: type.jam

package info (click to toggle)
boost-build 2.0-m11-2
  • links: PTS
  • area: main
  • in suites: etch, etch-m68k
  • size: 4,624 kB
  • ctags: 2,387
  • sloc: ansic: 12,978; python: 5,209; xml: 4,782; cpp: 555; yacc: 456; sh: 237; makefile: 71
file content (373 lines) | stat: -rw-r--r-- 11,192 bytes parent folder | download
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
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
#  Copyright (C) Vladimir Prus 2002. Permission to copy, use, modify, sell and
#  distribute this software is granted provided this copyright notice appears in
#  all copies. This software is provided "as is" without express or implied
#  warranty, and with no claim as to its suitability for any purpose.

#  Deals with target type declaration and defines target class which supports
#  typed targets.

import feature ;
import generators : * ;
import "class" : new ;
import errors ;
import property ;
import scanner ;
import project ;

# This creates a circular dependency
# project-test1 -> project -> project-root -> builtin -> type -> targets -> project
# import targets ; 

# The feature is optional so that it never implicitly added.
# It's used only for internal purposes, and in all cases we
# want to explicitly use it.
feature.feature target-type : : composite optional ;

# feature.feature base-target-type : : composite optional ;
feature.feature main-target-type : : optional incidental ;
feature.feature base-target-type : : composite optional free ;
# feature.feature main-target-type : : composite optional incidental ;

# Registers a target type, possible derived from a 'base-type'. 
# If 'suffixes' are provided, they given all the suffixes that mean a file is of 'type'.
# Also, the first element gives the suffix to be used when constructing and object of
# 'type'.
rule register ( type : suffixes * : base-type ? )
{
    # Type names cannot contain hyphens, because when used as
    # feature-values they will be interpreted as composite features
    # which need to be decomposed.
    switch $(type)
    {
        case *-* : errors.error "type name \"$(type)\" contains a hyphen" ;
    }
    
    if $(type) in $(.types)
    {
        errors.error "Type $(type) is already registered." ;
    }
    else
    {
        .types += $(type) ;
        .bases.$(type) = $(base-type) ;
        .derived.$(base-type) += $(type) ;        

        if $(suffixes)-not-empty
        {            
            # Generated targets of 'type' will use the first of 'suffixes'
            # (this may be overriden)            
            $(.suffixes).insert <target-type>$(type) : $(suffixes[1]) ;
            # Specify mapping from suffixes to type
            register-suffixes $(suffixes) : $(type) ;
        }
        
        feature.extend target-type : $(type) ;
        feature.extend main-target-type : $(type) ;
        
        feature.compose <target-type>$(type) : $(base-type:G=<base-target-type>) ; 
        feature.extend base-target-type : $(type) ;
#        feature.compose <target-type>$(type) : <base-target-type>$(type) ;
        feature.compose <base-target-type>$(type) : <base-target-type>$(base-type) ;

        # We used to declare main target rule only when 'main' parameter
        # is specified. However, it's hard to decide that a type *never*
        # will need a main target rule and so from time to time we needed
        # to make yet another type 'main'. So, now main target rule is defined
        # for each type.
        main-rule-name = [ type-to-rule-name $(type) ] ;            
        .main-target-type.$(main-rule-name) = $(type) ;    
            
        IMPORT $(__name__) : main-target-rule : : $(main-rule-name) ;
    }
}

# Given type, returns name of main target rule which creates
# targets of that type.
rule type-to-rule-name ( type )
{
    # Lowercase everything. Convert underscores to dashes.ame.
    import regex ;
    local n = [ regex.split $(type:L) "_" ] ;
    n = $(n:J=-) ;
    return $(n) ;
}

# Returns a type, given the name of a main rule.
rule type-from-rule-name ( main-target-name )
{
    return $(.main-target-type.$(main-target-name)) ;
}



# Specifies that targets with suffix from 'suffixes' has the type 'type'. 
# If different type is already specified for any of syffixes,
# issues an error.
rule register-suffixes ( suffixes + : type )
{
    for local s in $(suffixes)
    {        
        if ! $(.type.$(s)) 
        {
            .type.$(s) = $(type) ; 
        }
        else if $(.type.$(s)) != type 
        {
            errors.error Attempting to specify type for suffix \"$(s)\" 
              : "Old type $(.type.$(s)), New type $(type)" ;
        }
    }            
}


# Returns true iff type has been registered.
rule registered ( type )
{
    if $(type) in $(.types)
    {
        return true ;
    }
}

# Issues an error if 'type' is unknown.
rule validate ( type )
{
    if ! $(type) in $(.types)
    {
        errors.error "Unknown target type $(type)" ;
    }    
}


# Sets a scanner class that will be used for this 'type'.
rule set-scanner ( type : scanner )
{
    if ! $(type) in $(.types)
    {
        error "Type" $(type) "is not declared" ;
    }    
    .scanner.$(type) = $(scanner) ;
}

# Returns a scanner instance appropriate to 'type' and 'properties'.
rule get-scanner ( type : property-set )
{
    if $(.scanner.$(type)) {
        return [ scanner.get $(.scanner.$(type)) : $(property-set) ] ;
    }    
}

# returns  type and all of its bases in order of their distance from type.
rule all-bases ( type )
{
    local result = $(type) ;
    while $(type)
    {
        type = $(.bases.$(type)) ;
        result += $(type) ;
    }
    return $(result) ;
}

rule all-derived ( type )
{
    local result = $(type) ;
    for local d in $(.derived.$(type))
    {
        result += [ all-derived $(d) ] ;
    }
    return $(result) ;      
}


# Returns true if 'type' has 'base' as its direct or
# indirect base.
rule is-derived ( type base )
{
    if $(base) in [ all-bases $(type) ]
    {
        return true ;
    }
}

# Returns true if 'type' is either derived from 'base',
# or 'type' is equal to 'base'.
rule is-subtype ( type base )
{
    if $(type) = $(base)
    {
        return true ;
    }
    else
    {
        return [ is-derived $(type) $(base) ] ;
    }    
}


# Store suffixes for generated targets
.suffixes = [ new property-map ] ;

# Store prefixes for generated targets (e.g. "lib" for library)
.prefixes = [ new property-map ] ;


# Sets a target suffix that should be used when generating target 
# of 'type' with the specified properties. Can be called with
# empty properties if no suffix for 'type' was specified yet.
# This does not automatically specify that files 'suffix' have
# 'type' --- two different types can use the same suffix for
# generating, but only one type should be auto-detected for
# a file with that suffix. User should explicitly specify which
# one.
#
# The 'suffix' parameter can be empty string ("") to indicate that
# no suffix should be used.
rule set-generated-target-suffix ( type : properties * : suffix )
{
    set-generated-target-ps  suffix : $(type) : $(properties) : $(suffix) ;
}    

# Change the suffix previously registered for this type/properties 
# combination. If suffix is not yet specified, sets it.
rule change-generated-target-suffix ( type : properties * : suffix )
{        
    change-generated-target-ps  suffix : $(type) : $(properties) : $(suffix) ;
}

rule generated-target-suffix ( type : property-set )
{
    return [ generated-target-ps  suffix : $(type) : $(property-set) ] ;
}

# Sets a target prefix that should be used when generating target 
# of 'type' with the specified properties. Can be called with
# empty properties if no prefix for 'type' was specified yet.
#
# The 'prefix' parameter can be empty string ("") to indicate that
# no prefix should be used.
#
# Example usage is for library names that have to have a "lib"
# prefix as in unix.
rule set-generated-target-prefix ( type : properties * : prefix )
{
    set-generated-target-ps  prefix : $(type) : $(properties) : $(prefix) ;
}    

# Change the prefix previously registered for this type/properties 
# combination. If prefix is not yet specified, sets it.
rule change-generated-target-prefix ( type : properties * : prefix )
{        
    change-generated-target-ps  prefix : $(type) : $(properties) : $(prefix) ;
}

rule generated-target-prefix ( type : property-set )
{
    return [ generated-target-ps  prefix : $(type) : $(property-set) ] ;
}

# Common rules for prefix/suffix provisioning follow

rule set-generated-target-ps ( ps : type : properties * : psval )
{
    properties = <target-type>$(type) $(properties) ;
    $(.$(ps)es).insert $(properties) : $(psval) ;
}    

rule change-generated-target-ps ( ps : type : properties * : psval )
{        
    properties = <target-type>$(type) $(properties) ;    
    local prev = [ $(.$(ps)es).find-replace $(properties) : $(psval) ] ;
    if ! $(prev)
    {
        set-generated-target-ps $(ps) : $(type) : $(properties) : $(psval) ;
    }    
}

# Returns either prefix or suffix (as indicated by 'ps') that
# should be used when generating target of 'type' with the specified properties.  
# Parameter 'ps' can be either "prefix" or "suffix".  If no prefix/suffix is 
# specified for 'type', returns prefix/suffix for base type, if any.
rule generated-target-ps-real ( ps : type : properties * )
{
    local result ;
    local found ;
    while $(type) && ! $(found)
    {
        result = [ $(.$(ps)es).find <target-type>$(type) $(properties) ] ;
        # If the prefix/suffix is explicitly set to empty string, 
        # we consider prefix/suffix to be found. If we did not compare with "", 
        # there would be no way for user to set empty prefix/suffix.
        if $(result)-is-not-empty
        {
            found = true ;
        }
        type = $(.bases.$(type)) ;
    }
    if $(result) = "" 
    {
        result = ;
    }
    return $(result) ;
}


rule generated-target-ps ( ps : type : property-set )
{
    local key = .$(ps).$(type).$(property-set) ;
    local v = $($(key)) ;
    if ! $(v)
    {
        v = [ generated-target-ps-real $(ps) : $(type)
          : [ $(property-set).raw ] ] ;
        if ! $(v)
        {
            v = none ;
        }
        $(key) = $(v) ;
    }
    
    if $(v) != none
    {
        return $(v) ;
    }        
}


# Returns file type given it's name. If there are several dots in filename,
# tries each suffix. E.g. for name of "file.so.1.2" suffixes "2", "1", and 
# "so"  will be tried.
rule type ( filename ) 
{
    local type ;
    while ! $(type) && $(filename:S) 
    {
        local suffix = $(filename:S) ;
        type = $(.type$(suffix)) ;
        filename = $(filename:S=) ;
    }
    return $(type) ;
}



rule main-target-rule ( name : sources * : requirements * : default-build * 
                        : usage-requirements * )
{
    # First find required target type, which is equal to the name used
    # to invoke us.
    local bt = [ BACKTRACE 1 ] ;
    local rulename = $(bt[4]) ;
    
    # This rule may be only called from Jamfile, and therefore, 
    # CALLER_MODULE is Jamfile module, which is used to denote 
    # a project.
    local project = [ project.current ] ;
        
    # This is a circular module dependency, so it must be imported here
    import targets ;
    return [ targets.create-typed-target $(.main-target-type.$(rulename)) : $(project)
      : $(name) : $(sources) : $(requirements) 
      : $(default-build) : $(usage-requirements) ] ;        
}