Source

Goblin Camp / build / boost-build / tools / builtin.jam

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
# Copyright 2002, 2003, 2004, 2005 Dave Abrahams
# Copyright 2002, 2005, 2006, 2007, 2010 Rene Rivera
# Copyright 2006 Juergen Hunold
# Copyright 2005 Toon Knapen
# 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)

# Defines standard features and rules.

import alias ;
import "class" : new ;
import errors ;
import feature ;
import generators ;
import numbers ;
import os ;
import path ;
import print ;
import project ;
import property ;
import regex ;
import scanner ;
import sequence ;
import stage ;
import symlink ;
import toolset ;
import type ;
import types/register ;
import utility ;
import virtual-target ;
import message ;
import convert ;

# FIXME: the following generate module import is not needed here but removing it
# too hastly will break using code (e.g. the main Boost library Jamroot file)
# that forgot to import the generate module before calling the generate rule.
import generate ;


.os-names = aix bsd cygwin darwin freebsd hpux iphone linux netbsd
    openbsd osf qnx qnxnto sgi solaris unix unixware windows 
    elf # Not actually an OS -- used for targeting bare metal where
        # object format is ELF.  This catches both -elf and -eabi gcc
        # targets and well as other compilers targeting ELF. It is not
        # clear how often do we need to key of ELF specifically as opposed
        # to other bare metal targets, but let's stick with gcc naming.
    ;

# Feature used to determine which OS we're on. New <target-os> and <host-os>
# features should be used instead.
local os = [ modules.peek : OS ] ;
feature.feature os : $(os) : propagated link-incompatible ;


# Translates from bjam current OS to the os tags used in host-os and target-os,
# i.e. returns the running host-os.
#
local rule default-host-os ( )
{
    local host-os ;
    if [ os.name ] in $(.os-names:U)
    {
        host-os = [ os.name ] ;
    }
    else
    {
        switch [ os.name ]
        {
            case NT           : host-os = windows ;
            case AS400        : host-os = unix    ;
            case MINGW        : host-os = windows ;
            case BSDI         : host-os = bsd     ;
            case COHERENT     : host-os = unix    ;
            case DRAGONFLYBSD : host-os = bsd     ;
            case IRIX         : host-os = sgi     ;
            case MACOSX       : host-os = darwin  ;
            case KFREEBSD     : host-os = freebsd ;
            case LINUX        : host-os = linux   ;
            case SUNOS        : 
              ECHO "SunOS is not a supported operating system." ;
              ECHO "We believe last version of SunOS was released in 1992, " ;
              ECHO "so if you get this message, something is very wrong with configuration logic. " ;
              ECHO "Please report this as a bug. " ;
              EXIT ;
            case *            : host-os = unix    ;
        }
    }
    return $(host-os:L) ;
}


# The two OS features define a known set of abstract OS names. The host-os is
# the OS under which bjam is running. Even though this should really be a fixed
# property we need to list all the values to prevent unknown value errors. Both
# set the default value to the current OS to account for the default use case of
# building on the target OS.
feature.feature host-os : $(.os-names) ;
feature.set-default host-os : [ default-host-os ] ;

feature.feature target-os : $(.os-names) : propagated link-incompatible ;
feature.set-default target-os : [ default-host-os ] ;


feature.feature toolset            :                 : implicit propagated symmetric ;
feature.feature stdlib             : native          : propagated composite ;
feature.feature link               : shared static   : propagated ;
feature.feature runtime-link       : shared static   : propagated ;
feature.feature runtime-debugging  : on off          : propagated ;
feature.feature optimization       : off speed space : propagated ;
feature.feature profiling          : off on          : propagated ;
feature.feature inlining           : off on full     : propagated ;
feature.feature threading          : single multi    : propagated ;
feature.feature rtti               : on off          : propagated ;
feature.feature exception-handling : on off          : propagated ;

# Whether there is support for asynchronous EH (e.g. catching SEGVs).
feature.feature asynch-exceptions  : off on          : propagated ;

