How do I properly commonize libraries? I've got a ...
# kotlin-native
m
How do I properly commonize libraries? I've got a setup where each linuxX64Main, mingwX64Main, etc all depend on nativeMain in project A Then in project B, the nativeMain depends on project A, and again all <platform>Main sourcesets depend on nativeMain. It compiles just fine, but IntelliJ doesn't recognize the functions from project A's cinterops in the nativeMain module. (build.gradle.kts for A and B in thread)
project A (kpy-library):
Copy code
import org.jetbrains.kotlin.konan.target.KonanTarget

plugins {
    kotlin("multiplatform")
    `maven-publish`
    id("com.github.gmazzo.buildconfig")
}

kotlin {
    val targets = listOf(
        // X64
        linuxX64(),
        mingwX64(),
        macosX64(),

        // Arm
        linuxArm64(),
        macosArm64(),
    )

    val hostOs = System.getProperty("os.name")
    val isMingwX64 = hostOs.startsWith("Windows")
    val hostTarget = when {
        hostOs == "Mac OS X" -> KonanTarget.MACOS_X64
        hostOs == "Linux" -> KonanTarget.LINUX_X64
        isMingwX64 -> KonanTarget.MINGW_X64
        else -> error("Unsupported host OS: $hostOs")
    }

    sourceSets {
        val nativeMain by creating {

        }

        val linuxX64Main by getting {
            dependsOn(nativeMain)
        }

        val mingwX64Main by getting {
            dependsOn(nativeMain)
        }

        val macosX64Main by getting {
            dependsOn(nativeMain)
        }

        val linuxArm64Main by getting {
            dependsOn(nativeMain)
        }

        val macosArm64Main by getting {
            dependsOn(nativeMain)
        }
    }

    targets.forEach {
        it.apply {
            val main by compilations.getting {

            }
            val python by main.cinterops.creating {
                if (konanTarget != hostTarget && konanTarget == KonanTarget.MINGW_X64) {
                    defFile = project.file("src/nativeInterop/cinterop/python-github-MingwX64.def")
                }
            }

            binaries {
                staticLib {
                    binaryOptions["memoryModel"] = "experimental"
                    freeCompilerArgs += listOf("-Xgc=cms")
                }
            }
        }
    }
}

val pyVersion = findProperty("pythonVersion") as String? ?: "3.9"
version = "$version+$pyVersion"

buildConfig {
    packageName.set("com.martmists.kpy.cfg")

    buildConfigField("String", "VERSION", "\"${project.version}\"")
}

val generatePythonDef = tasks.create<Exec>("generatePythonDef") {
    val minPyVersion = pyVersion

    group = "interop"
    description = "Generate Python.def file"
    executable = "python3"

    val cinteropDir = "${project.projectDir.absolutePath}/src/nativeInterop/cinterop"
    val parts = minPyVersion.split(".").toMutableList()
    while (parts.size < 4) {
        parts.add("0")
    }
    val versionHex = parts.joinToString("") { it.toInt().toString(16) }

    args(
        "-c",
        """
import sysconfig
paths = sysconfig.get_paths()
template = '''
headers = Python.h
package = python
compilerOpts = -I"{INCLUDE_DIR}"
linkerOpts = -L"{LIB_DIR}" -l python3

---

struct KtPyObject {{
    PyObject base;
    void* ktObject;
}};

// Wrapper func for _PyUnicode_AsString macro
char* PyUnicode_AsString(PyObject* obj) {{
    return _PyUnicode_AsString(obj);
}}
'''.strip()

with open('${cinteropDir.replace('\\', '/')}/python.def', 'w') as fp:
    fp.write(template.format(
        INCLUDE_DIR=paths['platinclude'],
        LIB_DIR='/'.join(paths['platstdlib'].split('/')[:-1]),
        MIN_VERSION_HEX='0x${versionHex}'
    ))
with open('${cinteropDir.replace('\\', '/')}/python-github-MingwX64.def', 'w') as fp:
    fp.write(template.format(
        INCLUDE_DIR="mingw64/include/python${pyVersion}",
        LIB_DIR='/'.join(paths['platstdlib'].split('/')[:-1]),
        MIN_VERSION_HEX='0x${versionHex}'
    ))
        """.trim()
    )
    files(
        "${cinteropDir.replace('\\', '/')}/python.def",
        "${cinteropDir.replace('\\', '/')}/python-github-MingwX64.def",
    )
}

for (target in listOf("LinuxX64", "MacosX64", "MingwX64", "LinuxArm64", "MacosArm64")) {
    try {
        tasks.getByName("cinteropPython${target}") {
            dependsOn(generatePythonDef)
        }
    } catch (e: Exception) {
        println("Skipping cinteropPython${target} as it's not available on this OS")
    }
}
Project B (kpy-sample):
Copy code
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget

plugins {
    id("com.google.devtools.ksp")
    kotlin("multiplatform")
}

repositories {
    mavenCentral()
}

kotlin {
    val hostOs = System.getProperty("os.name")
    val isMingwX64 = hostOs.startsWith("Windows")
    val nativeTarget = when {
        hostOs == "Mac OS X" -> macosX64()
        hostOs == "Linux" -> linuxX64()
        isMingwX64 -> mingwX64()
        else -> throw GradleException("Host OS is not supported in Kotlin/Native.")
    }

    sourceSets {
        val nativeMain by creating {
            dependencies {
                implementation(project(":kpy-library"))
            }

            kotlin.srcDir(buildDir.absolutePath + "/generated/ksp/native/nativeMain/kotlin")
        }

        targets.forEach {
            if (it is KotlinNativeTarget) {
                sourceSets.getByName("${it.name}Main") {
                    dependsOn(nativeMain)
                }
            }
        }
    }

    nativeTarget.apply {
        val main by compilations.getting {

        }

        binaries {
            staticLib {
                binaryOptions["memoryModel"] = "experimental"
                freeCompilerArgs += listOf("-Xgc=cms")
            }
        }
    }
}

val setupMetadata by tasks.creating {
    actions.add {
        println("""
            |===METADATA START===
            |project_name = "${project.name}"
            |project_version = "${project.version}"
            |build_dir = "${buildDir.absolutePath.replace('\\', '/')}"
            |===METADATA END===
        """.trimMargin())
    }
}

ksp {
    arg("projectName", project.name)
}

dependencies {
    for (target in kotlin.targets) {
        if (target is KotlinNativeTarget) {
            add("ksp${target.name.capitalize()}", project(":kpy-processor"))
        }
    }
}
r
Try adding
kotlin.mpp.enableCInteropCommonization=true
to your gradle.properties file. By default cinterop dependencies don't get commonized which means the IDE doesn't know how to read them from shared sources like
nativeMain
.
m
I already have that in my gradle.properties and it still give a symbol does not exist error in the nativeMain sourceset
n
That isn't surprising considering the issue is already known by some Kotliners. With the current state of the issue it will be a showstopper for many Kotlin Native projects that use C libraries.