view build.gradle @ 3784:582edfecdb04

imported patch gradle-builders
author kcr
date Sat, 01 Jun 2013 08:00:37 -0700
parents 1d31e3a0617e
children 5e0dfe90b7f2
line wrap: on
line source
/*
 * Copyright (c) 2013, 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?
 *          - is jfxrt.jar there?
 *          - does jfxrt.jar contain stuff it shouldn't (doc-files, iml, etc)
 *          - does jfxrt.jar contain stuff it should (bss files, etc)
 *  - Perform sanity checking to make sure a JDK exists with javac, javah, etc
 *  - Support building with no known JDK location, as long as javac, javah, 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 the simple name of a library to the full name. For example, on mac
 * the simple name "glass" becomes "libglass.dylib". On Linux or Solaris, it would
 * be "libglass.so", and on Windows "glass.dll".
 *
 * @param name The simple name of the library
 * @return The name of the shared library file
 */
String library(String name) {
    return "${IS_WINDOWS ? '' : 'lib'}$name.${IS_WINDOWS ? 'dll' : IS_MAC ? 'dylib' : 'so'}"
}

/**
 * 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.
 * In the future I would like to modify this so that it only does work if
 * cygwin is installed, as I hope one day to remove the requirement to build
 * with cygwin, but at present (due to GStreamer / Webkit) cygwin is needed
 * anyway.
 * 
 * @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;
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    logger.info("Converting path '$path' via cygpath")
    exec {
        standardOutput = out
        commandLine "cmd", "/c", "cygpath", "-m", path
    }
    return out.toString().trim();
}

/**
 * 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) {
    COMPILE_TARGETS.split(",").each { target ->
        CompileTarget ct = new CompileTarget();
        ct.name = target;
        ct.upper = target.trim().toUpperCase()
        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) { }
    }
}

/******************************************************************************
 *                                                                            *
 *                   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   *
 *                                                                            *
 *****************************************************************************/

// BUILD_CLOSED is used to specify that a "closed" build should be done.
// It is false by default and is set by a command line property. If true then
// the closed build and property files will be read from
// ../rt-closed/closed-build.gradle and ../rt-closed/closed-properties.gradle
// respectively

def buildClosed = false;
if (hasProperty("BUILD_CLOSED")) {
    buildClosed = Boolean.parseBoolean(BUILD_CLOSED);
}
ext.BUILD_CLOSED = buildClosed

def closedDir = file("../rt-closed");
def supplementalPropertyFile = file("$closedDir/closed-properties.gradle");
def supplementalBuildFile = file("$closedDir/closed-build.gradle");

if (BUILD_CLOSED) {
    apply from: supplementalPropertyFile
}

// 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")

// Get the JDK_HOME automatically based on the version of Java used to execute gradle. Or, if specified,
// use a user supplied JDK_HOME, BINARY_STUB, JAVAC, and/or JAVAH, 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("JAVA_HOME"))
ext.JAVA_HOME = envJavaHome == null || envJavaHome.equals("") ? System.getProperty("java.home") : envJavaHome
def javaHomeFile = file(JAVA_HOME)
defineProperty("JDK_HOME",
        javaHomeFile.name == "jre" ?
        javaHomeFile.getParent().toString() :
        javaHomeFile.name.startsWith("jre") ?
        new File(javaHomeFile.getParent(), "jdk1.${javaHomeFile.name.substring(3)}.0").toString() :
        JAVA_HOME) // we have to bail and set it to something and this is as good as any!
defineProperty("JAVA", cygpath("$JDK_HOME/bin/java${IS_WINDOWS ? '.exe' : ''}"))
defineProperty("JAVAC", cygpath("$JDK_HOME/bin/javac${IS_WINDOWS ? '.exe' : ''}"))
defineProperty("JAVAH", cygpath("$JDK_HOME/bin/javah${IS_WINDOWS ? '.exe' : ''}"))
defineProperty("JAVADOC", cygpath("$JDK_HOME/bin/javadoc${IS_WINDOWS ? '.exe' : ''}"))
defineProperty("JDK_DOCS", "http://download.oracle.com/javase/7/docs/api")
defineProperty("BINARY_STUB", cygpath("$JDK_HOME/jre/lib/ext/jfxrt.jar"))
ext.BINARY_STUB = "".equals(BINARY_STUB) ? null : files(BINARY_STUB)

// COMPILE_WEBKIT specifies whether to build all of webkit. COMPILE_GSTREAMER
// specifies whether to build GStreamer. Both of these can be set by a
// command line property
defineProperty("COMPILE_WEBKIT", "false")
defineProperty("COMPILE_GSTREAMER", "false")
if (COMPILE_WEBKIT instanceof String) ext.COMPILE_WEBKIT = Boolean.parseBoolean(COMPILE_WEBKIT)
if (COMPILE_GSTREAMER instanceof String) ext.COMPILE_GSTREAMER = Boolean.parseBoolean(COMPILE_GSTREAMER)

// 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.7.2.v3740f" :
    IS_WINDOWS && IS_64 ? "org.eclipse.swt.win32.win32.x86_64_3.7.2.v3740f" :
    IS_WINDOWS && !IS_64 ? "org.eclipse.swt.win32.win32.x86_3.7.2.v3740f" :
    IS_LINUX && IS_64 ? "org.eclipse.swt.gtk.linux.x86_64_3.7.2.v3740f" :
    IS_LINUX && !IS_64 ? "org.eclipse.swt.gtk.linux.x86_3.7.2.v3740f" : ""

// Specify the build configuration (Release or Debug)
defineProperty("CONF", "Release")
ext.IS_DEBUG = CONF == "Debug"

// 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", "none")
ext.IS_DOC_LINT = DOC_LINT != "none"

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

// Define the number of threads to use when compiling (specifically for native compilation)
defineProperty("NUM_COMPILE_THREADS", "${Runtime.runtime.availableProcessors()}")

// Define settings used when creating the VersionInfo. These settings are generally left alone
// by developers and set only from Hudson. We have to manually rev this version number from
// release to release.
//
defineProperty("HUDSON_JOB_NAME", "not_hudson")
defineProperty("HUDSON_BUILD_NUMBER","0000")
defineProperty("PROMOTED_BUILD_NUMBER", "00")
defineProperty("PRODUCT_NAME", "OpenJFX")
// TODO: set the values of RAW_VERSION, RELEASE_NAME, and RELEASE_MILESTONE based on the
// settings in build.properties
defineProperty("RAW_VERSION", "8.0.0")
defineProperty("RELEASE_NAME", "8.0")
defineProperty("RELEASE_MILESTONE", "ea")

// 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")

