mfuerstenau / gradle-buildconfig-plugin

A BuildConfig plugin for Gradle java projects
MIT License
173 stars 20 forks source link

Generates BuildConfig.class in root project although it's only configured for sub-projects. #6

Closed mrt181 closed 8 years ago

mrt181 commented 8 years ago

I have a multiproject with this layout

rootProject.name = 'myproject'
include 'myproject-core'
include 'myproject-ui'
allprojects {
  apply plugin: 'idea'
  apply plugin: 'eclipse'

  eclipse.project {
    natures 'org.eclipse.buildship.core.gradleprojectnature'
  }
}

subprojects {
  group 'com.myproject'
  version '1.1.1'

  buildscript {
    repositories {
      maven {
        url 'https://plugins.gradle.org/m2/'
      }
    }
  }

  apply plugin: 'java'
  apply plugin: 'de.fuerstenau.buildconfig'

  /* closure needs to be present for build config to be created, build config is
   * created for "main" source set that way */
  buildConfig {}
}

The plugin generates the BuildConfig.class in

  1. ./myproject/build/gen/buildconfig/classes/main -- package de.fuerstenau.buildconfig;
  2. ./myproject-core/build/gen/buildconfig/classes/main -- package com.myproject;
  3. ./myproject-ui/build/gen/buildconfig/classes/main -- package com.myproject;

Shouldn't it skip the root project and just be applied for the sub projects?

mfuerstenau commented 8 years ago

I will look into this after work today. Honestly I did not a lot of multiproject testing. I guess it's about time.

mfuerstenau commented 8 years ago

@mrt181 I'm trying to reconstruct your project. But the above build.gradle doesn't work for me. I'm missing the classpath dependency? Did you miss

  dependencies {
    classpath 'gradle.plugin.de.fuerstenau:BuildConfigPlugin:1.1.6'
  }

also It doesn't work for me if I put the

buildscript {
  repositories {
    maven {
      url 'https://plugins.gradle.org/m2/'
    }
  }
  dependencies {
    classpath 'gradle.plugin.de.fuerstenau:BuildConfigPlugin:1.1.6'
  }
}

inside the subprojects { }-closure, instead I can put it like this

buildscript {
  repositories {
    maven {
      url 'https://plugins.gradle.org/m2/'
    }
  }
  dependencies {
    classpath 'gradle.plugin.de.fuerstenau:BuildConfigPlugin:1.1.6'
  }
}

allprojects {
  apply plugin: 'idea'
  apply plugin: 'eclipse'

  eclipse.project {
    natures 'org.eclipse.buildship.core.gradleprojectnature'
  }
}

subprojects {
  group '´'
  version '1.1.1'

  apply plugin: 'java'
  apply plugin: 'de.fuerstenau.buildconfig'

  /* closure needs to be present for build config to be created, build config is
   * created for "main" source set that way */
  buildConfig {}
}

though my results are

  1. none for root project
  2. ./myproject-core/build/gen/buildconfig/classes/main -- package com.myproject;
  3. ./myproject-ui/build/gen/buildconfig/classes/main -- package com.myproject;;
mrt181 commented 8 years ago

Ok, I have heavily abbreviated the first build file sample.

This is a more complete sample:

root build.gradle

plugins {
  id 'org.gradle.java'
  id 'de.fuerstenau.buildconfig' version '1.1.6'
}

allprojects {
  apply plugin: 'idea'
  apply plugin: 'eclipse'

  eclipse.project {
    natures 'org.eclipse.buildship.core.gradleprojectnature'
  }
}

