// Global settings
project.ext {
    // Source JRE used for compilation, etc
    if (!project.hasProperty('jre')) {
        jre = System.getProperty('java.home')
    }
    os = Os.current()
    arch = Arch.current()

    if (!project.hasProperty('targetJre')) {
        targetJre = jre
    }
}

def targetJreFile = file(targetJre)
def jreFile = file(jre)

// HotSpot itself
project('hotspot') {
    task clean(type: InvokeMake) {
        onlyIf { file('hotspot').exists() }
        args 'clean'
    }

    task prepareJvm {
        onlyIf { jreFile.canonicalPath != targetJreFile.canonicalPath }
        doLast {
            // FIXME: We probably should check if source path is different and delete old JRE in that case
            ant.copy(todir: targetJreFile, overwrite: true) {
                fileset(dir: jreFile)
            }
            ant.chmod(file: new File(targetJreFile, 'bin/java'), perm: '0755')
        }
    }

    task init(description: 'Initialize HotSpot repository') << {
        file('hotspot').mkdir()
        exec {
            executable 'hg'
            args 'init'
            ignoreExitValue = true
        }
    }

    task pull(description: 'Pull OpenJDK HotSpot changes') {
        doLast {
            def hotspotRepository = hotspotTag.contains('jdk7') ?
                'http://hg.openjdk.java.net/jdk7u/jdk7u/hotspot' :
                'http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot'
            exec {
                executable 'hg'
                args 'pull', hotspotRepository
            }
        }
    }

    task patch(description: 'Patch HotSpot sources', dependsOn: pull) {

        doLast {
            exec {
                executable 'hg'
                args 'qpop', '-a'
            }
            exec {
                executable 'hg'
                args 'update', '-C', '-r', hotspotTag
            }
            new ByteArrayOutputStream().withStream { os ->
                exec {
                    workingDir 'hotspot'
                    executable 'hg'
                    args 'status'
                    standardOutput os
                }
                // Purge unversioned files
                def str = os.toString()
                def matcher = str =~ /(?m)^\?\s+(.*)$/
                matcher.each {
                    ant.delete(file: new File(file('hotspot'), it[1]))
                }
            }
            def guards = [flavor, hotspotTag.contains('jdk7') ? 'jdk7' : 'jdk8',
                          hotspotTag, flavor + '-' + hotspotTag]
            exec {
                executable 'hg'
                args 'qselect'
                args guards
            }
            exec {
                executable 'hg'
                args 'qpush', '-a'
            }
        }
    }

    def arguments = ['product':   ['ENABLE_FULL_DEBUG_SYMBOLS=0'],
                     'fastdebug': ['ENABLE_FULL_DEBUG_SYMBOLS=1', 'STRIP_POLICY=no_strip', 'ZIP_DEBUGINFO_FILES=0']]

    ['product', 'fastdebug'].each { k ->
        // Compile given kind of DCEVM
        def compile = task("compile${k.capitalize()}", type: InvokeMake) {
            args arguments[k]
            args k
            doLast {
                ant.copy(todir: "build/${k}", overwrite: true) {
                    fileset(dir: "build/${os.buildPath}/${os.buildPath}_${arch.buildArch}_${compiler}/${k}",
                            includes: 'libjvm.so,libjsig.so,jvm.dll,jsig.dll,libjvm.dylib,libjsig.dylib')
                }
            }
        }
        compile.mustRunAfter(patch)

        // Install given kind of DCEVM into destination JRE
        task("install${k.capitalize()}", dependsOn: [prepareJvm, compile]) << {
            def installPath = new File(new File(targetJreFile, arch == Arch.X86 ? os.installPath32 : os.installPath64), jvmName)

            logger.info("Installing DCEVM runtime into JRE with JVM name '${jvmName}' and kind '${k}' at ${installPath}")
            ant.copy(todir: installPath, overwrite: true) {
                fileset(dir: "build/${os.buildPath}/${os.buildPath}_${arch.buildArch}_${compiler}/${k}",
                        includes: 'libjvm.so,libjsig.so,jvm.dll,jsig.dll,libjvm.dylib,libjsig.dylib')
            }
        }
    }
}

// Java projects for testing DCEVM
def setup(prjs, closure) {
    prjs.each { prj ->
        project(prj, closure)
    }
}

setup(['agent', 'dcevm'], {
    apply plugin: 'java'
    apply plugin: 'idea'

    repositories {
        mavenCentral()
    }
})

project('agent') {
    jar {
        manifest {
            from "src/main/java/META-INF/MANIFEST.MF"
        }
    }
}

