How can I import one Gradle script into another? - java

I have a complex Gradle script that wraps up a load of functionality around building and deploying a number of NetBeans projects to a number of environments.
The script works very well, but in essence it is all configured through half a dozen maps holding project and environment information.
I want to abstract the tasks away into another file, so that I can simply define my maps in a simple build file, and import the tasks from the other file. In this way, I can use the same core tasks for a number of projects and configure those projects with a simple set of maps.
Can anyone tell me how I can import one Gradle file into another, in a similar manner to Ant's task? I've trawled Gradle's docs to no avail so far.
Additional Info
After Tom's response below, I thought I'd try and clarify exactly what I mean.
Basically I have a Gradle script which runs a number of subprojects. However, the subprojects are all NetBeans projects, and come with their own ant build scripts, so I have tasks in Gradle to call each of these.
My problem is that I have some configuration at the top of the file, such as:
projects = [
[name:"MySubproject1", shortname: "sub1", env:"mainEnv", cvs_module="mod1"],
[name:"MySubproject2", shortname: "sub2", env:"altEnv", cvs_module="mod2"]
]
I then generate tasks such as:
projects.each({
task "checkout_$it.shortname" << {
// Code to for example check module out from cvs using config from 'it'.
}
})
I have many of these sort of task generation snippets, and all of them are generic - they entirely depend on the config in the projects list.
So what I want is a way to put this in a separate script and import it in the following sort of way:
projects = [
[name:"MySubproject1", shortname: "sub1", env:"mainEnv", cvs_module="mod1"],
[name:"MySubproject2", shortname: "sub2", env:"altEnv", cvs_module="mod2"]
]
import("tasks.gradle") // This will import and run the script so that all tasks are generated for the projects given above.
So, in this example, tasks.gradle will have all the generic task generation code in, and will get run for the projects defined in the main build.gradle file. In this way, tasks.gradle is a file that can be used by all large projects that consist of a number of sub-projects with NetBeans ant build files.

There is a new feature in 0.9. You can use apply from: 'other.gradle' command.
Read my question about same thing at: Is there a way to split/factor out common parts of Gradle build

The answer to the question turned out to be in the Plugins system, where you can add the desired functionality in a set of plugins which can be groovy files located in the directory buildSrc/src/main/groovy. Plugins can also be bundled as a Jar though I haven't tried this.
Details here: Custom Plugins

Well, it is hard to tell what serves you best without actually seeing your build file.
I could assume that stetting up your environment as multi-project build should provide you the abstraction you are looking for.
In your project root build.gradle you define all your domain specific stuff as well as the things that apply to all your subprojects:
repositories {
add(new org.apache.ivy.plugins.resolver.FileSystemResolver()) {
name = 'destRepo'
addIvyPattern( file( project.properties['repo.dest.dir']).absolutePath + '/[organisation]/[module]/ivys/ivy(-[revision]).xml')
addArtifactPattern( file( project.properties['repo.dest.dir']).absolutePath + '/[organisation]/[module]/[type]s/[artifact](-[revision]).[ext]')
descriptor = 'optional'
checkmodified = true
}
...
}
...
subprojects {
sourceCompatibility = 1.5
targetCompatibility = 1.5
group = 'my.group'
version = '1.0'
uploadArchives {
uploadDescriptor = true
repositories {
add rootProject.repositories.destRepo
}
}
apply{ type my.group.gradle.api.plugins.MyPlugin }
...
}
dependsOnChildren()
The project root directory might also contain a gradle.properties file where you define properties used by your projects:
buildDirName=staging
repo.dest.dir=/var/repo
...
Then in an additional file from your project root named settings.gradle you actually point to your subprojects:
include 'my-first-component',
'my-second-component'
...
project(':my-first-component').projectDir = new File(rootDir, 'path/to/first/component')
project(':my-second-component').projectDir = new File(rootDir, 'path/to/second/component')
...
Each sub-project directory contains a build.gradle file containing the sub-project specific stuff only.
No matter if you invoke gradle from your project root or sub-project directory, gradle will automatically consider all your definitions done in the various files.
Also note that no compile task will be executed for your project root as long as you don't load any plugin beyond the default plugin at the root level.

