We have the same problem, from what I can see in g...
# multiplatform
l
We have the same problem, from what I can see in gradle is that something is invalidating the cache every time
Copy code
The task was not up-to-date because of the following reasons:	
Output property 'buildResult$kotlin_gradle_plugin_common$1' file /Users/leon/git/voize/voize-core/android/mobile/build/cocoapods/synthetic/ios/build/XCBuildData/cbfe183fac4926b39634880cb9ecea50.xcbuilddata has been removed.	
Output property 'buildResult$kotlin_gradle_plugin_common$1' file /Users/leon/git/voize/voize-core/android/mobile/build/cocoapods/synthetic/ios/build/XCBuildData/cbfe183fac4926b39634880cb9ecea50.xcbuilddata/attachments has been removed.
When I look at the gradle task dependencies of our Multiplatform Project where projectB depends on projectA it looks like this
projectA:podBuildX <- projectA:cinteropXArm64 <- projectA:commonizeCInterop <- projectA:compileNativeMainKotlinMetadata <- projectA:iosArm64MetadataElements <- projectB:transformCommonMainDependenciesMetadata
for some reason the projectB transformCommonMainDependenciesMetadata task depends on the podBuild on projectA. This causes the ios podBuild to be triggered even if I only want to build for android on a mac. In combination with the gradle cache not working for the podBuild task not working, we spent 1 minute every time we do an android build, building ios pods.
t
@Andrey Yastrebov could you help here?
👌 1
l
hey @Andrey Yastrebov do you have an update on this?
a
Not yet, I'll try to take a look this week
l
I will create a reproducer project, so far I can only reproduce this in combination with cocoapods and ksp plugin and ksp common code generation https://github.com/google/ksp/issues/567
thank you color 1
Here the reproducer, it shows two errors: 1. if you run
./gradlew installDebug --scan
it will also run the PodBuild Tasks but they should not run for the android only build 2. if you run
./gradlew installDebug --scan
twice, in the second run all the PodBuild Tasks are not
UP-TO-DATE
I checked different combinations of Pods and for some the gradle tasks are correctly UP-TO-DATE, but for others it is not UP-TO-DATE
I think the problem 2) comes from
PodBuildTask
buildResult
, because this is referenced in the no up to date reason https://kotlinlang.slack.com/archives/C3PQML5NU/p1738263403334339?thread_ts=1725462362.768709&amp;cid=C3PQML5NU
Copy code
@Suppress("unused") // declares an output
    @get:OutputFiles
    internal val buildResult: FileCollection = objectFactory.fileTree()
        .from(synthetic.map { it.dir("build") })
        .matching {
            it.include("**/${pod.get().schemeName}.*/")
            it.include("**/${pod.get().schemeName}/")
        }
But from looking at the code I don't understand why the path
.../build/XCBuildData/...
is part of this
buildResult
, it does not match the
include
and when I print all the task outputs in gradle I also don't see the
.../build/XCBuildData/...
path.
a
@Leon Kiefer can you clarify why do you need ksp? Also the sample project is not configured corrrectly. Do you want to use pods in
:shared
project or only in
:composeApp
or probably both?
Yes, there is probably a problem with pod build tasks output. If you have several pods, they share one build folder and in some circumstances this can lead to the error. On the other hand I've managed to build ios artifact and all the pod build tasks were up to date. The second thing, I'm not sure about this hack:
Copy code
tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinCompile<*>>().configureEach {
    if(name != "kspCommonMainKotlinMetadata") {
        dependsOn("kspCommonMainKotlinMetadata")
    }
}
If I remove it, the task
:installDebug
runs without triggering
:podBuild...
l
I just extracted parts of our project config which are required to reproduce the issues we have in our project. I did not add all the code and not all the configuration, plugins and ksp configuration we use. We use KSP with the
reakt-native-toolkit
processor, which I also added to the demo project, this ksp processor generates common code, so we need to have the
dependsOn("kspCommonMainKotlinMetadata")
workaround (and also other config, which I not included in the demo project to keep it simple). The workaround is from here https://github.com/google/ksp/issues/567#issuecomment-1510477456. How did you build the iosArtifact without invalidating the podBuild Tasks?
a
How did you build the iosArtifact without invalidating the podBuild Tasks?
Removed the ksp stuff. Actually there is still a problem, from the second build the podBuild tasks are not up-to-date, but from 3-4 attempt they are. Need to investigate this.
We use KSP with the
reakt-native-toolkit
processor
I'm not familiar with ksp, but this problem is also worth investigating
👍 1
🙏 1
l
This issue currently blocks us on using compose previews, because everytime the previews are updated, the pods are build again.
a
Sorry, can't provide you with any workaround for now. The issue has been created and it is in our backlog
l
Is there an youtrack issue? So we can subscribe to updates?
a
Sure. This is for podBuild caching KT-74901 :podBuild tasks are never cached
And this one for KSP setup issue KT-75327 podBuild tasks are executed with KSP setup
l
thanks a lot
a
Hi @Leon Kiefer, I've played with KSP and cocoapods a bit and I think I have a solution for your problem. So, the problem comes from mixing cocoapods in multi-module setup. The best would be to use cocoapods only in top-level module (:composeApp in your case), but it is also possible to have cocoapods integration in other modules. I've created a new sample project to showcase the problem and a workaround.
Copy code
composeApp
|    ├──ios
|    |  └──cocoapods
|    |       ├──dependency_1
|    |       ├──dependency_2
|    |       ├──dependency_3
|    |       └──dependency_4 (link only)
|    └──android
|  
└──shared  
    ├──ios
    |  └──cocoapods
    |       └──dependency_4
    └──android