# Whether all extern "C" functions are considered nothrow by default.
feature.feature extern-c-nothrow   : off on          : propagated ;

feature.feature debug-symbols      : on off          : propagated ;
# Controls whether the binary should be stripped -- that is have
# everything not necessary to running removed. This option should
# not be very often needed. Also, this feature will show up in
# target paths of everything, not just binaries. Should fix that
# when impelementing feature relevance.
feature.feature strip              : off on          : propagated ;
feature.feature define             :                 : free ;
feature.feature undef              :                 : free ;
feature.feature "include"          :                 : free path ; #order-sensitive ;
feature.feature cflags             :                 : free ;
feature.feature cxxflags           :                 : free ;
feature.feature fflags             :                 : free ;
feature.feature asmflags           :                 : free ;
feature.feature linkflags          :                 : free ;
feature.feature archiveflags       :                 : free ;
feature.feature version            :                 : free ;

# Generic, i.e. non-language specific, flags for tools.
feature.feature flags           : : free ;
feature.feature location-prefix : : free ;


# The following features are incidental since they have no effect on built
# products. Not making them incidental will result in problems in corner cases,
# e.g.:
#
#    unit-test a : a.cpp : <use>b ;
#    lib b : a.cpp b ;
#
# Here, if <use> is not incidental, we would decide we have two targets for
# a.obj with different properties and complain about it.
#
# Note that making a feature incidental does not mean it is ignored. It may be
# ignored when creating a virtual target, but the rest of build process will use
# them.
feature.feature use                 : : free dependency incidental ;
feature.feature dependency          : : free dependency incidental ;
feature.feature implicit-dependency : : free dependency incidental ;

feature.feature warnings :
    on         # Enable default/"reasonable" warning level for the tool.
    all        # Enable all possible warnings issued by the tool.
    off        # Disable all warnings issued by the tool.
  : incidental propagated ;

feature.feature warnings-as-errors :
    off        # Do not fail the compilation if there are warnings.
    on         # Fail the compilation if there are warnings.
  : incidental propagated ;

# Feature that allows us to configure the maximal template instantiation depth
# level allowed by a C++ compiler. Applies only to C++ toolsets whose compilers
# actually support this configuration setting.
#
# Note that Boost Build currently does not allow defining features that take any
# positive integral value as a parameter, which is what we need here, so we just
# define some of the values here and leave it up to the user to extend this set
# as he needs using the feature.extend rule.
#
# TODO: This should be upgraded as soon as Boost Build adds support for custom
# validated feature values or at least features allowing any positive integral
# value. See related Boost Build related trac ticket #194.
#
feature.feature c++-template-depth
    :
        [ numbers.range 64 1024 : 64 ]
        [ numbers.range 20 1000 : 10 ]
        #   Maximum template instantiation depth guaranteed for ANSI/ISO C++
        # conforming programs.
        17
    :
        incidental optional propagated ;

feature.feature source              :            : free dependency incidental ;
feature.feature library             :            : free dependency incidental ;
feature.feature file                :            : free dependency incidental ;
feature.feature find-shared-library :            : free ; #order-sensitive ;
feature.feature find-static-library :            : free ; #order-sensitive ;
feature.feature library-path        :            : free path ; #order-sensitive ;

# Internal feature.
feature.feature library-file        :            : free dependency ;

feature.feature name                :            : free ;
feature.feature tag                 :            : free ;
feature.feature search              :            : free path ; #order-sensitive ;
feature.feature location            :            : free path ;
feature.feature dll-path            :            : free path ;
feature.feature hardcode-dll-paths  : true false : incidental ;


# An internal feature that holds the paths of all dependency shared libraries.
# On Windows, it is needed so that we can add all those paths to PATH when
# running applications. On Linux, it is needed to add proper -rpath-link command
# line options.
feature.feature xdll-path : : free path ;

# Provides means to specify def-file for windows DLLs.
feature.feature def-file : : free dependency ;

feature.feature suppress-import-lib : false true : incidental ;

# Internal feature used to store the name of a bjam action to call when building
# a target.
feature.feature action : : free ;

