From 59f1e8f1eefba059c4a1991c1045f9626b8fd917 Mon Sep 17 00:00:00 2001 From: Jendrik Johannes Date: Sat, 26 Aug 2023 00:24:50 +0200 Subject: [PATCH] Use variants to select JavaFX platform Jars in the dependency graph - Metadata Rule (metadata could later be published by JavaFX) - Transitive dependencies no longer need to be maintained in the plugin - Empty Jars are no longer on the classpath - Manual classpath and module path modification limited to minimum - No hard dependency on 'org.javamodularity:moduleplugin' anymore - Users can then choose between Gradle's built in Modul System support or the javamodularity plugin - Dependency declaration done lazily (via withDependencies) --- build.gradle | 1 - .../java/org/openjfx/gradle/JavaFXModule.java | 32 +---- .../org/openjfx/gradle/JavaFXOptions.java | 124 +++++++++++------- .../org/openjfx/gradle/JavaFXPlatform.java | 31 +++-- .../java/org/openjfx/gradle/JavaFXPlugin.java | 91 ++++++++++++- .../JavaFXComponentMetadataRule.java | 86 ++++++++++++ .../org/openjfx/gradle/tasks/ExecTask.java | 124 ------------------ .../org/openjfx/gradle/JavaFXModuleTest.java | 11 -- .../openjfx/gradle/JavaFXPluginSmokeTest.java | 2 + .../gradle/JavaFXPluginSmokeTestGradle61.java | 2 +- 10 files changed, 281 insertions(+), 223 deletions(-) create mode 100644 src/main/java/org/openjfx/gradle/metadatarule/JavaFXComponentMetadataRule.java delete mode 100644 src/main/java/org/openjfx/gradle/tasks/ExecTask.java diff --git a/build.gradle b/build.gradle index 423ad4e..1d8100f 100644 --- a/build.gradle +++ b/build.gradle @@ -18,7 +18,6 @@ repositories { dependencies { implementation 'com.google.gradle:osdetector-gradle-plugin:1.7.3' - implementation 'org.javamodularity:moduleplugin:1.8.12' testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.2' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine' diff --git a/src/main/java/org/openjfx/gradle/JavaFXModule.java b/src/main/java/org/openjfx/gradle/JavaFXModule.java index 982d844..353a96f 100644 --- a/src/main/java/org/openjfx/gradle/JavaFXModule.java +++ b/src/main/java/org/openjfx/gradle/JavaFXModule.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2020, Gluon + * Copyright (c) 2018, 2023, Gluon * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,7 +31,6 @@ import org.gradle.api.GradleException; -import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.Optional; @@ -43,22 +42,16 @@ public enum JavaFXModule { BASE, - GRAPHICS(BASE), - CONTROLS(BASE, GRAPHICS), - FXML(BASE, GRAPHICS), - MEDIA(BASE, GRAPHICS), - SWING(BASE, GRAPHICS), - WEB(BASE, CONTROLS, GRAPHICS, MEDIA); + GRAPHICS, + CONTROLS, + FXML, + MEDIA, + SWING, + WEB; static final String PREFIX_MODULE = "javafx."; private static final String PREFIX_ARTIFACT = "javafx-"; - private List dependentModules; - - JavaFXModule(JavaFXModule...dependentModules) { - this.dependentModules = List.of(dependentModules); - } - public static Optional fromModuleName(String moduleName) { return Stream.of(JavaFXModule.values()) .filter(javaFXModule -> moduleName.equals(javaFXModule.getModuleName())) @@ -88,7 +81,6 @@ public static Set getJavaFXModules(List moduleNames) { return moduleNames.stream() .map(JavaFXModule::fromModuleName) .flatMap(Optional::stream) - .flatMap(javaFXModule -> javaFXModule.getMavenDependencies().stream()) .collect(Collectors.toSet()); } @@ -101,14 +93,4 @@ public static void validateModules(List moduleNames) { throw new GradleException("Found one or more invalid JavaFX module names: " + invalidModules); } } - - public List getDependentModules() { - return dependentModules; - } - - public List getMavenDependencies() { - List dependencies = new ArrayList<>(dependentModules); - dependencies.add(0, this); - return dependencies; - } } diff --git a/src/main/java/org/openjfx/gradle/JavaFXOptions.java b/src/main/java/org/openjfx/gradle/JavaFXOptions.java index 9a117f8..b4de550 100644 --- a/src/main/java/org/openjfx/gradle/JavaFXOptions.java +++ b/src/main/java/org/openjfx/gradle/JavaFXOptions.java @@ -29,35 +29,59 @@ */ package org.openjfx.gradle; -import org.gradle.api.Project; +import com.google.gradle.osdetector.OsDetector; +import org.gradle.api.artifacts.Configuration; +import org.gradle.api.artifacts.ConfigurationContainer; +import org.gradle.api.artifacts.dsl.DependencyHandler; +import org.gradle.api.artifacts.dsl.RepositoryHandler; import org.gradle.api.artifacts.repositories.FlatDirectoryArtifactRepository; +import org.gradle.api.model.ObjectFactory; +import org.gradle.api.tasks.SourceSetContainer; +import org.gradle.nativeplatform.MachineArchitecture; +import org.gradle.nativeplatform.OperatingSystemFamily; +import javax.inject.Inject; import java.io.File; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; -import static org.openjfx.gradle.JavaFXModule.PREFIX_MODULE; +abstract public class JavaFXOptions { -public class JavaFXOptions { - - private static final String MAVEN_JAVAFX_ARTIFACT_GROUP_ID = "org.openjfx"; + static final String MAVEN_JAVAFX_ARTIFACT_GROUP_ID = "org.openjfx"; private static final String JAVAFX_SDK_LIB_FOLDER = "lib"; - private final Project project; private JavaFXPlatform platform; private String version = "17"; private String sdk; private String[] configurations = new String[] { "implementation" }; - private String[] lastUpdatedConfigurations; private List modules = new ArrayList<>(); private FlatDirectoryArtifactRepository customSDKArtifactRepository; - public JavaFXOptions(Project project) { - this.project = project; - this.platform = JavaFXPlatform.detect(project); + private final SourceSetContainer sourceSets; + private final Set seenConfigurations = new HashSet<>(); + + @Inject + abstract protected ObjectFactory getObjects(); + + @Inject + abstract protected RepositoryHandler getRepositories(); + + @Inject + abstract protected ConfigurationContainer getConfigurationContainer(); + + @Inject + abstract protected DependencyHandler getDependencies(); + + public JavaFXOptions(SourceSetContainer sourceSets, OsDetector osDetector) { + this.sourceSets = sourceSets; + platform = JavaFXPlatform.detect(osDetector); + setClasspathAttributesForAllSourceSets(); } public JavaFXPlatform getPlatform() { @@ -71,7 +95,23 @@ public JavaFXPlatform getPlatform() { */ public void setPlatform(String platform) { this.platform = JavaFXPlatform.fromString(platform); - updateJavaFXDependencies(); + setClasspathAttributesForAllSourceSets(); + } + + private void setClasspathAttributesForAllSourceSets() { + sourceSets.all(sourceSet -> { + setClasspathAttributes(getConfigurationContainer().getByName(sourceSet.getCompileClasspathConfigurationName())); + setClasspathAttributes(getConfigurationContainer().getByName(sourceSet.getRuntimeClasspathConfigurationName())); + }); + } + + private void setClasspathAttributes(Configuration classpath) { + classpath.getAttributes().attribute( + OperatingSystemFamily.OPERATING_SYSTEM_ATTRIBUTE, + getObjects().named(OperatingSystemFamily.class, platform.getOsFamily())); + classpath.getAttributes().attribute( + MachineArchitecture.ARCHITECTURE_ATTRIBUTE, + getObjects().named(MachineArchitecture.class, platform.getArch())); } public String getVersion() { @@ -80,7 +120,6 @@ public String getVersion() { public void setVersion(String version) { this.version = version; - updateJavaFXDependencies(); } /** @@ -90,7 +129,7 @@ public void setVersion(String version) { */ public void setSdk(String sdk) { this.sdk = sdk; - updateJavaFXDependencies(); + updateCustomSDKArtifactRepository(); } public String getSdk() { @@ -113,7 +152,12 @@ public void setConfiguration(String configuration) { */ public void setConfigurations(String[] configurations) { this.configurations = configurations; - updateJavaFXDependencies(); + for (String conf : configurations) { + if (!seenConfigurations.contains(conf)) { + declareFXDependencies(conf); + seenConfigurations.add(conf); + } + } } public String getConfiguration() { @@ -130,40 +174,44 @@ public List getModules() { public void setModules(List modules) { this.modules = modules; - updateJavaFXDependencies(); } public void modules(String...moduleNames) { setModules(List.of(moduleNames)); } - private void updateJavaFXDependencies() { - clearJavaFXDependencies(); - - String[] configurations = getConfigurations(); - for (String conf : configurations) { + private void declareFXDependencies(String conf) { + // Use 'withDependencies' to declare the dependencies late (i.e., right before dependency resolution starts). + // This allows users to make multiple modifications to the 'configurations' list at arbitrary times during + // build configuration. + getConfigurationContainer().getByName(conf).withDependencies(dependencySet -> { + if (!List.of(configurations).contains(conf)) { + // configuration was removed: do nothing + return; + } JavaFXModule.getJavaFXModules(this.modules).stream() .sorted() .forEach(javaFXModule -> { if (customSDKArtifactRepository != null) { - project.getDependencies().add(conf, Map.of("name", javaFXModule.getModuleName())); + dependencySet.add(getDependencies().create(Map.of("name", javaFXModule.getModuleName()))); } else { - project.getDependencies().add(conf, - String.format("%s:%s:%s:%s", MAVEN_JAVAFX_ARTIFACT_GROUP_ID, javaFXModule.getArtifactName(), - getVersion(), getPlatform().getClassifier())); + dependencySet.add(getDependencies().create( + MAVEN_JAVAFX_ARTIFACT_GROUP_ID + ":" + + javaFXModule.getArtifactName() + ":" + + getVersion() + )); } }); - } - lastUpdatedConfigurations = configurations; + }); } - private void clearJavaFXDependencies() { + private void updateCustomSDKArtifactRepository() { if (customSDKArtifactRepository != null) { - project.getRepositories().remove(customSDKArtifactRepository); + getRepositories().remove(customSDKArtifactRepository); customSDKArtifactRepository = null; } - if (sdk != null && ! sdk.isEmpty()) { + if (sdk != null && !sdk.isEmpty()) { Map dirs = new HashMap<>(); dirs.put("name", "customSDKArtifactRepository"); if (sdk.endsWith(File.separator)) { @@ -171,23 +219,7 @@ private void clearJavaFXDependencies() { } else { dirs.put("dirs", sdk + File.separator + JAVAFX_SDK_LIB_FOLDER); } - customSDKArtifactRepository = project.getRepositories().flatDir(dirs); - } - - if (lastUpdatedConfigurations == null) { - return; - } - - for (String conf : lastUpdatedConfigurations) { - var configuration = project.getConfigurations().findByName(conf); - if (configuration != null) { - if (customSDKArtifactRepository != null) { - configuration.getDependencies() - .removeIf(dependency -> dependency.getName().startsWith(PREFIX_MODULE)); - } - configuration.getDependencies() - .removeIf(dependency -> MAVEN_JAVAFX_ARTIFACT_GROUP_ID.equals(dependency.getGroup())); - } + customSDKArtifactRepository = getRepositories().flatDir(dirs); } } } diff --git a/src/main/java/org/openjfx/gradle/JavaFXPlatform.java b/src/main/java/org/openjfx/gradle/JavaFXPlatform.java index 2b33ba3..a424a43 100644 --- a/src/main/java/org/openjfx/gradle/JavaFXPlatform.java +++ b/src/main/java/org/openjfx/gradle/JavaFXPlatform.java @@ -31,34 +31,47 @@ import com.google.gradle.osdetector.OsDetector; import org.gradle.api.GradleException; -import org.gradle.api.Project; +import org.gradle.nativeplatform.MachineArchitecture; +import org.gradle.nativeplatform.OperatingSystemFamily; import java.util.Arrays; import java.util.stream.Collectors; public enum JavaFXPlatform { - LINUX("linux", "linux-x86_64"), - LINUX_AARCH64("linux-aarch64", "linux-aarch_64"), - WINDOWS("win", "windows-x86_64"), - OSX("mac", "osx-x86_64"), - OSX_AARCH64("mac-aarch64", "osx-aarch_64"); + LINUX("linux", "linux-x86_64", OperatingSystemFamily.LINUX, MachineArchitecture.X86_64), + LINUX_AARCH64("linux-aarch64", "linux-aarch_64", OperatingSystemFamily.LINUX, MachineArchitecture.ARM64), + WINDOWS("win", "windows-x86_64", OperatingSystemFamily.WINDOWS, MachineArchitecture.X86_64), + OSX("mac", "osx-x86_64", OperatingSystemFamily.MACOS, MachineArchitecture.X86_64), + OSX_AARCH64("mac-aarch64", "osx-aarch_64", OperatingSystemFamily.MACOS, MachineArchitecture.ARM64); private final String classifier; private final String osDetectorClassifier; + private final String osFamily; + private final String arch; - JavaFXPlatform(String classifier, String osDetectorClassifier) { + JavaFXPlatform(String classifier, String osDetectorClassifier, String osFamily, String arch) { this.classifier = classifier; this.osDetectorClassifier = osDetectorClassifier; + this.osFamily = osFamily; + this.arch = arch; } public String getClassifier() { return classifier; } - public static JavaFXPlatform detect(Project project) { + public String getOsFamily() { + return osFamily; + } + + public String getArch() { + return arch; + } + + public static JavaFXPlatform detect(OsDetector osDetector) { - final String osClassifier = project.getExtensions().getByType(OsDetector.class).getClassifier(); + final String osClassifier = osDetector.getClassifier(); for (JavaFXPlatform platform: values()) { if (platform.osDetectorClassifier.equals(osClassifier)) { diff --git a/src/main/java/org/openjfx/gradle/JavaFXPlugin.java b/src/main/java/org/openjfx/gradle/JavaFXPlugin.java index 2b5e59d..47f040c 100644 --- a/src/main/java/org/openjfx/gradle/JavaFXPlugin.java +++ b/src/main/java/org/openjfx/gradle/JavaFXPlugin.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Gluon + * Copyright (c) 2018, 2023, Gluon * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,21 +29,100 @@ */ package org.openjfx.gradle; +import com.google.gradle.osdetector.OsDetector; import com.google.gradle.osdetector.OsDetectorPlugin; +import org.gradle.api.GradleException; +import org.gradle.api.NonNullApi; import org.gradle.api.Plugin; import org.gradle.api.Project; -import org.javamodularity.moduleplugin.ModuleSystemPlugin; -import org.openjfx.gradle.tasks.ExecTask; +import org.gradle.api.file.FileCollection; +import org.gradle.api.plugins.ApplicationPlugin; +import org.gradle.api.plugins.JavaBasePlugin; +import org.gradle.api.tasks.JavaExec; +import org.gradle.api.tasks.SourceSetContainer; +import org.gradle.util.GradleVersion; +import org.openjfx.gradle.metadatarule.JavaFXComponentMetadataRule; +import java.io.File; +import java.util.Arrays; +import java.util.List; + +import static org.openjfx.gradle.JavaFXOptions.MAVEN_JAVAFX_ARTIFACT_GROUP_ID; + +@NonNullApi public class JavaFXPlugin implements Plugin { @Override public void apply(Project project) { + if (GradleVersion.current().compareTo(GradleVersion.version("6.1")) < 0) { + throw new GradleException("This plugin requires Gradle 6.1+"); + } + + // Make sure 'java-base' is applied first, which makes the 'SourceSetContainer' available. + // More concrete Java plugins that build on top of 'java-base' – like 'java-library' or 'application' – + // will be applied by the user. + project.getPlugins().apply(JavaBasePlugin.class); + + // Use 'OsDetectorPlugin' to select the platform the build runs on as default. project.getPlugins().apply(OsDetectorPlugin.class); - project.getPlugins().apply(ModuleSystemPlugin.class); - project.getExtensions().create("javafx", JavaFXOptions.class, project); + JavaFXOptions javaFXOptions = project.getExtensions().create("javafx", JavaFXOptions.class, + project.getExtensions().getByType(SourceSetContainer.class), + project.getExtensions().getByType(OsDetector.class)); + + // Register 'JavaFXComponentMetadataRule' to add variant information to all JavaFX modules. + // Future JavaFX versions could publish this information using Gradle Metadata. + for (JavaFXModule javaFXModule: JavaFXModule.values()) { + project.getDependencies().getComponents().withModule( + MAVEN_JAVAFX_ARTIFACT_GROUP_ID + ":" + javaFXModule.getArtifactName(), + JavaFXComponentMetadataRule.class); + } + + // Only set the default 'configuration' to 'implementation' when the 'java' plugin is applied. + // Otherwise, it won't exist. (Note: 'java' is implicitly applied by 'application' or 'java-library' + // and other Java-base plugins like Kotlin JVM) + project.getPlugins().withId("java", p -> javaFXOptions.setConfiguration("implementation")); + + // Only do addition configuration of the ':run' task when the 'application' plugin is applied. + // Otherwise, that task does not exist. + project.getPlugins().withId("application", p -> { + project.getTasks().named(ApplicationPlugin.TASK_RUN_NAME, JavaExec.class, execTask -> { + if (GradleVersion.current().compareTo(GradleVersion.version("6.4")) >= 0 && execTask.getMainModule().isPresent()) { + return; // a module, as determined by Gradle core + } + + execTask.doFirst(a -> { + if (execTask.getExtensions().findByName("moduleOptions") != null) { + return; // a module, as determined by modularity plugin + } + + putJavaFXJarsOnModulePathForClasspathApplication(execTask, javaFXOptions); + }); + }); + }); + } + + /** + * Gradle does currently not put anything on the --module-path if the application itself is executed from + * the classpath. Hence, this patches the setup of Gradle's standard ':run' task to move all JavaFX Jars + * from '-classpath' to '-module-path'. This functionality is only relevant for NON-MODULAR apps. + */ + private static void putJavaFXJarsOnModulePathForClasspathApplication(JavaExec execTask, JavaFXOptions javaFXOptions) { + FileCollection classpath = execTask.getClasspath(); + + execTask.setClasspath(classpath.filter(jar -> !isJavaFXJar(jar, javaFXOptions.getPlatform()))); + var modulePath = classpath.filter(jar -> isJavaFXJar(jar, javaFXOptions.getPlatform())); + + execTask.getJvmArgumentProviders().add(() -> List.of( + "--module-path", modulePath.getAsPath(), + "--add-modules", String.join(",", javaFXOptions.getModules()) + )); + } - project.getTasks().create("configJavafxRun", ExecTask.class, project); + private static boolean isJavaFXJar(File jar, JavaFXPlatform platform) { + return jar.isFile() && + Arrays.stream(JavaFXModule.values()).anyMatch(javaFXModule -> + javaFXModule.compareJarFileName(platform, jar.getName()) || + javaFXModule.getModuleJarFileName().equals(jar.getName())); } } diff --git a/src/main/java/org/openjfx/gradle/metadatarule/JavaFXComponentMetadataRule.java b/src/main/java/org/openjfx/gradle/metadatarule/JavaFXComponentMetadataRule.java new file mode 100644 index 0000000..f69dc99 --- /dev/null +++ b/src/main/java/org/openjfx/gradle/metadatarule/JavaFXComponentMetadataRule.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2023, Gluon + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.openjfx.gradle.metadatarule; + +import org.gradle.api.artifacts.CacheableRule; +import org.gradle.api.artifacts.ComponentMetadataContext; +import org.gradle.api.artifacts.ComponentMetadataDetails; +import org.gradle.api.artifacts.ComponentMetadataRule; +import org.gradle.api.model.ObjectFactory; +import org.gradle.nativeplatform.MachineArchitecture; +import org.gradle.nativeplatform.OperatingSystemFamily; +import org.openjfx.gradle.JavaFXPlatform; + +import javax.inject.Inject; + +import static org.gradle.nativeplatform.MachineArchitecture.ARCHITECTURE_ATTRIBUTE; +import static org.gradle.nativeplatform.OperatingSystemFamily.OPERATING_SYSTEM_ATTRIBUTE; + +/** + * + * Component Metadata RulesAdding variants for native jars + * + */ +@CacheableRule +abstract public class JavaFXComponentMetadataRule implements ComponentMetadataRule { + + @Inject + abstract protected ObjectFactory getObjects(); + + @Override + public void execute(ComponentMetadataContext context) { + var details = context.getDetails(); + + for (JavaFXPlatform javaFXPlatform : JavaFXPlatform.values()) { + addJavaFXPlatformVariant(javaFXPlatform, details, "Compile", "compile"); + addJavaFXPlatformVariant(javaFXPlatform, details, "Runtime", "runtime"); + } + } + + private void addJavaFXPlatformVariant(JavaFXPlatform javaFXPlatform, ComponentMetadataDetails details, String nameSuffix, String baseVariant) { + var name = details.getId().getName(); + var version = details.getId().getVersion(); + + // Use 'maybeAddVariant'. As long as the metadata is sourced from POM, 'compile' and 'runtime' exist. + // These are used as base for the additional variants so that those variants have the same dependencies. + // If future JavaFX versions should publish the variants directly with Gradle metadata, the rule will + // have no effect on these future versions, as the variants will be named different. + details.maybeAddVariant(javaFXPlatform.getClassifier() + nameSuffix, baseVariant, variant -> { + variant.attributes(attributes -> { + attributes.attribute(OPERATING_SYSTEM_ATTRIBUTE, getObjects().named(OperatingSystemFamily.class, javaFXPlatform.getOsFamily())); + attributes.attribute(ARCHITECTURE_ATTRIBUTE, getObjects().named(MachineArchitecture.class, javaFXPlatform.getArch())); + }); + variant.withFiles(files -> { + files.removeAllFiles(); + files.addFile(name + "-" + version + "-" + javaFXPlatform.getClassifier() + ".jar"); + }); + }); + } +} diff --git a/src/main/java/org/openjfx/gradle/tasks/ExecTask.java b/src/main/java/org/openjfx/gradle/tasks/ExecTask.java deleted file mode 100644 index 6b31f84..0000000 --- a/src/main/java/org/openjfx/gradle/tasks/ExecTask.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (c) 2019, 2021, Gluon - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * * Neither the name of the copyright holder nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package org.openjfx.gradle.tasks; - -import org.gradle.api.DefaultTask; -import org.gradle.api.GradleException; -import org.gradle.api.Project; -import org.gradle.api.file.FileCollection; -import org.gradle.api.logging.Logger; -import org.gradle.api.logging.Logging; -import org.gradle.api.plugins.ApplicationPlugin; -import org.gradle.api.tasks.JavaExec; -import org.gradle.api.tasks.TaskAction; -import org.javamodularity.moduleplugin.extensions.RunModuleOptions; -import org.openjfx.gradle.JavaFXModule; -import org.openjfx.gradle.JavaFXOptions; -import org.openjfx.gradle.JavaFXPlatform; - -import javax.inject.Inject; -import java.io.File; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.TreeSet; - -public class ExecTask extends DefaultTask { - - private static final Logger LOGGER = Logging.getLogger(ExecTask.class); - - private final Project project; - private JavaExec execTask; - - @Inject - public ExecTask(Project project) { - this.project = project; - project.getPluginManager().withPlugin(ApplicationPlugin.APPLICATION_PLUGIN_NAME, e -> { - execTask = (JavaExec) project.getTasks().findByName(ApplicationPlugin.TASK_RUN_NAME); - if (execTask != null) { - execTask.dependsOn(this); - } else { - throw new GradleException("Run task not found."); - } - }); - } - - @TaskAction - public void action() { - if (execTask != null) { - JavaFXOptions javaFXOptions = project.getExtensions().getByType(JavaFXOptions.class); - JavaFXModule.validateModules(javaFXOptions.getModules()); - - var definedJavaFXModuleNames = new TreeSet<>(javaFXOptions.getModules()); - if (!definedJavaFXModuleNames.isEmpty()) { - RunModuleOptions moduleOptions = execTask.getExtensions().findByType(RunModuleOptions.class); - - final FileCollection classpathWithoutJavaFXJars = execTask.getClasspath().filter( - jar -> Arrays.stream(JavaFXModule.values()).noneMatch(javaFXModule -> jar.getName().contains(javaFXModule.getArtifactName())) - ); - final FileCollection javaFXPlatformJars = execTask.getClasspath().filter(jar -> isJavaFXJar(jar, javaFXOptions.getPlatform())); - - if (moduleOptions != null) { - LOGGER.info("Modular JavaFX application found"); - // Remove empty JavaFX jars from classpath - execTask.setClasspath(classpathWithoutJavaFXJars.plus(javaFXPlatformJars)); - definedJavaFXModuleNames.forEach(javaFXModule -> moduleOptions.getAddModules().add(javaFXModule)); - } else { - LOGGER.info("Non-modular JavaFX application found"); - // Remove all JavaFX jars from classpath - execTask.setClasspath(classpathWithoutJavaFXJars); - - var javaFXModuleJvmArgs = List.of("--module-path", javaFXPlatformJars.getAsPath()); - - var jvmArgs = new ArrayList(); - jvmArgs.add("--add-modules"); - jvmArgs.add(String.join(",", definedJavaFXModuleNames)); - - List execJvmArgs = execTask.getJvmArgs(); - if (execJvmArgs != null) { - jvmArgs.addAll(execJvmArgs); - } - jvmArgs.addAll(javaFXModuleJvmArgs); - - execTask.setJvmArgs(jvmArgs); - } - } - } else { - throw new GradleException("Run task not found. Please, make sure the Application plugin is applied"); - } - } - - private static boolean isJavaFXJar(File jar, JavaFXPlatform platform) { - return jar.isFile() && - Arrays.stream(JavaFXModule.values()).anyMatch(javaFXModule -> - javaFXModule.compareJarFileName(platform, jar.getName()) || - javaFXModule.getModuleJarFileName().equals(jar.getName())); - } -} diff --git a/src/test/java/org/openjfx/gradle/JavaFXModuleTest.java b/src/test/java/org/openjfx/gradle/JavaFXModuleTest.java index 44517fe..4bc58bc 100644 --- a/src/test/java/org/openjfx/gradle/JavaFXModuleTest.java +++ b/src/test/java/org/openjfx/gradle/JavaFXModuleTest.java @@ -63,17 +63,6 @@ void getArtifactName() { assertEquals("javafx-base", JavaFXModule.BASE.getArtifactName()); } - @Test - void getDependencies() { - JavaFXModule module = JavaFXModule.CONTROLS; - - List dependencies = module.getMavenDependencies(); - assertTrue(dependencies.contains(module)); - for (JavaFXModule dependentModule : JavaFXModule.CONTROLS.getDependentModules()) { - assertTrue(dependencies.contains(dependentModule)); - } - } - @Test void validateWithValidModules() { var moduleNames = List.of(JavaFXModule.CONTROLS.getModuleName(), JavaFXModule.WEB.getModuleName()); diff --git a/src/test/java/org/openjfx/gradle/JavaFXPluginSmokeTest.java b/src/test/java/org/openjfx/gradle/JavaFXPluginSmokeTest.java index 5eb83e2..a94d96f 100644 --- a/src/test/java/org/openjfx/gradle/JavaFXPluginSmokeTest.java +++ b/src/test/java/org/openjfx/gradle/JavaFXPluginSmokeTest.java @@ -130,6 +130,8 @@ private static List> runtimeClassPath(BuildResult result) { } private static List> path(BuildResult result, String pathArg) { + // Parse classpath or module path from Gradle's '--debug' output. + // The :compileJava and :run tasks log them on that logging level. return result.getOutput().lines().filter(l -> l.contains(pathArg)).map(l -> { int pathArgIndex = l.indexOf(pathArg) + pathArg.length(); String pathString = l.substring(pathArgIndex, l.indexOf(" ", pathArgIndex)); diff --git a/src/test/java/org/openjfx/gradle/JavaFXPluginSmokeTestGradle61.java b/src/test/java/org/openjfx/gradle/JavaFXPluginSmokeTestGradle61.java index 00d869b..c8e4cb5 100644 --- a/src/test/java/org/openjfx/gradle/JavaFXPluginSmokeTestGradle61.java +++ b/src/test/java/org/openjfx/gradle/JavaFXPluginSmokeTestGradle61.java @@ -45,6 +45,6 @@ protected String modularApplicationRuntime() { @Disabled void smokeTestModular() { - // Support for build Java Modules was added in Gradle 6.4 + // Support for building Java Modules was only added in Gradle 6.4 } }