diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 48a5c25..44a6ad5 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -5,7 +5,7 @@ plugins { dependencies { implementation("com.github.ben-manes:gradle-versions-plugin:0.51.0") implementation("io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.23.1") - implementation("org.jetbrains.dokka:dokka-gradle-plugin:1.9.20") - implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.23") + implementation("org.jetbrains.dokka:dokka-gradle-plugin:1.9.10") + implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.10") implementation("org.jetbrains.kotlinx:kover-gradle-plugin:0.7.4") -} +} \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/buildsrc/conventions/lang/kotlin-multiplatform-base.gradle.kts b/buildSrc/src/main/kotlin/buildsrc/conventions/lang/kotlin-multiplatform-base.gradle.kts index d41b539..5665e86 100644 --- a/buildSrc/src/main/kotlin/buildsrc/conventions/lang/kotlin-multiplatform-base.gradle.kts +++ b/buildSrc/src/main/kotlin/buildsrc/conventions/lang/kotlin-multiplatform-base.gradle.kts @@ -3,9 +3,7 @@ package buildsrc.conventions.lang import buildsrc.utils.Rife2TestListener import org.gradle.api.tasks.testing.logging.TestExceptionFormat import org.gradle.api.tasks.testing.logging.TestLogEvent -import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi import org.jetbrains.kotlin.gradle.dsl.JvmTarget -import org.jetbrains.kotlin.gradle.dsl.KotlinVersion import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile @@ -26,21 +24,29 @@ plugins { kotlin { //jvmToolchain(11) - applyDefaultHierarchyTemplate() - - @OptIn(ExperimentalKotlinGradlePluginApi::class) - compilerOptions { - languageVersion = KotlinVersion.KOTLIN_1_6 + targets.configureEach { + compilations.configureEach { + kotlinOptions { + languageVersion = "1.6" + } + } } // configure all Kotlin/JVM Tests to use JUnit targets.withType().configureEach { testRuns.configureEach { executionTask.configure { - useJUnitPlatform() + // useJUnitPlatform() } } } + + sourceSets.configureEach { + languageSettings { + // languageVersion = + // apiVersion = + } + } } tasks { diff --git a/buildSrc/src/main/kotlin/buildsrc/conventions/lang/kotlin-multiplatform-js.gradle.kts b/buildSrc/src/main/kotlin/buildsrc/conventions/lang/kotlin-multiplatform-js.gradle.kts index 5474801..2a0034a 100644 --- a/buildSrc/src/main/kotlin/buildsrc/conventions/lang/kotlin-multiplatform-js.gradle.kts +++ b/buildSrc/src/main/kotlin/buildsrc/conventions/lang/kotlin-multiplatform-js.gradle.kts @@ -1,7 +1,5 @@ package buildsrc.conventions.lang -import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl - /** conventions for a Kotlin/JS subproject */ plugins { @@ -9,38 +7,12 @@ plugins { } kotlin { - js(IR) { - browser() - nodejs() - } - - @OptIn(ExperimentalWasmDsl::class) - wasmJs { - browser() - nodejs() - } - - @OptIn(ExperimentalWasmDsl::class) - wasmWasi { - nodejs() + targets { + js(IR) { + browser() + nodejs() + } } } relocateKotlinJsStore() - - -//region FIXME: WORKAROUND https://youtrack.jetbrains.com/issue/KT-65864 -rootProject.plugins.withType { - rootProject.extensions.configure { - // Use a Node.js version current enough to support Kotlin/Wasm - nodeVersion = "22.0.0-nightly2024010568c8472ed9" - logger.lifecycle("Using Node.js $nodeVersion to support Kotlin/Wasm") - nodeDownloadBaseUrl = "https://nodejs.org/download/nightly" - } -} - -rootProject.tasks.withType().configureEach { - // Prevent Yarn from complaining about newer Node.js versions. - args.add("--ignore-engines") -} -//endregion diff --git a/buildSrc/src/main/kotlin/buildsrc/conventions/lang/kotlin-multiplatform-native.gradle.kts b/buildSrc/src/main/kotlin/buildsrc/conventions/lang/kotlin-multiplatform-native.gradle.kts index 70022cc..f8ca72a 100644 --- a/buildSrc/src/main/kotlin/buildsrc/conventions/lang/kotlin-multiplatform-native.gradle.kts +++ b/buildSrc/src/main/kotlin/buildsrc/conventions/lang/kotlin-multiplatform-native.gradle.kts @@ -7,26 +7,94 @@ plugins { } kotlin { + + // Native targets all extend commonMain and commonTest. + // + // Some targets (ios, tvos, watchos) are shortcuts provided by the Kotlin DSL, that + // provide additional targets, except for 'simulators' which must be defined manually. + // https://kotlinlang.org/docs/multiplatform-share-on-platforms.html#use-target-shortcuts + // + // common/ + // └── native/ + // ├── linuxX64 + // ├── mingwX64 + // ├── macosX64 + // ├── macosArm64 + // ├── ios/ (shortcut) + // │ ├── iosArm64 + // │ ├── iosX64 + // │ └── iosSimulatorArm64 + // ├── tvos/ (shortcut) + // │ ├── tvosArm64 + // │ ├── tvosX64 + // │ └── tvosSimulatorArm64Main + // └── watchos/ (shortcut) + // ├── watchosArm32 + // ├── watchosArm64 + // ├── watchosX64 + // └── watchosSimulatorArm64Main + linuxX64() mingwX64() - linuxArm64() - macosX64() macosArm64() - iosArm64() - iosX64() + // https://kotlinlang.org/docs/multiplatform-share-on-platforms.html#use-target-shortcuts + ios() // iosArm64, iosX64 + watchos() // watchosArm32, watchosArm64, watchosX64 + tvos() // tvosArm64, tvosX64 + iosSimulatorArm64() - - watchosArm32() - watchosArm64() - watchosX64() - watchosSimulatorArm64() - watchosDeviceArm64() - - tvosArm64() - tvosX64() tvosSimulatorArm64() + watchosSimulatorArm64() + + @Suppress("UNUSED_VARIABLE") + sourceSets { + val commonMain by getting {} + val commonTest by getting {} + + val nativeMain by creating { dependsOn(commonMain) } + val nativeTest by creating { dependsOn(commonTest) } + + // Linux + val linuxX64Main by getting { dependsOn(nativeMain) } + val linuxX64Test by getting { dependsOn(nativeTest) } + + // Windows - MinGW + val mingwX64Main by getting { dependsOn(nativeMain) } + val mingwX64Test by getting { dependsOn(nativeTest) } + + // Apple - macOS + val macosArm64Main by getting { dependsOn(nativeMain) } + val macosArm64Test by getting { dependsOn(nativeTest) } + + val macosX64Main by getting { dependsOn(nativeMain) } + val macosX64Test by getting { dependsOn(nativeTest) } + + // Apple - iOS + val iosMain by getting { dependsOn(nativeMain) } + val iosTest by getting { dependsOn(nativeTest) } + + val iosSimulatorArm64Main by getting { dependsOn(iosMain) } + // val iosSimulatorArm64Test by getting { dependsOn(iosTest) } + + // // Apple - tvOS + // val tvosMain by getting { dependsOn(nativeMain) } + // val tvosTest by getting { dependsOn(nativeTest) } + + // val tvosSimulatorArm64Main by getting { dependsOn(tvosMain) } + // val tvosSimulatorArm64Test by getting { dependsOn(tvosTest) } + + // // Apple - watchOS + // val watchosMain by getting { dependsOn(nativeMain) } + // val watchosTest by getting { dependsOn(nativeTest) } + + // val watchosSimulatorArm64Main by getting { dependsOn(watchosMain) } + // val watchosSimulatorArm64Test by getting { dependsOn(watchosTest) } + + // val iosArm32Main by getting { dependsOn(desktopMain) } + // val iosArm32Test by getting { dependsOn(nativeTest) } + } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index b82aa23..a80b22c 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-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/settings.gradle.kts b/settings.gradle.kts index 9e70d32..03ddb65 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,90 +1,82 @@ rootProject.name = "urlencoder" pluginManagement { - repositories { - mavenCentral() - gradlePluginPortal() - } + repositories { + mavenCentral() + gradlePluginPortal() + } } @Suppress("UnstableApiUsage") dependencyResolutionManagement { - repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS) + repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS) - repositories { - mavenCentral() - maven("https://oss.sonatype.org/content/repositories/snapshots") { - name = "Sonatype Snapshots" - mavenContent { snapshotsOnly() } - } - - // Declare the Node.js & Yarn download repositories - exclusiveContent { - forRepositories( - ivy("https://nodejs.org/dist/") { - name = "Node Distributions at $url" - patternLayout { artifact("v[revision]/[artifact](-v[revision]-[classifier]).[ext]") } - metadataSources { artifact() } - }, - ivy("https://nodejs.org/download/v8-canary/") { - name = "Node Canary Distributions at $url" - patternLayout { artifact("v[revision]/[artifact](-v[revision]-[classifier]).[ext]") } - metadataSources { artifact() } - }, - ivy("https://nodejs.org/download/nightly/") { - name = "Node Nightly Distributions at $url" - patternLayout { artifact("v[revision]/[artifact](-v[revision]-[classifier]).[ext]") } - metadataSources { artifact() } - }, - ) - filter { includeGroup("org.nodejs") } - } - - exclusiveContent { - forRepository { - ivy("https://github.com/yarnpkg/yarn/releases/download") { - name = "Yarn Distributions at $url" - patternLayout { artifact("v[revision]/[artifact](-v[revision]).[ext]") } - metadataSources { artifact() } - } - } - filter { includeGroup("com.yarnpkg") } - } - - // workaround for https://youtrack.jetbrains.com/issue/KT-51379 - exclusiveContent { - forRepository { - ivy("https://download.jetbrains.com/kotlin/native/builds") { - name = "Kotlin Native" - patternLayout { - // example download URLs: - // https://download.jetbrains.com/kotlin/native/builds/releases/1.7.20/linux-x86_64/kotlin-native-prebuilt-linux-x86_64-1.7.20.tar.gz - // https://download.jetbrains.com/kotlin/native/builds/releases/1.7.20/windows-x86_64/kotlin-native-prebuilt-windows-x86_64-1.7.20.zip - // https://download.jetbrains.com/kotlin/native/builds/releases/1.7.20/macos-x86_64/kotlin-native-prebuilt-macos-x86_64-1.7.20.tar.gz - listOf( - "macos-x86_64", - "macos-aarch64", - "osx-x86_64", - "osx-aarch64", - "linux-x86_64", - "windows-x86_64", - ).forEach { os -> - listOf("dev", "releases").forEach { stage -> - artifact("$stage/[revision]/$os/[artifact]-[revision].[ext]") - } - } - } - metadataSources { artifact() } - } - } - filter { includeModuleByRegex(".*", ".*kotlin-native-prebuilt.*") } - } + repositories { + mavenCentral() + maven("https://oss.sonatype.org/content/repositories/snapshots") { + name = "Sonatype Snapshots" + mavenContent { snapshotsOnly() } } + + // Declare the Node.js & Yarn download repositories + exclusiveContent { + forRepository { + ivy("https://nodejs.org/dist/") { + name = "Node Distributions at $url" + patternLayout { artifact("v[revision]/[artifact](-v[revision]-[classifier]).[ext]") } + metadataSources { artifact() } + content { includeModule("org.nodejs", "node") } + } + } + filter { includeGroup("org.nodejs") } + } + + exclusiveContent { + forRepository { + ivy("https://github.com/yarnpkg/yarn/releases/download") { + name = "Yarn Distributions at $url" + patternLayout { artifact("v[revision]/[artifact](-v[revision]).[ext]") } + metadataSources { artifact() } + content { includeModule("com.yarnpkg", "yarn") } + } + } + filter { includeGroup("com.yarnpkg") } + } + + // workaround for https://youtrack.jetbrains.com/issue/KT-51379 + exclusiveContent { + forRepository { + ivy("https://download.jetbrains.com/kotlin/native/builds") { + name = "Kotlin Native" + patternLayout { + // example download URLs: + // https://download.jetbrains.com/kotlin/native/builds/releases/1.7.20/linux-x86_64/kotlin-native-prebuilt-linux-x86_64-1.7.20.tar.gz + // https://download.jetbrains.com/kotlin/native/builds/releases/1.7.20/windows-x86_64/kotlin-native-prebuilt-windows-x86_64-1.7.20.zip + // https://download.jetbrains.com/kotlin/native/builds/releases/1.7.20/macos-x86_64/kotlin-native-prebuilt-macos-x86_64-1.7.20.tar.gz + listOf( + "macos-x86_64", + "macos-aarch64", + "osx-x86_64", + "osx-aarch64", + "linux-x86_64", + "windows-x86_64", + ).forEach { os -> + listOf("dev", "releases").forEach { stage -> + artifact("$stage/[revision]/$os/[artifact]-[revision].[ext]") + } + } + } + metadataSources { artifact() } + } + } + filter { includeModuleByRegex(".*", ".*kotlin-native-prebuilt.*") } + } + } } enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") include( - ":urlencoder-app", - ":urlencoder-lib", + ":urlencoder-app", + ":urlencoder-lib", ) diff --git a/urlencoder-app/build.gradle.kts b/urlencoder-app/build.gradle.kts index c3fc159..93a9789 100644 --- a/urlencoder-app/build.gradle.kts +++ b/urlencoder-app/build.gradle.kts @@ -1,21 +1,17 @@ import org.jetbrains.dokka.gradle.DokkaTask -import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi plugins { buildsrc.conventions.lang.`kotlin-multiplatform-jvm` +// buildsrc.conventions.lang.`kotlin-multiplatform-js` +// buildsrc.conventions.lang.`kotlin-multiplatform-native` buildsrc.conventions.publishing + id("application") id("com.github.ben-manes.versions") } val urlEncoderMainClass = "net.thauvin.erik.urlencoder.UrlEncoder" kotlin { - jvm { - @OptIn(ExperimentalKotlinGradlePluginApi::class) - mainRun { - mainClass.set(urlEncoderMainClass) - } - } sourceSets { commonMain { dependencies { @@ -36,6 +32,10 @@ base { archivesName.set(rootProject.name) } +application { + mainClass.set(urlEncoderMainClass) +} + tasks { jvmJar { manifest { @@ -45,13 +45,14 @@ tasks { val fatJar by registering(Jar::class) { group = LifecycleBasePlugin.BUILD_GROUP + dependsOn.addAll(listOf("compileJava", "compileKotlinJvm", "processResources")) archiveClassifier.set("all") duplicatesStrategy = DuplicatesStrategy.EXCLUDE - manifest { attributes(mapOf("Main-Class" to urlEncoderMainClass)) } - from(sourceSets.main.map { it.output }) + manifest { attributes(mapOf("Main-Class" to application.mainClass)) } + from(sourceSets.main.get().output) dependsOn(configurations.jvmRuntimeClasspath) from(configurations.jvmRuntimeClasspath.map { classpath -> - classpath.filter { it.name.endsWith(".jar") }.map { zipTree(it) } + classpath.incoming.artifacts.artifactFiles.files.filter { it.name.endsWith("jar") }.map { zipTree(it) } }) } diff --git a/urlencoder-lib/src/commonTest/kotlin/net/thauvin/erik/urlencoder/TestData.kt b/urlencoder-lib/src/commonTest/kotlin/net/thauvin/erik/urlencoder/TestData.kt deleted file mode 100644 index 6cbf139..0000000 --- a/urlencoder-lib/src/commonTest/kotlin/net/thauvin/erik/urlencoder/TestData.kt +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2001-2024 the original author or 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. - */ - -package net.thauvin.erik.urlencoder - -import kotlin.jvm.JvmField - -const val standardContent = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVQXYZ0123456789-_." - -val invalidContent = listOf("sdkjfh%", "sdkjfh%6", "sdkjfh%xx", "sdfjfh%-1") - -/** - * List of unencoded content paired with the encoded content. - */ -val decodedToEncoded = listOf( - TestData("a test &", "a%20test%20%26"), - TestData( - "!abcdefghijklmnopqrstuvwxyz%%ABCDEFGHIJKLMNOPQRSTUVQXYZ0123456789-_.~=", - "%21abcdefghijklmnopqrstuvwxyz%25%25ABCDEFGHIJKLMNOPQRSTUVQXYZ0123456789-_.%7E%3D" - ), - TestData("%#okékÉȢ smile!😁", "%25%23ok%C3%A9k%C3%89%C8%A2%20smile%21%F0%9F%98%81"), - TestData("\uD808\uDC00\uD809\uDD00\uD808\uDF00\uD808\uDD00", "%F0%92%80%80%F0%92%94%80%F0%92%8C%80%F0%92%84%80"), -) - -data class TestData( - @JvmField - val unencoded: String, - @JvmField - val encoded: String, -) diff --git a/urlencoder-lib/src/commonTest/kotlin/net/thauvin/erik/urlencoder/UrlEncoderUtilTest.kt b/urlencoder-lib/src/commonTest/kotlin/net/thauvin/erik/urlencoder/UrlEncoderUtilTest.kt index 102bbfa..7b52619 100644 --- a/urlencoder-lib/src/commonTest/kotlin/net/thauvin/erik/urlencoder/UrlEncoderUtilTest.kt +++ b/urlencoder-lib/src/commonTest/kotlin/net/thauvin/erik/urlencoder/UrlEncoderUtilTest.kt @@ -23,17 +23,30 @@ import kotlin.test.DefaultAsserter.assertEquals import kotlin.test.DefaultAsserter.assertSame class UrlEncoderUtilTest { + private val same = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVQXYZ0123456789-_." + + companion object { + val invalid = listOf("sdkjfh%", "sdkjfh%6", "sdkjfh%xx", "sdfjfh%-1") + + val validMap = listOf( + "a test &" to "a%20test%20%26", + "!abcdefghijklmnopqrstuvwxyz%%ABCDEFGHIJKLMNOPQRSTUVQXYZ0123456789-_.~=" to + "%21abcdefghijklmnopqrstuvwxyz%25%25ABCDEFGHIJKLMNOPQRSTUVQXYZ0123456789-_.%7E%3D", + "%#okékÉȢ smile!😁" to "%25%23ok%C3%A9k%C3%89%C8%A2%20smile%21%F0%9F%98%81", + "\uD808\uDC00\uD809\uDD00\uD808\uDF00\uD808\uDD00" to "%F0%92%80%80%F0%92%94%80%F0%92%8C%80%F0%92%84%80", + ) + } @Test fun decodeURL() { - for ((unencoded, encoded) in decodedToEncoded) { - assertEquals(unencoded, decode(encoded)) + for (m in validMap) { + assertEquals(m.first, decode(m.second)) } } @Test fun decodeWithException() { - for (source in invalidContent) { + for (source in invalid) { assertFailsWith( message = "decode($source)", block = { decode(source) } @@ -43,7 +56,7 @@ class UrlEncoderUtilTest { @Test fun decodeWhenNoneNeeded() { - assertSame(standardContent, decode(standardContent)) + assertSame(same, decode(same)) assertEquals("decode('')", decode(""), "") assertEquals("decode(' ')", decode(" "), " ") } @@ -59,8 +72,8 @@ class UrlEncoderUtilTest { @Test fun encodeURL() { - for ((unencoded, encoded) in decodedToEncoded) { - assertEquals(encoded, encode(unencoded)) + for (m in validMap) { + assertEquals(m.second, encode(m.first)) } } @@ -73,8 +86,8 @@ class UrlEncoderUtilTest { @Test fun encodeWhenNoneNeeded() { - assertSame(encode(standardContent), standardContent) - assertSame("with empty allow", encode(standardContent, allow = ""), standardContent) + assertSame(encode(same), same) + assertSame("with empty allow", encode(same, allow = ""), same) } @Test diff --git a/urlencoder-lib/src/jvmTest/java/net/thauvin/erik/urlencoder/UrlEncoderJavaTest.java b/urlencoder-lib/src/jvmTest/java/net/thauvin/erik/urlencoder/UrlEncoderJavaTest.java deleted file mode 100644 index 8fa9561..0000000 --- a/urlencoder-lib/src/jvmTest/java/net/thauvin/erik/urlencoder/UrlEncoderJavaTest.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2001-2024 the original author or 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. - */ - -package net.thauvin.erik.urlencoder; - -import org.junit.jupiter.api.Test; - -import static net.thauvin.erik.urlencoder.TestDataKt.getDecodedToEncoded; -import static net.thauvin.erik.urlencoder.UrlEncoderUtil.decode; -import static net.thauvin.erik.urlencoder.UrlEncoderUtil.encode; -import static org.junit.jupiter.api.Assertions.assertAll; -import static org.junit.jupiter.api.Assertions.assertEquals; - -class UrlEncoderJavaTest { - - @Test - public void decodeURL() { - assertAll( - getDecodedToEncoded() - .stream() - .map(data -> - () -> assertEquals(data.unencoded, decode(data.encoded)) - ) - ); - } - - @Test - public void encodeURL() { - assertAll( - getDecodedToEncoded() - .stream() - .map(data -> - () -> assertEquals(data.encoded, encode(data.unencoded)) - ) - ); - } -}