Skip to content

Commit 14cea16

Browse files
committed
feat: tagfiles generation for cross-referencing in doxygen format
1 parent b3ac35d commit 14cea16

File tree

9 files changed

+700
-3
lines changed

9 files changed

+700
-3
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -644,8 +644,9 @@ jobs:
644644
for generator in "${generators[@]}"; do
645645
[[ $generator = xml && $variant = multi ]] && continue
646646
[[ $variant = multi ]] && multipage="true" || multipage="false"
647+
echo "Generating demos for $variant/$generator"
647648
mrdocs --config="$(pwd)/boost/libs/url/doc/mrdocs.yml" "../CMakeLists.txt" --output="$(pwd)/demos/boost-url/$variant/$generator" --multipage=$multipage --generate="$generator"
648-
echo "Number of files in demos/boost-url/$variant/$format: $(find demos/boost-url/$variant/$format -type f | wc -l)"
649+
echo "Number of files in demos/boost-url/$variant/$generator: $(find demos/boost-url/$variant/$generator -type f | wc -l)"
649650
done
650651
done
651652

docs/mrdocs.schema.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,12 @@
244244
"title": "System include paths",
245245
"type": "array"
246246
},
247+
"tagfile-path": {
248+
"default": "",
249+
"description": "Specifies the full path (filename) where the generated tagfile should be saved. If left empty, no tagfile will be generated.",
250+
"title": "Path for the tagfile",
251+
"type": "string"
252+
},
247253
"use-system-stdlib": {
248254
"default": false,
249255
"description": "True if the compiler has to use just the system standard library. When set to true, the compiler uses the system standard library instead of the standard library provided by the compiler.",

include/mrdocs/Corpus.hpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,39 @@ class MRDOCS_VISIBLE
159159
}
160160
}
161161