# This feature is used to allow specific generators to run. For example, QT
# tools can only be invoked when QT library is used. In that case, <allow>qt
# will be in usage requirement of the library.
feature.feature allow : : free ;

# The addressing model to generate code for. Currently a limited set only
# specifying the bit size of pointers.
feature.feature address-model : 16 32 64 32_64 : propagated optional ;

# Type of CPU architecture to compile for.
feature.feature architecture :
    # x86 and x86-64
    x86

    # ia64
    ia64

    # Sparc
    sparc

    # RS/6000 & PowerPC
    power

    # MIPS/SGI
    mips1 mips2 mips3 mips4 mips32 mips32r2 mips64

    # HP/PA-RISC
    parisc

    # Advanced RISC Machines
    arm

    # Combined architectures for platforms/toolsets that support building for
    # multiple architectures at once. "combined" would be the default multi-arch
    # for the toolset.
    combined
    combined-x86-power

    : propagated optional ;

# The specific instruction set in an architecture to compile.
feature.feature instruction-set :
    # x86 and x86-64
    native i386 i486 i586 i686 pentium pentium-mmx pentiumpro pentium2 pentium3
    pentium3m pentium-m pentium4 pentium4m prescott nocona core2 conroe conroe-xe
    conroe-l allendale mermon mermon-xe kentsfield kentsfield-xe penryn wolfdale
    yorksfield nehalem k6 k6-2 k6-3 athlon athlon-tbird athlon-4 athlon-xp
    athlon-mp k8 opteron athlon64 athlon-fx winchip-c6 winchip2 c3 c3-2

    # ia64
    itanium itanium1 merced itanium2 mckinley

    # Sparc
    v7 cypress v8 supersparc sparclite hypersparc sparclite86x f930 f934
    sparclet tsc701 v9 ultrasparc ultrasparc3

    # RS/6000 & PowerPC
    401 403 405 405fp 440 440fp 505 601 602 603 603e 604 604e 620 630 740 7400
    7450 750 801 821 823 860 970 8540 power-common ec603e g3 g4 g5 power power2
    power3 power4 power5 powerpc powerpc64 rios rios1 rsc rios2 rs64a

    # MIPS
    4kc 4kp 5kc 20kc m4k r2000 r3000 r3900 r4000 r4100 r4300 r4400 r4600 r4650
    r6000 r8000 rm7000 rm9000 orion sb1 vr4100 vr4111 vr4120 vr4130 vr4300
    vr5000 vr5400 vr5500

    # HP/PA-RISC
    700 7100 7100lc 7200 7300 8000

    # Advanced RISC Machines
    armv2 armv2a armv3 armv3m armv4 armv4t armv5 armv5t armv5te armv6 armv6j iwmmxt ep9312

    : propagated optional ;

# Used to select a specific variant of C++ ABI if the compiler supports several.
feature.feature c++abi : : propagated optional ;

feature.feature conditional : : incidental free ;

# The value of 'no' prevents building of a target.
feature.feature build : yes no : optional ;

# Windows-specific features

feature.feature user-interface : console gui wince native auto ;

feature.feature variant : : implicit composite propagated symmetric ;


