Commit 072e8bab authored by Lubomir Bulej's avatar Lubomir Bulej

- Added annotation processor (DislClassFinder) to find DiSL classes.

- Modified builds system to automatically find DiSL classes in test suites.
- Removed MANIFEST.MF files from test suites.
- Changed class name separator for DiSL-Classes attribute from colon to comma,
  because the Ant "jar" task does not like the colon as a separator when generating
  MANIFEST.MF on the fly.
parent ae452fa3
......@@ -223,6 +223,7 @@
<target name="build-disl" depends="compile-disl,build-disl-bypass">
<local name="disl.server.class" />
<find-class property="disl.server.class" dir="${build.disl}" name="DiSLServer" />
<find-class property="disl.classfinder.class" dir="${build.disl}" name="DislClassFinder" />
<jar destfile="${out.lib}/${disl.lib}">
<manifest>
......@@ -231,8 +232,10 @@
<attribute name="DiSL-Version" value="${disl.version}" />
</manifest>
<service type="javax.annotation.processing.Processor" provider="${disl.classfinder.class}" />
<fileset dir="${build.util}" />
<fileset dir="${build.disl}" excludes="**/ExtendThread.class" />
<fileset dir="${build.disl}" />
<resources refid="disl.bypass.files" />
</jar>
</target>
......@@ -411,16 +414,18 @@
<!-- JUnit tests and DiSL/Shadow VM test suites -->
<path id="test.classpath">
<path refid="util.classpath" />
<path refid="disl.classpath" />
<path refid="shvm.classpath" />
<path refid="junit.classpath" />
<path refid="asm.classpath" />
</path>
<target name="compile-test" depends="compile-util,compile-disl,compile-shvm">
<mkdir dir="${build.test}" />
<javac destdir="${build.test}" srcdir="${src.test}" debug="true">
<classpath>
<path refid="util.classpath" />
<path refid="disl.classpath" />
<path refid="shvm.classpath" />
<path refid="junit.classpath" />
<path refid="asm.classpath" />
</classpath>
<classpath refid="test.classpath" />
</javac>
<!-- Copy test resource files from source to class output directory. -->
......@@ -480,34 +485,43 @@
</manifest>
</jar>
<!-- Package instrumentation classes. -->
<property name="inst.jar" value="${out.test}/${test.suite.name}-inst.jar" />
<property name="inst.manifest" value="${test.suite.path}/instr/MANIFEST.MF" />
<fileset id="inst.files" dir="${build.test}">
<include name="${test.suite.path}/instr/**" />
<exclude name="${inst.manifest}" />
</fileset>
<!--
If the suite has a MANIFEST.MF, use it when creating the instrumentation
jar file, otherwise generate a manifest with a default DiSL class.
Process annotations to find DiSL classes.
Use an empty directory as destination for the classes to
make the "javac" task actually execute "javac" with the
source files. Using the normal build destination directory
would result in "javac" task excluding all source files
for which it could find up-to-date class files.
-->
<if>
<available file="${src.test}/${inst.manifest}" />
<then>
<jar destfile="${inst.jar}" manifest="${src.test}/${inst.manifest}">
<fileset refid="inst.files" />
</jar>
</then><else>
<jar destfile="${inst.jar}">
<fileset refid="inst.files" />
<manifest>
<attribute name="DiSL-Classes" value="${test.suite.pkg}.instr.DiSLClass" />
</manifest>
</jar>
</else>
</if>
<tempfile property="disl.classfinder.output" destDir="${out.dir}" createFile="true" deleteOnExit="true" />
<find-class property="disl.classfinder.class" dir="${build.disl}" name="DislClassFinder" />
<mkdir dir="${out.dir}/empty" />
<javac srcdir="${src.test}" destDir="${out.dir}/empty">
<classpath refid="test.classpath" />
<compilerArg value="-proc:only" />
<compilerArg line="-processor ${disl.classfinder.class}" />
<compilerArg value="-Adisl.classfinder.output=${disl.classfinder.output}" />
<compilerArg value="-Adisl.classfinder.separator=," />
<include name="${test.suite.path}/**/*.java" />
</javac>
<loadfile property="disl.classes" srcFile="${disl.classfinder.output}" />
<!-- Package instrumentation classes. -->
<property name="inst.jar" value="${out.test}/${test.suite.name}-inst.jar" />
<jar destfile="${inst.jar}">
<manifest>
<attribute name="DiSL-Classes" value="${disl.classes}" />
</manifest>
<fileset dir="${build.test}" includes="${test.suite.path}/instr/**" />
</jar>
</target>
......
package ch.usi.dag.disl.tools;
import java.io.FileNotFoundException;
import java.io.PrintStream;
import java.util.Collections;
import java.util.Set;
import javax.annotation.processing.Completion;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic.Kind;
import ch.usi.dag.disl.annotation.After;
import ch.usi.dag.disl.annotation.AfterReturning;
import ch.usi.dag.disl.annotation.AfterThrowing;
import ch.usi.dag.disl.annotation.ArgumentProcessor;
import ch.usi.dag.disl.annotation.Before;
import ch.usi.dag.disl.annotation.Guarded;
import ch.usi.dag.disl.annotation.ProcessAlso;
import ch.usi.dag.disl.annotation.SyntheticLocal;
import ch.usi.dag.util.Sets;
public final class DislClassFinder implements Processor {
private final Set <String> __dislClasses = Sets.newHashSet ();
//
private Messager __messager;
private PrintStream __output;
private String __separator;
private String __effectiveSeparator;
private boolean __forceEol;
//
private static final String __OPTION_OUTPUT__ = "disl.classfinder.output";
private static final String __OPTION_SEPARATOR__ = "disl.classfinder.separator";
private static final String __OPTION_FORCE_EOL__ = "disl.classfinder.force-eol";
private static final Set <String> __options__ = Collections.unmodifiableSet (
Sets.newHashSet (__OPTION_OUTPUT__, __OPTION_SEPARATOR__)
);
@Override
public Set <String> getSupportedOptions () {
return __options__;
}
private static final Class <?> [] __annotations__ = new Class <?> [] {
Before.class, After.class, AfterReturning.class, AfterThrowing.class,
ArgumentProcessor.class, Guarded.class, ProcessAlso.class,
SyntheticLocal.class, ThreadLocal.class
};
@Override
public Set <String> getSupportedAnnotationTypes () {
final Set <String> result = Sets.newLinkedHashSet ();
for (final Class <?> annotationClass : __annotations__) {
result.add (annotationClass.getName ());
}
return result;
}
@Override
public SourceVersion getSupportedSourceVersion () {
return SourceVersion.latestSupported ();
}
@Override
public void init (final ProcessingEnvironment env) {
__messager = env.getMessager ();
//
// Configure the class finder output. If no output is specified,
// the processor will not do any work. If "-" is specified as output,
// the system output will be used.
//
final String fileName = env.getOptions ().get (__OPTION_OUTPUT__);
if (fileName != null && !fileName.isEmpty ()) {
if ("-".equals (fileName)) {
__output = System.out;
} else {
try {
__output = new PrintStream (fileName);
} catch (final FileNotFoundException e) {
__messager.printMessage (Kind.WARNING, String.format (
"failed to create %s: %s", fileName, e.getMessage ()
));
}
}
}
//
// Configure class name separator. Use a new line by default.
// The effective separator starts as an empty string and after the
// first class printed, it becomes the configured separator.
//
final String separator = env.getOptions ().get (__OPTION_SEPARATOR__);
__separator = (separator != null) ? separator : "\n";
__effectiveSeparator = "";
//
// Configure whether to force end-of-line at the end of file.
// By default, EOL is not forced.
//
final String forceEol = env.getOptions ().get (__OPTION_FORCE_EOL__);
__forceEol = (forceEol != null) ? Boolean.parseBoolean (forceEol) : false;
}
@Override
public boolean process (
final Set <? extends TypeElement> annotations,
final RoundEnvironment env
) {
//
// If no file name is set, ignore the annotations.
//
if (__output == null) {
return false;
}
//
// The annotations may be processed in multiple rounds. Collect
// DiSL classes in each round and only report those not seen before.
//
final Set <String> newClasses = Sets.newHashSet ();
for (final TypeElement te : annotations) {
for (final Element e : env.getElementsAnnotatedWith (te)) {
final String className = __getEnlosingClassName (e);
if (__dislClasses.add (className)) {
newClasses.add (className);
}
}
}
for (final String dislClass : newClasses) {
__output.print (__effectiveSeparator);
__output.print (dislClass);
__effectiveSeparator = __separator;
}
//
// Force a new line in the output file at the end of processing
// and close the output unless it is system output.
//
if (env.processingOver ()) {
if (__forceEol && __dislClasses.size () > 0) {
__output.println ();
}
__output.flush ();
if (__output != System.out) {
__output.close ();
}
}
return true;
}
private String __getEnlosingClassName (final Element initial) {
//
// Our annotations apply to classes, methods, and fields.
// For methods and fields, traverse the chain of enclosing
// elements to the top-level class.
//
Element current = initial;
while (true) {
final Element parent = current.getEnclosingElement ();
if (parent == null || parent.getKind () == ElementKind.PACKAGE) {
return current.toString ();
}
current = parent;
}
}
@Override
public Iterable <? extends Completion> getCompletions (
final Element element, final AnnotationMirror annotation,
final ExecutableElement member, final String userText
) {
return Collections.emptyList ();
}
}
Manifest-Version: 1.0
DiSL-Classes: ch.usi.dag.disl.test.suite.guard.instr.DiSLClass:ch.usi.dag.disl.test.suite.guard.instr.ProcessorTest
\ No newline at end of file
Manifest-Version: 1.0
DiSL-Classes: ch.usi.dag.disl.test.suite.processor.instr.DiSLClass:ch.usi.dag.disl.test.suite.processor.instr.ProcessorTest:ch.usi.dag.disl.test.suite.processor.instr.ProcessorTest2
\ No newline at end of file
package ch.usi.dag.util;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.TreeSet;
/**
* Utility class providing generic static factory methods for creating instances
* of {@link Set} collection types. The factory methods rely on type inference
* to determine the type parameters needed for constructing a specific instance.
*
* @author Lubomir Bulej
*/
public final class Sets {
private Sets () {
// pure static class - not to be instantiated
}
/* ***********************************************************************
* HashSet
* ***********************************************************************/
/**
* Creates a new instance of a generic {@link HashSet}.
*
* @param <E>
* the type of the set element
* @return
* new {@link HashSet} instance of appropriate type
*/
public static <E> HashSet <E> newHashSet () {
return new HashSet <E> ();
}
/**
* Creates a new instance of a generic {@link HashSet} and fills it
* {@code elements} from the given array.
*
* @param <E>
* element type, inferred from the result type
* @param elements
* the elements to put into the set
* @return
* new {@link HashSet} instance containing the given elements
*/
public static <E> HashSet <E> newHashSet (final E ... elements) {
Assert.objectNotNull (elements, "elements");
//
return __addAllToSet (elements, new HashSet <E> (elements.length));
}
/* ***********************************************************************
* LinkedHashSet
* ***********************************************************************/
/**
* Creates a new instance of a generic {@link LinkedHashSet}.
*
* @param <E>
* the type of the set element
* @return
* new {@link LinkedHashSet} instance of appropriate type
*/
public static <E> LinkedHashSet <E> newLinkedHashSet () {
return new LinkedHashSet <E> ();
}
/* ***********************************************************************
* TreeSet
* ***********************************************************************/
/**
* Creates a new instance of a generic {@link TreeSet}.
*
* @param <E>
* the type of the set element
* @return
* new {@link TreeSet} instance of appropriate type
*/
public static <E> TreeSet <E> newTreeSet () {
return new TreeSet <E> ();
}
//
private static <E, L extends Set <E>> L __addAllToSet (
final E [] elements, final L result
) {
for (final E element : elements) {
result.add (element);
}
return result;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment