| // Copyright 2014 The Bazel Authors. 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.build.lib.rules.java; |
| |
| import com.google.common.collect.ImmutableSet; |
| import com.google.devtools.build.lib.vfs.FileSystemUtils; |
| import com.google.devtools.build.lib.vfs.PathFragment; |
| |
| /** Utility methods for use by Java-related parts of the build system. */ |
| public final class JavaUtil { |
| |
| private JavaUtil() {} |
| |
| // ---------- Java related methods |
| |
| /* |
| * TODO(bazel-team): (2009) |
| * |
| * This way of figuring out Java source roots is basically |
| * broken. I think we need to support these two use cases: |
| * (1) A user puts their shell into a directory named java. |
| * (2) Someplace in the tree, there's a package named java. |
| * |
| * (1) is more important than (2); and (2) cannot always be guaranteed |
| * due to sloppy implementations in the past; most notably the old |
| * tools/boilerplate_rules.mk code for compiling Java. |
| * |
| * Basically, to implement correct semantics, we will need to configure |
| * Java source roots based on the package path, plus some heuristics to |
| * support legacy code, maybe. |
| * |
| * Roughly: |
| * Given a path, find the source root that applies to it by |
| * - walk over the elements in the package path |
| * - add "java", "javatests" to them |
| * - find the first element that is a maximal prefix to the Java file |
| * - for experimental, some legacy support that basically has some |
| * arbitrary padding before the Java sourceroot. |
| */ |
| |
| /** |
| * Given the filename of a Java source file, returns the name of the toplevel Java class defined |
| * within it. |
| */ |
| public static String getJavaClassName(PathFragment path) { |
| return FileSystemUtils.removeExtension(path.getBaseName()); |
| } |
| |
| /** Java source roots, used to relativize resource paths and infer main_class values. */ |
| public static final ImmutableSet<String> KNOWN_SOURCE_ROOTS = |
| ImmutableSet.of("java", "javatests", "src", "testsrc"); |
| |
| /** |
| * Finds the index of the segment in a Java path fragment that precedes the source root. Starts |
| * from the first "java" or "javatests" or "src" or "testsrc" segment. If the found item was |
| * "src", check if this is followed by "main" or "test" and then "java" or "resources" (maven |
| * layout). If the found item was "src", or "java"/"javatests" at the first segment, check for a |
| * nested root directory (src, java or javatests). A nested root must be followed by |
| * (com|net|org), or matching maven structure for nested "src", to be accepted, to avoid false |
| * positives. |
| * |
| * @param path a Java source dir or file path |
| * @return the index of the java segment or -1 iff no java segment was found. |
| */ |
| private static int javaSegmentIndex(PathFragment path) { |
| if (path.isAbsolute()) { |
| throw new IllegalArgumentException("path must not be absolute: '" + path + "'"); |
| } |
| int rootIndex = path.getFirstSegment(KNOWN_SOURCE_ROOTS); |
| if (rootIndex < 0) { |
| return rootIndex; |
| } |
| final boolean isSrc = "src".equals(path.getSegment(rootIndex)); |
| int checkMavenIndex = isSrc ? rootIndex : -1; |
| if (rootIndex == 0 || isSrc) { |
| // Check for a nested root directory. |
| for (int i = rootIndex + 1, max = path.segmentCount() - 2; i <= max; i++) { |
| String segment = path.getSegment(i); |
| if ("src".equals(segment) |
| || (isSrc && ("javatests".equals(segment) || "java".equals(segment)))) { |
| String next = path.getSegment(i + 1); |
| if ("com".equals(next) || "org".equals(next) || "net".equals(next)) { |
| // Check for common first element of java package, to avoid false positives. |
| rootIndex = i; |
| } else if ("src".equals(segment)) { |
| // Also accept maven style src/(main|test)/(java|resources). |
| checkMavenIndex = i; |
| } |
| break; |
| } |
| } |
| } |
| // Check for (main|test)/(java|resources) after /src/. |
| if (checkMavenIndex >= 0 && checkMavenIndex + 2 < path.segmentCount()) { |
| String next = path.getSegment(checkMavenIndex + 1); |
| if ("main".equals(next) || "test".equals(next)) { |
| next = path.getSegment(checkMavenIndex + 2); |
| if ("java".equals(next) || "resources".equals(next)) { |
| rootIndex = checkMavenIndex + 2; |
| } |
| } |
| } |
| return rootIndex; |
| } |
| |
| /** Given the PathFragment of a Java source file, returns the Java package to which it belongs. */ |
| public static String getJavaPackageName(PathFragment path) { |
| int index = javaSegmentIndex(path) + 1; |
| path = path.subFragment(index, path.segmentCount() - 1); |
| return path.getPathString().replace('/', '.'); |
| } |
| |
| /** |
| * Given the PathFragment of a file without extension, returns the Java fully qualified class name |
| * based on the Java root relative path of the specified path or 'null' if no java root can be |
| * determined. |
| * |
| * <p>For example, "java/foo/bar/wiz" and "javatests/foo/bar/wiz" both result in "foo.bar.wiz". |
| * |
| * <p>TODO(bazel-team): (2011) We need to have a more robust way to determine the Java root of a |
| * relative path rather than simply trying to find the "java" or "javatests" or "src" directory. |
| */ |
| public static String getJavaFullClassname(PathFragment path) { |
| PathFragment javaPath = getJavaPath(path); |
| if (javaPath != null) { |
| return javaPath.getPathString().replace('/', '.'); |
| } |
| return null; |
| } |
| |
| /** |
| * Given the PathFragment of a Java source file, returns the Java root relative path or 'null' if |
| * no java root can be determined. |
| * |
| * <p>For example, "{workspace}/java/foo/bar/wiz" and "{workspace}/javatests/foo/bar/wiz" both |
| * result in "foo/bar/wiz". |
| * |
| * <p>TODO(bazel-team): (2011) We need to have a more robust way to determine the Java root of a |
| * relative path rather than simply trying to find the "java" or "javatests" directory. |
| */ |
| public static PathFragment getJavaPath(PathFragment path) { |
| int index = javaSegmentIndex(path); |
| if (index >= 0) { |
| return path.subFragment(index + 1); |
| } |
| return null; |
| } |
| |
| /** |
| * Given the PathFragment of a Java source file, returns the Java root of the specified path or |
| * 'null' if no java root can be determined. |
| * |
| * <p>Example 1: "{workspace}/java/foo/bar/wiz" and "{workspace}/javatests/foo/bar/wiz" result in |
| * "{workspace}/java" and "{workspace}/javatests" Example 2: "java/foo/bar/wiz" and |
| * "javatests/foo/bar/wiz" result in "java" and "javatests" |
| * |
| * <p>TODO(bazel-team): (2011) We need to have a more robust way to determine the Java root of a |
| * relative path rather than simply trying to find the "java" or "javatests" directory. |
| */ |
| public static PathFragment getJavaRoot(PathFragment path) { |
| int index = javaSegmentIndex(path); |
| if (index >= 0) { |
| return path.subFragment(0, index + 1); |
| } |
| return null; |
| } |
| } |