flambe
Advanced tools
Comparing version 3.1.0 to 3.1.1
@@ -31,2 +31,3 @@ #!/usr/bin/env node | ||
promise.catch(function (error) { | ||
if (Array.isArray(error)) error = error[0]; // NCP throws an array of errors...? | ||
if (error) console.error(error.message || error); | ||
@@ -33,0 +34,0 @@ process.exit(1); |
359
index.js
@@ -8,2 +8,3 @@ "use strict"; | ||
var fs = require("fs"); | ||
var ncp = require("ncp").ncp; | ||
var os = require("os"); | ||
@@ -13,12 +14,11 @@ var path = require("path"); | ||
var wrench = require("wrench"); | ||
var xmldom = require("xmldom"); | ||
var DATA_DIR = __dirname + "/data"; | ||
var CACHE_DIR = "build/.cache"; | ||
var DATA_DIR = __dirname + "/data/"; | ||
var CACHE_DIR = "build/.cache/"; | ||
var HAXE_COMPILER_PORT = 6000; | ||
var HTTP_PORT = 5000; | ||
var HTTP_PORT = 7000; | ||
var SOCKET_PORT = HTTP_PORT+1; | ||
exports.PLATFORMS = ["html", "flash", "android"]; | ||
exports.PLATFORMS = ["html", "flash", "android", "ios"]; | ||
@@ -42,6 +42,10 @@ exports.VERSION = JSON.parse(fs.readFileSync(__dirname + "/package.json")).version; | ||
var promise = | ||
Q.nfcall(wrench.copyDirRecursive, DATA_DIR+"/scaffold", output, {}) | ||
Q.nfcall(wrench.copyDirRecursive, DATA_DIR+"scaffold", output, {}) | ||
.then(function () { | ||
// Packaging it straight as .gitignore seems to create problems with NPM/Windows | ||
return Q.nfcall(fs.rename, output+"/_.gitignore", output+"/.gitignore"); | ||
}) | ||
.then(function () { | ||
// Can't include this empty directory in git, so create it manually | ||
return Q.nfcall(fs.mkdir, output+"/libs"); | ||
}); | ||
@@ -63,20 +67,2 @@ return promise; | ||
switch (platform) { | ||
case "android": | ||
var apk = "build/main-android.apk"; | ||
console.log("Installing: " + apk); | ||
return adt(["-uninstallApp", "-platform", "android", "-appid", id], | ||
{output: false, check: false}) | ||
.then(function () { | ||
return adt(["-installApp", "-platform", "android", "-package", apk]); | ||
}) | ||
.then(function () { | ||
var p = adt(["-launchApp", "-platform", "android", "-appid", id]); | ||
if (debug && !opts.noFdb) { | ||
console.log(); | ||
fdb(["run", "continue"]); | ||
} | ||
return p; | ||
}) | ||
break; | ||
case "html": case "flash": | ||
@@ -102,2 +88,20 @@ var url = "http://localhost:" + HTTP_PORT + "/?flambe=" + platform; | ||
break; | ||
case "android": case "ios": | ||
var app = (platform == "android") ? "build/main-android.apk" : "build/main-ios.ipa"; | ||
console.log("Installing: " + app); | ||
return adt(["-uninstallApp", "-platform", platform, "-appid", id], | ||
{output: false, check: false}) | ||
.then(function () { | ||
return adt(["-installApp", "-platform", platform, "-package", app]); | ||
}) | ||
.then(function () { | ||
var p = adt(["-launchApp", "-platform", platform, "-appid", id]); | ||
if (debug && !opts.noFdb) { | ||
console.log(); | ||
fdb(["run", "continue"]); | ||
} | ||
return p; | ||
}) | ||
break; | ||
} | ||
@@ -123,28 +127,86 @@ }; | ||
// Flags common to all swf-based targets (flash, android, ios) | ||
var swfFlags = ["--flash-strict", "-swf-header", "640:480:60:000000"]; | ||
if (debug) swfFlags.push("-D", "fdb", "-D", "advanced-telemetry"); | ||
else swfFlags.push("-D", "native_trace"); | ||
var assetPaths = getAllPaths(config, "assets"); | ||
var libPaths = getAllPaths(config, "libs"); | ||
var srcPaths = getAllPaths(config, "src"); | ||
var webPaths = getAllPaths(config, "web"); | ||
var _preparedWeb = false; | ||
var prepareWeb = function () { | ||
if (!_preparedWeb) { | ||
_preparedWeb = true; | ||
wrench.mkdirSyncRecursive("build/web/targets"); | ||
copyFileSync(DATA_DIR+"flambe.js", "build/web/flambe.js"); | ||
return copyDirs(webPaths, "build/web", {includeHidden: true}) | ||
.then(function () { | ||
if (fs.existsSync("icons")) { | ||
return copyDirs("icons", "build/web/icons"); | ||
} | ||
}); | ||
} | ||
return Q(); | ||
}; | ||
var prepareAssets = function (dest) { | ||
wrench.rmdirSyncRecursive(dest, true); | ||
// TODO(bruno): Filter out certain formats based on the platform | ||
return copyDirs(assetPaths, dest) | ||
.then(function () { | ||
return ["--macro", "flambe.platform.ManifestBuilder.use(\""+dest+"\")"]; | ||
}); | ||
}; | ||
var swfFlags = function (air) { | ||
// Flags common to all swf-based targets (flash, android, ios) | ||
var flags = ["--flash-strict", "-swf-header", "640:480:60:000000"]; | ||
if (debug) flags.push("-D", "fdb", "-D", "advanced-telemetry"); | ||
else flags.push("-D", "native_trace"); | ||
// Include any swc/swf libs in the libs directories | ||
libPaths.forEach(function (libPath) { | ||
forEachFileIn(libPath, function (file) { | ||
if (file.match(/.*\.(swc|swf)$/)) { | ||
flags.push("-swf-lib", libPath+"/"+file); | ||
} else if (air && file.match(/.*\.ane$/)) { | ||
// flags.push("-swf-lib-extern", libPath+"/"+file); | ||
// The current version of Haxe can't deal with .ane -swf-libs, so rename it to a | ||
// swc first | ||
var swc = CACHE_DIR+"air/"+file+".swc"; | ||
wrench.mkdirSyncRecursive(CACHE_DIR+"air"); | ||
copyFileSync(libPath+"/"+file, swc); | ||
flags.push("-swf-lib-extern", swc); | ||
} | ||
}); | ||
}); | ||
return flags; | ||
}; | ||
var buildHtml = function () { | ||
var htmlFlags = ["-D", "html"]; | ||
var unminified = CACHE_DIR+"/main-html.unminified.js"; | ||
var unminified = CACHE_DIR+"main-html.unminified.js"; | ||
var js = "build/web/targets/main-html.js"; | ||
console.log("Building: " + js); | ||
if (debug) { | ||
return haxe(commonFlags.concat(htmlFlags).concat(["-js", js])); | ||
} else { | ||
// Minify release builds | ||
return haxe(commonFlags.concat(htmlFlags).concat(["-js", unminified])) | ||
.then(function () { | ||
return minify([unminified], js, {strict: true}); | ||
}) | ||
.then(function () { | ||
// Delete the source map file produced by debug builds | ||
return Q.nfcall(fs.unlink, js+".map") | ||
.catch(function () { | ||
// Ignore errors | ||
return prepareWeb() | ||
.then(function () { return prepareAssets("build/web/assets") }) | ||
.then(function (assetFlags) { | ||
console.log("Building: " + js); | ||
var flags = commonFlags.concat(assetFlags).concat(htmlFlags); | ||
if (debug) { | ||
return haxe(flags.concat(["-js", js])); | ||
} else { | ||
// Minify release builds | ||
return haxe(flags.concat(["-js", unminified])) | ||
.then(function () { | ||
return minify([unminified], js, {strict: true}); | ||
}) | ||
.then(function () { | ||
// Delete the source map file produced by debug builds | ||
return Q.nfcall(fs.unlink, js+".map") | ||
.catch(function () { | ||
// Ignore errors | ||
}); | ||
}); | ||
}); | ||
} | ||
} | ||
}); | ||
}; | ||
@@ -154,18 +216,25 @@ | ||
var swf = "build/web/targets/main-flash.swf"; | ||
var flashFlags = swfFlags.concat(["-swf-version", "11", "-swf", swf]); | ||
console.log("Building: " + swf); | ||
return haxe(commonFlags.concat(flashFlags)); | ||
var flashFlags = swfFlags(false).concat([ | ||
"-swf-version", "11", "-swf", swf]); | ||
return prepareWeb() | ||
.then(function () { return prepareAssets("build/web/assets") }) | ||
.then(function (assetFlags) { | ||
console.log("Building: " + swf); | ||
return haxe(commonFlags.concat(assetFlags).concat(flashFlags)); | ||
}); | ||
}; | ||
var buildAir = function (flags) { | ||
wrench.mkdirSyncRecursive(CACHE_DIR+"/air"); | ||
wrench.copyDirSyncRecursive("assets", CACHE_DIR+"/air/assets", { | ||
excludeHiddenUnix: true, | ||
filter: /\.(ogg|wav|m4a)$/, | ||
var airFlags = swfFlags(true).concat(["-swf-version", "11.7", "-D", "air"]); | ||
wrench.mkdirSyncRecursive(CACHE_DIR+"air"); | ||
return prepareAssets(CACHE_DIR+"air/assets") | ||
.then(function (assetFlags) { | ||
return haxe(commonFlags.concat(assetFlags).concat(airFlags).concat(flags)); | ||
}); | ||
var airFlags = swfFlags.concat(["-swf-version", "11.7", "-D", "air"]); | ||
return haxe(commonFlags.concat(airFlags).concat(flags)) | ||
}; | ||
var generateAirXml = function (swf, output) { | ||
var xmldom = require("xmldom"); | ||
var xml = | ||
@@ -178,7 +247,54 @@ "<application xmlns=\"http://ns.adobe.com/air/application/3.7\">\n" + | ||
" <content>"+swf+"</content>\n" + | ||
" <aspectRatio>"+get(config, "orientation", "portrait")+"</aspectRatio>\n" + | ||
" <fullScreen>"+get(config, "fullscreen", "true")+"</fullScreen>\n" + | ||
" <autoOrients>true</autoOrients>\n" + // Enables 180 degree rotation | ||
" <renderMode>direct</renderMode>\n" + | ||
" </initialWindow>\n" + | ||
" <android>\n" + | ||
" <manifestAdditions><![CDATA[\n" + | ||
get(config, "android AndroidManifest.xml", "<manifest android:installLocation=\"auto\"/>") + | ||
" ]]></manifestAdditions>\n" + | ||
" </android>\n" + | ||
" <iPhone>\n" + | ||
" <InfoAdditions><![CDATA[\n" + | ||
get(config, "ios Info.plist", "") + | ||
" ]]></InfoAdditions>\n" + | ||
" <Entitlements><![CDATA[\n" + | ||
get(config, "ios Entitlements.plist", "") + | ||
" ]]></Entitlements>\n" + | ||
" </iPhone>\n" + | ||
"</application>"; | ||
var doc = new xmldom.DOMParser().parseFromString(xml); | ||
var pathOptions = []; // Path options to pass to ADT | ||
var extensions = doc.createElement("extensions"); | ||
libPaths.forEach(function (libPath) { | ||
if (!fs.existsSync(libPath)) { | ||
return; | ||
} | ||
var hasANE = false; | ||
forEachFileIn(libPath, function (file) { | ||
if (file.match(/.*\.ane$/)) { | ||
// Extract the extension ID from the .ane | ||
var AdmZip = require("adm-zip"); | ||
var zip = new AdmZip(libPath+"/"+file); | ||
var extension = new xmldom.DOMParser().parseFromString( | ||
zip.readAsText("META-INF/ANE/extension.xml")); | ||
var id = extension.getElementsByTagName("id")[0].textContent; | ||
var extensionID = doc.createElement("extensionID"); | ||
extensionID.textContent = id; | ||
extensions.appendChild(extensionID); | ||
hasANE = true; | ||
} | ||
}); | ||
if (hasANE) { | ||
pathOptions.push("-extdir", libPath); | ||
} | ||
}); | ||
if (extensions.firstChild) { | ||
doc.documentElement.appendChild(extensions); | ||
} | ||
var icons = doc.createElement("icon"); | ||
@@ -191,3 +307,3 @@ fs.readdirSync("icons").forEach(function (file) { | ||
var image = doc.createElement("image"+size+"x"+size); | ||
image.appendChild(doc.createTextNode("icons/"+file)); | ||
image.textContent = "icons/"+file; | ||
icons.appendChild(image); | ||
@@ -198,5 +314,9 @@ } else { | ||
}); | ||
doc.documentElement.appendChild(icons); | ||
if (icons.firstChild) { | ||
doc.documentElement.appendChild(icons); | ||
pathOptions.push("icons"); | ||
} | ||
fs.writeFileSync(output, new xmldom.XMLSerializer().serializeToString(doc)); | ||
return pathOptions; | ||
}; | ||
@@ -209,7 +329,6 @@ | ||
var swf = "main-android.swf"; | ||
var cert = CACHE_DIR+"/air/certificate-android.p12" | ||
var xml = CACHE_DIR+"/air/config-android.xml" | ||
var cert = CACHE_DIR+"air/certificate-android.p12"; | ||
var xml = CACHE_DIR+"air/config-android.xml"; | ||
var promise = | ||
buildAir(["-D", "android", "-swf", CACHE_DIR+"/air/"+swf]) | ||
return buildAir(["-D", "android", "-swf", CACHE_DIR+"air/"+swf]) | ||
.then(function () { | ||
@@ -223,3 +342,3 @@ // Generate a dummy certificate if it doesn't exist | ||
.then(function () { | ||
generateAirXml(swf, xml); | ||
var pathOptions = generateAirXml(swf, xml); | ||
@@ -234,18 +353,42 @@ var androidFlags = ["-package"]; | ||
androidFlags.push("-storetype", "pkcs12", "-keystore", cert, "-storepass", "password", | ||
apk, xml, "icons", "-C", CACHE_DIR+"/air", swf, "assets"); | ||
apk, xml); | ||
androidFlags = androidFlags.concat(pathOptions); | ||
androidFlags.push("-C", CACHE_DIR+"air", swf, "assets"); | ||
return adt(androidFlags); | ||
}) | ||
return promise; | ||
} | ||
}); | ||
}; | ||
var buildIos = function () { | ||
var ipa = "build/main-ios.ipa"; | ||
console.log("Building: " + ipa); | ||
var swf = "main-ios.swf"; | ||
var cert = "certs/ios-development.p12"; | ||
var mobileProvision = "certs/ios.mobileprovision"; | ||
var xml = CACHE_DIR+"air/config-ios.xml"; | ||
return buildAir(["-D", "ios", "-D", "no-flash-override", "-swf", CACHE_DIR+"air/"+swf]) | ||
.then(function () { | ||
var pathOptions = generateAirXml(swf, xml); | ||
var iosFlags = ["-package"]; | ||
if (debug) { | ||
var fdbHost = opts.fdbHost || getIP(); | ||
iosFlags.push("-target", "ipa-debug", "-connect", fdbHost); | ||
} else { | ||
iosFlags.push("-target", "ipa-ad-hoc"); | ||
} | ||
// TODO(bruno): Make these cert options configurable | ||
iosFlags.push("-storetype", "pkcs12", "-keystore", cert, "-storepass", "password", | ||
"-provisioning-profile", mobileProvision, ipa, xml); | ||
iosFlags = iosFlags.concat(pathOptions); | ||
iosFlags.push("-C", CACHE_DIR+"air", swf, "assets"); | ||
return adt(iosFlags); | ||
}); | ||
}; | ||
wrench.mkdirSyncRecursive(CACHE_DIR); | ||
wrench.mkdirSyncRecursive("build/web/targets"); | ||
copyDirContents("web", "build/web"); | ||
copyFile(DATA_DIR+"/flambe.js", "build/web/flambe.js"); | ||
wrench.copyDirSyncRecursive("assets", "build/web/assets", {excludeHiddenUnix: true, forceDelete: true}); | ||
wrench.copyDirSyncRecursive("icons", "build/web/icons", {excludeHiddenUnix: true, forceDelete: true}); | ||
var connectFlags = ["--connect", HAXE_COMPILER_PORT]; | ||
var promise = | ||
haxe(connectFlags, {check: false, verbose: false, output: false}) | ||
return haxe(connectFlags, {check: false, verbose: false, output: false}) | ||
.then(function (code) { | ||
@@ -262,3 +405,6 @@ // Hide the compilation server behind an environment variable for now until stable | ||
commonFlags = commonFlags.concat(toArray(get(config, "haxe_flags", []))); | ||
commonFlags.push("-lib", "flambe", "-cp", "src"); | ||
commonFlags.push("-lib", "flambe"); | ||
srcPaths.forEach(function (srcDir) { | ||
commonFlags.push("-cp", srcDir); | ||
}); | ||
commonFlags.push("-dce", "full"); | ||
@@ -276,2 +422,3 @@ if (debug) { | ||
android: buildAndroid, | ||
ios: buildIos, | ||
}; | ||
@@ -287,3 +434,2 @@ var promise = Q(); | ||
}); | ||
return promise; | ||
}; | ||
@@ -344,2 +490,4 @@ | ||
var haxe = function (flags, opts) { | ||
opts = opts || {}; | ||
opts.windowsCmd = false; | ||
return exec("haxe", flags, opts); | ||
@@ -393,3 +541,3 @@ }; | ||
opts = opts || {}; | ||
var flags = ["-jar", DATA_DIR+"/closure.jar", | ||
var flags = ["-jar", DATA_DIR+"closure.jar", | ||
"--warning_level", "QUIET", | ||
@@ -582,7 +730,23 @@ "--js_output_file", output, | ||
var get = function (config, name, defaultValue) { | ||
if (name in config) return config[name]; | ||
var fields = name.split(" "); | ||
for (var ii = 0, ll = fields.length; ii < ll; ++ii) { | ||
var field = fields[ii]; | ||
if (field in config) { | ||
config = config[field]; | ||
if (ii == ll-1) return config; | ||
} | ||
} | ||
if (typeof defaultValue != "undefined") return defaultValue; | ||
throw new Error("Missing required entry in config file: " + name); | ||
throw new Error("Missing required field in config file: " + name); | ||
}; | ||
var getAllPaths = function (config, name) { | ||
var paths = toArray(get(config, "extra_paths "+name, [])); | ||
if (paths.length == 0 || fs.existsSync(name)) { | ||
// Make the standard path in the project directory optional if you've defined extras | ||
paths.unshift(name); | ||
} | ||
return paths; | ||
}; | ||
var checkPlatforms = function (platforms) { | ||
@@ -597,18 +761,22 @@ for (var ii = 0; ii < platforms.length; ++ii) { | ||
/** | ||
* Copy all the files in a directory into another directory. Not a true merge, only one level deep. | ||
*/ | ||
var copyDirContents = function (from, to) { | ||
fs.readdirSync(from).forEach(function (file) { | ||
var src = path.join(from, file); | ||
var dest = path.join(to, file); | ||
if (fs.statSync(src).isDirectory()) { | ||
wrench.copyDirSyncRecursive(src, dest, {forceDelete: true}); | ||
} else { | ||
copyFile(src, dest); | ||
} | ||
}); | ||
var copyDirs = function (dirs, dest, opts) { | ||
opts = opts || {}; | ||
// Reverse it so files in earlier dirs have higher override priority | ||
dirs = toArray(dirs).reverse(); | ||
var ncpOptions = { | ||
stopOnErr: true, | ||
filter: function (file) { | ||
return opts.includeHidden || path.basename(file).charAt(0) != "."; | ||
}, | ||
}; | ||
return dirs.reduce(function (prev, dir) { | ||
return prev.then(function () { | ||
return Q.nfcall(ncp, dir, dest, ncpOptions); | ||
}); | ||
}, Q.nfcall(fs.mkdir, dest).catch(function (){})); | ||
}; | ||
var copyFile = function (from, to) { | ||
var copyFileSync = function (from, to) { | ||
var content = fs.readFileSync(from); | ||
@@ -618,2 +786,11 @@ fs.writeFileSync(to, content); | ||
var forEachFileIn = function (dir, callback) { | ||
try { | ||
var files = fs.readdirSync(dir); | ||
} catch (error) { | ||
return; // Ignore missing directory | ||
} | ||
files.forEach(callback); | ||
}; | ||
var getIP = function () { | ||
@@ -620,0 +797,0 @@ var ip = null; |
@@ -13,5 +13,7 @@ { | ||
"dependencies": { | ||
"adm-zip": "~0.4.3", | ||
"argparse": "~0.1.15", | ||
"connect": "~2.8.3", | ||
"js-yaml": "~2.1.0", | ||
"ncp": "~0.4.2", | ||
"open": "~0.0.3", | ||
@@ -34,3 +36,3 @@ "q": "~0.9.6", | ||
"repository": "https://github.com/aduros/flambe", | ||
"version": "3.1.0" | ||
"version": "3.1.1" | ||
} |
Sorry, the diff of this file is not supported yet
5464445
982
11
+ Addedadm-zip@~0.4.3
+ Addedncp@~0.4.2
+ Addedadm-zip@0.4.16(transitive)
+ Addedncp@0.4.2(transitive)