Skip to content

Instantly share code, notes, and snippets.

@killjoy1221
Created May 24, 2015 22:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save killjoy1221/873638de33e0c378043e to your computer and use it in GitHub Desktop.
Save killjoy1221/873638de33e0c378043e to your computer and use it in GitHub Desktop.
Groovy port of ForgeGradle's BasePlugin
package net.minecraftforge.groodle.common
import static net.minecraftforge.gradle.common.Constants.*
import java.lang.reflect.ParameterizedType
import net.minecraftforge.gradle.common.BaseExtension
import net.minecraftforge.gradle.common.Constants
import net.minecraftforge.gradle.tasks.CrowdinDownload
import net.minecraftforge.gradle.tasks.Download
import net.minecraftforge.gradle.tasks.DownloadAssetsTask
import net.minecraftforge.gradle.tasks.EtagDownloadTask
import net.minecraftforge.gradle.tasks.ExtractConfigTask
import net.minecraftforge.gradle.tasks.GenSrgs
import net.minecraftforge.gradle.tasks.JenkinsChangelog
import net.minecraftforge.gradle.tasks.MergeJars
import net.minecraftforge.gradle.tasks.ObtainFernFlowerTask
import net.minecraftforge.gradle.tasks.SignJar
import net.minecraftforge.gradle.util.FileLogListenner
import net.minecraftforge.gradle.util.GradleConfigurationException
import net.minecraftforge.gradle.util.delayed.DelayedFile
import net.minecraftforge.gradle.util.delayed.DelayedFileTree
import net.minecraftforge.gradle.util.delayed.DelayedString
import net.minecraftforge.gradle.util.delayed.TokenReplacer
import net.minecraftforge.gradle.util.json.JsonFactory
import net.minecraftforge.gradle.util.json.version.Library
import net.minecraftforge.gradle.util.json.version.Version
import org.gradle.api.DefaultTask
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.repositories.FlatDirectoryArtifactRepository
import org.gradle.api.artifacts.repositories.MavenArtifactRepository
import org.gradle.api.tasks.Delete
import org.gradle.testfixtures.ProjectBuilder
import com.google.common.base.Throwables
import com.google.common.cache.CacheBuilder
import com.google.common.cache.LoadingCache
import com.google.gson.reflect.TypeToken
abstract class BasePlugin<K extends BaseExtension> implements Plugin<Project> {
public Project project
public BasePlugin otherPlugin
@Override
final void apply(Project arg) {
project = arg
// check for gradle version
def split = project.gradle.gradleVersion.split(/\./)
def major = split[0].toInteger()
def minor = split[1].toInteger()
if (major <= 1 || (major == 2 && minor < 3))
throw new RuntimeException("ForgeGradle 2.0 requires Gradle 2.3 or above.")
// search for overlays...
project.plugins.findAll {it.is BasePlugin }.each {
if (!canOverlayPlugin())
throw new GradleConfigurationException("Seems you are trying to apply 2 ForgeGradle plugins that are not designed to overlay... Fix your buildscripts.");
project.logger.info 'Applying Overlay'
// found another BasePlugin that's already applied.
// do only overlay stuff and return
otherPlugin = it.with {
replacerCache = this.replacerCache
stringCache = this.stringCache
fileCache = this.fileCache
}
applyOverlayPlugin()
return
}
// logging
def projectCacheDir = project.gradle.startParameter.projectCacheDir
if (projectCacheDir == null)
projectCacheDir = new File(project.projectDir, '.gradle')
def listener = new FileLogListenner(new File(projectCacheDir, 'gradle.log'));
project.with {
logging.with {
standardOutputCaptureLevel += listener
standardErrorCaptureLevel += listener
}
gradle.addBuildListener listener
}
if (project.buildDir.absolutePath.contains('!'))
{
project.logger.error 'Build path has !, This will screw over a lot of java things as ! is used to denote archive paths, REMOVE IT if you want to continue'
throw new RuntimeException('Build path contains !')
}
// set the obvious replacements
TokenReplacer.replaceMap << [
REPLACE_CACHE_DIR:cacheFile('').absolutePath,
REPLACE_BUILD_DIR:project.buildDir.absolutePath
]
// extension objects
project.extensions.create EXT_NAME_MC, ((ParameterizedType)getClass().genericSuperclass).actualTypeArguments[0], this
// add buildscript usable tasks
project.extensions.extraProperties.set (
"SignJar":SignJar,
"Download":Download,
"EtagDownload":EtagDownloadTask,
"CrowdinDownload":CrowdinDownload,
"JenkinsChangelog":JenkinsChangelog)
// repos
// TODO: move to userdev
project.allprojects{ Project proj ->
addMavenRepo proj, 'forge', URL_FORGE_MAVEN
proj.getRepositories().mavenCentral()
addMavenRepo proj, 'minecraft', URL_LIBRARY
}
// do Mcp Snapshot Stuff
setVersionInfoJson()
project.configurations{
CONFIG_MCP_DATA
CONFIG_MAPPINGS
// set other configs
CONFIG_MC_DEPS
CONFIG_NATIVES
}
project.afterEvaluate { Project project ->
if (project.state.failure != null) return
this.afterEvaluate()
}
// some default tasks
makeCommonTasks()
// at last, apply the chind plugins
applyPlugin()
}
abstract void applyPlugin()
protected abstract void applyOverlayPlugin()
abstract boolean canOverlayPlugin()
private static boolean displayBanner = true
private void setVersionInfoJson() {
def jsonCache = cacheFile('McpMappings.json')
def etagFile = new File("${jsonCache.absolutePath}.etag")
extension.mcpJson = JsonFactory.GSON.fromJson(getWithEtag(URL_MCP_JSON, jsonCache, etagFile), new TypeToken<Map<String, Map<String, int[]>>>(){}.type)
}
protected void afterEvaluate() {
def mcVersion = delayedString(REPLACE_MC_VERSION)()
project.getDependencies.with {
add CONFIG_MAPPINGS, [
group:'de.oceanlabs.mcp',
name: delayedString("mcp_$REPLACE_MCP_CHANNEL")(),
version:delayedString("$REPLACE_MCP_VERSION-$mcVersion")(),
ext:'zip'
]
add CONFIG_MCP_DATA, [
group:'de.oceanlabs.mcp',
name:'mcp',
version:mcVersion,
classifier:'srg',
ext:'zip'
]
}
if (!displayBanner) return
project.logger.lifecycle """
****************************
Powered By MCP:
http://mcp.ocean-labs.de/
Searge, ProfMobius, Fesh0r,
R4wk, ZeuX, IngisKahn, bspkrs
MCP Data Version: $extension.mcpVersion
****************************
"""
displayBanner = false
}
private void makeCommonTasks() {
def dlClient = makeTask(TASK_DL_CLIENT, Download).with {
output = delayedFile JAR_CLIENT_FRESH
url = delayedString URL_MC_CLIENT
}
def dlServer = makeTask(TASK_DL_SERVER, Download).with {
output = delayedFile JAR_SERVER_FRESH
url = delayedString URL_MC_SERVER
}
makeTask(TASK_MERGE_JARS, MergeJars).with {
client = delayedFile JAR_CLIENT_FRESH
server = delayedFile JAR_SERVER_FRESH
outJar = delayedFile JAR_MERGED
dontProcess << ['org/bouncycastle', 'org/apache', 'com/google', 'com/mojang/authlib', 'com/mojang/util', 'gnu/trove', 'io/netty', 'javax/annotation', 'argo']
dependsOn dlClient, dlServer
}
def getVersionJson = makeTask(TASK_DL_VERSION_JSON, EtagDownloadTask).with {
url = delayedString URL_MC_JSON
file = delayedFile JSON_VERSION
dieWithError = false
doLast {
try {
// normalize the line endings...
def json = delayedFile(JSON_VERSION)()
if (!json.exists) {
return
}
def lines = json.readLines "UTF-8"
def buf = new StringBuilder()
lines.each { buf.append(it).append('\n') }
json.write buf.toString(), "UTF-8"
// grab the AssetIndex if it isnt already there
if (!TokenReplacer.hasReplacement(REPLACE_ASSET_INDEX))
JsonFactory.loadVersion json, json.parentFile
} catch(Throwable t) {
Throwables.propagate t
}
}
}
makeTask(TASK_EXTRACT_NATIVES, ExtractConfigTask).with {
destinationDir = delayedFile DIR_NATIVES
config = CONFIG_NATIVES
excludes << ['META-INF/**', 'META-INF/**']
doesCache()
dependsOn getVersionJson
}
def getAssetsIndex = makeTask(TASK_DL_ASSET_INDEX, EtagDownloadTask).with {
url = delayedString ASSETS_INDEX_URL
file = delayedFile JSON_ASSET_INDEX
dieWithError = false
dependsOn getVersionJson
}
makeTask(TASK_DL_ASSETS, DownloadAssetsTask).with {
assetsDir = delayedFile DIR_ASSETS
assetsIndex = JSON_ASSET_INDEX
dependsOn getAssetsIndex
}
def extractMcpData = makeTask(TASK_EXTRACT_MCP, ExtractConfigTask).with {
destinationDir = delayedFile DIR_MCP_DATA
config = CONFIG_MCP_DATA
doesCache = true
}
def extractMcpMappings = makeTask(TASK_EXTRACT_MAPPINGS, ExtractConfigTask).with {
destinationDir = delayedFile(DIR_MCP_MAPPINGS)
config = CONFIG_MAPPINGS
doesCache = true
}
makeTask(TASK_GENERATE_SRGS, GenSrgs).with {
inSrg = delayedFile MCP_DATA_SRG
inExc = delayedFile MCP_DATA_EXC
methodsCsv = delayedFile CSV_METHOD
fieldsCsv = delayedFile CSV_FIELD
notchToSrg = delayedFile SRG_NOTCH_TO_SRG
notchToMcp = delayedFile SRG_NOTCH_TO_MCP
mcpToSrg = delayedFile SRG_SRG_TO_MCP
mcpToNotch = delayedFile SRG_MCP_TO_NOTCH
srgExc = delayedFile EXC_SRG
mcpExc = delayedFile EXC_MCP
dependsOn extractMcpData, extractMcpMappings
}
makeTask(TASK_DL_FERNFLOWER, ObtainFernFlowerTask).with {
mcpUrl = delayedString URL_FF
ffJar = delayedFile JAR_FERNFLOWER
}
makeTask(TASK_CLEAN_CACHE, Delete).with {
delete delayedFile(REPLACE_CACHE_DIR)
group = GROUP_FG
description = 'Clears the ForgeGradle cache. DON\'T RUN THIS unless you want a fresh start, or the dev tell you to.'
}
}
def final K getExtension() {
otherPlugin != null && canOverlayPlugin() ? overlayExtension : project.extensions[EXT_NAME_MC]
}
def abstract K getOverlayExtension()
def DefaultTask makeTask(String name) {
makeTask name, DefaultTask
}
def maybeMakeTask(String name) {
maybeMakeTask name, DefaultTask
}
def <T extends Task> T makeTask(String name, Class<T> type) {
makeTask(project, name, type)
}
def <T extends Task> T maybeMakeTask(String name, Class<T> type) {
maybeMakeTask(project, name, type)
}
def static <T extends Task> T maybeMakeTask(Project proj, String name, Class<T> type) {
proj.tasks.maybCreate name, type
}
def static <T extends Task> T makeTask(Project proj, String name, Class<T> type) {
proj.tasks.create name, type
}
def static Project buildProject(File buildFile, Project proj) {
def builder = ProjectBuilder.builder()
builder.with {
if (buildFile != null) {
projectDir = buildFile.parentFile
name = buildFile.parentFile.name
} else {
projectDir = new File('.')
}
if (proj != null)
parent = proj
}
def project = builder.build
if (buildFile != null)
project.apply from:buildFile.absolutePath
project
}
def applyExternalPlugin(String plug) {
project.apply plugin:plug
}
public MavenArtifactRepository addMavenRepo(Project proj, String name, String url) {
proj.getRepositories().maven { MavenArtifactRepository repo ->
repo.name = name
repo.url = url
}
}
public FlatDirectoryArtifactRepository addFlatRepo(Project proj, String name, String dirs){
proj.getRepositories().flatDir { FlatDirectoryArtifactRepository repo ->
repo.name = name
repo.dirs = dirs
}
}
protected getWithEtag(String strUrl, File cache, File etagFile) {
try {
if (project.gradle.startParameter.isOffline()){
return cache.text('UTF-8')
}
// dude, it's been less than 1 minutes since the last time..
if (cache.exists && cache.lastModified + 60000 >= System.currentTimeMillis)
return cache.text('UTF-8')
String etag
if (etagFile.exists()) {
etag = etagFile.text('UTF-8')
} else {
etagFile.parentFile.mkdirs()
etag = ''
}
def url = strUrl.toURL()
def con = url.openConnection() as HttpURLConnection
con.instanceFollowRedirects = true
con.requestProperties << ["User-Agent":USER_AGENT]
con.ifModifiedSince = cache.lastModified
if (etag != null && !etag.empty){
con.requestProperties << ["If-None-Match":etag]
}
con.connect()
def out = null as String
if (con.responseCode == 304) {
cache.createNewFile()
out = cache.text 'UTF-8'
} else if (con.responseCode == 200) {
def data = con.inputStream.text 'UTF-8'
cache.write data, 'UTF-8'
etag = con.headerFields['ETag']
if (etag != null && !etag.empty) {
etagFile.createNewFile()
} else {
etagFile.write etag, 'UTF-8'
}
out = data
} else {
project.logger.error "Etag download for $strUrl failed with code $con.responseCode"
}
con.disconnect()
out
} catch( Exception e) {
e.printStackTrace()
}
if (cache.exists) {
try {
cache.text "UTF-8"
}catch(IOException e) {
Throwables.propagate e
}
}
throw new RuntimeException("Unable to obtain url ($strUrl) with etag!")
}
protected Version parseAndStoreVersion(File file, File... inheritanceDirs) {
if (!file.exists) {null}
def version = null
try{
version = JsonFactory.loadVersion file, delayedFile(Constants.DIR_JSONS)()
}catch(Exception e) {
project.logger.error "$file could not be parsed"
Throwables.propagate(e)
}
if (version == null) {
try {
version = JsonFactory.loadVersion file, delayedFile(Constants.DIR_JSONS)()
}catch(Exception e) {
project.logger.error "$file could not be parsed"
}
}
// apply the dep info
def handler = project.getDependencies()
def adder = { libs, config ->
if (project.getConfigurations()[config].state == Configuration.State.UNRESOLVED){
libs
.findAll{Library lib -> lib.natives == null}
.each{Library lib -> handler << [config:lib.artifactName]}
} else project.logger.debug "RESOLVED $config"
}
// actual dependencies
adder version.libraries, CONFIG_MC_DEPS
// the natives
adder version.libraries, CONFIG_NATIVES
TokenReplacer.replaceMap << [REPLACE_ASSET_INDEX:version.assets]
version
}
protected Version parseAndStoreVersion(File file) {
parseAndStoreVersion file, delayedFile(DIR_JSONS)()
}
// DELAYED STUFF ONLY ------------------------------------------------------------------------
private LoadingCache<String, TokenReplacer> replacerCache = CacheBuilder.newBuilder().weakValues().build{new TokenReplacer(it)};
private LoadingCache<String, DelayedString> stringCache = CacheBuilder.newBuilder().weakValues().build{new DelayedString(replacerCache.getUnchecked(it))};
private LoadingCache<String, DelayedFile> fileCache = CacheBuilder.newBuilder().weakValues().build{new DelayedFile(project, replacerCache.getUnchecked(it))}
protected DelayedString delayedString(String path)
{
stringCache.getUnchecked path
}
protected DelayedFile delayedFile(String path)
{
fileCache.getUnchecked path
}
protected DelayedFileTree delayedTree(String path)
{
new DelayedFileTree(project, replacerCache.getUnchecked(path))
}
protected File cacheFile(String path)
{
new File(project.gradle.gradleUserHomeDir, "caches/minecraft/$path")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment