1# Copyright 2003, 2004 Douglas Gregor
2# Copyright 2003, 2004, 2005 Vladimir Prus
3# Copyright 2006 Rene Rivera
4# Distributed under the Boost Software License, Version 1.0.
5# (See accompanying file LICENSE_1_0.txt or copy at
6# http://www.boost.org/LICENSE_1_0.txt)
7
8# This module defines rules to handle generation of various outputs from source
9# files documented with doxygen comments. The supported transformations are:
10#
11# * Source -> Doxygen XML -> BoostBook XML
12# * Source -> Doxygen HTML
13#
14# The type of transformation is selected based on the target requested. For
15# BoostBook XML, the default, specifying a target with an ".xml" suffix, or an
16# empty suffix, will produce a <target>.xml and <target>.boostbook. For Doxygen
17# HTML specifying a target with an ".html" suffix will produce a directory
18# <target> with the Doxygen html files, and a <target>.html file redirecting to
19# that directory.
20
21import alias ;
22import boostbook ;
23import "class" : new ;
24import common ;
25import feature ;
26import make ;
27import modules ;
28import generators ;
29import os ;
30import param ;
31import path ;
32import print ;
33import project ;
34import property ;
35import stage ;
36import targets ;
37import toolset ;
38import type ;
39import utility ;
40import xsltproc ;
41import virtual-target ;
42
43
44# Use to specify extra configuration parameters. These get translated into a
45# doxyfile which configures the building of the docs.
46feature.feature "doxygen:param" : : free ;
47
48# Specify the "<xsl:param>boost.doxygen.header.prefix" XSLT option.
49feature.feature prefix : : free ;
50
51# Specify the "<xsl:param>boost.doxygen.reftitle" XSLT option.
52feature.feature reftitle : : free ;
53
54# Which processor to use for various translations from Doxygen.
55feature.feature doxygen.processor : xsltproc doxproc : propagated implicit ;
56
57# To generate, or not, index sections.
58feature.feature doxygen.doxproc.index : no yes : propagated incidental ;
59
60# The ID for the resulting BoostBook reference section.
61feature.feature doxygen.doxproc.id : : free ;
62
63# The title for the resulting BoostBook reference section.
64feature.feature doxygen.doxproc.title : : free ;
65
66# Location for images when generating XML
67feature.feature "doxygen:xml-imagedir" : : free ;
68
69# Indicates whether the entire directory should be deleted
70feature.feature doxygen.rmdir : off on : optional incidental ;
71
72# Doxygen configuration input file.
73type.register DOXYFILE : doxyfile ;
74
75# Doxygen XML multi-file output.
76type.register DOXYGEN_XML_MULTIFILE : xml-dir : XML ;
77
78# Doxygen XML coallesed output.
79type.register DOXYGEN_XML : doxygen : XML ;
80
81# Doxygen HTML multifile directory.
82type.register DOXYGEN_HTML_MULTIFILE : html-dir : HTML ;
83
84# Redirection HTML file to HTML multifile directory.
85type.register DOXYGEN_HTML : : HTML ;
86
87type.register DOXYGEN_XML_IMAGES : doxygen-xml-images ;
88
89
90# Initialize the Doxygen module. Parameters are:
91#   name: the name of the 'doxygen' executable. If not specified, the name
92#         'doxygen' will be used
93#
94rule init ( name ? )
95{
96    if ! $(.initialized)
97    {
98        .initialized = true ;
99
100        .doxproc = [ modules.binding $(__name__) ] ;
101        .doxproc = $(.doxproc:D)/doxproc.py ;
102
103        generators.register-composing doxygen.headers-to-doxyfile
104            : H HPP CPP MARKDOWN : DOXYFILE ;
105        generators.register-standard doxygen.run
106            : DOXYFILE : DOXYGEN_XML_MULTIFILE ;
107        generators.register-standard doxygen.xml-dir-to-boostbook
108            : DOXYGEN_XML_MULTIFILE : BOOSTBOOK : <doxygen.processor>doxproc ;
109        generators.register-xslt doxygen.xml-to-boostbook
110            : DOXYGEN_XML : BOOSTBOOK : <doxygen.processor>xsltproc ;
111        generators.register-xslt doxygen.collect
112            : DOXYGEN_XML_MULTIFILE : DOXYGEN_XML ;
113        generators.register-standard doxygen.run
114            : DOXYFILE : DOXYGEN_HTML_MULTIFILE ;
115        generators.register-standard doxygen.html-redirect
116            : DOXYGEN_HTML_MULTIFILE : DOXYGEN_HTML ;
117        generators.register-standard doxygen.copy-latex-pngs
118            : DOXYGEN_HTML : DOXYGEN_XML_IMAGES ;
119
120        IMPORT $(__name__) : doxygen : : doxygen ;
121    }
122
123    if $(name)
124    {
125        modify-config ;
126        .doxygen = $(name) ;
127        check-doxygen ;
128    }
129
130    if ! $(.doxygen)
131    {
132        check-doxygen ;
133    }
134}
135
136
137local rule freeze-config ( )
138{
139    if ! $(.initialized)
140    {
141        import errors ;
142        errors.user-error doxygen must be initialized before it can be used. ;
143    }
144    if ! $(.config-frozen)
145    {
146        .config-frozen = true ;
147        if [ .is-cygwin ]
148        {
149            .is-cygwin = true ;
150        }
151    }
152}
153
154
155local rule modify-config ( )
156{
157    if $(.config-frozen)
158    {
159        import errors ;
160        errors.user-error "Cannot change doxygen after it has been used." ;
161    }
162}
163
164
165local rule check-doxygen ( )
166{
167    if --debug-configuration in [ modules.peek : ARGV ]
168    {
169        ECHO "notice:" using doxygen ":" $(.doxygen) ;
170    }
171    local extra-paths ;
172    if [ os.name ] = NT
173    {
174        local ProgramFiles = [ modules.peek : ProgramFiles ] ;
175        if $(ProgramFiles)
176        {
177            extra-paths = "$(ProgramFiles:J= )" ;
178        }
179        else
180        {
181            extra-paths = "C:\\Program Files" ;
182        }
183    }
184    .doxygen = [ common.get-invocation-command doxygen : doxygen : $(.doxygen) :
185        $(extra-paths) ] ;
186}
187
188
189rule name ( )
190{
191    freeze-config ;
192    return $(.doxygen) ;
193}
194
195
196local rule .is-cygwin ( )
197{
198    if [ os.on-windows ]
199    {
200        local file = [ path.make [ modules.binding $(__name__) ] ] ;
201        local dir = [ path.native [ path.join [ path.parent $(file) ] doxygen ]
202            ] ;
203        local command = cd \"$(dir)\" "&&" \"$(.doxygen)\"
204            windows-paths-check.doxyfile 2>&1 ;
205        command = $(command:J=" ") ;
206        result = [ SHELL $(command) ] ;
207        if [ MATCH "(Parsing file /)" : $(result) ]
208        {
209            return true ;
210        }
211    }
212}
213
214
215# Runs Doxygen on the given Doxygen configuration file (the source) to generate
216# the Doxygen files. The output is dumped according to the settings in the
217# Doxygen configuration file, not according to the target! Because of this, we
218# essentially "touch" the target file, in effect making it look like we have
219# really written something useful to it. Anyone that uses this action must deal
220# with this behavior.
221#
222actions doxygen-action
223{
224    $(RM) "$(*.XML)" & "$(NAME:E=doxygen)" "$(>)" && echo "Stamped" > "$(<)"
225}
226
227
228# Runs the Python doxproc XML processor.
229#
230actions doxproc
231{
232    python "$(DOXPROC)" "--xmldir=$(>)" "--output=$(<)" "$(OPTIONS)" "--id=$(ID)" "--title=$(TITLE)"
233}
234
235
236rule translate-path ( path )
237{
238    freeze-config ;
239    if [ os.on-windows ]
240    {
241        if [ os.name ] = CYGWIN
242        {
243            if $(.is-cygwin)
244            {
245                return $(path) ;
246            }
247            else
248            {
249                return $(path:W) ;
250            }
251        }
252        else
253        {
254            if $(.is-cygwin)
255            {
256                match = [ MATCH "^(.):(.*)" : $(path) ] ;
257                if $(match)
258                {
259                    return /cygdrive/$(match[1])$(match[2]:T) ;
260                }
261                else
262                {
263                    return $(path:T) ;
264                }
265            }
266            else
267            {
268                return $(path) ;
269            }
270        }
271    }
272    else
273    {
274        return $(path) ;
275    }
276}
277
278toolset.uses-features doxygen.headers-to-doxyfile : "<doxygen:param>" ;
279
280# Generates a doxygen configuration file (doxyfile) given a set of C++ sources
281# and a property list that may contain <doxygen:param> features.
282#
283rule headers-to-doxyfile ( target : sources * : properties * )
284{
285    local text = "# Generated by B2 version 2" ;
286
287    local output-dir ;
288
289    # Translate <doxygen:param> into command line flags.
290    for local param in [ feature.get-values <doxygen:param> : $(properties) ]
291    {
292        local namevalue = [ MATCH "([^=]*)=(.*)" : $(param) ] ;
293        if $(namevalue[1]) = OUTPUT_DIRECTORY
294        {
295            output-dir = [ translate-path [ utility.unquote $(namevalue[2]) ] ]
296                ;
297            text += "OUTPUT_DIRECTORY = \"$(output-dir)\"" ;
298        }
299        else
300        {
301            text += "$(namevalue[1]) = $(namevalue[2])" ;
302        }
303    }
304
305    if ! $(output-dir)
306    {
307        output-dir = [ translate-path [ on $(target) return $(LOCATE) ] ] ;
308        text += "OUTPUT_DIRECTORY = \"$(output-dir)\"" ;
309    }
310
311    local headers ;
312    for local header in $(sources:G=)
313    {
314        header = [ translate-path $(header) ] ;
315        headers += \"$(header)\" ;
316    }
317
318    # Doxygen generates LaTex by default. So disable it unconditionally, or at
319    # least until someone needs, and hence writes support for, LaTex output.
320    text += "GENERATE_LATEX = NO" ;
321    text += "INPUT = $(headers:J= )" ;
322    print.output $(target) plain ;
323    print.text $(text) : true ;
324}
325
326toolset.uses-features doxygen.run : <doxygen.rmdir> "<doxygen:param>" ;
327
328# Run Doxygen. See doxygen-action for a description of the strange properties of
329# this rule.
330#
331rule run ( target : source : properties * )
332{
333    freeze-config ;
334    if <doxygen.rmdir>on in $(properties)
335    {
336        local output-dir = [ path.make [ MATCH
337            "<doxygen:param>OUTPUT_DIRECTORY=\"?([^\"]*)" : $(properties) ] ] ;
338        local html-dir = [ path.make [ MATCH <doxygen:param>HTML_OUTPUT=(.*) :
339            $(properties) ] ] ;
340        if $(output-dir) && $(html-dir) &&
341            [ path.glob $(output-dir) : $(html-dir) ]
342        {
343            HTMLDIR on $(target) = [ path.native [ path.join $(output-dir)
344                $(html-dir) ] ] ;
345            rm-htmldir $(target) ;
346        }
347    }
348    doxygen-action $(target) : $(source) ;
349    NAME on $(target) = $(.doxygen) ;
350    RM on $(target) = [ modules.peek common : RM ] ;
351    *.XML on $(target) = [ path.native [ path.join [ path.make [ on $(target)
352        return $(LOCATE) ] ] $(target:B:S=) *.xml ] ] ;
353}
354
355
356if [ os.name ] = NT
357{
358    RMDIR = rmdir /s /q ;
359}
360else
361{
362    RMDIR = rm -rf ;
363}
364
365actions quietly rm-htmldir
366{
367    $(RMDIR) $(HTMLDIR)
368}
369
370
371# The rules below require BoostBook stylesheets, so we need some code to check
372# that the boostbook module has actually been initialized.
373#
374rule check-boostbook ( )
375{
376    if ! [ modules.peek boostbook : .initialized ]
377    {
378        import errors ;
379        errors.user-error
380            : The boostbook module is not initialized you have attempted to use
381            : the 'doxygen' toolset, which requires BoostBook, but never
382            : initialized BoostBook.
383            : "Hint:" add 'using boostbook \;' to your user-config.jam. ;
384    }
385}
386
387
388# Collect the set of Doxygen XML files into a single XML source file that can be
389# handled by an XSLT processor. The source is completely ignored (see
390# doxygen-action), because this action picks up the Doxygen XML index file xml/
391# index.xml. This is because we can not teach Doxygen to act like a NORMAL
392# program and take a "-o output.xml" argument (grrrr). The target of the
393# collection will be a single Doxygen XML file.
394#
395rule collect ( target : source : properties * )
396{
397    check-boostbook ;
398    local collect-xsl-dir
399        = [ path.native [ path.join [ boostbook.xsl-dir ] doxygen collect ] ] ;
400    local source-path
401        = [ path.make [ on $(source) return $(LOCATE) ] ] ;
402    local collect-path
403        = [ path.root [ path.join $(source-path) $(source:B) ] [ path.pwd ] ] ;
404    local native-path
405        = [ path.native $(collect-path) ] ;
406    local real-source
407        = [ path.native [ path.join $(collect-path) index.xml ] ] ;
408    xsltproc.xslt $(target) : $(real-source) $(collect-xsl-dir:S=.xsl)
409        : <xsl:param>doxygen.xml.path=$(native-path) ;
410}
411
412toolset.uses-features doxygen.xml-to-boostbook : <prefix> <reftitle> ;
413
414# Translate Doxygen XML into BoostBook.
415#
416rule xml-to-boostbook ( target : source : properties * )
417{
418    check-boostbook ;
419    local xsl-dir = [ boostbook.xsl-dir ] ;
420    local d2b-xsl = [ path.native [ path.join [ boostbook.xsl-dir ] doxygen
421        doxygen2boostbook.xsl ] ] ;
422
423    local xslt-properties = $(properties) ;
424    for local prefix in [ feature.get-values <prefix> : $(properties) ]
425    {
426        xslt-properties += "<xsl:param>boost.doxygen.header.prefix=$(prefix)" ;
427    }
428    for local title in [ feature.get-values <reftitle> : $(properties) ]
429    {
430        xslt-properties += "<xsl:param>boost.doxygen.reftitle=$(title)" ;
431    }
432
433    xsltproc.xslt $(target) : $(source) $(d2b-xsl) : $(xslt-properties) ;
434}
435
436
437toolset.flags doxygen.xml-dir-to-boostbook OPTIONS <doxygen.doxproc.index>yes :
438    --enable-index ;
439toolset.flags doxygen.xml-dir-to-boostbook ID <doxygen.doxproc.id> ;
440toolset.flags doxygen.xml-dir-to-boostbook TITLE <doxygen.doxproc.title> ;
441
442
443rule xml-dir-to-boostbook ( target : source : properties * )
444{
445    DOXPROC on $(target) = $(.doxproc) ;
446    LOCATE on $(source:S=) = [ on $(source) return $(LOCATE) ] ;
447    doxygen.doxproc $(target) : $(source:S=) ;
448}
449
450
451# Generate the HTML redirect to HTML dir index.html file.
452#
453rule html-redirect ( target : source : properties * )
454{
455    local uri = "$(target:B)/index.html" ;
456    print.output $(target) plain ;
457    print.text
458"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"
459    \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">
460<html xmlns=\"http://www.w3.org/1999/xhtml\">
461<head>
462  <meta http-equiv=\"refresh\" content=\"0; URL=$(uri)\" />
463
464  <title></title>
465</head>
466
467<body>
468  Automatic redirection failed, please go to <a href=
469  \"$(uri)\">$(uri)</a>.
470</body>
471</html>
472"
473        : true ;
474}
475
476rule copy-latex-pngs ( target : source : requirements * )
477{
478    local directory = [ path.native [ feature.get-values <doxygen:xml-imagedir>
479        : $(requirements) ] ] ;
480    local location = [ on $(target) return $(LOCATE) ] ;
481
482    local pdf-location = [ path.native [ path.join [ path.make $(location) ]
483        [ path.make $(directory) ] ] ] ;
484    local html-location = [ path.native [ path.join . html [ path.make
485        $(directory) ] ] ] ;
486
487    common.MkDir $(pdf-location) ;
488    common.MkDir $(html-location) ;
489
490    DEPENDS $(target) : $(pdf-location) $(html-location) ;
491
492    if [ os.name ] = NT
493    {
494        CP on $(target) = copy /y ;
495        FROM on $(target) = \\*.png ;
496        TOHTML on $(target) = .\\html\\$(directory) ;
497        TOPDF on $(target) = \\$(directory) ;
498    }
499    else
500    {
501        CP on $(target) = cp ;
502        FROM on $(target) = /*.png ;
503        TOHTML on $(target) = ./html/$(directory) ;
504        TOPDF on $(target) = $(target:D)/$(directory) ;
505    }
506}
507
508actions copy-latex-pngs
509{
510    $(CP) $(>:S=)$(FROM) $(TOHTML)
511    $(CP) $(>:S=)$(FROM) $(<:D)$(TOPDF)
512    echo "Stamped" > "$(<)"
513}
514
515
516# Building latex images for doxygen XML depends on latex, dvips, and gs being in
517# your PATH. This is true for most Unix installs, but not on Win32, where you
518# will need to install MkTex and Ghostscript and add these tools to your path.
519
520actions check-latex
521{
522    latex -version >$(<)
523}
524
525actions check-dvips
526{
527    dvips -version >$(<)
528}
529
530if [ os.name ] = "NT"
531{
532    actions check-gs
533    {
534        gswin32c -version >$(<)
535    }
536}
537else
538{
539    actions check-gs
540    {
541        gs -version >$(<)
542    }
543}
544
545
546local rule check-tools-targets ( project )
547{
548    if ! $(.check-tools-targets)
549    {
550        # Find the root project.
551        #
552        # This is a best effort attempt to avoid using different locations for
553        # storing *.check files depending on which project imported the doxygen
554        # toolset first. The files are stored in a location related to the
555        # project's root project. Note that this location may change depending
556        # on the folder the build was run from in case the build uses multiple
557        # related projects with their own Jamroot separate modules.
558        local project-module = [ $(project).project-module ] ;
559        local root-module = [ project.get-jamroot-module $(project-module) ] ;
560        if ! $(root-module)
561        {
562            import errors ;
563            if [ project.is-config-module $(project-module) ]
564            {
565                errors.user-error doxygen targets can not be declared in Boost
566                    Build's configuration modules. ;
567            }
568            else
569            {
570                errors.user-error doxygen targets can not be declared in
571                    standalone projects. : use a Jamfile/Jamroot project
572                    instead. ;
573            }
574        }
575        local root-project = [ project.target $(root-module) ] ;
576
577        local targets =
578            [ new file-target latex.check : : $(root-project) : [ new action :
579                doxygen.check-latex ] ]
580            [ new file-target dvips.check : : $(root-project) : [ new action :
581                doxygen.check-dvips ] ]
582            [ new file-target gs.check : : $(root-project) : [ new action :
583                doxygen.check-gs ] ] ;
584
585        for local target in $(targets)
586        {
587            .check-tools-targets += [ virtual-target.register $(target) ] ;
588        }
589    }
590    return $(.check-tools-targets) ;
591}
592
593
594project.initialize $(__name__) ;
595project doxygen ;
596
597class doxygen-check-tools-target-class : basic-target
598{
599    rule construct ( name : sources * : property-set )
600    {
601        IMPORT doxygen : check-tools-targets : $(__name__) :
602            doxygen.check-tools-targets ;
603        return [ property-set.empty ] [ doxygen.check-tools-targets [ project ]
604            ] ;
605    }
606}
607
608
609# Declares a metatarget for collecting version information on different external
610# tools used in this module.
611#
612rule check-tools ( target )
613{
614    freeze-config ;
615    targets.create-metatarget doxygen-check-tools-target-class :
616        [ project.current ] : $(target) ;
617}
618
619
620# User-level rule to generate HTML files or BoostBook XML from a set of headers
621# via Doxygen.
622#
623rule doxygen ( target : sources + : requirements * : default-build * :
624    usage-requirements * )
625{
626    param.handle-named-params
627        sources requirements default-build usage-requirements ;
628    requirements += <format>none ;
629    freeze-config ;
630    local project = [ project.current ] ;
631
632    if $(target:S) = .html
633    {
634        # Build an HTML directory from the sources.
635        local html-location = [ feature.get-values <location> : $(requirements)
636            ] ;
637        local output-dir ;
638        if [ $(project).get build-dir ]
639        {
640            # Explicitly specified build dir. Add html at the end.
641            output-dir = [ path.join [ $(project).build-dir ]
642                $(html-location:E=html) ] ;
643        }
644        else
645        {
646            # Trim 'bin' from implicit build dir, for no other reason than
647            # backward compatibility.
648            output-dir = [ path.join [ path.parent [ $(project).build-dir ] ]
649                $(html-location:E=html) ] ;
650        }
651        output-dir = [ path.root $(output-dir) [ path.pwd ] ] ;
652        local output-dir-native = [ path.native $(output-dir) ] ;
653        requirements = [ property.change $(requirements) : <location> ] ;
654
655        # The doxygen configuration file.
656        targets.create-typed-target DOXYFILE : $(project) : $(target:S=.tag)
657            : $(sources)
658            : $(requirements)
659                <doxygen:param>GENERATE_HTML=YES
660                <doxygen:param>GENERATE_XML=NO
661                <doxygen:param>"OUTPUT_DIRECTORY=\"$(output-dir-native)\""
662                <doxygen:param>HTML_OUTPUT=$(target:B)
663            : $(default-build) ;
664        $(project).mark-target-as-explicit $(target:S=.tag) ;
665
666        # The html directory to generate by running doxygen.
667        targets.create-typed-target DOXYGEN_HTML_MULTIFILE : $(project)
668            : $(target:S=.dir)  # Name.
669            : $(target:S=.tag)  # Sources.
670            : $(requirements)
671                <doxygen:param>"OUTPUT_DIRECTORY=\"$(output-dir-native)\""
672                <doxygen:param>HTML_OUTPUT=$(target:B)
673            : $(default-build) ;
674        $(project).mark-target-as-explicit $(target:S=.dir) ;
675
676        # The redirect html file into the generated html.
677        targets.create-typed-target DOXYGEN_HTML : $(project) : $(target)
678            : $(target:S=.dir)  # Sources.
679            : $(requirements) <location>$(output-dir)
680            : $(default-build) ;
681    }
682    else
683    {
684        # Build a BoostBook XML file from the sources.
685        local location-xml = [ feature.get-values <location> : $(requirements) ]
686            ;
687        requirements = [ property.change $(requirements) : <location> ] ;
688        local target-xml = $(target:B=$(target:B)-xml) ;
689
690        # Check whether we need to build images.
691        local images-location = [ feature.get-values <doxygen:xml-imagedir> :
692            $(requirements) ] ;
693        if $(images-location)
694        {
695            # Prepare a metatarget for collecting used external tool version
696            # information. We use only one such metatarget as they always
697            # produce the same files and we do not want to deal with multiple
698            # metatargets having matching names, causing 'ambiguous variants'
699            # errors.
700            if ! $(.check-tools)
701            {
702                # FIXME: Since we have the check-tools target object reference,
703                # see how we can use that instead of having to construct a valid
704                # target reference string for use in <dependency> property
705                # values.
706                local project-id = --doxygen.check-tools-project-- ;
707                local target-id = --doxygen.check-tools-- ;
708                local pm = [ $(project).project-module ] ;
709                project.register-id $(project-id) : $(pm) ;
710                check-tools $(target-id) ;
711                .check-tools = /$(project-id)//$(target-id) ;
712            }
713
714            doxygen $(target).doxygen-xml-images.html : $(sources) :
715                $(requirements)
716                <doxygen.rmdir>on
717                <doxygen:param>QUIET=YES
718                <doxygen:param>WARNINGS=NO
719                <doxygen:param>WARN_IF_UNDOCUMENTED=NO
720                <dependency>$(.check-tools) ;
721            $(project).mark-target-as-explicit $(target).doxygen-xml-images.html
722                ;
723
724            targets.create-typed-target DOXYGEN_XML_IMAGES : $(project)
725                : $(target).doxygen-xml-images  # Name.
726                : $(target).doxygen-xml-images.html  # Sources.
727                : $(requirements)
728                : $(default-build) ;
729            $(project).mark-target-as-explicit $(target).doxygen-xml-images ;
730
731            if ! [ MATCH (/)$ : $(images-location) ]
732            {
733                images-location = $(images-location)/ ;
734            }
735
736            requirements +=
737                <dependency>$(target).doxygen-xml-images
738                <xsl:param>boost.doxygen.formuladir=$(images-location) ;
739        }
740
741        # The doxygen configuration file.
742        targets.create-typed-target DOXYFILE : $(project) : $(target-xml:S=.tag)
743            : $(sources)
744            : $(requirements)
745                <doxygen:param>GENERATE_HTML=NO
746                <doxygen:param>GENERATE_XML=YES
747                <doxygen:param>XML_OUTPUT=$(target-xml)
748            : $(default-build) ;
749        $(project).mark-target-as-explicit $(target-xml:S=.tag) ;
750
751        # The Doxygen XML directory for the processed source files.
752        targets.create-typed-target DOXYGEN_XML_MULTIFILE : $(project)
753            : $(target-xml:S=.dir)  # Name.
754            : $(target-xml:S=.tag)  # Sources.
755            : $(requirements)
756            : $(default-build) ;
757        $(project).mark-target-as-explicit $(target-xml:S=.dir) ;
758
759        # The resulting BoostBook file is generated by the processor tool. The
760        # tool can be either the xsltproc plus accompanying XSL scripts. Or it
761        # can be the python doxproc.py script.
762        targets.create-typed-target BOOSTBOOK : $(project) : $(target-xml)
763            : $(target-xml:S=.dir)  # Sources.
764            : $(requirements)
765            : $(default-build) ;
766        $(project).mark-target-as-explicit $(target-xml) ;
767
768        stage $(target:S=.xml)  # Name.
769            : $(target-xml)  # Sources.
770            : $(requirements)
771                <location>$(location-xml:E=.)
772                <name>$(target:S=.xml)
773            : $(default-build) ;
774        $(project).mark-target-as-explicit $(target:S=.xml) ;
775
776        # TODO: See why this alias target is used here instead of simply naming
777        # the previous stage target $(target) and having it specify the alias
778        # target's usage requirements directly.
779        alias $(target) : : $(requirements) : $(default-build) :
780            $(usage-requirements) <dependency>$(target:S=.xml) ;
781    }
782}
783