Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Api for tsc --build and --incremental #31432

Merged
merged 44 commits into from
Jun 5, 2019
Merged
Changes from 1 commit
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
5443447
Instead of maintaining queue for invalidated projects, use the pendin…
sheetalkamat Apr 18, 2019
1de70de
No need to calculate and store project references graph
sheetalkamat Apr 18, 2019
b9eeaab
Remove unused variable
sheetalkamat Apr 18, 2019
3e77b96
Fix the graph ordering test case to check actual order and not just m…
sheetalkamat Apr 18, 2019
845f67a
Make api to return build order
sheetalkamat Apr 18, 2019
e9d4e0b
Unchanged output time is no more required
sheetalkamat Apr 18, 2019
9236502
Remove `getUpToDateStatusOfFile` from solution builder since that tes…
sheetalkamat Apr 18, 2019
b42e76b
Remove usage of fileMap for output unchanged timestamp modification a…
sheetalkamat Apr 18, 2019
d4474a5
More refactoring to move file map creation inside solution builder
sheetalkamat Apr 18, 2019
c615d48
Make use of maps in build order calculation
sheetalkamat Apr 18, 2019
50741e6
Use maps for all the watch data structures
sheetalkamat Apr 18, 2019
579d2bf
Make FileMap Apis to match map but just ensure that keys are always p…
sheetalkamat Apr 19, 2019
05257e8
configFileCache, projectStatus, buildInfoChecked is now ConfigFileMap
sheetalkamat Apr 19, 2019
fd6773f
Diagnostics as ConfigFileMap
sheetalkamat Apr 19, 2019
11b21fb
builderPrograms as ConfigFileMap
sheetalkamat Apr 19, 2019
65ed413
Remove resolveProjectName as api
sheetalkamat Apr 19, 2019
ddee617
Make SolutionBuilder and SolutionBuilderWithWatch separate
sheetalkamat Apr 19, 2019
4b572be
Instead of having two separate paths for building projects, (build al…
sheetalkamat Apr 19, 2019
04a972b
More refactoring
sheetalkamat Apr 19, 2019
1a75c62
Remove resetBuildContext
sheetalkamat Apr 19, 2019
9ba4ab1
Merge branch 'master' into builderAPI
sheetalkamat Apr 30, 2019
5b361c8
Make API to build project and wire cancellation token
sheetalkamat May 2, 2019
e8074f7
Rename cleanAll to clean and take optional project as input
sheetalkamat May 2, 2019
3da4796
Remove startWatching as explicit method from api
sheetalkamat May 2, 2019
5c18513
Make SolutionBuilder as Public API
sheetalkamat May 2, 2019
0a25524
Enable apis to create incremental program
sheetalkamat May 2, 2019
71b190a
Create api for buildNextProject
sheetalkamat May 2, 2019
f017433
Move everything into state so we can pass it around
sheetalkamat May 3, 2019
6227fab
Make invalidated project when only need to be built or updated
sheetalkamat May 7, 2019
5270b7e
Make invalidated projects as api so we can expose it later
sheetalkamat May 7, 2019
e4fe4ac
Make update bundle return invalidated project if it cant update the b…
sheetalkamat May 7, 2019
9f9ae00
Enable getSemanticDiagnosticsOfNextAffectedFile for EmitAndSemanticDi…
sheetalkamat May 7, 2019
f0b7e08
Move towards BuildInvalidatedProject api where one can query program …
sheetalkamat May 9, 2019
8c489bf
UpdateBundleProject<T> to contain emit method
sheetalkamat May 9, 2019
97fcea1
Api to get next invalidated project
sheetalkamat May 9, 2019
b3dac18
Merge branch 'master' into builderAPI
sheetalkamat May 9, 2019
f069534
Merge branch 'master' into builderAPI
sheetalkamat May 14, 2019
d7c3b5e
Add getParsedCommandLine as optional method on SolutionBuilderHost
sheetalkamat May 14, 2019
89d1475
Add writeFileCallbacks to done method and also on host
sheetalkamat May 15, 2019
629bc0c
Always emit tsbuild info if path says so (irrespecitive of if there e…
sheetalkamat May 15, 2019
0cb980d
Add api to build referenced projects
sheetalkamat May 15, 2019
ec4ea0e
Watch only built projects
sheetalkamat May 16, 2019
138f757
Fix the test since tsbuildinfo is now always emitted (629bc0c)
sheetalkamat May 16, 2019
098c900
Make more build options internal which correspond to internal compile…
sheetalkamat May 16, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 32 additions & 27 deletions src/compiler/tsbuild.ts
Original file line number Diff line number Diff line change
@@ -256,7 +256,7 @@ namespace ts {

export interface SolutionBuilder {
build(project?: string, cancellationToken?: CancellationToken): ExitStatus;
cleanAllProjects(): ExitStatus;
clean(project?: string): ExitStatus;

// Currently used for testing but can be made public if needed:
/*@internal*/ getBuildOrder(): ReadonlyArray<ResolvedConfigFileName>;
@@ -404,7 +404,7 @@ namespace ts {
} :
{
build,
cleanAllProjects,
clean,
getBuildOrder,
getUpToDateStatusOfProject,
invalidateProject,
@@ -1342,10 +1342,12 @@ namespace ts {
return priorNewestUpdateTime;
}

function getFilesToClean(): string[] {
// Get the same graph for cleaning we'd use for building
const filesToDelete: string[] = [];
for (const proj of getBuildOrder()) {
function clean(project?: string) {
const buildOrder = getBuildOrderFor(project);
if (!buildOrder) return ExitStatus.InvalidProject_OutputsSkipped;

const filesToDelete = options.dry ? [] as string[] : undefined;
for (const proj of buildOrder) {
const resolvedPath = toResolvedConfigFilePath(proj);
const parsed = parseConfigFile(proj, resolvedPath);
if (parsed === undefined) {
@@ -1356,22 +1358,19 @@ namespace ts {
const outputs = getAllProjectOutputs(parsed, !host.useCaseSensitiveFileNames());
for (const output of outputs) {
if (host.fileExists(output)) {
filesToDelete.push(output);
if (filesToDelete) {
filesToDelete.push(output);
}
else {
host.deleteFile(output);
invalidateResolvedProject(resolvedPath, ConfigFileProgramReloadLevel.None);
}
}
}
}
return filesToDelete;
}

function cleanAllProjects() {
const filesToDelete = getFilesToClean();
if (options.dry) {
if (filesToDelete) {
reportStatus(Diagnostics.A_non_dry_build_would_delete_the_following_files_Colon_0, filesToDelete.map(f => `\r\n * ${f}`).join(""));
return ExitStatus.Success;
}

for (const output of filesToDelete) {
host.deleteFile(output);
}

return ExitStatus.Success;
@@ -1429,7 +1428,23 @@ namespace ts {
cacheState = undefined;
}

function getBuildOrderFor(project: string | undefined) {
const resolvedProject = project && resolveProjectName(project);
if (resolvedProject) {
const projectPath = toResolvedConfigFilePath(resolvedProject);
const projectIndex = findIndex(
getBuildOrder(),
configFileName => toResolvedConfigFilePath(configFileName) === projectPath
);
if (projectIndex === -1) return undefined;
}
return resolvedProject ? createBuildOrder([resolvedProject]) : getBuildOrder();
}

function build(project?: string, cancellationToken?: CancellationToken): ExitStatus {
const buildOrder = getBuildOrderFor(project);
if (!buildOrder) return ExitStatus.InvalidProject_OutputsSkipped;

// Set initial build if not already built
if (allProjectBuildPending) {
allProjectBuildPending = false;
@@ -1447,16 +1462,6 @@ namespace ts {

let successfulProjects = 0;
let errorProjects = 0;
const resolvedProject = project && resolveProjectName(project);
if (resolvedProject) {
const projectPath = toResolvedConfigFilePath(resolvedProject);
const projectIndex = findIndex(
getBuildOrder(),
configFileName => toResolvedConfigFilePath(configFileName) === projectPath
);
if (projectIndex === -1) return ExitStatus.InvalidProject_OutputsSkipped;
}
const buildOrder = resolvedProject ? createBuildOrder([resolvedProject]) : getBuildOrder();
while (true) {
const invalidatedProject = getNextInvalidatedProject(buildOrder);
if (!invalidatedProject) {
8 changes: 2 additions & 6 deletions src/testRunner/unittests/tsbuild/emptyFiles.ts
Original file line number Diff line number Diff line change
@@ -18,9 +18,7 @@ namespace ts {
host.assertDiagnosticMessages([Diagnostics.The_files_list_in_config_file_0_is_empty, "/src/no-references/tsconfig.json"]);

// Check for outputs to not be written.
for (const output of allExpectedOutputs) {
assert(!fs.existsSync(output), `Expect file ${output} to not exist`);
}
verifyOutputsAbsent(fs, allExpectedOutputs);
});

it("does not have empty files diagnostic when files is empty and references are provided", () => {
@@ -33,9 +31,7 @@ namespace ts {
host.assertDiagnosticMessages(/*empty*/);

// Check for outputs to be written.
for (const output of allExpectedOutputs) {
assert(fs.existsSync(output), `Expect file ${output} to exist`);
}
verifyOutputsPresent(fs, allExpectedOutputs);
});
});
}
12 changes: 12 additions & 0 deletions src/testRunner/unittests/tsbuild/helpers.ts
Original file line number Diff line number Diff line change
@@ -82,6 +82,18 @@ declare const console: { log(msg: any): void; };`;
return fs;
}

export function verifyOutputsPresent(fs: vfs.FileSystem, outputs: readonly string[]) {
for (const output of outputs) {
assert(fs.existsSync(output), `Expect file ${output} to exist`);
}
}

export function verifyOutputsAbsent(fs: vfs.FileSystem, outputs: readonly string[]) {
for (const output of outputs) {
assert.isFalse(fs.existsSync(output), `Expect file ${output} to not exist`);
}
}

function generateSourceMapBaselineFiles(fs: vfs.FileSystem, mapFileNames: ReadonlyArray<string>) {
for (const mapFile of mapFileNames) {
if (!fs.existsSync(mapFile)) continue;
48 changes: 24 additions & 24 deletions src/testRunner/unittests/tsbuild/outFile.ts
Original file line number Diff line number Diff line change
@@ -366,18 +366,14 @@ namespace ts {
builder.build();
host.assertDiagnosticMessages(...initialExpectedDiagnostics);
// Verify they exist
for (const output of expectedOutputs) {
assert(fs.existsSync(output), `Expect file ${output} to exist`);
}
verifyOutputsPresent(fs, expectedOutputs);
host.clearDiagnostics();
builder.cleanAllProjects();
builder.clean();
host.assertDiagnosticMessages(/*none*/);
// Verify they are gone
for (const output of expectedOutputs) {
assert(!fs.existsSync(output), `Expect file ${output} to not exist`);
}
verifyOutputsAbsent(fs, expectedOutputs);
// Subsequent clean shouldn't throw / etc
builder.cleanAllProjects();
builder.clean();
});

it("verify buildInfo absence results in new build", () => {
@@ -392,9 +388,7 @@ namespace ts {
builder.build();
host.assertDiagnosticMessages(...initialExpectedDiagnostics);
// Verify they exist
for (const output of expectedOutputs) {
assert(fs.existsSync(output), `Expect file ${output} to exist`);
}
verifyOutputsPresent(fs, expectedOutputs);
// Delete bundle info
host.clearDiagnostics();
host.deleteFile(outputFiles[project.first][ext.buildinfo]);
@@ -419,10 +413,8 @@ namespace ts {
builder.build();
host.assertDiagnosticMessages(...initialExpectedDiagnostics);
// Verify they exist - without tsbuildinfo for third project
for (const output of expectedOutputFiles.slice(0, expectedOutputFiles.length - 2)) {
assert(fs.existsSync(output), `Expect file ${output} to exist`);
}
assert.isFalse(fs.existsSync(outputFiles[project.third][ext.buildinfo]), `Expect file ${outputFiles[project.third][ext.buildinfo]} to not exist`);
verifyOutputsPresent(fs, expectedOutputFiles.slice(0, expectedOutputFiles.length - 2));
verifyOutputsAbsent(fs, [outputFiles[project.third][ext.buildinfo]]);
});

it("rebuilds completely when version in tsbuildinfo doesnt match ts version", () => {
@@ -496,13 +488,23 @@ namespace ts {
const result = builder.build(sources[project.second][source.config]);
host.assertDiagnosticMessages(/*empty*/);
// First and Third is not built
for (const output of [...outputFiles[project.first], ...outputFiles[project.third]]) {
assert.isFalse(fs.existsSync(output), `Expect file ${output} to not exist`);
}
verifyOutputsAbsent(fs, [...outputFiles[project.first], ...outputFiles[project.third]]);
// second is built
for (const output of outputFiles[project.second]) {
assert(fs.existsSync(output), `Expect file ${output} to exist`);
}
verifyOutputsPresent(fs, outputFiles[project.second]);
assert.equal(result, ExitStatus.Success);
});

it("cleans till project specified", () => {
const fs = outFileFs.shadow();
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host, { verbose: false });
builder.build();
const result = builder.clean(sources[project.second][source.config]);
host.assertDiagnosticMessages(/*empty*/);
// First and Third output for present
verifyOutputsPresent(fs, [...outputFiles[project.first], ...outputFiles[project.third]]);
// second is cleaned
verifyOutputsAbsent(fs, outputFiles[project.second]);
assert.equal(result, ExitStatus.Success);
});

@@ -940,9 +942,7 @@ ${internal} enum internalEnum { a, b, c }`);
removeFileExtension(f) + Extension.Dts + ".map",
])
]);
for (const output of expectedOutputFiles) {
assert(fs.existsSync(output), `Expect file ${output} to exist`);
}
verifyOutputsPresent(fs, expectedOutputFiles);
});
});
}
24 changes: 6 additions & 18 deletions src/testRunner/unittests/tsbuild/referencesWithRootDirInParent.ts
Original file line number Diff line number Diff line change
@@ -21,9 +21,7 @@ namespace ts {
const builder = createSolutionBuilder(host, ["/src/src/main", "/src/src/other"], {});
builder.build();
host.assertDiagnosticMessages(/*empty*/);
for (const output of allExpectedOutputs) {
assert(fs.existsSync(output), `Expect file ${output} to exist`);
}
verifyOutputsPresent(fs, allExpectedOutputs);
});

it("verify that it reports error for same .tsbuildinfo file because no rootDir in the base", () => {
@@ -48,12 +46,8 @@ namespace ts {
[Diagnostics.Building_project_0, "/src/src/main/tsconfig.json"],
[Diagnostics.Cannot_write_file_0_because_it_will_overwrite_tsbuildinfo_file_generated_by_referenced_project_1, "/src/dist/tsconfig.tsbuildinfo", "/src/src/other"]
);
for (const output of allExpectedOutputs) {
assert(fs.existsSync(output), `Expect file ${output} to exist`);
}
for (const output of missingOutputs) {
assert.isFalse(fs.existsSync(output), `Expect file ${output} to not exist`);
}
verifyOutputsPresent(fs, allExpectedOutputs);
verifyOutputsAbsent(fs, missingOutputs);
});

it("verify that it reports error for same .tsbuildinfo file", () => {
@@ -84,12 +78,8 @@ namespace ts {
[Diagnostics.Building_project_0, "/src/src/main/tsconfig.json"],
[Diagnostics.Cannot_write_file_0_because_it_will_overwrite_tsbuildinfo_file_generated_by_referenced_project_1, "/src/dist/tsconfig.tsbuildinfo", "/src/src/other"]
);
for (const output of allExpectedOutputs) {
assert(fs.existsSync(output), `Expect file ${output} to exist`);
}
for (const output of missingOutputs) {
assert.isFalse(fs.existsSync(output), `Expect file ${output} to not exist`);
}
verifyOutputsPresent(fs, allExpectedOutputs);
verifyOutputsAbsent(fs, missingOutputs);
});

it("verify that it reports no error when .tsbuildinfo differ", () => {
@@ -120,9 +110,7 @@ namespace ts {
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/src/main/tsconfig.main.json", "src/dist/a.js"],
[Diagnostics.Building_project_0, "/src/src/main/tsconfig.main.json"]
);
for (const output of allExpectedOutputs) {
assert(fs.existsSync(output), `Expect file ${output} to exist`);
}
verifyOutputsPresent(fs, allExpectedOutputs);
});
});
}
14 changes: 4 additions & 10 deletions src/testRunner/unittests/tsbuild/resolveJsonModule.ts
Original file line number Diff line number Diff line change
@@ -23,9 +23,7 @@ namespace ts {
host.assertDiagnosticMessages(...expectedDiagnosticMessages);
if (!expectedDiagnosticMessages.length) {
// Check for outputs. Not an exhaustive list
for (const output of allExpectedOutputs) {
assert(fs.existsSync(output), `Expect file ${output} to exist`);
}
verifyOutputsPresent(fs, allExpectedOutputs);
}
}

