Kotlin Multiplatform - Objective-C 互操作性架构问题架构 x86_64 的未定义符号

Posted

技术标签:

【中文标题】Kotlin Multiplatform - Objective-C 互操作性架构问题架构 x86_64 的未定义符号【英文标题】:Kotlin Multiplatform - Objective-C interoperability architecture issue Undefined symbols for architecture x86_64 【发布时间】:2021-05-30 15:27:36 【问题描述】:

我刚刚尝试使用本机互操作功能,因为我需要在我的库中使用用 Objective-C 编写的本机代码。 所以首先我尝试使用简单的 hello 来互操作 Objective-C 代码

毕业:

kotlin 
     ...
 
     val iosX64 = iosX64("ios") 
          compilations.getByName("main) 
              val myInterop by cinterops.creating 
                 defFile(project.file("src/nativeInterop/cinterop/objective-c-file.def"))
              
          
     

     val iosArm32 = iosArm32 ("iosArm32 ") 
          compilations.getByName("main) 
              val myInterop by cinterops.creating 
                 defFile(project.file("src/nativeInterop/cinterop/objective-c-file.def"))
              
          
     

     val iosArm64 = iosArm64("iosArm64") 
          compilations.getByName("main) 
              val myInterop by cinterops.creating 
                 defFile(project.file("src/nativeInterop/cinterop/objective-c-file.def"))
              
          
     

     ...

我的 .def 配置:

language = Objective-C
headers = Hello.h Foundation/Foundation.h
package = org.native

compilerOpts = -Isrc/nativeInterop/include

linkerOpts = -framework Foundation "-F/Applications/Xcode 11.3.1.app/Contents/Developer/Platforms/"

这是项目结构的样子

我的你好.h

#import <Foundation/Foundation.h>

@interface Hello: NSObject

+ (id) init;
- (void) helloObjectiveC;

@end

你好.m

#import "Hello.h"

@implementation Hello

+ (id) init 
- (void) helloObjectiveC 
    printf("hello Objective c interop")


@end

从 gradle 运行 cinterop 任务后,从那个 hello 类生成的 klib,当然现在我可以将它导入到我的 kotlin 项目中。例如在我的 iOS 模块中:

package bca.lib

import org.native.Hello

actual object Platform 

    actual fun getPlatform() = "iOSMain"
    
    fun helloNativeInterop() 
        val hello = Hello()
        hello.helloObjectiveC()
    


然后我尝试在我的单元测试中调用它以检查我是否可以获得结果,或者我也尝试将它构建到框架但我得到一个错误:

> Task :cleanIosTest UP-TO-DATE
> Task :cinteropMyInteropIos UP-TO-DATE
> Task :generateBuildKonfig UP-TO-DATE
> Task :generateIosMainAppDatabaseInterface UP-TO-DATE
> Task :compileKotlinIos UP-TO-DATE
> Task :iosProcessResources UP-TO-DATE
> Task :iosMainKlibrary UP-TO-DATE
> Task :compileTestKotlinIos UP-TO-DATE
> Task :linkDebugTestIos FAILED
e: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld invocation reported errors
Please try to disable compiler caches and rerun the build. To disable compiler caches, add the following line to the gradle.properties file in the project's root directory:
    
    kotlin.native.cacheKind=none
    
Also, consider filing an issue with full Gradle log here: https://kotl.in/issue
The /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld command returned non-zero exit code: 1.
output:
Undefined symbols for architecture x86_64:
  "_OBJC_CLASS_$_Hello", referenced from:
      objc-class-ref in result.o
ld: symbol(s) not found for architecture x86_64
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':linkDebugTestIos'.
> Compilation finished with errors
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.
* Get more help at https://help.gradle.org
Deprecated Gradle features were used in this build, making it incompatible with Gradle 7.0.
Use '--warning-mode all' to show the individual deprecation warnings.
See https://docs.gradle.org/6.6.1/userguide/command_line_interface.html#sec:command_line_warnings
BUILD FAILED in 8s


任何人都知道如何解决这个问题,或者也许不可能以这种方式进行互操作?我只是需要它,以便在我成功将其构建到 .framework 后稍后在我的 iOS 应用程序中使用它

【问题讨论】:

从技术上讲,问题很简单。互操作处理器从 Objc 标头生成 Kotlin。但是,您还需要构建 Objc 并链接二进制文件。 linkDebugTestIos 正在尝试构建测试 kexe,但找不到 _OBJC_CLASS_$_Hello。我不知道有什么好方法可以做到这一点,或者我会把这个作为答案而不是评论:) 我相信有人有一个非常简单和明显的方法来做到这一点。 @KevinGalligan 所以这意味着我需要对二进制链接做一些事情......,当构建发生时......? 【参考方案1】:

这是一个已知限制,前段时间在 Kotlin 问题跟踪器中进行了描述:KT-39562。长话短说,cinterop 工具不提供开箱即用的源文件选项。这里最简单的方法是从您的 Objective-C 代码创建一个框架。之后,您将能够以文档中所述的简单方式使用它。

【讨论】:

我明白了,是的,我可以轻松地将其编译为静态库,然后将其包含在 def 文件中。如果不支持这种方式,请注意。非常感谢!

以上是关于Kotlin Multiplatform - Objective-C 互操作性架构问题架构 x86_64 的未定义符号的主要内容,如果未能解决你的问题,请参考以下文章

Kotlin-Multiplatform 中的 CPointer

Kotlin-multiplatform:如何执行 iOS 单元测试

在 kotlin-multiplatform 上生成 UUID?

Kotlin Multiplatform 项目包含 cocoapod 依赖项

如何使用 Kotlin-Multiplatform 在 iOS 应用程序的后台线程中运行任务?

如何在 Kotlin Multiplatform(纯 kotlin)中进行延迟