From: Vladimír Vondruš Date: Sun, 15 Sep 2024 18:06:52 +0000 (+0200) Subject: documentation/doxygen: cleanup, test, support overriding the file too. X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~cjwatson/git?a=commitdiff_plain;h=d3e6203dad8a5c6a87554f504c924ef36aa80ba6;p=blog.git documentation/doxygen: cleanup, test, support overriding the file too. Not just the header name, but also where the link points to. None of this is possible for namespaces, functions or other symbols, which is a bit sad, but that's Doxygen we're dealing with here. --- diff --git a/documentation/doxygen.py b/documentation/doxygen.py index f3d2e94d..638ce1b1 100755 --- a/documentation/doxygen.py +++ b/documentation/doxygen.py @@ -271,12 +271,20 @@ def make_include_strip_from_path(path: str, prefixes: List[str]) -> str: strip_candidates = list(filter(bool, map(lambda x: remove_path_prefix(path, x), prefixes))) return min(strip_candidates, key=len) if strip_candidates else path -def make_include(state: State, file, include_str=None) -> Tuple[str, str]: - if include_str is None: - include_str = make_include_strip_from_path(file, state.doxyfile['STRIP_FROM_INC_PATH']) if state.doxyfile['STRIP_FROM_INC_PATH'] is not None else file - +def make_include(state: State, file) -> Tuple[str, str]: if file in state.includes and state.compounds[state.includes[file]].has_details: - return (html.escape('<{}>'.format(include_str)), state.compounds[state.includes[file]].url) + return (html.escape('<{}>'.format(make_include_strip_from_path(file, state.doxyfile['STRIP_FROM_INC_PATH']) if state.doxyfile['STRIP_FROM_INC_PATH'] is not None else file)), state.compounds[state.includes[file]].url) + return None + +# Used only from a single place but put here to ensure it's kept consistent +# with make_include() above, in particular the checks +def make_class_include(state: State, file_id, name) -> Tuple[str, str]: + # state.includes is a map from a filename to file ID, and since we already + # have a file ID we don't need to use it. But if it's empty, it means + # includes are disabled globally, in which case we shouldn't return + # anything. + if state.includes and state.compounds[file_id].has_details: + return (html.escape('<{}>'.format(name)), state.compounds[file_id].url) return None def parse_id_and_include(state: State, element: ET.Element) -> Tuple[str, str, str, Tuple[str, str], bool]: @@ -2870,19 +2878,33 @@ def parse_xml(state: State, xml: str): compound.is_final = compounddef.attrib.get('final') == 'yes' # Decide about the include file for this compound. Classes get it always, - # namespaces without any members too. + # namespaces without any class / group members too. state.current_kind = compound.kind if compound.kind in ['struct', 'class', 'union'] or (compound.kind == 'namespace' and compounddef.find('innerclass') is None and compounddef.find('innernamespace') is None and compounddef.find('sectiondef') is None): location_attribs = compounddef.find('location').attrib file = location_attribs['declfile'] if 'declfile' in location_attribs else location_attribs['file'] - include_str = compounddef.find('includes').text if compounddef.find('includes') is not None else file - compound.include = make_include(state, file, include_str) + + # Classes, structs and unions allow supplying custom header file and a + # custom include name, which *seems* to be present in the first + # element of given compound. If that's the case, we use the + # provided ID as the include link target and the name as the include + # name. Otherwise the information is extracted from the tag. + # See test_compound.IncludesStripFromPath for a test case. + compound_includes = compounddef.find('includes') + if compound.kind in ['struct', 'class', 'union'] and compound_includes is not None: + compound.include = make_class_include(state, compound_includes.attrib['refid'], compound_includes.text) + else: + compound.include = make_include(state, file) # Save include for current compound. Every enum/var/function/... parser # checks against it and resets to None in case the include differs for # given entry, meaning all entries need to have their own include # definition instead. That's then finally reflected in has_details of # each entry. + # + # If the class overrode the include location to something else above, + # we *still* use the actual file from , as otherwise an + # include would get listed redundantly for all class members. state.current_include = file # Namespaces with members get a placeholder that gets filled from the diff --git a/documentation/test_doxygen/compound_includes_strip_from_path/Data_8h.html b/documentation/test_doxygen/compound_includes_strip_from_path/Data_8h.html new file mode 100644 index 00000000..f294c287 --- /dev/null +++ b/documentation/test_doxygen/compound_includes_strip_from_path/Data_8h.html @@ -0,0 +1,79 @@ + + + + + include/Library/Data.h file | My Project + + + + + +
+
+
+
+
+

