import javax.swing.* import java.awt.Dimension apply plugin: 'idea' apply plugin: 'eclipse' defaultTasks 'showGui' allprojects { project.ext.jQueryRepo = { repositories.ivy { url "http://code.jquery.com" layout "pattern", { artifact "[module]-[revision](.[classifier]).[ext]" } } } project.ext.jQueryUIRepo = { repositories.ivy { url "http://code.jquery.com/ui" layout "pattern", { artifact "[revision]/[module](.[classifier]).[ext]" } } } } project.convention.plugins.addoninfo = new AddOnInfo() project.convention.plugins.addoninfo.addExtension('providers', new ProviderExtension()) ext.addonInfoFile = file("$buildDir/tmp/addon/info.xml") task generateAddOnInfo { afterEvaluate { inputs.properties project.convention.plugins.addoninfo.properties outputs.file addonInfoFile } doLast { def text = new StringWriter() new groovy.xml.MarkupBuilder(text).extension(version: 1) { project.convention.plugins.addoninfo.addXml(delegate) } addonInfoFile.parentFile.mkdirs() addonInfoFile.text = text.toString() } } task addon(description: "Produces the .addon file", type: Zip) { dependsOn generateAddOnInfo inputs.files addonInfoFile, 'LICENSE.txt' destinationDir = file("$buildDir/lib") baseName = project.name.toLowerCase() // lower-case'ify the addon name to simplify usage in a web browser (which is case-sensitive) extension = 'addon' fileMode = 0644 from(addonInfoFile) // include addon-info.xml in binary from('LICENSE.txt') // include license in binary // have the build tell you where the archive was put! doLast { println "Addon created as ${addon.archivePath}" } } task build(dependsOn: addon) task clean(type: Delete) { delete buildDir } // not intended to be used directly. This is the default task for the script. This allows // the user to "double-click" on 'gradlew' and have the GUI launch. task showGui(description: 'Starts a graphical user interface for the build') << { org.gradle.gradleplugin.userinterface.swing.standalone.BlockingApplication.launchAndBlock(); } // Helper method to get the installation root directory of the server. This method will either // return whatever is in the 'serverRootDir' property (if it's set) ... File getServerDir() { if (!project.ext.has("serverRootDir")) project.ext.set("serverRootDir", null); if (project.ext.serverRootDir == null) { Properties props = new Properties() File optionsFile = new File('build.options') if (optionsFile.exists()) { optionsFile.withReader { props.load(it) } project.ext.serverRootDir = props.getProperty('serverRootDir') } if (project.ext.serverRootDir == null) { File dir = pickServerDir() if (dir == null) throw new Exception("""serverRootDir property (path to webctrl install directory) not set. Please set the serverRootDir property on the command line (-PserverRootDir=) or in the userHome/.gradle/gradle.properties file.""") project.ext.serverRootDir = dir.absolutePath.replaceAll('\\\\', '/') props.setProperty('serverRootDir', project.ext.serverRootDir) optionsFile.withWriter { props.store(it, null) } } } return new File(project.ext.serverRootDir) } project.ext.set("getServerDir", {getServerDir()}); // Helper methods to get the directory in which to deploy webapps (add-ons). ext.getDeployLoc = { new File(getServerDir(), 'addons/'+addon.baseName.toLowerCase()) } // task that deletes the .war (and exploded dir) from the webserver task cleanDeploy(description: 'Deletes the war from the webserver', overwrite: true, type:Delete) cleanDeploy.doFirst { delete deploy.destDir } // task that builds and deploys the .war to the webserver task deploy(description: 'Deploys the war to the webserver', type: Sync, dependsOn:generateAddOnInfo) { ext.destDir = getDeployLoc() doFirst { println "Deploying $addon.baseName to ${ext.destDir.canonicalPath}" } with addon into { ext.destDir } fileMode = 0644 } // if needed, specify a special repo for the keystore if (hasProperty('addon_sign_repo')) { repositories { maven { url addon_sign_repo } } } // Allows add-on to request signing. Only works for official add-ons. ext.signAddon = { // cannot sign jars with duplicate files, so make sure that doesn't happen addon { duplicatesStrategy = "exclude" } // add a task to perform the actual signing def requiredSigningProperties = ['addon_sign_keystore_gav', 'addon_sign_alias', 'addon_sign_password', 'addon_sign_keypass'] task signArchive(description: 'Signs the add-on archive', dependsOn: addon) << { if (!requiredSigningProperties.every { project.hasProperty(it) }) { println "All signing properties not defined, the built add-on will not be signed." println "The required prooperties are:" requiredSigningProperties.each { println " "+it } } else { def keyDep = dependencies.create(addon_sign_keystore_gav) def keystore = configurations.detachedConfiguration(keyDep).resolve().iterator().next() ant.signjar(jar: war.archivePath, alias:addon_sign_alias, storepass:addon_sign_password, keystore:keystore.path, keypass:addon_sign_keypass) } } // hook in the signArchive task to occur whenever a build is performed project.afterEvaluate { tasks['build'].dependsOn signArchive } // if signing, allow the add-on to specify it's own licensing requirements (including none). The server // will only honor this data if the add-on is signed with the valid private key. project.convention.plugins.addoninfo.addExtension('licenseRequirements', new LicenseRequirementsExtension()) } File pickServerDir() { def serverDir = null; def builder = new groovy.swing.SwingBuilder() builder.registerBeanFactory('folderChooser', com.jidesoft.swing.FolderChooser) builder.build { frame(id: 'mainframe', title:'Pick Server Root Dir', defaultCloseOperation:JFrame.EXIT_ON_CLOSE, size:[440,440], locationRelativeTo: null, show: true) { panel(border: emptyBorder(10)) { boxLayout axis: BoxLayout.Y_AXIS panel { boxLayout axis: BoxLayout.X_AXIS label text: 'In order to deploy the add-on to the server (and to run some tests), the build needs to know where the root directory of the server is located.' } rigidArea size: new Dimension(0, 10) panel { boxLayout axis: BoxLayout.Y_AXIS panel { boxLayout axis: BoxLayout.X_AXIS label text: 'Server root dir:' hglue() } rigidArea size: new Dimension(0, 5) panel { boxLayout axis: BoxLayout.X_AXIS folderChooser id: 'driverDirChooser', currentDirectory: new File('.'), navigationFieldVisible: false, controlButtonsAreShown: false, availableButtons: 0, recentListVisible: false hglue() } } rigidArea size: new Dimension(0, 10) panel { boxLayout axis: BoxLayout.X_AXIS hglue() button text: 'Accept', actionPerformed: { serverDir = builder.driverDirChooser.selectedFolder dispose() } hglue() button text: 'Cancel', actionPerformed: { dispose() } hglue() } } } } // block until the gui is closed while (builder.mainframe.isVisible()) { Thread.sleep(100) } if (serverDir == null) throw new GradleException("ERROR: Build cannot continue because the root directory of the server was not specified") return serverDir } idea { project { ipr.withXml { def node = it.asNode() def vcsConfig = node.component.find { it.'@name' == 'VcsDirectoryMappings' } vcsConfig.mapping[0].'@vcs' = 'Git' } } } buildscript { repositories { maven { url 'http://download.java.net/maven/2/' } } dependencies { classpath group: 'com.jidesoft', name: 'jide-oss', version: '2.9.0' } } // Support for info.xml class AddOnInfo { def String name def String description def String version def String vendor Map extensions = [:] void info(Closure c) { c.setDelegate(this) c.setResolveStrategy Closure.DELEGATE_ONLY c() } def addExtension(name, obj) { extensions.put(name, obj) } // for backwards compatibility def propertyMissing(String name, value) { if (name == 'systemMenuProvider') extensions.get('providers')?.systemMenu = value else throw new MissingPropertyException(name) } def methodMissing(String name, args) { if (args[0] instanceof Closure) { def ext = extensions.get(name) if (ext) { args[0].setDelegate(ext) args[0].setResolveStrategy Closure.DELEGATE_ONLY return args[0].call() } } throw new MissingMethodException(name, delegate, args) } Map getProperties() { [ name:name, description:description, version:version, vendor:vendor, extensions:extensions.collectEntries { key, value -> [(key) : value.getProperties()] } ] } private Map getPropMap() { def map = [:] if (name) map['name'] = name if (description) map['description'] = description if (version) map['version'] = version if (vendor) map['vendor'] = vendor return map } void addXml(root) { propMap.each { key, value -> root."$key" { root.mkp.yield(value) } } extensions.values().each { it.addXml(root) } } } // support for providers in info.xml class ProviderExtension { Map providers = [:] String propNameToXmlElementName(String propName) { // this converts a camelCase string to dash-separated all lower case string (i.e. camel-case) propName.replaceAll('[A-Z]', { '-'+it.toLowerCase() }) + '-provider' } def propertyMissing(name, value) { providers.put(name, value); } Map getProperties() { return providers; } void addXml(root) { providers.each { key, value -> root."${propNameToXmlElementName(key)}" { root.mkp.yield(value) } } } } // support for license requirements in info.xml (only available if signing is requested class LicenseRequirementsExtension { List features = [] Closure markup = {} Map getProperties() { return [features:features]; } void addXml(root) { root.'license-requirements' { features.each { str -> feature { mkp.yield(str) } } markup.delegate = root markup(); } } }