feat(gradle): link and unlink independent Gradle builds (#1413)

Opening a folder of independent Gradle builds selected only the first build. Link Gradle Projects… (pick a build's settings or build file) and Unlink on a build's root let an ordinary project window manage several independent builds without changing their scripts. The build model holds a list of Gradle builds; each keeps its own wrapper, working directory, environment, task routing and sync cache, keyed by the owning build directory. The links persist in the project's Gradle settings (linkedProjects); unlinking leaves files and saved run configurations alone. Existing includeBuild support is unchanged, and an explicitly linked build takes precedence over a composite that also includes it. Workspace windows keep Link/Unlink disabled.

Co-authored-by: gudcks0305 <[email protected]>
Цей коміт міститься в:
yoo hyeong chan authored and GitHub committed 2026-11-05 11:23:39 +08:00
1 джерело 682fee633d
коміт 0dfdbbc323
43 files changed
+1846 -254

No files matched your search

+7 -3
Переглянути файл
@@ -116,9 +116,13 @@ export function activate(): () => void {
const paths = own ? validateProjectStructure(settings?.projectStructure).contentRoots : [];
// The Build tool window's model (one load per build change), not a load of our own.
const build = useBuildModel.getState();
const gradleProjects = build.root === project?.root ? (build.model?.gradle?.projects ?? []) : [];
const rootName = gradleProjects.find((p) => p.path === ":")?.name ?? "";
const groups = own && project ? gradleModuleGroups(gradleProjects, rootName, project.root) : [];
const gradleBuilds = build.root === project?.root ? (build.model?.gradle ?? []) : [];
const groups =
own && project
? gradleBuilds.flatMap((g) =>
gradleModuleGroups(g.projects, g.projects.find((p) => p.path === ":")?.name ?? "", project.root, g.dir),
)
: [];
const key = JSON.stringify([paths, groups]);
if (key === shownRoots) return;
shownRoots = key;
+12
Переглянути файл
@@ -123,3 +123,15 @@ describe("module group nodes in the tree", () => {
expect(m.children.get(m.roots[0]!.key)!.map((n) => n.label)).toEqual(["billing", "core"]);
});
});
it("namespaces module group identities by independently linked build", () => {
const projects = [
{ path: ":", dir: "/w/first" },
{ path: ":app", dir: "/w/shared" },
];
const first = gradleModuleGroups(projects, "same", "/w/project", "first");
const second = gradleModuleGroups(projects, "same", "/w/project", "second");
expect(first.length).toBeGreaterThan(0);
expect(first[0]!.id).not.toBe(second[0]!.id);
expect(first.map(shape)).toEqual(second.map(shape));
});
+10 -2
Переглянути файл
@@ -29,7 +29,9 @@ export function gradleModuleGroups(
projects: readonly { path: string; dir: string; build?: string | null }[],
rootName: string,
projectRoot: string,
buildDir?: string,
): ExtraRoot[] {
const identity = (name: string) => (buildDir === undefined ? name : JSON.stringify([buildDir, name]));
const modules: Module[] = projects.map((p) => {
const path = p.path.split(":").filter(Boolean);
// An included build's modules are named after that build (`lib.core`); buildSrc and
@@ -51,7 +53,13 @@ export function gradleModuleGroups(
const moduleNode = (m: Module): ExtraRoot => {
const label = nameOf(m.dir) || m.dir;
return { id: m.name, label, detail: m.name === label ? undefined : m.name, icon: "gradle-module", path: m.dir };
return {
id: identity(m.name),
label,
detail: m.name === label ? undefined : m.name,
icon: "gradle-module",
path: m.dir,
};
};
// `modulesAndGroups`: ungrouped top-level modules as they are (the project folder is
// shown anyway), one node for the common group path of the grouped ones.
@@ -82,7 +90,7 @@ export function gradleModuleGroups(
const seen = new Set<string>();
const moduleNodes = members.filter((m) => !seen.has(m.dir) && !!seen.add(m.dir)).map(moduleNode);
return {
id: `gradleGroup:${group.join(".")}`,
id: identity(`gradleGroup:${group.join(".")}`),
label: group[group.length - 1]!,
icon: "gradle-project",
children: [...[...childGroups.values()].map(node).sort(byLabel), ...moduleNodes.sort(byLabel)],