From e27959f655d7bcaaacc963a107501bdd20696e8e Mon Sep 17 00:00:00 2001 From: Sam Blackshear Date: Tue, 12 Jan 2016 15:21:03 -0800 Subject: [PATCH] adding annotation processor to build class -> source map, with proper deps this time Reviewed By: jeremydubreil Differential Revision: D2823991 fb-gh-sync-id: 78f0d2c --- infer/annotations/Makefile | 2 +- .../annotprocess/ClassToSourceMapper.java | 102 ++++++++++++++++++ .../infer/annotprocess/JSONOutputUtils.java | 10 +- 3 files changed, 105 insertions(+), 9 deletions(-) create mode 100644 infer/annotations/com/facebook/infer/annotprocess/ClassToSourceMapper.java diff --git a/infer/annotations/Makefile b/infer/annotations/Makefile index 0f05e86c5..e6deb6dda 100644 --- a/infer/annotations/Makefile +++ b/infer/annotations/Makefile @@ -27,7 +27,7 @@ $(ANNOTATIONS_JAR): $(ANNOT_SOURCES) $(PROCESSOR_JAR): $(PROCESSOR_SOURCES) mkdir -p $(PROCESSOR_CLASSES) - javac $(PROCESSOR_SOURCES) -d $(PROCESSOR_CLASSES) + javac $(PROCESSOR_SOURCES) -cp .:$(JAVA_HOME)/lib/tools.jar -d $(PROCESSOR_CLASSES) jar cvMf processor.jar -C resources META-INF -C $(PROCESSOR_CLASSES) com mv processor.jar $(PROCESSOR_JAR) diff --git a/infer/annotations/com/facebook/infer/annotprocess/ClassToSourceMapper.java b/infer/annotations/com/facebook/infer/annotprocess/ClassToSourceMapper.java new file mode 100644 index 000000000..8e28dd997 --- /dev/null +++ b/infer/annotations/com/facebook/infer/annotprocess/ClassToSourceMapper.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2015 - present Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +package com.facebook.infer.annotprocess; + +import com.sun.source.tree.CompilationUnitTree; +import com.sun.source.util.Trees; +import com.sun.source.util.TreePath; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.annotation.processing.SupportedOptions; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.Element; +import javax.lang.model.element.TypeElement; +import javax.lang.model.util.Elements; + +@SupportedOptions({ "classSourceMapOutputFilename" }) +// this says: "process all classes, even ones without any annotations" +@SupportedAnnotationTypes({ "*" }) +public class ClassToSourceMapper extends AbstractProcessor { + + // map from class name -> absolute path to source file + // e.g., com.example.MyClass -> = /Users/me/MyClass.Java + // note that this map does not contain inner or anonymous classes + private Map mClassSourceMap = new LinkedHashMap(); + + private static final String DEFAULT_OUTPUT_FILENAME = "classSourceMap.json"; + + private static final String OUTPUT_FILENAME_OPTION = "classSourceMapOutputFilename"; + + private void exportClassSourceMap(String filename) throws FileNotFoundException, IOException { + try (PrintWriter out = new PrintWriter(filename)) { + out.println("{"); + int elemCount = 0; + int elemMax = mClassSourceMap.size(); + for (Map.Entry entry : mClassSourceMap.entrySet()) { + String className = entry.getKey(); + String sourcePath = entry.getValue(); + JSONOutputUtils.outputClassSourcePair(out, className, sourcePath, ++elemCount, elemMax); + } + out.println("}"); + } + } + + private void makeClassSourceMap(RoundEnvironment env) { + Set rootEnv = env.getRootElements(); + Trees trees = Trees.instance(processingEnv); + for (Element e : rootEnv) { + if (e instanceof TypeElement) { // class or interface + TreePath path = trees.getPath(e); + TypeElement typeElem = (TypeElement) e; + CompilationUnitTree compilationUnit = path.getCompilationUnit(); + String absoluteSourcePath = compilationUnit.getSourceFile().toUri().getPath(); + // map a class name to its source file. this will only capture top-level class names; inner + // classes (anonymous and otherwise) are dealt with later reading each top-level class's + // list of inner classes and mapping each one to the source file of the parent class + mClassSourceMap.put(typeElem.getQualifiedName().toString(), absoluteSourcePath); + } + } + } + + @Override + public boolean process(Set annotations, RoundEnvironment env) { + makeClassSourceMap(env); + if (env.processingOver()) { + try { + Map options = processingEnv.getOptions(); + String outputFilename = options.get(OUTPUT_FILENAME_OPTION); + if (outputFilename == null) { + outputFilename = DEFAULT_OUTPUT_FILENAME; + } + exportClassSourceMap(outputFilename); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + return false; + } + + @Override + public SourceVersion getSupportedSourceVersion() { + return SourceVersion.latestSupported(); + } + +} diff --git a/infer/annotations/com/facebook/infer/annotprocess/JSONOutputUtils.java b/infer/annotations/com/facebook/infer/annotprocess/JSONOutputUtils.java index 839ac5184..fe422964d 100644 --- a/infer/annotations/com/facebook/infer/annotprocess/JSONOutputUtils.java +++ b/infer/annotations/com/facebook/infer/annotprocess/JSONOutputUtils.java @@ -54,14 +54,8 @@ public class JSONOutputUtils { public static void outputClassSourcePair(PrintWriter out, String clazz, String source, int elemCount, int elemMax) { - String TAB1 = " "; - String TAB2 = TAB1 + TAB1; - out.println(TAB1 + "{"); - out.print(TAB2 + "\"class\": \"" + clazz + "\""); - out.println(","); - out.println(TAB2 + "\"source\": \"" + source + "\""); - - out.print(TAB1 + "}"); + String TAB = " "; + out.print(TAB + "\"" + clazz + "\": \"" + source + "\""); outputCommaIfNotLast(out, elemCount, elemMax); }