Using Gradle home…C:\gradle-1.0-milestone-6 > Using Gradle user home…D:\Users\bgde.gradle > :hello > Message From Greet task: Hello Gradle! > Message From Hello task: Hello Gradle!
Using Gradle home…C:\gradle-1.0-milestone-8a > Using Gradle user home…D:\Users\bgde.gradle > :hello > Message From Greet task: Hello world! > Message From Hello task: Hello world!
Unless I have missed something, I would expect to always get “Hello Gradle!” since this value is explicitly configured and should override the convention mapping value.
I can’t find that change that introduced this, but the new version is the correct behaviour.
Convention mappings are per property, not per instance variable. So the convention mapping applies to ‘Hello.message’ property which is never set explicitly, therefore the convention mapping is never invalidated. The m6 behaviour surprises me.
This is a rather strange setup with these task classes. I’m interested in what your real use case is as I see the “new” behaviour as correct, but I don’t know your case.
Also, you should avoid using ‘ConventionTask’. It’s better to use ‘DefaultTask’ and cast to ‘IConventionAware’ if you need to use convention mappings from Java. You don’t need to extend ‘ConventionTask’ to get convention mapping features.
The change should be introduced in Gradle 1.0-m8 because I just tried it with m7 and it works the same as in m6.
In my setup I rely on being able to extend a task and re-use its core functionality while modifying some bits by overriding certain properties. I also make extensive use of configuration via convention mapping to configure all tasks that inherit from certain base type. However with Gradle 1.0-m8 I’m not able to override values set via the convention mapping by merely overriding the getters
The Gradle 1.0-m6 AbstractClassGenerator implementation took care to override setters if they are already defined, the new Gradle 1.0-m8a implementation seems to fail to do so because of this piece of code:
for (MetaBeanProperty property : settableProperties) {
} else if (conventionProperties.contains(property)) {
for (MetaMethod method : methodsForProperty) {
builder.overrideSetMethod(property, method);
}
}
}
While testing this I noticed that setter is added 2nd time even for convention properties, because ‘methods’ map is keyed by the setter name e.g. “setMessage” while property name is “message” and ‘methodsForProperty’ is always empty.
Previously, the a convention had not been invalidated it would only be used if the default instance value was null or an empty map/collection. This was a bug. If a convention mapping has been set and has not been invalidated, it should always be returned no matter what the default instance value is.
So to be clear, the m6 and earlier behaviour was a bug and m8 has the correct behaviour.
Everything makes sense now, you are right that the current behavior is the correct one, but maybe this should be documented somewhere (if it is not already). After working with Gradle for some time, I figured that tasks must read their properties via the getters to trigger convention mapping lookup, and somehow I was convinced that if the getter returns ‘null’ then the convention value would be used. After reading through the code and seeing how this is implemented it is now clear to me that invalidation is done via the generated setter. Probably this is considered as internal implementation that should not be exposed in the user docs, but I think that if it is documented it would help people rather than confuse them. I will also fix all our tasks to extend from the DefaultTask instead of the ConventionTask as you suggested. By the way, is there a clean approach to make any instance convention aware, so far I have been using:
So it turns out the situation gets more complex. It turns out you’re right in a sense.
Previously a convention mapping would only apply if the decorated setter had not been explicitly set and if the “natural” value was not “defaultish”. The idea of of not applying convention mappings when the “natural” value is not “defaultish” is to work around some awkwardness with convention mappings and collections. The change that occurred in m7 is that we only treat non empty collections as non “defaultish” where as previously we also interpreted ‘null’ in this manner.
As we work towards making convention mapping public, we are trying to simplify it to remove this whole “defaultish” business as it complicates things too far. But, if you have a good reason for keeping the existing behaviour now would be a good time to raise it.
I was surprised to see convention mapping in the docs because it’s not public, I’ll remove it. Thanks.
There’s no public API for creating enhanced objects at this time, except for extensions.
For example - Gradle’s DefaultOSGiManifest - currently instances created via project.osgiManifest() are not convention aware, but I would like them to be. The reason is, because we are having a special task that switches project’s version to a release one (similar to ‘:releaseVersion’ task in Gradle’s own build), however since osgi manifests are created at project evaluation time they might read project’s ‘version’ before it’s being overridden by any ‘release’ task (actually we are detecting the presence of any ‘release’ task by examining start parameters). If osgi manifest is convention aware, this is easily fixed by using convention mapping:
I might switch to that, unfortunately the DefaultOsgiManifest does not always access the ‘version’ via the getter but happens to reference it via the property on one place (in setAnalyzerProperties), but this is a different topic and I will address it separately.
Thank you very much for helping me figure the property issue out, I will make sure the setters are always called to invalidate any convention values, also try to create new task types and avoid extending existing ones where possible.