doc: cmake: Enable out-of-tree builds

The following changes have been made to support out-of-tree builds:

* In order to avoid using relative hardcoded paths, use CMake's
  configure_file() to replace the paths in the doxygen input file
  so that the output directory is set correctly.
* All .rst and additional required files are now copied from the doc/
  folder into the build/rst folder using extract_content.py. The
  samples/ and boards/ folder are copied twice (once into build/rst
  and another into build/doc/rst) to manage relative paths.
* All paths are absolute where possible, including themes and static
  content.

This patch ensures that the Zephyr repo is not contaminated by the
build at all.

Signed-off-by: Carles Cufi <carles.cufi@nordicsemi.no>
This commit is contained in:
Carles Cufi 2018-07-16 19:05:05 +02:00 committed by Anas Nashif
commit e182dbc22e
7 changed files with 109 additions and 78 deletions

View file

@ -38,17 +38,17 @@ endif()
# the i18n builder cannot share the environment and doctrees with the others
set(I18NSPHINXOPTS ${SPHINXOPTS})
set(DOXYFILE ${CMAKE_CURRENT_LIST_DIR}/zephyr.doxyfile)
set(BUILD_DOXYFILE ${CMAKE_CURRENT_BINARY_DIR}/build.doxyfile)
set(DOC_LOG doc.log)
set(DOXY_LOG doxy.log)
set(SPHINX_LOG sphinx.log)
set(DOC_WARN doc.warnings)
set(DOXYFILE_IN ${CMAKE_CURRENT_LIST_DIR}/zephyr.doxyfile.in)
set(DOXYFILE_OUT ${CMAKE_CURRENT_BINARY_DIR}/zephyr.doxyfile)
set(RST_OUT ${CMAKE_CURRENT_BINARY_DIR}/rst)
set(DOC_LOG ${CMAKE_CURRENT_BINARY_DIR}/doc.log)
set(DOXY_LOG ${CMAKE_CURRENT_BINARY_DIR}/doxy.log)
set(SPHINX_LOG ${CMAKE_CURRENT_BINARY_DIR}/sphinx.log)
set(DOC_WARN ${CMAKE_CURRENT_BINARY_DIR}/doc.warnings)
configure_file(${DOXYFILE} ${BUILD_DOXYFILE} COPYONLY)
file(APPEND ${BUILD_DOXYFILE} "STRIP_FROM_PATH=${ZEPHYR_BASE}\n")
configure_file(${DOXYFILE_IN} ${DOXYFILE_OUT} @ONLY)
set(ARGS ${BUILD_DOXYFILE})
set(ARGS ${DOXYFILE_OUT})
add_custom_target(
doxy
@ -64,21 +64,22 @@ add_custom_target(
add_custom_target(
pristine
COMMAND ${CMAKE_COMMAND} -P ${ZEPHYR_BASE}/cmake/pristine.cmake
COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_CURRENT_LIST_DIR}/html
COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_CURRENT_LIST_DIR}/xml
COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_CURRENT_LIST_DIR}/doxygen
COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_CURRENT_LIST_DIR}/latex
COMMAND ${CMAKE_COMMAND} -E remove ${CMAKE_CURRENT_LIST_DIR}/reference/kconfig/*.rst
COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_CURRENT_LIST_DIR}/samples
COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_CURRENT_LIST_DIR}/boards
COMMAND ${CMAKE_COMMAND} -E remove ${CMAKE_CURRENT_LIST_DIR}/${DOXY_LOG}
COMMAND ${CMAKE_COMMAND} -E remove ${CMAKE_CURRENT_LIST_DIR}/${SPHINX_LOG}
COMMAND ${CMAKE_COMMAND} -E remove ${CMAKE_CURRENT_LIST_DIR}/${DOC_LOG}
)
add_custom_target(
content
COMMAND ${PYTHON_EXECUTABLE} scripts/extract_content.py
# Copy the .rst files in doc/ to the rst folder
COMMAND ${CMAKE_COMMAND} -E env
ZEPHYR_BUILD=${CMAKE_CURRENT_BINARY_DIR}
${PYTHON_EXECUTABLE} scripts/extract_content.py ${RST_OUT} doc
# Copy the .rst files in samples/ and boards/ to the rst folder
COMMAND ${CMAKE_COMMAND} -E env
ZEPHYR_BUILD=${CMAKE_CURRENT_BINARY_DIR}
${PYTHON_EXECUTABLE} scripts/extract_content.py ${RST_OUT} samples boards
# Copy the .rst files in samples/ and boards/ to the doc folder inside rst
COMMAND ${CMAKE_COMMAND} -E env
ZEPHYR_BUILD=${CMAKE_CURRENT_BINARY_DIR}
${PYTHON_EXECUTABLE} scripts/extract_content.py ${RST_OUT}/doc samples boards
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
)
@ -90,29 +91,31 @@ endif()
add_custom_target(
kconfig
${CMAKE_COMMAND} -E env
COMMAND ${CMAKE_COMMAND} -E make_directory ${RST_OUT}/doc/reference/kconfig
COMMAND ${CMAKE_COMMAND} -E env
PYTHONPATH="${ZEPHYR_BASE}/scripts/kconfig${SEP}$ENV{PYTHONPATH}"
srctree=${ZEPHYR_BASE}
ENV_VAR_BOARD_DIR=boards/*/*/
ENV_VAR_ARCH=*
KERNELVERSION=${PROJECT_VERSION}
SRCARCH=x86
${PYTHON_EXECUTABLE} scripts/genrest.py ../Kconfig reference/kconfig/
${PYTHON_EXECUTABLE} scripts/genrest.py ../Kconfig ${RST_OUT}/doc/reference/kconfig/
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
)
set(KI_SCRIPT ${ZEPHYR_BASE}/scripts/filter-known-issues.py)
set(CONFIG_DIR ${ZEPHYR_BASE}/.known-issues/doc)
set(SPHINX_CMD COMMAND ${SPHINXBUILD} -w ${SPHINX_LOG} -N -t ${DOC_TAG} -b html ${ALLSPHINXOPTS} ${CMAKE_CURRENT_LIST_DIR} ${CMAKE_CURRENT_BINARY_DIR}/html)
configure_file(${CMAKE_CURRENT_LIST_DIR}/conf.py ${RST_OUT}/doc/conf.py COPYONLY)
configure_file(${CMAKE_CURRENT_LIST_DIR}/substitutions.txt ${RST_OUT}/doc/substitutions.txt COPYONLY)
add_custom_target(
html
${SPHINX_CMD}
COMMAND ${CMAKE_COMMAND} -E env
ZEPHYR_BUILD=${CMAKE_CURRENT_BINARY_DIR}
${SPHINXBUILD} -w ${SPHINX_LOG} -N -t ${DOC_TAG} -b html ${ALLSPHINXOPTS} ${RST_OUT}/doc ${CMAKE_CURRENT_BINARY_DIR}/html
# Merge the Doxygen and Sphinx logs into a single file
COMMAND ${CMAKE_COMMAND} -P ${ZEPHYR_BASE}/cmake/util/fmerge.cmake ${DOC_LOG} ${DOXY_LOG} ${SPHINX_LOG}
COMMAND ${PYTHON_EXECUTABLE} ${KI_SCRIPT} --config-dir ${CONFIG_DIR} --errors ${DOC_WARN} --warnings ${DOC_WARN} ${DOC_LOG}
COMMAND ${CMAKE_COMMAND} -E remove_directory samples
COMMAND ${CMAKE_COMMAND} -E remove_directory boards
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
${SPHINX_USES_TERMINAL}
)