This is an example for Kotlin DSL (build.gradle.kts).
apply(from = "scripts/my-script.gradle.kts")
scripts/my-script.gradle.kts:
println(
"""
I am defined at the top level of the script and
executed at the configuration phase of build process
"""
)
tasks.create("MyTask") {
println(
"""
I am defined in a task and
run at the configration phase of build process"""
)
doLast {
// ...
}
}
See this answer and this answer for how to import a function from another script in Kotlin DSL.

Based off this similar question/answer, the easiest solution I've found after searching for days is using buildscript.sourceFile. It correctly gives the file being run rather than the pwd/cwd/parent-file of said process. I feel like this would solve your issue.

Related

How to shared gradle tasks across many projects in different git repositories

We have lots of libraries for which we have the following snippets to configure the respective plugins. We would want to avoid code duplication and rather want to pull these definitions from a base repository which can be shared across all projects. How should this be configured?
checkstyle {
showViolations = false
ignoreFailures = true
..
}
pmd {
..
}
license {
..
}
spotless {
..
}
artifactory {
..
}
With an easy approach you can import build script plugins into other build scripts even if they are located in a different location / different repository. The only requirement that I think is necessary that on the machine where you will build the project at the end you have access to those repositories.
Imagine you have GitHub repository that contains main configuration.
https://github.com/user/shared-gradle-config/blob/master/scriptPlugin.gradle
That contains:
checkstyle {
showViolations = false
ignoreFailures = true
..
}
pmd {
..
}
...
Now be sure that you use raw resources in other scripts. Every repository service will have different kind of link. Below example for GitHub:
https://raw.githubusercontent.com/user/shared-gradle-config/master/scriptPlugin.gradle
At the beginning of the Gradle build script for the actual projects you have to include application of the remote script plugin:
// Application of shared remote Gradle script plguin with common configuration
// With this line all the checkStyle, PMD configuration and whatever you have declared in the script will be applied to current project
apply from: "https://raw.githubusercontent.com/user/shared-gradle-config/master/scriptPlugin.gradle"
// Whatever custom logic below
wrapper {
version = "7.0.0"
distributionType = Wrapper.DistributionType.ALL
}
...
I made a prototype project miself as I tried to understand how to share build configuration this way. Maybe it can help you, https://github.com/rivancic/gradle/tree/master/script-plugin. Note its not in final version, still improving it..

Gradle + Eclipse : use class from existing project in a new project

