/* ### * 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 plugin: 'eclipse' apply from: 'gradle/root/eclipse.gradle' apply from: "gradle/support/eclipseLauncher.gradle" apply from: "gradle/support/loadApplicationProperties.gradle" /*************************************************************************************** * Print current Java and Gradle version and make sure the correct version of gradle is being used ***************************************************************************************/ import org.gradle.util.GradleVersion; println "Java: " + JavaVersion.current() println "Gradle: " + GradleVersion.current().version checkGradleVersion() /*************************************************************************************** * Define the location of JAVA_HOME ***************************************************************************************/ if (System.env.JAVA_HOME) { // Allow Gradle's default JAVA_HOME to be overridden by an environment variable we set project.ext.JAVA_HOME = System.env.JAVA_HOME; } else { project.ext.JAVA_HOME = "${System.properties.'java.home'}" } /*************************************************************************************** * Make sure Gradle wasn't launched with a 32-bit Java...it can cause confusing errors ***************************************************************************************/ if ("32".equals(System.getProperty("sun.arch.data.model"))) { throw new GradleException("\n\n\t32-bit Java detected! Please use 64-bit Java.\n\n"); } /*************************************************************************************** * Identify supported Python command ***************************************************************************************/ project.ext.SUPPORTED_PY_VERSIONS = ['3.12', '3.11', '3.10', '3.9'] project.ext.PYTHON3 = findPython3(true) project.ext.PYTHON_DEPS = new HashSet() /********************************************************************************* * Define the location of bin repo *********************************************************************************/ project.ext.GHIDRA_GROUP = "Z Ghidra" project.ext.ROOT_PROJECT_DIR = projectDir.absolutePath project.ext.BIN_REPO = file("${projectDir}/../ghidra.bin").absolutePath project.ext.DEPS_DIR = file("${projectDir}/dependencies") /********************************************************************************* * Prevent forked Java processes from stealing focus *********************************************************************************/ allprojects { tasks.withType(JavaForkOptions) { jvmArgs '-Djava.awt.headless=true' } } /********************************************************************************* * Use flat directory-style repository if flatRepo directory is present. *********************************************************************************/ def flatRepo = file("${DEPS_DIR}/flatRepo") if (flatRepo.isDirectory()) { allprojects { repositories { mavenLocal() mavenCentral() flatDir name: "flat", dirs:["$flatRepo"] } } } else { File f = file("ghidra.repos.config") if (!f.exists()) { throw new GradleException("\n\n\n\tUnable to find the local maven repo." + " Ensure you have created the ${flatRepo.getName()} directory or ${f.getName()} file.\n\n\n"); } } /********************************************************************************* * Imports * For these tasks to be available on all subprojects, this MUST be placed * after the "setupJava" configuration. * * Note: Distribution.gradle and distributionNew.gradle must not be applied at the * same time; they have tasks with the same name. The former is the 'old' way * of building (produces several zips) while the former produces only one. * Eventually distribution.gradle will be removed entirely, but it is included * here for the time being for those who need it. *********************************************************************************/ apply from: "GPL/utils.gradle" // adds utilities used in both Ghidra and GPL projects apply from: "GPL/nativePlatforms.gradle" // adds native platform support apply from: "gradle/root/test.gradle" // adds tasks for running tests apply from: "gradle/root/prepDev.gradle" // adds prepDev task for each subproject apply from: 'gradle/root/distribution.gradle' // adds zip tasks apply from: 'gradle/root/usage.gradle' // adds task documentation apply from: "gradle/root/svg.gradle" // adds task to process svg files apply from: "gradle/root/jacoco.gradle" // adds tasks for java code coverage apply from: "gradle/root/venv.gradle" // adds tasks python virtual environments apply plugin: 'base' clean { delete "$buildDir" } /********************************************************************************* * Throws a GradleException if the current Gradle version is outside of the supported * Gradle version range defined in application.properties. * * NOTE: This function is duplicated in buildExtension.gradle *********************************************************************************/ import org.gradle.util.GradleVersion; def checkGradleVersion() { GradleVersion min = null; GradleVersion max = null; try { min = GradleVersion.version("${rootProject.GRADLE_MIN}") } catch (IllegalArgumentException e) { String defaultMin = "1.0" println "Invalid minimum Gradle version specified in application.properties...using ${defaultMin}" min = GradleVersion.version(defaultMin) } try { if (rootProject.GRADLE_MAX) { max = GradleVersion.version("${rootProject.GRADLE_MAX}") } } catch (IllegalArgumentException e) { println "Invalid maximum Gradle version specified in application.properties...ignoring" } String gradleRange = "at least ${min}" if (max) { gradleRange += " and less than ${max}" } if (GradleVersion.current() < min || (max && GradleVersion.current() >= max)) { throw new GradleException("Requires ${gradleRange}, but was run with $gradle.gradleVersion") } } /********************************************************************************* * Identifies supported python3 command to be used when building and checks for pip install. * Although warnings may be produced no exception is thrown since python only required * for specific build tasks and is not required for prepdev *********************************************************************************/ def checkPythonVersion(List pyCmd) { try { def stdout = new ByteArrayOutputStream() exec { commandLine pyCmd args "-c", "import sys; print('{0}.{1}'.format(*sys.version_info))" standardOutput = stdout errorOutput = OutputStream.nullOutputStream() } def version = "$stdout".strip() return version } catch (Exception e) { return "ABSENT" } } def checkPip(List pyCmd, boolean shouldPrint) { try { def stdout = new ByteArrayOutputStream() exec { commandLine pyCmd args "-c", "import pip; print(pip.__version__)" standardOutput = stdout errorOutput = OutputStream.nullOutputStream() } def version = "$stdout".strip(); if (shouldPrint) { if (version.length() == 0) { println("Warning: Python3 pip not installed (required for build)") } else { println("Python3 pip version: ${version}") } } return version } catch (Exception e) { if (shouldPrint) { println("Warning: Python3 pip not installed (required for build)") } } } def findPython3(boolean shouldPrint) { def pyCmds = [['py'], ['python3'], ['python']] pyCmds += SUPPORTED_PY_VERSIONS.collectMany { [["python$it"], ["py", "-$it"]] } for (pyCmd in pyCmds) { def pyVer = checkPythonVersion(pyCmd) if (pyVer in SUPPORTED_PY_VERSIONS) { if (shouldPrint) { println("Python3 command: ${pyCmd} (version ${pyVer})") } checkPip(pyCmd, shouldPrint) return pyCmd } } if (shouldPrint) { println("Warning: Supported Python ${SUPPORTED_PY_VERSIONS} not found (required for build)") } // Don't fail until task execution. Just retun null, which can be gracefully handled later. return null } /****************************************************************************************** * * Utility methods used by multiple build.gradle files * *****************************************************************************************/ /********************************************************************************* * Returns a relative path from the root (ghidra) to the project's directory. * This is used to determine where inside a zip file to place a particular artifact * (we want them to all start at "Ghidra/...." * * ie: If we have the following: * project dir = /Users//git/ghidra.master/ghidra/Ghidra/Features/Base * root project = /Users//git/ghidra.master/ghidra/Ghidra * * Then the returned value will be: * Ghidra/Features/Base * * There are two special cases - Projects that live outside ghidra and projects * that are extension projects. Projects that live outside ghidra will * have zip paths that make the project appear as if it did live in ghidra. * Projects that extend other projects will appear as though they live in the project * that they extend. See the note at the top of the distribution.gradle file for more details. *********************************************************************************/ def getZipPath(Project p) { String path = getGhidraRelativePath(p) // if the project has been defined as an "extension" to another project, change its // zip path to the path of its "base" project. A project is an extension if it has // defined an "extendsFromProject" property. if (p.hasProperty('extendsFromProject')) { Project baseProject = p.extendsFromProject path = getGhidraRelativePath(baseProject); } if (p.hasProperty("pathExtension")) { path = path + "/" + p.pathExtension } return path } def getBaseProjectName(Project p) { if (p.hasProperty('extendsFromProject')) { Project baseProject = p.extendsFromProject return baseProject.name } return p.name } /********************************************************************************* * Returns the current date formatted as yyyyMMdd. *********************************************************************************/ def getCurrentDate() { def date = new Date() def formattedDate = date.format('yyyyMMdd') return formattedDate } /********************************************************************************* * Returns the current date/time formatted as yyyyMMdd-HHmm. *********************************************************************************/ def getCurrentDateTime() { def date = new Date() def formattedDate = date.format('yyyyMMdd-HHmm') return formattedDate } /********************************************************************************* * Returns the current date/time formatted as yyyy-MMM-dd HHmm z. *********************************************************************************/ def getCurrentDateTimeLong() { def date = new Date() def formattedDate = date.format('yyyy-MMM-dd HHmm z') return formattedDate } /********************************************************************************* * Returns a list of all the external library paths declared as dependencies for the * given project * *********************************************************************************/ List getExternalRuntimeDependencies(Project project) { List list = new ArrayList() if (project.configurations.find { it.name == 'api' }) { list.addAll(getExternalRuntimeDependencies(project, project.configurations.api)); } if (project.configurations.find { it.name == 'implementation' }) { list.addAll(getExternalRuntimeDependencies(project, project.configurations.implementation)); } if (project.configurations.find { it.name == 'runtimeOnly' }) { list.addAll(getExternalRuntimeDependencies(project, project.configurations.runtimeOnly)); } return list } List getExternalRuntimeDependencies(Project project, Configuration configuration) { List list = new ArrayList<>(); configuration.dependencies.each { dep -> // if the dependency is an external jar if (dep instanceof ExternalDependency) { String name = dep.getName() Set classifiers = dep.artifacts.classifier String group = dep.getGroup() String version = dep.getVersion() String searchString = name if (version != null) { searchString = name+"-"+version; } if (!classifiers.empty) { String cls = classifiers.first() if (cls != null) { searchString+= "-$cls" } } // search for the dependency in the runtime class path String depPath = project.configurations.runtimeClasspath.find { it.name.contains(searchString) } if (depPath == null) { println("****************DID NOT FIND DEPENDENCY: name = "+name+" version = "+version) } // if we found the path, then add it to the list if (depPath) { list.add(depPath) } } } return list; } /****************************************************************************************** * * Creates a file that lists the libraries used by each module. * ******************************************************************************************/ String generateLibraryDependencyMapping() { File libsFile = file("$buildDir/libraryDependencies.txt") // Check to make sure the build folder exists - if it doesn't, the 'libsFile.withWriter' // call (below) will fail miserably. def buildFolder = file ("$buildDir") if (!buildFolder.exists()) { buildFolder.mkdirs() } libsFile.withWriter { out -> subprojects { p -> p.plugins.withType(JavaPlugin) { List libs = getExternalRuntimeDependencies(p); if (libs != null) { out.println "Module: $p.name" libs.each { path -> out.println "\t$path" } } } } } return libsFile.absolutePath } task allSleighCompile { }