I have a task defined with `@OutputDirectory`. Th...
# plugin-development
s
I have a task defined with
@OutputDirectory
. This task performs XJC generation of JAXB binding classes (the java files) into said directory. Between branches we changed the package in which these classes are generated. What we've noticed is that when switching branches the output from the "other" branch is not removed and we end up with compile errors. Annoying but understandable. However, what I don't understand is that even
clean
does not help the situation. The only thing that works is
clean
+
--no-build-cache
. Any thoughts on why that is? Would switching to
@OutputFiles
help with that in any way? The difference (to me anyway) is not immediately obvious from their Javadocs
The only thing that works is
clean
+
--no-build-cache
To clarify, this won't work (get compile errors from the old sources):
Copy code
gradlew clean compile
Instead, I have to use:
Copy code
gradlew clean compile --no-build-cache
(for transparency, this uses the Ant XJC task under the covers as do all XJC plugins)
a
The thing is, that Gradle doesn't know that package is "an input", that can change. So it thinks that no input has change and thus it gets old classes from build cache. I recommend you add package as a task input.
s
It does
At least assuming it really truly does check the content of input files
The way XJC works, there is a "binding" file that controls some aspects of the generation. One of the items in there is the package name
That binding file is a file input to the task
a
Note that the best way to run "without cache" is with
--rerun
(reruns just one task) or with
--rerun-tasks
(reruns all in the chain). So just
Copy code
gradlew compile --rerun (or  probably --rerun-tasks if problematic task is not compile task)
How is that input defined?
s
Copy code
@InputFile
    @PathSensitive( PathSensitivity.RELATIVE )
    RegularFileProperty getXjcBindingFile() {
        return xjcBindingFile
    }
and
Copy code
@OutputDirectory
    @PathSensitive( PathSensitivity.RELATIVE )
    DirectoryProperty getOutputDirectory() {
        return outputDirectory
    }
I think path-sensitive is not relevant for outputs, but 🤷🏼
(again the Javadoc is silent about that)
a
Yeah, it's important just for inputs
s
So can you confirm that
@InputFile
should see that the content of said file has changed?
E.g...
Copy code
<schemaBindings>
    <package name="org.hibernate.boot.jaxb.mapping.spi" />
</schemaBindings>
So if I change that
<package name="..."/>
entry,
@InputFile
is supposed to be sensitive to that right?
• This will cause the task to be considered out-of-date when the file path or contents have changed.
a
Yes, when using
@InputFile
it will snapshot the file and if content changes the task will be out-of-date
My guess is that that after you change the package task gets out-of-date but old classes are still there and gets packaged in the build cache result.
s
I can understand why the old sources are still there. That's a function of not being able to make this an incremental task
Another option I am playing around with is to add a property to always delete the contents of that directory as the first step in the task action. That's "ok" right?
Well, actually maybe I can still make this incremental
Well, actually maybe I can still make this incremental
Ignore that. Incremental (TaskInputs) rely on the inputs
Deleting up front is the only real option I see
a
is to add a property to always delete the contents of that directory as the first step in the task action
If your task is not incremental, then you always want to compile all sources again, so that makes sense
1
s
If no imputs changed, the action is never executed... so I probably don't even need a property - just do it
👍 1
Thanks for the help
🙌 1
a
Np, and if performance is a problem, then creating an incremental task is an option to explore. But it's more complicated of course, and you have to handle cases like package change where you need to recompile all files again.
s
Right. The "problem" is that I really want this incremental relative to the OUTPUT, not the input
And that's just not an option as far as I can tell
Basically XSD + binding-file => XJC => multiple Java files
And the
=> XJC =>
bit is literally a call to an Ant task
a
Oh, I see. Yeah, I guess this is harder then. You would need to read XSD and understand how XSD maps to Java file. And if XSD changes figure out what Java files you need to delete.
s
Yes, really someone woudl really finally need to spend time detangling that Ant task to proper design
Oracle/Jakarta won't
😬 1
Hmm, apparently Maven may have 😄
I'll look at "learning from" their code
a
my preference for tasks that generate code is to generate the files into a temporary dir, and then use
FileSystemOperations#sync
to transfer the generated files from the temp dir into the actual output directory. Using sync is best, because it means you won't end up with outdated files.
Although, now that I write it down, I realise that if the generator doesn't delete the output the temporary dir would still need to be deleted anyway! Maybe there was another reason why I did it that way... 🤔