2014-07-13 01:04:16 +02:00
// This file contains the build logic for the public repo
var fs = require("fs");
var path = require("path");
2014-09-26 23:36:18 +02:00
var child_process = require("child_process");
2014-07-13 01:04:16 +02:00
// Variables
var compilerDirectory = "src/compiler/";
var servicesDirectory = "src/services/";
var harnessDirectory = "src/harness/";
var libraryDirectory = "src/lib/";
2014-07-24 13:53:42 +02:00
var scriptsDirectory = "scripts/";
2014-10-17 03:13:26 +02:00
var unittestsDirectory = "tests/cases/unittests/";
2014-09-26 23:36:18 +02:00
var docDirectory = "doc/";
2014-07-13 01:04:16 +02:00
var builtDirectory = "built/";
var builtLocalDirectory = "built/local/";
var LKGDirectory = "bin/";
var copyright = "CopyrightNotice.txt";
var thirdParty = "ThirdPartyNoticeText.txt";
2014-07-22 07:08:27 +02:00
// add node_modules to path so we don't need global modules, prefer the modules by adding them first
2014-07-24 04:30:13 +02:00
var nodeModulesPathPrefix = path.resolve("./node_modules/.bin/") + path.delimiter;
if (process.env.path !== undefined) {
process.env.path = nodeModulesPathPrefix + process.env.path;
} else if (process.env.PATH !== undefined) {
process.env.PATH = nodeModulesPathPrefix + process.env.PATH;
}
2014-07-22 07:08:27 +02:00
2014-07-13 01:04:16 +02:00
var compilerSources = [
"core.ts",
"sys.ts",
"types.ts",
"scanner.ts",
"parser.ts",
"binder.ts",
"checker.ts",
"emitter.ts",
"commandLineParser.ts",
2014-08-07 08:21:53 +02:00
"tsc.ts",
2014-07-13 01:04:16 +02:00
"diagnosticInformationMap.generated.ts"
].map(function (f) {
return path.join(compilerDirectory, f);
});
var servicesSources = [
"core.ts",
"types.ts",
"scanner.ts",
"parser.ts",
"binder.ts",
"checker.ts",
"emitter.ts"
].map(function (f) {
return path.join(compilerDirectory, f);
}).concat([
"services.ts",
"shims.ts",
2014-09-24 04:00:45 +02:00
"signatureHelp.ts",
2014-09-30 23:20:58 +02:00
"utilities.ts",
2014-10-08 00:07:49 +02:00
"navigationBar.ts",
"outliningElementsCollector.ts"
2014-07-13 01:04:16 +02:00
].map(function (f) {
return path.join(servicesDirectory, f);
}));
var harnessSources = [
"harness.ts",
"sourceMapRecorder.ts",
2014-07-29 19:37:01 +02:00
"harnessLanguageService.ts",
"fourslash.ts",
2014-07-13 01:04:16 +02:00
"runnerbase.ts",
"compilerRunner.ts",
"typeWriter.ts",
2014-07-29 19:37:01 +02:00
"fourslashRunner.ts",
2014-07-13 01:04:16 +02:00
"projectsRunner.ts",
2014-08-21 00:26:33 +02:00
"loggedIO.ts",
2014-07-13 01:04:16 +02:00
"rwcRunner.ts",
2014-07-29 19:37:01 +02:00
"runner.ts"
2014-07-13 01:04:16 +02:00
].map(function (f) {
return path.join(harnessDirectory, f);
2014-10-17 03:13:26 +02:00
}).concat([
2014-10-30 01:36:39 +01:00
"services/colorization.ts",
2014-10-25 01:03:59 +02:00
"services/documentRegistry.ts",
"services/preProcessFile.ts"
2014-10-17 03:13:26 +02:00
].map(function (f) {
return path.join(unittestsDirectory, f);
}));
2014-07-13 01:04:16 +02:00
var librarySourceMap = [
{ target: "lib.core.d.ts", sources: ["core.d.ts"] },
{ target: "lib.dom.d.ts", sources: ["importcore.d.ts", "extensions.d.ts", "dom.generated.d.ts"], },
{ target: "lib.webworker.d.ts", sources: ["importcore.d.ts", "extensions.d.ts", "webworker.generated.d.ts"], },
{ target: "lib.scriptHost.d.ts", sources: ["importcore.d.ts", "scriptHost.d.ts"], },
{ target: "lib.d.ts", sources: ["core.d.ts", "extensions.d.ts", "dom.generated.d.ts", "webworker.importscripts.d.ts", "scriptHost.d.ts"], },
];
var libraryTargets = librarySourceMap.map(function (f) {
return path.join(builtLocalDirectory, f.target);
});
// Prepends the contents of prefixFile to destinationFile
function prependFile(prefixFile, destinationFile) {
if (!fs.existsSync(prefixFile)) {
fail(prefixFile + " does not exist!");
}
if (!fs.existsSync(destinationFile)) {
fail(destinationFile + " failed to be created!");
}
var temp = "temptemp";
jake.cpR(prefixFile, temp, {silent: true});
fs.appendFileSync(temp, fs.readFileSync(destinationFile));
fs.renameSync(temp, destinationFile);
}
// concatenate a list of sourceFiles to a destinationFile
function concatenateFiles(destinationFile, sourceFiles) {
var temp = "temptemp";
// Copy the first file to temp
if (!fs.existsSync(sourceFiles[0])) {
fail(sourceFiles[0] + " does not exist!");
}
jake.cpR(sourceFiles[0], temp, {silent: true});
// append all files in sequence
for (var i = 1; i < sourceFiles.length; i++) {
if (!fs.existsSync(sourceFiles[i])) {
fail(sourceFiles[i] + " does not exist!");
}
fs.appendFileSync(temp, fs.readFileSync(sourceFiles[i]));
}
// Move the file to the final destination
fs.renameSync(temp, destinationFile);
}
var useDebugMode = false;
2014-10-08 05:51:23 +02:00
var generateDeclarations = false;
2014-09-02 21:19:58 +02:00
var host = (process.env.host || process.env.TYPESCRIPT_HOST || "node");
var compilerFilename = "tsc.js";
2014-07-13 01:04:16 +02:00
/* Compiles a file from a list of sources
* @param outFile: the target file name
* @param sources: an array of the names of the source files
* @param prereqs: prerequisite tasks to compiling the file
* @param prefixes: a list of files to prepend to the target file
* @param useBuiltCompiler: true to use the built compiler, false to use the LKG
* @param noOutFile: true to compile without using --out
*/
function compileFile(outFile, sources, prereqs, prefixes, useBuiltCompiler, noOutFile) {
file(outFile, prereqs, function() {
var dir = useBuiltCompiler ? builtLocalDirectory : LKGDirectory;
var options = "-removeComments --module commonjs -noImplicitAny "; //" -propagateEnumConstants "
2014-10-08 05:51:23 +02:00
if (generateDeclarations) {
options += "--declaration ";
}
2014-07-13 01:04:16 +02:00
2014-09-02 21:19:58 +02:00
var cmd = host + " " + dir + compilerFilename + " " + options + " ";
2014-07-13 01:04:16 +02:00
if (useDebugMode) {
cmd = cmd + " " + path.join(harnessDirectory, "external/es5compat.ts") + " " + path.join(harnessDirectory, "external/json2.ts") + " ";
}
cmd = cmd + sources.join(" ") + (!noOutFile ? " -out " + outFile : "");
if (useDebugMode) {
cmd = cmd + " -sourcemap -mapRoot file:///" + path.resolve(path.dirname(outFile));
}
console.log(cmd + "\n");
var ex = jake.createExec([cmd]);
// Add listeners for output and error
ex.addListener("stdout", function(output) {
process.stdout.write(output);
});
ex.addListener("stderr", function(error) {
process.stderr.write(error);
});
ex.addListener("cmdEnd", function() {
if (!useDebugMode && prefixes && fs.existsSync(outFile)) {
for (var i in prefixes) {
prependFile(prefixes[i], outFile);
}
}
complete();
});
ex.addListener("error", function() {
fs.unlinkSync(outFile);
console.log("Compilation of " + outFile + " unsuccessful");
});
ex.run();
}, {async: true});
}
// Prerequisite task for built directory and library typings
directory(builtLocalDirectory);
for (var i in libraryTargets) {
(function (i) {
var entry = librarySourceMap[i];
var target = libraryTargets[i];
var sources = [copyright].concat(entry.sources.map(function (s) {
return path.join(libraryDirectory, s);
}));
file(target, [builtLocalDirectory].concat(sources), function() {
concatenateFiles(target, sources);
});
})(i);
}
// Lib target to build the library files
desc("Builds the library targets");
task("lib", libraryTargets);
// Generate diagnostics
var processDiagnosticMessagesJs = path.join(scriptsDirectory, "processDiagnosticMessages.js");
var processDiagnosticMessagesTs = path.join(scriptsDirectory, "processDiagnosticMessages.ts");
var diagnosticMessagesJson = path.join(compilerDirectory, "diagnosticMessages.json");
var diagnosticInfoMapTs = path.join(compilerDirectory, "diagnosticInformationMap.generated.ts");
2014-08-01 02:20:57 +02:00
file(processDiagnosticMessagesTs)
2014-07-13 01:04:16 +02:00
// processDiagnosticMessages script
compileFile(processDiagnosticMessagesJs,
[processDiagnosticMessagesTs],
[processDiagnosticMessagesTs],
[],
false);
// The generated diagnostics map; built for the compiler and for the 'generate-diagnostics' task
file(diagnosticInfoMapTs, [processDiagnosticMessagesJs, diagnosticMessagesJson], function () {
var cmd = "node " + processDiagnosticMessagesJs + " " + diagnosticMessagesJson;
console.log(cmd);
var ex = jake.createExec([cmd]);
// Add listeners for output and error
ex.addListener("stdout", function(output) {
process.stdout.write(output);
});
ex.addListener("stderr", function(error) {
process.stderr.write(error);
});
ex.addListener("cmdEnd", function() {
complete();
});
ex.run();
}, {async: true})
desc("Generates a diagnostic file in TypeScript based on an input JSON file");
task("generate-diagnostics", [diagnosticInfoMapTs])
// Local target to build the compiler and services
2014-09-02 21:19:58 +02:00
var tscFile = path.join(builtLocalDirectory, compilerFilename);
2014-08-07 08:21:53 +02:00
compileFile(tscFile, compilerSources, [builtLocalDirectory, copyright].concat(compilerSources), [copyright], /*useBuiltCompiler:*/ false);
2014-07-13 01:04:16 +02:00
2014-08-07 08:21:53 +02:00
var servicesFile = path.join(builtLocalDirectory, "typescriptServices.js");
compileFile(servicesFile, servicesSources, [builtLocalDirectory, copyright].concat(servicesSources), [copyright], /*useBuiltCompiler:*/ true);
2014-07-13 01:04:16 +02:00
// Local target to build the compiler and services
desc("Builds the full compiler and services");
2014-08-07 08:21:53 +02:00
task("local", ["generate-diagnostics", "lib", tscFile, servicesFile]);
2014-07-13 01:04:16 +02:00
// Local target to build the compiler and services
desc("Emit debug mode files with sourcemaps");
task("debug", function() {
2014-10-08 05:51:23 +02:00
useDebugMode = true;
2014-07-13 01:04:16 +02:00
});
// Set the default task to "local"
task("default", ["local"]);
// Cleans the built directory
desc("Cleans the compiler output, declare files, and tests");
task("clean", function() {
jake.rmRf(builtDirectory);
});
2014-10-08 05:51:23 +02:00
// generate declarations for compiler and services
desc("Generate declarations for compiler and services");
task("declaration", function() {
generateDeclarations = true;
});
2014-09-26 23:36:18 +02:00
// Generate Markdown spec
var word2mdJs = path.join(scriptsDirectory, "word2md.js");
var word2mdTs = path.join(scriptsDirectory, "word2md.ts");
var specWord = path.join(docDirectory, "TypeScript Language Specification.docx");
var specMd = path.join(docDirectory, "spec.md");
file(word2mdTs);
// word2md script
compileFile(word2mdJs,
[word2mdTs],
[word2mdTs],
[],
false);
// The generated spec.md; built for the 'generate-spec' task
file(specMd, [word2mdJs, specWord], function () {
2014-10-08 05:51:23 +02:00
var specWordFullPath = path.resolve(specWord);
2014-10-16 22:27:48 +02:00
var cmd = "cscript //nologo " + word2mdJs + ' "' + specWordFullPath + '" ' + specMd;
2014-10-08 05:51:23 +02:00
console.log(cmd);
child_process.exec(cmd, function () {
complete();
});
2014-09-26 23:36:18 +02:00
}, {async: true})
desc("Generates a Markdown version of the Language Specification");
task("generate-spec", [specMd])
2014-07-13 01:04:16 +02:00
// Makes a new LKG. This target does not build anything, but errors if not all the outputs are present in the built/local directory
desc("Makes a new LKG out of the built js files");
task("LKG", libraryTargets, function() {
2014-08-07 08:21:53 +02:00
var expectedFiles = [tscFile, servicesFile].concat(libraryTargets);
2014-07-13 01:04:16 +02:00
var missingFiles = expectedFiles.filter(function (f) {
return !fs.existsSync(f);
});
if (missingFiles.length > 0) {
fail("Cannot replace the LKG unless all built targets are present in directory " + builtLocalDirectory +
". The following files are missing:\n" + missingFiles.join("\n"));
}
// Copy all the targets into the LKG directory
jake.mkdirP(LKGDirectory);
for (i in expectedFiles) {
jake.cpR(expectedFiles[i], LKGDirectory);
}
//var resourceDirectories = fs.readdirSync(builtLocalResourcesDirectory).map(function(p) { return path.join(builtLocalResourcesDirectory, p); });
//resourceDirectories.map(function(d) {
// jake.cpR(d, LKGResourcesDirectory);
//});
});
// Test directory
directory(builtLocalDirectory);
// Task to build the tests infrastructure using the built compiler
var run = path.join(builtLocalDirectory, "run.js");
2014-08-07 08:21:53 +02:00
compileFile(run, harnessSources, [builtLocalDirectory, tscFile].concat(libraryTargets).concat(harnessSources), [], /*useBuiltCompiler:*/ true);
2014-07-13 01:04:16 +02:00
var localBaseline = "tests/baselines/local/";
var refBaseline = "tests/baselines/reference/";
var localRwcBaseline = "tests/baselines/rwc/local/";
var refRwcBaseline = "tests/baselines/rwc/reference/";
desc("Builds the test infrastructure using the built compiler");
task("tests", ["local", run].concat(libraryTargets));
2014-07-17 21:32:51 +02:00
function exec(cmd, completeHandler) {
2014-07-13 01:04:16 +02:00
var ex = jake.createExec([cmd]);
// Add listeners for output and error
ex.addListener("stdout", function(output) {
process.stdout.write(output);
});
ex.addListener("stderr", function(error) {
process.stderr.write(error);
});
ex.addListener("cmdEnd", function() {
2014-07-17 21:32:51 +02:00
if (completeHandler) {
completeHandler();
}
2014-07-13 01:04:16 +02:00
complete();
});
2014-07-24 02:27:30 +02:00
ex.addListener("error", function(e, status) {
2014-09-02 23:22:14 +02:00
process.stderr.write(status);
process.stderr.write(e);
complete();
2014-07-24 02:27:30 +02:00
})
2014-07-13 01:04:16 +02:00
try{
2014-09-18 20:49:40 +02:00
ex.run();
2014-07-13 01:04:16 +02:00
} catch(e) {
console.log('Exception: ' + e)
}
}
function cleanTestDirs() {
// Clean the local baselines directory
if (fs.existsSync(localBaseline)) {
jake.rmRf(localBaseline);
}
// Clean the local Rwc baselines directory
if (fs.existsSync(localRwcBaseline)) {
jake.rmRf(localRwcBaseline);
}
jake.mkdirP(localBaseline);
}
// used to pass data from jake command line directly to run.js
function writeTestConfigFile(tests, testConfigFile) {
console.log('Running test(s): ' + tests);
var testConfigContents = '{\n' + '\ttest: [\'' + tests + '\']\n}';
2014-09-18 20:49:40 +02:00
fs.writeFileSync('test.config', testConfigContents);
2014-07-13 01:04:16 +02:00
}
2014-07-17 21:32:51 +02:00
function deleteTemporaryProjectOutput() {
if (fs.existsSync(localBaseline + "projectOutput/")) {
jake.rmRf(localBaseline + "projectOutput/");
}
}
2014-08-02 00:06:43 +02:00
var testTimeout = 5000;
2014-07-13 01:04:16 +02:00
desc("Runs the tests using the built run.js file. Syntax is jake runtests. Optional parameters 'host=', 'tests=[regex], reporter=[list|spec|json|<more>]'.");
task("runtests", ["tests", builtLocalDirectory], function() {
cleanTestDirs();
host = "mocha"
2014-07-18 00:26:50 +02:00
tests = process.env.test || process.env.tests || process.env.t;
2014-07-13 01:04:16 +02:00
var testConfigFile = 'test.config';
if(fs.existsSync(testConfigFile)) {
fs.unlinkSync(testConfigFile);
}
2014-09-03 19:37:32 +02:00
2014-07-13 01:04:16 +02:00
if(tests) {
writeTestConfigFile(tests, testConfigFile);
}
2014-09-03 19:37:32 +02:00
if (tests && tests.toLocaleLowerCase() === "rwc") {
testTimeout = 50000;
}
2014-07-13 01:04:16 +02:00
colors = process.env.colors || process.env.color
colors = colors ? ' --no-colors ' : ''
tests = tests ? ' -g ' + tests : '';
reporter = process.env.reporter || process.env.r || 'dot';
2014-07-26 02:01:01 +02:00
// timeout normally isn't necessary but Travis-CI has been timing out on compiler baselines occasionally
// default timeout is 2sec which really should be enough, but maybe we just need a small amount longer
2014-08-02 00:06:43 +02:00
var cmd = host + " -R " + reporter + tests + colors + ' -t ' + testTimeout + ' ' + run;
2014-07-13 01:04:16 +02:00
console.log(cmd);
2014-07-17 21:32:51 +02:00
exec(cmd, deleteTemporaryProjectOutput);
2014-07-13 01:04:16 +02:00
}, {async: true});
2014-07-25 01:03:13 +02:00
desc("Generates code coverage data via instanbul")
task("generate-code-coverage", ["tests", builtLocalDirectory], function () {
2014-09-02 23:22:14 +02:00
var cmd = 'istanbul cover node_modules/mocha/bin/_mocha -- -R min -t ' + testTimeout + ' ' + run;
console.log(cmd);
2014-09-18 20:49:40 +02:00
exec(cmd);
2014-07-25 01:03:13 +02:00
}, { async: true });
2014-07-13 01:04:16 +02:00
// Browser tests
var nodeServerOutFile = 'tests/webTestServer.js'
var nodeServerInFile = 'tests/webTestServer.ts'
2014-08-07 08:21:53 +02:00
compileFile(nodeServerOutFile, [nodeServerInFile], [builtLocalDirectory, tscFile], [], true, true);
2014-07-13 01:04:16 +02:00
desc("Runs browserify on run.js to produce a file suitable for running tests in the browser");
task("browserify", ["tests", builtLocalDirectory, nodeServerOutFile], function() {
var cmd = 'browserify built/local/run.js -o built/local/bundle.js';
exec(cmd);
}, {async: true});
desc("Runs the tests using the built run.js file like 'jake runtests'. Syntax is jake runtests-browser. Additional optional parameters tests=[regex], port=, browser=[chrome|IE]");
task("runtests-browser", ["tests", "browserify", builtLocalDirectory], function() {
cleanTestDirs();
host = "node"
2014-07-18 00:26:50 +02:00
port = process.env.port || process.env.p || '8888';
browser = process.env.browser || process.env.b || "IE";
tests = process.env.test || process.env.tests || process.env.t;
2014-07-13 01:04:16 +02:00
var testConfigFile = 'test.config';
if(fs.existsSync(testConfigFile)) {
fs.unlinkSync(testConfigFile);
}
if(tests) {
writeTestConfigFile(tests, testConfigFile);
}
tests = tests ? tests : '';
var cmd = host + " tests/webTestServer.js " + port + " " + browser + " " + tests
console.log(cmd);
exec(cmd);
}, {async: true});
// Baseline Diff
desc("Diffs the compiler baselines using the diff tool specified by the %DIFF% environment variable");
task('diff', function () {
var cmd = "%DIFF% " + refBaseline + ' ' + localBaseline;
console.log(cmd)
exec(cmd);
}, {async: true});
desc("Diffs the RWC baselines using the diff tool specified by the %DIFF% environment variable");
task('diff-rwc', function () {
var cmd = "%DIFF% " + refRwcBaseline + ' ' + localRwcBaseline;
console.log(cmd)
exec(cmd);
}, {async: true});
desc("Builds the test sources and automation in debug mode");
task("tests-debug", ["setDebugMode", "tests"]);
// Makes the test results the new baseline
desc("Makes the most recent test results the new baseline, overwriting the old baseline");
task("baseline-accept", function(hardOrSoft) {
if (!hardOrSoft || hardOrSoft == "hard") {
jake.rmRf(refBaseline);
fs.renameSync(localBaseline, refBaseline);
}
else if (hardOrSoft == "soft") {
var files = jake.readdirR(localBaseline);
for (var i in files) {
jake.cpR(files[i], refBaseline);
}
jake.rmRf(path.join(refBaseline, "local"));
}
});
desc("Makes the most recent rwc test results the new baseline, overwriting the old baseline");
task("baseline-accept-rwc", function() {
jake.rmRf(refRwcBaseline);
fs.renameSync(localRwcBaseline, refRwcBaseline);
});
// Webhost
var webhostPath = "tests/webhost/webtsc.ts";
var webhostJsPath = "tests/webhost/webtsc.js";
2014-08-07 08:21:53 +02:00
compileFile(webhostJsPath, [webhostPath], [tscFile, webhostPath].concat(libraryTargets), [], true);
2014-07-13 01:04:16 +02:00
desc("Builds the tsc web host");
task("webhost", [webhostJsPath], function() {
2014-09-02 23:22:14 +02:00
jake.cpR(path.join(builtLocalDirectory, "lib.d.ts"), "tests/webhost/", {silent: true});
2014-07-13 01:04:16 +02:00
});
// Perf compiler
2014-08-07 08:21:53 +02:00
var perftscPath = "tests/perftsc.ts";
var perftscJsPath = "built/local/perftsc.js";
compileFile(perftscJsPath, [perftscPath], [tscFile, perftscPath, "tests/perfsys.ts"].concat(libraryTargets), [], true);
2014-07-13 01:04:16 +02:00
desc("Builds augmented version of the compiler for perf tests");
2014-08-07 08:21:53 +02:00
task("perftsc", [perftscJsPath]);
2014-09-02 21:19:58 +02:00
// Instrumented compiler
var loggedIOpath = harnessDirectory + 'loggedIO.ts';
var loggedIOJsPath = builtLocalDirectory + 'loggedIO.js';
2014-09-02 23:22:14 +02:00
file(loggedIOJsPath, [builtLocalDirectory, loggedIOpath], function() {
var temp = builtLocalDirectory + 'temp';
2014-09-02 21:19:58 +02:00
jake.mkdirP(temp);
var options = "--outdir " + temp + ' ' + loggedIOpath;
var cmd = host + " " + LKGDirectory + compilerFilename + " " + options + " ";
console.log(cmd + "\n");
var ex = jake.createExec([cmd]);
ex.addListener("cmdEnd", function() {
2014-09-02 23:22:14 +02:00
fs.renameSync(temp + '/harness/loggedIO.js', loggedIOJsPath);
jake.rmRf(temp);
complete();
2014-09-02 21:19:58 +02:00
});
ex.run();
}, {async: true});
var instrumenterPath = harnessDirectory + 'instrumenter.ts';
var instrumenterJsPath = builtLocalDirectory + 'instrumenter.js';
compileFile(instrumenterJsPath, [instrumenterPath], [tscFile, instrumenterPath], [], true);
desc("Builds an instrumented tsc.js");
task('tsc-instrumented', [loggedIOJsPath, instrumenterJsPath, tscFile], function() {
2014-09-02 23:22:14 +02:00
var cmd = host + ' ' + instrumenterJsPath + ' record iocapture ' + builtLocalDirectory + compilerFilename;
console.log(cmd);
2014-09-02 21:19:58 +02:00
var ex = jake.createExec([cmd]);
2014-09-02 23:22:14 +02:00
ex.addListener("cmdEnd", function() {
complete();
2014-09-02 21:19:58 +02:00
});
2014-09-02 23:22:14 +02:00
ex.run();
}, { async: true });