view build.gradle @ 10914:f29f3fbe6810

8198329: Support FX build / test using JDK that doesn't include javafx.* modules Reviewed-by: prr, jvos, aghaisas
author kcr
date Fri, 27 Apr 2018 05:19:35 -0700
parents 40be25ced32b
children 80f2b4d0a9ab
line wrap: on
line source
/*
 * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

/**
 * The main build script for JavaFX.
 *
 * MUST FIX tasks to complete:
 *  - build check -- making sure the final artifact has the right bits
 *      - some things worth automatically sanity checking:
 *          - are there images in the javadocs?
 *          - are all of the expected dylibs etc there?
 *  - Perform sanity checking to make sure a JDK exists with javac, etc
 *  - Support building with no known JDK location, as long as javac, etc are on the path
 *  - Check all of the native flags. We're adding weight to some libs that don't need it, and so forth.
 *
 * Additional projects to work on as we go:
 *  - Add "developer debug". This is where the natives do not have debug symbols, but the Java code does
 *  - The genVSproperties.bat doesn't find the directory where RC.exe lives. So it is hard coded. Might be a problem.
 *  - special tasks for common needs, such as:
 *      - updating copyright headers
 *      - stripping trailing whitespace (?)
 *  - checkstyle
 *  - findbugs
 *  - re needs?
 *  - sqe testing
 *  - API change check
 *  - Pushing results to a repo?
 *  - ServiceWithSecurityManagerTest fails to complete when run from gradle.
 *  - Integrate Parfait reports for C code
 *  - FXML Project tests are not running
 */
defaultTasks = ["sdk"]

import java.util.concurrent.CountDownLatch
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import java.util.concurrent.Future

/******************************************************************************
 *                              Utility methods                               *
 *****************************************************************************/

/**
 * If the given named property is not defined, then this method will define
 * it with the given defaultValue. Any properties defined by this method can
 * be substituted on the command line by using -P, or by specifying a
 * gradle.properties file in the user home dir
 *
 * @param name The name of the property to define
 * @param defaultValue The default value to assign the property
 */
void defineProperty(String name, String defaultValue) {
    if (!project.hasProperty(name)) {
        project.ext.set(name, defaultValue);
    }
}

/**
 * If the given named property is not defined, then this method will attempt to
 * look up the property in the props map, and use the defaultValue if it cannot be found.
 *
 * @param name The name of the property to look up and/or define
 * @param props The properties to look for the named property in, if it has not already been defined
 * @param defaultValue The default value if the property has not been defined and the
 *                     props map does not contain the named property
 */
void defineProperty(String name, Properties props, String defaultValue) {
    if (!project.hasProperty(name)) {
        project.ext.set(name, props.getProperty(name, defaultValue));
    }
}

/**
 * Converts cygwin style paths to windows style paths, but with a forward slash.
 * This method is safe to call from any platform, and will only do work if
 * called on Windows (in all other cases it simply returns the supplied path.
 *
 * @param path the path to convert
 * @return the path converted to windows style, if on windows, otherwise it
 *         is the supplied path.
 */
String cygpath(String path) {
    if (!IS_WINDOWS) return path;
    if (path == null || "".equals(path)) return path;
    String ret = path.replaceAll('\\\\', '/')
    logger.info("Converting path '$path' via cygpath to "+ret)
    return ret
}

/**
 * Converts cygwin file paths for java executables to windows style
 * executable paths by changing forward slashes to back slashes and
 * adding the '.exe' extension.
 * This method is safe to call from any platform, and will only do work if
 * called on Windows (in all other cases it simply returns the supplied path).
 *
 * @param path the path to convert
 * @return the path converted to windows style, if on windows, otherwise it
 *         is the supplied path.
 */
String cygpathExe(String path) {
    if (!IS_WINDOWS) return path;
    if (path == null || "".equals(path)) return path;
    String ret = path.replaceAll('/', '\\\\')
    logger.info("Converting path '$path' via cygpath to "+ret)
    return ret + ".exe"
}

void loadProperties(String sourceFileName) {
    def config = new Properties()
    def propFile = new File(sourceFileName)
    if (propFile.canRead()) {
        config.load(new FileInputStream(propFile))
        for (java.util.Map.Entry property in config) {
            def keySplit = property.key.split("\\.");
            def key = keySplit[0];
            for (int i = 1; i < keySplit.length; i++) {
                key = key + keySplit[i].capitalize();
            }
            ext[key] = property.value;
        }
    }
}

/**
 * Struct used to contain some information passed to the closure
 * passed to compileTargets.
 */
class CompileTarget {
    String name;
    String upper;
    String capital;
}

/**
 * Iterates over each of the compile targets, passing the given closure
 * a CompileTarget instance.
 *
 * @param c The closure to call
 */
void compileTargets(Closure c) {
    if (COMPILE_TARGETS == "") {
        return
    }
    COMPILE_TARGETS.split(",").each { target ->
        CompileTarget ct = new CompileTarget();
        ct.name = target;
        ct.upper = target.trim().toUpperCase(Locale.ROOT)
        ct.capital = target.trim().capitalize()
        c(ct)
    }
}

/**
 * Manages the execution of some closure which is responsible for producing
 * content for a properties file built at build time and stored in the
 * root project's $buildDir, and then loading that properties file and
 * passing it to the processor closure.
 *
 * This is used on windows to produce a properties file containing all the
 * windows visual studio paths and environment variables, and on Linux
 * for storing the results of pkg-config calls.
 *
 * @param name the name of the file to produce
 * @param loader a closure which is invoked, given the properties file. This
 *        closure is invoked only if the properties file needs to be created
 *        and is responsible for populating the properties file.
 * @param processor a closure which is invoked every time this method is
 *        called and which will be given a Properties object, fully populated.
 *        The processor is then responsible for doing whatever it is that it
 *        must do with those properties (such as setting up environment
 *        variables used in subsequent native builds, or whatnot).
 */
void setupTools(String name, Closure loader, Closure processor) {
    // Check to see whether $buildDir/$name.properties file exists. If not,
    // then generate it. Once generated, we need to read the properties file to
    // help us define the defaults for this block of properties
    File propFile = file("$buildDir/${name}.properties");
    if (!propFile.exists()) {
        // Create the properties file
        propFile.getParentFile().mkdirs();
        propFile.createNewFile();
        loader(propFile);
    }

    // Try reading the properties in order to define the properties. If the property file cannot
    // be located, then we will throw an exception because we cannot guess these values
    InputStream propStream = null;
    try {
        Properties properties = new Properties();
        propStream = new FileInputStream(propFile);
        properties.load(propStream);
        processor(properties);
    } finally {
        try { propStream.close() } catch (Exception e) { }
    }
}

String[] parseJavaVersion(String jRuntimeVersion) {
    def jVersion = jRuntimeVersion.split("[-\\+]")[0]
    def tmpBuildNumber = "0"
    if (jVersion.startsWith("1.")) {
        // This is a pre-JEP-223 version string
        def dashbIdx = jRuntimeVersion.lastIndexOf("-b")
        if (dashbIdx != -1) {
            tmpBuildNumber = jRuntimeVersion.substring(dashbIdx + 2)
        }
    } else {
        // This is a post-JEP-223 version string
        def plusIdx = jRuntimeVersion.indexOf("+")
        if (plusIdx != -1) {
            tmpBuildNumber = jRuntimeVersion.substring(plusIdx + 1)
        }
    }
    def jBuildNumber = tmpBuildNumber.split("[-\\+]")[0]
    def versionInfo = new String[2];
    versionInfo[0] = jVersion
    versionInfo[1] = jBuildNumber
    return versionInfo
}

/**
 * Fails the build with the specified error message
 *
 * @param msg the reason for the failure
 */
void fail(String msg) {
    throw new GradleException("FAIL: " + msg);
}

/******************************************************************************
 *                                                                            *
 *                   Definition of project properties                         *
 *                                                                            *
 *  All properties defined using ext. are immediately available throughout    *
 *  the script as variables that can be used. These variables are attached    *
 *  to the root project (whereas if they were defined as def variables then   *
 *  they would only be available within the root project scope).              *
 *                                                                            *
 *  All properties defined using the "defineProperty" method can be replaced  *
 *  on the command line by using the -P flag. For example, to override the    *
 *  location of the binary plug, you would specify -PBINARY_PLUG=some/where   *
 *                                                                            *
 *****************************************************************************/

// If the ../rt-closed directory exists, then we are doing a closed build.
// In this case, build and property files will be read from
// ../rt-closed/closed-build.gradle and ../rt-closed/closed-properties.gradle
// respectively

def closedDir = file("../rt-closed")
def buildClosed = closedDir.isDirectory()
ext.BUILD_CLOSED = buildClosed

ext.RUNARGSFILE = "run.args"
ext.COMPILEARGSFILE = "compile.args"
ext.RUNJAVAPOLICYFILE = 'run.java.policy'

ext.TESTCOMPILEARGSFILE = "testcompile.args"
ext.TESTRUNARGSFILE = "testrun.args"
ext.TESTJAVAPOLICYFILE = 'test.java.policy'

// the file containing "extra" --add-exports
ext.EXTRAADDEXPORTS = 'buildSrc/addExports'

ext.MODULESOURCEPATH = "modulesourcepath.args"

// These variables indicate what platform is running the build. Is
// this build running on a Mac, Windows, or Linux machine? 32 or 64 bit?
ext.OS_NAME = System.getProperty("os.name").toLowerCase()
ext.OS_ARCH = System.getProperty("os.arch")
ext.IS_64 = OS_ARCH.toLowerCase().contains("64")
ext.IS_MAC = OS_NAME.contains("mac") || OS_NAME.contains("darwin")
ext.IS_WINDOWS = OS_NAME.contains("windows")
ext.IS_LINUX = OS_NAME.contains("linux")

// Verify that the architecture & OS are supported configurations. Note that
// at present building on PI is not supported, but we would only need to make
// some changes on assumptions on what should be built (like SWT / Swing) and
// such and we could probably make it work.
if (!IS_MAC && !IS_WINDOWS && !IS_LINUX) fail("Unsupported build OS ${OS_NAME}")
if (IS_WINDOWS && OS_ARCH != "x86" && OS_ARCH != "amd64") {
    fail("Unknown and unsupported build architecture: $OS_ARCH")
} else if (IS_MAC && OS_ARCH != "x86_64") {
    fail("Unknown and unsupported build architecture: $OS_ARCH")
} else if (IS_LINUX && OS_ARCH != "i386" && OS_ARCH != "amd64") {
    fail("Unknown and unsupported build architecture: $OS_ARCH")
}


// Get the JDK_HOME automatically based on the version of Java used to execute gradle. Or, if specified,
// use a user supplied JDK_HOME, STUB_RUNTIME, JAVAC, all of which may be specified
// independently (or we'll try to get the right one based on other supplied info). Sometimes the
// JRE might be the thing that is being used instead of the JRE embedded in the JDK, such as:
//    c:\Program Files (x86)\Java\jdk1.8.0\jre
//    c:\Program Files (x86)\Java\jre8\
// Because of this, you may sometimes get the jdk's JRE (in which case the logic we used to have here
// was correct and consistent with all other platforms), or it might be the standalone JRE (for the love!).
def envJavaHome = cygpath(System.getenv("JDK_HOME"))
if (envJavaHome == null || envJavaHome.equals("")) envJavaHome = cygpath(System.getenv("JAVA_HOME"))
def javaHome = envJavaHome == null || envJavaHome.equals("") ? System.getProperty("java.home") : envJavaHome
def javaHomeFile = file(javaHome)
defineProperty("JDK_HOME",
        javaHomeFile.name == "jre" ?
        javaHomeFile.getParent().toString() :
        javaHomeFile.name.startsWith("jre") ?
        new File(javaHomeFile.getParent(), "jdk1.${javaHomeFile.name.substring(3)}.0").toString() :
        javaHome) // we have to bail and set it to something and this is as good as any!
ext.JAVA_HOME = JDK_HOME

defineProperty("JAVA", cygpathExe("$JDK_HOME/bin/java"))
defineProperty("JAVAC", cygpathExe("$JDK_HOME/bin/javac"))
defineProperty("JAVADOC", cygpathExe("$JDK_HOME/bin/javadoc"))
defineProperty("JDK_DOCS", "https://docs.oracle.com/javase/10/docs/api/")
defineProperty("JDK_JMODS", cygpath(System.getenv("JDK_JMODS")) ?: cygpath(System.getenv("JDK_HOME") + "/jmods"))

defineProperty("javaRuntimeVersion", System.getProperty("java.runtime.version"))
def javaVersionInfo = parseJavaVersion(javaRuntimeVersion)
defineProperty("javaVersion", javaVersionInfo[0])
defineProperty("javaBuildNumber", javaVersionInfo[1])

defineProperty("libAVRepositoryURL", "https://libav.org/releases/")
defineProperty("FFmpegRepositoryURL", "https://www.ffmpeg.org/releases/")

loadProperties("$projectDir/build.properties")

def supplementalPreBuildFile = file("$closedDir/closed-pre-build.gradle");
def supplementalBuildFile = file("$closedDir/closed-build.gradle");

if (BUILD_CLOSED) {
    apply from: supplementalPreBuildFile
}

// GRADLE_VERSION_CHECK specifies whether to fail the build if the
// gradle version check fails
defineProperty("GRADLE_VERSION_CHECK", "true")
ext.IS_GRADLE_VERSION_CHECK = Boolean.parseBoolean(GRADLE_VERSION_CHECK)

// COMPILE_WEBKIT specifies whether to build all of webkit.
defineProperty("COMPILE_WEBKIT", "false")
ext.IS_COMPILE_WEBKIT = Boolean.parseBoolean(COMPILE_WEBKIT)

// COMPILE_MEDIA specifies whether to build all of media.
defineProperty("COMPILE_MEDIA", "false")
ext.IS_COMPILE_MEDIA = Boolean.parseBoolean(COMPILE_MEDIA)

// BUILD_LIBAV_STUBS specifies whether to download and build libav/ffmpeg libraries
defineProperty("BUILD_LIBAV_STUBS", "false")
ext.IS_BUILD_LIBAV_STUBS = IS_LINUX ? Boolean.parseBoolean(BUILD_LIBAV_STUBS) : false

// BUILD_WORKING_LIBAV specifies whether to build libav/ffmpeg libraries with
// decoder, demuxer, etc. required to run media. Valid only if BUILD_LIBAV_STUBS is true.
defineProperty("BUILD_WORKING_LIBAV", "false")
ext.IS_BUILD_WORKING_LIBAV = IS_LINUX ? Boolean.parseBoolean(BUILD_WORKING_LIBAV) : false

// COMPILE_PANGO specifies whether to build javafx_font_pango.
defineProperty("COMPILE_PANGO", "${IS_LINUX}")
ext.IS_COMPILE_PANGO = Boolean.parseBoolean(COMPILE_PANGO)

// COMPILE_HARFBUZZ specifies whether to use Harfbuzz.
defineProperty("COMPILE_HARFBUZZ", "false")
ext.IS_COMPILE_HARFBUZZ = Boolean.parseBoolean(COMPILE_HARFBUZZ)

// COMPILE_PARFAIT specifies whether to build parfait
defineProperty("COMPILE_PARFAIT", "false")
ext.IS_COMPILE_PARFAIT = Boolean.parseBoolean(COMPILE_PARFAIT)

// BUILD_FXPACKAGER enables building the packager modules and native code
defineProperty("BUILD_FXPACKAGER", "true")
ext.IS_BUILD_FXPACKAGER = Boolean.parseBoolean(BUILD_FXPACKAGER)

// RETAIN_PACKAGER_TESTS specifies whether the tests in fxpackager should
// keep generated files instead of attempting to automatically delete them
defineProperty("RETAIN_PACKAGER_TESTS", "false")
ext.IS_RETAIN_PACKAGER_TESTS = Boolean.parseBoolean(RETAIN_PACKAGER_TESTS)

// TEST_PACKAGER_DMG whether tests that create DMG files via hdiutil
// should be run.  On OSX 10.7 this tends to hang automated builds
defineProperty("TEST_PACKAGER_DMG", "false")
ext.IS_TEST_PACKAGER_DMG = Boolean.parseBoolean(TEST_PACKAGER_DMG)

// Define the SWT.jar that we are going to have to download during the build process based
// on what platform we are compiling from (not based on our target).
ext.SWT_FILE_NAME = IS_MAC ? "org.eclipse.swt.cocoa.macosx.x86_64_3.105.3.v20170228-0512" :
    IS_WINDOWS && IS_64 ? "org.eclipse.swt.win32.win32.x86_64_3.105.3.v20170228-0512" :
    IS_WINDOWS && !IS_64 ? "org.eclipse.swt.win32.win32.x86_3.105.3.v20170228-0512" :
    IS_LINUX && IS_64 ? "org.eclipse.swt.gtk.linux.x86_64_3.105.3.v20170228-0512" :
    IS_LINUX && !IS_64 ? "org.eclipse.swt.gtk.linux.x86_3.105.3.v20170228-0512" : ""

// Specifies whether to run full tests (true) or smoke tests (false)
defineProperty("FULL_TEST", "false")
ext.IS_FULL_TEST = Boolean.parseBoolean(FULL_TEST);

defineProperty("FORCE_TESTS", "false")
ext.IS_FORCE_TESTS = Boolean.parseBoolean(FORCE_TESTS);

// Specifies whether to run robot-based visual tests (only used when FULL_TEST is also enabled)
defineProperty("USE_ROBOT", "false")
ext.IS_USE_ROBOT = Boolean.parseBoolean(USE_ROBOT);

// Specified whether to run tests in headless mode
defineProperty("HEADLESS_TEST", "false")
ext.IS_HEADLESS_TEST = Boolean.parseBoolean(HEADLESS_TEST);

// Specifies whether to run system tests that depend on AWT (only used when FULL_TEST is also enabled)
defineProperty("AWT_TEST", "true")
ext.IS_AWT_TEST = Boolean.parseBoolean(AWT_TEST);

// Specifies whether to run system tests that depend on SWT (only used when FULL_TEST is also enabled)
defineProperty("SWT_TEST", "true")
ext.IS_SWT_TEST = Boolean.parseBoolean(SWT_TEST);

// Specifies whether to run unstable tests (true) - tests that don't run well with Hudson builds
// These tests should be protected with :
//    assumeTrue(Boolean.getBoolean("unstable.test"));
defineProperty("UNSTABLE_TEST", "false")
ext.IS_UNSTABLE_TEST = Boolean.parseBoolean(UNSTABLE_TEST);

// Toggle diagnostic output from the Gradle workaround and the Sandbox test apps.
defineProperty("WORKER_DEBUG", "false")
ext.IS_WORKER_DEBUG = Boolean.parseBoolean(WORKER_DEBUG);

// Specify the build configuration (Release, Debug, or DebugNative)
defineProperty("CONF", "Debug")
ext.IS_DEBUG_JAVA = CONF == "Debug" || CONF == "DebugNative"
ext.IS_DEBUG_NATIVE = CONF == "DebugNative"

// Defines the compiler warning levels to use. If empty, then no warnings are generated. If
// not empty, then the expected syntax is as a space or comma separated list of names, such
// as defined in the javac documentation.
defineProperty("LINT", "none")
ext.IS_LINT = LINT != "none"

defineProperty("DOC_LINT", "all")
ext.IS_DOC_LINT = DOC_LINT != ""

// Specifies whether to use the "useDepend" option when compiling Java sources
defineProperty("USE_DEPEND", "true")
ext.IS_USE_DEPEND = Boolean.parseBoolean(USE_DEPEND)

// Specifies whether to use the "incremental" option when compiling Java sources
defineProperty("INCREMENTAL", "false")
ext.IS_INCREMENTAL = Boolean.parseBoolean(INCREMENTAL)

// Specifies whether to include the Null3D pipeline (for perf debugging)
defineProperty("INCLUDE_NULL3D", "false")
ext.IS_INCLUDE_NULL3D = Boolean.parseBoolean(INCLUDE_NULL3D)

// Specifies whether to include the ES2 pipeline if available
defineProperty("INCLUDE_ES2", IS_WINDOWS ? "false" : "true")
ext.IS_INCLUDE_ES2 = Boolean.parseBoolean(INCLUDE_ES2)

// Specifies whether to generate code coverage statistics when running tests
defineProperty("JCOV", "false")
ext.DO_JCOV = Boolean.parseBoolean(JCOV)

// Define the number of threads to use when compiling (specifically for native compilation)
// On Mac we limit it to 1 by default due to problems running gcc in parallel
if (IS_MAC) {
    defineProperty("NUM_COMPILE_THREADS", "1")
} else {
    defineProperty("NUM_COMPILE_THREADS", "${Runtime.runtime.availableProcessors()}")
}

//
// The next three sections of properties are used to generate the
// VersionInfo class, and the Windows DLL manifest.
//

// The following properties should be left alone by developers and set only from Hudson.
defineProperty("HUDSON_JOB_NAME", "not_hudson")
defineProperty("HUDSON_BUILD_NUMBER","0000")
defineProperty("PROMOTED_BUILD_NUMBER", "0")
defineProperty("MILESTONE_FCS", "false")
ext.IS_MILESTONE_FCS = Boolean.parseBoolean(MILESTONE_FCS)

// The following properties define the product name for Oracle JDK and OpenJDK
// for VersionInfo and the DLL manifest.
if (BUILD_CLOSED) {
    defineProperty("PRODUCT_NAME", "Java(TM)")
    defineProperty("COMPANY_NAME", "Oracle Corporation")
    defineProperty("PLATFORM_NAME", "Platform SE")
} else {
    defineProperty("PRODUCT_NAME", "OpenJFX")
    defineProperty("COMPANY_NAME", "N/A")
    defineProperty("PLATFORM_NAME", "Platform")
}

// The following properties are set based on properties defined in
// build.properties. The release version and suffix should be updated
// in that file.
def relVer = 0
if (jfxReleasePatchVersion == "0") {
    if (jfxReleaseSecurityVersion == "0") {
        if (jfxReleaseMinorVersion == "0") {
            relVer = "${jfxReleaseMajorVersion}"
        } else {
            relVer = "${jfxReleaseMajorVersion}.${jfxReleaseMinorVersion}"
        }
    } else {
        relVer = "${jfxReleaseMajorVersion}.${jfxReleaseMinorVersion}.${jfxReleaseSecurityVersion}"
    }
} else {
    relVer = "${jfxReleaseMajorVersion}.${jfxReleaseMinorVersion}.${jfxReleaseSecurityVersion}.${jfxReleasePatchVersion}"
}
defineProperty("RELEASE_VERSION", relVer)
defineProperty("RELEASE_VERSION_PADDED", "${jfxReleaseMajorVersion}.${jfxReleaseMinorVersion}.${jfxReleaseSecurityVersion}.${jfxReleasePatchVersion}")

def buildDate = new java.util.Date()
def buildTimestamp = new java.text.SimpleDateFormat("yyyy-MM-dd-HHmmss").format(buildDate)
defineProperty("BUILD_TIMESTAMP", buildTimestamp)
def relSuffix = ""
def relOpt = ""
if (HUDSON_JOB_NAME == "not_hudson") {
    relSuffix = "-internal"
    relOpt = "-${buildTimestamp}"
} else {
    relSuffix = IS_MILESTONE_FCS ? "" : jfxReleaseSuffix
}
defineProperty("RELEASE_SUFFIX", relSuffix)
defineProperty("RELEASE_VERSION_SHORT", "${RELEASE_VERSION}${RELEASE_SUFFIX}")
defineProperty("RELEASE_VERSION_LONG", "${RELEASE_VERSION_SHORT}+${PROMOTED_BUILD_NUMBER}${relOpt}")

// Check whether the COMPILE_TARGETS property has been specified (if so, it was done by
// the user and not by this script). If it has not been defined then default
// to building the normal desktop build for this machine
project.ext.set("defaultHostTarget", IS_MAC ? "mac" : IS_WINDOWS ? "win" : IS_LINUX ? "linux" : "");
defineProperty("COMPILE_TARGETS", "$defaultHostTarget")

// Flag indicating whether to import cross compile tools
def importCrossTools = BUILD_CLOSED ? true : false;
if (!importCrossTools && hasProperty("IMPORT_CROSS_TOOLS")) {
    importCrossTools = Boolean.parseBoolean(IMPORT_CROSS_TOOLS);
}
ext.IS_IMPORT_CROSS_TOOLS = importCrossTools

// Location of the cross compile tools
def crossToolsDir = "../crosslibs"
if (hasProperty("CROSS_TOOLS_DIR")) {
    crossToolsDir = CROSS_TOOLS_DIR
}
ext.CROSS_TOOLS_DIR = file(crossToolsDir)

// Specifies whether to run tests with the existing javafx.* modules instead of compiling a new one
defineProperty("BUILD_SDK_FOR_TEST", "true")
ext.DO_BUILD_SDK_FOR_TEST = Boolean.parseBoolean(BUILD_SDK_FOR_TEST)

// All "classes" and "jar" tasks and their dependencies would be disabled
// when running with DO_BUILD_SDK_FOR_TEST=false as they're unneeded for running tests
if (!DO_BUILD_SDK_FOR_TEST) {
    gradle.taskGraph.useFilter({ task -> !task.name.equals("classes") && !task.name.equals("jar") })
}

// Make sure JDK_HOME/bin/java exists
if (!file(JAVA).exists()) throw new Exception("Missing or incorrect path to 'java': '$JAVA'. Perhaps bad JDK_HOME? $JDK_HOME")
if (!file(JAVAC).exists()) throw new Exception("Missing or incorrect path to 'javac': '$JAVAC'. Perhaps bad JDK_HOME? $JDK_HOME")
if (!file(JAVADOC).exists()) throw new Exception("Missing or incorrect path to 'javadoc': '$JAVADOC'. Perhaps bad JDK_HOME? $JDK_HOME")

