mirror of
https://gitlab.kitware.com/cmake/cmake.git
synced 2026-09-25 04:09:36 +03:00
CMake 4.1 and earlier did not issue an error if an imported shared library target
was missing an IMPORTED_IMPLIB property and nothing used that imported
library. There was no code path checking for the CMP0111 NEW behavior. Since
b626843d71 (fileAPI: Output all INTERFACE and IMPORTED targets, 2025-09-13),
we now include all imported targets in the file API replies, and that does trigger
that check. We need to tolerate such imported targets to preserve backward
compatibility, and to avoid issuing errors for problems in targets likely to be
coming from outside the project and beyond the developer's control.
Fixes: #27496
2417 lines
77 KiB
C++
2417 lines
77 KiB
C++
/* 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 <cmext/string_view>
|
|
|
|
#include <cm3p/json/value.h>
|
|
|
|
#include "cmCryptoHash.h"
|
|
#include "cmExportSet.h"
|
|
#include "cmFileAPI.h"
|
|
#include "cmFileSet.h"
|
|
#include "cmGenExContext.h"
|
|
#include "cmGeneratorExpression.h"
|
|
#include "cmGeneratorTarget.h"
|
|
#include "cmGlobalGenerator.h"
|
|
#include "cmInstallCxxModuleBmiGenerator.h"
|
|
#include "cmInstallDirectoryGenerator.h"
|
|
#include "cmInstallExportGenerator.h"
|
|
#include "cmInstallFileSetGenerator.h"
|
|
#include "cmInstallFilesGenerator.h"
|
|
#include "cmInstallGenerator.h"
|
|
#include "cmInstallGetRuntimeDependenciesGenerator.h"
|
|
#include "cmInstallImportedRuntimeArtifactsGenerator.h"
|
|
#include "cmInstallRuntimeDependencySet.h"
|
|
#include "cmInstallRuntimeDependencySetGenerator.h"
|
|
#include "cmInstallScriptGenerator.h"
|
|
#include "cmInstallSubdirectoryGenerator.h"
|
|
#include "cmInstallTargetGenerator.h"
|
|
#include "cmLinkItem.h"
|
|
#include "cmLinkLineComputer.h" // IWYU pragma: keep
|
|
#include "cmList.h"
|
|
#include "cmListFileCache.h"
|
|
#include "cmLocalGenerator.h"
|
|
#include "cmMakefile.h"
|
|
#include "cmMessageType.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 "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 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;
|
|
std::vector<cmSourceGroup> SourceGroupsLocal;
|
|
BacktraceData Backtraces;
|
|
|
|
std::map<std::string, CompileData> CompileDataMap;
|
|
|
|
std::unordered_map<cmSourceFile const*, Json::ArrayIndex> SourceMap;
|
|
Json::Value Sources = Json::arrayValue;
|
|
|
|
struct SourceGroup
|
|
{
|
|
std::string Name;
|
|
Json::Value SourceIndexes = Json::arrayValue;
|
|
};
|
|
std::unordered_map<cmSourceGroup const*, Json::ArrayIndex> SourceGroupsMap;
|
|
std::vector<SourceGroup> SourceGroups;
|
|
|
|
struct CompileGroup
|
|
{
|
|
std::unordered_map<CompileData, Json::ArrayIndex>::iterator Entry;
|
|
Json::Value SourceIndexes = Json::arrayValue;
|
|
};
|
|
std::unordered_map<CompileData, Json::ArrayIndex> CompileGroupMap;
|
|
std::vector<CompileGroup> CompileGroups;
|
|
|
|
using FileSetDatabase = std::map<std::string, Json::ArrayIndex>;
|
|
|
|
template <typename T>
|
|
JBT<T> ToJBT(BT<T> const& bt)
|
|
{
|
|
return JBT<T>(bt.Value, this->Backtraces.Add(bt.Backtrace));
|
|
}
|
|
|
|
template <typename T>
|
|
JBTs<T> ToJBTs(BTs<T> const& bts)
|
|
{
|
|
std::vector<JBTIndex> ids;
|
|
ids.reserve(bts.Backtraces.size());
|
|
for (cmListFileBacktrace const& backtrace : bts.Backtraces) {
|
|
ids.emplace_back(this->Backtraces.Add(backtrace));
|
|
}
|
|
return JBTs<T>(bts.Value, ids);
|
|
}
|
|
|
|
void ProcessLanguages();
|
|
void ProcessLanguage(std::string const& lang);
|
|
|
|
Json::ArrayIndex AddSourceGroup(cmSourceGroup* sg, Json::ArrayIndex si);
|
|
CompileData BuildCompileData(cmSourceFile* sf);
|
|
CompileData MergeCompileData(CompileData const& fd);
|
|
Json::ArrayIndex AddSourceCompileGroup(cmSourceFile* sf,
|
|
Json::ArrayIndex si);
|
|
void AddBacktrace(Json::Value& object, cmListFileBacktrace const& bt);
|
|
void AddBacktrace(Json::Value& object, JBTIndex bt);
|
|
Json::Value DumpPaths();
|
|
Json::Value DumpCompileData(CompileData const& cd);
|
|
Json::Value DumpInclude(CompileData::IncludeEntry const& inc);
|
|
Json::Value 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(cmFileSet const* fs,
|
|
std::vector<std::string> const& directories);
|
|
Json::Value DumpSources(FileSetDatabase const& fsdb);
|
|
Json::Value DumpSource(cmGeneratorTarget::SourceAndKind const& sk,
|
|
Json::ArrayIndex si, FileSetDatabase const& fsdb);
|
|
Json::Value DumpSourceGroups();
|
|
Json::Value DumpSourceGroup(SourceGroup& sg);
|
|
Json::Value DumpCompileGroups();
|
|
Json::Value DumpCompileGroup(CompileGroup& cg);
|
|
Json::Value DumpSysroot(std::string const& path);
|
|
Json::Value DumpInstall();
|
|
Json::Value DumpInstallPrefix();
|
|
Json::Value DumpInstallDestinations();
|
|
Json::Value DumpInstallDestination(cmInstallTargetGenerator* itGen);
|
|
Json::Value DumpArtifacts();
|
|
Json::Value DumpLink();
|
|
Json::Value DumpArchive();
|
|
Json::Value DumpLinkCommandFragments();
|
|
Json::Value DumpCommandFragments(std::vector<JBT<std::string>> const& frags);
|
|
Json::Value DumpCommandFragment(JBT<std::string> const& frag,
|
|
std::string const& role = std::string());
|
|
Json::Value DumpDependencies();
|
|
Json::Value DumpDependency(cmTargetDepend const& td);
|
|
|
|
Json::Value 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() == cmStateEnums::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 += "-" + 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(prefix, '-', buildDirRel);
|
|
} else if (!cmSystemTools::FileIsFullPath(sourceDirRel)) {
|
|
prefix = cmStrCat(prefix, '-', sourceDirRel);
|
|
}
|
|
for (char& c : prefix) {
|
|
if (c == '/' || c == '\\') {
|
|
c = '.';
|
|
}
|
|
}
|
|
if (!this->Config.empty()) {
|
|
prefix += "-" + this->Config;
|
|
}
|
|
|
|
DirectoryObject dir(d.LocalGenerator, this->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 (cmHasLiteralSuffix(dir, "/")) {
|
|
paths.append(this->DumpInstallerPath(
|
|
this->TopSource, dir.substr(0, dir.size() - 1), "."));
|
|
} else {
|
|
paths.append(this->DumpInstallerPath(
|
|
this->TopSource, dir, cmSystemTools::GetFilenameName(dir)));
|
|
}
|
|
}
|
|
installer["paths"] = std::move(paths);
|
|
if (installDir->GetOptional()) {
|
|
installer["isOptional"] = true;
|
|
}
|
|
// FIXME: Parse FilePermissions, DirPermissions, and LiteralArguments.
|
|
// to provide structured information.
|
|
} else if (auto* installExport =
|
|
dynamic_cast<cmInstallExportGenerator*>(gen)) {
|
|
installer["type"] = "export";
|
|
installer["destination"] = installExport->GetDestination();
|
|
cmExportSet* exportSet = installExport->GetExportSet();
|
|
installer["exportName"] = exportSet->GetName();
|
|
installer["exportTargets"] = this->DumpInstallerExportTargets(exportSet);
|
|
Json::Value paths = Json::arrayValue;
|
|
paths.append(
|
|
RelativeIfUnder(this->TopBuild, installExport->GetMainImportFile()));
|
|
installer["paths"] = std::move(paths);
|
|
} else if (auto* installScript =
|
|
dynamic_cast<cmInstallScriptGenerator*>(gen)) {
|
|
if (installScript->IsCode()) {
|
|
installer["type"] = "code";
|
|
} else {
|
|
installer["type"] = "script";
|
|
installer["scriptFile"] = RelativeIfUnder(
|
|
this->TopSource, installScript->GetScript(this->Config));
|
|
}
|
|
} else if (auto* installImportedRuntimeArtifacts =
|
|
dynamic_cast<cmInstallImportedRuntimeArtifactsGenerator*>(
|
|
gen)) {
|
|
installer["type"] = "importedRuntimeArtifacts";
|
|
installer["destination"] =
|
|
installImportedRuntimeArtifacts->GetDestination(this->Config);
|
|
if (installImportedRuntimeArtifacts->GetOptional()) {
|
|
installer["isOptional"] = true;
|
|
}
|
|
} else if (auto* installRuntimeDependencySet =
|
|
dynamic_cast<cmInstallRuntimeDependencySetGenerator*>(gen)) {
|
|
installer["type"] = "runtimeDependencySet";
|
|
installer["destination"] =
|
|
installRuntimeDependencySet->GetDestination(this->Config);
|
|
std::string name(
|
|
installRuntimeDependencySet->GetRuntimeDependencySet()->GetName());
|
|
if (!name.empty()) {
|
|
installer["runtimeDependencySetName"] = name;
|
|
}
|
|
switch (installRuntimeDependencySet->GetDependencyType()) {
|
|
case cmInstallRuntimeDependencySetGenerator::DependencyType::Framework:
|
|
installer["runtimeDependencySetType"] = "framework";
|
|
break;
|
|
case cmInstallRuntimeDependencySetGenerator::DependencyType::Library:
|
|
installer["runtimeDependencySetType"] = "library";
|
|
break;
|
|
}
|
|
} else if (auto* installFileSet =
|
|
dynamic_cast<cmInstallFileSetGenerator*>(gen)) {
|
|
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 dirCges = fileSet->CompileDirectoryEntries();
|
|
auto dirs = fileSet->EvaluateDirectoryEntries(dirCges, context, target);
|
|
|
|
auto entryCges = fileSet->CompileFileEntries();
|
|
std::map<std::string, std::vector<std::string>> entries;
|
|
for (auto const& entryCge : entryCges) {
|
|
fileSet->EvaluateFileEntry(dirs, entries, entryCge, context, target);
|
|
}
|
|
|
|
Json::Value files = Json::arrayValue;
|
|
for (auto const& it : entries) {
|
|
auto dir = it.first;
|
|
if (!dir.empty()) {
|
|
dir += '/';
|
|
}
|
|
for (auto const& file : it.second) {
|
|
files.append(this->DumpInstallerPath(
|
|
this->TopSource, file,
|
|
cmStrCat(dir, cmSystemTools::GetFilenameName(file))));
|
|
}
|
|
}
|
|
installer["paths"] = std::move(files);
|
|
installer["fileSetName"] = fileSet->GetName();
|
|
installer["fileSetType"] = fileSet->GetType();
|
|
installer["fileSetDirectories"] = Json::arrayValue;
|
|
for (auto const& dir : dirs) {
|
|
installer["fileSetDirectories"].append(
|
|
RelativeIfUnder(this->TopSource, dir));
|
|
}
|
|
installer["fileSetTarget"] = Json::objectValue;
|
|
installer["fileSetTarget"]["id"] = TargetId(target, this->TopBuild);
|
|
installer["fileSetTarget"]["index"] = this->TargetIndexMap[target];
|
|
|
|
if (installFileSet->GetOptional()) {
|
|
installer["isOptional"] = true;
|
|
}
|
|
} else if (auto* cxxModuleBmi =
|
|
dynamic_cast<cmInstallCxxModuleBmiGenerator*>(gen)) {
|
|
installer["type"] = "cxxModuleBmi";
|
|
installer["destination"] = cxxModuleBmi->GetDestination(this->Config);
|
|
|
|
auto const* target = cxxModuleBmi->GetTarget();
|
|
installer["cxxModuleBmiTarget"] = Json::objectValue;
|
|
installer["cxxModuleBmiTarget"]["id"] = TargetId(target, this->TopBuild);
|
|
installer["cxxModuleBmiTarget"]["index"] = this->TargetIndexMap[target];
|
|
|
|
// FIXME: Parse FilePermissions.
|
|
// FIXME: Parse MessageLevel.
|
|
if (cxxModuleBmi->GetOptional()) {
|
|
installer["isOptional"] = true;
|
|
}
|
|
}
|
|
|
|
// Add fields common to all install generators.
|
|
installer["component"] = gen->GetComponent();
|
|
if (gen->GetExcludeFromAll()) {
|
|
installer["isExcludeFromAll"] = true;
|
|
}
|
|
|
|
if (gen->GetAllComponentsFlag()) {
|
|
installer["isForAllComponents"] = true;
|
|
}
|
|
|
|
this->AddBacktrace(installer, gen->GetBacktrace());
|
|
|
|
return installer;
|
|
}
|
|
|
|
Json::Value DirectoryObject::DumpInstallerExportTargets(cmExportSet* exp)
|
|
{
|
|
Json::Value targets = Json::arrayValue;
|
|
for (auto const& targetExport : exp->GetTargetExports()) {
|
|
Json::Value target = Json::objectValue;
|
|
target["id"] = TargetId(targetExport->Target, this->TopBuild);
|
|
target["index"] = this->TargetIndexMap[targetExport->Target];
|
|
targets.append(std::move(target)); // NOLINT(*)
|
|
}
|
|
return targets;
|
|
}
|
|
|
|
Json::Value DirectoryObject::DumpInstallerPath(std::string const& top,
|
|
std::string const& fromPathIn,
|
|
std::string const& toPath)
|
|
{
|
|
Json::Value installPath;
|
|
|
|
std::string fromPath = RelativeIfUnder(top, fromPathIn);
|
|
|
|
// If toPath is the last component of fromPath, use just fromPath.
|
|
if (toPath.find_first_of('/') == std::string::npos &&
|
|
cmHasSuffix(fromPath, toPath) &&
|
|
(fromPath.size() == toPath.size() ||
|
|
fromPath[fromPath.size() - toPath.size() - 1] == '/')) {
|
|
installPath = fromPath;
|
|
} else {
|
|
installPath = Json::objectValue;
|
|
installPath["from"] = fromPath;
|
|
installPath["to"] = toPath;
|
|
}
|
|
|
|
return installPath;
|
|
}
|
|
|
|
Target::Target(cmGeneratorTarget* gt, 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())
|
|
, SourceGroupsLocal(this->GT->Makefile->GetSourceGroups())
|
|
, Backtraces(this->TopSource)
|
|
{
|
|
}
|
|
|
|
Json::Value Target::Dump()
|
|
{
|
|
Json::Value target = Json::objectValue;
|
|
|
|
cmStateEnums::TargetType const type = this->GT->GetType();
|
|
|
|
target["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 == cmStateEnums::EXECUTABLE ||
|
|
type == cmStateEnums::SHARED_LIBRARY ||
|
|
type == cmStateEnums::MODULE_LIBRARY) {
|
|
target["nameOnDisk"] = this->GT->GetFullName(this->Config);
|
|
if (!this->GT->IsImported()) {
|
|
target["link"] = this->DumpLink();
|
|
}
|
|
} else if (type == cmStateEnums::STATIC_LIBRARY) {
|
|
target["nameOnDisk"] = this->GT->GetFullName(this->Config);
|
|
if (!this->GT->IsImported()) {
|
|
target["archive"] = this->DumpArchive();
|
|
}
|
|
}
|
|
|
|
if (type == cmStateEnums::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);
|
|
|
|
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* sg, Json::ArrayIndex si)
|
|
{
|
|
auto i = this->SourceGroupsMap.find(sg);
|
|
if (i == this->SourceGroupsMap.end()) {
|
|
auto sgIndex = static_cast<Json::ArrayIndex>(this->SourceGroups.size());
|
|
i = this->SourceGroupsMap.emplace(sg, sgIndex).first;
|
|
SourceGroup g;
|
|
g.Name = sg->GetFullName();
|
|
this->SourceGroups.push_back(std::move(g));
|
|
}
|
|
this->SourceGroups[i->second].SourceIndexes.append(si);
|
|
return i->second;
|
|
}
|
|
|
|
CompileData Target::BuildCompileData(cmSourceFile* sf)
|
|
{
|
|
CompileData fd;
|
|
|
|
fd.Language = sf->GetOrDetermineLanguage();
|
|
if (fd.Language.empty()) {
|
|
return fd;
|
|
}
|
|
|
|
cmLocalGenerator* lg = this->GT->GetLocalGenerator();
|
|
cmGeneratorExpressionInterpreter genexInterpreter(lg, this->Config, this->GT,
|
|
fd.Language);
|
|
|
|
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));
|
|
}
|
|
|
|
// 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() && !sf->GetProperty("SKIP_PRECOMPILE_HEADERS")) {
|
|
std::string pchOptions;
|
|
auto pchIt = pchSources.find(sf->ResolveFullPath());
|
|
if (pchIt != pchSources.end()) {
|
|
pchOptions = this->GT->GetPchCreateCompileOptions(
|
|
this->Config, fd.Language, pchIt->second);
|
|
} else {
|
|
pchOptions =
|
|
this->GT->GetPchUseCompileOptions(this->Config, fd.Language);
|
|
}
|
|
|
|
BT<std::string> tmpOpt(pchOptions);
|
|
tmpOpt.Value = genexInterpreter.Evaluate(tmpOpt.Value, COMPILE_OPTIONS);
|
|
|
|
// After generator evaluation we need to use the AppendCompileOptions
|
|
// method so we handle situations where backtrace entries have lists
|
|
// and properly escape flags.
|
|
std::string tmp;
|
|
lg->AppendCompileOptions(tmp, tmpOpt.Value);
|
|
BT<std::string> opt(tmp, tmpOpt.Backtrace);
|
|
fd.Flags.emplace_back(this->ToJBT(opt));
|
|
}
|
|
|
|
// Add include directories from source file properties.
|
|
{
|
|
std::string const INCLUDE_DIRECTORIES("INCLUDE_DIRECTORIES");
|
|
for (BT<std::string> tmpInclude : sf->GetIncludeDirectories()) {
|
|
tmpInclude.Value =
|
|
genexInterpreter.Evaluate(tmpInclude.Value, INCLUDE_DIRECTORIES);
|
|
|
|
// After generator evaluation we need to use the AppendIncludeDirectories
|
|
// method so we handle situations where backtrace entries have lists.
|
|
std::vector<std::string> tmp;
|
|
lg->AppendIncludeDirectories(tmp, tmpInclude.Value, *sf);
|
|
for (std::string& i : tmp) {
|
|
bool const isSystemInclude =
|
|
this->GT->IsSystemIncludeDirectory(i, this->Config, fd.Language);
|
|
BT<std::string> include(i, tmpInclude.Backtrace);
|
|
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));
|
|
}
|
|
|
|
fd.Defines.reserve(fileDefines.size() + configFileDefines.size());
|
|
|
|
for (BT<std::string> const& def : fileDefines) {
|
|
fd.Defines.emplace_back(this->ToJBT(def));
|
|
}
|
|
|
|
for (std::string const& d : configFileDefines) {
|
|
fd.Defines.emplace_back(d, JBTIndex());
|
|
}
|
|
|
|
return fd;
|
|
}
|
|
|
|
CompileData Target::MergeCompileData(CompileData const& fd)
|
|
{
|
|
CompileData cd;
|
|
cd.Language = fd.Language;
|
|
if (cd.Language.empty()) {
|
|
return cd;
|
|
}
|
|
CompileData const& td = this->CompileDataMap.at(cd.Language);
|
|
|
|
// All compile groups share the sysroot of the target.
|
|
cd.Sysroot = td.Sysroot;
|
|
|
|
// All compile groups share the precompile headers of the target.
|
|
cd.PrecompileHeaders = td.PrecompileHeaders;
|
|
|
|
// All compile groups share the language standard of the target.
|
|
cd.LanguageStandard = td.LanguageStandard;
|
|
|
|
// Use target-wide flags followed by source-specific flags.
|
|
cd.Flags.reserve(td.Flags.size() + fd.Flags.size());
|
|
cd.Flags.insert(cd.Flags.end(), td.Flags.begin(), td.Flags.end());
|
|
cd.Flags.insert(cd.Flags.end(), fd.Flags.begin(), fd.Flags.end());
|
|
|
|
// Use source-specific includes followed by target-wide includes.
|
|
cd.Includes.reserve(fd.Includes.size() + td.Includes.size());
|
|
cd.Includes.insert(cd.Includes.end(), fd.Includes.begin(),
|
|
fd.Includes.end());
|
|
cd.Includes.insert(cd.Includes.end(), td.Includes.begin(),
|
|
td.Includes.end());
|
|
|
|
// Use 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;
|
|
|
|
// Build the fileset database.
|
|
auto const* tgt = this->GT->Target;
|
|
auto const& fs_names = tgt->GetAllFileSetNames();
|
|
|
|
if (!fs_names.empty()) {
|
|
fsJson = Json::arrayValue;
|
|
size_t fsIndex = 0;
|
|
for (auto const& fs_name : fs_names) {
|
|
auto const* fs = tgt->GetFileSet(fs_name);
|
|
if (!fs) {
|
|
this->GT->Makefile->IssueMessage(
|
|
MessageType::INTERNAL_ERROR,
|
|
cmStrCat("Target \"", tgt->GetName(),
|
|
"\" is tracked to have file set \"", fs_name,
|
|
"\", but it was not found."));
|
|
continue;
|
|
}
|
|
|
|
cm::GenEx::Context context(this->GT->LocalGenerator, this->Config);
|
|
|
|
auto fileEntries = fs->CompileFileEntries();
|
|
auto directoryEntries = fs->CompileDirectoryEntries();
|
|
|
|
auto directories =
|
|
fs->EvaluateDirectoryEntries(directoryEntries, context, this->GT);
|
|
|
|
fsJson.append(this->DumpFileSet(fs, directories));
|
|
|
|
std::map<std::string, std::vector<std::string>> files_per_dirs;
|
|
for (auto const& entry : fileEntries) {
|
|
fs->EvaluateFileEntry(directories, files_per_dirs, entry, context,
|
|
this->GT);
|
|
}
|
|
|
|
for (auto const& files_per_dir : files_per_dirs) {
|
|
auto const& dir = files_per_dir.first;
|
|
for (auto const& file : files_per_dir.second) {
|
|
std::string sf_path;
|
|
if (dir.empty()) {
|
|
sf_path = file;
|
|
} else {
|
|
sf_path = cmStrCat(dir, '/', file);
|
|
}
|
|
fsdb[sf_path] = static_cast<Json::ArrayIndex>(fsIndex);
|
|
}
|
|
}
|
|
|
|
++fsIndex;
|
|
}
|
|
}
|
|
|
|
return std::make_pair(fsJson, fsdb);
|
|
}
|
|
|
|
Json::Value Target::DumpFileSet(cmFileSet const* fs,
|
|
std::vector<std::string> const& directories)
|
|
{
|
|
Json::Value fileSet = Json::objectValue;
|
|
|
|
fileSet["name"] = fs->GetName();
|
|
fileSet["type"] = fs->GetType();
|
|
fileSet["visibility"] =
|
|
std::string(cmFileSetVisibilityToName(fs->GetVisibility()));
|
|
|
|
Json::Value baseDirs = Json::arrayValue;
|
|
for (auto const& directory : directories) {
|
|
baseDirs.append(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;
|
|
}
|
|
this->AddBacktrace(source, sk.Source.Backtrace);
|
|
|
|
auto fsit = fsdb.find(path);
|
|
if (fsit != fsdb.end()) {
|
|
source["fileSetIndex"] = fsit->second;
|
|
}
|
|
|
|
if (cmSourceGroup* sg =
|
|
this->GT->Makefile->FindSourceGroup(path, this->SourceGroupsLocal)) {
|
|
source["sourceGroupIndex"] = this->AddSourceGroup(sg, si);
|
|
}
|
|
|
|
switch (sk.Kind) {
|
|
case cmGeneratorTarget::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:
|
|
break;
|
|
}
|
|
|
|
return source;
|
|
}
|
|
|
|
Json::Value Target::DumpCompileData(CompileData const& cd)
|
|
{
|
|
Json::Value result = Json::objectValue;
|
|
|
|
if (!cd.Language.empty()) {
|
|
result["language"] = cd.Language;
|
|
}
|
|
if (!cd.Sysroot.empty()) {
|
|
result["sysroot"] = this->DumpSysroot(cd.Sysroot);
|
|
}
|
|
if (!cd.Flags.empty()) {
|
|
result["compileCommandFragments"] = this->DumpCommandFragments(cd.Flags);
|
|
}
|
|
if (!cd.Includes.empty()) {
|
|
Json::Value includes = Json::arrayValue;
|
|
for (auto const& i : cd.Includes) {
|
|
includes.append(this->DumpInclude(i));
|
|
}
|
|
result["includes"] = includes;
|
|
}
|
|
if (!cd.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);
|
|
return group;
|
|
}
|
|
|
|
Json::Value Target::DumpCompileGroups()
|
|
{
|
|
Json::Value compileGroups = Json::arrayValue;
|
|
for (auto& cg : this->CompileGroups) {
|
|
compileGroups.append(this->DumpCompileGroup(cg));
|
|
}
|
|
return compileGroups;
|
|
}
|
|
|
|
Json::Value Target::DumpCompileGroup(CompileGroup& cg)
|
|
{
|
|
Json::Value group =
|
|
this->DumpCompileData(this->MergeCompileData(cg.Entry->first));
|
|
group["sourceIndexes"] = std::move(cg.SourceIndexes);
|
|
return group;
|
|
}
|
|
|
|
Json::Value Target::DumpSysroot(std::string const& path)
|
|
{
|
|
Json::Value sysroot = Json::objectValue;
|
|
sysroot["path"] = path;
|
|
return sysroot;
|
|
}
|
|
|
|
Json::Value Target::DumpInstall()
|
|
{
|
|
Json::Value install = Json::objectValue;
|
|
install["prefix"] = this->DumpInstallPrefix();
|
|
install["destinations"] = this->DumpInstallDestinations();
|
|
return install;
|
|
}
|
|
|
|
Json::Value Target::DumpInstallPrefix()
|
|
{
|
|
Json::Value prefix = Json::objectValue;
|
|
std::string p =
|
|
this->GT->Makefile->GetSafeDefinition("CMAKE_INSTALL_PREFIX");
|
|
cmSystemTools::ConvertToUnixSlashes(p);
|
|
prefix["path"] = p;
|
|
return prefix;
|
|
}
|
|
|
|
Json::Value Target::DumpInstallDestinations()
|
|
{
|
|
Json::Value destinations = Json::arrayValue;
|
|
auto installGens = this->GT->Target->GetInstallGenerators();
|
|
for (auto* itGen : installGens) {
|
|
destinations.append(this->DumpInstallDestination(itGen));
|
|
}
|
|
return destinations;
|
|
}
|
|
|
|
Json::Value Target::DumpInstallDestination(cmInstallTargetGenerator* itGen)
|
|
{
|
|
Json::Value destination = Json::objectValue;
|
|
destination["path"] = itGen->GetDestination(this->Config);
|
|
this->AddBacktrace(destination, itGen->GetBacktrace());
|
|
return destination;
|
|
}
|
|
|
|
Json::Value Target::DumpArtifacts()
|
|
{
|
|
Json::Value artifacts = Json::arrayValue;
|
|
|
|
// Object libraries have only object files as artifacts.
|
|
if (this->GT->GetType() == cmStateEnums::OBJECT_LIBRARY) {
|
|
if (!this->GT->Target->HasKnownObjectFileLocation(nullptr)) {
|
|
return artifacts;
|
|
}
|
|
std::vector<cmSourceFile const*> objectSources;
|
|
this->GT->GetObjectSources(objectSources, this->Config);
|
|
std::string const obj_dir = this->GT->GetObjectDirectory(this->Config);
|
|
for (cmSourceFile const* sf : objectSources) {
|
|
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() != cmStateEnums::STATIC_LIBRARY) {
|
|
cmGeneratorTarget::OutputInfo const* output =
|
|
this->GT->GetOutputInfo(this->Config);
|
|
if (output && !output->PdbDir.empty()) {
|
|
Json::Value artifact = Json::objectValue;
|
|
artifact["path"] = RelativeIfUnder(this->TopBuild,
|
|
output->PdbDir + '/' +
|
|
this->GT->GetPDBName(this->Config));
|
|
artifacts.append(std::move(artifact)); // NOLINT(*)
|
|
}
|
|
}
|
|
return artifacts;
|
|
}
|
|
|
|
Json::Value Target::DumpLink()
|
|
{
|
|
Json::Value link = Json::objectValue;
|
|
std::string lang = this->GT->GetLinkerLanguage(this->Config);
|
|
link["language"] = lang;
|
|
{
|
|
Json::Value commandFragments = this->DumpLinkCommandFragments();
|
|
if (!commandFragments.empty()) {
|
|
link["commandFragments"] = std::move(commandFragments);
|
|
}
|
|
}
|
|
if (cmValue sysrootLink =
|
|
this->GT->Makefile->GetDefinition("CMAKE_SYSROOT_LINK")) {
|
|
link["sysroot"] = this->DumpSysroot(*sysrootLink);
|
|
} else if (cmValue sysroot =
|
|
this->GT->Makefile->GetDefinition("CMAKE_SYSROOT")) {
|
|
link["sysroot"] = this->DumpSysroot(*sysroot);
|
|
}
|
|
if (this->GT->IsIPOEnabled(lang, this->Config)) {
|
|
link["lto"] = true;
|
|
}
|
|
return link;
|
|
}
|
|
|
|
Json::Value Target::DumpArchive()
|
|
{
|
|
Json::Value archive = Json::objectValue;
|
|
{
|
|
// The "link" fragments not relevant to static libraries are empty.
|
|
Json::Value commandFragments = this->DumpLinkCommandFragments();
|
|
if (!commandFragments.empty()) {
|
|
archive["commandFragments"] = std::move(commandFragments);
|
|
}
|
|
}
|
|
std::string lang = this->GT->GetLinkerLanguage(this->Config);
|
|
if (this->GT->IsIPOEnabled(lang, this->Config)) {
|
|
archive["lto"] = true;
|
|
}
|
|
return archive;
|
|
}
|
|
|
|
Json::Value Target::DumpLinkCommandFragments()
|
|
{
|
|
Json::Value linkFragments = Json::arrayValue;
|
|
|
|
std::string linkLanguageFlags;
|
|
std::vector<BT<std::string>> linkFlags;
|
|
std::string frameworkPath;
|
|
std::vector<BT<std::string>> linkPath;
|
|
std::vector<BT<std::string>> linkLibs;
|
|
cmLocalGenerator* lg = this->GT->GetLocalGenerator();
|
|
cmGlobalGenerator* gg = this->GT->GetGlobalGenerator();
|
|
std::unique_ptr<cmLinkLineComputer> linkLineComputer =
|
|
gg->CreateLinkLineComputer(lg, lg->GetStateSnapshot().GetDirectory());
|
|
lg->GetTargetFlags(linkLineComputer.get(), this->Config, linkLibs,
|
|
linkLanguageFlags, linkFlags, frameworkPath, linkPath,
|
|
this->GT);
|
|
linkLanguageFlags = cmTrimWhitespace(linkLanguageFlags);
|
|
frameworkPath = cmTrimWhitespace(frameworkPath);
|
|
|
|
if (!linkLanguageFlags.empty()) {
|
|
linkFragments.append(
|
|
this->DumpCommandFragment(std::move(linkLanguageFlags), "flags"));
|
|
}
|
|
|
|
if (!linkFlags.empty()) {
|
|
for (BT<std::string> frag : linkFlags) {
|
|
frag.Value = cmTrimWhitespace(frag.Value);
|
|
linkFragments.append(
|
|
this->DumpCommandFragment(this->ToJBT(frag), "flags"));
|
|
}
|
|
}
|
|
|
|
if (!frameworkPath.empty()) {
|
|
linkFragments.append(
|
|
this->DumpCommandFragment(std::move(frameworkPath), "frameworkPath"));
|
|
}
|
|
|
|
if (!linkPath.empty()) {
|
|
for (BT<std::string> frag : linkPath) {
|
|
frag.Value = cmTrimWhitespace(frag.Value);
|
|
linkFragments.append(
|
|
this->DumpCommandFragment(this->ToJBT(frag), "libraryPath"));
|
|
}
|
|
}
|
|
|
|
if (!linkLibs.empty()) {
|
|
for (BT<std::string> frag : linkLibs) {
|
|
frag.Value = cmTrimWhitespace(frag.Value);
|
|
linkFragments.append(
|
|
this->DumpCommandFragment(this->ToJBT(frag), "libraries"));
|
|
}
|
|
}
|
|
|
|
return linkFragments;
|
|
}
|
|
|
|
Json::Value Target::DumpCommandFragments(
|
|
std::vector<JBT<std::string>> const& frags)
|
|
{
|
|
Json::Value commandFragments = Json::arrayValue;
|
|
for (JBT<std::string> const& f : frags) {
|
|
commandFragments.append(this->DumpCommandFragment(f));
|
|
}
|
|
return commandFragments;
|
|
}
|
|
|
|
Json::Value Target::DumpCommandFragment(JBT<std::string> const& frag,
|
|
std::string const& role)
|
|
{
|
|
Json::Value fragment = Json::objectValue;
|
|
fragment["fragment"] = frag.Value;
|
|
if (!role.empty()) {
|
|
fragment["role"] = role;
|
|
}
|
|
this->AddBacktrace(fragment, frag.Backtrace);
|
|
return fragment;
|
|
}
|
|
|
|
Json::Value Target::DumpDependencies()
|
|
{
|
|
Json::Value dependencies = Json::arrayValue;
|
|
cmGlobalGenerator* gg = this->GT->GetGlobalGenerator();
|
|
for (cmTargetDepend const& td : gg->GetTargetDirectDepends(this->GT)) {
|
|
dependencies.append(this->DumpDependency(td));
|
|
}
|
|
return dependencies;
|
|
}
|
|
|
|
Json::Value Target::DumpDependency(cmTargetDepend const& td)
|
|
{
|
|
Json::Value dependency = Json::objectValue;
|
|
dependency["id"] = TargetId(td, this->TopBuild);
|
|
this->AddBacktrace(dependency, td.GetBacktrace());
|
|
return dependency;
|
|
}
|
|
|
|
Json::Value Target::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();
|
|
}
|