| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying | |
| file LICENSE.rst 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 <cm3p/json/value.h> | |
| #include "cmCryptoHash.h" | |
| #include "cmExportSet.h" | |
| #include "cmFileAPI.h" | |
| #include "cmFileSetMetadata.h" | |
| #include "cmGenExContext.h" | |
| #include "cmGeneratorExpression.h" | |
| #include "cmGeneratorExpressionDAGChecker.h" | |
| #include "cmGeneratorFileSet.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 "cmLinkItem.h" | |
| #include "cmLinkLineComputer.h" // IWYU pragma: keep | |
| #include "cmList.h" | |
| #include "cmListFileCache.h" | |
| #include "cmLocalGenerator.h" | |
| #include "cmMakefile.h" | |
| #include "cmRange.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 "cmTargetTypes.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 int VersionMajor; | |
| unsigned int VersionMinor; | |
| Json::Value DumpPaths(); | |
| Json::Value DumpConfigurations(); | |
| Json::Value DumpConfiguration(std::string const& config); | |
| public: | |
| Codemodel(cmFileAPI& fileAPI, unsigned int versionMajor, | |
| unsigned int versionMinor); | |
| Json::Value Dump(); | |
| }; | |
| class CodemodelConfig | |
| { | |
| cmFileAPI& FileAPI; | |
| unsigned int VersionMajor; | |
| unsigned int VersionMinor; | |
| std::string const& Config; | |
| std::string TopSource; | |
| std::string TopBuild; | |
| struct Directory | |
| { | |
| cmStateSnapshot Snapshot; | |
| cmLocalGenerator const* LocalGenerator = nullptr; | |
| Json::Value BuildSystemTargetIndexes = Json::arrayValue; | |
| Json::Value AbstractTargetIndexes = 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 Json::ArrayIndex const NoParentIndex = | |
| static_cast<Json::ArrayIndex>(-1); | |
| Json::ArrayIndex ParentIndex = NoParentIndex; | |
| Json::Value ChildIndexes = Json::arrayValue; | |
| Json::Value DirectoryIndexes = Json::arrayValue; | |
| Json::Value BuildSystemTargetIndexes = Json::arrayValue; | |
| Json::Value AbstractTargetIndexes = Json::arrayValue; | |
| }; | |
| std::map<cmStateSnapshot, Json::ArrayIndex, cmStateSnapshot::StrictWeakOrder> | |
| ProjectMap; | |
| std::vector<Project> Projects; | |
| TargetIndexMapType TargetIndexMap; | |
| struct DumpedTargets | |
| { | |
| Json::Value BuildSystemTargets = Json::arrayValue; | |
| Json::Value AbstractTargets = Json::arrayValue; | |
| }; | |
| void ProcessDirectories(); | |
| Json::ArrayIndex GetDirectoryIndex(cmLocalGenerator const* lg); | |
| Json::ArrayIndex GetDirectoryIndex(cmStateSnapshot s); | |
| Json::ArrayIndex AddProject(cmStateSnapshot s); | |
| DumpedTargets 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 int versionMajor, | |
| unsigned int versionMinor, 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 cmStrCat(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; | |
| std::vector<IncludeEntry> Frameworks; | |
| 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 && l.Frameworks == r.Frameworks); | |
| } | |
| }; | |
| } | |
| 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.Frameworks) { | |
| 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; | |
| unsigned int VersionMajor; | |
| unsigned int VersionMinor; | |
| 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, unsigned int versionMajor, | |
| unsigned int versionMinor, std::string const& config, | |
| TargetIndexMapType& targetIndexMap); | |
| Json::Value Dump(); | |
| }; | |
| class Target | |
| { | |
| cmGeneratorTarget* GT; | |
| unsigned int VersionMajor; | |
| unsigned int VersionMinor; | |
| std::string const& Config; | |
| std::string TopSource; | |
| std::string TopBuild; | |
| 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; | |
| Json::Value InterfaceSourceIndexes = 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, std::vector<Json::ArrayIndex>>; | |
| using FileSetBacktraceDatabase = | |
| std::unordered_map<std::string, std::vector<cmListFileBacktrace>>; | |
| std::vector<cm::FileSetMetadata::Visibility> FileSetVisibilities; | |
| FileSetBacktraceDatabase FileSetBacktraces; | |
| 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 const* sg); | |
| 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 DumpFramework(CompileData::IncludeEntry const& fw); | |
| 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(cmGeneratorFileSet 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 DumpInterfaceIncludes(); | |
| Json::Value DumpInterfaceSources(FileSetDatabase const& fsdb); | |
| Json::Value DumpInterfaceSource(std::string path, 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 DumpLinkItem(cmLinkItem const& linkItem); | |
| Json::Value DumpLinkImplementationLibraries(cmGeneratorTarget::UseTo usage); | |
| Json::Value DumpLinkInterfaceLibraries(cmGeneratorTarget::UseTo usage); | |
| Json::Value DumpObjectDependencies(); | |
| Json::Value DumpOrderDependencies(); | |
| Json::Value DumpFolder(); | |
| Json::Value DumpLauncher(char const* name, char const* type); | |
| Json::Value DumpLaunchers(); | |
| Json::Value DumpDebugger(); | |
| public: | |
| Target(cmGeneratorTarget* gt, unsigned int versionMajor, | |
| unsigned int versionMinor, std::string const& config); | |
| Json::Value Dump(); | |
| }; | |
| Codemodel::Codemodel(cmFileAPI& fileAPI, unsigned int versionMajor, | |
| unsigned int versionMinor) | |
| : FileAPI(fileAPI) | |
| , VersionMajor(versionMajor) | |
| , VersionMinor(versionMinor) | |
| { | |
| } | |
| 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(); | |
| auto const& 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->VersionMajor, | |
| this->VersionMinor, config); | |
| return configuration.Dump(); | |
| } | |
| CodemodelConfig::CodemodelConfig(cmFileAPI& fileAPI, unsigned int versionMajor, | |
| unsigned int versionMinor, | |
| std::string const& config) | |
| : FileAPI(fileAPI) | |
| , VersionMajor(versionMajor) | |
| , VersionMinor(versionMinor) | |
| , Config(config) | |
| , TopSource(this->FileAPI.GetCMakeInstance()->GetHomeDirectory()) | |
| , TopBuild(this->FileAPI.GetCMakeInstance()->GetHomeOutputDirectory()) | |
| { | |
| } | |
| Json::Value CodemodelConfig::Dump() | |
| { | |
| Json::Value configuration = Json::objectValue; | |
| configuration["name"] = this->Config; | |
| this->ProcessDirectories(); | |
| DumpedTargets dumpedTargets = this->DumpTargets(); | |
| configuration["targets"] = dumpedTargets.BuildSystemTargets; | |
| configuration["abstractTargets"] = dumpedTargets.AbstractTargets; | |
| 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 (auto const& 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 (auto const& 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; | |
| } | |
| CodemodelConfig::DumpedTargets CodemodelConfig::DumpTargets() | |
| { | |
| DumpedTargets dumpedTargets; | |
| std::vector<cmGeneratorTarget*> targetList; | |
| cmGlobalGenerator* gg = | |
| this->FileAPI.GetCMakeInstance()->GetGlobalGenerator(); | |
| for (auto const& lg : gg->GetLocalGenerators()) { | |
| cm::append(targetList, lg->GetGeneratorTargets()); | |
| cm::append(targetList, lg->GetOwnedImportedGeneratorTargets()); | |
| } | |
| std::sort(targetList.begin(), targetList.end(), | |
| [](cmGeneratorTarget* l, cmGeneratorTarget* r) { | |
| return l->GetName() < r->GetName(); | |
| }); | |
| for (cmGeneratorTarget* gt : targetList) { | |
| if (gt->GetType() == cm::TargetType::GLOBAL_TARGET) { | |
| continue; | |
| } | |
| // Ignore targets starting with `__cmake_` as they are internal. | |
| if (cmHasLiteralPrefix(gt->GetName(), "__cmake_")) { | |
| continue; | |
| } | |
| Json::Value& targets = gt->IsInBuildSystem() | |
| ? dumpedTargets.BuildSystemTargets | |
| : dumpedTargets.AbstractTargets; | |
| targets.append(this->DumpTarget(gt, targets.size())); | |
| } | |
| return dumpedTargets; | |
| } | |
| Json::Value CodemodelConfig::DumpTarget(cmGeneratorTarget* gt, | |
| Json::ArrayIndex ti) | |
| { | |
| Target t(gt, this->VersionMajor, this->VersionMinor, this->Config); | |
| std::string safeTargetName = gt->GetName(); | |
| std::replace(safeTargetName.begin(), safeTargetName.end(), ':', '_'); | |
| std::string prefix = "target-" + safeTargetName; | |
| if (!this->Config.empty()) { | |
| prefix = cmStrCat(std::move(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; | |
| if (gt->IsInBuildSystem()) { | |
| this->Directories[di].BuildSystemTargetIndexes.append(ti); | |
| } else { | |
| this->Directories[di].AbstractTargetIndexes.append(ti); | |
| } | |
| // Cross-reference project containing target. | |
| Json::ArrayIndex pi = this->Directories[di].ProjectIndex; | |
| target["projectIndex"] = pi; | |
| if (gt->IsInBuildSystem()) { | |
| this->Projects[pi].BuildSystemTargetIndexes.append(ti); | |
| } else { | |
| this->Projects[pi].AbstractTargetIndexes.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.BuildSystemTargetIndexes.empty()) { | |
| directory["targetIndexes"] = std::move(d.BuildSystemTargetIndexes); | |
| } | |
| if (!d.AbstractTargetIndexes.empty()) { | |
| directory["abstractTargetIndexes"] = std::move(d.AbstractTargetIndexes); | |
| } | |
| 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(std::move(prefix), '-', buildDirRel); | |
| } else if (!cmSystemTools::FileIsFullPath(sourceDirRel)) { | |
| prefix = cmStrCat(std::move(prefix), '-', sourceDirRel); | |
| } | |
| for (char& c : prefix) { | |
| if (c == '/' || c == '\\') { | |
| c = '.'; | |
| } | |
| } | |
| if (!this->Config.empty()) { | |
| prefix = cmStrCat(std::move(prefix), '-', this->Config); | |
| } | |
| DirectoryObject dir(d.LocalGenerator, this->VersionMajor, this->VersionMinor, | |
| 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.BuildSystemTargetIndexes.empty()) { | |
| project["targetIndexes"] = std::move(p.BuildSystemTargetIndexes); | |
| } | |
| if (!p.AbstractTargetIndexes.empty()) { | |
| project["abstractTargetIndexes"] = std::move(p.AbstractTargetIndexes); | |
| } | |
| 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, | |
| unsigned int versionMajor, | |
| unsigned int versionMinor, | |
| std::string const& config, | |
| TargetIndexMapType& targetIndexMap) | |
| : LG(lg) | |
| , VersionMajor(versionMajor) | |
| , VersionMinor(versionMinor) | |
| , 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["codemodelVersion"] = | |
| cmFileAPI::BuildVersion(this->VersionMajor, this->VersionMinor); | |
| 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 (auto const& 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 (cmHasSuffix(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)) { | |
| auto const* fileSet = installFileSet->GetFileSet(); | |
| // No fileSet by that name exists for the associated target | |
| if (!fileSet) { | |
| return installer; | |
| } | |
| installer["type"] = "fileSet"; | |
| installer["destination"] = installFileSet->GetDestination(this->Config); | |
| auto* target = installFileSet->GetTarget(); | |
| cm::GenEx::Context context(target->LocalGenerator, this->Config); | |
| auto dirs = fileSet->GetDirectories(context, target); | |
| auto entries = fileSet->GetFiles(context, target); | |
| Json::Value files = Json::arrayValue; | |
| for (auto const& it : entries.first) { | |
| auto dir = it.first; | |
| if (!dir.empty()) { | |
| dir += '/'; | |
| } | |
| for (auto const& file : it.second) { | |
| files.append(this->DumpInstallerPath( | |
| this->TopSource, file, | |
| cmStrCat(dir, cmSystemTools::GetFilenameNameView(file)))); | |
| } | |
| } | |
| installer["paths"] = std::move(files); | |
| installer["fileSetName"] = fileSet->GetName(); | |
| installer["fileSetType"] = fileSet->GetType(); | |
| installer["fileSetDirectories"] = Json::arrayValue; | |
| for (auto const& dir : dirs.first) { | |
| 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, unsigned int versionMajor, | |
| unsigned int versionMinor, std::string const& config) | |
| : GT(gt) | |
| , VersionMajor(versionMajor) | |
| , VersionMinor(versionMinor) | |
| , Config(config) | |
| , TopSource(gt->GetGlobalGenerator()->GetCMakeInstance()->GetHomeDirectory()) | |
| , TopBuild( | |
| gt->GetGlobalGenerator()->GetCMakeInstance()->GetHomeOutputDirectory()) | |
| , Backtraces(this->TopSource) | |
| { | |
| } | |
| Json::Value Target::Dump() | |
| { | |
| Json::Value target = Json::objectValue; | |
| cm::TargetType const type = this->GT->GetType(); | |
| target["codemodelVersion"] = | |
| cmFileAPI::BuildVersion(this->VersionMajor, this->VersionMinor); | |
| target["name"] = this->GT->GetName(); | |
| target["type"] = cmState::GetTargetTypeName(type); | |
| target["id"] = TargetId(this->GT, this->TopBuild); | |
| if (this->GT->IsImported()) { | |
| target["imported"] = true; | |
| if (!this->GT->IsImportedGloballyVisible()) { | |
| target["local"] = true; | |
| } | |
| } | |
| if (this->GT->IsSymbolic()) { | |
| target["symbolic"] = true; | |
| } | |
| if (!this->GT->IsInBuildSystem()) { | |
| target["abstract"] = true; | |
| } | |
| 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 == cm::TargetType::EXECUTABLE || | |
| type == cm::TargetType::SHARED_LIBRARY || | |
| type == cm::TargetType::MODULE_LIBRARY) { | |
| target["nameOnDisk"] = this->GT->GetFullName(this->Config); | |
| if (!this->GT->IsImported()) { | |
| target["link"] = this->DumpLink(); | |
| } | |
| } else if (type == cm::TargetType::STATIC_LIBRARY) { | |
| target["nameOnDisk"] = this->GT->GetFullName(this->Config); | |
| if (!this->GT->IsImported()) { | |
| target["archive"] = this->DumpArchive(); | |
| } | |
| } | |
| if (type == cm::TargetType::EXECUTABLE) { | |
| Json::Value launchers = this->DumpLaunchers(); | |
| if (!launchers.empty()) { | |
| target["launchers"] = std::move(launchers); | |
| } | |
| } | |
| if (!this->GT->IsImported()) { | |
| Json::Value dependencies = this->DumpDependencies(); | |
| if (!dependencies.empty()) { | |
| target["dependencies"] = dependencies; | |
| } | |
| } | |
| { | |
| Json::Value linkLibraries = | |
| this->DumpLinkImplementationLibraries(cmGeneratorTarget::UseTo::Link); | |
| if (!linkLibraries.empty()) { | |
| target["linkLibraries"] = std::move(linkLibraries); | |
| } | |
| Json::Value ifaceLinkLibraries = | |
| this->DumpLinkInterfaceLibraries(cmGeneratorTarget::UseTo::Link); | |
| if (!ifaceLinkLibraries.empty()) { | |
| target["interfaceLinkLibraries"] = std::move(ifaceLinkLibraries); | |
| } | |
| Json::Value compileDependencies = | |
| this->DumpLinkImplementationLibraries(cmGeneratorTarget::UseTo::Compile); | |
| if (!compileDependencies.empty()) { | |
| target["compileDependencies"] = std::move(compileDependencies); | |
| } | |
| Json::Value ifaceCompileDependencies = | |
| this->DumpLinkInterfaceLibraries(cmGeneratorTarget::UseTo::Compile); | |
| if (!ifaceCompileDependencies.empty()) { | |
| target["interfaceCompileDependencies"] = | |
| std::move(ifaceCompileDependencies); | |
| } | |
| Json::Value objectDependencies = this->DumpObjectDependencies(); | |
| if (!objectDependencies.empty()) { | |
| target["objectDependencies"] = std::move(objectDependencies); | |
| } | |
| Json::Value orderDependencies = this->DumpOrderDependencies(); | |
| if (!orderDependencies.empty()) { | |
| target["orderDependencies"] = std::move(orderDependencies); | |
| } | |
| if (!this->GT->IsImported()) { | |
| this->ProcessLanguages(); | |
| } | |
| auto fileSetInfo = this->DumpFileSets(); | |
| if (!fileSetInfo.first.isNull()) { | |
| target["fileSets"] = fileSetInfo.first; | |
| } | |
| // Even though some types of targets can't have sources, we have to always | |
| // output a sources array to preserve backward compatibility | |
| target["sources"] = this->DumpSources(fileSetInfo.second); | |
| auto interfaceSources = this->DumpInterfaceSources(fileSetInfo.second); | |
| if (!interfaceSources.empty()) { | |
| target["interfaceSources"] = std::move(interfaceSources); | |
| } | |
| auto interfaceIncludes = this->DumpInterfaceIncludes(); | |
| if (!interfaceIncludes.empty()) { | |
| target["interfaceIncludes"] = std::move(interfaceIncludes); | |
| } | |
| Json::Value folder = this->DumpFolder(); | |
| if (!folder.isNull()) { | |
| target["folder"] = std::move(folder); | |
| } | |
| if (!this->GT->IsImported()) { | |
| 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(); | |
| if (!this->GT->IsImported()) { | |
| Json::Value debugger = this->DumpDebugger(); | |
| if (!debugger.isNull()) { | |
| target["debugger"] = std::move(debugger); | |
| } | |
| } | |
| 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) { | |
| if (this->GT->IsApple() && cmSystemTools::IsPathToFramework(i.Value)) { | |
| cd.Frameworks.emplace_back( | |
| this->ToJBT(i), | |
| this->GT->IsSystemIncludeDirectory(i.Value, this->Config, lang)); | |
| } else { | |
| 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 const* sg) | |
| { | |
| 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)); | |
| } | |
| return i->second; | |
| } | |
| CompileData Target::BuildCompileData(cmSourceFile* sf) | |
| { | |
| CompileData fd; | |
| fd.Language = this->GT->GetSourceFileLanguage(sf, this->Config); | |
| if (fd.Language.empty()) { | |
| return fd; | |
| } | |
| cmLocalGenerator* lg = this->GT->GetLocalGenerator(); | |
| cmGeneratorExpressionInterpreter genexInterpreter(lg, this->Config, this->GT, | |
| fd.Language); | |
| cmGeneratorFileSet const* fileSet = | |
| GT->GetFileSetForSource(this->Config, sf); | |
| std::string const 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()); | |
| } | |
| std::string const 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)); | |
| } | |
| // File set compile options, if any | |
| if (fileSet) { | |
| for (BT<std::string> const& tmpOpt : fileSet->BelongsTo(this->GT) | |
| ? fileSet->GetCompileOptions(this->Config, fd.Language) | |
| : fileSet->GetInterfaceCompileOptions(this->Config, fd.Language)) { | |
| // We need to use the AppendCompileOptions method so we handle situations | |
| // where backtrace entries have list 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> pchArchs = | |
| this->GT->GetPchArchs(this->Config, fd.Language); | |
| std::unordered_map<std::string, std::string> pchSources; | |
| for (std::string const& arch : pchArchs) { | |
| std::string const pchSource = | |
| this->GT->GetPchSource(this->Config, fd.Language, arch); | |
| if (!pchSource.empty()) { | |
| pchSources.insert(std::make_pair(pchSource, arch)); | |
| } | |
| } | |
| if (!pchSources.empty() && | |
| !((fileSet && fileSet->GetProperty("SKIP_PRECOMPILE_HEADERS")) || | |
| 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)); | |
| } | |
| std::string const INCLUDE_DIRECTORIES("INCLUDE_DIRECTORIES"); | |
| // Add include directories from file set properties. | |
| if (fileSet) { | |
| for (BT<std::string> const& tmpInclude : fileSet->BelongsTo(this->GT) | |
| ? fileSet->GetIncludeDirectories(this->Config, fd.Language) | |
| : fileSet->GetInterfaceIncludeDirectories(this->Config, | |
| fd.Language)) { | |
| // 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); | |
| if (this->GT->IsApple() && cmSystemTools::IsPathToFramework(i)) { | |
| fd.Frameworks.emplace_back(this->ToJBT(include), isSystemInclude); | |
| } else { | |
| fd.Includes.emplace_back(this->ToJBT(include), isSystemInclude); | |
| } | |
| } | |
| } | |
| } | |
| // Add include directories from source file properties. | |
| { | |
| 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); | |
| if (this->GT->IsApple() && cmSystemTools::IsPathToFramework(i)) { | |
| fd.Frameworks.emplace_back(this->ToJBT(include), isSystemInclude); | |
| } else { | |
| fd.Includes.emplace_back(this->ToJBT(include), isSystemInclude); | |
| } | |
| } | |
| } | |
| } | |
| std::string const 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 (std::string const& i : tmp) { | |
| BT<std::string> def(i, tmpDef.Backtrace); | |
| fileDefines.insert(def); | |
| } | |
| } | |
| std::set<std::string> configFileDefines; | |
| std::string const defPropName = | |
| "COMPILE_DEFINITIONS_" + cmSystemTools::UpperCase(this->Config); | |
| if (cmValue config_defs = sf->GetProperty(defPropName)) { | |
| lg->AppendDefines( | |
| configFileDefines, | |
| genexInterpreter.Evaluate(*config_defs, COMPILE_DEFINITIONS)); | |
| } | |
| std::set<BT<std::string>> fileSetDefines; | |
| if (fileSet) { | |
| for (BT<std::string> const& tmpDef : fileSet->BelongsTo(this->GT) | |
| ? fileSet->GetCompileDefinitions(this->Config, fd.Language) | |
| : fileSet->GetInterfaceCompileDefinitions(this->Config, | |
| fd.Language)) { | |
| // 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 (std::string const& i : tmp) { | |
| BT<std::string> def(i, tmpDef.Backtrace); | |
| fileSetDefines.insert(def); | |
| } | |
| } | |
| } | |
| fd.Defines.reserve(fileDefines.size() + configFileDefines.size() + | |
| fileSetDefines.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()); | |
| } | |
| for (BT<std::string> const& def : fileSetDefines) { | |
| fd.Defines.emplace_back(this->ToJBT(def)); | |
| } | |
| 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 source-specific frameworks followed by target-wide frameworks. | |
| cd.Frameworks.reserve(fd.Frameworks.size() + td.Frameworks.size()); | |
| cd.Frameworks.insert(cd.Frameworks.end(), fd.Frameworks.begin(), | |
| fd.Frameworks.end()); | |
| cd.Frameworks.insert(cd.Frameworks.end(), td.Frameworks.begin(), | |
| td.Frameworks.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; | |
| // We record the visibility of each file set for later use when dumping | |
| // interface sources, which needs to map files to file set visibility | |
| // with only an index available. Those indexes match this vector. | |
| this->FileSetVisibilities.clear(); | |
| this->FileSetBacktraces.clear(); | |
| // Build the fileset database. | |
| auto const& fileSets = this->GT->GetAllFileSets(); | |
| if (!fileSets.empty()) { | |
| fsJson = Json::arrayValue; | |
| size_t fsIndex = 0; | |
| for (auto const* fs : fileSets) { | |
| cm::GenEx::Context context(this->GT->LocalGenerator, this->Config); | |
| auto directories = fs->GetDirectories(context, this->GT); | |
| fsJson.append(this->DumpFileSet(fs, directories.first)); | |
| this->FileSetVisibilities.push_back(fs->GetVisibility()); | |
| auto files_per_dirs = fs->GetFiles(context, this->GT); | |
| for (auto const& files_per_dir : files_per_dirs.first) { | |
| auto const& dir = files_per_dir.first; | |
| for (auto const& file : files_per_dir.second) { | |
| std::string sf_path; | |
| if (dir.empty() || cmSystemTools::FileIsFullPath(file)) { | |
| sf_path = file; | |
| } else { | |
| sf_path = cmStrCat(dir, '/', file); | |
| } | |
| fsdb[sf_path].emplace_back(static_cast<Json::ArrayIndex>(fsIndex)); | |
| } | |
| } | |
| // Collect backtraces from each original file set FILES entry so that | |
| // source backtraces preserve line metadata and can include repeated | |
| // additions from multiple file sets. | |
| auto const& fileEntries = fs->GetFileEntries(); | |
| for (BT<std::string> const& fileEntry : fileEntries) { | |
| cmGeneratorExpression ge( | |
| *this->GT->GetLocalGenerator()->GetCMakeInstance(), | |
| fileEntry.Backtrace); | |
| for (std::string const& ex : cmList{ fileEntry.Value }) { | |
| std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(ex); | |
| std::map<std::string, std::vector<std::string>> filesForEntry; | |
| fs->EvaluateFileEntry(directories.first, filesForEntry, cge, context, | |
| this->GT); | |
| for (auto const& filesPerDir : filesForEntry) { | |
| std::string const& dir = filesPerDir.first; | |
| for (std::string const& file : filesPerDir.second) { | |
| std::string sf_path; | |
| if (dir.empty() || cmSystemTools::FileIsFullPath(file)) { | |
| sf_path = file; | |
| } else { | |
| sf_path = cmStrCat(dir, '/', file); | |
| } | |
| this->FileSetBacktraces[sf_path].push_back(fileEntry.Backtrace); | |
| } | |
| } | |
| } | |
| } | |
| ++fsIndex; | |
| } | |
| } | |
| return std::make_pair(fsJson, fsdb); | |
| } | |
| Json::Value Target::DumpFileSet(cmGeneratorFileSet 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(cm::FileSetMetadata::VisibilityToName(fs->GetVisibility())); | |
| Json::Value baseDirs = Json::arrayValue; | |
| for (auto const& directory : directories) { | |
| baseDirs.append(RelativeIfUnder(this->TopSource, 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; | |
| } | |
| JBTIndex sourceBacktrace = this->Backtraces.Add(sk.Source.Backtrace); | |
| JBTIndex primaryBacktrace = sourceBacktrace; | |
| Json::Value backtraces = Json::arrayValue; | |
| auto const fileSetBacktraces = this->FileSetBacktraces.find(path); | |
| if (fileSetBacktraces != this->FileSetBacktraces.end() && | |
| !fileSetBacktraces->second.empty()) { | |
| for (cmListFileBacktrace const& fsbt : fileSetBacktraces->second) { | |
| if (JBTIndex bt = this->Backtraces.Add(fsbt)) { | |
| if (!primaryBacktrace) { | |
| primaryBacktrace = bt; | |
| } | |
| backtraces.append(bt.Index); | |
| } | |
| } | |
| } else { | |
| if (sourceBacktrace) { | |
| backtraces.append(sourceBacktrace.Index); | |
| } | |
| } | |
| this->AddBacktrace(source, primaryBacktrace); | |
| if (!backtraces.empty()) { | |
| source["backtraces"] = std::move(backtraces); | |
| } | |
| auto fsit = fsdb.find(path); | |
| if (fsit != fsdb.end()) { | |
| source["fileSetIndex"] = fsit->second.back(); | |
| source["fileSetIndexes"] = Json::arrayValue; | |
| for (Json::ArrayIndex const& fsIndex : fsit->second) { | |
| source["fileSetIndexes"].append(fsIndex); | |
| } | |
| } | |
| if (cmSourceGroup const* sg = this->GT->LocalGenerator->FindSourceGroup( | |
| this->GT, sf, this->Config)) { | |
| Json::ArrayIndex const groupIndex = this->AddSourceGroup(sg); | |
| source["sourceGroupIndex"] = groupIndex; | |
| this->SourceGroups[groupIndex].SourceIndexes.append(si); | |
| } | |
| switch (sk.Kind) { | |
| case cmGeneratorTarget::SourceKindCxxModuleSource: | |
| 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: | |
| case cmGeneratorTarget::SourceKindRustMainCrateRoot: | |
| break; | |
| } | |
| return source; | |
| } | |
| Json::Value Target::DumpInterfaceSources(FileSetDatabase const& fsdb) | |
| { | |
| Json::Value interfaceSources = Json::arrayValue; | |
| auto dumpFile = [this, &interfaceSources, &fsdb](std::string const& file) { | |
| std::string path = file; | |
| if (!cmSystemTools::FileIsFullPath(path)) { | |
| path = cmStrCat( | |
| this->GT->GetLocalGenerator()->GetCurrentSourceDirectory(), '/', file); | |
| } | |
| path = cmSystemTools::CollapseFullPath(path); | |
| interfaceSources.append( | |
| this->DumpInterfaceSource(path, interfaceSources.size(), fsdb)); | |
| }; | |
| cmValue prop = this->GT->GetProperty("INTERFACE_SOURCES"); | |
| if (prop) { | |
| cmList files{ cmGeneratorExpression::Evaluate( | |
| *prop, this->GT->GetLocalGenerator(), this->Config, this->GT) }; | |
| for (std::string const& file : files) { | |
| dumpFile(file); | |
| } | |
| } | |
| for (auto const& fsIter : fsdb) { | |
| Json::ArrayIndex const index = fsIter.second.back(); | |
| // FileSetVisibilities was populated by DumpFileSets() and will always | |
| // have the same size as the file sets array that index is indexing into | |
| if (this->FileSetVisibilities[index] != | |
| cm::FileSetMetadata::Visibility::Private) { | |
| dumpFile(fsIter.first); | |
| } | |
| } | |
| return interfaceSources; | |
| } | |
| Json::Value Target::DumpInterfaceIncludes() | |
| { | |
| Json::Value interfaceIncludes = Json::arrayValue; | |
| cmValue prop = this->GT->GetProperty("INTERFACE_INCLUDE_DIRECTORIES"); | |
| cmValue systemIncludesProp = | |
| this->GT->GetProperty("INTERFACE_SYSTEM_INCLUDE_DIRECTORIES"); | |
| if (!prop) { | |
| return interfaceIncludes; | |
| } | |
| struct InterfaceInclude | |
| { | |
| std::string Path; | |
| bool IsSystem; | |
| std::set<std::string> Languages; | |
| }; | |
| // Get enabled languages | |
| std::vector<std::string> languages = | |
| this->GT->Makefile->GetState()->GetEnabledLanguages(); | |
| languages.erase(std::remove(languages.begin(), languages.end(), "NONE"), | |
| languages.end()); | |
| if (languages.empty()) { | |
| languages.emplace_back(); | |
| } | |
| // Evaluate an INCLUDE_DIRECTORIES property with language context | |
| auto evaluateIncludes = | |
| [this](cmCompiledGeneratorExpression const& expression, | |
| cm::GenEx::Context const& context) { | |
| cmGeneratorExpressionDAGChecker dagChecker{ this->GT, | |
| "INCLUDE_DIRECTORIES", | |
| nullptr, nullptr, context }; | |
| return cmList{ expression.Evaluate(context, &dagChecker, this->GT, | |
| this->GT) }; | |
| }; | |
| cmGeneratorExpression ge(*this->GT->Makefile->GetCMakeInstance()); | |
| auto includesExpression = ge.Parse(*prop); | |
| auto systemIncludesExpression = | |
| systemIncludesProp ? ge.Parse(*systemIncludesProp) : nullptr; | |
| bool const targetTreatsOwnIncludesAsSystem = | |
| this->GT->GetPropertyAsBool("SYSTEM") && | |
| (!this->GT->IsImported() || | |
| !this->GT->GetPropertyAsBool("IMPORTED_NO_SYSTEM")); | |
| std::vector<InterfaceInclude> includes; | |
| // Get includes for each enabled language and merge results | |
| for (std::string const& language : languages) { | |
| cm::GenEx::Context context(this->GT->GetLocalGenerator(), this->Config, | |
| language); | |
| // Get per-language system include set | |
| std::set<std::string> systemIncludes; | |
| if (systemIncludesExpression) { | |
| for (std::string include : | |
| evaluateIncludes(*systemIncludesExpression, context)) { | |
| cmSystemTools::ConvertToUnixSlashes(include); | |
| systemIncludes.emplace(std::move(include)); | |
| } | |
| } | |
| // Get per-language includes | |
| for (std::string include : | |
| evaluateIncludes(*includesExpression, context)) { | |
| cmSystemTools::ConvertToUnixSlashes(include); | |
| bool const isSystem = targetTreatsOwnIncludesAsSystem || | |
| systemIncludes.find(include) != systemIncludes.end(); | |
| auto entry = std::find_if( | |
| includes.begin(), includes.end(), | |
| [&include, isSystem](InterfaceInclude const& candidate) { | |
| return candidate.Path == include && candidate.IsSystem == isSystem; | |
| }); | |
| if (entry == includes.end()) { | |
| entry = includes.insert(includes.end(), | |
| { std::move(include), isSystem, {} }); | |
| } | |
| entry->Languages.insert(language); | |
| } | |
| } | |
| // Build JSON from InterfaceInclude vector | |
| for (InterfaceInclude const& include : includes) { | |
| Json::Value entry = | |
| this->DumpInclude({ JBT<std::string>(include.Path), include.IsSystem }); | |
| if (include.Languages.size() != languages.size()) { | |
| Json::Value entryLanguages = Json::arrayValue; | |
| for (std::string const& language : include.Languages) { | |
| entryLanguages.append(language); | |
| } | |
| entry["languages"] = std::move(entryLanguages); | |
| } | |
| interfaceIncludes.append(std::move(entry)); | |
| } | |
| return interfaceIncludes; | |
| } | |
| Json::Value Target::DumpInterfaceSource(std::string path, Json::ArrayIndex si, | |
| FileSetDatabase const& fsdb) | |
| { | |
| Json::Value source = Json::objectValue; | |
| cmSourceFile* sf = this->GT->Makefile->GetOrCreateSource(path); | |
| path = sf->ResolveFullPath(); | |
| source["path"] = RelativeIfUnder(this->TopSource, path); | |
| if (sf->GetIsGenerated()) { | |
| source["isGenerated"] = true; | |
| } | |
| auto fsit = fsdb.find(path); | |
| if (fsit != fsdb.end()) { | |
| source["fileSetIndex"] = fsit->second.back(); | |
| source["fileSetIndexes"] = Json::arrayValue; | |
| for (Json::ArrayIndex const& fsIndex : fsit->second) { | |
| source["fileSetIndexes"].append(fsIndex); | |
| } | |
| } | |
| if (cmSourceGroup const* sg = this->GT->LocalGenerator->FindSourceGroup( | |
| this->GT, sf, this->Config)) { | |
| Json::ArrayIndex const groupIndex = this->AddSourceGroup(sg); | |
| source["sourceGroupIndex"] = groupIndex; | |
| this->SourceGroups[groupIndex].InterfaceSourceIndexes.append(si); | |
| } | |
| 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.Frameworks.empty()) { | |
| Json::Value frameworks = Json::arrayValue; | |
| for (auto const& i : cd.Frameworks) { | |
| frameworks.append(this->DumpFramework(i)); | |
| } | |
| result["frameworks"] = frameworks; | |
| } | |
| 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::DumpFramework(CompileData::IncludeEntry const& fw) | |
| { | |
| // for now, idem as include | |
| return this->DumpInclude(fw); | |
| } | |
| 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); | |
| if (!sg.InterfaceSourceIndexes.empty()) { | |
| group["interfaceSourceIndexes"] = std::move(sg.InterfaceSourceIndexes); | |
| } | |
| 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() == cm::TargetType::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) { | |
| std::string const& 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. | |
| // NOTE: HasImportLibrary() only checks if the target SHOULD have an import | |
| // library, not whether it has one set. | |
| if (this->GT->HasImportLibrary(this->Config)) { | |
| std::string fullPath; | |
| if (this->GT->IsImported()) { | |
| // This imported target might not be well-formed. For Windows, it should | |
| // have its IMPORTED_IMPLIB property set, and CMP0111's NEW behavior is | |
| // intended to catch and report that. But if nothing uses the imported | |
| // target, there won't have been any opportunity to detect that property | |
| // being missing before here. Therefore, we tell ImportedGetFullPath() | |
| // not to raise that CMP0111 error if it sees the problem. We don't want | |
| // to trigger an error for a target that nothing uses, as that would be a | |
| // regression compared to CMake 4.1 and earlier behavior. | |
| fullPath = this->GT->Target->ImportedGetFullPath( | |
| this->Config, cmStateEnums::ImportLibraryArtifact, | |
| cmTarget::ImportArtifactMissingOk::Yes); | |
| if (cmHasLiteralSuffix(fullPath, "-NOTFOUND")) { | |
| fullPath.clear(); | |
| } | |
| } else { | |
| fullPath = this->GT->NormalGetFullPath( | |
| this->Config, cmStateEnums::ImportLibraryArtifact, false); | |
| } | |
| if (!fullPath.empty()) { | |
| Json::Value artifact = Json::objectValue; | |
| artifact["path"] = RelativeIfUnder(this->TopBuild, fullPath); | |
| artifacts.append(std::move(artifact)); // NOLINT(*) | |
| } | |
| } | |
| if (this->GT->IsDLLPlatform() && | |
| this->GT->GetType() != cm::TargetType::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, | |
| cmStrCat(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::DumpLinkItem(cmLinkItem const& linkItem) | |
| { | |
| Json::Value itemJson = Json::objectValue; | |
| if (linkItem.Target) { | |
| itemJson["id"] = TargetId(linkItem.Target, this->TopBuild); | |
| } else { | |
| itemJson["fragment"] = linkItem.AsStr(); | |
| } | |
| if (linkItem.InterfaceDirectFrom) { | |
| Json::Value jsonDirectFrom = Json::objectValue; | |
| jsonDirectFrom["id"] = | |
| TargetId(linkItem.InterfaceDirectFrom, this->TopBuild); | |
| itemJson["fromDependency"] = jsonDirectFrom; | |
| } | |
| this->AddBacktrace(itemJson, linkItem.Backtrace); | |
| return itemJson; | |
| } | |
| Json::Value Target::DumpLinkImplementationLibraries( | |
| cmGeneratorTarget::UseTo usage) | |
| { | |
| Json::Value jsonLibs = Json::arrayValue; | |
| cmLinkImplementationLibraries const* implLibs = | |
| this->GT->GetLinkImplementationLibraries(this->Config, usage); | |
| if (implLibs) { | |
| for (cmLinkItem const& linkItem : implLibs->Libraries) { | |
| // Non-target compile items are never used, so we drop them here too | |
| if (usage == cmGeneratorTarget::UseTo::Link || linkItem.Target) { | |
| jsonLibs.append(this->DumpLinkItem(linkItem)); | |
| } | |
| } | |
| } | |
| return jsonLibs; | |
| } | |
| Json::Value Target::DumpLinkInterfaceLibraries(cmGeneratorTarget::UseTo usage) | |
| { | |
| Json::Value jsonLibs = Json::arrayValue; | |
| cmLinkInterfaceLibraries const* ifaceLibs = | |
| this->GT->GetLinkInterfaceLibraries(this->Config, this->GT, usage); | |
| if (ifaceLibs) { | |
| for (cmLinkItem const& linkItem : ifaceLibs->Libraries) { | |
| // Non-target compile items are never used, so we drop them here too | |
| if (usage == cmGeneratorTarget::UseTo::Link || linkItem.Target) { | |
| jsonLibs.append(this->DumpLinkItem(linkItem)); | |
| } | |
| } | |
| } | |
| return jsonLibs; | |
| } | |
| Json::Value Target::DumpObjectDependencies() | |
| { | |
| // Object dependencies are a special case. They cannot be config-specific | |
| // because they are obtained by matching the pattern $<TARGET_OBJECTS:xxx> | |
| // against the SOURCES property, and the matcher rejects any cases where | |
| // "xxx" contains a generator expression. We can't use | |
| // GetSourceObjectLibraries() either because that also returns object | |
| // libraries added via LINK_LIBRARIES rather than $<TARGET_OBJECTS:xxx>, | |
| // and the whole point of orderDependencies is to capture those that are | |
| // not listed in LINK_LIBRARIES. | |
| std::vector<BT<cmGeneratorTarget*>> objectLibraries; | |
| this->GT->GetObjectLibrariesInSources(objectLibraries); | |
| // We don't want to repeat the same target in the list. We will only | |
| // retain one backtrace for cases where the same target is added multiple | |
| // times from different commands. We also need a deterministic ordering, | |
| // so we can't use cmGeneratorTarget* pointers in a std::set here. | |
| using TargetIdMap = std::map<std::string, BT<cmGeneratorTarget*>>; | |
| TargetIdMap uniqueObjectLibraries; | |
| for (BT<cmGeneratorTarget*> const& target : objectLibraries) { | |
| uniqueObjectLibraries[TargetId(target.Value, this->TopBuild)] = target; | |
| } | |
| Json::Value jsonDependencies = Json::arrayValue; | |
| for (TargetIdMap::value_type const& idTargetPair : uniqueObjectLibraries) { | |
| Json::Value jsonDependency = Json::objectValue; | |
| jsonDependency["id"] = idTargetPair.first; | |
| this->AddBacktrace(jsonDependency, idTargetPair.second.Backtrace); | |
| jsonDependencies.append(jsonDependency); | |
| } | |
| return jsonDependencies; | |
| } | |
| Json::Value Target::DumpOrderDependencies() | |
| { | |
| // The generated build systems don't account for per-config dependencies. | |
| // This is due to limitations of Xcode and/or Visual Studio, which have | |
| // (or at least once had) no way to express a per-config inter-target | |
| // dependency. | |
| Json::Value jsonDependencies = Json::arrayValue; | |
| for (cmLinkItem const& linkItem : this->GT->GetUtilityItems()) { | |
| // We don't want to dump dependencies on reserved targets like ZERO_CHECK. | |
| // We shouldn't see link items that are not targets, but for backward | |
| // compatibility reasons, they are currently allowed but silently ignored. | |
| if (!linkItem.Target || | |
| cmGlobalGenerator::IsReservedTarget(linkItem.Target->GetName())) { | |
| continue; | |
| } | |
| Json::Value jsonDependency = Json::objectValue; | |
| jsonDependency["id"] = TargetId(linkItem.Target, this->TopBuild); | |
| this->AddBacktrace(jsonDependency, linkItem.Backtrace); | |
| jsonDependencies.append(jsonDependency); | |
| } | |
| return jsonDependencies; | |
| } | |
| Json::Value Target::DumpFolder() | |
| { | |
| Json::Value folder; | |
| if (cmValue f = this->GT->GetProperty("FOLDER")) { | |
| folder = Json::objectValue; | |
| folder["name"] = *f; | |
| } | |
| return folder; | |
| } | |
| Json::Value Target::DumpLauncher(char const* name, char const* type) | |
| { | |
| cmValue property = this->GT->GetProperty(name); | |
| Json::Value launcher; | |
| if (property) { | |
| cmLocalGenerator* lg = this->GT->GetLocalGenerator(); | |
| cmGeneratorExpression ge(*lg->GetCMakeInstance()); | |
| cmList commandWithArgs{ ge.Parse(*property)->Evaluate(lg, this->Config) }; | |
| if (!commandWithArgs.empty() && !commandWithArgs[0].empty()) { | |
| std::string command(commandWithArgs[0]); | |
| cmSystemTools::ConvertToUnixSlashes(command); | |
| launcher = Json::objectValue; | |
| launcher["command"] = RelativeIfUnder(this->TopSource, command); | |
| launcher["type"] = type; | |
| Json::Value args; | |
| for (std::string const& arg : cmMakeRange(commandWithArgs).advance(1)) { | |
| args.append(arg); | |
| } | |
| if (!args.empty()) { | |
| launcher["arguments"] = std::move(args); | |
| } | |
| } | |
| } | |
| return launcher; | |
| } | |
| Json::Value Target::DumpLaunchers() | |
| { | |
| Json::Value launchers; | |
| { | |
| Json::Value launcher = DumpLauncher("TEST_LAUNCHER", "test"); | |
| if (!launcher.empty()) { | |
| launchers.append(std::move(launcher)); | |
| } | |
| } | |
| if (this->GT->Makefile->IsOn("CMAKE_CROSSCOMPILING")) { | |
| Json::Value emulator = DumpLauncher("CROSSCOMPILING_EMULATOR", "emulator"); | |
| if (!emulator.empty()) { | |
| launchers.append(std::move(emulator)); | |
| } | |
| } | |
| return launchers; | |
| } | |
| } | |
| Json::Value Target::DumpDebugger() | |
| { | |
| Json::Value debuggerInformation; | |
| if (cmValue debuggerWorkingDirectory = | |
| this->GT->GetGlobalGenerator()->GetDebuggerWorkingDirectory( | |
| this->GT)) { | |
| debuggerInformation = Json::objectValue; | |
| debuggerInformation["workingDirectory"] = *debuggerWorkingDirectory; | |
| } | |
| return debuggerInformation; | |
| } | |
| Json::Value cmFileAPICodemodelDump(cmFileAPI& fileAPI, | |
| unsigned int versionMajor, | |
| unsigned int versionMinor) | |
| { | |
| Codemodel codemodel(fileAPI, versionMajor, versionMinor); | |
| return codemodel.Dump(); | |
| } |