+ include/Library/Data.h file +

+

A library data header.

+ +

Should be shown as include/Library/Data.h in the page and also in the file tree, neither of them prefixed with project/. Its contents should then show #include <Library/Data.h>, without the project/include/ prefix.

+
+

Namespaces

+
+
namespace Library
+
A library namespace.
+
namespace Library::Helper
+
A helper subnamespace.
+
+
+
+

Classes

+
+
+ struct Library::Struct +
+
A library structure.
+
+ class Library::Class +
+
A class with overriden header file and name.
+
+
+
+
+
+
+ + diff --git a/documentation/test_doxygen/compound_includes_strip_from_path/Doxyfile b/documentation/test_doxygen/compound_includes_strip_from_path/Doxyfile new file mode 100644 index 00000000..34154d6a --- /dev/null +++ b/documentation/test_doxygen/compound_includes_strip_from_path/Doxyfile @@ -0,0 +1,20 @@ +INPUT = input.dox project/examples/example.cpp project/include/Library/Library.h project/include/Library/Data.h +AUTOLINK_SUPPORT = NO +QUIET = YES +GENERATE_HTML = NO +GENERATE_LATEX = NO +GENERATE_XML = YES +XML_PROGRAMLISTING = NO +CASE_SENSE_NAMES = YES + +# These are what makes the test actually work. The first entries don't exist +# and should be ignored. +STRIP_FROM_PATH = nonexistent/path/ project/ project/examples/ +STRIP_FROM_INC_PATH = examples/nonexistentpath/ project/include + +##! M_PAGE_FINE_PRINT = +##! M_THEME_COLOR = +##! M_FAVICON = +##! M_LINKS_NAVBAR1 = +##! M_LINKS_NAVBAR2 = files +##! M_SEARCH_DISABLED = YES diff --git a/documentation/test_doxygen/compound_includes_strip_from_path/Library_8h.html b/documentation/test_doxygen/compound_includes_strip_from_path/Library_8h.html new file mode 100644 index 00000000..452ad8d6 --- /dev/null +++ b/documentation/test_doxygen/compound_includes_strip_from_path/Library_8h.html @@ -0,0 +1,63 @@ + + + + + include/Library/Library.h file | My Project + + + + + +
+
+
+
+
+

+ include/Library/Library.h file +

+

A library header.

+ +

Should be shown as include/Library/Library.h in the page and also in the file tree, neither of them prefixed with project/. Its contents should then show #include <Library/Library.h>, without the project/include/ prefix.

+
+

Namespaces

+
+
namespace Library
+
A library namespace.
+
+
+
+
+
+
+ + diff --git a/documentation/test_doxygen/compound_includes_strip_from_path/classLibrary_1_1Class.html b/documentation/test_doxygen/compound_includes_strip_from_path/classLibrary_1_1Class.html new file mode 100644 index 00000000..83ad07c1 --- /dev/null +++ b/documentation/test_doxygen/compound_includes_strip_from_path/classLibrary_1_1Class.html @@ -0,0 +1,94 @@ + + + + + Library::Class class | My Project + + + + + +
+
+
+
+
+

+ Library::Class class + +

+

A class with overriden header file and name.

+ +