// Now we need to define the native compilation tasks. The set of parameters to
// native compiliation 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()
            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
    }
}

// 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];
    ["jfxrtJarExcludes", "compileSwing", "compileSWT", "compileFXPackager", "compileDesignTime", "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;
ext.COMPILE_DESIGN_TIME = false;
compileTargets { t ->
    if (project.ext[t.upper].compileSwing) COMPILE_SWING = true
    if (project.ext[t.upper].compileSWT) COMPILE_SWT = true
    if (project.ext[t.upper].compileFXPackager) COMPILE_FXPACKAGER = true
    if (project.ext[t.upper].compileDesignTime) COMPILE_DESIGN_TIME = true
}

/******************************************************************************
 *                                                                            *
 *                         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     *
 *                                                                            *
 *****************************************************************************/

// 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) logger.error("Unsupported build OS ${OS_NAME}")
if (IS_WINDOWS && OS_ARCH != "x86" && OS_ARCH != "amd64") {
    throw new Exception("Unknown and unsupported build architecture: $OS_ARCH")
} else if (IS_MAC && OS_ARCH != "x86_64") {
    throw new Exception("Unknown and unsupported build architecture: $OS_ARCH")
} else if (IS_LINUX && OS_ARCH != "i386" && OS_ARCH != "amd64") {
    throw new Exception("Unknown and unsupported build architecture: $OS_ARCH")
}

// 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!")
}

// Make sure JAVA_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(JAVAH).exists()) throw new Exception("Missing or incorrect path to 'javah': '$JAVAH'. 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")
if (BINARY_STUB != null && !BINARY_STUB.getSingleFile().exists()) logger.warn("Missing or incorrect path to 'jfxrt.jar': '${BINARY_STUB.asPath}'. Perhaps bad JDK_HOME? $JDK_HOME")

// Verify that CONF is something useful
if (CONF != "Release" && CONF != "Debug") 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 that Gradle 1.4 is in use. We haven't tried any other versions.
if (gradle.gradleVersion != "1.4") {
    logger.warn("Unsupported gradle version $gradle.gradleVersion in use. Only 1.4 is supported")
}

/******************************************************************************
 *                                                                            *
 *                      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("OS_NAME: $OS_NAME")
logger.quiet("OS_ARCH: $OS_ARCH")
logger.quiet("JAVA_HOME: $JAVA_HOME")
logger.quiet("JDK_HOME: $JDK_HOME")
logger.quiet("COMPILE_TARGETS: $COMPILE_TARGETS")
logger.quiet("COMPILE_FLAGS_FILES: $COMPILE_FLAGS_FILES")
logger.quiet("BINARY_STUB: ${BINARY_STUB == null ? '' : BINARY_STUB.asPath}")
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("RAW_VERSION: $RAW_VERSION")
logger.quiet("RELEASE_NAME: $RELEASE_NAME")
logger.quiet("RELEASE_MILESTONE: $RELEASE_MILESTONE")

/******************************************************************************
 *                                                                            *
 *                Definition of Native Code Compilation Tasks                 *
 *                                                                            *
 *    - JavaHeaderTask is used to run javah. The JAVAH property will point at *
 *      the version of javah to be used (i.e.: a path to javah)               *
 *    - CCTask compiles native code. Specifically it will compile .m, .c,     *
 *      .cpp, or .cc files. It uses the headers provided by the               *
 *      JavaHeaderTask 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 builders, JSL files, etc)
ext.BUILD_SRC = rootProject.files("buildSrc/build/libs/buildSrc.jar")

/**
 * Convenience method for creating javah, cc, link, and "native" tasks in the given project. These
 * tasks are parameterized by name, so that we can produce, for example, javahGlass, 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.
 * @param libName The name of the shared library we will create. Should be
 *        something like "prism-common", which we will turn into libprism-common.dylib
 *        or whatnot depending on the platform.
 */
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"
    }
    project.assemble.dependsOn(nativeTask)
    // 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 headerRootDir = project.file("$project.buildDir/generated-src/headers/$name")
    def nativeRootDir = project.file("$project.buildDir/native/$name")
    def libRootDir = project.file("$project.buildDir/libs/$name")
    // For each compile target, create a javah / cc / link triplet
    compileTargets { t ->
        def targetProperties = project.rootProject.ext[t.upper]
        def properties = targetProperties.get(name)
        def nativeDir = file("$nativeRootDir/${t.name}")
        def headerDir = file("$headerRootDir/${t.name}")
        def javahTask = project.task("javah${t.capital}${capitalName}", type: JavaHeaderTask, dependsOn: project.classes, group: "Build") {
            description = "Generates JNI Headers for ${name} for ${t.name}"
            source(project.sourceSets.main.output.classesDir)
            classpath = project.files(project.sourceSets.main.output.classesDir)
            if (rootProject.BINARY_STUB != null) classpath += rootProject.BINARY_STUB
            output = headerDir
            include(properties.javahInclude)
        }

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

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

    def compileCompilers = project.task("compile${name}Compilers", type: JavaCompile, dependsOn: project.compileJava) {
        description = "Compile the $name JSL Compilers"
        classpath = project.files(project.sourceSets.main.output.classesDir) +
                rootProject.BUILD_SRC +
                project.configurations.antlr3
        source = [project.file("src/main/jsl-$lowerName")]
        destinationDir = project.file("$project.buildDir/classes/jsl-compilers/$lowerName")
    }

    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/generated-src/jsl-$lowerName")
        inputs.dir sourceDir
        outputs.dir destinationDir
        doLast {
            compile(sourceDir, destinationDir)
        }
    }

    project.task("compile${name}JavaShaders", type: JavaCompile, dependsOn: generateShaders) {
        description = "Compile the Java $name JSL shaders"
        classpath = project.files(project.sourceSets.main.output.classesDir) + rootProject.BUILD_SRC
        source = [project.file("$project.buildDir/generated-src/jsl-$lowerName")]
        destinationDir = project.file("$project.buildDir/classes/jsl-$lowerName")
    }

    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/generated-src/jsl-$lowerName/$pkg")
    }

    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/generated-src/jsl-$lowerName") {
            include("**/*.frag")
        }
        into "$project.buildDir/resources/jsl-$lowerName"
    }
}

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

