Skip to content

Proposal: Add -H:Preserve support via the shared ArtifactSelection model #978

Description

@vjovanov

Note

This is a design proposal for discussion — not an accepted plan or committed work. It sketches a direction and its trade-offs so the approach can be reviewed before anyone implements it. Scope, DSL/XML surface, and naming are all open to change. Depends on #977.

Summary

Add first-class support for -H:Preserve (keeping classes reachable that closed-world analysis would otherwise drop) by reusing the ArtifactSelection model from #977 — no new selection vocabulary. The build-tool value is resolving dependency coordinates to path= entries; the all / bare module=/package= cases remain on the build-args pass-through.

Motivation

-H:Preserve shares LayerCreate's grammar exactly:

-H:Preserve=all
-H:Preserve=module=<m>,package=<p>,path=<jar>

The genuinely build-tool part — "preserve everything from dependency com.acme:extension and its closure" — cannot be written as a static buildArgs entry, because path=<jar> is computed by dependency resolution. That is the justification for a first-class surface; without resolution there is nothing to add over pass-through.

There is currently no tracking issue and no code for Preserve in NBT — it is greenfield on both. Introducing it after #977 means it reuses a proven model rather than inventing a parallel one.

Proposal

Reuse ArtifactSelection and add a renderPreserve(ArtifactSelection) in common/. Surface the same selector block used by layer contents:

binaries {
    main {
        preserve {
            dependencies("com.acme:extension")   // resolved → path=  (the build-tool value)
            packages("com.acme.plugins")         // dynamically-loaded plugin package
            // all()                              // = -H:Preserve=all
        }
    }
}
<preserve>
  <dependencies><dependency>com.acme:extension</dependency></dependencies>
  <packages><package>com.acme.plugins</package></packages>
  <!-- <all>true</all> -->
</preserve>

Both preserve blocks deserialize into the same ArtifactSelection bean and render through the same common code path (so escaping and arg-file conversion are identical).

Spec alignment

Spec links pinned to commit e067164.

  • NGOAL-no-flag-mirroring: first-class only for the dependency-selection dimension (needs resolution); all and bare module/package ride buildArgs/<buildArgs>. Because the selection model is shared with layers, this judgment call is made once and inherited.
  • FS-common-libraries.1 / GOAL-plugin-parity: rendering in common/, symmetric Gradle/Maven surfaces.

Depends on

Related

Deliverables

  • renderPreserve(ArtifactSelection) in common/ + unit tests.
  • Gradle preserve { } block on binaries.
  • Maven <preserve> config on the compile goals.
  • Functional test: preserve an individual dependency + its closure, verify a type that analysis would drop survives.

Open question

Scope Preserve per-binary only (matches how it's used), or also allow it on a created layer's contents? Recommend per-binary first; revisit if a use case appears.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions