diff --git a/default.nix b/default.nix index 4432960..4151b21 100644 --- a/default.nix +++ b/default.nix @@ -5,7 +5,9 @@ with pkgs; let - buildGradlePackage = callPackage ./gradle.nix { }; + buildMavenRepo = callPackage ./maven-repo.nix { }; + + buildGradlePackage = callPackage ./gradle.nix { inherit buildMavenRepo; }; gradle2nix = buildGradlePackage { pname = "gradle2nix"; @@ -38,7 +40,7 @@ let ''; passthru = { - inherit buildGradlePackage; + inherit buildGradlePackage buildMavenRepo; }; meta = with lib; { diff --git a/flake.nix b/flake.nix index 564a678..06633ff 100644 --- a/flake.nix +++ b/flake.nix @@ -21,7 +21,8 @@ in { builders = rec { - buildGradlePackage = pkgs.callPackage ./gradle.nix { }; + buildMavenRepo = pkgs.callPackage ./maven-repo.nix { }; + buildGradlePackage = pkgs.callPackage ./gradle.nix { inherit buildMavenRepo; }; default = buildGradlePackage; }; diff --git a/gradle.nix b/gradle.nix index 12c92cf..b505770 100644 --- a/gradle.nix +++ b/gradle.nix @@ -22,15 +22,9 @@ { lib, stdenv, - buildEnv, - fetchs3, - fetchurl, gradle, - maven, - runCommandLocal, - symlinkJoin, + buildMavenRepo, writeText, - writeTextDir, }: let @@ -112,129 +106,18 @@ in }@args: let - inherit (builtins) - attrValues - concatStringsSep - elemAt - filter - fromJSON - getAttr - head - length - mapAttrs - removeAttrs - replaceStrings - ; + inherit (builtins) removeAttrs; - inherit (lib) - mapAttrsToList - readFile - versionAtLeast - versionOlder - ; + inherit (lib) versionAtLeast versionOlder; - inherit (lib.strings) sanitizeDerivationName; - - toCoordinates = - id: - let - coords = builtins.split ":" id; - - parseVersion = - version: - let - parts = builtins.split ":" version; - base = elemAt parts 0; - in - if length parts >= 2 then - let - snapshot = elemAt parts 2; - in - replaceStrings [ "-SNAPSHOT" ] [ "-${snapshot}" ] base - else - base; - in - rec { - group = elemAt coords 0; - module = elemAt coords 2; - version = elemAt coords 4; - uniqueVersion = parseVersion version; - }; - - fetchers' = { - http = fetchurl; - https = fetchurl; - } // fetchers; - - fetch = - name: - { url, hash }: - let - scheme = head (builtins.match "([a-z0-9+.-]+)://.*" url); - fetch' = getAttr scheme fetchers'; - in - fetch' { inherit url hash; }; - - mkModule = - id: artifacts: - let - coords = toCoordinates id; - modulePath = "${replaceStrings [ "." ] [ "/" ] coords.group}/${coords.module}/${coords.version}"; - in - stdenv.mkDerivation { - pname = sanitizeDerivationName "${coords.group}-${coords.module}"; - version = coords.uniqueVersion; - - srcs = mapAttrsToList fetch artifacts; - - dontPatch = true; - dontConfigure = true; - dontBuild = true; - dontFixup = true; - dontInstall = true; - - preUnpack = '' - mkdir -p "$out/${modulePath}" - ''; - - unpackCmd = '' - cp "$curSrc" "$out/${modulePath}/$(stripHash "$curSrc")" - ''; - - sourceRoot = "."; - - preferLocalBuild = true; - 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 - let - lockedDependencySpecs = fromJSON (readFile lockFile); - in - mapAttrs mkModule lockedDependencySpecs; - - 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; + offlineRepo = buildMavenRepo { + inherit + lockFile + pname + version + fetchers + overlays + ; }; initScript = writeText "init.gradle" '' diff --git a/maven-repo.nix b/maven-repo.nix new file mode 100644 index 0000000..2a13615 --- /dev/null +++ b/maven-repo.nix @@ -0,0 +1,189 @@ +{ + lib, + stdenv, + fetchurl, + 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". + # 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}" = ; + # # ... + # } + # + # 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 ? [ ], +}: + +let + + inherit (builtins) + attrValues + elemAt + filter + fromJSON + getAttr + head + length + mapAttrs + replaceStrings + ; + + inherit (lib) mapAttrsToList readFile; + + inherit (lib.strings) sanitizeDerivationName; + + toCoordinates = + id: + let + coords = builtins.split ":" id; + + parseVersion = + version: + let + parts = builtins.split ":" version; + base = elemAt parts 0; + in + if length parts >= 2 then + let + snapshot = elemAt parts 2; + in + replaceStrings [ "-SNAPSHOT" ] [ "-${snapshot}" ] base + else + base; + in + rec { + group = elemAt coords 0; + module = elemAt coords 2; + version = elemAt coords 4; + uniqueVersion = parseVersion version; + }; + + fetchers' = { + http = fetchurl; + https = fetchurl; + } // fetchers; + + fetch = + name: + { url, hash }: + let + scheme = head (builtins.match "([a-z0-9+.-]+)://.*" url); + fetch' = getAttr scheme fetchers'; + in + fetch' { inherit url hash; }; + + mkModule = + id: artifacts: + let + coords = toCoordinates id; + modulePath = "${replaceStrings [ "." ] [ "/" ] coords.group}/${coords.module}/${coords.version}"; + in + stdenv.mkDerivation { + pname = sanitizeDerivationName "${coords.group}-${coords.module}"; + version = coords.uniqueVersion; + + srcs = mapAttrsToList fetch artifacts; + + dontPatch = true; + dontConfigure = true; + dontBuild = true; + dontFixup = true; + dontInstall = true; + + preUnpack = '' + mkdir -p "$out/${modulePath}" + ''; + + unpackCmd = '' + cp "$curSrc" "$out/${modulePath}/$(stripHash "$curSrc")" + ''; + + sourceRoot = "."; + + preferLocalBuild = true; + 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 + let + lockedDependencySpecs = fromJSON (readFile lockFile); + in + mapAttrs mkModule lockedDependencySpecs; + + 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; + }; +in +offlineRepo