Gradle 2.4 NPE with dynamic Task Inputs using groovy propertyMissing method

Given the following build script:

class CompileResourcesTask extends DefaultTask {
  def dynamicProperties= [:]
  def propertyMissing(String name, value) { dynamicProperties[name] = value }
  def propertyMissing(String name) { dynamicProperties= [name] }

  @TaskAction
  void execute() {
    println "Hello ${dynamicProperties["dynamic"]}"
  }
}

task hello(type: CompileResourcesTask) {
  dynamic = "World"
}

I get the following output with Gradle 2.3:

gradlew hello
:hello
Hello World

But a NPE with Gradle 2.4:

FAILURE: Build failed with an exception.

* Where:
Build file 'build.gradle' line: 13

* What went wrong:
A problem occurred evaluating root project 'test'.
> java.lang.NullPointerException (no error message)

* Try:
Run with --info or --debug option to get more log output.

* Exception is:
org.gradle.api.GradleScriptException: A problem occurred evaluating root project 'test'.
        at org.gradle.groovy.scripts.internal.DefaultScriptRunnerFactory$ScriptRunnerImpl.run(DefaultScriptRunnerFactory.java:76)
        at org.gradle.configuration.DefaultScriptPluginFactory$ScriptPluginImpl$1.run(DefaultScriptPluginFactory.java:148)
        at org.gradle.configuration.DefaultScriptPluginFactory$ScriptPluginImpl.apply(DefaultScriptPluginFactory.java:156)
        at org.gradle.configuration.project.BuildScriptProcessor.execute(BuildScriptProcessor.java:39)
        at org.gradle.configuration.project.BuildScriptProcessor.execute(BuildScriptProcessor.java:26)
        at org.gradle.configuration.project.ConfigureActionsProjectEvaluator.evaluate(ConfigureActionsProjectEvaluator.java:34)
        at org.gradle.configuration.project.LifecycleProjectEvaluator.evaluate(LifecycleProjectEvaluator.java:55)
        at org.gradle.api.internal.project.AbstractProject.evaluate(AbstractProject.java:487)
        at org.gradle.api.internal.project.AbstractProject.evaluate(AbstractProject.java:85)
        at org.gradle.execution.TaskPathProjectEvaluator.configureHierarchy(TaskPathProjectEvaluator.java:42)
        at org.gradle.configuration.DefaultBuildConfigurer.configure(DefaultBuildConfigurer.java:35)
        at org.gradle.initialization.DefaultGradleLauncher.doBuildStages(DefaultGradleLauncher.java:129)
        at org.gradle.initialization.DefaultGradleLauncher.doBuild(DefaultGradleLauncher.java:106)
        at org.gradle.initialization.DefaultGradleLauncher.run(DefaultGradleLauncher.java:86)
        at org.gradle.launcher.exec.InProcessBuildActionExecuter$DefaultBuildController.run(InProcessBuildActionExecuter.java:90)
        at org.gradle.tooling.internal.provider.ExecuteBuildActionRunner.run(ExecuteBuildActionRunner.java:28)
        at org.gradle.launcher.exec.ChainingBuildActionRunner.run(ChainingBuildActionRunner.java:35)
        at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:41)
        at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:28)
        at org.gradle.launcher.exec.DaemonUsageSuggestingBuildActionExecuter.execute(DaemonUsageSuggestingBuildActionExecuter.java:50)
        at org.gradle.launcher.exec.DaemonUsageSuggestingBuildActionExecuter.execute(DaemonUsageSuggestingBuildActionExecuter.java:27)
        at org.gradle.launcher.cli.RunBuildAction.run(RunBuildAction.java:40)
        at org.gradle.internal.Actions$RunnableActionAdapter.execute(Actions.java:169)
        at org.gradle.launcher.cli.CommandLineActionFactory$ParseAndBuildAction.execute(CommandLineActionFactory.java:237)
        at org.gradle.launcher.cli.CommandLineActionFactory$ParseAndBuildAction.execute(CommandLineActionFactory.java:210)
        at org.gradle.launcher.cli.JavaRuntimeValidationAction.execute(JavaRuntimeValidationAction.java:35)
        at org.gradle.launcher.cli.JavaRuntimeValidationAction.execute(JavaRuntimeValidationAction.java:24)
        at org.gradle.launcher.cli.CommandLineActionFactory$WithLogging.execute(CommandLineActionFactory.java:206)
        at org.gradle.launcher.cli.CommandLineActionFactory$WithLogging.execute(CommandLineActionFactory.java:169)
        at org.gradle.launcher.cli.ExceptionReportingAction.execute(ExceptionReportingAction.java:33)
        at org.gradle.launcher.cli.ExceptionReportingAction.execute(ExceptionReportingAction.java:22)
        at org.gradle.launcher.Main.doAction(Main.java:33)
        at org.gradle.launcher.bootstrap.EntryPoint.run(EntryPoint.java:45)
        at org.gradle.launcher.bootstrap.ProcessBootstrap.runNoExit(ProcessBootstrap.java:54)
        at org.gradle.launcher.bootstrap.ProcessBootstrap.run(ProcessBootstrap.java:35)
        at org.gradle.launcher.GradleMain.main(GradleMain.java:23)
        at org.gradle.wrapper.BootstrapMainStarter.start(BootstrapMainStarter.java:30)
        at org.gradle.wrapper.WrapperExecutor.execute(WrapperExecutor.java:127)
        at org.gradle.wrapper.GradleWrapperMain.main(GradleWrapperMain.java:61)
