How can I reuse library declarations in Gradle Kotlin DSL? - kotlin

Background:
We are trying to migrate to Gradle's Kotlin DSL, primarily for the additional type safety.
Some of our library definitions are quite large (20+ exclude calls in some extreme cases) so we currently have a giant dependencies.gradle Groovy script which declares a giant map of all libraries, which can then be used like libraries.blah. I'm trying to come up with a working way to do the same in Kotlin.
Investigation:
Dependency constraints (java-platform plugin) allow centralising the version number for the dependency, but that does nothing for the exclusions, and the exclusions are most of the problem in our case. (I'm sure we will eventually use java-platform at some point too.)
The most elegant-looking solution I found was in this one blog post which suggests declaring Kotlin objects, like this:
object Libraries {
val guava = "..."
}
You can then supposedly use the object like Libraries.guava. But this doesn't actually work, at least in Gradle 6.9.1, because of an issue in Kotlin itself. (I'm assuming that it worked on some older version and got broken by a Kotlin compiler upgrade.)
The workaround specified on the ticket comes out like:
class Libraries(dependencies: DependencyHandler) {
val guava = dependencies.create(...)
}
val libraries by project.extra { Libraries(dependencies) }
This gets me closer to a working solution in that my dependencies.gradle.kts file now compiles, but I still can't find a way to use the Libraries class I've defined. If you do this:
val libraries: Libraries by project.extra
Kotlin complains that Libraries is not defined. So it's as if one build script can't even export classes for another build script to use.
More Context
The actual project structure is quite complex, and looks a bit like this:
─ repo root
├─ mainproject1
│ ├─ subproject11
│ │ └─ build.gradle.kts
│ ├─ subproject12
│ │ └─ build.gradle.kts
│ ├─ build.gradle.kts (references dependencies.gradle.kts)
│ └─ settings.gradle.kts
├─ mainproject2
│ ├─ subproject21
│ │ └─ build.gradle.kts
│ ├─ subproject22
│ │ └─ build.gradle.kts
│ ├─ build.gradle.kts (references dependencies.gradle.kts)
│ └─ settings.gradle.kts
└─ shared
└─ gradle
└─ dependencies.gradle.kts
Question:
How are other people with enormous builds with >100 dependencies solving this problem?

Workaround: use buildSrc to instantiate Libraries object.
Create the following directories structure:
.
├── buildSrc
│ ├── build.gradle.kts
│ └── src
│ └── main
│ └── kotlin
│ └── Libraries.kt
├── settings.gradle.kts
└── build.gradle.kts
buildSrc/build.gradle.kts is minimalistic:
plugins {
`kotlin-dsl`
}
repositories {
mavenCentral()
}
Libraries.kt:
object Libraries {
const val guava = "com.google.guava:guava:31.0.1-jre"
}
Usage (root build.gradle.kts):
plugins {
`java-library`
}
dependencies {
implementation(Libraries.guava)
}
repositories {
mavenCentral()
}

Related

Gradle setup for Kotlin multi-module project structure

I have a gradle project setup which looks similar to the one graphed below and I have some trouble setting up the dependencies between the different modules
┌Top Kotlin Project
│
├── Project1
│ │
│ ├── ModuleA1 (uses C1)
│ ├── ModuleA2
│ ├── ModuleA3
│ └── settings.gradle
├── ModuleB1 (uses C1)
├── ModuleC1 (library project)
└── settings.gradle
The Modules in A1,A2,A3 and B1 are basically isolated Gradle applications with their own build.gradle.kts
Module C1 is supposed to be a library module with shared code, that is used by Module A1, and Module B1.
Originally I thought I only had to declare C1 as an implementation dependency in the build.gradle.kts files of A1 and B1, like this
implementation(project(":ModuleC1"))
and include it in the settings.gradle of the Top Kotlin Project and also Project 1
include(":ModuleC1")
project(":ModuleC1").projectDir = File("../modulec1") #this line only needed in settings.gradle of Project1
However this does not work for multiple reasons.
First of all, all other projects/modules need to include all the repositories ModuleC needs, which means, whenever ModuleC changes I might need to also change the build files of other modules.
Most importantly though, all modules need the kotlin gradle plugin to build.
However if ModuleC1 includes the plugin and ModuleA1 as well, I get error that
> Plugin request for plugin already on the classpath must not include a version
If I do not include the Plugin in ModuleC1 it works for ModuleA1, but ModuleC1 cannot be built alone anymore.
The gradle.build.kts of ModuleC1 currently looks like this:
plugins {
kotlin("jvm") version "1.6.20"
`java-library`
}
group = "org.test.project"
version = "0.1"
repositories {
mavenCentral()
}
dependencies {
testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.1")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.1")
}
tasks.getByName<Test>("test") {
useJUnitPlatform()
}
In conclusion I think, this setup of simply including the ModuleC1 in the other projects/modules is pretty flawed and there must be a better way to do this.
In summary, my goals are to be able to develop ModuleC1 independently but be able to use it in the other modules as if it was just another package there.

