# This file is generated by gradle2nix. # # Example usage (e.g. in default.nix): # # with (import {}); # let # buildGradle = callPackage ./gradle-env.nix {}; # in # buildGradle { # envSpec = ./gradle-env.json; # # src = ./.; # # gradleFlags = [ "installDist" ]; # # installPhase = '' # mkdir -p $out # cp -r app/build/install/myproject $out # ''; # } { lib , stdenv , buildEnv , fetchs3 , fetchurl , gradleGen , runCommandLocal , symlinkJoin , writeText , writeTextDir }: { # Path to the environment spec generated by gradle2nix (e.g. gradle-env.json). envSpec , pname ? "project" , version ? null , enableParallelBuilding ? true # Arguments to Gradle used to build the project in buildPhase. , gradleFlags ? [ "build" ] # Gradle package to use instead of the one generated by gradle2nix. , gradlePackage ? null # 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, sha256 }` 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 ? { } , ... } @ args: let inherit (builtins) attrValues concatStringsSep elemAt filter fromJSON getAttr head match replaceStrings sort; inherit (lib) assertMsg concatMapStringsSep groupBy' hasSuffix hasPrefix last mapAttrs mapAttrsToList optionalString readFile removeSuffix unique versionAtLeast versionOlder; inherit (lib.strings) sanitizeDerivationName; toCoordinates = id: let coords = builtins.split ":" id; in { group = elemAt coords 0; module = elemAt coords 2; }; fetchers' = { http = fetchurl; https = fetchurl; } // fetchers; # Fetch urls using the scheme for the first entry only; there isn't a # straightforward way to tell Nix to try multiple fetchers in turn # and short-circuit on the first successful fetch. fetch = _: { urls, hash }: let first = head urls; scheme = head (builtins.match "([a-z0-9+.-]+)://.*" first); fetch' = getAttr scheme fetchers'; urls' = filter (hasPrefix scheme) urls; in fetch' { urls = urls'; inherit hash; }; mkDep = id: version: artifacts: let coords = toCoordinates id; modulePath = "${replaceStrings ["."] ["/"] coords.group}/${coords.module}/${version}"; in stdenv.mkDerivation { pname = "${coords.group}-${coords.module}"; version = version; 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; }; mkModule = id: versions: mapAttrsToList (version: artifacts: mkDep id version artifacts) versions; mkModuleMetadata = id: versions: let modules = groupBy' (meta: id: let isNewer = versionOlder meta.latest id.version; isNewerRelease = !(hasSuffix "-SNAPSHOT" id.version) && versionOlder meta.release id.version; in { groupId = id.group; artifactId = id.name; latest = if isNewer then id.version else meta.latest; release = if isNewerRelease then id.version else meta.release; versions = meta.versions ++ [id.version]; } ) { latest = ""; release = ""; versions = []; } (id: "${replaceStrings ["."] ["/"] id.group}/${id.name}/maven-metadata.xml") ids; in attrValues (mapAttrs (path: meta: let versions' = sort versionOlder (unique meta.versions); in with meta; writeTextDir path '' ${groupId} ${artifactId} ${optionalString (latest != "") "${latest}"} ${optionalString (release != "") "${release}"} ${concatMapStringsSep "\n " (v: "${v}") versions'} '' ) modules); # mkSnapshotMetadata = deps: # let # snapshotDeps = filter (dep: dep ? build && dep ? timestamp) deps; # modules = groupBy' # (meta: dep: # let # id = dep.id; # isNewer = dep.build > meta.buildNumber; # # Timestamp values can be bogus, e.g. jitpack.io # updated = if (match "[0-9]{8}\.[0-9]{6}" dep.timestamp) != null # then replaceStrings ["."] [""] dep.timestamp # else ""; # in { # groupId = id.group; # artifactId = id.name; # version = id.version; # timestamp = if isNewer then dep.timestamp else meta.timestamp; # buildNumber = if isNewer then dep.build else meta.buildNumber; # lastUpdated = if isNewer then updated else meta.lastUpdated; # versions = meta.versions or [] ++ [{ # classifier = id.classifier or ""; # extension = id.extension; # value = "${removeSuffix "-SNAPSHOT" id.version}-${dep.timestamp}-${toString dep.build}"; # updated = updated; # }]; # } # ) # { # timestamp = ""; # buildNumber = -1; # lastUpdated = ""; # } # (dep: "${replaceStrings ["."] ["/"] dep.id.group}/${dep.id.name}/${dep.id.version}/maven-metadata.xml") # snapshotDeps; # mkSnapshotVersion = version: '' # # ${optionalString (version.classifier != "") "${version.classifier}"} # ${version.extension} # ${version.value} # ${optionalString (version.updated != "") "${version.updated}"} # # ''; # in # attrValues (mapAttrs (path: meta: # with meta; writeTextDir path '' # # # ${groupId} # ${artifactId} # ${version} # # # ${optionalString (timestamp != "") "${timestamp}"} # ${optionalString (buildNumber != -1) "${toString buildNumber}"} # # ${optionalString (lastUpdated != "") "${lastUpdated}"} # # ${concatMapStringsSep "\n " mkSnapshotVersion versions} # # # # '' # ) modules); mkRepo = name: deps: symlinkJoin { name = "${name}-gradle-env"; # paths = map mkDep deps ++ mkModuleMetadata deps ++ mkSnapshotMetadata deps; paths = mapAttrsToList mkModule deps; }; mkInitScript = projectSpec: gradle: let repos = mapAttrs (mkRepo projectSpec.name) projectSpec.dependencies; hasDependencies = mapAttrs (type: deps: deps != []) projectSpec.dependencies; inSettings = pred: script: optionalString pred ( if versionAtLeast gradle.version "6.0" then '' gradle.beforeSettings { ${script} } '' else '' gradle.settingsEvaluated { ${script} } '' ); in assert (assertMsg (hasDependencies.settings -> versionAtLeast gradle.version "6.0") '' Project `${projectSpec.name}' has settings script dependencies, such as settings plugins, which are not supported by gradle2nix for Gradle versions prior to 6.0. Potential remedies: - Pass `--gradle-version=' to the gradle2nix command. - Patch the `settings.gradle[.kts]' file to remove script dependencies. ''); writeText "init.gradle" '' static def offlineRepo(RepositoryHandler repositories, String env, String path) { repositories.clear() repositories.maven { name "Nix''${env.capitalize()}Offline" url path metadataSources { it.gradleMetadata() it.mavenPom() it. } } repositories.ivy { name "Nix''${env.capitalize()}IvyOffline" url path layout "maven" metadataSources { it.gradleMetadata() it.ivyDescriptor() } } } ${inSettings (hasDependencies.settings && (versionAtLeast gradle.version "6.0")) '' offlineRepo(it.buildscript.repositories, "settings", "${repos.settings}") ''} ${inSettings (hasDependencies.plugin) '' offlineRepo(it.pluginManagement.repositories, "plugin", "${repos.plugin}") ''} ${optionalString (hasDependencies.buildscript) '' gradle.projectsLoaded { allprojects { buildscript { offlineRepo(repositories, "buildscript", "${repos.buildscript}") } } } ''} ${optionalString (hasDependencies.project) ( if versionAtLeast gradle.version "6.8" then '' gradle.beforeSettings { it.dependencyResolutionManagement { offlineRepo(repositories, "project", "${repos.project}") repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS) } } '' else '' gradle.projectsLoaded { allprojects { offlineRepo(repositories, "project", "${repos.project}") } } '' )} ${extraInit} ''; mkGradle = gradleSpec: gradleGen.gradleGen { inherit (gradleSpec) nativeVersion; name = "gradle-${gradleSpec.version}-${gradleSpec.type}"; src = fetchurl { inherit (gradleSpec) url sha256; }; } // { inherit (gradleSpec) version; }; mkProjectEnv = projectSpec: rec { inherit (projectSpec) name path version; gradle = args.gradlePackage or mkGradle projectSpec.gradle; initScript = mkInitScript projectSpec gradle; }; gradleEnv = mapAttrs (_: p: mkProjectEnv p) (fromJSON (readFile envSpec)); projectEnv = gradleEnv.""; pname = args.pname or projectEnv.name; version = args.version or projectEnv.version; buildProject = env: flags: '' cp ${env.initScript} "$GRADLE_USER_HOME/init.d" gradle --offline --no-daemon --no-build-cache \ --info --full-stacktrace --warning-mode=all \ ${optionalString enableParallelBuilding "--parallel"} \ ${optionalString enableDebug "-Dorg.gradle.debug=true"} \ ${optionalString (buildJdk != null) "-Dorg.gradle.java.home=${buildJdk.home}"} \ --init-script ${env.initScript} \ ${optionalString (env.path != "") ''-p "${env.path}"''} \ ${concatStringsSep " " flags} ''; buildIncludedProjects = concatStringsSep "\n" (mapAttrsToList (_: env: buildProject env [ "build" ]) (removeAttrs gradleEnv [ "" ])); buildRootProject = buildProject projectEnv gradleFlags; # in stdenv.mkDerivation (args // { # inherit pname version; # nativeBuildInputs = (args.nativeBuildInputs or []) ++ [ projectEnv.gradle ]; # buildPhase = args.buildPhase or '' # runHook preBuild # ( # set -eux # # Work around https://github.com/gradle/gradle/issues/1055 # TMPHOME="$(mktemp -d)" # mkdir -p "$TMPHOME/init.d" # export GRADLE_USER_HOME="$TMPHOME" # ${buildIncludedProjects} # ${buildRootProject} # ) # runHook postBuild # ''; # dontStrip = true; # }) in mkRepo pname (fromJSON (readFile envSpec))