allprojects {
    // 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
    if (project == rootProject || project.name == "media" || project.name == "web") return
    // All of our projects are java projects
    apply plugin: "java"
    sourceCompatibility = 1.8

    // 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.
    repositories {
        mavenCentral()
        ivy {
            url "http://download.eclipse.org/eclipse/updates/3.7/R-3.7.2-201202080800/plugins/"
            layout "pattern", {
                artifact "[artifact].[ext]"
            }
        }
        flatDir {
            dir "$JDK_HOME/jre/lib/"
        }
    }

    // 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");
    }

    // 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 {
        jvmArgs("-Djava.ext.dirs=");
        if (rootProject.BINARY_STUB != null) classpath += rootProject.BINARY_STUB;
        executable = JAVA;
        enableAssertions = true;
        testLogging.exceptionFormat = "full";
        scanForTestClasses = false;
        include("**/*Test.*");
        exclude("**/DepthTest.*");
        exclude("**/*Abstract*.*");
    }

    // Exclude any non-public-API classes from having javadoc generated. This block is used
    // when generating JavaDocs for a specific project. When generating the JavaDocs for the
    // entire SDK, a different javadoc command is used (see the javadoc task on the top level)
    javadoc {
        exclude("com/**/*", "javafx/scene/ParentDesignInfo*", "Compile*", "javafx/builder/**/*");
        executable = JAVADOC;
        options.windowTitle("JavaFX Project ${project.name} ${RELEASE_NAME}")
        options.links(JDK_DOCS);
        options.addBooleanOption("XDignore.symbol.file").setValue(true);
        options.addBooleanOption("Xdoclint:none").setValue(!IS_DOC_LINT);
        options.addBooleanOption("javafx").setValue(true);
        // All doc-files are located in src/main/docs because Gradle's javadoc doesn't copy
        // over the doc-files if they are embedded with the sources. I find this arrangement
        // somewhat cleaner anyway (never was a fan of mixing javadoc files with the sources)
        doLast {
            copy {
                from "src/main/docs"
                into "$buildDir/docs/javadoc"
            }
        }
    }
}

// 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") {
    dependencies {
        compile BUILD_SRC
    }

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

    // Make sure to include $buildDir/generated-src/version-info that we previously created.
    // We DO NOT want to include src/main/version-info
    sourceSets.main.java.srcDirs = ["src/main/java", "$buildDir/generated-src/version-info"]
    compileJava.dependsOn processVersionInfo
}

// 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") {
    // Workaround for lack of Antlr 3 support in Gradle. By defining a configuration,
    // we can then give it a class path and use that classpath to execute a java command
    getConfigurations().add("antlr3");

    sourceSets {
        main
        test
        stub
    }

    dependencies {
        compile project(":base"), BUILD_SRC
        compile name: "plugin"
        compile name: SWT_FILE_NAME
        stubCompile group: "junit", name: "junit", version: "4.8.2",
            project(":base").sourceSets.test.output, sourceSets.main.output
        antlr3 group: "org.antlr", name: "antlr", version: "3.1.3"
    }

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

    addNative(project, "iio")
    addNative(project, "prismES2")

    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
            dependsOn javahWinPrismD3D
            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_s1a.h", "/DSpec=1", "/DSType=1", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_s2a.h", "/DSpec=2", "/DSType=1", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_s3a.h", "/DSpec=3", "/DSType=1", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_s1s.h", "/DSpec=1", "/DSType=2", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_s2s.h", "/DSpec=2", "/DSType=2", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_s3s.h", "/DSpec=3", "/DSType=2", "$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_b1a.h", "/DSpec=1", "/DSType=1", "/DBump=1", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_b2a.h", "/DSpec=2", "/DSType=1", "/DBump=1", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_b3a.h", "/DSpec=3", "/DSType=1", "/DBump=1", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_b1s.h", "/DSpec=1", "/DSType=2", "/DBump=1", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_b2s.h", "/DSpec=2", "/DSType=2", "/DBump=1", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_b3s.h", "/DSpec=3", "/DSType=2", "/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_s1ai.h", "/DSpec=1", "/DSType=1", "/DIllumMap=1", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_s2ai.h", "/DSpec=2", "/DSType=1", "/DIllumMap=1", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_s3ai.h", "/DSpec=3", "/DSType=1", "/DIllumMap=1", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_s1si.h", "/DSpec=1", "/DSType=2", "/DIllumMap=1", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_s2si.h", "/DSpec=2", "/DSType=2", "/DIllumMap=1", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_s3si.h", "/DSpec=3", "/DSType=2", "/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_b1ai.h", "/DSpec=1", "/DSType=1", "/DBump=1", "/DIllumMap=1", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_b2ai.h", "/DSpec=2", "/DSType=1", "/DBump=1", "/DIllumMap=1", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_b3ai.h", "/DSpec=3", "/DSType=1", "/DBump=1", "/DIllumMap=1", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_b1si.h", "/DSpec=1", "/DSType=2", "/DBump=1", "/DIllumMap=1", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_b2si.h", "/DSpec=2", "/DSType=2", "/DBump=1", "/DIllumMap=1", "$PS_3D_SRC"],
                        ["$FXC", "/nologo", "/T", "ps_2_0", "/Fh", "$buildDir/headers/PrismD3D/hlsl/Mtl1PS_b3si.h", "/DSpec=3", "/DSType=2", "/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/generated-src/jsl-*
    //      3) Compile the JSL Java sources in $buildDir/generated-src/jsl-* and put the output
    //         into classes/jsl-*
    //      4) Compile the native JSL sources in $buildDir/generated-src/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.

    addJSL(project, "Decora", "com/sun/scenario/effect/impl/hw/d3d/hlsl") { 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 = "modules/graphics"
                main = settings.generator
                classpath = configurations.compile + configurations.antlr3
                classpath += files("$buildDir/classes/main")
                classpath += files("$buildDir/classes/jsl-compilers/decora")
                args = ["-i", sourceDir, "-o", destinationDir, "-t", "-pkg", "com/sun/scenario/effect", "$settings.outputs", "$settings.fileName"]
                jvmArgs "-Djava.ext.dirs="
            }
        }
    }

    task generateDecoraNativeHeaders(type: JavaHeaderTask, dependsOn: compileDecoraJavaShaders) {
        description = "Generates JNI Headers for Decora SSE Natives"
        source file("$buildDir/classes/jsl-decora")
        source file("$buildDir/classes/main")
        include("com/sun/scenario/effect/impl/sw/sse/*");
        classpath = files("$buildDir/classes/main", "$buildDir/classes/jsl-decora")
        output = file("$buildDir/generated-src/headers/jsl-decora")
    }

    task nativeDecora(dependsOn: compileDecoraHLSLShaders);

    def headerDir = file("$buildDir/generated-src/headers/jsl-decora")
    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 properties = rootProject.ext[upperTarget];

        def ccTask = task("compileDecoraNativeShaders$capitalTarget", type: CCTask, dependsOn: generateDecoraNativeHeaders) {
            description = "Compiles Decora SSE natives"
            matches = ".*\\.cc"
            source file("$buildDir/generated-src/jsl-decora")
            source file("modules/graphics/src/main/native-decora")
            headers = headerDir
            params.addAll(properties.decora.ccFlags)
            output(file("$nativeRootDir/$target"))
            compiler = properties.decora.compiler
        }

        def linkTask = task("linkDecoraNativeShaders$capitalTarget", type: LinkTask, dependsOn: ccTask) {
            description = "Creates native dynamic library for Decora SSE"
            objectDir = file("$nativeRootDir/$target")
            linkParams.addAll(properties.decora.linkFlags)
            lib = file("$libRootDir/$t.name/${library(properties.decora.lib)}")
            linker = properties.decora.linker
        }

        nativeDecora.dependsOn(linkTask)
    }

    // Prism JSL
    addJSL(project, "Prism", "com/sun/prism/d3d/hlsl") { sourceDir, destinationDir ->
        def inputFiles = fileTree(dir: sourceDir)
        inputFiles.include "**/*.jsl"
        inputFiles.each { file ->
            javaexec {
                executable = JAVA
                workingDir = "modules/graphics"
                main = "CompileJSL"
                classpath = configurations.compile + configurations.antlr3
                classpath += files("$buildDir/classes/jsl-compilers/prism", "modules/graphics/src/main/jsl-prism") // for the .stg
                args = ["-i", sourceDir, "-o", destinationDir, "-t", "-pkg", "com/sun/prism", "-d3d", "-es2", "-name", "$file"]
                jvmArgs "-Djava.ext.dirs="
            }
        }
    }

    classes.dependsOn compilePrismJavaShaders;
    nativePrism.dependsOn compilePrismHLSLShaders;

    // Create a single "native" task which will depend on all the individual native tasks for graphics
    task("native", dependsOn: [nativeGlass, nativePrism, nativePrismSW, nativeDecora]) {
        group = "Build"
        description = "Compiles and Builds all native libraries for Graphics"
    }
    assemble.dependsOn nativeDecora
    processResources.dependsOn processDecoraShaders, processPrismShaders

    // The graphics project needs to completely replace the "test" task because the
    // tests have been split among the normal tests and the stub tests. I want to make sure
    // that invoking "graphics:test" runs both of these tasks, rather than only one of them.
    tasks.replace("test")
    test {
        group = "Verification"
        description = "Runs all the unit tests for Graphics"
    }
    task testWithoutStub(type: Test, dependsOn: ":graphics:compileJava", group: "Verification") {
        jvmArgs "-Djava.ext.dirs="
        classpath = sourceSets.test.runtimeClasspath
        if (rootProject.BINARY_STUB != null) classpath += rootProject.BINARY_STUB
    }
    task testWithStub(type: Test, dependsOn: [":graphics:compileJava"], group: "Verification") {
        jvmArgs "-Djava.ext.dirs=", "-Djavafx.toolkit=com.sun.javafx.pgstub.StubToolkit", "-DCSS_META_DATA_TEST_DIR=${file('$buildDir/classes/main/javafx')}"
        classpath = sourceSets.stub.runtimeClasspath + sourceSets.main.runtimeClasspath
        if (rootProject.BINARY_STUB != null) classpath += rootProject.BINARY_STUB
        testClassesDir = file("$buildDir/classes/stub")
        testResultsDir = file("$buildDir/stub-results")
        testReportDir = file("$buildDir/reports/stub")
    }
    [testWithoutStub, testWithStub].each { test ->
        test.enableAssertions = true
        test.testLogging.exceptionFormat = "full"
        test.scanForTestClasses = false
        test.include "**/*Test.*"
        test.exclude "**/*Abstract*.*"
    }
    test.dependsOn testWithoutStub, testWithStub

}

