Compare commits
10 commits
001a4e3177
...
b28af424ff
Author | SHA1 | Date | |
---|---|---|---|
b28af424ff | |||
664890c186 | |||
017df58da6 | |||
9784dc72d7 | |||
324d8498ec | |||
d88bc058fa | |||
d616184a2e | |||
7ea6ca3d65 | |||
74c0cf6f65 | |||
553c397856 |
139 changed files with 136816 additions and 103 deletions
16
build.gradle
16
build.gradle
|
@ -1,16 +1,14 @@
|
||||||
|
|
||||||
group = 'org.xbib.gradle.plugin'
|
|
||||||
|
|
||||||
wrapper {
|
wrapper {
|
||||||
gradleVersion = libs.versions.gradle.get()
|
gradleVersion = libs.versions.gradle.get()
|
||||||
distributionType = Wrapper.DistributionType.ALL
|
distributionType = Wrapper.DistributionType.BIN
|
||||||
}
|
}
|
||||||
|
|
||||||
ext {
|
ext {
|
||||||
user = 'jprante'
|
name = rootProject.name
|
||||||
name = 'gradle-plugins'
|
|
||||||
description = 'Gradle plugins'
|
description = 'Gradle plugins'
|
||||||
inceptionYear = '2021'
|
inceptionYear = '2021'
|
||||||
|
user = 'jprante'
|
||||||
url = 'https://github.com/' + user + '/' + name
|
url = 'https://github.com/' + user + '/' + name
|
||||||
scmUrl = 'https://github.com/' + user + '/' + name
|
scmUrl = 'https://github.com/' + user + '/' + name
|
||||||
scmConnection = 'scm:git:git://github.com/' + user + '/' + name + '.git'
|
scmConnection = 'scm:git:git://github.com/' + user + '/' + name + '.git'
|
||||||
|
@ -20,11 +18,3 @@ ext {
|
||||||
licenseName = 'The Apache License, Version 2.0'
|
licenseName = 'The Apache License, Version 2.0'
|
||||||
licenseUrl = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
|
licenseUrl = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
|
||||||
}
|
}
|
||||||
|
|
||||||
subprojects {
|
|
||||||
apply plugin: 'java-library'
|
|
||||||
apply from: rootProject.file('gradle/ide/idea.gradle')
|
|
||||||
apply from: rootProject.file('gradle/compile/java.gradle')
|
|
||||||
apply from: rootProject.file('gradle/test/junit5.gradle')
|
|
||||||
apply from: rootProject.file('gradle/repositories/maven.gradle')
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,21 +1,24 @@
|
||||||
plugins {
|
plugins {
|
||||||
id 'java-gradle-plugin'
|
|
||||||
alias(libs.plugins.publish)
|
alias(libs.plugins.publish)
|
||||||
}
|
}
|
||||||
|
|
||||||
apply plugin: 'java-gradle-plugin'
|
|
||||||
apply plugin: 'com.gradle.plugin-publish'
|
apply plugin: 'com.gradle.plugin-publish'
|
||||||
|
|
||||||
apply from: rootProject.file('gradle/compile/groovy.gradle')
|
apply from: rootProject.file('gradle/compile/groovy.gradle')
|
||||||
|
apply from: rootProject.file('gradle/test/junit5.gradle')
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
api gradleApi()
|
|
||||||
implementation libs.asciidoctorj
|
implementation libs.asciidoctorj
|
||||||
implementation libs.jruby
|
implementation libs.jruby
|
||||||
testImplementation libs.spock.core
|
implementation libs.asm
|
||||||
testImplementation libs.jsoup
|
implementation libs.asm.commons
|
||||||
|
implementation libs.asm.util
|
||||||
|
testImplementation platform(testLibs.spock.bom)
|
||||||
|
testImplementation testLibs.spock.core
|
||||||
|
testImplementation testLibs.jsoup
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
if (project.hasProperty('gradle.publish.key')) {
|
if (project.hasProperty('gradle.publish.key')) {
|
||||||
gradlePlugin {
|
gradlePlugin {
|
||||||
website = 'https://xbib.org/joerg/gradle-plugins/src/branch/main/gradle-plugin-asciidoctor'
|
website = 'https://xbib.org/joerg/gradle-plugins/src/branch/main/gradle-plugin-asciidoctor'
|
||||||
|
@ -32,3 +35,4 @@ if (project.hasProperty('gradle.publish.key')) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
|
@ -1,2 +1,2 @@
|
||||||
name = gradle-plugin-asciidoctor
|
name = gradle-plugin-asciidoctor
|
||||||
version = 3.0.2
|
version = 3.1.0
|
||||||
|
|
|
@ -174,5 +174,7 @@ class AsciidoctorExtensions {
|
||||||
)
|
)
|
||||||
|
|
||||||
new GroovyShell(new Binding(), config)
|
new GroovyShell(new Binding(), config)
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
201
gradle-plugin-c/LICENSE.txt
Normal file
201
gradle-plugin-c/LICENSE.txt
Normal file
|
@ -0,0 +1,201 @@
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright {yyyy} {name of copyright owner}
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
8
gradle-plugin-c/NOTICE.txt
Normal file
8
gradle-plugin-c/NOTICE.txt
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
|
||||||
|
This work is derived from
|
||||||
|
|
||||||
|
https://github.com/jjohannes/plain-c
|
||||||
|
|
||||||
|
as of January 14, 2024
|
||||||
|
|
||||||
|
(Apache License 2.0)
|
81
gradle-plugin-c/README.md
Normal file
81
gradle-plugin-c/README.md
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
# C - Gradle plugin
|
||||||
|
|
||||||
|
An experimental plugin for customized **C** development with Gradle.
|
||||||
|
It showcases how to use tasks that are provided by Gradle core for **C** development,
|
||||||
|
such as `org.gradle.language.c.tasks.CCompile`, directly as part of a custom build system.
|
||||||
|
This can make sense in a highly customized Gradle build system where specific (and multiple) **C** compilation/assembling/linking
|
||||||
|
steps are part of a larger build pipeline with other custom Gradle tasks.
|
||||||
|
|
||||||
|
The plugin provides an extension called `c` to:
|
||||||
|
|
||||||
|
- Set the `targetPlatform` property of a task to a generic platform via `targetPlatform.set(c.platform())`
|
||||||
|
- Set the `toolChain` property of a task to a locally installed tool via `toolChain.set(c.localTool("version", "/path/to/tool", ".o"))`
|
||||||
|
- Set the `toolChain` property of a task to a tool located in a ZIP file in a Maven repository via `toolChain.set(c.repositoryTool("group", "name", "version", "path/in/zip", ".o"))`
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
```
|
||||||
|
plugins {
|
||||||
|
id 'org.xbib.gradle.plugin.c"
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register<CCompile>("compileC") {
|
||||||
|
toolChain.set(c.localTool("14.0.0", "/usr/bin/clang", ".o"))
|
||||||
|
targetPlatform.set(c.platform())
|
||||||
|
|
||||||
|
source(layout.projectDirectory.files("src/c").asFileTree)
|
||||||
|
includes(layout.projectDirectory.files("src/headers"))
|
||||||
|
compilerArgs.add("-S")
|
||||||
|
|
||||||
|
objectFileDir.set(layout.buildDirectory.dir("out/o"))
|
||||||
|
}
|
||||||
|
|
||||||
|
val compileC2 = tasks.register<CCompile>("compileC2") {
|
||||||
|
toolChain.set(c.repositoryTool("com.example", "vendor-x", "1.2", "bin/xcc", ".src"))
|
||||||
|
targetPlatform.set(c.platform())
|
||||||
|
|
||||||
|
source(layout.projectDirectory.files("src/c-special").asFileTree)
|
||||||
|
includes(layout.projectDirectory.files("src/headers"))
|
||||||
|
|
||||||
|
objectFileDir.set(layout.buildDirectory.dir("out/src"))
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register<Assemble>("assembleC2") {
|
||||||
|
toolChain.set(c.repositoryTool("com.example", "vendor-x", "1.2", "bin/xas", ".o"))
|
||||||
|
targetPlatform.set(c.platform())
|
||||||
|
|
||||||
|
source(compileC2.map { it.objectFileDir.asFileTree })
|
||||||
|
includes(layout.projectDirectory.files("src/headers"))
|
||||||
|
assemblerArgs = listOf("-D")
|
||||||
|
|
||||||
|
objectFileDir = layout.buildDirectory.dir("out/o2").get().asFile
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## How does this work?
|
||||||
|
|
||||||
|
In order to fully configure one of the native tasks of Gradle (such as `org.gradle.language.c.tasks.CCompile`),
|
||||||
|
you require a **NativeToolchain** (instance of `org.gradle.nativeplatform.toolchain.NativeToolChain`)
|
||||||
|
and a **TargetPlatform** (instance of `org.gradle.nativeplatform.platform.NativePlatform`).
|
||||||
|
Gradle's own [native languages plugins](https://docs.gradle.org/current/userguide/plugin_reference.html#native_languages)
|
||||||
|
pre-configure these task properties through the so-called _native toolchain registry_.
|
||||||
|
This allows, for example, to compile the same code for multiple target platforms with different tools in the same build.
|
||||||
|
This is great, if this is what you need. However, in a scenario where we do not care about this feature and would rather register
|
||||||
|
tasks directly, we miss flexibility as the _native toolchain registry_ in Gradle is not very flexible right now and makes it difficult or impossible to register
|
||||||
|
your own tools (and it has not been improved for several years).
|
||||||
|
|
||||||
|
The [CExtension.java](src/main/java/org/xbib/gradle/plugin/c/CExtension.java) class now allows you to
|
||||||
|
set the `targetPlatform` and `toolChain` properties of any task without the _native toolchain registry_.
|
||||||
|
It does so by utilising internal Gradle APIs to:
|
||||||
|
- Construct a generic `NativePlatform` object. This is the same everywhere and will be ignored during tool selection.
|
||||||
|
In our scenario, we do not care about different platforms.
|
||||||
|
- Construct a `NativeToolChain` instance based on a concrete executable. Either by providing a concrete path to
|
||||||
|
an executable (`localTool(...)`) or coordinates pointing at a ZIP file in a Maven repository (`repositoryTool(...)`).
|
||||||
|
In the latter case, Gradle's dependency management is used to find, download, extract and cache the tool.
|
||||||
|
|
||||||
|
Behind the custom `NativeToolChain` implementation is a custom `PlatformToolProvider` implementation and extensions of
|
||||||
|
Gradle's `NativeCompiler` implementations. This allows for more customization and extension of Gradle's functionality
|
||||||
|
in this area. This uses a lot of APIs from `internal` packages. Which, however, have not been touched for years.
|
||||||
|
|
||||||
|
This showcase could help to determine which APIs in this area should probably be public.
|
||||||
|
|
30
gradle-plugin-c/build.gradle
Normal file
30
gradle-plugin-c/build.gradle
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
plugins {
|
||||||
|
alias(libs.plugins.publish)
|
||||||
|
}
|
||||||
|
|
||||||
|
apply plugin: 'com.gradle.plugin-publish'
|
||||||
|
|
||||||
|
apply from: rootProject.file('gradle/compile/groovy.gradle')
|
||||||
|
apply from: rootProject.file('gradle/test/junit5.gradle')
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
api gradleApi()
|
||||||
|
testImplementation gradleTestKit()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (project.hasProperty('gradle.publish.key')) {
|
||||||
|
gradlePlugin {
|
||||||
|
website = 'https://xbib.org/joerg/gradle-plugins/src/branch/main/gradle-plugin-c'
|
||||||
|
vcsUrl = 'https://xbib.org/joerg/gradle-plugins'
|
||||||
|
plugins {
|
||||||
|
cPlugin {
|
||||||
|
id = 'org.xbib.gradle.plugin.c'
|
||||||
|
implementationClass = 'org.xbib.gradle.plugin.c.CPlugin'
|
||||||
|
version = project.version
|
||||||
|
description = 'Gradle plugin for build with C'
|
||||||
|
displayName = 'Gradle Plugin for build with C'
|
||||||
|
tags.set(['c'])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
2
gradle-plugin-c/gradle.properties
Normal file
2
gradle-plugin-c/gradle.properties
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
name = gradle-plugin-c
|
||||||
|
version = 3.1.0
|
|
@ -0,0 +1,59 @@
|
||||||
|
package org.xbib.gradle.plugin.c;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import org.gradle.api.artifacts.Configuration;
|
||||||
|
import org.gradle.api.artifacts.ConfigurationContainer;
|
||||||
|
import org.gradle.api.artifacts.dsl.DependencyHandler;
|
||||||
|
import org.gradle.api.attributes.Attribute;
|
||||||
|
import org.gradle.api.file.FileCollection;
|
||||||
|
import org.gradle.api.model.ObjectFactory;
|
||||||
|
import org.gradle.nativeplatform.platform.NativePlatform;
|
||||||
|
import org.gradle.nativeplatform.platform.internal.DefaultArchitecture;
|
||||||
|
import org.gradle.nativeplatform.platform.internal.DefaultNativePlatform;
|
||||||
|
import org.gradle.nativeplatform.platform.internal.DefaultOperatingSystem;
|
||||||
|
import org.gradle.nativeplatform.toolchain.NativeToolChain;
|
||||||
|
import org.xbib.gradle.plugin.c.toolchain.CNativeToolChain;
|
||||||
|
|
||||||
|
public abstract class CExtension {
|
||||||
|
|
||||||
|
static final Attribute<Boolean> EXTRACTED_TOOLS_ATTRIBUTE = Attribute.of("extracted-tools", Boolean.class);
|
||||||
|
|
||||||
|
static final NativePlatform GENERIC_NATIVE_PLATFORM = new DefaultNativePlatform(
|
||||||
|
"generic_platform",
|
||||||
|
new DefaultOperatingSystem("generic_os"),
|
||||||
|
new DefaultArchitecture("generic_arch")
|
||||||
|
);
|
||||||
|
|
||||||
|
public NativeToolChain localTool(String version, String location, String objectFileExtension) {
|
||||||
|
File tool = new File(location);
|
||||||
|
return getObjects().newInstance(CNativeToolChain.class, version, tool.getName(),
|
||||||
|
getObjects().fileCollection().from(tool), objectFileExtension);
|
||||||
|
}
|
||||||
|
|
||||||
|
public NativeToolChain repositoryTool(String group,
|
||||||
|
String name,
|
||||||
|
String version,
|
||||||
|
String locationInZip,
|
||||||
|
String objectFileExtension) {
|
||||||
|
Configuration toolConfiguration = getConfigurations().detachedConfiguration(
|
||||||
|
getDependencies().create(group + ":" + name + ":" + version + "@zip"));
|
||||||
|
FileCollection tool = toolConfiguration.getIncoming().artifactView(a ->
|
||||||
|
a.getAttributes().attribute(EXTRACTED_TOOLS_ATTRIBUTE, true))
|
||||||
|
.getFiles().getAsFileTree().filter(file -> file.getPath().endsWith(locationInZip));
|
||||||
|
return getObjects().newInstance(CNativeToolChain.class, name, version, tool, objectFileExtension);
|
||||||
|
}
|
||||||
|
|
||||||
|
public NativePlatform platform() {
|
||||||
|
return GENERIC_NATIVE_PLATFORM;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
abstract protected ObjectFactory getObjects();
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
abstract protected ConfigurationContainer getConfigurations();
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
abstract protected DependencyHandler getDependencies();
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
package org.xbib.gradle.plugin.c;
|
||||||
|
|
||||||
|
import org.gradle.api.Plugin;
|
||||||
|
import org.gradle.api.Project;
|
||||||
|
import org.xbib.gradle.plugin.c.toolchain.transform.ExtractZipTransform;
|
||||||
|
import static org.gradle.api.artifacts.type.ArtifactTypeDefinition.ARTIFACT_TYPE_ATTRIBUTE;
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
abstract public class CPlugin implements Plugin<Project> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void apply(final Project project) {
|
||||||
|
// Register extension
|
||||||
|
project.getExtensions().create("c", CExtension.class);
|
||||||
|
// Make the ExtractZipTransform known
|
||||||
|
project.getDependencies().getAttributesSchema().attribute(CExtension.EXTRACTED_TOOLS_ATTRIBUTE);
|
||||||
|
project.getDependencies().getArtifactTypes().maybeCreate("zip")
|
||||||
|
.getAttributes().attribute(CExtension.EXTRACTED_TOOLS_ATTRIBUTE, false);
|
||||||
|
project.getDependencies().registerTransform(ExtractZipTransform.class, t -> {
|
||||||
|
t.getFrom().attribute(CExtension.EXTRACTED_TOOLS_ATTRIBUTE, false).attribute(ARTIFACT_TYPE_ATTRIBUTE, "zip");
|
||||||
|
t.getTo().attribute(CExtension.EXTRACTED_TOOLS_ATTRIBUTE, true).attribute(ARTIFACT_TYPE_ATTRIBUTE, "zip");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
package org.xbib.gradle.plugin.c.tasks;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import org.gradle.api.provider.MapProperty;
|
||||||
|
import org.gradle.api.tasks.CacheableTask;
|
||||||
|
import org.gradle.api.tasks.Input;
|
||||||
|
import org.gradle.language.c.tasks.CCompile;
|
||||||
|
import org.gradle.nativeplatform.toolchain.internal.NativeCompileSpec;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extension of the 'org.gradle.language.c.tasks.CCompile' that supports defining different compiler arguments
|
||||||
|
* for different source files.
|
||||||
|
*/
|
||||||
|
@CacheableTask
|
||||||
|
public abstract class ExtendedCCompile extends CCompile {
|
||||||
|
|
||||||
|
@Input
|
||||||
|
public abstract MapProperty<String, List<String>> getPerFileCompilerArgs();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected NativeCompileSpec createCompileSpec() {
|
||||||
|
return new ExtendedCCompileSpec();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configureSpec(NativeCompileSpec spec) {
|
||||||
|
super.configureSpec(spec);
|
||||||
|
ExtendedCCompileSpec extendedCCompileSpec = (ExtendedCCompileSpec) spec;
|
||||||
|
extendedCCompileSpec.getPerFileCompilerArgs().putAll(getPerFileCompilerArgs().get());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package org.xbib.gradle.plugin.c.tasks;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.gradle.language.c.internal.DefaultCCompileSpec;
|
||||||
|
|
||||||
|
public class ExtendedCCompileSpec extends DefaultCCompileSpec {
|
||||||
|
|
||||||
|
private final Map<String, List<String>> perFileCompilerArgs = new HashMap<>();
|
||||||
|
|
||||||
|
public Map<String, List<String>> getPerFileCompilerArgs() {
|
||||||
|
return perFileCompilerArgs;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,80 @@
|
||||||
|
package org.xbib.gradle.plugin.c.tasks;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import org.gradle.api.file.ConfigurableFileCollection;
|
||||||
|
import org.gradle.api.tasks.CacheableTask;
|
||||||
|
import org.gradle.api.tasks.TaskAction;
|
||||||
|
import org.gradle.api.tasks.WorkResult;
|
||||||
|
import org.gradle.internal.operations.logging.BuildOperationLogger;
|
||||||
|
import org.gradle.language.assembler.internal.DefaultAssembleSpec;
|
||||||
|
import org.gradle.language.assembler.tasks.Assemble;
|
||||||
|
import org.gradle.language.base.internal.compile.Compiler;
|
||||||
|
import org.gradle.language.base.internal.tasks.StaleOutputCleaner;
|
||||||
|
import org.gradle.nativeplatform.internal.BuildOperationLoggingCompilerDecorator;
|
||||||
|
import org.gradle.nativeplatform.platform.internal.NativePlatformInternal;
|
||||||
|
import org.gradle.nativeplatform.toolchain.internal.NativeToolChainInternal;
|
||||||
|
import org.gradle.nativeplatform.toolchain.internal.compilespec.AssembleSpec;
|
||||||
|
import org.gradle.work.ChangeType;
|
||||||
|
import org.gradle.work.FileChange;
|
||||||
|
import org.gradle.work.Incremental;
|
||||||
|
import org.gradle.work.InputChanges;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Incremental and cacheable version of the 'org.gradle.language.assembler.tasks.Assemble' task.
|
||||||
|
*/
|
||||||
|
@CacheableTask
|
||||||
|
public abstract class IncrementalAssemble extends Assemble {
|
||||||
|
|
||||||
|
@Incremental
|
||||||
|
@Override
|
||||||
|
public ConfigurableFileCollection getSource() {
|
||||||
|
return super.getSource();
|
||||||
|
}
|
||||||
|
|
||||||
|
@TaskAction
|
||||||
|
public void assemble(InputChanges inputs) {
|
||||||
|
// Copied from super class and adjusted for incremental assembling
|
||||||
|
|
||||||
|
BuildOperationLogger operationLogger = getOperationLoggerFactory().newOperationLogger(getName(), getTemporaryDir());
|
||||||
|
|
||||||
|
List<File> toDelete = new ArrayList<>();
|
||||||
|
List<File> toAssemble = new ArrayList<>();
|
||||||
|
|
||||||
|
for (FileChange change : inputs.getFileChanges(super.getSource())) {
|
||||||
|
if (change.getChangeType() == ChangeType.REMOVED) {
|
||||||
|
toDelete.add(change.getFile());
|
||||||
|
}
|
||||||
|
if (change.getChangeType() != ChangeType.REMOVED) {
|
||||||
|
toAssemble.add(change.getFile());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean cleanedOutputs = StaleOutputCleaner.cleanOutputs(
|
||||||
|
getDeleter(),
|
||||||
|
toDelete,
|
||||||
|
getObjectFileDir()
|
||||||
|
);
|
||||||
|
|
||||||
|
DefaultAssembleSpec spec = new DefaultAssembleSpec();
|
||||||
|
spec.setTempDir(getTemporaryDir());
|
||||||
|
|
||||||
|
spec.setObjectFileDir(getObjectFileDir());
|
||||||
|
spec.source(toAssemble);
|
||||||
|
spec.include(getIncludes());
|
||||||
|
spec.args(getAssemblerArgs());
|
||||||
|
spec.setOperationLogger(operationLogger);
|
||||||
|
|
||||||
|
NativeToolChainInternal nativeToolChain = (NativeToolChainInternal) getToolChain().get();
|
||||||
|
NativePlatformInternal nativePlatform = (NativePlatformInternal) getTargetPlatform().get();
|
||||||
|
Compiler<AssembleSpec> compiler = nativeToolChain.select(nativePlatform).newCompiler(AssembleSpec.class);
|
||||||
|
WorkResult result = BuildOperationLoggingCompilerDecorator.wrap(compiler).execute(spec);
|
||||||
|
setDidWork(result.getDidWork() || cleanedOutputs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void assemble() {
|
||||||
|
// disable assemble() without 'inputs' argument
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
package org.xbib.gradle.plugin.c.toolchain;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import org.gradle.api.NonNullApi;
|
||||||
|
import org.gradle.api.file.FileCollection;
|
||||||
|
import org.gradle.api.model.ObjectFactory;
|
||||||
|
import org.gradle.nativeplatform.platform.internal.NativePlatformInternal;
|
||||||
|
import org.gradle.nativeplatform.toolchain.NativeToolChain;
|
||||||
|
import org.gradle.nativeplatform.toolchain.internal.NativeLanguage;
|
||||||
|
import org.gradle.nativeplatform.toolchain.internal.NativeToolChainInternal;
|
||||||
|
import org.gradle.nativeplatform.toolchain.internal.PlatformToolProvider;
|
||||||
|
|
||||||
|
@NonNullApi
|
||||||
|
public abstract class CNativeToolChain implements NativeToolChain, NativeToolChainInternal {
|
||||||
|
|
||||||
|
private final String toolName;
|
||||||
|
|
||||||
|
private final String toolVersion;
|
||||||
|
|
||||||
|
private final FileCollection tool;
|
||||||
|
|
||||||
|
private final String objectFileExtension;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public CNativeToolChain(String toolName,
|
||||||
|
String toolVersion,
|
||||||
|
FileCollection tool,
|
||||||
|
String objectFileExtension) {
|
||||||
|
this.toolName = toolName;
|
||||||
|
this.toolVersion = toolVersion;
|
||||||
|
this.tool = tool;
|
||||||
|
this.objectFileExtension = objectFileExtension;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return toolName + "_" + toolVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PlatformToolProvider select(NativePlatformInternal nativePlatform) {
|
||||||
|
return getObjects().newInstance(CPlatformToolProvider.class, tool, toolVersion, objectFileExtension);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PlatformToolProvider select(NativeLanguage nativeLanguage, NativePlatformInternal nativePlatform) {
|
||||||
|
return select(nativePlatform);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDisplayName() {
|
||||||
|
return getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getOutputType() {
|
||||||
|
return getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void assertSupported() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
abstract protected ObjectFactory getObjects();
|
||||||
|
}
|
|
@ -0,0 +1,202 @@
|
||||||
|
package org.xbib.gradle.plugin.c.toolchain;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import org.gradle.api.file.FileCollection;
|
||||||
|
import org.gradle.internal.logging.text.DiagnosticsVisitor;
|
||||||
|
import org.gradle.internal.operations.BuildOperationExecutor;
|
||||||
|
import org.gradle.internal.work.WorkerLeaseService;
|
||||||
|
import org.gradle.language.base.internal.compile.CompileSpec;
|
||||||
|
import org.gradle.language.base.internal.compile.Compiler;
|
||||||
|
import org.gradle.language.base.internal.compile.DefaultCompilerVersion;
|
||||||
|
import org.gradle.language.base.internal.compile.VersionAwareCompiler;
|
||||||
|
import org.gradle.nativeplatform.internal.CompilerOutputFileNamingSchemeFactory;
|
||||||
|
import org.gradle.nativeplatform.internal.LinkerSpec;
|
||||||
|
import org.gradle.nativeplatform.internal.StaticLibraryArchiverSpec;
|
||||||
|
import org.gradle.nativeplatform.toolchain.internal.CommandLineToolContext;
|
||||||
|
import org.gradle.nativeplatform.toolchain.internal.CommandLineToolInvocationWorker;
|
||||||
|
import org.gradle.nativeplatform.toolchain.internal.DefaultCommandLineToolInvocationWorker;
|
||||||
|
import org.gradle.nativeplatform.toolchain.internal.DefaultMutableCommandLineToolContext;
|
||||||
|
import org.gradle.nativeplatform.toolchain.internal.EmptySystemLibraries;
|
||||||
|
import org.gradle.nativeplatform.toolchain.internal.OutputCleaningCompiler;
|
||||||
|
import org.gradle.nativeplatform.toolchain.internal.PlatformToolProvider;
|
||||||
|
import org.gradle.nativeplatform.toolchain.internal.SystemLibraries;
|
||||||
|
import org.gradle.nativeplatform.toolchain.internal.ToolType;
|
||||||
|
import org.gradle.nativeplatform.toolchain.internal.compilespec.AssembleSpec;
|
||||||
|
import org.gradle.nativeplatform.toolchain.internal.compilespec.CCompileSpec;
|
||||||
|
import org.gradle.nativeplatform.toolchain.internal.metadata.CompilerMetadata;
|
||||||
|
import org.gradle.nativeplatform.toolchain.internal.tools.CommandLineToolSearchResult;
|
||||||
|
import org.gradle.process.internal.ExecActionFactory;
|
||||||
|
import org.gradle.util.internal.VersionNumber;
|
||||||
|
import org.xbib.gradle.plugin.c.toolchain.tools.Assembler;
|
||||||
|
import org.xbib.gradle.plugin.c.toolchain.tools.CCompiler;
|
||||||
|
import org.xbib.gradle.plugin.c.toolchain.tools.Linker;
|
||||||
|
import org.xbib.gradle.plugin.c.toolchain.tools.StaticLibraryArchiver;
|
||||||
|
|
||||||
|
public abstract class CPlatformToolProvider implements PlatformToolProvider {
|
||||||
|
|
||||||
|
private final FileCollection tool;
|
||||||
|
|
||||||
|
private final String toolVersion;
|
||||||
|
|
||||||
|
private final String objectFileExtension;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public CPlatformToolProvider(FileCollection tool, String toolVersion, String objectFileExtension) {
|
||||||
|
this.tool = tool;
|
||||||
|
this.toolVersion = toolVersion;
|
||||||
|
this.objectFileExtension = objectFileExtension;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public <T extends CompileSpec> Compiler<T> newCompiler(Class<T> spec) {
|
||||||
|
if (CCompileSpec.class.isAssignableFrom(spec)) {
|
||||||
|
return (Compiler<T>) createCCompiler();
|
||||||
|
}
|
||||||
|
if (AssembleSpec.class.isAssignableFrom(spec)) {
|
||||||
|
return (Compiler<T>) createAssembler();
|
||||||
|
}
|
||||||
|
if (LinkerSpec.class.isAssignableFrom(spec)) {
|
||||||
|
return (Compiler<T>) createLinker();
|
||||||
|
}
|
||||||
|
if (StaticLibraryArchiverSpec.class.isAssignableFrom(spec)) {
|
||||||
|
return (Compiler<T>) createStaticLibraryArchiver();
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException(String.format("Don't know how to compile from a spec of type %s.",
|
||||||
|
spec.getSimpleName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Compiler<CCompileSpec> createCCompiler() {
|
||||||
|
CCompiler cCompiler = new CCompiler(getBuildOperationExecutor(), getCompilerOutputFileNamingSchemeFactory(),
|
||||||
|
commandLineTool("Compiler"), context(), objectFileExtension, getWorkerLeaseService());
|
||||||
|
OutputCleaningCompiler<CCompileSpec> outputCleaningCompiler = new OutputCleaningCompiler<>(cCompiler,
|
||||||
|
getCompilerOutputFileNamingSchemeFactory(), objectFileExtension);
|
||||||
|
return versionAwareCompiler(outputCleaningCompiler, "C Compiler");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Compiler<AssembleSpec> createAssembler() {
|
||||||
|
return new Assembler(getBuildOperationExecutor(), getCompilerOutputFileNamingSchemeFactory(),
|
||||||
|
commandLineTool("Assembler"), context(), objectFileExtension, getWorkerLeaseService());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Compiler<LinkerSpec> createLinker() {
|
||||||
|
Linker linker = new Linker(getBuildOperationExecutor(), commandLineTool("Linked"), context(),
|
||||||
|
getWorkerLeaseService());
|
||||||
|
return versionAwareCompiler(linker, "Linker");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Compiler<StaticLibraryArchiverSpec> createStaticLibraryArchiver() {
|
||||||
|
return new StaticLibraryArchiver(getBuildOperationExecutor(),
|
||||||
|
commandLineTool("Static Library"), context(), getWorkerLeaseService());
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T extends CompileSpec> VersionAwareCompiler<T> versionAwareCompiler(Compiler<T> compiler, String toolType) {
|
||||||
|
return new VersionAwareCompiler<>(compiler, new DefaultCompilerVersion(toolType,
|
||||||
|
tool.getSingleFile().getName(), VersionNumber.parse(toolVersion)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private CommandLineToolInvocationWorker commandLineTool(String name) {
|
||||||
|
return new DefaultCommandLineToolInvocationWorker(name, tool.getSingleFile(), getExecActionFactory());
|
||||||
|
}
|
||||||
|
|
||||||
|
private CommandLineToolContext context() {
|
||||||
|
return new DefaultMutableCommandLineToolContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getExecutableName(String executablePath) {
|
||||||
|
return executablePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSharedLibraryName(String libraryPath) {
|
||||||
|
return libraryPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getImportLibraryName(String libraryPath) {
|
||||||
|
return libraryPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSharedLibraryLinkFileName(String libraryPath) {
|
||||||
|
return libraryPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getStaticLibraryName(String libraryPath) {
|
||||||
|
return libraryPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getExecutableSymbolFileName(String executablePath) {
|
||||||
|
return executablePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getLibrarySymbolFileName(String libraryPath) {
|
||||||
|
return libraryPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SystemLibraries getSystemLibraries(ToolType compilerType) {
|
||||||
|
return new EmptySystemLibraries();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAvailable() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSupported() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean producesImportLibrary() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean requiresDebugBinaryStripping() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void explain(DiagnosticsVisitor visitor) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getObjectFileExtension() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> T get(Class<T> toolType) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompilerMetadata getCompilerMetadata(ToolType compilerType) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommandLineToolSearchResult locateTool(ToolType compilerType) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
protected abstract BuildOperationExecutor getBuildOperationExecutor();
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
protected abstract CompilerOutputFileNamingSchemeFactory getCompilerOutputFileNamingSchemeFactory();
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
protected abstract ExecActionFactory getExecActionFactory();
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
protected abstract WorkerLeaseService getWorkerLeaseService();
|
||||||
|
}
|
|
@ -0,0 +1,84 @@
|
||||||
|
package org.xbib.gradle.plugin.c.toolchain.tools;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import org.gradle.internal.Transformers;
|
||||||
|
import org.gradle.internal.operations.BuildOperationExecutor;
|
||||||
|
import org.gradle.internal.process.ArgWriter;
|
||||||
|
import org.gradle.internal.work.WorkerLeaseService;
|
||||||
|
import org.gradle.nativeplatform.internal.CompilerOutputFileNamingSchemeFactory;
|
||||||
|
import org.gradle.nativeplatform.toolchain.internal.CommandLineToolContext;
|
||||||
|
import org.gradle.nativeplatform.toolchain.internal.CommandLineToolInvocation;
|
||||||
|
import org.gradle.nativeplatform.toolchain.internal.CommandLineToolInvocationWorker;
|
||||||
|
import org.gradle.nativeplatform.toolchain.internal.NativeCompiler;
|
||||||
|
import org.gradle.nativeplatform.toolchain.internal.compilespec.AssembleSpec;
|
||||||
|
|
||||||
|
public class Assembler extends NativeCompiler<AssembleSpec> {
|
||||||
|
|
||||||
|
private final String objectFileExtension;
|
||||||
|
|
||||||
|
public Assembler(BuildOperationExecutor buildOperationExecutor,
|
||||||
|
CompilerOutputFileNamingSchemeFactory compilerOutputFileNamingSchemeFactory,
|
||||||
|
CommandLineToolInvocationWorker commandLineTool,
|
||||||
|
CommandLineToolContext invocationContext,
|
||||||
|
String objectFileExtension,
|
||||||
|
WorkerLeaseService workerLeaseService) {
|
||||||
|
super(buildOperationExecutor, compilerOutputFileNamingSchemeFactory, commandLineTool, invocationContext,
|
||||||
|
new AssemblerArgsTransformer(), Transformers.noOpTransformer(),
|
||||||
|
objectFileExtension, true,
|
||||||
|
workerLeaseService);
|
||||||
|
|
||||||
|
this.objectFileExtension = objectFileExtension;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected CommandLineToolInvocation createPerFileInvocation(List<String> genericArgs,
|
||||||
|
File sourceFile,
|
||||||
|
File objectDir,
|
||||||
|
AssembleSpec spec) {
|
||||||
|
List<String> sourceArgs = this.getSourceArgs(sourceFile);
|
||||||
|
List<String> outputArgs = this.getOutputArgs(spec, this.getOutputFileDir(sourceFile, objectDir,
|
||||||
|
objectFileExtension));
|
||||||
|
List<String> pchArgs = this.maybeGetPCHArgs(spec, sourceFile);
|
||||||
|
|
||||||
|
// Changed order: put 'outputArgs' first
|
||||||
|
return this.newInvocation("compiling ".concat(sourceFile.getName()), objectDir,
|
||||||
|
this.buildPerFileArgs(outputArgs, genericArgs, sourceArgs, pchArgs), spec.getOperationLogger());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Iterable<String> buildPerFileArgs(List<String> genericArgs,
|
||||||
|
List<String> sourceArgs,
|
||||||
|
List<String> outputArgs,
|
||||||
|
List<String> pchArgs) {
|
||||||
|
if (pchArgs != null && !pchArgs.isEmpty()) {
|
||||||
|
throw new UnsupportedOperationException("Precompiled header arguments cannot be specified for an Assembler compiler.");
|
||||||
|
}
|
||||||
|
return super.buildPerFileArgs(genericArgs, sourceArgs, outputArgs, pchArgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<String> getOutputArgs(AssembleSpec spec, File outputFile) {
|
||||||
|
return Arrays.asList("-o", outputFile.getAbsolutePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void addOptionsFileArgs(List<String> args, File tempDir) {
|
||||||
|
ArrayList<String> originalArgs = new ArrayList<>(args);
|
||||||
|
args.clear();
|
||||||
|
args.addAll(ArgWriter.argsFileGenerator(new File(tempDir, "options.txt"),
|
||||||
|
ArgWriter.unixStyleFactory()).transform(originalArgs));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<String> getPCHArgs(AssembleSpec spec) {
|
||||||
|
List<String> pchArgs = new ArrayList<>();
|
||||||
|
if (spec.getPrefixHeaderFile() != null) {
|
||||||
|
pchArgs.add("-include");
|
||||||
|
pchArgs.add(spec.getPrefixHeaderFile().getAbsolutePath());
|
||||||
|
}
|
||||||
|
return pchArgs;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package org.xbib.gradle.plugin.c.toolchain.tools;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import org.gradle.api.NonNullApi;
|
||||||
|
import org.gradle.nativeplatform.toolchain.internal.ArgsTransformer;
|
||||||
|
import org.gradle.nativeplatform.toolchain.internal.compilespec.AssembleSpec;
|
||||||
|
|
||||||
|
@NonNullApi
|
||||||
|
public class AssemblerArgsTransformer implements ArgsTransformer<AssembleSpec> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> transform(AssembleSpec assembleSpec) {
|
||||||
|
return assembleSpec.getAllArgs();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
package org.xbib.gradle.plugin.c.toolchain.tools;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import org.gradle.internal.Transformers;
|
||||||
|
import org.gradle.internal.operations.BuildOperationExecutor;
|
||||||
|
import org.gradle.internal.process.ArgWriter;
|
||||||
|
import org.gradle.internal.work.WorkerLeaseService;
|
||||||
|
import org.gradle.nativeplatform.internal.CompilerOutputFileNamingSchemeFactory;
|
||||||
|
import org.gradle.nativeplatform.toolchain.internal.CommandLineToolContext;
|
||||||
|
import org.gradle.nativeplatform.toolchain.internal.CommandLineToolInvocation;
|
||||||
|
import org.gradle.nativeplatform.toolchain.internal.CommandLineToolInvocationWorker;
|
||||||
|
import org.gradle.nativeplatform.toolchain.internal.NativeCompiler;
|
||||||
|
import org.gradle.nativeplatform.toolchain.internal.compilespec.CCompileSpec;
|
||||||
|
import org.xbib.gradle.plugin.c.tasks.ExtendedCCompileSpec;
|
||||||
|
|
||||||
|
public class CCompiler extends NativeCompiler<CCompileSpec> {
|
||||||
|
|
||||||
|
public CCompiler(BuildOperationExecutor buildOperationExecutor,
|
||||||
|
CompilerOutputFileNamingSchemeFactory compilerOutputFileNamingSchemeFactory,
|
||||||
|
CommandLineToolInvocationWorker commandLineToolInvocationWorker,
|
||||||
|
CommandLineToolContext invocationContext,
|
||||||
|
String objectFileExtension,
|
||||||
|
WorkerLeaseService workerLeaseService) {
|
||||||
|
super(buildOperationExecutor, compilerOutputFileNamingSchemeFactory, commandLineToolInvocationWorker, invocationContext,
|
||||||
|
new CCompilerArgsTransformer(), Transformers.noOpTransformer(),
|
||||||
|
objectFileExtension, true, workerLeaseService);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<String> getOutputArgs(CCompileSpec spec, File outputFile) {
|
||||||
|
return Arrays.asList("-o", outputFile.getAbsolutePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void addOptionsFileArgs(List<String> args, File tempDir) {
|
||||||
|
ArrayList<String> originalArgs = new ArrayList<>(args);
|
||||||
|
args.clear();
|
||||||
|
args.addAll(ArgWriter.argsFileGenerator(new File(tempDir, "options.txt"),
|
||||||
|
ArgWriter.unixStyleFactory()).transform(originalArgs));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<String> getPCHArgs(CCompileSpec spec) {
|
||||||
|
List<String> pchArgs = new ArrayList<>();
|
||||||
|
if (spec.getPrefixHeaderFile() != null) {
|
||||||
|
pchArgs.add("-include");
|
||||||
|
pchArgs.add(spec.getPrefixHeaderFile().getAbsolutePath());
|
||||||
|
}
|
||||||
|
return pchArgs;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected CommandLineToolInvocation createPerFileInvocation(List<String> genericArgs,
|
||||||
|
File sourceFile,
|
||||||
|
File objectDir,
|
||||||
|
CCompileSpec spec) {
|
||||||
|
ExtendedCCompileSpec extendedCCompileSpec = (ExtendedCCompileSpec) spec;
|
||||||
|
List<String> perFileArgs = extendedCCompileSpec.getPerFileCompilerArgs().get(sourceFile.getName());
|
||||||
|
if (perFileArgs != null) {
|
||||||
|
List<String> allPerFileArgs = new ArrayList<>(genericArgs);
|
||||||
|
allPerFileArgs.addAll(perFileArgs);
|
||||||
|
return super.createPerFileInvocation(allPerFileArgs, sourceFile, objectDir, spec);
|
||||||
|
} else {
|
||||||
|
return super.createPerFileInvocation(genericArgs, sourceFile, objectDir, spec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
package org.xbib.gradle.plugin.c.toolchain.tools;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import org.gradle.api.NonNullApi;
|
||||||
|
import org.gradle.nativeplatform.toolchain.internal.ArgsTransformer;
|
||||||
|
import org.gradle.nativeplatform.toolchain.internal.MacroArgsConverter;
|
||||||
|
import org.gradle.nativeplatform.toolchain.internal.compilespec.CCompileSpec;
|
||||||
|
|
||||||
|
@NonNullApi
|
||||||
|
public class CCompilerArgsTransformer implements ArgsTransformer<CCompileSpec> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> transform(CCompileSpec spec) {
|
||||||
|
List<String> args = new ArrayList<>();
|
||||||
|
|
||||||
|
for (File file : spec.getIncludeRoots()) {
|
||||||
|
args.add("-I");
|
||||||
|
args.add(file.getAbsolutePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (File file : spec.getSystemIncludeRoots()) {
|
||||||
|
args.add("-isystem");
|
||||||
|
args.add(file.getAbsolutePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String macroArg : new MacroArgsConverter().transform(spec.getMacros())) {
|
||||||
|
args.add("-D" + macroArg);
|
||||||
|
}
|
||||||
|
args.addAll(spec.getAllArgs());
|
||||||
|
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
package org.xbib.gradle.plugin.c.toolchain.tools;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import org.gradle.api.Action;
|
||||||
|
import org.gradle.internal.operations.BuildOperationExecutor;
|
||||||
|
import org.gradle.internal.operations.BuildOperationQueue;
|
||||||
|
import org.gradle.internal.process.ArgWriter;
|
||||||
|
import org.gradle.internal.work.WorkerLeaseService;
|
||||||
|
import org.gradle.nativeplatform.internal.LinkerSpec;
|
||||||
|
import org.gradle.nativeplatform.toolchain.internal.AbstractCompiler;
|
||||||
|
import org.gradle.nativeplatform.toolchain.internal.CommandLineToolContext;
|
||||||
|
import org.gradle.nativeplatform.toolchain.internal.CommandLineToolInvocation;
|
||||||
|
import org.gradle.nativeplatform.toolchain.internal.CommandLineToolInvocationWorker;
|
||||||
|
|
||||||
|
public class Linker extends AbstractCompiler<LinkerSpec> {
|
||||||
|
|
||||||
|
public Linker(BuildOperationExecutor buildOperationExecutor,
|
||||||
|
CommandLineToolInvocationWorker commandLineToolInvocationWorker,
|
||||||
|
CommandLineToolContext invocationContext,
|
||||||
|
WorkerLeaseService workerLeaseService) {
|
||||||
|
super(buildOperationExecutor, commandLineToolInvocationWorker, invocationContext,
|
||||||
|
new LinkerArgsTransformer(),
|
||||||
|
true, workerLeaseService);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Action<BuildOperationQueue<CommandLineToolInvocation>> newInvocationAction(final LinkerSpec spec,
|
||||||
|
List<String> args) {
|
||||||
|
final CommandLineToolInvocation invocation = newInvocation("linking " + spec.getOutputFile().getName(),
|
||||||
|
args, spec.getOperationLogger());
|
||||||
|
return buildQueue -> {
|
||||||
|
buildQueue.setLogLocation(spec.getOperationLogger().getLogLocation());
|
||||||
|
buildQueue.add(invocation);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void addOptionsFileArgs(List<String> args, File tempDir) {
|
||||||
|
ArrayList<String> originalArgs = new ArrayList<>(args);
|
||||||
|
args.clear();
|
||||||
|
args.addAll(ArgWriter.argsFileGenerator(new File(tempDir, "options.txt"),
|
||||||
|
ArgWriter.unixStyleFactory()).transform(originalArgs));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
package org.xbib.gradle.plugin.c.toolchain.tools;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import org.gradle.api.NonNullApi;
|
||||||
|
import org.gradle.nativeplatform.internal.LinkerSpec;
|
||||||
|
import org.gradle.nativeplatform.toolchain.internal.ArgsTransformer;
|
||||||
|
|
||||||
|
@NonNullApi
|
||||||
|
public class LinkerArgsTransformer implements ArgsTransformer<LinkerSpec> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> transform(LinkerSpec spec) {
|
||||||
|
|
||||||
|
List<String> args = new ArrayList<>(spec.getSystemArgs());
|
||||||
|
|
||||||
|
args.addAll(spec.getArgs());
|
||||||
|
|
||||||
|
for (File file : spec.getObjectFiles()) {
|
||||||
|
args.add(file.getAbsolutePath());
|
||||||
|
}
|
||||||
|
for (File file : spec.getLibraries()) {
|
||||||
|
args.add(file.getAbsolutePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
args.add("-o");
|
||||||
|
args.add(spec.getOutputFile().getAbsolutePath());
|
||||||
|
|
||||||
|
if (!spec.getLibraryPath().isEmpty()) {
|
||||||
|
throw new UnsupportedOperationException("Library Path not yet supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
package org.xbib.gradle.plugin.c.toolchain.tools;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.List;
|
||||||
|
import org.gradle.api.Action;
|
||||||
|
import org.gradle.api.GradleException;
|
||||||
|
import org.gradle.api.tasks.WorkResult;
|
||||||
|
import org.gradle.internal.operations.BuildOperationExecutor;
|
||||||
|
import org.gradle.internal.operations.BuildOperationQueue;
|
||||||
|
import org.gradle.internal.work.WorkerLeaseService;
|
||||||
|
import org.gradle.nativeplatform.internal.StaticLibraryArchiverSpec;
|
||||||
|
import org.gradle.nativeplatform.toolchain.internal.AbstractCompiler;
|
||||||
|
import org.gradle.nativeplatform.toolchain.internal.CommandLineToolContext;
|
||||||
|
import org.gradle.nativeplatform.toolchain.internal.CommandLineToolInvocation;
|
||||||
|
import org.gradle.nativeplatform.toolchain.internal.CommandLineToolInvocationWorker;
|
||||||
|
|
||||||
|
public class StaticLibraryArchiver extends AbstractCompiler<StaticLibraryArchiverSpec> {
|
||||||
|
|
||||||
|
public StaticLibraryArchiver(BuildOperationExecutor buildOperationExecutor,
|
||||||
|
CommandLineToolInvocationWorker commandLineToolInvocationWorker,
|
||||||
|
CommandLineToolContext invocationContext,
|
||||||
|
WorkerLeaseService workerLeaseService) {
|
||||||
|
super(buildOperationExecutor, commandLineToolInvocationWorker, invocationContext,
|
||||||
|
new StaticLibraryArchiverArgsTransformer(), false, workerLeaseService);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WorkResult execute(final StaticLibraryArchiverSpec spec) {
|
||||||
|
deletePreviousOutput(spec);
|
||||||
|
return super.execute(spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deletePreviousOutput(StaticLibraryArchiverSpec spec) {
|
||||||
|
// Need to delete the previous archive, otherwise stale object files will remain
|
||||||
|
if (!spec.getOutputFile().isFile()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!(spec.getOutputFile().delete())) {
|
||||||
|
throw new GradleException("Create static archive failed: could not delete previous archive");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Action<BuildOperationQueue<CommandLineToolInvocation>> newInvocationAction(final StaticLibraryArchiverSpec spec,
|
||||||
|
List<String> args) {
|
||||||
|
final CommandLineToolInvocation invocation = newInvocation(
|
||||||
|
"archiving " + spec.getOutputFile().getName(), spec.getOutputFile().getParentFile(), args, spec.getOperationLogger());
|
||||||
|
|
||||||
|
return buildQueue -> {
|
||||||
|
buildQueue.setLogLocation(spec.getOperationLogger().getLogLocation());
|
||||||
|
buildQueue.add(invocation);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void addOptionsFileArgs(List<String> args, File tempDir) {
|
||||||
|
// No support for command file
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package org.xbib.gradle.plugin.c.toolchain.tools;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import org.gradle.api.NonNullApi;
|
||||||
|
import org.gradle.nativeplatform.internal.StaticLibraryArchiverSpec;
|
||||||
|
import org.gradle.nativeplatform.toolchain.internal.ArgsTransformer;
|
||||||
|
|
||||||
|
@NonNullApi
|
||||||
|
public class StaticLibraryArchiverArgsTransformer implements ArgsTransformer<StaticLibraryArchiverSpec> {
|
||||||
|
|
||||||
|
public List<String> transform(StaticLibraryArchiverSpec spec) {
|
||||||
|
List<String> args = new ArrayList<>(spec.getAllArgs());
|
||||||
|
|
||||||
|
args.add("-rv");
|
||||||
|
|
||||||
|
args.add(spec.getOutputFile().getAbsolutePath());
|
||||||
|
|
||||||
|
for (File file : spec.getObjectFiles()) {
|
||||||
|
args.add(file.getAbsolutePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
package org.xbib.gradle.plugin.c.toolchain.transform;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.attribute.PosixFilePermissions;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
import java.util.zip.ZipInputStream;
|
||||||
|
import org.gradle.api.artifacts.transform.InputArtifact;
|
||||||
|
import org.gradle.api.artifacts.transform.TransformAction;
|
||||||
|
import org.gradle.api.artifacts.transform.TransformOutputs;
|
||||||
|
import org.gradle.api.artifacts.transform.TransformParameters;
|
||||||
|
import org.gradle.api.file.FileSystemLocation;
|
||||||
|
import org.gradle.api.provider.Provider;
|
||||||
|
import org.gradle.api.tasks.PathSensitive;
|
||||||
|
import org.gradle.api.tasks.PathSensitivity;
|
||||||
|
import org.gradle.internal.os.OperatingSystem;
|
||||||
|
|
||||||
|
public abstract class ExtractZipTransform implements TransformAction<TransformParameters.None> {
|
||||||
|
|
||||||
|
@InputArtifact
|
||||||
|
@PathSensitive(PathSensitivity.RELATIVE)
|
||||||
|
public abstract Provider<FileSystemLocation> getInputArtifact();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void transform(TransformOutputs outputs) {
|
||||||
|
File input = getInputArtifact().get().getAsFile();
|
||||||
|
File unzipDir = outputs.dir(input.getName().substring(0, input.getName().lastIndexOf(".zip")));
|
||||||
|
try {
|
||||||
|
unzipTo(input, unzipDir);
|
||||||
|
System.out.println("Transformed into: " + unzipDir.getAbsolutePath());
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void unzipTo(File input, File unzipDir) throws IOException {
|
||||||
|
byte[] buffer = new byte[1024];
|
||||||
|
ZipInputStream zis = new ZipInputStream(Files.newInputStream(input.toPath()));
|
||||||
|
ZipEntry zipEntry = zis.getNextEntry();
|
||||||
|
|
||||||
|
while (zipEntry != null) {
|
||||||
|
if (!zipEntry.isDirectory()) {
|
||||||
|
File newFile = new File(unzipDir, zipEntry.getName());
|
||||||
|
newFile.getParentFile().mkdirs();
|
||||||
|
FileOutputStream fos = new FileOutputStream(newFile);
|
||||||
|
int len;
|
||||||
|
while ((len = zis.read(buffer)) > 0) {
|
||||||
|
fos.write(buffer, 0, len);
|
||||||
|
}
|
||||||
|
fos.close();
|
||||||
|
if (OperatingSystem.current().isUnix()) {
|
||||||
|
Files.setPosixFilePermissions(newFile.toPath(), PosixFilePermissions.fromString("rwxr-xr-x"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
zipEntry = zis.getNextEntry();
|
||||||
|
}
|
||||||
|
|
||||||
|
zis.closeEntry();
|
||||||
|
zis.close();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
package org.xbib.gradle.plugin.c.test
|
||||||
|
|
||||||
|
import org.gradle.testkit.runner.BuildResult
|
||||||
|
import org.gradle.testkit.runner.GradleRunner
|
||||||
|
import org.gradle.testkit.runner.TaskOutcome
|
||||||
|
import org.junit.jupiter.api.BeforeEach
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.junit.jupiter.api.io.TempDir
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals
|
||||||
|
|
||||||
|
class CPluginIntegrationTest {
|
||||||
|
|
||||||
|
private File projectDir
|
||||||
|
|
||||||
|
private File settingsFile
|
||||||
|
|
||||||
|
private File buildFile
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setup(@TempDir File testProjectDir) throws IOException {
|
||||||
|
this.projectDir = testProjectDir
|
||||||
|
this.settingsFile = new File(testProjectDir, "settings.gradle")
|
||||||
|
this.buildFile = new File(testProjectDir, "build.gradle")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testPlugin() {
|
||||||
|
String settingsFileContent = '''
|
||||||
|
rootProject.name = 'c-test'
|
||||||
|
'''
|
||||||
|
settingsFile.write(settingsFileContent)
|
||||||
|
String buildFileContent = '''
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id 'base'
|
||||||
|
id 'org.xbib.gradle.plugin.c'
|
||||||
|
}
|
||||||
|
|
||||||
|
// dummy task, no source present
|
||||||
|
|
||||||
|
tasks.register('compileC', org.xbib.gradle.plugin.c.tasks.ExtendedCCompile) {
|
||||||
|
toolChain.set(c.localTool("14.0.0", "/usr/bin/clang", ".o"))
|
||||||
|
targetPlatform.set(c.platform())
|
||||||
|
source(layout.projectDirectory.files("src/c").asFileTree)
|
||||||
|
includes(layout.projectDirectory.files("src/headers"))
|
||||||
|
compilerArgs.add("-S")
|
||||||
|
objectFileDir.set(layout.buildDirectory.dir("out/o"))
|
||||||
|
}
|
||||||
|
|
||||||
|
'''
|
||||||
|
buildFile.write(buildFileContent)
|
||||||
|
BuildResult result = GradleRunner.create()
|
||||||
|
.withProjectDir(projectDir)
|
||||||
|
.withPluginClasspath()
|
||||||
|
.withArguments(":compileC", "--info", "--stacktrace", "--warning-mode=all")
|
||||||
|
.forwardOutput()
|
||||||
|
.build()
|
||||||
|
assertEquals(TaskOutcome.NO_SOURCE, result.task(":compileC").getOutcome())
|
||||||
|
}
|
||||||
|
}
|
8
gradle-plugin-cmake/NOTICE.txt
Normal file
8
gradle-plugin-cmake/NOTICE.txt
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
|
||||||
|
This work is based on
|
||||||
|
|
||||||
|
https://github.com/crimsonmagick/gradle-cmake-plugin
|
||||||
|
|
||||||
|
as of Januar 8, 2024
|
||||||
|
|
||||||
|
License: Apache 2.0
|
185
gradle-plugin-cmake/README.md
Normal file
185
gradle-plugin-cmake/README.md
Normal file
|
@ -0,0 +1,185 @@
|
||||||
|
# gradle-plugin-cmake
|
||||||
|
|
||||||
|
This plugin allows to configure and build using CMake.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
* `CMake` installed on the system. Available [here](https://www.cmake.org "CMake Homepage").
|
||||||
|
|
||||||
|
## To apply the plugin:
|
||||||
|
|
||||||
|
**plugins DSL**
|
||||||
|
|
||||||
|
```groovy
|
||||||
|
plugins {
|
||||||
|
id 'org.xbib.gradle.plugin.cmake' version '3.1.0'
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Legacy plugin application**
|
||||||
|
|
||||||
|
```groovy
|
||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
maven {
|
||||||
|
url "https://plugins.gradle.org/m2/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dependencies {
|
||||||
|
classpath 'org.xbib.gradle:gradle-plugin-cmake:3.1.0'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
apply plugin: "org.xbib.gradle.plugin.cmake"
|
||||||
|
```
|
||||||
|
|
||||||
|
and configure by:
|
||||||
|
|
||||||
|
```groovy
|
||||||
|
cmake {
|
||||||
|
// optional configration to path of cmake. Not required if cmake is on the path.
|
||||||
|
executable='/my/path/to/cmake'
|
||||||
|
// optional working folder. default is ./build/cmake
|
||||||
|
workingFolder=file("$buildDir/cmake")
|
||||||
|
// cmakeConfigure parameters
|
||||||
|
// optional source folder. This is where the main CMakeLists.txt file resides. Default is ./src/main/cpp
|
||||||
|
sourceFolder=file("$projectDir/src/main/cpp")
|
||||||
|
// optional install prefix. By default, install prefix is empty.
|
||||||
|
installPrefix="${System.properties['user.home']}"
|
||||||
|
// select a generator (optional, otherwise cmake's default generator is used)
|
||||||
|
generator='Visual Studio 15 2017'
|
||||||
|
// set a platform for generators that support it (usually Visual Studio)
|
||||||
|
platform='x64'
|
||||||
|
// set a toolset generators that support it (usually only Visual Studio)
|
||||||
|
toolset='v141'
|
||||||
|
// optionally set to build static libs
|
||||||
|
buildStaticLibs=true
|
||||||
|
// optionally set to build shared libs
|
||||||
|
buildSharedLibs=true
|
||||||
|
// define arbitrary CMake parameters. The below adds -Dtest=hello to cmake command line.
|
||||||
|
defs.test='hello'
|
||||||
|
// cmakeBuild parameters
|
||||||
|
// optional configuration to build
|
||||||
|
buildConfig='Release'
|
||||||
|
// optional build target
|
||||||
|
buildTarget='install'
|
||||||
|
// optional build clean. if set to true, calls cmake --build with --clean-first
|
||||||
|
buildClean=false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Auto-created tasks
|
||||||
|
|
||||||
|
* *cmakeConfigure*: Calls CMake to generate your build scripts in the folder selected by workingFolder.
|
||||||
|
|
||||||
|
* *cmakeBuild*: Calls CMake --build in the folder selected by workingFolder to actually build.
|
||||||
|
|
||||||
|
* *cmakeClean*: Cleans the workingFolder.
|
||||||
|
|
||||||
|
* *cmakeGenerators*: Trys to list the generators available on the current platform by parsing `cmake --help`'s output.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
clean, configure and build:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./gradlew cmakeClean cmakeConfigure cmakeBuild
|
||||||
|
```
|
||||||
|
|
||||||
|
if you have assemble and clean tasks in your gradle project already you can also use:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
assemble.dependsOn cmakeBuild
|
||||||
|
cmakeBuild.dependsOn cmakeConfigure
|
||||||
|
clean.dependsOn cmakeClean
|
||||||
|
```
|
||||||
|
|
||||||
|
and just call
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./gradlew clean assemble
|
||||||
|
```
|
||||||
|
|
||||||
|
If you want to get the output of cmake, add -i to your gradle call, for example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./gradlew cmakeConfigure -i
|
||||||
|
```
|
||||||
|
|
||||||
|
## Custom tasks
|
||||||
|
|
||||||
|
You can create custom tasks the following way:
|
||||||
|
|
||||||
|
```groovy
|
||||||
|
task configureFoo(type: org.xbib.gradle.plugin.cmake.CMakeConfigureTask) {
|
||||||
|
sourceFolder=file("$projectDir/src/main/cpp/foo")
|
||||||
|
workingFolder=file("$buildDir/cmake/foo")
|
||||||
|
// ... other parameters you need, see above, except the ones listed under cmakeBuild Parameters
|
||||||
|
}
|
||||||
|
|
||||||
|
task buildFoo(type: org.xbib.gradle.plugin.cmake.CMakeBuildTask) {
|
||||||
|
workingFolder=file("$buildDir/cmake/foo")
|
||||||
|
// ... other parameters you need, see above, except the ones listed under cmakeConfigure parameters
|
||||||
|
}
|
||||||
|
|
||||||
|
buildFoo.dependsOn configureFoo // optional --- make sure its configured when you run the build task
|
||||||
|
```
|
||||||
|
### Multiple targets (cross-compilation)
|
||||||
|
If you need to configure for multiple targets you can use the `targets` property:
|
||||||
|
|
||||||
|
```groovy
|
||||||
|
cmake {
|
||||||
|
sourceFolder = "$projectDir/src"
|
||||||
|
buildSharedLibs = true
|
||||||
|
buildClean = true
|
||||||
|
buildConfig = 'Release'
|
||||||
|
targets {
|
||||||
|
windows {
|
||||||
|
final os = OperatingSystem.WINDOWS
|
||||||
|
workingFolder = new File(project.getBuildDir(), "cmake" + File.separator + os.nativePrefix)
|
||||||
|
platform='x64'
|
||||||
|
}
|
||||||
|
linux {
|
||||||
|
final os = OperatingSystem.LINUX
|
||||||
|
workingFolder = new File(project.getBuildDir(), "cmake" + File.separator + os.nativePrefix)
|
||||||
|
platform = 'x64'
|
||||||
|
}
|
||||||
|
mac {
|
||||||
|
final os = OperatingSystem.MAC_OS
|
||||||
|
workingFolder = new File(project.getBuildDir(), "cmake" + File.separator + os.nativePrefix)
|
||||||
|
platform = 'arm64'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### Custom tasks using main configuration
|
||||||
|
|
||||||
|
As an alternative to using `targets` you can "import" the settings you've made in the main configuration "cmake" using the 'configureFromProject()' call:
|
||||||
|
|
||||||
|
```groovy
|
||||||
|
cmake {
|
||||||
|
executable='/my/path/to/cmake'
|
||||||
|
workingFolder=file("$buildDir/cmake")
|
||||||
|
sourceFolder=file("$projectDir/src/main/cpp")
|
||||||
|
installPrefix="${System.properties['user.home']}"
|
||||||
|
generator='Visual Studio 15 2017'
|
||||||
|
platform='x64'
|
||||||
|
}
|
||||||
|
|
||||||
|
task cmakeConfigureX86(type: org.xbib.gradle.plugin.cmake.CMakeConfigureTask) {
|
||||||
|
configureFromProject() // uses everything in the cmake { ... } section.
|
||||||
|
// overwrite target platform
|
||||||
|
platform='x86'
|
||||||
|
// set a different working folder to not collide with default task
|
||||||
|
workingFolder=file("$buildDir/cmake_x86")
|
||||||
|
}
|
||||||
|
|
||||||
|
task cmakeBuildX86(type: org.xbib.gradle.plugin.cmake.CMakeBuildTask) {
|
||||||
|
configureFromProject() // uses everything in the cmake { ... } section.
|
||||||
|
workingFolder=file("$buildDir/cmake_x86")
|
||||||
|
}
|
||||||
|
|
||||||
|
cmakeBuildX86.dependsOn cmakeConfigureX86
|
||||||
|
```
|
30
gradle-plugin-cmake/build.gradle
Normal file
30
gradle-plugin-cmake/build.gradle
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
plugins {
|
||||||
|
alias(libs.plugins.publish)
|
||||||
|
}
|
||||||
|
|
||||||
|
apply plugin: 'com.gradle.plugin-publish'
|
||||||
|
|
||||||
|
apply from: rootProject.file('gradle/compile/groovy.gradle')
|
||||||
|
apply from: rootProject.file('gradle/test/junit5.gradle')
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
api gradleApi()
|
||||||
|
testImplementation gradleTestKit()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (project.hasProperty('gradle.publish.key')) {
|
||||||
|
gradlePlugin {
|
||||||
|
website = 'https://xbib.org/joerg/gradle-plugins/src/branch/main/gradle-plugin-cmake'
|
||||||
|
vcsUrl = 'https://xbib.org/joerg/gradle-plugins'
|
||||||
|
plugins {
|
||||||
|
cmakePlugin {
|
||||||
|
id = 'org.xbib.gradle.plugin.cmake'
|
||||||
|
implementationClass = 'org.xbib.gradle.plugin.cmake.CMakePlugin'
|
||||||
|
version = project.version
|
||||||
|
description = 'Gradle plugin for build with cmake'
|
||||||
|
displayName = 'Gradle Plugin for build with cmake'
|
||||||
|
tags.set(['cmake'])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
2
gradle-plugin-cmake/gradle.properties
Normal file
2
gradle-plugin-cmake/gradle.properties
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
name = gradle-plugin-docker
|
||||||
|
version = 3.1.0
|
|
@ -0,0 +1,100 @@
|
||||||
|
package org.xbib.gradle.plugin.cmake;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import org.gradle.api.DefaultTask;
|
||||||
|
import org.gradle.api.file.DirectoryProperty;
|
||||||
|
import org.gradle.api.provider.Property;
|
||||||
|
import org.gradle.api.tasks.Input;
|
||||||
|
import org.gradle.api.tasks.InputDirectory;
|
||||||
|
import org.gradle.api.tasks.Optional;
|
||||||
|
import org.gradle.api.tasks.TaskAction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a configured Build with CMake
|
||||||
|
*/
|
||||||
|
public class CMakeBuildTask extends DefaultTask {
|
||||||
|
|
||||||
|
private final Property<String> executable;
|
||||||
|
|
||||||
|
private final DirectoryProperty workingFolder;
|
||||||
|
|
||||||
|
private final Property<String> buildConfig;
|
||||||
|
|
||||||
|
private final Property<String> buildTarget;
|
||||||
|
|
||||||
|
private final Property<Boolean> buildClean;
|
||||||
|
|
||||||
|
public CMakeBuildTask() {
|
||||||
|
setGroup("cmake");
|
||||||
|
setDescription("Build a configured Build with CMake");
|
||||||
|
executable = getProject().getObjects().property(String.class);
|
||||||
|
workingFolder = getProject().getObjects().directoryProperty();
|
||||||
|
buildConfig = getProject().getObjects().property(String.class);
|
||||||
|
buildTarget = getProject().getObjects().property(String.class);
|
||||||
|
buildClean = getProject().getObjects().property(Boolean.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void configureFromProject() {
|
||||||
|
CMakePluginExtension ext = (CMakePluginExtension) getProject().getExtensions().getByName("cmake");
|
||||||
|
executable.set(ext.getExecutable());
|
||||||
|
workingFolder.set(ext.getWorkingFolder());
|
||||||
|
buildConfig.set(ext.getBuildConfig());
|
||||||
|
buildTarget.set(ext.getBuildTarget());
|
||||||
|
buildClean.set(ext.getBuildClean());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Input
|
||||||
|
@Optional
|
||||||
|
public Property<String> getExecutable() {
|
||||||
|
return executable;
|
||||||
|
}
|
||||||
|
|
||||||
|
@InputDirectory
|
||||||
|
public DirectoryProperty getWorkingFolder() {
|
||||||
|
return workingFolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Input
|
||||||
|
@Optional
|
||||||
|
public Property<String> getBuildConfig() {
|
||||||
|
return buildConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Input
|
||||||
|
@Optional
|
||||||
|
public Property<String> getBuildTarget() {
|
||||||
|
return buildTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Input
|
||||||
|
@Optional
|
||||||
|
public Property<Boolean> getBuildClean() {
|
||||||
|
return buildClean;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> buildCmdLine() {
|
||||||
|
List<String> parameters = new ArrayList<>();
|
||||||
|
parameters.add(executable.getOrElse("cmake"));
|
||||||
|
parameters.add("--build");
|
||||||
|
parameters.add(".");
|
||||||
|
if (buildConfig.isPresent()) {
|
||||||
|
parameters.add("--config");
|
||||||
|
parameters.add(buildConfig.get());
|
||||||
|
}
|
||||||
|
if (buildTarget.isPresent()) {
|
||||||
|
parameters.add("--target");
|
||||||
|
parameters.add(buildTarget.get());
|
||||||
|
}
|
||||||
|
if (buildClean.getOrElse(Boolean.FALSE)) {
|
||||||
|
parameters.add("--clean-first");
|
||||||
|
}
|
||||||
|
return parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
@TaskAction
|
||||||
|
public void build() {
|
||||||
|
CMakeExecutor executor = new CMakeExecutor(getLogger(), getName());
|
||||||
|
executor.exec(buildCmdLine(), workingFolder.getAsFile().get());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,191 @@
|
||||||
|
package org.xbib.gradle.plugin.cmake;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.gradle.api.DefaultTask;
|
||||||
|
import org.gradle.api.file.DirectoryProperty;
|
||||||
|
import org.gradle.api.provider.MapProperty;
|
||||||
|
import org.gradle.api.provider.Property;
|
||||||
|
import org.gradle.api.tasks.Input;
|
||||||
|
import org.gradle.api.tasks.InputDirectory;
|
||||||
|
import org.gradle.api.tasks.Optional;
|
||||||
|
import org.gradle.api.tasks.OutputDirectory;
|
||||||
|
import org.gradle.api.tasks.TaskAction;
|
||||||
|
|
||||||
|
public class CMakeConfigureTask extends DefaultTask {
|
||||||
|
|
||||||
|
private final Property<String> executable;
|
||||||
|
|
||||||
|
private final DirectoryProperty workingFolder;
|
||||||
|
|
||||||
|
private final DirectoryProperty sourceFolder;
|
||||||
|
|
||||||
|
private final Property<String> configurationTypes;
|
||||||
|
|
||||||
|
private final Property<String> installPrefix;
|
||||||
|
|
||||||
|
private final Property<String> generator; // for example: "Visual Studio 16 2019"
|
||||||
|
|
||||||
|
private final Property<String> platform; // for example "x64" or "Win32" or "ARM" or "ARM64", supported on vs > 8.0
|
||||||
|
|
||||||
|
private final Property<String> toolset; // for example "v142", supported on vs > 10.0
|
||||||
|
|
||||||
|
private final Property<Boolean> buildSharedLibs;
|
||||||
|
|
||||||
|
private final Property<Boolean> buildStaticLibs;
|
||||||
|
|
||||||
|
private final MapProperty<String, String> def;
|
||||||
|
|
||||||
|
public CMakeConfigureTask() {
|
||||||
|
setGroup("cmake");
|
||||||
|
setDescription("Configure a Build with CMake");
|
||||||
|
executable = getProject().getObjects().property(String.class);
|
||||||
|
workingFolder = getProject().getObjects().directoryProperty();
|
||||||
|
sourceFolder = getProject().getObjects().directoryProperty();
|
||||||
|
configurationTypes = getProject().getObjects().property(String.class);
|
||||||
|
installPrefix = getProject().getObjects().property(String.class);
|
||||||
|
generator = getProject().getObjects().property(String.class);
|
||||||
|
platform = getProject().getObjects().property(String.class);
|
||||||
|
toolset = getProject().getObjects().property(String.class);
|
||||||
|
buildSharedLibs = getProject().getObjects().property(Boolean.class);
|
||||||
|
buildStaticLibs = getProject().getObjects().property(Boolean.class);
|
||||||
|
def = getProject().getObjects().mapProperty(String.class, String.class);
|
||||||
|
// default values
|
||||||
|
workingFolder.set(new File(getProject().getLayout().getBuildDirectory().get().getAsFile(), "cmake"));
|
||||||
|
sourceFolder.set(new File(getProject().getLayout().getBuildDirectory().get().getAsFile(), "src" + File.separator + "main" + File.separator + "cpp"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void configureFromProject() {
|
||||||
|
CMakePluginExtension ext = (CMakePluginExtension) getProject().getExtensions().getByName("cmake");
|
||||||
|
executable.set(ext.getExecutable());
|
||||||
|
workingFolder.set(ext.getWorkingFolder());
|
||||||
|
sourceFolder.set(ext.getSourceFolder());
|
||||||
|
configurationTypes.set(ext.getConfigurationTypes());
|
||||||
|
installPrefix.set(ext.getInstallPrefix());
|
||||||
|
generator.set(ext.getGenerator());
|
||||||
|
platform.set(ext.getPlatform());
|
||||||
|
toolset.set(ext.getToolset());
|
||||||
|
buildSharedLibs.set(ext.getBuildSharedLibs());
|
||||||
|
buildStaticLibs.set(ext.getBuildStaticLibs());
|
||||||
|
def.set(ext.getDefs());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// region getters
|
||||||
|
@Input
|
||||||
|
@Optional
|
||||||
|
public Property<String> getExecutable() {
|
||||||
|
return executable;
|
||||||
|
}
|
||||||
|
|
||||||
|
@OutputDirectory
|
||||||
|
public DirectoryProperty getWorkingFolder() {
|
||||||
|
return workingFolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
@InputDirectory
|
||||||
|
public DirectoryProperty getSourceFolder() {
|
||||||
|
return sourceFolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Input
|
||||||
|
@Optional
|
||||||
|
public Property<String> getConfigurationTypes() {
|
||||||
|
return configurationTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Input
|
||||||
|
@Optional
|
||||||
|
public Property<String> getInstallPrefix() {
|
||||||
|
return installPrefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Input
|
||||||
|
@Optional
|
||||||
|
public Property<String> getGenerator() {
|
||||||
|
return generator;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Input
|
||||||
|
@Optional
|
||||||
|
public Property<String> getPlatform() {
|
||||||
|
return platform;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Input
|
||||||
|
@Optional
|
||||||
|
public Property<String> getToolset() {
|
||||||
|
return toolset;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Input
|
||||||
|
@Optional
|
||||||
|
public Property<Boolean> getBuildSharedLibs() {
|
||||||
|
return buildSharedLibs;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Input
|
||||||
|
@Optional
|
||||||
|
public Property<Boolean> getBuildStaticLibs() {
|
||||||
|
return buildStaticLibs;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Input
|
||||||
|
@Optional
|
||||||
|
public MapProperty<String, String> getDef() {
|
||||||
|
return def;
|
||||||
|
}
|
||||||
|
/// endregion
|
||||||
|
|
||||||
|
private List<String> buildCmdLine() {
|
||||||
|
List<String> parameters = new ArrayList<>();
|
||||||
|
|
||||||
|
parameters.add(executable.getOrElse("cmake"));
|
||||||
|
|
||||||
|
if (generator.isPresent() && !generator.get().isEmpty()) {
|
||||||
|
parameters.add("-G");
|
||||||
|
parameters.add(generator.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (platform.isPresent() && !platform.get().isEmpty()) {
|
||||||
|
parameters.add("-A");
|
||||||
|
parameters.add(platform.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toolset.isPresent() && !toolset.get().isEmpty()) {
|
||||||
|
parameters.add("-T");
|
||||||
|
parameters.add(toolset.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (configurationTypes.isPresent() && !configurationTypes.get().isEmpty())
|
||||||
|
parameters.add("-DCMAKE_CONFIGURATION_TYPES=" + configurationTypes.get());
|
||||||
|
|
||||||
|
if (installPrefix.isPresent() && !installPrefix.get().isEmpty())
|
||||||
|
parameters.add("-DCMAKE_INSTALL_PREFIX=" + installPrefix.get());
|
||||||
|
|
||||||
|
|
||||||
|
if (buildSharedLibs.isPresent())
|
||||||
|
parameters.add("-DBUILD_SHARED_LIBS=" + (buildSharedLibs.get() ? "ON" : "OFF"));
|
||||||
|
|
||||||
|
if (buildStaticLibs.isPresent())
|
||||||
|
parameters.add("-DBUILD_STATIC_LIBS=" + (buildStaticLibs.get() ? "ON" : "OFF"));
|
||||||
|
|
||||||
|
|
||||||
|
if (def.isPresent()) {
|
||||||
|
for (Map.Entry<String, String> entry : def.get().entrySet())
|
||||||
|
parameters.add("-D" + entry.getKey() + "=" + entry.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
parameters.add(sourceFolder.getAsFile().get().getAbsolutePath());
|
||||||
|
|
||||||
|
return parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
@TaskAction
|
||||||
|
public void configure() {
|
||||||
|
CMakeExecutor executor = new CMakeExecutor(getLogger(), getName());
|
||||||
|
executor.exec(buildCmdLine(), workingFolder.getAsFile().get());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
package org.xbib.gradle.plugin.cmake;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
import org.gradle.api.GradleException;
|
||||||
|
import org.gradle.api.GradleScriptException;
|
||||||
|
import org.gradle.api.logging.Logger;
|
||||||
|
|
||||||
|
public class CMakeExecutor {
|
||||||
|
|
||||||
|
private final Logger logger;
|
||||||
|
private final String taskName;
|
||||||
|
|
||||||
|
CMakeExecutor(final Logger logger, final String taskName) {
|
||||||
|
this.logger = logger;
|
||||||
|
this.taskName = taskName;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void exec(final List<String> cmdLine, final File workingFolder) throws GradleException {
|
||||||
|
// log command line parameters
|
||||||
|
StringBuilder sb = new StringBuilder(" CMakePlugin.task " + taskName + " - exec: ");
|
||||||
|
for (String s : cmdLine) {
|
||||||
|
sb.append(s).append(" ");
|
||||||
|
}
|
||||||
|
logger.info(sb.toString());
|
||||||
|
ProcessBuilder pb = new ProcessBuilder(cmdLine);
|
||||||
|
pb.directory(workingFolder);
|
||||||
|
try (ExecutorService executor = Executors.newFixedThreadPool(2)) {
|
||||||
|
// make sure working folder exists
|
||||||
|
boolean b = workingFolder.mkdirs();
|
||||||
|
// start
|
||||||
|
Process process = pb.start();
|
||||||
|
Future<Void> stdoutFuture = executor.submit(() -> {
|
||||||
|
readStream(process.getInputStream(), true);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
Future<Void> stderrFuture = executor.submit(() -> {
|
||||||
|
readStream(process.getErrorStream(), false);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
int retCode = process.waitFor();
|
||||||
|
warnIfTimeout(stdoutFuture,
|
||||||
|
"CMakeExecutor[" + taskName + "]Warn: timed out waiting for stdout to be closed.");
|
||||||
|
warnIfTimeout(stderrFuture,
|
||||||
|
"CMakeExecutor[" + taskName + "]Warn: timed out waiting for stderr to be closed.");
|
||||||
|
if (retCode != 0) {
|
||||||
|
throw new GradleException("[" + taskName + "]Error: CMAKE returned " + retCode);
|
||||||
|
}
|
||||||
|
} catch (IOException | InterruptedException | ExecutionException e) {
|
||||||
|
throw new GradleScriptException("CMakeExecutor[" + taskName + "].", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readStream(final InputStream inputStream, boolean isStdOut) {
|
||||||
|
final Stream<String> lines = new BufferedReader(new InputStreamReader(inputStream)).lines();
|
||||||
|
if (isStdOut) {
|
||||||
|
lines.forEach(logger::info);
|
||||||
|
} else {
|
||||||
|
lines.forEach(logger::error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void warnIfTimeout(final Future<Void> future, final String message)
|
||||||
|
throws ExecutionException, InterruptedException {
|
||||||
|
try {
|
||||||
|
future.get(3, TimeUnit.SECONDS);
|
||||||
|
} catch (TimeoutException e) {
|
||||||
|
logger.warn(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,141 @@
|
||||||
|
package org.xbib.gradle.plugin.cmake;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import org.gradle.api.GradleException;
|
||||||
|
import org.gradle.api.GradleScriptException;
|
||||||
|
import org.gradle.api.Plugin;
|
||||||
|
import org.gradle.api.Project;
|
||||||
|
import org.gradle.api.Task;
|
||||||
|
import org.gradle.api.logging.LogLevel;
|
||||||
|
import org.gradle.api.tasks.TaskContainer;
|
||||||
|
|
||||||
|
public class CMakePlugin implements Plugin<Project> {
|
||||||
|
|
||||||
|
private static final String CMAKE_CONFIGURE = "cmakeConfigure";
|
||||||
|
|
||||||
|
private static final String CMAKE_BUILD = "cmakeBuild";
|
||||||
|
|
||||||
|
private boolean deleteDirectory(File directoryToBeDeleted) {
|
||||||
|
File[] allContents = directoryToBeDeleted.listFiles();
|
||||||
|
if (allContents != null) {
|
||||||
|
for (File file : allContents) {
|
||||||
|
deleteDirectory(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return directoryToBeDeleted.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void apply(Project project) {
|
||||||
|
project.getPlugins().apply("base");
|
||||||
|
final CMakePluginExtension extension = project.getExtensions()
|
||||||
|
.create("cmake", CMakePluginExtension.class, project);
|
||||||
|
final Task cmakeClean = project.task("cmakeClean").doFirst(task -> {
|
||||||
|
File workingFolder = extension.getWorkingFolder().getAsFile().get().getAbsoluteFile();
|
||||||
|
if (workingFolder.exists()) {
|
||||||
|
project.getLogger().info("Deleting folder " + workingFolder);
|
||||||
|
if (!deleteDirectory(workingFolder)) {
|
||||||
|
throw new GradleException("Could not delete working folder " + workingFolder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
cmakeClean.setGroup("cmake");
|
||||||
|
cmakeClean.setDescription("Clean CMake configuration");
|
||||||
|
final Task cmakeGenerators = project.task("cmakeGenerators").doFirst(task -> {
|
||||||
|
ProcessBuilder pb = new ProcessBuilder(extension.getExecutable().getOrElse("cmake"),
|
||||||
|
"--help");
|
||||||
|
try {
|
||||||
|
// start
|
||||||
|
Process process = pb.start();
|
||||||
|
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
||||||
|
String line;
|
||||||
|
boolean foundGenerators = false;
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
if (line.equals("Generators")) {
|
||||||
|
foundGenerators = true;
|
||||||
|
}
|
||||||
|
if (foundGenerators) {
|
||||||
|
project.getLogger().log(LogLevel.QUIET, line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
process.waitFor();
|
||||||
|
} catch (IOException | InterruptedException e) {
|
||||||
|
throw new GradleScriptException("cmake --help failed.", e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
cmakeGenerators.setGroup("cmake");
|
||||||
|
cmakeGenerators.setDescription("List available CMake generators");
|
||||||
|
project.afterEvaluate(p -> {
|
||||||
|
final TaskContainer tasks = project.getTasks();
|
||||||
|
if (extension.getTargets().getTargetContainer().isEmpty()) {
|
||||||
|
p.getTasks().register(CMAKE_CONFIGURE, CMakeConfigureTask.class, task -> {
|
||||||
|
task.getExecutable().set(extension.getExecutable());
|
||||||
|
task.getWorkingFolder().set(extension.getWorkingFolder());
|
||||||
|
task.getSourceFolder().set(extension.getSourceFolder());
|
||||||
|
task.getConfigurationTypes().set(extension.getConfigurationTypes());
|
||||||
|
task.getInstallPrefix().set(extension.getInstallPrefix());
|
||||||
|
task.getGenerator().set(extension.getGenerator());
|
||||||
|
task.getPlatform().set(extension.getPlatform());
|
||||||
|
task.getToolset().set(extension.getToolset());
|
||||||
|
task.getBuildSharedLibs().set(extension.getBuildSharedLibs());
|
||||||
|
task.getBuildStaticLibs().set(extension.getBuildStaticLibs());
|
||||||
|
task.getDef().set(extension.getDefs().isPresent() ? extension.getDefs() : extension.getDef());
|
||||||
|
});
|
||||||
|
|
||||||
|
p.getTasks().register(CMAKE_BUILD, CMakeBuildTask.class, task -> {
|
||||||
|
task.getExecutable().set(extension.getExecutable());
|
||||||
|
task.getWorkingFolder().set(extension.getWorkingFolder());
|
||||||
|
task.getBuildConfig().set(extension.getBuildConfig());
|
||||||
|
task.getBuildTarget().set(extension.getBuildTarget());
|
||||||
|
task.getBuildClean().set(extension.getBuildClean());
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
extension.getTargets().getTargetContainer().getAsMap()
|
||||||
|
.forEach((name, target) -> {
|
||||||
|
tasks.register(CMAKE_CONFIGURE + name, CMakeConfigureTask.class, task -> {
|
||||||
|
task.configureFromProject();
|
||||||
|
if (target.getExecutable().isPresent())
|
||||||
|
task.getExecutable().set(target.getExecutable());
|
||||||
|
if (target.getWorkingFolder().isPresent())
|
||||||
|
task.getWorkingFolder().set(target.getWorkingFolder());
|
||||||
|
if (target.getSourceFolder().isPresent())
|
||||||
|
task.getSourceFolder().set(target.getSourceFolder());
|
||||||
|
if (target.getConfigurationTypes().isPresent())
|
||||||
|
task.getConfigurationTypes().set(target.getConfigurationTypes());
|
||||||
|
if (target.getInstallPrefix().isPresent())
|
||||||
|
task.getInstallPrefix().set(target.getInstallPrefix());
|
||||||
|
if (target.getGenerator().isPresent()) task.getGenerator().set(target.getGenerator());
|
||||||
|
if (target.getPlatform().isPresent()) task.getPlatform().set(target.getPlatform());
|
||||||
|
if (target.getToolset().isPresent()) task.getToolset().set(target.getToolset());
|
||||||
|
if (target.getBuildSharedLibs().isPresent())
|
||||||
|
task.getBuildSharedLibs().set(target.getBuildSharedLibs());
|
||||||
|
if (target.getBuildStaticLibs().isPresent())
|
||||||
|
task.getBuildStaticLibs().set(target.getBuildStaticLibs());
|
||||||
|
if (target.getDefs().isPresent()) task.getDef().set(target.getDefs());
|
||||||
|
});
|
||||||
|
tasks.register(CMAKE_BUILD + name, CMakeBuildTask.class, task -> {
|
||||||
|
task.configureFromProject();
|
||||||
|
if (target.getExecutable().isPresent())
|
||||||
|
task.getExecutable().set(target.getExecutable());
|
||||||
|
if (target.getWorkingFolder().isPresent())
|
||||||
|
task.getWorkingFolder().set(target.getWorkingFolder());
|
||||||
|
if (target.getBuildConfig().isPresent())
|
||||||
|
task.getBuildConfig().set(target.getBuildConfig());
|
||||||
|
if (target.getBuildTarget().isPresent())
|
||||||
|
task.getBuildTarget().set(target.getBuildTarget());
|
||||||
|
if (target.getBuildClean().isPresent())
|
||||||
|
task.getBuildClean().set(target.getBuildClean());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
tasks.withType(CMakeBuildTask.class)
|
||||||
|
.forEach(task -> task.dependsOn(tasks.withType(CMakeConfigureTask.class)));
|
||||||
|
p.getTasks().named("clean").configure(task -> task.dependsOn("cmakeClean"));
|
||||||
|
p.getTasks().named("build").configure(task -> task.dependsOn(tasks.withType(
|
||||||
|
CMakeBuildTask.class)));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,121 @@
|
||||||
|
package org.xbib.gradle.plugin.cmake;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import org.gradle.api.Project;
|
||||||
|
import org.gradle.api.file.DirectoryProperty;
|
||||||
|
import org.gradle.api.provider.MapProperty;
|
||||||
|
import org.gradle.api.provider.Property;
|
||||||
|
|
||||||
|
public class CMakePluginExtension {
|
||||||
|
|
||||||
|
// parameters used by config and build step
|
||||||
|
private final Property<String> executable;
|
||||||
|
private final DirectoryProperty workingFolder;
|
||||||
|
|
||||||
|
// parameters used by config step
|
||||||
|
private final DirectoryProperty sourceFolder;
|
||||||
|
private final Property<String> configurationTypes;
|
||||||
|
private final Property<String> installPrefix;
|
||||||
|
private final Property<String> generator; // for example: "Visual Studio 16 2019"
|
||||||
|
|
||||||
|
private final Property<String> platform; // for example "x64" or "Win32" or "ARM" or "ARM64", supported on vs > 8.0
|
||||||
|
private final Property<String> toolset; // for example "v142", supported on vs > 10.0
|
||||||
|
private final Property<Boolean> buildSharedLibs;
|
||||||
|
private final Property<Boolean> buildStaticLibs;
|
||||||
|
private final MapProperty<String, String> defs;
|
||||||
|
private final MapProperty<String, String> def;
|
||||||
|
|
||||||
|
// parameters used on build step
|
||||||
|
private final Property<String> buildConfig;
|
||||||
|
private final Property<String> buildTarget;
|
||||||
|
private final Property<Boolean> buildClean;
|
||||||
|
private final TargetListExtension targets;
|
||||||
|
private final Project project;
|
||||||
|
|
||||||
|
public CMakePluginExtension(Project project) {
|
||||||
|
executable = project.getObjects().property(String.class);
|
||||||
|
workingFolder = project.getObjects().directoryProperty();
|
||||||
|
sourceFolder = project.getObjects().directoryProperty();
|
||||||
|
configurationTypes = project.getObjects().property(String.class);
|
||||||
|
installPrefix = project.getObjects().property(String.class);
|
||||||
|
generator = project.getObjects().property(String.class);
|
||||||
|
platform = project.getObjects().property(String.class);
|
||||||
|
toolset = project.getObjects().property(String.class);
|
||||||
|
buildSharedLibs = project.getObjects().property(Boolean.class);
|
||||||
|
buildStaticLibs = project.getObjects().property(Boolean.class);
|
||||||
|
defs = project.getObjects().mapProperty(String.class, String.class);
|
||||||
|
def = project.getObjects().mapProperty(String.class, String.class); // for backwards compat
|
||||||
|
buildConfig = project.getObjects().property(String.class);
|
||||||
|
buildTarget = project.getObjects().property(String.class);
|
||||||
|
buildClean = project.getObjects().property(Boolean.class);
|
||||||
|
this.targets = project.getObjects().newInstance(TargetListExtension.class, project);
|
||||||
|
// default values
|
||||||
|
workingFolder.set(new File(project.getLayout().getBuildDirectory().get().getAsFile(), "cmake"));
|
||||||
|
sourceFolder.set(new File(project.getLayout().getBuildDirectory().get().getAsFile(), "src" + File.separator + "main" + File.separator + "cpp"));
|
||||||
|
this.project = project;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Property<String> getExecutable() {
|
||||||
|
return executable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DirectoryProperty getWorkingFolder() {
|
||||||
|
return workingFolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DirectoryProperty getSourceFolder() {
|
||||||
|
return sourceFolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Property<String> getConfigurationTypes() {
|
||||||
|
return configurationTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Property<String> getInstallPrefix() {
|
||||||
|
return installPrefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Property<String> getGenerator() {
|
||||||
|
return generator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Property<String> getPlatform() {
|
||||||
|
return platform;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Property<String> getToolset() {
|
||||||
|
return toolset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Property<Boolean> getBuildSharedLibs() {
|
||||||
|
return buildSharedLibs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Property<Boolean> getBuildStaticLibs() {
|
||||||
|
return buildStaticLibs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MapProperty<String, String> getDef() {
|
||||||
|
return def;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MapProperty<String, String> getDefs() {
|
||||||
|
return defs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Property<String> getBuildConfig() {
|
||||||
|
return buildConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Property<String> getBuildTarget() {
|
||||||
|
return buildTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Property<Boolean> getBuildClean() {
|
||||||
|
return buildClean;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TargetListExtension getTargets() {
|
||||||
|
return targets;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,174 @@
|
||||||
|
package org.xbib.gradle.plugin.cmake;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.gradle.api.Project;
|
||||||
|
import org.gradle.api.file.DirectoryProperty;
|
||||||
|
import org.gradle.api.provider.MapProperty;
|
||||||
|
import org.gradle.api.provider.Property;
|
||||||
|
|
||||||
|
public class TargetExtension {
|
||||||
|
|
||||||
|
// parameters used by config and build step
|
||||||
|
private final Property<String> executable;
|
||||||
|
private final DirectoryProperty workingFolder;
|
||||||
|
|
||||||
|
// parameters used by config step
|
||||||
|
private final DirectoryProperty sourceFolder;
|
||||||
|
private final Property<String> configurationTypes;
|
||||||
|
private final Property<String> installPrefix;
|
||||||
|
private final Property<String> generator; // for example: "Visual Studio 16 2019"
|
||||||
|
private final Property<String> platform; // for example "x64" or "Win32" or "ARM" or "ARM64", supported on vs > 8.0
|
||||||
|
private final Property<String> toolset; // for example "v142", supported on vs > 10.0
|
||||||
|
private final Property<Boolean> buildSharedLibs;
|
||||||
|
private final Property<Boolean> buildStaticLibs;
|
||||||
|
private final MapProperty<String, String> defs;
|
||||||
|
|
||||||
|
// parameters used on build step
|
||||||
|
private final Property<String> buildConfig;
|
||||||
|
private final Property<String> buildTarget;
|
||||||
|
private final Property<Boolean> buildClean;
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
public TargetExtension(final Project project, final String name) {
|
||||||
|
executable = project.getObjects().property(String.class);
|
||||||
|
workingFolder = project.getObjects().directoryProperty();
|
||||||
|
sourceFolder = project.getObjects().directoryProperty();
|
||||||
|
configurationTypes = project.getObjects().property(String.class);
|
||||||
|
installPrefix = project.getObjects().property(String.class);
|
||||||
|
generator = project.getObjects().property(String.class);
|
||||||
|
platform = project.getObjects().property(String.class);
|
||||||
|
toolset = project.getObjects().property(String.class);
|
||||||
|
buildSharedLibs = project.getObjects().property(Boolean.class);
|
||||||
|
buildStaticLibs = project.getObjects().property(Boolean.class);
|
||||||
|
defs = project.getObjects().mapProperty(String.class, String.class);
|
||||||
|
buildConfig = project.getObjects().property(String.class);
|
||||||
|
buildTarget = project.getObjects().property(String.class);
|
||||||
|
buildClean = project.getObjects().property(Boolean.class);
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Property<String> getExecutable() {
|
||||||
|
return executable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DirectoryProperty getWorkingFolder() {
|
||||||
|
return workingFolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DirectoryProperty getSourceFolder() {
|
||||||
|
return sourceFolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Property<String> getConfigurationTypes() {
|
||||||
|
return configurationTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Property<String> getInstallPrefix() {
|
||||||
|
return installPrefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Property<String> getGenerator() {
|
||||||
|
return generator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Property<String> getPlatform() {
|
||||||
|
return platform;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Property<String> getToolset() {
|
||||||
|
return toolset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Property<Boolean> getBuildSharedLibs() {
|
||||||
|
return buildSharedLibs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Property<Boolean> getBuildStaticLibs() {
|
||||||
|
return buildStaticLibs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MapProperty<String, String> getDefs() {
|
||||||
|
return defs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Property<String> getBuildConfig() {
|
||||||
|
return buildConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Property<String> getBuildTarget() {
|
||||||
|
return buildTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Property<Boolean> getBuildClean() {
|
||||||
|
return buildClean;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setExecutable(String executable) {
|
||||||
|
this.executable.set(executable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWorkingFolder(File workingFolder) {
|
||||||
|
this.workingFolder.set(workingFolder);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWorkingFolder(String workingFolder) {
|
||||||
|
this.workingFolder.set(new File(workingFolder));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSourceFolder(File sourceFolder) {
|
||||||
|
this.sourceFolder.set(sourceFolder);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSourceFolder(String sourceFolder) {
|
||||||
|
this.sourceFolder.set(new File(sourceFolder));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setConfigurationTypes(String configurationTypes) {
|
||||||
|
this.configurationTypes.set(configurationTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInstallPrefix(String installPrefix) {
|
||||||
|
this.installPrefix.set(installPrefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGenerator(String generator) {
|
||||||
|
this.generator.set(generator);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPlatform(String platform) {
|
||||||
|
this.platform.set(platform);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setToolset(String toolset) {
|
||||||
|
this.toolset.set(toolset);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBuildSharedLibs(Boolean buildSharedLibs) {
|
||||||
|
this.buildSharedLibs.set(buildSharedLibs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBuildStaticLibs(Boolean buildStaticLibs) {
|
||||||
|
this.buildStaticLibs.set(buildStaticLibs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDefs(Map<String, String> defs) {
|
||||||
|
this.defs.set(defs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBuildConfig(String buildConfig) {
|
||||||
|
this.buildConfig.set(buildConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBuildTarget(String buildTarget) {
|
||||||
|
this.buildTarget.set(buildTarget);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBuildClean(Boolean buildClean) {
|
||||||
|
this.buildClean.set(buildClean);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
package org.xbib.gradle.plugin.cmake;
|
||||||
|
|
||||||
|
import groovy.lang.Closure;
|
||||||
|
import groovy.lang.MissingMethodException;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import org.gradle.api.NamedDomainObjectContainer;
|
||||||
|
import org.gradle.api.Project;
|
||||||
|
|
||||||
|
public class TargetListExtension {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public TargetListExtension(Project project) {
|
||||||
|
targetContainer = project.container(TargetExtension.class, name -> new TargetExtension(project, name));
|
||||||
|
}
|
||||||
|
|
||||||
|
private final NamedDomainObjectContainer<TargetExtension> targetContainer;
|
||||||
|
|
||||||
|
public NamedDomainObjectContainer<TargetExtension> getTargetContainer() {
|
||||||
|
return targetContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object methodMissing(String name, Object args) {
|
||||||
|
if (args instanceof Object[] && ((Object[]) args)[0] instanceof Closure<?> closure) {
|
||||||
|
return targetContainer.create(name, closure);
|
||||||
|
} else {
|
||||||
|
final Object[] normalizedArgs;
|
||||||
|
normalizedArgs = args instanceof Object[] ? (Object[]) args : new Object[]{args};
|
||||||
|
throw new MissingMethodException(name, this.getClass(), normalizedArgs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
package org.xbib.gradle.plugin.cmake.test
|
||||||
|
|
||||||
|
import org.gradle.testkit.runner.BuildResult
|
||||||
|
import org.gradle.testkit.runner.GradleRunner
|
||||||
|
import org.gradle.testkit.runner.TaskOutcome
|
||||||
|
import org.junit.jupiter.api.BeforeEach
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.junit.jupiter.api.condition.DisabledOnOs
|
||||||
|
import org.junit.jupiter.api.condition.OS
|
||||||
|
import org.junit.jupiter.api.io.TempDir
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals
|
||||||
|
|
||||||
|
@DisabledOnOs(OS.MAC)
|
||||||
|
class CmakePluginIntegrationTest {
|
||||||
|
|
||||||
|
private File projectDir
|
||||||
|
|
||||||
|
private File settingsFile
|
||||||
|
|
||||||
|
private File buildFile
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setup(@TempDir File testProjectDir) throws IOException {
|
||||||
|
this.projectDir = testProjectDir
|
||||||
|
File f = new File(testProjectDir, 'build/src/main/cpp')
|
||||||
|
f.mkdirs()
|
||||||
|
File cmakeList = new File(f, 'CMakeLists.txt')
|
||||||
|
cmakeList.write('')
|
||||||
|
this.settingsFile = new File(testProjectDir, "settings.gradle")
|
||||||
|
this.buildFile = new File(testProjectDir, "build.gradle")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testPlugin() {
|
||||||
|
String settingsFileContent = '''
|
||||||
|
rootProject.name = 'cmake-test'
|
||||||
|
'''
|
||||||
|
settingsFile.write(settingsFileContent)
|
||||||
|
String buildFileContent = '''
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id 'org.xbib.gradle.plugin.cmake'
|
||||||
|
}
|
||||||
|
|
||||||
|
'''
|
||||||
|
buildFile.write(buildFileContent)
|
||||||
|
BuildResult result = GradleRunner.create()
|
||||||
|
.withProjectDir(projectDir)
|
||||||
|
.withPluginClasspath()
|
||||||
|
.withArguments(":cmakeConfigure", "--info", "--stacktrace", "--warning-mode=all")
|
||||||
|
.forwardOutput()
|
||||||
|
.build()
|
||||||
|
assertEquals(TaskOutcome.SUCCESS, result.task(":cmakeConfigure").getOutcome())
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,12 +1,11 @@
|
||||||
plugins {
|
plugins {
|
||||||
id 'java-gradle-plugin'
|
|
||||||
alias(libs.plugins.publish)
|
alias(libs.plugins.publish)
|
||||||
}
|
}
|
||||||
|
|
||||||
apply plugin: 'java-gradle-plugin'
|
|
||||||
apply plugin: 'com.gradle.plugin-publish'
|
apply plugin: 'com.gradle.plugin-publish'
|
||||||
|
|
||||||
apply from: rootProject.file('gradle/compile/groovy.gradle')
|
apply from: rootProject.file('gradle/compile/groovy.gradle')
|
||||||
|
apply from: rootProject.file('gradle/test/junit5.gradle')
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
api gradleApi()
|
api gradleApi()
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
name = gradle-plugin-docker
|
name = gradle-plugin-docker
|
||||||
version = 3.0.2
|
version = 3.1.0
|
||||||
|
|
|
@ -5,10 +5,13 @@ import org.gradle.testkit.runner.GradleRunner
|
||||||
import org.gradle.testkit.runner.TaskOutcome
|
import org.gradle.testkit.runner.TaskOutcome
|
||||||
import org.junit.jupiter.api.BeforeEach
|
import org.junit.jupiter.api.BeforeEach
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.junit.jupiter.api.condition.DisabledOnOs
|
||||||
|
import org.junit.jupiter.api.condition.OS
|
||||||
import org.junit.jupiter.api.io.TempDir
|
import org.junit.jupiter.api.io.TempDir
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals
|
import static org.junit.jupiter.api.Assertions.assertEquals
|
||||||
|
|
||||||
|
@DisabledOnOs(OS.MAC)
|
||||||
class DockerPluginIntegrationTest {
|
class DockerPluginIntegrationTest {
|
||||||
|
|
||||||
private File projectDir
|
private File projectDir
|
||||||
|
|
|
@ -7,12 +7,13 @@ apply plugin: 'java-gradle-plugin'
|
||||||
apply plugin: 'com.gradle.plugin-publish'
|
apply plugin: 'com.gradle.plugin-publish'
|
||||||
|
|
||||||
apply from: rootProject.file('gradle/compile/groovy.gradle')
|
apply from: rootProject.file('gradle/compile/groovy.gradle')
|
||||||
|
apply from: rootProject.file('gradle/test/junit5.gradle')
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
api gradleApi()
|
api gradleApi()
|
||||||
api libs.groovy.git
|
api libs.groovy.git
|
||||||
testImplementation gradleTestKit()
|
testImplementation gradleTestKit()
|
||||||
testImplementation libs.spock.core
|
testImplementation testLibs.spock.core
|
||||||
testImplementation testLibs.junit4
|
testImplementation testLibs.junit4
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
name = gradle-plugin-docker
|
name = gradle-plugin-docker
|
||||||
version = 3.0.2
|
version = 3.1.0
|
||||||
|
|
34
gradle-plugin-j2html/build.gradle
Executable file
34
gradle-plugin-j2html/build.gradle
Executable file
|
@ -0,0 +1,34 @@
|
||||||
|
plugins {
|
||||||
|
id 'java-gradle-plugin'
|
||||||
|
alias(libs.plugins.publish)
|
||||||
|
}
|
||||||
|
|
||||||
|
apply plugin: 'java-gradle-plugin'
|
||||||
|
apply plugin: 'com.gradle.plugin-publish'
|
||||||
|
|
||||||
|
apply from: rootProject.file('gradle/compile/groovy.gradle')
|
||||||
|
apply from: rootProject.file('gradle/test/junit5.gradle')
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
api gradleApi()
|
||||||
|
implementation libs.jsoup
|
||||||
|
implementation libs.javapoet
|
||||||
|
testImplementation gradleTestKit()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (project.hasProperty('gradle.publish.key')) {
|
||||||
|
gradlePlugin {
|
||||||
|
website = 'https://xbib.org/joerg/gradle-plugins/src/branch/main/gradle-plugin-j2html'
|
||||||
|
vcsUrl = 'https://xbib.org/joerg/gradle-plugins'
|
||||||
|
plugins {
|
||||||
|
j2htmlPlugin {
|
||||||
|
id = 'org.xbib.gradle.plugin.j2html'
|
||||||
|
implementationClass = 'org.xbib.gradle.plugin.j2html.J2HtmlPlugin'
|
||||||
|
version = project.version
|
||||||
|
description = 'Gradle J2HTML plugin'
|
||||||
|
displayName = 'Gradle J2HTML plugin'
|
||||||
|
tags.set(['j2html'])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package com.j2html.codegen;
|
||||||
|
|
||||||
|
import com.j2html.codegen.generators.SpecializedTagClassCodeGenerator;
|
||||||
|
import com.j2html.codegen.generators.AttributeInterfaceCodeGenerator;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
|
public final class App
|
||||||
|
{
|
||||||
|
public static void main( String[] args )
|
||||||
|
{
|
||||||
|
final Path relPath = Paths.get("../library/src/main/java/j2html/");
|
||||||
|
final Path absPath = relPath.toAbsolutePath();
|
||||||
|
|
||||||
|
System.out.println("writing in "+absPath);
|
||||||
|
|
||||||
|
//decide if the files should be
|
||||||
|
//deleted or generated
|
||||||
|
final boolean delete = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
AttributeInterfaceCodeGenerator.generate(absPath, delete);
|
||||||
|
SpecializedTagClassCodeGenerator.generate(absPath, delete);
|
||||||
|
//TagCreatorCodeGenerator.print();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
//don't forget to auto-reformat the generated code.
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
package com.j2html.codegen;
|
||||||
|
|
||||||
|
import com.j2html.codegen.model.AttrD;
|
||||||
|
|
||||||
|
import static com.j2html.codegen.generators.TagCreatorCodeGenerator.containerTags;
|
||||||
|
import static com.j2html.codegen.generators.TagCreatorCodeGenerator.emptyTags;
|
||||||
|
import static com.j2html.codegen.model.AttributesList.attributesDescriptive;
|
||||||
|
import static com.j2html.codegen.model.AttributesList.getCustomAttributesForHtmlTag;
|
||||||
|
import static java.lang.System.*;
|
||||||
|
|
||||||
|
public class Export {
|
||||||
|
|
||||||
|
public static void main(String[] args){
|
||||||
|
for (final String tag : emptyTags()) {
|
||||||
|
out.print("EMPTY-ELEMENT[");
|
||||||
|
out.print(tag);
|
||||||
|
out.print("]");
|
||||||
|
out.println();
|
||||||
|
}
|
||||||
|
for (final String tag : containerTags()) {
|
||||||
|
out.print("ELEMENT[");
|
||||||
|
out.print(tag);
|
||||||
|
out.print("]");
|
||||||
|
out.println();
|
||||||
|
}
|
||||||
|
|
||||||
|
out.println();
|
||||||
|
|
||||||
|
for(AttrD attr : attributesDescriptive()){
|
||||||
|
if(attr.hasArgument){
|
||||||
|
out.print("STRING");
|
||||||
|
}else{
|
||||||
|
out.print("BOOLEAN");
|
||||||
|
}
|
||||||
|
out.print("[");
|
||||||
|
out.print(attr.attr);
|
||||||
|
out.print("]");
|
||||||
|
out.println();
|
||||||
|
for(String tag : attr.tags){
|
||||||
|
out.print("ATTRIBUTE[");
|
||||||
|
out.print(tag);
|
||||||
|
out.print(":");
|
||||||
|
out.print(attr.attr);
|
||||||
|
out.print("]");
|
||||||
|
out.println();
|
||||||
|
}
|
||||||
|
out.println();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,199 @@
|
||||||
|
package com.j2html.codegen;
|
||||||
|
|
||||||
|
import com.j2html.codegen.Model.Node;
|
||||||
|
import com.squareup.javapoet.*;
|
||||||
|
|
||||||
|
import javax.lang.model.element.Modifier;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static com.j2html.codegen.Model.Metadata.ON_OFF;
|
||||||
|
import static com.j2html.codegen.Model.Metadata.SELF_CLOSING;
|
||||||
|
|
||||||
|
public class Generator {
|
||||||
|
|
||||||
|
public static final ClassName INSTANCE = ClassName.get("j2html.tags", "IInstance");
|
||||||
|
public static final ClassName TAG = ClassName.get("j2html.tags", "Tag");
|
||||||
|
public static final ClassName EMPTY_TAG = ClassName.get("j2html.tags", "EmptyTag");
|
||||||
|
public static final ClassName CONTAINER_TAG = ClassName.get("j2html.tags", "ContainerTag");
|
||||||
|
|
||||||
|
public static void main(String... args) throws IOException {
|
||||||
|
Path path = Paths.get("j2html-codegen", "src", "test", "resources", "html.model");
|
||||||
|
String definitions = new String(Files.readAllBytes(path));
|
||||||
|
Model model = new Model();
|
||||||
|
Parser.parse(definitions, model);
|
||||||
|
|
||||||
|
Path dir = Paths.get("/j2html/generated-source");
|
||||||
|
Files.createDirectories(dir);
|
||||||
|
generate(dir, "j2html.tags.attributes", "j2html.tags.specialized", model);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void generate(Path root, String attributePkg, String elementPkg, Model model) throws IOException {
|
||||||
|
Map<String, JavaFile> attributes = generateAttributePackage(attributePkg, model);
|
||||||
|
for (JavaFile file : attributes.values()) {
|
||||||
|
file.writeTo(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, JavaFile> elements = generateElementPackage(elementPkg, model, attributes);
|
||||||
|
for (JavaFile file : elements.values()) {
|
||||||
|
file.writeTo(root);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Map<String, JavaFile> generateElementPackage(String pkg, Model model, Map<String, JavaFile> attributes) {
|
||||||
|
Map<String, JavaFile> files = new HashMap<>();
|
||||||
|
|
||||||
|
// Convert all elements into classes.
|
||||||
|
for (Node element : model.elements()) {
|
||||||
|
ClassName className = ClassName.get(pkg, capitalize(element.name) + "Tag");
|
||||||
|
|
||||||
|
TypeSpec.Builder type = defineElementClass(element, className);
|
||||||
|
|
||||||
|
// Assign attributes to this element.
|
||||||
|
for (Node attribute : element.children) {
|
||||||
|
JavaFile file = attributes.get(attribute.name);
|
||||||
|
type.addSuperinterface(
|
||||||
|
ParameterizedTypeName.get(
|
||||||
|
ClassName.get(file.packageName, file.typeSpec.name),
|
||||||
|
className
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
files.put(
|
||||||
|
element.name,
|
||||||
|
JavaFile.builder(pkg, type.build())
|
||||||
|
.skipJavaLangImports(true)
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Map<String, JavaFile> generateAttributePackage(String pkg, Model model) {
|
||||||
|
Map<String, JavaFile> files = new HashMap<>();
|
||||||
|
|
||||||
|
// Convert all attributes into classes.
|
||||||
|
for (Node attribute : model.attributes()) {
|
||||||
|
TypeSpec.Builder type = defineAttributeClass(pkg, attribute);
|
||||||
|
|
||||||
|
if (attribute.type.equals(Node.Type.STRING)) {
|
||||||
|
defineStringAttributeMethods(attribute, type);
|
||||||
|
} else if (attribute.type.equals(Node.Type.BOOLEAN) && !attribute.is(ON_OFF)) {
|
||||||
|
defineBooleanAttributeMethods(attribute, type);
|
||||||
|
} else if (attribute.type.equals(Node.Type.BOOLEAN) && attribute.is(ON_OFF)) {
|
||||||
|
defineOnOffAttributeMethods(attribute, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
files.put(
|
||||||
|
attribute.name,
|
||||||
|
JavaFile.builder(pkg, type.build())
|
||||||
|
.skipJavaLangImports(true)
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TypeSpec.Builder defineElementClass(Node element, ClassName className) {
|
||||||
|
MethodSpec constructor = MethodSpec.constructorBuilder()
|
||||||
|
.addModifiers(Modifier.PUBLIC)
|
||||||
|
.addStatement("super(\"" + element.name + "\")")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
TypeSpec.Builder type = TypeSpec.classBuilder(className)
|
||||||
|
.addModifiers(Modifier.PUBLIC)
|
||||||
|
.superclass(
|
||||||
|
ParameterizedTypeName.get(element.is(SELF_CLOSING) ? EMPTY_TAG : CONTAINER_TAG, className)
|
||||||
|
)
|
||||||
|
.addMethod(constructor);
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TypeSpec.Builder defineAttributeClass(String pkg, Node attribute) {
|
||||||
|
ClassName name = ClassName.get(pkg, "I" + capitalize(attribute.name));
|
||||||
|
return TypeSpec.interfaceBuilder(name)
|
||||||
|
.addSuperinterface(ParameterizedTypeName.get(INSTANCE, TypeVariableName.get("T")))
|
||||||
|
.addTypeVariable(TypeVariableName.get("T", ParameterizedTypeName.get(TAG, TypeVariableName.get("T"))))
|
||||||
|
.addModifiers(Modifier.PUBLIC);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void defineBooleanAttributeMethods(Node attribute, TypeSpec.Builder type) {
|
||||||
|
MethodSpec with = MethodSpec.methodBuilder(methodName("is", attribute.name))
|
||||||
|
.addModifiers(Modifier.PUBLIC, Modifier.DEFAULT)
|
||||||
|
.addStatement("return self().attr(\"" + attribute.name + "\")")
|
||||||
|
.returns(TypeVariableName.get("T"))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
MethodSpec withCond = MethodSpec.methodBuilder(methodName("withCond", attribute.name))
|
||||||
|
.addModifiers(Modifier.PUBLIC, Modifier.DEFAULT)
|
||||||
|
.addParameter(TypeName.BOOLEAN, "enable", Modifier.FINAL)
|
||||||
|
.addStatement("return enable ? self().attr(\"" + attribute.name + "\") : self()")
|
||||||
|
.returns(TypeVariableName.get("T"))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
type.addMethod(with);
|
||||||
|
type.addMethod(withCond);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void defineOnOffAttributeMethods(Node attribute, TypeSpec.Builder type) {
|
||||||
|
MethodSpec with = MethodSpec.methodBuilder(methodName("is", attribute.name))
|
||||||
|
.addModifiers(Modifier.PUBLIC, Modifier.DEFAULT)
|
||||||
|
.addStatement("return self().attr(\"" + attribute.name + "\", \"on\")")
|
||||||
|
.returns(TypeVariableName.get("T"))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
MethodSpec withCond = MethodSpec.methodBuilder(methodName("withCond", attribute.name))
|
||||||
|
.addModifiers(Modifier.PUBLIC, Modifier.DEFAULT)
|
||||||
|
.addParameter(TypeName.BOOLEAN, "enable", Modifier.FINAL)
|
||||||
|
.addStatement("return enable ? self().attr(\"" + attribute.name + "\", \"on\") : self()")
|
||||||
|
.returns(TypeVariableName.get("T"))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
type.addMethod(with);
|
||||||
|
type.addMethod(withCond);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void defineStringAttributeMethods(Node attribute, TypeSpec.Builder type) {
|
||||||
|
MethodSpec with = MethodSpec.methodBuilder(methodName("with", attribute.name))
|
||||||
|
.addModifiers(Modifier.PUBLIC, Modifier.DEFAULT)
|
||||||
|
.addParameter(String.class, parameter(attribute), Modifier.FINAL)
|
||||||
|
.addStatement("return self().attr(\"" + attribute.name + "\", " + parameter(attribute) + ")")
|
||||||
|
.returns(TypeVariableName.get("T"))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
MethodSpec withCond = MethodSpec.methodBuilder(methodName("withCond", attribute.name))
|
||||||
|
.addModifiers(Modifier.PUBLIC, Modifier.DEFAULT)
|
||||||
|
.addParameter(TypeName.BOOLEAN, "enable", Modifier.FINAL)
|
||||||
|
.addParameter(String.class, parameter(attribute), Modifier.FINAL)
|
||||||
|
.addStatement("return enable ? self().attr(\"" + attribute.name + "\", " + parameter(attribute) + ") : self()")
|
||||||
|
.returns(TypeVariableName.get("T"))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
type.addMethod(with);
|
||||||
|
type.addMethod(withCond);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String parameter(Node attribute) {
|
||||||
|
return attribute.name + "_";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String methodName(String... words) {
|
||||||
|
String[] camelCase = new String[words.length];
|
||||||
|
camelCase[0] = words[0];
|
||||||
|
for (int i = 1; i < words.length; i++) {
|
||||||
|
camelCase[i] = capitalize(words[i]);
|
||||||
|
}
|
||||||
|
return String.join("", camelCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String capitalize(String word) {
|
||||||
|
return word.substring(0, 1).toUpperCase() + word.substring(1).toLowerCase();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package com.j2html.codegen;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
public final class GeneratorUtil {
|
||||||
|
|
||||||
|
public static final void deleteAllFilesInDir(final Path dir) throws IOException {
|
||||||
|
for(final File file : dir.toFile().listFiles()){
|
||||||
|
System.out.println("deleting " + file.toPath());
|
||||||
|
Files.delete(file.toPath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
171
gradle-plugin-j2html/src/main/java/com/j2html/codegen/Model.java
Normal file
171
gradle-plugin-j2html/src/main/java/com/j2html/codegen/Model.java
Normal file
|
@ -0,0 +1,171 @@
|
||||||
|
package com.j2html.codegen;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import static com.j2html.codegen.Model.Metadata.ON_OFF;
|
||||||
|
import static com.j2html.codegen.Model.Metadata.SELF_CLOSING;
|
||||||
|
import static com.j2html.codegen.Model.Node.Type.*;
|
||||||
|
|
||||||
|
public class Model implements Parser.Listener {
|
||||||
|
|
||||||
|
private Map<String, Node> elements;
|
||||||
|
private Map<String, Node> attributes;
|
||||||
|
|
||||||
|
public Model() {
|
||||||
|
elements = new LinkedHashMap<>();
|
||||||
|
attributes = new LinkedHashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<Node> elements(){
|
||||||
|
return elements.values();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<Node> attributes(){
|
||||||
|
return attributes.values();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Node addElement(String name) {
|
||||||
|
return add(ELEMENT, name, elements);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Node addBooleanAttribute(String name) {
|
||||||
|
return add(BOOLEAN, name, attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Node addStringAttribute(String name) {
|
||||||
|
return add(STRING, name, attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Node element(String name) {
|
||||||
|
if (!elements.containsKey(name)) {
|
||||||
|
throw new NodeDoesNotExist(name);
|
||||||
|
}
|
||||||
|
return elements.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Node attribute(String name) {
|
||||||
|
if (!attributes.containsKey(name)) {
|
||||||
|
throw new NodeDoesNotExist(name);
|
||||||
|
}
|
||||||
|
return attributes.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Node add(Node.Type type, String name, Map<String, Node> nodes) {
|
||||||
|
if (nodes.containsKey(name)) {
|
||||||
|
throw new NodeAlreadyExists(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
Node node = new Node(type, name);
|
||||||
|
nodes.put(name, node);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void lineCommented(int line, String txt) {
|
||||||
|
// Ignore.
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void elementDefined(int line, String name) {
|
||||||
|
attempt(() -> addElement(name), line);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void emptyElementDefined(int line, String name) {
|
||||||
|
attempt(() -> addElement(name).annotate(SELF_CLOSING), line);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void booleanDefined(int line, String name) {
|
||||||
|
attempt(() -> addBooleanAttribute(name), line);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onOffDefined(int line, String name) {
|
||||||
|
attempt(() -> addBooleanAttribute(name).annotate(ON_OFF), line);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stringDefined(int line, String name) {
|
||||||
|
attempt(() -> addStringAttribute(name), line);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void attributeDefined(int line, String element, String name) {
|
||||||
|
attempt(() -> element(element).addChild(attribute(name)), line);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void invalidLine(int line, String txt) {
|
||||||
|
throw new RuntimeException("Invalid line [" + line + "]: " + txt);
|
||||||
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
private interface Unsafe {
|
||||||
|
void call() throws RuntimeException;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void attempt(Unsafe operation, int line) {
|
||||||
|
try {
|
||||||
|
operation.call();
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
throw new InvalidModel(e, line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Node {
|
||||||
|
enum Type {
|
||||||
|
ELEMENT,
|
||||||
|
BOOLEAN,
|
||||||
|
STRING
|
||||||
|
}
|
||||||
|
|
||||||
|
public final Type type;
|
||||||
|
public final String name;
|
||||||
|
public final List<Metadata> metadata;
|
||||||
|
public final List<Node> children;
|
||||||
|
|
||||||
|
private Node(Type type, String name) {
|
||||||
|
this.type = type;
|
||||||
|
this.name = name;
|
||||||
|
this.metadata = new ArrayList<>();
|
||||||
|
this.children = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void annotate(Metadata meta) {
|
||||||
|
metadata.add(meta);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addChild(Node node) {
|
||||||
|
children.add(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean is(Metadata annotation){
|
||||||
|
return metadata.contains(annotation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Metadata {
|
||||||
|
SELF_CLOSING,
|
||||||
|
ON_OFF,
|
||||||
|
OBSOLETE
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class InvalidModel extends RuntimeException {
|
||||||
|
public InvalidModel(Exception cause, int line) {
|
||||||
|
super(cause.getMessage() + ". At line " + line, cause);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class NodeAlreadyExists extends RuntimeException {
|
||||||
|
public NodeAlreadyExists(String name) {
|
||||||
|
super("Node already exists: " + name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class NodeDoesNotExist extends RuntimeException {
|
||||||
|
public NodeDoesNotExist(String name) {
|
||||||
|
super("Node does not exist: " + name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,91 @@
|
||||||
|
package com.j2html.codegen;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public class Parser {
|
||||||
|
|
||||||
|
private static final Pattern EMPTY_LINE_PATTERN = Pattern.compile("\\s*");
|
||||||
|
private static final Pattern COMMENT_PATTERN = Pattern.compile("#.*");
|
||||||
|
private static final Pattern NODE_PATTERN = Pattern.compile("(?<type>ELEMENT|EMPTY-ELEMENT|BOOLEAN|ONOFF|STRING)\\[(?<name>\\S+)\\]");
|
||||||
|
private static final Pattern ATTRIBUTE_PATTERN = Pattern.compile("ATTRIBUTE\\[(?<element>\\S+):(?<name>\\S+)\\]");
|
||||||
|
|
||||||
|
public interface Listener {
|
||||||
|
void lineCommented(int line, String txt);
|
||||||
|
|
||||||
|
void elementDefined(int line, String name);
|
||||||
|
|
||||||
|
void emptyElementDefined(int line, String name);
|
||||||
|
|
||||||
|
void booleanDefined(int line, String name);
|
||||||
|
|
||||||
|
void onOffDefined(int line, String name);
|
||||||
|
|
||||||
|
void stringDefined(int line, String name);
|
||||||
|
|
||||||
|
void attributeDefined(int line, String element, String name);
|
||||||
|
|
||||||
|
void invalidLine(int line, String txt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void parse(String txt, Listener listener) {
|
||||||
|
String[] lines = txt.split("[\r\n]+");
|
||||||
|
|
||||||
|
for (int i = 0; i < lines.length; i++) {
|
||||||
|
int number = i + 1;
|
||||||
|
String line = lines[i];
|
||||||
|
|
||||||
|
if (match(EMPTY_LINE_PATTERN, line)) continue;
|
||||||
|
|
||||||
|
if (match(COMMENT_PATTERN, line, matcher -> {
|
||||||
|
listener.lineCommented(number, line);
|
||||||
|
})) continue;
|
||||||
|
|
||||||
|
if (match(NODE_PATTERN, line, matcher -> {
|
||||||
|
String type = matcher.group("type");
|
||||||
|
String name = matcher.group("name");
|
||||||
|
switch (type) {
|
||||||
|
case "ELEMENT":
|
||||||
|
listener.elementDefined(number, name);
|
||||||
|
break;
|
||||||
|
case "EMPTY-ELEMENT":
|
||||||
|
listener.emptyElementDefined(number, name);
|
||||||
|
break;
|
||||||
|
case "BOOLEAN":
|
||||||
|
listener.booleanDefined(number, name);
|
||||||
|
break;
|
||||||
|
case "ONOFF":
|
||||||
|
listener.onOffDefined(number, name);
|
||||||
|
break;
|
||||||
|
case "STRING":
|
||||||
|
listener.stringDefined(number, name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
})) continue;
|
||||||
|
|
||||||
|
if (match(ATTRIBUTE_PATTERN, line, matcher -> {
|
||||||
|
listener.attributeDefined(
|
||||||
|
number,
|
||||||
|
matcher.group("element"),
|
||||||
|
matcher.group("name")
|
||||||
|
);
|
||||||
|
})) continue;
|
||||||
|
|
||||||
|
listener.invalidLine(number, line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean match(Pattern pattern, String txt) {
|
||||||
|
return pattern.matcher(txt).matches();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean match(Pattern pattern, String txt, Consumer<Matcher> onMatch) {
|
||||||
|
Matcher matcher = pattern.matcher(txt);
|
||||||
|
if (matcher.matches()) {
|
||||||
|
onMatch.accept(matcher);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,185 @@
|
||||||
|
package com.j2html.codegen.generators;
|
||||||
|
|
||||||
|
import com.j2html.codegen.GeneratorUtil;
|
||||||
|
import com.j2html.codegen.model.AttrD;
|
||||||
|
import com.j2html.codegen.model.AttributesList;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public final class AttributeInterfaceCodeGenerator {
|
||||||
|
|
||||||
|
private static final String relPath = "tags/attributes/";
|
||||||
|
|
||||||
|
public static void generate(final Path absPath, final boolean delete) throws IOException {
|
||||||
|
|
||||||
|
//delete all files in the directory for fresh generation
|
||||||
|
final Path dir = Paths.get(absPath.toString(),relPath);
|
||||||
|
GeneratorUtil.deleteAllFilesInDir(dir);
|
||||||
|
|
||||||
|
for (final AttrD attr : AttributesList.attributesDescriptive()) {
|
||||||
|
final Path path = makePath(attr.attr, absPath);
|
||||||
|
final String interfaceName = interfaceNameFromAttribute(attr.attr)+"<T extends Tag<T>>";
|
||||||
|
/*
|
||||||
|
IFormAction<T extends Tag<T>> extends IInstance<T>
|
||||||
|
|
||||||
|
default T withFormAction(String formAction){
|
||||||
|
return self().attr("formaction", formAction);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
final String interfaceStr = getInterfaceTemplate(
|
||||||
|
interfaceName,
|
||||||
|
Optional.of("IInstance<T>"),
|
||||||
|
Arrays.asList("j2html.tags.Tag","j2html.tags.IInstance"),
|
||||||
|
interfaceNameFromAttribute(attr.attr).substring(1),
|
||||||
|
attr
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!delete) {
|
||||||
|
System.out.println("writing to "+path);
|
||||||
|
Files.write(path, interfaceStr.getBytes());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getPackage(){
|
||||||
|
return "package j2html.tags.attributes;\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String makeReturnTypeAndMethodName(final String name){
|
||||||
|
return "default "+ "T "+name;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getInterfaceTemplate(
|
||||||
|
final String interfaceName,
|
||||||
|
final Optional<String> optExtends,
|
||||||
|
final List<String> imports,
|
||||||
|
final String interfaceNameSimple,
|
||||||
|
final AttrD attrD
|
||||||
|
){
|
||||||
|
|
||||||
|
final StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
|
sb.append(getPackage());
|
||||||
|
sb.append("\n");
|
||||||
|
|
||||||
|
for(String importName : imports){
|
||||||
|
sb.append("import ").append(importName).append(";\n");
|
||||||
|
}
|
||||||
|
sb.append("\n");
|
||||||
|
sb.append("public interface ")
|
||||||
|
.append(interfaceName);
|
||||||
|
|
||||||
|
optExtends.ifPresent(ext -> sb.append(" extends ").append(ext).append(" "));
|
||||||
|
|
||||||
|
sb.append(" {\n");
|
||||||
|
|
||||||
|
//interface contents
|
||||||
|
/*
|
||||||
|
IFormAction<T extends Tag> extends IInstance<T>
|
||||||
|
|
||||||
|
default T withFormAction(String formAction){
|
||||||
|
return self().attr("formaction", formAction);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
//IMPORTANT: '_' added as suffix to mitigate problems
|
||||||
|
//where attributes are java keywords. Just to make it consistent and avoid special cases.
|
||||||
|
final String attrName = interfaceNameSimple.toLowerCase();
|
||||||
|
final String paramName = attrName+"_";
|
||||||
|
|
||||||
|
//depending on if the attribute has an argument or not,
|
||||||
|
//generate methods according to the convention in Tag.java
|
||||||
|
// arg -> with$ATTR(arg), withCond$ATTR(condition, arg)
|
||||||
|
// no arg -> is$ATTR(), withCond$ATTR(condition)
|
||||||
|
|
||||||
|
//append the 'with$ATTR' method
|
||||||
|
writeAttributeMethod(interfaceNameSimple, attrD, sb, attrName, paramName);
|
||||||
|
writeAttributeMethodCond(interfaceNameSimple, attrD, sb, attrName, paramName);
|
||||||
|
|
||||||
|
sb.append("}\n");
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addAttributeNoArg(final StringBuilder sb, final String attrName){
|
||||||
|
//generate the code to add an attribute without an argument
|
||||||
|
|
||||||
|
//there are some special attributes
|
||||||
|
//which do take an argument, but where the argument
|
||||||
|
//is boolean (meaning on/off, yes/no and the like)
|
||||||
|
sb.append("self().attr(\"");
|
||||||
|
if (attrName.equals("autocomplete")){
|
||||||
|
sb.append(attrName).append("\",\"on\"");
|
||||||
|
} else {
|
||||||
|
sb.append(attrName).append("\"");
|
||||||
|
}
|
||||||
|
sb.append(");\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void writeAttributeMethodCond(String interfaceNameSimple, AttrD attrD, StringBuilder sb, String attrName, String paramName) {
|
||||||
|
|
||||||
|
sb.append(makeReturnTypeAndMethodName("withCond"+interfaceNameSimple));
|
||||||
|
|
||||||
|
if(attrD.hasArgument){
|
||||||
|
//add a variant where you can specify the argument
|
||||||
|
|
||||||
|
sb.append("(final boolean enable, final String ").append(paramName).append(") {");
|
||||||
|
|
||||||
|
sb.append("if (enable){\n");
|
||||||
|
sb.append("self().attr(\"").append(attrName).append("\", ").append(paramName).append(");\n");
|
||||||
|
sb.append("}\n");
|
||||||
|
|
||||||
|
sb.append("return self();\n");
|
||||||
|
}else{
|
||||||
|
//add a variant where you can toggle the attribute
|
||||||
|
|
||||||
|
sb.append("(final boolean enable) {");
|
||||||
|
sb.append("if (enable){\n");
|
||||||
|
addAttributeNoArg(sb, attrName);
|
||||||
|
sb.append("}\n");
|
||||||
|
sb.append("return self();\n");
|
||||||
|
}
|
||||||
|
sb.append("}\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void writeAttributeMethod(String interfaceNameSimple, AttrD attrD, StringBuilder sb, String attrName, String paramName) {
|
||||||
|
|
||||||
|
sb.append(makeReturnTypeAndMethodName(
|
||||||
|
((attrD.hasArgument)?"with":"is")+interfaceNameSimple)
|
||||||
|
);
|
||||||
|
|
||||||
|
if(attrD.hasArgument){
|
||||||
|
//add a variant where you can specify the argument
|
||||||
|
|
||||||
|
sb.append("(final String ").append(paramName).append(") {")
|
||||||
|
|
||||||
|
.append("return self().attr(\"").append(attrName).append("\", ").append(paramName).append(");\n");
|
||||||
|
}else{
|
||||||
|
//add a variant where you can toggle the attribute
|
||||||
|
|
||||||
|
sb.append("() {");
|
||||||
|
|
||||||
|
addAttributeNoArg(sb, attrName);
|
||||||
|
|
||||||
|
sb.append("return self();\n");
|
||||||
|
}
|
||||||
|
sb.append("}\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String interfaceNameFromAttribute(String attribute){
|
||||||
|
String res = attribute.substring(0,1).toUpperCase()+attribute.substring(1);
|
||||||
|
return "I" + res;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Path makePath(String tagLowerCase, final Path absPath){
|
||||||
|
final String filename = interfaceNameFromAttribute(tagLowerCase)+".java";
|
||||||
|
return Paths.get(absPath.toString(),relPath,filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,166 @@
|
||||||
|
package com.j2html.codegen.generators;
|
||||||
|
|
||||||
|
|
||||||
|
import com.j2html.codegen.GeneratorUtil;
|
||||||
|
import com.j2html.codegen.model.AttributesList;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static com.j2html.codegen.generators.TagCreatorCodeGenerator.containerTags;
|
||||||
|
import static com.j2html.codegen.generators.TagCreatorCodeGenerator.emptyTags;
|
||||||
|
|
||||||
|
public final class SpecializedTagClassCodeGenerator {
|
||||||
|
|
||||||
|
private static final String relPath = "tags/specialized";
|
||||||
|
|
||||||
|
public static void generate(final Path absPath, final boolean delete) throws IOException {
|
||||||
|
|
||||||
|
//delete all files in the directory for fresh generation
|
||||||
|
final Path dir = Paths.get(absPath.toString(),relPath);
|
||||||
|
GeneratorUtil.deleteAllFilesInDir(dir);
|
||||||
|
|
||||||
|
//the delete argument serves to give the possibility
|
||||||
|
//to delete the classes that were written before
|
||||||
|
System.out.println("// EmptyTags, generated in " + SpecializedTagClassCodeGenerator.class);
|
||||||
|
|
||||||
|
for (final String tag : emptyTags()) {
|
||||||
|
final String className = classNameFromTag(tag);
|
||||||
|
final Path path = makePath(absPath,tag);
|
||||||
|
|
||||||
|
final List<String> interfaceNames = getInterfaceNamesForTag(tag);
|
||||||
|
|
||||||
|
final String classString =
|
||||||
|
getClassTemplate(
|
||||||
|
className,
|
||||||
|
Optional.of("EmptyTag<"+className+">"),
|
||||||
|
Arrays.asList(
|
||||||
|
"j2html.tags.EmptyTag",
|
||||||
|
"j2html.tags.attributes.*"
|
||||||
|
),
|
||||||
|
tag,
|
||||||
|
interfaceNames
|
||||||
|
);
|
||||||
|
|
||||||
|
/*
|
||||||
|
public InputTag() {
|
||||||
|
super("input");
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
if(!delete){
|
||||||
|
System.out.println("writing to "+path);
|
||||||
|
Files.write(path, classString.getBytes());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("// ContainerTags, generated in " + SpecializedTagClassCodeGenerator.class);
|
||||||
|
|
||||||
|
for (final String tag : containerTags()) {
|
||||||
|
final Path path = makePath(absPath, tag);
|
||||||
|
final String className = classNameFromTag(tag);
|
||||||
|
|
||||||
|
final List<String> interfaceNames = getInterfaceNamesForTag(tag);
|
||||||
|
|
||||||
|
final String classString =
|
||||||
|
getClassTemplate(
|
||||||
|
className,
|
||||||
|
Optional.of("ContainerTag<"+className+">"),
|
||||||
|
Arrays.asList(
|
||||||
|
"j2html.tags.ContainerTag",
|
||||||
|
"j2html.tags.attributes.*"
|
||||||
|
),
|
||||||
|
tag,
|
||||||
|
interfaceNames
|
||||||
|
);
|
||||||
|
|
||||||
|
if(delete){
|
||||||
|
if(Files.exists(path)) {
|
||||||
|
System.out.println("deleting " + path);
|
||||||
|
Files.delete(path);
|
||||||
|
}
|
||||||
|
}else {
|
||||||
|
System.out.println("writing to "+path);
|
||||||
|
Files.write(path, classString.getBytes());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static String classNameFromTag(String tageNameLowerCase){
|
||||||
|
String res = tageNameLowerCase.substring(0,1).toUpperCase()+tageNameLowerCase.substring(1);
|
||||||
|
return res + "Tag";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Path makePath(final Path absPath, String tagLowerCase){
|
||||||
|
final String filename = classNameFromTag(tagLowerCase)+".java";
|
||||||
|
return Paths.get(absPath.toString(),relPath,filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getPackage(){
|
||||||
|
return "package j2html.tags.specialized;\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getClassTemplate(
|
||||||
|
final String className,
|
||||||
|
final Optional<String> optExtends,
|
||||||
|
final List<String> imports,
|
||||||
|
final String tag,
|
||||||
|
final List<String> interfaces
|
||||||
|
){
|
||||||
|
|
||||||
|
final StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
|
sb.append(getPackage());
|
||||||
|
sb.append("\n");
|
||||||
|
|
||||||
|
for(String importName : imports){
|
||||||
|
sb.append("import ").append(importName).append(";\n");
|
||||||
|
}
|
||||||
|
sb.append("\n");
|
||||||
|
sb.append("public final class ")
|
||||||
|
.append(className)
|
||||||
|
.append(" ");
|
||||||
|
|
||||||
|
optExtends.ifPresent(ext -> sb.append("extends ").append(ext).append(" "));
|
||||||
|
|
||||||
|
//add the 'implements' clause
|
||||||
|
if(!interfaces.isEmpty()) {
|
||||||
|
sb.append("\n");
|
||||||
|
sb.append("implements ");
|
||||||
|
|
||||||
|
final List<String> genericInterfaceNames
|
||||||
|
= interfaces.stream().map(iName -> iName+"<"+className+">")
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
sb.append(
|
||||||
|
String.join(",", genericInterfaceNames)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.append(" {\n");
|
||||||
|
|
||||||
|
//class contents
|
||||||
|
sb.append("public ")
|
||||||
|
.append(className)
|
||||||
|
.append("() {")
|
||||||
|
.append("super(\"").append(tag).append("\");")
|
||||||
|
.append("}\n");
|
||||||
|
|
||||||
|
sb.append("}\n");
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<String> getInterfaceNamesForTag(final String tagNameLowercase){
|
||||||
|
return AttributesList.getCustomAttributesForHtmlTag(tagNameLowercase)
|
||||||
|
.stream()
|
||||||
|
.map(
|
||||||
|
AttributeInterfaceCodeGenerator::interfaceNameFromAttribute
|
||||||
|
).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,189 @@
|
||||||
|
package com.j2html.codegen.generators;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public final class TagCreatorCodeGenerator {
|
||||||
|
|
||||||
|
public static void print() {
|
||||||
|
|
||||||
|
System.out.println("// EmptyTags, generated in " + TagCreatorCodeGenerator.class);
|
||||||
|
|
||||||
|
for (String tag : emptyTags()) {
|
||||||
|
final String className = SpecializedTagClassCodeGenerator.classNameFromTag(tag);
|
||||||
|
final String publicstaticTypeMethod = "public static "+className+" "+tag+" ";
|
||||||
|
final String castReturn = " return ("+className+") ";
|
||||||
|
final String construct = " new "+className+"()";
|
||||||
|
|
||||||
|
String emptyA1 = publicstaticTypeMethod + "()";
|
||||||
|
String emptyA2 = "{ return "+construct+"; }";
|
||||||
|
// Attr shorthands
|
||||||
|
String emptyB1 = publicstaticTypeMethod + "(Attr.ShortForm shortAttr)";
|
||||||
|
String emptyB2 = "{ "+castReturn+" Attr.addTo("+construct+", shortAttr); }";
|
||||||
|
// Print
|
||||||
|
System.out.println(String.format("%-80s%1s", emptyA1, emptyA2));
|
||||||
|
System.out.println(String.format("%-80s%1s", emptyB1, emptyB2));
|
||||||
|
System.out.println();
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("// ContainerTags, generated in " + TagCreatorCodeGenerator.class);
|
||||||
|
|
||||||
|
for (String tag : containerTags()) {
|
||||||
|
final String className = SpecializedTagClassCodeGenerator.classNameFromTag(tag);
|
||||||
|
final String publicstaticTypeMethod = "public static "+className+" "+tag+" ";
|
||||||
|
final String castReturn = " return ("+className+") ";
|
||||||
|
final String construct = " new "+className+"()";
|
||||||
|
|
||||||
|
String containerA1 = publicstaticTypeMethod+ "()";
|
||||||
|
String containerA2 = "{ "+castReturn + construct + "; }";
|
||||||
|
|
||||||
|
String containerB1 = publicstaticTypeMethod + "(String text)";
|
||||||
|
String containerB2 = "{ "+castReturn + construct + ".withText(text); }";
|
||||||
|
|
||||||
|
String containerC1 = publicstaticTypeMethod + "(DomContent... dc)";
|
||||||
|
String containerC2 = "{ "+castReturn + construct+".with(dc); }";
|
||||||
|
// Attr shorthands
|
||||||
|
String containerD1 = publicstaticTypeMethod + "(Attr.ShortForm shortAttr)";
|
||||||
|
String containerD2 = "{ "+castReturn+" Attr.addTo("+construct+", shortAttr); }";
|
||||||
|
|
||||||
|
String containerE1 = publicstaticTypeMethod + "(Attr.ShortForm shortAttr, String text)";
|
||||||
|
String containerE2 = "{ "+castReturn+" Attr.addTo("+construct+".withText(text), shortAttr); }";
|
||||||
|
|
||||||
|
String containerF1 = publicstaticTypeMethod + "(Attr.ShortForm shortAttr, DomContent... dc)";
|
||||||
|
String containerF2 = "{ "+castReturn+" Attr.addTo("+construct+".with(dc), shortAttr); }";
|
||||||
|
// Print
|
||||||
|
System.out.println(String.format("%-80s%1s", containerA1, containerA2));
|
||||||
|
System.out.println(String.format("%-80s%1s", containerB1, containerB2));
|
||||||
|
System.out.println(String.format("%-80s%1s", containerC1, containerC2));
|
||||||
|
System.out.println(String.format("%-80s%1s", containerD1, containerD2));
|
||||||
|
System.out.println(String.format("%-80s%1s", containerE1, containerE2));
|
||||||
|
System.out.println(String.format("%-80s%1s", containerF1, containerF2));
|
||||||
|
System.out.println();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is a method that contains all ContainerTags, there is nothing below it
|
||||||
|
public static List<String> emptyTags() {
|
||||||
|
return Arrays.asList(
|
||||||
|
"area",
|
||||||
|
"base",
|
||||||
|
"br",
|
||||||
|
"col",
|
||||||
|
//"!DOCTYPE html",
|
||||||
|
"embed",
|
||||||
|
"hr",
|
||||||
|
"img",
|
||||||
|
"input",
|
||||||
|
"keygen",
|
||||||
|
"link",
|
||||||
|
"meta",
|
||||||
|
"param",
|
||||||
|
"source",
|
||||||
|
"track",
|
||||||
|
"wbr"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<String> containerTags() {
|
||||||
|
return Arrays.asList(
|
||||||
|
"a",
|
||||||
|
"abbr",
|
||||||
|
"address",
|
||||||
|
"article",
|
||||||
|
"aside",
|
||||||
|
"audio",
|
||||||
|
"b",
|
||||||
|
"bdi",
|
||||||
|
"bdo",
|
||||||
|
"blockquote",
|
||||||
|
"body",
|
||||||
|
"button",
|
||||||
|
"canvas",
|
||||||
|
"caption",
|
||||||
|
"cite",
|
||||||
|
"code",
|
||||||
|
"colgroup",
|
||||||
|
"data",
|
||||||
|
"datalist",
|
||||||
|
"dd",
|
||||||
|
"del",
|
||||||
|
"details",
|
||||||
|
"dfn",
|
||||||
|
"dialog",
|
||||||
|
"div",
|
||||||
|
"dl",
|
||||||
|
"dt",
|
||||||
|
"em",
|
||||||
|
"fieldset",
|
||||||
|
"figcaption",
|
||||||
|
"figure",
|
||||||
|
"footer",
|
||||||
|
"form",
|
||||||
|
"h1",
|
||||||
|
"h2",
|
||||||
|
"h3",
|
||||||
|
"h4",
|
||||||
|
"h5",
|
||||||
|
"h6",
|
||||||
|
"head",
|
||||||
|
"header",
|
||||||
|
"html",
|
||||||
|
"i",
|
||||||
|
"iframe",
|
||||||
|
"ins",
|
||||||
|
"kbd",
|
||||||
|
"label",
|
||||||
|
"legend",
|
||||||
|
"li",
|
||||||
|
"main",
|
||||||
|
"map",
|
||||||
|
"mark",
|
||||||
|
"menu",
|
||||||
|
"menuitem",
|
||||||
|
"meter",
|
||||||
|
"nav",
|
||||||
|
"noscript",
|
||||||
|
"object",
|
||||||
|
"ol",
|
||||||
|
"optgroup",
|
||||||
|
"option",
|
||||||
|
"output",
|
||||||
|
"p",
|
||||||
|
"picture",
|
||||||
|
"pre",
|
||||||
|
"progress",
|
||||||
|
"q",
|
||||||
|
"rp",
|
||||||
|
"rt",
|
||||||
|
"ruby",
|
||||||
|
"s",
|
||||||
|
"samp",
|
||||||
|
"script",
|
||||||
|
"section",
|
||||||
|
"select",
|
||||||
|
"slot",
|
||||||
|
"small",
|
||||||
|
"span",
|
||||||
|
"strong",
|
||||||
|
"style",
|
||||||
|
"sub",
|
||||||
|
"summary",
|
||||||
|
"sup",
|
||||||
|
"table",
|
||||||
|
"tbody",
|
||||||
|
"td",
|
||||||
|
"template",
|
||||||
|
"textarea",
|
||||||
|
"tfoot",
|
||||||
|
"th",
|
||||||
|
"thead",
|
||||||
|
"time",
|
||||||
|
"title",
|
||||||
|
"tr",
|
||||||
|
"u",
|
||||||
|
"ul",
|
||||||
|
"var",
|
||||||
|
"video"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,94 @@
|
||||||
|
package com.j2html.codegen.generators;
|
||||||
|
|
||||||
|
import com.j2html.codegen.wattsi.AttributeDefinition;
|
||||||
|
import com.j2html.codegen.wattsi.ElementDefinition;
|
||||||
|
import com.j2html.codegen.wattsi.WattsiSource;
|
||||||
|
import com.squareup.javapoet.ClassName;
|
||||||
|
import com.squareup.javapoet.MethodSpec;
|
||||||
|
import com.squareup.javapoet.TypeSpec;
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
|
||||||
|
import javax.lang.model.element.Modifier;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class WattsiGenerator {
|
||||||
|
|
||||||
|
public static void main(String... args) throws IOException {
|
||||||
|
Path source = Paths.get(args[0]);
|
||||||
|
Document doc = Jsoup.parse(source.toFile(), "UTF-8", "https://html.spec.whatwg.org/");
|
||||||
|
WattsiSource wattsi = new WattsiSource(doc);
|
||||||
|
|
||||||
|
List<ElementDefinition> elements = wattsi.elementDefinitions();
|
||||||
|
List<AttributeDefinition> attributes = wattsi.attributeDefinitions();
|
||||||
|
|
||||||
|
// for (ElementDefinition element : elements) {
|
||||||
|
// System.out.println((element.isObsolete() ? "!" : "") + element.name());
|
||||||
|
// for (AttributeDefinition attribute : attributes) {
|
||||||
|
// if (attribute.appliesTo(element)) {
|
||||||
|
// System.out.println(" " + (attribute.isObsolete() ? "!" : "") + attribute.name());
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// System.out.println();
|
||||||
|
// }
|
||||||
|
|
||||||
|
for (ElementDefinition element : elements) {
|
||||||
|
ClassName className = ClassName.get(
|
||||||
|
"com.j2html",
|
||||||
|
capitalize(element.name()) + "Tag"
|
||||||
|
);
|
||||||
|
|
||||||
|
TypeSpec.Builder type = TypeSpec.classBuilder(className)
|
||||||
|
.addModifiers(Modifier.PUBLIC);
|
||||||
|
|
||||||
|
if (element.isObsolete()) {
|
||||||
|
type.addAnnotation(Deprecated.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (AttributeDefinition attribute : attributes) {
|
||||||
|
if (attribute.appliesTo(element)) {
|
||||||
|
String name = methodName("with", attribute.name().split("-"));
|
||||||
|
MethodSpec.Builder setter = MethodSpec.methodBuilder(name)
|
||||||
|
.addModifiers(Modifier.PUBLIC)
|
||||||
|
.returns(className)
|
||||||
|
.addStatement("return this");
|
||||||
|
|
||||||
|
if(attribute.isObsolete()){
|
||||||
|
setter.addAnnotation(Deprecated.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
type.addMethod(setter.build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println(type.build().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
// System.out.println(doc.select("dfn"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String methodName(String prefix, String... words){
|
||||||
|
String[] tmp = new String[words.length + 1];
|
||||||
|
tmp[0] = prefix;
|
||||||
|
for(int i = 0; i < words.length; i++){
|
||||||
|
tmp[i+1] = words[i];
|
||||||
|
}
|
||||||
|
return methodName(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String methodName(String... words){
|
||||||
|
String[] camelCase = new String[words.length];
|
||||||
|
camelCase[0] = words[0];
|
||||||
|
for(int i = 1; i < words.length; i++){
|
||||||
|
camelCase[i] = capitalize(words[i]);
|
||||||
|
}
|
||||||
|
return String.join("", camelCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String capitalize(String word){
|
||||||
|
return word.substring(0,1).toUpperCase() + word.substring(1).toLowerCase();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
package com.j2html.codegen.model;
|
||||||
|
|
||||||
|
public final class AttrD {
|
||||||
|
//attribute descriptor
|
||||||
|
|
||||||
|
public final String attr;
|
||||||
|
|
||||||
|
public final boolean hasArgument;
|
||||||
|
|
||||||
|
//the html tags that this attribute can be used on
|
||||||
|
public final String[] tags;
|
||||||
|
|
||||||
|
public AttrD(final String attr, boolean hasArgument){
|
||||||
|
this.attr = attr;
|
||||||
|
this.hasArgument = hasArgument;
|
||||||
|
this.tags = new String[]{};
|
||||||
|
}
|
||||||
|
|
||||||
|
public AttrD(final String attr, boolean hasArgument, final String... tags) {
|
||||||
|
this.attr = attr;
|
||||||
|
this.hasArgument = hasArgument;
|
||||||
|
this.tags = tags;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,182 @@
|
||||||
|
package com.j2html.codegen.model;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public final class AttributesList {
|
||||||
|
|
||||||
|
//https://www.w3schools.com/tags/ref_attributes.asp
|
||||||
|
|
||||||
|
public static List<String> getCustomAttributesForHtmlTag(final String tagLowercase){
|
||||||
|
|
||||||
|
final List<String> attrs = new ArrayList<>();
|
||||||
|
for(AttrD attrD : attributesDescriptive()){
|
||||||
|
if(
|
||||||
|
Arrays.asList(attrD.tags).contains(tagLowercase)
|
||||||
|
){
|
||||||
|
attrs.add(attrD.attr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return attrs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<AttrD> attributesDescriptive() {
|
||||||
|
return Arrays.asList(
|
||||||
|
new AttrD("accept", true, "input"),
|
||||||
|
//new AttrD("accept-charset","form"), //contains dashes, TODO
|
||||||
|
//new AttrD("accesskey"), //global attribute
|
||||||
|
new AttrD("action", true, "form"),
|
||||||
|
//"align", not supported in HTML5
|
||||||
|
new AttrD("alt", true, "area","img","input"),
|
||||||
|
new AttrD("async", false, "script"),
|
||||||
|
new AttrD("autocomplete", false, "form","input"),
|
||||||
|
new AttrD("autofocus", false, "button","input","select","textarea"),
|
||||||
|
new AttrD("autoplay", false, "audio","video"),
|
||||||
|
//"bgcolor", not supported in HTMTL5
|
||||||
|
//"border", not supported in HTML5
|
||||||
|
new AttrD("charset", true, "meta","script"),
|
||||||
|
new AttrD("checked", false, "input"),
|
||||||
|
new AttrD("cite", true, "blockquote","del","ins","q"),
|
||||||
|
//"class" already implemented in Tag.java // global attribute
|
||||||
|
new AttrD("cols", true, "textarea"),
|
||||||
|
new AttrD("colspan", true, "td","th"),
|
||||||
|
new AttrD("content", true, "meta"),
|
||||||
|
//"contenteditable" global attribute, should be in Tag.java
|
||||||
|
new AttrD("controls", false, "audio","video"),
|
||||||
|
new AttrD("coords", true, "area"),
|
||||||
|
new AttrD("data", true, "object"),
|
||||||
|
new AttrD("datetime", true, "del","ins","time"),
|
||||||
|
new AttrD("default", false, "track"),
|
||||||
|
new AttrD("defer", false, "script"),
|
||||||
|
//new AttrD("dir"), //global attribute
|
||||||
|
new AttrD("dirname", true, "input","textarea"),
|
||||||
|
new AttrD("disabled",false, "button","fieldset","input","optgroup","option","select","textarea"),
|
||||||
|
new AttrD("download",false, "a","area"),
|
||||||
|
//new AttrD("draggable") global attribute, should be in Tag.java
|
||||||
|
new AttrD("enctype", true, "form"),
|
||||||
|
new AttrD("for", true, "label","output"),
|
||||||
|
new AttrD("form", true, "button","fieldset","input","label","meter","object","output","select","textarea"),
|
||||||
|
new AttrD("formaction", true, "button","input"),
|
||||||
|
new AttrD("headers", true, "td","th"),
|
||||||
|
new AttrD("height", true, "canvas","embed","iframe","img","input","object","video"),
|
||||||
|
//new AttrD("hidden"), global attribute
|
||||||
|
new AttrD("high", true, "meter"),
|
||||||
|
new AttrD("href", true, "a","area","base","link"),
|
||||||
|
new AttrD("hreflang", true, "a","area","link"),
|
||||||
|
//"http-equiv", //TODO: '-' is problematic in code generation
|
||||||
|
//"id" global attribute, should be in Tag.java
|
||||||
|
new AttrD("ismap", false, "img"),
|
||||||
|
new AttrD("kind", true, "track"),
|
||||||
|
new AttrD("label", true, "track","option","optgroup"),
|
||||||
|
//"lang" global attribute, should be in Tag.java
|
||||||
|
new AttrD("list", true, "input"),
|
||||||
|
new AttrD("loop", false, "audio","video"),
|
||||||
|
new AttrD("low", true, "meter"),
|
||||||
|
new AttrD("max", true, "input","meter","progress"),
|
||||||
|
new AttrD("maxlength", true, "input","textarea"),
|
||||||
|
new AttrD("media", true, "a","area","link","source","style"),
|
||||||
|
new AttrD("method", true, "form"),
|
||||||
|
new AttrD("min", true, "input","meter"),
|
||||||
|
new AttrD("multiple", false, "input","select"),
|
||||||
|
new AttrD("muted", false, "video","audio"),
|
||||||
|
new AttrD("name", true, "button","fieldset","form","iframe","input","map","meta","object","output","param","select","slot","textarea"),
|
||||||
|
new AttrD("novalidate", false, "form"),
|
||||||
|
new AttrD("onabort", true, "audio","embed","img","object","video"),
|
||||||
|
new AttrD("onafterprint", true, "body"),
|
||||||
|
new AttrD("onbeforeprint", true, "body"),
|
||||||
|
new AttrD("onbeforeunload", true, "body"),
|
||||||
|
//new AttrD("onblur"), global attribute
|
||||||
|
new AttrD("oncanplay", true, "audio","embed","object","video"),
|
||||||
|
new AttrD("oncanplaythrough", true, "audio","video"),
|
||||||
|
/* a bunch of event attributes that are on all visible elements (so should be in Tag.java)
|
||||||
|
"onchange",
|
||||||
|
"onclick",
|
||||||
|
"oncontextmenu",
|
||||||
|
"oncopy",
|
||||||
|
*/
|
||||||
|
new AttrD("oncuechange", true, "track"),
|
||||||
|
/*
|
||||||
|
"oncut",
|
||||||
|
...
|
||||||
|
"ondrop",
|
||||||
|
*/
|
||||||
|
new AttrD("ondurationchange", true, "audio","video"),
|
||||||
|
new AttrD("onemptied", true, "audio","video"),
|
||||||
|
new AttrD("onended", true, "audio","video"),
|
||||||
|
new AttrD("onerror", true, "audio","body","embed","img","object","script","style","video"),
|
||||||
|
//new AttrD("onfocus"),// global attribute
|
||||||
|
new AttrD("onhashchange", true, "body"),
|
||||||
|
// ... a bunch of event attributes visible on all elements
|
||||||
|
new AttrD("onload", true, "body","iframe","img","input","link","script","style"),
|
||||||
|
new AttrD("onloadeddata", true, "audio","video"),
|
||||||
|
new AttrD("onloadedmetadata", true, "audio","video"),
|
||||||
|
new AttrD("onloadstart", true, "audio","video"),
|
||||||
|
// ... a bunch of event attributes visible on all elements
|
||||||
|
new AttrD("onoffline", true, "body"),
|
||||||
|
new AttrD("ononline", true, "body"),
|
||||||
|
new AttrD("onpagehide", true, "body"),
|
||||||
|
new AttrD("onpageshow", true, "body"),
|
||||||
|
//new AttrD("onpaste"),// global attribute
|
||||||
|
new AttrD("onpause", true, "audio","video"),
|
||||||
|
new AttrD("onplay", true, "audio","video"),
|
||||||
|
new AttrD("onplaying", true, "audio","video"),
|
||||||
|
new AttrD("onpopstate", true, "body"),
|
||||||
|
new AttrD("onprogress", true, "audio","video"),
|
||||||
|
new AttrD("onratechange", true, "audio","video"),
|
||||||
|
new AttrD("onreset", true, "form"),
|
||||||
|
new AttrD("onresize", true, "body"),
|
||||||
|
//new AttrD("onscroll"), //global attribute
|
||||||
|
new AttrD("onsearch", true, "input"),
|
||||||
|
new AttrD("onseeked", true, "audio","video"),
|
||||||
|
new AttrD("onseeking", true, "audio","video"),
|
||||||
|
//new AttrD("onselect"), //global attribute
|
||||||
|
new AttrD("onstalled", true, "audio","video"),
|
||||||
|
new AttrD("onstorage", true, "body"),
|
||||||
|
new AttrD("onsubmit", true, "form"),
|
||||||
|
new AttrD("onsuspend", true, "audio","video"),
|
||||||
|
new AttrD("ontimeupdate", true, "audio","video"),
|
||||||
|
new AttrD("ontoggle", true, "details"),
|
||||||
|
new AttrD("onunload", true, "body"),
|
||||||
|
new AttrD("onvolumechanged", true, "audio","video"),
|
||||||
|
new AttrD("onwaiting", true, "audio","video"),
|
||||||
|
//new AttrD("onwheel"), //global attribute
|
||||||
|
new AttrD("open", false, "details"),
|
||||||
|
new AttrD("optimum", true, "meter"),
|
||||||
|
new AttrD("pattern", true, "input"),
|
||||||
|
new AttrD("placeholder", true, "input","textarea"),
|
||||||
|
new AttrD("poster", true, "video"),
|
||||||
|
new AttrD("preload", true, "audio","video"),
|
||||||
|
new AttrD("readonly", false, "input","textarea"),
|
||||||
|
new AttrD("rel", true, "a","area","form","link"),
|
||||||
|
new AttrD("required", false, "input","select","textarea"),
|
||||||
|
new AttrD("reversed", false, "ol"),
|
||||||
|
new AttrD("rows", true, "textarea"),
|
||||||
|
new AttrD("rowspan", true, "td","th"),
|
||||||
|
new AttrD("sandbox", false, "iframe"),
|
||||||
|
new AttrD("scope", true, "th"),
|
||||||
|
new AttrD("selected", false, "option"),
|
||||||
|
new AttrD("shape", true, "area"),
|
||||||
|
new AttrD("size", true, "input","select"),
|
||||||
|
new AttrD("sizes", true, "img","link","source"),
|
||||||
|
new AttrD("span", true, "col","colgroup"),
|
||||||
|
//new AttrD("spellcheck"), //global attribute
|
||||||
|
new AttrD("src", true, "audio","embed","iframe","img","input","script","source","track","video"),
|
||||||
|
new AttrD("srcdoc", true, "iframe"),
|
||||||
|
new AttrD("srclang", true, "track"),
|
||||||
|
new AttrD("srcset", true, "img","source"),
|
||||||
|
new AttrD("start", true, "ol"),
|
||||||
|
new AttrD("step", true, "input"),
|
||||||
|
//new AttrD("style"), //global attribute
|
||||||
|
//new AttrD("tabindex"), //global attribute
|
||||||
|
new AttrD("target", true, "a","area","base","form"),
|
||||||
|
//new AttrD("title"), //global attribute
|
||||||
|
//new AttrD("translate"),// global attribute
|
||||||
|
new AttrD("type", true, "a","button","embed","input","link","menu","object","script","source","style"),
|
||||||
|
new AttrD("usemap", true, "img","object"),
|
||||||
|
new AttrD("value", true, "button","data","input","li","option","meter","progress","param"),
|
||||||
|
new AttrD("width", true, "canvas","embed","iframe","img","input","object","video"),
|
||||||
|
new AttrD("wrap", true, "textarea")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
package com.j2html.codegen.wattsi;
|
||||||
|
|
||||||
|
public interface AttributeDefinition {
|
||||||
|
|
||||||
|
String name();
|
||||||
|
|
||||||
|
boolean appliesTo(ElementDefinition element);
|
||||||
|
|
||||||
|
boolean isObsolete();
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
package com.j2html.codegen.wattsi;
|
||||||
|
|
||||||
|
public interface ElementDefinition {
|
||||||
|
|
||||||
|
String name();
|
||||||
|
|
||||||
|
boolean isSelfClosing();
|
||||||
|
|
||||||
|
boolean isObsolete();
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,209 @@
|
||||||
|
package com.j2html.codegen.wattsi;
|
||||||
|
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
import org.jsoup.nodes.Element;
|
||||||
|
import org.jsoup.nodes.Node;
|
||||||
|
import org.jsoup.nodes.TextNode;
|
||||||
|
import org.jsoup.select.Elements;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static java.util.stream.Collectors.toList;
|
||||||
|
|
||||||
|
public class WattsiSource {
|
||||||
|
|
||||||
|
private final Document doc;
|
||||||
|
|
||||||
|
private final Set<Reference> obsolete = new HashSet<>();
|
||||||
|
|
||||||
|
public WattsiSource(Document doc) {
|
||||||
|
this.doc = doc;
|
||||||
|
|
||||||
|
// Find where obsolete elements are defined or referenced.
|
||||||
|
Elements obsoleteElements = doc.select("p:contains(Elements in the following list are entirely obsolete) + dl");
|
||||||
|
|
||||||
|
// Convert definitions into references to record obsolete elements.
|
||||||
|
obsoleteElements.select("dt > dfn[element]")
|
||||||
|
.stream()
|
||||||
|
.map(WattsiElement::new)
|
||||||
|
.map(WattsiElement::reference)
|
||||||
|
.forEach(obsolete::add);
|
||||||
|
|
||||||
|
// Extract references to record obsolete elements.
|
||||||
|
obsoleteElements.select("dt > code")
|
||||||
|
.stream()
|
||||||
|
.map(Element::childNodes)
|
||||||
|
.map(Reference::from)
|
||||||
|
.forEach(obsolete::add);
|
||||||
|
|
||||||
|
// Find where obsolete attributes are defined or referenced.
|
||||||
|
Elements obsoleteAttributes = doc.select("p:contains(The following attributes are obsolete) + dl");
|
||||||
|
|
||||||
|
// Convert definitions into references to record obsolete attributes.
|
||||||
|
obsoleteAttributes.select("dt > dfn[element-attr]").stream()
|
||||||
|
.map(WattsiAttribute::new)
|
||||||
|
.map(WattsiAttribute::reference)
|
||||||
|
.forEach(obsolete::add);
|
||||||
|
|
||||||
|
// System.out.println(obsoleteAttributes.select("dt"));
|
||||||
|
|
||||||
|
// obsoleteAttributes.select("dt > code").stream()
|
||||||
|
// .map(Element::childNodes)
|
||||||
|
// .map(Reference::from)
|
||||||
|
// .forEach(System.err::println);
|
||||||
|
|
||||||
|
// System.out.println(
|
||||||
|
// doc.select("dfn[obsolete]")
|
||||||
|
// );
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ElementDefinition> elementDefinitions() {
|
||||||
|
return doc.select("dfn[element]").stream()
|
||||||
|
.map(WattsiElement::new)
|
||||||
|
.collect(toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<AttributeDefinition> attributeDefinitions() {
|
||||||
|
return doc.select("dfn[element-attr]").stream()
|
||||||
|
.map(WattsiAttribute::new)
|
||||||
|
.collect(toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public class WattsiElement implements ElementDefinition {
|
||||||
|
private final Element dfn;
|
||||||
|
|
||||||
|
WattsiElement(Element dfn) {
|
||||||
|
if (!"dfn".equals(dfn.tagName())) {
|
||||||
|
throw new IllegalArgumentException("Element cannot be defined from: " + dfn);
|
||||||
|
}
|
||||||
|
if (!dfn.hasAttr("element")) {
|
||||||
|
throw new IllegalArgumentException("Does not define an element: " + dfn);
|
||||||
|
}
|
||||||
|
if (dfn.childrenSize() != 1) {
|
||||||
|
throw new IllegalArgumentException("Element cannot have multiple definitions: " + dfn);
|
||||||
|
}
|
||||||
|
this.dfn = dfn;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Reference reference() {
|
||||||
|
return Reference.from(dfn.childNodes());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String name() {
|
||||||
|
if (dfn.hasAttr("data-x")) {
|
||||||
|
return dfn.attr("data-x");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Reference.from(dfn.childNodes()).key;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSelfClosing() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isObsolete() {
|
||||||
|
return obsolete.contains(reference());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class WattsiAttribute implements AttributeDefinition {
|
||||||
|
private final Element dfn;
|
||||||
|
|
||||||
|
WattsiAttribute(Element dfn) {
|
||||||
|
if (!"dfn".equals(dfn.tagName())) {
|
||||||
|
throw new IllegalArgumentException("Attribute cannot be defined from: " + dfn);
|
||||||
|
}
|
||||||
|
if (!dfn.hasAttr("element-attr")) {
|
||||||
|
throw new IllegalArgumentException("Does not define an attribute: " + dfn);
|
||||||
|
}
|
||||||
|
if (dfn.childrenSize() != 1) {
|
||||||
|
throw new IllegalArgumentException("Attribute cannot have multiple definitions: " + dfn);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dfn = dfn;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Reference reference() {
|
||||||
|
return Reference.from(dfn.childNodes());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String name() {
|
||||||
|
return reference().text;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> targets() {
|
||||||
|
if (dfn.hasAttr("for")) {
|
||||||
|
return Arrays.asList(dfn.attr("for").trim().split(","));
|
||||||
|
}
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean appliesTo(ElementDefinition element) {
|
||||||
|
return targets().contains(element.name());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isObsolete() {
|
||||||
|
return obsolete.contains(reference());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Reference {
|
||||||
|
private final String key;
|
||||||
|
private final String text;
|
||||||
|
|
||||||
|
Reference(String key, String text) {
|
||||||
|
this.key = key;
|
||||||
|
this.text = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return key + "[" + text + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
Reference reference = (Reference) o;
|
||||||
|
return key.equals(reference.key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Reference from(List<Node> nodes) {
|
||||||
|
if (nodes.stream().allMatch(n -> n instanceof TextNode)) {
|
||||||
|
String txt = nodes.stream()
|
||||||
|
.map(n -> (TextNode) n)
|
||||||
|
.map(TextNode::text)
|
||||||
|
.collect(Collectors.joining(" "));
|
||||||
|
return new Reference(txt, txt);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Node node : nodes) {
|
||||||
|
if (node instanceof Element) {
|
||||||
|
Element element = (Element) node;
|
||||||
|
if (element.is("code") || element.is("span")) {
|
||||||
|
if (element.hasAttr("data-x")) {
|
||||||
|
return new Reference(element.attr("data-x").toLowerCase(), element.text());
|
||||||
|
} else {
|
||||||
|
return new Reference(element.text().toLowerCase(), element.text());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package com.j2html.codegen;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class AppTest
|
||||||
|
{
|
||||||
|
@Test
|
||||||
|
public void shouldAnswerWithTrue()
|
||||||
|
{
|
||||||
|
//dummy, just to conform to the default mvn
|
||||||
|
//directory layout
|
||||||
|
assertTrue( true );
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
package com.j2html.codegen;
|
||||||
|
|
||||||
|
import com.j2html.codegen.generators.TagCreatorCodeGenerator;
|
||||||
|
import com.j2html.codegen.wattsi.ElementDefinition;
|
||||||
|
import com.j2html.codegen.wattsi.WattsiSource;
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Ignore;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static java.util.Collections.emptyList;
|
||||||
|
import static java.util.stream.Collectors.toList;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
public class CodeGeneratorComplianceTests {
|
||||||
|
|
||||||
|
private WattsiSource specification;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws IOException {
|
||||||
|
Path source = Paths.get("src","test","resources","2022-01.wattsi");
|
||||||
|
Document doc = Jsoup.parse(source.toFile(), "UTF-8", "https://html.spec.whatwg.org/");
|
||||||
|
specification = new WattsiSource(doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<String> generatedElements(){
|
||||||
|
Set<String> elements = new HashSet<>();
|
||||||
|
elements.addAll(TagCreatorCodeGenerator.emptyTags());
|
||||||
|
elements.addAll(TagCreatorCodeGenerator.containerTags());
|
||||||
|
return elements;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<String> specifiedElements(WattsiSource source){
|
||||||
|
Set<String> elements = new HashSet<>();
|
||||||
|
for(ElementDefinition element : source.elementDefinitions()){
|
||||||
|
elements.add(element.name());
|
||||||
|
}
|
||||||
|
return elements;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Ignore
|
||||||
|
// TODO restore this test once a policy has been determined for obsolete elements.
|
||||||
|
public void all_wattsi_elements_are_defined_in_the_code_generator() {
|
||||||
|
Set<String> generated = generatedElements();
|
||||||
|
|
||||||
|
List<String> undefined = specification.elementDefinitions().stream()
|
||||||
|
.filter(element -> !element.isObsolete())
|
||||||
|
.filter(element -> !generated.contains(element.name()))
|
||||||
|
.map(ElementDefinition::name)
|
||||||
|
.collect(toList());
|
||||||
|
|
||||||
|
assertEquals("HTML elements are missing", emptyList(), undefined);
|
||||||
|
// Currently missing (and mostly deprecated):
|
||||||
|
// hgroup
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void only_wattsi_elements_are_defined_in_the_code_generator(){
|
||||||
|
Set<String> specified = specifiedElements(specification);
|
||||||
|
|
||||||
|
List<String> invalid = generatedElements().stream()
|
||||||
|
.filter(element -> !specified.contains(element))
|
||||||
|
.collect(toList());
|
||||||
|
|
||||||
|
assertEquals("HTML elements are invalid", emptyList(), invalid);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
package com.j2html.codegen;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.mockito.InOrder;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
|
public class ParserTest {
|
||||||
|
|
||||||
|
private void verifyParsing(String txt, Consumer<Parser.Listener> checks) {
|
||||||
|
Parser.Listener listener = mock(Parser.Listener.class);
|
||||||
|
Parser.parse(txt, listener);
|
||||||
|
checks.accept(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void an_empty_input_has_no_events() {
|
||||||
|
verifyParsing("", listener -> {
|
||||||
|
verifyNoInteractions(listener);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whitespace_has_no_events() {
|
||||||
|
verifyParsing(" \t\t\t\t", listener -> {
|
||||||
|
verifyNoInteractions(listener);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void commented_lines_are_signaled() {
|
||||||
|
verifyParsing("#Comment 1.\n# Comment B?", listener -> {
|
||||||
|
InOrder order = inOrder(listener);
|
||||||
|
order.verify(listener).lineCommented(1, "#Comment 1.");
|
||||||
|
order.verify(listener).lineCommented(2, "# Comment B?");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void node_definitions_are_signaled() {
|
||||||
|
verifyParsing("ELEMENT[a]\nEMPTY-ELEMENT[b]\nBOOLEAN[c]\nONOFF[d]\nSTRING[e]", listener -> {
|
||||||
|
InOrder order = inOrder(listener);
|
||||||
|
order.verify(listener).elementDefined(1, "a");
|
||||||
|
order.verify(listener).emptyElementDefined(2, "b");
|
||||||
|
order.verify(listener).booleanDefined(3, "c");
|
||||||
|
order.verify(listener).onOffDefined(4, "d");
|
||||||
|
order.verify(listener).stringDefined(5, "e");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void attribute_definitions_are_signaled() {
|
||||||
|
verifyParsing("ATTRIBUTE[a:b]", listener -> {
|
||||||
|
InOrder order = inOrder(listener);
|
||||||
|
order.verify(listener).attributeDefined(1, "a", "b");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void invalid_lines_are_signaled() {
|
||||||
|
verifyParsing("lol, I dunno!\nIt Broke...", listener -> {
|
||||||
|
InOrder order = inOrder(listener);
|
||||||
|
order.verify(listener).invalidLine(1, "lol, I dunno!");
|
||||||
|
order.verify(listener).invalidLine(2, "It Broke...");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
128553
gradle-plugin-j2html/src/test/resources/2022-01.wattsi
Normal file
128553
gradle-plugin-j2html/src/test/resources/2022-01.wattsi
Normal file
File diff suppressed because it is too large
Load diff
622
gradle-plugin-j2html/src/test/resources/html.model
Normal file
622
gradle-plugin-j2html/src/test/resources/html.model
Normal file
|
@ -0,0 +1,622 @@
|
||||||
|
EMPTY-ELEMENT[area]
|
||||||
|
EMPTY-ELEMENT[base]
|
||||||
|
EMPTY-ELEMENT[br]
|
||||||
|
EMPTY-ELEMENT[col]
|
||||||
|
EMPTY-ELEMENT[embed]
|
||||||
|
EMPTY-ELEMENT[hr]
|
||||||
|
EMPTY-ELEMENT[img]
|
||||||
|
EMPTY-ELEMENT[input]
|
||||||
|
EMPTY-ELEMENT[keygen]
|
||||||
|
EMPTY-ELEMENT[link]
|
||||||
|
EMPTY-ELEMENT[meta]
|
||||||
|
EMPTY-ELEMENT[param]
|
||||||
|
EMPTY-ELEMENT[source]
|
||||||
|
EMPTY-ELEMENT[track]
|
||||||
|
EMPTY-ELEMENT[wbr]
|
||||||
|
|
||||||
|
ELEMENT[a]
|
||||||
|
ELEMENT[abbr]
|
||||||
|
ELEMENT[address]
|
||||||
|
ELEMENT[article]
|
||||||
|
ELEMENT[aside]
|
||||||
|
ELEMENT[audio]
|
||||||
|
ELEMENT[b]
|
||||||
|
ELEMENT[bdi]
|
||||||
|
ELEMENT[bdo]
|
||||||
|
ELEMENT[blockquote]
|
||||||
|
ELEMENT[body]
|
||||||
|
ELEMENT[button]
|
||||||
|
ELEMENT[canvas]
|
||||||
|
ELEMENT[caption]
|
||||||
|
ELEMENT[cite]
|
||||||
|
ELEMENT[code]
|
||||||
|
ELEMENT[colgroup]
|
||||||
|
ELEMENT[data]
|
||||||
|
ELEMENT[datalist]
|
||||||
|
ELEMENT[dd]
|
||||||
|
ELEMENT[del]
|
||||||
|
ELEMENT[details]
|
||||||
|
ELEMENT[dfn]
|
||||||
|
ELEMENT[dialog]
|
||||||
|
ELEMENT[div]
|
||||||
|
ELEMENT[dl]
|
||||||
|
ELEMENT[dt]
|
||||||
|
ELEMENT[em]
|
||||||
|
ELEMENT[fieldset]
|
||||||
|
ELEMENT[figcaption]
|
||||||
|
ELEMENT[figure]
|
||||||
|
ELEMENT[footer]
|
||||||
|
ELEMENT[form]
|
||||||
|
ELEMENT[h1]
|
||||||
|
ELEMENT[h2]
|
||||||
|
ELEMENT[h3]
|
||||||
|
ELEMENT[h4]
|
||||||
|
ELEMENT[h5]
|
||||||
|
ELEMENT[h6]
|
||||||
|
ELEMENT[head]
|
||||||
|
ELEMENT[header]
|
||||||
|
ELEMENT[html]
|
||||||
|
ELEMENT[i]
|
||||||
|
ELEMENT[iframe]
|
||||||
|
ELEMENT[ins]
|
||||||
|
ELEMENT[kbd]
|
||||||
|
ELEMENT[label]
|
||||||
|
ELEMENT[legend]
|
||||||
|
ELEMENT[li]
|
||||||
|
ELEMENT[main]
|
||||||
|
ELEMENT[map]
|
||||||
|
ELEMENT[mark]
|
||||||
|
ELEMENT[menu]
|
||||||
|
ELEMENT[menuitem]
|
||||||
|
ELEMENT[meter]
|
||||||
|
ELEMENT[nav]
|
||||||
|
ELEMENT[noscript]
|
||||||
|
ELEMENT[object]
|
||||||
|
ELEMENT[ol]
|
||||||
|
ELEMENT[optgroup]
|
||||||
|
ELEMENT[option]
|
||||||
|
ELEMENT[output]
|
||||||
|
ELEMENT[p]
|
||||||
|
ELEMENT[picture]
|
||||||
|
ELEMENT[pre]
|
||||||
|
ELEMENT[progress]
|
||||||
|
ELEMENT[q]
|
||||||
|
ELEMENT[rp]
|
||||||
|
ELEMENT[rt]
|
||||||
|
ELEMENT[ruby]
|
||||||
|
ELEMENT[s]
|
||||||
|
ELEMENT[samp]
|
||||||
|
ELEMENT[script]
|
||||||
|
ELEMENT[section]
|
||||||
|
ELEMENT[select]
|
||||||
|
ELEMENT[slot]
|
||||||
|
ELEMENT[small]
|
||||||
|
ELEMENT[span]
|
||||||
|
ELEMENT[strong]
|
||||||
|
ELEMENT[style]
|
||||||
|
ELEMENT[sub]
|
||||||
|
ELEMENT[summary]
|
||||||
|
ELEMENT[sup]
|
||||||
|
ELEMENT[table]
|
||||||
|
ELEMENT[tbody]
|
||||||
|
ELEMENT[td]
|
||||||
|
ELEMENT[template]
|
||||||
|
ELEMENT[textarea]
|
||||||
|
ELEMENT[tfoot]
|
||||||
|
ELEMENT[th]
|
||||||
|
ELEMENT[thead]
|
||||||
|
ELEMENT[time]
|
||||||
|
ELEMENT[title]
|
||||||
|
ELEMENT[tr]
|
||||||
|
ELEMENT[u]
|
||||||
|
ELEMENT[ul]
|
||||||
|
ELEMENT[var]
|
||||||
|
ELEMENT[video]
|
||||||
|
|
||||||
|
STRING[accept]
|
||||||
|
ATTRIBUTE[input:accept]
|
||||||
|
|
||||||
|
STRING[action]
|
||||||
|
ATTRIBUTE[form:action]
|
||||||
|
|
||||||
|
STRING[alt]
|
||||||
|
ATTRIBUTE[area:alt]
|
||||||
|
ATTRIBUTE[img:alt]
|
||||||
|
ATTRIBUTE[input:alt]
|
||||||
|
|
||||||
|
BOOLEAN[async]
|
||||||
|
ATTRIBUTE[script:async]
|
||||||
|
|
||||||
|
ONOFF[autocomplete]
|
||||||
|
ATTRIBUTE[form:autocomplete]
|
||||||
|
ATTRIBUTE[input:autocomplete]
|
||||||
|
|
||||||
|
BOOLEAN[autofocus]
|
||||||
|
ATTRIBUTE[button:autofocus]
|
||||||
|
ATTRIBUTE[input:autofocus]
|
||||||
|
ATTRIBUTE[select:autofocus]
|
||||||
|
ATTRIBUTE[textarea:autofocus]
|
||||||
|
|
||||||
|
BOOLEAN[autoplay]
|
||||||
|
ATTRIBUTE[audio:autoplay]
|
||||||
|
ATTRIBUTE[video:autoplay]
|
||||||
|
|
||||||
|
STRING[charset]
|
||||||
|
ATTRIBUTE[meta:charset]
|
||||||
|
ATTRIBUTE[script:charset]
|
||||||
|
|
||||||
|
BOOLEAN[checked]
|
||||||
|
ATTRIBUTE[input:checked]
|
||||||
|
|
||||||
|
STRING[cite]
|
||||||
|
ATTRIBUTE[blockquote:cite]
|
||||||
|
ATTRIBUTE[del:cite]
|
||||||
|
ATTRIBUTE[ins:cite]
|
||||||
|
ATTRIBUTE[q:cite]
|
||||||
|
|
||||||
|
STRING[cols]
|
||||||
|
ATTRIBUTE[textarea:cols]
|
||||||
|
|
||||||
|
STRING[colspan]
|
||||||
|
ATTRIBUTE[td:colspan]
|
||||||
|
ATTRIBUTE[th:colspan]
|
||||||
|
|
||||||
|
STRING[content]
|
||||||
|
ATTRIBUTE[meta:content]
|
||||||
|
|
||||||
|
BOOLEAN[controls]
|
||||||
|
ATTRIBUTE[audio:controls]
|
||||||
|
ATTRIBUTE[video:controls]
|
||||||
|
|
||||||
|
STRING[coords]
|
||||||
|
ATTRIBUTE[area:coords]
|
||||||
|
|
||||||
|
STRING[data]
|
||||||
|
ATTRIBUTE[object:data]
|
||||||
|
|
||||||
|
STRING[datetime]
|
||||||
|
ATTRIBUTE[del:datetime]
|
||||||
|
ATTRIBUTE[ins:datetime]
|
||||||
|
ATTRIBUTE[time:datetime]
|
||||||
|
|
||||||
|
BOOLEAN[default]
|
||||||
|
ATTRIBUTE[track:default]
|
||||||
|
|
||||||
|
BOOLEAN[defer]
|
||||||
|
ATTRIBUTE[script:defer]
|
||||||
|
|
||||||
|
STRING[dirname]
|
||||||
|
ATTRIBUTE[input:dirname]
|
||||||
|
ATTRIBUTE[textarea:dirname]
|
||||||
|
|
||||||
|
BOOLEAN[disabled]
|
||||||
|
ATTRIBUTE[button:disabled]
|
||||||
|
ATTRIBUTE[fieldset:disabled]
|
||||||
|
ATTRIBUTE[input:disabled]
|
||||||
|
ATTRIBUTE[optgroup:disabled]
|
||||||
|
ATTRIBUTE[option:disabled]
|
||||||
|
ATTRIBUTE[select:disabled]
|
||||||
|
ATTRIBUTE[textarea:disabled]
|
||||||
|
|
||||||
|
BOOLEAN[download]
|
||||||
|
ATTRIBUTE[a:download]
|
||||||
|
ATTRIBUTE[area:download]
|
||||||
|
|
||||||
|
STRING[enctype]
|
||||||
|
ATTRIBUTE[form:enctype]
|
||||||
|
|
||||||
|
STRING[for]
|
||||||
|
ATTRIBUTE[label:for]
|
||||||
|
ATTRIBUTE[output:for]
|
||||||
|
|
||||||
|
STRING[form]
|
||||||
|
ATTRIBUTE[button:form]
|
||||||
|
ATTRIBUTE[fieldset:form]
|
||||||
|
ATTRIBUTE[input:form]
|
||||||
|
ATTRIBUTE[label:form]
|
||||||
|
ATTRIBUTE[meter:form]
|
||||||
|
ATTRIBUTE[object:form]
|
||||||
|
ATTRIBUTE[output:form]
|
||||||
|
ATTRIBUTE[select:form]
|
||||||
|
ATTRIBUTE[textarea:form]
|
||||||
|
|
||||||
|
STRING[formaction]
|
||||||
|
ATTRIBUTE[button:formaction]
|
||||||
|
ATTRIBUTE[input:formaction]
|
||||||
|
|
||||||
|
STRING[headers]
|
||||||
|
ATTRIBUTE[td:headers]
|
||||||
|
ATTRIBUTE[th:headers]
|
||||||
|
|
||||||
|
STRING[height]
|
||||||
|
ATTRIBUTE[canvas:height]
|
||||||
|
ATTRIBUTE[embed:height]
|
||||||
|
ATTRIBUTE[iframe:height]
|
||||||
|
ATTRIBUTE[img:height]
|
||||||
|
ATTRIBUTE[input:height]
|
||||||
|
ATTRIBUTE[object:height]
|
||||||
|
ATTRIBUTE[video:height]
|
||||||
|
|
||||||
|
STRING[high]
|
||||||
|
ATTRIBUTE[meter:high]
|
||||||
|
|
||||||
|
STRING[href]
|
||||||
|
ATTRIBUTE[a:href]
|
||||||
|
ATTRIBUTE[area:href]
|
||||||
|
ATTRIBUTE[base:href]
|
||||||
|
ATTRIBUTE[link:href]
|
||||||
|
|
||||||
|
STRING[hreflang]
|
||||||
|
ATTRIBUTE[a:hreflang]
|
||||||
|
ATTRIBUTE[area:hreflang]
|
||||||
|
ATTRIBUTE[link:hreflang]
|
||||||
|
|
||||||
|
BOOLEAN[ismap]
|
||||||
|
ATTRIBUTE[img:ismap]
|
||||||
|
|
||||||
|
STRING[kind]
|
||||||
|
ATTRIBUTE[track:kind]
|
||||||
|
|
||||||
|
STRING[label]
|
||||||
|
ATTRIBUTE[track:label]
|
||||||
|
ATTRIBUTE[option:label]
|
||||||
|
ATTRIBUTE[optgroup:label]
|
||||||
|
|
||||||
|
STRING[list]
|
||||||
|
ATTRIBUTE[input:list]
|
||||||
|
|
||||||
|
BOOLEAN[loop]
|
||||||
|
ATTRIBUTE[audio:loop]
|
||||||
|
ATTRIBUTE[video:loop]
|
||||||
|
|
||||||
|
STRING[low]
|
||||||
|
ATTRIBUTE[meter:low]
|
||||||
|
|
||||||
|
STRING[max]
|
||||||
|
ATTRIBUTE[input:max]
|
||||||
|
ATTRIBUTE[meter:max]
|
||||||
|
ATTRIBUTE[progress:max]
|
||||||
|
|
||||||
|
STRING[maxlength]
|
||||||
|
ATTRIBUTE[input:maxlength]
|
||||||
|
ATTRIBUTE[textarea:maxlength]
|
||||||
|
|
||||||
|
STRING[media]
|
||||||
|
ATTRIBUTE[a:media]
|
||||||
|
ATTRIBUTE[area:media]
|
||||||
|
ATTRIBUTE[link:media]
|
||||||
|
ATTRIBUTE[source:media]
|
||||||
|
ATTRIBUTE[style:media]
|
||||||
|
|
||||||
|
STRING[method]
|
||||||
|
ATTRIBUTE[form:method]
|
||||||
|
|
||||||
|
STRING[min]
|
||||||
|
ATTRIBUTE[input:min]
|
||||||
|
ATTRIBUTE[meter:min]
|
||||||
|
|
||||||
|
BOOLEAN[multiple]
|
||||||
|
ATTRIBUTE[input:multiple]
|
||||||
|
ATTRIBUTE[select:multiple]
|
||||||
|
|
||||||
|
BOOLEAN[muted]
|
||||||
|
ATTRIBUTE[video:muted]
|
||||||
|
ATTRIBUTE[audio:muted]
|
||||||
|
|
||||||
|
STRING[name]
|
||||||
|
ATTRIBUTE[button:name]
|
||||||
|
ATTRIBUTE[fieldset:name]
|
||||||
|
ATTRIBUTE[form:name]
|
||||||
|
ATTRIBUTE[iframe:name]
|
||||||
|
ATTRIBUTE[input:name]
|
||||||
|
ATTRIBUTE[map:name]
|
||||||
|
ATTRIBUTE[meta:name]
|
||||||
|
ATTRIBUTE[object:name]
|
||||||
|
ATTRIBUTE[output:name]
|
||||||
|
ATTRIBUTE[param:name]
|
||||||
|
ATTRIBUTE[select:name]
|
||||||
|
ATTRIBUTE[slot:name]
|
||||||
|
ATTRIBUTE[textarea:name]
|
||||||
|
|
||||||
|
BOOLEAN[novalidate]
|
||||||
|
ATTRIBUTE[form:novalidate]
|
||||||
|
|
||||||
|
STRING[onabort]
|
||||||
|
ATTRIBUTE[audio:onabort]
|
||||||
|
ATTRIBUTE[embed:onabort]
|
||||||
|
ATTRIBUTE[img:onabort]
|
||||||
|
ATTRIBUTE[object:onabort]
|
||||||
|
ATTRIBUTE[video:onabort]
|
||||||
|
|
||||||
|
STRING[onafterprint]
|
||||||
|
ATTRIBUTE[body:onafterprint]
|
||||||
|
|
||||||
|
STRING[onbeforeprint]
|
||||||
|
ATTRIBUTE[body:onbeforeprint]
|
||||||
|
|
||||||
|
STRING[onbeforeunload]
|
||||||
|
ATTRIBUTE[body:onbeforeunload]
|
||||||
|
|
||||||
|
STRING[oncanplay]
|
||||||
|
ATTRIBUTE[audio:oncanplay]
|
||||||
|
ATTRIBUTE[embed:oncanplay]
|
||||||
|
ATTRIBUTE[object:oncanplay]
|
||||||
|
ATTRIBUTE[video:oncanplay]
|
||||||
|
|
||||||
|
STRING[oncanplaythrough]
|
||||||
|
ATTRIBUTE[audio:oncanplaythrough]
|
||||||
|
ATTRIBUTE[video:oncanplaythrough]
|
||||||
|
|
||||||
|
STRING[oncuechange]
|
||||||
|
ATTRIBUTE[track:oncuechange]
|
||||||
|
|
||||||
|
STRING[ondurationchange]
|
||||||
|
ATTRIBUTE[audio:ondurationchange]
|
||||||
|
ATTRIBUTE[video:ondurationchange]
|
||||||
|
|
||||||
|
STRING[onemptied]
|
||||||
|
ATTRIBUTE[audio:onemptied]
|
||||||
|
ATTRIBUTE[video:onemptied]
|
||||||
|
|
||||||
|
STRING[onended]
|
||||||
|
ATTRIBUTE[audio:onended]
|
||||||
|
ATTRIBUTE[video:onended]
|
||||||
|
|
||||||
|
STRING[onerror]
|
||||||
|
ATTRIBUTE[audio:onerror]
|
||||||
|
ATTRIBUTE[body:onerror]
|
||||||
|
ATTRIBUTE[embed:onerror]
|
||||||
|
ATTRIBUTE[img:onerror]
|
||||||
|
ATTRIBUTE[object:onerror]
|
||||||
|
ATTRIBUTE[script:onerror]
|
||||||
|
ATTRIBUTE[style:onerror]
|
||||||
|
ATTRIBUTE[video:onerror]
|
||||||
|
|
||||||
|
STRING[onhashchange]
|
||||||
|
ATTRIBUTE[body:onhashchange]
|
||||||
|
|
||||||
|
STRING[onload]
|
||||||
|
ATTRIBUTE[body:onload]
|
||||||
|
ATTRIBUTE[iframe:onload]
|
||||||
|
ATTRIBUTE[img:onload]
|
||||||
|
ATTRIBUTE[input:onload]
|
||||||
|
ATTRIBUTE[link:onload]
|
||||||
|
ATTRIBUTE[script:onload]
|
||||||
|
ATTRIBUTE[style:onload]
|
||||||
|
|
||||||
|
STRING[onloadeddata]
|
||||||
|
ATTRIBUTE[audio:onloadeddata]
|
||||||
|
ATTRIBUTE[video:onloadeddata]
|
||||||
|
|
||||||
|
STRING[onloadedmetadata]
|
||||||
|
ATTRIBUTE[audio:onloadedmetadata]
|
||||||
|
ATTRIBUTE[video:onloadedmetadata]
|
||||||
|
|
||||||
|
STRING[onloadstart]
|
||||||
|
ATTRIBUTE[audio:onloadstart]
|
||||||
|
ATTRIBUTE[video:onloadstart]
|
||||||
|
|
||||||
|
STRING[onoffline]
|
||||||
|
ATTRIBUTE[body:onoffline]
|
||||||
|
|
||||||
|
STRING[ononline]
|
||||||
|
ATTRIBUTE[body:ononline]
|
||||||
|
|
||||||
|
STRING[onpagehide]
|
||||||
|
ATTRIBUTE[body:onpagehide]
|
||||||
|
|
||||||
|
STRING[onpageshow]
|
||||||
|
ATTRIBUTE[body:onpageshow]
|
||||||
|
|
||||||
|
STRING[onpause]
|
||||||
|
ATTRIBUTE[audio:onpause]
|
||||||
|
ATTRIBUTE[video:onpause]
|
||||||
|
|
||||||
|
STRING[onplay]
|
||||||
|
ATTRIBUTE[audio:onplay]
|
||||||
|
ATTRIBUTE[video:onplay]
|
||||||
|
|
||||||
|
STRING[onplaying]
|
||||||
|
ATTRIBUTE[audio:onplaying]
|
||||||
|
ATTRIBUTE[video:onplaying]
|
||||||
|
|
||||||
|
STRING[onpopstate]
|
||||||
|
ATTRIBUTE[body:onpopstate]
|
||||||
|
|
||||||
|
STRING[onprogress]
|
||||||
|
ATTRIBUTE[audio:onprogress]
|
||||||
|
ATTRIBUTE[video:onprogress]
|
||||||
|
|
||||||
|
STRING[onratechange]
|
||||||
|
ATTRIBUTE[audio:onratechange]
|
||||||
|
ATTRIBUTE[video:onratechange]
|
||||||
|
|
||||||
|
STRING[onreset]
|
||||||
|
ATTRIBUTE[form:onreset]
|
||||||
|
|
||||||
|
STRING[onresize]
|
||||||
|
ATTRIBUTE[body:onresize]
|
||||||
|
|
||||||
|
STRING[onsearch]
|
||||||
|
ATTRIBUTE[input:onsearch]
|
||||||
|
|
||||||
|
STRING[onseeked]
|
||||||
|
ATTRIBUTE[audio:onseeked]
|
||||||
|
ATTRIBUTE[video:onseeked]
|
||||||
|
|
||||||
|
STRING[onseeking]
|
||||||
|
ATTRIBUTE[audio:onseeking]
|
||||||
|
ATTRIBUTE[video:onseeking]
|
||||||
|
|
||||||
|
STRING[onstalled]
|
||||||
|
ATTRIBUTE[audio:onstalled]
|
||||||
|
ATTRIBUTE[video:onstalled]
|
||||||
|
|
||||||
|
STRING[onstorage]
|
||||||
|
ATTRIBUTE[body:onstorage]
|
||||||
|
|
||||||
|
STRING[onsubmit]
|
||||||
|
ATTRIBUTE[form:onsubmit]
|
||||||
|
|
||||||
|
STRING[onsuspend]
|
||||||
|
ATTRIBUTE[audio:onsuspend]
|
||||||
|
ATTRIBUTE[video:onsuspend]
|
||||||
|
|
||||||
|
STRING[ontimeupdate]
|
||||||
|
ATTRIBUTE[audio:ontimeupdate]
|
||||||
|
ATTRIBUTE[video:ontimeupdate]
|
||||||
|
|
||||||
|
STRING[ontoggle]
|
||||||
|
ATTRIBUTE[details:ontoggle]
|
||||||
|
|
||||||
|
STRING[onunload]
|
||||||
|
ATTRIBUTE[body:onunload]
|
||||||
|
|
||||||
|
STRING[onvolumechanged]
|
||||||
|
ATTRIBUTE[audio:onvolumechanged]
|
||||||
|
ATTRIBUTE[video:onvolumechanged]
|
||||||
|
|
||||||
|
STRING[onwaiting]
|
||||||
|
ATTRIBUTE[audio:onwaiting]
|
||||||
|
ATTRIBUTE[video:onwaiting]
|
||||||
|
|
||||||
|
BOOLEAN[open]
|
||||||
|
ATTRIBUTE[details:open]
|
||||||
|
|
||||||
|
STRING[optimum]
|
||||||
|
ATTRIBUTE[meter:optimum]
|
||||||
|
|
||||||
|
STRING[pattern]
|
||||||
|
ATTRIBUTE[input:pattern]
|
||||||
|
|
||||||
|
STRING[placeholder]
|
||||||
|
ATTRIBUTE[input:placeholder]
|
||||||
|
ATTRIBUTE[textarea:placeholder]
|
||||||
|
|
||||||
|
STRING[poster]
|
||||||
|
ATTRIBUTE[video:poster]
|
||||||
|
|
||||||
|
STRING[preload]
|
||||||
|
ATTRIBUTE[audio:preload]
|
||||||
|
ATTRIBUTE[video:preload]
|
||||||
|
|
||||||
|
BOOLEAN[readonly]
|
||||||
|
ATTRIBUTE[input:readonly]
|
||||||
|
ATTRIBUTE[textarea:readonly]
|
||||||
|
|
||||||
|
STRING[rel]
|
||||||
|
ATTRIBUTE[a:rel]
|
||||||
|
ATTRIBUTE[area:rel]
|
||||||
|
ATTRIBUTE[form:rel]
|
||||||
|
ATTRIBUTE[link:rel]
|
||||||
|
|
||||||
|
BOOLEAN[required]
|
||||||
|
ATTRIBUTE[input:required]
|
||||||
|
ATTRIBUTE[select:required]
|
||||||
|
ATTRIBUTE[textarea:required]
|
||||||
|
|
||||||
|
BOOLEAN[reversed]
|
||||||
|
ATTRIBUTE[ol:reversed]
|
||||||
|
|
||||||
|
STRING[rows]
|
||||||
|
ATTRIBUTE[textarea:rows]
|
||||||
|
|
||||||
|
STRING[rowspan]
|
||||||
|
ATTRIBUTE[td:rowspan]
|
||||||
|
ATTRIBUTE[th:rowspan]
|
||||||
|
|
||||||
|
BOOLEAN[sandbox]
|
||||||
|
ATTRIBUTE[iframe:sandbox]
|
||||||
|
|
||||||
|
STRING[scope]
|
||||||
|
ATTRIBUTE[th:scope]
|
||||||
|
|
||||||
|
BOOLEAN[selected]
|
||||||
|
ATTRIBUTE[option:selected]
|
||||||
|
|
||||||
|
STRING[shape]
|
||||||
|
ATTRIBUTE[area:shape]
|
||||||
|
|
||||||
|
STRING[size]
|
||||||
|
ATTRIBUTE[input:size]
|
||||||
|
ATTRIBUTE[select:size]
|
||||||
|
|
||||||
|
STRING[sizes]
|
||||||
|
ATTRIBUTE[img:sizes]
|
||||||
|
ATTRIBUTE[link:sizes]
|
||||||
|
ATTRIBUTE[source:sizes]
|
||||||
|
|
||||||
|
STRING[span]
|
||||||
|
ATTRIBUTE[col:span]
|
||||||
|
ATTRIBUTE[colgroup:span]
|
||||||
|
|
||||||
|
STRING[src]
|
||||||
|
ATTRIBUTE[audio:src]
|
||||||
|
ATTRIBUTE[embed:src]
|
||||||
|
ATTRIBUTE[iframe:src]
|
||||||
|
ATTRIBUTE[img:src]
|
||||||
|
ATTRIBUTE[input:src]
|
||||||
|
ATTRIBUTE[script:src]
|
||||||
|
ATTRIBUTE[source:src]
|
||||||
|
ATTRIBUTE[track:src]
|
||||||
|
ATTRIBUTE[video:src]
|
||||||
|
|
||||||
|
STRING[srcdoc]
|
||||||
|
ATTRIBUTE[iframe:srcdoc]
|
||||||
|
|
||||||
|
STRING[srclang]
|
||||||
|
ATTRIBUTE[track:srclang]
|
||||||
|
|
||||||
|
STRING[srcset]
|
||||||
|
ATTRIBUTE[img:srcset]
|
||||||
|
ATTRIBUTE[source:srcset]
|
||||||
|
|
||||||
|
STRING[start]
|
||||||
|
ATTRIBUTE[ol:start]
|
||||||
|
|
||||||
|
STRING[step]
|
||||||
|
ATTRIBUTE[input:step]
|
||||||
|
|
||||||
|
STRING[target]
|
||||||
|
ATTRIBUTE[a:target]
|
||||||
|
ATTRIBUTE[area:target]
|
||||||
|
ATTRIBUTE[base:target]
|
||||||
|
ATTRIBUTE[form:target]
|
||||||
|
|
||||||
|
STRING[type]
|
||||||
|
ATTRIBUTE[a:type]
|
||||||
|
ATTRIBUTE[button:type]
|
||||||
|
ATTRIBUTE[embed:type]
|
||||||
|
ATTRIBUTE[input:type]
|
||||||
|
ATTRIBUTE[link:type]
|
||||||
|
ATTRIBUTE[menu:type]
|
||||||
|
ATTRIBUTE[object:type]
|
||||||
|
ATTRIBUTE[script:type]
|
||||||
|
ATTRIBUTE[source:type]
|
||||||
|
ATTRIBUTE[style:type]
|
||||||
|
|
||||||
|
STRING[usemap]
|
||||||
|
ATTRIBUTE[img:usemap]
|
||||||
|
ATTRIBUTE[object:usemap]
|
||||||
|
|
||||||
|
STRING[value]
|
||||||
|
ATTRIBUTE[button:value]
|
||||||
|
ATTRIBUTE[data:value]
|
||||||
|
ATTRIBUTE[input:value]
|
||||||
|
ATTRIBUTE[li:value]
|
||||||
|
ATTRIBUTE[option:value]
|
||||||
|
ATTRIBUTE[meter:value]
|
||||||
|
ATTRIBUTE[progress:value]
|
||||||
|
ATTRIBUTE[param:value]
|
||||||
|
|
||||||
|
STRING[width]
|
||||||
|
ATTRIBUTE[canvas:width]
|
||||||
|
ATTRIBUTE[embed:width]
|
||||||
|
ATTRIBUTE[iframe:width]
|
||||||
|
ATTRIBUTE[img:width]
|
||||||
|
ATTRIBUTE[input:width]
|
||||||
|
ATTRIBUTE[object:width]
|
||||||
|
ATTRIBUTE[video:width]
|
||||||
|
|
||||||
|
STRING[wrap]
|
||||||
|
ATTRIBUTE[textarea:wrap]
|
|
@ -1,2 +1,2 @@
|
||||||
name = gradle-plugin-jacc
|
name = gradle-plugin-jacc
|
||||||
version = 3.0.2
|
version = 3.1.0
|
||||||
|
|
202
gradle-plugin-java-conventions/LICENSE.txt
Normal file
202
gradle-plugin-java-conventions/LICENSE.txt
Normal file
|
@ -0,0 +1,202 @@
|
||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
32
gradle-plugin-java-conventions/build.gradle
Executable file
32
gradle-plugin-java-conventions/build.gradle
Executable file
|
@ -0,0 +1,32 @@
|
||||||
|
plugins {
|
||||||
|
id 'java-gradle-plugin'
|
||||||
|
alias(libs.plugins.publish)
|
||||||
|
}
|
||||||
|
|
||||||
|
apply plugin: 'java-gradle-plugin'
|
||||||
|
apply plugin: 'com.gradle.plugin-publish'
|
||||||
|
|
||||||
|
apply from: rootProject.file('gradle/compile/groovy.gradle')
|
||||||
|
apply from: rootProject.file('gradle/test/junit5.gradle')
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
api gradleApi()
|
||||||
|
testImplementation gradleTestKit()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (project.hasProperty('gradle.publish.key')) {
|
||||||
|
gradlePlugin {
|
||||||
|
website = 'https://xbib.org/joerg/gradle-plugins/src/branch/main/gradle-plugin-java-conventions'
|
||||||
|
vcsUrl = 'https://xbib.org/joerg/gradle-plugins'
|
||||||
|
plugins {
|
||||||
|
javaConventionsPlugin {
|
||||||
|
id = 'org.xbib.gradle.plugin.java.conventions'
|
||||||
|
implementationClass = 'org.xbib.gradle.plugin.java.conventions.JavaConventionsPlugin'
|
||||||
|
version = project.version
|
||||||
|
description = 'Gradle Java Conventions plugin'
|
||||||
|
displayName = 'Gradle Java Conventions plugin'
|
||||||
|
tags.set(['java', 'conventions'])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
2
gradle-plugin-java-conventions/gradle.properties
Normal file
2
gradle-plugin-java-conventions/gradle.properties
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
name = gradle-plugin-java-conventions
|
||||||
|
version = 1.0.0
|
|
@ -0,0 +1,11 @@
|
||||||
|
package org.xbib.gradle.plugin.java.conventions
|
||||||
|
|
||||||
|
class JavaConventionsExtension {
|
||||||
|
|
||||||
|
boolean runTestsParallel = false
|
||||||
|
|
||||||
|
int testThreads = Runtime.runtime.availableProcessors()
|
||||||
|
|
||||||
|
String jacocoVersion = "0.7.6.201602180812"
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
package org.xbib.gradle.plugin.java.conventions
|
||||||
|
|
||||||
|
import org.gradle.api.Plugin
|
||||||
|
import org.gradle.api.Project
|
||||||
|
import org.gradle.api.tasks.compile.JavaCompile
|
||||||
|
import org.gradle.api.tasks.javadoc.Javadoc
|
||||||
|
import org.gradle.jvm.toolchain.JavaLanguageVersion
|
||||||
|
|
||||||
|
class JavaConventionsPlugin implements Plugin<Project> {
|
||||||
|
|
||||||
|
JavaConventionsExtension extension
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void apply(Project project) {
|
||||||
|
extension = project.extensions.create('javaConventions', JavaConventionsExtension)
|
||||||
|
|
||||||
|
project.apply plugin: 'java-library'
|
||||||
|
|
||||||
|
project.java {
|
||||||
|
toolchain {
|
||||||
|
languageVersion = JavaLanguageVersion.of(21)
|
||||||
|
}
|
||||||
|
modularity.inferModulePath.set(true)
|
||||||
|
withSourcesJar()
|
||||||
|
withJavadocJar()
|
||||||
|
}
|
||||||
|
|
||||||
|
project.jar {
|
||||||
|
manifest {
|
||||||
|
attributes('Implementation-Version': project.version)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
project.tasks.withType(JavaCompile).configureEach {
|
||||||
|
options.fork = true
|
||||||
|
options.forkOptions.jvmArgs += ['-Duser.language=en','-Duser.country=US']
|
||||||
|
options.compilerArgs.add('-Xlint:-requires-transitive-automatic')
|
||||||
|
options.compilerArgs.add('-Xlint:deprecation')
|
||||||
|
options.encoding = 'UTF-8'
|
||||||
|
}
|
||||||
|
|
||||||
|
project.tasks.withType(Javadoc).configureEach {
|
||||||
|
options.addStringOption('Xdoclint:none', '-quiet')
|
||||||
|
options.encoding = 'UTF-8'
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
10
gradle-plugin-jdeps/NOTICE.txt
Normal file
10
gradle-plugin-jdeps/NOTICE.txt
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
|
||||||
|
This work is based on
|
||||||
|
|
||||||
|
https://github.com/kordamp/jdeps-gradle-plugin
|
||||||
|
|
||||||
|
by Andres Almiray
|
||||||
|
|
||||||
|
as of Feb 17, 2024
|
||||||
|
|
||||||
|
License: Apache 2.0
|
32
gradle-plugin-jdeps/build.gradle
Executable file
32
gradle-plugin-jdeps/build.gradle
Executable file
|
@ -0,0 +1,32 @@
|
||||||
|
plugins {
|
||||||
|
id 'java-gradle-plugin'
|
||||||
|
alias(libs.plugins.publish)
|
||||||
|
}
|
||||||
|
|
||||||
|
apply plugin: 'java-gradle-plugin'
|
||||||
|
apply plugin: 'com.gradle.plugin-publish'
|
||||||
|
|
||||||
|
apply from: rootProject.file('gradle/compile/groovy.gradle')
|
||||||
|
apply from: rootProject.file('gradle/test/junit5.gradle')
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
api gradleApi()
|
||||||
|
testImplementation gradleTestKit()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (project.hasProperty('gradle.publish.key')) {
|
||||||
|
gradlePlugin {
|
||||||
|
website = 'https://xbib.org/joerg/gradle-plugins/src/branch/main/gradle-plugin-jdeps'
|
||||||
|
vcsUrl = 'https://xbib.org/joerg/gradle-plugins'
|
||||||
|
plugins {
|
||||||
|
jdepsPlugin {
|
||||||
|
id = 'org.xbib.gradle.plugin.jdeps'
|
||||||
|
implementationClass = 'org.xbib.gradle.plugin.jdeps.JDepsPlugin'
|
||||||
|
version = project.version
|
||||||
|
description = 'Gradle JDeps plugin'
|
||||||
|
displayName = 'Gradle JDeps plugin'
|
||||||
|
tags.set(['jdeps'])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
package org.xbib.gradle.plugin.jdeps
|
||||||
|
|
||||||
|
import org.gradle.api.Action
|
||||||
|
import org.gradle.api.Plugin
|
||||||
|
import org.gradle.api.Project
|
||||||
|
import org.gradle.api.Task
|
||||||
|
import org.gradle.api.plugins.BasePlugin
|
||||||
|
import org.gradle.api.plugins.JavaPlugin
|
||||||
|
import org.gradle.api.plugins.JavaPluginConvention
|
||||||
|
import org.gradle.api.tasks.TaskProvider
|
||||||
|
import org.gradle.api.tasks.compile.JavaCompile
|
||||||
|
|
||||||
|
class JDepsPlugin implements Plugin<Project> {
|
||||||
|
|
||||||
|
void apply(Project project) {
|
||||||
|
project.plugins.apply(JavaPlugin)
|
||||||
|
TaskProvider<JDepsReportTask> report = project.tasks.register('jdepsReport', JDepsReportTask,
|
||||||
|
new Action<JDepsReportTask>() {
|
||||||
|
@Override
|
||||||
|
void execute(JDepsReportTask t) {
|
||||||
|
t.dependsOn(project.tasks.named('classes'))
|
||||||
|
t.group = BasePlugin.BUILD_GROUP
|
||||||
|
t.description = 'Generate a jdeps report on project classes and dependencies'
|
||||||
|
t.compileJava = project.tasks.named(JavaPlugin.COMPILE_JAVA_TASK_NAME, JavaCompile)
|
||||||
|
t.javaPluginConvention.set(project.convention.getPlugin(JavaPluginConvention))
|
||||||
|
t.projectName.set(project.name)
|
||||||
|
t.reportDir.convention(project.layout.buildDirectory.dir('reports/jdeps'))
|
||||||
|
t.projectConfigurations = project.configurations
|
||||||
|
}
|
||||||
|
})
|
||||||
|
project.tasks.named('check').configure(new Action<Task>() {
|
||||||
|
@Override
|
||||||
|
void execute(Task t) {
|
||||||
|
t.dependsOn(report)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,674 @@
|
||||||
|
package org.xbib.gradle.plugin.jdeps
|
||||||
|
|
||||||
|
import org.gradle.api.DefaultTask
|
||||||
|
import org.gradle.api.JavaVersion
|
||||||
|
import org.gradle.api.artifacts.Configuration
|
||||||
|
import org.gradle.api.artifacts.ConfigurationContainer
|
||||||
|
import org.gradle.api.file.Directory
|
||||||
|
import org.gradle.api.file.DirectoryProperty
|
||||||
|
import org.gradle.api.model.ObjectFactory
|
||||||
|
import org.gradle.api.plugins.JavaPluginConvention
|
||||||
|
import org.gradle.api.provider.ListProperty
|
||||||
|
import org.gradle.api.provider.MapProperty
|
||||||
|
import org.gradle.api.provider.Property
|
||||||
|
import org.gradle.api.provider.Provider
|
||||||
|
import org.gradle.api.tasks.Input
|
||||||
|
import org.gradle.api.tasks.InputDirectory
|
||||||
|
import org.gradle.api.tasks.Internal
|
||||||
|
import org.gradle.api.tasks.Optional
|
||||||
|
import org.gradle.api.tasks.OutputDirectory
|
||||||
|
import org.gradle.api.tasks.SourceSet
|
||||||
|
import org.gradle.api.tasks.TaskAction
|
||||||
|
import org.gradle.api.tasks.TaskProvider
|
||||||
|
import org.gradle.api.tasks.compile.JavaCompile
|
||||||
|
import org.gradle.api.tasks.options.Option
|
||||||
|
import org.gradle.process.ExecOperations
|
||||||
|
import org.xbib.gradle.plugin.jdeps.util.BooleanState
|
||||||
|
import org.xbib.gradle.plugin.jdeps.util.DirectoryState
|
||||||
|
import org.xbib.gradle.plugin.jdeps.util.ListState
|
||||||
|
import org.xbib.gradle.plugin.jdeps.util.MapState
|
||||||
|
import org.xbib.gradle.plugin.jdeps.util.SimpleBooleanState
|
||||||
|
import org.xbib.gradle.plugin.jdeps.util.SimpleDirectoryState
|
||||||
|
import org.xbib.gradle.plugin.jdeps.util.SimpleListState
|
||||||
|
import org.xbib.gradle.plugin.jdeps.util.SimpleMapState
|
||||||
|
import org.xbib.gradle.plugin.jdeps.util.SimpleStringState
|
||||||
|
import org.xbib.gradle.plugin.jdeps.util.StringState
|
||||||
|
|
||||||
|
import javax.inject.Inject
|
||||||
|
import java.util.regex.Pattern
|
||||||
|
|
||||||
|
class JDepsReportTask extends DefaultTask {
|
||||||
|
|
||||||
|
private static final Pattern WARNING = Pattern.compile("^(?:Warning:.*)|(?:.+->\\s([a-zA-Z\\.]+)\\s+JDK internal API.*)")
|
||||||
|
|
||||||
|
private static final Pattern ERROR = Pattern.compile("^(?:Error:.*)|Exception in thread.*")
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
protected ExecOperations execOperations;
|
||||||
|
|
||||||
|
private final BooleanState listDeps
|
||||||
|
|
||||||
|
private final BooleanState listReducedDeps
|
||||||
|
|
||||||
|
private final BooleanState printModuleDeps
|
||||||
|
|
||||||
|
private final BooleanState verbose
|
||||||
|
|
||||||
|
private final BooleanState modular
|
||||||
|
|
||||||
|
private final BooleanState summary
|
||||||
|
|
||||||
|
private final BooleanState profile
|
||||||
|
|
||||||
|
private final BooleanState recursive
|
||||||
|
|
||||||
|
private final BooleanState jdkinternals
|
||||||
|
|
||||||
|
private final BooleanState consoleOutput
|
||||||
|
|
||||||
|
private final BooleanState apionly
|
||||||
|
|
||||||
|
private final BooleanState failOnWarning
|
||||||
|
|
||||||
|
private final BooleanState missingDeps
|
||||||
|
|
||||||
|
private final BooleanState ignoreMissingDeps
|
||||||
|
|
||||||
|
private final ListState pkgs
|
||||||
|
|
||||||
|
private final ListState requires
|
||||||
|
|
||||||
|
private final StringState include
|
||||||
|
|
||||||
|
private final StringState regex
|
||||||
|
|
||||||
|
private final StringState filter
|
||||||
|
|
||||||
|
private final ListState configurations
|
||||||
|
|
||||||
|
private final ListState classpaths
|
||||||
|
|
||||||
|
private final ListState sourceSets
|
||||||
|
|
||||||
|
private final StringState multiRelease
|
||||||
|
|
||||||
|
private final MapState multiReleaseJars
|
||||||
|
|
||||||
|
private final DirectoryState dotOutput
|
||||||
|
|
||||||
|
@OutputDirectory
|
||||||
|
final DirectoryProperty reportDir
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
TaskProvider<JavaCompile> compileJava
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
final Property<JavaPluginConvention> javaPluginConvention
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
final Property<String> projectName
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
ConfigurationContainer projectConfigurations
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
JDepsReportTask(ObjectFactory objects) {
|
||||||
|
extensions.create('moduleOptions', ModuleOptions)
|
||||||
|
reportDir = objects.directoryProperty()
|
||||||
|
projectName = objects.property(String)
|
||||||
|
javaPluginConvention = objects.property(JavaPluginConvention)
|
||||||
|
listDeps = SimpleBooleanState.of(this, 'jdeps.list.deps', false)
|
||||||
|
listReducedDeps = SimpleBooleanState.of(this, 'jdeps.list.reduced.deps', false)
|
||||||
|
printModuleDeps = SimpleBooleanState.of(this, 'jdeps.print.module.deps', false)
|
||||||
|
verbose = SimpleBooleanState.of(this, 'jdeps.verbose', false)
|
||||||
|
modular = SimpleBooleanState.of(this, 'jdeps.modular', false)
|
||||||
|
summary = SimpleBooleanState.of(this, 'jdeps.summary', false)
|
||||||
|
profile = SimpleBooleanState.of(this, 'jdeps.profile', false)
|
||||||
|
recursive = SimpleBooleanState.of(this, 'jdeps.recursive', false)
|
||||||
|
jdkinternals = SimpleBooleanState.of(this, 'jdeps.jdkinternals', false)
|
||||||
|
consoleOutput = SimpleBooleanState.of(this, 'jdeps.console.output', true)
|
||||||
|
apionly = SimpleBooleanState.of(this, 'jdeps.apionly', false)
|
||||||
|
failOnWarning = SimpleBooleanState.of(this, 'jdeps.fail.on.warning', false)
|
||||||
|
missingDeps = SimpleBooleanState.of(this, 'jdeps.missing.deps', false)
|
||||||
|
ignoreMissingDeps = SimpleBooleanState.of(this, 'jdeps.ignore.missing.deps', false)
|
||||||
|
include = SimpleStringState.of(this, 'jdeps.include', "")
|
||||||
|
regex = SimpleStringState.of(this, 'jdeps.regex', "")
|
||||||
|
filter = SimpleStringState.of(this, 'jdeps.filter', "")
|
||||||
|
pkgs = SimpleListState.of(this, 'jdeps.package', [])
|
||||||
|
requires = SimpleListState.of(this, 'jdeps.require', [])
|
||||||
|
configurations = SimpleListState.of(this, 'jdeps.configurations', [])
|
||||||
|
classpaths = SimpleListState.of(this, 'jdeps.classpaths', [])
|
||||||
|
sourceSets = SimpleListState.of(this, 'jdeps.sourcesets', ['main'])
|
||||||
|
multiRelease = SimpleStringState.of(this, 'jdeps.multi.release', 'base')
|
||||||
|
multiReleaseJars = SimpleMapState.of(this, 'jdeps.multi.release.jars', [:])
|
||||||
|
dotOutput = SimpleDirectoryState.of(this, 'jdeps.dot.output', (Directory) null)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Option(option = 'list-deps', description = 'Lists the module dependences')
|
||||||
|
void setListDeps(boolean value) { listDeps.property.set(value) }
|
||||||
|
|
||||||
|
@Option(option = 'list-reduced-deps', description = 'Lists the module dependences')
|
||||||
|
void setListReducedDeps(boolean value) { listReducedDeps.property.set(value) }
|
||||||
|
|
||||||
|
@Option(option = 'print-module-deps', description = 'Comma-separated list of module dependences')
|
||||||
|
void setPrintModuleDeps(boolean value) { printModuleDeps.property.set(value) }
|
||||||
|
|
||||||
|
@Option(option = 'verbose', description = 'Print all class level dependences')
|
||||||
|
void setVerbose(boolean value) { verbose.property.set(value) }
|
||||||
|
|
||||||
|
@Option(option = 'modular', description = 'Uses the module path instead of the classpath')
|
||||||
|
void setModular(boolean value) { modular.property.set(value) }
|
||||||
|
|
||||||
|
@Option(option = 'summary', description = 'Print dependency summary only')
|
||||||
|
void setSummary(boolean value) { summary.property.set(value) }
|
||||||
|
|
||||||
|
@Option(option = 'profile', description = 'Show profile containing a package')
|
||||||
|
void setProfile(boolean value) { profile.property.set(value) }
|
||||||
|
|
||||||
|
@Option(option = 'recursive', description = 'Recursively traverse all run-time dependences')
|
||||||
|
void setRecursive(boolean value) { recursive.property.set(value) }
|
||||||
|
|
||||||
|
@Option(option = 'jdkinternals', description = 'Finds class-level dependences on JDK internal APIs')
|
||||||
|
void setJdkinternals(boolean value) { jdkinternals.property.set(value) }
|
||||||
|
|
||||||
|
@Option(option = 'console-output', description = 'Print out report to console')
|
||||||
|
void setConsoleOutput(boolean value) { consoleOutput.property.set(value) }
|
||||||
|
|
||||||
|
@Option(option = 'apionly', description = 'Restrict analysis to APIs')
|
||||||
|
void setApionly(boolean value) { apionly.property.set(value) }
|
||||||
|
|
||||||
|
@Option(option = 'fail-on-warning', description = 'Fails the build if jdeps finds any warnings')
|
||||||
|
void setFailOnWarning(boolean value) { failOnWarning.property.set(value) }
|
||||||
|
|
||||||
|
@Option(option = 'missing-deps', description = 'Finds missing dependences')
|
||||||
|
void setMissingDeps(boolean value) { missingDeps.property.set(value) }
|
||||||
|
|
||||||
|
@Option(option = 'ignore-missing-deps', description = 'Ignore missing dependences')
|
||||||
|
void setIgnoreMissingDeps(boolean value) { ignoreMissingDeps.property.set(value) }
|
||||||
|
|
||||||
|
@Option(option = 'include', description = 'Restrict analysis to classes matching pattern')
|
||||||
|
void setInclude(String value) { include.property.set(value) }
|
||||||
|
|
||||||
|
@Option(option = 'regex', description = 'Finds dependences matching the given pattern')
|
||||||
|
void setRegex(String value) { regex.property.set(value) }
|
||||||
|
|
||||||
|
@Option(option = 'filter', description = 'Filter dependences matching the given pattern')
|
||||||
|
void setFilter(String value) { filter.property.set(value) }
|
||||||
|
|
||||||
|
@Option(option = 'package', description = 'Finds dependences matching the given package name. REPEATABLE')
|
||||||
|
void setPackage(String value) { pkgs.property.add(value) }
|
||||||
|
|
||||||
|
@Option(option = 'require', description = 'Finds dependences matching the given module name. REPEATABLE')
|
||||||
|
void setRequire(String value) { requires.property.add(value) }
|
||||||
|
|
||||||
|
@Option(option = 'configurations', description = 'Configurations to be analyzed')
|
||||||
|
void setConfigurations(String value) { configurations.property.set(value.split(',').toList()) }
|
||||||
|
|
||||||
|
@Option(option = 'classpaths', description = 'Classpaths to be analyzed')
|
||||||
|
void setClasspaths(String value) { classpaths.property.set(value.split(',').toList()) }
|
||||||
|
|
||||||
|
@Option(option = 'sourcesets', description = 'SourceSets to be analyzed')
|
||||||
|
void setSourceSets(String value) { sourceSets.property.set(value.split(',').toList()) }
|
||||||
|
|
||||||
|
@Option(option = 'multi-release', description = 'Set the multi-release level')
|
||||||
|
void setMultiRelease(String value) { multiRelease.property.set(value) }
|
||||||
|
|
||||||
|
@Option(option = 'dot-output', description = 'Destination directory for DOT file output')
|
||||||
|
void setDotOutput(String value) { dotOutput.property.set(new File(value)) }
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
Property<Boolean> getListDeps() { listDeps.property }
|
||||||
|
|
||||||
|
@Input
|
||||||
|
Provider<Boolean> getResolvedListDeps() { listDeps.provider }
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
Property<Boolean> getListReducedDeps() { listReducedDeps.property }
|
||||||
|
|
||||||
|
@Input
|
||||||
|
Provider<Boolean> getResolvedListReducedDeps() { listReducedDeps.provider }
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
Property<Boolean> getPrintModuleDeps() { printModuleDeps.property }
|
||||||
|
|
||||||
|
@Input
|
||||||
|
Provider<Boolean> getResolvedPrintModuleDeps() { printModuleDeps.provider }
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
Property<Boolean> getVerbose() { verbose.property }
|
||||||
|
|
||||||
|
@Input
|
||||||
|
Provider<Boolean> getResolvedVerbose() { verbose.provider }
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
Property<Boolean> getModular() { modular.property }
|
||||||
|
|
||||||
|
@Input
|
||||||
|
Provider<Boolean> getResolvedModular() { modular.provider }
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
Property<Boolean> getSummary() { summary.property }
|
||||||
|
|
||||||
|
@Input
|
||||||
|
Provider<Boolean> getResolvedSummary() { summary.provider }
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
Property<Boolean> getProfile() { profile.property }
|
||||||
|
|
||||||
|
@Input
|
||||||
|
Provider<Boolean> getResolvedProfile() { profile.provider }
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
Property<Boolean> getRecursive() { recursive.property }
|
||||||
|
|
||||||
|
@Input
|
||||||
|
Provider<Boolean> getResolvedRecursive() { recursive.provider }
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
Property<Boolean> getJdkinternals() { jdkinternals.property }
|
||||||
|
|
||||||
|
@Input
|
||||||
|
Provider<Boolean> getResolvedJdkinternals() { jdkinternals.provider }
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
Property<Boolean> getConsoleOutput() { consoleOutput.property }
|
||||||
|
|
||||||
|
@Input
|
||||||
|
Provider<Boolean> getResolvedConsoleOutput() { consoleOutput.provider }
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
Property<Boolean> getApionly() { apionly.property }
|
||||||
|
|
||||||
|
@Input
|
||||||
|
Provider<Boolean> getResolvedApionly() { apionly.provider }
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
Property<Boolean> getFailOnWarning() { failOnWarning.property }
|
||||||
|
|
||||||
|
@Input
|
||||||
|
Provider<Boolean> getResolvedFailOnWarning() { failOnWarning.provider }
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
Property<Boolean> getMissingDeps() { missingDeps.property }
|
||||||
|
|
||||||
|
@Input
|
||||||
|
Provider<Boolean> getResolvedMissingDeps() { missingDeps.provider }
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
Property<Boolean> getIgnoreMissingDeps() { ignoreMissingDeps.property }
|
||||||
|
|
||||||
|
@Input
|
||||||
|
Provider<Boolean> getResolvedIgnoreMissingDeps() { ignoreMissingDeps.provider }
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
Property<String> getInclude() { include.property }
|
||||||
|
|
||||||
|
@Input
|
||||||
|
@Optional
|
||||||
|
Provider<String> getResolvedInclude() { include.provider }
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
Property<String> getRegex() { regex.property }
|
||||||
|
|
||||||
|
@Input
|
||||||
|
@Optional
|
||||||
|
Provider<String> getResolvedRegex() { regex.provider }
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
Property<String> getFilter() { filter.property }
|
||||||
|
|
||||||
|
@Input
|
||||||
|
@Optional
|
||||||
|
Provider<String> getResolvedFilter() { filter.provider }
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
ListProperty<String> getPackages() { pkgs.property }
|
||||||
|
|
||||||
|
@Input
|
||||||
|
@Optional
|
||||||
|
Provider<List<String>> getResolvedPackages() { pkgs.provider }
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
ListProperty<String> getRequires() { requires.property }
|
||||||
|
|
||||||
|
@Input
|
||||||
|
@Optional
|
||||||
|
Provider<List<String>> getResolvedRequires() { requires.provider }
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
ListProperty<String> getConfigurations() { configurations.property }
|
||||||
|
|
||||||
|
@Input
|
||||||
|
@Optional
|
||||||
|
Provider<List<String>> getResolvedConfigurations() { configurations.provider }
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
ListProperty<String> getClasspaths() { classpaths.property }
|
||||||
|
|
||||||
|
@Input
|
||||||
|
@Optional
|
||||||
|
Provider<List<String>> getResolvedClasspaths() { classpaths.provider }
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
ListProperty<String> getSourceSets() { sourceSets.property }
|
||||||
|
|
||||||
|
@Input
|
||||||
|
@Optional
|
||||||
|
Provider<List<String>> getResolvedSourceSets() { sourceSets.provider }
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
Property<String> getMultiRelease() { multiRelease.property }
|
||||||
|
|
||||||
|
@Input
|
||||||
|
Provider<String> getResolvedMultiRelease() { multiRelease.provider }
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
MapProperty<String, String> getMultiReleaseJars() { multiReleaseJars.property }
|
||||||
|
|
||||||
|
@Input
|
||||||
|
@Optional
|
||||||
|
Provider<Map<String, String>> getResolvedMultiReleaseJars() { multiReleaseJars.provider }
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
DirectoryProperty getDotOutput() { dotOutput.property }
|
||||||
|
|
||||||
|
@InputDirectory
|
||||||
|
@Optional
|
||||||
|
Provider<Directory> getResolvedDotOutput() { dotOutput.provider }
|
||||||
|
|
||||||
|
@TaskAction
|
||||||
|
void evaluate() {
|
||||||
|
ModuleOptions moduleOptions = extensions.getByType(ModuleOptions)
|
||||||
|
String classpath = compileJava.get().classpath.asPath
|
||||||
|
List<String> compilerArgs = compileJava.get().options.compilerArgs
|
||||||
|
List<String> commandOutput = []
|
||||||
|
int explicitCommand = 0
|
||||||
|
if (resolvedListDeps.get()) {
|
||||||
|
explicitCommand++
|
||||||
|
}
|
||||||
|
if (resolvedListReducedDeps.get()) {
|
||||||
|
explicitCommand++
|
||||||
|
}
|
||||||
|
if (resolvedPrintModuleDeps.get()) {
|
||||||
|
explicitCommand++
|
||||||
|
}
|
||||||
|
if (explicitCommand > 1) {
|
||||||
|
throw new IllegalArgumentException("--list-deps, --list-reduced-deps, --print-module-deps are mutually exclusive")
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<String> baseCmd = ['jdeps']
|
||||||
|
if (resolvedSummary.get()) {
|
||||||
|
if (resolvedMissingDeps.get()) {
|
||||||
|
throw new IllegalArgumentException("-s, --missing-deps are mutually exclusive")
|
||||||
|
}
|
||||||
|
baseCmd << '-s'
|
||||||
|
}
|
||||||
|
if (resolvedVerbose.get()) {
|
||||||
|
baseCmd << '-v'
|
||||||
|
}
|
||||||
|
if (resolvedProfile.get()) {
|
||||||
|
baseCmd << '-P'
|
||||||
|
}
|
||||||
|
if (resolvedRecursive.get()) {
|
||||||
|
baseCmd << '-R'
|
||||||
|
}
|
||||||
|
if (resolvedJdkinternals.get()) {
|
||||||
|
baseCmd << '-jdkinternals'
|
||||||
|
}
|
||||||
|
if (resolvedApionly.get()) {
|
||||||
|
baseCmd << '-apionly'
|
||||||
|
}
|
||||||
|
if (getResolvedDotOutput().present) {
|
||||||
|
baseCmd << '-dotoutput'
|
||||||
|
baseCmd << resolvedDotOutput.get().asFile.absolutePath
|
||||||
|
resolvedDotOutput.get().asFile.mkdirs()
|
||||||
|
}
|
||||||
|
if (resolvedIgnoreMissingDeps.get()) {
|
||||||
|
baseCmd << '--ignore-missing-deps'
|
||||||
|
}
|
||||||
|
if (JavaVersion.current().java9Compatible) {
|
||||||
|
if (resolvedMultiRelease.present) {
|
||||||
|
baseCmd << '--multi-release'
|
||||||
|
baseCmd << resolvedMultiRelease.get()
|
||||||
|
}
|
||||||
|
if (resolvedModular.get()) {
|
||||||
|
int modulePathIndex = compilerArgs.indexOf('--module-path')
|
||||||
|
if (modulePathIndex > -1) {
|
||||||
|
baseCmd << '--module-path'
|
||||||
|
baseCmd << compilerArgs[modulePathIndex + 1]
|
||||||
|
} else {
|
||||||
|
baseCmd << '--module-path'
|
||||||
|
baseCmd << classpath
|
||||||
|
}
|
||||||
|
} else if (classpath) {
|
||||||
|
baseCmd << '--class-path'
|
||||||
|
baseCmd << classpath
|
||||||
|
} else {
|
||||||
|
int modulePathIndex = compilerArgs.indexOf('--module-path')
|
||||||
|
if (modulePathIndex > -1) {
|
||||||
|
baseCmd << '--module-path'
|
||||||
|
baseCmd << compilerArgs[modulePathIndex + 1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!moduleOptions.addModules.empty) {
|
||||||
|
baseCmd << '--add-modules'
|
||||||
|
baseCmd << moduleOptions.addModules.join(',')
|
||||||
|
} else {
|
||||||
|
int addModulesIndex = compilerArgs.indexOf('--add-modules')
|
||||||
|
if (addModulesIndex > -1) {
|
||||||
|
baseCmd << '--add-modules'
|
||||||
|
baseCmd << compilerArgs[addModulesIndex + 1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
List<String> requires = resolvedRequires.get()
|
||||||
|
List<String> packages = resolvedPackages.get()
|
||||||
|
String regex = resolvedRegex.orNull
|
||||||
|
int exclusive = 0
|
||||||
|
if (!requires.isEmpty()) {
|
||||||
|
exclusive++
|
||||||
|
}
|
||||||
|
if (!packages.isEmpty()) {
|
||||||
|
exclusive++
|
||||||
|
}
|
||||||
|
if (regex) {
|
||||||
|
exclusive++
|
||||||
|
}
|
||||||
|
if (exclusive > 1) {
|
||||||
|
throw new IllegalArgumentException("--package, --regex, --require are mutually exclusive")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resolvedMissingDeps.get()) {
|
||||||
|
exclusive = 1
|
||||||
|
if (!packages.isEmpty()) {
|
||||||
|
exclusive++
|
||||||
|
}
|
||||||
|
if (regex) {
|
||||||
|
exclusive++
|
||||||
|
}
|
||||||
|
if (exclusive > 1) {
|
||||||
|
throw new IllegalArgumentException("--package, --regex, --missing-deps are mutually exclusive")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
requires.each { s ->
|
||||||
|
baseCmd << '--require'
|
||||||
|
baseCmd << s
|
||||||
|
}
|
||||||
|
packages.each { s ->
|
||||||
|
baseCmd << '--package'
|
||||||
|
baseCmd << s
|
||||||
|
}
|
||||||
|
if (regex) {
|
||||||
|
baseCmd << '--regex'
|
||||||
|
baseCmd << regex
|
||||||
|
}
|
||||||
|
if (resolvedFilter.get()) {
|
||||||
|
String filter = resolvedFilter.get()
|
||||||
|
if (filter in [':package', ':archive', ':module', ':none']) {
|
||||||
|
baseCmd << '-filter' + filter
|
||||||
|
} else {
|
||||||
|
baseCmd << '-filter'
|
||||||
|
baseCmd << filter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// compileJava.get().classpath = project.files()
|
||||||
|
//logger.info("jdeps version is ${executeCommand(['jdeps', '-version'])}")
|
||||||
|
//Map<String, String> outputs = [:]
|
||||||
|
resolvedSourceSets.get().each { sc ->
|
||||||
|
SourceSet sourceSet = javaPluginConvention.get().sourceSets.findByName(sc)
|
||||||
|
logger.info("Running jdeps on sourceSet ${sourceSet.name}")
|
||||||
|
sourceSet.output.files.each { File file ->
|
||||||
|
if (!file.exists()) {
|
||||||
|
return // skip
|
||||||
|
}
|
||||||
|
List<String> cmd = applyExplicitCommand(baseCmd)
|
||||||
|
logger.info("jdeps command set to ${cmd.join(' ')}")
|
||||||
|
executeCommandOn(cmd, file.absolutePath)
|
||||||
|
/*if (output) {
|
||||||
|
commandOutput << "\nProject: ${projectName.get()}\n${output}".toString()
|
||||||
|
outputs[sourceSet.name] = output
|
||||||
|
}*/
|
||||||
|
List<String> warnings = getWarnings(output)
|
||||||
|
if (warnings && getResolvedFailOnWarning().get()) {
|
||||||
|
throw new IllegalStateException("jdeps reported warnings: " +
|
||||||
|
System.lineSeparator() +
|
||||||
|
warnings.join(System.lineSeparator()))
|
||||||
|
}
|
||||||
|
List<String> errors = getErrors(output)
|
||||||
|
if (errors) {
|
||||||
|
throw new IllegalStateException("jdeps reported errors: " +
|
||||||
|
System.lineSeparator() +
|
||||||
|
errors.join(System.lineSeparator()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (String c : resolvedConfigurations.get()) {
|
||||||
|
inspectConfiguration(projectConfigurations[c.trim()], baseCmd)
|
||||||
|
}
|
||||||
|
for (String c : resolvedClasspaths.get()) {
|
||||||
|
inspectConfiguration(projectConfigurations[c.trim()], baseCmd)
|
||||||
|
}
|
||||||
|
/*if (commandOutput) {
|
||||||
|
commandOutput = commandOutput.unique()
|
||||||
|
if (resolvedConsoleOutput.get()) {
|
||||||
|
println commandOutput.join('\n')
|
||||||
|
}
|
||||||
|
File parentFile = reportDir.get().asFile
|
||||||
|
if (!parentFile.exists()) {
|
||||||
|
parentFile.mkdirs()
|
||||||
|
}
|
||||||
|
File logFile = new File(parentFile, 'jdeps-report.txt')
|
||||||
|
logFile.append(commandOutput.join('\n'))
|
||||||
|
String prefix = 'jdeps-'
|
||||||
|
if (resolvedListDeps.get()) {
|
||||||
|
prefix = 'list-deps-'
|
||||||
|
} else if (resolvedListReducedDeps.get()) {
|
||||||
|
prefix = 'list-reduced-deps-'
|
||||||
|
} else if (resolvedPrintModuleDeps.get()) {
|
||||||
|
prefix = 'print-module-deps-'
|
||||||
|
}
|
||||||
|
outputs.each { k, v ->
|
||||||
|
logFile = new File(parentFile, prefix + k + '.txt')
|
||||||
|
logFile.append(v)
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
private void inspectConfiguration(Configuration configuration,
|
||||||
|
List<String> baseCmd) {
|
||||||
|
logger.info("Running jdeps on configuration ${configuration.name}")
|
||||||
|
configuration.resolve().each { File file ->
|
||||||
|
if (!file.exists()) {
|
||||||
|
return // skip
|
||||||
|
}
|
||||||
|
List<String> command = new ArrayList<>(baseCmd)
|
||||||
|
if (JavaVersion.current().java9Compatible) {
|
||||||
|
String multiReleaseVersion = resolveMultiReleaseVersion(file.name, resolvedMultiReleaseJars.get())
|
||||||
|
if (multiReleaseVersion) {
|
||||||
|
command.add(1, multiReleaseVersion)
|
||||||
|
command.add(1, '--multi-release')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
command = applyExplicitCommand(command)
|
||||||
|
logger.info("jdeps command set to: ${command.join(' ')} ${file.absolutePath}")
|
||||||
|
executeCommandOn(command, file.absolutePath)
|
||||||
|
//if (output) {
|
||||||
|
//commandOutput << "\nDependency: ${file.name}\n${output}".toString()
|
||||||
|
//outputs[configuration.name] = output
|
||||||
|
//}
|
||||||
|
/*List<String> warnings = getWarnings(output)
|
||||||
|
if (warnings && getResolvedFailOnWarning().get()) {
|
||||||
|
throw new IllegalStateException("jdeps reported errors/warnings: " +
|
||||||
|
System.lineSeparator() +
|
||||||
|
warnings.join(System.lineSeparator()))
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> applyExplicitCommand(List<String> cmd) {
|
||||||
|
List<String> c = new ArrayList<>(cmd)
|
||||||
|
String subcommand = ''
|
||||||
|
if (resolvedListDeps.get()) {
|
||||||
|
subcommand = '--list-deps'
|
||||||
|
} else if (resolvedListReducedDeps.get()) {
|
||||||
|
subcommand = '--list-reduced-deps'
|
||||||
|
} else if (resolvedPrintModuleDeps.get()) {
|
||||||
|
subcommand = '--print-module-deps'
|
||||||
|
}
|
||||||
|
if (subcommand) {
|
||||||
|
if (c.contains('--class-path')) {
|
||||||
|
c.add(c.indexOf('--class-path'), subcommand)
|
||||||
|
} else if (c.contains('--module-path')) {
|
||||||
|
c.add(c.indexOf('--module-path'), subcommand)
|
||||||
|
} else {
|
||||||
|
c << subcommand
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c
|
||||||
|
}
|
||||||
|
|
||||||
|
private String executeCommandOn(List<String> baseCmd, String path) {
|
||||||
|
List<String> cmd = []
|
||||||
|
cmd.addAll(baseCmd)
|
||||||
|
cmd.add(path)
|
||||||
|
return executeCommand(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
private String executeCommand(List<String> cmd) {
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream()
|
||||||
|
execOperations.exec(e -> {
|
||||||
|
e.setErrorOutput(out)
|
||||||
|
e.commandLine(cmd)
|
||||||
|
})
|
||||||
|
return out.toString().trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String resolveMultiReleaseVersion(String artifactName, Map<String, String> multiReleaseJars) {
|
||||||
|
for (Map.Entry<String, String> e : multiReleaseJars.entrySet()) {
|
||||||
|
if (artifactName.matches(e.key.trim())) {
|
||||||
|
return e.value.trim()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<String> getWarnings(String output) {
|
||||||
|
List<String> warnings = []
|
||||||
|
output.eachLine { String line ->
|
||||||
|
if (WARNING.matcher(line).matches()) {
|
||||||
|
warnings.add(line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
warnings
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<String> getErrors(String output) {
|
||||||
|
List<String> errors = []
|
||||||
|
output.eachLine { String line ->
|
||||||
|
if (ERROR.matcher(line).matches()) {
|
||||||
|
errors.add(line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
errors
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package org.xbib.gradle.plugin.jdeps
|
||||||
|
|
||||||
|
import groovy.transform.Canonical
|
||||||
|
|
||||||
|
@Canonical
|
||||||
|
class ModuleOptions {
|
||||||
|
|
||||||
|
List<String> addModules = []
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
package org.xbib.gradle.plugin.jdeps.util
|
||||||
|
|
||||||
|
import org.gradle.api.provider.Property
|
||||||
|
import org.gradle.api.provider.Provider
|
||||||
|
|
||||||
|
interface BooleanState {
|
||||||
|
|
||||||
|
Property<Boolean> getProperty()
|
||||||
|
|
||||||
|
Provider<Boolean> getProvider()
|
||||||
|
|
||||||
|
boolean getValue()
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package org.xbib.gradle.plugin.jdeps.util
|
||||||
|
|
||||||
|
import org.gradle.api.file.Directory
|
||||||
|
import org.gradle.api.file.DirectoryProperty
|
||||||
|
import org.gradle.api.provider.Provider
|
||||||
|
|
||||||
|
interface DirectoryState {
|
||||||
|
|
||||||
|
DirectoryProperty getProperty()
|
||||||
|
|
||||||
|
Provider<Directory> getProvider()
|
||||||
|
|
||||||
|
Directory getValue()
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
package org.xbib.gradle.plugin.jdeps.util
|
||||||
|
|
||||||
|
import org.gradle.api.provider.ListProperty
|
||||||
|
import org.gradle.api.provider.Provider
|
||||||
|
|
||||||
|
interface ListState {
|
||||||
|
|
||||||
|
ListProperty<String> getProperty()
|
||||||
|
|
||||||
|
Provider<List<String>> getProvider()
|
||||||
|
|
||||||
|
List<String> getValue()
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
package org.xbib.gradle.plugin.jdeps.util;
|
||||||
|
|
||||||
|
import org.gradle.api.provider.MapProperty
|
||||||
|
import org.gradle.api.provider.Provider
|
||||||
|
|
||||||
|
interface MapState {
|
||||||
|
|
||||||
|
MapProperty<String, String> getProperty()
|
||||||
|
|
||||||
|
Provider<Map<String, String>> getProvider()
|
||||||
|
|
||||||
|
Map<String, String> getValue()
|
||||||
|
}
|
|
@ -0,0 +1,100 @@
|
||||||
|
package org.xbib.gradle.plugin.jdeps.util
|
||||||
|
|
||||||
|
import org.gradle.api.Project
|
||||||
|
import org.gradle.api.Task
|
||||||
|
import org.gradle.api.internal.provider.Providers
|
||||||
|
import org.gradle.api.provider.Property
|
||||||
|
import org.gradle.api.provider.Provider
|
||||||
|
import org.gradle.api.provider.ProviderFactory
|
||||||
|
|
||||||
|
import static java.util.Objects.requireNonNull
|
||||||
|
|
||||||
|
final class SimpleBooleanState implements BooleanState {
|
||||||
|
|
||||||
|
final Property<Boolean> property
|
||||||
|
|
||||||
|
final Provider<Boolean> provider
|
||||||
|
|
||||||
|
private final Project project
|
||||||
|
|
||||||
|
SimpleBooleanState(Project project,
|
||||||
|
Property<Boolean> property,
|
||||||
|
Provider<Boolean> provider) {
|
||||||
|
this.project = requireNonNull(project, "Argument 'project' must not be null")
|
||||||
|
this.property = requireNonNull(property, "Argument 'property' must not be null")
|
||||||
|
this.provider = requireNonNull(provider, "Argument 'provider' must not be null")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean getValue() {
|
||||||
|
booleanProvider(project.providers, property, provider, false).get()
|
||||||
|
}
|
||||||
|
|
||||||
|
static SimpleBooleanState of(Task task,
|
||||||
|
String key,
|
||||||
|
boolean defaultValue) {
|
||||||
|
requireNonNull(task, "Argument 'task' must not be null")
|
||||||
|
Project project = task.project
|
||||||
|
Property<Boolean> property = project.objects.property(Boolean).convention(Providers.<Boolean>notDefined())
|
||||||
|
Provider<Boolean> provider = booleanProvider(key, property, project, defaultValue)
|
||||||
|
new SimpleBooleanState(project, property, provider)
|
||||||
|
}
|
||||||
|
|
||||||
|
static Provider<Boolean> booleanProvider(ProviderFactory providers,
|
||||||
|
Property<Boolean> property,
|
||||||
|
Provider<Boolean> provider,
|
||||||
|
boolean defaultValue) {
|
||||||
|
providers.provider {
|
||||||
|
return provider ? provider.getOrElse(property.getOrElse(defaultValue)) : property.getOrElse(defaultValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Provider<Boolean> booleanProvider(String key,
|
||||||
|
Property<Boolean> property,
|
||||||
|
Project project,
|
||||||
|
boolean defaultValue) {
|
||||||
|
booleanProvider(toEnv(key), toProperty(key), property, project, defaultValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
static Provider<Boolean> booleanProvider(String envKey,
|
||||||
|
String propertyKey,
|
||||||
|
Property<Boolean> property,
|
||||||
|
Project project,
|
||||||
|
boolean defaultValue) {
|
||||||
|
project.providers.provider {
|
||||||
|
String value = resolveValue(envKey, propertyKey, project)
|
||||||
|
return !isBlank(value) ? Boolean.parseBoolean(value) : property.getOrElse(defaultValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String resolveValue(String envKey, String propertyKey, Project project) {
|
||||||
|
String value = System.getenv(envKey)
|
||||||
|
if (isBlank(value)) {
|
||||||
|
value = System.getProperty(propertyKey)
|
||||||
|
}
|
||||||
|
if (isBlank(value)) {
|
||||||
|
value = project.findProperty(propertyKey) as String
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isBlank(String str) {
|
||||||
|
if (str == null || str.length() == 0) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for (char c : str.toCharArray()) {
|
||||||
|
if (!Character.isWhitespace(c)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String toEnv(String key) {
|
||||||
|
key.toUpperCase().replace('.', '_')
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String toProperty(String key) {
|
||||||
|
key.uncapitalize().replace('_', '.')
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,107 @@
|
||||||
|
package org.xbib.gradle.plugin.jdeps.util
|
||||||
|
|
||||||
|
import org.gradle.api.Project
|
||||||
|
import org.gradle.api.Task
|
||||||
|
import org.gradle.api.file.Directory
|
||||||
|
import org.gradle.api.file.DirectoryProperty
|
||||||
|
import org.gradle.api.internal.provider.Providers
|
||||||
|
import org.gradle.api.provider.Provider
|
||||||
|
import org.gradle.api.provider.ProviderFactory
|
||||||
|
|
||||||
|
import java.nio.file.Paths
|
||||||
|
|
||||||
|
import static java.util.Objects.requireNonNull
|
||||||
|
|
||||||
|
final class SimpleDirectoryState implements DirectoryState {
|
||||||
|
|
||||||
|
final DirectoryProperty property
|
||||||
|
|
||||||
|
final Provider<Directory> provider
|
||||||
|
|
||||||
|
private final Project project
|
||||||
|
|
||||||
|
SimpleDirectoryState(Project project,
|
||||||
|
DirectoryProperty property,
|
||||||
|
Provider<Directory> provider) {
|
||||||
|
this.project = requireNonNull(project, "Argument 'project' must not be null")
|
||||||
|
this.property = requireNonNull(property, "Argument 'property' must not be null")
|
||||||
|
this.provider = requireNonNull(provider, "Argument 'provider' must not be null")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Directory getValue() {
|
||||||
|
directoryProvider(project.providers, property, provider, (Directory) null).get()
|
||||||
|
}
|
||||||
|
|
||||||
|
static SimpleDirectoryState of(Task task, String key, Directory defaultValue) {
|
||||||
|
requireNonNull(task, "Argument 'task' must not be null")
|
||||||
|
Project project = task.project
|
||||||
|
DirectoryProperty property = project.objects.directoryProperty().convention(Providers.<Directory>notDefined())
|
||||||
|
Provider<Directory> provider = directoryProvider(key, property, project, defaultValue)
|
||||||
|
new SimpleDirectoryState(project, property, provider)
|
||||||
|
}
|
||||||
|
|
||||||
|
static Provider<Directory> directoryProvider(ProviderFactory providers,
|
||||||
|
DirectoryProperty property,
|
||||||
|
Provider<Directory> provider,
|
||||||
|
Directory defaultValue) {
|
||||||
|
providers.provider {
|
||||||
|
return provider ? provider.getOrElse(property.getOrElse(defaultValue)) : property.getOrElse(defaultValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Provider<Directory> directoryProvider(String key,
|
||||||
|
DirectoryProperty property,
|
||||||
|
Project project,
|
||||||
|
Directory defaultValue) {
|
||||||
|
directoryProvider(toEnv(key), toProperty(key), property, project, defaultValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
static Provider<Directory> directoryProvider(String envKey,
|
||||||
|
String propertyKey,
|
||||||
|
DirectoryProperty property,
|
||||||
|
Project project,
|
||||||
|
Directory defaultValue) {
|
||||||
|
project.providers.provider {
|
||||||
|
String value = resolveValue(envKey, propertyKey, project)
|
||||||
|
if (!isBlank(value)) {
|
||||||
|
DirectoryProperty p = project.objects.directoryProperty()
|
||||||
|
p.set(Paths.get(value).toFile())
|
||||||
|
return p.get()
|
||||||
|
}
|
||||||
|
return property.getOrElse(defaultValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static String resolveValue(String envKey, String propertyKey, Project project) {
|
||||||
|
String value = System.getenv(envKey)
|
||||||
|
if (isBlank(value)) {
|
||||||
|
value = System.getProperty(propertyKey)
|
||||||
|
}
|
||||||
|
if (isBlank(value)) {
|
||||||
|
value = project.findProperty(propertyKey) as String
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isBlank(String str) {
|
||||||
|
if (str == null || str.length() == 0) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for (char c : str.toCharArray()) {
|
||||||
|
if (!Character.isWhitespace(c)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String toEnv(String key) {
|
||||||
|
key.toUpperCase().replace('.', '_')
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String toProperty(String key) {
|
||||||
|
key.uncapitalize().replace('_', '.')
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,108 @@
|
||||||
|
package org.xbib.gradle.plugin.jdeps.util
|
||||||
|
|
||||||
|
import org.gradle.api.Project
|
||||||
|
import org.gradle.api.Task
|
||||||
|
import org.gradle.api.internal.provider.Providers
|
||||||
|
import org.gradle.api.provider.ListProperty
|
||||||
|
import org.gradle.api.provider.Provider
|
||||||
|
import org.gradle.api.provider.ProviderFactory
|
||||||
|
|
||||||
|
import static java.util.Objects.requireNonNull
|
||||||
|
|
||||||
|
final class SimpleListState implements ListState {
|
||||||
|
|
||||||
|
final ListProperty<String> property
|
||||||
|
|
||||||
|
final Provider<List<String>> provider
|
||||||
|
|
||||||
|
private final Project project
|
||||||
|
|
||||||
|
SimpleListState(Project project,
|
||||||
|
ListProperty<String> property,
|
||||||
|
Provider<List<String>> provider) {
|
||||||
|
this.project = requireNonNull(project, "Argument 'project' must not be null")
|
||||||
|
this.property = requireNonNull(property, "Argument 'property' must not be null")
|
||||||
|
this.provider = requireNonNull(provider, "Argument 'provider' must not be null")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
List<String> getValue() {
|
||||||
|
listProvider(project.providers, property, provider, Collections.<String> emptyList()).get()
|
||||||
|
}
|
||||||
|
|
||||||
|
static SimpleListState of(Task task, String key, List<String> defaultValue) {
|
||||||
|
requireNonNull(task, "Argument 'task' must not be null")
|
||||||
|
Project project = task.project
|
||||||
|
ListProperty<String> property = project.objects.listProperty(String).convention(Providers.<List<String>>notDefined())
|
||||||
|
Provider<List<String>> provider = listProvider(key, property, project, defaultValue)
|
||||||
|
new SimpleListState(project, property, provider)
|
||||||
|
}
|
||||||
|
|
||||||
|
static Provider<List<String>> listProvider(ProviderFactory providers,
|
||||||
|
ListProperty<String> property,
|
||||||
|
Provider<List<String>> provider,
|
||||||
|
List<String> defaultValue) {
|
||||||
|
providers.provider {
|
||||||
|
return provider ? provider.getOrElse(property.getOrElse(defaultValue)) : property.getOrElse(defaultValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Provider<List<String>> listProvider(String key,
|
||||||
|
ListProperty<String> property,
|
||||||
|
Project project,
|
||||||
|
List<String> defaultValue) {
|
||||||
|
listProvider(toEnv(key), toProperty(key), property, project, defaultValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
static Provider<List<String>> listProvider(String envKey,
|
||||||
|
String propertyKey,
|
||||||
|
ListProperty<String> property,
|
||||||
|
Project project,
|
||||||
|
List<String> defaultValue) {
|
||||||
|
project.providers.provider {
|
||||||
|
if (property.present) {
|
||||||
|
return property.get()
|
||||||
|
}
|
||||||
|
String value = resolveValue(envKey, propertyKey, project)
|
||||||
|
if (!isBlank(value)) {
|
||||||
|
List<String> list = new ArrayList<>()
|
||||||
|
for (String v : value.split(',')) {
|
||||||
|
if (!isBlank(v)) list.add(v.trim())
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
defaultValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String resolveValue(String envKey, String propertyKey, Project project) {
|
||||||
|
String value = System.getenv(envKey)
|
||||||
|
if (isBlank(value)) {
|
||||||
|
value = System.getProperty(propertyKey)
|
||||||
|
}
|
||||||
|
if (isBlank(value)) {
|
||||||
|
value = project.findProperty(propertyKey) as String
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isBlank(String str) {
|
||||||
|
if (str == null || str.length() == 0) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for (char c : str.toCharArray()) {
|
||||||
|
if (!Character.isWhitespace(c)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String toEnv(String key) {
|
||||||
|
key.toUpperCase().replace('.', '_')
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String toProperty(String key) {
|
||||||
|
key.uncapitalize().replace('_', '.')
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,115 @@
|
||||||
|
package org.xbib.gradle.plugin.jdeps.util
|
||||||
|
|
||||||
|
import org.gradle.api.Project
|
||||||
|
import org.gradle.api.Task
|
||||||
|
import org.gradle.api.internal.provider.Providers
|
||||||
|
import org.gradle.api.provider.MapProperty
|
||||||
|
import org.gradle.api.provider.Provider
|
||||||
|
import org.gradle.api.provider.ProviderFactory
|
||||||
|
|
||||||
|
import static java.util.Objects.requireNonNull
|
||||||
|
|
||||||
|
final class SimpleMapState implements MapState {
|
||||||
|
|
||||||
|
final MapProperty<String, String> property
|
||||||
|
|
||||||
|
final Provider<Map<String, String>> provider
|
||||||
|
|
||||||
|
private final Project project
|
||||||
|
|
||||||
|
SimpleMapState(Project project,
|
||||||
|
MapProperty<String, String> property,
|
||||||
|
Provider<Map<String, String>> provider) {
|
||||||
|
this.project = requireNonNull(project, "Argument 'project' must not be null")
|
||||||
|
this.property = requireNonNull(property, "Argument 'property' must not be null")
|
||||||
|
this.provider = requireNonNull(provider, "Argument 'provider' must not be null")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Map<String, String> getValue() {
|
||||||
|
mapProvider(project.providers, property, provider, Collections.<String, String> emptyMap()).get()
|
||||||
|
}
|
||||||
|
|
||||||
|
static SimpleMapState of(Task task, String key, Map<String, String> defaultValue) {
|
||||||
|
requireNonNull(task, "Argument 'task' must not be null")
|
||||||
|
Project project = task.project
|
||||||
|
MapProperty<String, String> property = project.objects.mapProperty(String, String).convention(Providers.<Map<String, String>>notDefined())
|
||||||
|
Provider<Map<String, String>> provider = mapProvider(key, property, project, defaultValue)
|
||||||
|
new SimpleMapState(project, property, provider)
|
||||||
|
}
|
||||||
|
|
||||||
|
static <K, V> Provider<Map<K, V>> mapProvider(ProviderFactory providers,
|
||||||
|
MapProperty<K, V> property,
|
||||||
|
Provider<Map<K, V>> provider,
|
||||||
|
Map<K, V> defaultValue) {
|
||||||
|
providers.provider {
|
||||||
|
Map<K, V> map = new LinkedHashMap<>(defaultValue)
|
||||||
|
if (property.present) {
|
||||||
|
map.putAll(property.get())
|
||||||
|
}
|
||||||
|
if (provider) {
|
||||||
|
map.putAll(provider.get())
|
||||||
|
}
|
||||||
|
return map
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static <K,V> Provider<Map<K, V>> mapProvider(String key,
|
||||||
|
MapProperty<K, V> property,
|
||||||
|
Project project,
|
||||||
|
Map<K, V> defaultValue) {
|
||||||
|
mapProvider(toEnv(key), toProperty(key), property, project, defaultValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
static <K, V> Provider<Map<K, V>> mapProvider(String envKey,
|
||||||
|
String propertyKey,
|
||||||
|
MapProperty<K, V> property,
|
||||||
|
Project project,
|
||||||
|
Map<K, V> defaultValue) {
|
||||||
|
project.providers.provider {
|
||||||
|
String value = resolveValue(envKey, propertyKey, project)
|
||||||
|
if (i!sBlank(value)) {
|
||||||
|
Map<K, V> map = new LinkedHashMap<>()
|
||||||
|
for (String val : value.split(',')) {
|
||||||
|
String[] kv = val.split('=')
|
||||||
|
if (kv.length == 2) {
|
||||||
|
map.put(kv[0] as K, kv[1] as V)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map
|
||||||
|
}
|
||||||
|
return property.orElse(defaultValue).get()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String resolveValue(String envKey, String propertyKey, Project project) {
|
||||||
|
String value = System.getenv(envKey)
|
||||||
|
if (isBlank(value)) {
|
||||||
|
value = System.getProperty(propertyKey)
|
||||||
|
}
|
||||||
|
if (isBlank(value)) {
|
||||||
|
value = project.findProperty(propertyKey) as String
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isBlank(String str) {
|
||||||
|
if (str == null || str.length() == 0) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for (char c : str.toCharArray()) {
|
||||||
|
if (!Character.isWhitespace(c)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String toEnv(String key) {
|
||||||
|
key.toUpperCase().replace('.', '_')
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String toProperty(String key) {
|
||||||
|
key.uncapitalize().replace('_', '.')
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,98 @@
|
||||||
|
package org.xbib.gradle.plugin.jdeps.util
|
||||||
|
|
||||||
|
import org.gradle.api.Project
|
||||||
|
import org.gradle.api.Task
|
||||||
|
import org.gradle.api.internal.provider.Providers
|
||||||
|
import org.gradle.api.provider.Property
|
||||||
|
import org.gradle.api.provider.Provider
|
||||||
|
import org.gradle.api.provider.ProviderFactory
|
||||||
|
|
||||||
|
import static java.util.Objects.requireNonNull
|
||||||
|
|
||||||
|
final class SimpleStringState implements StringState {
|
||||||
|
|
||||||
|
final Property<String> property
|
||||||
|
|
||||||
|
final Provider<String> provider
|
||||||
|
|
||||||
|
private final Project project
|
||||||
|
|
||||||
|
@Override
|
||||||
|
String getValue() {
|
||||||
|
stringProvider(project.providers, property, provider, '').get()
|
||||||
|
}
|
||||||
|
|
||||||
|
SimpleStringState(Project project,
|
||||||
|
Property<String> property,
|
||||||
|
Provider<String> provider) {
|
||||||
|
this.project = requireNonNull(project, "Argument 'project' must not be null")
|
||||||
|
this.property = requireNonNull(property, "Argument 'property' must not be null")
|
||||||
|
this.provider = requireNonNull(provider, "Argument 'provider' must not be null")
|
||||||
|
}
|
||||||
|
|
||||||
|
static SimpleStringState of(Task task, String key, String defaultValue) {
|
||||||
|
requireNonNull(task, "Argument 'task' must not be null")
|
||||||
|
Project project = task.project
|
||||||
|
Property<String> property = project.objects.property(String).convention(Providers.<String>notDefined())
|
||||||
|
Provider<String> provider = stringProvider(key, property, project, defaultValue)
|
||||||
|
new SimpleStringState(project, property, provider)
|
||||||
|
}
|
||||||
|
|
||||||
|
static Provider<String> stringProvider(ProviderFactory providers,
|
||||||
|
Property<String> property,
|
||||||
|
Provider<String> provider,
|
||||||
|
String defaultValue) {
|
||||||
|
providers.provider {
|
||||||
|
return provider ? provider.getOrElse(property.getOrElse(defaultValue)) : property.getOrElse(defaultValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Provider<String> stringProvider(String key,
|
||||||
|
Property<String> property,
|
||||||
|
Project project,
|
||||||
|
String defaultValue) {
|
||||||
|
stringProvider(toEnv(key), toProperty(key), property, project, defaultValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
static Provider<String> stringProvider(String envKey,
|
||||||
|
String propertyKey,
|
||||||
|
Property<String> property,
|
||||||
|
Project project,
|
||||||
|
String defaultValue) {
|
||||||
|
project.providers.provider {
|
||||||
|
String value = resolveValue(envKey, propertyKey, project)
|
||||||
|
return !isBlank(value) ? value : property.getOrElse(defaultValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String resolveValue(String envKey, String propertyKey, Project project) {
|
||||||
|
String value = System.getenv(envKey)
|
||||||
|
if (isBlank(value)) {
|
||||||
|
value = System.getProperty(propertyKey)
|
||||||
|
}
|
||||||
|
if (isBlank(value)) {
|
||||||
|
value = project.findProperty(propertyKey) as String
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isBlank(String str) {
|
||||||
|
if (str == null || str.length() == 0) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for (char c : str.toCharArray()) {
|
||||||
|
if (!Character.isWhitespace(c)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String toEnv(String key) {
|
||||||
|
key.toUpperCase().replace('.', '_')
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String toProperty(String key) {
|
||||||
|
key.uncapitalize().replace('_', '.')
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
package org.xbib.gradle.plugin.jdeps.util
|
||||||
|
|
||||||
|
import org.gradle.api.provider.Property
|
||||||
|
import org.gradle.api.provider.Provider
|
||||||
|
|
||||||
|
interface StringState {
|
||||||
|
|
||||||
|
Property<String> getProperty()
|
||||||
|
|
||||||
|
Provider<String> getProvider()
|
||||||
|
|
||||||
|
String getValue()
|
||||||
|
}
|
|
@ -1,2 +1,2 @@
|
||||||
name = gradle-plugin-jflex
|
name = gradle-plugin-jflex
|
||||||
version = 3.0.2
|
version = 3.1.0
|
||||||
|
|
32
gradle-plugin-jlink/build.gradle
Executable file
32
gradle-plugin-jlink/build.gradle
Executable file
|
@ -0,0 +1,32 @@
|
||||||
|
plugins {
|
||||||
|
id 'java-gradle-plugin'
|
||||||
|
alias(libs.plugins.publish)
|
||||||
|
}
|
||||||
|
|
||||||
|
apply plugin: 'java-gradle-plugin'
|
||||||
|
apply plugin: 'com.gradle.plugin-publish'
|
||||||
|
|
||||||
|
apply from: rootProject.file('gradle/compile/groovy.gradle')
|
||||||
|
apply from: rootProject.file('gradle/test/junit5.gradle')
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
api gradleApi()
|
||||||
|
testImplementation gradleTestKit()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (project.hasProperty('gradle.publish.key')) {
|
||||||
|
gradlePlugin {
|
||||||
|
website = 'https://xbib.org/joerg/gradle-plugins/src/branch/main/gradle-plugin-jlink'
|
||||||
|
vcsUrl = 'https://xbib.org/joerg/gradle-plugins'
|
||||||
|
plugins {
|
||||||
|
jlinkPlugin {
|
||||||
|
id = 'org.xbib.gradle.plugin.jlink'
|
||||||
|
implementationClass = 'org.xbib.gradle.plugin.jlink.JlinkPlugin'
|
||||||
|
version = project.version
|
||||||
|
description = 'Gradle Jlink plugin'
|
||||||
|
displayName = 'Gradle Jlink plugin'
|
||||||
|
tags.set(['jlink'])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
9
gradle-plugin-jpackage/NOTICE.txt
Normal file
9
gradle-plugin-jpackage/NOTICE.txt
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
This work is based on
|
||||||
|
|
||||||
|
https://github.com/gradlex-org/java-module-packaging
|
||||||
|
|
||||||
|
by Jendrik Johannes
|
||||||
|
|
||||||
|
as of Feb 11, 2024
|
||||||
|
|
||||||
|
License: Apache 2.0
|
34
gradle-plugin-jpackage/build.gradle
Executable file
34
gradle-plugin-jpackage/build.gradle
Executable file
|
@ -0,0 +1,34 @@
|
||||||
|
plugins {
|
||||||
|
id 'java-gradle-plugin'
|
||||||
|
alias(libs.plugins.publish)
|
||||||
|
}
|
||||||
|
|
||||||
|
apply plugin: 'java-gradle-plugin'
|
||||||
|
apply plugin: 'com.gradle.plugin-publish'
|
||||||
|
|
||||||
|
apply from: rootProject.file('gradle/compile/groovy.gradle')
|
||||||
|
apply from: rootProject.file('gradle/test/junit5.gradle')
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
api gradleApi()
|
||||||
|
testImplementation gradleTestKit()
|
||||||
|
testImplementation testLibs.spock.core
|
||||||
|
testImplementation testLibs.spock.junit4
|
||||||
|
}
|
||||||
|
|
||||||
|
if (project.hasProperty('gradle.publish.key')) {
|
||||||
|
gradlePlugin {
|
||||||
|
website = 'https://xbib.org/joerg/gradle-plugins/src/branch/main/gradle-plugin-jpackage'
|
||||||
|
vcsUrl = 'https://xbib.org/joerg/gradle-plugins'
|
||||||
|
plugins {
|
||||||
|
jpackagePlugin {
|
||||||
|
id = 'org.xbib.gradle.plugin.jpackage'
|
||||||
|
implementationClass = 'org.xbib.gradle.plugin.jpackage.JPackagePlugin'
|
||||||
|
version = project.version
|
||||||
|
description = 'Gradle JPackage plugin'
|
||||||
|
displayName = 'Gradle JPackage plugin'
|
||||||
|
tags.set(['jpackage'])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,204 @@
|
||||||
|
package org.xbib.gradle.plugin.jpackage;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import org.gradle.api.Action;
|
||||||
|
import org.gradle.api.NamedDomainObjectContainer;
|
||||||
|
import org.gradle.api.NonNullApi;
|
||||||
|
import org.gradle.api.Project;
|
||||||
|
import org.gradle.api.Task;
|
||||||
|
import org.gradle.api.artifacts.Configuration;
|
||||||
|
import org.gradle.api.artifacts.ConfigurationContainer;
|
||||||
|
import org.gradle.api.attributes.Attribute;
|
||||||
|
import org.gradle.api.attributes.Bundling;
|
||||||
|
import org.gradle.api.attributes.Category;
|
||||||
|
import org.gradle.api.attributes.LibraryElements;
|
||||||
|
import org.gradle.api.attributes.Usage;
|
||||||
|
import org.gradle.api.attributes.java.TargetJvmEnvironment;
|
||||||
|
import org.gradle.api.file.ConfigurableFileCollection;
|
||||||
|
import org.gradle.api.file.DirectoryProperty;
|
||||||
|
import org.gradle.api.model.ObjectFactory;
|
||||||
|
import org.gradle.api.plugins.ApplicationPlugin;
|
||||||
|
import org.gradle.api.plugins.JavaApplication;
|
||||||
|
import org.gradle.api.plugins.JavaPluginExtension;
|
||||||
|
import org.gradle.api.provider.Property;
|
||||||
|
import org.gradle.api.tasks.JavaExec;
|
||||||
|
import org.gradle.api.tasks.SourceSet;
|
||||||
|
import org.gradle.api.tasks.SourceSetContainer;
|
||||||
|
import org.gradle.api.tasks.TaskContainer;
|
||||||
|
import org.gradle.api.tasks.TaskProvider;
|
||||||
|
import org.gradle.jvm.toolchain.JavaToolchainService;
|
||||||
|
import org.gradle.nativeplatform.MachineArchitecture;
|
||||||
|
import org.gradle.nativeplatform.OperatingSystemFamily;
|
||||||
|
import static org.gradle.language.base.plugins.LifecycleBasePlugin.ASSEMBLE_TASK_NAME;
|
||||||
|
import static org.gradle.language.base.plugins.LifecycleBasePlugin.BUILD_GROUP;
|
||||||
|
import static org.gradle.nativeplatform.MachineArchitecture.ARCHITECTURE_ATTRIBUTE;
|
||||||
|
import static org.gradle.nativeplatform.OperatingSystemFamily.LINUX;
|
||||||
|
import static org.gradle.nativeplatform.OperatingSystemFamily.MACOS;
|
||||||
|
import static org.gradle.nativeplatform.OperatingSystemFamily.OPERATING_SYSTEM_ATTRIBUTE;
|
||||||
|
import static org.gradle.nativeplatform.OperatingSystemFamily.WINDOWS;
|
||||||
|
|
||||||
|
@NonNullApi
|
||||||
|
public abstract class JPackageExtension {
|
||||||
|
|
||||||
|
public abstract Property<String> getApplicationName();
|
||||||
|
|
||||||
|
public abstract Property<String> getApplicationVersion();
|
||||||
|
|
||||||
|
public abstract Property<String> getApplicationDescription();
|
||||||
|
|
||||||
|
public abstract Property<String> getVendor();
|
||||||
|
|
||||||
|
public abstract Property<String> getCopyright();
|
||||||
|
|
||||||
|
public abstract DirectoryProperty getJpackageResources();
|
||||||
|
|
||||||
|
public abstract ConfigurableFileCollection getResources();
|
||||||
|
|
||||||
|
private final NamedDomainObjectContainer<JPackageTarget> targets = getObjects().domainObjectContainer(JPackageTarget.class);
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
protected abstract JavaToolchainService getJavaToolchains();
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
protected abstract ObjectFactory getObjects();
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
protected abstract Project getProject();
|
||||||
|
|
||||||
|
public JPackageTarget target(String label, Action<? super JPackageTarget> action) {
|
||||||
|
JPackageTarget target;
|
||||||
|
if (targets.getNames().contains(label)) {
|
||||||
|
target = targets.getByName(label);
|
||||||
|
} else {
|
||||||
|
target = targets.create(label, this::newTarget);
|
||||||
|
}
|
||||||
|
action.execute(target);
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JPackageTarget primaryTarget(JPackageTarget target) {
|
||||||
|
SourceSetContainer sourceSets = getProject().getExtensions().getByType(SourceSetContainer.class);
|
||||||
|
ConfigurationContainer configurations = getProject().getConfigurations();
|
||||||
|
sourceSets.all(sourceSet -> {
|
||||||
|
configureTargetAttributes(configurations.getByName(sourceSet.getCompileClasspathConfigurationName()), target);
|
||||||
|
configureTargetAttributes(configurations.getByName(sourceSet.getRuntimeClasspathConfigurationName()), target);
|
||||||
|
configurations.matching(conf -> "mainRuntimeClasspath".equals(conf.getName())).all(conf -> configureTargetAttributes(conf, target));
|
||||||
|
});
|
||||||
|
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void newTarget(JPackageTarget target) {
|
||||||
|
target.getPackageTypes().convention(target.getOperatingSystem().map(os -> switch (os) {
|
||||||
|
case WINDOWS -> Arrays.asList("exe", "msi");
|
||||||
|
case MACOS -> Arrays.asList("pkg", "dmg");
|
||||||
|
case LINUX -> Arrays.asList("rpm");
|
||||||
|
default -> Collections.emptyList();
|
||||||
|
}));
|
||||||
|
ConfigurationContainer configurations = getProject().getConfigurations();
|
||||||
|
SourceSetContainer sourceSets = getProject().getExtensions().getByType(SourceSetContainer.class);
|
||||||
|
sourceSets.all(sourceSet -> {
|
||||||
|
Configuration internal = maybeCreateInternalConfiguration();
|
||||||
|
configurations.create(target.getName() + capitalize(sourceSet.getCompileClasspathConfigurationName()), c -> {
|
||||||
|
c.setCanBeConsumed(false);
|
||||||
|
c.setVisible(false);
|
||||||
|
configureJavaStandardAttributes(c, Usage.JAVA_API);
|
||||||
|
configureTargetAttributes(c, target);
|
||||||
|
c.extendsFrom(
|
||||||
|
configurations.getByName(sourceSet.getImplementationConfigurationName()),
|
||||||
|
configurations.getByName(sourceSet.getCompileOnlyConfigurationName()),
|
||||||
|
internal
|
||||||
|
);
|
||||||
|
});
|
||||||
|
Configuration runtimeClasspath = configurations.create(target.getName() + capitalize(sourceSet.getRuntimeClasspathConfigurationName()), c -> {
|
||||||
|
c.setCanBeConsumed(false);
|
||||||
|
c.setVisible(false);
|
||||||
|
configureJavaStandardAttributes(c, Usage.JAVA_RUNTIME);
|
||||||
|
configureTargetAttributes(c, target);
|
||||||
|
c.extendsFrom(
|
||||||
|
configurations.getByName(sourceSet.getImplementationConfigurationName()),
|
||||||
|
configurations.getByName(sourceSet.getRuntimeOnlyConfigurationName()),
|
||||||
|
internal
|
||||||
|
);
|
||||||
|
});
|
||||||
|
if (SourceSet.isMain(sourceSet)) {
|
||||||
|
getProject().getPlugins().withType(ApplicationPlugin.class, p -> registerTargetSpecificTasks(target, sourceSet.getJarTaskName(), runtimeClasspath));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void configureJavaStandardAttributes(Configuration resolvable, String usage) {
|
||||||
|
resolvable.getAttributes().attribute(Usage.USAGE_ATTRIBUTE, getObjects().named(Usage.class, usage));
|
||||||
|
resolvable.getAttributes().attribute(Category.CATEGORY_ATTRIBUTE, getObjects().named(Category.class, Category.LIBRARY));
|
||||||
|
resolvable.getAttributes().attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, getObjects().named(LibraryElements.class, LibraryElements.JAR));
|
||||||
|
resolvable.getAttributes().attribute(TargetJvmEnvironment.TARGET_JVM_ENVIRONMENT_ATTRIBUTE, getObjects().named(TargetJvmEnvironment.class, TargetJvmEnvironment.STANDARD_JVM));
|
||||||
|
resolvable.getAttributes().attribute(Bundling.BUNDLING_ATTRIBUTE, getObjects().named(Bundling.class, Bundling.EXTERNAL));
|
||||||
|
resolvable.getAttributes().attribute(Attribute.of("javaModule", Boolean.class), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void configureTargetAttributes(Configuration resolvable, JPackageTarget target) {
|
||||||
|
resolvable.getAttributes().attributeProvider(OPERATING_SYSTEM_ATTRIBUTE, target.getOperatingSystem().map(name -> getObjects().named(OperatingSystemFamily.class, name)));
|
||||||
|
resolvable.getAttributes().attributeProvider(ARCHITECTURE_ATTRIBUTE, target.getArchitecture().map(name -> getObjects().named(MachineArchitecture.class, name)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerTargetSpecificTasks(JPackageTarget target, String applicationJarTask, Configuration runtimeClasspath) {
|
||||||
|
TaskContainer tasks = getProject().getTasks();
|
||||||
|
JavaPluginExtension java = getProject().getExtensions().getByType(JavaPluginExtension.class);
|
||||||
|
JavaApplication application = getProject().getExtensions().getByType(JavaApplication.class);
|
||||||
|
TaskProvider<JPackageTask> jpackage = tasks.register("jpackage" + capitalize(target.getName()), JPackageTask.class, t -> {
|
||||||
|
t.getJavaInstallation().convention(getJavaToolchains().compilerFor(java.getToolchain()).get().getMetadata());
|
||||||
|
t.getOperatingSystem().convention(target.getOperatingSystem());
|
||||||
|
t.getArchitecture().convention(target.getArchitecture());
|
||||||
|
t.getMainModule().convention(application.getMainModule());
|
||||||
|
t.getVersion().convention(getApplicationVersion());
|
||||||
|
t.getModulePath().from(tasks.named(applicationJarTask));
|
||||||
|
t.getModulePath().from(runtimeClasspath);
|
||||||
|
t.getApplicationName().convention(getApplicationName());
|
||||||
|
t.getJpackageResources().convention(getJpackageResources().dir(target.getOperatingSystem()));
|
||||||
|
t.getApplicationDescription().convention(getApplicationDescription());
|
||||||
|
t.getVendor().convention(getVendor());
|
||||||
|
t.getCopyright().convention(getCopyright());
|
||||||
|
t.getJavaOptions().convention(application.getApplicationDefaultJvmArgs());
|
||||||
|
t.getOptions().convention(target.getOptions());
|
||||||
|
t.getPackageTypes().convention(target.getPackageTypes());
|
||||||
|
t.getResources().from(getResources());
|
||||||
|
t.getDestination().convention(getProject().getLayout().getBuildDirectory().dir("packages/" + target.getName()));
|
||||||
|
t.getTempDirectory().convention(getProject().getLayout().getBuildDirectory().dir("tmp/jpackage/" + target.getName()));
|
||||||
|
});
|
||||||
|
tasks.register("run" + capitalize(target.getName()), JavaExec.class, t -> {
|
||||||
|
t.setGroup(ApplicationPlugin.APPLICATION_GROUP);
|
||||||
|
t.setDescription("Run this project as a JVM application on " + target.getName());
|
||||||
|
t.getJavaLauncher().convention(getJavaToolchains().launcherFor(java.getToolchain()));
|
||||||
|
t.getMainModule().convention(application.getMainModule());
|
||||||
|
t.getMainClass().convention(application.getMainClass());
|
||||||
|
t.setJvmArgs(application.getApplicationDefaultJvmArgs());
|
||||||
|
t.classpath(tasks.named("jar"), runtimeClasspath);
|
||||||
|
});
|
||||||
|
String targetAssembleLifecycle = "assemble" + capitalize(target.getName());
|
||||||
|
if (!tasks.getNames().contains(targetAssembleLifecycle)) {
|
||||||
|
TaskProvider<Task> lifecycleTask = tasks.register(targetAssembleLifecycle, t -> {
|
||||||
|
t.setGroup(BUILD_GROUP);
|
||||||
|
t.setDescription("Builds this project for " + target.getName());
|
||||||
|
});
|
||||||
|
tasks.named(ASSEMBLE_TASK_NAME, t -> t.dependsOn(lifecycleTask));
|
||||||
|
}
|
||||||
|
tasks.named(targetAssembleLifecycle, t -> t.dependsOn(jpackage));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Configuration maybeCreateInternalConfiguration() {
|
||||||
|
Configuration internal = getProject().getConfigurations().findByName("internal");
|
||||||
|
if (internal != null) {
|
||||||
|
return internal;
|
||||||
|
}
|
||||||
|
return getProject().getConfigurations().create("internal", i -> {
|
||||||
|
i.setCanBeResolved(false);
|
||||||
|
i.setCanBeConsumed(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private String capitalize(String s) {
|
||||||
|
return s.substring(0, 1).toUpperCase() + s.substring(1);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package org.xbib.gradle.plugin.jpackage;
|
||||||
|
|
||||||
|
import org.gradle.api.NonNullApi;
|
||||||
|
import org.gradle.api.Plugin;
|
||||||
|
import org.gradle.api.Project;
|
||||||
|
import org.gradle.api.file.SourceDirectorySet;
|
||||||
|
import org.gradle.api.plugins.JavaPlugin;
|
||||||
|
import org.gradle.api.tasks.SourceSetContainer;
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
@NonNullApi
|
||||||
|
public abstract class JPackagePlugin implements Plugin<Project> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void apply(Project project) {
|
||||||
|
project.getPlugins().apply(JavaPlugin.class);
|
||||||
|
SourceSetContainer sourceSets = project.getExtensions().getByType(SourceSetContainer.class);
|
||||||
|
SourceDirectorySet mainResources = sourceSets.getByName("main").getResources();
|
||||||
|
JPackageExtension jPackageExtension = project.getExtensions().create("jpackage", JPackageExtension.class);
|
||||||
|
jPackageExtension.getApplicationName().convention(project.getName());
|
||||||
|
jPackageExtension.getApplicationVersion().convention(project.provider(() ->
|
||||||
|
(String) project.getVersion()));
|
||||||
|
jPackageExtension.getJpackageResources().convention(project.provider(() ->
|
||||||
|
project.getLayout().getProjectDirectory().dir(mainResources.getSrcDirs().iterator().next().getParent() + "/resourcesPackage")));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package org.xbib.gradle.plugin.jpackage;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import org.gradle.api.provider.ListProperty;
|
||||||
|
import org.gradle.api.provider.Property;
|
||||||
|
|
||||||
|
public abstract class JPackageTarget {
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
public abstract Property<String> getOperatingSystem();
|
||||||
|
|
||||||
|
public abstract Property<String> getArchitecture();
|
||||||
|
|
||||||
|
public abstract ListProperty<String> getPackageTypes();
|
||||||
|
|
||||||
|
public abstract ListProperty<String> getOptions();
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public JPackageTarget(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,236 @@
|
||||||
|
package org.xbib.gradle.plugin.jpackage;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import org.gradle.api.DefaultTask;
|
||||||
|
import org.gradle.api.file.ConfigurableFileCollection;
|
||||||
|
import org.gradle.api.file.Directory;
|
||||||
|
import org.gradle.api.file.DirectoryProperty;
|
||||||
|
import org.gradle.api.internal.file.FileOperations;
|
||||||
|
import org.gradle.api.provider.ListProperty;
|
||||||
|
import org.gradle.api.provider.Property;
|
||||||
|
import org.gradle.api.tasks.CacheableTask;
|
||||||
|
import org.gradle.api.tasks.Classpath;
|
||||||
|
import org.gradle.api.tasks.Input;
|
||||||
|
import org.gradle.api.tasks.InputDirectory;
|
||||||
|
import org.gradle.api.tasks.InputFiles;
|
||||||
|
import org.gradle.api.tasks.Internal;
|
||||||
|
import org.gradle.api.tasks.Nested;
|
||||||
|
import org.gradle.api.tasks.Optional;
|
||||||
|
import org.gradle.api.tasks.OutputDirectory;
|
||||||
|
import org.gradle.api.tasks.PathSensitive;
|
||||||
|
import org.gradle.api.tasks.PathSensitivity;
|
||||||
|
import org.gradle.api.tasks.TaskAction;
|
||||||
|
import org.gradle.jvm.toolchain.JavaInstallationMetadata;
|
||||||
|
import org.gradle.process.ExecOperations;
|
||||||
|
import static java.util.Objects.requireNonNull;
|
||||||
|
import static org.gradle.nativeplatform.OperatingSystemFamily.WINDOWS;
|
||||||
|
|
||||||
|
@CacheableTask
|
||||||
|
public abstract class JPackageTask extends DefaultTask {
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
public abstract Property<JavaInstallationMetadata> getJavaInstallation();
|
||||||
|
|
||||||
|
@Input
|
||||||
|
public abstract Property<String> getOperatingSystem();
|
||||||
|
|
||||||
|
@Input
|
||||||
|
public abstract Property<String> getArchitecture();
|
||||||
|
|
||||||
|
@Input
|
||||||
|
public abstract Property<String> getMainModule();
|
||||||
|
|
||||||
|
@Input
|
||||||
|
public abstract Property<String> getVersion();
|
||||||
|
|
||||||
|
@Classpath
|
||||||
|
public abstract ConfigurableFileCollection getModulePath();
|
||||||
|
|
||||||
|
@Input
|
||||||
|
public abstract Property<String> getApplicationName();
|
||||||
|
|
||||||
|
@Input
|
||||||
|
@Optional
|
||||||
|
public abstract Property<String> getApplicationDescription();
|
||||||
|
|
||||||
|
@InputDirectory
|
||||||
|
@PathSensitive(PathSensitivity.RELATIVE)
|
||||||
|
public abstract DirectoryProperty getJpackageResources();
|
||||||
|
|
||||||
|
@InputFiles
|
||||||
|
@PathSensitive(PathSensitivity.RELATIVE)
|
||||||
|
public abstract ConfigurableFileCollection getResources();
|
||||||
|
|
||||||
|
@Input
|
||||||
|
@Optional
|
||||||
|
public abstract Property<String> getVendor();
|
||||||
|
|
||||||
|
@Input
|
||||||
|
@Optional
|
||||||
|
public abstract Property<String> getCopyright();
|
||||||
|
|
||||||
|
@Input
|
||||||
|
public abstract ListProperty<String> getJavaOptions();
|
||||||
|
|
||||||
|
@Input
|
||||||
|
public abstract ListProperty<String> getOptions();
|
||||||
|
|
||||||
|
@Input
|
||||||
|
public abstract ListProperty<String> getPackageTypes();
|
||||||
|
|
||||||
|
@OutputDirectory
|
||||||
|
public abstract DirectoryProperty getDestination();
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
public abstract DirectoryProperty getTempDirectory();
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
protected abstract FileOperations getFiles();
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
protected abstract ExecOperations getExec();
|
||||||
|
|
||||||
|
@TaskAction
|
||||||
|
public void runJpackage() throws Exception {
|
||||||
|
getFiles().delete(getTempDirectory());
|
||||||
|
getFiles().delete(getDestination());
|
||||||
|
String os = getOperatingSystem().get();
|
||||||
|
String arch = getArchitecture().get();
|
||||||
|
String hostOs = System.getProperty("os.name").replace(" ", "").toLowerCase();
|
||||||
|
String hostArch = System.getProperty("os.arch");
|
||||||
|
validateHostSystem(arch, hostArch, os, hostOs);
|
||||||
|
Directory resourcesDir = getTempDirectory().get().dir("jpackage-resources");
|
||||||
|
Directory appImageParent = getTempDirectory().get().dir("app-image");
|
||||||
|
resourcesDir.getAsFile().mkdirs();
|
||||||
|
getFiles().copy(c -> {
|
||||||
|
c.from(getJpackageResources());
|
||||||
|
c.into(resourcesDir);
|
||||||
|
c.rename(f -> f.replace("icon", getApplicationName().get()));
|
||||||
|
});
|
||||||
|
String executableName = WINDOWS.equals(os) ? "jpackage.exe" : "jpackage";
|
||||||
|
String jpackage = getJavaInstallation().get()
|
||||||
|
.getInstallationPath().file("bin/" + executableName)
|
||||||
|
.getAsFile().getAbsolutePath();
|
||||||
|
getExec().exec(e -> {
|
||||||
|
e.commandLine(
|
||||||
|
jpackage,
|
||||||
|
"--type",
|
||||||
|
"app-image",
|
||||||
|
"--module",
|
||||||
|
getMainModule().get(),
|
||||||
|
"--resource-dir",
|
||||||
|
resourcesDir.getAsFile().getPath(),
|
||||||
|
"--app-version",
|
||||||
|
getVersion().get(),
|
||||||
|
"--module-path",
|
||||||
|
getModulePath().getAsPath(),
|
||||||
|
"--name",
|
||||||
|
getApplicationName().get(),
|
||||||
|
"--dest",
|
||||||
|
appImageParent.getAsFile().getPath()
|
||||||
|
);
|
||||||
|
if (getApplicationDescription().isPresent()) {
|
||||||
|
e.args("--description", getApplicationDescription().get());
|
||||||
|
}
|
||||||
|
if (getVendor().isPresent()) {
|
||||||
|
e.args("--vendor", getVendor().get());
|
||||||
|
}
|
||||||
|
if (getCopyright().isPresent()) {
|
||||||
|
e.args("--copyright", getCopyright().get());
|
||||||
|
}
|
||||||
|
for (String javaOption : getJavaOptions().get()) {
|
||||||
|
e.args("--java-options", javaOption);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
File appImageFolder = requireNonNull(appImageParent.getAsFile().listFiles())[0];
|
||||||
|
File appResourcesFolder;
|
||||||
|
if (os.contains("macos")) {
|
||||||
|
appResourcesFolder = new File(appImageFolder, "Contents/app");
|
||||||
|
} else if (os.contains("windows")) {
|
||||||
|
appResourcesFolder = new File(appImageFolder, "app");
|
||||||
|
} else {
|
||||||
|
appResourcesFolder = new File(appImageFolder, "lib/app");
|
||||||
|
}
|
||||||
|
getFiles().copy(c -> {
|
||||||
|
c.from(getResources());
|
||||||
|
c.into(appResourcesFolder);
|
||||||
|
});
|
||||||
|
getPackageTypes().get().forEach(packageType ->
|
||||||
|
getExec().exec(e -> {
|
||||||
|
e.commandLine(
|
||||||
|
jpackage,
|
||||||
|
"--type",
|
||||||
|
packageType,
|
||||||
|
"--app-image",
|
||||||
|
appImageFolder.getPath(),
|
||||||
|
"--dest",
|
||||||
|
getDestination().get().getAsFile().getPath()
|
||||||
|
);
|
||||||
|
for (String option : getOptions().get()) {
|
||||||
|
e.args(option);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
generateChecksums();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void generateChecksums() throws NoSuchAlgorithmException, IOException {
|
||||||
|
File destination = getDestination().get().getAsFile();
|
||||||
|
List<File> allFiles = Arrays.stream(requireNonNull(destination.listFiles()))
|
||||||
|
.filter(File::isFile)
|
||||||
|
.toList();
|
||||||
|
for (File result : allFiles) {
|
||||||
|
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||||||
|
byte[] encoded = digest.digest(Files.readAllBytes(result.toPath()));
|
||||||
|
Files.write(new File(destination, result.getName() + ".sha256").toPath(), bytesToHex(encoded).getBytes());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String bytesToHex(byte[] hash) {
|
||||||
|
StringBuilder hexString = new StringBuilder(2 * hash.length);
|
||||||
|
for (byte b : hash) {
|
||||||
|
String hex = Integer.toHexString(0xff & b);
|
||||||
|
if (hex.length() == 1) {
|
||||||
|
hexString.append('0');
|
||||||
|
}
|
||||||
|
hexString.append(hex);
|
||||||
|
}
|
||||||
|
return hexString.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateHostSystem(String arch, String hostArch, String os, String hostOs) {
|
||||||
|
if (os.contains("macos")) {
|
||||||
|
if (!hostOs.contains(os)) {
|
||||||
|
wrongHostSystemError(hostOs, os);
|
||||||
|
}
|
||||||
|
} else if (os.contains("windows")) {
|
||||||
|
if (!hostOs.contains(os)) {
|
||||||
|
wrongHostSystemError(hostOs, os);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (hostOs.contains("windows") || hostOs.contains("macos")) {
|
||||||
|
wrongHostSystemError(hostOs, os);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (arch.contains("64") && !hostArch.contains("64")) {
|
||||||
|
wrongHostSystemError(hostArch, arch);
|
||||||
|
}
|
||||||
|
if (arch.contains("aarch") && !hostArch.contains("aarch")) {
|
||||||
|
wrongHostSystemError(hostArch, arch);
|
||||||
|
}
|
||||||
|
if (!arch.contains("aarch") && hostArch.contains("aarch")) {
|
||||||
|
wrongHostSystemError(hostArch, arch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void wrongHostSystemError(String hostOs, String os) {
|
||||||
|
throw new RuntimeException("Running on " + hostOs + "; cannot build for " + os);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
package org.xbib.gradle.plugin.jpackage
|
||||||
|
|
||||||
|
import org.xbib.gradle.plugin.jpackage.fixture.GradleBuild
|
||||||
|
import spock.lang.Specification
|
||||||
|
|
||||||
|
import static org.gradle.testkit.runner.TaskOutcome.FAILED
|
||||||
|
import static org.gradle.testkit.runner.TaskOutcome.SUCCESS
|
||||||
|
import static org.xbib.gradle.plugin.jpackage.fixture.GradleBuild.hostOs
|
||||||
|
import static org.xbib.gradle.plugin.jpackage.fixture.GradleBuild.runsOnLinux
|
||||||
|
import static org.xbib.gradle.plugin.jpackage.fixture.GradleBuild.runsOnMacos
|
||||||
|
import static org.xbib.gradle.plugin.jpackage.fixture.GradleBuild.runsOnWindows
|
||||||
|
|
||||||
|
class JPackageTest extends Specification {
|
||||||
|
|
||||||
|
@Delegate
|
||||||
|
GradleBuild build = new GradleBuild()
|
||||||
|
|
||||||
|
def "can use plugin on #os with success=#success"() {
|
||||||
|
given:
|
||||||
|
def taskToRun = ":app:assemble${label.capitalize()}"
|
||||||
|
def taskToCheck = ":app:jpackage${label.capitalize()}"
|
||||||
|
def macosArch = System.getProperty('os.arch').contains('aarch') ? 'aarch64' : 'x86-64'
|
||||||
|
appBuildFile << """
|
||||||
|
version = "1.0"
|
||||||
|
jpackage {
|
||||||
|
target("fedora") {
|
||||||
|
operatingSystem = "linux"
|
||||||
|
architecture = "x86-64"
|
||||||
|
}
|
||||||
|
target("macos") {
|
||||||
|
operatingSystem = "macos"
|
||||||
|
architecture = "$macosArch"
|
||||||
|
}
|
||||||
|
target("windows") {
|
||||||
|
operatingSystem = "windows"
|
||||||
|
architecture = "x86-64"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
appModuleInfoFile << '''
|
||||||
|
module org.example.app {
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
|
||||||
|
when:
|
||||||
|
def result = success ? build(taskToRun) : fail(taskToRun)
|
||||||
|
|
||||||
|
then:
|
||||||
|
result.task(taskToCheck).outcome == (success ? SUCCESS : FAILED)
|
||||||
|
success || result.output.contains("> Running on ${hostOs()}; cannot build for $os")
|
||||||
|
|
||||||
|
where:
|
||||||
|
label | os | success
|
||||||
|
'fedora' | 'linux' | runsOnLinux()
|
||||||
|
'windows' | 'windows' | runsOnWindows()
|
||||||
|
'macos' | 'macos' | runsOnMacos()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,121 @@
|
||||||
|
package org.xbib.gradle.plugin.jpackage.fixture
|
||||||
|
|
||||||
|
import org.gradle.testkit.runner.BuildResult
|
||||||
|
import org.gradle.testkit.runner.GradleRunner
|
||||||
|
|
||||||
|
import java.lang.management.ManagementFactory
|
||||||
|
import java.nio.file.Files
|
||||||
|
|
||||||
|
class GradleBuild {
|
||||||
|
|
||||||
|
final File projectDir
|
||||||
|
final File settingsFile
|
||||||
|
final File appBuildFile
|
||||||
|
final File appModuleInfoFile
|
||||||
|
final File libBuildFile
|
||||||
|
final File libModuleInfoFile
|
||||||
|
|
||||||
|
final String gradleVersionUnderTest = System.getProperty('gradleVersionUnderTest')
|
||||||
|
|
||||||
|
GradleBuild(File projectDir = Files.createTempDirectory('gradle-build').toFile()) {
|
||||||
|
this.projectDir = projectDir
|
||||||
|
this.settingsFile = file('settings.gradle.kts')
|
||||||
|
this.appBuildFile = file('app/build.gradle.kts')
|
||||||
|
this.appModuleInfoFile = file('app/src/main/java/module-info.java')
|
||||||
|
this.libBuildFile = file('lib/build.gradle.kts')
|
||||||
|
this.libModuleInfoFile = file('lib/src/main/java/module-info.java')
|
||||||
|
|
||||||
|
file('app/src/main/resourcesPackage/windows').mkdirs()
|
||||||
|
file('app/src/main/resourcesPackage/macos').mkdirs()
|
||||||
|
file('app/src/main/resourcesPackage/linux').mkdirs()
|
||||||
|
|
||||||
|
settingsFile << '''
|
||||||
|
dependencyResolutionManagement { repositories.mavenCentral() }
|
||||||
|
includeBuild(".")
|
||||||
|
rootProject.name = "test-project"
|
||||||
|
include("app", "lib")
|
||||||
|
'''
|
||||||
|
appBuildFile << '''
|
||||||
|
plugins {
|
||||||
|
id("org.xbib.gradle.plugin.jpackage")
|
||||||
|
id("application")
|
||||||
|
}
|
||||||
|
group = "org.example"
|
||||||
|
application {
|
||||||
|
mainModule.set("org.example.app")
|
||||||
|
mainClass.set("org.example.app.Main")
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
file("app/src/main/java/org/example/app/Main.java") << '''
|
||||||
|
package org.example.app;
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
public static void main(String... args) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
file("app/src/test/java/org/example/app/test/MainTest.java") << '''
|
||||||
|
package org.example.app.test;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.example.app.Main;
|
||||||
|
|
||||||
|
public class MainTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testApp() {
|
||||||
|
new Main();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
|
||||||
|
libBuildFile << '''
|
||||||
|
plugins {
|
||||||
|
id("org.xbib.gradle.plugin.jpackage")
|
||||||
|
id("java-library")
|
||||||
|
}
|
||||||
|
group = "org.example"
|
||||||
|
'''
|
||||||
|
}
|
||||||
|
|
||||||
|
File file(String path) {
|
||||||
|
new File(projectDir, path).tap {
|
||||||
|
it.getParentFile().mkdirs()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean runsOnWindows() {
|
||||||
|
hostOs().contains('win')
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean runsOnMacos() {
|
||||||
|
hostOs().contains('mac')
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean runsOnLinux() {
|
||||||
|
!runsOnWindows() && !runsOnMacos()
|
||||||
|
}
|
||||||
|
|
||||||
|
static String hostOs() {
|
||||||
|
System.getProperty("os.name").replace(" ", "").toLowerCase()
|
||||||
|
}
|
||||||
|
|
||||||
|
BuildResult build(taskToRun) {
|
||||||
|
runner(taskToRun).build()
|
||||||
|
}
|
||||||
|
|
||||||
|
BuildResult fail(taskToRun) {
|
||||||
|
runner(taskToRun).buildAndFail()
|
||||||
|
}
|
||||||
|
|
||||||
|
GradleRunner runner(String... args) {
|
||||||
|
GradleRunner.create()
|
||||||
|
.forwardOutput()
|
||||||
|
.withPluginClasspath()
|
||||||
|
.withProjectDir(projectDir)
|
||||||
|
.withArguments(Arrays.asList(args) + '-s')
|
||||||
|
.withDebug(ManagementFactory.getRuntimeMXBean().getInputArguments().toString().contains("-agentlib:jdwp")).with {
|
||||||
|
gradleVersionUnderTest ? it.withGradleVersion(gradleVersionUnderTest) : it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,6 +7,7 @@ apply plugin: 'java-gradle-plugin'
|
||||||
apply plugin: 'com.gradle.plugin-publish'
|
apply plugin: 'com.gradle.plugin-publish'
|
||||||
|
|
||||||
apply from: rootProject.file('gradle/compile/groovy.gradle')
|
apply from: rootProject.file('gradle/compile/groovy.gradle')
|
||||||
|
apply from: rootProject.file('gradle/test/junit5.gradle')
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
api gradleApi()
|
api gradleApi()
|
||||||
|
@ -34,7 +35,7 @@ if (project.hasProperty('gradle.publish.key')) {
|
||||||
version = project.version
|
version = project.version
|
||||||
description = 'Java implementation for RPM packaging'
|
description = 'Java implementation for RPM packaging'
|
||||||
displayName = 'Java implementation for RPM packaging'
|
displayName = 'Java implementation for RPM packaging'
|
||||||
implementationClass = 'org.xbib.gradle.plugin.RpmPlugin'
|
implementationClass = 'org.xbib.gradle.plugin.rpm.RpmPlugin'
|
||||||
tags.set(['rpm'])
|
tags.set(['rpm'])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
name = gradle-plugin-rpm
|
name = gradle-plugin-rpm
|
||||||
version = 3.0.3
|
version = 3.1.0
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package org.xbib.gradle.plugin
|
package org.xbib.gradle.plugin.rpm
|
||||||
|
|
||||||
import org.gradle.api.internal.file.copy.CopyAction
|
import org.gradle.api.internal.file.copy.CopyAction
|
||||||
import org.gradle.api.tasks.Input
|
import org.gradle.api.tasks.Input
|
||||||
|
@ -127,6 +127,14 @@ class Rpm extends AbstractArchiveTask {
|
||||||
@Input
|
@Input
|
||||||
Integer gid
|
Integer gid
|
||||||
|
|
||||||
|
@Optional
|
||||||
|
@Input
|
||||||
|
Boolean setuid
|
||||||
|
|
||||||
|
@Optional
|
||||||
|
@Input
|
||||||
|
Boolean setgid
|
||||||
|
|
||||||
@Optional
|
@Optional
|
||||||
@Input
|
@Input
|
||||||
String maintainer
|
String maintainer
|
||||||
|
@ -349,10 +357,15 @@ class Rpm extends AbstractArchiveTask {
|
||||||
sb.toString()
|
sb.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Directory directory(String path) {
|
Directory directory(String path) {
|
||||||
Directory directory = new Directory(path: path)
|
Directory directory = new Directory(path: path)
|
||||||
directories << directory
|
directories.add(directory)
|
||||||
|
directory
|
||||||
|
}
|
||||||
|
|
||||||
|
Directory directory(String path, String user, String group) {
|
||||||
|
Directory directory = new Directory(path: path, user: user, group: group)
|
||||||
|
directories.add(directory)
|
||||||
directory
|
directory
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package org.xbib.gradle.plugin
|
package org.xbib.gradle.plugin.rpm
|
||||||
|
|
||||||
|
import groovy.util.logging.Log
|
||||||
import org.gradle.api.Project
|
import org.gradle.api.Project
|
||||||
import org.gradle.api.file.DuplicatesStrategy
|
import org.gradle.api.file.DuplicatesStrategy
|
||||||
import org.gradle.api.file.FileCopyDetails
|
import org.gradle.api.file.FileCopyDetails
|
||||||
|
@ -18,7 +19,10 @@ import org.xbib.rpm.payload.Directive
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.nio.file.StandardOpenOption
|
import java.nio.file.StandardOpenOption
|
||||||
|
import java.nio.file.attribute.PosixFileAttributeView
|
||||||
|
import java.nio.file.attribute.PosixFileAttributes
|
||||||
|
|
||||||
|
@Log
|
||||||
class RpmCopyAction implements CopyAction {
|
class RpmCopyAction implements CopyAction {
|
||||||
|
|
||||||
Project project
|
Project project
|
||||||
|
@ -105,8 +109,9 @@ class RpmCopyAction implements CopyAction {
|
||||||
builder.addProvides(provides.packageName, provides.flags, provides.version)
|
builder.addProvides(provides.packageName, provides.flags, provides.version)
|
||||||
}
|
}
|
||||||
for (Directory directory : task.directories) {
|
for (Directory directory : task.directories) {
|
||||||
String user = directory.user ? directory.user : task.user
|
String user = directory.user ?: task.user
|
||||||
String group = directory.group ? directory.group : task.group
|
String group = directory.group ?: task.group
|
||||||
|
log.info("dir = " + directory.path)
|
||||||
builder.addDirectory(directory.path, directory.permissions, null, user, group, directory.addParents)
|
builder.addDirectory(directory.path, directory.permissions, null, user, group, directory.addParents)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -123,20 +128,29 @@ class RpmCopyAction implements CopyAction {
|
||||||
private class StreamAction implements CopyActionProcessingStreamAction {
|
private class StreamAction implements CopyActionProcessingStreamAction {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void processFile(FileCopyDetailsInternal fileCopyDetailsInternal) {
|
void processFile(FileCopyDetailsInternal details) {
|
||||||
boolean addParents = task.addParentDirs != null ? task.addParentDirs : task.addParentDirs
|
boolean addParents = task.addParentDirs != null ? task.addParentDirs : false
|
||||||
Path path = extractPath(tempDir, fileCopyDetailsInternal)
|
Path path = extractPath(tempDir, details)
|
||||||
String p = "/${fileCopyDetailsInternal.path}"
|
PosixFileAttributeView view = Files.getFileAttributeView(path, PosixFileAttributeView)
|
||||||
|
PosixFileAttributes attributes = view.readAttributes()
|
||||||
|
String user = task.user ?: attributes.owner().name
|
||||||
|
String group = task.group ?: attributes.group().name
|
||||||
|
String p = "/${details.path}"
|
||||||
if (Files.isSymbolicLink(path)) {
|
if (Files.isSymbolicLink(path)) {
|
||||||
builder.addLink(p, Files.readSymbolicLink(path).toFile().path, -1)
|
builder.addLink(p, Files.readSymbolicLink(path).toFile().path, -1)
|
||||||
} else if (!fileCopyDetailsInternal.isDirectory()) {
|
} else if (!details.isDirectory()) {
|
||||||
int mode = fileCopyDetailsInternal.mode
|
int mode = details.mode
|
||||||
|
if (task.setuid) {
|
||||||
|
mode |= 01000
|
||||||
|
} task.group
|
||||||
int dirmode = -1
|
int dirmode = -1
|
||||||
|
if (task.setgid) {
|
||||||
|
dirmode |= 02000
|
||||||
|
}
|
||||||
EnumSet<Directive> directive = makeDirective(task.fileType)
|
EnumSet<Directive> directive = makeDirective(task.fileType)
|
||||||
String user = task.user ?: task.user
|
|
||||||
String group = task.group ?: task.group
|
|
||||||
builder.addFile(p, path, mode, dirmode, directive, user, group, addParents)
|
builder.addFile(p, path, mode, dirmode, directive, user, group, addParents)
|
||||||
}
|
}
|
||||||
|
// we skip all directories
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package org.xbib.gradle.plugin
|
package org.xbib.gradle.plugin.rpm
|
||||||
|
|
||||||
import org.gradle.api.Plugin
|
import org.gradle.api.Plugin
|
||||||
import org.gradle.api.Project
|
import org.gradle.api.Project
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue