/* ### * 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. */ apply from: "$rootProject.projectDir/gradle/distributableGhidraModule.gradle" apply from: "$rootProject.projectDir/gradle/javaProject.gradle" apply from: "$rootProject.projectDir/gradle/jacocoProject.gradle" apply from: "$rootProject.projectDir/gradle/javaTestProject.gradle" apply plugin: 'eclipse' eclipse.project.name = 'Features GhidraServer' def yajswRelease = "yajsw-stable-13.12" configurations { runGhidraServer } dependencies { api project(":FileSystem") api project(":DB") api project(":Generic") runGhidraServer project } CopySpec yajswCopySpec = copySpec { File depsFile = file("${DEPS_DIR}/GhidraServer/${yajswRelease}.zip") File binRepoFile = file("${BIN_REPO}/Ghidra/Features/GhidraServer/${yajswRelease}.zip") // First check if the file is in the dependencies repo. If not, check in the bin repo. def yajswZipTree = depsFile.exists() ? zipTree(depsFile) : zipTree(binRepoFile) from(yajswZipTree) { include "${yajswRelease}/lib/core/**" include "${yajswRelease}/lib/extended/**" include "${yajswRelease}/templates/**" include "${yajswRelease}/*.jar" include "${yajswRelease}/doc/**" include "${yajswRelease}/LICENSE.txt" include "${yajswRelease}/yajsw.policy.txt" } } // Unpack YAJSW archive into build/data for development use task yajswDevUnpack(type:Copy) { description = "Unpack YAJSW archive for development use" group = "Development Preparation" with yajswCopySpec destinationDir = file("build/data") } /********************************************************************************* * CLASSPATH FRAGMENT FILE: Production * * This task creates a file, 'GhidraServer/build/classpath.fragment', * which contains all classpath info necessary for running the server in * production mode. * * The basic conops is this: * 1. Loop over each lib in the runGhidraServer configuration. * 2. For each, find the location of that library in our distribution. * a. Search the artifacts in the parent project (GhidraServer) * b. If not found, loop over all dependent projects, searching their * artifacts. * 3. Put the path in the output file. *********************************************************************************/ task generateGhidraServerClasspath { description = "Generate a configuration fragment for the Ghidra Server's classpath (release)" File outfile = file("${buildDir}/classpath.frag") outputs.file outfile // Force the task to be executed every time by setting to false. outputs.upToDateWhen { false } def p = project doLast { outfile.parentFile.mkdirs() outfile.withPrintWriter { out -> int idx = 0 configurations.runGhidraServer.each { jarFile -> File distPath = file("/") String resPath for (Configuration conf : p.getConfigurations()) { resPath = findJarInDistribution(p, conf, jarFile) if (resPath != null) { out.println("wrapper.java.classpath.${++idx}=\${ghidra_home}/${resPath}") break } } } } } } /** * The classpath.frag file created below needs to be placed in the staging folder under the root * path of this project. * * Note that we use 'this.project' to reference the GhidraServer project - this is because * inside the closure, 'project' refers to the root project, while 'this' refers to * GhidraServer. */ rootProject.assembleDistribution { into (getZipPath(this.project) + "/data") { with yajswCopySpec from generateGhidraServerClasspath } into (getZipPath(this.project) + "/os") { from (projectDir.toString() + "/os") } } /********************************************************************************* * CLASSPATH FRAGMENT FILE: Developer * * This task creates a file, 'GhidraServer/build/dev-meta/classpath.fragment', * which contains all classpath info necessary for running the server in * dev mode. * * All items in the classpath will have the form: * wrapper.java.classpath.1=${ghidra_home}/ * * The items we gather are pulled exclusively from the 'runGhidraServer' configuration, * which is essentially the 'runtime' config + the GhidraServer jar. * *********************************************************************************/ task generateDevGhidraServerClasspath { description = "Generate a configuration fragment for the Ghidra Server's classpath (development)" File outfile = file("${buildDir}/dev-meta/classpath.frag") outputs.file outfile // Force the task to be executed every time by setting to false. outputs.upToDateWhen { false } doLast { outfile.parentFile.mkdirs() outfile.withPrintWriter { out -> int idx = 0 configurations.runGhidraServer.each { jarFile -> def JAR_PATH = jarFile.absolutePath def JAR_PATH_ECLIPSE = null // There might be dependencies in the gradle cache, which could be anywhere // (including a different drive). We can only use relative paths if the jar is in // the root project repo or bin repo. if (JAR_PATH.startsWith(rootProject.getProjectDir().absolutePath) || JAR_PATH.startsWith(BIN_REPO)) { JAR_PATH = "\${ghidra_home}/" + rootProject.relativePath(JAR_PATH) } JAR_PATH = JAR_PATH.replace('\\','/') // necessary for windows def index = JAR_PATH.indexOf("/build/") if (index != -1) { // Also use Module's bin/ class directory (produced by Eclipse) in addition to // jar (even if Eclipse will not be used) JAR_PATH_ECLIPSE = JAR_PATH.substring(0, index) + "/bin/main" } if (!jarFile.path.contains("/libsForBuild/")) { // Ensure Eclipse's compiled classes have precedence over the jars built by // Gradle by putting them first if (JAR_PATH_ECLIPSE) { out.println("wrapper.java.classpath.${++idx}=${JAR_PATH_ECLIPSE}") } out.println("wrapper.java.classpath.${++idx}=${JAR_PATH}") } } } } } /***************************************************************************************** * We want the two main tasks in this build file to be run at the appropriate time; the * dev task should be run during prepDev; the other should be run during the build *****************************************************************************************/ rootProject.prepDev.dependsOn(generateDevGhidraServerClasspath) rootProject.prepDev.dependsOn(yajswDevUnpack) //compileJava.dependsOn(generateGhidraServerClasspath) /********************************************************************************* * Searches the artifacts of a given configuration for a given jar. If * found, constructs a path to that jar ralative to the given project. * * Note that we have to check both the given configuration, AND the resolved * configuration to ensure we find all possible matches. *********************************************************************************/ def String searchArtifactsForJar (proj, conf, jarFile) { if (conf == null) { return null } String resolution = null PublishArtifactSet artifacts = conf.getArtifacts() artifacts.getFiles().each { f -> if (jarFile.equals(f)) { String path = rootProject.relativePath(proj.projectDir) + "/lib/" + f.name resolution = path } } if (artifacts.isEmpty()) { if (conf.isCanBeResolved()) { ResolvedConfiguration resolvedConfiguration = conf.getResolvedConfiguration() Set resolvedArtifacts = resolvedConfiguration.getResolvedArtifacts() for (ResolvedArtifact resolvedArtifact : resolvedArtifacts) { if (resolvedArtifact.getFile().equals(jarFile)) { Project artifactProject = getProjectForArtifact(resolvedArtifact) if (artifactProject != null) { String path = rootProject.relativePath(artifactProject.projectDir) + "/lib/" + resolvedArtifact.getFile().name resolution = path } break; } } } } return resolution } /********************************************************************************* * Returns the Project associated with a given ResolvedArtifact. *********************************************************************************/ def Project getProjectForArtifact(resolvedArtifact) { ResolvedModuleVersion moduleVersion = resolvedArtifact.getModuleVersion() ModuleVersionIdentifier versionIdent = moduleVersion.getId() String moduleName = versionIdent.getName() Project project = rootProject.findProject(moduleName) return project } /********************************************************************************* * Searches the files of the given configuration for the given jar. If found, * constructs a path to that jar ralative to the given project. * * Note that we hvae to use the resolved configuration here; if we don't have * access to this, the file is not available. *********************************************************************************/ def String searchFilesForJar (proj, conf, jar) { if (conf == null) { return null } String resolution = null if (conf.isCanBeResolved()) { conf.getFiles().each { f -> if (jar.equals(f)) { resolution = rootProject.relativePath(proj.projectDir) + "/lib/" + f.name } } } return resolution } /********************************************************************************* * Searches all dependencies of a given configuration for a particular jar * file. * * Note that there are two types of dependencies we have to check here: project * dependencies and normal files. The former requires that we recursively check * all the artifacts and dependencies of that project; the latter is just a check * of a list of files. *********************************************************************************/ def String searchDependenciesForJar (proj, conf, jarFile) { String relPath = null Set projectDependencies = conf.getAllDependencies().withType(ProjectDependency) for (ProjectDependency dependency : projectDependencies) { Project dependencyProject = dependency.metaClass.respondsTo(dependency, "getPath") ? proj.project(dependency.path) : dependency.getDependencyProject() // Remove this case after upgrading to 8.11 or later Configuration depProjectConf = dependencyProject.getConfigurations().getByName(conf.getName()) relPath = findJarInDistribution(dependencyProject, depProjectConf, jarFile); if (relPath != null) { return relPath; } } relPath = searchFilesForJar(proj, conf, jarFile) return relPath } /********************************************************************************* * Finds the location of a given jar in our distribution and returns the path * (relative to the root project). * * To do this, first search the artifacts of the project and if it's not found, * search all dependencies. *********************************************************************************/ def String findJarInDistribution(proj, conf, jarFile) { if (conf == null) { return null } String path = null path = searchArtifactsForJar(proj, conf, jarFile) if (path == null) { path = searchDependenciesForJar(proj, conf, jarFile) } return path }