Files
cappuccino/common.jake
T

613 lines
17 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 jake = require("objj-jake");
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.CLEAN = require("objj-jake/lib/jake/clean.js").CLEAN;
global.CLOBBER = require("objj-jake/lib/jake/clean.js").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()
{
console.log("in 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)
{
print("setupEnvironment warning: " + e);
//throw e;
}
}
function setupEnvironment()
{
try
{
debugger;
require("objj-runtime").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.rm(aFilename, {recursive: true, force: true}); }
catch (anException) { }
};
// from stackoverflow
function copyRecursiveSync (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("i systemSync");
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() + " " + "npx objj-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();
console.log(" Version: \0purple(" + pkg["version"] + "\0)");
console.log(" Revision: \0purple(" + pkg["cappuccino-revision"] + "\0)");
console.log(" 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.lstatSync(sourcePath).isDirectory())
return;
var packageName = path.basename(sourcePath),
targetPath = path.join(SYSTEM.prefix, "packages", packageName);
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"))
if (fs.lstatSync(binPath).isDirectory())
{
fs.readdirSync(binPath).forEach(function (name)
{
var binary = path.join(binPath, name);
fs.chmodSync(binary, 0o755);
});
}
};
global.spawnJake = function(/*String*/ aTaskName)
{
console.log("i spawnJake");
//if (systemSync(serializedENV() + " " + "npx --node-options='--inspect-brk' objj-jake" + " " + aTaskName))
if (systemSync(serializedENV() + " " + "npx objj-jake" + " " + aTaskName))
console.log("exited in spawnJake with code 1");
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()
{
console.log("in debug mode");
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"]);