How to allow / workaround nested composite Gradle builds - eclipse

I'm running into the same issue as reported here:
I have a Java project A which depends on project B (module), and
project B dependes on project C (another module). For project A I
would like to setup "includeBuild ../projectB" and for project B I
would like too setup "includeBuild ../projectC" so that I could
develop everything in Eclipse + Buildship 2.0 without the need to run
Gradle for every small change in each of the projecta A, B and C.
But if I setup this I get: "Included build '%s' cannot have included
builds.".
Expected Behavior
Recursive "includeBuild" would recursively include dependent projects.
Current Behavior
I get "Included build '%s' cannot have included builds.".
Your Environment
Gradle 3.5, Buildship 2.0, Eclipse 3.6
How can I resolve / work around this issue? In my instance, I have utility project that includes email functionality (using JavaMail). The email functionality is needed in the data project and a UI project. The UI project also depends on the data project.

Adding another answer for a different approach...
Each settings.gradle could add a check to before includeBuild to see if it's already inside a composite. If it's already inside a composite, it doesn't includeBuild.
See here for extra info on the composite check
Eg
project2/settings.gradle
boolean inComposite = gradle.parent != null
if (!inComposite) {
includeBuild '../project1'
}
project3/settings.gradle
boolean inComposite = gradle.parent != null
if (!inComposite) {
includeBuild '../project1'
includeBuild '../project2'
}
project4/settings.gradle
boolean inComposite = gradle.parent != null
if (!inComposite) {
includeBuild '../project1'
includeBuild '../project2'
includeBuild '../project3'
}
etc etc
Using this approach you could run gradle from wherever you like and it should behave as expected (ie substitute dependencies with local projects)

Have you considered
Ensuring that none of the individual builds are composite builds
Having an "uber" build which is a composite of everything
Note that the settings.gradle is itself a groovy script, so you could create a dynamic composite, eg all sub-folders with a build.gradle under a parent.
uber/settings.gradle
new File("c:/someFolder").listFiles().each { File f ->
if (f.directory && new File(f, 'build.gradle').exists()) {
includeBuild f
}
}
Eg
c:/someFolder/project1/build.gradle
c:/someFolder/project1/src/main/java/**/*.java
c:/someFolder/project2/build.gradle
c:/someFolder/project2/src/main/java/**/*.java
c:/uber/settings.gradle (as above)

Related

Gradle DSL - Eclipse Equivalent for IDEA Module Property

