1# Copyright (c) 2005 Vladimir Prus.
2# Copyright 2006 Rene Rivera.
3#
4# Use, modification and distribution is subject to the Boost Software
5# License Version 1.0. (See accompanying file LICENSE_1_0.txt or
6# http://www.boost.org/LICENSE_1_0.txt)
7
8# Provides mechanism for installing whole packages into a specific directory
9# structure. This is opposed to the 'install' rule, that installs a number of
10# targets to a single directory, and does not care about directory structure at
11# all.
12
13# Example usage:
14#
15#   package.install boost : <properties>
16#                         : <binaries>
17#                         : <libraries>
18#                         : <headers>
19#                         ;
20#
21# This will install binaries, libraries and headers to the 'proper' location,
22# given by command line options --prefix, --exec-prefix, --bindir, --libdir and
23# --includedir.
24#
25# The rule is just a convenient wrapper, avoiding the need to define several
26# 'install' targets.
27#
28# The only install-related feature is <install-source-root>. It will apply to
29# headers only and if present, paths of headers relatively to source root will
30# be retained after installing. If it is not specified, then "." is assumed, so
31# relative paths in headers are always preserved.
32
33import "class" : new ;
34import option ;
35import project ;
36import feature ;
37import path ;
38import property ;
39import stage ;
40import targets ;
41import modules ;
42import os ;
43
44feature.feature install-default-prefix : : free incidental ;
45
46class package-paths
47{
48    import feature ;
49    import modules ;
50    import option ;
51    import os ;
52    import path ;
53    rule __init__ ( default-prefix )
54    {
55        local explicit-options = [ MATCH --(prefix|bindir|libdir|includedir|datarootdir)=.*
56          : [ modules.peek : ARGV ] ] ;
57        self.has-$(explicit-options) = true ;
58        if prefix in $(explicit-options)
59        {
60            # If --prefix is explicitly specified on the command line,
61            # then we need wipe away any settings of libdir/includir that
62            # is specified via options in config files.
63            option.set bindir : ;
64            option.set libdir : ;
65            option.set includedir : ;
66            option.set datarootdir : ;
67        }
68
69        handle-path prefix : $(default-prefix) ;
70        handle-path libdir : $(self.prefix)/lib ;
71        handle-path bindir : $(self.prefix)/bin ;
72        handle-path includedir : $(self.prefix)/include ;
73        handle-path datarootdir : $(self.prefix)/share ;
74    }
75
76    local rule handle-path ( option : default-value )
77    {
78        local opt = [ option.get $(option) ] ;
79        if $(opt)
80        {
81            opt = [ path.root [ path.make $(opt) ] [ path.pwd ] ] ;
82        }
83        else
84        {
85            opt = $(default-value) ;
86        }
87        self.$(option) = $(opt) ;
88    }
89
90    rule prefix ( )
91    {
92        return $(self.prefix) ;
93    }
94
95    rule libdir ( )
96    {
97        return $(self.libdir) ;
98    }
99
100    rule bindir ( )
101    {
102        return $(self.bindir) ;
103    }
104
105    rule includedir ( )
106    {
107        return $(self.includedir) ;
108    }
109
110    rule datarootdir ( )
111    {
112        return $(self.datarootdir) ;
113    }
114
115    rule get ( option )
116    {
117        if ! $(self.$(option))
118        {
119            local info = [ modules.peek package : .options.$(option) ] ;
120            local default-value = $(info[1]) ;
121            local relative-to = $(info[2]) ;
122            if $(self.has-$(relative-to))
123            {
124                option.set $(option) ;
125                self.has-$(option) = true ;
126            }
127            if [ MATCH --$(option)=(.*) : [ modules.peek : ARGV ] ]
128            {
129                self.has-$(option) = true ;
130            }
131            local adjusted-default =
132                [ path.join [ get $(relative-to) ] $(default-value) ] ;
133            handle-path $(option) : $(adjusted-default) ;
134        }
135        return $(self.$(option)) ;
136    }
137}
138
139# Registers an additional path option.  The option name
140# can then be used with a package-paths object.
141#
142# default-path is the default path that will be used if
143# the option is not set explicitly.  It will be interpreted
144# relative to another option.  This allows options to be
145# defined hierarchically with --prefix as the root.
146#
147# relative-to should be the name of another option.  It defaults
148# to prefix.
149#
150# Example::
151#
152#   package.add-path-option cmakedir : cmake : libdir ;
153#   cmakedir = [ $(mypaths).get cmakedir ] ; # defaults to /usr/local/lib/cmake
154#
155rule add-path-option ( name : default-path : relative-to ? )
156{
157    local value = $(default-path) $(relative-to:E=prefix) ;
158    if $(.options.$(name)) && $(.options.$(name)) != $(value)
159    {
160        import errors ;
161        errors.error Duplicate definition of $(name) ;
162    }
163    .options.$(name) = $(value) ;
164}
165
166
167# Returns a package-paths object that can be used
168# to find the various install paths.  If requirements
169# contains <install-default-prefix> then that will be used
170# as the default prefix, otherwise a platform specific
171# default prefix will be used.  All other properties
172# in requirements are ignored.
173#
174rule paths ( package-name : requirements * )
175{
176    local default-prefix = [ feature.get-values <install-default-prefix> : $(requirements) ] ;
177    # Or some likely defaults if neither is given.
178    if ! $(default-prefix)
179    {
180        if [ os.name ] = NT { default-prefix = C:\\$(package-name) ; }
181        else { default-prefix = /usr/local ; }
182    }
183    default-prefix = [ path.make $(default-prefix) ] ;
184    if ! $(.package-paths.$(default-prefix))
185    {
186        .package-paths.$(default-prefix) = [ new package-paths $(default-prefix) ] ;
187    }
188    return $(.package-paths.$(default-prefix)) ;
189}
190
191rule install ( name package-name ? : requirements * : binaries * : libraries * : headers * )
192{
193    package-name ?= $(name) ;
194
195    # If <install-source-root> is not specified, all headers are installed to
196    # prefix/include, no matter what their relative path is. Sometimes that is
197    # what is needed.
198    local install-source-root = [ property.select <install-source-root> :
199        $(requirements) ] ;
200    install-source-root = $(install-source-root:G=) ;
201    requirements = [ property.change $(requirements) : <install-source-root> ] ;
202
203    local install-header-subdir = [ property.select <install-header-subdir> :
204        $(requirements) ] ;
205    install-header-subdir = /$(install-header-subdir:G=) ;
206    install-header-subdir ?= "" ;
207    requirements = [ property.change $(requirements) : <install-header-subdir> ]
208        ;
209
210    # First, figure out all locations. Use the default if no prefix option
211    # given.
212    local paths = [ paths $(package-name) : $(requirements) ] ;
213
214    # Binaries.
215    local bin-locate = [ $(paths).bindir ] ;
216
217    # Object code libraries.
218    local lib-locate = [ $(paths).libdir ] ;
219
220    # Source header files.
221    local include-locate = [ $(paths).includedir ] ;
222
223    stage.install $(name)-bin : $(binaries) : $(requirements)
224        <location>$(bin-locate) ;
225    alias $(name)-lib : $(name)-lib-shared $(name)-lib-static ;
226
227    # Since the install location of shared libraries differs on universe
228    # and cygwin, use target alternatives to make different targets.
229    # We should have used indirection conditioanl requirements, but it's
230    # awkward to pass bin-locate and lib-locate from there to another rule.
231    alias $(name)-lib-shared : $(name)-lib-shared-universe ;
232    alias $(name)-lib-shared : $(name)-lib-shared-cygwin : <target-os>cygwin ;
233
234    # For shared libraries, we install both explicitly specified one and the
235    # shared libraries that the installed executables depend on.
236    stage.install $(name)-lib-shared-universe : $(binaries) $(libraries) : $(requirements)
237      <location>$(lib-locate) <install-dependencies>on <install-type>SHARED_LIB ;
238    stage.install $(name)-lib-shared-cygwin : $(binaries) $(libraries) : $(requirements)
239      <location>$(bin-locate) <install-dependencies>on <install-type>SHARED_LIB ;
240
241    # For static libraries, we do not care about executable dependencies, since
242    # static libraries are already incorporated into them.
243    stage.install $(name)-lib-static : $(libraries) : $(requirements)
244        <location>$(lib-locate) <install-dependencies>on <install-type>STATIC_LIB ;
245    stage.install $(name)-headers : $(headers) : $(requirements)
246        <location>$(include-locate)$(install-header-subdir)
247        <install-source-root>$(install-source-root) ;
248    alias $(name) : $(name)-bin $(name)-lib $(name)-headers ;
249
250    local c = [ project.current ] ;
251    modules.call-in [ $(c).project-module ] : explicit $(name) $(name)-bin
252        $(name)-lib $(name)-headers $(name)-lib-shared $(name)-lib-static
253        $(name)-lib-shared-universe $(name)-lib-shared-cygwin ;
254}
255
256rule install-data ( target-name : package-name : data * : requirements * )
257{
258    package-name ?= target-name ;
259
260    local paths = [ paths $(package-name) : $(requirements) ] ;
261    local datadir = [ $(paths).datarootdir ] ;
262
263    stage.install $(target-name)
264        : $(data)
265        : $(requirements) <location>$(datadir)/$(package-name)
266        ;
267
268    local c = [ project.current ] ;
269    local project-module = [ $(c).project-module ] ;
270    module $(project-module)
271    {
272        explicit $(1) ;
273    }
274}
275