...
Cross Builds
The build is configured to support cross builds, that is, the ability to build an SDK for a platform other than the one you are building from. There are multiple gradle files located in buildSrc which represent specific compile targets. These include:
- win.gradle
- mac.gradle
- linux.gradle
- android.gradle
- ios.gradle
- armv6sf.gradle
- armv6hf.gradle
Each of these have specific prerequisites that must be met before they can be built. win.gradle can only be used on Windows, mac.gradle on Mac, and linux.gradle on Linux. Android can be cross built from Mac, Windows, or Linux so long as the Android SDK and NDK are installed and the build knows where to find them. iOS can be cross built on Mac. ARM (soft float and hard float) can be cross built from Linux.
By default, the OpenJFX build system will only build the SDK for the desktop platform you are building from. To ask it to build for a specific compile target, you must pass a COMPILE_TARGETS property to the build system, instructing it which to build. This is a comma separated list. Assuming you have already setup the prerequisites for building ARM (for example, when targeting the Raspberry PI), you would invoke gradle like this:
Code Block | ||||
---|---|---|---|---|
| ||||
rbair$ gradle -PCOMPILE_TARGETS=armv6hf |
Anchor | ||||
---|---|---|---|---|
|
The build can be customized fairly extensively through the use of Gradle properties. Gradle provides many ways to supply properties to the build system. However the most common approach will be to use a gradle.properties file located in the rt directory. Simply make a copy of gradle.properties.template and then edit the resulting gradle.properties file to customize your build.
Code Block | ||||
---|---|---|---|---|
| ||||
rbair$ cp gradle.properties.template gradle.properties |
The gradle.properties file that you have just created is heavily documented and contains information on all the different configuration options at your disposal. Some of the most common are:
- Enabling building of native source code (Prism, Glass, GStreamer, WebKit, etc)
- Specifying the build configuration (Release or Debug)
- Enabling building of JavaDoc
- Customizing the JDK_HOME
- Supplying compiler LINT options
Arguably the most important property in the build is the JDK_HOME property. Almost all other properties are derived automatically from this one. The JDK_HOME is by default based on the java.home System property, which is set automatically by the JVM based on which version of Java is executed. Typically, then, the version of Java you will be using to compile with will be the version of Java you have setup on your path. You can of course specify the JDK_HOME yourself. Note also that on Windows, the version of the JDK you have set as JDK_HOME will determine whether you build 32 or 64 bit binaries.
Testing
The next basic task which you may want to perform is test. The test task will execute the unit tests. You generally will execute the top level test because unlike with Ant, Gradle will only re-execute those tests which have changed (or were dependent on code that was changed) on subsequent runs. You can of course execute gradle cleanTest in order to clean all the test results so they will run fresh. Or, if you want to execute only those tests related to a single project, you can do so in the normal fashion:
Code Block | ||||
---|---|---|---|---|
| ||||
rbair$ gradle :base:test The CompileOptions.useAnt property has been deprecated and is scheduled to be removed in Gradle 2.0. There is no replacement for this property. :base:processVersion UP-TO-DATE :build-tools:generateGrammarSource UP-TO-DATE :build-tools:compileJava UP-TO-DATE :build-tools:processResources UP-TO-DATE :build-tools:classes UP-TO-DATE :build-tools:jar UP-TO-DATE :base:compileJava UP-TO-DATE :base:processResources UP-TO-DATE :base:classes UP-TO-DATE :base:compileTestJava UP-TO-DATE :base:processTestResources UP-TO-DATE :base:testClasses UP-TO-DATE > Building > :base:test > 3411 tests completed, 45 skipped |
Gradle gives helpful output during execution of the number of tests completed and the number skipped without dumping out lots of output to the console (unless you opt for --info). Also, once the tests complete, an HTML report is dumped to the project's build/reports/test directory (for example, modules/base/build/reports/test):
For the sake of performance, most of the tests are configured to run in the same VM. However some tests by design cannot be run in the same VM, and others cannot yet run in the same VM due to bugs or issues in the test. In order to improve the quality of the project we need to run as many tests as possible in the same VM. The more tests we can run on pre-integration the less likely we are to see failures leak into master. Being able to run 20,000 tests in a minute is extremely useful, but not possible, unless they run in the same VM. Something to keep in mind.
Overlay - JDK 8
After a successful build, the final step could be copying the results, overlaying them over an existing JDK. This step will replace any JavaFX/OpenJFX binaries in that runtime with your newly built binaries.
First execute the gradle task which creates an overlay bundle.
Code Block | ||
---|---|---|
| ||
rbair$ gradle [-PCOMPILE_TARGETS=armv6hf] zips # If you are cross compiling you will need the COMPILE_TARGETS flag # with the appropriate target # -PCOMPILE_TARGETS=armv6hf |
This will create a zip bundle containing the OpenJFX binaries and is designed to be extracted into a JDK or JRE.
The created bundle is located in build/[platform-]bundles/javafx-sdk-overlay.zip. [platform-] will only be present for a cross build, and will be the name of the cross platform (for example armv6hf-bundles).
Please note that there might be some differences in the contents of the runtime found in build/[platform-]sdk/ and the contents of the bundles. The overlay bundle is designed to match JavaFX in a production JDK.
Within a JDK there is a directory that contains the Java Runtime Environment, "./jre". The zip bundle is designed to be extracted in the directory that contains "./jre".
As zip bundles do not always preserve permissions, sometimes it is necessary to modify the file permissions to match the others in the JRE. In particular, check the permissions on the extracted native libraries.
Sandbox Testing with JDK9
Using the results of a modular JDK9 OpenJFX build is quite simple. A "run" args file can be used to point to the overriding modules that are in your build. (args file support for java was added in JDK9) The file build/run.args and build/compile.args are created during the FX build process. The run.args file contains full paths to the overriding modules and shared libraries, and so must be recreated if you are using a copied or downloaded module set (for example from a nightly build). A script is provided that will recreate the xpatch.args file in the current directory:
bash tools/scripts/make_runargs.sh /absolute_path_to/modular-sdk
The following can be used to set up an alias that can be used to launch a JFX application, but using the FX binaries from your development tree. This alias will override the modules built into JDK9.
export JIGSAW_HOME="path_to_top_of_JDK9"
export JFX_BUILD="path_to_top_of_your_repo"
export JFX_PATCH=$JFX_BUILD/build/run.args (or the path to one created by make_runargs.sh)
alias javafx='$JIGSAW_HOME/bin/java @$JFX_PATCH'
This alias uses the @argfile mechanism to include all that Xpatch/java.library.path verbosity to create a single command to run FX backed by your recently built binaries.
In Windows, the paths for the alias can be a bit tricky to get right, as the JDK wants native Windows paths, and cygwin often works better with a Unix path. Here is an example that works with Cygwin:
export JIGSAW_HOME=`cygpath -m "/cygdrive/c/Program Files/Java/jdk-9/"`
export JFX_PATCH=`cygpath -m "$JFX_BUILD/build/run.args"`
alias javafx='"$JIGSAW_HOME/bin/java" @$JFX_PATCH'
Integration with OpenJDK 9
With the module system in JDK 9, it is not possible to easily overlay an OpenJFX build over an existing JDK as was possible with JDK 8. It is possible to build an OpenJDK that included the updated OpenJFX modules.
To create an integrated OpenJDK 9 with OpenJFX requires two builds:
- OpenJFX for JDK 9
- OpenJDK 9, with a configure reference that includes your OpenJFX build.
The steps for building the OpenJDK are the same as for JDK 8. Use the repository path http://hg.openjdk.java.net/jdk9/dev.
Build OpenJFX first.
Configure the JDK with the following addition:
--with-import-modules=_path_to_openjfx/9-dev/build/modular-sdk
Then build the JDK as normal.
Understanding a JDK 9 Modular world in our developer build
The export of module packages is governed by two sets of files:
- module-info.java, the per module declarations
- module-info.java.extra, fragments of declarations used to augment standard JDK module-info.
During the build process, we generate some files for use by the build, and also by developers working in the sandbox.
- runs.args: for use with the java command, overrides as much as possible the FX modules in the JDK
- must use absolute system paths internally, so cannot be easily copied without editing
- can be rebuilt with tools/scripts/make_runargs.sh
- cannot override any changes in module-info
- does not "re" grant any privileges that our default FX modules have with a security manager
- compile.args: arguments to allow for compile using the sandbox libraries
- cannot override any changes in module-info, so added packages may need an "--add-exports to be seen.
- run.java.policy: a minimum permissions file to use with the security manager.
- intended primarily as a base to start with before adding test specific permissions.
Each of these files has a "test" variant, for example "testrun.args". These files are altered to add in the "shims" version of the module. Note that the build/shims is not populated by the "sdk" task. Use the "copyGeneratedShims" or "test" task.
When dealing primarily with unit tests, addition arguments are needed to access non public API from within the unit tests. These additional arguments have been placed in "addExports" that are local to the tests that need them. For example, "modules/javafx.graphics/src/test/addExports" contains all of the "--add-exports" clauses required to compile and run all of the graphics module junit tests. Care should be taken when modifying these files, as additions may mean that package module-info may need updates too. Keep in mind - if you are adding an "--add-exports" to ALL-UNNAMED so that a junit test can see the API, then the addExports the right place. If you are trying to fix access by another module, it likely is the wrong place.
Adding new packages in a modular world
JDK 9 Modules add complexity to the development chain, but particularly when adding new API and especially packages. Adding a new package or changing package visibility will be a multi step task that will require at least two change sets to implement.
Our developer sandbox build uses several items to work around module export during build and testing that you should be familiar with.
Create a "followup JBS" to cover the cleanup/removal of module access workarounds. Be sure to link this new followup JBS to the one you started with.
First Step - development
Modify affected modules module-info to reflect the proposed changes. These changes will only directly affect the current build java compile process. It is key to remember that the java runtime will ignore any changes to module-info, even while it uses "--patch-module".
The next modify all of the buildSrc/addExport files in the build for the modules that need to access the new module package. These files, like "modules/javafx.controls/src/test/addExports" are passed into the running of the unit tests. Note that these changes will be part of your commit changeset. to mirror changes that were made in the module-info files. Mark any additions with a comment containing the "Completion JBS" number, like this:
# to be removed by 81XXXXX
--add-exports=javafx.graphics/com.sun.javafx.newpackage=ALL-UNNAMEDjavafx.controls
Complete development of your new package and adding unit test coverage, and all of the other process we normally do.
Note, if you add a junit test that used the new package, you will likely also need an export to ALL-UNNAMED (which the junit jar is a member of). The result may be two exports in buildSrc/addExport to add the temporary workarounds required for both development and test.
Your complete change set will now contain all of the delta required for the nightly build and test your changes. The promotion process will soon merge your module-info changes into the JDK. Once there is a promoted JDK that has the new module-info changes, it is possible to move to the second step.
One consideration - building a local development copy of the JDK is not difficult. In some cases, it may be useful to create a local developer JDK that incorporates the module-info changes, even before development of the changeset it complete. This developer JDK will honor the new package exports without the need of the changes to the addExport files. Note however, your change set may break the build if it has not be tested with the current minimum promoted JDK build.
Second Step - cleanup
Once the changes are promoted into a JDK, the second step to remove the addExports workarounds can be scheduled with the team lead.
As both the build machine and the other developers will need to update to the newer JDK build, this step will need to be coordinated.
Create a change set with:
- the now unneeded addExport lines removed
- the minimum JDK version used by the build has been updated to the new minimum
Test, review and commit as normal.