Good localtime,
I am in the process of updating legacy (4.8.1) Gradle build files for a big-McLarge-huge, multimodule project. We utilize an intellij.gradle file which has the following line (marked by comment):
idea {
module {
inheritOutputDirs = true // <-- HOW DO I DO THIS
downloadJavadoc = true
downloadSources = true
}
workspace.iws.withXml { provider ->
def node = provider.asNode()
def dynamicClasspath = node.component.find { it."#name" == "dynamic.classpath" }
if (dynamicClasspath != null) {
dynamicClasspath."#value" = "true"
}
}
From the 4.8.1 DSL docs:
If true, output directories for this module will be located below the
output directory for the project; otherwise, they will be set to the
directories specified by IdeaModule.getOutputDir() and
IdeaModule.getTestOutputDir().
Any ideas on what the Eclipse DSL equivalent of inheritOutputDirs? Should this be handled using the eclipseClasspath API? Right now everything is building fine, but the Eclipse Java builder is is flagging things.
References:
https://docs.gradle.org/4.8.1/dsl/org.gradle.plugins.ide.idea.model.IdeaModule.html
https://docs.gradle.org/4.8.1/dsl/org.gradle.plugins.ide.eclipse.model.EclipseClasspath.html
Usually this would have been picked up through sourceSets but I can't see what your project looks like...
If your subproject uses Gradle to generate sources into /build/cxf/generated-sources directory, then you can tell Eclipse via Gradle DSL to include that as a source folder like this:
plugins { id 'eclipse' }
eclipse.classpath.file.whenMerged {
// this is the brute-force approach; there is likely a better way to add a source folder
entries << new org.gradle.plugins.ide.eclipse.model.SourceFolder('build/cxf/generated-sources', null)
}
Once this is run (via gradle eclipseClasspath) you should see a build/cxf/generated-sources folder under your project node in the Package Explorer or Project Explorer. Sort of like this:
NOTE: This is untested because I don not have a sample project to work with.
There is more discussion here: How to add gradle generated source folder to Eclipse project?

Eclipse Projectdependency on multiple src directories

today I have a special problem which already took me a while at the debugger.
I have two projects Project A and Project B.
Project A has multiple src-directories.
src
├───main
│ └───java
└───generated
└───java
both are recognized by eclipse as actual src directories. Both will be compiled to bin which looks like this:
bin
├───main
└───generated
Project B has a Project-dependencie on Project A.
And now comes the strange part: When I look for a class from Project A/src/main/java via Class.forName() inside Project B it will be found. When i look for a class from Project A/src/generated/java I get a ClassNotFound exception.
I would be very glad if you could point out a way to tell eclipse to create a dependencie on both src-directories.
btw, just in case it is important: I am using java 9.
and here is an excerpt of .classpath from Project B
<classpathentry kind="src" path="/Project A"/>
Thanks for your help.
This looks like a bug of Eclipse, Gradle or a combination of both. Try to delete the run configuration and restart the application. Make also sure the gradle.build file is in sync with the Eclipse project. If all this doesn't help, you can use a single output folder as workaround:
Manually in Project > Properties: Java Build Path, in the tab Source:
Uncheck the checkbox Allow output folders for source folders
In the field Default output folder enter bin/main
or via following gradle.build snippet:
apply plugin: 'eclipse'
eclipse.classpath.file.whenMerged {
entries.find { it.path == 'src/main/java' }.output = 'bin/main'
entries.find { it.path == 'src/generated/java' }.output = 'bin/main'
}

system compiler variables are not overwritten in merged project

I have a launcher in a sub-project whose name is dependend on the ${compiler:sys.version} variable.
The project is merged into a parent project, which is build with a gradle task on command line.
task serverSetup(type: com.install4j.gradle.Install4jTask, dependsOn: 'initialize') {
projectFile = file('server.install4j')
release = artifactVersion
}
Problem is, that the launcher of the sub-project is created with the value set in the sub-projects Application Info and not overridden with the version set by the gradle task.
Am I doing something wrong? The documentation says:
As a result of flat merging, there are no intermediary artifacts for merged projects. .... In particular, all elements in the final result share the same
namespace for compiler and installer variables.
Because of this I expect the sys.version variable to be overriden in the sub-project.
This is a bug, it will be fixed in 7.0.5. Please contact support#ej-technologies.com to get a build where this already fixed.

How to cherry pick ZIP contents with Gradle Distribution plugin?

My Gradle build currently produces the following directory structure under a build dir in my project root:
myapp/
src/
build.gradle
build/
docs/
groovydoc/* (all Groovydocs)
libs/
myapp-SNAPSHOT.jar
myapp-SNAPSHOT-sources.jar
reports/
codenarc/
main.html
test-results/* (JUnit test results)
I would like to add the distribution plugin (or anything that accomplishes my goals, really) to have Gradle produce a ZIP file with the following directory structure:
myapp-SNAPSHOT-buildreport.zip/
tests/
(JUnit tests from build/test-results above)
reports/
main.html (CodeNarc report from build/reports/codenarc above)
api/
(Groovydocs from build/docs above)
source/
myapp-SNAPSHOT-sources.jar (from build/libs above)
bin/
myapp-SNAPSHOT.jar (from build/libs above)
After reading the plugin's documentation, I can't tell how to configure it to suit these needs. Its obvious that I need to run gradle distZip, but as to how to actually configure it to produce the desired directory structure, it doesn't seem to provide any documentation/examples. Any ideas?
Note: The JAR's version is obviously SNAPSHOT, and is passed into the Gradle build with a -Pversion=SNAPSHOT command-line argument.
The Gradle Distribution plugin automatically has defaults (the problem is that the docs do not tell us the defaults, but the Gradle project default structure is what is assumed) so if your Gradle project is fairly straightforward and already using src/main/groovy or src/main/java, you typically just need to...
Use the CopySpec reversing pattern of giving your into{} (makes a dir) containing the contents of from{} , rather than the reverse, like so:
apply plugin: 'groovy'
apply plugin: 'eclipse'
apply plugin: 'application'
distributions {
main {
baseName= 'vz_sde_dm'
contents {
into('bin/config') {
from 'config'
}
into('lib/samples') {
from 'samples'
}
}
}
}
Notice I did not need to define a from{} for my contents{}, but only into{}s that's because I am already using the default Gradle Groovy project layout and only added 2 extra folders (config & samples) under my project in Eclipse but needed those 2 folders to land into a slightly different hierarchy for my Distribution compared to my regular build folder layout.
I would probably not use the distribution plugin and instead just create a new custom Zip task. It would look something like this:
task buildreportZip(type: Zip, dependsOn: build) {
classifier = 'buildreport'
from('build/test-results') {
into 'tests'
}
from('build/reports/codenarc') {
into 'reports'
}
from('build/docs') {
into 'api'
}
from(sourcesJar) { // or whatever you source jar task name is
into 'source'
}
from(jar) {
into 'bin'
}
}
I was trying to make a custom layout also, and had real trouble figuring out how to exclude the project output from build/libs from the yourProject.zip/yourProject/lib directory (and excluding things in general) and putting it instead into yourProject.zip/yourProject.
After quite a few hours across multiple days of searching and poking around in the API I finally found something that worked using actual configurations of the Distribution and underlying CopySpec (documented here and here, respectively for Gradle 5.6.1, you can just replace 5.6.1 with current in the URL to get the most recent API docs, 5.6.1 just happens to be the version I'm using):
distributions {
main {
baseName = appName
contents {
filesMatching("**/${appName}.jar", {
if (it.getPath().contains('/lib/')) {
it.setPath(it.getPath().replace('lib/', ''))
}
})
into('config') {
exclude(['server.crt', 'spotbugs-exclusion-filters.xml'])
from 'src/main/resources'
}
}
}
}
For exclusions the only thing that worked was matching on a glob pattern and specifying the correct action (to copy it to the root dist directory instead of root/lib) with a Closure through the filesMatching method of the main distribution's content CopySpec. You can see also how destination for configs is changed from the root to the root/config directory. Thanks so Thad's answer for helping to guide me to the correct build configuration, also.

