Merge pull request #581 from Microsoft/instrument
Add jake task for making instrumented tsc
This commit is contained in:
commit
4381f725ed
54
Jakefile
54
Jakefile
|
@ -123,6 +123,8 @@ function concatenateFiles(destinationFile, sourceFiles) {
|
|||
}
|
||||
|
||||
var useDebugMode = false;
|
||||
var host = (process.env.host || process.env.TYPESCRIPT_HOST || "node");
|
||||
var compilerFilename = "tsc.js";
|
||||
/* 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
|
||||
|
@ -134,10 +136,9 @@ var useDebugMode = false;
|
|||
function compileFile(outFile, sources, prereqs, prefixes, useBuiltCompiler, noOutFile) {
|
||||
file(outFile, prereqs, function() {
|
||||
var dir = useBuiltCompiler ? builtLocalDirectory : LKGDirectory;
|
||||
var compilerFilename = "tsc.js";
|
||||
var options = "-removeComments --module commonjs -noImplicitAny "; //" -propagateEnumConstants "
|
||||
|
||||
var cmd = (process.env.host || process.env.TYPESCRIPT_HOST || "node") + " " + dir + compilerFilename + " " + options + " ";
|
||||
var cmd = host + " " + dir + compilerFilename + " " + options + " ";
|
||||
if (useDebugMode) {
|
||||
cmd = cmd + " " + path.join(harnessDirectory, "external/es5compat.ts") + " " + path.join(harnessDirectory, "external/json2.ts") + " ";
|
||||
}
|
||||
|
@ -230,7 +231,7 @@ task("generate-diagnostics", [diagnosticInfoMapTs])
|
|||
|
||||
|
||||
// Local target to build the compiler and services
|
||||
var tscFile = path.join(builtLocalDirectory, "tsc.js");
|
||||
var tscFile = path.join(builtLocalDirectory, compilerFilename);
|
||||
compileFile(tscFile, compilerSources, [builtLocalDirectory, copyright].concat(compilerSources), [copyright], /*useBuiltCompiler:*/ false);
|
||||
|
||||
var servicesFile = path.join(builtLocalDirectory, "typescriptServices.js");
|
||||
|
@ -312,9 +313,9 @@ function exec(cmd, completeHandler) {
|
|||
complete();
|
||||
});
|
||||
ex.addListener("error", function(e, status) {
|
||||
process.stderr.write(status);
|
||||
process.stderr.write(e);
|
||||
complete();
|
||||
process.stderr.write(status);
|
||||
process.stderr.write(e);
|
||||
complete();
|
||||
})
|
||||
try{
|
||||
ex.run();
|
||||
|
@ -382,9 +383,9 @@ task("runtests", ["tests", builtLocalDirectory], function() {
|
|||
|
||||
desc("Generates code coverage data via instanbul")
|
||||
task("generate-code-coverage", ["tests", builtLocalDirectory], function () {
|
||||
var cmd = 'istanbul cover node_modules/mocha/bin/_mocha -- -R min -t ' + testTimeout + ' ' + run;
|
||||
console.log(cmd);
|
||||
exec(cmd);
|
||||
var cmd = 'istanbul cover node_modules/mocha/bin/_mocha -- -R min -t ' + testTimeout + ' ' + run;
|
||||
console.log(cmd);
|
||||
exec(cmd);
|
||||
}, { async: true });
|
||||
|
||||
// Browser tests
|
||||
|
@ -469,7 +470,7 @@ compileFile(webhostJsPath, [webhostPath], [tscFile, webhostPath].concat(libraryT
|
|||
|
||||
desc("Builds the tsc web host");
|
||||
task("webhost", [webhostJsPath], function() {
|
||||
jake.cpR(path.join(builtLocalDirectory, "lib.d.ts"), "tests/webhost/", {silent: true});
|
||||
jake.cpR(path.join(builtLocalDirectory, "lib.d.ts"), "tests/webhost/", {silent: true});
|
||||
});
|
||||
|
||||
// Perf compiler
|
||||
|
@ -478,3 +479,36 @@ var perftscJsPath = "built/local/perftsc.js";
|
|||
compileFile(perftscJsPath, [perftscPath], [tscFile, perftscPath, "tests/perfsys.ts"].concat(libraryTargets), [], true);
|
||||
desc("Builds augmented version of the compiler for perf tests");
|
||||
task("perftsc", [perftscJsPath]);
|
||||
|
||||
// Instrumented compiler
|
||||
var loggedIOpath = harnessDirectory + 'loggedIO.ts';
|
||||
var loggedIOJsPath = builtLocalDirectory + 'loggedIO.js';
|
||||
file(loggedIOJsPath, [builtLocalDirectory, loggedIOpath], function() {
|
||||
var temp = builtLocalDirectory + 'temp';
|
||||
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() {
|
||||
fs.renameSync(temp + '/harness/loggedIO.js', loggedIOJsPath);
|
||||
jake.rmRf(temp);
|
||||
complete();
|
||||
});
|
||||
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() {
|
||||
var cmd = host + ' ' + instrumenterJsPath + ' record iocapture ' + builtLocalDirectory + compilerFilename;
|
||||
console.log(cmd);
|
||||
var ex = jake.createExec([cmd]);
|
||||
ex.addListener("cmdEnd", function() {
|
||||
complete();
|
||||
});
|
||||
ex.run();
|
||||
}, { async: true });
|
||||
|
|
|
@ -732,8 +732,10 @@ module Harness {
|
|||
|
||||
var filemap: { [name: string]: ts.SourceFile; } = {};
|
||||
var register = (file: { unitName: string; content: string; }) => {
|
||||
var filename = Path.switchToForwardSlashes(file.unitName);
|
||||
filemap[getCanonicalFileName(filename)] = ts.createSourceFile(filename, file.content, options.target, /*version:*/ "0");
|
||||
if (file.content !== undefined) {
|
||||
var filename = Path.switchToForwardSlashes(file.unitName);
|
||||
filemap[getCanonicalFileName(filename)] = ts.createSourceFile(filename, file.content, options.target, /*version:*/ "0");
|
||||
}
|
||||
};
|
||||
inputFiles.forEach(register);
|
||||
otherFiles.forEach(register);
|
||||
|
@ -824,7 +826,7 @@ module Harness {
|
|||
globalErrors.forEach(err => outputErrorText(err));
|
||||
|
||||
// 'merge' the lines of each input file with any errors associated with it
|
||||
inputFiles.forEach(inputFile => {
|
||||
inputFiles.filter(f => f.content !== undefined).forEach(inputFile => {
|
||||
// Filter down to the errors in the file
|
||||
var fileErrors = diagnostics.filter(e => {
|
||||
var errFn = e.filename;
|
||||
|
@ -1253,7 +1255,7 @@ module Harness {
|
|||
}
|
||||
|
||||
export function isLibraryFile(filePath: string): boolean {
|
||||
return filePath.indexOf('lib.d.ts') >= 0 || filePath.indexOf('lib.core.d.ts') >= 0;
|
||||
return (Path.getFileName(filePath) === 'lib.d.ts') || (Path.getFileName(filePath) === 'lib.core.d.ts');
|
||||
}
|
||||
|
||||
if (Error) (<any>Error).stackTraceLimit = 1;
|
||||
|
|
53
src/harness/instrumenter.ts
Normal file
53
src/harness/instrumenter.ts
Normal file
|
@ -0,0 +1,53 @@
|
|||
declare var require: any, process: any;
|
||||
var fs: any = require('fs');
|
||||
var path: any = require('path');
|
||||
|
||||
function instrumentForRecording(fn: string, tscPath: string) {
|
||||
instrument(tscPath, 'sys = Playback.wrapSystem(sys); sys.startRecord("' + fn + '");', 'sys.endRecord();');
|
||||
}
|
||||
|
||||
function instrumentForReplay(logFilename: string, tscPath: string) {
|
||||
instrument(tscPath, 'sys = Playback.wrapSystem(sys); sys.startReplay("' + logFilename + '");');
|
||||
}
|
||||
|
||||
function instrument(tscPath: string, prepareCode: string, cleanupCode: string = '') {
|
||||
var bak = tscPath + '.bak';
|
||||
fs.exists(bak, (backupExists: boolean) => {
|
||||
var filename = tscPath;
|
||||
if (backupExists) {
|
||||
filename = bak;
|
||||
}
|
||||
|
||||
fs.readFile(filename, 'utf-8', (err: any, tscContent: string) => {
|
||||
if (err) throw err;
|
||||
|
||||
fs.writeFile(bak, tscContent, (err: any) => {
|
||||
if (err) throw err;
|
||||
|
||||
fs.readFile(path.resolve(path.dirname(tscPath) + '/loggedIO.js'), 'utf-8', (err: any, loggerContent: string) => {
|
||||
if (err) throw err;
|
||||
|
||||
var invocationLine = 'ts.executeCommandLine(sys.args);';
|
||||
var index1 = tscContent.indexOf(invocationLine);
|
||||
var index2 = index1 + invocationLine.length;
|
||||
var newContent = tscContent.substr(0, index1) + loggerContent + prepareCode + invocationLine + cleanupCode + tscContent.substr(index2) + '\r\n';
|
||||
fs.writeFile(tscPath, newContent);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
var isJson = (arg: string) => arg.indexOf(".json") > 0;
|
||||
|
||||
var record = process.argv.indexOf('record');
|
||||
var tscPath = process.argv[process.argv.length - 1];
|
||||
if (record >= 0) {
|
||||
console.log('Instrumenting ' + tscPath + ' for recording');
|
||||
instrumentForRecording(process.argv[record + 1], tscPath);
|
||||
} else if (process.argv.some(isJson)) {
|
||||
var filename = process.argv.filter(isJson)[0];
|
||||
instrumentForReplay(filename, tscPath);
|
||||
}
|
||||
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
/// <reference path="..\..\src\compiler\sys.ts" />
|
||||
/// <reference path="..\..\src\harness\harness.ts" />
|
||||
/// <reference path="..\..\src\harness\runnerbase.ts" />
|
||||
|
||||
interface FileInformation {
|
||||
contents: string;
|
||||
|
@ -93,6 +95,7 @@ module Playback {
|
|||
|
||||
function createEmptyLog(): IOLog {
|
||||
return {
|
||||
timestamp: (new Date()).toString(),
|
||||
arguments: [],
|
||||
currentDirectory: '',
|
||||
filesRead: [],
|
||||
|
@ -119,6 +122,8 @@ module Playback {
|
|||
};
|
||||
wrapper.startReplayFromData = log => {
|
||||
replayLog = log;
|
||||
// Remove non-found files from the log (shouldn't really need them, but we still record them for diganostic purposes)
|
||||
replayLog.filesRead = replayLog.filesRead.filter(f => f.result.contents !== undefined);
|
||||
};
|
||||
|
||||
wrapper.endReplay = () => {
|
||||
|
@ -170,22 +175,46 @@ module Playback {
|
|||
}
|
||||
|
||||
function findResultByPath<T>(wrapper: { resolvePath(s: string): string }, logArray: { path: string; result?: T }[], expectedPath: string, defaultValue?: T): T {
|
||||
var results = logArray.filter(e => pathsAreEquivalent(e.path, expectedPath, wrapper));
|
||||
if (results.length === 0) {
|
||||
if (defaultValue === undefined) {
|
||||
throw new Error('No matching result in log array for path: ' + expectedPath);
|
||||
} else {
|
||||
return defaultValue;
|
||||
var normalizedName = Harness.Path.switchToForwardSlashes(expectedPath).toLowerCase();
|
||||
// Try to find the result through normal filename
|
||||
for (var i = 0; i < logArray.length; i++) {
|
||||
if (Harness.Path.switchToForwardSlashes(logArray[i].path).toLowerCase() === normalizedName) {
|
||||
return logArray[i].result;
|
||||
}
|
||||
}
|
||||
return results[0].result;
|
||||
// Fallback, try to resolve the target paths as well
|
||||
if (replayLog.pathsResolved.length > 0) {
|
||||
var normalizedResolvedName = wrapper.resolvePath(expectedPath).toLowerCase();
|
||||
for (var i = 0; i < logArray.length; i++) {
|
||||
if (wrapper.resolvePath(logArray[i].path).toLowerCase() === normalizedResolvedName) {
|
||||
return logArray[i].result;
|
||||
}
|
||||
}
|
||||
}
|
||||
// If we got here, we didn't find a match
|
||||
if (defaultValue === undefined) {
|
||||
throw new Error('No matching result in log array for path: ' + expectedPath);
|
||||
} else {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
var pathEquivCache: any = {};
|
||||
function pathsAreEquivalent(left: string, right: string, wrapper: { resolvePath(s: string): string }) {
|
||||
var key = left + '-~~-' + right;
|
||||
function areSame(a: string, b: string) {
|
||||
return Harness.Path.switchToForwardSlashes(a).toLowerCase() === Harness.Path.switchToForwardSlashes(b).toLowerCase();
|
||||
}
|
||||
return areSame(left, right) || areSame(wrapper.resolvePath(left), right) || areSame(left, wrapper.resolvePath(right)) || areSame(wrapper.resolvePath(left), wrapper.resolvePath(right));
|
||||
function check() {
|
||||
if (Harness.Path.getFileName(left).toLowerCase() === Harness.Path.getFileName(right).toLowerCase()) {
|
||||
return areSame(left, right) || areSame(wrapper.resolvePath(left), right) || areSame(left, wrapper.resolvePath(right)) || areSame(wrapper.resolvePath(left), wrapper.resolvePath(right));
|
||||
}
|
||||
}
|
||||
if (pathEquivCache.hasOwnProperty(key)) {
|
||||
return pathEquivCache[key];
|
||||
} else {
|
||||
return pathEquivCache[key] = check();
|
||||
}
|
||||
}
|
||||
|
||||
function noOpReplay(name: string) {
|
||||
|
@ -258,7 +287,12 @@ module Playback {
|
|||
memoize((path) => findResultByFields(replayLog.pathsResolved, { path: path }, !ts.isRootedDiskPath(ts.normalizeSlashes(path)) && replayLog.currentDirectory ? replayLog.currentDirectory + '/' + path : ts.normalizeSlashes(path))));
|
||||
|
||||
wrapper.readFile = recordReplay(wrapper.readFile, underlying)(
|
||||
(path) => callAndRecord(underlying.readFile(path), recordLog.filesRead, { path: path, codepage: 0 }),
|
||||
(path) => {
|
||||
var result = underlying.readFile(path);
|
||||
var logEntry = { path: path, codepage: 0, result: { contents: result, codepage: 0 } };
|
||||
recordLog.filesRead.push(logEntry);
|
||||
return result;
|
||||
},
|
||||
memoize((path) => findResultByPath(wrapper, replayLog.filesRead, path).contents));
|
||||
|
||||
wrapper.writeFile = recordReplay(wrapper.writeFile, underlying)(
|
||||
|
|
|
@ -88,7 +88,7 @@ module RWC {
|
|||
}
|
||||
});
|
||||
|
||||
// do not use lib since we shouldnt be reading any files that arent in the ioLog
|
||||
// do not use lib since we already read it in above
|
||||
opts.options.noLib = true;
|
||||
|
||||
// Emit the results
|
||||
|
|
Loading…
Reference in a new issue