I know there are a lot of questions that seem similar. I have also spent a few hours getting to grips with Gradle multiprojects. But I still don't understand what the best course of action is here. Incidentally I am using Groovy as my coding language, but explanations referencing Java would be just as good.
I have developed an Eclipse Gradle project, "ProjectA", which in particular has a class, IndexManager, which is responsible for creating and opening and querying Lucene indices.
Now I am developing a new Eclipse Gradle project, "ProjectB", which would like to use the IndexManager class from ProjectA.
This doesn't really mean that I would like both projects to be part of a multiproject. I don't want to compile the latest version of ProjectA each time I compile ProjectB - instead I would like ProjectB to be dependent on a specific version of ProjectA's IndexManager. With the option of upgrading to a new version at some future point. I.e. much as with the sorts of dependencies you get from Maven or JCenter...
Both projects have the application plugin, so ProjectA produces an executable .jar file whose name incorporates the version. But currently this contains only the .class files, the resource files, and a file called MANIFEST.MF containing the line "Manifest-Version: 1.0". Obviously it doesn't contain any of the dependencies (e.g. Lucene jar files) needed by the .class files.
The application plugin also lets you produce a runnable distribution: this consists of an executable file (2 in fact, one for *nix/Cygwin, one for Windows), but also all the .jar dependencies needed to run it.
Could someone explain how I might accomplish the task of packaging up this class, IndexManager (or alternatively all the classes in ProjectA possibly), and then including it in my dependencies clause of ProjectB's build.gradle... and then using it in a given file (Groovy or Java) of ProjectB?
Or point to some tutorial about the best course of action?
One possible answer to this which I seem to have found, but find a bit unsatisfactory, appears to be to take the class which is to be used by multiple projects, here IndexManager, and put it in a Gradle project which is specifically designed to be a Groovy library. To this end, you can kick it off by creating the project directory and then:
$ gradle init --type groovy-library
... possible to do from the Cygwin prompt, but not from within Eclipse as far as I know. So you then have to import it into Eclipse. build.gradle in this library project then has to include the dependencies needed by IndexManager, in this case:
compile 'org.apache.lucene:lucene-analyzers-common:6.+'
compile 'org.apache.lucene:lucene-queryparser:6.+'
compile 'org.apache.lucene:lucene-highlighter:6.+'
compile 'commons-io:commons-io:2.6'
compile 'org.apache.poi:poi-ooxml:4.0.0'
compile 'ch.qos.logback:logback-classic:1.2.1'
After this, I ran gradle jar to create the .jar which contains this IndexManager class, initially without any fancy stuff in the manifest (e.g. name, version). And I put this .jar file in a dedicated local directory.
Then I created another Gradle project to use this .jar file, the critical dependency here being
compile files('D:/My Documents/software projects/misc/localJars/XGradleLibExp.jar' )
The file to use this class looks like this:
package core
import XGradleLibExp.IndexManager
class Test {
public static void main( args ) {
println "hello xxx"
Printer printer = new Printer()
IndexManager im = new IndexManager( printer )
def result = im.makeIndexFromDbaseTable()
println "call result $result"
}
}
class Printer {
def outPS = new PrintStream(System.out, true, 'UTF-8' )
}
... I had designed IndexManager to use an auxiliary class, which had a property outPS. Groovy duck-typing means you just have to supply anything with such a property and hopefully things work.
The above arrangement didn't run: although you can do build and installdist without errors, the attempt to execute the distributed executable fails because the above 6 compile dependency lines are not present in build.gradle of the "consumer" project. When you put them in this "consumer" Gradle project's build.gradle, it works.
No doubt you can add the version to the generated .jar file, and thus keep older versions for use with "consumer" projects. What I don't understand is how you might harness the mechanism which makes the downloading and use of the dependencies needed by the .jar as automatic as we are used to for things obtained from "real repositories".
PS in the course of my struggles today I seem to have found that Gradle's "maven-publish" plugin is not compatible with Gradle 5.+ (which I'm using). This may or may not be relevant: some people have talked of using a "local Maven repository". I have no idea whether this is the answer to my problem... Await input from an über-Gradle-geek... :)
You should be able to update the Eclipse model to reflect this project-to-project dependency. It looks something like this (in ProjectB's build.gradle):
apply plugin: 'eclipse'
eclipse {
classpath.file.whenMerged {
entries << new org.gradle.plugins.ide.eclipse.model.ProjectDependency('/ProjectA')
}
project.file.whenMerged {
// add a project reference, which should show up in /ProjectB/.project's <projects> element
}
}
These changes may be to the running data model, so they may not actually alter the .classpath and .project files. More info can be found here: https://docs.gradle.org/current/dsl/org.gradle.plugins.ide.eclipse.model.EclipseModel.html
This issue is discussed here: http://gradle.1045684.n5.nabble.com/Gradle-s-Eclipse-DSL-and-resolving-dependencies-to-workspace-projects-td4856525.html and a bug was opened but never resolved here: https://issues.gradle.org/browse/GRADLE-1014

Gradle - one project, multiple packages, multiple jars

I'm asking you about a very basic question but I hope you can find the time to help me:
I'm trying to realise a java-project, that can spit out several different programs which partially have dependencies on other projects of mine.
In order to keep it simple, I want to have all the code in one project, run by Gradle, so if I make changes to a central library (the database connector for example) all the child-programs automatically recieve the changes.
An example could look like this:
project:
program_A
central_library
program_B
output:
program_A.jar (including central library)
program_B.jar (including central library)
Now I'm having serious troubles finding a correct buildscript for this and was wondering if someone here could help me out.
P.S. : Since I'm new to this, if I should realize this through different modules within the Gradleproject instead of different packages in the Gradleprojects sourcefile, feel free to tell me :)
One way to approach this is to have a root project, that holds the three other projects inside of it.
Specify the sub-projects inside its settings.gradle file:
rootProject.name = 'RootProject'
include 'program_A'
include 'central_library'
include 'program_B'
With this in place, program_a can depend on central_library by adding a dependency in its build.gradle:
dependencies {
compile project(':central_library')
}
I have a similar setup in one of my projects, although the "central library" is the root project and the submodules are test environments.
Create a root directory and put each library or program into its own sub-directory.
Create a gradle project in each subproject.
You can for example create a skeleton gradle project by running
gradle init --type=java-library
or
gradle init --type=java-application
Then in the root directory create a gradle multi-module project. Basically
run only
gradle init
and then create a settings.gradle and list all sub-projects there.
This is actually described very well in the gradle documentation:
https://guides.gradle.org/creating-multi-project-builds/
If I understand correctly, what you want to do is, when you change your local projects, you want other projects to see those details. For this you need to publish your projects to some kind of repo, like maven repo. You can do this from command line gradle publishToMavenLocal, or gradle build pTMl. You can also do this in build.gradle file with something like the following:
task sourceJar (type : Jar) {
classifier = constants.extSources
from sourceSets.main.allSource
}
publications {
mavenJava(MavenPublication) {
from components.java
artifact(sourceJar) {
classifier "sources" //classifier = constants.extSources
}
}
}

Basic gradle Q for multiple projects

This is a basic gradle question but I could not find an answer:
If I have multiple independent projects that share the same root directory, how should I use different build.gradle file for them?
for example the directory structure is like this:
src/main/java/package1/base1/project1package/
src/main/java/package1/base1/project2package/
It looks like the build.gradle file should be placed at root, but how to differ two projects which are totally independent and not related to each other? When I use gradle build command, how can I specify one project to be built? Thanks.
To be honest I don't like the structure you are proposing. The standard one looks like this:
<root>/build.gradle
<root>/<subproject1>/build.gradle
<root>/<subproject1>/src/main/java/<basepackage>/<subproject1>/
<root>/<subproject2>/build.gradle
<root>/<subproject2>/src/main/java/<basepackage>/<subproject2>/
The subproject1 and subproject2 are independent of each other. Moreover:
<root>/build.gradle can contain a build configuration, tasks and so on common to all sub-projects. subproject1-specific stuff is then in <root>/<subproject1>/build.gradle
If you execute gradle build in <root>/, all sub-projects will be built. If you do this in <root>/<subproject1>/, only the subproject1 will be built.
For more, see http://www.gradle.org/docs/current/userguide/multi_project_builds.html
Let me know if I am off base, but essentially what I think you want to do is simply produce two separate JARs from two sets of source that happen to live in the same root directory. This is a pretty funny way to structure your project, as #Tomas mentioned, but assuming you cannot change your project structure, you could simply create custom source sets to solve this problem.
Your build could look something like this.
apply plugin: 'java-base'
sourceSets {
projA {
java {
srcDir 'src/main/java'
include 'pkg1/**'
}
}
projB {
java {
srcDir 'src/main/java'
include 'pkg2/**'
}
}
}
task projAJar(type: Jar) {
baseName 'projA'
from sourceSets.projA.output
}
task projBJar(type: Jar) {
baseName 'projB'
from sourceSets.projB.output
}
Although this is technically one Gradle project, these two source sets each have their own compile tasks, jar tasks, and configurations. You would configure your dependencies like so.
dependencies {
projACompile 'foo.org:lib:1.0'
projBCompile 'bar.org:lib:2.0'
}

How do you create additional jar artifacts from compiled classes using Gradle?

I can't tell if this is a bug with Gradle 1.0m7, or if we are just doing this wrong.
We have some classes that get compiled as apart of a project, that we want to individually jar into it's own artifact. These are for example standalone domain model objects, that we want to share with another project.
I'd prefer not to go the multi-project build route, so how do we tell Gradle to create another jar for these?
Currently we are doing this:
task modelJar(type: Jar) {
classifier = 'model'
from fileTree(dir: sourceSets.main.classesDir).matching { include 'com/foo/bar/model/**' }
}
artifacts {
archives modeljar
}
The issue here, is the modeljar task runs before the classes are compiled. At first we didn't realise this and thought this was working. Turns out, the artifact was picking up classes from the previous run, not the current run. Doing clean before the build results in a jar with no classes in it, and reveals the problem.
I was looking at custom configuration, but it seems pretty complex and I didn't want to overly complicate the build file.
Appreciate any advice.
Thanks.
the most convenient way to do this is
task modelJar(type: Jar) {
classifier = 'model'
from sourceSets.main.output
include 'com/foo/bar/model/**'
}
Some background:
sourceSets.main.output is a buildable filecollection. This means that if a task works with this file collection, gradle knows that this file collection must be created before another task can use it. in this particular case, sourcesets.main.classes is wired to the classes task of the java plugin. Therefore you your modelJar task does not need to depend on the classes task explicitly.
How about making modelJar task depend on classes (built-in) task? This should make sure compilation is done before modelJar task.
task modelJar(dependsOn: classes, type: Jar){
...

Categories