// Determine the verion of Java in JDK_HOME. It looks like this:
//
// $ java -version
// java version "1.7.0_45"
// Java(TM) SE Runtime Environment (build 1.7.0_45-b18)
// Java HotSpot(TM) 64-Bit Server VM (build 24.45-b08, mixed mode)
//
// We need to parse the second line
def inStream = new java.io.BufferedReader(new java.io.InputStreamReader(new java.lang.ProcessBuilder(JAVA, "-fullversion").start().getErrorStream()));
try {
    String v = inStream.readLine().trim();
    if (v != null) {
        int ib = v.indexOf("full version \"");
        if (ib != -1) {
            String str = v.substring(ib);
            String ver = str.substring(str.indexOf("\"") + 1, str.size() - 1);

            defineProperty("jdkRuntimeVersion", ver)
            def jdkVersionInfo = parseJavaVersion(ver)
            defineProperty("jdkVersion", jdkVersionInfo[0])
            defineProperty("jdkBuildNumber", jdkVersionInfo[1])
        }
    }
} finally {
    inStream.close();
}
if (!project.hasProperty("jdkRuntimeVersion")) throw new Exception("Unable to determine the version of Java in JDK_HOME at $JDK_HOME");


// Determine whether the javafx.* modules are present in the JDK. To do this,
// we will execute "java --list-modules" and search for javafx.base
ext.HAS_JAVAFX_MODULES = false;
def inStream2 = new java.io.BufferedReader(new java.io.InputStreamReader(new java.lang.ProcessBuilder(JAVA, "--list-modules").start().getInputStream()));
try {
    String v;
    while ((v = inStream2.readLine()) != null) {
        v = v.trim();
        if (v.startsWith("javafx.base")) ext.HAS_JAVAFX_MODULES = true;
    }
} finally {
    inStream2.close();
}

// The HAS_JAVAFX_MODULES flag will be used to determine the mode for building
// and running the applications and tests.
// If HAS_JAVAFX_MODULES is true, then we will build / test javafx modules
// for exporting to a JDK build. If HAS_JAVAFX_MODULES is false, then we will
// build / test a standalone sdk for running with a JDK that does not include
// the javafx modules.


/**
 * Fetch/Check that external tools are present for the build. This method
 * will conditionally download the packages from project defined ivy repositories
 * and unpack them into the specified destdir
 *
 * @param configName A unique name to distinguish the configuration (ie "ARMSFV6")
 * @param packages A list of required packages (with extensions .tgz, .zip)
 * @param destdir where the packages should be unpacked
 * @param doFetch if true, the named packages will be download
 */
void fetchExternalTools(String configName, List packages, File destdir, boolean doFetch) {
    if (doFetch) {
        // create a unique configuration for this fetch
        def String fetchToolsConfig = "fetchTools$configName"
        rootProject.configurations.create(fetchToolsConfig)

        def List<String> fetchedPackages = []
        def int fetchCount = 0

        packages.each { pkgname->
            def int dotdex = pkgname.lastIndexOf('.')
            def int dashdex = pkgname.lastIndexOf('-')
            def String basename = pkgname.substring(0,dashdex)
            def String ver = pkgname.substring(dashdex+1,dotdex)
            def String ext = pkgname.substring(dotdex+1)
            def File pkgdir = file("$destdir/$basename-$ver")

            if (!pkgdir.isDirectory()) {
                rootProject.dependencies.add(fetchToolsConfig, "javafx:$basename:$ver", {
                    artifact {
                        name = basename
                        type = ext
                    }
                })
                println "adding $pkgname as a downloadable item did not find $pkgdir"
                fetchedPackages.add(pkgname)
                fetchCount++
            }
        }

        //fetch all the missing packages
        if (fetchedPackages.size > 0) {
            destdir.mkdirs()

            logger.quiet "fetching missing packages $fetchedPackages"
            copy {
                from rootProject.configurations[fetchToolsConfig]
                into destdir
            }

            // unpack the fetched packages
            fetchedPackages.each { pkgname->
                logger.quiet "expanding the package $pkgname"
                def srcball = file("${destdir}/${pkgname}")

                if (!srcball.exists()) {
                    throw new GradleException("Failed to fetch $pkgname");
                }

                def String basename = pkgname.substring(0,pkgname.lastIndexOf("."))
                def File pkgdir = file("$destdir/$basename")

                if (pkgname.endsWith(".tgz")) {
                    if (IS_LINUX || IS_MAC) {
                        // use native tar to support symlinks
                        pkgdir.mkdirs()
                        exec {
                            workingDir pkgdir
                            commandLine "tar", "zxf", "${srcball}"
                         }
                    } else {
                        copy {
                            from tarTree(resources.gzip("${srcball}"))
                            into pkgdir
                        }
                    }
                } else if (pkgname.endsWith(".zip")) {
                     copy {
                         from zipTree("${srcball}")
                         into pkgdir
                     }
                } else {
                    throw new GradleException("Unhandled package type for compile package ${pkgname}")
                }
                srcball.deleteOnExit();
            }
        } else {
            logger.quiet "all tool packages are present $packages"
        }
    } else { // !doFetch - so just check they are present
        // check that all the dirs are really there
        def List<String> errors = []
        packages.each { pkgname->
            def String basename = pkgname.substring(0,pkgname.lastIndexOf("."))
            def File pkgdir = file("$destdir/$basename")

            if (!pkgdir.isDirectory()) {
                errors.add(pkgname)
            }
        }
        if (errors.size > 0) {
            throw new GradleException("Error: missing tool packages: $errors")
        } else {
            logger.quiet "all tool packages are present $packages"
        }
    }
}

// Make a forked ANT call.
// This needs to be forked so that ant can be used with the right JDK and updated modules
// for testing obscure things like packaging of apps
void ant(String conf,   // platform configuration
         String dir,    // directory to run from
         String target, // ant target
         List<String>  params // parameters (usually -Dxxx=yyy)
         ) {
    // Try to use ANT_HOME
    String antHomeEnv = System.getenv("ANT_HOME")
    String antHome = antHomeEnv != null ? cygpath(antHomeEnv) : null;
    String ant = (antHome != null && !antHome.equals("")) ? "$antHome/bin/ant" : "ant";

    exec {
        workingDir = dir
        environment("JDK_HOME", JDK_HOME)
        environment("JAVA_HOME", JDK_HOME)
        if (IS_WINDOWS) {
            environment([
                    "VCINSTALLDIR"         : WINDOWS_VS_VCINSTALLDIR,
                    "VSINSTALLDIR"         : WINDOWS_VS_VSINSTALLDIR,
                    "DEVENVDIR"            : WINDOWS_VS_DEVENVDIR,
                    "MSVCDIR"              : WINDOWS_VS_MSVCDIR,
                    "INCLUDE"              : WINDOWS_VS_INCLUDE,
                    "LIB"                  : WINDOWS_VS_LIB,
                    "LIBPATH"              : WINDOWS_VS_LIBPATH,
                    "DXSDK_DIR"            : WINDOWS_DXSDK_DIR
            ]);
            commandLine "cmd", "/c", ant, "-Dbuild.compiler=javac1.7"
        } else {
            commandLine ant, "-Dbuild.compiler=javac1.7"
        }
        if ((conf != null) && !rootProject.defaultHostTarget.equals(conf)) {
            def targetProperties = rootProject.ext[conf.trim().toUpperCase()]
            args("-Dcross.platform=$conf")
            if (targetProperties.containsKey('arch')) {
                args("-Dcross.platform.arch=${targetProperties.arch}")
            }
        }
        if (params != null) {
            params.each() { s->
                args(s)
            }
        }
        if (IS_MILESTONE_FCS) {
            args('-Djfx.release.suffix=""')
        }
        args(target);
    }
}

List<String> computeLibraryPath(boolean working) {
    List<String> lp = []

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

        // the build/modular-sdk area
        def platformPrefix = ""
        def bundledSdkDirName = "${platformPrefix}modular-sdk"
        def bundledSdkDir = "${rootProject.buildDir}/${bundledSdkDirName}"
        def modulesLibsDir = "${bundledSdkDir}/modules_libs"

        modsWithNative.each() { m ->
            lp << cygpath("${modulesLibsDir}/javafx.${m}")
        }
    } else {
        def platformPrefix = ""
        def standaloneSdkDirName = "${platformPrefix}sdk"
        def standaloneSdkDir = "${rootProject.buildDir}/${standaloneSdkDirName}"
        def modulesLibName = IS_WINDOWS ? "bin" : "lib"
        def modulesLibsDir = "${standaloneSdkDir}/${modulesLibName}"
        lp << cygpath("${modulesLibsDir}")
    }

    return lp
}

// Return list with the arguments needed for --patch-module or --module-path
// for the provided projects. Used with Java executables ie. tests
List<String> computePatchModuleArgs(List<String> deps, boolean test, boolean includeJLP) {
    List<String> pma = []

    if (HAS_JAVAFX_MODULES) {
        deps.each { String projname ->
            def proj = project(projname)
            if (proj.hasProperty("moduleName")) {
                File dir;
                if (test && proj.sourceSets.hasProperty('shims')) {
                    dir = file("${rootProject.buildDir}/shims")
                } else {
                    dir = file("${rootProject.buildDir}/modular-sdk/modules")
                }
                String moduleName = proj.ext.moduleName
                String dirpath = cygpath("${dir}/${moduleName}")
                pma += "--patch-module=${moduleName}=${dirpath}"
            }
        }
    } else {
        String mp = null
        deps.each { String projname ->
            def proj = project(projname)
            if (proj.hasProperty("moduleName")) {
                String moduleName = proj.ext.moduleName
                File dir;
                if (test && proj.sourceSets.hasProperty('shims')) {
                    dir = file("${rootProject.buildDir}/shims/${moduleName}")
                } else {
                    dir = file("${rootProject.buildDir}/sdk/lib/${moduleName}.jar")
                }
                if (mp == null) {
                    mp = dir.path
                } else {
                    mp = mp + File.pathSeparator + dir.path
                }
            }
        }

        // in some cases like base we could end up with an empty
        // path... make sure we don't pass one back
        if (mp == null) {
            return null
        }

        pma += '--module-path'
        pma += mp

        String addm = null
        deps.each {String projname ->
            def proj = project(projname)
            if (proj.hasProperty("moduleName") && proj.buildModule) {
                if (addm == null) {
                    addm = proj.moduleName
                } else {
                    addm = addm + "," + proj.moduleName
                }
            }
        }
        if (addm != null) {
            pma += "--add-modules=${addm}"
        }
    }

    if (includeJLP) {
        pma += "-Djava.library.path=" + computeLibraryPath(true).join(File.pathSeparator)
    }

    return pma
}

// Return a list containing the --upgrade-module-path or --module-path
// used with Javac
List<String> computeModulePathArgs(String  pname, List<String> deps, boolean test) {
    List<String> mpa = HAS_JAVAFX_MODULES ? [ '--upgrade-module-path' ] : [ '--module-path' ]
    String mp = null
    deps.each { String projname ->
        def proj = project(projname)
        // for a non test set of args, we don't want the current module in the list
        // for a test test, we do need it to update what we built

        if (proj.hasProperty("moduleName") &&
                proj.buildModule &&
                !(!test && proj.name.equals(pname))) {

            File dir;
            if (test && proj.sourceSets.hasProperty('shims')) {
                dir = new File(proj.sourceSets.shims.java.outputDir, proj.ext.moduleName);
            } else {
                dir = new File(proj.sourceSets.main.java.outputDir, proj.ext.moduleName);
            }
            if (mp == null) {
                mp = dir.path
            } else {
                mp = mp + File.pathSeparator + dir.path
            }
        }
    }

    // in some cases like base we could end up with an empty
    // path... make sure we don't pass one back
    if (mp == null) {
        return null
    }

    mpa += mp

    if (!HAS_JAVAFX_MODULES) {
        String addm = null
        deps.each {String projname ->
            def proj = project(projname)
            // for a non test set of args, we don't want the current module in the list
            // for a test test, we do need it to update what we built

            if (proj.hasProperty("moduleName") &&
                    proj.buildModule &&
                    !(!test && proj.name.equals(pname))) {

                if (addm == null) {
                    addm = proj.moduleName
                } else {
                    addm = addm + "," + proj.moduleName
                }
            }
        }
        if (addm != null) {
            mpa += "--add-modules=${addm}"
        }
    }

    return mpa
}


void writeRunArgsFile(File dest, List<String> libpath, List<String> modpath, List<String> modules) {

    dest.delete()

    logger.info("Creating file ${dest.path}")

    if (libpath != null) {
        dest <<  "-Djava.library.path=\"\\\n"
        libpath.each() { e->
            dest << "  "
            dest << e
            dest << File.pathSeparator
            dest << "\\\n"
        }
        dest <<  "  \"\n"
    }

    if (HAS_JAVAFX_MODULES) {
        modpath.each { e ->
            dest <<  "--patch-module=\""
            dest << e
            dest << "\"\n"
        }
    } else {
        if (modpath.size() == 1) {
            dest <<  "--module-path=\""
            dest << modpath[0]
            dest << "\"\n"
        } else {
            dest <<  "--module-path=\"\\\n"
            modpath.each() { e->
                dest << "  "
                dest << e
                dest << File.pathSeparator
                dest << "\\\n"
            }
            dest <<  "  \"\n"
        }
    }

    if (modules != null) {
        dest <<  "--add-modules="
        dest << modules.join(",")
        dest << "\n"
    }
}

void appendQualExports(File dest, List<String> qualExports) {
    qualExports.each { exp ->
        dest << exp
        dest << "\n"
    }
}

// perform common project manipulation for modules
void commonModuleSetup(Project p, List<String> moduleChain) {

    p.ext.moduleChain = moduleChain

    if (p.hasProperty("moduleName")) {
        p.ext.moduleDir = new File (p.sourceSets.main.java.outputDir, "${p.moduleName}")
        if (p.sourceSets.hasProperty('shims')) {
            p.ext.moduleShimsDir = new File (p.sourceSets.shims.java.outputDir, "${p.moduleName}")
        }
    }

    def mpa = computeModulePathArgs(p.name, moduleChain, false)
    if (mpa != null) {
        p.ext.modulePathArgs = mpa
    }

    p.ext.testModulePathArgs = computePatchModuleArgs(moduleChain, true, false)
    p.ext.patchModuleArgs = computePatchModuleArgs(moduleChain ,false, true)
    p.ext.testPatchModuleArgs = computePatchModuleArgs(moduleChain, true, true)

    moduleChain.each() {e ->
        if (!e.equals(p.name)) {
            p.compileJava.dependsOn(project(e).classes)
            p.compileTestJava.dependsOn(project(e).testClasses)
        }
    }

    // read in any addExports file
    File addExportsFile = new File(p.projectDir,"src/test/addExports")
    if (addExportsFile.exists()) {
        List<String> ae = []
        addExportsFile.eachLine { line ->
            line = line.trim()
            if (!(line.startsWith("#") || line.equals(""))) {
                ae += line.split(' ')
            }
        }
        p.ext.testAddExports  = ae.flatten()
    }

    // read in the temporary addExports file EXTRAADDEXPORTS)
    //
    // These extra --add-exports will be used in two places and so we
    // create/modify two items:
    // p.testAddExports - add the extra items so they are included in test builds
    //
    // p.extraAddExports - for use in any other place where we don't automatically update
    //    for example any non modular, non 'test' compile, any compile that does not
    //    use a module-source-path that includes the dependent modules
    //
    // Note that we don't modify the modular build (main, shims) because they use
    // module-info directly, and we don't want to cover up any missing items there.
    //
    if (!rootProject.hasProperty("EXTRA_ADDEXPORTS_ARGS")) {
        List<String> extraAddExportsList = []
        String fullae = ""
        File tmpaddExportsFile = new File(rootProject.projectDir, EXTRAADDEXPORTS)
        if (tmpaddExportsFile.exists()) {
            String nl = System.getProperty("line.separator")
            tmpaddExportsFile.eachLine { line ->
                line = line.trim()
                fullae += line + nl
                if (!(line.startsWith("#") || line.equals(""))) {
                    extraAddExportsList += line.split(' ')
                }
            }
        }
        // This string is used in the creation of the build/*.args files
        // so we preserve comments
        if (!extraAddExportsList.isEmpty()) {
            rootProject.ext.EXTRA_ADDEXPORTS_STRING = fullae
        }
        rootProject.ext.EXTRA_ADDEXPORTS_ARGS = extraAddExportsList
    }

    if (HAS_JAVAFX_MODULES) {
        // use this variable, because it shows we have a non empty addition
        if (rootProject.hasProperty("EXTRA_ADDEXPORTS_STRING")) {
            p.ext.extraAddExports = EXTRA_ADDEXPORTS_ARGS.flatten()
            if (p.hasProperty("testAddExports")) {
                p.testAddExports += EXTRA_ADDEXPORTS_ARGS.flatten()
            }
        }
    }
}

// Now we need to define the native compilation tasks. The set of parameters to
// native compilation depends on the target platform (and also to some extent what platform
// you are compiling on). These settings are contained in various gradle files
// such as mac.gradle and linux.gradle and armhf.gradle. Additionally, the developer
// can specify COMPILE_FLAGS_FILE to be a URL or path to a different gradle file
// that will contain the appropriate flags.
defineProperty("COMPILE_FLAGS_FILES", COMPILE_TARGETS.split(",").collect {"buildSrc/${it.trim()}.gradle"}.join(","))
if (COMPILE_TARGETS == "all") {
    def tmp = []
    File buildSrcDir = file("buildSrc")
    buildSrcDir.listFiles().each { File f ->
        if (f.isFile() && f.name.endsWith(".gradle") && !f.name.equals("build.gradle")) {
            def target = f.name.substring(0, f.name.lastIndexOf('.gradle')).toUpperCase(Locale.ROOT)
            apply from: f
            if (project.ext["${target}"].canBuild) {
                tmp.add(target)
            }
        }
    }
    COMPILE_FLAGS_FILES = tmp.collect { "buildSrc/${it}.gradle"}.join(",")
    COMPILE_TARGETS = tmp.collect { "${it.toLowerCase()}"}.join(",")
} else {
    COMPILE_FLAGS_FILES.split(",").each {
        logger.info("Applying COMPILE_FLAGS_FILE '$it'")
        apply from: it
    }
}

if (COMPILE_TARGETS != "") {
    def tmp = []
    COMPILE_TARGETS.split(",").each {target ->
        if (project.ext["${target.toUpperCase(Locale.ROOT)}"].canBuild) {
            tmp.add(target)
        }
    }
    COMPILE_TARGETS = tmp.collect { "${it.toLowerCase()}"}.join(",")
}

// Sanity check the expected properties all exist
compileTargets { t ->
    // Every platform must define these variables
    if (!project.hasProperty(t.upper)) throw new Exception("ERROR: Incorrectly configured compile flags file, missing ${t.name} property")
    def props = project.ext[t.upper];
    // TODO: we could remove libDest in favor of modLibDest
    ["compileSwing", "compileSWT", "compileFXPackager", "libDest"].each { prop ->
        if (!props.containsKey(prop)) throw new Exception("ERROR: Incorrectly configured compile flags file, missing ${prop} property on ${t.name}")
    }
}

// Various build flags may be set by the different target files, such as
// whether to build Swing, SWT, FXPackager, etc. We iterate over all
// compile targets and look for these settings in our properties. Note that
// these properties cannot be set from the command line, but are set by
// the target build files such as armv6hf.gradle or mac.gradle.
ext.COMPILE_SWING = false;
ext.COMPILE_SWT = false;
ext.COMPILE_FXPACKAGER = false;
compileTargets { t ->
    def targetProperties = project.rootProject.ext[t.upper]

    if (targetProperties.compileSwing) COMPILE_SWING = true
    if (targetProperties.compileSWT) COMPILE_SWT = true
    if (IS_BUILD_FXPACKAGER && HAS_JAVAFX_MODULES && targetProperties.compileFXPackager) COMPILE_FXPACKAGER = true

    if (!targetProperties.containsKey('compileWebnodeNative')) {
        // unless specified otherwise, we will compile native Webnode if IS_COMPILE_WEBKIT
        targetProperties.compileWebnodeNative = true
    }

    if (!targetProperties.containsKey('compileMediaNative')) {
        // unless specified otherwise, we will compile native Media if IS_COMPILE_MEDIA
        targetProperties.compileMediaNative = true
    }

    if (!targetProperties.containsKey('includeSWT')) targetProperties.includeSWT = true
    if (!targetProperties.containsKey('includeSwing')) targetProperties.includeSwing = true
    if (!targetProperties.containsKey('includeNull3d')) targetProperties.includeNull3d = true
    if (!targetProperties.containsKey('includeMonocle')) targetProperties.includeMonocle = false
    if (!targetProperties.containsKey('includeEGL')) targetProperties.includeEGL = false

    if (!targetProperties.containsKey('includeGTK')) targetProperties.includeGTK = IS_LINUX

    if (!targetProperties.containsKey('modLibDest')) targetProperties.modLibDest = targetProperties.libDest

    // This value is used as a prefix for various directories under ./build,
    // such as sdk, to allow for a common name for the hosted build
    // (for use when building apps) and a unique name for cross builds.
    if (rootProject.defaultHostTarget.equals(t.name)) {
        // use a simple common default for the "host" build
        targetProperties.platformPrefix=""
    } else {
        // and a more complex one for cross builds
        targetProperties.platformPrefix="${t.name}-"
    }
}

/******************************************************************************
 *                                                                            *
 *                         Build Setup Sanity Checks                          *
 *                                                                            *
 *  Here we do a variety of checks so that if the version of Java you are     *
 *  building with is misconfigured, or you are using the wrong version of     *
 *  gradle, etc you will get some kind of helpful error / warning message     *
 *                                                                            *
 *****************************************************************************/

// Sanity check that we actually have a list of compile targets to execute
if (COMPILE_TARGETS == null || COMPILE_TARGETS == "") {
    throw new Exception("Unable to determine compilation platform, must specify valid COMPILE_TARGETS!")
}

// Verify that CONF is something useful
if (CONF != "Release" && CONF != "Debug" && CONF != "DebugNative") {
    logger.warn("Unknown configuration CONF='$CONF'. Treating as 'Release'")
}

// If the number of compile threads is less than 1 then we have a problem!
if (Integer.parseInt(NUM_COMPILE_THREADS.toString()) < 1) {
    logger.warn("NUM_COMPILE_THREADS was specified as '$NUM_COMPILE_THREADS' which is less than the minimum value of 1. " +
            "Building with a value of 1 instead.")
    NUM_COMPILE_THREADS = 1
}

// Check for Gradle 4.3, error if < 3.0.
if (gradle.gradleVersion != "4.3") {
    def ver = gradle.gradleVersion.split("[\\.]");
    def gradleMajor = Integer.parseInt(ver[0]);
    def gradleMinor = Integer.parseInt(ver[1].split("[^0-9]")[0]);
    def err = "";
    if (gradleMajor < 4 || (gradleMajor == 4 && gradleMinor < 3)) {
        err = "Gradle version too old: ${gradle.gradleVersion}; must be at least 4.3"
    }

    if (IS_GRADLE_VERSION_CHECK && err != "") {
        fail(err);
    }

    logger.warn("*****************************************************************");
    logger.warn("Unsupported gradle version $gradle.gradleVersion in use.");
    logger.warn("Only version 4.3 is supported. Use this version at your own risk");
    if ( err != "") logger.warn(err);
    logger.warn("*****************************************************************");
}

// Look for stub runtime in bundled sdk, standalone sdk, or boot JDK

def String cachedBundledRuntime = cygpath("$projectDir") + "/../caches/modular-sdk"
def String cachedStandaloneRuntime = cygpath("$projectDir") + "/../caches/sdk"
def String jdkStubRuntime = cygpath("$JDK_HOME")

def defaultStubRuntime = ""
if (file(cachedBundledRuntime).exists()) {
    defaultStubRuntime = cachedBundledRuntime
} else if (file(cachedStandaloneRuntime).exists()) {
    defaultStubRuntime = cachedStandaloneRuntime
} else if (BUILD_CLOSED) {
    defaultStubRuntime = cachedBundledRuntime
} else {
    defaultStubRuntime = jdkStubRuntime
}

defineProperty("STUB_RUNTIME", defaultStubRuntime)

if (STUB_RUNTIME.endsWith("/modular-sdk")) {
    def stubModulesLib = "$STUB_RUNTIME/modules_libs"
    defineProperty("MEDIA_STUB", "$stubModulesLib/javafx.media")
    defineProperty("WEB_STUB", "$stubModulesLib/javafx.web")
} else {
    def libraryStub = IS_WINDOWS ? "$STUB_RUNTIME/bin" : "$STUB_RUNTIME/lib"

    defineProperty("MEDIA_STUB", libraryStub)
    defineProperty("WEB_STUB", libraryStub)
}

ext.UPDATE_STUB_CACHE = (BUILD_CLOSED && STUB_RUNTIME != "" && !file(STUB_RUNTIME).isDirectory())


/******************************************************************************
 *                                                                            *
 *                      Logging of Properties and Settings                    *
 *                                                                            *
 *  Log some of the settings we've determined. We could log more here, it     *
 *  doesn't really hurt.                                                      *
 *                                                                            *
 *****************************************************************************/

