diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-05-11 05:11:19 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-05-11 05:11:19 +0000 |
commit | 1ec8f13e5a938f306b54e76fcdf067599be5b189 (patch) | |
tree | 8bc4195c13ee1307d8932db6a918310925339f01 | |
parent | 5a2654beb9ac2b94d506627057e33496b7122870 (diff) | |
parent | 446b435418361a872663352949eb46afae1ab506 (diff) | |
download | ndkports-android13-mainline-mediaprovider-release.tar.gz |
Snap for 8570526 from 446b435418361a872663352949eb46afae1ab506 to mainline-mediaprovider-releaseaml_mpr_331918000aml_mpr_331812020aml_mpr_331711020aml_mpr_331613010aml_mpr_331512020aml_mpr_331412040aml_mpr_331311080aml_mpr_331112050aml_mpr_331112030aml_mpr_331011070aml_mpr_330911040aml_mpr_330811020android13-mainline-mediaprovider-release
Change-Id: I4632401a22624a84f7dd640fb3bb0bbebe4a9f6e
62 files changed, 2038 insertions, 1288 deletions
@@ -1,5 +1,10 @@ .gradle /build/ +/buildSrc/build/ +/curl/build/ +/googletest/build/ +/jsoncpp/build/ +/openssl/build/ # Ignore Gradle GUI config gradle-app.setting @@ -43,11 +48,11 @@ gradle-app.setting # When using Gradle or Maven with auto-import, you should exclude module files, # since they will be recreated, and may cause churn. Uncomment if using # auto-import. -# .idea/modules.xml -# .idea/*.iml -# .idea/modules -# *.iml -# *.ipr +.idea/modules.xml +.idea/*.iml +.idea/modules +*.iml +*.ipr # CMake cmake-build-*/ diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index e3439a9..93ae7ac 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -10,6 +10,7 @@ </JetCodeStyleSettings> <codeStyleSettings language="kotlin"> <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" /> + <option name="KEEP_LINE_BREAKS" value="false" /> <option name="WRAP_ON_TYPING" value="0" /> </codeStyleSettings> </code_scheme> diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..443b5d2 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="CompilerConfiguration"> + <bytecodeTargetLevel target="1.6" /> + </component> +</project>
\ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml new file mode 100644 index 0000000..6fde95a --- /dev/null +++ b/.idea/jarRepositories.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="RemoteRepositoriesConfiguration"> + <remote-repository> + <option name="id" value="central" /> + <option name="name" value="Maven Central repository" /> + <option name="url" value="https://repo1.maven.org/maven2" /> + </remote-repository> + <remote-repository> + <option name="id" value="jboss.community" /> + <option name="name" value="JBoss Community repository" /> + <option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" /> + </remote-repository> + <remote-repository> + <option name="id" value="MavenRepo" /> + <option name="name" value="MavenRepo" /> + <option name="url" value="https://repo.maven.apache.org/maven2/" /> + </remote-repository> + <remote-repository> + <option name="id" value="maven" /> + <option name="name" value="maven" /> + <option name="url" value="https://dl.bintray.com/s1m0nw1/KtsRunner" /> + </remote-repository> + <remote-repository> + <option name="id" value="BintrayJCenter" /> + <option name="name" value="BintrayJCenter" /> + <option name="url" value="https://jcenter.bintray.com/" /> + </remote-repository> + <remote-repository> + <option name="id" value="Google" /> + <option name="name" value="Google" /> + <option name="url" value="https://dl.google.com/dl/android/maven2/" /> + </remote-repository> + </component> +</project>
\ No newline at end of file diff --git a/.idea/kotlinScripting.xml b/.idea/kotlinScripting.xml index a6fe551..e56386c 100644 --- a/.idea/kotlinScripting.xml +++ b/.idea/kotlinScripting.xml @@ -1,6 +1,9 @@ <?xml version="1.0" encoding="UTF-8"?> <project version="4"> <component name="KotlinScriptingSettings"> - <option name="isAutoReloadEnabled" value="true" /> + <scriptDefinition className="org.jetbrains.kotlin.scripting.resolve.KotlinScriptDefinitionFromAnnotatedTemplate" definitionName="KotlinBuildScript"> + <order>2147483647</order> + <autoReloadConfigurations>true</autoReloadConfigurations> + </scriptDefinition> </component> </project>
\ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index bc8d0a3..15b559a 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <project version="4"> <component name="ExternalStorageConfigurationManager" enabled="true" /> - <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK"> + <component name="ProjectRootManager" version="2" languageLevel="JDK_1_6" default="false" project-jdk-name="1.8" project-jdk-type="JavaSDK"> <output url="file://$PROJECT_DIR$/out" /> </component> </project>
\ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index 347b2d7..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project version="4"> - <component name="ProjectModuleManager"> - <modules> - <module fileurl="file://$PROJECT_DIR$/.idea/ndkports.iml" filepath="$PROJECT_DIR$/.idea/ndkports.iml" /> - </modules> - </component> -</project>
\ No newline at end of file diff --git a/.idea/ndkports.iml b/.idea/ndkports.iml deleted file mode 100644 index d6ebd48..0000000 --- a/.idea/ndkports.iml +++ /dev/null @@ -1,9 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<module type="JAVA_MODULE" version="4"> - <component name="NewModuleRootManager" inherit-compiler-output="true"> - <exclude-output /> - <content url="file://$MODULE_DIR$" /> - <orderEntry type="inheritedJdk" /> - <orderEntry type="sourceFolder" forTests="false" /> - </component> -</module>
\ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 73193ea..899b2f1 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -27,6 +27,34 @@ <option name="issueRegexp" value="\bcs/([^\s]+[\w$])" /> <option name="linkRegexp" value="https://cs.corp.google.com/search/?q=$1" /> </IssueNavigationLink> + <IssueNavigationLink> + <option name="issueRegexp" value="\bb/(\d+)(#\w+)?\b" /> + <option name="linkRegexp" value="https://buganizer.corp.google.com/issues/$1$2" /> + </IssueNavigationLink> + <IssueNavigationLink> + <option name="issueRegexp" value="\b(?:BUG=|FIXED=)(\d+)\b" /> + <option name="linkRegexp" value="https://buganizer.corp.google.com/issues/$1" /> + </IssueNavigationLink> + <IssueNavigationLink> + <option name="issueRegexp" value="\b(?:cl/|cr/|OCL=|DIFFBASE=|ROLLBACK_OF=)(\d+)\b" /> + <option name="linkRegexp" value="https://critique.corp.google.com/$1" /> + </IssueNavigationLink> + <IssueNavigationLink> + <option name="issueRegexp" value="\bomg/(\d+)\b" /> + <option name="linkRegexp" value="https://omg.corp.google.com/$1" /> + </IssueNavigationLink> + <IssueNavigationLink> + <option name="issueRegexp" value="\b(?:go/|goto/)([^,.<>()"\s]+(?:[.,][^,.<>()"\s]+)*)" /> + <option name="linkRegexp" value="https://goto.google.com/$1" /> + </IssueNavigationLink> + <IssueNavigationLink> + <option name="issueRegexp" value="\bcs/([^\s]+[\w$])" /> + <option name="linkRegexp" value="https://cs.corp.google.com/search/?q=$1" /> + </IssueNavigationLink> + <IssueNavigationLink> + <option name="issueRegexp" value="(LINT\.IfChange)|(LINT\.ThenChange)" /> + <option name="linkRegexp" value="https://goto.google.com/ifthisthenthatlint" /> + </IssueNavigationLink> </list> </option> </component> @@ -1,15 +1,20 @@ -FROM gcr.io/cloud-builders/gradle:5.6.2-jdk-8 +FROM gcr.io/cloud-builders/javac:8 -RUN apt-get update -RUN apt-get install -y curl -RUN apt-get install -y ninja-build -RUN apt-get install -y python3-pip +RUN apt-get update && apt-get install -y \ + cmake \ + curl \ + ninja-build \ + python3-pip RUN pip3 install meson RUN curl -o ndk.zip \ - https://dl.google.com/android/repository/android-ndk-r20b-linux-x86_64.zip + https://dl.google.com/android/repository/android-ndk-r21e-linux-x86_64.zip RUN unzip ndk.zip -RUN mv android-ndk-r20b /ndk +RUN mv android-ndk-r21e /ndk +RUN curl -L -o platform-tools.zip \ + https://dl.google.com/android/repository/platform-tools-latest-linux.zip +RUN unzip platform-tools.zip platform-tools/adb +RUN mv platform-tools/adb /usr/bin/adb WORKDIR /src ENTRYPOINT ["./gradlew"] -CMD ["-PndkPath=/ndk", "release"] +CMD ["--stacktrace", "-PndkPath=/ndk", "release"] diff --git a/build.gradle.kts b/build.gradle.kts index a00d626..f217598 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,113 +1,47 @@ -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile +buildscript { + val snapshotSuffix = if (hasProperty("release")) { + // We're still tagging releases as betas until we have more thorough + // test automation. + "-beta-1" + } else { + "-SNAPSHOT" + } -plugins { - kotlin("jvm") version "1.3.72" - application + extra.apply { + set("snapshotSuffix", snapshotSuffix) + } } group = "com.android" -version = "1.0.0-SNAPSHOT" +version = "1.0.0${extra.get("snapshotSuffix")}" + +plugins { + distribution +} repositories { mavenCentral() jcenter() google() - maven(url = "https://dl.bintray.com/s1m0nw1/KtsRunner") -} - -dependencies { - implementation(kotlin("stdlib", "1.3.72")) - implementation(kotlin("reflect", "1.3.72")) - - implementation("com.google.prefab:api:1.0.0") - - implementation("com.github.ajalt:clikt:2.2.0") - implementation("de.swirtz:ktsRunner:0.0.7") - implementation("org.apache.maven:maven-core:3.6.2") - implementation("org.redundent:kotlin-xml-builder:1.5.3") - - testImplementation("org.jetbrains.kotlin:kotlin-test") - testImplementation("org.jetbrains.kotlin:kotlin-test-junit") - testImplementation("org.junit.jupiter:junit-jupiter-api:5.6.0-M1") - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.6.0-M1") -} - -application { - // Define the main class for the application. - mainClassName = "com.android.ndkports.CliKt" -} - -tasks.withType<KotlinCompile> { - kotlinOptions.jvmTarget = "1.8" - kotlinOptions.freeCompilerArgs += listOf( - "-progressive", - "-Xuse-experimental=kotlinx.serialization.ImplicitReflectionSerializer" - ) -} - -val portsBuildDir = buildDir.resolve("ports") - -val allPorts = listOf("openssl", "curl", "jsoncpp") - -// Can be specified in ~/.gradle/gradle.properties: -// -// ndkPath=/path/to/ndk -// -// Or on the command line: -// -// ./gradlew -PndkPath=/path/to/ndk run -val ndkPath: String by project -tasks.named<JavaExec>("run") { - // Order matters since we don't do any dependency sorting, so we can't just - // use the directory list. - args = listOf("--ndk", ndkPath, "-o", portsBuildDir.toString()) + allPorts -} - -for (port in allPorts) { - distributions { - create(port) { - contents { - includeEmptyDirs = false - from(portsBuildDir.resolve(port)) { - include("**/*.aar") - include("**/*.pom") - } - } - } - } - - tasks.named("${port}DistTar") { - dependsOn(":run") - } - - tasks.named("${port}DistZip") { - dependsOn(":run") - } } distributions { - create("all") { + main { contents { - includeEmptyDirs = false - from(portsBuildDir) { - include("**/*.aar") - include("**/*.pom") - } + from("${rootProject.buildDir}/repository") + include("**/*.aar") + include("**/*.pom") } } } -tasks.named("allDistTar") { - dependsOn(":run") -} - -tasks.named("allDistZip") { - dependsOn(":run") +tasks { + distZip { + dependsOn(project.getTasksByName("publish", true)) + } } tasks.register("release") { - dependsOn(":allDistZip") - for (port in allPorts) { - dependsOn(":${port}DistZip") - } + dependsOn(project.getTasksByName("test", true)) + dependsOn(":distZip") } diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts new file mode 100644 index 0000000..4de21c7 --- /dev/null +++ b/buildSrc/build.gradle.kts @@ -0,0 +1,62 @@ +val kotlinVersion = "1.4.20" + +plugins { + id("org.jetbrains.kotlin.jvm") version "1.4.20" + id("java-gradle-plugin") + id("maven-publish") +} + +group = "com.android.ndkports" +version = "1.0.0-SNAPSHOT" + +repositories { + mavenCentral() + jcenter() + google() +} + +dependencies { + implementation(kotlin("stdlib", kotlinVersion)) + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.1") + + implementation("com.google.prefab:api:1.1.2") + implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.0.0") + implementation("org.redundent:kotlin-xml-builder:1.6.1") + + testImplementation(kotlin("test", kotlinVersion)) + testImplementation(kotlin("test-junit", kotlinVersion)) + testImplementation("org.junit.jupiter:junit-jupiter-api:5.7.0") + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.7.0") +} + +tasks { + compileJava { + @Suppress("UnstableApiUsage") + options.release.set(8) + } + + compileKotlin { + kotlinOptions.jvmTarget = "1.8" + } + + compileTestKotlin { + kotlinOptions.jvmTarget = "1.8" + } +} + +gradlePlugin { + plugins { + create("ndkports") { + id = "com.android.ndkports.NdkPorts" + implementationClass = "com.android.ndkports.NdkPortsPlugin" + } + } +} + +publishing { + repositories { + maven { + url = uri("${rootProject.buildDir}/repository") + } + } +} diff --git a/buildSrc/settings.gradle.kts b/buildSrc/settings.gradle.kts new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/buildSrc/settings.gradle.kts diff --git a/src/main/kotlin/com/android/ndkports/Result.kt b/buildSrc/src/main/kotlin/com/android/ndkports/Abi.kt index 21c203e..155c59c 100644 --- a/src/main/kotlin/com/android/ndkports/Result.kt +++ b/buildSrc/src/main/kotlin/com/android/ndkports/Abi.kt @@ -16,21 +16,23 @@ package com.android.ndkports -sealed class Result<out T, out E> { - data class Ok<T>(val value: T) : Result<T, Nothing>() - data class Error<E>(val error: E) : Result<Nothing, E>() +import java.util.Collections.max - inline fun onSuccess(block: (T) -> Unit): Result<T, E> { - if (this is Ok<T>) { - block(value) - } - return this - } +enum class Abi( + val archName: String, + val abiName: String, + val triple: String, + val minSupportedVersion: Int +) { + Arm("arm", "armeabi-v7a", "arm-linux-androideabi", 16), + Arm64("arm64", "arm64-v8a", "aarch64-linux-android", 21), + X86("x86", "x86", "i686-linux-android", 16), + X86_64("x86_64", "x86_64", "x86_64-linux-android", 21); + + fun adjustMinSdkVersion(minSdkVersion: Int) = + max(listOf(minSdkVersion, minSupportedVersion)) - inline fun onFailure(block: (E) -> Unit): Result<T, E> { - if (this is Error<E>) { - block(error) - } - return this + companion object { + fun fromAbiName(name: String) = values().find { it.abiName == name } } }
\ No newline at end of file diff --git a/buildSrc/src/main/kotlin/com/android/ndkports/AdHocPortTask.kt b/buildSrc/src/main/kotlin/com/android/ndkports/AdHocPortTask.kt new file mode 100644 index 0000000..4c9fcbc --- /dev/null +++ b/buildSrc/src/main/kotlin/com/android/ndkports/AdHocPortTask.kt @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2019 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. + */ + +package com.android.ndkports + +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Input +import java.io.File + +open class RunBuilder { + val cmd = mutableListOf<String>() + fun arg(arg: String) = cmd.add(arg) + fun args(vararg args: String) = cmd.addAll(args) + + val env = mutableMapOf<String, String>() + fun env(key: String, value: String) = env.set(key, value) +} + +class AdHocBuilder( + val sourceDirectory: File, + val buildDirectory: File, + val installDirectory: File, + val toolchain: Toolchain, + val sysroot: File, + val ncpus: Int, +) { + val runs = mutableListOf<RunBuilder>() + fun run(block: RunBuilder.() -> Unit) { + runs.add(RunBuilder().apply { block() }) + } +} + +abstract class AdHocPortTask : PortTask() { + @get:Input + abstract val builder: Property<AdHocBuilder.() -> Unit> + + fun builder(block: AdHocBuilder.() -> Unit) = builder.set(block) + + override fun buildForAbi( + toolchain: Toolchain, + workingDirectory: File, + buildDirectory: File, + installDirectory: File + ) { + buildDirectory.mkdirs() + + val builderBlock = builder.get() + val builder = AdHocBuilder( + sourceDirectory.get().asFile, + buildDirectory, + installDirectory, + toolchain, + prefabGenerated.get().asFile, + ncpus, + ) + builder.builderBlock() + + for (run in builder.runs) { + executeSubprocess( + run.cmd, buildDirectory, additionalEnvironment = run.env + ) + } + } +}
\ No newline at end of file diff --git a/src/main/kotlin/com/android/ndkports/AutoconfPort.kt b/buildSrc/src/main/kotlin/com/android/ndkports/AutoconfPortTask.kt index 1c009cb..e288e33 100644 --- a/src/main/kotlin/com/android/ndkports/AutoconfPort.kt +++ b/buildSrc/src/main/kotlin/com/android/ndkports/AutoconfPortTask.kt @@ -16,33 +16,39 @@ package com.android.ndkports +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Input import java.io.File -abstract class AutoconfPort : Port() { - open fun configureArgs( - workingDirectory: File, - toolchain: Toolchain - ): List<String> = emptyList() +class AutoconfBuilder(val toolchain: Toolchain, val sysroot: File) : + RunBuilder() - open fun configureEnv( - workingDirectory: File, - toolchain: Toolchain - ): Map<String, String> = emptyMap() +abstract class AutoconfPortTask : PortTask() { + @get:Input + abstract val autoconf: Property<AutoconfBuilder.() -> Unit> - override fun configure( + fun autoconf(block: AutoconfBuilder.() -> Unit) = autoconf.set(block) + + override fun buildForAbi( toolchain: Toolchain, - sourceDirectory: File, + workingDirectory: File, buildDirectory: File, - installDirectory: File, - workingDirectory: File - ): Result<Unit, String> { + installDirectory: File + ) { buildDirectory.mkdirs() - return executeProcessStep( - listOf( - "${sourceDirectory.absolutePath}/configure", - "--host=${toolchain.binutilsTriple}", - "--prefix=${installDirectory.absolutePath}" - ) + configureArgs(workingDirectory, toolchain), + + val autoconfBlock = autoconf.get() + val builder = AutoconfBuilder( + toolchain, + prefabGenerated.get().asFile.resolve(toolchain.abi.triple) + ) + builder.autoconfBlock() + + executeSubprocess(listOf( + "${sourceDirectory.get().asFile.absolutePath}/configure", + "--host=${toolchain.binutilsTriple}", + "--prefix=${installDirectory.absolutePath}" + ) + builder.cmd, buildDirectory, additionalEnvironment = mutableMapOf( "AR" to toolchain.ar.absolutePath, @@ -51,24 +57,12 @@ abstract class AutoconfPort : Port() { "RANLIB" to toolchain.ranlib.absolutePath, "STRIP" to toolchain.strip.absolutePath, "PATH" to "${toolchain.binDir}:${System.getenv("PATH")}" - ).apply { putAll(configureEnv(workingDirectory, toolchain)) } - ) - } + ).apply { putAll(builder.env) }) - override fun build( - toolchain: Toolchain, - buildDirectory: File - ): Result<Unit, String> = - executeProcessStep( - listOf("make", "-j$ncpus"), buildDirectory - ) + executeSubprocess(listOf("make", "-j$ncpus"), buildDirectory) - override fun install( - toolchain: Toolchain, - buildDirectory: File, - installDirectory: File - ): Result<Unit, String> = - executeProcessStep( + executeSubprocess( listOf("make", "-j$ncpus", "install"), buildDirectory ) + } }
\ No newline at end of file diff --git a/src/main/kotlin/com/android/ndkports/CMakeCompatibleVersion.kt b/buildSrc/src/main/kotlin/com/android/ndkports/CMakeCompatibleVersion.kt index 6d3ee45..ba58ccb 100644 --- a/src/main/kotlin/com/android/ndkports/CMakeCompatibleVersion.kt +++ b/buildSrc/src/main/kotlin/com/android/ndkports/CMakeCompatibleVersion.kt @@ -16,7 +16,7 @@ package com.android.ndkports -import java.io.File +import java.io.Serializable /** * A version number that is compatible with CMake's package version format. @@ -31,7 +31,7 @@ data class CMakeCompatibleVersion( val minor: Int?, val patch: Int?, val tweak: Int? -) { +) : Serializable { init { if (tweak != null) { require(patch != null) diff --git a/buildSrc/src/main/kotlin/com/android/ndkports/CMakePortTask.kt b/buildSrc/src/main/kotlin/com/android/ndkports/CMakePortTask.kt new file mode 100644 index 0000000..dba2017 --- /dev/null +++ b/buildSrc/src/main/kotlin/com/android/ndkports/CMakePortTask.kt @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2021 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. + */ + +package com.android.ndkports + +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Input +import java.io.File + +class CMakeBuilder(val toolchain: Toolchain, val sysroot: File) : + RunBuilder() + +abstract class CMakePortTask : PortTask() { + @get:Input + abstract val cmake: Property<CMakeBuilder.() -> Unit> + + fun cmake(block: CMakeBuilder.() -> Unit) = cmake.set(block) + + override fun buildForAbi( + toolchain: Toolchain, + workingDirectory: File, + buildDirectory: File, + installDirectory: File + ) { + configure(toolchain, buildDirectory, installDirectory) + build(buildDirectory) + install(buildDirectory) + } + + private fun configure( + toolchain: Toolchain, buildDirectory: File, installDirectory: File + ) { + val cmakeBlock = cmake.get() + val builder = CMakeBuilder( + toolchain, + prefabGenerated.get().asFile.resolve(toolchain.abi.triple) + ) + builder.cmakeBlock() + + val toolchainFile = + toolchain.ndk.path.resolve("build/cmake/android.toolchain.cmake") + + buildDirectory.mkdirs() + executeSubprocess( + listOf( + "cmake", + "-DCMAKE_TOOLCHAIN_FILE=${toolchainFile.absolutePath}", + "-DCMAKE_BUILD_TYPE=RelWithDebInfo", + "-DCMAKE_INSTALL_PREFIX=${installDirectory.absolutePath}", + "-DANDROID_ABI=${toolchain.abi.abiName}", + "-DANDROID_API_LEVEL=${toolchain.api}", + "-GNinja", + sourceDirectory.get().asFile.absolutePath, + ) + builder.cmd, buildDirectory, builder.env + ) + } + + private fun build(buildDirectory: File) = + executeSubprocess(listOf("ninja", "-v"), buildDirectory) + + private fun install(buildDirectory: File) = + executeSubprocess(listOf("ninja", "-v", "install"), buildDirectory) +}
\ No newline at end of file diff --git a/buildSrc/src/main/kotlin/com/android/ndkports/Devices.kt b/buildSrc/src/main/kotlin/com/android/ndkports/Devices.kt new file mode 100644 index 0000000..3ed1d9a --- /dev/null +++ b/buildSrc/src/main/kotlin/com/android/ndkports/Devices.kt @@ -0,0 +1,89 @@ +package com.android.ndkports + +import java.io.File + +data class AdbException(val args: Iterable<String>, val output: String) : + RuntimeException("${formatCmd(args)}:\n$output") { + val cmd: String by lazy { formatCmd(args) } + + companion object { + fun formatCmd(args: Iterable<String>) = args.joinToString(" ") + } +} + +private fun adb(args: Iterable<String>, serial: String? = null): String { + val adbCmd = if (serial == null) { + listOf("adb") + } else { + listOf("adb", "-s", serial) + } + val result = ProcessBuilder(adbCmd + args).redirectErrorStream(true).start() + val output = result.inputStream.bufferedReader().use { it.readText() } + if (result.waitFor() != 0) { + throw AdbException(args, output) + } + return output +} + +data class Device(val serial: String) { + private val abis: List<Abi> by lazy { + val abiProps = listOf( + "ro.product.cpu.abi", + "ro.product.cpu.abi2", + "ro.product.cpu.abilist", + ) + val abiSet = mutableSetOf<Abi>() + for (abiProp in abiProps) { + for (abiName in getProp(abiProp).trim().split(",")) { + Abi.fromAbiName(abiName)?.let { abiSet.add(it) } + } + } + abiSet.toList().sortedBy { it.abiName } + } + + private val version: Int by lazy { + getProp("ro.build.version.sdk").trim().toInt() + } + + fun compatibleWith(abi: Abi, minSdkVersion: Int) = + abi in abis && minSdkVersion <= version + + fun push(src: File, dest: File) = + run(listOf("push", src.toString(), dest.toString())) + + fun shell(cmd: Iterable<String>) = run(listOf("shell") + cmd) + + private fun getProp(name: String): String = shell(listOf("getprop", name)) + + private fun run(args: Iterable<String>): String = adb(args, serial) +} + +class DeviceFleet { + private fun lineHasUsableDevice(line: String): Boolean { + if (line.isBlank()) { + return false + } + if (line == "List of devices attached") { + return false + } + if (line.contains("offline")) { + return false + } + if (line.contains("unauthorized")) { + return false + } + if (line.startsWith("* daemon")) { + return false + } + return true + } + + private val devices: List<Device> by lazy { + adb(listOf("devices")).lines().filter { lineHasUsableDevice(it) }.map { + Device(it.split("\\s".toRegex()).first()) + } + } + + fun findDeviceFor(abi: Abi, minSdkVersion: Int): Device? = + devices.find { it.compatibleWith(abi, minSdkVersion) } +}
\ No newline at end of file diff --git a/src/main/kotlin/com/android/ndkports/MesonPort.kt b/buildSrc/src/main/kotlin/com/android/ndkports/MesonPortTask.kt index 8c1f706..6be9b9e 100644 --- a/src/main/kotlin/com/android/ndkports/MesonPort.kt +++ b/buildSrc/src/main/kotlin/com/android/ndkports/MesonPortTask.kt @@ -16,24 +16,41 @@ package com.android.ndkports +import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Input import java.io.File +import javax.inject.Inject -abstract class MesonPort : Port() { +@Suppress("UnstableApiUsage") +abstract class MesonPortTask @Inject constructor(objects: ObjectFactory) : + PortTask() { enum class DefaultLibraryType(val argument: String) { - Both("both"), - Shared("shared"), - Static("static") + Both("both"), Shared("shared"), Static("static") } - open val defaultLibraryType: DefaultLibraryType = DefaultLibraryType.Shared + @get:Input + val defaultLibraryType: Property<DefaultLibraryType> = + objects.property(DefaultLibraryType::class.java) + .convention(DefaultLibraryType.Shared) - override fun configure( + override fun buildForAbi( toolchain: Toolchain, - sourceDirectory: File, + workingDirectory: File, buildDirectory: File, - installDirectory: File, - workingDirectory: File - ): Result<Unit, String> { + installDirectory: File + ) { + configure(toolchain, workingDirectory, buildDirectory, installDirectory) + build(buildDirectory) + install(buildDirectory) + } + + private fun configure( + toolchain: Toolchain, + workingDirectory: File, + buildDirectory: File, + installDirectory: File + ) { val cpuFamily = when (toolchain.abi) { Abi.Arm -> "arm" Abi.Arm64 -> "aarch64" @@ -49,7 +66,8 @@ abstract class MesonPort : Port() { } val crossFile = workingDirectory.resolve("cross_file.txt").apply { - writeText(""" + writeText( + """ [binaries] ar = '${toolchain.ar}' c = '${toolchain.clang}' @@ -61,10 +79,11 @@ abstract class MesonPort : Port() { cpu_family = '$cpuFamily' cpu = '$cpu' endian = 'little' - """.trimIndent()) + """.trimIndent() + ) } - return executeProcessStep( + executeSubprocess( listOf( "meson", "--cross-file", @@ -74,23 +93,16 @@ abstract class MesonPort : Port() { "--prefix", installDirectory.absolutePath, "--default-library", - defaultLibraryType.argument, - sourceDirectory.absolutePath, + defaultLibraryType.get().argument, + sourceDirectory.get().asFile.absolutePath, buildDirectory.absolutePath ), workingDirectory ) } - override fun build( - toolchain: Toolchain, - buildDirectory: File - ): Result<Unit, String> = - executeProcessStep(listOf("ninja", "-v"), buildDirectory) + private fun build(buildDirectory: File) = + executeSubprocess(listOf("ninja", "-v"), buildDirectory) - override fun install( - toolchain: Toolchain, - buildDirectory: File, - installDirectory: File - ): Result<Unit, String> = - executeProcessStep(listOf("ninja", "-v", "install"), buildDirectory) + private fun install(buildDirectory: File) = + executeSubprocess(listOf("ninja", "-v", "install"), buildDirectory) }
\ No newline at end of file diff --git a/src/main/kotlin/com/android/ndkports/Ndk.kt b/buildSrc/src/main/kotlin/com/android/ndkports/Ndk.kt index af66cb5..7ccd255 100644 --- a/src/main/kotlin/com/android/ndkports/Ndk.kt +++ b/buildSrc/src/main/kotlin/com/android/ndkports/Ndk.kt @@ -17,7 +17,6 @@ package com.android.ndkports import java.io.File -import java.lang.RuntimeException class Ndk(val path: File) { val version = NdkVersion.fromNdk(path) @@ -36,4 +35,5 @@ class Ndk(val path: File) { private val toolchainDirectory = llvmBaseDir.resolve(hostTag) val toolchainBinDirectory = toolchainDirectory.resolve("bin") + val sysrootDirectory = toolchainDirectory.resolve("sysroot") }
\ No newline at end of file diff --git a/buildSrc/src/main/kotlin/com/android/ndkports/NdkPortsPlugin.kt b/buildSrc/src/main/kotlin/com/android/ndkports/NdkPortsPlugin.kt new file mode 100644 index 0000000..ff629c0 --- /dev/null +++ b/buildSrc/src/main/kotlin/com/android/ndkports/NdkPortsPlugin.kt @@ -0,0 +1,179 @@ +package com.android.ndkports + +import org.gradle.api.InvalidUserDataException +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.artifacts.Configuration +import org.gradle.api.attributes.Attribute +import org.gradle.api.component.SoftwareComponentFactory +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.model.ObjectFactory +import org.gradle.api.plugins.BasePlugin +import org.gradle.api.provider.Property +import org.gradle.api.provider.Provider +import org.gradle.api.tasks.bundling.Zip +import javax.inject.Inject + +abstract class NdkPortsExtension { + abstract val source: RegularFileProperty + + abstract val ndkPath: DirectoryProperty + + abstract val minSdkVersion: Property<Int> +} + +class NdkPortsPluginImpl( + private val project: Project, + private val softwareComponentFactory: SoftwareComponentFactory, + objects: ObjectFactory, +) { + private val topBuildDir = project.buildDir.resolve("port") + + private val extension = + project.extensions.create("ndkPorts", NdkPortsExtension::class.java) + + private var portTaskAdded: Boolean = false + private val portTask = objects.property(PortTask::class.java) + private lateinit var prefabTask: Provider<PrefabTask> + private lateinit var extractTask: Provider<SourceExtractTask> + private lateinit var packageTask: Provider<PackageBuilderTask> + private lateinit var aarTask: Provider<Zip> + + private lateinit var implementation: Configuration + private lateinit var exportedAars: Configuration + private lateinit var consumedAars: Configuration + + private val artifactType = Attribute.of("artifactType", String::class.java) + + private fun createConfigurations() { + implementation = project.configurations.create("implementation") { + it.isCanBeResolved = false + it.isCanBeConsumed = false + } + + exportedAars = project.configurations.create("exportedAars") { + it.isCanBeResolved = false + it.isCanBeConsumed = true + it.extendsFrom(implementation) + it.attributes { attributes -> + with(attributes) { + attribute(artifactType, "aar") + } + } + } + + consumedAars = project.configurations.create("consumedAars") { + it.isCanBeResolved = true + it.isCanBeConsumed = false + it.extendsFrom(implementation) + it.attributes { attributes -> + with(attributes) { + attribute(artifactType, "aar") + } + } + } + } + + private fun createTasks() { + prefabTask = project.tasks.register("prefab", PrefabTask::class.java) { + with(it) { + aars = consumedAars.incoming.artifacts.artifactFiles + outputDirectory.set(topBuildDir.resolve("dependencies")) + ndkPath.set(extension.ndkPath) + minSdkVersion.set(extension.minSdkVersion) + } + } + + extractTask = project.tasks.register( + "extractSrc", SourceExtractTask::class.java + ) { + with(it) { + source.set(extension.source) + outDir.set(topBuildDir.resolve("src")) + } + } + + packageTask = project.tasks.register( + "prefabPackage", PackageBuilderTask::class.java + ) { + if (!portTask.isPresent) { + throw InvalidUserDataException( + "The ndkports plugin was applied but no port task was " + + "registered. A task deriving from NdkPortsTask " + + "must be registered." + ) + } + with(it) { + sourceDirectory.set(extractTask.get().outDir) + outDir.set(topBuildDir) + ndkPath.set(extension.ndkPath) + installDirectory.set(portTask.get().installDir) + minSdkVersion.set(extension.minSdkVersion) + } + } + + aarTask = project.tasks.register("packageAar", Zip::class.java) { + it.from(packageTask.get().intermediatesDirectory) + it.archiveExtension.set("aar") + it.dependsOn(packageTask) + } + + project.artifacts.add(exportedAars.name, aarTask) + + val portTasks = project.tasks.withType(PortTask::class.java) + portTasks.whenTaskAdded { portTask -> + if (portTaskAdded) { + throw InvalidUserDataException( + "Cannot define multiple port tasks for a single module" + ) + } + portTaskAdded = true + this.portTask.set(portTask) + + with (portTask) { + sourceDirectory.set(extractTask.get().outDir) + ndkPath.set(extension.ndkPath) + buildDir.set(topBuildDir) + minSdkVersion.set(extension.minSdkVersion) + prefabGenerated.set(prefabTask.get().generatedDirectory) + } + } + + val testTasks = + project.tasks.withType(AndroidExecutableTestTask::class.java) + testTasks.whenTaskAdded { testTask -> + with (testTask) { + dependsOn(aarTask) + minSdkVersion.set(extension.minSdkVersion) + ndkPath.set(extension.ndkPath) + } + project.tasks.getByName("check").dependsOn(testTask) + } + } + + private fun createComponents() { + val adhocComponent = softwareComponentFactory.adhoc("prefab") + project.components.add(adhocComponent) + adhocComponent.addVariantsFromConfiguration(exportedAars) { + it.mapToMavenScope("runtime") + } + } + + fun apply() { + project.pluginManager.apply(BasePlugin::class.java) + createConfigurations() + createTasks() + createComponents() + } +} + +@Suppress("UnstableApiUsage", "Unused") +class NdkPortsPlugin @Inject constructor( + private val objects: ObjectFactory, + private val softwareComponentFactory: SoftwareComponentFactory, +) : Plugin<Project> { + override fun apply(project: Project) { + NdkPortsPluginImpl(project, softwareComponentFactory, objects).apply() + } +}
\ No newline at end of file diff --git a/src/main/kotlin/com/android/ndkports/NdkVersion.kt b/buildSrc/src/main/kotlin/com/android/ndkports/NdkVersion.kt index fce1304..fce1304 100644 --- a/src/main/kotlin/com/android/ndkports/NdkVersion.kt +++ b/buildSrc/src/main/kotlin/com/android/ndkports/NdkVersion.kt diff --git a/buildSrc/src/main/kotlin/com/android/ndkports/PackageBuilderTask.kt b/buildSrc/src/main/kotlin/com/android/ndkports/PackageBuilderTask.kt new file mode 100644 index 0000000..a7c408d --- /dev/null +++ b/buildSrc/src/main/kotlin/com/android/ndkports/PackageBuilderTask.kt @@ -0,0 +1,134 @@ +package com.android.ndkports + +import org.gradle.api.DefaultTask +import org.gradle.api.NamedDomainObjectContainer +import org.gradle.api.Project +import org.gradle.api.file.Directory +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.ListProperty +import org.gradle.api.provider.MapProperty +import org.gradle.api.provider.Property +import org.gradle.api.provider.Provider +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.InputDirectory +import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.Nested +import org.gradle.api.tasks.OutputDirectory +import org.gradle.api.tasks.TaskAction +import javax.inject.Inject + +abstract class ModuleProperty @Inject constructor( + objectFactory: ObjectFactory, + @get:Input val name: String, +) { + @Suppress("UnstableApiUsage") + @get:Input + val static: Property<Boolean> = + objectFactory.property(Boolean::class.java).convention(false) + + @Suppress("UnstableApiUsage") + @get:Input + val headerOnly: Property<Boolean> = + objectFactory.property(Boolean::class.java).convention(false) + + @Suppress("UnstableApiUsage") + @get:Input + val includesPerAbi: Property<Boolean> = + objectFactory.property(Boolean::class.java).convention(false) + + @Suppress("UnstableApiUsage") + @get:Input + val dependencies: ListProperty<String> = + objectFactory.listProperty(String::class.java).convention(emptyList()) +} + +abstract class PackageBuilderTask @Inject constructor( + objectFactory: ObjectFactory, +) : DefaultTask() { + /** + * The name of the port. Will be used as the package name in prefab.json. + */ + @Suppress("UnstableApiUsage") + @get:Input + val packageName: Property<String> = + objectFactory.property(String::class.java).convention(project.name) + + /** + * The version to encode in the prefab.json. + * + * This version must be compatible with CMake's `find_package` for + * config-style packages. This means that it must be one to four decimal + * separated integers. No other format is allowed. + * + * If not set, the default is [Project.getVersion] as interpreted by + * [CMakeCompatibleVersion.parse]. + * + * For example, OpenSSL 1.1.1g will set this value to + * `CMakeCompatibleVersion(1, 1, 1, 7)`. + */ + @get:Input + abstract val version: Property<CMakeCompatibleVersion> + + @get:Input + abstract val minSdkVersion: Property<Int> + + @get:Nested + abstract val modules: NamedDomainObjectContainer<ModuleProperty> + + @Suppress("UnstableApiUsage") + @get:Input + val licensePath: Property<String> = + objectFactory.property(String::class.java).convention("LICENSE") + + @Suppress("UnstableApiUsage") + @get:Input + abstract val dependencies: MapProperty<String, String> + + @get:InputDirectory + abstract val sourceDirectory: DirectoryProperty + + @get:InputDirectory + abstract val installDirectory: DirectoryProperty + + @get:Internal + abstract val outDir: DirectoryProperty + + @get:OutputDirectory + val intermediatesDirectory: Provider<Directory> + get() = outDir.dir("aar") + + @get:InputDirectory + abstract val ndkPath: DirectoryProperty + + private val ndk: Ndk + get() = Ndk(ndkPath.asFile.get()) + + @TaskAction + fun run() { + val modules = modules.asMap.values.map { + ModuleDescription( + it.name, + it.static.get(), + it.headerOnly.get(), + it.includesPerAbi.get(), + it.dependencies.get() + ) + } + PrefabPackageBuilder( + PackageData( + packageName.get(), + project.version as String, + version.get(), + minSdkVersion.get(), + licensePath.get(), + modules, + dependencies.get(), + ), + intermediatesDirectory.get().asFile, + installDirectory.get().asFile, + sourceDirectory.get().asFile, + ndk, + ).build() + } +}
\ No newline at end of file diff --git a/buildSrc/src/main/kotlin/com/android/ndkports/PortTask.kt b/buildSrc/src/main/kotlin/com/android/ndkports/PortTask.kt new file mode 100644 index 0000000..223afb0 --- /dev/null +++ b/buildSrc/src/main/kotlin/com/android/ndkports/PortTask.kt @@ -0,0 +1,94 @@ +package com.android.ndkports + +import org.gradle.api.DefaultTask +import org.gradle.api.file.Directory +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.provider.Property +import org.gradle.api.provider.Provider +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.InputDirectory +import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.OutputDirectory +import org.gradle.api.tasks.TaskAction +import java.io.File + +@Suppress("UnstableApiUsage") +abstract class PortTask : DefaultTask() { + + @get:InputDirectory + abstract val sourceDirectory: DirectoryProperty + + @get:OutputDirectory + abstract val buildDir: DirectoryProperty + + @get:OutputDirectory + val installDir: Provider<Directory> + get() = buildDir.dir("install") + + @get:InputDirectory + abstract val prefabGenerated: DirectoryProperty + + @get:Input + abstract val minSdkVersion: Property<Int> + + @get:InputDirectory + abstract val ndkPath: DirectoryProperty + + private val ndk: Ndk + get() = Ndk(ndkPath.asFile.get()) + + /** + * The number of CPUs available for building. + * + * May be passed to the build system if required. + */ + @Internal + protected val ncpus = Runtime.getRuntime().availableProcessors() + + protected fun executeSubprocess( + args: List<String>, + workingDirectory: File, + additionalEnvironment: Map<String, String>? = null + ) { + val pb = ProcessBuilder(args).redirectErrorStream(true) + .directory(workingDirectory) + + if (additionalEnvironment != null) { + pb.environment().putAll(additionalEnvironment) + } + + val result = pb.start() + val output = result.inputStream.bufferedReader().use { it.readText() } + if (result.waitFor() != 0) { + throw RuntimeException("Subprocess failed with:\n$output") + } + } + + @Suppress("MemberVisibilityCanBePrivate") + fun buildDirectoryFor(abi: Abi): File = + buildDir.asFile.get().resolve("build/$abi") + + @Suppress("MemberVisibilityCanBePrivate") + fun installDirectoryFor(abi: Abi): File = + installDir.get().asFile.resolve("$abi") + + @TaskAction + fun run() { + for (abi in Abi.values()) { + val api = abi.adjustMinSdkVersion(minSdkVersion.get()) + buildForAbi( + Toolchain(ndk, abi, api), + buildDir.asFile.get(), + buildDirectory = buildDirectoryFor(abi), + installDirectory = installDirectoryFor(abi), + ) + } + } + + abstract fun buildForAbi( + toolchain: Toolchain, + workingDirectory: File, + buildDirectory: File, + installDirectory: File + ) +}
\ No newline at end of file diff --git a/buildSrc/src/main/kotlin/com/android/ndkports/PrefabPackageBuilder.kt b/buildSrc/src/main/kotlin/com/android/ndkports/PrefabPackageBuilder.kt new file mode 100644 index 0000000..27be792 --- /dev/null +++ b/buildSrc/src/main/kotlin/com/android/ndkports/PrefabPackageBuilder.kt @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2019 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. + */ + +package com.android.ndkports + +import com.google.prefab.api.AndroidAbiMetadata +import com.google.prefab.api.ModuleMetadataV1 +import com.google.prefab.api.PackageMetadataV1 +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json +import org.redundent.kotlin.xml.xml +import java.io.File +import java.io.Serializable + +data class PackageData( + val name: String, + val mavenVersion: String, + val prefabVersion: CMakeCompatibleVersion, + val minSdkVersion: Int, + val licensePath: String, + val modules: List<ModuleDescription>, + val dependencies: Map<String, String>, +) + +/** + * A module exported by the package. + * + * As currently implemented by ndkports, one module is exactly one library. + * Prefab supports header-only libraries, but ndkports does not support these + * yet. + * + * Static libraries are not currently supported by ndkports. + * + * @property[name] The name of the module. Note that currently the name of the + * installed library file must be exactly `lib$name.so`. + * @property[includesPerAbi] Set to true if a different set of headers should be + * exposed per-ABI. Not currently implemented. + * @property[dependencies] A list of other modules required by this module, in + * the format described by https://google.github.io/prefab/. + */ +data class ModuleDescription( + val name: String, + val static: Boolean, + val headerOnly: Boolean, + val includesPerAbi: Boolean, + val dependencies: List<String>, +) : Serializable + +class PrefabPackageBuilder( + private val packageData: PackageData, + private val packageDirectory: File, + private val directory: File, + private val sourceDirectory: File, + private val ndk: Ndk, +) { + private val prefabDirectory = packageDirectory.resolve("prefab") + private val modulesDirectory = prefabDirectory.resolve("modules") + + // TODO: Get from gradle. + private val packageName = "com.android.ndk.thirdparty.${packageData.name}" + + private fun preparePackageDirectory() { + if (packageDirectory.exists()) { + packageDirectory.deleteRecursively() + } + modulesDirectory.mkdirs() + } + + private fun makePackageMetadata() { + prefabDirectory.resolve("prefab.json").writeText( + Json.encodeToString( + PackageMetadataV1( + packageData.name, + schemaVersion = 1, + dependencies = packageData.dependencies.keys.toList(), + version = packageData.prefabVersion.toString() + ) + ) + ) + } + + private fun makeModuleMetadata(module: ModuleDescription, moduleDirectory: File) { + moduleDirectory.resolve("module.json").writeText( + Json.encodeToString( + ModuleMetadataV1( + exportLibraries = module.dependencies + ) + ) + ) + } + + private fun installLibForAbi(module: ModuleDescription, abi: Abi, libsDir: File) { + val extension = if (module.static) "a" else "so" + val libName = "lib${module.name}.${extension}" + val installDirectory = libsDir.resolve("android.${abi.abiName}").apply { + mkdirs() + } + + directory.resolve("$abi/lib/$libName") + .copyTo(installDirectory.resolve(libName)) + + installDirectory.resolve("abi.json").writeText( + Json.encodeToString( + AndroidAbiMetadata( + abi = abi.abiName, + api = abi.adjustMinSdkVersion(packageData.minSdkVersion), + ndk = ndk.version.major, + stl = "c++_shared" + ) + ) + ) + } + + private fun installLicense() { + val src = sourceDirectory.resolve(packageData.licensePath) + val dest = packageDirectory.resolve("META-INF") + .resolve(File(packageData.licensePath).name) + src.copyTo(dest) + } + + private fun createAndroidManifest() { + packageDirectory.resolve("AndroidManifest.xml") + .writeText(xml("manifest") { + attributes( + "xmlns:android" to "http://schemas.android.com/apk/res/android", + "package" to packageName, + "android:versionCode" to 1, + "android:versionName" to "1.0" + ) + + "uses-sdk" { + attributes( + "android:minSdkVersion" to packageData.minSdkVersion, + "android:targetSdkVersion" to 29 + ) + } + }.toString()) + } + + fun build() { + preparePackageDirectory() + makePackageMetadata() + for (module in packageData.modules) { + val moduleDirectory = modulesDirectory.resolve(module.name).apply { + mkdirs() + } + + makeModuleMetadata(module, moduleDirectory) + + if (module.includesPerAbi) { + TODO() + } else { + // TODO: Check that headers are actually identical across ABIs. + directory.resolve("${Abi.Arm}/include") + .copyRecursively(moduleDirectory.resolve("include")) + } + + if (!module.headerOnly) { + val libsDir = moduleDirectory.resolve("libs").apply { mkdirs() } + for (abi in Abi.values()) { + installLibForAbi(module, abi, libsDir) + } + } + } + + installLicense() + + createAndroidManifest() + } +} diff --git a/buildSrc/src/main/kotlin/com/android/ndkports/PrefabSysrootPlugin.kt b/buildSrc/src/main/kotlin/com/android/ndkports/PrefabSysrootPlugin.kt new file mode 100644 index 0000000..2828764 --- /dev/null +++ b/buildSrc/src/main/kotlin/com/android/ndkports/PrefabSysrootPlugin.kt @@ -0,0 +1,60 @@ +package com.android.ndkports + +import com.google.prefab.api.BuildSystemInterface +import com.google.prefab.api.Module +import com.google.prefab.api.Package +import com.google.prefab.api.PlatformDataInterface +import java.io.File + +class PrefabSysrootPlugin( + override val outputDirectory: File, override val packages: List<Package> +) : BuildSystemInterface { + + override fun generate(requirements: Collection<PlatformDataInterface>) { + prepareOutputDirectory(outputDirectory) + + for (pkg in packages) { + for (module in pkg.modules) { + for (requirement in requirements) { + installModule(module, requirement) + } + } + } + } + + private fun installModule( + module: Module, requirement: PlatformDataInterface + ) { + val installDir = outputDirectory.resolve(requirement.targetTriple) + val includeDir = installDir.resolve("include") + + if (module.isHeaderOnly) { + installHeaders(module.includePath.toFile(), includeDir) + return + } + + val library = module.getLibraryFor(requirement) + installHeaders(module.includePath.toFile(), includeDir) + val libDir = installDir.resolve("lib").apply { + mkdirs() + } + library.path.toFile().apply { copyTo(libDir.resolve(name)) } + } + + private fun installHeaders(src: File, dest: File) { + src.copyRecursively(dest) { file, exception -> + if (exception !is FileAlreadyExistsException) { + throw exception + } + + if (!file.readBytes().contentEquals(exception.file.readBytes())) { + val path = file.relativeTo(dest) + throw RuntimeException( + "Found duplicate headers with non-equal contents: $path" + ) + } + + OnErrorAction.SKIP + } + } +}
\ No newline at end of file diff --git a/buildSrc/src/main/kotlin/com/android/ndkports/PrefabTask.kt b/buildSrc/src/main/kotlin/com/android/ndkports/PrefabTask.kt new file mode 100644 index 0000000..d58f1fa --- /dev/null +++ b/buildSrc/src/main/kotlin/com/android/ndkports/PrefabTask.kt @@ -0,0 +1,95 @@ +package com.android.ndkports + +import com.google.prefab.api.Android +import com.google.prefab.api.BuildSystemInterface +import com.google.prefab.api.Package +import org.gradle.api.DefaultTask +import org.gradle.api.file.Directory +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.FileCollection +import org.gradle.api.provider.Property +import org.gradle.api.provider.Provider +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.InputDirectory +import org.gradle.api.tasks.InputFiles +import org.gradle.api.tasks.Optional +import org.gradle.api.tasks.OutputDirectory +import org.gradle.api.tasks.TaskAction +import java.io.File +import java.util.zip.ZipFile + +abstract class PrefabTask : DefaultTask() { + @InputFiles + lateinit var aars: FileCollection + + @get:OutputDirectory + abstract val outputDirectory: DirectoryProperty + + @get:OutputDirectory + val generatedDirectory: Provider<Directory> + get() = outputDirectory.dir("generated") + + @get:Optional + @get:Input + abstract val generator: Property<Class<out BuildSystemInterface>> + + @get:InputDirectory + abstract val ndkPath: DirectoryProperty + + @get:Input + abstract val minSdkVersion: Property<Int> + + private val ndk: Ndk + get() = Ndk(ndkPath.asFile.get()) + + @TaskAction + fun run() { + if (!generator.isPresent) { + // Creating the generated directory even if we have no generator + // makes it easier to write tasks that *might* consume prefab + // packages. + generatedDirectory.get().asFile.mkdirs() + return + } + + val outDir = outputDirectory.get().asFile + val packages = mutableListOf<Package>() + for (aar in aars) { + val packagePath = outDir.resolve(aar.nameWithoutExtension) + extract(aar, packagePath) + packages.add(Package(packagePath.toPath().resolve("prefab"))) + } + generateSysroot(packages, minSdkVersion.get(), ndk.version.major) + } + + private fun extract(aar: File, extractDir: File) { + ZipFile(aar).use { zip -> + zip.entries().asSequence().forEach { entry -> + zip.getInputStream(entry).use { input -> + val outFile = extractDir.resolve(entry.name) + if (entry.isDirectory) { + outFile.mkdirs() + } else { + outFile.outputStream().use { output -> + input.copyTo(output) + } + } + } + } + } + } + + private fun generateSysroot( + packages: List<Package>, osVersion: Int, ndkVersion: Int + ) { + val generatorType = generator.get() + val constructor = + generatorType.getConstructor(File::class.java, List::class.java) + val buildSystemIntegration = + constructor.newInstance(generatedDirectory.get().asFile, packages) + + buildSystemIntegration.generate(Android.Abi.values().map { + Android(it, osVersion, Android.Stl.CxxShared, ndkVersion) + }) + } +}
\ No newline at end of file diff --git a/buildSrc/src/main/kotlin/com/android/ndkports/SourceExtractTask.kt b/buildSrc/src/main/kotlin/com/android/ndkports/SourceExtractTask.kt new file mode 100644 index 0000000..4f2660f --- /dev/null +++ b/buildSrc/src/main/kotlin/com/android/ndkports/SourceExtractTask.kt @@ -0,0 +1,34 @@ +package com.android.ndkports + +import org.gradle.api.DefaultTask +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.OutputDirectory +import org.gradle.api.tasks.TaskAction + +abstract class SourceExtractTask : DefaultTask() { + @get:InputFile + abstract val source: RegularFileProperty + + @get:OutputDirectory + abstract val outDir: DirectoryProperty + + @TaskAction + fun run() { + val pb = ProcessBuilder( + listOf( + "tar", + "xf", + source.get().asFile.absolutePath, + "--strip-components=1" + ) + ).redirectErrorStream(true).directory(outDir.get().asFile) + + val result = pb.start() + val output = result.inputStream.bufferedReader().use { it.readText() } + if (result.waitFor() != 0) { + throw RuntimeException("Subprocess failed with:\n$output") + } + } +}
\ No newline at end of file diff --git a/buildSrc/src/main/kotlin/com/android/ndkports/Testing.kt b/buildSrc/src/main/kotlin/com/android/ndkports/Testing.kt new file mode 100644 index 0000000..203e04f --- /dev/null +++ b/buildSrc/src/main/kotlin/com/android/ndkports/Testing.kt @@ -0,0 +1,151 @@ +package com.android.ndkports + +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.channels.SendChannel +import kotlinx.coroutines.channels.toList +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import org.gradle.api.DefaultTask +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.InputDirectory +import org.gradle.api.tasks.TaskAction +import java.io.File + +sealed class TestResult(val name: String, val abi: Abi) { + class Success(name: String, abi: Abi) : TestResult(name, abi) { + override fun toString(): String = "PASS $abi $name" + } + + class Failure(name: String, abi: Abi, private val output: String) : + TestResult(name, abi) { + override fun toString(): String = "FAIL $abi $name: $output" + } +} + +private val BASE_DEVICE_DIRECTORY = File("/data/local/tmp/ndkports") + +data class PushSpec(val src: File, val dest: File) + +class PushBuilder(val abi: Abi, val toolchain: Toolchain) { + val pushSpecs = mutableListOf<PushSpec>() + + fun push(src: File, dest: File) = pushSpecs.add(PushSpec(src, dest)) +} + +data class ShellTestSpec(val name: String, val cmd: Iterable<String>) + +class ShellTestBuilder(val deviceDirectory: File, val abi: Abi) { + val runSpecs = mutableListOf<ShellTestSpec>() + + fun shellTest(name: String, cmd: Iterable<String>) = + runSpecs.add(ShellTestSpec(name, cmd)) +} + +abstract class AndroidExecutableTestTask : DefaultTask() { + @get:InputDirectory + abstract val ndkPath: DirectoryProperty + + private val ndk: Ndk + get() = Ndk(ndkPath.asFile.get()) + + @get:Input + abstract val minSdkVersion: Property<Int> + + @get:Input + abstract val push: Property<PushBuilder.() -> Unit> + + fun push(block: PushBuilder.() -> Unit) = push.set(block) + + @get:Input + abstract val run: Property<ShellTestBuilder.() -> Unit> + + fun run(block: ShellTestBuilder.() -> Unit) = run.set(block) + + private fun deviceDirectoryForAbi(abi: Abi): File = + BASE_DEVICE_DIRECTORY.resolve(project.name).resolve(abi.toString()) + + private suspend fun runTests( + device: Device, abi: Abi, resultChannel: SendChannel<TestResult> + ) = coroutineScope { + val deviceDirectory = deviceDirectoryForAbi(abi) + + val pushBlock = push.get() + val runBlock = run.get() + + val pushBuilder = + PushBuilder(abi, Toolchain(ndk, abi, minSdkVersion.get())) + pushBuilder.pushBlock() + coroutineScope { + pushBuilder.pushSpecs.forEach { + launch(Dispatchers.IO) { + device.push( + it.src, deviceDirectory.resolve(it.dest) + ) + } + } + } + + val runBuilder = ShellTestBuilder(deviceDirectory, abi) + runBuilder.runBlock() + runBuilder.runSpecs.forEach { + launch(Dispatchers.IO) { + val result = try { + device.shell(it.cmd) + TestResult.Success(it.name, abi) + } catch (ex: AdbException) { + TestResult.Failure(it.name, abi, "${ex.cmd}\n${ex.output}") + } + + resultChannel.send(result) + } + } + } + + @Suppress("UnstableApiUsage") + @TaskAction + fun runTask() = runBlocking { + val fleet = DeviceFleet() + val warningChannel = Channel<String>(Channel.UNLIMITED) + val resultChannel = Channel<TestResult>(Channel.UNLIMITED) + coroutineScope { + for (abi in Abi.values()) { + launch { + val device = fleet.findDeviceFor( + abi, abi.adjustMinSdkVersion(minSdkVersion.get()) + ) + if (device == null) { + warningChannel.send( + "No device capable of running tests for $abi " + + "minSdkVersion 21" + ) + return@launch + } + device.shell( + listOf( + "rm", "-rf", deviceDirectoryForAbi(abi).toString() + ) + ) + runTests(device, abi, resultChannel) + } + } + } + warningChannel.close() + resultChannel.close() + + for (warning in warningChannel) { + logger.warn(warning) + } + + val failures = + resultChannel.toList().filterIsInstance<TestResult.Failure>() + if (failures.isNotEmpty()) { + throw RuntimeException( + "Tests failed:\n${failures.joinToString("\n")}" + ) + } + } +}
\ No newline at end of file diff --git a/src/main/kotlin/com/android/ndkports/Toolchain.kt b/buildSrc/src/main/kotlin/com/android/ndkports/Toolchain.kt index d42e8d1..1293ecb 100644 --- a/src/main/kotlin/com/android/ndkports/Toolchain.kt +++ b/buildSrc/src/main/kotlin/com/android/ndkports/Toolchain.kt @@ -29,6 +29,8 @@ class Toolchain(val ndk: Ndk, val abi: Abi, val api: Int) { else -> "$binutilsTriple$api" } + val sysrootLibs = ndk.sysrootDirectory.resolve("usr/lib/$binutilsTriple") + val binDir = ndk.toolchainBinDirectory val ar = binDir.resolve("$binutilsTriple-ar") val clang = binDir.resolve("$clangTriple-clang") diff --git a/src/test/kotlin/com/android/ndkports/CMakeCompatibleVersionTest.kt b/buildSrc/src/test/kotlin/com/android/ndkports/CMakeCompatibleVersionTest.kt index 7724a8e..7724a8e 100644 --- a/src/test/kotlin/com/android/ndkports/CMakeCompatibleVersionTest.kt +++ b/buildSrc/src/test/kotlin/com/android/ndkports/CMakeCompatibleVersionTest.kt diff --git a/src/test/kotlin/com/android/ndkports/NdkVersionTest.kt b/buildSrc/src/test/kotlin/com/android/ndkports/NdkVersionTest.kt index 750cc52..750cc52 100644 --- a/src/test/kotlin/com/android/ndkports/NdkVersionTest.kt +++ b/buildSrc/src/test/kotlin/com/android/ndkports/NdkVersionTest.kt diff --git a/src/test/resources/junit-platform.properties b/buildSrc/src/test/resources/junit-platform.properties index d265fd8..d265fd8 100644 --- a/src/test/resources/junit-platform.properties +++ b/buildSrc/src/test/resources/junit-platform.properties diff --git a/curl/build.gradle.kts b/curl/build.gradle.kts new file mode 100644 index 0000000..089ef04 --- /dev/null +++ b/curl/build.gradle.kts @@ -0,0 +1,106 @@ +import com.android.ndkports.AutoconfPortTask +import com.android.ndkports.CMakeCompatibleVersion +import com.android.ndkports.PrefabSysrootPlugin + +val portVersion = "7.79.1" + +group = "com.android.ndk.thirdparty" +version = "$portVersion${rootProject.extra.get("snapshotSuffix")}" + +plugins { + id("maven-publish") + id("com.android.ndkports.NdkPorts") +} + +dependencies { + implementation(project(":openssl")) +} + +ndkPorts { + ndkPath.set(File(project.findProperty("ndkPath") as String)) + source.set(project.file("src.tar.gz")) + minSdkVersion.set(16) +} + +tasks.prefab { + generator.set(PrefabSysrootPlugin::class.java) +} + +tasks.register<AutoconfPortTask>("buildPort") { + autoconf { + args( + "--disable-ntlm-wb", + "--enable-ipv6", + "--with-zlib", + "--with-ca-path=/system/etc/security/cacerts", + "--with-ssl=$sysroot" + ) + + // aarch64 still defaults to bfd which transitively checks libraries. + // When curl is linking one of its own libraries which depends on + // openssl, it doesn't pass -rpath-link to be able to find the SSL + // libraries and fails to build because of it. + // + // TODO: Switch to lld once we're using r21. + env("LDFLAGS", "-fuse-ld=gold") + } +} + +tasks.prefabPackage { + version.set(CMakeCompatibleVersion.parse(portVersion)) + + licensePath.set("COPYING") + + @Suppress("UnstableApiUsage") dependencies.set( + mapOf( + "openssl" to "1.1.1k" + ) + ) + + modules { + create("curl") { + dependencies.set( + listOf( + "//openssl:crypto", "//openssl:ssl" + ) + ) + } + } +} + +publishing { + publications { + create<MavenPublication>("maven") { + from(components["prefab"]) + pom { + name.set("curl") + description.set("The ndkports AAR for curl.") + url.set( + "https://android.googlesource.com/platform/tools/ndkports" + ) + licenses { + license { + name.set("The curl License") + url.set("https://curl.haxx.se/docs/copyright.html") + distribution.set("repo") + } + } + developers { + developer { + name.set("The Android Open Source Project") + } + } + scm { + url.set("https://android.googlesource.com/platform/tools/ndkports") + connection.set("scm:git:https://android.googlesource.com/platform/tools/ndkports") + } + } + } + } + + repositories { + maven { + url = uri("${rootProject.buildDir}/repository") + } + } +} diff --git a/curl/src.tar.gz b/curl/src.tar.gz Binary files differnew file mode 100644 index 0000000..b6d3b85 --- /dev/null +++ b/curl/src.tar.gz diff --git a/googletest/build.gradle.kts b/googletest/build.gradle.kts new file mode 100644 index 0000000..c365519 --- /dev/null +++ b/googletest/build.gradle.kts @@ -0,0 +1,124 @@ +import com.android.ndkports.AndroidExecutableTestTask +import com.android.ndkports.CMakeCompatibleVersion +import com.android.ndkports.CMakePortTask + +val portVersion = "1.11.0" + +group = "com.android.ndk.thirdparty" +version = "$portVersion${rootProject.extra.get("snapshotSuffix")}" + +plugins { + id("maven-publish") + id("com.android.ndkports.NdkPorts") +} + +ndkPorts { + ndkPath.set(File(project.findProperty("ndkPath") as String)) + source.set(project.file("src.tar.gz")) + minSdkVersion.set(16) +} + +val buildTask = tasks.register<CMakePortTask>("buildPort") { + cmake { + arg("-Dgtest_build_tests=ON") + arg("-Dgmock_build_tests=ON") + } +} + +tasks.prefabPackage { + version.set(CMakeCompatibleVersion.parse(portVersion)) + + modules { + create("gtest") { + static.set(true) + } + create("gtest_main") { + static.set(true) + } + create("gmock") { + static.set(true) + } + create("gmock_main") { + static.set(true) + } + } +} + +fun findTests(directory: File) = directory.listFiles()!!.filter { + // There are also many tests that end with test_, but those require running + // Python on the device. + it.name.endsWith("test") +} + +tasks.register<AndroidExecutableTestTask>("test") { + push { + val buildDir = buildTask.get().buildDirectoryFor(abi) + findTests(buildDir.resolve("googlemock")).forEach { test -> + push(test, File("googlemock").resolve(test.name)) + } + findTests(buildDir.resolve("googletest")).forEach { test -> + push(test, File("googletest").resolve(test.name)) + } + } + + run { + val buildDir = buildTask.get().buildDirectoryFor(abi) + findTests(buildDir.resolve("googlemock")).forEach { test -> + shellTest( + test.name, listOf( + "cd", + deviceDirectory.resolve("googlemock").toString(), + "&&", + "./${test.name}" + ) + ) + } + findTests(buildDir.resolve("googletest")).forEach { test -> + shellTest( + test.name, listOf( + "cd", + deviceDirectory.resolve("googletest").toString(), + "&&", + "./${test.name}" + ) + ) + } + } +} + +publishing { + publications { + create<MavenPublication>("maven") { + from(components["prefab"]) + pom { + name.set("GoogleTest") + description.set("The ndkports AAR for GoogleTest.") + url.set( + "https://android.googlesource.com/platform/tools/ndkports" + ) + licenses { + license { + name.set("BSD-3-Clause License") + url.set("https://github.com/google/googletest/blob/master/LICENSE") + distribution.set("repo") + } + } + developers { + developer { + name.set("The Android Open Source Project") + } + } + scm { + url.set("https://android.googlesource.com/platform/tools/ndkports") + connection.set("scm:git:https://android.googlesource.com/platform/tools/ndkports") + } + } + } + } + + repositories { + maven { + url = uri("${rootProject.buildDir}/repository") + } + } +} diff --git a/googletest/src.tar.gz b/googletest/src.tar.gz Binary files differnew file mode 100644 index 0000000..8163d74 --- /dev/null +++ b/googletest/src.tar.gz diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar Binary files differindex 94336fc..e708b1c 100644 --- a/gradle/wrapper/gradle-wrapper.jar +++ b/gradle/wrapper/gradle-wrapper.jar diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 7c4388a..da9702f 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-5.6.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.8-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists @@ -1,5 +1,21 @@ #!/usr/bin/env sh +# +# Copyright 2015 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. +# + ############################################################################## ## ## Gradle start up script for UN*X @@ -28,7 +44,7 @@ APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" @@ -66,6 +82,7 @@ esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then @@ -109,10 +126,11 @@ if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath @@ -138,19 +156,19 @@ if $cygwin ; then else eval `echo args$i`="\"$arg\"" fi - i=$((i+1)) + i=`expr $i + 1` done case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi @@ -159,14 +177,9 @@ save () { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } -APP_ARGS=$(save "$@") +APP_ARGS=`save "$@"` # Collect all arguments for the java command, following the shell quoting and substitution rules eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" -fi - exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index f955316..107acd3 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,3 +1,19 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @@ -13,15 +29,18 @@ if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if "%ERRORLEVEL%" == "0" goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -35,7 +54,7 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% @@ -45,28 +64,14 @@ echo location of your Java installation. goto fail -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell diff --git a/jsoncpp/build.gradle.kts b/jsoncpp/build.gradle.kts new file mode 100644 index 0000000..29ffc66 --- /dev/null +++ b/jsoncpp/build.gradle.kts @@ -0,0 +1,103 @@ +import com.android.ndkports.AndroidExecutableTestTask +import com.android.ndkports.CMakeCompatibleVersion +import com.android.ndkports.MesonPortTask + +val portVersion = "1.9.5" + +group = "com.android.ndk.thirdparty" +version = "$portVersion${rootProject.extra.get("snapshotSuffix")}" + +plugins { + id("maven-publish") + id("com.android.ndkports.NdkPorts") +} + +ndkPorts { + ndkPath.set(File(project.findProperty("ndkPath") as String)) + source.set(project.file("src.tar.gz")) + minSdkVersion.set(16) +} + +tasks.extractSrc { + doLast { + // jsoncpp has a "version" file on the include path that conflicts with + // https://en.cppreference.com/w/cpp/header/version. Remove it so we can + // build. + outDir.get().asFile.resolve("version").delete() + } +} + +val buildTask = tasks.register<MesonPortTask>("buildPort") + +tasks.prefabPackage { + version.set(CMakeCompatibleVersion.parse(portVersion)) + + modules { + create("jsoncpp") + } +} + +tasks.register<AndroidExecutableTestTask>("test") { + push { + push( + buildTask.get().buildDirectoryFor(abi).resolve("jsoncpp_test"), + File("jsoncpp_test") + ) + push( + buildTask.get().installDirectoryFor(abi) + .resolve("lib/libjsoncpp.so"), File("libjsoncpp.so") + ) + push( + toolchain.sysrootLibs.resolve("libc++_shared.so"), + File("libc++_shared.so") + ) + } + + run { + // JsonCpp has other tests, but they require running Python on the + // device. + shellTest( + "jsoncpp_test", listOf( + "LD_LIBRARY_PATH=$deviceDirectory", + deviceDirectory.resolve("jsoncpp_test").toString() + ) + ) + } +} + +publishing { + publications { + create<MavenPublication>("maven") { + from(components["prefab"]) + pom { + name.set("JsonCpp") + description.set("The ndkports AAR for JsonCpp.") + url.set( + "https://android.googlesource.com/platform/tools/ndkports" + ) + licenses { + license { + name.set("The JsonCpp License") + url.set("https://github.com/open-source-parsers/jsoncpp/blob/master/LICENSE") + distribution.set("repo") + } + } + developers { + developer { + name.set("The Android Open Source Project") + } + } + scm { + url.set("https://android.googlesource.com/platform/tools/ndkports") + connection.set("scm:git:https://android.googlesource.com/platform/tools/ndkports") + } + } + } + } + + repositories { + maven { + url = uri("${rootProject.buildDir}/repository") + } + } +} diff --git a/jsoncpp/src.tar.gz b/jsoncpp/src.tar.gz Binary files differnew file mode 100644 index 0000000..35052af --- /dev/null +++ b/jsoncpp/src.tar.gz diff --git a/openssl/build.gradle.kts b/openssl/build.gradle.kts new file mode 100644 index 0000000..b7c7a48 --- /dev/null +++ b/openssl/build.gradle.kts @@ -0,0 +1,180 @@ +import com.android.ndkports.AdHocPortTask +import com.android.ndkports.AndroidExecutableTestTask +import com.android.ndkports.CMakeCompatibleVersion + +fun openSslVersionToCMakeVersion(openSslVersion: String): CMakeCompatibleVersion { + val (major, minor, microAndLetter) = openSslVersion.split(".") + val letter = microAndLetter.last() + val micro = microAndLetter.substringBefore(letter) + val tweak = if (letter.isDigit()) { + // 1.1.1 is 1.1.1.0. + 0 + } else { + // 1.1.1a is 1.1.1.1. + letter.toInt() - 'a'.toInt() + 1 + } + + return CMakeCompatibleVersion( + major.toInt(), minor.toInt(), micro.toInt(), tweak + ) +} + +val portVersion = "1.1.1l" +val prefabVersion = openSslVersionToCMakeVersion(portVersion) + +group = "com.android.ndk.thirdparty" +version = "$portVersion${rootProject.extra.get("snapshotSuffix")}" + +plugins { + id("maven-publish") + id("com.android.ndkports.NdkPorts") +} + +ndkPorts { + ndkPath.set(File(project.findProperty("ndkPath") as String)) + source.set(project.file("src.tar.gz")) + minSdkVersion.set(16) +} + +val buildTask = tasks.register<AdHocPortTask>("buildPort") { + builder { + run { + args( + sourceDirectory.resolve("Configure").absolutePath, + "android-${toolchain.abi.archName}", + "-D__ANDROID_API__=${toolchain.api}", + "--prefix=${installDirectory.absolutePath}", + "--openssldir=${installDirectory.absolutePath}", + "no-sctp", + "shared" + ) + + env("ANDROID_NDK", toolchain.ndk.path.absolutePath) + env("PATH", "${toolchain.binDir}:${System.getenv("PATH")}") + } + + run { + args("make", "-j$ncpus", "SHLIB_EXT=.so") + + env("ANDROID_NDK", toolchain.ndk.path.absolutePath) + env("PATH", "${toolchain.binDir}:${System.getenv("PATH")}") + } + + run { + args("make", "install_sw", "SHLIB_EXT=.so") + + env("ANDROID_NDK", toolchain.ndk.path.absolutePath) + env("PATH", "${toolchain.binDir}:${System.getenv("PATH")}") + } + } +} + +tasks.prefabPackage { + version.set(prefabVersion) + + modules { + create("crypto") + create("ssl") + } +} + +tasks.register<AndroidExecutableTestTask>("test") { + val srcDir = tasks.extractSrc.get().outDir.asFile.get() + val testSrc = srcDir.resolve("test/ssl-tests") + val deviceTestRelPath = File("testconf") + + val unsupportedTests = listOf( + // This test is empty and appears to just be broken in 1.1.1k. + "16-certstatus.conf", + // zlib support is not enabled. + "22-compression.conf", + // Android does not support SCTP sockets and this test requires them. + "29-dtls-sctp-label-bug.conf" + ) + + push { + val ignoredExtensions = listOf("o", "d") + val buildDirectory = buildTask.get().buildDirectoryFor(abi) + push( + srcDir.resolve("test/ct/log_list.conf"), File("log_list.conf") + ) + for (file in buildDirectory.walk()) { + if (!file.isFile) { + continue + } + + if (file.extension in ignoredExtensions) { + continue + } + + push(file, file.relativeTo(buildDirectory)) + } + for (file in testSrc.walk()) { + if (file.extension == "conf") { + push( + file, deviceTestRelPath.resolve(file.relativeTo(testSrc)) + ) + } + } + push(srcDir.resolve("test/certs"), File("certs")) + } + + run { + // https://github.com/openssl/openssl/blob/master/test/README.ssltest.md + val sslTest = deviceDirectory.resolve("test/ssl_test") + val ctlogFile = deviceDirectory.resolve("log_list.conf") + val testCertDir = deviceDirectory.resolve("certs") + for (file in testSrc.walk()) { + val test = deviceDirectory.resolve(deviceTestRelPath) + .resolve(file.relativeTo(testSrc)) + if (file.extension == "conf" && file.name !in unsupportedTests) { + shellTest( + file.relativeTo(testSrc).toString(), listOf( + "LD_LIBRARY_PATH=$deviceDirectory", + "CTLOG_FILE=$ctlogFile", + "TEST_CERTS_DIR=$testCertDir", + sslTest.toString(), + test.toString() + ) + ) + } + } + } +} + +publishing { + publications { + create<MavenPublication>("maven") { + from(components["prefab"]) + pom { + name.set("OpenSSL") + description.set("The ndkports AAR for OpenSSL.") + url.set( + "https://android.googlesource.com/platform/tools/ndkports" + ) + licenses { + license { + name.set("Dual OpenSSL and SSLeay License") + url.set("https://www.openssl.org/source/license-openssl-ssleay.txt") + distribution.set("repo") + } + } + developers { + developer { + name.set("The Android Open Source Project") + } + } + scm { + url.set("https://android.googlesource.com/platform/tools/ndkports") + connection.set("scm:git:https://android.googlesource.com/platform/tools/ndkports") + } + } + } + } + + repositories { + maven { + url = uri("${rootProject.buildDir}/repository") + } + } +} diff --git a/ports/openssl/src.tar.gz b/openssl/src.tar.gz Binary files differindex e768f9e..81be7f9 100644 --- a/ports/openssl/src.tar.gz +++ b/openssl/src.tar.gz diff --git a/ports/curl/port.kts b/ports/curl/port.kts deleted file mode 100644 index a870a86..0000000 --- a/ports/curl/port.kts +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2019 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. - */ - -package com.android.ndkports - -import java.io.File - -object : AutoconfPort() { - override val name = "curl" - override val version = "7.69.1" - override val mavenVersion = "$version-alpha-1" - override val licensePath = "COPYING" - - override val license = License( - "The curl License", "https://curl.haxx.se/docs/copyright.html" - ) - - override val dependencies = listOf("openssl") - - override val modules = listOf( - Module( - "curl", - dependencies = listOf("//openssl:crypto", "//openssl:ssl") - ) - ) - - override fun configureArgs( - workingDirectory: File, - toolchain: Toolchain - ): List<String> { - val sslPrefix = installDirectoryForPort( - "openssl", - workingDirectory, - toolchain - ).absolutePath - return listOf( - "--disable-ntlm-wb", - "--enable-ipv6", - "--with-zlib", - "--with-ca-path=/system/etc/security/cacerts", - "--with-ssl=$sslPrefix" - ) - } - - override fun configureEnv( - workingDirectory: File, - toolchain: Toolchain - ): Map<String, String> = mapOf( - // aarch64 still defaults to bfd which transitively checks libraries. - // When curl is linking one of its own libraries which depends on - // openssl, it doesn't pass -rpath-link to be able to find the SSL - // libraries and fails to build because of it. - // - // TODO: Switch to lld once we're using r21. - "LDFLAGS" to "-fuse-ld=gold" - ) -} diff --git a/ports/curl/src.tar.gz b/ports/curl/src.tar.gz Binary files differdeleted file mode 100644 index b0da690..0000000 --- a/ports/curl/src.tar.gz +++ /dev/null diff --git a/ports/jsoncpp/port.kts b/ports/jsoncpp/port.kts deleted file mode 100644 index 8a3e1ba..0000000 --- a/ports/jsoncpp/port.kts +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2019 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. - */ - -package com.android.ndkports - -import java.io.File - -object : MesonPort() { - override val name = "jsoncpp" - override val version = "1.8.4" - override val mavenVersion = "$version-alpha-1" - - override val license = License( - "The JsonCpp License", - "https://github.com/open-source-parsers/jsoncpp/blob/master/LICENSE" - ) - - override val modules = listOf( - Module("jsoncpp") - ) - - override fun extractSource( - sourceTarball: File, - sourceDirectory: File, - workingDirectory: File - ): Result<Unit, String> = - super.extractSource(sourceTarball, sourceDirectory, workingDirectory) - .onSuccess { - // jsoncpp has a "version" file on the include path that - // conflicts with - // https://en.cppreference.com/w/cpp/header/version. Remove it - // so we can build. - sourceDirectory.resolve("version").delete() - } -} diff --git a/ports/jsoncpp/src.tar.gz b/ports/jsoncpp/src.tar.gz Binary files differdeleted file mode 100644 index 7f3cd4b..0000000 --- a/ports/jsoncpp/src.tar.gz +++ /dev/null diff --git a/ports/openssl/port.kts b/ports/openssl/port.kts deleted file mode 100644 index 45b5206..0000000 --- a/ports/openssl/port.kts +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (C) 2019 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. - */ - -package com.android.ndkports - -import java.io.File - -object : Port() { - override val name = "openssl" - override val version = "1.1.1g" - override val mavenVersion = "$version-alpha-1" - override val prefabVersion = CMakeCompatibleVersion(1, 1, 1, 7) - - override val license = License( - "Dual OpenSSL and SSLeay License", - "https://www.openssl.org/source/license-openssl-ssleay.txt" - ) - - override val modules = listOf( - Module("crypto"), - Module("ssl") - ) - - override fun configure( - toolchain: Toolchain, - sourceDirectory: File, - buildDirectory: File, - installDirectory: File, - workingDirectory: File - ): Result<Unit, String> { - buildDirectory.mkdirs() - return executeProcessStep( - listOf( - sourceDirectory.resolve("Configure").absolutePath, - "android-${toolchain.abi.archName}", - "-D__ANDROID_API__=${toolchain.api}", - "--prefix=${installDirectory.absolutePath}", - "--openssldir=${installDirectory.absolutePath}", - "shared" - ), - buildDirectory, - additionalEnvironment = mapOf( - "ANDROID_NDK" to toolchain.ndk.path.absolutePath, - "PATH" to "${toolchain.binDir}:${System.getenv("PATH")}" - ) - ) - } - - override fun build( - toolchain: Toolchain, - buildDirectory: File - ): Result<Unit, String> = - executeProcessStep( - listOf( - "make", - "-j$ncpus", - "SHLIB_EXT=.so" - ), buildDirectory, - additionalEnvironment = mapOf( - "ANDROID_NDK" to toolchain.ndk.path.absolutePath, - "PATH" to "${toolchain.binDir}:${System.getenv("PATH")}" - ) - ) - - override fun install( - toolchain: Toolchain, - buildDirectory: File, - installDirectory: File - ): Result<Unit, String> = - executeProcessStep( - listOf("make", "install_sw", "SHLIB_EXT=.so"), buildDirectory, - additionalEnvironment = mapOf( - "ANDROID_NDK" to toolchain.ndk.path.absolutePath, - "PATH" to "${toolchain.binDir}:${System.getenv("PATH")}" - ) - ) -} diff --git a/scripts/build.sh b/scripts/build.sh deleted file mode 100755 index e7c16b6..0000000 --- a/scripts/build.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash -set -e -docker build -t ndkports . -docker run --rm -v $(pwd):/src ndkports diff --git a/scripts/build_release.sh b/scripts/build_release.sh new file mode 100755 index 0000000..f99e223 --- /dev/null +++ b/scripts/build_release.sh @@ -0,0 +1,8 @@ +#!/bin/bash +set -e +docker build -t ndkports . +# We need to specify the full argument list for gradle explicitly because +# there's no way to append to docker's CMD. This should be kept the same as the +# default, but adding -Prelease. +docker run --rm -v $(pwd):/src ndkports \ + --stacktrace -PndkPath=/ndk -Prelease release diff --git a/scripts/build_snapshot.sh b/scripts/build_snapshot.sh new file mode 100755 index 0000000..4c9d767 --- /dev/null +++ b/scripts/build_snapshot.sh @@ -0,0 +1,6 @@ +#!/bin/bash +set -e +docker build -t ndkports . +# Default command for the docker image handles the NDK location, --stacktrace, +# task list, etc. +docker run --rm -v $(pwd):/src ndkports diff --git a/settings.gradle.kts b/settings.gradle.kts index 4549d2e..726326c 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -8,4 +8,9 @@ pluginManagement { } } } -}
\ No newline at end of file +} + +include("curl") +include("googletest") +include("jsoncpp") +include("openssl") diff --git a/src/main/kotlin/com/android/ndkports/Abi.kt b/src/main/kotlin/com/android/ndkports/Abi.kt deleted file mode 100644 index 58981a2..0000000 --- a/src/main/kotlin/com/android/ndkports/Abi.kt +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 2019 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. - */ - -package com.android.ndkports - -enum class Abi(val archName: String, val abiName: String) { - Arm("arm", "armeabi-v7a"), - Arm64("arm64", "arm64-v8a"), - X86("x86", "x86"), - X86_64("x86_64", "x86_64"), -}
\ No newline at end of file diff --git a/src/main/kotlin/com/android/ndkports/Cli.kt b/src/main/kotlin/com/android/ndkports/Cli.kt deleted file mode 100644 index e2a5879..0000000 --- a/src/main/kotlin/com/android/ndkports/Cli.kt +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (C) 2019 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. - */ - -package com.android.ndkports - -import com.github.ajalt.clikt.core.CliktCommand -import com.github.ajalt.clikt.parameters.arguments.argument -import com.github.ajalt.clikt.parameters.arguments.multiple -import com.github.ajalt.clikt.parameters.arguments.validate -import com.github.ajalt.clikt.parameters.options.convert -import com.github.ajalt.clikt.parameters.options.default -import com.github.ajalt.clikt.parameters.options.flag -import com.github.ajalt.clikt.parameters.options.option -import com.github.ajalt.clikt.parameters.options.required -import com.github.ajalt.clikt.parameters.types.file -import de.swirtz.ktsrunner.objectloader.KtsObjectLoader -import java.io.File -import java.io.FileNotFoundException -import java.lang.RuntimeException -import kotlin.system.exitProcess - -class Cli : CliktCommand(help = "ndkports") { - private val outDir: File by option( - "-o", - "--out", - help = "Build directory." - ).file().default(File("out")) - - private val publishToMavenLocal: Boolean by option( - help = "Publish AARs to the local Maven repository (~/.m2/repository)" - ).flag() - - private val packages: List<String> by argument( - help = "Names of packages to build." - ).multiple().validate { - require(it.isNotEmpty()) { "must provide at least one package" } - } - - private val ndk: Ndk by option().convert { Ndk(File(it)) }.required() - - private fun portDirectoryFor(name: String): File = - File("ports").resolve(name) - - private fun loadPort(name: String): Port { - val portDir = portDirectoryFor(name).also { - if (!it.exists()) { - throw FileNotFoundException("Could not find ${it.path}") - } - } - - val portFile = portDir.resolve("port.kts").also { - if (!it.exists()) { - throw FileNotFoundException("Could not find ${it.path}") - } - } - - return KtsObjectLoader().load(portFile.reader()) - } - - override fun run() { - println("Building packages: ${packages.joinToString(", ")}") - val portsByName = packages.map { loadPort(it) }.associateBy { it.name } - for (port in portsByName.values) { - val workingDirectory = - outDir.resolve(port.name).also { it.mkdirs() } - - val sourceDirectory = workingDirectory.resolve("src") - val sourceTarball = - portDirectoryFor(port.name).resolve("src.tar.gz") - - port.extractSource(sourceTarball, sourceDirectory, workingDirectory) - - val apiForAbi = mapOf( - Abi.Arm to 16, - Abi.Arm64 to 21, - Abi.X86 to 16, - Abi.X86_64 to 21 - ) - for (abi in Abi.values()) { - val api = apiForAbi.getOrElse(abi) { - throw RuntimeException( - "No API level specified for ${abi.abiName}" - ) - } - val toolchain = Toolchain(ndk, abi, api) - - val buildDirectory = workingDirectory.resolve("build/$abi") - val installDirectory = installDirectoryForPort( - port.name, workingDirectory, toolchain - ) - - port.run( - toolchain, - sourceDirectory, - buildDirectory, - installDirectory, - workingDirectory - ).onFailure { - println(it) - exitProcess(1) - } - } - - PrefabPackageBuilder( - port, - workingDirectory, - sourceDirectory, - publishToMavenLocal, - ndk, - apiForAbi, - portsByName - ).build() - } - } -} - -fun main(args: Array<String>) { - Cli().main(args) -}
\ No newline at end of file diff --git a/src/main/kotlin/com/android/ndkports/License.kt b/src/main/kotlin/com/android/ndkports/License.kt deleted file mode 100644 index 8ec639f..0000000 --- a/src/main/kotlin/com/android/ndkports/License.kt +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2019 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. - */ - -package com.android.ndkports - -data class License(val name: String, val url: String)
\ No newline at end of file diff --git a/src/main/kotlin/com/android/ndkports/Port.kt b/src/main/kotlin/com/android/ndkports/Port.kt deleted file mode 100644 index 2e0433f..0000000 --- a/src/main/kotlin/com/android/ndkports/Port.kt +++ /dev/null @@ -1,284 +0,0 @@ -/* - * Copyright (C) 2019 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. - */ - -package com.android.ndkports - -import java.io.File - -@Suppress("unused") -fun executeProcessStep( - args: List<String>, - workingDirectory: File, - additionalEnvironment: Map<String, String>? = null -): Result<Unit, String> { - val pb = ProcessBuilder(args) - .redirectOutput(ProcessBuilder.Redirect.INHERIT) - .redirectError(ProcessBuilder.Redirect.INHERIT) - .directory(workingDirectory) - - if (additionalEnvironment != null) { - pb.environment().putAll(additionalEnvironment) - } - - return pb.start() - .waitFor().let { - if (it == 0) { - Result.Ok(Unit) - } else { - Result.Error("Process failed with exit code $it") - } - } -} - -fun installDirectoryForPort( - name: String, - workingDirectory: File, - toolchain: Toolchain -): File = workingDirectory.parentFile.resolve("$name/install/${toolchain.abi}") - -/** - * A module exported by the package. - * - * As currently implemented by ndkports, one module is exactly one library. - * Prefab supports header-only libraries, but ndkports does not support these - * yet. - * - * Static libraries are not currently supported by ndkports. - * - * @property[name] The name of the module. Note that currently the name of the - * installed library file must be exactly `lib$name.so`. - * @property[includesPerAbi] Set to true if a different set of headers should be - * exposed per-ABI. Not currently implemented. - * @property[dependencies] A list of other modules required by this module, in - * the format described by https://google.github.io/prefab/. - */ -data class Module( - val name: String, - val includesPerAbi: Boolean = false, - val dependencies: List<String> = emptyList() -) - -/** - * The base class for all ports. - */ -abstract class Port { - /** - * The name of the port. Will be used as the package name in prefab.json. - */ - abstract val name: String - - /** - * The version of the package. - * - * Used as the default for [prefabVersion] and [mavenVersion] when - * appropriate. - */ - abstract val version: String - - /** - * The version to encode in the prefab.json. - * - * This version must be compatible with CMake's `find_package` for - * config-style packages. This means that it must be one to four decimal - * separated integers. No other format is allowed. - * - * If not set, the default is [version] as interpreted by - * [CMakeCompatibleVersion.parse]. - * - * For example, OpenSSL 1.1.1g will set this value to - * `CMakeCompatibleVersion(1, 1, 1, 7)`. - */ - open val prefabVersion: CMakeCompatibleVersion - get() = CMakeCompatibleVersion.parse(version) - - /** - * The version to use for the maven package. - * - * This field allows versioning the maven package differently from the - * package itself, which is sometimes necessary given CMake's strict version - * format requirements. - * - * If not set, the default is [version]. - * - * For example, this could be set to `"$name-$version-alpha-1"` to - * distribute an alpha of the package. - */ - open val mavenVersion: String - get() = version - - /** - * The relative path to the license file of this package. - * - * This file will be packaged in the AAR in the META-INF directory. - */ - open val licensePath: String = "LICENSE" - - /** - * A description of the license (name and URL) for use in the pom.xml. - */ - abstract val license: License - - /** - * A list of dependencies for this package. - * - * For example, curl depends on `listOf("openssl")`. - */ - open val dependencies: List<String> = emptyList() - - /** - * A list of modules exported by this package. - */ - abstract val modules: List<Module> - - /** - * The number of CPUs available for building. - * - * May be passed to the build system if required. - */ - protected val ncpus = Runtime.getRuntime().availableProcessors() - - fun run( - toolchain: Toolchain, - sourceDirectory: File, - buildDirectory: File, - installDirectory: File, - workingDirectory: File - ): Result<Unit, String> { - configure( - toolchain, - sourceDirectory, - buildDirectory, - installDirectory, - workingDirectory - ).onFailure { return Result.Error(it) } - - build(toolchain, buildDirectory).onFailure { return Result.Error(it) } - - install( - toolchain, - buildDirectory, - installDirectory - ).onFailure { return Result.Error(it) } - - return Result.Ok(Unit) - } - - /** - * Overridable build step for extracting the source package. - * - * @param[sourceTarball] The path to the source tarball. - * @param[sourceDirectory] The destination directory for the extracted - * source. - * @param[workingDirectory] The working top-level directory for this - * package. - * @return A [Result<Unit, String>][Result] describing the result of the - * operation. On failure, [Result.Error<String>][Result.Error] containing an - * error message is returned. - */ - open fun extractSource( - sourceTarball: File, - sourceDirectory: File, - workingDirectory: File - ): Result<Unit, String> { - sourceDirectory.mkdirs() - return executeProcessStep( - listOf( - "tar", - "xf", - sourceTarball.absolutePath, - "--strip-components=1" - ), sourceDirectory - ) - } - - /** - * Overridable build step for configuring the build. - * - * Any pre-build steps should be run here. - * - * In an autoconf build, this runs `configure`. - * - * @param[toolchain] The toolchain used for this build. - * @param[sourceDirectory] The directory containing the extracted package - * source. - * @param[buildDirectory] The directory to use for building. - * @param[installDirectory] The destination directory for this package's - * installed headers and libraries. - * @param[workingDirectory] The top-level working directory for this - * package. - * @return A [Result<Unit, String>][Result] describing the result of the - * operation. On failure, [Result.Error<String>][Result.Error] containing an - * error message is returned. - */ - open fun configure( - toolchain: Toolchain, - sourceDirectory: File, - buildDirectory: File, - installDirectory: File, - workingDirectory: File - ): Result<Unit, String> = Result.Ok(Unit) - - /** - * Overridable build step for building the package. - * - * In an autoconf build, this runs `make`. - * - * @param[toolchain] The toolchain used for this build. - * @param[buildDirectory] The directory to use for building. - * @return A [Result<Unit, String>][Result] describing the result of the - * operation. On failure, [Result.Error<String>][Result.Error] containing an - * error message is returned. - */ - open fun build( - toolchain: Toolchain, - buildDirectory: File - ): Result<Unit, String> = Result.Ok(Unit) - - /** - * Overridable build step for installing built artifacts for packaging. - * - * The install step is expected to install headers and libraries to the - * [installDirectory] with the following layout: - * - * [installDirectory] - * include/ - * <package headers> - * lib/ - * <package libraries> - * - * A matching `lib${module.name}.so` must be present in the `lib` directory - * for every item in [modules]. - * - * Note that it is expected for all modules to use the same headers. This is - * currently the case for all ports currently maintained, but could change - * in the future. - * - * In an autoconf build, this runs `make install`. - * - * @param[toolchain] The toolchain used for this build. - * @param[buildDirectory] The directory containing build artifacts. - * @param[installDirectory] The destination directory for this package's - * installed headers and libraries. - * @return A [Result<Unit, String>][Result] describing the result of the - * operation. On failure, [Result.Error<String>][Result.Error] containing an - * error message is returned. - */ - open fun install( - toolchain: Toolchain, - buildDirectory: File, - installDirectory: File - ): Result<Unit, String> = Result.Ok(Unit) -}
\ No newline at end of file diff --git a/src/main/kotlin/com/android/ndkports/PrefabPackageBuilder.kt b/src/main/kotlin/com/android/ndkports/PrefabPackageBuilder.kt deleted file mode 100644 index 1a58f5a..0000000 --- a/src/main/kotlin/com/android/ndkports/PrefabPackageBuilder.kt +++ /dev/null @@ -1,253 +0,0 @@ -/* - * Copyright (C) 2019 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. - */ - -package com.android.ndkports - -import com.google.prefab.api.AndroidAbiMetadata -import com.google.prefab.api.ModuleMetadataV1 -import com.google.prefab.api.PackageMetadataV1 -import kotlinx.serialization.json.Json -import kotlinx.serialization.stringify -import org.apache.maven.model.Dependency -import org.apache.maven.model.Developer -import org.apache.maven.model.License -import org.apache.maven.model.Scm -import org.apache.maven.model.io.DefaultModelWriter -import org.apache.maven.project.MavenProject -import org.redundent.kotlin.xml.xml -import java.io.File - -class PrefabPackageBuilder( - private val port: Port, - private val directory: File, - private val sourceDirectory: File, - private val publishToMavenLocal: Boolean, - private val ndk: Ndk, - private val abiToApiMap: Map<Abi, Int>, - private val portsByName: Map<String, Port> -) { - private val packageDirectory = directory.resolve("aar") - private val prefabDirectory = packageDirectory.resolve("prefab") - private val modulesDirectory = prefabDirectory.resolve("modules") - - private val packageComponents = listOf( - "com", - "android", - "ndk", - "thirdparty", - port.name - ) - - private val packageName = packageComponents.joinToString(".") - private val groupComponents = packageComponents.dropLast(1) - private val groupId = groupComponents.joinToString(".") - private val artifactId = packageComponents.last() - - private val mavenProject = MavenProject().also { - it.name = port.name - it.description = "The ndkports AAR for ${port.name}." - it.url = "https://android.googlesource.com/platform/tools/ndkports" - it.groupId = groupId - it.artifactId = artifactId - it.version = port.mavenVersion - it.packaging = "aar" - it.licenses = listOf( - License().also { license -> - license.name = port.license.name - license.url = port.license.url - license.distribution = "repo" - } - ) - it.developers = listOf( - Developer().also { developer -> - developer.name = "The Android Open Source Project" - } - ) - it.scm = Scm().also { scm -> - scm.url = "https://android.googlesource.com/platform/tools/ndkports" - scm.connection = "scm:git:https://android.googlesource.com/platform/tools/ndkports" - } - it.dependencies = port.dependencies.map { depName -> - val depPort = portsByName[depName] ?: throw RuntimeException( - "${port.name} depends on unknown port: $depName" - ) - Dependency().also { dep -> - dep.artifactId = depPort.name - dep.groupId = groupId - dep.version = depPort.mavenVersion - dep.type = "aar" - // TODO: Make this an option in the Port. - // We currently only have one dependency from curl to OpenSSL, - // and that's (from the perspective of the AAR consumer), a - // runtime dependency. If we ever have compile dependencies, - // we'll want to make it possible for each port to customize its - // scope. - dep.scope = "runtime" - } - } - // TODO: Issue management? - } - - private fun preparePackageDirectory() { - if (packageDirectory.exists()) { - packageDirectory.deleteRecursively() - } - modulesDirectory.mkdirs() - } - - private fun makePackageMetadata() { - prefabDirectory.resolve("prefab.json").writeText( - Json.stringify( - PackageMetadataV1( - port.name, - schemaVersion = 1, - dependencies = port.dependencies, - version = port.prefabVersion.toString() - ) - ) - ) - } - - private fun makeModuleMetadata(module: Module, moduleDirectory: File) { - moduleDirectory.resolve("module.json").writeText( - Json.stringify( - ModuleMetadataV1( - exportLibraries = module.dependencies - ) - ) - ) - } - - private fun installLibForAbi(module: Module, abi: Abi, libsDir: File) { - val libName = "lib${module.name}.so" - val installDirectory = libsDir.resolve("android.${abi.abiName}").apply { - mkdirs() - } - - directory.resolve("install/$abi/lib/$libName") - .copyTo(installDirectory.resolve(libName)) - - val api = abiToApiMap.getOrElse(abi) { - throw RuntimeException( - "No API level specified for ${abi.abiName}" - ) - } - - installDirectory.resolve("abi.json").writeText( - Json.stringify( - AndroidAbiMetadata( - abi = abi.abiName, - api = api, - ndk = ndk.version.major, - stl = "c++_shared" - ) - ) - ) - } - - private fun installLicense() { - val src = sourceDirectory.resolve(port.licensePath) - val dest = packageDirectory.resolve("META-INF") - .resolve(File(port.licensePath).name) - src.copyTo(dest) - } - - private fun createAndroidManifest() { - packageDirectory.resolve("AndroidManifest.xml") - .writeText(xml("manifest") { - attributes( - "xmlns:android" to "http://schemas.android.com/apk/res/android", - "package" to packageName, - "android:versionCode" to 1, - "android:versionName" to "1.0" - ) - - "uses-sdk" { - attributes( - "android:minSdkVersion" to 16, - "android:targetSdkVersion" to 29 - ) - } - }.toString()) - } - - private fun createPom(pomFile: File) { - DefaultModelWriter().write(pomFile, null, mavenProject.model) - } - - private fun installToLocalMaven(archive: File, pomFile: File) { - val pb = ProcessBuilder( - listOf( - "mvn", - "install:install-file", - "-Dfile=$archive", - "-DpomFile=$pomFile" - ) - ) - .redirectOutput(ProcessBuilder.Redirect.INHERIT) - .redirectError(ProcessBuilder.Redirect.INHERIT) - - return pb.start() - .waitFor().let { - if (it != 0) { - throw RuntimeException( - "Failed to install archive to local maven " + - "repository: $archive $pomFile" - ) - } - } - } - - private fun createArchive() { - val archive = directory.resolve("${port.name}-${port.mavenVersion}.aar") - val pomFile = directory.resolve("${port.name}-${port.mavenVersion}.pom") - createZipFromDirectory(archive, packageDirectory) - createPom(pomFile) - if (publishToMavenLocal) { - installToLocalMaven(archive, pomFile) - } - } - - fun build() { - preparePackageDirectory() - makePackageMetadata() - for (module in port.modules) { - val moduleDirectory = modulesDirectory.resolve(module.name).apply { - mkdirs() - } - - makeModuleMetadata(module, moduleDirectory) - - if (module.includesPerAbi) { - TODO() - } else { - // TODO: Perform sanity check. - directory.resolve("install/${Abi.Arm}/include") - .copyRecursively(moduleDirectory.resolve("include")) - } - - val libsDir = moduleDirectory.resolve("libs").apply { mkdirs() } - for (abi in Abi.values()) { - installLibForAbi(module, abi, libsDir) - } - } - - installLicense() - - createAndroidManifest() - createArchive() - } -} diff --git a/src/main/kotlin/com/android/ndkports/Zip.kt b/src/main/kotlin/com/android/ndkports/Zip.kt deleted file mode 100644 index a23ff90..0000000 --- a/src/main/kotlin/com/android/ndkports/Zip.kt +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2019 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. - */ - -package com.android.ndkports - -import java.io.File -import java.io.FileInputStream -import java.io.FileOutputStream -import java.util.zip.ZipEntry -import java.util.zip.ZipOutputStream - -private fun zipDirectory(name: String, zipOut: ZipOutputStream) { - zipOut.putNextEntry(ZipEntry("$name/")) - zipOut.closeEntry() -} - -private fun zipFile(file: File, name: String, zipOut: ZipOutputStream) { - zipOut.putNextEntry(ZipEntry(name)) - FileInputStream(file).use { - it.copyTo(zipOut) - } -} - -private fun zip(file: File, name: String, zipOut: ZipOutputStream) { - if (file.isDirectory) { - zipDirectory(name, zipOut) - } else { - zipFile(file, name, zipOut) - } -} - -fun createZipFromDirectory(output: File, input: File) { - FileOutputStream(output).use { fos -> - ZipOutputStream(fos).use { zos -> - input.walk().filter { it != input }.forEach { - zip(it, it.relativeTo(input).path, zos) - } - } - } -}
\ No newline at end of file diff --git a/src/test/kotlin/com/android/ndkports/ResultTest.kt b/src/test/kotlin/com/android/ndkports/ResultTest.kt deleted file mode 100644 index 0abef18..0000000 --- a/src/test/kotlin/com/android/ndkports/ResultTest.kt +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2019 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. - */ - -package com.android.ndkports - -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.fail - -class ResultTest { - @Test - fun `onFailure executes block on failure`() { - val result = Result.Error("foo") - result.onFailure { - assertEquals("foo", it) - return - } - fail() - } - - @Test - fun `onFailure does not execute block on success`() { - val result = Result.Ok(Unit) - result.onFailure { fail() } - } - - @Test - fun `onFailure returns same result object`() { - val result = Result.Error(Unit) - assertEquals(result, result.onFailure {}) - } - - @Test - fun `onSuccess executes block on success`() { - val result = Result.Ok("foo") - result.onSuccess { - assertEquals("foo", it) - return - } - fail() - } - - @Test - fun `onSuccess does not execute block on failure`() { - val result = Result.Error(Unit) - result.onSuccess { fail() } - } - - @Test - fun `onSuccess returns same result object`() { - val result = Result.Ok(Unit) - assertEquals(result, result.onSuccess {}) - } -}
\ No newline at end of file |