Summary HTML Report not generated when path is specified in terms of classpath in Karate

I am trying to run some test cases using Karate v1.2.0 (with Junit5). Everything is working well, except for a minor discrepancy that seems to be occurring during report generation. The project folder structure is something like this
my-project/
├── src/
│ └── test/
│ └── java/
│ ├── ronan/
│ │ ├── api/
│ │ │ ├── ApiTest.java
│ │ │ ├── feature-1.feature
│ │ │ └── feature-2.feature
│ │ └── util/
│ │ └── SomeUtil.java
│ ├── karate-config.js
│ └── logback-test.xml
└── build.gradle
When my ApiTest.java specifies the path by individual feature file names, the individual feature HTML reports and the summary HTML report are generated.
#Karate.Test
Karate testAPI() {
// Generates individual HTML reports as well as the summary HTML report
return Karate.run("feature-1", "feature-2")
.outputJunitXml(true)
.relativeTo(getClass());
}
However, when I specify the path using classpath:, only the individual feature HTML reports are generated and not the summary report.
#Karate.Test
Karate testAPI() {
// Generates ONLY individual reports
return Karate.run("classpath:ronan/api")
.outputJunitXml(true)
.relativeTo(getClass());
}
The test task is configured in build.gradle as below
test {
useJUnitPlatform()
systemProperty "karate.options", System.properties.getProperty("karate.options")
systemProperty "karate.env", System.properties.getProperty("karate.env")
outputs.upToDateWhen { false }
}
I wasn't able to find any relevant documentation or SO answers related to the same, so I'm not sure if this is expected and if I'm doing something wrong or if it's an issue with report generation.
Any help on this would be greatly appreciated. Thanks!
For CI, please use the Runner API, and only then can you run tests in parallel. The JUnit helpers are just for IDE dev-loop convenience.
Please read this for details: https://stackoverflow.com/a/65578167/143475
Now if you still see issues with the Runner, then open an issue with a way to replicate. Meanwhile you are welcome to contribute code to fix the problem you describe in your question.

Fortran fpm Run Tests on Module in Parent Directory

I have written a module in fortran that I want to test using fortran-fpm and vegetables.
My directory structure looks like this:
my_repo/
├─ foo/
│ ├─ bar/
│ │ ├─ my_module.f90
│ │ ├─ test/
│ │ │ ├─ main.f90
│ │ │ ├─ my_module_test.f90
├─ fpm.toml
When I run fpm test, I get this error:
<ERROR>*cmd_run*:targets error:Unable to find source for module dependency: "my_module" used by "foo/bar/test/my_module_test.f90"
STOP 1
If I instead move my_module.f90 into the test directory, all the tests run fine. How do I point my_module_test.f90 to my_module.f90 without having my source code in the test directory?
I have tried:
Including:
[test-dependencies]
my_module = { path = "foo/bar/my_module.f90" }
in my fpm.toml file at the top of my repo as suggested in the documentation. It then prompts me to put another fpm.toml file at foo/bar/. When I do that, it still gives me the same error.
Using putting file: "foo/bar/my_module.f90 at the top of my_module_test.f90. As suggested here.
EDIT:
Note that my fpm.toml file looks like this:
name = "My_Project"
author = "NolantheNerd"
[install]
library = true
[library]
source-dir = "foo/bar"
include-dir = "foo/bar"
[build]
external-modules = "foo/bar"
link = "foo/bar/my_module.f90"
[dev-dependencies]
vegetables = { git = "https://gitlab.com/everythingfunctional/vegetables.git", tag = "v7.2.2" }
[[test]]
name = "TestsForMyModule"
source-dir = "foo/bar/test"
main = "main.f90"
link = "foo/bar/my_module.f90"
Many thanks to everythingfunctional, the creator of vegetables for his solution:
Your build section is incorrect, and actually unnecessary in your case. Try removing it and let us know how it goes.
Edit: just noticed as well that the link entry in your test section is incorrect and unnecessary as well.
See the thread here.