subprojects {
  def getVersionName = { ->
    try {
      def stdout = new ByteArrayOutputStream()
      exec {
        commandLine 'git', 'describe', '--tags', '--dirty', '--always', '--long'
        standardOutput = stdout
      }
      return stdout.toString().trim()
    }
    catch (ignored) {
      return null;
    }
  }

  group 'com.myproject'
  version getVersionName()

  buildscript {
    repositories {
      maven {
        url 'https://plugins.gradle.org/m2/'
      }
    }
    dependencies {
      classpath 'gradle.plugin.edu.sc.seis.gradle:launch4j:1.5.1'
    }
  }

  apply plugin: 'java'
  apply plugin: 'de.fuerstenau.buildconfig'

  /* closure needs to be present for build config to be created, build config is
   * created for "main" source set that way */
  buildConfig {}

  sourceCompatibility = 1.8
  targetCompatibility = 1.8
  [compileJava, compileTestJava]*.options*.encoding = 'UTF-8'

  repositories {
    maven { url "http://central.maven.org/maven2" }
    maven { url "https://plugins.gradle.org/m2/" }
    maven { url "http://d-maven.googlecode.com/svn/trunk/repo" }
  }

  //noinspection GroovyAssignabilityCheck
  dependencies {
    compile 'com.google.guava:guava:18.0'
    compile 'org.apache.commons:commons-lang3:3.4'
    compile 'org.apache.camel:camel-core:2.16.+'
    compile 'org.apache.poi:poi:3.14'
    compile 'org.apache.commons:commons-dbcp2:2.1.+'
    compile 'org.springframework:spring-context:4.2.+'
    compile 'net.bytebuddy:byte-buddy:1.3.+'
    compile 'javax.money:money-api:1.0'
    compile 'org.javamoney:moneta:1.0'
    compile 'com.google.code.findbugs:jsr305:3.0.+'
    compile 'javax.validation:validation-api:1.1.0.Final'
    compile 'log4j:log4j:1.2.+'
    compile 'org.slf4j:slf4j-api:1.6.+'
    compile 'org.slf4j:slf4j-log4j12:1.6.+'
    compile 'org.slf4j:jcl-over-slf4j:1.6.+'
    testCompile ('junit:junit:4.12') { exclude group: 'org.hamcrest', module: 'hamcrest-core' }
    testCompile 'org.codehaus.groovy:groovy-all:2.4'
    testCompile 'org.spockframework:spock-core:1.0-groovy-2.4'
    testCompile 'org.apache.camel:camel-test:2.17.+'
    testCompile ('org.mockito:mockito-core:1.10.+') { exclude group: 'org.hamcrest', module: 'hamcrest-core' }
    testCompile 'org.hamcrest:hamcrest-junit:2.0.+'
    testCompile 'org.hamcrest:hamcrest-all:1.3'
  }

  eclipse {
    classpath {
      downloadSources = true
      downloadJavadoc = true
    }
  }

  idea {
    module {
      downloadSources = true
      downloadJavadoc = true
    }
  }

  gradle.projectsEvaluated {
    tasks.withType(JavaCompile) {
      options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation"
    }
  }
}

This creates the BuildConfig.class in the root project:

~ > sourcecode > myproject >  develop > 0748cd0 > ./gradlew clean compileJava
:clean
:attila-core:clean
:attila-ui:clean
:eclipseClasspath
:generateBuildConfig
:compileBuildConfig
:compileJava UP-TO-DATE
:attila-core:eclipseClasspath
:attila-core:generateBuildConfig
:attila-core:compileBuildConfig
:attila-core:compileJava
:attila-core:compileGroovy UP-TO-DATE
:attila-core:processResources
:attila-core:classes
:attila-core:jar
:attila-ui:eclipseClasspath
:attila-ui:generateBuildConfig
:attila-ui:compileBuildConfig
:attila-ui:compileJava

BUILD SUCCESSFUL

