Commit 42ce9f1e authored by Bill Hoffman's avatar Bill Hoffman Committed by Brad King
Browse files

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
parent d5257063
Pipeline #25872 passed with stage
......@@ -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.
......@@ -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.
......
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.
......@@ -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
......
/*============================================================================
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";
}
/*============================================================================
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
......@@ -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());
......
......@@ -54,6 +54,7 @@ private:
cmCAString Namespace;
cmCAString Filename;
cmCAEnabler ExportOld;
cmCAString AndroidMKFile;
cmExportSet* ExportSet;
......
/*============================================================================
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;
}
/*============================================================================
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,