20

I'd like to generate a Java class using a Gradle task for a Java project, similar to how the Android plugin creates BuildConfig.java using the buildConfig <String> notation, e.g.:

android {
    ...

    buildTypes {
        final String PROVIDER_AUTHORITY_RELEASE = "public static final String PROVIDER_AUTHORITY = \"com.example.project.ContentProvider\";\n";
        final String PROVIDER_AUTHORITY_DEBUG = "public static final String PROVIDER_AUTHORITY = \"com.example.project.debug.ContentProvider\";\n";

        debug {
            ...
            buildConfig PROVIDER_AUTHORITY_DEBUG
        }

        release {
            ...
            buildConfig PROVIDER_AUTHORITY_RELEASE
        }
    }
}

The use case is that I'm developing an open source app which requires the use of an API key and secret; I have the key and secret in gradle.properties which isn't committed to my VCS.

So far, I have this:

task generateSources {
    outputDir = file("$buildDir/../src/main/java/uk/co/ataulm/mijur/core/api")
    outputs.dir outputDir
    doFirst {
        outputDir.exists() || outputDir.mkdirs()

        String API_CLIENT_ID = "public static final String API_CLIENT_ID = \"<your imgur uk.co.ataulm.mijur.core.api client id>\";\n"
        String API_CLIENT_SECRET = "public static final String API_CLIENT_SECRET = \"<your imgur uk.co.ataulm.mijur.core.api client secret>\";\n"

        try {
            API_CLIENT_ID = "public static final String API_CLIENT_ID = \"" + apiClientId + "\";\n"
            API_CLIENT_SECRET = "public static final String API_CLIENT_SECRET = \"" + apiClientSecret + "\";\n"
        } catch (Exception e) {
            println "gradle.properties not set with apiClientId and/or apiClientSecret. API calls will not work.";
        }

        new File(outputDir, "ApiConstants.java").write("package uk.co.ataulm.mijur.core.api;\n\npublic class ApiConstants {\n" + "    " + API_CLIENT_ID + "    " + API_CLIENT_SECRET + "}")
    }
}

compileJava.source generateSources.outputs.files, sourceSets.main.java

and it works - it generates that file at the specified location. But it's very fragile; explicitly naming the package is prone to error. I'd be happy if the file is generated in some other package (e.g. at the root of the src/main/java) as long as I can access it in Java, using MyGeneratedFile.MyConstant.

Appreciate any ideas (even if they are down a different track).

6
  • Why don't you simply use a properties file with a placeholder, replaced at build time by the real value, and loaded at runtime by your class? Commented Nov 20, 2013 at 18:49
  • 2
    @JBNizet replaced at build time - as in, when it's first accessed? By having gradle generate it for me, I can provide different configurations per build flavour. Commented Nov 20, 2013 at 21:19
  • I've been considering the same approach too, but would be very interested in a Gradle based solution. Commented Nov 20, 2013 at 21:24
  • By "at build time", I mean "by Gradle". It just seems much easier and natural to me to just filter a properties file than to generate Java file during the build. Commented Nov 20, 2013 at 22:04
  • I don't see how putting placeholder values in the properties file would help; given the build script is version controlled, shouldn't the real values be in the properties? Commented Nov 20, 2013 at 22:14

2 Answers 2

20

I wrote a gradle plugin which aims to provide a build config like the one from android tooling gradle plugin for java/groovy projects:

https://github.com/mfuerstenau/gradle-buildconfig-plugin

It's per source set, because I needed a specific version to display for different artifacts relying on two different source sets.

just apply the plugin (gradle 2.1+)

plugins {
    'de.fuerstenau.buildconfig'
}

buildConfig {
   sourceSets {
      // this is a build config for "main" source set
      main {
         // if not defined, these are the defaults
         // packageName = project.group (not specified -> de.fuerstenau.buildconfig)
         // appName = project.name
         // version = project.version

         // custom fields con also be added
         // for example:
         // buildConfigField "String" "MYSTRING" "value"
      }
      // add more or other sourceSets like "main", be aware
      // of conflicts if you use
      // them combined
   }
}

will result in a class de.fuerstenau.buildconfig.BuildConfig (package can be configured as above):

package de.fuerstenau.buildconfig;

public final class BuildConfig
{
   private BuildConfig { }
   public final String VERSION = "<whatever-version-was-configured>";
   public final String NAME = "<whatever-appName-was-configured>";
}

Two tasks will be created and added, one to generate the class, the other to compile it, they execute before "compileJava"-task and the result is added as dependency.

Sign up to request clarification or add additional context in comments.

1 Comment

I updated the plugin to version 1.1.0 recently. It now supports setting properties in the general closure and overriding in the source set closures. Renaming of the build config is now suported. And the UP-TO-DATE-calculation of the tasks is better, so the generation only happens if something changed.
6

More elegant approach is to write a Gradle Plugin.

task javaSourceGen(type: com.somecompany.gensrc.GenerateSouce) {
  //pass appropriate parameters
}
compileJava.dependsOn(javaSourceGen)

Then in the Gradle Plugin, Any Java Source generator project can be used.

For ex: https://github.com/square/javapoet or http://codemodel.java.net/

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.