Android Support: Exporting to Android Archive (AAR) (#10271)

* added android triplets

* added android support to vcpkg

* added export directories to git ignore

* fix libraries naming

* added vckpg sources to visual studio project files

* rename file location

* issue with std::string fs:path copy initialization

* format path on VStudio

* fix checks format cannot work on fs::path

* support header only libraries

* support using architecture instead of triplets

* added prefab support

* added debug logs and prefab debug flag

* added support for empty packages i.e openssl
This commit is contained in:
atkawa7 2020-04-06 23:36:17 +02:00 committed by GitHub
parent 6283a51112
commit 52b5dfd2ef
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 972 additions and 9 deletions

6
.gitignore vendored
View file

@ -304,6 +304,10 @@ __pycache__/
!triplets/community/x86-windows-static.cmake
!triplets/community/x86-windows-static-md.cmake
!triplets/community/x64-osx-dynamic.cmake
!triplets/community/x64-android.cmake
!triplets/community/x86-android.cmake
!triplets/community/arm-android.cmake
!triplets/community/arm64-android.cmake
!triplets/arm-uwp.cmake
!triplets/x64-uwp.cmake
!triplets/x64-windows.cmake
@ -320,3 +324,5 @@ __pycache__/
# vcpkg - End
############################################################
archives
.DS_Store
prefab/

View file

@ -0,0 +1,124 @@
## Exporting to Android Archives (AAR files)
Vcpkg current supports exporting to android archive files([AAR files](https://developer.android.com/studio/projects/android-library)). Once the archive is created it can imported in Android Studio as a native dependent. The archive is automatically consumed using [android studio's prefab tool](https://github.com/google/prefab). For more information on Prefab checkout the following article ["Native Dependencies in Android Studio 4.0"](https://android-developers.googleblog.com/2020/02/native-dependencies-in-android-studio-40.html) and the documentation on how to use prefab on [https://google.github.io/prefab/](https://google.github.io/prefab).
#### To support export to android the following tools should be available;
- `maven <optional>`
- `ndk <required>`
- `7zip <required on windows>` or `zip <required on linux>`
**Android triplets that support the following architectures arm64-v8a, armeabi-v7a, x86_64 x86 must be present**
#### An example of a triplet configuration targeting android would be
```cmake
set(VCPKG_TARGET_ARCHITECTURE arm64)
set(VCPKG_CRT_LINKAGE dynamic)
set(VCPKG_LIBRARY_LINKAGE dynamic)
set(VCPKG_CMAKE_SYSTEM_NAME Android)
```
The following table outlines the mapping from vcpkg architectures to android architectures
|vcpkg architecture | android architecture |
|-------------------|----------------------|
|arm64 | arm64-v8a |
|arm | armeabi-v7a |
|x64 | x86_64 |
|x86 | x86 |
**Please note the four architectures are required. If any is missing the export will fail**
**To export the following environment `ANDROID_NDK_HOME` variable is required for exporting**
#### Example exporting [jsoncpp]
The `--prefab-maven` flag is option. Only call it when you have maven installed
```
./vcpkg export --triplet x64-android jsoncpp --prefab --prefab-maven
```
```
The following packages are already built and will be exported:
jsoncpp:x86-android
Exporting package jsoncpp...
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------< org.apache.maven:standalone-pom >-------------------
[INFO] Building Maven Stub Project (No POM) 1
[INFO] --------------------------------[ pom ]---------------------------------
[INFO]
[INFO] --- maven-install-plugin:2.4:install-file (default-cli) @ standalone-pom ---
[INFO] Installing<root>/prefab/jsoncpp/jsoncpp-1.9.2.aar to /.m2/repository/com/vcpkg/ndk/support/jsoncpp/1.9.2/jsoncpp-1.9.2.aar
[INFO] Installing <vcpkg_root>/prefab/jsoncpp/pom.xml to /.m2/repository/com/vcpkg/ndk/support/jsoncpp/1.9.2/jsoncpp-1.9.2.pom
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.301 s
[INFO] Finished at: 2020-03-01T10:18:15Z
[INFO] ------------------------------------------------------------------------
In app/build.gradle
com.vcpkg.ndk.support:jsoncpp:1.9.2
And cmake flags
externalNativeBuild {
cmake {
arguments '-DANDROID_STL=c++_shared'
cppFlags "-std=c++17"
}
}
In gradle.properties
android.enablePrefab=true
android.enableParallelJsonGen=false
android.prefabVersion=${prefab.version}
Successfuly exported jsoncpp. Checkout <vcpkg_root>/prefab/jsoncpp/aar
```
#### The output directory after export
```
prefab
└── jsoncpp
├── aar
│   ├── AndroidManifest.xml
│   ├── META-INF
│   │   └── LICENCE
│   └── prefab
│   ├── modules
│   │   └── jsoncpp
│   │   ├── include
│   │   │   └── json
│   │   │   ├── allocator.h
│   │   │   ├── assertions.h
│   │   │   ├── autolink.h
│   │   │   ├── config.h
│   │   │   ├── forwards.h
│   │   │   ├── json.h
│   │   │   ├── json_features.h
│   │   │   ├── reader.h
│   │   │   ├── value.h
│   │   │   ├── version.h
│   │   │   └── writer.h
│   │   ├── libs
│   │   │   ├── android.arm64-v8a
│   │   │   │   ├── abi.json
│   │   │   │   └── libjsoncpp.so
│   │   │   ├── android.armeabi-v7a
│   │   │   │   ├── abi.json
│   │   │   │   └── libjsoncpp.so
│   │   │   ├── android.x86
│   │   │   │   ├── abi.json
│   │   │   │   └── libjsoncpp.so
│   │   │   └── android.x86_64
│   │   │   ├── abi.json
│   │   │   └── libjsoncpp.so
│   │   └── module.json
│   └── prefab.json
├── jsoncpp-1.9.2.aar
└── pom.xml
13 directories, 25 files
```

View file

@ -0,0 +1,82 @@
#pragma once
#include <vcpkg/base/system.h>
#include <vcpkg/dependencies.h>
#include <vcpkg/vcpkgpaths.h>
#include <vector>
namespace vcpkg::Export::Prefab
{
constexpr int kFragmentSize = 3;
struct Options
{
Optional<std::string> maybe_group_id;
Optional<std::string> maybe_artifact_id;
Optional<std::string> maybe_version;
Optional<std::string> maybe_min_sdk;
Optional<std::string> maybe_target_sdk;
bool enable_maven;
bool enable_debug;
};
struct NdkVersion
{
NdkVersion(int _major, int _minor, int _patch) : m_major{_major},
m_minor{_minor},
m_patch{_patch}{
}
int major() { return this->m_major; }
int minor() { return this->m_minor; }
int patch() { return this->m_patch; }
std::string to_string();
void to_string(std::string& out);
private:
int m_major;
int m_minor;
int m_patch;
};
struct ABIMetadata
{
std::string abi;
int api;
int ndk;
std::string stl;
std::string to_string();
};
struct PlatformModuleMetadata
{
std::vector<std::string> export_libraries;
std::string library_name;
std::string to_json();
};
struct ModuleMetadata
{
std::vector<std::string> export_libraries;
std::string library_name;
PlatformModuleMetadata android;
std::string to_json();
};
struct PackageMetadata
{
std::string name;
int schema;
std::vector<std::string> dependencies;
std::string version;
std::string to_json();
};
void do_export(const std::vector<Dependencies::ExportPlanAction>& export_plan,
const VcpkgPaths& paths,
const Options& prefab_options, const Triplet& triplet);
Optional<std::string> find_ndk_version(const std::string &content);
Optional<NdkVersion> to_version(const std::string &version);
}

View file

@ -23,6 +23,11 @@ namespace vcpkg
static const Triplet X64_UWP;
static const Triplet ARM_UWP;
static const Triplet ARM64_UWP;
static const Triplet ARM_ANDROID;
static const Triplet ARM64_ANDROID;
static const Triplet X86_ANDROID;
static const Triplet X64_ANDROID;
const std::string& canonical_name() const;
const std::string& to_string() const;

View file

@ -14,6 +14,8 @@ namespace vcpkg
namespace Tools
{
static const std::string SEVEN_ZIP = "7zip";
static const std::string SEVEN_ZIP_ALT = "7z";
static const std::string MAVEN = "mvn";
static const std::string CMAKE = "cmake";
static const std::string GIT = "git";
static const std::string NINJA = "ninja";

View file

@ -3,6 +3,7 @@
#include <vcpkg/commands.h>
#include <vcpkg/dependencies.h>
#include <vcpkg/export.chocolatey.h>
#include <vcpkg/export.prefab.h>
#include <vcpkg/export.h>
#include <vcpkg/export.ifw.h>
#include <vcpkg/help.h>
@ -196,6 +197,7 @@ namespace vcpkg::Export
{
constexpr const ArchiveFormat ZIP(ArchiveFormat::BackingEnum::ZIP, "zip", "zip");
constexpr const ArchiveFormat SEVEN_ZIP(ArchiveFormat::BackingEnum::SEVEN_ZIP, "7z", "7zip");
constexpr const ArchiveFormat AAR(ArchiveFormat::BackingEnum::ZIP, "aar", "zip");
}
static fs::path do_archive_export(const VcpkgPaths& paths,
@ -263,6 +265,7 @@ namespace vcpkg::Export
bool zip = false;
bool seven_zip = false;
bool chocolatey = false;
bool prefab = false;
bool all_installed = false;
Optional<std::string> maybe_output;
@ -271,6 +274,7 @@ namespace vcpkg::Export
Optional<std::string> maybe_nuget_version;
IFW::Options ifw_options;
Prefab::Options prefab_options;
Chocolatey::Options chocolatey_options;
std::vector<PackageSpec> specs;
};
@ -293,8 +297,20 @@ namespace vcpkg::Export
static constexpr StringLiteral OPTION_CHOCOLATEY_MAINTAINER = "--x-maintainer";
static constexpr StringLiteral OPTION_CHOCOLATEY_VERSION_SUFFIX = "--x-version-suffix";
static constexpr StringLiteral OPTION_ALL_INSTALLED = "--x-all-installed";
static constexpr StringLiteral OPTION_PREFAB = "--prefab";
static constexpr StringLiteral OPTION_PREFAB_GROUP_ID = "--prefab-group-id";
static constexpr StringLiteral OPTION_PREFAB_ARTIFACT_ID = "--prefab-artifact-id";
static constexpr StringLiteral OPTION_PREFAB_VERSION = "--prefab-version";
static constexpr StringLiteral OPTION_PREFAB_SDK_MIN_VERSION = "--prefab-min-sdk";
static constexpr StringLiteral OPTION_PREFAB_SDK_TARGET_VERSION = "--prefab-target-sdk";
static constexpr StringLiteral OPTION_PREFAB_ENABLE_MAVEN = "--prefab-maven";
static constexpr StringLiteral OPTION_PREFAB_ENABLE_DEBUG = "--prefab-debug";
static constexpr std::array<CommandSwitch, 8> EXPORT_SWITCHES = {{
static constexpr std::array<CommandSwitch, 11> EXPORT_SWITCHES = {{
{OPTION_DRY_RUN, "Do not actually export"},
{OPTION_RAW, "Export to an uncompressed directory"},
{OPTION_NUGET, "Export a NuGet package"},
@ -302,10 +318,13 @@ namespace vcpkg::Export
{OPTION_ZIP, "Export to a zip file"},
{OPTION_SEVEN_ZIP, "Export to a 7zip (.7z) file"},
{OPTION_CHOCOLATEY, "Export a Chocolatey package (experimental feature)"},
{OPTION_PREFAB, "Export to Prefab format"},
{OPTION_PREFAB_ENABLE_MAVEN, "Enable maven"},
{OPTION_PREFAB_ENABLE_DEBUG, "Enable prefab debug"},
{OPTION_ALL_INSTALLED, "Export all installed packages"},
}};
static constexpr std::array<CommandSetting, 10> EXPORT_SETTINGS = {{
static constexpr std::array<CommandSetting, 15> EXPORT_SETTINGS = {{
{OPTION_OUTPUT, "Specify the output name (used to construct filename)"},
{OPTION_NUGET_ID, "Specify the id for the exported NuGet package (overrides --output)"},
{OPTION_NUGET_VERSION, "Specify the version for the exported NuGet package"},
@ -318,6 +337,11 @@ namespace vcpkg::Export
"Specify the maintainer for the exported Chocolatey package (experimental feature)"},
{OPTION_CHOCOLATEY_VERSION_SUFFIX,
"Specify the version suffix to add for the exported Chocolatey package (experimental feature)"},
{OPTION_PREFAB_GROUP_ID, "GroupId uniquely identifies your project according maven specifications"},
{OPTION_PREFAB_ARTIFACT_ID, "Artifact Id is the name of the project according maven specifications"},
{OPTION_PREFAB_VERSION, "Version is the name of the project according maven specifications"},
{OPTION_PREFAB_SDK_MIN_VERSION, "Android minimum supported sdk version"},
{OPTION_PREFAB_SDK_TARGET_VERSION, "Android target sdk version"},
}};
const CommandStructure COMMAND_STRUCTURE = {
@ -343,8 +367,11 @@ namespace vcpkg::Export
ret.zip = options.switches.find(OPTION_ZIP) != options.switches.cend();
ret.seven_zip = options.switches.find(OPTION_SEVEN_ZIP) != options.switches.cend();
ret.chocolatey = options.switches.find(OPTION_CHOCOLATEY) != options.switches.cend();
ret.all_installed = options.switches.find(OPTION_ALL_INSTALLED) != options.switches.end();
ret.prefab = options.switches.find(OPTION_PREFAB) != options.switches.cend();
ret.prefab_options.enable_maven = options.switches.find(OPTION_PREFAB_ENABLE_MAVEN) != options.switches.cend();
ret.prefab_options.enable_debug = options.switches.find(OPTION_PREFAB_ENABLE_DEBUG) != options.switches.cend();
ret.maybe_output = maybe_lookup(options.settings, OPTION_OUTPUT);
ret.all_installed = options.switches.find(OPTION_ALL_INSTALLED) != options.switches.end();
if (ret.all_installed)
{
@ -363,10 +390,10 @@ namespace vcpkg::Export
});
}
if (!ret.raw && !ret.nuget && !ret.ifw && !ret.zip && !ret.seven_zip && !ret.dry_run && !ret.chocolatey)
if (!ret.raw && !ret.nuget && !ret.ifw && !ret.zip && !ret.seven_zip && !ret.dry_run && !ret.chocolatey && !ret.prefab)
{
System::print2(System::Color::error,
"Must provide at least one export type: --raw --nuget --ifw --zip --7zip --chocolatey\n");
"Must provide at least one export type: --raw --nuget --ifw --zip --7zip --chocolatey --prefab\n");
System::print2(COMMAND_STRUCTURE.example_text);
Checks::exit_fail(VCPKG_LINE_INFO);
}
@ -417,6 +444,16 @@ namespace vcpkg::Export
{OPTION_IFW_CONFIG_FILE_PATH, ret.ifw_options.maybe_config_file_path},
{OPTION_IFW_INSTALLER_FILE_PATH, ret.ifw_options.maybe_installer_file_path},
});
options_implies(OPTION_PREFAB,
ret.prefab,
{
{OPTION_PREFAB_ARTIFACT_ID, ret.prefab_options.maybe_artifact_id},
{OPTION_PREFAB_GROUP_ID, ret.prefab_options.maybe_group_id},
{OPTION_PREFAB_SDK_MIN_VERSION, ret.prefab_options.maybe_min_sdk},
{OPTION_PREFAB_SDK_TARGET_VERSION, ret.prefab_options.maybe_target_sdk},
{OPTION_PREFAB_VERSION, ret.prefab_options.maybe_version},
});
options_implies(OPTION_CHOCOLATEY,
ret.chocolatey,
@ -605,6 +642,10 @@ With a project open, go to Tools->NuGet Package Manager->Package Manager Console
Chocolatey::do_export(export_plan, paths, opts.chocolatey_options);
}
if(opts.prefab){
Prefab::do_export(export_plan, paths, opts.prefab_options, default_triplet);
}
Checks::exit_success(VCPKG_LINE_INFO);
}
}

View file

@ -0,0 +1,695 @@
#include "pch.h"
#include <vcpkg/base/checks.h>
#include <vcpkg/base/files.h>
#include <vcpkg/base/system.print.h>
#include <vcpkg/base/system.process.h>
#include <vcpkg/build.h>
#include <vcpkg/cmakevars.h>
#include <vcpkg/commands.h>
#include <vcpkg/export.h>
#include <vcpkg/export.prefab.h>
#include <vcpkg/install.h>
namespace vcpkg::Export::Prefab
{
using Dependencies::ExportPlanAction;
using Dependencies::ExportPlanType;
using Install::InstallDir;
using System::CPUArchitecture;
std::vector<fs::path> find_modules(const VcpkgPaths& system, const fs::path& root, const std::string& ext)
{
std::vector<fs::path> paths;
Files::Filesystem& utils = system.get_filesystem();
std::error_code error_code;
if (!utils.exists(root, error_code) || !utils.is_directory(root)) return paths;
fs::stdfs::recursive_directory_iterator it(root);
fs::stdfs::recursive_directory_iterator endit;
while (it != endit)
{
if (utils.is_regular_file(*it) && it->path().extension() == ext)
{
paths.push_back(it->path().filename());
}
++it;
}
return paths;
}
std::string NdkVersion::to_string()
{
std::string ret;
this->to_string(ret);
return ret;
}
void NdkVersion::to_string(std::string& out)
{
out.append("NdkVersion{major=")
.append(std::to_string(major()))
.append(",minor=")
.append(std::to_string(minor()))
.append(",patch=")
.append(std::to_string(patch()))
.append("}");
}
std::string jsonify(const std::vector<std::string>& dependencies)
{
std::vector<std::string> deps;
for (const auto& dep : dependencies)
{
deps.push_back("\"" + dep + "\"");
}
return Strings::join(",", deps);
}
std::string null_if_empty(const std::string& str)
{
std::string copy = str;
if (copy.size() == 0)
{
copy = "null";
}
else
{
copy = "\"" + copy + "\"";
}
return copy;
}
std::string null_if_empty_array(const std::string& str)
{
std::string copy = str;
if (copy.size() == 0)
{
copy = "null";
}
else
{
copy = "[" + copy + "]";
}
return copy;
}
std::string ABIMetadata::to_string()
{
std::string TEMPLATE = R"({
"abi":"@ABI@",
"api":@API@,
"ndk":@NDK@,
"stl":"@STL@"
})";
std::string json = Strings::replace_all(std::move(TEMPLATE), "@ABI@", abi);
json = Strings::replace_all(std::move(json), "@API@", std::to_string(api));
json = Strings::replace_all(std::move(json), "@NDK@", std::to_string(ndk));
json = Strings::replace_all(std::move(json), "@STL@", stl);
return json;
}
std::string PlatformModuleMetadata::to_json()
{
std::string TEMPLATE = R"({
"export_libraries": @LIBRARIES@,
"library_name": @LIBRARY_NAME@
})";
std::string json = Strings::replace_all(std::move(TEMPLATE), "@LIBRARY_NAME@", null_if_empty(library_name));
json = Strings::replace_all(std::move(json), "@LIBRARIES@", null_if_empty_array(jsonify(export_libraries)));
return json;
}
std::string ModuleMetadata::to_json()
{
std::string TEMPLATE = R"({
"export_libraries": [@LIBRARIES@],
"library_name":@LIBRARY_NAME@,
"android": @ANDROID_METADATA@
})";
std::string json = Strings::replace_all(std::move(TEMPLATE), "@LIBRARY_NAME@", null_if_empty(library_name));
json = Strings::replace_all(std::move(json), "@LIBRARIES@", jsonify(export_libraries));
json = Strings::replace_all(std::move(json), "@ANDROID_METADATA@", android.to_json());
return json;
}
std::string PackageMetadata::to_json()
{
std::string deps = jsonify(dependencies);
std::string TEMPLATE = R"({
"name":"@PACKAGE_NAME@",
"schema_version": @PACKAGE_SCHEMA@,
"dependencies":[@PACKAGE_DEPS@],
"version":"@PACKAGE_VERSION@"
})";
std::string json = Strings::replace_all(std::move(TEMPLATE), "@PACKAGE_NAME@", name);
json = Strings::replace_all(std::move(json), "@PACKAGE_SCHEMA@", std::to_string(schema));
json = Strings::replace_all(std::move(json), "@PACKAGE_DEPS@", deps);
json = Strings::replace_all(std::move(json), "@PACKAGE_VERSION@", version);
return json;
}
Optional<std::string> find_ndk_version(const std::string& content)
{
std::smatch pkg_match;
std::regex pkg_regex(R"(Pkg\.Revision\s*=\s*(\d+)(\.\d+)(\.\d+)\s*)");
if (std::regex_search(content, pkg_match, pkg_regex))
{
for (const auto& p : pkg_match)
{
std::string delimiter = "=";
std::string s = p.str();
auto it = s.find(delimiter);
if (it != std::string::npos)
{
std::string token = (s.substr(s.find(delimiter) + 1, s.size()));
return Strings::trim(std::move(token));
}
}
}
return {};
}
Optional<NdkVersion> to_version(const std::string& version)
{
if (version.size() > 100) return {};
size_t last = 0;
size_t next = 0;
std::vector<int> fragments(0);
while ((next = version.find(".", last)) != std::string::npos)
{
fragments.push_back(std::stoi(version.substr(last, next - last)));
last = next + 1;
}
fragments.push_back(std::stoi(version.substr(last)));
if (fragments.size() == kFragmentSize)
{
return NdkVersion(fragments[0], fragments[1], fragments[2]);
}
return {};
}
static void compress_directory(const VcpkgPaths& paths, const fs::path& source, const fs::path& destination)
{
auto& fs = paths.get_filesystem();
std::error_code ec;
fs.remove(destination, ec);
Checks::check_exit(
VCPKG_LINE_INFO, !fs.exists(destination), "Could not remove file: %s", destination.u8string());
#if defined(_WIN32)
auto&& seven_zip_exe = paths.get_tool_exe(Tools::SEVEN_ZIP);
System::cmd_execute_and_capture_output(
Strings::format(
R"("%s" a "%s" "%s\*")", seven_zip_exe.u8string(), destination.u8string(), source.u8string()),
System::get_clean_environment());
#else
System::cmd_execute_clean(
Strings::format(R"(cd '%s' && zip --quiet -r '%s' *)", source.u8string(), destination.u8string()));
#endif
}
void maven_install(const fs::path& aar, const fs::path& pom, const Options& prefab_options)
{
if(prefab_options.enable_debug){
System::print2("\n[DEBUG] Installing POM and AAR file to ~/.m2\n\n");
}
const char* cmd_line_format = prefab_options.enable_debug ? R"("%s" "install:install-file" "-Dfile=%s" "-DpomFile=%s")"
: R"("%s" "-q" "install:install-file" "-Dfile=%s" "-DpomFile=%s")";
const auto cmd_line = Strings::format(cmd_line_format,
Tools::MAVEN,
aar.u8string(),
pom.u8string());
const int exit_code = System::cmd_execute_clean(cmd_line);
Checks::check_exit(VCPKG_LINE_INFO, exit_code == 0, "Error: %s installing maven file", aar.generic_u8string());
}
Build::PreBuildInfo build_info_from_triplet(const VcpkgPaths& paths,
const std::unique_ptr<CMakeVars::CMakeVarProvider>& provider,
const Triplet& triplet)
{
provider->load_generic_triplet_vars(triplet);
const Build::PreBuildInfo pre_build_info(
paths, triplet, provider->get_generic_triplet_vars(triplet).value_or_exit(VCPKG_LINE_INFO));
return pre_build_info;
}
bool is_supported(const Build::PreBuildInfo& info)
{
return Strings::case_insensitive_ascii_equals(info.cmake_system_name, "android");
}
void do_export(const std::vector<ExportPlanAction>& export_plan,
const VcpkgPaths& paths,
const Options& prefab_options,
const Triplet& default_triplet)
{
auto provider = CMakeVars::make_triplet_cmake_var_provider(paths);
auto build_info = build_info_from_triplet(paths, provider, default_triplet);
Checks::check_exit(VCPKG_LINE_INFO, is_supported(build_info), "Currenty supported on android triplets");
std::vector<VcpkgPaths::TripletFile> available_triplets = paths.get_available_triplets();
std::unordered_map<CPUArchitecture, std::string> required_archs = {
{CPUArchitecture::ARM, "armeabi-v7a"},
{CPUArchitecture::ARM64, "arm64-v8a"},
{CPUArchitecture::X86, "x86"},
{CPUArchitecture::X64, "x86_64"}};
std::unordered_map<CPUArchitecture, int> cpu_architecture_api_map = {{CPUArchitecture::ARM64, 21},
{CPUArchitecture::ARM, 16},
{CPUArchitecture::X64, 21},
{CPUArchitecture::X86, 16}};
std::vector<Triplet> triplets;
std::unordered_map<Triplet, std::string> triplet_abi_map;
std::unordered_map<Triplet, int> triplet_api_map;
for (auto& triplet_file : available_triplets){
if (triplet_file.name.size() > 0){
Triplet triplet = Triplet::from_canonical_name(std::move(triplet_file.name));
auto build_info = build_info_from_triplet(paths, provider, triplet);
if (is_supported(build_info)){
auto cpu_architecture =System::to_cpu_architecture(build_info.target_architecture).value_or_exit(VCPKG_LINE_INFO);
auto required_arch = required_archs.find(cpu_architecture);
if (required_arch != required_archs.end()){
triplets.push_back(triplet);
triplet_abi_map[triplet] = required_archs[cpu_architecture];
triplet_api_map[triplet] = cpu_architecture_api_map[cpu_architecture];
required_archs.erase(required_arch);
}
}
}
}
Checks::check_exit(
VCPKG_LINE_INFO, required_archs.empty(), "Export requires the following architectures arm64-v8a, armeabi-v7a, x86_64, x86 to be present");
Optional<std::string> android_ndk_home = System::get_environment_variable("ANDROID_NDK_HOME");
Checks::check_exit(
VCPKG_LINE_INFO, android_ndk_home.has_value(), "Error: ANDROID_NDK_HOME environment missing");
Files::Filesystem& utils = paths.get_filesystem();
const fs::path ndk_location = android_ndk_home.value_or_exit(VCPKG_LINE_INFO);
Checks::check_exit(VCPKG_LINE_INFO,
utils.exists(ndk_location),
"Error: ANDROID_NDK_HOME Directory does not exists %s",
ndk_location.generic_u8string());
const fs::path source_properties_location = ndk_location / "source.properties";
Checks::check_exit(VCPKG_LINE_INFO,
utils.exists(ndk_location),
"Error: source.properties missing in ANDROID_NDK_HOME directory %s",
source_properties_location.generic_u8string());
std::string content = utils.read_contents(source_properties_location, VCPKG_LINE_INFO);
Optional<std::string> version_opt = find_ndk_version(content);
Checks::check_exit(VCPKG_LINE_INFO,
version_opt.has_value(),
"Error: NDK version missing %s",
source_properties_location.generic_u8string());
NdkVersion version = to_version(version_opt.value_or_exit(VCPKG_LINE_INFO)).value_or_exit(VCPKG_LINE_INFO);
const fs::path vcpkg_root_path = paths.root;
const fs::path raw_exported_dir_path = vcpkg_root_path / "prefab";
utils.remove_all(raw_exported_dir_path, VCPKG_LINE_INFO);
/*
prefab
<name>
aar
   AndroidManifest.xml
   META-INF
      LICENCE
   prefab
   modules
      <module>
      include
      libs
         android.arm64-v8a
            abi.json
            lib<module>.so
         android.armeabi-v7a
            abi.json
            lib<module>.so
         android.x86
            abi.json
            lib<module>.so
         android.x86_64
         abi.json
         lib<module>.so
      module.json
   prefab.json
<name>-<version>.aar
pom.xml
*/
std::unordered_map<std::string, std::string> version_map;
std::error_code error_code;
std::unordered_map<std::string, std::set<PackageSpec>> empty_package_dependencies;
//
for (const auto& action : export_plan)
{
const std::string name = action.spec.name();
auto dependencies = action.dependencies(default_triplet);
const auto build_info = Build::read_build_info(utils, paths.build_info_file_path(action.spec));
const bool is_empty_package = build_info.policies.is_enabled(Build::BuildPolicy::EMPTY_PACKAGE);
if(is_empty_package){
empty_package_dependencies[name] = std::set<PackageSpec>();
for(auto dependency : dependencies){
if(empty_package_dependencies.find(dependency.name()) != empty_package_dependencies.end()){
auto& child_deps = empty_package_dependencies[name];
auto& parent_deps = empty_package_dependencies[dependency.name()];
for(auto parent_dep: parent_deps){
child_deps.insert(parent_dep);
}
}
else {
empty_package_dependencies[name].insert(dependency);
}
}
continue;
}
const fs::path per_package_dir_path = raw_exported_dir_path / name;
const auto& binary_paragraph = action.core_paragraph().value_or_exit(VCPKG_LINE_INFO);
const std::string norm_version = binary_paragraph.version;
version_map[name] = norm_version;
System::print2("\nExporting package ", name, "...\n");
fs::path package_directory = per_package_dir_path / "aar";
fs::path prefab_directory = package_directory / "prefab";
fs::path modules_directory = prefab_directory / "modules";
utils.create_directories(modules_directory, error_code);
std::string artifact_id = prefab_options.maybe_artifact_id.value_or(name);
std::string group_id = prefab_options.maybe_group_id.value_or("com.vcpkg.ndk.support");
std::string sdk_min_version = prefab_options.maybe_min_sdk.value_or("16");
std::string sdk_target_version = prefab_options.maybe_target_sdk.value_or("29");
std::string MANIFEST_TEMPLATE =
R"(<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="@GROUP_ID@.@ARTIFACT_ID@" android:versionCode="1" android:versionName="1.0">
<uses-sdk android:minSdkVersion="@MIN_SDK_VERSION@" android:targetSdkVersion="@SDK_TARGET_VERSION@" />
</manifest>)";
std::string manifest = Strings::replace_all(std::move(MANIFEST_TEMPLATE), "@GROUP_ID@", group_id);
manifest = Strings::replace_all(std::move(manifest), "@ARTIFACT_ID@", artifact_id);
manifest = Strings::replace_all(std::move(manifest), "@MIN_SDK_VERSION@", sdk_min_version);
manifest = Strings::replace_all(std::move(manifest), "@SDK_TARGET_VERSION@", sdk_target_version);
fs::path manifest_path = package_directory / "AndroidManifest.xml";
fs::path prefab_path = prefab_directory / "prefab.json";
fs::path meta_dir = package_directory / "META-INF";
utils.create_directories(meta_dir, error_code);
const fs::path share_root =
vcpkg_root_path / "packages" / Strings::format("%s_%s", name, action.spec.triplet());
utils.copy_file(share_root / "share" / name / "copyright",
meta_dir / "LICENSE",
fs::copy_options::overwrite_existing,
error_code);
PackageMetadata pm;
pm.name = artifact_id;
pm.schema = 1;
pm.version = norm_version;
std::set<PackageSpec> dependencies_minus_empty_packages;
for(auto dependency: dependencies){
if(empty_package_dependencies.find(dependency.name()) != empty_package_dependencies.end()){
for(auto& empty_package_dep: empty_package_dependencies[dependency.name()]){
dependencies_minus_empty_packages.insert(empty_package_dep);
}
}
else {
dependencies_minus_empty_packages.insert(dependency);
}
}
std::vector<std::string> pom_dependencies;
if (dependencies_minus_empty_packages.size() > 0)
{
pom_dependencies.push_back("\n<dependencies>");
}
for (const auto& it : dependencies_minus_empty_packages)
{
std::string maven_pom = R"( <dependency>
<groupId>@GROUP_ID@</groupId>
<artifactId>@ARTIFACT_ID@</artifactId>
<version>@VERSION@</version>
<type>aar</type>
<scope>runtime</scope>
</dependency>)";
std::string pom = Strings::replace_all(std::move(maven_pom), "@GROUP_ID@", group_id);
pom = Strings::replace_all(std::move(pom), "@ARTIFACT_ID@", it.name());
pom = Strings::replace_all(std::move(pom), "@VERSION@", version_map[it.name()]);
pom_dependencies.push_back(pom);
pm.dependencies.push_back(it.name());
}
if (dependencies_minus_empty_packages.size() > 0)
{
pom_dependencies.push_back("</dependencies>\n");
}
if(prefab_options.enable_debug){
System::print2(Strings::format(
"[DEBUG]\n\tWriting manifest\n\tTo %s\n\tWriting prefab meta data\n\tTo %s\n\n",
manifest_path.generic_u8string(), prefab_path.generic_u8string()));
}
utils.write_contents(manifest_path, manifest, VCPKG_LINE_INFO);
utils.write_contents(prefab_path, pm.to_json(), VCPKG_LINE_INFO);
if(prefab_options.enable_debug){
std::vector<std::string> triplet_names;
for(auto triplet: triplets){
triplet_names.push_back(triplet.canonical_name());
}
System::print2(Strings::format("[DEBUG] Found %d triplets\n\t%s\n\n", triplets.size(),
Strings::join("\n\t", triplet_names)));
}
for (const auto& triplet : triplets)
{
const fs::path listfile = vcpkg_root_path / "installed" / "vcpkg" / "info" /
(Strings::format("%s_%s_%s", name, norm_version, triplet) + ".list");
const fs::path installed_dir = vcpkg_root_path / "packages" / Strings::format("%s_%s", name, triplet);
Checks::check_exit(VCPKG_LINE_INFO,
utils.exists(listfile),
"Error: Packages not installed %s:%s %s",
name,
triplet,
listfile.generic_u8string());
fs::path libs = installed_dir / "lib";
std::vector<fs::path> modules;
std::vector<fs::path> modules_shared = find_modules(paths, libs, ".so");
for (const auto& module : modules_shared)
{
modules.push_back(module);
}
std::vector<fs::path> modules_static = find_modules(paths, libs, ".a");
for (const auto& module : modules_static)
{
modules.push_back(module);
}
// header only libs
if (modules.empty())
{
fs::path module_dir = modules_directory / name;
fs::path module_libs_dir = module_dir / "libs";
utils.create_directories(module_libs_dir, error_code);
fs::path installed_headers_dir = installed_dir / "include";
fs::path exported_headers_dir = module_dir / "include";
ModuleMetadata meta;
fs::path module_meta_path = module_dir / "module.json";
utils.write_contents(module_meta_path, meta.to_json(), VCPKG_LINE_INFO);
utils.copy(installed_headers_dir, exported_headers_dir, fs::copy_options::recursive);
break;
}
else
{
for (const auto& module : modules)
{
std::string module_name = module.stem().generic_u8string();
std::string extension = module.extension().generic_u8string();
ABIMetadata ab;
ab.abi = triplet_abi_map[triplet];
ab.api = triplet_api_map[triplet];
ab.stl = Strings::contains(extension, "a") ?"c++_static": "c++_shared";
ab.ndk = version.major();
if(prefab_options.enable_debug){
System::print2(Strings::format("[DEBUG] Found module %s:%s\n", module_name, ab.abi));
}
module_name = Strings::trim(std::move(module_name));
if (Strings::starts_with(module_name, "lib"))
{
module_name = module_name.substr(3);
}
fs::path module_dir = (modules_directory / module_name);
fs::path module_libs_dir =
module_dir / "libs" / Strings::format("android.%s", ab.abi);
utils.create_directories(module_libs_dir, error_code);
fs::path abi_path = module_libs_dir / "abi.json";
if(prefab_options.enable_debug){
System::print2(Strings::format("\tWriting abi metadata\n\tTo %s\n",
abi_path.generic_u8string()));
}
utils.write_contents(abi_path, ab.to_string(), VCPKG_LINE_INFO);
fs::path installed_module_path = libs / module.filename();
fs::path exported_module_path = module_libs_dir / module.filename();
utils.copy_file(installed_module_path,
exported_module_path,
fs::copy_options::overwrite_existing,
error_code);
if(prefab_options.enable_debug){
System::print2(Strings::format("\tCopying libs\n\tFrom %s\n\tTo %s\n",
installed_module_path.generic_u8string(), exported_module_path.generic_u8string()));
}
fs::path installed_headers_dir = installed_dir / "include";
fs::path exported_headers_dir = module_libs_dir / "include";
if(prefab_options.enable_debug){
System::print2(Strings::format("\tCopying headers\n\tFrom %s\n\tTo %s\n",
installed_headers_dir.generic_u8string(), exported_headers_dir.generic_u8string()));
}
utils.copy(installed_headers_dir, exported_headers_dir, fs::copy_options::recursive);
ModuleMetadata meta;
fs::path module_meta_path = module_dir / "module.json";
if(prefab_options.enable_debug){
System::print2(Strings::format("\tWriting module metadata\n\tTo %s\n\n",
module_meta_path.generic_u8string()));
}
utils.write_contents(module_meta_path, meta.to_json(), VCPKG_LINE_INFO);
}
}
}
fs::path exported_archive_path = per_package_dir_path / Strings::format("%s-%s.aar", name, norm_version);
fs::path pom_path = per_package_dir_path / "pom.xml";
if(prefab_options.enable_debug){
System::print2(Strings::format("[DEBUG] Exporting AAR And POM\n\tAAR Path %s\n\tPOM Path %s\n",
exported_archive_path.generic_u8string(), pom_path.generic_u8string()));
}
compress_directory(paths, package_directory, exported_archive_path);
std::string POM = R"(<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>@GROUP_ID@</groupId>
<artifactId>@ARTIFACT_ID@</artifactId>
<version>@VERSION@</version>
<packaging>aar</packaging>
<description>The Vcpkg AAR for @ARTIFACT_ID@</description>
<url>https://github.com/microsoft/vcpkg.git</url>
@DEPENDENCIES@
</project>)";
std::string pom = Strings::replace_all(std::move(POM), "@GROUP_ID@", group_id);
pom = Strings::replace_all(std::move(pom), "@ARTIFACT_ID@", artifact_id);
pom = Strings::replace_all(std::move(pom), "@DEPENDENCIES@", Strings::join("\n", pom_dependencies));
pom = Strings::replace_all(std::move(pom), "@VERSION@", norm_version);
utils.write_contents(pom_path, pom, VCPKG_LINE_INFO);
if (prefab_options.enable_maven)
{
maven_install(exported_archive_path, pom_path, prefab_options);
if(prefab_options.enable_debug){
System::print2(
Strings::format("\n\n[DEBUG] Configuration properties in Android Studio\nIn app/build.gradle\n\n\t%s:%s:%s\n\n",
group_id, artifact_id, norm_version));
System::print2(R"(And cmake flags
externalNativeBuild {
cmake {
arguments '-DANDROID_STL=c++_shared'
cppFlags "-std=c++17"
}
}
)");
System::print2(R"(In gradle.properties
android.enablePrefab=true
android.enableParallelJsonGen=false
android.prefabVersion=${prefab.version}
)");}
}
System::print2(System::Color::success,
Strings::format("Successfuly exported %s. Checkout %s \n",
name,
raw_exported_dir_path.generic_u8string()));
}
}
}

