Java tutorial
/* * Copyright 2014 Google Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.devtools.kythe.extractors.java.standalone; import com.google.common.base.Joiner; import com.google.common.base.Strings; import com.google.common.base.Throwables; import com.google.common.collect.Lists; import com.google.common.hash.Hashing; import com.google.common.io.Files; import com.google.devtools.kythe.extractors.java.JavaCompilationUnitExtractor; import com.google.devtools.kythe.extractors.shared.CompilationDescription; import com.google.devtools.kythe.extractors.shared.FileVNames; import com.google.devtools.kythe.extractors.shared.IndexInfoUtils; import com.google.devtools.kythe.platform.indexpack.Archive; import com.google.devtools.kythe.proto.Analysis.CompilationUnit; import com.google.devtools.kythe.proto.Analysis.FileData; import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.Collection; import java.util.Collections; import java.util.List; /** * General logic for a javac-based {@link CompilationUnit} extractor. * * Environment Variables Used: * KYTHE_VNAMES: optional path to a JSON configuration file for {@link FileVNames} to populate * the {@link CompilationUnit}'s required input {@link VName}s * * KYTHE_CORPUS: if KYTHE_VNAMES is not given, all {@link VName}s will be populated with this * corpus (default {@link DEFAULT_CORPUS}) * * KYTHE_ROOT_DIRECTORY: required root path for file inputs; the {@link FileData} paths stored in * the {@link CompilationUnit} will be made to be relative to this directory * * KYTHE_OUTPUT_DIRECTORY: required directory path to store the resulting .kindex file * * KYTHE_INDEX_PACK: if set to a non-empty value, interpret KYTHE_OUTPUT_DIRECTORY as the root of * an indexpack instead of a collection of .kindex files */ public abstract class AbstractJavacWrapper { public static final String DEFAULT_CORPUS = "kythe"; protected abstract CompilationDescription processCompilation(String[] arguments, JavaCompilationUnitExtractor javaCompilationUnitExtractor) throws Exception; protected abstract void passThrough(String[] args) throws Exception; /** * Given the command-line arguments to javac, construct a {@link CompilationUnit} and write it to * a .kindex file or indexpack. Parameters to the extraction logic are passed by environment * variables (see class comment). */ public void process(String[] args) { try { if (!passThroughIfAnalysisOnly(args)) { String vnamesConfig = System.getenv("KYTHE_VNAMES"); JavaCompilationUnitExtractor extractor; if (Strings.isNullOrEmpty(vnamesConfig)) { String corpus = readEnvironmentVariable("KYTHE_CORPUS", DEFAULT_CORPUS); extractor = new JavaCompilationUnitExtractor(corpus, readEnvironmentVariable("KYTHE_ROOT_DIRECTORY")); } else { extractor = new JavaCompilationUnitExtractor(FileVNames.fromFile(vnamesConfig), readEnvironmentVariable("KYTHE_ROOT_DIRECTORY")); } CompilationDescription indexInfo = processCompilation(getCleanedUpArguments(args), extractor); String outputDir = readEnvironmentVariable("KYTHE_OUTPUT_DIRECTORY"); if (Strings.isNullOrEmpty(System.getenv("KYTHE_INDEX_PACK"))) { writeIndexInfoToFile(outputDir, indexInfo); } else { new Archive(outputDir).writeDescription(indexInfo); } CompilationUnit compilationUnit = indexInfo.getCompilationUnit(); if (compilationUnit.getHasCompileErrors()) { System.err.println("Errors encountered during compilation"); System.exit(1); } } } catch (IOException e) { System.err.println( String.format("Unexpected IO error (probably while writing to index file): %s", e.toString())); System.err.println(Throwables.getStackTraceAsString(e)); System.exit(2); } catch (Exception e) { System.err.println( String.format("Unexpected error compiling and indexing java compilation: %s", e.toString())); System.err.println(Throwables.getStackTraceAsString(e)); System.exit(2); } } private static String[] getCleanedUpArguments(String[] args) throws IOException { // Expand all @file arguments List<String> expandedArgs = Lists.newArrayList(); for (String arg : args) { if (arg.startsWith("@")) { File file = new File(arg.substring(1)); for (String line : Files.readLines(file, StandardCharsets.UTF_8)) { expandedArgs.add(line.replaceAll("^\"(.*)\"$", "$1")); } } else { expandedArgs.add(arg); } } // We skip some arguments that would normally be passed to javac: // -J, these are flags to the java environment running javac. // -XD, these are internal flags used for special debug information // when compiling javac itself. // -Werror, we do not want to treat any warnings as errors. // -target, we do not care about the compiler outputs boolean skipArg = false; List<String> cleanedUpArgs = Lists.newArrayList(); for (String arg : expandedArgs) { if (arg.equals("-target")) { skipArg = true; continue; } else if (!(skipArg || arg.startsWith("-J") || arg.startsWith("-XD") || arg.startsWith("-Werror"))) { cleanedUpArgs.add(arg); } skipArg = false; } String[] cleanedUpArgsArray = new String[cleanedUpArgs.size()]; return cleanedUpArgs.toArray(cleanedUpArgsArray); } private static void writeIndexInfoToFile(String rootDirectory, CompilationDescription indexInfo) throws IOException { String name = indexInfo.getCompilationUnit().getVName().getSignature().trim().replaceAll("^/+|/+$", "") .replace('/', '_'); String path = IndexInfoUtils.getIndexFilename(rootDirectory, name); IndexInfoUtils.writeIndexInfoToFile(indexInfo, path); } private boolean passThroughIfAnalysisOnly(String[] args) throws Exception { // If '-proc:only' is passed as an argument, this is not a source file compilation, but an // analysis. We will let the real java compiler do its work. boolean hasProcOnly = false; for (String arg : args) { if (arg.equals("-proc:only")) { hasProcOnly = true; break; } } if (hasProcOnly) { passThrough(args); return true; } return false; } protected static String createTargetFromSourceFiles(List<String> sourceFiles) { List<String> sortedSourceFiles = Lists.newArrayList(sourceFiles); Collections.sort(sortedSourceFiles); String joinedSourceFiles = Joiner.on(":").join(sortedSourceFiles); return "#" + Hashing.sha256().hashUnencodedChars(joinedSourceFiles).toString(); } protected static List<String> splitPaths(String path) { List<String> paths = Lists.newArrayList(); if (path != null) { for (String cp : path.split(":")) { paths.add(cp); } } return paths; } static String readEnvironmentVariable(String variableName) { return readEnvironmentVariable(variableName, null); } static String readEnvironmentVariable(String variableName, String defaultValue) { String result = System.getenv(variableName); if (Strings.isNullOrEmpty(result)) { if (Strings.isNullOrEmpty(defaultValue)) { System.err.println(String.format("Missing environment variable: %s", variableName)); System.exit(1); } result = defaultValue; } return result; } protected static List<String> getSourceList(Collection<File> files) { List<String> sources = Lists.newArrayList(); for (File file : files) { sources.add(file.getPath()); } return sources; } }