Conditional loading of Dojo modules

Let me first present directory structure of what I have
/
├── dojo/
├── dojox/
├── dijit/
└── app/
├── a/
│ └── moduleA.js
├── b/
│ └── moduleA.js
└── c/
I'm trying to configure Dojo to be able to load moduleA.js in following way:
require(["app/moduleA"]
and in the same time resolve it according to below pseudocode:
if moduleA exists in c
load "app/c/moduleA.js"
else if moduleA exists in b
load "app/b/moduleA.js"
else if moduleA exists in a
load "app/a/moduleA.js"
else
standard fail, same as when no module is defined
It would be great if order a, b and c could be passed as array for instance ["app/c/moduleA", "app/b/moduleA", "app/a/moduleA"].
I was looking into documentation hoping to find something, but no luck. https://dojotoolkit.org/reference-guide/1.7/loader/amd.html#module-identifiers was closest I got. There is packages.location property, but it takes string to load module, so I guess it has no additional logic regards to arrays.
Any idea how to solve this problem?

Can I attach a storyboard to a Static Library

I have a very simple single view application that I need to convert into a library.
I believe I am off to the correct start of going to my project settings and then adding a Cocoa Touch Static Library. After doing that I moved my ViewController.h, ViewController.m and Main.storyboard files to the new library directory.
I then went to my applications project settings and added my new library to the "Link Binary With Libraries". Then under the general tab I set the Main interface to nothing since I had moved the storyboard to the new library directory.
Then I went to my libraries project settings and add the Main.storyboard to the Copy Files. Lastly I deleted the two default .h and .m files that were created when I added the library and then added the ViewController.m file to the libraries Compile sources.
The app loads a blank screen and gives warnings because it can't access any of the inputs on the storyboard. How do I get library to load the storyboard or is this just completely wrong?
You need to make sure that the application (not the library) is copying the storyboard.
You should probably build a resource bundle for your resources (a zipped file that ends with .bundle).
There is a hack you could try if you really want to get down to one file, Embedded Frameworks.
Although frameworks are an improvement over libraries, Xcode ignores any resources contained within frameworks. So if you have xibs, images, sounds, or other files in your framework, Xcode won't see them. An embedded framework is a way to trick Xcode into seeing the included resources.
You can see the structure here.
KFData.embeddedframework
├── KFData.framework
│ ├── Headers -> Versions/Current/Headers
│ ├── KFData -> Versions/Current/KFData
│ ├── Resources -> Versions/Current/Resources
│ └── Versions
│ ├── A
│ │ ├── Headers
│ │ │ ├── ..
│ │ ├── KFData
│ │ └── Resources
│ │ ├── ACKNOWLEDGEMENTS
│ │ ├── Info.plist
│ │ ├── LICENSE
│ │ ├── KFData.bundle
│ │ │ ├── ..
│ │ ├── KFData.xcconfig
│ │ └── VERSION
│ └── Current -> A
└── Resources
├── KFData-Acknowledgements -> ../KFData.framework/Resources/ACKNOWLEDGEMENTS
├── KFData-License -> ../KFData.framework/Resources/LICENSE
├── KFData-Version -> ../KFData.framework/Resources/VERSION
├── KFData.bundle -> ../KFData.framework/Resources/KFData.bundle
└── KFData.xcconfig -> ../KFData.framework/Resources/KFData.xcconfig