diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/script/osgi/BundleCompiler.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/BundleCompiler.java similarity index 86% rename from Ghidra/Features/Base/src/main/java/ghidra/app/script/osgi/BundleCompiler.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/BundleCompiler.java index 195e3ded45..f393d7f92e 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/script/osgi/BundleCompiler.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/BundleCompiler.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.script.osgi; +package ghidra.app.plugin.core.osgi; import java.io.*; import java.nio.charset.Charset; @@ -38,33 +38,33 @@ import generic.jar.ResourceFile; import ghidra.app.script.*; public class BundleCompiler { - - public static final String GENERATED_ACTIVATOR_CLASSNAME = "GeneratedActivator"; private BundleHost bh; - public BundleCompiler(BundleHost bh) { + static final String GENERATED_ACTIVATOR_CLASSNAME = "GeneratedActivator"; + + BundleCompiler(BundleHost bh) { this.bh = bh; } - JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + private JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); /** * compile a source directory to an exploded bundle * - * @param sbi the bundle info to build + * @param sb the bundle info to build * @param writer for updating the user during compilation * @throws IOException for source/manifest file reading/generation and binary deletion/creation * @throws OSGiException if generation of bundle metadata fails */ - public void compileToExplodedBundle(SourceBundleInfo sbi, PrintWriter writer) + void compileToExplodedBundle(GhidraSourceBundle sb, PrintWriter writer) throws IOException, OSGiException { - sbi.compileAttempted(); - sbi.setSummary(String.format("build %d files, skipping %d%s", sbi.getNewSourcesCount(), - sbi.getFailingSourcesCount(), sbi.newManifestFile() ? ", new manifest" : "")); + sb.compileAttempted(); + sb.setSummary(String.format("build %d files, skipping %d%s", sb.getNewSourcesCount(), + sb.getFailingSourcesCount(), sb.newManifestFile() ? ", new manifest" : "")); - ResourceFile srcdir = sbi.getSourceDir(); - Path bindir = sbi.getBinDir(); + ResourceFile srcdir = sb.getSourceDir(); + Path bindir = sb.getBinDir(); Files.createDirectories(bindir); List options = new ArrayList<>(); @@ -79,7 +79,7 @@ public class BundleCompiler { // final JavaFileManager rfm = new ResourceFileJavaFileManager(Collections.singletonList(bi.getSourceDir())); final JavaFileManager rfm = - new ResourceFileJavaFileManager(Collections.singletonList(sbi.getSourceDir())); + new ResourceFileJavaFileManager(Collections.singletonList(sb.getSourceDir())); BundleJavaManager bjm = new BundleJavaManager(bh.getHostFramework(), rfm, options); // The phidias BundleJavaManager is for compiling from within a bundle -- it makes the @@ -88,7 +88,7 @@ public class BundleCompiler { // XXX skip this if there's a source manifest, emit warnings about @imports // get wires for currently active bundles to satisfy all requirements - List reqs = sbi.getAllReqs(); + List reqs = sb.getAllReqs(); List bundleWirings = bh.resolve(reqs); if (!reqs.isEmpty()) { @@ -98,19 +98,19 @@ public class BundleCompiler { writer.printf(" %s\n", req.toString()); } - sbi.setSummary( + sb.setSummary( String.format("%d missing @import%s:", reqs.size(), reqs.size() > 1 ? "s" : "", reqs.stream().flatMap( r -> OSGiUtils.extractPackages(r.toString()).stream()).distinct().collect( Collectors.joining(",")))); } else { - sbi.setSummary(""); + sb.setSummary(""); } // XXX add sources that will fail to call attention - List newSource = sbi.getNewSources(); + List newSource = sb.getNewSources(); for (BundleRequirement req : reqs) { - newSource.addAll(sbi.req2file.get(req.toString())); + newSource.addAll(sb.req2file.get(req.toString())); } // send the capabilities to phidias @@ -142,7 +142,7 @@ public class BundleCompiler { writer.write(err); ResourceFileJavaFileObject sf = (ResourceFileJavaFileObject) d.getSource(); ResourceFile rf = sf.getFile(); - sbi.buildError(rf, err); // remember all errors for this file + sb.buildError(rf, err); // remember all errors for this file if (sourceFiles.remove(sf)) { writer.printf("skipping %s\n", sf.toString()); // if it's a script, mark it for having compile errors @@ -162,9 +162,8 @@ public class BundleCompiler { } } // buildErrors is now up to date, set status - if (sbi.getFailingSourcesCount() > 0) { - sbi.appendSummary( - String.format("%d failing source files", sbi.getFailingSourcesCount())); + if (sb.getFailingSourcesCount() > 0) { + sb.appendSummary(String.format("%d failing source files", sb.getFailingSourcesCount())); } ResourceFile smf = new ResourceFile(srcdir, "META-INF" + File.separator + "MANIFEST.MF"); @@ -203,7 +202,7 @@ public class BundleCompiler { manifest = analyzer.calcManifest(); } catch (Exception e) { - sbi.appendSummary("bad manifest"); + sb.appendSummary("bad manifest"); throw new OSGiException("failed to calculate manifest by analyzing code", e); } Attributes ma = manifest.getMainAttributes(); @@ -219,22 +218,24 @@ public class BundleCompiler { } } catch (Exception e) { - sbi.appendSummary("failed bnd analysis"); + sb.appendSummary("failed bnd analysis"); throw new OSGiException("failed to query classes while searching for activator", e); } if (activator_classname == null) { activator_classname = GENERATED_ACTIVATOR_CLASSNAME; if (!buildDefaultActivator(bindir, activator_classname, writer)) { - sbi.appendSummary("failed to build generated activator"); + sb.appendSummary("failed to build generated activator"); return; } // since we add the activator after bndtools built the imports, we should add its imports too String imps = ma.getValue(Constants.IMPORT_PACKAGE); if (imps == null) { - ma.putValue(Constants.IMPORT_PACKAGE, "ghidra.app.script.osgi"); + ma.putValue(Constants.IMPORT_PACKAGE, + GhidraBundleActivator.class.getPackageName()); } else { - ma.putValue(Constants.IMPORT_PACKAGE, imps + ",ghidra.app.script.osgi"); + ma.putValue(Constants.IMPORT_PACKAGE, + imps + "," + GhidraBundleActivator.class.getPackageName()); } } ma.putValue(Constants.BUNDLE_ACTIVATOR, activator_classname); @@ -265,7 +266,7 @@ public class BundleCompiler { try (PrintWriter out = new PrintWriter(Files.newBufferedWriter(activator_dest, Charset.forName("UTF-8")))) { - out.println("import ghidra.app.script.osgi.GhidraBundleActivator;"); + out.println("import " + GhidraBundleActivator.class.getName() + ";"); out.println("import org.osgi.framework.BundleActivator;"); out.println("import org.osgi.framework.BundleContext;"); out.println("public class " + GENERATED_ACTIVATOR_CLASSNAME + diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/script/osgi/BundleHost.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/BundleHost.java similarity index 84% rename from Ghidra/Features/Base/src/main/java/ghidra/app/script/osgi/BundleHost.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/BundleHost.java index 24e926970c..dd699d95e1 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/script/osgi/BundleHost.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/BundleHost.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.script.osgi; +package ghidra.app.plugin.core.osgi; import static java.util.stream.Collectors.*; @@ -35,15 +35,13 @@ import org.osgi.framework.launch.Framework; import org.osgi.framework.wiring.*; import org.osgi.service.log.*; -import generic.io.NullPrintWriter; import generic.jar.ResourceFile; import ghidra.app.script.GhidraScriptUtil; import ghidra.util.exception.CancelledException; import ghidra.util.task.*; -// XXX this class should be part of a service/plugin public class BundleHost { - // XXX embedded OSGi should be a service, but ScriptProviders don't have any way to access services + // XXX ScriptProviders don't have any way to access services or other way to access system wide resources static private BundleHost _instance; static public BundleHost getInstance() { @@ -69,16 +67,28 @@ public class BundleHost { } } - // XXX this should be remembered in bundlehosts's savestate - HashMap file2sbi = new HashMap<>(); + // XXX this should be remembered by bundlehosts's savestate + HashMap file2sbi = new HashMap<>(); - public SourceBundleInfo getSourceBundleInfo(ResourceFile sourceDir) { - return file2sbi.computeIfAbsent(sourceDir, sd -> new SourceBundleInfo(this, sd)); + public GhidraBundle getGhidraBundle(ResourceFile path) { + return file2sbi.computeIfAbsent(path, this::createGhidraBundle); + } + + public GhidraBundle createGhidraBundle(ResourceFile path) { + switch (GhidraBundle.getType(path)) { + case SourceDir: + return new GhidraSourceBundle(this, path); + case BndScript: + case Jar: + default: + break; + } + return null; } // XXX consumers must clean up after themselves - public void removeSourceBundleInfo(ResourceFile sourceDir) { - file2sbi.remove(sourceDir); + public void removeSourceBundleInfo(ResourceFile path) { + file2sbi.remove(path); } /** @@ -86,21 +96,16 @@ public class BundleHost { * * @param imports Import-Package value * @return deduced requirements or null if there was an error - * @throws OSGiException on parse failure + * @throws BundleException on parse failure */ - static List parseImports(String imports) throws OSGiException { + static List parseImports(String imports) throws BundleException { // parse it with Felix's ManifestParser to a list of BundleRequirement objects Map headerMap = new HashMap<>(); headerMap.put(Constants.IMPORT_PACKAGE, imports); ManifestParser mp; - try { - mp = new ManifestParser(null, null, null, headerMap); - return mp.getRequirements(); - } - catch (org.osgi.framework.BundleException e) { - throw new OSGiException("parsing Import-Package: " + imports, e); - } + mp = new ManifestParser(null, null, null, headerMap); + return mp.getRequirements(); } /** @@ -415,6 +420,14 @@ public class BundleHost { } } + public void deactivateSynchronously(String bundleLoc) + throws GhidraBundleException, InterruptedException { + Bundle b = getBundle(bundleLoc); + if (b != null) { + deactivateSynchronously(b); + } + } + void forceStopFelix() { Task t = new Task("killing felix", false, false, true, true) { @Override @@ -568,76 +581,9 @@ public class BundleHost { } } - /** - * compile a source bundle if it's out of sync - * - * @param sbi the bundle info - * @param writer where to write issues - * @throws OSGiException if bundle operations fail - * @throws IOException if there are issues with the contents of the bundle - * @return the activated bundle - * @throws InterruptedException if interrupted while waiting for bundle state change - */ - public boolean compileSourceBundle(SourceBundleInfo sbi, PrintWriter writer) - throws OSGiException, IOException, InterruptedException { - if (writer == null) { - writer = new NullPrintWriter(); - } - - boolean needsCompile = false; - - sbi.updateFromFilesystem(writer); - - int failing = sbi.getFailingSourcesCount(); - int newSourcecount = sbi.getNewSourcesCount(); - - long lastBundleActivation = 0; // XXX record last bundle activation in bundlestatusmodel - if (failing > 0 && (lastBundleActivation > sbi.getLastCompileAttempt())) { - needsCompile = true; - } - - if (newSourcecount == 0) { - if (failing > 0) { - writer.printf("%s hasn't changed, with %d file%s failing in previous build(s):\n", - sbi.getSourceDir().toString(), failing, failing > 1 ? "s" : ""); - writer.printf("%s\n", sbi.getPreviousBuildErrors()); - } - if (sbi.newManifestFile()) { - needsCompile = true; - } - } - else { - needsCompile = true; - } - - if (needsCompile) { - writer.printf("%d new files, %d skipped, %s\n", newSourcecount, failing, - sbi.newManifestFile() ? ", new manifest" : ""); - - // if there a bundle is currently active, uninstall it - Bundle b = sbi.getBundle(); - if (b != null) { - deactivateSynchronously(b); - } - - // once we've committed to recompile and regenerate generated classes, delete the old stuff - sbi.deleteOldBinaries(); - - BundleCompiler bundleCompiler = new BundleCompiler(this); - - long startTime = System.nanoTime(); - bundleCompiler.compileToExplodedBundle(sbi, writer); - long endTime = System.nanoTime(); - writer.printf("%3.2f seconds compile time.\n", (endTime - startTime) / 1e9); - fireSourceBundleCompiled(sbi); - return true; - } - return false; - } - List osgiListeners = new ArrayList<>(); - void fireSourceBundleCompiled(SourceBundleInfo sbi) { + void fireSourceBundleCompiled(GhidraSourceBundle sbi) { synchronized (osgiListeners) { for (OSGiListener l : osgiListeners) { l.sourceBundleCompiled(sbi); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/osgi/BundlePath.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/BundleStatus.java similarity index 55% rename from Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/osgi/BundlePath.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/BundleStatus.java index 6ecd38508d..894ae00268 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/osgi/BundlePath.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/BundleStatus.java @@ -13,67 +13,33 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.plugin.core.script.osgi; - -import java.io.File; +package ghidra.app.plugin.core.osgi; import generic.jar.ResourceFile; import generic.util.Path; /** - * The BundlePath class represents the runtime state and user preferences for OSGi bundles in Ghidra. + * The BundleStatus class represents the runtime state and user preferences for OSGi bundles in Ghidra. */ -public class BundlePath extends Path { - final Type type; +public class BundleStatus extends Path { + final GhidraBundle.Type type; boolean active = false; boolean busy = false; String summary; - public static enum Type { - BndScript, Jar, SourceDir, INVALID - } - - static public Type getType(File f) { - if (f.isDirectory()) { - return Type.SourceDir; - } - String n = f.getName().toLowerCase(); - if (n.endsWith(".bnd")) { - return Type.BndScript; - } - if (n.endsWith(".jar")) { - return Type.Jar; - } - return Type.INVALID; - } - - static public Type getType(ResourceFile rf) { - if (rf.isDirectory()) { - return Type.SourceDir; - } - String n = rf.getName().toLowerCase(); - if (n.endsWith(".bnd")) { - return Type.BndScript; - } - if (n.endsWith(".jar")) { - return Type.Jar; - } - return Type.INVALID; - } - - public Type getType() { + public GhidraBundle.Type getType() { return type; } - BundlePath(String path, boolean enabled, boolean readonly) { + BundleStatus(String path, boolean enabled, boolean readonly) { super(path, enabled, false /*editable */, readonly); - type = getType(getPath()); + type = GhidraBundle.getType(getPath()); } - BundlePath(ResourceFile path, boolean enabled, boolean readonly) { + BundleStatus(ResourceFile path, boolean enabled, boolean readonly) { super(path, enabled, false /* editable */, readonly); - type = getType(getPath()); + type = GhidraBundle.getType(getPath()); } public boolean isDirectory() { @@ -109,4 +75,8 @@ public class BundlePath extends Path { return summary; } + public boolean pathExists() { + return exists(); + } + } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/osgi/BundleStatusListener.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/BundleStatusListener.java similarity index 71% rename from Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/osgi/BundleStatusListener.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/BundleStatusListener.java index f84e96181d..67eb3ee9af 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/osgi/BundleStatusListener.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/BundleStatusListener.java @@ -14,16 +14,23 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.plugin.core.script.osgi; +package ghidra.app.plugin.core.osgi; public interface BundleStatusListener { /** * Called when the list of bundle paths changes */ - public void bundlesChanged(); + default public void bundlesChanged() { + // + } + + default public void bundleEnablementChanged(BundleStatus status, boolean newValue) { + // + } + + default public void bundleActivationChanged(BundleStatus status, boolean newValue) { + // + } - public void bundleEnablementChanged(BundlePath path, boolean newValue); - public void bundleActivationChanged(BundlePath path, boolean newValue); - } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/osgi/BundleStatusModel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/BundleStatusModel.java similarity index 57% rename from Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/osgi/BundleStatusModel.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/BundleStatusModel.java index 4628374ffa..7aa7f9b6fa 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/osgi/BundleStatusModel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/BundleStatusModel.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.plugin.core.script.osgi; +package ghidra.app.plugin.core.osgi; import java.io.File; import java.util.*; @@ -25,12 +25,11 @@ import org.osgi.framework.Bundle; import docking.widgets.table.AbstractSortedTableModel; import generic.jar.ResourceFile; import ghidra.app.script.GhidraScriptUtil; -import ghidra.app.script.osgi.*; import ghidra.framework.options.SaveState; import ghidra.framework.preferences.Preferences; import ghidra.util.Msg; -public class BundleStatusModel extends AbstractSortedTableModel { +public class BundleStatusModel extends AbstractSortedTableModel { List columns = new ArrayList<>(); class Column { @@ -45,15 +44,15 @@ public class BundleStatusModel extends AbstractSortedTableModel { this.clazz = clazz; } - boolean editable(BundlePath path) { + boolean editable(BundleStatus status) { return false; } - Object getValue(BundlePath path) { + Object getValue(BundleStatus status) { return null; } - void setValue(BundlePath path, Object aValue) { + void setValue(BundleStatus status, Object aValue) { throw new RuntimeException(name + " is not editable!"); } @@ -61,55 +60,55 @@ public class BundleStatusModel extends AbstractSortedTableModel { Column enabledColumn = new Column("Enabled", Boolean.class) { @Override - boolean editable(BundlePath path) { - return path.exists(); + boolean editable(BundleStatus status) { + return status.pathExists(); } @Override - Object getValue(BundlePath path) { - return path.isEnabled(); + Object getValue(BundleStatus status) { + return status.isEnabled(); } @Override - void setValue(BundlePath path, Object newValue) { - path.setEnabled((Boolean) newValue); - fireBundleEnablementChanged(path, (Boolean) newValue); + void setValue(BundleStatus status, Object newValue) { + status.setEnabled((Boolean) newValue); + fireBundleEnablementChanged(status, (Boolean) newValue); } }; Column activeColumn = new Column("Active", Boolean.class) { @Override - boolean editable(BundlePath path) { - return path.exists(); // XXX maybe only if it's already enabled + boolean editable(BundleStatus status) { + return status.pathExists(); // XXX maybe only if it's already enabled } @Override - Object getValue(BundlePath path) { - return path.isActive(); + Object getValue(BundleStatus status) { + return status.isActive(); } @Override - void setValue(BundlePath path, Object newValue) { - path.setActive((Boolean) newValue); - fireBundleActivationChanged(path, (Boolean) newValue); + void setValue(BundleStatus status, Object newValue) { + status.setActive((Boolean) newValue); + fireBundleActivationChanged(status, (Boolean) newValue); } }; Column typeColumn = new Column("Type", String.class) { @Override - Object getValue(BundlePath path) { - return path.getType().toString(); + Object getValue(BundleStatus status) { + return status.getType().toString(); } }; - Column pathColumn = new Column("Path", BundlePath.class) { + Column pathColumn = new Column("Path", String.class) { @Override - Object getValue(BundlePath path) { - return path; + Object getValue(BundleStatus status) { + return status.getPath().toString(); } }; - Column summaryColumn = new Column("Summary", BundlePath.class) { + Column summaryColumn = new Column("Summary", String.class) { @Override - Object getValue(BundlePath path) { - return path.getSummary(); + Object getValue(BundleStatus status) { + return status.getSummary(); } }; @@ -127,41 +126,29 @@ public class BundleStatusModel extends AbstractSortedTableModel { } private BundleStatusProvider provider; - private List paths; + private List statuses; private BundleHost bundleHost; OSGiListener bundleListener; - private Map loc2bp = new HashMap<>(); + private Map loc2status = new HashMap<>(); - BundlePath getPath(String bundleLocation) { - return loc2bp.get(bundleLocation); + BundleStatus getStatus(String bundleLocation) { + return loc2status.get(bundleLocation); } - public String getBundleLoc(BundlePath bp) { - switch (bp.getType()) { - case Jar: - return bp.getPath().getAbsolutePath(); - case SourceDir: - SourceBundleInfo bi = bundleHost.getSourceBundleInfo(bp.getPath()); - return bi.getBundleLoc(); - case BndScript: - // XXX - case INVALID: - default: - break; - } - return null; + public String getBundleLoc(BundleStatus status) { + return bundleHost.getGhidraBundle(status.getPath()).getBundleLoc(); } /** * (re)compute cached mapping from bundleloc to bundlepath */ private void computeCache() { - loc2bp.clear(); - for (BundlePath bp : paths) { - String loc = getBundleLoc(bp); + loc2status.clear(); + for (BundleStatus status : statuses) { + String loc = getBundleLoc(status); if (loc != null) { - loc2bp.put(loc, bp); + loc2status.put(loc, status); } } } @@ -172,18 +159,20 @@ public class BundleStatusModel extends AbstractSortedTableModel { this.bundleHost = bundleHost; // add unmodifiable paths - this.paths = GhidraScriptUtil.getSystemScriptPaths().stream().distinct().map( - f -> new BundlePath(f, true, true)).collect(Collectors.toList()); + this.statuses = GhidraScriptUtil.getSystemScriptPaths().stream().distinct().map( + f -> new BundleStatus(f, true, true)).collect(Collectors.toList()); // add user path - this.paths.add(0, new BundlePath(GhidraScriptUtil.getUserScriptDirectory(), true, false)); + this.statuses.add(0, + new BundleStatus(GhidraScriptUtil.getUserScriptDirectory(), true, false)); + computeCache(); bundleHost.addListener(bundleListener = new OSGiListener() { @Override - public void sourceBundleCompiled(SourceBundleInfo sbi) { - BundlePath bp = getPath(sbi.getBundleLoc()); + public void sourceBundleCompiled(GhidraSourceBundle sb) { + BundleStatus bp = getStatus(sb.getBundleLoc()); if (bp != null) { - bp.setSummary(sbi.getSummary()); + bp.setSummary(sb.getSummary()); int row = getRowIndex(bp); fireTableRowsUpdated(row, row); } @@ -191,7 +180,7 @@ public class BundleStatusModel extends AbstractSortedTableModel { @Override public void bundleActivationChange(Bundle b, boolean newActivation) { - BundlePath bp = getPath(b.getLocation()); + BundleStatus bp = getStatus(b.getLocation()); if (newActivation) { if (bp != null) { bp.setActive(true); @@ -219,71 +208,70 @@ public class BundleStatusModel extends AbstractSortedTableModel { bundleHost.removeListener(bundleListener); } - void clear() { - paths.clear(); - } - - List getAllPaths() { - return new ArrayList(paths); - } - - public List getPaths() { + public List getEnabledPaths() { List list = new ArrayList<>(); - for (BundlePath path : paths) { - if (path.isEnabled()) { - list.add(path.getPath()); + for (BundleStatus status : statuses) { + if (status.isEnabled()) { + list.add(status.getPath()); } } return list; } - private void addPath(BundlePath path) { - if (paths.contains(path)) { + private void addStatus(BundleStatus path) { + if (statuses.contains(path)) { return; } String loc = getBundleLoc(path); if (loc != null) { - loc2bp.put(loc, path); + loc2status.put(loc, path); } - int index = paths.size(); - paths.add(path); + int index = statuses.size(); + statuses.add(path); fireTableRowsInserted(index, index); } - private BundlePath addNewPath(ResourceFile path, boolean enabled, boolean readonly) { - BundlePath p = new BundlePath(path, enabled, readonly); - addPath(p); + private BundleStatus addNewStatus(ResourceFile path, boolean enabled, boolean readonly) { + BundleStatus p = new BundleStatus(path, enabled, readonly); + addStatus(p); return p; } - private BundlePath addNewPath(String path, boolean enabled, boolean readonly) { - BundlePath p = new BundlePath(path, enabled, readonly); - addPath(p); + private BundleStatus addNewStatus(String path, boolean enabled, boolean readonly) { + BundleStatus p = new BundleStatus(path, enabled, readonly); + addStatus(p); return p; } + /** + * create new BundleStatus objects for each of the given files + * + * @param files the files.. given... + * @param enabled mark them all as enabled + * @param readonly mark them all as readonly + */ void addNewPaths(List files, boolean enabled, boolean readonly) { for (File f : files) { - BundlePath p = new BundlePath(new ResourceFile(f), enabled, readonly); - addPath(p); + BundleStatus status = new BundleStatus(new ResourceFile(f), enabled, readonly); + addStatus(status); } fireBundlesChanged(); } void remove(int[] selectedRows) { - List list = new ArrayList<>(); + List toRemove = new ArrayList<>(); for (int selectedRow : selectedRows) { - list.add(paths.get(selectedRow)); + toRemove.add(statuses.get(selectedRow)); } - for (BundlePath path : list) { - if (!path.isReadOnly()) { - paths.remove(path); - loc2bp.remove(getBundleLoc(path)); + for (BundleStatus status : toRemove) { + if (!status.isReadOnly()) { + statuses.remove(status); + loc2status.remove(getBundleLoc(status)); } else { Msg.showInfo(this, this.provider.getComponent(), "Unabled to remove path", - "System path cannot be removed: " + path.toString()); + "System path cannot be removed: " + status.toString()); } } fireTableDataChanged(); @@ -299,7 +287,7 @@ public class BundleStatusModel extends AbstractSortedTableModel { @Override public int getRowCount() { - return paths.size(); + return statuses.size(); } @Override @@ -309,8 +297,8 @@ public class BundleStatusModel extends AbstractSortedTableModel { @Override public boolean isCellEditable(int rowIndex, int columnIndex) { - BundlePath path = paths.get(rowIndex); - return getColumn(columnIndex).editable(path); + BundleStatus status = statuses.get(rowIndex); + return getColumn(columnIndex).editable(status); } @Override @@ -320,15 +308,15 @@ public class BundleStatusModel extends AbstractSortedTableModel { @Override public void setValueAt(Object aValue, int rowIndex, int columnIndex) { - BundlePath path = paths.get(rowIndex); - getColumn(columnIndex).setValue(path, aValue); - // XXX: preclude RowObjectSelectionManager.repairSelection + BundleStatus status = statuses.get(rowIndex); + getColumn(columnIndex).setValue(status, aValue); + // XXX: avoid RowObjectSelectionManager.repairSelection by selecting the row we're editing provider.selectRow(rowIndex); } @Override - public Object getColumnValueForRow(BundlePath path, int columnIndex) { - return getColumn(columnIndex).getValue(path); + public Object getColumnValueForRow(BundleStatus status, int columnIndex) { + return getColumn(columnIndex).getValue(status); } @Override @@ -338,12 +326,12 @@ public class BundleStatusModel extends AbstractSortedTableModel { @Override public String getName() { - return "BundlePathManagerModel"; + return BundleStatusModel.class.getSimpleName(); } @Override - public List getModelData() { - return paths; + public List getModelData() { + return statuses; } /** @@ -353,7 +341,7 @@ public class BundleStatusModel extends AbstractSortedTableModel { */ public boolean enablePath(ResourceFile file) { ResourceFile dir = file.isDirectory() ? file : file.getParentFile(); - for (BundlePath path : getAllPaths()) { + for (BundleStatus path : statuses) { if (path.getPath().equals(dir)) { if (!path.isEnabled()) { path.setEnabled(true); @@ -364,7 +352,7 @@ public class BundleStatusModel extends AbstractSortedTableModel { return false; } } - addNewPath(dir, true, false); + addNewStatus(dir, true, false); Preferences.setProperty(BundleStatusProvider.preferenceForLastSelectedBundle, dir.getAbsolutePath()); fireBundlesChanged(); @@ -377,8 +365,8 @@ public class BundleStatusModel extends AbstractSortedTableModel { * @return true if the bundle is managed and not marked readonly */ public boolean isWriteable(ResourceFile bundle) { - Optional o = paths.stream().filter( - bp -> bp.isDirectory() && bp.getPath().equals(bundle)).findFirst(); + Optional o = statuses.stream().filter( + status -> status.isDirectory() && status.getPath().equals(bundle)).findFirst(); return o.isPresent() && !o.get().isReadOnly(); } @@ -387,10 +375,10 @@ public class BundleStatusModel extends AbstractSortedTableModel { * * each path is marked editable and non-readonly * - * @param testingPaths the paths to use + * @param testingPaths the statuses to use */ public void setPathsForTesting(List testingPaths) { - this.paths = testingPaths.stream().map(f -> new BundlePath(f, true, false)).collect( + this.statuses = testingPaths.stream().map(f -> new BundleStatus(f, true, false)).collect( Collectors.toList()); computeCache(); fireTableDataChanged(); @@ -403,7 +391,7 @@ public class BundleStatusModel extends AbstractSortedTableModel { * @param path the path to insert */ public void insertPathForTesting(String path) { - addNewPath(path, true, false); + addNewStatus(path, true, false); } private ArrayList listeners = new ArrayList<>(); @@ -430,7 +418,7 @@ public class BundleStatusModel extends AbstractSortedTableModel { } } - void fireBundleEnablementChanged(BundlePath path, boolean newValue) { + void fireBundleEnablementChanged(BundleStatus path, boolean newValue) { synchronized (listeners) { for (BundleStatusListener listener : listeners) { listener.bundleEnablementChanged(path, newValue); @@ -438,7 +426,7 @@ public class BundleStatusModel extends AbstractSortedTableModel { } } - void fireBundleActivationChanged(BundlePath path, boolean newValue) { + void fireBundleActivationChanged(BundleStatus path, boolean newValue) { synchronized (listeners) { for (BundleStatusListener listener : listeners) { listener.bundleActivationChanged(path, newValue); @@ -447,7 +435,7 @@ public class BundleStatusModel extends AbstractSortedTableModel { } /** - * Restores the paths from the specified SaveState object. + * Restores the statuses from the specified SaveState object. * @param ss the SaveState object */ public void restoreState(SaveState ss) { @@ -460,49 +448,47 @@ public class BundleStatusModel extends AbstractSortedTableModel { boolean[] enableArr = ss.getBooleans("BundleStatus_ENABLE", new boolean[pathArr.length]); boolean[] readonlyArr = ss.getBooleans("BundleStatus_READ", new boolean[pathArr.length]); - List currentPaths = getAllPaths(); - clear(); + List currentPaths = new ArrayList<>(statuses); + statuses.clear(); for (int i = 0; i < pathArr.length; i++) { - BundlePath currentPath = getPath(pathArr[i], currentPaths); - if (currentPath != null) { - currentPaths.remove(currentPath); - addNewPath(pathArr[i], enableArr[i], readonlyArr[i]); + BundleStatus currentStatus = getStatus(pathArr[i], currentPaths); + if (currentStatus != null) { + currentPaths.remove(currentStatus); + addNewStatus(pathArr[i], enableArr[i], readonlyArr[i]); } else if (!readonlyArr[i]) { - // skip read-only paths which are not present in the current config + // skip read-only statuses which are not present in the current config // This is needed to thin-out old default entries - addNewPath(pathArr[i], enableArr[i], readonlyArr[i]); + addNewStatus(pathArr[i], enableArr[i], readonlyArr[i]); } } fireBundlesChanged(); } - private static BundlePath getPath(String filepath, List paths) { - for (BundlePath path : paths) { - if (filepath.equals(path.getPathAsString())) { - return path; + private static BundleStatus getStatus(String filepath, List statuses) { + for (BundleStatus status : statuses) { + if (filepath.equals(status.getPathAsString())) { + return status; } } return null; } /** - * Saves the paths to the specified SaveState object. + * Saves the statuses to the specified SaveState object. * @param ss the SaveState object */ public void saveState(SaveState ss) { - List currentPaths = getAllPaths(); - - String[] pathArr = new String[currentPaths.size()]; - boolean[] enableArr = new boolean[currentPaths.size()]; - boolean[] readonlyArr = new boolean[currentPaths.size()]; + String[] pathArr = new String[statuses.size()]; + boolean[] enableArr = new boolean[statuses.size()]; + boolean[] readonlyArr = new boolean[statuses.size()]; int index = 0; - for (BundlePath path : currentPaths) { - pathArr[index] = path.getPathAsString(); - enableArr[index] = path.isEnabled(); - readonlyArr[index] = path.isReadOnly(); + for (BundleStatus status : statuses) { + pathArr[index] = status.getPathAsString(); + enableArr[index] = status.isEnabled(); + readonlyArr[index] = status.isReadOnly(); ++index; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/osgi/BundleStatusProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/BundleStatusProvider.java similarity index 78% rename from Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/osgi/BundleStatusProvider.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/BundleStatusProvider.java index 3288c9fd96..e6d7cd0191 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/osgi/BundleStatusProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/BundleStatusProvider.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.plugin.core.script.osgi; +package ghidra.app.plugin.core.osgi; import java.awt.*; import java.io.File; @@ -27,12 +27,15 @@ import javax.swing.table.TableColumn; import docking.widgets.filechooser.GhidraFileChooser; import docking.widgets.filechooser.GhidraFileChooserMode; import docking.widgets.table.*; -import ghidra.app.script.osgi.BundleHost; +import ghidra.app.services.ConsoleService; import ghidra.framework.plugintool.ComponentProviderAdapter; import ghidra.framework.plugintool.PluginTool; import ghidra.framework.preferences.Preferences; +import ghidra.util.Msg; +import ghidra.util.exception.CancelledException; import ghidra.util.filechooser.GhidraFileChooserModel; import ghidra.util.filechooser.GhidraFileFilter; +import ghidra.util.task.*; import resources.ResourceManager; /** @@ -49,14 +52,29 @@ public class BundleStatusProvider extends ComponentProviderAdapter { private Color selectionColor; private GhidraFileChooser fileChooser; private GhidraFileFilter filter; + private final BundleHost bundleHost; public void notifyTableChanged() { bundleStatusTable.notifyTableChanged(new TableModelEvent(bundleStatusModel)); } - public BundleStatusProvider(PluginTool tool, String owner, BundleHost bundleHost) { + public BundleStatusProvider(PluginTool tool, String owner) { super(tool, "Bundle Status Manager", owner); + this.bundleHost = BundleHost.getInstance(); this.bundleStatusModel = new BundleStatusModel(this, bundleHost); + bundleStatusModel.addListener(new BundleStatusListener() { + @Override + public void bundleEnablementChanged(BundleStatus status, boolean enabled) { + if (!enabled && status.isActive()) { + startActivateDeactiveTask(status, false); + } + } + + @Override + public void bundleActivationChanged(BundleStatus status, boolean newValue) { + startActivateDeactiveTask(status, newValue); + } + }); this.filter = new GhidraFileFilter() { @Override @@ -66,7 +84,7 @@ public class BundleStatusProvider extends ComponentProviderAdapter { @Override public boolean accept(File path, GhidraFileChooserModel model) { - return BundlePath.getType(path) != BundlePath.Type.INVALID; + return GhidraBundle.getType(path) != GhidraBundle.Type.INVALID; } }; this.fileChooser = null; @@ -108,7 +126,7 @@ public class BundleStatusProvider extends ComponentProviderAdapter { buttonPanel.add(removeButton, gbc); bundleStatusTable = new GTable(bundleStatusModel); - bundleStatusTable.setName("BUNDLEPATH_TABLE"); + bundleStatusTable.setName("BUNDLESTATUS_TABLE"); bundleStatusTable.setSelectionBackground(selectionColor); bundleStatusTable.setSelectionForeground(Color.BLACK); bundleStatusTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); @@ -133,9 +151,9 @@ public class BundleStatusProvider extends ComponentProviderAdapter { column.setCellRenderer(new GBooleanCellRenderer() { @Override public Component getTableCellRendererComponent(GTableCellRenderingData data) { - BundlePath path = (BundlePath) data.getRowObject(); + BundleStatus status = (BundleStatus) data.getRowObject(); Component x = super.getTableCellRendererComponent(data); - if (path.getBusy()) { + if (status.getBusy()) { cb.setVisible(false); cb.setEnabled(false); setHorizontalAlignment(SwingConstants.CENTER); @@ -156,7 +174,7 @@ public class BundleStatusProvider extends ComponentProviderAdapter { FontMetrics fontmetrics = panel.getFontMetrics(panel.getFont()); column.setMaxWidth(10 + - SwingUtilities.computeStringWidth(fontmetrics, BundlePath.Type.SourceDir.toString())); + SwingUtilities.computeStringWidth(fontmetrics, GhidraBundle.Type.SourceDir.toString())); column = bundleStatusTable.getColumnModel().getColumn(bundleStatusModel.pathColumn.index); column.setCellRenderer(new GTableCellRenderer() { @@ -164,15 +182,15 @@ public class BundleStatusProvider extends ComponentProviderAdapter { public Component getTableCellRendererComponent(GTableCellRenderingData data) { JLabel c = (JLabel) super.getTableCellRendererComponent(data); - BundlePath path = (BundlePath) data.getValue(); - if (!path.exists()) { + BundleStatus status = (BundleStatus) data.getValue(); + if (!status.pathExists()) { c.setForeground(Color.RED); } return c; } }); - GTableFilterPanel filterPanel = + GTableFilterPanel filterPanel = new GTableFilterPanel<>(bundleStatusTable, bundleStatusModel); JScrollPane scrollPane = new JScrollPane(bundleStatusTable); @@ -272,6 +290,39 @@ public class BundleStatusProvider extends ComponentProviderAdapter { bundleStatusTable.dispose(); } + private void startActivateDeactiveTask(BundleStatus status, boolean activate) { + status.setBusy(true); + notifyTableChanged(); + ConsoleService console = getTool().getService(ConsoleService.class); + + new TaskLauncher(new Task((activate ? "Activating" : "Deactivating ") + " bundle...") { + @Override + public void run(TaskMonitor monitor) throws CancelledException { + try { + GhidraBundle sb = bundleHost.getGhidraBundle(status.getPath()); + if (activate) { + sb.build(console.getStdErr()); + bundleHost.activateSynchronously(sb.getBundleLoc()); + } + else { // deactivate + bundleHost.deactivateSynchronously(sb.getBundleLoc()); + } + } + catch (Exception e) { + e.printStackTrace(console.getStdErr()); + status.setActive(!activate); + + Msg.showError(this, getComponent(), "bundle activation failed", e.getMessage()); + } + finally { + status.setBusy(false); + notifyTableChanged(); + } + } + }, null, 1000); + } + + // XXX workaround for RowObjectSelection.. repair void selectRow(int rowIndex) { bundleStatusTable.selectRow(rowIndex); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/GhidraBundle.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/GhidraBundle.java new file mode 100644 index 0000000000..074bb20700 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/GhidraBundle.java @@ -0,0 +1,61 @@ +package ghidra.app.plugin.core.osgi; + +import java.io.File; +import java.io.PrintWriter; + +import org.osgi.framework.Bundle; + +import generic.jar.ResourceFile; + +public interface GhidraBundle { + + enum Type { + BndScript, Jar, SourceDir, INVALID + } + + /** + * attempt to build loadable bundle, if possible + * + * @param writer console for user messages + * @return true if build was successful + * @throws Exception XXX + */ + boolean build(PrintWriter writer) throws Exception; + + String getBundleLoc(); + + Bundle getBundle() throws GhidraBundleException; + + Bundle install() throws GhidraBundleException; + + String getSummary(); + + static GhidraBundle.Type getType(ResourceFile rf) { + if (rf.isDirectory()) { + return GhidraBundle.Type.SourceDir; + } + String n = rf.getName().toLowerCase(); + if (n.endsWith(".bnd")) { + return GhidraBundle.Type.BndScript; + } + if (n.endsWith(".jar")) { + return GhidraBundle.Type.Jar; + } + return GhidraBundle.Type.INVALID; + } + + static public GhidraBundle.Type getType(File f) { + if (f.isDirectory()) { + return GhidraBundle.Type.SourceDir; + } + String n = f.getName().toLowerCase(); + if (n.endsWith(".bnd")) { + return GhidraBundle.Type.BndScript; + } + if (n.endsWith(".jar")) { + return GhidraBundle.Type.Jar; + } + return GhidraBundle.Type.INVALID; + } + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/script/osgi/GhidraBundleActivator.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/GhidraBundleActivator.java similarity index 96% rename from Ghidra/Features/Base/src/main/java/ghidra/app/script/osgi/GhidraBundleActivator.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/GhidraBundleActivator.java index cf33093b22..19174d7110 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/script/osgi/GhidraBundleActivator.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/GhidraBundleActivator.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.script.osgi; +package ghidra.app.plugin.core.osgi; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/script/osgi/GhidraBundleException.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/GhidraBundleException.java similarity index 98% rename from Ghidra/Features/Base/src/main/java/ghidra/app/script/osgi/GhidraBundleException.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/GhidraBundleException.java index 4d1d3bf650..ca15948665 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/script/osgi/GhidraBundleException.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/GhidraBundleException.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package ghidra.app.script.osgi; +package ghidra.app.plugin.core.osgi; import java.util.stream.Collectors; @@ -51,9 +51,9 @@ public class GhidraBundleException extends OSGiException { BundleException be = (BundleException) e; switch (be.getType()) { default: - case BundleException.UNSPECIFIED: return "No exception type"; - + case BundleException.UNSPECIFIED: + return "UNSPECIFIED"; /** * The operation was unsupported. This type can be used anywhere a * BundleException can be thrown. diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/script/osgi/SourceBundleInfo.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/GhidraSourceBundle.java similarity index 71% rename from Ghidra/Features/Base/src/main/java/ghidra/app/script/osgi/SourceBundleInfo.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/GhidraSourceBundle.java index 24371c3a71..a7a70d8688 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/script/osgi/SourceBundleInfo.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/GhidraSourceBundle.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.script.osgi; +package ghidra.app.plugin.core.osgi; import java.io.*; import java.nio.file.Files; @@ -22,20 +22,23 @@ import java.util.*; import java.util.stream.Collectors; import org.osgi.framework.Bundle; +import org.osgi.framework.BundleException; import org.osgi.framework.wiring.BundleRequirement; +import generic.io.NullPrintWriter; import generic.jar.ResourceFile; +import ghidra.app.plugin.core.osgi.BundleHost.BuildFailure; import ghidra.app.script.GhidraScriptUtil; import ghidra.app.script.ScriptInfo; -import ghidra.app.script.osgi.BundleHost.BuildFailure; /** * The SourceBundleInfo class is a cache of information for bundles built from source directories. */ -public class SourceBundleInfo { - private final BundleHost bundleHost; +public class GhidraSourceBundle implements GhidraBundle { + final private BundleHost bundleHost; final private ResourceFile sourceDir; - final String symbolicName; + + final private String symbolicName; final private Path binDir; final private String bundleLoc; boolean foundNewManifest; @@ -51,9 +54,9 @@ public class SourceBundleInfo { // cached values parsed form @imports tags on default-package source files - public SourceBundleInfo(BundleHost bundleHost, ResourceFile sourceDir) { + public GhidraSourceBundle(BundleHost bundleHost, ResourceFile sourceDirectory) { this.bundleHost = bundleHost; - this.sourceDir = sourceDir; + this.sourceDir = sourceDirectory; this.symbolicName = BundleHost.getSymbolicNameFromSourceDir(sourceDir); this.binDir = GhidraScriptUtil.getCompiledBundlesDir().resolve(symbolicName); @@ -61,6 +64,12 @@ public class SourceBundleInfo { } + /** + * for testing only!!! + * + * @param sourceFile a ghidra script file + * @return the directory its class is compiled to + */ static public Path getBindirFromScriptFile(ResourceFile sourceFile) { ResourceFile tmpSourceDir = sourceFile.getParentFile(); String tmpSymbolicName = BundleHost.getSymbolicNameFromSourceDir(tmpSourceDir); @@ -88,6 +97,7 @@ public class SourceBundleInfo { return binDir; } + @Override public Bundle install() throws GhidraBundleException { return bundleHost.installFromLoc(getBundleLoc()); } @@ -105,10 +115,9 @@ public class SourceBundleInfo { /** * update buildReqs based on \@imports tag in java files from the default package - * - * @throws OSGiException on failure to parse the \@imports tag + * @throws GhidraBundleException on failure to parse the \@imports tag */ - private void computeRequirements() throws OSGiException { + private void computeRequirements() throws GhidraBundleException { // parse metadata from all Java source in sourceDir buildReqs.clear(); req2file.clear(); @@ -119,7 +128,13 @@ public class SourceBundleInfo { ScriptInfo si = GhidraScriptUtil.getScriptInfo(rf); String imps = si.getImports(); if (imps != null && !imps.isEmpty()) { - List reqs = BundleHost.parseImports(imps); + List reqs; + try { + reqs = BundleHost.parseImports(imps); + } + catch (BundleException e) { + throw new GhidraBundleException(getBundleLoc(), "parsing manifest", e); + } buildReqs.put(rf, reqs); for (BundleRequirement req : reqs) { req2file.computeIfAbsent(req.toString(), x -> new ArrayList<>()).add(rf); @@ -235,16 +250,75 @@ public class SourceBundleInfo { } } + @Override public String getSummary() { return summary; } - public Bundle getBundle() { + @Override + public Bundle getBundle() throws GhidraBundleException { return bundleHost.getBundle(getBundleLoc()); } + @Override public String getBundleLoc() { return bundleLoc; } + @Override + public boolean build(PrintWriter writer) throws Exception { + if (writer == null) { + writer = new NullPrintWriter(); + } + + boolean needsCompile = false; + + updateFromFilesystem(writer); + + int failing = getFailingSourcesCount(); + int newSourcecount = getNewSourcesCount(); + + long lastBundleActivation = 0; // XXX record last bundle activation in bundlestatusmodel + if (failing > 0 && (lastBundleActivation > getLastCompileAttempt())) { + needsCompile = true; + } + + if (newSourcecount == 0) { + if (failing > 0) { + writer.printf("%s hasn't changed, with %d file%s failing in previous build(s):\n", + getSourceDir().toString(), failing, failing > 1 ? "s" : ""); + writer.printf("%s\n", getPreviousBuildErrors()); + } + if (newManifestFile()) { + needsCompile = true; + } + } + else { + needsCompile = true; + } + + if (needsCompile) { + writer.printf("%d new files, %d skipped, %s\n", newSourcecount, failing, + newManifestFile() ? ", new manifest" : ""); + + // if there a bundle is currently active, uninstall it + Bundle b = getBundle(); + if (b != null) { + bundleHost.deactivateSynchronously(b); + } + + // once we've committed to recompile and regenerate generated classes, delete the old stuff + deleteOldBinaries(); + + BundleCompiler bundleCompiler = new BundleCompiler(bundleHost); + + long startTime = System.nanoTime(); + bundleCompiler.compileToExplodedBundle(this, writer); + long endTime = System.nanoTime(); + writer.printf("%3.2f seconds compile time.\n", (endTime - startTime) / 1e9); + bundleHost.fireSourceBundleCompiled(this); + return true; + } + return false; + } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/script/osgi/OSGiException.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/OSGiException.java similarity index 95% rename from Ghidra/Features/Base/src/main/java/ghidra/app/script/osgi/OSGiException.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/OSGiException.java index 559daebbe6..ef8b129093 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/script/osgi/OSGiException.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/OSGiException.java @@ -13,10 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.script.osgi; - +package ghidra.app.plugin.core.osgi; import ghidra.util.exception.UsrException; + public class OSGiException extends UsrException { public OSGiException(String msg, Throwable cause) { super(msg, cause); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/script/osgi/OSGiListener.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/OSGiListener.java similarity index 60% rename from Ghidra/Features/Base/src/main/java/ghidra/app/script/osgi/OSGiListener.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/OSGiListener.java index 378730b7e2..fdc839f2fa 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/script/osgi/OSGiListener.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/OSGiListener.java @@ -1,10 +1,10 @@ -package ghidra.app.script.osgi; +package ghidra.app.plugin.core.osgi; import org.osgi.framework.Bundle; public interface OSGiListener { - void sourceBundleCompiled(SourceBundleInfo sbi); + void sourceBundleCompiled(GhidraSourceBundle sb); void bundleActivationChange(Bundle b, boolean newActivation); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/script/osgi/OSGiUtils.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/OSGiUtils.java similarity index 96% rename from Ghidra/Features/Base/src/main/java/ghidra/app/script/osgi/OSGiUtils.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/OSGiUtils.java index 788f7965a1..7f03cd180d 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/script/osgi/OSGiUtils.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/OSGiUtils.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package ghidra.app.script.osgi; +package ghidra.app.plugin.core.osgi; import java.util.List; import java.util.Scanner; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptComponentProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptComponentProvider.java index b72fa11db2..177d9f7125 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptComponentProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptComponentProvider.java @@ -29,7 +29,6 @@ import javax.swing.tree.TreePath; import javax.swing.tree.TreeSelectionModel; import org.apache.commons.lang3.StringUtils; -import org.osgi.framework.Bundle; import docking.ActionContext; import docking.action.KeyBindingData; @@ -40,10 +39,8 @@ import docking.widgets.tree.GTree; import docking.widgets.tree.GTreeNode; import docking.widgets.tree.support.BreadthFirstIterator; import generic.jar.ResourceFile; -import ghidra.app.plugin.core.script.osgi.*; +import ghidra.app.plugin.core.osgi.*; import ghidra.app.script.*; -import ghidra.app.script.osgi.BundleHost; -import ghidra.app.script.osgi.SourceBundleInfo; import ghidra.app.services.ConsoleService; import ghidra.framework.options.SaveState; import ghidra.framework.plugintool.ComponentProviderAdapter; @@ -99,10 +96,9 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter { } }; - GhidraScriptComponentProvider(GhidraScriptMgrPlugin plugin, BundleHost bundleHost) { + GhidraScriptComponentProvider(GhidraScriptMgrPlugin plugin) { super(plugin.getTool(), "Script Manager", plugin.getName()); this.plugin = plugin; - this.bundleHost = bundleHost; setHelpLocation(new HelpLocation(plugin.getName(), plugin.getName())); setIcon(ResourceManager.loadImage("images/play.png")); @@ -116,7 +112,6 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter { } void dispose() { - editorMap.clear(); scriptCategoryTree.dispose(); scriptTable.dispose(); @@ -138,7 +133,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter { } private void performRefresh() { - GhidraScriptUtil.setScriptBundlePaths(bundleStatusProvider.getModel().getPaths()); + GhidraScriptUtil.setScriptBundlePaths(bundleStatusProvider.getModel().getEnabledPaths()); GhidraScriptUtil.clearMetadata(); refresh(); } @@ -341,7 +336,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter { } public List getScriptDirectories() { - return bundleStatusProvider.getModel().getPaths().stream().filter( + return bundleStatusProvider.getModel().getEnabledPaths().stream().filter( ResourceFile::isDirectory).collect(Collectors.toList()); } @@ -397,7 +392,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter { } void runScript(String scriptName, TaskListener listener) { - List dirPaths = bundleStatusProvider.getModel().getPaths(); + List dirPaths = bundleStatusProvider.getModel().getEnabledPaths(); for (ResourceFile dir : dirPaths) { ResourceFile scriptSource = new ResourceFile(dir, scriptName); if (scriptSource.exists()) { @@ -457,7 +452,8 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter { console.addErrorMessage("", "Unable to instantiate script: " + scriptName); } catch (ClassNotFoundException e) { - console.addErrorMessage("", "Unable to locate script class: " + e.getMessage()); + console.addErrorMessage("", "Unable to locate script class: " + scriptName); + e.printStackTrace(); } // show the error icon @@ -537,7 +533,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter { private void updateAvailableScriptFilesForAllPaths() { List scriptsToRemove = tableModel.getScripts(); List scriptAccumulator = new ArrayList<>(); - List bundlePaths = bundleStatusProvider.getModel().getPaths(); + List bundlePaths = bundleStatusProvider.getModel().getEnabledPaths(); for (ResourceFile bundlePath : bundlePaths) { if (bundlePath.isDirectory()) { updateAvailableScriptFilesForDirectory(scriptsToRemove, scriptAccumulator, @@ -732,56 +728,8 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter { return true; } - final private BundleHost bundleHost; - - void startActivateDeactiveTask(BundlePath path, boolean activate) { - path.setBusy(true); - bundleStatusProvider.notifyTableChanged(); - ConsoleService console = plugin.getTool().getService(ConsoleService.class); - - new TaskLauncher(new Task((activate ? "Activating" : "Deactivating ") + " bundle...") { - @Override - public void run(TaskMonitor monitor) throws CancelledException { - try { - if (activate) { - if (path.getType() == BundlePath.Type.SourceDir) { - SourceBundleInfo sbi = bundleHost.getSourceBundleInfo(path.getPath()); - bundleHost.compileSourceBundle(sbi, console.getStdErr()); - } - String loc = bundleStatusProvider.getModel().getBundleLoc(path); - if (loc != null) { - bundleHost.activateSynchronously(loc); - } - } - else { // deactivate - String loc = bundleStatusProvider.getModel().getBundleLoc(path); - if (loc != null) { - Bundle bundle = bundleHost.getBundle(loc); - if (bundle != null) { - bundleHost.deactivateSynchronously(bundle); - } - } - } - } - catch (Exception e) { - e.printStackTrace(console.getStdErr()); - path.setActive(!activate); - - Msg.showError(this, GhidraScriptComponentProvider.this.getComponent(), - "bundle activation failed", e.getMessage()); - } - finally { - path.setBusy(false); - bundleStatusProvider.notifyTableChanged(); - } - } - }, null, 1000); - - } - private void build() { - bundleStatusProvider = - new BundleStatusProvider(plugin.getTool(), plugin.getName(), bundleHost); + bundleStatusProvider = new BundleStatusProvider(plugin.getTool(), plugin.getName()); bundleStatusProvider.getModel().addListener(new BundleStatusListener() { @Override @@ -791,20 +739,11 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter { } @Override - public void bundleEnablementChanged(BundlePath path, boolean enabled) { - if (!enabled && path.isActive()) { - startActivateDeactiveTask(path, false); - } - - if (path.isDirectory()) { + public void bundleEnablementChanged(BundleStatus status, boolean enabled) { + if (status.isDirectory()) { performRefresh(); } } - - @Override - public void bundleActivationChanged(BundlePath path, boolean newValue) { - startActivateDeactiveTask(path, newValue); - } }); scriptRoot = new RootNode(); @@ -1075,7 +1014,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter { bundleStatusProvider.getModel().restoreState(saveState); // pull in the just-loaded paths - List paths = bundleStatusProvider.getModel().getPaths(); + List paths = bundleStatusProvider.getModel().getEnabledPaths(); GhidraScriptUtil.setScriptBundlePaths(paths); actionManager.restoreUserDefinedKeybindings(saveState); actionManager.restoreScriptsThatAreInTool(saveState); @@ -1213,7 +1152,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter { public List getWritableScriptDirectories() { BundleStatusModel m = bundleStatusProvider.getModel(); - return m.getPaths().stream().filter(ResourceFile::isDirectory).filter( + return m.getEnabledPaths().stream().filter(ResourceFile::isDirectory).filter( m::isWriteable).collect(Collectors.toList()); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptMgrPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptMgrPlugin.java index 1acc3f3c63..d32df4dfaa 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptMgrPlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptMgrPlugin.java @@ -26,7 +26,6 @@ import ghidra.app.plugin.ProgramPlugin; import ghidra.app.plugin.core.eclipse.EclipseConnection; import ghidra.app.plugin.core.eclipse.EclipseIntegrationOptionsPlugin; import ghidra.app.script.GhidraState; -import ghidra.app.script.osgi.BundleHost; import ghidra.app.services.*; import ghidra.framework.options.SaveState; import ghidra.framework.options.ToolOptions; @@ -51,13 +50,10 @@ public class GhidraScriptMgrPlugin extends ProgramPlugin implements GhidraScript final private GhidraScriptComponentProvider provider; - final private BundleHost bundleHost; - public GhidraScriptMgrPlugin(PluginTool tool) { super(tool, true, true, true); - bundleHost = BundleHost.getInstance(); - provider = new GhidraScriptComponentProvider(this, bundleHost); + provider = new GhidraScriptComponentProvider(this); } @Override diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/script/GhidraScriptUtil.java b/Ghidra/Features/Base/src/main/java/ghidra/app/script/GhidraScriptUtil.java index 01e3f92211..e928dc6d9d 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/script/GhidraScriptUtil.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/script/GhidraScriptUtil.java @@ -169,7 +169,7 @@ public class GhidraScriptUtil { * @deprecated accessing class file directly precludes OSGi wiring according to requirements and capabilities */ @Deprecated - public static List getExplodedBundlePaths() { + public static List getExplodedCompiledSourceBundlePaths() { try { return Files.list(getOsgiDir()).filter(Files::isDirectory).map( x -> new ResourceFile(x.toFile())).collect(Collectors.toList()); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/script/JavaScriptProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/script/JavaScriptProvider.java index c75cf049b1..7733c93d63 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/script/JavaScriptProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/script/JavaScriptProvider.java @@ -16,21 +16,20 @@ package ghidra.app.script; import java.io.*; -import java.lang.reflect.InvocationTargetException; import org.osgi.framework.Bundle; import generic.jar.ResourceFile; -import ghidra.app.script.osgi.*; +import ghidra.app.plugin.core.osgi.*; import ghidra.util.Msg; public class JavaScriptProvider extends GhidraScriptProvider { - static public SourceBundleInfo getBundleInfoForSource(ResourceFile sourceFile) { + static public GhidraSourceBundle getBundleForSource(ResourceFile sourceFile) { ResourceFile sourceDir = getSourceDirectoryContaining(sourceFile); if (sourceDir == null) { return null; } - return BundleHost.getInstance().getSourceBundleInfo(sourceDir); + return (GhidraSourceBundle) BundleHost.getInstance().getGhidraBundle(sourceDir); } @Override @@ -45,16 +44,16 @@ public class JavaScriptProvider extends GhidraScriptProvider { @Override public boolean deleteScript(ResourceFile sourceFile) { - Bundle b = getBundleInfoForSource(sourceFile).getBundle(); - if (b != null) { - try { + try { + Bundle b = getBundleForSource(sourceFile).getBundle(); + if (b != null) { BundleHost.getInstance().deactivateSynchronously(b); } - catch (GhidraBundleException | InterruptedException e) { - e.printStackTrace(); - Msg.error(this, "while stopping script's bundle to delete it", e); - return false; - } + } + catch (GhidraBundleException | InterruptedException e) { + e.printStackTrace(); + Msg.error(this, "while deactivating bundle for delete", e); + return false; } return super.deleteScript(sourceFile); } @@ -62,7 +61,6 @@ public class JavaScriptProvider extends GhidraScriptProvider { @Override public GhidraScript getScriptInstance(ResourceFile sourceFile, PrintWriter writer) throws ClassNotFoundException, InstantiationException, IllegalAccessException { - try { Class clazz = loadClass(sourceFile, writer); Object object; @@ -80,25 +78,21 @@ public class JavaScriptProvider extends GhidraScriptProvider { return null; // class is not GhidraScript } - catch (OSGiException | IOException | InterruptedException | IllegalArgumentException - | InvocationTargetException | NoSuchMethodException | SecurityException e) { + catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) { + throw e; + } + catch (Exception e) { throw new ClassNotFoundException("", e); } } - static public Class loadClass(ResourceFile sourceFile, PrintWriter writer) - throws IOException, OSGiException, ClassNotFoundException, InterruptedException { + static public Class loadClass(ResourceFile sourceFile, PrintWriter writer) throws Exception { BundleHost bundleHost = BundleHost.getInstance(); - SourceBundleInfo bi = getBundleInfoForSource(sourceFile); - bundleHost.compileSourceBundle(bi, writer); - - // as much source as possible built, install bundle and start it if necessary - Bundle b = bi.getBundle(); - if (b == null) { - b = bi.install(); - } + GhidraSourceBundle bi = getBundleForSource(sourceFile); + bi.build(writer); + Bundle b = bi.install(); bundleHost.activateSynchronously(b); String classname = bi.classNameForScript(sourceFile); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/AbstractGhidraScriptMgrPluginTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/AbstractGhidraScriptMgrPluginTest.java index b625eafb15..bfecd10a33 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/AbstractGhidraScriptMgrPluginTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/AbstractGhidraScriptMgrPluginTest.java @@ -48,7 +48,7 @@ import generic.jar.ResourceFile; import generic.test.TestUtils; import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin; import ghidra.app.plugin.core.console.ConsoleComponentProvider; -import ghidra.app.plugin.core.script.osgi.BundleStatusProvider; +import ghidra.app.plugin.core.osgi.BundleStatusProvider; import ghidra.app.script.*; import ghidra.app.services.ConsoleService; import ghidra.framework.Application; @@ -979,7 +979,7 @@ public abstract class AbstractGhidraScriptMgrPluginTest // destroy any NewScriptxxx files...and Temp ones too BundleStatusProvider bundleStatusProvider = (BundleStatusProvider) TestUtils.getInstanceField("bundleStatusProvider", provider); - List paths = bundleStatusProvider.getModel().getPaths(); + List paths = bundleStatusProvider.getModel().getEnabledPaths(); for (ResourceFile path : paths) { File file = path.getFile(false); File[] listFiles = file.listFiles(); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/GhidraScriptMgrPlugin2Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/GhidraScriptMgrPlugin2Test.java index 94d2184ee7..88fc7a46f2 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/GhidraScriptMgrPlugin2Test.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/GhidraScriptMgrPlugin2Test.java @@ -26,9 +26,9 @@ import org.junit.Test; import docking.widgets.table.SelectionManager; import generic.jar.ResourceFile; import generic.test.AbstractGenericTest; +import ghidra.app.plugin.core.osgi.GhidraSourceBundle; import ghidra.app.script.GhidraScriptUtil; import ghidra.app.script.JavaScriptProvider; -import ghidra.app.script.osgi.SourceBundleInfo; import ghidra.test.ScriptTaskListener; import ghidra.util.TaskUtilities; import utilities.util.FileUtilities; @@ -149,7 +149,7 @@ public class GhidraScriptMgrPlugin2Test extends AbstractGhidraScriptMgrPluginTes // remove all class files from the user script bin dir File userScriptsBinDir = - SourceBundleInfo.getBindirFromScriptFile(new ResourceFile(newScriptFile)).toFile(); + GhidraSourceBundle.getBindirFromScriptFile(new ResourceFile(newScriptFile)).toFile(); File[] userScriptBinDirFiles; if (userScriptsBinDir.exists()) { userScriptBinDirFiles = userScriptsBinDir.listFiles(classFileFilter); @@ -195,7 +195,7 @@ public class GhidraScriptMgrPlugin2Test extends AbstractGhidraScriptMgrPluginTes // verify that the generated class file is placed in the default scripting home/bin File userScriptsBinDir = - SourceBundleInfo.getBindirFromScriptFile(systemScriptFile).toFile(); + GhidraSourceBundle.getBindirFromScriptFile(systemScriptFile).toFile(); String className = scriptName.replace(".java", ".class"); File expectedClassFile = new File(userScriptsBinDir, className); @@ -232,7 +232,7 @@ public class GhidraScriptMgrPlugin2Test extends AbstractGhidraScriptMgrPluginTes // verify a bin dir was created and that the class file is in it File binDir = - SourceBundleInfo.getBindirFromScriptFile(new ResourceFile(newScriptFile)).toFile(); + GhidraSourceBundle.getBindirFromScriptFile(new ResourceFile(newScriptFile)).toFile(); assertTrue("bin output dir not created", binDir.exists()); File scriptClassFile = new File(binDir, rawScriptName + ".class"); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/GhidraScriptMgrPlugin3Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/GhidraScriptMgrPlugin3Test.java index 034f6be98e..f5ec0baafb 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/GhidraScriptMgrPlugin3Test.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/GhidraScriptMgrPlugin3Test.java @@ -30,7 +30,7 @@ import docking.action.DockingActionIf; import docking.widgets.filter.FilterTextField; import docking.widgets.list.ListPanel; import generic.jar.ResourceFile; -import ghidra.app.plugin.core.script.osgi.BundleStatusProvider; +import ghidra.app.plugin.core.osgi.BundleStatusProvider; import ghidra.app.script.GhidraScriptUtil; import ghidra.app.script.JavaScriptProvider; import ghidra.util.StringUtilities; diff --git a/Ghidra/Features/Python/src/main/java/ghidra/python/GhidraPythonInterpreter.java b/Ghidra/Features/Python/src/main/java/ghidra/python/GhidraPythonInterpreter.java index 069294be9a..617f5eba0a 100644 --- a/Ghidra/Features/Python/src/main/java/ghidra/python/GhidraPythonInterpreter.java +++ b/Ghidra/Features/Python/src/main/java/ghidra/python/GhidraPythonInterpreter.java @@ -138,7 +138,7 @@ public class GhidraPythonInterpreter extends InteractiveInterpreter { systemState.path.append(Py.newString(resourceFile.getFile(false).getAbsolutePath())); } - for (ResourceFile resourceFile : GhidraScriptUtil.getExplodedBundlePaths()) { + for (ResourceFile resourceFile : GhidraScriptUtil.getExplodedCompiledSourceBundlePaths()) { systemState.path.append(Py.newString(resourceFile.getFile(false).getAbsolutePath())); } diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/GhidraScriptMgrPluginScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/GhidraScriptMgrPluginScreenShots.java index 413c64909b..0822ed0198 100644 --- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/GhidraScriptMgrPluginScreenShots.java +++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/GhidraScriptMgrPluginScreenShots.java @@ -27,8 +27,8 @@ import docking.widgets.tree.GTree; import docking.widgets.tree.GTreeNode; import generic.jar.ResourceFile; import ghidra.app.plugin.core.console.ConsoleComponentProvider; +import ghidra.app.plugin.core.osgi.BundleStatusProvider; import ghidra.app.plugin.core.script.*; -import ghidra.app.plugin.core.script.osgi.BundleStatusProvider; import ghidra.app.script.GhidraScriptUtil; import ghidra.app.services.ConsoleService; import ghidra.util.HelpLocation;