diff options
Diffstat (limited to 'Source/cmFileAPICodemodel.cxx')
-rw-r--r-- | Source/cmFileAPICodemodel.cxx | 2046 |
1 files changed, 2046 insertions, 0 deletions
diff --git a/Source/cmFileAPICodemodel.cxx b/Source/cmFileAPICodemodel.cxx new file mode 100644 index 0000000..d3683d4 --- /dev/null +++ b/Source/cmFileAPICodemodel.cxx @@ -0,0 +1,2046 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmFileAPICodemodel.h" + +#include <algorithm> +#include <cassert> +#include <cstddef> +#include <functional> +#include <limits> +#include <map> +#include <memory> +#include <set> +#include <string> +#include <unordered_map> +#include <utility> +#include <vector> + +#include <cm/string_view> +#include <cmext/algorithm> +#include <cmext/string_view> + +#include <cm3p/json/value.h> + +#include "cmCryptoHash.h" +#include "cmExportSet.h" +#include "cmFileAPI.h" +#include "cmFileSet.h" +#include "cmGeneratorExpression.h" +#include "cmGeneratorTarget.h" +#include "cmGlobalGenerator.h" +#include "cmInstallCxxModuleBmiGenerator.h" +#include "cmInstallDirectoryGenerator.h" +#include "cmInstallExportGenerator.h" +#include "cmInstallFileSetGenerator.h" +#include "cmInstallFilesGenerator.h" +#include "cmInstallGenerator.h" +#include "cmInstallGetRuntimeDependenciesGenerator.h" +#include "cmInstallImportedRuntimeArtifactsGenerator.h" +#include "cmInstallRuntimeDependencySet.h" +#include "cmInstallRuntimeDependencySetGenerator.h" +#include "cmInstallScriptGenerator.h" +#include "cmInstallSubdirectoryGenerator.h" +#include "cmInstallTargetGenerator.h" +#include "cmLinkLineComputer.h" // IWYU pragma: keep +#include "cmListFileCache.h" +#include "cmLocalGenerator.h" +#include "cmMakefile.h" +#include "cmMessageType.h" +#include "cmSourceFile.h" +#include "cmSourceGroup.h" +#include "cmState.h" +#include "cmStateDirectory.h" +#include "cmStateSnapshot.h" +#include "cmStateTypes.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" +#include "cmTarget.h" +#include "cmTargetDepend.h" +#include "cmTargetExport.h" +#include "cmValue.h" +#include "cmake.h" + +namespace { + +using TargetIndexMapType = + std::unordered_map<cmGeneratorTarget const*, Json::ArrayIndex>; + +std::string RelativeIfUnder(std::string const& top, std::string const& in) +{ + return cmSystemTools::RelativeIfUnder(top, in); +} + +class JBTIndex +{ +public: + JBTIndex() = default; + explicit operator bool() const { return this->Index != None; } + Json::ArrayIndex Index = None; + static Json::ArrayIndex const None = static_cast<Json::ArrayIndex>(-1); +}; + +template <typename T> +class JBT +{ +public: + JBT(T v = T(), JBTIndex bt = JBTIndex()) + : Value(std::move(v)) + , Backtrace(bt) + { + } + T Value; + JBTIndex Backtrace; + friend bool operator==(JBT<T> const& l, JBT<T> const& r) + { + return l.Value == r.Value && l.Backtrace.Index == r.Backtrace.Index; + } + static bool ValueEq(JBT<T> const& l, JBT<T> const& r) + { + return l.Value == r.Value; + } + static bool ValueLess(JBT<T> const& l, JBT<T> const& r) + { + return l.Value < r.Value; + } +}; + +template <typename T> +class JBTs +{ +public: + JBTs(T v = T(), std::vector<JBTIndex> ids = std::vector<JBTIndex>()) + : Value(std::move(v)) + , Backtraces(std::move(ids)) + { + } + T Value; + std::vector<JBTIndex> Backtraces; + friend bool operator==(JBTs<T> const& l, JBTs<T> const& r) + { + if ((l.Value == r.Value) && (l.Backtraces.size() == r.Backtraces.size())) { + for (size_t i = 0; i < l.Backtraces.size(); i++) { + if (l.Backtraces[i].Index != r.Backtraces[i].Index) { + return false; + } + } + } + return true; + } + static bool ValueEq(JBTs<T> const& l, JBTs<T> const& r) + { + return l.Value == r.Value; + } + static bool ValueLess(JBTs<T> const& l, JBTs<T> const& r) + { + return l.Value < r.Value; + } +}; + +class BacktraceData +{ + std::string TopSource; + std::unordered_map<std::string, Json::ArrayIndex> CommandMap; + std::unordered_map<std::string, Json::ArrayIndex> FileMap; + std::unordered_map<cmListFileContext const*, Json::ArrayIndex> NodeMap; + Json::Value Commands = Json::arrayValue; + Json::Value Files = Json::arrayValue; + Json::Value Nodes = Json::arrayValue; + + Json::ArrayIndex AddCommand(std::string const& command) + { + auto i = this->CommandMap.find(command); + if (i == this->CommandMap.end()) { + auto cmdIndex = static_cast<Json::ArrayIndex>(this->Commands.size()); + i = this->CommandMap.emplace(command, cmdIndex).first; + this->Commands.append(command); + } + return i->second; + } + + Json::ArrayIndex AddFile(std::string const& file) + { + auto i = this->FileMap.find(file); + if (i == this->FileMap.end()) { + auto fileIndex = static_cast<Json::ArrayIndex>(this->Files.size()); + i = this->FileMap.emplace(file, fileIndex).first; + this->Files.append(RelativeIfUnder(this->TopSource, file)); + } + return i->second; + } + +public: + BacktraceData(std::string topSource); + JBTIndex Add(cmListFileBacktrace const& bt); + Json::Value Dump(); +}; + +BacktraceData::BacktraceData(std::string topSource) + : TopSource(std::move(topSource)) +{ +} + +JBTIndex BacktraceData::Add(cmListFileBacktrace const& bt) +{ + JBTIndex index; + if (bt.Empty()) { + return index; + } + cmListFileContext const* top = &bt.Top(); + auto found = this->NodeMap.find(top); + if (found != this->NodeMap.end()) { + index.Index = found->second; + return index; + } + Json::Value entry = Json::objectValue; + entry["file"] = this->AddFile(top->FilePath); + if (top->Line) { + entry["line"] = static_cast<int>(top->Line); + } + if (!top->Name.empty()) { + entry["command"] = this->AddCommand(top->Name); + } + if (JBTIndex parent = this->Add(bt.Pop())) { + entry["parent"] = parent.Index; + } + index.Index = this->NodeMap[top] = this->Nodes.size(); + this->Nodes.append(std::move(entry)); // NOLINT(*) + return index; +} + +Json::Value BacktraceData::Dump() +{ + Json::Value backtraceGraph; + this->CommandMap.clear(); + this->FileMap.clear(); + this->NodeMap.clear(); + backtraceGraph["commands"] = std::move(this->Commands); + backtraceGraph["files"] = std::move(this->Files); + backtraceGraph["nodes"] = std::move(this->Nodes); + return backtraceGraph; +} + +class Codemodel +{ + cmFileAPI& FileAPI; + unsigned long Version; + + Json::Value DumpPaths(); + Json::Value DumpConfigurations(); + Json::Value DumpConfiguration(std::string const& config); + +public: + Codemodel(cmFileAPI& fileAPI, unsigned long version); + Json::Value Dump(); +}; + +class CodemodelConfig +{ + cmFileAPI& FileAPI; + unsigned long Version; + std::string const& Config; + std::string TopSource; + std::string TopBuild; + + struct Directory + { + cmStateSnapshot Snapshot; + cmLocalGenerator const* LocalGenerator = nullptr; + Json::Value TargetIndexes = Json::arrayValue; + Json::ArrayIndex ProjectIndex; + bool HasInstallRule = false; + }; + std::map<cmStateSnapshot, Json::ArrayIndex, cmStateSnapshot::StrictWeakOrder> + DirectoryMap; + std::vector<Directory> Directories; + + struct Project + { + cmStateSnapshot Snapshot; + static const Json::ArrayIndex NoParentIndex = + static_cast<Json::ArrayIndex>(-1); + Json::ArrayIndex ParentIndex = NoParentIndex; + Json::Value ChildIndexes = Json::arrayValue; + Json::Value DirectoryIndexes = Json::arrayValue; + Json::Value TargetIndexes = Json::arrayValue; + }; + std::map<cmStateSnapshot, Json::ArrayIndex, cmStateSnapshot::StrictWeakOrder> + ProjectMap; + std::vector<Project> Projects; + + TargetIndexMapType TargetIndexMap; + + void ProcessDirectories(); + + Json::ArrayIndex GetDirectoryIndex(cmLocalGenerator const* lg); + Json::ArrayIndex GetDirectoryIndex(cmStateSnapshot s); + + Json::ArrayIndex AddProject(cmStateSnapshot s); + + Json::Value DumpTargets(); + Json::Value DumpTarget(cmGeneratorTarget* gt, Json::ArrayIndex ti); + + Json::Value DumpDirectories(); + Json::Value DumpDirectory(Directory& d); + Json::Value DumpDirectoryObject(Directory& d); + + Json::Value DumpProjects(); + Json::Value DumpProject(Project& p); + + Json::Value DumpMinimumCMakeVersion(cmStateSnapshot s); + +public: + CodemodelConfig(cmFileAPI& fileAPI, unsigned long version, + std::string const& config); + Json::Value Dump(); +}; + +std::string TargetId(cmGeneratorTarget const* gt, std::string const& topBuild) +{ + cmCryptoHash hasher(cmCryptoHash::AlgoSHA3_256); + std::string path = RelativeIfUnder( + topBuild, gt->GetLocalGenerator()->GetCurrentBinaryDirectory()); + std::string hash = hasher.HashString(path); + hash.resize(20, '0'); + return gt->GetName() + CMAKE_DIRECTORY_ID_SEP + hash; +} + +struct CompileData +{ + struct IncludeEntry + { + JBT<std::string> Path; + bool IsSystem = false; + IncludeEntry(JBT<std::string> path, bool isSystem) + : Path(std::move(path)) + , IsSystem(isSystem) + { + } + friend bool operator==(IncludeEntry const& l, IncludeEntry const& r) + { + return l.Path == r.Path && l.IsSystem == r.IsSystem; + } + }; + + std::string Language; + std::string Sysroot; + JBTs<std::string> LanguageStandard; + std::vector<JBT<std::string>> Flags; + std::vector<JBT<std::string>> Defines; + std::vector<JBT<std::string>> PrecompileHeaders; + std::vector<IncludeEntry> Includes; + + friend bool operator==(CompileData const& l, CompileData const& r) + { + return (l.Language == r.Language && l.Sysroot == r.Sysroot && + l.Flags == r.Flags && l.Defines == r.Defines && + l.PrecompileHeaders == r.PrecompileHeaders && + l.LanguageStandard == r.LanguageStandard && + l.Includes == r.Includes); + } +}; +} + +namespace std { + +template <> +struct hash<CompileData> +{ + std::size_t operator()(CompileData const& in) const + { + using std::hash; + size_t result = + hash<std::string>()(in.Language) ^ hash<std::string>()(in.Sysroot); + for (auto const& i : in.Includes) { + result = result ^ + (hash<std::string>()(i.Path.Value) ^ + hash<Json::ArrayIndex>()(i.Path.Backtrace.Index) ^ + (i.IsSystem ? std::numeric_limits<size_t>::max() : 0)); + } + for (auto const& i : in.Flags) { + result = result ^ hash<std::string>()(i.Value) ^ + hash<Json::ArrayIndex>()(i.Backtrace.Index); + } + for (auto const& i : in.Defines) { + result = result ^ hash<std::string>()(i.Value) ^ + hash<Json::ArrayIndex>()(i.Backtrace.Index); + } + for (auto const& i : in.PrecompileHeaders) { + result = result ^ hash<std::string>()(i.Value) ^ + hash<Json::ArrayIndex>()(i.Backtrace.Index); + } + if (!in.LanguageStandard.Value.empty()) { + result = result ^ hash<std::string>()(in.LanguageStandard.Value); + for (JBTIndex backtrace : in.LanguageStandard.Backtraces) { + result = result ^ hash<Json::ArrayIndex>()(backtrace.Index); + } + } + return result; + } +}; + +} // namespace std + +namespace { +class DirectoryObject +{ + cmLocalGenerator const* LG = nullptr; + std::string const& Config; + TargetIndexMapType& TargetIndexMap; + std::string TopSource; + std::string TopBuild; + BacktraceData Backtraces; + + void AddBacktrace(Json::Value& object, cmListFileBacktrace const& bt); + + Json::Value DumpPaths(); + Json::Value DumpInstallers(); + Json::Value DumpInstaller(cmInstallGenerator* gen); + Json::Value DumpInstallerExportTargets(cmExportSet* exp); + Json::Value DumpInstallerPath(std::string const& top, + std::string const& fromPathIn, + std::string const& toPath); + +public: + DirectoryObject(cmLocalGenerator const* lg, std::string const& config, + TargetIndexMapType& targetIndexMap); + Json::Value Dump(); +}; + +class Target +{ + cmGeneratorTarget* GT; + std::string const& Config; + std::string TopSource; + std::string TopBuild; + std::vector<cmSourceGroup> SourceGroupsLocal; + BacktraceData Backtraces; + + std::map<std::string, CompileData> CompileDataMap; + + std::unordered_map<cmSourceFile const*, Json::ArrayIndex> SourceMap; + Json::Value Sources = Json::arrayValue; + + struct SourceGroup + { + std::string Name; + Json::Value SourceIndexes = Json::arrayValue; + }; + std::unordered_map<cmSourceGroup const*, Json::ArrayIndex> SourceGroupsMap; + std::vector<SourceGroup> SourceGroups; + + struct CompileGroup + { + std::unordered_map<CompileData, Json::ArrayIndex>::iterator Entry; + Json::Value SourceIndexes = Json::arrayValue; + }; + std::unordered_map<CompileData, Json::ArrayIndex> CompileGroupMap; + std::vector<CompileGroup> CompileGroups; + + using FileSetDatabase = std::map<std::string, Json::ArrayIndex>; + + template <typename T> + JBT<T> ToJBT(BT<T> const& bt) + { + return JBT<T>(bt.Value, this->Backtraces.Add(bt.Backtrace)); + } + + template <typename T> + JBTs<T> ToJBTs(BTs<T> const& bts) + { + std::vector<JBTIndex> ids; + ids.reserve(bts.Backtraces.size()); + for (cmListFileBacktrace const& backtrace : bts.Backtraces) { + ids.emplace_back(this->Backtraces.Add(backtrace)); + } + return JBTs<T>(bts.Value, ids); + } + + void ProcessLanguages(); + void ProcessLanguage(std::string const& lang); + + Json::ArrayIndex AddSourceGroup(cmSourceGroup* sg, Json::ArrayIndex si); + CompileData BuildCompileData(cmSourceFile* sf); + CompileData MergeCompileData(CompileData const& fd); + Json::ArrayIndex AddSourceCompileGroup(cmSourceFile* sf, + Json::ArrayIndex si); + void AddBacktrace(Json::Value& object, cmListFileBacktrace const& bt); + void AddBacktrace(Json::Value& object, JBTIndex bt); + Json::Value DumpPaths(); + Json::Value DumpCompileData(CompileData const& cd); + Json::Value DumpInclude(CompileData::IncludeEntry const& inc); + Json::Value DumpPrecompileHeader(JBT<std::string> const& header); + Json::Value DumpLanguageStandard(JBTs<std::string> const& standard); + Json::Value DumpDefine(JBT<std::string> const& def); + std::pair<Json::Value, FileSetDatabase> DumpFileSets(); + Json::Value DumpFileSet(cmFileSet const* fs, + std::vector<std::string> const& directories); + Json::Value DumpSources(FileSetDatabase const& fsdb); + Json::Value DumpSource(cmGeneratorTarget::SourceAndKind const& sk, + Json::ArrayIndex si, FileSetDatabase const& fsdb); + Json::Value DumpSourceGroups(); + Json::Value DumpSourceGroup(SourceGroup& sg); + Json::Value DumpCompileGroups(); + Json::Value DumpCompileGroup(CompileGroup& cg); + Json::Value DumpSysroot(std::string const& path); + Json::Value DumpInstall(); + Json::Value DumpInstallPrefix(); + Json::Value DumpInstallDestinations(); + Json::Value DumpInstallDestination(cmInstallTargetGenerator* itGen); + Json::Value DumpArtifacts(); + Json::Value DumpLink(); + Json::Value DumpArchive(); + Json::Value DumpLinkCommandFragments(); + Json::Value DumpCommandFragments(std::vector<JBT<std::string>> const& frags); + Json::Value DumpCommandFragment(JBT<std::string> const& frag, + std::string const& role = std::string()); + Json::Value DumpDependencies(); + Json::Value DumpDependency(cmTargetDepend const& td); + Json::Value DumpFolder(); + +public: + Target(cmGeneratorTarget* gt, std::string const& config); + Json::Value Dump(); +}; + +Codemodel::Codemodel(cmFileAPI& fileAPI, unsigned long version) + : FileAPI(fileAPI) + , Version(version) +{ +} + +Json::Value Codemodel::Dump() +{ + Json::Value codemodel = Json::objectValue; + + codemodel["paths"] = this->DumpPaths(); + codemodel["configurations"] = this->DumpConfigurations(); + + return codemodel; +} + +Json::Value Codemodel::DumpPaths() +{ + Json::Value paths = Json::objectValue; + paths["source"] = this->FileAPI.GetCMakeInstance()->GetHomeDirectory(); + paths["build"] = this->FileAPI.GetCMakeInstance()->GetHomeOutputDirectory(); + return paths; +} + +Json::Value Codemodel::DumpConfigurations() +{ + Json::Value configurations = Json::arrayValue; + cmGlobalGenerator* gg = + this->FileAPI.GetCMakeInstance()->GetGlobalGenerator(); + const auto& makefiles = gg->GetMakefiles(); + if (!makefiles.empty()) { + std::vector<std::string> const& configs = + makefiles[0]->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); + for (std::string const& config : configs) { + configurations.append(this->DumpConfiguration(config)); + } + } + return configurations; +} + +Json::Value Codemodel::DumpConfiguration(std::string const& config) +{ + CodemodelConfig configuration(this->FileAPI, this->Version, config); + return configuration.Dump(); +} + +CodemodelConfig::CodemodelConfig(cmFileAPI& fileAPI, unsigned long version, + std::string const& config) + : FileAPI(fileAPI) + , Version(version) + , Config(config) + , TopSource(this->FileAPI.GetCMakeInstance()->GetHomeDirectory()) + , TopBuild(this->FileAPI.GetCMakeInstance()->GetHomeOutputDirectory()) +{ + static_cast<void>(this->Version); +} + +Json::Value CodemodelConfig::Dump() +{ + Json::Value configuration = Json::objectValue; + configuration["name"] = this->Config; + this->ProcessDirectories(); + configuration["targets"] = this->DumpTargets(); + configuration["directories"] = this->DumpDirectories(); + configuration["projects"] = this->DumpProjects(); + return configuration; +} + +void CodemodelConfig::ProcessDirectories() +{ + cmGlobalGenerator* gg = + this->FileAPI.GetCMakeInstance()->GetGlobalGenerator(); + auto const& localGens = gg->GetLocalGenerators(); + + // Add directories in forward order to process parents before children. + this->Directories.reserve(localGens.size()); + for (const auto& lg : localGens) { + auto directoryIndex = + static_cast<Json::ArrayIndex>(this->Directories.size()); + this->Directories.emplace_back(); + Directory& d = this->Directories[directoryIndex]; + d.Snapshot = lg->GetStateSnapshot().GetBuildsystemDirectory(); + d.LocalGenerator = lg.get(); + this->DirectoryMap[d.Snapshot] = directoryIndex; + + d.ProjectIndex = this->AddProject(d.Snapshot); + this->Projects[d.ProjectIndex].DirectoryIndexes.append(directoryIndex); + } + + // Update directories in reverse order to process children before parents. + for (auto di = this->Directories.rbegin(); di != this->Directories.rend(); + ++di) { + Directory& d = *di; + + // Accumulate the presence of install rules on the way up. + for (const auto& gen : + d.LocalGenerator->GetMakefile()->GetInstallGenerators()) { + if (!dynamic_cast<cmInstallSubdirectoryGenerator*>(gen.get())) { + d.HasInstallRule = true; + break; + } + } + if (!d.HasInstallRule) { + for (cmStateSnapshot const& child : d.Snapshot.GetChildren()) { + cmStateSnapshot childDir = child.GetBuildsystemDirectory(); + Json::ArrayIndex const childIndex = this->GetDirectoryIndex(childDir); + if (this->Directories[childIndex].HasInstallRule) { + d.HasInstallRule = true; + break; + } + } + } + } +} + +Json::ArrayIndex CodemodelConfig::GetDirectoryIndex(cmLocalGenerator const* lg) +{ + return this->GetDirectoryIndex( + lg->GetStateSnapshot().GetBuildsystemDirectory()); +} + +Json::ArrayIndex CodemodelConfig::GetDirectoryIndex(cmStateSnapshot s) +{ + auto i = this->DirectoryMap.find(s); + assert(i != this->DirectoryMap.end()); + return i->second; +} + +Json::ArrayIndex CodemodelConfig::AddProject(cmStateSnapshot s) +{ + cmStateSnapshot ps = s.GetBuildsystemDirectoryParent(); + if (ps.IsValid() && ps.GetProjectName() == s.GetProjectName()) { + // This directory is part of its parent directory project. + Json::ArrayIndex const parentDirIndex = this->GetDirectoryIndex(ps); + return this->Directories[parentDirIndex].ProjectIndex; + } + + // This directory starts a new project. + auto projectIndex = static_cast<Json::ArrayIndex>(this->Projects.size()); + this->Projects.emplace_back(); + Project& p = this->Projects[projectIndex]; + p.Snapshot = s; + this->ProjectMap[s] = projectIndex; + if (ps.IsValid()) { + Json::ArrayIndex const parentDirIndex = this->GetDirectoryIndex(ps); + p.ParentIndex = this->Directories[parentDirIndex].ProjectIndex; + this->Projects[p.ParentIndex].ChildIndexes.append(projectIndex); + } + return projectIndex; +} + +Json::Value CodemodelConfig::DumpTargets() +{ + Json::Value targets = Json::arrayValue; + + std::vector<cmGeneratorTarget*> targetList; + cmGlobalGenerator* gg = + this->FileAPI.GetCMakeInstance()->GetGlobalGenerator(); + for (const auto& lg : gg->GetLocalGenerators()) { + cm::append(targetList, lg->GetGeneratorTargets()); + } + std::sort(targetList.begin(), targetList.end(), + [](cmGeneratorTarget* l, cmGeneratorTarget* r) { + return l->GetName() < r->GetName(); + }); + + for (cmGeneratorTarget* gt : targetList) { + if (gt->GetType() == cmStateEnums::GLOBAL_TARGET || + !gt->IsInBuildSystem()) { + continue; + } + + targets.append(this->DumpTarget(gt, targets.size())); + } + + return targets; +} + +Json::Value CodemodelConfig::DumpTarget(cmGeneratorTarget* gt, + Json::ArrayIndex ti) +{ + Target t(gt, this->Config); + std::string prefix = "target-" + gt->GetName(); + for (char& c : prefix) { + // CMP0037 OLD behavior allows slashes in target names. Remove them. + if (c == '/' || c == '\\') { + c = '_'; + } + } + if (!this->Config.empty()) { + prefix += "-" + this->Config; + } + Json::Value target = this->FileAPI.MaybeJsonFile(t.Dump(), prefix); + target["name"] = gt->GetName(); + target["id"] = TargetId(gt, this->TopBuild); + + // Cross-reference directory containing target. + Json::ArrayIndex di = this->GetDirectoryIndex(gt->GetLocalGenerator()); + target["directoryIndex"] = di; + this->Directories[di].TargetIndexes.append(ti); + + // Cross-reference project containing target. + Json::ArrayIndex pi = this->Directories[di].ProjectIndex; + target["projectIndex"] = pi; + this->Projects[pi].TargetIndexes.append(ti); + + this->TargetIndexMap[gt] = ti; + + return target; +} + +Json::Value CodemodelConfig::DumpDirectories() +{ + Json::Value directories = Json::arrayValue; + for (Directory& d : this->Directories) { + directories.append(this->DumpDirectory(d)); + } + return directories; +} + +Json::Value CodemodelConfig::DumpDirectory(Directory& d) +{ + Json::Value directory = this->DumpDirectoryObject(d); + + std::string sourceDir = d.Snapshot.GetDirectory().GetCurrentSource(); + directory["source"] = RelativeIfUnder(this->TopSource, sourceDir); + + std::string buildDir = d.Snapshot.GetDirectory().GetCurrentBinary(); + directory["build"] = RelativeIfUnder(this->TopBuild, buildDir); + + cmStateSnapshot parentDir = d.Snapshot.GetBuildsystemDirectoryParent(); + if (parentDir.IsValid()) { + directory["parentIndex"] = this->GetDirectoryIndex(parentDir); + } + + Json::Value childIndexes = Json::arrayValue; + for (cmStateSnapshot const& child : d.Snapshot.GetChildren()) { + childIndexes.append( + this->GetDirectoryIndex(child.GetBuildsystemDirectory())); + } + if (!childIndexes.empty()) { + directory["childIndexes"] = std::move(childIndexes); + } + + directory["projectIndex"] = d.ProjectIndex; + + if (!d.TargetIndexes.empty()) { + directory["targetIndexes"] = std::move(d.TargetIndexes); + } + + Json::Value minimumCMakeVersion = this->DumpMinimumCMakeVersion(d.Snapshot); + if (!minimumCMakeVersion.isNull()) { + directory["minimumCMakeVersion"] = std::move(minimumCMakeVersion); + } + + if (d.HasInstallRule) { + directory["hasInstallRule"] = true; + } + + return directory; +} + +Json::Value CodemodelConfig::DumpDirectoryObject(Directory& d) +{ + std::string prefix = "directory"; + std::string sourceDirRel = RelativeIfUnder( + this->TopSource, d.Snapshot.GetDirectory().GetCurrentSource()); + std::string buildDirRel = RelativeIfUnder( + this->TopBuild, d.Snapshot.GetDirectory().GetCurrentBinary()); + if (!cmSystemTools::FileIsFullPath(buildDirRel)) { + prefix = cmStrCat(prefix, '-', buildDirRel); + } else if (!cmSystemTools::FileIsFullPath(sourceDirRel)) { + prefix = cmStrCat(prefix, '-', sourceDirRel); + } + for (char& c : prefix) { + if (c == '/' || c == '\\') { + c = '.'; + } + } + if (!this->Config.empty()) { + prefix += "-" + this->Config; + } + + DirectoryObject dir(d.LocalGenerator, this->Config, this->TargetIndexMap); + return this->FileAPI.MaybeJsonFile(dir.Dump(), prefix); +} + +Json::Value CodemodelConfig::DumpProjects() +{ + Json::Value projects = Json::arrayValue; + for (Project& p : this->Projects) { + projects.append(this->DumpProject(p)); + } + return projects; +} + +Json::Value CodemodelConfig::DumpProject(Project& p) +{ + Json::Value project = Json::objectValue; + + project["name"] = p.Snapshot.GetProjectName(); + + if (p.ParentIndex != Project::NoParentIndex) { + project["parentIndex"] = p.ParentIndex; + } + + if (!p.ChildIndexes.empty()) { + project["childIndexes"] = std::move(p.ChildIndexes); + } + + project["directoryIndexes"] = std::move(p.DirectoryIndexes); + + if (!p.TargetIndexes.empty()) { + project["targetIndexes"] = std::move(p.TargetIndexes); + } + + return project; +} + +Json::Value CodemodelConfig::DumpMinimumCMakeVersion(cmStateSnapshot s) +{ + Json::Value minimumCMakeVersion; + if (cmValue def = s.GetDefinition("CMAKE_MINIMUM_REQUIRED_VERSION")) { + minimumCMakeVersion = Json::objectValue; + minimumCMakeVersion["string"] = *def; + } + return minimumCMakeVersion; +} + +DirectoryObject::DirectoryObject(cmLocalGenerator const* lg, + std::string const& config, + TargetIndexMapType& targetIndexMap) + : LG(lg) + , Config(config) + , TargetIndexMap(targetIndexMap) + , TopSource(lg->GetGlobalGenerator()->GetCMakeInstance()->GetHomeDirectory()) + , TopBuild( + lg->GetGlobalGenerator()->GetCMakeInstance()->GetHomeOutputDirectory()) + , Backtraces(this->TopSource) +{ +} + +Json::Value DirectoryObject::Dump() +{ + Json::Value directoryObject = Json::objectValue; + directoryObject["paths"] = this->DumpPaths(); + directoryObject["installers"] = this->DumpInstallers(); + directoryObject["backtraceGraph"] = this->Backtraces.Dump(); + return directoryObject; +} + +void DirectoryObject::AddBacktrace(Json::Value& object, + cmListFileBacktrace const& bt) +{ + if (JBTIndex backtrace = this->Backtraces.Add(bt)) { + object["backtrace"] = backtrace.Index; + } +} + +Json::Value DirectoryObject::DumpPaths() +{ + Json::Value paths = Json::objectValue; + + std::string const& sourceDir = this->LG->GetCurrentSourceDirectory(); + paths["source"] = RelativeIfUnder(this->TopSource, sourceDir); + + std::string const& buildDir = this->LG->GetCurrentBinaryDirectory(); + paths["build"] = RelativeIfUnder(this->TopBuild, buildDir); + + return paths; +} + +Json::Value DirectoryObject::DumpInstallers() +{ + Json::Value installers = Json::arrayValue; + for (const auto& gen : this->LG->GetMakefile()->GetInstallGenerators()) { + Json::Value installer = this->DumpInstaller(gen.get()); + if (!installer.empty()) { + installers.append(std::move(installer)); // NOLINT(*) + } + } + return installers; +} + +Json::Value DirectoryObject::DumpInstaller(cmInstallGenerator* gen) +{ + assert(gen); + Json::Value installer = Json::objectValue; + + // Exclude subdirectory installers and file(GET_RUNTIME_DEPENDENCIES) + // installers. They are implementation details. + if (dynamic_cast<cmInstallSubdirectoryGenerator*>(gen) || + dynamic_cast<cmInstallGetRuntimeDependenciesGenerator*>(gen)) { + return installer; + } + + // Exclude installers not used in this configuration. + if (!gen->InstallsForConfig(this->Config)) { + return installer; + } + + // Add fields specific to each kind of install generator. + if (auto* installTarget = dynamic_cast<cmInstallTargetGenerator*>(gen)) { + cmInstallTargetGenerator::Files const& files = + installTarget->GetFiles(this->Config); + if (files.From.empty()) { + return installer; + } + + installer["type"] = "target"; + installer["destination"] = installTarget->GetDestination(this->Config); + installer["targetId"] = + TargetId(installTarget->GetTarget(), this->TopBuild); + installer["targetIndex"] = + this->TargetIndexMap[installTarget->GetTarget()]; + + std::string fromDir = files.FromDir; + if (!fromDir.empty()) { + fromDir.push_back('/'); + } + + std::string toDir = files.ToDir; + if (!toDir.empty()) { + toDir.push_back('/'); + } + + Json::Value paths = Json::arrayValue; + for (size_t i = 0; i < files.From.size(); ++i) { + std::string const& fromPath = cmStrCat(fromDir, files.From[i]); + std::string const& toPath = cmStrCat(toDir, files.To[i]); + paths.append(this->DumpInstallerPath(this->TopBuild, fromPath, toPath)); + } + installer["paths"] = std::move(paths); + + if (installTarget->GetOptional()) { + installer["isOptional"] = true; + } + + if (installTarget->IsImportLibrary()) { + installer["targetIsImportLibrary"] = true; + } + + switch (files.NamelinkMode) { + case cmInstallTargetGenerator::NamelinkModeNone: + break; + case cmInstallTargetGenerator::NamelinkModeOnly: + installer["targetInstallNamelink"] = "only"; + break; + case cmInstallTargetGenerator::NamelinkModeSkip: + installer["targetInstallNamelink"] = "skip"; + break; + } + + // FIXME: Parse FilePermissions to provide structured information. + // FIXME: Thread EXPORT name through from install() call. + } else if (auto* installFiles = + dynamic_cast<cmInstallFilesGenerator*>(gen)) { + std::vector<std::string> const& files = + installFiles->GetFiles(this->Config); + if (files.empty()) { + return installer; + } + + installer["type"] = "file"; + installer["destination"] = installFiles->GetDestination(this->Config); + Json::Value paths = Json::arrayValue; + std::string const& rename = installFiles->GetRename(this->Config); + if (!rename.empty() && files.size() == 1) { + paths.append(this->DumpInstallerPath(this->TopSource, files[0], rename)); + } else { + for (std::string const& file : installFiles->GetFiles(this->Config)) { + paths.append(RelativeIfUnder(this->TopSource, file)); + } + } + installer["paths"] = std::move(paths); + if (installFiles->GetOptional()) { + installer["isOptional"] = true; + } + // FIXME: Parse FilePermissions to provide structured information. + } else if (auto* installDir = + dynamic_cast<cmInstallDirectoryGenerator*>(gen)) { + std::vector<std::string> const& dirs = + installDir->GetDirectories(this->Config); + if (dirs.empty()) { + return installer; + } + + installer["type"] = "directory"; + installer["destination"] = installDir->GetDestination(this->Config); + Json::Value paths = Json::arrayValue; + for (std::string const& dir : dirs) { + if (cmHasLiteralSuffix(dir, "/")) { + paths.append(this->DumpInstallerPath( + this->TopSource, dir.substr(0, dir.size() - 1), ".")); + } else { + paths.append(this->DumpInstallerPath( + this->TopSource, dir, cmSystemTools::GetFilenameName(dir))); + } + } + installer["paths"] = std::move(paths); + if (installDir->GetOptional()) { + installer["isOptional"] = true; + } + // FIXME: Parse FilePermissions, DirPermissions, and LiteralArguments. + // to provide structured information. + } else if (auto* installExport = + dynamic_cast<cmInstallExportGenerator*>(gen)) { + installer["type"] = "export"; + installer["destination"] = installExport->GetDestination(); + cmExportSet* exportSet = installExport->GetExportSet(); + installer["exportName"] = exportSet->GetName(); + installer["exportTargets"] = this->DumpInstallerExportTargets(exportSet); + Json::Value paths = Json::arrayValue; + paths.append( + RelativeIfUnder(this->TopBuild, installExport->GetMainImportFile())); + installer["paths"] = std::move(paths); + } else if (auto* installScript = + dynamic_cast<cmInstallScriptGenerator*>(gen)) { + if (installScript->IsCode()) { + installer["type"] = "code"; + } else { + installer["type"] = "script"; + installer["scriptFile"] = RelativeIfUnder( + this->TopSource, installScript->GetScript(this->Config)); + } + } else if (auto* installImportedRuntimeArtifacts = + dynamic_cast<cmInstallImportedRuntimeArtifactsGenerator*>( + gen)) { + installer["type"] = "importedRuntimeArtifacts"; + installer["destination"] = + installImportedRuntimeArtifacts->GetDestination(this->Config); + if (installImportedRuntimeArtifacts->GetOptional()) { + installer["isOptional"] = true; + } + } else if (auto* installRuntimeDependencySet = + dynamic_cast<cmInstallRuntimeDependencySetGenerator*>(gen)) { + installer["type"] = "runtimeDependencySet"; + installer["destination"] = + installRuntimeDependencySet->GetDestination(this->Config); + std::string name( + installRuntimeDependencySet->GetRuntimeDependencySet()->GetName()); + if (!name.empty()) { + installer["runtimeDependencySetName"] = name; + } + switch (installRuntimeDependencySet->GetDependencyType()) { + case cmInstallRuntimeDependencySetGenerator::DependencyType::Framework: + installer["runtimeDependencySetType"] = "framework"; + break; + case cmInstallRuntimeDependencySetGenerator::DependencyType::Library: + installer["runtimeDependencySetType"] = "library"; + break; + } + } else if (auto* installFileSet = + dynamic_cast<cmInstallFileSetGenerator*>(gen)) { + installer["type"] = "fileSet"; + installer["destination"] = installFileSet->GetDestination(this->Config); + + auto* fileSet = installFileSet->GetFileSet(); + auto* target = installFileSet->GetTarget(); + + auto dirCges = fileSet->CompileDirectoryEntries(); + auto dirs = fileSet->EvaluateDirectoryEntries( + dirCges, target->GetLocalGenerator(), this->Config, target); + + auto entryCges = fileSet->CompileFileEntries(); + std::map<std::string, std::vector<std::string>> entries; + for (auto const& entryCge : entryCges) { + fileSet->EvaluateFileEntry(dirs, entries, entryCge, + target->GetLocalGenerator(), this->Config, + target); + } + + Json::Value files = Json::arrayValue; + for (auto const& it : entries) { + auto dir = it.first; + if (!dir.empty()) { + dir += '/'; + } + for (auto const& file : it.second) { + files.append(this->DumpInstallerPath( + this->TopSource, file, + cmStrCat(dir, cmSystemTools::GetFilenameName(file)))); + } + } + installer["paths"] = std::move(files); + installer["fileSetName"] = fileSet->GetName(); + installer["fileSetType"] = fileSet->GetType(); + installer["fileSetDirectories"] = Json::arrayValue; + for (auto const& dir : dirs) { + installer["fileSetDirectories"].append( + RelativeIfUnder(this->TopSource, dir)); + } + installer["fileSetTarget"] = Json::objectValue; + installer["fileSetTarget"]["id"] = TargetId(target, this->TopBuild); + installer["fileSetTarget"]["index"] = this->TargetIndexMap[target]; + + if (installFileSet->GetOptional()) { + installer["isOptional"] = true; + } + } else if (auto* cxxModuleBmi = + dynamic_cast<cmInstallCxxModuleBmiGenerator*>(gen)) { + installer["type"] = "cxxModuleBmi"; + installer["destination"] = cxxModuleBmi->GetDestination(this->Config); + + auto const* target = cxxModuleBmi->GetTarget(); + installer["cxxModuleBmiTarget"] = Json::objectValue; + installer["cxxModuleBmiTarget"]["id"] = TargetId(target, this->TopBuild); + installer["cxxModuleBmiTarget"]["index"] = this->TargetIndexMap[target]; + + // FIXME: Parse FilePermissions. + // FIXME: Parse MessageLevel. + if (cxxModuleBmi->GetOptional()) { + installer["isOptional"] = true; + } + } + + // Add fields common to all install generators. + installer["component"] = gen->GetComponent(); + if (gen->GetExcludeFromAll()) { + installer["isExcludeFromAll"] = true; + } + + if (gen->GetAllComponentsFlag()) { + installer["isForAllComponents"] = true; + } + + this->AddBacktrace(installer, gen->GetBacktrace()); + + return installer; +} + +Json::Value DirectoryObject::DumpInstallerExportTargets(cmExportSet* exp) +{ + Json::Value targets = Json::arrayValue; + for (auto const& targetExport : exp->GetTargetExports()) { + Json::Value target = Json::objectValue; + target["id"] = TargetId(targetExport->Target, this->TopBuild); + target["index"] = this->TargetIndexMap[targetExport->Target]; + targets.append(std::move(target)); // NOLINT(*) + } + return targets; +} + +Json::Value DirectoryObject::DumpInstallerPath(std::string const& top, + std::string const& fromPathIn, + std::string const& toPath) +{ + Json::Value installPath; + + std::string fromPath = RelativeIfUnder(top, fromPathIn); + + // If toPath is the last component of fromPath, use just fromPath. + if (toPath.find_first_of('/') == std::string::npos && + cmHasSuffix(fromPath, toPath) && + (fromPath.size() == toPath.size() || + fromPath[fromPath.size() - toPath.size() - 1] == '/')) { + installPath = fromPath; + } else { + installPath = Json::objectValue; + installPath["from"] = fromPath; + installPath["to"] = toPath; + } + + return installPath; +} + +Target::Target(cmGeneratorTarget* gt, std::string const& config) + : GT(gt) + , Config(config) + , TopSource(gt->GetGlobalGenerator()->GetCMakeInstance()->GetHomeDirectory()) + , TopBuild( + gt->GetGlobalGenerator()->GetCMakeInstance()->GetHomeOutputDirectory()) + , SourceGroupsLocal(this->GT->Makefile->GetSourceGroups()) + , Backtraces(this->TopSource) +{ +} + +Json::Value Target::Dump() +{ + Json::Value target = Json::objectValue; + + cmStateEnums::TargetType const type = this->GT->GetType(); + + target["name"] = this->GT->GetName(); + target["type"] = cmState::GetTargetTypeName(type); + target["id"] = TargetId(this->GT, this->TopBuild); + target["paths"] = this->DumpPaths(); + if (this->GT->Target->GetIsGeneratorProvided()) { + target["isGeneratorProvided"] = true; + } + + this->AddBacktrace(target, this->GT->GetBacktrace()); + + if (this->GT->Target->GetHaveInstallRule()) { + target["install"] = this->DumpInstall(); + } + + if (this->GT->HaveWellDefinedOutputFiles()) { + Json::Value artifacts = this->DumpArtifacts(); + if (!artifacts.empty()) { + target["artifacts"] = std::move(artifacts); + } + } + + if (type == cmStateEnums::EXECUTABLE || + type == cmStateEnums::SHARED_LIBRARY || + type == cmStateEnums::MODULE_LIBRARY) { + target["nameOnDisk"] = this->GT->GetFullName(this->Config); + target["link"] = this->DumpLink(); + } else if (type == cmStateEnums::STATIC_LIBRARY) { + target["nameOnDisk"] = this->GT->GetFullName(this->Config); + target["archive"] = this->DumpArchive(); + } + + Json::Value dependencies = this->DumpDependencies(); + if (!dependencies.empty()) { + target["dependencies"] = dependencies; + } + + { + this->ProcessLanguages(); + + auto fileSetInfo = this->DumpFileSets(); + + if (!fileSetInfo.first.isNull()) { + target["fileSets"] = fileSetInfo.first; + } + + target["sources"] = this->DumpSources(fileSetInfo.second); + + Json::Value folder = this->DumpFolder(); + if (!folder.isNull()) { + target["folder"] = std::move(folder); + } + + Json::Value sourceGroups = this->DumpSourceGroups(); + if (!sourceGroups.empty()) { + target["sourceGroups"] = std::move(sourceGroups); + } + + Json::Value compileGroups = this->DumpCompileGroups(); + if (!compileGroups.empty()) { + target["compileGroups"] = std::move(compileGroups); + } + } + + target["backtraceGraph"] = this->Backtraces.Dump(); + + return target; +} + +void Target::ProcessLanguages() +{ + std::set<std::string> languages; + this->GT->GetLanguages(languages, this->Config); + for (std::string const& lang : languages) { + this->ProcessLanguage(lang); + } +} + +void Target::ProcessLanguage(std::string const& lang) +{ + CompileData& cd = this->CompileDataMap[lang]; + cd.Language = lang; + if (cmValue sysrootCompile = + this->GT->Makefile->GetDefinition("CMAKE_SYSROOT_COMPILE")) { + cd.Sysroot = *sysrootCompile; + } else if (cmValue sysroot = + this->GT->Makefile->GetDefinition("CMAKE_SYSROOT")) { + cd.Sysroot = *sysroot; + } + cmLocalGenerator* lg = this->GT->GetLocalGenerator(); + { + // FIXME: Add flags from end section of ExpandRuleVariable, + // which may need to be factored out. + std::vector<BT<std::string>> flags = + lg->GetTargetCompileFlags(this->GT, this->Config, lang); + + cd.Flags.reserve(flags.size()); + for (const BT<std::string>& f : flags) { + cd.Flags.emplace_back(this->ToJBT(f)); + } + } + std::set<BT<std::string>> defines = + lg->GetTargetDefines(this->GT, this->Config, lang); + cd.Defines.reserve(defines.size()); + for (BT<std::string> const& d : defines) { + cd.Defines.emplace_back(this->ToJBT(d)); + } + std::vector<BT<std::string>> includePathList = + lg->GetIncludeDirectories(this->GT, lang, this->Config); + for (BT<std::string> const& i : includePathList) { + cd.Includes.emplace_back( + this->ToJBT(i), + this->GT->IsSystemIncludeDirectory(i.Value, this->Config, lang)); + } + std::vector<BT<std::string>> precompileHeaders = + this->GT->GetPrecompileHeaders(this->Config, lang); + for (BT<std::string> const& pch : precompileHeaders) { + cd.PrecompileHeaders.emplace_back(this->ToJBT(pch)); + } + BTs<std::string> const* languageStandard = + this->GT->GetLanguageStandardProperty(lang, this->Config); + if (languageStandard) { + cd.LanguageStandard = this->ToJBTs(*languageStandard); + } +} + +Json::ArrayIndex Target::AddSourceGroup(cmSourceGroup* sg, Json::ArrayIndex si) +{ + auto i = this->SourceGroupsMap.find(sg); + if (i == this->SourceGroupsMap.end()) { + auto sgIndex = static_cast<Json::ArrayIndex>(this->SourceGroups.size()); + i = this->SourceGroupsMap.emplace(sg, sgIndex).first; + SourceGroup g; + g.Name = sg->GetFullName(); + this->SourceGroups.push_back(std::move(g)); + } + this->SourceGroups[i->second].SourceIndexes.append(si); + return i->second; +} + +CompileData Target::BuildCompileData(cmSourceFile* sf) +{ + CompileData fd; + + fd.Language = sf->GetOrDetermineLanguage(); + if (fd.Language.empty()) { + return fd; + } + + cmLocalGenerator* lg = this->GT->GetLocalGenerator(); + cmGeneratorExpressionInterpreter genexInterpreter(lg, this->Config, this->GT, + fd.Language); + + const std::string COMPILE_FLAGS("COMPILE_FLAGS"); + if (cmValue cflags = sf->GetProperty(COMPILE_FLAGS)) { + std::string flags = genexInterpreter.Evaluate(*cflags, COMPILE_FLAGS); + fd.Flags.emplace_back(std::move(flags), JBTIndex()); + } + const std::string COMPILE_OPTIONS("COMPILE_OPTIONS"); + for (BT<std::string> tmpOpt : sf->GetCompileOptions()) { + tmpOpt.Value = genexInterpreter.Evaluate(tmpOpt.Value, COMPILE_OPTIONS); + // After generator evaluation we need to use the AppendCompileOptions + // method so we handle situations where backtrace entries have lists + // and properly escape flags. + std::string tmp; + lg->AppendCompileOptions(tmp, tmpOpt.Value); + BT<std::string> opt(tmp, tmpOpt.Backtrace); + fd.Flags.emplace_back(this->ToJBT(opt)); + } + + // Add precompile headers compile options. + std::vector<std::string> architectures; + this->GT->GetAppleArchs(this->Config, architectures); + if (architectures.empty()) { + architectures.emplace_back(); + } + + std::unordered_map<std::string, std::string> pchSources; + for (const std::string& arch : architectures) { + const std::string pchSource = + this->GT->GetPchSource(this->Config, fd.Language, arch); + if (!pchSource.empty()) { + pchSources.insert(std::make_pair(pchSource, arch)); + } + } + + if (!pchSources.empty() && !sf->GetProperty("SKIP_PRECOMPILE_HEADERS")) { + std::string pchOptions; + auto pchIt = pchSources.find(sf->ResolveFullPath()); + if (pchIt != pchSources.end()) { + pchOptions = this->GT->GetPchCreateCompileOptions( + this->Config, fd.Language, pchIt->second); + } else { + pchOptions = + this->GT->GetPchUseCompileOptions(this->Config, fd.Language); + } + + BT<std::string> tmpOpt(pchOptions); + tmpOpt.Value = genexInterpreter.Evaluate(tmpOpt.Value, COMPILE_OPTIONS); + + // After generator evaluation we need to use the AppendCompileOptions + // method so we handle situations where backtrace entries have lists + // and properly escape flags. + std::string tmp; + lg->AppendCompileOptions(tmp, tmpOpt.Value); + BT<std::string> opt(tmp, tmpOpt.Backtrace); + fd.Flags.emplace_back(this->ToJBT(opt)); + } + + // Add include directories from source file properties. + { + const std::string INCLUDE_DIRECTORIES("INCLUDE_DIRECTORIES"); + for (BT<std::string> tmpInclude : sf->GetIncludeDirectories()) { + tmpInclude.Value = + genexInterpreter.Evaluate(tmpInclude.Value, INCLUDE_DIRECTORIES); + + // After generator evaluation we need to use the AppendIncludeDirectories + // method so we handle situations where backtrace entries have lists. + std::vector<std::string> tmp; + lg->AppendIncludeDirectories(tmp, tmpInclude.Value, *sf); + for (std::string& i : tmp) { + bool const isSystemInclude = + this->GT->IsSystemIncludeDirectory(i, this->Config, fd.Language); + BT<std::string> include(i, tmpInclude.Backtrace); + fd.Includes.emplace_back(this->ToJBT(include), isSystemInclude); + } + } + } + + const std::string COMPILE_DEFINITIONS("COMPILE_DEFINITIONS"); + std::set<BT<std::string>> fileDefines; + for (BT<std::string> tmpDef : sf->GetCompileDefinitions()) { + tmpDef.Value = + genexInterpreter.Evaluate(tmpDef.Value, COMPILE_DEFINITIONS); + + // After generator evaluation we need to use the AppendDefines method + // so we handle situations where backtrace entries have lists. + std::set<std::string> tmp; + lg->AppendDefines(tmp, tmpDef.Value); + for (const std::string& i : tmp) { + BT<std::string> def(i, tmpDef.Backtrace); + fileDefines.insert(def); + } + } + + std::set<std::string> configFileDefines; + const std::string defPropName = + "COMPILE_DEFINITIONS_" + cmSystemTools::UpperCase(this->Config); + if (cmValue config_defs = sf->GetProperty(defPropName)) { + lg->AppendDefines( + configFileDefines, + genexInterpreter.Evaluate(*config_defs, COMPILE_DEFINITIONS)); + } + + fd.Defines.reserve(fileDefines.size() + configFileDefines.size()); + + for (BT<std::string> const& def : fileDefines) { + fd.Defines.emplace_back(this->ToJBT(def)); + } + + for (std::string const& d : configFileDefines) { + fd.Defines.emplace_back(d, JBTIndex()); + } + + return fd; +} + +CompileData Target::MergeCompileData(CompileData const& fd) +{ + CompileData cd; + cd.Language = fd.Language; + if (cd.Language.empty()) { + return cd; + } + CompileData const& td = this->CompileDataMap.at(cd.Language); + + // All compile groups share the sysroot of the target. + cd.Sysroot = td.Sysroot; + + // All compile groups share the precompile headers of the target. + cd.PrecompileHeaders = td.PrecompileHeaders; + + // All compile groups share the language standard of the target. + cd.LanguageStandard = td.LanguageStandard; + + // Use target-wide flags followed by source-specific flags. + cd.Flags.reserve(td.Flags.size() + fd.Flags.size()); + cd.Flags.insert(cd.Flags.end(), td.Flags.begin(), td.Flags.end()); + cd.Flags.insert(cd.Flags.end(), fd.Flags.begin(), fd.Flags.end()); + + // Use source-specific includes followed by target-wide includes. + cd.Includes.reserve(fd.Includes.size() + td.Includes.size()); + cd.Includes.insert(cd.Includes.end(), fd.Includes.begin(), + fd.Includes.end()); + cd.Includes.insert(cd.Includes.end(), td.Includes.begin(), + td.Includes.end()); + + // Use target-wide defines followed by source-specific defines. + cd.Defines.reserve(td.Defines.size() + fd.Defines.size()); + cd.Defines.insert(cd.Defines.end(), td.Defines.begin(), td.Defines.end()); + cd.Defines.insert(cd.Defines.end(), fd.Defines.begin(), fd.Defines.end()); + + // De-duplicate defines. + std::stable_sort(cd.Defines.begin(), cd.Defines.end(), + JBT<std::string>::ValueLess); + auto end = std::unique(cd.Defines.begin(), cd.Defines.end(), + JBT<std::string>::ValueEq); + cd.Defines.erase(end, cd.Defines.end()); + + return cd; +} + +Json::ArrayIndex Target::AddSourceCompileGroup(cmSourceFile* sf, + Json::ArrayIndex si) +{ + CompileData compileData = this->BuildCompileData(sf); + auto i = this->CompileGroupMap.find(compileData); + if (i == this->CompileGroupMap.end()) { + Json::ArrayIndex cgIndex = + static_cast<Json::ArrayIndex>(this->CompileGroups.size()); + i = this->CompileGroupMap.emplace(std::move(compileData), cgIndex).first; + CompileGroup g; + g.Entry = i; + this->CompileGroups.push_back(std::move(g)); + } + this->CompileGroups[i->second].SourceIndexes.append(si); + return i->second; +} + +void Target::AddBacktrace(Json::Value& object, cmListFileBacktrace const& bt) +{ + if (JBTIndex backtrace = this->Backtraces.Add(bt)) { + object["backtrace"] = backtrace.Index; + } +} + +void Target::AddBacktrace(Json::Value& object, JBTIndex bt) +{ + if (bt) { + object["backtrace"] = bt.Index; + } +} + +Json::Value Target::DumpPaths() +{ + Json::Value paths = Json::objectValue; + cmLocalGenerator* lg = this->GT->GetLocalGenerator(); + + std::string const& sourceDir = lg->GetCurrentSourceDirectory(); + paths["source"] = RelativeIfUnder(this->TopSource, sourceDir); + + std::string const& buildDir = lg->GetCurrentBinaryDirectory(); + paths["build"] = RelativeIfUnder(this->TopBuild, buildDir); + + return paths; +} + +std::pair<Json::Value, Target::FileSetDatabase> Target::DumpFileSets() +{ + Json::Value fsJson = Json::nullValue; + FileSetDatabase fsdb; + + // Build the fileset database. + auto const* tgt = this->GT->Target; + auto const& fs_names = tgt->GetAllFileSetNames(); + + if (!fs_names.empty()) { + fsJson = Json::arrayValue; + size_t fsIndex = 0; + for (auto const& fs_name : fs_names) { + auto const* fs = tgt->GetFileSet(fs_name); + if (!fs) { + this->GT->Makefile->IssueMessage( + MessageType::INTERNAL_ERROR, + cmStrCat("Target \"", tgt->GetName(), + "\" is tracked to have file set \"", fs_name, + "\", but it was not found.")); + continue; + } + + auto fileEntries = fs->CompileFileEntries(); + auto directoryEntries = fs->CompileDirectoryEntries(); + + auto directories = fs->EvaluateDirectoryEntries( + directoryEntries, this->GT->LocalGenerator, this->Config, this->GT); + + fsJson.append(this->DumpFileSet(fs, directories)); + + std::map<std::string, std::vector<std::string>> files_per_dirs; + for (auto const& entry : fileEntries) { + fs->EvaluateFileEntry(directories, files_per_dirs, entry, + this->GT->LocalGenerator, this->Config, + this->GT); + } + + for (auto const& files_per_dir : files_per_dirs) { + auto const& dir = files_per_dir.first; + for (auto const& file : files_per_dir.second) { + std::string sf_path; + if (dir.empty()) { + sf_path = file; + } else { + sf_path = cmStrCat(dir, '/', file); + } + fsdb[sf_path] = static_cast<Json::ArrayIndex>(fsIndex); + } + } + + ++fsIndex; + } + } + + return std::make_pair(fsJson, fsdb); +} + +Json::Value Target::DumpFileSet(cmFileSet const* fs, + std::vector<std::string> const& directories) +{ + Json::Value fileSet = Json::objectValue; + + fileSet["name"] = fs->GetName(); + fileSet["type"] = fs->GetType(); + fileSet["visibility"] = + std::string(cmFileSetVisibilityToName(fs->GetVisibility())); + + Json::Value baseDirs = Json::arrayValue; + for (auto const& directory : directories) { + baseDirs.append(directory); + } + fileSet["baseDirectories"] = baseDirs; + + return fileSet; +} + +Json::Value Target::DumpSources(FileSetDatabase const& fsdb) +{ + Json::Value sources = Json::arrayValue; + cmGeneratorTarget::KindedSources const& kinded = + this->GT->GetKindedSources(this->Config); + for (cmGeneratorTarget::SourceAndKind const& sk : kinded.Sources) { + sources.append(this->DumpSource(sk, sources.size(), fsdb)); + } + return sources; +} + +Json::Value Target::DumpSource(cmGeneratorTarget::SourceAndKind const& sk, + Json::ArrayIndex si, + FileSetDatabase const& fsdb) +{ + Json::Value source = Json::objectValue; + + cmSourceFile* sf = sk.Source.Value; + std::string const path = sf->ResolveFullPath(); + source["path"] = RelativeIfUnder(this->TopSource, path); + if (sk.Source.Value->GetIsGenerated()) { + source["isGenerated"] = true; + } + this->AddBacktrace(source, sk.Source.Backtrace); + + auto fsit = fsdb.find(path); + if (fsit != fsdb.end()) { + source["fileSetIndex"] = fsit->second; + } + + if (cmSourceGroup* sg = + this->GT->Makefile->FindSourceGroup(path, this->SourceGroupsLocal)) { + source["sourceGroupIndex"] = this->AddSourceGroup(sg, si); + } + + switch (sk.Kind) { + case cmGeneratorTarget::SourceKindObjectSource: { + source["compileGroupIndex"] = + this->AddSourceCompileGroup(sk.Source.Value, si); + } break; + case cmGeneratorTarget::SourceKindAppManifest: + case cmGeneratorTarget::SourceKindCertificate: + case cmGeneratorTarget::SourceKindCustomCommand: + case cmGeneratorTarget::SourceKindExternalObject: + case cmGeneratorTarget::SourceKindExtra: + case cmGeneratorTarget::SourceKindHeader: + case cmGeneratorTarget::SourceKindIDL: + case cmGeneratorTarget::SourceKindManifest: + case cmGeneratorTarget::SourceKindModuleDefinition: + case cmGeneratorTarget::SourceKindResx: + case cmGeneratorTarget::SourceKindXaml: + case cmGeneratorTarget::SourceKindUnityBatched: + break; + } + + return source; +} + +Json::Value Target::DumpCompileData(CompileData const& cd) +{ + Json::Value result = Json::objectValue; + + if (!cd.Language.empty()) { + result["language"] = cd.Language; + } + if (!cd.Sysroot.empty()) { + result["sysroot"] = this->DumpSysroot(cd.Sysroot); + } + if (!cd.Flags.empty()) { + result["compileCommandFragments"] = this->DumpCommandFragments(cd.Flags); + } + if (!cd.Includes.empty()) { + Json::Value includes = Json::arrayValue; + for (auto const& i : cd.Includes) { + includes.append(this->DumpInclude(i)); + } + result["includes"] = includes; + } + if (!cd.Defines.empty()) { + Json::Value defines = Json::arrayValue; + for (JBT<std::string> const& d : cd.Defines) { + defines.append(this->DumpDefine(d)); + } + result["defines"] = std::move(defines); + } + if (!cd.PrecompileHeaders.empty()) { + Json::Value precompileHeaders = Json::arrayValue; + for (JBT<std::string> const& pch : cd.PrecompileHeaders) { + precompileHeaders.append(this->DumpPrecompileHeader(pch)); + } + result["precompileHeaders"] = std::move(precompileHeaders); + } + if (!cd.LanguageStandard.Value.empty()) { + result["languageStandard"] = + this->DumpLanguageStandard(cd.LanguageStandard); + } + + return result; +} + +Json::Value Target::DumpInclude(CompileData::IncludeEntry const& inc) +{ + Json::Value include = Json::objectValue; + include["path"] = inc.Path.Value; + if (inc.IsSystem) { + include["isSystem"] = true; + } + this->AddBacktrace(include, inc.Path.Backtrace); + return include; +} + +Json::Value Target::DumpPrecompileHeader(JBT<std::string> const& header) +{ + Json::Value precompileHeader = Json::objectValue; + precompileHeader["header"] = header.Value; + this->AddBacktrace(precompileHeader, header.Backtrace); + return precompileHeader; +} + +Json::Value Target::DumpLanguageStandard(JBTs<std::string> const& standard) +{ + Json::Value languageStandard = Json::objectValue; + languageStandard["standard"] = standard.Value; + if (!standard.Backtraces.empty()) { + Json::Value backtraces = Json::arrayValue; + for (JBTIndex backtrace : standard.Backtraces) { + backtraces.append(backtrace.Index); + } + languageStandard["backtraces"] = backtraces; + } + return languageStandard; +} + +Json::Value Target::DumpDefine(JBT<std::string> const& def) +{ + Json::Value define = Json::objectValue; + define["define"] = def.Value; + this->AddBacktrace(define, def.Backtrace); + return define; +} + +Json::Value Target::DumpSourceGroups() +{ + Json::Value sourceGroups = Json::arrayValue; + for (auto& sg : this->SourceGroups) { + sourceGroups.append(this->DumpSourceGroup(sg)); + } + return sourceGroups; +} + +Json::Value Target::DumpSourceGroup(SourceGroup& sg) +{ + Json::Value group = Json::objectValue; + group["name"] = sg.Name; + group["sourceIndexes"] = std::move(sg.SourceIndexes); + return group; +} + +Json::Value Target::DumpCompileGroups() +{ + Json::Value compileGroups = Json::arrayValue; + for (auto& cg : this->CompileGroups) { + compileGroups.append(this->DumpCompileGroup(cg)); + } + return compileGroups; +} + +Json::Value Target::DumpCompileGroup(CompileGroup& cg) +{ + Json::Value group = + this->DumpCompileData(this->MergeCompileData(cg.Entry->first)); + group["sourceIndexes"] = std::move(cg.SourceIndexes); + return group; +} + +Json::Value Target::DumpSysroot(std::string const& path) +{ + Json::Value sysroot = Json::objectValue; + sysroot["path"] = path; + return sysroot; +} + +Json::Value Target::DumpInstall() +{ + Json::Value install = Json::objectValue; + install["prefix"] = this->DumpInstallPrefix(); + install["destinations"] = this->DumpInstallDestinations(); + return install; +} + +Json::Value Target::DumpInstallPrefix() +{ + Json::Value prefix = Json::objectValue; + std::string p = + this->GT->Makefile->GetSafeDefinition("CMAKE_INSTALL_PREFIX"); + cmSystemTools::ConvertToUnixSlashes(p); + prefix["path"] = p; + return prefix; +} + +Json::Value Target::DumpInstallDestinations() +{ + Json::Value destinations = Json::arrayValue; + auto installGens = this->GT->Target->GetInstallGenerators(); + for (auto* itGen : installGens) { + destinations.append(this->DumpInstallDestination(itGen)); + } + return destinations; +} + +Json::Value Target::DumpInstallDestination(cmInstallTargetGenerator* itGen) +{ + Json::Value destination = Json::objectValue; + destination["path"] = itGen->GetDestination(this->Config); + this->AddBacktrace(destination, itGen->GetBacktrace()); + return destination; +} + +Json::Value Target::DumpArtifacts() +{ + Json::Value artifacts = Json::arrayValue; + + // Object libraries have only object files as artifacts. + if (this->GT->GetType() == cmStateEnums::OBJECT_LIBRARY) { + if (!this->GT->Target->HasKnownObjectFileLocation(nullptr)) { + return artifacts; + } + std::vector<cmSourceFile const*> objectSources; + this->GT->GetObjectSources(objectSources, this->Config); + std::string const obj_dir = this->GT->GetObjectDirectory(this->Config); + for (cmSourceFile const* sf : objectSources) { + const std::string& obj = this->GT->GetObjectName(sf); + Json::Value artifact = Json::objectValue; + artifact["path"] = RelativeIfUnder(this->TopBuild, obj_dir + obj); + artifacts.append(std::move(artifact)); // NOLINT(*) + } + return artifacts; + } + + // Other target types always have a "main" artifact. + { + Json::Value artifact = Json::objectValue; + artifact["path"] = + RelativeIfUnder(this->TopBuild, + this->GT->GetFullPath( + this->Config, cmStateEnums::RuntimeBinaryArtifact)); + artifacts.append(std::move(artifact)); // NOLINT(*) + } + + // Add Windows-specific artifacts produced by the linker. + if (this->GT->HasImportLibrary(this->Config)) { + Json::Value artifact = Json::objectValue; + artifact["path"] = + RelativeIfUnder(this->TopBuild, + this->GT->GetFullPath( + this->Config, cmStateEnums::ImportLibraryArtifact)); + artifacts.append(std::move(artifact)); // NOLINT(*) + } + if (this->GT->IsDLLPlatform() && + this->GT->GetType() != cmStateEnums::STATIC_LIBRARY) { + cmGeneratorTarget::OutputInfo const* output = + this->GT->GetOutputInfo(this->Config); + if (output && !output->PdbDir.empty()) { + Json::Value artifact = Json::objectValue; + artifact["path"] = RelativeIfUnder(this->TopBuild, + output->PdbDir + '/' + + this->GT->GetPDBName(this->Config)); + artifacts.append(std::move(artifact)); // NOLINT(*) + } + } + return artifacts; +} + +Json::Value Target::DumpLink() +{ + Json::Value link = Json::objectValue; + std::string lang = this->GT->GetLinkerLanguage(this->Config); + link["language"] = lang; + { + Json::Value commandFragments = this->DumpLinkCommandFragments(); + if (!commandFragments.empty()) { + link["commandFragments"] = std::move(commandFragments); + } + } + if (cmValue sysrootLink = + this->GT->Makefile->GetDefinition("CMAKE_SYSROOT_LINK")) { + link["sysroot"] = this->DumpSysroot(*sysrootLink); + } else if (cmValue sysroot = + this->GT->Makefile->GetDefinition("CMAKE_SYSROOT")) { + link["sysroot"] = this->DumpSysroot(*sysroot); + } + if (this->GT->IsIPOEnabled(lang, this->Config)) { + link["lto"] = true; + } + return link; +} + +Json::Value Target::DumpArchive() +{ + Json::Value archive = Json::objectValue; + { + // The "link" fragments not relevant to static libraries are empty. + Json::Value commandFragments = this->DumpLinkCommandFragments(); + if (!commandFragments.empty()) { + archive["commandFragments"] = std::move(commandFragments); + } + } + std::string lang = this->GT->GetLinkerLanguage(this->Config); + if (this->GT->IsIPOEnabled(lang, this->Config)) { + archive["lto"] = true; + } + return archive; +} + +Json::Value Target::DumpLinkCommandFragments() +{ + Json::Value linkFragments = Json::arrayValue; + + std::string linkLanguageFlags; + std::vector<BT<std::string>> linkFlags; + std::string frameworkPath; + std::vector<BT<std::string>> linkPath; + std::vector<BT<std::string>> linkLibs; + cmLocalGenerator* lg = this->GT->GetLocalGenerator(); + cmGlobalGenerator* gg = this->GT->GetGlobalGenerator(); + std::unique_ptr<cmLinkLineComputer> linkLineComputer = + gg->CreateLinkLineComputer(lg, lg->GetStateSnapshot().GetDirectory()); + lg->GetTargetFlags(linkLineComputer.get(), this->Config, linkLibs, + linkLanguageFlags, linkFlags, frameworkPath, linkPath, + this->GT); + linkLanguageFlags = cmTrimWhitespace(linkLanguageFlags); + frameworkPath = cmTrimWhitespace(frameworkPath); + + if (!linkLanguageFlags.empty()) { + linkFragments.append( + this->DumpCommandFragment(std::move(linkLanguageFlags), "flags")); + } + + if (!linkFlags.empty()) { + for (BT<std::string> frag : linkFlags) { + frag.Value = cmTrimWhitespace(frag.Value); + linkFragments.append( + this->DumpCommandFragment(this->ToJBT(frag), "flags")); + } + } + + if (!frameworkPath.empty()) { + linkFragments.append( + this->DumpCommandFragment(std::move(frameworkPath), "frameworkPath")); + } + + if (!linkPath.empty()) { + for (BT<std::string> frag : linkPath) { + frag.Value = cmTrimWhitespace(frag.Value); + linkFragments.append( + this->DumpCommandFragment(this->ToJBT(frag), "libraryPath")); + } + } + + if (!linkLibs.empty()) { + for (BT<std::string> frag : linkLibs) { + frag.Value = cmTrimWhitespace(frag.Value); + linkFragments.append( + this->DumpCommandFragment(this->ToJBT(frag), "libraries")); + } + } + + return linkFragments; +} + +Json::Value Target::DumpCommandFragments( + std::vector<JBT<std::string>> const& frags) +{ + Json::Value commandFragments = Json::arrayValue; + for (JBT<std::string> const& f : frags) { + commandFragments.append(this->DumpCommandFragment(f)); + } + return commandFragments; +} + +Json::Value Target::DumpCommandFragment(JBT<std::string> const& frag, + std::string const& role) +{ + Json::Value fragment = Json::objectValue; + fragment["fragment"] = frag.Value; + if (!role.empty()) { + fragment["role"] = role; + } + this->AddBacktrace(fragment, frag.Backtrace); + return fragment; +} + +Json::Value Target::DumpDependencies() +{ + Json::Value dependencies = Json::arrayValue; + cmGlobalGenerator* gg = this->GT->GetGlobalGenerator(); + for (cmTargetDepend const& td : gg->GetTargetDirectDepends(this->GT)) { + dependencies.append(this->DumpDependency(td)); + } + return dependencies; +} + +Json::Value Target::DumpDependency(cmTargetDepend const& td) +{ + Json::Value dependency = Json::objectValue; + dependency["id"] = TargetId(td, this->TopBuild); + this->AddBacktrace(dependency, td.GetBacktrace()); + return dependency; +} + +Json::Value Target::DumpFolder() +{ + Json::Value folder; + if (cmValue f = this->GT->GetProperty("FOLDER")) { + folder = Json::objectValue; + folder["name"] = *f; + } + return folder; +} +} + +Json::Value cmFileAPICodemodelDump(cmFileAPI& fileAPI, unsigned long version) +{ + Codemodel codemodel(fileAPI, version); + return codemodel.Dump(); +} |