logger.quiet("gradle.gradleVersion: $gradle.gradleVersion")
logger.quiet("OS_NAME: $OS_NAME")
logger.quiet("OS_ARCH: $OS_ARCH")
logger.quiet("JAVA_HOME: $JAVA_HOME")
logger.quiet("JDK_HOME: $JDK_HOME")
logger.quiet("java.runtime.version: ${javaRuntimeVersion}")
logger.quiet("java version: ${javaVersion}")
logger.quiet("java build number: ${javaBuildNumber}")
logger.quiet("jdk.runtime.version: ${jdkRuntimeVersion}")
logger.quiet("jdk version: ${jdkVersion}")
logger.quiet("jdk build number: ${jdkBuildNumber}")
logger.quiet("minimum jdk version: ${jfxBuildJdkVersionMin}")
logger.quiet("minimum jdk build number: ${jfxBuildJdkBuildnumMin}")
logger.quiet("HAS_JAVAFX_MODULES: $HAS_JAVAFX_MODULES")
logger.quiet("STUB_RUNTIME: $STUB_RUNTIME")
logger.quiet("CONF: $CONF")
logger.quiet("NUM_COMPILE_THREADS: $NUM_COMPILE_THREADS")
logger.quiet("COMPILE_TARGETS: $COMPILE_TARGETS")
logger.quiet("COMPILE_FLAGS_FILES: $COMPILE_FLAGS_FILES")
logger.quiet("HUDSON_JOB_NAME: $HUDSON_JOB_NAME")
logger.quiet("HUDSON_BUILD_NUMBER: $HUDSON_BUILD_NUMBER")
logger.quiet("PROMOTED_BUILD_NUMBER: $PROMOTED_BUILD_NUMBER")
logger.quiet("PRODUCT_NAME: $PRODUCT_NAME")
logger.quiet("RELEASE_VERSION: $RELEASE_VERSION")
logger.quiet("RELEASE_SUFFIX: $RELEASE_SUFFIX")
logger.quiet("RELEASE_VERSION_SHORT: $RELEASE_VERSION_SHORT")
logger.quiet("RELEASE_VERSION_LONG: $RELEASE_VERSION_LONG")
logger.quiet("RELEASE_VERSION_PADDED: $RELEASE_VERSION_PADDED")
logger.quiet("UPDATE_STUB_CACHE: $UPDATE_STUB_CACHE")

/******************************************************************************
 *                                                                            *
 *                Definition of Native Code Compilation Tasks                 *
 *                                                                            *
 *    - CCTask compiles native code. Specifically it will compile .m, .c,     *
 *      .cpp, or .cc files. It uses the headers provided by running           *
 *      'javac -h' plus additional platform specific headers. It will         *
 *      compile into .obj files.                                              *
 *    - LinkTask will perform native linking and create the .dll / .so /      *
 *      .dylib as necessary.                                                  *
 *                                                                            *
 *****************************************************************************/

// Save a reference to the buildSrc.jar file because we need it for actually
// compiling things, not just for the sake of this build script
// (such as generating the JSL files, etc)
ext.BUILD_SRC = rootProject.files("buildSrc/build/libs/buildSrc.jar")

/**
 * Convenience method for creating cc, link, and "native" tasks in the given project. These
 * tasks are parameterized by name, so that we can produce, for example, ccGlass, etc
 * named tasks.
 *
 * @param project The project to add tasks to
 * @param name The name of the project, such as "prism-common". This name is used
 *        in the name of the generated task, such as ccPrismCommon, and also
 *        in the name of the final library, such as libprism-common.dylib.
 */
void addNative(Project project, String name) {
    // TODO if we want to handle 32/64 bit windows in the same build,
    // Then we will need to modify the win compile target to be win32 or win64
    def capitalName = name.split("-").collect{it.capitalize()}.join()
    def nativeTask = project.task("native$capitalName", group: "Build") {
        description = "Generates JNI headers, compiles, and builds native dynamic library for $name for all compile targets"
    }
    def cleanTask = project.task("cleanNative$capitalName", type: Delete, group: "Build") {
        description = "Clean native objects for $name"
    }
    if (project.hasProperty("nativeAllTask")) project.nativeAllTask.dependsOn nativeTask
    project.assemble.dependsOn(nativeTask)
    if (project.hasProperty("cleanNativeAllTask")) project.cleanNativeAllTask.dependsOn cleanTask

    // Each of the different compile targets will be placed in a sub directory
    // of these root dirs, with the name of the dir being the name of the target
    def nativeRootDir = project.file("$project.buildDir/native/$name")
    def libRootDir = project.file("$project.buildDir/libs/$name")
    // For each compile target, create a cc / link pair
    compileTargets { t ->
        def targetProperties = project.rootProject.ext[t.upper]
        def library = targetProperties.library
        def properties = targetProperties.get(name)
        def nativeDir = file("$nativeRootDir/${t.name}")
        def headerDir = file("${project.buildDir}/gensrc/headers/${project.moduleName}")

        // If there is not a library clause in the properties, assume it is not wanted
        if (!targetProperties.containsKey(name)) {
            println("Ignoring native library ${name}. Not defined in ${t.name} project properties");
            return
        }

        // check for the property disable${name} = true
        def String disableKey = "disable${name}"
        def boolean disabled = targetProperties.containsKey(disableKey) ? targetProperties.get(disableKey) : false
        if (disabled) {
            println("Native library ${name} disabled in ${t.name} project properties");
            return
        }

        def variants = properties.containsKey("variants") ? properties.variants : [""];
        variants.each { variant ->
            def variantProperties = variant == "" ? properties : properties.get(variant)
            def capitalVariant = variant.capitalize()
            def ccOutput = variant == "" ? nativeDir : file("$nativeDir/$variant")
            def ccTask = project.task("cc${t.capital}$capitalName$capitalVariant", type: CCTask, group: "Build") {
                description = "Compiles native sources for ${name} for ${t.name}${capitalVariant != '' ? ' for variant ' + capitalVariant : ''}"
                matches = ".*\\.c|.*\\.cpp|.*\\.m|.*\\.cc"
                headers = headerDir
                output(ccOutput)
                params.addAll(variantProperties.ccFlags)
                compiler = variantProperties.compiler
                source(variantProperties.nativeSource)
                cleanTask.delete ccOutput
            }
            def linkTask = project.task("link${t.capital}$capitalName$capitalVariant", type: LinkTask, dependsOn: ccTask, group: "Build") {
                description = "Creates native dynamic library for $name for ${t.name}${capitalVariant != '' ? ' for variant ' + capitalVariant : ''}"
                objectDir = ccOutput
                linkParams.addAll(variantProperties.linkFlags)
                lib = file("$libRootDir/${t.name}/${variant == '' ? library(properties.lib) : library(variantProperties.lib)}")
                linker = variantProperties.linker
                cleanTask.delete "$libRootDir/${t.name}"
            }
            nativeTask.dependsOn(linkTask)
            if (IS_WINDOWS && t.name == "win") {
                def rcTask = project.task("rc$capitalName$capitalVariant", type: CompileResourceTask, group: "Build") {
                    description = "Compiles native sources for $name"
                    matches = ".*\\.rc"
                    compiler = variantProperties.rcCompiler
                    source(variantProperties.rcSource)
                    if (variantProperties.rcFlags) {
                        rcParams.addAll(variantProperties.rcFlags)
                    }
                    output(ccOutput)
                }
                linkTask.dependsOn rcTask;
            }
        }

        def useLipo = targetProperties.containsKey('useLipo') ? targetProperties.useLipo : false
        if (useLipo) {
            def lipoTask = project.task("lipo${t.capital}$capitalName", type: LipoTask, group: "Build") {
                description = "Creates native fat library for $name for ${t.name}"
                libDir = file("$libRootDir/${t.name}")
                lib = file("$libRootDir/${t.name}/${library(properties.lib)}")
            }
            nativeTask.dependsOn(lipoTask)
        }
    }
}

void addJSL(Project project, String name, String pkg, List<String> addExports, Closure compile) {
    def lowerName = name.toLowerCase()

    def modulePath = "${project.sourceSets.main.java.outputDir}"
    modulePath += File.pathSeparator + "${rootProject.projectDir}/modules/javafx.base/build/classes/java/main"
    def compileCompilers = project.task("compile${name}Compilers",
            type: JavaCompile,
            dependsOn: project.compileJava) {
        description = "Compile the $name JSL Compilers"

        classpath =
               project.files(project.sourceSets.jslc.java.outputDir) +
               project.configurations.antlr
        source = [project.file("src/main/jsl-$lowerName")]
        destinationDir = project.file("$project.buildDir/classes/jsl-compilers/$lowerName")

        options.compilerArgs.addAll([
            "-implicit:none",
            "--module-path", modulePath,
            "--add-modules=javafx.graphics"
            ])
        if (addExports != null) {
            options.compilerArgs.addAll(addExports)
        }
    }

    def generateShaders = project.task("generate${name}Shaders",
            dependsOn: compileCompilers) {
        description = "Generate $name shaders from JSL"
        def sourceDir = project.file("src/main/jsl-$lowerName")
        def destinationDir = project.file("$project.buildDir/gensrc/jsl-$lowerName")
        inputs.dir sourceDir
        outputs.dir destinationDir
        doLast {
            compile(sourceDir, destinationDir)
        }
    }

    def compileHLSLShaders = project.task("compile${name}HLSLShaders",
            dependsOn: generateShaders,
            type: CompileHLSLTask) {
        enabled = IS_WINDOWS
        description = "Compile $name HLSL files into .obj files"
        matches = ".*\\.hlsl"
        output project.file("$project.buildDir/hlsl/$name/$pkg")
        source project.file("$project.buildDir/gensrc/jsl-$lowerName/$pkg")
    }

    def processShaders = project.task("process${name}Shaders",
            dependsOn: [generateShaders, compileHLSLShaders],
            type: Copy,
            description: "Copy hlsl / frag shaders to build/resources/jsl-$lowerName") {
        from("$project.buildDir/hlsl/$name") {
            include "**/*.obj"
        }
        from("$project.buildDir/gensrc/jsl-$lowerName") {
            include("**/*.frag")
        }
        into project.moduleDir
    }

    project.processShaders.dependsOn(processShaders)
    project.sourceSets.shaders.output.dir("$project.buildDir/gensrc/jsl-$lowerName", builtBy: processShaders )

    def processShimsShaders = project.task("process${name}ShimsShaders",
            dependsOn: [generateShaders, compileHLSLShaders],
            type: Copy,
            description: "Copy hlsl / frag shaders to shims") {
        from("$project.buildDir/hlsl/$name") {
            include "**/*.obj"
        }
        from("$project.buildDir/gensrc/jsl-$lowerName") {
            include("**/*.frag")
        }
        into project.moduleShimsDir
    }

    project.processShimsShaders.dependsOn(processShimsShaders)

}

/**
 * Parses a JDK version string. The string must be in one of the following
 * two formats:
 *
 *     major.minor.subminor
 * or
 *     major.minor.subminor_update
 *
 * In both cases a list of 4 integers is returned, with element 3 set to
 * 0 in the former case.
 */
List parseJdkVersion(String version) {
    def arr = version.split("[_\\.]");
    def intArr = [];
    arr.each { s -> intArr += Integer.parseInt(s); }
    while (intArr.size() < 4) intArr += 0;
    return intArr;
}

/**
 * Returns -1, 0, or 1 depending on whether JDK version "a" is less than,
 * equal to, or grater than version "b".
 */
int compareJdkVersion(String a, String b) {
    def aIntArr = parseJdkVersion(a);
    def bIntArr = parseJdkVersion(b);

    for (int i = 0; i < 4; i++) {
        if (aIntArr[i] < bIntArr[i]) return -1;
        if (aIntArr[i] > bIntArr[i]) return  1;
    }
    return 0;
}

// Task to verify the minimum level of Java needed to build JavaFX
task verifyJava() {
    doLast {
        def status = compareJdkVersion(jdkVersion, jfxBuildJdkVersionMin);
        if (status < 0) {
            fail("java version mismatch: JDK version (${jdkVersion}) < minimum version (${jfxBuildJdkVersionMin})")
        } else if (status == 0) {
            def buildNum = Integer.parseInt(jdkBuildNumber)
            def minBuildNum = Integer.parseInt(jfxBuildJdkBuildnumMin)
            if (buildNum != 0 && buildNum < minBuildNum) {
                fail("JDK build number ($buildNum) < minimum build number ($minBuildNum)")
            }
        }
    }
}

task updateCacheIfNeeded() {
    // an empty task we can add to as needed for UPDATE_STUB_CACHE
}

task createTestArgfiles {
    // an empty task we can add to as needed
}


/*****************************************************************************
*        Project definitions (dependencies, etc)                             *
*****************************************************************************/

void addJCov(p, test) {
    test.doFirst {
        def jcovJVMArgument =
                "include=javafx," +
                "include=com.sun.javafx," +
                "include=com.sun.glass," +
                "include=com.sun.openpisces," +
                "include=com.sun.pisces," +
                "include=com.sun.prism," +
                "include=com.sun.scenario," +
                "include=com.sun.webkit," +
                "exclude=com," +
                "exclude=java," +
                "exclude=javax," +
                "exclude=\"**.test\"," +
                "exclude=\"**.*Test\"," +
                "file=build/reports/jcov/report.xml," +
                "merge=merge";
        test.jvmArgs("-javaagent:${p.configurations.testCompile.files.find { it.name.startsWith('jcov') }}=$jcovJVMArgument");
        p.mkdir p.file("build/reports/jcov")
    }
    test.doLast {
        def reportFile = p.file("build/reports/jcov/report.xml")
        if (reportFile.exists()) {
            p.javaexec {
                workingDir = p.file("build/reports/jcov")
                classpath = p.files(p.configurations.testCompile.files.find { it.name.startsWith('jcov') })
                main = "com.sun.tdk.jcov.Helper"
                args = [
                        "RepGen",
                        "-exclude", "\"**.test\"",
                        "-exclude", "\"**.*Test\"",
                        "-output", ".",
                        "-source", p.sourceSets.main.java.srcDirs.collect{p.file(it)}.join(":"),
                        "report.xml"
                ]
            }
        }
    }
}

allprojects {
    // Setup the repositories that we'll download libraries from. Maven Central is
    // just easy for most things. The custom "ivy" repo is for downloading SWT. The way it
    // works is to setup the download URL such that it will resolve to the actual jar file
    // to download. See SWT_FILE_NAME for the name of the jar that will be used as the
    // "artifact" in the pattern below. Note that the closed builds use different repositories
    // so if you are debugging a closed-build artifact related build issue, check out the
    // closed gradle file instead.
    if (!BUILD_CLOSED) {
        repositories {
            mavenCentral()
            ivy {
                url "http://download.eclipse.org/eclipse/updates/4.6/R-4.6.3-201703010400/plugins/"
                layout "pattern", {
                    artifact "[artifact].[ext]"
                }
            }
        }
    }

    if (!BUILD_CLOSED && IS_BUILD_LIBAV_STUBS) {
        repositories {
            ivy {
                url libAVRepositoryURL
                layout "pattern", {
                    artifact "[artifact].[ext]"
                }
            }
            ivy {
                url FFmpegRepositoryURL
                layout "pattern", {
                    artifact "[artifact].[ext]"
                }
            }
        }
    }

    // We want to configure all projects as java projects and use the same compile settings
    // etc, except for the root project which we just want to ignore (and for now media)
    if (project == rootProject) {
       return
    }
    if (project.path.startsWith(":apps")) {
        // Lets handle the apps tree differently, as it is a collection of ant builds,
        // and the ant importer collides with the 'apply plugin:java'
        return
    }

    // All of our projects are java projects

    apply plugin: "java"
    sourceCompatibility = 1.9

    // By default all of our projects require junit for testing so we can just
    // setup this dependency here.
    dependencies {
        testCompile group: "junit", name: "junit", version: "4.8.2"
        if (BUILD_CLOSED && DO_JCOV)  {
            testCompile name: "jcov"
        }
    }

    compileJava.dependsOn verifyJava

    // At the moment the ASM library shipped with Gradle that is used to
    // discover the different test classes fails on Java 8, so in order
    // to have sourceCompatibility set to 1.8 I have to also turn scanForClasses off
    // and manually specify the includes / excludes. At the moment we use
    // Java 7 but when we switch to 8 this will be needed, and probably again when
    // we start building with Java 9.
    test {
        executable = JAVA;
        enableAssertions = true;
        testLogging.exceptionFormat = "full";
        scanForTestClasses = false;
        include("**/*Test.*");
        if (BUILD_CLOSED && DO_JCOV) {
            addJCov(project, test)
        }

        if (IS_HEADLESS_TEST) {
            systemProperty 'glass.platform', 'Monocle'
            systemProperty 'monocle.platform', 'Headless'
            systemProperty 'prism.order', 'sw'
            systemProperty 'com.sun.javafx.gestures.zoom', 'true'
            systemProperty 'com.sun.javafx.gestures.rotate', 'true'
            systemProperty 'com.sun.javafx.gestures.scroll', 'true'
        }

        systemProperty 'unstable.test', IS_UNSTABLE_TEST
    }

    compileTestJava {
    }
}

// Qualified exports needed by javafx.* modules (excluding javafx.swing)
def qualExportsCore = [
    "--add-exports=java.desktop/sun.print=javafx.graphics",
]

// Qualified exports needed by javafx.swing
def qualExportsSwing = [
        "--add-exports=java.desktop/java.awt.dnd.peer=javafx.swing",
        "--add-exports=java.desktop/sun.awt=javafx.swing",
        "--add-exports=java.desktop/sun.awt.dnd=javafx.swing",
        "--add-exports=java.desktop/sun.awt.image=javafx.swing",
        "--add-exports=java.desktop/sun.java2d=javafx.swing",
        "--add-exports=java.desktop/sun.swing=javafx.swing",
]

// These strings define the module-source-path to be used in compilation.
// They need to contain the full paths to the sources and the * will be
// used to infer the module name that is used.
project.ext.defaultModuleSourcePath =
    cygpath(rootProject.projectDir.path + '/modules/*/src/main/java') +
        File.pathSeparator  +
    cygpath(rootProject.projectDir.path + '/modules/*/build/gensrc/{java,jsl-decora,jsl-prism}')

// graphics pass one
project.ext.defaultModuleSourcePath_GraphicsOne =
    cygpath(rootProject.projectDir.path + '/modules/*/src/main/java') +
        File.pathSeparator  +
    cygpath(rootProject.projectDir.path + '/modules/*/build/gensrc/{java,jsl-decora,jsl-prism}')

// web pass one
project.ext.defaultModuleSourcePath_WebOne =
    cygpath(rootProject.projectDir.path + '/modules/*/src/main/java')

// Compiling the test shim files too.
project.ext.defaultModuleSourcePathShim =
    cygpath(rootProject.projectDir.path + '/modules/*/src/{main,shims}/java') +
        File.pathSeparator  +
    cygpath(rootProject.projectDir.path + '/modules/*/build/gensrc/{java,jsl-decora,jsl-prism}')

// The "base" project is our first module and the most basic one required for
// all other modules. It is useful even for non-GUI applications.
project(":base") {
    project.ext.buildModule = true
    project.ext.includeSources = true
    project.ext.moduleRuntime = true
    project.ext.moduleName = "javafx.base"

    sourceSets {
        main
        shims
        test
    }

    dependencies {
        testCompile group: "junit", name: "junit", version: "4.8.2"
    }

    commonModuleSetup(project, [ 'base' ])

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

    // We need to take the VersionInfo.java file and replace the various
    // properties within it
    def replacements = [
        "BUILD_TIMESTAMP": BUILD_TIMESTAMP,
        "HUDSON_JOB_NAME": HUDSON_JOB_NAME,
        "HUDSON_BUILD_NUMBER": HUDSON_BUILD_NUMBER,
        "PROMOTED_BUILD_NUMBER": PROMOTED_BUILD_NUMBER,
        "PRODUCT_NAME": PRODUCT_NAME,
        "RELEASE_VERSION": RELEASE_VERSION,
        "RELEASE_SUFFIX": RELEASE_SUFFIX];
    task processVersionInfo(type: Copy, description: "Replace params in VersionInfo and copy file to destination") {
        doFirst { mkdir "$buildDir/gensrc/java" }
        from "src/main/version-info"
        into "$buildDir/gensrc/java/com/sun/javafx/runtime"
        filter {line->
            replacements.each() {k, v ->
                line = line.replace("@$k@", v.toString());
            }
            line
        }
    }

    // Make sure to include $buildDir/gensrc/java that we previously created.
    // We DO NOT want to include src/main/version-info

    sourceSets.main.java.srcDirs += "$buildDir/gensrc/java"

    compileJava.dependsOn processVersionInfo

    compileJava.options.compilerArgs.addAll(qualExportsCore)
}

