ghidra/gradle/processorProject.gradle
2021-03-17 18:22:50 -04:00

277 lines
11 KiB
Groovy

/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*****************************************************************************************
This file is a "mix-in" gradle script that individual gradle projects should include if they
have processor definitions.
A gradle project can add native code support by including the following it its build.gradle file:
apply from: "$rootProject.projectDir/gradle/nativeProject.gradle"
*****************************************************************************************/
/*****************************************************************************************
*
* Create a configuration so the a dependency can be declared on the the software modeling
* project which is where the sleigh compiler java code lives. This will be used to
* form the classpath of the sleighCompile task that follows.
*
*****************************************************************************************/
configurations {
sleighConfig
}
dependencies {
sleighConfig project(':SoftwareModeling')
}
/*****************************************************************************************
*
* Sleigh compile options to be written to sleighArgs.txt in support of the following
* use cases:
* - Ant build using data/build.xml (development and distribution)
* - Eclipse Sleigh launcher (development only)
* - Ghidra runtime language rebuild (SleighLanguage.reloadLanguage; development and distribution)
* - Distribution build (sleighCompile task; development layout)
*
* This list may be added to or replaced by a specific processor project/module.
*
* Example: MIPS processor module dependency within a slaspec specified as:
*
* @include "$(BaseDir)$(MIPS)/data/language/maips.sinc
*
* with the corresponding MIPS definition specified within the sleighCompileOptions
* list specified within the module's build.gradle file:
*
* sleighCompileOptions.add "-DMIPS=%%MIPS%%"
* -or-
* sleighCompileOptions = [
* "-l",
* "-DMIPS=%%MIPS%%"
* ]
*
*****************************************************************************************/
ext.sleighCompileOptions = [ ]
/*****************************************************************************************
*
* Check for invalid sleighCompileOptions
*
*****************************************************************************************/
def checkSleighCompileOptions() {
sleighCompileOptions.each { a ->
def arg = a.trim()
assert !(arg.startsWith("-a") || arg.startsWith("-i")) : "Invalid sleighCompileOption: ${arg}"
}
}
/*****************************************************************************************
*
* Task to write sleigh compiler args for use with sleigh compiler.
* Due to the possible presence of module dependency paths two different sleighArgs.txt
* files are produced: one for development layout (build/tmp/sleighArgs.txt) and
* one for distribution layout ([build/]data/sleighArgs.txt). When invoking the
* Sleigh compiler and using a sleighArgs.txt file the appropriate 'BaseDir' property
* must be specified. Withing a distribution install 'BaseDir' must specifiy the
* path to the install directory while in a development layout 'BaseDir' must specify
* the repos root directory which contains the 'ghidra' repo directory.
*
*****************************************************************************************/
task saveSleighArgs {
def sleighArgsFile = file("build/data/sleighArgs.txt")
def sleighArgsDevFile = file("build/tmp/sleighArgs.txt")
outputs.files sleighArgsFile, sleighArgsDevFile
outputs.upToDateWhen { false }
doLast {
checkSleighCompileOptions()
sleighArgsFile.withWriter { out->
sleighCompileOptions.each { a->
out.println resolveSleighArg(a, false)
}
}
sleighArgsDevFile.withWriter { out->
sleighCompileOptions.each { a->
out.println resolveSleighArg(a, true)
}
}
}
}
rootProject.prepDev.dependsOn(saveSleighArgs)
apply plugin: 'base'
clean {
delete file("build/data/sleighArgs.txt")
delete file("build/tmp/sleighArgs.txt")
}
/*****************************************************************************************
*
* Task to write sleigh build.xml file for use is development mode only.
*
*****************************************************************************************/
task writeSleighDevBuild {
def templateFilePath = project(':BuildFiles').projectDir.toString() + "/sleighDevBuild.template"
doLast {
// Generate build.xml with injected classpath for running sleigh compiler
def sleighDevClasspath = project(':SoftwareModeling').sourceSets.main.runtimeClasspath.collect { it.absolutePath }.join(':')
copy {
into "data"
from (templateFilePath) {
rename { "build.xml" }
expand ( [ 'gradleSleighDevClasspath': sleighDevClasspath ] )
}
}
}
}
rootProject.prepDev.dependsOn(writeSleighDevBuild)
/*****************************************************************************************
*
* Write sleigh build.xml file for each language module into assembleDistribution
*
*****************************************************************************************/
rootProject.assembleDistribution {
into (getZipPath(this.project) + "/data") {
from (rootProject.projectDir.toString() + "/GhidraBuild/BuildFiles/sleighDistBuild.template") {
rename { "build.xml" }
}
}
}
/*****************************************************************************************
*
* Task to compile language files using the sleigh compiler.
*
*****************************************************************************************/
task sleighCompile (type: JavaExec) {
dependsOn saveSleighArgs
group = rootProject.GHIDRA_GROUP
description " Compiles all the sleigh languages. [gradle/processorProject.gradle]\n"
// define standard parameters for JavaExec
classpath configurations.sleighConfig
main = 'ghidra.pcodeCPort.slgh_compile.SleighCompile'
// Delay adding the directory argument until the first part of the execution phase, so
// that any extra args added by a project override will be added to the arg list before
// these arguments.
// NOTE: projects should no longer add arguments to this task and should instead
// add such args to the sleighCompileOptions list.
doFirst {
args "-i"
args "./build/tmp/sleighArgs.txt"
args "-DBaseDir=${getProjectReposRootPath()}"
args '-a'
args './data/languages'
}
jvmArgs '-Xmx2048M'
}
// The task that copies the common files to the distribution folder must depend on
// the sleigh tasks before executing.
rootProject.assembleDistribution.dependsOn sleighCompile
// Add in this projects sleighCompile to the allSleighCompile task
rootProject.allSleighCompile.dependsOn sleighCompile
/*****************************************************************************************
*
* Task to clean out the compile language files (*.sla)
*
*****************************************************************************************/
task cleanSleigh {
group rootProject.GHIDRA_GROUP
description "Removes all the compile sleigh language files (*.sla). [gradle/processorProject.gradle]\n"
doLast {
def deleteTree = fileTree(dir: "data/languages", include: "*.sla")
deleteTree.each { File file ->
delete file
}
}
}
/****************************************************************************************
*
* Set up inputs and outputs for the sleighCompile task so that languages only get build
* when the inputs change
*
* sleigh compile outputs to same directory as input. All files except .sla are input
*
******************************************************************************************/
def taskInputs = fileTree(dir: 'data/languages', exclude: '**/*.sla')
def taskOutputs = fileTree(dir: 'data/languages', include: '**/*.sla')
// define the sleigh compile inputs and outputs so that gradle can check if they need building
sleighCompile.inputs.files (taskInputs)
sleighCompile.outputs.files (taskOutputs)
// define the sleigh compile inputs to saveSleighArgs to limit task creation to language modules
saveSleighArgs.inputs.files (taskInputs)
/*****************************************************************************************
*
* Gets the absolute repos root directory path with a trailing File separator.
* This path may be used for specifying 'BaseDir' to the sleigh compiler within a
* development layout.
*
*****************************************************************************************/
def getProjectReposRootPath() {
return rootProject.projectDir.getParent() + File.separator
}
/*****************************************************************************************
*
* Filter a sleigh compiler argument replacing any project/module reference of the form
* %%MODULE%% witha that MODULE's relative path. If useDevPath is true the path will
* include the containing repo directory (e.g., ghidra/Ghidra/...), otherwise the
* path should start at the application root 'Ghidra/'. Only a single replacement per
* arg is supported.
*
* This mechanism relies on the relative depth of a language module project within a
* repository directory hierarchy. In general language module projects must reside
* within the directory Ghidra/Processors.
*
*****************************************************************************************/
def resolveSleighArg(String arg, boolean useDevPath) {
arg = arg.trim()
int index = arg.indexOf("%%")
if (index < 0) {
return arg
}
String newArg = arg.substring(0, index)
String tail = arg.substring(index+2)
index = tail.indexOf("%%")
assert index > 0 : "Badly formed sleigh path-replacment option: ${arg}"
String moduleName = tail.substring(0, index)
tail = tail.substring(index+2)
def moduleProject = project(":${moduleName}")
def modulePath
if (useDevPath) {
// first path element is the containing repo directory
modulePath = moduleProject.projectDir.absolutePath
modulePath = modulePath.substring(getProjectReposRootPath().length())
}
else {
// first path element is the Ghidra directory
modulePath = getZipPath(moduleProject)
}
newArg += modulePath
newArg += tail
return newArg
}