• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download

<lambda>null1 import com.android.build.api.variant.*
2 import kotlin.math.max
3 
4 plugins {
5     id("com.android.application")
6     id("org.jetbrains.kotlin.android")
7 }
8 
9 val PYTHON_DIR = file("../../..").canonicalPath
10 val PYTHON_CROSS_DIR = "$PYTHON_DIR/cross-build"
11 
12 val ABIS = mapOf(
13     "arm64-v8a" to "aarch64-linux-android",
14     "x86_64" to "x86_64-linux-android",
<lambda>null15 ).filter { file("$PYTHON_CROSS_DIR/${it.value}").exists() }
16 if (ABIS.isEmpty()) {
17     throw GradleException(
18         "No Android ABIs found in $PYTHON_CROSS_DIR: see Android/README.md " +
19         "for building instructions."
20     )
21 }
22 
<lambda>null23 val PYTHON_VERSION = file("$PYTHON_DIR/Include/patchlevel.h").useLines {
24     for (line in it) {
25         val match = """#define PY_VERSION\s+"(\d+\.\d+)""".toRegex().find(line)
26         if (match != null) {
27             return@useLines match.groupValues[1]
28         }
29     }
30     throw GradleException("Failed to find Python version")
31 }
32 
33 
<lambda>null34 android {
35     namespace = "org.python.testbed"
36     compileSdk = 34
37 
38     defaultConfig {
39         applicationId = "org.python.testbed"
40         minSdk = 21
41         targetSdk = 34
42         versionCode = 1
43         versionName = "1.0"
44 
45         ndk.abiFilters.addAll(ABIS.keys)
46         externalNativeBuild.cmake.arguments(
47             "-DPYTHON_CROSS_DIR=$PYTHON_CROSS_DIR",
48             "-DPYTHON_VERSION=$PYTHON_VERSION",
49             "-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON",
50         )
51 
52         testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
53     }
54 
55     val androidEnvFile = file("../../android-env.sh").absoluteFile
56     ndkVersion = androidEnvFile.useLines {
57         for (line in it) {
58             """ndk_version=(\S+)""".toRegex().find(line)?.let {
59                 return@useLines it.groupValues[1]
60             }
61         }
62         throw GradleException("Failed to find NDK version in $androidEnvFile")
63     }
64     externalNativeBuild.cmake {
65         path("src/main/c/CMakeLists.txt")
66     }
67 
68     // Set this property to something non-empty, otherwise it'll use the default
69     // list, which ignores asset directories beginning with an underscore.
70     aaptOptions.ignoreAssetsPattern = ".git"
71 
72     compileOptions {
73         sourceCompatibility = JavaVersion.VERSION_1_8
74         targetCompatibility = JavaVersion.VERSION_1_8
75     }
76     kotlinOptions {
77         jvmTarget = "1.8"
78     }
79 
80     testOptions {
81         managedDevices {
82             localDevices {
83                 create("minVersion") {
84                     device = "Small Phone"
85 
86                     // Managed devices have a minimum API level of 27.
87                     apiLevel = max(27, defaultConfig.minSdk!!)
88 
89                     // ATD devices are smaller and faster, but have a minimum
90                     // API level of 30.
91                     systemImageSource = if (apiLevel >= 30) "aosp-atd" else "aosp"
92                 }
93 
94                 create("maxVersion") {
95                     device = "Small Phone"
96                     apiLevel = defaultConfig.targetSdk!!
97                     systemImageSource = "aosp-atd"
98                 }
99             }
100 
101             // If the previous test run succeeded and nothing has changed,
102             // Gradle thinks there's no need to run it again. Override that.
103             afterEvaluate {
104                 (localDevices.names + listOf("connected")).forEach {
105                     tasks.named("${it}DebugAndroidTest") {
106                         outputs.upToDateWhen { false }
107                     }
108                 }
109             }
110         }
111     }
112 }
113 
<lambda>null114 dependencies {
115     implementation("androidx.appcompat:appcompat:1.6.1")
116     implementation("com.google.android.material:material:1.11.0")
117     implementation("androidx.constraintlayout:constraintlayout:2.1.4")
118     androidTestImplementation("androidx.test.ext:junit:1.1.5")
119     androidTestImplementation("androidx.test:rules:1.5.0")
120 }
121 
122 
123 // Create some custom tasks to copy Python and its standard library from
124 // elsewhere in the repository.
variantnull125 androidComponents.onVariants { variant ->
126     val pyPlusVer = "python$PYTHON_VERSION"
127     generateTask(variant, variant.sources.assets!!) {
128         into("python") {
129             into("include/$pyPlusVer") {
130                 for (triplet in ABIS.values) {
131                     from("$PYTHON_CROSS_DIR/$triplet/prefix/include/$pyPlusVer")
132                 }
133                 duplicatesStrategy = DuplicatesStrategy.EXCLUDE
134             }
135 
136             into("lib/$pyPlusVer") {
137                 // To aid debugging, the source directory takes priority.
138                 from("$PYTHON_DIR/Lib")
139 
140                 // The cross-build directory provides ABI-specific files such as
141                 // sysconfigdata.
142                 for (triplet in ABIS.values) {
143                     from("$PYTHON_CROSS_DIR/$triplet/prefix/lib/$pyPlusVer")
144                 }
145 
146                 into("site-packages") {
147                     from("$projectDir/src/main/python")
148                 }
149 
150                 duplicatesStrategy = DuplicatesStrategy.EXCLUDE
151                 exclude("**/__pycache__")
152             }
153         }
154     }
155 
156     generateTask(variant, variant.sources.jniLibs!!) {
157         for ((abi, triplet) in ABIS.entries) {
158             into(abi) {
159                 from("$PYTHON_CROSS_DIR/$triplet/prefix/lib")
160                 include("libpython*.*.so")
161                 include("lib*_python.so")
162             }
163         }
164     }
165 }
166 
167 
generateTasknull168 fun generateTask(
169     variant: ApplicationVariant, directories: SourceDirectories,
170     configure: GenerateTask.() -> Unit
171 ) {
172     val taskName = "generate" +
173         listOf(variant.name, "Python", directories.name)
174             .map { it.replaceFirstChar(Char::uppercase) }
175             .joinToString("")
176 
177     directories.addGeneratedSourceDirectory(
178         tasks.register<GenerateTask>(taskName) {
179             into(outputDir)
180             configure()
181         },
182         GenerateTask::outputDir)
183 }
184 
185 
186 // addGeneratedSourceDirectory requires the task to have a DirectoryProperty.
187 abstract class GenerateTask: Sync() {
188     @get:OutputDirectory
189     abstract val outputDir: DirectoryProperty
190 }
191