// The graphics module is needed for any graphical JavaFX application. It requires
// the base module and includes the scene graph, layout, css, prism, windowing, etc.
// This is a fairly complicated module. There are many different types of native components
// that all need to be compiled.
project(":graphics") {

    project.ext.buildModule = true
    project.ext.includeSources = true
    project.ext.moduleRuntime = true
    project.ext.moduleName = "javafx.graphics"

    getConfigurations().create("antlr");

    sourceSets {
        jslc   // JSLC gramar subset
        main
        shims
        shaders // generated shaders (prism & decora)
        test
        stub
    }

    dependencies {
        stubCompile group: "junit", name: "junit", version: "4.8.2"

        antlr group: "org.antlr", name: "antlr-complete", version: "3.5.2"
    }

    project.ext.moduleSourcePath = defaultModuleSourcePath_GraphicsOne
    project.ext.moduleSourcePathShim = defaultModuleSourcePathShim

    commonModuleSetup(project, [ 'base', 'graphics' ])

    List<String> decoraAddExports = [
            '--add-exports=javafx.graphics/com.sun.scenario.effect=ALL-UNNAMED',
            '--add-exports=javafx.graphics/com.sun.scenario.effect.light=ALL-UNNAMED',
            '--add-exports=javafx.graphics/com.sun.scenario.effect.impl.state=ALL-UNNAMED'
            ]
    /*
    Graphics compilation is "complicated" by the generated shaders.

    We have two shader groups - Decora and Prism.

    The shader groups each will generate a custom compiler that
    then genarates the shader code. These compilers rely on the JSLC
    gramar parser which is antlr generated and compile separately.

    The decora compiler relies on compileJava - which is sourceSet.main.java
    It also accesses module private packages, so will need add-exports

    Once the shader java code is generated, we can compileFullJava

    After that, we can generate the required native header and then build the native code
    */

    project.task("processShaders") {
        // an empty task to hang the prism and decora shaders on
    }

    project.task("processShimsShaders") {
        // an empty task to hang the prism and decora shaders on
    }

    compileShimsJava.dependsOn("processShimsShaders")

    // Generate the JSLC support grammar
    project.task("generateGrammarSource", type: JavaExec) {
        // use antlr to generate our grammar.
        // note: the antlr plugin creates some issues with the other compiles
        // so we will do this by hand

        File wd = file(project.projectDir.path + "/src/jslc/antlr")

        executable = JAVA
        classpath = project.configurations.antlr
        workingDir = wd
        main = "org.antlr.Tool"

        args = [
            "-Xconversiontimeout",
            "30000",
            "-o",
            "$buildDir/gensrc/antlr",
            "com/sun/scenario/effect/compiler/JSL.g" ]

        inputs.dir wd
        outputs.dir file("$buildDir/gensrc/antlr")
    }
    sourceSets.jslc.java.srcDirs += "$buildDir/gensrc/antlr"

    // and compile the JSLC support classes
    compileJslcJava.dependsOn(generateGrammarSource)
    compileJslcJava.classpath = project.configurations.antlr

    compileJava.dependsOn(compileJslcJava)

    // this task is the "second pass" compile of all of the module classes
    project.task("compileFullJava", type: JavaCompile, dependsOn: processShaders) {
        description = "Compile all of the graphics java classes - main and shaders"

        classpath = configurations.compile

        source = project.sourceSets.main.java.srcDirs
        source += "$buildDir/gensrc/java"
        source += project.sourceSets.shaders.output

        destinationDir = project.sourceSets.main.java.outputDir
        options.compilerArgs.addAll([
            '-h', "$buildDir/gensrc/headers/",  // Note: this creates the native headers
            '-implicit:none',
            '--module-source-path', defaultModuleSourcePath
            ] )
        options.compilerArgs.addAll(qualExportsCore)
    }
    classes.dependsOn(compileFullJava)

    project.sourceSets.shims.java.srcDirs += project.sourceSets.shaders.output
    project.sourceSets.shims.java.srcDirs += "$buildDir/gensrc/jsl-prism"
    project.sourceSets.shims.java.srcDirs += "$buildDir/gensrc/jsl-decora"

    compileShimsJava.dependsOn(compileFullJava)

    // Create a single "native" task which will depend on all the individual native tasks for graphics
    project.ext.nativeAllTask = task("native", group: "Build", description: "Compiles and Builds all native libraries for Graphics");
    project.ext.cleanNativeAllTask = task("cleanNative", group: "Build", description: "Clean all native libraries and objects for Graphics");

    // Add tasks for native compilation
    addNative(project, "glass");
    addNative(project, "prism")
    addNative(project, "prismSW")
    addNative(project, "font")
    addNative(project, "iio")
    addNative(project, "prismES2")

    if (IS_COMPILE_PANGO) {
        addNative(project, "fontFreetype")
        addNative(project, "fontPango")
    }

    if (IS_WINDOWS) {
        addNative(project, "prismD3D")
        // TODO need to hook this up to be executed only if PassThroughVS.h is missing or PassThroughVS.hlsl is changed
        task generateD3DHeaders(group: "Build") {
            enabled = IS_WINDOWS
            inputs.file "src/main/native-prism-d3d/hlsl/Mtl1PS.hlsl"
            inputs.file "src/main/native-prism-d3d/hlsl/Mtl1VS.hlsl"
            inputs.file "src/main/native-prism-d3d/PassThroughVS.hlsl"
            outputs.dir "$buildDir/headers/PrismD3D/"
            outputs.dir "$buildDir/headers/PrismD3D/hlsl/"
            description = "Generate headers by compiling hlsl files"
            doLast {
                mkdir file("$buildDir/headers/PrismD3D/hlsl")
                def PS_3D_SRC = file("src/main/native-prism-d3d/hlsl/Mtl1PS.hlsl")
                def VS_3D_SRC = file("src/main/native-prism-d3d/hlsl/Mtl1VS.hlsl")
                def PASSTHROUGH_VS_SRC = file("src/main/native-prism-d3d/PassThroughVS.hlsl")
                def jobs = [
                        ["$FXC", "/nologo", "/T", "vs_3_0", "/Fh", "$buildDir/headers/PrismD3D/PassThroughVS.h", "/E", "passThrough", "$PASSTHROUGH_VS_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS.h", "/DSpec=0", "/DSType=0", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_i.h", "/DSpec=0", "/DSType=0", "/DIllumMap=1", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_s1n.h", "/DSpec=1", "/DSType=0", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_s2n.h", "/DSpec=2", "/DSType=0", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_s3n.h", "/DSpec=3", "/DSType=0", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_s1t.h", "/DSpec=1", "/DSType=1", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_s2t.h", "/DSpec=2", "/DSType=1", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_s3t.h", "/DSpec=3", "/DSType=1", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_s1c.h", "/DSpec=1", "/DSType=2", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_s2c.h", "/DSpec=2", "/DSType=2", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_s3c.h", "/DSpec=3", "/DSType=2", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_s1m.h", "/DSpec=1", "/DSType=3", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_s2m.h", "/DSpec=2", "/DSType=3", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_s3m.h", "/DSpec=3", "/DSType=3", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_b1n.h", "/DSpec=1", "/DSType=0", "/DBump=1", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_b2n.h", "/DSpec=2", "/DSType=0", "/DBump=1", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_b3n.h", "/DSpec=3", "/DSType=0", "/DBump=1", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_b1t.h", "/DSpec=1", "/DSType=1", "/DBump=1", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_b2t.h", "/DSpec=2", "/DSType=1", "/DBump=1", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_b3t.h", "/DSpec=3", "/DSType=1", "/DBump=1", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_b1c.h", "/DSpec=1", "/DSType=2", "/DBump=1", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_b2c.h", "/DSpec=2", "/DSType=2", "/DBump=1", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_b3c.h", "/DSpec=3", "/DSType=2", "/DBump=1", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_b1m.h", "/DSpec=1", "/DSType=3", "/DBump=1", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_b2m.h", "/DSpec=2", "/DSType=3", "/DBump=1", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_b3m.h", "/DSpec=3", "/DSType=3", "/DBump=1", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_s1ni.h", "/DSpec=1", "/DSType=0", "/DIllumMap=1", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_s2ni.h", "/DSpec=2", "/DSType=0", "/DIllumMap=1", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_s3ni.h", "/DSpec=3", "/DSType=0", "/DIllumMap=1", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_s1ti.h", "/DSpec=1", "/DSType=1", "/DIllumMap=1", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_s2ti.h", "/DSpec=2", "/DSType=1", "/DIllumMap=1", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_s3ti.h", "/DSpec=3", "/DSType=1", "/DIllumMap=1", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_s1ci.h", "/DSpec=1", "/DSType=2", "/DIllumMap=1", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_s2ci.h", "/DSpec=2", "/DSType=2", "/DIllumMap=1", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_s3ci.h", "/DSpec=3", "/DSType=2", "/DIllumMap=1", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_s1mi.h", "/DSpec=1", "/DSType=3", "/DIllumMap=1", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_s2mi.h", "/DSpec=2", "/DSType=3", "/DIllumMap=1", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_s3mi.h", "/DSpec=3", "/DSType=3", "/DIllumMap=1", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_b1ni.h", "/DSpec=1", "/DSType=0", "/DBump=1", "/DIllumMap=1", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_b2ni.h", "/DSpec=2", "/DSType=0", "/DBump=1", "/DIllumMap=1", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_b3ni.h", "/DSpec=3", "/DSType=0", "/DBump=1", "/DIllumMap=1", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_b1ti.h", "/DSpec=1", "/DSType=1", "/DBump=1", "/DIllumMap=1", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_b2ti.h", "/DSpec=2", "/DSType=1", "/DBump=1", "/DIllumMap=1", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_b3ti.h", "/DSpec=3", "/DSType=1", "/DBump=1", "/DIllumMap=1", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_b1ci.h", "/DSpec=1", "/DSType=2", "/DBump=1", "/DIllumMap=1", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_b2ci.h", "/DSpec=2", "/DSType=2", "/DBump=1", "/DIllumMap=1", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_b3ci.h", "/DSpec=3", "/DSType=2", "/DBump=1", "/DIllumMap=1", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_b1mi.h", "/DSpec=1", "/DSType=3", "/DBump=1", "/DIllumMap=1", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_b2mi.h", "/DSpec=2", "/DSType=3", "/DBump=1", "/DIllumMap=1", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_b3mi.h", "/DSpec=3", "/DSType=3", "/DBump=1", "/DIllumMap=1", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "vs_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1VS_Obj.h", "/DVertexType=ObjVertex", "$VS_3D_SRC"]
                ]
                final ExecutorService executor = Executors.newFixedThreadPool(Integer.parseInt(project.NUM_COMPILE_THREADS.toString()));
                final CountDownLatch latch = new CountDownLatch(jobs.size());
                List futures = new ArrayList<Future>();
                jobs.each { cmd ->
                    futures.add(executor.submit(new Runnable() {
                        @Override public void run() {
                            try {
                                exec {
                                    commandLine cmd
                                }
                            } finally {
                                latch.countDown();
                            }
                        }
                    }));
                }
                latch.await();
                // Looking for whether an exception occurred while executing any of the futures.
                // By calling "get()" on each future an exception will be thrown if one had occurred
                // on the background thread.
                futures.each {it.get();}
            }
        }

        ccWinPrismD3D.dependsOn generateD3DHeaders
    }

    // The Decora and Prism JSL files have to be generated in a very specific set of steps.
    //      1) Compile the *Compile.java classes. These live in src/main/jsl-* and will be
    //         output to $buildDir/classes/jsl-compilers/* (where * == decora or prism).
    //      2) Generate source files from the JSL files contained in src/main/jsl-*. These
    //         will be output to $buildDir/gensrc/jsl-*
    //      3) Compile the JSL Java sources in $buildDir/gensrc/jsl-* and put the output
    //         into classes/jsl-*
    //      4) Compile the native JSL sources in $buildDir/gensrc/jsl-* and put the obj
    //         files into native/jsl-* and the resulting library into libs/jsl-*.dll|so|dylib
    //      5) Modify the jar step to include classes/jsl-*
    // The native library must be copied over during SDK creation time in the "sdk" task. In
    // addition to these steps, the clean task is created. Note that I didn't bother to create
    // a new task for each of the decora files, preferring instead just to create a rule?? Also
    // need "clean" tasks for each compile task.

    def modulePath = "${project.sourceSets.main.java.outputDir}"
    modulePath += File.pathSeparator + "${rootProject.projectDir}/modules/javafx.base/build/classes/java/main"
    addJSL(project, "Decora", "com/sun/scenario/effect/impl/hw/d3d/hlsl", decoraAddExports) { sourceDir, destinationDir ->
        [[fileName: "ColorAdjust", generator: "CompileJSL", outputs: "-all"],
         [fileName: "Brightpass", generator: "CompileJSL", outputs: "-all"],
         [fileName: "SepiaTone", generator: "CompileJSL", outputs: "-all"],
         [fileName: "PerspectiveTransform", generator: "CompileJSL", outputs: "-all"],
         [fileName: "DisplacementMap", generator: "CompileJSL", outputs: "-all"],
         [fileName: "InvertMask", generator: "CompileJSL", outputs: "-all"],
         [fileName: "Blend", generator: "CompileBlend", outputs: "-all"],
         [fileName: "PhongLighting", generator: "CompilePhong", outputs: "-all"],
         [fileName: "LinearConvolve", generator: "CompileLinearConvolve", outputs: "-hw"],
         [fileName: "LinearConvolveShadow", generator: "CompileLinearConvolve", outputs: "-hw"]].each { settings ->
            javaexec {
                executable = JAVA
                workingDir = project.projectDir
                main = settings.generator
                classpath = configurations.compile + configurations.antlr
                classpath += files(project.sourceSets.jslc.java.outputDir)

                classpath += files("${project.projectDir}/src/jslc/resources")

                classpath += files("$buildDir/classes/jsl-compilers/decora")
                jvmArgs += "--module-path=$modulePath"
                jvmArgs += "--add-modules=javafx.graphics"
                jvmArgs += decoraAddExports
                args += ["-i", sourceDir, "-o", destinationDir, "-t", "-pkg", "com/sun/scenario/effect", "$settings.outputs", "$settings.fileName"]
            }
        }
    }


    task nativeDecora(dependsOn: compileDecoraHLSLShaders, group: "Build") {
        description = "Generates JNI headers, compiles, and builds native dynamic library for Decora"
    }
    task cleanNativeDecora(type: Delete, group: "Build") {
        description = "Clean native objects for Decora"
    }

    def headerDir = file("$buildDir/gensrc/headers/javafx.graphics")
    def nativeRootDir = project.file("$project.buildDir/native/jsl-decora")
    def libRootDir = project.file("$project.buildDir/libs/jsl-decora")
    // For each compile target, create cc and link tasks
    compileTargets { t ->
        def target = t.name
        def upperTarget = t.upper
        def capitalTarget = t.capital
        def targetProperties = rootProject.ext[upperTarget];
        def library = targetProperties.library
        def properties = targetProperties.get('decora')
        def nativeDir = file("$nativeRootDir/$target");

        def variants = properties.containsKey("variants") ? properties.variants : [""];
        variants.each { variant ->
            def variantProperties = variant == "" ? properties : properties.get(variant)
            def capitalVariant = variant.capitalize()
            def ccOutput = variant == "" ? nativeDir : file("$nativeDir/$variant")

            def ccTask = task("compileDecoraNativeShaders$capitalTarget$capitalVariant", type: CCTask ) {
                description = "Compiles Decora SSE natives for ${t.name}${capitalVariant != '' ? ' for variant ' + capitalVariant : ''}"
                matches = ".*\\.cc"
                source file("$buildDir/gensrc/jsl-decora")
                source file(project.projectDir.path + "/src/main/native-decora")
                headers = headerDir
                params.addAll(variantProperties.ccFlags)
                output(ccOutput)
                compiler = variantProperties.compiler
                cleanNativeDecora.delete ccOutput
            }

            def linkTask = task("linkDecoraNativeShaders$capitalTarget$capitalVariant", type: LinkTask, dependsOn: ccTask) {
                description = "Creates native dynamic library for Decora SSE ${t.name}${capitalVariant != '' ? ' for variant ' + capitalVariant : ''}"
                objectDir = ccOutput
                linkParams.addAll(variantProperties.linkFlags)
                lib = file("$libRootDir/$t.name/${library(variantProperties.lib)}")
                linker = variantProperties.linker
                cleanNativeDecora.delete "$libRootDir/$t.name/"
            }

            if (IS_WINDOWS && target == "win") {
                def rcTask = project.task("rcDecoraNativeShaders$capitalTarget$capitalVariant", type: CompileResourceTask) {
                    description = "Compiles native sources for Decora SSE"
                    matches = ".*\\.rc"
                    compiler = variantProperties.rcCompiler
                    source(variantProperties.rcSource)
                    if (variantProperties.rcFlags) {
                        rcParams.addAll(variantProperties.rcFlags)
                    }
                    output(ccOutput)
                }
                linkTask.dependsOn rcTask;
            }

            nativeDecora.dependsOn(linkTask)
        }
    }

    // Prism JSL
    addJSL(project, "Prism", "com/sun/prism/d3d/hlsl", null) { sourceDir, destinationDir ->
        def inputFiles = fileTree(dir: sourceDir)
        inputFiles.include "**/*.jsl"
        inputFiles.each { file ->
            javaexec {
                executable = JAVA
                workingDir = project.projectDir
                main = "CompileJSL"
                classpath = configurations.compile + configurations.antlr
                classpath += files(project.sourceSets.jslc.java.outputDir)
                classpath += files(project.sourceSets.jslc.resources)
                classpath += files("$buildDir/classes/jsl-compilers/prism",
                    project.projectDir.path + "/src/main/jsl-prism") // for the .stg
                args = ["-i", sourceDir, "-o", destinationDir, "-t", "-pkg", "com/sun/prism", "-d3d", "-es2", "-name", "$file"]
            }
        }
    }

    nativePrism.dependsOn compilePrismHLSLShaders;

    project.nativeAllTask.dependsOn nativeDecora
    project.cleanNativeAllTask.dependsOn cleanNativeDecora
    assemble.dependsOn nativeDecora
    processResources.dependsOn processDecoraShaders, processPrismShaders

    test {
        def cssDir = file("$buildDir/classes/java/main/${moduleName}/javafx")
        jvmArgs "-Djavafx.toolkit=test.com.sun.javafx.pgstub.StubToolkit",
            "-DCSS_META_DATA_TEST_DIR=$cssDir"
        enableAssertions = true
        testLogging.exceptionFormat = "full"
        scanForTestClasses = false
        include "**/*Test.*"
        if (BUILD_CLOSED && DO_JCOV) {
            addJCov(project, test)
        }
    }

    // To enable the IDEs to all be happy (no red squiggles) we need to have the libraries
    // available in some known location. Maybe in the future the Gradle plugins to each
    // of the IDEs will be good enough that we won't need this hack anymore.
    classes {
        doLast {
            // Copy all of the download libraries to the libs directory for the sake of the IDEs
            File libsDir = rootProject.file("build/libs");

            // In some IDEs (Eclipse for example), touching these libraries
            // cauese a full build within the IDE. When gradle is used
            // outside of the IDE, for example to build the native code,
            // a full rebuild is caused within the IDE. The fix is to check
            // for the presence of the target files in the lib directory
            // and not copy the files if all are present.

            libsDir.mkdirs();

            def allLibsPresent = true
            def libNames = [ "antlr-complete-3.5.2.jar" ]
            libNames.each { name ->
                File f = new File(libsDir, name)
                if (!f.exists()) allLibsPresent = false
            }
            if (allLibsPresent) return;

            for (File f : [configurations.compile.files, configurations.antlr.files].flatten()) {
                copy {
                    into libsDir
                    from f.getParentFile()
                    include "**/antlr-complete-3.5.2.jar"
                    includeEmptyDirs = false
                }
            }
        }
    }

    compileJava.options.compilerArgs.addAll(qualExportsCore)
}

project(":controls") {
    project.ext.buildModule = true
    project.ext.includeSources = true
    project.ext.moduleRuntime = true
    project.ext.moduleName = "javafx.controls"

    sourceSets {
        main
        shims
        test
    }

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

    commonModuleSetup(project, [ 'base', 'graphics', 'controls' ])

    dependencies {
        testCompile project(":graphics").sourceSets.test.output
        testCompile project(":base").sourceSets.test.output
    }

    test {
        def cssDir = file("$buildDir/classes/java/main/${moduleName}/javafx")
        jvmArgs "-Djavafx.toolkit=test.com.sun.javafx.pgstub.StubToolkit",
            "-DCSS_META_DATA_TEST_DIR=$cssDir"
    }

    def modulePath = "${project.sourceSets.main.java.outputDir}"
    modulePath += File.pathSeparator + "${rootProject.projectDir}/modules/javafx.graphics/build/classes/java/main"
    modulePath += File.pathSeparator + "${rootProject.projectDir}/modules/javafx.base/build/classes/java/main"
    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"
                main = "com.sun.javafx.css.parser.Css2Bin"
                args css
            }
        }
      }
    }

    processShimsResources.dependsOn(project.task("copyShimBss", type: Copy) {
        from project.moduleDir
        into project.moduleShimsDir
        include "**/*.bss"
    })

    compileJava.options.compilerArgs.addAll(qualExportsCore)
}

project(":swing") {

    tasks.all {
        if (!COMPILE_SWING) it.enabled = false
    }

    project.ext.buildModule = COMPILE_SWING
    project.ext.includeSources = true
    project.ext.moduleRuntime = true
    project.ext.moduleName = "javafx.swing"

    sourceSets {
        main
        //shims // no test shims needed
        test
    }

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

    commonModuleSetup(project, [ 'base', 'graphics', 'swing' ])

    dependencies {
    }

    test {
        enabled = IS_FULL_TEST && IS_AWT_TEST

        if (!HAS_JAVAFX_MODULES) {
            jvmArgs += qualExportsSwing
        }
    }

    compileJava.options.compilerArgs.addAll(qualExportsCore)
    compileJava.options.compilerArgs.addAll(qualExportsSwing)
}

project(":swt") {
    tasks.all {
        if (!COMPILE_SWT) it.enabled = false
    }

    // javafx.swt is an automatic module
    project.ext.buildModule = false

    commonModuleSetup(project, [ 'base', 'graphics' ])

    dependencies {
        compile name: SWT_FILE_NAME
    }

    classes {
        doLast {
            // Copy all of the download libraries to libs directory for the sake of the IDEs
            File libsDir = rootProject.file("build/libs");
            File swtLib = new File(libsDir, "swt-debug.jar")
            libsDir.mkdirs();

            // Skip copy if file is present.
            if (swtLib.exists()) return;

            for (File f : configurations.compile.files) {
                // Have to rename the swt jar because it is some platform specific name but
                // for the sake of the IDEs we need to have a single stable name that works
                // on every platform
                copy {
                    into libsDir
                    from f.getParentFile()
                    include "**/*swt*.jar"
                    includeEmptyDirs = false
                    rename ".*swt.*jar", "swt-debug\\.jar"
                }
            }
        }
    }

    compileJava.options.compilerArgs.addAll([
            "--add-exports=javafx.graphics/com.sun.glass.ui=ALL-UNNAMED",
            "--add-exports=javafx.graphics/com.sun.javafx.cursor=ALL-UNNAMED",
            "--add-exports=javafx.graphics/com.sun.javafx.embed=ALL-UNNAMED",
            "--add-exports=javafx.graphics/com.sun.javafx.stage=ALL-UNNAMED",
            "--add-exports=javafx.graphics/com.sun.javafx.tk=ALL-UNNAMED",
            ])

    test {
        //enabled = IS_FULL_TEST && IS_SWT_TEST
        enabled = false // FIXME: JIGSAW -- support this with modules
        logger.info("JIGSAW Testing disabled for swt")

        if (IS_MAC) {
            enabled = false
            logger.info("SWT tests are disabled on MAC, because Gradle test runner does not handle -XstartOnFirstThread properly (https://issues.gradle.org/browse/GRADLE-3290).")
        }
    }
}

project(":fxml") {
    project.ext.buildModule = true
    project.ext.includeSources = true
    project.ext.moduleRuntime = true
    project.ext.moduleName = "javafx.fxml"

    sourceSets {
        main
        shims
        test
    }

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

    commonModuleSetup(project, [ 'base', 'graphics', 'controls', 'fxml' ])


    dependencies {
        testCompile project(":graphics").sourceSets.test.output
        testCompile project(":base").sourceSets.test.output
    }

    test {
        // StubToolkit is not *really* needed here, but because some code inadvertently invokes performance
        // tracker and this attempts to fire up the toolkit and this looks for native libraries and fails,
        // we have to use the stub toolkit for now.
        jvmArgs "-Djavafx.toolkit=test.com.sun.javafx.pgstub.StubToolkit"
        // FIXME: change this to also allow JDK 9 boot jdk
        classpath += files("$JDK_HOME/jre/lib/ext/nashorn.jar")
    }

    compileJava.options.compilerArgs.addAll(qualExportsCore)
}

project(":fxpackagerservices") {
    project.ext.buildModule = COMPILE_FXPACKAGER
    project.ext.includeSources = true
    project.ext.moduleRuntime = false
    project.ext.moduleName = "jdk.packager.services"

    sourceSets {
        main
        //shims // no test shims needed
        test
    }

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

    commonModuleSetup(project, [ 'base', 'graphics', 'controls' ])

    tasks.all {
        if (!COMPILE_FXPACKAGER) it.enabled = false
    }


    compileTestJava.enabled = false // FIXME: JIGSAW -- support this with modules

    test {
        enabled = false // FIXME: JIGSAW -- support this with modules
        logger.info("JIGSAW Testing disabled for fxpackagerservices")
    }
}

