Imported Upstream version 1.57.0
[platform/upstream/boost.git] / tools / build / src / tools / stage.jam
1 # Copyright 2003 Dave Abrahams
2 # Copyright 2005, 2006 Rene Rivera
3 # Copyright 2002, 2003, 2004, 2005, 2006 Vladimir Prus
4 # Distributed under the Boost Software License, Version 1.0.
5 # (See accompanying file LICENSE_1_0.txt or copy at
6 # http://www.boost.org/LICENSE_1_0.txt)
7
8 # This module defines the 'install' rule, used to copy a set of targets to a
9 # single location.
10
11 import "class" : new ;
12 import feature ;
13 import generators ;
14 import path ;
15 import project ;
16 import targets ;
17 import type ;
18 import types/register ;
19 import virtual-target ;
20
21
22 feature.feature <install-dependencies> : off on : incidental      ;
23 feature.feature <install-type>         :        : free incidental ;
24 feature.feature <install-source-root>  :        : free path       ;
25 feature.feature <so-version>           :        : free incidental ;
26
27 # If 'on', version symlinks for shared libraries will not be created. Affects
28 # Unix builds only.
29 feature.feature <install-no-version-symlinks> : on : optional incidental ;
30
31
32 class install-target-class : basic-target
33 {
34     import "class" : new ;
35     import feature ;
36     import generators ;
37     import path ;
38     import project ;
39     import property ;
40     import property-set ;
41     import stage ;
42     import type ;
43
44     rule __init__ ( name-and-dir : project : sources * : requirements * :
45         default-build * : usage-requirements * )
46     {
47         # The usage-requirements specified here are ignored but are taken as a
48         # parameter to have this metatarget class have the same standard
49         # instantiation interface as all the other Boost Build metatarget
50         # classes.
51         basic-target.__init__ $(name-and-dir) : $(project) : $(sources) :
52             $(requirements) : $(default-build) ;
53     }
54
55     # If <location> is not set, sets it based on the project data.
56     #
57     rule update-location ( property-set )
58     {
59         local loc = [ $(property-set).get <location> ] ;
60         if ! $(loc)
61         {
62             loc = [ path.root $(self.name) [ $(self.project).get location ] ] ;
63             property-set = [ $(property-set).add-raw $(loc:G=<location>) ] ;
64         }
65
66         return $(property-set) ;
67     }
68
69     # Takes a target that is installed and a property set which is used when
70     # installing.
71     #
72     rule adjust-properties ( target : build-property-set )
73     {
74         local ps-raw ;
75         local a = [ $(target).action ] ;
76         if $(a)
77         {
78             local ps = [ $(a).properties ] ;
79             ps-raw = [ $(ps).raw ] ;
80
81             # Unless <hardcode-dll-paths>true is in properties, which can happen
82             # only if the user has explicitly requested it, nuke all <dll-path>
83             # properties.
84             if [ $(build-property-set).get <hardcode-dll-paths> ] != true
85             {
86                 ps-raw = [ property.change $(ps-raw) : <dll-path> ] ;
87             }
88
89             # If any <dll-path> properties were specified for installing, add
90             # them.
91             local l = [ $(build-property-set).get <dll-path> ] ;
92             ps-raw += $(l:G=<dll-path>) ;
93
94             # Also copy <linkflags> feature from current build set, to be used
95             # for relinking.
96             local l = [ $(build-property-set).get <linkflags> ] ;
97             ps-raw += $(l:G=<linkflags>) ;
98
99             # Remove the <tag> feature on original targets.
100             ps-raw = [ property.change $(ps-raw) : <tag> ] ;
101
102             # And <location>. If stage target has another stage target in
103             # sources, then we shall get virtual targets with the <location>
104             # property set.
105             ps-raw = [ property.change $(ps-raw) : <location> ] ;
106         }
107
108         local d = [ $(build-property-set).get <dependency> ] ;
109         ps-raw += $(d:G=<dependency>) ;
110
111         local d = [ $(build-property-set).get <location> ] ;
112         ps-raw += $(d:G=<location>) ;
113
114         local ns = [ $(build-property-set).get <install-no-version-symlinks> ] ;
115         ps-raw += $(ns:G=<install-no-version-symlinks>) ;
116
117         local d = [ $(build-property-set).get <install-source-root> ] ;
118         # Make the path absolute: we shall use it to compute relative paths and
119         # making the path absolute will help.
120         if $(d)
121         {
122             d = [ path.root $(d) [ path.pwd ] ] ;
123             ps-raw += $(d:G=<install-source-root>) ;
124         }
125
126         if $(ps-raw)
127         {
128             return [ property-set.create $(ps-raw) ]  ;
129         }
130         else
131         {
132             return [ property-set.empty ] ;
133         }
134     }
135
136     rule construct ( name : source-targets * : property-set )
137     {
138         source-targets = [ targets-to-stage $(source-targets) :
139             $(property-set) ] ;
140
141         property-set = [ update-location $(property-set) ] ;
142
143         local ename = [ $(property-set).get <name> ] ;
144
145         if $(ename) && $(source-targets[2])
146         {
147             import errors : error : $(__name__) : errors.error ;
148             errors.error When <name> property is used "in" 'install', only one
149                 source is allowed. ;
150         }
151
152         local result ;
153         for local i in $(source-targets)
154         {
155             local staged-targets ;
156
157             local new-properties = [ adjust-properties $(i) :
158                 $(property-set) ] ;
159
160             # See if something special should be done when staging this type. It
161             # is indicated by the presence of a special "INSTALLED_" type.
162             local t = [ $(i).type ] ;
163             if $(t) && [ type.registered INSTALLED_$(t) ]
164             {
165                 if $(ename)
166                 {
167                     import errors : error : $(__name__) : errors.error ;
168                     errors.error In 'install': <name> property specified with
169                         target that requires relinking. ;
170                 }
171                 else
172                 {
173                     local targets = [ generators.construct $(self.project)
174                         $(name) : INSTALLED_$(t) : $(new-properties) : $(i) ] ;
175                     staged-targets += $(targets[2-]) ;
176                 }
177             }
178             else
179             {
180                 staged-targets = [ stage.copy-file $(self.project) $(ename) :
181                     $(i) : $(new-properties) ] ;
182             }
183
184             if ! $(staged-targets)
185             {
186                 import errors : error : $(__name__) : errors.error ;
187                 errors.error Unable to generate staged version of
188                     [ $(source).str ] ;
189             }
190
191             for t in $(staged-targets)
192             {
193                 result += [ virtual-target.register $(t) ] ;
194             }
195         }
196
197         return [ property-set.empty ] $(result) ;
198     }
199
200     # Given the list of source targets explicitly passed to 'stage', returns the
201     # list of targets which must be staged.
202     #
203     rule targets-to-stage ( source-targets * : property-set )
204     {
205         local result ;
206
207         # Traverse the dependencies, if needed.
208         if [ $(property-set).get <install-dependencies> ] = "on"
209         {
210             source-targets = [ collect-targets $(source-targets) ] ;
211         }
212
213         # Filter the target types, if needed.
214         local included-types = [ $(property-set).get <install-type> ] ;
215         for local r in $(source-targets)
216         {
217             local ty = [ $(r).type ] ;
218             if $(ty)
219             {
220                 # Do not stage searched libs.
221                 if $(ty) != SEARCHED_LIB
222                 {
223                     if $(included-types)
224                     {
225                         if [ include-type $(ty) : $(included-types) ]
226                         {
227                             result += $(r) ;
228                         }
229                     }
230                     else
231                     {
232                         result += $(r) ;
233                     }
234                 }
235             }
236             else if ! $(included-types)
237             {
238                 # Do not install typeless targets if there is an explicit list
239                 # of allowed types.
240                 result += $(r) ;
241             }
242         }
243
244         return $(result) ;
245     }
246
247     # CONSIDER: figure out why we can not use virtual-target.traverse here.
248     #
249     rule collect-targets ( targets * )
250     {
251         # Find subvariants
252         local s ;
253         for local t in $(targets)
254         {
255             s += [ $(t).creating-subvariant ] ;
256         }
257         s = [ sequence.unique $(s) ] ;
258
259         local result = [ new set ] ;
260         $(result).add $(targets) ;
261
262         for local i in $(s)
263         {
264             $(i).all-referenced-targets $(result) ;
265         }
266         local result2 ;
267         for local r in [ $(result).list ]
268         {
269             if $(r:G) != <use>
270             {
271                 result2 += $(r:G=) ;
272             }
273         }
274         DELETE_MODULE $(result) ;
275         return [ sequence.unique $(result2) ] ;
276     }
277
278     # Returns true iff 'type' is subtype of some element of 'types-to-include'.
279     #
280     local rule include-type ( type : types-to-include * )
281     {
282         local found ;
283         while $(types-to-include) && ! $(found)
284         {
285             if [ type.is-subtype $(type) $(types-to-include[1]) ]
286             {
287                 found = true ;
288             }
289             types-to-include = $(types-to-include[2-]) ;
290         }
291
292         return $(found) ;
293     }
294 }
295
296
297 # Creates a copy of target 'source'. The 'properties' object should have a
298 # <location> property which specifies where the target must be placed.
299 #
300 rule copy-file ( project name ? : source : properties )
301 {
302     name ?= [ $(source).name ] ;
303     local relative ;
304
305     local new-a = [ new non-scanning-action $(source) : common.copy :
306         $(properties) ] ;
307     local source-root = [ $(properties).get <install-source-root> ] ;
308     if $(source-root)
309     {
310         # Get the real path of the target. We probably need to strip relative
311         # path from the target name at construction.
312         local path = [ $(source).path ] ;
313         path = [ path.root $(name:D) $(path) ] ;
314         # Make the path absolute. Otherwise, it would be hard to compute the
315         # relative path. The 'source-root' is already absolute, see the
316         # 'adjust-properties' method above.
317         path = [ path.root $(path) [ path.pwd ] ] ;
318
319         relative = [ path.relative-to $(source-root) $(path) ] ;
320     }
321
322     # Note: Using $(name:D=$(relative)) might be faster here, but then we would
323     # need to explicitly check that relative is not ".", otherwise we might get
324     # paths like '<prefix>/boost/.', try to create it and mkdir would obviously
325     # fail.
326     name = [ path.join $(relative) $(name:D=) ] ;
327
328     return [ new file-target $(name) exact : [ $(source).type ] : $(project) :
329         $(new-a) ] ;
330 }
331
332
333 rule symlink ( name : project : source : properties )
334 {
335     local a = [ new action $(source) : symlink.ln : $(properties) ] ;
336     local t = [ new file-target $(name) exact : [ $(source).type ] : $(project)
337         : $(a) ] ;
338     return [ virtual-target.register $(t) ] ;
339 }
340
341
342 rule relink-file ( project : source : property-set  )
343 {
344     local action = [ $(source).action ] ;
345     local cloned-action = [ virtual-target.clone-action $(action) : $(project) :
346         "" : $(property-set) ] ;
347     return [ $(cloned-action).targets ] ;
348 }
349
350
351 # Declare installed version of the EXE type. Generator for this type will cause
352 # relinking to the new location.
353 type.register INSTALLED_EXE : : EXE ;
354
355
356 class installed-exe-generator : generator
357 {
358     import type ;
359     import property-set ;
360     import modules ;
361     import stage ;
362
363     rule __init__ ( )
364     {
365         generator.__init__ install-exe : EXE : INSTALLED_EXE ;
366     }
367
368     rule run ( project name ? : property-set : source : multiple ? )
369     {
370         local stage-rule = stage.copy-file ;
371
372         if ! [ $(property-set).get <os> ] in NT CYGWIN &&
373             ! [ $(property-set).get <target-os> ] in windows cygwin
374         {
375             # If dll-path properties have been changed for the stage target,
376             # relink instead of copying.
377             local a = [ $(source).action ] ;
378             local p = [ $(a).properties ] ;
379             local original = [ $(p).get <dll-path> ] ;
380             local current = [ $(property-set).get <dll-path> ] ;
381
382             if $(current) != $(original)
383             {
384                 stage-rule = stage.relink-file ;
385             }
386         }
387
388         return [ $(stage-rule) $(project) : $(source) : $(property-set) ] ;
389     }
390 }
391
392
393 generators.register [ new installed-exe-generator ] ;
394
395
396 # Installing a shared link on Unix might cause a creation of versioned symbolic
397 # links.
398 type.register INSTALLED_SHARED_LIB : : SHARED_LIB ;
399
400
401 class installed-shared-lib-generator : generator
402 {
403     import type ;
404     import property-set ;
405     import modules ;
406     import stage ;
407
408     rule __init__ ( )
409     {
410         generator.__init__ install-shared-lib : SHARED_LIB :
411             INSTALLED_SHARED_LIB ;
412     }
413
414     rule run ( project name ? : property-set : source : multiple ? )
415     {
416         if [ $(property-set).get <os> ] in NT CYGWIN ||
417             [ $(property-set).get <target-os> ] in windows cygwin
418         {
419             local copied = [ stage.copy-file $(project) : $(source) :
420                 $(property-set) ] ;
421             return [ virtual-target.register $(copied) ] ;
422         }
423         else
424         {
425             local a = [ $(source).action ] ;
426             local copied ;
427             if ! $(a)
428             {
429                 # Non-derived file, just copy.
430                 copied = [ stage.copy-file $(project) : $(source) :
431                     $(property-set) ] ;
432             }
433             else
434             {
435                 local cp = [ $(a).properties ] ;
436                 local current-dll-path = [ $(cp).get <dll-path> ] ;
437                 local new-dll-path = [ $(property-set).get <dll-path> ] ;
438
439                 if $(current-dll-path) != $(new-dll-path)
440                 {
441                     # Rpath changed, need to relink.
442                     copied = [ stage.relink-file $(project) : $(source) :
443                         $(property-set) ] ;
444                 }
445                 else
446                 {
447                     copied = [ stage.copy-file $(project) : $(source) :
448                         $(property-set) ] ;
449                 }
450             }
451
452             copied = [ virtual-target.register $(copied) ] ;
453
454             local result = $(copied) ;
455             # If the name is in the form NNN.XXX.YYY.ZZZ, where all 'X', 'Y' and
456             # 'Z' are numbers, we need to create NNN.XXX and NNN.XXX.YYY
457             # symbolic links.
458             local m = [ MATCH
459                 (.*)\\.([0123456789]+)\\.([0123456789]+)\\.([0123456789]+)$ :
460                 [ $(copied).name ] ] ;
461             if $(m)
462             {
463                 # Symlink without version at all is used to make
464                 # -lsome_library work.
465                 result += [ stage.symlink $(m[1]) : $(project) : $(copied) :
466                     $(property-set) ] ;
467
468                 # Symlinks of some libfoo.N and libfoo.N.M are used so that
469                 # library can found at runtime, if libfoo.N.M.X has soname of
470                 # libfoo.N. That happens when the library makes some binary
471                 # compatibility guarantees. If not, it is possible to skip those
472                 # symlinks.
473                 local suppress = [ $(property-set).get
474                     <install-no-version-symlinks> ] ;
475
476                 if $(suppress) != "on"
477                 {
478                     result += [ stage.symlink $(m[1]).$(m[2]) : $(project) :
479                         $(copied) : $(property-set) ] ;
480                     result += [ stage.symlink $(m[1]).$(m[2]).$(m[3]) :
481                         $(project) : $(copied) : $(property-set) ] ;
482                 }
483             }
484
485             return $(result) ;
486         }
487     }
488 }
489
490 generators.register [ new installed-shared-lib-generator ] ;
491
492
493 # Main target rule for 'install'.
494 #
495 rule install ( name : sources * : requirements * : default-build * )
496 {
497     local project = [ project.current ] ;
498
499     # Unless the user has explicitly asked us to hardcode dll paths, add
500     # <hardcode-dll-paths>false in requirements, to override default value.
501     if ! <hardcode-dll-paths>true in $(requirements)
502     {
503         requirements += <hardcode-dll-paths>false ;
504     }
505
506     if <tag> in $(requirements:G)
507     {
508         import errors ;
509         errors.user-error The <tag> property is not allowed for the 'install'
510             rule. ;
511     }
512
513     targets.create-metatarget install-target-class : $(project) : $(name) :
514         $(sources) : $(requirements) : $(default-build) ;
515 }
516
517
518 IMPORT $(__name__) : install : : install ;
519 IMPORT $(__name__) : install : : stage ;