blob: 0060b645d5cdc2c57b04facdac37c2c8ff18a85f [file] [log] [blame]
lowRISC Contributors802543a2019-08-31 12:12:56 +01001#!/usr/bin/env python3
2# Copyright lowRISC contributors.
3# Licensed under the Apache License, Version 2.0, see LICENSE for details.
4# SPDX-License-Identifier: Apache-2.0
5#
lowRISC Contributors802543a2019-08-31 12:12:56 +01006# Usage:
7# run './build_docs.py' to generate the documentation and keep it updated
Garret Kelly9eebde02019-10-22 15:36:49 -04008# open 'http://localhost:1313/' to check live update (this opens the top
lowRISC Contributors802543a2019-08-31 12:12:56 +01009# level index page). you can also directly access a specific document by
Garret Kelly9eebde02019-10-22 15:36:49 -040010# accessing 'http://localhost:1313/path/to/doc',
11# e.g. http://localhost:1313/hw/ip/uart/doc
lowRISC Contributors802543a2019-08-31 12:12:56 +010012
13import argparse
14import logging
15import os
Philipp Wagnercd57f862019-10-31 14:20:39 +000016import platform
Silvestrs Timofejevsecaf87c2019-11-12 21:52:57 +000017import re
Garret Kelly9eebde02019-10-22 15:36:49 -040018import subprocess
Tobias Wölfel19a86e22020-05-14 14:31:00 +020019import sys
Sam Elliott692078c2020-02-07 17:23:21 +000020import textwrap
lowRISC Contributors802543a2019-08-31 12:12:56 +010021from pathlib import Path
22
Philipp Wagner2e217a92020-10-09 12:19:08 +020023import check_tool_requirements
Garret Kelly9eebde02019-10-22 15:36:49 -040024import dashboard.gen_dashboard_entry as gen_dashboard_entry
Sam Elliottb6745d32020-04-08 21:46:28 +010025import difgen.gen_dif_listing as gen_dif_listing
Garret Kelly9eebde02019-10-22 15:36:49 -040026import reggen.gen_cfg_html as gen_cfg_html
27import reggen.gen_html as gen_html
Eunchan Kim9790e3e2019-11-01 15:17:32 -070028import reggen.gen_selfdoc as reggen_selfdoc
Udi Jonnalagaddadf49fb82020-03-17 11:05:17 -070029import dvsim.testplanner.testplan_utils as testplan_utils
Eunchan Kim9790e3e2019-11-01 15:17:32 -070030import tlgen
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +000031from reggen.ip_block import IpBlock
lowRISC Contributors802543a2019-08-31 12:12:56 +010032
33USAGE = """
34 build_docs [options]
35"""
36
Philipp Wagnercd57f862019-10-31 14:20:39 +000037# Version of hugo extended to be used to build the docs
Philipp Wagner2e217a92020-10-09 12:19:08 +020038try:
Rupert Swarbrick9ab407e2021-01-25 11:40:17 +000039 TOOL_REQUIREMENTS = check_tool_requirements.read_tool_requirements()
40 HUGO_EXTENDED_VERSION = TOOL_REQUIREMENTS['hugo_extended'].min_version
Philipp Wagner2e217a92020-10-09 12:19:08 +020041except Exception as e:
42 print("Unable to get required hugo version: %s" % str(e), file=sys.stderr)
43 sys.exit(1)
Philipp Wagnercd57f862019-10-31 14:20:39 +000044
lowRISC Contributors802543a2019-08-31 12:12:56 +010045# Configurations
Tobias Wölfel7840d7b2019-10-07 12:29:33 +020046# TODO: Move to config.yaml
lowRISC Contributors802543a2019-08-31 12:12:56 +010047SRCTREE_TOP = Path(__file__).parent.joinpath('..').resolve()
48config = {
49 # Toplevel source directory
Garret Kelly9eebde02019-10-22 15:36:49 -040050 "topdir":
51 SRCTREE_TOP,
lowRISC Contributors802543a2019-08-31 12:12:56 +010052
Garret Kelly9eebde02019-10-22 15:36:49 -040053 # Pre-generate register and hwcfg fragments from these files.
54 "hardware_definitions": [
55 "hw/ip/aes/data/aes.hjson",
Tom Roberts7f173f22020-12-01 14:29:31 +000056 "hw/ip/aon_timer/data/aon_timer.hjson",
Sam Elliottd834c702020-02-12 11:18:47 +000057 "hw/top_earlgrey/ip/alert_handler/data/autogen/alert_handler.hjson",
Mark Branstad4efd35e2019-12-19 15:44:45 +000058 "hw/ip/entropy_src/data/entropy_src.hjson",
Martin Lueker-Bodenc76cbc72020-07-21 21:13:35 +000059 "hw/ip/csrng/data/csrng.hjson",
Timothy Chen11017d62021-03-10 15:07:53 -080060 "hw/ip/dcd/data/dcd.hjson",
Martin Lueker-Boden94bfa222020-09-25 12:27:31 -070061 "hw/ip/edn/data/edn.hjson",
Garret Kelly9eebde02019-10-22 15:36:49 -040062 "hw/ip/flash_ctrl/data/flash_ctrl.hjson",
63 "hw/ip/gpio/data/gpio.hjson",
64 "hw/ip/hmac/data/hmac.hjson",
65 "hw/ip/i2c/data/i2c.hjson",
Timothy Chenc2e3f052020-09-10 18:47:25 -070066 "hw/ip/keymgr/data/keymgr.hjson",
Eunchan Kim4096f7d2020-10-20 17:55:47 -070067 "hw/ip/kmac/data/kmac.hjson",
Michael Schaffnere81ab752020-09-30 18:57:29 -070068 "hw/ip/lc_ctrl/data/lc_ctrl.hjson",
Michael Schaffner1795f4d2019-10-29 10:59:12 -070069 "hw/ip/nmi_gen/data/nmi_gen.hjson",
Philipp Wagner05a5f142020-06-03 23:42:20 +010070 "hw/ip/otbn/data/otbn.hjson",
Michael Schaffner41319c22020-04-12 15:40:24 -070071 "hw/ip/otp_ctrl/data/otp_ctrl.hjson",
Martin Lueker-Boden4d4acea2020-08-22 15:23:43 -070072 "hw/ip/pattgen/data/pattgen.hjson",
Martin Lueker-Boden5dd1bfb2020-11-15 21:18:59 -080073 "hw/ip/pwm/data/pwm.hjson",
Sam Elliottd834c702020-02-12 11:18:47 +000074 "hw/top_earlgrey/ip/pinmux/data/autogen/pinmux.hjson",
Timothy Chen375be342020-11-02 12:55:09 -080075 "hw/top_earlgrey/ip/clkmgr/data/autogen/clkmgr.hjson",
Timothy Chend7d6e0f2020-08-11 18:59:50 -070076 "hw/top_earlgrey/ip/pwrmgr/data/autogen/pwrmgr.hjson",
Timothy Chen1e68dd82020-09-22 16:20:17 -070077 "hw/top_earlgrey/ip/rstmgr/data/autogen/rstmgr.hjson",
78 "hw/top_earlgrey/ip/rv_plic/data/autogen/rv_plic.hjson",
Garret Kelly9eebde02019-10-22 15:36:49 -040079 "hw/ip/rv_timer/data/rv_timer.hjson",
Martin Lueker-Bodeneb9498c2021-02-02 08:33:29 -080080 "hw/ip/spi_host/data/spi_host.hjson",
Garret Kelly9eebde02019-10-22 15:36:49 -040081 "hw/ip/spi_device/data/spi_device.hjson",
Michael Schaffner86c0e322020-10-07 19:56:32 -070082 "hw/ip/sram_ctrl/data/sram_ctrl.hjson",
Garret Kelly9eebde02019-10-22 15:36:49 -040083 "hw/ip/uart/data/uart.hjson",
84 "hw/ip/usbdev/data/usbdev.hjson",
85 "hw/ip/usbuart/data/usbuart.hjson",
86 ],
87
88 # Pre-generate dashboard fragments from these directories.
Philipp Wagner8171ede2021-03-15 21:47:01 +000089 "dashboard_definitions": {
90 "comportable": [
91 "hw/ip",
92 ],
93 },
Garret Kelly9eebde02019-10-22 15:36:49 -040094
95 # Pre-generate testplan fragments from these files.
96 "testplan_definitions": [
Rasmus Madsen206784b2019-11-13 14:55:04 -080097 "hw/ip/aes/data/aes_testplan.hjson",
Cindy Chen70f9ca52019-12-02 16:32:58 -080098 "hw/ip/alert_handler/data/alert_handler_testplan.hjson",
Srikrishna Iyer1abc7b92021-03-08 21:46:49 -080099 "hw/ip/aon_timer/data/aon_timer_testplan.hjson",
Steve Nelsond677d782020-04-30 12:35:44 -0700100 "hw/ip/entropy_src/data/entropy_src_testplan.hjson",
Steve Nelson8cc9fc62020-12-08 08:00:45 -0800101 "hw/ip/csrng/data/csrng_testplan.hjson",
Timothy Chen11017d62021-03-10 15:07:53 -0800102 "hw/ip/dcd/data/dcd_testplan.hjson",
Steve Nelson8adade02020-12-17 13:40:35 -0800103 "hw/ip/edn/data/edn_testplan.hjson",
Srikrishna Iyerfb037392020-07-20 17:11:08 -0700104 "hw/ip/flash_ctrl/data/flash_ctrl_testplan.hjson",
Garret Kelly9eebde02019-10-22 15:36:49 -0400105 "hw/ip/gpio/data/gpio_testplan.hjson",
106 "hw/ip/hmac/data/hmac_testplan.hjson",
107 "hw/ip/i2c/data/i2c_testplan.hjson",
Timothy Chenc2e3f052020-09-10 18:47:25 -0700108 "hw/ip/keymgr/data/keymgr_testplan.hjson",
Cindy Chenb30114e2020-11-25 14:41:12 -0800109 "hw/ip/lc_ctrl/data/lc_ctrl_testplan.hjson",
Rupert Swarbrick7b247662021-03-19 13:15:28 +0000110 "hw/ip/otbn/data/otbn_testplan.hjson",
Cindy Chenda493592020-11-25 10:42:37 -0800111 "hw/ip/otp_ctrl/data/otp_ctrl_testplan.hjson",
Martin Lueker-Boden4d4acea2020-08-22 15:23:43 -0700112 "hw/ip/pattgen/data/pattgen_testplan.hjson",
Hoang Tung0e1e8aa2021-03-14 23:41:18 -0700113 "hw/ip/pwm/data/pwm_testplan.hjson",
Cindy Chendf135f02020-04-03 17:20:15 -0700114 "hw/ip/pinmux/data/pinmux_fpv_testplan.hjson",
Cindy Chen9be86fe2019-11-02 11:42:18 -0700115 "hw/ip/rv_plic/data/rv_plic_fpv_testplan.hjson",
Garret Kelly9eebde02019-10-22 15:36:49 -0400116 "hw/ip/rv_timer/data/rv_timer_testplan.hjson",
Weicai Yangdda59482019-11-12 16:42:18 -0800117 "hw/ip/spi_device/data/spi_device_testplan.hjson",
Srikrishna Iyere09bafc2021-03-08 21:56:25 -0800118 "hw/ip/sram_ctrl/data/sram_ctrl_base_testplan.hjson",
Garret Kelly9eebde02019-10-22 15:36:49 -0400119 "hw/ip/uart/data/uart_testplan.hjson",
Srikrishna Iyer627f7822020-01-15 21:39:29 -0800120 "hw/ip/usbdev/data/usbdev_testplan.hjson",
Weicai Yang76c765e2019-11-01 15:09:02 -0700121 "hw/ip/tlul/data/tlul_testplan.hjson",
Srikrishna Iyer5cddaf82020-04-24 14:19:49 -0700122 "hw/top_earlgrey/data/chip_testplan.hjson",
Timothy Chenf2d77262019-11-01 17:17:20 -0700123 "hw/top_earlgrey/data/standalone_sw_testplan.hjson",
Udi Jonnalagaddadf49fb82020-03-17 11:05:17 -0700124 "util/dvsim/testplanner/examples/foo_testplan.hjson",
Garret Kelly9eebde02019-10-22 15:36:49 -0400125 ],
lowRISC Contributors802543a2019-08-31 12:12:56 +0100126
Eunchan Kim9790e3e2019-11-01 15:17:32 -0700127 # Pre-generated utility selfdoc
128 "selfdoc_tools": ["tlgen", "reggen"],
129
Sam Elliottb6745d32020-04-08 21:46:28 +0100130 # DIF Docs
131 "difs-directory": "sw/device/lib/dif",
132
lowRISC Contributors802543a2019-08-31 12:12:56 +0100133 # Output directory for documents
Garret Kelly9eebde02019-10-22 15:36:49 -0400134 "outdir":
135 SRCTREE_TOP.joinpath('build', 'docs'),
136 "outdir-generated":
137 SRCTREE_TOP.joinpath('build', 'docs-generated'),
138 "verbose":
139 False,
lowRISC Contributors802543a2019-08-31 12:12:56 +0100140}
141
142
Garret Kelly9eebde02019-10-22 15:36:49 -0400143def generate_dashboards():
Philipp Wagner8171ede2021-03-15 21:47:01 +0000144 for dashboard_name, dirs in config["dashboard_definitions"].items():
Garret Kelly9eebde02019-10-22 15:36:49 -0400145 hjson_paths = []
Philipp Wagner8171ede2021-03-15 21:47:01 +0000146 for d in dirs:
147 hjson_paths += SRCTREE_TOP.joinpath(d).rglob('*.prj.hjson')
148
149 hjson_paths.sort(key=lambda f: f.name)
Garret Kelly9eebde02019-10-22 15:36:49 -0400150
151 dashboard_path = config["outdir-generated"].joinpath(
Philipp Wagner8171ede2021-03-15 21:47:01 +0000152 dashboard_name, 'dashboard')
153 dashboard_path.parent.mkdir(exist_ok=True, parents=True)
Philipp Wagner57df62c2019-11-04 14:49:24 +0000154 dashboard_html = open(str(dashboard_path), mode='w')
Garret Kelly9eebde02019-10-22 15:36:49 -0400155 for hjson_path in hjson_paths:
156 gen_dashboard_entry.gen_dashboard_html(hjson_path, dashboard_html)
157 dashboard_html.close()
lowRISC Contributors802543a2019-08-31 12:12:56 +0100158
159
Garret Kelly9eebde02019-10-22 15:36:49 -0400160def generate_hardware_blocks():
161 for hardware in config["hardware_definitions"]:
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +0000162 regs = IpBlock.from_path(str(SRCTREE_TOP.joinpath(hardware)), [])
Garret Kelly9eebde02019-10-22 15:36:49 -0400163
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +0000164 hw_path = config["outdir-generated"].joinpath(hardware)
165 dst_path = hw_path.parent
166 dst_path.mkdir(parents=True, exist_ok=True)
Garret Kelly9eebde02019-10-22 15:36:49 -0400167
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +0000168 regs_path = dst_path.joinpath(hw_path.name + '.registers')
169 with open(regs_path, 'w') as regs_file:
170 gen_html.gen_html(regs, regs_file)
Garret Kelly9eebde02019-10-22 15:36:49 -0400171
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +0000172 hwcfg_path = dst_path.joinpath(hw_path.name + '.hwcfg')
173 with open(hwcfg_path, 'w') as hwcfg_file:
174 gen_cfg_html.gen_cfg_html(regs, hwcfg_file)
lowRISC Contributors802543a2019-08-31 12:12:56 +0100175
176
Garret Kelly9eebde02019-10-22 15:36:49 -0400177def generate_testplans():
178 for testplan in config["testplan_definitions"]:
179 plan = testplan_utils.parse_testplan(SRCTREE_TOP.joinpath(testplan))
180
181 plan_path = config["outdir-generated"].joinpath(testplan + '.testplan')
182 plan_path.parent.mkdir(parents=True, exist_ok=True)
183
Philipp Wagner57df62c2019-11-04 14:49:24 +0000184 testplan_html = open(str(plan_path), mode='w')
Garret Kelly9eebde02019-10-22 15:36:49 -0400185 testplan_utils.gen_html_testplan_table(plan, testplan_html)
186 testplan_html.close()
lowRISC Contributors802543a2019-08-31 12:12:56 +0100187
188
Eunchan Kim9790e3e2019-11-01 15:17:32 -0700189def generate_selfdocs():
190 """Generate documents for the tools in `util/` if `--doc` option exists.
191
192 Each tool creates selfdoc differently. Manually invoked.
193 """
194 for tool in config["selfdoc_tools"]:
195 selfdoc_path = config["outdir-generated"].joinpath(tool + '.selfdoc')
196 selfdoc_path.parent.mkdir(parents=True, exist_ok=True)
Philipp Wagner57df62c2019-11-04 14:49:24 +0000197 with open(str(selfdoc_path), mode='w') as fout:
Eunchan Kim9790e3e2019-11-01 15:17:32 -0700198 if tool == "reggen":
199 reggen_selfdoc.document(fout)
200 elif tool == "tlgen":
201 fout.write(tlgen.selfdoc(heading=3, cmd='tlgen.py --doc'))
202
Tobias Wölfelff543342020-05-14 16:14:56 +0200203
Pirmin Vogel3ff52be2021-01-20 18:20:25 +0100204def generate_pkg_reqs():
205 """Generate an apt/yum command line invocation from
206 apt/yum-requirements.txt
Sam Elliott692078c2020-02-07 17:23:21 +0000207
Pirmin Vogel3ff52be2021-01-20 18:20:25 +0100208 This will be saved in outdir-generated/apt_cmd.txt and
209 outdir-generated/yum_cmd.txt, respectively.
Sam Elliott692078c2020-02-07 17:23:21 +0000210 """
Sam Elliott692078c2020-02-07 17:23:21 +0000211
Pirmin Vogel3ff52be2021-01-20 18:20:25 +0100212 for pkgmgr in ["apt", "yum"]:
213 # Read the apt/yum-requirements.txt
214 requirements = []
215 requirements_file = open(str(SRCTREE_TOP.joinpath(pkgmgr + "-requirements.txt")))
216 for package_line in requirements_file.readlines():
217 # Ignore everything after `#` on each line, and strip whitespace
218 package = package_line.split('#', 1)[0].strip()
219 if package:
220 # only add non-empty lines to packages
221 requirements.append(package)
222
223 cmd = "$ sudo " + pkgmgr + " install " + " ".join(requirements)
224 cmd_lines = textwrap.wrap(cmd,
Sam Elliott692078c2020-02-07 17:23:21 +0000225 width=78,
226 replace_whitespace=True,
227 subsequent_indent=' ')
Pirmin Vogel3ff52be2021-01-20 18:20:25 +0100228 # Newlines need to be escaped
229 cmd = " \\\n".join(cmd_lines)
Sam Elliott692078c2020-02-07 17:23:21 +0000230
Pirmin Vogel3ff52be2021-01-20 18:20:25 +0100231 # And then to write the generated string directly to the file.
232 cmd_path = config["outdir-generated"].joinpath(pkgmgr + '_cmd.txt')
233 cmd_path.parent.mkdir(parents=True, exist_ok=True)
234 with open(str(cmd_path), mode='w') as fout:
235 fout.write(cmd)
Sam Elliott692078c2020-02-07 17:23:21 +0000236
Tobias Wölfelff543342020-05-14 16:14:56 +0200237
Pirmin Vogel0761f1c2020-03-09 17:47:45 +0100238def generate_tool_versions():
239 """Generate an tool version number requirement from tool_requirements.py
240
241 The version number per tool will be saved in outdir-generated/version_$TOOL_NAME.txt
242 """
243
Pirmin Vogel0761f1c2020-03-09 17:47:45 +0100244 # And then write a version file for every tool.
Rupert Swarbrick9ab407e2021-01-25 11:40:17 +0000245 for tool, req in TOOL_REQUIREMENTS.items():
Pirmin Vogel0761f1c2020-03-09 17:47:45 +0100246 version_path = config["outdir-generated"].joinpath('version_' + tool + '.txt')
247 version_path.parent.mkdir(parents=True, exist_ok=True)
248 with open(str(version_path), mode='w') as fout:
Rupert Swarbrick9ab407e2021-01-25 11:40:17 +0000249 fout.write(req.min_version)
Pirmin Vogel0761f1c2020-03-09 17:47:45 +0100250
Eunchan Kim4096f7d2020-10-20 17:55:47 -0700251
Sam Elliottb6745d32020-04-08 21:46:28 +0100252def generate_dif_docs():
253 """Generate doxygen documentation and DIF listings from DIF source comments.
254
255 This invokes Doxygen, and a few other things. Be careful of changing any
256 paths here, some correspond to paths in other configuration files.
257 """
258
259 logging.info("Generating Software API Documentation (Doxygen)...")
260
261 doxygen_out_path = config["outdir-generated"].joinpath("sw")
262
263 # The next two paths correspond to relative paths specified in the Doxyfile
264 doxygen_xml_path = doxygen_out_path.joinpath("api-xml")
265
266 # We need to prepare this path because doxygen won't `mkdir -p`
267 doxygen_sw_path = doxygen_out_path.joinpath("public-api/sw/apis")
268 doxygen_sw_path.mkdir(parents=True, exist_ok=True)
269
Sam Elliottc8fa5582020-04-08 21:02:05 +0100270 # This is where warnings will be generated
271 doxygen_warnings_path = doxygen_out_path.joinpath("doxygen_warnings.log")
272 if doxygen_warnings_path.exists():
273 doxygen_warnings_path.unlink()
274
Sam Elliottb6745d32020-04-08 21:46:28 +0100275 doxygen_args = [
276 "doxygen",
277 str(SRCTREE_TOP.joinpath("util/doxygen/Doxyfile")),
278 ]
279
Eunchan Kim4096f7d2020-10-20 17:55:47 -0700280 doxygen_results = subprocess.run( # noqa: F841
281 doxygen_args, check=True,
Sam Elliottb6745d32020-04-08 21:46:28 +0100282 cwd=str(SRCTREE_TOP), stdout=subprocess.PIPE,
Eunchan Kim4096f7d2020-10-20 17:55:47 -0700283 env=dict(
284 os.environ,
Sam Elliottb6745d32020-04-08 21:46:28 +0100285 SRCTREE_TOP=str(SRCTREE_TOP),
286 DOXYGEN_OUT=str(doxygen_out_path),
287 ))
288
289 logging.info("Generated Software API Documentation (Doxygen)")
290
Sam Elliottc8fa5582020-04-08 21:02:05 +0100291 if doxygen_warnings_path.exists():
Eunchan Kim4096f7d2020-10-20 17:55:47 -0700292 logging.warning("Doxygen Generated Warnings "
293 "(saved in {})".format(str(doxygen_warnings_path)))
Sam Elliottc8fa5582020-04-08 21:02:05 +0100294
Sam Elliottb6745d32020-04-08 21:46:28 +0100295 combined_xml = gen_dif_listing.get_combined_xml(doxygen_xml_path)
296
297 dif_paths = []
298 dif_paths.extend(sorted(SRCTREE_TOP.joinpath(config["difs-directory"]).glob("dif_*.h")))
299
300 dif_listings_root_path = config["outdir-generated"].joinpath("sw/difs_listings")
301 difrefs_root_path = config["outdir-generated"].joinpath("sw/difref")
302
303 for dif_header_path in dif_paths:
304 dif_header = str(dif_header_path.relative_to(SRCTREE_TOP))
305
306 dif_listings_filename = dif_listings_root_path.joinpath(dif_header + ".html")
307 dif_listings_filename.parent.mkdir(parents=True, exist_ok=True)
308
Sam Elliott24398ab2020-07-17 17:02:05 +0100309 with open(str(dif_listings_filename), mode='w') as dif_listings_html:
310 gen_dif_listing.gen_listing_html(combined_xml, dif_header,
311 dif_listings_html)
Sam Elliottb6745d32020-04-08 21:46:28 +0100312
313 difref_functions = gen_dif_listing.get_difref_info(combined_xml, dif_header)
314 for function in difref_functions:
315 difref_filename = difrefs_root_path.joinpath(function["name"] + '.html')
316 difref_filename.parent.mkdir(parents=True, exist_ok=True)
317
Sam Elliott24398ab2020-07-17 17:02:05 +0100318 with open(str(difref_filename), mode='w') as difref_html:
319 gen_dif_listing.gen_difref_html(function, difref_html)
Sam Elliottb6745d32020-04-08 21:46:28 +0100320
321 logging.info("Generated DIF Listing for {}".format(dif_header))
322
Eunchan Kim9790e3e2019-11-01 15:17:32 -0700323
Rupert Swarbrick63da48e2020-07-08 10:16:57 +0100324def generate_otbn_isa():
325 '''Generate the OTBN ISA documentation fragment
326
327 The result is in Markdown format and is written to
328 outdir-generated/otbn-isa.md
329
330 '''
331 otbn_dir = SRCTREE_TOP / 'hw/ip/otbn'
332 script = otbn_dir / 'util/yaml_to_doc.py'
333 yaml_file = otbn_dir / 'data/insns.yml'
Rupert Swarbrick4077bec2020-10-05 11:50:11 +0100334 impl_file = otbn_dir / 'dv/otbnsim/sim/insn.py'
Rupert Swarbrick63da48e2020-07-08 10:16:57 +0100335
Rupert Swarbrickd3de4bb2020-09-01 17:50:19 +0100336 out_dir = config['outdir-generated'].joinpath('otbn-isa')
Rupert Swarbrick4077bec2020-10-05 11:50:11 +0100337 subprocess.run([str(script), str(yaml_file), str(impl_file), str(out_dir)],
338 check=True)
Rupert Swarbrick63da48e2020-07-08 10:16:57 +0100339
340
Tobias Wölfel19a86e22020-05-14 14:31:00 +0200341def hugo_match_version(hugo_bin_path, version):
342 logging.info("Hugo binary path: %s", hugo_bin_path)
343 args = [str(hugo_bin_path), "version"]
Silvestrs Timofejevsecaf87c2019-11-12 21:52:57 +0000344 process = subprocess.run(args,
345 universal_newlines=True,
346 stdout=subprocess.PIPE,
347 check=True,
348 cwd=str(SRCTREE_TOP))
349
Tobias Wölfel19a86e22020-05-14 14:31:00 +0200350 logging.info("Checking for correct Hugo version: %s", version)
Silvestrs Timofejevsecaf87c2019-11-12 21:52:57 +0000351 # Hugo version string example:
Tobias Wölfelff543342020-05-14 16:14:56 +0200352 # "Hugo Static Site Generator v0.59.0-1DD0C69C/extended linux/amd64 BuildDate: 2019-10-21T09:45:38Z" # noqa: E501
353 return bool(re.search("v" + version + ".*/extended", process.stdout))
Silvestrs Timofejevsecaf87c2019-11-12 21:52:57 +0000354
355
Philipp Wagnercd57f862019-10-31 14:20:39 +0000356def install_hugo(install_dir):
357 """Download and "install" hugo into |install_dir|
358
359 install_dir is created if it doesn't exist yet.
360
361 Limitations:
Sam Elliott1087d272020-11-17 18:56:19 +0000362 Currently only 64-bit x86 Linux and macOS is supported."""
Philipp Wagnercd57f862019-10-31 14:20:39 +0000363
364 # TODO: Support more configurations
Sam Elliott1087d272020-11-17 18:56:19 +0000365 if platform.system() == 'Linux' and platform.machine() == 'x86_64':
366 download_url = ('https://github.com/gohugoio/hugo/releases/download/v{version}'
Timothy Chen7df14342021-03-23 17:05:38 -0700367 '/hugo_extended_{version}_Linux-64bit.tar.gz').format(
368 version=HUGO_EXTENDED_VERSION)
Philipp Wagnercd57f862019-10-31 14:20:39 +0000369
Sam Elliott1087d272020-11-17 18:56:19 +0000370 elif platform.system() == 'Darwin' and platform.machine() == 'x86_64':
Timothy Chen7df14342021-03-23 17:05:38 -0700371 download_url = ('https://github.com/gohugoio/hugo/releases/download/v{version}'
372 '/hugo_extended_{version}_macOS-64bit.tar.gz').format(
373 version=HUGO_EXTENDED_VERSION)
Sam Elliott1087d272020-11-17 18:56:19 +0000374
375 else:
376 logging.fatal(
377 "Auto-install of hugo only supported for 64-bit x86 Linux and "
378 "macOS. Manually install hugo and re-run this script with --force-global.")
379 return False
380
Philipp Wagnercd57f862019-10-31 14:20:39 +0000381 install_dir.mkdir(exist_ok=True, parents=True)
382 hugo_bin_path = install_dir / 'hugo'
383
Tobias Wölfel19a86e22020-05-14 14:31:00 +0200384 try:
385 if hugo_match_version(hugo_bin_path, HUGO_EXTENDED_VERSION):
386 return hugo_bin_path
Tobias Wölfelff543342020-05-14 16:14:56 +0200387 except PermissionError:
Tobias Wölfel19a86e22020-05-14 14:31:00 +0200388 # If there is an error checking the version just continue to download
Tobias Wölfelff543342020-05-14 16:14:56 +0200389 logging.info("Hugo version could not be verified. Continue to download.")
390 except FileNotFoundError:
Tobias Wölfel19a86e22020-05-14 14:31:00 +0200391 pass
392
Philipp Wagnercd57f862019-10-31 14:20:39 +0000393 # TODO: Investigate the use of Python builtins for downloading. Extracting
394 # the archive will probably will be a call to tar.
Sam Elliott1087d272020-11-17 18:56:19 +0000395 cmd = 'curl -sL {download_url} | tar -xzO hugo > {hugo_bin_file}'.format(
Philipp Wagnercd57f862019-10-31 14:20:39 +0000396 hugo_bin_file=str(hugo_bin_path), download_url=download_url)
397 logging.info("Calling %s to download hugo.", cmd)
Philipp Wagner57df62c2019-11-04 14:49:24 +0000398 subprocess.run(cmd, shell=True, check=True, cwd=str(SRCTREE_TOP))
Philipp Wagnercd57f862019-10-31 14:20:39 +0000399 hugo_bin_path.chmod(0o755)
Tobias Wölfel19a86e22020-05-14 14:31:00 +0200400 return hugo_bin_path
Philipp Wagnercd57f862019-10-31 14:20:39 +0000401
402
Tobias Wölfel19a86e22020-05-14 14:31:00 +0200403def invoke_hugo(preview, hugo_bin_path):
Garret Kelly9eebde02019-10-22 15:36:49 -0400404 site_docs = SRCTREE_TOP.joinpath('site', 'docs')
405 config_file = str(site_docs.joinpath('config.toml'))
406 layout_dir = str(site_docs.joinpath('layouts'))
407 args = [
Tobias Wölfel19a86e22020-05-14 14:31:00 +0200408 str(hugo_bin_path),
Garret Kelly9eebde02019-10-22 15:36:49 -0400409 "--config",
410 config_file,
411 "--destination",
412 str(config["outdir"]),
413 "--contentDir",
414 str(SRCTREE_TOP),
415 "--layoutDir",
416 layout_dir,
417 ]
418 if preview:
419 args += ["server"]
Philipp Wagner57df62c2019-11-04 14:49:24 +0000420 subprocess.run(args, check=True, cwd=str(SRCTREE_TOP))
lowRISC Contributors802543a2019-08-31 12:12:56 +0100421
422
423def main():
424 logging.basicConfig(level=logging.INFO,
425 format="%(asctime)s - %(message)s",
426 datefmt="%Y-%m-%d %H:%M")
427
428 parser = argparse.ArgumentParser(
429 prog="build_docs",
430 formatter_class=argparse.RawDescriptionHelpFormatter,
431 usage=USAGE)
432 parser.add_argument(
433 '--preview',
434 action='store_true',
Tobias Wölfelff543342020-05-14 16:14:56 +0200435 help="""Starts a local server with live reload (updates triggered upon
436 changes in the documentation files). This feature is intended
lowRISC Contributors802543a2019-08-31 12:12:56 +0100437 to preview the documentation locally.""")
Tobias Wölfel19a86e22020-05-14 14:31:00 +0200438 parser.add_argument(
439 '--force-global',
440 action='store_true',
441 help="""Use a global installation of Hugo. This skips the version
442 check and relies on Hugo to be available from the environment.""")
Garret Kelly9eebde02019-10-22 15:36:49 -0400443 parser.add_argument('--hugo', help="""TODO""")
lowRISC Contributors802543a2019-08-31 12:12:56 +0100444
445 args = parser.parse_args()
446
Garret Kelly9eebde02019-10-22 15:36:49 -0400447 generate_hardware_blocks()
448 generate_dashboards()
449 generate_testplans()
Eunchan Kim9790e3e2019-11-01 15:17:32 -0700450 generate_selfdocs()
Pirmin Vogel3ff52be2021-01-20 18:20:25 +0100451 generate_pkg_reqs()
Pirmin Vogel0761f1c2020-03-09 17:47:45 +0100452 generate_tool_versions()
Sam Elliottb6745d32020-04-08 21:46:28 +0100453 generate_dif_docs()
Rupert Swarbrick63da48e2020-07-08 10:16:57 +0100454 generate_otbn_isa()
Philipp Wagnercd57f862019-10-31 14:20:39 +0000455
456 hugo_localinstall_dir = SRCTREE_TOP / 'build' / 'docs-hugo'
457 os.environ["PATH"] += os.pathsep + str(hugo_localinstall_dir)
458
Tobias Wölfel19a86e22020-05-14 14:31:00 +0200459 hugo_bin_path = "hugo"
460 if not args.force_global:
461 try:
462 hugo_bin_path = install_hugo(hugo_localinstall_dir)
463 except KeyboardInterrupt:
464 pass
Silvestrs Timofejevsecaf87c2019-11-12 21:52:57 +0000465
Tobias Wölfel19a86e22020-05-14 14:31:00 +0200466 try:
467 invoke_hugo(args.preview, hugo_bin_path)
468 except subprocess.CalledProcessError:
469 sys.exit("Error building site")
470 except PermissionError:
471 sys.exit("Error running Hugo")
Philipp Wagnere08be112019-10-31 14:43:52 +0000472 except KeyboardInterrupt:
473 pass
lowRISC Contributors802543a2019-08-31 12:12:56 +0100474
475
lowRISC Contributors802543a2019-08-31 12:12:56 +0100476if __name__ == "__main__":
477 main()