diff --git a/app/src/dist/share/gradle-env.nix b/app/src/dist/share/gradle-env.nix index e1ca199..9c7f708 100644 --- a/app/src/dist/share/gradle-env.nix +++ b/app/src/dist/share/gradle-env.nix @@ -32,12 +32,12 @@ let inherit (builtins) - filter sort replaceStrings attrValues match fromJSON - concatStringsSep; + attrValues concatStringsSep filter fromJSON match replaceStrings sort; inherit (stdenv.lib) - versionOlder unique mapAttrs mapAttrsToList last concatMapStringsSep - removeSuffix optionalString groupBy' readFile hasSuffix; + assertMsg concatMapStringsSep groupBy' hasSuffix last mapAttrs + mapAttrsToList optionalString readFile removeSuffix unique versionAtLeast + versionOlder; mkDep = depSpec: stdenv.mkDerivation { inherit (depSpec) name; @@ -176,10 +176,20 @@ let paths = map mkDep deps ++ mkModuleMetadata deps ++ mkSnapshotMetadata deps; }; - mkInitScript = projectSpec: + mkInitScript = projectSpec: gradle: let repos = mapAttrs (mkRepo projectSpec.name) projectSpec.dependencies; + hasDependencies = mapAttrs (type: deps: deps != []) projectSpec.dependencies; 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() @@ -204,18 +214,35 @@ let } } - gradle.settingsEvaluated { - offlineRepo(it.pluginManagement.repositories, "plugin", "${repos.plugin}") - } + ${optionalString (hasDependencies.settings && (versionAtLeast gradle.version "6.0")) '' + gradle.beforeSettings { + offlineRepo(it.buildscript.repositories, "settings", "${repos.settings}") + } + ''} - gradle.projectsLoaded { - allprojects { - buildscript { - offlineRepo(repositories, "buildscript", "${repos.buildscript}") - } - offlineRepo(repositories, "project", "${repos.project}") - } - } + ${optionalString (hasDependencies.plugin) '' + gradle.settingsEvaluated { + offlineRepo(it.pluginManagement.repositories, "plugin", "${repos.plugin}") + } + ''} + + ${optionalString (hasDependencies.buildscript) '' + gradle.projectsLoaded { + allprojects { + buildscript { + offlineRepo(repositories, "buildscript", "${repos.buildscript}") + } + } + } + ''} + + ${optionalString (hasDependencies.project) '' + gradle.projectsLoaded { + allprojects { + offlineRepo(repositories, "project", "${repos.project}") + } + } + ''} ''; mkGradle = gradleSpec: @@ -227,12 +254,14 @@ let src = fetchurl { inherit (gradleSpec) url sha256; }; + } // { + inherit (gradleSpec) version; }; - mkProjectEnv = projectSpec: { + mkProjectEnv = projectSpec: rec { inherit (projectSpec) name path version; - initScript = mkInitScript projectSpec; gradle = args.gradlePackage or mkGradle projectSpec.gradle; + initScript = mkInitScript projectSpec gradle; }; gradleEnv = mapAttrs diff --git a/app/src/main/kotlin/org/nixos/gradle2nix/Env.kt b/app/src/main/kotlin/org/nixos/gradle2nix/Env.kt index 18ef9e0..6c4135a 100644 --- a/app/src/main/kotlin/org/nixos/gradle2nix/Env.kt +++ b/app/src/main/kotlin/org/nixos/gradle2nix/Env.kt @@ -19,6 +19,7 @@ fun buildEnv(builds: Map): Map = path = path, gradle = build.gradle, dependencies = mapOf( + "settings" to build.settingsDependencies, "plugin" to build.pluginDependencies, "buildscript" to build.rootProject.collectDependencies(DefaultProject::buildscriptDependencies), "project" to build.rootProject.collectDependencies(DefaultProject::projectDependencies) diff --git a/fixtures/integration/settings-buildscript/groovy/build.gradle b/fixtures/integration/settings-buildscript/groovy/build.gradle new file mode 100644 index 0000000..db4a9a2 --- /dev/null +++ b/fixtures/integration/settings-buildscript/groovy/build.gradle @@ -0,0 +1 @@ +version = '1.1.0' \ No newline at end of file diff --git a/fixtures/integration/settings-buildscript/groovy/default.nix b/fixtures/integration/settings-buildscript/groovy/default.nix new file mode 100644 index 0000000..292d035 --- /dev/null +++ b/fixtures/integration/settings-buildscript/groovy/default.nix @@ -0,0 +1,9 @@ +with (import {}); +let + buildGradle = callPackage ./gradle-env.nix {}; +in + buildGradle { + envSpec = ./gradle-env.json; + src = ./.; + gradleFlags = [ "tasks" ]; + } \ No newline at end of file diff --git a/fixtures/integration/settings-buildscript/groovy/gradle-env.json b/fixtures/integration/settings-buildscript/groovy/gradle-env.json new file mode 100644 index 0000000..2f884e0 --- /dev/null +++ b/fixtures/integration/settings-buildscript/groovy/gradle-env.json @@ -0,0 +1,456 @@ +{ + "": { + "name": "settings-buildscript", + "version": "1.1.0", + "path": "", + "gradle": { + "version": "5.0", + "type": "bin", + "url": "https://services.gradle.org/distributions/gradle-5.0-bin.zip", + "sha256": "6157ac9f3410bc63644625b3b3e9e96c963afd7910ae0697792db57813ee79a6", + "nativeVersion": "0.14" + }, + "dependencies": { + "settings": [ + { + "id": { + "group": "com.googlecode.javaewah", + "name": "JavaEWAH", + "version": "1.1.6", + "type": "jar", + "extension": "jar" + }, + "name": "JavaEWAH-1.1.6.jar", + "path": "com/googlecode/javaewah/JavaEWAH/1.1.6", + "urls": [ + "https://plugins.gradle.org/m2/com/googlecode/javaewah/JavaEWAH/1.1.6/JavaEWAH-1.1.6.jar" + ], + "sha256": "f78d44a1e3877f1ce748b4a85df5171e5e8e9a5c3c6f63bb9003db6f84cce952" + }, + { + "id": { + "group": "com.googlecode.javaewah", + "name": "JavaEWAH", + "version": "1.1.6", + "type": "pom", + "extension": "pom" + }, + "name": "JavaEWAH-1.1.6.pom", + "path": "com/googlecode/javaewah/JavaEWAH/1.1.6", + "urls": [ + "https://plugins.gradle.org/m2/com/googlecode/javaewah/JavaEWAH/1.1.6/JavaEWAH-1.1.6.pom" + ], + "sha256": "7f4ff919b1ee17bf3776e058a3f20e6173db23a5e44cf2d107ec7570c186abf0" + }, + { + "id": { + "group": "com.jcraft", + "name": "jsch", + "version": "0.1.54", + "type": "jar", + "extension": "jar" + }, + "name": "jsch-0.1.54.jar", + "path": "com/jcraft/jsch/0.1.54", + "urls": [ + "https://plugins.gradle.org/m2/com/jcraft/jsch/0.1.54/jsch-0.1.54.jar" + ], + "sha256": "92eb273a3316762478fdd4fe03a0ce1842c56f496c9c12fe1235db80450e1fdb" + }, + { + "id": { + "group": "com.jcraft", + "name": "jsch", + "version": "0.1.54", + "type": "pom", + "extension": "pom" + }, + "name": "jsch-0.1.54.pom", + "path": "com/jcraft/jsch/0.1.54", + "urls": [ + "https://plugins.gradle.org/m2/com/jcraft/jsch/0.1.54/jsch-0.1.54.pom" + ], + "sha256": "ab8f512039be7f6ae20e18e743b4a9d8a20958494431917da58ae5aaef8a3478" + }, + { + "id": { + "group": "commons-codec", + "name": "commons-codec", + "version": "1.6", + "type": "jar", + "extension": "jar" + }, + "name": "commons-codec-1.6.jar", + "path": "commons-codec/commons-codec/1.6", + "urls": [ + "https://plugins.gradle.org/m2/commons-codec/commons-codec/1.6/commons-codec-1.6.jar" + ], + "sha256": "54b34e941b8e1414bd3e40d736efd3481772dc26db3296f6aa45cec9f6203d86" + }, + { + "id": { + "group": "commons-codec", + "name": "commons-codec", + "version": "1.6", + "type": "pom", + "extension": "pom" + }, + "name": "commons-codec-1.6.pom", + "path": "commons-codec/commons-codec/1.6", + "urls": [ + "https://plugins.gradle.org/m2/commons-codec/commons-codec/1.6/commons-codec-1.6.pom" + ], + "sha256": "a06e35d3fff3a6b813d94894ebf3e498f9540c864c5b39ae783907e3a6c72889" + }, + { + "id": { + "group": "commons-logging", + "name": "commons-logging", + "version": "1.1.3", + "type": "jar", + "extension": "jar" + }, + "name": "commons-logging-1.1.3.jar", + "path": "commons-logging/commons-logging/1.1.3", + "urls": [ + "https://plugins.gradle.org/m2/commons-logging/commons-logging/1.1.3/commons-logging-1.1.3.jar" + ], + "sha256": "70903f6fc82e9908c8da9f20443f61d90f0870a312642991fe8462a0b9391784" + }, + { + "id": { + "group": "commons-logging", + "name": "commons-logging", + "version": "1.1.3", + "type": "pom", + "extension": "pom" + }, + "name": "commons-logging-1.1.3.pom", + "path": "commons-logging/commons-logging/1.1.3", + "urls": [ + "https://plugins.gradle.org/m2/commons-logging/commons-logging/1.1.3/commons-logging-1.1.3.pom" + ], + "sha256": "3250ac3ac6bd60ed0631f5cd0335032b2993d63e405a6ae0555d27a7e4865849" + }, + { + "id": { + "group": "gradle.plugin.net.vivin", + "name": "gradle-semantic-build-versioning", + "version": "4.0.0", + "type": "jar", + "extension": "jar" + }, + "name": "gradle-semantic-build-versioning-4.0.0.jar", + "path": "gradle/plugin/net/vivin/gradle-semantic-build-versioning/4.0.0", + "urls": [ + "https://plugins.gradle.org/m2/gradle/plugin/net/vivin/gradle-semantic-build-versioning/4.0.0/gradle-semantic-build-versioning-4.0.0.jar" + ], + "sha256": "5138e67ce8e019437800b93c9f6f9d0fcbebefadc96fbc4ebc0975c99a261ef8" + }, + { + "id": { + "group": "gradle.plugin.net.vivin", + "name": "gradle-semantic-build-versioning", + "version": "4.0.0", + "type": "pom", + "extension": "pom" + }, + "name": "gradle-semantic-build-versioning-4.0.0.pom", + "path": "gradle/plugin/net/vivin/gradle-semantic-build-versioning/4.0.0", + "urls": [ + "https://plugins.gradle.org/m2/gradle/plugin/net/vivin/gradle-semantic-build-versioning/4.0.0/gradle-semantic-build-versioning-4.0.0.pom" + ], + "sha256": "4f2828741607ed102d95eb5f189d496c7840ed463acea89f1e51a60567714ef7" + }, + { + "id": { + "group": "org.apache", + "name": "apache", + "version": "9", + "type": "pom", + "extension": "pom" + }, + "name": "apache-9.pom", + "path": "org/apache/apache/9", + "urls": [ + "https://plugins.gradle.org/m2/org/apache/apache/9/apache-9.pom" + ], + "sha256": "4946e60a547c8eda69f3bc23c5b6f0dadcf8469ea49b1d1da7de34aecfcf18dd" + }, + { + "id": { + "group": "org.apache", + "name": "apache", + "version": "13", + "type": "pom", + "extension": "pom" + }, + "name": "apache-13.pom", + "path": "org/apache/apache/13", + "urls": [ + "https://plugins.gradle.org/m2/org/apache/apache/13/apache-13.pom" + ], + "sha256": "ff513db0361fd41237bef4784968bc15aae478d4ec0a9496f811072ccaf3841d" + }, + { + "id": { + "group": "org.apache.commons", + "name": "commons-parent", + "version": "22", + "type": "pom", + "extension": "pom" + }, + "name": "commons-parent-22.pom", + "path": "org/apache/commons/commons-parent/22", + "urls": [ + "https://plugins.gradle.org/m2/org/apache/commons/commons-parent/22/commons-parent-22.pom" + ], + "sha256": "fb8c5e55e30a7addb4ff210858a0e8d2494ed6757bbe19012da99d51586c3cbb" + }, + { + "id": { + "group": "org.apache.commons", + "name": "commons-parent", + "version": "28", + "type": "pom", + "extension": "pom" + }, + "name": "commons-parent-28.pom", + "path": "org/apache/commons/commons-parent/28", + "urls": [ + "https://plugins.gradle.org/m2/org/apache/commons/commons-parent/28/commons-parent-28.pom" + ], + "sha256": "14733a68e8b120b69de60cd96d222146dcf32f03c1c6cc6a750b1269bafe86c7" + }, + { + "id": { + "group": "org.apache.httpcomponents", + "name": "httpclient", + "version": "4.3.6", + "type": "jar", + "extension": "jar" + }, + "name": "httpclient-4.3.6.jar", + "path": "org/apache/httpcomponents/httpclient/4.3.6", + "urls": [ + "https://plugins.gradle.org/m2/org/apache/httpcomponents/httpclient/4.3.6/httpclient-4.3.6.jar" + ], + "sha256": "79838d9eaef73d4f852c63a480830c3a2d4b590f0ab3ae815a489463e4714004" + }, + { + "id": { + "group": "org.apache.httpcomponents", + "name": "httpclient", + "version": "4.3.6", + "type": "pom", + "extension": "pom" + }, + "name": "httpclient-4.3.6.pom", + "path": "org/apache/httpcomponents/httpclient/4.3.6", + "urls": [ + "https://plugins.gradle.org/m2/org/apache/httpcomponents/httpclient/4.3.6/httpclient-4.3.6.pom" + ], + "sha256": "d02634f6131e914961c02aa836711ebac72704b27e26c5bd223bbad89b1b64c3" + }, + { + "id": { + "group": "org.apache.httpcomponents", + "name": "httpcomponents-client", + "version": "4.3.6", + "type": "pom", + "extension": "pom" + }, + "name": "httpcomponents-client-4.3.6.pom", + "path": "org/apache/httpcomponents/httpcomponents-client/4.3.6", + "urls": [ + "https://plugins.gradle.org/m2/org/apache/httpcomponents/httpcomponents-client/4.3.6/httpcomponents-client-4.3.6.pom" + ], + "sha256": "4ada2827b496339826891c7c81dceba647029de6fc1888b16b3cab5650abcc63" + }, + { + "id": { + "group": "org.apache.httpcomponents", + "name": "httpcomponents-core", + "version": "4.3.3", + "type": "pom", + "extension": "pom" + }, + "name": "httpcomponents-core-4.3.3.pom", + "path": "org/apache/httpcomponents/httpcomponents-core/4.3.3", + "urls": [ + "https://plugins.gradle.org/m2/org/apache/httpcomponents/httpcomponents-core/4.3.3/httpcomponents-core-4.3.3.pom" + ], + "sha256": "c16e2fc0d49ba7a02cef5b5e2600585a9f673553328a6f9e58f24296df1dd031" + }, + { + "id": { + "group": "org.apache.httpcomponents", + "name": "httpcore", + "version": "4.3.3", + "type": "jar", + "extension": "jar" + }, + "name": "httpcore-4.3.3.jar", + "path": "org/apache/httpcomponents/httpcore/4.3.3", + "urls": [ + "https://plugins.gradle.org/m2/org/apache/httpcomponents/httpcore/4.3.3/httpcore-4.3.3.jar" + ], + "sha256": "5285de80af1651c489313b91a9f40c65a4cdcb6b3bde716fcc028d16869a5a93" + }, + { + "id": { + "group": "org.apache.httpcomponents", + "name": "httpcore", + "version": "4.3.3", + "type": "pom", + "extension": "pom" + }, + "name": "httpcore-4.3.3.pom", + "path": "org/apache/httpcomponents/httpcore/4.3.3", + "urls": [ + "https://plugins.gradle.org/m2/org/apache/httpcomponents/httpcore/4.3.3/httpcore-4.3.3.pom" + ], + "sha256": "b427f7cf67c75a4e3f9e2108d35bf45303573c145ec778fcadcffacef17a1264" + }, + { + "id": { + "group": "org.apache.httpcomponents", + "name": "project", + "version": "7", + "type": "pom", + "extension": "pom" + }, + "name": "project-7.pom", + "path": "org/apache/httpcomponents/project/7", + "urls": [ + "https://plugins.gradle.org/m2/org/apache/httpcomponents/project/7/project-7.pom" + ], + "sha256": "3d6eba428555a558de046b5d76eacc1f5a54b4f5f20b84d636ed7aff18aa48c3" + }, + { + "id": { + "group": "org.eclipse.jgit", + "name": "org.eclipse.jgit", + "version": "4.8.0.201706111038-r", + "type": "jar", + "extension": "jar" + }, + "name": "org.eclipse.jgit-4.8.0.201706111038-r.jar", + "path": "org/eclipse/jgit/org.eclipse.jgit/4.8.0.201706111038-r", + "urls": [ + "https://plugins.gradle.org/m2/org/eclipse/jgit/org.eclipse.jgit/4.8.0.201706111038-r/org.eclipse.jgit-4.8.0.201706111038-r.jar" + ], + "sha256": "49d912e8d5cce0dd08dca3d390189db8692a8f7e3363cdbbe182581462000aba" + }, + { + "id": { + "group": "org.eclipse.jgit", + "name": "org.eclipse.jgit", + "version": "4.8.0.201706111038-r", + "type": "pom", + "extension": "pom" + }, + "name": "org.eclipse.jgit-4.8.0.201706111038-r.pom", + "path": "org/eclipse/jgit/org.eclipse.jgit/4.8.0.201706111038-r", + "urls": [ + "https://plugins.gradle.org/m2/org/eclipse/jgit/org.eclipse.jgit/4.8.0.201706111038-r/org.eclipse.jgit-4.8.0.201706111038-r.pom" + ], + "sha256": "a556a9f5adfc6af49b2a12e72dc3cd7e43db8fdc216c0f35885972a2f5ada27d" + }, + { + "id": { + "group": "org.eclipse.jgit", + "name": "org.eclipse.jgit-parent", + "version": "4.8.0.201706111038-r", + "type": "pom", + "extension": "pom" + }, + "name": "org.eclipse.jgit-parent-4.8.0.201706111038-r.pom", + "path": "org/eclipse/jgit/org.eclipse.jgit-parent/4.8.0.201706111038-r", + "urls": [ + "https://plugins.gradle.org/m2/org/eclipse/jgit/org.eclipse.jgit-parent/4.8.0.201706111038-r/org.eclipse.jgit-parent-4.8.0.201706111038-r.pom" + ], + "sha256": "396a4cc894206873ff107d066a996252b0b47f585b88cf57fc3b31e93d492878" + }, + { + "id": { + "group": "org.slf4j", + "name": "slf4j-api", + "version": "1.7.2", + "type": "jar", + "extension": "jar" + }, + "name": "slf4j-api-1.7.2.jar", + "path": "org/slf4j/slf4j-api/1.7.2", + "urls": [ + "https://plugins.gradle.org/m2/org/slf4j/slf4j-api/1.7.2/slf4j-api-1.7.2.jar" + ], + "sha256": "3bae789b401333b2a1d1603b7fa573e19908628191707203f6eb708cdee2c052" + }, + { + "id": { + "group": "org.slf4j", + "name": "slf4j-api", + "version": "1.7.2", + "type": "pom", + "extension": "pom" + }, + "name": "slf4j-api-1.7.2.pom", + "path": "org/slf4j/slf4j-api/1.7.2", + "urls": [ + "https://plugins.gradle.org/m2/org/slf4j/slf4j-api/1.7.2/slf4j-api-1.7.2.pom" + ], + "sha256": "2eaca71afe0a1516f4abd8e9ff907838d268f38c81c3a542cce8d7f3b87c5d4c" + }, + { + "id": { + "group": "org.slf4j", + "name": "slf4j-parent", + "version": "1.7.2", + "type": "pom", + "extension": "pom" + }, + "name": "slf4j-parent-1.7.2.pom", + "path": "org/slf4j/slf4j-parent/1.7.2", + "urls": [ + "https://plugins.gradle.org/m2/org/slf4j/slf4j-parent/1.7.2/slf4j-parent-1.7.2.pom" + ], + "sha256": "1d8e084a6f2384ade42685332b52a0ece090478641dc14c0fa8c52e1e2984425" + }, + { + "id": { + "group": "org.sonatype.oss", + "name": "oss-parent", + "version": "5", + "type": "pom", + "extension": "pom" + }, + "name": "oss-parent-5.pom", + "path": "org/sonatype/oss/oss-parent/5", + "urls": [ + "https://plugins.gradle.org/m2/org/sonatype/oss/oss-parent/5/oss-parent-5.pom" + ], + "sha256": "1678d4120a585d8a630131aeec4c524d928398583b7eab616ee7d5a87f520d3d" + }, + { + "id": { + "group": "org.sonatype.oss", + "name": "oss-parent", + "version": "6", + "type": "pom", + "extension": "pom" + }, + "name": "oss-parent-6.pom", + "path": "org/sonatype/oss/oss-parent/6", + "urls": [ + "https://plugins.gradle.org/m2/org/sonatype/oss/oss-parent/6/oss-parent-6.pom" + ], + "sha256": "b4306d13e8f5392458a1b30866f1cff161b3d2e6999a88d059eea3932c8a8499" + } + ], + "plugin": [], + "buildscript": [], + "project": [] + } + } +} \ No newline at end of file diff --git a/fixtures/integration/settings-buildscript/groovy/gradle-env.nix b/fixtures/integration/settings-buildscript/groovy/gradle-env.nix new file mode 100644 index 0000000..9c7f708 --- /dev/null +++ b/fixtures/integration/settings-buildscript/groovy/gradle-env.nix @@ -0,0 +1,318 @@ +# 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 +# ''; +# } + +{ stdenv, buildEnv, fetchurl, gradleGen, writeText, writeTextDir }: + +{ envSpec +, pname ? null +, version ? null +, enableParallelBuilding ? true +, gradleFlags ? [ "build" ] +, gradlePackage ? null +, enableDebug ? false +, ... } @ args: + +let + inherit (builtins) + attrValues concatStringsSep filter fromJSON match replaceStrings sort; + + inherit (stdenv.lib) + assertMsg concatMapStringsSep groupBy' hasSuffix last mapAttrs + mapAttrsToList optionalString readFile removeSuffix unique versionAtLeast + versionOlder; + + mkDep = depSpec: stdenv.mkDerivation { + inherit (depSpec) name; + + src = fetchurl { + inherit (depSpec) urls sha256; + }; + + phases = "installPhase"; + + installPhase = '' + mkdir -p $out/${depSpec.path} + ln -s $src $out/${depSpec.path}/${depSpec.name} + ''; + }; + + mkModuleMetadata = deps: + let + ids = filter + (id: id.type == "pom") + (map (dep: dep.id) deps); + + 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 = project: type: deps: buildEnv { + name = "${project}-gradle-${type}-env"; + paths = map mkDep deps ++ mkModuleMetadata deps ++ mkSnapshotMetadata deps; + }; + + mkInitScript = projectSpec: gradle: + let + repos = mapAttrs (mkRepo projectSpec.name) projectSpec.dependencies; + hasDependencies = mapAttrs (type: deps: deps != []) projectSpec.dependencies; + 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()}MavenOffline" + url path + metadataSources { + it.gradleMetadata() + it.mavenPom() + it.artifact() + } + } + repositories.ivy { + name "Nix''${env.capitalize()}IvyOffline" + url path + layout "maven" + metadataSources { + it.gradleMetadata() + it.ivyDescriptor() + it.artifact() + } + } + } + + ${optionalString (hasDependencies.settings && (versionAtLeast gradle.version "6.0")) '' + gradle.beforeSettings { + offlineRepo(it.buildscript.repositories, "settings", "${repos.settings}") + } + ''} + + ${optionalString (hasDependencies.plugin) '' + gradle.settingsEvaluated { + offlineRepo(it.pluginManagement.repositories, "plugin", "${repos.plugin}") + } + ''} + + ${optionalString (hasDependencies.buildscript) '' + gradle.projectsLoaded { + allprojects { + buildscript { + offlineRepo(repositories, "buildscript", "${repos.buildscript}") + } + } + } + ''} + + ${optionalString (hasDependencies.project) '' + gradle.projectsLoaded { + allprojects { + offlineRepo(repositories, "project", "${repos.project}") + } + } + ''} + ''; + + 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: '' + gradle --offline --no-daemon --no-build-cache \ + --info --full-stacktrace --warning-mode=all \ + ${optionalString enableParallelBuilding "--parallel"} \ + ${optionalString enableDebug "-Dorg.gradle.debug=true"} \ + --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 -x + + # use the init script here + TMPHOME=$(mktemp -d) + mkdir -p $TMPHOME/init.d + cp ${projectEnv.initScript} $TMPHOME/init.d + + export "GRADLE_USER_HOME=$TMPHOME" + ${buildIncludedProjects} + ${buildRootProject} + ) + + runHook postBuild + ''; + + dontStrip = true; +}) diff --git a/fixtures/integration/settings-buildscript/groovy/semantic-build-versioning.gradle b/fixtures/integration/settings-buildscript/groovy/semantic-build-versioning.gradle new file mode 100644 index 0000000..4a81ba9 --- /dev/null +++ b/fixtures/integration/settings-buildscript/groovy/semantic-build-versioning.gradle @@ -0,0 +1 @@ +startingVersion = '1.0.0' \ No newline at end of file diff --git a/fixtures/integration/settings-buildscript/groovy/settings.gradle b/fixtures/integration/settings-buildscript/groovy/settings.gradle new file mode 100644 index 0000000..1dfc8cd --- /dev/null +++ b/fixtures/integration/settings-buildscript/groovy/settings.gradle @@ -0,0 +1,14 @@ +buildscript { + repositories { + maven { + url 'https://plugins.gradle.org/m2/' + } + } + dependencies { + classpath 'gradle.plugin.net.vivin:gradle-semantic-build-versioning:4.0.0' + } +} + +rootProject.name = "settings-buildscript" + +//apply plugin: 'net.vivin.gradle-semantic-build-versioning' \ No newline at end of file diff --git a/fixtures/repositories/m2/org/apache/test/1.0.0/test-1.0.0.jar b/fixtures/repositories/m2/org/apache/test/1.0.0/test-1.0.0.jar new file mode 100644 index 0000000..fde9ffe Binary files /dev/null and b/fixtures/repositories/m2/org/apache/test/1.0.0/test-1.0.0.jar differ diff --git a/fixtures/repositories/m2/org/apache/test/1.0.0/test-1.0.0.pom b/fixtures/repositories/m2/org/apache/test/1.0.0/test-1.0.0.pom new file mode 100644 index 0000000..87c66fa --- /dev/null +++ b/fixtures/repositories/m2/org/apache/test/1.0.0/test-1.0.0.pom @@ -0,0 +1,14 @@ + + + 4.0.0 + + org.apache + test + 1.0.0 + + test + + + test + + diff --git a/fixtures/repositories/m2/org/hdrhistogram/HdrHistogram/2.1.12/HdrHistogram-2.1.12.jar.sha1 b/fixtures/repositories/m2/org/hdrhistogram/HdrHistogram/2.1.12/HdrHistogram-2.1.12.jar.sha1 deleted file mode 100644 index 9d20fa0..0000000 --- a/fixtures/repositories/m2/org/hdrhistogram/HdrHistogram/2.1.12/HdrHistogram-2.1.12.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -6eb7552156e0d517ae80cc2247be1427c8d90452 \ No newline at end of file diff --git a/fixtures/repositories/m2/org/hdrhistogram/HdrHistogram/2.1.12/HdrHistogram-2.1.12.pom.sha1 b/fixtures/repositories/m2/org/hdrhistogram/HdrHistogram/2.1.12/HdrHistogram-2.1.12.pom.sha1 deleted file mode 100644 index 0c5ef57..0000000 --- a/fixtures/repositories/m2/org/hdrhistogram/HdrHistogram/2.1.12/HdrHistogram-2.1.12.pom.sha1 +++ /dev/null @@ -1 +0,0 @@ -9797702ee3e52e4be6bfbbc9fd20ac5447e7a541 \ No newline at end of file diff --git a/fixtures/settings/buildscript/groovy/settings.gradle b/fixtures/settings/buildscript/groovy/settings.gradle new file mode 100644 index 0000000..972356a --- /dev/null +++ b/fixtures/settings/buildscript/groovy/settings.gradle @@ -0,0 +1,10 @@ +buildscript { + repositories { + maven { + url 'http://localhost:9999/' + } + } + dependencies { + classpath 'org.apache:test:1.0.0' + } +} diff --git a/model/src/main/kotlin/org/nixos/gradle2nix/Impl.kt b/model/src/main/kotlin/org/nixos/gradle2nix/Impl.kt index 8c76fef..c4e4d17 100644 --- a/model/src/main/kotlin/org/nixos/gradle2nix/Impl.kt +++ b/model/src/main/kotlin/org/nixos/gradle2nix/Impl.kt @@ -8,12 +8,14 @@ import java.lang.IllegalArgumentException @JsonClass(generateAdapter = true) data class DefaultBuild( override val gradle: DefaultGradle, + override val settingsDependencies: List, override val pluginDependencies: List, override val rootProject: DefaultProject, override val includedBuilds: List ) : Build, Serializable { constructor(model: Build) : this( DefaultGradle(model.gradle), + model.settingsDependencies.map(::DefaultArtifact), model.pluginDependencies.map(::DefaultArtifact), DefaultProject(model.rootProject), model.includedBuilds.map(::DefaultIncludedBuild) diff --git a/model/src/main/kotlin/org/nixos/gradle2nix/Model.kt b/model/src/main/kotlin/org/nixos/gradle2nix/Model.kt index cf7b57f..153c051 100644 --- a/model/src/main/kotlin/org/nixos/gradle2nix/Model.kt +++ b/model/src/main/kotlin/org/nixos/gradle2nix/Model.kt @@ -2,6 +2,7 @@ package org.nixos.gradle2nix interface Build { val gradle: Gradle + val settingsDependencies: List val pluginDependencies: List val rootProject: Project val includedBuilds: List diff --git a/plugin/src/compatTest/kotlin/org/nixos/gradle2nix/PluginTest.kt b/plugin/src/compatTest/kotlin/org/nixos/gradle2nix/PluginTest.kt index 010a9e9..47d5b6f 100644 --- a/plugin/src/compatTest/kotlin/org/nixos/gradle2nix/PluginTest.kt +++ b/plugin/src/compatTest/kotlin/org/nixos/gradle2nix/PluginTest.kt @@ -1,10 +1,13 @@ package org.nixos.gradle2nix import dev.minutest.Tests +import dev.minutest.experimental.FOCUS +import dev.minutest.experimental.minus import dev.minutest.junit.JUnit5Minutests import dev.minutest.rootContext import strikt.api.expectThat import strikt.assertions.contains +import strikt.assertions.containsExactly class PluginTest : JUnit5Minutests { @Tests diff --git a/plugin/src/compatTest/kotlin/org/nixos/gradle2nix/SettingsTest.kt b/plugin/src/compatTest/kotlin/org/nixos/gradle2nix/SettingsTest.kt new file mode 100644 index 0000000..4822a8e --- /dev/null +++ b/plugin/src/compatTest/kotlin/org/nixos/gradle2nix/SettingsTest.kt @@ -0,0 +1,26 @@ +package org.nixos.gradle2nix + +import dev.minutest.Tests +import dev.minutest.junit.JUnit5Minutests +import dev.minutest.rootContext +import strikt.api.expectThat +import strikt.assertions.containsExactly + +class SettingsTest : JUnit5Minutests { + @Tests + fun tests() = rootContext("settings tests") { + withRepository("m2") { + withFixture("settings/buildscript") { + test("resolves settings plugin in buildscript classpath") { + expectThat(build()) { + get("settings dependencies") { settingsDependencies }.ids + .containsExactly( + "org.apache:test:1.0.0@jar", + "org.apache:test:1.0.0@pom" + ) + } + } + } + } + } +} \ No newline at end of file diff --git a/plugin/src/compatTest/kotlin/org/nixos/gradle2nix/TestUtil.kt b/plugin/src/compatTest/kotlin/org/nixos/gradle2nix/TestUtil.kt index 1fd762d..92db71f 100644 --- a/plugin/src/compatTest/kotlin/org/nixos/gradle2nix/TestUtil.kt +++ b/plugin/src/compatTest/kotlin/org/nixos/gradle2nix/TestUtil.kt @@ -168,29 +168,27 @@ fun ContextBuilder<*>.withRepository( fun ContextBuilder<*>.withFixture( name: String, block: TestContextBuilder<*, ProjectFixture>.() -> Unit -) { - derivedContext(name) { - val url = checkNotNull(Thread.currentThread().contextClassLoader.getResource(name)?.toURI()) { - "$name: No test fixture found" - } - val fixtureRoot = Paths.get(url).toFile().absoluteFile +) = derivedContext(name) { + val url = checkNotNull(Thread.currentThread().contextClassLoader.getResource(name)?.toURI()) { + "$name: No test fixture found" + } + val fixtureRoot = Paths.get(url).toFile().absoluteFile - deriveFixture { - TestFixture(name, fixtureRoot) - } + deriveFixture { + TestFixture(name, fixtureRoot) + } - val testRoots = fixtureRoot.listFiles()!! - .filter { it.isDirectory } - .map { it.absoluteFile } - .toList() + val testRoots = fixtureRoot.listFiles()!! + .filter { it.isDirectory } + .map { it.absoluteFile } + .toList() - testRoots.forEach { testRoot -> - derivedContext(testRoot.name) { - deriveFixture { ProjectFixture(this, testRoot) } - before { copy() } - after { close() } - block() - } + testRoots.forEach { testRoot -> + derivedContext(testRoot.name) { + deriveFixture { ProjectFixture(this, testRoot) } + before { copy() } + after { close() } + block() } } } diff --git a/plugin/src/main/kotlin/org/nixos/gradle2nix/ConfigurationResolver.kt b/plugin/src/main/kotlin/org/nixos/gradle2nix/ConfigurationResolver.kt index 8bcca14..e07ae9c 100644 --- a/plugin/src/main/kotlin/org/nixos/gradle2nix/ConfigurationResolver.kt +++ b/plugin/src/main/kotlin/org/nixos/gradle2nix/ConfigurationResolver.kt @@ -28,7 +28,8 @@ import org.gradle.util.GradleVersion enum class ConfigurationScope { BUILDSCRIPT, PLUGIN, - PROJECT + PROJECT, + SETTINGS } internal class ConfigurationResolverFactory( diff --git a/plugin/src/main/kotlin/org/nixos/gradle2nix/Gradle2NixPlugin.kt b/plugin/src/main/kotlin/org/nixos/gradle2nix/Gradle2NixPlugin.kt index d84a930..0523826 100644 --- a/plugin/src/main/kotlin/org/nixos/gradle2nix/Gradle2NixPlugin.kt +++ b/plugin/src/main/kotlin/org/nixos/gradle2nix/Gradle2NixPlugin.kt @@ -6,6 +6,7 @@ import org.gradle.api.Project import org.gradle.api.Task import org.gradle.api.artifacts.Configuration import org.gradle.api.artifacts.ProjectDependency +import org.gradle.api.internal.GradleInternal import org.gradle.api.invocation.Gradle import org.gradle.api.tasks.TaskContainer import org.gradle.api.tasks.wrapper.Wrapper @@ -85,6 +86,7 @@ private fun Project.buildModel( modelProperties: ModelProperties, pluginRequests: List ): DefaultBuild { + val settingsDependencies = settingsDependencies() val plugins = buildPlugins(pluginRequests) val subprojects = if (modelProperties.subprojects.isEmpty()) { @@ -99,6 +101,7 @@ private fun Project.buildModel( return DefaultBuild( gradle = buildGradle(), + settingsDependencies = settingsDependencies, pluginDependencies = plugins, rootProject = buildProject(modelProperties.configurations, subprojects, plugins), includedBuilds = includedBuilds() @@ -126,6 +129,31 @@ private fun Project.buildGradle(): DefaultGradle = ) } +private fun Project.settingsDependencies(): List { + val buildscript = (gradle as GradleInternal).settings.buildscript + + val resolverFactory = ConfigurationResolverFactory(this, ConfigurationScope.SETTINGS, buildscript.repositories) + val resolver = resolverFactory.create(buildscript.dependencies) + + logger.lifecycle(" Settings script") + + val dependencies = buildscript.configurations + .flatMap(resolver::resolve) + .distinct() + .sorted() + + if (resolver.unresolved.isNotEmpty()) { + logger.warn(buildString { + append(" Failed to resolve settings script dependencies:\n") + for (id in resolver.unresolved) { + append(" - $id\n") + } + }) + } + + return dependencies +} + private fun Project.buildPlugins(pluginRequests: List): List { return objects.newInstance(this).resolve(pluginRequests).distinct().sorted() }