# Declares a new variant.
#
# First determines explicit properties for this variant, by refining parents'
# explicit properties with the passed explicit properties. The result is
# remembered and will be used if this variant is used as parent.
#
# Second, determines the full property set for this variant by adding to the
# explicit properties default values for all missing non-symmetric properties.
#
# Lastly, makes appropriate value of 'variant' property expand to the full
# property set.
#
rule variant ( name            # Name of the variant
    : parents-or-properties *  # Specifies parent variants, if
                               # 'explicit-properties' are given, and
                               # explicit-properties or parents otherwise.
    : explicit-properties *    # Explicit properties.
    )
{
    local parents ;
    if ! $(explicit-properties)
    {
        if $(parents-or-properties[1]:G)
        {
            explicit-properties = $(parents-or-properties) ;
        }
        else
        {
            parents = $(parents-or-properties) ;
        }
    }
    else
    {
        parents = $(parents-or-properties) ;
    }

    # The problem is that we have to check for conflicts between base variants.
    if $(parents[2])
    {
        errors.error "multiple base variants are not yet supported" ;
    }

    local inherited ;
    # Add explicitly specified properties for parents.
    for local p in $(parents)
    {
        # TODO: This check may be made stricter.
        if ! [ feature.is-implicit-value $(p) ]
        {
            errors.error "Invalid base variant" $(p)  ;
        }

        inherited += $(.explicit-properties.$(p)) ;
    }
    property.validate $(explicit-properties) ;
    explicit-properties = [ property.refine $(inherited)
        : $(explicit-properties) ] ;

    # Record explicitly specified properties for this variant. We do this after
    # inheriting parents' properties so they affect other variants derived from
    # this one.
    .explicit-properties.$(name) = $(explicit-properties) ;

    feature.extend variant : $(name) ;
    feature.compose <variant>$(name) : $(explicit-properties) ;
}
IMPORT $(__name__) : variant : : variant ;


variant debug   : <optimization>off <debug-symbols>on <inlining>off
                  <runtime-debugging>on ;
variant release : <optimization>speed <debug-symbols>off <inlining>full
                  <runtime-debugging>off <define>NDEBUG ;
variant profile : release : <profiling>on <debug-symbols>on ;


class searched-lib-target : abstract-file-target
{
    rule __init__ ( name
        : project
        : shared ?
        : search *
        : action
    )
    {
        abstract-file-target.__init__ $(name) : SEARCHED_LIB : $(project)
          : $(action) : ;

        self.shared = $(shared) ;
        self.search = $(search) ;
    }

    rule shared ( )
    {
        return $(self.shared) ;
    }

    rule search ( )
    {
        return $(self.search) ;
    }

    rule actualize-location ( target )
    {
        NOTFILE $(target) ;
    }

    rule path ( )
    {
    }
}


# The generator class for libraries (target type LIB). Depending on properties
# it will request building of the appropriate specific library type --
# -- SHARED_LIB, STATIC_LIB or SHARED_LIB.
#
class lib-generator : generator
{
    rule __init__ ( * : * )
    {
        generator.__init__ $(1) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ;
    }

    rule run ( project name ? : property-set : sources * )
    {
        # The lib generator is composing, and can be only invoked with an
        # explicit name. This check is present in generator.run (and so in
        # builtin.linking-generator) but duplicated here to avoid doing extra
        # work.
        if $(name)
        {
            local properties = [ $(property-set).raw ] ;
            # Determine the needed target type.
            local actual-type ;
                # <source>files can be generated by <conditional>@rule feature
                # in which case we do not consider it a SEARCHED_LIB type.
            if ! <source> in $(properties:G) &&
               ( <search> in $(properties:G) || <name> in $(properties:G) )
            {
                actual-type = SEARCHED_LIB ;
            }
            else if <file> in $(properties:G)
            {
                actual-type = LIB ;
            }
            else if <link>shared in $(properties)
            {
                actual-type = SHARED_LIB ;
            }
            else
            {
                actual-type = STATIC_LIB ;
            }
            property-set = [ $(property-set).add-raw <main-target-type>LIB ] ;
            # Construct the target.
            return [ generators.construct $(project) $(name) : $(actual-type)
                : $(property-set) : $(sources) ] ;
        }
    }

    rule viable-source-types ( )
    {
        return * ;
    }
}


generators.register [ new lib-generator builtin.lib-generator :  : LIB ] ;


