Skip to content

Commit 3e2bb7b

Browse files
committed
MustacheGenerator inherits from Generator, use generateDocumentation as base
1 parent 4881512 commit 3e2bb7b

File tree

8 files changed

+283
-209
lines changed

8 files changed

+283
-209
lines changed

clang-tools-extra/clang-doc/Generators.cpp

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,15 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "Generators.h"
10+
#include "support/File.h"
11+
#include "llvm/Support/TimeProfiler.h"
1012

1113
LLVM_INSTANTIATE_REGISTRY(clang::doc::GeneratorRegistry)
1214

15+
using namespace llvm;
16+
using namespace llvm::json;
17+
using namespace llvm::mustache;
18+
1319
namespace clang {
1420
namespace doc {
1521

@@ -42,6 +48,136 @@ std::string getTagType(TagTypeKind AS) {
4248
llvm_unreachable("Unknown TagTypeKind");
4349
}
4450

51+
Error createFileOpenError(StringRef FileName, std::error_code EC) {
52+
return createFileError("cannot open file " + FileName, EC);
53+
}
54+
55+
Error MustacheGenerator::setupTemplate(
56+
std::unique_ptr<MustacheTemplateFile> &Template, StringRef TemplatePath,
57+
std::vector<std::pair<StringRef, StringRef>> Partials) {
58+
auto T = MustacheTemplateFile::createMustacheFile(TemplatePath);
59+
if (Error Err = T.takeError())
60+
return Err;
61+
Template = std::move(T.get());
62+
for (const auto &[Name, FileName] : Partials)
63+
if (auto Err = Template->registerPartialFile(Name, FileName))
64+
return Err;
65+
return Error::success();
66+
}
67+
68+
Error MustacheGenerator::generateDocumentation(
69+
StringRef RootDir, StringMap<std::unique_ptr<doc::Info>> Infos,
70+
const clang::doc::ClangDocContext &CDCtx, std::string DirName) {
71+
{
72+
llvm::TimeTraceScope TS("Setup Templates");
73+
if (auto Err = setupTemplateFiles(CDCtx))
74+
return Err;
75+
}
76+
77+
{
78+
llvm::TimeTraceScope TS("Generate JSON for Mustache");
79+
if (auto JSONGenerator = findGeneratorByName("json")) {
80+
if (Error Err = JSONGenerator.get()->generateDocumentation(
81+
RootDir, std::move(Infos), CDCtx))
82+
return Err;
83+
} else
84+
return JSONGenerator.takeError();
85+
}
86+
87+
SmallString<128> JSONPath;
88+
sys::path::native(RootDir.str() + "/json", JSONPath);
89+
90+
{
91+
llvm::TimeTraceScope TS("Iterate JSON files");
92+
std::error_code EC;
93+
sys::fs::recursive_directory_iterator JSONIter(JSONPath, EC);
94+
std::vector<json::Value> JSONFiles;
95+
JSONFiles.reserve(Infos.size());
96+
if (EC)
97+
return createStringError("Failed to create directory iterator.");
98+
99+
SmallString<128> DocsDirPath(RootDir.str() + '/' + DirName);
100+
sys::path::native(DocsDirPath);
101+
if (auto EC = sys::fs::create_directories(DocsDirPath))
102+
return createFileError(DocsDirPath, EC);
103+
while (JSONIter != sys::fs::recursive_directory_iterator()) {
104+
// create the same directory structure in the docs format dir
105+
if (JSONIter->type() == sys::fs::file_type::directory_file) {
106+
SmallString<128> DocsClonedPath(JSONIter->path());
107+
sys::path::replace_path_prefix(DocsClonedPath, JSONPath, DocsDirPath);
108+
if (auto EC = sys::fs::create_directories(DocsClonedPath)) {
109+
return createFileError(DocsClonedPath, EC);
110+
}
111+
}
112+
113+
if (EC)
114+
return createFileError("Failed to iterate: " + JSONIter->path(), EC);
115+
116+
auto Path = StringRef(JSONIter->path());
117+
if (!Path.ends_with(".json")) {
118+
JSONIter.increment(EC);
119+
continue;
120+
}
121+
122+
auto File = MemoryBuffer::getFile(Path);
123+
if (EC = File.getError(); EC) {
124+
// TODO: Buffer errors to report later, look into using Clang
125+
// diagnostics.
126+
llvm::errs() << "Failed to open file: " << Path << " " << EC.message()
127+
<< '\n';
128+
}
129+
130+
auto Parsed = json::parse((*File)->getBuffer());
131+
if (!Parsed)
132+
return Parsed.takeError();
133+
auto ValidJSON = Parsed.get();
134+
135+
std::error_code FileErr;
136+
SmallString<128> DocsFilePath(JSONIter->path());
137+
sys::path::replace_path_prefix(DocsFilePath, JSONPath, DocsDirPath);
138+
sys::path::replace_extension(DocsFilePath, DirName);
139+
raw_fd_ostream InfoOS(DocsFilePath, FileErr, sys::fs::OF_None);
140+
if (FileErr)
141+
return createFileOpenError(Path, FileErr);
142+
143+
auto RelativeRootPath = getRelativePathToRoot(DocsFilePath, DocsDirPath);
144+
auto InfoTypeStr =
145+
getInfoTypeStr(Parsed->getAsObject(), sys::path::stem(DocsFilePath));
146+
if (!InfoTypeStr)
147+
return InfoTypeStr.takeError();
148+
if (Error Err = generateDocForJSON(*Parsed, InfoOS, CDCtx,
149+
InfoTypeStr.get(), RelativeRootPath))
150+
return Err;
151+
JSONIter.increment(EC);
152+
}
153+
}
154+
155+
return Error::success();
156+
}
157+
158+
Expected<std::string> MustacheGenerator::getInfoTypeStr(Object *Info,
159+
StringRef Filename) {
160+
auto StrValue = (*Info)["InfoType"];
161+
if (StrValue.kind() != json::Value::Kind::String)
162+
return createStringError("JSON file '%s' does not contain key: 'InfoType'.",
163+
Filename.str().c_str());
164+
auto ObjTypeStr = StrValue.getAsString();
165+
if (!ObjTypeStr.has_value())
166+
return createStringError(
167+
"JSON file '%s' does not contain 'InfoType' field as a string.",
168+
Filename.str().c_str());
169+
return ObjTypeStr.value().str();
170+
}
171+
172+
SmallString<128>
173+
MustacheGenerator::getRelativePathToRoot(StringRef PathToFile,
174+
StringRef DocsRootPath) {
175+
SmallString<128> PathVec(PathToFile);
176+
// Remove filename, or else the relative path will have an extra "../"
177+
sys::path::remove_filename(PathVec);
178+
return computeRelativePath(DocsRootPath, PathVec);
179+
}
180+
45181
llvm::Error Generator::createResources(ClangDocContext &CDCtx) {
46182
return llvm::Error::success();
47183
}

clang-tools-extra/clang-doc/Generators.h

Lines changed: 84 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414

1515
#include "Representation.h"
1616
#include "llvm/Support/Error.h"
17+
#include "llvm/Support/JSON.h"
18+
#include "llvm/Support/Mustache.h"
1719
#include "llvm/Support/Registry.h"
1820

1921
namespace clang {
@@ -27,10 +29,9 @@ class Generator {
2729

2830
// Write out the decl info for the objects in the given map in the specified
2931
// format.
30-
virtual llvm::Error
31-
generateDocs(StringRef RootDir,
32-
llvm::StringMap<std::unique_ptr<doc::Info>> Infos,
33-
const ClangDocContext &CDCtx) = 0;
32+
virtual llvm::Error generateDocumentation(
33+
StringRef RootDir, llvm::StringMap<std::unique_ptr<doc::Info>> Infos,
34+
const ClangDocContext &CDCtx, std::string DirName = "") = 0;
3435

3536
// This function writes a file with the index previously constructed.
3637
// It can be overwritten by any of the inherited generators.
@@ -52,6 +53,85 @@ findGeneratorByName(llvm::StringRef Format);
5253

5354
std::string getTagType(TagTypeKind AS);
5455

56+
llvm::Error createFileOpenError(StringRef FileName, std::error_code EC);
57+
58+
class MustacheTemplateFile {
59+
llvm::BumpPtrAllocator Allocator;
60+
llvm::StringSaver Saver;
61+
llvm::mustache::MustacheContext Ctx;
62+
llvm::mustache::Template T;
63+
std::unique_ptr<llvm::MemoryBuffer> Buffer;
64+
65+
public:
66+
static Expected<std::unique_ptr<MustacheTemplateFile>>
67+
createMustacheFile(StringRef FileName) {
68+
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> BufferOrError =
69+
llvm::MemoryBuffer::getFile(FileName);
70+
if (auto EC = BufferOrError.getError())
71+
return createFileOpenError(FileName, EC);
72+
return std::make_unique<MustacheTemplateFile>(
73+
std::move(BufferOrError.get()));
74+
}
75+
76+
llvm::Error registerPartialFile(StringRef Name, StringRef FileName) {
77+
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> BufferOrError =
78+
llvm::MemoryBuffer::getFile(FileName);
79+
if (auto EC = BufferOrError.getError())
80+
return createFileOpenError(FileName, EC);
81+
82+
std::unique_ptr<llvm::MemoryBuffer> Buffer = std::move(BufferOrError.get());
83+
StringRef FileContent = Buffer->getBuffer();
84+
T.registerPartial(Name.str(), FileContent.str());
85+
return llvm::Error::success();
86+
}
87+
88+
void render(llvm::json::Value &V, raw_ostream &OS) { T.render(V, OS); }
89+
90+
MustacheTemplateFile(std::unique_ptr<llvm::MemoryBuffer> &&B)
91+
: Saver(Allocator), Ctx(Allocator, Saver), T(B->getBuffer(), Ctx),
92+
Buffer(std::move(B)) {}
93+
};
94+
95+
struct MustacheGenerator : public Generator {
96+
Expected<std::string> getInfoTypeStr(llvm::json::Object *Info,
97+
StringRef Filename);
98+
99+
/// Used to find the relative path from the file to the format's docs root.
100+
/// Mainly used for the HTML resource paths.
101+
SmallString<128> getRelativePathToRoot(StringRef PathToFile,
102+
StringRef DocsRootPath);
103+
virtual ~MustacheGenerator() = default;
104+
105+
/// Initializes the template files from disk and calls setupTemplate to
106+
/// register partials
107+
virtual llvm::Error setupTemplateFiles(const ClangDocContext &CDCtx) = 0;
108+
109+
/// Populates templates with data from JSON and calls any specifics for the
110+
/// format. For example, for HTML it will render the paths for CSS and JS.
111+
virtual llvm::Error generateDocForJSON(llvm::json::Value &JSON,
112+
llvm::raw_fd_ostream &OS,
113+
const ClangDocContext &CDCtx,
114+
StringRef ObjectTypeStr,
115+
StringRef RelativeRootPath) = 0;
116+
117+
/// Registers partials to templates.
118+
llvm::Error
119+
setupTemplate(std::unique_ptr<MustacheTemplateFile> &Template,
120+
StringRef TemplatePath,
121+
std::vector<std::pair<StringRef, StringRef>> Partials);
122+
123+
/// \brief The main orchestrator for Mustache-based documentation.
124+
///
125+
/// 1. Initializes templates files from disk by calling setupTemplateFiles.
126+
/// 2. Calls the JSON generator to write JSON to disk.
127+
/// 3. Iterates over the JSON files, recreates the directory structure from
128+
/// JSON, and calls generateDocForJSON for each file.
129+
/// 4. A file of the desired format is created.
130+
llvm::Error generateDocumentation(
131+
StringRef RootDir, llvm::StringMap<std::unique_ptr<doc::Info>> Infos,
132+
const clang::doc::ClangDocContext &CDCtx, std::string DirName) override;
133+
};
134+
55135
// This anchor is used to force the linker to link in the generated object file
56136
// and thus register the generators.
57137
extern volatile int YAMLGeneratorAnchorSource;

clang-tools-extra/clang-doc/HTMLGenerator.cpp

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -897,20 +897,19 @@ class HTMLGenerator : public Generator {
897897
public:
898898
static const char *Format;
899899

900-
llvm::Error generateDocs(StringRef RootDir,
901-
llvm::StringMap<std::unique_ptr<doc::Info>> Infos,
902-
const ClangDocContext &CDCtx) override;
900+
llvm::Error generateDocumentation(
901+
StringRef RootDir, llvm::StringMap<std::unique_ptr<doc::Info>> Infos,
902+
const ClangDocContext &CDCtx, std::string DirName) override;
903903
llvm::Error createResources(ClangDocContext &CDCtx) override;
904904
llvm::Error generateDocForInfo(Info *I, llvm::raw_ostream &OS,
905905
const ClangDocContext &CDCtx) override;
906906
};
907907

908908
const char *HTMLGenerator::Format = "html";
909909

910-
llvm::Error
911-
HTMLGenerator::generateDocs(StringRef RootDir,
912-
llvm::StringMap<std::unique_ptr<doc::Info>> Infos,
913-
const ClangDocContext &CDCtx) {
910+
llvm::Error HTMLGenerator::generateDocumentation(
911+
StringRef RootDir, llvm::StringMap<std::unique_ptr<doc::Info>> Infos,
912+
const ClangDocContext &CDCtx, std::string DirName) {
914913
// Track which directories we already tried to create.
915914
llvm::StringSet<> CreatedDirs;
916915

0 commit comments

Comments
 (0)