Hello, I have 3 kmp library modules in same kmp pr...
# multiplatform
d
Hello, I have 3 kmp library modules in same kmp project as follow: •
library-base
library-data
which "tries to" embed
library-base
via
api(projects.libraryBase)
librart-multi
which adds
library-data
via
api(projects.libraryData)
My problem is
library-multi
does not add
library-data
. And
library-data
does not have
library-base
. Calling
./gradlew :library-multi:publishAndroidReleasePublicationToMavenLocal
creates the needed AAR files, but I don't have these dependencies embed. Here is a simple repo which provides the problem - https://github.com/dimitar71/multi-kmp. Added a brief explanation of the issue. You can run
refapp
, since it is an Android app module. The issue which I am struggling is, that
api(projects.libraryData)
is not adding the source code when creating the AAR builds. Can someone provide details what I am doing wrong? Reading the docs around, using ChatGPT, Copilot, everything points that I am doing things right. However, no luck.
c
./gradlew library multipublishAndroidReleasePublicationToMavenLocal
This only publishes
library-multi
. If you want to use the other libraries, you have to publish them too!
It's exactly the same in regular Android without KMP modules, by the way If you instead run
Copy code
./gradlew publishAndroidReleasePublicationToMavenLocal
then everything should work
d
Same problem, same error. I am using
api()
(not
implementation()
). So technically,
library-data
source code should be part of
library-multi
AAR. But it is not, exactly the same error even when calling
./gradlew publishAndroidReleasePublicationToMavenLocal
c
So technically,
library-data
source code should be part of
library-multi
AAR.
No, that's not what
api()
does.
api()
is the same as
implementation()
except it makes symbols available at compile-time. It doesn't touch the contents of the archive in any way.
But it is not, exactly the same error even when calling
./gradlew publishAndroidReleasePublicationToMavenLocal
Are you sure? I've run your first command and the libraries were not in
~/.m2
, then after executing that one they were in
~/.m2
.
Do you have a Gradle command to run the app? I don't have Android studio here
d
Will record a short video, 2 min
Here is the video, same problem
Screen Recording 2024-12-24 at 11.32.37 AM.mov
c
uh your files are named weirdly
j
if you run
./gradlew publishToMavenLocal
, is the issue fixed?
c
what your build publishes is
library-data-android
, but what your build wants is
library-data
… Did you rename the artifacts somewhere?
d
This is how multiplatform works. It does this suffix at the end
-android
The MAJOR problem I am having 🙂
j
It is a kmp library, that is the reason it has
-android
suffix
c
No, that's not how normal KMP works. In a normal KMP module, there should be a common artifact too
j
As it is not running the whole publication, the files that contain the “wiring” to understand that calling it without -android should use -android are not being published
Try
Copy code
./gradlew publishToMavenLocal
c
Do: •
./gradlew publishToMavenLocal
./gradlew :refapp:checkDebugAarMetadata
it should work
d
OMG! Thank you! Yes, ``publishToMavenLocal`` solved the problem
c
Your problem is you're running:
:library-multi:publishAndroidReleasePublicationToMavenLocal
but that: • only publishes
:library-multi
• only publishes the
AndroidReleasePublication
So you're not publishing
:library-base
nor
:library-multi
, and you're not publishing
:library-multi
's
common
, only its
android
d
So... does it mean I don't need to use
api()
? Or should
api()
still be needed?
j
That depends on what you want
if you want to get the dependencies transitively or explicitly
👎 1
c
api()
and
implementation()
are different If you're writing a module X, and you depend on a module Y If X has a type from Y in any of its
public
declarations, you should use
api()
("it's part of my API!") If X only uses Y inside code but it never appears in public signatures, you should use
implementation()
("it's part of my implementation!")
j
if you use api for a dependency in the module A, you don’t need to explicit declare that dependency in the module B and you will be able to see the symbols
d
I want them explicitly, since the whole idea is a "library" which consists of 5 small supplemental library modules. However, couldn't go with BOM, since some of these lib modules are using
core
lib module. A bit messy, but seems the only approach of what we are trying to achieve
c
@Javier that's incorrect, both
api()
and
implementation()
are transitive.
compileOnly
and
runtimeOnly
are not transitive.
j
it being a library or not is not a reason to use only implementation or only api, it depends on what your consumers expect to do
if all APIs are necessary, having to add 5 libraries versus 1 would be annoying
d
I've always thought
api()
embeds code. TIL is every day. This library is a bit of everything - for some cases all 5, for others just
core
, hard to explain. Is there a gradle ability to include (not just use, but include) a dependency into our own dependency?
c
There are plugins for that (the key name is "shading") but in general it's a very bad idea, because it duplicates the contents of those libraries
j
@CLOVIS just semantics, the concept about what I mean there with transitively is clear, it is even shown in the docs
> Dependencies appearing in the api configurations will be transitively exposed to consumers of the library, and as such will appear on the compile classpath of consumers. Dependencies found in the implementation configuration will, on the other hand, not be exposed to consumers, and therefore not leak into the consumers' compile classpath. This comes with several benefits:
if your consumers will add all those libraries by default, always, create a core module which uses api on the rest of modules so your consumers can add ir easily
c
@Javier that's much more nuanced than what you said (and a bit ambiguous).
api()
: • transitively available to consumers (
runtimeClasspath
) • transitively exposed to consumers (
compileClasspath
)
implementation()
: • transitively available to consumers (
runtimeClasspath
) • not exposed to consumers (
compileClasspath
) sadly this is always explained in ambiguous ways so many people are confused 😕
j
@CLOVIS you are ignoring the context and the messages I shared, which are the key, as I talk about being able to use the symbols.
c
…I'm responding to your very first message, there was no context there… Anyway, the OP's problem is solved, there's no need to further debate this
j
I wrote two messages because I am from mobile, both are part of the context. You were too fast than I was able to be from mobile 😛 Agree about the problem is solved 👍
😅 1
d
I want to thank both of you! This was quick and on point! Happy Holidays to both! And cheers!
🙂 1
c
Happy holidays! K