Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JavaFX Incubator Modules #1375

Draft
wants to merge 33 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
d19de4d
WIP: 8309381: Create JavaFX incubator module
kevinrushforth Nov 17, 2023
75e99c0
eclipse config
andy-goryachev-oracle Nov 17, 2023
20098e1
Merge branch 'master'
kevinrushforth Feb 20, 2024
0cb3c6b
Change module name to javafx.incubator.myfeature
kevinrushforth Feb 20, 2024
0e6d56b
Rename package to javafx.incubator.scene.mypkg
kevinrushforth Feb 20, 2024
cd63979
Create INCUBATOR-MODULES.md doc
kevinrushforth Feb 20, 2024
0966c55
Fix typos
kevinrushforth Feb 21, 2024
21d7a7c
Merge remote-tracking branch 'upstream/master' into javafx.incubator
kevinrushforth Feb 29, 2024
02a0b49
Change javafx.incubator to jfx.incubator
kevinrushforth Feb 22, 2024
ee76f09
Say that a JEP is needed to finalize or drop an incubating feature
kevinrushforth Feb 22, 2024
a61f395
Clarify that a feature should not incubate indefinitely
kevinrushforth Feb 23, 2024
acfecc2
Change module name to jfx.incubator.myfeature
kevinrushforth Feb 23, 2024
136776c
Rename package to jfx.incubator.scene.mypkg
kevinrushforth Feb 23, 2024
93ef93e
Bump copyright date
kevinrushforth Feb 23, 2024
a4bf721
Additional comments
kevinrushforth Feb 23, 2024
691d52b
WIP: Utility for printing incubator warning
kevinrushforth Feb 24, 2024
d3bf81e
fixup comments in helper class
kevinrushforth Mar 1, 2024
6b8b571
fix typo
kevinrushforth Mar 1, 2024
1de2efe
add missing javadoc
kevinrushforth Mar 1, 2024
97575a1
Update instructions to create a new incubator module
kevinrushforth Mar 1, 2024
8e04e95
Merge remote-tracking branch 'upstream/master' into javafx.incubator.dev
kevinrushforth May 1, 2024
75d0c07
Add missing dependency on incubator module to systemTests so
kevinrushforth May 1, 2024
ccc5c77
Merge branch 'master' into javafx.incubator.merge
kevinrushforth Jun 13, 2024
1fd34b0
Minor formatting update
kevinrushforth May 10, 2024
5626175
Cleanup prior to preparing RFE for incubator dependencies
kevinrushforth May 10, 2024
bb46c46
Add some implementation notes
kevinrushforth May 13, 2024
dd5f351
Merge branch 'master' into javafx.incubator.dev
kevinrushforth Jul 25, 2024
fa50d1e
Fixup javafx.* assumptions
kevinrushforth Jul 25, 2024
b6631ca
8337281: build.gradle assumes all modules are named "javafx.$project"
kevinrushforth Jul 26, 2024
7d25dd8
Remove debug prints
kevinrushforth Jul 26, 2024
32e3e75
Remove BUG comments and debug prints
kevinrushforth Jul 26, 2024
50d9012
Fix bug where String was being used as if it were a Project
kevinrushforth Jul 26, 2024
4bb1b59
Merge branch '8337281-module-name' into javafx.incubator.dev
kevinrushforth Jul 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions INCUBATOR-MODULES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# JavaFX Incubator Modules

## Overview

