blob: fafe41eebb7796b8678118166ed5df8b9b4cf272 [file] [log] [blame] [edit]
//===-- Generators.cpp - Generator Registry ----------------------*- C++-*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "Generators.h"
#include "support/File.h"
#include "llvm/Support/TimeProfiler.h"
LLVM_INSTANTIATE_REGISTRY(clang::doc::GeneratorRegistry)
using namespace llvm;
using namespace llvm::json;
using namespace llvm::mustache;
namespace clang {
namespace doc {
llvm::Expected<std::unique_ptr<Generator>>
findGeneratorByName(llvm::StringRef Format) {
for (const auto &Generator : GeneratorRegistry::entries()) {
if (Generator.getName() != Format)
continue;
return Generator.instantiate();
}
return createStringError(llvm::inconvertibleErrorCode(),
"can't find generator: " + Format);
}
// Enum conversion
std::string getTagType(TagTypeKind AS) {
switch (AS) {
case TagTypeKind::Class:
return "class";
case TagTypeKind::Union:
return "union";
case TagTypeKind::Interface:
return "interface";
case TagTypeKind::Struct:
return "struct";
case TagTypeKind::Enum:
return "enum";
}
llvm_unreachable("Unknown TagTypeKind");
}
Error createFileOpenError(StringRef FileName, std::error_code EC) {
return createFileError("cannot open file " + FileName, EC);
}
Error MustacheGenerator::setupTemplate(
std::unique_ptr<MustacheTemplateFile> &Template, StringRef TemplatePath,
std::vector<std::pair<StringRef, StringRef>> Partials) {
auto T = MustacheTemplateFile::createMustacheFile(TemplatePath);
if (Error Err = T.takeError())
return Err;
Template = std::move(T.get());
for (const auto &[Name, FileName] : Partials)
if (auto Err = Template->registerPartialFile(Name, FileName))
return Err;
return Error::success();
}
Error MustacheGenerator::generateDocumentation(
StringRef RootDir, StringMap<std::unique_ptr<doc::Info>> Infos,
const clang::doc::ClangDocContext &CDCtx, std::string DirName) {
{
llvm::TimeTraceScope TS("Setup Templates");
if (auto Err = setupTemplateFiles(CDCtx))
return Err;
}
{
llvm::TimeTraceScope TS("Generate JSON for Mustache");
if (auto JSONGenerator = findGeneratorByName("json")) {
if (Error Err = JSONGenerator.get()->generateDocumentation(
RootDir, std::move(Infos), CDCtx))
return Err;
} else
return JSONGenerator.takeError();
}
SmallString<128> JSONDirPath(RootDir);
SmallString<128> DocsDirPath(RootDir);
{
TimeTraceScope TS("Create Output Directories");
sys::path::append(JSONDirPath, "json");
if (auto EC = sys::fs::create_directories(JSONDirPath))
return createFileError(JSONDirPath, EC);
sys::path::append(DocsDirPath, DirName);
if (auto EC = sys::fs::create_directories(DocsDirPath))
return createFileError(DocsDirPath, EC);
}
{
llvm::TimeTraceScope TS("Iterate JSON files");
std::error_code EC;
sys::fs::recursive_directory_iterator JSONIter(JSONDirPath, EC);
std::vector<json::Value> JSONFiles;
JSONFiles.reserve(Infos.size());
if (EC)
return createStringError("Failed to create directory iterator.");
while (JSONIter != sys::fs::recursive_directory_iterator()) {
// create the same directory structure in the docs format dir
if (JSONIter->type() == sys::fs::file_type::directory_file) {
SmallString<128> DocsClonedPath(JSONIter->path());
sys::path::replace_path_prefix(DocsClonedPath, JSONDirPath,
DocsDirPath);
if (auto EC = sys::fs::create_directories(DocsClonedPath)) {
return createFileError(DocsClonedPath, EC);
}
}
if (EC)
return createFileError("Failed to iterate: " + JSONIter->path(), EC);
auto Path = StringRef(JSONIter->path());
if (!Path.ends_with(".json")) {
JSONIter.increment(EC);
continue;
}
auto File = MemoryBuffer::getFile(Path);
if (EC = File.getError(); EC) {
unsigned ID = CDCtx.Diags.getCustomDiagID(DiagnosticsEngine::Warning,
"Failed to open file: %0 %1");
CDCtx.Diags.Report(ID) << Path << EC.message();
JSONIter.increment(EC);
continue;
}
auto Parsed = json::parse((*File)->getBuffer());
if (!Parsed)
return Parsed.takeError();
auto ValidJSON = Parsed.get();
std::error_code FileErr;
SmallString<128> DocsFilePath(JSONIter->path());
sys::path::replace_path_prefix(DocsFilePath, JSONDirPath, DocsDirPath);
sys::path::replace_extension(DocsFilePath, DirName);
raw_fd_ostream InfoOS(DocsFilePath, FileErr, sys::fs::OF_None);
if (FileErr)
return createFileOpenError(Path, FileErr);
auto RelativeRootPath = getRelativePathToRoot(DocsFilePath, DocsDirPath);
auto InfoTypeStr =
getInfoTypeStr(Parsed->getAsObject(), sys::path::stem(DocsFilePath));
if (!InfoTypeStr)
return InfoTypeStr.takeError();
if (Error Err = generateDocForJSON(*Parsed, InfoOS, CDCtx,
InfoTypeStr.get(), RelativeRootPath))
return Err;
JSONIter.increment(EC);
}
}
return Error::success();
}
Expected<std::string> MustacheGenerator::getInfoTypeStr(Object *Info,
StringRef Filename) {
// Checking for a USR ensures that only the special top-level index file is
// caught here, since it is not an Info.
if (Filename == "index" && !Info->get("USR"))
return "index";
auto StrValue = (*Info)["InfoType"];
if (StrValue.kind() != json::Value::Kind::String)
return createStringError("JSON file '%s' does not contain key: 'InfoType'.",
Filename.str().c_str());
auto ObjTypeStr = StrValue.getAsString();
if (!ObjTypeStr.has_value())
return createStringError(
"JSON file '%s' does not contain 'InfoType' field as a string.",
Filename.str().c_str());
return ObjTypeStr.value().str();
}
SmallString<128>
MustacheGenerator::getRelativePathToRoot(StringRef PathToFile,
StringRef DocsRootPath) {
SmallString<128> PathVec(PathToFile);
// Remove filename, or else the relative path will have an extra "../"
sys::path::remove_filename(PathVec);
return computeRelativePath(DocsRootPath, PathVec);
}
llvm::Error Generator::createResources(ClangDocContext &CDCtx) {
return llvm::Error::success();
}
// A function to add a reference to Info in Idx.
// Given an Info X with the following namespaces: [B,A]; a reference to X will
// be added in the children of a reference to B, which should be also a child of
// a reference to A, where A is a child of Idx.
// Idx
// |-- A
// |--B
// |--X
// If the references to the namespaces do not exist, they will be created. If
// the references already exist, the same one will be used.
void Generator::addInfoToIndex(Index &Idx, const doc::Info *Info) {
// Index pointer that will be moving through Idx until the first parent
// namespace of Info (where the reference has to be inserted) is found.
Index *I = &Idx;
// The Namespace vector includes the upper-most namespace at the end so the
// loop will start from the end to find each of the namespaces.
for (const auto &R : llvm::reverse(Info->Namespace)) {
// Look for the current namespace in the children of the index I is
// pointing.
auto It = llvm::find(I->Children, R.USR);
if (It != I->Children.end()) {
// If it is found, just change I to point the namespace reference found.
I = &*It;
} else {
// If it is not found a new reference is created
I->Children.emplace_back(R.USR, R.Name, R.RefType, R.Path);
// I is updated with the reference of the new namespace reference
I = &I->Children.back();
}
}
// Look for Info in the vector where it is supposed to be; it could already
// exist if it is a parent namespace of an Info already passed to this
// function.
auto It = llvm::find(I->Children, Info->USR);
if (It == I->Children.end()) {
// If it is not in the vector it is inserted
I->Children.emplace_back(Info->USR, Info->extractName(), Info->IT,
Info->Path);
} else {
// If it not in the vector we only check if Path and Name are not empty
// because if the Info was included by a namespace it may not have those
// values.
if (It->Path.empty())
It->Path = Info->Path;
if (It->Name.empty())
It->Name = Info->extractName();
}
}
// This anchor is used to force the linker to link in the generated object file
// and thus register the generators.
[[maybe_unused]] static int YAMLGeneratorAnchorDest = YAMLGeneratorAnchorSource;
[[maybe_unused]] static int MDGeneratorAnchorDest = MDGeneratorAnchorSource;
[[maybe_unused]] static int HTMLGeneratorAnchorDest = HTMLGeneratorAnchorSource;
[[maybe_unused]] static int JSONGeneratorAnchorDest = JSONGeneratorAnchorSource;
} // namespace doc
} // namespace clang