This message was deleted.
# community-support
s
This message was deleted.
d
A bit more specific context in my case in case it helps.... • I have a web framework that comes with both a "library" and an "application" plugin. So I am already in a place where I can easily create a producer / consumer relationship. • I have tried to read "https://docs.gradle.org/current/userguide/cross_project_publications.html" (Sharing outputs between projects) multiple times, but I never really understood it. • A library might include a JS klib and/or a JVM jar. If there's JS code, I want to include metadata specific to it; and JVM code with its own metadata. • I want to include some common metadata that should always be present (for example, the version of my framework used to build this library), no matter whether just a JS klib, just a JVM jar, or both are produced.
One approach that's easy to wrap my head around is, at least for the JS and JVM metadata, to include it inside the META-INF folders of the klib and jar. As for the common metadata, that's a bit weirder. If a library contains both JS and JVM artifacts, do I duplicate the common metadata into both? Alternately, I think I can share the common metadata (and maybe even the JS/JVM metadata) using a Gradle configuration OR variant-aware resolution (this is my understanding based on the docs linked earlier).
So it seems like these are my choices? 1) Just put all the metadata (common, JVM, JS) into the library artifacts under META-INF. Common metadata will get duplicated. 2) Put platform specific metadata (JVM, JS) into the library artifacts, and use a Gradle configuration for common metadata. 3) Put platform specific metadata (JVM, JS) into the library artifacts, and use variant-aware resolution for common metadata. 4) Use Gradle configurations for all metadata. 5) Use variant-aware resolution for all metadata.
I'm genuinely frozen -- I have no idea which option is best, or if there's a 6th option I'm not considering, and I have no idea how to figure this out. Hoping that someone in this community can shed light on my confusion. Thank you!
v
I think you are mixing up concepts a bit. Actually, 2 and 4 are irrelevant for your case. They are just simplifications of 3 and 5 for usage within one build, but not to provide it to other consumers. And also 3 and 5 are about putting something in a consumable configuration, but also assigning attributes that the consumer can then use for resolving the right thing. What metadata are you talking about? What kind of metadata? How does the consumer consume that metadata? If you talk about metadata in the context of libraries, the metadata is more the information that is kept in
pom.xml
,
ivy.xml
, or the Gradle Module Metadata. Those metadata are used to resolve the artifacts you need in the end, so for example your consumer depends on
foo:bar:1.0
with capability
foo:bar-js
to resolve to the JS feature variant or capability
foo:bar-jvm
to resolve to the JVM feature variant. If the "metadata" you refer to is just some resource your consumer or library needs at runtime, it is not so much metadata, but a resource. You could for example package it with each of the variants, or you could have some common artifact if possible that the other two then depend on or something like that. If that is not what you meant, you probably need to become even more concrete, or optimally show in an MCVE what you mean exactly.
d
Thanks for the reply!
For the JS / JVM artifacts, the user's code is annotated and the library plugin processes it, generating metadata that contains information discovered during that process pass. The application plugin finds those files and uses it to generate code in the user's final application that calls the right methods from the library. As for the common metadata, it will for now just contain the version of the framework used to build the library. The application plugin will just use its existence for now to check if the library was built by my framework, and if not, it will skip over looking for stuff in it. In the future, I can imagine checking that version and branching behavior in the application plugin based on it. I could also imagine more information getting put into that common metadata over time.
And yes, I think what I want to expose here is more "resource" and less "metadata" if the terms have a loaded meaning in this case.
For a while now, I've already solved the JS / JVM case by bundling the resources I needed in the artifacts themselves. But now I have a common resource I want to put somewhere, and I'm not sure where it should go.
Concrete example:
Copy code
# user's library code
src/
  kotlin/
    jsMain/
       ... stuff ...
    jvmMain
       ... stuff ...
build.gradle.kts
  # applies com.varabyte.kobweb.library
build/
  libs/
     user-library-1.2.3-js.klib
     user-library-1.2.3-jvm.jar

# user's application code
src/
  ...
build.gradle.kts
  # applies com.varabyte.kobweb.application
  # specifies a dependency on `user-library:1.2.3`
build/
  generated/
     # main.kt calls register methods both from all libraries as well
     # as application code if annotated
     main.kt
Currently, the library plugin processes the user's code, generating
META-INF/kobweb/frontend.json
and
META-INF/kobweb/backend.json
and adding them as resources for each platform-specific artifact so they get bundled with them. Later, the application plugin runs over all of its dependencies, unzipping artifacts and looking for the
frontend.json
and
backend.json
files. I'd like to limit the set of dependencies that the application plugin runs over, which I could do if I associated a top-level, common resource with the library artifact. I'm thinking it makes sense to use Gradle machinery to expose that common resource. But then, once I do that, maybe I should be using it for the
frontend.json
and
backend.json
resources as well?
After digging into the options a lot more over the weekend, I feel like what I really want to do is record some top level values in the Gradle .module file that gets created next to the pom.xml file. Basically my own version of what Gradle is doing. However, as far as I can tell, writing (and later reading) out top level module properties is not something that's exposed to users?
v
Correct, the GMM file is not a store for custom information. Besides custom attributes you set on variants of course. If you would want to put arbitrary things in there, you would need to majorly abuse it. But I'm not sure that really is what you want. You don't have the GMM at runtime.
d
Well I do want to add a common key/value pair that gets associated with a module, but no, I don't want to abuse anything.
At this point I think I'm just going to create a resource data file and duplicate it into every artifact my module builds (so one copy in the JS klib and one copy in the JVM jar)
I suppose I could add a custom attribute to all relevant configurations as well, but I already have code putting resources into JS/JVM artifacts, and it's a value I want to check when I'm already extracting those resources anyway, so this seems like the path of least resistance.
v
Yeah, as I said. POM / Ivy / GMM, are all resolution time information / metadata. If it is about information you need at runtime, it is better to package that information into your artifacts, or have some common artifact both depend on if possible.
But I guess with that multi-platform, this could be problematic?
d
I don't think it will be too problematic (I'm trying that now). It's more that I wasn't sure if I should be smarter about how I was doing this, taking advantage of some Gradle feature I wasn't aware of. My biggest thought was, if I could query Gradle for some metadata about the library, then I wouldn't even need to unzip artifacts in the first place to peek around, and that seemed to make me feel that was the right way. But the more I've learned, the more I think Gradle isn't designed for doing this, so I'll just go ahead, drop the resource in my library artifacts, then just unzip every dependency from the application side, looking for those resource files directly.
v
If you need those files unzipped or transformed or whatever at build time, you could maybe leverage "artifact transforms". But if you need them anyway at execution time, I would just read them from the classpath, yeah.
👍 1
i
Couldn't you do this by adding a capability to the libraries you're interested in, then creating a configuration that extends
implementation
with that added capability? This way, Gradle would automatically figure out which subset of dependencies from the
implementation
configuration have that capability, during the dependency resolution phase, so you could trust that all libraries left are the ones you're interested in?
d
As far as I understand it, capabilities are unique. But I want people to be able to include mutliple libraries built by my framework.