project(":controls") {
    dependencies {
        compile BUILD_SRC, project(":base"), project(":graphics"), project(":designTime")
        // TODO not sure how to specify this? processResources project(":base"), project(":graphics")
        testCompile project(":graphics").sourceSets.stub.output
    }

    test {
        jvmArgs "-Djavafx.toolkit=com.sun.javafx.pgstub.StubToolkit"
        // TODO fix the Controls tests so that we can run them all in one VM
        // This is highly unfortunate, test execution time goes from 7 sec to 2 min 8 sec
        // however there are 8 failing tests when we don't fork. Need to fix those tests.
        forkEvery = 1
    }

    // TODO Css2Bin really should be moved out and put into buildSrc if it can be
    // TODO could change script to dynamically locate all .css files and create bss for them, probably better
    // TODO also not sure there is any benefit to having css files in the jfxrt.jar at all
    processResources << {
        ["$buildDir/resources/main/com/sun/javafx/scene/control/skin/caspian/caspian.css",
        "$buildDir/resources/main/com/sun/javafx/scene/control/skin/caspian/embedded-qvga.css",
        "$buildDir/resources/main/com/sun/javafx/scene/control/skin/caspian/embedded.css",
        "$buildDir/resources/main/com/sun/javafx/scene/control/skin/caspian/fxvk.css",
        "$buildDir/resources/main/com/sun/javafx/scene/control/skin/caspian/highcontrast.css",
        "$buildDir/resources/main/com/sun/javafx/scene/control/skin/modena/modena.css",
        "$buildDir/resources/main/com/sun/javafx/scene/control/skin/modena/touch.css"].each { css ->
            javaexec {
                executable = JAVA
                workingDir = "modules/controls"
                classpath files("$buildDir/classes/main",
                        project(":graphics").sourceSets.main.output,
                        project(":base").sourceSets.main.output)
                main = "com.sun.javafx.css.parser.Css2Bin"
                args css
                jvmArgs "-Djava.ext.dirs="
            }
        }
    }
}

project(":swing") {
    tasks.all {
        if (!COMPILE_SWING) it.enabled = false
    }
    dependencies {
        compile BUILD_SRC, project(":base"), project(":graphics")
    }
}

project(":swt") {
    tasks.all {
        if (!COMPILE_SWT) it.enabled = false
    }
    dependencies {
        compile BUILD_SRC, project(":base"), project(":graphics")
        compile name: SWT_FILE_NAME
    }
}

project(":fxml") {
    dependencies {
        compile BUILD_SRC, project(":base"), project(":graphics"),
                project(":controls"), project(":swt"), project(":swing")
        testCompile project(":graphics").sourceSets.stub.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=com.sun.javafx.pgstub.StubToolkit"
    }
}

project(":builders") {
    sourceCompatibility = 1.7

    dependencies {
        compile BUILD_SRC, project(":base"), project(":graphics"),
                project(":controls"), project(":swt"), project(":swing")
        testCompile project(":graphics").sourceSets.stub.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=com.sun.javafx.pgstub.StubToolkit"
    }
}

