# Copyright 2003, 2004 Douglas Gregor # Copyright 2003, 2004, 2005 Vladimir Prus # Copyright 2006 Rene Rivera # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) # This module defines rules to handle generation of various outputs from source # files documented with doxygen comments. The supported transformations are: # # * Source -> Doxygen XML -> BoostBook XML # * Source -> Doxygen HTML # # The type of transformation is selected based on the target requested. For # BoostBook XML, the default, specifying a target with an ".xml" suffix, or an # empty suffix, will produce a .xml and .boostbook. For Doxygen # HTML specifying a target with an ".html" suffix will produce a directory # with the Doxygen html files, and a .html file redirecting to # that directory. import alias ; import boostbook ; import "class" : new ; import common ; import feature ; import make ; import modules ; import generators ; import os ; import path ; import print ; import project ; import property ; import stage ; import targets ; import toolset ; import type ; import utility ; import xsltproc ; import virtual-target ; # Use to specify extra configuration paramters. These get translated into a # doxyfile which configures the building of the docs. feature.feature doxygen:param : : free ; # Specify the "boost.doxygen.header.prefix" XSLT option. feature.feature prefix : : free ; # Specify the "boost.doxygen.reftitle" XSLT option. feature.feature reftitle : : free ; # Which processor to use for various translations from Doxygen. feature.feature doxygen.processor : xsltproc doxproc : propagated implicit ; # To generate, or not, index sections. feature.feature doxygen.doxproc.index : no yes : propagated incidental ; # The ID for the resulting BoostBook reference section. feature.feature doxygen.doxproc.id : : free ; # The title for the resulting BoostBook reference section. feature.feature doxygen.doxproc.title : : free ; # Location for images when generating XML feature.feature doxygen:xml-imagedir : : free ; # Indicates whether the entire directory should be deleted feature.feature doxygen.rmdir : off on : optional incidental ; # Doxygen configuration input file. type.register DOXYFILE : doxyfile ; # Doxygen XML multi-file output. type.register DOXYGEN_XML_MULTIFILE : xml-dir : XML ; # Doxygen XML coallesed output. type.register DOXYGEN_XML : doxygen : XML ; # Doxygen HTML multifile directory. type.register DOXYGEN_HTML_MULTIFILE : html-dir : HTML ; # Redirection HTML file to HTML multifile directory. type.register DOXYGEN_HTML : : HTML ; type.register DOXYGEN_XML_IMAGES : doxygen-xml-images ; # Initialize the Doxygen module. Parameters are: # name: the name of the 'doxygen' executable. If not specified, the name # 'doxygen' will be used # rule init ( name ? ) { if ! $(.initialized) { .initialized = true ; .doxproc = [ modules.binding $(__name__) ] ; .doxproc = $(.doxproc:D)/doxproc.py ; generators.register-composing doxygen.headers-to-doxyfile : H HPP CPP : DOXYFILE ; generators.register-standard doxygen.run : DOXYFILE : DOXYGEN_XML_MULTIFILE ; generators.register-standard doxygen.xml-dir-to-boostbook : DOXYGEN_XML_MULTIFILE : BOOSTBOOK : doxproc ; generators.register-standard doxygen.xml-to-boostbook : DOXYGEN_XML : BOOSTBOOK : xsltproc ; generators.register-standard doxygen.collect : DOXYGEN_XML_MULTIFILE : DOXYGEN_XML ; generators.register-standard doxygen.run : DOXYFILE : DOXYGEN_HTML_MULTIFILE ; generators.register-standard doxygen.html-redirect : DOXYGEN_HTML_MULTIFILE : DOXYGEN_HTML ; generators.register-standard doxygen.copy-latex-pngs : DOXYGEN_HTML : DOXYGEN_XML_IMAGES ; IMPORT $(__name__) : doxygen : : doxygen ; } if $(name) { modify-config ; .doxygen = $(name) ; check-doxygen ; } if ! $(.doxygen) { check-doxygen ; } } local rule freeze-config ( ) { if ! $(.initialized) { import errors ; errors.user-error doxygen must be initialized before it can be used. ; } if ! $(.config-frozen) { .config-frozen = true ; if [ .is-cygwin ] { .is-cygwin = true ; } } } local rule modify-config ( ) { if $(.config-frozen) { import errors ; errors.user-error "Cannot change doxygen after it has been used." ; } } local rule check-doxygen ( ) { if --debug-configuration in [ modules.peek : ARGV ] { ECHO "notice:" using doxygen ":" $(.doxygen) ; } local extra-paths ; if [ os.name ] = NT { local ProgramFiles = [ modules.peek : ProgramFiles ] ; if $(ProgramFiles) { extra-paths = "$(ProgramFiles:J= )" ; } else { extra-paths = "C:\\Program Files" ; } } .doxygen = [ common.get-invocation-command doxygen : doxygen : $(.doxygen) : $(extra-paths) ] ; } rule name ( ) { freeze-config ; return $(.doxygen) ; } local rule .is-cygwin ( ) { if [ os.on-windows ] { local file = [ path.make [ modules.binding $(__name__) ] ] ; local dir = [ path.native [ path.join [ path.parent $(file) ] doxygen ] ] ; local command = cd \"$(dir)\" "&&" \"$(.doxygen)\" windows-paths-check.doxyfile 2>&1 ; command = $(command:J=" ") ; result = [ SHELL $(command) ] ; if [ MATCH "(Parsing file /)" : $(result) ] { return true ; } } } # Runs Doxygen on the given Doxygen configuration file (the source) to generate # the Doxygen files. The output is dumped according to the settings in the # Doxygen configuration file, not according to the target! Because of this, we # essentially "touch" the target file, in effect making it look like we have # really written something useful to it. Anyone that uses this action must deal # with this behavior. # actions doxygen-action { $(RM) "$(*.XML)" & "$(NAME:E=doxygen)" "$(>)" && echo "Stamped" > "$(<)" } # Runs the Python doxproc XML processor. # actions doxproc { python "$(DOXPROC)" "--xmldir=$(>)" "--output=$(<)" "$(OPTIONS)" "--id=$(ID)" "--title=$(TITLE)" } rule translate-path ( path ) { freeze-config ; if [ os.on-windows ] { if [ os.name ] = CYGWIN { if $(.is-cygwin) { return $(path) ; } else { return $(path:W) ; } } else { if $(.is-cygwin) { match = [ MATCH ^(.):(.*) : $(path) ] ; if $(match) { return /cygdrive/$(match[1])$(match[2]:T) ; } else { return $(path:T) ; } } else { return $(path) ; } } } else { return $(path) ; } } # Generates a doxygen configuration file (doxyfile) given a set of C++ sources # and a property list that may contain features. # rule headers-to-doxyfile ( target : sources * : properties * ) { local text = "# Generated by Boost.Build version 2" ; local output-dir ; # Translate into command line flags. for local param in [ feature.get-values : $(properties) ] { local namevalue = [ MATCH ([^=]*)=(.*) : $(param) ] ; if $(namevalue[1]) = OUTPUT_DIRECTORY { output-dir = [ translate-path [ utility.unquote $(namevalue[2]) ] ] ; text += "OUTPUT_DIRECTORY = \"$(output-dir)\"" ; } else { text += "$(namevalue[1]) = $(namevalue[2])" ; } } if ! $(output-dir) { output-dir = [ translate-path [ on $(target) return $(LOCATE) ] ] ; text += "OUTPUT_DIRECTORY = \"$(output-dir)\"" ; } local headers ; for local header in $(sources:G=) { header = [ translate-path $(header) ] ; headers += \"$(header)\" ; } # Doxygen generates LaTex by default. So disable it unconditionally, or at # least until someone needs, and hence writes support for, LaTex output. text += "GENERATE_LATEX = NO" ; text += "INPUT = $(headers:J= )" ; print.output $(target) plain ; print.text $(text) : true ; } # Run Doxygen. See doxygen-action for a description of the strange properties of # this rule. # rule run ( target : source : properties * ) { freeze-config ; if on in $(properties) { local output-dir = [ path.make [ MATCH OUTPUT_DIRECTORY=\"?([^\"]*) : $(properties) ] ] ; local html-dir = [ path.make [ MATCH HTML_OUTPUT=(.*) : $(properties) ] ] ; if $(output-dir) && $(html-dir) && [ path.glob $(output-dir) : $(html-dir) ] { HTMLDIR on $(target) = [ path.native [ path.join $(output-dir) $(html-dir) ] ] ; rm-htmldir $(target) ; } } doxygen-action $(target) : $(source) ; NAME on $(target) = $(.doxygen) ; RM on $(target) = [ modules.peek common : RM ] ; *.XML on $(target) = [ path.native [ path.join [ path.make [ on $(target) return $(LOCATE) ] ] $(target:B:S=) *.xml ] ] ; } if [ os.name ] = NT { RMDIR = rmdir /s /q ; } else { RMDIR = rm -rf ; } actions quietly rm-htmldir { $(RMDIR) $(HTMLDIR) } # The rules below require BoostBook stylesheets, so we need some code to check # that the boostbook module has actualy been initialized. # rule check-boostbook ( ) { if ! [ modules.peek boostbook : .initialized ] { import errors ; errors.user-error : The boostbook module is not initialized you have attempted to use : the 'doxygen' toolset, which requires BoostBook, but never : initialized BoostBook. : Hint: add 'using boostbook ;' to your user-config.jam. ; } } # Collect the set of Doxygen XML files into a single XML source file that can be # handled by an XSLT processor. The source is completely ignored (see # doxygen-action), because this action picks up the Doxygen XML index file xml/ # index.xml. This is because we can not teach Doxygen to act like a NORMAL # program and take a "-o output.xml" argument (grrrr). The target of the # collection will be a single Doxygen XML file. # rule collect ( target : source : properties * ) { check-boostbook ; local collect-xsl-dir = [ path.native [ path.join [ boostbook.xsl-dir ] doxygen collect ] ] ; local source-path = [ path.make [ on $(source) return $(LOCATE) ] ] ; local collect-path = [ path.root [ path.join $(source-path) $(source:B) ] [ path.pwd ] ] ; local native-path = [ path.native $(collect-path) ] ; local real-source = [ path.native [ path.join $(collect-path) index.xml ] ] ; xsltproc.xslt $(target) : $(real-source) $(collect-xsl-dir:S=.xsl) : doxygen.xml.path=$(native-path) ; } # Translate Doxygen XML into BoostBook. # rule xml-to-boostbook ( target : source : properties * ) { check-boostbook ; local xsl-dir = [ boostbook.xsl-dir ] ; local d2b-xsl = [ path.native [ path.join [ boostbook.xsl-dir ] doxygen doxygen2boostbook.xsl ] ] ; local xslt-properties = $(properties) ; for local prefix in [ feature.get-values : $(properties) ] { xslt-properties += "boost.doxygen.header.prefix=$(prefix)" ; } for local title in [ feature.get-values : $(properties) ] { xslt-properties += "boost.doxygen.reftitle=$(title)" ; } xsltproc.xslt $(target) : $(source) $(d2b-xsl) : $(xslt-properties) ; } toolset.flags doxygen.xml-dir-to-boostbook OPTIONS yes : --enable-index ; toolset.flags doxygen.xml-dir-to-boostbook ID ; toolset.flags doxygen.xml-dir-to-boostbook TITLE ; rule xml-dir-to-boostbook ( target : source : properties * ) { DOXPROC on $(target) = $(.doxproc) ; LOCATE on $(source:S=) = [ on $(source) return $(LOCATE) ] ; doxygen.doxproc $(target) : $(source:S=) ; } # Generate the HTML redirect to HTML dir index.html file. # rule html-redirect ( target : source : properties * ) { local uri = "$(target:B)/index.html" ; print.output $(target) plain ; print.text " Automatic redirection failed, please go to $(uri). " : true ; } rule copy-latex-pngs ( target : source : requirements * ) { local directory = [ path.native [ feature.get-values : $(requirements) ] ] ; local location = [ on $(target) return $(LOCATE) ] ; local pdf-location = [ path.native [ path.join [ path.make $(location) ] [ path.make $(directory) ] ] ] ; local html-location = [ path.native [ path.join . html [ path.make $(directory) ] ] ] ; common.MkDir $(pdf-location) ; common.MkDir $(html-location) ; DEPENDS $(target) : $(pdf-location) $(html-location) ; if [ os.name ] = NT { CP on $(target) = copy /y ; FROM on $(target) = \\*.png ; TOHTML on $(target) = .\\html\\$(directory) ; TOPDF on $(target) = \\$(directory) ; } else { CP on $(target) = cp ; FROM on $(target) = /*.png ; TOHTML on $(target) = ./html/$(directory) ; TOPDF on $(target) = $(target:D)/$(directory) ; } } actions copy-latex-pngs { $(CP) $(>:S=)$(FROM) $(TOHTML) $(CP) $(>:S=)$(FROM) $(<:D)$(TOPDF) echo "Stamped" > "$(<)" } # Building latex images for doxygen XML depends on latex, dvips, and gs being in # your PATH. This is true for most Unix installs, but not on Win32, where you # will need to install MkTex and Ghostscript and add these tools to your path. actions check-latex { latex -version >$(<) } actions check-dvips { dvips -version >$(<) } if [ os.name ] = "NT" { actions check-gs { gswin32c -version >$(<) } } else { actions check-gs { gs -version >$(<) } } local rule check-tools-targets ( project ) { if ! $(.check-tools-targets) { # Find the root project. # # This is a best effort attempt to avoid using different locations for # storing *.check files depending on which project imported the doxygen # toolset first. The files are stored in a location related to the # project's root project. Note that this location may change depending # on the folder the build was run from in case the build uses multiple # related projects with their own Jamroot separate modules. local project-module = [ $(project).project-module ] ; local root-module = [ project.get-jamroot-module $(project-module) ] ; if ! $(root-module) { import errors ; if [ project.is-config-module $(project-module) ] { errors.user-error doxygen targets can not be declared in Boost Build's configuration modules. ; } else { errors.user-error doxygen targets can not be declared in standalone projects. : use a Jamfile/Jamroot project instead. ; } } local root-project = [ project.target $(root-module) ] ; local targets = [ new file-target latex.check : : $(root-project) : [ new action : doxygen.check-latex ] ] [ new file-target dvips.check : : $(root-project) : [ new action : doxygen.check-dvips ] ] [ new file-target gs.check : : $(root-project) : [ new action : doxygen.check-gs ] ] ; for local target in $(targets) { .check-tools-targets += [ virtual-target.register $(target) ] ; } } return $(.check-tools-targets) ; } project.initialize $(__name__) ; project doxygen ; class doxygen-check-tools-target-class : basic-target { rule construct ( name : sources * : property-set ) { IMPORT doxygen : check-tools-targets : $(__name__) : doxygen.check-tools-targets ; return [ property-set.empty ] [ doxygen.check-tools-targets [ project ] ] ; } } # Declares a metatarget for collecting version information on different external # tools used in this module. # rule check-tools ( target ) { freeze-config ; targets.create-metatarget doxygen-check-tools-target-class : [ project.current ] : $(target) ; } # User-level rule to generate HTML files or BoostBook XML from a set of headers # via Doxygen. # rule doxygen ( target : sources + : requirements * : default-build * : usage-requirements * ) { requirements += none ; freeze-config ; local project = [ project.current ] ; if $(target:S) = .html { # Build an HTML directory from the sources. local html-location = [ feature.get-values : $(requirements) ] ; local output-dir ; if [ $(project).get build-dir ] { # Explicitly specified build dir. Add html at the end. output-dir = [ path.join [ $(project).build-dir ] $(html-location:E=html) ] ; } else { # Trim 'bin' from implicit build dir, for no other reason than # backward compatibility. output-dir = [ path.join [ path.parent [ $(project).build-dir ] ] $(html-location:E=html) ] ; } output-dir = [ path.root $(output-dir) [ path.pwd ] ] ; local output-dir-native = [ path.native $(output-dir) ] ; requirements = [ property.change $(requirements) : ] ; # The doxygen configuration file. targets.create-typed-target DOXYFILE : $(project) : $(target:S=.tag) : $(sources) : $(requirements) GENERATE_HTML=YES GENERATE_XML=NO "OUTPUT_DIRECTORY=\"$(output-dir-native)\"" HTML_OUTPUT=$(target:B) : $(default-build) ; $(project).mark-target-as-explicit $(target:S=.tag) ; # The html directory to generate by running doxygen. targets.create-typed-target DOXYGEN_HTML_MULTIFILE : $(project) : $(target:S=.dir) # Name. : $(target:S=.tag) # Sources. : $(requirements) "OUTPUT_DIRECTORY=\"$(output-dir-native)\"" HTML_OUTPUT=$(target:B) : $(default-build) ; $(project).mark-target-as-explicit $(target:S=.dir) ; # The redirect html file into the generated html. targets.create-typed-target DOXYGEN_HTML : $(project) : $(target) : $(target:S=.dir) # Sources. : $(requirements) $(output-dir) : $(default-build) ; } else { # Build a BoostBook XML file from the sources. local location-xml = [ feature.get-values : $(requirements) ] ; requirements = [ property.change $(requirements) : ] ; local target-xml = $(target:B=$(target:B)-xml) ; # Check whether we need to build images. local images-location = [ feature.get-values : $(requirements) ] ; if $(images-location) { # Prepare a metatarget for collecting used external tool version # information. We use only one such metatarget as they always # produce the same files and we do not want to deal with multiple # metatargets having matching names, causing 'ambiguous variants' # errors. if ! $(.check-tools) { # FIXME: Since we have the check-tools target object reference, # see how we can use that instead of having to construct a valid # target reference string for use in property # values. local project-id = --doxygen.check-tools-project-- ; local target-id = --doxygen.check-tools-- ; local pm = [ $(project).project-module ] ; project.register-id $(project-id) : $(pm) ; check-tools $(target-id) ; .check-tools = /$(project-id)//$(target-id) ; } doxygen $(target).doxygen-xml-images.html : $(sources) : $(requirements) on QUIET=YES WARNINGS=NO WARN_IF_UNDOCUMENTED=NO $(.check-tools) ; $(project).mark-target-as-explicit $(target).doxygen-xml-images.html ; targets.create-typed-target DOXYGEN_XML_IMAGES : $(project) : $(target).doxygen-xml-images # Name. : $(target).doxygen-xml-images.html # Sources. : $(requirements) : $(default-build) ; $(project).mark-target-as-explicit $(target).doxygen-xml-images ; if ! [ MATCH (/)$ : $(images-location) ] { images-location = $(images-location)/ ; } requirements += $(target).doxygen-xml-images boost.doxygen.formuladir=$(images-location) ; } # The doxygen configuration file. targets.create-typed-target DOXYFILE : $(project) : $(target-xml:S=.tag) : $(sources) : $(requirements) GENERATE_HTML=NO GENERATE_XML=YES XML_OUTPUT=$(target-xml) : $(default-build) ; $(project).mark-target-as-explicit $(target-xml:S=.tag) ; # The Doxygen XML directory for the processed source files. targets.create-typed-target DOXYGEN_XML_MULTIFILE : $(project) : $(target-xml:S=.dir) # Name. : $(target-xml:S=.tag) # Sources. : $(requirements) : $(default-build) ; $(project).mark-target-as-explicit $(target-xml:S=.dir) ; # The resulting BoostBook file is generated by the processor tool. The # tool can be either the xsltproc plus accompanying XSL scripts. Or it # can be the python doxproc.py script. targets.create-typed-target BOOSTBOOK : $(project) : $(target-xml) : $(target-xml:S=.dir) # Sources. : $(requirements) : $(default-build) ; $(project).mark-target-as-explicit $(target-xml) ; stage $(target:S=.xml) # Name. : $(target-xml) # Sources. : $(requirements) $(location-xml:E=.) $(target:S=.xml) : $(default-build) ; $(project).mark-target-as-explicit $(target:S=.xml) ; # TODO: See why this alias target is used here instead of simply naming # the previous stage target $(target) and having it specify the alias # target's usage requirements directly. alias $(target) : : $(requirements) : $(default-build) : $(usage-requirements) $(target:S=.xml) ; } }