clang-tools 22.0.0git
Generators.cpp
Go to the documentation of this file.
1//===-- Generators.cpp - Generator Registry ----------------------*- C++-*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "Generators.h"
10#include "support/File.h"
11#include "llvm/Support/TimeProfiler.h"
12
13LLVM_INSTANTIATE_REGISTRY(clang::doc::GeneratorRegistry)
14
15using namespace llvm;
16using namespace llvm::json;
17using namespace llvm::mustache;
18
19namespace clang {
20namespace doc {
21
22llvm::Expected<std::unique_ptr<Generator>>
23findGeneratorByName(llvm::StringRef Format) {
24 for (const auto &Generator : GeneratorRegistry::entries()) {
25 if (Generator.getName() != Format)
26 continue;
27 return Generator.instantiate();
28 }
29 return createStringError(llvm::inconvertibleErrorCode(),
30 "can't find generator: " + Format);
31}
32
33// Enum conversion
34
35std::string getTagType(TagTypeKind AS) {
36 switch (AS) {
37 case TagTypeKind::Class:
38 return "class";
39 case TagTypeKind::Union:
40 return "union";
41 case TagTypeKind::Interface:
42 return "interface";
43 case TagTypeKind::Struct:
44 return "struct";
45 case TagTypeKind::Enum:
46 return "enum";
47 }
48 llvm_unreachable("Unknown TagTypeKind");
49}
50
51Error createFileOpenError(StringRef FileName, std::error_code EC) {
52 return createFileError("cannot open file " + FileName, EC);
53}
54
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
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> JSONDirPath(RootDir);
88 SmallString<128> DocsDirPath(RootDir);
89 {
90 TimeTraceScope TS("Create Output Directories");
91 sys::path::append(JSONDirPath, "json");
92 if (auto EC = sys::fs::create_directories(JSONDirPath))
93 return createFileError(JSONDirPath, EC);
94 sys::path::append(DocsDirPath, DirName);
95 if (auto EC = sys::fs::create_directories(DocsDirPath))
96 return createFileError(DocsDirPath, EC);
97 }
98
99 {
100 llvm::TimeTraceScope TS("Iterate JSON files");
101 std::error_code EC;
102 sys::fs::recursive_directory_iterator JSONIter(JSONDirPath, EC);
103 std::vector<json::Value> JSONFiles;
104 JSONFiles.reserve(Infos.size());
105 if (EC)
106 return createStringError("Failed to create directory iterator.");
107
108 while (JSONIter != sys::fs::recursive_directory_iterator()) {
109 // create the same directory structure in the docs format dir
110 if (JSONIter->type() == sys::fs::file_type::directory_file) {
111 SmallString<128> DocsClonedPath(JSONIter->path());
112 sys::path::replace_path_prefix(DocsClonedPath, JSONDirPath,
113 DocsDirPath);
114 if (auto EC = sys::fs::create_directories(DocsClonedPath)) {
115 return createFileError(DocsClonedPath, EC);
116 }
117 }
118
119 if (EC)
120 return createFileError("Failed to iterate: " + JSONIter->path(), EC);
121
122 auto Path = StringRef(JSONIter->path());
123 if (!Path.ends_with(".json")) {
124 JSONIter.increment(EC);
125 continue;
126 }
127
128 auto File = MemoryBuffer::getFile(Path);
129 if (EC = File.getError(); EC) {
130 unsigned ID = CDCtx.Diags.getCustomDiagID(DiagnosticsEngine::Warning,
131 "Failed to open file: %0 %1");
132 CDCtx.Diags.Report(ID) << Path << EC.message();
133 JSONIter.increment(EC);
134 continue;
135 }
136
137 auto Parsed = json::parse((*File)->getBuffer());
138 if (!Parsed)
139 return Parsed.takeError();
140 auto ValidJSON = Parsed.get();
141
142 std::error_code FileErr;
143 SmallString<128> DocsFilePath(JSONIter->path());
144 sys::path::replace_path_prefix(DocsFilePath, JSONDirPath, DocsDirPath);
145 sys::path::replace_extension(DocsFilePath, DirName);
146 raw_fd_ostream InfoOS(DocsFilePath, FileErr, sys::fs::OF_None);
147 if (FileErr)
148 return createFileOpenError(Path, FileErr);
149
150 auto RelativeRootPath = getRelativePathToRoot(DocsFilePath, DocsDirPath);
151 auto InfoTypeStr =
152 getInfoTypeStr(Parsed->getAsObject(), sys::path::stem(DocsFilePath));
153 if (!InfoTypeStr)
154 return InfoTypeStr.takeError();
155 if (Error Err = generateDocForJSON(*Parsed, InfoOS, CDCtx,
156 InfoTypeStr.get(), RelativeRootPath))
157 return Err;
158 JSONIter.increment(EC);
159 }
160 }
161
162 return Error::success();
163}
164
165Expected<std::string> MustacheGenerator::getInfoTypeStr(Object *Info,
166 StringRef Filename) {
167 // Checking for a USR ensures that only the special top-level index file is
168 // caught here, since it is not an Info.
169 if (Filename == "index" && !Info->get("USR"))
170 return "index";
171 auto StrValue = (*Info)["InfoType"];
172 if (StrValue.kind() != json::Value::Kind::String)
173 return createStringError("JSON file '%s' does not contain key: 'InfoType'.",
174 Filename.str().c_str());
175 auto ObjTypeStr = StrValue.getAsString();
176 if (!ObjTypeStr.has_value())
177 return createStringError(
178 "JSON file '%s' does not contain 'InfoType' field as a string.",
179 Filename.str().c_str());
180 return ObjTypeStr.value().str();
181}
182
183SmallString<128>
185 StringRef DocsRootPath) {
186 SmallString<128> PathVec(PathToFile);
187 // Remove filename, or else the relative path will have an extra "../"
188 sys::path::remove_filename(PathVec);
189 return computeRelativePath(DocsRootPath, PathVec);
190}
191
193 return llvm::Error::success();
194}
195
196// A function to add a reference to Info in Idx.
197// Given an Info X with the following namespaces: [B,A]; a reference to X will
198// be added in the children of a reference to B, which should be also a child of
199// a reference to A, where A is a child of Idx.
200// Idx
201// |-- A
202// |--B
203// |--X
204// If the references to the namespaces do not exist, they will be created. If
205// the references already exist, the same one will be used.
207 // Index pointer that will be moving through Idx until the first parent
208 // namespace of Info (where the reference has to be inserted) is found.
209 Index *I = &Idx;
210 // The Namespace vector includes the upper-most namespace at the end so the
211 // loop will start from the end to find each of the namespaces.
212 for (const auto &R : llvm::reverse(Info->Namespace)) {
213 // Look for the current namespace in the children of the index I is
214 // pointing.
215 auto It = llvm::find(I->Children, R.USR);
216 if (It != I->Children.end()) {
217 // If it is found, just change I to point the namespace reference found.
218 I = &*It;
219 } else {
220 // If it is not found a new reference is created
221 I->Children.emplace_back(R.USR, R.Name, R.RefType, R.Path);
222 // I is updated with the reference of the new namespace reference
223 I = &I->Children.back();
224 }
225 }
226 // Look for Info in the vector where it is supposed to be; it could already
227 // exist if it is a parent namespace of an Info already passed to this
228 // function.
229 auto It = llvm::find(I->Children, Info->USR);
230 if (It == I->Children.end()) {
231 // If it is not in the vector it is inserted
232 I->Children.emplace_back(Info->USR, Info->extractName(), Info->IT,
233 Info->Path);
234 } else {
235 // If it not in the vector we only check if Path and Name are not empty
236 // because if the Info was included by a namespace it may not have those
237 // values.
238 if (It->Path.empty())
239 It->Path = Info->Path;
240 if (It->Name.empty())
241 It->Name = Info->extractName();
242 }
243}
244
245// This anchor is used to force the linker to link in the generated object file
246// and thus register the generators.
248[[maybe_unused]] static int MDGeneratorAnchorDest = MDGeneratorAnchorSource;
251} // namespace doc
252} // namespace clang
static void addInfoToIndex(Index &Idx, const doc::Info *Info)
virtual llvm::Error createResources(ClangDocContext &CDCtx)
Error generateDocumentation(StringRef RootDir, llvm::StringMap< std::unique_ptr< doc::Info > > Infos, const ClangDocContext &CDCtx, std::string DirName) override
static Expected< std::unique_ptr< MustacheTemplateFile > > createMustacheFile(StringRef FileName)
Definition Generators.h:67
std::string getTagType(TagTypeKind AS)
llvm::Expected< std::unique_ptr< Generator > > findGeneratorByName(llvm::StringRef Format)
volatile int JSONGeneratorAnchorSource
llvm::SmallString< 128 > computeRelativePath(llvm::StringRef Destination, llvm::StringRef Origin)
Definition File.cpp:31
static int MDGeneratorAnchorDest
volatile int HTMLGeneratorAnchorSource
volatile int YAMLGeneratorAnchorSource
Error createFileOpenError(StringRef FileName, std::error_code EC)
static int JSONGeneratorAnchorDest
volatile int MDGeneratorAnchorSource
llvm::Registry< Generator > GeneratorRegistry
Definition Generators.h:49
static int HTMLGeneratorAnchorDest
static int YAMLGeneratorAnchorDest
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Some operations such as code completion produce a set of candidates.
Definition Generators.h:145
clang::DiagnosticsEngine & Diags
std::vector< Index > Children
A base struct for Infos.
llvm::SmallString< 128 > Path
llvm::SmallString< 16 > extractName() const
llvm::SmallVector< Reference, 4 > Namespace
llvm::Error generateDocumentation(StringRef RootDir, llvm::StringMap< std::unique_ptr< doc::Info > > Infos, const clang::doc::ClangDocContext &CDCtx, std::string DirName) override
The main orchestrator for Mustache-based documentation.
Expected< std::string > getInfoTypeStr(llvm::json::Object *Info, StringRef Filename)
virtual llvm::Error setupTemplateFiles(const ClangDocContext &CDCtx)=0
Initializes the template files from disk and calls setupTemplate to register partials.
virtual llvm::Error generateDocForJSON(llvm::json::Value &JSON, llvm::raw_fd_ostream &OS, const ClangDocContext &CDCtx, StringRef ObjectTypeStr, StringRef RelativeRootPath)=0
Populates templates with data from JSON and calls any specifics for the format.
SmallString< 128 > getRelativePathToRoot(StringRef PathToFile, StringRef DocsRootPath)
Used to find the relative path from the file to the format's docs root.
llvm::Error setupTemplate(std::unique_ptr< MustacheTemplateFile > &Template, StringRef TemplatePath, std::vector< std::pair< StringRef, StringRef > > Partials)
Registers partials to templates.