From b211454ddb4b0d6f8b145da94924c2dafa30d35e Mon Sep 17 00:00:00 2001 From: Piotr Zierhoffer Date: Tue, 25 Aug 2020 13:44:24 +0200 Subject: [PATCH 1/2] docs: Add generator for cell index This addresses #86 Signed-off-by: Piotr Zierhoffer --- docs/conf.py | 1 + scripts/python-skywater-pdk/README.rst | 2 +- .../skywater_pdk/cell_list.py | 166 ++++++++++++++++++ 3 files changed, 168 insertions(+), 1 deletion(-) create mode 100644 scripts/python-skywater-pdk/skywater_pdk/cell_list.py diff --git a/docs/conf.py b/docs/conf.py index 36ba1bb..45a5903 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -127,6 +127,7 @@ exclude_patterns = [ 'rules/summary/*-key.rst', 'rules/layers/*-key.rst', 'rules/hv/*-key.rst', + '**/cell-list.rst', ] # The name of the Pygments (syntax highlighting) style to use. diff --git a/scripts/python-skywater-pdk/README.rst b/scripts/python-skywater-pdk/README.rst index 970dc60..4303d92 100644 --- a/scripts/python-skywater-pdk/README.rst +++ b/scripts/python-skywater-pdk/README.rst @@ -7,7 +7,7 @@ the SkyWater PDK. It includes tools for decoding things like file names into human readable descriptions. -It also includes tools for combining files together. +It also includes tools for combining files together and generating list of cells in libraries for the documentation. License ======= diff --git a/scripts/python-skywater-pdk/skywater_pdk/cell_list.py b/scripts/python-skywater-pdk/skywater_pdk/cell_list.py new file mode 100644 index 0000000..d82f5ec --- /dev/null +++ b/scripts/python-skywater-pdk/skywater_pdk/cell_list.py @@ -0,0 +1,166 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright 2020 SkyWater PDK Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +import argparse +import json +import os +import pathlib +import pprint +import sys +import textwrap + +from typing import Tuple, List, Dict + +# using a list-table here to allow for easier line breaks in description +rst_header_line_char = '-' +rst_header = 'List of cells in :lib:`{libname}`' +rst_template ="""\ +{header_line} +{header_underline} + +.. list-table:: + :header-rows: 1 + + * - Cell name + - Description + - Type + - Verilog name +{cell_list} +""" + + +cell_template = """\ + * - {cell_name} + - {description} + - {type} + - {verilog_name} +""" + + +def collect(library_dir) -> Tuple[str, List[str]]: + """Collect the available definitions for cells in a library + + Parameters + ---------- + library_dir: str or pathlib.Path + Path to a library. + + Returns + ------- + lib : str + Library name + + cells : list of pathlib.Path + definition files for cells in the library. + """ + + if not isinstance(library_dir, pathlib.Path): + library_dir = pathlib.Path(library_dir) + + libname = None + cells = set() + + for p in library_dir.rglob("definition.json"): + if not p.is_file(): + continue + cells.add(p) + if libname is None: + with open(str(p), "r") as sample_json: + sample_def = json.load(sample_json) + libname = sample_def['library'] + + assert len(libname) > 0 + cells = list(sorted(cells)) + return libname, cells + + +def generate_rst(library_dir, library_name, cells): + """Generate the RST file containing basic information about cells + + Parameters + ---------- + library_dir: str or pathlib.Path + Path to a library. + + library_name: str + Name of the library + + cells: list of pathlib.Path + List of paths to JSON description files + + Returns + ------- + path: str + Path to generated file + """ + + if not isinstance(library_dir, pathlib.Path): + library_dir = pathlib.Path(library_dir) + + file_name = "cell-list.rst" + cell_list_file = pathlib.Path(library_dir, file_name) + cell_list = "" + + for cell in cells: + with open(str(cell), "r") as c: + cell_json = json.load(c) + cell_list = cell_list + cell_template.format( + cell_name = cell_json['name'], + #description = cell_json['description'].replace("\n", "\n "), + description = textwrap.indent(cell_json['description'], ' ').lstrip(), + type = cell_json['type'], + verilog_name = cell_json['verilog_name'] + ) + + header = rst_header.format(libname = library_name) + try: + with(open(str(cell_list_file), "w")) as c: + c.write(rst_template.format( + header_line = header, + header_underline = rst_header_line_char * len(header), + cell_list = cell_list + )) + except FileNotFoundError: + print(f"ERROR: Failed to create {str(cell_list_file)}", file=sys.stderr) + raise + + return cell_list_file + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument( + "library_dir", + help="Path to the library.", + type=pathlib.Path, + nargs=1) + + args = parser.parse_args() + lib = args.library_dir[0] + + print(f"Analysing {lib}") + libname, cells = collect(lib) + print(f"Library name: {libname}, found {len(cells)} cells") + file = generate_rst(lib, libname, cells) + print(f'Generated {file}') + + +if __name__ == "__main__": + sys.exit(main()) + From 8e9a17a8659352ce5fdee81a89d704a8fc8cb76e Mon Sep 17 00:00:00 2001 From: Wojciech Gryncewicz Date: Wed, 4 Nov 2020 19:49:07 +0100 Subject: [PATCH 2/2] cell_index wrapped as Sphinx extension --- .../skywater_pdk => docs/_ext}/cell_list.py | 64 ++++++++++++++----- docs/conf.py | 4 +- 2 files changed, 52 insertions(+), 16 deletions(-) rename {scripts/python-skywater-pdk/skywater_pdk => docs/_ext}/cell_list.py (72%) diff --git a/scripts/python-skywater-pdk/skywater_pdk/cell_list.py b/docs/_ext/cell_list.py similarity index 72% rename from scripts/python-skywater-pdk/skywater_pdk/cell_list.py rename to docs/_ext/cell_list.py index d82f5ec..e3e16ac 100644 --- a/scripts/python-skywater-pdk/skywater_pdk/cell_list.py +++ b/docs/_ext/cell_list.py @@ -24,6 +24,10 @@ import pathlib import pprint import sys import textwrap +from docutils import nodes +from docutils.parsers.rst import Directive +from docutils.statemachine import ViewList +from sphinx.util.nodes import nested_parse_with_titles from typing import Tuple, List, Dict @@ -91,7 +95,7 @@ def collect(library_dir) -> Tuple[str, List[str]]: def generate_rst(library_dir, library_name, cells): - """Generate the RST file containing basic information about cells + """Generate the RST paragraph containing basic information about cells Parameters ---------- @@ -106,15 +110,14 @@ def generate_rst(library_dir, library_name, cells): Returns ------- - path: str - Path to generated file + paragraph: str + Generated paragraph """ if not isinstance(library_dir, pathlib.Path): library_dir = pathlib.Path(library_dir) - file_name = "cell-list.rst" - cell_list_file = pathlib.Path(library_dir, file_name) + paragraph = "" cell_list = "" for cell in cells: @@ -129,19 +132,42 @@ def generate_rst(library_dir, library_name, cells): ) header = rst_header.format(libname = library_name) - try: - with(open(str(cell_list_file), "w")) as c: - c.write(rst_template.format( + paragraph = rst_template.format( header_line = header, header_underline = rst_header_line_char * len(header), cell_list = cell_list - )) - except FileNotFoundError: - print(f"ERROR: Failed to create {str(cell_list_file)}", file=sys.stderr) - raise + ) + return paragraph - return cell_list_file +# --- Sphinx extension wrapper --- +class CellList(Directive): + + def run(self): + env = self.state.document.settings.env + dirname = env.docname.rpartition('/')[0] + libname, cells = collect(dirname) + paragraph = generate_rst(dirname, libname, cells) + # parse rst string to docutils nodes + rst = ViewList() + for i,line in enumerate(paragraph.split('\n')): + rst.append(line, libname+"-cell-list.rst", i+1) + node = nodes.section() + node.document = self.state.document + nested_parse_with_titles(self.state, rst, node) + return node.children + + +def setup(app): + app.add_directive("cell_list", CellList) + + return { + 'version': '0.1', + 'parallel_read_safe': True, + 'parallel_write_safe': True, + } + +# --- stand alone, command line operation --- def main(): parser = argparse.ArgumentParser() @@ -157,8 +183,16 @@ def main(): print(f"Analysing {lib}") libname, cells = collect(lib) print(f"Library name: {libname}, found {len(cells)} cells") - file = generate_rst(lib, libname, cells) - print(f'Generated {file}') + paragraph = generate_rst(lib, libname, cells) + library_dir = pathlib.Path(lib) + cell_list_file = pathlib.Path(library_dir, "cell-list.rst") + try: + with(open(str(cell_list_file), "w")) as c: + c.write(paragraph) + print(f'Generated {cell_list_file}') + except FileNotFoundError: + print(f"ERROR: Failed to create {str(cell_list_file)}", file=sys.stderr) + raise if __name__ == "__main__": diff --git a/docs/conf.py b/docs/conf.py index 45a5903..5bb3a33 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -31,8 +31,9 @@ import docutils import os import re -# import sys +import sys # sys.path.insert(0, os.path.abspath('.')) +sys.path.insert(0, os.path.abspath('./_ext')) # -- Project information ----------------------------------------------------- @@ -65,6 +66,7 @@ extensions = [ 'sphinx.ext.napoleon', 'sphinx.ext.todo', 'sphinxcontrib_hdl_diagrams', + 'cell_list', ] # Add any paths that contain templates here, relative to this directory.