So I have an interesting issue. I have an android...
# community-support
c
So I have an interesting issue. I have an android app, and an android library (included via git submodules). The project won't build because the android library/git submodule wont compile because it can't find the toml that's used in the android library/git submodule. If I add a line in my settings.gradle.kts for
versionCatalogs { create("lib") { from(mysubmoduledir/the.toml )}}
then it builds (hooray), but then if I try to add a toml in my actual projects gradle folder then I get an error "You can only call the 'from' method a single time." Ideas?
v
Do you also have the conventional
gradle/libs.verisons.toml
file in the project where you get the error? Then Gradle does one of the calls with that file automatically so you have two
from
"calls", even if you only see one.
โ˜๏ธ 1
c
Do you also have the conventional
gradle/libs.verisons.toml
file in the project where you get the error?
Yeah, that's what I meant by "but then if I try to add a toml in my actual projects gradle folder then I get an error"
v
Yeah, well, you cannot have two sources for one version catalog
But you can have two version catalogs with different names
c
Is there a way to turn off the "automatic" from call? So that I could (hopefully) put
Copy code
versionCatalogs {
create("libs") { from(files("mysubmodule", "gradle/libs.versions.toml")) }
}
v
Wouldn't help, because wouldn't work
c
hm. what would you recommend?
v
If you want both in one version catalog, you have to use the programmatic API to define the version catalog, by parsing both files and combining them in any logic you want.
Otherwise, as I said, just declare a second version catalog with a different name
Actually what you had in your original post did that, but that is just a typo ๐Ÿ˜„
c
oh. I didn't understand thats what you meant.
v
There you declare one with name
lib
, so you would have
lib...
for the submodule catalog,
libs...
for your own
c
Actually what you had in your original post did that, but that is just a typo
damnit. i was trying to save typing (cant copy pasta from company laptop, lol)
v
But in reality you probably had
libs
there or you would not have get that error
t
you might also try a symlink? so both project just point to the same file
v
Copy code
versionCatalogs {
    create("submoduleLibs") { from(mysubmoduledir/the.toml) }
}
and you are fine
you might also try a symlink? so both project just point to the same file
It is unlikely that this will work, if it were the same content, he wouldn't need to have both, but only one would be fine
โž• 1
Also, symlinks are bad if there is any remote chance a Windows-user needs to work with the build ๐Ÿ™‚
c
so id prefer not to rename the submodule's toml file. So I guess I will rename the app level toml file as "app.libs.versions.toml"
v
so id prefer not to rename the submodule's toml file.
I did not say you should
๐Ÿ‘€ 1
You don't need to rename any files
The conventional defaults are, that you have a
libs
catalog that is feed from a
libs.versions.toml
file. But that both are called
libs
is no rule, that's just convention and consistency.
You can also name the version catalog
foo
and feed it from a file called
bar.toml
(the missing
versions.
is not a typo)
Just like a build script does not need to be called
build.gradle.kts
, but could also be called
foo.gradle.kts
for a project called
bar
if you configure it like that
It is not a good idea, but you can do it.
c
Intersting. So versionCatalogs { create("submoduleLibs") { from(mysubmoduledir/the.toml) } } and then I can just keep my
gradle/libs.versions.toml
v
Yes, as I said above ๐Ÿ™‚
c
Okay. so i would need to rename my toml in the submodule though right?
sorry if im just not understanding it. im trying a few renames on my end and it still won't compile. looks like if i rename my submodules toml, then i would need to update all references of libs.* in my submodule
v
Okay. so i would need to rename my toml in the submodule though right?
As said I think 3 times with different words: NO, you can name the file anything you like, including the current name
looks like if i rename my submodules toml, then i would need to update all references of libs.* in my submodule
I think we are at a point where you need to shown an MCVE. You probably omit the small little detail that makes me not giving the answer you need.
c
Alright, let me re-write the code from my company laptop. sorry for the confusion
๐Ÿ‘Œ 1
Currently I have
Copy code
gradle/
  libs.versions.toml
submodules/
  mylibrary1/
    gradle/
    libs.versions.toml