The overriden header file is used as the target location (where it links to the Library.h file, not to Data.h in which it's defined), the header name is what gets displayed.

+
+

Public functions

+
+
+ void foo() +
+
Class function.
+
+
+ +
+

Function documentation

+
+

+ void Library::Class::foo() +

+

Class function.

+

The class has the location overriden to Library.h and thus location for the function (which is in Data.h and can't be overriden) doesn't match the class. Showing a different include for it won't make sense tho as it's a member, so the code pretends the current include matches the actual location and not what was overriden.

+
+
+

+ void related() + +

+

A related function.

+

This function is related to the class, but is in a different header, so it should have #include <Library/Library.h> even though the class has overriden the header file (but not name) to point to Library.h. The code still treats Data.h as the actual class definiton because otherwise all class members would list redundant #include in its detailed docs.

+
+
+
+
+
+
+ + diff --git a/documentation/test_doxygen/compound_includes_strip_from_path/dir_d44c64559bbebec7f509842c48db8b23.html b/documentation/test_doxygen/compound_includes_strip_from_path/dir_d44c64559bbebec7f509842c48db8b23.html new file mode 100644 index 00000000..c607a75a --- /dev/null +++ b/documentation/test_doxygen/compound_includes_strip_from_path/dir_d44c64559bbebec7f509842c48db8b23.html @@ -0,0 +1,63 @@ + + + + + include/ directory | My Project + + + + + +
+
+
+
+
+

+ include/ directory +

+

A top-level include directory.

+ +

Should be shown only as include/. The file tree also shouldn't list the top-level project/ directory.

+
+

Directories

+
+
directory Library/
+
A library include directory.
+
+
+
+
+
+
+ + diff --git a/documentation/test_doxygen/compound_includes_strip_from_path/dir_f3b5534f769798fe34f6616e7fe90e4d.html b/documentation/test_doxygen/compound_includes_strip_from_path/dir_f3b5534f769798fe34f6616e7fe90e4d.html new file mode 100644 index 00000000..20257997 --- /dev/null +++ b/documentation/test_doxygen/compound_includes_strip_from_path/dir_f3b5534f769798fe34f6616e7fe90e4d.html @@ -0,0 +1,65 @@ + + + + + include/Library/ directory | My Project + + + + + +
+
+
+
+
+

+ include/Library/ directory +

+

A library include directory.

+ +

Should be shown only only as include/Library/. The file tree also shouldn't list the top-level project/ directory.

+
+

Files

+
+
file Data.h
+
A library data header.
+
file Library.h
+
A library header.
+
+
+
+
+
+
+ + diff --git a/documentation/test_doxygen/compound_includes_strip_from_path/example_8cpp.html b/documentation/test_doxygen/compound_includes_strip_from_path/example_8cpp.html new file mode 100644 index 00000000..031a9f54 --- /dev/null +++ b/documentation/test_doxygen/compound_includes_strip_from_path/example_8cpp.html @@ -0,0 +1,65 @@ + + + + + example.cpp file | My Project + + + + + +
+
+
+
+
+

+ example.cpp file +

+

An example file.

+ +

The file tree should list this file in the root, w/o project/examples/.

+
+

Functions

+
+
+ auto main() -> int +
+
An example function.
+
+
+
+
+
+
+ + diff --git a/documentation/test_doxygen/compound_includes_strip_from_path/files.html b/documentation/test_doxygen/compound_includes_strip_from_path/files.html new file mode 100644 index 00000000..3c7306cc --- /dev/null +++ b/documentation/test_doxygen/compound_includes_strip_from_path/files.html @@ -0,0 +1,72 @@ + + + + + My Project + + + + + +
+
+
+
+
+

Files

+ + +
+
+
+
+ + diff --git a/documentation/test_doxygen/compound_includes_strip_from_path/input.dox b/documentation/test_doxygen/compound_includes_strip_from_path/input.dox new file mode 100644 index 00000000..5a3b1318 --- /dev/null +++ b/documentation/test_doxygen/compound_includes_strip_from_path/input.dox @@ -0,0 +1,17 @@ +/* The top-level project/ directory shouldn't be shown anywhere. Also, the + `input.dox` which this is generated from shouldn't be shown anywhere, nor it + should expand the file tree one level up. */ + +/** @dir project/include + * @brief A top-level include directory + * + * Should be shown only as `include/`. The file tree also shouldn't list + * the top-level `project/` directory. + */ + +/** @dir project/include/Library + * @brief A library include directory + * + * Should be shown only only as `include/Library/`. The file tree also + * shouldn't list the top-level `project/` directory. + */ diff --git a/documentation/test_doxygen/compound_includes_strip_from_path/namespaceLibrary.html b/documentation/test_doxygen/compound_includes_strip_from_path/namespaceLibrary.html new file mode 100644 index 00000000..fd8827a6 --- /dev/null +++ b/documentation/test_doxygen/compound_includes_strip_from_path/namespaceLibrary.html @@ -0,0 +1,140 @@ + + + + + Library namespace | My Project + + + + + +
+
+
+
+
+

+ Library namespace +

+

A library namespace.

+ +

Should have no include listed, as it's spread over multiple files.

+
+

Namespaces

+
+
namespace Helper
+
A helper subnamespace.
+
+
+
+

Classes

+
+
+ class Class +
+
A class with overriden header file and name.
+
+ struct Struct +
+
A library structure.
+
+
+
+

Enums

+
+
+ enum Enum { } +
+
A library enum.
+
+
+
+

Typedefs

+
+
+ using Typedef = Struct +
+
A library typedef.
+
+
+
+

Functions

+
+
+ void function() +
+
A library entrypoint.
+
+
+
+

Enum documentation

+
+

+ enum Library::Enum + +

+

A library enum.

+

Should have #include <Library/Library.h> listed, without project/include/.

+
+
+
+

Typedef documentation

+
+

+ typedef Struct Library::Typedef + +

+

A library typedef.

+

Should have #include <Library/Data.h> listed, without project/include/.

+
+
+
+

Function documentation

+
+

+ void Library::function() + +

+

A library entrypoint.

+

Should have #include <Library/Library.h> listed, without project/include/.

+
+
+
+
+
+
+ + diff --git a/documentation/test_doxygen/compound_includes_strip_from_path/namespaceLibrary_1_1Helper.html b/documentation/test_doxygen/compound_includes_strip_from_path/namespaceLibrary_1_1Helper.html new file mode 100644 index 00000000..72d60e61 --- /dev/null +++ b/documentation/test_doxygen/compound_includes_strip_from_path/namespaceLibrary_1_1Helper.html @@ -0,0 +1,76 @@ + + + + + Library::Helper namespace | My Project + + + + + +
+
+
+
+
+

+ Library::Helper namespace + +

+

A helper subnamespace.

+ +

Should have #include <Library/Data.h> listed, without project/include/.

+
+

Functions

+
+
+ auto version() -> unsigned +
+
Library version.
+
+
+
+

Function documentation

+
+

+ unsigned Library::Helper::version() +

+

Library version.

+

Should have no incldue listed, as the namespace is contained in a single file.

+
+
+
+
+
+
+ + diff --git a/documentation/test_doxygen/compound_includes_strip_from_path/project/examples/example.cpp b/documentation/test_doxygen/compound_includes_strip_from_path/project/examples/example.cpp new file mode 100644 index 00000000..0bf762ae --- /dev/null +++ b/documentation/test_doxygen/compound_includes_strip_from_path/project/examples/example.cpp @@ -0,0 +1,11 @@ +/** @file + * @brief An example file + * + * The file tree should list this file in the root, w/o `project/examples/`. + */ + +#include +#include + +/** @brief An example function */ +int main() {} diff --git a/documentation/test_doxygen/compound_includes_strip_from_path/project/include/Library/Data.h b/documentation/test_doxygen/compound_includes_strip_from_path/project/include/Library/Data.h new file mode 100644 index 00000000..77e115c9 --- /dev/null +++ b/documentation/test_doxygen/compound_includes_strip_from_path/project/include/Library/Data.h @@ -0,0 +1,64 @@ +/** @file + * @brief A library data header + * + * Should be shown as `include/Library/Data.h` in the page and also in the + * file tree, neither of them prefixed with `project/`. Its contents should + * then show `#include `, without the `project/include/` + * prefix. + */ + +namespace Library { + +/** + * @brief A library structure + * + * Should have `#include ` listed, without `project/include/`. + */ +struct Struct {}; + +class Class { + public: + /** + * @brief Class function + * + * The class has the location overriden to `Library.h` and thus + * location for the function (which is in `Data.h` and can't be + * overriden) doesn't match the class. Showing a different include for + * it won't make sense tho as it's a member, so the code pretends the + * current include matches the actual location and not what was + * overriden. + */ + void foo(); +}; + +/** @class Class project/include/Library/Library.h FakeHeader.h + * @brief A class with overriden header file and name + * + * The overriden header *file* is used as the target location (where it links + * to the `Library.h` file, *not* to `Data.h` in which it's defined), the + * header *name* is what gets displayed. + */ + +/** + * @brief A helper subnamespace + * + * Should have `#include ` listed, without `project/include/`. + */ +namespace Helper { + /** + * @brief Library version + * + * Should have no incldue listed, as the namespace is contained in a single + * file. + */ + unsigned version(); +} + +/** + * @brief A library typedef + * + * Should have `#include ` listed, without `project/include/`. + */ +typedef Struct Typedef; + +} diff --git a/documentation/test_doxygen/compound_includes_strip_from_path/project/include/Library/Library.h b/documentation/test_doxygen/compound_includes_strip_from_path/project/include/Library/Library.h new file mode 100644 index 00000000..fd1088cb --- /dev/null +++ b/documentation/test_doxygen/compound_includes_strip_from_path/project/include/Library/Library.h @@ -0,0 +1,44 @@ +/** @file + * @brief A library header + * + * Should be shown as `include/Library/Library.h` in the page and also in the + * file tree, neither of them prefixed with `project/`. Its contents should + * then show `#include `, without the `project/include/` + * prefix. + */ + +/** + * @brief A library namespace + * + * Should have no include listed, as it's spread over multiple files. + */ +namespace Library { + +/** + * @brief A library entrypoint + * + * Should have `#include ` listed, without + * `project/include/`. + */ +void function(); + +/** + * @brief A library enum + * + * Should have `#include ` listed, without + * `project/include/`. + */ +enum Enum {}; + +/** @related Class + * @brief A related function + * + * This function is related to the class, but is in a different header, so it + * should have `#include ` even though the class has + * overriden the header file (but not name) to point to `Library.h`. The code + * still treats `Data.h` as the actual class definiton because otherwise all + * class members would list redundant `#include` in its detailed docs. + */ +void related(); + +} diff --git a/documentation/test_doxygen/compound_includes_strip_from_path/structLibrary_1_1Struct.html b/documentation/test_doxygen/compound_includes_strip_from_path/structLibrary_1_1Struct.html new file mode 100644 index 00000000..7b28afdf --- /dev/null +++ b/documentation/test_doxygen/compound_includes_strip_from_path/structLibrary_1_1Struct.html @@ -0,0 +1,46 @@ + + + + + Library::Struct struct | My Project + + + + + +
+
+
+
+
+

+ Library::Struct struct + +

+

A library structure.

+

Should have #include <Library/Data.h> listed, without project/include/.

+
+
+
+
+ + diff --git a/documentation/test_doxygen/test_compound.py b/documentation/test_doxygen/test_compound.py index fec2e969..a53418e2 100644 --- a/documentation/test_doxygen/test_compound.py +++ b/documentation/test_doxygen/test_compound.py @@ -306,6 +306,28 @@ class IncludesDisabled(IntegrationTestCase): self.assertEqual(*self.actual_expected_contents('group__group.html')) self.assertEqual(*self.actual_expected_contents('structSpreadClass.html')) +class IncludesStripFromPath(IntegrationTestCase): + def test(self): + self.run_doxygen(wildcard='*.xml') + + # Directories and files should not be prefixed with project/ + self.assertEqual(*self.actual_expected_contents('dir_d44c64559bbebec7f509842c48db8b23.html')) + self.assertEqual(*self.actual_expected_contents('dir_f3b5534f769798fe34f6616e7fe90e4d.html')) + self.assertEqual(*self.actual_expected_contents('Data_8h.html')) + self.assertEqual(*self.actual_expected_contents('Library_8h.html')) + self.assertEqual(*self.actual_expected_contents('example_8cpp.html')) + + # Namespaces and classes should show the correct #include not prefixed + # with project/includes/ + self.assertEqual(*self.actual_expected_contents('namespaceLibrary.html')) + self.assertEqual(*self.actual_expected_contents('namespaceLibrary_1_1Helper.html')) + self.assertEqual(*self.actual_expected_contents('classLibrary_1_1Class.html')) + self.assertEqual(*self.actual_expected_contents('structLibrary_1_1Struct.html')) + + # The file tree should show the two dirs and three files with correct + # nesting and again without the project/ prefix + self.assertEqual(*self.actual_expected_contents('files.html')) + class IncludesUndocumentedFiles(IntegrationTestCase): def test(self): self.run_doxygen(wildcard='*.xml')