project(":designTime") {
    tasks.all {
        if (!COMPILE_DESIGN_TIME) it.enabled = false
    }
    dependencies {
        compile project(":graphics")
    }
}

project(":fxpackager") {
    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
    repositories {
        maven {
            url "https://repository.apache.org"
        }
    }

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

    // When producing the jar, we need to relocate a few class files
    // from their normal location to a resources/classes or resources/web-files
    // location
    jar {
        includeEmptyDirs = false
        archiveName = "ant-javafx.jar"
        eachFile { FileCopyDetails details ->
            if (details.path.startsWith("com/javafx/main")) {
                details.path = "resources/classes/$details.path"
            } else if (details.path.startsWith("web-files")) {
                details.path = "resources/$details.path"
            }
        }
    }

    // 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

    // Compile the native launchers. These are included in ant-javafx.jar
    // TODO should teach this to know 32 / 64 bit
    if (IS_WINDOWS && COMPILE_FXPACKAGER) {
        task compileWinLauncher(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);
            doLast {
                copy {
                    from "$buildDir/native/WinLauncher/WinLauncher.exe"
                    into "$buildDir/classes/main/com/sun/javafx/tools/resource/windows"
                }
            }
        }
        task compileIconSwap(type: CCTask, group: "Build") {
            description = "Compiles native sources for the application co-bundle launcher"
            matches = "IconSwap\\.cpp"
            params.addAll(WIN.iconLauncher.ccFlags)
            output(file("$buildDir/native/IconSwap"))
            source file("src/main/native/launcher/win")
            compiler = WIN.launcher.compiler
            exe = true
            linkerOptions.addAll(WIN.iconLauncher.linkFlags)
            doLast {
                copy {
                    from "$buildDir/native/IconSwap/IconSwap.exe"
                    into "$buildDir/classes/main/com/sun/javafx/tools/resource/windows"
                }
            }
        }
        task compileLauncher(dependsOn: [compileWinLauncher, compileIconSwap])
        jar.dependsOn compileLauncher;
    } else if (COMPILE_FXPACKAGER) {
        task compileLauncher(type: CCTask, group: "Build") {
            description = "Compiles native sources for the application co-bundle launcher"
            if (IS_MAC) {
                matches = ".*\\.m"
                output(file("$buildDir/classes/main/com/sun/javafx/tools/resource/mac"))
                params.addAll(MAC.launcher.ccFlags)
                source file("src/main/native/launcher/mac")
                compiler = MAC.launcher.compiler
                eachOutputFile = { f ->
                    return new File(f.getParent(), "JavaAppLauncher")
                }
            } else {
                matches = ".*\\.c"
                output(file("$buildDir/classes/main/com/sun/javafx/tools/resource/linux"))
                params.addAll(LINUX.launcher.ccFlags)
                compiler = LINUX.launcher.compiler
                linkerOptions.addAll(LINUX.launcher.linkFlags)
                source file("src/main/native/launcher/linux")
                eachOutputFile = { f ->
                    return new File(f.getParent(), "JavaAppLauncher")
                }
            }
        }
        jar.dependsOn compileLauncher;
    }

    // Builds the javafxpackager 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 buildJavaFXPackager(type: CCTask, group: "Build") {
            description = "Compiles native sources for javafxpackager.exe"
            matches = "javafxpackager\\.cpp"
            params.addAll(WIN.fxpackager.ccFlags)
            compiler = WIN.fxpackager.compiler
            output(file("$buildDir/native/javafxpackager"))
            source WIN.fxpackager.nativeSource
            doLast {
                mkdir "$buildDir/native"
                exec({
                    commandLine("$RC", "/nologo", "/l", "0x409", "/r", "/dJFX_DVERSION=8", "/dJFX_VERSION=8",
                            "/fo$buildDir/native/javafxpackager/javafxpackager.res",
                            "src/main/native/javafxpackager/win/javafxpackager.rc");
                    environment(WINDOWS_NATIVE_COMPILE_ENVIRONMENT);
                });
            }
            doLast {
                mkdir "$buildDir/javafxpackager"
                exec({
                    commandLine("$WIN.fxpackager.linker", "/nologo", "/opt:REF", "/incremental:no", "/manifest", "kernel32.lib", "advapi32.lib",
                            "/out:$buildDir/native/javafxpackager/javafxpackager.exe",
                            "$buildDir/native/javafxpackager/javafxpackager.obj",
                            "$buildDir/native/javafxpackager/javafxpackager.res")
                    environment(WINDOWS_NATIVE_COMPILE_ENVIRONMENT)
                })
            }
            doLast {
                copy {
                    from file("src/main/native/javafxpackager/win/javafxpackager.manifest")
                    into file("$buildDir/native/javafxpackager")
                }
                // TODO, not sure MT.exe is actually being used currently, so for now skipping.
//                exec({
//                    commandLine("$MT -nologo -manifest $buildDir/native/javafxpackager/javafxpackager.manifest")
//                    environment(WINDOWS_NATIVE_COMPILE_ENVIRONMENT)
//                })
                copy {
                    from file("$buildDir/native/javafxpackager/javafxpackager.exe")
                    into file("$buildDir/javafxpackager")
                }
            }
        }
    } else {
        task buildJavaFXPackager(group: "Build") << {
            enabled = COMPILE_FXPACKAGER
            copy {
                from "src/main/native/javafxpackager/shell"
                into "$buildDir/javafxpackager"
                fileMode = 0755
            }
        }
    }

    jar.dependsOn buildJavaFXPackager
}

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.
    //
    // Also, we need to remove jfxrt.jar from the ext classpath (if it is there)
    // and instead each module that depends on closed bits is going to need to
    // add BINARY_STUB to its list of dependencies.
    tasks.withType(Compile) { compile ->
        // It looks like we have to use ant to compile instead of the built-in gradle
        // compiler stuff because otherwise it won't compile on CYGWIN
        // TODO need to file issue with Gradle
        compile.options.useAnt = true
        compile.options.debug = true // we always generate debugging info in the class files
        compile.options.debugOptions.debugLevel = IS_DEBUG ? "source,lines,vars" : "source,lines"
        compile.options.fork = true
        compile.options.forkOptions.executable = JAVAC
        compile.options.warnings = IS_LINT
        compile.options.useDepend = USE_DEPEND
        compile.options.compilerArgs = ["-Djava.ext.dirs=", "-XDignore.symbol.file", "-encoding", "UTF-8"]

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

        // Somewhat dangerous in case there is a java source set other than "test" or "stub" or "main"!
        // Note that the compileFooJava task is automatically created whenever a source set is
        // defined. So the "test" source set automatically creates a "compileTestJava" task (among others).
        if (name == "compileTestJava") {
            classpath = sourceSets.test.compileClasspath
            if (rootProject.BINARY_STUB != null) classpath += rootProject.BINARY_STUB
        } else if (name == "compileStubJava") {
            classpath = sourceSets.stub.compileClasspath
            if (rootProject.BINARY_STUB != null) classpath += rootProject.BINARY_STUB
        } else if (name == "compileJava") {
            // Not everything needs the builder processor
            classpath = sourceSets.main.compileClasspath
            if (rootProject.BINARY_STUB != null) classpath += rootProject.BINARY_STUB
        } else {
            logger.info("Not using BINARY_STUB in compilation: $name")
        }
    }
}

