Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions buildSrc/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
plugins {
`kotlin-dsl`
}

repositories {
mavenCentral()
}

dependencies {
testImplementation(kotlin("test"))
testImplementation(gradleTestKit())
}
121 changes: 121 additions & 0 deletions buildSrc/src/main/kotlin/NativeBuildResolver.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import org.gradle.api.file.Directory
import org.gradle.api.provider.Provider
import org.gradle.nativeplatform.platform.NativePlatform
import java.io.File

/**
* Gradle mostly uses Java os.arch names for architectures which feeds into default
* targetPlatform names. Notable exception Gradle 6.9.x reports MacOS/ARM as arm-v8.
*
* The Maven osdetector plugin (which we recommend to developers) uses different
* arch names, so that's what we need for artifacts.
*
* This class encapsulates both naming schemes as well as other per-platform information
* about native builds.
*
* This information is project independent and should rarely change over time.
*/
enum class NativeBuildVariant(
val os: String,
val mavenArch: String, // osdetector / Maven architecture name
val gradleArch: String, // Gradle name, used for NDK / toolchain etc
val boringBuildDir: String = "build64", // Where to find prebuilt BoringSSL libcrypto
val crossCompile: Boolean = false // Whether we can cross-compile for this architecture
) {
WINDOWS_X64("windows", "x86_64", "x86-64"),
LINUX_X64("linux", "x86_64", "x86-64"),
OSX_X64("osx", "x86_64", "x86-64", "build.x86", true),
OSX_ARM64("osx", "aarch_64", "aarch64", "build.arm", true);

override fun toString(): String =
"<os=$os target=$mavenArch gradle=$gradleArch boring=$boringBuildDir>"

companion object {
/**
* Finds the NativeBuildVariant for a particular Maven osdetector architecture.
*/
fun find(os: String, arch: String) = values().find { it.os == os && it.mavenArch == arch }

/**
* Finds the NativeBuildVariant for a particular Gradle architecture.
*/
fun findForGradle(os: String, arch: String) =
values().find { it.os == os && it.gradleArch == arch }

/**
* Finds all the NativeBuildVariants which can be built on a particular
* host OS.
*/
fun findAll(os: String, arch: String) = values().filter {
it.os == os && (it.mavenArch == arch || it.crossCompile)
}
}
}

/**
* Encapsulates native information for the current project, i.e. a combination of the
* NativeBuildVariant and information about the current project such as its build directory.
*/
data class NativeBuildInformation(
val buildDir: Provider<Directory>,
private val variant: NativeBuildVariant
) {
/** Classifier used for jars etc */
val mavenClassifier: String = "${variant.os}-${variant.mavenArch}"

/** Target platform name as used for Gradle native builds */
val targetPlatform: String = "${variant.os}_${variant.gradleArch}"

/** Directory for native resources for this builds */
val nativeResourcesDir: String
get() = File(buildDir.get().asFile, "$mavenClassifier/native-resources").absolutePath

/** Directory for jar resources for this builds */
val jarNativeResourcesDir: String
get() = File(nativeResourcesDir, "META-INF/native").absolutePath

/** Name of the native library directory in $BORINGSSL_HOME */
val boringBuildDir
get() = variant.boringBuildDir

override fun toString(): String =
"NativeBuildInfo<buildDir=${buildDir.orNull} variant=$variant>"
}

/**
* Replacement for the Groovy NativeBuildInfo enum using the above classes.
*
* Needs to be instantiated with the buildDir of the current build and then makes this
* available where needed in NativeBuildInformation objects.
*/
class NativeBuildResolver(private val buildDir: Provider<Directory>) {
// Wraps an immutable NativeBuildVariant with project information for this build */
private fun wrap(variant: NativeBuildVariant?) = variant?.let {
NativeBuildInformation(buildDir, it)
} ?: error("Null build variant")


/**
* Returns a NativeBuildInformation for the provided Maven architecture in the current project.
*/
fun find(os: String, arch: String) = wrap(NativeBuildVariant.find(os, arch))

/**
* Returns a NativeBuildInformation for the provided Gradle NativePlatform in the current project.
*/
fun find(nativePlatform: NativePlatform) = wrap(
NativeBuildVariant.findForGradle(
nativePlatform.operatingSystem.name,
nativePlatform.architecture.name
)
)

/**
* Returns a list of NativeBuildInformation for all the architectures buildable on a
* particular host OS and Architecture.
*/
fun findAll(os: String, arch: String): List<NativeBuildInformation> =
NativeBuildVariant.findAll(os, arch).map {
NativeBuildInformation(buildDir, it)
}
}
132 changes: 132 additions & 0 deletions buildSrc/src/test/kotlin/NativeBuildResolverTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import org.gradle.nativeplatform.platform.internal.DefaultNativePlatform
import org.gradle.testfixtures.ProjectBuilder
import java.nio.file.Files
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertNull
import kotlin.test.assertTrue

