1# Copyright (c) 2010 Vladimir Prus.
2# Copyright (c) 2013 Steven Watanabe
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
8import property-set ;
9import path ;
10import modules ;
11import "class" ;
12import errors ;
13import configure ;
14import project ;
15import virtual-target ;
16import generators ;
17import property ;
18import print ;
19
20project.initialize $(__name__) ;
21.project = [ project.current ] ;
22project ac ;
23
24rule generate-include ( target : sources * : properties * )
25{
26    local header = [ property.select <include> : $(properties) ] ;
27    print.output $(target) ;
28    print.text "#include <$(header:G=)>" : true ;
29}
30
31rule generate-main ( target : sources * : properties * )
32{
33    print.output $(target) ;
34    print.text "int main() {}" : true ;
35}
36
37rule find-include-path ( properties : header : provided-path ? )
38{
39    if $(provided-path) && [ path.exists [ path.root $(header) $(provided-path) ] ]
40    {
41        return $(provided-path) ;
42    }
43    else
44    {
45        local a = [ class.new action : ac.generate-include : [ property-set.create <include>$(header) ] ] ;
46        local cpp = [ class.new file-target $(header).cpp exact : CPP : $(.project) : $(a) ] ;
47        cpp = [ virtual-target.register $(cpp) ] ;
48        local result = [ generators.construct $(.project) $(header) : OBJ : $(properties) : $(cpp) : true ] ;
49        local jam-targets ;
50        for t in $(result[2-])
51        {
52            jam-targets += [ $(t).actualize ] ;
53        }
54        if [ UPDATE_NOW $(jam-targets) : [ modules.peek configure : .log-fd ]
55            : ignore-minus-n : ignore-minus-q ]
56        {
57            return %default ;
58        }
59    }
60}
61
62rule construct-library ( name : property-set : provided-path ? )
63{
64    property-set = [ $(property-set).refine [ property-set.create $(link-opt) ] ] ;
65    local lib-props = [ $(property-set).add-raw <name>$(name) <search>$(provided-path) ] ;
66    return [ generators.construct $(.project) lib-$(name)
67        : SEARCHED_LIB : $(lib-props) : : true ] ;
68}
69
70
71rule find-library ( properties : names + : provided-path ? )
72{
73    local result ;
74    if ! $(.main.cpp)
75    {
76        local a = [ class.new action : ac.generate-main :
77                    [ property-set.empty ] ] ;
78        .main.cpp = [ virtual-target.register
79            [ class.new file-target main.cpp exact
80                : CPP : $(.project) : $(a) ] ] ;
81    }
82    if [ $(properties).get <link> ] = shared
83    {
84        link-opts = <link>shared <link>static ;
85    }
86    else
87    {
88        link-opts = <link>static <link>shared ;
89    }
90    while $(link-opts)
91    {
92        local names-iter = $(names) ;
93        properties = [ $(properties).refine [ property-set.create $(link-opts[1]) ] ] ;
94        while $(names-iter)
95        {
96            local name = $(names-iter[1]) ;
97            local lib = [ construct-library $(name) : $(properties) : $(provided-path) ] ;
98            local test = [ generators.construct $(.project) $(name) : EXE
99                : [ $(properties).add $(lib[1]) ] : $(.main.cpp) $(lib[2-])
100                : true ] ;
101            local jam-targets ;
102            for t in $(test[2-])
103            {
104                jam-targets += [ $(t).actualize ] ;
105            }
106            if [ UPDATE_NOW $(jam-targets) : [ modules.peek configure : .log-fd ]
107                    : ignore-minus-n : ignore-minus-q ]
108            {
109                result = $(name) $(link-opts[1]) ;
110                names-iter = ; link-opts = ; # break
111            }
112            names-iter = $(names-iter[2-]) ;
113        }
114        link-opts = $(link-opts[2-]) ;
115    }
116    return $(result) ;
117}
118
119class ac-library : basic-target
120{
121    import errors ;
122    import indirect ;
123    import virtual-target ;
124    import ac ;
125    import configure ;
126    import config-cache ;
127
128    rule __init__ ( name : project : requirements * : include-path ? : library-path ? : library-name ? )
129    {
130        basic-target.__init__ $(name) : $(project) : : $(requirements) ;
131
132        reconfigure $(include-path) : $(library-path) : $(library-name) ;
133    }
134
135    rule set-header ( header )
136    {
137        self.header = $(header) ;
138    }
139
140    rule set-default-names ( names + )
141    {
142        self.default-names = $(names) ;
143    }
144
145    rule reconfigure ( include-path ? : library-path ? : library-name ? )
146    {
147        if $(include-path) || $(library-path) || $(library-name)
148        {
149            check-not-configured ;
150
151            self.include-path = $(include-path) ;
152            self.library-path = $(library-path) ;
153            self.library-name = $(library-name) ;
154        }
155    }
156
157    rule set-target ( target )
158    {
159        check-not-configured ;
160        self.target = $(target) ;
161    }
162
163    rule check-not-configured ( )
164    {
165        if $(self.include-path) || $(self.library-path) || $(self.library-name) || $(self.target)
166        {
167            errors.user-error [ name ] "is already configured" ;
168        }
169    }
170
171    rule construct ( name : sources * : property-set )
172    {
173        if $(self.target)
174        {
175            return [ $(self.target).generate $(property-set) ] ;
176        }
177        else
178        {
179            local use-environment ;
180            if ! $(self.library-name) && ! $(self.include-path) && ! $(self.library-path)
181            {
182                use-environment = true ;
183            }
184            local libnames = $(self.library-name) ;
185            if ! $(libnames) && $(use-environment)
186            {
187                libnames = [ modules.peek : $(name:U)_NAME ] ;
188                # Backward compatibility only.
189                libnames ?= [ modules.peek : $(name:U)_BINARY ] ;
190            }
191            libnames ?= $(self.default-names) ;
192
193            local include-path = $(self.include-path) ;
194            if ! $(include-path) && $(use-environment)
195            {
196                include-path = [ modules.peek : $(name:U)_INCLUDE ] ;
197            }
198
199            local library-path = $(self.library-path) ;
200            if ! $(library-path) && $(use-environment)
201            {
202                library-path = [ modules.peek : $(name:U)_LIBRARY_PATH ] ;
203                # Backwards compatibility only
204                library-path ?= [ modules.peek : $(name:U)_LIBPATH ] ;
205            }
206
207            local toolset = [ $(property-set).get <toolset> ] ;
208            local toolset-version-property = "<toolset-$(toolset):version>" ;
209            local relevant = [ property.select <target-os> <toolset>
210                $(toolset-version-property) <link> <address-model> <architecture> :
211                [ $(property-set).raw ] ] ;
212
213            local key = ac-library-$(name)-$(relevant:J=-) ;
214            local lookup = [ config-cache.get $(key) ] ;
215
216            if $(lookup)
217            {
218                if $(lookup) = missing
219                {
220                    configure.log-library-search-result $(name) : "no  (cached)" ;
221                    return [ property-set.empty ] ;
222                }
223                else
224                {
225                    local includes = $(lookup[1]) ;
226                    if $(includes) = %default
227                    {
228                        includes = ;
229                    }
230                    local library = [ ac.construct-library $(lookup[2]) :
231                        [ $(property-set).refine [ property-set.create $(lookup[3]) ] ] : $(library-path) ] ;
232                    configure.log-library-search-result $(name) : "yes (cached)" ;
233                    return [ $(library[1]).add-raw <include>$(includes) ] $(library[2-]) ;
234                }
235            }
236            else
237            {
238                local includes = [ ac.find-include-path $(property-set) : $(self.header) : $(include-path) ] ;
239                local library = [ ac.find-library $(property-set) : $(libnames) : $(library-path) ] ;
240                if $(includes) && $(library)
241                {
242                    config-cache.set $(key) : $(includes) $(library) ;
243                    if $(includes) = %default
244                    {
245                        includes = ;
246                    }
247                    library = [ ac.construct-library $(library[1]) :
248                        [ $(property-set).refine [ property-set.create $(library[2]) ] ]  : $(library-path) ] ;
249                    configure.log-library-search-result $(name) : "yes" ;
250                    return [ $(library[1]).add-raw <include>$(includes) ] $(library[2-]) ;
251                }
252                else
253                {
254                    config-cache.set $(key) : missing ;
255                    configure.log-library-search-result $(name) : "no" ;
256                    return [ property-set.empty ] ;
257                }
258            }
259        }
260    }
261}
262
263class check-library-worker
264{
265    import property-set ;
266    import targets ;
267    import property ;
268
269    rule __init__ ( target : true-properties * : false-properties * )
270    {
271        self.target = $(target) ;
272        self.true-properties = $(true-properties) ;
273        self.false-properties = $(false-properties) ;
274    }
275
276    rule check ( properties * )
277    {
278        local choosen ;
279        local t = [ targets.current ] ;
280        local p =  [ $(t).project ] ;
281        local ps = [ property-set.create $(properties) ] ;
282        ps = [ $(ps).propagated ] ;
283        local generated =
284            [ targets.generate-from-reference $(self.target) : $(p) : $(ps) ] ;
285        if $(generated[2])
286        {
287            choosen = $(self.true-properties) ;
288        }
289        else
290        {
291            choosen = $(self.false-properties) ;
292        }
293        return [ property.evaluate-conditionals-in-context $(choosen) :
294            $(properties) ] ;
295    }
296}
297
298rule check-library ( target : true-properties * : false-properties * )
299{
300    local instance = [ class.new check-library-worker $(target) :
301        $(true-properties) : $(false-properties) ] ;
302    return <conditional>@$(instance).check ;
303}
304