13 Commits

23 changed files with 580 additions and 125 deletions

View File

@@ -1,3 +1,8 @@
0.0.18
------
* Add meta.fps project param
0.0.12 0.0.12
------ ------
@@ -16,4 +21,4 @@
* Windows compatibility * Windows compatibility
* Change FlashPlayer download link * Change FlashPlayer download link
* Use 'fs-extra' without 'mkdirp' and 'rmdir' * Use 'fs-extra' without 'mkdirp' and 'rmdir'

59
README.md Normal file → Executable file
View File

@@ -8,6 +8,7 @@
* Universal project config for simple HaXe build and OpenFL build. * Universal project config for simple HaXe build and OpenFL build.
* Branch project config for many build presets (many OpenFL apps in one project). * Branch project config for many build presets (many OpenFL apps in one project).
* Packaging linux output in DEB. * Packaging linux output in DEB.
* Packaging windows output in exe install with InnoSetup.
* Create html wrapper for flash output. * Create html wrapper for flash output.
### Flaws ### Flaws
@@ -15,9 +16,7 @@
* Hardcode build output in {projectDir}/target (need param in Project.Config). * Hardcode build output in {projectDir}/target (need param in Project.Config).
* HTML5 run output not caught. * HTML5 run output not caught.
* Adobe AIR Build System in progress. * Adobe AIR Build System in progress.
* Packaging Windows application in progress (with [Inno Setup](http://www.jrsoftware.org/isinfo.php)) * Windows target depends Visual Studio 2017 build tools
* OpenFL android build failed due to problems with Android NDK
## API ## API
@@ -60,10 +59,15 @@ create gulp tasks in module:
<config.name>:<platform>:test <config.name>:<platform>:test
<config.name>:flash:html <config.name>:flash:html
<config.name>:linux:archive
<config.name>:linux:deb <config.name>:linux:deb
<config.name>:windows:archive
<config.name>:windows:installer
``` ```
eg: ```gulp app:flash:build``` eg: ```gulp app:flash:test```
## Example ## Example
@@ -75,12 +79,12 @@ eg: ```gulp app:flash:build```
"devDependencies": { "devDependencies": {
"gulp": "^4.0.0", "gulp": "^4.0.0",
"gulp-clean": "^0.4.0", "gulp-clean": "^0.4.0",
"gulp-haxetool": "^0.0.12" "gulp-haxetool": "^0.1.0"
}, },
"haxeDependencies": { "haxeDependencies": {
"lime": "6.0.1", "lime": "7.6.0",
"openfl": "7.0.0", "openfl": "8.9.2",
"hxcpp": "3.4.188" "hxcpp": "4.0.19"
} }
} }
``` ```
@@ -108,7 +112,7 @@ class Main {
const gulp = require('gulp'); const gulp = require('gulp');
const gulpClean = require('gulp-clean'); const gulpClean = require('gulp-clean');
const packageInfo = require('./package.json'); const packageInfo = require('./package.json');
const {Sdk, Haxe, Project} = require('gulp-haxetool'); const {System, Sdk, Haxe, Project} = require('gulp-haxetool');
//Sdk.dir = '/opt/sdk'; //Sdk.dir = '/opt/sdk';
@@ -144,19 +148,38 @@ const project = new Project(
Project.Platform.FLASH, Project.Platform.FLASH,
Project.Platform.HTML5, Project.Platform.HTML5,
Project.Platform.LINUX, Project.Platform.LINUX,
Project.Platform.WINDOWS,
], ],
config config
).bind(module); ).bind(module);
module.exports.default = gulp.series(
const defaultSeries = [
exports.clean, exports.clean,
exports.install,
module.exports['app:flash:build'], module.exports['app:flash:build'],
module.exports['app:html5:build'],
module.exports['app:linux:build'],
module.exports['app:flash:html'], module.exports['app:flash:html'],
module.exports['app:linux:deb'], module.exports['app:html5:build'],
); ];
if (System.isLinux) {
defaultSeries.push(
module.exports['app:linux:build'],
module.exports['app:linux:archive'],
module.exports['app:linux:deb'],
module.exports['app:android:build'],
);
}
if (System.isWindows) {
defaultSeries.push(
module.exports['app:windows:build'],
module.exports['app:windows:archive'],
module.exports['app:windows:installer'],
);
}
module.exports.default = gulp.series(defaultSeries);
``` ```
Build for all platforms: `gulp default` Build for all platforms: `gulp default`
@@ -165,6 +188,10 @@ Build app for specific platform: `gulp app:flash:build`
Build and run flash app in flashplayer: `gulp app:flash:test` Build and run flash app in flashplayer: `gulp app:flash:test`
Build and run html5 app in brpwser: `gulp app:html5:test` Build and run html5 app in browser: `gulp app:html5:test`
Build and run linux app native: `gulp app:linux:test` Build and run linux app native: `gulp app:linux:test`
Build and run windows app native: `gulp app:windows:test`
Build and run android app: `gulp app:android:test`

35
example/gulpfile.js Normal file → Executable file
View File

