diff options
24 files changed, 941 insertions, 9 deletions
diff --git a/Help/command/export.rst b/Help/command/export.rst index 4419dc1..53675a7 100644 --- a/Help/command/export.rst +++ b/Help/command/export.rst @@ -55,3 +55,18 @@ build tree. In some cases, for example for packaging and for system wide installations, it is not desirable to write the user package registry. If the :variable:`CMAKE_EXPORT_NO_PACKAGE_REGISTRY` variable is enabled, the ``export(PACKAGE)`` command will do nothing. + +:: + + export(TARGETS [target1 [target2 [...]]] [ANDROID_MK <filename>]) + +This signature exports cmake built targets to the android ndk build system +by creating an Android.mk file that references the prebuilt targets. The +Android NDK supports the use of prebuilt libraries, both static and shared. +This allows cmake to build the libraries of a project and make them available +to an ndk build system complete with transitive dependencies, include flags +and defines required to use the libraries. The signature takes a list of +targets and puts them in the Android.mk file specified by the ``<filename>`` +given. This signature can only be used if policy CMP0022 is NEW for all +targets given. A error will be issued if that policy is set to OLD for one +of the targets. diff --git a/Help/command/install.rst b/Help/command/install.rst index aaf12cc..d57dd75 100644 --- a/Help/command/install.rst +++ b/Help/command/install.rst @@ -314,7 +314,8 @@ Installing Exports :: install(EXPORT <export-name> DESTINATION <dir> - [NAMESPACE <namespace>] [FILE <name>.cmake] + [NAMESPACE <namespace>] [[FILE <name>.cmake]| + [EXPORT_ANDROID_MK <name>.mk]] [PERMISSIONS permissions...] [CONFIGURATIONS [Debug|Release|...]] [EXPORT_LINK_INTERFACE_LIBRARIES] @@ -342,6 +343,13 @@ specified that does not match that given to the targets associated with included in the export but a target to which it links is not included the behavior is unspecified. +In additon to cmake language files, the ``EXPORT_ANDROID_MK`` option maybe +used to specifiy an export to the android ndk build system. The Android +NDK supports the use of prebuilt libraries, both static and shared. This +allows cmake to build the libraries of a project and make them available +to an ndk build system complete with transitive dependencies, include flags +and defines required to use the libraries. + The ``EXPORT`` form is useful to help outside projects use targets built and installed by the current project. For example, the code @@ -349,9 +357,11 @@ and installed by the current project. For example, the code install(TARGETS myexe EXPORT myproj DESTINATION bin) install(EXPORT myproj NAMESPACE mp_ DESTINATION lib/myproj) + install(EXPORT_ANDROID_MK myexp DESTINATION share/ndk-modules) will install the executable myexe to ``<prefix>/bin`` and code to import -it in the file ``<prefix>/lib/myproj/myproj.cmake``. An outside project +it in the file ``<prefix>/lib/myproj/myproj.cmake`` and +``<prefix>/lib/share/ndk-modules/Android.mk``. An outside project may load this file with the include command and reference the ``myexe`` executable from the installation tree using the imported target name ``mp_myexe`` as if the target were built in its own tree. diff --git a/Help/release/dev/add_androidmk_generator.rst b/Help/release/dev/add_androidmk_generator.rst new file mode 100644 index 0000000..dd7867c --- /dev/null +++ b/Help/release/dev/add_androidmk_generator.rst @@ -0,0 +1,10 @@ +add_androidmk_generator +----------------------- + +* The :command:`install` command gained an ``EXPORT_ANDROID_MK`` + subcommand to install ``Android.mk`` files referencing installed + libraries as prebuilts for the Android NDK build system. + +* The :command:`export` command gained an ``ANDROID_MK`` option + to generate ``Android.mk`` files referencing CMake-built + libraries as prebuilts for the Android NDK build system. diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index f5c2e52..39773e1 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -221,10 +221,14 @@ set(SRCS cmExprLexer.cxx cmExprParser.cxx cmExprParserHelper.cxx + cmExportBuildAndroidMKGenerator.h + cmExportBuildAndroidMKGenerator.cxx cmExportBuildFileGenerator.h cmExportBuildFileGenerator.cxx cmExportFileGenerator.h cmExportFileGenerator.cxx + cmExportInstallAndroidMKGenerator.h + cmExportInstallAndroidMKGenerator.cxx cmExportInstallFileGenerator.h cmExportInstallFileGenerator.cxx cmExportTryCompileFileGenerator.h diff --git a/Source/cmExportBuildAndroidMKGenerator.cxx b/Source/cmExportBuildAndroidMKGenerator.cxx new file mode 100644 index 0000000..3247cc8 --- /dev/null +++ b/Source/cmExportBuildAndroidMKGenerator.cxx @@ -0,0 +1,193 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ +#include "cmExportBuildAndroidMKGenerator.h" + +#include "cmExportSet.h" +#include "cmGeneratorTarget.h" +#include "cmGlobalGenerator.h" +#include "cmLocalGenerator.h" +#include "cmMakefile.h" +#include "cmTargetExport.h" + +cmExportBuildAndroidMKGenerator::cmExportBuildAndroidMKGenerator() +{ + this->LG = CM_NULLPTR; + this->ExportSet = CM_NULLPTR; +} + +void cmExportBuildAndroidMKGenerator::GenerateImportHeaderCode( + std::ostream& os, const std::string&) +{ + os << "LOCAL_PATH := $(call my-dir)\n\n"; +} + +void cmExportBuildAndroidMKGenerator::GenerateImportFooterCode(std::ostream&) +{ +} + +void cmExportBuildAndroidMKGenerator::GenerateExpectedTargetsCode( + std::ostream&, const std::string&) +{ +} + +void cmExportBuildAndroidMKGenerator::GenerateImportTargetCode( + std::ostream& os, const cmGeneratorTarget* target) +{ + std::string targetName = this->Namespace; + targetName += target->GetExportName(); + os << "include $(CLEAR_VARS)\n"; + os << "LOCAL_MODULE := "; + os << targetName << "\n"; + os << "LOCAL_SRC_FILES := "; + std::string path = target->GetLocalGenerator()->ConvertToOutputFormat( + target->GetFullPath(), cmOutputConverter::MAKERULE); + os << path << "\n"; +} + +void cmExportBuildAndroidMKGenerator::GenerateImportPropertyCode( + std::ostream&, const std::string&, cmGeneratorTarget const*, + ImportPropertyMap const&) +{ +} + +void cmExportBuildAndroidMKGenerator::GenerateMissingTargetsCheckCode( + std::ostream&, const std::vector<std::string>&) +{ +} + +void cmExportBuildAndroidMKGenerator::GenerateInterfaceProperties( + const cmGeneratorTarget* target, std::ostream& os, + const ImportPropertyMap& properties) +{ + std::string config = ""; + if (this->Configurations.size()) { + config = this->Configurations[0]; + } + cmExportBuildAndroidMKGenerator::GenerateInterfaceProperties( + target, os, properties, cmExportBuildAndroidMKGenerator::BUILD, config); +} + +void cmExportBuildAndroidMKGenerator::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 == cmExportBuildAndroidMKGenerator::BUILD) { + w << "export(TARGETS ... ANDROID_MK) called with policy CMP0022"; + } else { + w << "install( EXPORT_ANDROID_MK ...) 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()) { + os << "LOCAL_CPP_FEATURES := rtti exceptions\n"; + for (ImportPropertyMap::const_iterator pi = properties.begin(); + pi != properties.end(); ++pi) { + if (pi->first == "INTERFACE_COMPILE_OPTIONS") { + os << "LOCAL_CPP_FEATURES += "; + os << (pi->second) << "\n"; + } else if (pi->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(pi->second, libraries); + std::string staticLibs; + std::string sharedLibs; + std::string ldlibs; + for (std::vector<std::string>::iterator i = libraries.begin(); + i != libraries.end(); ++i) { + cmGeneratorTarget* gt = + target->GetLocalGenerator()->FindGeneratorTargetToUse(*i); + if (gt) { + + if (gt->GetType() == cmState::SHARED_LIBRARY || + gt->GetType() == cmState::MODULE_LIBRARY) { + sharedLibs += " " + *i; + } else { + staticLibs += " " + *i; + } + } else { + // evaluate any generator expressions with the current + // build type of the makefile + cmGeneratorExpression ge; + CM_AUTO_PTR<cmCompiledGeneratorExpression> cge = ge.Parse(*i); + std::string evaluated = + cge->Evaluate(target->GetLocalGenerator(), config); + bool relpath = false; + if (type == cmExportBuildAndroidMKGenerator::INSTALL) { + relpath = i->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 += " " + evaluated; + // if it is not a path and does not have a -l then add -l + } else if (!evaluated.empty()) { + ldlibs += " -l" + evaluated; + } + } + } + if (!sharedLibs.empty()) { + os << "LOCAL_SHARED_LIBRARIES :=" << sharedLibs << "\n"; + } + if (!staticLibs.empty()) { + os << "LOCAL_STATIC_LIBRARIES :=" << staticLibs << "\n"; + } + if (!ldlibs.empty()) { + os << "LOCAL_EXPORT_LDLIBS :=" << ldlibs << "\n"; + } + } else if (pi->first == "INTERFACE_INCLUDE_DIRECTORIES") { + std::string includes = pi->second; + std::vector<std::string> includeList; + cmSystemTools::ExpandListArgument(includes, includeList); + os << "LOCAL_EXPORT_C_INCLUDES := "; + std::string end; + for (std::vector<std::string>::iterator i = includeList.begin(); + i != includeList.end(); ++i) { + os << end << *i; + end = "\\\n"; + } + os << "\n"; + } else { + os << "# " << pi->first << " " << (pi->second) << "\n"; + } + } + } + switch (target->GetType()) { + case cmState::SHARED_LIBRARY: + case cmState::MODULE_LIBRARY: + os << "include $(PREBUILT_SHARED_LIBRARY)\n"; + break; + case cmState::STATIC_LIBRARY: + os << "include $(PREBUILT_STATIC_LIBRARY)\n"; + break; + case cmState::EXECUTABLE: + case cmState::UTILITY: + case cmState::OBJECT_LIBRARY: + case cmState::GLOBAL_TARGET: + case cmState::INTERFACE_LIBRARY: + case cmState::UNKNOWN_LIBRARY: + break; + } + os << "\n"; +} diff --git a/Source/cmExportBuildAndroidMKGenerator.h b/Source/cmExportBuildAndroidMKGenerator.h new file mode 100644 index 0000000..e26aba0 --- /dev/null +++ b/Source/cmExportBuildAndroidMKGenerator.h @@ -0,0 +1,68 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ +#ifndef cmExportBuildAndroidMKGenerator_h +#define cmExportBuildAndroidMKGenerator_h + +#include "cmExportBuildFileGenerator.h" +#include "cmListFileCache.h" + +class cmExportSet; + +/** \class cmExportBuildAndroidMKGenerator + * \brief Generate a file exporting targets from a build tree. + * + * cmExportBuildAndroidMKGenerator generates a file exporting targets from + * a build tree. This exports the targets to the Android ndk build tool + * makefile format for prebuilt libraries. + * + * This is used to implement the EXPORT() command. + */ +class cmExportBuildAndroidMKGenerator : public cmExportBuildFileGenerator +{ +public: + cmExportBuildAndroidMKGenerator(); + // this is so cmExportInstallAndroidMKGenerator 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); + +protected: + // Implement virtual methods from the superclass. + virtual void GeneratePolicyHeaderCode(std::ostream&) {} + virtual void GeneratePolicyFooterCode(std::ostream&) {} + virtual void GenerateImportHeaderCode(std::ostream& os, + const std::string& config = ""); + virtual void GenerateImportFooterCode(std::ostream& os); + virtual void GenerateImportTargetCode(std::ostream& os, + const cmGeneratorTarget* target); + virtual void GenerateExpectedTargetsCode(std::ostream& os, + const std::string& expectedTargets); + virtual void GenerateImportPropertyCode(std::ostream& os, + const std::string& config, + cmGeneratorTarget const* target, + ImportPropertyMap const& properties); + virtual void GenerateMissingTargetsCheckCode( + std::ostream& os, const std::vector<std::string>& missingTargets); + virtual void GenerateInterfaceProperties( + cmGeneratorTarget const* target, std::ostream& os, + const ImportPropertyMap& properties); +}; + +#endif diff --git a/Source/cmExportCommand.cxx b/Source/cmExportCommand.cxx index fc62492..134a63f 100644 --- a/Source/cmExportCommand.cxx +++ b/Source/cmExportCommand.cxx @@ -18,6 +18,7 @@ #include <cmsys/Encoding.hxx> #include <cmsys/RegularExpression.hxx> +#include "cmExportBuildAndroidMKGenerator.h" #include "cmExportBuildFileGenerator.h" #if defined(__HAIKU__) @@ -34,6 +35,7 @@ cmExportCommand::cmExportCommand() , Namespace(&Helper, "NAMESPACE", &ArgumentGroup) , Filename(&Helper, "FILE", &ArgumentGroup) , ExportOld(&Helper, "EXPORT_LINK_INTERFACE_LIBRARIES", &ArgumentGroup) + , AndroidMKFile(&Helper, "ANDROID_MK") { this->ExportSet = CM_NULLPTR; } @@ -66,13 +68,18 @@ bool cmExportCommand::InitialPass(std::vector<std::string> const& args, } std::string fname; - if (!this->Filename.WasFound()) { + bool android = false; + if (this->AndroidMKFile.WasFound()) { + fname = this->AndroidMKFile.GetString(); + android = true; + } + if (!this->Filename.WasFound() && fname.empty()) { if (args[0] != "EXPORT") { this->SetError("FILE <filename> option missing."); return false; } fname = this->ExportSetName.GetString() + ".cmake"; - } else { + } else if (fname.empty()) { // Make sure the file has a .cmake extension. if (cmSystemTools::GetFilenameLastExtension(this->Filename.GetCString()) != ".cmake") { @@ -176,7 +183,12 @@ bool cmExportCommand::InitialPass(std::vector<std::string> const& args, } // Setup export file generation. - cmExportBuildFileGenerator* ebfg = new cmExportBuildFileGenerator; + cmExportBuildFileGenerator* ebfg = CM_NULLPTR; + if (android) { + ebfg = new cmExportBuildAndroidMKGenerator; + } else { + ebfg = new cmExportBuildFileGenerator; + } ebfg->SetExportFile(fname.c_str()); ebfg->SetNamespace(this->Namespace.GetCString()); ebfg->SetAppendMode(this->Append.IsEnabled()); diff --git a/Source/cmExportCommand.h b/Source/cmExportCommand.h index 0a149af..481d2c5 100644 --- a/Source/cmExportCommand.h +++ b/Source/cmExportCommand.h @@ -54,6 +54,7 @@ private: cmCAString Namespace; cmCAString Filename; cmCAEnabler ExportOld; + cmCAString AndroidMKFile; cmExportSet* ExportSet; diff --git a/Source/cmExportInstallAndroidMKGenerator.cxx b/Source/cmExportInstallAndroidMKGenerator.cxx new file mode 100644 index 0000000..8f815b7 --- /dev/null +++ b/Source/cmExportInstallAndroidMKGenerator.cxx @@ -0,0 +1,146 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ +#include "cmExportInstallAndroidMKGenerator.h" + +#include "cmAlgorithms.h" +#include "cmExportBuildAndroidMKGenerator.h" +#include "cmExportSet.h" +#include "cmExportSetMap.h" +#include "cmGeneratedFileStream.h" +#include "cmGeneratorTarget.h" +#include "cmGlobalGenerator.h" +#include "cmInstallExportGenerator.h" +#include "cmInstallTargetGenerator.h" +#include "cmLocalGenerator.h" +#include "cmTargetExport.h" + +cmExportInstallAndroidMKGenerator::cmExportInstallAndroidMKGenerator( + cmInstallExportGenerator* iegen) + : cmExportInstallFileGenerator(iegen) +{ +} + +void cmExportInstallAndroidMKGenerator::GenerateImportHeaderCode( + std::ostream& os, const std::string&) +{ + std::string installDir = this->IEGen->GetDestination(); + os << "LOCAL_PATH := $(call my-dir)\n"; + size_t numDotDot = cmSystemTools::CountChar(installDir.c_str(), '/'); + numDotDot += (installDir.size() > 0) ? 1 : 0; + std::string path; + for (size_t n = 0; n < numDotDot; n++) { + path += "/.."; + } + os << "_IMPORT_PREFIX := " + << "$(LOCAL_PATH)" << path << "\n\n"; + for (std::vector<cmTargetExport*>::const_iterator tei = + this->IEGen->GetExportSet()->GetTargetExports()->begin(); + tei != this->IEGen->GetExportSet()->GetTargetExports()->end(); ++tei) { + // Collect import properties for this target. + cmTargetExport const* te = *tei; + if (te->Target->GetType() == cmState::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 cmExportInstallAndroidMKGenerator::GenerateImportFooterCode(std::ostream&) +{ +} + +void cmExportInstallAndroidMKGenerator::GenerateImportTargetCode( + std::ostream& os, const cmGeneratorTarget* target) +{ + std::string targetName = this->Namespace; + targetName += target->GetExportName(); + os << "include $(CLEAR_VARS)\n"; + os << "LOCAL_MODULE := "; + os << targetName << "\n"; + os << "LOCAL_SRC_FILES := $(_IMPORT_PREFIX)/"; + os << target->Target->GetProperty("__dest") << "/"; + std::string config = ""; + if (this->Configurations.size()) { + config = this->Configurations[0]; + } + os << target->GetFullName(config) << "\n"; +} + +void cmExportInstallAndroidMKGenerator::GenerateExpectedTargetsCode( + std::ostream&, const std::string&) +{ +} + +void cmExportInstallAndroidMKGenerator::GenerateImportPropertyCode( + std::ostream&, const std::string&, cmGeneratorTarget const*, + ImportPropertyMap const&) +{ +} + +void cmExportInstallAndroidMKGenerator::GenerateMissingTargetsCheckCode( + std::ostream&, const std::vector<std::string>&) +{ +} + +void cmExportInstallAndroidMKGenerator::GenerateInterfaceProperties( + cmGeneratorTarget const* target, std::ostream& os, + const ImportPropertyMap& properties) +{ + std::string config = ""; + if (this->Configurations.size()) { + config = this->Configurations[0]; + } + cmExportBuildAndroidMKGenerator::GenerateInterfaceProperties( + target, os, properties, cmExportBuildAndroidMKGenerator::INSTALL, config); +} + +void cmExportInstallAndroidMKGenerator::LoadConfigFiles(std::ostream&) +{ +} + +void cmExportInstallAndroidMKGenerator::GenerateImportPrefix(std::ostream&) +{ +} + +void cmExportInstallAndroidMKGenerator::GenerateRequiredCMakeVersion( + std::ostream&, const char*) +{ +} + +void cmExportInstallAndroidMKGenerator::CleanupTemporaryVariables( + std::ostream&) +{ +} + +void cmExportInstallAndroidMKGenerator::GenerateImportedFileCheckLoop( + std::ostream&) +{ +} + +void cmExportInstallAndroidMKGenerator::GenerateImportedFileChecksCode( + std::ostream&, cmGeneratorTarget*, ImportPropertyMap const&, + const std::set<std::string>&) +{ +} + +bool cmExportInstallAndroidMKGenerator::GenerateImportFileConfig( + const std::string&, std::vector<std::string>&) +{ + return true; +} diff --git a/Source/cmExportInstallAndroidMKGenerator.h b/Source/cmExportInstallAndroidMKGenerator.h new file mode 100644 index 0000000..4b9f51c --- /dev/null +++ b/Source/cmExportInstallAndroidMKGenerator.h @@ -0,0 +1,72 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ +#ifndef cmExportInstallAndroidMKGenerator_h +#define cmExportInstallAndroidMKGenerator_h + +#include "cmExportInstallFileGenerator.h" + +class cmInstallExportGenerator; +class cmInstallTargetGenerator; + +/** \class cmExportInstallAndroidMKGenerator + * \brief Generate a file exporting targets from an install tree. + * + * cmExportInstallAndroidMKGenerator 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_ANDROID_MK) command. + */ +class cmExportInstallAndroidMKGenerator : public cmExportInstallFileGenerator +{ +public: + /** Construct with the export installer that will install the + files. */ + cmExportInstallAndroidMKGenerator(cmInstallExportGenerator* iegen); + +protected: + // Implement virtual methods from the superclass. + virtual void GeneratePolicyHeaderCode(std::ostream&) {} + virtual void GeneratePolicyFooterCode(std::ostream&) {} + virtual void GenerateImportHeaderCode(std::ostream& os, + const std::string& config = ""); + virtual void GenerateImportFooterCode(std::ostream& os); + virtual void GenerateImportTargetCode(std::ostream& os, + const cmGeneratorTarget* target); + virtual void GenerateExpectedTargetsCode(std::ostream& os, + const std::string& expectedTargets); + virtual void GenerateImportPropertyCode(std::ostream& os, + const std::string& config, + cmGeneratorTarget const* target, + ImportPropertyMap const& properties); + virtual void GenerateMissingTargetsCheckCode( + std::ostream& os, const std::vector<std::string>& missingTargets); + virtual void GenerateInterfaceProperties( + cmGeneratorTarget const* target, std::ostream& os, + const ImportPropertyMap& properties); + virtual void GenerateImportPrefix(std::ostream& os); + virtual void LoadConfigFiles(std::ostream&); + virtual void GenerateRequiredCMakeVersion(std::ostream& os, + const char* versionString); + virtual void CleanupTemporaryVariables(std::ostream&); + virtual void GenerateImportedFileCheckLoop(std::ostream& os); + virtual void GenerateImportedFileChecksCode( + std::ostream& os, cmGeneratorTarget* target, + ImportPropertyMap const& properties, + const std::set<std::string>& importedLocations); + virtual bool GenerateImportFileConfig(const std::string& config, + std::vector<std::string>&); +}; + +#endif diff --git a/Source/cmInstallCommand.cxx b/Source/cmInstallCommand.cxx index 4912eac..e464bce 100644 --- a/Source/cmInstallCommand.cxx +++ b/Source/cmInstallCommand.cxx @@ -83,6 +83,8 @@ bool cmInstallCommand::InitialPass(std::vector<std::string> const& args, return this->HandleDirectoryMode(args); } else if (args[0] == "EXPORT") { return this->HandleExportMode(args); + } else if (args[0] == "EXPORT_ANDROID_MK") { + return this->HandleExportAndroidMKMode(args); } // Unknown mode. @@ -1097,6 +1099,100 @@ bool cmInstallCommand::HandleDirectoryMode( return true; } +bool cmInstallCommand::HandleExportAndroidMKMode( + 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_ANDROID_MK"); + 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(0); + + 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(":/\\") != fname.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) != ".mk") { + std::ostringstream e; + e << args[0] << " given invalid export file name \"" << fname << "\". " + << "The FILE argument must specify a name ending in \".mk\"."; + this->SetError(e.str()); + return false; + } + if (fname.find_first_of(":/\\") != fname.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; + } + // Use the default name + if (fname.empty()) { + fname = "Android.mk"; + } + + 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(), true); + this->Makefile->AddInstallGenerator(exportGenerator); + + return true; +#else + static_cast<void>(args); + this->SetError("EXPORT_ANDROID_MK not supported in bootstrap cmake"); + return false; +#endif +} + bool cmInstallCommand::HandleExportMode(std::vector<std::string> const& args) { // This is the EXPORT mode. @@ -1203,7 +1299,7 @@ bool cmInstallCommand::HandleExportMode(std::vector<std::string> const& args) 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()); + exportOld.IsEnabled(), false); this->Makefile->AddInstallGenerator(exportGenerator); return true; diff --git a/Source/cmInstallCommand.h b/Source/cmInstallCommand.h index 3718ad5..7bc974c 100644 --- a/Source/cmInstallCommand.h +++ b/Source/cmInstallCommand.h @@ -48,6 +48,7 @@ private: bool HandleFilesMode(std::vector<std::string> const& args); 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 MakeFilesFullPath(const char* modeName, const std::vector<std::string>& relFiles, std::vector<std::string>& absFiles); diff --git a/Source/cmInstallExportAndroidMKGenerator.cxx b/Source/cmInstallExportAndroidMKGenerator.cxx new file mode 100644 index 0000000..43bdc01 --- /dev/null +++ b/Source/cmInstallExportAndroidMKGenerator.cxx @@ -0,0 +1,149 @@ + +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ +#include "cmInstallExportAndroidMKGenerator.h" + +#include <stdio.h> + +#include "cmExportInstallFileGenerator.h" +#include "cmExportSet.h" +#include "cmGeneratedFileStream.h" +#include "cmGlobalGenerator.h" +#include "cmInstallFilesGenerator.h" +#include "cmInstallTargetGenerator.h" +#include "cmLocalGenerator.h" +#include "cmMakefile.h" +#include "cmake.h" + +cmInstallExportAndroidMKGenerator::cmInstallExportAndroidMKGenerator( + 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) + : cmInstallExportGenerator(exportSet, destination, file_permissions, + configurations, component, message, + exclude_from_all, filename, name_space, exportOld) +{ +} + +cmInstallExportAndroidMKGenerator::~cmInstallExportAndroidMKGenerator() +{ +} + +void cmInstallExportAndroidMKGenerator::Compute(cmLocalGenerator* lg) +{ + this->LocalGenerator = lg; + this->ExportSet->Compute(lg); +} + +void cmInstallExportAndroidMKGenerator::GenerateScript(std::ostream& os) +{ + // Skip empty sets. + if (ExportSet->GetTargetExports()->empty()) { + std::ostringstream e; + e << "INSTALL(EXPORT) given unknown export \"" << ExportSet->GetName() + << "\""; + cmSystemTools::Error(e.str().c_str()); + return; + } + + // Create the temporary directory in which to store the files. + this->ComputeTempDir(); + cmSystemTools::MakeDirectory(this->TempDir.c_str()); + + // Construct a temporary location for the file. + this->MainImportFile = this->TempDir; + this->MainImportFile += "/"; + this->MainImportFile += this->FileName; + + // Generate the import file for this export set. + this->EFGen->SetExportFile(this->MainImportFile.c_str()); + this->EFGen->SetNamespace(this->Namespace); + this->EFGen->SetExportOld(this->ExportOld); + if (this->ConfigurationTypes->empty()) { + if (!this->ConfigurationName.empty()) { + this->EFGen->AddConfiguration(this->ConfigurationName); + } else { + this->EFGen->AddConfiguration(""); + } + } else { + for (std::vector<std::string>::const_iterator ci = + this->ConfigurationTypes->begin(); + ci != this->ConfigurationTypes->end(); ++ci) { + this->EFGen->AddConfiguration(*ci); + } + } + this->EFGen->GenerateImportFile(); + + // Perform the main install script generation. + this->cmInstallGenerator::GenerateScript(os); +} + +void cmInstallExportAndroidMKGenerator::GenerateScriptConfigs( + std::ostream& os, Indent const& indent) +{ + // Create the main install rules first. + this->cmInstallGenerator::GenerateScriptConfigs(os, indent); + + // Now create a configuration-specific install rule for the import + // file of each configuration. + std::vector<std::string> files; + for (std::map<std::string, std::string>::const_iterator i = + this->EFGen->GetConfigImportFiles().begin(); + i != this->EFGen->GetConfigImportFiles().end(); ++i) { + files.push_back(i->second); + std::string config_test = this->CreateConfigTest(i->first); + os << indent << "if(" << config_test << ")\n"; + this->AddInstallRule(os, this->Destination, cmInstallType_FILES, files, + false, this->FilePermissions.c_str(), CM_NULLPTR, + CM_NULLPTR, CM_NULLPTR, indent.Next()); + os << indent << "endif()\n"; + files.clear(); + } +} + +void cmInstallExportAndroidMKGenerator::GenerateScriptActions( + std::ostream& os, Indent const& indent) +{ + // Remove old per-configuration export files if the main changes. + std::string installedDir = "$ENV{DESTDIR}"; + installedDir += this->ConvertToAbsoluteDestination(this->Destination); + installedDir += "/"; + std::string installedFile = installedDir; + installedFile += this->FileName; + os << indent << "if(EXISTS \"" << installedFile << "\")\n"; + Indent indentN = indent.Next(); + Indent indentNN = indentN.Next(); + Indent indentNNN = indentNN.Next(); + /* clang-format off */ + os << indentN << "file(DIFFERENT EXPORT_FILE_CHANGED FILES\n" + << indentN << " \"" << installedFile << "\"\n" + << indentN << " \"" << this->MainImportFile << "\")\n"; + os << indentN << "if(EXPORT_FILE_CHANGED)\n"; + os << indentNN << "file(GLOB OLD_CONFIG_FILES \"" << installedDir + << this->EFGen->GetConfigImportFileGlob() << "\")\n"; + os << indentNN << "if(OLD_CONFIG_FILES)\n"; + os << indentNNN << "message(STATUS \"Old export file \\\"" << installedFile + << "\\\" will be replaced. Removing files [${OLD_CONFIG_FILES}].\")\n"; + os << indentNNN << "file(REMOVE ${OLD_CONFIG_FILES})\n"; + os << indentNN << "endif()\n"; + os << indentN << "endif()\n"; + os << indent << "endif()\n"; + /* clang-format on */ + + // Install the main export file. + std::vector<std::string> files; + files.push_back(this->MainImportFile); + this->AddInstallRule(os, this->Destination, cmInstallType_FILES, files, + false, this->FilePermissions.c_str(), CM_NULLPTR, + CM_NULLPTR, CM_NULLPTR, indent); +} diff --git a/Source/cmInstallExportAndroidMKGenerator.h b/Source/cmInstallExportAndroidMKGenerator.h new file mode 100644 index 0000000..158972d --- /dev/null +++ b/Source/cmInstallExportAndroidMKGenerator.h @@ -0,0 +1,46 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ +#ifndef cmInstallExportAndroidMKGenerator_h +#define cmInstallExportAndroidMKGenerator_h + +#include "cmInstallExportGenerator.h" + +class cmExportInstallFileGenerator; +class cmInstallFilesGenerator; +class cmInstallTargetGenerator; +class cmExportSet; +class cmMakefile; + +/** \class cmInstallExportAndroidMKGenerator + * \brief Generate rules for creating an export files. + */ +class cmInstallExportAndroidMKGenerator : public cmInstallExportGenerator +{ +public: + cmInstallExportAndroidMKGenerator( + cmExportSet* exportSet, const char* dest, const char* file_permissions, + const std::vector<std::string>& configurations, const char* component, + MessageLevel message, bool exclude_from_all, const char* filename, + const char* name_space, bool exportOld); + ~cmInstallExportAndroidMKGenerator(); + + void Compute(cmLocalGenerator* lg); + +protected: + virtual void GenerateScript(std::ostream& os); + virtual void GenerateScriptConfigs(std::ostream& os, Indent const& indent); + virtual void GenerateScriptActions(std::ostream& os, Indent const& indent); + void GenerateImportFile(cmExportSet const* exportSet); + void GenerateImportFile(const char* config, cmExportSet const* exportSet); +}; + +#endif diff --git a/Source/cmInstallExportGenerator.cxx b/Source/cmInstallExportGenerator.cxx index 27628f4..5ea7faf 100644 --- a/Source/cmInstallExportGenerator.cxx +++ b/Source/cmInstallExportGenerator.cxx @@ -16,6 +16,9 @@ #include <sstream> #include <utility> +#ifdef CMAKE_BUILD_WITH_CMAKE +#include "cmExportInstallAndroidMKGenerator.h" +#endif #include "cmExportInstallFileGenerator.h" #include "cmExportSet.h" #include "cmInstallType.h" @@ -27,7 +30,7 @@ cmInstallExportGenerator::cmInstallExportGenerator( 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) + const char* filename, const char* name_space, bool exportOld, bool android) : cmInstallGenerator(destination, configurations, component, message, exclude_from_all) , ExportSet(exportSet) @@ -37,7 +40,13 @@ cmInstallExportGenerator::cmInstallExportGenerator( , ExportOld(exportOld) , LocalGenerator(CM_NULLPTR) { - this->EFGen = new cmExportInstallFileGenerator(this); + if (android) { +#ifdef CMAKE_BUILD_WITH_CMAKE + this->EFGen = new cmExportInstallAndroidMKGenerator(this); +#endif + } else { + this->EFGen = new cmExportInstallFileGenerator(this); + } exportSet->AddInstallation(this); } diff --git a/Source/cmInstallExportGenerator.h b/Source/cmInstallExportGenerator.h index 5539827..ac02386 100644 --- a/Source/cmInstallExportGenerator.h +++ b/Source/cmInstallExportGenerator.h @@ -37,7 +37,8 @@ public: const std::vector<std::string>& configurations, const char* component, MessageLevel message, bool exclude_from_all, const char* filename, - const char* name_space, bool exportOld); + const char* name_space, bool exportOld, + bool android); ~cmInstallExportGenerator() CM_OVERRIDE; cmExportSet* GetExportSet() { return this->ExportSet; } diff --git a/Tests/RunCMake/AndroidMK/AndroidMK-check.cmake b/Tests/RunCMake/AndroidMK/AndroidMK-check.cmake new file mode 100644 index 0000000..691e326 --- /dev/null +++ b/Tests/RunCMake/AndroidMK/AndroidMK-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}" ANDROID_MK) + # clean up new lines + string(REGEX REPLACE "\r\n" "\n" ANDROID_MK "${ANDROID_MK}") + string(REGEX REPLACE "\n+$" "" ANDROID_MK "${ANDROID_MK}") + # 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 "${ANDROID_MK}" MATCHES "${expected}") + set(RunCMake_TEST_FAILED + "${file} does not match ${expected_file}: + +Android.mk contents = [\n${ANDROID_MK}\n] +Expected = [\n${expected}\n]") + endif() +endmacro() + +compare_file_to_expected( +"${RunCMake_BINARY_DIR}/AndroidMK-build/Android.mk" +"${RunCMake_TEST_SOURCE_DIR}/expectedBuildAndroidMK.txt") +compare_file_to_expected( +"${RunCMake_BINARY_DIR}/AndroidMK-build/CMakeFiles/Export/share/ndk-modules/Android.mk" +"${RunCMake_TEST_SOURCE_DIR}/expectedInstallAndroidMK.txt") diff --git a/Tests/RunCMake/AndroidMK/AndroidMK.cmake b/Tests/RunCMake/AndroidMK/AndroidMK.cmake new file mode 100644 index 0000000..ed21e58 --- /dev/null +++ b/Tests/RunCMake/AndroidMK/AndroidMK.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 foo.cxx) +add_library(dog foo.cxx) +target_link_libraries(foo car bar dog debug -lm) +export(TARGETS bar dog car foo ANDROID_MK + ${build_BINARY_DIR}/Android.mk) +install(TARGETS bar dog car foo DESTINATION lib EXPORT myexp) +install(EXPORT_ANDROID_MK myexp DESTINATION share/ndk-modules) diff --git a/Tests/RunCMake/AndroidMK/CMakeLists.txt b/Tests/RunCMake/AndroidMK/CMakeLists.txt new file mode 100644 index 0000000..576787a --- /dev/null +++ b/Tests/RunCMake/AndroidMK/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/AndroidMK/RunCMakeTest.cmake b/Tests/RunCMake/AndroidMK/RunCMakeTest.cmake new file mode 100644 index 0000000..786d45b --- /dev/null +++ b/Tests/RunCMake/AndroidMK/RunCMakeTest.cmake @@ -0,0 +1,2 @@ +include(RunCMake) +run_cmake(AndroidMK) diff --git a/Tests/RunCMake/AndroidMK/expectedBuildAndroidMK.txt b/Tests/RunCMake/AndroidMK/expectedBuildAndroidMK.txt new file mode 100644 index 0000000..def8fcb --- /dev/null +++ b/Tests/RunCMake/AndroidMK/expectedBuildAndroidMK.txt @@ -0,0 +1,23 @@ +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.* +include.*PREBUILT_STATIC_LIBRARY.* +.* +include.*CLEAR_VARS.* +LOCAL_MODULE.*car +LOCAL_SRC_FILES.*.*car.* +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 +include.*PREBUILT_STATIC_LIBRARY.* diff --git a/Tests/RunCMake/AndroidMK/expectedInstallAndroidMK.txt b/Tests/RunCMake/AndroidMK/expectedInstallAndroidMK.txt new file mode 100644 index 0000000..1bdb308 --- /dev/null +++ b/Tests/RunCMake/AndroidMK/expectedInstallAndroidMK.txt @@ -0,0 +1,25 @@ +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.* +include.*PREBUILT_STATIC_LIBRARY.* + +include.*CLEAR_VARS.* +LOCAL_MODULE.*car +LOCAL_SRC_FILES.*_IMPORT_PREFIX./lib.*car.* +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 +include.*PREBUILT_STATIC_LIBRARY.* diff --git a/Tests/RunCMake/AndroidMK/foo.cxx b/Tests/RunCMake/AndroidMK/foo.cxx new file mode 100644 index 0000000..3695dc9 --- /dev/null +++ b/Tests/RunCMake/AndroidMK/foo.cxx @@ -0,0 +1,3 @@ +void foo() +{ +} diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index fa3d0f9..e36b2a6 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -331,6 +331,8 @@ add_RunCMake_test_group(CPack "DEB;RPM;TGZ") # for MSVC compilers CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS property is used add_RunCMake_test(AutoExportDll) +add_RunCMake_test(AndroidMK) + if(CMake_TEST_ANDROID_NDK OR CMake_TEST_ANDROID_STANDALONE_TOOLCHAIN) if(NOT "${CMAKE_GENERATOR}" MATCHES "Make|Ninja") message(FATAL_ERROR "Android tests supported only by Makefile and Ninja generators") |