mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-09-20 09:31:47 +00:00
GP-3490: Fixing GhidraDev classpath issues
This commit is contained in:
parent
8cde73e787
commit
88bec10e60
|
@ -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.
|
||||
|
@ -16,7 +16,6 @@
|
|||
package ghidra.app.plugin.runtimeinfo;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
@ -249,11 +248,9 @@ class RuntimeInfoProvider extends ReusableDialogComponentProvider {
|
|||
*/
|
||||
private Map<Integer, String> getClasspathMap(String propertyName) {
|
||||
Map<Integer, String> map = new HashMap<>();
|
||||
StringTokenizer st =
|
||||
new StringTokenizer(System.getProperty(propertyName, ""), File.pathSeparator);
|
||||
int i = 0;
|
||||
while (st.hasMoreTokens()) {
|
||||
map.put(i++, st.nextToken());
|
||||
for (String entry : GhidraClassLoader.getClasspath(propertyName)) {
|
||||
map.put(i++, entry);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
@ -30,7 +30,6 @@ import java.util.stream.Collectors;
|
|||
|
||||
import javax.swing.event.ChangeListener;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
|
@ -491,24 +490,11 @@ public class ClassSearcher {
|
|||
// jar files will *not* be on the standard classpath, but instead will be on CP_EXT.
|
||||
//
|
||||
List<String> rawPaths = new ArrayList<>();
|
||||
getPropertyPaths(GhidraClassLoader.CP, rawPaths);
|
||||
getPropertyPaths(GhidraClassLoader.CP_EXT, rawPaths);
|
||||
rawPaths.addAll(GhidraClassLoader.getClasspath(GhidraClassLoader.CP));
|
||||
rawPaths.addAll(GhidraClassLoader.getClasspath(GhidraClassLoader.CP_EXT));
|
||||
return canonicalizePaths(rawPaths);
|
||||
}
|
||||
|
||||
private static void getPropertyPaths(String property, List<String> results) {
|
||||
String paths = System.getProperty(property);
|
||||
log.trace("Paths in {}: {}", property, paths);
|
||||
if (StringUtils.isBlank(paths)) {
|
||||
return;
|
||||
}
|
||||
|
||||
StringTokenizer st = new StringTokenizer(paths, File.pathSeparator);
|
||||
while (st.hasMoreTokens()) {
|
||||
results.add(st.nextToken());
|
||||
}
|
||||
}
|
||||
|
||||
private static List<String> canonicalizePaths(Collection<String> paths) {
|
||||
|
||||
//@formatter:off
|
||||
|
|
|
@ -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.
|
||||
|
@ -169,28 +169,15 @@ public class GhidraApplicationLayout extends ApplicationLayout {
|
|||
});
|
||||
}
|
||||
|
||||
// Examine the classpath to look for modules outside of the application root directories.
|
||||
// These might exist if Ghidra was launched from an Eclipse project that resides
|
||||
// external to the Ghidra installation.
|
||||
for (String entry : System.getProperty("java.class.path", "").split(File.pathSeparator)) {
|
||||
ResourceFile classpathEntry = new ResourceFile(entry);
|
||||
|
||||
// We only care about directories (skip jars)
|
||||
if (!classpathEntry.isDirectory()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip extensions in an application root directory... already found those.
|
||||
if (FileUtilities.isPathContainedWithin(applicationRootDirs, classpathEntry)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// We are going to assume that the classpath entry is in a subdirectory of the module
|
||||
// directory (i.e., bin/), so only check parent directory for the module.
|
||||
ResourceFile classpathEntryParent = classpathEntry.getParentFile();
|
||||
if (classpathEntryParent != null &&
|
||||
ModuleUtilities.isModuleDirectory(classpathEntryParent)) {
|
||||
moduleRootDirectories.add(classpathEntryParent);
|
||||
// Add external modules defined via a system property. This will typically be used by
|
||||
// user's developing 3rd party modules from something like Eclipse.
|
||||
String externalModules = System.getProperty("ghidra.external.modules", "");
|
||||
if (!externalModules.isBlank()) {
|
||||
for (String path : externalModules.split(File.pathSeparator)) {
|
||||
ResourceFile eclipseProjectDir = new ResourceFile(path);
|
||||
if (ModuleUtilities.isModuleDirectory(eclipseProjectDir)) {
|
||||
moduleRootDirectories.add(eclipseProjectDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
@ -18,8 +18,7 @@ package ghidra;
|
|||
import java.io.File;
|
||||
import java.lang.instrument.Instrumentation;
|
||||
import java.net.*;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.util.Msg;
|
||||
|
||||
|
@ -51,6 +50,25 @@ public class GhidraClassLoader extends URLClassLoader {
|
|||
*/
|
||||
public static final String CP_EXT = "java.class.path.ext";
|
||||
|
||||
/**
|
||||
* Gets a {@link List} containing the current classpath referenced by the given property name
|
||||
*
|
||||
* @param propertyName The property name of the classpath to get
|
||||
* @return A {@link List} containing the current classpath referenced by the given property name
|
||||
*/
|
||||
public static List<String> getClasspath(String propertyName) {
|
||||
List<String> result = new ArrayList<>();
|
||||
|
||||
// StringTokenizer is better than split() here because our result list will stay empty if
|
||||
// the classpath is empty
|
||||
StringTokenizer st =
|
||||
new StringTokenizer(System.getProperty(propertyName, ""), File.pathSeparator);
|
||||
while (st.hasMoreTokens()) {
|
||||
result.add(st.nextToken());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to prevent duplicate URL's from being added to the classpath
|
||||
*/
|
||||
|
|
|
@ -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.
|
||||
|
@ -150,11 +150,15 @@ public class GhidraLauncher {
|
|||
List<String> classpathList = new ArrayList<>();
|
||||
Map<String, GModule> modules = getOrderedModules(layout);
|
||||
|
||||
// First add any "bin" paths the module might have. These could come from external modules
|
||||
// being developed and passed in via system property if we are in release mode, or they
|
||||
// could be generated for each Ghidra module by Eclipse if we are in development mode.
|
||||
addModuleBinPaths(classpathList, modules);
|
||||
|
||||
if (SystemUtilities.isInDevelopmentMode()) {
|
||||
|
||||
// First add Eclipse's module "bin" paths. If we didn't find any, assume Ghidra was
|
||||
// If we didn't find any "bin" paths and we are in development mode, assume Ghidra was
|
||||
// compiled with Gradle, and add the module jars Gradle built.
|
||||
addModuleBinPaths(classpathList, modules);
|
||||
boolean gradleDevMode = classpathList.isEmpty();
|
||||
if (gradleDevMode) {
|
||||
// Add the module jars Gradle built.
|
||||
|
@ -165,22 +169,16 @@ public class GhidraLauncher {
|
|||
else { /* Eclipse dev mode */
|
||||
// Support loading pre-built, jar-based, non-repo extensions in Eclipse dev mode
|
||||
addExtensionJarPaths(classpathList, modules, layout);
|
||||
|
||||
// Eclipse launches the Utility module, so it's already on the classpath. We don't
|
||||
// want to add it a second time, so remove the one we discovered.
|
||||
GModule utilityModule = modules.get("Utility");
|
||||
if (utilityModule == null) {
|
||||
throw new IOException("Failed to find the 'Utility' module!");
|
||||
}
|
||||
classpathList.removeIf(
|
||||
e -> e.startsWith(utilityModule.getModuleRoot().getAbsolutePath()));
|
||||
}
|
||||
|
||||
// In development mode, jars do not live in module directories. Instead, each jar lives
|
||||
// in an external, non-repo location, which is listed in build/libraryDependencies.txt.
|
||||
// In development mode, 3rd party library jars do not live in module directories.
|
||||
// Instead, each jar lives in an external, non-repo location, which is listed in
|
||||
// build/libraryDependencies.txt.
|
||||
addExternalJarPaths(classpathList, layout.getApplicationRootDirs());
|
||||
}
|
||||
else {
|
||||
|
||||
// Release mode is simple. We expect all of Ghidra's modules to be in pre-build jars.
|
||||
addPatchPaths(classpathList, layout.getPatchDir());
|
||||
addModuleJarPaths(classpathList, modules);
|
||||
}
|
||||
|
@ -194,8 +192,16 @@ public class GhidraLauncher {
|
|||
// the standard classpath.)
|
||||
setExtensionJarPaths(modules, layout, classpathList);
|
||||
|
||||
classpathList = orderClasspath(classpathList, modules);
|
||||
return classpathList;
|
||||
// Ghidra launches from the Utility module, so it's already on the classpath. We don't
|
||||
// want to add it a second time, so remove the one we discovered.
|
||||
GModule utilityModule = modules.get("Utility");
|
||||
if (utilityModule == null) {
|
||||
throw new IOException("Failed to find the 'Utility' module!");
|
||||
}
|
||||
classpathList.removeIf(
|
||||
e -> e.startsWith(utilityModule.getModuleRoot().getAbsolutePath()));
|
||||
|
||||
return orderClasspath(classpathList, modules);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -265,9 +271,9 @@ public class GhidraLauncher {
|
|||
}
|
||||
|
||||
/**
|
||||
* Add extension module lib jars to the given path list. (This only needed in dev mode to find
|
||||
* any pre-built extensions that have been installed, since we already find extension module
|
||||
* jars in production mode.)
|
||||
* Add extension module lib jars to the given path list. (This is only needed in dev mode to
|
||||
* find any pre-built extensions that have been installed, since we already find extension
|
||||
* module jars in production mode.)
|
||||
*
|
||||
* @param pathList The list of paths to add to.
|
||||
* @param modules The modules to get the jars of.
|
||||
|
|
|
@ -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.
|
||||
|
@ -20,6 +20,7 @@ import java.text.SimpleDateFormat;
|
|||
import java.util.*;
|
||||
|
||||
import generic.jar.ResourceFile;
|
||||
import ghidra.GhidraApplicationLayout;
|
||||
|
||||
/**
|
||||
* The application properties. Application properties may either be stored on disk, or created
|
||||
|
@ -52,8 +53,11 @@ public class ApplicationProperties extends Properties {
|
|||
* Current application versions are:
|
||||
* <ul>
|
||||
* <li>1: Layout used by Ghidra < 11.1</li>
|
||||
* <li>2: Introduced with Ghidra 11.1. Default user settings/cache/temp directories changed,
|
||||
* and XDG environment variables are supported.
|
||||
* <li>2: Introduced with Ghidra 11.1. Default user settings/cache/temp directories changed,
|
||||
* and XDG environment variables are supported.</li>
|
||||
* <li>3: Introduced with Ghidra 11.2. Ghidra no longer finds external modules by examining
|
||||
* the initial classpath. Instead, the "ghidra.external.modules" system property is used
|
||||
* (see {@link GhidraApplicationLayout}).</li>
|
||||
* </ul>
|
||||
*/
|
||||
public static final String APPLICATION_LAYOUT_VERSION_PROPERTY = "application.layout.version";
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
application.name=Ghidra
|
||||
application.version=11.2
|
||||
application.release.name=DEV
|
||||
application.layout.version=2
|
||||
application.layout.version=3
|
||||
application.gradle.min=8.5
|
||||
application.gradle.max=
|
||||
application.java.min=21
|
||||
|
|
|
@ -158,6 +158,7 @@
|
|||
<setAttribute key="selected_target_bundles">
|
||||
<setEntry value="bcpg@default:default"/>
|
||||
<setEntry value="bcprov@default:default"/>
|
||||
<setEntry value="bcutil@default:default"/>
|
||||
<setEntry value="biz.aQute.bnd.util@default:default"/>
|
||||
<setEntry value="biz.aQute.bndlib@default:default"/>
|
||||
<setEntry value="ch.qos.logback.classic@default:default"/>
|
||||
|
@ -166,16 +167,14 @@
|
|||
<setEntry value="com.google.guava.failureaccess@default:default"/>
|
||||
<setEntry value="com.google.guava@default:default"/>
|
||||
<setEntry value="com.ibm.icu@default:default"/>
|
||||
<setEntry value="com.python.pydev.analysis@default:default"/>
|
||||
<setEntry value="com.python.pydev.debug@default:default"/>
|
||||
<setEntry value="com.python.pydev.refactoring@default:default"/>
|
||||
<setEntry value="com.python.pydev.analysis*6.3.1.201802272029@default:default"/>
|
||||
<setEntry value="com.sun.jna.platform@default:default"/>
|
||||
<setEntry value="com.sun.jna@default:default"/>
|
||||
<setEntry value="jakarta.annotation-api*1.3.5@default:default"/>
|
||||
<setEntry value="jakarta.annotation-api*2.1.1@default:default"/>
|
||||
<setEntry value="jakarta.annotation-api@default:default"/>
|
||||
<setEntry value="jakarta.inject.jakarta.inject-api*1.0.5@default:default"/>
|
||||
<setEntry value="jakarta.inject.jakarta.inject-api*2.0.1@default:default"/>
|
||||
<setEntry value="jakarta.servlet-api@default:default"/>
|
||||
<setEntry value="javax.annotation@default:default"/>
|
||||
<setEntry value="javax.xml@default:default"/>
|
||||
<setEntry value="jaxen@default:default"/>
|
||||
<setEntry value="org.apache.aries.spifly.dynamic.bundle@default:default"/>
|
||||
|
@ -204,7 +203,7 @@
|
|||
<setEntry value="org.eclipse.buildship.compat@default:default"/>
|
||||
<setEntry value="org.eclipse.buildship.core@default:default"/>
|
||||
<setEntry value="org.eclipse.buildship.ui@default:default"/>
|
||||
<setEntry value="org.eclipse.cdt.core.macosx*5.3.0.201502131403@default:default"/>
|
||||
<setEntry value="org.eclipse.cdt.core.macosx*5.3.0.201502131403@default:false"/>
|
||||
<setEntry value="org.eclipse.cdt.core.native*5.7.0.201502131403@default:default"/>
|
||||
<setEntry value="org.eclipse.cdt.core@default:default"/>
|
||||
<setEntry value="org.eclipse.cdt.ui@default:default"/>
|
||||
|
@ -219,7 +218,7 @@
|
|||
<setEntry value="org.eclipse.core.expressions@default:default"/>
|
||||
<setEntry value="org.eclipse.core.externaltools@default:default"/>
|
||||
<setEntry value="org.eclipse.core.filebuffers@default:default"/>
|
||||
<setEntry value="org.eclipse.core.filesystem.macosx@default:default"/>
|
||||
<setEntry value="org.eclipse.core.filesystem.macosx@default:false"/>
|
||||
<setEntry value="org.eclipse.core.filesystem@default:default"/>
|
||||
<setEntry value="org.eclipse.core.jobs@default:default"/>
|
||||
<setEntry value="org.eclipse.core.net@default:default"/>
|
||||
|
@ -248,7 +247,7 @@
|
|||
<setEntry value="org.eclipse.e4.ui.services@default:default"/>
|
||||
<setEntry value="org.eclipse.e4.ui.widgets@default:default"/>
|
||||
<setEntry value="org.eclipse.e4.ui.workbench.addons.swt@default:default"/>
|
||||
<setEntry value="org.eclipse.e4.ui.workbench.renderers.swt.cocoa@default:default"/>
|
||||
<setEntry value="org.eclipse.e4.ui.workbench.renderers.swt.cocoa@default:false"/>
|
||||
<setEntry value="org.eclipse.e4.ui.workbench.renderers.swt@default:default"/>
|
||||
<setEntry value="org.eclipse.e4.ui.workbench.swt@default:default"/>
|
||||
<setEntry value="org.eclipse.e4.ui.workbench3@default:default"/>
|
||||
|
@ -266,6 +265,7 @@
|
|||
<setEntry value="org.eclipse.equinox.frameworkadmin.equinox@default:default"/>
|
||||
<setEntry value="org.eclipse.equinox.frameworkadmin@default:default"/>
|
||||
<setEntry value="org.eclipse.equinox.http.jetty@default:default"/>
|
||||
<setEntry value="org.eclipse.equinox.http.service.api@default:default"/>
|
||||
<setEntry value="org.eclipse.equinox.http.servlet@default:default"/>
|
||||
<setEntry value="org.eclipse.equinox.p2.artifact.repository@default:default"/>
|
||||
<setEntry value="org.eclipse.equinox.p2.core@default:default"/>
|
||||
|
@ -285,7 +285,7 @@
|
|||
<setEntry value="org.eclipse.equinox.p2.ui@default:default"/>
|
||||
<setEntry value="org.eclipse.equinox.preferences@default:default"/>
|
||||
<setEntry value="org.eclipse.equinox.registry@default:default"/>
|
||||
<setEntry value="org.eclipse.equinox.security.macosx@default:default"/>
|
||||
<setEntry value="org.eclipse.equinox.security.macosx@default:false"/>
|
||||
<setEntry value="org.eclipse.equinox.security.ui@default:default"/>
|
||||
<setEntry value="org.eclipse.equinox.security@default:default"/>
|
||||
<setEntry value="org.eclipse.equinox.simpleconfigurator.manipulator@default:default"/>
|
||||
|
@ -293,7 +293,7 @@
|
|||
<setEntry value="org.eclipse.help.base@default:default"/>
|
||||
<setEntry value="org.eclipse.help.ui@default:default"/>
|
||||
<setEntry value="org.eclipse.help@default:default"/>
|
||||
<setEntry value="org.eclipse.jdt.annotation*2.2.800.v20231029-1039@default:default"/>
|
||||
<setEntry value="org.eclipse.jdt.annotation*2.3.0.v20240111-2306@default:default"/>
|
||||
<setEntry value="org.eclipse.jdt.core.compiler.batch@default:default"/>
|
||||
<setEntry value="org.eclipse.jdt.core.manipulation@default:default"/>
|
||||
<setEntry value="org.eclipse.jdt.core@default:default"/>
|
||||
|
@ -309,6 +309,7 @@
|
|||
<setEntry value="org.eclipse.jetty.ee8.server@default:default"/>
|
||||
<setEntry value="org.eclipse.jetty.ee8.servlet@default:default"/>
|
||||
<setEntry value="org.eclipse.jetty.ee8.webapp@default:default"/>
|
||||
<setEntry value="org.eclipse.jetty.ee@default:default"/>
|
||||
<setEntry value="org.eclipse.jetty.http@default:default"/>
|
||||
<setEntry value="org.eclipse.jetty.io@default:default"/>
|
||||
<setEntry value="org.eclipse.jetty.security@default:default"/>
|
||||
|
@ -336,21 +337,20 @@
|
|||
<setEntry value="org.eclipse.m2e.workspace.cli@default:default"/>
|
||||
<setEntry value="org.eclipse.orbit.xml-apis-ext@default:default"/>
|
||||
<setEntry value="org.eclipse.osgi.compatibility.state@default:false"/>
|
||||
<setEntry value="org.eclipse.osgi.services@default:default"/>
|
||||
<setEntry value="org.eclipse.osgi.util@default:default"/>
|
||||
<setEntry value="org.eclipse.osgi@-1:true"/>
|
||||
<setEntry value="org.eclipse.osgi@1:true"/>
|
||||
<setEntry value="org.eclipse.platform@default:default"/>
|
||||
<setEntry value="org.eclipse.rap.tools.launch.rwt@default:default"/>
|
||||
<setEntry value="org.eclipse.search.core@default:default"/>
|
||||
<setEntry value="org.eclipse.search@default:default"/>
|
||||
<setEntry value="org.eclipse.swt.cocoa.macosx.aarch64@default:default"/>
|
||||
<setEntry value="org.eclipse.swt.cocoa.macosx.aarch64@default:false"/>
|
||||
<setEntry value="org.eclipse.swt@default:default"/>
|
||||
<setEntry value="org.eclipse.team.core@default:default"/>
|
||||
<setEntry value="org.eclipse.team.ui@default:default"/>
|
||||
<setEntry value="org.eclipse.text@default:default"/>
|
||||
<setEntry value="org.eclipse.ui.browser@default:default"/>
|
||||
<setEntry value="org.eclipse.ui.cheatsheets@default:default"/>
|
||||
<setEntry value="org.eclipse.ui.cocoa@default:default"/>
|
||||
<setEntry value="org.eclipse.ui.cocoa@default:false"/>
|
||||
<setEntry value="org.eclipse.ui.console@default:default"/>
|
||||
<setEntry value="org.eclipse.ui.editors@default:default"/>
|
||||
<setEntry value="org.eclipse.ui.forms@default:default"/>
|
||||
|
@ -381,7 +381,7 @@
|
|||
<setEntry value="org.eclipse.wst.xsd.core@default:default"/>
|
||||
<setEntry value="org.eclipse.xsd@default:default"/>
|
||||
<setEntry value="org.gradle.toolingapi@default:default"/>
|
||||
<setEntry value="org.hamcrest.core@default:default"/>
|
||||
<setEntry value="org.hamcrest.core*2.2.0.v20230809-1000@default:default"/>
|
||||
<setEntry value="org.hamcrest@default:default"/>
|
||||
<setEntry value="org.jdom2@default:default"/>
|
||||
<setEntry value="org.junit@default:default"/>
|
||||
|
@ -395,6 +395,7 @@
|
|||
<setEntry value="org.osgi.service.component@default:default"/>
|
||||
<setEntry value="org.osgi.service.device@default:default"/>
|
||||
<setEntry value="org.osgi.service.event@default:default"/>
|
||||
<setEntry value="org.osgi.service.http.whiteboard@default:default"/>
|
||||
<setEntry value="org.osgi.service.metatype@default:default"/>
|
||||
<setEntry value="org.osgi.service.prefs@default:default"/>
|
||||
<setEntry value="org.osgi.service.provisioning@default:default"/>
|
||||
|
@ -407,19 +408,14 @@
|
|||
<setEntry value="org.osgi.util.position@default:default"/>
|
||||
<setEntry value="org.osgi.util.promise@default:default"/>
|
||||
<setEntry value="org.osgi.util.xml@default:default"/>
|
||||
<setEntry value="org.python.pydev.ast@default:default"/>
|
||||
<setEntry value="org.python.pydev.core@default:default"/>
|
||||
<setEntry value="org.python.pydev.customizations@default:default"/>
|
||||
<setEntry value="org.python.pydev.debug@default:default"/>
|
||||
<setEntry value="org.python.pydev.django@default:default"/>
|
||||
<setEntry value="org.python.pydev.help@default:default"/>
|
||||
<setEntry value="org.python.pydev.jython@default:default"/>
|
||||
<setEntry value="org.python.pydev.parser@default:default"/>
|
||||
<setEntry value="org.python.pydev.refactoring@default:default"/>
|
||||
<setEntry value="org.python.pydev.shared_core@default:default"/>
|
||||
<setEntry value="org.python.pydev.shared_interactive_console@default:default"/>
|
||||
<setEntry value="org.python.pydev.shared_ui@default:default"/>
|
||||
<setEntry value="org.python.pydev@default:default"/>
|
||||
<setEntry value="org.python.pydev*6.3.1.201802272029@default:default"/>
|
||||
<setEntry value="org.python.pydev.ast*6.3.1.201802272029@default:default"/>
|
||||
<setEntry value="org.python.pydev.core*6.3.1.201802272029@default:default"/>
|
||||
<setEntry value="org.python.pydev.jython*6.3.1.201802272029@default:default"/>
|
||||
<setEntry value="org.python.pydev.parser*6.3.1.201802272029@default:default"/>
|
||||
<setEntry value="org.python.pydev.shared_core*6.3.1.201802272029@default:default"/>
|
||||
<setEntry value="org.python.pydev.shared_interactive_console*6.3.1.201802272029@default:default"/>
|
||||
<setEntry value="org.python.pydev.shared_ui*6.3.1.201802272029@default:default"/>
|
||||
<setEntry value="org.sat4j.core@default:default"/>
|
||||
<setEntry value="org.sat4j.pb@default:default"/>
|
||||
<setEntry value="org.tukaani.xz@default:default"/>
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
<h1>GhidraDev README</h1>
|
||||
<p>GhidraDev provides support for developing and debugging Ghidra scripts and modules in Eclipse.
|
||||
</p>
|
||||
<p>The information provided in this document is effective as of GhidraDev 3.1.0 and is subject to
|
||||
<p>The information provided in this document is effective as of GhidraDev 4.0.0 and is subject to
|
||||
change with future releases.</p>
|
||||
|
||||
<ul>
|
||||
|
@ -54,6 +54,24 @@ change with future releases.</p>
|
|||
</ul>
|
||||
|
||||
<h2><a name="ChangeHistory"></a>Change History</h2>
|
||||
<p><u><b>4.0.0</b>:</u>
|
||||
<ul>
|
||||
<li>
|
||||
GhidraDev has been upgraded to be compatible with Ghidra 11.2 and later. Older versions of
|
||||
GhidraDev will report an error when trying to link against Ghidra 11.2 or later.
|
||||
</li>
|
||||
<li>
|
||||
GhidraDev now requires Eclipse 2023-12 4.30 or later.
|
||||
</li>
|
||||
<li>
|
||||
GhidraDev now requires JDK 21.
|
||||
</li>
|
||||
<li>
|
||||
Fixed an issue that could result in a <i>GhidraHelpService</i> exception when launching
|
||||
Ghidra. GhidraDev now properly enforces that Ghidra is only launched with <i>Utility.jar</i> on
|
||||
the initial classpath.
|
||||
</li>
|
||||
</ul>
|
||||
<p><u><b>3.1.0</b>:</u>
|
||||
<ul>
|
||||
<li>
|
||||
|
@ -176,8 +194,8 @@ that specify other projects on their build paths.</p>
|
|||
|
||||
<h2><a name="MinimumRequirements"></a>Minimum Requirements</h2>
|
||||
<ul>
|
||||
<li>Eclipse 2021-12 4.22 or later</li>
|
||||
<li>Ghidra 9.1 or later</li>
|
||||
<li>Eclipse 2023-12 4.30 or later</li>
|
||||
<li>Ghidra 11.2 or later</li>
|
||||
</ul>
|
||||
<p>(<a href="#top">Back to Top</a>)</p>
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ Manifest-Version: 1.0
|
|||
Bundle-ManifestVersion: 2
|
||||
Bundle-Name: GhidraDev
|
||||
Bundle-SymbolicName: ghidra.ghidradev;singleton:=true
|
||||
Bundle-Version: 3.1.0.qualifier
|
||||
Bundle-Version: 4.0.0.qualifier
|
||||
Bundle-Activator: ghidradev.Activator
|
||||
Require-Bundle: org.eclipse.ant.core;bundle-version="3.6.200",
|
||||
org.eclipse.buildship.core;bundle-version="3.1.5",
|
||||
|
@ -27,7 +27,7 @@ Require-Bundle: org.eclipse.ant.core;bundle-version="3.6.200",
|
|||
org.python.pydev.ast;bundle-version="[6.3.1,10.0.0)";resolution:=optional,
|
||||
org.eclipse.cdt.core;bundle-version="5.9.1";resolution:=optional,
|
||||
org.eclipse.cdt.ui;bundle-version="5.9.0";resolution:=optional
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-17
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-21
|
||||
Bundle-Vendor: Ghidra
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Bundle-ClassPath: .,
|
||||
|
|
|
@ -365,9 +365,13 @@
|
|||
<extension
|
||||
point="org.eclipse.debug.core.launchConfigurationTypes">
|
||||
<launchConfigurationType
|
||||
allowPrototypes="true"
|
||||
delegate="ghidradev.ghidraprojectcreator.launchers.GhidraLaunchDelegate"
|
||||
id="GhidraGuiLaunchConfigurationType"
|
||||
name="Ghidra">
|
||||
migrationDelegate="org.eclipse.jdt.internal.launching.JavaMigrationDelegate"
|
||||
name="Ghidra"
|
||||
sourceLocatorId="org.eclipse.jdt.launching.sourceLocator.JavaSourceLookupDirector"
|
||||
sourcePathComputerId="org.eclipse.jdt.launching.sourceLookup.javaSourcePathComputer">
|
||||
</launchConfigurationType>
|
||||
<launchConfigurationType
|
||||
delegate="ghidradev.ghidraprojectcreator.launchers.GhidraLaunchDelegate"
|
||||
|
|
|
@ -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.
|
||||
|
@ -100,6 +100,15 @@ public class GhidraLaunchDelegate extends JavaLaunchDelegate {
|
|||
// Set VM arguments
|
||||
String vmArgs = javaConfig.getLaunchProperties().getVmArgs();
|
||||
vmArgs += " " + configuration.getAttribute(GhidraLaunchUtils.ATTR_VM_ARGUMENTS, "").trim();
|
||||
vmArgs += " -Dghidra.external.modules=\"%s%s%s\"".formatted(
|
||||
javaProject.getProject().getLocation(), File.pathSeparator,
|
||||
getProjectDependencyDirs(javaProject));
|
||||
File pyDevSrcDir = PyDevUtils.getPyDevSrcDir();
|
||||
if (pyDevSrcDir != null) {
|
||||
vmArgs += " " + "-Declipse.pysrc.dir=\"" + pyDevSrcDir + "\"";
|
||||
}
|
||||
|
||||
//---------Legacy properties--------------
|
||||
vmArgs += " " + "-Declipse.install.dir=\"" +
|
||||
Platform.getInstallLocation().getURL().getFile() + "\"";
|
||||
vmArgs += " " + "-Declipse.workspace.dir=\"" +
|
||||
|
@ -107,10 +116,8 @@ public class GhidraLaunchDelegate extends JavaLaunchDelegate {
|
|||
vmArgs += " " + "-Declipse.project.dir=\"" + javaProject.getProject().getLocation() + "\"";
|
||||
vmArgs += " " + "-Declipse.project.dependencies=\"" +
|
||||
getProjectDependencyDirs(javaProject) + "\"";
|
||||
File pyDevSrcDir = PyDevUtils.getPyDevSrcDir();
|
||||
if (pyDevSrcDir != null) {
|
||||
vmArgs += " " + "-Declipse.pysrc.dir=\"" + pyDevSrcDir + "\"";
|
||||
}
|
||||
//----------------------------------------
|
||||
|
||||
wc.setAttribute(IJavaLaunchConfigurationConstants.ATTR_VM_ARGUMENTS, vmArgs);
|
||||
|
||||
// Handle special debug mode tasks
|
||||
|
|
|
@ -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.
|
||||
|
@ -22,7 +22,8 @@ import org.eclipse.core.runtime.CoreException;
|
|||
import org.eclipse.debug.core.ILaunchConfiguration;
|
||||
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
|
||||
import org.eclipse.debug.ui.*;
|
||||
import org.eclipse.jdt.debug.ui.launchConfigurations.JavaClasspathTab;
|
||||
import org.eclipse.debug.ui.sourcelookup.SourceLookupTab;
|
||||
import org.eclipse.jdt.debug.ui.launchConfigurations.JavaDependenciesTab;
|
||||
import org.eclipse.jdt.debug.ui.launchConfigurations.JavaMainTab;
|
||||
import org.eclipse.swt.SWT;
|
||||
import org.eclipse.swt.layout.GridData;
|
||||
|
@ -47,7 +48,8 @@ public class GhidraLaunchTabGroup extends AbstractLaunchConfigurationTabGroup {
|
|||
List<ILaunchConfigurationTab> tabs = new ArrayList<>();
|
||||
tabs.add(getJavaMainTab());
|
||||
tabs.add(getUserDefinedArgumentsTab());
|
||||
tabs.add(new JavaClasspathTab());
|
||||
tabs.add(getJavaDependenciesTab());
|
||||
tabs.add(getSourceLookupTab());
|
||||
tabs.add(new EnvironmentTab());
|
||||
tabs.add(getCommonTab());
|
||||
|
||||
|
@ -169,6 +171,49 @@ public class GhidraLaunchTabGroup extends AbstractLaunchConfigurationTabGroup {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link JavaDependenciesTab} to use, with all Ghidra jars removed except Utility.jar.
|
||||
*
|
||||
* @return The {@link JavaDependenciesTab} to use, with all Ghidra jars removed except
|
||||
* Utility.jar.
|
||||
*/
|
||||
private JavaDependenciesTab getJavaDependenciesTab() {
|
||||
return new JavaDependenciesTab() {
|
||||
@Override
|
||||
public void initializeFrom(ILaunchConfiguration config) {
|
||||
try {
|
||||
ILaunchConfigurationWorkingCopy wc = config.getWorkingCopy();
|
||||
GhidraLaunchUtils.setClasspath(wc);
|
||||
super.initializeFrom(wc.doSave());
|
||||
}
|
||||
catch (CoreException e) {
|
||||
EclipseMessageUtils.error("Failed to initialize the java dependencies tab.", e);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link SourceLookupTab} to use, with all Ghidra jars added.
|
||||
*
|
||||
* @return The {@link SourceLookupTab} to use, with all Ghidra jars added.
|
||||
*/
|
||||
private SourceLookupTab getSourceLookupTab() {
|
||||
return new SourceLookupTab() {
|
||||
@Override
|
||||
public void initializeFrom(ILaunchConfiguration config) {
|
||||
try {
|
||||
ILaunchConfigurationWorkingCopy wc = config.getWorkingCopy();
|
||||
GhidraLaunchUtils.setSource(wc);
|
||||
super.initializeFrom(wc.doSave());
|
||||
}
|
||||
catch (CoreException e) {
|
||||
EclipseMessageUtils.error("Failed to initialize the source lookup tab.", e);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link CommonTab} to use, with the new launch configuration added to the favorites.
|
||||
*
|
||||
|
|
|
@ -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.
|
||||
|
@ -19,13 +19,15 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.core.runtime.IPath;
|
||||
import org.eclipse.debug.core.*;
|
||||
import org.eclipse.debug.internal.ui.DebugUIPlugin;
|
||||
import org.eclipse.debug.internal.ui.launchConfigurations.LaunchConfigurationManager;
|
||||
import org.eclipse.debug.internal.ui.launchConfigurations.LaunchHistory;
|
||||
import org.eclipse.debug.ui.IDebugUIConstants;
|
||||
import org.eclipse.jdt.core.IClasspathEntry;
|
||||
import org.eclipse.jdt.core.IJavaProject;
|
||||
import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
|
||||
import org.eclipse.jdt.launching.*;
|
||||
|
||||
import ghidra.GhidraLauncher;
|
||||
|
||||
|
@ -83,6 +85,8 @@ public class GhidraLaunchUtils {
|
|||
javaProject.getProject().getName());
|
||||
setMainTypeName(wc);
|
||||
setMemory(wc, runConfigMemory);
|
||||
setClasspath(wc);
|
||||
setSource(wc);
|
||||
setFavorites(wc);
|
||||
return wc;
|
||||
}
|
||||
|
@ -168,6 +172,63 @@ public class GhidraLaunchUtils {
|
|||
return wc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all project jars from the classpath except Utility.jar.
|
||||
*
|
||||
* @param wc The launch configuration working copy to modify.
|
||||
* @return The modified working copy.
|
||||
* @throws CoreException if there was an Eclipse-related issue modifying the classpath.
|
||||
*/
|
||||
public static ILaunchConfigurationWorkingCopy setClasspath(ILaunchConfigurationWorkingCopy wc)
|
||||
throws CoreException {
|
||||
List<String> newList = new ArrayList<>();
|
||||
for (IRuntimeClasspathEntry entry : JavaRuntime.computeUnresolvedRuntimeClasspath(wc)) {
|
||||
switch (entry.getClasspathEntry().getEntryKind()) {
|
||||
case IClasspathEntry.CPE_LIBRARY:
|
||||
if (entry.getPath().toOSString().endsWith("Utility.jar")) {
|
||||
newList.add(entry.getMemento());
|
||||
}
|
||||
break;
|
||||
case IClasspathEntry.CPE_CONTAINER:
|
||||
newList.add(entry.getMemento());
|
||||
break;
|
||||
case IClasspathEntry.CPE_PROJECT:
|
||||
case IClasspathEntry.CPE_SOURCE:
|
||||
case IClasspathEntry.CPE_VARIABLE:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
wc.setAttribute(IJavaLaunchConfigurationConstants.ATTR_CLASSPATH, newList);
|
||||
wc.setAttribute(IJavaLaunchConfigurationConstants.ATTR_DEFAULT_CLASSPATH, false);
|
||||
return wc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds all project jars that have associated source to the source path
|
||||
*
|
||||
* @param wc The launch configuration working copy to modify.
|
||||
* @return The modified working copy.
|
||||
* @throws CoreException if there was an Eclipse-related issue modifying the source path.
|
||||
*/
|
||||
public static ILaunchConfigurationWorkingCopy setSource(ILaunchConfigurationWorkingCopy wc)
|
||||
throws CoreException {
|
||||
List<String> newList = new ArrayList<>();
|
||||
IJavaProject javaProject = JavaRuntime.getJavaProject(wc);
|
||||
if (javaProject != null) {
|
||||
for (IClasspathEntry entry : javaProject.getRawClasspath()) {
|
||||
IPath sourcePath = entry.getSourceAttachmentPath();
|
||||
if (sourcePath != null) {
|
||||
newList.add(
|
||||
JavaRuntime.newArchiveRuntimeClasspathEntry(sourcePath).getMemento());
|
||||
}
|
||||
}
|
||||
}
|
||||
wc.setAttribute(IJavaLaunchConfigurationConstants.ATTR_SOURCE_PATH, newList);
|
||||
wc.setAttribute(IJavaLaunchConfigurationConstants.ATTR_DEFAULT_SOURCE_PATH, false);
|
||||
return wc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the favorites attribute in the provided working copy to include the launcher in both
|
||||
* the run and debug launch groups.
|
||||
|
|
|
@ -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.
|
||||
|
@ -62,6 +62,14 @@ public class GhidraModuleUtils {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores a source folder and its corresponding output folder
|
||||
*
|
||||
* @param sourceFolder The source folder
|
||||
* @param outputFolder The output folder
|
||||
*/
|
||||
private record SourceFolderInfo(IFolder sourceFolder, IFolder outputFolder) {}
|
||||
|
||||
/**
|
||||
* Creates a new Ghidra module project with the given name.
|
||||
*
|
||||
|
@ -90,20 +98,27 @@ public class GhidraModuleUtils {
|
|||
IProject project = javaProject.getProject();
|
||||
|
||||
// Create source directories
|
||||
List<IFolder> sourceFolders = new ArrayList<>();
|
||||
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);
|
||||
List<SourceFolderInfo> sourceFolderInfos = new ArrayList<>();
|
||||
sourceFolderInfos.add(new SourceFolderInfo(project.getFolder("src/main/java"),
|
||||
project.getFolder("bin/main")));
|
||||
sourceFolderInfos.add(new SourceFolderInfo(project.getFolder("src/main/help"),
|
||||
project.getFolder("bin/main")));
|
||||
sourceFolderInfos.add(new SourceFolderInfo(project.getFolder("src/main/resources"),
|
||||
project.getFolder("bin/main")));
|
||||
sourceFolderInfos.add(new SourceFolderInfo(project.getFolder("src/test/java"),
|
||||
project.getFolder("bin/test")));
|
||||
sourceFolderInfos.add(new SourceFolderInfo(project.getFolder("ghidra_scripts"),
|
||||
project.getFolder("bin/scripts")));
|
||||
for (SourceFolderInfo sourceFolderInfo : sourceFolderInfos) {
|
||||
GhidraProjectUtils.createFolder(sourceFolderInfo.outputFolder(), monitor);
|
||||
}
|
||||
|
||||
// Put the source directories in the project's classpath
|
||||
List<IClasspathEntry> classpathEntries = new LinkedList<>();
|
||||
for (IFolder sourceFolder : sourceFolders) {
|
||||
classpathEntries.add(JavaCore.newSourceEntry(sourceFolder.getFullPath()));
|
||||
for (SourceFolderInfo sourceFolderInfo : sourceFolderInfos) {
|
||||
classpathEntries
|
||||
.add(JavaCore.newSourceEntry(sourceFolderInfo.sourceFolder().getFullPath(),
|
||||
new IPath[0], sourceFolderInfo.outputFolder().getFullPath()));
|
||||
}
|
||||
GhidraProjectUtils.addToClasspath(javaProject, classpathEntries, monitor);
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
@ -301,8 +301,8 @@ public class GhidraProjectUtils {
|
|||
// Configure Java compiler for the project
|
||||
configureJavaCompiler(javaProject, javaConfig);
|
||||
|
||||
// Setup bin folder
|
||||
IFolder binFolder = project.getFolder("bin");
|
||||
// Setup default bin folder
|
||||
IFolder binFolder = project.getFolder("bin/default");
|
||||
javaProject.setOutputLocation(binFolder.getFullPath(), monitor);
|
||||
|
||||
// Add Eclipse's built-in JUnit to classpath
|
||||
|
|
Loading…
Reference in New Issue
Block a user