project('native') {
    apply plugin: 'base'
    task compile(type: Exec) {
        doFirst {
            buildDir.mkdirs()
        }
        if (os != Os.WINDOWS) {
            commandLine 'gcc'
            args "-I${jre}/../include"
            args '-shared'
            args 'natives.c'
            if (os == Os.UNIX) {
                args "-I${jre}/../include/linux"
                args '-o'
                args 'build/libnatives.so'
                args (arch == Arch.X86 ? '-m32' : '-m64')
            } else if (os == Os.MAC) {
                args "-I${jre}/../include/darwin"
                args '-o'
                args 'build/libnatives.dylib'
            }
        } else {
            commandLine 'cmd', '/c', 'compile.cmd'
            environment ARCH: arch == Arch.X86 ? 'x86' : 'x64'
            environment JAVA_HOME: jre
        }
    }
}

project('dcevm') {
    dependencies {
        compile project(':agent')
        compile group: 'asm', name: 'asm-all', version: '3.3.+'
        compile files(jre + '/../lib/tools.jar')

        testCompile group: 'junit', name: 'junit', version: '4.11'
    }

    def m = System.getProperty('java.version') =~ /^1\.([0-9]+)\.*/
    def major = m[0][1].toInteger()
    if (major >= 7) {
        sourceSets.test.java.srcDirs += 'src/test/java7'
    }
    if (major >= 8) {
        sourceSets.test.java.srcDirs += 'src/test/java8'
    }

    test {
        executable new File(targetJreFile, 'bin/java')

        systemProperty 'dcevm.test.light', (flavor == 'light')

        if (kind == 'fastdebug') {
            jvmArgs '-XX:LogFile=build/hotspot.log'
        }
        jvmArgs "-XXaltjvm=${jvmName}"
        jvmArgs '-javaagent:../agent/build/libs/agent.jar'
        if (arch == Arch.X86_64) {
            jvmArgs(project.oops == "compressed" ? '-XX:+UseCompressedOops' : "-XX:-UseCompressedOops")
        }
        jvmArgs "-XX:TraceRedefineClasses=${traceRedefinition}"
        jvmArgs "-Djava.library.path=../native/build"

        ignoreFailures = true
        outputs.upToDateWhen { false }
        useJUnit {
            excludeCategories ('com.github.dcevm.test.category.' + (flavor == 'light' ? 'Full' : 'Light'))
        }
    }

    test.dependsOn project(':native').tasks['compile']
    test.dependsOn project(':hotspot').tasks[kind == 'fastdebug' ? 'installFastdebug' : 'installProduct']
}


enum Arch {
    X86(["i386", "i486", "i586", "x86"], 'i486', 32),
    X86_64(["x86_64", "x64", "amd64"], 'amd64', 64)

    final List<String> names
    final String buildArch
    final int bits

    Arch(List<String> names, String buildArch, int bits) {
        this.names = names
        this.buildArch = buildArch
        this.bits = bits
    }

    static Arch find(String token) {
        Arch res = values().find({ v -> v.names.contains(token) })
        return res
    }

    static Arch current() {
        return find(System.getProperty("os.arch"))
    }
}

enum Os {
    MAC('bsd', 'lib', 'lib'),
    WINDOWS('windows', 'bin', 'bin'),
    UNIX('linux', 'lib/i386', 'lib/amd64')

    final String buildPath;
    final String installPath32;
    final String installPath64;

    Os(String buildPath, String installPath32, String installPath64) {
        this.buildPath = buildPath
        this.installPath32 = installPath32
        this.installPath64 = installPath64
    }

    static Os current() {
        return values().find { os -> org.apache.tools.ant.taskdefs.condition.Os.isFamily(os.name().toLowerCase()) }
    }
}