@@ -71,9 +69,7 @@ export default hello.hello`);
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, configFile, "src/dist/src/index.js"],
[Diagnostics.Building_project_0, `/${configFile}`]
);
for (const output of [...allExpectedOutputs, "/src/dist/src/index.js.map"]) {
assert(fs.existsSync(output), `Expect file ${output} to exist`);
}
verifyOutputsPresent(fs, [...allExpectedOutputs, "/src/dist/src/index.js.map"]);
host.clearDiagnostics();
builder = createSolutionBuilder(host, [configFile], { verbose: true });
tick();
@@ -96,9 +92,7 @@ export default hello.hello`);
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, configFile, "src/src/index.js"],
[Diagnostics.Building_project_0, `/${configFile}`]
);
for (const output of ["/src/src/index.js", "/src/src/index.d.ts"]) {
assert(fs.existsSync(output), `Expect file ${output} to exist`);
}
verifyOutputsPresent(fs, ["/src/src/index.js", "/src/src/index.d.ts"]);
host.clearDiagnostics();
builder = createSolutionBuilder(host, [configFile], { verbose: true });
tick();
@@ -137,7 +131,7 @@ export default hello.hello`);
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, mainConfigFile, "src/main/index.js"],
[Diagnostics.Building_project_0, `/${mainConfigFile}`],
);
assert(fs.existsSync(expectedOutput), `Expect file ${expectedOutput} to exist`);
verifyOutputsPresent(fs, [expectedOutput]);
host.clearDiagnostics();
builder = createSolutionBuilder(host, [configFile], { verbose: true });
tick();
Loading