Caused by: java.lang.NullPointerException
        at org.gradle.api.internal.coerce.StringToEnumTransformer.transformValue(StringToEnumTransformer.java:64)
        at org.gradle.api.internal.BeanDynamicObject$MetaClassAdapter.setProperty(BeanDynamicObject.java:193)
        at org.gradle.api.internal.BeanDynamicObject.setProperty(BeanDynamicObject.java:111)
        at org.gradle.api.internal.CompositeDynamicObject.setProperty(CompositeDynamicObject.java:112)
        at CompileResourcesTask_Decorated.setProperty(Unknown Source)
        at build_o2h0g64uhauf4rd26bvg1fwx$_run_closure1.doCall(C:\Users\dhe\Desktop\test\build.gradle:13)
        at org.gradle.api.internal.ClosureBackedAction.execute(ClosureBackedAction.java:67)
        at org.gradle.util.ConfigureUtil.configure(ConfigureUtil.java:130)
        at org.gradle.util.ConfigureUtil.configure(ConfigureUtil.java:110)
        at org.gradle.api.internal.AbstractTask.configure(AbstractTask.java:492)
        at org.gradle.api.internal.project.AbstractProject.task(AbstractProject.java:874)
        at org.gradle.api.internal.BeanDynamicObject$MetaClassAdapter.invokeMethod(BeanDynamicObject.java:232)
        at org.gradle.api.internal.BeanDynamicObject.invokeMethod(BeanDynamicObject.java:127)
        at org.gradle.api.internal.CompositeDynamicObject.invokeMethod(CompositeDynamicObject.java:150)
        at org.gradle.groovy.scripts.BasicScript.methodMissing(BasicScript.java:79)
        at build_o2h0g64uhauf4rd26bvg1fwx.run(build.gradle:12)
        at org.gradle.groovy.scripts.internal.DefaultScriptRunnerFactory$ScriptRunnerImpl.run(DefaultScriptRunnerFactory.java:74)
        ... 38 more


BUILD FAILED

Total time: 3.04 secs

Is there a better way / workaround to achieve the same thing in Gradle 2.4 (i.e. using non-predefined dynamic task inputs)?

The NPE seems to stem from commit

6eed51605b7e096707c947acb98a7199fa42de33

where it can’t find the property type because the property doesn’t exist.

What are you trying to do? Gradle tasks already have dynamic/extensible properties, so you’re clashing with that.

How do dynamic task inputs work in gradle, using ext? Is there a way I could intercept them to use as in the scenario below?

Essentially the task is only delegating to a thirdparty library I can’t/don’t want to change. Imagine the library having an interface like this:

void setConfigValue(String name, String value);
String getConfigValue(String name);
void doStuffWithConfigValues()

So my task currently looks like this:

class MyWrapperTask extends DefaultTask {

  def thirdparty
  def propertyMissing(String name, value) { 
    thirdparty.setConfigValue(name, value) 
  }
  def propertyMissing(String name) { 
    return thirdparty.getConfigValue(name) 
  }

  @TaskAction
  void execute() {
    thirdparty.doStuffWithConfigValues()
  }
}

Ah, I see what you’re trying to do.

What you would usually do is add the properties you supported from the third party tool to your task. This gives you the opportunity to mark which ones should be considered inputs or outputs. And you can also add documentation/workarounds/etc. Then the first step of your execute() would be to take all the properties and set the properties on the thirdparty object.

If that doesn’t make sense (i.e., your task has no inputs/outputs and should run all the time), you could just expose the thirdparty property directly. So a user might do something like:

task yourTask(type: MyWrapperTask) {
   thirdparty.with {
       prop1 = 'blah'
   }
   // or
   thirdparty.prop1 = 'blah'
}