mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 02:09:44 +02:00
GP-707: GhidraDev updates
- New wizard to import a module source dir - Ghidra won't launch if build dir is present - Better validation when exporting extension
This commit is contained in:
parent
fc0e3e1b6f
commit
91cb801521
20 changed files with 568 additions and 61 deletions
|
@ -73,6 +73,15 @@ public class GhidraLaunchDelegate extends JavaLaunchDelegate {
|
|||
return;
|
||||
}
|
||||
|
||||
// Make sure there isn't a build/ directory present...it messes up the classpath.
|
||||
// The build directory could exist if the user built an extension from the command line
|
||||
// rather than from the Eclipse wizard
|
||||
if (javaProject.getProject().getFolder("build").exists()) {
|
||||
EclipseMessageUtils.showErrorDialog("Failed to launch project \"" + projectName +
|
||||
"\".\nDelete top-level 'build' directory and try again.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Set program arguments
|
||||
String customProgramArgs =
|
||||
configuration.getAttribute(GhidraLaunchUtils.ATTR_PROGAM_ARGUMENTS, "").trim();
|
||||
|
|
|
@ -44,6 +44,12 @@ public class GhidraProjectCreatorPreferences {
|
|||
*/
|
||||
static final String GHIDRA_LAST_PROJECT_ROOT_PATH = "ghidradev.ghidraLastProjectRootPath";
|
||||
|
||||
/**
|
||||
* Path to the last used Ghidra module source directory.
|
||||
*/
|
||||
static final String GHIDRA_LAST_MODULE_SOURCE_DIR_PATH =
|
||||
"ghidradev.ghidraLastModuleSourceDirPath";
|
||||
|
||||
/**
|
||||
* The last used Gradle distribution.
|
||||
*/
|
||||
|
@ -123,6 +129,28 @@ public class GhidraProjectCreatorPreferences {
|
|||
prefs.setValue(GHIDRA_LAST_PROJECT_ROOT_PATH, path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the last used Ghidra module source directory path that's defined in the preferences.
|
||||
*
|
||||
* @return The last used Ghidra module source directory path that's defined in the preferences.
|
||||
* Could be the empty string.
|
||||
*/
|
||||
public static String getGhidraLastModuleSourceDirPath() {
|
||||
IPreferenceStore prefs = Activator.getDefault().getPreferenceStore();
|
||||
return prefs.getString(GHIDRA_LAST_MODULE_SOURCE_DIR_PATH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the last used Ghidra module source directory path that's defined in the preferences.
|
||||
*
|
||||
* @param path The last used Ghidra module source directory path that's defined in the
|
||||
* preferences.
|
||||
*/
|
||||
public static void setGhidraLastModuleSourceDirPath(String path) {
|
||||
IPreferenceStore prefs = Activator.getDefault().getPreferenceStore();
|
||||
prefs.setValue(GHIDRA_LAST_MODULE_SOURCE_DIR_PATH, path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the last used Ghidra Gradle distribution that's defined in the preferences.
|
||||
*
|
||||
|
|
|
@ -32,6 +32,7 @@ import ghidra.GhidraLauncher;
|
|||
/**
|
||||
* Utility methods for working with Ghidra launchers in Eclipse.
|
||||
*/
|
||||
@SuppressWarnings("restriction")
|
||||
public class GhidraLaunchUtils {
|
||||
|
||||
/**
|
||||
|
|
|
@ -94,6 +94,7 @@ public class GhidraModuleUtils {
|
|||
sourceFolders.add(project.getFolder("src/main/java"));
|
||||
sourceFolders.add(project.getFolder("src/main/help"));
|
||||
sourceFolders.add(project.getFolder("src/main/resources"));
|
||||
sourceFolders.add(project.getFolder("src/test/java"));
|
||||
sourceFolders.add(project.getFolder("ghidra_scripts"));
|
||||
for (IFolder sourceFolder : sourceFolders) {
|
||||
GhidraProjectUtils.createFolder(sourceFolder, monitor);
|
||||
|
@ -203,6 +204,76 @@ public class GhidraModuleUtils {
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports a Ghidra module source directory to a new Ghidra module project with the given name.
|
||||
*
|
||||
* @param projectName The name of the project to create.
|
||||
* @param moduleSourceDir The module source directory to import.
|
||||
* @param createRunConfig Whether or not to create a new run configuration for the project.
|
||||
* @param runConfigMemory The run configuration's desired memory. Could be null.
|
||||
* @param ghidraLayout The Ghidra layout to link the project to.
|
||||
* @param jythonInterpreterName The name of the Jython interpreter to use for Python support.
|
||||
* Could be null if Python support is not wanted.
|
||||
* @param monitor The progress monitor to use during project creation.
|
||||
* @return The imported project.
|
||||
* @throws IOException If there was a file-related problem with creating the project.
|
||||
* @throws ParseException If there was a parse-related problem with creating the project.
|
||||
* @throws CoreException If there was an Eclipse-related problem with creating the project.
|
||||
*/
|
||||
public static IJavaProject importGhidraModuleSource(String projectName, File moduleSourceDir,
|
||||
boolean createRunConfig, String runConfigMemory, GhidraApplicationLayout ghidraLayout,
|
||||
String jythonInterpreterName, IProgressMonitor monitor)
|
||||
throws IOException, ParseException, CoreException {
|
||||
|
||||
// Create empty Ghidra project
|
||||
IJavaProject javaProject =
|
||||
GhidraProjectUtils.createEmptyGhidraProject(projectName, moduleSourceDir,
|
||||
createRunConfig, runConfigMemory, ghidraLayout, jythonInterpreterName, monitor);
|
||||
IProject project = javaProject.getProject();
|
||||
|
||||
// Find source directory paths
|
||||
List<IPath> sourcePaths = new ArrayList<>();
|
||||
IFolder srcFolder = project.getFolder("src");
|
||||
List<IFolder> srcSubFolders = getSubFolders(srcFolder);
|
||||
if (!srcSubFolders.isEmpty()) {
|
||||
for (IFolder srcSubFolder : srcSubFolders) {
|
||||
List<IFolder> subSubFolders = getSubFolders(srcSubFolder);
|
||||
if (!subSubFolders.isEmpty()) {
|
||||
sourcePaths.addAll(subSubFolders.stream().map(e -> e.getFullPath()).toList());
|
||||
}
|
||||
else {
|
||||
sourcePaths.add(srcSubFolder.getFullPath());
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
sourcePaths.add(srcFolder.getFullPath());
|
||||
}
|
||||
|
||||
// Find jar file paths
|
||||
List<IPath> jarPaths = new ArrayList<>();
|
||||
IFolder libFolder = project.getFolder("lib");
|
||||
if (libFolder.exists()) {
|
||||
for (IResource resource : libFolder.members()) {
|
||||
if (resource.getType() == IResource.FILE &&
|
||||
resource.getFileExtension().equals("jar")) {
|
||||
jarPaths.add(resource.getFullPath());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Put the source and jar paths in the project's classpath
|
||||
List<IClasspathEntry> cp = new ArrayList<>();
|
||||
cp.addAll(sourcePaths.stream().map(e -> JavaCore.newSourceEntry(e)).toList());
|
||||
cp.addAll(jarPaths.stream().map(e -> JavaCore.newLibraryEntry(e, null, null)).toList());
|
||||
GhidraProjectUtils.addToClasspath(javaProject, cp, monitor);
|
||||
|
||||
// Update language ant properties file
|
||||
GhidraModuleUtils.writeAntProperties(project, ghidraLayout);
|
||||
|
||||
return javaProject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes project-specific ant properties, which get imported by the module project's language
|
||||
* build.xml file to allow building against a Ghidra that lives in an external location. If the
|
||||
|
@ -278,4 +349,24 @@ public class GhidraModuleUtils {
|
|||
Change change = refactoring.createChange(monitor);
|
||||
change.perform(monitor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a {@link List} of sub-folders
|
||||
*
|
||||
* @param folder The folder to get the sub-folders of
|
||||
* @return A {@link List} of
|
||||
* @throws CoreException If there was an Eclipse-related problem getting the sub-folders
|
||||
*/
|
||||
private static List<IFolder> getSubFolders(IFolder folder) throws CoreException {
|
||||
List<IFolder> subFolders = new ArrayList<>();
|
||||
if (folder.exists()) {
|
||||
for (IResource resource : folder.members()) {
|
||||
if (resource.getType() == IResource.FOLDER) {
|
||||
subFolders.add(folder.getFolder(resource.getName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return subFolders;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
package ghidradev.ghidraprojectcreator.utils;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.ParseException;
|
||||
import java.util.*;
|
||||
|
||||
|
@ -43,6 +44,7 @@ import utility.module.ModuleUtilities;
|
|||
/**
|
||||
* Utility methods for working with Eclipse Ghidra projects.
|
||||
*/
|
||||
@SuppressWarnings("restriction")
|
||||
public class GhidraProjectUtils {
|
||||
|
||||
/**
|
||||
|
@ -290,6 +292,9 @@ public class GhidraProjectUtils {
|
|||
IJavaProject javaProject = JavaCore.create(project);
|
||||
project.open(monitor);
|
||||
|
||||
// Set the project's default encoding
|
||||
project.setDefaultCharset(StandardCharsets.UTF_8.displayName(), monitor);
|
||||
|
||||
// Clear the project's classpath
|
||||
javaProject.setRawClasspath(new IClasspathEntry[0], monitor);
|
||||
|
||||
|
|
|
@ -61,7 +61,7 @@ public class CreateGhidraModuleProjectWizard extends Wizard implements INewWizar
|
|||
@Override
|
||||
public void init(IWorkbench wb, IStructuredSelection selection) {
|
||||
workbench = wb;
|
||||
projectPage = new CreateGhidraProjectWizardPage();
|
||||
projectPage = new CreateGhidraProjectWizardPage(true);
|
||||
projectConfigPage = new ConfigureGhidraModuleProjectWizardPage();
|
||||
ghidraInstallationPage = new ChooseGhidraInstallationWizardPage();
|
||||
pythonPage = new EnablePythonWizardPage(ghidraInstallationPage);
|
||||
|
|
|
@ -54,7 +54,7 @@ public class CreateGhidraScriptProjectWizard extends Wizard implements INewWizar
|
|||
|
||||
@Override
|
||||
public void init(IWorkbench wb, IStructuredSelection selection) {
|
||||
projectPage = new CreateGhidraProjectWizardPage("GhidraScripts");
|
||||
projectPage = new CreateGhidraProjectWizardPage("GhidraScripts", true);
|
||||
projectConfigPage = new ConfigureGhidraScriptProjectWizardPage();
|
||||
ghidraInstallationPage = new ChooseGhidraInstallationWizardPage();
|
||||
pythonPage = new EnablePythonWizardPage(ghidraInstallationPage);
|
||||
|
|
|
@ -41,6 +41,7 @@ import org.eclipse.ui.IWorkbench;
|
|||
|
||||
import ghidra.GhidraApplicationLayout;
|
||||
import ghidra.launch.JavaConfig;
|
||||
import ghidradev.EclipseMessageUtils;
|
||||
import ghidradev.ghidraprojectcreator.utils.GhidraProjectUtils;
|
||||
import ghidradev.ghidraprojectcreator.wizards.pages.ChooseGhidraModuleProjectWizardPage;
|
||||
import ghidradev.ghidraprojectcreator.wizards.pages.ConfigureGradleWizardPage;
|
||||
|
@ -77,6 +78,10 @@ public class ExportGhidraModuleWizard extends Wizard implements INewWizard {
|
|||
|
||||
@Override
|
||||
public boolean performFinish() {
|
||||
if (!validate()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
IJavaProject javaProject = projectPage.getGhidraModuleProject();
|
||||
GradleDistribution gradleDist = gradlePage.getGradleDistribution();
|
||||
try {
|
||||
|
@ -169,4 +174,26 @@ public class ExportGhidraModuleWizard extends Wizard implements INewWizard {
|
|||
monitor.done();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the wizard pages. If they are invalid, an error popup will be displayed which
|
||||
* will indicate the problem.
|
||||
*
|
||||
* @return True if the data returned from the wizard pages are valid; otherwise, false
|
||||
*/
|
||||
private boolean validate() {
|
||||
String title = "Invalid Ghidra Module Extension";
|
||||
IJavaProject javaProject = projectPage.getGhidraModuleProject();
|
||||
if (!javaProject.getProject().getFile("extension.properties").exists()) {
|
||||
EclipseMessageUtils.showErrorDialog(title,
|
||||
"Cannot export extension because 'extension.properties' file does not exist.");
|
||||
return false;
|
||||
}
|
||||
if (!javaProject.getProject().getFile("Module.manifest").exists()) {
|
||||
EclipseMessageUtils.showErrorDialog(title,
|
||||
"Cannot export extension because 'Module.manifest' file does not exist.");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,150 @@
|
|||
/* ###
|
||||
* 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.
|
||||
*/
|
||||
package ghidradev.ghidraprojectcreator.wizards;
|
||||
|
||||
import static ghidradev.EclipseMessageUtils.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.text.ParseException;
|
||||
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.core.runtime.IProgressMonitor;
|
||||
import org.eclipse.jface.viewers.IStructuredSelection;
|
||||
import org.eclipse.jface.wizard.Wizard;
|
||||
import org.eclipse.ui.IImportWizard;
|
||||
import org.eclipse.ui.IWorkbench;
|
||||
|
||||
import ghidra.GhidraApplicationLayout;
|
||||
import ghidradev.EclipseMessageUtils;
|
||||
import ghidradev.ghidraprojectcreator.utils.GhidraModuleUtils;
|
||||
import ghidradev.ghidraprojectcreator.wizards.pages.*;
|
||||
import utilities.util.FileUtilities;
|
||||
|
||||
/**
|
||||
* Wizard for importing Ghidra module source to a new Ghidra module project.
|
||||
*/
|
||||
public class ImportGhidraModuleSourceWizard extends Wizard implements IImportWizard {
|
||||
|
||||
private ChooseGhidraModuleSourceWizardPage sourcePage;
|
||||
private CreateGhidraProjectWizardPage projectPage;
|
||||
private ChooseGhidraInstallationWizardPage ghidraInstallationPage;
|
||||
private EnablePythonWizardPage pythonPage;
|
||||
|
||||
public ImportGhidraModuleSourceWizard() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(IWorkbench wb, IStructuredSelection selection) {
|
||||
sourcePage = new ChooseGhidraModuleSourceWizardPage();
|
||||
projectPage = new CreateGhidraProjectWizardPage(false);
|
||||
ghidraInstallationPage = new ChooseGhidraInstallationWizardPage();
|
||||
pythonPage = new EnablePythonWizardPage(ghidraInstallationPage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPages() {
|
||||
addPage(sourcePage);
|
||||
addPage(projectPage);
|
||||
addPage(ghidraInstallationPage);
|
||||
addPage(pythonPage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean performFinish() {
|
||||
if (!validate()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
File moduleSourceDir = sourcePage.getSourceDir();
|
||||
File ghidraInstallDir = ghidraInstallationPage.getGhidraInstallDir();
|
||||
String projectName = projectPage.getProjectName();
|
||||
boolean createRunConfig = projectPage.shouldCreateRunConfig();
|
||||
String runConfigMemory = projectPage.getRunConfigMemory();
|
||||
String jythonInterpreterName = pythonPage.getJythonInterpreterName();
|
||||
try {
|
||||
getContainer().run(true, false,
|
||||
monitor -> importModuleSource(ghidraInstallDir, projectName, moduleSourceDir,
|
||||
createRunConfig, runConfigMemory, jythonInterpreterName, monitor));
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
return false;
|
||||
}
|
||||
catch (InvocationTargetException e) {
|
||||
error(showWizardErrorDialog(getShell(), e), e);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports a Ghidra module source directory to a new Ghidra module project.
|
||||
*
|
||||
* @param ghidraInstallDir The Ghidra installation directory to use.
|
||||
* @param projectName The name of the project to create.
|
||||
* @param moduleSourceDir The module source directory to import.
|
||||
* @param createRunConfig Whether or not to create a new run configuration for the project.
|
||||
* @param runConfigMemory The run configuration's desired memory. Could be null.
|
||||
* @param jythonInterpreterName The name of the Jython interpreter to use for Python support.
|
||||
* Could be null if Python support is not wanted.
|
||||
* @param monitor The monitor to use during project creation.
|
||||
* @throws InvocationTargetException if an error occurred during project creation.
|
||||
*/
|
||||
private void importModuleSource(File ghidraInstallDir, String projectName, File moduleSourceDir,
|
||||
boolean createRunConfig, String runConfigMemory, String jythonInterpreterName,
|
||||
IProgressMonitor monitor) throws InvocationTargetException {
|
||||
try {
|
||||
info("Importing " + projectName + " at " + moduleSourceDir);
|
||||
monitor.beginTask("Importing " + projectName, 2);
|
||||
|
||||
GhidraApplicationLayout ghidraLayout = new GhidraApplicationLayout(ghidraInstallDir);
|
||||
monitor.worked(1);
|
||||
|
||||
GhidraModuleUtils.importGhidraModuleSource(projectName, moduleSourceDir,
|
||||
createRunConfig, runConfigMemory, ghidraLayout, jythonInterpreterName, monitor);
|
||||
monitor.worked(1);
|
||||
|
||||
info("Finished importing " + projectName);
|
||||
}
|
||||
catch (IOException | ParseException | CoreException e) {
|
||||
throw new InvocationTargetException(e);
|
||||
}
|
||||
finally {
|
||||
monitor.done();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the wizard pages. If they are invalid, an error popup will be displayed which
|
||||
* will indicate the problem.
|
||||
*
|
||||
* @return True if the data returned from the wizard pages are valid; otherwise, false
|
||||
*/
|
||||
private boolean validate() {
|
||||
if (FileUtilities.isPathContainedWithin(ghidraInstallationPage.getGhidraInstallDir(),
|
||||
sourcePage.getSourceDir())) {
|
||||
EclipseMessageUtils.showErrorDialog("Invalid Module Source Directory",
|
||||
"Module source directory cannot reside inside of the selected Ghidra installation directory.");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
/* ###
|
||||
* 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.
|
||||
*/
|
||||
package ghidradev.ghidraprojectcreator.wizards.pages;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.eclipse.jface.wizard.WizardPage;
|
||||
import org.eclipse.swt.SWT;
|
||||
import org.eclipse.swt.layout.GridData;
|
||||
import org.eclipse.swt.layout.GridLayout;
|
||||
import org.eclipse.swt.widgets.*;
|
||||
|
||||
import ghidradev.ghidraprojectcreator.preferences.GhidraProjectCreatorPreferences;
|
||||
|
||||
/**
|
||||
* A wizard page that lets the user choose a Ghidra module source directory.
|
||||
*/
|
||||
public class ChooseGhidraModuleSourceWizardPage extends WizardPage {
|
||||
|
||||
private Text sourceDirText;
|
||||
private Button sourceDirButton;
|
||||
|
||||
/**
|
||||
* Creates a new Ghidra module source chooser wizard page.
|
||||
*/
|
||||
public ChooseGhidraModuleSourceWizardPage() {
|
||||
super("ChooseGhidraModuleSourceWizardPage");
|
||||
setTitle("Choose Ghidra Module Source");
|
||||
setDescription("Choose a Ghidra module source directory.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createControl(Composite parent) {
|
||||
|
||||
Composite container = new Composite(parent, SWT.NULL);
|
||||
container.setLayout(new GridLayout(3, false));
|
||||
|
||||
// Source directory
|
||||
Label sourceDirLabel = new Label(container, SWT.NULL);
|
||||
String sourceDirToolTip = "The Ghidra module source directory.";
|
||||
sourceDirLabel.setText("Source directory:");
|
||||
sourceDirLabel.setToolTipText(sourceDirToolTip);
|
||||
sourceDirText = new Text(container, SWT.BORDER | SWT.SINGLE);
|
||||
sourceDirText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
|
||||
sourceDirText.setText(GhidraProjectCreatorPreferences.getGhidraLastModuleSourceDirPath());
|
||||
sourceDirText.addModifyListener(evt -> validate());
|
||||
sourceDirText.setToolTipText(sourceDirToolTip);
|
||||
sourceDirButton = new Button(container, SWT.BUTTON1);
|
||||
sourceDirButton.setText("...");
|
||||
sourceDirButton.setToolTipText("Browse to select source directory");
|
||||
sourceDirButton.addListener(SWT.Selection, evt -> {
|
||||
DirectoryDialog dialog = new DirectoryDialog(container.getShell());
|
||||
String path = dialog.open();
|
||||
if (path != null) {
|
||||
sourceDirText.setText(path);
|
||||
}
|
||||
});
|
||||
|
||||
validate();
|
||||
setControl(container);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the module source directory.
|
||||
*
|
||||
* @return The module source directory. Could be null if unspecified, however, the page will not
|
||||
* be valid until the module source directory is valid, so it should never be null when called
|
||||
* by other classes.
|
||||
*/
|
||||
public File getSourceDir() {
|
||||
if (sourceDirText.getText().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return new File(sourceDirText.getText());
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the fields on the page and updates the page's status.
|
||||
* Should be called every time a field on the page changes.
|
||||
*/
|
||||
private void validate() {
|
||||
String message = null;
|
||||
File sourceDir = new File(sourceDirText.getText());
|
||||
|
||||
if (!sourceDir.isAbsolute()) {
|
||||
message = "Source directory must be an absolute path";
|
||||
}
|
||||
else if (!sourceDir.isDirectory()) {
|
||||
message = "Source directory does not exist";
|
||||
}
|
||||
else if (!new File(sourceDir, "Module.manifest").exists()) {
|
||||
message = "Source directory does not contain a Module.manifest file";
|
||||
}
|
||||
else if (!new File(sourceDir, "build.gradle").exists()) {
|
||||
message = "Source directory does not contain a build.gradle file";
|
||||
}
|
||||
else if (new File(sourceDir, ".project").exists()) {
|
||||
message = "Source directory already contains a .project file";
|
||||
}
|
||||
else if (new File(sourceDir, ".classpath").exists()) {
|
||||
message = "Source directory already contains a .classpath file";
|
||||
}
|
||||
|
||||
setErrorMessage(message);
|
||||
setPageComplete(message == null);
|
||||
if (message == null) {
|
||||
GhidraProjectCreatorPreferences
|
||||
.setGhidraLastModuleSourceDirPath(sourceDirText.getText());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -39,6 +39,7 @@ import ghidradev.ghidraprojectcreator.utils.GhidraProjectUtils;
|
|||
public class CreateGhidraProjectWizardPage extends WizardPage {
|
||||
|
||||
private String suggestedProjectName;
|
||||
private boolean showProjectDir;
|
||||
|
||||
private Text projectNameText;
|
||||
private Text projectRootDirText;
|
||||
|
@ -50,19 +51,25 @@ public class CreateGhidraProjectWizardPage extends WizardPage {
|
|||
* Creates a Ghidra new project wizard page with the given suggested project name.
|
||||
*
|
||||
* @param suggestedProjectName The suggested project name.
|
||||
* @param showProjectDir True to show a component for selecting the root project directory;
|
||||
* otherwise, false
|
||||
*/
|
||||
public CreateGhidraProjectWizardPage(String suggestedProjectName) {
|
||||
public CreateGhidraProjectWizardPage(String suggestedProjectName, boolean showProjectDir) {
|
||||
super("CreateGhidraProjectWizardPage");
|
||||
setTitle("Create Ghidra Project");
|
||||
setDescription("Create a new Ghidra project.");
|
||||
this.suggestedProjectName = suggestedProjectName;
|
||||
this.showProjectDir = showProjectDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Ghidra new project wizard page.
|
||||
*
|
||||
* @param showProjectDir True to show a component for selecting the root project directory;
|
||||
* otherwise, false
|
||||
*/
|
||||
public CreateGhidraProjectWizardPage() {
|
||||
this("");
|
||||
public CreateGhidraProjectWizardPage(boolean showProjectDir) {
|
||||
this("", showProjectDir);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -81,25 +88,28 @@ public class CreateGhidraProjectWizardPage extends WizardPage {
|
|||
new Label(container, SWT.NONE).setText(""); // empty grid cell
|
||||
|
||||
// Project directory
|
||||
Label projectDirLabel = new Label(container, SWT.NULL);
|
||||
String projectDirToolTip = "The directory where this project will be created.";
|
||||
projectDirLabel.setText("Project root directory:");
|
||||
projectDirLabel.setToolTipText(projectDirToolTip);
|
||||
projectRootDirText = new Text(container, SWT.BORDER | SWT.SINGLE);
|
||||
projectRootDirText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
|
||||
projectRootDirText.setText(GhidraProjectCreatorPreferences.getGhidraLastProjectRootPath());
|
||||
projectRootDirText.addModifyListener(evt -> validate());
|
||||
projectRootDirText.setToolTipText(projectDirToolTip);
|
||||
projectDirButton = new Button(container, SWT.BUTTON1);
|
||||
projectDirButton.setText("...");
|
||||
projectDirButton.setToolTipText("Browse to select project root directory");
|
||||
projectDirButton.addListener(SWT.Selection, evt -> {
|
||||
DirectoryDialog dialog = new DirectoryDialog(container.getShell());
|
||||
String path = dialog.open();
|
||||
if (path != null) {
|
||||
projectRootDirText.setText(path);
|
||||
}
|
||||
});
|
||||
if (showProjectDir) {
|
||||
Label projectDirLabel = new Label(container, SWT.NULL);
|
||||
String projectDirToolTip = "The directory where this project will be created.";
|
||||
projectDirLabel.setText("Project root directory:");
|
||||
projectDirLabel.setToolTipText(projectDirToolTip);
|
||||
projectRootDirText = new Text(container, SWT.BORDER | SWT.SINGLE);
|
||||
projectRootDirText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
|
||||
projectRootDirText
|
||||
.setText(GhidraProjectCreatorPreferences.getGhidraLastProjectRootPath());
|
||||
projectRootDirText.addModifyListener(evt -> validate());
|
||||
projectRootDirText.setToolTipText(projectDirToolTip);
|
||||
projectDirButton = new Button(container, SWT.BUTTON1);
|
||||
projectDirButton.setText("...");
|
||||
projectDirButton.setToolTipText("Browse to select project root directory");
|
||||
projectDirButton.addListener(SWT.Selection, evt -> {
|
||||
DirectoryDialog dialog = new DirectoryDialog(container.getShell());
|
||||
String path = dialog.open();
|
||||
if (path != null) {
|
||||
projectRootDirText.setText(path);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Create run configuration checkbox
|
||||
createRunConfigCheckboxButton = new Button(container, SWT.CHECK);
|
||||
|
@ -153,14 +163,13 @@ public class CreateGhidraProjectWizardPage extends WizardPage {
|
|||
* Gets the project directory. This is the directory where the .project file should live.
|
||||
*
|
||||
* @return The project directory. This is the directory where the .project file should live.
|
||||
* Could be null if unspecified, however, the page will not be valid until the project
|
||||
* directory is valid, so it should never be null when called by other classes.
|
||||
* Could be null if unspecified.
|
||||
*/
|
||||
public File getProjectDir() {
|
||||
if (projectNameText.getText().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
if (projectRootDirText.getText().isEmpty()) {
|
||||
if (projectRootDirText == null || projectRootDirText.getText().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return new File(projectRootDirText.getText(), getProjectName());
|
||||
|
@ -227,10 +236,10 @@ public class CreateGhidraProjectWizardPage extends WizardPage {
|
|||
else if (BAD.chars().anyMatch(ch -> projectName.indexOf(ch) != -1)) {
|
||||
message = "Project name cannot contain invalid characters:\n " + BAD;
|
||||
}
|
||||
else if (projectDir == null) {
|
||||
else if (showProjectDir && projectDir == null) {
|
||||
message = "Project root directory must be specified";
|
||||
}
|
||||
else if (projectDir.exists()) {
|
||||
else if (showProjectDir && projectDir.exists()) {
|
||||
message = "Project already exists at: " + projectDir.getAbsolutePath();
|
||||
}
|
||||
else if (ResourcesPlugin.getWorkspace().getRoot().getProject(projectName).exists()) {
|
||||
|
@ -250,8 +259,10 @@ public class CreateGhidraProjectWizardPage extends WizardPage {
|
|||
setErrorMessage(message);
|
||||
setPageComplete(message == null);
|
||||
if (message == null) {
|
||||
GhidraProjectCreatorPreferences.setGhidraLastProjectRootPath(
|
||||
projectRootDirText.getText());
|
||||
if (projectRootDirText != null) {
|
||||
GhidraProjectCreatorPreferences
|
||||
.setGhidraLastProjectRootPath(projectRootDirText.getText());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ import org.eclipse.ui.ide.IDE;
|
|||
|
||||
import ghidradev.EclipseMessageUtils;
|
||||
|
||||
@SuppressWarnings("restriction")
|
||||
public class OpenDeclarations {
|
||||
|
||||
private IProject project;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue