Add support for creating prebuilt Android.mk files

Add options to the `install()` and `export()` commands to export the
targets we build into Android.mk files that reference them as prebuilt
libraries with associated usage requirements (compile definitions,
include directories, link libraries).  This will allow CMake-built
projects to be imported into projects using the Android NDK build
system.

Closes: #15562
This commit is contained in:
Bill Hoffman 2016-07-12 13:26:55 -04:00 committed by Brad King
parent d5257063b0
commit 42ce9f1e71
24 changed files with 941 additions and 9 deletions

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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

View File

@ -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";
}

View File

@ -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

View File

@ -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());

View File

@ -54,6 +54,7 @@ private:
cmCAString Namespace;
cmCAString Filename;
cmCAEnabler ExportOld;
cmCAString AndroidMKFile;
cmExportSet* ExportSet;

View File

@ -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;
}

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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);
}

View File

@ -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

View File

@ -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);
}

View File

@ -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; }

View File

@ -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")

View File

@ -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)

View File

@ -0,0 +1,3 @@
cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE) # or languages needed
include(${RunCMake_TEST}.cmake)

View File

@ -0,0 +1,2 @@
include(RunCMake)
run_cmake(AndroidMK)

View File

@ -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.*

View File

@ -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.*

View File

@ -0,0 +1,3 @@
void foo()
{
}

View File

@ -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")