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> 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
158Expected<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
172SmallString<128>
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
182 return llvm::Error::success();
183}
184
185// A function to add a reference to Info in Idx.
186// Given an Info X with the following namespaces: [B,A]; a reference to X will
187// be added in the children of a reference to B, which should be also a child of
188// a reference to A, where A is a child of Idx.
189// Idx
190// |-- A
191// |--B
192// |--X
193// If the references to the namespaces do not exist, they will be created. If
194// the references already exist, the same one will be used.
196 // Index pointer that will be moving through Idx until the first parent
197 // namespace of Info (where the reference has to be inserted) is found.
198 Index *I = &Idx;
199 // The Namespace vector includes the upper-most namespace at the end so the
200 // loop will start from the end to find each of the namespaces.
201 for (const auto &R : llvm::reverse(Info->Namespace)) {
202 // Look for the current namespace in the children of the index I is
203 // pointing.
204 auto It = llvm::find(I->Children, R.USR);
205 if (It != I->Children.end()) {
206 // If it is found, just change I to point the namespace reference found.
207 I = &*It;
208 } else {
209 // If it is not found a new reference is created
210 I->Children.emplace_back(R.USR, R.Name, R.RefType, R.Path);
211 // I is updated with the reference of the new namespace reference
212 I = &I->Children.back();
213 }
214 }
215 // Look for Info in the vector where it is supposed to be; it could already
216 // exist if it is a parent namespace of an Info already passed to this
217 // function.
218 auto It = llvm::find(I->Children, Info->USR);
219 if (It == I->Children.end()) {
220 // If it is not in the vector it is inserted
221 I->Children.emplace_back(Info->USR, Info->extractName(), Info->IT,
222 Info->Path);
223 } else {
224 // If it not in the vector we only check if Path and Name are not empty
225 // because if the Info was included by a namespace it may not have those
226 // values.
227 if (It->Path.empty())
228 It->Path = Info->Path;
229 if (It->Name.empty())
230 It->Name = Info->extractName();
231 }
232}
233
234// This anchor is used to force the linker to link in the generated object file
235// and thus register the generators.
237[[maybe_unused]] static int MDGeneratorAnchorDest = MDGeneratorAnchorSource;
239[[maybe_unused]] static int MHTMLGeneratorAnchorDest =
242} // namespace doc
243} // 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
static int MHTMLGeneratorAnchorDest
volatile int HTMLGeneratorAnchorSource
volatile int YAMLGeneratorAnchorSource
Error createFileOpenError(StringRef FileName, std::error_code EC)
static int JSONGeneratorAnchorDest
volatile int MDGeneratorAnchorSource
volatile int MHTMLGeneratorAnchorSource
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:146
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.