1 /* 2 * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /* 25 * @test 26 * @bug 8043643 27 * @summary Run the langtools coding rules over the langtools source code. 28 * @modules jdk.compiler/com.sun.tools.javac.util 29 */ 30 31 32 import java.io.*; 33 import java.lang.reflect.Method; 34 import java.net.URL; 35 import java.net.URLClassLoader; 36 import java.nio.file.Files; 37 import java.nio.file.Path; 38 import java.nio.file.Paths; 39 import java.nio.file.StandardOpenOption; 40 import java.util.*; 41 import java.util.stream.Collectors; 42 import java.util.stream.Stream; 43 44 import javax.tools.Diagnostic; 45 import javax.tools.DiagnosticListener; 46 import javax.tools.JavaCompiler; 47 import javax.tools.JavaFileObject; 48 import javax.tools.StandardJavaFileManager; 49 import javax.tools.ToolProvider; 50 51 import com.sun.tools.javac.util.Assert; 52 53 /** 54 * This is a test to verify specific coding standards for source code in the langtools repository. 55 * 56 * As such, it is not a standard unit, regression or functional test, and will 57 * automatically skip if the langtools source code is not available. 58 * 59 * If the source code is available, it will find and compile the coding 60 * style analyzers found in langtools/make/tools/crules/*.java, and run the resulting 61 * code on all source files under langtools/src/share/classes. Any coding style 62 * violations will cause the test to fail. 63 */ 64 public class RunCodingRules { main(String... args)65 public static void main(String... args) throws Exception { 66 new RunCodingRules().run(); 67 } 68 run()69 public void run() throws Exception { 70 Path testSrc = Paths.get(System.getProperty("test.src", ".")); 71 Path targetDir = Paths.get("."); 72 List<Path> sourceDirs = null; 73 Path crulesDir = null; 74 Path mainSrcDir = null; 75 for (Path d = testSrc; d != null; d = d.getParent()) { 76 if (Files.exists(d.resolve("TEST.ROOT"))) { 77 d = d.getParent(); 78 Path toolsPath = d.resolve("make/tools"); 79 if (Files.exists(toolsPath)) { 80 mainSrcDir = d.resolve("src"); 81 crulesDir = toolsPath; 82 sourceDirs = Files.walk(mainSrcDir, 1) 83 .map(p -> p.resolve("share/classes")) 84 .filter(p -> Files.isDirectory(p)) 85 .collect(Collectors.toList()); 86 break; 87 } 88 } 89 } 90 91 if (sourceDirs == null || crulesDir == null) { 92 System.err.println("Warning: sources not found, test skipped."); 93 return ; 94 } 95 96 JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler(); 97 try (StandardJavaFileManager fm = javaCompiler.getStandardFileManager(null, null, null)) { 98 DiagnosticListener<JavaFileObject> noErrors = diagnostic -> { 99 Assert.check(diagnostic.getKind() != Diagnostic.Kind.ERROR, diagnostic.toString()); 100 }; 101 String FS = File.separator; 102 String PS = File.pathSeparator; 103 104 //compile crules: 105 List<File> crulesFiles = Files.walk(crulesDir) 106 .filter(entry -> entry.getFileName().toString().endsWith(".java")) 107 .filter(entry -> entry.getParent().endsWith("crules")) 108 .map(entry -> entry.toFile()) 109 .collect(Collectors.toList()); 110 111 Path crulesTarget = targetDir.resolve("crules"); 112 Files.createDirectories(crulesTarget); 113 List<String> crulesOptions = Arrays.asList( 114 "--add-exports", "jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED", 115 "--add-exports", "jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED", 116 "--add-exports", "jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED", 117 "--add-exports", "jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED", 118 "--add-exports", "jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED", 119 "-d", crulesTarget.toString()); 120 javaCompiler.getTask(null, fm, noErrors, crulesOptions, null, 121 fm.getJavaFileObjectsFromFiles(crulesFiles)).call(); 122 Path registration = crulesTarget.resolve("META-INF/services/com.sun.source.util.Plugin"); 123 Files.createDirectories(registration.getParent()); 124 try (Writer metaInfServices = Files.newBufferedWriter(registration, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) { 125 metaInfServices.write("crules.CodingRulesAnalyzerPlugin\n"); 126 } 127 128 //generate CompilerProperties.java: 129 List<File> propertiesParserFiles = 130 Files.walk(crulesDir.resolve("propertiesparser")) 131 .filter(entry -> entry.getFileName().toString().endsWith(".java")) 132 .map(entry -> entry.toFile()) 133 .collect(Collectors.toList()); 134 135 Path propertiesParserTarget = targetDir.resolve("propertiesParser"); 136 Files.createDirectories(propertiesParserTarget); 137 List<String> propertiesParserOptions = Arrays.asList( 138 "-d", propertiesParserTarget.toString()); 139 javaCompiler.getTask(null, fm, noErrors, propertiesParserOptions, null, 140 fm.getJavaFileObjectsFromFiles(propertiesParserFiles)).call(); 141 142 Path genSrcTarget = targetDir.resolve("gensrc"); 143 144 ClassLoader propertiesParserLoader = new URLClassLoader(new URL[] { 145 propertiesParserTarget.toUri().toURL(), 146 crulesDir.toUri().toURL() 147 }); 148 Class propertiesParserClass = 149 Class.forName("propertiesparser.PropertiesParser", false, propertiesParserLoader); 150 Method propertiesParserRun = 151 propertiesParserClass.getDeclaredMethod("run", String[].class, PrintStream.class); 152 String compilerProperties = 153 "jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties"; 154 Path propertiesPath = mainSrcDir.resolve(compilerProperties.replace("/", FS)); 155 Path genSrcTargetDir = genSrcTarget.resolve(mainSrcDir.relativize(propertiesPath.getParent())); 156 157 Files.createDirectories(genSrcTargetDir); 158 String[] propertiesParserRunOptions = new String[] { 159 "-compile", propertiesPath.toString(), genSrcTargetDir.toString() 160 }; 161 162 Object result = propertiesParserRun.invoke(null, propertiesParserRunOptions, System.err); 163 164 if (!(result instanceof Boolean) || !(Boolean) result) { 165 throw new AssertionError("Cannot parse properties: " + result); 166 } 167 168 //compile langtools sources with crules enabled: 169 List<File> sources = sourceDirs.stream() 170 .flatMap(dir -> silentFilesWalk(dir)) 171 .filter(entry -> entry.getFileName().toString().endsWith(".java")) 172 .map(p -> p.toFile()) 173 .collect(Collectors.toList()); 174 175 Path sourceTarget = targetDir.resolve("classes"); 176 Files.createDirectories(sourceTarget); 177 String processorPath = crulesTarget + PS + crulesDir; 178 179 List<String> options = Arrays.asList( 180 "-d", sourceTarget.toString(), 181 "--module-source-path", mainSrcDir + FS + "*" + FS + "share" + FS + "classes" + PS 182 + genSrcTarget + FS + "*" + FS + "share" + FS + "classes", 183 "-XDaccessInternalAPI", 184 "-processorpath", processorPath, 185 "-Xplugin:coding_rules"); 186 javaCompiler.getTask(null, fm, noErrors, options, null, 187 fm.getJavaFileObjectsFromFiles(sources)).call(); 188 } 189 } 190 silentFilesWalk(Path dir)191 Stream<Path> silentFilesWalk(Path dir) throws IllegalStateException { 192 try { 193 return Files.walk(dir); 194 } catch (IOException ex) { 195 throw new IllegalStateException(ex); 196 } 197 } 198 } 199