diff --git a/THIRD_PARTY_LICENSES.md b/THIRD_PARTY_LICENSES.md index 9a377fec..94751e75 100644 --- a/THIRD_PARTY_LICENSES.md +++ b/THIRD_PARTY_LICENSES.md @@ -1,5 +1,13 @@ # Third-party licenses +## Project Lombok + +`plugins/lombok/lombok-1.18.48-openidea.1.jar` contains Project Lombok v1.18.48 with the Equinox +classloader fix from [projectlombok/lombok#4059](https://github.com/projectlombok/lombok/pull/4059). +Copyright (C) 2009-2021 The Project Lombok Authors. MIT license and bundled component +notices are retained in [plugins/lombok/LICENSE](plugins/lombok/LICENSE) and inside the jar. +Source tag, patch commits and build instructions: [plugins/lombok/README.md](plugins/lombok/README.md). + ## JetBrains IntelliJ Platform icons `src/ui/expui/**` contains SVG icons from the IntelliJ Platform "new UI" (expui) diff --git a/crates/ide-sdk/src/java.rs b/crates/ide-sdk/src/java.rs index 75577856..bf86f581 100644 --- a/crates/ide-sdk/src/java.rs +++ b/crates/ide-sdk/src/java.rs @@ -717,6 +717,20 @@ mod tests { configure_jdtls(&mut plain, Path::new("/p"), Path::new("/data"), &[], None, None, JDTLS_MIN_JAVA, &[], None); let data = |c: &LanguageServerConfig| c.args[c.args.iter().position(|a| a == "-data").unwrap() + 1].clone(); assert_ne!(data(&cfg), data(&plain), "a separate workspace, so everything is rebuilt with the agent"); + let patched = Path::new("/managed/lombok-1.18.48-openidea.1.jar"); + let mut fixed = jdtls(); + configure_jdtls( + &mut fixed, + Path::new("/p"), + Path::new("/data"), + &[], + None, + None, + JDTLS_MIN_JAVA, + &[], + Some(patched), + ); + assert_ne!(data(&cfg), data(&fixed), "the patch must rebuild classes compiled with the official agent"); let before = cfg.clone(); configure_jdtls(&mut cfg, Path::new("/p"), Path::new("/data"), &[], None, None, JDTLS_MIN_JAVA, &[], Some(jar)); assert_eq!(cfg, before, "idempotent"); diff --git a/crates/ide-sdk/src/lombok.rs b/crates/ide-sdk/src/lombok.rs index f8958bc4..67c38338 100644 --- a/crates/ide-sdk/src/lombok.rs +++ b/crates/ide-sdk/src/lombok.rs @@ -1,6 +1,6 @@ //! Lombok for jdtls: ECJ only generates Lombok members when the language //! server's JVM runs `-javaagent:lombok.jar`, so a project using Lombok gets the -//! newest jar available, managed (installed by the IDE) or in the Maven / Gradle caches. +//! app-provided jar, or falls back to the newest release in the Maven / Gradle caches. use crate::home_dir; use std::path::{Path, PathBuf}; @@ -18,7 +18,7 @@ pub enum LombokAgent { Missing, Found { jar: PathBuf, - /// `None` for the managed jar (the latest release when installed). + /// `None` for the app-provided jar (its version is shown by the tool registry). version: Option, }, } @@ -64,7 +64,7 @@ pub fn agent_in_args<'a>(args: impl IntoIterator) -> Option std::io::Result { + let Method::Bundled { filename, bytes, .. } = spec.method else { unreachable!("bundled tool") }; + let dir = root.join(spec.name).join("bin"); + // The agent path participates in the JDT LS workspace key: upgrades must + // discard incremental state compiled with the previous agent. + let path = dir.join(filename); + if std::fs::read(&path).is_ok_and(|old| old == bytes) { + return Ok(path); + } + std::fs::create_dir_all(&dir)?; + let nonce = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap_or_default().as_nanos(); + let temporary = dir.join(format!(".{filename}-{}-{nonce}.tmp", std::process::id())); + let result = std::fs::write(&temporary, bytes).and_then(|_| std::fs::rename(&temporary, &path)); + if let Err(error) = result { + let _ = std::fs::remove_file(&temporary); + // Another window may have installed the same bytes concurrently. + if !std::fs::read(&path).is_ok_and(|old| old == bytes) { + tracing::warn!(tool = spec.name, %error, "Bundled plugin unavailable"); + return Err(error); + } + } + Ok(path) +} diff --git a/crates/ide-tools/src/lib.rs b/crates/ide-tools/src/lib.rs index d5cd36c0..51272bf6 100644 --- a/crates/ide-tools/src/lib.rs +++ b/crates/ide-tools/src/lib.rs @@ -10,6 +10,7 @@ //! [`tools_bin_dirs`] on the process `PATH` at startup. pub mod archive; +mod bundled; pub mod jdk; pub mod platform; mod process; @@ -146,7 +147,7 @@ fn managed_bin_dir(root: &Path, spec: &ToolSpec, platform: Platform) -> Option