project(":fxpackager") {
    project.ext.buildModule = COMPILE_FXPACKAGER
    project.ext.includeSources = true
    project.ext.moduleRuntime = false
    project.ext.moduleName = "jdk.packager"

    sourceSets {
        main
        //shims // no test shims needed
        antplugin {
            java {
                compileClasspath += main.output
                runtimeClasspath += main.output
            }
        }
        test
    }

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

    commonModuleSetup(project, [ 'base', 'graphics', 'controls', 'fxpackagerservices', 'fxpackager' ])

    manifest {
        attributes(
                "Main-Class": "com.sun.javafx.tools.packager.Main"
        )
    }

    tasks.all {
        if (!COMPILE_FXPACKAGER) it.enabled = false
    }

    // fxpackager has a dependency on ant in order to build the ant jar,
    // and as such needs to point to the apache binary repository
    if (!BUILD_CLOSED) {
        repositories {
            maven {
                url "https://repository.apache.org"
            }
        }
    }

    dependencies {
        antpluginCompile group: "org.apache.ant", name: "ant", version: "1.8.2"

        testCompile project(":controls"),
            group: "org.apache.ant", name: "ant", version: "1.8.2",
            sourceSets.antplugin.output
    }

    //Note: these should be reflected in the module-info additions passed to the JDK
    compileJava.options.compilerArgs.addAll([
            "--add-exports=java.base/sun.security.timestamp=jdk.packager",
            "--add-exports=java.base/sun.security.x509=jdk.packager",

            // Note: retain jdk.jlink qualified export for cases where the JDK does
            // not contain the jdk.packager module.
            "--add-exports=jdk.jlink/jdk.tools.jlink.internal.packager=jdk.packager",

            // Note: not in extras...
            "--add-exports=java.base/sun.security.pkcs=jdk.packager",
            "--add-exports=java.logging/java.util.logging=jdk.packager",
            ])

    compileAntpluginJava.dependsOn([ compileJava, processResources ])
    compileAntpluginJava.options.compilerArgs.addAll(
        computeModulePathArgs("antlib", project.moduleChain, false))

    task buildVersionFile() {
        File dir = new File("${project.projectDir}/build/resources/antplugin/resources");
        File versionFile = new File(dir, "/version.properties");
        doLast {
            dir.mkdirs()
            if (!versionFile.exists()) {
                versionFile << "version=$RELEASE_VERSION\n"
            }
        }
        outputs.file(versionFile)
    }

    // When producing the ant-javafx.jar, we need to relocate a few class files
    // from their normal location to a resources/classes or resources/web-files
    // location
    task antpluginJar(type: Jar, dependsOn: [ compileJava, jar, compileAntpluginJava, buildVersionFile ]) {
        includeEmptyDirs = false
        archiveName = "ant-javafx.jar"

        from (sourceSets.antplugin.output) {
            eachFile { FileCopyDetails details ->
                if (details.path.startsWith("com/javafx/main")) {
                    details.path = "resources/classes/$details.path"
                }
            }
        }

        from (sourceSets.main.resources) {
            includes = [ "com/sun/javafx/tools/ant/**" ]
        }

        from (sourceSets.main.output.resourcesDir) {
            includes = [ "resources/web-files/**" ]
        }
    }

    assemble.dependsOn(antpluginJar)

    // The "man" task will create a $buildDir/man containing the man
    // files for the system being built
    task man(type: Copy) {
        includeEmptyDirs = false
        enabled = (IS_LINUX || IS_MAC) && COMPILE_FXPACKAGER
        from "src/main/man"
        into "$buildDir/man"
        exclude "**/*.html"
        if (IS_MAC) exclude "**/ja_JP.UTF-8/**"
    }
    processResources.dependsOn man

    String buildClassesDir = "${sourceSets.main.java.outputDir}/${moduleName}"

    // Compile the native launchers. These are included in jdk.packager.jmod.
    if (IS_WINDOWS && COMPILE_FXPACKAGER) {
        task buildWinLauncher(type: CCTask, group: "Build") {
            description = "Compiles native sources for the application co-bundle launcher";
            matches = "WinLauncher\\.cpp";
            params.addAll(WIN.launcher.ccFlags);
            output(file("$buildDir/native/WinLauncher"));
            source(file("src/main/native/launcher/win"));
            compiler = WIN.launcher.compiler
            exe = true;
            linkerOptions.addAll(WIN.launcher.linkFlags);
        }

        task copyWinLauncher(type: Copy, group: "Build", dependsOn: buildWinLauncher) {
            from "$buildDir/native/WinLauncher/WinLauncher.exe"
            from "$MSVCR"
            from "$MSVCP"
            into "${buildClassesDir}/com/oracle/tools/packager/windows"
        }

        task compileWinLibrary(type: CCTask, group: "Build") {
            description = "Compiles native sources for the application co-bundle launcher library";
            matches = ".*\\.cpp"
            source(file("src/main/native/library/common"));
            params.addAll(WIN.launcherlibrary.ccFlags)
            output(file("$buildDir/native/WinLauncher/obj"));
            compiler = WIN.launcherlibrary.compiler
        }

        task linkWinLibrary(type: LinkTask, group: "Build", dependsOn: compileWinLibrary) {
            description = "Links native sources for the application co-bundle launcher library";
            objectDir = file("$buildDir/native/WinLauncher/obj")
            linkParams.addAll(WIN.launcherlibrary.linkFlags);
            lib = file("$buildDir/native/WinLauncher/packager.dll")
            linker = WIN.launcherlibrary.linker
        }

        task copyWinLibrary(type: Copy, group: "Build", dependsOn: linkWinLibrary) {
            from "$buildDir/native/WinLauncher/packager.dll"
            into "${buildClassesDir}/com/oracle/tools/packager/windows"
        }

        task buildWinLauncherSvc(type: CCTask, group: "Build") {
            description = "Compiles native sources for the application co-bundle launcher";
            matches = "WinLauncherSvc\\.cpp";
            params.addAll(WIN.launcher.ccFlags);
            output(file("$buildDir/native/WinLauncherSvc"));
            source(file("src/main/native/service/win"));
            compiler = WIN.launcher.compiler
            exe = true;
            linkerOptions.addAll(WIN.launcher.linkFlags);
        }

        task copyWinLauncherSvc(type: Copy, group: "Build", dependsOn: buildWinLauncherSvc) {
            from "$buildDir/native/WinLauncherSvc/WinLauncherSvc.exe"
            into "${buildClassesDir}/com/oracle/tools/packager/windows"
        }

        task compileLauncher(dependsOn: [copyWinLauncher, copyWinLibrary, copyWinLauncherSvc])
    } else if (IS_MAC && COMPILE_FXPACKAGER) {
        task buildMacLauncher(type: CCTask, group: "Build") {
            description = "Compiles native sources for the application co-bundle launcher"
            matches = ".*\\.m"
            source file("src/main/native/launcher/mac")
            params.addAll(MAC.launcher.ccFlags)
            compiler = MAC.launcher.compiler
            output(file("${buildClassesDir}/com/oracle/tools/packager/mac"))
            outputs.file(file("${buildClassesDir}/main/com/oracle/tools/packager/mac/JavaAppLauncher"))
            eachOutputFile = { f ->
                return new File(f.getParent(), "JavaAppLauncher")
            }
        }
        task compileMacLibrary(type: CCTask, group: "Build") {
            description = "Compiles native sources for the application co-bundle launcher library"
            matches = ".*\\.cpp|.*\\.mm"
            source file("src/main/native/library/common");
            params.addAll(MAC.launcherlibrary.ccFlags)
            compiler = MAC.launcherlibrary.compiler
            output(file("$buildDir/native/maclauncher/obj"))
        }
        task linkMacLibrary(type: LinkTask, group: "Build", dependsOn: compileMacLibrary) {
            description = "Links native sources for the application co-bundle launcher library"
            objectDir = file("$buildDir/native/maclauncher/obj")
            linkParams.addAll(MAC.launcherlibrary.linkFlags)
            linker = MAC.launcherlibrary.linker
            lib = file("${buildClassesDir}/com/oracle/tools/packager/mac/libpackager.dylib")
        }
        task compileLauncher(dependsOn: [buildMacLauncher, linkMacLibrary])
    } else if (IS_LINUX && COMPILE_FXPACKAGER) {
        task compileLinuxLauncher(type: CCTask, group: "Build") {
            description = "Compiles native sources for the application co-bundle launcher"
            matches = ".*\\.cpp"
            source file("src/main/native/launcher/linux")
            params.addAll(LINUX.launcher.ccFlags)
            compiler = LINUX.launcher.compiler
            output(file("$buildDir/native/linuxlauncher/launcherobj"))
        }
        task linkLinuxLauncher(type: LinkTask, dependsOn: compileLinuxLauncher, group: "Build") {
            description = "Links native dynamic library for the application co-bundle launcher"
            objectDir = file("$buildDir/native/linuxlauncher/launcherobj")
            linkParams.addAll(LINUX.launcher.linkFlags)
            linker = LINUX.launcher.linker
            lib = file("${buildClassesDir}/com/oracle/tools/packager/linux/JavaAppLauncher")
        }
        task compileLinuxLibrary(type: CCTask, group: "Build") {
            description = "Compiles native sources for the application co-bundle launcher library"
            matches = ".*\\.cpp"
            source file("src/main/native/library/common")
            params.addAll(LINUX.launcherlibrary.ccFlags)
            compiler = LINUX.launcherlibrary.compiler
            output(file("$buildDir/native/linuxlauncher/obj"))
        }
        task linkLinuxLibrary(type: LinkTask, dependsOn: compileLinuxLibrary, group: "Build") {
            description = "Links native dynamic library for the application co-bundle launcher library"
            objectDir = file("$buildDir/native/linuxlauncher/obj")
            linkParams.addAll(LINUX.launcherlibrary.linkFlags)
            linker = LINUX.launcherlibrary.linker
            lib = file("${buildClassesDir}/com/oracle/tools/packager/linux/libpackager.so")
        }
        task compileLauncher(dependsOn: [linkLinuxLauncher, linkLinuxLibrary])
    }

    // Builds the javapackager executable. For everything other than windows,
    // this is simply moving the existing shell script and ensuring it has proper
    // permissions. For Windows, this includes compiling the native executable
    if (IS_WINDOWS && COMPILE_FXPACKAGER){
        task setupCompileJavaPackager(type: Copy, group: "Build") {
            mkdir "$buildDir/native"
            mkdir "$buildDir/native/javapackager"
            from file("src/main/native/javapackager/win/javapackager.manifest")
            into file("$buildDir/native/javapackager")
            filter { line->
                line = line.replace("FXVERSION", RELEASE_VERSION_PADDED)
            }
        }

        task compileJavaPackager(type: CCTask, group: "Build", dependsOn: setupCompileJavaPackager) {
            description = "Compiles native sources for javapackager.exe"
            matches = ".*\\.cpp"
            params.addAll(WIN.fxpackager.ccFlags)
            compiler = WIN.fxpackager.compiler
            output(file("$buildDir/native/javapackager/obj"))
            source WIN.fxpackager.nativeSource
            doLast {
                mkdir "$buildDir/native"
                exec {
                    environment(WINDOWS_NATIVE_COMPILE_ENVIRONMENT)
                    commandLine(WIN.fxpackager.rcCompiler)
                    args(WIN.fxpackager.rcFlags)
                    args("/fo$buildDir/native/javapackager/javapackager.res")
                    args(WIN.fxpackager.rcSource)
                }
            }
        }

        task linkJavaPackager(type: LinkTask, dependsOn: compileJavaPackager, group: "Build") {
            description = "Links javapackager.exe"
            objectDir = file("$buildDir/native/javapackager/obj")
            linkParams.addAll(WIN.fxpackager.linkFlags);
            lib = file("$buildDir/native/javapackager/javapackager.exe")
            linker = WIN.fxpackager.linker
            doLast {
                exec({
                    commandLine("$MC", "-manifest",
                                       "$buildDir/native/javapackager/javapackager.manifest",
                                       "-outputresource:$buildDir/native/javapackager/javapackager.exe")
                    environment(WINDOWS_NATIVE_COMPILE_ENVIRONMENT)
                })
            }
        }

        task copyJavaPackager(type: Copy, group: "Build", dependsOn: linkJavaPackager) {
            from file("$buildDir/native/javapackager/javapackager.exe")
            into file("$buildDir/javapackager")
        }

        task buildJavaPackager(dependsOn: [copyJavaPackager])
    } else {
        task buildJavaPackager(type: Copy, group: "Build") {
            enabled = COMPILE_FXPACKAGER
            from "src/main/native/javapackager/shell"
            into "$buildDir/javapackager"
            fileMode = 0755
        }
    }

    if (COMPILE_FXPACKAGER) {
        assemble.dependsOn compileLauncher;
        assemble.dependsOn buildJavaPackager
    }

    classes {
        doLast {
            // Copy all of the download libraries to libs directory for the sake of the IDEs
            File libsDir = rootProject.file("build/libs");
            File antLib = new File(libsDir, "ant-1.8.2.jar")
            libsDir.mkdirs();

            // Skip copy if file is present.
            if (antLib.exists()) return;

            for (File f : configurations.compile.files) {
                copy {
                    into libsDir
                    from f.getParentFile()
                    include "**/ant-1.8.2.jar"
                    includeEmptyDirs = false
                }
            }
        }
    }

    task setupPackagerFakeJar(type: Copy) {
        from "$projectDir/src/main/resources/com/oracle/tools/packager/linux/javalogo_white_48.png"
        from "$projectDir/src/main/resources/com/oracle/tools/packager/mac/GenericAppHiDPI.icns"
        from "$projectDir/src/main/resources/com/oracle/tools/packager/windows/javalogo_white_48.ico"
        from "$projectDir/src/test/resources/hello/java-logo2.gif"
        from "$projectDir/src/test/resources/hello/small.ico"
        from "$projectDir/src/test/resources/hello/test.icns"
        from "$projectDir/src/test/resources/hello/LICENSE-RTF.rtf"
        from "$projectDir/../../LICENSE"
        into project.file("$projectDir/build/tmp/tests/appResources")
    }

    task setupPackagerFakeJarLicense(type: Copy) {
        from "$projectDir/../../LICENSE"
        into project.file("$projectDir/build/tmp/tests/appResources")
        rename '(.*)LICENSE', '$1LICENSE2'
    }

    task packagerFakeJar(type: Jar, dependsOn: [setupPackagerFakeJar, setupPackagerFakeJarLicense]) {
        dependsOn compileTestJava
        from compileTestJava.destinationDir
        include "hello/**"

        destinationDir project.file("build/tmp/tests/appResources")
        archiveName "mainApp.jar"

        manifest {
            attributes(
                    "Main-Class": "hello.HelloRectangle",
                    "Custom-Attribute": " Is it stripped?"
            )
        }
    }

    task packagerFXPackagedJar(type: Jar) {
        dependsOn packagerFakeJar
        from compileTestJava.destinationDir
        include "hello/**"

        destinationDir project.file("build/tmp/tests/appResources")
        archiveName "packagedMainApp.jar"

        manifest {
            attributes(
                "JavaFX-Application-Class": "hello.TestPackager",
            )
        }
    }

    if (!DO_BUILD_SDK_FOR_TEST) {
        def antJavafxJar = new File(rootProject.buildDir,
            "modular-sdk/modules_libs/${project.ext.moduleName}/ant-javafx.jar")
        [compileTestJava, test].each {
            it.classpath = files(antJavafxJar) + it.classpath
        }
    }

    compileTestJava.enabled = false // FIXME: JIGSAW -- support this with modules
    test {
        enabled = false // FIXME: JIGSAW -- support this with modules
        logger.info("JIGSAW Testing disabled for fxpackager")

        dependsOn packagerFXPackagedJar
        systemProperty "RETAIN_PACKAGER_TESTS", RETAIN_PACKAGER_TESTS
        systemProperty "TEST_PACKAGER_DMG", TEST_PACKAGER_DMG
        systemProperty "FULL_TEST", FULL_TEST
        executable = JAVA;
    }

    def packagerDevOpts = []
    try {
        packagerDevOpts.addAll(PACKAGER_DEV_OPTS.split(' '))
    } catch (MissingPropertyException ignore) {
        packagerDevOpts.addAll("image")
    }

    task packagerDev(dependsOn: [jar, testClasses, packagerFakeJar], type:JavaExec) {
        workingDir = project.file("build/tmp/tests/appResources/")
        executable = JAVA
        classpath = project.files("build/libs/ant-javafx.jar", "build/classes/test", "build/resources/test")
        main = "hello.SimpleBundle"
        args = [
                '--module-path', JDK_JMODS,
                '-o', "$projectDir/build/dev",
                '-all',
                packagerDevOpts
        ].flatten()
    }

    task createPackagerServicesModule(type: Jar) {
        if (project.hasProperty("DEBUGJDK_HOME")) {
            def dir = file("$DEBUGJDK_HOME/newmodules")
            dir.mkdirs()

            includeEmptyDirs = false
            archiveName = "jdk.packager.services.jar"
            destinationDir = dir

            from (project.file("$rootProject.buildDir/modular-sdk/modules/jdk.packager.services")) {
                include "**"
            }

            from (project.file("$rootProject.buildDir/../modules/jdk.packager/build/classes/java/main/jdk.packager.services")) {
                include "module-info.class"
            }
        }
    }

    task createPackagerModule(type: Jar) {
        if (project.hasProperty("DEBUGJDK_HOME")) {
            def dir = file("$DEBUGJDK_HOME/newmodules")
            dir.mkdirs()

            includeEmptyDirs = false
            archiveName = "jdk.packager.jar"
            destinationDir = dir

            from (project.file("$rootProject.buildDir/modular-sdk/modules/jdk.packager")) {
                include "**"
            }

            from (project.file("$rootProject.buildDir/../modules/jdk.packager/build/classes/java/main/jdk.packager")) {
                include "module-info.class"
            }
        }
    }

    task createRunScript() {
        def TEXT = "DEBUG_ARG=\"-J-Xdebug:\"\n" +
"\n" +
"# Argument parsing.\n" +
"ARGS=()\n" +
"for i in \"\$@\"; do\n" +
"    if [[ \"\$i\" == \${DEBUG_ARG}* ]]; then\n" +
"        ADDRESS=\${i:\${#DEBUG_ARG}}\n" +
"        DEBUG=\"-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=\${ADDRESS}\"\n" +
"    else\n" +
"        ARGS+=(\"\$i\")\n" +
"    fi\n" +
"done\n" +
"\n" +
"\${JAVA_HOME}/bin/java.original --upgrade-module-path \${JAVA_HOME}/newmodules \$(IFS=\$\' \'; echo \"\${ARGS[*]}\")\n"

        doLast {
            new File("$DEBUGJDK_HOME/bin").mkdirs()
            def runscript = new File("$DEBUGJDK_HOME/bin/java")//.withWriter('utf-8') //{
            runscript.write(TEXT)
        }

        doLast {
            exec {
                commandLine('chmod',  '+x', "$DEBUGJDK_HOME/bin/java")
            }
        }
    }

    task createBuildScript() {
        def TEXT = "DEBUG_ARG=\"-J-Xdebug:\"\n" +
"\n" +
"# Argument parsing.\n" +
"ARGS=()\n" +
"for i in \"\$@\"; do\n" +
"    if [[ \"\$i\" == \${DEBUG_ARG}* ]]; then\n" +
"        ADDRESS=\${i:\${#DEBUG_ARG}}\n" +
"        DEBUG=\"-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=\${ADDRESS}\"\n" +
"    else\n" +
"        ARGS+=(\"\$i\")\n" +
"    fi\n" +
"done\n" +
"\n" +
"\${JAVA_HOME}/bin/javac.original --upgrade-module-path \${JAVA_HOME}/newmodules \$(IFS=\$\' \'; echo \"\${ARGS[*]}\")\n"

        doLast {
            new File("$DEBUGJDK_HOME/bin").mkdirs()
            def buildscript = new File("$DEBUGJDK_HOME/bin/javac")//.withWriter('utf-8') //{
            buildscript.write(TEXT)
        }

        doLast {
            exec {
                commandLine('chmod',  '+x', "$DEBUGJDK_HOME/bin/javac")
            }
        }
    }

    task createDebugJDK(dependsOn: [createPackagerModule, createPackagerServicesModule, createRunScript, createBuildScript]) {
        def EXE = IS_WINDOWS ? ".exe" : ""

        doLast {
            copy {
                from SOURCEJDK_HOME
                into DEBUGJDK_HOME
                exclude("*/ant-javafx.jar")
                exclude("*/javapackager$EXE")
                exclude("*/java$EXE")
                exclude("*/javac$EXE")
                exclude("*/jdk.packager.services.jmod")
            }

            copy {
              from "$SOURCEJDK_HOME/bin/java$EXE"
              into "$DEBUGJDK_HOME/bin"
              rename "java$EXE", "java.original$EXE"
            }

            copy {
              from "$SOURCEJDK_HOME/bin/javac$EXE"
              into "$DEBUGJDK_HOME/bin"
              rename "javac$EXE", "javac.original$EXE"
            }

            copy {
              from "$rootProject.buildDir/modular-sdk/modules_libs/jdk.packager/ant-javafx.jar"
              into "$DEBUGJDK_HOME/lib"
            }

            copy {
              from "$rootProject.buildDir/modular-sdk/modules_cmds/jdk.packager/javapackager$EXE"
              into "$DEBUGJDK_HOME/bin"
            }

            copy {
              from "$DEBUGJDK_HOME/newmodules/jdk.packager.services.jar"
              into "$DEBUGJDK_HOME/jmods"
            }
        }
    }

    task copyRedistributableFiles(type: Copy) {
        def projectDir = "tools/java/legacy"
        def sourceDir = "src/$projectDir"
        def buildDir = "build/$projectDir"
        def resourceDir = "${moduleDir}/jdk/packager/internal/resources/tools/legacy"

        from "$sourceDir/jre.list"
        into project.file("$resourceDir")
    }

    processResources.dependsOn copyRedistributableFiles

    task copyDTtoPackager(type: Copy) {
        def destDt = "${moduleDir}/com/sun/javafx/tools/resource"
        from (sourceSets.main.output.resourcesDir) {
            includes = [ "resources/web-files/**" ]
        }
        into new File("$destDt", "dtoolkit")
    }

    processResources.dependsOn copyDTtoPackager
}

