Hi,I've been trying to migrate the Asciidoctor Gradle plugin to the new software model as it seems like a really good fit. The new 2.11 features have certainly helped, but it looks like the binary mechanism seems too limited. I apologise in advance for the lengthy email, but I think it's important to get as much detail in as possible.The perceived limitations of the software model could be due to a lack of understanding on my part, so I want to give you an idea of what I want to achieve and how I'd like to achieve it. Perhaps it will turn out that there are no such limitations, but perhaps not.The way I want the Asciidoctor plugin to work is quite straightforward. You have multiple components, perhaps representing a user guide, a reference guide, some HOWTOs, etc. Each component has one and only one associated Asciidoc source set. The binaries produced for a component can be one or more of things like HTML, PDF, and Docbook. This model should then produce tasks of the formbuild<Component>AsciidocAs<Binary>For example, `buildDocsAsciidocAsPdf`. I'd also like to allow other plugin or build authors to register additional binary types (Latex perhaps).
From the user's perspective, they should just need to configure which binaries to produce for a component, and possibly configure the location(s) of the source files. I'd like to do this via a DSL like this:model {docs(AsciidocDocument) {binaries {pdf(AsciidocPdf)}}}
My current attempts to achieve the above have hit a few problems. My primary concern is that it seems impossible to selectively enable or disable the binary types for a component. As I understand it, all registered binaries are created for all components. Is that correct? If so, I feel this needs to change.The other significant issue relates to the binary definitions themselves. I'm trying to use managed objects, but the normal rules of Java classes don't apply. Every binary has a single property representing the backend, e.g. "pdf", "html5", etc. These are kind of internal to Asciidoctor. I prefer types in this case. But the following model doesn't work:@Managedinterface AsciidoctorBinary extends BinarySpec {String getBackend()}@Managedabstract AsciidocPdf implements AsciidoctorBinary {String getBackend() { return "pdf" }}
The problem may have been due to trying to incorporate internal views for both of these as well, but I need an internal view so that I can set the component for the binary. I need to do that so I can generate tasks with the required names.
Would it not make sense to allow specialisations of managed interfaces? Not everything should be configurable to the user.So, does this all seem reasonable? Is it currently achievable while still using managed objects? If not, could the model be enhanced to make it easier to achieve what I want?Thanks,Peter
--
You received this message because you are subscribed to the Google Groups "gradle-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to gradle-dev+...@googlegroups.com.
To post to this group, send email to gradl...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/gradle-dev/CANBeYQQ2pztc%3DKvcEM21aCYJT%2B_yC2dx%2BgwVV7VncLE2iL%3DATg%40mail.gmail.com.
For more options, visit https://groups.google.com/d/optout.
For example, `buildDocsAsciidocAsPdf`. I'd also like to allow other plugin or build authors to register additional binary types (Latex perhaps).You can get the task names created through the binary's task collection, which I thing would get you something close to what you desired, and at worst still follows the convention of other binary tasks.binary.getTasks().taskName("build")
I'm not aware of a way to do this, but my impression has been that they expect the properties of the component to determine which binaries are created (such as how adding different java platforms triggers different jar binaries). Maybe something like this. However, I do see your desired approach being helpful.docs(AsciidocDocument) {format 'pdf'format 'html'}
@Managedabstract AsciidocPdf implements AsciidoctorBinary {String getBackend() { return "pdf" }}Maybe there are other properties you left out for brevity, but could you alternately have only one base type with the "String getBackend()" or skip that property by just relying on the type?
From the user's perspective, they should just need to configure which binaries to produce for a component, and possibly configure the location(s) of the source files. I'd like to do this via a DSL like this:model {docs(AsciidocDocument) {binaries {pdf(AsciidocPdf)}}}I'm not aware of a way to do this, but my impression has been that they expect the properties of the component to determine which binaries are created (such as how adding different java platforms triggers different jar binaries). Maybe something like this. However, I do see your desired approach being helpful.docs(AsciidocDocument) {format 'pdf'format 'html'}
My current attempts to achieve the above have hit a few problems. My primary concern is that it seems impossible to selectively enable or disable the binary types for a component. As I understand it, all registered binaries are created for all components. Is that correct? If so, I feel this needs to change.The other significant issue relates to the binary definitions themselves. I'm trying to use managed objects, but the normal rules of Java classes don't apply. Every binary has a single property representing the backend, e.g. "pdf", "html5", etc. These are kind of internal to Asciidoctor. I prefer types in this case. But the following model doesn't work:@Managedinterface AsciidoctorBinary extends BinarySpec {String getBackend()}@Managedabstract AsciidocPdf implements AsciidoctorBinary {String getBackend() { return "pdf" }}Maybe there are other properties you left out for brevity, but could you alternately have only one base type with the "String getBackend()" or skip that property by just relying on the type?
Note that since your plugin would create the "pdf" binary, you don't need to provide the type anymore. Similarily, you *could* decide that your plugin would also create the top-level "docs" component too. However, if you do so, be aware that you should probably choose an alternate, less potential conflicting name. You might wonder why creating all binaries if in the end, the user only wants one. There are several things to this:- is the creation of a binary model element cheap? It should be cheap, otherwise, it probably means you are initializing too much in there (heavyweight operations should be deferred at execution time)
- do you want the user to see the potential export formats in the model report? If so, yes, create all binaries, even if they are not used.
- it gives you the opportunity to create a "hat" task that creates all exports in a single call- still gives you the opportunity to call a single export task
@Managedinterface AsciidoctorBinaryInternal extends AsciidoctorBinary {String getBackend()void setBackend(String backend)}
@Defaultsvoid createExportBinaries(AsciidocDocument docs) {docs.binaries.create("pdf", AsciidoctorBinaryInternal) {binary.backend = 'pdf'}}@Modelvoid docs(AsciidocDocument docs) {}}Does that help?
This is where I disagree a little. I think that the types of binaries should be part of the component definition. If someone executes `build` or `assemble` for my build, they should get the standard binaries for that build. HTML and PDF are fairly common, Docbook less so. But Docbook should probably still be registered as a binary type, just not built by default.
Also, you need to factor in the time that it takes to generate each binary. In general, I disable the PDF in the build until I want to publish it because it slows the process down too much.As I see it, the current model seems to assume that the plugin defines the binary types for a component, whereas I think the model should be extended to allow binary types as part of the component definition. If that makes sense.@Managedinterface AsciidoctorBinaryInternal extends AsciidoctorBinary {String getBackend()void setBackend(String backend)}This is similar to what I tried, but I only put the setter in the internal view. I'm not sure I really want to make the getter private.
@Defaultsvoid createExportBinaries(AsciidocDocument docs) {docs.binaries.create("pdf", AsciidoctorBinaryInternal) {binary.backend = 'pdf'}}@Modelvoid docs(AsciidocDocument docs) {}}Does that help?I understand that this is an option, but having different types representing different types of binary makes the most sense to me. This may just be a limitation of the way managed objects work, but I'd like the team to think about whether it's possible to support fixed implementations of methods on derived managed types. I hope I'm using the vocabulary correctly :)
Sure, but it's totally up-to-you to attach a binary to the build task or not. That is to say that by default, you might want to attach only HTML and PDF, but it's really up to you, as the plugin author, to create the dependency between the build task and the binary. If you don't attach *all* binaries to the build task, then, you need to provide the user with a way to do it (could be by declaring the export formats explicitly).
The question you have to ask yourself is if the user needs to access the name of the backend. As far as I see it, a user doesn't need to, so can be private I think.
@Managedinterface AsciidoctorPdf extends AsciidoctorBinary {double getCompression()void setCompression(double d)}@Managedabstract class AsciidoctorPdfInternal implements AsciidoctorPdf, AsciidoctorBinaryInternal {String getBackend() { 'pdf' }}
Sure, but it's totally up-to-you to attach a binary to the build task or not. That is to say that by default, you might want to attach only HTML and PDF, but it's really up to you, as the plugin author, to create the dependency between the build task and the binary. If you don't attach *all* binaries to the build task, then, you need to provide the user with a way to do it (could be by declaring the export formats explicitly).The answer by Andreas on this thread:suggests that all binaries are attached to `assemble` by default. Is that not correct?
I was hoping to avoid having a `formats` property on the component, but there seems to be no way round that.The question you have to ask yourself is if the user needs to access the name of the backend. As far as I see it, a user doesn't need to, so can be private I think.I don't yet know. But regardless, should the software model support public getters with internal setters? I'm guessing so.
@Managedinterface AsciidoctorPdf extends AsciidoctorBinary {double getCompression()void setCompression(double d)}@Managedabstract class AsciidoctorPdfInternal implements AsciidoctorPdf, AsciidoctorBinaryInternal {String getBackend() { 'pdf' }}I tried something like this, but it didn't work. If Gradle reports an error for the above, should I raise an issue for it?
Anyway, thanks for the feedback. I think I have enough to do a bit more experimentation.Peter
--
You received this message because you are subscribed to the Google Groups "gradle-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to gradle-dev+...@googlegroups.com.
To post to this group, send email to gradl...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/gradle-dev/CANBeYQSYonPWjw0xorq7XO7-Uq1-y4tDf6p02yEN42SzDwSjVA%40mail.gmail.com.
The answer by Andreas on this thread:suggests that all binaries are attached to `assemble` by default. Is that not correct?It is correct if you use the `@BinaryTasks` annotation. But nothing prevents you from wiring yourself without this annotation.
@Managedabstract class AsciidoctorPdfInternal implements AsciidoctorPdf, AsciidoctorBinaryInternal {String getBackend() { 'pdf' }}I tried something like this, but it didn't work. If Gradle reports an error for the above, should I raise an issue for it?This works on master (I tested it), but yeah, it's in heavy development, 2.12 will likely be a huge step forward in unifying/cleaning up the way the software model is built.
--
You received this message because you are subscribed to the Google Groups "gradle-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to gradle-dev+...@googlegroups.com.
To post to this group, send email to gradl...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/gradle-dev/CANBeYQRBWB0m3UnbtqfGUtOFV19S8j9dDZONXoAP-8HHPwnaMQ%40mail.gmail.com.