mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Add option --stopBuildOnErrors to tsbuild to get previous behavior of not building downstream projects if upstream has errors (#59433)
This commit is contained in:
@@ -1696,6 +1696,13 @@ export const optionsForBuild: CommandLineOption[] = [
|
||||
type: "boolean",
|
||||
defaultValueDescription: false,
|
||||
},
|
||||
{
|
||||
name: "stopBuildOnErrors",
|
||||
category: Diagnostics.Command_line_Options,
|
||||
description: Diagnostics.Skip_building_downstream_projects_on_error_in_upstream_project,
|
||||
type: "boolean",
|
||||
defaultValueDescription: false,
|
||||
},
|
||||
];
|
||||
|
||||
/** @internal */
|
||||
|
||||
@@ -5716,6 +5716,14 @@
|
||||
"category": "Message",
|
||||
"code": 6361
|
||||
},
|
||||
"Skipping build of project '{0}' because its dependency '{1}' has errors": {
|
||||
"category": "Message",
|
||||
"code": 6362
|
||||
},
|
||||
"Project '{0}' can't be built because its dependency '{1}' has errors": {
|
||||
"category": "Message",
|
||||
"code": 6363
|
||||
},
|
||||
"Build one or more projects and their dependencies, if out of date": {
|
||||
"category": "Message",
|
||||
"code": 6364
|
||||
@@ -5760,6 +5768,14 @@
|
||||
"category": "Message",
|
||||
"code": 6381
|
||||
},
|
||||
"Skipping build of project '{0}' because its dependency '{1}' was not built": {
|
||||
"category": "Message",
|
||||
"code": 6382
|
||||
},
|
||||
"Project '{0}' can't be built because its dependency '{1}' was not built": {
|
||||
"category": "Message",
|
||||
"code": 6383
|
||||
},
|
||||
"Have recompiles in '--incremental' and '--watch' assume that changes within a file will only affect files directly depending on it.": {
|
||||
"category": "Message",
|
||||
"code": 6384
|
||||
@@ -6095,6 +6111,10 @@
|
||||
"category": "Message",
|
||||
"code": 6639
|
||||
},
|
||||
"Skip building downstream projects on error in upstream project.": {
|
||||
"category": "Message",
|
||||
"code": 6640
|
||||
},
|
||||
"Specify a list of glob patterns that match files to be included in compilation.": {
|
||||
"category": "Message",
|
||||
"code": 6641
|
||||
|
||||
@@ -25,6 +25,7 @@ export enum UpToDateStatusType {
|
||||
OutOfDateOptions,
|
||||
OutOfDateRoots,
|
||||
UpstreamOutOfDate,
|
||||
UpstreamBlocked,
|
||||
ComputingUpstream,
|
||||
TsVersionOutputOfDate,
|
||||
UpToDateWithInputFileText,
|
||||
@@ -47,6 +48,7 @@ export type UpToDateStatus =
|
||||
| Status.OutOfDateBuildInfo
|
||||
| Status.OutOfDateRoots
|
||||
| Status.UpstreamOutOfDate
|
||||
| Status.UpstreamBlocked
|
||||
| Status.ComputingUpstream
|
||||
| Status.TsVersionOutOfDate
|
||||
| Status.ContainerOnly
|
||||
@@ -135,6 +137,15 @@ export namespace Status {
|
||||
upstreamProjectName: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* This project depends an upstream project with build errors
|
||||
*/
|
||||
export interface UpstreamBlocked {
|
||||
type: UpToDateStatusType.UpstreamBlocked;
|
||||
upstreamProjectName: string;
|
||||
upstreamProjectBlocked: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computing status of upstream projects referenced
|
||||
*/
|
||||
|
||||
@@ -135,6 +135,7 @@ export interface BuildOptions {
|
||||
dry?: boolean;
|
||||
force?: boolean;
|
||||
verbose?: boolean;
|
||||
stopBuildOnErrors?: boolean;
|
||||
|
||||
/** @internal */ clean?: boolean;
|
||||
/** @internal */ watch?: boolean;
|
||||
@@ -1254,6 +1255,23 @@ function getNextInvalidatedProjectCreateInfo<T extends BuilderProgram>(
|
||||
}
|
||||
}
|
||||
|
||||
if (status.type === UpToDateStatusType.UpstreamBlocked) {
|
||||
verboseReportProjectStatus(state, project, status);
|
||||
reportAndStoreErrors(state, projectPath, getConfigFileParsingDiagnostics(config));
|
||||
projectPendingBuild.delete(projectPath);
|
||||
if (options.verbose) {
|
||||
reportStatus(
|
||||
state,
|
||||
status.upstreamProjectBlocked ?
|
||||
Diagnostics.Skipping_build_of_project_0_because_its_dependency_1_was_not_built :
|
||||
Diagnostics.Skipping_build_of_project_0_because_its_dependency_1_has_errors,
|
||||
project,
|
||||
status.upstreamProjectName,
|
||||
);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (status.type === UpToDateStatusType.ContainerOnly) {
|
||||
verboseReportProjectStatus(state, project, status);
|
||||
reportAndStoreErrors(state, projectPath, getConfigFileParsingDiagnostics(config));
|
||||
@@ -1455,6 +1473,20 @@ function getUpToDateStatusWorker<T extends BuilderProgram>(state: SolutionBuilde
|
||||
continue;
|
||||
}
|
||||
|
||||
// An upstream project is blocked
|
||||
if (
|
||||
state.options.stopBuildOnErrors && (
|
||||
refStatus.type === UpToDateStatusType.Unbuildable ||
|
||||
refStatus.type === UpToDateStatusType.UpstreamBlocked
|
||||
)
|
||||
) {
|
||||
return {
|
||||
type: UpToDateStatusType.UpstreamBlocked,
|
||||
upstreamProjectName: ref.path,
|
||||
upstreamProjectBlocked: refStatus.type === UpToDateStatusType.UpstreamBlocked,
|
||||
};
|
||||
}
|
||||
|
||||
if (!force) (referenceStatuses ||= []).push({ ref, refStatus, resolvedRefPath, resolvedConfig });
|
||||
}
|
||||
}
|
||||
@@ -1848,6 +1880,8 @@ function queueReferencingProjects<T extends BuilderProgram>(
|
||||
buildOrder: readonly ResolvedConfigFileName[],
|
||||
buildResult: BuildResultFlags,
|
||||
) {
|
||||
// Queue only if there are no errors
|
||||
if (state.options.stopBuildOnErrors && (buildResult & BuildResultFlags.AnyErrors)) return;
|
||||
// Only composite projects can be referenced by other projects
|
||||
if (!config.options.composite) return;
|
||||
// Always use build order to queue projects
|
||||
@@ -1883,6 +1917,12 @@ function queueReferencingProjects<T extends BuilderProgram>(
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
case UpToDateStatusType.UpstreamBlocked:
|
||||
if (toResolvedConfigFilePath(state, resolveProjectName(state, status.upstreamProjectName)) === projectPath) {
|
||||
clearProjectStatus(state, nextProjectPath);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
addProjToQueue(state, nextProjectPath, ProgramUpdateLevel.Update);
|
||||
@@ -2386,6 +2426,15 @@ function reportUpToDateStatus<T extends BuilderProgram>(state: SolutionBuilderSt
|
||||
relName(state, configFileName),
|
||||
relName(state, status.upstreamProjectName),
|
||||
);
|
||||
case UpToDateStatusType.UpstreamBlocked:
|
||||
return reportStatus(
|
||||
state,
|
||||
status.upstreamProjectBlocked ?
|
||||
Diagnostics.Project_0_can_t_be_built_because_its_dependency_1_was_not_built :
|
||||
Diagnostics.Project_0_can_t_be_built_because_its_dependency_1_has_errors,
|
||||
relName(state, configFileName),
|
||||
relName(state, status.upstreamProjectName),
|
||||
);
|
||||
case UpToDateStatusType.Unbuildable:
|
||||
return reportStatus(
|
||||
state,
|
||||
|
||||
@@ -26,7 +26,7 @@ export function getFsContentsForSampleProjectReferencesLogicConfig(withNodeNext?
|
||||
],
|
||||
});
|
||||
}
|
||||
export function getFsContentsForSampleProjectReferences(withNodeNext?: boolean): FsContents {
|
||||
export function getFsContentsForSampleProjectReferences(withNodeNext?: boolean, skipReferenceCoreFromTest?: boolean): FsContents {
|
||||
return {
|
||||
[libFile.path]: libFile.content,
|
||||
"/user/username/projects/sample1/core/tsconfig.json": jsonToReadableText({
|
||||
@@ -55,10 +55,14 @@ export function getFsContentsForSampleProjectReferences(withNodeNext?: boolean):
|
||||
export const m = mod;
|
||||
`,
|
||||
"/user/username/projects/sample1/tests/tsconfig.json": jsonToReadableText({
|
||||
references: [
|
||||
{ path: "../core" },
|
||||
{ path: "../logic" },
|
||||
],
|
||||
references: !skipReferenceCoreFromTest ?
|
||||
[
|
||||
{ path: "../core" },
|
||||
{ path: "../logic" },
|
||||
] :
|
||||
[
|
||||
{ path: "../logic" },
|
||||
],
|
||||
files: ["index.ts"],
|
||||
compilerOptions: {
|
||||
...getProjectConfigWithNodeNext(withNodeNext),
|
||||
@@ -81,9 +85,9 @@ export function getFsContentsForSampleProjectReferences(withNodeNext?: boolean):
|
||||
};
|
||||
}
|
||||
|
||||
export function getFsForSampleProjectReferences() {
|
||||
export function getFsForSampleProjectReferences(withNodeNext?: boolean, skipReferenceCoreFromTest?: boolean) {
|
||||
return loadProjectFromFiles(
|
||||
getFsContentsForSampleProjectReferences(),
|
||||
getFsContentsForSampleProjectReferences(withNodeNext, skipReferenceCoreFromTest),
|
||||
{
|
||||
cwd: "/user/username/projects/sample1",
|
||||
executingFilePath: libFile.path,
|
||||
@@ -91,9 +95,9 @@ export function getFsForSampleProjectReferences() {
|
||||
);
|
||||
}
|
||||
|
||||
export function getSysForSampleProjectReferences(withNodeNext?: boolean) {
|
||||
export function getSysForSampleProjectReferences(withNodeNext?: boolean, skipReferenceCoreFromTest?: boolean) {
|
||||
return createWatchedSystem(
|
||||
getFsContentsForSampleProjectReferences(withNodeNext),
|
||||
getFsContentsForSampleProjectReferences(withNodeNext, skipReferenceCoreFromTest),
|
||||
{
|
||||
currentDirectory: "/user/username/projects/sample1",
|
||||
},
|
||||
|
||||
@@ -359,6 +359,23 @@ describe("unittests:: tsbuild:: on 'sample1' project", () => {
|
||||
modifyFs: fs => replaceText(fs, "logic/index.ts", "c.multiply(10, 15)", `c.muitply()`),
|
||||
edits: noChangeOnlyRuns,
|
||||
});
|
||||
|
||||
[false, true].forEach(skipReferenceCoreFromTest =>
|
||||
verifyTsc({
|
||||
scenario: "sample1",
|
||||
subScenario: `skips builds downstream projects if upstream projects have errors with stopBuildOnErrors${skipReferenceCoreFromTest ? " when test does not reference core" : ""}`,
|
||||
fs: () => getFsForSampleProjectReferences(/*withNodeNext*/ undefined, skipReferenceCoreFromTest),
|
||||
commandLineArgs: ["--b", "tests", "--verbose", "--stopBuildOnErrors"],
|
||||
modifyFs: fs => appendText(fs, "core/index.ts", `multiply();`),
|
||||
edits: [
|
||||
noChangeRun,
|
||||
{
|
||||
caption: "fix error",
|
||||
edit: fs => replaceText(fs, "core/index.ts", "multiply();", ""),
|
||||
},
|
||||
],
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
describe("project invalidation", () => {
|
||||
|
||||
@@ -316,6 +316,28 @@ createSomeObject().message;`,
|
||||
}
|
||||
verifyIncrementalErrors("when preserveWatchOutput is not used", ts.emptyArray);
|
||||
verifyIncrementalErrors("when preserveWatchOutput is passed on command line", ["--preserveWatchOutput"]);
|
||||
verifyIncrementalErrors("when stopBuildOnErrors is passed on command line", ["--stopBuildOnErrors"]);
|
||||
|
||||
[false, true].forEach(skipReferenceCoreFromTest =>
|
||||
verifyTscWatch({
|
||||
scenario: "programUpdates",
|
||||
subScenario: `skips builds downstream projects if upstream projects have errors with stopBuildOnErrors${skipReferenceCoreFromTest ? " when test does not reference core" : ""}`,
|
||||
sys: () => {
|
||||
const sys = getSysForSampleProjectReferences(/*withNodeNext*/ undefined, skipReferenceCoreFromTest);
|
||||
sys.appendFile("core/index.ts", `multiply();`);
|
||||
return sys;
|
||||
},
|
||||
commandLineArgs: ["--b", "-w", "tests", "--verbose", "--stopBuildOnErrors"],
|
||||
edits: [{
|
||||
caption: "fix error",
|
||||
edit: sys => sys.replaceFileText("core/index.ts", "multiply();", ""),
|
||||
timeouts: sys => {
|
||||
sys.runQueuedTimeoutCallbacks();
|
||||
sys.runQueuedTimeoutCallbacks();
|
||||
},
|
||||
}],
|
||||
})
|
||||
);
|
||||
|
||||
describe("when declaration emit errors are present", () => {
|
||||
const solution = "solution";
|
||||
|
||||
Reference in New Issue
Block a user