project(":media") {
    configurations {
        media
    }

    project.ext.buildModule = true
    project.ext.includeSources = true
    project.ext.moduleRuntime = true
    project.ext.moduleName = "javafx.media"

    sourceSets {
        main
        //shims // no test shims needed
        test
        tools {
            java.srcDir "src/tools/java"
        }
    }

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

    commonModuleSetup(project, [ 'base', 'graphics', 'media' ])

    dependencies {
        if (IS_BUILD_LIBAV_STUBS) {
            media name: "libav-9.14", ext: "tar.gz"
            media name: "libav-11.4", ext: "tar.gz"
            media name: "libav-12.1", ext: "tar.gz"
            media name: "ffmpeg-3.3.3", ext: "tar.gz"
        }
    }

    compileJava.dependsOn updateCacheIfNeeded

    compileJava {
        // generate the native headers during compile
        options.compilerArgs.addAll([
            '-h', "${project.buildDir}/gensrc/headers"
            ])
    }

    compileToolsJava {
        enabled = IS_COMPILE_MEDIA
        def modulePath = "${project.sourceSets.main.java.outputDir}"
        options.compilerArgs.addAll([
            "--module-path=$modulePath",
            "--add-modules=javafx.media",
            '--add-exports', 'javafx.media/com.sun.media.jfxmedia=ALL-UNNAMED',
            ])
    }

    project.ext.makeJobsFlag = IS_WINDOWS && IS_DEBUG_NATIVE ? "-j1" : "-j5";
    project.ext.buildType = IS_DEBUG_NATIVE ? "Debug" : "Release";

    def nativeSrcDir = file("${projectDir}/src/main/native")
    def generatedHeadersDir = file("${buildDir}/gensrc/headers/${project.moduleName}")

    task generateMediaErrorHeader(dependsOn: [compileJava, compileToolsJava]) {
        enabled = IS_COMPILE_MEDIA
        def headerpath = file("$generatedHeadersDir/jfxmedia_errors.h");
        doLast {
            def classpath = files(sourceSets.tools.output);
            def sourcepath = sourceSets.main.java.srcDirs;
            def srcRoot = (sourcepath.toArray())[0];

            mkdir generatedHeadersDir;

            def modulePath = "${project.sourceSets.main.java.outputDir}"
            modulePath += File.pathSeparator + "${rootProject.projectDir}/modules/javafx.graphics/build/classes/java/main"
            modulePath += File.pathSeparator + "${rootProject.projectDir}/modules/javafx.base/build/classes/java/main"

            exec {
                commandLine("$JAVA");
                args += patchModuleArgs
                args += [ "--module-path=$modulePath" ]
                args += [ "--add-modules=javafx.media" ]
                args +=  [ '--add-exports=javafx.media/com.sun.media.jfxmedia=ALL-UNNAMED' ]
                args +=  [ '-classpath', "${classpath.asPath}" ]
                args += [ "headergen.HeaderGen", "$headerpath", "$srcRoot" ]
            }
        }
        outputs.file(project.file("$headerpath"))
    }

    task buildNativeTargets {
        enabled = IS_COMPILE_MEDIA
    }

    compileTargets { t->
        def targetProperties = project.rootProject.ext[t.upper]
        def nativeOutputDir = file("${buildDir}/native/${t.name}")
        def projectDir = t.name.startsWith("arm") ? "linux" : t.name
        def mediaProperties = targetProperties.media
        // Makefile for OSX needs to know if we're building for parfait
        def compileParfait = IS_COMPILE_PARFAIT ? "true" : "false"

        def buildNative = task("build${t.capital}Native", dependsOn: [generateMediaErrorHeader]) {
            enabled = targetProperties.compileMediaNative
            if (!targetProperties.compileMediaNative) {
                println("Not compiling native Media for ${t.name} per configuration request");
            }

            doLast {
                exec {
                    commandLine ("make", "${makeJobsFlag}", "-C", "${nativeSrcDir}/jfxmedia/projects/${projectDir}")
                    args("JAVA_HOME=${JDK_HOME}", "GENERATED_HEADERS_DIR=${generatedHeadersDir}",
                         "OUTPUT_DIR=${nativeOutputDir}", "BUILD_TYPE=${buildType}", "BASE_NAME=jfxmedia",
                         "COMPILE_PARFAIT=${compileParfait}",
                         IS_64 ? "ARCH=x64" : "ARCH=x32",
                        "CC=${mediaProperties.compiler}", "LINKER=${mediaProperties.linker}")

                    if (t.name == "win") {
                        environment(WINDOWS_NATIVE_COMPILE_ENVIRONMENT)
                        args( "RESOURCE=${nativeOutputDir}/${buildType}/${WIN.media.jfxmediaRcFile}")
                    } else {
                        if (t.name.startsWith("arm")) {
                            args("EXTRA_CFLAGS=${mediaProperties.extra_cflags}", "EXTRA_LDFLAGS=${mediaProperties.extra_ldflags}")
                        } else {
                            args("HOST_COMPILE=1")
                        }
                    }
                }
            }
        }

        // check for the property disable${name} = true
        def boolean disabled = targetProperties.containsKey('disableMedia') ? targetProperties.get('disableMedia') : false
        if (!disabled) {
            // Building GStreamer
            def buildGStreamer = task("build${t.capital}GStreamer") {
                enabled = IS_COMPILE_MEDIA
                doLast {
                    exec {
                        commandLine ("make", "${makeJobsFlag}", "-C", "${nativeSrcDir}/gstreamer/projects/${projectDir}/gstreamer-lite")
                        args("OUTPUT_DIR=${nativeOutputDir}", "BUILD_TYPE=${buildType}", "BASE_NAME=gstreamer-lite",
                             IS_64 ? "ARCH=x64" : "ARCH=x32", "CC=${mediaProperties.compiler}",
                             "AR=${mediaProperties.ar}", "LINKER=${mediaProperties.linker}")

                        if (t.name == "win") {
                            environment(WINDOWS_NATIVE_COMPILE_ENVIRONMENT)
                            args("RESOURCE=${nativeOutputDir}/${buildType}/${WIN.media.gstreamerRcFile}")
                        }
                    }
                }
            }

            def buildPlugins = task("build${t.capital}Plugins", dependsOn: buildGStreamer) {
                enabled = IS_COMPILE_MEDIA

                doLast {
                    exec {
                        commandLine ("make", "${makeJobsFlag}", "-C", "${nativeSrcDir}/gstreamer/projects/${projectDir}/fxplugins")
                        args("OUTPUT_DIR=${nativeOutputDir}", "BUILD_TYPE=${buildType}", "BASE_NAME=fxplugins",
                             IS_64 ? "ARCH=x64" : "ARCH=x32",
                             "CC=${mediaProperties.compiler}", "AR=${mediaProperties.ar}", "LINKER=${mediaProperties.linker}")

                        if (t.name == "win") {
                            Map winEnv = new HashMap(WINDOWS_NATIVE_COMPILE_ENVIRONMENT)

                            String sdkDir = System.getenv("BASECLASSES_SDK_DIR");
                            if (sdkDir == null) {
                                sdkDir = "C:/Program Files/Microsoft SDKs/Windows/v7.1" // Default value
                                winEnv["BASECLASSES_SDK_DIR"] = sdkDir
                            }
                            environment(winEnv)

                            args("RESOURCE=${nativeOutputDir}/${buildType}/${WIN.media.fxpluginsRcFile}")
                        }
                    }
                }
            }

            buildNative.dependsOn buildPlugins

            if (t.name == "linux") {
                // Pre-defined command line arguments
                def cfgCMDArgs = ["sh", "configure"]
                def commonCfgArgs = ["--enable-shared", "--disable-debug", "--disable-static", "--disable-yasm", "--disable-doc", "--disable-programs", "--disable-everything"]
                def codecsCfgArgs = ["--enable-decoder=aac,mp3,mp3float,h264", "--enable-parser=aac,h264", "--enable-demuxer=aac,h264,mpegts,mpegtsraw"]

                def copyLibAVStubs = {String fromDir, String toDir ->
                    FileCollection config = files("config.h")
                    FileCollection libavcodec = files("avcodec.h", "avfft.h", "dxva2.h", "vaapi.h", "vda.h",
                                                      "vdpau.h", "version.h", "xvmc.h", "old_codec_ids.h")
                    FileCollection libavdevice = files("avdevice.h", "version.h")
                    FileCollection libavfilter = files("avfiltergraph.h", "avfilter.h", "buffersink.h", "buffersrc.h", "version.h");
                    FileCollection libavformat = files("avformat.h", "avio.h", "version.h")
                    FileCollection libavresample = files("avresample.h", "version.h")
                    FileCollection libavutil = files("adler32.h", "blowfish.h", "error.h", "log.h", "pixfmt.h",
                                                     "aes.h", "bswap.h", "eval.h", "lzo.h", "random_seed.h",
                                                     "attributes.h", "buffer.h", "fifo.h", "macros.h", "rational.h",
                                                     "audio_fifo.h", "channel_layout.h", "file.h", "mathematics.h", "samplefmt.h",
                                                     "avassert.h", "common.h", "frame.h", "md5.h", "sha.h",
                                                     "avconfig.h", "imgutils.h", "mem.h", "time.h", "avstring.h",
                                                     "cpu_internal.h", "intfloat.h", "opt.h", "version.h", "avutil.h",
                                                     "crc.h", "intreadwrite.h", "parseutils.h", "xtea.h", "base64.h",
                                                     "dict.h", "lfg.h", "pixdesc.h", "intfloat_readwrite.h", "old_pix_fmts.h", "audioconvert.h",
                                                     "cpu.h")
                    FileCollection libavutil_x86 = files("cpu.h") // Use cpu.h from x86 instead of libavutil root if exist
                    FileCollection libswscale = files("swscale.h", "version.h")

                    def copyLibAVFiles = {FileCollection files, String fDir, String tDir ->
                        File dir = file(tDir)
                        dir.mkdirs()

                        files.each { File file ->
                            copy {
                                from fDir
                                into tDir
                                include file.name
                            }
                        }
                    }

                    copyLibAVFiles(config, fromDir, "${toDir}/include")
                    copyLibAVFiles(libavcodec, "${fromDir}/libavcodec", "${toDir}/include/libavcodec")
                    copyLibAVFiles(libavdevice, "${fromDir}/libavdevice", "${toDir}/include/libavdevice")
                    copyLibAVFiles(libavfilter, "${fromDir}/libavfilter", "${toDir}/include/libavfilter")
                    copyLibAVFiles(libavformat, "${fromDir}/libavformat", "${toDir}/include/libavformat")
                    copyLibAVFiles(libavresample, "${fromDir}/libavresample", "${toDir}/include/libavresample")
                    copyLibAVFiles(libavutil, "${fromDir}/libavutil", "${toDir}/include/libavutil")
                    copyLibAVFiles(libavutil_x86, "${fromDir}/libavutil/x86", "${toDir}/include/libavutil")
                    copyLibAVFiles(libswscale, "${fromDir}/libswscale", "${toDir}/include/libswscale")

                    // Copy libs
                    FileTree libs = fileTree(dir: "${fromDir}", include: "**/*.so*")
                    libs.each {File file ->
                        copy {
                            from file
                            into "${toDir}/lib"
                        }
                    }
                }

                def buildLibAVStubs = task("buildLibAVStubs", dependsOn: []) {
                    enabled = IS_BUILD_LIBAV_STUBS

                    doLast {
                        project.ext.libav = [:]
                        project.ext.libav.basedir = "${buildDir}/native/linux/libav"
                        project.ext.libav.versions = [ "9.14", "11.4", "12.1" ]
                        project.ext.libav.versionmap = [ "9.14" : "54", "11.4" : "56", "12.1" : "57" ]

                        libav.versions.each { version ->
                            def libavDir = "${libav.basedir}/libav-${version}"
                            for (File f : configurations.media.files) {
                                if (f.name.startsWith("libav-${version}")) {
                                    File dir = file(libavDir)
                                    dir.mkdirs()
                                    def libavTar = "${libav.basedir}/libav-${version}.tar"
                                    ant.gunzip(src: f, dest: libavTar)
                                    ant.untar(src: libavTar, dest: libav.basedir)
                                }
                            }
                        }

                        libav.versions.each { version ->
                            def libavDir = "${libav.basedir}/libav-${version}"
                            File dir = file(libavDir)
                            if (dir.exists()) {
                                def configFile = "${libav.basedir}/libav-${version}/config.h"
                                File cfgFile = file(configFile)
                                if (!cfgFile.exists()) {
                                    // Add execute permissions to version.sh, otherwise build fails
                                    exec {
                                        workingDir("$libavDir")
                                        commandLine("chmod", "+x", "version.sh")
                                    }
                                    exec {
                                        workingDir("$libavDir")
                                        if (IS_BUILD_WORKING_LIBAV) {
                                            commandLine(cfgCMDArgs + commonCfgArgs + codecsCfgArgs)
                                        } else {
                                            commandLine(cfgCMDArgs + commonCfgArgs)
                                        }
                                    }
                                }
                                exec {
                                    workingDir("$libavDir")
                                    commandLine("make")
                                }
                            }
                        }

                        libav.versions.each { version ->
                            def fromDir = "${libav.basedir}/libav-${version}"
                            def majorVersion = libav.versionmap[version]
                            def toDir = "${libav.basedir}/libav-${majorVersion}"
                            copyLibAVStubs(fromDir, toDir)
                        }
                    }
                }

                def buildLibAVFFmpegStubs = task("buildLibAVFFmpegStubs", dependsOn: []) {
                    enabled = IS_BUILD_LIBAV_STUBS

                    def extraCfgArgs = ["--build-suffix=-ffmpeg"]

                    doLast {
                        project.ext.libav = [:]
                        project.ext.libav.basedir = "${buildDir}/native/linux/libavffmpeg"
                        project.ext.libav.versions = [ "11.4" ]
                        project.ext.libav.versionmap = [ "11.4" : "56" ]

                        libav.versions.each { version ->
                            def libavDir = "${libav.basedir}/libav-${version}"
                            for (File f : configurations.media.files) {
                                if (f.name.startsWith("libav-${version}")) {
                                    File dir = file(libavDir)
                                    dir.mkdirs()
                                    def libavTar = "${libav.basedir}/libav-${version}.tar"
                                    ant.gunzip(src: f, dest: libavTar)
                                    ant.untar(src: libavTar, dest: libav.basedir)
                                }
                            }
                        }

                        libav.versions.each { version ->
                            def libavDir = "${libav.basedir}/libav-${version}"
                            File dir = file(libavDir)
                            if (dir.exists()) {
                                def configFile = "${libav.basedir}/libav-${version}/config.h"
                                File cfgFile = file(configFile)
                                if (!cfgFile.exists()) {
                                    // Patch *.v files, so we have *_FFMPEG_$MAJOR instead of *_$MAJOR, otherwise library will not be loaded
                                    FileTree vfiles = fileTree(dir: "${libavDir}", include: "**/*.v")
                                    vfiles.each {File file ->
                                        String data = file.getText("UTF-8")
                                        data = data.replace("_\$MAJOR", "_FFMPEG_\$MAJOR")
                                        file.write(data, "UTF-8")
                                    }
                                    // Add execute permissions to version.sh, otherwise build fails
                                    exec {
                                        workingDir("$libavDir")
                                        commandLine("chmod", "+x", "version.sh")
                                    }
                                    exec {
                                        workingDir("$libavDir")
                                        if (IS_BUILD_WORKING_LIBAV) {
                                            commandLine(cfgCMDArgs + commonCfgArgs + codecsCfgArgs + extraCfgArgs)
                                        } else {
                                            commandLine(cfgCMDArgs + commonCfgArgs + extraCfgArgs)
                                        }
                                    }
                                }
                                exec {
                                    workingDir("$libavDir")
                                    commandLine("make")
                                }
                            }
                        }

                        libav.versions.each { version ->
                            def fromDir = "${libav.basedir}/libav-${version}"
                            def majorVersion = libav.versionmap[version]
                            def toDir = "${libav.basedir}/libav-${majorVersion}"
                            copyLibAVStubs(fromDir, toDir)

                            // Special case to copy *-ffmpeg.so to *.so
                            FileTree libs = fileTree(dir: "${fromDir}", include: "**/*-ffmpeg.so")
                            libs.each {File file ->
                                copy {
                                    from file
                                    into "${toDir}/lib"
                                    rename { String fileName ->
                                        fileName.replace("-ffmpeg", "")
                                    }
                                }
                            }
                        }
                    }
                }

                def buildFFmpegStubs = task("buildFFmpegStubs", dependsOn: []) {
                    enabled = IS_BUILD_LIBAV_STUBS

                    doLast {
                        project.ext.libav = [:]
                        project.ext.libav.basedir = "${buildDir}/native/linux/ffmpeg"
                        project.ext.libav.versions = [ "3.3.3" ]
                        project.ext.libav.versionmap = [ "3.3.3" : "57" ]

                        libav.versions.each { version ->
                            def libavDir = "${libav.basedir}/ffmpeg-${version}"
                            for (File f : configurations.media.files) {
                                if (f.name.startsWith("ffmpeg-${version}")) {
                                    File dir = file(libavDir)
                                    dir.mkdirs()
                                    def libavTar = "${libav.basedir}/ffmpeg-${version}.tar"
                                    ant.gunzip(src: f, dest: libavTar)
                                    ant.untar(src: libavTar, dest: libav.basedir)
                                }
                            }
                        }

                        libav.versions.each { version ->
                            def libavDir = "${libav.basedir}/ffmpeg-${version}"
                            File dir = file(libavDir)
                            if (dir.exists()) {
                                def configFile = "${libav.basedir}/ffmpeg-${version}/config.h"
                                File cfgFile = file(configFile)
                                if (!cfgFile.exists()) {
                                    // Add execute permissions to version.sh, otherwise build fails
                                    exec {
                                        workingDir("$libavDir")
                                        commandLine("chmod", "+x", "version.sh")
                                    }
                                    exec {
                                        workingDir("$libavDir")
                                        if (IS_BUILD_WORKING_LIBAV) {
                                            commandLine(cfgCMDArgs + commonCfgArgs + codecsCfgArgs)
                                        } else {
                                            commandLine(cfgCMDArgs + commonCfgArgs)
                                        }
                                    }
                                }
                                exec {
                                    workingDir("$libavDir")
                                    commandLine("make")
                                }
                            }
                        }

                        libav.versions.each { version ->
                            def fromDir = "${libav.basedir}/ffmpeg-${version}"
                            def majorVersion = libav.versionmap[version]
                            def toDir = "${libav.basedir}/ffmpeg-${majorVersion}"
                            copyLibAVStubs(fromDir, toDir)
                        }
                    }
                }

                def buildAVPlugin = task( "buildAVPlugin", dependsOn: [buildPlugins, buildLibAVStubs, buildLibAVFFmpegStubs, buildFFmpegStubs]) {
                    enabled = IS_COMPILE_MEDIA

                    doLast {
                        if (IS_BUILD_LIBAV_STUBS) {
                            project.ext.libav = [:]
                            project.ext.libav.basedir = "${buildDir}/native/linux/libav/libav"
                            project.ext.libav.versions = [ "53", "54", "55", "56", "57" ]
                            project.ext.libav.libavffmpeg = [:]
                            project.ext.libav.libavffmpeg.basedir = "${buildDir}/native/linux/libavffmpeg/libav"
                            project.ext.libav.libavffmpeg.versions = [ "56" ]
                            project.ext.libav.ffmpeg = [:]
                            project.ext.libav.ffmpeg.basedir = "${buildDir}/native/linux/ffmpeg/ffmpeg"
                            project.ext.libav.ffmpeg.versions = [ "57" ]

                            project.ext.libav.versions.each { version ->
                                def libavDir = "${project.ext.libav.basedir}-${version}"
                                File dir = file(libavDir)
                                if (dir.exists()) {
                                    exec {
                                        commandLine ("make", "${makeJobsFlag}", "-C", "${nativeSrcDir}/gstreamer/projects/linux/avplugin")
                                        args("CC=${mediaProperties.compiler}", "LINKER=${mediaProperties.linker}",
                                             "OUTPUT_DIR=${nativeOutputDir}", "BUILD_TYPE=${buildType}",
                                             "BASE_NAME=avplugin", "VERSION=${version}", "LIBAV_DIR=${libavDir}",
                                             "SUFFIX=", IS_64 ? "ARCH=x64" : "ARCH=x32")
                                    }
                                }
                            }

                            project.ext.libav.libavffmpeg.versions.each { version ->
                                def libavDir = "${project.ext.libav.libavffmpeg.basedir}-${version}"
                                File dir = file(libavDir)
                                if (dir.exists()) {
                                    exec {
                                        commandLine ("make", "${makeJobsFlag}", "-C", "${nativeSrcDir}/gstreamer/projects/linux/avplugin")
                                        args("CC=${mediaProperties.compiler}", "LINKER=${mediaProperties.linker}",
                                             "OUTPUT_DIR=${nativeOutputDir}", "BUILD_TYPE=${buildType}",
                                             "BASE_NAME=avplugin", "VERSION=${version}", "LIBAV_DIR=${libavDir}",
                                             "SUFFIX=-ffmpeg", IS_64 ? "ARCH=x64" : "ARCH=x32")
                                    }
                                }
                            }

                            project.ext.libav.ffmpeg.versions.each { version ->
                                def libavDir = "${project.ext.libav.ffmpeg.basedir}-${version}"
                                File dir = file(libavDir)
                                if (dir.exists()) {
                                    exec {
                                        commandLine ("make", "${makeJobsFlag}", "-C", "${nativeSrcDir}/gstreamer/projects/linux/avplugin")
                                        args("CC=${mediaProperties.compiler}", "LINKER=${mediaProperties.linker}",
                                             "OUTPUT_DIR=${nativeOutputDir}", "BUILD_TYPE=${buildType}",
                                             "BASE_NAME=avplugin", "VERSION=${version}", "LIBAV_DIR=${libavDir}",
                                             "SUFFIX=-ffmpeg", IS_64 ? "ARCH=x64" : "ARCH=x32")
                                    }
                                }
                            }
                        } else {
                            // Building fxavcodec plugin (libav plugin)
                            exec {
                                commandLine ("make", "${makeJobsFlag}", "-C", "${nativeSrcDir}/gstreamer/projects/linux/avplugin")
                                args("CC=${mediaProperties.compiler}", "LINKER=${mediaProperties.linker}",
                                     "OUTPUT_DIR=${nativeOutputDir}", "BUILD_TYPE=${buildType}",
                                     "BASE_NAME=avplugin", IS_64 ? "ARCH=x64" : "ARCH=x32")
                            }
                        }
                    }
                }
                buildNative.dependsOn buildAVPlugin
            }

            if (t.name == "win") {
                def buildResources = task("buildResources") {
                    doLast {
                        def rcOutputDir = "${nativeOutputDir}/${buildType}"
                        mkdir rcOutputDir
                        exec {
                            environment(WINDOWS_NATIVE_COMPILE_ENVIRONMENT)
                            commandLine (WIN.media.rcCompiler)
                            args(WIN.media.glibRcFlags)
                            args("/Fo${rcOutputDir}/${WIN.media.glibRcFile}", WIN.media.rcSource)
                        }

                        exec {
                            environment(WINDOWS_NATIVE_COMPILE_ENVIRONMENT)
                            commandLine (WIN.media.rcCompiler)
                            args(WIN.media.gstreamerRcFlags)
                            args("/Fo${rcOutputDir}/${WIN.media.gstreamerRcFile}", WIN.media.rcSource)
                        }

                        exec {
                            environment(WINDOWS_NATIVE_COMPILE_ENVIRONMENT)
                            commandLine (WIN.media.rcCompiler)
                            args(WIN.media.fxpluginsRcFlags)
                            args("/Fo${rcOutputDir}/${WIN.media.fxpluginsRcFile}", WIN.media.rcSource)
                        }

                        exec {
                            environment(WINDOWS_NATIVE_COMPILE_ENVIRONMENT)
                            commandLine (WIN.media.rcCompiler)
                            args(WIN.media.jfxmediaRcFlags)
                            args("/Fo${rcOutputDir}/${WIN.media.jfxmediaRcFile}", WIN.media.rcSource)
                        }
                    }
                }

                def buildGlib = task("build${t.capital}Glib", dependsOn: [buildResources]) {
                    enabled = IS_COMPILE_MEDIA
                    doLast {
                        exec {
                            environment(WINDOWS_NATIVE_COMPILE_ENVIRONMENT)
                            commandLine ("make", "${makeJobsFlag}", "-C", "${nativeSrcDir}/gstreamer/projects/${projectDir}/glib-lite")
                            args("OUTPUT_DIR=${nativeOutputDir}", "BUILD_TYPE=${buildType}", "BASE_NAME=glib-lite",
                                 IS_64 ? "ARCH=x64" : "ARCH=x32", "RESOURCE=${nativeOutputDir}/${buildType}/${WIN.media.glibRcFile}",
                                 "CC=${mediaProperties.compiler}", "AR=${mediaProperties.ar}", "LINKER=${mediaProperties.linker}")
                        }
                    }
                }
                buildGStreamer.dependsOn buildGlib

            } else if (t.name == "mac") {
                def buildGlib = task("build${t.capital}Glib") {
                    enabled = IS_COMPILE_MEDIA
                    doLast {
                        exec {
                            commandLine ("make", "${makeJobsFlag}", "-C", "${nativeSrcDir}/gstreamer/projects/${projectDir}/libffi")
                            args("OUTPUT_DIR=${nativeOutputDir}", "BUILD_TYPE=${buildType}", "BASE_NAME=ffi")
                            args ("CC=${mediaProperties.compiler}", "LINKER=${mediaProperties.linker}", "AR=${mediaProperties.ar}")
                        }

                        exec {
                            commandLine ("make", "${makeJobsFlag}", "-C", "${nativeSrcDir}/gstreamer/projects/${projectDir}/glib-lite")
                            args("OUTPUT_DIR=${nativeOutputDir}", "BUILD_TYPE=${buildType}", "BASE_NAME=glib-lite")
                            args ("CC=${mediaProperties.compiler}", "LINKER=${mediaProperties.linker}")
                        }
                    }
                }
                buildGStreamer.dependsOn buildGlib
            }
        }

        buildNativeTargets.dependsOn buildNative
    }

    jar {
        exclude("headergen/**")

        dependsOn compileJava
        if (IS_COMPILE_MEDIA) {
            dependsOn buildNativeTargets
        }
    }

    compileJava.options.compilerArgs.addAll(qualExportsCore)
}

project(":web") {
    configurations {
        webkit
    }
    project.ext.buildModule = true
    project.ext.includeSources = true
    project.ext.moduleRuntime = true
    project.ext.moduleName = "javafx.web"

    sourceSets {
        main
        shims
        test
    }

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

    commonModuleSetup(project, [ 'base', 'graphics', 'controls', 'media', 'web' ])

    dependencies {
    }

    compileJava.dependsOn updateCacheIfNeeded

    task webArchiveJar(type: Jar) {
        from (project.file("$projectDir/src/test/resources/test/html")) {
            include "**/archive-*.*"
        }
        archiveName = "webArchiveJar.jar"
        destinationDir = file("$buildDir/testing/resources")
    }

    def gensrcDir = "${buildDir}/gensrc/java"

    // add in the wrappers to the compile
    sourceSets.main.java.srcDirs += "${gensrcDir}"

    if (IS_COMPILE_WEBKIT) {
        compileJava {
            // generate the native headers during compile
            // only needed if we are doing the native compile
            options.compilerArgs.addAll([
                '-h', "${project.buildDir}/gensrc/headers"
                ])
        }
    }

    // Copy these to a common location in the moduleSourcePath
    def copyWrappers = project.task("copyPreGeneratedWrappers", type: Copy) {
        from "src/main/native/Source/WebCore/bindings/java/dom3/java"
        into "${gensrcDir}"
    }

    compileJava.dependsOn(copyWrappers);

    test {
        doFirst {
            if (!IS_COMPILE_WEBKIT) {
                println "*****************************************************"
                println "WARNING: running web tests without building webkit."
                println "The webkit native library will be copied from the JDK,"
                println "which might lead to failures in some web tests."
                println "To avoid these failures, you should either build"
                println "webkit locally, copy the native webkit library from a"
                println "recent build, or skip execution of web test cases with"
                println "'-x :web:test'"
                println "*****************************************************"
            }
        }
        // Run web tests in headless mode
        systemProperty 'glass.platform', 'Monocle'
        systemProperty 'monocle.platform', 'Headless'
        systemProperty 'prism.order', 'sw'
        dependsOn webArchiveJar
        def testResourceDir = file("$buildDir/testing/resources")
        jvmArgs "-DWEB_ARCHIVE_JAR_TEST_DIR=$testResourceDir"
    }

    task compileJavaDOMBinding()

    compileTargets { t ->
        def targetProperties = project.rootProject.ext[t.upper]
        def classifier = (t.name != "linux" && t.name != "win") ? t.name :
                          IS_64 ? "${t.name}-amd64" : "${t.name}-i586"

        def webkitOutputDir = cygpath("$buildDir/${t.name}")
        def webkitConfig = IS_DEBUG_NATIVE ? "Debug" : "Release"

        File nativeBuildDir = new File("${webkitOutputDir}")
        nativeBuildDir.mkdirs()

        def compileNativeTask = task("compileNative${t.capital}", dependsOn: [compileJava]) {
            println "Building Webkit configuration /$webkitConfig/ into $webkitOutputDir"
            enabled =  (IS_COMPILE_WEBKIT)

            doLast {
                exec {
                    workingDir("$webkitOutputDir")
                    commandLine("perl", "$projectDir/src/main/native/Tools/Scripts/set-webkit-configuration", "--$webkitConfig")
                    environment(["WEBKIT_OUTPUTDIR" : webkitOutputDir])
                }

                exec {
                    workingDir("$webkitOutputDir")
                    def cmakeArgs = "-DENABLE_TOOLS=1"
                    if (t.name == "win") {
                        String parfaitPath = IS_COMPILE_PARFAIT ? System.getenv().get("PARFAIT_PATH") + ";" : "";
                        Map environmentSettings = new HashMap(WINDOWS_NATIVE_COMPILE_ENVIRONMENT)
                        environmentSettings["PATH"] = parfaitPath + "$WINDOWS_VS_PATH"
                        /* To build with ICU:
                        1. Download http://javaweb.us.oracle.com/jcg/fx-webrevs/RT-17164/WebKitLibrariesICU.zip
                        and unzip it to WebKitLibraries folder.
                        2. Copy DLLs from
                        WebKitLibrariesICU.zip\WebKitLibraries\import\runtime
                        to %windir%\system32
                        3. Uncomment the line below
                         */
                        // args("--icu-unicode")

                        // To enable ninja build on Windows
                        environment(WINDOWS_NATIVE_COMPILE_ENVIRONMENT + ['CC' : 'cl', 'CXX' : 'cl'])
                    } else if (t.name == "mac") {
                        cmakeArgs = "-DCMAKE_OSX_DEPLOYMENT_TARGET=$MACOSX_MIN_VERSION -DCMAKE_OSX_SYSROOT=$MACOSX_SDK_PATH"
                    } else if (t.name == "linux") {
                        cmakeArgs = "-DCMAKE_SYSTEM_NAME=Linux"
                        if (IS_64) {
                            cmakeArgs = "$cmakeArgs -DCMAKE_SYSTEM_PROCESSOR=x86_64"
                        } else {
                            cmakeArgs = "$cmakeArgs -DCMAKE_SYSTEM_PROCESSOR=i586 -DCMAKE_C_FLAGS=-m32 -DCMAKE_CXX_FLAGS=-m32"
                        }
                    } else if (t.name.startsWith("arm")) {
                        fail("ARM target is not supported as of now.")
                    }

                    if (IS_COMPILE_PARFAIT) {
                        environment([
                            "COMPILE_PARFAIT" : "true"
                        ])
                        cmakeArgs = "-DCMAKE_C_COMPILER=parfait-gcc -DCMAKE_CXX_COMPILER=parfait-g++"
                    }

                    environment([
                        "JAVA_HOME"       : JDK_HOME,
                        "WEBKIT_OUTPUTDIR" : webkitOutputDir,
                        "PYTHONDONTWRITEBYTECODE" : "1",
                    ])

                    def targetCpuBitDepthSwitch = ""
                    if (IS_64) {
                        targetCpuBitDepthSwitch = "--64-bit"
                    } else {
                        targetCpuBitDepthSwitch = "--32-bit"
                    }
                    cmakeArgs += " -DJAVAFX_RELEASE_VERSION=${jfxReleaseMajorVersion}"
                    commandLine("perl", "$projectDir/src/main/native/Tools/Scripts/build-webkit",
                        "--java", "--icu-unicode", targetCpuBitDepthSwitch,
                        "--cmakeargs=${cmakeArgs}")
                }
            }
        }

        def copyDumpTreeNativeTask = task("copyDumpTreeNative${t.capital}", type: Copy,
                dependsOn: [ compileNativeTask]) {
            def library = rootProject.ext[t.upper].library
            from "$webkitOutputDir/$webkitConfig/lib/${library('DumpRenderTreeJava')}"
            into "$buildDir/test/${t.name}"
        }

        def copyNativeTask = task("copyNative${t.capital}", type: Copy,
                dependsOn: [compileNativeTask, , copyDumpTreeNativeTask]) {
            enabled =  (IS_COMPILE_WEBKIT)
            def library = rootProject.ext[t.upper].library
            from "$webkitOutputDir/$webkitConfig/lib/${library('jfxwebkit')}"
            into "$buildDir/libs/${t.name}"
        }

        if (IS_WINDOWS && t.name == "win") {
            def webkitProperties = project.rootProject.ext[t.upper].webkit
            def rcTask = project.task("rc${t.capital}", type: CompileResourceTask) {
                compiler = webkitProperties.rcCompiler
                source(webkitProperties.rcSource)
                if (webkitProperties.rcFlags) {
                    rcParams.addAll(webkitProperties.rcFlags)
                }
                output(file("$webkitOutputDir/$webkitConfig/WebCore/obj"))
            }
            compileNativeTask.dependsOn rcTask
        }

        def compileJavaDOMBindingTask = task("compileJavaDOMBinding${t.capital}", type: JavaCompile,
                dependsOn: [compileJava, compileNativeTask, copyNativeTask]) {
            destinationDir = file("$buildDir/classes/java/main")
            classpath = configurations.compile
            source = project.sourceSets.main.java.srcDirs
            options.compilerArgs.addAll([
                '-implicit:none',
                '--module-source-path', defaultModuleSourcePath
                ])
        }

        compileJavaDOMBinding.dependsOn compileJavaDOMBindingTask

        if (!targetProperties.compileWebnodeNative) {
            println("Not compiling native Webkit for ${t.name} per configuration request");
            compileNativeTask.enabled = false
        }
    }

    def drtClasses = "**/com/sun/javafx/webkit/drt/**"
    task drtJar(type: Jar, dependsOn: compileJava) {
        archiveName = "drt.jar"
        destinationDir = file("$buildDir/test")
        from "$buildDir/classes/java/main/javafx.web/"
        include drtClasses
        includeEmptyDirs = false
    }

    if (IS_COMPILE_WEBKIT) {
        assemble.dependsOn compileJavaDOMBinding, drtJar
    }

    compileJava.options.compilerArgs.addAll(qualExportsCore)
}