/******************************************************************************
 *                                                                            *
 *                             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 javadoc(type: Javadoc) {
    group = "Basic"
    description = "Generates the JavaDoc for all the public API"
    executable = JAVADOC
    def projectsToDocument = [
            project(":base"), project(":graphics"), project(":controls"),
            project(":swing"), project(":swt"), project(":fxml")]
    source(projectsToDocument.collect({
        [it.sourceSets.main.java, "$it.buildDir/generated-src/builders"]
    }));
    setDestinationDir(new File(buildDir, 'javadoc'));
    // Might need a classpath
    classpath = files(projectsToDocument.collect { project ->
        project.sourceSets.main.compileClasspath
    });
    classpath += files(projectsToDocument.collect { project ->
        project.sourceSets.main.output
    });
    exclude("com/**/*", "javafx/scene/ParentDesignInfo*", "Compile*", "javafx/builder/**/*");
    options.windowTitle("JavaFX ${RELEASE_NAME}")
    options.links(JDK_DOCS);
    options.addBooleanOption("XDignore.symbol.file").setValue(true);
    options.addBooleanOption("Xdoclint:none").setValue(!IS_DOC_LINT);
    options.addBooleanOption("javafx").setValue(true);
    doLast {
        projectsToDocument.each { p ->
            copy {
                from "$p.projectDir/src/main/docs"
                into "$buildDir/javadoc"
            }
        }
    }

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

task jfxrt()

task sdk()

task apps() {
    dependsOn(sdk)

    // TODO: implement this
    doLast {
        println "rt/apps not yet built"
    }
}

task fullTest() {
// TODO: WHY DOES THE FOLLOWING NOT WORK???
//    dependsOn(test)

    // TODO additional tests
    doLast {
        println "fullTest task is not implemented"
    }
}

task findbugs() {
    dependsOn(sdk)

    // TODO: implement this
    doLast {
        if (!BUILD_CLOSED) {
            println "findbugs task is not implemented"
        }
    }
}

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

// TODO: consider moving the "public-sdk" portion of this task here
task publicExports() {
    dependsOn(sdk)
    doLast {
        if (!BUILD_CLOSED) {
            println "publicExports task is only run for a closed build"
        }
    }
}

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

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

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


compileTargets { t ->
    def targetProperties = project.ext[t.upper]
    // The jfxrt task is responsible for creating the jfxrt.jar. A developer may
    // have multiple SDK's on their system at any one time, depending on which
    // cross compiles they have done. For example, I might have:
    //      build/mac-sdk/rt/lib/ext/jfxrt.jar
    //      build/ios-sdk/rt/lib/ext/jfxrt.jar
    //      build/win-sdk/rt/lib/ext/jfxrt.jar
    //      build/armhf-sdk/rt/lib/ext/jfxrt.jar
    //
    // and so forth. This arrangement allows for multiple independent SDKs to
    // exist on a developer's system.
    def jfxrtTask = task("jfxrt$t.capital", type: Jar) {
        group = "Basic"
        description = "Creates the jfxrt.jar for the $t.name target"
        archiveName = "build/${t.name}-sdk/rt/lib/ext/jfxrt.jar";
        includeEmptyDirs = false
        from("modules/base/build/classes/main",
             "modules/base/build/resources/main",
             "modules/builders/build/classes/main",
             "modules/graphics/build/classes/main",
             "modules/graphics/build/resources/main",
             "modules/controls/build/classes/main",
             "modules/controls/build/resources/main",
             "modules/fxml/build/classes/main",
             "modules/fxml/build/resources/main",
             "modules/graphics/build/classes/jsl-decora",
             "modules/graphics/build/resources/jsl-decora",
             "modules/graphics/build/classes/jsl-prism",
             "modules/graphics/build/resources/jsl-prism");
        if (COMPILE_SWING) from ("modules/swing/build/classes/main", "modules/swing/build/resources/main")
        if (COMPILE_SWT) from ("modules/swt/build/classes/main", "modules/swt/build/resources/main")
        exclude("js/**/*", // er...
                "PrismLoaderBackend*", // More decora stuff
                "**/*.stg",    // any glue files for decora must be excluded
                "**/*.java");  // Builder java files are in build/classes and should be excluded

        // Filter out platform specific Java sources (glass) when compiling for other targets
        exclude(targetProperties.jfxrtJarExcludes)

        dependsOn(subprojects.collect { project -> project.getTasksByName("assemble", true)});
    }
    jfxrt.dependsOn(jfxrtTask)

    // The 'sdk' task will build the rest of the SDK, and depends on the 'jfxrtTask' task. After
    // executing this task the sdk bundle for the current COMPILE_TARGETS will be fully created.
    def sdkTask = task("sdk$t.capital") {
        group = "Basic"
        description = "Creates the SDK for $t.name"
        doLast {
            // TODO instead of using copy everywhere, I probably want to use "sync" instead?
            // Copy all of the .dll / .so / .dylib native libraries into build/sdk/rt/lib/
            copy {
                from("modules/graphics/build/libs/jsl-decora/${t.name}/${library(targetProperties.decora.lib)}",
                     "modules/graphics/build/libs/prism/${t.name}/${library(targetProperties.prism.lib)}",
                     "modules/graphics/build/libs/prismSW/${t.name}/${library(targetProperties.prismSW.lib)}",
                     "modules/graphics/build/libs/iio/${t.name}/${library(targetProperties.iio.lib)}");
                def es2Variants = targetProperties.prismES2.containsKey("variants") ? targetProperties.prismES2.variants : [""];
                es2Variants.each { variant ->
                    def variantProperties = variant == "" ? targetProperties.prismES2 : targetProperties.prismES2.get(variant)
                    from ("modules/graphics/build/libs/prismES2/${t.name}/${library(variantProperties.lib)}");
                }
                def glassVariants = targetProperties.glass.containsKey("variants") ? targetProperties.glass.variants : [""];
                glassVariants.each { variant ->
                    def variantProperties = variant == "" ? targetProperties.glass : targetProperties.glass.get(variant)
                    from ("modules/graphics/build/libs/glass/${t.name}/${library(variantProperties.lib)}");
                }
                if (IS_WINDOWS) {
                    from ("modules/graphics/build/libs/prismD3D/${t.name}/${library(targetProperties.prismD3D.lib)}");
                    rename("lib(.*).dll", "\$1.dll");
                }
                def libDest = targetProperties.libDest
                into ("build/${t.name}-sdk/rt/$libDest")
            }

            // Create the javafx.properties file
            final File javafxProperties = file("build/${t.name}-sdk/rt/lib/javafx.properties")
            javafxProperties.delete()
            javafxProperties << "javafx.runtime.version=$RAW_VERSION";
            // Include any properties that have been defined (most likely in
            // one of the various platform gradle files)
            if (targetProperties.containsKey("javafxProperties")) {
                javafxProperties << "\n"
                javafxProperties << targetProperties.javafxProperties
            }

            // Embedded builds define this file as well
            if (targetProperties.containsKey("javafxPlatformProperties")) {
                final File javafxPlatformProperties = file("build/${t.name}-sdk/rt/lib/javafx.platform.properties")
                javafxPlatformProperties.delete()
                javafxPlatformProperties << targetProperties.javafxPlatformProperties
            }

            // Copy over the javadocs that were generated. This is done rather than just generating
            // the docs into the "right place" because for a cross-compile you only need one set of
            // docs but need to have a copy in each created sdk
            copy {
                from "build/javadoc"
                into "build/${t.name}-sdk/docs/api"
            }

            // Copy over the fxpackager and rename as ant-javafx.jar
            copy {
                from "modules/fxpackager/build/libs"
                into "build/${t.name}-sdk/lib"
            }

            // Copy over the FXPackager man files
            copy {
                from "modules/fxpackager/build/man"
                into "build/${t.name}-sdk/man"
            }

            // Copy over the javafxpackager executable
            if (t.name == "win" || t.name == "linux" || t.name == "mac") {
                copy {
                    from "modules/fxpackager/build/javafxpackager"
                    into "build/${t.name}-sdk/bin"
                }
            }
        }
        dependsOn(jfxrtTask)
        dependsOn(javadoc)
    }
    sdk.dependsOn(sdkTask)
}

    //task integrationCheck {
    //    group = "Basic"
    //    description = "Performs all the tasks necessary to ensure that the current build is ready for integration."
    //    dependsOn sdk
    //    dependsOn subprojects.collect { project -> project.getTasksByName("check", true)}
    //}

