Skip to content

Speed up workspace compilation e.g. in Eclipse - New -rebuildtriggerpolicy instruction to avoid unnecessary rebuild cascades#7209

Open
Copilot wants to merge 14 commits intomasterfrom
copilot/optimize-bnd-workspace-builds
Open

Speed up workspace compilation e.g. in Eclipse - New -rebuildtriggerpolicy instruction to avoid unnecessary rebuild cascades#7209
Copilot wants to merge 14 commits intomasterfrom
copilot/optimize-bnd-workspace-builds

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 18, 2026

This pull request introduces a new build optimization to the bnd build system, allowing projects to avoid unnecessary rebuilds of dependent artifacts when the output JAR's content or exported API has not changed. The feature is controlled by a new -rebuildtriggerpolicy property, which can be set to either always (the default) or api. When set to api, the system preserves the JAR's timestamp if the content or exported API is unchanged, thereby preventing downstream rebuild cascades. The implementation includes content and API digest calculation and storage, as well as comprehensive tests validating the new behavior.

Testing

  1. use Eclipse with bndtools
  2. put the following in your /cnf/build.bnd
-rebuildtriggerpolicy: api

or to tie it only to Eclipse builds

-rebuildtriggerpolicy: ${if;${driver;eclipse};api;always}
  1. Press Refresh Bnd Workspace button

  2. rebuild the whole workspace (e.g. clean all just once)

  3. Go to a Bundle A of your choise which has many downstream bundles which depend on A (usually some util bundle at the root of the bundle hiearchy which is used by every bundle)

  4. Pick a random java method and add a System.out.println("hello");

  5. Save the file

Expected result:

  • now only the bundle where you added the change should be build (this is the new behavior and optimization... it just builds the bundle you just changed). Try the same with adding a method comment.
  1. Now make a public API change: e.g. add a new default method to an interface (which just prints hello world)
    Expected result:
  • the current bundle and all downstream bundles should be rebuild (since this is an API change it should behave as before)

Previous bahavior before this PR:
If you remove -rebuildtriggerpolicy from build.bnd if should behave as before this PR - all downstream projects should be build with each change all the time.

Key changes:

Build Optimization: Content/API Digest and Timestamp Preservation

  • Introduced the RebuildTriggerPolicy mechanism in Project.java, which determines whether to preserve the build artifact's timestamp based on content or API digests. This prevents unnecessary downstream rebuilds when the output JAR is unchanged or only non-API changes occurred. The policy supports always (default) and api modes, with digest files (.digest and .api-digest) used for comparison. [1] [2] [3]
  • On build, content and API digests are computed and stored as sidecar files. If the digests match the previous build, the JAR's timestamp is preserved. If not, the timestamp is updated as usual. [1] [2]
  • When removing build files, any associated digest files are also deleted to avoid stale state.

Configuration and Documentation

  • Added the new -rebuildtriggerpolicy property to Constants.java and included it in the recognized options set. [1] [2]
  • Updated the help system with a detailed description of the new property, including usage and examples, in Syntax.java.

Testing

  • Added comprehensive tests in ProjectTest.java to verify:
    • Builds are skipped and timestamps preserved when content is unchanged.
    • JARs are rewritten and digests updated when content changes.
    • The API digest mechanism correctly preserves timestamps when only internal (non-API) changes are made.

Dependency and Import Updates

  • Added necessary imports for digest calculation and API analysis in Project.java. [1] [2] [3] [4] [5]

Copilot AI and others added 2 commits April 18, 2026 08:44
After building a project's JAR, compute a timeless content digest
and compare with the previously stored digest. If the content is
unchanged, preserve the old JAR's timestamp. This prevents
dependent projects from seeing a newer timestamp and triggering
unnecessary rebuilds.

The optimization uses Jar.getTimelessDigest() which ignores
build-time-specific data (BND_LASTMODIFIED, version qualifier)
to determine if the meaningful content has changed. A .digest
sidecar file stores the hex digest alongside each build output.

Agent-Logs-Url: https://github.com/bndtools/bnd/sessions/662b413c-3754-4104-a50f-cb8503721220

Co-authored-by: chrisrueger <188422+chrisrueger@users.noreply.github.com>
Copilot AI requested a review from chrisrueger April 18, 2026 08:49
@chrisrueger chrisrueger changed the title Add content-hash optimization to avoid unnecessary rebuild cascades Add content-hash optimization to avoid unnecessary rebuild cascades (Opus 4.6) Apr 18, 2026
Signed-off-by: Christoph Rueger <chrisrueger@gmail.com>
Compute a SHA-1 digest of the exported API surface (public/protected
types, methods, and fields in Export-Package packages) using the
existing DiffPluginImpl/JavaElement infrastructure.

