diff --git a/Samples/SwiftJavaExtractFFMSampleApp/Package.swift b/Samples/SwiftJavaExtractFFMSampleApp/Package.swift index 98d1bd33f..564889239 100644 --- a/Samples/SwiftJavaExtractFFMSampleApp/Package.swift +++ b/Samples/SwiftJavaExtractFFMSampleApp/Package.swift @@ -75,7 +75,7 @@ let package = Package( .unsafeFlags(["-I\(javaIncludePath)", "-I\(javaPlatformIncludePath)"]) ], plugins: [ - .plugin(name: "JExtractSwiftPlugin", package: "swift-java"), + // .plugin(name: "JExtractSwiftPlugin", package: "swift-java"), ] ), ] diff --git a/Samples/SwiftJavaExtractFFMSampleApp/build.gradle b/Samples/SwiftJavaExtractFFMSampleApp/build.gradle deleted file mode 100644 index dcfacdd39..000000000 --- a/Samples/SwiftJavaExtractFFMSampleApp/build.gradle +++ /dev/null @@ -1,197 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2024 Apple Inc. and the Swift.org project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift.org project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import groovy.json.JsonSlurper -import org.swift.swiftkit.gradle.BuildUtils - -import java.nio.file.* - -plugins { - id("build-logic.java-application-conventions") - id("me.champeau.jmh") version "0.7.2" -} - -group = "org.swift.swiftkit" -version = "1.0-SNAPSHOT" - -repositories { - mavenCentral() -} - -java { - toolchain { - languageVersion.set(JavaLanguageVersion.of(25)) - } -} - -def swiftProductsWithJExtractPlugin() { - def stdout = new ByteArrayOutputStream() - def stderr = new ByteArrayOutputStream() - - def processBuilder = new ProcessBuilder('swift', 'package', 'describe', '--type', 'json') - def process = processBuilder.start() - - process.consumeProcessOutput(stdout, stderr) - process.waitFor() - - def exitValue = process.exitValue() - def jsonOutput = stdout.toString() - - if (exitValue == 0) { - def json = new JsonSlurper().parseText(jsonOutput) - def products = json.targets - .findAll { target -> - target.product_dependencies?.contains("JExtractSwiftPlugin") - } - .collectMany { target -> - target.product_memberships ?: [] - } - return products - } else { - logger.warn("Command failed: ${stderr.toString()}") - return [] - } -} - - -def swiftCheckValid = tasks.register("swift-check-valid", Exec) { - commandLine "swift" - args("-version") -} - -def jextract = tasks.register("jextract", Exec) { - description = "Generate Java wrappers for swift target" - dependsOn swiftCheckValid - - // only because we depend on "live developing" the plugin while using this project to test it - inputs.file(new File(rootDir, "Package.swift")) - inputs.dir(new File(rootDir, "Sources")) - - // If the package description changes, we should execute jextract again, maybe we added jextract to new targets - inputs.file(new File(projectDir, "Package.swift")) - - // monitor all targets/products which depend on the JExtract plugin - swiftProductsWithJExtractPlugin().each { - logger.info("[swift-java:jextract (Gradle)] Swift input target: ${it}") - inputs.dir(new File(layout.projectDirectory.asFile, "Sources/${it}".toString())) - } - outputs.dir(layout.buildDirectory.dir("../.build/plugins/outputs/${layout.projectDirectory.asFile.getName().toLowerCase()}")) - - File baseSwiftPluginOutputsDir = layout.buildDirectory.dir("../.build/plugins/outputs/").get().asFile - if (!baseSwiftPluginOutputsDir.exists()) { - baseSwiftPluginOutputsDir.mkdirs() - } - Files.walk(layout.buildDirectory.dir("../.build/plugins/outputs/").get().asFile.toPath()).each { - // Add any Java sources generated by the plugin to our sourceSet - if (it.endsWith("JExtractSwiftPlugin/src/generated/java")) { - outputs.dir(it) - } - } - - workingDir = layout.projectDirectory - commandLine "swift" - // FIXME: disable prebuilts until swift-syntax isn't broken on 6.2 anymore: https://github.com/swiftlang/swift-java/issues/418 - args("build", "--disable-experimental-prebuilts") // since Swift targets which need to be jextract-ed have the jextract build plugin, we just need to build - // If we wanted to execute a specific subcommand, we can like this: - // args("run",/* - // "swift-java", "jextract", - // "--swift-module", "MySwiftLibrary", - // // java.package is obtained from the swift-java.config in the swift module - // "--output-java", "${layout.buildDirectory.dir(".build/plugins/outputs/${layout.projectDirectory.asFile.getName().toLowerCase()}/JExtractSwiftPlugin/src/generated/java").get()}", - // "--output-swift", "${layout.buildDirectory.dir(".build/plugins/outputs/${layout.projectDirectory.asFile.getName().toLowerCase()}/JExtractSwiftPlugin/Sources").get()}", - // "--log-level", (logging.level <= LogLevel.INFO ? "debug" : */"info") - // ) -} - -// Add the java-swift generated Java sources -sourceSets { - main { - java { - srcDir(jextract) - } - } - test { - java { - srcDir(jextract) - } - } - jmh { - java { - srcDir(jextract) - } - } -} - -tasks.build { - dependsOn("jextract") -} - - -def cleanSwift = tasks.register("cleanSwift", Exec) { - workingDir = layout.projectDirectory - commandLine "swift" - args("package", "clean") -} -tasks.clean { - dependsOn("cleanSwift") -} - -dependencies { - implementation(project(':SwiftKitCore')) - implementation(project(':SwiftKitFFM')) - - testRuntimeOnly("org.junit.platform:junit-platform-launcher") // TODO: workaround for not finding junit: https://github.com/gradle/gradle/issues/34512 // TODO: workaround for not finding junit: https://github.com/gradle/gradle/issues/34512 - testImplementation(platform("org.junit:junit-bom:5.10.0")) - testImplementation("org.junit.jupiter:junit-jupiter") -} - -tasks.named('test', Test) { - useJUnitPlatform() -} - -application { - mainClass = "com.example.swift.HelloJava2Swift" - - applicationDefaultJvmArgs = [ - "--enable-native-access=ALL-UNNAMED", - - // Include the library paths where our dylibs are that we want to load and call - "-Djava.library.path=" + - (BuildUtils.javaLibraryPaths(rootDir) + - BuildUtils.javaLibraryPaths(project.projectDir)).join(":"), - - - // Enable tracing downcalls (to Swift) - "-Djextract.trace.downcalls=true" - ] -} - -String jmhIncludes = findProperty("jmhIncludes") - -jmh { - if (jmhIncludes != null) { - includes = [jmhIncludes] - } - - jvmArgsAppend = [ - "--enable-native-access=ALL-UNNAMED", - - "-Djava.library.path=" + - (BuildUtils.javaLibraryPaths(rootDir) + - BuildUtils.javaLibraryPaths(project.projectDir)).join(":"), - - // Enable tracing downcalls (to Swift) - "-Djextract.trace.downcalls=false" - ] -} diff --git a/Samples/SwiftJavaExtractFFMSampleApp/build.gradle.kts b/Samples/SwiftJavaExtractFFMSampleApp/build.gradle.kts new file mode 100644 index 000000000..4800da0d0 --- /dev/null +++ b/Samples/SwiftJavaExtractFFMSampleApp/build.gradle.kts @@ -0,0 +1,111 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift.org project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift.org project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonObject +import kotlinx.serialization.json.jsonArray +import kotlinx.serialization.json.jsonObject +import kotlinx.serialization.json.jsonPrimitive +import org.swift.swiftkit.gradle.BuildUtils +import java.io.ByteArrayOutputStream +import java.io.File + +import java.nio.file.* +import kotlin.concurrent.thread + +plugins { + id("build-logic.java-application-conventions") + id("me.champeau.jmh") version "0.7.2" + id("swiftpm-import-plugin") +} + +group = "org.swift.swiftkit" +version = "1.0-SNAPSHOT" + +repositories { + mavenCentral() +} + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(25)) + } +} + +// Add the java-swift generated Java sources + + +val cleanSwift = tasks.register("cleanSwift", Exec::class.java) { + workingDir = layout.projectDirectory.asFile + commandLine = listOf("swift") + args("package", "clean") +} +tasks.clean { + dependsOn("cleanSwift") +} + +dependencies { + implementation(project(":SwiftKitCore")) + implementation(project(":SwiftKitFFM")) + + testRuntimeOnly("org.junit.platform:junit-platform-launcher") // TODO: workaround for not finding junit: https://github.com/gradle/gradle/issues/34512 // TODO: workaround for not finding junit: https://github.com/gradle/gradle/issues/34512 + testImplementation(platform("org.junit:junit-bom:5.10.0")) + testImplementation("org.junit.jupiter:junit-jupiter") +} + +tasks.named("test", Test::class.java) { + useJUnitPlatform() +} + +java { + toolchain.languageVersion.set(JavaLanguageVersion.of(25)) +} + +application { + mainClass = "com.example.swift.HelloJava2Swift" +} + +val jmhIncludes = findProperty("jmhIncludes") as? String + +jmh { + if (jmhIncludes != null) { + includes.set(setOf(jmhIncludes)) + } + + jvmArgsAppend = listOf( + "--enable-native-access=ALL-UNNAMED", + + "-Djava.library.path=" + + ((BuildUtils.javaLibraryPaths(rootDir) as Iterable) + + (BuildUtils.javaLibraryPaths(project.projectDir) as Iterable)).joinToString(":"), + + // Enable tracing downcalls (to Swift) + "-Djextract.trace.downcalls=false" + ) +} + +swiftPMDependencies { + `package`( + url = url("https://github.com/abdulowork/SwiftPMImport.git"), + version = exact("1.0.12"), + products = listOf(product("SwiftPMImport", importedModules = setOf("SwiftTarget", "TargetWithAlgorithms"))), + ) + + localPackage( + path = projectDir, + products = listOf("MySwiftLibrary") + ) + + swiftJavaRepository.set(rootDir) +} \ No newline at end of file diff --git a/Samples/SwiftJavaExtractFFMSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java b/Samples/SwiftJavaExtractFFMSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java index a13125478..ed0c8e025 100644 --- a/Samples/SwiftJavaExtractFFMSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java +++ b/Samples/SwiftJavaExtractFFMSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java @@ -18,10 +18,13 @@ // Import javakit/swiftkit support libraries +import com.example.swift.TargetWithAlgorithms.TargetWithAlgorithms; import org.swift.swiftkit.core.CallTraces; import org.swift.swiftkit.core.SwiftLibraries; import org.swift.swiftkit.ffm.AllocatingSwiftArena; import org.swift.swiftkit.ffm.SwiftRuntime; +import com.example.swift.MySwiftLibrary.*; +import com.example.swift.SwiftTarget.*; import java.util.Optional; import java.util.OptionalLong; @@ -57,6 +60,15 @@ static void examples() { try (var arena = AllocatingSwiftArena.ofConfined()) { MySwiftClass obj = MySwiftClass.init(2222, 7777, arena); + SwiftApi objFromAnotherLibrary = SwiftApi.init(1, arena); + objFromAnotherLibrary.printFoo(); + + var data = TargetWithAlgorithms.randomSampleWrapper("hello world hello world hello world", 10, arena); + data.withUnsafeBytes((retBytes) -> { + var str = retBytes.getString(0); + CallTraces.trace("Use API with transitive Algorithms usage = " + str); + }); + // just checking retains/releases work CallTraces.trace("retainCount = " + SwiftRuntime.retainCount(obj)); SwiftRuntime.retain(obj); diff --git a/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/com/example/swift/DataImportTest.java b/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/com/example/swift/DataImportTest.java index 52a63f815..23a724429 100644 --- a/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/com/example/swift/DataImportTest.java +++ b/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/com/example/swift/DataImportTest.java @@ -14,6 +14,7 @@ package com.example.swift; +import com.example.swift.MySwiftLibrary.*; import org.junit.jupiter.api.Test; import org.swift.swiftkit.ffm.AllocatingSwiftArena; diff --git a/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/com/example/swift/MultipleTypesFromSingleFileTest.java b/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/com/example/swift/MultipleTypesFromSingleFileTest.java index d3cd791c0..084327903 100644 --- a/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/com/example/swift/MultipleTypesFromSingleFileTest.java +++ b/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/com/example/swift/MultipleTypesFromSingleFileTest.java @@ -24,6 +24,8 @@ import static org.junit.jupiter.api.Assertions.*; +import com.example.swift.MySwiftLibrary.*; + public class MultipleTypesFromSingleFileTest { @Test diff --git a/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/com/example/swift/MySwiftClassTest.java b/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/com/example/swift/MySwiftClassTest.java index 1f0464eb6..80ec8adb7 100644 --- a/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/com/example/swift/MySwiftClassTest.java +++ b/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/com/example/swift/MySwiftClassTest.java @@ -24,6 +24,8 @@ import static org.junit.jupiter.api.Assertions.*; +import com.example.swift.MySwiftLibrary.*; + public class MySwiftClassTest { void checkPaths(Throwable throwable) { diff --git a/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/com/example/swift/MySwiftLibraryTest.java b/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/com/example/swift/MySwiftLibraryTest.java index a6c34b579..34afd69ab 100644 --- a/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/com/example/swift/MySwiftLibraryTest.java +++ b/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/com/example/swift/MySwiftLibraryTest.java @@ -21,10 +21,12 @@ import static org.junit.jupiter.api.Assertions.*; +import com.example.swift.MySwiftLibrary.*; + public class MySwiftLibraryTest { static { - System.loadLibrary(MySwiftLibrary.LIB_NAME); +// System.loadLibrary(MySwiftLibrary.LIB_NAME); } @Test diff --git a/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/com/example/swift/OptionalImportTest.java b/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/com/example/swift/OptionalImportTest.java index 57e8dba61..24fe56261 100644 --- a/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/com/example/swift/OptionalImportTest.java +++ b/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/com/example/swift/OptionalImportTest.java @@ -22,6 +22,8 @@ import static org.junit.jupiter.api.Assertions.*; +import com.example.swift.MySwiftLibrary.*; + public class OptionalImportTest { @Test void test_Optional_receive() { diff --git a/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/com/example/swift/SwiftTypeInSubDirectoryTest.java b/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/com/example/swift/SwiftTypeInSubDirectoryTest.java index 87bcfde80..85ed9e049 100644 --- a/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/com/example/swift/SwiftTypeInSubDirectoryTest.java +++ b/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/com/example/swift/SwiftTypeInSubDirectoryTest.java @@ -24,6 +24,8 @@ import static org.junit.jupiter.api.Assertions.*; +import com.example.swift.MySwiftLibrary.*; + public class SwiftTypeInSubDirectoryTest { @Test diff --git a/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/com/example/swift/UnsignedNumbersTest.java b/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/com/example/swift/UnsignedNumbersTest.java index beb0f817f..d8389f5c1 100644 --- a/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/com/example/swift/UnsignedNumbersTest.java +++ b/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/com/example/swift/UnsignedNumbersTest.java @@ -17,6 +17,8 @@ import org.junit.jupiter.api.Test; import org.swift.swiftkit.ffm.AllocatingSwiftArena; +import com.example.swift.MySwiftLibrary.*; + public class UnsignedNumbersTest { @Test void take_uint32() { diff --git a/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/com/example/swift/WithBufferTest.java b/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/com/example/swift/WithBufferTest.java index 54206423c..8d715ebbd 100644 --- a/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/com/example/swift/WithBufferTest.java +++ b/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/com/example/swift/WithBufferTest.java @@ -25,6 +25,8 @@ import java.util.concurrent.atomic.AtomicLong; import java.util.stream.IntStream; +import com.example.swift.MySwiftLibrary.*; + public class WithBufferTest { @Test void test_withBuffer() { diff --git a/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/org/swift/swiftkitffm/MySwiftClassTest.java b/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/org/swift/swiftkitffm/MySwiftClassTest.java index 17db4c418..4855b2d44 100644 --- a/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/org/swift/swiftkitffm/MySwiftClassTest.java +++ b/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/org/swift/swiftkitffm/MySwiftClassTest.java @@ -18,7 +18,7 @@ import org.junit.jupiter.api.Test; -import com.example.swift.MySwiftClass; +import com.example.swift.MySwiftLibrary.MySwiftClass; import org.swift.swiftkit.ffm.AllocatingSwiftArena; import org.swift.swiftkit.ffm.SwiftRuntime; diff --git a/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/org/swift/swiftkitffm/MySwiftStructTest.java b/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/org/swift/swiftkitffm/MySwiftStructTest.java index d904f7e82..68fbf0c42 100644 --- a/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/org/swift/swiftkitffm/MySwiftStructTest.java +++ b/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/org/swift/swiftkitffm/MySwiftStructTest.java @@ -14,7 +14,7 @@ package org.swift.swiftkitffm; -import com.example.swift.MySwiftStruct; +import com.example.swift.MySwiftLibrary.MySwiftStruct; import org.junit.jupiter.api.Test; import org.swift.swiftkit.ffm.AllocatingSwiftArena; diff --git a/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/org/swift/swiftkitffm/SwiftArenaTest.java b/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/org/swift/swiftkitffm/SwiftArenaTest.java index 08dd9b1bc..aa6444554 100644 --- a/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/org/swift/swiftkitffm/SwiftArenaTest.java +++ b/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/org/swift/swiftkitffm/SwiftArenaTest.java @@ -14,8 +14,8 @@ package org.swift.swiftkitffm; -import com.example.swift.MySwiftClass; -import com.example.swift.MySwiftStruct; +import com.example.swift.MySwiftLibrary.MySwiftClass; +import com.example.swift.MySwiftLibrary.MySwiftStruct; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.DisabledIf; import org.swift.swiftkit.core.util.PlatformUtils; diff --git a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+SwiftThunkPrinting.swift b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+SwiftThunkPrinting.swift index 09de17782..9f22f57d0 100644 --- a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+SwiftThunkPrinting.swift +++ b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+SwiftThunkPrinting.swift @@ -108,6 +108,7 @@ extension FFMSwift2JavaGenerator { // Generated by swift-java import SwiftRuntimeFunctions + import \(self.swiftModuleName) """) @@ -136,6 +137,7 @@ extension FFMSwift2JavaGenerator { // Generated by swift-java import SwiftRuntimeFunctions + import \(self.swiftModuleName) """ ) diff --git a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift index a0f68ee00..5814d51e6 100644 --- a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift +++ b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift @@ -368,7 +368,7 @@ extension FFMSwift2JavaGenerator { func printClassConstants(printer: inout CodePrinter) { printer.print( """ - static final String LIB_NAME = "\(swiftModuleName)"; + static final String LIB_NAME = "\(swiftModuleName)_Bridges"; static final Arena LIBRARY_ARENA = Arena.ofAuto(); """ ) diff --git a/Sources/JExtractSwiftLib/Swift2Java.swift b/Sources/JExtractSwiftLib/Swift2Java.swift index 30b6bd924..eb634d7d6 100644 --- a/Sources/JExtractSwiftLib/Swift2Java.swift +++ b/Sources/JExtractSwiftLib/Swift2Java.swift @@ -49,7 +49,7 @@ public struct SwiftToJava { let inputPaths = inputSwift.split(separator: ",").map { URL(string: String($0))! } log.info("Input paths = \(inputPaths)") - let allFiles = collectAllFiles(suffix: ".swift", in: inputPaths, log: translator.log) + let allFiles = inputPaths // collectAllFiles(suffix: ".swiftinterface", in: inputPaths, log: translator.log) // Register files to the translator. let fileManager = FileManager.default diff --git a/buildSrc/src/main/groovy/org/swift/swiftkit/gradle/BuildUtils.groovy b/buildSrc/src/main/groovy/org/swift/swiftkit/gradle/BuildUtils.groovy index 9512307f6..32eaf758b 100644 --- a/buildSrc/src/main/groovy/org/swift/swiftkit/gradle/BuildUtils.groovy +++ b/buildSrc/src/main/groovy/org/swift/swiftkit/gradle/BuildUtils.groovy @@ -30,7 +30,7 @@ final class BuildUtils { } /// Find library paths for 'java.library.path' when running or testing projects inside this build. - static def javaLibraryPaths(File rootDir) { + static def javaLibraryPaths(java.io.File rootDir) { def osName = System.getProperty("os.name") def osArch = System.getProperty("os.arch") def isLinux = osName.toLowerCase(Locale.getDefault()).contains("linux") diff --git a/settings.gradle b/settings.gradle index 9bf38ba51..ccd8b3224 100644 --- a/settings.gradle +++ b/settings.gradle @@ -14,6 +14,7 @@ pluginManagement { includeBuild("BuildLogic") + includeBuild("swiftPMImportPlugin") } rootProject.name = "swift-java" @@ -23,7 +24,7 @@ include "SwiftKitFFM" // Include sample apps -- you can run them via `gradle Name:run` new File(rootDir, "Samples").listFiles().each { - if (it.directory && new File(it, 'build.gradle').exists()) { + if (it.directory && (new File(it, 'build.gradle').exists() || new File(it, 'build.gradle.kts').exists())) { include ":Samples:${it.name}" } } diff --git a/swiftPMImportPlugin/build.gradle.kts b/swiftPMImportPlugin/build.gradle.kts new file mode 100644 index 000000000..f8ae2185c --- /dev/null +++ b/swiftPMImportPlugin/build.gradle.kts @@ -0,0 +1,25 @@ +plugins { + kotlin("jvm") version "2.2.21" + `java-gradle-plugin` +} + +dependencies { + implementation("com.google.code.gson:gson:2.13.2") +} + +java { + targetCompatibility = JavaVersion.VERSION_1_8 +} + +kotlin { + compilerOptions.jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_1_8) +} + +gradlePlugin { + plugins { + create("swift_java_swiftpm_import_plugin") { + id = "swiftpm-import-plugin" + implementationClass = "SwiftPMImportPlugin" + } + } +} \ No newline at end of file diff --git a/swiftPMImportPlugin/settings.gradle.kts b/swiftPMImportPlugin/settings.gradle.kts new file mode 100644 index 000000000..203b2fb8f --- /dev/null +++ b/swiftPMImportPlugin/settings.gradle.kts @@ -0,0 +1,5 @@ +dependencyResolutionManagement { + repositories { + mavenCentral() + } +} \ No newline at end of file diff --git a/swiftPMImportPlugin/src/main/kotlin/SwiftPMImportExtension.kt b/swiftPMImportPlugin/src/main/kotlin/SwiftPMImportExtension.kt new file mode 100644 index 000000000..be64af44e --- /dev/null +++ b/swiftPMImportPlugin/src/main/kotlin/SwiftPMImportExtension.kt @@ -0,0 +1,252 @@ +package org.jetbrains.kotlin.gradle.plugin.mpp.apple.swiftimport + +import org.gradle.api.DomainObjectSet +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.Property +import org.jetbrains.kotlin.gradle.plugin.mpp.apple.swiftimport.SwiftPMDependency.Remote.Repository +import org.jetbrains.kotlin.gradle.plugin.mpp.apple.swiftimport.SwiftPMDependency.Remote.Version +import java.io.File +import java.io.Serializable +import javax.inject.Inject + +abstract class SwiftImportExtension @Inject constructor( + private val objects: ObjectFactory, +) { + val swiftJavaRepository: RegularFileProperty = objects.fileProperty() + val macosDeploymentVersion: Property = objects.property(String::class.java).convention("15.0") + + internal abstract val spmDependencies: DomainObjectSet + + fun url(value: String): Repository.Url = Repository.Url(value) + fun id(value: String): Repository.Id = Repository.Id(value) + + fun from(version: String): Version = Version.From(version) + fun exact(version: String): Version = Version.Exact(version) + fun revision(version: String): Version = Version.Revision(version) + fun branch(version: String): Version = Version.Branch(version) + fun range(from: String, through: String): Version = Version.Range(from, through) + + fun product( + name: String, + platforms: Set? = null, + importedModules: Set = if (platforms != null) setOf(name) else emptySet(), + ): SwiftPMDependency.Product = SwiftPMDependency.Product( + name, + importedModules, + platforms + ) + fun iOS(): SwiftPMDependency.Platform = SwiftPMDependency.Platform.iOS + fun macOS(): SwiftPMDependency.Platform = SwiftPMDependency.Platform.macOS + fun watchOS(): SwiftPMDependency.Platform = SwiftPMDependency.Platform.watchOS + fun tvOS(): SwiftPMDependency.Platform = SwiftPMDependency.Platform.tvOS + + fun `package`( + url: String, + version: String, + products: List, + packageName: String = inferPackageName(url), + importedModules: List = products, + traits: Set = setOf(), + ) { + spmDependencies.add( + SwiftPMDependency.Remote( + repository = Repository.Url(url), + version = Version.From(version), + packageName = packageName, + products = products.map { SwiftPMDependency.Product(it) }, + cinteropClangModules = importedModules.map { + SwiftPMDependency.CinteropClangModule(it) + }, + traits = traits + ) + ) + } + + fun `package`( + url: Repository.Url, + version: Version, + products: List, + packageName: String = inferPackageName(url.value), + importedModules: List = products.filter { + it.platformConstraints == null && it.cinteropClangModules.isEmpty() + }.map { it.name }, + traits: Set = setOf(), + ) { + spmDependencies.add( + SwiftPMDependency.Remote( + repository = url, + version = version, + packageName = packageName, + products = products, + cinteropClangModules = importedModules.map { + SwiftPMDependency.CinteropClangModule(it) + } + products.flatMap { product -> + product.cinteropClangModules.map { + SwiftPMDependency.CinteropClangModule(it, product.platformConstraints) + } + }, + traits = traits, + ) + ) + } + + fun `package`( + repository: Repository, + version: Version, + products: List, + packageName: String, + importedModules: List = products.filter { + it.platformConstraints == null && it.cinteropClangModules.isEmpty() + }.map { it.name }, + traits: Set = setOf(), + ) { + spmDependencies.add( + SwiftPMDependency.Remote( + repository = repository, + version = version, + packageName = packageName, + products = products, + cinteropClangModules = importedModules.map { + SwiftPMDependency.CinteropClangModule(it) + } + products.flatMap { product -> + product.cinteropClangModules.map { + SwiftPMDependency.CinteropClangModule(it, product.platformConstraints) + } + }, + traits = traits, + ) + ) + } + + fun localPackage( + path: File, + products: List, + importedModules: List = products, + traits: Set = setOf(), + ) { + spmDependencies.add( + SwiftPMDependency.Local( + path = path, + packageName = path.name, + products = products.map { SwiftPMDependency.Product(it) }, + cinteropClangModules = importedModules.map { + SwiftPMDependency.CinteropClangModule(it) + }, + traits = traits, + ) + ) + } + + fun localPackage( + path: File, + products: List, + importedModules: List = products.filter { + it.platformConstraints == null && it.cinteropClangModules.isEmpty() + }.map { it.name }, + traits: Set = setOf(), + @Suppress("unused", "UNUSED_PARAMETER") javaOverloadsArePain: Boolean = true, + ) { + spmDependencies.add( + SwiftPMDependency.Local( + path = path, + packageName = path.name, + products = products, + cinteropClangModules = importedModules.map { + SwiftPMDependency.CinteropClangModule(it) + } + products.flatMap { product -> + product.cinteropClangModules.map { + SwiftPMDependency.CinteropClangModule(it, product.platformConstraints) + } + }, + traits = traits, + ) + ) + } + + // FIXME: check if this is actually correct + private fun inferPackageName(url: String) = url.split("/").last().split(".git").first() + + companion object { + const val EXTENSION_NAME = "swiftPMDependencies" + } +} + +class SwiftPMImport( + val macosDeploymentVersion: String?, + val dependencies: Set +) : Serializable + +sealed class SwiftPMDependency( + val packageName: String, + val products: List, + val cinteropClangModules: List, + val traits: Set, +) : Serializable { + class Product internal constructor( + val name: String, + // this is not actually used for translation + val cinteropClangModules: Set = setOf(), + val platformConstraints: Set? = null, + ) : Serializable + + class CinteropClangModule internal constructor( + val name: String, + val platformConstraints: Set? = null, + ) : Serializable + + enum class Platform { + iOS, + macOS, + tvOS, + watchOS; + + val swiftEnumName: String get() = when (this) { + iOS -> "iOS" + macOS -> "macOS" + tvOS -> "tvOS" + watchOS -> "watchOS" + } + } + + class Remote internal constructor( + val repository: Repository, + val version: Version, + // FIXME: This can actually be inferred from the repository URL + products: List, + cinteropClangModules: List, + packageName: String, + traits: Set, + ) : SwiftPMDependency( + products = products, + cinteropClangModules = cinteropClangModules, + packageName = packageName, + traits = traits, + ) { + sealed class Version : Serializable { + data class Exact internal constructor(val value: String) : Version() + data class From internal constructor(val value: String) : Version() + data class Range internal constructor(val from: String, val through: String) : Version() + data class Branch internal constructor(val value: String) : Version() + data class Revision internal constructor(val value: String) : Version() + } + + sealed class Repository : Serializable { + data class Id internal constructor(val value: String) : Repository() + data class Url internal constructor(val value: String) : Repository() + } + } + + class Local internal constructor( + val path: File, + products: List, + cinteropClangModules: List, + packageName: String, + traits: Set, + ) : SwiftPMDependency( + products = products, + cinteropClangModules = cinteropClangModules, + packageName = packageName, + traits = traits, + ) +} \ No newline at end of file diff --git a/swiftPMImportPlugin/src/main/kotlin/SwiftPMImportPlugin.kt b/swiftPMImportPlugin/src/main/kotlin/SwiftPMImportPlugin.kt new file mode 100644 index 000000000..62094a291 --- /dev/null +++ b/swiftPMImportPlugin/src/main/kotlin/SwiftPMImportPlugin.kt @@ -0,0 +1,1049 @@ +import DiscoverSwiftcAndLdArguments.Companion.DUMP_FILE_ARGS_SEPARATOR +import com.google.gson.Gson +import org.gradle.api.DefaultTask +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.FileSystemOperations +import org.gradle.api.file.RegularFile +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.provider.ListProperty +import org.gradle.api.provider.Property +import org.gradle.api.provider.Provider +import org.gradle.api.provider.SetProperty +import org.gradle.api.tasks.IgnoreEmptyDirectories +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.InputDirectory +import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.InputFiles +import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.JavaExec +import org.gradle.api.tasks.Nested +import org.gradle.api.tasks.Optional +import org.gradle.api.tasks.OutputDirectory +import org.gradle.api.tasks.OutputFile +import org.gradle.api.tasks.OutputFiles +import org.gradle.api.tasks.PathSensitive +import org.gradle.api.tasks.PathSensitivity +import org.gradle.api.tasks.SourceSetContainer +import org.gradle.api.tasks.TaskAction +import org.gradle.api.tasks.testing.Test +import org.gradle.internal.extensions.core.serviceOf +import org.gradle.process.ExecOperations +import org.gradle.work.DisableCachingByDefault +import org.jetbrains.kotlin.gradle.plugin.mpp.apple.swiftimport.SwiftImportExtension +import org.jetbrains.kotlin.gradle.plugin.mpp.apple.swiftimport.SwiftPMDependency +import java.io.ByteArrayOutputStream +import java.io.File +import java.io.Serializable +import javax.inject.Inject +import kotlin.collections.forEach +import kotlin.collections.joinToString +import kotlin.collections.plus + +class SwiftPMImportPlugin : Plugin { + + override fun apply(target: Project) { + val project = target + + val swiftPMImportExtension = project.swiftPMDependenciesExtension() + val importedModules = project.provider { + swiftPMImportExtension.spmDependencies.flatMap { it.cinteropClangModules.map { it.name } }.toSet() + } + + val computeLocalPackageDependencyInputFiles = project.registerTask( + ComputeLocalPackageDependencyInputFiles.TASK_NAME, + ) {} + + val syntheticImportProjectGenerationTaskForSwiftcAndLdDumps = project.registerTask( + GenerateSyntheticLinkageImportProject.TASK_NAME, + ) { + it.configureWithExtension(swiftPMImportExtension) + it.syntheticProductType.set(GenerateSyntheticLinkageImportProject.SyntheticProductType.DYNAMIC) + } + + val fetchSyntheticImportProjectPackages = project.registerTask( + FetchSyntheticImportProjectPackages.TASK_NAME, + ) { + it.dependsOn(syntheticImportProjectGenerationTaskForSwiftcAndLdDumps) + it.syntheticImportProjectRoot.set(syntheticImportProjectGenerationTaskForSwiftcAndLdDumps.map { it.syntheticImportProjectRoot.get() }) + } + + val targetPlatform = "macOS" + val targetSdk = "macosx" + val archs = setOf(AppleArchitecture.ARM64) + + val swiftcAndLdDumpTask = project.registerTask( + "dumpSwiftcAndLdArgumentsFromImportedSwiftPMDependencies" + ) { + it.dependsOn(fetchSyntheticImportProjectPackages) + it.dependsOn(computeLocalPackageDependencyInputFiles) + it.buildScheme.set(SYNTHETIC_IMPORT_TARGET_MAGIC_NAME) + it.aggregateModuleName.set(SYNTHETIC_IMPORT_TARGET_MAGIC_NAME) + it.importedSwiftModules.set(importedModules) + it.resolvedPackagesState.from( + fetchSyntheticImportProjectPackages.map { it.inputManifests }, + fetchSyntheticImportProjectPackages.map { it.lockFile }, + ) + it.xcodebuildPlatform.set(targetPlatform) + it.xcodebuildSdk.set(targetSdk) + it.swiftPMDependenciesCheckout.set(fetchSyntheticImportProjectPackages.map { it.swiftPMDependenciesCheckout.get() }) + it.syntheticImportProjectRoot.set(syntheticImportProjectGenerationTaskForSwiftcAndLdDumps.map { it.syntheticImportProjectRoot.get() }) + it.filesToTrackFromLocalPackages.set(computeLocalPackageDependencyInputFiles.flatMap { it.filesToTrackFromLocalPackages }) + it.hasSwiftPMDependencies.set(project.provider { swiftPMImportExtension.spmDependencies.isNotEmpty() }) + it.architectures.set(archs) + } + + val execOps = project.serviceOf() + val swiftJavaRepoPath = swiftPMImportExtension.swiftJavaRepository.map { it.asFile } + val buildSwiftJava = target.registerTask("buildSwiftJava") {} + + val swiftJavaIntermediates = project.layout.buildDirectory.dir("swiftJavaIntermediates") + + val importedModuleToSourcesDump = importedModules.map { modules -> + modules.map { module -> + ImportedModuleInfo( + module, + swiftcAndLdDumpTask.get().moduleSwiftSources(archs.single(), module).get().asFile, + swiftJavaIntermediates.map { it.dir(module).dir("SwiftBridges") }.get().asFile, + swiftJavaIntermediates.map { it.dir(module).dir("JavaBridges") }.get().asFile, + ) + } + } + + val swiftJavaPath = buildSwiftJava.map { it.outputBinary() } + + val convertSwiftInterfacesIntoJavaSources = target.registerTask("convertImportedSwiftModulesIntoJavaSources") { + it.dependsOn(buildSwiftJava) + it.dependsOn(swiftcAndLdDumpTask) + it.doLast { + val swiftJavaPath = swiftJavaPath.get() + val intermediates = swiftJavaIntermediates.get().asFile + if (intermediates.exists()) { intermediates.deleteRecursively() } + intermediates.mkdirs() + + importedModuleToSourcesDump.get().forEach { moduleInfo -> + execOps.exec { + it.commandLine( + swiftJavaPath, + "jextract", + "--swift-module", moduleInfo.moduleName, + "--java-package", "com.example.swift.${moduleInfo.moduleName}", + "--input-swift", moduleInfo.sourcesDump.readLines().first(), + "--output-swift", moduleInfo.swiftBridges, + "--output-java", moduleInfo.javaBridges, + ) + } + } + } + } + + val aggregateTargetName = GeneratePackageForBridgesCompilation.BRIDGES_COMPILATION_PROJECT_NAME + val bridgesPackage = target.registerTask(GeneratePackageForBridgesCompilation.TASK_NAME) { + it.dependsOn(convertSwiftInterfacesIntoJavaSources) + it.configureWithExtension(swiftPMImportExtension) + it.moduleInfos.set(importedModuleToSourcesDump) + it.aggregateModuleName.set(aggregateTargetName) + it.swiftcSearchPathsFile.set(swiftcAndLdDumpTask.map { it.swiftSearchPaths(archs.single()).getFile() }) + it.ldArgsFile.set(swiftcAndLdDumpTask.map { it.ldFilePath(archs.single()).getFile() }) + } + + val compileSwiftBridges = target.registerTask("compileSwiftBridges") { + it.dependsOn(bridgesPackage) + it.buildScheme.set("${aggregateTargetName}-Package") + it.aggregateModuleName.set(aggregateTargetName) + it.xcodebuildPlatform.set(targetPlatform) + it.xcodebuildSdk.set(targetSdk) + it.filesToTrackFromLocalPackages.set(computeLocalPackageDependencyInputFiles.flatMap { it.filesToTrackFromLocalPackages }) + it.hasSwiftPMDependencies.set(project.provider { swiftPMImportExtension.spmDependencies.isNotEmpty() }) + it.importedSwiftModules.set(emptySet()) + it.swiftPMDependenciesCheckout.set(fetchSyntheticImportProjectPackages.map { it.swiftPMDependenciesCheckout.get() }) + it.syntheticImportProjectRoot.set(bridgesPackage.flatMap { it.syntheticImportProjectRoot }) + it.architectures.set(archs) + } + + syntheticImportProjectGenerationTaskForSwiftcAndLdDumps.configure { + it.directlyImportedSpmModules.set(swiftPMImportExtension.spmDependencies) + } + swiftPMImportExtension.spmDependencies.all { dependency -> + when (dependency) { + is SwiftPMDependency.Local -> { + computeLocalPackageDependencyInputFiles.configure { + it.localPackages.add(dependency.path) + } + fetchSyntheticImportProjectPackages.configure { + it.localPackageManifests.from( + dependency.path.resolve("Package.swift") + ) + } + } + is SwiftPMDependency.Remote -> Unit + } + } + + target.pluginManager.withPlugin("java") { + val sourceSets = target.extensions.getByName("sourceSets") as SourceSetContainer + val mainSourceSet = sourceSets.getByName("main") + mainSourceSet.java.srcDir(convertSwiftInterfacesIntoJavaSources.map { + importedModuleToSourcesDump.get().map { + it.javaBridges + } + }) + + val initialCompilationSearchPathFile = swiftcAndLdDumpTask.flatMap { + it.librarySearchpathFilePath(archs.single()) + } + val bridgesCompilationSearchPathFile = compileSwiftBridges.flatMap { + it.librarySearchpathFilePath(archs.single()) + } + + fun intermediateSearchPaths(): List { + val initialCompilationSearchPath = initialCompilationSearchPathFile.getFile().readLines().first().split(DUMP_FILE_ARGS_SEPARATOR) + val bridgesCompilationSearchPath = bridgesCompilationSearchPathFile.getFile().readLines().first().split(DUMP_FILE_ARGS_SEPARATOR) + val intermediateSearchPaths = initialCompilationSearchPath + bridgesCompilationSearchPath + return intermediateSearchPaths + } + + fun computeLibraryPath(): String { + val libraryPath = (listOf("/usr/lib/swift") + intermediateSearchPaths()).joinToString(":") + return libraryPath + } + + target.tasks.withType(JavaExec::class.java).configureEach { + it.dependsOn(compileSwiftBridges) + it.doFirst { task -> + task as JavaExec + task.jvmArgs("--enable-native-access=ALL-UNNAMED") + task.systemProperty("java.library.path", computeLibraryPath()) + task.environment("DYLD_FALLBACK_LIBRARY_PATH", intermediateSearchPaths().joinToString(":")) + } + } + + target.tasks.withType(Test::class.java) { + it.dependsOn(compileSwiftBridges) + it.doFirst { task -> + task as Test + task.jvmArgs("--enable-native-access=ALL-UNNAMED") + task.systemProperty("java.library.path", computeLibraryPath()) + task.environment("DYLD_FALLBACK_LIBRARY_PATH", intermediateSearchPaths().joinToString(":")) + } + } + } + } +} + +internal inline fun Project.registerTask(name: String, crossinline configure: (T) -> Unit) = + tasks.register(name, T::class.java) { + configure(it) + } + +internal data class ImportedModuleInfo( + @get:Input + val moduleName: String, + @get:Internal + val sourcesDump: File, + @get:PathSensitive(PathSensitivity.RELATIVE) + @get:InputDirectory + val swiftBridges: File, + @get:PathSensitive(PathSensitivity.RELATIVE) + @get:InputDirectory + val javaBridges: File, +) : Serializable + +internal fun Provider.getFile() = get().asFile + +internal fun Project.swiftPMDependenciesExtension(): SwiftImportExtension { + val existingExtension = project.extensions.findByName(SwiftImportExtension.EXTENSION_NAME) + if (existingExtension != null) { + return existingExtension as SwiftImportExtension + } + project.extensions.create( + SwiftImportExtension.EXTENSION_NAME, + SwiftImportExtension::class.java + ) + return project.extensions.getByName(SwiftImportExtension.EXTENSION_NAME) as SwiftImportExtension +} + +@DisableCachingByDefault(because = "...") +internal abstract class BuildSwiftJava : DefaultTask() { + + @get:Internal + val swiftJavaRepoPath = project.rootDir + + @get:InputFile + protected val manifest get() = swiftJavaRepoPath.resolve("Package.swift") + @get:InputFile + protected val lockFile get() = swiftJavaRepoPath.resolve("Package.resolved") + @get:InputDirectory + protected val sources get() = swiftJavaRepoPath.resolve("Sources") + + @get:Inject + protected abstract val execOps: ExecOperations + + @get:OutputFile + val outputBinary get() = { + val showBinPathOutput = ByteArrayOutputStream() + execOps.exec { + it.workingDir(swiftJavaRepoPath) + it.commandLine("swift", "build", "--show-bin-path") + it.standardOutput = showBinPathOutput + } + File(showBinPathOutput.toString().lineSequence().first()).resolve("swift-java") + } + + @TaskAction + fun build() { + execOps.exec { + it.workingDir(swiftJavaRepoPath) + it.commandLine("swift", "build", "--product", "swift-java") + } + } + +} + +@DisableCachingByDefault(because = "...") +internal abstract class GenerateSyntheticLinkageImportProject : DefaultTask() { + + @get:Input + abstract val directlyImportedSpmModules: SetProperty + + @get:Internal + val syntheticImportProjectRoot: DirectoryProperty = project.objects.directoryProperty().convention( + project.layout.buildDirectory.dir("swiftJava/swiftImport") + ) + + @get:OutputFiles + protected val projectRootTrackedFiles get() = syntheticImportProjectRoot.asFileTree.matching { + it.exclude("Package.resolved") + } + + @get:Optional + @get:Input + abstract val macosDeploymentVersion: Property + + @get:Input + abstract val syntheticProductType: Property + + enum class SyntheticProductType : Serializable { + DYNAMIC, + INFERRED, + } + + fun configureWithExtension(swiftPMImportExtension: SwiftImportExtension) { + macosDeploymentVersion.set(swiftPMImportExtension.macosDeploymentVersion) + } + + @TaskAction + fun generateSwiftPMSyntheticImportProjectAndFetchPackages() { + val packageRoot = syntheticImportProjectRoot.get().asFile + + val forceAllLibrariesToBeDynamic = "forceAllLibrariesToBeDynamic" + generatePackageManifest( + identifier = SYNTHETIC_IMPORT_TARGET_MAGIC_NAME, + packageRoot = packageRoot, + syntheticProductType = syntheticProductType.get(), + directlyImportedSwiftPMDependencies = directlyImportedSpmModules.get(), + localSyntheticPackages = setOf(forceAllLibrariesToBeDynamic) + ) + generatePackageManifest( + identifier = forceAllLibrariesToBeDynamic, + packageRoot = packageRoot.resolve(forceAllLibrariesToBeDynamic), + syntheticProductType = SyntheticProductType.DYNAMIC, + directlyImportedSwiftPMDependencies = directlyImportedSpmModules.get(), + localSyntheticPackages = emptySet(), + ) + } + + private fun generatePackageManifest( + identifier: String, + packageRoot: File, + syntheticProductType: SyntheticProductType, + directlyImportedSwiftPMDependencies: Set, + localSyntheticPackages: Set, + ) { + val repoDependencies = directlyImportedSwiftPMDependencies.map { importedPackage -> + buildString { + appendLine(".package(") + when (importedPackage) { + is SwiftPMDependency.Remote -> { + when (val repository = importedPackage.repository) { + is SwiftPMDependency.Remote.Repository.Id -> { + appendLine(" id: \"${repository.value}\",") + } + is SwiftPMDependency.Remote.Repository.Url -> { + appendLine(" url: \"${repository.value}\",") + } + } + when (val version = importedPackage.version) { + is SwiftPMDependency.Remote.Version.Exact -> appendLine(" exact: \"${version.value}\",") + is SwiftPMDependency.Remote.Version.From -> appendLine(" from: \"${version.value}\",") + is SwiftPMDependency.Remote.Version.Range -> appendLine(" \"${version.from}\"...\"${version.through}\",") + is SwiftPMDependency.Remote.Version.Branch -> appendLine(" branch: \"${version.value}\",") + is SwiftPMDependency.Remote.Version.Revision -> appendLine(" revision: \"${version.value}\",") + } + } + is SwiftPMDependency.Local -> { + appendLine(" path: \"${importedPackage.path.path}\",") + } + } + if (importedPackage.traits.isNotEmpty()) { + val traitsString = importedPackage.traits.joinToString(", ") { "\"${it}\"" } + appendLine(" traits: [${traitsString}],") + } + appendLine("),") + } + } + localSyntheticPackages.map { + ".package(path: \"${it}\")," + } + + val targetDependencies = directlyImportedSwiftPMDependencies.flatMap { dep -> dep.products.map { it to dep.packageName } }.map { + buildString { + appendLine(".product(") + appendLine(" name: \"${it.first.name}\",") + appendLine(" package: \"${it.second}\",") + val platformConstraints = it.first.platformConstraints + if (platformConstraints != null) { + val platformsString = platformConstraints.joinToString(", ") { ".${it.swiftEnumName}" } + appendLine(" condition: .when(platforms: [${platformsString}]),") + } + appendLine("),") + } + } + localSyntheticPackages.map { + ".product(name: \"${it}\", package: \"${it}\")," + } + + val platforms = listOf(".macOS(\"${macosDeploymentVersion.get()}\"),") + + val productType = when (syntheticProductType) { + SyntheticProductType.DYNAMIC -> ".dynamic" + SyntheticProductType.INFERRED -> ".none" + } + + val manifest = packageRoot.resolve(MANIFEST_NAME) + manifest.also { + it.parentFile.mkdirs() + }.writeText( + buildString { + appendLine("// swift-tools-version: 6.0") + appendLine("import PackageDescription") + appendLine("let package = Package(") + appendLine(" name: \"$identifier\",") + appendLine(" platforms: [") + platforms.forEach { appendLine(" $it")} + appendLine(" ],") + appendLine( + """ + products: [ + .library( + name: "$identifier", + type: ${productType}, + targets: ["$identifier"] + ), + ], + """.replaceIndent(" ") + ) + appendLine(" dependencies: [") + repoDependencies.forEach { appendLine(it.replaceIndent(" ")) } + appendLine(" ],") + appendLine(" targets: [") + appendLine(" .target(") + appendLine(" name: \"$identifier\",") + appendLine(" dependencies: [") + targetDependencies.forEach { appendLine(it.replaceIndent(" ")) } + appendLine(" ],") + appendLine(" ),") + appendLine(" ]") + appendLine(")") + } + ) + + val swiftSource = "Sources/${identifier}/${identifier}.swift" + // Generate swift sources specifically because the next SWIFT_EXEC-overriding step relies on passing a swiftc shim to dump compiler arguments + packageRoot.resolve(swiftSource).also { + it.parentFile.mkdirs() + }.writeText("") + } + + companion object { + const val TASK_NAME = "generateSyntheticLinkageSwiftPMImportProject" + const val SYNTHETIC_IMPORT_TARGET_MAGIC_NAME = "_internal_linkage_SwiftPMImport" + const val MANIFEST_NAME = "Package.swift" + } +} + +@DisableCachingByDefault(because = "...") +internal abstract class GeneratePackageForBridgesCompilation : DefaultTask() { + + @get:Nested + abstract val moduleInfos: ListProperty + + @get:InputFile + abstract val swiftcSearchPathsFile: Property + + @get:InputFile + abstract val ldArgsFile: Property + + @get:Internal + val syntheticImportProjectRoot: DirectoryProperty = project.objects.directoryProperty().convention( + project.layout.buildDirectory.dir("swiftJava/swiftImportBridgesCompilation") + ) + + @get:Input + abstract val aggregateModuleName: Property + + @get:Optional + @get:Input + abstract val macosDeploymentVersion: Property + + @get:OutputFile + protected val manifest get() = syntheticImportProjectRoot.asFile.get().resolve("Package.swift") + @get:OutputDirectory + protected val sources get() = syntheticImportProjectRoot.asFile.get().resolve("Sources") + + @get:Inject + protected abstract val fsOps: FileSystemOperations + + fun configureWithExtension(swiftPMImportExtension: SwiftImportExtension) { + macosDeploymentVersion.set(swiftPMImportExtension.macosDeploymentVersion) + } + + @TaskAction + fun generatePackageForBridgesCompilation() { + val packageRoot = syntheticImportProjectRoot.get().asFile + + val platforms = listOf(".macOS(\"${macosDeploymentVersion.get()}\"),") + val linkerSettingsShim = "_linkerSettingsShim" + + val targets = mutableListOf() + val products = mutableListOf() + val swiftcSearchPaths = swiftcSearchPathsFile.get() + .readLines().first().split(DUMP_FILE_ARGS_SEPARATOR).joinToString(", ") { "\"${it}\"" } + val ldArgs = ldArgsFile.get() + .readLines().first().split(DUMP_FILE_ARGS_SEPARATOR).joinToString(", ") { "\"${it}\"" } + val bridgeModules = mutableSetOf() + targets.add( + """ + .target( + name: "${linkerSettingsShim}", + linkerSettings: [.unsafeFlags([${ldArgs}])] + ), + """.trimIndent() + ) + + moduleInfos.get().forEach { + val bridgeName = "${it.moduleName}_Bridges" + targets.add( + """ + .target( + name: "${bridgeName}", + dependencies: ["${linkerSettingsShim}"], + swiftSettings: [ + .swiftLanguageMode(.v5), + .unsafeFlags([${swiftcSearchPaths}]) + ], + ), + """.trimIndent() + ) + products.add( + """ + .library( + name: "${bridgeName}", + type: .dynamic, + targets: ["${bridgeName}"] + ), + """.trimIndent() + ) + bridgeModules.add(bridgeName) + } + + val dependenciesString = (bridgeModules + listOf(linkerSettingsShim)).joinToString(", ") { "\"${it}\"" } + targets.add( + """ + .target( + name: "${aggregateModuleName.get()}", + dependencies: [${dependenciesString}] + ), + """.trimIndent() + ) + products.add( + """ + .library( + name: "${aggregateModuleName.get()}", + type: .dynamic, + targets: ["${aggregateModuleName.get()}"] + ), + """.trimIndent() + ) + + val manifest = packageRoot.resolve(MANIFEST_NAME) + manifest.also { + it.parentFile.mkdirs() + }.writeText( + buildString { + appendLine("// swift-tools-version: 6.0") + appendLine("import PackageDescription") + appendLine("let package = Package(") + appendLine(" name: \"$BRIDGES_COMPILATION_PROJECT_NAME\",") + appendLine(" platforms: [") + platforms.forEach { appendLine(" $it")} + appendLine(" ],") + appendLine(" products: [") + products.forEach { appendLine(" $it")} + appendLine(" ],") + appendLine(" targets: [") + targets.forEach { appendLine(" $it") } + appendLine(" ]") + appendLine(")") + } + ) + + moduleInfos.get().forEach { module -> + val swiftSource = "Sources/${module.moduleName}_Bridges" + val bridgePath = packageRoot.resolve(swiftSource).also { + it.parentFile.mkdirs() + } + fsOps.sync { + it.from(module.swiftBridges) + it.into(bridgePath) + } + } + listOf( + aggregateModuleName.get(), + linkerSettingsShim + ).forEach { + packageRoot.resolve("Sources/${it}/${it}.swift").also { + it.parentFile.mkdirs() + }.writeText("") + } + } + + companion object { + const val TASK_NAME = "generateSwiftBridgesCompilationPackage" + const val BRIDGES_COMPILATION_PROJECT_NAME = "SwiftBridgesCompilation" + const val MANIFEST_NAME = "Package.swift" + } +} + +@DisableCachingByDefault(because = "...") +internal abstract class FetchSyntheticImportProjectPackages : DefaultTask() { + + /** + * Refetch when Package manifests of local SwiftPM dependencies change + */ + @get:InputFiles + @get:PathSensitive(PathSensitivity.RELATIVE) + abstract val localPackageManifests: ConfigurableFileCollection + + @get:Internal + val syntheticImportProjectRoot: DirectoryProperty = project.objects.directoryProperty() + + /** + * These are own manifest and manifests from project/modular dependencies. Refetch when any of these Package manifests changed. + */ + @get:IgnoreEmptyDirectories + @get:InputFiles + @get:PathSensitive(PathSensitivity.RELATIVE) + val inputManifests + get() = syntheticImportProjectRoot + .asFileTree + .matching { + it.include("**/Package.swift") + } + + @get:Internal + val swiftPMDependenciesCheckout: DirectoryProperty = project.objects.directoryProperty().convention( + project.layout.buildDirectory.dir("swiftJava/swiftPMCheckout") + ) + + /** + * Invalidate fetch when Package.swift or Package.resolved files changed. + */ + @get:OutputFile + val lockFile = syntheticImportProjectRoot.file("Package.resolved") + + @get:Internal + protected val swiftPMDependenciesCheckoutLogs: DirectoryProperty = project.objects.directoryProperty().convention( + project.layout.buildDirectory.dir("swiftJava/swiftPMCheckoutDD") + ) + + @get:Inject + protected abstract val execOps: ExecOperations + + @TaskAction + fun generateSwiftPMSyntheticImportProjectAndFetchPackages() { + checkoutSwiftPMDependencies() + } + + private fun checkoutSwiftPMDependencies() { + execOps.exec { + it.workingDir(syntheticImportProjectRoot.get().asFile) + it.commandLine( + "xcodebuild", "-resolvePackageDependencies", + "-scheme", SYNTHETIC_IMPORT_TARGET_MAGIC_NAME, + XCODEBUILD_SWIFTPM_CHECKOUT_PATH_PARAMETER, swiftPMDependenciesCheckout.get().asFile.path, + "-derivedDataPath", swiftPMDependenciesCheckoutLogs.get().asFile.path, + ) + } + } + + companion object { + const val TASK_NAME = "fetchSyntheticImportProjectPackages" + } +} + +@DisableCachingByDefault(because = "...") +internal abstract class DiscoverSwiftcAndLdArguments : DefaultTask() { + @get:Input + abstract val buildScheme: Property + + @get:Input + abstract val aggregateModuleName: Property + + @get:Input + abstract val xcodebuildPlatform: Property + @get:Input + abstract val xcodebuildSdk: Property + + @get:Input + abstract val architectures: SetProperty + + @get:Input + abstract val hasSwiftPMDependencies: Property + + @get:InputFile + @get:PathSensitive(PathSensitivity.RELATIVE) + abstract val filesToTrackFromLocalPackages: RegularFileProperty + @get:InputFiles + @get:PathSensitive(PathSensitivity.RELATIVE) + protected val localPackageSources get() = filesToTrackFromLocalPackages.map { it.asFile.readLines().filter { it.isNotEmpty() }.map { File(it) } } + + @get:InputFiles + @get:PathSensitive(PathSensitivity.RELATIVE) + abstract val resolvedPackagesState: ConfigurableFileCollection + + private val layout = project.layout + + @get:OutputDirectory + protected val swiftDump = xcodebuildSdk.flatMap { sdk -> + layout.buildDirectory.dir("swiftJava/${buildScheme.get()}/swiftImportSwiftDump/${sdk}") + } + + @get:OutputDirectory + protected val ldDump = xcodebuildSdk.flatMap { sdk -> + layout.buildDirectory.dir("swiftJava/${buildScheme.get()}/swiftImportLdDump/${sdk}") + } + + @get:Internal + abstract val swiftPMDependenciesCheckout: DirectoryProperty + + @get:Internal + abstract val syntheticImportProjectRoot: DirectoryProperty + @get:InputFile + protected val manifest get() = syntheticImportProjectRoot.asFile.get().resolve("Package.swift") + @get:InputDirectory + protected val sources get() = syntheticImportProjectRoot.asFile.get().resolve("Sources") + + + @get:Internal + val syntheticImportDd get() = layout.buildDirectory.dir("swiftJava/${buildScheme.get()}/swiftImportDd") + + @get:Input + abstract val importedSwiftModules: SetProperty + + @get:Inject + protected abstract val execOps: ExecOperations + + @TaskAction + fun generateDefFiles() { + if (!hasSwiftPMDependencies.get()) { + architectures.get().forEach { architecture -> + // moduleSwiftSources(architecture).getFile().writeText("\n") + ldFilePath(architecture).getFile().writeText("\n") + librarySearchpathFilePath(architecture).getFile().writeText("\n") + } + return + } + + val dumpIntermediates = xcodebuildSdk.flatMap { sdk -> + layout.buildDirectory.dir("swiftJava/swiftImportDump/${sdk}") + }.get().asFile.also { + if (it.exists()) { + it.deleteRecursively() + } + it.mkdirs() + } + + val swiftcPathOutput = ByteArrayOutputStream() + execOps.exec { + it.commandLine("xcrun", "-f", "swiftc") + it.standardOutput = swiftcPathOutput + } + val swiftArgsDumpScript = dumpIntermediates.resolve("swiftc") + val swiftcPath = File(swiftcPathOutput.toString().lineSequence().first()) + // Otherwise SwiftPM dependencies build explodes due to missing features + val compilerFeatures = swiftcPath.parentFile.parentFile.resolve("share/swift/features.json") + val compilerFeaturesCopy = swiftArgsDumpScript.parentFile.parentFile.resolve("share/swift/features.json") + compilerFeaturesCopy.parentFile.mkdirs() + compilerFeatures.copyTo(compilerFeaturesCopy, overwrite = true) + swiftArgsDumpScript.writeText(swiftcArgsDumpScript()) + swiftArgsDumpScript.setExecutable(true) + val swiftArgsDump = dumpIntermediates.resolve("swift_args_dump") + swiftArgsDump.mkdirs() + + val ldArgsDumpScript = dumpIntermediates.resolve("ldDump.sh") + ldArgsDumpScript.writeText(ldArgsDumpScript()) + ldArgsDumpScript.setExecutable(true) + val ldArgsDump = dumpIntermediates.resolve("ld_args_dump") + ldArgsDump.mkdirs() + + val targetArchitectures = architectures.get().map { + it.xcodebuildArch + } + + val projectRoot = syntheticImportProjectRoot.get().asFile + val dd = syntheticImportDd.get().asFile.resolve("dd_${xcodebuildSdk.get()}") + + execOps.exec { exec -> + exec.workingDir(projectRoot) + exec.commandLine( + "xcodebuild", "clean", "build", + "-scheme", + buildScheme.get(), + "-destination", "generic/platform=${xcodebuildPlatform.get()}", + "-derivedDataPath", dd.path, + XCODEBUILD_SWIFTPM_CHECKOUT_PATH_PARAMETER, swiftPMDependenciesCheckout.get().asFile.path, + "SWIFT_EXEC=${swiftArgsDumpScript.path}", + "LD=${ldArgsDumpScript.path}", + "ARCHS=${targetArchitectures.joinToString(" ")}", + "CODE_SIGN_IDENTITY=-", + "COMPILER_INDEX_STORE_ENABLE=NO", + "SWIFT_INDEX_STORE_ENABLE=NO", + "SWIFT_USE_INTEGRATED_DRIVER=NO", + // "SWIFT_EMIT_MODULE_INTERFACE=YES", ended up not being useful + "-IDEPackageSupportCreateDylibsForDynamicProducts=YES", + ) + exec.environment(KOTLIN_SWIFTC_ARGS_DUMP_FILE_ENV, swiftArgsDump) + exec.environment(KOTLIN_LD_ARGS_DUMP_FILE_ENV, ldArgsDump) + } + + val importedSwiftModules = importedSwiftModules.get() + + architectures.get().forEach { architecture -> + val clangArchitecture = architecture.clangArch + val swiftSourceFiles = mutableMapOf() + val swiftSearchPaths = mutableListOf() + + val javaHome = File(System.getProperty("java.home")) + swiftSearchPaths.add("-I${javaHome.resolve("include").path}") + swiftSearchPaths.add("-I${javaHome.resolve("include/darwin").path}") + + swiftArgsDump.listFiles().filter { + it.isFile + }.forEach { + val swiftcArgs = it.readLines().single() + val isCompilationSwiftcCall = "-target${DUMP_FILE_ARGS_SEPARATOR}${clangArchitecture}-apple" in swiftcArgs + && "-module-name" in swiftcArgs + if (isCompilationSwiftcCall) { + val splitArgs = swiftcArgs.split(DUMP_FILE_ARGS_SEPARATOR) + val moduleName = splitArgs[splitArgs.indexOf("-module-name") + 1] + if (moduleName in importedSwiftModules) { + val sourcesPath = splitArgs.first { it.startsWith("@") && it.endsWith(".SwiftFileList") }.substring(1) + swiftSourceFiles[moduleName] = File(sourcesPath).readLines().joinToString(",") + } + if (moduleName == aggregateModuleName.get()) { + val takePlusOne = setOf("-I", "-F", "-Isystem", "-Xcc") + splitArgs.forEachIndexed { index, arg -> + if (arg in takePlusOne) { + swiftSearchPaths.add(arg) + swiftSearchPaths.add(splitArgs[index + 1]) + } else if (arg.startsWith("-I")) { + swiftSearchPaths.add(arg) + } + } + } + } + } + + swiftSourceFiles.forEach { moduleName, sourcesPaths -> + val swiftSourcesPath = moduleSwiftSources(architecture, moduleName) + swiftSourcesPath.getFile().writeText(sourcesPaths) + } + val swiftSearchPathsFile = swiftSearchPaths(architecture) + swiftSearchPathsFile.getFile().writeText(swiftSearchPaths.joinToString(DUMP_FILE_ARGS_SEPARATOR)) + + val architectureSpecificProductLdCalls = ldArgsDump.listFiles().filter { + it.isFile + }.filter { + // This will actually be a clang call + val ldArgs = it.readLines().single() + ("@rpath/lib${aggregateModuleName.get()}.dylib" in ldArgs || "@rpath/${aggregateModuleName.get()}.framework" in ldArgs) + && "-target${DUMP_FILE_ARGS_SEPARATOR}${clangArchitecture}-apple" in ldArgs + } + val architectureSpecificProductLdCall = architectureSpecificProductLdCalls.single() + val ldArgs = mutableListOf() + val resplitLdCall = architectureSpecificProductLdCall.readLines().single().split(DUMP_FILE_ARGS_SEPARATOR) + val linkTimeFrameworkSearchPaths = mutableSetOf() + val librarySearchPaths = mutableSetOf() + + resplitLdCall.forEachIndexed { index, arg -> + if (arg == "-filelist" || arg == "-framework" || (arg.startsWith("-") && arg.endsWith("_framework"))) { + ldArgs.addAll(listOf(arg, resplitLdCall[index + 1])) + } + if (arg.startsWith("-l")) { + ldArgs.add(arg) + } + if (arg.startsWith("-F/")) { + ldArgs.add(arg) + linkTimeFrameworkSearchPaths.add(arg.substring(2)) + } + if (arg.startsWith("-L/")) { + ldArgs.add(arg) + librarySearchPaths.add(arg.substring(2)) + } + if (arg.startsWith("/")) { + if (arg.endsWith(".a")) { + ldArgs.add(arg) + } + if (arg.endsWith(".dylib")) { + ldArgs.add(arg) + librarySearchPaths.add((File(arg).parentFile.path)) + } + if (".framework/" in arg) { + ldArgs.add(arg) + linkTimeFrameworkSearchPaths.add( + File(arg).parentFile.parentFile.path + ) + } + } + } + + ldFilePath(architecture).getFile() + .writeText(ldArgs.joinToString(DUMP_FILE_ARGS_SEPARATOR)) + librarySearchpathFilePath(architecture).getFile() + .writeText(librarySearchPaths.joinToString(DUMP_FILE_ARGS_SEPARATOR)) + } + } + + fun moduleSwiftSources(architecture: AppleArchitecture, moduleName: String) = swiftDump.map { it.file("${architecture.xcodebuildArch}_${moduleName}_swift_sources") } + fun swiftSearchPaths(architecture: AppleArchitecture) = swiftDump.map { it.file("${architecture.xcodebuildArch}_swift_search_paths") } + fun ldFilePath(architecture: AppleArchitecture) = ldDump.map { it.file("${architecture.xcodebuildArch}.ld") } + fun librarySearchpathFilePath(architecture: AppleArchitecture) = ldDump.map { it.file("${architecture.xcodebuildArch}_library_search_paths") } + + private fun swiftcArgsDumpScript() = argsDumpScript("swiftc", KOTLIN_SWIFTC_ARGS_DUMP_FILE_ENV) + private fun ldArgsDumpScript() = argsDumpScript("clang", KOTLIN_LD_ARGS_DUMP_FILE_ENV) + + private fun argsDumpScript( + targetCli: String, + dumpPathEnv: String, + ) = """ + #!/bin/bash + + DUMP_FILE="${'$'}{${dumpPathEnv}}/${'$'}(/usr/bin/uuidgen)" + for arg in "$@" + do + echo -n "${'$'}arg" >> "${'$'}{DUMP_FILE}" + echo -n "$DUMP_FILE_ARGS_SEPARATOR" >> "${'$'}{DUMP_FILE}" + done + + ${targetCli} "$@" + """.trimIndent() + + companion object Companion { + const val KOTLIN_SWIFTC_ARGS_DUMP_FILE_ENV = "KOTLIN_SWIFTC_ARGS_DUMP_FILE" + const val KOTLIN_LD_ARGS_DUMP_FILE_ENV = "KOTLIN_LD_ARGS_DUMP_FILE" + const val DUMP_FILE_ARGS_SEPARATOR = ";" + } + +} + +@DisableCachingByDefault(because = "...") +internal abstract class ComputeLocalPackageDependencyInputFiles : DefaultTask() { + + @get:Input + val localPackages: SetProperty = project.objects.setProperty(File::class.java) + + /** + * Recompute if the manifests change + */ + @get:InputFiles + @get:PathSensitive(PathSensitivity.RELATIVE) + protected val manifests get() = localPackages.map { it.map { it.resolve("Package.swift") } } + + @get:OutputFile + val filesToTrackFromLocalPackages: RegularFileProperty = project.objects.fileProperty().convention( + project.layout.buildDirectory.file("swiftJava/swiftImportFilesToTrackFromLocalPackages") + ) + + @get:Inject + protected abstract val execOps: ExecOperations + + @TaskAction + fun generateSwiftPMSyntheticImportProjectAndFetchPackages() { + val localPackageFiles = localPackages.get().flatMap { packageRoot -> + listOf( + packageRoot.resolve("Package.swift") + ) + findLocalPackageSources(packageRoot) + }.map { + it.path + } + filesToTrackFromLocalPackages.getFile().writeText( + localPackageFiles.joinToString("\n") + ) + } + + @Suppress("UNCHECKED_CAST") + private fun findLocalPackageSources(path: File): List { + val jsonBuffer = ByteArrayOutputStream() + execOps.exec { exec -> + exec.workingDir(path) + exec.standardOutput = jsonBuffer + exec.commandLine("swift", "package", "describe", "--type", "json") + exec.environment.keys.filter { + // Swift CLIs try to compile the manifest for iphonesimulator... with these envs + it.startsWith("SDK") + }.forEach { + exec.environment.remove(it) + } + } + val packageJson = Gson().fromJson( + jsonBuffer.toString(), Map::class.java + ) as Map + val targets = packageJson["targets"] as List> + val relativeSourceRootPaths = targets + .filter { + val moduleType = it["module_type"] + moduleType == "SwiftTarget" || moduleType == "ClangTarget" + } + .map { + it["path"] as String + } + return relativeSourceRootPaths.map { + path.resolve(it) + } + } + + companion object { + const val TASK_NAME = "computeLocalPackageDependencyInputFiles" + } +} + + +internal enum class AppleArchitecture : Serializable { + ARM64, + X86_64, + ARMV7K, + ARM64_32; + + val xcodebuildArch get() = clangArch + val clangArch + get() = when (this) { + ARM64 -> "arm64" + X86_64 -> "x86_64" + ARMV7K -> "armv7k" + ARM64_32 -> "arm64_32" + } +} + +internal const val XCODEBUILD_SWIFTPM_CHECKOUT_PATH_PARAMETER = "-clonedSourcePackagesDirPath" +internal const val SYNTHETIC_IMPORT_TARGET_MAGIC_NAME = "_internal_linkage_SwiftPMImport" \ No newline at end of file