# The implementation of the 'lib' rule. Beyond standard syntax that rule allows
# simplified: "lib a b c ;".
#
rule lib ( names + : sources * : requirements * : default-build * :
    usage-requirements * )
{
    if $(names[2])
    {
        if <name> in $(requirements:G)
        {
            errors.user-error "When several names are given to the 'lib' rule" :
                "it is not allowed to specify the <name> feature." ;
        }
        if $(sources)
        {
            errors.user-error "When several names are given to the 'lib' rule" :
                "it is not allowed to specify sources." ;
        }
    }

    # This is a circular module dependency so it must be imported here.
    import targets ;

    local project = [ project.current ] ;
    local result ;

    for local name in $(names)
    {
        local r = $(requirements) ;
        # Support " lib a ; " and " lib a b c ; " syntax.
        if ! $(sources) && ! <name> in $(requirements:G)
                        && ! <file> in $(requirements:G)
        {
            r += <name>$(name) ;
        }
        result += [ targets.main-target-alternative
            [ new typed-target $(name) : $(project) : LIB
                : [ targets.main-target-sources $(sources) : $(name) ]
                : [ targets.main-target-requirements $(r) : $(project) ]
                : [ targets.main-target-default-build $(default-build) : $(project) ]
                : [ targets.main-target-usage-requirements $(usage-requirements) : $(project) ]
            ] ] ;
    }
    return $(result) ;
}
IMPORT $(__name__) : lib : : lib ;


class searched-lib-generator : generator
{
    import property-set ;

    rule __init__ ( )
    {
        # The requirements cause the generators to be tried *only* when we're
        # building a lib target with a 'search' feature. This seems ugly --- all
        # we want is to make sure searched-lib-generator is not invoked deep
        # inside transformation search to produce intermediate targets.
        generator.__init__ searched-lib-generator : : SEARCHED_LIB ;
    }

    rule run ( project name ? : property-set : sources * )
    {
        if $(name)
        {
            # If 'name' is empty, it means we have not been called to build a
            # top-level target. In this case, we just fail immediately, because
            # searched-lib-generator cannot be used to produce intermediate
            # targets.

            local properties = [ $(property-set).raw ] ;
            local shared ;
            if <link>shared in $(properties)
            {
                shared = true ;
            }

            local search = [ feature.get-values <search> : $(properties) ] ;

            local a = [ new null-action $(property-set) ] ;
            local lib-name = [ feature.get-values <name> : $(properties) ] ;
            lib-name ?= $(name) ;
            local t = [ new searched-lib-target $(lib-name) : $(project)
                : $(shared) : $(search) : $(a) ] ;
            # We return sources for a simple reason. If there is
            #    lib png : z : <name>png ;
            # the 'z' target should be returned, so that apps linking to 'png'
            # will link to 'z', too.
            return [ property-set.create <xdll-path>$(search) ]
                   [ virtual-target.register $(t) ] $(sources) ;
        }
    }
}

generators.register [ new searched-lib-generator ] ;


class prebuilt-lib-generator : generator
{
    rule __init__ ( * : * )
    {
        generator.__init__ $(1) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ;
    }

    rule run ( project name ? : property-set : sources * )
    {
        local f = [ $(property-set).get <file> ] ;
        return $(f) $(sources) ;
    }
}

generators.register
  [ new prebuilt-lib-generator builtin.prebuilt : : LIB : <file> ] ;

generators.override builtin.prebuilt : builtin.lib-generator ;


class compile-action : action
{
    import sequence ;

    rule __init__ ( targets * : sources * : action-name : properties * )
    {
        action.__init__ $(targets) : $(sources) : $(action-name) : $(properties) ;
    }

    # For all virtual targets for the same dependency graph as self, i.e. which
    # belong to the same main target, add their directories to the include path.
    #
    rule adjust-properties ( property-set )
    {
        local s = [ $(self.targets[1]).creating-subvariant ] ;
        return [ $(property-set).add-raw
          [ $(s).implicit-includes "include" : H ] ] ;
    }
}


# Declare a special compiler generator. The only thing it does is changing the
# type used to represent 'action' in the constructed dependency graph to
# 'compile-action'. That class in turn adds additional include paths to handle
# cases when a source file includes headers which are generated themselves.
#
class C-compiling-generator : generator
{
    rule __init__ ( id : source-types + : target-types + : requirements *
        : optional-properties * )
    {
        generator.__init__ $(id) : $(source-types) : $(target-types) :
            $(requirements) : $(optional-properties) ;
    }

