The actual use of custom configurations in gradle - gradle

I am new to gradle and struggling with a basic problem. I have a set of compile time dependencies declared in my project. My problem statement is, I want to make a subset of dependencies non transitive and the remaining transitive.
I have tried to make a custom configuration which extends from compile and set its transitive property to false.
Customcompile.extendsFrom(compile)
Customcompile.transitive = false
By this, I assume that whatever I declare with
Customcompile 'xxx:xxx:1.0' will have transitive=false applied and that it will act as compile time dependency.
But this is not able to compile my project with these dependencies
Am I wrong anywhere in this assumption?

You need to change customCompile.extendsFrom(compile) to compile.extendsFrom(customCompile).
configurations {
customCompile
customCompile.transitive = false
compile.extendsFrom(customCompile)
}
This is because the compilation classpath is derived from the dependencies for the compile configuration.
By making compile configuration extend from customCompile configuration, you are now including all dependencies from customCompile configuration into compile configuration.

Related

How can I access the dependencies of an application from within the build file of a dependency embedded in the application?

I have a Gradle-based library that is imported as a dependency into consuming applications. In other words, an application that consumes my library will have a build.gradle file with a list of dependencies that includes both my library as well as any other dependencies they wish to import.
From within my library's build.gradle file, I need to write a Gradle task that can access the full set of dependencies declared by the consuming application. In theory, this should be pretty straightforward, but hours of searching has not yielded a working solution yet.
The closest I've come is to follow this example and define an additional task in the library's build.gradle file that runs after the library is built:
build {
doLast {
project.getConfigurations().getByName('runtime')
.resolvedConfiguration
.firstLevelModuleDependencies
.each { println(it.name) }
}
}
I keep getting an error message that the 'runtime' configuration (passed into getByName and referenced in the Gradle forum post I linked) cannot be found. I have tried other common Gradle configurations that I can think of, but I never get any dependencies back from this code.
So: what is the best way to access the full set of dependencies declared by a consuming application from within the build file of one of those dependencies?
Okay, I mostly figured it out. The code snippet is essentially correct, but the configuration I should have been accessing was 'compileClasspath' or 'runtimeClasspath', not 'runtime'. This page helped me understand the configuration I was looking for.
The final build task in the library looks roughly like this:
build {
doLast {
// ...
def deps = project.getConfigurations().getByName('compileClasspath')
.resolvedConfiguration
.firstLevelModuleDependencies
.each {
// it.name will give you the dependency in the standard Gradle format (e.g."org.springframework.boot:spring-boot:1.5.22.RELEASE")
}
}
}

How to make Kotlin `internal` objects accessible to tests?