162+
/** Visit the members of specified Info.
163+
164+
This function iterates the members of the specified
165+
Info `I`. For each member associated with a
166+
function with the same name as the member, the
167+
function object `f` is invoked with the member
168+
as the first argument, followed by `args...`.
169+
170+
When there are more than one member function
171+
with the same name, the function object `f` is
172+
invoked with an @ref OverloadSet as the first
173+
argument, followed by `args...`.
174+
175+
@param I The Info to traverse.
176+
@param pred The predicate to use to determine if the member should be visited.
177+
@param f The function to invoke with the member as the first argument, followed by `args...`.
178+
@param args The arguments to pass to the function.
179+
*/
180+
template <InfoParent T, class Pred, class F, class... Args>
181+
void
182+
traverseIf(
183+
T const& I, Pred&& pred, F&& f, Args&&... args) const
184+
{
185+
for (auto const& id : I.Members)
186+
{
187+
if (std::forward<Pred>(pred)(get(id)))
188+
{
189+
visit(get(id), std::forward<F>(f),
190+
std::forward<Args>(args)...);
191+
}
192+
}
193+
}
194+
162195
/** Visit the member overloads of specified ScopeInfo.
163196
164197
This function iterates the members of the

src/lib/Gen/hbs/HandlebarsGenerator.cpp

Lines changed: 79 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,73 @@
1515
#include "Builder.hpp"
1616
#include "MultiPageVisitor.hpp"
1717
#include "SinglePageVisitor.hpp"
18+
19+
#include "lib/Lib/TagfileWriter.hpp"
20+
#include "lib/Support/RawOstream.hpp"
21+
22+
#include <llvm/ADT/SmallString.h>
23+
#include <llvm/Support/FileSystem.h>
24+
#include <llvm/Support/Path.h>
25+
1826
#include <mrdocs/Support/Path.hpp>
27+
28+
#include <fstream>
1929
#include <sstream>
2030

2131
namespace clang {
2232
namespace mrdocs {
2333
namespace hbs {
2434

35+
namespace {
36+
37+
/** Generate a Tagfile for cross-referencing. */
38+
Error
39+
generateTagfile(
40+
HandlebarsCorpus const& hbsCorpus,
41+
std::string_view tagFilePath,
42+
std::string_view generatedFilesPath,
43+
bool isSinglePage)
44+
{
45+
if (tagFilePath.empty())
46+
return Error::success();
47+
48+
auto tagFileWriterEx = TagfileWriter::create(hbsCorpus, tagFilePath, generatedFilesPath, isSinglePage);
49+
if (!tagFileWriterEx)
50+
return tagFileWriterEx.error();
51+
52+
auto tagfileWriter = std::move(*tagFileWriterEx);
53+
tagfileWriter.initialize();
54+
visit(hbsCorpus->globalNamespace(), tagfileWriter);
55+
tagfileWriter.finalize();
56+
return Error::success();
57+
}
58+
59+
std::string
60+
getSinglePageFileName(
61+
std::string_view outputPath,
62+
std::string_view extension)
63+
{
64+
namespace path = llvm::sys::path;
65+
66+
using SmallString = llvm::SmallString<0>;
67+
68+
SmallString ext(".");
69+
ext += extension;
70+
71+
SmallString fileName(outputPath);
72+
if(path::extension(outputPath).compare_insensitive(ext) != 0)
73+
{
74+
// directory specified
75+
path::append(fileName, "reference");
76+
path::replace_extension(fileName, ext);
77+
}
78+
79+
return fileName.str().str();
80+
}
81+
82+
83+
} // anonymous namespace
84+
2585
Expected<ExecutorGroup<Builder>>
2686
createExecutors(
2787
HandlebarsCorpus const& hbsCorpus)
@@ -70,7 +130,16 @@ build(
70130
{
71131
if (!corpus.config->multipage)
72132
{
73-
return Generator::build(outputPath, corpus);
133+
Error e = Generator::build(outputPath, corpus);
134+
if (!e.failed() && !corpus.config->tagfilePath.empty())
135+
{
136+
// Generate tagfile if specified
137+
auto const singlePageFileName = getSinglePageFileName(outputPath, fileExtension());
138+
HandlebarsCorpus hbsCorpus = createDomCorpus(*this, corpus);
139+
return generateTagfile(hbsCorpus, corpus.config->tagfilePath, singlePageFileName, true);
140+
}
141+
142+
return e;
74143
}
75144

76145
// Create corpus and executors
@@ -84,7 +153,15 @@ build(
84153

85154
// Wait for all executors to finish and check errors
86155
auto errors = ex->wait();
87-
MRDOCS_CHECK_OR(errors.empty(), errors);
156+
if (!errors.empty())
157+
{
158+
return Error(errors);
159+
}
160+
if (!corpus.config->tagfilePath.empty())
161+
{
162+
return generateTagfile(domCorpus, corpus.config->tagfilePath, outputPath, false);
163+
}
164+
88165
return Error::success();
89166
}
90167

src/lib/Gen/xml/XMLTags.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,9 @@ void
193193
XMLTags::
194194
nest(int levels)
195195
{
196+
if (!nesting_)
197+
return;
198+
196199
if(levels > 0)
197200
{
198201
indent_.append(levels * 2, ' ');

src/lib/Gen/xml/XMLTags.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ class XMLTags
186186
public:
187187
std::string indent_;
188188
llvm::raw_ostream& os_;
189+
bool nesting_ = true;
189190

190191
explicit
191192
XMLTags(
@@ -201,6 +202,7 @@ class XMLTags
201202
void write(dom::String const&,
202203
llvm::StringRef value = {}, Attributes = {});
203204
void close(dom::String const&);
205+
void nesting(bool enable) noexcept { nesting_ = enable; }
204206

205207
void nest(int levels);
206208
};

src/lib/Lib/ConfigOptions.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,15 @@
148148
"default": "<mrdocs-root>/share/mrdocs/addons",
149149
"relativeto": "<config-dir>"
150150
},
151+
{
152+
"name": "tagfile-path",
153+
"brief": "Path for the tagfile",
154+
"details": "Specifies the full path (filename) where the generated tagfile should be saved. If left empty, no tagfile will be generated.",
155+
"type": "file-path",
156+
"must-exist": false,
157+
"default": "",
158+
"relativeto": "<config-dir>"
159+
},
151160
{
152161
"name": "legible-names",
153162
"brief": "Use legible names",

0 commit comments

Comments
 (0)