Skip to content

Commit 14e9579

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

File tree

11 files changed

+772
-54
lines changed

11 files changed

+772
-54
lines changed

.github/workflows/ci.yml

Lines changed: 38 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ jobs:
7777
libcxx-runtimes: libcxx;libcxxabi{{#if (ine os 'windows') }};libunwind{{/if}}
7878
libcxx-targets: cxx {{#if (ine os 'windows') }}cxxabi unwind{{/if}} install-cxx {{#if (ine os 'windows') }}install-cxxabi install-unwind{{/if}}
7979
libcxx-cxxflags: {{#if (ieq os 'windows') }}-D__ORDER_LITTLE_ENDIAN__=1234 -D__ORDER_BIG_ENDIAN__=4321 -D__BYTE_ORDER__=__ORDER_LITTLE_ENDIAN__{{/if}}
80-
libcxx-cmake-args: -D LLVM_ENABLE_RUNTIMES="{{ libcxx-runtimes }}" {{#if (ieq os 'windows') }}-D LIBCXXABI_USE_LLVM_UNWINDER=OFF -D LIBCXXABI_ENABLE_SHARED=OFF -D LIBCXXABI_ENABLE_STATIC=ON -D LIBCXX_ENABLE_SHARED=OFF -D LIBCXX_NO_VCRUNTIME=ON{{/if}} {{#if (ieq os 'macos') }}-D CMAKE_OSX_ARCHITECTURES=""{{/if}}
80+
libcxx-cmake-args: -D LLVM_ENABLE_RUNTIMES="{{ libcxx-runtimes }}" {{#if (ieq os 'windows') }}-D LIBCXXABI_USE_LLVM_UNWINDER=OFF -D LIBCXXABI_ENABLE_SHARED=OFF -D LIBCXXABI_ENABLE_STATIC=ON -D LIBCXX_ENABLE_SHARED=OFF -D LIBCXX_NO_VCRUNTIME=ON{{/if}} {{#if (ieq os 'macos') }}-D CMAKE_OSX_ARCHITECTURES=""{{/if}}
8181
mrdocs-ccflags: {{ ccflags }} {{#if (eq compiler 'gcc') }}-static{{/if}} {{#if asan }}-static-libasan{{/if}} {{#if tsan }}-static-libtsan{{/if}}
8282
mrdocs-cxxflags: {{ cxxflags }} {{#if (eq compiler 'gcc') }}-static{{/if}} {{#if asan }}-static-libasan{{/if}} {{#if tsan }}-static-libtsan{{/if}}
8383
mrdocs-package-generators: {{#if (ieq os 'windows') }}7Z ZIP WIX{{else}}TGZ TXZ{{/if}}
@@ -233,19 +233,19 @@ jobs:
233233
build-type: Release
234234
extra-args: |
235235
-D LIBXML2_WITH_PROGRAMS=ON
236-
-D LIBXML2_WITH_FTP=OFF
236+
-D LIBXML2_WITH_FTP=OFF
237237
-D LIBXML2_WITH_HTTP=OFF
238-
-D LIBXML2_WITH_ICONV=OFF
238+
-D LIBXML2_WITH_ICONV=OFF
239239
-D LIBXML2_WITH_LEGACY=OFF
240-
-D LIBXML2_WITH_LZMA=OFF
241-
-D LIBXML2_WITH_ZLIB=OFF
240+
-D LIBXML2_WITH_LZMA=OFF
241+
-D LIBXML2_WITH_ZLIB=OFF
242242
-D LIBXML2_WITH_ICU=OFF
243-
-D LIBXML2_WITH_TESTS=OFF
244-
-D LIBXML2_WITH_HTML=ON
243+
-D LIBXML2_WITH_TESTS=OFF
244+
-D LIBXML2_WITH_HTML=ON
245245
-D LIBXML2_WITH_C14N=ON
246-
-D LIBXML2_WITH_CATALOG=ON
247-
-D LIBXML2_WITH_DEBUG=ON
248-
-D LIBXML2_WITH_ISO8859X=ON
246+
-D LIBXML2_WITH_CATALOG=ON
247+
-D LIBXML2_WITH_DEBUG=ON
248+
-D LIBXML2_WITH_ISO8859X=ON
249249
-D LIBXML2_WITH_MEM_DEBUG=OFF
250250
-D LIBXML2_WITH_MODULES=ON
251251
-D LIBXML2_WITH_OUTPUT=ON
@@ -400,7 +400,7 @@ jobs:
400400
run: |
401401
# Find python
402402
python=$(command -v python3 || command -v python)
403-
# The schema in this branch is up to date
403+
# The schema in this branch is up to date
404404
"$python" ./util/generate-yaml-schema.py --check
405405
# The schema in the docs folder is valid
406406
npx -y -p ajv-cli -- ajv compile -s docs/mrdocs.schema.json
@@ -428,12 +428,12 @@ jobs:
428428
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
429429
run: |
430430
set -x
431-
431+
432432
if [ -z "$CODECOV_TOKEN" ]; then
433433
echo "CODECOV_TOKEN is not set. Skipping coverage report."
434434
exit 0
435435
fi
436-
436+
437437
# Find gcov
438438
gcov_tool="gcov"
439439
for version in "${{steps.setup-cpp.outputs.version-major}}.${{steps.setup-cpp.outputs.version-minor}}" "${{steps.setup-cpp.outputs.version-major}}"; do
@@ -442,18 +442,18 @@ jobs:
442442
break
443443
fi
444444
done
445-
445+
446446
for dir in "./build"; do
447447
# Generate reports
448448
echo "Generate report: $dir"
449449
lcov --rc lcov_branch_coverage=0 --gcov-tool "$gcov_tool" --directory "$dir" --capture --output-file "$dir/all.info"
450450
lcov --rc lcov_branch_coverage=0 --list "$dir/all.info"
451-
452-
# Upload to codecov
451+
452+
# Upload to codecov
453453
echo "Upload to codecov: $dir"
454454
bash <(curl -s https://codecov.io/bash) -f "$dir/all.info"
455455
done
456-
456+
457457
# Summary
458458
echo "# Coverage" >> $GITHUB_STEP_SUMMARY
459459
echo "" >> $GITHUB_STEP_SUMMARY
@@ -516,17 +516,17 @@ jobs:
516516
- name: Install MrDocs from Package
517517
run: |
518518
set -x
519-
519+
520520
# Delete packages/_CPack_Packages files from previous runs
521521
rm -rf packages/_CPack_Packages
522-
522+
523523
# Print tree structure
524524
find packages -print | sed 's;[^/]*/;|____;g;s;____|; |;g'
525-
525+
526526
if [[ ${{ runner.os }} != 'Windows' ]]; then
527527
dest_dir="$HOME/local"
528-
mkdir -p "$dest_dir"
529-
find packages -maxdepth 1 -name 'MrDocs-*.tar.gz' -exec tar -vxzf {} -C $dest_dir --strip-components=1 \;
528+
mkdir -p "$dest_dir"
529+
find packages -maxdepth 1 -name 'MrDocs-*.tar.gz' -exec tar -vxzf {} -C $dest_dir --strip-components=1 \;
530530
else
531531
dest_dir="$GITHUB_WORKSPACE/usr/local"
532532
dest_dir=$(echo "$dest_dir" | sed 's/\\/\//g')
@@ -541,7 +541,7 @@ jobs:
541541
fi
542542
MRDOCS_ROOT="$dest_dir"
543543
echo -e "MRDOCS_ROOT=$MRDOCS_ROOT" >> $GITHUB_ENV
544-
echo -e "$MRDOCS_ROOT/bin" >> $GITHUB_PATH
544+
echo -e "$MRDOCS_ROOT/bin" >> $GITHUB_PATH
545545
$MRDOCS_ROOT/bin/mrdocs --version
546546
547547
- name: Clone Boost.URL
@@ -571,7 +571,7 @@ jobs:
571571
exit 1
572572
fi
573573
fi
574-
574+
575575
# Step 3: Ensure llvm-symbolizer is in your PATH
576576
llvm_bin_path=$(brew --prefix)/opt/llvm/bin
577577
PATH="$PATH:$llvm_bin_path"
@@ -582,12 +582,12 @@ jobs:
582582
else
583583
echo "llvm-symbolizer found at: $LLVM_SYMBOLIZER_PATH"
584584
fi
585-
585+
586586
# Step 4: Export LLVM_SYMBOLIZER_PATH environment variable
587587
export LLVM_SYMBOLIZER_PATH="$LLVM_SYMBOLIZER_PATH"
588588
echo -e "LLVM_SYMBOLIZER_PATH=$LLVM_SYMBOLIZER_PATH" >> $GITHUB_ENV
589589
echo "Environment variable LLVM_SYMBOLIZER_PATH set to: $LLVM_SYMBOLIZER_PATH"
590-
590+
591591
- name: Generate Landing Page
592592
working-directory: docs/website
593593
run: |
@@ -600,7 +600,7 @@ jobs:
600600
- name: Generate Remote Documentation
601601
working-directory: docs
602602
run: |
603-
# This playbook renders the documentation
603+
# This playbook renders the documentation
604604
# content for the website. It includes
605605
# master, develop, and tags.
606606
GH_TOKEN="${{ secrets.GITHUB_TOKEN }}"
@@ -620,7 +620,7 @@ jobs:
620620
- name: Generate Local Documentation
621621
working-directory: docs
622622
run: |
623-
# This playbook allows us to render the
623+
# This playbook allows us to render the
624624
# documentation content and visualize it
625625
# before a workflow that pushes to the
626626
# website is triggered.
@@ -638,17 +638,18 @@ jobs:
638638
"xml"
639639
"html"
640640
)
641-
641+
642642
# Generate the demos for each variant and generator
643643
for variant in single multi; do
644644
for generator in "${generators[@]}"; do
645645
[[ $generator = xml && $variant = multi ]] && continue
646646
[[ $variant = multi ]] && multipage="true" || multipage="false"
647-
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/$generator: $(find demos/boost-url/$variant/$generator -type f | wc -l)"
647+
echo "Generating demos for $variant/$generator/"
648+
mrdocs --config="$(pwd)/boost/libs/url/doc/mrdocs.yml" "../CMakeLists.txt" --output="$(pwd)/demos/boost-url/$variant/$generator/" --multipage=$multipage --generate="$generator"
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
651-
652+
652653
# Compress demos for the artifact
653654
tar -cjf $(pwd)/demos.tar.gz -C $(pwd)/demos --strip-components 1 .
654655
echo "demos_path=$(pwd)/demos.tar.gz" >> $GITHUB_ENV
@@ -757,7 +758,7 @@ jobs:
757758
# Copy files: This step will copy the landing page and the documentation to www.mrdocs.com
758759
chmod 755 -R $(pwd)/build/website
759760
scp -o StrictHostKeyChecking=no -r $(pwd)/build/website/* [email protected]:/var/www/mrdox.com/
760-
761+
761762
# Remove previous demos associated with this tag
762763
demo_dir="/var/www/mrdox.com/demos/${{ github.ref_name }}"
763764
ssh -o StrictHostKeyChecking=no [email protected] "rm -rf $demo_dir/boost-url; mkdir -p $demo_dir/boost-url"
@@ -855,10 +856,10 @@ jobs:
855856
run: |
856857
# LLVM is be installed with the default compiler
857858
set -x
858-
859+
859860
# Compress the LLVM installation
860861
cd ../third-party/llvm-project
861-
862+
862863
# Use 7z on windows
863864
if [[ ${{ runner.os }} == 'Windows' ]]; then
864865
7z a -t7z -m0=lzma2 -mx=9 -mfb=64 -md=32m -ms=on ${{ matrix.llvm-archive-filename }} install
@@ -871,15 +872,15 @@ jobs:
871872
working-directory: ../third-party/llvm-project
872873
run: |
873874
set -x
874-
875+
875876
# Ensure required commands exist
876877
for cmd in ssh-keyscan ssh-agent ssh-add scp; do
877878
if ! command -v $cmd >/dev/null; then
878879
echo "$cmd not found"
879880
exit 1
880881
fi
881882
done
882-
883+
883884
# Add SSH key
884885
mkdir -p ~/.ssh
885886
ssh-keyscan dev-websites.cpp.al >> ~/.ssh/known_hosts

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": "<output>/reference.tag.xml",
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

include/mrdocs/Generator.hpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,29 @@ class MRDOCS_VISIBLE
182182
Corpus const& corpus) const;
183183
};
184184

185+
/** Return the full path for single page output.
186+
187+
This function determines the full path for a single-page output file
188+
based on the provided `outputPath` and file `extension`.
189+
190+
If the `outputPath` already exists:
191+
- If it is a directory, appends the default file name with the provided extension.
192+
- If it is a file, uses the provided `outputPath` directly.
193+
194+
If the `outputPath` does not exist:
195+
- If it ends with a '/', assumes it is a directory and appends the default file name.
196+
- Otherwise, it returns an error because the path is ambiguous.
197+
198+
@return The full path or an error if the `outputPath` is ambiguous.
199+
200+
@param outputPath The specified output path, which can be a directory or file.
201+
@param extension The file extension to use for single-page output.
202+
*/
203+
Expected<std::string>
204+
getSinglePageFullPath(
205+
std::string_view outputPath,
206+
std::string_view extension);
207+
185208
} // mrdocs
186209
} // clang
187210

src/lib/Gen/hbs/HandlebarsGenerator.cpp

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,17 @@
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 {
@@ -30,6 +40,30 @@ createEscapeFn(HandlebarsGenerator const& gen)
3040
};
3141
}
3242

43+
namespace {
44+
45+
/** Generate a Tagfile to enable cross-referencing between documentation symbols. */
46+
Expected<void>
47+
generateTagfile(
48+
HandlebarsCorpus const& hbsCorpus,
49+
std::string_view tagFilePath,
50+
std::string_view outputFilesBasePath
51+
)
52+
{
53+
MRDOCS_ASSERT(!tagFilePath.empty());
54+
55+
auto tagFileWriterEx = TagfileWriter::create(hbsCorpus, tagFilePath, outputFilesBasePath);
56+
MRDOCS_CHECK_OR(tagFileWriterEx, Unexpected(tagFileWriterEx.error()));
57+
58+
auto tagfileWriter = std::move(*tagFileWriterEx);
59+
tagfileWriter.initialize();
60+
visit(hbsCorpus->globalNamespace(), tagfileWriter);
61+
tagfileWriter.finalize();
62+
return {};
63+
}
64+
65+
} // anonymous namespace
66+
3367
Expected<ExecutorGroup<Builder>>
3468
createExecutors(
3569
HandlebarsGenerator const& gen,
@@ -79,7 +113,17 @@ build(
79113
{
80114
if (!corpus.config->multipage)
81115
{
82-
return Generator::build(outputPath, corpus);
116+
auto e = Generator::build(outputPath, corpus);
117+
if (e.has_value() && !corpus.config->tagfilePath.empty())
118+
{
119+
// Generate tagfile if specified
120+
auto const singlePageFileName = getSinglePageFullPath(outputPath, fileExtension());
121+
MRDOCS_CHECK_OR(singlePageFileName, Unexpected(singlePageFileName.error()));
122+
HandlebarsCorpus hbsCorpus = createDomCorpus(*this, corpus);
123+
return generateTagfile(hbsCorpus, corpus.config->tagfilePath, *singlePageFileName);
124+
}
125+
126+
return e;
83127
}
84128

85129
// Create corpus and executors
@@ -93,6 +137,12 @@ build(
93137
// Wait for all executors to finish and check errors
94138
auto errors = ex.wait();
95139
MRDOCS_CHECK_OR(errors.empty(), Unexpected(errors));
140+
141+
if (! corpus.config->tagfilePath.empty())
142+
{
143+
return generateTagfile(domCorpus, corpus.config->tagfilePath, outputPath);
144+
}
145+
96146
return {};
97147
}
98148

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, ' ');

0 commit comments

Comments
 (0)