--- /dev/null
+#include <msp/fs/utils.h>
+#include "androidapplicationcomponent.h"
+#include "androidmanifestfile.h"
+#include "androidresourcefile.h"
+#include "builder.h"
+#include "installedfile.h"
+#include "sharedlibrary.h"
+#include "sourcepackage.h"
+#include "tool.h"
+#include "toolchain.h"
+
+using namespace std;
+using namespace Msp;
+
+AndroidApplicationComponent::AndroidApplicationComponent(SourcePackage &p, const string &n):
+ Component(p, n)
+{ }
+
+void AndroidApplicationComponent::create_targets() const
+{
+ Builder &builder = package.get_builder();
+ BuildGraph &build_graph = builder.get_build_graph();
+
+ const BuildGraph::TargetMap &targets = build_graph.get_targets();
+ list<Target *> contents;
+ for(BuildGraph::TargetMap::const_iterator i=targets.begin(); i!=targets.end(); ++i)
+ if(i->second->get_package()==&package)
+ if(InstalledFile *inst = dynamic_cast<InstalledFile *>(i->second))
+ contents.push_back(inst->get_real_target());
+
+ AndroidManifestFile *manifest = new AndroidManifestFile(builder, *this);
+
+ list<Target *> resource_sources;
+ resource_sources.push_back(manifest);
+
+ const Toolchain &toolchain = builder.get_toolchain();
+ Tool © = toolchain.get_tool("CP");
+ SourceList source_filenames = collect_source_files();
+ for(SourceList::const_iterator i=source_filenames.begin(); i!=source_filenames.end(); ++i)
+ {
+ Target *tgt = new AndroidResourceFile(builder, *this, *i);
+ resource_sources.push_back(copy.create_target(*tgt, "//"));
+ }
+
+ Tool &aapt = toolchain.get_tool("AAPT");
+ Target *resource_bundle = aapt.create_target(resource_sources);
+
+ list<Target *> apk_sources;
+ apk_sources.push_back(resource_bundle);
+
+ const Architecture &arch = package.get_builder().get_current_arch();
+ string lib_dir = "//"+name+"/lib/";
+ if(arch.get_type()=="arm")
+ {
+ lib_dir += "armeabi";
+ if(arch.get_cpu()=="armv7a")
+ lib_dir += "-v7a";
+ }
+ else
+ lib_dir += arch.get_type();
+
+ string assets_dir = "//"+name+"/assets";
+ for(list<Target *>::const_iterator i=contents.begin(); i!=contents.end(); ++i)
+ {
+ Target *staged = 0;
+ if(SharedLibrary *shlib = dynamic_cast<SharedLibrary *>(*i))
+ {
+ manifest->set_native_library(shlib);
+ staged = copy.create_target(**i, lib_dir);
+ }
+ else
+ staged = copy.create_target(**i, assets_dir);
+ apk_sources.push_back(staged);
+ }
+
+ Tool &apk_builder = toolchain.get_tool("APK");
+ Target *apk = apk_builder.create_target(apk_sources);
+ builder.get_build_graph().add_primary_target(*apk);
+}
--- /dev/null
+#ifndef ANDROIDAPPLICATIONCOMPONENT_H_
+#define ANDROIDAPPLICATIONCOMPONENT_H_
+
+#include "component.h"
+
+class AndroidApplicationComponent: public Component
+{
+public:
+ AndroidApplicationComponent(SourcePackage &, const std::string &);
+
+ virtual void create_targets() const;
+};
+
+#endif
--- /dev/null
+#include <msp/fs/utils.h>
+#include "androidassetpackagingtool.h"
+#include "androidmanifestfile.h"
+#include "androidresourcebundle.h"
+#include "androidtools.h"
+#include "component.h"
+#include "externaltask.h"
+#include "sourcepackage.h"
+
+using namespace std;
+using namespace Msp;
+
+AndroidAssetPackagingTool::AndroidAssetPackagingTool(Builder &b, const AndroidSdk &s):
+ Tool(b, "AAPT"),
+ sdk(s)
+{
+ set_command((sdk.get_build_tools_dir()/"aapt").str());
+}
+
+Target *AndroidAssetPackagingTool::create_target(const list<Target *> &sources, const string &)
+{
+ AndroidManifestFile *manifest = 0;
+ list<FileTarget *> resources;
+ for(list<Target *>::const_iterator i=sources.begin(); i!=sources.end(); ++i)
+ {
+ if(AndroidManifestFile *m = dynamic_cast<AndroidManifestFile *>(*i))
+ manifest = m;
+ else if(FileTarget *f = dynamic_cast<FileTarget *>(*i))
+ resources.push_back(f);
+ }
+
+ if(!manifest)
+ throw invalid_argument("AndroidAssetPackagingTool::create_target");
+
+ AndroidResourceBundle *res = new AndroidResourceBundle(builder, *manifest->get_component(), *manifest, resources);
+ res->set_tool(*this);
+ return res;
+}
+
+Task *AndroidAssetPackagingTool::run(const Target &tgt) const
+{
+ const AndroidResourceBundle &res = dynamic_cast<const AndroidResourceBundle &>(tgt);
+
+ ExternalTask::Arguments argv;
+ argv.push_back(executable->get_path().str());
+ argv.push_back("package");
+
+ FS::Path work_dir = res.get_component()->get_package().get_source_directory();
+
+ argv.push_back("-I");
+ argv.push_back(sdk.get_platform_jar().str());
+
+ argv.push_back("-F");
+ argv.push_back(FS::relative(res.get_path(), work_dir).str());
+
+ const Target::Dependencies &depends = res.get_dependencies();
+ list<FS::Path> resource_dirs;
+ for(Target::Dependencies::const_iterator i=depends.begin(); i!=depends.end(); ++i)
+ {
+ FileTarget *file = dynamic_cast<FileTarget *>(*i);
+ Target *real = (*i)->get_real_target();
+
+ if(dynamic_cast<AndroidManifestFile *>(real))
+ {
+ argv.push_back("-M");
+ argv.push_back(FS::relative(file->get_path(), work_dir).str());
+ }
+ else if(real->get_package()==res.get_package())
+ {
+ const FS::Path &path = file->get_path();
+ resource_dirs.push_back(path.subpath(0, path.size()-2));
+ }
+ }
+
+ set<string> seen_dirs;
+ for(list<FS::Path>::const_iterator i=resource_dirs.begin(); i!=resource_dirs.end(); ++i)
+ if(seen_dirs.insert(i->str()).second)
+ {
+ argv.push_back("-S");
+ argv.push_back(FS::relative(*i, work_dir).str());
+ }
+
+ return new ExternalTask(argv, work_dir);
+}
--- /dev/null
+#ifndef ANDROIDASSETPACKAGINGTOOL_H_
+#define ANDROIDASSETPACKAGINGTOOL_H_
+
+#include "tool.h"
+
+class AndroidSdk;
+
+class AndroidAssetPackagingTool: public Tool
+{
+private:
+ const AndroidSdk &sdk;
+
+public:
+ AndroidAssetPackagingTool(Builder &, const AndroidSdk &);
+
+ virtual Target *create_target(const std::list<Target *> &, const std::string &);
+ virtual Task *run(const Target &) const;
+};
+
+#endif
--- /dev/null
+#include "androidapplicationcomponent.h"
+#include "androidmanifestfile.h"
+#include "builder.h"
+#include "sourcepackage.h"
+
+AndroidManifestFile::AndroidManifestFile(Builder &b, const AndroidApplicationComponent &a):
+ FileTarget(b, a.get_package(), a.get_package().get_temp_directory()/a.get_name()/"AndroidManifest.xml"),
+ native_lib(0)
+{
+ component = &a;
+ tool = &builder.get_toolchain().get_tool("AMG");
+
+ add_dependency(package->get_build_file());
+}
+
+void AndroidManifestFile::set_native_library(SharedLibrary *lib)
+{
+ native_lib = lib;
+}
--- /dev/null
+#ifndef ANDROIDMANIFESTFILE_H_
+#define ANDROIDMANIFESTFILE_H_
+
+#include "filetarget.h"
+
+class AndroidApplicationComponent;
+class SharedLibrary;
+
+/**
+Metadata file for an Android application.
+*/
+class AndroidManifestFile: public FileTarget
+{
+private:
+ SharedLibrary *native_lib;
+
+public:
+ AndroidManifestFile(Builder &, const AndroidApplicationComponent &);
+
+ virtual const char *get_type() const { return "AndroidManifestFile"; }
+
+ void set_native_library(SharedLibrary *);
+ SharedLibrary *get_native_library() const { return native_lib; }
+};
+
+#endif
--- /dev/null
+#include <msp/io/file.h>
+#include <msp/io/print.h>
+#include "androidmanifestfile.h"
+#include "androidmanifestgenerator.h"
+#include "component.h"
+#include "sharedlibrary.h"
+#include "sourcepackage.h"
+
+using namespace std;
+using namespace Msp;
+
+AndroidManifestGenerator::AndroidManifestGenerator(Builder &b):
+ Tool(b, "AMG")
+{ }
+
+Target *AndroidManifestGenerator::create_target(const list<Target *> &, const string &)
+{
+ throw logic_error("not implemented");
+}
+
+Task *AndroidManifestGenerator::run(const Target &target) const
+{
+ const AndroidManifestFile &manifest = dynamic_cast<const AndroidManifestFile &>(target);
+ Worker *worker = new Worker(manifest);
+ return new InternalTask(worker);
+}
+
+
+AndroidManifestGenerator::Worker::Worker(const AndroidManifestFile &m):
+ manifest(m)
+{ }
+
+void AndroidManifestGenerator::Worker::main()
+{
+ const Component &comp = *manifest.get_component();
+ const SourcePackage &pkg = comp.get_package();
+
+ IO::BufferedFile out(manifest.get_path().str(), IO::M_WRITE);
+ out.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
+ IO::print(out, "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\" package=\"%s\">\n", comp.get_name());
+ out.write("\t<uses-sdk android:minSdkVersion=\"9\" />\n");
+ // TODO Make the icon name configurable
+ IO::print(out, "\t<application android:icon=\"@drawable/icon\" android:label=\"%s\" android:hasCode=\"false\">\n", pkg.get_label());
+ if(SharedLibrary *native_lib = manifest.get_native_library())
+ {
+ out.write("\t\t<activity android:name=\"android.app.NativeActivity\">\n");
+ IO::print(out, "\t\t\t<meta-data android:name=\"android.app.lib_name\" android:value=\"%s\" />\n", native_lib->get_libname());
+ out.write("\t\t\t<intent-filter>\n");
+ out.write("\t\t\t\t<action android:name=\"android.intent.action.MAIN\" />\n");
+ out.write("\t\t\t\t<category android:name=\"android.intent.category.LAUNCHER\" />\n");
+ out.write("\t\t\t</intent-filter>\n");
+ out.write("\t\t</activity>\n");
+ }
+ out.write("\t</application>\n");
+ out.write("</manifest>\n");
+
+ status = Task::SUCCESS;
+}
--- /dev/null
+#ifndef ANDROIDMANIFESTGENERATOR_H_
+#define ANDROIDMANIFESTGENERATOR_H_
+
+#include "internaltask.h"
+#include "tool.h"
+
+class AndroidManifestFile;
+
+class AndroidManifestGenerator: public Tool
+{
+private:
+ class Worker: public InternalTask::Worker
+ {
+ private:
+ const AndroidManifestFile &manifest;
+
+ public:
+ Worker(const AndroidManifestFile &);
+
+ private:
+ virtual void main();
+ };
+
+public:
+ AndroidManifestGenerator(Builder &);
+
+ virtual Target *create_target(const std::list<Target *> &, const std::string &);
+ virtual Task *run(const Target &) const;
+};
+
+#endif
--- /dev/null
+#include "androidpackagefile.h"
+#include "androidresourcebundle.h"
+#include "component.h"
+#include "sourcepackage.h"
+
+using namespace std;
+
+AndroidPackageFile::AndroidPackageFile(Builder &b, const Component &c, AndroidResourceBundle &resource_bundle, const list<FileTarget *> &other_files):
+ FileTarget(b, c.get_package(), c.get_package().get_output_directory()/(c.get_name()+".apk"))
+{
+ component = &c;
+
+ add_dependency(resource_bundle);
+ for(list<FileTarget *>::const_iterator i=other_files.begin(); i!=other_files.end(); ++i)
+ add_dependency(**i);
+}
--- /dev/null
+#ifndef ANDROIDPACKAGEFILE_H_
+#define ANDROIDPACKAGEFILE_H_
+
+#include "filetarget.h"
+
+class AndroidResourceBundle;
+
+class AndroidPackageFile: public FileTarget
+{
+public:
+ AndroidPackageFile(Builder &, const Component &, AndroidResourceBundle &, const std::list<FileTarget *> &);
+
+ virtual const char *get_type() const { return "AndroidPackageFile"; }
+};
+
+#endif
--- /dev/null
+#include "androidmanifestfile.h"
+#include "androidresourcebundle.h"
+#include "component.h"
+#include "sourcepackage.h"
+
+using namespace std;
+
+AndroidResourceBundle::AndroidResourceBundle(Builder &b, const Component &c, AndroidManifestFile &manifest, const list<FileTarget *> &resources):
+ FileTarget(b, c.get_package(), c.get_package().get_temp_directory()/c.get_name()/(c.get_name()+".ap_"))
+{
+ component = &c;
+
+ add_dependency(manifest);
+ for(list<FileTarget *>::const_iterator i=resources.begin(); i!=resources.end(); ++i)
+ add_dependency(**i);
+}
--- /dev/null
+#ifndef ANDROIDRESOURCEBUNDLE_H_
+#define ANDROIDRESOURCEBUNDLE_H_
+
+#include "filetarget.h"
+
+class AndroidManifestFile;
+
+class AndroidResourceBundle: public FileTarget
+{
+public:
+ AndroidResourceBundle(Builder &, const Component &, AndroidManifestFile &, const std::list<FileTarget *> &);
+
+ virtual const char *get_type() const { return "AndroidResourceBundle"; }
+};
+
+#endif
--- /dev/null
+#include <msp/fs/utils.h>
+#include "androidresourcefile.h"
+#include "component.h"
+
+using namespace std;
+using namespace Msp;
+
+AndroidResourceFile::AndroidResourceFile(Builder &b, const Component &c, const FS::Path &p):
+ FileTarget(b, c.get_package(), p)
+{
+ string ext = FS::extpart(FS::basename(p));
+ if(ext==".png" || ext==".jpg")
+ resource_type = "drawable";
+ // TODO recognize various xml files; must inspect contents
+ else
+ resource_type = "raw";
+
+ install_location = "res/"+resource_type;
+}
--- /dev/null
+#ifndef ANDROIDRESOURCEFILE_H_
+#define ANDROIDRESOURCEFILE_H_
+
+#include "filetarget.h"
+
+/**
+A file destined to be used as a resource in an Android application.
+*/
+class AndroidResourceFile: public FileTarget
+{
+private:
+ std::string resource_type;
+
+public:
+ AndroidResourceFile(Builder &, const Component &, const Msp::FS::Path &);
+
+ virtual const char *get_type() const { return "AndroidResourceFile"; }
+};
+
+#endif
#include <cstdlib>
#include <msp/fs/dir.h>
+#include <msp/strings/format.h>
#include <msp/strings/lexicalcast.h>
#include <msp/strings/utils.h>
+#include "androidassetpackagingtool.h"
#include "androidccompiler.h"
#include "androidcxxcompiler.h"
#include "androidlinker.h"
+#include "androidmanifestgenerator.h"
#include "androidtools.h"
+#include "apkbuilder.h"
#include "architecture.h"
#include "builder.h"
+#include "jarsigner.h"
using namespace std;
using namespace Msp;
-AndroidTools::AndroidTools(Builder &builder, const Architecture &arch):
- ndk(builder, arch)
+// TODO Mark problems somewhere instead of throwing exceptions
+
+unsigned parse_version(const std::string &version_str)
{
- add_tool(new AndroidCCompiler(builder, arch, ndk));
- add_tool(new AndroidCxxCompiler(builder, arch, ndk));
- add_tool(new AndroidLinker(builder, arch, ndk));
+ vector<string> version_parts = split(version_str, '.');
+ unsigned version = lexical_cast<unsigned>(version_parts[0])<<16;
+ if(version_parts.size()>1)
+ version += lexical_cast<unsigned>(version_parts[1])<<8;
+ if(version_parts.size()>2)
+ version += lexical_cast<unsigned>(version_parts[2]);
+ return version;
}
-AndroidNdk::AndroidNdk(Builder &b, const Architecture &a):
- builder(b),
- architecture(a)
+AndroidDevKit::AndroidDevKit(Builder &b, const string &type):
+ builder(b)
{
- if(const char *env_ndk_root = getenv("ANDROID_NDK_ROOT"))
- ndk_root = env_ndk_root;
+ string var = format("ANDROID_%s_ROOT", type);
+ if(const char *value = getenv(var.c_str()))
+ root = value;
else
- throw runtime_error("ANDROID_NDK_ROOT must be set");
+ throw runtime_error(var+" must be set");
+
+ FS::Path platforms_dir = root/"platforms";
+ builder.get_logger().log("files", format("Traversing %s", platforms_dir.str()));
+ list<string> platforms = list_filtered(platforms_dir, "^android-[1-9][0-9]*$");
+
+ for(list<string>::const_iterator i=platforms.begin(); i!=platforms.end(); ++i)
+ supported_api_levels.insert(lexical_cast<unsigned>(i->substr(8)));
+}
+
+void AndroidDevKit::select_api_level(unsigned api)
+{
+ if(!supported_api_levels.count(api))
+ throw invalid_argument("AndroidDevKit::select_api_level");
+
+ init_api_level(api);
+}
+
+
+AndroidSdk::AndroidSdk(Builder &b):
+ AndroidDevKit(b, "SDK")
+{
+ find_build_tools_dir();
+}
+
+void AndroidSdk::find_build_tools_dir()
+{
+ FS::Path bt_dir = root/"build-tools";
+ builder.get_logger().log("files", format("Traversing %s", bt_dir.str()));
+ list<string> tool_versions = list_files(bt_dir);
+
+ string use_tools;
+ unsigned latest_version = 0;
+ for(list<string>::const_iterator i=tool_versions.begin(); i!=tool_versions.end(); ++i)
+ {
+ unsigned version = parse_version(*i);
+ if(version>latest_version)
+ {
+ use_tools = *i;
+ latest_version = version;
+ }
+ }
+
+ if(use_tools.empty())
+ throw runtime_error("No build tools found");
+
+ build_tools_dir = bt_dir/use_tools;
+ builder.get_logger().log("tools", format("Android build tools are in %s", build_tools_dir.str()));
+}
+
+void AndroidSdk::init_api_level(unsigned api)
+{
+ if(!supported_api_levels.count(api))
+ throw invalid_argument("AndroidSdk::select_api_level");
+
+ platform_jar = root/"platforms"/format("android-%d", api)/"android.jar";
+}
+
+AndroidNdk::AndroidNdk(Builder &b, const Architecture &a):
+ AndroidDevKit(b, "NDK"),
+ architecture(a)
+{
find_toolchain_dir();
- find_platform_dir();
}
void AndroidNdk::find_toolchain_dir()
{
- FS::Path toolchains_dir = ndk_root/"toolchains";
+ FS::Path toolchains_dir = root/"toolchains";
builder.get_logger().log("files", format("Traversing %s", toolchains_dir.str()));
- list<string> toolchains = list_files(toolchains_dir);
-
string prefix = architecture.get_cross_prefix()+"-";
+ list<string> toolchains = list_filtered(toolchains_dir, "^"+prefix);
+
string use_toolchain;
unsigned latest_version = 0;
for(list<string>::const_iterator i=toolchains.begin(); i!=toolchains.end(); ++i)
- if(!i->compare(0, prefix.size(), prefix))
+ {
+ string version_str = i->substr(prefix.size());
+ string compiler = "gcc";
+ if(!isdigit(version_str[0]))
+ {
+ unsigned j;
+ for(j=1; (j<version_str.size() && !isdigit(version_str[j])); ++j) ;
+ compiler = version_str.substr(0, j);
+ version_str = version_str.substr(j);
+ }
+
+ if(compiler!="gcc")
+ continue;
+
+ unsigned version = parse_version(version_str);
+ if(version>latest_version)
{
- string version_str = i->substr(prefix.size());
- string compiler = "gcc";
- if(!isdigit(version_str[0]))
- {
- unsigned j;
- for(j=1; (j<version_str.size() && !isdigit(version_str[j])); ++j) ;
- compiler = version_str.substr(0, j);
- version_str = version_str.substr(j);
- }
-
- if(compiler!="gcc")
- continue;
-
- vector<string> version_parts = split(version_str, '.');
- unsigned version = (lexical_cast<unsigned>(version_parts[0])<<8);
- if(version_parts.size()>1)
- version += lexical_cast<unsigned>(version_parts[1]);
-
- if(version>latest_version)
- {
- use_toolchain = *i;
- latest_version = version;
- }
+ use_toolchain = *i;
+ latest_version = version;
}
+ }
if(use_toolchain.empty())
throw runtime_error("No applicable toolchains found");
builder.get_logger().log("tools", format("Android toolchain binaries are in %s", bin_dir.str()));
}
-void AndroidNdk::find_platform_dir()
+void AndroidNdk::init_api_level(unsigned api)
{
- FS::Path platforms_dir = ndk_root/"platforms";
- builder.get_logger().log("files", format("Traversing %s", platforms_dir.str()));
- list<string> platforms = list_files(platforms_dir);
+ if(!supported_api_levels.count(api))
+ throw invalid_argument("AndroidSdk::select_api_level");
- string use_platform;
- unsigned highest_level = 0;
- for(list<string>::const_iterator i=platforms.begin(); i!=platforms.end(); ++i)
- if(!i->compare(0, 8, "android-"))
- {
- unsigned level = lexical_cast<unsigned>(i->substr(8));
- if(level>highest_level)
- {
- use_platform = *i;
- highest_level = level;
- }
- }
-
- if(use_platform.empty())
- throw runtime_error("No applicable platforms found");
-
- FS::Path platform_archs_dir = platforms_dir/use_platform;
+ FS::Path platform_archs_dir = root/"platforms"/format("android-%d", api);
builder.get_logger().log("files", format("Traversing %s", platform_archs_dir.str()));
list<string> platform_archs = list_filtered(platform_archs_dir, "^arch-");
for(list<string>::iterator i=platform_archs.begin(); i!=platform_archs.end(); ++i)
if(use_arch.empty())
throw runtime_error("No matching platform found");
- platform_sysroot = platforms_dir/use_platform/use_arch;
- builder.get_logger().log("tools", format("Android platform sysroot is %s", platform_sysroot.str()));
+ platform_sysroot = platform_archs_dir/("arch-"+use_arch);
+ builder.get_logger().log("tools", format("Android NDK sysroot is %s", platform_sysroot.str()));
+}
+
+
+AndroidTools::AndroidTools(Builder &builder, const Architecture &arch):
+ sdk(builder),
+ ndk(builder, arch)
+{
+ const set<unsigned> &sdk_api_levels = sdk.get_supported_api_levels();
+ const set<unsigned> &ndk_api_levels = ndk.get_supported_api_levels();
+ unsigned highest_common = 0;
+ for(set<unsigned>::const_reverse_iterator i=sdk_api_levels.rbegin(); (!highest_common && i!=sdk_api_levels.rend()); ++i)
+ if(ndk_api_levels.count(*i))
+ highest_common = *i;
+
+ if(!highest_common)
+ throw runtime_error("No usable Android platforms found");
+
+ sdk.select_api_level(highest_common);
+ ndk.select_api_level(highest_common);
+
+ add_tool(new AndroidCCompiler(builder, arch, ndk));
+ add_tool(new AndroidCxxCompiler(builder, arch, ndk));
+ add_tool(new AndroidLinker(builder, arch, ndk));
+ add_tool(new AndroidManifestGenerator(builder));
+ add_tool(new AndroidAssetPackagingTool(builder, sdk));
+ add_tool(new ApkBuilder(builder));
+ add_tool(new JarSigner(builder));
}
#ifndef ANDROIDTOOLS_H_
#define ANDROIDTOOLS_H_
+#include <set>
#include <msp/fs/path.h>
#include "toolchain.h"
class Architecture;
class Builder;
-class AndroidNdk
+class AndroidDevKit
{
-private:
+protected:
Builder &builder;
+ Msp::FS::Path root;
+ std::set<unsigned> supported_api_levels;
+
+ AndroidDevKit(Builder &, const std::string &);
+ ~AndroidDevKit() { }
+
+public:
+ const Msp::FS::Path &get_root_dir() const { return root; }
+ const std::set<unsigned> &get_supported_api_levels() const { return supported_api_levels; }
+ void select_api_level(unsigned);
+protected:
+ virtual void init_api_level(unsigned) = 0;
+};
+
+class AndroidSdk: public AndroidDevKit
+{
+private:
+ Msp::FS::Path build_tools_dir;
+ // TODO use a FileTarget for the jar
+ Msp::FS::Path platform_jar;
+
+public:
+ AndroidSdk(Builder &);
+
+private:
+ void find_build_tools_dir();
+ virtual void init_api_level(unsigned);
+
+public:
+ const Msp::FS::Path &get_build_tools_dir() const { return build_tools_dir; }
+ const Msp::FS::Path &get_platform_jar() const { return platform_jar; }
+};
+
+class AndroidNdk: public AndroidDevKit
+{
+private:
const Architecture &architecture;
- Msp::FS::Path ndk_root;
Msp::FS::Path bin_dir;
Msp::FS::Path platform_sysroot;
private:
void find_toolchain_dir();
- void find_platform_dir();
+ virtual void init_api_level(unsigned);
public:
- const Msp::FS::Path &get_root_dir() const { return ndk_root; }
const Msp::FS::Path &get_bin_dir() const { return bin_dir; }
const Msp::FS::Path &get_platform_sysroot() const { return platform_sysroot; }
};
class AndroidTools: public Toolchain
{
private:
+ AndroidSdk sdk;
AndroidNdk ndk;
public:
--- /dev/null
+#include <msp/fs/utils.h>
+#include "androidpackagefile.h"
+#include "androidresourcebundle.h"
+#include "apkbuilder.h"
+#include "builder.h"
+#include "chainedtask.h"
+#include "component.h"
+#include "externaltask.h"
+#include "filetarget.h"
+#include "sourcepackage.h"
+
+using namespace std;
+using namespace Msp;
+
+// TODO Separate jar into its own tool and have this one just chain the two
+
+ApkBuilder::ApkBuilder(Builder &b):
+ Tool(b, "APK"),
+ jarsigner(0)
+{
+ set_command("jar");
+}
+
+Target *ApkBuilder::create_target(const list<Target *> &sources, const string &)
+{
+ AndroidResourceBundle *resource_bundle = 0;
+ list<FileTarget *> other_files;
+ for(list<Target *>::const_iterator i=sources.begin(); i!=sources.end(); ++i)
+ {
+ if(AndroidResourceBundle *r = dynamic_cast<AndroidResourceBundle *>(*i))
+ resource_bundle = r;
+ else if(FileTarget *f = dynamic_cast<FileTarget *>(*i))
+ other_files.push_back(f);
+ }
+ AndroidPackageFile *apk = new AndroidPackageFile(builder, *resource_bundle->get_component(), *resource_bundle, other_files);
+ apk->set_tool(*this);
+ return apk;
+}
+
+void ApkBuilder::do_prepare()
+{
+ jarsigner = &builder.get_toolchain().get_tool("JSGN");
+ jarsigner->prepare();
+}
+
+Task *ApkBuilder::run(const Target &tgt) const
+{
+ const AndroidPackageFile &apk = dynamic_cast<const AndroidPackageFile &>(tgt);
+
+ ExternalTask::Arguments argv;
+ argv.push_back(executable->get_path().str());
+ argv.push_back("u");
+
+ const Target::Dependencies &depends = apk.get_dependencies();
+ FS::Path input_path;
+ list<FS::Path> files;
+ for(Target::Dependencies::const_iterator i=depends.begin(); i!=depends.end(); ++i)
+ {
+ FileTarget *file = dynamic_cast<FileTarget *>(*i);
+ Target *real = (*i)->get_real_target();
+
+ if(dynamic_cast<AndroidResourceBundle *>(real))
+ input_path = file->get_path();
+ else if(real->get_package()==apk.get_package())
+ files.push_back(file->get_path());
+ }
+
+ FS::Path work_dir = FS::dirname(input_path);
+
+ for(list<FS::Path>::const_iterator i=files.begin(); i!=files.end(); ++i)
+ argv.push_back(FS::relative(*i, work_dir).str());
+
+ ExternalTask *task = new ExternalTask(argv, work_dir);
+ task->set_stdin(FS::basename(input_path));
+ task->set_stdout(FS::relative(apk.get_path(), work_dir));
+ ChainedTask *chain = new ChainedTask(task);
+ chain->add_task(jarsigner->run(tgt));
+ return chain;
+}
--- /dev/null
+#ifndef APKBUILDER_H_
+#define APKBUILDER_H_
+
+#include "tool.h"
+
+class ApkBuilder: public Tool
+{
+private:
+ Tool *jarsigner;
+
+public:
+ ApkBuilder(Builder &);
+
+ virtual Target *create_target(const std::list<Target *> &, const std::string &);
+protected:
+ virtual void do_prepare();
+public:
+ virtual Task *run(const Target &) const;
+};
+
+#endif
--- /dev/null
+#include <cstdlib>
+#include <msp/fs/utils.h>
+#include "component.h"
+#include "externaltask.h"
+#include "filetarget.h"
+#include "jarsigner.h"
+#include "sourcepackage.h"
+
+using namespace std;
+using namespace Msp;
+
+JarSigner::JarSigner(Builder &b):
+ Tool(b, "JSGN")
+{
+ set_command("jarsigner");
+}
+
+Target *JarSigner::create_target(const list<Target *> &, const string &)
+{
+ throw logic_error("not implemented");
+}
+
+Task *JarSigner::run(const Target &tgt) const
+{
+ const FileTarget &file = dynamic_cast<const FileTarget &>(tgt);
+
+ ExternalTask::Arguments argv;
+ argv.push_back(executable->get_path().str());
+
+ // TODO Make this generic
+ FS::Path home_dir = getenv("HOME");
+ argv.push_back("-keystore");
+ argv.push_back((home_dir/".android"/"debug.keystore").str());
+ argv.push_back("-storepass");
+ argv.push_back("android");
+
+ FS::Path work_dir = file.get_component()->get_package().get_source_directory();
+
+ argv.push_back(FS::relative(file.get_path(), work_dir).str());
+ argv.push_back("androiddebugkey");
+
+ return new ExternalTask(argv, work_dir);
+}
--- /dev/null
+#ifndef JARSIGNER_H_
+#define JARSIGNER_H_
+
+#include "tool.h"
+
+class JarSigner: public Tool
+{
+public:
+ JarSigner(Builder &);
+
+ virtual Target *create_target(const std::list<Target *> &, const std::string &);
+ virtual Task *run(const Target &) const;
+};
+
+#endif
#include <msp/io/print.h>
#include <msp/strings/lexicalcast.h>
#include <msp/strings/utils.h>
+#include "androidapplicationcomponent.h"
#include "binarycomponent.h"
#include "binarypackage.h"
#include "booleanevaluator.h"
void SourcePackage::Loader::init(const Config::InputOptions *o)
{
options = o;
+ add("android_application", &Loader::component<AndroidApplicationComponent>);
add("build_info", &Loader::build_info);
add("datapack", &Loader::component<DataPackComponent>);
add("description", &SourcePackage::description);