/******************************************************************************
 *                                                                            *
 *                 Generation of NetBeans Project Files                       *
 *                                                                            *
 *  This next section is responsible for setting up the work for generation   *
 *  of NetBeans project files. The developer needs to invoke                  *
 *                                                                            *
 *      gradle netbeans                                                       *
 *                                                                            *
 *  and all necessary project files will be generated. Note that unlike       *
 *  Eclipse and IDEA, NetBeans natively understands Gradle build files so     *
 *  the project files generated for NetBeans is limited to just shared        *
 *  configuration files. Make sure you have the latest stable release of      *
 *                                                                            *
 *  http://plugins.netbeans.org/plugin/44510/gradle-support                   *
 *                                                                            *
 *  for your version of the IDE.                                              *
 *                                                                            *
 *****************************************************************************/

/*
  <built-in-tasks>
    <task>
      <display-name>build</display-name>
      <non-blocking>yes</non-blocking>
      <task-names>
        <name must-exist="no">build</name>
      </task-names>
      <task-args>
        <arg>-PBUILD_CLOSED=true</arg>
      </task-args>
      <task-jvm-args/>
    </task>
  </built-in-tasks>
  <common-tasks>
    <task>
      <display-name>sdk</display-name>
      <non-blocking>no</non-blocking>
      <task-names>
        <name must-exist="yes">tasks</name>
      </task-names>
      <task-args>
        <arg>-PBUILD_CLOSED=true</arg>
      </task-args>
      <task-jvm-args/>
    </task>
  </common-tasks>

 */

task netBeans() {
    description = "Creates the NetBeans project files for JavaFX"
    group = "IDE"

    doLast {
        File nbGradleProperties = file(".nb-gradle-properties")
        if (nbGradleProperties.exists()) nbGradleProperties.delete()
        nbGradleProperties.createNewFile()
        nbGradleProperties <<
"""<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<gradle-project-properties>
  <!--DO NOT EDIT THIS FILE! - Used by the Gradle plugin of NetBeans.-->
  <source-encoding>UTF-8</source-encoding>
  <target-platform-name>j2se</target-platform-name>
  <target-platform>1.7</target-platform>
  <source-level>1.7</source-level>
  <script-platform>
    <spec-name>j2se</spec-name>
    <spec-version>1.7</spec-version>
  </script-platform>
  <gradle-home>?VER=1.4</gradle-home>
  <license-header>
    <name>OpenJFX</name>
    <template>.nb-openjfx-template-license</template>
    <property name="organization">OpenJFX</property>
  </license-header>
</gradle-project-properties>"""

        File nbGradleTemplate = file(".nb-openjfx-template-license")
        if (nbGradleTemplate.exists()) nbGradleTemplate.delete()
        nbGradleTemplate.createNewFile()
        nbGradleTemplate <<
"""My License will reign supreme!"""
    }
}

task cleanNetBeans() {
    description = "Deletes generated NetBeans files"
    group = "IDE"
    doLast {
        file(".nb-gradle-properties").delete()
    }
}

/******************************************************************************
 *                                                                            *
 *                   Generation of IDEA Project Files                         *
 *                                                                            *
 *  This next section is responsible for setting up the work for generation   *
 *  of IntelliJ IDEA project files. The developer needs to invoke             *
 *                                                                            *
 *      gradle idea                                                           *
 *                                                                            *
 *  and all necessary project files will be generated.                        *
 *                                                                            *
 *****************************************************************************/

