1# Copyright 2003, 2004, 2005 Dave Abrahams
2# Copyright 2003, 2004, 2005 Douglas Gregor
3# Copyright 2005, 2006, 2007 Rene Rivera
4# Copyright 2003, 2004, 2005 Vladimir Prus
5# Distributed under the Boost Software License, Version 1.0.
6# (See accompanying file LICENSE_1_0.txt or copy at
7# http://www.boost.org/LICENSE_1_0.txt)
8
9# This module defines rules to handle generation of documentation from BoostBook
10# sources.
11#
12# The type of output is controlled by the <format> feature which can have the
13# following values:
14#   * html: Generates html documentation. This is the default.
15#   * xhtml: Generates xhtml documentation.
16#   * htmlhelp: Generates html help output.
17#   * onehtml: Generates a single html page.
18#   * man: Generates man pages.
19#   * pdf: Generates pdf documentation.
20#   * ps: Generates postscript output.
21#   * docbook: Generates docbook XML.
22#   * fo: Generates XSL formating objects.
23#   * tests: Extracts test cases from the boostbook XML.
24#
25# <format> is an implicit feature, so for example, typing pdf on the command
26# line is a short-cut for format=pdf.
27
28import build-system ;
29import "class" : new ;
30import common ;
31import feature ;
32import generators ;
33import make ;
34import modules ;
35import os ;
36import path ;
37import print ;
38import project ;
39import property ;
40import property-set ;
41import regex ;
42import scanner ;
43import sequence ;
44import targets ;
45import type ;
46import virtual-target ;
47import xsltproc ;
48
49# Make this module into a project.
50project.initialize $(__name__) ;
51project boostbook ;
52
53.debug-configuration = [ MATCH ^(--debug-configuration)$ : [ modules.peek : ARGV
54    ] ] ;
55
56feature.feature format
57    : html xhtml htmlhelp onehtml man pdf ps docbook fo tests
58    : incidental implicit composite propagated ;
59
60type.register DTDXML : dtdxml ;
61type.register XML : xml ;
62type.register BOOSTBOOK : boostbook : XML ;
63type.register DOCBOOK : docbook : XML ;
64type.register FO : fo : XML ;
65type.register PDF : pdf ;
66type.register PS : ps ;
67type.register XSLT : xsl : XML ;
68type.register HTMLDIR ;
69type.register XHTMLDIR ;
70type.register HTMLHELP ;
71type.register MANPAGES ;
72type.register TESTS : tests ;
73
74
75# Initialize BoostBook support.
76#
77rule init (
78      docbook-xsl-dir ? # The DocBook XSL stylesheet directory. If not provided,
79                        # we use DOCBOOK_XSL_DIR from the environment (if
80                        # available) or look in standard locations. Otherwise,
81                        # we let the XML processor load the stylesheets
82                        # remotely.
83
84    : docbook-dtd-dir ? # The DocBook DTD directory. If not provided, we use
85                        # DOCBOOK_DTD_DIR From the environment (if available) or
86                        # look in standard locations. Otherwise, we let the XML
87                        # processor load the DTD remotely.
88
89    : boostbook-dir ?   # The BoostBook directory with the DTD and XSL subdirs.
90)
91{
92    if ! $(.initialized)
93    {
94        .initialized = true ;
95
96        check-boostbook-dir $(boostbook-dir) ;
97        find-tools $(docbook-xsl-dir) : $(docbook-dtd-dir) : $(boostbook-dir) ;
98
99        # Register generators only if we were called via "using boostbook ;"
100        local reg-gen = generators.register-standard ;
101        $(reg-gen) boostbook.dtdxml-to-boostbook  : DTDXML  : XML ;
102        $(reg-gen) boostbook.boostbook-to-docbook : XML     : DOCBOOK ;
103        $(reg-gen) boostbook.boostbook-to-tests   : XML     : TESTS ;
104        $(reg-gen) boostbook.docbook-to-onehtml   : DOCBOOK : HTML ;
105        $(reg-gen) boostbook.docbook-to-htmldir   : DOCBOOK : HTMLDIR ;
106        $(reg-gen) boostbook.docbook-to-xhtmldir  : DOCBOOK : XHTMLDIR ;
107        $(reg-gen) boostbook.docbook-to-htmlhelp  : DOCBOOK : HTMLHELP ;
108        $(reg-gen) boostbook.docbook-to-manpages  : DOCBOOK : MANPAGES ;
109        $(reg-gen) boostbook.docbook-to-fo        : DOCBOOK : FO ;
110
111        # The same about Jamfile main target rules.
112        IMPORT $(__name__) : boostbook : : boostbook ;
113    }
114    else
115    {
116        if $(docbook-xsl-dir)
117        {
118            modify-config ;
119            .docbook-xsl-dir = [ path.make $(docbook-xsl-dir) ] ;
120            check-docbook-xsl-dir ;
121        }
122        if $(docbook-dtd-dir)
123        {
124            modify-config ;
125            .docbook-dtd-dir = [ path.make $(docbook-dtd-dir) ] ;
126            check-docbook-dtd-dir ;
127        }
128        if $(boostbook-dir)
129        {
130            modify-config ;
131            check-boostbook-dir $(boostbook-dir) ;
132            local boostbook-xsl-dir = [ path.glob $(boostbook-dir) : xsl ] ;
133            local boostbook-dtd-dir = [ path.glob $(boostbook-dir) : dtd ] ;
134            .boostbook-xsl-dir = $(boostbook-xsl-dir[1]) ;
135            .boostbook-dtd-dir = $(boostbook-dtd-dir[1]) ;
136            check-boostbook-xsl-dir ;
137            check-boostbook-dtd-dir ;
138        }
139    }
140}
141
142
143local rule lock-config ( )
144{
145    if ! $(.initialized)
146    {
147        import errors ;
148        errors.user-error BoostBook has not been configured. ;
149    }
150    if ! $(.config-locked)
151    {
152        .config-locked = true ;
153
154        if $(.error-message)
155        {
156            print-error $(.error-message) ;
157        }
158    }
159}
160
161
162local rule modify-config ( )
163{
164    if $(.config-locked)
165    {
166        import errors ;
167        errors.user-error BoostBook configuration cannot be changed after it has
168            been used. ;
169    }
170}
171
172rule print-error ( location message * )
173{
174    ECHO error: at $(location) ;
175    ECHO error: $(message) ;
176    EXIT ;
177}
178
179rule make-error ( message * )
180{
181    import errors ;
182    return [ errors.nearest-user-location ] $(message) ;
183}
184
185
186rule find-boost-in-registry ( keys * )
187{
188    local boost-root ;
189    for local R in $(keys)
190    {
191        local installed-boost = [ W32_GETREG
192            "HKEY_LOCAL_MACHINE\\SOFTWARE\\$(R)" : "InstallRoot" ] ;
193        if $(installed-boost)
194        {
195            boost-root += [ path.make $(installed-boost) ] ;
196        }
197    }
198    return $(boost-root) ;
199}
200
201
202rule check-docbook-xsl-dir ( )
203{
204    if $(.docbook-xsl-dir)
205    {
206        if ! [ path.glob $(.docbook-xsl-dir) : common/common.xsl ]
207        {
208            .error-message = [ make-error BoostBook: could not find docbook XSL stylesheets
209                in: [ path.native $(.docbook-xsl-dir) ] ] ;
210        }
211        else if $(.debug-configuration)
212        {
213            ECHO notice: BoostBook: found docbook XSL stylesheets in: [
214                path.native $(.docbook-xsl-dir) ] ;
215        }
216    }
217}
218
219
220rule check-docbook-dtd-dir ( )
221{
222    if $(.docbook-dtd-dir)
223    {
224        if ! [ path.glob $(.docbook-dtd-dir) : docbookx.dtd ]
225        {
226            .error-message = [ make-error BoostBook: could not find docbook DTD in: [
227                path.native $(.docbook-dtd-dir) ] ] ;
228        }
229        else if $(.debug-configuration)
230        {
231            ECHO notice: BoostBook: found docbook DTD in: [ path.native
232                $(.docbook-dtd-dir) ] ;
233        }
234    }
235}
236
237
238rule check-boostbook-xsl-dir ( )
239{
240    if ! $(.boostbook-xsl-dir)
241    {
242        .error-message = [ make-error BoostBook: could not find boostbook XSL stylesheets. ] ;
243    }
244    else if ! [ path.glob $(.boostbook-xsl-dir) : docbook.xsl ]
245    {
246        .error-message = [ make-error  BoostBook: could not find docbook XSL stylesheets in:
247            [ path.native $(.boostbook-xsl-dir) ] ] ;
248    }
249    else if $(.debug-configuration)
250    {
251        ECHO notice: BoostBook: found boostbook XSL stylesheets in: [
252            path.native $(.boostbook-xsl-dir) ] ;
253    }
254}
255
256
257rule check-boostbook-dtd-dir ( )
258{
259    if ! $(.boostbook-dtd-dir)
260    {
261        .error-message = [ make-error BoostBook: could not find boostbook DTD. ] ;
262    }
263    else if ! [ path.glob $(.boostbook-dtd-dir) : boostbook.dtd ]
264    {
265        .error-message = [ make-error BoostBook: could not find boostbook DTD in: [
266            path.native $(.boostbook-dtd-dir) ] ] ;
267    }
268    else if $(.debug-configuration)
269    {
270        ECHO notice: BoostBook: found boostbook DTD in: [ path.native
271            $(.boostbook-dtd-dir) ] ;
272    }
273}
274
275
276rule check-boostbook-dir ( boostbook-dir ? )
277{
278    if $(boostbook-dir) && ! [ path.glob $(boostbook-dir) : xsl ]
279    {
280        .error-message = [ make-error BoostBook: could not find boostbook in: [ path.native
281            $(boostbook-dir) ] ] ;
282    }
283}
284
285
286rule find-tools ( docbook-xsl-dir ? : docbook-dtd-dir ? : boostbook-dir ? )
287{
288    docbook-xsl-dir ?= [ modules.peek : DOCBOOK_XSL_DIR ] ;
289    docbook-dtd-dir ?= [ modules.peek : DOCBOOK_DTD_DIR ] ;
290    boostbook-dir ?= [ modules.peek : BOOSTBOOK_DIR ] ;
291
292    # Look for the boostbook stylesheets relative to BOOST_ROOT and Boost.Build.
293    local boost-build-root = [ path.make [ build-system.location ] ] ;
294    local boostbook-search-dirs = [ path.join $(boost-build-root) .. .. ] ;
295
296    local boost-root = [ modules.peek : BOOST_ROOT ] ;
297    if $(boost-root)
298    {
299        boostbook-search-dirs += [ path.join [ path.make $(boost-root) ] tools ]
300            ;
301    }
302    boostbook-dir ?= [ path.glob $(boostbook-search-dirs) : boostbook* ] ;
303
304    # Try to find the tools in platform specific locations.
305    if [ os.name ] = NT
306    {
307        # If installed by the Boost installer.
308        local boost-root = ;
309
310        local boost-installer-versions = snapshot cvs 1.33.0 ;
311        local boost-consulting-installer-versions = 1.33.1 1.34.0 1.34.1 ;
312        local boostpro-installer-versions =
313            1.35.0 1.36.0 1.37.0 1.38.0 1.39.0 1.40.0 1.41.0 1.42.0
314            1.43.0 1.44.0 1.45.0 1.46.0 1.47.0 1.48.0 1.49.0 1.50.0 ;
315
316        local old-installer-root = [ find-boost-in-registry
317            Boost.org\\$(boost-installer-versions) ] ;
318
319        # Make sure that the most recent version is searched for first.
320        boost-root += [ sequence.reverse [ find-boost-in-registry
321            Boost-Consulting.com\\$(boost-consulting-installer-versions)
322            boostpro.com\\$(boostpro-installer-versions) ] ] ;
323
324        # Plausible locations.
325        local root = [ PWD ] ;
326        while $(root) != $(root:D) { root = $(root:D) ; }
327        root = [ path.make $(root) ] ;
328        local search-dirs ;
329        local docbook-search-dirs ;
330        for local p in $(boost-root)
331        {
332            search-dirs += [ path.join $(p) tools ] ;
333        }
334        for local p in $(old-installer-root)
335        {
336            search-dirs += [ path.join $(p) share ] ;
337            docbook-search-dirs += [ path.join $(p) share ] ;
338        }
339        search-dirs += [ path.join $(root) Boost tools ] ;
340        search-dirs += [ path.join $(root) Boost share ] ;
341        docbook-search-dirs += [ path.join $(root) Boost share ] ;
342
343        docbook-xsl-dir ?= [ path.glob $(docbook-search-dirs) : docbook-xsl* ] ;
344        docbook-dtd-dir ?= [ path.glob $(docbook-search-dirs) : docbook-xml* ] ;
345        boostbook-dir ?= [ path.glob $(search-dirs) : boostbook* ] ;
346    }
347    else
348    {
349        # Plausible locations.
350
351        local share = /usr/local/share /usr/share /opt/share /opt/local/share ;
352        local dtd-versions = 4.2 ;
353
354        docbook-xsl-dir ?= [ path.glob $(share) : docbook-xsl* ] ;
355        docbook-xsl-dir ?= [ path.glob $(share)/sgml/docbook : xsl-stylesheets ]
356            ;
357        docbook-xsl-dir ?= [ path.glob $(share)/xsl : docbook* ] ;
358
359        docbook-dtd-dir ?= [ path.glob $(share) : docbook-xml* ] ;
360        docbook-dtd-dir ?= [ path.glob $(share)/sgml/docbook :
361            xml-dtd-$(dtd-versions)* ] ;
362        docbook-dtd-dir ?= [ path.glob $(share)/xml/docbook : $(dtd-versions) ]
363            ;
364
365        boostbook-dir ?= [ path.glob $(share) : boostbook* ] ;
366
367        # Ubuntu Linux.
368        docbook-xsl-dir ?= [ path.glob /usr/share/xml/docbook/stylesheet :
369            nwalsh ] ;
370        docbook-dtd-dir ?= [ path.glob /usr/share/xml/docbook/schema/dtd :
371            $(dtd-versions) ] ;
372
373        # SUSE.
374        docbook-xsl-dir ?= [ path.glob /usr/share/xml/docbook/stylesheet/nwalsh
375            : current ] ;
376    }
377
378    if $(docbook-xsl-dir)
379    {
380        .docbook-xsl-dir = [ path.make $(docbook-xsl-dir[1]) ] ;
381    }
382    if $(docbook-dtd-dir)
383    {
384        .docbook-dtd-dir = [ path.make $(docbook-dtd-dir[1]) ] ;
385    }
386
387    if $(.debug-configuration)
388    {
389        ECHO notice: Boost.Book: searching XSL/DTD "in" ;
390        ECHO notice: [ sequence.transform path.native : $(boostbook-dir) ] ;
391    }
392    local boostbook-xsl-dir ;
393    for local dir in $(boostbook-dir)
394    {
395        boostbook-xsl-dir += [ path.glob $(dir) : xsl ] ;
396    }
397    local boostbook-dtd-dir ;
398    for local dir in $(boostbook-dir)
399    {
400        boostbook-dtd-dir += [ path.glob $(dir) : dtd ] ;
401    }
402    .boostbook-xsl-dir = $(boostbook-xsl-dir[1]) ;
403    .boostbook-dtd-dir = $(boostbook-dtd-dir[1]) ;
404
405    check-docbook-xsl-dir ;
406    check-docbook-dtd-dir ;
407    check-boostbook-xsl-dir ;
408    check-boostbook-dtd-dir ;
409}
410
411
412rule xsl-dir
413{
414    lock-config ;
415    return $(.boostbook-xsl-dir) ;
416}
417
418
419rule dtd-dir
420{
421    lock-config ;
422    return $(.boostbook-dtd-dir) ;
423}
424
425
426rule docbook-xsl-dir
427{
428    lock-config ;
429    return $(.docbook-xsl-dir) ;
430}
431
432
433rule docbook-dtd-dir
434{
435    lock-config ;
436    return $(.docbook-dtd-dir) ;
437}
438
439
440rule dtdxml-to-boostbook ( target : source : properties * )
441{
442    lock-config ;
443    xsltproc.xslt $(target) : $(source)
444        "$(.boostbook-xsl-dir)/dtd/dtd2boostbook.xsl" : $(properties) ;
445}
446
447
448rule boostbook-to-docbook ( target : source : properties * )
449{
450    lock-config ;
451    local stylesheet = [ path.native $(.boostbook-xsl-dir)/docbook.xsl ] ;
452    xsltproc.xslt $(target) : $(source) $(stylesheet) : $(properties) ;
453}
454
455
456rule docbook-to-onehtml ( target : source : properties * )
457{
458    lock-config ;
459    local stylesheet = [ path.native $(.boostbook-xsl-dir)/html-single.xsl ] ;
460    xsltproc.xslt $(target) : $(source) $(stylesheet) : $(properties) ;
461}
462
463
464rule docbook-to-htmldir ( target : source : properties * )
465{
466    lock-config ;
467    local stylesheet = [ path.native $(.boostbook-xsl-dir)/html.xsl ] ;
468    xsltproc.xslt-dir $(target) : $(source) $(stylesheet) : $(properties) : html
469        ;
470}
471
472
473rule docbook-to-xhtmldir ( target : source : properties * )
474{
475    lock-config ;
476    local stylesheet = [ path.native $(.boostbook-xsl-dir)/xhtml.xsl ] ;
477    xsltproc.xslt-dir $(target) : $(source) $(stylesheet) : $(properties) :
478        xhtml ;
479}
480
481
482rule docbook-to-htmlhelp ( target : source : properties * )
483{
484    lock-config ;
485    local stylesheet = [ path.native $(.boostbook-xsl-dir)/html-help.xsl ] ;
486    xsltproc.xslt-dir $(target) : $(source) $(stylesheet) : $(properties) :
487        htmlhelp ;
488}
489
490
491rule docbook-to-manpages ( target : source : properties * )
492{
493    lock-config ;
494    local stylesheet = [ path.native $(.boostbook-xsl-dir)/manpages.xsl ] ;
495    xsltproc.xslt-dir $(target) : $(source) $(stylesheet) : $(properties) : man
496        ;
497}
498
499
500rule docbook-to-fo ( target : source : properties * )
501{
502    lock-config ;
503    local stylesheet = [ path.native $(.boostbook-xsl-dir)/fo.xsl ] ;
504    xsltproc.xslt $(target) : $(source) $(stylesheet) : $(properties) ;
505}
506
507
508rule format-catalog-path ( path )
509{
510    local result = $(path) ;
511    if [ xsltproc.is-cygwin ]
512    {
513        if [ os.name ] = NT
514        {
515            drive = [ MATCH ^/(.):(.*)$ : $(path) ] ;
516            result = /cygdrive/$(drive[1])$(drive[2]) ;
517        }
518    }
519    else
520    {
521        if [ os.name ] = CYGWIN
522        {
523            local native-path = [ path.native $(path) ] ;
524            result = [ path.make $(native-path:W) ] ;
525        }
526    }
527    return [ regex.replace $(result) " " "%20" ] ;
528}
529
530
531rule generate-xml-catalog ( target : sources * : properties * )
532{
533    print.output $(target) ;
534
535    # BoostBook DTD catalog entry.
536    local boostbook-dtd-dir = [ boostbook.dtd-dir ] ;
537    if $(boostbook-dtd-dir)
538    {
539        boostbook-dtd-dir = [ format-catalog-path $(boostbook-dtd-dir) ] ;
540    }
541
542    print.text
543        "<?xml version=\"1.0\"?>"
544        "<!DOCTYPE catalog "
545        "  PUBLIC \"-//OASIS/DTD Entity Resolution XML Catalog V1.0//EN\""
546        "  \"http://www.oasis-open.org/committees/entity/release/1.0/catalog.dtd\">"
547        "<catalog xmlns=\"urn:oasis:names:tc:entity:xmlns:xml:catalog\">"
548        "  <rewriteURI uriStartString=\"http://www.boost.org/tools/boostbook/dtd/\" rewritePrefix=\"file://$(boostbook-dtd-dir)/\"/>"
549        : true ;
550
551    local docbook-xsl-dir = [ boostbook.docbook-xsl-dir ] ;
552    if ! $(docbook-xsl-dir)
553    {
554        ECHO "BoostBook warning: no DocBook XSL directory specified." ;
555        ECHO "  If you have the DocBook XSL stylesheets installed, please " ;
556        ECHO "  set DOCBOOK_XSL_DIR to the stylesheet directory on either " ;
557        ECHO "  the command line (via -sDOCBOOK_XSL_DIR=...) or in a " ;
558        ECHO "  Boost.Jam configuration file. The DocBook XSL stylesheets " ;
559        ECHO "  are available here: http://docbook.sourceforge.net/ " ;
560        ECHO "  Stylesheets will be downloaded on-the-fly (very slow!) " ;
561    }
562    else
563    {
564        docbook-xsl-dir = [ format-catalog-path $(docbook-xsl-dir) ] ;
565        print.text "  <rewriteURI uriStartString=\"http://docbook.sourceforge.net/release/xsl/current/\" rewritePrefix=\"file://$(docbook-xsl-dir)/\"/>" ;
566    }
567
568    local docbook-dtd-dir = [ boostbook.docbook-dtd-dir ] ;
569    if ! $(docbook-dtd-dir)
570    {
571        ECHO "BoostBook warning: no DocBook DTD directory specified." ;
572        ECHO "  If you have the DocBook DTD installed, please set " ;
573        ECHO "  DOCBOOK_DTD_DIR to the DTD directory on either " ;
574        ECHO "  the command line (via -sDOCBOOK_DTD_DIR=...) or in a " ;
575        ECHO "  Boost.Jam configuration file. The DocBook DTD is available " ;
576        ECHO "  here: http://www.oasis-open.org/docbook/xml/4.2/index.shtml" ;
577        ECHO "  The DTD will be downloaded on-the-fly (very slow!) " ;
578    }
579    else
580    {
581        docbook-dtd-dir = [ format-catalog-path $(docbook-dtd-dir) ] ;
582        print.text "  <rewriteURI uriStartString=\"http://www.oasis-open.org/docbook/xml/4.2/\" rewritePrefix=\"file://$(docbook-dtd-dir)/\"/>" ;
583    }
584
585    print.text "</catalog>" ;
586}
587
588
589# Returns information about the global XML catalog target, creating it lazily if
590# needed. To get the global catalog generated only once we do not create it in
591# every project that requests it but instead only create it based on the first
592# project requesting it and then reuse it from there for any later requests.
593#
594# To get 'as close as possible' to having the global catalog stored in the same
595# location independent of which folder our build was run from, we assign its
596# target to the given project's base Jamroot project. This works correctly as
597# long as we know the passed project is not standalone or one of Boost Build's
598# configuration module projects, as those to not have a Jamroot project in their
599# parent chain. Note also that we can still get our targets generated in
600# different folders in case when one build project references a target from
601# another build project with its own separate Jamroot.
602#
603# FIXME: Ideally the catalog target should be created as part of the boostbook
604# project and stored in some central location for all used standalone pojects,
605# shared between all builds made on that system. This however would require much
606# more though to add the necessary changes to Boost Build's internal design.
607#
608local rule xml-catalog ( project )
609{
610    if ! $(.xml-catalog)
611    {
612        local project-module = [ $(project).project-module ] ;
613        local root-module = [ project.get-jamroot-module $(project-module) ] ;
614        if ! $(root-module)
615        {
616            import errors ;
617            if [ project.is-config-module $(project-module) ]
618            {
619                errors.user-error boostbook targets can not be declared in Boost
620                    Build's configuration modules. ;
621            }
622            else
623            {
624                errors.user-error boostbook targets can not be declared in
625                    standalone projects. : use a Jamfile/Jamroot project
626                    instead. ;
627            }
628        }
629        local root-project = [ project.target $(root-module) ] ;
630
631        .xml-catalog = [ virtual-target.register [ new file-target
632            boostbook_catalog : XML : $(root-project) : [ new action :
633            boostbook.generate-xml-catalog ] ] ] ;
634        .xml-catalog-file = [ $(.xml-catalog).path ] [ $(.xml-catalog).name ] ;
635        .xml-catalog-file = $(.xml-catalog-file:J=/) ;
636    }
637    return $(.xml-catalog) $(.xml-catalog-file) ;
638}
639
640
641class boostbook-target-class : basic-target
642{
643    import generators ;
644    import property-set ;
645    import virtual-target ;
646
647    rule construct ( name : sources * : property-set )
648    {
649        # Generate the catalog, but only once.
650        IMPORT boostbook : xml-catalog : $(__name__) : boostbook.xml-catalog ;
651        local global-catalog = [ boostbook.xml-catalog [ project ] ] ;
652        local catalog = $(global-catalog[1]) ;
653        local catalog-file = $(global-catalog[2]) ;
654        local targets ;
655
656        # Add the catalog to the property set.
657        property-set = [ $(property-set).add-raw <catalog>$(catalog-file) ] ;
658
659        local type = none ;
660        local manifest ;
661        local format = [ $(property-set).get <format> ] ;
662        switch $(format)
663        {
664            case html     : type = HTMLDIR ; manifest = HTML.manifest ;
665            case xhtml    : type = XHTMLDIR ; manifest = HTML.manifest ;
666            case htmlhelp : type = HTMLHELP ; manifest = HTML.manifest ;
667            case onehtml  : type = HTML ;
668            case man      : type = MANPAGES ; manifest = man.manifest ;
669            case docbook  : type = DOCBOOK ;
670            case fo       : type = FO ;
671            case pdf      : type = PDF ;
672            case ps       : type = PS ;
673            case tests    : type = TESTS ;
674        }
675
676        local target ;
677        if $(manifest)
678        {
679            # Sources --> DOCBOOK.
680            local docbook-target = [ generators.construct [ project ] : DOCBOOK
681                : $(property-set) : $(sources) ] ;
682            docbook-target = $(docbook-target[2]) ;
683            $(docbook-target).depends $(catalog) ;
684
685            # DOCBOOK --> type.
686            target = [ generators.construct [ project ] $(name)_$(manifest) :
687                $(type) : [ $(property-set).add-raw
688                <xsl:param>manifest=$(name)_$(manifest) ] : $(docbook-target) ]
689                ;
690            target = $(target[2]) ;
691            local name = [ $(property-set).get <name> ] ;
692            name ?= $(format) ;
693            $(target).set-path $(name) ;
694        }
695        else
696        {
697            # Sources --> type.
698            target = [ generators.construct [ project ] : $(type) :
699                $(property-set) : $(sources) ] ;
700            target = $(target[2]) ;
701            if ! $(target)
702            {
703                import errors ;
704                errors.error Cannot build documentation type '$(format)'. ;
705            }
706        }
707        $(target).depends $(catalog) ;
708
709        return [ property-set.empty ] $(target) ;
710    }
711}
712
713
714# Declare a boostbook target.
715#
716rule boostbook ( target-name : sources * : requirements * : default-build * )
717{
718    return [ targets.create-metatarget boostbook-target-class :
719        [ project.current ] : $(target-name) : $(sources) : $(requirements) :
720        $(default-build) ] ;
721}
722
723
724rule boostbook-to-tests ( target : source : properties * )
725{
726    lock-config ;
727    local boost_root = [ modules.peek : BOOST_ROOT ] ;
728    local native-path = [ path.native [ path.join $(.boostbook-xsl-dir) testing
729        Jamfile ] ] ;
730    local stylesheet = $(native-path:S=.xsl) ;
731    xsltproc.xslt $(target) : $(source) $(stylesheet) : $(properties)
732        <xsl:param>boost.root=$(boost_root) ;
733}
734
735
736#############################################################################
737# Dependency scanners
738#############################################################################
739# XInclude scanner. Mostly stolen from c-scanner. :)
740# Note that this assumes an "xi" prefix for XIncludes. This is not always the
741# case for XML documents, but we assume it is true for anything we encounter.
742#
743class xinclude-scanner : scanner
744{
745    import scanner ;
746
747    rule __init__ ( includes * )
748    {
749        scanner.__init__ ;
750        self.includes = $(includes) ;
751    }
752
753    rule pattern ( )
754    {
755        return "xi:include[ ]*href=\"([^\"]*)\"" ;
756    }
757
758    rule process ( target : matches * : binding )
759    {
760        local target_path = [ NORMALIZE_PATH $(binding:D) ] ;
761
762        NOCARE $(matches) ;
763        INCLUDES $(target) : $(matches) ;
764        SEARCH on $(matches) = $(target_path) $(self.includes:G=) ;
765
766        scanner.propagate $(__name__) : $(matches) : $(target) ;
767    }
768}
769
770scanner.register xinclude-scanner : xsl:path ;
771type.set-scanner XML : xinclude-scanner ;
772