mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-08-25 04:57:03 +00:00
628 lines
18 KiB
JavaScript
628 lines
18 KiB
JavaScript
/*
|
|
* command.jake
|
|
* toolchain
|
|
*
|
|
* Copyright 2012 The Cappuccino Foundation
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
const fs = require('fs');
|
|
const child_process = require('child_process');
|
|
const path = require('path');
|
|
const { stream } = ObjectiveJ.term;
|
|
|
|
requiresSudo = false;
|
|
|
|
process.argv.slice(1).forEach(function(arg)
|
|
{
|
|
if (arg === "sudo-install")
|
|
requiresSudo = true;
|
|
});
|
|
|
|
// Record the initial process.env so we know which need to be serialized later.
|
|
var envInitial = Object.freeze(JSON.parse(JSON.stringify(process.env)));
|
|
|
|
// Set up development environment variables.
|
|
process.env["BUILD_PATH"] = path.resolve(
|
|
process.env["BUILD_PATH"] ||
|
|
process.env["CAPP_BUILD"] || // Global Cappuccino build directory.
|
|
process.env["STEAM_BUILD"] || // Maintain backwards compatibility with steam.
|
|
path.join(path.dirname(module.path), "Build") // Just build here.
|
|
);
|
|
|
|
if (!process.env["CAPP_BUILD"] && process.env["STEAM_BUILD"])
|
|
console.error("STEAM_BUILD environment variable is deprecated; Please use CAPP_BUILD instead.");
|
|
|
|
if (!process.env["CONFIG"])
|
|
process.env["CONFIG"] = "Release";
|
|
|
|
global.ENV = process.env;
|
|
|
|
global.task = JAKE.task;
|
|
global.directory = JAKE.directory;
|
|
global.file = JAKE.file;
|
|
global.filedir = JAKE.filedir;
|
|
global.FileList = JAKE.FileList;
|
|
|
|
global.$CONFIGURATION = process.env['CONFIG'];
|
|
global.$INLINE_MSG_SEND = process.env['INLINE_MSG_SEND'];
|
|
global.$BUILD_DIR = process.env['BUILD_PATH'];
|
|
global.$BUILD_CONFIGURATION_DIR = path.join($BUILD_DIR, $CONFIGURATION);
|
|
|
|
global.$BUILD_CJS_OBJECTIVE_J = path.join($BUILD_CONFIGURATION_DIR, "CommonJS", "objective-j");
|
|
|
|
global.$BUILD_CJS_CAPPUCCINO = path.join($BUILD_CONFIGURATION_DIR, "CommonJS", "cappuccino");
|
|
global.$BUILD_CJS_CAPPUCCINO_BIN = path.join($BUILD_CJS_CAPPUCCINO, "bin");
|
|
global.$BUILD_CJS_CAPPUCCINO_LIB = path.join($BUILD_CJS_CAPPUCCINO, "lib");
|
|
global.$BUILD_CJS_CAPPUCCINO_FRAMEWORKS = path.join($BUILD_CJS_CAPPUCCINO, "Frameworks");
|
|
|
|
global.OBJJ_INCLUDE_PATHS.push(global.$BUILD_CJS_CAPPUCCINO_FRAMEWORKS);
|
|
|
|
global.CLEAN = JAKE.CLEAN_AND_CLOBBER.CLEAN;
|
|
global.CLOBBER = JAKE.CLEAN_AND_CLOBBER.CLOBBER;
|
|
global.CLEAN.include(path.join(global.$BUILD_DIR, "*.build"));
|
|
global.CLOBBER.include(global.$BUILD_DIR);
|
|
|
|
global.$HOME_DIR = path.resolve(path.dirname(module.path));
|
|
global.$LICENSE_FILE = path.resolve(path.join(path.dirname(module.path), 'LICENSE'));
|
|
|
|
global.FIXME_fileDependency = function(destinationPath, sourcePath)
|
|
{
|
|
file(destinationPath, [sourcePath], function()
|
|
{
|
|
var time = new Date();
|
|
try {
|
|
fs.utimesSync(destinationPath, time, time);
|
|
} catch (err) {
|
|
fs.closeSync(fs.openSync(destinationPath, 'w'));
|
|
}
|
|
});
|
|
};
|
|
|
|
// logic to determine which packages should be loaded but are not.
|
|
// used in serializedENV()
|
|
function additionalPackages()
|
|
{
|
|
var builtObjectiveJPackage = path.join(path.resolve($BUILD_CONFIGURATION_DIR), "CommonJS", "objective-j");
|
|
var builtCappuccinoPackage = path.join(path.resolve($BUILD_CONFIGURATION_DIR), "CommonJS", "cappuccino");
|
|
|
|
var packages = [];
|
|
// load built objective-j if exists, otherwise unbuilt
|
|
if (!fs.existsSync(path.join(builtObjectiveJPackage, "package.json"))) {
|
|
//if (!packageInCatalog(builtObjectiveJPackage))
|
|
packages.push(builtObjectiveJPackage);
|
|
}
|
|
|
|
// load built cappuccino if it exists
|
|
if (!fs.existsSync(path.join(builtCappuccinoPackage, "package.json"))) {
|
|
//if (!packageInCatalog(builtCappuccinoPackage))
|
|
packages.push(builtCappuccinoPackage);
|
|
}
|
|
|
|
return packages;
|
|
}
|
|
|
|
// checks to see if a path is in the package catalog
|
|
function packageInCatalog(path)
|
|
{
|
|
var catalog = require("narwhal/packages").catalog;
|
|
for (var name in catalog)
|
|
if (String(catalog[name].directory) === String(path))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
// from narwhal
|
|
function enquote(word) {
|
|
return "'" + String(word).replace(/'/g, "'\"'\"'") + "'";
|
|
}
|
|
|
|
serializedENV = function()
|
|
{
|
|
var envNew = {};
|
|
|
|
// add changed keys to the new ENV
|
|
Object.keys(process.env).forEach(function(key)
|
|
{
|
|
if (process.env[key] !== envInitial[key])
|
|
envNew[key] = process.env[key];
|
|
});
|
|
|
|
// pseudo-HACK: add NARWHALOPT with packages we should ensure are loaded
|
|
var packages = additionalPackages();
|
|
|
|
if (packages.length)
|
|
{
|
|
envNew["NARWHALOPT"] = packages.map(function(p) { return "-p " + enquote(p); }).join(" ");
|
|
envNew["PATH"] = packages.map(function(p) { return path.join(p, "bin"); }).concat(process.env["PATH"]).join(":");
|
|
}
|
|
|
|
return Object.keys(envNew).map(function(key)
|
|
{
|
|
return key + "=" + enquote(envNew[key]);
|
|
}).join(" ");
|
|
};
|
|
|
|
function getShellConfigFile()
|
|
{
|
|
var homeDir = process.env["HOME"] + "/";
|
|
// use order outlined by http://hayne.net/MacDev/Notes/unixFAQ.html#shellStartup
|
|
var possibilities = [homeDir + ".bash_profile",
|
|
homeDir + ".bash_login",
|
|
homeDir + ".profile",
|
|
homeDir + ".bashrc"];
|
|
|
|
for (var i = 0; i < possibilities.length; i++)
|
|
{
|
|
if (fs.existsSync(possibilities[i]))
|
|
return possibilities[i];
|
|
}
|
|
}
|
|
|
|
function reforkWithPackages()
|
|
{
|
|
console.log("in reforkWithPackages");
|
|
if (additionalPackages().length > 0)
|
|
{
|
|
var cmd = serializedENV() + " " + process.argv.map(enquote).join(" ");
|
|
console.log("cmd: " + cmd);
|
|
child_process.execSync(cmd, {stdio: 'inherit'});
|
|
process.exit();
|
|
}
|
|
}
|
|
|
|
// reforkWithPackages();
|
|
|
|
function handleSetupEnvironmentError(e)
|
|
{
|
|
if (String(e).indexOf("require error") == -1)
|
|
{
|
|
console.log("setupEnvironment warning: " + e);
|
|
//throw e;
|
|
}
|
|
}
|
|
|
|
function setupEnvironment()
|
|
{
|
|
try
|
|
{
|
|
ObjectiveJ.OBJJ_INCLUDE_PATHS.push(path.join($BUILD_CONFIGURATION_DIR, "CommonJS", "cappuccino", "Frameworks"));
|
|
}
|
|
catch (e)
|
|
{
|
|
handleSetupEnvironmentError(e);
|
|
}
|
|
}
|
|
|
|
setupEnvironment();
|
|
|
|
global.rm_rf = function(/*String*/ aFilename)
|
|
{
|
|
try { fs.rmSync(aFilename, {recursive: true, force: true}); }
|
|
catch (anException) { }
|
|
};
|
|
|
|
|
|
// from stackoverflow
|
|
global.copyRecursiveSync = function (src, dest) {
|
|
var exists = fs.existsSync(src);
|
|
var stats = exists && fs.statSync(src);
|
|
var isDirectory = exists && stats.isDirectory();
|
|
if (isDirectory) {
|
|
fs.mkdirSync(dest, { recursive: true });
|
|
fs.readdirSync(src).forEach(function(childItemName) {
|
|
copyRecursiveSync(path.join(src, childItemName), path.join(dest, childItemName));
|
|
});
|
|
} else {
|
|
fs.copyFileSync(src, dest);
|
|
}
|
|
};
|
|
|
|
global.cp_r = function(/*String*/ from, /*String*/ to)
|
|
{
|
|
if (fs.existsSync(to))
|
|
rm_rf(to);
|
|
|
|
if (fs.lstatSync(from).isDirectory())
|
|
copyRecursiveSync(from, to);
|
|
else
|
|
{
|
|
try
|
|
{
|
|
fs.copyFileSync(from, to);
|
|
}
|
|
catch (e)
|
|
{
|
|
print(e + fs.existsSync(from) + " " + fs.existsSync(path.dirname(to)));
|
|
}
|
|
}
|
|
};
|
|
|
|
global.cp = function(/*String*/ from, /*String*/ to)
|
|
{
|
|
fs.copyFileSync(from, to)
|
|
// FILE.chmod(to, FILE.mod(from));
|
|
};
|
|
|
|
global.mv = function(/*String*/ from, /*String*/ to)
|
|
{
|
|
fs.renameSync(from, to);
|
|
};
|
|
|
|
function systemSync(command)
|
|
{
|
|
//console.log("command: " + command)
|
|
try {
|
|
child_process.execSync(command, {stdio: 'inherit'});
|
|
return 0;
|
|
} catch (error) {
|
|
//console.log(error);
|
|
//console.log(error.output.toString());
|
|
return error.status;
|
|
}
|
|
}
|
|
|
|
global.subjake = function(/*Array<String>*/ directories, /*String*/ aTaskName)
|
|
{
|
|
|
|
if (!Array.isArray(directories))
|
|
directories = [directories];
|
|
|
|
directories.forEach(function(/*String*/ aDirectory)
|
|
{
|
|
if (fs.lstatSync(aDirectory).isDirectory() && fs.lstatSync(path.join(aDirectory, "Jakefile")).isFile())
|
|
{
|
|
var cmd = "cd " + enquote(aDirectory) + " && " + serializedENV() + " " + "jake" + " " + enquote(aTaskName);
|
|
var returnCode = systemSync(cmd);
|
|
|
|
if (returnCode) {
|
|
process.exit(returnCode);
|
|
}
|
|
}
|
|
else
|
|
print("warning: subjake missing: " + aDirectory + " (this is not necessarily an error, " + aDirectory + " may be optional)");
|
|
});
|
|
};
|
|
|
|
global.executableExists = function(/*String*/ executableName)
|
|
{
|
|
var paths = process.env["PATH"].split(':');
|
|
for (var i = 0; i < paths.length; i++) {
|
|
path.join(paths[i], executableName);
|
|
var p = path.join(paths[i], executableName);
|
|
if (fs.existsSync(p))
|
|
return p
|
|
}
|
|
return null;
|
|
};
|
|
|
|
$OBJJ_TEMPLATE_EXECUTABLE = path.join(__dirname, "Objective-J", "CommonJS", "objj-executable");
|
|
|
|
global.make_objj_executable = function(aPath)
|
|
{
|
|
cp($OBJJ_TEMPLATE_EXECUTABLE, aPath);
|
|
fs.chmodSync(aPath, 0o755);
|
|
};
|
|
|
|
global.symlink_executable = function(source)
|
|
{
|
|
rel = path.relative($ENVIRONMENT_NARWHAL_BIN_DIR, source);
|
|
dest = path.join($ENVIRONMENT_NARWHAL_BIN_DIR, path.basename(source));
|
|
fs.symlinkSync(rel, dest);
|
|
};
|
|
|
|
global.getCappuccinoVersion = function()
|
|
{
|
|
var versionFile = path.join(module.path, "package.json");
|
|
return JSON.parse(fs.readFileSync(versionFile, { encoding: "utf8" })).version;
|
|
};
|
|
|
|
global.setPackageMetadata = function(packagePath)
|
|
{
|
|
var pkg = JSON.parse(fs.readFileSync(packagePath, { encoding: "utf8" } ));
|
|
|
|
try {
|
|
var output = child_process.execSync("git rev-parse --verify HEAD");
|
|
var sha = output.toString().split("\n")[0];
|
|
if (sha.length === 40)
|
|
pkg["cappuccino-revision"] = sha;
|
|
|
|
} catch (error) {
|
|
console.log(error.output.toString());
|
|
console.log("setPackageMetadata error " + error.status);
|
|
}
|
|
|
|
pkg["cappuccino-timestamp"] = new Date().getTime();
|
|
pkg["version"] = getCappuccinoVersion();
|
|
|
|
stream.print(" Version: \0purple(" + pkg["version"] + "\0)");
|
|
stream.print(" Revision: \0purple(" + pkg["cappuccino-revision"] + "\0)");
|
|
stream.print(" Timestamp: \0purple(" + pkg["cappuccino-timestamp"] + "\0)");
|
|
|
|
fs.writeFileSync(packagePath, JSON.stringify(pkg, null, 4), 'utf8');
|
|
};
|
|
|
|
global.subtasks = function(subprojects, taskNames)
|
|
{
|
|
taskNames.forEach(function(aTaskName)
|
|
{
|
|
var subtaskName = aTaskName + "_subprojects";
|
|
|
|
task (aTaskName, [subtaskName]);
|
|
|
|
task (subtaskName, function()
|
|
{
|
|
subjake(subprojects, aTaskName);
|
|
});
|
|
});
|
|
};
|
|
|
|
global.installSymlink = function(sourcePath)
|
|
{
|
|
if (!fs.lstatSync(sourcePath).isDirectory())
|
|
return;
|
|
|
|
|
|
var packageName = path.basename(sourcePath),
|
|
targetPath = path.join(SYSTEM.prefix, "packages", packageName);
|
|
|
|
if (FILE.isDirectory(targetPath))
|
|
FILE.rmtree(targetPath);
|
|
else if (FILE.linkExists(targetPath))
|
|
FILE.remove(targetPath);
|
|
|
|
stream.print("Symlinking \0cyan(" + targetPath + "\0) ==> \0cyan(" + sourcePath + "\0)");
|
|
FILE.symlink(sourcePath, targetPath);
|
|
|
|
var binPath = FILE.Path(FILE.join(targetPath, "bin"));
|
|
|
|
if (binPath.isDirectory())
|
|
{
|
|
var narwhalBin = FILE.Path(FILE.join(SYSTEM.prefix, "bin"));
|
|
|
|
binPath.list().forEach(function (name)
|
|
{
|
|
var binary = binPath.join(name);
|
|
binary.chmod(0755);
|
|
|
|
var target = narwhalBin.join(name),
|
|
relative = FILE.relative(target, binary);
|
|
|
|
if (target.linkExists())
|
|
target.remove();
|
|
|
|
FILE.symlink(relative, target);
|
|
});
|
|
}
|
|
};
|
|
|
|
global.installCopy = function(sourcePath, useSudo)
|
|
{
|
|
if (!fs.existsSync(sourcePath))
|
|
return;
|
|
|
|
var packageName = path.basename(sourcePath),
|
|
targetPath = path.join(__dirname, "dist", packageName);
|
|
|
|
// create the dist directory if it does not exist
|
|
if (!fs.existsSync(targetPath)) {
|
|
fs.mkdirSync(targetPath, { recursive: true });
|
|
}
|
|
|
|
if (fs.lstatSync(targetPath).isDirectory())
|
|
fs.rmSync(targetPath, {recursive: true, force: true});
|
|
else if (fs.fstatSync(targetPath).isSymbolicLink() || fs.existsSync(targetPath))
|
|
fs.rmSync(targetPath);
|
|
|
|
stream.print("Copying \0cyan(" + sourcePath + "\0) ==> \0cyan(" + targetPath + "\0)");
|
|
|
|
// hacky way to do a sudo copy.
|
|
if (useSudo)
|
|
child_process.execSync(["sudo", "cp", "-r", sourcePath, targetPath].join(" "));
|
|
else
|
|
copyRecursiveSync(sourcePath, targetPath);
|
|
|
|
var binPath = path.resolve(path.join(targetPath, "bin"))
|
|
|
|
// create the bin directory if it does not exist
|
|
if (!fs.existsSync(binPath)) {
|
|
fs.mkdirSync(targetPath)
|
|
}
|
|
|
|
if (fs.lstatSync(binPath).isDirectory())
|
|
{
|
|
fs.readdirSync(binPath).forEach(function (name)
|
|
{
|
|
var binary = path.join(binPath, name);
|
|
|
|
fs.chmodSync(binary, 0o755);
|
|
});
|
|
}
|
|
fs.readdirSync(binPath).forEach(function (name)
|
|
{
|
|
child_process.execSync("sudo ln -sf " + path.join(binPath, name) + " /usr/local/bin/" + name);
|
|
});
|
|
};
|
|
|
|
|
|
global.spawnJake = function(/*String*/ aTaskName)
|
|
{
|
|
//if (systemSync(serializedENV() + " " + "npx --node-options='--inspect-brk' objj-jake" + " " + aTaskName))
|
|
if (systemSync(serializedENV() + " " + "jake" + " " + aTaskName)) {
|
|
console.log("exited in spawnJake with error");
|
|
process.exit(1); //rake abort if ($? != 0)
|
|
}
|
|
};
|
|
|
|
var normalizeCommand = function(/*Array or String*/ command)
|
|
{
|
|
if (Array.isArray(command))
|
|
return command.map(function (arg)
|
|
{
|
|
return enquote(arg);
|
|
}).join(" ");
|
|
else
|
|
return command;
|
|
};
|
|
|
|
global.sudo = function(/*Array or String*/ command)
|
|
{
|
|
// First try without sudo
|
|
command = normalizeCommand(command);
|
|
|
|
var returnCode = systemSync(command + " >/dev/null 2>&1");
|
|
|
|
if (returnCode)
|
|
{
|
|
// if this is set, then disable the use of sudo.
|
|
// This is very usefull for CI scripts and stuff like that
|
|
if (process.env["CAPP_NOSUDO"] == 1)
|
|
return returnCode;
|
|
|
|
return systemSync("sudo -p '\nEnter your admin password: ' " + command);
|
|
}
|
|
|
|
|
|
return 0;
|
|
};
|
|
|
|
global.exec = function(/*Array or String*/ command, quiet)
|
|
{
|
|
command = normalizeCommand(command) + (quiet === true ? " >/dev/null 2>&1" : "");
|
|
return systemSync(command);
|
|
};
|
|
|
|
global.copyManPage = function(/*String*/ name, /*int*/ section)
|
|
{
|
|
var manDir = "/usr/local/share/man/man" + section,
|
|
pageFile = name + "." + section,
|
|
manPagePath = path.join(manDir, pageFile);
|
|
|
|
|
|
|
|
if (!fs.existsSync(manPagePath) || fs.lstatSync(pageFile).mtime > fs.lstatSync(manPagePath).mtime)
|
|
{
|
|
if (!fs.lstatSync(manDir).isDirectory())
|
|
{
|
|
if (sudo(["mkdir", "-p", "-m", "0755", manDir]))
|
|
stream.print("\0red(Unable to create the man directory.\0)");
|
|
}
|
|
|
|
if (sudo(["cp", "-f", pageFile, manDir]))
|
|
stream.print("\0red(Unable to copy the man file.\0)");
|
|
}
|
|
};
|
|
|
|
global.xcodebuildCanListSDKs = function()
|
|
{
|
|
return global.exec("xcodebuild -showsdks", true) === 0;
|
|
};
|
|
|
|
global.xcodebuildHasTenPointFiveSDK = function()
|
|
{
|
|
if (xcodebuildCanListSDKs())
|
|
return global.exec("xcodebuild -showsdks | grep 'macosx10.5'", true) === 0;
|
|
|
|
return fs.existsSync(path.join("/", "Developer", "SDKs", "MacOSX10.5.sdk"));
|
|
};
|
|
|
|
global.colorize = function(/* String */ message, /* String */ color)
|
|
{
|
|
var matches = color.match(/(bold(?: |\+))?(.+)/);
|
|
|
|
if (!matches)
|
|
return;
|
|
|
|
message = "\0" + matches[2] + "(" + message + "\0)";
|
|
|
|
if (matches[1])
|
|
message = "\0bold(" + message + "\0)";
|
|
|
|
return message;
|
|
};
|
|
|
|
global.colorPrint = function(/* String */ message, /* String */ color)
|
|
{
|
|
stream.print(colorize(message, color));
|
|
};
|
|
|
|
// built in tasks
|
|
|
|
task ("build");
|
|
task ("default", "build");
|
|
|
|
task ("release", function()
|
|
{
|
|
process.env["CONFIG"] = "Release";
|
|
spawnJake("build");
|
|
});
|
|
|
|
task ("debug", function()
|
|
{
|
|
process.env["CONFIG"] = "Debug";
|
|
spawnJake("build");
|
|
});
|
|
|
|
task ("all", ["debug", "release"]);
|
|
|
|
task ("sudo-install-symlinks", function()
|
|
{
|
|
sudo("jake install-symlinks");
|
|
});
|
|
|
|
task ("sudo-install-debug-symlinks", function()
|
|
{
|
|
sudo("jake install-debug-symlinks");
|
|
});
|
|
|
|
task ("clean-debug", function()
|
|
{
|
|
process.env['CONFIG'] = 'Debug';
|
|
spawnJake("clean");
|
|
});
|
|
|
|
task ("cleandebug", ["clean-debug"]);
|
|
|
|
task ("clean-release", function()
|
|
{
|
|
process.env["CONFIG"] = "Release";
|
|
spawnJake("clean");
|
|
});
|
|
|
|
task ("cleanrelease", ["clean-release"]);
|
|
|
|
task ("clean-all", ["clean-debug", "clean-release"]);
|
|
task ("cleanall", ["clean-all"]);
|
|
|
|
task ("clobber-debug", function()
|
|
{
|
|
process.env["CONFIG"] = "Debug";
|
|
spawnJake("clobber");
|
|
});
|
|
|
|
task ("clobberdebug", ["clobber-debug"]);
|
|
|
|
task ("clobber-release", function()
|
|
{
|
|
process.env["CONFIG"] = "Release";
|
|
spawnJake("clobber");
|
|
});
|
|
|
|
task ("clobberrelease", ['clobber-release']);
|
|
|
|
task ("clobber-all", ["clobber-debug", "clobber-release"]);
|
|
task ("clobberall", ["clobber-all"]);
|