diff --git a/CHANGELOG.md b/CHANGELOG.md index a8f271e..6b97937 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,47 +7,60 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added + +### Changed +- Separate release tasks for each target + +### Deprecated + +### Removed + +### Fixed + +### Security + +## [0.11.1-kotlin-1.6.10] +### Added +- Default templates for README and ARTIFACT + +### Changed +- Replaced Groovy templates by FreeMarker + +### Deprecated + +### Removed + +### Fixed +- JS publication sources jar + +### Security + +## [0.10.9-kotlin-1.6.10] +### Added - html builders for readme ### Changed - Kotlin 1.6.0 - Use indy lambdas by default #32 - -### Deprecated - -### Removed +- Change version scheme to `-kotlin-` ### Fixed - remove `nativeMain` dependency from `nativeTest` -### Security - ## [0.10.4] -### Added - ### Changed - Kotlin 1.6 - -### Deprecated - -### Removed - ### Fixed - Some issues with opt-ins - -### Security - ## [0.10.2] ### Added - Experimental automatic JS project bundling in MPP - ### Changed - Remove vcs requirement for Space publication - ### Fixed -Release task (#19) @@ -57,12 +70,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - BOM for kotlin-wrappers on JS - Jupyter loader - ### Changed - API validation disabled for dev versions - Kotlin plugins are propagated downstream - ### Removed - bson support @@ -71,18 +82,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Disable API validation for snapshots - `-Xjvm-default=all` on JVM - ### Changed - `publication.platform` changed to `publishing.platform` - Dokka version to `1.4.30` - `useDateTime` in extension - Kotlin 1.5 - ### Removed - Publish plugin. Use MavenPublish instead - ### Fixed - Removed unnecessary `afterEvaluate` for compatibility with gradle 7.0 @@ -90,16 +98,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Skip sonatype publishing for dev versions - ### Changed - Publishing repositories are explicit and defined in the top level project - Paths to publishing properties now use dot notation like `publishing.github.user` - ### Deprecated - Publishing plugin - ### Removed - Bintray publishing @@ -108,12 +113,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Adaptive support for host OS in native - CSS support for JS targets - ### Changed - Kotlin 1.4.31 - Coroutines 1.4.3 - ### Fixed - Plugin loading order for publishing - Release task @@ -125,7 +128,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add sonatype publishing - Per-platform release publishing - ### Changed - Kotlin to 1.4.30 stable. - Added intermediate jsCommon main/test sourcesSet for node plugin. @@ -133,11 +135,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Common plugin id changed to `common` - Plugins group changed to `ru.mipt.npm` with `gradle` prefix - ### Removed - kaml - ### Fixed - Fix publishing load order for sonatype - Fix root project readme @@ -151,7 +151,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Moved all logic to a common plugin, leaving only proxies for platform plugins - Suppress API validation for modules with maturity below DEVELOPMENT - ### Changed - Remove node plugin. Node binaries should be turned on manually. - Use default webpack distribution path. @@ -162,12 +161,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Moved internals to internals - Kotlin 1.4.30-RC - ### Deprecated - Support of `kaml` and `snake-yaml` in favor of `yamlKt` - Publish plugin - ### Removed - `useDokka` method. Documentation jar should be added manually if needed. @@ -178,7 +175,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add `application()` toggle in plugin configuration to produce binaries on JS and applicaion plugin on jvm. - Add `publish` to expose publishing configuration. - ### Changed -Publishing in bintray now is automatic. diff --git a/build.gradle.kts b/build.gradle.kts index 863a158..61dc866 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -33,12 +33,13 @@ dependencies { implementation(libs.dokka.gradle) implementation(libs.kotlin.jupyter.gradle) implementation(libs.kotlin.serialization) - @Suppress("UnstableApiUsage") implementation(libs.kotlinx.html) implementation("org.tomlj:tomlj:1.0.0") // // nexus publishing plugin // implementation("io.github.gradle-nexus:publish-plugin:1.1.0") + implementation("org.freemarker:freemarker:2.3.31") + testImplementation(kotlin("test")) } @@ -58,7 +59,7 @@ gradlePlugin { create("project") { id = "ru.mipt.npm.gradle.project" - description = "The root plugin for multimodule project infrastructure" + description = "The root plugin for multi-module project infrastructure" implementationClass = "ru.mipt.npm.gradle.KScienceProjectPlugin" } @@ -122,7 +123,7 @@ afterEvaluate { publications { create("catalog") { from(components["versionCatalog"]) - this.artifactId = "version-catalog" + artifactId = "version-catalog" pom { name.set("version-catalog") @@ -134,6 +135,7 @@ afterEvaluate { artifact(javadocsJar) pom { + name.set(project.name) description.set(project.description) url.set(vcs) @@ -212,6 +214,10 @@ afterEvaluate { } } +tasks.withType { + kotlinOptions.jvmTarget = "11" +} + tasks.processResources.configure { duplicatesStrategy = DuplicatesStrategy.INCLUDE from("gradle/libs.versions.toml") diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0ff079c..e54f996 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,25 +1,25 @@ [versions] -tools = "0.10.7" -kotlin = "1.6.0" -atomicfu = "0.16.3" +tools = "0.11.2-kotlin-1.6.10" +kotlin = "1.6.10" +atomicfu = "0.17.1" binary-compatibility-validator = "0.8.0" changelog = "1.3.1" -dokka = "1.5.30" -kotlin-jupyter = "0.10.3-36" -kotlinx-benchmark = "0.3.1" -kotlinx-cli = "0.3.3" -kotlinx-collections-immutable = "0.3.4" -kotlinx-coroutines = "1.5.2" -kotlinx-datetime = "0.3.1" +dokka = "1.6.10" +kotlin-jupyter = "0.11.0-62" +kotlinx-benchmark = "0.4.2" +kotlinx-cli = "0.3.4" +kotlinx-collections-immutable = "0.3.5" +kotlinx-coroutines = "1.6.0" +kotlinx-datetime = "0.3.2" kotlinx-html = "0.7.3" -kotlinx-knit = "0.2.3" +kotlinx-knit = "0.3.0" kotlinx-nodejs = "0.0.7" -kotlinx-serialization = "1.3.1" -ktor = "1.6.3" -xmlutil = "0.83.0" +kotlinx-serialization = "1.3.2" +ktor = "1.6.7" +xmlutil = "0.84.0" yamlkt = "0.10.2" -jsBom = "0.0.1-pre.265-kotlin-1.5.31" -junit = "5.8.1" +jsBom = "0.0.1-pre.313-kotlin-1.6.10" +junit = "5.8.2" [libraries] atomicfu-gradle = { module = "org.jetbrains.kotlinx:atomicfu-gradle-plugin", version.ref = "atomicfu" } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7454180..41d9927 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 ffed3a2..41dfb87 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/settings.gradle.kts b/settings.gradle.kts index 4de58c3..c4a5192 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,3 +1 @@ rootProject.name = "gradle-tools" - -enableFeaturePreview("VERSION_CATALOGS") diff --git a/src/main/kotlin/ru/mipt/npm/gradle/KScienceCommonPlugin.kt b/src/main/kotlin/ru/mipt/npm/gradle/KScienceCommonPlugin.kt index ee8db3a..51b22fc 100644 --- a/src/main/kotlin/ru/mipt/npm/gradle/KScienceCommonPlugin.kt +++ b/src/main/kotlin/ru/mipt/npm/gradle/KScienceCommonPlugin.kt @@ -2,169 +2,10 @@ package ru.mipt.npm.gradle import org.gradle.api.Plugin import org.gradle.api.Project -import org.gradle.api.plugins.JavaPluginExtension -import org.gradle.api.tasks.Copy -import org.gradle.api.tasks.testing.Test -import org.gradle.kotlin.dsl.* -import org.jetbrains.dokka.gradle.DokkaPlugin -import org.jetbrains.kotlin.gradle.dsl.KotlinJsProjectExtension -import org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompile -import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension -import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension -import ru.mipt.npm.gradle.internal.applyRepos -import ru.mipt.npm.gradle.internal.applySettings -import ru.mipt.npm.gradle.internal.fromJsDependencies @Suppress("UNUSED_VARIABLE") public open class KScienceCommonPlugin : Plugin { - public companion object{ - public val defaultJvmArgs: List = listOf("-Xjvm-default=all","-Xlambdas=indy") - } - - - override fun apply(project: Project): Unit = project.run { - //Common configuration - registerKScienceExtension() - repositories.applyRepos() - - //Configuration for K-JVM plugin - pluginManager.withPlugin("org.jetbrains.kotlin.jvm") { - //logger.info("Applying KScience configuration for JVM project") - configure { - explicitApiWarning() - - sourceSets.all { - languageSettings.applySettings() - } - - sourceSets["test"].apply { - dependencies { - implementation(kotlin("test-junit5")) - implementation("org.junit.jupiter:junit-jupiter:${KScienceVersions.junit}") - } - } - } - tasks.withType { - kotlinOptions { - jvmTarget = KScienceVersions.JVM_TARGET.toString() - freeCompilerArgs = freeCompilerArgs + defaultJvmArgs - } - } - - extensions.findByType()?.apply { - targetCompatibility = KScienceVersions.JVM_TARGET - } - - tasks.withType { - useJUnitPlatform() - } - } - - pluginManager.withPlugin("org.jetbrains.kotlin.js") { - //logger.info("Applying KScience configuration for JS project") - configure { - explicitApiWarning() - - js(IR) { - browser { - commonWebpackConfig { - cssSupport.enabled = true - } - } - } - - sourceSets.all { - languageSettings.applySettings() - } - - sourceSets["main"].apply { - dependencies { - api(project.dependencies.platform("org.jetbrains.kotlin-wrappers:kotlin-wrappers-bom:${KScienceVersions.jsBom}")) - } - } - - sourceSets["test"].apply { - dependencies { - implementation(kotlin("test-js")) - } - } - } - - (tasks.findByName("processResources") as? Copy)?.apply { - fromJsDependencies("runtimeClasspath") - } - - } - - pluginManager.withPlugin("org.jetbrains.kotlin.multiplatform") { - configure { - explicitApiWarning() - - jvm { - compilations.all { - kotlinOptions { - jvmTarget = KScienceVersions.JVM_TARGET.toString() - freeCompilerArgs = freeCompilerArgs + defaultJvmArgs - } - } - } - - js(IR) { - browser { - commonWebpackConfig { - cssSupport.enabled = true - } - } - } - - sourceSets { - val commonMain by getting { - dependencies { - api(project.dependencies.platform("org.jetbrains.kotlin-wrappers:kotlin-wrappers-bom:${KScienceVersions.jsBom}")) - } - } - val commonTest by getting { - dependencies { - implementation(kotlin("test-common")) - implementation(kotlin("test-annotations-common")) - } - } - val jvmMain by getting - val jvmTest by getting { - dependencies { - implementation(kotlin("test-junit5")) - implementation("org.junit.jupiter:junit-jupiter:${KScienceVersions.junit}") - } - } - val jsMain by getting - val jsTest by getting { - dependencies { - implementation(kotlin("test-js")) - } - } - } - - sourceSets.all { - languageSettings.applySettings() - } - - (tasks.findByName("jsProcessResources") as? Copy)?.apply { - fromJsDependencies("jsRuntimeClasspath") - } - - extensions.findByType()?.apply { - targetCompatibility = KScienceVersions.JVM_TARGET - } - - tasks.withType { - useJUnitPlatform() - } - } - } - - // apply dokka for all projects - if (!plugins.hasPlugin("org.jetbrains.dokka")) { - apply() - } - } + override fun apply(project: Project): Unit = project.configureKScience( + KotlinVersion(1, 6, 10) + ) } diff --git a/src/main/kotlin/ru/mipt/npm/gradle/KScienceExtension.kt b/src/main/kotlin/ru/mipt/npm/gradle/KScienceExtension.kt index 747a768..5a3740f 100644 --- a/src/main/kotlin/ru/mipt/npm/gradle/KScienceExtension.kt +++ b/src/main/kotlin/ru/mipt/npm/gradle/KScienceExtension.kt @@ -141,12 +141,13 @@ public class KScienceExtension(public val project: Project) { } /** - * Apply jupyter plugin and add entry point for the jupyter library + * Apply jupyter plugin and add entry point for the jupyter library. + * If left empty applies a plugin without declaring library producers */ - public fun jupyterLibrary(pluginClass: String, vararg additionalPluginClasses: String) { + public fun jupyterLibrary(vararg pluginClasses: String) { project.plugins.apply("org.jetbrains.kotlin.jupyter.api") project.tasks.named("processJupyterApiResources", JupyterApiResourcesTask::class.java) { - libraryProducers = listOf(pluginClass, *additionalPluginClasses) + libraryProducers = pluginClasses.toList() } } diff --git a/src/main/kotlin/ru/mipt/npm/gradle/KScienceMPPlugin.kt b/src/main/kotlin/ru/mipt/npm/gradle/KScienceMPPlugin.kt index 0b0fa43..6ee5518 100644 --- a/src/main/kotlin/ru/mipt/npm/gradle/KScienceMPPlugin.kt +++ b/src/main/kotlin/ru/mipt/npm/gradle/KScienceMPPlugin.kt @@ -16,5 +16,5 @@ public open class KScienceMPPlugin : Plugin { } apply() - } + } } diff --git a/src/main/kotlin/ru/mipt/npm/gradle/KScienceNativePlugin.kt b/src/main/kotlin/ru/mipt/npm/gradle/KScienceNativePlugin.kt index d79f27b..45b1865 100644 --- a/src/main/kotlin/ru/mipt/npm/gradle/KScienceNativePlugin.kt +++ b/src/main/kotlin/ru/mipt/npm/gradle/KScienceNativePlugin.kt @@ -23,6 +23,8 @@ public class KScienceNativePlugin : Plugin { linuxX64(), mingwX64(), macosX64(), + iosX64(), + iosArm64() ) sourceSets { diff --git a/src/main/kotlin/ru/mipt/npm/gradle/KScienceNodePlugin.kt b/src/main/kotlin/ru/mipt/npm/gradle/KScienceNodePlugin.kt index 261a337..b3ebb78 100644 --- a/src/main/kotlin/ru/mipt/npm/gradle/KScienceNodePlugin.kt +++ b/src/main/kotlin/ru/mipt/npm/gradle/KScienceNodePlugin.kt @@ -2,7 +2,10 @@ package ru.mipt.npm.gradle import org.gradle.api.Plugin import org.gradle.api.Project -import org.gradle.kotlin.dsl.* +import org.gradle.kotlin.dsl.apply +import org.gradle.kotlin.dsl.configure +import org.gradle.kotlin.dsl.findPlugin +import org.gradle.kotlin.dsl.invoke import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension /** diff --git a/src/main/kotlin/ru/mipt/npm/gradle/KScienceProjectPlugin.kt b/src/main/kotlin/ru/mipt/npm/gradle/KScienceProjectPlugin.kt index 5eae110..f48f6d0 100644 --- a/src/main/kotlin/ru/mipt/npm/gradle/KScienceProjectPlugin.kt +++ b/src/main/kotlin/ru/mipt/npm/gradle/KScienceProjectPlugin.kt @@ -1,11 +1,10 @@ package ru.mipt.npm.gradle -import groovy.text.SimpleTemplateEngine import kotlinx.validation.ApiValidationExtension import kotlinx.validation.BinaryCompatibilityValidatorPlugin import org.gradle.api.Plugin import org.gradle.api.Project -import org.gradle.api.Task +import org.gradle.api.publish.maven.tasks.PublishToMavenRepository import org.gradle.kotlin.dsl.* import org.jetbrains.changelog.ChangelogPlugin import org.jetbrains.changelog.ChangelogPluginExtension @@ -13,11 +12,12 @@ import org.jetbrains.dokka.gradle.AbstractDokkaTask import org.jetbrains.dokka.gradle.DokkaPlugin import ru.mipt.npm.gradle.internal.* -private fun Project.allTasks(): Set = allprojects.flatMapTo(HashSet()) { it.tasks } - -@Suppress("unused") +/** + * Simplifies adding repositories for Maven publishing, responds for releasing tasks for projects. + */ public class KSciencePublishingExtension(public val project: Project) { private var isVcsInitialized = false + internal val repositoryNames = mutableSetOf() @Deprecated("Use git function and report an issue if other VCS is used.") public fun vcs(vcsUrl: String) { @@ -54,61 +54,60 @@ public class KSciencePublishingExtension(public val project: Project) { } } - private fun linkPublicationsToReleaseTask(name: String) = project.afterEvaluate { - allTasks() - .filter { it.name == "publish${publicationTarget}To${name.capitalize()}Repository" } - .forEach { releaseTask?.dependsOn(it) } - } - /** * Adds GitHub as VCS and adds GitHub Packages Maven repository to publishing. * * @param githubProject the GitHub project. * @param githubOrg the GitHub user or organization. - * @param release whether publish packages in the `release` task to the GitHub repository. + * @param addToRelease publish packages in the `release` task to the GitHub repository. */ - public fun github(githubProject: String, githubOrg: String = "mipt-npm", release: Boolean = false, publish: Boolean = true) { + public fun github( + githubProject: String, + githubOrg: String = "mipt-npm", + addToRelease: Boolean = project.requestPropertyOrNull("publishing.github") == "true", + ) { // Automatically initialize VCS using GitHub if (!isVcsInitialized) { git("https://github.com/$githubOrg/${githubProject}", "https://github.com/$githubOrg/${githubProject}.git") } - if (publish) project.addGithubPublishing(githubOrg, githubProject) - if (release) linkPublicationsToReleaseTask("github") - } - - private val releaseTask by lazy { - project.tasks.findByName("release") + if (addToRelease) { + try { + project.addGithubPublishing(githubOrg, githubProject) + repositoryNames += "github" + } catch (t: Throwable) { + project.logger.error("Failed to set up github publication", t) + } + } } /** * Adds Space Packages Maven repository to publishing. * * @param spaceRepo the repository URL. - * @param release whether publish packages in the `release` task to the Space repository. + * @param addToRelease publish packages in the `release` task to the Space repository. */ - public fun space(spaceRepo: String = "https://maven.pkg.jetbrains.space/mipt-npm/p/sci/maven", release: Boolean = true) { + public fun space( + spaceRepo: String = "https://maven.pkg.jetbrains.space/mipt-npm/p/sci/maven", + addToRelease: Boolean = project.requestPropertyOrNull("publishing.space") != "false", + ) { project.addSpacePublishing(spaceRepo) - if (release) linkPublicationsToReleaseTask("space") + if (addToRelease) repositoryNames += "space" } -// // Bintray publishing -// var bintrayOrg: String? by project.extra -// var bintrayUser: String? by project.extra -// var bintrayApiKey: String? by project.extra -// var bintrayRepo: String? by project.extra - /** * Adds Sonatype Maven repository to publishing. * - * @param release whether publish packages in the `release` task to the Sonatype repository. + * @param addToRelease publish packages in the `release` task to the Sonatype repository. */ - public fun sonatype(release: Boolean = true) { + public fun sonatype( + addToRelease: Boolean = (project.requestPropertyOrNull("publishing.sonatype") != "false"), + ) { require(isVcsInitialized) { "The project vcs is not set up use 'git' method to do so" } project.addSonatypePublishing() - if (release) linkPublicationsToReleaseTask("sonatype") + if (addToRelease) repositoryNames += "sonatype" } } @@ -119,7 +118,6 @@ public class KSciencePublishingExtension(public val project: Project) { public open class KScienceProjectPlugin : Plugin { override fun apply(target: Project): Unit = target.run { apply() - apply() apply() @@ -136,7 +134,8 @@ public open class KScienceProjectPlugin : Plugin { } val rootReadmeExtension = KScienceReadmeExtension(this) - extensions.add("ksciencePublish", KSciencePublishingExtension(this)) + val ksciencePublish = KSciencePublishingExtension(this) + extensions.add("ksciencePublish", ksciencePublish) extensions.add("readme", rootReadmeExtension) //Add readme generators to individual subprojects @@ -150,7 +149,7 @@ public open class KScienceProjectPlugin : Plugin { if (readmeExtension.readmeTemplate.exists()) { inputs.file(readmeExtension.readmeTemplate) } - readmeExtension.additionalFiles.forEach { + readmeExtension.inputFiles.forEach { if (it.exists()) { inputs.file(it) } @@ -172,6 +171,36 @@ public open class KScienceProjectPlugin : Plugin { } } + val releaseAll by tasks.creating { + group = RELEASE_GROUP + description = "Publish development or production release based on version suffix" + } + + allprojects { + afterEvaluate { + ksciencePublish.repositoryNames.forEach { repositoryName -> + val repositoryNameCapitalized = repositoryName.capitalize() + + tasks.withType() + .filter { it.name.startsWith("publish") && it.name.endsWith("To${repositoryNameCapitalized}Repository") } + .forEach { + val theName = "release${ + it.name.removePrefix("publish").removeSuffix("To${repositoryNameCapitalized}Repository") + }" + + val targetReleaseTask = tasks.findByName(theName) ?: tasks.create(theName) { + group = RELEASE_GROUP + description = "Publish platform release artifact" + } + + releaseAll.dependsOn(targetReleaseTask) + + targetReleaseTask.dependsOn(it) + } + } + } + } + val generateReadme by tasks.creating { group = "documentation" description = "Generate a README file and a feature matrix if stub is present" @@ -185,7 +214,8 @@ public open class KScienceProjectPlugin : Plugin { if (rootReadmeExtension.readmeTemplate.exists()) { inputs.file(rootReadmeExtension.readmeTemplate) } - rootReadmeExtension.additionalFiles.forEach { + + rootReadmeExtension.inputFiles.forEach { if (it.exists()) { inputs.file(it) } @@ -207,8 +237,7 @@ public open class KScienceProjectPlugin : Plugin { val name = subproject.name val path = subproject.path.replaceFirst(":", "").replace(":", "/") val ext = subproject.extensions.findByType() - appendLine("
") - appendLine("\n* ### [$name]($path)") + appendLine("\n### [$name]($path)") if (ext != null) { appendLine("> ${ext.description}") appendLine(">\n> **Maturity**: ${ext.maturity}") @@ -219,16 +248,13 @@ public open class KScienceProjectPlugin : Plugin { } } } - appendLine("
") } - val rootReadmeProperties: Map = - rootReadmeExtension.actualizedProperties + ("modules" to modulesString) + rootReadmeExtension.property("modules", modulesString) - readmeFile.writeText( - SimpleTemplateEngine().createTemplate(rootReadmeExtension.readmeTemplate) - .make(rootReadmeProperties).toString() - ) + rootReadmeExtension.readmeString()?.let { + readmeFile.writeText(it) + } } } @@ -238,14 +264,6 @@ public open class KScienceProjectPlugin : Plugin { dependsOn(generateReadme) } - //val patchChangelog by tasks.getting - - @Suppress("UNUSED_VARIABLE") val release by tasks.creating { - group = RELEASE_GROUP - description = "Publish development or production release based on version suffix" - dependsOn(generateReadme) - } - // Disable API validation for snapshots if (isSnapshot()) { extensions.findByType()?.apply { diff --git a/src/main/kotlin/ru/mipt/npm/gradle/KScienceReadmeExtension.kt b/src/main/kotlin/ru/mipt/npm/gradle/KScienceReadmeExtension.kt index 9d2b283..1ba3785 100644 --- a/src/main/kotlin/ru/mipt/npm/gradle/KScienceReadmeExtension.kt +++ b/src/main/kotlin/ru/mipt/npm/gradle/KScienceReadmeExtension.kt @@ -1,6 +1,9 @@ package ru.mipt.npm.gradle -import groovy.text.SimpleTemplateEngine +import freemarker.cache.StringTemplateLoader +import freemarker.template.Configuration +import freemarker.template.Template +import freemarker.template.TemplateNotFoundException import kotlinx.html.TagConsumer import kotlinx.html.div import kotlinx.html.stream.createHTML @@ -8,6 +11,7 @@ import kotlinx.validation.ApiValidationExtension import org.gradle.api.Project import org.gradle.kotlin.dsl.findByType import java.io.File +import java.io.StringWriter import kotlin.collections.component1 import kotlin.collections.component2 import kotlin.collections.set @@ -20,6 +24,12 @@ public enum class Maturity { DEPRECATED } +private fun Template.processToString(args: Map): String { + val writer = StringWriter() + process(args, writer) + return writer.toString() +} + public class KScienceReadmeExtension(public val project: Project) { public var description: String = project.description ?: "" @@ -40,7 +50,41 @@ public class KScienceReadmeExtension(public val project: Project) { } } + /** + * If true, use default templates provided by plugin if override is not defined + */ + public var useDefaultReadmeTemplate: Boolean = true + + /** + * Use this template file if it is provided, otherwise use default template + */ public var readmeTemplate: File = project.file("docs/README-TEMPLATE.md") + set(value) { + field = value + if (value.exists()) { + fmLoader.putTemplate("readme", value.readText()) + } + } + + private val fmLoader = StringTemplateLoader().apply { + putTemplate( + "artifact", + this@KScienceReadmeExtension.javaClass.getResource("/templates/ARTIFACT-TEMPLATE.md")!!.readText() + ) + if (readmeTemplate.exists()) { + putTemplate("readme", readmeTemplate.readText()) + } else if (useDefaultReadmeTemplate) { + putTemplate( + "readme", + this@KScienceReadmeExtension.javaClass.getResource("/templates/README-TEMPLATE.md")!!.readText() + ) + } + } + + private val fmCfg = Configuration(Configuration.VERSION_2_3_31).apply { + defaultEncoding = "UTF-8" + templateLoader = fmLoader + } public data class Feature(val id: String, val description: String, val ref: String?, val name: String = id) @@ -73,11 +117,20 @@ public class KScienceReadmeExtension(public val project: Project) { "name" to { project.name }, "group" to { project.group }, "version" to { project.version }, - "features" to { featuresString() } + "description" to { project.description ?: "" }, + "features" to { featuresString() }, + "published" to { project.plugins.findPlugin("maven-publish") != null }, + "artifact" to { + val projectProperties = mapOf( + "name" to project.name, + "group" to project.group, + "version" to project.version + ) + fmCfg.getTemplate("artifact").processToString(projectProperties) + } ) - public val actualizedProperties: Map - get() = properties.mapValues { (_, value) -> value() } + public fun getPropertyValues(): Map = properties.mapValues { (_, value) -> value() } public fun property(key: String, value: Any?) { properties[key] = { value } @@ -87,17 +140,28 @@ public class KScienceReadmeExtension(public val project: Project) { properties[key] = value } - public fun propertyByTemplate(key: String, template: String) { - val actual = actualizedProperties - properties[key] = { SimpleTemplateEngine().createTemplate(template).make(actual).toString() } + public fun propertyByTemplate(key: String, templateString: String) { + //need to freeze it, otherwise values could change + val actual = getPropertyValues() + fmLoader.putTemplate(key, templateString) + val template = fmCfg.getTemplate(key) + + properties[key] = { template.processToString(actual) } } - internal val additionalFiles = ArrayList() + /** + * Files that are use in readme generation + */ + internal val inputFiles = ArrayList() - public fun propertyByTemplate(key: String, template: File) { - val actual = actualizedProperties - properties[key] = { SimpleTemplateEngine().createTemplate(template).make(actual).toString() } - additionalFiles += template + public fun propertyByTemplate(key: String, templateFile: File) { + //need to freeze it, otherwise values could change + val actual = getPropertyValues() + fmLoader.putTemplate(key, templateFile.readText()) + val template: Template = fmCfg.getTemplate(key) + + properties[key] = { template.processToString(actual) } + inputFiles += templateFile } /** @@ -110,12 +174,12 @@ public class KScienceReadmeExtension(public val project: Project) { } /** - * Generate a readme string from the stub + * Generate a readme string from the template */ - public fun readmeString(): String? = if (readmeTemplate.exists()) { - val actual = actualizedProperties - SimpleTemplateEngine().createTemplate(readmeTemplate).make(actual).toString() - } else { + public fun readmeString(): String? = try { + fmCfg.getTemplate("readme").processToString(getPropertyValues()) + } catch (ex: TemplateNotFoundException) { + project.logger.warn("Template with name ${ex.templateName} not found in ${project.name}") null } } diff --git a/src/main/kotlin/ru/mipt/npm/gradle/commonConfigurations.kt b/src/main/kotlin/ru/mipt/npm/gradle/commonConfigurations.kt new file mode 100644 index 0000000..de1b304 --- /dev/null +++ b/src/main/kotlin/ru/mipt/npm/gradle/commonConfigurations.kt @@ -0,0 +1,166 @@ +package ru.mipt.npm.gradle + +import org.gradle.api.Project +import org.gradle.api.plugins.JavaPluginExtension +import org.gradle.api.tasks.Copy +import org.gradle.api.tasks.testing.Test +import org.gradle.kotlin.dsl.* +import org.jetbrains.dokka.gradle.DokkaPlugin +import org.jetbrains.kotlin.gradle.dsl.KotlinJsProjectExtension +import org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompile +import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension +import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension +import ru.mipt.npm.gradle.internal.applyRepos +import ru.mipt.npm.gradle.internal.applySettings +import ru.mipt.npm.gradle.internal.fromJsDependencies + + +private val defaultJvmArgs: List = listOf("-Xjvm-default=all", "-Xlambdas=indy") + +public fun Project.configureKScience( + kotlinVersion: KotlinVersion +){ + //Common configuration + registerKScienceExtension() + repositories.applyRepos() + + //Configuration for K-JVM plugin + pluginManager.withPlugin("org.jetbrains.kotlin.jvm") { + //logger.info("Applying KScience configuration for JVM project") + configure { + explicitApiWarning() + + sourceSets.all { + languageSettings.applySettings(kotlinVersion) + } + + sourceSets["test"].apply { + dependencies { + implementation(kotlin("test-junit5")) + implementation("org.junit.jupiter:junit-jupiter:${KScienceVersions.junit}") + } + } + } + tasks.withType { + kotlinOptions { + jvmTarget = KScienceVersions.JVM_TARGET.toString() + freeCompilerArgs = freeCompilerArgs + defaultJvmArgs + } + } + + extensions.findByType()?.apply { + targetCompatibility = KScienceVersions.JVM_TARGET + } + + tasks.withType { + useJUnitPlatform() + } + } + + pluginManager.withPlugin("org.jetbrains.kotlin.js") { + //logger.info("Applying KScience configuration for JS project") + configure { + explicitApiWarning() + + js(IR) { + browser { + commonWebpackConfig { + cssSupport.enabled = true + } + } + } + + sourceSets.all { + languageSettings.applySettings(kotlinVersion) + } + + sourceSets["main"].apply { + dependencies { + api(project.dependencies.platform("org.jetbrains.kotlin-wrappers:kotlin-wrappers-bom:${KScienceVersions.jsBom}")) + } + } + + sourceSets["test"].apply { + dependencies { + implementation(kotlin("test-js")) + } + } + } + + (tasks.findByName("processResources") as? Copy)?.apply { + fromJsDependencies("runtimeClasspath") + } + + } + + pluginManager.withPlugin("org.jetbrains.kotlin.multiplatform") { + configure { + explicitApiWarning() + + jvm { + compilations.all { + kotlinOptions { + jvmTarget = KScienceVersions.JVM_TARGET.toString() + freeCompilerArgs = freeCompilerArgs + defaultJvmArgs + } + } + } + + js(IR) { + browser { + commonWebpackConfig { + cssSupport.enabled = true + } + } + } + + sourceSets { + val commonMain by getting { + dependencies { + api(project.dependencies.platform("org.jetbrains.kotlin-wrappers:kotlin-wrappers-bom:${KScienceVersions.jsBom}")) + } + } + val commonTest by getting { + dependencies { + implementation(kotlin("test-common")) + implementation(kotlin("test-annotations-common")) + } + } + val jvmMain by getting + val jvmTest by getting { + dependencies { + implementation(kotlin("test-junit5")) + implementation("org.junit.jupiter:junit-jupiter:${KScienceVersions.junit}") + } + } + val jsMain by getting + val jsTest by getting { + dependencies { + implementation(kotlin("test-js")) + } + } + } + + sourceSets.all { + languageSettings.applySettings(kotlinVersion) + } + + (tasks.findByName("jsProcessResources") as? Copy)?.apply { + fromJsDependencies("jsRuntimeClasspath") + } + + extensions.findByType()?.apply { + targetCompatibility = KScienceVersions.JVM_TARGET + } + + tasks.withType { + useJUnitPlatform() + } + } + } + + // apply dokka for all projects + if (!plugins.hasPlugin("org.jetbrains.dokka")) { + apply() + } +} \ No newline at end of file diff --git a/src/main/kotlin/ru/mipt/npm/gradle/internal/common.kt b/src/main/kotlin/ru/mipt/npm/gradle/internal/common.kt index b8c8f13..673d2a1 100644 --- a/src/main/kotlin/ru/mipt/npm/gradle/internal/common.kt +++ b/src/main/kotlin/ru/mipt/npm/gradle/internal/common.kt @@ -11,9 +11,12 @@ import org.gradle.language.jvm.tasks.ProcessResources import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension import org.jetbrains.kotlin.gradle.plugin.LanguageSettingsBuilder -internal fun LanguageSettingsBuilder.applySettings() { - languageVersion = "1.6" - apiVersion = "1.6" +internal fun LanguageSettingsBuilder.applySettings( + kotlinVersion: KotlinVersion +) { + val versionString = "${kotlinVersion.major}.${kotlinVersion.minor}" + languageVersion = versionString + apiVersion = versionString progressiveMode = true optIn("kotlin.RequiresOptIn") @@ -48,10 +51,10 @@ internal fun Copy.fromJsDependencies(configurationName: String) = project.run { from(task) } } - } + } } -internal fun KotlinMultiplatformExtension.bundleJsBinaryAsResource(bundleName: String = "js/bundle.js"){ +internal fun KotlinMultiplatformExtension.bundleJsBinaryAsResource(bundleName: String = "js/bundle.js") { js { binaries.executable() browser { diff --git a/src/main/kotlin/ru/mipt/npm/gradle/internal/fx.kt b/src/main/kotlin/ru/mipt/npm/gradle/internal/fx.kt index 3420cb3..c64c81b 100644 --- a/src/main/kotlin/ru/mipt/npm/gradle/internal/fx.kt +++ b/src/main/kotlin/ru/mipt/npm/gradle/internal/fx.kt @@ -2,7 +2,6 @@ package ru.mipt.npm.gradle.internal import org.apache.tools.ant.taskdefs.condition.Os import org.gradle.api.Project -import org.gradle.kotlin.dsl.configure import org.gradle.kotlin.dsl.findByType import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension diff --git a/src/main/kotlin/ru/mipt/npm/gradle/internal/publishing.kt b/src/main/kotlin/ru/mipt/npm/gradle/internal/publishing.kt index 43c7d67..921631d 100644 --- a/src/main/kotlin/ru/mipt/npm/gradle/internal/publishing.kt +++ b/src/main/kotlin/ru/mipt/npm/gradle/internal/publishing.kt @@ -11,10 +11,10 @@ import org.gradle.plugins.signing.SigningPlugin import org.jetbrains.kotlin.gradle.dsl.KotlinJsProjectExtension import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension -private fun Project.requestPropertyOrNull(propertyName: String): String? = findProperty(propertyName) as? String +internal fun Project.requestPropertyOrNull(propertyName: String): String? = findProperty(propertyName) as? String ?: System.getenv(propertyName) -private fun Project.requestProperty(propertyName: String): String = requestPropertyOrNull(propertyName) +internal fun Project.requestProperty(propertyName: String): String = requestPropertyOrNull(propertyName) ?: error("Property $propertyName not defined") @@ -27,20 +27,18 @@ internal fun Project.setupPublication(mavenPomConfiguration: MavenPom.() -> Unit val sourcesJar by tasks.creating(Jar::class) { archiveClassifier.set("sources") - - kotlin.sourceSets.all { - from(kotlin) + kotlin.sourceSets.forEach { + from(it.kotlin) } } - afterEvaluate { - publications.create("js") { - kotlin.js().components.forEach { - from(it) - } - - artifact(sourcesJar) + publications.create("js") { + kotlin.js().components.forEach { + from(it) } + + artifact(sourcesJar) } + } plugins.withId("org.jetbrains.kotlin.jvm") { @@ -69,37 +67,35 @@ internal fun Project.setupPublication(mavenPomConfiguration: MavenPom.() -> Unit } // Process each publication we have in this project - afterEvaluate { - publications.withType { - artifact(dokkaJar) + publications.withType { + artifact(dokkaJar) - pom { - name.set(project.name) - description.set(project.description ?: project.name) + pom { + name.set(project.name) + description.set(project.description ?: project.name) - licenses { - license { - name.set("The Apache Software License, Version 2.0") - url.set("https://www.apache.org/licenses/LICENSE-2.0.txt") - distribution.set("repo") - } + licenses { + license { + name.set("The Apache Software License, Version 2.0") + url.set("https://www.apache.org/licenses/LICENSE-2.0.txt") + distribution.set("repo") } - - developers { - developer { - id.set("MIPT-NPM") - name.set("MIPT nuclear physics methods laboratory") - organization.set("MIPT") - organizationUrl.set("https://npm.mipt.ru") - } - } - - scm { - tag.set(project.version.toString()) - } - - mavenPomConfiguration() } + + developers { + developer { + id.set("MIPT-NPM") + name.set("MIPT nuclear physics methods laboratory") + organization.set("MIPT") + organizationUrl.set("https://npm.mipt.ru") + } + } + + scm { + tag.set(project.version.toString()) + } + + mavenPomConfiguration() } } } @@ -108,16 +104,6 @@ internal fun Project.setupPublication(mavenPomConfiguration: MavenPom.() -> Unit internal fun Project.isSnapshot() = "dev" in version.toString() || version.toString().endsWith("SNAPSHOT") -internal val Project.publicationTarget: String - get() { - val publicationPlatform = project.findProperty("publishing.platform") as? String - return if (publicationPlatform == null) { - "AllPublications" - } else { - publicationPlatform.capitalize() + "Publication" - } - } - internal fun Project.addGithubPublishing( githubOrg: String, githubProject: String, @@ -126,8 +112,8 @@ internal fun Project.addGithubPublishing( logger.info("Skipping github publishing because publishing is disabled") return } - if (requestPropertyOrNull("publishing.github") == "false") { - logger.info("Skipping github publishing because `publishing.github == false`") + if (requestPropertyOrNull("publishing.github") != "false") { + logger.info("Skipping github publishing because `publishing.github != true`") return } diff --git a/src/main/resources/templates/ARTIFACT-TEMPLATE.md b/src/main/resources/templates/ARTIFACT-TEMPLATE.md new file mode 100644 index 0000000..4f04b4d --- /dev/null +++ b/src/main/resources/templates/ARTIFACT-TEMPLATE.md @@ -0,0 +1,26 @@ +## Artifact: + +The Maven coordinates of this project are `${group}:${name}:${version}`. + +**Gradle Groovy:** +```groovy +repositories { + maven { url 'https://repo.kotlin.link' } + mavenCentral() +} + +dependencies { + implementation '${group}:${name}:${version}' +} +``` +**Gradle Kotlin DSL:** +```kotlin +repositories { + maven("https://repo.kotlin.link") + mavenCentral() +} + +dependencies { + implementation("${group}:${name}:${version}") +} +``` \ No newline at end of file diff --git a/src/main/resources/templates/README-TEMPLATE.md b/src/main/resources/templates/README-TEMPLATE.md new file mode 100644 index 0000000..63e6d31 --- /dev/null +++ b/src/main/resources/templates/README-TEMPLATE.md @@ -0,0 +1,15 @@ +# Module ${name} + +${description} + +<#if features?has_content> +## Features + +${features} + + +<#if published> +## Usage + +${artifact} +