View file

@ -39,6 +39,12 @@ namespace vcpkg
const Triplet Triplet::ARM_UWP = from_canonical_name("arm-uwp");
const Triplet Triplet::ARM64_UWP = from_canonical_name("arm64-uwp");
//
const Triplet Triplet::ARM_ANDROID = from_canonical_name("arm-android");
const Triplet Triplet::ARM64_ANDROID = from_canonical_name("arm64-android");
const Triplet Triplet::X86_ANDROID = from_canonical_name("x86-android");
const Triplet Triplet::X64_ANDROID = from_canonical_name("x64-android");
Triplet Triplet::from_canonical_name(std::string&& triplet_as_string)
{
std::string s(Strings::ascii_to_lowercase(std::move(triplet_as_string)));
@ -55,19 +61,19 @@ namespace vcpkg
Optional<System::CPUArchitecture> Triplet::guess_architecture() const noexcept
{
using System::CPUArchitecture;
if (*this == X86_WINDOWS || *this == X86_UWP)
if (*this == X86_WINDOWS || *this == X86_UWP || *this == X86_ANDROID)
{
return CPUArchitecture::X86;
}
else if (*this == X64_WINDOWS || *this == X64_UWP)
else if (*this == X64_WINDOWS || *this == X64_UWP || *this ==X64_ANDROID)
{
return CPUArchitecture::X64;
}
else if (*this == ARM_WINDOWS || *this == ARM_UWP)
else if (*this == ARM_WINDOWS || *this == ARM_UWP || *this == ARM_ANDROID)
{
return CPUArchitecture::ARM;
}
else if (*this == ARM64_WINDOWS || *this == ARM64_UWP)
else if (*this == ARM64_WINDOWS || *this == ARM64_UWP || *this == ARM64_ANDROID)
{
return CPUArchitecture::ARM64;
}

View file

@ -179,6 +179,7 @@
<ClInclude Include="..\include\vcpkg\export.chocolatey.h" />
<ClInclude Include="..\include\vcpkg\export.h" />
<ClInclude Include="..\include\vcpkg\export.ifw.h" />
<ClInclude Include="..\include\vcpkg\export.prefab.h" />
<ClInclude Include="..\include\vcpkg\globalstate.h" />
<ClInclude Include="..\include\vcpkg\help.h" />
<ClInclude Include="..\include\vcpkg\input.h" />
@ -258,6 +259,7 @@
<ClCompile Include="..\src\vcpkg\dependencies.cpp" />
<ClCompile Include="..\src\vcpkg\export.cpp" />
<ClCompile Include="..\src\vcpkg\export.chocolatey.cpp" />
<ClCompile Include="..\src\vcpkg\export.prefab.cpp" />
<ClCompile Include="..\src\vcpkg\globalstate.cpp" />
<ClCompile Include="..\src\vcpkg\help.cpp" />
<ClCompile Include="..\src\vcpkg\input.cpp" />