[GH] Support building sqlite / room on Github
This CL adds sqlite as a project to GitHub and makes it possible compile.
On AOSP, we use sqlite prebuilts to find sources. On GitHub, we'll download sqlite
amalgamation. To ensure they stay consistent, we check the version in the header
file which will also be checked on AOSP build.
Fixes: 306669673
Test: ./gradlew playgroundCIHostTests
This is an imported pull request from https://github.com/androidx/androidx/pull/647.
Resolves #647
Github-Pr-Head-Sha: 2a3918bf6f9cd7cc957a8ec80e927780a439ebde
GitOrigin-RevId: 67ca8f41db5998fe448cbb9a3cdae7103990bf03
Change-Id: I7a9c7b6f430569039937ef00d3a9b26803830a49
diff --git a/.github/workflows/presubmit.yml b/.github/workflows/presubmit.yml
index 3ceb6a8..8261313 100644
--- a/.github/workflows/presubmit.yml
+++ b/.github/workflows/presubmit.yml
@@ -117,12 +117,14 @@
os: [ubuntu-latest]
# If you would like to remove some projects temporarily, use .github/ci-control/ci-config.json instead.
# Keep these in alphabetical order.
- project: ["activity", "appcompat", "biometric", "collection", "compose-runtime", "core", "datastore", "fragment", "lifecycle", "navigation", "paging", "room", "work"]
+ project: ["activity", "appcompat", "biometric", "collection", "compose-runtime", "core", "datastore", "fragment", "lifecycle", "navigation", "paging", "room", "sqlite", "work"]
include:
- project: "compose-runtime"
project-root: "compose/runtime"
- project: "collection"
custom-os: "macos-latest"
+ - project: "sqlite"
+ custom-os: "macos-latest"
runs-on: ${{ matrix.custom-os || matrix.os }}
needs: [setup, lint]
env:
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/KonanPrebuiltsSetup.kt b/buildSrc/private/src/main/kotlin/androidx/build/KonanPrebuiltsSetup.kt
index 1424f4e..19a217e 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/KonanPrebuiltsSetup.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/KonanPrebuiltsSetup.kt
@@ -34,16 +34,22 @@
/**
* Creates a Konan distribution with the given [prebuiltsDirectory] and [konanHome].
+ *
+ * @param prebuiltsDirectory The directory where AndroidX prebuilts are present. Can be `null`
+ * for playground builds which means we'll fetch Kotlin Native prebuilts from the
+ * internet using the Kotlin Gradle Plugin.
*/
fun createKonanDistribution(
- prebuiltsDirectory: File,
+ prebuiltsDirectory: File?,
konanHome: File
) = Distribution(
konanHome = konanHome.canonicalPath,
onlyDefaultProfiles = false,
- propertyOverrides = mapOf(
- "dependenciesUrl" to "file://${prebuiltsDirectory.canonicalPath}"
- )
+ propertyOverrides = prebuiltsDirectory?.let {
+ mapOf(
+ "dependenciesUrl" to "file://${it.canonicalPath}"
+ )
+ }
)
/**
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/clang/KonanBuildService.kt b/buildSrc/private/src/main/kotlin/androidx/build/clang/KonanBuildService.kt
index 6e74707..57c3cfe 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/clang/KonanBuildService.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/clang/KonanBuildService.kt
@@ -17,6 +17,7 @@
package androidx.build.clang
import androidx.build.KonanPrebuiltsSetup
+import androidx.build.ProjectLayoutType
import androidx.build.clang.KonanBuildService.Companion.obtain
import androidx.build.getKonanPrebuiltsFolder
import java.io.ByteArrayOutputStream
@@ -29,6 +30,7 @@
import org.gradle.api.provider.Provider
import org.gradle.api.services.BuildService
import org.gradle.api.services.BuildServiceParameters
+import org.gradle.api.tasks.Optional
import org.gradle.process.ExecOperations
import org.gradle.process.ExecSpec
import org.jetbrains.kotlin.gradle.plugin.KotlinMultiplatformPluginWrapper
@@ -52,8 +54,18 @@
private val execOperations: ExecOperations
) : BuildService<KonanBuildService.Parameters> {
private val dist by lazy {
+ // double check that we don't initialize konan distribution without prebuilts in AOSP
+ check(
+ parameters.projectLayoutType.get() == ProjectLayoutType.PLAYGROUND ||
+ parameters.prebuilts.isPresent
+ ) {
+ """
+ Prebuilts directory for Konan must be provided when the project is not a playground
+ project.
+ """.trimIndent()
+ }
KonanPrebuiltsSetup.createKonanDistribution(
- prebuiltsDirectory = parameters.prebuilts.get().asFile,
+ prebuiltsDirectory = parameters.prebuilts.orNull?.asFile,
konanHome = parameters.konanHome.get().asFile
)
}
@@ -76,7 +88,7 @@
add("--compile")
parameters.includes.files.forEach { includeDirectory ->
check(includeDirectory.isDirectory) {
- "Include parameter for clang must be a directory"
+ "Include parameter for clang must be a directory: $includeDirectory"
}
add("-I${includeDirectory.canonicalPath}")
}
@@ -213,8 +225,22 @@
}
interface Parameters : BuildServiceParameters {
+ /**
+ * KONAN_HOME parameter for initializing konan
+ */
val konanHome: DirectoryProperty
+
+ /**
+ * Location if konan prebuilts. Can be null if this is a playground project
+ */
+ @get:Optional
val prebuilts: DirectoryProperty
+
+ /**
+ * The type of the project (Playground vs AOSP main). This value is used to ensure
+ * we initialize Konan distribution properly.
+ */
+ val projectLayoutType: Property<ProjectLayoutType>
}
companion object {
@@ -240,9 +266,14 @@
it.parameters.konanHome.set(
nativeCompilerDownloader.compilerDirectory
)
- it.parameters.prebuilts.set(
- project.getKonanPrebuiltsFolder()
+ it.parameters.projectLayoutType.set(
+ ProjectLayoutType.from(project)
)
+ if (!ProjectLayoutType.isPlayground(project)) {
+ it.parameters.prebuilts.set(
+ project.getKonanPrebuiltsFolder()
+ )
+ }
}
}
}
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/clang/NativeTargetCompilation.kt b/buildSrc/private/src/main/kotlin/androidx/build/clang/NativeTargetCompilation.kt
index 041cec82..6d4c312 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/clang/NativeTargetCompilation.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/clang/NativeTargetCompilation.kt
@@ -16,6 +16,7 @@
package androidx.build.clang
+import androidx.build.ProjectLayoutType
import java.io.File
import org.gradle.api.Named
import org.gradle.api.Project
@@ -98,7 +99,9 @@
// jni_md.h -> Includes machine dependant definitions.
// Internal Devs: You can read more about it here: http://go/androidx-jni-cross-compilation
val javaHome = File(System.getProperty("java.home"))
-
+ if (ProjectLayoutType.isPlayground(project)) {
+ return findJniHeadersInPlayground(javaHome)
+ }
// for jni_md, we need to find the prebuilts because each jdk ships with jni_md only for
// its own target family.
val jdkPrebuiltsRoot = javaHome.parentFile
@@ -137,4 +140,25 @@
}
}
}
+
+ /**
+ * JDK ships with JNI headers only for the current platform. As a result, we don't have access
+ * to cross-platform jni headers. They are mostly the same and we don't ship cross compiled code
+ * from GitHub so it is acceptable to use local JNI headers for cross platform compilation on
+ * GitHub.
+ */
+ private fun findJniHeadersInPlayground(
+ javaHome: File
+ ): List<File> {
+ val include = File(javaHome, "include")
+ if (!include.exists()) {
+ error("Cannot find header directory in $javaHome")
+ }
+ return listOf(
+ include,
+ File(include, "darwin"),
+ File(include, "linux"),
+ File(include, "win32"),
+ ).filter { it.exists() }
+ }
}
diff --git a/playground-common/playground-plugin/src/main/kotlin/androidx/playground/PlaygroundExtension.kt b/playground-common/playground-plugin/src/main/kotlin/androidx/playground/PlaygroundExtension.kt
index 800bf43..9ca83a4 100644
--- a/playground-common/playground-plugin/src/main/kotlin/androidx/playground/PlaygroundExtension.kt
+++ b/playground-common/playground-plugin/src/main/kotlin/androidx/playground/PlaygroundExtension.kt
@@ -237,7 +237,6 @@
private val UNSUPPORTED_PROJECTS = listOf(
":benchmark:benchmark-common", // requires prebuilts
":core:core", // stable aidl, b/270593834
- ":sqlite:sqlite-bundled", // clang compilation, b/306669673
":inspection:inspection", // native compilation
)
}
diff --git a/playground-projects/sqlite-playground/.idea/codeStyles/Project.xml b/playground-projects/sqlite-playground/.idea/codeStyles/Project.xml
new file mode 120000
index 0000000..32e9847
--- /dev/null
+++ b/playground-projects/sqlite-playground/.idea/codeStyles/Project.xml
@@ -0,0 +1 @@
+../../../../.idea/codeStyles/Project.xml
\ No newline at end of file
diff --git a/playground-projects/sqlite-playground/.idea/codeStyles/codeStyleConfig.xml b/playground-projects/sqlite-playground/.idea/codeStyles/codeStyleConfig.xml
new file mode 120000
index 0000000..873592ff
--- /dev/null
+++ b/playground-projects/sqlite-playground/.idea/codeStyles/codeStyleConfig.xml
@@ -0,0 +1 @@
+../../../../.idea/codeStyles/codeStyleConfig.xml
\ No newline at end of file
diff --git a/playground-projects/sqlite-playground/.idea/copyright/AndroidCopyright.xml b/playground-projects/sqlite-playground/.idea/copyright/AndroidCopyright.xml
new file mode 120000
index 0000000..f9a587a
--- /dev/null
+++ b/playground-projects/sqlite-playground/.idea/copyright/AndroidCopyright.xml
@@ -0,0 +1 @@
+../../../../.idea/copyright/AndroidCopyright.xml
\ No newline at end of file
diff --git a/playground-projects/sqlite-playground/.idea/copyright/profiles_settings.xml b/playground-projects/sqlite-playground/.idea/copyright/profiles_settings.xml
new file mode 120000
index 0000000..0b054ba
--- /dev/null
+++ b/playground-projects/sqlite-playground/.idea/copyright/profiles_settings.xml
@@ -0,0 +1 @@
+../../../../.idea/copyright/profiles_settings.xml
\ No newline at end of file
diff --git a/playground-projects/sqlite-playground/.idea/inspectionProfiles/Project_Default.xml b/playground-projects/sqlite-playground/.idea/inspectionProfiles/Project_Default.xml
new file mode 120000
index 0000000..b8d649c
--- /dev/null
+++ b/playground-projects/sqlite-playground/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1 @@
+../../../../.idea/inspectionProfiles/Project_Default.xml
\ No newline at end of file
diff --git a/playground-projects/sqlite-playground/.idea/scopes/Ignore_API_Files.xml b/playground-projects/sqlite-playground/.idea/scopes/Ignore_API_Files.xml
new file mode 120000
index 0000000..009eced
--- /dev/null
+++ b/playground-projects/sqlite-playground/.idea/scopes/Ignore_API_Files.xml
@@ -0,0 +1 @@
+../../../../.idea/scopes/Ignore_API_Files.xml
\ No newline at end of file
diff --git a/playground-projects/sqlite-playground/.idea/scopes/buildSrc.xml b/playground-projects/sqlite-playground/.idea/scopes/buildSrc.xml
new file mode 120000
index 0000000..a4d6cbd
--- /dev/null
+++ b/playground-projects/sqlite-playground/.idea/scopes/buildSrc.xml
@@ -0,0 +1 @@
+../../../../.idea/scopes/buildSrc.xml
\ No newline at end of file
diff --git a/playground-projects/sqlite-playground/gradle b/playground-projects/sqlite-playground/gradle
new file mode 120000
index 0000000..27b2e9c
--- /dev/null
+++ b/playground-projects/sqlite-playground/gradle
@@ -0,0 +1 @@
+../../playground-common/gradle
\ No newline at end of file
diff --git a/playground-projects/sqlite-playground/gradle.properties b/playground-projects/sqlite-playground/gradle.properties
new file mode 120000
index 0000000..bbd5978
--- /dev/null
+++ b/playground-projects/sqlite-playground/gradle.properties
@@ -0,0 +1 @@
+../../playground-common/androidx-shared.properties
\ No newline at end of file
diff --git a/playground-projects/sqlite-playground/gradlew b/playground-projects/sqlite-playground/gradlew
new file mode 120000
index 0000000..d9f055c
--- /dev/null
+++ b/playground-projects/sqlite-playground/gradlew
@@ -0,0 +1 @@
+../../playground-common/gradlew
\ No newline at end of file
diff --git a/playground-projects/sqlite-playground/gradlew.bat b/playground-projects/sqlite-playground/gradlew.bat
new file mode 120000
index 0000000..c35bc92
--- /dev/null
+++ b/playground-projects/sqlite-playground/gradlew.bat
@@ -0,0 +1 @@
+../../playground-common/gradlew.bat
\ No newline at end of file
diff --git a/playground-projects/sqlite-playground/settings.gradle b/playground-projects/sqlite-playground/settings.gradle
new file mode 100644
index 0000000..2ecf4ae
--- /dev/null
+++ b/playground-projects/sqlite-playground/settings.gradle
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * 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
+ *
+ * http://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.
+ */
+
+// see ../playground-common/README.md for details on how this works
+pluginManagement {
+ apply from: "../../playground-common/configure-plugin-management.gradle", to: it
+}
+plugins {
+ id "playground"
+}
+
+rootProject.name = "sqlite-playground"
+
+playground {
+ setupPlayground("../..")
+ selectProjectsFromAndroidX({ name ->
+ if (name.startsWith(":sqlite:") && !name.contains("inspection")) return true
+ return false
+ })
+}
diff --git a/sqlite/sqlite-bundled/PrepareSqliteSourcesTask.groovy b/sqlite/sqlite-bundled/PrepareSqliteSourcesTask.groovy
new file mode 100644
index 0000000..d2868ec
--- /dev/null
+++ b/sqlite/sqlite-bundled/PrepareSqliteSourcesTask.groovy
@@ -0,0 +1,266 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * 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
+ *
+ * http://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.
+ */
+
+import androidx.build.ProjectLayoutType
+import org.gradle.api.DefaultTask
+import org.gradle.api.file.DirectoryProperty
+import org.gradle.api.provider.Property
+import org.gradle.api.tasks.Copy
+import org.gradle.api.tasks.PathSensitivity
+
+import javax.inject.Inject
+
+/**
+ * Finds the sqlite sources and puts them into the `destinationDirectory`.
+ *
+ * This task is setup differently between AOSP and Github (Playground), hence use the helper
+ * `registerPrepareSqliteSourcesTask` method to create an instance of it.
+ *
+ * On AOSP, the sources are in an external prebuilts repository and simply get copied into the given
+ * [destinationDirectory].
+ * On Github, they are downloaded from SQLite servers and copied into the `destinationDirectory`
+ * from there.
+ *
+ * To ensure each version is consistent, we use the `sqliteVersion` parameter and check the Sqlite
+ * source code for them.
+ */
+abstract class PrepareSqliteSourcesTask extends DefaultTask {
+ // defined in https://github.com/sqlite/sqlite/blob/master/src/sqlite.h.in#L149
+ private static String VERSION_PREFIX = "#define SQLITE_VERSION"
+ private FileSystemOperations fileSystemOperations
+
+ /**
+ * The Sqlite version to prepare
+ */
+ @Input
+ abstract Property<String> getSqliteVersion()
+
+ /**
+ * The target directory where the Sqlite source will be put
+ */
+ @OutputDirectory
+ abstract DirectoryProperty getDestinationDirectory()
+
+ /**
+ * The source directory which includes the original Sqlite amalgamation distribution
+ */
+ @InputDirectory
+ @PathSensitive(PathSensitivity.NONE)
+ abstract DirectoryProperty getSources()
+
+ @Inject
+ PrepareSqliteSourcesTask(FileSystemOperations fileSystemOperations) {
+ this.fileSystemOperations = fileSystemOperations
+ description = "Create a directory containing Sqlite sources."
+ group = "build"
+ }
+
+ @TaskAction
+ void prepareSources() {
+ File originalSqliteSources = sources.asFile.get()
+ validateSqliteVersion(originalSqliteSources)
+ File targetSourceDirectory = destinationDirectory.asFile.get()
+ targetSourceDirectory.deleteDir()
+ targetSourceDirectory.mkdirs()
+
+ fileSystemOperations.copy { CopySpec copySpec ->
+ copySpec.from(originalSqliteSources)
+ copySpec.into(targetSourceDirectory)
+ copySpec.include("sqlite3.c", "sqlite3.h")
+ }
+ }
+
+ /**
+ * Finds the sqlite version definition in the source file and ensures it is the same
+ * version as [sqliteVersion] to ensure they never go out of sync.
+ */
+ private void validateSqliteVersion(File sourceDir) {
+ File headerFile = new File(sourceDir, "sqlite3.h")
+ if (!headerFile.isFile() || !headerFile.canRead()) {
+ throw new IllegalStateException("Cannot find header file at location: ${headerFile}")
+ }
+ String versionLine = headerFile.text.split('\n').find { it.contains(VERSION_PREFIX) }
+ if (versionLine == null) {
+ throw new IllegalStateException("Cannot find the version line in sqlite.")
+ }
+ String strippedVersion = versionLine.takeAfter(VERSION_PREFIX).trim()
+ .takeBetween("\"", "\"")
+ if (strippedVersion != sqliteVersion.get()) {
+ throw new IllegalStateException("""
+ Expected ${sqliteVersion.get()}, found $strippedVersion. Please update the
+ sqliteVersion parameter if this was intentional.
+ """.trim())
+ }
+ }
+}
+
+/**
+ * Downloads the sqlite amalgamation for the given version.
+ * See: https://sqlite.org/amalgamation.html and https://www.sqlite.org/download.html for
+ * details.
+ */
+@CacheableTask
+abstract class DownloadSQLiteAmalgamationTask extends DefaultTask {
+ /**
+ * The Sqlite version to download
+ */
+ @Input
+ abstract Property<String> getReleaseVersion()
+
+ /**
+ * The year which Sqlite version was released. It is necessary because the download
+ * URL includes the year.
+ */
+ @Input
+ abstract Property<Integer> getReleaseYear()
+
+ /**
+ * Target file where the downloaded amalgamation zip file will be written.
+ */
+ @OutputFile
+ abstract RegularFileProperty getDownloadTargetFile()
+
+ DownloadSQLiteAmalgamationTask() {
+ description = "Downloads the Sqlite amalgamation build from sqlite servers"
+ group = "build"
+ }
+
+ @TaskAction
+ void download() {
+ File downloadTarget = downloadTargetFile.asFile.get()
+ downloadTarget.delete()
+ downloadTarget.parentFile.mkdirs()
+ String downloadUrl = buildDownloadUrl(releaseYear.get(), releaseVersion.get())
+ downloadTarget.withOutputStream { outputStream ->
+ new URL(downloadUrl).withInputStream { inputStream ->
+ inputStream.transferTo(outputStream)
+ }
+ }
+ }
+
+ /**
+ * Computes the download URL from the sqlite version and release year inputs.
+ */
+ private static String buildDownloadUrl(int releaseYear, String releaseVersion) {
+ // see https://www.sqlite.org/download.html
+ // The version is encoded so that filenames sort in order of increasing version number
+ // when viewed using "ls".
+ // For version 3.X.Y the filename encoding is 3XXYY00.
+ // For branch version 3.X.Y.Z, the encoding is 3XXYYZZ.
+ def sections = releaseVersion.split('\\.')
+ if (sections.size() < 3) {
+ throw new IllegalArgumentException("Invalid sqlite version $releaseVersion")
+ }
+ int major = sections[0].toInteger()
+ int minor = sections[1].toInteger()
+ int patch = sections[2].toInteger()
+ int branch = sections.size() >= 4 ? sections[3].toInteger() : 0
+ String fileName = String.format("%d%02d%02d%02d.zip", major, minor, patch, branch)
+ return "https://www.sqlite.org/${releaseYear}/sqlite-amalgamation-${fileName}"
+ }
+}
+
+/**
+ * Configuration object for preparing relevant sqlite sources.
+ */
+abstract class Configuration {
+ /**
+ * The Sqlite version to be prepared.
+ */
+ abstract Property<String> getSqliteVersion()
+
+ /**
+ * The release year of the requested Sqlite version.
+ * It is necessary because the download URL for sqlite amalgamation includes the
+ * release year.
+ */
+ abstract Property<Integer> getSqliteReleaseYear()
+
+ /**
+ * The location to put prepared sqlite sources.
+ */
+ abstract DirectoryProperty getDestinationDirectory()
+
+ /**
+ * Set when sqlite is downloaded from prebuilts rather than from Sqlite servers (used in AOSP).
+ */
+ abstract DirectoryProperty getSqlitePrebuiltsDirectory()
+}
+
+/**
+ * Utility method to create an instance of [PrepareSqliteSourcesTask] that is compatible
+ * with both AOSP and GitHub builds.
+ * This is exported into the build script via ext properties.
+ */
+TaskProvider<PrepareSqliteSourcesTask> registerPrepareSqliteSourcesTask(
+ Project project,
+ String name,
+ Action<Configuration> configure
+) {
+ def configuration = project.objects.newInstance(Configuration.class)
+ configure.execute(configuration)
+
+ def distDirectory = project.objects.directoryProperty()
+ if (ProjectLayoutType.isPlayground(project)) {
+ def downloadTaskProvider = project.tasks.register(
+ name.capitalize() + "DownloadAmalgamation",
+ DownloadSQLiteAmalgamationTask
+ ) {
+ it.releaseVersion.set(configuration.sqliteVersion)
+ it.releaseYear.set(configuration.sqliteReleaseYear)
+ it.downloadTargetFile.set(
+ project.layout.buildDirectory.file("sqlite3/download/amalgamation.zip")
+ )
+ }
+
+ def unzipTaskProvider = project.tasks.register(
+ name.capitalize() + "UnzipAmalgamation",
+ Copy
+ ) {
+ it.from(
+ project.zipTree(downloadTaskProvider.map { it.downloadTargetFile })
+ )
+ it.into(
+ project.layout.buildDirectory.dir("sqlite3/download/unzipped")
+ )
+ it.eachFile {
+ it.path = it.path.replaceFirst(/sqlite-amalgamation-\d+\//, '')
+ }
+ }
+ distDirectory.set(
+ project.objects.directoryProperty().fileProvider(
+ unzipTaskProvider.map { it.destinationDir }
+ )
+ )
+ } else {
+ distDirectory.set(configuration.sqlitePrebuiltsDirectory)
+ }
+
+ def prepareSourcesTaskProvider = project.tasks.register(
+ name,
+ PrepareSqliteSourcesTask
+ ) {
+ it.sources.set(distDirectory)
+ it.sqliteVersion.set(configuration.sqliteVersion)
+ it.destinationDirectory.set(
+ project.layout.buildDirectory.dir("sqlite/selected-sources")
+ )
+ }
+ return prepareSourcesTaskProvider
+}
+
+// export a function to register the task
+ext.registerPrepareSqliteSourcesTask = this.®isterPrepareSqliteSourcesTask
diff --git a/sqlite/sqlite-bundled/build.gradle b/sqlite/sqlite-bundled/build.gradle
index 821dd58..025a419 100644
--- a/sqlite/sqlite-bundled/build.gradle
+++ b/sqlite/sqlite-bundled/build.gradle
@@ -24,6 +24,7 @@
import androidx.build.AndroidXConfig
import androidx.build.KmpPlatformsKt
import androidx.build.PlatformIdentifier
+import androidx.build.ProjectLayoutType
import androidx.build.Publish
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
import org.jetbrains.kotlin.konan.target.Family
@@ -34,6 +35,8 @@
id("com.android.library")
}
+apply(from: "PrepareSqliteSourcesTask.groovy")
+
// List of flags we use to compile SQLite.
// See: https://www.sqlite.org/compile.html
// TODO(b/310681164): Validate these flags and compare to other platforms.
@@ -55,19 +58,26 @@
"-DSQLITE_THREADSAFE=2",
]
-// Copy SQLite sources into a build directory. Even though this is not needed, it will be
-// nice to have them there so that we can swap them with SQLite amalgamation for Github.
-// TODO(b/306669673): Support GitHub builds
-def copySqliteSourcesTask = tasks.register("copySqliteSources", Copy) {
- from(new File(AndroidXConfig.getExternalProjectPath(project), "sqlite/dist/orig")) {
- include "sqlite3.c"
- include "sqlite3.h"
+// see PrepareSqliteSourcesTask.groovy for implementation details of the register method
+// This task prepares a directory to build sqlite and bundle it with the artifact.
+// SQLite is in public domain: https://www.sqlite.org/copyright.html.
+// Including it inside the library implies relicencing it to Apache 2.0.
+def prepareSqliteSourcesTask = registerPrepareSqliteSourcesTask(
+ project, // project
+ "prepareSqliteSourcesTask" // task name
+) {
+ it.sqliteVersion.set("3.42.0")
+ it.sqliteReleaseYear.set(2023)
+ it.destinationDirectory.set(project.layout.buildDirectory.dir("sqlite3/src"))
+ if (!ProjectLayoutType.isPlayground(project)) {
+ it.sqlitePrebuiltsDirectory.set(
+ new File(AndroidXConfig.getExternalProjectPath(project), "sqlite/dist/orig")
+ )
}
- into(project.layout.buildDirectory.dir("sqlite3/src"))
}
configurations {
- // Configuration for producing an shareable archive file of compiled SQLite. Only the Linux X64
+ // Configuration for producing a shareable archive file of compiled SQLite. Only the Linux X64
// target of SQLite is produced hence the explicit name and attributes
linuxSharedArchive {
canBeConsumed = true
@@ -76,6 +86,14 @@
attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.NATIVE_LINK))
}
}
+ // Configuration for producing a shareable directory that includes SQLite amalgamation sources.
+ sqliteSources {
+ canBeConsumed = true
+ canBeResolved = false
+ attributes {
+ attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, "sqlite-amalgamation"))
+ }
+ }
}
def nativeEnabled = KmpPlatformsKt.enableNative(project)
@@ -99,12 +117,12 @@
configureEachTarget { nativeCompilation ->
// add SQLite header
nativeCompilation.includes.from(
- copySqliteSourcesTask.map { it.destinationDir }
+ prepareSqliteSourcesTask.map { it.destinationDirectory }
)
// add SQLite sources
nativeCompilation.sources.from(
- copySqliteSourcesTask.map {
- fileTree(it.destinationDir).matching { include "**/*.c" }
+ prepareSqliteSourcesTask.map {
+ fileTree(it.destinationDirectory).matching { include "**/*.c" }
}
)
// add SQLite compile flags
@@ -125,6 +143,10 @@
sqliteCompilation.sharedArchiveOutputFor(KonanTarget.LINUX_X64.INSTANCE)
)
}
+ artifacts.add(
+ "sqliteSources",
+ prepareSqliteSourcesTask.map { it.destinationDirectory }
+ )
// Defince C++ compilation of JNI
def jvmArtJniImplementation = createNativeCompilation("jvmArtJniImplementation") {
@@ -133,7 +155,7 @@
nativeCompilation.addJniHeaders()
// add SQLite headers
nativeCompilation.includes.from(
- copySqliteSourcesTask.map {it.destinationDir }
+ prepareSqliteSourcesTask.map {it.destinationDirectory }
)
// add our JNI sources, i.e. the SQLite bindings
nativeCompilation.sources.from(
diff --git a/sqlite/sqlite-framework/build.gradle b/sqlite/sqlite-framework/build.gradle
index b2ad298..0e13cf7 100644
--- a/sqlite/sqlite-framework/build.gradle
+++ b/sqlite/sqlite-framework/build.gradle
@@ -24,7 +24,6 @@
import androidx.build.KmpPlatformsKt
import androidx.build.PlatformIdentifier
import androidx.build.Publish
-import androidx.build.SdkHelperKt
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
import org.jetbrains.kotlin.konan.target.KonanTarget
@@ -44,6 +43,14 @@
attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.NATIVE_LINK))
}
}
+ // Configuration for resolving SQLite sources from sqlite-bundled.
+ sqliteSources {
+ canBeConsumed = false
+ canBeResolved = true
+ attributes {
+ attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, "sqlite-amalgamation"))
+ }
+ }
}
androidXMultiplatform {
@@ -103,12 +110,12 @@
// See: https://kotlinlang.org/docs/whatsnew19.html#explicit-c-interoperability-stability-guarantees
main.compilerOptions.options.optIn.add("kotlinx.cinterop.ExperimentalForeignApi")
main.cinterops {
- sqlite3 {
- def externalSQLiteDir = new File(
- SdkHelperKt.getCheckoutRoot(project),
- "/external/sqlite/dist/orig/"
- )
- includeDirs(externalSQLiteDir)
+ sqlite3 { cInteropSettings ->
+ includeDirs(configurations.sqliteSources.incoming.files)
+ // TODO KT-62795 We shouldn't need this dependency once that issue is fixed.
+ tasks.named(cInteropSettings.interopProcessingTaskName).configure {
+ it.dependsOn(configurations.sqliteSources)
+ }
}
}
@@ -136,6 +143,7 @@
dependencies {
sqliteSharedArchive(project(":sqlite:sqlite-bundled"))
+ sqliteSources(project(":sqlite:sqlite-bundled"))
}
androidx {