CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
259 lines
9.4 KiB
C++
259 lines
9.4 KiB
C++
//===-- 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
|
|
|
|
llvm::StringRef 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<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) {
|
|
if (Filename == "all_files")
|
|
return "all_files";
|
|
// Checking for an InfoType ensures that only the special top-level index file
|
|
// is caught here, since it is not an Info.
|
|
if (Filename == "index" && !Info->get("InfoType"))
|
|
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 = I->Children.find(llvm::toStringRef(R.USR));
|
|
if (It != I->Children.end()) {
|
|
// If it is found, just change I to point the namespace reference found.
|
|
I = &It->second;
|
|
} else {
|
|
// If it is not found a new reference is created
|
|
auto [NewInfo, success] = I->Children.try_emplace(
|
|
llvm::toStringRef(R.USR), R.USR, R.Name, R.RefType, R.Path);
|
|
// I is updated with the reference of the new namespace reference
|
|
if (success)
|
|
I = &NewInfo->second;
|
|
}
|
|
}
|
|
// 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 = I->Children.find(llvm::toStringRef(Info->USR));
|
|
if (It == I->Children.end()) {
|
|
// If it is not in the vector it is inserted
|
|
I->Children.try_emplace(llvm::toStringRef(Info->USR), 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->second.Path.empty())
|
|
It->second.Path = Info->Path;
|
|
if (It->second.Name.empty())
|
|
It->second.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;
|
|
[[maybe_unused]] static int MDMustacheGeneratorAnchorDest =
|
|
MDMustacheGeneratorAnchorSource;
|
|
} // namespace doc
|
|
} // namespace clang
|