How can I build a libcurl Kotlin Native application on Windows? - kotlin

I'm relatively new to Kotlin Native, and to learn more about it, I am studying these two tutorials, one from the official JetBrains documentation and the other from the jonnyzzz blog, both focused on creating an application using C Interop and libcurl:
https://kotlinlang.org/docs/native-app-with-c-and-libcurl.html
https://jonnyzzz.com/blog/2018/10/29/kn-libcurl-windows/
I'm trying to build the above application on Windows 11.
After struggling a little, I finally managed to import the libcurl library in my Kotlin Native project, making it possible to call the relative C functions. So far, so good, but when I try to build the entire project, these are the errors I receive:
e: C:\Users\Nicola\.konan\dependencies\llvm-11.1.0-windows-x64-essentials/bin/clang++ invocation reported errors
The C:\Users\Nicola\.konan\dependencies\llvm-11.1.0-windows-x64-essentials/bin/clang++ command returned non-zero exit code: 1.
output:
lld-link: error: undefined symbol: __declspec(dllimport) curl_easy_strerror
>>> referenced by C:\Users\Nicola\IdeaProjects\SimpleHttpClient\src\nativeMain\kotlin\Main.kt:15
>>> C:\Users\Nicola\AppData\Local\Temp\konan_temp9458678904101419787\result.o:(libcurl_curl_easy_strerror_wrapper33)
lld-link: error: undefined symbol: __declspec(dllimport) curl_easy_init
>>> referenced by C:\Users\Nicola\IdeaProjects\SimpleHttpClient\src\nativeMain\kotlin\Main.kt:15
>>> C:\Users\Nicola\AppData\Local\Temp\konan_temp9458678904101419787\result.o:(libcurl_curl_easy_init_wrapper36)
lld-link: error: undefined symbol: __declspec(dllimport) curl_easy_perform
>>> referenced by C:\Users\Nicola\IdeaProjects\SimpleHttpClient\src\nativeMain\kotlin\Main.kt:15
>>> C:\Users\Nicola\AppData\Local\Temp\konan_temp9458678904101419787\result.o:(libcurl_curl_easy_perform_wrapper37)
lld-link: error: undefined symbol: __declspec(dllimport) curl_easy_cleanup
>>> referenced by C:\Users\Nicola\IdeaProjects\SimpleHttpClient\src\nativeMain\kotlin\Main.kt:15
>>> C:\Users\Nicola\AppData\Local\Temp\konan_temp9458678904101419787\result.o:(libcurl_curl_easy_cleanup_wrapper38)
lld-link: error: undefined symbol: curl_easy_setopt
>>> referenced by C:\Users\Nicola\AppData\Local\Temp\konan_temp9458678904101419787\result.o:(knifunptr_libcurl39_curl_easy_setopt)
clang++: error: linker command failed with exit code 1 (use -v to see invocation)
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':linkDebugExecutableNative'.
> 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
BUILD FAILED in 8s
And this is the code I wrote:
Main.kt:
import libcurl.*
import kotlinx.cinterop.*
fun main(args: Array<String>) {
val curl = curl_easy_init()
if (curl != null) {
curl_easy_setopt(curl, CURLOPT_URL, "http://jonnyzzz.com")
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L)
val res = curl_easy_perform(curl)
if (res != CURLE_OK) {
println("curl_easy_perform() failed ${curl_easy_strerror(res)?.toKString()}")
}
curl_easy_cleanup(curl)
}
}
libcurl.def:
headers = curl/curl.h
headerFilter = curl/*
build.gradle.kts:
plugins {
kotlin("multiplatform") version "1.7.10"
}
group = "me.nicola"
version = "1.0-SNAPSHOT"
repositories {
mavenCentral()
}
kotlin {
val hostOs = System.getProperty("os.name")
val isMingwX64 = hostOs.startsWith("Windows")
val nativeTarget = when {
hostOs == "Mac OS X" -> macosX64("native")
hostOs == "Linux" -> linuxX64("native")
isMingwX64 -> mingwX64("native")
else -> throw GradleException("Host OS is not supported in Kotlin/Native.")
}
nativeTarget.apply {
compilations.getByName("main") {
cinterops {
val libcurl by creating {
val includePath = "C:\\Users\\Nicola\\Documents\\curl-7.87.0\\curl-7.87.0\\include"
defFile(project.file("src/nativeInterop/cinterop/libcurl.def"))
packageName("libcurl")
compilerOpts("-I/$includePath")
includeDirs.allHeaders(includePath)
}
}
}
binaries {
executable {
val buildPath = "C:\\Users\\Nicola\\Documents\\curl-7.87.0\\curl-7.87.0\\builds\\libcurl-vc-x86-release-dll-ipv6-sspi-schannel\\lib\\libcurl.lib"
entryPoint = "main"
linkerOpts(buildPath)
}
}
}
sourceSets {
val nativeMain by getting
val nativeTest by getting
}
}
What am I missing?
Thanks a lot for your precious time.

There's a more complete demonstration of how to link Curl in the Ktor project - although it probably contains a lot more configuration than you need. The important part is
# libcurl.def
linkerOpts.mingw_x64 = -lcurl \
-L/usr/lib64 \
-L/usr/lib/x86_64-linux-gnu \
-L/opt/local/lib \
-L/usr/local/opt/curl/lib \
-L/opt/homebrew/opt/curl/lib \
-LC:/msys64/mingw64/lib \
-LC:/Tools/msys64/mingw64/lib \
-LC:/Tools/msys2/mingw64/lib
This says "this program needs the library libcurl.a." Where will it look for that file? On any path that is provided by -L, so you'll also have to add -L/dir/that/contains/libraries to your .def file.
Alternatively, you could statically link Curl, and then the Kotlin/Native compiler would automatically link the library.
# libcurl.def
staticLibraries = libcurl.a
# -lcurl is not needed
linkerOpts.mingw_x64 = -L/usr/lib64 \
-L/usr/lib/x86_64-linux-gnu \
-L/opt/local/lib \
-L/usr/local/opt/curl/lib \
-L/opt/homebrew/opt/curl/lib \
-LC:/msys64/mingw64/lib \
-LC:/Tools/msys64/mingw64/lib \
-LC:/Tools/msys2/mingw64/lib
Note that it's also possible to get link errors if libcurl.a is compiled for the wrong platform, or if it's compiled using an incompatible version of gcc or libc (because Kotlin/Native uses old versions of gcc and libc).

Related

Kotlin native platform posix compilation without gradle

I am trying to compile a basic Kotlin/Native program with just the kotlinc-native CLI compiler rather than a full Gradle build. However, I cannot get the platform.posix library to work, despite compiling on linux for linux.
The source code I am using is:
import platform.posix.exit
fun main() {
println("Should exit with status 10")
exit(10)
}
When compiled with the following build.gradle.kts it works fine:
plugins {
kotlin("multiplatform") version "1.6.10"
}
repositories {
mavenCentral()
}
kotlin {
linuxX64("native") {
binaries {
executable()
}
}
}
However, when compiled from the command-line with:
kotlinc-native test.kt
I get:
test.kt:1:8: error: unresolved reference: platform
import platform.posix.exit
^
test.kt:4:5: error: unresolved reference: exit
exit(10)
^
Anyone know how to get Kotlin/Native compilation working fully without using Gradle? Or is there some magic that Gradle does that makes it work?
Thanks.

Kotlin/Native project built without errors, but fails to run

I create a Kotlin/Native project with gradle init, and follow the instructions from here, managed to build the project without problems, being generated a build/bin/native/debugExecutable/executable.kexe executable file.
But when I try to run the project, I got the message:
/build/bin/native/debugExecutable/executable.kexe: error while loading shared libraries: libone.so: cannot open shared object file: No such file or directory
I am using a C library, located in the directory ../libone/libone.so (relative to the project folder). I have this *.dex file on the directory src/nativeInterop/cinterop of my project:
headers = libone.h
package = libone
compilerOpts.linux = -I/.../libone
linkerOpts.linux = -L/.../libone -lone
I have tried put the executable (executable.kexe) and the library (libone.so) in the same directory, but do not work either (same error occurs). What I am missing here?
UPDATE I made work manually setting the linux environment variable LD_LIBRARY_PATH to the library directory. I wonder if I could make work without this change in the system.
build.gradle
plugins {
id 'org.jetbrains.kotlin.multiplatform' version '1.5.31'
}
repositories {
mavenCentral()
}
kotlin {
def hostOs = System.getProperty("os.name")
def isMingwX64 = hostOs.startsWith("Windows")
def nativeTarget
if (hostOs == "Mac OS X") nativeTarget = macosX64('native')
else if (hostOs == "Linux") nativeTarget = linuxX64("native")
else if (isMingwX64) nativeTarget = mingwX64("native")
else throw new FileNotFoundException("Host OS is not supported in Kotlin/Native.")
nativeTarget.with {
compilations.main { // NL
cinterops { // NL
libone // NL
} // NL
} // NL
binaries {
executable {
entryPoint = 'main'
}
}
}
sourceSets {
nativeMain {
}
nativeTest {
}
}
}
Makefile for libone
all: libone
libone: libone.o
gcc -shared -o libone.so libone.o -Wl,--out-implib,libone.a
libone.o: libone.c
gcc -fPIC -c libone.c -o libone.o
You can try changing the path in the def file to an absolute path, in my project I usually generate the def dynamically and make it a gradle task. But I still recommend you to use absolute paths anyway, there are enough examples here kotlin native samples

protoc ^ Unresolved reference

I am trying to follow this example, and I've read numerous SoF and have tried countless examples of this, including straight from the official plugin page, but I continue to run into problems building a simple protobuf app
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import com.google.protobuf.gradle.protoc // here my editor gives a red ___ to .protobuf.
buildscript {
repositories {
maven {
url = uri("https://plugins.gradle.org/m2/")
}
}
dependencies {
classpath("gradle.plugin.com.google.protobuf:protobuf-gradle-plugin:0.8.18")
}
}
apply(plugin = "com.google.protobuf")
plugins {
id("com.google.protobuf") version "0.8.18"
id("org.jetbrains.kotlin.jvm") version "1.4.31"
application
}
repositories {
mavenCentral()
}
dependencies {
// Align versions of all Kotlin components
implementation(platform("org.jetbrains.kotlin:kotlin-bom"))
// Use the Kotlin JDK 8 standard library.
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
// This dependency is used by the application.
implementation("com.google.guava:guava:30.0-jre")
// Use the Kotlin test library.
testImplementation("org.jetbrains.kotlin:kotlin-test")
// Use the Kotlin JUnit integration.
testImplementation("org.jetbrains.kotlin:kotlin-test-junit")
}
protobuf { // my editor knows nothing about this
protoc {
artifact = "com.google.protobuf:protoc:0.8.14"
}
}
tasks.withType<KotlinCompile> {
kotlinOptions {
freeCompilerArgs = listOf("-Xjsr305=strict")
jvmTarget = "15"
}
}
tasks.withType<Test> {
useJUnitPlatform()
}
application {
// Define the main class for the application.
mainClass.set("io.example.AppKt")
}
Error:
* What went wrong:
Script compilation error:
Line 50: protoc {
^ Unresolved reference. None of the following candidates is applicable because of receiver type mismatch:
public fun ProtobufConfigurator.protoc(action: ExecutableLocator.() -> Unit): Unit defined in com.google.protobuf.gradle
1 error
* 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
I think you need the statement import com.google.protobuf.gradle.protobuf first, but I'm not sure it can fix your issue.
There is an example project in the protobuf-gradle-plugin repository which is expected to work fine, I think that it can be helpful for you. they are written in Kotlin DSL also. please refer.
https://github.com/google/protobuf-gradle-plugin/blob/master/examples/exampleKotlinDslProject/build.gradle.kts

KMM in Android Studio build return Command PhaseScriptExecution failed with a nonzero exit code

I've just created a new KMM project through out KMM Plugin, but I can't run or even debug in Xcode iosApp part of the project. When I try to run iosApp from Android Studio, the build process fails (Command PhaseScriptExecution failed with nonzero exit code)
The final lines of building was:
FAILURE: Build failed with an exception.
What went wrong:
Execution failed for task ':shared:compileKotlinIosX64'.
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
BUILD FAILED in 8s
1 actionable task: 1 executed
Command PhaseScriptExecution failed with a nonzero exit code
** BUILD FAILED **
The following build commands failed:
PhaseScriptExecution Run\ Script /Users/tamegajr/AndroidStudioProjects/TesteKMM5/build/ios/iosApp.build/Release-iphonesimulator/iosApp.build/Script-7555FFB5242A651A00829871.sh
(1 failure)
Can anyone help to solve this problem?
I had the same issue but this solution helped me:
From KMM pluging you will obtain (on dependencies):
Change this:
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10")
To this:
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.31")
What i found to be the solution was to uncomment the "iosSimulatorArm64()" in the "build.gradle.kts(:shared)".
kotlin {
android()
iosX64()
iosArm64()
iosSimulatorArm64() //sure all ios dependencies support this target
cocoapods {
summary = "Some description for the Shared Module"
homepage = "Link to the Shared Module homepage"
ios.deploymentTarget = "14.1"
podfile = project.file("../iosLink/Podfile")
framework {
baseName = "shared"
}
}
sourceSets {
val commonMain by getting
val commonTest by getting {
dependencies {
implementation(kotlin("test-common"))
implementation(kotlin("test-annotations-common"))
}
}
val androidMain by getting
val androidTest by getting {
dependencies {
implementation(kotlin("test-junit"))
implementation("junit:junit:4.13.2")
}
}
val iosX64Main by getting
val iosArm64Main by getting
val iosSimulatorArm64Main by getting
val iosMain by creating {
dependsOn(commonMain)
iosX64Main.dependsOn(this)
iosArm64Main.dependsOn(this)
iosSimulatorArm64Main.dependsOn(this)
}
val iosX64Test by getting
val iosArm64Test by getting
//val iosSimulatorArm64Test by getting
val iosTest by creating {
dependsOn(commonTest)
iosX64Test.dependsOn(this)
iosArm64Test.dependsOn(this)
//iosSimulatorArm64Test.dependsOn(this)
}
}
}
After some code review from 2-3 months old KMM examples project and comparing them with new ones, I found out a solution for this build failure when trying to run iosApp on Ios Simulators, just apply this change to build.gradle.kts on root project:
From KMM pluging you will obtain (on dependencies):
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10")
Change it to:
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.31")
And that's it, problem solve. I hope someone on Jetbrains can solve this problem in future updates of KMM plugin.
08/30/2020:
It seems that Jetbrains has correct some issues and now you can build and run a KMM project with version 1.7.10, the last stable version at this time.
By the way, if you have any trouble is worthy to check this stack overflow post about JDK version used by Android Studio : How to set or change the default Java (JDK) version on macOS?
Multiplatform error when building iosApp: Command PhaseScriptExecution failed with a nonzero exit code

Unresolved reference: protoc - when using Gradle + Protocol Buffers

I have a gradle project which uses the Kotlin DSL
build.gradle.kts
plugins {
kotlin("jvm") version "1.4.21"
id("com.google.protobuf") version "0.8.10"
}
group = "schema"
version = "0.0.1-SNAPSHOT"
repositories {
mavenCentral()
}
protobuf {
protoc {
artifact = "com.google.protobuf:protoc:3.0.0"
}
}
schema.proto
syntax = "proto3";
package schema;
message Message {
string content = 1;
string date_time = 2;
}
and project structure
schema
-src/main/proto/schema.proto
-build.gradle.kts
Whenever I run gradle build I get error:
FAILURE: Build failed with an exception.
* Where:
Build file '/Users/x/schema/build.gradle.kts' line: 13
* What went wrong:
Script compilation errors:
Line 15: protoc {
^ Unresolved reference: protoc
Line 16: artifact = "com.google.protobuf:protoc:3.0.0"
^ Unresolved reference: artifact
* 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
BUILD FAILED in 8s
The only thing I am doing different to various tutorials is using the Gradle Kotlin DSL. What am I doing wrong?
I think this is happening because you are referencing a single task within the protobuf gradle plugin. Try importing the whole bundle to make use of type-safe accessors as you desired.
import com.google.protobuf.gradle.*
For whatever reason this works
import com.google.protobuf.gradle.protoc
protobuf {
protobuf.protoc {
artifact = "com.google.protobuf:protoc:3.0.0"
}
}
It seems a little ugly having to specify protobuf twice mind