1# $Id: boost.jam 62249 2010-05-26 19:05:19Z steven_watanabe $
2# Copyright 2008 Roland Schwarz
3# Distributed under the Boost Software License, Version 1.0.
4# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
5
6# Boost library support module.
7#
8# This module allows to use the boost library from boost-build projects.
9# The location of a boost source tree or the path to a pre-built
10# version of the library can be configured from either site-config.jam
11# or user-config.jam. If no location  is configured the module looks for
12# a BOOST_ROOT environment variable, which should point to a boost source
13# tree. As a last resort it tries to use pre-built libraries from the standard
14# search path of the compiler.
15#
16# If the location to a source tree is known, the module can be configured
17# from the *-config.jam files:
18#
19# using boost : 1.35 : <root>/path-to-boost-root ;
20#
21# If the location to a pre-built version is known:
22#
23# using boost : 1.34
24#   : <include>/usr/local/include/boost_1_34
25#     <library>/usr/local/lib
26#   ;
27#
28# It is legal to configure more than one boost library version in the config
29# files. The version identifier is used to disambiguate between them.
30# The first configured version becomes the default.
31#
32# To use a boost library you need to put a 'use' statement into your
33# Jamfile:
34#
35# import boost ;
36#
37# boost.use-project 1.35 ;
38#
39# If you don't care about a specific version you just can omit the version
40# part, in which case the default is picked up:
41#
42# boost.use-project ;
43#
44# The library can be referenced with the project identifier '/boost'. To
45# reference the program_options you would specify:
46#
47# exe myexe : mysrc.cpp : <library>/boost//program_options ;
48#
49# Note that the requirements are automatically transformed into suitable
50# tags to find the correct pre-built library.
51#
52
53import re
54
55import bjam
56
57from b2.build import alias, property, property_set, feature
58from b2.manager import get_manager
59from b2.tools import builtin, common
60from b2.util import bjam_signature, regex
61
62
63# TODO: This is currently necessary in Python Port, but was not in Jam.
64feature.feature('layout', ['system', 'versioned', 'tag'], ['optional'])
65feature.feature('root', [], ['optional', 'free'])
66feature.feature('build-id', [], ['optional', 'free'])
67
68__initialized = None
69__boost_auto_config = property_set.create([property.Property('layout', 'system')])
70__boost_configured = {}
71__boost_default = None
72__build_id = None
73
74__debug = None
75
76def debug():
77    global __debug
78    if __debug is None:
79        __debug = "--debug-configuration" in bjam.variable("ARGV")
80    return __debug
81
82
83# Configuration of the boost library to use.
84#
85# This can either be a boost source tree or
86# pre-built libraries. The 'version' parameter must be a valid boost
87# version number, e.g. 1.35, if specifying a pre-built version with
88# versioned layout. It may be a symbolic name, e.g. 'trunk' if specifying
89# a source tree. The options are specified as named parameters (like
90# properties). The following paramters are available:
91#
92# <root>/path-to-boost-root: Specify a source tree.
93#
94# <include>/path-to-include: The include directory to search.
95#
96# <library>/path-to-library: The library directory to search.
97#
98# <layout>system or <layout>versioned.
99#
100# <build-id>my_build_id: The custom build id to use.
101#
102def init(version, options = None):
103    assert(isinstance(version,list))
104    assert(len(version)==1)
105    version = version[0]
106    if version in __boost_configured:
107        get_manager().errors()("Boost {} already configured.".format(version));
108    else:
109        global __boost_default
110        if debug():
111            if not __boost_default:
112                print "notice: configuring default boost library {}".format(version)
113            print "notice: configuring boost library {}".format(version)
114
115        if not __boost_default:
116            __boost_default = version
117        properties = []
118        for option in options:
119            properties.append(property.create_from_string(option))
120        __boost_configured[ version ] = property_set.PropertySet(properties)
121
122projects = get_manager().projects()
123rules = projects.project_rules()
124
125
126# Use a certain version of the library.
127#
128# The use-project rule causes the module to define a boost project of
129# searchable pre-built boost libraries, or references a source tree
130# of the boost library. If the 'version' parameter is omitted either
131# the configured default (first in config files) is used or an auto
132# configuration will be attempted.
133#
134@bjam_signature(([ "version", "?" ], ))
135def use_project(version = None):
136    projects.push_current( projects.current() )
137    if not version:
138        version = __boost_default
139    if not version:
140        version = "auto_config"
141
142    global __initialized
143    if __initialized:
144        if __initialized != version:
145            get_manager().errors()('Attempt to use {} with different parameters'.format('boost'))
146    else:
147        if version in __boost_configured:
148            opts = __boost_configured[ version ]
149            root = opts.get('<root>' )
150            inc = opts.get('<include>')
151            lib = opts.get('<library>')
152
153            if debug():
154                print "notice: using boost library {} {}".format( version, opt.raw() )
155
156            global __layout
157            global __version_tag
158            __layout = opts.get('<layout>')
159            if not __layout:
160                __layout = 'versioned'
161            __build_id = opts.get('<build-id>')
162            __version_tag = re.sub("[*\\/:.\"\' ]", "_", version)
163            __initialized = version
164
165            if ( root and inc ) or \
166                ( root and lib ) or \
167                ( lib and not inc ) or \
168                ( not lib and inc ):
169                get_manager().errors()("Ambiguous parameters, use either <root> or <inlude> with <library>.")
170            elif not root and not inc:
171                root = bjam.variable("BOOST_ROOT")
172
173            module = projects.current().project_module()
174
175            if root:
176                bjam.call('call-in-module', module, 'use-project', ['boost', root])
177            else:
178                projects.initialize(__name__)
179                if version == '0.0.1':
180                    boost_0_0_1( inc, lib )
181                else:
182                    boost_std( inc, lib )
183        else:
184            get_manager().errors()("Reference to unconfigured boost version.")
185    projects.pop_current()
186
187
188rules.add_rule( 'boost.use-project', use_project )
189
190def boost_std(inc = None, lib = None):
191    # The default definitions for pre-built libraries.
192    rules.project(
193        ['boost'],
194        ['usage-requirements'] + ['<include>{}'.format(i) for i in inc] + ['<define>BOOST_ALL_NO_LIB'],
195        ['requirements'] + ['<search>{}'.format(l) for l in lib])
196
197    # TODO: There should be a better way to add a Python function into a
198    # project requirements property set.
199    tag_prop_set = property_set.create([property.Property('<tag>', tag_std)])
200    attributes = projects.attributes(projects.current().project_module())
201    attributes.requirements = attributes.requirements.refine(tag_prop_set)
202
203    alias('headers')
204
205    def boost_lib(lib_name, dyn_link_macro):
206        if (isinstance(lib_name,str)):
207            lib_name = [lib_name]
208        builtin.lib(lib_name, usage_requirements=['<link>shared:<define>{}'.format(dyn_link_macro)])
209
210    boost_lib('date_time'           , 'BOOST_DATE_TIME_DYN_LINK'      )
211    boost_lib('filesystem'          , 'BOOST_FILE_SYSTEM_DYN_LINK'    )
212    boost_lib('graph'               , 'BOOST_GRAPH_DYN_LINK'          )
213    boost_lib('graph_parallel'      , 'BOOST_GRAPH_DYN_LINK'          )
214    boost_lib('iostreams'           , 'BOOST_IOSTREAMS_DYN_LINK'      )
215    boost_lib('locale'              , 'BOOST_LOG_DYN_LINK'            )
216    boost_lib('log'                 , 'BOOST_LOG_DYN_LINK'            )
217    boost_lib('log_setup'           , 'BOOST_LOG_DYN_LINK'            )
218    boost_lib('math_tr1'            , 'BOOST_MATH_TR1_DYN_LINK'       )
219    boost_lib('math_tr1f'           , 'BOOST_MATH_TR1_DYN_LINK'       )
220    boost_lib('math_tr1l'           , 'BOOST_MATH_TR1_DYN_LINK'       )
221    boost_lib('math_c99'            , 'BOOST_MATH_TR1_DYN_LINK'       )
222    boost_lib('math_c99f'           , 'BOOST_MATH_TR1_DYN_LINK'       )
223    boost_lib('math_c99l'           , 'BOOST_MATH_TR1_DYN_LINK'       )
224    boost_lib('mpi'                 , 'BOOST_MPI_DYN_LINK'            )
225    boost_lib('program_options'     , 'BOOST_PROGRAM_OPTIONS_DYN_LINK')
226    boost_lib('python'              , 'BOOST_PYTHON_DYN_LINK'         )
227    boost_lib('python3'             , 'BOOST_PYTHON_DYN_LINK'         )
228    boost_lib('random'              , 'BOOST_RANDOM_DYN_LINK'         )
229    boost_lib('regex'               , 'BOOST_REGEX_DYN_LINK'          )
230    boost_lib('serialization'       , 'BOOST_SERIALIZATION_DYN_LINK'  )
231    boost_lib('wserialization'      , 'BOOST_SERIALIZATION_DYN_LINK'  )
232    boost_lib('signals'             , 'BOOST_SIGNALS_DYN_LINK'        )
233    boost_lib('system'              , 'BOOST_SYSTEM_DYN_LINK'         )
234    boost_lib('unit_test_framework' , 'BOOST_TEST_DYN_LINK'           )
235    boost_lib('prg_exec_monitor'    , 'BOOST_TEST_DYN_LINK'           )
236    boost_lib('test_exec_monitor'   , 'BOOST_TEST_DYN_LINK'           )
237    boost_lib('thread'              , 'BOOST_THREAD_DYN_DLL'          )
238    boost_lib('wave'                , 'BOOST_WAVE_DYN_LINK'           )
239
240def boost_0_0_1( inc, lib ):
241    print "You are trying to use an example placeholder for boost libs." ;
242    # Copy this template to another place (in the file boost.jam)
243    # and define a project and libraries modelled after the
244    # boost_std rule. Please note that it is also possible to have
245    # a per version taging rule in case they are different between
246    # versions.
247
248def tag_std(name, type, prop_set):
249    name = 'boost_' + name
250    if 'static' in prop_set.get('<link>') and 'windows' in prop_set.get('<target-os>'):
251        name = 'lib' + name
252    result = None
253
254    if __layout == 'system':
255        versionRe = re.search('^([0-9]+)_([0-9]+)', __version_tag)
256        if versionRe and versionRe.group(1) == '1' and int(versionRe.group(2)) < 39:
257            result = tag_tagged(name, type, prop_set)
258        else:
259            result = tag_system(name, type, prop_set)
260    elif __layout == 'tagged':
261        result = tag_tagged(name, type, prop_set)
262    elif __layout == 'versioned':
263        result = tag_versioned(name, type, prop_set)
264    else:
265        get_manager().errors()("Missing layout")
266    return result
267
268def tag_maybe(param):
269    return ['-{}'.format(param)] if param else []
270
271def tag_system(name, type, prop_set):
272    return common.format_name(['<base>'] + tag_maybe(__build_id), name, type, prop_set)
273
274def tag_tagged(name, type, prop_set):
275    return common.format_name(['<base>', '<threading>', '<runtime>'] + tag_maybe(__build_id), name, type, prop_set)
276
277def tag_versioned(name, type, prop_set):
278    return common.format_name(['<base>', '<toolset>', '<threading>', '<runtime>'] + tag_maybe(__version_tag) + tag_maybe(__build_id),
279        name, type, prop_set)
280