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 {
  65     public static void main(String... args) throws Exception {
  66         new RunCodingRules().run();
  67     }
  68 
  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                     "-XaddExports:jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED",
 115                     "-XaddExports:jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
 116                     "-XaddExports:jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED",
 117                     "-XaddExports:jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
 118                     "-XaddExports: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                     "-modulesourcepath", 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 
 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 }