JetBrains / kotlin-native

Kotlin/Native infrastructure
Apache License 2.0
7.02k stars 566 forks source link

How to generate interop stubs for custom objc framework #1764

Closed ildarsharafutdinov closed 6 years ago

ildarsharafutdinov commented 6 years ago

hi,

I'm trying to add RxCocoa to sample project kotlin-native/samples/uikit.

RxCocoa is built with carthage and saved as kotlin-native/samples/uikit/Carthage/Build/iOS/RxCocoa.framework.

My RxCocoa.def contains the following:

depends = Foundation darwin posix
language = Objective-C
package = RxCocoa 
headers = RxCocoa/RxCocoa.h

headerFilter = RxCocoa/**

compilerOpts = -framework RxCocoa
linkerOpts = -framework RxCocoa

I run cinterop as follows(working dir is kotlin-native/samples/uikit/):

../../dist/bin/cinterop -def RxCocoa.def  -o RxCocoa
Exception in thread "main" java.lang.Error: /var/folders/zk/9j2yt_lj0hj4pf_qyk46j2gc0000gn/T/tmp2133410363181771511.m:1:10: fatal error: 'RxCocoa/RxCocoa.h' file not found

What directory does cinterop use as headers source? Should I pass a command line param? Or is it better to move RxCocoa.framework to another location?

I checked your docs, but didn't manage to find an answer. Any help is appreciated.

olonho commented 6 years ago

Use -compilerOpts "-Iwhere/my/headers/are"

ildarsharafutdinov commented 6 years ago

@olonho, thank you.

However, I guess I need more help.

I looked at https://github.com/JetBrains/kotlin-native/blob/master/platformLibs/build.gradle and updated kotlin-native/samples/uikit/build.gradle to be as follows:

apply plugin: 'konan'

konan.targets = ['iphone', 'iphone_sim']

konanArtifacts {
  program('app') {
    libraries {
      klibs 'RxCocoa'
    }
  }

  interop ('RxCocoa') {
    defFile 'RxCocoa.def'
    artifactName 'RxCocoa'
  }
}

and now ../gradlew compileKonanappIos_x64 results in:

> Task :uikit:compileKonanAppIos_x64
error: compilation failed: /targets/ios_x64/kotlin

 * Source files: main.kt
 * Compiler version info: Konan: 0.8-dev / Kotlin: 1.2.70
 * Output kind: PROGRAM

exception: java.nio.file.NoSuchFileException: /targets/ios_x64/kotlin

Am I going right direction? Also, once I've produced klib with cinterop, do I still need to add my framework to xcode project manually?

ildarsharafutdinov commented 6 years ago

it looks like i just need to move interop to be above program

ildarsharafutdinov commented 6 years ago

I added include dirs and framework linker flag, but I still get "file not found exception".

Here is my build.gradle:

apply plugin: 'konan'

konan.targets = ['iphone', 'iphone_sim']

konanArtifacts {
  interop ('RxCocoa') {
    defFile 'RxCocoa.def'

    includeDirs 'Carthage/Build/iOS/RxCocoa.framework/Headers'
  }

  program('app') {
    libraries {
      artifact 'RxCocoa'
    }

    linkerOpts '-F Carthage/Build/iOS'
  }
}

--info doesn't' shed any light on what file is not found.

Is there any way to know what file the compiler fails to find? Any hint on build.gradle is also welcome.

drofwarcs commented 6 years ago

I believe includeDirs is looking for an absolute path. I was able to generate stubs for AFNetworking using the following:

def file

language = Objective-C
headers = Headers/AFURLSessionManager.h Headers/AFURLResponseSerialization.h Headers/AFHTTPSessionManager.h
compilerOpts = -framework AFNetworking
linkerOpts = -F/absolute/path/to/objcframework -framework AFNetworking

and in build.gradle file

konanArtifacts {
    interop('AFNetworking'){
        packageName 'com.afnetworking'
        includeDirs{
            allHeaders '/absolute/path/to/objcframework/AFNetworking.framework'
        }
    }

    program('MeteoriteFinder'){
        libraries {
            artifact 'AFNetworking'
        }
    }
}

Notice, that I needed to put absolute path in both files. My builds would fail if it was missing out of either. Maybe I was doing something wrong, but this is the only way I have been able to get interop to generate stubs from 3rd party objc frameworks. Hope that helps.

ildarsharafutdinov commented 6 years ago

@drofwarcs, thanks.

Headers seem to work without absolute paths.

AFNetworking.def:

language = Objective-C
package = com.afnetworking
headers = AFURLSessionManager.h AFURLResponseSerialization.h AFHTTPSessionManager.h
headerFilter = **

compilerOpts = -framework AFNetworking
linkerOpts = -framework AFNetworking

build.gradle:

apply plugin: 'konan'

konan.targets = ['iphone', 'iphone_sim']

konanArtifacts {

  interop('AFNetworking') {
    defFile 'AFNetworking.def'
    includeDirs 'Carthage/Build/iOS/AFNetworking.framework/Headers'
  }

  program('app') {
    libraries {
      artifact 'AFNetworking'
    }

    linkerOpts '-F Carthage/Build/iOS'
  }
}

Cartfile:

github "AFNetworking/AFNetworking" ~> 3.2.1

If I comment the line includeDirs 'Carthage/Build/iOS/AFNetworking.framework/Headers', I get fatal error: 'AFURLSessionManager.h' file not found. It makes me think, that relative path does its work.

However, I didn't manage to build the sample even using absolute path at linkerOpts '-F /<absolute path>/Carthage/Build/iOS'.

Here is how I build(working directory kotlin-native/samples/uikit):

carthage update
...

../../dist/bin/cinterop -def AFNetworking.def  -o AFNetworking -compilerOpts "-ICarthage/Build/iOS/AFNetworking.framework/Headers"
...

../gradlew compileKonanappIos_x64
> Task :uikit:compileKonanAppIos_x64
error: compilation failed: /targets/ios_x64/kotlin

 * Source files: main.kt
 * Compiler version info: Konan: 0.8-dev / Kotlin: 1.2.70
 * Output kind: PROGRAM

exception: java.nio.file.NoSuchFileException: /targets/ios_x64/kotlin
...

It's unclear what file is missed(absolute path to framework didn't work either).

Any advice is highly appreciated.

ildarsharafutdinov commented 6 years ago

@drofwarcs,

In case you decide to get rid of absolute paths, I've finally found good example https://github.com/JetBrains/kotlinconf-app/blob/master/ios/build.gradle

Here how it works for me:

konanArtifacts {

  def productsDir = new File("Carthage/Build/iOS").absolutePath

  interop('AFNetworking') {
    defFile 'AFNetworking.def'

    compilerOpts "-F${productsDir}"
    linkerOpts "-F${productsDir}"

    includeDirs "${productsDir}/AFNetworking.framework/Headers"
  }

  program('app') {
    libraries {
      artifact 'AFNetworking'
    }

    linkerOpts '-rpath', '@executable_path/Frameworks'
    linkerOpts "-F${productsDir}"
  }
}
drofwarcs commented 6 years ago

@ildarsharafutdinov worked like a charm. Thx 👍