// This project is for system tests that need to run with a full SDK.
// Most of them display a stage or do other things that preclude running
// them in a shared JVM or as part of the "smoke test" run (which must
// not pop up any windows or use audio). As such, they are only enabled
// when FULL_TEST is specified, and each test runs in its own JVM
project(":systemTests") {

    sourceSets {
        test

        // Source sets for standalone test apps (used for launcher tests)
        testapp1

        // Modular applications
        testapp2
        testapp3
        testapp4
        testapp5
        testapp6
    }

    project.ext.buildModule = false
    project.ext.moduleRuntime = false
    project.ext.moduleName = "systemTests"

    dependencies {
        testCompile project(":graphics").sourceSets.test.output
        testCompile project(":base").sourceSets.test.output
        testCompile project(":controls").sourceSets.test.output
        testCompile project(":swing").sourceSets.test.output
    }

    def dependentProjects = [ 'base', 'graphics', 'controls', 'media', 'web', 'swing', 'fxml' ]
    commonModuleSetup(project, dependentProjects)

    File testJavaPolicyFile = new File(rootProject.buildDir, TESTJAVAPOLICYFILE);
    File testRunArgsFile = new File(rootProject.buildDir,TESTRUNARGSFILE);

    File stRunArgsFile = new File(project.buildDir,"st.run.args");

    def sts = task("systemTestSetup") {
        outputs.file(stRunArgsFile)

        doLast() {
            stRunArgsFile.delete()

            logger.info("Creating patchmodule.args file ${stRunArgsFile}")

            // Create an argfile with the information needed to launch
            // the stand alone system unit tests.

            //First add in all of the patch-module args we use for the
            //normal unit tests, copied from test.run.args
            testRunArgsFile.eachLine { str ->
                stRunArgsFile <<  "${str}\n"
            }

            // Now add in the working classpath elements (junit, test classes...)
            stRunArgsFile <<  "-cp \"\\\n"
            test.classpath.each() { elem ->
                def e = cygpath("${elem}")
                stRunArgsFile <<  "  ${e}${File.pathSeparator}\\\n"
            }
            stRunArgsFile <<  "\"\n"
        }
    }

    test.dependsOn(sts)
    test.dependsOn(createTestArgfiles);

    // Tasks to create standalone test applications for the launcher tests

    if (project.hasProperty('testModulePathArgs')) {
        compileTestapp1Java.options.compilerArgs.addAll(testModulePathArgs)
    }
    dependentProjects.each { e ->
        compileTestapp1Java.dependsOn(rootProject.project(e).testClasses)
    }

    def testapp1JarName = "testapp1.jar"
    task createTestapp1Jar1(type: Jar) {
        dependsOn compileTestapp1Java
        enabled = IS_FULL_TEST

        destinationDir = file("$buildDir/testapp1")
        archiveName = testapp1JarName
        includeEmptyDirs = false
        from project.sourceSets.testapp1.java.outputDir
        include("testapp/**")
        include("com/javafx/main/**")

        manifest {
            attributes(
                "Main-Class" : "com.javafx.main.Main",
                "JavaFX-Version" : "2.2",
                "JavaFX-Application-Class" : "testapp.HelloWorld",
                "JavaFX-Class-Path" : "jar2.jar"
            )
        }
    }

    task createTestapp1Jar2(type: Jar) {
        dependsOn compileTestapp1Java
        enabled = IS_FULL_TEST

        destinationDir = file("$buildDir/testapp1")
        archiveName = "jar2.jar";
        includeEmptyDirs = false
        from project.sourceSets.testapp1.java.outputDir
        include("pkg2/**")
    }

    task createTestApps() {
        dependsOn(createTestapp1Jar1)
        dependsOn(createTestapp1Jar2)
    }
    test.dependsOn(createTestApps);

    def modtestapps = [ "testapp2", "testapp3", "testapp4", "testapp5", "testapp6"  ]
    modtestapps.each { testapp ->
        def testappCapital = testapp.capitalize()
        def copyTestAppTask = task("copy${testappCapital}", type: Copy) {
            from project.sourceSets."${testapp}".java.outputDir
            from project.sourceSets."${testapp}".output.resourcesDir
            into "${project.buildDir}/modules/${testapp}"
        }

        def List<String> testAppSourceDirs = []
        project.sourceSets."${testapp}".java.srcDirs.each { dir ->
            testAppSourceDirs += dir
        }
        def testappCompileTasks = project.getTasksByName("compile${testappCapital}Java", true);
        def testappResourceTasks = project.getTasksByName("process${testappCapital}Resources", true);
        testappCompileTasks.each { appCompileTask ->
            appCompileTask.options.compilerArgs.addAll([
                '-implicit:none',
                '--module-source-path', testAppSourceDirs.join(File.pathSeparator),
                ] )
            if (project.hasProperty('testModulePathArgs')) {
                appCompileTask.options.compilerArgs.addAll(testModulePathArgs)
            }

            dependentProjects.each { e ->
                appCompileTask.dependsOn(rootProject.project(e).testClasses)
            }

            copyTestAppTask.dependsOn(appCompileTask)
        }
        testappResourceTasks.each { appResourceTask ->
            copyTestAppTask.dependsOn(appResourceTask)
        }

        createTestApps.dependsOn(copyTestAppTask)
    }

    test {
        enabled = IS_FULL_TEST

        // Parse testPatchModuleArgs looking for "--module-path".
        // Save path if found so we can pass it to the module launcher tests
        def pendingModulePath = false
        testPatchModuleArgs.each { str ->
            if (pendingModulePath) {
                project.ext.launcherModulePath = str;
                pendingModulePath = false
            } else if (str == "--module-path") {
                pendingModulePath = true
            }
        }

        // Properties passed to launcher tests
        systemProperty "launchertest.testapp1.jar", "build/testapp1/$testapp1JarName"
        modtestapps.each { testapp ->
            systemProperty "launchertest.${testapp}.module.path",
                    "${project.buildDir}/modules/${testapp}"
        }

        // Properties passed to test.util.Util
        systemProperties 'worker.debug': IS_WORKER_DEBUG
        systemProperties 'worker.patchmodule.file': cygpath(stRunArgsFile.path)
        if (project.hasProperty("launcherModulePath")) {
            systemProperties 'worker.module.path': launcherModulePath
        }
        systemProperties 'worker.patch.policy': cygpath(testJavaPolicyFile.path)
        systemProperties 'worker.java.cmd': JAVA

        if (!IS_USE_ROBOT) {
            // Disable all robot-based visual tests
            exclude("test/robot/**");
        }
        if (!IS_UNSTABLE_TEST) {
            // JDK-8196607 Don't run monocle test cases 
            exclude("test/robot/com/sun/glass/ui/monocle/**");
        }
        if (!IS_AWT_TEST) {
            // Disable all AWT-based tests
            exclude("**/javafx/embed/swing/*.*");
            exclude("**/com/sun/javafx/application/Swing*.*");
        }

        if (!HAS_JAVAFX_MODULES) {
            jvmArgs += qualExportsSwing
        }

        forkEvery = 1
    }
}

allprojects {
    // The following block is a workaround for the fact that presently Gradle
    // can't set the -XDignore.symbol.file flag, because it appears that the
    // javac API is lacking support for it. So what we'll do is find any Compile
    // task and manually provide the options necessary to fire up the
    // compiler with the right settings.
    tasks.withType(JavaCompile) { compile ->
        if (compile.options.hasProperty("useAnt")) {
            compile.options.useAnt = true
            compile.options.useDepend = IS_USE_DEPEND
        } else if (compile.options.hasProperty("incremental")) {
            compile.options.incremental = IS_INCREMENTAL
        }
        compile.options.debug = true // we always generate debugging info in the class files
        compile.options.debugOptions.debugLevel = IS_DEBUG_JAVA ? "source,lines,vars" : "source,lines"
        compile.options.fork = true

        compile.options.forkOptions.executable = JAVAC

        compile.options.warnings = IS_LINT

        compile.options.compilerArgs += ["-XDignore.symbol.file", "-encoding", "UTF-8"]

        // we use a custom javadoc command
        project.javadoc.enabled = false

        // Add in the -Xlint options
        if (IS_LINT) {
            LINT.split("[, ]").each { s ->
                compile.options.compilerArgs += "-Xlint:$s"
            }
        }
    } // tasks with javaCompile

    // If I am a module....
    if (project.hasProperty('moduleSourcePath') &&
            (project.hasProperty('buildModule') && project.buildModule)) {
        project.compileJava {
            options.compilerArgs.addAll([
                '-implicit:none',
                '--module-source-path', project.moduleSourcePath
                ])
        }
        // no jars needed for modules
        project.jar.enabled = false

        // and redirect the resources into the module
        project.processResources.destinationDir = project.moduleDir
    }

    if (project.hasProperty('moduleSourcePathShim') &&
            project.sourceSets.hasProperty('shims')) {

        // sync up the obvious source directories with the shims
        // others (like the shaders in graphics) should be added in there
        project.sourceSets.shims.java.srcDirs += project.sourceSets.main.java.srcDirs
        project.sourceSets.shims.java.srcDirs += "$buildDir/gensrc/java"

        project.compileShimsJava {
            options.compilerArgs.addAll([
                '-implicit:none',
                '--module-source-path', project.moduleSourcePathShim
                ])
        }
        project.compileShimsJava.dependsOn(project.compileJava)

        def copyGeneratedShimsTask = task("copyGeneratedShims", type: Copy, dependsOn: [compileShimsJava, processShimsResources]) {
            from project.sourceSets.shims.java.outputDir
            into "${rootProject.buildDir}/shims"
            if (HAS_JAVAFX_MODULES) {
                exclude("*/module-info.class")
            }
        }

        project.processShimsResources.dependsOn(project.processResources)

        // shims resources should have the main resouces as a base
        project.sourceSets.shims.resources.srcDirs += project.sourceSets.main.resources.srcDirs

        // and redirect the resources into the module
        project.processShimsResources.destinationDir = project.moduleShimsDir

       compileTestJava.dependsOn(copyGeneratedShimsTask)
    }

    if (project.hasProperty('modulePathArgs')) {
        project.compileJava.options.compilerArgs.addAll(modulePathArgs)
    }

    if (project.hasProperty('testModulePathArgs')) {
        project.compileTestJava.options.compilerArgs.addAll(testModulePathArgs)
    }

    if (project.hasProperty('testPatchModuleArgs')) {
        project.test.jvmArgs += testPatchModuleArgs
    }

    /* Note: we should not have to add extraAddExports to the normal
     * modular compile, as it contains all of the module-info files.
     * In fact doing so might cover up a module-info issue.
     * so we don't do it, and I will leave this commented out
     * block as a reminder of this fact.
    if (project.hasProperty('extraAddExports')) {
        project.compileJava.options.compilerArgs.addAll(extraAddExports);
    }
    */

    if (project.hasProperty('testAddExports')) {
        project.compileTestJava.options.compilerArgs.addAll(testAddExports);
        project.test.jvmArgs += testAddExports
    }

    if (rootProject.hasProperty("EXTRA_TEST_ARGS") && project.hasProperty('test')) {
        EXTRA_TEST_ARGS.split(' ').each() { e ->
            project.test.jvmArgs += e
        }
    }

    if (rootProject.hasProperty("EXTRA_COMPILE_ARGS") && project.hasProperty('compileJava')) {
        project.compileJava.options.compilerArgs.addAll(EXTRA_COMPILE_ARGS.split(' '))
    }

    if (rootProject.hasProperty("EXTRA_COMPILE_ARGS") && project.hasProperty('compileTestJava')) {
        project.compileTestJava.options.compilerArgs.addAll(EXTRA_COMPILE_ARGS.split(' '))
    }

}

/******************************************************************************
 *                                                                            *
 *                             Top Level Tasks                                *
 *                                                                            *
 *  These are the tasks which are defined only for the top level project and  *
 *  not for any sub projects. These are generally the entry point that is     *
 *  used by Hudson and by the continuous build system.                        *
 *                                                                            *
 *****************************************************************************/

task clean() {
    group = "Basic"
    description = "Deletes the build directory and the build directory of all sub projects"
    getSubprojects().each { subProject ->
        dependsOn(subProject.getTasksByName("clean", true));
    }
    doLast {
        delete(buildDir);
    }
}

task cleanAll() {
    group = "Basic"
    description = "Scrubs the repo of build artifacts"
    dependsOn(clean)
    doLast {
        //delete(".gradle"); This causes problems on windows.
        delete("buildSrc/build");
    }
}

task createMSPfile() {
    group = "Build"
    File mspFile = new File(rootProject.buildDir,MODULESOURCEPATH)
    outputs.file(mspFile)

    doLast {
        mspFile.delete()
        mspFile << "--module-source-path\n"
        mspFile << defaultModuleSourcePath
        mspFile << "\n"

        if (!HAS_JAVAFX_MODULES) {
            appendQualExports(mspFile, qualExportsSwing)
        }
    }
}

task javadoc(type: Javadoc, dependsOn: createMSPfile) {
    group = "Basic"
    description = "Generates the JavaDoc for all the public API"
    executable = JAVADOC
    def projectsToDocument = [
            project(":base"), project(":graphics"), project(":controls"), project(":media"),
            project(":swing"), /*project(":swt"),*/ project(":fxml"), project(":web")]
    source(projectsToDocument.collect({
        [it.sourceSets.main.java]
    }));
    setDestinationDir(new File(buildDir, 'javadoc'));

    exclude("com/**/*", "Compile*", "javafx/builder/**/*", "javafx/scene/accessibility/**/*");

    options.tags("apiNote:a:API Note:")
    options.tags("implSpec:a:Implementation Requirements:")
    options.tags("implNote:a:Implementation Note:")
    options.tags("param")
    options.tags("return")
    options.tags("throws")
    options.tags("moduleGraph:X")
    options.tags("since")
    options.tags("version")
    options.tags("serialData")
    options.tags("factory")
    options.tags("see")

    options.windowTitle("${javadocTitle}")
    options.header("${javadocHeader}")
    options.bottom("${javadocBottom}")
    options.locale("en");
    if (BUILD_CLOSED) {
        options.linksOffline(JDK_DOCS, JDK_DOCS_CLOSED);
    } else {
        options.links(JDK_DOCS);
    }
    options.addBooleanOption("XDignore.symbol.file").setValue(true);
    options.addBooleanOption("Xdoclint:${DOC_LINT}").setValue(IS_DOC_LINT);
    options.addBooleanOption("html5").setValue(true);
    options.addBooleanOption("javafx").setValue(true);
    options.addBooleanOption("use").setValue(true);

    options.setOptionFiles([
        new File(rootProject.buildDir,MODULESOURCEPATH)
        ]);

    doLast {
        projectsToDocument.each { p ->
            copy {
                from("$p.projectDir/src/main/docs") {
                    include "**/*.html"
                    filter { line->
                        line = line.replace("@FXVERSION@", RELEASE_VERSION)
                    }
                }
                from("$p.projectDir/src/main/docs") {
                    exclude "**/*.html"
                }

                into "$buildDir/javadoc"
            }
        }
    }

    dependsOn(projectsToDocument.collect { project -> project.getTasksByName("classes", true)});
}

task sdk() {
    if (DO_BUILD_SDK_FOR_TEST) {
        rootProject.getTasksByName("test", true).each { t ->
            if (t.enabled) t.dependsOn(sdk)
        }
    }
}

task appsjar() {
    dependsOn(sdk)
    // Note: the jar dependencies get added elsewhere see project(":apps")
}

// these are empty tasks, allowing us to depend on the task, which may have other
// real work items added later.
task copyAppsArtifacts() {
    dependsOn(appsjar)
}

task apps() {
    dependsOn(sdk)
    dependsOn(appsjar)
    dependsOn(copyAppsArtifacts)
}

task findbugs() {
    dependsOn(sdk)

    doLast {
        if (!BUILD_CLOSED) {
            println "findbugs task is only run for a closed build"
        }
    }
}

// create the zip file of modules for a JDK build
task jdkZip {
    dependsOn(sdk)
}

// The following tasks are for the closed build only. They are a no-op for the open build

task checkCache() {
    dependsOn(updateCacheIfNeeded)
}

task publicExports() {
    dependsOn(sdk, apps, javadoc, jdkZip)
    // note the real work is below in the compileTargets
}

task perf() {
    dependsOn(sdk, apps)
    doLast {
        if (!BUILD_CLOSED) {
            println "perf task is only run for a closed build"
        }
    }
}

task zips() {
    dependsOn(sdk, javadoc, apps, jdkZip, publicExports, perf)
    // note the real work is below in the compileTargets
}

task all() {
    dependsOn(sdk,publicExports,apps,perf,zips)
}


// Construct list of subprojects that are modules
ext.moduleProjList = []
subprojects {
    if (project.hasProperty("buildModule") && project.ext.buildModule) {
        rootProject.ext.moduleProjList += project
        println "module: $project (buildModule=YES)"
    } else {
        println "module: $project (buildModule=NO)"
    }
}


// Define the sdk task, which also produces the javafx.swt modular jar

compileTargets { t ->

    def javafxSwtTask = task("javafxSwt$t.capital", type: Jar) {
        enabled = COMPILE_SWT
        group = "Basic"
        description = "Creates the javafx-swt.jar for the $t.name target"
        archiveName = "${project(":swt").buildDir}/libs/javafx-swt.jar";
        includeEmptyDirs = false
        from("${project(":swt").buildDir}/classes/java/main");
        include("**/javafx/embed/swt/**")

        dependsOn(
            project(":swt").compileJava,
            project(":swt").processResources,
            // note: assemble and classes are not enough for DidWork
            project(":swt").classes,
            // classes is needed for a jar copy
            )
    }

    // FIXME: do we really need the index task for this modular jar?
    def javafxSwtIndexTask = task("javafxSwtIndex$t.capital") {
        //the following is a workaround for the lack of indexing in gradle 1.4 through 1.7
        dependsOn(javafxSwtTask)

        doLast() {
            ant.jar (update: true, index: true, destfile: javafxSwtTask.archiveName)
        }
    }

    def sdkTask = task("sdk$t.capital") {
        group = "Basic"
        dependsOn(javafxSwtIndexTask)
    }

    sdk.dependsOn(sdkTask)
}

project(":apps") {
    // The apps build is Ant based, we will exec ant from gradle.

    // Download the Lucene libraries needed for the Ensemble8 app
    getConfigurations().create("lucene");
    dependencies {
        lucene group: "org.apache.lucene", name: "lucene-core", version: "7.1.0"
        lucene group: "org.apache.lucene", name: "lucene-grouping", version: "7.1.0"
        lucene group: "org.apache.lucene", name: "lucene-queryparser", version: "7.1.0"
    }

    // Copy Lucene libraries into the Ensemble8/lib directory
    File ensembleLibDir = rootProject.file("apps/samples/Ensemble8/lib");
    def libNames = [ "lucene-core-7.1.0.jar",
                     "lucene-grouping-7.1.0.jar",
                     "lucene-queryparser-7.1.0.jar" ]


    task getLucene(type: Copy) {
        doFirst {
            ensembleLibDir.mkdirs();
        }
        into ensembleLibDir
        includeEmptyDirs = false
        configurations.lucene.files.each { f ->
            libNames.each { name ->
                if (name == f.getName()) {
                    from f.getPath()
                }
            }
        }
    }

    compileTargets { t ->
        List<String> params = []

        params << "-DtargetBld=$t.name"

        if (!rootProject.ext[t.upper].compileSwing) {
            params << "-DJFX_CORE_ONLY=true"
        }
        params << "-Dplatforms.JDK_1.9.home=${rootProject.ext.JDK_HOME}"
        params << "-Dcompile.patch=@${rootProject.buildDir}/${COMPILEARGSFILE}"
        params << "-Drun.patch=@${rootProject.buildDir}/${RUNARGSFILE}"

        def appsJar = project.task("appsJar${t.capital}") {
            dependsOn(sdk, getLucene)
            doLast() {
                ant(t.name,
                      projectDir.path,
                      "appsJar",
                      params);
            }
        }
        rootProject.appsjar.dependsOn(appsJar)

        def appsClean = project.task("clean${t.capital}") {
            doLast() {
                ant(t.name,
                      project.projectDir.path,
                      "clean",
                      params);
                delete(ensembleLibDir);
            }
        }
        rootProject.clean.dependsOn(appsClean)
    }
}

// Tasks to create the disk layout for the sdk, jmods, and docs
// in the artifacts directory (publicExports), and zip them up in
// artifacts/bundles (zips)
// These tasks are only used for the standalone SDK.
compileTargets { t ->
    if (!HAS_JAVAFX_MODULES) {
        def targetProperties = rootProject.ext[t.upper]
        def platformPrefix = targetProperties.platformPrefix

        def artifactsDir = "${rootProject.buildDir}/artifacts"
        def bundlesDir = "${artifactsDir}/bundles"

        def sdkDirName = "${platformPrefix}sdk"
        def sdkDir = "${rootProject.buildDir}/${sdkDirName}"
        def sdkBundleName = "javafx-sdk-${RELEASE_VERSION}"
        def sdkArtifactsDir = "${artifactsDir}/${sdkBundleName}"

        def docsDirName = "javadoc"
        def docsDir = "${rootProject.buildDir}/${docsDirName}"
        def docsBundleName = "javafx-docs-${RELEASE_VERSION}"
        def docsArtifactsDir = "${artifactsDir}/${docsBundleName}"

        def publicExportsTask = task ("publicExportsStandalone${t.capital}") {
            group = "Basic"
            description = "Creates the disk layout for sdk, jmods, and docs"
        }
        publicExports.dependsOn(publicExportsTask)

        def copyArtifactsSdkTask = task("copyArtifactsSdk$t.capital", type: Copy, dependsOn: [sdk,apps,javadoc]) {
            from sdkDir
            into sdkArtifactsDir
        }
        publicExportsTask.dependsOn(copyArtifactsSdkTask)

        // Need to modify file permissions Windows to make sure that the
        // execute bit is set, and that the files are world readable
        def chmodArtifactsSdkTask = task("chmodArtifactsSdk$t.capital", dependsOn: copyArtifactsSdkTask) {
            if (IS_WINDOWS) {
                doLast {
                    exec {
                        workingDir(sdkArtifactsDir)
                        commandLine("chmod", "-R", "755", ".")
                    }
                }
            }
        }
        publicExportsTask.dependsOn(chmodArtifactsSdkTask)

        def copyArtifactsDocsTask = task("copyArtifactsDocs$t.capital", type: Copy, dependsOn: chmodArtifactsSdkTask) {
            from docsDir
            into "${docsArtifactsDir}/api"
        }
        publicExportsTask.dependsOn(copyArtifactsDocsTask)

        def zipsTask = task ("zipsStandalone${t.capital}") {
            group = "Basic"
            description = "Creates the public zip bundles"
        }
        zips.dependsOn(zipsTask)

        // Use native zip tool so that file permissions are preserved on Windows
        def zipSdkTask = task("zipSdk$t.capital", dependsOn: publicExportsTask) {
            doLast {
                def outZipFile = "${bundlesDir}/${sdkBundleName}.zip"
                mkdir bundlesDir
                exec {
                    workingDir(artifactsDir)
                    commandLine("zip", "-q", "-r", outZipFile, sdkBundleName)
                }
            }
        }
        zipsTask.dependsOn(zipSdkTask)

        def zipDocsTask = task("zipDocs$t.capital", type: Zip, dependsOn: zipSdkTask) {
            destinationDir = file("${bundlesDir}")
            archiveName = "${docsBundleName}.zip"
            includeEmptyDirs = false
            from docsArtifactsDir
            into "${docsBundleName}"
        }
        zipsTask.dependsOn(zipDocsTask)
    }
}


/******************************************************************************
 *                                                                            *
 *                               Modules                                      *
 *                                                                            *
 *****************************************************************************/

ext.moduleDependencies = [file("dependencies")]

task buildModules {
}

