Skip to content

Commit d238909

Browse files
authored
Merge pull request #315 from Kotlin/refactor-build
Refactor build, fix #306 - Use version catalogs - Use typesafe project accessors - Move common logic to the build plugin - Expose configurable options as extensions - Reduce the number of project options - Use only lazy task configuration (register instead of create) - Get rid of libraries submodule, load libraries on demand
2 parents 9dd9c11 + c750442 commit d238909

File tree

81 files changed

+2466
-1968
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

81 files changed

+2466
-1968
lines changed

.gitignore

+6-1
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,16 @@
1414
# Folders with build files
1515
out/
1616
/build/
17-
/*jupyter*/build/
17+
/build-plugin/**/.idea/
18+
/build-plugin/**/build/
19+
!/build-plugin/**/src/build/
1820
/*jupyter*/*/build/
1921
/api-examples/*/build/
2022
/teamcity-artifacts/
2123

24+
# Folder with library descriptors
25+
libraries/
26+
2227
# Gradle caches and internal files
2328
.gradle/
2429

.gitmodules

-3
This file was deleted.

api-examples/getting-started/build.gradle.kts

+5-9
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,15 @@ kotlinJupyter {
99
addScannerDependency()
1010
}
1111

12-
project.version = rootProject.version
13-
1412
dependencies {
15-
implementation(kotlin("stdlib"))
16-
implementation(kotlin("reflect"))
13+
implementation(libs.kotlin.stable.stdlib)
14+
implementation(libs.kotlin.stable.reflect)
1715
}
1816

1917
kotlinPublications {
2018
publication {
21-
publicationName = "example-getting-started"
22-
artifactId = "kotlin-jupyter-example-getting-started"
23-
description = "Basic API usage example"
24-
packageName = artifactId
25-
publishToSonatype = false
19+
publicationName.set("example-getting-started")
20+
description.set("Basic API usage example")
21+
publishToSonatype.set(false)
2622
}
2723
}

build-plugin/build.gradle.kts

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
2+
3+
plugins {
4+
id("build.plugins.versions")
5+
`kotlin-dsl`
6+
}
7+
8+
repositories {
9+
mavenCentral()
10+
gradlePluginPortal()
11+
}
12+
13+
dependencies {
14+
implementation(projects.commonDependencies)
15+
api(libs.bundles.allGradlePlugins)
16+
}
17+
18+
sourceSets {
19+
main {
20+
java.setSrcDirs(listOf("src"))
21+
}
22+
test {
23+
allJava.setSrcDirs(emptyList<String>())
24+
resources.setSrcDirs(emptyList<String>())
25+
}
26+
}
27+
28+
tasks.withType<KotlinCompile> {
29+
kotlinOptions {
30+
freeCompilerArgs = freeCompilerArgs + listOf("-Xopt-in=kotlin.RequiresOptIn")
31+
}
32+
}
33+
34+
gradlePlugin {
35+
plugins {
36+
create("dependencies") {
37+
id = "build.plugins.main"
38+
implementationClass = "build.KernelBuildPlugin"
39+
}
40+
}
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
plugins {
2+
id("ru.ileasile.kotlin.publisher")
3+
kotlin("jvm")
4+
}
5+
6+
repositories {
7+
mavenCentral()
8+
}
9+
10+
dependencies {
11+
implementation(libs.kotlin.gradle.stdlib)
12+
13+
// HTTP4K for resolving remote library dependencies
14+
api(libs.bundles.http4k)
15+
16+
// Serialization implementation for kernel code
17+
api(libs.serialization.json)
18+
}
19+
20+
sourceSets {
21+
main {
22+
java.setSrcDirs(listOf("src"))
23+
}
24+
test {
25+
allJava.setSrcDirs(emptyList<String>())
26+
resources.setSrcDirs(emptyList<String>())
27+
}
28+
}
29+
30+
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
31+
kotlinOptions {
32+
apiVersion = "1.4"
33+
languageVersion = "1.4"
34+
35+
@Suppress("SuspiciousCollectionReassignment")
36+
freeCompilerArgs += listOf("-Xopt-in=kotlin.RequiresOptIn")
37+
}
38+
}
39+
40+
kotlinPublications {
41+
publication {
42+
publicationName.set("common-dependencies")
43+
description.set("Notebook API entities used for building kernel documentation")
44+
}
45+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
package org.jetbrains.kotlinx.jupyter.common
2+
3+
import kotlinx.serialization.json.JsonObject
4+
import kotlinx.serialization.json.JsonPrimitive
5+
import kotlinx.serialization.json.jsonPrimitive
6+
import org.slf4j.Logger
7+
import org.slf4j.LoggerFactory
8+
import java.io.File
9+
10+
fun interface ExceptionsHandler {
11+
fun handle(logger: Logger, message: String, exception: Throwable)
12+
13+
object DEFAULT : ExceptionsHandler {
14+
override fun handle(logger: Logger, message: String, exception: Throwable) {
15+
logger.error(message)
16+
throw exception
17+
}
18+
}
19+
}
20+
21+
class LibraryDescriptorsManager private constructor(
22+
user: String,
23+
repo: String,
24+
private val remotePath: String,
25+
localPath: String,
26+
private val homePath: String,
27+
userPath: String,
28+
private val exceptionsHandler: ExceptionsHandler,
29+
userSettingsDir: File,
30+
private val logger: Logger,
31+
) {
32+
private val apiPrefix = "https://$GITHUB_API_HOST/repos/$user/$repo"
33+
val userLibrariesDir = userSettingsDir.resolve(userPath)
34+
val userCacheDir = userSettingsDir.resolve("cache")
35+
val localLibrariesDir = File(localPath)
36+
val defaultBranch = "master"
37+
val latestCommitOnDefaultBranch by lazy {
38+
getLatestCommitToLibraries(defaultBranch)!!.first
39+
}
40+
41+
fun homeLibrariesDir(homeDir: File? = null) = (homeDir ?: File("")).resolve(homePath)
42+
43+
val localPropertiesFile = localLibrariesDir.resolve(PROPERTIES_FILE)
44+
val commitHashFile by lazy {
45+
localLibrariesDir.resolve(COMMIT_HASH_FILE).also { file ->
46+
if (!file.exists()) {
47+
file.createDirsAndWrite()
48+
}
49+
}
50+
}
51+
52+
fun descriptorFileName(name: String) = "$name.$DESCRIPTOR_EXTENSION"
53+
54+
fun isLibraryDescriptor(file: File): Boolean {
55+
return file.isFile && file.name.endsWith(".$DESCRIPTOR_EXTENSION")
56+
}
57+
58+
fun getLatestCommitToLibraries(ref: String, sinceTimestamp: String? = null): Pair<String, String>? {
59+
return catchAll {
60+
var url = "$apiPrefix/commits?path=$remotePath&sha=$ref"
61+
if (sinceTimestamp != null) {
62+
url += "&since=$sinceTimestamp"
63+
}
64+
logger.info("Checking for new commits to library descriptors at $url")
65+
val arr = getHttp(url).jsonArray
66+
if (arr.isEmpty()) {
67+
if (sinceTimestamp != null) {
68+
getLatestCommitToLibraries(ref, null)
69+
} else {
70+
logger.info("Didn't find any commits to libraries at $url")
71+
null
72+
}
73+
} else {
74+
val commit = arr[0] as JsonObject
75+
val sha = (commit["sha"] as JsonPrimitive).content
76+
val timestamp = (((commit["commit"] as JsonObject)["committer"] as JsonObject)["date"] as JsonPrimitive).content
77+
sha to timestamp
78+
}
79+
}
80+
}
81+
82+
fun downloadLibraryDescriptor(ref: String, name: String): String {
83+
val url = "$apiPrefix/contents/$remotePath/$name.$DESCRIPTOR_EXTENSION?ref=$ref"
84+
logger.info("Requesting library descriptor at $url")
85+
return downloadSingleFile(url)
86+
}
87+
88+
fun checkRefExistence(ref: String): Boolean {
89+
val response = getHttp("$apiPrefix/contents/$remotePath?ref=$ref")
90+
return response.status.successful
91+
}
92+
93+
fun checkIfRefUpToDate(remoteRef: String): Boolean {
94+
if (!commitHashFile.exists()) return false
95+
val localRef = commitHashFile.readText()
96+
return localRef == remoteRef
97+
}
98+
99+
fun downloadLibraries(ref: String) {
100+
localLibrariesDir.mkdirs()
101+
102+
val url = "$apiPrefix/contents/$remotePath?ref=$ref"
103+
logger.info("Requesting library descriptors at $url")
104+
val response = getHttp(url).jsonArray
105+
106+
for (item in response) {
107+
item as JsonObject
108+
if (item["type"]?.jsonPrimitive?.content != "file") continue
109+
110+
val fileName = item["name"]!!.jsonPrimitive.content
111+
if (!fileName.endsWith(".$DESCRIPTOR_EXTENSION")) continue
112+
113+
val downloadUrl = item["download_url"]!!.jsonPrimitive.content
114+
val descriptorResponse = getHttp(downloadUrl)
115+
116+
val descriptorText = descriptorResponse.text
117+
val file = localLibrariesDir.resolve(fileName)
118+
file.writeText(descriptorText)
119+
}
120+
121+
saveLocalRef(ref)
122+
}
123+
124+
fun downloadLatestPropertiesFile() {
125+
val ref = latestCommitOnDefaultBranch
126+
val url = "$apiPrefix/contents/$remotePath/$PROPERTIES_FILE?ref=$ref"
127+
logger.info("Requesting $PROPERTIES_FILE file at $url")
128+
val text = downloadSingleFile(url)
129+
localPropertiesFile.createDirsAndWrite(text)
130+
}
131+
132+
private fun downloadSingleFile(contentsApiUrl: String): String {
133+
val response = getHttp(contentsApiUrl).jsonObject
134+
val downloadUrl = response["download_url"]!!.jsonPrimitive.content
135+
val res = getHttp(downloadUrl)
136+
return res.text
137+
}
138+
139+
private fun saveLocalRef(ref: String) {
140+
commitHashFile.createDirsAndWrite(ref)
141+
}
142+
143+
private fun File.createDirsAndWrite(text: String = "") {
144+
parentFile.mkdirs()
145+
writeText(text)
146+
}
147+
148+
private fun <T> catchAll(message: String = "", body: () -> T): T? = try {
149+
body()
150+
} catch (e: Throwable) {
151+
exceptionsHandler.handle(logger, message, e)
152+
null
153+
}
154+
155+
companion object {
156+
private const val GITHUB_API_HOST = "api.github.com"
157+
private const val DESCRIPTOR_EXTENSION = "json"
158+
private const val PROPERTIES_FILE = ".properties"
159+
private const val COMMIT_HASH_FILE = "commit_sha"
160+
161+
fun getInstance(
162+
logger: Logger = LoggerFactory.getLogger(LibraryDescriptorsManager::class.java),
163+
exceptionsHandler: ExceptionsHandler = ExceptionsHandler.DEFAULT,
164+
): LibraryDescriptorsManager {
165+
return LibraryDescriptorsManager(
166+
"Kotlin",
167+
"kotlin-jupyter-libraries",
168+
"",
169+
"libraries",
170+
"libraries",
171+
"libraries",
172+
exceptionsHandler,
173+
File(System.getProperty("user.home")).resolve(".jupyter_kotlin"),
174+
logger
175+
)
176+
}
177+
}
178+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
plugins {
2+
`java-gradle-plugin`
3+
`kotlin-dsl`
4+
}
5+
6+
repositories {
7+
mavenCentral()
8+
gradlePluginPortal()
9+
}
10+
11+
dependencies {
12+
implementation(libs.plugin.ktlint)
13+
implementation(libs.plugin.publisher)
14+
implementation(libs.plugin.serialization)
15+
}
16+
17+
sourceSets {
18+
main {
19+
java.setSrcDirs(listOf("src"))
20+
}
21+
test {
22+
allJava.setSrcDirs(emptyList<String>())
23+
resources.setSrcDirs(emptyList<String>())
24+
}
25+
}
26+
27+
gradlePlugin {
28+
plugins {
29+
create("plugins-versions") {
30+
id = "build.plugins.versions"
31+
implementationClass = "build.PluginVersionsPlugin"
32+
}
33+
}
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
@file:Suppress("UnstableApiUsage")
2+
3+
rootProject.name = "plugin-versions"
4+
5+
dependencyResolutionManagement {
6+
versionCatalogs {
7+
create("libs") {
8+
from(files("../../gradle/libs.versions.toml"))
9+
}
10+
}
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package build
2+
3+
import org.gradle.api.Plugin
4+
import org.gradle.api.Project
5+
6+
class PluginVersionsPlugin: Plugin<Project> {
7+
override fun apply(project: Project) {
8+
with(project.plugins) {
9+
apply("org.jlleitschuh.gradle.ktlint")
10+
apply("org.gradle.java-gradle-plugin")
11+
apply("org.jetbrains.kotlin.plugin.serialization")
12+
}
13+
}
14+
}

build-plugin/settings.gradle.kts

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
@file:Suppress("UnstableApiUsage")
2+
3+
enableFeaturePreview("VERSION_CATALOGS")
4+
enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
5+
6+
rootProject.name = "build"
7+
8+
dependencyResolutionManagement {
9+
versionCatalogs {
10+
create("libs") {
11+
from(files("../gradle/libs.versions.toml"))
12+
}
13+
}
14+
}
15+
16+
includeBuild("plugin-versions-plugin")
17+
18+
subproject("common-dependencies", "")
19+
20+
fun subproject(name: String, parentPath: String) {
21+
include(name)
22+
project(":$name").projectDir = file("$parentPath$name")
23+
}

0 commit comments

Comments
 (0)