So we have
composeApp
with some cocoapods dependencies and
shared
module which also have a cocoapod dependency. From the ksp docs we need to specify the following dependency in order to generate metadata from
commonMain
Copy code
tasks.withType<KotlinCompilationTask<*>>().configureEach {
    if (name != "kspCommonMainKotlinMetadata") {
        dependsOn("kspCommonMainKotlinMetadata")
    }
}
This solution works fine for
composeApp
and when running
./gradlew installDebug
the pod build tasks for
dependency_1/2/3
will not be triggered, but it will trigger the pod build for
dependency_4
. To fix that, we can disable all iOS related tasks when running
installDebug
To summarize: • try to use cocoapods only for top level module • use as little pods as possible (even better to not use them at all) • disable ios related tasks when running
installDebug
or any other android related task
And here is a sample with cocoapods and ksp integration
l
Thanks for the investigation. I have some questions: 1. the original reproducer of mine only uses pods in the
shared
project so it does not mix cocoapods in multi-modules. When I move the pods in your sample project into the
shared
project I can reproduce the problem again and every time I execute installDebug the pods are also rebuild. 2. how to disable all iOS related tasks during android builds? And is this even possible if we have tasks which are required by android which depend on common code generation of KSP?
a
1. in your project
shared
is not a top level module, it is a dependency for
composeApp
. It's better to have pods in top level module only. The second thing is that you use direct integration of
composeApp
and cocoapods in
shared
, you shouldn't mix it. 2. Add this to your build script (see in my sample)
Copy code
gradle.taskGraph.whenReady {
    if (allTasks.any { it.name.contains("installDebug", ignoreCase = true) } &&
        allTasks.any { !it.name.contains("syncFramework", ignoreCase = true) }) {
        allTasks.forEach { task ->
            if (task.name.matches(Regex(".*(pod|ios|cocoa).*", RegexOption.IGNORE_CASE))) {
                task.enabled = false
            }
        }
    }
}
l
1. Usually you don't want to have code or dependencies in the top level module and instead in the sub modules. I also don't see the setup you describe in the official kotlin sample projects. They all use the setup where there are sub projects which depend on each other. 2. this does not work for us, because we use gradle composite build and the two projects are actually in to different gradle inlcuded builds, the taskGraph only shows tasks from the current project and does not allow to manipulate tasks in a different include build.
a
You can refer to documentation for cocoapods setup https://kotlinlang.org/docs/native-cocoapods.html
l
yes this is exactly how I did it in the sample project I shared, the top level module has and the sub modules have the plugin applied and the pods configured
Copy code
alias(libs.plugins.kotlinCocoapods) apply false
Here is a screenshot form the documentation you linked
a
No, your configuration is not correct.
shared
is just a dependency for
composeApp
First of all you don't need this in
shared
Copy code
listOf(
    iosX64(),
    iosArm64(),
    iosSimulatorArm64()
).forEach { iosTarget ->
    iosTarget.binaries.framework {
        baseName = "shared"
        isStatic = true
    }
}
In cocoapods block you don't need this information, as we just want to add some pod dependencies
Copy code
version = "1.0"
ios.deploymentTarget = "16.0"
summary = "Some description for a Kotlin/Native module"
homepage = "Link to a Kotlin/Native module homepage"
name = "MyCocoaPod"

framework {
    baseName = "MyFramework"
    isStatic = false
}
l
Thanks for the clarification, I tried to remove all of the config you named, but had few problems: 1.
Cocoapods Integration requires pod version to be specified.
2.
Specs satisfying the 'AppAuth (= 1.5.0)' dependency were found, but they required a higher minimum deployment target.
Therefore I had to keep this config:
Copy code
version = "1.0"
        ios.deploymentTarget = "16.0"
Now the framework is only configured in the project which is published as Framework for ios. however still a composeAppinstallDebug will cause the pods definend in shared to be rebuild. I can send you the updated sample project if you like.
a
Therefore I had to keep this config:
Yes, thats right, or as alternative you can specify version on top level
Copy code
group = "org.example.shared"
version = "1.0-SNAPSHOT"

kotlin {
composeAppinstallDebug will cause the pods definend in shared to be rebuild
After further investigation I've noticed that it's more like ksp bug. Look at the tasks graph
./gradlew installDebug --dry-run
Copy code
:shared:compileAppleMainKotlinMetadata SKIPPED
:shared:metadataAppleMainProcessResources SKIPPED
:shared:metadataAppleMainClasses SKIPPED
:shared:transformIosMainDependenciesMetadata SKIPPED
:shared:compileIosMainKotlinMetadata SKIPPED
:shared:metadataIosMainProcessResources SKIPPED
:shared:metadataIosMainClasses SKIPPED
:shared:iosArm64MetadataElements SKIPPED
:shared:iosSimulatorArm64MetadataElements SKIPPED
:shared:iosX64MetadataElements SKIPPED
:composeApp:transformCommonMainDependenciesMetadata SKIPPED
:composeApp:kspCommonMainKotlinMetadata SKIPPED
ios related tasks should not be triggered. I'll keep you updated
🙏 1