allow failing scripts

This commit is contained in:
Jason P. Leasure
2020-02-12 15:15:36 -05:00
parent 59bd3806cc
commit d5d75fac9f
6 changed files with 154 additions and 111 deletions

View File

@@ -1,10 +1,10 @@
##VERSION: 2.0 ##VERSION: 2.0
##MODULE IP: BSD
##MODULE IP: MIT
##MODULE IP: Apache License 2.0 ##MODULE IP: Apache License 2.0
##MODULE IP: BSD
##MODULE IP: Copyright Distribution Permitted ##MODULE IP: Copyright Distribution Permitted
##MODULE IP: FAMFAMFAM Icons - CC 2.5 ##MODULE IP: FAMFAMFAM Icons - CC 2.5
##MODULE IP: LGPL 2.1 ##MODULE IP: LGPL 2.1
##MODULE IP: MIT
##MODULE IP: Modified Nuvola Icons - LGPL 2.1 ##MODULE IP: Modified Nuvola Icons - LGPL 2.1
##MODULE IP: Nuvola Icons - LGPL 2.1 ##MODULE IP: Nuvola Icons - LGPL 2.1
##MODULE IP: Oxygen Icons - LGPL 3.0 ##MODULE IP: Oxygen Icons - LGPL 3.0

View File

