Last active
October 15, 2024 10:00
-
-
Save tadfisher/17000caf8653019a9a98fd9b9b921d93 to your computer and use it in GitHub Desktop.
Simplified Nix integration with Gradle
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ lib | |
, stdenv | |
, jdk | |
, gradle | |
, mavenRepo | |
}: | |
stdenv.mkDerivation { | |
pname = "built-with-gradle"; | |
version = "0.0"; | |
nativeBuildInputs = [ gradle ]; | |
JDK_HOME = "${jdk.home}"; | |
buildPhase = '' | |
runHook preBuild | |
gradle build \ | |
--offline --no-daemon --no-build-cache --info --full-stacktrace \ | |
--warning-mode=all --parallel --console=plain \ | |
-DnixMavenRepo=file://${mavenRepo} | |
runHook postBuild | |
''; | |
installPhase = '' | |
runHook preInstall | |
mkdir -p $out | |
cp -r build/dist/* $out | |
runHook postInstall | |
''; | |
dontStrip = true; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ pkgs ? import <nixpkgs> { } }: | |
with pkgs; | |
lib.makeScope newScope (self: with self; { | |
gradle = callPackage ./gradle.nix { }; | |
updateLocks = callPackage ./update-locks.nix { | |
inherit (haskellPackages) xml-to-json; | |
}; | |
buildMavenRepo = callPackage ./maven-repo.nix { }; | |
mavenRepo = buildMavenRepo { | |
name = "nix-maven-repo"; | |
repos = [ | |
"https://plugins.gradle.org/m2" | |
"https://repo1.maven.org/maven2" | |
]; | |
deps = builtins.fromJSON (builtins.readFile ./deps.json); | |
}; | |
builtWithGradle = callPackage ./build.nix { }; | |
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ gradleGen, fetchurl }: | |
gradleGen rec { | |
name = "gradle-7.0-milestone-2"; | |
nativeVersion = "0.22-milestone-10"; | |
src = fetchurl ( | |
url = "https://services.gradle.org/distributions/${name}-bin.zip"; | |
sha256 = "10a2zhr7yhj7a9pi2rn1jaqdf1nxnxxpljvfnnz0v3il4p6v2wd9"; | |
}; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ lib | |
, stdenv | |
, buildEnv | |
, fetchurl | |
, writeTextDir | |
}: | |
{ name ? "maven-deps" | |
, repos ? [ ] | |
, deps ? [ ] | |
, extraPaths ? [ ] | |
}: | |
with lib; | |
let | |
mavenize = sep: replaceStrings ["."] [sep]; | |
fetch = | |
{ group | |
, name | |
, version | |
, file | |
, sha256 | |
}: | |
fetchurl { | |
name = file; | |
urls = map (repo: "${repo}/${mavenize "/" group}/${name}/${version}/${file}") repos; | |
inherit sha256; | |
meta.platforms = platforms.all; | |
}; | |
fetchDependency = | |
{ group | |
, name | |
, version | |
, artifacts | |
}: | |
let | |
fetchArtifact = file: sha256: | |
fetch { inherit group name version file sha256; }; | |
# Each artifact uses the filename in the Gradle cache, which doesn't | |
# correspond to the filename in the Maven repo. The mapping of name to URL | |
# is provided by Gradle module metadata, so we fetch that first. See | |
# https://github.com/gradle/gradle/blob/master/subprojects/docs/src/docs/design/gradle-module-metadata-latest-specification.md | |
# for the file format. | |
isModule = hasSuffix ".module"; | |
moduleArtifacts = filterAttrs (file: _: isModule file) artifacts; | |
otherArtifacts = filterAttrs (file: _: !isModule file) artifacts; | |
modules = mapAttrsToList fetchArtifact moduleArtifacts; | |
replacements = listToAttrs (flatten (map (module: | |
let | |
json = builtins.fromJSON (builtins.readFile module); | |
variants = json.variants or [ ]; | |
files = flatten (map (v: v.files or [ ]) variants); | |
in | |
map ({ name, url, ... }: nameValuePair name url) files | |
) modules)); | |
replaced = mapAttrs' (file: sha256: | |
nameValuePair (replacements.${file} or file) sha256 | |
) otherArtifacts; | |
in | |
if moduleArtifacts == { } | |
then mapAttrsToList fetchArtifact artifacts | |
else modules ++ (mapAttrsToList fetchArtifact replaced); | |
mkDep = | |
{ group | |
, name | |
, version | |
, artifacts | |
}@dep: | |
stdenv.mkDerivation { | |
pname = "${mavenize "-" group}-${name}"; | |
inherit version; | |
srcs = fetchDependency dep; | |
sourceRoot = "."; | |
phases = "installPhase"; | |
enableParallelBuilding = true; | |
preferLocalBuild = true; | |
installPhase = '' | |
dest=$out/${mavenize "/" group}/${name}/${version} | |
mkdir -p $dest | |
for src in $srcs; do | |
cp $src $dest/$(stripHash $src) | |
done | |
''; | |
}; | |
mkMetadata = deps: | |
let | |
modules = groupBy' | |
(meta: { group, name, version, ... }: | |
let | |
isNewer = versionOlder meta.latest version; | |
isNewerRelease = versionOlder meta.release version; | |
in { | |
groupId = group; | |
artifactId = name; | |
latest = if isNewer then version else meta.latest; | |
release = if isNewerRelease then version else meta.release; | |
versions = meta.versions ++ [ version ]; | |
} | |
) | |
{ | |
latest = ""; | |
release = ""; | |
versions = [ ]; | |
} | |
({ group, name, ... }: "${mavenize "/" group}/${name}/maven-metadata.xml") | |
deps; | |
in | |
attrValues (mapAttrs (path: { groupId, artifactId, latest, release, versions }: | |
let | |
versions' = sort versionOlder (unique versions); | |
in | |
writeTextDir path '' | |
<?xml version="1.0" encoding="UTF-8"?> | |
<metadata modelVersion="1.1"> | |
<groupId>${groupId}</groupId> | |
<artifactId>${artifactId}</artifactId> | |
<versioning> | |
${optionalString (latest != "") "<latest>${latest}</latest>"} | |
${optionalString (release != "") "<release>${release}</release>"} | |
<versions> | |
${concatMapStringsSep "\n " (v: "<version>${v}</version>") versions'} | |
</versions> | |
</versioning> | |
</metadata> | |
'' | |
) modules); | |
mkGradleRedirectionPoms = deps: | |
let | |
depsMissingPoms = filter ({ artifacts, ... }@dep: | |
any (f: hasSuffix ".module" f) (attrNames artifacts) && | |
!(any (f: hasSuffix ".pom" f) (attrNames artifacts)) | |
) deps; | |
in | |
map ({ group, name, version, ... }: | |
writeTextDir "${mavenize "/" group}/${name}/${version}/${name}-${version}.pom" '' | |
<project xmlns="http://maven.apache.org/POM/4.0.0" | |
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 | |
http://maven.apache.org/xsd/maven-4.0.0.xsd" | |
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> | |
<!-- This module was also published with a richer model, Gradle metadata, --> | |
<!-- which should be used instead. Do not delete the following line which --> | |
<!-- is to indicate to Gradle or any Gradle module metadata file consumer --> | |
<!-- that they should prefer consuming it instead. --> | |
<!-- do_not_remove: published-with-gradle-metadata --> | |
<modelVersion>4.0.0</modelVersion> | |
<groupId>${group}</groupId> | |
<artifactId>${name}</artifactId> | |
<version>${version}</version> | |
</project> | |
'' | |
) depsMissingPoms; | |
in | |
buildEnv { | |
inherit name; | |
paths = map mkDep deps ++ mkMetadata deps ++ mkGradleRedirectionPoms deps ++ extraPaths; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
val repoProperty = "nixMavenRepo" | |
val systemRepo: Provider<String> = | |
providers.systemProperty(repoProperty).forUseAtConfigurationTime() | |
val gradleRepo: Provider<String> = | |
providers.gradleProperty(repoProperty).forUseAtConfigurationTime() | |
val repo: Provider<List<String>> = | |
systemRepos.orElse(gradleRepos) | |
pluginManagement.repositories { | |
if (repo.isPresent) { | |
clear() | |
maven(repo.get()) | |
} | |
} | |
dependencyResolutionManagement { | |
if (repo.isPresent) { | |
clear() | |
maven(repo.get()) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ lib | |
, writeShellScriptBin | |
, gradle | |
, jq | |
, xml-to-json | |
}: | |
writeShellScriptBin "update-locks" '' | |
set -eu -o pipefail | |
${gradle}/bin/gradle lock --write-locks | |
${gradle}/bin/gradle --write-verification-metadata sha256 dependencies | |
${xml-to-json}/bin/xml-to-json -sam -t components gradle/verification-metadata.xml \ | |
| ${jq}/bin/jq '[ | |
.[] | .component | | |
{ group, name, version, | |
artifacts: [([.artifact] | flatten | .[] | {(.name): .sha256.value})] | add | |
} | |
]' > deps.json | |
rm gradle/verification-metadata.xml | |
'' |
good solution, thanks. Is there a lib where supports all the maven fetch depedencies nix functions?
To avoid re-write them every project?
Here's a follow-up on this chain of work, supporting Gradle 8 and packaged as a flake: https://github.com/owickstrom/minimal-kotlin-nix-example
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I've used this to create a minimal example: https://github.com/tmcl/minimal-android-nix-example