[WIP] JSON generator
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index 7031d5a..146ef36 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -181,12 +181,16 @@
cmExportBuildAndroidMKGenerator.cxx
cmExportBuildFileGenerator.h
cmExportBuildFileGenerator.cxx
+ cmExportBuildJSONGenerator.h
+ cmExportBuildJSONGenerator.cxx
cmExportFileGenerator.h
cmExportFileGenerator.cxx
cmExportInstallAndroidMKGenerator.h
cmExportInstallAndroidMKGenerator.cxx
cmExportInstallFileGenerator.h
cmExportInstallFileGenerator.cxx
+ cmExportInstallJSONGenerator.h
+ cmExportInstallJSONGenerator.cxx
cmExportTryCompileFileGenerator.h
cmExportTryCompileFileGenerator.cxx
cmExportSet.h
diff --git a/Source/cmExportBuildJSONGenerator.cxx b/Source/cmExportBuildJSONGenerator.cxx
new file mode 100644
index 0000000..4b7918e
--- /dev/null
+++ b/Source/cmExportBuildJSONGenerator.cxx
@@ -0,0 +1,298 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#include "cmExportBuildJSONGenerator.h"
+
+#include <algorithm>
+#include <memory> // IWYU pragma: keep
+#include <numeric>
+#include <sstream>
+#include <utility>
+
+#include "cmExportSet.h"
+#include "cmGeneratorExpression.h"
+#include "cmGeneratorTarget.h"
+#include "cmLinkItem.h"
+#include "cmGlobalGenerator.h"
+#include "cmLocalGenerator.h"
+#include "cmMakefile.h"
+#include "cmPolicies.h"
+#include "cmStateTypes.h"
+#include "cmSystemTools.h"
+#include "cmTarget.h"
+#include "cmake.h"
+
+cmExportBuildJSONGenerator::cmExportBuildJSONGenerator()
+{
+ this->LG = nullptr;
+ this->ExportSet = nullptr;
+}
+
+void cmExportBuildJSONGenerator::Compute(cmLocalGenerator* lg)
+{
+ this->LG = lg;
+ if (this->ExportSet) {
+ this->ExportSet->Compute(lg);
+ }
+}
+
+bool cmExportBuildJSONGenerator::GenerateMainFile(std::ostream& os)
+{
+ {
+ std::string expectedTargets;
+ std::string sep;
+ std::vector<std::string> targets;
+ this->GetTargets(targets);
+ for (std::string const& tei : targets) {
+ cmGeneratorTarget* te = this->LG->FindGeneratorTargetToUse(tei);
+ expectedTargets += sep + this->Namespace + te->GetExportName();
+ sep = " ";
+ if (this->ExportedTargets.insert(te).second) {
+ this->Exports.push_back(te);
+ } else {
+ std::ostringstream e;
+ e << "given target \"" << te->GetName() << "\" more than once.";
+ this->LG->GetGlobalGenerator()->GetCMakeInstance()->IssueMessage(
+ cmake::FATAL_ERROR, e.str(),
+ this->LG->GetMakefile()->GetBacktrace());
+ return false;
+ }
+ }
+ }
+
+ std::vector<std::string> missingTargets;
+
+ os << "{\n"
+ << " \"targets\": {\n";
+
+ // Create all the imported targets.
+ for (auto i = this->Exports.begin(); i != this->Exports.end(); ++i) {
+ cmGeneratorTarget* gte = *i;
+
+ this->GenerateImportTargetCode(os, gte);
+
+ gte->Target->AppendBuildInterfaceIncludes();
+
+ ImportPropertyMap properties;
+
+ this->PopulateInterfaceProperty("INTERFACE_INCLUDE_DIRECTORIES", gte,
+ cmGeneratorExpression::BuildInterface,
+ properties, missingTargets);
+ this->PopulateInterfaceProperty("INTERFACE_SOURCES", gte,
+ cmGeneratorExpression::BuildInterface,
+ properties, missingTargets);
+ this->PopulateInterfaceProperty("INTERFACE_COMPILE_DEFINITIONS", gte,
+ cmGeneratorExpression::BuildInterface,
+ properties, missingTargets);
+ this->PopulateInterfaceProperty("INTERFACE_COMPILE_OPTIONS", gte,
+ cmGeneratorExpression::BuildInterface,
+ properties, missingTargets);
+ this->PopulateInterfaceProperty("INTERFACE_AUTOUIC_OPTIONS", gte,
+ cmGeneratorExpression::BuildInterface,
+ properties, missingTargets);
+ this->PopulateInterfaceProperty("INTERFACE_COMPILE_FEATURES", gte,
+ cmGeneratorExpression::BuildInterface,
+ properties, missingTargets);
+ this->PopulateInterfaceProperty("INTERFACE_POSITION_INDEPENDENT_CODE", gte,
+ properties);
+ const bool newCMP0022Behavior =
+ gte->GetPolicyStatusCMP0022() != cmPolicies::WARN &&
+ gte->GetPolicyStatusCMP0022() != cmPolicies::OLD;
+ if (newCMP0022Behavior) {
+ this->PopulateInterfaceLinkLibrariesProperty(
+ gte, cmGeneratorExpression::BuildInterface, properties,
+ missingTargets);
+ }
+ this->PopulateCompatibleInterfaceProperties(gte, properties);
+
+ this->GenerateInterfaceProperties(gte, os, properties);
+
+ os << " }";
+ if (i != this->Exports.end() - 1)
+ os << ",";
+ os << "\n";
+ }
+ os << " }\n";
+
+ // Generate import file content for each configuration.
+ for (std::string const& c : this->Configurations) {
+ this->GenerateImportConfig(os, c, missingTargets);
+ }
+
+ this->GenerateMissingTargetsCheckCode(os, missingTargets);
+
+ os << "}\n";
+
+ return true;
+}
+
+void cmExportBuildJSONGenerator::GenerateImportTargetCode(
+ std::ostream& os, const cmGeneratorTarget* target)
+{
+ std::string targetName = this->Namespace;
+ targetName += target->GetExportName();
+ os << " \"" << targetName << "\": {\n";
+ std::string path =
+ cmSystemTools::ConvertToOutputPath(target->GetFullPath().c_str());
+ os << " \"output\": \"" << path << "\",\n";
+}
+
+void cmExportBuildJSONGenerator::GenerateImportPropertyCode(
+ std::ostream& os, const std::string&, cmGeneratorTarget const*,
+ ImportPropertyMap const& properties)
+{
+ for (auto const& property : properties) {
+ if (property.first == "IMPORTED_SONAME") {
+ } else if (property.first == "IMPORTED_LINK_INTERFACE_LANGUAGES") {
+ } else if (property.first == "IMPORTED_LOCATION") {
+ }
+ os << " " << property.first << " " << (property.second) << "\n";
+ }
+}
+
+void cmExportBuildJSONGenerator::GenerateMissingTargetsCheckCode(
+ std::ostream&, const std::vector<std::string>&)
+{
+}
+
+void cmExportBuildJSONGenerator::GenerateInterfaceProperties(
+ const cmGeneratorTarget* target, std::ostream& os,
+ const ImportPropertyMap& properties)
+{
+ std::string config;
+ if (!this->Configurations.empty()) {
+ config = this->Configurations[0];
+ }
+ cmExportBuildJSONGenerator::GenerateInterfaceProperties(
+ target, os, properties, cmExportBuildJSONGenerator::BUILD, config);
+}
+
+void cmExportBuildJSONGenerator::GenerateInterfaceProperties(
+ const cmGeneratorTarget* target, std::ostream& os,
+ const ImportPropertyMap& properties, GenerateType type,
+ std::string const& config)
+{
+ const bool newCMP0022Behavior =
+ target->GetPolicyStatusCMP0022() != cmPolicies::WARN &&
+ target->GetPolicyStatusCMP0022() != cmPolicies::OLD;
+ if (!newCMP0022Behavior) {
+ std::ostringstream w;
+ if (type == cmExportBuildJSONGenerator::BUILD) {
+ w << "export(TARGETS ... JSON) called with policy CMP0022";
+ } else {
+ w << "install(JSON ...) called with policy CMP0022";
+ }
+ w << " set to OLD for target " << target->Target->GetName() << ". "
+ << "The export will only work with CMP0022 set to NEW.";
+ target->Makefile->IssueMessage(cmake::AUTHOR_WARNING, w.str());
+ }
+ if (!properties.empty()) {
+ for (auto const& property : properties) {
+ if (property.first == "INTERFACE_COMPILE_OPTIONS") {
+ os << "LOCAL_CPP_FEATURES += ";
+ os << (property.second) << "\n";
+ } else if (property.first == "INTERFACE_LINK_LIBRARIES") {
+ // need to look at list in pi->second and see if static or shared
+ // FindTargetToLink
+ // target->GetLocalGenerator()->FindGeneratorTargetToUse()
+ // then add to LOCAL_CPPFLAGS
+ std::vector<std::string> libraries;
+ cmSystemTools::ExpandListArgument(property.second, libraries);
+ std::vector<std::string> deps;
+ std::vector<std::string> staticLibs;
+ std::vector<std::string> sharedLibs;
+ std::vector<std::string> ldlibs;
+ for (std::string const& lib : libraries) {
+ cmGeneratorTarget* gt =
+ target->GetLocalGenerator()->FindGeneratorTargetToUse(lib);
+ if (gt) {
+ deps.push_back(lib);
+ if (gt->GetType() == cmStateEnums::SHARED_LIBRARY ||
+ gt->GetType() == cmStateEnums::MODULE_LIBRARY) {
+ sharedLibs.push_back(lib);
+ } else {
+ staticLibs.push_back(lib);
+ }
+ } else {
+ // evaluate any generator expressions with the current
+ // build type of the makefile
+ cmGeneratorExpression ge;
+ std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(lib);
+ std::string evaluated =
+ cge->Evaluate(target->GetLocalGenerator(), config);
+ bool relpath = false;
+ if (type == cmExportBuildJSONGenerator::INSTALL) {
+ relpath = lib.substr(0, 3) == "../";
+ }
+ // check for full path or if it already has a -l, or
+ // in the case of an install check for relative paths
+ // if it is full or a link library then use string directly
+ if (cmSystemTools::FileIsFullPath(evaluated) || evaluated.substr(0, 2) == "-l" || relpath) {
+ ldlibs.push_back(evaluated);
+ // if it is not a path and does not have a -l then add -l
+ } else if (!evaluated.empty()) {
+ ldlibs.push_back("-l" + evaluated);
+ }
+ }
+ }
+ auto join = [](std::string &ss, std::string &s) {
+ return ss.empty() ? s : ss + "\", \"" + s;
+ };
+ if (!deps.empty()) {
+ std::string str = std::accumulate(std::begin(deps), std::end(deps), std::string(), join);
+ os << " \"deps\": [\"" << str << "\"],\n";
+ }
+ //if (!sharedLibs.empty()) {
+ // std::string str = std::accumulate(std::begin(sharedLibs), std::end(sharedLibs), std::string(), join);
+ // os << " \"shared_libraries\": [\"" << str << "\"],\n";
+ //}
+ //if (!staticLibs.empty()) {
+ // std::string str = std::accumulate(std::begin(staticLibs), std::end(staticLibs), std::string(), join);
+ // os << " \"static_libraries\": [\"" << str << "\"],\n";
+ //}
+ if (!ldlibs.empty()) {
+ std::string str = std::accumulate(std::begin(ldlibs), std::end(ldlibs), std::string(), join);
+ os << " \"libs\": [\"" << str << "\"],\n";
+ }
+ } else if (property.first == "INTERFACE_INCLUDE_DIRECTORIES") {
+ std::string includes = property.second;
+ std::vector<std::string> includeList;
+ cmSystemTools::ExpandListArgument(includes, includeList);
+ auto join = [](std::string &ss, std::string &s) {
+ return ss.empty() ? s : ss + "\", \"" + s;
+ };
+ std::string str = std::accumulate(std::begin(includeList), std::end(includeList), std::string(), join);
+ os << " \"include_dirs\": [\"" << str << "\"],\n";
+ } else {
+ os << "# " << property.first << " " << (property.second) << "\n";
+ }
+ }
+ }
+
+ if (target->GetType() == cmStateEnums::STATIC_LIBRARY) {
+ cmLinkImplementation const* li = target->GetLinkImplementation(config);
+ if (std::find(li->Languages.begin(), li->Languages.end(), "CXX") !=
+ li->Languages.end()) {
+ }
+ }
+
+ switch (target->GetType()) {
+ case cmStateEnums::SHARED_LIBRARY:
+ os << " \"target_type\": \"shared_library\"\n";
+ break;
+ case cmStateEnums::MODULE_LIBRARY:
+ os << " \"target_type\": \"module_library\"\n";
+ break;
+ case cmStateEnums::STATIC_LIBRARY:
+ os << " \"target_type\": \"static_library\"\n";
+ break;
+ case cmStateEnums::EXECUTABLE:
+ os << " \"target_type\": \"executable\"\n";
+ break;
+ case cmStateEnums::UTILITY:
+ case cmStateEnums::OBJECT_LIBRARY:
+ case cmStateEnums::GLOBAL_TARGET:
+ case cmStateEnums::INTERFACE_LIBRARY:
+ case cmStateEnums::UNKNOWN_LIBRARY:
+ break;
+ }
+}
diff --git a/Source/cmExportBuildJSONGenerator.h b/Source/cmExportBuildJSONGenerator.h
new file mode 100644
index 0000000..e957a7c
--- /dev/null
+++ b/Source/cmExportBuildJSONGenerator.h
@@ -0,0 +1,75 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#ifndef cmExportBuildJSONGenerator_h
+#define cmExportBuildJSONGenerator_h
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include <iosfwd>
+#include <string>
+#include <vector>
+
+#include "cmExportBuildFileGenerator.h"
+#include "cmExportFileGenerator.h"
+
+class cmGeneratorTarget;
+
+/** \class cmExportBuildJSONGenerator
+ * \brief Generate a file exporting targets from a build tree.
+ *
+ * cmExportBuildJSONGenerator generates a file exporting targets from
+ * a build tree. This exports the targets to the JSON format that is
+ * intended to be consumed by other tools.
+ *
+ * This is used to implement the EXPORT() command.
+ */
+class cmExportBuildJSONGenerator : public cmExportBuildFileGenerator
+{
+public:
+ cmExportBuildJSONGenerator();
+ // this is so cmExportInstallJSONGenerator can share this
+ // function as they are almost the same
+ enum GenerateType
+ {
+ BUILD,
+ INSTALL
+ };
+ static void GenerateInterfaceProperties(cmGeneratorTarget const* target,
+ std::ostream& os,
+ const ImportPropertyMap& properties,
+ GenerateType type,
+ std::string const& config);
+
+ void Compute(cmLocalGenerator* lg);
+
+protected:
+ // Implement virtual methods from the superclass.
+ bool GenerateMainFile(std::ostream& os) override;
+ void GeneratePolicyHeaderCode(std::ostream&) override {}
+ void GeneratePolicyFooterCode(std::ostream&) override {}
+ void GenerateImportHeaderCode(std::ostream&, const std::string&) override {};
+ void GenerateImportFooterCode(std::ostream&) override {};
+ void GenerateImportTargetCode(std::ostream& os,
+ const cmGeneratorTarget* target) override;
+ void GenerateImportPropertyCode(
+ std::ostream& os, const std::string& config,
+ cmGeneratorTarget const* target,
+ ImportPropertyMap const& properties) override;
+ void GenerateImportedFileChecksCode(
+ std::ostream&, cmGeneratorTarget*,
+ ImportPropertyMap const&, const std::set<std::string>&) override {};
+ void GenerateImportedFileCheckLoop(std::ostream&) override {};
+ void GenerateMissingTargetsCheckCode(
+ std::ostream& os, const std::vector<std::string>& missingTargets) override;
+ void GenerateExpectedTargetsCode(
+ std::ostream&, const std::string&) override {};
+
+ void GenerateImportTargetsConfig(
+ std::ostream&, const std::string&, std::string const&,
+ std::vector<std::string>&) override {};
+ void GenerateInterfaceProperties(
+ cmGeneratorTarget const* target, std::ostream& os,
+ const ImportPropertyMap& properties) override;
+};
+
+#endif
diff --git a/Source/cmExportCommand.cxx b/Source/cmExportCommand.cxx
index 9913129..e92cfcb 100644
--- a/Source/cmExportCommand.cxx
+++ b/Source/cmExportCommand.cxx
@@ -8,6 +8,7 @@
#include "cmExportBuildAndroidMKGenerator.h"
#include "cmExportBuildFileGenerator.h"
+#include "cmExportBuildJSONGenerator.h"
#include "cmExportSetMap.h"
#include "cmGeneratedFileStream.h"
#include "cmGlobalGenerator.h"
@@ -34,6 +35,7 @@
, Filename(&Helper, "FILE", &ArgumentGroup)
, ExportOld(&Helper, "EXPORT_LINK_INTERFACE_LIBRARIES", &ArgumentGroup)
, AndroidMKFile(&Helper, "ANDROID_MK")
+ , JSONFile(&Helper, "JSON")
{
this->ExportSet = nullptr;
}
@@ -72,6 +74,11 @@
fname = this->AndroidMKFile.GetString();
android = true;
}
+ bool json = false;
+ if (this->JSONFile.WasFound()) {
+ fname = this->JSONFile.GetString();
+ json = true;
+ }
if (!this->Filename.WasFound() && fname.empty()) {
if (args[0] != "EXPORT") {
this->SetError("FILE <filename> option missing.");
@@ -187,6 +194,8 @@
cmExportBuildFileGenerator* ebfg = nullptr;
if (android) {
ebfg = new cmExportBuildAndroidMKGenerator;
+ } else if (json) {
+ ebfg = new cmExportBuildJSONGenerator;
} else {
ebfg = new cmExportBuildFileGenerator;
}
diff --git a/Source/cmExportCommand.h b/Source/cmExportCommand.h
index a5c6751..4c7d1d7 100644
--- a/Source/cmExportCommand.h
+++ b/Source/cmExportCommand.h
@@ -46,6 +46,7 @@
cmCAString Filename;
cmCAEnabler ExportOld;
cmCAString AndroidMKFile;
+ cmCAString JSONFile;
cmExportSet* ExportSet;
diff --git a/Source/cmExportInstallJSONGenerator.cxx b/Source/cmExportInstallJSONGenerator.cxx
new file mode 100644
index 0000000..8ad4f89
--- /dev/null
+++ b/Source/cmExportInstallJSONGenerator.cxx
@@ -0,0 +1,232 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#include "cmExportInstallJSONGenerator.h"
+
+#include <ostream>
+#include <stddef.h>
+
+#include "cmExportBuildJSONGenerator.h"
+#include "cmExportSet.h"
+#include "cmGeneratorTarget.h"
+#include "cmInstallExportGenerator.h"
+#include "cmInstallTargetGenerator.h"
+#include "cmStateTypes.h"
+#include "cmSystemTools.h"
+#include "cmTarget.h"
+#include "cmTargetExport.h"
+
+cmExportInstallJSONGenerator::cmExportInstallJSONGenerator(
+ cmInstallExportGenerator* iegen)
+ : cmExportInstallFileGenerator(iegen)
+{
+}
+
+bool cmExportInstallJSONGenerator::GenerateMainFile(std::ostream& os)
+{
+ std::vector<cmTargetExport*> allTargets;
+ {
+ std::string expectedTargets;
+ std::string sep;
+ for (cmTargetExport* te :
+ *this->IEGen->GetExportSet()->GetTargetExports()) {
+ expectedTargets += sep + this->Namespace + te->Target->GetExportName();
+ sep = " ";
+ if (this->ExportedTargets.insert(te->Target).second) {
+ allTargets.push_back(te);
+ } else {
+ std::ostringstream e;
+ e << "install(EXPORT \"" << this->IEGen->GetExportSet()->GetName()
+ << "\" ...) "
+ << "includes target \"" << te->Target->GetName()
+ << "\" more than once in the export set.";
+ cmSystemTools::Error(e.str().c_str());
+ return false;
+ }
+ }
+ }
+
+ std::vector<std::string> missingTargets;
+
+ os << "{\n"
+ << " \"targets\": {\n";
+
+ bool requiresConfigFiles = false;
+ // Create all the imported targets.
+ for (auto i = allTargets.begin(); i != allTargets.end(); ++i) {
+ cmTargetExport* te = *i;
+ cmGeneratorTarget* gt = te->Target;
+
+ requiresConfigFiles =
+ requiresConfigFiles || gt->GetType() != cmStateEnums::INTERFACE_LIBRARY;
+
+ this->GenerateImportTargetCode(os, gt);
+
+ ImportPropertyMap properties;
+
+ this->PopulateIncludeDirectoriesInterface(
+ te, cmGeneratorExpression::InstallInterface, properties, missingTargets);
+ this->PopulateSourcesInterface(te, cmGeneratorExpression::InstallInterface,
+ properties, missingTargets);
+ this->PopulateInterfaceProperty("INTERFACE_SYSTEM_INCLUDE_DIRECTORIES", gt,
+ cmGeneratorExpression::InstallInterface,
+ properties, missingTargets);
+ this->PopulateInterfaceProperty("INTERFACE_COMPILE_DEFINITIONS", gt,
+ cmGeneratorExpression::InstallInterface,
+ properties, missingTargets);
+ this->PopulateInterfaceProperty("INTERFACE_COMPILE_OPTIONS", gt,
+ cmGeneratorExpression::InstallInterface,
+ properties, missingTargets);
+ this->PopulateInterfaceProperty("INTERFACE_AUTOUIC_OPTIONS", gt,
+ cmGeneratorExpression::InstallInterface,
+ properties, missingTargets);
+ this->PopulateInterfaceProperty("INTERFACE_COMPILE_FEATURES", gt,
+ cmGeneratorExpression::InstallInterface,
+ properties, missingTargets);
+
+ const bool newCMP0022Behavior =
+ gt->GetPolicyStatusCMP0022() != cmPolicies::WARN &&
+ gt->GetPolicyStatusCMP0022() != cmPolicies::OLD;
+ if (newCMP0022Behavior) {
+ this->PopulateInterfaceLinkLibrariesProperty(
+ gt, cmGeneratorExpression::InstallInterface, properties,
+ missingTargets);
+ }
+
+ this->PopulateInterfaceProperty("INTERFACE_POSITION_INDEPENDENT_CODE", gt,
+ properties);
+
+ this->PopulateCompatibleInterfaceProperties(gt, properties);
+
+ this->GenerateInterfaceProperties(gt, os, properties);
+
+ os << " }";
+ if (i != allTargets.end() - 1)
+ os << ",";
+ os << "\n";
+ }
+ os << " }\n";
+
+ this->LoadConfigFiles(os);
+
+ this->CleanupTemporaryVariables(os);
+ this->GenerateImportedFileCheckLoop(os);
+
+ bool result = true;
+ // Generate an import file for each configuration.
+ // Don't do this if we only export INTERFACE_LIBRARY targets.
+ if (requiresConfigFiles) {
+ for (std::string const& c : this->Configurations) {
+ if (!this->GenerateImportFileConfig(c, missingTargets)) {
+ result = false;
+ }
+ }
+ }
+
+ this->GenerateMissingTargetsCheckCode(os, missingTargets);
+
+ os << "}\n";
+
+ return result;
+}
+
+void cmExportInstallJSONGenerator::GenerateImportHeaderCode(
+ std::ostream& os, const std::string&)
+{
+ std::string installDir = this->IEGen->GetDestination();
+ for (cmTargetExport* te : *this->IEGen->GetExportSet()->GetTargetExports()) {
+ // Collect import properties for this target.
+ if (te->Target->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
+ continue;
+ }
+ std::string dest;
+ if (te->LibraryGenerator) {
+ dest = te->LibraryGenerator->GetDestination("");
+ }
+ if (te->ArchiveGenerator) {
+ dest = te->ArchiveGenerator->GetDestination("");
+ }
+ te->Target->Target->SetProperty("__dest", dest.c_str());
+ }
+}
+
+void cmExportInstallJSONGenerator::GenerateImportFooterCode(std::ostream&)
+{
+}
+
+void cmExportInstallJSONGenerator::GenerateImportTargetCode(
+ std::ostream& os, const cmGeneratorTarget* target)
+{
+ std::string targetName = this->Namespace;
+ targetName += target->GetExportName();
+ os << " \"" << targetName << "\": {\n";
+ os << " \"output\": \"";
+ os << target->Target->GetProperty("__dest") << "/";
+ std::string config;
+ if (!this->Configurations.empty()) {
+ config = this->Configurations[0];
+ }
+ os << target->GetFullName(config) << "\",\n";
+}
+
+void cmExportInstallJSONGenerator::GenerateExpectedTargetsCode(
+ std::ostream&, const std::string&)
+{
+}
+
+void cmExportInstallJSONGenerator::GenerateImportPropertyCode(
+ std::ostream&, const std::string&, cmGeneratorTarget const*,
+ ImportPropertyMap const&)
+{
+}
+
+void cmExportInstallJSONGenerator::GenerateMissingTargetsCheckCode(
+ std::ostream&, const std::vector<std::string>&)
+{
+}
+
+void cmExportInstallJSONGenerator::GenerateInterfaceProperties(
+ cmGeneratorTarget const* target, std::ostream& os,
+ const ImportPropertyMap& properties)
+{
+ std::string config;
+ if (!this->Configurations.empty()) {
+ config = this->Configurations[0];
+ }
+ cmExportBuildJSONGenerator::GenerateInterfaceProperties(
+ target, os, properties, cmExportBuildJSONGenerator::INSTALL, config);
+}
+
+void cmExportInstallJSONGenerator::LoadConfigFiles(std::ostream&)
+{
+}
+
+void cmExportInstallJSONGenerator::GenerateImportPrefix(std::ostream&)
+{
+}
+
+void cmExportInstallJSONGenerator::GenerateRequiredCMakeVersion(
+ std::ostream&, const char*)
+{
+}
+
+void cmExportInstallJSONGenerator::CleanupTemporaryVariables(
+ std::ostream&)
+{
+}
+
+void cmExportInstallJSONGenerator::GenerateImportedFileCheckLoop(
+ std::ostream&)
+{
+}
+
+void cmExportInstallJSONGenerator::GenerateImportedFileChecksCode(
+ std::ostream&, cmGeneratorTarget*, ImportPropertyMap const&,
+ const std::set<std::string>&)
+{
+}
+
+bool cmExportInstallJSONGenerator::GenerateImportFileConfig(
+ const std::string&, std::vector<std::string>&)
+{
+ return true;
+}
diff --git a/Source/cmExportInstallJSONGenerator.h b/Source/cmExportInstallJSONGenerator.h
new file mode 100644
index 0000000..5f0568f
--- /dev/null
+++ b/Source/cmExportInstallJSONGenerator.h
@@ -0,0 +1,72 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#ifndef cmExportInstallJSONGenerator_h
+#define cmExportInstallJSONGenerator_h
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include <iosfwd>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "cmExportFileGenerator.h"
+#include "cmExportInstallFileGenerator.h"
+
+class cmGeneratorTarget;
+class cmInstallExportGenerator;
+
+/** \class cmExportInstallJSONGenerator
+ * \brief Generate a file exporting targets from an install tree.
+ *
+ * cmExportInstallJSONGenerator generates files exporting targets from
+ * install an installation tree. The files are placed in a temporary
+ * location for installation by cmInstallExportGenerator. The file format
+ * is for the ndk build system and is a makefile fragment specifing prebuilt
+ * libraries to the ndk build system.
+ *
+ * This is used to implement the INSTALL(EXPORT_JSON) command.
+ */
+class cmExportInstallJSONGenerator : public cmExportInstallFileGenerator
+{
+public:
+ /** Construct with the export installer that will install the
+ files. */
+ cmExportInstallJSONGenerator(cmInstallExportGenerator* iegen);
+
+protected:
+ // Implement virtual methods from the superclass.
+ bool GenerateMainFile(std::ostream& os) override;
+ void GeneratePolicyHeaderCode(std::ostream&) override {}
+ void GeneratePolicyFooterCode(std::ostream&) override {}
+ void GenerateImportHeaderCode(std::ostream& os,
+ const std::string& config = "") override;
+ void GenerateImportFooterCode(std::ostream& os) override;
+ void GenerateImportTargetCode(std::ostream& os,
+ const cmGeneratorTarget* target) override;
+ void GenerateExpectedTargetsCode(
+ std::ostream& os, const std::string& expectedTargets) override;
+ void GenerateImportPropertyCode(
+ std::ostream& os, const std::string& config,
+ cmGeneratorTarget const* target,
+ ImportPropertyMap const& properties) override;
+ void GenerateMissingTargetsCheckCode(
+ std::ostream& os, const std::vector<std::string>& missingTargets) override;
+ void GenerateInterfaceProperties(
+ cmGeneratorTarget const* target, std::ostream& os,
+ const ImportPropertyMap& properties) override;
+ void GenerateImportPrefix(std::ostream& os) override;
+ void LoadConfigFiles(std::ostream&) override;
+ void GenerateRequiredCMakeVersion(std::ostream& os,
+ const char* versionString) override;
+ void CleanupTemporaryVariables(std::ostream&) override;
+ void GenerateImportedFileCheckLoop(std::ostream& os) override;
+ void GenerateImportedFileChecksCode(
+ std::ostream& os, cmGeneratorTarget* target,
+ ImportPropertyMap const& properties,
+ const std::set<std::string>& importedLocations) override;
+ bool GenerateImportFileConfig(const std::string& config,
+ std::vector<std::string>&) override;
+};
+
+#endif
diff --git a/Source/cmInstallCommand.cxx b/Source/cmInstallCommand.cxx
index 685fc67..9f2aac5 100644
--- a/Source/cmInstallCommand.cxx
+++ b/Source/cmInstallCommand.cxx
@@ -102,6 +102,9 @@
if (mode == "EXPORT_ANDROID_MK") {
return this->HandleExportAndroidMKMode(args);
}
+ if (mode == "EXPORT_JSON") {
+ return this->HandleExportJSONMode(args);
+ }
// Unknown mode.
std::string e = "called with unknown mode ";
@@ -1247,7 +1250,7 @@
exportSet, ica.GetDestination().c_str(), ica.GetPermissions().c_str(),
ica.GetConfigurations(), ica.GetComponent().c_str(), message,
ica.GetExcludeFromAll(), fname.c_str(), name_space.GetCString(),
- exportOld.IsEnabled(), true);
+ exportOld.IsEnabled(), true, false);
this->Makefile->AddInstallGenerator(exportGenerator);
return true;
@@ -1258,6 +1261,112 @@
#endif
}
+bool cmInstallCommand::HandleExportJSONMode(
+ std::vector<std::string> const& args)
+{
+#ifdef CMAKE_BUILD_WITH_CMAKE
+ // This is the EXPORT mode.
+ cmInstallCommandArguments ica(this->DefaultComponentName);
+ cmCAString exp(&ica.Parser, "EXPORT_JSON");
+ cmCAString name_space(&ica.Parser, "NAMESPACE", &ica.ArgumentGroup);
+ cmCAEnabler exportOld(&ica.Parser, "EXPORT_LINK_INTERFACE_LIBRARIES",
+ &ica.ArgumentGroup);
+ cmCAString filename(&ica.Parser, "FILE", &ica.ArgumentGroup);
+ exp.Follows(nullptr);
+
+ ica.ArgumentGroup.Follows(&exp);
+ std::vector<std::string> unknownArgs;
+ ica.Parse(&args, &unknownArgs);
+
+ if (!unknownArgs.empty()) {
+ // Unknown argument.
+ std::ostringstream e;
+ e << args[0] << " given unknown argument \"" << unknownArgs[0] << "\".";
+ this->SetError(e.str());
+ return false;
+ }
+
+ if (!ica.Finalize()) {
+ return false;
+ }
+
+ // Make sure there is a destination.
+ if (ica.GetDestination().empty()) {
+ // A destination is required.
+ std::ostringstream e;
+ e << args[0] << " given no DESTINATION!";
+ this->SetError(e.str());
+ return false;
+ }
+
+ // Check the file name.
+ std::string fname = filename.GetString();
+ if (fname.find_first_of(":/\\") != std::string::npos) {
+ std::ostringstream e;
+ e << args[0] << " given invalid export file name \"" << fname << "\". "
+ << "The FILE argument may not contain a path. "
+ << "Specify the path in the DESTINATION argument.";
+ this->SetError(e.str());
+ return false;
+ }
+
+ // Check the file extension.
+ if (!fname.empty() &&
+ cmSystemTools::GetFilenameLastExtension(fname) != ".json") {
+ std::ostringstream e;
+ e << args[0] << " given invalid export file name \"" << fname << "\". "
+ << "The FILE argument must specify a name ending in \".json\".";
+ this->SetError(e.str());
+ return false;
+ }
+ if (fname.find_first_of(":/\\") != std::string::npos) {
+ std::ostringstream e;
+ e << args[0] << " given export name \"" << exp.GetString() << "\". "
+ << "This name cannot be safely converted to a file name. "
+ << "Specify a different export name or use the FILE option to set "
+ << "a file name explicitly.";
+ this->SetError(e.str());
+ return false;
+ }
+
+ // Construct the file name.
+ if (fname.empty()) {
+ fname = exp.GetString();
+ fname += ".json";
+
+ if (fname.find_first_of(":/\\") != std::string::npos) {
+ std::ostringstream e;
+ e << args[0] << " given export name \"" << exp.GetString() << "\". "
+ << "This name cannot be safely converted to a file name. "
+ << "Specify a different export name or use the FILE option to set "
+ << "a file name explicitly.";
+ this->SetError(e.str());
+ return false;
+ }
+ }
+
+ cmExportSet* exportSet =
+ this->Makefile->GetGlobalGenerator()->GetExportSets()[exp.GetString()];
+
+ cmInstallGenerator::MessageLevel message =
+ cmInstallGenerator::SelectMessageLevel(this->Makefile);
+
+ // Create the export install generator.
+ cmInstallExportGenerator* exportGenerator = new cmInstallExportGenerator(
+ exportSet, ica.GetDestination().c_str(), ica.GetPermissions().c_str(),
+ ica.GetConfigurations(), ica.GetComponent().c_str(), message,
+ ica.GetExcludeFromAll(), fname.c_str(), name_space.GetCString(),
+ exportOld.IsEnabled(), false, true);
+ this->Makefile->AddInstallGenerator(exportGenerator);
+
+ return true;
+#else
+ static_cast<void>(args);
+ this->SetError("EXPORT_JSON not supported in bootstrap cmake");
+ return false;
+#endif
+}
+
bool cmInstallCommand::HandleExportMode(std::vector<std::string> const& args)
{
// This is the EXPORT mode.
@@ -1361,7 +1470,7 @@
exportSet, ica.GetDestination().c_str(), ica.GetPermissions().c_str(),
ica.GetConfigurations(), ica.GetComponent().c_str(), message,
ica.GetExcludeFromAll(), fname.c_str(), name_space.GetCString(),
- exportOld.IsEnabled(), false);
+ exportOld.IsEnabled(), false, false);
this->Makefile->AddInstallGenerator(exportGenerator);
return true;
diff --git a/Source/cmInstallCommand.h b/Source/cmInstallCommand.h
index 8bd0159..b42a291 100644
--- a/Source/cmInstallCommand.h
+++ b/Source/cmInstallCommand.h
@@ -40,6 +40,7 @@
bool HandleDirectoryMode(std::vector<std::string> const& args);
bool HandleExportMode(std::vector<std::string> const& args);
bool HandleExportAndroidMKMode(std::vector<std::string> const& args);
+ bool HandleExportJSONMode(std::vector<std::string> const& args);
bool MakeFilesFullPath(const char* modeName,
const std::vector<std::string>& relFiles,
std::vector<std::string>& absFiles);
diff --git a/Source/cmInstallExportGenerator.cxx b/Source/cmInstallExportGenerator.cxx
index fdd231c..d8c778f 100644
--- a/Source/cmInstallExportGenerator.cxx
+++ b/Source/cmInstallExportGenerator.cxx
@@ -10,6 +10,9 @@
#include "cmExportInstallAndroidMKGenerator.h"
#endif
#include "cmExportInstallFileGenerator.h"
+#ifdef CMAKE_BUILD_WITH_CMAKE
+#include "cmExportInstallJSONGenerator.h"
+#endif
#include "cmExportSet.h"
#include "cmInstallType.h"
#include "cmLocalGenerator.h"
@@ -20,7 +23,8 @@
cmExportSet* exportSet, const char* destination,
const char* file_permissions, std::vector<std::string> const& configurations,
const char* component, MessageLevel message, bool exclude_from_all,
- const char* filename, const char* name_space, bool exportOld, bool android)
+ const char* filename, const char* name_space, bool exportOld, bool android,
+ bool json)
: cmInstallGenerator(destination, configurations, component, message,
exclude_from_all)
, ExportSet(exportSet)
@@ -34,6 +38,10 @@
#ifdef CMAKE_BUILD_WITH_CMAKE
this->EFGen = new cmExportInstallAndroidMKGenerator(this);
#endif
+ } else if (json) {
+#ifdef CMAKE_BUILD_WITH_CMAKE
+ this->EFGen = new cmExportInstallJSONGenerator(this);
+#endif
} else {
this->EFGen = new cmExportInstallFileGenerator(this);
}
diff --git a/Source/cmInstallExportGenerator.h b/Source/cmInstallExportGenerator.h
index d23cf06..2d22fec 100644
--- a/Source/cmInstallExportGenerator.h
+++ b/Source/cmInstallExportGenerator.h
@@ -29,7 +29,7 @@
const char* component, MessageLevel message,
bool exclude_from_all, const char* filename,
const char* name_space, bool exportOld,
- bool android);
+ bool android, bool json);
~cmInstallExportGenerator() override;
cmExportSet* GetExportSet() { return this->ExportSet; }
diff --git a/Tests/RunCMake/JSON/CMakeLists.txt b/Tests/RunCMake/JSON/CMakeLists.txt
new file mode 100644
index 0000000..576787a
--- /dev/null
+++ b/Tests/RunCMake/JSON/CMakeLists.txt
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.5)
+project(${RunCMake_TEST} NONE) # or languages needed
+include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/JSON/JSON-check.cmake b/Tests/RunCMake/JSON/JSON-check.cmake
new file mode 100644
index 0000000..ae723a4
--- /dev/null
+++ b/Tests/RunCMake/JSON/JSON-check.cmake
@@ -0,0 +1,30 @@
+# This file does a regex file compare on the generated
+# Android.mk files from the AndroidMK test
+
+macro(compare_file_to_expected file expected_file)
+ file(READ "${file}" JSON)
+ # clean up new lines
+ string(REGEX REPLACE "\r\n" "\n" JSON "${JSON}")
+ string(REGEX REPLACE "\n+$" "" JSON "${JSON}")
+ # read in the expected regex file
+ file(READ "${expected_file}" expected)
+ # clean up new lines
+ string(REGEX REPLACE "\r\n" "\n" expected "${expected}")
+ string(REGEX REPLACE "\n+$" "" expected "${expected}")
+ # compare the file to the expected regex and if there is not a match
+ # put an error message in RunCMake_TEST_FAILED
+ if(NOT "${JSON}" MATCHES "${expected}")
+ set(RunCMake_TEST_FAILED
+ "${file} does not match ${expected_file}:
+
+JSON contents = [\n${JSON}\n]
+Expected = [\n${expected}\n]")
+ endif()
+endmacro()
+
+compare_file_to_expected(
+ "${RunCMake_BINARY_DIR}/JSON-build/myexp.json"
+ "${RunCMake_TEST_SOURCE_DIR}/expectedBuildJSON.txt")
+compare_file_to_expected(
+ "${RunCMake_BINARY_DIR}/JSON-build/CMakeFiles/Export/share/myexp.json"
+ "${RunCMake_TEST_SOURCE_DIR}/expectedInstallJSON.txt")
diff --git a/Tests/RunCMake/JSON/JSON.cmake b/Tests/RunCMake/JSON/JSON.cmake
new file mode 100644
index 0000000..4e3b5c4
--- /dev/null
+++ b/Tests/RunCMake/JSON/JSON.cmake
@@ -0,0 +1,11 @@
+project(build)
+set(CMAKE_BUILD_TYPE Debug)
+add_library(foo foo.cxx)
+add_library(car foo.cxx)
+add_library(bar bar.c)
+add_library(dog foo.cxx)
+target_link_libraries(foo car bar dog debug -lm)
+export(TARGETS bar dog car foo JSON
+ ${build_BINARY_DIR}/myexp.json)
+install(TARGETS bar dog car foo DESTINATION lib EXPORT myexp)
+install(EXPORT_JSON myexp DESTINATION share)
diff --git a/Tests/RunCMake/JSON/RunCMakeTest.cmake b/Tests/RunCMake/JSON/RunCMakeTest.cmake
new file mode 100644
index 0000000..2156eee
--- /dev/null
+++ b/Tests/RunCMake/JSON/RunCMakeTest.cmake
@@ -0,0 +1,2 @@
+include(RunCMake)
+run_cmake(JSON)
diff --git a/Tests/RunCMake/JSON/bar.c b/Tests/RunCMake/JSON/bar.c
new file mode 100644
index 0000000..e1f4df6
--- /dev/null
+++ b/Tests/RunCMake/JSON/bar.c
@@ -0,0 +1,3 @@
+void bar(void)
+{
+}
diff --git a/Tests/RunCMake/JSON/expectedBuildJSON.txt b/Tests/RunCMake/JSON/expectedBuildJSON.txt
new file mode 100644
index 0000000..bbf67a5
--- /dev/null
+++ b/Tests/RunCMake/JSON/expectedBuildJSON.txt
@@ -0,0 +1,26 @@
+LOCAL_PATH.*call my-dir.*
+include.*CLEAR_VARS.*
+LOCAL_MODULE.*bar
+LOCAL_SRC_FILES.*bar.*
+include.*PREBUILT_STATIC_LIBRARY.*
+.*
+include.*CLEAR_VARS.*
+LOCAL_MODULE.*dog
+LOCAL_SRC_FILES.*.*dog.*
+LOCAL_HAS_CPP := true
+include.*PREBUILT_STATIC_LIBRARY.*
+.*
+include.*CLEAR_VARS.*
+LOCAL_MODULE.*car
+LOCAL_SRC_FILES.*.*car.*
+LOCAL_HAS_CPP := true
+include.*PREBUILT_STATIC_LIBRARY.*
+.*
+include.*CLEAR_VARS.*
+LOCAL_MODULE.*foo
+LOCAL_SRC_FILES.*.*foo.*
+LOCAL_CPP_FEATURES.*rtti exceptions
+LOCAL_STATIC_LIBRARIES.*car bar dog
+LOCAL_EXPORT_LDLIBS := -lm
+LOCAL_HAS_CPP := true
+include.*PREBUILT_STATIC_LIBRARY.*
diff --git a/Tests/RunCMake/JSON/expectedInstallJSON.txt b/Tests/RunCMake/JSON/expectedInstallJSON.txt
new file mode 100644
index 0000000..3515fb9
--- /dev/null
+++ b/Tests/RunCMake/JSON/expectedInstallJSON.txt
@@ -0,0 +1,28 @@
+LOCAL_PATH.*call my-dir.*
+_IMPORT_PREFIX.*LOCAL_PATH./../..
+
+include.*CLEAR_VARS.*
+LOCAL_MODULE.*bar
+LOCAL_SRC_FILES.*_IMPORT_PREFIX./lib.*bar.*
+include.*PREBUILT_STATIC_LIBRARY.*
+
+include.*CLEAR_VARS.
+LOCAL_MODULE.*dog
+LOCAL_SRC_FILES.*_IMPORT_PREFIX./lib.*dog.*
+LOCAL_HAS_CPP := true
+include.*PREBUILT_STATIC_LIBRARY.*
+
+include.*CLEAR_VARS.*
+LOCAL_MODULE.*car
+LOCAL_SRC_FILES.*_IMPORT_PREFIX./lib.*car.*
+LOCAL_HAS_CPP := true
+include.*PREBUILT_STATIC_LIBRARY.*
+
+include.*CLEAR_VARS.*
+LOCAL_MODULE.*foo
+LOCAL_SRC_FILES.*_IMPORT_PREFIX\)/lib.*foo.*
+LOCAL_CPP_FEATURES.*rtti exceptions
+LOCAL_STATIC_LIBRARIES.*car bar dog
+LOCAL_EXPORT_LDLIBS := -lm
+LOCAL_HAS_CPP := true
+include.*PREBUILT_STATIC_LIBRARY.*
diff --git a/Tests/RunCMake/JSON/foo.cxx b/Tests/RunCMake/JSON/foo.cxx
new file mode 100644
index 0000000..3695dc9
--- /dev/null
+++ b/Tests/RunCMake/JSON/foo.cxx
@@ -0,0 +1,3 @@
+void foo()
+{
+}