# Copyright 2003 Dave Abrahams # Copyright 2005, 2006 Rene Rivera # Copyright 2002, 2003, 2004, 2005, 2006 Vladimir Prus # 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) # This module defines the 'install' rule, used to copy a set of targets to a # single location. import targets ; import "class" : new ; import errors ; import type ; import generators ; import feature ; import project ; import virtual-target ; import path ; import types/register ; feature.feature : off on : incidental ; feature.feature : : free incidental ; feature.feature : : free path ; feature.feature : : free incidental ; # If 'on', version symlinks for shared libraries will not be created. Affects # Unix builds only. feature.feature : on : optional incidental ; class install-target-class : basic-target { import feature ; import project ; import type ; import errors ; import generators ; import path ; import stage ; import "class" : new ; import property ; import property-set ; rule __init__ ( name-and-dir : project : sources * : requirements * : default-build * ) { basic-target.__init__ $(name-and-dir) : $(project) : $(sources) : $(requirements) : $(default-build) ; } # If is not set, sets it based on the project data. # rule update-location ( property-set ) { local loc = [ $(property-set).get ] ; if ! $(loc) { loc = [ path.root $(self.name) [ $(self.project).get location ] ] ; property-set = [ $(property-set).add-raw $(loc:G=) ] ; } return $(property-set) ; } # Takes a target that is installed and a property set which is used when # installing. # rule adjust-properties ( target : build-property-set ) { local ps-raw ; local a = [ $(target).action ] ; if $(a) { local ps = [ $(a).properties ] ; ps-raw = [ $(ps).raw ] ; # Unless true is in properties, which can happen # only if the user has explicitly requested it, nuke all # properties. if [ $(build-property-set).get ] != true { ps-raw = [ property.change $(ps-raw) : ] ; } # If any properties were specified for installing, add # them. local l = [ $(build-property-set).get ] ; ps-raw += $(l:G=) ; # Also copy feature from current build set, to be used # for relinking. local l = [ $(build-property-set).get ] ; ps-raw += $(l:G=) ; # Remove the feature on original targets. ps-raw = [ property.change $(ps-raw) : ] ; # And . If stage target has another stage target in # sources, then we shall get virtual targets with the # property set. ps-raw = [ property.change $(ps-raw) : ] ; } local d = [ $(build-property-set).get ] ; ps-raw += $(d:G=) ; local d = [ $(build-property-set).get ] ; ps-raw += $(d:G=) ; local ns = [ $(build-property-set).get ] ; ps-raw += $(ns:G=) ; local d = [ $(build-property-set).get ] ; # Make the path absolute: we shall use it to compute relative paths and # making the path absolute will help. if $(d) { d = [ path.root $(d) [ path.pwd ] ] ; ps-raw += $(d:G=) ; } if $(ps-raw) { return [ property-set.create $(ps-raw) ] ; } else { return [ property-set.empty ] ; } } rule construct ( name : source-targets * : property-set ) { source-targets = [ targets-to-stage $(source-targets) : $(property-set) ] ; property-set = [ update-location $(property-set) ] ; local ename = [ $(property-set).get ] ; if $(ename) && $(source-targets[2]) { errors.error "When property is used in 'install', only one" "source is allowed" ; } local result ; for local i in $(source-targets) { local staged-targets ; local new-properties = [ adjust-properties $(i) : $(property-set) ] ; # See if something special should be done when staging this type. It # is indicated by the presence of a special "INSTALLED_" type. local t = [ $(i).type ] ; if $(t) && [ type.registered INSTALLED_$(t) ] { if $(ename) { errors.error "In 'install': property specified with target that requires relinking." ; } else { local targets = [ generators.construct $(self.project) $(name) : INSTALLED_$(t) : $(new-properties) : $(i) ] ; staged-targets += $(targets[2-]) ; } } else { staged-targets = [ stage.copy-file $(self.project) $(ename) : $(i) : $(new-properties) ] ; } if ! $(staged-targets) { errors.error "Unable to generate staged version of " [ $(source).str ] ; } for t in $(staged-targets) { result += [ virtual-target.register $(t) ] ; } } return [ property-set.empty ] $(result) ; } # Given the list of source targets explicitly passed to 'stage', returns the # list of targets which must be staged. # rule targets-to-stage ( source-targets * : property-set ) { local result ; # Traverse the dependencies, if needed. if [ $(property-set).get ] = "on" { source-targets = [ collect-targets $(source-targets) ] ; } # Filter the target types, if needed. local included-types = [ $(property-set).get ] ; for local r in $(source-targets) { local ty = [ $(r).type ] ; if $(ty) { # Do not stage searched libs. if $(ty) != SEARCHED_LIB { if $(included-types) { if [ include-type $(ty) : $(included-types) ] { result += $(r) ; } } else { result += $(r) ; } } } else if ! $(included-types) { # Don't install typeless target if there is an explicit list of # allowed types. result += $(r) ; } } return $(result) ; } # CONSIDER: figure out why we can not use virtual-target.traverse here. # rule collect-targets ( targets * ) { # Find subvariants local s ; for local t in $(targets) { s += [ $(t).creating-subvariant ] ; } s = [ sequence.unique $(s) ] ; local result = [ new set ] ; $(result).add $(targets) ; for local i in $(s) { $(i).all-referenced-targets $(result) ; } local result2 ; for local r in [ $(result).list ] { if $(r:G) != { result2 += $(r:G=) ; } } DELETE_MODULE $(result) ; result = [ sequence.unique $(result2) ] ; } # Returns true iff 'type' is subtype of some element of 'types-to-include'. # local rule include-type ( type : types-to-include * ) { local found ; while $(types-to-include) && ! $(found) { if [ type.is-subtype $(type) $(types-to-include[1]) ] { found = true ; } types-to-include = $(types-to-include[2-]) ; } return $(found) ; } } # Creates a copy of target 'source'. The 'properties' object should have a # property which specifies where the target must be placed. # rule copy-file ( project name ? : source : properties ) { name ?= [ $(source).name ] ; local relative ; local new-a = [ new non-scanning-action $(source) : common.copy : $(properties) ] ; local source-root = [ $(properties).get ] ; if $(source-root) { # Get the real path of the target. We probably need to strip relative # path from the target name at construction. local path = [ $(source).path ] ; path = [ path.root $(name:D) $(path) ] ; # Make the path absolute. Otherwise, it would be hard to compute the # relative path. The 'source-root' is already absolute, see the # 'adjust-properties' method above. path = [ path.root $(path) [ path.pwd ] ] ; relative = [ path.relative-to $(source-root) $(path) ] ; } # Note: Using $(name:D=$(relative)) might be faster here, but then we would # need to explicitly check that relative is not ".", otherwise we might get # paths like '/boost/.', try to create it and mkdir would obviously # fail. name = [ path.join $(relative) $(name:D=) ] ; return [ new file-target $(name) exact : [ $(source).type ] : $(project) : $(new-a) ] ; } rule symlink ( name : project : source : properties ) { local a = [ new action $(source) : symlink.ln : $(properties) ] ; return [ new file-target $(name) exact : [ $(source).type ] : $(project) : $(a) ] ; } rule relink-file ( project : source : property-set ) { local action = [ $(source).action ] ; local cloned-action = [ virtual-target.clone-action $(action) : $(project) : "" : $(property-set) ] ; return [ $(cloned-action).targets ] ; } # Declare installed version of the EXE type. Generator for this type will cause # relinking to the new location. type.register INSTALLED_EXE : : EXE ; class installed-exe-generator : generator { import type ; import property-set ; import modules ; import stage ; rule __init__ ( ) { generator.__init__ install-exe : EXE : INSTALLED_EXE ; } rule run ( project name ? : property-set : source : multiple ? ) { local need-relink ; if [ $(property-set).get ] in NT CYGWIN || [ $(property-set).get ] in windows cygwin { } else { # See if the dll-path properties are not changed during # install. If so, copy, don't relink. local a = [ $(source).action ] ; local p = [ $(a).properties ] ; local original = [ $(p).get ] ; local current = [ $(property-set).get ] ; if $(current) != $(original) { need-relink = true ; } } if $(need-relink) { return [ stage.relink-file $(project) : $(source) : $(property-set) ] ; } else { return [ stage.copy-file $(project) : $(source) : $(property-set) ] ; } } } generators.register [ new installed-exe-generator ] ; # Installing a shared link on Unix might cause a creation of versioned symbolic # links. type.register INSTALLED_SHARED_LIB : : SHARED_LIB ; class installed-shared-lib-generator : generator { import type ; import property-set ; import modules ; import stage ; rule __init__ ( ) { generator.__init__ install-shared-lib : SHARED_LIB : INSTALLED_SHARED_LIB ; } rule run ( project name ? : property-set : source : multiple ? ) { if [ $(property-set).get ] in NT CYGWIN || [ $(property-set).get ] in windows cygwin { local copied = [ stage.copy-file $(project) : $(source) : $(property-set) ] ; return [ virtual-target.register $(copied) ] ; } else { local a = [ $(source).action ] ; local copied ; if ! $(a) { # Non-derived file, just copy. copied = [ stage.copy-file $(project) : $(source) : $(property-set) ] ; } else { local cp = [ $(a).properties ] ; local current-dll-path = [ $(cp).get ] ; local new-dll-path = [ $(property-set).get ] ; if $(current-dll-path) != $(new-dll-path) { # Rpath changed, need to relink. copied = [ stage.relink-file $(project) : $(source) : $(property-set) ] ; } else { copied = [ stage.copy-file $(project) : $(source) : $(property-set) ] ; } } copied = [ virtual-target.register $(copied) ] ; local result = $(copied) ; # If the name is in the form NNN.XXX.YYY.ZZZ, where all 'X', 'Y' and # 'Z' are numbers, we need to create NNN.XXX and NNN.XXX.YYY # symbolic links. local m = [ MATCH (.*)\\.([0123456789]+)\\.([0123456789]+)\\.([0123456789]+)$ : [ $(copied).name ] ] ; if $(m) { # Symlink without version at all is used to make # -lsome_library work. result += [ stage.symlink $(m[1]) : $(project) : $(copied) : $(property-set) ] ; # Symlinks of some libfoo.N and libfoo.N.M are used so that # library can found at runtime, if libfoo.N.M.X has soname of # libfoo.N. That happens when the library makes some binary # compatibility guarantees. If not, it is possible to skip those # symlinks. local suppress = [ $(property-set).get ] ; if $(suppress) != "on" { result += [ stage.symlink $(m[1]).$(m[2]) : $(project) : $(copied) : $(property-set) ] ; result += [ stage.symlink $(m[1]).$(m[2]).$(m[3]) : $(project) : $(copied) : $(property-set) ] ; } } return $(result) ; } } } generators.register [ new installed-shared-lib-generator ] ; # Main target rule for 'install'. # rule install ( name : sources * : requirements * : default-build * ) { local project = [ project.current ] ; # Unless the user has explicitly asked us to hardcode dll paths, add # false in requirements, to override default value. if ! true in $(requirements) { requirements += false ; } if in $(requirements:G) { errors.user-error "The property is not allowed for the 'install' rule" ; } targets.main-target-alternative [ new install-target-class $(name) : $(project) : [ targets.main-target-sources $(sources) : $(name) ] : [ targets.main-target-requirements $(requirements) : $(project) ] : [ targets.main-target-default-build $(default-build) : $(project) ] ] ; } IMPORT $(__name__) : install : : install ; IMPORT $(__name__) : install : : stage ;