In my root project settings.gradle.kts I have
Copy code
versionCatalogs {
    create("libs") { from(files("submodules/mylibrary1/gradle/libs.versions.toml")) }
}
When I try to sync, I get a "You can only call the 'from' method a single time" If I update my root project settings.gradle.kts to
Copy code
versionCatalogs {
    create("submoduleLibs") { from(files("submodules/mylibrary1/gradle/libs.versions.toml")) }
}
then I get build errors in
submodules/mylibrary1/mysdk/build.gradle.kts
of "Unresolved reference to version catalog" when it tries to compile
alias(libs.plugins.android.library)
. If I change it to
alias(submoduleLibs.plugins.android.library)
then it works. But I'd prefer to not have to change all of the
libs
references in mylibrary1 to
submoduleLibs
so that's where it's currently not clicking for me of "NO, you can name the file anything you like, including the current name" because it seems like I still have to go and update all of my usages of
libs.*
in mylibrary1.
v
What
include
/
includeBuild
do you do in your root build settings script?
c
My root projects settings.gradle.kts at the bottom has
Copy code
include(":app")
include(":mysdk") <--- this is from the git submodule
project(":mysdk").projectDir = file("submodules/mylibrary1/mysdk") <--- this is from the git submodule
v
And this
mysdk
project, is this exclusively used in this root build you are including it to, or do you also include it in other builds?
c
This main project (our app) only has a single module (app) and app/build.gradle.kts has
implementation(project("mysdk"))
v
That was not the question
๐Ÿ˜… 1
c
apologies. i think maybe i dont have a great grasp on "including it to" means.
v
Is there somewhere on the planet even a single other settings script that does
include
the
mysdk
folder, not meaning the physical folder on disk, but also the same folder pulled into other repositories with submodules and similar things.
c
if i understand correctly, then yes Inside of
submodules/mylibrary1/settings.gradle.kts
there is a line of
include(":mysdk")
v
Now we are there, that is your root problem and a very very very bad idea One project should never be part of multiple builds, never ever ever ever. A given project should always only belong to exactly one build. If you need it in another build, then
includeBuild
the whole build that owns that project or you come into hell's kitchen quite easily.
๐Ÿคฏ 1
... as you just have seen
Change the
include(":mysdk")
to
includeBuild("submodules/mylibrary1")
and your
implementation(project("mysdk"))
to
implementation("group-of-sdk:mysdk")
and you should be fine and also no problems with version catalog, as
mysdkยด
uses the version catalog of its own build which is totally independent from your root build, and just happens to be built before as far as necessary to satisfy the dependency.
c
ah okay. so i (my teammate ๐Ÿ˜… ) completely misconfigured how we added our library via git submodules. it should be an includedBuild instead of this makeshift thing we had going.
and then everything stays isolated (exactly what i was looking for)!
Change the
include(":mysdk")
to
includeBuild("submodules/mylibrary1")
Done
implementation(project("mysdk"))
to
implementation("group-of-sdk:mysdk")
Wont build "String module notation 'com.mygroupname.sdk:mysdk' is invalid" FWIW, I'm using the com.mygroupname.sdk from the sdks modules publishing block (maven) where we declared a groupId of "com.mygroupname.sdk", but I'm guessing maybe I shouldn't be using the maven publishing blocks group id?
t
IIUC gradle is going to map the normal maven dependency notation to what the composite build exposes. so you need a version value there, but it won't really mean anything since the composite build will replace it
๐Ÿ‘€ 1
v
You should actually never mess with the group, name, and version in the publishing block. You should set the group on the project, the version on the project, and name the version after the artifact name that is published, so that the defaults for the publication work.
โž• 1
If you change group or name in the publishing block, for example a simple
includeBuild
will not work as you changed the identity and so you need to add manual substitution rules
But much cleaner would be if you not manipulate the publication
Regarding
Wont build "String module notation 'com.mygroupname.sdk:mysdk' is invalid"
can you show a build scan, or maybe the full error? Did you copy it or type it. If you typed it you might fixed the error on the fly. Because the notation should be fine.
c
interesting. first time writing a library so im still learning. personally i would love to consume the library via maven since we have that working, but it was decided to use git submodules so we can iterate faster. I will remove the group and name in the publishing block and see if that'll work.
v
IIUC gradle is going to map the normal maven dependency notation to what the composite build exposes. so you need a version value there, but it won't really mean anything since the composite build will replace it
No, you don't need a version, when it is served from an included build it would anyway ignore the version, so you also do not need to declare one.
๐Ÿ‘ 1
t
I want to say if you are still going to publish things out for consumption outside of the composite build you do need it so your pom/module files have the correct info in them?
๐Ÿ‘ 1
v
but it was decided to use git submodules so we can iterate faster.
Yeah, for developing lib and consumer together efficiently is exactly the original use-case for composite builds (
includeBuild
)
โค๏ธ 1
I will remove the group and name in the publishing block and see if that'll work.
It will probably not solve an invalid string notation
But once you fixed the invalid notation, it might fix the error you get then, that it is not found as you changed the coordinates in the publishing block ๐Ÿ˜„
I want to say if you are still going to publish things out for consumption outside of the composite build you do need it so your pom/module files have the correct info in them?
That might be correct indeed, yes.
Or maybe not, one needs to try.
If it is, then that would be bad actually
c
i removed the entire publishing block for testing purposes. I am now trying to get the right
implementation("")
declaration but still no dice. I have a feeling that I just don't know what the group id actually is. lol
v
Because you could declare version 1 in the build script, but get version 5 through the included build
And then the pom for external consumers would say you work with version 1 while you actually might need version 5
If that is really the case, I would recommend using a binary dependency from published component and just use composite build locally during developing the library further. That is how I usually do it.
c
Yeah let me try to mess around with this. reading the docs now so i can stop guessing interestingly https://docs.gradle.org/current/userguide/composite_builds.html does seem to include version numbers in the implementation statement
v
As I said, those are at least ignored during building, not sure about the published metadata. But as I said, the original use-case for composite builds is, that you have a binary dependency like
implementation("commons-io:commons-io:1.0.0)
and at the point where you want to develop a change for 1.0.1 in commons-io and test that right away in project X, you make X include the
commons-io
build until you are happy with your changes and then remove the
includeBuild
again. (
commons-io
per-se would of course not work as it is not a Gradle build, just serving as example)
One other important note is, that you have to keep the Gradle versions in sync. Regarding the Gradle version the isolation stops. The Gradle version of the including build is used for both builds, so the included build has to be compatible with that, so best is if both builds are written for the same Gradle version and stay in sync.
c
Cool okay. I'm going to consider this question "done" for now. Looks like root cause was not using included builds. let me hack around on this to see if i can learn a bit more. (if i have a follow up... i will likely just create a new question) On my todo list is: 1. learn what the group name is for my library 2. Remove the group name and group id from the publishing block (find docs to support this change) 3. make sure gradle versions in both the library and app are equal (find docs to support this) thanks all
๐Ÿ‘Œ 1
Got it working! Woot woot! thank you @Vampire and @TrevJonez before
Copy code
./gradlew projects

------------------------------------------------------------
Root project 'MyApp'
------------------------------------------------------------

Root project 'MyApp'
+--- Project ':app'
\--- Project ':mylibrary'
after
Copy code
------------------------------------------------------------
Root project 'MyApp'
------------------------------------------------------------

Root project 'MyApp'
\--- Project ':app'

Included builds:

\--- Included build ':mylibrary1'
The line I needed was (which of course seems so obvious now)
Copy code
implementation("mylibrary1:mylibrary")
๐Ÿ‘Œ 1
Point of clarification "A given project should always only belong to exactly one build." What does it mean for a project to "belong to a build"? does that mean it should only be in a single "include()" statement? or is there some other thing that makes a project belong to a build?
v
No, that's exactly it
โค๏ธ 1
c
One more question about "One other important note is, that you have to keep the Gradle versions in sync. The Gradle version of the including build is used for both builds, so the included build has to be compatible with that, so best is if both builds are written for the same Gradle version and stay in sync." I've been searching through the docs and can't find anything like that. Do you know if that is published somewhere that I can use for reference with my team?
v