allprojects {
    apply plugin: 'idea'
    if (project == rootProject || project.name == "media" || project.name == "web") return

    // Also need to un-exclude the build-dir since we put stuff in it, and then re-exclude other stuff
    if (rootProject.BINARY_STUB != null) {
        configurations {
          binaryStub
          binaryStub.extendsFrom(compile)
        }

        dependencies {
          binaryStub rootProject.BINARY_STUB
        }
    }

    idea {
        module {
            inheritOutputDirs = true;
            excludeDirs -= buildDir;
            excludeDirs += [
                    new File(buildDir, "classes"),
                    new File(buildDir, "dependency-cache"),
                    new File(buildDir, "libs"),
                    new File(buildDir, "resources"),
                    new File(buildDir, "tmp")];

            if (rootProject.BINARY_STUB != null) {
                scopes.COMPILE.plus += configurations.binaryStub

                // Some hackery. I have to make sure the binary stub is the VERY LAST entry
                // in the class path or things will be somewhat unpredictable
                iml {
                    withXml { xmlProvider ->
                        def root = xmlProvider.asNode();
                        // Find the <root url=".../jfxrt.jar!/" />. Walk up 3 steps to <orderEntry>
                        def orderEntry = root.depthFirst().find { it.name() == "root" }.parent().parent().parent(); // && it.@url.contains(rootProject.files(rootProject.BINARY_STUB))
                        if (orderEntry.name() != "orderEntry") throw new Exception("Couldn't find the order entry");
                        // Move that <orderEntry> to be the last child of its parent.
                        def parent = orderEntry.parent();
                        parent.remove(orderEntry);
                        parent.append(orderEntry);
                    }
                }
            }
        }
    }
}

/**
 * I want to keep all the project files and out directory and such all hidden from the filesystem
 * for the most part, so we don't have a lot of .iml files strewn about. So what we will do is
 * to redirect all of the .iml files into a .idea directory on the root project level, and stash the
 * out/ for all the projects there as well as the project module definition files.
 */
idea {
    project {
        jdkName = "JavaFX 1.7"
        languageLevel = "1.7"
        wildcards += ["?*.css", "?*.bss", "?*.glsl", "?*.frag", "?*.ttf", "?*.txt", "?*.fxml"]
        ipr {
            withXml { xmlProvider ->
                def root = xmlProvider.asNode();
                def doc = root.depthFirst();

                // Set the copyright
                def copyrightManager = doc.find { it.name() == "component" && it.@name == "CopyrightManager" };
                copyrightManager.@default="OpenJFX";
                def module2copyright = copyrightManager.get("module2copyright");
                module2copyright.replaceNode {
                    copyright {
                        option(name: "notice", value:
"""Copyright (c) 2013, 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.""");
                        option(name: "keyword", value: "Copyright");
                        option(name: "allowReplaceKeyword", value: "");
                        option(name: "myName", value: "OpenJFX");
                        option(name: "myLocal", value: "true");
                    }
                }

                // Remove some setting and change the project output directory
                def projectRootManager = doc.find { it.name() == "component" && it.@name == "ProjectRootManager" };
                projectRootManager.attributes.remove("assert-jdk-15");
                projectRootManager.children()[0].@url = "file://\$PROJECT_DIR\$/.idea-out";

                // Add in some custom dictionary stuff so that people get fewer green-squigglies
                // in their code. Nothing like a green squiggly to ruin my day.
                copyrightManager.plus {
                    component(name: "ProjectDictionaryState") {
                        dictionary(name: "javafx") {
                            words {
                                w (text: "backoff")
                                w (text: "blhr")
                                w (text: "blvr")
                                w (text: "brhr")
                                w (text: "brvr")
                                w (text: "gradle")
                                w (text: "gzip")
                                w (text: "halignment")
                                w (text: "interpolator")
                                w (text: "jfxrt")
                                w (text: "multithreaded")
                                w (text: "redoable")
                                w (text: "resized")
                                w (text: "squigglies")
                                w (text: "subclassing")
                                w (text: "timeline")
                                w (text: "timeline's")
                                w (text: "tlhr")
                                w (text: "tlvr")
                                w (text: "trhr")
                                w (text: "trvr")
                                w (text: "unmanaged")
                                w (text: "unselect")
                                w (text: "urlencoded")
                                w (text: "valignment")
                            }
                        }
                    }
                }

                // Setup some common project settings like tabs vs. spaces
                copyrightManager.plus {
                    component(name: "CodeStyleSettingsManager") {
                        option(name: "PER_PROJECT_SETTINGS") {
                            value {
                                ADDITIONAL_INDENT_OPTIONS(fileType: "java") {
                                    option (name: "IDENT_SIZE", value: "4")
                                    option (name: "CONTINUATION_INDENT_SIZE", value: "8")
                                    option (name: "TAB_SIZE", value: "4")
                                    option (name: "USE_TAB_CHARACTER", value: "false")
                                    option (name: "SMART_TABS", value: "false")
                                    option (name: "LABEL_INDENT_SIZE", value: "0")
                                    option (name: "LABEL_INDENT_ABSOLUTE", value: "false")
                                    option (name: "USE_RELATIVE_INDENTS", value: "false")
                                }
                                ADDITIONAL_INDENT_OPTIONS(fileType: "xml") {
                                    option (name: "IDENT_SIZE", value: "4")
                                    option (name: "CONTINUATION_INDENT_SIZE", value: "8")
                                    option (name: "TAB_SIZE", value: "4")
                                    option (name: "USE_TAB_CHARACTER", value: "false")
                                    option (name: "SMART_TABS", value: "false")
                                    option (name: "LABEL_INDENT_SIZE", value: "0")
                                    option (name: "LABEL_INDENT_ABSOLUTE", value: "false")
                                    option (name: "USE_RELATIVE_INDENTS", value: "false")
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

//project("buildSrc") {
//    // Add antlr to the sources for IDEA
//    idea {
//        module {
//            sourceDirs += file("$buildDir/generated-src/antlr")
//        }
//    }
//}

project(":base") {
    // Add in the version-info generated sources for IDEA projects
    idea {
        module {
            sourceDirs += file("$buildDir/generated-src/version-info")
        }
    }
}

// TODO need to do the iOS stuff (ios package of prism-es2-native, ios package of javafx-iio-native)

project(":graphics") {
    idea {
        module {
            sourceDirs += [file("$buildDir/generated-src/headers"),
                    file("src/main/native-decora"),
                    file("src/main/native-glass/gtk"),
                    file("src/main/native-glass/ios"),
                    file("src/main/native-glass/lens"),
                    file("src/main/native-glass/mac"),
                    file("src/main/native-glass/win"),
                    file("src/main/native-prism"),
                    file("src/main/native-prism-sw")]
            // TODO generated JSL directories need to be added
//            sourceDirs += file("$buildDir/generated-src/jsl")
//            sourceDirs += file("$buildDir/generated-src/jsl")
            excludeDirs += new File(buildDir, "native");
            testSourceDirs += [file("src/stub/java"), file("src/stub/resources")];
        }
    }
}

project(":controls") {
    idea {
        module {
            sourceDirs += file("src/main/dt");
        }
    }
}

cleanIdeaWorkspace.group = "IDE"
cleanIdeaWorkspace.description = "Deletes the javafx.ipw file"

/******************************************************************************
 *                                                                            *
 *                              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
}