Skip to content

Commit 6d04378

Browse files
committed
Testing WIP
1 parent 7e0825a commit 6d04378

File tree

15 files changed

+184
-29
lines changed

15 files changed

+184
-29
lines changed

src/compiler/sys.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1073,6 +1073,7 @@ namespace ts {
10731073
_fs.utimesSync(path, time, time);
10741074
}
10751075
catch (e) {
1076+
return;
10761077
}
10771078
}
10781079

@@ -1081,6 +1082,7 @@ namespace ts {
10811082
return _fs.unlinkSync(path);
10821083
}
10831084
catch (e) {
1085+
return;
10841086
}
10851087
}
10861088

src/compiler/tsbuild.ts

Lines changed: 42 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ namespace ts {
77
* The primary thing we track here is which files were written to,
88
* but unchanged, because this enables fast downstream updates
99
*/
10-
interface BuildContext {
10+
export interface BuildContext {
1111
options: BuildOptions;
1212
/**
1313
* Map from output file name to its pre-build timestamp
@@ -299,6 +299,9 @@ namespace ts {
299299

300300
function parseConfigFile(configFilePath: string) {
301301
const sourceFile = host.getSourceFile(configFilePath, ScriptTarget.JSON) as JsonSourceFile;
302+
if (sourceFile === undefined) {
303+
return undefined;
304+
}
302305
const parsed = parseJsonSourceFileConfigFileContent(sourceFile, configParseHost, getDirectoryPath(configFilePath));
303306
parsed.options.configFilePath = configFilePath;
304307
cache.setValue(configFilePath, parsed);
@@ -322,7 +325,7 @@ namespace ts {
322325
return fileExtensionIs(fileName, ".d.ts");
323326
}
324327

325-
function createBuildContext(options: BuildOptions): BuildContext {
328+
export function createBuildContext(options: BuildOptions): BuildContext {
326329
const verboseDiag = options.verbose && createDiagnosticReporter(sys, /*pretty*/ false);
327330
return {
328331
options,
@@ -334,18 +337,15 @@ namespace ts {
334337
};
335338
}
336339

337-
export function performBuild(args: string[]) {
338-
const diagReporter = createDiagnosticReporter(sys, /*pretty*/true);
339-
const host = createCompilerHost({});
340-
340+
export function performBuild(host: CompilerHost, reportDiagnostic: DiagnosticReporter, args: string[]) {
341341
let verbose = false;
342342
let dry = false;
343343
let force = false;
344344
let clean = false;
345345

346346
const projects: string[] = [];
347-
for (let i = 0; i < args.length; i++) {
348-
switch (args[i].toLowerCase()) {
347+
for (const arg of args) {
348+
switch (arg.toLowerCase()) {
349349
case "-v":
350350
case "--verbose":
351351
verbose = true;
@@ -363,7 +363,7 @@ namespace ts {
363363
continue;
364364
}
365365
// Not a flag, parse as filename
366-
addProject(args[i]);
366+
addProject(arg);
367367
}
368368

369369
if (projects.length === 0) {
@@ -372,7 +372,7 @@ namespace ts {
372372
}
373373

374374
const context = createBuildContext({ verbose, dry, force });
375-
const builder = createSolutionBuilder(host, context);
375+
const builder = createSolutionBuilder(host, reportDiagnostic, context);
376376
if (clean) {
377377
builder.cleanProjects(projects);
378378
}
@@ -384,15 +384,14 @@ namespace ts {
384384
const fileName = resolvePath(host.getCurrentDirectory(), projectSpecification);
385385
const refPath = resolveProjectReferencePath(host, { path: fileName });
386386
if (!host.fileExists(refPath)) {
387-
diagReporter(createCompilerDiagnostic(Diagnostics.File_0_does_not_exist, fileName));
387+
reportDiagnostic(createCompilerDiagnostic(Diagnostics.File_0_does_not_exist, fileName));
388388
}
389389
projects.push(refPath);
390390

391391
}
392392
}
393393

394-
export function createSolutionBuilder(host: CompilerHost, context: BuildContext) {
395-
const diagReporter = createDiagnosticReporter(sys, /*pretty*/true);
394+
export function createSolutionBuilder(host: CompilerHost, reportDiagnostic: DiagnosticReporter, context: BuildContext) {
396395
const configFileCache = createConfigFileCache(host);
397396

398397
return {
@@ -418,7 +417,7 @@ namespace ts {
418417
else {
419418
const outputs: string[] = [];
420419
for (const inputFile of project.fileNames) {
421-
(outputs as string[]).push(...getOutputFileNames(inputFile, project));
420+
outputs.push(...getOutputFileNames(inputFile, project));
422421
}
423422
return outputs;
424423
}
@@ -553,7 +552,8 @@ namespace ts {
553552
for (const root of roots) {
554553
const config = configFileCache.parseConfigFile(root);
555554
if (config === undefined) {
556-
throw new Error(`Could not parse ${root}`);
555+
reportDiagnostic(createCompilerDiagnostic(Diagnostics.File_0_does_not_exist, root));
556+
continue;
557557
}
558558
enumerateReferences(normalizePath(root), config);
559559
}
@@ -564,7 +564,7 @@ namespace ts {
564564
dependencyMap
565565
};
566566

567-
function enumerateReferences(fileName: string, root: ts.ParsedCommandLine): void {
567+
function enumerateReferences(fileName: string, root: ParsedCommandLine): void {
568568
const myBuildLevel = buildQueue[buildQueuePosition] = buildQueue[buildQueuePosition] || [];
569569
if (myBuildLevel.indexOf(fileName) < 0) {
570570
myBuildLevel.push(fileName);
@@ -605,7 +605,7 @@ namespace ts {
605605
// TODO Accept parsedCommandLine
606606
function buildSingleProject(proj: string) {
607607
if (context.options.dry) {
608-
diagReporter(createCompilerDiagnostic(Diagnostics.Would_build_project_0, proj));
608+
reportDiagnostic(createCompilerDiagnostic(Diagnostics.Would_build_project_0, proj));
609609
}
610610

611611
context.verbose(Diagnostics.Building_project_0, proj);
@@ -627,18 +627,18 @@ namespace ts {
627627

628628
const programOptions: CreateProgramOptions = {
629629
projectReferences: configFile.projectReferences,
630-
host: host,
630+
host,
631631
rootNames: configFile.fileNames,
632632
options: configFile.options
633633
};
634-
const program = ts.createProgram(programOptions);
634+
const program = createProgram(programOptions);
635635

636636
// Don't emit anything in the presence of syntactic errors or options diagnostics
637637
const syntaxDiagnostics = [...program.getOptionsDiagnostics(), ...program.getSyntacticDiagnostics()];
638638
if (syntaxDiagnostics.length) {
639639
resultFlags |= BuildResultFlags.SyntaxErrors;
640640
for (const diag of syntaxDiagnostics) {
641-
diagReporter(diag);
641+
reportDiagnostic(diag);
642642
}
643643
return resultFlags;
644644
}
@@ -649,7 +649,7 @@ namespace ts {
649649
if (declDiagnostics.length) {
650650
resultFlags |= BuildResultFlags.DeclarationEmitErrors;
651651
for (const diag of declDiagnostics) {
652-
diagReporter(diag);
652+
reportDiagnostic(diag);
653653
}
654654
return resultFlags;
655655
}
@@ -659,13 +659,13 @@ namespace ts {
659659
if (semanticDiagnostics.length) {
660660
resultFlags |= BuildResultFlags.TypeErrors;
661661
for (const diag of semanticDiagnostics) {
662-
diagReporter(diag);
662+
reportDiagnostic(diag);
663663
}
664664
return resultFlags;
665665
}
666666

667667
let newestDeclarationFileContentChangedTime = minimumDate;
668-
program.emit(undefined, (fileName, content, writeBom, onError) => {
668+
program.emit(/*targetSourceFile*/ undefined, (fileName, content, writeBom, onError) => {
669669
let priorChangeTime: Date | undefined;
670670

671671
if (isDeclarationFile(fileName) && host.fileExists(fileName)) {
@@ -690,7 +690,7 @@ namespace ts {
690690

691691
function updateOutputTimestamps(proj: ParsedCommandLine) {
692692
if (context.options.dry) {
693-
diagReporter(createCompilerDiagnostic(Diagnostics.Would_build_project_0, proj.options.configFilePath));
693+
reportDiagnostic(createCompilerDiagnostic(Diagnostics.Would_build_project_0, proj.options.configFilePath));
694694
return;
695695
}
696696

@@ -731,13 +731,29 @@ namespace ts {
731731
}
732732

733733
if (context.options.dry) {
734-
diagReporter(createCompilerDiagnostic(Diagnostics.Would_delete_the_following_files_Colon_0, fileReport.map(f => `\r\n * ${f}`).join("")));
734+
reportDiagnostic(createCompilerDiagnostic(Diagnostics.Would_delete_the_following_files_Colon_0, fileReport.map(f => `\r\n * ${f}`).join("")));
735735
}
736736
}
737737

738738
function buildProjects(configFileNames: string[]) {
739+
const resolvedNames: string[] = [];
740+
for (const name of configFileNames) {
741+
let fullPath = resolvePath(host.getCurrentDirectory(), name);
742+
if (host.fileExists(fullPath)) {
743+
resolvedNames.push(fullPath);
744+
continue;
745+
}
746+
fullPath = combinePaths(fullPath, "tsconfig.json");
747+
if (host.fileExists(fullPath)) {
748+
resolvedNames.push(fullPath);
749+
continue;
750+
}
751+
reportDiagnostic(createCompilerDiagnostic(Diagnostics.File_0_not_found, fullPath));
752+
return;
753+
}
754+
739755
// Establish what needs to be built
740-
const graph = createDependencyGraph(configFileNames);
756+
const graph = createDependencyGraph(resolvedNames);
741757

742758
const queue = graph.buildQueue;
743759
reportBuildQueue(graph);

src/compiler/tsc.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,9 @@ namespace ts {
4848

4949
export function executeCommandLine(args: string[]): void {
5050
if ((args[0].toLowerCase() === "--build") || (args[0].toLowerCase() === "-b")) {
51-
return performBuild(args.slice(1));
51+
return performBuild(createCompilerHost({}), createDiagnosticReporter(sys), args.slice(1));
5252
}
53-
53+
5454
const commandLine = parseCommandLine(args);
5555

5656
// Configuration file name (if any)

src/harness/fakes.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,10 @@ namespace fakes {
131131
return stats ? stats.mtime : undefined;
132132
}
133133

134+
public setModifiedTime(path: string, time: Date) {
135+
this.vfs.utimesSync(path, time, time);
136+
}
137+
134138
public createHash(data: string): string {
135139
return data;
136140
}
@@ -252,6 +256,14 @@ namespace fakes {
252256
return this.sys.directoryExists(directoryName);
253257
}
254258

259+
public getModifiedTime(fileName: string) {
260+
return this.sys.getModifiedTime(fileName);
261+
}
262+
263+
public setModifiedTime(fileName: string, time: Date) {
264+
return this.sys.setModifiedTime(fileName, time);
265+
}
266+
255267
public getDirectories(path: string): string[] {
256268
return this.sys.getDirectories(path);
257269
}

src/harness/tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
"../compiler/builder.ts",
5353
"../compiler/resolutionCache.ts",
5454
"../compiler/watch.ts",
55+
"../compiler/tsbuild.ts",
5556
"../compiler/commandLineParser.ts",
5657

5758
"../services/types.ts",

src/harness/unittests/tsbuild.ts

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/// <reference path="../vfs.ts" />
2+
3+
namespace ts {
4+
let currentTime = 100;
5+
const bfs = new vfs.FileSystem(/*ignoreCase*/ false, { time });
6+
const lastDiagnostics: Diagnostic[] = [];
7+
const reportDiagnostic: DiagnosticReporter = diagnostic => lastDiagnostics.push(diagnostic);
8+
9+
const sampleRoot = resolvePath(__dirname, "../../tests/projects/sample1");
10+
loadFsMirror(bfs, sampleRoot, "/src");
11+
bfs.mkdirpSync("/lib");
12+
bfs.writeFileSync("/lib/lib.d.ts", Harness.IO.readFile(combinePaths(Harness.libFolder, "lib.d.ts")));
13+
bfs.meta.set("defaultLibLocation", "/lib");
14+
bfs.makeReadonly();
15+
16+
describe("tsbuild tests", () => {
17+
it("builds the referenced project", () => {
18+
const fs = bfs.shadow();
19+
const host = new fakes.CompilerHost(fs);
20+
const builder = createSolutionBuilder(host, reportDiagnostic, createBuildContext({ dry: false, force: false, verbose: false }));
21+
22+
fs.chdir("/src/tests");
23+
fs.debugPrint();
24+
builder.buildProjects(["."]);
25+
printDiagnostics();
26+
fs.debugPrint();
27+
assertDiagnosticMessages(Diagnostics.File_0_does_not_exist);
28+
29+
tick();
30+
});
31+
});
32+
33+
function assertDiagnosticMessages(...expected: DiagnosticMessage[]) {
34+
const actual = lastDiagnostics.slice();
35+
actual.sort((a, b) => b.code - a.code);
36+
expected.sort((a, b) => b.code - a.code);
37+
if (actual.length !== expected.length) {
38+
assert.fail<any>(actual, expected, `Diagnostic arrays did not match - expected ${actual.join(",")}, got ${expected.join(",")}`);
39+
}
40+
for (let i = 0; i < actual.length; i++) {
41+
if (actual[i].code !== expected[i].code) {
42+
assert.fail(actual[i].messageText, expected[i].message, "Mismatched error code");
43+
}
44+
}
45+
}
46+
47+
export function printDiagnostics() {
48+
const out = createDiagnosticReporter(sys);
49+
for (const d of lastDiagnostics) {
50+
out(d);
51+
}
52+
}
53+
54+
function tick() {
55+
currentTime += 10;
56+
}
57+
function time() {
58+
return currentTime;
59+
}
60+
61+
function loadFsMirror(vfs: vfs.FileSystem, localRoot: string, virtualRoot: string) {
62+
vfs.mkdirpSync(virtualRoot);
63+
for (const path of Harness.IO.readDirectory(localRoot)) {
64+
const file = getBaseFileName(path);
65+
vfs.writeFileSync(virtualRoot + "/" + file, Harness.IO.readFile(localRoot + "/" + file));
66+
}
67+
for (const dir of Harness.IO.getDirectories(localRoot)){
68+
loadFsMirror(vfs, localRoot + "/" + dir, virtualRoot + "/" + dir);
69+
}
70+
}
71+
}

src/harness/vfs.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ namespace vfs {
55
*/
66
export const builtFolder = "/.ts";
77

8+
/**
9+
* Posix-style path to additional mountable folders (./tests/projects in this repo)
10+
*/
11+
export const projectsFolder = "/.projects";
12+
813
/**
914
* Posix-style path to additional test libraries
1015
*/
@@ -404,7 +409,18 @@ namespace vfs {
404409
}
405410

406411
/**
407-
* Get file status.
412+
* Change file access times
413+
*
414+
* NOTE: do not rename this method as it is intended to align with the same named export of the "fs" module.
415+
*/
416+
public utimesSync(path: string, atime: Date, mtime: Date) {
417+
const entry = this._walk(this._resolve(path));
418+
entry.node.atimeMs = +atime;
419+
entry.node.mtimeMs = +mtime;
420+
}
421+
422+
/**
423+
* Get file status. If `path` is a symbolic link, it is dereferenced.
408424
*
409425
* @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/lstat.html
410426
*
@@ -414,6 +430,7 @@ namespace vfs {
414430
return this._stat(this._walk(this._resolve(path), /*noFollow*/ true));
415431
}
416432

433+
417434
private _stat(entry: WalkResult) {
418435
const node = entry.node;
419436
if (!node) throw createIOError("ENOENT");
@@ -1282,6 +1299,7 @@ namespace vfs {
12821299
files: {
12831300
[builtFolder]: new Mount(vpath.resolve(host.getWorkspaceRoot(), "built/local"), resolver),
12841301
[testLibFolder]: new Mount(vpath.resolve(host.getWorkspaceRoot(), "tests/lib"), resolver),
1302+
[projectsFolder]: new Mount(vpath.resolve(host.getWorkspaceRoot(), "tests/projects"), resolver),
12851303
[srcFolder]: {}
12861304
},
12871305
cwd: srcFolder,
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export function leftPad(s: string, n: number) { return s + n; }
2+
export function multiply(a: number, b: number) { return a * b; }

0 commit comments

Comments
 (0)