From 69c2b735a0fe48c56fb4c36f671d4e52f2ebf643 Mon Sep 17 00:00:00 2001 From: shmyga Date: Sun, 17 Dec 2017 22:29:16 +0300 Subject: [PATCH] [build] added gulp builder --- .gitignore | 5 +- WORK.md | 25 ++ build/client.js | 48 ++++ build/prepare.js | 39 +++ build/server.js | 63 +++++ build/version.js | 2 + config.example.json | 7 + gulpfile.js | 31 +++ package.json | 31 +++ project.xml | 10 +- server.hxml | 2 + src/client/haxe/ru/m/tankz/Client.hx | 5 +- src/client/haxe/ru/m/tankz/Const.hx | 19 ++ src/server/haxe/ru/m/tankz/server/Server.hx | 2 + tasks/adobeAir.js | 259 ++++++++++++++++++++ tasks/android.js | 72 ++++++ tasks/debug.js | 41 ++++ tasks/download.js | 24 ++ tasks/exec.js | 32 +++ tasks/flashplayer.js | 171 +++++++++++++ tasks/haxe.js | 251 +++++++++++++++++++ tasks/neko.js | 35 +++ tasks/sdk.js | 77 ++++++ tasks/tail.js | 26 ++ 24 files changed, 1273 insertions(+), 4 deletions(-) create mode 100644 WORK.md create mode 100755 build/client.js create mode 100755 build/prepare.js create mode 100755 build/server.js create mode 100644 build/version.js create mode 100755 config.example.json create mode 100755 gulpfile.js create mode 100755 package.json create mode 100755 src/client/haxe/ru/m/tankz/Const.hx create mode 100755 tasks/adobeAir.js create mode 100644 tasks/android.js create mode 100755 tasks/debug.js create mode 100644 tasks/download.js create mode 100755 tasks/exec.js create mode 100755 tasks/flashplayer.js create mode 100755 tasks/haxe.js create mode 100644 tasks/neko.js create mode 100755 tasks/sdk.js create mode 100755 tasks/tail.js diff --git a/.gitignore b/.gitignore index 2ec2183..ffec2b7 100755 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,7 @@ out/ *.iws *.ids *.stackdump -.idea/ \ No newline at end of file +.idea/ +config.json +package-lock.json +/node_modules \ No newline at end of file diff --git a/WORK.md b/WORK.md new file mode 100644 index 0000000..5e24e5a --- /dev/null +++ b/WORK.md @@ -0,0 +1,25 @@ +* build + * gulp + +* deploy + * capistrano + +* ui + * login frame + * select person frame (autoselect) + * game mode frame (single, start server, find server) + * game frame + +* engine + * config + * map + * tanks + * bullets + * boxes + * map changes + * bonuses + +* proto + +* common + * single game \ No newline at end of file diff --git a/build/client.js b/build/client.js new file mode 100755 index 0000000..23b8871 --- /dev/null +++ b/build/client.js @@ -0,0 +1,48 @@ +"use strict"; +const gulp = require('gulp'); +const yargs = require('yargs'); +const tail = require('../tasks/tail'); +const Haxe = require('../tasks/haxe'); +const FlashPlayer = require('../tasks/flashplayer'); +const version = require('./version'); +const dateformat = require('dateformat'); +const prepare = require('./prepare'); +const debug = require('../tasks/debug'); + + +const build = (params) => function build() { + params = params || {}; + const argv = yargs.argv; + const values = { + build: dateformat(new Date(), 'yyyy-mm-dd HH:MM:ss'), + }; + let outputFile = 'tankz.swf'; + if (params.outputFile) { + outputFile = params.outputFile; + } + if (argv.dev) { + values.dev = true; + if (!values['dev.address']) values['dev.address'] = argv['dev-address'] || 'localhost'; + if (!values['dev.port']) values['dev.port'] = argv['dev-port'] || 5000; + } + return gulp.src('.') + .pipe(new Haxe().openfl({ + command: 'build', + platform: 'flash', + version: version, + values: values, + outputFile: outputFile, + })) + .pipe(gulp.dest('target')); +}; + +const test = (build) => function test() { + const argv = yargs.argv; + return build() + .pipe(new FlashPlayer().run(argv.dev)) + .pipe(tail(debug.log)); +}; + + +exports['client'] = gulp.series(prepare(Haxe.ID), build()); +exports['client:test'] = gulp.series(prepare(Haxe.ID, FlashPlayer.ID), test(build())); diff --git a/build/prepare.js b/build/prepare.js new file mode 100755 index 0000000..b6120d6 --- /dev/null +++ b/build/prepare.js @@ -0,0 +1,39 @@ +const AdobeAir = require('../tasks/adobeAir'); +const Haxe = require('../tasks/haxe'); +const FlashPlayer = require('../tasks/flashplayer'); + +const packages = [ + {name:'openfl', version:'6.0.1'}, + {name:'lime', version:'5.3.0'}, + 'promhx', + 'protohx', + 'haxework', +]; + + +const prepareOne = (value) => { + switch (value) { + case Haxe.ID: + const haxe = new Haxe(); + return haxe.prepare().then(() => haxe.install(packages)); + case AdobeAir.ID: + return new AdobeAir().prepare(); + case FlashPlayer.ID: + return new FlashPlayer().prepare(); + default: + throw Error(`Unknown target: ${value}`) + } +}; + +const prepare = (...targets) => function prepare() { + const tasks = targets.map((target) => prepareOne(target)); + return Promise.all(tasks); +}; + + +const update = () => { + return new Haxe().upgrade(); +}; + +module.exports = prepare; +module.exports.update = update; diff --git a/build/server.js b/build/server.js new file mode 100755 index 0000000..c51f163 --- /dev/null +++ b/build/server.js @@ -0,0 +1,63 @@ +"use strict"; +const gulp = require('gulp'); +const yargs = require('yargs'); +const tail = require('../tasks/tail'); +const Haxe = require('../tasks/haxe'); +const FlashPlayer = require('../tasks/flashplayer'); +const Neko = require('../tasks/neko'); +const version = require('./version'); +const dateformat = require('dateformat'); +const prepare = require('./prepare'); +const debug = require('../tasks/debug'); + + +const build = (params) => function build() { + params = params || {}; + const argv = yargs.argv; + const values = { + build: dateformat(new Date(), 'yyyy-mm-dd h:MM:ss'), + }; + let outputFile = 'tankz.n'; + if (params.outputFile) { + outputFile = params.outputFile; + } + if (argv.dev) { + values.dev = true; + if (!values['dev.address']) values['dev.address'] = argv['dev-address'] || 'localhost'; + if (!values['dev.port']) values['dev.port'] = argv['dev-port'] || 5000; + } + return gulp.src('.') + .pipe(new Haxe().build({ + platform: 'neko', + version: version, + values: values, + lib: [ + 'protohx', + 'orm', + 'haxework', + ], + cp: [ + 'src/common/haxe', + 'src/server/haxe', + 'src-gen/haxe', + ], + macro: [ + `CompilationOption.set('debug.address','${values['dev.address']}')`, + `CompilationOption.set('debug.port','${values['dev.port']}')`, + ], + main: 'ru.m.tankz.server.Server', + outputFile: outputFile, + })) + .pipe(gulp.dest('target')); +}; + +const test = (build) => function test() { + const argv = yargs.argv; + return build() + .pipe(new Neko().run(argv.dev)) + .pipe(debug()); +}; + + +exports['server'] = gulp.series(prepare(Haxe.ID), build()); +exports['server:test'] = gulp.series(prepare(Haxe.ID, FlashPlayer.ID), test(build())); diff --git a/build/version.js b/build/version.js new file mode 100644 index 0000000..2e7c713 --- /dev/null +++ b/build/version.js @@ -0,0 +1,2 @@ +const packageInfo = require('../package.json'); +module.exports = packageInfo.version; \ No newline at end of file diff --git a/config.example.json b/config.example.json new file mode 100755 index 0000000..ce6ff44 --- /dev/null +++ b/config.example.json @@ -0,0 +1,7 @@ +{ + "SdkDir": "C:\\sdk", + "SSH": { + "PrivateKey": null, + "Passphrase": null + } +} diff --git a/gulpfile.js b/gulpfile.js new file mode 100755 index 0000000..8e0ad9d --- /dev/null +++ b/gulpfile.js @@ -0,0 +1,31 @@ +"use strict"; +const os = require('os'); +const gulp = require('gulp'); +const clean = require('gulp-clean'); +const Sdk = require('./tasks/sdk'); +const Config = require('./config.json'); +const prepare = require('./build/prepare'); + +if (Config.SdkDir) { + Sdk.dir = Config.SdkDir; +} + +exports.clean = () => { + return gulp.src('target/*', {read: false}).pipe(clean()); +}; + +const merge = (value) => { + if (typeof value === 'string') { + value = require(value); + } + for (let key in value) if (value.hasOwnProperty(key)) { + exports[key] = value[key]; + } +}; + +exports.update = prepare.update; +merge('./build/prepare'); +merge('./build/client'); +merge('./build/server'); + +exports.default = gulp.series(exports.clean, exports.client, exports.server); \ No newline at end of file diff --git a/package.json b/package.json new file mode 100755 index 0000000..071ac31 --- /dev/null +++ b/package.json @@ -0,0 +1,31 @@ +{ + "name": "tankz", + "version": "0.0.1", + "private": true, + "devDependencies": { + "async": "^2.1.2", + "dateformat": "^2.0.0", + "fs-extra": "^3.0.1", + "gulp": "github:gulpjs/gulp#4.0", + "gulp-add-src": "^0.2.0", + "gulp-cat": "^0.3.3", + "gulp-clean": "^0.3.2", + "gulp-exec": "^2.1.3", + "gulp-gunzip": "^1.0.0", + "gulp-mark": "0.0.2", + "gulp-replace-task": "^0.11.0", + "gulp-sequence": "^0.4.6", + "gulp-ssh": "^0.6.0", + "gulp-untar": "0.0.6", + "gulp-unzip": "^0.2.0", + "merge-stream": "^1.0.1", + "progress": "^2.0.0", + "request": "^2.81.0", + "request-progress": "^3.0.0", + "tail": "^1.2.2", + "tmp-file": "^2.0.1", + "vinyl-source-stream": "^1.1.0", + "yargs": "^8.0.2" + }, + "dependencies": {} +} diff --git a/project.xml b/project.xml index 727e606..52717d7 100755 --- a/project.xml +++ b/project.xml @@ -2,6 +2,7 @@ + @@ -14,8 +15,13 @@ - - + +
+ + + + +
\ No newline at end of file diff --git a/server.hxml b/server.hxml index 5b5c8d2..d5c59b2 100755 --- a/server.hxml +++ b/server.hxml @@ -5,5 +5,7 @@ -cp src/common/haxe -cp src/server/haxe -cp src-gen/haxe +--macro CompilationOption.set('debug.address','localhost') +--macro CompilationOption.set('debug.port','5000') -neko target/server.n # -D proto_debug \ No newline at end of file diff --git a/src/client/haxe/ru/m/tankz/Client.hx b/src/client/haxe/ru/m/tankz/Client.hx index 77c53c3..5554846 100755 --- a/src/client/haxe/ru/m/tankz/Client.hx +++ b/src/client/haxe/ru/m/tankz/Client.hx @@ -31,7 +31,10 @@ class Client implements IConnectionHandler { #if flash L.push(new JSLogger()); #end - L.d(TAG, Meta.getVersion()); + Const.init(); + L.d(TAG, "Debug: " + Const.DEBUG); + L.i(TAG, "Version: " + Const.VERSION); + L.i(TAG, "Build: " + Const.BUILD); new Client(); } diff --git a/src/client/haxe/ru/m/tankz/Const.hx b/src/client/haxe/ru/m/tankz/Const.hx new file mode 100755 index 0000000..cda81df --- /dev/null +++ b/src/client/haxe/ru/m/tankz/Const.hx @@ -0,0 +1,19 @@ +package ru.m.tankz; + +import flash.Lib; +import flash.system.Capabilities; + + +class Const { + public static var FPS:Int; + public static var BUILD:String; + public static var VERSION:String; + public static var DEBUG:Bool; + + public static function init():Void { + FPS = Lib.current.stage.application.config.fps; + BUILD = CompilationOption.get("build"); + VERSION = Lib.current.stage.application.config.version; + DEBUG = Capabilities.isDebugger; + } +} \ No newline at end of file diff --git a/src/server/haxe/ru/m/tankz/server/Server.hx b/src/server/haxe/ru/m/tankz/server/Server.hx index 6f9c2ad..68031c8 100755 --- a/src/server/haxe/ru/m/tankz/server/Server.hx +++ b/src/server/haxe/ru/m/tankz/server/Server.hx @@ -1,5 +1,6 @@ package ru.m.tankz.server; +import haxework.log.SocketLogger; import ru.m.core.connect.IConnection.IPacketBuilder; import haxework.provider.Provider; import haxework.log.TraceLogger; @@ -37,6 +38,7 @@ class Server extends ThreadServer { public static function main() { L.push(new TraceLogger()); + L.push(new SocketLogger()); L.d(TAG, "Running"); Provider.set(IPacketBuilder, new PacketBuilder()); var wserver = new Server(); diff --git a/tasks/adobeAir.js b/tasks/adobeAir.js new file mode 100755 index 0000000..241ca7f --- /dev/null +++ b/tasks/adobeAir.js @@ -0,0 +1,259 @@ +"use strict"; +const fs = require('fs'); +const tmp = require('tmp-file'); +const gutil = require('gulp-util'); +const exec = require('./exec'); +const download = require('./download'); +const unzip = require('gulp-unzip'); +const gulp = require('gulp'); +const through = require('through2'); +const mark = require('gulp-mark'); +const replace = require('gulp-replace-task'); +const col = gutil.colors; +const Sdk = require('./sdk'); + + +class AdobeAir extends Sdk { + + get adtBin() { + return `${this.path}/bin/adt.bat`; + } + + get adlBin() { + return `${this.path}/bin/adl.exe`; + } + + static buildAdtArgs(params, descriptor, keystore, files, output) { + //const quote = (value) => `"${value}"`; + const quote = (value) => value; // ToDo: + const command = ['-package']; + const target = params.target || 'native'; + if (target !== 'native') command.push('-target', target); //ToDo: adobe bleat' (target param position in native and apk-captive-runtime targets) + if (params.useLegacyAOT) { + command.push('-useLegacyAOT', 'yes'); + } + if (params.profile) { + command.push('-provisioning-profile', quote(params.profile)); + } + if (keystore) { + command.push('-storetype', 'PKCS12'); + command.push('-keystore', keystore.path); + command.push('-storepass', keystore.storepass); + } + if (params.target === 'native') { + command.push('-tsa', 'http://sha256timestamp.ws.symantec.com/sha256/timestamp'); + command.push('-target', target); + } + command.push(quote(output), quote(descriptor.path)); + if (params.extdir) { + command.push('-extdir', quote(params.extdir)); + } + if (params.content) { + for (let k in params.content) if (params.content.hasOwnProperty(k)) { + command.push('-C', quote(k), quote(params.content[k])); + } + } + for (let file of files) { + command.push('-C', quote(file.base), quote(file.path.split(file.base)[1])); + } + return command; + }; + + constructor(version) { + super(AdobeAir.ID, version || AdobeAir.VERSION); + } + + get prepared() { + return fs.existsSync(`${this.path}/air-sdk-description.xml`); + } + + get link() { + return `http://airdownload.adobe.com/air/win/download/${this.version}/AIRSDK_Compiler.zip`; + } + + adt(path, args) { + return exec(path, [this.adtBin].concat(args).join(' ')); + } + + adl(path, args) { + return exec(path, [this.adlBin].concat(args).join(' ')); + } + + pack(params) { + const files = []; + let descriptor = null; + let keystore = null; + let stream = null; + + const bufferContents = (file, enc, callback) => { + // ToDo: check file not stream + switch (file.mark) { + case 'descriptor': + descriptor = file; + break; + case 'keystore': + keystore = file; + break; + default: + files.push(file); + } + callback(); + }; + + const endStream = (callback) => { + gutil.log(this.tag, col.cyan('adt', 'build', params.target), '=>', col.magenta(params.outputFile)); + if (!descriptor) { + stream.emit('error', new gutil.PluginError({plugin: this.name, message: 'descriptor is not defined'})); + callback(); + return; + } + const tmpFile = tmp.generateFile(); + const command = AdobeAir.buildAdtArgs(params, descriptor, keystore, files, tmpFile.path); + this.adt('.', command) + .then(() => { + stream.push(new gutil.File({ + path: params.outputFile, + contents: fs.createReadStream(tmpFile.path) + })); + callback(); + }) + .catch((error) => { + stream.emit('error', new gutil.PluginError({plugin: this.name, message: error})); + callback(); + }); + }; + + return stream = through.obj(bufferContents, endStream); + } + + install() { + let stream = null; + const bufferContents = (file, enc, callback) => { + gutil.log(this.tag, col.cyan('adt', 'install'), col.magenta(file.name)); + const command = ['-installApp', '-platform', 'android', '-package', file.path]; + this.adt('.', command) + .then(() => { + stream.push(file); + callback(); + }) + .catch((error) => { + stream.emit('error', new gutil.PluginError({plugin: this.name, message: error})); + callback(); + }); + }; + return stream = through.obj(bufferContents); + } + + launch(appid) { + let stream = null; + const bufferContents = (file, enc, callback) => { + gutil.log(this.tag, col.cyan('adt', 'launch'), col.magenta(appid)); + const command = ['-launchApp', '-platform', 'android', '-appid', appid]; + this.adt('.', command) + .then(() => { + stream.push(file); + callback(); + }) + .catch((error) => { + stream.emit('error', new gutil.PluginError({plugin: this.name, message: error})); + callback(); + }); + }; + return stream = through.obj(bufferContents); + } + + test(params) { + let root = null; + let descriptor = null; + let stream = null; + + const bufferContents = (file, enc, callback) => { + // ToDo: check file not stream + switch (file.mark) { + case 'descriptor': + descriptor = file; + break; + default: + root = file; + } + callback(); + }; + + const endStream = (callback) => { + gutil.log(this.tag, col.cyan('adl')); + //const command = AdobeAir.buildAdtArgs(params, files, tmpFile.path); + const command = []; + if (params.profile) command.push('-profile', params.profile); + if (params.extdir) command.push('-extdir', params.extdir); + if (!descriptor) { + stream.emit('error', new gutil.PluginError({plugin: this.name, message: 'descriptor is not defined'})); + callback(); + return; + } + command.push(descriptor.path); + command.push(root.path); + if (params.args) { + command.push('--'); + for (let key of Object.keys(params.args)) { + const value = params.args[key]; + if (value === true) { + command.push(`-${key}`); + } else if (value) { + command.push(`-${key}`, `${value}`); + } + } + } + this.adl('.', command) + .then(() => { + stream.emit('end'); + callback(); + }) + .catch((error) => { + stream.emit('error', new gutil.PluginError({plugin: this.name, message: error})); + callback(); + }); + + //callback(); + stream.push(root); + }; + + return stream = through.obj(bufferContents, endStream); + } + + static descriptor(template, values) { + const tmpFile = tmp.generateFile(); + const patterns = []; + for (let k in values) if (values.hasOwnProperty(k)) { + patterns.push({match: k, replacement: values[k]}); + } + return gulp.src(template) + .pipe(replace({patterns: patterns})) + .pipe(mark.set('descriptor')) + .pipe(gulp.dest(tmpFile.path)); + } + + static keystore(keystore, storepass) { + return gulp.src(keystore).pipe(through.obj((file, enc, callback) => { + file.mark = 'keystore'; + file.storepass = storepass; + callback(null, file); + })); + } + + static pack(params) { + return new AdobeAir().pack(params); + } + + static test(params) { + return new AdobeAir().test(params); + } +} + +AdobeAir.ID = 'adobe-air'; + +AdobeAir.VERSION_26 = '26.0'; +AdobeAir.VERSION_25 = '25.0'; +AdobeAir.VERSION_24 = '24.0'; +AdobeAir.VERSION = AdobeAir.VERSION_26; + +module.exports = AdobeAir; \ No newline at end of file diff --git a/tasks/android.js b/tasks/android.js new file mode 100644 index 0000000..cfe4bff --- /dev/null +++ b/tasks/android.js @@ -0,0 +1,72 @@ +"use strict"; +const exec = require('./exec'); +const fs = require('fs'); +const through = require('through2'); + + +const Android = { + adb: (args) => { + const adbBin = `${process.env.ANDROID_HOME}/platform-tools/adb.exe`; + 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(`${process.env.ANDROID_HOME}/build-tools`).forEach(file => { + buildToolsVersion = file; + }); + const aaptBin = `${process.env.ANDROID_HOME}/build-tools/${buildToolsVersion}/aapt.exe`; + return exec('.', [aaptBin].concat(args).join(' ')); + }, + + apk: () => { + return through.obj(function(file, enc, callback) { + Android.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: () => { + return through.obj(function(file, enc, callback) { + Android.adb(['install', '-r', file.path]).then(() => { + this.push(file); + callback(); + }); + }); + }, + + start: () => { + return through.obj((file, enc, callback) => { + const name = `${file.package}/${file.activity}`; + Android.adb(['shell', 'am', 'start', '-n', name]).then(() => callback()); + }); + } +}; + + +module.exports = Android; \ No newline at end of file diff --git a/tasks/debug.js b/tasks/debug.js new file mode 100755 index 0000000..37f3efb --- /dev/null +++ b/tasks/debug.js @@ -0,0 +1,41 @@ +const gulp = require('gulp'); +const gutil = require('gulp-util'); +const col = gutil.colors; +const net = require('net'); +const through = require('through2'); + +const color = (level) => { + return { + '[DEBUG]': col.white, + '[INFO]': col.cyan, + '[ERROR]': col.red, + '[WARNING]': col.yellow, + }[level] || col.reset; +}; + +const log = (line) => { + const result = line.split(' '); + console.log(col.gray(result[0]) + ' ' + color(result[1])(result.slice(1).join(' '))); +}; + +module.exports = () => { + return through.obj(function (file, enc, callback) { + const server = net.createServer((socket) => { + socket.on("data", (data) => { + const lines = data.toString().split('\n'); + for (let line of lines) if (line.length > 2) { + log(line.substr(2)); + } + }); + socket.on("close", () => { + socket.destroy(); + server.close(); + this.emit('end'); + callback(); + }); + }); + server.listen(5000); + }) +}; + +module.exports.log = log; \ No newline at end of file diff --git a/tasks/download.js b/tasks/download.js new file mode 100644 index 0000000..3fd43cc --- /dev/null +++ b/tasks/download.js @@ -0,0 +1,24 @@ +'use strict'; +const path = require('path'); +const request = require('request'); +const progress = require('request-progress'); +const source = require('vinyl-source-stream'); +const gutil = require('gulp-util'); + + +module.exports = (url) => { + const name = path.basename(url); + let stream = null; + return stream = progress(request({url: url, encoding: null}, function(error, response, body) { + try { + if (error) throw error; + if (response.statusCode !== 200) throw `${response.statusCode} ${response.statusMessage}`; + } catch (e) { + this.emit('error', new gutil.PluginError({plugin: 'download', message: e})); + } + })) + .on('error', (e) => stream.emit('error', e)) + .on('progress', (p) => stream.emit('progress', p)) + .pipe(source(name)); +}; + diff --git a/tasks/exec.js b/tasks/exec.js new file mode 100755 index 0000000..9a78b4f --- /dev/null +++ b/tasks/exec.js @@ -0,0 +1,32 @@ +const gutil = require('gulp-util'); +const child_process = require('child_process'); +const async = require('async'); +const col = gutil.colors; +const Promise = require("bluebird"); + + +const TAG = col.green('[exec]'); + +const queue = async.queue((task, done) => { + //gutil.log(TAG, col.magenta(task.command)); + //process.chdir(task.dir); + child_process.exec(task.command, {cwd: task.dir, maxBuffer: 1024 * 5000}, (err, stdout, stderr) => { + if (err) { + task.failure(stderr || stdout || err); + } else { + task.success({stdout: stdout, stderr: stderr}); + } + done(); + }); +}); + +module.exports = (dir, command) => { + return new Promise((success, failure) => { + queue.push({ + dir: dir, + command: command, + success: success, + failure: failure, + }); + }); +}; \ No newline at end of file diff --git a/tasks/flashplayer.js b/tasks/flashplayer.js new file mode 100755 index 0000000..fdbaa97 --- /dev/null +++ b/tasks/flashplayer.js @@ -0,0 +1,171 @@ +const path = require('path'); +const fs = require('fs'); +const fse = require('fs-extra'); +const os = require('os'); +const gunzip = require('gulp-gunzip'); +const untar = require('gulp-untar'); +const through = require('through2'); +const gulp = require('gulp'); +const gutil = require('gulp-util'); +const Sdk = require('./sdk'); +const exec = require('./exec'); +const col = gutil.colors; + + +class FlashPlayer extends Sdk { + + constructor(version) { + super(FlashPlayer.ID, version || FlashPlayer.VERSION); + } + + playerPath(debug) { + const v = this.version.split('.'); + const dir = `${v[0]}_${v[1]}_r${v[2]}_${v[3]}`; + return `${this.path}/${dir}${debug ? '_debug': ''}`; + } + + prepare() { + let p = super.prepare(); + if (!this.prepared && os.type() === 'Linux') { + p = p.then(() => { + let arch = null; + if (os.arch() === 'ia32') { arch = 'i386'} + if (os.arch() === 'x64') { arch = 'x86_64'} + + const extract = (debug) => { + const v = this.version.split('.'); + const playerPath = this.playerPath(debug); + const archive = `${playerPath}/flashplayer${v[0]}_${v[1]}r${v[2]}_${v[3]}_linux_sa${debug ? '_debug' : ''}.${arch}.tar.gz`; + return gulp.src(archive) + .pipe(gunzip()).pipe(untar()) + .pipe(gulp.dest(playerPath)); + }; + + return new Promise((success, fail) => { + extract().on('end', () => { + extract(true).on('end', success).on('error', fail) + }).on('error', fail); + }) + }) + } + return p; + } + + get prepared() { + try { + return fs.existsSync(`${this.flashPlayerBin()}`); + } catch (e) { + return false; + } + } + + get link() { + return `https://fpdownload.macromedia.com/pub/flashplayer/installers/archive/fp_${this.version}_archive.zip`; + } + + flashPlayerBin(debug) { + if (os.type() === 'Windows_NT') { + const v = this.version.split('.'); + const playerName = `flashplayer${v[0]}_${v[1]}r${v[2]}_${v[3]}_win_sa${debug ? '_debug' : ''}.exe`; + return `${this.playerPath(debug)}/${playerName}`; + } else if (os.type() === 'Linux') { + const binPath = `${this.playerPath(debug)}/${debug ? 'flashplayerdebugger': 'flashplayer'}`; + fs.chmodSync(binPath, 0o755); + return binPath; + } else { + throw `Unsupported os '${os.type()}'`; + } + } + + static get flashPlayerDir() { + if (os.type() === 'Windows_NT') { + return `${process.env.APPDATA}/Macromedia/Flash Player`; + } else if (os.type() === 'Linux') { + return `${os.homedir()}/.macromedia/Flash_Player`; + } + } + + static get log() { + return `${this.flashPlayerDir}/Logs/flashlog.txt`; + } + + static enableLog() { + const filename = `${os.homedir()}/mm.cfg`; + const value = 'TraceOutputFileEnable=1'; + if (fs.exists(filename)) { + const data = fs.readFileSync(filename); + if (data.indexOf(value) === -1) { + fs.appendFileSync(filename, `${value}\n`); + } + } else { + fs.writeFileSync(filename, `${value}\n`); + } + } + + static trust(value) { + const filename = `${this.flashPlayerDir}/#Security/FlashPlayerTrust/gulp.cfg`; + if (fs.exists(filename)) { + const data = fs.readFileSync(filename); + if (data.indexOf(value) === -1) { + fs.appendFileSync(filename, `${value}\n`); + } + } else { + if (!fs.exists(path.dirname(filename))) { + fse.ensureDirSync(path.dirname(filename)); + } + fs.writeFileSync(filename, `${value}\n`); + } + } + + run(debug) { + let stream = null; + const bufferContents = (file, enc, callback) => { + gutil.log(this.tag, col.cyan("run"), col.magenta(file.path)); + FlashPlayer.trust(file.path); + FlashPlayer.enableLog(); + exec('.', [this.flashPlayerBin(debug), file.path].join(' ')).then(() => { + stream.emit('end'); + //callback(); + }).catch((error) => { + stream.emit('end'); + //callback(); + stream.emit('error', error); + }); + //stream.push(file); + // ToDo: watch when file is exists + // or create log file in FlashPlayer.enableLog()? + stream.push(new gutil.File({ + path: FlashPlayer.log + })); + }; + + return stream = through.obj(bufferContents); + } +} + +FlashPlayer.ID = "flashplayer"; + +FlashPlayer.VERSION_11_2_202_644 = '11.2.202.644'; +FlashPlayer.VERSION_11 = FlashPlayer.VERSION_11_2_202_644; + +FlashPlayer.VERSION_24_0_0_186 = '24.0.0.186'; +FlashPlayer.VERSION_24_0_0_194 = '24.0.0.194'; +FlashPlayer.VERSION_24_0_0_221 = '24.0.0.221'; +FlashPlayer.VERSION_24 = FlashPlayer.VERSION_24_0_0_221; + +FlashPlayer.VERSION_25_0_0_127 = '25.0.0.127'; +FlashPlayer.VERSION_25_0_0_148 = '25.0.0.148'; +FlashPlayer.VERSION_25_0_0_163 = '25.0.0.163'; +FlashPlayer.VERSION_25_0_0_117 = '25.0.0.171'; +FlashPlayer.VERSION_25 = FlashPlayer.VERSION_25_0_0_117; + +FlashPlayer.VERSION_26_0_0_131 = '26.0.0.131'; +FlashPlayer.VERSION_26 = FlashPlayer.VERSION_26_0_0_131; + +if (os.type() === 'Linux' && os.arch() === 'ia32') { //Linux i386 + FlashPlayer.VERSION = FlashPlayer.VERSION_11; +} else { + FlashPlayer.VERSION = FlashPlayer.VERSION_26; +} + +module.exports = FlashPlayer; \ No newline at end of file diff --git a/tasks/haxe.js b/tasks/haxe.js new file mode 100755 index 0000000..e9b38bd --- /dev/null +++ b/tasks/haxe.js @@ -0,0 +1,251 @@ +"use strict"; +const fs = require('fs'); +const os = require('os'); +const path = require('path'); +const tmp = require('tmp-file'); +const gutil = require('gulp-util'); +const exec = require('./exec'); +const download = require('./download'); +const gulp = require('gulp'); +const through = require('through2'); +const col = gutil.colors; +const Sdk = require('./sdk'); + + +class Haxe extends Sdk { + + getBin(name) { + if (os.type() === 'Windows_NT') { + return `${this.binPath}/${name}.exe`; + } else if (os.type() === 'Linux') { + const binPath = `${this.binPath}/${name}`; + fs.chmodSync(binPath, 0o755); + return binPath; + } + return `` + } + + get binPath() { + if (os.type() === 'Windows_NT') { + if (this.intVersion >= 342) { + return `${this.path}`; + } else { + return `${this.path}/haxe-${this.version}`; + } + } else if (os.type() === 'Linux') { + return `${this.path}/haxe-${this.version}`; + } + } + + get haxeBin() { + return this.getBin('haxe'); + } + + get haxelibBin() { + return this.getBin('haxelib'); + } + + constructor(version) { + super(Haxe.ID, version || Haxe.VERSION); + } + + get prepared() { + try { + return fs.existsSync(this.haxeBin); + } catch (e) { + return false; + } + } + + get link() { + if (os.type() === 'Windows_NT') { + return `https://github.com/HaxeFoundation/haxe/releases/download/${this.version}/haxe-${this.version}-win.zip`; + } else if (os.type() === 'Linux') { + let arch = null; + if (os.arch() === 'ia32') { arch = '32'} + if (os.arch() === 'x64') { arch = '64'} + return `https://github.com/HaxeFoundation/haxe/releases/download/${this.version}/haxe-${this.version}-linux${arch}.tar.gz`; + } + } + + haxe(args) { + return exec('.', [this.haxeBin].concat(args).join(' ')); + } + + haxelib(args) { + const haxelibBin = this.haxelibBin; + return exec(this.binPath, [path.basename(haxelibBin)].concat(args).join(' ')); + } + + install(packages) { + let promise = this.haxelib(['setup', `${this.path}/lib`]); + const next = (args) => () => { + gutil.log(this.tag, col.cyan('haxelib', 'install'), col.magenta(args[1])); + return this.haxelib(args); + }; + + for (let pack of packages) { + const args = []; + let version = null; + if (typeof pack === 'string') { + args.push('install', pack); + } else if (typeof pack === 'object') { + version = pack.version; + if (pack.git) { + args.push('git', pack.name, pack.git); + if (pack.branch) args.push(pack.branch); + } else { + args.push('install', pack.name); + if (version) args.push(version); + } + args.push('--always'); + } + let path = `${this.path}/lib/${args[1]}`; + if (version) path += `/${version.replace(/\./g, ',')}`; + if (!fs.existsSync(path)) { + promise = promise.then(next(args)); + } + } + return promise; + } + + upgrade() { + let promise = this.haxelib(['setup', `${this.path}/lib`]); + promise = promise.then(() => this.haxelib(['upgrade', '--always'])); + return promise; + } + + /** + * + * @param params + * @returns {*} + * + * { + * command: 'build', + * platform: 'flash', + * version: '1.0.0', + * build: '1999.12.12 00:00', + * values: {}, + * outputFile: 'out.swf', + * } + */ + openfl(params) { + const files = []; + let stream = null; + + const bufferContents = (file, enc, callback) => { + // ToDo: check file not stream + files.push(file); + callback(); + }; + + const endStream = (callback) => { + gutil.log(this.tag, col.cyan("openfl", params.command, params.platform), '=>', col.magenta(params.outputFile)); + const args = ['-cwd', files[0].path, 'run', 'openfl', params.command, params.platform]; + if (params.values) for (let key of Object.keys(params.values)) { + const value = params.values[key]; + if (value === true) { + args.push(`-D${key}`); + } else if (value) { + args.push(`-D${key}="${value}"`); + } + } + const tmpFile = tmp.generateFile(); + const dir = path.dirname(tmpFile.path); + const name = path.basename(tmpFile.path); + args.push(`--app-path=${dir}`); + args.push(`--app-file=${name}`); + if (params.version) args.push(`--meta-version=${params.version}`); + if (params.build) args.push(`--haxedef=BUILD="${params.build}"`); + this.haxelib(args).then(() => { + stream.push(new gutil.File({ + path: params.outputFile, + contents: fs.createReadStream(`${dir}/flash/bin/${name}.swf`) + })); + callback(); + }).catch((error) => { + stream.emit('error', new gutil.PluginError({plugin: this.name, message: error})); + callback(); + }); + }; + + return stream = through.obj(bufferContents, endStream); + } + + /** + * + * @param params + * + * { + * platform: 'neko', + * version: '1.0.0', + * build: '1999.12.12 00:00', + * values: {}, + * lib: [], + * src: [], + * main: 'Main.hx', + * outputFile: 'out.n', + * } + */ + build(params) { + const files = []; + let stream = null; + + const bufferContents = (file, enc, callback) => { + // ToDo: check file not stream + files.push(file); + callback(); + }; + + const endStream = (callback) => { + gutil.log(this.tag, col.cyan("haxe", params.platform), '=>', col.magenta(params.outputFile)); + const args = []; + args.push('-main', params.main); + for (const lib of params.lib) { + args.push('-lib', lib); + } + for (const cp of params.cp) { + args.push('-cp', cp); + } + for (const macro of params.macro) { + args.push('--macro', `"${macro}"`); + } + if (params.values) for (let key of Object.keys(params.values)) { + const value = params.values[key]; + if (value === true) { + args.push(`-D ${key}`); + } else if (value) { + args.push(`-D ${key}="${value}"`); + } + } + const tmpFile = tmp.generateFile(); + const dir = path.dirname(tmpFile.path); + const name = path.basename(tmpFile.path); + args.push(`-${params.platform}`, tmpFile.path); + if (params.build) args.push(`--haxedef=BUILD="${params.build}"`); + //console.log('haxe', args.join(' ')); + this.haxe(args).then(() => { + stream.push(new gutil.File({ + path: params.outputFile, + contents: fs.createReadStream(tmpFile.path) + })); + callback(); + }).catch((error) => { + stream.emit('error', new gutil.PluginError({plugin: this.name, message: error})); + callback(); + }); + }; + + return stream = through.obj(bufferContents, endStream); + } +} + +Haxe.ID = 'haxe'; + +Haxe.VERSION_3_4_0 = '3.4.0'; +Haxe.VERSION_3_4_2 = '3.4.2'; +Haxe.VERSION_3_4_3 = '3.4.3'; +Haxe.VERSION_3 = Haxe.VERSION_3_4_2; +Haxe.VERSION = Haxe.VERSION_3; + +module.exports = Haxe; \ No newline at end of file diff --git a/tasks/neko.js b/tasks/neko.js new file mode 100644 index 0000000..6cc8b2b --- /dev/null +++ b/tasks/neko.js @@ -0,0 +1,35 @@ +const exec = require('./exec'); +const gutil = require('gulp-util'); +const through = require('through2'); +const col = gutil.colors; + + +class Neko { + + constructor() { + this.tag = 'Neko'; + } + + run() { + let stream = null; + const bufferContents = (file, enc, callback) => { + gutil.log(this.tag, col.cyan("run"), col.magenta(file.path)); + + exec('.', ['neko', file.path].join(' ')) + .then(() => { + stream.emit('end'); + callback(); + }) + .catch((error) => { + stream.emit('error', new gutil.PluginError({plugin: this.tag, message: error})); + callback(); + }); + + stream.push(file); + }; + + return stream = through.obj(bufferContents); + } +} + +module.exports = Neko; \ No newline at end of file diff --git a/tasks/sdk.js b/tasks/sdk.js new file mode 100755 index 0000000..2743757 --- /dev/null +++ b/tasks/sdk.js @@ -0,0 +1,77 @@ +"use strict"; +const os = require('os'); +const gulp = require('gulp'); +const gutil = require('gulp-util'); +const col = gutil.colors; +const download = require('./download'); +const unzip = require('gulp-unzip'); +const gunzip = require('gulp-gunzip'); +const untar = require('gulp-untar'); +const Promise = require('bluebird'); +const ProgressBar = require('progress'); + +class Sdk { + static set dir(value) { + Sdk._dir = value + } + + static get dir() { + return Sdk._dir || `${os.homedir()}/sdk`; + } + + static path(name, version) { + return `${this.dir}/${name}/${version}`; + } + + constructor(name, version) { + this.name = name; + this.tag = col.green(`[${name}]`); + this.version = version; + this.path = Sdk.path(name, version); + } + + get intVersion() { + const vArr = this.version.split('\.').reverse(); + let m = 1; + let r = 0; + for (let v of vArr) { + r += parseInt(v) * m; + m *= 10; + } + return r; + } + + get prepared() { + throw "Not implemented"; + } + + get link() { + throw "Not implemented"; + } + + prepare() { + gutil.log(this.tag, `version: ${col.magenta(this.version)}`); + return new Promise((success, fail) => { + if (this.prepared) { + success(); + } else { + const bar = new ProgressBar(`${this.tag} [:bar] :percent :etas`, {width: 40, total: 1000, clear: true}); + let stream = download(this.link) + .on('error', fail) + .on('progress', (p) => bar.update(p.percent)) + .on('end', () => bar.update(1)); + if (this.link.endsWith('.zip')) { + stream = stream.pipe(unzip()); + } else if (this.link.endsWith('tar.gz')) { + stream = stream.pipe(gunzip()).pipe(untar()) + } + return stream + .pipe(gulp.dest(this.path)) + .on('end', success) + .on('error', fail); + } + }); + } +} + +module.exports = Sdk; \ No newline at end of file diff --git a/tasks/tail.js b/tasks/tail.js new file mode 100755 index 0000000..f177b15 --- /dev/null +++ b/tasks/tail.js @@ -0,0 +1,26 @@ +'use strict'; +const through = require('through2'); +const gutil = require('gulp-util'); +const col = gutil.colors; +const Tail = require('tail').Tail; + +module.exports = (handler) => { + let stream = null; + let tail = null; + + const bufferContents = (file, enc, callback) => { + tail = new Tail(file.path); + tail.on("line", handler); + callback(); + }; + + const endStream = (callback) => { + if (tail) { + tail.unwatch(); + tail = null; + } + callback(); + }; + + return stream = through.obj(bufferContents, endStream); +}; \ No newline at end of file