{ Some(root.join(spec.name).join("venv").join(if platform.os == Os::Windows { "Scripts" } else { "bin" })) } - Method::Rustup { .. } | Method::PipUser { .. } | Method::PipTarget { .. } => None, + Method::Bundled { .. } | Method::Rustup { .. } | Method::PipUser { .. } | Method::PipTarget { .. } => None, } } @@ -389,9 +390,12 @@ fn is_bundle(command: &str) -> bool { command.ends_with(".jar") } -/// Path of an installed plugin bundle (`//bin/`), if present. +/// Path of an app-provided or installed plugin jar. Embedded jars are materialized on demand. pub fn managed_bundle(app_data: &Path, name: &str) -> Option { let spec = registry::find(name)?; + if matches!(spec.method, Method::Bundled { .. }) { + return bundled::install(&tools_dir(app_data), spec).ok(); + } let jar = spec.commands.first().filter(|c| is_bundle(c))?; let path = tools_dir(app_data).join(spec.name).join("bin").join(jar); path.is_file().then_some(path) @@ -542,6 +546,7 @@ impl Tools { fn method_missing(&self, method: Method) -> Option { let need = |ok: bool, msg: &str| (!ok).then(|| msg.to_string()); match method { + Method::Bundled { .. } => None, Method::Rustup { .. } => need(self.which("rustup").is_some(), "rustup is required (https://rustup.rs)"), Method::Npm { .. } => need( self.which_any(&["npm", "pnpm"]).is_some(), @@ -604,7 +609,8 @@ impl Tools { }; let (status, path, version) = self.detect(spec); let unavailable_reason = self.unavailable_reason(spec); - let can_uninstall = status == ToolStatus::Installed; + let bundled = matches!(spec.method, Method::Bundled { .. }); + let can_uninstall = status == ToolStatus::Installed && !bundled; Some(ToolInfo { name: spec.name.to_string(), display_name: spec.display.to_string(), @@ -615,7 +621,7 @@ impl Tools { path: path.map(Into::into), version, install_method: Some(self.install_method(spec).label().to_string()), - can_install: unavailable_reason.is_none(), + can_install: unavailable_reason.is_none() && !bundled, unavailable_reason, can_uninstall, install_hint, @@ -654,6 +660,12 @@ impl Tools { } fn detect(&self, spec: &ToolSpec) -> (ToolStatus, Option, Option) { + if let Method::Bundled { version, .. } = spec.method { + return match bundled::install(&self.root, spec) { + Ok(path) => (ToolStatus::Installed, Some(path), Some(version.to_owned())), + Err(_) => (ToolStatus::Missing, None, None), + }; + } let main = spec.commands[0]; let managed = managed_bin_dir(&self.root, spec, self.platform).and_then(|d| self.exe_in(&d, main)); if let Some(path) = managed { @@ -782,6 +794,9 @@ impl Tools { let method = self.install_method(spec); emit(InstallEvent::Line { text: format!("Installing {} via {}…", spec.display, method.label()) }); match method { + Method::Bundled { .. } => { + bundled::install(&self.root, spec)?; + } Method::Rustup { component } => { let mut c = self.command(self.which("rustup").expect("checked")); c.args(["component", "add", component]); @@ -1188,6 +1203,8 @@ impl Tools { let cancel = CancellationToken::new(); let quiet = |_: InstallEvent| {}; match self.install_method(spec) { + // App-owned resources remain available for every project/window. + Method::Bundled { .. } => {} Method::Rustup { component } => { let rustup = self.which("rustup").ok_or_else(|| IdeError::unavailable("rustup not found"))?; let mut c = self.command(rustup); diff --git a/crates/ide-tools/src/registry.rs b/crates/ide-tools/src/registry.rs index 8bac7190..d23f6db3 100644 --- a/crates/ide-tools/src/registry.rs +++ b/crates/ide-tools/src/registry.rs @@ -31,6 +31,8 @@ pub enum ReleaseSource { #[derive(Clone, Copy, Debug)] pub enum Method { + /// An app-owned plugin jar, materialized without a network download. + Bundled { filename: &'static str, version: &'static str, bytes: &'static [u8] }, /// `rustup component add `. Rustup { component: &'static str }, /// npm / pnpm packages installed into `/node_modules`; the first is the primary one. @@ -56,6 +58,7 @@ pub enum Method { impl Method { pub fn label(&self) -> &'static str { match self { + Method::Bundled { .. } => "Bundled", Method::Rustup { .. } => "rustup", Method::Npm { .. } => "npm", Method::Go { .. } => "go install", @@ -578,15 +581,13 @@ pub static TOOLS: &[ToolSpec] = &[ display: "Lombok (jdtls agent)", kind: Plugin, commands: &["lombok.jar"], - // Newer than the Lombok a project pins: old ones fail inside current ECJ. - method: Method::Release { - source: ReleaseSource::MavenCentral { group: "org.projectlombok", artifact: "lombok" }, - assets: &[ - any_arch(Mac, "lombok-*.jar"), - any_arch(Linux, "lombok-*.jar"), - any_arch(Windows, "lombok-*.jar"), - ], - locate: &["lombok.jar"], + // Equinox removed getModuleClassLoader(boolean), breaking lombok.config lookup. + // Return to Maven Central once an official release includes lombok#4059. + // Source commit, build instructions and checksum: plugins/lombok/README.md. + method: Method::Bundled { + filename: "lombok-1.18.48-openidea.1.jar", + version: "1.18.48-openidea.1", + bytes: include_bytes!("../../../plugins/lombok/lombok-1.18.48-openidea.1.jar"), }, fallback: None, version: V, diff --git a/crates/ide-tools/src/sources.rs b/crates/ide-tools/src/sources.rs index 09ec12a4..2337bc75 100644 --- a/crates/ide-tools/src/sources.rs +++ b/crates/ide-tools/src/sources.rs @@ -434,7 +434,6 @@ mod tests { assert_eq!(d.version, "1.18.48"); assert_eq!(d.name, "lombok-1.18.48.jar"); assert_eq!(d.url, "https://repo1.maven.org/maven2/org/projectlombok/lombok/1.18.48/lombok-1.18.48.jar"); - assert!(select_asset(&[d.name.as_str()], find("lombok").unwrap().asset_rules(), LINUX).is_some()); assert!(maven_pick("g", "a", "").is_err()); assert!(maven_pick("g", "a", "../x").is_err()); } diff --git a/crates/ide-tools/src/tests.rs b/crates/ide-tools/src/tests.rs index e6aa29c2..00a3c55e 100644 --- a/crates/ide-tools/src/tests.rs +++ b/crates/ide-tools/src/tests.rs @@ -43,6 +43,61 @@ fn find<'a>(list: &'a [ToolInfo], name: &str) -> &'a ToolInfo { list.iter().find(|t| t.name == name).unwrap_or_else(|| panic!("{name} not listed")) } +#[test] +fn bundled_lombok_replaces_legacy_installs_on_every_platform_without_downloads() { + for os in [Os::Mac, Os::Linux, Os::Windows] { + for arch in [platform::Arch::Arm64, platform::Arch::X64] { + let env = Env::new(); + let legacy = env.root.join("lombok/bin/lombok.jar"); + fs::create_dir_all(legacy.parent().unwrap()).unwrap(); + fs::write(&legacy, b"old official agent").unwrap(); + fs::write(env.root.join("lombok").join(MANIFEST), r#"{"version":"1.18.48"}"#).unwrap(); + let tools = Tools::with_paths(env.root.clone(), OsString::new(), Platform { os, arch }); + let info = tools.info("lombok", &[]).unwrap(); + let path = managed_bundle(env.root.parent().unwrap(), "lombok").unwrap(); + assert_eq!(info.status, ToolStatus::Installed); + assert_eq!(info.version.as_deref(), Some("1.18.48-openidea.1")); + assert_eq!(info.path, Some(path.clone().into())); + assert_eq!(path.file_name().unwrap(), "lombok-1.18.48-openidea.1.jar"); + assert!(!info.can_install && !info.can_uninstall); + assert_eq!(tools.unavailable_reason(registry::find("lombok").unwrap()), None); + assert_eq!(fs::read(&legacy).unwrap(), b"old official agent", "other windows may still use it"); + } + } +} + +#[test] +fn bundled_lombok_is_available_offline_and_repairs_missing_or_corrupt_files() { + let env = Env::new(); + let data = env.root.parent().unwrap(); + let agent = managed_bundle(data, "lombok").unwrap(); + let checksum = include_str!("../../../plugins/lombok/SHA256SUMS").split_whitespace().next().unwrap(); + assert_eq!(sources::sha256_file(&agent).unwrap(), checksum); + for contents in [None, Some(b"incomplete jar".as_slice())] { + if let Some(contents) = contents { + fs::write(&agent, contents).unwrap(); + } else { + fs::remove_file(&agent).unwrap(); + } + assert_eq!(managed_bundle(data, "lombok"), Some(agent.clone())); + assert_eq!(sources::sha256_file(&agent).unwrap(), checksum); + } + assert_eq!(fs::read_dir(agent.parent().unwrap()).unwrap().count(), 1, "no temporary jars left behind"); +} + +#[test] +fn concurrent_windows_get_the_same_complete_bundled_lombok() { + let env = Env::new(); + let data = env.root.parent().unwrap(); + std::thread::scope(|scope| { + let windows: Vec<_> = (0..8).map(|_| scope.spawn(|| managed_bundle(data, "lombok").unwrap())).collect(); + let paths: Vec<_> = windows.into_iter().map(|window| window.join().unwrap()).collect(); + assert!(paths.iter().all(|path| path == &paths[0])); + let checksum = include_str!("../../../plugins/lombok/SHA256SUMS").split_whitespace().next().unwrap(); + assert_eq!(sources::sha256_file(&paths[0]).unwrap(), checksum); + }); +} + #[test] fn version_parsing() { assert_eq!(parse_version("rust-analyzer 1.85.0 (4d91de4e 2025-02-17)").as_deref(), Some("1.85.0")); diff --git a/plugins/lombok/LICENSE b/plugins/lombok/LICENSE new file mode 100644 index 00000000..465d9bf8 --- /dev/null +++ b/plugins/lombok/LICENSE @@ -0,0 +1,104 @@ +Copyright (C) 2009-2021 The Project Lombok Authors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +============================================================================== +Licenses for included components: + +org.ow2.asm:asm +org.ow2.asm:asm-analysis +org.ow2.asm:asm-commons +org.ow2.asm:asm-tree +org.ow2.asm:asm-util +ASM: a very small and fast Java bytecode manipulation framework + Copyright (c) 2000-2011 INRIA, France Telecom + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + THE POSSIBILITY OF SUCH DAMAGE. + +------------------------------------------------------------------------------ +rzwitserloot/com.zwitserloot.cmdreader + + Copyright © 2010 Reinier Zwitserloot. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +------------------------------------------------------------------------------ + +projectlombok/lombok.patcher + + Copyright (C) 2009-2021 The Project Lombok Authors. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +------------------------------------------------------------------------------ diff --git a/plugins/lombok/README.md b/plugins/lombok/README.md new file mode 100644 index 00000000..70119834 --- /dev/null +++ b/plugins/lombok/README.md @@ -0,0 +1,71 @@ +# Lombok agent for JDT LS + +`lombok-1.18.48-openidea.1.jar` is the app-provided Java agent for JDT LS. It includes +[projectlombok/lombok#4059](https://github.com/projectlombok/lombok/pull/4059), which restores +`lombok.config` lookup after Equinox removed `getModuleClassLoader(boolean)`. + +The jar is embedded by `ide-tools`, so every platform receives the same bytes without a download. +Both Java server startup and launch diagnostics obtain it through `managed_bundle`. Tool detection +reports the same version and path, replacing older IDE-installed official agents automatically. +An explicit user `-javaagent` still takes precedence. Project Maven/Gradle dependencies are unchanged. +The filename includes the patch version because JDT LS workspace keys include the agent path; +changing agents must rebuild classes produced by the old agent. Old jars remain for running windows. + +Replace this temporary bundle with an official Maven Central release once that release includes +#4059. Restore the registry's Maven Central install method and remove the embedded jar. Keep the +workspace migration test when changing its path. + +## Provenance and build + +- Repository: https://github.com/projectlombok/lombok +- Base tag: `v1.18.48` (`b48657c83ce44d027984f539bd36048293ce5e8e`). +- Cherry-picked commits from PR #4059, in order: + - `2d535482a913de46c27d06c5b0b294918ede3f9c` + - `3098f4dd78b5349c2f382e7310e64db810dd380b` +- Upstream version: `1.18.48`; `openidea.1` identifies this distribution of the patched agent. + Upstream's own `java -jar ... --version` still reports `1.18.48`. +- SHA-256: `10f5cd1a32b946d3ff1c075735b94f35fffdbac11dea2be29d24dbf591dd51a4` +- License: [MIT, including bundled component notices](LICENSE), also retained inside the jar. +- Build environment: macOS arm64, OpenJDK 23.0.1, Apache Ant 1.10.15, upstream Ivy setup. + +Build using the upstream Ant entry point described in its contributing guide: + +```sh +git clone https://github.com/projectlombok/lombok.git +cd lombok +git checkout -b openidea-1.18.48 v1.18.48 +git fetch https://github.com/chagong/lombok.git fix-equinox-bundle-wiring +git cherry-pick 2d535482a913de46c27d06c5b0b294918ede3f9c 3098f4dd78b5349c2f382e7310e64db810dd380b +ant dist +cp dist/lombok.jar /path/to/openidea/plugins/lombok/lombok-1.18.48-openidea.1.jar +``` + +The changelog context conflicts because the first commit adds a release-note line under the +older development heading and the second removes it. Resolve mechanically by adding then removing +only that line, retaining the complete v1.18.48 changelog. The final source diff from v1.18.48 changes +only `src/eclipseAgent/lombok/launch/PatchFixesHider.java`, `test/core/src/lombok/TestEclipse.java`, +and `test/core/src/lombok/launch/PatchFixesHiderTest.java`; no other Java source changes were made. +In the build checkout, the two remote resolvers in +`buildScripts/ivysettings.xml` were reordered to try Maven Central before Eclipse staging; the +local resolver and all dependency coordinates were retained. The JVM was configured to use the +local HTTP/HTTPS proxy for downloads. Ant's timestamps mean a rebuild need not be byte-identical; +`SHA256SUMS` records the checked-in artifact and is verified by the Rust regression tests. + +## Validation + +A parent POM with DTO and application modules uses Lombok 1.18.46. Root `lombok.config` enables +`config.stopBubbling = true` and `lombok.accessors.chain = true`. + +On macOS arm64 with JDT LS 1.63.0 / Equinox 3.25.0, server OpenJDK 25 and project JDK 21: + +- Official 1.18.46 and 1.18.48 agents generated `void` setters; the following Maven incremental + build reused the DTO classes and failed with `void cannot be dereferenced`. +- This agent passed five fresh project/workspace builds. `javap` confirmed setters returning + the DTO class; the following Maven incremental install passed. +- Without `lombok.config`, both official 1.18.48 and patched agents built successfully and produced + identical `javap` output, including default `void` setters. +- Upstream `PatchFixesHiderTest` passed both the standard OSGi and legacy Equinox lookup tests. + +Windows and Linux runtime validation has not been performed. Rust tests cover source selection +for both supported architectures on all three OS variants, offline provisioning, corruption +repair, concurrent windows, explicit user overrides, and JDT LS workspace migration. diff --git a/plugins/lombok/SHA256SUMS b/plugins/lombok/SHA256SUMS new file mode 100644 index 00000000..67cbcd67 --- /dev/null +++ b/plugins/lombok/SHA256SUMS @@ -0,0 +1 @@ +10f5cd1a32b946d3ff1c075735b94f35fffdbac11dea2be29d24dbf591dd51a4 lombok-1.18.48-openidea.1.jar diff --git a/plugins/lombok/lombok-1.18.48-openidea.1.jar b/plugins/lombok/lombok-1.18.48-openidea.1.jar new file mode 100644 index 00000000..d8a4dce2 Binary files /dev/null and b/plugins/lombok/lombok-1.18.48-openidea.1.jar differ