Make API to build project and wire cancellation token

This commit is contained in:
Sheetal Nandi
2019-05-02 14:16:06 -07:00
parent 9ba4ab1eae
commit 5b361c8497
14 changed files with 234 additions and 118 deletions
+108 -45
View File
@@ -255,7 +255,7 @@ namespace ts {
}
export interface SolutionBuilder {
buildAllProjects(): ExitStatus;
build(project?: string, cancellationToken?: CancellationToken): ExitStatus;
cleanAllProjects(): ExitStatus;
// Currently used for testing but can be made public if needed:
@@ -264,14 +264,21 @@ namespace ts {
// Testing only
/*@internal*/ getUpToDateStatusOfProject(project: string): UpToDateStatus;
/*@internal*/ invalidateProject(configFileName: string, reloadLevel?: ConfigFileProgramReloadLevel): void;
/*@internal*/ buildInvalidatedProject(): void;
/*@internal*/ buildNextInvalidatedProject(): void;
}
export interface SolutionBuilderWithWatch {
buildAllProjects(): ExitStatus;
build(project?: string, cancellationToken?: CancellationToken): ExitStatus;
/*@internal*/ startWatching(): void;
}
interface InvalidatedProject {
project: ResolvedConfigFileName;
projectPath: ResolvedConfigFilePath;
reloadLevel: ConfigFileProgramReloadLevel;
projectIndex: number;
}
/**
* Create a function that reports watch status by writing to the system and handles the formating of the diagnostic
*/
@@ -386,18 +393,22 @@ namespace ts {
const allWatchedInputFiles = createMap() as ConfigFileMap<Map<FileWatcher>>;
const allWatchedConfigFiles = createMap() as ConfigFileMap<FileWatcher>;
let allProjectBuildPending = true;
let needsSummary = true;
// let watchAllProjectsPending = watch;
return watch ?
{
buildAllProjects,
build,
startWatching
} :
{
buildAllProjects,
build,
cleanAllProjects,
getBuildOrder,
getUpToDateStatusOfProject,
invalidateProject,
buildInvalidatedProject,
buildNextInvalidatedProject,
};
function toPath(fileName: string) {
@@ -800,6 +811,7 @@ namespace ts {
configFileCache.delete(resolved);
buildOrder = undefined;
}
needsSummary = true;
clearProjectStatus(resolved);
addProjToQueue(resolved, reloadLevel);
enableCache();
@@ -823,16 +835,16 @@ namespace ts {
}
}
function getNextInvalidatedProject() {
Debug.assert(hasPendingInvalidatedProjects());
return forEach(getBuildOrder(), (project, projectIndex) => {
const projectPath = toResolvedConfigFilePath(project);
const reloadLevel = projectPendingBuild.get(projectPath);
if (reloadLevel !== undefined) {
projectPendingBuild.delete(projectPath);
return { project, projectPath, reloadLevel, projectIndex };
}
});
function getNextInvalidatedProject(buildOrder: readonly ResolvedConfigFileName[]): InvalidatedProject | undefined {
return hasPendingInvalidatedProjects() ?
forEach(buildOrder, (project, projectIndex) => {
const projectPath = toResolvedConfigFilePath(project);
const reloadLevel = projectPendingBuild.get(projectPath);
if (reloadLevel !== undefined) {
return { project, projectPath, reloadLevel, projectIndex };
}
}) :
undefined;
}
function hasPendingInvalidatedProjects() {
@@ -846,18 +858,19 @@ namespace ts {
if (timerToBuildInvalidatedProject) {
hostWithWatch.clearTimeout(timerToBuildInvalidatedProject);
}
timerToBuildInvalidatedProject = hostWithWatch.setTimeout(buildInvalidatedProject, 250);
timerToBuildInvalidatedProject = hostWithWatch.setTimeout(buildNextInvalidatedProject, 250);
}
function buildInvalidatedProject() {
function buildNextInvalidatedProject() {
timerToBuildInvalidatedProject = undefined;
if (reportFileChangeDetected) {
reportFileChangeDetected = false;
projectErrorsReported.clear();
reportWatchStatus(Diagnostics.File_change_detected_Starting_incremental_compilation);
}
if (hasPendingInvalidatedProjects()) {
buildNextInvalidatedProject();
const invalidatedProject = getNextInvalidatedProject(getBuildOrder());
if (invalidatedProject) {
buildInvalidatedProject(invalidatedProject);
if (hasPendingInvalidatedProjects()) {
if (watch && !timerToBuildInvalidatedProject) {
scheduleBuildInvalidatedProject();
@@ -872,6 +885,7 @@ namespace ts {
function reportErrorSummary() {
if (watch || host.reportErrorSummary) {
needsSummary = false;
// Report errors from the other projects
getBuildOrder().forEach(project => {
const projectPath = toResolvedConfigFilePath(project);
@@ -890,11 +904,11 @@ namespace ts {
}
}
function buildNextInvalidatedProject() {
const { project, projectPath, reloadLevel, projectIndex } = getNextInvalidatedProject()!;
function buildInvalidatedProject({ project, projectPath, reloadLevel, projectIndex }: InvalidatedProject, cancellationToken?: CancellationToken) {
const config = parseConfigFile(project, projectPath);
if (!config) {
reportParseConfigFileDiagnostic(projectPath);
projectPendingBuild.delete(projectPath);
return;
}
@@ -920,6 +934,7 @@ namespace ts {
// In a dry build, inform the user of this fact
reportStatus(Diagnostics.Project_0_is_up_to_date, project);
}
projectPendingBuild.delete(projectPath);
return;
}
@@ -927,24 +942,28 @@ namespace ts {
reportAndStoreErrors(projectPath, config.errors);
// Fake that files have been built by updating output file stamps
updateOutputTimestamps(config, projectPath);
projectPendingBuild.delete(projectPath);
return;
}
if (status.type === UpToDateStatusType.UpstreamBlocked) {
reportAndStoreErrors(projectPath, config.errors);
if (options.verbose) reportStatus(Diagnostics.Skipping_build_of_project_0_because_its_dependency_1_has_errors, project, status.upstreamProjectName);
projectPendingBuild.delete(projectPath);
return;
}
if (status.type === UpToDateStatusType.ContainerOnly) {
reportAndStoreErrors(projectPath, config.errors);
// Do nothing
projectPendingBuild.delete(projectPath);
return;
}
const buildResult = needsBuild(status, config) ?
buildSingleProject(project, projectPath) : // Actual build
updateBundle(project, projectPath); // Fake that files have been built by manipulating prepend and existing output
buildSingleProject(project, projectPath, cancellationToken) : // Actual build
updateBundle(project, projectPath, cancellationToken); // Fake that files have been built by manipulating prepend and existing output
projectPendingBuild.delete(projectPath);
// Only composite projects can be referenced by other projects
if (!(buildResult & BuildResultFlags.AnyErrors) && config.options.composite) {
queueReferencingProjects(project, projectPath, projectIndex, !(buildResult & BuildResultFlags.DeclarationOutputUnchanged));
@@ -1050,7 +1069,7 @@ namespace ts {
}
}
function buildSingleProject(proj: ResolvedConfigFileName, resolvedPath: ResolvedConfigFilePath): BuildResultFlags {
function buildSingleProject(proj: ResolvedConfigFileName, resolvedPath: ResolvedConfigFilePath, cancellationToken: CancellationToken | undefined): BuildResultFlags {
if (options.dry) {
reportStatus(Diagnostics.A_non_dry_build_would_build_project_0, proj);
return BuildResultFlags.Success;
@@ -1112,15 +1131,15 @@ namespace ts {
// Don't emit anything in the presence of syntactic errors or options diagnostics
const syntaxDiagnostics = [
...program.getConfigFileParsingDiagnostics(),
...program.getOptionsDiagnostics(),
...program.getGlobalDiagnostics(),
...program.getSyntacticDiagnostics()];
...program.getOptionsDiagnostics(cancellationToken),
...program.getGlobalDiagnostics(cancellationToken),
...program.getSyntacticDiagnostics(/*sourceFile*/ undefined, cancellationToken)];
if (syntaxDiagnostics.length) {
return buildErrors(syntaxDiagnostics, BuildResultFlags.SyntaxErrors, "Syntactic");
}
// Same as above but now for semantic diagnostics
const semanticDiagnostics = program.getSemanticDiagnostics();
const semanticDiagnostics = program.getSemanticDiagnostics(/*sourceFile*/ undefined, cancellationToken);
if (semanticDiagnostics.length) {
return buildErrors(semanticDiagnostics, BuildResultFlags.TypeErrors, "Semantic");
}
@@ -1132,7 +1151,14 @@ namespace ts {
let declDiagnostics: Diagnostic[] | undefined;
const reportDeclarationDiagnostics = (d: Diagnostic) => (declDiagnostics || (declDiagnostics = [])).push(d);
const outputFiles: OutputFile[] = [];
emitFilesAndReportErrors(program, reportDeclarationDiagnostics, /*writeFileName*/ undefined, /*reportSummary*/ undefined, (name, text, writeByteOrderMark) => outputFiles.push({ name, text, writeByteOrderMark }));
emitFilesAndReportErrors(
program,
reportDeclarationDiagnostics,
/*writeFileName*/ undefined,
/*reportSummary*/ undefined,
(name, text, writeByteOrderMark) => outputFiles.push({ name, text, writeByteOrderMark }),
cancellationToken
);
// Don't emit .d.ts if there are decl file errors
if (declDiagnostics) {
program.restoreState();
@@ -1221,7 +1247,7 @@ namespace ts {
return readBuilderProgram(parsed.options, readFileWithCache) as any as T;
}
function updateBundle(proj: ResolvedConfigFileName, resolvedPath: ResolvedConfigFilePath): BuildResultFlags {
function updateBundle(proj: ResolvedConfigFileName, resolvedPath: ResolvedConfigFilePath, cancellationToken: CancellationToken | undefined): BuildResultFlags {
if (options.dry) {
reportStatus(Diagnostics.A_non_dry_build_would_update_output_of_project_0, proj);
return BuildResultFlags.Success;
@@ -1241,7 +1267,7 @@ namespace ts {
});
if (isString(outputFiles)) {
reportStatus(Diagnostics.Cannot_update_output_of_project_0_because_there_was_error_reading_file_1, proj, relName(outputFiles));
return buildSingleProject(proj, resolvedPath);
return buildSingleProject(proj, resolvedPath, cancellationToken);
}
// Actual Emit
@@ -1403,21 +1429,58 @@ namespace ts {
cacheState = undefined;
}
function buildAllProjects(): ExitStatus {
if (options.watch) { reportWatchStatus(Diagnostics.Starting_compilation_in_watch_mode); }
enableCache();
function build(project?: string, cancellationToken?: CancellationToken): ExitStatus {
// Set initial build if not already built
if (allProjectBuildPending) {
allProjectBuildPending = false;
if (options.watch) { reportWatchStatus(Diagnostics.Starting_compilation_in_watch_mode); }
enableCache();
const buildOrder = getBuildOrder();
reportBuildQueue(buildOrder);
buildOrder.forEach(configFileName =>
projectPendingBuild.set(toResolvedConfigFilePath(configFileName), ConfigFileProgramReloadLevel.None));
const buildOrder = getBuildOrder();
reportBuildQueue(buildOrder);
buildOrder.forEach(configFileName =>
projectPendingBuild.set(toResolvedConfigFilePath(configFileName), ConfigFileProgramReloadLevel.None));
while (hasPendingInvalidatedProjects()) {
buildNextInvalidatedProject();
if (cancellationToken) {
cancellationToken.throwIfCancellationRequested();
}
}
reportErrorSummary();
disableCache();
return diagnostics.size ? ExitStatus.DiagnosticsPresent_OutputsSkipped : ExitStatus.Success;
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) {
if (needsSummary) {
disableCache();
reportErrorSummary();
}
break;
}
buildInvalidatedProject(invalidatedProject, cancellationToken);
if (diagnostics.has(invalidatedProject.projectPath)) {
errorProjects++;
}
else {
successfulProjects++;
}
}
return errorProjects ?
successfulProjects ?
ExitStatus.DiagnosticsPresent_OutputsGenerated :
ExitStatus.DiagnosticsPresent_OutputsSkipped :
ExitStatus.Success;
}
function needsBuild(status: UpToDateStatus, config: ParsedCommandLine) {
+3
View File
@@ -3053,6 +3053,9 @@ namespace ts {
// Diagnostics were produced and outputs were generated in spite of them.
DiagnosticsPresent_OutputsGenerated = 2,
// When build skipped because passed in project is invalid
InvalidProject_OutputsSkipped = 3,
}
export interface EmitResult {
+18 -11
View File
@@ -113,12 +113,12 @@ namespace ts {
getCurrentDirectory(): string;
getCompilerOptions(): CompilerOptions;
getSourceFiles(): ReadonlyArray<SourceFile>;
getSyntacticDiagnostics(): ReadonlyArray<Diagnostic>;
getOptionsDiagnostics(): ReadonlyArray<Diagnostic>;
getGlobalDiagnostics(): ReadonlyArray<Diagnostic>;
getSemanticDiagnostics(): ReadonlyArray<Diagnostic>;
getSyntacticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic>;
getOptionsDiagnostics(cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic>;
getGlobalDiagnostics(cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic>;
getSemanticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic>;
getConfigFileParsingDiagnostics(): ReadonlyArray<Diagnostic>;
emit(targetSourceFile?: SourceFile, writeFile?: WriteFileCallback): EmitResult;
emit(targetSourceFile?: SourceFile, writeFile?: WriteFileCallback, cancellationToken?: CancellationToken): EmitResult;
}
export function listFiles(program: ProgramToEmitFilesAndReportErrors, writeFileName: (s: string) => void) {
@@ -132,25 +132,32 @@ namespace ts {
/**
* Helper that emit files, report diagnostics and lists emitted and/or source files depending on compiler options
*/
export function emitFilesAndReportErrors(program: ProgramToEmitFilesAndReportErrors, reportDiagnostic: DiagnosticReporter, writeFileName?: (s: string) => void, reportSummary?: ReportEmitErrorSummary, writeFile?: WriteFileCallback) {
export function emitFilesAndReportErrors(
program: ProgramToEmitFilesAndReportErrors,
reportDiagnostic: DiagnosticReporter,
writeFileName?: (s: string) => void,
reportSummary?: ReportEmitErrorSummary,
writeFile?: WriteFileCallback,
cancellationToken?: CancellationToken
) {
// First get and report any syntactic errors.
const diagnostics = program.getConfigFileParsingDiagnostics().slice();
const configFileParsingDiagnosticsLength = diagnostics.length;
addRange(diagnostics, program.getSyntacticDiagnostics());
addRange(diagnostics, program.getSyntacticDiagnostics(/*sourceFile*/ undefined, cancellationToken));
// If we didn't have any syntactic errors, then also try getting the global and
// semantic errors.
if (diagnostics.length === configFileParsingDiagnosticsLength) {
addRange(diagnostics, program.getOptionsDiagnostics());
addRange(diagnostics, program.getGlobalDiagnostics());
addRange(diagnostics, program.getOptionsDiagnostics(cancellationToken));
addRange(diagnostics, program.getGlobalDiagnostics(cancellationToken));
if (diagnostics.length === configFileParsingDiagnosticsLength) {
addRange(diagnostics, program.getSemanticDiagnostics());
addRange(diagnostics, program.getSemanticDiagnostics(/*sourceFile*/ undefined, cancellationToken));
}
}
// Emit and report any errors we ran into.
const { emittedFiles, emitSkipped, diagnostics: emitDiagnostics } = program.emit(/*targetSourceFile*/ undefined, writeFile);
const { emittedFiles, emitSkipped, diagnostics: emitDiagnostics } = program.emit(/*targetSourceFile*/ undefined, writeFile, cancellationToken);
addRange(diagnostics, emitDiagnostics);
sortAndDeduplicateDiagnostics(diagnostics).forEach(reportDiagnostic);
@@ -14,7 +14,7 @@ namespace ts {
const builder = createSolutionBuilder(host, ["/src/no-references"], { dry: false, force: false, verbose: false });
host.clearDiagnostics();
builder.buildAllProjects();
builder.build();
host.assertDiagnosticMessages([Diagnostics.The_files_list_in_config_file_0_is_empty, "/src/no-references/tsconfig.json"]);
// Check for outputs to not be written.
@@ -29,7 +29,7 @@ namespace ts {
const builder = createSolutionBuilder(host, ["/src/with-references"], { dry: false, force: false, verbose: false });
host.clearDiagnostics();
builder.buildAllProjects();
builder.build();
host.assertDiagnosticMessages(/*empty*/);
// Check for outputs to be written.
+1 -1
View File
@@ -172,7 +172,7 @@ declare const console: { log(msg: any): void; };`;
}
return originalReadFile.call(host, path);
};
builder.buildAllProjects();
builder.build();
generateSourceMapBaselineFiles(fs, expectedMapFileNames);
generateBuildInfoSectionBaselineFiles(fs, expectedBuildInfoFilesForSectionBaselines || emptyArray);
fs.makeReadonly();
@@ -5,7 +5,7 @@ namespace ts {
const fs = projFs.shadow();
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host, ["/src/tsconfig.json"], {});
builder.buildAllProjects();
builder.build();
host.assertDiagnosticMessages(
[Diagnostics.The_specified_path_does_not_exist_Colon_0, "/src/foobar.json"],
[Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2, "/src/tsconfig.first.json", "[\"**/*\"]", "[]"],
+27 -10
View File
@@ -363,7 +363,7 @@ namespace ts {
];
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host);
builder.buildAllProjects();
builder.build();
host.assertDiagnosticMessages(...initialExpectedDiagnostics);
// Verify they exist
for (const output of expectedOutputs) {
@@ -389,7 +389,7 @@ namespace ts {
];
const host = new fakes.SolutionBuilderHost(fs);
let builder = createSolutionBuilder(host);
builder.buildAllProjects();
builder.build();
host.assertDiagnosticMessages(...initialExpectedDiagnostics);
// Verify they exist
for (const output of expectedOutputs) {
@@ -399,7 +399,7 @@ namespace ts {
host.clearDiagnostics();
host.deleteFile(outputFiles[project.first][ext.buildinfo]);
builder = createSolutionBuilder(host);
builder.buildAllProjects();
builder.build();
host.assertDiagnosticMessages(
getExpectedDiagnosticForProjectsInBuild(relSources[project.first][source.config], relSources[project.second][source.config], relSources[project.third][source.config]),
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, relSources[project.first][source.config], relOutputFiles[project.first][ext.buildinfo]],
@@ -416,7 +416,7 @@ namespace ts {
const host = new fakes.SolutionBuilderHost(fs);
replaceText(fs, sources[project.third][source.config], `"composite": true,`, "");
const builder = createSolutionBuilder(host);
builder.buildAllProjects();
builder.build();
host.assertDiagnosticMessages(...initialExpectedDiagnostics);
// Verify they exist - without tsbuildinfo for third project
for (const output of expectedOutputFiles.slice(0, expectedOutputFiles.length - 2)) {
@@ -429,12 +429,12 @@ namespace ts {
const fs = outFileFs.shadow();
const host = new fakes.SolutionBuilderHost(fs);
let builder = createSolutionBuilder(host);
builder.buildAllProjects();
builder.build();
host.assertDiagnosticMessages(...initialExpectedDiagnostics);
host.clearDiagnostics();
builder = createSolutionBuilder(host);
changeCompilerVersion(host);
builder.buildAllProjects();
builder.build();
host.assertDiagnosticMessages(
getExpectedDiagnosticForProjectsInBuild(relSources[project.first][source.config], relSources[project.second][source.config], relSources[project.third][source.config]),
[Diagnostics.Project_0_is_out_of_date_because_output_for_it_was_generated_with_version_1_that_differs_with_current_version_2, relSources[project.first][source.config], fakes.version, version],
@@ -454,7 +454,7 @@ namespace ts {
// Build with command line incremental
const host = new fakes.SolutionBuilderHost(fs);
let builder = createSolutionBuilder(host, { incremental: true });
builder.buildAllProjects();
builder.build();
host.assertDiagnosticMessages(...initialExpectedDiagnostics);
host.clearDiagnostics();
tick();
@@ -462,7 +462,7 @@ namespace ts {
// Make non incremental build with change in file that doesnt affect dts
appendText(fs, relSources[project.first][source.ts][part.one], "console.log(s);");
builder = createSolutionBuilder(host, { verbose: true });
builder.buildAllProjects();
builder.build();
host.assertDiagnosticMessages(getExpectedDiagnosticForProjectsInBuild(relSources[project.first][source.config], relSources[project.second][source.config], relSources[project.third][source.config]),
[Diagnostics.Project_0_is_out_of_date_because_oldest_output_1_is_older_than_newest_input_2, relSources[project.first][source.config], relOutputFiles[project.first][ext.js], relSources[project.first][source.ts][part.one]],
[Diagnostics.Building_project_0, sources[project.first][source.config]],
@@ -476,7 +476,7 @@ namespace ts {
// Make incremental build with change in file that doesnt affect dts
appendText(fs, relSources[project.first][source.ts][part.one], "console.log(s);");
builder = createSolutionBuilder(host, { verbose: true, incremental: true });
builder.buildAllProjects();
builder.build();
// Builds completely because tsbuildinfo is old.
host.assertDiagnosticMessages(
getExpectedDiagnosticForProjectsInBuild(relSources[project.first][source.config], relSources[project.second][source.config], relSources[project.third][source.config]),
@@ -489,6 +489,23 @@ namespace ts {
host.clearDiagnostics();
});
it("builds till project specified", () => {
const fs = outFileFs.shadow();
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host, { verbose: false });
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`);
}
// second is built
for (const output of outputFiles[project.second]) {
assert(fs.existsSync(output), `Expect file ${output} to exist`);
}
assert.equal(result, ExitStatus.Success);
});
describe("Prepend output with .tsbuildinfo", () => {
// Prologues
describe("Prologues", () => {
@@ -904,7 +921,7 @@ ${internal} enum internalEnum { a, b, c }`);
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host);
builder.buildAllProjects();
builder.build();
host.assertDiagnosticMessages(
getExpectedDiagnosticForProjectsInBuild(relSources[project.first][source.config], relSources[project.second][source.config], relSources[project.third][source.config]),
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, relSources[project.first][source.config], "src/first/first_PART1.js"],
@@ -19,7 +19,7 @@ namespace ts {
const fs = projFs.shadow();
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host, ["/src/src/main", "/src/src/other"], {});
builder.buildAllProjects();
builder.build();
host.assertDiagnosticMessages(/*empty*/);
for (const output of allExpectedOutputs) {
assert(fs.existsSync(output), `Expect file ${output} to exist`);
@@ -39,7 +39,7 @@ namespace ts {
replaceText(fs, "/src/tsconfig.base.json", `"rootDir": "./src/",`, "");
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host, ["/src/src/main"], { verbose: true });
builder.buildAllProjects();
builder.build();
host.assertDiagnosticMessages(
getExpectedDiagnosticForProjectsInBuild("src/src/other/tsconfig.json", "src/src/main/tsconfig.json"),
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/src/other/tsconfig.json", "src/dist/other.js"],
@@ -75,7 +75,7 @@ namespace ts {
}));
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host, ["/src/src/main"], { verbose: true });
builder.buildAllProjects();
builder.build();
host.assertDiagnosticMessages(
getExpectedDiagnosticForProjectsInBuild("src/src/other/tsconfig.json", "src/src/main/tsconfig.json"),
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/src/other/tsconfig.json", "src/dist/other.js"],
@@ -112,7 +112,7 @@ namespace ts {
}));
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host, ["/src/src/main/tsconfig.main.json"], { verbose: true });
builder.buildAllProjects();
builder.build();
host.assertDiagnosticMessages(
getExpectedDiagnosticForProjectsInBuild("src/src/other/tsconfig.other.json", "src/src/main/tsconfig.main.json"),
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/src/other/tsconfig.other.json", "src/dist/other.js"],
@@ -19,7 +19,7 @@ namespace ts {
function verifyProjectWithResolveJsonModuleWithFs(fs: vfs.FileSystem, configFile: string, allExpectedOutputs: ReadonlyArray<string>, ...expectedDiagnosticMessages: fakes.ExpectedDiagnostic[]) {
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host, [configFile], { dry: false, force: false, verbose: false });
builder.buildAllProjects();
builder.build();
host.assertDiagnosticMessages(...expectedDiagnosticMessages);
if (!expectedDiagnosticMessages.length) {
// Check for outputs. Not an exhaustive list
@@ -65,7 +65,7 @@ export default hello.hello`);
replaceText(fs, configFile, `"composite": true,`, `"composite": true, "sourceMap": true,`);
const host = new fakes.SolutionBuilderHost(fs);
let builder = createSolutionBuilder(host, [configFile], { verbose: true });
builder.buildAllProjects();
builder.build();
host.assertDiagnosticMessages(
getExpectedDiagnosticForProjectsInBuild(configFile),
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, configFile, "src/dist/src/index.js"],
@@ -77,7 +77,7 @@ export default hello.hello`);
host.clearDiagnostics();
builder = createSolutionBuilder(host, [configFile], { verbose: true });
tick();
builder.buildAllProjects();
builder.build();
host.assertDiagnosticMessages(
getExpectedDiagnosticForProjectsInBuild(configFile),
[Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2, configFile, "src/src/index.ts", "src/dist/src/index.js"]
@@ -90,7 +90,7 @@ export default hello.hello`);
replaceText(fs, configFile, `"outDir": "dist",`, "");
const host = new fakes.SolutionBuilderHost(fs);
let builder = createSolutionBuilder(host, [configFile], { verbose: true });
builder.buildAllProjects();
builder.build();
host.assertDiagnosticMessages(
getExpectedDiagnosticForProjectsInBuild(configFile),
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, configFile, "src/src/index.js"],
@@ -102,7 +102,7 @@ export default hello.hello`);
host.clearDiagnostics();
builder = createSolutionBuilder(host, [configFile], { verbose: true });
tick();
builder.buildAllProjects();
builder.build();
host.assertDiagnosticMessages(
getExpectedDiagnosticForProjectsInBuild(configFile),
[Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2, configFile, "src/src/index.ts", "src/src/index.js"]
@@ -129,7 +129,7 @@ export default hello.hello`);
const mainConfigFile = "src/main/tsconfig.json";
const host = new fakes.SolutionBuilderHost(fs);
let builder = createSolutionBuilder(host, [configFile], { verbose: true });
builder.buildAllProjects();
builder.build();
host.assertDiagnosticMessages(
getExpectedDiagnosticForProjectsInBuild(stringsConfigFile, mainConfigFile, configFile),
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, stringsConfigFile, "src/strings/tsconfig.tsbuildinfo"],
@@ -141,7 +141,7 @@ export default hello.hello`);
host.clearDiagnostics();
builder = createSolutionBuilder(host, [configFile], { verbose: true });
tick();
builder.buildAllProjects();
builder.build();
host.assertDiagnosticMessages(
getExpectedDiagnosticForProjectsInBuild(stringsConfigFile, mainConfigFile, configFile),
[Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2, stringsConfigFile, "src/strings/foo.json", "src/strings/tsconfig.tsbuildinfo"],
+53 -27
View File
@@ -21,7 +21,7 @@ namespace ts {
const builder = createSolutionBuilder(host, ["/src/tests"], { dry: false, force: false, verbose: false });
host.clearDiagnostics();
builder.buildAllProjects();
builder.build();
host.assertDiagnosticMessages(/*empty*/);
// Check for outputs. Not an exhaustive list
@@ -39,7 +39,7 @@ namespace ts {
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host, ["/src/tests"], {});
builder.buildAllProjects();
builder.build();
host.assertDiagnosticMessages(/*empty*/);
const expectedOutputs = allExpectedOutputs.map(f => f.replace("/logic/", "/logic/outDir/"));
// Check for outputs. Not an exhaustive list
@@ -57,7 +57,7 @@ namespace ts {
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host, ["/src/tests"], {});
builder.buildAllProjects();
builder.build();
host.assertDiagnosticMessages(/*empty*/);
const expectedOutputs = allExpectedOutputs.map(f => f.replace("/logic/index.d.ts", "/logic/out/decls/index.d.ts"));
// Check for outputs. Not an exhaustive list
@@ -71,7 +71,7 @@ namespace ts {
replaceText(fs, "/src/core/tsconfig.json", `"composite": true,`, "");
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host, ["/src/core"], { verbose: true });
builder.buildAllProjects();
builder.build();
host.assertDiagnosticMessages(
getExpectedDiagnosticForProjectsInBuild("src/core/tsconfig.json"),
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/core/tsconfig.json", "src/core/anotherModule.js"],
@@ -88,7 +88,7 @@ namespace ts {
const fs = projFs.shadow();
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host, ["/src/tests"], { dry: true, force: false, verbose: false });
builder.buildAllProjects();
builder.build();
host.assertDiagnosticMessages(
[Diagnostics.A_non_dry_build_would_build_project_0, "/src/core/tsconfig.json"],
[Diagnostics.A_non_dry_build_would_build_project_0, "/src/logic/tsconfig.json"],
@@ -106,12 +106,12 @@ namespace ts {
const host = new fakes.SolutionBuilderHost(fs);
let builder = createSolutionBuilder(host, ["/src/tests"], { dry: false, force: false, verbose: false });
builder.buildAllProjects();
builder.build();
tick();
host.clearDiagnostics();
builder = createSolutionBuilder(host, ["/src/tests"], { dry: true, force: false, verbose: false });
builder.buildAllProjects();
builder.build();
host.assertDiagnosticMessages(
[Diagnostics.Project_0_is_up_to_date, "/src/core/tsconfig.json"],
[Diagnostics.Project_0_is_up_to_date, "/src/logic/tsconfig.json"],
@@ -126,7 +126,7 @@ namespace ts {
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host, ["/src/tests"], { dry: false, force: false, verbose: false });
builder.buildAllProjects();
builder.build();
// Verify they exist
for (const output of allExpectedOutputs) {
assert(fs.existsSync(output), `Expect file ${output} to exist`);
@@ -146,15 +146,16 @@ namespace ts {
const fs = projFs.shadow();
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host, ["/src/tests"], { dry: false, force: true, verbose: false });
builder.buildAllProjects();
let builder = createSolutionBuilder(host, ["/src/tests"], { dry: false, force: true, verbose: false });
builder.build();
let currentTime = time();
checkOutputTimestamps(currentTime);
tick();
Debug.assert(time() !== currentTime, "Time moves on");
currentTime = time();
builder.buildAllProjects();
builder = createSolutionBuilder(host, ["/src/tests"], { dry: false, force: true, verbose: false });
builder.build();
checkOutputTimestamps(currentTime);
function checkOutputTimestamps(expected: number) {
@@ -172,7 +173,7 @@ namespace ts {
const fs = projFs.shadow();
const host = new fakes.SolutionBuilderHost(fs);
let builder = createSolutionBuilder(host, ["/src/tests"], { verbose: true });
builder.buildAllProjects();
builder.build();
host.clearDiagnostics();
tick();
builder = createSolutionBuilder(host, ["/src/tests"], { ...(opts || {}), verbose: true });
@@ -183,7 +184,7 @@ namespace ts {
const fs = projFs.shadow();
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host, ["/src/tests"], { verbose: true });
builder.buildAllProjects();
builder.build();
host.assertDiagnosticMessages(
getExpectedDiagnosticForProjectsInBuild("src/core/tsconfig.json", "src/logic/tsconfig.json", "src/tests/tsconfig.json"),
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/core/tsconfig.json", "src/core/anotherModule.js"],
@@ -198,7 +199,7 @@ namespace ts {
// All three projects are up to date
it("Detects that all projects are up to date", () => {
const { host, builder } = initializeWithBuild();
builder.buildAllProjects();
builder.build();
host.assertDiagnosticMessages(
getExpectedDiagnosticForProjectsInBuild("src/core/tsconfig.json", "src/logic/tsconfig.json", "src/tests/tsconfig.json"),
[Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2, "src/core/tsconfig.json", "src/core/anotherModule.ts", "src/core/anotherModule.js"],
@@ -211,7 +212,7 @@ namespace ts {
it("Only builds the leaf node project", () => {
const { fs, host, builder } = initializeWithBuild();
fs.writeFileSync("/src/tests/index.ts", "const m = 10;");
builder.buildAllProjects();
builder.build();
host.assertDiagnosticMessages(
getExpectedDiagnosticForProjectsInBuild("src/core/tsconfig.json", "src/logic/tsconfig.json", "src/tests/tsconfig.json"),
[Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2, "src/core/tsconfig.json", "src/core/anotherModule.ts", "src/core/anotherModule.js"],
@@ -225,7 +226,7 @@ namespace ts {
it("Detects type-only changes in upstream projects", () => {
const { fs, host, builder } = initializeWithBuild();
replaceText(fs, "/src/core/index.ts", "HELLO WORLD", "WELCOME PLANET");
builder.buildAllProjects();
builder.build();
host.assertDiagnosticMessages(
getExpectedDiagnosticForProjectsInBuild("src/core/tsconfig.json", "src/logic/tsconfig.json", "src/tests/tsconfig.json"),
@@ -242,7 +243,7 @@ namespace ts {
it("rebuilds completely when version in tsbuildinfo doesnt match ts version", () => {
const { host, builder } = initializeWithBuild();
changeCompilerVersion(host);
builder.buildAllProjects();
builder.build();
host.assertDiagnosticMessages(
getExpectedDiagnosticForProjectsInBuild("src/core/tsconfig.json", "src/logic/tsconfig.json", "src/tests/tsconfig.json"),
[Diagnostics.Project_0_is_out_of_date_because_output_for_it_was_generated_with_version_1_that_differs_with_current_version_2, "src/core/tsconfig.json", fakes.version, version],
@@ -256,7 +257,7 @@ namespace ts {
it("rebuilds from start if --f is passed", () => {
const { host, builder } = initializeWithBuild({ force: true });
builder.buildAllProjects();
builder.build();
host.assertDiagnosticMessages(
getExpectedDiagnosticForProjectsInBuild("src/core/tsconfig.json", "src/logic/tsconfig.json", "src/tests/tsconfig.json"),
[Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2, "src/core/tsconfig.json", "src/core/anotherModule.ts", "src/core/anotherModule.js"],
@@ -271,7 +272,7 @@ namespace ts {
it("rebuilds when tsconfig changes", () => {
const { fs, host, builder } = initializeWithBuild();
replaceText(fs, "/src/tests/tsconfig.json", `"composite": true`, `"composite": true, "target": "es3"`);
builder.buildAllProjects();
builder.build();
host.assertDiagnosticMessages(
getExpectedDiagnosticForProjectsInBuild("src/core/tsconfig.json", "src/logic/tsconfig.json", "src/tests/tsconfig.json"),
[Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2, "src/core/tsconfig.json", "src/core/anotherModule.ts", "src/core/anotherModule.js"],
@@ -287,7 +288,7 @@ namespace ts {
replaceText(fs, "/src/tests/tsconfig.json", `"references": [`, `"extends": "./tsconfig.base.json", "references": [`);
const host = new fakes.SolutionBuilderHost(fs);
let builder = createSolutionBuilder(host, ["/src/tests"], { verbose: true });
builder.buildAllProjects();
builder.build();
host.assertDiagnosticMessages(
getExpectedDiagnosticForProjectsInBuild("src/core/tsconfig.json", "src/logic/tsconfig.json", "src/tests/tsconfig.json"),
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/core/tsconfig.json", "src/core/anotherModule.js"],
@@ -301,7 +302,7 @@ namespace ts {
tick();
builder = createSolutionBuilder(host, ["/src/tests"], { verbose: true });
fs.writeFileSync("/src/tests/tsconfig.base.json", JSON.stringify({ compilerOptions: {} }));
builder.buildAllProjects();
builder.build();
host.assertDiagnosticMessages(
getExpectedDiagnosticForProjectsInBuild("src/core/tsconfig.json", "src/logic/tsconfig.json", "src/tests/tsconfig.json"),
[Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2, "src/core/tsconfig.json", "src/core/anotherModule.ts", "src/core/anotherModule.js"],
@@ -310,6 +311,31 @@ namespace ts {
[Diagnostics.Building_project_0, "/src/tests/tsconfig.json"]
);
});
it("builds till project specified", () => {
const fs = projFs.shadow();
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host, ["/src/tests"], {});
const result = builder.build("/src/logic");
host.assertDiagnosticMessages(/*empty*/);
assert.isFalse(fs.existsSync(allExpectedOutputs[0]), `Expect file ${allExpectedOutputs[0]} to not exist`);
for (const output of allExpectedOutputs.slice(1)) {
assert(fs.existsSync(output), `Expect file ${output} to exist`);
}
assert.equal(result, ExitStatus.Success);
});
it("building project in not build order doesnt throw error", () => {
const fs = projFs.shadow();
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host, ["/src/tests"], {});
const result = builder.build("/src/logic2");
host.assertDiagnosticMessages(/*empty*/);
for (const output of allExpectedOutputs) {
assert.isFalse(fs.existsSync(output), `Expect file ${output} to not exist`);
}
assert.equal(result, ExitStatus.InvalidProject_OutputsSkipped);
});
});
describe("downstream-blocked compilations", () => {
@@ -320,7 +346,7 @@ namespace ts {
// Induce an error in the middle project
replaceText(fs, "/src/logic/index.ts", "c.multiply(10, 15)", `c.muitply()`);
builder.buildAllProjects();
builder.build();
host.assertDiagnosticMessages(
getExpectedDiagnosticForProjectsInBuild("src/core/tsconfig.json", "src/logic/tsconfig.json", "src/tests/tsconfig.json"),
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/core/tsconfig.json", "src/core/anotherModule.js"],
@@ -340,7 +366,7 @@ namespace ts {
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host, ["/src/tests"], { dry: false, force: false, verbose: false });
builder.buildAllProjects();
builder.build();
host.assertDiagnosticMessages(/*empty*/);
// Update a timestamp in the middle project
@@ -366,7 +392,7 @@ export class cNew {}`);
// Rebuild this project
tick();
builder.invalidateProject("/src/logic");
builder.buildInvalidatedProject();
builder.buildNextInvalidatedProject();
// The file should be updated
assert.isTrue(writtenFiles.has("/src/logic/index.js"), "JS file should have been rebuilt");
assert.equal(fs.statSync("/src/logic/index.js").mtimeMs, time(), "JS file should have been rebuilt");
@@ -376,7 +402,7 @@ export class cNew {}`);
// Build downstream projects should update 'tests', but not 'core'
tick();
builder.buildInvalidatedProject();
builder.buildNextInvalidatedProject();
if (expectedToWriteTests) {
assert.isTrue(writtenFiles.has("/src/tests/index.js"), "Downstream JS file should have been rebuilt");
}
@@ -394,7 +420,7 @@ export class cNew {}`);
const fs = projFs.shadow();
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host, ["/src/tests"], { listFiles: true });
builder.buildAllProjects();
builder.build();
assert.deepEqual(host.traces, [
"/lib/lib.d.ts",
"/src/core/anotherModule.ts",
@@ -421,7 +447,7 @@ export class cNew {}`);
const fs = projFs.shadow();
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host, ["/src/tests"], { listEmittedFiles: true });
builder.buildAllProjects();
builder.build();
assert.deepEqual(host.traces, [
"TSFILE: /src/core/anotherModule.js",
"TSFILE: /src/core/anotherModule.d.ts.map",
@@ -30,7 +30,7 @@ namespace ts {
const host = new fakes.SolutionBuilderHost(fs);
modifyDiskLayout(fs);
const builder = createSolutionBuilder(host, ["/src/tsconfig.c.json"], { listFiles: true });
builder.buildAllProjects();
builder.build();
host.assertDiagnosticMessages(...expectedDiagnostics);
for (const output of allExpectedOutputs) {
assert(fs.existsSync(output), `Expect file ${output} to exist`);
+6 -6
View File
@@ -24,7 +24,7 @@ namespace ts.tscWatch {
function createSolutionBuilderWithWatch(system: TsBuildWatchSystem, rootNames: ReadonlyArray<string>, defaultOptions?: BuildOptions) {
const host = createSolutionBuilderWithWatchHost(system);
const solutionBuilder = ts.createSolutionBuilderWithWatch(host, rootNames, defaultOptions || { watch: true });
solutionBuilder.buildAllProjects();
solutionBuilder.build();
solutionBuilder.startWatching();
return solutionBuilder;
}
@@ -608,7 +608,7 @@ let x: string = 10;`);
// Build the composite project
const host = createTsBuildWatchSystem(allFiles, { currentDirectory });
const solutionBuilder = createSolutionBuilder(host, [solutionBuilderconfig], {});
solutionBuilder.buildAllProjects();
solutionBuilder.build();
const outputFileStamps = getOutputFileStamps(host);
for (const stamp of outputFileStamps) {
assert.isDefined(stamp[1], `${stamp[0]} expected to be present`);
@@ -723,7 +723,7 @@ let x: string = 10;`);
function foo() {
}`);
solutionBuilder.invalidateProject(`${project}/${SubProject.logic}`);
solutionBuilder.buildInvalidatedProject();
solutionBuilder.buildNextInvalidatedProject();
// not ideal, but currently because of d.ts but no new file is written
// There will be timeout queued even though file contents are same
@@ -736,7 +736,7 @@ function foo() {
export function gfoo() {
}`);
solutionBuilder.invalidateProject(logic[0].path);
solutionBuilder.buildInvalidatedProject();
solutionBuilder.buildNextInvalidatedProject();
}, expectedProgramFiles);
});
@@ -747,7 +747,7 @@ export function gfoo() {
references: [{ path: "../core" }]
}));
solutionBuilder.invalidateProject(logic[0].path, ConfigFileProgramReloadLevel.Full);
solutionBuilder.buildInvalidatedProject();
solutionBuilder.buildNextInvalidatedProject();
}, [tests[1].path, libFile.path, coreIndexDts, coreAnotherModuleDts, projectFilePath(SubProject.logic, "decls/index.d.ts")]);
});
});
@@ -967,7 +967,7 @@ export function gfoo() {
export function gfoo() {
}`);
solutionBuilder.invalidateProject(bTsconfig.path);
solutionBuilder.buildInvalidatedProject();
solutionBuilder.buildNextInvalidatedProject();
},
emptyArray,
expectedProgramFiles,
@@ -5,7 +5,7 @@ namespace ts.projectSystem {
// ts build should succeed
const solutionBuilder = tscWatch.createSolutionBuilder(host, rootNames, {});
solutionBuilder.buildAllProjects();
solutionBuilder.build();
assert.equal(host.getOutput().length, 0);
return host;
+2 -2
View File
@@ -216,7 +216,7 @@ namespace ts {
updateCreateProgram(buildHost);
buildHost.afterProgramEmitAndDiagnostics = program => reportStatistics(program.getProgram());
const builder = createSolutionBuilderWithWatch(buildHost, projects, buildOptions);
builder.buildAllProjects();
builder.build();
return builder.startWatching();
}
else {
@@ -224,7 +224,7 @@ namespace ts {
updateCreateProgram(buildHost);
buildHost.afterProgramEmitAndDiagnostics = program => reportStatistics(program.getProgram());
const builder = createSolutionBuilder(buildHost, projects, buildOptions);
return sys.exit(buildOptions.clean ? builder.cleanAllProjects() : builder.buildAllProjects());
return sys.exit(buildOptions.clean ? builder.cleanAllProjects() : builder.build());
}
}