1 /*
2  * Copyright 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.build
18 
19 import androidx.build.gradle.extraPropertyOrNull
20 import java.util.Locale
21 import org.gradle.api.Project
22 import org.gradle.kotlin.dsl.create
23 import org.gradle.kotlin.dsl.findByType
24 
25 /**
26  * A comma-separated list of target platform groups you wish to enable or disable.
27  *
28  * For example, `-jvm,+mac,+linux,+js` disables all JVM (including Android) target platforms and
29  * enables all Mac (including iOS), Linux, and JavaScript target platforms.
30  */
31 const val ENABLED_KMP_TARGET_PLATFORMS = "androidx.enabled.kmp.target.platforms"
32 
33 /** Target platform groups supported by the AndroidX implementation of Kotlin multi-platform. */
34 enum class PlatformGroup {
35     JVM,
36     JS,
37     WASM,
38     MAC,
39     WINDOWS,
40     LINUX,
41     DESKTOP,
42     ANDROID_NATIVE;
43 
44     companion object {
45         /** Target platform groups which require native compilation (e.g. LLVM). */
46         val native = listOf(MAC, LINUX, WINDOWS, ANDROID_NATIVE)
47 
48         /**
49          * Target platform groups which are enabled by default. We currently enable all platforms by
50          * default.
51          */
52         val enabledByDefault = listOf(ANDROID_NATIVE, DESKTOP, JS, JVM, LINUX, MAC, WASM, WINDOWS)
53     }
54 }
55 
56 /** Target platforms supported by the AndroidX implementation of Kotlin multi-platform. */
57 enum class PlatformIdentifier(val id: String, val group: PlatformGroup) {
58     JVM("jvm", PlatformGroup.JVM),
59     JVM_STUBS("jvmStubs", PlatformGroup.JVM),
60     JS("js", PlatformGroup.JS),
61     WASM_JS("wasmJs", PlatformGroup.WASM),
62     ANDROID("android", PlatformGroup.JVM),
63     ANDROID_NATIVE_ARM32("androidNativeArm32", PlatformGroup.ANDROID_NATIVE),
64     ANDROID_NATIVE_ARM64("androidNativeArm64", PlatformGroup.ANDROID_NATIVE),
65     ANDROID_NATIVE_X86("androidNativeX86", PlatformGroup.ANDROID_NATIVE),
66     ANDROID_NATIVE_X64("androidNativeX64", PlatformGroup.ANDROID_NATIVE),
67     MAC_ARM_64("macosarm64", PlatformGroup.MAC),
68     MAC_OSX_64("macosx64", PlatformGroup.MAC),
69     MINGW_X_64("mingwx64", PlatformGroup.WINDOWS),
70     LINUX_ARM_64("linuxarm64", PlatformGroup.LINUX),
71     LINUX_X_64("linuxx64", PlatformGroup.LINUX),
72     LINUX_X_64_STUBS("linuxx64Stubs", PlatformGroup.LINUX),
73     IOS_SIMULATOR_ARM_64("iossimulatorarm64", PlatformGroup.MAC),
74     IOS_X_64("iosx64", PlatformGroup.MAC),
75     IOS_ARM_64("iosarm64", PlatformGroup.MAC),
76     WATCHOS_SIMULATOR_ARM_64("watchossimulatorarm64", PlatformGroup.MAC),
77     WATCHOS_X_64("watchosx64", PlatformGroup.MAC),
78     WATCHOS_ARM_32("watchosarm32", PlatformGroup.MAC),
79     WATCHOS_ARM_64("watchosarm64", PlatformGroup.MAC),
80     WATCHOS_DEVICE_ARM_64("watchosdevicearm64", PlatformGroup.MAC),
81     TVOS_SIMULATOR_ARM_64("tvossimulatorarm64", PlatformGroup.MAC),
82     TVOS_X_64("tvosx64", PlatformGroup.MAC),
83     TVOS_ARM_64("tvosarm64", PlatformGroup.MAC),
84     DESKTOP("desktop", PlatformGroup.JVM);
85 
86     companion object {
<lambda>null87         private val byId = values().associateBy { it.id }
88 
fromIdnull89         fun fromId(id: String): PlatformIdentifier? = byId[id]
90     }
91 }
92 
93 fun parseTargetPlatformsFlag(flag: String?): Set<PlatformGroup> {
94     if (flag.isNullOrBlank()) {
95         return PlatformGroup.enabledByDefault.toSortedSet()
96     }
97     val enabled = PlatformGroup.enabledByDefault.toMutableList()
98     flag.split(",").forEach {
99         val directive = it.firstOrNull() ?: ""
100         val platform = it.drop(1)
101         when (directive) {
102             '+' -> enabled.addAll(matchingPlatformGroups(platform))
103             '-' -> enabled.removeAll(matchingPlatformGroups(platform))
104             else -> {
105                 throw RuntimeException("Invalid value $flag for $ENABLED_KMP_TARGET_PLATFORMS")
106             }
107         }
108     }
109     return enabled.toSortedSet()
110 }
111 
matchingPlatformGroupsnull112 private fun matchingPlatformGroups(flag: String) =
113     if (flag == "native") {
114         PlatformGroup.native
115     } else {
116         listOf(PlatformGroup.valueOf(flag.uppercase(Locale.getDefault())))
117     }
118 
119 private val Project.enabledKmpPlatforms: Set<PlatformGroup>
120     get() {
121         val extension: KmpPlatformsExtension =
122             extensions.findByType() ?: extensions.create("androidx.build.KmpPlatforms", this)
123         return extension.enabledKmpPlatforms
124     }
125 
126 /** Extension used to store parsed KMP configuration information. */
127 private open class KmpPlatformsExtension(project: Project) {
128     val enabledKmpPlatforms =
129         parseTargetPlatformsFlag(
130             project.extraPropertyOrNull(ENABLED_KMP_TARGET_PLATFORMS) as? String
131         )
132 }
133 
Projectnull134 fun Project.enableJs(): Boolean = enabledKmpPlatforms.contains(PlatformGroup.JS)
135 
136 fun Project.enableAndroidNative(): Boolean =
137     enabledKmpPlatforms.contains(PlatformGroup.ANDROID_NATIVE)
138 
139 fun Project.enableMac(): Boolean = enabledKmpPlatforms.contains(PlatformGroup.MAC)
140 
141 fun Project.enableWindows(): Boolean = enabledKmpPlatforms.contains(PlatformGroup.WINDOWS)
142 
143 fun Project.enableLinux(): Boolean = enabledKmpPlatforms.contains(PlatformGroup.LINUX)
144 
145 fun Project.enableJvm(): Boolean = enabledKmpPlatforms.contains(PlatformGroup.JVM)
146 
147 fun Project.enableDesktop(): Boolean = enabledKmpPlatforms.contains(PlatformGroup.DESKTOP)
148 
149 fun Project.enableWasmJs(): Boolean = enabledKmpPlatforms.contains(PlatformGroup.WASM)
150