244 lines · cpp
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-exception6//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 conversion34 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 55Error 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 68Error 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 } else84 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 dir105 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 Clang125 // 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>173MustacheGenerator::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 181llvm::Error Generator::createResources(ClangDocContext &CDCtx) {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 will187// be added in the children of a reference to B, which should be also a child of188// a reference to A, where A is a child of Idx.189// Idx190// |-- A191// |--B192// |--X193// If the references to the namespaces do not exist, they will be created. If194// the references already exist, the same one will be used.195void Generator::addInfoToIndex(Index &Idx, const doc::Info *Info) {196 // Index pointer that will be moving through Idx until the first parent197 // 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 the200 // 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 is203 // 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 created210 I->Children.emplace_back(R.USR, R.Name, R.RefType, R.Path);211 // I is updated with the reference of the new namespace reference212 I = &I->Children.back();213 }214 }215 // Look for Info in the vector where it is supposed to be; it could already216 // exist if it is a parent namespace of an Info already passed to this217 // 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 inserted221 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 empty225 // because if the Info was included by a namespace it may not have those226 // 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 file235// and thus register the generators.236[[maybe_unused]] static int YAMLGeneratorAnchorDest = YAMLGeneratorAnchorSource;237[[maybe_unused]] static int MDGeneratorAnchorDest = MDGeneratorAnchorSource;238[[maybe_unused]] static int HTMLGeneratorAnchorDest = HTMLGeneratorAnchorSource;239[[maybe_unused]] static int MHTMLGeneratorAnchorDest =240 MHTMLGeneratorAnchorSource;241[[maybe_unused]] static int JSONGeneratorAnchorDest = JSONGeneratorAnchorSource;242} // namespace doc243} // namespace clang244