My project uses several Gradle source sets for its production code base instead of just main:
domain
dal
rest
test
dbUnitTest
This has proven very useful for limiting dependencies and enforcing separation of concern.
It comes with one downside however: we cannot access classes or methods with visibility internal from within test classes. The reason for this is that the Kotlin compiler places every source set in its own "module":
$ find . -name '*.kotlin_module'
./classes/kotlin/domain/META-INF/contact-management_domain.kotlin_module
./classes/kotlin/dal/META-INF/contact-management_dal.kotlin_module
./classes/kotlin/rest/META-INF/contact-management_dal.kotlin_module
./classes/kotlin/test/META-INF/contact-management.kotlin_module
./classes/kotlin/dbUnitTest/META-INF/contact-management_dbUnitTest.kotlin_module
I would like all sourceset to use the same module name "contact-management", as the main sourceset would by default.
I tried to override the name with the compiler option -module-name:
tasks.withType<KotlinCompile> {
kotlinOptions {
// place all source sets in the same kotlin module, making objects with 'internal' visibility available to every source sets of this project
freeCompilerArgs += listOf("-module-name \"contact-management\")
}
}
Upon running gradlew build, I get
> Task :contact-management:compileDomainKotlin FAILED
e: Invalid argument: -module-name "contact-management"
The reason being that -module-name "contact-management_domain" is set before by the Gradle code invoking the Kotlin compiler as well, but apparently this option is only accepted once.
In a Gradle build, how can I control what is being considered "one module" by the Kotlin compiler?
A related question where the test source set is to be split has no satisfactory answers so far.
You can do that using kotlin compilations. (As far as I understand, a compilation is simply a block of files that are compiled together. A good explanation can be found here)
When you create a sourceset in gradle, the kotlin plugin creates a compilation under the hood (with the same name as the sourceset).
What you can do now with compilations is create associations. If a compilation A is associated with another compilation B, source code in A gets access to internal code units of B.
So in your case, if the test sourceset should get access to the dal sourceset you can simply associate the test compilation with the dal compilation:
kotlin.target.compilations.getByName("test").associateWith(kotlin.target.compilations.getByName("dal"))
PS: It also works the other way around. If you create compilations explicitly, the corresponding sourcesets are created under the hood. So for custom sourcesets you can create compilations and associate them:
val domainCompilation = kotlin.target.compilations.create("domain")
val dalCompilation = kotlin.target.compilations.create("dal") {
associateWith(domainCompilation)
}
In above example, the sourceset domain will have access to internal code units of the sourceset dal.

update gradle from 4.4 to 5.4 make joda-time dependancy issue

HI I've migrated a project to gradle version 5.4 from 4.4. Since then gradlew build returns error as below.
....ConvTable.java:6: error: package org.joda.time does not exist
import org.joda.time.DateTime;
...ConvetService.java:5: error: package org.joda.time does not exist
import org.joda.time.DateTime;
...ConvetService.java:34: error: cannot find symbol
ConvTableP getLastCononTableDate(String fromCurrency, String toCurrency, DateTime dateTimeZone) throws IOException;
symbol: class DateTime
location: interface ConvetService
Note: Some input files use or override a deprecated API.
Note: Recompile with -Xlint:deprecation for details
* What went wrong:
Execution failed for task ':cur-api:compileJava'
gradle file looks like below. and its a sub project of bigger one
apply plugin: "j-library"
apply plugin: "m-publish"
group = "com.t.cur"
sourceCompatibility = JavaVersion.VERSION_1_7
targetCompatibility = JavaVersion.VERSION_1_7
publishing { publications { mavenJava(MavenPublication) { } }
repositories {
maven { url "${mv_repo_url}" } }
}
dependencies {
implementation "com.t.com:x-core:1.0.0"
implementation "commons-lang:commons-lang:2.6"
}
My guess is that as part of the upgrade, you changed compile configurations with implementation. One of the differences with the new configuration is that the dependencies will not be exposed to consuming projects as part of the compilation classpath. The idea is that the dependencies you put into implementation are implementation specific and should not "leak" onto the consuming projects. This speeds up the build when using incremental compilation as dependent classes are only recompiled if the public API changes but not the internal implementation. There is also a case to be made for providing looser coupling between projects, though this is a bit more subjective. The implementation dependencies will still be part of, and resolved, in the runtimeClasspath configuration though.
So (assuming this is the underlying issue of cause), the dependency x-core used to provide Joda as a transitive dependency for compilation. But this is no longer the case.
There are two ways to fix it. If you use Joda as part of the public API of x-core, you need to declare it using the api configuration instead of implementation (and use the java-library plugin if you don't already). This will make Joda part of the compilation classpath of dependent projects.
On the other hand, if this sub-project just happens to use Joda as well, but in a completely unrelated way to x-core, you should declare it as dependency here as well (either as implementation or api using the same rules as before).

Depend on multiple configurations from the same dependency

I'm developing a gradle plugin that adds 3 configurations. one, two, and three
When I use this plugin I can add dependencies to these configurations and the plugin then used those dependencies in a certain way depending on the configuration. This works great in a single project, however if in a multi-project build I'm forced to add the following to get the dependencies from my dependent projects:
dependencies {
one project(path: ':projectA', configuration: 'one')
two project( path: ':projectA', configuration: 'two')
three project( path: ':projectA', configuration: 'three')
}
Is there a way to make is so that when I declare a dependency on my other project it automatically pulls in my three custom configurations: one, two, and three
In my plugin I create a task that uses the different configurations:
project.getTasks().create("my-task", MyTask.class, t -> {
t.getOnes().setFrom(
project.getConfigurations().getByName("one"));
t.getTwos().setFrom(
project.getConfigurations().getByName("two"));
t.getThrees().setFrom(
project.getConfigurations().getByName("three"));
});
Maybe the method DomainObjectCollection.whenObjectAdded can help you.
Find the docs here:
https://docs.gradle.org/current/javadoc/org/gradle/api/DomainObjectCollection.html
You could try to get a DependencySet from all configurations of your project, which is a DomainObjectCollection, and call whenObjectAdded on it, and then check if what is being added is your "projectA", and then do your thing.
Alternatively, you could use project.afterEvaluate and then search through all the dependencies at that point in time.
The same problem in both cases is to get a collection of all the dependencies, and inspect them.
Something like this could work:
configurations.all().each {
if (it.allDependencies.contains(project(path: ':projectA'))) {
// do something
}
}

How to tell tasks and other code blocks apart in Gradle?

I have a build.gradle file cobbled together from examples online:
apply plugin: "java"
sourceSets {
java {
srcDirs = ['src']
}
}
repositories {
flatDir {
name "fileRepo"
dirs "repo"
}
}
uploadArchives {
repositories {
add project.repositories.fileRepo
}
}
When I run gradle tasks --all, I can see that "uploadArchives" is a task. How can I tell what is a task by looking at the build.gradle file? If "repositories" and "sourceSets" aren't considered tasks, what are they?
You simply can't.
But, the pure knowledge whether a closure configures a task or something else, won't give you anything. To understand a build script, you will need to understand the basic concept of Gradle and the used plugins, either built-in or third-party.
Each build.gradle script is executed against a Project instance. Everything you can access from the build script belongs to one of the following scopes:
The Project object itself. This scope includes any property getters and setters declared by the Project implementation class. For example, getRootProject() is accessible as the rootProject property. The properties of this scope are readable or writable depending on the presence of the corresponding getter or setter method.
The extra properties of the project. Each project maintains a map of extra properties, which can contain any arbitrary name -> value pair. Once defined, the properties of this scope are readable and writable. See extra properties for more details.
The extensions added to the project by the plugins. Each extension is available as a read-only property with the same name as the extension.
The convention properties added to the project by the plugins. A plugin can add properties and methods to a project through the project's Convention object. The properties of this scope may be readable or writable, depending on the convention objects.
The tasks of the project. A task is accessible by using its name as a property name. The properties of this scope are read-only. For example, a task called compile is accessible as the compile property.
The extra properties and convention properties are inherited from the project's parent, recursively up to the root project. The properties of this scope are read-only.
For your specific example, uploadArchives is a task, repositories belongs to the original Project object (it is available in each build script) and sourceSets is an extension of the java plugin.
Please note, that many plugins do not require or plan direct task configuration. They provide a DSL extension for configuration and then generate the tasks based on this configuration.

Resources