Files
gulp-haxetool/haxetool/project.js

527 lines
14 KiB
JavaScript
Executable File

const gulp = require('gulp');
const path = require('path');
const os = require('os');
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 InnoSetup = require('./innosetup');
const Env = require('./env');
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');
const rename = require('gulp-rename');
const template = require('lodash.template');
const tar = require('gulp-tar');
const gzip = require('gulp-gzip');
const zip = require('gulp-zip');
const streamToPromise = (stream) => {
return new Promise((resolve, reject) => {
stream.on("end", resolve);
stream.on("error", reject);
});
};
/**
*
*/
class Target {
constructor(config, platform) {
this.config = config;
this.platform = platform;
this.target = 'target';
}
prepare() {
return Promise.resolve();
}
call() {
throw 'Not Implemented';
}
resolveTargetPath(platform = this.platform) {
return path.resolve(this.target, this.config.name, platform)
}
get targetPath() {
return this.resolveTargetPath();
}
}
/**
*
*/
class Builder extends Target {
constructor(config, platform, buildSystem, debug) {
super(config, platform);
this.buildSystem = buildSystem;
this.debug = debug;
}
static register(buildSystem, builder) {
Builder.factory[buildSystem] = builder;
}
static new(config, platform, buildSystem, debug) {
return new Builder.factory[buildSystem](config, platform, buildSystem, debug);
}
}
Builder.factory = {};
/**
*
*/
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.platform === Platform.ANDROID) {
result = result
.then(() => this.android.prepare())
.then(() => this.android.activate())
.then(() => this.android.sdkmanager([
'tools',
'platform-tools',
'build-tools;27.0.3',
//'platforms;android-19',
'platforms;android-26',
//'ndk-bundle',
'lldb;3.1',
'cmake;3.6.4111459',
]));
const AndroidSDK = this.android.android_home;
const AndroidNDK = this.android.ndk_home;
const answer = `echo "${[AndroidSDK, AndroidNDK].join('\n')}"`;
result = result.then(() => {
return exec('.', [answer, '|', this.haxe.haxelibBin, 'run', 'openfl', 'setup', this.platform].join(' '));
});
}
}
return result;
}
call() {
const target = this.targetPath;
switch (this.buildSystem) {
case BuildSystem.OPENFL:
return this.haxe.openfl('build', this.platform, this.config, this.debug)
.then(result => streamToPromise(result.pipe(vfs.dest(target))));
case BuildSystem.HAXE:
return this.haxe.build(this.platform, this.config, this.debug)
.then(result => streamToPromise(result.pipe(vfs.dest(target))));
}
}
}
Builder.register(BuildSystem.HAXE, HaxeBuilder);
Builder.register(BuildSystem.OPENFL, HaxeBuilder);
/**
*
*/
class Packer extends Target {
call() {
throw 'Not Implemented';
}
static register(platform, name, packer) {
if (!Packer.factory[platform]) Packer.factory[platform] = {};
Packer.factory[platform][name] = packer;
}
static new(config, platform, name) {
return new Packer.factory[platform][name](config);
}
}
Packer.factory = {};
/**
*
*/
class FlashHTMLPacker extends Packer {
constructor(config) {
super(config, Platform.FLASH);
}
call() {
const target = this.targetPath;
const indexTemplate = template(fs.readFileSync(path.resolve(__dirname, '..', 'template/flash/index.html')));
const index = indexTemplate(this.config);
fs.writeFileSync(path.resolve(target, 'index.html'), index);
fs.copyFileSync(path.resolve(__dirname, '..', 'template/flash/swf.js'), path.resolve(target, 'swf.js'));
return Promise.resolve();
}
}
FlashHTMLPacker.NAME = 'html';
Packer.register(Platform.FLASH, FlashHTMLPacker.NAME, FlashHTMLPacker);
/**
*
*/
class LinuxDEBPacker extends Packer {
constructor(config) {
super(config, Platform.LINUX);
}
call() {
const target = this.targetPath;
const buildDir = path.join(os.tmpdir(), 'build', this.config.name, 'debian');
const iconfile = `${this.config.meta.filename}${path.extname(this.config.meta.icon)}`;
const desktopTemplate = template(fs.readFileSync(path.resolve(__dirname, '..', 'template/linux/app.desktop')));
const desktop = desktopTemplate(Object.assign({iconfile:iconfile}, this.config));
fse.ensureDirSync(`${buildDir}/usr/share/applications`);
fs.writeFileSync(`${buildDir}/usr/share/applications/${this.config.meta.filename}.desktop`, desktop);
fse.copySync(`${target}`, `${buildDir}/usr/share/${this.config.meta.filename}/`);
fse.copySync(this.config.meta.icon, `${buildDir}/usr/share/${this.config.meta.filename}/${iconfile}`);
return gulp.src(`${buildDir}/*`)
.pipe(deb({
package: this.config.meta.filename,
version: this.config.meta.version,
section: 'base',
priority: 'optional',
architecture: 'all',
maintainer: this.config.meta.author,
description: this.config.meta.title,
changelog: [],
postinst: [
'if [ "$1" = "configure" ] && [ -x "`which update-menus 2>/dev/null`" ] ; then\n' +
' update-menus\n' +
'fi'
],
_target: '/',
_out: this.resolveTargetPath('debian'),
_clean: true,
_verbose: false
}));
}
}
LinuxDEBPacker.NAME = 'deb';
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);
/**
*
*/
class Runner extends Target {
constructor(config, platform, debug) {
super(config, platform);
this.debug = debug;
}
log(stream) {
return stream
.pipe(tail(debug.log))
.pipe(rename('out.log'))
.pipe(gulp.dest(this.targetPath));
}
static register(platform, runner) {
Runner.factory[platform] = runner;
}
static new(config, platform, debug) {
return new Runner.factory[platform](config, debug);
}
}
Runner.factory = {};
/**
*
*/
class FlashRunner extends Runner {
constructor(config, debug) {
super(config, Platform.FLASH, debug);
this.player = new FlashPlayer(debug);
}
prepare() {
return this.player.prepare();
}
call() {
const target = this.targetPath;
const filename = path.resolve(target, this.config.meta.filename+'.swf');
const result = this.player.run(filename, {cwd: target, verbosity: 0});
return this.log(result);
}
}
Runner.register(Platform.FLASH, FlashRunner);
/**
*
*/
class Html5Runner extends Runner {
constructor(config, debug) {
super(config, Platform.HTML5, debug);
}
call() {
return gulp.src(this.targetPath)
.pipe(webserver({
// ToDo: config?
host: '0.0.0.0', port: 3000,
open: true,
fallback: 'index.html'
}));
}
}
Runner.register(Platform.HTML5, Html5Runner);
/**
*
*/
class LinuxRunner extends Runner {
constructor(config, debug) {
super(config, Platform.LINUX, debug);
}
call() {
const target = this.targetPath;
const filename = path.resolve(target, this.config.meta.filename);
const result = gulp.src(filename).pipe(run("./<%=file.basename%>", {cwd: target, verbosity: 0}));
return this.log(result);
}
}
Runner.register(Platform.LINUX, LinuxRunner);
/**
*
*/
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);
/**
*
*/
class NekoRunner extends Runner {
constructor(config, debug) {
super(config, Platform.NEKO, debug);
this.neko = new Neko();
}
prepare() {
return this.neko.prepare();
}
call() {
const target = this.targetPath;
const filename = path.resolve(target, this.config.meta.filename+'.n');
const result = this.neko.run(filename, {cwd: target, verbosity: 0});
return this.log(result);
}
}
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;
return this.log(gulp.src(`${target}/${this.config.meta.filename}_*.apk`)
.pipe(this.android.apk())
.pipe(this.android.install())
.pipe(this.android.start())
.pipe(this.android.logcat())
);
}
}
Runner.register(Platform.ANDROID, AndroidRunner);
/**
*
*/
class Project {
constructor(buildSystem, platforms, config, prepare) {
this.buildSystem = buildSystem;
this.platforms = Array.isArray(platforms) ? platforms : [platforms];
this.config = config;
this.prepare = prepare;
}
build(platform, debug) {
const builder = Builder.new(this.config, platform, this.buildSystem, debug);
const tasks = [builder.prepare.bind(builder)];
if (this.prepare) tasks.push(this.prepare);
tasks.push(builder.call.bind(builder));
return tasks;
}
run(platform, debug) {
const runner = Runner.new(this.config, platform, debug);
return [
runner.prepare.bind(runner),
runner.call.bind(runner),
];
}
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),
];
}
bind(module, external_gulp /* ToDo: spike */) {
const _gulp = (external_gulp || gulp);
for (const platform of this.platforms) {
module.exports[`${this.config.name}:${platform}:build`] = _gulp.series(this.build(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]) {
for (const name of Object.keys(Packer.factory[platform])) {
module.exports[`${this.config.name}:${platform}:${name}`] = _gulp.series(this.pack(platform, name));
}
}
}
return this;
}
}
Project.BuildSystem = BuildSystem;
Project.Platform = Platform;
Project.Config = Config;
Project.Builder = Builder;
Project.Runner = Runner;
module.exports = Project;