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