In [JEP 11](https://openjdk.org/jeps/11), the JDK provides incubator modules as a means of putting non-final API in the hands of developers, while the API progresses towards either finalization or removal in a future release.

Similarly, some JavaFX APIs would benefit from spending a period of time in a JavaFX release prior to being deemed stable. Being in the mainline `jfx` repository, and thus in downstream binaries such as those at jdk.java.net, makes it easier for interested parties outside of the immediate OpenJDK Community to use the new feature. Experience gained and fed back through the usual channels such as blogs, mailing lists, outreach programs, and conferences can then be acted upon before finalizing, or else removing, the feature in a future release.

This is especially useful for complex features with a large API surface. Such features are nearly impossible to get right the first time, even after an extensive review. Using an incubator module will allow the API to evolve in future releases without the strict compatibility constraints that core JavaFX modules have.

## Description

An incubating feature is an API of non-trivial size, that is under development for eventual inclusion in the core set of JavaFX APIs. The API is not yet sufficiently proven, so it is desirable to defer finalization for a small number of feature releases in order to gain additional experience and feedback.

See [JEP 11](https://openjdk.org/jeps/11) for a description of incubator modules.

JavaFX incubator modules have a few differences from JDK incubator modules:

- A JavaFX incubator module is identified by the `jfx.incubator.` prefix in its module name.
- A JavaFX incubating API is identified by the `jfx.incubator.` prefix in its exported package names. An incubating API is exported only by an incubator module.
- Incubator modules must export incubating APIs only, i.e., packages in the `jfx.incubator` namespace. Consequently:
- JavaFX incubator modules must not export core JavaFX APIs in the `javafx.` namespace. This distinguishes incubator modules from core modules such as `javafx.base`.
- JavaFX non-incubator modules must not specify `requires transitive` dependences upon incubator modules, or otherwise expose types exported from incubator modules in their own exported APIs. In exceptional cases, it may be acceptable for non-incubator modules to specify `requires` dependences (as opposed to `requires transitive`) upon incubator modules.
- JavaFX incubator modules can specify requires or requires transitive dependences upon other incubator modules.
- A warning must be issued when first loading a class from a publicly exported package in a JavaFX incubator module, even if the module is not jlinked into the JDK. We will provide a utility method in `javafx.base` to facilitate this.
- To either make a JavaFX incubating API final, or to remove it, a new JEP should be submitted, referencing the original incubator JEP.
- By default, a JavaFX feature that is delivered in an incubator module will re-incubate in subsequent versions (the default in the JDK is to drop the feature). If any changes are needed to the API, they will be done with new JBS enhancement along with an associated CSR. However, this is not intended to suggest the possibility of a permanently incubating feature. As with incubating features in the JDK, if an incubating API is not promoted to final status after a reasonably small number of JavaFX feature releases, then it will be dropped: its packages and incubator module will be removed. As a guideline:
- An incubating API that was not updated in the current shipping feature release and has not been updated in the feature release being developed, is either stable or is not being actively developed. Such an API should either be finalized or dropped.
- An incubating API that spans beyond a 24-month period (4 feature releases), and is not yet ready to be finalized, will need explicit approval from a Project Lead to remain incubating for some additional period at the discretion of a Project Lead. Otherwise, a Project Lead will submit a removal JEP.
- The submitter of the original JEP can propose to remove it at any time.

## How to add a new incubator module

Use [this patch](https://github.com/openjdk/jfx/pull/1375.diff) as a starting point for your incubator module. Then do the following:
- Rename `modules/jfx.incubator.myfeature` to the desired name of your module, keeping the `jfx.incubator.` prefix.
- Modify `build.gradle`, `settings.gradle`, and `modules/javafx.base/src/main/java/module-info.java` to update the name of your module. Look for comments of the form `// TODO: incubator template` for where to make the changes.
- Develop your module as you would with any JavaFX module, keeping in mind the rules in this JEP about public exports and dependencies.

FIXME: find a permanent home for the incubator module template patch, possibly in the jfx-sandbox repo.
37 changes: 37 additions & 0 deletions NOTES-INCUBATOR.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# NOTES: JavaFX Incubator Modules

## Overview

These are notes regarding the implementation of JavaFX incubator modules.

## Changes / additions in the javafx.incubator branch

The `javafx.incubator` branch has three main types of changes:

1. The [JavaFX Incubator Modules JEP](INCUBATOR-MODULES.md) and these implementation notes.
2. Changes needed to support incubator modules. These include: build scripts (`build.gradle`, `settings.gradle`), qualified exports from `javafx.base`, and a utility class to produce warnings when first using an incubator module.
3. A sample incubator module, `jfx.incubator.myfeature`.

Note that this branch is meant to be illustrative in nature. It will _never_ be integrated, so the PR based on this branch will remain in Draft state as long as the PR is open (it will _never_ become `rfr`).

## RFE: Support incubator modules

The changes reflected in item 2 in the previous section need to be integrated into mainline ahead of the first incubator module, so a separate PR will be made from a branch that has just those changes. In support of this, the needed changes have been highlighted using comments of the following pattern:

```
// RFE: incubator dependency
```

These identify the blocks that are needed as part of the RFE to add the needed dependencies. The PR that includes them will remove the `RFE: incubator dependency` line, since that is only there to differentiate those changes needed to support incubator modules in general from those changes that add the specific sample incubator module (`jfx.incubator.myfeature`). The sample incubator module changes must be reverted in the branch that will be used to add the dependencies.

Some of the incubator dependency changes include comments about where to add build logic when creating an incubator module. Those comment will be part of the incubator dependency PR. For example:

```
// RFE: incubator dependency
// TODO: incubator: Add entry for each incubator module here
// BEGIN: incubator placeholder
//'incubator.mymod',
// END: incubator placeholder
```

When proposing the incubator dependency PR, the `// RFE: incubator dependency` line will be removed (since it only exists to help identify the needed dependency), but the rest of the comment block (starting with `TODO: incubator: ...`) will be part of the PR.
208 changes: 189 additions & 19 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -967,7 +967,7 @@ List<String> computeLibraryPath(boolean working) {
List<String> lp = []

if (HAS_JAVAFX_MODULES) {
List<String> modsWithNative = [ 'graphics', 'media', 'web' ]
List<String> modsWithNative = [ 'javafx.graphics', 'javafx.media', 'javafx.web' ]

// the build/modular-sdk area
def platformPrefix = ""
Expand All @@ -976,7 +976,7 @@ List<String> computeLibraryPath(boolean working) {
def modulesLibsDir = "${bundledSdkDir}/modules_libs"

modsWithNative.each() { m ->
lp << cygpath("${modulesLibsDir}/javafx.${m}")
lp << cygpath("${modulesLibsDir}/${m}")
}
} else {
def platformPrefix = ""
Expand Down Expand Up @@ -1710,6 +1710,16 @@ void addMavenPublication(Project project, List<String> projectDependencies) {
return
}

if (!project.hasProperty("moduleName")) {
fail("Project ${project} has no module name")
}
projectDependencies.each { projName ->
def dep = project.project(":$projName")
if (!dep.hasProperty("moduleName")) {
fail("${project} dependency ${dep} has no module name")
}
}

project.apply plugin: 'maven-publish'

project.group = MAVEN_GROUP_ID
Expand Down Expand Up @@ -1760,7 +1770,8 @@ void addMavenPublication(Project project, List<String> projectDependencies) {
project.publishing {
publications {
maven(MavenPublication) {
artifactId = "javafx-${project.name}"
def artifactName = project.moduleName.replace('.', '-')
artifactId = artifactName

afterEvaluate {
artifact project.tasks."moduleEmptyPublicationJar$t.capital"
Expand All @@ -1779,15 +1790,17 @@ void addMavenPublication(Project project, List<String> projectDependencies) {

Node projectDependencyPlatform = dependencies.appendNode("dependency")
projectDependencyPlatform.appendNode("groupId", MAVEN_GROUP_ID)
projectDependencyPlatform.appendNode("artifactId", "javafx-${project.name}")
projectDependencyPlatform.appendNode("artifactId", artifactName)
projectDependencyPlatform.appendNode("version", MAVEN_VERSION)
projectDependencyPlatform.appendNode("classifier", "\${javafx.platform}")

if (!projectDependencies.empty) {
projectDependencies.each { dep ->
projectDependencies.each { projName ->
def dep = project.project(":$projName")
def depName = dep.moduleName.replace('.', '-')
Node projectDependency = dependencies.appendNode("dependency")
projectDependency.appendNode("groupId", MAVEN_GROUP_ID)
projectDependency.appendNode("artifactId", "javafx-$dep")
projectDependency.appendNode("artifactId", depName)
projectDependency.appendNode("version", MAVEN_VERSION)
}
}
Expand Down Expand Up @@ -2795,6 +2808,113 @@ project(":controls") {
addValidateSourceSets(project, sourceSets)
}

// RFE: incubator dependency
// TODO: incubator: Add a project declaration for each incubator module here
// BEGIN: incubator placeholder
//project(":incubator.mymod") {
// project.ext.buildModule = true
// project.ext.includeSources = true
// project.ext.moduleRuntime = true
// project.ext.moduleName = "jfx.incubator.mymod"
// project.ext.incubating = true
// ...
//}
// END: incubator placeholder

// TODO: incubator template -- follow instruction below, then remove this comment
// To create an incubator module:
// 1) apply the changes from this patch
// 2) Look for "TODO: incubator template" comments, and replace "myfeature"
// with the name of your feature
// 3) Refactor / rename the files under "modules/javafx.incubator.myfeature"
// to match the name of your feature.
// 4) Remove this comment block
project(":incubator.myfeature") {
project.ext.buildModule = true
project.ext.includeSources = true
project.ext.moduleRuntime = true
project.ext.moduleName = "jfx.incubator.myfeature"
project.ext.incubating = true

sourceSets {
main
shims {
java {
compileClasspath += sourceSets.main.output
runtimeClasspath += sourceSets.main.output
}
}
test {
java {
compileClasspath += sourceSets.shims.output
runtimeClasspath += sourceSets.shims.output
}
}
}

project.ext.moduleSourcePath = defaultModuleSourcePath
project.ext.moduleSourcePathShim = defaultModuleSourcePathShim

commonModuleSetup(project, [ 'base', 'graphics', 'controls', 'incubator.myfeature' ])

dependencies {
testImplementation project(":base").sourceSets.test.output
testImplementation project(":graphics").sourceSets.test.output
testImplementation project(":controls").sourceSets.test.output
implementation project(':base')
implementation project(':graphics')
implementation project(':controls')
}

test {
jvmArgs "-Djavafx.toolkit=test.com.sun.javafx.pgstub.StubToolkit"
}

def modulePath = "${project.sourceSets.main.java.getDestinationDirectory().get().getAsFile()}"
modulePath += File.pathSeparator + "${rootProject.projectDir}/modules/javafx.controls/build/classes/java/main"
modulePath += File.pathSeparator + "${rootProject.projectDir}/modules/javafx.graphics/build/classes/java/main"
modulePath += File.pathSeparator + "${rootProject.projectDir}/modules/javafx.base/build/classes/java/main"

// TODO: incubator template -- follow instruction below, then remove this comment block
// The following block is used if and only if you have .css resource files
// in your incubator module. If you do, uncomment the block and make the
// appropriate changes for the location of your resource files. Otherwise,
// delete the block.

// processResources {
// doLast {
// def cssFiles = fileTree(dir: "$moduleDir/com/sun/javafx/scene/control/skin")
// cssFiles.include "**/*.css"
// cssFiles.each { css ->
// logger.info("converting CSS to BSS ${css}");
//
// javaexec {
// executable = JAVA
// workingDir = project.projectDir
// jvmArgs += patchModuleArgs
// jvmArgs += "--module-path=$modulePath"
// jvmArgs += "--add-modules=javafx.graphics"
// mainClass = "com.sun.javafx.css.parser.Css2Bin"
// args css
// }
// }
// }
// }
//
// def copyShimBssTask = project.task("copyShimBss", type: Copy,
// dependsOn: [project.tasks.getByName("compileJava"),
// project.tasks.getByName("processResources")]) {
// from project.moduleDir
// into project.moduleShimsDir
// include "**/*.bss"
// }
// processShimsResources.dependsOn(copyShimBssTask)

addMavenPublication(project, [ 'graphics' , 'controls'])

addValidateSourceSets(project, sourceSets)
}

project(":swing") {

// We need to skip setting compiler.options.release for this module,
Expand Down Expand Up @@ -3911,7 +4031,26 @@ project(":systemTests") {
testImplementation project(":swing").sourceSets.test.output
}

def dependentProjects = [ 'base', 'graphics', 'controls', 'media', 'web', 'swing', 'fxml' ]
// RFE: incubator dependency
def dependentProjects = [
'base',
'graphics',
'controls',

// RFE: incubator dependency
// TODO: incubator: Add entry for each incubator module here
// BEGIN: incubator placeholder
//'incubator.mymod',
// END: incubator placeholder

// TODO: incubator template -- rename module, then remove this TODO comment
'incubator.myfeature',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this change fixed the jenkins build of the rich text area incubator, thank you!


'media',
'web',
'swing',
'fxml'
]
commonModuleSetup(project, dependentProjects)

File testJavaPolicyFile = new File(rootProject.buildDir, TESTJAVAPOLICYFILE);
Expand Down Expand Up @@ -4350,9 +4489,27 @@ task javadoc(type: Javadoc, dependsOn: createMSPfile) {
group = "Basic"
description = "Generates the JavaDoc for all the public API"
executable = JAVADOC
// RFE: incubator dependency
def projectsToDocument = [
project(":base"), project(":graphics"), project(":controls"), project(":media"),
project(":swing"), /*project(":swt"),*/ project(":fxml"), project(":web")]
project(":base"),
project(":graphics"),
project(":controls"),

// RFE: incubator dependency
// TODO: incubator: Add entry for each incubator module here
// BEGIN: incubator placeholder
//project(":incubator.mymod"),
// END: incubator placeholder

// TODO: incubator template -- rename module, then remove this TODO comment
project(":incubator.myfeature"),

project(":media"),
project(":swing"),
/*project(":swt"),*/
project(":fxml"),
project(":web")
]
source(projectsToDocument.collect({
[it.sourceSets.main.java]
}));
Expand Down Expand Up @@ -5675,6 +5832,8 @@ compileTargets { t ->
def modnames = []
moduleProjList.each { project ->
if (project.hasProperty("moduleName") && project.buildModule) {
// RFE: incubator dependency
def incubating = project.hasProperty("incubating") && project.ext.incubating
modnames << project.ext.moduleName
File dir;
if (project.sourceSets.hasProperty('shims')) {
Expand All @@ -5686,16 +5845,19 @@ compileTargets { t ->
def dstModuleDir = cygpath(dir.path)
modpath << "${dstModuleDir}"

String themod = dir.toURI()
testJavaPolicyFile << "grant codeBase \"${themod}\" {\n" +
" permission java.security.AllPermission;\n" +
"};\n"

dir = new File(rootProject.buildDir, "sdk/lib/${project.ext.moduleName}.jar")
themod = dir.toURI()
runJavaPolicyFile << "grant codeBase \"${themod}\" {\n" +
" permission java.security.AllPermission;\n" +
"};\n"
// RFE: incubator dependency
if (!incubating) {
String themod = dir.toURI()
testJavaPolicyFile << "grant codeBase \"${themod}\" {\n" +
" permission java.security.AllPermission;\n" +
"};\n"

dir = new File(rootProject.buildDir, "sdk/lib/${project.ext.moduleName}.jar")
themod = dir.toURI()
runJavaPolicyFile << "grant codeBase \"${themod}\" {\n" +
" permission java.security.AllPermission;\n" +
"};\n"
}
}
}

Expand Down Expand Up @@ -5741,6 +5903,9 @@ compileTargets { t ->
def jmodName = "${moduleName}.jmod"
def jmodFile = "${jmodsDir}/${jmodName}"

// RFE: incubator dependency
def incubating = project.hasProperty("incubating") && project.ext.incubating

// On Windows, copy the native libraries in the jmod image
// to a "javafx" subdir to avoid conflicting with the Microsoft
// DLLs that are shipped with the JDK
Expand Down Expand Up @@ -5782,6 +5947,11 @@ compileTargets { t ->
if (sourceDateEpoch != null) {
args("--date", extendedTimestamp)
}
// RFE: incubator dependency
if (incubating) {
args("--do-not-resolve-by-default")
args("--warn-if-resolved=incubating")
}
args(jmodFile)
}
}
Expand Down
Loading