// Helper task to run make targets against hotspot
class InvokeMake extends org.gradle.api.tasks.Exec {
    InvokeMake() {
        def root = project.rootProject
        logging.captureStandardOutput LogLevel.INFO
        if (root.os != Os.WINDOWS) {
            commandLine 'make', '-C', 'make'
        } else {
            // Using launcher script
            commandLine 'cmd', '/c', '..\\build.cmd'
            environment ARCH: root.arch == Arch.X86 ? 'x86' : 'x64'
        }
        args 'OPENJDK=true'
        args "HOTSPOT_BUILD_VERSION=dcevm${root.flavor}-${root.buildNumber}"
        args "ARCH_DATA_MODEL=${root.arch.bits}"
        args "ALT_BOOTDIR=${root.jre.replace('\\', '/')}/.."
        // Replacing backslashes is essential for Windows!
        args 'COMPILER_WARNINGS_FATAL=false' // Clang is very serious about warnings
        args 'HOTSPOT_BUILD_JOBS=4'
    }
}
href='#n93'>93</a>
<a id='n94' href='#n94'>94</a>
<a id='n95' href='#n95'>95</a>
<a id='n96' href='#n96'>96</a>
<a id='n97' href='#n97'>97</a>
<a id='n98' href='#n98'>98</a>
<a id='n99' href='#n99'>99</a>
<a id='n100' href='#n100'>100</a>
<a id='n101' href='#n101'>101</a>
<a id='n102' href='#n102'>102</a>
<a id='n103' href='#n103'>103</a>
<a id='n104' href='#n104'>104</a>
<a id='n105' href='#n105'>105</a>
<a id='n106' href='#n106'>106</a>
<a id='n107' href='#n107'>107</a>
<a id='n108' href='#n108'>108</a>
<a id='n109' href='#n109'>109</a>
<a id='n110' href='#n110'>110</a>
<a id='n111' href='#n111'>111</a>
<a id='n112' href='#n112'>112</a>
<a id='n113' href='#n113'>113</a>
<a id='n114' href='#n114'>114</a>
<a id='n115' href='#n115'>115</a>
<a id='n116' href='#n116'>116</a>
<a id='n117' href='#n117'>117</a>
<a id='n118' href='#n118'>118</a>
<a id='n119' href='#n119'>119</a>
<a id='n120' href='#n120'>120</a>
<a id='n121' href='#n121'>121</a>
<a id='n122' href='#n122'>122</a>
<a id='n123' href='#n123'>123</a>
<a id='n124' href='#n124'>124</a>
<a id='n125' href='#n125'>125</a>
<a id='n126' href='#n126'>126</a>
<a id='n127' href='#n127'>127</a>
<a id='n128' href='#n128'>128</a>
<a id='n129' href='#n129'>129</a>
<a id='n130' href='#n130'>130</a>
<a id='n131' href='#n131'>131</a>
<a id='n132' href='#n132'>132</a>
<a id='n133' href='#n133'>133</a>
<a id='n134' href='#n134'>134</a>
<a id='n135' href='#n135'>135</a>
<a id='n136' href='#n136'>136</a>
<a id='n137' href='#n137'>137</a>
<a id='n138' href='#n138'>138</a>
<a id='n139' href='#n139'>139</a>
<a id='n140' href='#n140'>140</a>
<a id='n141' href='#n141'>141</a>
<a id='n142' href='#n142'>142</a>
<a id='n143' href='#n143'>143</a>
<a id='n144' href='#n144'>144</a>
<a id='n145' href='#n145'>145</a>
<a id='n146' href='#n146'>146</a>
<a id='n147' href='#n147'>147</a>
<a id='n148' href='#n148'>148</a>
<a id='n149' href='#n149'>149</a>
<a id='n150' href='#n150'>150</a>
<a id='n151' href='#n151'>151</a>
<a id='n152' href='#n152'>152</a>
<a id='n153' href='#n153'>153</a>
<a id='n154' href='#n154'>154</a>
<a id='n155' href='#n155'>155</a>
<a id='n156' href='#n156'>156</a>
<a id='n157' href='#n157'>157</a>
<a id='n158' href='#n158'>158</a>
<a id='n159' href='#n159'>159</a>
<a id='n160' href='#n160'>160</a>
<a id='n161' href='#n161'>161</a>
<a id='n162' href='#n162'>162</a>
<a id='n163' href='#n163'>163</a>
<a id='n164' href='#n164'>164</a>
<a id='n165' href='#n165'>165</a>
<a id='n166' href='#n166'>166</a>
<a id='n167' href='#n167'>167</a>
<a id='n168' href='#n168'>168</a>
<a id='n169' href='#n169'>169</a>
<a id='n170' href='#n170'>170</a>
<a id='n171' href='#n171'>171</a>
<a id='n172' href='#n172'>172</a>
<a id='n173' href='#n173'>173</a>
<a id='n174' href='#n174'>174</a>
<a id='n175' href='#n175'>175</a>
<a id='n176' href='#n176'>176</a>
<a id='n177' href='#n177'>177</a>
<a id='n178' href='#n178'>178</a>
<a id='n179' href='#n179'>179</a>
<a id='n180' href='#n180'>180</a>
<a id='n181' href='#n181'>181</a>
<a id='n182' href='#n182'>182</a>
<a id='n183' href='#n183'>183</a>
<a id='n184' href='#n184'>184</a>
<a id='n185' href='#n185'>185</a>
<a id='n186' href='#n186'>186</a>
<a id='n187' href='#n187'>187</a>
<a id='n188' href='#n188'>188</a>
<a id='n189' href='#n189'>189</a>
<a id='n190' href='#n190'>190</a>
<a id='n191' href='#n191'>191</a>
<a id='n192' href='#n192'>192</a>
<a id='n193' href='#n193'>193</a>
<a id='n194' href='#n194'>194</a>
<a id='n195' href='#n195'>195</a>
<a id='n196' href='#n196'>196</a>
<a id='n197' href='#n197'>197</a>
<a id='n198' href='#n198'>198</a>
<a id='n199' href='#n199'>199</a>
<a id='n200' href='#n200'>200</a>
<a id='n201' href='#n201'>201</a>
<a id='n202' href='#n202'>202</a>
</pre></td>
<td class='lines'><pre><code>