From 4452a6bda8966a04f569f4ecb2a7f964e71349b7 Mon Sep 17 00:00:00 2001 From: Fernando Pelliccioni Date: Thu, 12 Sep 2024 12:49:52 +0200 Subject: [PATCH] feat: tagfiles generation for cross-referencing in doxygen format --- .github/workflows/ci.yml | 3 +- include/mrdocs/Corpus.hpp | 33 ++ include/mrdocs/Generator.hpp | 37 +- src/lib/Gen/adoc/AdocGenerator.cpp | 48 ++- src/lib/Gen/adoc/AdocGenerator.hpp | 3 +- src/lib/Gen/adoc/MultiPageVisitor.cpp | 8 +- src/lib/Gen/adoc/MultiPageVisitor.hpp | 6 +- src/lib/Gen/adoc/SinglePageVisitor.cpp | 5 +- src/lib/Gen/adoc/SinglePageVisitor.hpp | 9 +- src/lib/Gen/html/HTMLGenerator.cpp | 44 ++- src/lib/Gen/html/HTMLGenerator.hpp | 3 +- src/lib/Gen/html/MultiPageVisitor.cpp | 1 + src/lib/Gen/html/MultiPageVisitor.hpp | 6 +- src/lib/Gen/html/SinglePageVisitor.cpp | 4 +- src/lib/Gen/html/SinglePageVisitor.hpp | 9 +- src/lib/Gen/xml/XMLGenerator.cpp | 3 +- src/lib/Gen/xml/XMLGenerator.hpp | 3 +- src/lib/Gen/xml/XMLTags.cpp | 3 + src/lib/Gen/xml/XMLTags.hpp | 2 + src/lib/Lib/TagfileWriter.cpp | 505 +++++++++++++++++++++++++ src/lib/Lib/TagfileWriter.hpp | 73 ++++ src/lib/Support/Generator.cpp | 35 +- 22 files changed, 821 insertions(+), 22 deletions(-) create mode 100644 src/lib/Lib/TagfileWriter.cpp create mode 100644 src/lib/Lib/TagfileWriter.hpp diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 900b71a4e..4fe7ba873 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -632,8 +632,9 @@ jobs: for generator in "${generators[@]}"; do [[ $generator = xml && $variant = multi ]] && continue [[ $variant = multi ]] && multipage="true" || multipage="false" + echo "Generating demos for $variant/$generator" mrdocs --config="$(pwd)/boost/libs/url/doc/mrdocs.yml" "../CMakeLists.txt" --output="$(pwd)/demos/boost-url/$variant/$generator" --multipage=$multipage --generate="$generator" - echo "Number of files in demos/boost-url/$variant/$format: $(find demos/boost-url/$variant/$format -type f | wc -l)" + echo "Number of files in demos/boost-url/$variant/$generator: $(find demos/boost-url/$variant/$generator -type f | wc -l)" done done diff --git a/include/mrdocs/Corpus.hpp b/include/mrdocs/Corpus.hpp index aaa10d293..d9c59232f 100644 --- a/include/mrdocs/Corpus.hpp +++ b/include/mrdocs/Corpus.hpp @@ -160,6 +160,39 @@ class MRDOCS_VISIBLE } } + /** Visit the members of specified Info. + + This function iterates the members of the specified + Info `I`. For each member associated with a + function with the same name as the member, the + function object `f` is invoked with the member + as the first argument, followed by `args...`. + + When there are more than one member function + with the same name, the function object `f` is + invoked with an @ref OverloadSet as the first + argument, followed by `args...`. + + @param I The Info to traverse. + @param pred The predicate to use to determine if the member should be visited. + @param f The function to invoke with the member as the first argument, followed by `args...`. + @param args The arguments to pass to the function. + */ + template + void + traverseIf( + T const& I, Pred&& pred, F&& f, Args&&... args) const + { + for (auto const& id : I.Members) + { + if (std::forward(pred)(get(id))) + { + visit(get(id), std::forward(f), + std::forward(args)...); + } + } + } + /** Visit the function members of specified Info. This function iterates the members of the specified diff --git a/include/mrdocs/Generator.hpp b/include/mrdocs/Generator.hpp index dcc4f7c41..3f9914db2 100644 --- a/include/mrdocs/Generator.hpp +++ b/include/mrdocs/Generator.hpp @@ -26,6 +26,14 @@ namespace clang { namespace mrdocs { +/** Context for the output of a single page. + * Needed to write the tagfile. +*/ +struct GenerationContext { + std::ostream& tagFileOs; + std::string tagFileName; +}; + /** Base class for documentation generators. */ class MRDOCS_VISIBLE @@ -100,6 +108,33 @@ class MRDOCS_VISIBLE std::string_view outputPath, Corpus const& corpus) const; + /** Build reference documentation for the corpus. + + This function invokes the generator to emit + the full documentation to an output stream, + as a single entity. + + @par Thread Safety + @li Different `corpus` object: may be called concurrently. + @li Same `corpus` object: may not be called concurrently. + + @return The error, if any occurred. + + @param os The stream to write to. + + @param corpus The metadata to emit. + + @param context The context to use for the output. + */ + MRDOCS_DECL + virtual + Error + buildOne( + std::ostream& os, + Corpus const& corpus, + GenerationContext const& context) const = 0; + + /** Build reference documentation for the corpus. This function invokes the generator to emit @@ -121,7 +156,7 @@ class MRDOCS_VISIBLE Error buildOne( std::ostream& os, - Corpus const& corpus) const = 0; + Corpus const& corpus) const; /** Build the reference as a single page to a file. diff --git a/src/lib/Gen/adoc/AdocGenerator.cpp b/src/lib/Gen/adoc/AdocGenerator.cpp index 5c02bf54d..cf94f89f2 100644 --- a/src/lib/Gen/adoc/AdocGenerator.cpp +++ b/src/lib/Gen/adoc/AdocGenerator.cpp @@ -15,8 +15,15 @@ #include "MultiPageVisitor.hpp" #include "SinglePageVisitor.hpp" #include "lib/Support/LegibleNames.hpp" +#include "lib/Support/RawOstream.hpp" + #include #include +#include + +#include + +#include #include #include @@ -69,10 +76,35 @@ build( if(! ex) return ex.error(); - MultiPageVisitor visitor(*ex, outputPath, corpus); + std::string path = files::appendPath(outputPath, "reference.tag.xml"); + if(auto err = files::createDirectory(outputPath)) + { + return err; + } + + std::ofstream os; + try + { + os.open(path, + std::ios_base::binary | + std::ios_base::out | + std::ios_base::trunc // | std::ios_base::noreplace + ); + } + catch(std::exception const& ex) + { + return formatError("std::ofstream(\"{}\") threw \"{}\"", path, ex.what()); + } + + RawOstream raw_os(os); + auto tagfileWriter = TagfileWriter(raw_os, corpus); + tagfileWriter.initialize(); + + MultiPageVisitor visitor(*ex, outputPath, corpus, tagfileWriter); visitor(corpus.globalNamespace()); auto errors = ex->wait(); + tagfileWriter.finalize(); if(! errors.empty()) return Error(errors); return Error::success(); @@ -82,8 +114,11 @@ Error AdocGenerator:: buildOne( std::ostream& os, - Corpus const& corpus) const + Corpus const& corpus, + GenerationContext const& context) const { + namespace path = llvm::sys::path; + auto options = loadOptions(corpus); if(! options) return options.error(); @@ -105,9 +140,16 @@ buildOne( if(! errors.empty()) return {errors}; - SinglePageVisitor visitor(*ex, corpus, os); + + RawOstream raw_os(context.tagFileOs); + auto tagfileWriter = TagfileWriter(raw_os, corpus); + tagfileWriter.initialize(); + + auto const justFileName = path::filename(context.tagFileName); + SinglePageVisitor visitor(*ex, corpus, os, justFileName, tagfileWriter); visitor(corpus.globalNamespace()); errors = ex->wait(); + tagfileWriter.finalize(); if(! errors.empty()) return {errors}; diff --git a/src/lib/Gen/adoc/AdocGenerator.hpp b/src/lib/Gen/adoc/AdocGenerator.hpp index 452e42b80..0c0bda1f5 100644 --- a/src/lib/Gen/adoc/AdocGenerator.hpp +++ b/src/lib/Gen/adoc/AdocGenerator.hpp @@ -49,7 +49,8 @@ class AdocGenerator Error buildOne( std::ostream& os, - Corpus const& corpus) const override; + Corpus const& corpus, + GenerationContext const& context) const override; }; } // adoc diff --git a/src/lib/Gen/adoc/MultiPageVisitor.cpp b/src/lib/Gen/adoc/MultiPageVisitor.cpp index df5e4b319..ee71e514c 100644 --- a/src/lib/Gen/adoc/MultiPageVisitor.cpp +++ b/src/lib/Gen/adoc/MultiPageVisitor.cpp @@ -50,8 +50,12 @@ operator()(T const& I) { ex_.async([this, &I](Builder& builder) { - if(const auto r = builder(I)) - writePage(*r, builder.domCorpus.getXref(I)); + if(const auto r = builder(I)) + { + auto const xref = builder.domCorpus.getXref(I); + writePage(*r, xref); + tagfileWriter_(I, xref, 0); + } else r.error().Throw(); if constexpr( diff --git a/src/lib/Gen/adoc/MultiPageVisitor.hpp b/src/lib/Gen/adoc/MultiPageVisitor.hpp index 3707d3ff3..e51be599c 100644 --- a/src/lib/Gen/adoc/MultiPageVisitor.hpp +++ b/src/lib/Gen/adoc/MultiPageVisitor.hpp @@ -12,6 +12,7 @@ #define MRDOCS_LIB_GEN_ADOC_MULTIPAGEVISITOR_HPP #include "Builder.hpp" +#include #include #include #include @@ -29,6 +30,7 @@ class MultiPageVisitor ExecutorGroup& ex_; std::string_view outputPath_; Corpus const& corpus_; + TagfileWriter& tagfileWriter_; void writePage( @@ -39,10 +41,12 @@ class MultiPageVisitor MultiPageVisitor( ExecutorGroup& ex, std::string_view outputPath, - Corpus const& corpus) noexcept + Corpus const& corpus, + TagfileWriter& tagfileWriter) noexcept : ex_(ex) , outputPath_(outputPath) , corpus_(corpus) + , tagfileWriter_(tagfileWriter) { } diff --git a/src/lib/Gen/adoc/SinglePageVisitor.cpp b/src/lib/Gen/adoc/SinglePageVisitor.cpp index bc21794f3..dce65ecba 100644 --- a/src/lib/Gen/adoc/SinglePageVisitor.cpp +++ b/src/lib/Gen/adoc/SinglePageVisitor.cpp @@ -22,8 +22,11 @@ operator()(T const& I) { ex_.async([this, &I, page = numPages_++](Builder& builder) { - if(auto r = builder(I)) + if(auto r = builder(I)) + { writePage(*r, page); + tagfileWriter_(I, fileName_, page); + } else r.error().Throw(); }); diff --git a/src/lib/Gen/adoc/SinglePageVisitor.hpp b/src/lib/Gen/adoc/SinglePageVisitor.hpp index f5cb3207d..9e01a205b 100644 --- a/src/lib/Gen/adoc/SinglePageVisitor.hpp +++ b/src/lib/Gen/adoc/SinglePageVisitor.hpp @@ -13,6 +13,7 @@ #include "Builder.hpp" #include +#include #include #include #include @@ -35,16 +36,22 @@ class SinglePageVisitor std::size_t topPage_ = 0; std::vector> pages_; + std::string fileName_; + TagfileWriter& tagfileWriter_; void writePage(std::string pageText, std::size_t pageNumber); public: SinglePageVisitor( ExecutorGroup& ex, Corpus const& corpus, - std::ostream& os) noexcept + std::ostream& os, + std::string_view fileName, + TagfileWriter& tagfileWriter) noexcept : ex_(ex) , corpus_(corpus) , os_(os) + , fileName_(fileName) + , tagfileWriter_(tagfileWriter) { } diff --git a/src/lib/Gen/html/HTMLGenerator.cpp b/src/lib/Gen/html/HTMLGenerator.cpp index 40fe09f24..52d451bda 100644 --- a/src/lib/Gen/html/HTMLGenerator.cpp +++ b/src/lib/Gen/html/HTMLGenerator.cpp @@ -15,9 +15,13 @@ #include "MultiPageVisitor.hpp" #include "SinglePageVisitor.hpp" #include "lib/Support/LegibleNames.hpp" +#include "lib/Support/RawOstream.hpp" + #include #include #include + +#include #include #include @@ -67,9 +71,34 @@ build( if(! ex) return ex.error(); - MultiPageVisitor visitor(*ex, outputPath, corpus); + std::string path = files::appendPath(outputPath, "reference.tag.xml"); + if(auto err = files::createDirectory(outputPath)) + { + return err; + } + + std::ofstream os; + try + { + os.open(path, + std::ios_base::binary | + std::ios_base::out | + std::ios_base::trunc // | std::ios_base::noreplace + ); + } + catch(std::exception const& ex) + { + return formatError("std::ofstream(\"{}\") threw \"{}\"", path, ex.what()); + } + + RawOstream raw_os(os); + auto tagfileWriter = TagfileWriter(raw_os, corpus); + tagfileWriter.initialize(); + + MultiPageVisitor visitor(*ex, outputPath, corpus, tagfileWriter); visitor(corpus.globalNamespace()); auto errors = ex->wait(); + tagfileWriter.finalize(); if(! errors.empty()) return Error(errors); return Error::success(); @@ -79,8 +108,9 @@ Error HTMLGenerator:: buildOne( std::ostream& os, - Corpus const& corpus) const -{ + Corpus const& corpus, + GenerationContext const& context) const +{ HTMLCorpus domCorpus(corpus); auto ex = createExecutors(domCorpus); if(! ex) @@ -98,9 +128,15 @@ buildOne( if(! errors.empty()) return Error(errors); - SinglePageVisitor visitor(*ex, corpus, os); + + RawOstream raw_os(context.tagFileOs); + auto tagfileWriter = TagfileWriter(raw_os, corpus); + tagfileWriter.initialize(); + + SinglePageVisitor visitor(*ex, corpus, os, context.tagFileName, tagfileWriter); visitor(corpus.globalNamespace()); errors = ex->wait(); + tagfileWriter.finalize(); if(! errors.empty()) return Error(errors); diff --git a/src/lib/Gen/html/HTMLGenerator.hpp b/src/lib/Gen/html/HTMLGenerator.hpp index c74c13247..fc8c136e7 100644 --- a/src/lib/Gen/html/HTMLGenerator.hpp +++ b/src/lib/Gen/html/HTMLGenerator.hpp @@ -49,7 +49,8 @@ class HTMLGenerator Error buildOne( std::ostream& os, - Corpus const& corpus) const override; + Corpus const& corpus, + GenerationContext const& context) const override; }; } // html diff --git a/src/lib/Gen/html/MultiPageVisitor.cpp b/src/lib/Gen/html/MultiPageVisitor.cpp index eaa08034e..ab42ab146 100644 --- a/src/lib/Gen/html/MultiPageVisitor.cpp +++ b/src/lib/Gen/html/MultiPageVisitor.cpp @@ -50,6 +50,7 @@ renderPage( std::ios_base::trunc // | std::ios_base::noreplace ); os.write(pageText.data(), pageText.size()); + tagfileWriter_(I, fileName, 0); } catch(std::exception const& ex) { diff --git a/src/lib/Gen/html/MultiPageVisitor.hpp b/src/lib/Gen/html/MultiPageVisitor.hpp index 0c355aba0..e98ef4c90 100644 --- a/src/lib/Gen/html/MultiPageVisitor.hpp +++ b/src/lib/Gen/html/MultiPageVisitor.hpp @@ -12,6 +12,7 @@ #define MRDOCS_LIB_GEN_HTML_MULTIPAGEVISITOR_HPP #include "Builder.hpp" +#include #include #include #include @@ -29,15 +30,18 @@ class MultiPageVisitor ExecutorGroup& ex_; std::string_view outputPath_; Corpus const& corpus_; + TagfileWriter& tagfileWriter_; public: MultiPageVisitor( ExecutorGroup& ex, std::string_view outputPath, - Corpus const& corpus) noexcept + Corpus const& corpus, + TagfileWriter& tagfileWriter) noexcept : ex_(ex) , outputPath_(outputPath) , corpus_(corpus) + , tagfileWriter_(tagfileWriter) { } diff --git a/src/lib/Gen/html/SinglePageVisitor.cpp b/src/lib/Gen/html/SinglePageVisitor.cpp index d327a2036..e018495e3 100644 --- a/src/lib/Gen/html/SinglePageVisitor.cpp +++ b/src/lib/Gen/html/SinglePageVisitor.cpp @@ -22,8 +22,10 @@ operator()(T const& I) { ex_.async([this, &I, page = numPages_++](Builder& builder) { - if(auto r = builder(I)) + if(auto r = builder(I)) { writePage(*r, page); + tagfileWriter_(I, fileName_, page); + } else r.error().Throw(); }); diff --git a/src/lib/Gen/html/SinglePageVisitor.hpp b/src/lib/Gen/html/SinglePageVisitor.hpp index e153dbe70..999f045a5 100644 --- a/src/lib/Gen/html/SinglePageVisitor.hpp +++ b/src/lib/Gen/html/SinglePageVisitor.hpp @@ -12,6 +12,7 @@ #define MRDOCS_LIB_GEN_HTML_SINGLEPAGEVISITOR_HPP #include "Builder.hpp" +#include #include #include #include @@ -34,15 +35,21 @@ class SinglePageVisitor std::size_t topPage_ = 0; std::vector> pages_; + std::string fileName_; + TagfileWriter& tagfileWriter_; public: SinglePageVisitor( ExecutorGroup& ex, Corpus const& corpus, - std::ostream& os) noexcept + std::ostream& os, + std::string_view fileName, + TagfileWriter& tagfileWriter) noexcept : ex_(ex) , corpus_(corpus) , os_(os) + , fileName_(fileName) + , tagfileWriter_(tagfileWriter) { } diff --git a/src/lib/Gen/xml/XMLGenerator.cpp b/src/lib/Gen/xml/XMLGenerator.cpp index 7fb19c637..2c4580833 100644 --- a/src/lib/Gen/xml/XMLGenerator.cpp +++ b/src/lib/Gen/xml/XMLGenerator.cpp @@ -24,7 +24,8 @@ Error XMLGenerator:: buildOne( std::ostream& os, - Corpus const& corpus) const + Corpus const& corpus, + GenerationContext const& /*context*/) const { namespace fs = llvm::sys::fs; RawOstream raw_os(os); diff --git a/src/lib/Gen/xml/XMLGenerator.hpp b/src/lib/Gen/xml/XMLGenerator.hpp index 6531775d9..3dae2315a 100644 --- a/src/lib/Gen/xml/XMLGenerator.hpp +++ b/src/lib/Gen/xml/XMLGenerator.hpp @@ -47,7 +47,8 @@ struct XMLGenerator : Generator Error buildOne( std::ostream& os, - Corpus const& corpus) const override; + Corpus const& corpus, + GenerationContext const& context) const override; }; } // xml diff --git a/src/lib/Gen/xml/XMLTags.cpp b/src/lib/Gen/xml/XMLTags.cpp index 780ddffa7..480d2766c 100644 --- a/src/lib/Gen/xml/XMLTags.cpp +++ b/src/lib/Gen/xml/XMLTags.cpp @@ -193,6 +193,9 @@ void XMLTags:: nest(int levels) { + if (!nesting_) + return; + if(levels > 0) { indent_.append(levels * 2, ' '); diff --git a/src/lib/Gen/xml/XMLTags.hpp b/src/lib/Gen/xml/XMLTags.hpp index 31d1b6348..a1cfe9a7f 100644 --- a/src/lib/Gen/xml/XMLTags.hpp +++ b/src/lib/Gen/xml/XMLTags.hpp @@ -186,6 +186,7 @@ class XMLTags public: std::string indent_; llvm::raw_ostream& os_; + bool nesting_ = true; explicit XMLTags( @@ -201,6 +202,7 @@ class XMLTags void write(dom::String const&, llvm::StringRef value = {}, Attributes = {}); void close(dom::String const&); + void nesting(bool enable) noexcept { nesting_ = enable; } void nest(int levels); }; diff --git a/src/lib/Lib/TagfileWriter.cpp b/src/lib/Lib/TagfileWriter.cpp new file mode 100644 index 000000000..410b1aef5 --- /dev/null +++ b/src/lib/Lib/TagfileWriter.cpp @@ -0,0 +1,505 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// Licensed under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// Copyright (c) 2024 Fernando Pelliccioni (fpelliccioni@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#include "../Gen/xml/CXXTags.hpp" +#include "TagfileWriter.hpp" +#include "lib/Lib/ConfigImpl.hpp" + +namespace clang { +namespace mrdocs { + +//------------------------------------------------ +// +// TagfileWriter +// +//------------------------------------------------ + +TagfileWriter:: +TagfileWriter( + llvm::raw_ostream& os, + Corpus const& corpus) noexcept + : tags_(os) + , os_(os) + , corpus_(corpus) +{ + tags_.nesting(false); +} + +void +TagfileWriter:: +initialize() { + os_ << "\n"; + os_ << "\n"; +} + +void +TagfileWriter:: +finalize() { + os_ << "\n"; +} + +//------------------------------------------------ + +void +TagfileWriter:: +writeIndex() +{ + // Do nothing +} + +//------------------------------------------------ + +template +void +TagfileWriter:: +operator()( + T const& I + , std::string_view filename, + std::size_t page) + +{ + #define INFO(Type) if constexpr(T::is##Type()) write##Type(I, filename, page); + #include +} + +template +void +TagfileWriter:: +operator()( + T const& I, + std::string_view filename, + std::size_t page, + SimpleWriterTag) +{ + #define INFO(Type) if constexpr(T::is##Type()) write##Type(I, filename, page, SimpleWriterTag{}); + #include +} + +//------------------------------------------------ + +void +TagfileWriter:: +writeNamespace( + NamespaceInfo const& I, + std::string_view filename, + std::size_t page, + SimpleWriterTag) +{ + // Do nothing +} + +bool +TagfileWriter:: +containsOnlyNamespaces( + NamespaceInfo const& I) const +{ + bool onlyNamespaces = true; + corpus_.traverse(I, [&](Info const& I) + { + if (I.Kind != InfoKind::Namespace) + { + onlyNamespaces = false; + return false; + } + return true; + }); + return onlyNamespaces; +} + +void +TagfileWriter:: +writeNamespace( + NamespaceInfo const& I, + std::string_view filename, + std::size_t page) +{ + if (!containsOnlyNamespaces(I)) + { + tags_.open("compound", { + { "kind", "namespace" } + }); + + std::string temp; + temp.reserve(256); + tags_.write("name", corpus_.getFullyQualifiedName(I, temp)); + tags_.write("name", I.Name); + + corpus_.traverseIf(I, [](Info const& I) + { + return I.Kind != InfoKind::Function; + }, *this, filename, page, SimpleWriterTag{}); + + corpus_.traverseIf(I, [](Info const& I) + { + return I.Kind == InfoKind::Function; + }, *this, filename, page, SimpleWriterTag{}); + + tags_.close("compound"); + } +} + +template +void +TagfileWriter:: +writeClassLike( + T const& I, + std::string_view filename, + std::size_t page, + SimpleWriterTag) +{ + std::string temp; + temp.reserve(256); + tags_.write("class", corpus_.getFullyQualifiedName(I, temp), {{"kind", "class"}}); +} + +template +void +TagfileWriter:: +writeClassLike( + T const& I, + std::string_view filename, + std::size_t page) +{ + tags_.open("compound", { + { "kind", "class" } + }); + + std::string temp; + temp.reserve(256); + tags_.write("name", corpus_.getFullyQualifiedName(I, temp)); + tags_.write("filename", filename); + if (page != 0) { + tags_.write("anchor", std::to_string(page)); + } + tags_.close("compound"); +} + +void +TagfileWriter:: +writeEnum( + EnumInfo const& I, + std::string_view filename, + std::size_t page, + SimpleWriterTag) +{ + writeClassLike(I, filename, page, SimpleWriterTag{}); +} + +void +TagfileWriter:: +writeEnum( + EnumInfo const& I, + std::string_view filename, + std::size_t page) +{ + writeClassLike(I, filename, page); +} + +void +TagfileWriter:: +writeEnumerator( + EnumeratorInfo const& I, + std::string_view filename, + std::size_t page, + SimpleWriterTag) +{ + writeClassLike(I, filename, page, SimpleWriterTag{}); +} + +void +TagfileWriter:: +writeEnumerator( + EnumeratorInfo const& I, + std::string_view filename, + std::size_t page) +{ + writeClassLike(I, filename, page); +} + +void +TagfileWriter:: +writeFriend( + FriendInfo const& I, + std::string_view filename, + std::size_t page, + SimpleWriterTag) +{ + writeClassLike(I, filename, page, SimpleWriterTag{}); +} + +void +TagfileWriter:: +writeFriend( + FriendInfo const& I, + std::string_view filename, + std::size_t page) +{ + writeClassLike(I, filename, page); +} + +void +TagfileWriter:: +writeFunction( + FunctionInfo const& I, + std::string_view filename, + std::size_t page, + SimpleWriterTag) +{ + // Do nothing +} + +void +TagfileWriter:: +writeFunction( + FunctionInfo const& I, + std::string_view filename, + std::size_t page) +{ + using xml::toString; + + tags_.open("member", {{"kind", "function"}}); + tags_.write("type", toString(*I.ReturnType)); + tags_.write("name", I.Name); + + std::string arglist = "("; + for(auto const& J : I.Params) + { + arglist += toString(*J.Type); + arglist += " "; + arglist += J.Name; + arglist += ", "; + } + if (arglist.size() > 2) { + arglist.resize(arglist.size() - 2); + } + arglist += ")"; + tags_.write("arglist", arglist); + tags_.write("anchorfile", filename); + if (page != 0) { + tags_.write("anchor", std::to_string(page)); + } + tags_.close("member"); +} + +void +TagfileWriter:: +writeGuide( + GuideInfo const& I, + std::string_view filename, + std::size_t page, + SimpleWriterTag) +{ + // Do nothing +} + +void +TagfileWriter:: +writeGuide( + GuideInfo const& I, + std::string_view filename, + std::size_t page) +{ +} + +void +TagfileWriter:: +writeConcept( + ConceptInfo const& I, + std::string_view filename, + std::size_t page, + SimpleWriterTag) +{ + writeClassLike(I, filename, page, SimpleWriterTag{}); +} + +void +TagfileWriter:: +writeConcept( + ConceptInfo const& I, + std::string_view filename, + std::size_t page) +{ + writeClassLike(I, filename, page); +} + + +void +TagfileWriter:: +writeAlias( + AliasInfo const& I, + std::string_view filename, + std::size_t page, + SimpleWriterTag) +{ + writeClassLike(I, filename, page, SimpleWriterTag{}); +} + +void +TagfileWriter:: +writeAlias( + AliasInfo const& I, + std::string_view filename, + std::size_t page) +{ + writeClassLike(I, filename, page); +} + +void +TagfileWriter:: +writeUsing( + UsingInfo const& I, + std::string_view filename, + std::size_t page, + SimpleWriterTag) +{ + writeClassLike(I, filename, page, SimpleWriterTag{}); +} + +void +TagfileWriter:: +writeUsing( + UsingInfo const& I, + std::string_view filename, + std::size_t page) +{ + writeClassLike(I, filename, page); +} + +void +TagfileWriter:: +writeRecord( + RecordInfo const& I, + std::string_view filename, + std::size_t page, + SimpleWriterTag) +{ + writeClassLike(I, filename, page, SimpleWriterTag{}); +} + +void +TagfileWriter:: +writeRecord( + RecordInfo const& I, + std::string_view filename, + std::size_t page) +{ + writeClassLike(I, filename, page); +} + +void +TagfileWriter:: +writeTypedef( + TypedefInfo const& I, + std::string_view filename, + std::size_t page, + SimpleWriterTag) +{ + writeClassLike(I, filename, page, SimpleWriterTag{}); +} + +void +TagfileWriter:: +writeTypedef( + TypedefInfo const& I, + std::string_view filename, + std::size_t page) +{ + writeClassLike(I, filename, page); +} + +void +TagfileWriter:: +writeField( + const FieldInfo& I, + std::string_view filename, + std::size_t page, + SimpleWriterTag) +{ + // Do nothing +} + +void +TagfileWriter:: +writeField( + const FieldInfo& I, + std::string_view filename, + std::size_t page) +{ + // Do nothing +} + +void +TagfileWriter:: +writeVariable( + VariableInfo const& I, + std::string_view filename, + std::size_t page, + SimpleWriterTag) +{ + // Do nothing +} + +void +TagfileWriter:: +writeVariable( + VariableInfo const& I, + std::string_view filename, + std::size_t page) +{ + // Do nothing +} + +void +TagfileWriter:: +openTemplate( + std::unique_ptr const& I) +{ + // Do nothing +} + +void +TagfileWriter:: +closeTemplate( + std::unique_ptr const& I) +{ + // Do nothing +} + +void +TagfileWriter:: +writeSpecialization( + const SpecializationInfo& I, + std::string_view filename, + std::size_t page, + SimpleWriterTag) +{ + // Do nothing +} + +void +TagfileWriter:: +writeSpecialization( + const SpecializationInfo& I, + std::string_view filename, + std::size_t page) +{ + // Do nothing +} + + +#define DEFINE(T) template void \ + TagfileWriter::operator()(T const&, std::string_view, std::size_t) + +#define INFO(Type) DEFINE(Type##Info); +#include + +} // mrdocs +} // clang diff --git a/src/lib/Lib/TagfileWriter.hpp b/src/lib/Lib/TagfileWriter.hpp new file mode 100644 index 000000000..0c9e5d809 --- /dev/null +++ b/src/lib/Lib/TagfileWriter.hpp @@ -0,0 +1,73 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// Licensed under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// Copyright (c) 2024 Fernando Pelliccioni (fpelliccioni@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_LIB_TAGFILEWRITER_HPP +#define MRDOCS_LIB_TAGFILEWRITER_HPP + +#include "../Gen/xml/XMLTags.hpp" +#include +#include +#include +#include + +namespace clang { +namespace mrdocs { + +struct SimpleWriterTag {}; //Tag dispatch for simple writers + +class jit_indenter; + +/** A writer which outputs Tagfiles. +*/ +class TagfileWriter +{ +protected: + xml::XMLTags tags_; + llvm::raw_ostream& os_; + Corpus const& corpus_; + +public: + TagfileWriter( + llvm::raw_ostream& os, + Corpus const& corpus) noexcept; + + void initialize(); + void finalize(); + + void writeIndex(); + + template + void operator()(T const&, std::string_view, std::size_t); + + template + void operator()(T const&, std::string_view, std::size_t, SimpleWriterTag); + + bool containsOnlyNamespaces(NamespaceInfo const& I) const; + +#define INFO(Type) void write##Type(Type##Info const&, std::string_view, std::size_t); +#include + +#define INFO(Type) void write##Type(Type##Info const&, std::string_view, std::size_t, SimpleWriterTag); +#include + + template + void writeClassLike(T const& I, std::string_view, std::size_t); + template + void writeClassLike(T const& I, std::string_view, std::size_t, SimpleWriterTag); + + void openTemplate(std::unique_ptr const& I); + void closeTemplate(std::unique_ptr const& I); +}; + +} // mrdocs +} // clang + +#endif diff --git a/src/lib/Support/Generator.cpp b/src/lib/Support/Generator.cpp index 39f423f12..4ce37f2de 100644 --- a/src/lib/Support/Generator.cpp +++ b/src/lib/Support/Generator.cpp @@ -54,12 +54,25 @@ build( return buildOne(fileName.str(), corpus); } +Error +Generator:: +buildOne( + std::ostream& os, + Corpus const& corpus) const +{ + std::ostringstream noopStream; + return buildOne(os, corpus, GenerationContext{noopStream, ""}); +} + Error Generator:: buildOne( std::string_view fileName, Corpus const& corpus) const { + namespace path = llvm::sys::path; + using SmallString = llvm::SmallString<0>; + std::string dir = files::getParentDir(fileName); if(auto err = files::createDirectory(dir)) return err; @@ -78,9 +91,29 @@ buildOne( return formatError("std::ofstream threw \"{}\"", ex.what()); } + SmallString tagFileName(fileName); + path::replace_extension(tagFileName, "tag.xml"); + std::ofstream osTagfile; + try + { + osTagfile.open(tagFileName.str().str(), + std::ios_base::binary | + std::ios_base::out | + std::ios_base::trunc // | std::ios_base::noreplace + ); + } + catch(std::exception const& ex) + { + return formatError("std::ofstream(\"{}\") threw \"{}\"", tagFileName.str().str(), ex.what()); + } + try { - return buildOne(os, corpus); + return buildOne( + os, + corpus, + GenerationContext{osTagfile, tagFileName.str().str()} + ); } catch(std::exception const& ex) {