Scala project won't compile in Eclipse; "Could not find the main class."

I have installed Eclipse 3.5.2 and today's Scala plugin from /update-current (that's Scala 2.8 final.) I can compile and run Scala projects consisting of a single singleton object that implements main().
But, if a project contains more classes, I receive the "Could not find the main class" error.
I have tried searching for the solution and I discovered:
Eclipse is correctly looking for the Main$ class, not the Main class
* under Debug Configurations, my main class is correctly identified as mypackage.Main
* my plugin is up to date and recommended for my version of Eclipse
* cleaning, restarting etc. doesn't help.
The same project will compile with scalac.
Thanks for any ideas on how to solve this.
EDIT: MatthieuF suggested I should post the code.
This snippet produces an error. It's not the most idiomatic code, but I wrote it that way to test my environment. I tried it as a single file and as separate files. It DOES work with scalac.
import swing._
class HelloFrame extends Frame {
title = "First program"
contents = new Label("Hello, world!")
}
object Hello {
val frame = new HelloFrame
def main(args : Array[String]) : Unit = {
frame.visible = true
}
}
BUT, if I nest the definition of HelloFrame within Hello, it works. This snippet runs perfectly:
import swing._
object Hello {
class HelloFrame extends Frame {
title = "First program"
contents = new Label("Hello, world!")
}
val frame = new HelloFrame
def main(args : Array[String]) : Unit = {
frame.visible = true
}
}
For me, the problem was that there was a build error (see Problems tab) which was preventing compilation; oops! The reason you see the error is that the run macro proceeds despite the failed compilation step, and attempts to run class files it expects to be there; they don't exist because there was a build error preventing compilation, so it says it can't find Main (not compiled).
Problem goes away when build can complete successfully, i.e. errors are fixed.
I guess, theoretically, there may be more complicated reasons your build is not completing successfully that are not listed in Problems.
One possibility is that you are trying to launch using ctrl-F11, but from a different class.
The Scala Eclipse plugin does not obey the defaults for Java launching. In Preferences->Run/Debug->Launching, there are some options Launch Operation->Always Launch the previously selected application, etc. This currently does not work in the Scala eclipse plugin. To launch a specified main, you need to launch it from the editor for the class.
There has been a bug raised for this. http://scala-ide.assembla.com/spaces/scala-ide/tickets/1000023-scala-launch--does-not-follow-jdt-behaviour
EDIT: This is now (mostly) fixed.
For me it was Eclipse specific problem. I noticed that .class file wasn't built at all. So bin directory doesn't have compiled classes.
When I manually compiled *.scala file using *.sbt and copied it to bin directory it was working as expected.
I tried different tips and tricks and it wasn't worked until I reinstalled Scala plugin in Eclipse .
I'd solve similar problem by executig "Project->Clean.." with next automatically building.
I had the same error message with a Java application made by myself.
The problem was that I deleted (though inside Eclipse) a jar that belonged to the Java build path, without deleting it from the Java build path (project's Properties window). When I did it the class could compile and run again.
Make sure that the .class files exist, usually below the bin directory.
In particular, if you have errors in unrelated files in the same project then the compilation may fail, and no .class files will be produced.
There can be the case of projects, containing errors, added to the build path of the application which prevents the completion of successful compilation. Make sure you remove any such project from the build path before running the application.
Removing these projects solved the problem for me.
Do you have a proper build tool setup? Like sbt have you installed it?
You can check its version by $sbt --version
If it is not setup you can download from here http://www.scala-sbt.org/download.html
You might have to restart your eclipse after installation.
Just copy your XXX.scala file code. Remove the package and create a new Scala Class. Paste your XXX.scala code. (If you are using maven, do a maven clean and build.) Run configuration again. This works for me.
I have faced this issue. I have just deleted the package name, created scala class, Written the same code, Set Build to "Build Automatically". Finally, It works perfectly fine.
Check scala-ide.log
For me the issue was that there were errors on:
AppData\Local\Temp\sbt_10d322fb\xsbt\ClassName.scala:16: error: not found: value enteringPhase
enteringPhase(currentRun.flattenPhase.next) { s fullName separator }
If you are using Intellij, mark directory as source root