GP-5138: GhidraDev/PyDev/PyGhidra integration

This commit is contained in:
Ryan Kurtz 2024-12-11 12:43:39 -05:00
parent 7c4d91f568
commit 31ee251a5c
35 changed files with 1300 additions and 315 deletions

View file

@ -78,20 +78,20 @@ public class LaunchSupport {
try {
File installDir = new File(installDirPath).getCanonicalFile(); // change relative path to absolute
JavaConfig javaConfig = new JavaConfig(installDir);
AppConfig appConfig = new AppConfig(installDir);
JavaFinder javaFinder = JavaFinder.create();
// Pass control to a mode-specific handler
switch (mode.toLowerCase()) {
case "-java_home":
exitCode = handleJavaHome(javaConfig, javaFinder, JavaFilter.ANY, ask, save);
exitCode = handleJavaHome(appConfig, javaFinder, JavaFilter.ANY, ask, save);
break;
case "-jdk_home":
exitCode =
handleJavaHome(javaConfig, javaFinder, JavaFilter.JDK_ONLY, ask, save);
handleJavaHome(appConfig, javaFinder, JavaFilter.JDK_ONLY, ask, save);
break;
case "-vmargs":
exitCode = handleVmArgs(javaConfig);
exitCode = handleVmArgs(appConfig);
break;
default:
System.err.println("LaunchSupport received illegal argument: " + mode);
@ -109,7 +109,7 @@ public class LaunchSupport {
* Handles figuring out a Java home directory to use for the launch. If it is successfully
* determined, an exit code that indicates success is returned.
*
* @param javaConfig The Java configuration that defines what we support.
* @param appConfig The appConfig configuration that defines what we support.
* @param javaFinder The Java finder.
* @param javaFilter A filter used to restrict what kind of Java installations we search for.
* @param ask True to interact with the user to they can specify a Java home directory.
@ -120,12 +120,12 @@ public class LaunchSupport {
* successfully determined.
* @throws IOException if there was a disk-related problem.
*/
private static int handleJavaHome(JavaConfig javaConfig, JavaFinder javaFinder,
private static int handleJavaHome(AppConfig appConfig, JavaFinder javaFinder,
JavaFilter javaFilter, boolean ask, boolean save) throws IOException {
if (ask) {
return askJavaHome(javaConfig, javaFinder, javaFilter);
return askJavaHome(appConfig, javaFinder, javaFilter);
}
return findJavaHome(javaConfig, javaFinder, javaFilter, save);
return findJavaHome(appConfig, javaFinder, javaFilter, save);
}
/**
@ -133,7 +133,7 @@ public class LaunchSupport {
* found, its path is printed to STDOUT and an exit code that indicates success is
* returned. Otherwise, nothing is printed to STDOUT and an error exit code is returned.
*
* @param javaConfig The Java configuration that defines what we support.
* @param appConfig The application configuration that defines what we support.
* @param javaFinder The Java finder.
* @param javaFilter A filter used to restrict what kind of Java installations we search for.
* @param save True if the determined Java home directory should get saved to a file.
@ -141,19 +141,19 @@ public class LaunchSupport {
* successfully determined.
* @throws IOException if there was a problem saving the java home to disk.
*/
private static int findJavaHome(JavaConfig javaConfig, JavaFinder javaFinder,
private static int findJavaHome(AppConfig appConfig, JavaFinder javaFinder,
JavaFilter javaFilter, boolean save) throws IOException {
File javaHomeDir;
LaunchProperties launchProperties = javaConfig.getLaunchProperties();
LaunchProperties launchProperties = appConfig.getLaunchProperties();
// PRIORITY 1: JAVA_HOME_OVERRIDE property
// If a valid java home override is specified in the launch properties, use that.
// Someone presumably wants to force that specific version.
javaHomeDir = launchProperties.getJavaHomeOverride();
if (javaConfig.isSupportedJavaHomeDir(javaHomeDir, javaFilter)) {
if (appConfig.isSupportedJavaHomeDir(javaHomeDir, javaFilter)) {
if (save) {
javaConfig.saveJavaHome(javaHomeDir);
appConfig.saveJavaHome(javaHomeDir);
}
System.out.println(javaHomeDir);
return EXIT_SUCCESS;
@ -162,10 +162,10 @@ public class LaunchSupport {
// PRIORITY 2: Java on PATH
// This program (LaunchSupport) was started with the Java on the PATH. Try to use this one
// next because it is most likely the one that is being upgraded on the user's system.
javaHomeDir = javaFinder.findSupportedJavaHomeFromCurrentJavaHome(javaConfig, javaFilter);
javaHomeDir = javaFinder.findSupportedJavaHomeFromCurrentJavaHome(appConfig, javaFilter);
if (javaHomeDir != null) {
if (save) {
javaConfig.saveJavaHome(javaHomeDir);
appConfig.saveJavaHome(javaHomeDir);
}
System.out.println(javaHomeDir);
return EXIT_SUCCESS;
@ -173,19 +173,19 @@ public class LaunchSupport {
// PRIORITY 3: Last used Java
// Check to see if a prior launch resulted in that Java being saved. If so, try to use that.
javaHomeDir = javaConfig.getSavedJavaHome();
if (javaConfig.isSupportedJavaHomeDir(javaHomeDir, javaFilter)) {
javaHomeDir = appConfig.getSavedJavaHome();
if (appConfig.isSupportedJavaHomeDir(javaHomeDir, javaFilter)) {
System.out.println(javaHomeDir);
return EXIT_SUCCESS;
}
// PRIORITY 4: Find all supported Java installations, and use the newest.
List<File> javaHomeDirs =
javaFinder.findSupportedJavaHomeFromInstallations(javaConfig, javaFilter);
javaFinder.findSupportedJavaHomeFromInstallations(appConfig, javaFilter);
if (!javaHomeDirs.isEmpty()) {
javaHomeDir = javaHomeDirs.iterator().next();
if (save) {
javaConfig.saveJavaHome(javaHomeDir);
appConfig.saveJavaHome(javaHomeDir);
}
System.out.println(javaHomeDir);
return EXIT_SUCCESS;
@ -199,7 +199,7 @@ public class LaunchSupport {
* If a valid Java home directory was successfully determined, it is saved to the user's
* Java home save file, and an exit code that indicates success is returned.
*
* @param javaConfig The Java configuration that defines what we support.
* @param appConfig The application configuration that defines what we support.
* @param javaFinder The Java finder.
* @param javaFilter A filter used to restrict what kind of Java installations we search for.
* * @return A suggested exit code based on whether or not a valid Java home directory was
@ -207,13 +207,13 @@ public class LaunchSupport {
* @throws IOException if there was a problem interacting with the user, or saving the java
* home location to disk.
*/
private static int askJavaHome(JavaConfig javaConfig, JavaFinder javaFinder,
private static int askJavaHome(AppConfig appConfig, JavaFinder javaFinder,
JavaFilter javaFilter) throws IOException {
String javaName = javaFilter.equals(JavaFilter.JDK_ONLY) ? "JDK" : "Java";
String javaRange;
int min = javaConfig.getMinSupportedJava();
int max = javaConfig.getMaxSupportedJava();
int min = appConfig.getMinSupportedJava();
int max = appConfig.getMaxSupportedJava();
if (min == max) {
javaRange = min + "";
}
@ -226,7 +226,7 @@ public class LaunchSupport {
System.out.println("******************************************************************");
System.out.println(
javaName + " " + javaRange + " (" + javaConfig.getSupportedArchitecture() +
javaName + " " + javaRange + " (" + appConfig.getSupportedArchitecture() +
"-bit) could not be found and must be manually chosen!");
System.out.println("******************************************************************");
@ -254,13 +254,13 @@ public class LaunchSupport {
continue;
}
try {
JavaVersion javaVersion = javaConfig.getJavaVersion(javaHomeDir, javaFilter);
if (javaConfig.isJavaVersionSupported(javaVersion)) {
JavaVersion javaVersion = appConfig.getJavaVersion(javaHomeDir, javaFilter);
if (appConfig.isJavaVersionSupported(javaVersion)) {
break;
}
System.out.println(
"Java version " + javaVersion + " is outside of supported range: [" +
javaRange + " " + javaConfig.getSupportedArchitecture() + "-bit]");
javaRange + " " + appConfig.getSupportedArchitecture() + "-bit]");
}
catch (FileNotFoundException e) {
System.out.println(
@ -271,7 +271,7 @@ public class LaunchSupport {
}
}
File javaHomeSaveFile = javaConfig.saveJavaHome(javaHomeDir);
File javaHomeSaveFile = appConfig.saveJavaHome(javaHomeDir);
System.out.println("Saved changes to " + javaHomeSaveFile);
return EXIT_SUCCESS;
}
@ -281,18 +281,18 @@ public class LaunchSupport {
* to STDOUT as a new-line delimited string that can be parsed and added to the command line,
* and an exit code that indicates success is returned.
* @param javaConfig The Java configuration that defines what we support.
* @param appConfig The appConfig configuration that defines what we support.
* @return A suggested exit code based on whether or not the VM arguments were successfully
* gotten.
*/
private static int handleVmArgs(JavaConfig javaConfig) {
if (javaConfig.getLaunchProperties() == null) {
private static int handleVmArgs(AppConfig appConfig) {
if (appConfig.getLaunchProperties() == null) {
System.out.println("Launch properties file was not specified!");
return EXIT_FAILURE;
}
// Force newline style to make cross-platform parsing consistent
javaConfig.getLaunchProperties().getVmArgList().forEach(e -> System.out.print(e + "\r\n"));
appConfig.getLaunchProperties().getVmArgList().forEach(e -> System.out.print(e + "\r\n"));
return EXIT_SUCCESS;
}
}

View file

@ -4,9 +4,9 @@
* 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.
@ -17,21 +17,23 @@ package ghidra.launch;
import java.io.*;
import java.text.ParseException;
import java.util.Properties;
import java.util.*;
import ghidra.launch.JavaFinder.JavaFilter;
/**
* Class to determine and represent a required Java configuration, including minimum and maximum
* supported versions, compiler compliance level, etc.
* Class to determine and represent a required application configuration, including minimum and
* maximum supported Java versions, compiler compliance level, etc.
*/
public class JavaConfig {
public class AppConfig {
private static final String LAUNCH_PROPERTIES_NAME = "launch.properties";
private static final String JAVA_HOME_SAVE_NAME = "java_home.save";
private static final String PYTHON_COMMAND_SAVE_NAME = "python_command.save";
private LaunchProperties launchProperties;
private File javaHomeSaveFile;
private File pythonCommandSaveFile;
private String applicationName; // example: Ghidra
private String applicationVersion; // example: 9.0.1
@ -42,43 +44,43 @@ public class JavaConfig {
private String compilerComplianceLevel;
/**
* Creates a new Java configuration for the given installation.
* Creates a new application configuration for the given installation.
*
* @param installDir The installation directory.
* @throws FileNotFoundException if a required file was not found.
* @throws IOException if there was a problem reading a required file.
* @throws ParseException if there was a problem parsing a required file.
*/
public JavaConfig(File installDir) throws FileNotFoundException, IOException, ParseException {
public AppConfig(File installDir) throws FileNotFoundException, IOException, ParseException {
initApplicationProperties(installDir);
initLaunchProperties(installDir);
initJavaHomeSaveFile(installDir);
javaHomeSaveFile = getSaveFile(installDir, JAVA_HOME_SAVE_NAME);
pythonCommandSaveFile = getSaveFile(installDir, PYTHON_COMMAND_SAVE_NAME);
}
/**
* Gets the launch properties associated with this Java configuration. Certain aspects of the
* Java configuration are stored in the launch properties.
* Gets the launch properties associated with this application configuration.
*
* @return The launch properties associated with this Java configuration. Could be null if
* this Java configuration does not use launch properties.
* @return The launch properties associated with this application configuration. Could be null
* if this application configuration does not use launch properties.
*/
public LaunchProperties getLaunchProperties() {
return launchProperties;
}
/**
* Gets the Java configuration's minimum supported major Java version.
* Gets the application configuration's minimum supported major Java version.
*
* @return The Java configuration's minimum supported major Java version.
* @return The application configuration's minimum supported major Java version.
*/
public int getMinSupportedJava() {
return minSupportedJava;
}
/**
* Gets the Java configuration's maximum supported major Java version.
* Gets the application configuration's maximum supported major Java version.
*
* @return The Java configuration's maximum supported major Java version. If there is no
* @return The application configuration's maximum supported major Java version. If there is no
* restriction, the value will be 0.
*/
public int getMaxSupportedJava() {
@ -86,20 +88,20 @@ public class JavaConfig {
}
/**
* Gets the Java configuration's supported Java architecture. All supported Java
* Gets the application configuration's supported Java architecture. All supported Java
* configurations must have an architecture of <code>64</code>.
*
* @return The Java configuration's supported Java architecture (64).
* @return The application configuration's supported Java architecture (64).
*/
public int getSupportedArchitecture() {
return 64;
}
/**
* Gets the Java configuration's compiler compliance level that was used to build the
* associated installation.
* Gets the application configuration's Java compiler compliance level that was used to build
* the associated installation.
*
* @return The Java configuration's compiler compliance level.
* @return The application configuration's compiler compliance level.
*/
public String getCompilerComplianceLevel() {
return compilerComplianceLevel;
@ -115,8 +117,11 @@ public class JavaConfig {
public File getSavedJavaHome() throws IOException {
try (BufferedReader reader = new BufferedReader(new FileReader(javaHomeSaveFile))) {
String line = reader.readLine().trim();
if (line != null && !line.isEmpty()) {
return new File(line);
if (line != null) {
line = line.trim();
if (!line.isEmpty()) {
return new File(line);
}
}
}
catch (FileNotFoundException e) {
@ -148,12 +153,12 @@ public class JavaConfig {
}
/**
* Tests to see if the given directory is a supported Java home directory for this Java
* Tests to see if the given directory is a supported Java home directory for this application
* configuration.
*
* @param dir The directory to test.
* @param javaFilter A filter used to restrict what kind of Java installations we support.
* @return True if the given directory is a supported Java home directory for this Java
* @return True if the given directory is a supported Java home directory for this application
* configuration.
*/
public boolean isSupportedJavaHomeDir(File dir, JavaFilter javaFilter) {
@ -166,10 +171,10 @@ public class JavaConfig {
}
/**
* Tests to see if the given Java version is supported by this Java launch configuration.
* Tests to see if the given Java version is supported by this application configuration.
*
* @param javaVersion The java version to check.
* @return True if the given Java version is supported by this Java launch configuration.
* @return True if the given Java version is supported by this application configuration.
*/
public boolean isJavaVersionSupported(JavaVersion javaVersion) {
if (javaVersion.getArchitecture() != getSupportedArchitecture()) {
@ -233,6 +238,30 @@ public class JavaConfig {
return runAndGetJavaVersion(javaExecutable);
}
/**
* Gets the Python command from the user's Python command save file.
*
* @return The Python command from the user's Python command save file, or null if the file
* does not exist or is empty.
* @throws IOException if there was a problem reading the Python command save file.
*/
public List<String> getSavedPythonCommand() throws IOException {
List<String> command = new ArrayList<>();
try (BufferedReader reader = new BufferedReader(new FileReader(pythonCommandSaveFile))) {
String line = null;
while ((line = reader.readLine()) != null) {
line = line.trim();
if (!line.isEmpty()) {
command.add(line);
}
}
return command;
}
catch (FileNotFoundException e) {
return null;
}
}
/**
* Gets the version of the given Java executable from the output of running "java -version".
*
@ -354,13 +383,13 @@ public class JavaConfig {
}
/**
* Initializes the Java home save file.
* Gets the given "save file".
*
* @param installDir The Ghidra installation directory. This is the directory that has the
* "Ghidra" subdirectory in it.
* @throws FileNotFoundException if the user's home directory was not found.
*/
private void initJavaHomeSaveFile(File installDir) throws FileNotFoundException {
private File getSaveFile(File installDir, String saveFileName) throws FileNotFoundException {
boolean isDev = new File(installDir, "build.gradle").isFile();
String appName = applicationName.replaceAll("\\s", "").toLowerCase();
@ -385,16 +414,14 @@ public class JavaConfig {
// Handle legacy application layout
if (applicationLayoutVersion.equals("1")) {
userSettingsDir = new File(userHomeDir, "." + appName + "/." + userSettingsDirName);
javaHomeSaveFile = new File(userSettingsDir, JAVA_HOME_SAVE_NAME);
return;
return new File(userSettingsDir, saveFileName);
}
// Look for XDG environment variable
String xdgConfigHomeDirStr = System.getenv("XDG_CONFIG_HOME");
if (xdgConfigHomeDirStr != null && !xdgConfigHomeDirStr.isEmpty()) {
userSettingsDir = new File(xdgConfigHomeDirStr, appName + "/" + userSettingsDirName);
javaHomeSaveFile = new File(userSettingsDir, JAVA_HOME_SAVE_NAME);
return;
return new File(userSettingsDir, saveFileName);
}
// Look in current user settings directory
@ -420,7 +447,7 @@ public class JavaConfig {
"Failed to find the user settings directory: Unsupported operating system.");
}
javaHomeSaveFile = new File(userSettingsDir, JAVA_HOME_SAVE_NAME);
return new File(userSettingsDir, saveFileName);
}
/**

View file

@ -4,9 +4,9 @@
* 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.
@ -79,11 +79,11 @@ public abstract class JavaFinder {
* Returns a list of supported Java home directories from discovered Java installations.
* The list is sorted from newest Java version to oldest.
*
* @param javaConfig The Java configuration that defines what we support.
* @param appConfig The appConfig configuration that defines what we support.
* @param javaFilter A filter used to restrict what kind of Java installations we search for.
* @return A sorted list of supported Java home directories from discovered Java installations.
*/
public List<File> findSupportedJavaHomeFromInstallations(JavaConfig javaConfig,
public List<File> findSupportedJavaHomeFromInstallations(AppConfig appConfig,
JavaFilter javaFilter) {
Set<File> potentialJavaHomeSet = new TreeSet<>();
for (File javaRootInstallDir : getJavaRootInstallDirs()) {
@ -107,8 +107,8 @@ public abstract class JavaFinder {
for (File potentialJavaHomeDir : potentialJavaHomeSet) {
try {
JavaVersion javaVersion =
javaConfig.getJavaVersion(potentialJavaHomeDir, javaFilter);
if (javaConfig.isJavaVersionSupported(javaVersion)) {
appConfig.getJavaVersion(potentialJavaHomeDir, javaFilter);
if (appConfig.isJavaVersionSupported(javaVersion)) {
javaHomeToVersionMap.put(potentialJavaHomeDir, javaVersion);
}
}
@ -130,12 +130,12 @@ public abstract class JavaFinder {
* Returns the Java home directory corresponding to the current "java.home" system
* property (if it supported).
*
* @param javaConfig The Java configuration that defines what we support.
* @param appConfig The appConfig configuration that defines what we support.
* @param javaFilter A filter used to restrict what kind of Java installations we search for.
* @return The Java home directory corresponding to the current "java.home" system property.
* Could be null if the current "java.home" is not supported.
*/
public File findSupportedJavaHomeFromCurrentJavaHome(JavaConfig javaConfig,
public File findSupportedJavaHomeFromCurrentJavaHome(AppConfig appConfig,
JavaFilter javaFilter) {
Set<File> potentialJavaHomeSet = new HashSet<>();
String javaHomeProperty = System.getProperty("java.home");
@ -149,8 +149,8 @@ public abstract class JavaFinder {
}
for (File potentialJavaHomeDir : potentialJavaHomeSet) {
try {
if (javaConfig.isJavaVersionSupported(
javaConfig.getJavaVersion(potentialJavaHomeDir, javaFilter))) {
if (appConfig.isJavaVersionSupported(
appConfig.getJavaVersion(potentialJavaHomeDir, javaFilter))) {
return potentialJavaHomeDir;
}
}