Total time: 20.404 secs
~ > sourcecode > myproject >  develop > 0748cd0 >  find . -name "BuildConfig*"
./myproject-core/build/gen/buildconfig/classes/main/com/myproject/BuildConfig.class
./myproject-core/build/gen/buildconfig/src/main/com/myproject/BuildConfig.java
./myproject-ui/build/gen/buildconfig/classes/main/com/myproject/BuildConfig.class
./myproject-ui/build/gen/buildconfig/src/main/com/myproject/BuildConfig.java
./build/gen/buildconfig/classes/main/de/fuerstenau/buildconfig/BuildConfig.class
./build/gen/buildconfig/src/main/de/fuerstenau/buildconfig/BuildConfig.java

This is my environment

~ > sourcecode > myproject >  develop > 0748cd0 > ./gradlew -version

------------------------------------------------------------
Gradle 2.14.1
------------------------------------------------------------

Build time:   2016-07-18 06:38:37 UTC
Revision:     d9e2113d9fb05a5caabba61798bdb8dfdca83719

Groovy:       2.4.4
Ant:          Apache Ant(TM) version 1.9.6 compiled on June 29 2015
JVM:          1.8.0_74 (Oracle Corporation 25.74-b02)
OS:           Windows 7 6.1 amd64

~ > sourcecode > myproject >  develop > 0748cd0 > uname -a
CYGWIN_NT-6.1 windows-machine 2.5.2(0.297/5/3) 2016-06-23 14:29 x86_64 Cygwin
~ > sourcecode > myproject >  develop > 0748cd0 >  java -version
java version "1.8.0_74"
Java(TM) SE Runtime Environment (build 1.8.0_74-b02)
Java HotSpot(TM) 64-Bit Server VM (build 25.74-b02, mixed mode)
mfuerstenau commented 8 years ago

Okay. The interesting part is

   plugins 'de.fuerstenau.buildconfig'

because it applies the plugin to the main/root project. Not the buildconfig { }-closure triggers the creation of the default buildconfig task, the applying of the plugin does. There is no way I know of to check the existence of the closure in the buildscript, as it always exists if the plugin is applied (even if not written in the script). That's why I made the task upon applying the plugin. A workaround would be to write

buildscript {
  repositories {
    maven {
      url 'https://plugins.gradle.org/m2/'
    }
  }
  dependencies {
    classpath 'gradle.plugin.de.fuerstenau:BuildConfigPlugin:1.1.6'
  }
}
plugins {
    id 'java'
}

instead of

plugins {
  id 'org.gradle.java'
  id 'de.fuerstenau.buildconfig' version '1.1.6'
}

Note: the missing apply plugin: 'de.fuerstenau.buildconfig'. That way the classpath is available (to apply to the subprojects as You do), but the plugin is not applied to the main/root project.

mrt181 commented 8 years ago

Hi, after moving this

plugins {
  id 'de.fuerstenau.buildconfig' version '1.1.6'
}

into the sub projects build.gradle files and applying the plugins there the class is no longer generated in the root project.

mfuerstenau commented 8 years ago

Good to hear. I made some effort yesterday to find a way to detect if an configuration closure was put into the buildscript. Its simply not possible. The closure is applied via Groovy delegate-magic and there is no way to detect that. That way the mere presence of an empty configuration closure cannot be used as a trigger to create the tasks.

Maybe a closure with a value defaultTaskCreation = true which can be set to false would be a viable way to make it easy to have automatic tasks created for the default case and prevent it in special cases.

mrt181 commented 8 years ago

For completeness and in case someone else has a similar question. I also added this to see the generated src files in eclipse when using the eclipse_plugin otherwise only the class files would be available in eclipse. The idea_plugin provides a similar mechanism for intellij

eclipse {
  classpath {
    file.beforeMerged { cp ->
      cp.entries.add( new org.gradle.plugins.ide.eclipse.model.SourceFolder('build/gen/buildconfig/src/main', null))
    }
  }
}
mfuerstenau commented 8 years ago

Thank you. Yes, the IDEs add a lot of magic through their plugins. I am not sure if I like that, there should be a pure gradle way to do this. IntelliJ is great, I use it at work, but I shudder everytime I am aware of the abstractions it lays over a simple gradle project.