    rule action-class ( )
    {
        return compile-action ;
    }
}


rule register-c-compiler ( id : source-types + : target-types + : requirements *
    : optional-properties * )
{
    generators.register [ new C-compiling-generator $(id) : $(source-types) :
        $(target-types) : $(requirements) : $(optional-properties) ] ;
}

# FIXME: this is ugly, should find a better way (we would like client code to
# register all generators as "generators.some-rule" instead of
# "some-module.some-rule".)
#
IMPORT $(__name__) : register-c-compiler : : generators.register-c-compiler ;


# The generator class for handling EXE and SHARED_LIB creation.
#
class linking-generator : generator
{
    import path ;
    import project ;
    import property-set ;
    import type ;

    rule __init__ ( id
        composing ?    :  # The generator will be composing if a non-empty
                          # string is passed or the parameter is not given. To
                          # make the generator non-composing, pass an empty
                          # string ("").
        source-types + :
        target-types + :
        requirements * )
    {
        composing ?= true ;
        generator.__init__ $(id) $(composing) : $(source-types)
            : $(target-types) : $(requirements) ;
    }

    rule run ( project name ? : property-set : sources + )
    {
        sources += [ $(property-set).get <library>  ] ;

        # Add <library-path> properties for all searched libraries.
        local extra ;
        for local s in $(sources)
        {
            if [ $(s).type ] = SEARCHED_LIB
            {
                local search = [ $(s).search ] ;
                extra += <library-path>$(search) ;
            }
        }

        # It is possible that sources include shared libraries that did not came
        # from 'lib' targets, e.g. .so files specified as sources. In this case
        # we have to add extra dll-path properties and propagate extra xdll-path
        # properties so that application linking to us will get xdll-path to
        # those libraries.
        local extra-xdll-paths ;
        for local s in $(sources)
        {
            if [ type.is-derived [ $(s).type ] SHARED_LIB ] && ! [ $(s).action ]
            {
                # Unfortunately, we do not have a good way to find the path to a
                # file, so use this nasty approach.
                #
                # TODO: This needs to be done better. One thing that is really
                # broken with this is that it does not work correctly with
                # projects having multiple source locations.
                local p = [ $(s).project ] ;
                local location = [ path.root [ $(s).name ]
                    [ $(p).get source-location ] ] ;
                extra-xdll-paths += [ path.parent $(location) ] ;
            }
        }

        # Hardcode DLL paths only when linking executables.
        # Pros: do not need to relink libraries when installing.
        # Cons: "standalone" libraries (plugins, python extensions) can not
        # hardcode paths to dependent libraries.
        if [ $(property-set).get <hardcode-dll-paths> ] = true
            && [ type.is-derived $(self.target-types[1]) EXE ]
        {
            local xdll-path = [ $(property-set).get <xdll-path> ] ;
            extra += <dll-path>$(xdll-path) <dll-path>$(extra-xdll-paths) ;
        }

        if $(extra)
        {
            property-set = [ $(property-set).add-raw $(extra) ] ;
        }

        local result = [ generator.run $(project) $(name) : $(property-set)
            : $(sources) ] ;

        local ur ;
        if $(result)
        {
            ur = [ extra-usage-requirements $(result) : $(property-set) ] ;
            ur = [ $(ur).add
              [ property-set.create <xdll-path>$(extra-xdll-paths) ] ] ;
        }
        return $(ur) $(result) ;
    }

    rule extra-usage-requirements ( created-targets * : property-set )
    {
        local result = [ property-set.empty ] ;
        local extra ;

        # Add appropricate <xdll-path> usage requirements.
        local raw = [ $(property-set).raw ] ;
        if <link>shared in $(raw)
        {
            local paths ;
            local pwd = [ path.pwd ] ;
            for local t in $(created-targets)
            {
                if [ type.is-derived [ $(t).type ] SHARED_LIB ]
                {
                    paths += [ path.root [ path.make [ $(t).path ] ] $(pwd) ] ;
                }
            }
            extra += $(paths:G=<xdll-path>) ;
        }

        # We need to pass <xdll-path> features that we've got from sources,
        # because if a shared library is built, exe using it needs to know paths
        # to other shared libraries this one depends on in order to be able to
        # find them all at runtime.

        # Just pass all features in property-set, it is theorically possible
        # that we will propagate <xdll-path> features explicitly specified by
        # the user, but then the user is to blaim for using an internal feature.
        local values = [ $(property-set).get <xdll-path> ] ;
        extra += $(values:G=<xdll-path>) ;

        if $(extra)
        {
            result = [ property-set.create $(extra) ] ;
        }
        return $(result) ;
    }

