3 Commits

Author SHA1 Message Date
Tad Fisher
b1b0ba14db Update README and add copyright license 2020-01-23 15:38:12 -08:00
Tad Fisher
823e43d592 Merge pull request #7 from tadfisher/custom-resolver
Custom dependency resolution
2020-01-23 10:27:57 -08:00
Tad Fisher
648be6bd07 Use custom dependency resolution
- Use Apache Ivy to resolve artifact URLs
- Update build model with full artifact IDs
- Generate Maven module metadata to support dynamic version constraints
- Resolve snapshot versions and generate snapshot metadata
- Add test fixtures and rewrite Gradle plugin tests
- Update dependencies
2020-01-23 10:01:38 -08:00
76 changed files with 6010 additions and 3063 deletions

20
COPYING Normal file
View File

@@ -0,0 +1,20 @@
Copyright © Tad Fisher and the gradle2nix contributors
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -1,12 +1,156 @@
* gradle2nix #+STARTUP: inlineimages
Application and Gradle plugin to generate Nix build scripts for Gradle-based [[./assets/gradle2nix.png]]
projects.
Generate [[https://nixos.org/nix/][Nix]] expressions which build [[https://gradle.org/][Gradle]]-based projects.
** Why?
Nix is an OS-agnostic package manager, a language-agnostic build
system, and a bespoke programming language. One of its unique features
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
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
someone else running the same function over the same inputs.
Gradle is not a functional build system. Most Gradle-based projects
will produce highly variable outputs depending on a host of impure
inputs, including:
- The JVM hosting the build
- The Gradle installation running the build
- Any usage of dynamic version constraints for dependencies
- SNAPSHOT dependencies
- 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.
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;
- Reduce or eliminate flakiness and maintenance headaches from CI/CD
pipelines
- Distribute a recipe which can reliably build a Gradle project in
repositories such as the [[https://nixos.org/nixpkgs/][Nix Package Collection]].
** Installation ** Installation
A [[./default.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") {}
#+end_src
If this expression is in, say, =gradle2nix.nix=, =gradle2nix= can be
built and placed in =.//result= with the following:
#+begin_example
nix build -f gradle2nix.nix
#+end_example
You can also use the following one-liners to build or install
=gradle2nix= in your user profile:
#+begin_example
# Build and place in ./result/
nix build -f "https://github.com/tadfisher/gradle2nix/archive/master.tar.gz"
# Build and install in the user profile
nix-env -if "https://github.com/tadfisher/gradle2nix/archive/master.tar.gz"
#+end_example
=gradle2nix= is not yet packaged in =nixpkgs= itself, but work is
[[https://github.com/NixOS/nixpkgs/pull/77422][in progress]].
** Usage ** Usage
#+begin_example
Usage: gradle2nix [OPTIONS] [PROJECT-DIR]
Options:
-g, --gradle-version VERSION Use a specific Gradle version
-a, --gradle-args ARGS Extra arguments to pass to Gradle
-c, --configuration NAME Add a configuration to resolve (default:
all configurations)
-i, --include DIR Add an additional project to include
-p, --project PATH Only resolve these subproject paths, e.g.
':', or ':sub:project' (default: all
projects)
-o, --out-dir DIR Path to write generated files (default:
PROJECT-DIR)
-e, --env FILENAME Prefix for environment files (.json and
.nix) (default: gradle-env)
-b, --build-src / -nb, --no-build-src
Include buildSrc project (default: true)
-q, --quiet Disable logging
-h, --help Show this message and exit
Arguments:
PROJECT-DIR Path to the project root (default: .)
#+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-env.json= and =gradle-env.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 [[./default.nix][default.nix]].
*** Specifying the Gradle version
By default, if the project has configured the Gradle wrapper, that
version will be detected and pinned; otherwise, the version of Gradle
installed on your system will be pinned. You can override this with
the =--gradle-version= argument, which also avoids the need to have
Gradle installed.
#+begin_example
gradle2nix -g 6.1
#+end_example
*** Multi-project builds
If you want to resolve only a subset of projects in a [[https://docs.gradle.org/current/userguide/intro_multi_project_builds.html][multi-project
build]], add the =--project= option for each project. For example, in a
project where you only want to build the subprojects =:app= and
=:widget=:
#+begin_example
gradle2nix -p :app -p :widget
#+end_example
Any [[https://docs.gradle.org/current/userguide/declaring_dependencies.html#sub:project_dependencies][project dependencies]] will be also be included when pinning
dependency artifacts.
** Contributing
Bug reports and feature requests are encouraged.
[[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 [[./plugin/src/compatTest/kotlin/org/nixos/gradle2nix][existing compatibility tests]] for
examples of testing with these fixtures.
** License ** License
=gradle2nix= is licensed under the [[./COPYING][MIT License]].

View File

@@ -6,9 +6,6 @@ plugins {
application application
} }
group = "org.nixos"
version = "1.0.0-SNAPSHOT"
dependencies { dependencies {
implementation(project(":model")) implementation(project(":model"))
implementation(kotlin("stdlib-jdk8")) implementation(kotlin("stdlib-jdk8"))

View File

@@ -1,16 +1,16 @@
# This is a Gradle generated file for dependency locking. # This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised. # Manual edits can break the build and are not advised.
# This file is expected to be part of source control. # This file is expected to be part of source control.
com.github.ajalt:clikt:2.2.0 com.github.ajalt:clikt:2.3.0
com.squareup.moshi:moshi-adapters:1.9.1 com.squareup.moshi:moshi-adapters:1.9.2
com.squareup.moshi:moshi-kotlin:1.9.1 com.squareup.moshi:moshi-kotlin:1.9.2
com.squareup.moshi:moshi:1.9.1 com.squareup.moshi:moshi:1.9.2
com.squareup.okio:okio:2.4.1 com.squareup.okio:okio:2.4.3
org.gradle:gradle-tooling-api:5.6.3 org.gradle:gradle-tooling-api:6.1
org.jetbrains.kotlin:kotlin-reflect:1.3.50 org.jetbrains.kotlin:kotlin-reflect:1.3.61
org.jetbrains.kotlin:kotlin-stdlib-common:1.3.50 org.jetbrains.kotlin:kotlin-stdlib-common:1.3.61
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.41 org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.61
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.41 org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.61
org.jetbrains.kotlin:kotlin-stdlib:1.3.50 org.jetbrains.kotlin:kotlin-stdlib:1.3.61
org.jetbrains:annotations:13.0 org.jetbrains:annotations:13.0
org.slf4j:slf4j-api:2.0.0-alpha1 org.slf4j:slf4j-api:2.0.0-alpha1

View File

@@ -1,17 +1,18 @@
# This is a Gradle generated file for dependency locking. # This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised. # Manual edits can break the build and are not advised.
# This file is expected to be part of source control. # This file is expected to be part of source control.
com.github.ajalt:clikt:2.2.0 com.github.ajalt:clikt:2.3.0
com.squareup.moshi:moshi-adapters:1.9.1 com.squareup.moshi:moshi-adapters:1.9.2
com.squareup.moshi:moshi-kotlin:1.9.1 com.squareup.moshi:moshi-kotlin:1.9.2
com.squareup.moshi:moshi:1.9.1 com.squareup.moshi:moshi:1.9.2
com.squareup.okio:okio:2.4.1 com.squareup.okio:okio:2.4.3
org.gradle:gradle-tooling-api:5.6.3 net.swiftzer.semver:semver:1.1.1
org.jetbrains.kotlin:kotlin-reflect:1.3.50 org.gradle:gradle-tooling-api:6.1
org.jetbrains.kotlin:kotlin-stdlib-common:1.3.50 org.jetbrains.kotlin:kotlin-reflect:1.3.61
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.41 org.jetbrains.kotlin:kotlin-stdlib-common:1.3.61
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.41 org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.61
org.jetbrains.kotlin:kotlin-stdlib:1.3.50 org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.61
org.jetbrains.kotlin:kotlin-stdlib:1.3.61
org.jetbrains:annotations:13.0 org.jetbrains:annotations:13.0
org.slf4j:slf4j-api:2.0.0-alpha1 org.slf4j:slf4j-api:2.0.0-alpha1
org.slf4j:slf4j-simple:2.0.0-alpha1 org.slf4j:slf4j-simple:2.0.0-alpha1

View File

@@ -1,16 +1,16 @@
# This is a Gradle generated file for dependency locking. # This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised. # Manual edits can break the build and are not advised.
# This file is expected to be part of source control. # This file is expected to be part of source control.
com.github.ajalt:clikt:2.2.0 com.github.ajalt:clikt:2.3.0
com.squareup.moshi:moshi-adapters:1.9.1 com.squareup.moshi:moshi-adapters:1.9.2
com.squareup.moshi:moshi-kotlin:1.9.1 com.squareup.moshi:moshi-kotlin:1.9.2
com.squareup.moshi:moshi:1.9.1 com.squareup.moshi:moshi:1.9.2
com.squareup.okio:okio:2.4.1 com.squareup.okio:okio:2.4.3
org.gradle:gradle-tooling-api:5.6.3 org.gradle:gradle-tooling-api:6.1
org.jetbrains.kotlin:kotlin-reflect:1.3.50 org.jetbrains.kotlin:kotlin-reflect:1.3.61
org.jetbrains.kotlin:kotlin-stdlib-common:1.3.50 org.jetbrains.kotlin:kotlin-stdlib-common:1.3.61
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.41 org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.61
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.41 org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.61
org.jetbrains.kotlin:kotlin-stdlib:1.3.50 org.jetbrains.kotlin:kotlin-stdlib:1.3.61
org.jetbrains:annotations:13.0 org.jetbrains:annotations:13.0
org.slf4j:slf4j-api:2.0.0-alpha1 org.slf4j:slf4j-api:2.0.0-alpha1

View File

@@ -1,17 +1,18 @@
# This is a Gradle generated file for dependency locking. # This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised. # Manual edits can break the build and are not advised.
# This file is expected to be part of source control. # This file is expected to be part of source control.
com.github.ajalt:clikt:2.2.0 com.github.ajalt:clikt:2.3.0
com.squareup.moshi:moshi-adapters:1.9.1 com.squareup.moshi:moshi-adapters:1.9.2
com.squareup.moshi:moshi-kotlin:1.9.1 com.squareup.moshi:moshi-kotlin:1.9.2
com.squareup.moshi:moshi:1.9.1 com.squareup.moshi:moshi:1.9.2
com.squareup.okio:okio:2.4.1 com.squareup.okio:okio:2.4.3
org.gradle:gradle-tooling-api:5.6.3 net.swiftzer.semver:semver:1.1.1
org.jetbrains.kotlin:kotlin-reflect:1.3.50 org.gradle:gradle-tooling-api:6.1
org.jetbrains.kotlin:kotlin-stdlib-common:1.3.50 org.jetbrains.kotlin:kotlin-reflect:1.3.61
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.41 org.jetbrains.kotlin:kotlin-stdlib-common:1.3.61
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.41 org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.61
org.jetbrains.kotlin:kotlin-stdlib:1.3.50 org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.61
org.jetbrains.kotlin:kotlin-stdlib:1.3.61
org.jetbrains:annotations:13.0 org.jetbrains:annotations:13.0
org.slf4j:slf4j-api:2.0.0-alpha1 org.slf4j:slf4j-api:2.0.0-alpha1
org.slf4j:slf4j-simple:2.0.0-alpha1 org.slf4j:slf4j-simple:2.0.0-alpha1

View File

@@ -19,7 +19,7 @@
# ''; # '';
# } # }
{ stdenv, lib, buildEnv, fetchurl, gradleGen, writeText }: { stdenv, buildEnv, fetchurl, gradleGen, writeText, writeTextDir }:
{ envSpec { envSpec
, pname ? null , pname ? null
@@ -27,9 +27,18 @@
, enableParallelBuilding ? true , enableParallelBuilding ? true
, gradleFlags ? [ "build" ] , gradleFlags ? [ "build" ]
, gradlePackage ? null , gradlePackage ? null
, enableDebug ? false
, ... } @ args: , ... } @ args:
let let
inherit (builtins)
filter sort replaceStrings attrValues match fromJSON
concatStringsSep;
inherit (stdenv.lib)
versionOlder unique mapAttrs last concatMapStringsSep removeSuffix
optionalString groupBy' readFile hasSuffix;
mkDep = depSpec: stdenv.mkDerivation { mkDep = depSpec: stdenv.mkDerivation {
inherit (depSpec) name; inherit (depSpec) name;
@@ -41,40 +50,171 @@ let
installPhase = '' installPhase = ''
mkdir -p $out/${depSpec.path} mkdir -p $out/${depSpec.path}
ln -s $src $out/${depSpec.path}/${depSpec.filename} 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 ''
<?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);
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: ''
<snapshotVersion>
${optionalString (version.classifier != "") "<classifier>${version.classifier}</classifier>"}
<extension>${version.extension}</extension>
<value>${version.value}</value>
${optionalString (version.updated != "") "<updated>${version.updated}</updated>"}
</snapshotVersion>
'';
in
attrValues (mapAttrs (path: meta:
with meta; writeTextDir path ''
<?xml version="1.0" encoding="UTF-8"?>
<metadata modelVersion="1.1">
<groupId>${groupId}</groupId>
<artifactId>${artifactId}</artifactId>
<version>${version}</version>
<versioning>
<snapshot>
${optionalString (timestamp != "") "<timestamp>${timestamp}</timestamp>"}
${optionalString (buildNumber != -1) "<buildNumber>${toString buildNumber}</buildNumber>"}
</snapshot>
${optionalString (lastUpdated != "") "<lastUpdated>${lastUpdated}</lastUpdated>"}
<snapshotVersions>
${concatMapStringsSep "\n " mkSnapshotVersion versions}
</snapshotVersions>
</versioning>
</metadata>
''
) modules);
mkRepo = project: type: deps: buildEnv { mkRepo = project: type: deps: buildEnv {
name = "${project}-gradle-${type}-env"; name = "${project}-gradle-${type}-env";
paths = map mkDep deps; paths = map mkDep deps ++ mkModuleMetadata deps ++ mkSnapshotMetadata deps;
}; };
mkInitScript = projectSpec: mkInitScript = projectSpec:
let let
repos = builtins.mapAttrs (mkRepo projectSpec.name) projectSpec.dependencies; repos = mapAttrs (mkRepo projectSpec.name) projectSpec.dependencies;
in in
writeText "init.gradle" '' 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()
}
}
}
gradle.settingsEvaluated { gradle.settingsEvaluated {
it.pluginManagement.repositories { offlineRepo(it.pluginManagement.repositories, "plugin", "${repos.plugin}")
clear()
maven { url = uri("${repos.plugin}") }
}
} }
gradle.projectsLoaded { gradle.projectsLoaded {
allprojects { allprojects {
buildscript { buildscript {
repositories { offlineRepo(repositories, "buildscript", "${repos.buildscript}")
clear() }
maven { url = uri("${repos.buildscript}") } offlineRepo(repositories, "project", "${repos.project}")
}
} }
repositories {
clear()
maven { url = uri("${repos.project}") }
}
}
} }
''; '';
@@ -95,9 +235,9 @@ let
gradle = args.gradlePackage or mkGradle projectSpec.gradle; gradle = args.gradlePackage or mkGradle projectSpec.gradle;
}; };
gradleEnv = builtins.mapAttrs gradleEnv = mapAttrs
(_: p: mkProjectEnv p) (_: p: mkProjectEnv p)
(builtins.fromJSON (builtins.readFile envSpec)); (fromJSON (readFile envSpec));
projectEnv = gradleEnv.""; projectEnv = gradleEnv."";
pname = args.pname or projectEnv.name; pname = args.pname or projectEnv.name;
@@ -118,9 +258,10 @@ in stdenv.mkDerivation (args // {
"GRADLE_USER_HOME=$(mktemp -d)" \ "GRADLE_USER_HOME=$(mktemp -d)" \
gradle --offline --no-daemon --no-build-cache \ gradle --offline --no-daemon --no-build-cache \
--info --full-stacktrace --warning-mode=all \ --info --full-stacktrace --warning-mode=all \
${lib.optionalString enableParallelBuilding "--parallel"} \ ${optionalString enableParallelBuilding "--parallel"} \
${optionalString enableDebug "-Dorg.gradle.debug=true"} \
--init-script ${projectEnv.initScript} \ --init-script ${projectEnv.initScript} \
${builtins.concatStringsSep " " gradleFlags} ${concatStringsSep " " gradleFlags}
) )
runHook postBuild runHook postBuild

View File

@@ -8,16 +8,7 @@ data class NixGradleEnv(
val version: String, val version: String,
val path: String, val path: String,
val gradle: DefaultGradle, val gradle: DefaultGradle,
val dependencies: Map<String, List<Dependency>> val dependencies: Map<String, List<DefaultArtifact>>
)
@JsonClass(generateAdapter = true)
data class Dependency(
val name: String,
val filename: String,
val path: String,
val urls: List<String>,
val sha256: String
) )
fun buildEnv(builds: Map<String, DefaultBuild>): Map<String, NixGradleEnv> = fun buildEnv(builds: Map<String, DefaultBuild>): Map<String, NixGradleEnv> =
@@ -28,58 +19,31 @@ fun buildEnv(builds: Map<String, DefaultBuild>): Map<String, NixGradleEnv> =
path = path, path = path,
gradle = build.gradle, gradle = build.gradle,
dependencies = mapOf( dependencies = mapOf(
"plugin" to buildRepo(build.pluginDependencies).values.toList(), "plugin" to build.pluginDependencies,
"buildscript" to build.rootProject.collectDependencies(DefaultProject::buildscriptDependencies) "buildscript" to build.rootProject.collectDependencies(DefaultProject::buildscriptDependencies),
.values.toList(),
"project" to build.rootProject.collectDependencies(DefaultProject::projectDependencies) "project" to build.rootProject.collectDependencies(DefaultProject::projectDependencies)
.values.toList()
) )
) )
} }
private fun DefaultProject.collectDependencies(chooser: DefaultProject.() -> DefaultDependencies): Map<DefaultArtifact, Dependency> { private fun DefaultProject.collectDependencies(
val result = mutableMapOf<DefaultArtifact, Dependency>() chooser: DefaultProject.() -> List<DefaultArtifact>
mergeRepo(result, buildRepo(chooser())) ): List<DefaultArtifact> {
val result = mutableMapOf<ArtifactIdentifier, DefaultArtifact>()
mergeRepo(result, chooser())
for (child in children) { for (child in children) {
mergeRepo(result, child.collectDependencies(chooser)) mergeRepo(result, child.collectDependencies(chooser))
} }
return result return result.values.toList()
} }
private fun buildRepo(deps: DefaultDependencies): Map<DefaultArtifact, Dependency> = private fun mergeRepo(
deps.artifacts.associate { artifact -> base: MutableMap<ArtifactIdentifier, DefaultArtifact>,
val name = with(artifact) { extra: List<DefaultArtifact>
buildString { ) {
append("$groupId-$artifactId-$version") extra.forEach { artifact ->
if (classifier.isNotEmpty()) append("-$classifier") base.merge(artifact.id, artifact) { old, new ->
append("-$extension") old.copy(urls = old.urls.union(new.urls).toList())
replace(Regex("[^A-Za-z0-9+\\-._?=]"), "_")
}
}
val filename = with(artifact) {
buildString {
append("$artifactId-$version")
if (classifier.isNotEmpty()) append("-$classifier")
append(".$extension")
}
}
val path = with(artifact) { "${groupId.replace(".", "/")}/$artifactId/$version" }
val dep = Dependency(
name = name,
filename = filename,
path = path,
urls = deps.repositories.maven.flatMap { repo ->
repo.urls.map { "${it.removeSuffix("/")}/$path/$filename" }
},
sha256 = artifact.sha256
)
artifact to dep
}
private fun mergeRepo(base: MutableMap<DefaultArtifact, Dependency>, extra: Map<DefaultArtifact, Dependency>) {
extra.forEach { (artifact, dep) ->
base.merge(artifact, dep) { old, new ->
old.copy(urls = old.urls + (new.urls - old.urls))
} }
} }
} }

View File

@@ -21,8 +21,10 @@ fun ProjectConnection.getBuildModel(config: Config, path: String): DefaultBuild
"-Porg.nixos.gradle2nix.configurations=${config.configurations.joinToString(",")}", "-Porg.nixos.gradle2nix.configurations=${config.configurations.joinToString(",")}",
"-Porg.nixos.gradle2nix.subprojects=${config.subprojects.joinToString(",")}" "-Porg.nixos.gradle2nix.subprojects=${config.subprojects.joinToString(",")}"
) )
if (config.gradleArgs != null) addArguments(config.gradleArgs)
if (path.isNotEmpty()) addArguments("--project-dir=$path") if (path.isNotEmpty()) addArguments("--project-dir=$path")
if (!config.quiet) { if (!config.quiet) {
setColorOutput(true)
setStandardOutput(System.err) setStandardOutput(System.err)
setStandardError(System.err) setStandardError(System.err)
} }

View File

@@ -4,7 +4,8 @@ import java.io.PrintStream
class Logger( class Logger(
val out: PrintStream = System.err, val out: PrintStream = System.err,
val verbose: Boolean) { val verbose: Boolean
) {
val log: (String) -> Unit = { if (verbose) out.println(it) } val log: (String) -> Unit = { if (verbose) out.println(it) }
val warn: (String) -> Unit = { out.println("Warning: $it")} val warn: (String) -> Unit = { out.println("Warning: $it")}

View File

@@ -8,7 +8,12 @@ import com.github.ajalt.clikt.parameters.arguments.ProcessedArgument
import com.github.ajalt.clikt.parameters.arguments.argument import com.github.ajalt.clikt.parameters.arguments.argument
import com.github.ajalt.clikt.parameters.arguments.convert import com.github.ajalt.clikt.parameters.arguments.convert
import com.github.ajalt.clikt.parameters.arguments.default import com.github.ajalt.clikt.parameters.arguments.default
import com.github.ajalt.clikt.parameters.options.* import com.github.ajalt.clikt.parameters.options.default
import com.github.ajalt.clikt.parameters.options.flag
import com.github.ajalt.clikt.parameters.options.multiple
import com.github.ajalt.clikt.parameters.options.option
import com.github.ajalt.clikt.parameters.options.split
import com.github.ajalt.clikt.parameters.options.validate
import com.github.ajalt.clikt.parameters.types.file import com.github.ajalt.clikt.parameters.types.file
import com.squareup.moshi.Moshi import com.squareup.moshi.Moshi
import com.squareup.moshi.Types import com.squareup.moshi.Types
@@ -20,6 +25,7 @@ val shareDir: String = System.getProperty("org.nixos.gradle2nix.share")
data class Config( data class Config(
val gradleVersion: String?, val gradleVersion: String?,
val gradleArgs: String?,
val configurations: List<String>, val configurations: List<String>,
val projectDir: File, val projectDir: File,
val includes: List<File>, val includes: List<File>,
@@ -37,6 +43,10 @@ class Main : CliktCommand(
metavar = "VERSION", metavar = "VERSION",
help = "Use a specific Gradle version") help = "Use a specific Gradle version")
private val gradleArgs: String? by option("--gradle-args", "-a",
metavar = "ARGS",
help = "Extra arguments to pass to Gradle")
private val configurations: List<String> by option("--configuration", "-c", private val configurations: List<String> by option("--configuration", "-c",
metavar = "NAME", metavar = "NAME",
help = "Add a configuration to resolve (default: all configurations)") help = "Add a configuration to resolve (default: all configurations)")
@@ -96,7 +106,16 @@ class Main : CliktCommand(
} }
override fun run() { override fun run() {
val config = Config(gradleVersion, configurations, projectDir, includes, subprojects, buildSrc, quiet) val config = Config(
gradleVersion,
gradleArgs,
configurations,
projectDir,
includes,
subprojects,
buildSrc,
quiet
)
val (log, _, _) = Logger(verbose = !config.quiet) val (log, _, _) = Logger(verbose = !config.quiet)
val paths = resolveProjects(config).map { p -> val paths = resolveProjects(config).map { p ->

BIN
assets/gradle2nix.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

680
assets/gradle2nix.svg Normal file
View File

@@ -0,0 +1,680 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="1280"
height="640"
viewBox="0 0 338.66666 169.33334"
version="1.1"
id="svg8"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
sodipodi:docname="gradle2nix.svg">
<defs
id="defs2">
<linearGradient
gradientTransform="matrix(0.52333152,0,0,0.52333152,25.176839,183.12552)"
id="linear-gradient"
x1="1.5"
y1="29.09"
x2="88.3"
y2="38.21"
gradientUnits="userSpaceOnUse">
<stop
offset="0"
stop-color="#06a0ce"
id="stop4734" />
<stop
offset="1"
stop-color="#51cbbf"
id="stop4736" />
</linearGradient>
<style
id="style4930">.Graphic-Style-2{fill:url(#linear-gradient);}</style>
<linearGradient
gradientUnits="userSpaceOnUse"
y2="38.21"
x2="88.3"
y1="29.09"
x1="1.5"
id="linear-gradient-5"
gradientTransform="matrix(0.52333152,0,0,0.52333152,25.176839,183.12552)">
<stop
id="stop4932"
stop-color="#06a0ce"
offset="0" />
<stop
id="stop4934"
stop-color="#51cbbf"
offset="1" />
</linearGradient>
<linearGradient
id="linearGradient5562"
inkscape:collect="always">
<stop
id="stop5564"
offset="0"
style="stop-color:#699ad7;stop-opacity:1" />
<stop
style="stop-color:#7eb1dd;stop-opacity:1"
offset="0.24345198"
id="stop5566" />
<stop
id="stop5568"
offset="1"
style="stop-color:#7ebae4;stop-opacity:1" />
</linearGradient>
<linearGradient
id="linearGradient5053"
inkscape:collect="always">
<stop
id="stop5055"
offset="0"
style="stop-color:#415e9a;stop-opacity:1" />
<stop
style="stop-color:#4a6baf;stop-opacity:1"
offset="0.23168644"
id="stop5057" />
<stop
id="stop5059"
offset="1"
style="stop-color:#5277c3;stop-opacity:1" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient5960">
<stop
style="stop-color:#637ddf;stop-opacity:1"
offset="0"
id="stop5962" />
<stop
id="stop5964"
offset="0.23168644"
style="stop-color:#649afa;stop-opacity:1" />
<stop
style="stop-color:#719efa;stop-opacity:1"
offset="1"
id="stop5966" />
</linearGradient>
<linearGradient
id="linearGradient5867"
inkscape:collect="always">
<stop
id="stop5869"
offset="0"
style="stop-color:#7363df;stop-opacity:1" />
<stop
style="stop-color:#6478fa;stop-opacity:1"
offset="0.23168644"
id="stop5871" />
<stop
id="stop5873"
offset="1"
style="stop-color:#719efa;stop-opacity:1" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5960"
id="linearGradient5855"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(983.36076,601.38885)"
x1="213.95642"
y1="338.62445"
x2="282.26105"
y2="515.97058" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5867"
id="linearGradient5855-8"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(-197.75174,-337.1451)"
x1="213.95642"
y1="338.62445"
x2="282.26105"
y2="515.97058" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5960"
id="linearGradient4544"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(983.36076,601.38885)"
x1="-775.20807"
y1="102.74675"
x2="-702.75317"
y2="247.58188" />
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath4501">
<circle
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#adadad;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="circle4503"
cx="335.13995"
cy="686.09473"
r="241.06563" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath5410">
<circle
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="circle5412"
cx="335.98114"
cy="340.98975"
r="241.13741" />
</clipPath>
<linearGradient
y2="937.71399"
x2="-496.29703"
y1="782.33563"
x1="-584.19934"
gradientTransform="translate(864.55062,-2197.497)"
gradientUnits="userSpaceOnUse"
id="linearGradient5137"
xlink:href="#linearGradient5053"
inkscape:collect="always" />
<linearGradient
y2="506.18814"
x2="290.08701"
y1="351.41116"
x1="200.59668"
gradientTransform="translate(70.505061,-1761.3076)"
gradientUnits="userSpaceOnUse"
id="linearGradient5162"
xlink:href="#linearGradient5562"
inkscape:collect="always" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5562"
id="linearGradient3917"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(70.650339,-1055.1511)"
x1="200.59668"
y1="351.41116"
x2="290.08701"
y2="506.18814" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5053"
id="linearGradient3919"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(864.69589,-1491.3405)"
x1="-584.19934"
y1="782.33563"
x2="-496.29703"
y2="937.71399" />
<linearGradient
inkscape:collect="always"
xlink:href="#linear-gradient-5"
id="linearGradient4732"
x1="418.76779"
y1="264.43814"
x2="652.08905"
y2="296.6008"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5867"
id="linearGradient4745"
x1="421.11343"
y1="273.91626"
x2="524.12122"
y2="273.91626"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linear-gradient-5"
id="linearGradient4763"
gradientUnits="userSpaceOnUse"
x1="418.76779"
y1="264.43814"
x2="652.08905"
y2="296.6008" />
<linearGradient
inkscape:collect="always"
xlink:href="#linear-gradient-5"
id="linearGradient4765"
gradientUnits="userSpaceOnUse"
x1="418.76779"
y1="264.43814"
x2="652.08905"
y2="296.6008" />
<linearGradient
inkscape:collect="always"
xlink:href="#linear-gradient-5"
id="linearGradient4767"
gradientUnits="userSpaceOnUse"
x1="418.76779"
y1="264.43814"
x2="652.08905"
y2="296.6008" />
<linearGradient
inkscape:collect="always"
xlink:href="#linear-gradient-5"
id="linearGradient4769"
gradientUnits="userSpaceOnUse"
x1="418.76779"
y1="264.43814"
x2="652.08905"
y2="296.6008" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5867"
id="linearGradient4771"
gradientUnits="userSpaceOnUse"
x1="421.11343"
y1="273.91626"
x2="524.12122"
y2="273.91626" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5867"
id="linearGradient4773"
gradientUnits="userSpaceOnUse"
x1="421.11343"
y1="273.91626"
x2="524.12122"
y2="273.91626" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5867"
id="linearGradient4775"
gradientUnits="userSpaceOnUse"
x1="421.11343"
y1="273.91626"
x2="524.12122"
y2="273.91626" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.38515625"
inkscape:cx="791.08312"
inkscape:cy="364.77075"
inkscape:document-units="px"
inkscape:current-layer="g4759"
showgrid="false"
units="px"
showguides="false"
inkscape:guide-bbox="true"
inkscape:window-width="1660"
inkscape:window-height="1011"
inkscape:window-x="260"
inkscape:window-y="30"
inkscape:window-maximized="0">
<sodipodi:guide
position="0,148.16667"
orientation="0,1"
id="guide4518"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,0,255)" />
<sodipodi:guide
position="0,21.166667"
orientation="0,1"
id="guide4793"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,0,255)" />
<sodipodi:guide
position="21.166667,0"
orientation="1,0"
id="guide4795"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,0,255)" />
<sodipodi:guide
position="317.5,0"
orientation="1,0"
id="guide4797"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,0,255)" />
</sodipodi:namedview>
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-127.66665)">
<g
id="g3833"
transform="matrix(1.087741,0,0,1.0877425,-6.2376695,-19.285721)">
<path
style="fill:url(#linear-gradient-5);stroke-width:0.52333152"
inkscape:connector-curvature="0"
class="Graphic-Style-2"
d="m 69.717638,185.31305 a 7.4679407,7.4679407 0 0 0 -10.37771,-0.17802 0.72219753,0.72219753 0 0 0 0,1.04667 l 0.900124,0.92105 a 0.71173086,0.71173086 0 0 0 0.931533,0.0627 4.2808518,4.2808518 0 0 1 5.610133,6.45792 c -5.929373,5.92412 -13.842146,-10.68643 -31.802883,-2.12473 a 2.4334915,2.4334915 0 0 0 -1.04665,3.4226 l 3.082411,5.32227 a 2.4282583,2.4282583 0 0 0 3.296998,0.90536 l 0.07318,-0.0419 -0.05756,0.0419 1.350185,-0.75359 a 31.551656,31.551656 0 0 0 4.301795,-3.20804 0.75359739,0.75359739 0 0 1 0.978628,-0.0314 v 0 a 0.70126424,0.70126424 0 0 1 0.03145,1.04667 32.242454,32.242454 0 0 1 -4.542531,3.41735 h -0.04707 l -1.365891,0.76407 a 3.8412533,3.8412533 0 0 1 -1.889235,0.49194 3.8988198,3.8988198 0 0 1 -3.385946,-1.94158 l -2.914958,-5.0292 c -5.573487,3.96685 -8.996077,11.58655 -7.143483,21.23679 a 0.71173086,0.71173086 0 0 0 0.696033,0.58089 h 3.286533 a 0.71173086,0.71173086 0 0 0 0.72743,-0.64893 4.8617496,4.8617496 0 0 1 9.639776,0 0.70649755,0.70649755 0 0 0 0.70126,0.62278 h 3.213252 a 0.71173086,0.71173086 0 0 0 0.701262,-0.62278 4.8617496,4.8617496 0 0 1 9.639776,0 0.71173086,0.71173086 0 0 0 0.70126,0.62278 h 3.192327 a 0.71173086,0.71173086 0 0 0 0.711723,-0.70128 c 0.07318,-4.50064 1.287386,-9.67117 4.746628,-12.26165 11.98433,-8.96468 8.83387,-16.64718 6.06022,-19.42083 z m -12.21985,13.53858 -2.286941,-1.1461 v 0 a 1.4339283,1.4339283 0 1 1 2.286941,1.15134 z"
id="path4941" />
<g
transform="matrix(0.08170627,0,0,0.08170627,58.715285,207.19198)"
id="g5193">
<g
id="layer7"
inkscape:label="bg"
style="display:none"
transform="translate(-23.75651,-24.84972)">
<rect
transform="translate(-132.5822,958.04022)"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="rect5389"
width="1543.4283"
height="483.7439"
x="132.5822"
y="-957.77832" />
</g>
<g
id="layer6"
inkscape:label="logo-guide"
style="display:none"
transform="translate(-156.33871,933.1905)">
<rect
y="-958.02759"
x="132.65129"
height="484.30399"
width="550.41602"
id="rect5379"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#5c201e;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
inkscape:export-filename="/home/tim/dev/nix/homepage/logo/nix-wiki.png"
inkscape:export-xdpi="22.07"
inkscape:export-ydpi="22.07" />
<rect
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#c24a46;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="rect5372"
width="501.94415"
height="434.30405"
x="156.12303"
y="-933.02759"
inkscape:export-filename="/home/tim/dev/nix/homepage/logo/nixos-logo-only-hires-print.png"
inkscape:export-xdpi="212.2"
inkscape:export-ydpi="212.2" />
<rect
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#d98d8a;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="rect5381"
width="24.939611"
height="24.939611"
x="658.02826"
y="-958.04022" />
</g>
<g
inkscape:label="print-logo"
id="layer1-6"
style="display:inline"
transform="translate(-156.33871,933.1905)">
<path
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#5277c3;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 309.40365,-710.2521 122.19683,211.6751 -56.15706,0.5268 -32.6236,-56.8692 -32.85645,56.5653 -27.90237,-0.011 -14.29086,-24.6896 46.81047,-80.4902 -33.22946,-57.8256 z"
id="path4861"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccccc" />
<path
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#7ebae4;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 353.50926,-797.4433 -122.21756,211.6631 -28.53477,-48.37 32.93839,-56.6875 -65.41521,-0.1719 -13.9414,-24.1698 14.23637,-24.721 93.11177,0.2939 33.46371,-57.6903 z"
id="use4863"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccccc" />
<path
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#7ebae4;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 362.88537,-628.243 244.41439,0.012 -27.62229,48.8968 -65.56199,-0.1817 32.55876,56.7371 -13.96098,24.1585 -28.52722,0.032 -46.3013,-80.7841 -66.69317,-0.1353 z"
id="use4865"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccccc" />
<path
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#7ebae4;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 505.14318,-720.9886 -122.19683,-211.6751 56.15706,-0.5268 32.6236,56.8692 32.85645,-56.5653 27.90237,0.011 14.29086,24.6896 -46.81047,80.4902 33.22946,57.8256 z"
id="use4867"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccccc" />
<path
sodipodi:nodetypes="cccccccccc"
inkscape:connector-curvature="0"
id="path4873"
d="m 309.40365,-710.2521 122.19683,211.6751 -56.15706,0.5268 -32.6236,-56.8692 -32.85645,56.5653 -27.90237,-0.011 -14.29086,-24.6896 46.81047,-80.4902 -33.22946,-57.8256 z"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#5277c3;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
<path
sodipodi:nodetypes="cccccccccc"
inkscape:connector-curvature="0"
id="use4875"
d="m 451.3364,-803.53264 -244.4144,-0.012 27.62229,-48.89685 65.56199,0.18175 -32.55875,-56.73717 13.96097,-24.15851 28.52722,-0.0315 46.3013,80.78414 66.69317,0.13524 z"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#5277c3;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
<path
sodipodi:nodetypes="cccccccccc"
inkscape:connector-curvature="0"
id="use4877"
d="m 460.87178,-633.8425 122.21757,-211.66304 28.53477,48.37003 -32.93839,56.68751 65.4152,0.1718 13.9414,24.1698 -14.23636,24.7211 -93.11177,-0.294 -33.46371,57.6904 z"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#5277c3;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
<g
id="layer2"
inkscape:label="guides"
style="display:none"
transform="translate(72.039038,-1799.4476)">
<path
d="M 460.60629,594.72881 209.74183,594.7288 84.309616,377.4738 209.74185,160.21882 l 250.86446,1e-5 125.43222,217.255 z"
inkscape:randomized="0"
inkscape:rounded="0"
inkscape:flatsided="true"
sodipodi:arg2="1.5707963"
sodipodi:arg1="1.0471976"
sodipodi:r2="217.25499"
sodipodi:r1="250.86446"
sodipodi:cy="377.47382"
sodipodi:cx="335.17407"
sodipodi:sides="6"
id="path6032"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.23600003;fill:#4e4d52;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
sodipodi:type="star" />
<path
transform="translate(0,-308.26772)"
sodipodi:type="star"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#4e4d52;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
id="path5875"
sodipodi:sides="6"
sodipodi:cx="335.17407"
sodipodi:cy="685.74158"
sodipodi:r1="100.83495"
sodipodi:r2="87.32563"
sodipodi:arg1="1.0471976"
sodipodi:arg2="1.5707963"
inkscape:flatsided="true"
inkscape:rounded="0"
inkscape:randomized="0"
d="m 385.59154,773.06721 -100.83495,0 -50.41747,-87.32564 50.41748,-87.32563 100.83495,10e-6 50.41748,87.32563 z" />
<path
transform="translate(0,-308.26772)"
sodipodi:nodetypes="ccccccccc"
inkscape:connector-curvature="0"
id="path5851"
d="m 1216.5591,938.53395 123.0545,228.14035 -42.6807,-1.2616 -43.4823,-79.7725 -39.6506,80.3267 -32.6875,-19.7984 53.4737,-100.2848 -37.1157,-73.88955 z"
style="fill:url(#linearGradient5855);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<rect
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.41499999;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#c53a3a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="rect5884"
width="48.834862"
height="226.22897"
x="-34.74221"
y="446.17056"
transform="rotate(-30)" />
<path
transform="translate(0,-308.26772)"
sodipodi:type="star"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.50899999;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="path3428"
sodipodi:sides="6"
sodipodi:cx="223.93674"
sodipodi:cy="878.63831"
sodipodi:r1="28.048939"
sodipodi:r2="24.291094"
sodipodi:arg1="0"
sodipodi:arg2="0.52359878"
inkscape:flatsided="true"
inkscape:rounded="0"
inkscape:randomized="0"
d="m 251.98568,878.63831 -14.02447,24.29109 h -28.04894 l -14.02447,-24.29109 14.02447,-24.2911 h 28.04894 z" />
<use
x="0"
y="0"
xlink:href="#rect5884"
id="use4252"
transform="rotate(60,268.29786,489.4515)"
width="100%"
height="100%" />
<rect
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:0.6507937;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="rect4254"
width="5.3947482"
height="115.12564"
x="545.71014"
y="467.07007"
transform="rotate(30,575.23539,-154.13386)" />
</g>
</g>
<g
id="layer3"
inkscape:label="gradient-logo"
style="display:inline;opacity:1"
transform="translate(-156.33871,933.1905)">
<path
sodipodi:nodetypes="cccccccccc"
inkscape:connector-curvature="0"
id="path3336-6"
d="m 309.54892,-710.38827 122.19683,211.67512 -56.15706,0.5268 -32.6236,-56.8692 -32.85645,56.5653 -27.90237,-0.011 -14.29086,-24.6896 46.81047,-80.4901 -33.22946,-57.8257 z"
style="opacity:1;fill:url(#linearGradient3917);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<use
height="100%"
width="100%"
transform="rotate(60,407.11155,-715.78724)"
id="use3439-6"
inkscape:transform-center-y="151.59082"
inkscape:transform-center-x="124.43045"
xlink:href="#path3336-6"
y="0"
x="0" />
<use
height="100%"
width="100%"
transform="rotate(-60,407.31177,-715.70016)"
id="use3445-0"
inkscape:transform-center-y="75.573958"
inkscape:transform-center-x="-168.20651"
xlink:href="#path3336-6"
y="0"
x="0" />
<use
height="100%"
width="100%"
transform="rotate(180,407.41868,-715.7565)"
id="use3449-5"
inkscape:transform-center-y="-139.94592"
inkscape:transform-center-x="59.669705"
xlink:href="#path3336-6"
y="0"
x="0" />
<path
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#linearGradient3919);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 309.54892,-710.38827 122.19683,211.67512 -56.15706,0.5268 -32.6236,-56.8692 -32.85645,56.5653 -27.90237,-0.011 -14.29086,-24.6896 46.81047,-80.4901 -33.22946,-57.8256 z"
id="path4260-0"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccccc" />
<use
height="100%"
width="100%"
transform="rotate(120,407.33916,-716.08356)"
id="use4354-5"
xlink:href="#path4260-0"
y="0"
x="0"
style="display:inline" />
<use
height="100%"
width="100%"
transform="rotate(-120,407.28823,-715.86995)"
id="use4362-2"
xlink:href="#path4260-0"
y="0"
x="0"
style="display:inline" />
</g>
</g>
</g>
<flowRoot
xml:space="preserve"
id="flowRoot6066"
style="font-style:normal;font-weight:normal;font-size:40px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><flowRegion
id="flowRegion6068"><rect
id="rect6070"
width="650"
height="201.42857"
x="428.57144"
y="231.42857" /></flowRegion><flowPara
id="flowPara6072" /></flowRoot> <g
id="g3895"
transform="matrix(1.087741,0,0,1.0877425,-1.5979005,-62.174062)">
<g
id="g4759"
transform="translate(0,-0.48648156)">
<flowRoot
transform="matrix(0.45162205,0,0,0.45162205,-75.796378,125.9913)"
style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:96px;line-height:1.25;font-family:Vegur;-inkscape-font-specification:'Vegur, Light';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;opacity:1;fill:url(#linearGradient4769);fill-opacity:1;stroke:none"
id="flowRoot3849"
xml:space="preserve"><flowRegion
style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:96px;font-family:Vegur;-inkscape-font-specification:'Vegur, Light';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:url(#linearGradient4765);fill-opacity:1"
id="flowRegion3845"><rect
style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:96px;font-family:Vegur;-inkscape-font-specification:'Vegur, Light';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:url(#linearGradient4763);fill-opacity:1"
y="218.57143"
x="412.85715"
height="200"
width="787.14282"
id="rect3843" /></flowRegion><flowPara
style="fill:url(#linearGradient4767);fill-opacity:1"
id="flowPara3847">gradle</flowPara></flowRoot> <flowRoot
transform="matrix(0.45162205,0,0,0.45162205,34.199761,125.86986)"
style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:96px;line-height:1.25;font-family:Vegur;-inkscape-font-specification:'Vegur, Light';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ff2a2a;fill-opacity:0.68707485;stroke:none"
id="flowRoot3865"
xml:space="preserve"><flowRegion
style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:96px;font-family:Vegur;-inkscape-font-specification:'Vegur, Light';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#ff2a2a;fill-opacity:0.68707485"
id="flowRegion3861"><rect
style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:96px;font-family:Vegur;-inkscape-font-specification:'Vegur, Light';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#ff2a2a;fill-opacity:0.68707485"
y="218.57143"
x="412.85715"
height="200"
width="787.14282"
id="rect3859" /></flowRegion><flowPara
id="flowPara3863">2</flowPara></flowRoot> <flowRoot
xml:space="preserve"
id="flowRoot3873"
style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:96px;line-height:1.25;font-family:Vegur;-inkscape-font-specification:'Vegur, Light';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;opacity:1;fill:url(#linearGradient4745);fill-opacity:1;stroke:none"
transform="matrix(0.45162205,0,0,0.45162205,56.653631,125.86986)"><flowRegion
id="flowRegion3869"
style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:96px;font-family:Vegur;-inkscape-font-specification:'Vegur, Light';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:url(#linearGradient4773);fill-opacity:1"><rect
id="rect3867"
width="787.14282"
height="200"
x="412.85715"
y="218.57143"
style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:96px;font-family:Vegur;-inkscape-font-specification:'Vegur, Light';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:url(#linearGradient4771);fill-opacity:1" /></flowRegion><flowPara
style="fill:url(#linearGradient4775);fill-opacity:1"
id="flowPara3871">nix</flowPara></flowRoot> </g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 37 KiB

View File

@@ -3,15 +3,20 @@ plugins {
idea idea
kotlin("jvm") version embeddedKotlinVersion apply false kotlin("jvm") version embeddedKotlinVersion apply false
kotlin("kapt") version embeddedKotlinVersion apply false kotlin("kapt") version embeddedKotlinVersion apply false
id("com.github.johnrengelman.shadow") version "5.1.0" apply false id("com.github.johnrengelman.shadow") version "5.2.0" apply false
id("org.ajoberstar.stutter") version "0.5.0" apply false id("org.ajoberstar.stutter") version "0.5.1" apply false
} }
group = "org.nixos.gradle2nix"
version = property("VERSION") ?: "unspecified"
subprojects { subprojects {
repositories { repositories {
jcenter() jcenter()
maven { url = uri("https://repo.gradle.org/gradle/libs-releases") } maven { url = uri("https://repo.gradle.org/gradle/libs-releases") }
} }
group = rootProject.group
version = rootProject.version
} }
allprojects { allprojects {
@@ -45,7 +50,7 @@ allprojects {
tasks { tasks {
wrapper { wrapper {
gradleVersion = "5.6.3" gradleVersion = "6.1"
distributionType = Wrapper.DistributionType.ALL distributionType = Wrapper.DistributionType.ALL
} }
} }

View File

@@ -0,0 +1,13 @@
plugins {
id 'java'
}
repositories {
jcenter()
mavenCentral()
}
dependencies {
implementation 'com.squareup.okio:okio:2.2.2'
implementation 'com.squareup.moshi:moshi:1.8.0'
}

View File

@@ -0,0 +1,13 @@
plugins {
java
}
repositories {
jcenter()
mavenCentral()
}
dependencies {
implementation("com.squareup.okio:okio:2.2.2")
implementation("com.squareup.moshi:moshi:1.8.0")
}

View File

@@ -0,0 +1,11 @@
plugins {
id 'java'
}
repositories {
mavenCentral()
}
dependencies {
implementation 'com.badlogicgames.gdx:gdx-platform:1.9.9:natives-desktop'
}

View File

@@ -0,0 +1,11 @@
plugins {
java
}
repositories {
mavenCentral()
}
dependencies {
implementation("com.badlogicgames.gdx:gdx-platform:1.9.9:natives-desktop")
}

View File

@@ -0,0 +1,12 @@
plugins {
id 'java'
}
repositories {
jcenter()
maven { url 'https://jitpack.io' }
}
dependencies {
implementation 'com.github.anuken:packr:-SNAPSHOT'
}

View File

@@ -0,0 +1,12 @@
plugins {
id "java"
}
repositories {
mavenCentral()
maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
}
dependencies {
implementation "com.squareup.okio:okio:2.5.0-SNAPSHOT"
}

View File

@@ -0,0 +1,12 @@
plugins {
java
}
repositories {
mavenCentral()
maven { url = uri("https://oss.sonatype.org/content/repositories/snapshots/") }
}
dependencies {
"implementation"("com.squareup.okio:okio:2.5.0-SNAPSHOT")
}

View File

@@ -0,0 +1,20 @@
plugins {
java
}
repositories {
ivy {
url = uri("https://asset.opendof.org")
layout("pattern") {
this as IvyPatternRepositoryLayout
ivy("ivy2/[organisation]/[module]/[revision]/ivy(.[platform]).xml")
artifact("artifact/[organisation]/[module]/[revision](/[platform])(/[type]s)/[artifact]-[revision](-[classifier]).[ext]")
}
}
}
dependencies {
dependencies {
implementation("org.opendof.core-java:dof-cipher-sms4:1.0")
}
}

View File

@@ -0,0 +1,7 @@
plugins {
id "org.jetbrains.kotlin.jvm" version "1.3.50"
}
repositories {
jcenter()
}

View File

@@ -0,0 +1,7 @@
plugins {
kotlin("jvm") version "1.3.50"
}
repositories {
jcenter()
}

View File

@@ -0,0 +1,15 @@
subprojects {
apply plugin: 'java'
}
project(':child-a') {
dependencies {
implementation project(':child-b')
}
}
project(':child-b') {
dependencies {
implementation project(':child-c')
}
}

View File

@@ -0,0 +1 @@
include ':child-a', ':child-b', ':child-c', ':child-d'

View File

@@ -0,0 +1,13 @@
plugins {
id 'java'
}
allprojects {
repositories {
jcenter()
}
}
dependencies {
testImplementation 'junit:junit:4.12'
}

View File

@@ -0,0 +1,7 @@
plugins {
id 'java'
}
dependencies {
implementation 'com.squareup.okio:okio:2.2.2'
}

View File

@@ -0,0 +1,8 @@
plugins {
id 'java'
}
dependencies {
implementation project(':child-a')
implementation 'com.squareup.moshi:moshi:1.8.0'
}

View File

@@ -0,0 +1 @@
include ':child-a', ':child-b'

View File

@@ -0,0 +1,13 @@
plugins {
java
}
allprojects {
repositories {
jcenter()
}
}
dependencies {
testImplementation("junit:junit:4.12")
}

View File

@@ -0,0 +1,7 @@
plugins {
java
}
dependencies {
implementation("com.squareup.okio:okio:2.2.2")
}

View File

@@ -0,0 +1,8 @@
plugins {
java
}
dependencies {
implementation(project(":child-a"))
implementation("com.squareup.moshi:moshi:1.8.0")
}

View File

@@ -0,0 +1 @@
include(":child-a", ":child-b")

File diff suppressed because it is too large Load Diff

View File

@@ -19,7 +19,7 @@
# ''; # '';
# } # }
{ stdenv, lib, buildEnv, fetchurl, gradleGen, writeText }: { stdenv, buildEnv, fetchurl, gradleGen, writeText, writeTextDir }:
{ envSpec { envSpec
, pname ? null , pname ? null
@@ -27,9 +27,18 @@
, enableParallelBuilding ? true , enableParallelBuilding ? true
, gradleFlags ? [ "build" ] , gradleFlags ? [ "build" ]
, gradlePackage ? null , gradlePackage ? null
, enableDebug ? false
, ... } @ args: , ... } @ args:
let let
inherit (builtins)
filter sort replaceStrings attrValues match fromJSON
concatStringsSep;
inherit (stdenv.lib)
versionOlder unique mapAttrs last concatMapStringsSep removeSuffix
optionalString groupBy' readFile hasSuffix;
mkDep = depSpec: stdenv.mkDerivation { mkDep = depSpec: stdenv.mkDerivation {
inherit (depSpec) name; inherit (depSpec) name;
@@ -41,40 +50,171 @@ let
installPhase = '' installPhase = ''
mkdir -p $out/${depSpec.path} mkdir -p $out/${depSpec.path}
ln -s $src $out/${depSpec.path}/${depSpec.filename} 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 ''
<?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);
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: ''
<snapshotVersion>
${optionalString (version.classifier != "") "<classifier>${version.classifier}</classifier>"}
<extension>${version.extension}</extension>
<value>${version.value}</value>
${optionalString (version.updated != "") "<updated>${version.updated}</updated>"}
</snapshotVersion>
'';
in
attrValues (mapAttrs (path: meta:
with meta; writeTextDir path ''
<?xml version="1.0" encoding="UTF-8"?>
<metadata modelVersion="1.1">
<groupId>${groupId}</groupId>
<artifactId>${artifactId}</artifactId>
<version>${version}</version>
<versioning>
<snapshot>
${optionalString (timestamp != "") "<timestamp>${timestamp}</timestamp>"}
${optionalString (buildNumber != -1) "<buildNumber>${toString buildNumber}</buildNumber>"}
</snapshot>
${optionalString (lastUpdated != "") "<lastUpdated>${lastUpdated}</lastUpdated>"}
<snapshotVersions>
${concatMapStringsSep "\n " mkSnapshotVersion versions}
</snapshotVersions>
</versioning>
</metadata>
''
) modules);
mkRepo = project: type: deps: buildEnv { mkRepo = project: type: deps: buildEnv {
name = "${project}-gradle-${type}-env"; name = "${project}-gradle-${type}-env";
paths = map mkDep deps; paths = map mkDep deps ++ mkModuleMetadata deps ++ mkSnapshotMetadata deps;
}; };
mkInitScript = projectSpec: mkInitScript = projectSpec:
let let
repos = builtins.mapAttrs (mkRepo projectSpec.name) projectSpec.dependencies; repos = mapAttrs (mkRepo projectSpec.name) projectSpec.dependencies;
in in
writeText "init.gradle" '' 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()
}
}
}
gradle.settingsEvaluated { gradle.settingsEvaluated {
it.pluginManagement.repositories { offlineRepo(it.pluginManagement.repositories, "plugin", "${repos.plugin}")
clear()
maven { url = uri("${repos.plugin}") }
}
} }
gradle.projectsLoaded { gradle.projectsLoaded {
allprojects { allprojects {
buildscript { buildscript {
repositories { offlineRepo(repositories, "buildscript", "${repos.buildscript}")
clear() }
maven { url = uri("${repos.buildscript}") } offlineRepo(repositories, "project", "${repos.project}")
}
} }
repositories {
clear()
maven { url = uri("${repos.project}") }
}
}
} }
''; '';
@@ -95,9 +235,9 @@ let
gradle = args.gradlePackage or mkGradle projectSpec.gradle; gradle = args.gradlePackage or mkGradle projectSpec.gradle;
}; };
gradleEnv = builtins.mapAttrs gradleEnv = mapAttrs
(_: p: mkProjectEnv p) (_: p: mkProjectEnv p)
(builtins.fromJSON (builtins.readFile envSpec)); (fromJSON (readFile envSpec));
projectEnv = gradleEnv.""; projectEnv = gradleEnv."";
pname = args.pname or projectEnv.name; pname = args.pname or projectEnv.name;
@@ -118,9 +258,10 @@ in stdenv.mkDerivation (args // {
"GRADLE_USER_HOME=$(mktemp -d)" \ "GRADLE_USER_HOME=$(mktemp -d)" \
gradle --offline --no-daemon --no-build-cache \ gradle --offline --no-daemon --no-build-cache \
--info --full-stacktrace --warning-mode=all \ --info --full-stacktrace --warning-mode=all \
${lib.optionalString enableParallelBuilding "--parallel"} \ ${optionalString enableParallelBuilding "--parallel"} \
${optionalString enableDebug "-Dorg.gradle.debug=true"} \
--init-script ${projectEnv.initScript} \ --init-script ${projectEnv.initScript} \
${builtins.concatStringsSep " " gradleFlags} ${concatStringsSep " " gradleFlags}
) )
runHook postBuild runHook postBuild

3
gradle.properties Normal file
View File

@@ -0,0 +1,3 @@
org.gradle.jvmargs='-Dfile.encoding=UTF-8'
VERSION=1.0.0-rc1

Binary file not shown.

View File

@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.3-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-6.1-all.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

29
gradlew vendored
View File

@@ -154,19 +154,19 @@ if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
else else
eval `echo args$i`="\"$arg\"" eval `echo args$i`="\"$arg\""
fi fi
i=$((i+1)) i=`expr $i + 1`
done done
case $i in case $i in
(0) set -- ;; 0) set -- ;;
(1) set -- "$args0" ;; 1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;; 2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;; 3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;; 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac esac
fi fi
@@ -175,14 +175,9 @@ save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " " echo " "
} }
APP_ARGS=$(save "$@") APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules # Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@" exec "$JAVACMD" "$@"

View File

@@ -4,7 +4,7 @@ plugins {
} }
dependencies { dependencies {
api("com.squareup.moshi:moshi:+") api("com.squareup.moshi:moshi:latest.release")
api("com.squareup.okio:okio:+") kapt("com.squareup.moshi:moshi-kotlin-codegen:latest.release")
kapt("com.squareup.moshi:moshi-kotlin-codegen:+") implementation("net.swiftzer.semver:semver:latest.release")
} }

View File

@@ -1,11 +1,12 @@
# This is a Gradle generated file for dependency locking. # This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised. # Manual edits can break the build and are not advised.
# This file is expected to be part of source control. # This file is expected to be part of source control.
com.squareup.moshi:moshi:1.9.1 com.squareup.moshi:moshi:1.9.2
com.squareup.okio:okio:2.4.1 com.squareup.okio:okio:1.16.0
org.jetbrains.kotlin:kotlin-reflect:1.3.41 net.swiftzer.semver:semver:1.1.1
org.jetbrains.kotlin:kotlin-stdlib-common:1.3.50 org.jetbrains.kotlin:kotlin-reflect:1.3.61
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.41 org.jetbrains.kotlin:kotlin-stdlib-common:1.3.61
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.41 org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.61
org.jetbrains.kotlin:kotlin-stdlib:1.3.50 org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.61
org.jetbrains.kotlin:kotlin-stdlib:1.3.61
org.jetbrains:annotations:13.0 org.jetbrains:annotations:13.0

View File

@@ -1,8 +1,6 @@
# This is a Gradle generated file for dependency locking. # This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised. # Manual edits can break the build and are not advised.
# This file is expected to be part of source control. # This file is expected to be part of source control.
com.squareup.moshi:moshi:1.9.1 com.squareup.moshi:moshi:1.9.2
com.squareup.okio:okio:2.4.1 com.squareup.okio:okio:1.16.0
org.jetbrains.kotlin:kotlin-stdlib-common:1.3.50 net.swiftzer.semver:semver:1.1.1
org.jetbrains.kotlin:kotlin-stdlib:1.3.50
org.jetbrains:annotations:13.0

View File

@@ -1,11 +1,12 @@
# This is a Gradle generated file for dependency locking. # This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised. # Manual edits can break the build and are not advised.
# This file is expected to be part of source control. # This file is expected to be part of source control.
com.squareup.moshi:moshi:1.9.1 com.squareup.moshi:moshi:1.9.2
com.squareup.okio:okio:2.4.1 com.squareup.okio:okio:1.16.0
org.jetbrains.kotlin:kotlin-reflect:1.3.41 net.swiftzer.semver:semver:1.1.1
org.jetbrains.kotlin:kotlin-stdlib-common:1.3.50 org.jetbrains.kotlin:kotlin-reflect:1.3.61
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.41 org.jetbrains.kotlin:kotlin-stdlib-common:1.3.61
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.41 org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.61
org.jetbrains.kotlin:kotlin-stdlib:1.3.50 org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.61
org.jetbrains.kotlin:kotlin-stdlib:1.3.61
org.jetbrains:annotations:13.0 org.jetbrains:annotations:13.0

View File

@@ -1,11 +1,12 @@
# This is a Gradle generated file for dependency locking. # This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised. # Manual edits can break the build and are not advised.
# This file is expected to be part of source control. # This file is expected to be part of source control.
com.squareup.moshi:moshi:1.9.1 com.squareup.moshi:moshi:1.9.2
com.squareup.okio:okio:2.4.1 com.squareup.okio:okio:1.16.0
org.jetbrains.kotlin:kotlin-reflect:1.3.41 net.swiftzer.semver:semver:1.1.1
org.jetbrains.kotlin:kotlin-stdlib-common:1.3.50 org.jetbrains.kotlin:kotlin-reflect:1.3.61
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.41 org.jetbrains.kotlin:kotlin-stdlib-common:1.3.61
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.41 org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.61
org.jetbrains.kotlin:kotlin-stdlib:1.3.50 org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.61
org.jetbrains.kotlin:kotlin-stdlib:1.3.61
org.jetbrains:annotations:13.0 org.jetbrains:annotations:13.0

View File

@@ -1,20 +1,22 @@
package org.nixos.gradle2nix package org.nixos.gradle2nix
import com.squareup.moshi.JsonClass import com.squareup.moshi.JsonClass
import net.swiftzer.semver.SemVer
import java.io.Serializable import java.io.Serializable
import java.lang.IllegalArgumentException
@JsonClass(generateAdapter = true) @JsonClass(generateAdapter = true)
data class DefaultBuild( data class DefaultBuild(
override val gradle: DefaultGradle, override val gradle: DefaultGradle,
override val pluginDependencies: DefaultDependencies, override val pluginDependencies: List<DefaultArtifact>,
override val rootProject: DefaultProject, override val rootProject: DefaultProject,
override val includedBuilds: List<DefaultIncludedBuild> override val includedBuilds: List<DefaultIncludedBuild>
) : Build, Serializable { ) : Build, Serializable {
constructor(model: Build) : this( constructor(model: Build) : this(
DefaultGradle(model.gradle), DefaultGradle(model.gradle),
DefaultDependencies(model.pluginDependencies), model.pluginDependencies.map(::DefaultArtifact),
DefaultProject(model.rootProject), DefaultProject(model.rootProject),
model.includedBuilds.map { DefaultIncludedBuild(it) } model.includedBuilds.map(::DefaultIncludedBuild)
) )
} }
@@ -52,8 +54,8 @@ data class DefaultProject(
override val version: String, override val version: String,
override val path: String, override val path: String,
override val projectDir: String, override val projectDir: String,
override val buildscriptDependencies: DefaultDependencies, override val buildscriptDependencies: List<DefaultArtifact>,
override val projectDependencies: DefaultDependencies, override val projectDependencies: List<DefaultArtifact>,
override val children: List<DefaultProject> override val children: List<DefaultProject>
) : Project, Serializable { ) : Project, Serializable {
constructor(model: Project) : this( constructor(model: Project) : this(
@@ -61,59 +63,75 @@ data class DefaultProject(
model.version, model.version,
model.path, model.path,
model.projectDir, model.projectDir,
DefaultDependencies(model.buildscriptDependencies), model.buildscriptDependencies.map(::DefaultArtifact),
DefaultDependencies(model.projectDependencies), model.projectDependencies.map(::DefaultArtifact),
model.children.map { DefaultProject(it) } model.children.map { DefaultProject(it) }
) )
} }
@JsonClass(generateAdapter = true)
data class DefaultDependencies(
override val repositories: DefaultRepositories,
override val artifacts: List<DefaultArtifact>
) : Dependencies, Serializable {
constructor(model: Dependencies) : this(
DefaultRepositories(model.repositories),
model.artifacts.map { DefaultArtifact(it) }
)
}
@JsonClass(generateAdapter = true)
data class DefaultRepositories(
override val maven: List<DefaultMaven>
) : Repositories, Serializable {
constructor(model: Repositories) : this(
model.maven.map { DefaultMaven(it) }
)
}
@JsonClass(generateAdapter = true)
data class DefaultMaven(
override val urls: List<String>
) : Maven, Serializable {
constructor(model: Maven) : this(
model.urls.toList()
)
}
@JsonClass(generateAdapter = true) @JsonClass(generateAdapter = true)
data class DefaultArtifact( data class DefaultArtifact(
override val groupId: String, override val id: DefaultArtifactIdentifier,
override val artifactId: String, override val name: String,
override val version: String, override val path: String,
override val classifier: String, override val timestamp: String? = null,
override val extension: String, override val build: Int? = null,
override val urls: List<String>,
override val sha256: String override val sha256: String
) : Artifact, Comparable<DefaultArtifact>, Serializable { ) : Artifact, Comparable<DefaultArtifact>, Serializable {
constructor(model: Artifact) : this( constructor(model: Artifact) : this(
model.groupId, DefaultArtifactIdentifier(model.id),
model.artifactId, model.name,
model.version, model.path,
model.classifier, model.timestamp,
model.extension, model.build,
model.urls,
model.sha256 model.sha256
) )
override fun toString() = "$groupId:$artifactId:$version:$classifier:$extension" override fun toString() = id.toString()
override fun compareTo(other: DefaultArtifact): Int = toString().compareTo(other.toString()) override fun compareTo(other: DefaultArtifact): Int = id.compareTo(other.id)
} }
@JsonClass(generateAdapter = true)
data class DefaultArtifactIdentifier(
override val group: String,
override val name: String,
override val version: String,
override val type: String,
override val extension: String = type,
override val classifier: String? = null
) : ArtifactIdentifier, Comparable<DefaultArtifactIdentifier>, Serializable {
constructor(model: ArtifactIdentifier) : this(
model.group,
model.name,
model.version,
model.type,
model.extension,
model.classifier
)
@delegate:Transient
private val semver: SemVer? by lazy {
try {
SemVer.parse(version)
} catch (_: IllegalArgumentException) {
null
}
}
override fun compareTo(other: DefaultArtifactIdentifier): Int {
return group.compareTo(other.group).takeUnless { it == 0 }
?: name.compareTo(other.name).takeUnless { it == 0 }
?: other.semver?.let { semver?.compareTo(it) }?.takeUnless { it == 0 }
?: type.compareTo(other.type).takeUnless { it == 0 }
?: other.classifier?.let { classifier?.compareTo(it) }?.takeUnless { it == 0 }
?: 0
}
override fun toString(): String = buildString {
append("$group:$name:$version")
if (classifier != null) append(":$classifier")
append("@$type")
}
}

View File

@@ -2,7 +2,7 @@ package org.nixos.gradle2nix
interface Build { interface Build {
val gradle: Gradle val gradle: Gradle
val pluginDependencies: Dependencies val pluginDependencies: List<Artifact>
val rootProject: Project val rootProject: Project
val includedBuilds: List<IncludedBuild> val includedBuilds: List<IncludedBuild>
} }
@@ -25,29 +25,26 @@ interface Project {
val version: String val version: String
val path: String val path: String
val projectDir: String val projectDir: String
val buildscriptDependencies: Dependencies val buildscriptDependencies: List<Artifact>
val projectDependencies: Dependencies val projectDependencies: List<Artifact>
val children: List<Project> val children: List<Project>
} }
interface Dependencies {
val repositories: Repositories
val artifacts: List<Artifact>
}
interface Repositories {
val maven: List<Maven>
}
interface Maven {
val urls: List<String>
}
interface Artifact { interface Artifact {
val groupId: String val id: ArtifactIdentifier
val artifactId: String val name: String
val version: String val path: String
val classifier: String val timestamp: String?
val extension: String val build: Int?
val urls: List<String>
val sha256: String val sha256: String
} }
interface ArtifactIdentifier {
val group: String
val name: String
val version: String
val type: String
val extension: String
val classifier: String?
}

View File

@@ -5,4 +5,6 @@
5.3.1 5.3.1
5.4.1 5.4.1
5.5.1 5.5.1
5.6.3 5.6.4
6.0.1
6.1

View File

@@ -12,4 +12,6 @@
5.3.1 5.3.1
5.4.1 5.4.1
5.5.1 5.5.1
5.6.3 5.6.4
6.0.1
6.1

View File

@@ -9,12 +9,14 @@ plugins {
id("com.github.johnrengelman.shadow") id("com.github.johnrengelman.shadow")
id("org.ajoberstar.stutter") id("org.ajoberstar.stutter")
} }
apply {
plugin("kotlin")
}
group = "org.nixos" sourceSets {
version = "1.0.0-SNAPSHOT" compatTest {
resources {
srcDir("$rootDir/fixtures")
}
}
}
dependencyLocking { dependencyLocking {
lockAllConfigurations() lockAllConfigurations()
@@ -30,17 +32,21 @@ dependencies {
implementation(project(":model")) implementation(project(":model"))
shadow(gradleApi()) shadow(gradleApi())
compileOnly("org.gradle:gradle-tooling-api:${gradle.gradleVersion}") compileOnly("org.gradle:gradle-tooling-api:${gradle.gradleVersion}")
implementation("org.apache.maven:maven-model:latest.release") implementation("org.apache.ivy:ivy:latest.release")
implementation("org.apache.maven:maven-model-builder:latest.release") implementation("org.apache.maven:maven-repository-metadata:latest.release")
compatTestImplementation(embeddedKotlin("stdlib-jdk8")) compatTestImplementation(embeddedKotlin("stdlib-jdk8"))
compatTestImplementation(embeddedKotlin("test-junit5")) compatTestImplementation(embeddedKotlin("test-junit5"))
compatTestImplementation(embeddedKotlin("reflect")) compatTestImplementation(embeddedKotlin("reflect"))
compatTestImplementation("org.junit.jupiter:junit-jupiter-api:5.4+") compatTestImplementation("org.junit.jupiter:junit-jupiter-api:latest.release")
compatTestRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.4+") compatTestRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:latest.release")
compatTestImplementation("org.junit.jupiter:junit-jupiter-params:latest.release")
compatTestRuntimeOnly("org.junit.platform:junit-platform-launcher:latest.release")
compatTestImplementation("dev.minutest:minutest:latest.release")
compatTestImplementation(gradleTestKit()) compatTestImplementation(gradleTestKit())
compatTestImplementation(project(":model")) compatTestImplementation(project(":model"))
compatTestImplementation("io.strikt:strikt-core:latest.release") compatTestImplementation("io.strikt:strikt-core:latest.release")
compatTestImplementation("com.squareup.okio:okio:latest.release")
} }
gradlePlugin { gradlePlugin {

View File

@@ -7,28 +7,31 @@ commons-logging:commons-logging:1.2
de.undercouch:gradle-download-task:3.4.3 de.undercouch:gradle-download-task:3.4.3
org.apache.httpcomponents:httpclient:4.5.3 org.apache.httpcomponents:httpclient:4.5.3
org.apache.httpcomponents:httpcore:4.4.6 org.apache.httpcomponents:httpcore:4.4.6
org.gradle.kotlin.kotlin-dsl:org.gradle.kotlin.kotlin-dsl.gradle.plugin:1.2.9 org.gradle.kotlin.kotlin-dsl:org.gradle.kotlin.kotlin-dsl.gradle.plugin:1.3.3
org.gradle.kotlin:plugins:1.2.9 org.gradle.kotlin:plugins:1.3.3
org.jetbrains.intellij.deps:trove4j:1.0.20181211 org.jetbrains.intellij.deps:trove4j:1.0.20181211
org.jetbrains.kotlin:kotlin-android-extensions:1.3.41 org.jetbrains.kotlin:kotlin-android-extensions:1.3.61
org.jetbrains.kotlin:kotlin-annotation-processing-gradle:1.3.41 org.jetbrains.kotlin:kotlin-annotation-processing-gradle:1.3.61
org.jetbrains.kotlin:kotlin-build-common:1.3.41 org.jetbrains.kotlin:kotlin-build-common:1.3.61
org.jetbrains.kotlin:kotlin-compiler-embeddable:1.3.41 org.jetbrains.kotlin:kotlin-compiler-embeddable:1.3.61
org.jetbrains.kotlin:kotlin-compiler-runner:1.3.41 org.jetbrains.kotlin:kotlin-compiler-runner:1.3.61
org.jetbrains.kotlin:kotlin-daemon-client:1.3.41 org.jetbrains.kotlin:kotlin-daemon-client:1.3.61
org.jetbrains.kotlin:kotlin-gradle-plugin-api:1.3.41 org.jetbrains.kotlin:kotlin-daemon-embeddable:1.3.61
org.jetbrains.kotlin:kotlin-gradle-plugin-model:1.3.41 org.jetbrains.kotlin:kotlin-gradle-plugin-api:1.3.61
org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.41 org.jetbrains.kotlin:kotlin-gradle-plugin-model:1.3.61
org.jetbrains.kotlin:kotlin-reflect:1.3.41 org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.61
org.jetbrains.kotlin:kotlin-sam-with-receiver:1.3.41 org.jetbrains.kotlin:kotlin-native-utils:1.3.61
org.jetbrains.kotlin:kotlin-script-runtime:1.3.41 org.jetbrains.kotlin:kotlin-reflect:1.3.61
org.jetbrains.kotlin:kotlin-scripting-common:1.3.41 org.jetbrains.kotlin:kotlin-sam-with-receiver:1.3.61
org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:1.3.41 org.jetbrains.kotlin:kotlin-script-runtime:1.3.61
org.jetbrains.kotlin:kotlin-scripting-compiler-impl-embeddable:1.3.41 org.jetbrains.kotlin:kotlin-scripting-common:1.3.61
org.jetbrains.kotlin:kotlin-scripting-jvm:1.3.41 org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:1.3.61
org.jetbrains.kotlin:kotlin-stdlib-common:1.3.41 org.jetbrains.kotlin:kotlin-scripting-compiler-impl-embeddable:1.3.61
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.41 org.jetbrains.kotlin:kotlin-scripting-jvm:1.3.61
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.41 org.jetbrains.kotlin:kotlin-stdlib-common:1.3.61
org.jetbrains.kotlin:kotlin-stdlib:1.3.41 org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.61
org.jetbrains.kotlinx:kotlinx-coroutines-core:1.1.1 org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.61
org.jetbrains.kotlin:kotlin-stdlib:1.3.61
org.jetbrains.kotlin:kotlin-util-io:1.3.61
org.jetbrains.kotlinx:kotlinx-coroutines-core:1.2.1
org.jetbrains:annotations:13.0 org.jetbrains:annotations:13.0

View File

@@ -1,20 +1,23 @@
# This is a Gradle generated file for dependency locking. # This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised. # Manual edits can break the build and are not advised.
# This file is expected to be part of source control. # This file is expected to be part of source control.
com.squareup.moshi:moshi:1.9.1 com.squareup.moshi:moshi:1.9.2
com.squareup.okio:okio:2.4.1 com.squareup.okio:okio:2.4.3
io.strikt:strikt-core:0.22.2 dev.minutest:minutest:1.10.0
org.apiguardian:apiguardian-api:1.0.0 io.strikt:strikt-core:0.23.4
org.jetbrains.kotlin:kotlin-reflect:1.3.41 org.apiguardian:apiguardian-api:1.1.0
org.jetbrains.kotlin:kotlin-stdlib-common:1.3.50 org.jetbrains.kotlin:kotlin-reflect:1.3.61
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.50 org.jetbrains.kotlin:kotlin-stdlib-common:1.3.61
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.50 org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.61
org.jetbrains.kotlin:kotlin-stdlib:1.3.50 org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.61
org.jetbrains.kotlin:kotlin-test-annotations-common:1.3.41 org.jetbrains.kotlin:kotlin-stdlib:1.3.61
org.jetbrains.kotlin:kotlin-test-common:1.3.41 org.jetbrains.kotlin:kotlin-test-annotations-common:1.3.61
org.jetbrains.kotlin:kotlin-test-junit5:1.3.41 org.jetbrains.kotlin:kotlin-test-common:1.3.61
org.jetbrains.kotlin:kotlin-test:1.3.41 org.jetbrains.kotlin:kotlin-test-junit5:1.3.61
org.jetbrains.kotlin:kotlin-test:1.3.61
org.jetbrains:annotations:13.0 org.jetbrains:annotations:13.0
org.junit.jupiter:junit-jupiter-api:5.4.2 org.junit.jupiter:junit-jupiter-api:5.6.0
org.junit.platform:junit-platform-commons:1.4.2 org.junit.jupiter:junit-jupiter-params:5.6.0
org.junit.platform:junit-platform-commons:1.6.0
org.junit:junit-bom:5.6.0
org.opentest4j:opentest4j:1.2.0 org.opentest4j:opentest4j:1.2.0

View File

@@ -2,23 +2,29 @@
# Manual edits can break the build and are not advised. # Manual edits can break the build and are not advised.
# This file is expected to be part of source control. # This file is expected to be part of source control.
com.christophsturm:filepeek:0.1.1 com.christophsturm:filepeek:0.1.1
com.squareup.moshi:moshi:1.9.1 com.squareup.moshi:moshi:1.9.2
com.squareup.okio:okio:2.4.1 com.squareup.okio:okio:2.4.3
io.strikt:strikt-core:0.22.2 dev.minutest:minutest:1.10.0
org.apiguardian:apiguardian-api:1.0.0 io.github.classgraph:classgraph:4.8.28
org.jetbrains.kotlin:kotlin-reflect:1.3.50 io.strikt:strikt-core:0.23.4
org.jetbrains.kotlin:kotlin-stdlib-common:1.3.50 net.swiftzer.semver:semver:1.1.1
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.50 org.apiguardian:apiguardian-api:1.1.0
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.50 org.jetbrains.kotlin:kotlin-reflect:1.3.61
org.jetbrains.kotlin:kotlin-stdlib:1.3.50 org.jetbrains.kotlin:kotlin-stdlib-common:1.3.61
org.jetbrains.kotlin:kotlin-test-annotations-common:1.3.41 org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.61
org.jetbrains.kotlin:kotlin-test-common:1.3.41 org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.61
org.jetbrains.kotlin:kotlin-test-junit5:1.3.41 org.jetbrains.kotlin:kotlin-stdlib:1.3.61
org.jetbrains.kotlin:kotlin-test:1.3.41 org.jetbrains.kotlin:kotlin-test-annotations-common:1.3.61
org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.1 org.jetbrains.kotlin:kotlin-test-common:1.3.61
org.jetbrains.kotlin:kotlin-test-junit5:1.3.61
org.jetbrains.kotlin:kotlin-test:1.3.61
org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.3
org.jetbrains:annotations:13.0 org.jetbrains:annotations:13.0
org.junit.jupiter:junit-jupiter-api:5.4.2 org.junit.jupiter:junit-jupiter-api:5.6.0
org.junit.jupiter:junit-jupiter-engine:5.4.2 org.junit.jupiter:junit-jupiter-engine:5.6.0
org.junit.platform:junit-platform-commons:1.4.2 org.junit.jupiter:junit-jupiter-params:5.6.0
org.junit.platform:junit-platform-engine:1.4.2 org.junit.platform:junit-platform-commons:1.6.0
org.junit.platform:junit-platform-engine:1.6.0
org.junit.platform:junit-platform-launcher:1.6.0
org.junit:junit-bom:5.6.0
org.opentest4j:opentest4j:1.2.0 org.opentest4j:opentest4j:1.2.0

View File

@@ -1,21 +1,15 @@
# This is a Gradle generated file for dependency locking. # This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised. # Manual edits can break the build and are not advised.
# This file is expected to be part of source control. # This file is expected to be part of source control.
com.squareup.moshi:moshi:1.9.1 com.squareup.moshi:moshi:1.9.2
com.squareup.okio:okio:2.4.1 com.squareup.okio:okio:1.16.0
javax.inject:javax.inject:1 org.apache.ivy:ivy:2.5.0
org.apache.commons:commons-lang3:3.8.1 org.apache.maven:maven-repository-metadata:3.6.3
org.apache.maven:maven-artifact:3.6.2
org.apache.maven:maven-builder-support:3.6.2
org.apache.maven:maven-model-builder:3.6.2
org.apache.maven:maven-model:3.6.2
org.codehaus.plexus:plexus-interpolation:1.25
org.codehaus.plexus:plexus-utils:3.2.1 org.codehaus.plexus:plexus-utils:3.2.1
org.eclipse.sisu:org.eclipse.sisu.inject:0.3.3 org.gradle:gradle-tooling-api:6.1
org.gradle:gradle-tooling-api:5.6.3 org.jetbrains.kotlin:kotlin-reflect:1.3.61
org.jetbrains.kotlin:kotlin-reflect:1.3.41 org.jetbrains.kotlin:kotlin-stdlib-common:1.3.61
org.jetbrains.kotlin:kotlin-stdlib-common:1.3.50 org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.61
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.41 org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.61
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.41 org.jetbrains.kotlin:kotlin-stdlib:1.3.61
org.jetbrains.kotlin:kotlin-stdlib:1.3.50
org.jetbrains:annotations:13.0 org.jetbrains:annotations:13.0

View File

@@ -1,17 +1,9 @@
# This is a Gradle generated file for dependency locking. # This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised. # Manual edits can break the build and are not advised.
# This file is expected to be part of source control. # This file is expected to be part of source control.
com.squareup.moshi:moshi:1.9.1 com.squareup.moshi:moshi:1.9.2
com.squareup.okio:okio:2.4.1 com.squareup.okio:okio:1.16.0
javax.inject:javax.inject:1 net.swiftzer.semver:semver:1.1.1
org.apache.commons:commons-lang3:3.8.1 org.apache.ivy:ivy:2.5.0
org.apache.maven:maven-artifact:3.6.2 org.apache.maven:maven-repository-metadata:3.6.3
org.apache.maven:maven-builder-support:3.6.2
org.apache.maven:maven-model-builder:3.6.2
org.apache.maven:maven-model:3.6.2
org.codehaus.plexus:plexus-interpolation:1.25
org.codehaus.plexus:plexus-utils:3.2.1 org.codehaus.plexus:plexus-utils:3.2.1
org.eclipse.sisu:org.eclipse.sisu.inject:0.3.3
org.jetbrains.kotlin:kotlin-stdlib-common:1.3.50
org.jetbrains.kotlin:kotlin-stdlib:1.3.50
org.jetbrains:annotations:13.0

View File

@@ -1,20 +1,14 @@
# This is a Gradle generated file for dependency locking. # This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised. # Manual edits can break the build and are not advised.
# This file is expected to be part of source control. # This file is expected to be part of source control.
com.squareup.moshi:moshi:1.9.1 com.squareup.moshi:moshi:1.9.2
com.squareup.okio:okio:2.4.1 com.squareup.okio:okio:1.16.0
javax.inject:javax.inject:1 org.apache.ivy:ivy:2.5.0
org.apache.commons:commons-lang3:3.8.1 org.apache.maven:maven-repository-metadata:3.6.3
org.apache.maven:maven-artifact:3.6.2
org.apache.maven:maven-builder-support:3.6.2
org.apache.maven:maven-model-builder:3.6.2
org.apache.maven:maven-model:3.6.2
org.codehaus.plexus:plexus-interpolation:1.25
org.codehaus.plexus:plexus-utils:3.2.1 org.codehaus.plexus:plexus-utils:3.2.1
org.eclipse.sisu:org.eclipse.sisu.inject:0.3.3 org.jetbrains.kotlin:kotlin-reflect:1.3.61
org.jetbrains.kotlin:kotlin-reflect:1.3.41 org.jetbrains.kotlin:kotlin-stdlib-common:1.3.61
org.jetbrains.kotlin:kotlin-stdlib-common:1.3.50 org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.61
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.41 org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.61
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.41 org.jetbrains.kotlin:kotlin-stdlib:1.3.61
org.jetbrains.kotlin:kotlin-stdlib:1.3.50
org.jetbrains:annotations:13.0 org.jetbrains:annotations:13.0

View File

@@ -1,20 +1,15 @@
# This is a Gradle generated file for dependency locking. # This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised. # Manual edits can break the build and are not advised.
# This file is expected to be part of source control. # This file is expected to be part of source control.
com.squareup.moshi:moshi:1.9.1 com.squareup.moshi:moshi:1.9.2
com.squareup.okio:okio:2.4.1 com.squareup.okio:okio:1.16.0
javax.inject:javax.inject:1 net.swiftzer.semver:semver:1.1.1
org.apache.commons:commons-lang3:3.8.1 org.apache.ivy:ivy:2.5.0
org.apache.maven:maven-artifact:3.6.2 org.apache.maven:maven-repository-metadata:3.6.3
org.apache.maven:maven-builder-support:3.6.2
org.apache.maven:maven-model-builder:3.6.2
org.apache.maven:maven-model:3.6.2
org.codehaus.plexus:plexus-interpolation:1.25
org.codehaus.plexus:plexus-utils:3.2.1 org.codehaus.plexus:plexus-utils:3.2.1
org.eclipse.sisu:org.eclipse.sisu.inject:0.3.3 org.jetbrains.kotlin:kotlin-reflect:1.3.61
org.jetbrains.kotlin:kotlin-reflect:1.3.41 org.jetbrains.kotlin:kotlin-stdlib-common:1.3.61
org.jetbrains.kotlin:kotlin-stdlib-common:1.3.50 org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.61
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.41 org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.61
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.41 org.jetbrains.kotlin:kotlin-stdlib:1.3.61
org.jetbrains.kotlin:kotlin-stdlib:1.3.50
org.jetbrains:annotations:13.0 org.jetbrains:annotations:13.0

View File

@@ -1,97 +1,51 @@
package org.nixos.gradle2nix package org.nixos.gradle2nix
import org.junit.jupiter.api.Test import dev.minutest.Tests
import org.junit.jupiter.api.io.TempDir import dev.minutest.junit.JUnit5Minutests
import java.io.File import dev.minutest.rootContext
import kotlin.test.assertEquals import org.gradle.api.internal.artifacts.dsl.DefaultRepositoryHandler.BINTRAY_JCENTER_URL
import org.gradle.api.internal.artifacts.dsl.DefaultRepositoryHandler.MAVEN_CENTRAL_URL
import strikt.api.expectThat
import strikt.assertions.all
import strikt.assertions.containsExactly
import strikt.assertions.get
import strikt.assertions.hasSize
import strikt.assertions.isEqualTo
import strikt.assertions.map
import strikt.assertions.startsWith
class BasicTest : JUnit5Minutests {
@Tests
fun tests() = rootContext<Fixture>("basic tests") {
withFixture("basic/basic-java-project") {
test("builds basic java project") {
expectThat(build()) {
get("gradle version") { gradle.version }.isEqualTo(System.getProperty("compat.gradle.version"))
class BasicTest { get("root project dependencies") { rootProject.projectDependencies }.and {
@TempDir lateinit var projectDir: File ids.containsExactly(
"com.squareup.moshi:moshi:1.8.0@jar",
"com.squareup.moshi:moshi:1.8.0@pom",
"com.squareup.moshi:moshi-parent:1.8.0@pom",
"com.squareup.okio:okio:2.2.2@jar",
"com.squareup.okio:okio:2.2.2@pom",
"org.jetbrains:annotations:13.0@jar",
"org.jetbrains:annotations:13.0@pom",
"org.jetbrains.kotlin:kotlin-stdlib:1.2.60@jar",
"org.jetbrains.kotlin:kotlin-stdlib:1.2.60@pom",
"org.jetbrains.kotlin:kotlin-stdlib-common:1.2.60@jar",
"org.jetbrains.kotlin:kotlin-stdlib-common:1.2.60@pom",
"org.sonatype.oss:oss-parent:7@pom"
)
@Test map { it.urls }.all {
fun `builds basic project with kotlin dsl`() { hasSize(2)
val model = projectDir.buildKotlin(""" get(0).startsWith(BINTRAY_JCENTER_URL)
plugins { get(1).startsWith(MAVEN_CENTRAL_URL)
java }
}
}
} }
repositories {
jcenter()
}
dependencies {
implementation("com.squareup.okio:okio:2.2.2")
implementation("com.squareup.moshi:moshi:1.8.0")
}
""".trimIndent())
assertEquals(model.gradle.version, System.getProperty("compat.gradle.version"))
with(model.rootProject.projectDependencies) {
with(repositories) {
assertEquals(1, maven.size)
assertEquals(maven[0].urls[0], "https://jcenter.bintray.com/")
}
assertArtifacts(
pom("com.squareup.moshi:moshi-parent:1.8.0"),
jar("com.squareup.moshi:moshi:1.8.0"),
pom("com.squareup.moshi:moshi:1.8.0"),
jar("com.squareup.okio:okio:2.2.2"),
pom("com.squareup.okio:okio:2.2.2"),
jar("org.jetbrains.kotlin:kotlin-stdlib-common:1.2.60"),
pom("org.jetbrains.kotlin:kotlin-stdlib-common:1.2.60"),
jar("org.jetbrains.kotlin:kotlin-stdlib:1.2.60"),
pom("org.jetbrains.kotlin:kotlin-stdlib:1.2.60"),
jar("org.jetbrains:annotations:13.0"),
pom("org.jetbrains:annotations:13.0"),
pom("org.sonatype.oss:oss-parent:7"),
actual = artifacts
)
}
}
@Test
fun `builds basic project with groovy dsl`() {
val model = projectDir.buildGroovy("""
plugins {
id("java")
}
repositories {
jcenter()
}
dependencies {
implementation 'com.squareup.okio:okio:2.2.2'
implementation 'com.squareup.moshi:moshi:1.8.0'
}
""".trimIndent())
assertEquals(model.gradle.version, System.getProperty("compat.gradle.version"))
with(model.rootProject.projectDependencies) {
with(repositories) {
assertEquals(1, maven.size)
assertEquals(maven[0].urls[0], "https://jcenter.bintray.com/")
}
assertArtifacts(
pom("com.squareup.moshi:moshi-parent:1.8.0"),
jar("com.squareup.moshi:moshi:1.8.0"),
pom("com.squareup.moshi:moshi:1.8.0"),
jar("com.squareup.okio:okio:2.2.2"),
pom("com.squareup.okio:okio:2.2.2"),
jar("org.jetbrains.kotlin:kotlin-stdlib-common:1.2.60"),
pom("org.jetbrains.kotlin:kotlin-stdlib-common:1.2.60"),
jar("org.jetbrains.kotlin:kotlin-stdlib:1.2.60"),
pom("org.jetbrains.kotlin:kotlin-stdlib:1.2.60"),
jar("org.jetbrains:annotations:13.0"),
pom("org.jetbrains:annotations:13.0"),
pom("org.sonatype.oss:oss-parent:7"),
actual = artifacts
)
} }
} }
} }

View File

@@ -0,0 +1,69 @@
package org.nixos.gradle2nix
import dev.minutest.Tests
import dev.minutest.junit.JUnit5Minutests
import dev.minutest.rootContext
import strikt.api.expectThat
import strikt.assertions.all
import strikt.assertions.contains
import strikt.assertions.containsExactly
import strikt.assertions.filter
import strikt.assertions.isEqualTo
import strikt.assertions.isNotEqualTo
import strikt.assertions.isNotNull
import strikt.assertions.single
import strikt.assertions.startsWith
class DependencyTest : JUnit5Minutests {
@Tests
fun tests() = rootContext<Fixture>("dependency tests") {
withFixture("dependency/classifier") {
test("resolves dependency with classifier") {
expectThat(build()) {
get("root project dependencies") { rootProject.projectDependencies }.ids.containsExactly(
"com.badlogicgames.gdx:gdx-parent:1.9.9@pom",
"com.badlogicgames.gdx:gdx-platform:1.9.9:natives-desktop@jar",
"com.badlogicgames.gdx:gdx-platform:1.9.9@pom",
"org.sonatype.oss:oss-parent:5@pom"
)
}
}
}
withFixture("dependency/dynamic-snapshot") {
test("resolves snapshot dependency with dynamic version") {
expectThat(build()) {
get("root project dependencies") { rootProject.projectDependencies }
.filter { it.id.name == "packr" }
.all {
get("id.version") { id.version }.isEqualTo("-SNAPSHOT")
get("timestamp") { timestamp }.isNotNull()
get("build") { build }.isNotNull()
}
}
}
}
withFixture("dependency/snapshot") {
test("resolves snapshot dependency") {
expectThat(build()) {
get("root project dependencies") { rootProject.projectDependencies }
.filter { it.id.name == "okio" }
.and {
ids.containsExactly(
"com.squareup.okio:okio:2.5.0-SNAPSHOT@jar",
"com.squareup.okio:okio:2.5.0-SNAPSHOT@module",
"com.squareup.okio:okio:2.5.0-SNAPSHOT@pom"
)
all {
get("timestamp") { timestamp }.isNotNull()
get("build") { build }.isNotNull()
get("urls") { urls }.single().startsWith(SONATYPE_OSS_URL)
}
}
}
}
}
}
}

View File

@@ -0,0 +1,33 @@
package org.nixos.gradle2nix
import dev.minutest.Tests
import dev.minutest.junit.JUnit5Minutests
import dev.minutest.rootContext
import strikt.api.expectThat
import strikt.assertions.all
import strikt.assertions.containsExactly
import strikt.assertions.map
import strikt.assertions.single
import strikt.assertions.startsWith
class IvyTest : JUnit5Minutests {
@Tests
fun tests() = rootContext<Fixture>("ivy tests") {
withFixture("ivy/basic") {
test("resolves ivy dependencies") {
expectThat(build()) {
get("root project dependencies") { rootProject.projectDependencies }.and {
ids.containsExactly(
"org.opendof.core-java:dof-cipher-sms4:1.0@jar",
"org.opendof.core-java:dof-oal:7.0.2@jar"
)
map { it.urls }.all {
single().startsWith("https://asset.opendof.org/artifact")
}
}
}
}
}
}
}

View File

@@ -0,0 +1,21 @@
package org.nixos.gradle2nix
import dev.minutest.Tests
import dev.minutest.junit.JUnit5Minutests
import dev.minutest.rootContext
import strikt.api.expectThat
import strikt.assertions.contains
class PluginTest : JUnit5Minutests {
@Tests
fun tests() = rootContext<Fixture>("plugin tests") {
withFixture("plugin/resolves-from-default-repo") {
test("resolves plugin from default repo") {
expectThat(build()) {
get("plugin dependencies") { pluginDependencies }.ids
.contains("org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin:1.3.50@pom")
}
}
}
}
}

View File

@@ -1,461 +1,151 @@
package org.nixos.gradle2nix package org.nixos.gradle2nix
import org.junit.jupiter.api.Test import dev.minutest.Tests
import org.junit.jupiter.api.io.TempDir import dev.minutest.junit.JUnit5Minutests
import dev.minutest.rootContext
import org.gradle.api.internal.artifacts.dsl.DefaultRepositoryHandler.BINTRAY_JCENTER_URL
import strikt.api.expectThat import strikt.api.expectThat
import strikt.assertions.all
import strikt.assertions.containsExactly
import strikt.assertions.containsExactlyInAnyOrder import strikt.assertions.containsExactlyInAnyOrder
import strikt.assertions.get
import strikt.assertions.hasSize
import strikt.assertions.isEqualTo
import strikt.assertions.map import strikt.assertions.map
import java.io.File import strikt.assertions.single
import kotlin.test.assertEquals import strikt.assertions.startsWith
class SubprojectsTest { class SubprojectsTest : JUnit5Minutests {
@TempDir @Tests
lateinit var root: File fun tests() = rootContext<Fixture>("subproject tests") {
withFixture("subprojects/multi-module") {
test("builds multi-module project") {
expectThat(build().rootProject) {
get("root project dependencies") { projectDependencies }.and {
ids.containsExactly(
"junit:junit:4.12@jar",
"junit:junit:4.12@pom",
"org.hamcrest:hamcrest-core:1.3@jar",
"org.hamcrest:hamcrest-core:1.3@pom",
"org.hamcrest:hamcrest-parent:1.3@pom"
)
all {
get("urls") { urls }.single().startsWith(BINTRAY_JCENTER_URL)
}
}
@Test get("children") { children }.and {
fun `builds multi-module project with kotlin dsl`() { hasSize(2)
root.resolve("child-a").also { it.mkdirs() }
.resolve("build.gradle.kts").writeText("""
plugins {
java
}
dependencies { get(0).and {
implementation("com.squareup.okio:okio:2.2.2") get("name") { name }.isEqualTo("child-a")
} get("projectDir") { projectDir }.isEqualTo("child-a")
""".trimIndent())
root.resolve("child-b").also { it.mkdirs() } get("child-a project dependencies") { projectDependencies }.and {
.resolve("build.gradle.kts").writeText(""" ids.containsExactly(
plugins { "com.squareup.okio:okio:2.2.2@jar",
java "com.squareup.okio:okio:2.2.2@pom",
} "org.jetbrains:annotations:13.0@jar",
"org.jetbrains:annotations:13.0@pom",
"org.jetbrains.kotlin:kotlin-stdlib:1.2.60@jar",
"org.jetbrains.kotlin:kotlin-stdlib:1.2.60@pom",
"org.jetbrains.kotlin:kotlin-stdlib-common:1.2.60@jar",
"org.jetbrains.kotlin:kotlin-stdlib-common:1.2.60@pom"
)
dependencies { all {
implementation(project(":child-a")) get("urls") { urls }.single().startsWith(BINTRAY_JCENTER_URL)
implementation("com.squareup.moshi:moshi:1.8.0") }
} }
""".trimIndent()) }
root.resolve("settings.gradle.kts").writeText(""" get(1).and {
include(":child-a", ":child-b") get("name") { name }.isEqualTo("child-b")
""".trimIndent()) get("projectDir") { projectDir }.isEqualTo("child-b")
val model = root.buildKotlin(""" get("child-b project dependencies") { projectDependencies }.and {
plugins { ids.containsExactly(
java "com.squareup.moshi:moshi:1.8.0@jar",
} "com.squareup.moshi:moshi:1.8.0@pom",
"com.squareup.moshi:moshi-parent:1.8.0@pom",
"com.squareup.okio:okio:1.16.0@jar", // compileClasspath
"com.squareup.okio:okio:1.16.0@pom", // compileClasspath
"com.squareup.okio:okio:2.2.2@jar", // runtimeClasspath
"com.squareup.okio:okio:2.2.2@pom", // runtimeClasspath
"com.squareup.okio:okio-parent:1.16.0@pom", // compileClasspath
"org.jetbrains:annotations:13.0@jar",
"org.jetbrains:annotations:13.0@pom",
"org.jetbrains.kotlin:kotlin-stdlib:1.2.60@jar",
"org.jetbrains.kotlin:kotlin-stdlib:1.2.60@pom",
"org.jetbrains.kotlin:kotlin-stdlib-common:1.2.60@jar",
"org.jetbrains.kotlin:kotlin-stdlib-common:1.2.60@pom",
"org.sonatype.oss:oss-parent:7@pom"
)
allprojects { all {
repositories { get("urls") { urls }.single().startsWith(BINTRAY_JCENTER_URL)
jcenter() }
}
}
}
} }
} }
dependencies { test("builds single subproject") {
testImplementation("junit:junit:4.12") expectThat(build(subprojects = listOf(":child-a")).rootProject) {
} get("root project dependencies") { projectDependencies }.and {
""".trimIndent()) ids.containsExactly(
"junit:junit:4.12@jar",
"junit:junit:4.12@pom",
"org.hamcrest:hamcrest-core:1.3@jar",
"org.hamcrest:hamcrest-core:1.3@pom",
"org.hamcrest:hamcrest-parent:1.3@pom"
)
with(model.rootProject) { all {
with(projectDependencies) { get("urls") { urls }.single().startsWith(BINTRAY_JCENTER_URL)
assertEquals(listOf(DefaultMaven(urls = listOf("https://jcenter.bintray.com/"))), }
repositories.maven) }
assertArtifacts( get("children") { children }.single().and {
jar("junit:junit:4.12"), get("name") { name }.isEqualTo("child-a")
pom("junit:junit:4.12"), get("projectDir") { projectDir }.isEqualTo("child-a")
jar("org.hamcrest:hamcrest-core:1.3"),
pom("org.hamcrest:hamcrest-core:1.3"),
pom("org.hamcrest:hamcrest-parent:1.3"),
actual = artifacts)
}
assertEquals(2, children.size) get("child-a project dependencies") { projectDependencies }.and {
ids.containsExactly(
"com.squareup.okio:okio:2.2.2@jar",
"com.squareup.okio:okio:2.2.2@pom",
"org.jetbrains:annotations:13.0@jar",
"org.jetbrains:annotations:13.0@pom",
"org.jetbrains.kotlin:kotlin-stdlib:1.2.60@jar",
"org.jetbrains.kotlin:kotlin-stdlib:1.2.60@pom",
"org.jetbrains.kotlin:kotlin-stdlib-common:1.2.60@jar",
"org.jetbrains.kotlin:kotlin-stdlib-common:1.2.60@pom"
)
with(children[0]) { all {
assertEquals("child-a", name) get("urls") { urls }.single().startsWith(BINTRAY_JCENTER_URL)
assertEquals(root.resolve("child-a").toRelativeString(root), projectDir) }
}
with(projectDependencies) { }
assertEquals(
listOf(DefaultMaven(urls = listOf("https://jcenter.bintray.com/"))),
repositories.maven
)
assertArtifacts(
jar("com.squareup.okio:okio:2.2.2"),
pom("com.squareup.okio:okio:2.2.2"),
jar("org.jetbrains.kotlin:kotlin-stdlib-common:1.2.60"),
pom("org.jetbrains.kotlin:kotlin-stdlib-common:1.2.60"),
jar("org.jetbrains.kotlin:kotlin-stdlib:1.2.60"),
pom("org.jetbrains.kotlin:kotlin-stdlib:1.2.60"),
jar("org.jetbrains:annotations:13.0"),
pom("org.jetbrains:annotations:13.0"),
actual = artifacts
)
}
}
with(children[1]) {
assertEquals("child-b", name)
assertEquals(root.resolve("child-b").toRelativeString(root), projectDir)
with(projectDependencies) {
assertEquals(
listOf(DefaultMaven(urls = listOf("https://jcenter.bintray.com/"))),
repositories.maven
)
assertArtifacts(
pom("com.squareup.moshi:moshi-parent:1.8.0"),
jar("com.squareup.moshi:moshi:1.8.0"),
pom("com.squareup.moshi:moshi:1.8.0"),
pom("com.squareup.okio:okio-parent:1.16.0"),
jar("com.squareup.okio:okio:1.16.0"),
pom("com.squareup.okio:okio:1.16.0"),
jar("com.squareup.okio:okio:2.2.2"),
pom("com.squareup.okio:okio:2.2.2"),
jar("org.jetbrains.kotlin:kotlin-stdlib-common:1.2.60"),
pom("org.jetbrains.kotlin:kotlin-stdlib-common:1.2.60"),
jar("org.jetbrains.kotlin:kotlin-stdlib:1.2.60"),
pom("org.jetbrains.kotlin:kotlin-stdlib:1.2.60"),
jar("org.jetbrains:annotations:13.0"),
pom("org.jetbrains:annotations:13.0"),
pom("org.sonatype.oss:oss-parent:7"),
actual = artifacts
)
} }
} }
} }
}
@Test withFixture("subprojects/dependent-subprojects") {
fun `builds multi-module project with groovy dsl`() { test("includes dependent subprojects") {
root.resolve("child-a").also { it.mkdirs() } expectThat(build(subprojects = listOf(":child-a"))) {
.resolve("build.gradle").writeText(""" get("children") { rootProject.children }
plugins { .map { it.path }
id 'java' .containsExactlyInAnyOrder(":child-a", ":child-b", ":child-c")
} }
dependencies { expectThat(build(subprojects = listOf(":child-b"))) {
implementation 'com.squareup.okio:okio:2.2.2' get("children") { rootProject.children }
} .map { it.path }
""".trimIndent()) .containsExactlyInAnyOrder(":child-b", ":child-c")
root.resolve("child-b").also { it.mkdirs() }
.resolve("build.gradle").writeText("""
plugins {
id 'java'
}
dependencies {
implementation project(':child-a')
implementation 'com.squareup.moshi:moshi:1.8.0'
}
""".trimIndent())
root.resolve("settings.gradle").writeText("""
include ':child-a', ':child-b'
""".trimIndent())
val model = root.buildGroovy("""
plugins {
id 'java'
}
allprojects {
repositories {
jcenter()
} }
} }
dependencies {
testImplementation 'junit:junit:4.12'
}
""".trimIndent())
with(model.rootProject) {
with(projectDependencies) {
assertEquals(listOf(DefaultMaven(urls = listOf("https://jcenter.bintray.com/"))),
repositories.maven)
assertArtifacts(
jar("junit:junit:4.12"),
pom("junit:junit:4.12"),
jar("org.hamcrest:hamcrest-core:1.3"),
pom("org.hamcrest:hamcrest-core:1.3"),
pom("org.hamcrest:hamcrest-parent:1.3"),
actual = artifacts)
}
assertEquals(2, children.size)
with(children[0]) {
assertEquals("child-a", name)
assertEquals(root.resolve("child-a").toRelativeString(root), projectDir)
with(projectDependencies) {
assertEquals(
listOf(DefaultMaven(urls = listOf("https://jcenter.bintray.com/"))),
repositories.maven
)
assertArtifacts(
jar("com.squareup.okio:okio:2.2.2"),
pom("com.squareup.okio:okio:2.2.2"),
jar("org.jetbrains.kotlin:kotlin-stdlib-common:1.2.60"),
pom("org.jetbrains.kotlin:kotlin-stdlib-common:1.2.60"),
jar("org.jetbrains.kotlin:kotlin-stdlib:1.2.60"),
pom("org.jetbrains.kotlin:kotlin-stdlib:1.2.60"),
jar("org.jetbrains:annotations:13.0"),
pom("org.jetbrains:annotations:13.0"),
actual = artifacts
)
}
}
with(children[1]) {
assertEquals("child-b", name)
assertEquals(root.resolve("child-b").toRelativeString(root), projectDir)
with(projectDependencies) {
assertEquals(listOf(DefaultMaven(urls = listOf("https://jcenter.bintray.com/"))),
repositories.maven)
assertArtifacts(
pom("com.squareup.moshi:moshi-parent:1.8.0"),
jar("com.squareup.moshi:moshi:1.8.0"),
pom("com.squareup.moshi:moshi:1.8.0"),
pom("com.squareup.okio:okio-parent:1.16.0"),
jar("com.squareup.okio:okio:1.16.0"),
pom("com.squareup.okio:okio:1.16.0"),
jar("com.squareup.okio:okio:2.2.2"),
pom("com.squareup.okio:okio:2.2.2"),
jar("org.jetbrains.kotlin:kotlin-stdlib-common:1.2.60"),
pom("org.jetbrains.kotlin:kotlin-stdlib-common:1.2.60"),
jar("org.jetbrains.kotlin:kotlin-stdlib:1.2.60"),
pom("org.jetbrains.kotlin:kotlin-stdlib:1.2.60"),
jar("org.jetbrains:annotations:13.0"),
pom("org.jetbrains:annotations:13.0"),
pom("org.sonatype.oss:oss-parent:7"),
actual = artifacts)
}
}
}
}
@Test
fun `builds single subproject in multi-module project with kotlin dsl`() {
root.resolve("child-a").also { it.mkdirs() }
.resolve("build.gradle.kts").writeText("""
plugins {
java
}
dependencies {
implementation("com.squareup.okio:okio:2.2.2")
}
""".trimIndent())
root.resolve("child-b").also { it.mkdirs() }
.resolve("build.gradle.kts").writeText("""
plugins {
java
}
dependencies {
implementation("com.squareup.moshi:moshi:1.8.0")
}
""".trimIndent())
root.resolve("settings.gradle.kts").writeText("""
include(":child-a", ":child-b")
""".trimIndent())
val model = root.buildKotlin("""
plugins {
java
}
allprojects {
repositories {
jcenter()
}
}
dependencies {
testImplementation("junit:junit:4.12")
}
""".trimIndent(),
subprojects = listOf(":child-a"))
with(model.rootProject) {
with(projectDependencies) {
assertEquals(listOf(DefaultMaven(urls = listOf("https://jcenter.bintray.com/"))),
repositories.maven)
assertArtifacts(
jar("junit:junit:4.12"),
pom("junit:junit:4.12"),
jar("org.hamcrest:hamcrest-core:1.3"),
pom("org.hamcrest:hamcrest-core:1.3"),
pom("org.hamcrest:hamcrest-parent:1.3"),
actual = artifacts)
}
assertEquals(1, children.size)
with(children[0]) {
assertEquals("child-a", name)
assertEquals(root.resolve("child-a").toRelativeString(root), projectDir)
with(projectDependencies) {
assertEquals(
listOf(DefaultMaven(urls = listOf("https://jcenter.bintray.com/"))),
repositories.maven
)
assertArtifacts(
jar("com.squareup.okio:okio:2.2.2"),
pom("com.squareup.okio:okio:2.2.2"),
jar("org.jetbrains.kotlin:kotlin-stdlib-common:1.2.60"),
pom("org.jetbrains.kotlin:kotlin-stdlib-common:1.2.60"),
jar("org.jetbrains.kotlin:kotlin-stdlib:1.2.60"),
pom("org.jetbrains.kotlin:kotlin-stdlib:1.2.60"),
jar("org.jetbrains:annotations:13.0"),
pom("org.jetbrains:annotations:13.0"),
actual = artifacts
)
}
}
}
}
@Test
fun `builds single subproject in multi-module project with groovy dsl`() {
root.resolve("child-a").also { it.mkdirs() }
.resolve("build.gradle").writeText("""
plugins {
id 'java'
}
dependencies {
implementation 'com.squareup.okio:okio:2.2.2'
}
""".trimIndent())
root.resolve("child-b").also { it.mkdirs() }
.resolve("build.gradle").writeText("""
plugins {
id 'java'
}
dependencies {
implementation 'com.squareup.moshi:moshi:1.8.0'
}
""".trimIndent())
root.resolve("settings.gradle").writeText("""
include ':child-a', ':child-b'
""".trimIndent())
val model = root.buildGroovy("""
plugins {
id 'java'
}
allprojects {
repositories {
jcenter()
}
}
dependencies {
testImplementation 'junit:junit:4.12'
}
""".trimIndent(),
subprojects = listOf(":child-a"))
with(model.rootProject) {
with(projectDependencies) {
assertEquals(listOf(DefaultMaven(urls = listOf("https://jcenter.bintray.com/"))),
repositories.maven)
assertArtifacts(
jar("junit:junit:4.12"),
pom("junit:junit:4.12"),
jar("org.hamcrest:hamcrest-core:1.3"),
pom("org.hamcrest:hamcrest-core:1.3"),
pom("org.hamcrest:hamcrest-parent:1.3"),
actual = artifacts)
}
assertEquals(1, children.size)
with(children[0]) {
assertEquals("child-a", name)
assertEquals(root.resolve("child-a").toRelativeString(root), projectDir)
with(projectDependencies) {
assertEquals(
listOf(DefaultMaven(urls = listOf("https://jcenter.bintray.com/"))),
repositories.maven
)
assertArtifacts(
jar("com.squareup.okio:okio:2.2.2"),
pom("com.squareup.okio:okio:2.2.2"),
jar("org.jetbrains.kotlin:kotlin-stdlib-common:1.2.60"),
pom("org.jetbrains.kotlin:kotlin-stdlib-common:1.2.60"),
jar("org.jetbrains.kotlin:kotlin-stdlib:1.2.60"),
pom("org.jetbrains.kotlin:kotlin-stdlib:1.2.60"),
jar("org.jetbrains:annotations:13.0"),
pom("org.jetbrains:annotations:13.0"),
actual = artifacts
)
}
}
}
}
@Test
fun `includes subproject dependencies`() {
root.resolve("child-a").also { it.mkdirs() }.resolve("build.gradle.kts").writeText("")
root.resolve("child-b").also { it.mkdirs() }.resolve("build.gradle.kts").writeText("")
root.resolve("child-c").also { it.mkdirs() }.resolve("build.gradle.kts").writeText("")
root.resolve("child-d").also { it.mkdirs() }.resolve("build.gradle.kts").writeText("")
root.resolve("settings.gradle.kts").writeText("""
include(":child-a", ":child-b", ":child-c", ":child-d")
""".trimIndent())
val buildscript = """
subprojects {
apply(plugin = "java")
}
project(":child-a") {
dependencies {
"implementation"(project(":child-b"))
}
}
project(":child-b") {
dependencies {
"implementation"(project(":child-c"))
}
}
""".trimIndent()
with(root.buildKotlin(buildscript, subprojects = listOf(":child-a"))) {
expectThat(rootProject.children).map { it.path }
.containsExactlyInAnyOrder(":child-a", ":child-b", ":child-c")
}
with(root.buildKotlin(buildscript, subprojects = listOf(":child-b"))) {
expectThat(rootProject.children).map { it.path }.containsExactlyInAnyOrder(":child-b", ":child-c")
} }
} }
} }

View File

@@ -1,20 +1,33 @@
package org.nixos.gradle2nix package org.nixos.gradle2nix
import com.squareup.moshi.Moshi import com.squareup.moshi.Moshi
import dev.minutest.ContextBuilder
import dev.minutest.MinutestFixture
import dev.minutest.TestContextBuilder
import okio.buffer import okio.buffer
import okio.source import okio.source
import org.gradle.api.internal.artifacts.dsl.ParsedModuleStringNotation
import org.gradle.internal.classpath.DefaultClassPath import org.gradle.internal.classpath.DefaultClassPath
import org.gradle.testkit.runner.GradleRunner import org.gradle.testkit.runner.GradleRunner
import org.gradle.testkit.runner.internal.DefaultGradleRunner
import org.gradle.testkit.runner.internal.PluginUnderTestMetadataReading import org.gradle.testkit.runner.internal.PluginUnderTestMetadataReading
import org.gradle.tooling.GradleConnector import org.gradle.util.GradleVersion
import org.gradle.tooling.events.ProgressListener import org.junit.jupiter.api.Assumptions.assumeTrue
import strikt.api.Assertion
import strikt.assertions.map
import java.io.File import java.io.File
import kotlin.test.assertTrue import java.io.StringWriter
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
import kotlin.streams.toList
const val SONATYPE_OSS_URL = "https://oss.sonatype.org/content/repositories/snapshots/"
private val moshi = Moshi.Builder().build() private val moshi = Moshi.Builder().build()
private val gradleVersion = GradleVersion.version(System.getProperty("compat.gradle.version"))
val GRADLE_4_5 = GradleVersion.version("4.5")
private fun File.initscript() = resolve("init.gradle").also { private fun File.initscript() = resolve("init.gradle").also {
it.writer().use { out -> it.writer().use { out ->
val classpath = DefaultClassPath.of(PluginUnderTestMetadataReading.readImplementationClasspath()) val classpath = DefaultClassPath.of(PluginUnderTestMetadataReading.readImplementationClasspath())
@@ -31,20 +44,12 @@ private fun File.initscript() = resolve("init.gradle").also {
} }
} }
fun File.buildGroovy(
script: String,
configurations: List<String> = emptyList(),
subprojects: List<String> = emptyList()
): DefaultBuild {
resolve("build.gradle").writeText(script)
return build(configurations, subprojects)
}
fun File.buildKotlin( fun File.buildKotlin(
script: String, script: String,
configurations: List<String> = emptyList(), configurations: List<String> = emptyList(),
subprojects: List<String> = emptyList() subprojects: List<String> = emptyList()
): DefaultBuild { ): DefaultBuild {
assumeTrue(gradleVersion >= GRADLE_4_5)
resolve("build.gradle.kts").writeText(script) resolve("build.gradle.kts").writeText(script)
return build(configurations, subprojects) return build(configurations, subprojects)
} }
@@ -53,10 +58,13 @@ private fun File.build(
configurations: List<String>, configurations: List<String>,
subprojects: List<String> subprojects: List<String>
): DefaultBuild { ): DefaultBuild {
GradleRunner.create() val log = StringWriter()
.withGradleVersion(System.getProperty("compat.gradle.version"))
val result = GradleRunner.create()
.withGradleVersion(gradleVersion.version)
.withProjectDir(this) .withProjectDir(this)
.forwardOutput() .forwardStdOutput(log)
.forwardStdError(log)
.withArguments( .withArguments(
"nixModel", "nixModel",
"--init-script=${initscript()}", "--init-script=${initscript()}",
@@ -64,8 +72,12 @@ private fun File.build(
"-Porg.nixos.gradle2nix.configurations=${configurations.joinToString(",")}", "-Porg.nixos.gradle2nix.configurations=${configurations.joinToString(",")}",
"-Porg.nixos.gradle2nix.subprojects=${subprojects.joinToString(",")}" "-Porg.nixos.gradle2nix.subprojects=${subprojects.joinToString(",")}"
) )
.build() .runCatching { build() }
result.onFailure { error ->
System.err.print(log)
throw error
}
return resolve("build/nix/model.json").run { return resolve("build/nix/model.json").run {
println(readText()) println(readText())
source().buffer().use { src -> source().buffer().use { src ->
@@ -73,75 +85,48 @@ private fun File.build(
} }
} }
} }
//
// return GradleConnector.newConnector()
// .useGradleVersion(System.getProperty("compat.gradle.version"))
// .forProjectDirectory(this)
// .connect()
// .model(Build::class.java).apply {
// addArguments("--init-script=${initscript()}", "--stacktrace")
// addJvmArguments(
// "-Dorg.gradle.debug=true",
// "-Dorg.nixos.gradle2nix.configurations=${configurations.joinToString(",")}",
// "-Dorg.nixos.gradle2nix.subprojects=${subprojects.joinToString(",")}"
// )
// setStandardOutput(System.out)
// setStandardError(System.out)
// }
// .get()
// .let { DefaultBuild(it) }
fun jar(notation: String, sha256: String = ""): DefaultArtifact = val <T : Iterable<Artifact>> Assertion.Builder<T>.ids: Assertion.Builder<Iterable<String>>
artifact(notation, sha256, "jar") get() = map { it.id.toString() }
fun pom(notation: String, sha256: String = ""): DefaultArtifact = @MinutestFixture
artifact(notation, sha256, "pom") class Fixture(val testRoots: List<Path>)
private fun artifact(notation: String, sha256: String, type: String): DefaultArtifact { @MinutestFixture
val parsed = ParsedModuleStringNotation(notation, type) class ProjectFixture(val testRoot: Path) {
return DefaultArtifact( fun build(
groupId = parsed.group ?: "", configurations: List<String> = emptyList(),
artifactId = parsed.name ?: "", subprojects: List<String> = emptyList()
version = parsed.version ?: "", ) = testRoot.toFile().build(configurations, subprojects)
classifier = parsed.classifier ?: "",
extension = type,
sha256 = sha256
)
} }
private fun artifactEquals(expected: DefaultArtifact, actual: DefaultArtifact?): Boolean { fun ContextBuilder<Fixture>.withFixture(
return actual != null && with (expected) { name: String,
groupId == actual.groupId && block: TestContextBuilder<Fixture, ProjectFixture>.() -> Unit
artifactId == actual.artifactId && ) = context(name) {
version == actual.version && val url = checkNotNull(Thread.currentThread().contextClassLoader.getResource(name)?.toURI()) {
classifier == actual.classifier && "$name: No test fixture found"
extension == actual.extension &&
(sha256.takeIf { it.isNotEmpty() }?.equals(actual.sha256) ?: true)
} }
} val fixtureRoot = Paths.get(url)
val dest = createTempDir("gradle2nix").toPath()
val src = checkNotNull(fixtureRoot.takeIf(Files::exists)) {
"$name: Test fixture not found: $fixtureRoot}"
}
src.toFile().copyRecursively(dest.toFile())
val testRoots = Files.list(dest).filter { Files.isDirectory(it) }.toList()
fun assertArtifacts(vararg expected: DefaultArtifact, actual: List<DefaultArtifact>) { fixture {
val mismatches = mutableListOf<Mismatch>() Fixture(testRoots)
val remaining = mutableListOf<DefaultArtifact>().also { it.addAll(actual) } }
expected.forEachIndexed { i: Int, exp: DefaultArtifact ->
val act = actual.elementAtOrNull(i) afterAll {
if (!artifactEquals(exp, act)) { dest.toFile().deleteRecursively()
mismatches += Mismatch(i, exp, act) }
} else if (act != null) {
remaining -= act testRoots.forEach { testRoot ->
derivedContext<ProjectFixture>(testRoot.fileName.toString()) {
deriveFixture { ProjectFixture(testRoot) }
block()
} }
} }
assertTrue(mismatches.isEmpty() && remaining.isEmpty(), """
Artifact mismatches:
${mismatches.joinToString("\n ", prefix = " ")}
Missing artifacts:
${remaining.joinToString("\n ", prefix = " ")}
""")
} }
data class Mismatch(
val index: Int,
val expected: DefaultArtifact,
val actual: DefaultArtifact?
)

View File

@@ -1,22 +1,25 @@
package org.nixos.gradle2nix package org.nixos.gradle2nix
import org.junit.jupiter.api.Test import dev.minutest.Tests
import org.junit.jupiter.api.io.TempDir import dev.minutest.junit.JUnit5Minutests
import dev.minutest.rootContext
import strikt.api.expectThat
import strikt.assertions.isEqualTo
import java.io.File import java.io.File
import kotlin.test.assertEquals
class WrapperTest { class WrapperTest : JUnit5Minutests {
@TempDir @Tests
lateinit var root: File fun tests() = rootContext<File>("wrapper tests") {
fixture { createTempDir("gradle2nix") }
@Test test("resolves gradle wrapper version") {
fun `resolves gradle version from wrapper configuration`() { expectThat(buildKotlin("""
val model = root.buildKotlin(""" tasks.withType<org.gradle.api.tasks.wrapper.Wrapper> {
tasks.withType<org.gradle.api.tasks.wrapper.Wrapper> { gradleVersion = "5.5.1"
gradleVersion = "5.5.1" }
""".trimIndent())) {
get("gradle version") { gradle.version }.isEqualTo("5.5.1")
} }
""".trimIndent()) }
assertEquals(model.gradle.version, "5.5.1")
} }
} }

View File

@@ -0,0 +1,255 @@
package org.nixos.gradle2nix
import org.apache.ivy.Ivy
import org.apache.ivy.core.settings.IvySettings
import org.apache.ivy.plugins.parser.m2.PomReader
import org.apache.ivy.plugins.parser.xml.XmlModuleDescriptorParser
import org.apache.ivy.plugins.repository.url.URLResource
import org.apache.ivy.plugins.resolver.ChainResolver
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.ModuleIdentifier
import org.gradle.api.artifacts.ResolvedArtifact
import org.gradle.api.artifacts.component.ComponentArtifactIdentifier
import org.gradle.api.artifacts.component.ModuleComponentIdentifier
import org.gradle.api.artifacts.dsl.DependencyHandler
import org.gradle.api.artifacts.dsl.RepositoryHandler
import org.gradle.api.artifacts.query.ArtifactResolutionQuery
import org.gradle.api.artifacts.result.ResolvedArtifactResult
import org.gradle.api.internal.artifacts.repositories.ResolutionAwareRepository
import org.gradle.ivy.IvyDescriptorArtifact
import org.gradle.ivy.IvyModule
import org.gradle.kotlin.dsl.getArtifacts
import org.gradle.kotlin.dsl.withArtifacts
import org.gradle.maven.MavenModule
import org.gradle.maven.MavenPomArtifact
import org.gradle.util.GradleVersion
import java.io.File
internal class ConfigurationResolverFactory(repositories: RepositoryHandler) {
private val ivySettings = IvySettings().apply {
defaultInit()
setDefaultRepositoryCacheBasedir(createTempDir("gradle2nix-cache").apply(File::deleteOnExit).absolutePath)
setDictatorResolver(ChainResolver().also { chain ->
chain.settings = this@apply
for (resolver in resolvers) chain.add(resolver)
})
}
private val resolvers = repositories.filterIsInstance<ResolutionAwareRepository>()
.mapNotNull { it.repositoryResolver(ivySettings) }
fun create(dependencies: DependencyHandler): ConfigurationResolver =
ConfigurationResolver(ivySettings, resolvers, dependencies)
}
internal class ConfigurationResolver(
ivySettings: IvySettings,
private val resolvers: List<RepositoryResolver>,
private val dependencies: DependencyHandler
) {
private val ivy = Ivy.newInstance(ivySettings)
fun resolve(configuration: Configuration): List<DefaultArtifact> {
val resolved = configuration.resolvedConfiguration
val topLevelMetadata = resolved.firstLevelModuleDependencies
.flatMap { resolveMetadata(it.moduleGroup, it.moduleName, it.moduleVersion) }
val allArtifacts = resolved.resolvedArtifacts
.filter { it.id.componentIdentifier is ModuleComponentIdentifier }
.flatMap(::resolve)
return (topLevelMetadata + allArtifacts).filter { it.urls.isNotEmpty() }
}
private fun resolve(resolvedArtifact: ResolvedArtifact): List<DefaultArtifact> {
val componentId = resolvedArtifact.id.componentIdentifier as ModuleComponentIdentifier
val artifactId = DefaultArtifactIdentifier(
group = componentId.group,
name = componentId.module,
version = componentId.version,
type = resolvedArtifact.type,
extension = resolvedArtifact.extension,
classifier = resolvedArtifact.classifier
)
val sha256 = resolvedArtifact.file.sha256()
val artifacts = resolvers.mapNotNull { it.resolve(artifactId, sha256) }.merge()
return artifacts + componentId.run { resolveMetadata(group, module, version) }
}
private fun resolveMetadata(
group: String,
name: String,
version: String
): List<DefaultArtifact> {
return resolvePoms(group, name, version) +
resolveDescriptors(group, name, version) +
resolveGradleMetadata(group, name, version)
}
private fun resolvePoms(
group: String,
name: String,
version: String
): List<DefaultArtifact> {
return dependencies.createArtifactResolutionQuery()
.forModuleCompat(group, name, version)
.withArtifacts(MavenModule::class, MavenPomArtifact::class)
.execute()
.resolvedComponents
.flatMap { it.getArtifacts(MavenPomArtifact::class) }
.filterIsInstance<ResolvedArtifactResult>()
.flatMap { it.withParentPoms() }
.flatMap { resolvedPom ->
val componentId = resolvedPom.id.componentIdentifier as ModuleComponentIdentifier
val artifactId = DefaultArtifactIdentifier(
group = componentId.group,
name = componentId.module,
version = componentId.version,
type = "pom"
)
val sha256 = resolvedPom.file.sha256()
resolvers.mapNotNull { it.resolve(artifactId, sha256) }.merge()
}
}
private fun resolveDescriptors(
group: String,
name: String,
version: String
): List<DefaultArtifact> {
return dependencies.createArtifactResolutionQuery()
.forModuleCompat(group, name, version)
.withArtifacts(IvyModule::class, IvyDescriptorArtifact::class)
.execute()
.resolvedComponents
.flatMap { it.getArtifacts(IvyDescriptorArtifact::class) }
.filterIsInstance<ResolvedArtifactResult>()
.flatMap { it.withParentDescriptors() }
.flatMap { resolvedDesc ->
val componentId = resolvedDesc.id.componentIdentifier as ModuleComponentIdentifier
val artifactId = DefaultArtifactIdentifier(
group = componentId.group,
name = componentId.module,
version = componentId.version,
type = "ivy",
extension = "xml"
)
val sha256 = resolvedDesc.file.sha256()
resolvers.mapNotNull { it.resolve(artifactId, sha256) }.merge()
}
}
private fun resolveGradleMetadata(
group: String,
name: String,
version: String
): List<DefaultArtifact> {
val artifactId = DefaultArtifactIdentifier(
group = group,
name = name,
version = version,
type = "module"
)
return resolvers.mapNotNull { it.resolve(artifactId) }.merge()
}
private fun ResolvedArtifactResult.parentPom(): ResolvedArtifactResult? {
val resource = URLResource(file.toURI().toURL())
val reader = PomReader(resource.url, resource)
return if (reader.hasParent()) {
dependencies.createArtifactResolutionQuery()
.forModuleCompat(reader.parentGroupId, reader.parentArtifactId, reader.parentVersion)
.withArtifacts(MavenModule::class, MavenPomArtifact::class)
.execute()
.resolvedComponents
.flatMap { it.getArtifacts(MavenPomArtifact::class) }
.filterIsInstance<ResolvedArtifactResult>()
.firstOrNull()
} else {
null
}
}
private fun ResolvedArtifactResult.withParentPoms(): List<ResolvedArtifactResult> =
generateSequence(this) { it.parentPom() }.toList()
private fun ResolvedArtifactResult.parentDescriptors(seen: Set<ComponentArtifactIdentifier>): List<ResolvedArtifactResult> {
val url = file.toURI().toURL()
val parser = XmlModuleDescriptorParser.getInstance()
val descriptor = parser.parseDescriptor(ivy.settings, url, false)
return descriptor.inheritedDescriptors.mapNotNull { desc ->
dependencies.createArtifactResolutionQuery()
.forModuleCompat(
desc.parentRevisionId.organisation,
desc.parentRevisionId.name,
desc.parentRevisionId.revision
)
.withArtifacts(IvyModule::class, IvyDescriptorArtifact::class)
.execute()
.resolvedComponents
.flatMap { it.getArtifacts(IvyDescriptorArtifact::class) }
.filterIsInstance<ResolvedArtifactResult>()
.firstOrNull()
}.filter { it.id !in seen }
}
private fun ResolvedArtifactResult.withParentDescriptors(): List<ResolvedArtifactResult> {
val seen = mutableSetOf<ComponentArtifactIdentifier>()
return generateSequence(listOf(this)) { descs ->
val parents = descs.flatMap { it.parentDescriptors(seen) }
seen.addAll(parents.map(ResolvedArtifactResult::id))
parents.takeUnless { it.isEmpty() }
}.flatten().distinct().toList()
}
}
private fun ArtifactResolutionQuery.forModuleCompat(
group: String,
name: String,
version: String
): ArtifactResolutionQuery {
return if (GradleVersion.current() >= GradleVersion.version("4.5")) {
forModule(group, name, version)
} else {
forComponents(ModuleComponentId(group, name, version))
}
}
private data class ModuleComponentId(
private val moduleId: ModuleId,
private val version: String
) : ModuleComponentIdentifier {
constructor(
group: String,
name: String,
version: String
) : this(ModuleId(group, name), version)
override fun getGroup(): String = moduleId.group
override fun getModule(): String = moduleId.name
override fun getVersion(): String = version
override fun getModuleIdentifier(): ModuleIdentifier = moduleId
override fun getDisplayName(): String =
arrayOf(group, module, version).joinToString(":")
}
private data class ModuleId(
private val group: String,
private val name: String
) : ModuleIdentifier {
override fun getGroup(): String = group
override fun getName(): String = name
}
private fun List<DefaultArtifact>.merge(): List<DefaultArtifact> {
return groupingBy { it.id }
.reduce { _, dest, next -> dest.copy(urls = dest.urls + next.urls) }
.values.toList()
}

View File

@@ -1,169 +0,0 @@
package org.nixos.gradle2nix
import org.apache.maven.model.Parent
import org.apache.maven.model.Repository
import org.apache.maven.model.building.DefaultModelBuilderFactory
import org.apache.maven.model.building.DefaultModelBuildingRequest
import org.apache.maven.model.building.ModelBuildingRequest
import org.apache.maven.model.building.ModelSource2
import org.apache.maven.model.resolution.ModelResolver
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.ConfigurationContainer
import org.gradle.api.artifacts.Dependency
import org.gradle.api.artifacts.component.ModuleComponentIdentifier
import org.gradle.api.artifacts.component.ProjectComponentIdentifier
import org.gradle.api.artifacts.dsl.DependencyHandler
import org.gradle.api.artifacts.result.ResolvedArtifactResult
import org.gradle.api.logging.Logger
import org.gradle.api.logging.Logging
import org.gradle.maven.MavenModule
import org.gradle.maven.MavenPomArtifact
import java.io.File
import java.io.InputStream
import java.net.URI
import java.security.MessageDigest
internal class DependencyResolver(
private val configurations: ConfigurationContainer,
private val dependencies: DependencyHandler,
private val logger: Logger = Logging.getLogger(DependencyResolver::class.simpleName)
) {
private val mavenPomResolver = MavenPomResolver(configurations, dependencies)
fun resolveDependencies(configuration: Configuration): Set<DefaultArtifact> {
if (!configuration.isCanBeResolved) {
logger.warn("Cannot resolve configuration ${configuration.name}; ignoring.")
return emptySet()
}
return configuration.resolvedConfiguration.resolvedArtifacts
.filterNot { it.id.componentIdentifier is ProjectComponentIdentifier }
.mapTo(sortedSetOf()) {
with (it) {
DefaultArtifact(
groupId = moduleVersion.id.group,
artifactId = moduleVersion.id.name,
version = moduleVersion.id.version,
classifier = classifier ?: "",
extension = extension,
sha256 = sha256(file)
)
}
}
}
fun resolveDependencies(
dependencies: Collection<Dependency>,
includeTransitive: Boolean = false
): Set<DefaultArtifact> {
val configuration = configurations.detachedConfiguration(*(dependencies.toTypedArray()))
configuration.isTransitive = includeTransitive
return resolveDependencies(configuration)
}
fun resolvePoms(configuration: Configuration): Set<DefaultArtifact> {
return dependencies.createArtifactResolutionQuery()
.forComponents(configuration.incoming.resolutionResult.allComponents.map { it.id })
.withArtifacts(MavenModule::class.java, MavenPomArtifact::class.java)
.execute()
.resolvedComponents.asSequence()
.flatMap { component ->
val id = component.id
if (id !is ModuleComponentIdentifier) {
emptySequence()
} else {
component.getArtifacts(MavenPomArtifact::class.java).asSequence()
.filterIsInstance<ResolvedArtifactResult>()
.map { id to it }
}
}
.flatMapTo(sortedSetOf()) { (id, artifact) ->
sequenceOf(DefaultArtifact(
groupId = id.group,
artifactId = id.module,
version = id.version,
classifier = "",
extension = artifact.file.extension,
sha256 = sha256(artifact.file)
)) + mavenPomResolver.resolve(artifact.file).asSequence()
}
}
fun resolvePoms(
dependencies: Collection<Dependency>,
includeTransitive: Boolean = false
): Set<DefaultArtifact> {
val configuration = configurations.detachedConfiguration(*(dependencies.toTypedArray()))
configuration.isTransitive = includeTransitive
return resolvePoms(configuration)
}
}
private class MavenPomResolver(
private val configurations: ConfigurationContainer,
private val dependencies: DependencyHandler
) : ModelResolver {
private val modelBuilder = DefaultModelBuilderFactory().newInstance()
private val resolvedDependencies = mutableSetOf<DefaultArtifact>()
@Synchronized
fun resolve(pom: File): Set<DefaultArtifact> {
resolvedDependencies.clear()
modelBuilder.build(
DefaultModelBuildingRequest()
.setModelResolver(this)
.setPomFile(pom)
.setSystemProperties(System.getProperties())
.setValidationLevel(ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL)
).effectiveModel
return resolvedDependencies.toSet()
}
override fun newCopy() = this
override fun resolveModel(
groupId: String,
artifactId: String,
version: String
): ModelSource2 {
val file = configurations
.detachedConfiguration(dependencies.create("$groupId:$artifactId:$version@pom"))
.singleFile
resolvedDependencies.add(DefaultArtifact(
groupId = groupId,
artifactId = artifactId,
version = version,
classifier = "",
extension = file.extension,
sha256 = sha256(file)
))
return object : ModelSource2 {
override fun getLocation(): String = file.absolutePath
override fun getLocationURI(): URI = file.absoluteFile.toURI()
override fun getRelatedSource(relPath: String?): ModelSource2? = null
override fun getInputStream(): InputStream = file.inputStream()
}
}
override fun resolveModel(parent: Parent): ModelSource2 =
resolveModel(parent.groupId, parent.artifactId, parent.version)
override fun resolveModel(dependency: org.apache.maven.model.Dependency): ModelSource2 =
resolveModel(dependency.groupId, dependency.artifactId, dependency.version)
override fun addRepository(repository: Repository) {}
override fun addRepository(repository: Repository, replace: Boolean) {}
}
private const val HEX = "0123456789abcdef"
private fun sha256(file: File): String = buildString {
MessageDigest.getInstance("SHA-256").digest(file.readBytes())
.asSequence()
.map { it.toInt() }
.forEach {
append(HEX[it shr 4 and 0x0f])
append(HEX[it and 0x0f])
}
}

View File

@@ -1,19 +1,16 @@
package org.nixos.gradle2nix package org.nixos.gradle2nix
import com.squareup.moshi.Moshi import com.squareup.moshi.Moshi
import okio.buffer
import okio.sink
import org.gradle.api.Plugin import org.gradle.api.Plugin
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.artifacts.ArtifactRepositoryContainer import org.gradle.api.Task
import org.gradle.api.artifacts.Configuration import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.ProjectDependency import org.gradle.api.artifacts.ProjectDependency
import org.gradle.api.artifacts.dsl.RepositoryHandler
import org.gradle.api.artifacts.repositories.MavenArtifactRepository
import org.gradle.api.internal.GradleInternal
import org.gradle.api.invocation.Gradle import org.gradle.api.invocation.Gradle
import org.gradle.api.tasks.TaskContainer
import org.gradle.api.tasks.wrapper.Wrapper import org.gradle.api.tasks.wrapper.Wrapper
import org.gradle.kotlin.dsl.getByName import org.gradle.kotlin.dsl.getByName
import org.gradle.kotlin.dsl.newInstance
import org.gradle.kotlin.dsl.support.serviceOf import org.gradle.kotlin.dsl.support.serviceOf
import org.gradle.kotlin.dsl.withType import org.gradle.kotlin.dsl.withType
import org.gradle.plugin.management.PluginRequest import org.gradle.plugin.management.PluginRequest
@@ -33,15 +30,17 @@ open class Gradle2NixPlugin : Plugin<Gradle> {
rootProject.serviceOf<ToolingModelBuilderRegistry>() rootProject.serviceOf<ToolingModelBuilderRegistry>()
.register(NixToolingModelBuilder(modelProperties, pluginRequests)) .register(NixToolingModelBuilder(modelProperties, pluginRequests))
rootProject.tasks.register("nixModel") { rootProject.tasks.registerCompat("nixModel") {
doLast { doLast {
val outFile = project.mkdir(project.buildDir.resolve("nix")).resolve("model.json") val outFile = project.mkdir(project.buildDir.resolve("nix")).resolve("model.json")
val model = project.buildModel(modelProperties, pluginRequests) val model = project.buildModel(modelProperties, pluginRequests)
outFile.sink().buffer().use { out -> outFile.bufferedWriter().use { out ->
Moshi.Builder().build() out.write(
.adapter(DefaultBuild::class.java) Moshi.Builder().build()
.indent(" ") .adapter(DefaultBuild::class.java)
.toJson(out, model) .indent(" ")
.toJson(model)
)
out.flush() out.flush()
} }
} }
@@ -50,7 +49,13 @@ open class Gradle2NixPlugin : Plugin<Gradle> {
} }
} }
private const val NIX_MODEL_NAME = "org.nixos.gradle2nix.Build" private fun TaskContainer.registerCompat(name: String, configureAction: Task.() -> Unit) {
if (GradleVersion.current() >= GradleVersion.version("4.9")) {
register(name, configureAction)
} else {
create(name, configureAction)
}
}
private class NixToolingModelBuilder( private class NixToolingModelBuilder(
private val modelProperties: ModelProperties, private val modelProperties: ModelProperties,
@@ -100,6 +105,7 @@ private fun Project.buildModel(
) )
} }
@Suppress("UnstableApiUsage")
private fun Project.buildGradle(): DefaultGradle = private fun Project.buildGradle(): DefaultGradle =
with(tasks.getByName<Wrapper>("wrapper")) { with(tasks.getByName<Wrapper>("wrapper")) {
DefaultGradle( DefaultGradle(
@@ -113,18 +119,16 @@ private fun Project.buildGradle(): DefaultGradle =
} }
?: throw IllegalStateException( ?: throw IllegalStateException(
""" """
Failed to find native-platform jar in ${gradle.gradleHomeDir}. Failed to find native-platform jar in ${gradle.gradleHomeDir}.
Ask Tad to fix this.
Ask Tad to fix this. """.trimIndent()
""".trimIndent()
) )
) )
} }
private fun Project.buildPlugins(pluginRequests: List<PluginRequest>): DefaultDependencies = private fun Project.buildPlugins(pluginRequests: List<PluginRequest>): List<DefaultArtifact> {
with(PluginResolver(gradle as GradleInternal, pluginRequests)) { return objects.newInstance<PluginResolver>().resolve(pluginRequests).distinct().sorted()
DefaultDependencies(repositories.repositories(), artifacts()) }
}
private fun Project.includedBuilds(): List<DefaultIncludedBuild> = private fun Project.includedBuilds(): List<DefaultIncludedBuild> =
gradle.includedBuilds.map { gradle.includedBuilds.map {
@@ -134,7 +138,7 @@ private fun Project.includedBuilds(): List<DefaultIncludedBuild> =
private fun Project.buildProject( private fun Project.buildProject(
explicitConfigurations: List<String>, explicitConfigurations: List<String>,
explicitSubprojects: Collection<Project>, explicitSubprojects: Collection<Project>,
plugins: DefaultDependencies pluginArtifacts: List<DefaultArtifact>
): DefaultProject { ): DefaultProject {
logger.lifecycle(" Subproject: $path") logger.lifecycle(" Subproject: $path")
return DefaultProject( return DefaultProject(
@@ -142,34 +146,33 @@ private fun Project.buildProject(
version = version.toString(), version = version.toString(),
path = path, path = path,
projectDir = projectDir.toRelativeString(rootProject.projectDir), projectDir = projectDir.toRelativeString(rootProject.projectDir),
buildscriptDependencies = buildscriptDependencies(plugins), buildscriptDependencies = buildscriptDependencies(pluginArtifacts),
projectDependencies = projectDependencies(explicitConfigurations), projectDependencies = projectDependencies(explicitConfigurations),
children = explicitSubprojects.map { it.buildProject(explicitConfigurations, emptyList(), plugins) } children = explicitSubprojects.map {
it.buildProject(explicitConfigurations, emptyList(), pluginArtifacts)
}
) )
} }
private fun Project.buildscriptDependencies(plugins: DefaultDependencies): DefaultDependencies = private fun Project.buildscriptDependencies(pluginArtifacts: List<DefaultArtifact>): List<DefaultArtifact> {
with(DependencyResolver(buildscript.configurations, buildscript.dependencies)) { val resolverFactory = ConfigurationResolverFactory(buildscript.repositories)
DefaultDependencies( val resolver = resolverFactory.create(buildscript.dependencies)
repositories = buildscript.repositories.repositories(), val pluginIds = pluginArtifacts.map(DefaultArtifact::id)
artifacts = buildscript.configurations return buildscript.configurations
.filter { it.isCanBeResolved } .flatMap(resolver::resolve)
.flatMap { resolveDependencies(it) + resolvePoms(it) } .distinct()
.minus(plugins.artifacts) .filter { it.id !in pluginIds }
.distinct() .sorted()
) }
}
private fun Project.projectDependencies(explicitConfigurations: List<String>): DefaultDependencies = private fun Project.projectDependencies(explicitConfigurations: List<String>): List<DefaultArtifact> {
with(DependencyResolver(configurations, dependencies)) { val resolverFactory = ConfigurationResolverFactory(repositories)
val toResolve = collectConfigurations(explicitConfigurations) val resolver = resolverFactory.create(dependencies)
DefaultDependencies( return collectConfigurations(explicitConfigurations)
repositories = repositories.repositories(), .flatMap(resolver::resolve)
artifacts = toResolve.flatMap { resolveDependencies(it) + resolvePoms(it) } .distinct()
.sorted() .sorted()
.distinct() }
)
}
private fun Project.dependentSubprojects(explicitConfigurations: List<String>): Set<Project> { private fun Project.dependentSubprojects(explicitConfigurations: List<String>): Set<Project> {
return collectConfigurations(explicitConfigurations) return collectConfigurations(explicitConfigurations)
@@ -190,19 +193,6 @@ private fun Project.collectConfigurations(
} }
} }
private val excludedRepoNames = setOf(
"Embedded Kotlin Repository",
ArtifactRepositoryContainer.DEFAULT_MAVEN_LOCAL_REPO_NAME
)
internal fun RepositoryHandler.repositories() = DefaultRepositories(
maven = filterIsInstance<MavenArtifactRepository>()
.filter { it.name !in excludedRepoNames }
.map { repo ->
DefaultMaven(listOf(repo.url.toString()) + repo.artifactUrls.map { it.toString() })
}
)
private fun fetchDistSha256(url: String): String { private fun fetchDistSha256(url: String): String {
return URL("$url.sha256").openConnection().run { return URL("$url.sha256").openConnection().run {
connect() connect()
@@ -217,6 +207,9 @@ private val Wrapper.sha256: String
return if (GradleVersion.current() < GradleVersion.version("4.5")) { return if (GradleVersion.current() < GradleVersion.version("4.5")) {
fetchDistSha256(distributionUrl) fetchDistSha256(distributionUrl)
} else { } else {
@Suppress("UnstableApiUsage")
distributionSha256Sum ?: fetchDistSha256(distributionUrl) distributionSha256Sum ?: fetchDistSha256(distributionUrl)
} }
} }
private const val NIX_MODEL_NAME = "org.nixos.gradle2nix.Build"

View File

@@ -1,6 +1,5 @@
package org.nixos.gradle2nix package org.nixos.gradle2nix
import java.util.Properties
import org.gradle.api.Project import org.gradle.api.Project
data class ModelProperties( data class ModelProperties(

View File

@@ -1,101 +1,27 @@
package org.nixos.gradle2nix package org.nixos.gradle2nix
import org.gradle.api.artifacts.ExternalModuleDependency import org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency
import org.gradle.api.internal.GradleInternal
import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionSelectorScheme
import org.gradle.api.internal.plugins.PluginImplementation
import org.gradle.kotlin.dsl.support.serviceOf
import org.gradle.plugin.management.PluginRequest import org.gradle.plugin.management.PluginRequest
import org.gradle.plugin.management.internal.PluginRequestInternal
import org.gradle.plugin.use.PluginId
import org.gradle.plugin.use.internal.PluginDependencyResolutionServices import org.gradle.plugin.use.internal.PluginDependencyResolutionServices
import org.gradle.plugin.use.resolve.internal.ArtifactRepositoriesPluginResolver import javax.inject.Inject
import org.gradle.plugin.use.resolve.internal.PluginResolution
import org.gradle.plugin.use.resolve.internal.PluginResolutionResult
import org.gradle.plugin.use.resolve.internal.PluginResolveContext
internal class PluginResolver( internal open class PluginResolver @Inject constructor(
gradle: GradleInternal, pluginDependencyResolutionServices: PluginDependencyResolutionServices
private val pluginRequests: Collection<PluginRequest>
) { ) {
private val pluginDependencyResolutionServices = gradle.serviceOf<PluginDependencyResolutionServices>() private val configurations = pluginDependencyResolutionServices.configurationContainer
private val versionSelectorScheme = gradle.serviceOf<VersionSelectorScheme>()
private val artifactRepositoriesPluginResolver = ArtifactRepositoriesPluginResolver( private val resolver = ConfigurationResolverFactory(pluginDependencyResolutionServices.resolveRepositoryHandler)
pluginDependencyResolutionServices, .create(pluginDependencyResolutionServices.dependencyHandler)
versionSelectorScheme
)
val repositories = pluginDependencyResolutionServices.resolveRepositoryHandler fun resolve(pluginRequests: List<PluginRequest>): List<DefaultArtifact> {
val markerDependencies = pluginRequests.map {
private val resolver by lazy { it.module?.let { selector ->
DependencyResolver( DefaultExternalModuleDependency(selector.group, selector.name, selector.version)
pluginDependencyResolutionServices.configurationContainer, } ?: it.id.run {
pluginDependencyResolutionServices.dependencyHandler DefaultExternalModuleDependency(id, "$id.gradle.plugin", it.version)
)
}
private val pluginResult by lazy {
PluginResult().apply {
for (request in pluginRequests.filterIsInstance<PluginRequestInternal>()) {
artifactRepositoriesPluginResolver.resolve(request, this)
}
}
}
private val pluginContext by lazy {
PluginContext().apply {
for (result in pluginResult.found) result.execute(this)
}
}
fun artifacts(): List<DefaultArtifact> {
return (resolver.resolveDependencies(pluginContext.dependencies, true) +
resolver.resolvePoms(pluginContext.dependencies, true))
.sorted()
.distinct()
}
private class PluginResult : PluginResolutionResult {
val found = mutableSetOf<PluginResolution>()
override fun notFound(sourceDescription: String?, notFoundMessage: String?) {}
override fun notFound(
sourceDescription: String?,
notFoundMessage: String?,
notFoundDetail: String?
) {
}
override fun isFound(): Boolean = true
override fun found(sourceDescription: String, pluginResolution: PluginResolution) {
found.add(pluginResolution)
}
}
private class PluginContext : PluginResolveContext {
val dependencies = mutableSetOf<ExternalModuleDependency>()
val repositories = mutableSetOf<String>()
override fun add(plugin: PluginImplementation<*>) {
println("add: $plugin")
}
override fun addFromDifferentLoader(plugin: PluginImplementation<*>) {
println("addFromDifferentLoader: $plugin")
}
override fun addLegacy(pluginId: PluginId, m2RepoUrl: String, dependencyNotation: Any) {
repositories.add(m2RepoUrl)
}
override fun addLegacy(pluginId: PluginId, dependencyNotation: Any) {
if (dependencyNotation is ExternalModuleDependency) {
dependencies.add(dependencyNotation)
} }
} }
return resolver.resolve(configurations.detachedConfiguration(*markerDependencies.toTypedArray()))
} }
} }

View File

@@ -0,0 +1,212 @@
package org.nixos.gradle2nix
import org.apache.ivy.core.LogOptions
import org.apache.ivy.core.cache.ArtifactOrigin
import org.apache.ivy.core.cache.CacheResourceOptions
import org.apache.ivy.core.cache.DefaultRepositoryCacheManager
import org.apache.ivy.core.cache.RepositoryCacheManager
import org.apache.ivy.core.module.id.ArtifactRevisionId
import org.apache.ivy.core.module.id.ModuleRevisionId
import org.apache.ivy.core.resolve.DownloadOptions
import org.apache.ivy.core.settings.IvySettings
import org.apache.ivy.plugins.repository.url.URLResource
import org.apache.ivy.plugins.resolver.IBiblioResolver
import org.apache.ivy.plugins.resolver.URLResolver
import org.apache.maven.artifact.repository.metadata.io.xpp3.MetadataXpp3Reader
import org.codehaus.plexus.util.ReaderFactory
import org.codehaus.plexus.util.xml.pull.XmlPullParserException
import org.gradle.api.artifacts.repositories.ArtifactRepository
import org.gradle.api.artifacts.repositories.IvyArtifactRepository
import org.gradle.api.artifacts.repositories.MavenArtifactRepository
import org.gradle.api.internal.artifacts.repositories.ResolutionAwareRepository
import org.gradle.api.internal.artifacts.repositories.resolver.IvyResolver
import org.gradle.api.logging.Logger
import org.gradle.api.logging.Logging
import java.io.IOException
import org.apache.ivy.core.module.descriptor.Artifact as IvyArtifact
import org.apache.ivy.core.module.descriptor.DefaultArtifact as IvyDefaultArtifact
import org.apache.ivy.plugins.resolver.RepositoryResolver as IvyRepositoryResolver
internal fun ResolutionAwareRepository.repositoryResolver(ivySettings: IvySettings): RepositoryResolver? =
when(this) {
is MavenArtifactRepository -> MavenResolver(ivySettings, this)
is IvyArtifactRepository -> IvyResolver(ivySettings, this)
else -> null
}
internal sealed class RepositoryResolver {
companion object {
@JvmStatic
protected val log: Logger = Logging.getLogger("gradle2nix")
}
abstract val ivyResolver: IvyRepositoryResolver
abstract fun resolve(
artifactId: DefaultArtifactIdentifier,
sha256: String? = null
): DefaultArtifact?
}
internal class MavenResolver(
ivySettings: IvySettings,
repository: MavenArtifactRepository
) : RepositoryResolver() {
override val ivyResolver: IBiblioResolver = IBiblioResolver().apply {
name = repository.name
root = repository.url.toString()
isM2compatible = true
settings = ivySettings
setCache(cacheManager(ivySettings, repository).name)
}
override fun resolve(artifactId: DefaultArtifactIdentifier, sha256: String?): DefaultArtifact? {
val ivyArtifact: IvyArtifact = artifactId.toArtifact()
val origin = ivyResolver.locate(ivyArtifact)?.takeIf(ArtifactOrigin::isExists) ?: return null
val hash = sha256 ?: ivyResolver.download(origin, downloadOptions).localFile?.sha256() ?: return null
val snapshotVersion: SnapshotVersion? = artifactId.version.snapshotVersion()?.let {
findSnapshotVersion(artifactId, it)
}
return DefaultArtifact(
id = artifactId,
name = artifactId.filename(snapshotVersion),
path = artifactId.repoPath(),
timestamp = snapshotVersion?.timestamp,
build = snapshotVersion?.build,
urls = listOf(origin.location),
sha256 = hash
)
}
private fun findSnapshotVersion(
artifactId: ArtifactIdentifier,
snapshotVersion: SnapshotVersion
): SnapshotVersion {
if (snapshotVersion.timestamp != null) return snapshotVersion
val metadataLocation = "${ivyResolver.root}${artifactId.repoPath()}/maven-metadata.xml".toUrl()
val metadataFile = ivyResolver.repositoryCacheManager.downloadRepositoryResource(
URLResource(metadataLocation, ivyResolver.timeoutConstraint),
"maven-metadata",
"maven-metadata",
"xml",
CacheResourceOptions(),
ivyResolver.repository
).localFile
if (metadataFile == null) {
log.warn("maven-metadata.xml not found for snapshot dependency: $artifactId")
return snapshotVersion
}
fun parseError(e: Throwable): Pair<String?, Int?> {
log.error("Failed to parse maven-metadata.xml for artifact: $artifactId")
log.error("Error was: ${e.message}", e)
return null to null
}
val (timestamp: String?, build: Int?) = try {
MetadataXpp3Reader()
.read(ReaderFactory.newXmlReader(metadataFile))
.versioning?.snapshot?.run { timestamp to buildNumber }
?: null to null
} catch (e: IOException) {
parseError(e)
} catch (e: XmlPullParserException) {
parseError(e)
}
return snapshotVersion.copy(timestamp = timestamp, build = build)
}
}
internal class IvyResolver(
ivySettings: IvySettings,
repository: IvyArtifactRepository
) : RepositoryResolver() {
override val ivyResolver: URLResolver = URLResolver().apply {
name = repository.name
val ivyResolver = (repository as ResolutionAwareRepository).createResolver() as IvyResolver
isM2compatible = ivyResolver.isM2compatible
for (p in ivyResolver.ivyPatterns) addIvyPattern(p)
for (p in ivyResolver.artifactPatterns) addArtifactPattern(p)
settings = ivySettings
setCache(cacheManager(ivySettings, repository).name)
}
override fun resolve(artifactId: DefaultArtifactIdentifier, sha256: String?): DefaultArtifact? {
val ivyArtifact: IvyArtifact = artifactId.toArtifact()
val origin = ivyResolver.locate(ivyArtifact)?.takeIf(ArtifactOrigin::isExists) ?: return null
val hash = sha256 ?: ivyResolver.download(origin, downloadOptions).localFile?.sha256() ?: return null
return DefaultArtifact(
id = DefaultArtifactIdentifier(artifactId),
name = artifactId.filename(null),
path = artifactId.repoPath(),
urls = listOf(origin.location),
sha256 = hash
)
}
}
private fun cacheManager(ivySettings: IvySettings, repository: ArtifactRepository): RepositoryCacheManager {
return DefaultRepositoryCacheManager(
"${repository.name}-cache",
ivySettings,
createTempDir("gradle2nix-${repository.name}-cache")
).also {
ivySettings.addRepositoryCacheManager(it)
}
}
private val metadataTypes = setOf("pom", "ivy")
private fun ArtifactIdentifier.toArtifact(): IvyArtifact {
val moduleRevisionId = ModuleRevisionId.newInstance(group, name, version)
val artifactRevisionId = ArtifactRevisionId.newInstance(
moduleRevisionId,
name,
type,
extension,
classifier?.let { mapOf("classifier" to it) }
)
return IvyDefaultArtifact(artifactRevisionId, null, null, type in metadataTypes)
}
private data class SnapshotVersion(
val base: String,
val timestamp: String?,
val build: Int?
) {
override fun toString(): String {
return if (timestamp != null && build != null) {
"$base-$timestamp-$build"
} else {
"$base-SNAPSHOT"
}
}
}
private val SNAPSHOT_REGEX = Regex("^(.*)-SNAPSHOT$")
private val SNAPSHOT_TIMESTAMPED_REGEX = Regex("^(.*)-([0-9]{8}.[0-9]{6})-([0-9]+)$")
private fun String.snapshotVersion(): SnapshotVersion? {
return SNAPSHOT_REGEX.find(this)?.destructured?.let { (base) ->
SnapshotVersion(base, null, null)
} ?: SNAPSHOT_TIMESTAMPED_REGEX.find(this)?.destructured?.let { (base, timestamp, build) ->
SnapshotVersion(base, timestamp, build.toInt())
}
}
private fun ArtifactIdentifier.repoPath(): String =
"${group.replace('.', '/')}/$name/$version"
private fun ArtifactIdentifier.filename(
snapshotVersion: SnapshotVersion?
): String = buildString {
append(name, "-", snapshotVersion ?: version)
if (classifier != null) append("-", classifier)
append(".", extension)
}
private val downloadOptions = DownloadOptions().apply { log = LogOptions.LOG_QUIET }

View File

@@ -0,0 +1,21 @@
package org.nixos.gradle2nix
import java.io.File
import java.net.URL
import java.security.MessageDigest
private const val HEX = "0123456789abcdef"
internal fun File.sha256(): String = readBytes().sha256()
private fun ByteArray.sha256() = buildString {
MessageDigest.getInstance("SHA-256").digest(this@sha256)
.asSequence()
.map(Byte::toInt)
.forEach {
append(HEX[it shr 4 and 0x0f])
append(HEX[it and 0x0f])
}
}
internal fun String.toUrl(): URL = URL(this)