# ********************************************************** # Copyright (c) 2012-2021 Google, Inc. All rights reserved. # Copyright (c) 2009-2010 VMware, Inc. All rights reserved. # ********************************************************** # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # * Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # # * Neither the name of VMware, Inc. nor the names of its contributors may be # used to endorse or promote products derived from this software without # specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH # DAMAGE. ########################################################################### # Usage: # + Add a custom command to invoke this script in the docs build dir # + Assumes a favicon.ico is present in the target build dir # # Args: # + DOXYGEN_EXECUTABLE : path to doxygen # + doxygen_ver : version of doxygen # + version_number : version number to put in the docs # + module_string_long : replacement text for Modules # + module_string_short : replacement text for Module # + files_string : replacement text for Files; optional # + structs_string : replacement text for Data Structures; optional # + home_url : home page URL # + home_title : home page title # + logo_imgfile : image file with logo # + embeddable : whether to edit content for jekyll embedding # + proj_srcdir : project base source dir, for editing titles # + proj_bindir : project base binary dir, for editing titles # ########################################################################### # Any quotes carry over and it's simplest to assume not there. string(REPLACE "\"" "" module_string_long ${module_string_long}) string(REPLACE "\"" "" module_string_short ${module_string_short}) if (DEFINED files_string) string(REPLACE "\"" "" files_string ${files_string}) endif () if (DEFINED structs_string) string(REPLACE "\"" "" structs_string ${structs_string}) endif () string(REPLACE "\"" "" home_url ${home_url}) string(REPLACE "\"" "" home_title ${home_title}) string(REPLACE "\"" "" logo_imgfile ${logo_imgfile}) # First, generate header.html and footer.html suitable for current # doxygen version and then tweak them. # Note that we must generate a css file and we must name it doxygen.css # or else we have to later copy it to html dir. execute_process(COMMAND ${DOXYGEN_EXECUTABLE} -w html header.html footer.html doxygen.css RESULT_VARIABLE doxygen_w_result ERROR_VARIABLE doxygen_w_error OUTPUT_QUIET ) if (doxygen_w_result OR doxygen_w_error) message(FATAL_ERROR "*** ${DOXYGEN_EXECUTABLE} failed: ***\n${doxygen_w_error}") endif (doxygen_w_result OR doxygen_w_error) # Add favicon to header file(READ ${CMAKE_CURRENT_BINARY_DIR}/header.html string) string(REGEX REPLACE "" "<link rel=\"shortcut icon\" type=\"image/x-icon\" href=\"favicon.ico\"/>\n<title>" string "${string}") if (doxygen_ver VERSION_LESS "1.7.0") # For some reason older doxygen doesn't replace its own vars w/ custom header string(REGEX REPLACE "\\$relpath\\$" "" string "${string}") endif () if (embeddable) # Add jekyll header. set(string "--- title: \"$title\" layout: default --- ${string}") endif () file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/header.html "${string}") # Tweak footer to have version file(READ ${CMAKE_CURRENT_BINARY_DIR}/footer.html string) set(foot "<img border=0 src=\"favicon.png\">   ") set(foot "${foot} $projectname version ${version_number} --- $datetime") set(foot "${foot}   <img border=0 src=\"favicon.png\">") # Recent doxygen string(REGEX REPLACE "\\$generated.*doxygenversion" "${foot}" string "${string}") # Older doxygen string(REGEX REPLACE "Generated.*doxygenversion" "${foot}" string "${string}") # Prefer center to default float:right string(REGEX REPLACE "class=\"footer\"" "class=\"footer\" style=\"float:none;text-align:center\"" string "${string}") file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/footer.html "${string}") if (doxygen_ver STRGREATER "1.7.2") # Create a layout file so we can control the treeview index execute_process(COMMAND ${DOXYGEN_EXECUTABLE} -l RESULT_VARIABLE doxygen_l_result ERROR_VARIABLE doxygen_l_error OUTPUT_QUIET ) if (doxygen_l_result OR doxygen_l_error) message(FATAL_ERROR "*** ${DOXYGEN_EXECUTABLE} failed: ***\n${doxygen_l_error}") endif (doxygen_l_result OR doxygen_l_error) file(READ ${CMAKE_CURRENT_BINARY_DIR}/DoxygenLayout.xml string) # Rename "Modules" (done below for older doxygen) string(REGEX REPLACE "tab type=\"modules\" visible=\"yes\" title=\"\"" "tab type=\"modules\" visible=\"yes\" title=\"${module_string_long}\"" string "${string}") if (doxygen_ver STRGREATER "1.7.5") # Add link to home page (done below for older doxygen; skipped for 1.7.3-1.7.5) string(REGEX REPLACE "</navindex>" "<tab type=\"user\" url=\"${home_url}\" title=\"${home_title}\"/>\n</navindex>" string "${string}") endif () file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/DoxygenLayout.xml "${string}") endif () # Run doxygen and fail on warnings. # Now update to latest doxygen. Suppress warnings since they're misleading: # they say "please run doxygen -u" but we're currently doing just that. execute_process(COMMAND ${DOXYGEN_EXECUTABLE} RESULT_VARIABLE doxygen_u_result ERROR_VARIABLE doxygen_u_error OUTPUT_QUIET ) if (doxygen_u_result OR doxygen_u_error) message(FATAL_ERROR "*** ${DOXYGEN_EXECUTABLE} failed: ***\n${doxygen_u_error}") endif (doxygen_u_result OR doxygen_u_error) # Doxygen does not put header.html on the top-level frame so we add favicon manually file(READ ${CMAKE_CURRENT_BINARY_DIR}/html/index.html string) string(REGEX REPLACE "<title>" "<link rel=\"shortcut icon\" type=\"image/x-icon\" href=\"favicon.ico\"/>\n<title>" string "${string}") file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/html/index.html "${string}") if (embeddable) # We need to make some edits for insertion into our jekyll site. file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/html/keywords.js "---\nlayout: null\n---\n") file(GLOB all_html ${CMAKE_CURRENT_BINARY_DIR}/html/*.html) foreach (html ${all_html}) file(READ ${html} string) # Remove headers and body tag. string(REGEX REPLACE "<!-- HTML header.*<body>\n" "" string "${string}") # Remove body and html close tags. string(REGEX REPLACE "</body>.*</html>\n" "" string "${string}") # Remove full paths. string(REGEX REPLACE "\ntitle: \"${proj_bindir}" "\ntitle: \"" string "${string}") string(REGEX REPLACE "\ntitle: \"${proj_srcdir}" "\ntitle: \"" string "${string}") # Add permalink to front matter so the web site hides the docs/ subdir. get_filename_component(linkname ${html} NAME) string(REGEX REPLACE "(\nlayout: default\n)" "\\1permalink: /${linkname}\n" string "${string}") # Our images are in an images/ subdir. string(REGEX REPLACE "<img src=\"" "<img src=\"/images/" string "${string}") string(REGEX REPLACE "<object type=\"image/svg\\+xml\" data=\"" "<object type=\"image/svg+xml\" data=\"/images/" string "${string}") # Ensure that strings that look like Jekyll tags are not interpreted as such. # We add the Jekyll raw-endraw tags around "{%" and "%}" separately. We use # a random suffix for jekyll_raw_tag and jekyll_endraw_tag to reduce the # possibility of a conflict with an actual use of these strings. string(REGEX REPLACE "{%" "jekyll_raw_tag_f00d{%jekyll_endraw_tag_f00d" string "${string}") string(REGEX REPLACE "%}" "jekyll_raw_tag_f00d%}jekyll_endraw_tag_f00d" string "${string}") string(REGEX REPLACE "jekyll_raw_tag_f00d" "{% raw %}" string "${string}") string(REGEX REPLACE "jekyll_endraw_tag_f00d" "{% endraw %}" string "${string}") # Collect type info for keyword searches with direct anchor links (else the # search finds the page but the user then has to manually search within the long # page -- and there are no doygen options to split each onto its own page). # The format here is the javascript object used by search.js to set up lunr. # XXX: This is fragile and highly dependent on the precise doxygen output. get_filename_component(fname ${html} NAME_WE) # The ;'s mess up the MATCHALL results so we remove them. string(REPLACE ";" "_" nosemis "${string}") string(REGEX MATCHALL "<tr class=\"memitem[^\n]*</tr>" types "${nosemis}") foreach (type ${types}) string(REGEX MATCH "href=\"[^\"]+\"" url "${type}") string(REGEX REPLACE "href=\"([^\"]+)\"" "\\1" url "${url}") string(REGEX MATCH "\">[_a-zA-Z0-9]+</a>" name "${type}") string(REGEX REPLACE "\">([_a-zA-Z0-9]+)</a>" "\\1" name "${name}") if (name STREQUAL "") continue () endif () # Support searching by partial names. set(extra "") string(REPLACE "_" " " separate "${name}") if (NOT separate STREQUAL name) set(extra "${extra} ${separate}") endif () set(prefix "${name}") while (prefix MATCHES "[^_]_[^_]") string(REGEX REPLACE "_[^_]*$" "" prefix "${prefix}") set(extra "${extra} ${prefix}") endwhile () set(keywords "window.data[\"${fname}-${name}\"]={\ \"name\":\"${fname}-${name}\",\ \"title\":\"${name} in ${fname} header\",\ \"url\":\"${url}\",\ \"content\":\"${name} ${extra}\"};\n") file(APPEND ${CMAKE_CURRENT_BINARY_DIR}/html/keywords.js "${keywords}") endforeach () # Similarly, put in explicit search entries for sections so searches go # straight to the anchor link. string(REGEX MATCHALL "\n<h1><a class=\"anchor\" id=\"[^\"]*\"></a>\n[^\n]+</h1>" sections "${nosemis}") foreach (section ${sections}) string(REGEX REPLACE ".* id=\"([^\"]+)\".*" "\\1" id "${section}") string(REGEX REPLACE ".*</a>\n([^<]+)</h1>" "\\1" name "${section}") if (name STREQUAL "" OR id STREQUAL "" OR name MATCHES "<") message(FATAL_ERROR "Failed to find section name or anchor: ${section}") endif () set(url "/${fname}.html#${id}") # Quotes cause JavaScript syntax errors. string(REPLACE "\"" "" name "${name}") # Spaces would be ok but it feels nicer w/o them in the key. string(REPLACE " " "_" key "${name}") set(keywords "window.data[\"${fname}-${key}\"]={\ \"name\":\"${fname}-${key}\",\ \"title\":\"${name}\",\ \"url\":\"${url}\",\ \"content\":\"${name}\"};\n") file(APPEND ${CMAKE_CURRENT_BINARY_DIR}/html/keywords.js "${keywords}") endforeach () file(WRITE ${html} "${string}") endforeach () # We leverage the javascript menu from the non-embedded version. # Menu hierarchy changes: # (1) Remove the outer "DynamoRIO" layer. # (2) Remove home page at the end. # (3) Insert an "API Reference" layer around API details. # (4) Move the "Extension API" menu inside "Build Your Own Tool". file(GLOB all_js ${CMAKE_CURRENT_BINARY_DIR}/../html/*.js) set(found_user_docs OFF) foreach (js ${all_js}) if (js MATCHES "navtree.js" OR js MATCHES "resize.js" OR js MATCHES "navtreeindex") continue () endif () file(READ ${js} string) # Ask jekyll to inline, instead of just naming for JS. string(REGEX REPLACE ", \"([^\"]+)\" \\]" ", {% include_relative \\1.js %} ]" string "${string}") if (js MATCHES "navtreedata.js") # Add a jekyll header. set(string "---\npermalink: /navtreedata.js\n---\n${string}") # Remove the index array from navtreedata.js. string(REGEX REPLACE "var NAVTREEINDEX =[^;]*;" "" string "${string}") # Remove one layer from navtreedata.js. string(REGEX REPLACE "\n *\\[ \"DynamoRIO\", \"index.html\", \\[" "" string "${string}") # Remove the home page at the end. string(REGEX REPLACE "\n[^\n]*DynamoRIO Home Page[^\n]*\n" "" string "${string}") # Insert a layer in navtreedata.js, using the end of the top layer we removed. string(REGEX REPLACE "\n( *\\[ \"Deprecated List)" "\n[ \"API Reference\", \"files.html\", [\n\\1" string "${string}") if (NOT string MATCHES "Extension API") message(FATAL_ERROR "Cannot find menu entry for page \"Extension API\"") endif () string(REGEX MATCH "\n[^\n]+\"Extension API\"[^\n]+\n" ext_entry "${string}") # If ext_entry ends with a [, the menu items are expanded inline and the # whole Extension API menu can be moved within this file. string(REGEX MATCH "\\[\n" ext_multiline "${ext_entry}") if (ext_multiline) # Extract everything up to but not including the newly-inserted API Reference. string(REGEX REPLACE "(\n[^\n]+\"Extension API\",.+)(\n\\[ \"API Reference\")" "\\2" string "${string}") # Remember the full Extension API match. set(ext_entry ${CMAKE_MATCH_1}) if (NOT ext_entry) message(FATAL_ERROR "Cannot move menu entry for \"Extension API\"") endif () # Now insert the Extension API menu right above Disassembly Library. if (NOT string MATCHES "Disassembly Library") message(FATAL_ERROR "Cannot find menu entry for page \"Disassembly Library\"") endif () string(REGEX REPLACE "(\n[^\n]+\"Disassembly Library)" "${ext_entry}\\1" string "${string}") # Set the user_docs flag since we found its contents here. set(found_user_docs ON) else () # Remove the 1-line Extension API string from this file. string(REPLACE "${ext_entry}" "\n" string "${string}") endif () else () # Remove name so we can inline. string(REGEX REPLACE "var [^\n]* =\n" "" string "${string}") # End in a comma for inlining. string(REGEX REPLACE "\\];" "]," string "${string}") # Update directory index names which are hashes that differ between the # treeview and embed generations. string(REGEX MATCHALL "\"[a-zA-Z_0-9]+\", \"dir_[a-f0-9]+\\.html\"" dir_hashes "${string}") foreach (dir_hash ${dir_hashes}) string(REGEX REPLACE "\"([a-zA-Z_0-9]+)\".*" "\\1" key "${dir_hash}") # We go search the dir_*.html embed files looking for key. file(GLOB dir_html ${CMAKE_CURRENT_BINARY_DIR}/html/dir_*.html) set(found_hash OFF) foreach (html ${dir_html}) file(READ ${html} html_string) if (html_string MATCHES "${key} Directory Reference") get_filename_component(fname ${html} NAME) string(REGEX REPLACE "\"${key}\", \"dir_[a-f0-9]+\\.html\"" "\"${key}\", \"${fname}\"" string "${string}") set(found_hash ON) break () endif () endforeach () if (NOT found_hash) message(FATAL_ERROR "Cannot find corresonding page for ${key}") endif () endforeach () endif () if (js MATCHES "page_user_docs.js") # CMake 3.6+ guarantees the glob is sorted lexicographically, so we've already # seen navtreedata.js. if (found_user_docs) message(FATAL_ERROR "Found unexpected \"page_user_docs\" menu file") endif () set(found_user_docs ON) if (ext_entry) if (NOT string MATCHES "Disassembly Library") message(FATAL_ERROR "Cannot find menu entry for page \"Disassembly Library\"") endif () string(REGEX REPLACE "\n([^\n]+\"Disassembly Library)" "${ext_entry}\\1" string "${string}") else () message(FATAL_ERROR "Failed to find the menu entries to move") endif () endif () # Put the modified contents into our dir. get_filename_component(fname ${js} NAME) file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/html/${fname} "${string}") endforeach () if (NOT found_user_docs) message(FATAL_ERROR "Cannot find \"page_user_docs\" menu file") endif () else () # Edit navbar. if (doxygen_ver VERSION_LESS "1.7.3") # Add link to home page to treeview pane file(APPEND ${CMAKE_CURRENT_BINARY_DIR}/html/tree.html "<p> <a target=\"main\" href=\"${home_url}/\">${home_title}</a></p>") # Add our logo to treeview pane (yes there is no closing </body></html>) file(APPEND ${CMAKE_CURRENT_BINARY_DIR}/html/tree.html "<p> <br><img src=\"${logo_imgfile}\"></p>") else () # This is a fragile insertion into the javascript used for the navbar. # We insert at the end of the final function (initNavTree). # This works in doxygen 1.7.4-1.8.1. set(add_logo " var imgdiv = document.createElement(\"div\"); document.getElementById(\"nav-tree-contents\").appendChild(imgdiv); var img = document.createElement(\"img\"); imgdiv.appendChild(img); img.src = \"${logo_imgfile}\"; img.style.marginLeft = \"80px\"; img.style.marginTop = \"30px\"; ") file(READ ${CMAKE_CURRENT_BINARY_DIR}/html/navtree.js string) string(REGEX REPLACE "\\}\n*$" "${add_logo}\n}" string "${string}") # Doxygen 1.9.1 has this license-end line. string(REGEX REPLACE "\\}\n/\\* @license-end \\*/\n$" "${add_logo}\n}" string "${string}") # While we're here we replace Files and Data Structures, if requested if (DEFINED files_string) string(REGEX REPLACE "\"Files\"" "\"${files_string}\"" string "${string}") endif () if (DEFINED structs_string) string(REGEX REPLACE "\"Data Structures\"" "\"${structs_string}\"" string "${string}") endif () file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/html/navtree.js "${string}") endif () endif () # For modules/extensions (i#277/PR 540817) we use doxygen groups which show up under # top-level "Modules" which we rename here if (doxygen_ver VERSION_LESS "1.7.3") file(READ ${CMAKE_CURRENT_BINARY_DIR}/html/tree.html string) string(REGEX REPLACE "Modules" "${module_string_long}" string "${string}") file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/html/tree.html "${string}") endif () if (EXISTS ${CMAKE_CURRENT_BINARY_DIR}/html/modules.html) file(READ ${CMAKE_CURRENT_BINARY_DIR}/html/modules.html string) string(REGEX REPLACE "Module" "${module_string_short}" string "${string}") string(REGEX REPLACE "module" "${module_string_short}" string "${string}") file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/html/modules.html "${string}") endif () # We do not copy samples or favicon for build dir: those are install rules