View file

@ -100,16 +100,15 @@ folder, here are the commands to generate the html content locally:
cmake -GNinja ..
# Now run ninja on the generated build system:
ninja htmldocs
# If you modify or add .rst files, run ninja again:
ninja htmldocs
Depending on your development system, it will take about 15 minutes to
Depending on your development system, it will take up to 15 minutes to
collect and generate the HTML content. When done, you can view the HTML
output with your browser started at ``doc/_build/html/index.html``
If you want to build the documentation from scratch you can use this command:
.. code-block:: console
ninja pristine
If you want to build the documentation from scratch just delete the contents
of the build folder and run ``cmake`` and then ``ninja`` again.
On Unix platforms a convenience :file:`Makefile` at the root folder
of the Zephyr repository can be used to build the documentation directly from

View file

@ -17,13 +17,17 @@ import os
if "ZEPHYR_BASE" not in os.environ:
sys.exit("$ZEPHYR_BASE environment variable undefined.")
ZEPHYR_BASE = os.environ["ZEPHYR_BASE"]
ZEPHYR_BASE = os.path.abspath(os.environ["ZEPHYR_BASE"])
if "ZEPHYR_BUILD" not in os.environ:
sys.exit("$ZEPHYR_BUILD environment variable undefined.")
ZEPHYR_BUILD = os.path.abspath(os.environ["ZEPHYR_BUILD"])
# Add the 'extensions' directory to sys.path, to enable finding Sphinx
# extensions within.
sys.path.insert(0, os.path.join(os.path.abspath('.'), 'extensions'))
sys.path.insert(0, os.path.join(ZEPHYR_BASE, 'doc', 'extensions'))
# Also add west, to be able to pull in its API docs.
sys.path.append(os.path.abspath(os.path.join(ZEPHYR_BASE, 'scripts', 'meta')))
sys.path.append(os.path.join(ZEPHYR_BASE, 'scripts', 'meta'))
# -- General configuration ------------------------------------------------
@ -149,14 +153,14 @@ try:
import sphinx_rtd_theme
except ImportError:
html_theme = 'zephyr'
html_theme_path = ['./themes']
html_theme_path = ['{}/doc/themes'.format(ZEPHYR_BASE)]
else:
html_theme = "sphinx_rtd_theme"
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
if tags.has('daily') or tags.has('release'):
html_theme = 'zephyr-docs-theme'
html_theme_path = ['./themes']
html_theme_path = ['{}/doc/themes'.format(ZEPHYR_BASE)]
if tags.has('release'):
@ -189,7 +193,7 @@ html_title = "Zephyr Project Documentation"
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['static']
html_static_path = ['{}/doc/static'.format(ZEPHYR_BASE)]
# Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied
@ -341,8 +345,8 @@ texinfo_documents = [
#texinfo_no_detailmenu = False
breathe_projects = {
"Zephyr": "doxygen/xml",
"doc-examples": "doxygen/xml"
"Zephyr": "{}/doxygen/xml".format(ZEPHYR_BUILD),
"doc-examples": "{}/doxygen/xml".format(ZEPHYR_BUILD)
}
breathe_default_project = "Zephyr"

View file

@ -340,7 +340,7 @@ are an allowed exception.
zephyr-app-commands Directive
*****************************
.. include:: /extensions/zephyr/application.py
.. include:: ../extensions/zephyr/application.py
:start-line: 10
:start-after: '''
:end-before: '''

View file

@ -1 +0,0 @@
This directory has auto-generated files, do not edit any of the files here.

View file

@ -1,5 +1,6 @@
#!/usr/bin/env python3
#
# Copyright (c) 2018, Nordic Semiconductor ASA
# Copyright (c) 2017, Intel Corporation
#
# SPDX-License-Identifier: Apache-2.0
@ -7,15 +8,13 @@
# Very quick script to move docs from different places into the doc directory
# to fix the website and external links
import os
import shutil
import re
import sys
import errno
import filecmp
import fnmatch
# direcories to search for .rst files
CONTENT_DIRS = ["samples", "boards"]
import os
import re
import shutil
import sys
# directives to parse for included files
DIRECTIVES = ["figure","include","image","literalinclude"]
@ -25,18 +24,35 @@ if "ZEPHYR_BASE" not in os.environ:
exit(1)
ZEPHYR_BASE = os.environ["ZEPHYR_BASE"]
def get_rst_files(dir):
if "ZEPHYR_BUILD" in os.environ:
ZEPHYR_BUILD = os.environ["ZEPHYR_BUILD"]
else:
ZEPHYR_BUILD = None
def copy_if_different(src, dst):
# Copies 'src' as 'dst', but only if dst does not exist or if itx contents
# differ from src.This avoids unnecessary # timestamp updates, which
# trigger documentation rebuilds.
if os.path.exists(dst) and filecmp.cmp(src, dst):
return
shutil.copyfile(src, dst)
def get_rst_files(dest, dir):
matches = []
for root, dirnames, filenames in os.walk('%s/%s' %(ZEPHYR_BASE, dir)):
if ZEPHYR_BUILD:
if os.path.normpath(root).startswith(os.path.normpath(ZEPHYR_BUILD)):
# Build folder, skip it
continue
for filename in fnmatch.filter(filenames, '*.rst'):
matches.append(os.path.join(root, filename))
for file in matches:
frel = file.replace(ZEPHYR_BASE,"").strip("/")
dir=os.path.dirname(frel)
if not os.path.exists(os.path.join(ZEPHYR_BASE, "doc", dir)):
os.makedirs(os.path.join(ZEPHYR_BASE, "doc", dir))
if not os.path.exists(os.path.join(dest, dir)):
os.makedirs(os.path.join(dest, dir))
shutil.copyfile(file, os.path.join(ZEPHYR_BASE, "doc", frel))
copy_if_different(file, os.path.join(dest, frel))
try:
with open(file, encoding="utf-8") as f:
@ -50,12 +66,14 @@ def get_rst_files(dir):
if m:
inf = m.group(2)
ind = os.path.dirname(inf)
if not os.path.exists(os.path.join(ZEPHYR_BASE, "doc", dir, ind)):
os.makedirs(os.path.join(ZEPHYR_BASE, "doc", dir, ind))
if not os.path.exists(os.path.join(dest, dir, ind)):
os.makedirs(os.path.join(dest, dir, ind))
src = os.path.join(ZEPHYR_BASE, dir, inf)
dst = os.path.join(dest, dir, inf)
try:
shutil.copyfile(os.path.join(ZEPHYR_BASE, dir, inf),
os.path.join(ZEPHYR_BASE, "doc", dir, inf))
copy_if_different(src, dst)
except FileNotFoundError:
sys.stderr.write("File not found: %s\n reference by %s\n" % (inf, file))
@ -73,8 +91,16 @@ def get_rst_files(dir):
f.close()
def main():
for d in CONTENT_DIRS:
get_rst_files(d)
if len(sys.argv) < 3:
print("usage: {} <dest> <org dirs>", file=sys.stderr)
sys.exit(1)
dest = sys.argv[1]
content_dirs = sys.argv[2:]
for d in content_dirs:
get_rst_files(dest, d)
if __name__ == "__main__":
main()

View file

@ -58,7 +58,7 @@ PROJECT_LOGO =
# entered, it will be relative to the location where doxygen was started. If
# left blank the current directory will be used.
OUTPUT_DIRECTORY = doxygen/
OUTPUT_DIRECTORY = @CMAKE_CURRENT_BINARY_DIR@/doxygen/
# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-
# directories (in 2 levels) under the output directory of each output format and
@ -144,7 +144,7 @@ FULL_PATH_NAMES = YES
# will be relative from the directory where doxygen is started.
# This tag requires that the tag FULL_PATH_NAMES is set to YES.
STRIP_FROM_PATH =
STRIP_FROM_PATH = @ZEPHYR_BASE@
# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
# path mentioned in the documentation of a class, which tells the reader which
@ -750,19 +750,19 @@ WARN_LOGFILE =
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
# Note: If this tag is empty the current directory is searched.
INPUT = ../include/ \
../include/misc/ \
../include/arch/x86/ \
../include/arch/arc/ \
../include/arch/arc/v2 \
../include/arch/arm/ \
../include/arch/arm/cortex_m \
../include/arch/nios2/ \
../lib/libc/minimal/include/ \
../include/net/coap.h \
../include/net/dns_resolve.h \
../tests/ztest/include/ \
../tests/kernel/
INPUT = @ZEPHYR_BASE@/include/ \
@ZEPHYR_BASE@/include/misc/ \
@ZEPHYR_BASE@/include/arch/x86/ \
@ZEPHYR_BASE@/include/arch/arc/ \
@ZEPHYR_BASE@/include/arch/arc/v2 \
@ZEPHYR_BASE@/include/arch/arm/ \
@ZEPHYR_BASE@/include/arch/arm/cortex_m \
@ZEPHYR_BASE@/include/arch/nios2/ \
@ZEPHYR_BASE@/lib/libc/minimal/include/ \
@ZEPHYR_BASE@/include/net/coap.h \
@ZEPHYR_BASE@/include/net/dns_resolve.h \
@ZEPHYR_BASE@/tests/ztest/include/ \
@ZEPHYR_BASE@/tests/kernel/
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
@ -804,9 +804,9 @@ RECURSIVE = YES
# Note that relative paths are relative to the directory from which doxygen is
# run.
EXCLUDE = ../include/spi_legacy.h \
../include/net/http_legacy.h \
../include/misc/util.h
EXCLUDE = @ZEPHYR_BASE@/include/spi_legacy.h \
@ZEPHYR_BASE@/include/net/http_legacy.h \
@ZEPHYR_BASE@/include/misc/util.h
# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
# directories that are symbolic links (a Unix file system feature) are excluded