After building a JAR, store the API digest in a .api-digest sidecar
file. When checking isStale() for dependencies, if a dependency's
JAR is newer but its API digest hasn't changed, skip the rebuild.

This handles the common case where only internal implementation
details change (method bodies, private fields) but the exported
API surface is unchanged - dependent projects don't need rebuilding.

Also records dependency API digest state at build time in a
deps-api-digests properties file so we can detect changes between
builds.

Agent-Logs-Url: https://github.com/bndtools/bnd/sessions/4441a6c7-8747-4fb8-aeac-a4eb08e14c64

Co-authored-by: chrisrueger <188422+chrisrueger@users.noreply.github.com>
Copilot AI and others added 2 commits April 18, 2026 10:56
…ebuilds

Move the API digest check from isStale() into calcPreserveTimestamp().
When a JAR's content changes but its exported API surface is unchanged
(e.g. only method bodies modified), the JAR's timestamp is preserved.
This prevents downstream projects from ever seeing a newer dependency,
eliminating unnecessary cascade rebuilds at the source.

Previously, the API digest check was in isStale() as a secondary check,
but the recursive dependency.isStale(visited) call could short-circuit
before reaching it. By integrating the check into the timestamp
preservation logic, the optimization works with the existing staleness
infrastructure rather than adding a secondary check that can be bypassed.

Removed the now-redundant Level 2 isStale() infrastructure:
- hasDependencyApiChanged()
- saveDependencyApiDigests()
- DEPS_API_DIGESTS constant
- deps-api-digests properties file

Agent-Logs-Url: https://github.com/bndtools/bnd/sessions/dd3b844c-df7b-41a7-a3f1-eb0c95564c39

Co-authored-by: chrisrueger <188422+chrisrueger@users.noreply.github.com>
Agent-Logs-Url: https://github.com/bndtools/bnd/sessions/dd3b844c-df7b-41a7-a3f1-eb0c95564c39

Co-authored-by: chrisrueger <188422+chrisrueger@users.noreply.github.com>
Signed-off-by: Christoph Rueger <chrisrueger@gmail.com>
@chrisrueger
Copy link
Copy Markdown
Contributor

First experiments in my larger project look very promising. Simple java changes which do not change public API or the manifest do not cause a downstream rebuild cascade. Only e.g. changes to interfaces , method signatures etc. would cause a downstream rebuild of dependant projects like before.

This makes development nicer since it cuts down waiting time a lot.

Introduce BuildChangePolicy for build digests

Move JAR content/API digest computation and timestamp-preservation logic into a new BuildChangePolicy helper and use it from Project.saveBuildWithoutClose. Project now calls BuildChangePolicy.doBuildChangePolicy to decide whether to preserve output timestamps and to obtain digest file paths; persistBuildChangePolicyResult stores digest files and applies the preserved timestamp when appropriate. Added the -buildchangepolicy constant and Syntax help (supports 'always' and 'api' behaviors) and updated option sets and call sites to use the new accessors. Removed the older calcDigest/calcApiDigest/calcPreserveTimestamp helpers and inlined their behavior into the new class to centralize policy behavior.

Signed-off-by: Christoph Rueger <chrisrueger@gmail.com>
Signed-off-by: Christoph Rueger <chrisrueger@gmail.com>
intent is clearer

Signed-off-by: Christoph Rueger <chrisrueger@gmail.com>
Signed-off-by: Christoph Rueger <chrisrueger@gmail.com>
Signed-off-by: Christoph Rueger <chrisrueger@gmail.com>
Signed-off-by: Christoph Rueger <chrisrueger@gmail.com>
@chrisrueger chrisrueger changed the title Add content-hash optimization to avoid unnecessary rebuild cascades (Opus 4.6) New -rebuildtriggerpolicy instruction to avoid unnecessary rebuild cascades (Opus 4.6) Apr 18, 2026
Agent-Logs-Url: https://github.com/bndtools/bnd/sessions/57d334d7-fc24-4905-ba18-38784bd01631

Co-authored-by: chrisrueger <188422+chrisrueger@users.noreply.github.com>
@chrisrueger chrisrueger marked this pull request as ready for review April 18, 2026 19:49
@chrisrueger
Copy link
Copy Markdown
Contributor

@peterkir FYI Let's discuss next week.

@chrisrueger chrisrueger changed the title New -rebuildtriggerpolicy instruction to avoid unnecessary rebuild cascades (Opus 4.6) New -rebuildtriggerpolicy instruction to avoid unnecessary rebuild cascades Apr 18, 2026
@chrisrueger chrisrueger changed the title New -rebuildtriggerpolicy instruction to avoid unnecessary rebuild cascades Speed up workspace compilation e.g. in Eclipse - New -rebuildtriggerpolicy instruction to avoid unnecessary rebuild cascades Apr 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants