From 4ed7fb3a020b1e0430b7b8e91cbd814061fd293b Mon Sep 17 00:00:00 2001 From: shmyga Date: Tue, 8 May 2018 18:05:29 +0300 Subject: [PATCH] [android] added module --- haxetool/android.js | 127 ++++++++++++++++++++++++++++++++++++++++++++ haxetool/haxe.js | 8 ++- haxetool/project.js | 118 +++++++++++++++++++++++++++------------- haxetool/sdk.js | 2 +- 4 files changed, 214 insertions(+), 41 deletions(-) create mode 100644 haxetool/android.js diff --git a/haxetool/android.js b/haxetool/android.js new file mode 100644 index 0000000..3d93428 --- /dev/null +++ b/haxetool/android.js @@ -0,0 +1,127 @@ +const fs = require('fs'); +const exec = require('./exec'); +const through = require('through2'); +const Sdk = require('./sdk'); +const Env = require('./env'); + + +class Android extends Sdk { + + constructor(version) { + super(Android.ID, version || Android.VERSION); + } + + get prepared() { + try { + return fs.existsSync(`${this.path}/platform-tools/adb`); + } catch (e) { + return false; + } + } + + get link() { + const system = Sdk.System.isWindows ? 'windows' : Sdk.System.isLinux ? 'linux' : null; + if (!system) throw 'Unsupported system'; + //return `https://dl.google.com/android/repository/sdk-tools-${system}-${this.version}.zip`; + return `https://dl.google.com/android/repository/tools_r${this.version}-${system}.zip`; + } + + prepare() { + return this.prepared ? Promise.resolve() : super.prepare(0).then(() => { + return this.sdkmanager([ + 'ndk-bundle', + 'tools', + 'platform-tools', + 'build-tools;27.0.3', + 'platforms;android-27', + ]); + }); + } + + sdkmanager(packages) { + const androidBin = `${this.path}/tools/bin/sdkmanager`; + if (fs.existsSync(androidBin)) { + fs.chmodSync(androidBin, 0o755); + } + const yes = '(while sleep 3; do echo "y"; done)'; + return exec('.', [yes, '|', androidBin].concat(packages.map(name => `"${name}"`)).join(' ')); + } + + activate() { + Env.set('ANDROID_HOME', this.path); + } + + adb(args) { + const adbBin = `${this.path}/platform-tools/adb`; + return exec('.', [adbBin].concat(args).join(' ')).then(data => { + for (let line of data.stderr.split('\n')) { + if (line.indexOf('Error') > -1) { + throw line; + } + } + }); + } + + aapt(args) { + let buildToolsVersion = null; + fs.readdirSync(`${this.path}/build-tools`).forEach(file => { + buildToolsVersion = file; + }); + const aaptBin = `${this.path}/build-tools/${buildToolsVersion}/aapt`; + return exec('.', [aaptBin].concat(args).join(' ')); + } + + apk() { + const self = this; + return through.obj(function(file, enc, callback) { + self.aapt(['l', '-a', file.path]).then(data => { + let activity = false; + for (let line of data.stdout.split('\n')) { + if (line.indexOf('package') > -1) { + const value = /"(.*?)"/.exec(line); + if (value) file.package = value[1] + } + if (line.indexOf('activity') > -1) { + activity = true; + } + if (activity && line.indexOf('name') > -1) { + const value = /"(.*?)"/.exec(line); + if (value) { + file.activity = value[1]; + activity = false; + } + } + } + this.push(file); + callback(); + }); + }); + } + + install() { + const self = this; + return through.obj(function(file, enc, callback) { + self.adb(['install', '-r', file.path]).then(() => { + this.push(file); + callback(); + }); + }); + } + + start() { + const self = this; + return through.obj((file, enc, callback) => { + const name = `${file.package}/${file.activity}`; + self.adb(['shell', 'am', 'start', '-n', name]).then(() => callback()); + }); + } +} + +Android.ID = 'android'; + +Android.VERSION_25_2_3 = '25.2.3'; +Android.VERSION_3859397 = '3859397'; + +Android.VERSION = Android.VERSION_25_2_3; + +module.exports = Android; diff --git a/haxetool/haxe.js b/haxetool/haxe.js index 45500f1..ed5ae3b 100755 --- a/haxetool/haxe.js +++ b/haxetool/haxe.js @@ -92,7 +92,11 @@ class Haxe extends Sdk { } const target = path.resolve(buildDir, platform, 'bin'); fse.emptyDirSync(target); - return this.haxelib(args).then(() => vfs.src(`${target}/**/*`)); + const result = { + 'android': 'app/build/outputs/apk/*.apk', + 'flash': '*.swf', + }[platform] || '**/*'; + return this.haxelib(args).then(() => vfs.src(`${target}/${result}`)); } build(platform, config, debug=false) { @@ -121,7 +125,7 @@ class Haxe extends Sdk { args.push('-debug'); } const target = path.resolve(buildDir, platform, 'bin'); - fse.emptyDirSync(target) + fse.emptyDirSync(target); for (const asset of config.assets) { fse.copySync(asset, path.join(target, asset.split(path.sep).pop())); } diff --git a/haxetool/project.js b/haxetool/project.js index a642542..29a23b2 100644 --- a/haxetool/project.js +++ b/haxetool/project.js @@ -5,11 +5,13 @@ const fs = require('fs'); const fse = require('fs-extra'); const Haxe = require('./haxe'); const FlashPlayer = require('./flashplayer'); +const Android = require('./android'); const Neko = require('./neko'); const debug = require('./debug'); const webserver = require('gulp-webserver'); const run = require('../run/index'); const tail = require('./tail'); +const exec = require('./exec'); const deb = require('gulp-debian'); const {BuildSystem, Platform, Config} = require('./core'); const vfs = require('vinyl-fs'); @@ -36,6 +38,14 @@ class Target { this.target = 'target'; } + prepare() { + return Promise.resolve(); + } + + call() { + throw 'Not Implemented'; + } + get targetPath() { return path.resolve(this.target, this.config.name, this.platform); } @@ -52,14 +62,6 @@ class Builder extends Target { this.debug = debug; } - prepare() { - return Promise.resolve(); - } - - call() { - throw 'Not Implemented'; - } - static register(buildSystem, builder) { Builder.factory[buildSystem] = builder; } @@ -79,15 +81,26 @@ class HaxeBuilder extends Builder { constructor(config, platform, buildSystem, debug) { super(config, platform, buildSystem, debug); this.haxe = new Haxe(); + this.android = new Android(); } prepare() { let result = this.haxe.prepare().then(() => this.haxe.install(this.config.libs)); - /*if (this.buildSystem === BuildSystem.OPENFL) { + if (this.buildSystem === BuildSystem.OPENFL) { + if (this.platform === Platform.ANDROID) { + result = result.then(() => this.android.prepare()).then(() => this.android.activate()); + } result = result.then(() => { - return this.haxe.haxelib(['run', 'openfl', 'setup', this.platform]); + return exec('.', [ + 'echo', + `"${this.android.path}"`, + `"${this.android.path + '/ndk-bundle'}"`, + '|', + this.haxe.haxelibBin, 'run', 'openfl', 'setup', this.platform, + ].join(' ')); + //return this.haxe.haxelib(['echo'].concat(answers.map(item => `"${item}"`)).concat(['|', 'run', 'openfl', 'setup', this.platform])); }); - }*/ + } return result; } @@ -112,20 +125,17 @@ Builder.register(BuildSystem.OPENFL, HaxeBuilder); */ class Packer extends Target { - prepare() { - return Promise.resolve(); - } - call() { throw 'Not Implemented'; } - static register(platform, packer) { - Packer.factory[platform] = packer; + static register(platform, name, packer) { + if (!Packer.factory[platform]) Packer.factory[platform] = {}; + Packer.factory[platform][name] = packer; } - static new(config, platform) { - return new Packer.factory[platform](config); + static new(config, platform, name) { + return new Packer.factory[platform][name](config); } } @@ -134,7 +144,7 @@ Packer.factory = {}; /** * */ -class FlashPacker extends Packer { +class FlashHTMLPacker extends Packer { constructor(config) { super(config, Platform.FLASH); @@ -150,12 +160,14 @@ class FlashPacker extends Packer { } } -Packer.register(Platform.FLASH, FlashPacker); +FlashHTMLPacker.NAME = 'html'; + +Packer.register(Platform.FLASH, FlashHTMLPacker.NAME, FlashHTMLPacker); /** * */ -class LinuxPacker extends Packer { +class LinuxDEBPacker extends Packer { constructor(config) { super(config, Platform.LINUX); @@ -192,7 +204,9 @@ class LinuxPacker extends Packer { } } -Packer.register(Platform.LINUX, LinuxPacker); +LinuxDEBPacker.NAME = 'deb'; + +Packer.register(Platform.LINUX, LinuxDEBPacker.NAME, LinuxDEBPacker); /** * @@ -204,14 +218,6 @@ class Runner extends Target { this.debug = debug; } - prepare() { - return Promise.resolve(); - } - - call() { - throw 'Not Implemented'; - } - log(stream) { return stream .pipe(tail(debug.log)) @@ -319,6 +325,33 @@ class NekoRunner extends Runner { Runner.register(Platform.NEKO, NekoRunner); +/** + * + */ +class AndroidRunner extends Runner { + + constructor(config, debug) { + super(config, Platform.ANDROID, debug); + this.android = new Android(); + } + + prepare() { + return this.android.prepare(); + } + + call() { + const target = this.targetPath; + const filename = path.resolve(target, this.config.meta.filename+'-debug.apk'); + console.log(filename); + return gulp.src(filename) + .pipe(this.android.apk()) + .pipe(this.android.install()) + .pipe(this.android.start()); + } +} + +Runner.register(Platform.ANDROID, AndroidRunner); + /** * @@ -340,16 +373,20 @@ class Project { return tasks; } - run(platform) { + run(platform, debug) { const runner = Runner.new(this.config, platform, debug); - return this.build(platform, debug).concat([ + return [ runner.prepare.bind(runner), runner.call.bind(runner), - ]); + ]; } - pack(platform) { - const packer = Packer.new(this.config, platform); + test(platform) { + return this.build(platform, debug).concat(this.run(platform, debug)); + } + + pack(platform, name) { + const packer = Packer.new(this.config, platform, name); return [ packer.prepare.bind(packer), packer.call.bind(packer), @@ -360,9 +397,14 @@ class Project { const _gulp = (external_gulp || gulp); for (const platform of this.platforms) { module.exports[`${this.config.name}:${platform}:build`] = _gulp.series(this.build(platform)); - module.exports[`${this.config.name}:${platform}:run`] = _gulp.series(this.run(platform)); + if (Runner.factory[platform]) { + module.exports[`${this.config.name}:${platform}:run`] = _gulp.series(this.run(platform)); + module.exports[`${this.config.name}:${platform}:test`] = _gulp.series(this.test(platform)); + } if (Packer.factory[platform]) { - module.exports[`${this.config.name}:${platform}:pack`] = _gulp.series(this.pack(platform)); + for (const name of Object.keys(Packer.factory[platform])) { + module.exports[`${this.config.name}:${platform}:${name}`] = _gulp.series(this.pack(platform, name)); + } } } return this; diff --git a/haxetool/sdk.js b/haxetool/sdk.js index 088a35c..cf1a204 100755 --- a/haxetool/sdk.js +++ b/haxetool/sdk.js @@ -85,7 +85,7 @@ class Sdk { stream = stream.on('downloadProgress', (p) => bar.update(p.percent)); if (this.link.endsWith('.zip')) { stream = stream.pipe(unzip.Parse()).on('entry', (entry) => { - const filePath = entry.path.split('/').slice(1).join(path.sep); + const filePath = entry.path.split('/').slice(strip).join(path.sep); if (filePath.length > 0) { if (entry.type === 'Directory') { fse.ensureDirSync(path.join(this.path, path.normalize(filePath)));