дзеркало
https://github.com/l0ng-ai/openidea.git
синхронізовано 2026-11-09 19:25:54 +02:00
fix(gradle): unlink a build by the path the UI sends
Linked builds are saved normalised (canonical, forward slashes), but unlinking compared the paths it was given verbatim, so a build outside the project could not be unlinked on Windows (C:\… vs C:/…), nor through a symlinked path. Unlink now normalises each path the way linking saves it. The Gradle linkage tests also create the platform's wrapper (gradlew.bat on Windows) and compare canonical paths without the \\?\ prefix.
Цей коміт міститься в:
1 джерело
ac436160b5
коміт
43c85ce8f3
4 files changed
+43
-7
No files matched your search
@@ -821,13 +821,14 @@ fn launch_class_dirs(system: BuildSystem, module: &Path, test: bool) -> Vec<Path
|
||||
mod tests {
|
||||
#[test]
|
||||
fn independent_gradle_builds_keep_their_launchers_and_task_paths() {
|
||||
let wrapper = if cfg!(windows) { "gradlew.bat" } else { "gradlew" };
|
||||
let temp = tempfile::tempdir().unwrap();
|
||||
let root = temp.path();
|
||||
for dir in ["a", "b"] {
|
||||
std::fs::create_dir_all(root.join(dir).join("app/src/main/java")).unwrap();
|
||||
std::fs::write(root.join(dir).join("settings.gradle"), "include 'app'").unwrap();
|
||||
std::fs::write(root.join(dir).join("app/build.gradle"), "plugins { id 'java' }").unwrap();
|
||||
std::fs::write(root.join(dir).join("gradlew"), "").unwrap();
|
||||
std::fs::write(root.join(dir).join(wrapper), "").unwrap();
|
||||
}
|
||||
let settings = ide_core::settings::GradleSettings {
|
||||
linked_projects: Some(vec!["a".into(), "b".into()]),
|
||||
@@ -842,7 +843,7 @@ mod tests {
|
||||
assert_eq!(all.steps[1].cwd, root.join("b"));
|
||||
let module = super::plan(root, &model, super::BuildKind::Rebuild, Some(&root.join("b/app")), None).unwrap();
|
||||
assert_eq!(module.steps.len(), 1);
|
||||
assert_eq!(module.steps[0].program, root.join("b/gradlew").to_string_lossy());
|
||||
assert_eq!(module.steps[0].program, root.join("b").join(wrapper).to_string_lossy());
|
||||
assert_eq!(module.steps[0].cwd, root.join("b"));
|
||||
assert_eq!(module.steps[0].env["JAVA_HOME"], "/jdk-b");
|
||||
assert_eq!(module.steps[0].args, ["--console=plain", ":app:clean", ":app:classes", ":app:testClasses"]);
|
||||
@@ -854,7 +855,7 @@ mod tests {
|
||||
nested.path = ":b:app".into();
|
||||
model.gradle[0].projects.push(nested);
|
||||
let nested = super::plan(root, &model, super::BuildKind::Build, Some(&root.join("b/app")), None).unwrap();
|
||||
assert_eq!(nested.steps[0].program, root.join("b/gradlew").to_string_lossy());
|
||||
assert_eq!(nested.steps[0].program, root.join("b").join(wrapper).to_string_lossy());
|
||||
assert_eq!(nested.steps[0].args, ["--console=plain", ":app:classes", ":app:testClasses"]);
|
||||
model.gradle.clear();
|
||||
assert!(super::plan(root, &model, super::BuildKind::Build, Some(&root.join("b/app")), None).is_err());
|
||||
@@ -862,6 +863,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn absolute_linked_roots_build_the_project_and_external_source_module() {
|
||||
let wrapper = if cfg!(windows) { "gradlew.bat" } else { "gradlew" };
|
||||
let temp = tempfile::tempdir().unwrap();
|
||||
let base = dunce::canonicalize(temp.path()).unwrap();
|
||||
let root = base.join("workspace");
|
||||
@@ -871,7 +873,7 @@ mod tests {
|
||||
std::fs::write(external.join("settings.gradle"), "include 'app'").unwrap();
|
||||
let source = external.join("app/src/main/java/App.java");
|
||||
std::fs::write(&source, "class App {}").unwrap();
|
||||
std::fs::write(external.join("gradlew"), "").unwrap();
|
||||
std::fs::write(external.join(wrapper), "").unwrap();
|
||||
let settings = ide_core::settings::GradleSettings {
|
||||
linked_projects: Some(vec![external.to_string_lossy().into_owned()]),
|
||||
..Default::default()
|
||||
@@ -880,7 +882,7 @@ mod tests {
|
||||
model.gradle[0].env.insert("JAVA_HOME".into(), "/external-jdk".into());
|
||||
let all = super::plan(&root, &model, super::BuildKind::Build, None, None).unwrap();
|
||||
assert_eq!(all.steps[0].cwd, external);
|
||||
assert_eq!(all.steps[0].program, external.join("gradlew").to_string_lossy());
|
||||
assert_eq!(all.steps[0].program, external.join(wrapper).to_string_lossy());
|
||||
let module = super::plan(&root, &model, super::BuildKind::Rebuild, Some(&source), None).unwrap();
|
||||
assert_eq!(module.steps[0].cwd, external);
|
||||
assert_eq!(module.steps[0].env["JAVA_HOME"], "/external-jdk");
|
||||
|
||||
@@ -481,6 +481,15 @@ pub fn normalize_linked_projects(root: &Path, dirs: &[String]) -> ide_core::IdeR
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
/// The form `normalize_linked_projects` saves `dir` in, also for a folder that no longer
|
||||
/// exists, so a path the UI sends (`C:\…` on Windows) matches the saved entry.
|
||||
pub fn linked_project_key(root: &Path, dir: &str) -> String {
|
||||
match (dunce::canonicalize(root), dunce::canonicalize(root.join(dir))) {
|
||||
(Ok(root), Ok(path)) => rel(&root, &path),
|
||||
_ => dir.replace('\\', "/"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Linked build identities without reading their scripts (also used by index hot paths).
|
||||
pub(crate) fn linked_dirs(root: &Path, settings: &ide_core::settings::GradleSettings) -> Vec<String> {
|
||||
match &settings.linked_projects {
|
||||
@@ -675,6 +684,30 @@ pub(crate) fn has_war_plugin(script: &str) -> bool {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn a_path_from_the_ui_finds_the_saved_link() {
|
||||
let temp = tempfile::tempdir().unwrap();
|
||||
let root = temp.path().join("workspace");
|
||||
let external = temp.path().join("external");
|
||||
for dir in [root.join("a"), external.clone()] {
|
||||
std::fs::create_dir_all(&dir).unwrap();
|
||||
std::fs::write(dir.join("settings.gradle"), "").unwrap();
|
||||
}
|
||||
let native = |p: &std::path::Path| p.to_string_lossy().into_owned();
|
||||
// Saved by linking; unlinking may send a native, absolute or non-canonical spelling.
|
||||
for (dir, sent) in [
|
||||
("a", native(&root.join("a"))),
|
||||
("a", "a".to_owned()),
|
||||
("a", native(&root.join("a").join("..").join("a"))),
|
||||
("../external", native(&external)),
|
||||
] {
|
||||
let saved = super::normalize_linked_projects(&root, &[dir.into()]).unwrap();
|
||||
assert_eq!(super::linked_project_key(&root, &sent), saved[0], "{sent}");
|
||||
}
|
||||
// A folder deleted since it was linked still matches its saved entry.
|
||||
assert_eq!(super::linked_project_key(&root, r"gone\build"), "gone/build");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn explicit_linkage_preserves_default_discovery_and_can_unlink_every_build() {
|
||||
let temp = tempfile::tempdir().unwrap();
|
||||
|
||||
@@ -216,7 +216,7 @@ mod tests {
|
||||
};
|
||||
assert_eq!(
|
||||
gradle_external_roots(&root, &temp.path().join("cache"), &settings),
|
||||
[external.canonicalize().unwrap()]
|
||||
[dunce::canonicalize(&external).unwrap()]
|
||||
);
|
||||
settings.linked_projects = Some(vec![]);
|
||||
assert!(gradle_external_roots(&root, &temp.path().join("cache"), &settings).is_empty());
|
||||
|
||||
@@ -137,7 +137,8 @@ fn linked_gradle_paths(
|
||||
}
|
||||
}
|
||||
} else {
|
||||
linked.retain(|path| !paths.contains(path));
|
||||
let unlinked: Vec<String> = paths.iter().map(|p| ide_build::gradle::linked_project_key(root, p)).collect();
|
||||
linked.retain(|path| !paths.contains(path) && !unlinked.contains(path));
|
||||
}
|
||||
Ok(linked)
|
||||
}
|
||||
|
||||
Reference in new issue
Заблокувати користувача