KT-62745: add missing BUILT_PRODUCTS_DIR test

This commit is contained in:
Andrey Yastrebov
2023-11-17 15:34:59 +01:00
committed by Space Team
parent 259a2f5ec0
commit f4097db0c2
2 changed files with 177 additions and 58 deletions
@@ -13,8 +13,9 @@ import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.condition.OS
import org.junit.jupiter.api.io.TempDir
import java.nio.file.Path
import kotlin.io.path.absolutePathString
import kotlin.io.path.appendText
import java.nio.file.attribute.PosixFilePermission
import kotlin.io.path.*
@OsCondition(supportedOn = [OS.MAC], enabledOnCI = [OS.MAC])
@DisplayName("Tests for K/N with Apple Framework")
@@ -28,27 +29,34 @@ class AppleFrameworkIT : KGPBaseTest() {
fun shouldAssembleAppleFrameworkForXcodeForIosArm64(
gradleVersion: GradleVersion,
) {
nativeProject(
"sharedAppleFramework",
gradleVersion,
buildOptions = defaultBuildOptions,
environmentVariables = EnvironmentalVariables(
buildOptions = defaultBuildOptions
) {
val environmentVariables = mapOf(
"CONFIGURATION" to "debug",
"SDK_NAME" to "iphoneos123",
"ARCHS" to "arm64",
"TARGET_BUILD_DIR" to "no use",
"FRAMEWORKS_FOLDER_PATH" to "no use"
),
) {
"FRAMEWORKS_FOLDER_PATH" to "no use",
"BUILT_PRODUCTS_DIR" to iosBuildProductsDir().absolutePathString(),
)
build("assembleDebugAppleFrameworkForXcodeIosArm64") {
build(
"assembleDebugAppleFrameworkForXcodeIosArm64",
environmentVariables = EnvironmentalVariables(environmentVariables)
) {
assertTasksExecuted(":shared:assembleDebugAppleFrameworkForXcodeIosArm64")
assertDirectoryInProjectExists("shared/build/xcode-frameworks/debug/iphoneos123/sdk.framework")
assertDirectoryInProjectExists("shared/build/xcode-frameworks/debug/iphoneos123/sdk.framework.dSYM")
}
build("assembleCustomDebugAppleFrameworkForXcodeIosArm64") {
build(
"assembleCustomDebugAppleFrameworkForXcodeIosArm64",
environmentVariables = EnvironmentalVariables(environmentVariables)
) {
assertTasksExecuted(":shared:assembleCustomDebugAppleFrameworkForXcodeIosArm64")
assertDirectoryInProjectExists("shared/build/xcode-frameworks/debug/iphoneos123/lib.framework")
assertDirectoryInProjectExists("shared/build/xcode-frameworks/debug/iphoneos123/lib.framework.dSYM")
@@ -73,7 +81,8 @@ class AppleFrameworkIT : KGPBaseTest() {
"SDK_NAME" to "iphonesimulator",
"ARCHS" to "arm64 x86_64",
"TARGET_BUILD_DIR" to "no use",
"FRAMEWORKS_FOLDER_PATH" to "no use"
"FRAMEWORKS_FOLDER_PATH" to "no use",
"BUILT_PRODUCTS_DIR" to iosBuildProductsDir().absolutePathString(),
)
build("assembleReleaseAppleFrameworkForXcode", environmentVariables = EnvironmentalVariables(environmentVariables)) {
assertTasksExecuted(":shared:linkReleaseFrameworkIosSimulatorArm64")
@@ -103,7 +112,8 @@ class AppleFrameworkIT : KGPBaseTest() {
"ARCHS" to "x86_64",
"EXPANDED_CODE_SIGN_IDENTITY" to "-",
"TARGET_BUILD_DIR" to testBuildDir.toString(),
"FRAMEWORKS_FOLDER_PATH" to "build/xcode-derived"
"FRAMEWORKS_FOLDER_PATH" to "build/xcode-derived",
"BUILT_PRODUCTS_DIR" to iosBuildProductsDir().absolutePathString(),
)
build(":shared:embedAndSignAppleFrameworkForXcode", environmentVariables = EnvironmentalVariables(environmentVariables)) {
assertTasksExecuted(":shared:assembleDebugAppleFrameworkForXcodeMacosX64")
@@ -129,7 +139,8 @@ class AppleFrameworkIT : KGPBaseTest() {
"SDK_NAME" to "iphoneos",
"ARCHS" to "arm64",
"TARGET_BUILD_DIR" to projectPath.absolutePathString(),
"FRAMEWORKS_FOLDER_PATH" to "build/xcode-derived"
"FRAMEWORKS_FOLDER_PATH" to "build/xcode-derived",
"BUILT_PRODUCTS_DIR" to iosBuildProductsDir().absolutePathString(),
)
build(":shared:embedAndSignAppleFrameworkForXcode", environmentVariables = EnvironmentalVariables(environmentVariables)) {
assertDirectoryInProjectExists("build/xcode-derived/sdk.framework")
@@ -154,7 +165,8 @@ class AppleFrameworkIT : KGPBaseTest() {
"ARCHS" to "arm64",
"EXPANDED_CODE_SIGN_IDENTITY" to "-",
"TARGET_BUILD_DIR" to projectPath.absolutePathString(),
"FRAMEWORKS_FOLDER_PATH" to "build/xcode-derived"
"FRAMEWORKS_FOLDER_PATH" to "build/xcode-derived",
"BUILT_PRODUCTS_DIR" to iosBuildProductsDir().absolutePathString(),
)
build(":shared:embedAndSignAppleFrameworkForXcode", environmentVariables = EnvironmentalVariables(environmentVariables)) {
assertDirectoryInProjectExists("build/xcode-derived/sdk.framework")
@@ -192,7 +204,8 @@ class AppleFrameworkIT : KGPBaseTest() {
"ARCHS" to "arm64",
"EXPANDED_CODE_SIGN_IDENTITY" to "-",
"TARGET_BUILD_DIR" to testBuildDir.toString(),
"FRAMEWORKS_FOLDER_PATH" to "testFrameworksDir"
"FRAMEWORKS_FOLDER_PATH" to "testFrameworksDir",
"BUILT_PRODUCTS_DIR" to iosBuildProductsDir().absolutePathString(),
)
buildAndAssertAllTasks(
registeredTasks = listOf(
@@ -229,14 +242,44 @@ class AppleFrameworkIT : KGPBaseTest() {
"EXPANDED_CODE_SIGN_IDENTITY" to "-",
"TARGET_BUILD_DIR" to projectPath.absolutePathString(),
"FRAMEWORKS_FOLDER_PATH" to "build/xcode-derived",
"ENABLE_USER_SCRIPT_SANDBOXING" to "YES"
"ENABLE_USER_SCRIPT_SANDBOXING" to "YES",
"BUILT_PRODUCTS_DIR" to iosBuildProductsDir().absolutePathString(),
)
buildAndFail(
":shared:embedAndSignAppleFrameworkForXcode",
environmentVariables = EnvironmentalVariables(environmentVariables)
) {
assertTasksFailed(":shared:embedAndSignAppleFrameworkForXcode")
assertOutputContains("Sandboxing for user scripts is currently enabled")
assertTasksFailed(":shared:checkSandboxAndWriteProtection")
assertOutputContains("You have sandboxing for user scripts enabled.")
}
}
}
@DisplayName("embedAndSignAppleFrameworkForXcode was registered with missing BUILT_PRODUCTS_DIR directory")
@OptIn(EnvironmentalVariablesOverride::class)
@GradleTest
fun shouldFailEmbedAndSignAppleFrameworkForXcodeWithMissingBuildProductsDir(
gradleVersion: GradleVersion,
) {
nativeProject(
"sharedAppleFramework",
gradleVersion,
) {
val environmentVariables = mapOf(
"CONFIGURATION" to "Debug",
"SDK_NAME" to "iphoneos",
"ARCHS" to "arm64",
"EXPANDED_CODE_SIGN_IDENTITY" to "-",
"TARGET_BUILD_DIR" to projectPath.absolutePathString(),
"FRAMEWORKS_FOLDER_PATH" to "build/xcode-derived",
"BUILT_PRODUCTS_DIR" to iosBuildProductsDir(true).absolutePathString(),
)
buildAndFail(
":shared:embedAndSignAppleFrameworkForXcode",
environmentVariables = EnvironmentalVariables(environmentVariables)
) {
assertTasksFailed(":shared:checkSandboxAndWriteProtection")
assertOutputContains("BUILT_PRODUCTS_DIR is not accessible, probably you have sandboxing for user scripts enabled.")
}
}
}
@@ -255,7 +298,8 @@ class AppleFrameworkIT : KGPBaseTest() {
mapOf(
"CONFIGURATION" to "Debug",
"SDK_NAME" to "iphoneos",
"ARCHS" to "arm64"
"ARCHS" to "arm64",
"BUILT_PRODUCTS_DIR" to iosBuildProductsDir().absolutePathString(),
)
)
buildAndAssertAllTasks(
@@ -303,7 +347,8 @@ class AppleFrameworkIT : KGPBaseTest() {
"SDK_NAME" to "iphoneos123",
"ARCHS" to "arm64",
"TARGET_BUILD_DIR" to "no use",
"FRAMEWORKS_FOLDER_PATH" to "no use"
"FRAMEWORKS_FOLDER_PATH" to "no use",
"BUILT_PRODUCTS_DIR" to iosBuildProductsDir().absolutePathString(),
)
)
@@ -339,7 +384,8 @@ class AppleFrameworkIT : KGPBaseTest() {
"SDK_NAME" to "iphoneos123",
"ARCHS" to "arm64",
"TARGET_BUILD_DIR" to "no use",
"FRAMEWORKS_FOLDER_PATH" to "no use"
"FRAMEWORKS_FOLDER_PATH" to "no use",
"BUILT_PRODUCTS_DIR" to iosBuildProductsDir().absolutePathString(),
)
)
@@ -375,7 +421,8 @@ class AppleFrameworkIT : KGPBaseTest() {
"SDK_NAME" to "iphoneos123",
"ARCHS" to "arm64",
"TARGET_BUILD_DIR" to "no use",
"FRAMEWORKS_FOLDER_PATH" to "no use"
"FRAMEWORKS_FOLDER_PATH" to "no use",
"BUILT_PRODUCTS_DIR" to iosBuildProductsDir().absolutePathString(),
)
)
@@ -405,7 +452,8 @@ class AppleFrameworkIT : KGPBaseTest() {
"SDK_NAME" to "iphoneos123",
"ARCHS" to "arm64",
"TARGET_BUILD_DIR" to "no use",
"FRAMEWORKS_FOLDER_PATH" to "no use"
"FRAMEWORKS_FOLDER_PATH" to "no use",
"BUILT_PRODUCTS_DIR" to iosBuildProductsDir().absolutePathString(),
)
)
@@ -435,7 +483,8 @@ class AppleFrameworkIT : KGPBaseTest() {
"SDK_NAME" to "iphoneos123",
"ARCHS" to "arm64",
"TARGET_BUILD_DIR" to "no use",
"FRAMEWORKS_FOLDER_PATH" to "no use"
"FRAMEWORKS_FOLDER_PATH" to "no use",
"BUILT_PRODUCTS_DIR" to iosBuildProductsDir().absolutePathString(),
)
)
@@ -469,7 +518,8 @@ class AppleFrameworkIT : KGPBaseTest() {
"SDK_NAME" to "iphoneos123",
"ARCHS" to "arm64",
"TARGET_BUILD_DIR" to "no use",
"FRAMEWORKS_FOLDER_PATH" to "no use"
"FRAMEWORKS_FOLDER_PATH" to "no use",
"BUILT_PRODUCTS_DIR" to iosBuildProductsDir().absolutePathString(),
)
)
@@ -534,4 +584,17 @@ class AppleFrameworkIT : KGPBaseTest() {
}
}
}
}
private val GradleProject.darwinBuildProductsDir: Path
get() = projectPath.resolve("DerivedSources").apply {
if (notExists()) {
createDirectory()
}
}
private fun GradleProject.iosBuildProductsDir(writeProtected: Boolean = false): Path = darwinBuildProductsDir.apply {
if (writeProtected) {
setPosixFilePermissions(setOf(PosixFilePermission.OWNER_READ))
}
}
@@ -30,11 +30,14 @@ import org.jetbrains.kotlin.gradle.utils.mapToFile
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.util.capitalizeDecapitalize.toLowerCaseAsciiOnly
import java.io.File
import java.io.IOException
import java.nio.file.Files
import javax.inject.Inject
internal object AppleXcodeTasks {
const val embedAndSignTaskPrefix = "embedAndSign"
const val embedAndSignTaskPostfix = "AppleFrameworkForXcode"
const val checkSandboxAndWriteProtection = "checkSandboxAndWriteProtection"
}
private object XcodeEnvironment {
@@ -74,9 +77,15 @@ private object XcodeEnvironment {
return File(xcodeTargetBuildDir, xcodeFrameworksFolderPath).absoluteFile
}
val buildProductsDir: File?
get() {
val derivedFileDir = System.getenv("BUILT_PRODUCTS_DIR") ?: return null
return File(derivedFileDir).absoluteFile
}
val sign: String? get() = System.getenv("EXPANDED_CODE_SIGN_IDENTITY")
val userScriptSandboxingEnabled: String? get() = System.getenv("ENABLE_USER_SCRIPT_SANDBOXING")
val userScriptSandboxingEnabled: Boolean get() = System.getenv("ENABLE_USER_SCRIPT_SANDBOXING") == "YES"
override fun toString() = """
XcodeEnvironment:
@@ -84,7 +93,9 @@ private object XcodeEnvironment {
targets=$targets
frameworkSearchDir=$frameworkSearchDir
embeddedFrameworksDir=$embeddedFrameworksDir
buildProductsDir=$buildProductsDir
sign=$sign
userScriptSandboxingEnabled=$userScriptSandboxingEnabled
""".trimIndent()
}
@@ -143,6 +154,63 @@ private fun Project.registerAssembleAppleFrameworkTask(framework: Framework): Ta
}
}
private fun fireEnvException(frameworkTaskName: String) {
val envBuildType = XcodeEnvironment.buildType
val envConfiguration = System.getenv("CONFIGURATION")
if (envConfiguration != null && envBuildType == null) {
throw IllegalStateException(
"Unable to detect Kotlin framework build type for CONFIGURATION=$envConfiguration automatically. " +
"Specify 'KOTLIN_FRAMEWORK_BUILD_TYPE' to 'debug' or 'release'" +
"\n$XcodeEnvironment"
)
} else {
throw IllegalStateException(
"Please run the $frameworkTaskName task from Xcode " +
"('SDK_NAME', 'CONFIGURATION', 'TARGET_BUILD_DIR', 'ARCHS' and 'FRAMEWORKS_FOLDER_PATH' not provided)" +
"\n$XcodeEnvironment"
)
}
}
private fun fireSandboxException(frameworkTaskName: String) {
val userScriptSandboxingEnabled = XcodeEnvironment.userScriptSandboxingEnabled
val message = if (userScriptSandboxingEnabled) "You " else "BUILT_PRODUCTS_DIR is not accessible, probably you "
throw IllegalStateException(
message +
"have sandboxing for user scripts enabled." +
"\nTo make the $frameworkTaskName task pass, disable this feature. " +
"\nIn your Xcode project, navigate to \"Build Setting\", " +
"and under \"Build Options\" set \"User script sandboxing\" (ENABLE_USER_SCRIPT_SANDBOXING) to \"NO\". " +
"\nThen, run \"./gradlew --stop\" to stop the Gradle daemon" +
"\nFor more information, see documentation: https://jb.gg/ltd9e6"
)
}
private enum class DirAccessibility {
ACCESSIBLE,
NOT_ACCESSIBLE,
DOES_NOT_EXIST
}
private fun buildProductsDirAccessibility(): DirAccessibility {
val buildProductsDir = XcodeEnvironment.buildProductsDir
return if (buildProductsDir != null) {
try {
Files.createDirectories(buildProductsDir.toPath())
val tempFile = File.createTempFile("sandbox", ".tmp", buildProductsDir)
if (tempFile.exists()) {
tempFile.delete()
}
DirAccessibility.ACCESSIBLE
} catch (e: IOException) {
DirAccessibility.NOT_ACCESSIBLE
}
} else {
DirAccessibility.DOES_NOT_EXIST
}
}
internal fun Project.registerEmbedAndSignAppleFrameworkTask(framework: Framework) {
val envBuildType = XcodeEnvironment.buildType
val envTargets = XcodeEnvironment.targets
@@ -151,48 +219,37 @@ internal fun Project.registerEmbedAndSignAppleFrameworkTask(framework: Framework
val envSign = XcodeEnvironment.sign
val userScriptSandboxingEnabled = XcodeEnvironment.userScriptSandboxingEnabled
val frameworkTaskName = lowerCamelCaseName(AppleXcodeTasks.embedAndSignTaskPrefix, framework.namePrefix, AppleXcodeTasks.embedAndSignTaskPostfix)
val frameworkTaskName = lowerCamelCaseName(
AppleXcodeTasks.embedAndSignTaskPrefix,
framework.namePrefix,
AppleXcodeTasks.embedAndSignTaskPostfix
)
if (envBuildType == null || envTargets.isEmpty() || envEmbeddedFrameworksDir == null || envFrameworkSearchDir == null) {
locateOrRegisterTask<DefaultTask>(frameworkTaskName) { task ->
task.group = BasePlugin.BUILD_GROUP
task.description = "Embed and sign ${framework.namePrefix} framework as requested by Xcode's environment variables"
task.doFirst {
val envConfiguration = System.getenv("CONFIGURATION")
if (envConfiguration != null && envBuildType == null) {
throw IllegalStateException(
"Unable to detect Kotlin framework build type for CONFIGURATION=$envConfiguration automatically. " +
"Specify 'KOTLIN_FRAMEWORK_BUILD_TYPE' to 'debug' or 'release'" +
"\n$XcodeEnvironment"
)
} else {
throw IllegalStateException(
"Please run the $frameworkTaskName task from Xcode " +
"('SDK_NAME', 'CONFIGURATION', 'TARGET_BUILD_DIR', 'ARCHS' and 'FRAMEWORKS_FOLDER_PATH' not provided)" +
"\n$XcodeEnvironment"
)
}
fireEnvException(frameworkTaskName)
}
}
return
}
if (userScriptSandboxingEnabled == "YES") {
locateOrRegisterTask<DefaultTask>(frameworkTaskName) { task ->
task.group = BasePlugin.BUILD_GROUP
task.description = "Embed and sign ${framework.namePrefix} framework as requested by Xcode's environment variables"
task.doFirst {
throw IllegalStateException(
"You have sandboxing for user scripts enabled. " +
"\nTo make the $frameworkTaskName task pass, disable this feature. " +
"\nIn your Xcode project, navigate to \"Build Setting\", " +
"and under \"Build Options\" set \"User script sandboxing\" (ENABLE_USER_SCRIPT_SANDBOXING) to \"NO\". " +
"\nThen, run \"./gradlew --stop\" to stop the Gradle daemon" +
"\nFor more information, see documentation: https://jb.gg/ltd9e6"
)
val checkSandboxAndWriteProtectionTask = locateOrRegisterTask<DefaultTask>(AppleXcodeTasks.checkSandboxAndWriteProtection) { task ->
task.group = BasePlugin.BUILD_GROUP
task.description = "Check BUILT_PRODUCTS_DIR accessible and ENABLE_USER_SCRIPT_SANDBOXING not enabled"
task.doFirst {
val dirAccessible = buildProductsDirAccessibility()
when (dirAccessible) {
DirAccessibility.NOT_ACCESSIBLE -> fireSandboxException(frameworkTaskName)
DirAccessibility.DOES_NOT_EXIST,
DirAccessibility.ACCESSIBLE
-> if (userScriptSandboxingEnabled) {
fireSandboxException(frameworkTaskName)
}
}
}
return
}
val embedAndSignTask = locateOrRegisterTask<FrameworkCopy>(frameworkTaskName) { task ->
@@ -203,12 +260,10 @@ internal fun Project.registerEmbedAndSignAppleFrameworkTask(framework: Framework
property("type", envBuildType)
property("targets", envTargets)
property("embeddedFrameworksDir", envEmbeddedFrameworksDir)
property("userScriptSandboxingEnabled", userScriptSandboxingEnabled)
if (envSign != null) {
property("sign", envSign)
}
if (userScriptSandboxingEnabled != null) {
property("userScriptSandboxingEnabled", userScriptSandboxingEnabled)
}
}
}
@@ -217,6 +272,7 @@ internal fun Project.registerEmbedAndSignAppleFrameworkTask(framework: Framework
embedAndSignTask.configure { task ->
val frameworkFile = framework.outputFile
task.dependsOn(checkSandboxAndWriteProtectionTask)
task.dependsOn(assembleTask)
task.sourceFramework.fileProvider(appleFrameworkDir(envFrameworkSearchDir).map { it.resolve(frameworkFile.name) })
task.destinationDirectory.set(envEmbeddedFrameworksDir)