# 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 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 "class" : new ; import targets ; import feature ; import property ; import generators ; import boostbook ; import type ; import path ; import print ; import regex ; import stage ; import project ; import xsltproc ; import make ; import os ; import toolset : flags ; import alias ; import common ; import modules ; import project ; import utility ; import errors ; # 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 ; } } rule freeze-config ( ) { if ! $(.initialized) { errors.user-error "doxygen must be initialized before it can be used." ; } if ! $(.config-frozen) { .config-frozen = true ; if [ .is-cygwin ] { .is-cygwin = true ; } } } rule modify-config ( ) { if $(.config-frozen) { errors.user-error "Cannot change doxygen after it has been used." ; } } 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) ; } 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" ; 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 = [ regex.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 Boost.Book stylesheets, so we need some code to check # that the boostbook module has actualy been initialized. # rule check-boostbook ( ) { if ! [ modules.peek boostbook : .initialized ] { ECHO "error: the boostbook module is not initialized" ; ECHO "error: you've attempted to use the 'doxygen' toolset, " ; ECHO "error: which requires Boost.Book," ; ECHO "error: but never initialized Boost.Book." ; EXIT "error: 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) ; } flags doxygen.xml-dir-to-boostbook OPTIONS yes : --enable-index ; flags doxygen.xml-dir-to-boostbook ID ; 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 ps 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 >$(<) } } rule check-tools ( ) { if ! $(.check-tools-targets) { # Find the root project. local root-project = [ project.current ] ; root-project = [ $(root-project).project-module ] ; while [ project.attribute $(root-project) parent-module ] && [ project.attribute $(root-project) parent-module ] != user-config { root-project = [ project.attribute $(root-project) parent-module ] ; } .latex.check = [ new file-target latex.check : : [ project.target $(root-project) ] : [ new action : doxygen.check-latex ] : ] ; .dvips.check = [ new file-target dvips.check : : [ project.target $(root-project) ] : [ new action : doxygen.check-dvips ] : ] ; .gs.check = [ new file-target gs.check : : [ project.target $(root-project) ] : [ new action : doxygen.check-gs ] : ] ; .check-tools-targets = $(.latex.check) $(.dvips.check) $(.gs.check) ; } return $(.check-tools-targets) ; } project.initialize $(__name__) ; project doxygen ; class doxygen-check-tools-target-class : basic-target { import doxygen ; rule construct ( name : sources * : property-set ) { return [ property-set.empty ] [ doxygen.check-tools ] ; } } local project = [ project.current ] ; targets.main-target-alternative [ new doxygen-check-tools-target-class check-tools : $(project) : [ targets.main-target-sources : check-tools : no-renaming ] : [ targets.main-target-requirements : $(project) ] : [ targets.main-target-default-build : $(project) ] : [ targets.main-target-usage-requirements : $(project) ] ] ; # User-level rule to generate BoostBook XML from a set of headers via Doxygen. # rule doxygen ( target : sources * : requirements * : default-build * : usage-requirements * ) { 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 that 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.main-target-alternative [ new typed-target $(target:S=.tag) : $(project) : DOXYFILE : [ targets.main-target-sources $(sources) : $(target:S=.tag) ] : [ targets.main-target-requirements $(requirements) GENERATE_HTML=YES GENERATE_XML=NO "OUTPUT_DIRECTORY=\"$(output-dir-native)\"" HTML_OUTPUT=$(target:B) : $(project) ] : [ targets.main-target-default-build $(default-build) : $(project) ] ] ; $(project).mark-target-as-explicit $(target:S=.tag) ; ## The html directory to generate by running doxygen. targets.main-target-alternative [ new typed-target $(target:S=.dir) : $(project) : DOXYGEN_HTML_MULTIFILE : $(target:S=.tag) : [ targets.main-target-requirements $(requirements) "OUTPUT_DIRECTORY=\"$(output-dir-native)\"" HTML_OUTPUT=$(target:B) : $(project) ] : [ targets.main-target-default-build $(default-build) : $(project) ] ] ; $(project).mark-target-as-explicit $(target:S=.dir) ; ## The redirect html file into the generated html. targets.main-target-alternative [ new typed-target $(target) : $(project) : DOXYGEN_HTML : $(target:S=.dir) : [ targets.main-target-requirements $(requirements) $(output-dir) : $(project) ] : [ targets.main-target-default-build $(default-build) : $(project) ] ] ; } 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) { doxygen $(target).doxygen-xml-images.html : $(sources) : $(requirements) on QUIET=YES WARNINGS=NO WARN_IF_UNDOCUMENTED=NO /doxygen//check-tools ; $(project).mark-target-as-explicit $(target).doxygen-xml-images.html ; targets.main-target-alternative [ new typed-target $(target).doxygen-xml-images : $(project) : DOXYGEN_XML_IMAGES : $(target).doxygen-xml-images.html : [ targets.main-target-requirements $(requirements) : $(project) ] : [ targets.main-target-default-build $(default-build) : $(project) ] ] ; $(project).mark-target-as-explicit $(target).doxygen-xml-images ; if ! [ regex.match "^(.*/)$" : $(images-location) ] { images-location = $(images-location)/ ; } requirements += $(target).doxygen-xml-images boost.doxygen.formuladir=$(images-location) ; } ## The doxygen configuration file. targets.main-target-alternative [ new typed-target $(target-xml:S=.tag) : $(project) : DOXYFILE : [ targets.main-target-sources $(sources) : $(target-xml:S=.tag) ] : [ targets.main-target-requirements $(requirements) GENERATE_HTML=NO GENERATE_XML=YES XML_OUTPUT=$(target-xml) : $(project) ] : [ targets.main-target-default-build $(default-build) : $(project) ] ] ; $(project).mark-target-as-explicit $(target-xml:S=.tag) ; ## The Doxygen XML directory of the processed source files. targets.main-target-alternative [ new typed-target $(target-xml:S=.dir) : $(project) : DOXYGEN_XML_MULTIFILE : $(target-xml:S=.tag) : [ targets.main-target-requirements $(requirements) : $(project) ] : [ targets.main-target-default-build $(default-build) : $(project) ] ] ; $(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.main-target-alternative [ new typed-target $(target-xml) : $(project) : BOOSTBOOK : $(target-xml:S=.dir) : [ targets.main-target-requirements $(requirements) : $(project) ] : [ targets.main-target-default-build $(default-build) : $(project) ] ] ; $(project).mark-target-as-explicit $(target-xml) ; targets.main-target-alternative [ new install-target-class $(target:S=.xml) : $(project) : $(target-xml) : [ targets.main-target-requirements $(requirements) $(location-xml:E=.) $(target:S=.xml) : $(project) ] : [ targets.main-target-default-build $(default-build) : $(project) ] ] ; $(project).mark-target-as-explicit $(target:S=.xml) ; targets.main-target-alternative [ new alias-target-class $(target) : $(project) : : [ targets.main-target-requirements $(requirements) : $(project) ] : [ targets.main-target-default-build $(default-build) : $(project) ] : [ targets.main-target-usage-requirements $(usage-requirements) $(target:S=.xml) : $(project) ] ] ; } }