Skip to content

Commit 204636a

Browse files
committed
Downloading Kotlin compiler dependencies at runtime
1 parent 707b18a commit 204636a

File tree

22 files changed

+593
-59
lines changed

22 files changed

+593
-59
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,5 @@ out/
3232

3333
# Linux temp files
3434
*~
35+
36+
local.properties

build-logic/convention/src/main/kotlin/br/com/devsrsouza/bukkript/buildlogic/BukkriptBuildPlugin.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class BukkriptBuildPlugin : Plugin<Project> {
2020

2121
plugins.withType<KotlinBasePlugin> {
2222
extensions.configure<KotlinJvmProjectExtension> {
23-
jvmToolchain(8) // TODO: upgrade to 11?
23+
jvmToolchain(17) // TODO: upgrade to 11?
2424
}
2525
tasks.withType<KotlinCompile> {
2626
kotlinOptions.freeCompilerArgs = listOf("-Xcontext-receivers")

build.gradle.kts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,12 @@ plugins {
33
id(libs.plugins.bukkript.build.get().pluginId) apply false
44
alias(libs.plugins.dependencyGraph)
55
//alias(libs.plugins.maven) apply false
6-
76
}
7+
8+
subprojects {
9+
repositories {
10+
mavenCentral()
11+
maven("https://repo.papermc.io/repository/maven-public/")
12+
maven("https://raw.githubusercontent.com/KotlinMinecraft/KotlinBukkitAPI-Repository/main")
13+
}
14+
}

gradle.properties

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
1-
kotlin.code.style=official
1+
kotlin.code.style=official
2+
kotlin.stdlib.default.dependency=false
3+
4+
version=1.0.0-SNAPSHOT

gradle/libs.versions.toml

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ plugin-ktlint = "3.15.0"
33
plugin-maven = "0.22.0"
44
plugin-dependency-graph = "0.7.0"
55
plugin-shadow = "8.1.1"
6-
kotlin = "1.8.20"
6+
kotlin = "1.8.22"
77

88
coroutines = "1.7.1"
99
kodein = "7.20.1"
@@ -16,6 +16,7 @@ junit = "5.8.2"
1616

1717
# minecraft
1818
spigot = "1.20.1-R0.1-SNAPSHOT"
19+
paper = "1.20-R0.1-SNAPSHOT"
1920
kotlinbukkitapi = "1.0.0-SNAPSHOT"
2021
pluginYml = "0.6.0"
2122
bstats = "3.0.2"
@@ -33,12 +34,18 @@ hikaricp = { module = "com.zaxxer:HikariCP", version.ref = "hikaricp" }
3334

3435
kotlinx-serialization-core = { module = "org.jetbrains.kotlinx:kotlinx-serialization-core", version.ref = "ktxSerialization" }
3536

37+
maven-resolver-transportHttp = { module = "org.apache.maven.resolver:maven-resolver-transport-http", version = "1.9.13" }
38+
maven-resolver-connectorBasic = { module = "org.apache.maven.resolver:maven-resolver-connector-basic", version = "1.9.13" }
39+
40+
# tests
41+
3642
junit-engine = { module = "org.junit.jupiter:junit-jupiter-engine", version.ref = "junit" }
3743
junit-api = { module = "org.junit.jupiter:junit-jupiter-api", version.ref = "junit" }
3844

3945
# minecraft
4046

4147
spigot-api = { module = "org.spigotmc:spigot-api", version.ref = "spigot" }
48+
paper-api = { module = "io.papermc.paper:paper-api", version.ref = "paper" }
4249

4350
bstats = { module = "org.bstats:bstats-bukkit", version.ref = "bstats" }
4451

@@ -63,7 +70,8 @@ kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", versi
6370
ktlint = { id = "org.jmailen.kotlinter", version.ref = "plugin-ktlint"}
6471
dependencyGraph = { id = "com.vanniktech.dependency.graph.generator", version.ref = "plugin-dependency-graph" }
6572
maven = { id = "com.vanniktech.maven.publish", version.ref = "plugin-maven" }
66-
pluginYml = { id = "net.minecrell.plugin-yml.bukkit", version.ref = "pluginYml" }
73+
pluginYml-bukkit = { id = "net.minecrell.plugin-yml.bukkit", version.ref = "pluginYml" }
74+
pluginYml-paper = { id = "net.minecrell.plugin-yml.paper", version.ref = "pluginYml" }
6775
shadow = { id = "com.github.johnrengelman.shadow", version.ref = "plugin-shadow" }
6876

6977
bukkript-build = { id = "bukkript.build" }

plugin/build.gradle.kts

Lines changed: 69 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,53 @@
1+
import java.util.Properties
2+
13
plugins {
24
id(libs.plugins.bukkript.build.get().pluginId)
35
alias(libs.plugins.shadow)
4-
alias(libs.plugins.pluginYml)
6+
alias(libs.plugins.pluginYml.bukkit)
7+
alias(libs.plugins.pluginYml.paper)
58
}
69

710
dependencies {
811
compileOnly(libs.spigot.api)
9-
12+
compileOnly(libs.paper.api)
13+
compileOnly(files("/Users/gabriel/opensource/minecraft/minecraft-server/versions/1.20.1/paper-1.20.1.jar"))
14+
compileOnly(libs.maven.resolver.transportHttp)
15+
compileOnly(libs.maven.resolver.connectorBasic)
16+
1017
implementation(libs.bstats)
1118

12-
implementation(libs.coroutines)
13-
14-
implementation(projects.scriptHost) {
15-
// Excluding aether already available at Spigot latest versions
16-
exclude("org.apache.maven.resolver")
17-
exclude("org.apache.maven.wagon")
18-
exclude("org.apache.maven.shared")
19-
exclude("org.apache.maven")
19+
implementation(projects.scriptHost)
20+
21+
bukkitLibrary(kotlin("stdlib"))
22+
implementation(libs.kotlinbukkitapi.architecture) {
23+
// ignore this, this will be downloaded by spigot
24+
exclude("org.jetbrains.kotlin")
25+
exclude("org.jetbrains.kotlinx") // TODO: remove when KBAPI Architecture no longer depend on Coroutines for no reason
2026
}
21-
22-
implementation(libs.kotlinbukkitapi.coroutines)
23-
implementation(libs.kotlinbukkitapi.utility)
24-
implementation(libs.kotlinbukkitapi.architecture)
25-
implementation(libs.kotlinbukkitapi.extensions)
26-
implementation(libs.kotlinbukkitapi.exposed)
27-
implementation(libs.kotlinbukkitapi.commandLegacy)
28-
implementation(libs.kotlinbukkitapi.menu)
29-
implementation(libs.kotlinbukkitapi.scoreboardLegacy)
27+
28+
// this is Called paper libraries but will also be used in Spigot
29+
// because spigot does not support non maven central repository
30+
// so the logic here is: we shadow kotlinbukkitapi-architecture only without Kotlin
31+
// we let kotlin stdlib be downloaded by Spigot and then with Kotlin + Architecture
32+
// the plugin loaded correctly, and we can the Maven Aether Resolver to fully resolve
33+
// dependencies from paper dependency file.
34+
// On Paper side, we just download Kotlin Stdlib because the rest will be avaiable at the
35+
// final paper dependencies files.
36+
paperLibrary(libs.coroutines)
37+
paperLibrary(libs.kotlinbukkitapi.coroutines)
38+
paperLibrary(libs.kotlinbukkitapi.utility)
39+
paperLibrary(libs.kotlinbukkitapi.extensions)
40+
paperLibrary(libs.kotlinbukkitapi.exposed)
41+
paperLibrary(libs.kotlinbukkitapi.commandLegacy)
42+
paperLibrary(libs.kotlinbukkitapi.menu)
43+
paperLibrary(libs.kotlinbukkitapi.scoreboardLegacy)
44+
45+
// scripting dependencies
46+
paperLibrary(kotlin("scripting-jvm"))
47+
paperLibrary(kotlin("scripting-dependencies"))
48+
paperLibrary(kotlin("scripting-dependencies-maven"))
49+
paperLibrary(kotlin("scripting-jvm-host-unshaded"))
50+
//paperLibrary(kotlin("scripting-compiler-embeddable"))
3051
}
3152

3253
tasks {
@@ -40,12 +61,40 @@ tasks {
4061
}
4162
}
4263

64+
val pluginMain = "br.com.devsrsouza.bukkript.plugin.BukkriptPlugin"
65+
4366
bukkit {
4467
name = "Bukkript"
45-
main = "br.com.devsrsouza.bukkript.plugin.BukkriptPlugin"
68+
main = pluginMain
4669
author = "DevSrSouza"
4770
website = "mygithub.libinneed.workers.dev.br/DevSrSouza"
71+
apiVersion = "1.19"
4872

4973
description = "Bukkript Scripting."
5074
}
5175

76+
paper {
77+
loader = "br.com.devsrsouza.bukkript.libraryresolver.PluginLibrariesLoader"
78+
generateLibrariesJson = true
79+
main = pluginMain
80+
description = "Bukkript Scripting."
81+
apiVersion = "1.19"
82+
}
83+
84+
val localProperties = Properties()
85+
.apply {
86+
load(File(rootDir, "local.properties").inputStream())
87+
}
88+
89+
val serverPluginFolder = localProperties["serverPluginsFolder"]?.toString()
90+
if(serverPluginFolder != null) {
91+
tasks.register<Copy>("copyToServer") {
92+
dependsOn(tasks.shadowJar)
93+
doFirst {
94+
File(serverPluginFolder).listFiles()?.filter { it.startsWith("Bukkript") && it.extension == "jar" }
95+
?.forEach { it.delete() }
96+
}
97+
this.from(tasks.shadowJar.get().archiveFile.get().asFile)
98+
this.into(File(serverPluginFolder))
99+
}
100+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package br.com.devsrsouza.bukkript.libraryresolver;
2+
3+
import io.papermc.paper.plugin.loader.library.ClassPathLibrary;
4+
import io.papermc.paper.plugin.loader.library.LibraryLoadingException;
5+
import io.papermc.paper.plugin.loader.library.LibraryStore;
6+
import org.jetbrains.annotations.NotNull;
7+
8+
import java.io.File;
9+
import java.util.List;
10+
11+
public class BukkriptClassPathLibrary implements ClassPathLibrary {
12+
private final BukkriptMavenLibraryResolver resolver;
13+
14+
public BukkriptClassPathLibrary(BukkriptMavenLibraryResolver resolver) {
15+
this.resolver = resolver;
16+
}
17+
18+
@Override
19+
public void register(@NotNull LibraryStore store) throws LibraryLoadingException {
20+
List<File> libraries = resolver.download();
21+
for (File library : libraries) {
22+
store.addLibrary(library.toPath());
23+
}
24+
}
25+
}
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
package br.com.devsrsouza.bukkript.libraryresolver;
2+
3+
import com.google.common.collect.Lists;
4+
import org.apache.maven.repository.internal.MavenRepositorySystemUtils;
5+
import org.eclipse.aether.DefaultRepositorySystemSession;
6+
import org.eclipse.aether.RepositorySystem;
7+
import org.eclipse.aether.collection.CollectRequest;
8+
import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory;
9+
import org.eclipse.aether.graph.Dependency;
10+
import org.eclipse.aether.impl.DefaultServiceLocator;
11+
import org.eclipse.aether.repository.LocalRepository;
12+
import org.eclipse.aether.repository.RemoteRepository;
13+
import org.eclipse.aether.repository.RepositoryPolicy;
14+
import org.eclipse.aether.resolution.DependencyRequest;
15+
import org.eclipse.aether.resolution.DependencyResolutionException;
16+
import org.eclipse.aether.resolution.DependencyResult;
17+
import org.eclipse.aether.spi.connector.RepositoryConnectorFactory;
18+
import org.eclipse.aether.spi.connector.transport.TransporterFactory;
19+
import org.eclipse.aether.transfer.AbstractTransferListener;
20+
import org.eclipse.aether.transfer.TransferCancelledException;
21+
import org.eclipse.aether.transfer.TransferEvent;
22+
import org.eclipse.aether.transport.http.HttpTransporterFactory;
23+
import org.eclipse.aether.util.filter.ExclusionsDependencyFilter;
24+
import org.eclipse.aether.util.repository.SimpleArtifactDescriptorPolicy;
25+
import org.jetbrains.annotations.NotNull;
26+
27+
import java.io.File;
28+
import java.util.ArrayList;
29+
import java.util.List;
30+
import java.util.logging.Level;
31+
import java.util.logging.Logger;
32+
import java.util.stream.Collectors;
33+
34+
public class BukkriptMavenLibraryResolver {
35+
36+
private static final Logger logger = Logger.getLogger("BukkriptMavenLibraryResolver");
37+
38+
// This dependencies is shaded or already loaded before the plugin initialize.
39+
private static final List<String> excludedDependencies = Lists.newArrayList(
40+
"br.com.devsrsouza.kotlinbukkitapi:architecture"
41+
);
42+
private static final String kotlinStd = "org.jetbrains.kotlin:kotlin-stdlib";
43+
44+
45+
private final RepositorySystem repository;
46+
private final DefaultRepositorySystemSession session;
47+
private final List<RemoteRepository> repositories = new ArrayList<>();
48+
private final List<Dependency> dependencies = new ArrayList<>();
49+
private boolean shouldExcludeKotlin = false;
50+
51+
/**
52+
* Creates a new maven library resolver instance.
53+
* <p>
54+
* The created instance will use the servers {@code libraries} folder to cache fetched libraries in.
55+
* Notably, the resolver is created without any repository, not even maven central.
56+
* It is hence crucial that plugins which aim to use this api register all required repositories before
57+
* submitting the {@link io.papermc.paper.plugin.loader.library.impl.MavenLibraryResolver} to the {@link io.papermc.paper.plugin.loader.PluginClasspathBuilder}.
58+
*/
59+
public BukkriptMavenLibraryResolver() {
60+
DefaultServiceLocator locator = MavenRepositorySystemUtils.newServiceLocator();
61+
locator.addService(RepositoryConnectorFactory.class, BasicRepositoryConnectorFactory.class);
62+
locator.addService(TransporterFactory.class, HttpTransporterFactory.class);
63+
64+
this.repository = locator.getService(RepositorySystem.class);
65+
this.session = MavenRepositorySystemUtils.newSession();
66+
67+
this.session.setArtifactDescriptorPolicy(new SimpleArtifactDescriptorPolicy(true, true)); // this is also required for Github repo type.
68+
this.session.setChecksumPolicy(RepositoryPolicy.CHECKSUM_POLICY_IGNORE); // Ignore instead of FAIL/WARN
69+
this.session.setLocalRepositoryManager(this.repository.newLocalRepositoryManager(this.session, new LocalRepository("libraries")));
70+
this.session.setTransferListener(new AbstractTransferListener() {
71+
@Override
72+
public void transferInitiated(@NotNull TransferEvent event) throws TransferCancelledException {
73+
logger.log(Level.INFO, "Downloading {0}", event.getResource().getRepositoryUrl() + event.getResource().getResourceName());
74+
}
75+
});
76+
this.session.setReadOnly();
77+
78+
// SETUP DEPENDENCIES FROM FILE
79+
LibrariesReader.PluginLibraries libraries = LibrariesReader.load(BukkriptMavenLibraryResolver.class);
80+
dependencies.addAll(libraries.asDependencies().collect(Collectors.toList()));
81+
repositories.addAll(libraries.asRepositories().collect(Collectors.toList()));
82+
}
83+
84+
public void addDependency(@NotNull Dependency dependency) {
85+
this.dependencies.add(dependency);
86+
}
87+
88+
public void addRepository(@NotNull RemoteRepository remoteRepository) {
89+
this.repositories.add(remoteRepository);
90+
}
91+
92+
public List<File> download() {
93+
List<RemoteRepository> repos = this.repository.newResolutionRepositories(this.session, this.repositories);
94+
95+
DependencyResult result;
96+
try {
97+
ArrayList<String> exclusion = Lists.newArrayList(excludedDependencies);
98+
if(shouldExcludeKotlin) exclusion.add(kotlinStd);
99+
100+
result = this.repository.resolveDependencies(
101+
this.session,
102+
new DependencyRequest(
103+
new CollectRequest((Dependency) null, this.dependencies, repos),
104+
new ExclusionsDependencyFilter(exclusion)
105+
)
106+
);
107+
} catch (DependencyResolutionException ex) {
108+
throw new LibraryLoadingException("Error resolving libraries", ex);
109+
}
110+
111+
return result.getArtifactResults()
112+
.stream()
113+
.map(artifact -> artifact.getArtifact().getFile())
114+
.collect(Collectors.toList());
115+
}
116+
117+
public BukkriptMavenLibraryResolver shouldExcludeKotlin(boolean shouldExcludeKotlin) {
118+
this.shouldExcludeKotlin = shouldExcludeKotlin;
119+
return this;
120+
}
121+
}
122+
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package br.com.devsrsouza.bukkript.libraryresolver;
2+
3+
import com.google.gson.Gson;
4+
import org.eclipse.aether.artifact.DefaultArtifact;
5+
import org.eclipse.aether.graph.Dependency;
6+
import org.eclipse.aether.repository.RemoteRepository;
7+
8+
import java.io.IOException;
9+
import java.io.InputStreamReader;
10+
import java.nio.charset.StandardCharsets;
11+
import java.util.List;
12+
import java.util.Map;
13+
import java.util.stream.Stream;
14+
15+
public class LibrariesReader {
16+
public static PluginLibraries load(Class<?> context) {
17+
try (var in = context.getResourceAsStream("/paper-libraries.json")) {
18+
return new Gson().fromJson(new InputStreamReader(in, StandardCharsets.UTF_8), PluginLibraries.class);
19+
} catch (IOException e) {
20+
throw new RuntimeException(e);
21+
}
22+
}
23+
24+
public record PluginLibraries(Map<String, String> repositories, List<String> dependencies) {
25+
public Stream<Dependency> asDependencies() {
26+
return dependencies.stream()
27+
.map(d -> new Dependency(new DefaultArtifact(d), null));
28+
}
29+
30+
public Stream<RemoteRepository> asRepositories() {
31+
return repositories.entrySet().stream()
32+
.map(e -> new RemoteRepository.Builder(e.getKey(), "default", e.getValue()).build());
33+
}
34+
}
35+
}

0 commit comments

Comments
 (0)