@@ -1,7 +1,7 @@
const gulp = require('gulp'); const gulp = require('gulp');
const gulpClean = require('gulp-clean'); const gulpClean = require('gulp-clean');
const packageInfo = require('./package.json'); const packageInfo = require('./package.json');
const {Sdk, Haxe, Project} = require('gulp-haxetool'); const {System, Sdk, Haxe, Project} = require('gulp-haxetool');
//Sdk.dir = '/opt/sdk'; //Sdk.dir = '/opt/sdk';
@@ -37,16 +37,35 @@ const project = new Project(
Project.Platform.FLASH, Project.Platform.FLASH,
Project.Platform.HTML5, Project.Platform.HTML5,
Project.Platform.LINUX, Project.Platform.LINUX,
Project.Platform.WINDOWS,
], ],
config config
).bind(module, gulp); ).bind(module);
module.exports.default = gulp.series(
const defaultSeries = [
exports.clean, exports.clean,
exports.install,
module.exports['app:flash:build'], module.exports['app:flash:build'],
module.exports['app:html5:build'],
module.exports['app:linux:build'],
module.exports['app:flash:html'], module.exports['app:flash:html'],
module.exports['app:linux:deb'], module.exports['app:html5:build'],
); ];
if (System.isLinux) {
defaultSeries.push(
module.exports['app:linux:build'],
module.exports['app:linux:archive'],
module.exports['app:linux:deb'],
module.exports['app:android:build'],
);
}
if (System.isWindows) {
defaultSeries.push(
module.exports['app:windows:build'],
module.exports['app:windows:archive'],
module.exports['app:windows:installer'],
);
}
module.exports.default = gulp.series(defaultSeries);

8
example/package.json Normal file → Executable file
View File

@@ -4,11 +4,11 @@
"devDependencies": { "devDependencies": {
"gulp": "^4.0.0", "gulp": "^4.0.0",
"gulp-clean": "^0.4.0", "gulp-clean": "^0.4.0",
"gulp-haxetool": "^0.0.12" "gulp-haxetool": "^0.1.0"
}, },
"haxeDependencies": { "haxeDependencies": {
"lime": "6.0.1", "lime": "7.6.0",
"openfl": "7.0.0", "openfl": "8.9.2",
"hxcpp": "3.4.188" "hxcpp": "4.0.19"
} }
} }

8
gulp-haxetool.iml Executable file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

89
haxetool/android.js Normal file → Executable file
View File

@@ -1,6 +1,9 @@
const fs = require('fs'); const fs = require('fs');
const path = require('path'); const path = require('path');
const exec = require('./exec'); const exec = require('./exec');
const {StringWritable} = require('./tail');
const System = require('./system');
const Command = require('../run/command');
const through = require('through2'); const through = require('through2');
const Sdk = require('./sdk'); const Sdk = require('./sdk');
const Env = require('./env'); const Env = require('./env');
@@ -10,18 +13,19 @@ class Android extends Sdk {
constructor(version) { constructor(version) {
super(Android.ID, version || Android.VERSION); super(Android.ID, version || Android.VERSION);
this.ndk_version = 'r15c';
} }
get prepared() { get prepared() {
try { try {
return fs.existsSync(`${this.path}/platform-tools/adb`); return fs.existsSync(`${this.path}/tools/bin/sdkmanager`);
} catch (e) { } catch (e) {
return false; return false;
} }
} }
get link() { get link() {
const system = Sdk.System.isWindows ? 'windows' : Sdk.System.isLinux ? 'linux' : null; const system = System.isWindows ? 'windows' : System.isLinux ? 'linux' : null;
if (!system) throw 'Unsupported system'; if (!system) throw 'Unsupported system';
if (this.version.indexOf('.') > -1) { if (this.version.indexOf('.') > -1) {
return `https://dl.google.com/android/repository/tools_r${this.version}-${system}.zip`; return `https://dl.google.com/android/repository/tools_r${this.version}-${system}.zip`;
@@ -39,10 +43,23 @@ class Android extends Sdk {
} }
prepare() { prepare() {
return super.prepare(0); const result = [];
result.push(super.prepare(0));
if (!fs.existsSync(path.join(this.ndk_home))) {
result.push(this.prepare_ndk());
}
return Promise.all(result);
}
prepare_ndk() {
// ToDo: support windows
const url = `https://dl.google.com/android/repository/android-ndk-${this.ndk_version}-linux-x86_64.zip`;
this.log.d('download: *%s*', url);
return Sdk.Downloader.download(url, this.ndk_home, 1, true);
} }
sdkmanager(packages) { sdkmanager(packages) {
this.log.i('sdkmanager: *%s*', packages.join(', '));
const androidBin = path.join(this.path, 'tools/bin/sdkmanager'); const androidBin = path.join(this.path, 'tools/bin/sdkmanager');
if (fs.existsSync(androidBin)) { if (fs.existsSync(androidBin)) {
fs.chmodSync(androidBin, 0o755); fs.chmodSync(androidBin, 0o755);
@@ -57,14 +74,18 @@ class Android extends Sdk {
Env.set('NDK_HOME', this.ndk_home); Env.set('NDK_HOME', this.ndk_home);
} }
get adbBin() {
return path.join(this.path, 'platform-tools/adb');
}
adb(args) { adb(args) {
const adbBin = path.join(this.path, 'platform-tools/adb'); return exec('.', [this.adbBin].concat(args).join(' ')).then(data => {
return exec('.', [adbBin].concat(args).join(' ')).then(data => {
for (let line of data.stderr.split('\n')) { for (let line of data.stderr.split('\n')) {
if (line.indexOf('Error') > -1) { if (line.indexOf('Error') > -1) {
throw line; throw line;
} }
} }
return data;
}); });
} }
@@ -104,10 +125,35 @@ class Android extends Sdk {
}); });
} }
_installApk(filename, pack) {
this.log.i('install *%s*', filename);
return this.adb(['install', '-r', '-d', filename])
.then(() => {
return false;
})
.catch(error => {
if (error.includes('INSTALL_FAILED_UPDATE_INCOMPATIBLE')) {
this.log.w('!INSTALL FAILED UPDATE INCOMPATIBLE!');
return true;
} else {
throw error;
}
})
.then(reinstall => {
if (reinstall) {
this.log.i('uninstall *%s*', pack);
return this.adb(['uninstall', pack]).then(() => {
this.log.i('install *%s*', filename);
return this.adb(['install', '-r', '-d', filename]);
});
}
});
}
install() { install() {
const self = this; const self = this;
return through.obj(function(file, enc, callback) { return through.obj(function(file, enc, callback) {
self.adb(['install', '-r', file.path]).then(() => { self._installApk(file.path, file.package).then(() => {
this.push(file); this.push(file);
callback(); callback();
}); });
@@ -116,9 +162,33 @@ class Android extends Sdk {
start() { start() {
const self = this; const self = this;
return through.obj((file, enc, callback) => { return through.obj(function(file, enc, callback) {
const name = `${file.package}/${file.activity}`; const name = `${file.package}/${file.activity}`;
self.adb(['shell', 'am', 'start', '-n', name]).then(() => callback()); self.log.i('start *%s*', name);
self.adb(['shell', 'am', 'start', '-n', name]).then(() => {
setTimeout(() => {
self.adb(['shell', `"ps | grep ${file.package}"`]).then(result => {
file.pid = result.stdout.split(/\s+/)[1];
this.push(file);
callback();
});
}, 1000);
});
});
}
logcat() {
const self = this;
return through.obj(function(file, enc, callback) {
const cmd = `${self.adbBin} logcat --pid ${file.pid}`;
const command = new Command(cmd).exec();
this.push(command);
command.contents.pipe(new StringWritable(line => {
if (line.includes('SDL') && (line.includes('onDestroy()') || line.includes('onStop()'))) {
callback();
throw 'Exit'; // ToDo:
}
}));
}); });
} }
} }
@@ -127,7 +197,8 @@ Android.ID = 'android';
Android.VERSION_25_2_3 = '25.2.3'; Android.VERSION_25_2_3 = '25.2.3';
Android.VERSION_3859397 = '3859397'; Android.VERSION_3859397 = '3859397';
Android.VERSION_4333796 = '4333796';
Android.VERSION = Android.VERSION_3859397; Android.VERSION = Android.VERSION_4333796;
module.exports = Android; module.exports = Android;

View File

@@ -42,9 +42,13 @@ class Config {
company: null, company: null,
width: 800, width: 800,
height: 600, height: 600,
mobileWidth: null,
mobileHeight: null,
fps: 60,
}; };
if (params) { if (params) {
this.update(params); this.update(params);
this.afterUpdate();
} }
} }
@@ -67,12 +71,18 @@ class Config {
if (this.meta.icon) this.icon = Config.absolutePath(this.meta.icon); if (this.meta.icon) this.icon = Config.absolutePath(this.meta.icon);
} }
afterUpdate() {
if (this.meta.mobileWidth === null) this.meta.mobileWidth = this.meta.width;
if (this.meta.mobileHeight === null) this.meta.mobileHeight = Math.round(this.meta.mobileWidth / 1.777777778);
}
branch(params) { branch(params) {
const result = new Config(); const result = new Config();
for (const params of this._params) { for (const params of this._params) {
result.update(params); result.update(params);
} }
result.update(params); result.update(params);
result.afterUpdate();
return result; return result;
} }
} }

6
haxetool/env.js Normal file → Executable file
View File

@@ -7,6 +7,10 @@ class Env {
process.env[key] = value; process.env[key] = value;
} }
static get(key) {
return process.env[key];
}
static addPath(value, key='PATH') { static addPath(value, key='PATH') {
if (!process.env[key]) { if (!process.env[key]) {
process.env[key] = value; process.env[key] = value;
@@ -17,4 +21,4 @@ class Env {
} }
module.exports = Env; module.exports = Env;

View File

@@ -27,4 +27,4 @@ module.exports = (dir, command) => {
failure: failure, failure: failure,
}); });
}); });
}; };

