diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 8bdaf60..61285a6 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 2a84e18..dbc3ce4 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.0-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index ef07e01..adff685 100755 --- a/gradlew +++ b/gradlew @@ -114,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH="\\\"\\\"" # Determine the Java command to use to start the JVM. @@ -172,7 +171,6 @@ fi # For Cygwin or MSYS, switch paths to Windows format before running java if "$cygwin" || "$msys" ; then APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) JAVACMD=$( cygpath --unix "$JAVACMD" ) @@ -212,7 +210,6 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" diff --git a/gradlew.bat b/gradlew.bat index db3a6ac..c4bdd3a 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -70,11 +70,10 @@ goto fail :execute @rem Setup the command line -set CLASSPATH= @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/src/main/kotlin/eu/cloudnetservice/gradle/juppiter/JuppiterPlugin.kt b/src/main/kotlin/eu/cloudnetservice/gradle/juppiter/JuppiterPlugin.kt index ae6bdf5..b46d3e8 100644 --- a/src/main/kotlin/eu/cloudnetservice/gradle/juppiter/JuppiterPlugin.kt +++ b/src/main/kotlin/eu/cloudnetservice/gradle/juppiter/JuppiterPlugin.kt @@ -16,45 +16,177 @@ package eu.cloudnetservice.gradle.juppiter -import eu.cloudnetservice.gradle.juppiter.util.GradleUtil +import eu.cloudnetservice.gradle.juppiter.data.* +import eu.cloudnetservice.gradle.juppiter.flavor.FlavorExtension +import eu.cloudnetservice.gradle.juppiter.tasks.GenerateModuleJson +import eu.cloudnetservice.gradle.juppiter.tasks.PrepareModuleJson +import eu.cloudnetservice.gradle.juppiter.util.ChecksumHelper +import org.gradle.api.Action +import org.gradle.api.NamedDomainObjectProvider import org.gradle.api.Plugin import org.gradle.api.Project +import org.gradle.api.artifacts.Configuration +import org.gradle.api.artifacts.ConfigurationContainer +import org.gradle.api.artifacts.ResolvedDependency +import org.gradle.api.artifacts.repositories.MavenArtifactRepository import org.gradle.api.plugins.JavaPlugin import org.gradle.api.tasks.SourceSet import org.gradle.api.tasks.SourceSetContainer import org.gradle.kotlin.dsl.getByType import org.gradle.kotlin.dsl.register import org.gradle.kotlin.dsl.withType +import java.io.File class JuppiterPlugin : Plugin { + fun ConfigurationContainer.declarable(name: String) = declarable(name) {} + fun ConfigurationContainer.declarable( + name: String, action: Action + ): NamedDomainObjectProvider = register(name) { + isCanBeResolved = false + isCanBeConsumed = false + action.execute(this) + } + + private fun Project.collect( + dependency: ResolvedDependency, + target: MutableSet = HashSet() + ): Set { + // Empty artifacts can exist for BOMs. We ignore those for now + if (dependency.moduleArtifacts.isEmpty()) { + return target + } + // For now, we require a single artifact + dependency.moduleArtifacts.single().apply { + val loader = "maven" + val environments = setOf("*") + val optional = false + val group = moduleVersion.id.group + val name = name + val version = moduleVersion.id.version + val classifier = classifier + val snapshot = version.endsWith("-SNAPSHOT") + val checksum = if (snapshot) null else ChecksumHelper.sha3256(file) + target.add( + IntermediateExternalDependency( + loader, + environments, + optional, + group, + name, + version, + classifier, + checksum, + snapshot, + file + ) + ) + if (!snapshot) { + dependency.children.forEach { + collect(it, target) + } + } + } + return target + } + override fun apply(target: Project) { target.run { - val libraries = configurations.maybeCreate("moduleLibrary") - val moduleDependencies = configurations.maybeCreate("moduleDependency") + val objects = objects + val libraries = configurations.declarable("moduleLibrary") + val moduleDependencies = configurations.declarable("moduleDependency") + val librariesOnly = configurations.declarable("moduleLibraryOnly") { + extendsFrom(libraries) + } + val moduleDependenciesOnly = configurations.declarable("moduleDependencyOnly") { + extendsFrom(moduleDependencies) + } + val librariesClasspath = configurations.resolvable("moduleLibraryClasspath") { extendsFrom(libraries) } + val dependenciesClasspath = + configurations.resolvable("moduleDependencyClasspath") { extendsFrom(moduleDependencies) } + val librariesOnlyClasspath = + configurations.resolvable("moduleLibraryOnlyClasspath") { extendsFrom(librariesOnly) } + val dependenciesOnlyClasspath = + configurations.resolvable("moduleDependencyOnlyClasspath") { extendsFrom(moduleDependenciesOnly) } - val moduleExtension = - GradleUtil.findOrAddExtension(extensions, "moduleJson", ModuleConfiguration::class) { - ModuleConfiguration(target.objects) - } + val moduleExtension = ModuleConfiguration(objects) + extensions.add("moduleJson", moduleExtension) + + val flavorExtension = FlavorExtension(this) + extensions.add("flavors", flavorExtension) + + val prepareModuleJsonTask = tasks.register("prepareModule") { + moduleJson.convention { temporaryDir.resolve("prepared-module.json") } + repositories.convention( + target.repositories.filterIsInstance().map { it.url.toString() }) + unresolvedModuleDependencies.convention(dependenciesOnlyClasspath.map { configuration -> + configuration.allDependencies.map { dependency -> + // We resolve a detached configuration with our single dependency. + val file = configurations.detachedConfiguration(dependency) + .also { it.isTransitive = false }.incoming.artifacts.resolvedArtifacts.map { set -> set.single() } + .map { it.file }.get() + + val versionRange = dependency.version!! + val optional = false + val type = ModuleDependencyType.REQUIRED + PrepareModuleJson.UnresolvedModuleDependency(versionRange, optional, file, type) + } + }) + unresolvedExternalDependencies.convention(librariesOnlyClasspath.map { configuration -> + configuration.allDependencies.flatMap { dependency -> + + val configuration = configurations.detachedConfiguration(dependency) + + + val intermediates = + configuration.resolvedConfiguration.firstLevelModuleDependencies.single().let { collect(it) } + intermediates.map { e -> + PrepareModuleJson.UnresolvedExternalDependency( + e.snapshot, + e.group, + e.name, + e.version, + e.classifier, + e.environments, + e.optional, + e.file + ) + } + } + }) + + } val generateModuleTask = tasks.register("genModuleJson") { - fileName.convention("module.json") - outputDirectory.convention(layout.buildDirectory.dir("generated/module-json")) - moduleConfiguration.convention( - provider { - moduleExtension.setDefaults(this@run, libraries, moduleDependencies) - - // This provider is lazy, so it should be applied after project configuration - moduleExtension.resolveRepositories(project.repositories) - moduleExtension - }, - ) - - doFirst { - moduleExtension.validate() + dependsOn(prepareModuleJsonTask) + outputFile.convention( + layout.buildDirectory.dir("generated/module-json").map { it.file("cloudnet-module.json") }) + moduleConfiguration.convention(moduleExtension) + } + + val prepared = prepareModuleJsonTask.flatMap { it.moduleJson }.map { it.asFile } + .map { it.readText() }.map { PrepareModuleJson.PreparedModuleJson.deserialize(it) } + moduleExtension.externalDependencies.addAll(prepared.map { prepared -> + prepared.externalDependencies.map { e -> + ModuleExternalDependency(objects).apply { + this.loader.convention(e.loader) + this.optional.convention(e.optional) + this.environments.convention(e.environments) + e.properties.forEach { (string, any) -> + this.properties.putSimple(string, any) + } } } + }) + moduleExtension.dependencies.addAll(prepared.map { prepared -> + prepared.moduleDependencies.map { e -> + ModuleDependency(objects).apply { + this.id.convention(e.id) + this.versionRange.convention(e.versionRange) + this.dependencyType.convention(e.dependencyType) + } + } + }) plugins.withType { extensions.getByType().named(SourceSet.MAIN_SOURCE_SET_NAME) { @@ -65,3 +197,16 @@ class JuppiterPlugin : Plugin { } } } + +data class IntermediateExternalDependency( + val loader: String, + val environments: Set, + val optional: Boolean, + val group: String, + val name: String, + val version: String, + val classifier: String?, + val checksum: String?, + val snapshot: Boolean, + val file: File +) diff --git a/src/main/kotlin/eu/cloudnetservice/gradle/juppiter/ModuleConfiguration.kt b/src/main/kotlin/eu/cloudnetservice/gradle/juppiter/ModuleConfiguration.kt deleted file mode 100644 index 34c36fc..0000000 --- a/src/main/kotlin/eu/cloudnetservice/gradle/juppiter/ModuleConfiguration.kt +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright 2019-present CloudNetService team & contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package eu.cloudnetservice.gradle.juppiter - -import com.fasterxml.jackson.annotation.JsonIgnore -import com.fasterxml.jackson.annotation.JsonTypeInfo -import com.fasterxml.jackson.databind.annotation.JsonSerialize -import eu.cloudnetservice.gradle.juppiter.jackson.JavaVersionSerializer -import eu.cloudnetservice.gradle.juppiter.util.ChecksumHelper -import eu.cloudnetservice.gradle.juppiter.util.MavenUtility -import groovy.lang.Closure -import org.gradle.api.JavaVersion -import org.gradle.api.NamedDomainObjectContainer -import org.gradle.api.Project -import org.gradle.api.artifacts.Configuration -import org.gradle.api.artifacts.dsl.RepositoryHandler -import org.gradle.api.artifacts.repositories.MavenArtifactRepository -import org.gradle.api.internal.artifacts.repositories.resolver.MavenUniqueSnapshotComponentIdentifier -import org.gradle.api.model.ObjectFactory -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.Internal -import org.gradle.api.tasks.Nested -import org.gradle.api.tasks.Optional -import java.util.concurrent.atomic.AtomicBoolean - -@Suppress("unused") -open class ModuleConfiguration( - objectFactory: ObjectFactory, -) { - // internal marker to prevent duplicate resolving of dependencies - // this was introduced initially because of a gradle issue introduces - // in version 7.4 but might be useful in the future too - // https://github.com/gradle/gradle/issues/19848 - @JsonIgnore - private val resolved: AtomicBoolean = AtomicBoolean() - - @Input - var runtimeModule = false - - @Input - var storesSensitiveData = false - - @Input - var main: String? = null - - @Input - @Optional - var name: String? = null - - @Input - @Optional - var group: String? = null - - @Input - @Optional - var author: String? = null - - @Input - @Optional - var version: String? = null - - @Input - @Optional - var website: String? = null - - @Input - @Optional - var dataFolder: String? = null - - @Input - @Optional - var description: String? = null - - @Input - @Optional - @JsonSerialize(using = JavaVersionSerializer::class, nullsUsing = JavaVersionSerializer::class) - var minJavaVersionId: JavaVersion? = null - - @Nested - val properties: Map = HashMap() - - @Nested - @JsonTypeInfo(use = JsonTypeInfo.Id.DEDUCTION, include = JsonTypeInfo.As.WRAPPER_OBJECT) - val repositories: NamedDomainObjectContainer = objectFactory.domainObjectContainer(Repository::class.java) - - @Nested - @JsonTypeInfo(use = JsonTypeInfo.Id.DEDUCTION, include = JsonTypeInfo.As.WRAPPER_OBJECT) - val dependencies: NamedDomainObjectContainer = objectFactory.domainObjectContainer(Dependency::class.java) - - // for groovy - fun repositories(closure: Closure) = repositories.configure(closure) - - fun dependencies(closure: Closure) = dependencies.configure(closure) - - data class Repository( - @Input val name: String, - ) { - @Input - var url: String? = null - } - - data class Dependency( - @Input val name: String, - ) { - @Input - var group: String? = null - - @Input - var version: String? = null - - @Input - @Optional - var url: String? = null - - @Input - @Optional - var repo: String? = null - - @Input - @Optional - var checksum: String? = null - - @Internal - @JsonIgnore - var classifier: String? = null - - @Internal - @JsonIgnore - var timestampedVersion: String? = null - - @Input - @JsonIgnore - var needsRepoResolve: Boolean = true - } - - fun setDefaults( - project: Project, - libraries: Configuration, - moduleDependencies: Configuration, - ) { - if (!this.resolved.getAndSet(true)) { - name = name ?: project.name - group = group ?: project.group.toString() - version = version ?: project.version.toString() - - // other stuff - author = author ?: "Anonymous" - website = website ?: "https://cloudnetservice.eu" - description = description ?: project.description ?: "Just another CloudNet module" - - // dependencies of the module we need to resolve - libraries.resolvedConfiguration.resolvedArtifacts.forEach { - val versionId = it.moduleVersion.id - val dependency = Dependency(it.name) - dependency.group = versionId.group - dependency.version = versionId.version - dependency.classifier = it.classifier - dependency.checksum = ChecksumHelper.fileShaSum(it.file) - - val componentIdentifier = it.id.componentIdentifier - if (versionId.version.endsWith("-SNAPSHOT") && componentIdentifier is MavenUniqueSnapshotComponentIdentifier) { - dependency.timestampedVersion = componentIdentifier.timestampedVersion - } - - dependencies.add(dependency) - } - - // dependencies of the module that are other modules, so we only need: group, name, version - moduleDependencies.resolvedConfiguration.firstLevelModuleDependencies - .map { it.module.id } - .forEach { - val dependency = Dependency(it.name) - dependency.group = it.group - dependency.version = it.version - dependency.needsRepoResolve = false - - dependencies.add(dependency) - } - } - } - - fun resolveRepositories(repositoryHandler: RepositoryHandler) { - val repos = repositoryHandler.filterIsInstance() - dependencies - .filter { it.needsRepoResolve } - .forEach { - val repo = MavenUtility.findRepository(it, repos) ?: return@forEach - repositories.add(repo) - } - } - - fun validate() { - if (main.isNullOrEmpty()) { - throw InvalidModuleDescription("main class must be set") - } - } -} diff --git a/src/main/kotlin/eu/cloudnetservice/gradle/juppiter/data/ModuleArtifact.kt b/src/main/kotlin/eu/cloudnetservice/gradle/juppiter/data/ModuleArtifact.kt new file mode 100644 index 0000000..dc488d1 --- /dev/null +++ b/src/main/kotlin/eu/cloudnetservice/gradle/juppiter/data/ModuleArtifact.kt @@ -0,0 +1,49 @@ +/* + * Copyright 2019-present CloudNetService team & contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package eu.cloudnetservice.gradle.juppiter.data + +import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.Property +import org.gradle.api.provider.SetProperty +import org.gradle.api.tasks.Input +import org.gradle.kotlin.dsl.property +import org.gradle.kotlin.dsl.setProperty + +class ModuleArtifact(objectFactory: ObjectFactory) { + @Input + val source: Property = objectFactory.property() + + @Input + val sourcePath: Property = objectFactory.property() + + @Input + val targetPath: Property = objectFactory.property() + + @Input + val environments: SetProperty = objectFactory.setProperty() + + init { + source.finalizeValueOnRead() + sourcePath.finalizeValueOnRead() + targetPath.finalizeValueOnRead() + environments.finalizeValueOnRead() + } +} + +enum class ModuleArtifactSource { + CLASSPATH, FILESYSTEM +} diff --git a/src/main/kotlin/eu/cloudnetservice/gradle/juppiter/data/ModuleConfiguration.kt b/src/main/kotlin/eu/cloudnetservice/gradle/juppiter/data/ModuleConfiguration.kt new file mode 100644 index 0000000..2c5fa84 --- /dev/null +++ b/src/main/kotlin/eu/cloudnetservice/gradle/juppiter/data/ModuleConfiguration.kt @@ -0,0 +1,140 @@ +/* + * Copyright 2019-present CloudNetService team & contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package eu.cloudnetservice.gradle.juppiter.data + +import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.MapProperty +import org.gradle.api.provider.Property +import org.gradle.api.provider.SetProperty +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Nested +import org.gradle.api.tasks.Optional +import org.gradle.kotlin.dsl.mapProperty +import org.gradle.kotlin.dsl.property +import org.gradle.kotlin.dsl.setProperty + +class ModuleConfiguration( + objectFactory: ObjectFactory, +) { + @Input + val id: Property = objectFactory.property() + + @Input + val name: Property = objectFactory.property() + + @Input + @Optional + val description: Property = objectFactory.property() + + @Input + val entrypoint: Property = objectFactory.property() + + @Input + @Optional + val version: Property = objectFactory.property() + + @Nested + val artifacts: SetProperty = objectFactory.setProperty() + + @Nested + val dependencies: SetProperty = objectFactory.setProperty() + + @Nested + val externalDependencies: SetProperty = objectFactory.setProperty() + + @Nested + val contributors: SetProperty = objectFactory.setProperty() + + @Nested + val properties: MapProperty = objectFactory.mapProperty() + + init { + id.finalizeValueOnRead() + name.finalizeValueOnRead() + description.finalizeValueOnRead() + entrypoint.finalizeValueOnRead() + version.finalizeValueOnRead() + artifacts.finalizeValueOnRead() + dependencies.finalizeValueOnRead() + externalDependencies.finalizeValueOnRead() + contributors.finalizeValueOnRead() + properties.finalizeValueOnRead() + } + + +// fun setDefaults( +// project: Project, +// libraries: Configuration, +// moduleDependencies: Configuration, +// ) { +// if (!this.resolved.getAndSet(true)) { +// name = name ?: project.name +// group = group ?: project.group.toString() +// version = version ?: project.version.toString() +// +// // other stuff +// author = author ?: "Anonymous" +// website = website ?: "https://cloudnetservice.eu" +// description = description ?: project.description ?: "Just another CloudNet module" +// +// // dependencies of the module we need to resolve +// libraries.resolvedConfiguration.resolvedArtifacts.forEach { +// val versionId = it.moduleVersion.id +// val dependency = Dependency(it.name) +// dependency.group = versionId.group +// dependency.version = versionId.version +// dependency.classifier = it.classifier +// dependency.checksum = ChecksumHelper.fileShaSum(it.file) +// +// val componentIdentifier = it.id.componentIdentifier +// if (versionId.version.endsWith("-SNAPSHOT") && componentIdentifier is MavenUniqueSnapshotComponentIdentifier) { +// dependency.timestampedVersion = componentIdentifier.timestampedVersion +// } +// +// dependencies.add(dependency) +// } +// +// // dependencies of the module that are other modules, so we only need: group, name, version +// moduleDependencies.resolvedConfiguration.firstLevelModuleDependencies +// .map { it.module.id } +// .forEach { +// val dependency = Dependency(it.name) +// dependency.group = it.group +// dependency.version = it.version +// dependency.needsRepoResolve = false +// +// dependencies.add(dependency) +// } +// } +// } +// +// fun resolveRepositories(repositoryHandler: RepositoryHandler) { +// val repos = repositoryHandler.filterIsInstance() +// dependencies +// .filter { it.needsRepoResolve } +// .forEach { +// val repo = MavenUtility.findRepository(it, repos) ?: return@forEach +// repositories.add(repo) +// } +// } +// +// fun validate() { +// if (main.isNullOrEmpty()) { +// throw InvalidModuleDescription("main class must be set") +// } +// } +} diff --git a/src/main/kotlin/eu/cloudnetservice/gradle/juppiter/data/ModuleContributor.kt b/src/main/kotlin/eu/cloudnetservice/gradle/juppiter/data/ModuleContributor.kt new file mode 100644 index 0000000..a73ccfd --- /dev/null +++ b/src/main/kotlin/eu/cloudnetservice/gradle/juppiter/data/ModuleContributor.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2019-present CloudNetService team & contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package eu.cloudnetservice.gradle.juppiter.data + +import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.MapProperty +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Nested +import org.gradle.kotlin.dsl.mapProperty +import org.gradle.kotlin.dsl.property + +class ModuleContributor(objectFactory: ObjectFactory) { + @Input + val name: Property = objectFactory.property() + + @Nested + val properties: MapProperty = objectFactory.mapProperty() + + init { + name.finalizeValueOnRead() + properties.finalizeValueOnRead() + } +} diff --git a/src/main/kotlin/eu/cloudnetservice/gradle/juppiter/data/ModuleDependency.kt b/src/main/kotlin/eu/cloudnetservice/gradle/juppiter/data/ModuleDependency.kt new file mode 100644 index 0000000..4520484 --- /dev/null +++ b/src/main/kotlin/eu/cloudnetservice/gradle/juppiter/data/ModuleDependency.kt @@ -0,0 +1,43 @@ +/* + * Copyright 2019-present CloudNetService team & contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package eu.cloudnetservice.gradle.juppiter.data + +import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Input +import org.gradle.kotlin.dsl.property + +class ModuleDependency(objectFactory: ObjectFactory) { + @Input + val id: Property = objectFactory.property() + @Input + val versionRange: Property = objectFactory.property() + @Input + val dependencyType: Property = objectFactory.property() + + init { + id.finalizeValueOnRead() + versionRange.finalizeValueOnRead() + dependencyType.finalizeValueOnRead() + } +} + +enum class ModuleDependencyType { + REQUIRED, + SUGGESTED, + OPTIONAL +} diff --git a/src/main/kotlin/eu/cloudnetservice/gradle/juppiter/data/ModuleExternalDependency.kt b/src/main/kotlin/eu/cloudnetservice/gradle/juppiter/data/ModuleExternalDependency.kt new file mode 100644 index 0000000..3fb29e1 --- /dev/null +++ b/src/main/kotlin/eu/cloudnetservice/gradle/juppiter/data/ModuleExternalDependency.kt @@ -0,0 +1,50 @@ +/* + * Copyright 2019-present CloudNetService team & contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package eu.cloudnetservice.gradle.juppiter.data + +import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.MapProperty +import org.gradle.api.provider.Property +import org.gradle.api.provider.SetProperty +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Nested +import org.gradle.api.tasks.Optional +import org.gradle.kotlin.dsl.mapProperty +import org.gradle.kotlin.dsl.property +import org.gradle.kotlin.dsl.setProperty + +class ModuleExternalDependency(objectFactory: ObjectFactory) { + @Input + val loader: Property = objectFactory.property() + + @Input + @Optional + val optional: Property = objectFactory.property() + + @Input + val environments: SetProperty = objectFactory.setProperty() + + @Nested + val properties: MapProperty = objectFactory.mapProperty() + + init { + loader.finalizeValueOnRead() + optional.finalizeValueOnRead() + environments.finalizeValueOnRead() + properties.finalizeValueOnRead() + } +} diff --git a/src/main/kotlin/eu/cloudnetservice/gradle/juppiter/data/PropertyValueHolder.kt b/src/main/kotlin/eu/cloudnetservice/gradle/juppiter/data/PropertyValueHolder.kt new file mode 100644 index 0000000..c0fe082 --- /dev/null +++ b/src/main/kotlin/eu/cloudnetservice/gradle/juppiter/data/PropertyValueHolder.kt @@ -0,0 +1,60 @@ +/* + * Copyright 2019-present CloudNetService team & contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package eu.cloudnetservice.gradle.juppiter.data + +import org.gradle.api.provider.MapProperty +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.Nested +import org.gradle.api.tasks.Optional + +/** + * Gradle does not allow mixing @Input and @Nested. + * + * This class is to support both primitive @Input types and complex @Nested types + */ +class PropertyValueHolder { + private constructor(simple: Any?, complex: Any) { + this.simple = simple + this.complex = complex + } + + @Input + @Optional + val simple: Any? + + @Nested + val complex: Any + + @get:Internal + val value: Any + get() = simple ?: complex + + companion object { + fun simple(value: Any) = PropertyValueHolder(value, Empty) + fun complex(value: Any) = PropertyValueHolder(null, value) + } + + private object Empty +} + +fun MapProperty.putSimple(key: String, value: Any) = + put(key, PropertyValueHolder.simple(value)) + +fun MapProperty.putComplex(key: String, value: Any) = + put(key, PropertyValueHolder.complex(value)) + diff --git a/src/main/kotlin/eu/cloudnetservice/gradle/juppiter/flavor/Flavor.kt b/src/main/kotlin/eu/cloudnetservice/gradle/juppiter/flavor/Flavor.kt new file mode 100644 index 0000000..95771bf --- /dev/null +++ b/src/main/kotlin/eu/cloudnetservice/gradle/juppiter/flavor/Flavor.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2019-present CloudNetService team & contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package eu.cloudnetservice.gradle.juppiter.flavor + +import org.gradle.api.provider.SetProperty + +class Flavor internal constructor(val name: String, val dependsOn: SetProperty) { + init { + dependsOn.finalizeValueOnRead() + } + + fun dependOn(other: Flavor) { + dependsOn.add(other) + } +} diff --git a/src/main/kotlin/eu/cloudnetservice/gradle/juppiter/flavor/FlavorExtension.kt b/src/main/kotlin/eu/cloudnetservice/gradle/juppiter/flavor/FlavorExtension.kt new file mode 100644 index 0000000..ed46ef7 --- /dev/null +++ b/src/main/kotlin/eu/cloudnetservice/gradle/juppiter/flavor/FlavorExtension.kt @@ -0,0 +1,114 @@ +/* + * Copyright 2019-present CloudNetService team & contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package eu.cloudnetservice.gradle.juppiter.flavor + +import org.gradle.api.Project +import org.gradle.api.plugins.JavaPluginExtension +import org.gradle.jvm.tasks.Jar +import org.gradle.kotlin.dsl.getByType +import org.gradle.kotlin.dsl.register +import org.gradle.kotlin.dsl.setProperty + +class FlavorExtension(private val project: Project) { + private var javaRegistered = false + private val flavors = HashSet() + + val main: Flavor = Flavor("main", project.objects.setProperty().apply { finalizeValue() }) + + fun addFlavor(name: String): Flavor { + return Flavor(name, project.objects.setProperty()).apply { + flavors.add(this) + if (javaRegistered) { + registerJava(this) + } + } + } + + fun registerJavaSourceSets() { + if (javaRegistered) return + javaRegistered = true + flavors.forEach { + registerJava(it) + } + } + + private fun registerJava(flavor: Flavor) { + val java = project.extensions.getByType() + val sourceSets = java.sourceSets + val sourceSet = sourceSets.register(flavor.name) + val files = project.objects.fileCollection() + files.from(flavor.dependsOn.map { flavors -> +// val flavors = collectDependencies(flavor = flavor).filter { it !== flavor } + flavors.map { dependencyFlavor -> + sourceSets.named(dependencyFlavor.name).map { it.output } + } + }) + val configurations = project.configurations + val apiConfiguration = configurations.register(sourceSet.get().apiConfigurationName) + val compileOnlyApiConfiguration = configurations.register(sourceSet.get().compileOnlyApiConfigurationName) + configurations.named(sourceSet.get().implementationConfigurationName).configure { + extendsFrom(apiConfiguration.get()) + } + configurations.named(sourceSet.get().compileOnlyConfigurationName).configure { + extendsFrom(compileOnlyApiConfiguration.get()) + } + + // Get around IDE loading this too early. Ugly but works + // Loading early causes flavor.dependsOn to be read-only + project.afterEvaluate { + project.dependencies.add(sourceSet.get().apiConfigurationName, files) + + apiConfiguration.configure { + flavor.dependsOn.get().map { sourceSets.getByName(it.name) }.forEach { + extendsFrom(configurations.getByName(it.apiConfigurationName)) + } + } + compileOnlyApiConfiguration.configure { + flavor.dependsOn.get().map { sourceSets.getByName(it.name) }.forEach { + extendsFrom(configurations.getByName(it.compileOnlyApiConfigurationName)) + } + } + configurations.named(sourceSet.get().runtimeOnlyConfigurationName).configure { + flavor.dependsOn.get().map { sourceSets.getByName(it.name) }.forEach { + extendsFrom(configurations.getByName(it.runtimeClasspathConfigurationName)) + } + } + } + + val task = project.tasks.register(sourceSet.get().jarTaskName) { + val dependencies = flavor.dependsOn.get() + archiveClassifier.set(flavor.name) + from(sourceSet.map { it.output }) + dependencies.forEach { dependency -> + val taskName = sourceSets.named(dependency.name).get().jarTaskName + from(project.tasks.named(taskName)) + } + } + project.tasks.named("assemble").configure { + dependsOn(task.get()) + } + } + + private fun collectDependencies(set: MutableSet = HashSet(), flavor: Flavor): MutableSet { + if (set.add(flavor)) { + flavor.dependsOn.get().forEach { + collectDependencies(set, it) + } + } + return set + } +} diff --git a/src/main/kotlin/eu/cloudnetservice/gradle/juppiter/GenerateModuleJson.kt b/src/main/kotlin/eu/cloudnetservice/gradle/juppiter/tasks/GenerateModuleJson.kt similarity index 51% rename from src/main/kotlin/eu/cloudnetservice/gradle/juppiter/GenerateModuleJson.kt rename to src/main/kotlin/eu/cloudnetservice/gradle/juppiter/tasks/GenerateModuleJson.kt index 5f0b2de..8a11d5c 100644 --- a/src/main/kotlin/eu/cloudnetservice/gradle/juppiter/GenerateModuleJson.kt +++ b/src/main/kotlin/eu/cloudnetservice/gradle/juppiter/tasks/GenerateModuleJson.kt @@ -14,32 +14,35 @@ * limitations under the License. */ -package eu.cloudnetservice.gradle.juppiter +package eu.cloudnetservice.gradle.juppiter.tasks import com.fasterxml.jackson.annotation.JsonInclude import com.fasterxml.jackson.core.JsonFactory import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.core.StreamWriteConstraints +import com.fasterxml.jackson.databind.JsonSerializer import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.databind.SerializerProvider +import com.fasterxml.jackson.databind.module.SimpleModule import com.fasterxml.jackson.module.kotlin.registerKotlinModule +import eu.cloudnetservice.gradle.juppiter.data.ModuleConfiguration +import eu.cloudnetservice.gradle.juppiter.data.PropertyValueHolder import org.gradle.api.DefaultTask -import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.RegularFileProperty import org.gradle.api.provider.Property +import org.gradle.api.provider.Provider import org.gradle.api.tasks.CacheableTask -import org.gradle.api.tasks.Input import org.gradle.api.tasks.Nested -import org.gradle.api.tasks.OutputDirectory +import org.gradle.api.tasks.OutputFile import org.gradle.api.tasks.TaskAction @CacheableTask abstract class GenerateModuleJson : DefaultTask() { - @get:Input - abstract val fileName: Property - @get:Nested abstract val moduleConfiguration: Property - @get:OutputDirectory - abstract val outputDirectory: DirectoryProperty + @get:OutputFile + abstract val outputFile: RegularFileProperty @TaskAction fun generate() { @@ -47,13 +50,36 @@ abstract class GenerateModuleJson : DefaultTask() { JsonFactory() .enable(JsonGenerator.Feature.IGNORE_UNKNOWN) .enable(JsonGenerator.Feature.STRICT_DUPLICATE_DETECTION) + .setStreamWriteConstraints(StreamWriteConstraints.builder().maxNestingDepth(2000).build()) + val module = SimpleModule().apply { + addSerializer(Provider::class.java, object : JsonSerializer>() { + override fun serialize( + value: Provider<*>, + gen: JsonGenerator, + serializers: SerializerProvider + ) { + if (value.isPresent) { + gen.writeObject(value.get()) + } + else gen.writeNull() + } + }) + addSerializer(PropertyValueHolder::class.java, object : JsonSerializer() { + override fun serialize( + value: PropertyValueHolder, gen: JsonGenerator, serializers: SerializerProvider + ) { + gen.writeObject(value.value) + } + }) + } val mapper = ObjectMapper(factory) .registerKotlinModule() - .setSerializationInclusion(JsonInclude.Include.NON_EMPTY) + .registerModule(module) + .setSerializationInclusion(JsonInclude.Include.NON_EMPTY).writerWithDefaultPrettyPrinter() val moduleConfiguration = moduleConfiguration.get() - mapper.writeValue(outputDirectory.file(fileName).get().asFile, moduleConfiguration) + mapper.writeValue(outputFile.get().asFile, moduleConfiguration) } } diff --git a/src/main/kotlin/eu/cloudnetservice/gradle/juppiter/tasks/PrepareModuleJson.kt b/src/main/kotlin/eu/cloudnetservice/gradle/juppiter/tasks/PrepareModuleJson.kt new file mode 100644 index 0000000..99f5607 --- /dev/null +++ b/src/main/kotlin/eu/cloudnetservice/gradle/juppiter/tasks/PrepareModuleJson.kt @@ -0,0 +1,181 @@ +/* + * Copyright 2019-present CloudNetService team & contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package eu.cloudnetservice.gradle.juppiter.tasks + +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.module.kotlin.readValue +import com.fasterxml.jackson.module.kotlin.registerKotlinModule +import eu.cloudnetservice.gradle.juppiter.data.ModuleDependencyType +import eu.cloudnetservice.gradle.juppiter.util.ChecksumHelper +import eu.cloudnetservice.gradle.juppiter.util.ExtractModuleDependencyInformation +import eu.cloudnetservice.gradle.juppiter.util.UnknownDependencyException +import org.gradle.api.DefaultTask +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.provider.SetProperty +import org.gradle.api.tasks.* +import java.io.File +import java.net.HttpURLConnection +import java.net.URL + +@CacheableTask +abstract class PrepareModuleJson : DefaultTask() { + @get:Input + abstract val repositories: SetProperty + + @get:Nested + abstract val unresolvedModuleDependencies: SetProperty + + @get:Nested + abstract val unresolvedExternalDependencies: SetProperty + + @get:OutputFile + abstract val moduleJson: RegularFileProperty + + init { + repositories.finalizeValueOnRead() + unresolvedModuleDependencies.finalizeValueOnRead() + unresolvedExternalDependencies.finalizeValueOnRead() + } + + @TaskAction + fun run() { + val repositories = repositories.get() + val externalDependencies = unresolvedExternalDependencies.get().map { dep -> + if (dep.snapshot) { + // We pass all repositories for snapshot dependencies. + // Some dependencies of the snapshots could be on other repositories, there is no clear "source" repository + val properties = HashMap() + properties["group"] = dep.group + properties["name"] = dep.name + properties["version"] = dep.version + dep.classifier?.let { properties["classifier"] = it } + properties["repositories"] = repositories + ResolvedExternalDependency("maven-snapshot", dep.optional, dep.environments, properties) + } else { + repositories.forEach { repository -> + val repositoryUrl = URL(repository) + val depUrl = buildURL(repositoryUrl, dep) + if (!depUrl.hasBackedResource()) { + return@forEach + } + + val properties = HashMap() + properties["group"] = dep.group + properties["name"] = dep.name + properties["version"] = dep.version + dep.classifier?.let { properties["classifier"] = it } + properties["repository"] = repository + properties["url"] = depUrl.toString() + properties["checksum"] = "sha3256:${ChecksumHelper.sha3256(dep.file)}" + return@map ResolvedExternalDependency("maven", dep.optional, dep.environments, properties) + } + throw UnknownDependencyException("Failed to resolve $dep") + } + }.toSet() + val moduleDependencies = unresolvedModuleDependencies.get().map { dep -> + val id = ExtractModuleDependencyInformation.extract(dep.file).id + ResolvedModuleDependency(id, dep.versionRange, dep.dependencyType) + }.toSet() + val prepared = PreparedModuleJson(moduleDependencies, externalDependencies) + moduleJson.get().asFile.writeText(prepared.serialized()) + } + + private fun URL.hasBackedResource(): Boolean { + return with(openConnection() as HttpURLConnection) { + useCaches = false + connectTimeout = 500 + requestMethod = "HEAD" + instanceFollowRedirects = true + + setRequestProperty("User-Agent", "CloudNetService/juppiter Repository Resolve") + connect() + + responseCode == 200 + } + } + + private fun buildURL(repositoryUrl: URL, dep: UnresolvedExternalDependency): URL { + val group = dep.group.replace(".", "/") + val version = dep.version + val classifier = dep.classifier?.let { "-$it" } ?: "" + val componentName = "${dep.name}-$version$classifier.jar" + val urlPath = "$group/${dep.name}/$version/$componentName" + return URL(repositoryUrl, urlPath) + } + + data class UnresolvedModuleDependency( + @Input + val versionRange: String, + @Input + val optional: Boolean, + @InputFile + @PathSensitive(PathSensitivity.NONE) + val file: File, + @Input + val dependencyType: ModuleDependencyType + ) + + data class UnresolvedExternalDependency( + @Input + val snapshot: Boolean, + @Input + val group: String, + @Input + val name: String, + @Input + val version: String, + @Input + @Optional + val classifier: String?, + @Input + val environments: Set, + @Input + val optional: Boolean, + @InputFile + @PathSensitive(PathSensitivity.NONE) + val file: File + ) + + data class ResolvedModuleDependency( + val id: String, + val versionRange: String, + val dependencyType: ModuleDependencyType + ) + + data class ResolvedExternalDependency( + val loader: String, + val optional: Boolean, + val environments: Set, + // For future maintainers: take great care of serialization + val properties: Map + ) + + data class PreparedModuleJson( + val moduleDependencies: Set, + val externalDependencies: Set + ) { + fun serialized(): String = mapper.writeValueAsString(this) + + companion object { + private val mapper = ObjectMapper().registerKotlinModule() + + fun deserialize(input: String): PreparedModuleJson { + return mapper.readValue(input) + } + } + } +} diff --git a/src/main/kotlin/eu/cloudnetservice/gradle/juppiter/util/ChecksumHelper.kt b/src/main/kotlin/eu/cloudnetservice/gradle/juppiter/util/ChecksumHelper.kt index 71c7d6e..f025c03 100644 --- a/src/main/kotlin/eu/cloudnetservice/gradle/juppiter/util/ChecksumHelper.kt +++ b/src/main/kotlin/eu/cloudnetservice/gradle/juppiter/util/ChecksumHelper.kt @@ -24,11 +24,12 @@ import java.security.NoSuchAlgorithmException // copied from the CloudNet-Updater to keep consistent when generating checksums object ChecksumHelper { @Throws(IOException::class) - fun fileShaSum(path: File): String = - newSha3256Digest().run { - update(path.readBytes()) - bytesToHex(digest()) - } + fun fileShaSum(path: File): String = sha3256(path) + + fun sha3256(path: File): String = newSha3256Digest().run { + update(path.readBytes()) + bytesToHex(digest()) + } @Throws(NoSuchAlgorithmException::class) private fun newSha3256Digest(): MessageDigest = MessageDigest.getInstance("SHA3-256") diff --git a/src/main/kotlin/eu/cloudnetservice/gradle/juppiter/util/ExtractModuleDependencyInformation.kt b/src/main/kotlin/eu/cloudnetservice/gradle/juppiter/util/ExtractModuleDependencyInformation.kt new file mode 100644 index 0000000..ec916a7 --- /dev/null +++ b/src/main/kotlin/eu/cloudnetservice/gradle/juppiter/util/ExtractModuleDependencyInformation.kt @@ -0,0 +1,54 @@ +/* + * Copyright 2019-present CloudNetService team & contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package eu.cloudnetservice.gradle.juppiter.util + +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.ObjectMapper +import java.io.File +import java.util.jar.JarFile + +object ExtractModuleDependencyInformation { + private val objectMapper = ObjectMapper() + fun extract(file: File): ExtractedModuleDependency { + val jarFile = JarFile(file) + + // TODO remove, used for testing with old module system + jarFile.getJarEntry("module.json")?.let { jarFile.getInputStream(it) }.use { objectMapper.readTree(it) } + ?.let { it["name"] }?.textValue()?.let { return ExtractedModuleDependency(it) } + + val entry = jarFile.getJarEntry("META-INF/cloudnet-module.json") + ?: throw IllegalArgumentException("File ${file.absolutePath} is not a CloudNet module") + val jsonNode = jarFile.getInputStream(entry).use { objectMapper.readTree(it) }!! + + val schemaNode: JsonNode? = jsonNode["schema"] + if (schemaNode == null || !schemaNode.isTextual) { + throw IllegalArgumentException("File ${file.absolutePath} has a badly formatted cloudnet-module.json") + } + val schema = schemaNode.textValue()!! + return when (schema) { + "v1" -> { + val id = jsonNode["id"]?.textValue() + ?: throw IllegalArgumentException("File ${file.absolutePath} is missing module id") + ExtractedModuleDependency(id) + } + + else -> throw IllegalArgumentException("Unknown schema: $schema") + } + } +} + +data class ExtractedModuleDependency(val id: String) diff --git a/src/main/kotlin/eu/cloudnetservice/gradle/juppiter/util/MavenUtility.kt b/src/main/kotlin/eu/cloudnetservice/gradle/juppiter/util/MavenUtility.kt index 579de78..2e375fb 100644 --- a/src/main/kotlin/eu/cloudnetservice/gradle/juppiter/util/MavenUtility.kt +++ b/src/main/kotlin/eu/cloudnetservice/gradle/juppiter/util/MavenUtility.kt @@ -16,59 +16,105 @@ package eu.cloudnetservice.gradle.juppiter.util -import eu.cloudnetservice.gradle.juppiter.ModuleConfiguration -import eu.cloudnetservice.gradle.juppiter.UnknownDependencyException +import eu.cloudnetservice.gradle.juppiter.data.ModuleConfiguration import org.gradle.api.artifacts.repositories.MavenArtifactRepository +import org.gradle.api.internal.artifacts.repositories.resolver.MavenUniqueSnapshotComponentIdentifier import java.net.HttpURLConnection import java.net.URL -object MavenUtility { - fun findRepository( - dependency: ModuleConfiguration.Dependency, - repositories: Iterable, - ): ModuleConfiguration.Repository? { - repositories.forEach { - val urlInRepository = resolveUrlInRepository(dependency, it) ?: return@forEach - if (dependency.timestampedVersion != null || dependency.classifier != null) { - // timestamped version and classifier are not supported by CloudNet module loading currently - // therefore, we need to hack around this limitation by providing the url directly - dependency.url = urlInRepository.toExternalForm() - return null - } else { - // CloudNet can download this dependency directly from the maven repository - val repository = ModuleConfiguration.Repository(it.name) - repository.url = it.url.toURL().toExternalForm() - dependency.repo = repository.name - return repository - } - } +data class MavenDependency(val group: String, val name: String, val version: String, val repositoryName: String) - throw UnknownDependencyException(dependency) - } +data class MavenRepository(val url: String) - private fun resolveUrlInRepository( - dependency: ModuleConfiguration.Dependency, - repository: MavenArtifactRepository, - ): URL? { - val groupForUrl = dependency.group!!.replace(".", "/") - val componentVersion = dependency.timestampedVersion ?: dependency.version - val classifier = if (dependency.classifier != null) "-${dependency.classifier}" else "" - val componentName = "${dependency.name}-$componentVersion$classifier.jar" - val urlPath = "$groupForUrl/${dependency.name}/${dependency.version}/$componentName" - val fullUrl = URL(repository.url.toURL(), urlPath) - return if (resourceExists(fullUrl)) fullUrl else null - } +//object MavenUtility { +// fun findRepository( +// dependency: MavenDependency, +// repositories: Iterable, +// ): MavenRepository? { +// repositories.forEach { +// val urlInRepository = resolveUrlInRepository(dependency, it) ?: return@forEach +// if (dependency.timestampedVersion != null || dependency.classifier != null) { +// // timestamped version and classifier are not supported by CloudNet module loading currently +// // therefore, we need to hack around this limitation by providing the url directly +// dependency.url = urlInRepository.toExternalForm() +// return null +// } else { +// // CloudNet can download this dependency directly from the maven repository +// val repository = ModuleConfiguration.Repository(it.name) +// repository.url = it.url.toURL().toExternalForm() +// dependency.repo = repository.name +// return repository +// } +// } +// +// throw UnknownDependencyException(dependency) +// } +// +// private fun resolveUrlInRepository( +// dependency: MavenDependency, +// repository: MavenArtifactRepository, +// ): URL? { +// val groupForUrl = dependency.group!!.replace(".", "/") +// val componentVersion = dependency.timestampedVersion ?: dependency.version +// val classifier = if (dependency.classifier != null) "-${dependency.classifier}" else "" +// val componentName = "${dependency.name}-$componentVersion$classifier.jar" +// val urlPath = "$groupForUrl/${dependency.name}/${dependency.version}/$componentName" +// val fullUrl = URL(repository.url.toURL(), urlPath) +// return if (resourceExists(fullUrl)) fullUrl else null +// } +// +// private fun resourceExists(url: URL): Boolean = +// with(url.openConnection() as HttpURLConnection) { +// useCaches = false +// connectTimeout = 5000 +// requestMethod = "HEAD" +// instanceFollowRedirects = true +// +// setRequestProperty("User-Agent", "CloudNetService/juppiter Repository Resolve") +// connect() +// +// responseCode == 200 +// } +//} - private fun resourceExists(url: URL): Boolean = - with(url.openConnection() as HttpURLConnection) { - useCaches = false - connectTimeout = 5000 - requestMethod = "HEAD" - instanceFollowRedirects = true - - setRequestProperty("User-Agent", "CloudNetService/juppiter Repository Resolve") - connect() - - responseCode == 200 - } +fun test() { +// if (!this.resolved.getAndSet(true)) { +// name = name ?: project.name +// group = group ?: project.group.toString() +// version = version ?: project.version.toString() +// +// // other stuff +// author = author ?: "Anonymous" +// website = website ?: "https://cloudnetservice.eu" +// description = description ?: project.description ?: "Just another CloudNet module" +// +// // dependencies of the module we need to resolve +// libraries.resolvedConfiguration.resolvedArtifacts.forEach { +// val versionId = it.moduleVersion.id +// val dependency = Dependency(it.name) +// dependency.group = versionId.group +// dependency.version = versionId.version +// dependency.classifier = it.classifier +// dependency.checksum = ChecksumHelper.fileShaSum(it.file) +// +// val componentIdentifier = it.id.componentIdentifier +// if (versionId.version.endsWith("-SNAPSHOT") && componentIdentifier is MavenUniqueSnapshotComponentIdentifier) { +// dependency.timestampedVersion = componentIdentifier.timestampedVersion +// } +// +// dependencies.add(dependency) +// } +// +// // dependencies of the module that are other modules, so we only need: group, name, version +// moduleDependencies.resolvedConfiguration.firstLevelModuleDependencies +// .map { it.module.id } +// .forEach { +// val dependency = Dependency(it.name) +// dependency.group = it.group +// dependency.version = it.version +// dependency.needsRepoResolve = false +// +// dependencies.add(dependency) +// } +// } } diff --git a/src/main/kotlin/eu/cloudnetservice/gradle/juppiter/UnknownDependencyException.kt b/src/main/kotlin/eu/cloudnetservice/gradle/juppiter/util/UnknownDependencyException.kt similarity index 79% rename from src/main/kotlin/eu/cloudnetservice/gradle/juppiter/UnknownDependencyException.kt rename to src/main/kotlin/eu/cloudnetservice/gradle/juppiter/util/UnknownDependencyException.kt index 4c9fde8..429f750 100644 --- a/src/main/kotlin/eu/cloudnetservice/gradle/juppiter/UnknownDependencyException.kt +++ b/src/main/kotlin/eu/cloudnetservice/gradle/juppiter/util/UnknownDependencyException.kt @@ -14,8 +14,8 @@ * limitations under the License. */ -package eu.cloudnetservice.gradle.juppiter +package eu.cloudnetservice.gradle.juppiter.util class UnknownDependencyException( - dep: ModuleConfiguration.Dependency, -) : Exception("Unable to resolve dependency ${dep.group}:${dep.name}:${dep.version}") + dependencyIdentifier: String, +) : Exception("Unable to resolve dependency $dependencyIdentifier") diff --git a/test/build.gradle.kts b/test/build.gradle.kts new file mode 100644 index 0000000..e809edb --- /dev/null +++ b/test/build.gradle.kts @@ -0,0 +1,127 @@ +import eu.cloudnetservice.gradle.juppiter.data.* + +/* + * Copyright 2019-present CloudNetService team & contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { + id("eu.cloudnetservice.juppiter") + `java-library` + id("net.fabricmc.fabric-loom") version "1.15-SNAPSHOT" apply false +// id("net.fabricmc.fabric-loom-remap") apply false +} + +data class Cls(@Input val x: String) + +moduleJson { + entrypoint = "abc" + id = "test" + name = "Test Module" + artifacts.add(ModuleArtifact(objects).apply { + this.source = ModuleArtifactSource.CLASSPATH + this.sourcePath = "test/source" + this.targetPath = "test/target" + this.environments.add("wrapper env ??") + }) + artifacts.add(ModuleArtifact(objects).apply { + this.source = ModuleArtifactSource.FILESYSTEM + this.sourcePath = "test/source" + this.targetPath = "test/target" + this.environments.add("wrapper env ??") + }) +// dependencies.add(ModuleDependency(objects).apply { +// this.id = "bridge" +// this.versionRange = "69+" +// this.dependencyType = ModuleDependencyType.REQUIRED +// }) +// externalDependencies.add(ModuleExternalDependency(objects).apply { +// this.environments.add("* or sth") +// this.loader = "was auch immer loader sein soll" +// this.optional = true +// this.properties.putComplex("test1", Cls("val1")) +// this.properties.putComplex("test2", provider { Cls("val2") }) +// }) + contributors.add(ModuleContributor(objects).apply { + this.name = "se big bad noob" + this.properties.putSimple("test1", "val1") + this.properties.putSimple("test2", provider { "val2" }) + }) + this.properties.putSimple("test1", "val1") + this.properties.putSimple("test2", provider { "val2" }) +} + +flavors { + registerJavaSourceSets() + val common = addFlavor("common") + common.dependOn(main) + val fabric = addFlavor("fabric/base") + fabric.dependOn(common) + val forge = addFlavor("forge") + forge.dependOn(common) + val fabric1211 = addFlavor("fabric/1_21_1") + fabric1211.dependOn(fabric) + + val merged = addFlavor("merged") + merged.dependOn(forge) + merged.dependOn(fabric1211) +} + +tasks.assemble { + dependsOn(tasks.named("mergedJar")) +} + +repositories { + mavenCentral() + maven("https://central.sonatype.com/repository/maven-snapshots/") + maven { + name = "papermc" + url = uri("https://repo.papermc.io/repository/maven-public/") + } + + maven("https://repository.derklaro.dev/releases/") + maven("https://repository.derklaro.dev/snapshots/") +} + +java.toolchain.languageVersion.set(JavaLanguageVersion.of(25)) +//java.disableAutoTargetJvm() + +dependencies { + "moduleDependency"("eu.cloudnetservice.cloudnet:bridge-impl:[4.0.0-RC14, 4.0.0-RC16]") +// api("com.google.code.gson:gson:2.13.2") + moduleLibrary("com.google.code.gson:gson:[2.13.0, 2.13.5]") +// compileOnly("io.papermc.paper:paper-api:1.21.11-R0.1-20260109.190012-50") { +// this.isTransitive = false +// } + moduleLibrary("com.google.guava:guava:33.5.0-jre") + moduleLibrary("io.papermc.paper:paper-api:1.21.11-R0.1-SNAPSHOT") +// moduleLibrary("io.papermc.paper:paper-api:1.21.11-R0.1-20260310.030221-86:javadoc") +// "commonApi"("com.google.code.gson:gson:2.13.2") +} + +val a = true + +val c2 = configurations.resolvable("abc") { + this.extendsFrom(configurations.compileOnly) +} +//c2.get().run { +// this.allDependencies.forEach { +// println(it.version) +// } +// resolvedConfiguration.resolvedArtifacts.forEach { +// val version = it.moduleVersion.id.version +// println(version) +// println(it.id.componentIdentifier is MavenUniqueSnapshotComponentIdentifier) +// } +//} diff --git a/test/gradle.properties b/test/gradle.properties new file mode 100644 index 0000000..e7f7d7f --- /dev/null +++ b/test/gradle.properties @@ -0,0 +1,28 @@ +# +# Copyright 2019-present CloudNetService team & contributors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# kotlin configuration +kotlin.code.style=official + +# gradle configuration +org.gradle.caching=true +org.gradle.parallel=true +org.gradle.warning.mode=all +org.gradle.logging.level=warn +org.gradle.configureondemand=true +org.gradle.configuration-cache=true +org.gradle.configuration-cache.problems=fail +org.gradle.jvmargs=-Xmx2G -XX:MaxMetaspaceSize=512m -Dfile.encoding=UTF-8 diff --git a/test/gradle/wrapper/gradle-wrapper.jar b/test/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..d997cfc Binary files /dev/null and b/test/gradle/wrapper/gradle-wrapper.jar differ diff --git a/test/gradle/wrapper/gradle-wrapper.properties b/test/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..dbc3ce4 --- /dev/null +++ b/test/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.0-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/test/gradlew b/test/gradlew new file mode 100755 index 0000000..0262dcb --- /dev/null +++ b/test/gradlew @@ -0,0 +1,248 @@ +#!/bin/sh + +# +# Copyright © 2015 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/b631911858264c0b6e4d6603d677ff5218766cee/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/test/gradlew.bat b/test/gradlew.bat new file mode 100644 index 0000000..c4bdd3a --- /dev/null +++ b/test/gradlew.bat @@ -0,0 +1,93 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/src/main/kotlin/eu/cloudnetservice/gradle/juppiter/util/GradleUtil.kt b/test/settings.gradle.kts similarity index 59% rename from src/main/kotlin/eu/cloudnetservice/gradle/juppiter/util/GradleUtil.kt rename to test/settings.gradle.kts index a0652b7..2031a59 100644 --- a/src/main/kotlin/eu/cloudnetservice/gradle/juppiter/util/GradleUtil.kt +++ b/test/settings.gradle.kts @@ -14,21 +14,14 @@ * limitations under the License. */ -package eu.cloudnetservice.gradle.juppiter.util +pluginManagement { + includeBuild("..") -import org.gradle.api.plugins.ExtensionContainer -import kotlin.reflect.KClass - -object GradleUtil { - fun findOrAddExtension( - extensions: ExtensionContainer, - name: String, - type: KClass, - factory: () -> E, - ): E = - extensions.findByType(type.java) ?: run { - val extension = factory.invoke() - extensions.add(name, extension) - extension + repositories { + maven("https://maven.fabricmc.net/") { + name = "Fabric" } + mavenCentral() + gradlePluginPortal() + } } diff --git a/test/src/common/java/test/TestCommon.java b/test/src/common/java/test/TestCommon.java new file mode 100644 index 0000000..2d2b8f7 --- /dev/null +++ b/test/src/common/java/test/TestCommon.java @@ -0,0 +1,21 @@ +/* + * Copyright 2019-present CloudNetService team & contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package test; + +public class TestCommon { + +} diff --git a/test/src/fabric/1_21_1/java/test/TestFabric1_21_1.java b/test/src/fabric/1_21_1/java/test/TestFabric1_21_1.java new file mode 100644 index 0000000..587e547 --- /dev/null +++ b/test/src/fabric/1_21_1/java/test/TestFabric1_21_1.java @@ -0,0 +1,28 @@ +/* + * Copyright 2019-present CloudNetService team & contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package test; + +import com.google.gson.Gson; + +public class TestFabric1_21_1 { + { + TestFabric.class.getName(); + Gson.class.getName(); + TestApi.class.getName(); + TestCommon.class.getName(); + } +} diff --git a/test/src/fabric/base/java/test/TestFabric.java b/test/src/fabric/base/java/test/TestFabric.java new file mode 100644 index 0000000..d2ce5d9 --- /dev/null +++ b/test/src/fabric/base/java/test/TestFabric.java @@ -0,0 +1,26 @@ +/* + * Copyright 2019-present CloudNetService team & contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package test; + +public class TestFabric { + + { + System.out.println(TestApi.class.getName()); + com.google.gson.Gson g; + } + +} diff --git a/test/src/forge/java/test/TestForge.java b/test/src/forge/java/test/TestForge.java new file mode 100644 index 0000000..648962d --- /dev/null +++ b/test/src/forge/java/test/TestForge.java @@ -0,0 +1,21 @@ +/* + * Copyright 2019-present CloudNetService team & contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package test; + +public class TestForge { + +} diff --git a/test/src/main/java/test/TestApi.java b/test/src/main/java/test/TestApi.java new file mode 100644 index 0000000..f6dc111 --- /dev/null +++ b/test/src/main/java/test/TestApi.java @@ -0,0 +1,25 @@ +/* + * Copyright 2019-present CloudNetService team & contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package test; + +import com.google.gson.Gson; + +public class TestApi { + { + Gson g; + } +}