class NativeBuildResolverTest {
// Verify lookup by Maven osdetector names (os + maven arch).
@Test
fun findByOsdetectorExact() {
assertEquals(
NativeBuildVariant.OSX_ARM64,
NativeBuildVariant.find("osx", "aarch_64")
)
assertEquals(
NativeBuildVariant.LINUX_X64,
NativeBuildVariant.find("linux", "x86_64")
)
}

// Verify lookup by Gradle native platform names (os + gradle arch).
@Test
fun findByGradleExact() {
assertEquals(
NativeBuildVariant.OSX_X64,
NativeBuildVariant.findForGradle("osx", "x86-64")
)
assertEquals(
NativeBuildVariant.OSX_ARM64,
NativeBuildVariant.findForGradle("osx", "aarch64")
)
}

// Unknown or unsupported platform/arch combinations return null.
@Test
fun findUnknownReturnsNull() {
assertNull(NativeBuildVariant.find("linux", "armv7"))
assertNull(NativeBuildVariant.findForGradle("windows", "aarch64"))
}

// macOS returns both x86_64 and aarch64 variants (cross-compile).
@Test
fun findAllByOs() {
val osx = NativeBuildVariant.findAll("osx", "aarch_64").toSet()
assertEquals(setOf(NativeBuildVariant.OSX_X64, NativeBuildVariant.OSX_ARM64), osx)
}

// Linux returns only the matching variant (no cross-compile (yet)).
@Test
fun findAllLinuxReturnsSingleVariant() {
val linux = NativeBuildVariant.findAll("linux", "x86_64")
assertEquals(listOf(NativeBuildVariant.LINUX_X64), linux)
}

// Unknown OS returns an empty list.
@Test
fun findAllUnknownReturnsEmpty() {
assertTrue(NativeBuildVariant.findAll("solaris", "sparc").isEmpty())
}

// Windows x86_64 variant is resolvable.
@Test
fun findWindowsVariant() {
assertEquals(
NativeBuildVariant.WINDOWS_X64,
NativeBuildVariant.find("windows", "x86_64")
)
}

// Maven classifier, Gradle target, and BoringSSL dir strings are correct.
@Test
fun computedStringsAreStable() {
assertEquals(
"osx-aarch_64",
NativeBuildVariant.OSX_ARM64.let { "${it.os}-${it.mavenArch}" })
assertEquals(
"osx_aarch64",
NativeBuildVariant.OSX_ARM64.let { "${it.os}_${it.gradleArch}" })
assertEquals("build.arm", NativeBuildVariant.OSX_ARM64.boringBuildDir)
assertEquals("build64", NativeBuildVariant.LINUX_X64.boringBuildDir)
}

// NativeBuildInformation derives resource paths from project build dir.
@Test
fun directoriesAreDerivedCorrectlyFromBuilddir() {
val tmp = Files.createTempDirectory("nativeBuildTest").toFile().apply { deleteOnExit() }
val project = ProjectBuilder.builder().withProjectDir(tmp).build()
val info = NativeBuildInformation(project.layout.buildDirectory, NativeBuildVariant.OSX_X64)

assertTrue(
info.nativeResourcesDir.replace('\\', '/')
.endsWith("osx-x86_64/native-resources")
)
assertTrue(
info.jarNativeResourcesDir.replace('\\', '/')
.endsWith("osx-x86_64/native-resources/META-INF/native")
)
assertEquals("osx-x86_64", info.mavenClassifier)
assertEquals("osx_x86-64", info.targetPlatform)
}

// NativeBuildResolver maps a Gradle NativePlatform to the correct variant.
@Test
fun resolverFindsByNativePlatform() {
val project = ProjectBuilder.builder().build()
val resolver = NativeBuildResolver(project.layout.buildDirectory)
val platform = DefaultNativePlatform("osx_aarch64").apply {
operatingSystem("osx")
architecture("aarch64")
}

val info = resolver.find(platform)
assertNotNull(info)
assertEquals("osx-aarch_64", info.mavenClassifier)
assertEquals("osx_aarch64", info.targetPlatform)
}

// NativeBuildResolver wraps variants with project build directory.
@Test
fun resolverWrapsVariants() {
val project = ProjectBuilder.builder().build()
val resolver = NativeBuildResolver(project.layout.buildDirectory)

// There should only be a single Linux variant for now.
val info = resolver.findAll("linux", "x86_64").single()
assertEquals("linux-x86_64", info.mavenClassifier)
assertEquals(project.layout.buildDirectory.get().asFile, info.buildDir.get().asFile)
}
}