1# Copyright 2003 Dave Abrahams
2# Copyright 2005, 2006 Rene Rivera
3# Copyright 2002, 2003, 2004, 2005, 2006 Vladimir Prus
4# Copyright 2020 Dmitry Arkhipov
5# Distributed under the Boost Software License, Version 1.0.
6# (See accompanying file LICENSE_1_0.txt or copy at
7# http://www.boost.org/LICENSE_1_0.txt)
8
9#| tag::doc[]
10
11[[bbv2.reference.modules.stage]]
12= stage
13This module defines the `install` rule, used to copy a set of targets to a
14single location.
15
16|# # end::doc[]
17
18
19import "class" : new ;
20import feature ;
21import generators ;
22import option ;
23import path ;
24import project ;
25import property ;
26import targets ;
27import type ;
28import types/register ;
29import virtual-target ;
30
31
32feature.feature <install-dependencies> : off on : incidental      ;
33feature.feature <install-type>         :        : free incidental ;
34feature.feature <install-source-root>  :        : free path       ;
35feature.feature <so-version>           :        : free incidental ;
36
37# If 'on', version symlinks for shared libraries will not be created. Affects
38# Unix builds only.
39feature.feature <install-no-version-symlinks> : on : optional incidental ;
40
41
42#| tag::features-install-package-doc[]
43
44[[bbv2.builtin.features.install-package]]`install-package`::
45Specifies the name of the package to which installed files belong.  This is
46used for default installation prefix on certain platforms.
47
48|# # end::features-install-package-doc[]
49
50feature.feature install-package : : free ;
51
52
53#| tag::doc[]
54
55[[bbv2.reference.modules.stage.add-install-dir]]
56. `rule add-install-dir ( name : suffix ? : parent ? : options * )`
57+
58Defines a named installation directory.
59+
60For example, `add-install-dir foo : bar : baz ;` creates feature
61<<bbv2.builtin.features.install-prefix,`<install-foo>`>> and adds support for
62named directory `(foo)` to `install` rule. The rule will try to use the value
63of `<install-foo>` property if present, otherwise will fallback to `(baz)/bar`.
64+
65Arguments:
66+
67* `name`: the name of the directory.
68* `suffix`: the path suffix appended to the parent named directory.
69* `parent`: the optional name of parent named directory.
70* `options`: special options that modify treatment of the directory.
71  Allowed options:
72+
73** `package-suffix`: append the package name to the default value. For example:
74+
75[source]
76----
77add-install-dir foo : bar : baz : package-suffix ;
78install (foo) : a : <install-package>xyz ;
79----
80+
81installs `a` into `(baz)/bar/xyz`.
82
83|# # end::doc[]
84
85.dirs = ;
86rule add-install-dir ( name : suffix ? : parent ? : options * )
87{
88    suffix ?= "" ;
89    if $(name) in $(.dirs)
90    {
91        import errors ;
92        errors.error Directory name $(name) is already registered. ;
93    }
94    feature.feature install-$(name) : : free ;
95    .dirs += $(name) ;
96    .dir.$(name) = $(suffix) $(parent) ;
97    .dir.$(name).options = $(options) ;
98}
99
100
101#| tag::doc[]
102
103. `rule install-dir-names ( )`
104+
105Returns names of all registered installation directories.
106
107|# # end::doc[]
108
109rule install-dir-names ( )
110{
111    return $(.dirs) ;
112}
113
114
115#| tag::features-install-prefix-doc[]
116
117[[bbv2.builtin.features.install-prefix]]`install-<name>`::
118Specifies installation prefix for <<bbv2.tasks.installing,`install`>> targets.
119These named installation prefixes are registered by default:
120+
121* `prefix`: `C:\<package name>` if `<target-os>windows` is in the property set,
122  `/usr/local` otherwise
123* `exec-prefix`: `(prefix)`
124* `bindir`: `(exec-prefix)/bin`
125* `sbindir`: `(exec-prefix)/sbin`
126* `libexecdir`: `(exec-prefix)/libexec`
127* `libdir`: `(exec-prefix)/lib`
128* `datarootdir`: `(prefix)/share`
129* `datadir`: `(datarootdir)`
130* `sysconfdir`: `(prefix)/etc`
131* `sharedstatedir`: `(prefix)/com`
132* `localstatedir`: `(prefix)/var`
133* `runstatedir`: `(localstatedir)/run`
134* `includedir`: `(prefix)/include`
135* `oldincludedir`: `/usr/include`
136* `docdir`: `(datarootdir)/doc/<package name>`
137* `infodir`: `(datarootdir)/info`
138* `htmldir`: `(docdir)`
139* `dvidir` : `(docdir)`
140* `pdfdir` : `(docdir)`
141* `psdir` : `(docdir)`
142* `lispdir`: `(datarootdir)/emacs/site-lisp`
143* `localedir`: `(datarootdir)/locale`
144* `mandir`: `(datarootdir)/man`
145
146If more are necessary, they could be added with
147<<bbv2.reference.modules.stage.add-install-dir,`stage.add-install-dir`>>.
148
149|# # end::features-install-prefix-doc[]
150
151feature.feature install-prefix : : free path ;
152add-install-dir exec-prefix : "" : prefix ;
153add-install-dir bindir : bin : exec-prefix ;
154add-install-dir sbindir : sbin : exec-prefix ;
155add-install-dir libexecdir : libexec : exec-prefix ;
156add-install-dir libdir : lib : exec-prefix ;
157add-install-dir datarootdir : share : prefix ;
158add-install-dir datadir : "" : datarootdir ;
159add-install-dir sysconfdir : etc : prefix ;
160add-install-dir sharedstatedir : com : prefix ;
161add-install-dir localstatedir : var : prefix ;
162add-install-dir runstatedir : run : localstatedir ;
163add-install-dir includedir : "include" : prefix ;
164add-install-dir oldincludedir : /usr/include ;
165add-install-dir docdir : doc : datarootdir : package-suffix ;
166add-install-dir infodir : info : datarootdir ;
167add-install-dir htmldir : "" : docdir ;
168add-install-dir dvidir : "" : docdir ;
169add-install-dir pdfdir : "" : docdir ;
170add-install-dir psdir : "" : docdir ;
171add-install-dir lispdir : emacs/site-lisp : datarootdir ;
172add-install-dir localedir : locale : datarootdir ;
173add-install-dir mandir : man : datarootdir ;
174
175
176#| tag::features-staging-prefix-doc[]
177
178[[bbv2.builtin.features.staging-prefix]]`staging-prefix`::
179Specifies staging prefix for <<bbv2.tasks.installing,`install`>> targets.
180If present, it will be used instead of the path to named directory `prefix`.
181Example:
182+
183[source]
184----
185project : requirements <install-prefix>x/y/z ;
186install a1 : a : <location>(bindir) ; # installs into x/y/z/bin
187install a2 : a : <location>(bindir) <staging-prefix>q ; # installs into q/bin
188----
189The feature is useful when you cannot (or don't want to) put build artfiacts
190into their intented locations during the build (such as when cross-compiling),
191but still need to communicate those intended locations to the build system,
192e.g. to generate configuration files.
193
194|# # end::features-staging-prefix-doc[]
195
196feature.feature staging-prefix : : free path ;
197
198
199class install-target-class : basic-target
200{
201    import "class" : new ;
202    import feature ;
203    import generators ;
204    import path ;
205    import project ;
206    import property ;
207    import property-set ;
208    import stage ;
209    import type ;
210
211    rule __init__ ( name-and-dir : project : sources * : requirements * :
212        default-build * : usage-requirements * )
213    {
214        # The usage-requirements specified here are ignored but are taken as a
215        # parameter to have this metatarget class have the same standard
216        # instantiation interface as all the other Boost Build metatarget
217        # classes.
218        basic-target.__init__ $(name-and-dir) : $(project) : $(sources) :
219            $(requirements) : $(default-build) ;
220    }
221
222    # If <location> is not set, sets it based on the project data.
223    # Either way, expands installation prefixes.
224    rule update-location ( property-set )
225    {
226        local location = [ $(property-set).get <location> ] ;
227        local project-location = [ $(self.project).get location ] ;
228
229        local prefix ;
230        local suffix = $(location) ;
231        if $(suffix)
232        {
233            local rel = [ path.relative $(suffix) $(project-location)
234                : no-error ] ;
235            if not-a-child != $(rel)
236            {
237                suffix = $(rel) ;
238            }
239        }
240        suffix ?= $(self.name) ;
241        local matches = [ MATCH "^\\((.+)\\)(/(.*))?$" : $(suffix) ] ;
242
243        # if location can be split into named directory and optional trailing
244        # path, do the split and expand the name into path
245        if $(matches)
246        {
247            suffix = $(matches[3]) ;
248            suffix ?= "" ;
249            local package-name = [ stage.get-package-name $(property-set)
250                : [ $(self.project).project-module ] ] ;
251            prefix = [ stage.get-dir $(matches[1]) : $(property-set)
252                : $(package-name) : staged ] ;
253        }
254        # prefix location with the project's path
255        else if ! $(location)
256        {
257            prefix = $(project-location) ;
258        }
259
260        # only modify location if it's necessary
261        if $(prefix)
262        {
263            suffix = [ path.root $(suffix) $(prefix) ] ;
264            local properties = [ $(property-set).raw ] ;
265            properties = [ property.change $(properties) : <location> ] ;
266            property-set = [ property-set.create $(properties) <location>$(suffix) ] ;
267        }
268
269        return $(property-set) ;
270    }
271
272    # Takes a target that is installed and a property set which is used when
273    # installing.
274    #
275    rule adjust-properties ( target : build-property-set )
276    {
277        local ps-raw ;
278        local a = [ $(target).action ] ;
279        if $(a)
280        {
281            local ps = [ $(a).properties ] ;
282            ps-raw = [ $(ps).raw ] ;
283
284            # Unless <hardcode-dll-paths>true is in properties, which can happen
285            # only if the user has explicitly requested it, nuke all <dll-path>
286            # properties.
287            if [ $(build-property-set).get <hardcode-dll-paths> ] != true
288            {
289                ps-raw = [ property.change $(ps-raw) : <dll-path> ] ;
290            }
291
292            # If any <dll-path> properties were specified for installing, add
293            # them.
294            local l = [ $(build-property-set).get <dll-path> ] ;
295            ps-raw += $(l:G=<dll-path>) ;
296
297            # Also copy <linkflags> feature from current build set, to be used
298            # for relinking.
299            local l = [ $(build-property-set).get <linkflags> ] ;
300            ps-raw += $(l:G=<linkflags>) ;
301
302            # Remove the <tag> feature on original targets.
303            ps-raw = [ property.change $(ps-raw) : <tag> ] ;
304
305            # And <location>. If stage target has another stage target in
306            # sources, then we shall get virtual targets with the <location>
307            # property set.
308            ps-raw = [ property.change $(ps-raw) : <location> ] ;
309        }
310
311        local d = [ $(build-property-set).get <dependency> ] ;
312        ps-raw += $(d:G=<dependency>) ;
313
314        local d = [ $(build-property-set).get <location> ] ;
315        ps-raw += $(d:G=<location>) ;
316
317        local ns = [ $(build-property-set).get <install-no-version-symlinks> ] ;
318        ps-raw += $(ns:G=<install-no-version-symlinks>) ;
319
320        local d = [ $(build-property-set).get <install-source-root> ] ;
321        # Make the path absolute: we shall use it to compute relative paths and
322        # making the path absolute will help.
323        if $(d)
324        {
325            d = [ path.root $(d) [ path.pwd ] ] ;
326            ps-raw += $(d:G=<install-source-root>) ;
327        }
328
329        if $(ps-raw)
330        {
331            return [ property-set.create $(ps-raw) ]  ;
332        }
333        else
334        {
335            return [ property-set.empty ] ;
336        }
337    }
338
339    rule construct ( name : source-targets * : property-set )
340    {
341        source-targets = [ targets-to-stage $(source-targets) :
342            $(property-set) ] ;
343
344        property-set = [ update-location $(property-set) ] ;
345
346        local ename = [ $(property-set).get <name> ] ;
347
348        if $(ename) && $(source-targets[2])
349        {
350            import errors : error : $(__name__) : errors.error ;
351            errors.error When <name> property is used "in" 'install', only one
352                source is allowed. ;
353        }
354
355        local result ;
356        for local i in $(source-targets)
357        {
358            local staged-targets ;
359
360            local new-properties = [ adjust-properties $(i) :
361                $(property-set) ] ;
362
363            # See if something special should be done when staging this type. It
364            # is indicated by the presence of a special "INSTALLED_" type.
365            local t = [ $(i).type ] ;
366            if $(t) && [ type.registered INSTALLED_$(t) ]
367            {
368                if $(ename)
369                {
370                    import errors : error : $(__name__) : errors.error ;
371                    errors.error In "'install':" <name> property specified with
372                        target that requires relinking. ;
373                }
374                else
375                {
376                    local targets = [ generators.construct $(self.project)
377                        $(name) : INSTALLED_$(t) : $(new-properties) : $(i) ] ;
378                    staged-targets += $(targets[2-]) ;
379                }
380            }
381            else
382            {
383                staged-targets = [ stage.copy-file $(self.project) $(ename) :
384                    $(i) : $(new-properties) ] ;
385            }
386
387            if ! $(staged-targets)
388            {
389                import errors : error : $(__name__) : errors.error ;
390                errors.error Unable to generate staged version of
391                    [ $(source).str ] ;
392            }
393
394            for t in $(staged-targets)
395            {
396                result += [ virtual-target.register $(t) ] ;
397            }
398        }
399
400        return [ property-set.empty ] $(result) ;
401    }
402
403    # Given the list of source targets explicitly passed to 'stage', returns the
404    # list of targets which must be staged.
405    #
406    rule targets-to-stage ( source-targets * : property-set )
407    {
408        local result ;
409
410        # Traverse the dependencies, if needed.
411        if [ $(property-set).get <install-dependencies> ] = "on"
412        {
413            source-targets = [ collect-targets $(source-targets) ] ;
414        }
415
416        # Filter the target types, if needed.
417        local included-types = [ $(property-set).get <install-type> ] ;
418        for local r in $(source-targets)
419        {
420            local ty = [ $(r).type ] ;
421            if $(ty)
422            {
423                # Do not stage searched libs.
424                if $(ty) != SEARCHED_LIB
425                {
426                    if $(included-types)
427                    {
428                        if [ include-type $(ty) : $(included-types) ]
429                        {
430                            result += $(r) ;
431                        }
432                    }
433                    else
434                    {
435                        result += $(r) ;
436                    }
437                }
438            }
439            else if ! $(included-types)
440            {
441                # Do not install typeless targets if there is an explicit list
442                # of allowed types.
443                result += $(r) ;
444            }
445        }
446
447        return $(result) ;
448    }
449
450    # CONSIDER: figure out why we can not use virtual-target.traverse here.
451    #
452    rule collect-targets ( targets * )
453    {
454        # Find subvariants
455        local s ;
456        for local t in $(targets)
457        {
458            s += [ $(t).creating-subvariant ] ;
459        }
460        s = [ sequence.unique $(s) ] ;
461
462        local result = [ new set ] ;
463        $(result).add $(targets) ;
464
465        for local i in $(s)
466        {
467            $(i).all-referenced-targets $(result) ;
468        }
469        local result2 ;
470        for local r in [ $(result).list ]
471        {
472            if $(r:G) != <use>
473            {
474                result2 += $(r:G=) ;
475            }
476        }
477        DELETE_MODULE $(result) ;
478        return [ sequence.unique $(result2) ] ;
479    }
480
481    # Returns true iff 'type' is subtype of some element of 'types-to-include'.
482    #
483    local rule include-type ( type : types-to-include * )
484    {
485        local found ;
486        while $(types-to-include) && ! $(found)
487        {
488            if [ type.is-subtype $(type) $(types-to-include[1]) ]
489            {
490                found = true ;
491            }
492            types-to-include = $(types-to-include[2-]) ;
493        }
494
495        return $(found) ;
496    }
497}
498
499
500# Creates a copy of target 'source'. The 'properties' object should have a
501# <location> property which specifies where the target must be placed.
502#
503rule copy-file ( project name ? : source : properties )
504{
505    name ?= [ $(source).name ] ;
506    local relative ;
507
508    local new-a = [ new non-scanning-action $(source) : common.copy :
509        $(properties) ] ;
510    local source-root = [ $(properties).get <install-source-root> ] ;
511    if $(source-root)
512    {
513        # Get the real path of the target. We probably need to strip relative
514        # path from the target name at construction.
515        local path = [ $(source).path ] ;
516        path = [ path.root $(name:D) $(path) ] ;
517        # Make the path absolute. Otherwise, it would be hard to compute the
518        # relative path. The 'source-root' is already absolute, see the
519        # 'adjust-properties' method above.
520        path = [ path.root $(path) [ path.pwd ] ] ;
521
522        relative = [ path.relative-to $(source-root) $(path) ] ;
523    }
524
525    # Note: Using $(name:D=$(relative)) might be faster here, but then we would
526    # need to explicitly check that relative is not ".", otherwise we might get
527    # paths like '<prefix>/boost/.', try to create it and mkdir would obviously
528    # fail.
529    name = [ path.join $(relative) $(name:D=) ] ;
530
531    return [ new file-target $(name) exact : [ $(source).type ] : $(project) :
532        $(new-a) ] ;
533}
534
535
536rule symlink ( name : project : source : properties )
537{
538    local a = [ new action $(source) : symlink.ln : $(properties) ] ;
539    local t = [ new file-target $(name) exact : [ $(source).type ] : $(project)
540        : $(a) ] ;
541    return [ virtual-target.register $(t) ] ;
542}
543
544
545rule relink-file ( project : source : property-set  )
546{
547    local action = [ $(source).action ] ;
548    local cloned-action = [ virtual-target.clone-action $(action) : $(project) :
549        "" : $(property-set) ] ;
550    return [ $(cloned-action).targets ] ;
551}
552
553
554# Declare installed version of the EXE type. Generator for this type will cause
555# relinking to the new location.
556type.register INSTALLED_EXE : : EXE ;
557
558
559class installed-exe-generator : generator
560{
561    import type ;
562    import property-set ;
563    import modules ;
564    import stage ;
565
566    rule __init__ ( )
567    {
568        generator.__init__ install-exe : EXE : INSTALLED_EXE ;
569    }
570
571    rule run ( project name ? : property-set : source : multiple ? )
572    {
573        local stage-rule = stage.copy-file ;
574
575        if ! [ $(property-set).get <os> ] in NT CYGWIN &&
576            ! [ $(property-set).get <target-os> ] in windows cygwin
577        {
578            # If dll-path properties have been changed for the stage target,
579            # relink instead of copying.
580            local a = [ $(source).action ] ;
581            local p = [ $(a).properties ] ;
582            local original = [ $(p).get <dll-path> ] ;
583            local current = [ $(property-set).get <dll-path> ] ;
584
585            if $(current) != $(original)
586            {
587                stage-rule = stage.relink-file ;
588            }
589        }
590
591        return [ $(stage-rule) $(project) : $(source) : $(property-set) ] ;
592    }
593}
594
595
596generators.register [ new installed-exe-generator ] ;
597
598
599# Installing a shared link on Unix might cause a creation of versioned symbolic
600# links.
601type.register INSTALLED_SHARED_LIB : : SHARED_LIB ;
602
603
604class installed-shared-lib-generator : generator
605{
606    import type ;
607    import property-set ;
608    import modules ;
609    import stage ;
610
611    rule __init__ ( )
612    {
613        generator.__init__ install-shared-lib : SHARED_LIB :
614            INSTALLED_SHARED_LIB ;
615    }
616
617    rule run ( project name ? : property-set : source : multiple ? )
618    {
619        if [ $(property-set).get <os> ] in NT CYGWIN ||
620            [ $(property-set).get <target-os> ] in windows cygwin
621        {
622            local copied = [ stage.copy-file $(project) : $(source) :
623                $(property-set) ] ;
624            return [ virtual-target.register $(copied) ] ;
625        }
626        else
627        {
628            local a = [ $(source).action ] ;
629            local copied ;
630            if ! $(a)
631            {
632                # Non-derived file, just copy.
633                copied = [ stage.copy-file $(project) : $(source) :
634                    $(property-set) ] ;
635            }
636            else
637            {
638                local cp = [ $(a).properties ] ;
639                local current-dll-path = [ $(cp).get <dll-path> ] ;
640                local new-dll-path = [ $(property-set).get <dll-path> ] ;
641
642                if $(current-dll-path) != $(new-dll-path)
643                {
644                    # Rpath changed, need to relink.
645                    copied = [ stage.relink-file $(project) : $(source) :
646                        $(property-set) ] ;
647                }
648                else
649                {
650                    copied = [ stage.copy-file $(project) : $(source) :
651                        $(property-set) ] ;
652                }
653            }
654
655            copied = [ virtual-target.register $(copied) ] ;
656
657            local result = $(copied) ;
658            # If the name is in the form NNN.XXX.YYY.ZZZ, where all 'X', 'Y' and
659            # 'Z' are numbers, we need to create NNN.XXX and NNN.XXX.YYY
660            # symbolic links.
661            local m = [ MATCH
662                "(.*)\\.([0123456789]+)\\.([0123456789]+)\\.([0123456789]+)$" :
663                [ $(copied).name ] ] ;
664            if $(m)
665            {
666                # Symlink without version at all is used to make
667                # -lsome_library work.
668                result += [ stage.symlink $(m[1]) : $(project) : $(copied) :
669                    $(property-set) ] ;
670
671                # Symlinks of some libfoo.N and libfoo.N.M are used so that
672                # library can found at runtime, if libfoo.N.M.X has soname of
673                # libfoo.N. That happens when the library makes some binary
674                # compatibility guarantees. If not, it is possible to skip those
675                # symlinks.
676                local suppress = [ $(property-set).get
677                    <install-no-version-symlinks> ] ;
678
679                if $(suppress) != "on"
680                {
681                    result += [ stage.symlink $(m[1]).$(m[2]) : $(project) :
682                        $(copied) : $(property-set) ] ;
683                    result += [ stage.symlink $(m[1]).$(m[2]).$(m[3]) :
684                        $(project) : $(copied) : $(property-set) ] ;
685                }
686            }
687
688            return $(result) ;
689        }
690    }
691}
692
693generators.register [ new installed-shared-lib-generator ] ;
694
695
696#| tag::doc[]
697
698. `rule get-dir ( name : property-set : package-name : flags * )`
699+
700Returns the path to a named installation directory. For a given `name=xyz` the
701rule uses the value of `<install-xyz>` property if it is present in
702`property-set`. Otherwise it tries to construct the default value of the path
703recursively getting the path to ``name``'s registered base named directory and
704relative path. For example:
705+
706[source]
707----
708stage.add-install-dir foo : bar : baz ;
709
710local ps = [ property-set.create <install-foo>x/y/z ] ;
711echo [ stage.get-dir foo : $(ps) : $(__name__) ] ; # outputs x/y/z
712
713ps = [ property-set.create <install-baz>a/b/c/d ] ;
714echo [ stage.get-dir foo : $(ps) : $(__name__) ] ; # outputs a/b/c/d/bar
715----
716+
717The argument `package-name` is used to construct the path for named directories
718that were registered with `package-suffix` option and also to construct
719`install-prefix` when targeting Windows.
720+
721Available `flags`:
722+
723* `staged`: take <<bbv2.builtin.features.staging-prefix,`staging-prefix`>> into
724  account.
725* `relative`: return the path to `name` relative to its base directory.
726
727|# # end::doc[]
728
729rule get-dir ( name : property-set : package-name : flags * )
730{
731    local result ;
732
733    # We treat the 'prefix' directory in a special way, because it's default
734    # is based on the value of <target-os> property.
735    if $(name) = prefix
736    {
737        result = [ get-install-prefix $(property-set) : $(package-name)
738            : $(flags) ] ;
739    }
740    else
741    {
742        # First, try getting the path for requested directory from properties.
743        result = [ $(property-set).get <install-$(name)> ] ;
744        local info = [ get-dir-info $(name) : $(package-name) ] ;
745        # Otherwise, use the default path. In both cases, it could be a
746        # relative path.
747        result ?= $(info[1]) ;
748        if $(result)
749        {
750            result = [ path.make $(result) ] ;
751        }
752
753        # If there is a base directory, we may need to modify result further.
754        if $(info[2])
755        {
756            local base = [ get-dir $(info[2]) : $(property-set)
757                : $(package-name) : $(flags) ] ;
758            if relative in $(flags)
759            {
760                local rel = [ path.relative $(result) $(base) : no-error ] ;
761                if not-a-child != $(rel)
762                {
763                    result = $(rel) ;
764                }
765            }
766            else
767            {
768                result = [ path.root $(result) $(base) ] ;
769            }
770        }
771    }
772
773    return $(result) ;
774}
775
776
777# For a given named directory returns its parent directory and relative path
778local rule get-dir-info ( name : package-name ) {
779    local path = $(.dir.$(name)[1]) ;
780    if ! x$(path)
781    {
782        import errors ;
783        errors.error $(name) is not an installation directory name. ;
784    }
785
786    if package-suffix in $(.dir.$(name).options)
787    {
788        path = [ path.join $(path) $(package-name) ] ;
789    }
790
791    return $(path) $(.dir.$(name)[2]) ;
792}
793
794
795local rule get-install-prefix ( property-set : package-name : flags * )
796{
797    local prefix ;
798    if staged in $(flags)
799    {
800        prefix = [ $(property-set).get <staging-prefix> ] ;
801    }
802    prefix ?= [ $(property-set).get <install-prefix> ] ;
803    prefix = $(prefix[0]) ;
804    prefix ?= [ option.get prefix ] ;
805    if ! $(prefix)
806    {
807        if windows = [ $(property-set).get <target-os> ]
808        {
809          prefix = C:\\$(package-name) ;
810        }
811        else
812        {
813            prefix = /usr/local ;
814        }
815    }
816    return [ path.make $(prefix) ] ;
817}
818
819
820#| tag::doc[]
821
822. `rule get-package-name ( property-set : project-module ? )`
823+
824Returns the package name that will be used for `install` targets when
825constructing installation location. The rule uses the value of
826<<bbv2.builtin.features.install-package,`<install-package>`>> property if it's
827present in `property-set`. Otherwise it deduces the package name using
828``project-module``'s attributes. It traverses the project hierarchy up to the
829root searching for the first project with an id. If none is found, the base
830name of the root project's location is used. If `project-module` is empty, the
831caller module is used (this allows invoking just `[ get-package-name $(ps) ]`
832in project jam files).
833
834|# # end::doc[]
835
836rule get-package-name ( property-set : project-module ? )
837{
838    local package = [ $(property-set).get <install-package> ] ;
839    if ! $(package)
840    {
841        project-module ?= [ CALLER_MODULE 1 ] ;
842
843        local m = $(project-module) ;
844        package = [ project.attribute $(m) id ] ;
845        while ! $(package)
846        {
847            m = [ project.attribute $(m) parent-module ] ;
848            if ! $(m) { break ; }
849
850            package = [ project.attribute $(m) id ] ;
851        }
852
853        if ! $(package)
854        {
855            local root = [ project.attribute $(project-module) project-root ] ;
856            package = [ path.root $(root) [ path.pwd ] ] ;
857        }
858
859        package = $(package:B) ;
860    }
861    return $(package) ;
862}
863
864
865rule stage-translate-path ( feature value : properties * : project-id : project-location )
866{
867    if $(feature) = <location> && [ MATCH "^\\((.+)\\)(/(.*))?$" : $(value) ]
868    {
869        return $(value) ;
870    }
871}
872
873
874# Main target rule for 'install'.
875#
876rule install ( name : sources * : requirements * : default-build * )
877{
878    local project = [ project.current ] ;
879
880    # Unless the user has explicitly asked us to hardcode dll paths, add
881    # <hardcode-dll-paths>false in requirements, to override default value.
882    if ! <hardcode-dll-paths>true in $(requirements)
883    {
884        requirements += <hardcode-dll-paths>false ;
885    }
886
887    if <tag> in $(requirements:G)
888    {
889        import errors ;
890        errors.user-error The <tag> property is not allowed for the 'install'
891            rule. ;
892    }
893
894    targets.create-metatarget install-target-class : $(project) : $(name) :
895        $(sources) : $(requirements) <translate-path>@stage-translate-path : $(default-build) ;
896}
897
898
899IMPORT $(__name__) : install : : install ;
900IMPORT $(__name__) : install : : stage ;
901IMPORT $(__name__) : stage-translate-path : : stage-translate-path ;
902