View File

@@ -4,6 +4,7 @@ const fse = require('fs-extra');
const os = require('os'); const os = require('os');
const through = require('through2'); const through = require('through2');
const Sdk = require('./sdk'); const Sdk = require('./sdk');
const System = require('./system');
const run = require('../run/index'); const run = require('../run/index');
const {TailVinyl} = require('./tail'); const {TailVinyl} = require('./tail');
@@ -29,9 +30,9 @@ class FlashPlayer extends Sdk {
get link() { get link() {
const baseUrl = `https://fpdownload.macromedia.com/pub/flashplayer/updaters/${this.version}/`; const baseUrl = `https://fpdownload.macromedia.com/pub/flashplayer/updaters/${this.version}/`;
if (Sdk.System.isWindows) { if (System.isWindows) {
return baseUrl + `flashplayer_${this.version}_sa${this.debug ? '_debug' : ''}.exe`; return baseUrl + `flashplayer_${this.version}_sa${this.debug ? '_debug' : ''}.exe`;
} else if (Sdk.System.isLinux) { } else if (System.isLinux) {
return baseUrl + `flash_player_sa_linux${this.debug ? '_debug' : ''}.x86_64.tar.gz`; return baseUrl + `flash_player_sa_linux${this.debug ? '_debug' : ''}.x86_64.tar.gz`;
} else { } else {
throw `Unsupported os '${os.type()}'`; throw `Unsupported os '${os.type()}'`;
@@ -116,9 +117,12 @@ FlashPlayer.VERSION_24 = '24';
FlashPlayer.VERSION_25 = '25'; FlashPlayer.VERSION_25 = '25';
FlashPlayer.VERSION_26 = '26'; FlashPlayer.VERSION_26 = '26';
FlashPlayer.VERSION_27 = '27'; FlashPlayer.VERSION_27 = '27';
FlashPlayer.VERSION_28 = '28';
FlashPlayer.VERSION_29 = '29'; FlashPlayer.VERSION_29 = '29';
FlashPlayer.VERSION_30 = '30'; FlashPlayer.VERSION_30 = '30';
FlashPlayer.VERSION_31 = '31';
FlashPlayer.VERSION_32 = '32';
FlashPlayer.VERSION = FlashPlayer.VERSION_30; FlashPlayer.VERSION = FlashPlayer.VERSION_32;
module.exports = FlashPlayer; module.exports = FlashPlayer;

View File

@@ -4,18 +4,19 @@ const fse = require('fs-extra');
const path = require('path'); const path = require('path');
const exec = require('./exec'); const exec = require('./exec');
const Sdk = require('./sdk'); const Sdk = require('./sdk');
const System = require('./system');
const Env = require('./env'); const Env = require('./env');
const Neko = require('./neko'); const Neko = require('./neko');
const vfs = require('vinyl-fs'); const vfs = require('vinyl-fs');
const template = require('lodash.template'); const template = require('lodash.template');
const rename = require('gulp-rename');
class Haxe extends Sdk { class Haxe extends Sdk {
getBin(name) { getBin(name) {
if (Sdk.System.isWindows) { if (System.isWindows) {
return path.join(this.path, name + '.exe'); return path.join(this.path, name + '.exe');
} else if (Sdk.System.isLinux) { } else if (System.isLinux) {
const binPath = path.join(this.path, name); const binPath = path.join(this.path, name);
if (fs.existsSync(binPath)) { if (fs.existsSync(binPath)) {
fs.chmodSync(binPath, 0o755); fs.chmodSync(binPath, 0o755);
@@ -57,24 +58,26 @@ class Haxe extends Sdk {
prepare() { prepare() {
return Promise.all([this.neko.prepare(), super.prepare()]).then(() => { return Promise.all([this.neko.prepare(), super.prepare()]).then(() => {
fs.copyFileSync(path.resolve(__dirname, '..', 'template/activate'), path.resolve(this.path, 'activate')); fs.copyFileSync(path.resolve(__dirname, '..', 'template/haxe/activate'), path.resolve(this.path, 'activate'));
}); });
} }
get link() { get link() {
if (Sdk.System.isWindows) { if (System.isWindows) {
return `https://github.com/HaxeFoundation/haxe/releases/download/${this.version}/haxe-${this.version}-win.zip`; return `https://github.com/HaxeFoundation/haxe/releases/download/${this.version}/haxe-${this.version}-win.zip`;
} else if (Sdk.System.isLinux) { } else if (System.isLinux) {
let arch = Sdk.System.archInt; let arch = System.archInt;
return `https://github.com/HaxeFoundation/haxe/releases/download/${this.version}/haxe-${this.version}-linux${arch}.tar.gz`; return `https://github.com/HaxeFoundation/haxe/releases/download/${this.version}/haxe-${this.version}-linux${arch}.tar.gz`;
} }
} }
haxe(args) { haxe(args) {
this.activate();
return exec('.', [this.haxeBin].concat(args).join(' ')); return exec('.', [this.haxeBin].concat(args).join(' '));
} }
haxelib(args) { haxelib(args) {
this.activate();
return exec('.', [this.haxelibBin].concat(args).join(' ')); return exec('.', [this.haxelibBin].concat(args).join(' '));
} }
@@ -98,10 +101,16 @@ class Haxe extends Sdk {
const target = path.resolve(buildDir, platform, 'bin'); const target = path.resolve(buildDir, platform, 'bin');
fse.emptyDirSync(target); fse.emptyDirSync(target);
const result = { const result = {
'android': 'app/build/outputs/apk/*.apk', 'android': 'app/build/outputs/apk/*-debug.apk',
'flash': '*.swf', 'flash': '*.swf',
}[platform] || '**/*'; }[platform] || '**/*';
return this.haxelib(args).then(() => vfs.src(`${target}/${result}`)); return this.haxelib(args).then(() => {
let r = vfs.src(`${target}/${result}`);
if (platform === 'android') {
r = r.pipe(rename(`${config.meta.filename}_${config.meta.version}.apk`));
}
return r;
});
} }
build(platform, config, debug=false) { build(platform, config, debug=false) {
@@ -132,7 +141,7 @@ class Haxe extends Sdk {
const target = path.resolve(buildDir, platform, 'bin'); const target = path.resolve(buildDir, platform, 'bin');
fse.emptyDirSync(target); fse.emptyDirSync(target);
for (const asset of config.assets) { for (const asset of config.assets) {
fse.copySync(asset, path.join(target, asset.split(path.sep).pop())); fse.copySync(asset, path.join(target, asset.split("/").pop()));
} }
return this.haxe(args).then(() => vfs.src(`${target}/**/*`)); return this.haxe(args).then(() => vfs.src(`${target}/**/*`));
} }

57
haxetool/innosetup.js Executable file
View File

@@ -0,0 +1,57 @@
const fs = require('fs');
const fse = require('fs-extra');
const os = require('os');
const path = require('path');
const Sdk = require('./sdk');
const exec = require('./exec');
const template = require('lodash.template');
class InnoSetup extends Sdk {
constructor(version) {
super(InnoSetup.ID, version || InnoSetup.VERSION);
}
get link() {
return 'http://www.jrsoftware.org/download.php/is.exe';
}
prepare(strip = 1) {
let result = super.prepare(strip);
if (!this.prepared) {
result = result.then(() => {
return exec(this.path, `is.exe /VERYSILENT /SUPPRESSMSGBOXES /DIR=${this.path}`);
});
}
return result;
}
get isccBin() {
return path.join(this.path, 'ISCC.exe')
}
get prepared() {
try {
return fs.existsSync(this.isccBin);
} catch (e) {
return false;
}
}
pack(config, source, output) {
const buildDir = path.join(os.tmpdir(), 'build', config.name, 'innosetup');
const appTemplate = template(fs.readFileSync(path.resolve(__dirname, '..', 'template/windows/app.iss')));
const app = appTemplate({...config, buildDir: buildDir, output: output});
fse.ensureDirSync(buildDir);
fse.copySync(`${source}`, `${buildDir}`);
fs.writeFileSync(path.resolve(buildDir, 'app.iss'), app);
return exec(buildDir, [`"${this.isccBin}"`, 'app.iss'].join(' '));
}
}
InnoSetup.ID = 'innosetup';
InnoSetup.VERSION = '6';
module.exports = InnoSetup;

View File

@@ -61,6 +61,12 @@ class Neko extends Sdk {
Env.addPath(this.path, 'LD_LIBRARY_PATH'); Env.addPath(this.path, 'LD_LIBRARY_PATH');
} }
prepare() {
return Promise.all([super.prepare()]).then(() => {
fs.copyFileSync(path.resolve(__dirname, '..', 'template/neko/activate'), path.resolve(this.path, 'activate'));
});
}
get nekoBin() { get nekoBin() {
let binname = 'neko'; let binname = 'neko';
if (os.type() === 'Windows_NT') binname += '.exe'; if (os.type() === 'Windows_NT') binname += '.exe';
@@ -79,4 +85,4 @@ Neko.ID = 'neko';
Neko.VERSION_2_2_0 = '2.2.0'; Neko.VERSION_2_2_0 = '2.2.0';
Neko.VERSION = Neko.VERSION_2_2_0; Neko.VERSION = Neko.VERSION_2_2_0;
module.exports = Neko; module.exports = Neko;

117
haxetool/project.js Normal file → Executable file
View File

@@ -7,6 +7,7 @@ const Haxe = require('./haxe');
const FlashPlayer = require('./flashplayer'); const FlashPlayer = require('./flashplayer');
const Android = require('./android'); const Android = require('./android');
const Neko = require('./neko'); const Neko = require('./neko');
const InnoSetup = require('./innosetup');
const Env = require('./env'); const Env = require('./env');
const debug = require('./debug'); const debug = require('./debug');
const webserver = require('gulp-webserver'); const webserver = require('gulp-webserver');
@@ -18,7 +19,9 @@ const {BuildSystem, Platform, Config} = require('./core');
const vfs = require('vinyl-fs'); const vfs = require('vinyl-fs');
const rename = require('gulp-rename'); const rename = require('gulp-rename');
const template = require('lodash.template'); const template = require('lodash.template');
const tar = require('gulp-tar');
const gzip = require('gulp-gzip');
const zip = require('gulp-zip');
const streamToPromise = (stream) => { const streamToPromise = (stream) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@@ -47,8 +50,12 @@ class Target {
throw 'Not Implemented'; throw 'Not Implemented';
} }
resolveTargetPath(platform = this.platform) {
return path.resolve(this.target, this.config.name, platform)
}
get targetPath() { get targetPath() {
return path.resolve(this.target, this.config.name, this.platform); return this.resolveTargetPath();
} }
} }
@@ -96,8 +103,9 @@ class HaxeBuilder extends Builder {
'tools', 'tools',
'platform-tools', 'platform-tools',
'build-tools;27.0.3', 'build-tools;27.0.3',
'platforms;android-19', //'platforms;android-19',
'ndk-bundle', 'platforms;android-26',
//'ndk-bundle',
'lldb;3.1', 'lldb;3.1',
'cmake;3.6.4111459', 'cmake;3.6.4111459',
])); ]));
@@ -207,7 +215,7 @@ class LinuxDEBPacker extends Packer {
'fi' 'fi'
], ],
_target: '/', _target: '/',
_out: path.join(target, '..', 'debian'), _out: this.resolveTargetPath('debian'),
_clean: true, _clean: true,
_verbose: false _verbose: false
})); }));
@@ -218,6 +226,74 @@ LinuxDEBPacker.NAME = 'deb';
Packer.register(Platform.LINUX, LinuxDEBPacker.NAME, LinuxDEBPacker); Packer.register(Platform.LINUX, LinuxDEBPacker.NAME, LinuxDEBPacker);
/**
*
*/
class LinuxArchivePacker extends Packer {
constructor(config) {
super(config, Platform.LINUX);
}
call() {
const target = this.targetPath;
const archiveName = `${this.config.meta.filename}_${this.config.meta.version}_linux.tar`;
return gulp.src(`${target}/**/*`)
.pipe(tar(archiveName))
.pipe(gzip())
.pipe(gulp.dest(this.resolveTargetPath('archive')))
}
}
LinuxArchivePacker.NAME = 'archive';
Packer.register(Platform.LINUX, LinuxArchivePacker.NAME, LinuxArchivePacker);
/**
*
*/
class WindowsInstallerPacker extends Packer {
constructor(config) {
super(config, Platform.WINDOWS);
this.innosetup = new InnoSetup();
}
prepare() {
return this.innosetup.prepare();
}
call() {
return this.innosetup.pack(this.config, this.targetPath, this.resolveTargetPath('installer'));
}
}
WindowsInstallerPacker.NAME = 'installer';
Packer.register(Platform.WINDOWS, WindowsInstallerPacker.NAME, WindowsInstallerPacker);
/**
*
*/
class WindowsArchivePacker extends Packer {
constructor(config) {
super(config, Platform.WINDOWS);
}
call() {
const target = this.targetPath;
const archiveName = `${this.config.meta.filename}_${this.config.meta.version}_win.zip`;
return gulp.src(`${target}/**/*`)
.pipe(zip(archiveName))
.pipe(gulp.dest(this.resolveTargetPath('archive')))
}
}
WindowsArchivePacker.NAME = 'archive';
Packer.register(Platform.WINDOWS, WindowsArchivePacker.NAME, WindowsArchivePacker);
/** /**
* *
*/ */
@@ -282,7 +358,8 @@ class Html5Runner extends Runner {
call() { call() {
return gulp.src(this.targetPath) return gulp.src(this.targetPath)
.pipe(webserver({ .pipe(webserver({
host: 'localhost', port: 3000, // ToDo: config?
host: '0.0.0.0', port: 3000,
open: true, open: true,
fallback: 'index.html' fallback: 'index.html'
})); }));
@@ -309,7 +386,25 @@ class LinuxRunner extends Runner {
} }
Runner.register(Platform.LINUX, LinuxRunner); Runner.register(Platform.LINUX, LinuxRunner);
Runner.register(Platform.WINDOWS, LinuxRunner); //ToDo:
/**
*
*/
class WindowsRunner extends Runner {
constructor(config, debug) {
super(config, Platform.WINDOWS, debug);
}
call() {
const target = this.targetPath;
const filename = path.resolve(target, this.config.meta.filename + '.exe');
const result = gulp.src(filename).pipe(run("<%=file.basename%>", {cwd: target, verbosity: 0}));
return this.log(result);
}
}
Runner.register(Platform.WINDOWS, WindowsRunner);
/** /**
* *
@@ -351,12 +446,12 @@ class AndroidRunner extends Runner {
call() { call() {
const target = this.targetPath; const target = this.targetPath;
const filename = path.resolve(target, this.config.meta.filename+'-debug.apk'); return this.log(gulp.src(`${target}/${this.config.meta.filename}_*.apk`)
console.log(filename);
return gulp.src(filename)
.pipe(this.android.apk()) .pipe(this.android.apk())
.pipe(this.android.install()) .pipe(this.android.install())
.pipe(this.android.start()); .pipe(this.android.start())
.pipe(this.android.logcat())
);
} }
} }

View File

@@ -5,32 +5,71 @@ const ps = require('promise-streams');
const got = require('got'); const got = require('got');
const unzip = require('unzip-stream'); const unzip = require('unzip-stream');
const tar = require('tar'); const tar = require('tar');
const ProgressBar = require('progress'); const progress = require('cli-progress');
const fse = require('fs-extra'); const fse = require('fs-extra');
const Log = require('./log'); const Log = require('./log');
class Downloader {
class System { static createProgressBar() {
if (Downloader.bar == null) {
static get isWindows() { Downloader.bar = new progress.MultiBar({
return os.type() === 'Windows_NT'; clearOnComplete: true,
}, progress.Presets.shades_grey);
}
return Downloader.bar.create();
} }
static get isLinux() { static releaseProgressBar(bar) {
return os.type() === 'Linux'; bar.stop();
if (Downloader.bar != null) {
Downloader.bar.remove(bar);
if (Downloader.bar.bars.length === 0) {
Downloader.bar.stop();
Downloader.bar = null;
}
}
} }
static get archInt() { static download(url, dest, strip = 1, executable = false) {
if (os.arch() === 'ia32') return 32; fse.ensureDirSync(dest);
if (os.arch() === 'x64') return 64; const bar = Downloader.createProgressBar();
} let stream = got.stream(url);
stream = stream
static get isArch32() { .on('request', r => {
return this.archInt === 32; if (bar) bar.start(1, 0);
} })
.on('downloadProgress', p => {
static get isArch64() { if (bar) {
return this.archInt === 64; bar.setTotal(p.total);
bar.update(p.transferred);
if (p.total === p.transferred) {
Downloader.releaseProgressBar(bar);
}
}
});
if (url.endsWith('.zip')) {
stream = stream.pipe(unzip.Parse()).on('entry', entry => {
const filePath = entry.path.split('/').slice(strip).join(path.sep);
if (filePath.length > 0) {
if (entry.type === 'Directory') {
fse.ensureDirSync(path.join(dest, path.normalize(filePath)));
} else if (entry.type === 'File') {
entry.pipe(fs.createWriteStream(path.join(dest, path.normalize(filePath)), {
// ToDo: zip entry file mode?
mode: executable ? 0o755 : undefined,
}));
}
} else {
entry.autodrain();
}
});
} else if (url.endsWith('tar.gz')) {
stream = stream.pipe(tar.x({C: dest, strip: strip}));
} else {
stream = stream.pipe(fs.createWriteStream(path.join(dest, url.split('/').pop())));
}
return ps.wait(stream);
} }
} }
@@ -74,39 +113,17 @@ class Sdk {
throw "Not implemented"; throw "Not implemented";
} }
prepare(strip=1) { prepare(strip = 1) {
this.log.i('version: *%s*', this.version); this.log.i('version: *%s*', this.version);
if (this.prepared) { if (this.prepared) {
return Promise.resolve(); return Promise.resolve();
} else { } else {
fse.ensureDirSync(this.path);
const bar = new ProgressBar(`${this.tag} [:bar] :percent :etas`, {width: 40, total: 1000, clear: true});
this.log.d('download: *%s*', this.link); this.log.d('download: *%s*', this.link);
let stream = got.stream(this.link); return Downloader.download(this.link, this.path, strip);
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(strip).join(path.sep);
if (filePath.length > 0) {
if (entry.type === 'Directory') {
fse.ensureDirSync(path.join(this.path, path.normalize(filePath)));
} else if (entry.type === 'File') {
entry.pipe(fs.createWriteStream(path.join(this.path, path.normalize(filePath))));
}
} else {
entry.autodrain();
}
});
} else if (this.link.endsWith('tar.gz')) {
stream = stream.pipe(tar.x({C: this.path, strip: strip}));
} else {
stream = stream.pipe(fs.createWriteStream(path.join(this.path, this.link.split('/').pop())));
}
return ps.wait(stream);
} }
} }
} }
Sdk.System = System; Sdk.Downloader = Downloader;
module.exports = Sdk; module.exports = Sdk;

27
haxetool/system.js Executable file
View File

@@ -0,0 +1,27 @@
const os = require('os');
class System {
static get isWindows() {
return os.type() === 'Windows_NT';
}
static get isLinux() {
return os.type() === 'Linux';
}
static get archInt() {
if (os.arch() === 'ia32') return 32;
if (os.arch() === 'x64') return 64;
}
static get isArch32() {
return this.archInt === 32;
}
static get isArch64() {
return this.archInt === 64;
}
}
module.exports = System;

View File

@@ -67,3 +67,4 @@ module.exports = (handler) => {
}; };
module.exports.TailVinyl = TailVinyl; module.exports.TailVinyl = TailVinyl;
module.exports.StringWritable = StringWritable;

4
index.js Normal file → Executable file
View File

@@ -1,7 +1,9 @@
module.exports = { module.exports = {
System: require('./haxetool/system'),
Sdk: require('./haxetool/sdk'), Sdk: require('./haxetool/sdk'),
Haxe: require('./haxetool/haxe'), Haxe: require('./haxetool/haxe'),
FlashPlayer: require('./haxetool/flashplayer'), FlashPlayer: require('./haxetool/flashplayer'),
Android: require('./haxetool/android'),
AdobeAir: require('./haxetool/adobe_air'), AdobeAir: require('./haxetool/adobe_air'),
Project: require('./haxetool/project'), Project: require('./haxetool/project'),
}; };

11
package.json Normal file → Executable file
View File

@@ -1,26 +1,29 @@
{ {
"name": "gulp-haxetool", "name": "gulp-haxetool",
"version": "0.0.17", "version": "0.1.0",
"description": "HaXe Tool for Gulp", "description": "HaXe Tool for Gulp",
"main": "index.js", "main": "index.js",
"dependencies": { "dependencies": {
"ansi-colors": "^1.1.0", "ansi-colors": "^1.1.0",
"async": "^2.6.0", "async": "^2.6.0",
"cli-progress": "^3.0.0",
"fancy-log": "^1.3.2", "fancy-log": "^1.3.2",
"fs-extra": "^5.0.0", "fs-extra": "^5.0.0",
"got": "^8.3.0", "got": "^9.6.0",
"gulp": "^4.0.0", "gulp": "^4.0.0",
"gulp-debian": "^0.1.9", "gulp-debian": "^0.1.9",
"gulp-gzip": "^1.4.2",
"gulp-rename": "^1.2.2", "gulp-rename": "^1.2.2",
"gulp-spawn": "^0.4.0", "gulp-spawn": "^0.4.0",
"gulp-tar": "^3.0.0",
"gulp-webserver": "^0.9.1", "gulp-webserver": "^0.9.1",
"gulp-zip": "^5.0.0",
"lodash.defaults": "^4.2.0", "lodash.defaults": "^4.2.0",
"lodash.template": "^4.4.0", "lodash.template": "^4.4.0",
"plugin-error": "^1.0.1", "plugin-error": "^1.0.1",
"progress": "^2.0.0",
"promise-streams": "^2.1.1", "promise-streams": "^2.1.1",
"tail": "^1.2.3", "tail": "^1.2.3",
"tar": "^4.4.1", "tar": "^4.4.10",
"through2": "^2.0.3", "through2": "^2.0.3",
"tmp-file": "^2.0.1", "tmp-file": "^2.0.1",
"unzip-stream": "^0.3.0", "unzip-stream": "^0.3.0",

View File

@@ -3,10 +3,10 @@
deactivate () { deactivate () {
# reset old environment variables # reset old environment variables
# ! [ -z ${VAR+_} ] returns true if VAR is declared at all # ! [ -z ${VAR+_} ] returns true if VAR is declared at all
if ! [ -z "${_OLD_VIRTUAL_PATH+_}" ] ; then if ! [ -z "${_OLD_HAXE_VIRTUAL_PATH+_}" ] ; then
PATH="$_OLD_VIRTUAL_PATH" PATH="$_OLD_HAXE_VIRTUAL_PATH"
export PATH export PATH
unset _OLD_VIRTUAL_PATH unset _OLD_HAXE_VIRTUAL_PATH
fi fi
if ! [ -z "${_OLD_VIRTUAL_LD_LIBRARY_PATH+_}" ] ; then if ! [ -z "${_OLD_VIRTUAL_LD_LIBRARY_PATH+_}" ] ; then
LD_LIBRARY_PATH="$_OLD_VIRTUAL_LD_LIBRARY_PATH" LD_LIBRARY_PATH="$_OLD_VIRTUAL_LD_LIBRARY_PATH"
@@ -21,10 +21,10 @@ deactivate () {
hash -r 2>/dev/null hash -r 2>/dev/null
fi fi
if ! [ -z "${_OLD_VIRTUAL_PS1+_}" ] ; then if ! [ -z "${_OLD_HAXE_VIRTUAL_PS1+_}" ] ; then
PS1="$_OLD_VIRTUAL_PS1" PS1="$_OLD_HAXE_VIRTUAL_PS1"
export PS1 export PS1
unset _OLD_VIRTUAL_PS1 unset _OLD_HAXE_VIRTUAL_PS1
fi fi
unset HAXE_HOME unset HAXE_HOME
@@ -43,7 +43,7 @@ export HAXE_HOME
HAXE_STD_PATH="${HAXE_HOME}/std" HAXE_STD_PATH="${HAXE_HOME}/std"
export HAXE_STD_PATH export HAXE_STD_PATH
_OLD_VIRTUAL_PATH="$PATH" _OLD_HAXE_VIRTUAL_PATH="$PATH"
PATH="${PATH}:${HAXE_HOME}" PATH="${PATH}:${HAXE_HOME}"
export PATH export PATH
@@ -52,7 +52,7 @@ LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${HAXE_HOME}"
export LD_LIBRARY_PATH export LD_LIBRARY_PATH
if [ -z "${HAXE_DISABLE_PROMPT-}" ] ; then if [ -z "${HAXE_DISABLE_PROMPT-}" ] ; then
_OLD_VIRTUAL_PS1="$PS1" _OLD_HAXE_VIRTUAL_PS1="$PS1"
if [ "x" != x ] ; then if [ "x" != x ] ; then
PS1="$PS1" PS1="$PS1"
else else

69
template/neko/activate Normal file
View File

@@ -0,0 +1,69 @@
# From python venv activate
deactivate () {
# reset old environment variables
# ! [ -z ${VAR+_} ] returns true if VAR is declared at all
if ! [ -z "${_OLD_NEKO_VIRTUAL_PATH+_}" ] ; then
PATH="$_OLD_NEKO_VIRTUAL_PATH"
export PATH
unset _OLD_NEKO_VIRTUAL_PATH
fi
if ! [ -z "${_OLD_VIRTUAL_LD_LIBRARY_PATH+_}" ] ; then
LD_LIBRARY_PATH="$_OLD_VIRTUAL_LD_LIBRARY_PATH"
export LD_LIBRARY_PATH
unset _OLD_VIRTUAL_LD_LIBRARY_PATH
fi
# This should detect bash and zsh, which have a hash command that must
# be called to get it to forget past commands. Without forgetting
# past commands the $PATH changes we made may not be respected
if [ -n "${BASH-}" ] || [ -n "${ZSH_VERSION-}" ] ; then
hash -r 2>/dev/null
fi
if ! [ -z "${_OLD_NEKO_VIRTUAL_PS1+_}" ] ; then
PS1="$_OLD_NEKO_VIRTUAL_PS1"
export PS1
unset _OLD_NEKO_VIRTUAL_PS1
fi
unset NEKO_HOME
if [ ! "${1-}" = "nondestructive" ] ; then
# Self destruct!
unset -f deactivate
fi
}
# unset irrelevant variables
deactivate nondestructive
NEKO_HOME="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
export NEKO_HOME
NEKO_STD_PATH="${NEKO_HOME}/std"
export NEKO_STD_PATH
_OLD_NEKO_VIRTUAL_PATH="$PATH"
PATH="${PATH}:${NEKO_HOME}"
export PATH
_OLD_VIRTUAL_LD_LIBRARY_PATH="$PATH"
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${NEKO_HOME}"
export LD_LIBRARY_PATH
if [ -z "${NEKO_DISABLE_PROMPT-}" ] ; then
_OLD_NEKO_VIRTUAL_PS1="$PS1"
if [ "x" != x ] ; then
PS1="$PS1"
else
PS1="(neko) $PS1"
fi
export PS1
fi
# This should detect bash and zsh, which have a hash command that must
# be called to get it to forget past commands. Without forgetting
# past commands the $PATH changes we made may not be respected
if [ -n "${BASH-}" ] || [ -n "${ZSH_VERSION-}" ] ; then
hash -r 2>/dev/null
fi

View File

@@ -6,7 +6,8 @@
<% sources.forEach(function(item) { %> <% sources.forEach(function(item) { %>
<source path="<%=item%>"/><% }); %> <source path="<%=item%>"/><% }); %>
<% assets.forEach(function(item) { %> <% assets.forEach(function(item) { %>
<assets path="<%=item%>" rename="<%=item.split('/').pop()%>" include="*"/><% }); %> <assets if="flash" path="<%=item%>" rename="<%=item.split('/').pop()%>" include="*" exclude="*.ogg"/>
<assets unless="flash" path="<%=item%>" rename="<%=item.split('/').pop()%>" include="*" exclude="*.mp3"/><% }); %>
<% libs.forEach(function(item) { %> <% libs.forEach(function(item) { %>
<haxelib name="<%=item.name%>" version="<%=item.version.split('@').shift()%>"/><% }); %> <haxelib name="<%=item.name%>" version="<%=item.version.split('@').shift()%>"/><% }); %>
<% macros.forEach(function(item) { %> <% macros.forEach(function(item) { %>
@@ -14,8 +15,10 @@
<% flags.forEach(function(item) { %> <% flags.forEach(function(item) { %>
<haxeflag name="-D" value="<%=item%>"/><% }); %> <haxeflag name="-D" value="<%=item%>"/><% }); %>
<window fps="30"/> <window fps="<%=meta.fps%>"/>
<window width="<%=meta.width%>" height="<%=meta.height%>" unless="html5"/> <window fps="<%=meta.fps%>" width="<%=meta.width%>" height="<%=meta.height%>" unless="html5"/>
<window fps="<%=meta.fps%>" width="<%=meta.mobileWidth%>" height="<%=meta.mobileHeight%>"
orientation="landscape" resizable="false" if="mobile"/>
<haxeflag name="-D" value="swf-gpu"/> <haxeflag name="-D" value="swf-gpu"/>
<haxeflag name="-D" value="native-trace"/> <haxeflag name="-D" value="native-trace"/>

18
template/windows/app.iss Executable file
View File

@@ -0,0 +1,18 @@
[Setup]
AppName=<%=meta.title%>
AppVersion=<%=meta.version%>
WizardStyle=modern
DefaultDirName={autopf}\<%=meta.filename%>
DefaultGroupName=<%=meta.title%>
UninstallDisplayIcon={app}\<%=meta.filename%>.exe
Compression=lzma2
SolidCompression=yes
OutputDir=<%=output%>
OutputBaseFilename=<%=meta.filename%>_<%=meta.version%>
[Files]
Source: *; Excludes: "*.iss,*.log"; DestDir: "{app}"; Flags: recursesubdirs
[Icons]
Name: "{group}\<%=meta.title%>"; Filename: "{app}\<%=meta.filename%>.exe"
Name: "{group}\Uninstall <%=meta.title%>"; Filename: "{uninstallexe}"