import org.apache.tools.ant.filters.ReplaceTokens defaultTasks 'buildExtension' apply plugin: 'java' /***************************************************************************************** * * Reads the application.properties file and sets properties for the version, * release name, and distro prefix (ghidira_) * *****************************************************************************************/ def ghidraDir = buildscript.sourceFile.getAbsolutePath() + "/../../Ghidra" def ghidraProps = new Properties() file(ghidraDir + "/application.properties").withReader { reader -> ghidraProps.load(reader) project.ext.ghidra_version = ghidraProps.getProperty('application.version') project.ext.RELEASE_NAME = ghidraProps.getProperty('application.release.name') project.ext.DISTRO_PREFIX = "ghidra_${ghidra_version}" } configurations { helpPath } artifacts { helpPath jar } compileJava { sourceCompatibility = ghidraProps.getProperty('application.java.compiler') targetCompatibility = ghidraProps.getProperty('application.java.compiler') } dependencies { compile fileTree(dir: 'lib', include: "*.jar") compile fileTree(dir: ghidraDir + '/Framework', include: "**/*.jar") compile fileTree(dir: ghidraDir + '/Features', include: "**/*.jar") helpPath fileTree(dir: ghidraDir + '/Features/Base', include: "**/Base.jar") } def ZIP_NAME_PREFIX = "${DISTRO_PREFIX}_${RELEASE_NAME}_${getCurrentDate()}" def DISTRIBUTION_DIR = file("dist") def pathInZip = "${project.name}" task zipSource (type: Zip) { // Define some metadata about the zip (name, location, version, etc....) it.baseName project.name + "-src" it.extension 'zip' it.destinationDir file(project.projectDir.path + "/build/tmp/src") // We MUST copy from a directory, and not just grab a list of source files. // This is the only way to preserve the directory structure. it.from project.projectDir it.include 'src/**/*' } task buildExtension (type: Zip) { baseName "${ZIP_NAME_PREFIX}_${project.name}" extension 'zip' destinationDir DISTRIBUTION_DIR version '' // Make sure that we don't try to copy the same file with the same path into the // zip (this can happen!) duplicatesStrategy 'exclude' // This filtered property file copy must appear before the general // copy to ensure that it is prefered over the unmodified file File propFile = new File(project.projectDir, "extension.properties") from (propFile) { String version = "${ghidra_version}" String name = "${project.name}" filter (ReplaceTokens, tokens: [extversion: version]) filter (ReplaceTokens, tokens: [extname: name]) into pathInZip } from (project.jar) { into pathInZip + "/lib" } from (project.projectDir) { exclude 'build/**' exclude '*.gradle' exclude 'certification.manifest' exclude 'dist/**' exclude 'bin/**' exclude 'src/**' exclude '.gradle/**' exclude '.classpath' exclude '.project' exclude '.settings/**' exclude 'developer_scripts' exclude '.antProperties.xml' into pathInZip } ///////////////// // SOURCE ///////////////// from (tasks["zipSource"]) { into pathInZip + "/lib" }.dependsOn(zipSource) ///////////////// // GLOBALS ///////////////// // First get a list of all files that are under 'src/global'. FileTree fileTree = project.fileTree('src/global') { include '**/*' } // Now loop over each one, copying it into the zip we're creating. Each will be placed // at the root level, starting with the first folder AFTER 'src/global/'. // // eg: If the file is '/Ghidra/Extensions/Sample/src/global/docs/hello.html', then // the file in the zip will be at /docs/hello.html // fileTree.each { File file -> String filePath = stripGlobalFilePath(file) from (file) { into filePath } } doLast { println "\nCreated " + baseName + "." + extension + " in " + destinationDir } } // task for calling the java help indexer task indexHelp(type: JavaExec) { File helpRootDir = file('src/main/help') // this the root dir for the help source File outputFile = file("build/help/main/help/${project.name}_JavaHelpSearch") onlyIf {helpRootDir.exists()} dependsOn configurations.helpPath inputs.dir helpRootDir outputs.dir outputFile classpath = sourceSets.main.runtimeClasspath // this modules runtime classpath (contains jhall.jar) main = 'com.sun.java.help.search.Indexer' // main class to call // tell the indexer where send its output args '-db', outputFile.absolutePath // The index has a config file parameter. The only thing we use in the config file // is a root directory path that should be stripped off all the help references to // make them relative instead of absolute File configFile = file('build/helpconfig') // this is the config file that we will create // create the config file when the task runs and not during configuration. doFirst { configFile.parentFile.mkdirs(); configFile.write "IndexRemove ${helpRootDir.absolutePath}" + File.separator + "\n" } // pass the config file we created as an argument to the indexer args '-c',"$configFile" // gather up all the help files into a file collection FileTree helpFiles = fileTree('src/main/help') { include '**/*.htm' include '**/*.html' } doFirst { // for each help file that was found, add it as an argument to the indexer helpFiles.each { File file -> args "${file.absolutePath}" } } group "private" description "indexes the helps files for this module. [gradle/helpProject.gradle]" } // task for building Ghidra help files - depends on the output from the help indexer task buildHelp(type: JavaExec, dependsOn: indexHelp) { File helpRootDir = file('src/main/help/help') // help root where topics dir lives File outputDir = file('build/help/main/help') // dir where we want the help files to be generated onlyIf {helpRootDir.exists()} inputs.dir helpRootDir outputs.dir outputDir classpath = sourceSets.main.runtimeClasspath // this modules runtime classpath (contains jhall.jar) main = 'help.GHelpBuilder' // program to run to build help files. args '-n', "${project.name}" // use the modules name as the base for the help file name args '-o', "${outputDir.absolutePath}" // set the output directory arg doFirst { configurations.helpPath.each { args "-hp" args "${it.absolutePath}" } args "${helpRootDir.absolutePath}" // tell the help builder what help dir to process } description " Builds the help for this module. [gradle/helpProject.gradle]\n" } // include the help into the module's jar jar { from "build/help/main" // include the generated help index files from "src/main/help" // include the help source files version = "" } // build the help whenever this module's jar file is built jar.dependsOn 'buildHelp' /********************************************************************************* * Takes the given file and returns a string representing the file path with everything * up-to and including 'src/global' removed, as well as the filename. * * eg: If the file path is '/Ghidra/Configurations/Common/src/global/docs/hello.html', * the returned string will be at /docs * * Note: We have to use 'File.separator' instead of a slash ('/') because of how * windows/unix handle slashes ('/' vs. '\'). We only need to do this in cases where we're * using java string manipulation libraries (eg String.replace); Gradle already * understands how to use the proper slash. *********************************************************************************/ String stripGlobalFilePath(File file) { // First strip off everything before 'src/global/ in the file path. def slashIndex = file.path.indexOf('src' + File.separator + 'global') String filePath = file.path.substring(slashIndex); // Now remove 'src/global/' from the string. filePath = filePath.replace('src' + File.separator + 'global' + File.separator, ""); // Now we need to strip off the filename itself, which we do by finding the last // instance of a slash ('/') in the string. Unfortunately, groovy doesn't give // us a "lastIndexOf('/')" or something nice like that, so we reverse the string // and look for the slash that way, remove the filename, then reverse it back. // // Note that it's possible there is no slash (all we have is a filename), meaning // this file will be placed at the root level. // String reverseFilePath = filePath.reverse() slashIndex = reverseFilePath.indexOf(File.separator) if (slashIndex != -1) { reverseFilePath = reverseFilePath.substring(slashIndex) filePath = reverseFilePath.reverse() } else { filePath = "" } return filePath } /********************************************************************************* * Returns the current date formatted as yyyyMMdd. *********************************************************************************/ def getCurrentDate() { def date = new Date() def formattedDate = date.format('yyyyMMdd') return formattedDate }