brintos

brintos / llvm-project-archived public Read only

0
0
Text · 8.8 KiB · 667e5d5 Raw
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