From 17a6cb587399b8650d331a85456336ccbc31e9fe Mon Sep 17 00:00:00 2001 From: Lars Bergstrom Date: Thu, 24 Sep 2015 13:33:55 -0500 Subject: New Android suppport --- support/android/apk/AndroidManifest.xml | 27 ++ support/android/apk/build.xml | 92 +++++++ support/android/apk/jni/Android.mk | 22 ++ support/android/apk/jni/Application.mk | 2 + support/android/apk/jni/main.c | 58 ++++ support/android/apk/project.properties | 1 + support/android/apk/res/mipmap/servo.png | Bin 0 -> 521100 bytes .../apk/src/com/mozilla/servo/MainActivity.java | 8 + support/android/build-apk/Cargo.lock | 4 + support/android/build-apk/Cargo.toml | 13 + support/android/build-apk/src/main.rs | 302 +++++++++++++++++++++ 11 files changed, 529 insertions(+) create mode 100644 support/android/apk/AndroidManifest.xml create mode 100644 support/android/apk/build.xml create mode 100644 support/android/apk/jni/Android.mk create mode 100644 support/android/apk/jni/Application.mk create mode 100644 support/android/apk/jni/main.c create mode 100644 support/android/apk/project.properties create mode 100644 support/android/apk/res/mipmap/servo.png create mode 100644 support/android/apk/src/com/mozilla/servo/MainActivity.java create mode 100644 support/android/build-apk/Cargo.lock create mode 100644 support/android/build-apk/Cargo.toml create mode 100644 support/android/build-apk/src/main.rs (limited to 'support/android') diff --git a/support/android/apk/AndroidManifest.xml b/support/android/apk/AndroidManifest.xml new file mode 100644 index 00000000000..5f3a9e1ea5e --- /dev/null +++ b/support/android/apk/AndroidManifest.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/support/android/apk/build.xml b/support/android/apk/build.xml new file mode 100644 index 00000000000..3d1611517a8 --- /dev/null +++ b/support/android/apk/build.xml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/support/android/apk/jni/Android.mk b/support/android/apk/jni/Android.mk new file mode 100644 index 00000000000..d922b052b4e --- /dev/null +++ b/support/android/apk/jni/Android.mk @@ -0,0 +1,22 @@ +# Copyright (C) 2010 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := servo +LOCAL_SRC_FILES := $(TARGET_ARCH_ABI)/libmain.so +include $(PREBUILT_SHARED_LIBRARY) + +# $(call import-module) diff --git a/support/android/apk/jni/Application.mk b/support/android/apk/jni/Application.mk new file mode 100644 index 00000000000..3784f3fc6c5 --- /dev/null +++ b/support/android/apk/jni/Application.mk @@ -0,0 +1,2 @@ +APP_ABI := armeabi +APP_PLATFORM := android-18 diff --git a/support/android/apk/jni/main.c b/support/android/apk/jni/main.c new file mode 100644 index 00000000000..beddf906059 --- /dev/null +++ b/support/android/apk/jni/main.c @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +//BEGIN_INCLUDE(all) +#include +#include +#include +#include + +#include +#include + +#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "servo-wrapper", __VA_ARGS__)) +#define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "servo-wrapper", __VA_ARGS__)) + +/** + * This is the main entry point of a native application that is using + * android_native_app_glue. It runs in its own thread, with its own + * event loop for receiving input events and doing other things. + */ +void android_main(struct android_app* state) { + LOGI("in android_main"); + void* libservo = dlopen("libservo.so", RTLD_NOW); + if (libservo == NULL) { + LOGI("failed to load servo lib: %s", dlerror()); + return; + } + + LOGI("loaded libservo.so"); + void (*android_main)(struct android_app*); + *(void**)(&android_main) = dlsym(libservo, "android_main"); + if (android_main) { + LOGI("go into android_main()"); + (*android_main)(state); + return; + } +} +//END_INCLUDE(all) + +int main(int argc, char* argv[]) +{ + LOGI("WAT"); + return 0; +} diff --git a/support/android/apk/project.properties b/support/android/apk/project.properties new file mode 100644 index 00000000000..c0442c37105 --- /dev/null +++ b/support/android/apk/project.properties @@ -0,0 +1 @@ +target=android-18 diff --git a/support/android/apk/res/mipmap/servo.png b/support/android/apk/res/mipmap/servo.png new file mode 100644 index 00000000000..5d5ef43bbc2 Binary files /dev/null and b/support/android/apk/res/mipmap/servo.png differ diff --git a/support/android/apk/src/com/mozilla/servo/MainActivity.java b/support/android/apk/src/com/mozilla/servo/MainActivity.java new file mode 100644 index 00000000000..d99478ee3da --- /dev/null +++ b/support/android/apk/src/com/mozilla/servo/MainActivity.java @@ -0,0 +1,8 @@ +package com.mozilla.servo; +import android.util.Log; + +public class MainActivity extends android.app.NativeActivity { + static { + Log.i("servo_wrapper", "Loading the NativeActivity"); + } +} diff --git a/support/android/build-apk/Cargo.lock b/support/android/build-apk/Cargo.lock new file mode 100644 index 00000000000..aa6f83fabec --- /dev/null +++ b/support/android/build-apk/Cargo.lock @@ -0,0 +1,4 @@ +[root] +name = "build-apk" +version = "0.0.1" + diff --git a/support/android/build-apk/Cargo.toml b/support/android/build-apk/Cargo.toml new file mode 100644 index 00000000000..23257facd3b --- /dev/null +++ b/support/android/build-apk/Cargo.toml @@ -0,0 +1,13 @@ +[package] + +name = "build-apk" +version = "0.0.1" +authors = ["Pierre Krieger ", "The Servo Project Developers"] + +[[bin]] +name = "build-apk" +path = "src/main.rs" +test = false +doc = false +bench = false + diff --git a/support/android/build-apk/src/main.rs b/support/android/build-apk/src/main.rs new file mode 100644 index 00000000000..460dd620da1 --- /dev/null +++ b/support/android/build-apk/src/main.rs @@ -0,0 +1,302 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//extern crate num; +#![feature(dir_builder, fs_walk)] +use std::collections::{HashMap, HashSet}; +use std::env; +use std::fs; +use std::fs::DirBuilder; +use std::path::{Path, PathBuf}; +use std::process; +use std::process::{Command, Stdio}; + +fn main() { + let (args, passthrough) = parse_arguments(); + + // Find all the native shared libraries that exist in the target directory. + let native_shared_libs = find_native_libs(&args); + + // Get the SDK path from the ANDROID_HOME env. + let sdk_path = env::var("ANDROID_HOME").ok().expect("Please set the ANDROID_HOME environment variable"); + let sdk_path = Path::new(&sdk_path); + + // Get the NDK path from NDK_HOME env. + let ndk_path = env::var("NDK_HOME").ok().expect("Please set the NDK_HOME environment variable"); + let ndk_path = Path::new(&ndk_path); + + // Get the standalone NDK path from NDK_STANDALONE env. + // let standalone_path = env::var("NDK_STANDALONE").ok().unwrap_or("/opt/ndk_standalone".to_string()); + // let standalone_path = Path::new(&standalone_path); + + let debug = passthrough.contains(&"-d".to_string()); + + // Set the build directory that will contain all the necessary files to create the apk + let directory = args.root_path.join("support").join("android").join("apk"); + let resdir = args.root_path.join("resources/"); + + // executing ndk-build + env::set_var("V", "1"); + if debug { + env::set_var("NDK_DEBUG", "1"); + env::set_var("APP_OPTIM", "0"); + } else{ + // Overrides android:debuggable propery in the .xml file. + env::set_var("APP_OPTIM", "1"); + } + + // Copy libservo.so into the jni folder for inclusion in the build + // TODO: pass/detect target architecture + { + let source = &args.target_path.join("libservo.so"); + let target_dir = &directory.join("jni").join("armeabi"); + let _ = DirBuilder::new().recursive(true).create(target_dir); + let target = target_dir.join("libmain.so"); + println!("Copying the file {:?} to {:?}", source, target); + fs::copy(source, target).unwrap(); + } + + let ndkcmd = Command::new(ndk_path.join("ndk-build")) + .arg("-B") + .stdout(Stdio::inherit()) + .stderr(Stdio::inherit()) + .current_dir(directory.clone()) + .status(); + if ndkcmd.is_err() || ndkcmd.unwrap().code().unwrap() != 0 { + println!("Error while executing program `ndk-build`, or missing program."); + process::exit(1); + } + + // Copy the additional native libs into the libs directory. + for (name, path) in native_shared_libs.iter() { + let target = &directory.join("libs").join("armeabi").join(name); + println!("Copying the file {:?} to {:?}", name, target); + fs::copy(path, target).unwrap(); + } + + // Copy over the resources + let cpcmd= Command::new("cp") + .arg("-R") + .arg(&resdir) + .arg(&directory.join("assets")) + .stdout(Stdio::inherit()) + .stderr(Stdio::inherit()) + .current_dir(directory.clone()) + .status(); + if cpcmd.is_err() || cpcmd.unwrap().code().unwrap() != 0 { + println!("Error while copying files from the resources dir to the assets dir"); + process::exit(1); + } + + // Update the project + let androidcmd = Command::new(sdk_path.join("tools").join("android")) + .arg("update") + .arg("project") + .arg("--name") + .arg("Servo") + .arg("--target") + .arg("android-18") + .arg("--path") + .arg(".") + .stdout(Stdio::inherit()) + .stderr(Stdio::inherit()) + .current_dir(directory.clone()) + .status(); + if androidcmd.is_err() || androidcmd.unwrap().code().unwrap() != 0 { + println!("Error while updating the project with the android command"); + process::exit(1); + } + + // Build the APK + let mut antcmd = Command::new("ant"); + if debug { + antcmd.arg("debug"); + } else { + antcmd.arg("release"); + } + let antresult = antcmd + .stdout(Stdio::inherit()) + .stderr(Stdio::inherit()) + .current_dir(directory.clone()) + .status(); + if antresult.is_err() || antresult.unwrap().code().unwrap() != 0 { + println!("Error while executing program `ant`, or missing program."); + process::exit(1); + } + + // Copying apk file to the requested output + // Release builds also need to be signed. For now, we use a simple debug + // signing key. + if debug { + fs::copy(&directory.join("bin").join("Servo-debug.apk"), + &args.output).unwrap(); + } else { + let keystore_dir = env::home_dir().expect("Please have a home directory"); + let keystore_dir = Path::new(&keystore_dir).join(".keystore"); + let keytoolcmd = Command::new("keytool") + .arg("-list") + .arg("-storepass") + .arg("android") + .arg("-alias") + .arg("androiddebugkey") + .arg("-keystore") + .arg(&keystore_dir) + .stdout(Stdio::inherit()) + .stderr(Stdio::inherit()) + .current_dir(directory.clone()) + .status(); + if keytoolcmd.is_err() || keytoolcmd.unwrap().code().unwrap() != 0 { + let keytoolcreatecmd = Command::new("keytool") + .arg("-genkeypair") + .arg("-keystore") + .arg(&keystore_dir) + .arg("-storepass") + .arg("android") + .arg("-alias") + .arg("androiddebugkey") + .arg("-keypass") + .arg("android") + .arg("-dname") + .arg("CN=Android Debug,O=Android,C=US") + .arg("-keyalg") + .arg("RSA") + .arg("-validity") + .arg("365") + .stdout(Stdio::inherit()) + .stderr(Stdio::inherit()) + .current_dir(directory.clone()) + .status(); + if keytoolcreatecmd.is_err() || + keytoolcreatecmd.unwrap().code().unwrap() != 0 { + println!("Error while using `keytool` to create the debug keystore."); + process::exit(1); + } + } + + let jarsigncmd = Command::new("jarsigner") + .arg("-digestalg") + .arg("SHA1") + .arg("-sigalg") + .arg("MD5withRSA") + .arg("-storepass") + .arg("android") + .arg("-keystore") + .arg(&keystore_dir) + .arg(&directory.join("bin").join("Servo-release-unsigned.apk")) + .arg("androiddebugkey") + .stdout(Stdio::inherit()) + .stderr(Stdio::inherit()) + .current_dir(directory.clone()) + .status(); + if jarsigncmd.is_err() || jarsigncmd.unwrap().code().unwrap() != 0 { + println!("Error while using `jarsign` to sign the APK."); + process::exit(1); + } + + let aligncmd = Command::new(sdk_path.join("tools").join("zipalign")) + .arg("-f") + .arg("-v") + .arg("4") + .arg(&directory.join("bin").join("Servo-release-unsigned.apk")) + .arg(&directory.join("bin").join("Servo-release.apk")) + .stdout(Stdio::inherit()) + .stderr(Stdio::inherit()) + .current_dir(directory.clone()) + .status(); + if aligncmd.is_err() || aligncmd.unwrap().code().unwrap() != 0 { + println!("Error while using `zipalign` to sign the APK."); + process::exit(1); + } + + fs::copy(&directory.join("bin").join("Servo-release.apk"), + &args.output).unwrap(); + } + +} + +struct Args { + output: PathBuf, + root_path: PathBuf, + target_path: PathBuf, + shared_libraries: HashSet, +} + +fn parse_arguments() -> (Args, Vec) { + let mut result_output = None; + let mut result_root_path = None; + let mut result_target_path = None; + let mut result_shared_libraries = HashSet::new(); + let mut result_passthrough = Vec::new(); + + let args = env::args(); + let mut args = args.skip(1); + + loop { + let arg = match args.next() { + None => return ( + Args { + output: result_output.expect("Could not find -o argument"), + root_path: result_root_path.expect("Could not find -r enlistment root argument"), + target_path: result_target_path.expect("Could not find -t target path argument"), + shared_libraries: result_shared_libraries, + }, + result_passthrough + ), + Some(arg) => arg + }; + + match &*arg { + "-o" => { + result_output = Some(PathBuf::from(args.next().expect("-o must be followed by the output name"))); + }, + "-r" => { + result_root_path = Some(PathBuf::from(args.next().expect("-r must be followed by the enlistment root directory"))); + }, + "-t" => { + result_target_path = Some(PathBuf::from(args.next().expect("-t must be followed by the target output directory"))); + }, + "-l" => { + let name = args.next().expect("-l must be followed by a library name"); + result_shared_libraries.insert(vec!["lib", &name, ".so"].concat()); + + // Also pass these through. + result_passthrough.push(arg); + result_passthrough.push(name); + } + _ => { + if arg.starts_with("-l") { + result_shared_libraries.insert(vec!["lib", &arg[2..], ".so"].concat()); + } + result_passthrough.push(arg) + } + }; + } +} + +fn find_native_libs(args: &Args) -> HashMap { + let mut native_shared_libs: HashMap = HashMap::new(); + + // Add requested .so files + fs::walk_dir(&args.target_path).and_then(|dir_walker| { + for path in dir_walker { + let path = path.unwrap().path(); + match (path.file_name(), path.extension()) { + (Some(filename), Some(ext)) => { + let filename = filename.to_str().unwrap(); + if filename.starts_with("lib") + && ext == "so" + && args.shared_libraries.contains(filename) { + println!("Adding the file {:?}", filename); + native_shared_libs.insert(filename.to_string(), path.clone()); + break; + } + } + _ => {} + } + } + Ok(()) + }).ok(); + + native_shared_libs +} -- cgit v1.2.3