@@ -130,8 +130,11 @@ public class JavaScriptProvider extends GhidraScriptProvider {
bundle_host.startBundleWatcher(); bundle_host.startBundleWatcher();
} }
// wait for bundle to be started
try { 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)) { if (!bundle_host.waitForBundleStart(bi.bundleLoc)) {
Msg.error(this, "starting bundle"); Msg.error(this, "starting bundle");
return null; return null;
@@ -187,7 +190,7 @@ public class JavaScriptProvider extends GhidraScriptProvider {
final ResourceFile sourceDir; final ResourceFile sourceDir;
final String symbolicName; final String symbolicName;
final Path binDir; final Path binDir;
final String bundleLoc; String bundleLoc;
public ScriptBundleInfo(ResourceFile sourceDir) { public ScriptBundleInfo(ResourceFile sourceDir) {
this.sourceDir = sourceDir; this.sourceDir = sourceDir;

View File

@@ -43,6 +43,10 @@ public class ResourceFileJavaFileObject implements JavaFileObject {
pathName = file.getAbsolutePath().substring(sourceRootPath.length() + 1); pathName = file.getAbsolutePath().substring(sourceRootPath.length() + 1);
} }
public ResourceFile getFile() {
return file;
}
@Override @Override
public URI toUri() { public URI toUri() {
return file.toURI(); return file.toURI();

View File

@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package ghidra.app.script.osgi; package ghidra.app.script.osgi;
import java.io.*; import java.io.*;
@@ -35,6 +34,7 @@ import aQute.bnd.osgi.*;
import aQute.bnd.osgi.Clazz.QUERY; import aQute.bnd.osgi.Clazz.QUERY;
import generic.jar.ResourceFile; import generic.jar.ResourceFile;
import ghidra.app.script.*; import ghidra.app.script.*;
import ghidra.util.Msg;
public class BundleCompiler { public class BundleCompiler {
@@ -44,10 +44,11 @@ public class BundleCompiler {
this.bh = bh; this.bh = bh;
} }
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
// compile a source directory to an exploded bundle // 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 { throws IOException {
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
List<String> options = new ArrayList<>(); List<String> options = new ArrayList<>();
options.add("-g"); options.add("-g");
options.add("-d"); options.add("-d");
@@ -60,7 +61,6 @@ public class BundleCompiler {
final JavaFileManager fm0 = final JavaFileManager fm0 =
new ResourceFileJavaFileManager(GhidraScriptUtil.getScriptSourceDirectories()); new ResourceFileJavaFileManager(GhidraScriptUtil.getScriptSourceDirectories());
BundleJavaManager fm = new BundleJavaManager(bh.getHostFramework(), fm0, options); BundleJavaManager fm = new BundleJavaManager(bh.getHostFramework(), fm0, options);
DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
final List<ResourceFileJavaFileObject> compilationUnits = new ArrayList<>(); final List<ResourceFileJavaFileObject> compilationUnits = new ArrayList<>();
@@ -76,29 +76,54 @@ public class BundleCompiler {
Files.delete(dmf); Files.delete(dmf);
} }
JavaCompiler.CompilationTask task = // try to compile, if we fail remove offenders and try again
compiler.getTask(output, fm, diagnostics, options, null, compilationUnits); while (!compilationUnits.isEmpty()) {
// task.setProcessors // for annotation processing / code generation DiagnosticCollector<JavaFileObject> diagnostics =
new DiagnosticCollector<JavaFileObject>();
JavaCompiler.CompilationTask task =
compiler.getTask(writer, fm, diagnostics, options, null, compilationUnits);
// task.setProcessors // for annotation processing / code generation
Boolean successfulCompilation = task.call(); Boolean successfulCompilation = task.call();
output.flush(); if (successfulCompilation) {
System.err.printf("%s: %s\n", successfulCompilation ? "Success" : "Fail", output); break;
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;
} }
for (Diagnostic<? extends JavaFileObject> 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 ResourceFile smf = new ResourceFile(srcdir, "META-INF" + File.separator + "MANIFEST.MF");
Analyzer analyzer = new Analyzer(); if (smf.exists()) {
analyzer.setJar(new Jar(bindir.toFile())); // give bnd the contents System.err.printf("Found manifest, not generating one\n");
Stream<Object> bjars = Files.list(bh.getCompiledBundlesDir()).filter( Files.createFile(dmf);
f -> f.toString().endsWith(".jar")).map(f -> { 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<Object> bjars =
Files.list(bh.getCompiledBundlesDir()).filter(f -> f.toString().endsWith(".jar")).map(
f -> {
try { try {
return new Jar(f.toFile()); return new Jar(f.toFile());
} }
@@ -108,91 +133,99 @@ public class BundleCompiler {
} }
}); });
analyzer.addClasspath(bjars.collect(Collectors.toUnmodifiableList())); analyzer.addClasspath(bjars.collect(Collectors.toUnmodifiableList()));
analyzer.setProperty("Bundle-SymbolicName", analyzer.setProperty("Bundle-SymbolicName",
JavaScriptProvider.getSymbolicNameFromSourceDir(srcdir)); JavaScriptProvider.getSymbolicNameFromSourceDir(srcdir));
analyzer.setProperty("Bundle-Version", "1.0"); analyzer.setProperty("Bundle-Version", "1.0");
analyzer.setProperty("Import-Package", "*"); analyzer.setProperty("Import-Package", "*");
// analyzer.setBundleActivator(s); // analyzer.setBundleActivator(s);
try { try {
Manifest manifest = analyzer.calcManifest(); Manifest manifest = analyzer.calcManifest();
Attributes ma = manifest.getMainAttributes(); Attributes ma = manifest.getMainAttributes();
String activator_classname = null; String activator_classname = null;
for (Clazz clazz : analyzer.getClassspace().values()) { for (Clazz clazz : analyzer.getClassspace().values()) {
if (clazz.is(QUERY.IMPLEMENTS, if (clazz.is(QUERY.IMPLEMENTS,
new Instruction("org.osgi.framework.BundleActivator"), analyzer)) { new Instruction("org.osgi.framework.BundleActivator"), analyzer)) {
System.err.printf("found BundleActivator class %s\n", clazz); System.err.printf("found BundleActivator class %s\n", clazz);
activator_classname = clazz.toString(); 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<? extends JavaFileObject> 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);
} }
} }
catch (Exception e) { if (activator_classname == null) {
e.printStackTrace(); 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 { catch (Exception e) {
for (Diagnostic<? extends JavaFileObject> dm : diagnostics.getDiagnostics()) { e.printStackTrace();
System.err.printf("COMPILE ERROR: %s\n", dm); }
} 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<String> 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<? extends JavaFileObject> compilationUnits2 =
fm0.getJavaFileObjectsFromPaths(List.of(activator_dest));
DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
JavaCompiler.CompilationTask task2 =
compiler.getTask(output, fm, diagnostics, options, null, compilationUnits2);
if (!task2.call()) {
for (Diagnostic<? extends JavaFileObject> d : diagnostics.getDiagnostics()) {
output.write(d.getSource().toString() + ": " + d.getMessage(null) + "\n");
}
return false;
}
return true;
}
} }

View File

@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package ghidra.app.script.osgi; package ghidra.app.script.osgi;
import static java.util.stream.Collectors.*; import static java.util.stream.Collectors.*;
@@ -28,7 +27,6 @@ import java.util.regex.Pattern;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import org.apache.felix.fileinstall.internal.FileInstall;
import org.apache.felix.framework.FrameworkFactory; import org.apache.felix.framework.FrameworkFactory;
import org.apache.felix.framework.util.FelixConstants; import org.apache.felix.framework.util.FelixConstants;
import org.apache.felix.main.AutoProcessor; import org.apache.felix.main.AutoProcessor;
@@ -68,6 +66,11 @@ public class BundleHost {
return bc.installBundle(location, new FileInputStream(new File(path_to_jar))); 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() { void dumpLoadedBundles() {
System.err.printf("=== Bundles ===\n"); System.err.printf("=== Bundles ===\n");
for (Bundle bundle : bc.getBundles()) { for (Bundle bundle : bc.getBundles()) {
@@ -196,8 +199,9 @@ public class BundleHost {
}); });
felix.start(); felix.start();
fileinstall_bundle = installFromPath(findJarForClass(FileInstall.class)); // fileinstall_bundle = installFromPath(findJarForClass(FileInstall.class));
fileinstall_bundle.start(); // fileinstall_bundle.start();
} }
private Path getOsgiDir() { private Path getOsgiDir() {

View File

@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package ghidra.app.script.osgi; package ghidra.app.script.osgi;
import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleActivator;