Move Gradle build logic to a setup hook

This commit is contained in:
Tad Fisher
2024-06-13 15:21:58 -07:00
parent a4ef499401
commit b32bf21a6c
26 changed files with 768 additions and 470 deletions

View File

@@ -2,7 +2,8 @@
[[./assets/gradle2nix.png]]
Generate [[https://nixos.org/nix/][Nix]] expressions which build [[https://gradle.org/][Gradle]]-based projects.
Generate [[https://nixos.org/nix/][Nix]] expressions which build
[[https://gradle.org/][Gradle]]-based projects.
** Table of contents
@@ -13,6 +14,10 @@ Generate [[https://nixos.org/nix/][Nix]] expressions which build [[https://gradl
- [[#usage][Usage]]
- [[#for-packagers][For packagers]]
- [[#specifying-the-gradle-installation][Specifying the Gradle installation]]
- [[#reference][Reference]]
- [[#buildGradlePackage][=buildGradlePackage=]]
- [[#buildMavenRepo][=buildMavenRepo=]]
- [[#gradleSetupHook][=gradleSetupHook=]]
- [[#contributing][Contributing]]
- [[#license][License]]
#+END_QUOTE
@@ -25,7 +30,8 @@ is that it is purely functional; a "package" is a function which
accepts inputs (source code, configuration, etc) and produces an
output (binaries, a Java JAR, documentation, really anything).
One benefit of a functional build system is [[https://reproducible-builds.org/][reproducibility]]. If you
One benefit of a functional build system is
[[https://reproducible-builds.org/][reproducibility]]. If you
specify your inputs precisely, and take care not to introduce
impurities—such as files retrieved over a network without tracking
their content—you will receive, byte-for-byte, the exact output as
@@ -42,31 +48,33 @@ inputs, including:
- Environment variables and command-line options
- Artifacts cached on the system hosting the build
=gradle2nix= helps to solve this problem by leveraging Nix to control
the most common inputs to a Gradle build. When run on a project, it
will record all dependencies for both the build environment (including
=plugins= and =buildscript= blocks) and the project, and provide a Nix
expression to run the build given these dependencies. The build itself
is then run in a sandbox, where only content-tracked network requests
are allowed to fetch dependencies, and a local Maven repository is
created on-the-fly to host the dependency artifacts somewhere Gradle
can resolve them without a network.
=gradle2nix= helps to solve this problem by leveraging Nix to
control the most common inputs to a Gradle build. When run on a
project, it will record all dependencies for both the build
environment (including =plugins= and =buildscript= blocks) and the
project, and provide a Nix expression to run the build given these
dependencies. The build itself is then run in a sandbox, where only
content-tracked network requests are allowed to fetch dependencies,
and a local Maven repository is created on-the-fly to host the
dependency artifacts somewhere Gradle can resolve them without a
network.
This tool is useful for both development and packaging. You can use
=gradle2nix= to:
- Create isolated and reproducible development environments that work
anywhere Nix itself can run;
- Create isolated and reproducible development environments that
work anywhere Nix itself can run.
- Reduce or eliminate flakiness and maintenance headaches from CI/CD
pipelines
pipelines.
- Distribute a recipe which can reliably build a Gradle project in
repositories such as the [[https://nixos.org/nixpkgs/][Nix Package Collection]].
repositories such as the [[https://nixos.org/nixpkgs/][Nix Package
Collection]].
** Installation
A [[./gradle.nix][Nix expression]] (generated by =gradle2nix= itself) is provided for
convenience. The following expression will fetch and build the latest
version of this package:
A [[./gradle.nix][Nix expression]] (generated by =gradle2nix=
itself) is provided for convenience. The following expression will
fetch and build the latest version of this package:
#+begin_src nix
import (fetchTarball "https://github.com/tadfisher/gradle2nix/archive/master.tar.gz") {}
@@ -93,8 +101,9 @@ nix-env -if "https://github.com/tadfisher/gradle2nix/archive/master.tar.gz"
=gradle2nix= is not yet packaged in =nixpkgs= itself, but work is
[[https://github.com/NixOS/nixpkgs/pull/77422][in progress]].
The [[./gradle.nix][buildGradlePackage]] function is provided via the
=gradle2nix.passthru.buildGradlePackage= attribute.
The [[./nix/build-gradle-package.nix][buildGradlePackage]] function
is provided via the =gradle2nix.passthru.buildGradlePackage=
attribute.
#+begin_src nix
{ pkgs ? import <nixpkgs> {} }:
@@ -106,23 +115,24 @@ gradle2nix.buildGradlePackage {
pname = "my-package";
version = "1.0";
lockFile = ./gradle.lock;
gradleFlags = [ "installDist" ];
gradleInstallFlags = [ "installDist" ];
# ...
}
#+end_src
*** Flake
A [[./flake.nix][flake.nix]] is provided for those using [[https://nixos.wiki/wiki/Flakes][Nix flakes]]. For example, the
following will build and run =gradle2nix= with the arguments provided
after =--=:
A [[./flake.nix][flake.nix]] is provided for those using
[[https://nixos.wiki/wiki/Flakes][Nix flakes]]. For example, the
following will build and run =gradle2nix= with the arguments
provided after =--=:
#+begin_example
nix run github:tadfisher/gradle2nix -- --help
#+end_example
The [[./gradle.nix][buildGradlePackage]] function is provided via the
=builders= output.
The [[./nix/build-gradle-package.nix][buildGradlePackage]] function
is provided via the =builders= output.
#+begin_src nix
{
@@ -134,7 +144,7 @@ The [[./gradle.nix][buildGradlePackage]] function is provided via the
pname = "my-package";
version = "1.0";
lockFile = ./gradle.lock;
gradleFlags = [ "installDist" ];
gradleInstallFlags = [ "installDist" ];
# ...
};
};
@@ -169,12 +179,12 @@ Arguments:
<args> Extra arguments to pass to Gradle
#+end_example
Simply running =gradle2nix= in the root directory of a project should
be enough for most projects. This will produce two files, by default
called =gradle.lock= and =gradle.nix=, which contain the
pinned dependencies for the project and a standard build expression
which can be imported or called by other Nix expressions. An example
of such an expression can be found in this project's [[./gradle2nix.nix][gradle2nix.nix]].
Simply running =gradle2nix= in the root directory of a project
should be enough for most projects. This will produce a lock file,
by default called =gradle.lock=, which contains the pinned
dependencies for the project. An example of a build expression using
this lock file can be found in this project's
[[./default.nix][default.nix]].
*** For packagers
@@ -211,16 +221,203 @@ gradle2nix --gradle-home=`nix eval nixpkgs#gradle.outPath`/lib/gradle
gradle2nix --gradle-wrapper=8.7
#+end_example
** Reference
*** =buildGradlePackage=
This function is a convenience wrapper around =stdenv.mkDerivation=
that simplifies building Gradle projects with the lock files
produced by =gradle2nix=. It performs the following:
1. Applies [[#gradleSetupHook][=gradleSetupHook=]], overriding the
required =gradle= package if specified.
2. Builds the offline Maven repository with
[[#buildMavenRepo][=buildMavenRepo=]].
3. Sets the JDK used to run Gradle if specified.
4. Applies the offline repo to the Gradle build using an
initialization script.
- Source:
[[./nix/build-gradle-package.nix][build-gradle-package.nix]]
- Location:
- Nix :: =gradle2nix.passthru.buildGradlePackage=
- Flake :: =builders.${system}.buildGradlePackage=
**** Arguments to =buildGradlePackage=
- =lockFile= :: Path to the lock file generated by =gradle2nix=
(e.g. =gradle.lock=).
- =gradlePackage= :: The Gradle package to use. Default is
=pkgs.gradle=.
- =buildJdk= :: Override the default JDK used to run Gradle itself.
- =fetchers= :: Override functions which fetch dependency
artifacts.
See [[#fetchers][detailed documentation]] below.
- =overrides= :: Override artifacts in the offline Maven repository.
See [[#override][detailed documentation]] below.
In addition, this function accepts:
- All arguments to =stdenv.mkDerivation=.
- Arguments specific to =gradleSetupHook= (see
[[#gradleSetupHook][below]]).
*** =buildMavenRepo=
This function takes a lock file and produces a derivation which
downloads all dependencies into a Maven local repository. The
derivation provides a passthru =gradleInitScript= attribute, which
is a Gradle initialization script that can be applied using =gradle
--init-script== or placed in =$GRADLE_USER_HOME/init.d=. The init
script replaces all repositories referenced in the project with the
local repository.
- Source: [[./nix/build-maven-repo.nix][build-maven-repo.nix]]
- Location:
- Nix :: =gradle2nix.passthru.buildMavenRepo=
- Flake :: =builders.${system}.buildMavenRepo=
**** Arguments to =buildMavenRepo=
- =lockFile= :: Path to the lock file generated by gradle2nix (e.g.
=gradle.lock=).
- =fetchers= :: Override functions which fetch dependency
artifacts.
See [[#fetchers][detailed documentation]] below.
- =overrides= :: Override artifacts in the offline Maven repository.
See [[#override][detailed documentation]] below.
*** =gradleSetupHook=
A
[[https://nixos.org/manual/nixpkgs/unstable/#ssec-setup-hooks][setup
hook]] to simplify building Gradle packages. Overrides the default
configure, build, check, and install phases.
To use, add =gradleSetupHook= to a derivation's =nativeBuildInputs=.
- Source: [[./nix/setup-hook.sh][setup-hook.sh]]
- Location:
- Nix :: =gradle2nix.passthru.gradleSetupHook=
- Flake :: =packages.${system}.gradleSetupHook=
**** Variables controlling =gradleSetupHook=
- =gradleInitScript= :: Path to an
[[https://docs.gradle.org/current/userguide/init_scripts.html][initialization
script]] used by =gradle= during all phases.
- =gradleFlags= :: Controls the arguments passed to =gradle= during
all phases.
- =gradleBuildFlags= :: Controls the arguments passed to =gradle=
during the build phase. The build phase is skipped if this is
unset.
- =gradleCheckFlags= :: Controls the arguments passed to =gradle=
during the check phase. The check phase is skipped if this is
unset.
- =gradleInstallFlags= :: Controls the arguments passed to =gradle=
during the install phase. This install phase is skipped if this is
unset.
- =dontUseGradleConfigure= :: When set to true, don't use the
predefined =gradleConfigurePhase=. This will also disable the use
of =gradleInitScript=.
- =dontUseGradleCheck= :: When set to true, don't use the predefined
=gradleCheckPhase=.
- =dontUseGradleInstall= :: When set to true, don't use the
predefined =gradleInstallPhase=.
**** Honored variables
The following variables commonly used by =stdenv.mkDerivation= are
honored by =gradleSetupHook=.
- =enableParallelBuilding=
- =enableParallelChecking=
- =enableParallelInstalling=
*** Common arguments
**** =fetchers=
Names in this set are URL schemes such as "https" or "s3". Values
are functions which take an artifact in the form ={ url, hash }=
and fetches it into the Nix store. For example:
#+begin_src nix
{
s3 = { name, url, hash }: fetchs3 {
s3url = url;
# TODO This doesn't work without patching fetchs3 to accept SRI hashes
inherit name hash;
region = "us-west-2";
credentials = {
access_key_id = "foo";
secret_access_key = "bar";
};
};
}
#+end_src
**** =overrides=
This is an attrset of the form:
#+begin_src nix
{
"${group}:${module}:${version}" = {
"${filename}" = <override function>;
}
}
#+end_src
The override function takes the original derivation from 'fetchers'
(e.g. the result of 'fetchurl') and produces a new derivation to
replace it.
- Replace a dependency's JAR artifact:
#+begin_src nix
{
"com.squareup.okio:okio:3.9.0"."okio-3.9.0.jar" = _: fetchurl {
url = "https://repo.maven.apache.org/maven2/com/squareup/okio/okio/3.9.0/okio-3.9.0.jar";
hash = "...";
downloadToTemp = true;
postFetch = "install -Dt $out/com/squareup/okio/okio/3.9.0/ $downloadedFile"
};
}
#+end_src
- Patch a JAR containing native binaries:
#+begin_src nix
{
"com.android.tools.build:aapt2:8.5.0-rc02-11315950" = {
"aapt2-8.5.0-rc02-11315950-linux.jar" = src: runCommandCC src.name {
nativeBuildInputs = [ jdk autoPatchelfHook ];
dontAutoPatchelf = true;
} ''
cp ${src} aapt2.jar
jar xf aapt2.jar aapt2
chmod +x aapt2
autoPatchelf aapt2
jar uf aapt2.jar aapt2
cp aapt2.jar $out
'';
}
}
#+end_src
** Contributing
Bug reports and feature requests are encouraged.
[[https://github.com/tadfisher/gradle2nix/issues/new][Create an issue]]
[[https://github.com/tadfisher/gradle2nix/issues/new][Create an
issue]]
Code contributions are also encouraged. Please review the test cases
in the [[./fixtures][fixtures]] directory and create a new one to reproduce any fixes
or test new features. See the [[./app/src/test/kotlin/org/nixos/gradle2nix/GoldenTest.kt][existing tests]]
for examples of testing with these fixtures.
in the [[./fixtures][fixtures]] directory and create a new one to
reproduce any fixes or test new features. See the
[[./app/src/test/kotlin/org/nixos/gradle2nix/GoldenTest.kt][existing
tests]] for examples of testing with these fixtures.
** License

View File

@@ -16,7 +16,8 @@ fun connect(
config: Config,
projectDir: File = config.projectDir,
): ProjectConnection =
GradleConnector.newConnector()
GradleConnector
.newConnector()
.apply {
when (val source = config.gradleSource) {
is GradleSource.Distribution -> useDistribution(source.uri)
@@ -24,8 +25,7 @@ fun connect(
GradleSource.Project -> useBuildDistribution()
is GradleSource.Wrapper -> useGradleVersion(source.version)
}
}
.forProjectDirectory(projectDir)
}.forProjectDirectory(projectDir)
.connect()
suspend fun ProjectConnection.buildModel(): GradleBuild =
@@ -67,8 +67,7 @@ suspend fun ProjectConnection.build(
"--refresh-dependencies",
"--gradle-user-home=${config.gradleHome}",
"--init-script=${config.appHome}/init.gradle",
)
.apply {
).apply {
if (config.logger.stacktrace) {
addArguments("--stacktrace")
}
@@ -80,12 +79,14 @@ suspend fun ProjectConnection.build(
withSystemProperties(
mapOf(
"org.gradle.internal.operations.trace" to
config.outDir.toPath().resolve("debug").absolutePathString(),
config.outDir
.toPath()
.resolve("debug")
.absolutePathString(),
),
)
}
}
.run(
}.run(
object : ResultHandler<DependencySet> {
override fun onComplete(result: DependencySet) {
continuation.resume(result)

View File

@@ -48,13 +48,19 @@ val JsonFormat =
}
sealed interface GradleSource {
data class Distribution(val uri: URI) : GradleSource
data class Distribution(
val uri: URI,
) : GradleSource
data class Path(val path: File) : GradleSource
data class Path(
val path: File,
) : GradleSource
data object Project : GradleSource
data class Wrapper(val version: String) : GradleSource
data class Wrapper(
val version: String,
) : GradleSource
}
enum class LogLevel {
@@ -64,9 +70,10 @@ enum class LogLevel {
ERROR,
}
class Gradle2Nix : CliktCommand(
class Gradle2Nix :
CliktCommand(
name = "gradle2nix",
) {
) {
private val tasks: List<String> by option(
"--task",
"-t",
@@ -93,8 +100,7 @@ class Gradle2Nix : CliktCommand(
"-o",
metavar = "DIR",
help = "Path to write generated files",
)
.file(canBeFile = false, canBeDir = true)
).file(canBeFile = false, canBeDir = true)
.defaultLazy("<project>") { projectDir }
internal val lockFile: String by option(
@@ -133,8 +139,7 @@ class Gradle2Nix : CliktCommand(
private val logLevel: LogLevel by option(
"--log",
help = "Print messages with this priority or higher",
)
.enum<LogLevel>(key = { it.name.lowercase() })
).enum<LogLevel>(key = { it.name.lowercase() })
.default(LogLevel.INFO, "info")
private val dumpEvents: Boolean by option(
@@ -192,7 +197,9 @@ class Gradle2Nix : CliktCommand(
addAll(root.editableBuilds)
}
builds.mapNotNull { build ->
build.rootProject.projectDirectory.resolve("buildSrc").takeIf { it.exists() }
build.rootProject.projectDirectory
.resolve("buildSrc")
.takeIf { it.exists() }
}
}

View File

@@ -8,8 +8,8 @@ import kotlin.io.encoding.ExperimentalEncodingApi
fun processDependencies(
config: Config,
dependencySets: Iterable<DependencySet>,
): Env {
return buildMap<DependencyCoordinates, Map<String, Artifact>> {
): Env =
buildMap<DependencyCoordinates, Map<String, Artifact>> {
for (dependencySet in dependencySets) {
val env = dependencySet.toEnv()
@@ -40,16 +40,14 @@ fun processDependencies(
artifacts.toSortedMap()
}.toSortedMap(coordinatesComparator)
.mapKeys { (coordinates, _) -> coordinates.id }
}
private fun DependencySet.toEnv(): Map<DependencyCoordinates, Map<String, Artifact>> {
return dependencies.associate { dep ->
private fun DependencySet.toEnv(): Map<DependencyCoordinates, Map<String, Artifact>> =
dependencies.associate { dep ->
dep.coordinates to
dep.artifacts.associate {
it.name to Artifact(it.url, it.hash.toSri())
}
}
}
@OptIn(ExperimentalEncodingApi::class, ExperimentalStdlibApi::class)
internal fun String.toSri(): String =

View File

@@ -2,7 +2,11 @@ package org.nixos.gradle2nix
import java.util.concurrent.ConcurrentHashMap
class Version(val source: String, val parts: List<String>, base: Version?) : Comparable<Version> {
class Version(
val source: String,
val parts: List<String>,
base: Version?,
) : Comparable<Version> {
private val base: Version
val numericParts: List<Long?>

View File

@@ -3,7 +3,8 @@ package org.nixos.gradle2nix
import io.kotest.core.extensions.install
import io.kotest.core.spec.style.FunSpec
class GoldenTest : FunSpec({
class GoldenTest :
FunSpec({
install(MavenRepo)
context("basic") {
@@ -44,4 +45,4 @@ class GoldenTest : FunSpec({
context("subprojects") {
golden("subprojects/multi-module")
}
})
})

View File

@@ -47,9 +47,7 @@ private val json =
val testLogger = Logger(logLevel = LogLevel.DEBUG, stacktrace = true)
fun fixture(path: String): File {
return Paths.get("../fixtures", path).toFile()
}
fun fixture(path: String): File = Paths.get("../fixtures", path).toFile()
@OptIn(ExperimentalKotest::class, ExperimentalSerializationApi::class, KotestInternal::class)
suspend fun TestScope.fixture(
@@ -60,7 +58,8 @@ suspend fun TestScope.fixture(
val tmp = Paths.get("build/tmp/gradle2nix").apply { toFile().mkdirs() }
val baseDir = Paths.get("../fixtures/projects", project).toFile()
val children =
baseDir.listFiles(FileFilter { it.isDirectory && (it.name == "groovy" || it.name == "kotlin") })
baseDir
.listFiles(FileFilter { it.isDirectory && (it.name == "groovy" || it.name == "kotlin") })
?.toList()
val cases =
if (children.isNullOrEmpty()) {
@@ -175,8 +174,8 @@ object MavenRepo : MountableExtension<MavenRepo.Config, NettyApplicationEngine>,
return tryStart(3).also { this.server = it }
}
private fun tryStart(attempts: Int): NettyApplicationEngine {
return try {
private fun tryStart(attempts: Int): NettyApplicationEngine =
try {
val p = config.port ?: Random.nextInt(10000, 65000)
val s =
embeddedServer(Netty, port = p, host = config.host) {
@@ -202,7 +201,6 @@ object MavenRepo : MountableExtension<MavenRepo.Config, NettyApplicationEngine>,
} catch (e: Throwable) {
if (config.port == null && attempts > 0) tryStart(attempts - 1) else throw e
}
}
override suspend fun afterSpec(spec: Spec) {
server?.stop()

View File

@@ -5,9 +5,16 @@
with pkgs;
let
buildMavenRepo = callPackage ./maven-repo.nix { };
buildMavenRepo = callPackage ./nix/build-maven-repo.nix { };
buildGradlePackage = callPackage ./gradle.nix { inherit buildMavenRepo; };
gradleSetupHook = makeSetupHook {
name = "gradle-setup-hook";
propagatedBuildInputs = [ gradle ];
} ./nix/setup-hook.sh;
buildGradlePackage = callPackage ./nix/build-gradle-package.nix {
inherit buildMavenRepo gradleSetupHook;
};
gradle2nix = buildGradlePackage {
pname = "gradle2nix";
@@ -30,9 +37,9 @@ let
};
};
gradleFlags = [ "installDist" ];
gradleInstallFlags = [ ":app:installDist" ];
installPhase = ''
postInstall = ''
mkdir -p $out/{bin,/lib/gradle2nix}
cp -r app/build/install/gradle2nix/* $out/lib/gradle2nix/
rm $out/lib/gradle2nix/bin/gradle2nix.bat
@@ -40,7 +47,7 @@ let
'';
passthru = {
inherit buildGradlePackage buildMavenRepo;
inherit buildGradlePackage buildMavenRepo gradleSetupHook;
};
meta = with lib; {

6
flake.lock generated
View File

@@ -20,11 +20,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1716769173,
"narHash": "sha256-7EXDb5WBw+d004Agt+JHC/Oyh/KTUglOaQ4MNjBbo5w=",
"lastModified": 1718160348,
"narHash": "sha256-9YrUjdztqi4Gz8n3mBuqvCkMo4ojrA6nASwyIKWMpus=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "9ca3f649614213b2aaf5f1e16ec06952fe4c2632",
"rev": "57d6973abba7ea108bac64ae7629e7431e0199b6",
"type": "github"
},
"original": {

View File

@@ -20,15 +20,15 @@
inherit (nixpkgs) lib;
in
{
builders = rec {
buildMavenRepo = pkgs.callPackage ./maven-repo.nix { };
buildGradlePackage = pkgs.callPackage ./gradle.nix { inherit buildMavenRepo; };
default = buildGradlePackage;
builders = {
inherit (self.packages.${system}.gradle2nix) buildGradlePackage buildMavenRepo;
default = self.packages.${system}.buildGradlePackage;
};
packages = rec {
packages = {
inherit (self.packages.${system}.gradle2nix) gradleSetupHook;
gradle2nix = pkgs.callPackage ./default.nix { };
default = gradle2nix;
default = self.packages.${system}.gradle2nix;
};
apps = rec {

View File

@@ -1,238 +0,0 @@
# This file is generated by gradle2nix.
#
# Example usage (e.g. in default.nix):
#
# with (import <nixpkgs> {});
# let
# buildGradle = callPackage ./gradle.nix {};
# in
# buildGradle {
# lockFile = ./gradle.lock;
#
# src = ./.;
#
# gradleFlags = [ "installDist" ];
#
# installPhase = ''
# mkdir -p $out
# cp -r app/build/install/myproject $out
# '';
# }
{
lib,
stdenv,
gradle,
buildMavenRepo,
writeText,
}:
let
defaultGradle = gradle;
in
{
# Path to the lockfile generated by gradle2nix (e.g. gradle.lock).
lockFile ? null,
pname ? "project",
version ? null,
enableParallelBuilding ? true,
# The Gradle package to use. Default is 'pkgs.gradle'.
gradle ? defaultGradle,
# Arguments to Gradle used to build the project in buildPhase.
gradleFlags ? [ "build" ],
# Enable debugging for the Gradle build; this will cause Gradle to run
# a debug server and wait for a JVM debugging client to attach.
enableDebug ? false,
# Additional code to run in the Gradle init script (init.gradle).
extraInit ? "",
# Override the default JDK used to run Gradle itself.
buildJdk ? null,
# Override functions which fetch dependency artifacts.
# Keys in this set are URL schemes such as "https" or "s3".
# Values are functions which take a dependency in the form
# `{ urls, hash }` and fetch into the Nix store. For example:
#
# {
# s3 = { name, urls, hash }: fetchs3 {
# s3url = builtins.head urls;
# # TODO This doesn't work without patching fetchs3 to accept SRI hashes
# inherit name hash;
# region = "us-west-2";
# credentials = {
# access_key_id = "foo";
# secret_access_key = "bar";
# };
# };
# }
fetchers ? { },
# Overlays for dependencies in the offline Maven repository.
#
# Acceps an attrset of dependencies (usually parsed from 'lockFile'), and produces an attrset
# containing dependencies to merge into the final set.
#
# The attrset is of the form:
#
# {
# "${group}:${module}:${version}" = <derivation>;
# # ...
# }
#
# A dependency derivation unpacks multiple source files into a single Maven-style directory named
# "${out}/${groupPath}/${module}/${version}/", where 'groupPath' is the dependency group ID with dot
# characters ('.') replaced by the path separator ('/').
#
# Examples:
#
# 1. Add or replace a dependency with a single JAR file:
#
# (_: {
# "com.squareup.okio:okio:3.9.0" = fetchurl {
# url = "https://repo.maven.apache.org/maven2/com/squareup/okio/okio/3.9.0/okio-3.9.0.jar";
# hash = "...";
# downloadToTemmp = true;
# postFetch = "install -Dt $out/com/squareup/okio/okio/3.9.0/ $downloadedFile"
# };
# })
#
# 2. Remove a dependency entirely:
#
# # This works because the result is filtered for values that are derivations.
# (_: {
# "org.apache.log4j:core:2.23.1" = null;
# })
overlays ? [ ],
...
}@args:
let
inherit (builtins) removeAttrs;
inherit (lib) versionAtLeast versionOlder;
offlineRepo = buildMavenRepo {
inherit
lockFile
pname
version
fetchers
overlays
;
};
initScript = writeText "init.gradle" ''
import org.gradle.util.GradleVersion
static boolean versionAtLeast(String version) {
return GradleVersion.current() >= GradleVersion.version(version)
}
static void configureRepos(RepositoryHandler repositories) {
repositories.configureEach { ArtifactRepository repo ->
if (repo instanceof MavenArtifactRepository) {
repo.setArtifactUrls(new HashSet<URI>())
repo.url 'file:${offlineRepo}'
repo.metadataSources {
gradleMetadata()
mavenPom()
artifact()
}
} else if (repo instanceof IvyArtifactRepository) {
repo.url 'file:${offlineRepo}'
repo.layout('maven')
repo.metadataSources {
gradleMetadata()
ivyDescriptor()
artifact()
}
} else if (repo instanceof UrlArtifactRepository) {
repo.url 'file:/homeless-shelter'
}
}
}
beforeSettings { settings ->
configureRepos(settings.pluginManagement.repositories)
configureRepos(settings.buildscript.repositories)
if (versionAtLeast("6.8")) {
configureRepos(settings.dependencyResolutionManagement.repositories)
}
}
beforeProject { project ->
configureRepos(project.buildscript.repositories)
configureRepos(project.repositories)
}
${extraInit}
'';
buildGradlePackage = stdenv.mkDerivation (
finalAttrs:
{
inherit
buildJdk
enableParallelBuilding
enableDebug
gradle
gradleFlags
pname
version
;
dontStrip = true;
nativeBuildInputs = [ finalAttrs.gradle ];
buildPhase =
let
finalGradleFlags =
[
"--console=plain"
"--no-build-cache"
"--no-configuration-cache"
"--no-daemon"
"--no-watch-fs"
"--offline"
]
++ lib.optional (finalAttrs.buildJdk != null) "-Dorg.gradle.java.home=${finalAttrs.buildJdk.home}"
++ lib.optional finalAttrs.enableDebug "-Dorg.gradle.debug=true"
++ lib.optional finalAttrs.enableParallelBuilding "--parallel"
++ lib.optional (versionAtLeast finalAttrs.gradle.version "8.0") "--init-script=${initScript}"
++ finalAttrs.gradleFlags;
in
''
runHook preBuild
(
set -eux
export NIX_OFFLINE_REPO='${offlineRepo}'
export GRADLE_USER_HOME="$(mktemp -d)"
${lib.optionalString (versionOlder finalAttrs.gradle.version "8.0") ''
# Work around https://github.com/gradle/gradle/issues/1055
mkdir -p "$GRADLE_USER_HOME/init.d"
ln -s ${initScript} "$GRADLE_USER_HOME/init.d/nix-init.gradle"
''}
gradle ${builtins.toString finalGradleFlags}
)
runHook postBuild
'';
passthru = {
inherit offlineRepo;
};
}
// removeAttrs args [
"lockFile"
"extraInit"
"fetchers"
"overlays"
]
);
in
buildGradlePackage

View File

@@ -0,0 +1,145 @@
# This file is generated by gradle2nix.
#
# Example usage (e.g. in default.nix):
#
# with (import <nixpkgs> {});
# let
# buildGradle = callPackage ./gradle.nix {};
# in
# buildGradle {
# lockFile = ./gradle.lock;
#
# src = ./.;
#
# gradleFlags = [ "installDist" ];
#
# installPhase = ''
# mkdir -p $out
# cp -r app/build/install/myproject $out
# '';
# }
{
lib,
stdenv,
gradle,
buildMavenRepo,
gradleSetupHook,
writeText,
}:
{
# Path to the lockfile generated by gradle2nix (e.g. gradle.lock).
lockFile ? null,
pname ? "project",
version ? null,
# The Gradle package to use. Default is 'pkgs.gradle'.
gradlePackage ? gradle,
# Override the default JDK used to run Gradle itself.
buildJdk ? null,
# Override functions which fetch dependency artifacts.
# Names in this set are URL schemes such as "https" or "s3".
# Values are functions which take a dependency in the form
# `{ urls, hash }` and fetch into the Nix store. For example:
#
# {
# s3 = { name, urls, hash }: fetchs3 {
# s3url = builtins.head urls;
# # TODO This doesn't work without patching fetchs3 to accept SRI hashes
# inherit name hash;
# region = "us-west-2";
# credentials = {
# access_key_id = "foo";
# secret_access_key = "bar";
# };
# };
# }
fetchers ? { },
# Override artifacts in the offline Maven repository.
#
# This is an attrset is of the form:
#
# {
# "${group}:${module}:${version}" = {
# "${filename}" = <override function>;
# }
# }
#
# The override function takes the original derivation from 'fetchers' (e.g. the result of
# 'fetchurl') and produces a new derivation to replace it.
#
# Examples:
#
# 1. Replace a dependency's JAR artifact:
#
# {
# "com.squareup.okio:okio:3.9.0"."okio-3.9.0.jar" = _: fetchurl {
# url = "https://repo.maven.apache.org/maven2/com/squareup/okio/okio/3.9.0/okio-3.9.0.jar";
# hash = "...";
# downloadToTemp = true;
# postFetch = "install -Dt $out/com/squareup/okio/okio/3.9.0/ $downloadedFile"
# };
# }
#
# 2. Patch a JAR containing native binaries:
#
# {
# "com.android.tools.build:aapt2:8.5.0-rc02-11315950" = {
# "aapt2-8.5.0-rc02-11315950-linux.jar" = src: runCommandCC src.name {
# nativeBuildInputs = [ jdk autoPatchelfHook ];
# dontAutoPatchelf = true;
# } ''
# cp ${src} aapt2.jar
# jar xf aapt2.jar aapt2
# chmod +x aapt2
# autoPatchelf aapt2
# jar uf aapt2.jar aapt2
# cp aapt2.jar $out
# '';
# }
# }
overrides ? { },
...
}@args:
let
inherit (builtins) removeAttrs;
gradleSetupHook' = gradleSetupHook.overrideAttrs (_: {
propagatedBuildInputs = [ gradlePackage ];
});
offlineRepo =
if lockFile != null then buildMavenRepo { inherit lockFile fetchers overrides; } else null;
buildGradlePackage = stdenv.mkDerivation (
finalAttrs:
{
inherit buildJdk pname version;
inherit (offlineRepo) gradleInitScript;
dontStrip = true;
nativeBuildInputs = (args.nativeBuildInputs or [ ]) ++ [ gradleSetupHook' ];
gradleFlags =
[ "--console=plain" ]
++ lib.optional (finalAttrs.buildJdk != null) "-Dorg.gradle.java.home=${finalAttrs.buildJdk.home}";
passthru =
lib.optionalAttrs (offlineRepo != null) { inherit offlineRepo; } // (args.passthru or { });
}
// removeAttrs args [
"gradle"
"gradleInitScript"
"lockFile"
"fetchers"
"nativeBuildInputs"
"overlays"
"passthru"
]
);
in
buildGradlePackage

View File

@@ -2,16 +2,15 @@
lib,
stdenv,
fetchurl,
substitute,
symlinkJoin,
}:
{
# Path to the lockfile generated by gradle2nix (e.g. gradle.lock).
lockFile,
pname ? "project",
version ? null,
# Override functions which fetch dependency artifacts.
# Keys in this set are URL schemes such as "https" or "s3".
# Names in this set are URL schemes such as "https" or "s3".
# Values are functions which take a dependency in the form
# `{ urls, hash }` and fetch into the Nix store. For example:
#
@@ -28,42 +27,50 @@
# };
# }
fetchers ? { },
# Overlays for dependencies in the offline Maven repository.
# Override artifacts in the offline Maven repository.
#
# Acceps an attrset of dependencies (usually parsed from 'lockFile'), and produces an attrset
# containing dependencies to merge into the final set.
#
# The attrset is of the form:
# This is an attrset is of the form:
#
# {
# "${group}:${module}:${version}" = <derivation>;
# # ...
# "${group}:${module}:${version}" = {
# "${filename}" = <override function>;
# }
# }
#
# A dependency derivation unpacks multiple source files into a single Maven-style directory named
# "${out}/${groupPath}/${module}/${version}/", where 'groupPath' is the dependency group ID with dot
# characters ('.') replaced by the path separator ('/').
# The override function takes the original derivation from 'fetchers' (e.g. the result of
# 'fetchurl') and produces a new derivation to replace it.
#
# Examples:
#
# 1. Add or replace a dependency with a single JAR file:
# 1. Replace a dependency's JAR artifact:
#
# (_: {
# "com.squareup.okio:okio:3.9.0" = fetchurl {
# {
# "com.squareup.okio:okio:3.9.0"."okio-3.9.0.jar" = _: fetchurl {
# url = "https://repo.maven.apache.org/maven2/com/squareup/okio/okio/3.9.0/okio-3.9.0.jar";
# hash = "...";
# downloadToTemmp = true;
# downloadToTemp = true;
# postFetch = "install -Dt $out/com/squareup/okio/okio/3.9.0/ $downloadedFile"
# };
# })
# }
#
# 2. Remove a dependency entirely:
# 2. Patch a JAR containing native binaries:
#
# # This works because the result is filtered for values that are derivations.
# (_: {
# "org.apache.log4j:core:2.23.1" = null;
# })
overlays ? [ ],
# {
# "com.android.tools.build:aapt2:8.5.0-rc02-11315950" = {
# "aapt2-8.5.0-rc02-11315950-linux.jar" = src: runCommandCC src.name {
# nativeBuildInputs = [ jdk autoPatchelfHook ];
# dontAutoPatchelf = true;
# } ''
# cp ${src} aapt2.jar
# jar xf aapt2.jar aapt2
# chmod +x aapt2
# autoPatchelf aapt2
# jar uf aapt2.jar aapt2
# cp aapt2.jar $out
# '';
# }
# }
overrides ? { },
}:
let
@@ -116,25 +123,28 @@ let
} // fetchers;
fetch =
name:
coords: overrides: name:
{ url, hash }:
let
scheme = head (builtins.match "([a-z0-9+.-]+)://.*" url);
fetch' = getAttr scheme fetchers';
artifact = fetch' { inherit url hash; };
override = overrides.name or lib.id;
in
fetch' { inherit url hash; };
override artifact;
mkModule =
id: artifacts:
let
coords = toCoordinates id;
modulePath = "${replaceStrings [ "." ] [ "/" ] coords.group}/${coords.module}/${coords.version}";
moduleOverrides = overrides.${id} or { };
in
stdenv.mkDerivation {
pname = sanitizeDerivationName "${coords.group}-${coords.module}";
version = coords.uniqueVersion;
srcs = mapAttrsToList fetch artifacts;
srcs = mapAttrsToList (fetch coords moduleOverrides) artifacts;
dontPatch = true;
dontConfigure = true;
@@ -156,34 +166,24 @@ let
allowSubstitutes = false;
};
# Intermediate dependency spec.
#
# We want to allow overriding dependencies via the 'dependencies' function,
# so we pass an intermediate set that maps each Maven coordinate to the
# derivation created with 'mkModule'. This allows users extra flexibility
# to do things like patching native libraries with patchelf or replacing
# artifacts entirely.
lockedDependencies =
final:
if lockFile == null then
{ }
else
modulePaths =
let
lockedDependencySpecs = fromJSON (readFile lockFile);
dependencies = fromJSON (readFile lockFile);
modules = mapAttrs mkModule dependencies;
in
mapAttrs mkModule lockedDependencySpecs;
filter lib.isDerivation (attrValues modules);
finalDependencies =
let
composedExtension = lib.composeManyExtensions overlays;
extended = lib.extends composedExtension lockedDependencies;
fixed = lib.fix extended;
in
filter lib.isDerivation (attrValues fixed);
offlineRepo = symlinkJoin {
name = if version != null then "${pname}-${version}-gradle-repo" else "${pname}-gradle-repo";
paths = finalDependencies;
mavenRepo = symlinkJoin {
name = "gradle-maven-repo";
paths = modulePaths;
passthru.gradleInitScript = substitute {
src = ./init.gradle;
substitutions = [
"--replace"
"@mavenRepo@"
"${mavenRepo}"
];
};
};
in
offlineRepo
mavenRepo

42
nix/init.gradle Normal file
View File

@@ -0,0 +1,42 @@
import org.gradle.util.GradleVersion
static boolean versionAtLeast(String version) {
return GradleVersion.current() >= GradleVersion.version(version)
}
static void configureRepos(RepositoryHandler repositories) {
repositories.configureEach { ArtifactRepository repo ->
if (repo instanceof MavenArtifactRepository) {
repo.setArtifactUrls(new HashSet<URI>())
repo.url 'file:@mavenRepo@'
repo.metadataSources {
gradleMetadata()
mavenPom()
artifact()
}
} else if (repo instanceof IvyArtifactRepository) {
repo.url 'file:@mavenRepo@'
repo.layout('maven')
repo.metadataSources {
gradleMetadata()
ivyDescriptor()
artifact()
}
} else if (repo instanceof UrlArtifactRepository) {
repo.url 'file:/homeless-shelter'
}
}
}
beforeSettings { settings ->
configureRepos(settings.pluginManagement.repositories)
configureRepos(settings.buildscript.repositories)
if (versionAtLeast("6.8")) {
configureRepos(settings.dependencyResolutionManagement.repositories)
}
}
beforeProject { project ->
configureRepos(project.buildscript.repositories)
configureRepos(project.repositories)
}

125
nix/setup-hook.sh Normal file
View File

@@ -0,0 +1,125 @@
# shellcheck shell=bash disable=SC2206,SC2155
gradleConfigurePhase() {
runHook preConfigure
if ! [[ -v enableParallelBuilding ]]; then
enableParallelBuilding=1
echo "gradle: enabled parallel building"
fi
if ! [[ -v enableParallelChecking ]]; then
enableParallelChecking=1
echo "gradle: enabled parallel checking"
fi
if ! [[ -v enableParallelInstalling ]]; then
enableParallelInstalling=1
echo "gradle: enabled parallel installing"
fi
export GRADLE_USER_HOME="$(mktemp -d)"
if [ -n "$gradleInitScript" ]; then
if [ ! -f "$gradleInitScript" ]; then
echo "gradleInitScript is not a file path: $gradleInitScript"
exit 1
fi
mkdir -p "$GRADLE_USER_HOME/init.d"
ln -s "$gradleInitScript" "$GRADLE_USER_HOME/init.d"
fi
runHook postConfigure
}
gradleBuildPhase() {
runHook preBuild
if [ -z "${gradleBuildFlags:-}" ] && [ -z "${gradleBuildFlagsArray[*]}" ]; then
echo "gradleBuildFlags is not set, doing nothing"
else
local flagsArray=(
$gradleFlags "${gradleFlagsArray[@]}"
$gradleBuildFlags "${gradleBuildFlagsArray[@]}"
)
if [ -n "$enableParallelBuilding" ]; then
flagsArray+=(--parallel --max-workers ${NIX_BUILD_CORES})
else
flagsArray+=(--no-parallel)
fi
echoCmd 'gradleBuildPhase flags' "${flagsArray[@]}"
gradle "${flagsArray[@]}"
fi
runHook postBuild
}
gradleCheckPhase() {
runHook preCheck
if [ -z "${gradleCheckFlags:-}" ] && [ -z "${gradleCheckFlagsArray[*]}" ]; then
echo "gradleCheckFlags is not set, doing nothing"
else
local flagsArray=(
$gradleFlags "${gradleFlagsArray[@]}"
$gradleCheckFlags "${gradleCheckFlagsArray[@]}"
${gradleCheckTasks:-check}
)
if [ -n "$enableParallelChecking" ]; then
flagsArray+=(--parallel --max-workers ${NIX_BUILD_CORES})
else
flagsArray+=(--no-parallel)
fi
echoCmd 'gradleCheckPhase flags' "${flagsArray[@]}"
gradle "${flagsArray[@]}"
fi
runHook postCheck
}
gradleInstallPhase() {
runHook preInstall
if [ -z "${gradleInstallFlags:-}" ] && [ -z "${gradleInstallFlagsArray[*]}" ]; then
echo "gradleInstallFlags is not set, doing nothing"
else
local flagsArray=(
$gradleFlags "${gradleFlagsArray[@]}"
$gradleInstallFlags "${gradleInstallFlagsArray[@]}"
)
if [ -n "$enableParallelInstalling" ]; then
flagsArray+=(--parallel --max-workers ${NIX_BUILD_CORES})
else
flagsArray+=(--no-parallel)
fi
echoCmd 'gradleInstallPhase flags' "${flagsArray[@]}"
gradle "${flagsArray[@]}"
fi
runHook postInstall
}
if [ -z "${dontUseGradleConfigure-}" ] && [ -z "${configurePhase-}" ]; then
configurePhase=gradleConfigurePhase
fi
if [ -z "${dontUseGradleBuild-}" ] && [ -z "${buildPhase-}" ]; then
buildPhase=gradleBuildPhase
fi
if [ -z "${dontUseGradleCheck-}" ] && [ -z "${checkPhase-}" ]; then
checkPhase=gradleCheckPhase
fi
if [ -z "${dontUseGradleInstall-}" ] && [ -z "${installPhase-}" ]; then
installPhase=gradleInstallPhase
fi

View File

@@ -1,7 +1,8 @@
package org.nixos.gradle2nix
abstract class Gradle2NixPlugin : AbstractGradle2NixPlugin(
abstract class Gradle2NixPlugin :
AbstractGradle2NixPlugin(
GradleCacheAccessFactoryBase,
DependencyExtractorApplierBase,
ResolveAllArtifactsApplierBase,
)
)

View File

@@ -4,12 +4,12 @@ import org.gradle.api.internal.artifacts.ivyservice.ArtifactCachesProvider
import org.gradle.api.invocation.Gradle
object GradleCacheAccessFactoryBase : GradleCacheAccessFactory {
override fun create(gradle: Gradle): GradleCacheAccess {
return GradleCacheAccessBase(gradle)
}
override fun create(gradle: Gradle): GradleCacheAccess = GradleCacheAccessBase(gradle)
}
class GradleCacheAccessBase(gradle: Gradle) : GradleCacheAccess {
class GradleCacheAccessBase(
gradle: Gradle,
) : GradleCacheAccess {
private val artifactCachesProvider = gradle.service<ArtifactCachesProvider>()
override fun useCache(block: () -> Unit) {

View File

@@ -116,22 +116,27 @@ private fun cachedComponentId(file: File): DependencyCoordinates? {
val parts = file.invariantSeparatorsPath.split('/')
if (parts.size < 6) return null
if (parts[parts.size - 6] != "files-2.1") return null
return parts.dropLast(2).takeLast(3).joinToString(":").let(DefaultDependencyCoordinates::parse)
return parts
.dropLast(2)
.takeLast(3)
.joinToString(":")
.let(DefaultDependencyCoordinates::parse)
}
@OptIn(ExperimentalSerializationApi::class)
private fun parseFileMappings(file: File): Map<String, String>? =
try {
Json.decodeFromStream<JsonObject>(file.inputStream())
.jsonObject["variants"]?.jsonArray
Json
.decodeFromStream<JsonObject>(file.inputStream())
.jsonObject["variants"]
?.jsonArray
?.flatMap { it.jsonObject["files"]?.jsonArray ?: emptyList() }
?.map { it.jsonObject }
?.mapNotNull {
val name = it["name"]?.jsonPrimitive?.content ?: return@mapNotNull null
val url = it["url"]?.jsonPrimitive?.content ?: return@mapNotNull null
if (name != url) name to url else null
}
?.toMap()
}?.toMap()
?.takeUnless { it.isEmpty() }
} catch (e: Throwable) {
null

View File

@@ -17,11 +17,10 @@ class DependencySetModelBuilder(
override fun buildAll(
modelName: String,
project: Project,
): DependencySet {
return dependencyExtractor.buildDependencySet(
): DependencySet =
dependencyExtractor.buildDependencySet(
cacheAccess,
checksumService,
fileStoreAndIndexProvider,
)
}
}

View File

@@ -38,14 +38,15 @@ abstract class AbstractResolveAllArtifactsApplier : ResolveAllArtifactsApplier {
abstract class ResolveProjectDependenciesTask : DefaultTask() {
@Internal
protected fun getReportableConfigurations(): List<Configuration> {
return project.configurations.filter { (it as? DeprecatableConfiguration)?.canSafelyBeResolved() ?: true }
protected fun getReportableConfigurations(): List<Configuration> =
project.configurations.filter {
(it as? DeprecatableConfiguration)?.canSafelyBeResolved() ?: true
}
protected fun Configuration.artifactFiles(): FileCollection {
return incoming.artifactView { viewConfiguration ->
protected fun Configuration.artifactFiles(): FileCollection =
incoming
.artifactView { viewConfiguration ->
viewConfiguration.isLenient = true
viewConfiguration.componentFilter { it is ModuleComponentIdentifier }
}.files
}
}

View File

@@ -18,10 +18,12 @@ object DependencyExtractorApplierG8 : DependencyExtractorApplier {
extractor: DependencyExtractor,
) {
val serviceProvider =
gradle.sharedServices.registerIfAbsent(
gradle.sharedServices
.registerIfAbsent(
"nixDependencyExtractor",
DependencyExtractorService::class.java,
) {}.map { service ->
) {}
.map { service ->
service.apply { this.extractor = extractor }
}
@@ -31,7 +33,9 @@ object DependencyExtractorApplierG8 : DependencyExtractorApplier {
@Suppress("UnstableApiUsage")
internal abstract class DependencyExtractorService :
BuildService<BuildServiceParameters.None>, BuildOperationListener, AutoCloseable {
BuildService<BuildServiceParameters.None>,
BuildOperationListener,
AutoCloseable {
var extractor: DependencyExtractor? = null
override fun started(

View File

@@ -23,13 +23,12 @@ abstract class ResolveProjectDependenciesTaskG8
) : ResolveProjectDependenciesTask() {
private val artifactFiles = Cached.of { artifactFiles() }
private fun artifactFiles(): FileCollection {
return objects.fileCollection().from(
private fun artifactFiles(): FileCollection =
objects.fileCollection().from(
getReportableConfigurations().map { configuration ->
configuration.artifactFiles()
},
)
}
@TaskAction
fun action() {

View File

@@ -1,7 +1,8 @@
package org.nixos.gradle2nix
abstract class Gradle2NixPlugin : AbstractGradle2NixPlugin(
abstract class Gradle2NixPlugin :
AbstractGradle2NixPlugin(
GradleCacheAccessFactoryG80,
DependencyExtractorApplierG8,
ResolveAllArtifactsApplierG8,
)
)

View File

@@ -4,12 +4,12 @@ import org.gradle.api.internal.artifacts.ivyservice.ArtifactCachesProvider
import org.gradle.api.invocation.Gradle
object GradleCacheAccessFactoryG80 : GradleCacheAccessFactory {
override fun create(gradle: Gradle): GradleCacheAccess {
return GradleCacheAccessG80(gradle)
}
override fun create(gradle: Gradle): GradleCacheAccess = GradleCacheAccessG80(gradle)
}
class GradleCacheAccessG80(gradle: Gradle) : GradleCacheAccess {
class GradleCacheAccessG80(
gradle: Gradle,
) : GradleCacheAccess {
private val artifactCachesProvider = gradle.service<ArtifactCachesProvider>()
override fun useCache(block: () -> Unit) {

View File

@@ -1,7 +1,8 @@
package org.nixos.gradle2nix
abstract class Gradle2NixPlugin : AbstractGradle2NixPlugin(
abstract class Gradle2NixPlugin :
AbstractGradle2NixPlugin(
GradleCacheAccessFactoryG81,
DependencyExtractorApplierG8,
ResolveAllArtifactsApplierG8,
)
)

View File

@@ -4,12 +4,12 @@ import org.gradle.api.internal.artifacts.ivyservice.ArtifactCachesProvider
import org.gradle.api.invocation.Gradle
object GradleCacheAccessFactoryG81 : GradleCacheAccessFactory {
override fun create(gradle: Gradle): GradleCacheAccess {
return GradleCacheAccessG81(gradle)
}
override fun create(gradle: Gradle): GradleCacheAccess = GradleCacheAccessG81(gradle)
}
class GradleCacheAccessG81(gradle: Gradle) : GradleCacheAccess {
class GradleCacheAccessG81(
gradle: Gradle,
) : GradleCacheAccess {
private val artifactCachesProvider = gradle.service<ArtifactCachesProvider>()
override fun useCache(block: () -> Unit) {