From d5d75fac9f73076811bc76a9d79f0d5604eac59b Mon Sep 17 00:00:00 2001 From: "Jason P. Leasure" Date: Wed, 12 Feb 2020 15:15:36 -0500 Subject: [PATCH] allow failing scripts --- Ghidra/Features/Base/certification.manifest | 4 +- .../ghidra/app/script/JavaScriptProvider.java | 7 +- .../script/ResourceFileJavaFileObject.java | 4 + .../app/script/osgi/BundleCompiler.java | 237 ++++++++++-------- .../ghidra/app/script/osgi/BundleHost.java | 12 +- .../script/osgi/GhidraBundleActivator.java | 1 - 6 files changed, 154 insertions(+), 111 deletions(-) diff --git a/Ghidra/Features/Base/certification.manifest b/Ghidra/Features/Base/certification.manifest index 366a02322b..8afcc61691 100644 --- a/Ghidra/Features/Base/certification.manifest +++ b/Ghidra/Features/Base/certification.manifest @@ -1,10 +1,10 @@ ##VERSION: 2.0 -##MODULE IP: BSD -##MODULE IP: MIT ##MODULE IP: Apache License 2.0 +##MODULE IP: BSD ##MODULE IP: Copyright Distribution Permitted ##MODULE IP: FAMFAMFAM Icons - CC 2.5 ##MODULE IP: LGPL 2.1 +##MODULE IP: MIT ##MODULE IP: Modified Nuvola Icons - LGPL 2.1 ##MODULE IP: Nuvola Icons - LGPL 2.1 ##MODULE IP: Oxygen Icons - LGPL 3.0 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 83da0d3c40..8860deb537 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 @@ -130,8 +130,11 @@ public class JavaScriptProvider extends GhidraScriptProvider { bundle_host.startBundleWatcher(); } - // wait for bundle to be started try { + Bundle b = bundle_host.installExplodedPath(bi.binDir); + bi.bundleLoc = b.getLocation(); + System.err.printf("new bundle loc is %s\n", bi.bundleLoc); + b.start(); if (!bundle_host.waitForBundleStart(bi.bundleLoc)) { Msg.error(this, "starting bundle"); return null; @@ -187,7 +190,7 @@ public class JavaScriptProvider extends GhidraScriptProvider { final ResourceFile sourceDir; final String symbolicName; final Path binDir; - final String bundleLoc; + String bundleLoc; public ScriptBundleInfo(ResourceFile sourceDir) { this.sourceDir = sourceDir; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/script/ResourceFileJavaFileObject.java b/Ghidra/Features/Base/src/main/java/ghidra/app/script/ResourceFileJavaFileObject.java index aed7114790..08a9a98c44 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/script/ResourceFileJavaFileObject.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/script/ResourceFileJavaFileObject.java @@ -43,6 +43,10 @@ public class ResourceFileJavaFileObject implements JavaFileObject { pathName = file.getAbsolutePath().substring(sourceRootPath.length() + 1); } + public ResourceFile getFile() { + return file; + } + @Override public URI toUri() { return file.toURI(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/script/osgi/BundleCompiler.java b/Ghidra/Features/Base/src/main/java/ghidra/app/script/osgi/BundleCompiler.java index 01f33e0189..d9ffbf6ba9 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/script/osgi/BundleCompiler.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/script/osgi/BundleCompiler.java @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package ghidra.app.script.osgi; import java.io.*; @@ -35,6 +34,7 @@ import aQute.bnd.osgi.*; import aQute.bnd.osgi.Clazz.QUERY; import generic.jar.ResourceFile; import ghidra.app.script.*; +import ghidra.util.Msg; public class BundleCompiler { @@ -44,10 +44,11 @@ public class BundleCompiler { this.bh = bh; } + JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + // compile a source directory to an exploded bundle - public void compileToExplodedBundle(ResourceFile srcdir, Path bindir, Writer output) + public void compileToExplodedBundle(ResourceFile srcdir, Path bindir, Writer writer) throws IOException { - JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); List options = new ArrayList<>(); options.add("-g"); options.add("-d"); @@ -60,7 +61,6 @@ public class BundleCompiler { final JavaFileManager fm0 = new ResourceFileJavaFileManager(GhidraScriptUtil.getScriptSourceDirectories()); BundleJavaManager fm = new BundleJavaManager(bh.getHostFramework(), fm0, options); - DiagnosticCollector diagnostics = new DiagnosticCollector(); final List compilationUnits = new ArrayList<>(); @@ -76,29 +76,54 @@ public class BundleCompiler { Files.delete(dmf); } - JavaCompiler.CompilationTask task = - compiler.getTask(output, fm, diagnostics, options, null, compilationUnits); - // task.setProcessors // for annotation processing / code generation + // try to compile, if we fail remove offenders and try again + while (!compilationUnits.isEmpty()) { + DiagnosticCollector diagnostics = + new DiagnosticCollector(); + JavaCompiler.CompilationTask task = + compiler.getTask(writer, fm, diagnostics, options, null, compilationUnits); + // task.setProcessors // for annotation processing / code generation - Boolean successfulCompilation = task.call(); - output.flush(); - System.err.printf("%s: %s\n", successfulCompilation ? "Success" : "Fail", output); - - if (successfulCompilation) { - ResourceFile smf = - new ResourceFile(srcdir, "META-INF" + File.separator + "MANIFEST.MF"); - if (smf.exists()) { - System.err.printf("Found manifest, not generating one\n"); - Files.createFile(dmf); - Files.copy(smf.getInputStream(), dmf, StandardCopyOption.REPLACE_EXISTING); - return; + Boolean successfulCompilation = task.call(); + if (successfulCompilation) { + break; } + for (Diagnostic d : diagnostics.getDiagnostics()) { + writer.write(d.getSource().toString() + ": " + d.getMessage(null) + "\n"); + ResourceFileJavaFileObject sf = (ResourceFileJavaFileObject) d.getSource(); + if (compilationUnits.remove(sf)) { + writer.write(String.format("skipping %s\n", sf.toString())); + ResourceFile rf = sf.getFile(); + if (GhidraScriptUtil.contains(rf)) { + ScriptInfo info = GhidraScriptUtil.getScriptInfo(rf); + info.setCompileErrors(true); + } + } + } + } + // make sure the succeses are marked + for (ResourceFileJavaFileObject sf : compilationUnits) { + ResourceFile rf = sf.getFile(); + if (GhidraScriptUtil.contains(rf)) { + ScriptInfo info = GhidraScriptUtil.getScriptInfo(rf); + info.setCompileErrors(false); + } + } - // no manifest, so create one with bndtools - Analyzer analyzer = new Analyzer(); - analyzer.setJar(new Jar(bindir.toFile())); // give bnd the contents - Stream bjars = Files.list(bh.getCompiledBundlesDir()).filter( - f -> f.toString().endsWith(".jar")).map(f -> { + ResourceFile smf = new ResourceFile(srcdir, "META-INF" + File.separator + "MANIFEST.MF"); + if (smf.exists()) { + System.err.printf("Found manifest, not generating one\n"); + Files.createFile(dmf); + Files.copy(smf.getInputStream(), dmf, StandardCopyOption.REPLACE_EXISTING); + return; + } + + // no manifest, so create one with bndtools + Analyzer analyzer = new Analyzer(); + analyzer.setJar(new Jar(bindir.toFile())); // give bnd the contents + Stream bjars = + Files.list(bh.getCompiledBundlesDir()).filter(f -> f.toString().endsWith(".jar")).map( + f -> { try { return new Jar(f.toFile()); } @@ -108,91 +133,99 @@ public class BundleCompiler { } }); - analyzer.addClasspath(bjars.collect(Collectors.toUnmodifiableList())); - analyzer.setProperty("Bundle-SymbolicName", - JavaScriptProvider.getSymbolicNameFromSourceDir(srcdir)); - analyzer.setProperty("Bundle-Version", "1.0"); - analyzer.setProperty("Import-Package", "*"); - // analyzer.setBundleActivator(s); + analyzer.addClasspath(bjars.collect(Collectors.toUnmodifiableList())); + analyzer.setProperty("Bundle-SymbolicName", + JavaScriptProvider.getSymbolicNameFromSourceDir(srcdir)); + analyzer.setProperty("Bundle-Version", "1.0"); + analyzer.setProperty("Import-Package", "*"); + // analyzer.setBundleActivator(s); - try { - Manifest manifest = analyzer.calcManifest(); - Attributes ma = manifest.getMainAttributes(); + try { + Manifest manifest = analyzer.calcManifest(); + Attributes ma = manifest.getMainAttributes(); - String activator_classname = null; - for (Clazz clazz : analyzer.getClassspace().values()) { - if (clazz.is(QUERY.IMPLEMENTS, - new Instruction("org.osgi.framework.BundleActivator"), analyzer)) { - System.err.printf("found BundleActivator class %s\n", clazz); - activator_classname = clazz.toString(); - } - } - if (activator_classname == null) { - Path activator_dest = bindir.resolve("GeneratedActivator.java"); - try (PrintWriter writer = new PrintWriter( - Files.newBufferedWriter(activator_dest, Charset.forName("UTF-8")))) { - writer.println("import ghidra.app.script.osgi.GhidraBundleActivator;"); - writer.println("import org.osgi.framework.BundleActivator;"); - writer.println("import org.osgi.framework.BundleContext;"); - writer.println( - "public class GeneratedActivator extends GhidraBundleActivator {"); - writer.println(" protected void start(BundleContext bc, Object api) {"); - writer.println(" // TODO: stuff to do on bundle start"); - writer.println(" }"); - writer.println(" protected void stop(BundleContext bc, Object api) {"); - writer.println(" // TODO: stuff to do on bundle stop"); - writer.println(" }"); - writer.println(); - writer.println("}"); - } - catch (IOException ex) { - ex.printStackTrace(); - return; - } - activator_classname = "GeneratedActivator"; - - options.clear(); - options.add("-g"); - options.add("-d"); - options.add(bindir.toString()); - options.add("-sourcepath"); - options.add(bindir.toString()); - options.add("-classpath"); - options.add(System.getProperty("java.class.path")); - options.add("-proc:none"); - - StandardJavaFileManager fm02 = - compiler.getStandardFileManager(null, null, null); - fm = new BundleJavaManager(bh.getHostFramework(), fm02, options); - Iterable compilationUnits2 = - fm02.getJavaFileObjectsFromPaths(List.of(activator_dest)); - - JavaCompiler.CompilationTask task2 = - compiler.getTask(output, fm, diagnostics, options, null, compilationUnits2); - if (!task2.call()) { - return; - } - // since we add the activator after bndtools built the imports, we should add its imports too - String imps = ma.getValue(Constants.IMPORT_PACKAGE); - ma.putValue(Constants.IMPORT_PACKAGE, imps + ",ghidra.app.script.osgi"); - } - ma.putValue(Constants.BUNDLE_ACTIVATOR, activator_classname); - - Files.createDirectories(dmf.getParent()); - try (OutputStream out = Files.newOutputStream(dmf)) { - manifest.write(out); + String activator_classname = null; + for (Clazz clazz : analyzer.getClassspace().values()) { + if (clazz.is(QUERY.IMPLEMENTS, + new Instruction("org.osgi.framework.BundleActivator"), analyzer)) { + System.err.printf("found BundleActivator class %s\n", clazz); + activator_classname = clazz.toString(); } } - catch (Exception e) { - e.printStackTrace(); + if (activator_classname == null) { + activator_classname = "GeneratedActivator"; + if (!createActivator(bindir, activator_classname, writer)) { + Msg.error(this, "failed to create 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); + ma.putValue(Constants.IMPORT_PACKAGE, imps + ",ghidra.app.script.osgi"); + } + ma.putValue(Constants.BUNDLE_ACTIVATOR, activator_classname); + + // write the manifest + Files.createDirectories(dmf.getParent()); + try (OutputStream out = Files.newOutputStream(dmf)) { + manifest.write(out); } - analyzer.close(); } - else { - for (Diagnostic dm : diagnostics.getDiagnostics()) { - System.err.printf("COMPILE ERROR: %s\n", dm); - } + catch (Exception e) { + e.printStackTrace(); + } + finally { + analyzer.close(); } } + private boolean createActivator(Path bindir, String activator_classname, Writer output) + throws IOException { + Path activator_dest = bindir.resolve(activator_classname + ".java"); + + try (PrintWriter writer = + new PrintWriter(Files.newBufferedWriter(activator_dest, Charset.forName("UTF-8")))) { + writer.println("import ghidra.app.script.osgi.GhidraBundleActivator;"); + writer.println("import org.osgi.framework.BundleActivator;"); + writer.println("import org.osgi.framework.BundleContext;"); + writer.println("public class GeneratedActivator extends GhidraBundleActivator {"); + writer.println(" protected void start(BundleContext bc, Object api) {"); + writer.println(" // TODO: stuff to do on bundle start"); + writer.println(" }"); + writer.println(" protected void stop(BundleContext bc, Object api) {"); + writer.println(" // TODO: stuff to do on bundle stop"); + writer.println(" }"); + writer.println(); + writer.println("}"); + } + catch (IOException ex) { + ex.printStackTrace(); + return false; + } + + List options = new ArrayList<>(); + options.add("-g"); + options.add("-d"); + options.add(bindir.toString()); + options.add("-sourcepath"); + options.add(bindir.toString()); + options.add("-classpath"); + options.add(System.getProperty("java.class.path")); + options.add("-proc:none"); + + StandardJavaFileManager fm0 = compiler.getStandardFileManager(null, null, null); + BundleJavaManager fm = new BundleJavaManager(bh.getHostFramework(), fm0, options); + Iterable compilationUnits2 = + fm0.getJavaFileObjectsFromPaths(List.of(activator_dest)); + DiagnosticCollector diagnostics = new DiagnosticCollector(); + JavaCompiler.CompilationTask task2 = + compiler.getTask(output, fm, diagnostics, options, null, compilationUnits2); + if (!task2.call()) { + for (Diagnostic d : diagnostics.getDiagnostics()) { + output.write(d.getSource().toString() + ": " + d.getMessage(null) + "\n"); + } + return false; + } + return true; + } + } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/script/osgi/BundleHost.java b/Ghidra/Features/Base/src/main/java/ghidra/app/script/osgi/BundleHost.java index 4b8ab1c7b8..caadb9ffa6 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/script/osgi/BundleHost.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/script/osgi/BundleHost.java @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package ghidra.app.script.osgi; import static java.util.stream.Collectors.*; @@ -28,7 +27,6 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; -import org.apache.felix.fileinstall.internal.FileInstall; import org.apache.felix.framework.FrameworkFactory; import org.apache.felix.framework.util.FelixConstants; import org.apache.felix.main.AutoProcessor; @@ -68,6 +66,11 @@ public class BundleHost { return bc.installBundle(location, new FileInputStream(new File(path_to_jar))); } + public Bundle installExplodedPath(Path p) throws BundleException { + return bc.installBundle("reference:file://" + p.toAbsolutePath().normalize().toString()); + + } + void dumpLoadedBundles() { System.err.printf("=== Bundles ===\n"); for (Bundle bundle : bc.getBundles()) { @@ -196,8 +199,9 @@ public class BundleHost { }); felix.start(); - fileinstall_bundle = installFromPath(findJarForClass(FileInstall.class)); - fileinstall_bundle.start(); + // fileinstall_bundle = installFromPath(findJarForClass(FileInstall.class)); + // fileinstall_bundle.start(); + } private Path getOsgiDir() { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/script/osgi/GhidraBundleActivator.java b/Ghidra/Features/Base/src/main/java/ghidra/app/script/osgi/GhidraBundleActivator.java index 2f536ad6db..cf33093b22 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/script/osgi/GhidraBundleActivator.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/script/osgi/GhidraBundleActivator.java @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package ghidra.app.script.osgi; import org.osgi.framework.BundleActivator;