# Best practice to handle frequently API signature changes

**URL:** <https://discuss.gradle.org/t/best-practice-to-handle-frequently-api-signature-changes/19338>\
**Category:** Plugin Portal\
**Created:** [September 2, 2016, 7:17am UTC](https://discuss.gradle.org/t/best-practice-to-handle-frequently-api-signature-changes/19338 "2016-09-02T07:17:31Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![Horcrux7](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/horcrux7/32/96_2.png) [@Horcrux7](https://discuss.gradle.org/u/Horcrux7)\
**Post date:** [September 2, 2016, 7:17am UTC](https://discuss.gradle.org/t/best-practice-to-handle-frequently-api-signature-changes/19338/1 "2016-09-02T07:17:31Z")

</div>

What is the best practice to handle the frequently API signature changes in Gradle? We need to compile our plugin for different Gradle versions again.

Without the recompile or if you use the wrong plugin version the users receive a java.lang.NoSuchMethodError at runtime. Currently we have already 4 different plugin versions for different Gradle versions. Can the plugin portal handle this?

> **[i-net-software/SetupBuilder](https://github.com/i-net-software/SetupBuilder)**
>
> SetupBuilder - Gradle plugin for building setups for different platforms.

---

<div class="post-metadata">

**Author:** ![st\_oehme](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/st_oehme/32/5249_2.png) [@st\_oehme](https://discuss.gradle.org/u/st_oehme)\
**Post date:** [September 2, 2016, 1:21pm UTC](https://discuss.gradle.org/t/best-practice-to-handle-frequently-api-signature-changes/19338/2 "2016-09-02T13:21:00Z")

</div>

This sounds like you are using internal APIs, because we don’t break the public ones. Could you please elaborate which APIs you are referring to?

---

<div class="post-metadata">

**Author:** ![st\_oehme](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/st_oehme/32/5249_2.png) [@st\_oehme](https://discuss.gradle.org/u/st_oehme)\
**Post date:** [September 2, 2016, 1:30pm UTC](https://discuss.gradle.org/t/best-practice-to-handle-frequently-api-signature-changes/19338/3 "2016-09-02T13:30:21Z")

</div>

I just had a look at the project and there is internal API usage everywhere. Anything containing the word `internal` is _not_ supported.

Please refactor the plugin towards using only the public APIs. If there is something that you can’t do with the public API, please let us know. We’ll try to find a solution together.

---

<div class="post-metadata">

**Author:** ![Horcrux7](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/horcrux7/32/96_2.png) [@Horcrux7](https://discuss.gradle.org/u/Horcrux7)\
**Post date:** [September 2, 2016, 2:05pm UTC](https://discuss.gradle.org/t/best-practice-to-handle-frequently-api-signature-changes/19338/4 "2016-09-02T14:05:08Z")

</div>

- First our tasks and extensions implements CopySpec. There is no default implementation. If you extends this interface we need to do it also.
- Then we use DefaultExecAction because the java.lang.ProcessBuilder has produce problems inside from Gradle.
- Get a FileResolver which self is internal.
- Get a CopySpecResolver which self is internal

Theoretical your are right. We should not use internal. But then we need to implements all self like a CopySpec. Then the build scripts can not interact with other tasks harmonically.

If you try to implements a task like “zip” in your own plugin without the usage of the internal API then you will see where are the problems.

---

<div class="post-metadata">

**Author:** ![st\_oehme](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/st_oehme/32/5249_2.png) [@st\_oehme](https://discuss.gradle.org/u/st_oehme)\
**Post date:** [September 2, 2016, 2:24pm UTC](https://discuss.gradle.org/t/best-practice-to-handle-frequently-api-signature-changes/19338/5 "2016-09-02T14:24:40Z")

</div>

Did you check out Project.exec, project.copySpec and project.file? They  
should solve what you are currently doing with subclasses.

CopySpec and ExecAction are not meant to be subclassed, that’s why they are  
annotated with @HasInternalProtocol.

---

<div class="post-metadata">

**Author:** ![Horcrux7](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/horcrux7/32/96_2.png) [@Horcrux7](https://discuss.gradle.org/u/Horcrux7)\
**Post date:** [September 2, 2016, 2:39pm UTC](https://discuss.gradle.org/t/best-practice-to-handle-frequently-api-signature-changes/19338/6 "2016-09-02T14:39:12Z")

</div>

This APIs can I only call but we want implement it. I need to implement it that the user can write Gradle typical scripts like:

```
mytask {
    from( "${buildDir}/data") {
       inlicude '*.abc'
    }
}

```

Also If I use the CopySpec from project I need internal API to use it. For example iterate.

If you mean that a task should not implements CopySpec why then such many buildin task has implement it like jar, zip, tar, …

Project.exec: We was not able to call it from Java. A closure is simple from Groovy but not from Java.

---

<div class="post-metadata">

**Author:** ![st\_oehme](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/st_oehme/32/5249_2.png) [@st\_oehme](https://discuss.gradle.org/u/st_oehme)\
**Post date:** [September 2, 2016, 3:57pm UTC](https://discuss.gradle.org/t/best-practice-to-handle-frequently-api-signature-changes/19338/7 "2016-09-02T15:57:27Z")

</div>

> [@Horcrux7](#):
>
> I need to implement it that the user can write Gradle typical scripts like:

You don’t need to implement it for that. Here’s a rough sketch how to use CopySpec in a custom task:

```gradle
task mytask(type: MyTask) {
  outputDir = "$buildDir/foo"
  content {
    from 'dummy'
  }
}

class MyTask extends DefaultTask {
  CopySpec content
  def outputDir

  MyTask() {
    content = project.copySpec()
  }

  @TaskAction
  def doStuff() {
    project.copy {
      into outputDir
      with content
    }
  }

  def content(Action<? super CopySpec> config) {
    config.execute(content)
  }
}

```

> [@Horcrux7](#):
>
> Also If I use the CopySpec from project I need internal API to use it. For example iterate.

Why iterate? You can execute the copy as shown in the custom task above. Then you can invoke some other tool on the copied directory, like invoking `tar`.

> [@Horcrux7](#):
>
> If you mean that a task should not implements CopySpec why then such many buildin task has implement it like jar, zip, tar, …

Legacy 🙂 We wouldn’t do it like that again, but now users are used to having the `from` method directly on the `Jar` task, so it will be hard to change.

> [@Horcrux7](#):
>
> Project.exec: We was not able to call it from Java. A closure is simple from Groovy but not from Java.

Calling a Closure from Java isn’t that hard. It’s definitely not a blocker. And we are adding `Action` methods for all `Closure` methods in recent Gradle versions. Here’s how to call a closure method:

```gradle
project.exec(new Closure(null) {
  public void doCall(ExecSpec spec) {
    // your code here
  }
}

```

---

<div class="post-metadata">

**Author:** ![st\_oehme](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/st_oehme/32/5249_2.png) [@st\_oehme](https://discuss.gradle.org/u/st_oehme)\
**Post date:** [September 3, 2016, 9:26am UTC](https://discuss.gradle.org/t/best-practice-to-handle-frequently-api-signature-changes/19338/8 "2016-09-03T09:26:44Z")

</div>

Please don’t get me wrong by the way. I’m sure there are things that are only possible with internal Apis at the moment. We should talk about those use cases and then publish the necessary minimal Api.

But what I saw so far in the plugin could definitely be done with far less internals. So the first step should be refactoring to use as little internals as possible. The second step would be to look at the remaining internals together to see what needs to be public.

---

<div class="post-metadata">

**Author:** ![Horcrux7](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/horcrux7/32/96_2.png) [@Horcrux7](https://discuss.gradle.org/u/Horcrux7)\
**Post date:** [September 5, 2016, 6:35am UTC](https://discuss.gradle.org/t/best-practice-to-handle-frequently-api-signature-changes/19338/9 "2016-09-05T06:35:36Z")

</div>

The “content” variable look not like typical Gradle code. The calling of a copy() method will produce a very large performance decrement. It is very bad practice to copy hundreds of megabytes to receive the directory structure and then delete it.

But we have lost touch the original question. It there any good option to handle the version of plugins depends on the Gradle version?

---

<div class="post-metadata">

**Author:** ![st\_oehme](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/st_oehme/32/5249_2.png) [@st\_oehme](https://discuss.gradle.org/u/st_oehme)\
**Post date:** [September 5, 2016, 11:18am UTC](https://discuss.gradle.org/t/best-practice-to-handle-frequently-api-signature-changes/19338/10 "2016-09-05T11:18:43Z")

</div>

> [@Horcrux7](#):
>
> The “content” variable look not like typical Gradle code

I don’t know what you mean. The distribution plugin does the same and this is how all copying/bundling tasks should look like.

> [@Horcrux7](#):
>
> But we have lost touch the original question. It there any good option to handle the version of plugins depends on the Gradle version?

As I said, you shouldn’t need to. Only use public APIs and tell us about the specific situations that don’t work, so we can fix them. How is Gradle supposed to get better if we don’t know the things that don’t work for you?

I’ve forwarded the bundling use case to the team. I’m sure we can make something happen there.
