mirror of
https://github.com/tadfisher/gradle2nix.git
synced 2026-01-11 15:30:38 -05:00
Move Gradle build logic to a setup hook
This commit is contained in:
271
README.org
271
README.org
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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() }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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?>
|
||||
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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()
|
||||
|
||||
17
default.nix
17
default.nix
@@ -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
6
flake.lock
generated
@@ -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": {
|
||||
|
||||
12
flake.nix
12
flake.nix
@@ -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 {
|
||||
|
||||
238
gradle.nix
238
gradle.nix
@@ -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
|
||||
145
nix/build-gradle-package.nix
Normal file
145
nix/build-gradle-package.nix
Normal 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
|
||||
@@ -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
42
nix/init.gradle
Normal 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
125
nix/setup-hook.sh
Normal 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
|
||||
@@ -1,7 +1,8 @@
|
||||
package org.nixos.gradle2nix
|
||||
|
||||
abstract class Gradle2NixPlugin : AbstractGradle2NixPlugin(
|
||||
abstract class Gradle2NixPlugin :
|
||||
AbstractGradle2NixPlugin(
|
||||
GradleCacheAccessFactoryBase,
|
||||
DependencyExtractorApplierBase,
|
||||
ResolveAllArtifactsApplierBase,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -17,11 +17,10 @@ class DependencySetModelBuilder(
|
||||
override fun buildAll(
|
||||
modelName: String,
|
||||
project: Project,
|
||||
): DependencySet {
|
||||
return dependencyExtractor.buildDependencySet(
|
||||
): DependencySet =
|
||||
dependencyExtractor.buildDependencySet(
|
||||
cacheAccess,
|
||||
checksumService,
|
||||
fileStoreAndIndexProvider,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
package org.nixos.gradle2nix
|
||||
|
||||
abstract class Gradle2NixPlugin : AbstractGradle2NixPlugin(
|
||||
abstract class Gradle2NixPlugin :
|
||||
AbstractGradle2NixPlugin(
|
||||
GradleCacheAccessFactoryG80,
|
||||
DependencyExtractorApplierG8,
|
||||
ResolveAllArtifactsApplierG8,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
package org.nixos.gradle2nix
|
||||
|
||||
abstract class Gradle2NixPlugin : AbstractGradle2NixPlugin(
|
||||
abstract class Gradle2NixPlugin :
|
||||
AbstractGradle2NixPlugin(
|
||||
GradleCacheAccessFactoryG81,
|
||||
DependencyExtractorApplierG8,
|
||||
ResolveAllArtifactsApplierG8,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user