// Combine the classes, lib, and bin for each module
compileTargets { t ->
    def targetProperties = project.ext[t.upper]

    def platformPrefix = targetProperties.platformPrefix
    def bundledSdkDirName = "${platformPrefix}modular-sdk"
    def bundledSdkDir = "${rootProject.buildDir}/${bundledSdkDirName}"
    def modulesDir = "${bundledSdkDir}/modules"
    def modulesCmdsDir = "${bundledSdkDir}/modules_cmds"
    def modulesLibsDir = "${bundledSdkDir}/modules_libs"
    def modulesSrcDir = "${bundledSdkDir}/modules_src"
    def modulesConfDir = "${bundledSdkDir}/modules_conf"
    def modulesLegalDir = "${bundledSdkDir}/modules_legal"
    def modulesMakeDir = "${bundledSdkDir}/make"

    final File runArgsFile = file("${rootProject.buildDir}/${RUNARGSFILE}")
    final File compileArgsFile = file("${rootProject.buildDir}/${COMPILEARGSFILE}")

    project.files(runArgsFile);

    def buildModulesTask = task("buildModules$t.capital", group: "Build") {
        // BUNDLED SDK

        // Copy dependencies/*/module-info.java.extra
        // merging as needed, removing duplicates
        // only lines with 'exports' will be copied
        def dependencyRoots = moduleDependencies
        if (rootProject.hasProperty("closedModuleDepedencies")) {
            dependencyRoots = [dependencyRoots, closedModuleDepedencies].flatten()
        }

        // Create the inputs/outputs list first to support UP-TO-DATE
        ArrayList outputNames = new ArrayList()
        dependencyRoots.each { root ->
            FileTree ft = fileTree(root).include('**/*.extra')
            ft.each() { e->
                inputs.file(e)

                String usename = e.path
                String filePath = e.getAbsolutePath()
                String folderPath = root.getAbsolutePath()
                if (filePath.startsWith(folderPath)) {
                    usename = filePath.substring(folderPath.length() + 1);
                }
                if (! outputNames.contains(usename) ) {
                    outputNames.add(usename)
                }
            }
        }

        outputNames.each() { e->
                File f = new File(modulesSrcDir, e)
                outputs.file(f)
        }

        def outputPolicyDir = "${modulesConfDir}/java.base/security"
        def outputPolicyFile = file("${outputPolicyDir}/java.policy.extra")

        outputs.file(outputPolicyFile)
        moduleProjList.each { project ->
            def policyDir = "${project.projectDir}/src/main/conf/security"
            def policyFile = file("${policyDir}/java.policy")
            if (policyFile.exists()) {
                inputs.file(policyFile)
            }
        }

        doLast {
            Map extras = [:]

            dependencyRoots.each { root ->
                FileTree ft = fileTree(root).include('**/*.extra')
                ft.each() { e->
                    String usename = e.path
                    String filePath = e.getAbsolutePath()
                    String folderPath = root.getAbsolutePath()
                    if (filePath.startsWith(folderPath)) {
                        usename = filePath.substring(folderPath.length() + 1);
                    }
                    if (extras.containsKey(usename)) {
                        List<String> lines = extras.get(usename)
                        e.eachLine { line ->
                            line = line.trim()
                            if (line.length() > 1 && Character.isLetter(line.charAt(0))) {
                                lines << line
                            }
                        }

                    } else {
                        List<String> lines = []
                        e.eachLine { line ->
                            line = line.trim()
                            if (line.length() > 1 && Character.isLetter(line.charAt(0))) {
                                lines << line
                            }
                        }
                        extras.put(usename,lines)
                    }
                }
            }
            extras.keySet().each() { e->
                File f = new File(modulesSrcDir, e)
                f.getParentFile().mkdirs()
                f.delete()

                extras.get(e).unique().each() { l->
                    f << l
                    f << "\n"
                }
            }

            // concatecate java.policy files into a single file
            //
            mkdir outputPolicyDir
            outputPolicyFile.delete()
            moduleProjList.each { project ->
                def policyDir = "${project.projectDir}/src/main/conf/security"
                def policyFile = file("${policyDir}/java.policy")
                if (policyFile.exists()) outputPolicyFile << policyFile.text
            }
        }
    }
    buildModules.dependsOn(buildModulesTask)

    // BUNDLED SDK
    moduleProjList.each { project ->
        // Copy classes, bin, and lib directories

        def moduleName = project.ext.moduleName
        def buildDir = project.buildDir

        def srcClassesDir = "${buildDir}/${platformPrefix}module-classes"
        def dstClassesDir = "${modulesDir}/${moduleName}"
        def copyClassFilesTask = project.task("copyClassFiles$t.capital", type: Copy, dependsOn: project.assemble) {
            from srcClassesDir
            into dstClassesDir
            exclude("module-info.class")
        }

        def srcCmdsDir = "${buildDir}/${platformPrefix}module-bin"
        def dstCmdsDir = "${modulesCmdsDir}/${moduleName}"
        def copyBinFilesTask = project.task("copyBinFiles$t.capital", type: Copy, dependsOn: copyClassFilesTask) {
            from srcCmdsDir
            into dstCmdsDir
        }

        def srcLibsDir = "${buildDir}/${platformPrefix}module-lib"
        def dstLibsDir = "${modulesLibsDir}/${moduleName}"
        def copyLibFilesTask = project.task("copyLibFiles$t.capital", type: Copy, dependsOn: copyBinFilesTask) {
            from srcLibsDir
            into dstLibsDir
        }

        // Copy module sources
        // FIXME: javafx.swt sources?
        def copySources = project.hasProperty("includeSources") && project.includeSources
        def copySourceFilesTask = project.task("copySourceFiles$t.capital", type: Copy, dependsOn: copyLibFilesTask) {
            if (copySources) {
                from "${project.projectDir}/src/main/java"
                if (project.name.equals("base")) {
                    from "${project.projectDir}/build/gensrc/java"
                }
                if (project.name.equals("web")) {
                    from "${project.projectDir}/src/main/native/Source/WebCore/bindings/java/dom3/java"
                }
            } else {
                from "${project.projectDir}/src/main/java/module-info.java"
            }
            into "${modulesSrcDir}/${moduleName}"
            include "**/*.java"
            if (project.hasProperty("sourceFilter")) {
                filter(project.sourceFilter)
            }
        }

        // Copy .html and other files needed for doc bundles
        def copyDocFiles = project.task("copyDocFiles$t.capital", type: Copy, dependsOn: copySourceFilesTask) {
            if (copySources) {
                from("${project.projectDir}/src/main/docs") {
                    include "**/*.html"
                    filter { line->
                        line = line.replace("@FXVERSION@", RELEASE_VERSION)
                    }
                }
                from("${project.projectDir}/src/main/docs") {
                    exclude "**/*.html"
                }
                from("${project.projectDir}/src/main/java") {
                    exclude "**/*.java"
                }

                into "${modulesSrcDir}/${moduleName}"
            }
        }

        // Copy make/build.properties
        def srcMakeDir = "${project.projectDir}/make"
        def dstMakeDir = "${modulesMakeDir}/${moduleName}"
        def copyBuildPropertiesTask = project.task("copyBuildProperties$t.capital", type: Copy, dependsOn: copyDocFiles) {
            from srcMakeDir
            into dstMakeDir
        }

        // Copy legal files
        def srcLegalDir = "${project.projectDir}/src/main/legal"
        def dstLegalDir = "${modulesLegalDir}/${moduleName}"
        def copyLegalTask = project.task("copyLegal$t.capital", type: Copy, dependsOn: copyBuildPropertiesTask) {
            from srcLegalDir
            into dstLegalDir

            // Exclude ANGLE since we (currently) do not use it
            exclude("angle.md")
        }

        buildModulesTask.dependsOn(
            copyClassFilesTask,
            copyLibFilesTask,
            copySourceFilesTask,
            copyDocFiles,
            copyBuildPropertiesTask,
            copyLegalTask)
    }

    // ============================================================

    def standaloneSdkDirName = "${platformPrefix}sdk"
    def standaloneSdkDir = "${rootProject.buildDir}/${standaloneSdkDirName}"
    def standaloneLibDir = "${standaloneSdkDir}/lib"
    def libDest=targetProperties.libDest
    def standaloneNativeDir = "${standaloneSdkDir}/${libDest}"
    def standaloneLegalDir = "${standaloneSdkDir}/legal"
    def standaloneSrcZipName = "src.zip"

    // STANDALONE SDK
    moduleProjList.each { project ->
        // Copy classes, bin, and lib directories

        def moduleName = project.ext.moduleName
        def buildDir = project.buildDir

        // Create modular jars
        def srcClassesDir = "${buildDir}/${platformPrefix}module-classes"
        def dstModularJarDir = "${standaloneLibDir}"
        def modularJarName = "${moduleName}.jar"
        def modularJarTask = project.task("modularJarStandalone$t.capital", type: Jar, dependsOn: project.assemble) {
            destinationDir = file("${dstModularJarDir}")
            archiveName = modularJarName
            includeEmptyDirs = false
            from srcClassesDir
        }

        // Copy native libraries
        def srcNativeDir = "${buildDir}/${platformPrefix}module-lib"
        def dstNativeDir = "${standaloneNativeDir}"
        def copyNativeFilesTask = project.task("copyNativeFilesStandalone$t.capital", type: Copy, dependsOn: modularJarTask) {
            from srcNativeDir
            into dstNativeDir
            include("*.dll")
        }

        // Copy other lib files
        def srcLibsDir = "${buildDir}/${platformPrefix}module-lib"
        def dstLibsDir = "${standaloneLibDir}"
        def copyLibFilesTask = project.task("copyLibFilesStandalone$t.capital", type: Copy, dependsOn: copyNativeFilesTask) {
            from srcLibsDir
            into dstLibsDir
            exclude("*.dll")
        }

        // Copy legal files
        def licenseFiles = [ "ADDITIONAL_LICENSE_INFO", "ASSEMBLY_EXCEPTION", "LICENSE" ]
        def srcLegalDir = "${project.projectDir}/src/main/legal"
        def dstLegalDir = "${standaloneLegalDir}/${moduleName}"
        def copyLegalTask = project.task("copyLegalStandalone$t.capital", type: Copy, dependsOn: copyLibFilesTask) {

            def rtDir = rootProject.file('.')
            licenseFiles.each { lFile ->
                from "${rtDir}/${lFile}"
            }

            from srcLegalDir

            into dstLegalDir

            // Exclude ANGLE since we (currently) do not use it
            exclude("angle.md")
        }

        buildModulesTask.dependsOn(
            modularJarTask,
            copyNativeFilesTask,
            copyLibFilesTask,
            copyLegalTask)
    }

    // Zip module sources for standalone SDK
    //
    // NOTE: the input is taken from the modular-sdk/modules_src dir
    // so that we don't have to duplicate the logic and create another
    // temporary directory. This is somewhat inelegant, since the bundled sdk
    // and the standalone sdk should be independent of one another, but seems
    // better than the alternatives.
    def zipSourceFilesTask = project.task("zipSourceFilesStandalone$t.capital", type: Zip, dependsOn: buildModulesTask) {
        destinationDir = file("${standaloneLibDir}")
        archiveName = standaloneSrcZipName
        includeEmptyDirs = false
        from modulesSrcDir
        include "**/*.java"
    }
    buildModules.dependsOn(zipSourceFilesTask)

    // ============================================================

    def buildRunArgsTask = task("buildRunArgs$t.capital",
            group: "Build", dependsOn: buildModulesTask) {
        outputs.file(runArgsFile);
        inputs.file(EXTRAADDEXPORTS);
        doLast() {
            List<String>modpath = []
            List<String>modnames = []

            moduleProjList.each { project ->
                def moduleName = project.ext.moduleName
                def dstModuleDir = cygpath("${modulesDir}/${moduleName}")
                if (HAS_JAVAFX_MODULES) {
                    modpath <<  "${moduleName}=${dstModuleDir}"
                } else {
                    modnames << moduleName
                }
            }

            if (HAS_JAVAFX_MODULES) {
                writeRunArgsFile(runArgsFile, computeLibraryPath(true), modpath, null)
                writeRunArgsFile(compileArgsFile, null, modpath, null)

                if (rootProject.hasProperty("EXTRA_ADDEXPORTS_STRING")) {
                    runArgsFile << EXTRA_ADDEXPORTS_STRING
                    compileArgsFile << EXTRA_ADDEXPORTS_STRING
                }
            } else {
                modpath = [ cygpath("${standaloneLibDir}") ]
                writeRunArgsFile(runArgsFile, null, modpath, modnames)
                writeRunArgsFile(compileArgsFile, null, modpath, modnames)
            }
        }
    }
    buildModules.dependsOn(buildRunArgsTask)

    def isWindows = IS_WINDOWS && t.name == "win";
    def isMac = IS_MAC && t.name == "mac";

    // Create layout for modular classes
    moduleProjList.each { project ->
        def buildModuleClassesTask = project.task("buildModule$t.capital", group: "Build", type: Copy) {
            dependsOn(project.assemble)
            def buildDir = project.buildDir
            def sourceBuildDirs = [
                "${buildDir}/classes/java/main/${project.moduleName}",
            ]

            def moduleClassesDir = "$buildDir/${platformPrefix}module-classes"
                includeEmptyDirs = false
                sourceBuildDirs.each { d ->
                    from d
                }
                into moduleClassesDir

                // Exclude obsolete, experimental, or non-shipping code
                exclude("version.rc")
                exclude("com/sun/glass/ui/swt")
                exclude("com/sun/javafx/tools/ant")
                exclude("com/javafx/main")
                exclude("com/sun/javafx/webkit/drt")
                if (!IS_INCLUDE_NULL3D) {
                    exclude ("com/sun/prism/null3d")
                }
                if (!IS_INCLUDE_ES2) {
                       exclude("com/sun/prism/es2",
                               "com/sun/scenario/effect/impl/es2")
                }

                // Exclude platform-specific classes for other platforms

                if (!isMac) {
                    exclude ("com/sun/media/jfxmediaimpl/platform/osx",
                             "com/sun/prism/es2/MacGL*",
                             "com/sun/glass/events/mac",
                             "com/sun/glass/ui/mac",
                             )
                }

                if (!isWindows) {
                    exclude ("**/*.hlsl",
                             "com/sun/glass/ui/win",
                             "com/sun/prism/d3d",
                             "com/sun/prism/es2/WinGL*",
                             "com/sun/scenario/effect/impl/hw/d3d"
                             )
                }

                if (!targetProperties.includeGTK) { //usually IS_LINUX
                    exclude (
                             "com/sun/glass/ui/gtk",
                             "com/sun/prism/es2/EGL*",
                             "com/sun/prism/es2/X11GL*"
                             )
                }

                if (!targetProperties.includeEGL) {
                    exclude ("com/sun/prism/es2/EGL*")
                }

                if (!targetProperties.includeMonocle) {
                    exclude ("com/sun/glass/ui/monocle")
                    exclude("com/sun/prism/es2/Monocle*")
                }

                if (t.name != 'ios') {
                    exclude ("com/sun/media/jfxmediaimpl/platform/ios",
                             "com/sun/glass/ui/ios",
                             "com/sun/prism/es2/IOS*"
                             )
                }

                if (t.name != 'android' && t.name != 'dalvik') {
                    exclude ("com/sun/glass/ui/android")
                }

                // Filter out other platform-specific classes
                if (targetProperties.containsKey('jfxrtJarExcludes')) {
                    exclude(targetProperties.jfxrtJarExcludes)
                }

                /* FIXME: JIGSAW -- handle this in the module itself
                String webbld = project(":web").buildDir.path
                String ctrlbld = project(":controls").buildDir.path
                if (t.name == 'android') {
                    from ("${webbld}/classes/android",
                          "${webbld}/resources/android",
                          "${ctrlbld}/classes/android",
                          "${ctrlbld}/resources/android")
                } else if (t.name == 'ios') {
                    from ("${webbld}/classes/ios",
                          "${webbld}/resources/ios")
                } else {
                    from ("${webbld}/classes/java/main")
                }
                */
        }
        buildModulesTask.dependsOn(buildModuleClassesTask)
    }

    def buildModuleLibsTask = task("buildModuleLibs$t.capital") {
        group = "Basic"

        def baseProject = project(":base");

        def graphicsProject = project(":graphics");

        def mediaProject = project(":media");

        def webProject = project(":web");
        dependsOn(webProject.assemble)

        def swtProject = project(":swt");

        def packagerProject = project(":fxpackager");
        dependsOn(packagerProject.assemble)
        dependsOn(packagerProject.jar)
        dependsOn(project(":fxpackagerservices").jar)

        def library = targetProperties.library

        def useLipo = targetProperties.containsKey('useLipo') ? targetProperties.useLipo : false
        def modLibDest = targetProperties.modLibDest
        def moduleNativeDirName = "${platformPrefix}module-$modLibDest"

        def buildModuleBaseTask = task("buildModuleBase$t.capital", dependsOn: baseProject.assemble) {
            group = "Basic"
            description = "creates javafx.base property files"

            def moduleLibDir = "${baseProject.buildDir}/${platformPrefix}module-lib"
            final File javafxProperties = file("${moduleLibDir}/javafx.properties")
            outputs.file(javafxProperties)

            if (targetProperties.containsKey("javafxPlatformProperties")) {
                final File javafxPlatformProperties = file("${moduleLibDir}/javafx.platform.properties")
                outputs.file(javafxPlatformProperties)
            }

            doLast {
                mkdir moduleLibDir

                javafxProperties.delete()
                javafxProperties << "javafx.version=$RELEASE_VERSION_SHORT";
                javafxProperties << "\n"
                javafxProperties << "javafx.runtime.version=$RELEASE_VERSION_LONG";
                javafxProperties << "\n"
                javafxProperties << "javafx.runtime.build=$PROMOTED_BUILD_NUMBER";
                javafxProperties << "\n"
                // Include any properties that have been defined (most likely in
                // one of the various platform gradle files)
                if (targetProperties.containsKey("javafxProperties")) {
                    javafxProperties << targetProperties.javafxProperties
                    javafxProperties << "\n"
                }

                // Embedded builds define this file as well
                if (targetProperties.containsKey("javafxPlatformProperties")) {
                    final File javafxPlatformProperties = file("${moduleLibDir}/javafx.platform.properties")
                    javafxPlatformProperties.delete()
                    javafxPlatformProperties << targetProperties.javafxPlatformProperties
                    javafxPlatformProperties << "\n"
                }
            }
        }

        def buildModuleGraphicsTask = task("buildModuleGraphics$t.capital", type: Copy, dependsOn: graphicsProject.assemble) {
            group = "Basic"
            description = "copies javafx.graphics native libraries"

            into "${graphicsProject.buildDir}/${moduleNativeDirName}"

            from("${graphicsProject.buildDir}/libs/jsl-decora/${t.name}/${library(targetProperties.decora.lib)}")
            def libs = ['font', 'prism', 'prismSW', 'glass', 'iio']
            if (IS_INCLUDE_ES2) {
                libs += ['prismES2'];
            }
            if (IS_COMPILE_PANGO) {
                libs += ['fontFreetype', 'fontPango'];
            }
            libs.each { lib ->
                def variants = targetProperties[lib].containsKey('variants') && !useLipo ? targetProperties[lib].variants : [null]
                variants.each { variant ->
                    def variantProperties = variant ? targetProperties[lib][variant] : targetProperties[lib]
                    from ("${graphicsProject.buildDir}/libs/$lib/$t.name/${library(variantProperties.lib)}")
                }
            }
            if (IS_WINDOWS) {
                from ("${graphicsProject.buildDir}/libs/prismD3D/${t.name}/${library(targetProperties.prismD3D.lib)}");
                targetProperties.VS2017DLLs.each { vslib ->
                    from ("$vslib");
                }
                targetProperties.WinSDKDLLs.each { winsdklib ->
                    from ("$winsdklib");
                }
            }
        }

        def buildModuleMediaTask = task("buildModuleMedia$t.capital", type: Copy, dependsOn: mediaProject.assemble) {
            group = "Basic"
            description = "copies javafx.media native libraries"

            into "${mediaProject.buildDir}/${moduleNativeDirName}"

            def mediaBuildType = project(":media").ext.buildType
            if (IS_COMPILE_MEDIA) {
                [ "fxplugins", "gstreamer-lite", "jfxmedia" ].each { name ->
                    from ("${mediaProject.buildDir}/native/${t.name}/${mediaBuildType}/${library(name)}") }

                if (t.name == "mac") {
                    // OSX media natives
                    [ "jfxmedia_qtkit", "jfxmedia_avf", "glib-lite" ].each { name ->
                        from ("${mediaProject.buildDir}/native/${t.name}/${mediaBuildType}/${library(name)}") }
                } else if (t.name == "linux") {
                    from("${mediaProject.buildDir}/native/${t.name}/${mediaBuildType}") { include "libavplugin*.so" }
                } else from ("${mediaProject.buildDir}/native/${t.name}/${mediaBuildType}/${library("glib-lite")}")
            } else {
                if (t.name != "android"  && t.name != "dalvik" ) {
                    [ "fxplugins", "gstreamer-lite", "jfxmedia" ].each { name ->
                        from ("$MEDIA_STUB/${library(name)}") }
                }

                if (t.name == "mac") {
                    // copy libjfxmedia_{avf,qtkit}.dylib if they exist
                    [ "jfxmedia_qtkit", "jfxmedia_avf", "glib-lite" ].each { name ->
                        from ("$MEDIA_STUB/${library(name)}") }
                } else if (t.name == "linux") {
                    from(MEDIA_STUB) { include "libavplugin*.so" }
                }
                else if (t.name != "android"  && t.name != "dalvik" ) {
                    from ("$MEDIA_STUB/${library("glib-lite")}")
                }
            }
        }

        def buildModuleWeb = task("buildModuleWeb$t.capital", type: Copy, dependsOn: webProject.assemble) {
            group = "Basic"
            description = "copies javafx.web native libraries"

            into "${webProject.buildDir}/${moduleNativeDirName}"

            if (IS_COMPILE_WEBKIT) {
                from ("${webProject.buildDir}/libs/${t.name}/${library('jfxwebkit')}")
            } else {
                if (t.name != "android" && t.name != "ios" && t.name != "dalvik") {
                    from ("$WEB_STUB/${library('jfxwebkit')}")
                }
            }
        }

        def buildModuleSWT = task("buildModuleSWT$t.capital", type: Copy) {
            group = "Basic"
            description = "copies SWT JAR"

            // FIXME: the following is a hack to workaround the fact that there
            // is no way to deliver javafx-swt.jar other than in one of the
            // existing runtime modules.

            dependsOn(buildModuleGraphicsTask) // we copy to the graphics module

            if (COMPILE_SWT) {
                def javafxSwtIndexTask = tasks.getByName("javafxSwtIndex${t.capital}");
                dependsOn(javafxSwtIndexTask)
                //enabled = COMPILE_SWT
            }

            // Copy javafx-swt.jar to the javafx-graphics module lib dir
            from "${swtProject.buildDir}/libs/javafx-swt.jar"
            into "${graphicsProject.buildDir}/${platformPrefix}module-lib"
        }

        def buildModulePackagerLibs = task("buildModulePackagerLibs$t.capital",
                type: Copy,
                dependsOn: [ packagerProject.assemble, project(":fxpackagerservices").assemble ]) {
            group = "Basic"
            description = "copies jdk.packager libraries"

            from "${packagerProject.buildDir}/libs"
            into "${packagerProject.buildDir}/${platformPrefix}module-lib"
        }

        def buildModulePackagerExes = task("buildModulePackagerExe$t.capital",
                type: Copy,
                dependsOn: [ packagerProject.assemble, project(":fxpackagerservices").assemble ]) {
            group = "Basic"
            description = "copies jdk.packager executable"

            // Copy over the javapackager executable
            enabled = (t.name == "win" || t.name == "linux" || t.name == "mac")

            from "${packagerProject.buildDir}/javapackager"
            into "${packagerProject.buildDir}/${platformPrefix}module-bin"
        }

        dependsOn(
            buildModuleBaseTask,
            buildModuleGraphicsTask,
            buildModuleMediaTask,
            buildModuleWeb,
            buildModuleSWT,
            buildModulePackagerLibs,
            buildModulePackagerExes
            )
    }
    buildModulesTask.dependsOn(buildModuleLibsTask)

    def zipTask = project.task("buildModuleZip$t.capital", type: Zip, group: "Build",
            dependsOn: buildModulesTask ) {

        // FIXME: JIGSAW -- this should be moved to a sub-directory so we can keep the same name
        def jfxBundle = "${platformPrefix}javafx-exports.zip"

        doFirst() {
            file("${rootProject.buildDir}/${jfxBundle}").delete()
        }

        archiveName = jfxBundle
        destinationDir = file("${rootProject.buildDir}")
        includeEmptyDirs = false
        from "${bundledSdkDir}"
    }
    jdkZip.dependsOn(zipTask)

    Task testArgFiles = task("createTestArgfiles${t.capital}") {

        File testRunArgsFile = new File(rootProject.buildDir, TESTRUNARGSFILE)
        //test (shimed) version
        File testCompileArgsFile = new File(rootProject.buildDir, TESTCOMPILEARGSFILE)
        // And a test java.policy file
        File testJavaPolicyFile = new File(rootProject.buildDir, TESTJAVAPOLICYFILE)
        // and the non-test version to go with run.args
        File runJavaPolicyFile = new File(rootProject.buildDir, RUNJAVAPOLICYFILE);

        outputs.file(testRunArgsFile)
        outputs.file(testCompileArgsFile)
        outputs.file(testJavaPolicyFile)
        outputs.file(runJavaPolicyFile)
        inputs.file(EXTRAADDEXPORTS);

        doLast() {
            rootProject.buildDir.mkdir()

            List<String> projNames = []
            moduleProjList.each { project ->
                projNames << project.name
            }

            // And the test (shimed) variation...

            testRunArgsFile.delete()
            testCompileArgsFile.delete()

            testJavaPolicyFile.delete()
            runJavaPolicyFile.delete()

            List<String> modpath = []

            if (HAS_JAVAFX_MODULES) {
                moduleProjList.each { project ->
                    if (project.hasProperty("moduleName") && project.buildModule) {
                        File dir;
                        if (project.sourceSets.hasProperty('shims')) {
                           dir = new File(rootProject.buildDir, "shims/${project.ext.moduleName}")
                        } else {
                           dir = new File(rootProject.buildDir, "modular-sdk/modules/${project.ext.moduleName}")
                        }

                        def dstModuleDir = cygpath(dir.path)
                        modpath << "${project.ext.moduleName}=${dstModuleDir}"

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

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

                writeRunArgsFile(testCompileArgsFile, null, modpath, null)
                writeRunArgsFile(testRunArgsFile, computeLibraryPath(true), modpath, null)

                if (rootProject.hasProperty("EXTRA_ADDEXPORTS_STRING")) {
                    testCompileArgsFile << EXTRA_ADDEXPORTS_STRING
                    testRunArgsFile << EXTRA_ADDEXPORTS_STRING
                }
            } else  {
                def modnames = []
                moduleProjList.each { project ->
                    if (project.hasProperty("moduleName") && project.buildModule) {
                        modnames << project.ext.moduleName
                        File dir;
                        if (project.sourceSets.hasProperty('shims')) {
                           dir = new File(rootProject.buildDir, "shims/${project.ext.moduleName}")
                        } else {
                           dir = new File(rootProject.buildDir, "sdk/lib/${project.ext.moduleName}.jar")
                        }

                        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"
                    }
                }

                writeRunArgsFile(testCompileArgsFile, null, modpath, modnames)
                writeRunArgsFile(testRunArgsFile, computeLibraryPath(true), modpath, modnames)

                appendQualExports(testCompileArgsFile, qualExportsSwing)
                appendQualExports(testRunArgsFile, qualExportsSwing)
            }
        }
    }
    sdk.dependsOn(testArgFiles)
    createTestArgfiles.dependsOn(testArgFiles)

    def sdkTask = tasks.getByName("sdk${t.capital}");
    sdkTask.dependsOn(buildModulesTask)
}
sdk.dependsOn(buildModules)

task checkrepo() {
    doLast {
        logger.info("checking for whitespace (open)");
        exec {
            if (IS_WINDOWS) {
                commandLine  'bash', 'tools/scripts/checkWhiteSpace'
            } else {
                commandLine  'bash', 'tools/scripts/checkWhiteSpace', '-x'
            }
        }
    }
}

task checkrepoall() {
    doLast {
        logger.info("checking for all whitespace (open)");
        exec {
            if (IS_WINDOWS) {
                commandLine  'bash', 'tools/scripts/checkWhiteSpace', '-a'
            } else {
                commandLine  'bash', 'tools/scripts/checkWhiteSpace', '-x', '-a'
            }
        }
    }
}

/******************************************************************************
 *                                                                            *
 *                              BUILD_CLOSED                                  *
 *                                                                            *
 * This next section should remain at the end of the build script. It allows  *
 * for a "supplemental" gradle file to be used to extend the normal build     *
 * structure. For example, this is used for passing a supplemental gradle     *
 * file for producing official JavaFX builds.                                 *
 *                                                                            *
 *****************************************************************************/

if (BUILD_CLOSED) {
    apply from: supplementalBuildFile
}

task showFlags {
}

compileTargets { t ->
    // Every platform must define these variables
    def props = project.ext[t.upper];
    showFlags.dependsOn(
        project.task("showFlags$t.upper") {
            doLast() {
                println "Properties set for $t.upper"
                props.each { println it }
            }
        }
    )

}