summaryrefslogtreecommitdiffstats
path: root/devtools/client/webide/modules/build.js
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/client/webide/modules/build.js')
-rw-r--r--devtools/client/webide/modules/build.js199
1 files changed, 199 insertions, 0 deletions
diff --git a/devtools/client/webide/modules/build.js b/devtools/client/webide/modules/build.js
new file mode 100644
index 000000000..34cbcc0b7
--- /dev/null
+++ b/devtools/client/webide/modules/build.js
@@ -0,0 +1,199 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const {Cu, Cc, Ci} = require("chrome");
+
+const promise = require("promise");
+const { Task } = require("devtools/shared/task");
+const { TextDecoder, OS } = Cu.import("resource://gre/modules/osfile.jsm", {});
+const Subprocess = require("sdk/system/child_process/subprocess");
+
+const ProjectBuilding = exports.ProjectBuilding = {
+ fetchPackageManifest: Task.async(function* (project) {
+ let manifestPath = OS.Path.join(project.location, "package.json");
+ let exists = yield OS.File.exists(manifestPath);
+ if (!exists) {
+ // No explicit manifest, try to generate one if possible
+ return this.generatePackageManifest(project);
+ }
+
+ let data = yield OS.File.read(manifestPath);
+ data = new TextDecoder().decode(data);
+ let manifest;
+ try {
+ manifest = JSON.parse(data);
+ } catch (e) {
+ throw new Error("Error while reading WebIDE manifest at: '" + manifestPath +
+ "', invalid JSON: " + e.message);
+ }
+ return manifest;
+ }),
+
+ /**
+ * For common frameworks in the community, attempt to detect the build
+ * settings if none are defined. This makes it much easier to get started
+ * with WebIDE. Later on, perhaps an add-on could define such things for
+ * different frameworks.
+ */
+ generatePackageManifest: Task.async(function* (project) {
+ // Cordova
+ let cordovaConfigPath = OS.Path.join(project.location, "config.xml");
+ let exists = yield OS.File.exists(cordovaConfigPath);
+ if (!exists) {
+ return;
+ }
+ let data = yield OS.File.read(cordovaConfigPath);
+ data = new TextDecoder().decode(data);
+ if (data.contains("cordova.apache.org")) {
+ return {
+ "webide": {
+ "prepackage": "cordova prepare",
+ "packageDir": "./platforms/firefoxos/www"
+ }
+ };
+ }
+ }),
+
+ hasPrepackage: Task.async(function* (project) {
+ let manifest = yield ProjectBuilding.fetchPackageManifest(project);
+ return manifest && manifest.webide && "prepackage" in manifest.webide;
+ }),
+
+ // If the app depends on some build step, run it before pushing the app
+ build: Task.async(function* ({ project, logger }) {
+ if (!(yield this.hasPrepackage(project))) {
+ return;
+ }
+
+ let manifest = yield ProjectBuilding.fetchPackageManifest(project);
+
+ logger("start");
+ try {
+ yield this._build(project, manifest, logger);
+ logger("succeed");
+ } catch (e) {
+ logger("failed", e);
+ }
+ }),
+
+ _build: Task.async(function* (project, manifest, logger) {
+ // Look for `webide` property
+ manifest = manifest.webide;
+
+ let command, cwd, args = [], env = [];
+
+ // Copy frequently used env vars
+ let envService = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
+ ["HOME", "PATH"].forEach(key => {
+ let value = envService.get(key);
+ if (value) {
+ env.push(key + "=" + value);
+ }
+ });
+
+ if (typeof (manifest.prepackage) === "string") {
+ command = manifest.prepackage.replace(/%project%/g, project.location);
+ } else if (manifest.prepackage.command) {
+ command = manifest.prepackage.command;
+
+ args = manifest.prepackage.args || [];
+ args = args.map(a => a.replace(/%project%/g, project.location));
+
+ env = env.concat(manifest.prepackage.env || []);
+ env = env.map(a => a.replace(/%project%/g, project.location));
+
+ if (manifest.prepackage.cwd) {
+ // Normalize path for Windows support (converts / to \)
+ let path = OS.Path.normalize(manifest.prepackage.cwd);
+ // Note that Path.join also support absolute path and argument.
+ // So that if cwd is absolute, it will return cwd.
+ let rel = OS.Path.join(project.location, path);
+ let exists = yield OS.File.exists(rel);
+ if (exists) {
+ cwd = rel;
+ }
+ }
+ } else {
+ throw new Error("pre-package manifest is invalid, missing or invalid " +
+ "`prepackage` attribute");
+ }
+
+ if (!cwd) {
+ cwd = project.location;
+ }
+
+ logger("Running pre-package hook '" + command + "' " +
+ args.join(" ") +
+ " with ENV=[" + env.join(", ") + "]" +
+ " at " + cwd);
+
+ // Run the command through a shell command in order to support non absolute
+ // paths.
+ // On Windows `ComSpec` env variable is going to refer to cmd.exe,
+ // Otherwise, on Linux and Mac, SHELL env variable should refer to
+ // the user chosen shell program.
+ // (We do not check for OS, as on windows, with cygwin, ComSpec isn't set)
+ let shell = envService.get("ComSpec") || envService.get("SHELL");
+ args.unshift(command);
+
+ // For cmd.exe, we have to pass the `/C` option,
+ // but for unix shells we need -c.
+ // That to interpret next argument as a shell command.
+ if (envService.exists("ComSpec")) {
+ args.unshift("/C");
+ } else {
+ args.unshift("-c");
+ }
+
+ // Subprocess changes CWD, we have to save and restore it.
+ let originalCwd = yield OS.File.getCurrentDirectory();
+ try {
+ let defer = promise.defer();
+ Subprocess.call({
+ command: shell,
+ arguments: args,
+ environment: env,
+ workdir: cwd,
+
+ stdout: data =>
+ logger(data),
+ stderr: data =>
+ logger(data),
+
+ done: result => {
+ logger("Terminated with error code: " + result.exitCode);
+ if (result.exitCode == 0) {
+ defer.resolve();
+ } else {
+ defer.reject("pre-package command failed with error code " + result.exitCode);
+ }
+ }
+ });
+ defer.promise.then(() => {
+ OS.File.setCurrentDirectory(originalCwd);
+ });
+ yield defer.promise;
+ } catch (e) {
+ throw new Error("Unable to run pre-package command '" + command + "' " +
+ args.join(" ") + ":\n" + (e.message || e));
+ }
+ }),
+
+ getPackageDir: Task.async(function* (project) {
+ let manifest = yield ProjectBuilding.fetchPackageManifest(project);
+ if (!manifest || !manifest.webide || !manifest.webide.packageDir) {
+ return project.location;
+ }
+ manifest = manifest.webide;
+
+ let packageDir = OS.Path.join(project.location, manifest.packageDir);
+ // On Windows, replace / by \\
+ packageDir = OS.Path.normalize(packageDir);
+ let exists = yield OS.File.exists(packageDir);
+ if (exists) {
+ return packageDir;
+ }
+ throw new Error("Unable to resolve application package directory: '" + manifest.packageDir + "'");
+ })
+};