    rule generated-targets ( sources + : property-set : project name ? )
    {
        local sources2 ;     # Sources to pass to inherited rule.
        local properties2 ;  # Properties to pass to inherited rule.
        local libraries ;    # Library sources.

        # Searched libraries are not passed as arguments to the linker but via
        # some option. So, we pass them to the action using a property.
        properties2 = [ $(property-set).raw ] ;
        local fsa ;
        local fst ;
        for local s in $(sources)
        {
            if [ type.is-derived [ $(s).type ] SEARCHED_LIB ]
            {
                local name = [ $(s).name ] ;
                if [ $(s).shared ]
                {
                    fsa += $(name) ;
                }
                else
                {
                    fst += $(name) ;
                }
            }
            else
            {
                sources2 += $(s) ;
            }
        }
        properties2 += <find-shared-library>$(fsa:J=&&)
                       <find-static-library>$(fst:J=&&) ;

        return [ generator.generated-targets $(sources2)
            : [ property-set.create $(properties2) ] : $(project) $(name) ] ;
    }
}


rule register-linker ( id composing ? : source-types + : target-types +
    : requirements * )
{
    generators.register [ new linking-generator $(id) $(composing)
        : $(source-types) : $(target-types) : $(requirements) ] ;
}


# The generator class for handling STATIC_LIB creation.
#
class archive-generator : generator
{
    import property-set ;

    rule __init__ ( id composing ? : source-types + : target-types +
        : requirements * )
    {
        composing ?= true ;
        generator.__init__ $(id) $(composing) : $(source-types)
            : $(target-types) : $(requirements) ;
    }

    rule run ( project name ? : property-set : sources + )
    {
        sources += [ $(property-set).get <library>  ] ;

        local result = [ generator.run $(project) $(name) : $(property-set)
            : $(sources) ] ;

        # For static linking, if we get a library in source, we can not directly
        # link to it so we need to cause our dependencies to link to that
        # library. There are two approaches:
        # - adding the library to the list of returned targets.
        # - using the <library> usage requirements.
        # The problem with the first is:
        #
        #     lib a1 : : <file>liba1.a ;
        #     lib a2 : a2.cpp a1 : <link>static ;
        #     install dist : a2 ;
        #
        # here we will try to install 'a1', even though it is not necessary in
        # the general case. With the second approach, even indirect dependants
        # will link to the library, but it should not cause any harm. So, return
        # all LIB sources together with created targets, so that dependants link
        # to them.
        local usage-requirements ;
        if [ $(property-set).get <link> ] = static
        {
            for local t in $(sources)
            {
                if [ type.is-derived [ $(t).type ] LIB ]
                {
                    usage-requirements += <library>$(t) ;
                }
            }
        }

        usage-requirements = [ property-set.create $(usage-requirements) ] ;

        return $(usage-requirements) $(result) ;
    }
}


rule register-archiver ( id composing ? : source-types + : target-types +
    : requirements * )
{
    generators.register [ new archive-generator $(id) $(composing)
        : $(source-types) : $(target-types) : $(requirements) ] ;
}


# Generator that accepts everything and produces nothing. Useful as a general
# fallback for toolset-specific actions like PCH generation.
#
class dummy-generator : generator
{
    import property-set ;

    rule run ( project name ? : property-set : sources + )
    {
        return [ property-set.empty ] ;
    }
}

IMPORT $(__name__) : register-linker register-archiver
    : : generators.register-linker generators.register-archiver ;