Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* gremlin
*
* Copyright (c) 2025 Jason Penilla
*
* 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 xyz.jpenilla.gremlin.gradle

import org.gradle.api.file.ArchiveOperations
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.provider.Property
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.Optional
import org.gradle.api.tasks.TaskProvider
import org.gradle.jvm.tasks.Jar
import org.gradle.kotlin.dsl.attributes
import javax.inject.Inject

abstract class BootstrapJar : Jar() {
@get:InputFiles
abstract val gremlinRuntime: ConfigurableFileCollection

@get:Inject
abstract val archiveOps: ArchiveOperations

@get:Input
@get:Optional
abstract val mainClass: Property<String>

init {
manifest.attributes(
"Main-Class" to "xyz.jpenilla.gremlin.runtime.GremlinBootstrap",
)
val runtime = gremlinRuntime.elements.map {
it.map { e ->
archiveOps.zipTree(e)
}
}
from(runtime) {
exclude("META-INF/*")
}
}

override fun copy() {
manifest.attributes(
"Gremlin-Main-Class" to mainClass.get(),
)
super.copy()
}

fun nestJars(task: TaskProvider<out PrepareNestedJars>) {
from(task.flatMap { it.outputDir }) {
into("nested-jars")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,32 @@
package xyz.jpenilla.gremlin.gradle

import org.gradle.api.provider.Property
import org.gradle.api.tasks.AbstractCopyTask
import org.gradle.api.tasks.TaskProvider

abstract class GremlinExtension {
abstract val defaultJarRelocatorDependencies: Property<Boolean>
abstract val defaultGremlinRuntimeDependency: Property<Boolean>
abstract val addGremlinRuntimeToCompileClasspath: Property<Boolean>
abstract val addGremlinRuntimeToRuntimeClasspath: Property<Boolean>

init {
init()
}

private fun init() {
defaultJarRelocatorDependencies.convention(true)
defaultGremlinRuntimeDependency.convention(true)
addGremlinRuntimeToCompileClasspath.convention(true)
addGremlinRuntimeToRuntimeClasspath.convention(true)
}

fun nestJars(
prepareNestedJars: TaskProvider<out PrepareNestedJars>,
into: TaskProvider<out AbstractCopyTask>
) {
into.configure {
from(prepareNestedJars.flatMap { it.outputDir }) {
into("nested-jars")
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,19 @@ class GremlinPlugin : Plugin<Project> {
runtimeClasspathAttributes(target.objects)
}

val gremlinRuntime = target.configurations.register("gremlinRuntime") {
makeResolvable()
runtimeClasspathAttributes(target.objects)
defaultDependencies {
add(target.dependencies.create(Dependencies.DEFAULT_GREMLIN_RUNTIME))
}
}

val nestedJars = target.configurations.register("nestedJars") {
makeResolvable()
runtimeClasspathAttributes(target.objects)
}

val writeDependencies = target.tasks.register("writeDependencies", WriteDependencySet::class) {
dependencies.setFrom(runtimeDownload)
relocationDependencies.setFrom(jarRelocatorRuntime)
Expand All @@ -64,6 +77,17 @@ class GremlinPlugin : Plugin<Project> {
}
}

val prepareNestedJars = target.tasks.register<PrepareNestedJars>("prepareNestedJars") {
this.nestedJars.setFrom(nestedJars)
}

target.tasks.register<BootstrapJar>("bootstrapJar") {
this.gremlinRuntime.from(gremlinRuntime)
archiveClassifier.convention("gremlin")
destinationDirectory.convention(target.layout.buildDirectory.dir("libs"))
nestJars(prepareNestedJars)
}

target.afterEvaluate {
if (ext.defaultJarRelocatorDependencies.get()) {
target.dependencies {
Expand All @@ -72,9 +96,14 @@ class GremlinPlugin : Plugin<Project> {
}
}
}
if (ext.defaultGremlinRuntimeDependency.get()) {
target.dependencies {
java.sourceSets.getByName("main").implementationConfigurationName(Dependencies.DEFAULT_GREMLIN_RUNTIME)
if (ext.addGremlinRuntimeToCompileClasspath.get()) {
target.configurations.named(java.sourceSets.getByName("main").compileClasspathConfigurationName) {
extendsFrom(gremlinRuntime.get())
}
}
if (ext.addGremlinRuntimeToRuntimeClasspath.get()) {
target.configurations.named(java.sourceSets.getByName("main").runtimeClasspathConfigurationName) {
extendsFrom(gremlinRuntime.get())
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* gremlin
*
* Copyright (c) 2025 Jason Penilla
*
* 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 xyz.jpenilla.gremlin.gradle

import org.gradle.api.DefaultTask
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.TaskAction
import java.nio.file.Files
import kotlin.io.path.ExperimentalPathApi
import kotlin.io.path.deleteRecursively
import kotlin.io.path.writeText

@OptIn(ExperimentalPathApi::class)
abstract class PrepareNestedJars : DefaultTask() {
@get:OutputDirectory
abstract val outputDir: DirectoryProperty

@get:InputFiles
abstract val nestedJars: ConfigurableFileCollection

init {
init()
}

private fun init() {
outputDir.convention(project.layout.buildDirectory.dir("nested-jars/$name"))
}

@TaskAction
fun run() {
val outputPath = outputDir.path
outputPath.deleteRecursively()
Files.createDirectories(outputPath)

val index = mutableListOf<String>()
nestedJars.files.forEach { jar ->
Files.copy(jar.toPath(), outputPath.resolve(jar.name))
index += jar.name
}
outputPath.resolve("index.txt").writeText(index.joinToString("\n"))
}
}
14 changes: 14 additions & 0 deletions gradle-plugin/src/main/kotlin/xyz/jpenilla/gremlin/gradle/utils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,25 @@
*/
package xyz.jpenilla.gremlin.gradle

import org.gradle.api.file.FileSystemLocationProperty
import java.io.InputStream
import java.net.URI
import java.nio.file.FileSystem
import java.nio.file.FileSystems
import java.nio.file.Path
import java.security.MessageDigest
import kotlin.io.path.inputStream

val FileSystemLocationProperty<*>.path: Path
get() = get().asFile.toPath()

fun Path.jarUri(): URI = URI.create("jar:" + toUri().toString())

fun <R> Path.openZip(op: (FileSystem) -> R): R =
FileSystems.newFileSystem(jarUri(), emptyMap<String, Any>()).use { fs ->
op(fs)
}

enum class HashingAlgorithm(val algorithmName: String) {
SHA256("SHA-256"),
SHA1("SHA-1");
Expand Down
Loading