Unverified Commit d3f007d0 authored by Tomodachi94's avatar Tomodachi94 Committed by GitHub
Browse files

buildMavenPackage: Add `overrideMavenAttrs` function and `buildOffline` documentation (#313152)

parents b94e8520 49a11892
Loading
Loading
Loading
Loading
+84 −0
Original line number Diff line number Diff line
@@ -49,6 +49,90 @@ This package calls `maven.buildMavenPackage` to do its work. The primary differe
After setting `maven.buildMavenPackage`, we then do standard Java `.jar` installation by saving the `.jar` to `$out/share/java` and then making a wrapper which allows executing that file; see [](#sec-language-java) for additional generic information about packaging Java applications.
:::

### Overriding Maven package attributes {#maven-overriding-package-attributes}

```
overrideMavenAttrs :: (AttrSet -> Derivation) | ((AttrSet -> Attrset) -> Derivation) -> Derivation
```

The output of `buildMavenPackage` has an `overrideMavenAttrs` attribute, which is a function that takes either
- any subset of the attributes that can be passed to `buildMavenPackage`

  or
- a function that takes the argument passed to the previous invocation of `buildMavenPackage` (conventionally called `old`) and returns an attribute set that can be passed to `buildMavenPackage`

and returns a derivation that builds a Maven package based on the old and new arguments merged.

This is similar to [](#sec-pkg-overrideAttrs), but notably does not allow accessing the final value of the argument to `buildMavenPackage`.

:::{.example}
### `overrideMavenAttrs` Example

Use `overrideMavenAttrs` to build `jd-cli` version 1.2.0 and disable some flaky test:

```nix
jd-cli.overrideMavenAttrs (old: rec {
  version = "1.2.0";
  src = fetchFromGitHub {
    owner = old.src.owner;
    repo = old.src.repo;
    rev = "${old.pname}-${version}";
    # old source hash of 1.2.0 version
    hash = "sha256-US7j6tQ6mh1libeHnQdFxPGoxHzbZHqehWSgCYynKx8=";
  };

  # tests can be disabled by prefixing it with `!`
  # see Maven documentation for more details:
  # https://maven.apache.org/surefire/maven-surefire-plugin/examples/single-test.html#Multiple_Formats_in_One
  mvnParameters = lib.escapeShellArgs [
    "-Dsurefire.failIfNoSpecifiedTests=false"
    "-Dtest=!JavaDecompilerTest#basicTest,!JavaDecompilerTest#patternMatchingTest"
  ];

  # old mvnHash of 1.2.0 maven dependencies
  mvnHash = "sha256-N9XC1pg6Y4sUiBWIQUf16QSXCuiAPpXEHGlgApviF4I=";
});
```
:::

### Offline build {#maven-offline-build}

By default, `buildMavenPackage` does the following:

1. Run `mvn package -Dmaven.repo.local=$out/.m2 ${mvnParameters}` in the
   `fetchedMavenDeps` [fixed-output derivation](https://nixos.org/manual/nix/stable/glossary.html#gloss-fixed-output-derivation).
2. Run `mvn package -o -nsu "-Dmaven.repo.local=$mvnDeps/.m2"
   ${mvnParameters}` again in the main derivation.

As a result, tests are run twice.
This also means that a failing test will trigger a new attempt to realise the fixed-output derivation, which in turn downloads all dependencies again.
For bigger Maven projects, this might lead to a long feedback cycle.

Use `buildOffline = true` to change the behaviour of `buildMavenPackage to the following:
1. Run `mvn de.qaware.maven:go-offline-maven-plugin:1.2.8:resolve-dependencies
   -Dmaven.repo.local=$out/.m2 ${mvnDepsParameters}` in the fixed-output derivation.
2. Run `mvn package -o -nsu "-Dmaven.repo.local=$mvnDeps/.m2"
   ${mvnParameters}` in the main derivation.

As a result, all dependencies are downloaded in step 1 and the tests are executed in step 2.
A failing test only triggers a rebuild of step 2 as it can reuse the dependencies of step 1 because they have not changed.

::: {.warning}
Test dependencies are not downloaded in step 1 and are therefore missing in
step 2 which will most probably fail the build. The `go-offline` plugin cannot
handle these so-called [dynamic dependencies](https://github.com/qaware/go-offline-maven-plugin?tab=readme-ov-file#dynamic-dependencies).
In that case you must add these dynamic dependencies manually with:
```nix
maven.buildMavenPackage rec {
  manualMvnArtifacts = [
    # add dynamic test dependencies here
    "org.apache.maven.surefire:surefire-junit-platform:3.1.2"
    "org.junit.platform:junit-platform-launcher:1.10.0"
  ];
};
```
:::

### Stable Maven plugins {#stable-maven-plugins}

Maven defines default versions for its core plugins, e.g. `maven-compiler-plugin`. If your project does not override these versions, an upgrade of Maven will change the version of the used plugins, and therefore the derivation and hash.
+6 −0
Original line number Diff line number Diff line
@@ -3190,6 +3190,12 @@
  "maven-buildmavenpackage": [
    "index.html#maven-buildmavenpackage"
  ],
  "maven-overriding-package-attributes": [
    "index.html#maven-overriding-package-attributes"
  ],
  "maven-offline-build": [
    "index.html#maven-offline-build"
  ],
  "stable-maven-plugins": [
    "index.html#stable-maven-plugins"
  ],
+25 −9
Original line number Diff line number Diff line
@@ -6,7 +6,6 @@
  makeWrapper,
  stdenvNoCC,
}:

stdenvNoCC.mkDerivation (finalAttrs: {
  pname = "maven";
  version = "3.9.9";
@@ -34,13 +33,30 @@ stdenvNoCC.mkDerivation (finalAttrs: {
    runHook postInstall
  '';

  passthru = {
  passthru =
    let
      makeOverridableMavenPackage =
        mavenRecipe: mavenArgs:
        let
          drv = mavenRecipe mavenArgs;
          overrideWith =
            newArgs: mavenArgs // (if lib.isFunction newArgs then newArgs mavenArgs else newArgs);
        in
        drv
        // {
          overrideMavenAttrs = newArgs: makeOverridableMavenPackage mavenRecipe (overrideWith newArgs);
        };
    in
    {
      buildMaven = callPackage ./build-maven.nix {
        maven = finalAttrs.finalPackage;
      };
    buildMavenPackage = callPackage ./build-maven-package.nix {

      buildMavenPackage = makeOverridableMavenPackage (
        callPackage ./build-maven-package.nix {
          maven = finalAttrs.finalPackage;
    };
        }
      );
    };

  meta = {
@@ -54,7 +70,7 @@ stdenvNoCC.mkDerivation (finalAttrs: {
    '';
    license = lib.licenses.asl20;
    mainProgram = "mvn";
    maintainers = [ ] ++ lib.teams.java.members;
    maintainers = with lib.maintainers; [ tricktron ] ++ lib.teams.java.members;
    inherit (jdk_headless.meta) platforms;
  };
})