aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build
diff options
context:
space:
mode:
authorGravatar tomlu <tomlu@google.com>2017-12-22 00:56:58 -0800
committerGravatar Copybara-Service <copybara-piper@google.com>2017-12-22 00:58:31 -0800
commite63a867feb2483851b5169894ff7b3c0bdb26581 (patch)
tree68306264ab9dc8f2383f987a42fb768b2d6436a8 /src/main/java/com/google/devtools/build
parent85e8d51c72c5dcb4d1f6a8f3186149339ca59fac (diff)
Redo FileType to reduce generated garbage.
* Change FileType to no longer assume it operates on just the base name (it can now be given a full path). * Move the responsibility to specific classes (Artifact, Path, PathFragment) to decide how they want to offer up a string that includes the file name. * Flip the order in which users are expected to check Artifact type, from FileType#matches(Artifact) to Artifact#isFileType(FileType). This looks natural and should encourage developers to use efficient file type checking methods. * Change CppCompileAction to use the new API. RELNOTES: None PiperOrigin-RevId: 179903239
Diffstat (limited to 'src/main/java/com/google/devtools/build')
-rw-r--r--src/main/java/com/google/devtools/build/lib/BUILD33
-rw-r--r--src/main/java/com/google/devtools/build/lib/actions/Artifact.java131
-rw-r--r--src/main/java/com/google/devtools/build/lib/analysis/configuredtargets/FileConfiguredTarget.java12
-rw-r--r--src/main/java/com/google/devtools/build/lib/packages/FileTarget.java14
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileAction.java22
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/cpp/CppFileTypes.java155
-rw-r--r--src/main/java/com/google/devtools/build/lib/util/FileType.java129
-rw-r--r--src/main/java/com/google/devtools/build/lib/util/FileTypeSet.java21
-rw-r--r--src/main/java/com/google/devtools/build/lib/vfs/BUILD2
-rw-r--r--src/main/java/com/google/devtools/build/lib/vfs/Path.java9
-rw-r--r--src/main/java/com/google/devtools/build/lib/vfs/PathFragment.java8
11 files changed, 309 insertions, 227 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/BUILD b/src/main/java/com/google/devtools/build/lib/BUILD
index 499c3b5114..c48d1efc27 100644
--- a/src/main/java/com/google/devtools/build/lib/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/BUILD
@@ -97,11 +97,10 @@ java_library(
java_library(
name = "base-util",
srcs = [
- "util/StringCanonicalizer.java",
"util/VarInt.java",
],
+ exports = [":string_util"],
deps = [
- "//src/main/java/com/google/devtools/build/lib/concurrent",
"//third_party:guava",
],
)
@@ -161,19 +160,24 @@ java_library(
"util/BlazeClock.java",
"util/Clock.java",
"util/ExitCode.java",
+ "util/FileType.java",
+ "util/FileTypeSet.java",
"util/JavaClock.java",
"util/OS.java",
"util/ProcessUtils.java",
"util/SingleLineFormatter.java",
"util/StringCanonicalizer.java",
"util/StringTrie.java",
+ "util/StringUtil.java",
"util/VarInt.java",
],
),
exports = [
"//src/main/java/com/google/devtools/build/lib:base-util",
"//src/main/java/com/google/devtools/build/lib:exitcode-external",
+ "//src/main/java/com/google/devtools/build/lib:filetype",
"//src/main/java/com/google/devtools/build/lib:os_util",
+ "//src/main/java/com/google/devtools/build/lib:string_util",
"//src/main/java/com/google/devtools/build/lib/clock",
"//src/main/java/com/google/devtools/build/lib/collect",
],
@@ -196,6 +200,31 @@ java_library(
)
java_library(
+ name = "filetype",
+ srcs = [
+ "util/FileType.java",
+ "util/FileTypeSet.java",
+ ],
+ deps = [
+ ":string_util",
+ "//third_party:guava",
+ "//third_party:jsr305",
+ ],
+)
+
+java_library(
+ name = "string_util",
+ srcs = [
+ "util/StringCanonicalizer.java",
+ "util/StringUtil.java",
+ ],
+ deps = [
+ "//src/main/java/com/google/devtools/build/lib/concurrent",
+ "//third_party:guava",
+ ],
+)
+
+java_library(
name = "exitcode-external",
srcs = [
"util/ExitCode.java",
diff --git a/src/main/java/com/google/devtools/build/lib/actions/Artifact.java b/src/main/java/com/google/devtools/build/lib/actions/Artifact.java
index e47d0e3bd6..7468de1759 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/Artifact.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/Artifact.java
@@ -38,6 +38,7 @@ import com.google.devtools.build.lib.skylarkinterface.SkylarkValue;
import com.google.devtools.build.lib.syntax.EvalUtils;
import com.google.devtools.build.lib.syntax.EvalUtils.ComparisonException;
import com.google.devtools.build.lib.util.FileType;
+import com.google.devtools.build.lib.util.FileTypeSet;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.lib.vfs.PathFragment;
import java.util.ArrayList;
@@ -48,67 +49,73 @@ import java.util.Objects;
import javax.annotation.Nullable;
/**
- * An Artifact represents a file used by the build system, whether it's a source
- * file or a derived (output) file. Not all Artifacts have a corresponding
- * FileTarget object in the <code>build.lib.packages</code> API: for example,
- * low-level intermediaries internal to a given rule, such as a Java class files
- * or C++ object files. However all FileTargets have a corresponding Artifact.
+ * An Artifact represents a file used by the build system, whether it's a source file or a derived
+ * (output) file. Not all Artifacts have a corresponding FileTarget object in the <code>
+ * build.lib.packages</code> API: for example, low-level intermediaries internal to a given rule,
+ * such as a Java class files or C++ object files. However all FileTargets have a corresponding
+ * Artifact.
*
- * <p>In any given call to SkyframeExecutor#buildArtifacts(), no two Artifacts in the
- * action graph may refer to the same path.
+ * <p>In any given call to SkyframeExecutor#buildArtifacts(), no two Artifacts in the action graph
+ * may refer to the same path.
+ *
+ * <p>Artifacts generally fall into two classifications, source and derived, but there exist a few
+ * other cases that are fuzzy and difficult to classify. The following cases exist:
*
- * <p>Artifacts generally fall into two classifications, source and derived, but
- * there exist a few other cases that are fuzzy and difficult to classify. The
- * following cases exist:
* <ul>
- * <li>Well-formed source Artifacts will have null generating Actions and a root
- * that is orthogonal to execRoot. (With the root coming from the package path.)
- * <li>Well-formed derived Artifacts will have non-null generating Actions, and
- * a root that is below execRoot.
- * <li>Symlinked include source Artifacts under the output/include tree will
- * appear to be derived artifacts with null generating Actions.
- * <li>Some derived Artifacts, mostly in the genfiles tree and mostly discovered
- * during include validation, will also have null generating Actions.
+ * <li>Well-formed source Artifacts will have null generating Actions and a root that is
+ * orthogonal to execRoot. (With the root coming from the package path.)
+ * <li>Well-formed derived Artifacts will have non-null generating Actions, and a root that is
+ * below execRoot.
+ * <li>Symlinked include source Artifacts under the output/include tree will appear to be derived
+ * artifacts with null generating Actions.
+ * <li>Some derived Artifacts, mostly in the genfiles tree and mostly discovered during include
+ * validation, will also have null generating Actions.
* </ul>
*
- * In the usual case, an Artifact represents a single file. However, an Artifact may
- * also represent the following:
+ * In the usual case, an Artifact represents a single file. However, an Artifact may also represent
+ * the following:
+ *
* <ul>
- * <li>A TreeArtifact, which is a directory containing a tree of unknown {@link Artifact}s.
- * In the future, Actions will be able to examine these files as inputs and declare them as outputs
- * at execution time, but this is not yet implemented. This is used for Actions where
- * the inputs and/or outputs might not be discoverable except during Action execution.
- * <li>A directory of unknown contents, but not a TreeArtifact.
- * This is a legacy facility and should not be used by any new rule implementations.
- * In particular, the file system cache integrity checks fail for directories.
- * <li>An 'aggregating middleman' special Artifact, which may be expanded using a
- * {@link ArtifactExpander} at Action execution time. This is used by a handful of rules to save
- * memory.
- * <li>A 'constant metadata' special Artifact. These represent real files, changes to which are
- * ignored by the build system. They are useful for files which change frequently but do not affect
- * the result of a build, such as timestamp files.
- * <li>A 'Fileset' special Artifact. This is a legacy type of Artifact and should not be used
- * by new rule implementations.
+ * <li>A TreeArtifact, which is a directory containing a tree of unknown {@link Artifact}s. In the
+ * future, Actions will be able to examine these files as inputs and declare them as outputs
+ * at execution time, but this is not yet implemented. This is used for Actions where the
+ * inputs and/or outputs might not be discoverable except during Action execution.
+ * <li>A directory of unknown contents, but not a TreeArtifact. This is a legacy facility and
+ * should not be used by any new rule implementations. In particular, the file system cache
+ * integrity checks fail for directories.
+ * <li>An 'aggregating middleman' special Artifact, which may be expanded using a {@link
+ * ArtifactExpander} at Action execution time. This is used by a handful of rules to save
+ * memory.
+ * <li>A 'constant metadata' special Artifact. These represent real files, changes to which are
+ * ignored by the build system. They are useful for files which change frequently but do not
+ * affect the result of a build, such as timestamp files.
+ * <li>A 'Fileset' special Artifact. This is a legacy type of Artifact and should not be used by
+ * new rule implementations.
* </ul>
- * <p>This class is "theoretically" final; it should not be subclassed except by
- * {@link SpecialArtifact}.
+ *
+ * <p>This class is "theoretically" final; it should not be subclassed except by {@link
+ * SpecialArtifact}.
*/
@Immutable
-@SkylarkModule(name = "File",
- category = SkylarkModuleCategory.BUILTIN,
- doc = "<p>This type represents a file or directory used by the build system. It can be "
- + "either a source file or a derived file produced by a rule.</p>"
- + "<p>The File constructor is private, so you cannot call it directly to create new "
- + "Files. If you have a Skylark rule that needs to create a new File, you have two options:"
- + "<ul>"
- + "<li>use <a href='actions.html#declare_file'>ctx.actions.declare_file</a> "
- + "or <a href='actions.html#declare_directory'>ctx.actions.declare_directory</a> to "
- + "declare a new file in the rule implementation.</li>"
- + "<li>add the label to the attrs (if it's an input) or the outputs (if it's an output)."
- + " Then you can access the File through the rule's "
- + "<a href='ctx.html#outputs'>ctx.outputs</a>.")
+@SkylarkModule(
+ name = "File",
+ category = SkylarkModuleCategory.BUILTIN,
+ doc =
+ "<p>This type represents a file or directory used by the build system. It can be "
+ + "either a source file or a derived file produced by a rule.</p>"
+ + "<p>The File constructor is private, so you cannot call it directly to create new "
+ + "Files. If you have a Skylark rule that needs to create a new File, "
+ + "you have two options:"
+ + "<ul>"
+ + "<li>use <a href='actions.html#declare_file'>ctx.actions.declare_file</a> "
+ + "or <a href='actions.html#declare_directory'>ctx.actions.declare_directory</a> to "
+ + "declare a new file in the rule implementation.</li>"
+ + "<li>add the label to the attrs (if it's an input) or the outputs (if it's an output)."
+ + " Then you can access the File through the rule's "
+ + "<a href='ctx.html#outputs'>ctx.outputs</a>."
+)
public class Artifact
- implements FileType.HasFilename, ActionInput, SkylarkValue, Comparable<Object> {
+ implements FileType.HasFileType, ActionInput, SkylarkValue, Comparable<Object> {
/** Compares artifact according to their exec paths. Sorts null values first. */
@SuppressWarnings("ReferenceEquality") // "a == b" is an optimization
@@ -281,7 +288,6 @@ public class Artifact
/**
* Returns the base file name of this artifact, similar to basename(1).
*/
- @Override
@SkylarkCallable(name = "basename", structField = true,
doc = "The base name of this file. This is the name of the file inside the directory.")
public final String getFilename() {
@@ -294,6 +300,27 @@ public class Artifact
}
/**
+ * Checks whether this artifact is of the supplied file type.
+ *
+ * <p>Prefer this method to pulling out strings from the Artifact and passing to {@link
+ * FileType#matches(String)} manually. This method has been optimized to generate a minimum of
+ * garbage.
+ */
+ public boolean isFileType(FileType fileType) {
+ return fileType.matches(this);
+ }
+
+ /** Checks whether this artifact is any of the supplied file types. */
+ public boolean isAnyFileType(FileTypeSet fileTypeSet) {
+ return fileTypeSet.matches(filePathForFileTypeMatcher());
+ }
+
+ @Override
+ public String filePathForFileTypeMatcher() {
+ return getExecPath().filePathForFileTypeMatcher();
+ }
+
+ /**
* Returns the artifact owner. May be null.
*/
@Nullable public final Label getOwner() {
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/configuredtargets/FileConfiguredTarget.java b/src/main/java/com/google/devtools/build/lib/analysis/configuredtargets/FileConfiguredTarget.java
index 4a009fe326..30e9f2f203 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/configuredtargets/FileConfiguredTarget.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/configuredtargets/FileConfiguredTarget.java
@@ -35,11 +35,11 @@ import com.google.devtools.build.lib.packages.Provider;
import com.google.devtools.build.lib.util.FileType;
/**
- * A ConfiguredTarget for a source FileTarget. (Generated files use a
- * subclass, OutputFileConfiguredTarget.)
+ * A ConfiguredTarget for a source FileTarget. (Generated files use a subclass,
+ * OutputFileConfiguredTarget.)
*/
public abstract class FileConfiguredTarget extends AbstractConfiguredTarget
- implements FileType.HasFilename, LicensesProvider {
+ implements FileType.HasFileType, LicensesProvider {
private final Artifact artifact;
private final TransitiveInfoProviderMap providers;
@@ -78,12 +78,16 @@ public abstract class FileConfiguredTarget extends AbstractConfiguredTarget
/**
* Returns the file name of this file target.
*/
- @Override
public String getFilename() {
return getTarget().getFilename();
}
@Override
+ public String filePathForFileTypeMatcher() {
+ return getFilename();
+ }
+
+ @Override
public <P extends TransitiveInfoProvider> P getProvider(Class<P> provider) {
AnalysisUtils.checkProvider(provider);
return providers.getProvider(provider);
diff --git a/src/main/java/com/google/devtools/build/lib/packages/FileTarget.java b/src/main/java/com/google/devtools/build/lib/packages/FileTarget.java
index a488eff5bc..51cdb1afa0 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/FileTarget.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/FileTarget.java
@@ -17,14 +17,14 @@ package com.google.devtools.build.lib.packages;
import com.google.common.base.Preconditions;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.packages.License.DistributionType;
-import com.google.devtools.build.lib.util.FileType.HasFilename;
+import com.google.devtools.build.lib.util.FileType;
import java.util.Set;
/**
- * Common superclass for InputFile and OutputFile which provides implementation
- * for the file operations in common.
+ * Common superclass for InputFile and OutputFile which provides implementation for the file
+ * operations in common.
*/
-public abstract class FileTarget implements Target, HasFilename {
+public abstract class FileTarget implements Target, FileType.HasFileType {
protected final Package pkg;
protected final Label label;
@@ -37,7 +37,6 @@ public abstract class FileTarget implements Target, HasFilename {
this.label = label;
}
- @Override
public String getFilename() {
return label.getName();
}
@@ -58,6 +57,11 @@ public abstract class FileTarget implements Target, HasFilename {
}
@Override
+ public String filePathForFileTypeMatcher() {
+ return getFilename();
+ }
+
+ @Override
public String toString() {
return getTargetKind() + "(" + getLabel() + ")"; // Just for debugging
}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileAction.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileAction.java
index 4a97d11181..4f7bce06a0 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileAction.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileAction.java
@@ -381,7 +381,7 @@ public class CppCompileAction extends AbstractAction
// discarded as orphans.
// This is strictly better than marking all transitive modules as inputs, which would also
// effectively disable orphan detection for .pcm files.
- if (CppFileTypes.CPP_MODULE.matches(outputFile.getFilename())) {
+ if (outputFile.isFileType(CppFileTypes.CPP_MODULE)) {
return ImmutableSet.of(outputFile);
}
return super.getMandatoryOutputs();
@@ -447,7 +447,7 @@ public class CppCompileAction extends AbstractAction
if (shouldPruneModules) {
Set<Artifact> initialResultSet = Sets.newLinkedHashSet(initialResult);
- if (CppFileTypes.CPP_MODULE.matches(sourceFile.getFilename())) {
+ if (sourceFile.isFileType(CppFileTypes.CPP_MODULE)) {
usedModules = ImmutableSet.of(sourceFile);
initialResultSet.add(sourceFile);
} else {
@@ -485,7 +485,7 @@ public class CppCompileAction extends AbstractAction
value, "Owner %s of %s not in graph %s", artifact.getArtifactOwner(), artifact, skyKey);
// We can get the generating action here because #canRemoveAfterExecution is overridden.
Preconditions.checkState(
- CppFileTypes.CPP_MODULE.matches(artifact.getFilename()),
+ artifact.isFileType(CppFileTypes.CPP_MODULE),
"Non-module? %s (%s %s)",
artifact,
this,
@@ -493,7 +493,7 @@ public class CppCompileAction extends AbstractAction
CppCompileAction action =
(CppCompileAction) value.getGeneratingActionDangerousReadJavadoc(artifact);
for (Artifact input : action.getInputs()) {
- if (CppFileTypes.CPP_MODULE.matches(input.getFilename())) {
+ if (input.isFileType(CppFileTypes.CPP_MODULE)) {
additionalModules.add(input);
}
}
@@ -631,7 +631,7 @@ public class CppCompileAction extends AbstractAction
@Override
public Artifact getMainIncludeScannerSource() {
- return CppFileTypes.CPP_MODULE_MAP.matches(getSourceFile().getPath())
+ return getSourceFile().isFileType(CppFileTypes.CPP_MODULE_MAP)
? Iterables.getFirst(context.getHeaderModuleSrcs(), null)
: getSourceFile();
}
@@ -639,7 +639,7 @@ public class CppCompileAction extends AbstractAction
@Override
public Collection<Artifact> getIncludeScannerSources() {
NestedSetBuilder<Artifact> builder = NestedSetBuilder.stableOrder();
- if (CppFileTypes.CPP_MODULE_MAP.matches(getSourceFile().getPath())) {
+ if (getSourceFile().isFileType(CppFileTypes.CPP_MODULE_MAP)) {
// If this is an action that compiles the header module itself, the source we build is the
// module map, and we need to include-scan all headers that are referenced in the module map.
// We need to do include scanning as long as we want to support building code bases that are
@@ -702,7 +702,7 @@ public class CppCompileAction extends AbstractAction
public boolean canRemoveAfterExecution() {
// Module-generating actions are needed because the action may be retrieved in
// #discoverInputsStage2.
- return !CppFileTypes.CPP_MODULE.matches(getPrimaryOutput().getFilename());
+ return !getPrimaryOutput().isFileType(CppFileTypes.CPP_MODULE);
}
@Override
@@ -962,7 +962,7 @@ public class CppCompileAction extends AbstractAction
Iterable<Artifact> potentialModules) {
ImmutableList.Builder<String> usedModulePaths = ImmutableList.builder();
for (Artifact input : potentialModules) {
- if (CppFileTypes.CPP_MODULE.matches(input.getFilename())) {
+ if (input.isFileType(CppFileTypes.CPP_MODULE)) {
usedModulePaths.add(input.getExecPathString());
}
}
@@ -1242,7 +1242,7 @@ public class CppCompileAction extends AbstractAction
*/
private void ensureCoverageNotesFilesExist() throws ActionExecutionException {
for (Artifact output : getOutputs()) {
- if (CppFileTypes.COVERAGE_NOTES.matches(output.getFilename()) // ".gcno"
+ if (output.isFileType(CppFileTypes.COVERAGE_NOTES) // ".gcno"
&& !output.getPath().exists()) {
try {
FileSystemUtils.createEmptyFile(output.getPath());
@@ -1281,8 +1281,8 @@ public class CppCompileAction extends AbstractAction
@Override
public String getMnemonic() {
- if (CppFileTypes.OBJC_SOURCE.matches(sourceFile.getExecPath())
- || CppFileTypes.OBJCPP_SOURCE.matches(sourceFile.getExecPath())) {
+ if (sourceFile.isFileType(CppFileTypes.OBJC_SOURCE)
+ || sourceFile.isFileType(CppFileTypes.OBJCPP_SOURCE)) {
return "ObjcCompile";
} else {
return "CppCompile";
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppFileTypes.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppFileTypes.java
index c63f7d8e00..0c7fa7d795 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppFileTypes.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppFileTypes.java
@@ -42,44 +42,52 @@ public final class CppFileTypes {
public static final FileType CPP_TEXTUAL_INCLUDE = FileType.of(".inc");
public static final FileType PIC_PREPROCESSED_C = FileType.of(".pic.i");
- public static final FileType PREPROCESSED_C = new FileType() {
- final String ext = ".i";
- @Override
- public boolean apply(String filename) {
- return filename.endsWith(ext) && !PIC_PREPROCESSED_C.matches(filename);
- }
- @Override
- public List<String> getExtensions() {
- return ImmutableList.of(ext);
- }
- };
+ public static final FileType PREPROCESSED_C =
+ new FileType() {
+ final String ext = ".i";
+
+ @Override
+ public boolean apply(String path) {
+ return path.endsWith(ext) && !PIC_PREPROCESSED_C.matches(path);
+ }
+
+ @Override
+ public List<String> getExtensions() {
+ return ImmutableList.of(ext);
+ }
+ };
public static final FileType PIC_PREPROCESSED_CPP = FileType.of(".pic.ii");
- public static final FileType PREPROCESSED_CPP = new FileType() {
- final String ext = ".ii";
- @Override
- public boolean apply(String filename) {
- return filename.endsWith(ext) && !PIC_PREPROCESSED_CPP.matches(filename);
- }
- @Override
- public List<String> getExtensions() {
- return ImmutableList.of(ext);
- }
- };
+ public static final FileType PREPROCESSED_CPP =
+ new FileType() {
+ final String ext = ".ii";
+
+ @Override
+ public boolean apply(String path) {
+ return path.endsWith(ext) && !PIC_PREPROCESSED_CPP.matches(path);
+ }
+
+ @Override
+ public List<String> getExtensions() {
+ return ImmutableList.of(ext);
+ }
+ };
public static final FileType ASSEMBLER_WITH_C_PREPROCESSOR = FileType.of(".S");
public static final FileType PIC_ASSEMBLER = FileType.of(".pic.s");
- public static final FileType ASSEMBLER = new FileType() {
- final String ext = ".s";
- @Override
- public boolean apply(String filename) {
- return (filename.endsWith(ext) && !PIC_ASSEMBLER.matches(filename))
- || filename.endsWith(".asm");
- }
- @Override
- public List<String> getExtensions() {
- return ImmutableList.of(ext, ".asm");
- }
- };
+ public static final FileType ASSEMBLER =
+ new FileType() {
+ final String ext = ".s";
+
+ @Override
+ public boolean apply(String path) {
+ return (path.endsWith(ext) && !PIC_ASSEMBLER.matches(path)) || path.endsWith(".asm");
+ }
+
+ @Override
+ public List<String> getExtensions() {
+ return ImmutableList.of(ext, ".asm");
+ }
+ };
public static final FileType PIC_ARCHIVE = FileType.of(".pic.a");
public static final FileType ARCHIVE =
@@ -87,9 +95,9 @@ public final class CppFileTypes {
final List<String> extensions = ImmutableList.of(".a", ".lib");
@Override
- public boolean apply(String filename) {
+ public boolean apply(String path) {
for (String ext : extensions) {
- if (filename.endsWith(ext) && !PIC_ARCHIVE.matches(filename)) {
+ if (path.endsWith(ext) && !PIC_ARCHIVE.matches(path)) {
return true;
}
}
@@ -103,30 +111,36 @@ public final class CppFileTypes {
};
public static final FileType ALWAYS_LINK_PIC_LIBRARY = FileType.of(".pic.lo");
- public static final FileType ALWAYS_LINK_LIBRARY = new FileType() {
- final String ext = ".lo";
- @Override
- public boolean apply(String filename) {
- return filename.endsWith(ext) && !ALWAYS_LINK_PIC_LIBRARY.matches(filename);
- }
- @Override
- public List<String> getExtensions() {
- return ImmutableList.of(ext);
- }
- };
+ public static final FileType ALWAYS_LINK_LIBRARY =
+ new FileType() {
+ final String ext = ".lo";
+
+ @Override
+ public boolean apply(String path) {
+ return path.endsWith(ext) && !ALWAYS_LINK_PIC_LIBRARY.matches(path);
+ }
+
+ @Override
+ public List<String> getExtensions() {
+ return ImmutableList.of(ext);
+ }
+ };
public static final FileType PIC_OBJECT_FILE = FileType.of(".pic.o");
- public static final FileType OBJECT_FILE = new FileType() {
- final String ext = ".o";
- @Override
- public boolean apply(String filename) {
- return filename.endsWith(ext) && !PIC_OBJECT_FILE.matches(filename);
- }
- @Override
- public List<String> getExtensions() {
- return ImmutableList.of(ext);
- }
- };
+ public static final FileType OBJECT_FILE =
+ new FileType() {
+ final String ext = ".o";
+
+ @Override
+ public boolean apply(String path) {
+ return path.endsWith(ext) && !PIC_OBJECT_FILE.matches(path);
+ }
+
+ @Override
+ public List<String> getExtensions() {
+ return ImmutableList.of(ext);
+ }
+ };
// Minimized bitcode file emitted by the ThinLTO compile step and used just for LTO indexing.
public static final FileType LTO_INDEXING_OBJECT_FILE = FileType.of(".indexing.o");
@@ -144,19 +158,20 @@ public final class CppFileTypes {
// libmylib.so.2 or libmylib.so.2.10.
private static final Pattern VERSIONED_SHARED_LIBRARY_PATTERN =
Pattern.compile("^.+\\.so(\\.\\d+)+$");
- public static final FileType VERSIONED_SHARED_LIBRARY = new FileType() {
- @Override
- public boolean apply(String filename) {
- // Because regex matching can be slow, we first do a quick digit check on the final
- // character before risking the full-on regex match. This should eliminate the performance
- // hit on practically every non-qualifying file type.
- if (Character.isDigit(filename.charAt(filename.length() - 1))) {
- return VERSIONED_SHARED_LIBRARY_PATTERN.matcher(filename).matches();
- } else {
- return false;
+ public static final FileType VERSIONED_SHARED_LIBRARY =
+ new FileType() {
+ @Override
+ public boolean apply(String path) {
+ // Because regex matching can be slow, we first do a quick digit check on the final
+ // character before risking the full-on regex match. This should eliminate the performance
+ // hit on practically every non-qualifying file type.
+ if (Character.isDigit(path.charAt(path.length() - 1))) {
+ return VERSIONED_SHARED_LIBRARY_PATTERN.matcher(path).matches();
+ } else {
+ return false;
+ }
}
- }
- };
+ };
public static final FileType COVERAGE_NOTES = FileType.of(".gcno");
public static final FileType COVERAGE_DATA = FileType.of(".gcda");
diff --git a/src/main/java/com/google/devtools/build/lib/util/FileType.java b/src/main/java/com/google/devtools/build/lib/util/FileType.java
index b4cc4b6bc3..e2f9eb390a 100644
--- a/src/main/java/com/google/devtools/build/lib/util/FileType.java
+++ b/src/main/java/com/google/devtools/build/lib/util/FileType.java
@@ -19,8 +19,6 @@ import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
-import com.google.devtools.build.lib.vfs.Path;
-import com.google.devtools.build.lib.vfs.PathFragment;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -32,19 +30,22 @@ import javax.annotation.concurrent.Immutable;
@Immutable
public abstract class FileType implements Predicate<String> {
// A special file type
- public static final FileType NO_EXTENSION = new FileType() {
- @Override
- public boolean apply(String filename) {
- return filename.lastIndexOf('.') == -1;
- }
- };
+ public static final FileType NO_EXTENSION =
+ new FileType() {
+ @Override
+ public boolean apply(String path) {
+ int lastSlashIndex = path.lastIndexOf('/');
+ return path.indexOf('.', lastSlashIndex + 1) == -1;
+ }
+ };
public static FileType of(final String ext) {
return new FileType() {
@Override
- public boolean apply(String filename) {
- return filename.endsWith(ext);
+ public boolean apply(String path) {
+ return path.endsWith(ext);
}
+
@Override
public List<String> getExtensions() {
return ImmutableList.of(ext);
@@ -55,14 +56,15 @@ public abstract class FileType implements Predicate<String> {
public static FileType of(final Iterable<String> extensions) {
return new FileType() {
@Override
- public boolean apply(String filename) {
+ public boolean apply(String path) {
for (String ext : extensions) {
- if (filename.endsWith(ext)) {
+ if (path.endsWith(ext)) {
return true;
}
}
return false;
}
+
@Override
public List<String> getExtensions() {
return ImmutableList.copyOf(extensions);
@@ -79,12 +81,9 @@ public abstract class FileType implements Predicate<String> {
return getExtensions().toString();
}
- /**
- * Returns true if the the filename matches. The filename should be a basename (the filename
- * component without a path) for performance reasons.
- */
+ /** Returns true if the file matches. Subclasses are expected to handle a full path. */
@Override
- public abstract boolean apply(String filename);
+ public abstract boolean apply(String path);
/**
* Get a list of filename extensions this matcher handles. The first entry in the list (if
@@ -97,35 +96,29 @@ public abstract class FileType implements Predicate<String> {
return ImmutableList.of();
}
- /** Return true if a file name is matched by the FileType */
- public boolean matches(String filename) {
- int slashIndex = filename.lastIndexOf('/');
- if (slashIndex != -1) {
- filename = filename.substring(slashIndex + 1);
- }
- return apply(filename);
- }
-
- /** Return true if a file referred by path is matched by the FileType */
- public boolean matches(Path path) {
- return apply(path.getBaseName());
+ /** Return true if a file path is matched by this FileType */
+ @Deprecated
+ public boolean matches(String path) {
+ return apply(path);
}
- /** Return true if a file referred by fragment is matched by the FileType */
- public boolean matches(PathFragment fragment) {
- return apply(fragment.getBaseName());
+ /** Return true if the item is matched by this FileType */
+ public boolean matches(HasFileType item) {
+ return apply(item.filePathForFileTypeMatcher());
}
// Check FileTypes
- /**
- * An interface for entities that have a filename.
- */
- public interface HasFilename {
+ /** An interface for entities that have a file type. */
+ public interface HasFileType {
/**
- * Returns the filename of this entity.
+ * Return a file path that ends with the file name.
+ *
+ * <p>The path will be used by {@link FileType} for matching. An example valid implementation
+ * could return the full path of the file, or just the file name, depending on what can
+ * efficiently be provided.
*/
- String getFilename();
+ String filePathForFileTypeMatcher();
}
/**
@@ -133,12 +126,12 @@ public abstract class FileType implements Predicate<String> {
*
* <p>At least one FileType must be specified.
*/
- public static <T extends HasFilename> boolean contains(final Iterable<T> items,
- FileType... fileTypes) {
+ public static <T extends HasFileType> boolean contains(
+ final Iterable<T> items, FileType... fileTypes) {
Preconditions.checkState(fileTypes.length > 0, "Must specify at least one file type");
final FileTypeSet fileTypeSet = FileTypeSet.of(fileTypes);
for (T item : items) {
- if (fileTypeSet.matches(item.getFilename())) {
+ if (fileTypeSet.matches(item.filePathForFileTypeMatcher())) {
return true;
}
}
@@ -150,32 +143,31 @@ public abstract class FileType implements Predicate<String> {
*
* <p>At least one FileType must be specified.
*/
- public static <T extends HasFilename> boolean contains(T item, FileType... fileTypes) {
- return FileTypeSet.of(fileTypes).matches(item.getFilename());
+ public static <T extends HasFileType> boolean contains(T item, FileType... fileTypes) {
+ return FileTypeSet.of(fileTypes).matches(item.filePathForFileTypeMatcher());
}
-
- private static <T extends HasFilename> Predicate<T> typeMatchingPredicateFor(
+ private static <T extends HasFileType> Predicate<T> typeMatchingPredicateFor(
final FileType matchingType) {
- return item -> matchingType.matches(item.getFilename());
+ return item -> matchingType.matches(item.filePathForFileTypeMatcher());
}
- private static <T extends HasFilename> Predicate<T> typeMatchingPredicateFor(
+ private static <T extends HasFileType> Predicate<T> typeMatchingPredicateFor(
final FileTypeSet matchingTypes) {
- return item -> matchingTypes.matches(item.getFilename());
+ return item -> matchingTypes.matches(item.filePathForFileTypeMatcher());
}
- private static <T extends HasFilename> Predicate<T> typeMatchingPredicateFrom(
+ private static <T extends HasFileType> Predicate<T> typeMatchingPredicateFrom(
final Predicate<String> fileTypePredicate) {
- return item -> fileTypePredicate.apply(item.getFilename());
+ return item -> fileTypePredicate.apply(item.filePathForFileTypeMatcher());
}
/**
* A filter for Iterable<? extends HasFileType> that returns only those whose FileType matches the
* specified Predicate.
*/
- public static <T extends HasFilename> Iterable<T> filter(final Iterable<T> items,
- final Predicate<String> predicate) {
+ public static <T extends HasFileType> Iterable<T> filter(
+ final Iterable<T> items, final Predicate<String> predicate) {
return Iterables.filter(items, typeMatchingPredicateFrom(predicate));
}
@@ -183,8 +175,8 @@ public abstract class FileType implements Predicate<String> {
* A filter for Iterable<? extends HasFileType> that returns only those of the specified file
* types.
*/
- public static <T extends HasFilename> Iterable<T> filter(final Iterable<T> items,
- FileType... fileTypes) {
+ public static <T extends HasFileType> Iterable<T> filter(
+ final Iterable<T> items, FileType... fileTypes) {
return filter(items, FileTypeSet.of(fileTypes));
}
@@ -192,8 +184,8 @@ public abstract class FileType implements Predicate<String> {
* A filter for Iterable<? extends HasFileType> that returns only those of the specified file
* types.
*/
- public static <T extends HasFilename> Iterable<T> filter(final Iterable<T> items,
- FileTypeSet fileTypes) {
+ public static <T extends HasFileType> Iterable<T> filter(
+ final Iterable<T> items, FileTypeSet fileTypes) {
return Iterables.filter(items, typeMatchingPredicateFor(fileTypes));
}
@@ -201,8 +193,8 @@ public abstract class FileType implements Predicate<String> {
* A filter for Iterable<? extends HasFileType> that returns only those of the specified file
* type.
*/
- public static <T extends HasFilename> Iterable<T> filter(final Iterable<T> items,
- FileType fileType) {
+ public static <T extends HasFileType> Iterable<T> filter(
+ final Iterable<T> items, FileType fileType) {
return Iterables.filter(items, typeMatchingPredicateFor(fileType));
}
@@ -210,18 +202,17 @@ public abstract class FileType implements Predicate<String> {
* A filter for Iterable<? extends HasFileType> that returns everything except the specified file
* type.
*/
- public static <T extends HasFilename> Iterable<T> except(final Iterable<T> items,
- FileType fileType) {
+ public static <T extends HasFileType> Iterable<T> except(
+ final Iterable<T> items, FileType fileType) {
return Iterables.filter(items, Predicates.not(typeMatchingPredicateFor(fileType)));
}
-
/**
* A filter for List<? extends HasFileType> that returns only those of the specified file types.
* The result is a mutable list, computed eagerly; see {@link #filter} for a lazy variant.
*/
- public static <T extends HasFilename> List<T> filterList(final Iterable<T> items,
- FileType... fileTypes) {
+ public static <T extends HasFileType> List<T> filterList(
+ final Iterable<T> items, FileType... fileTypes) {
if (fileTypes.length > 0) {
return filterList(items, FileTypeSet.of(fileTypes));
} else {
@@ -233,11 +224,11 @@ public abstract class FileType implements Predicate<String> {
* A filter for List<? extends HasFileType> that returns only those of the specified file type.
* The result is a mutable list, computed eagerly.
*/
- public static <T extends HasFilename> List<T> filterList(final Iterable<T> items,
- final FileType fileType) {
+ public static <T extends HasFileType> List<T> filterList(
+ final Iterable<T> items, final FileType fileType) {
List<T> result = new ArrayList<>();
for (T item : items) {
- if (fileType.matches(item.getFilename())) {
+ if (fileType.matches(item.filePathForFileTypeMatcher())) {
result.add(item);
}
}
@@ -248,11 +239,11 @@ public abstract class FileType implements Predicate<String> {
* A filter for List<? extends HasFileType> that returns only those of the specified file types.
* The result is a mutable list, computed eagerly.
*/
- public static <T extends HasFilename> List<T> filterList(final Iterable<T> items,
- final FileTypeSet fileTypeSet) {
+ public static <T extends HasFileType> List<T> filterList(
+ final Iterable<T> items, final FileTypeSet fileTypeSet) {
List<T> result = new ArrayList<>();
for (T item : items) {
- if (fileTypeSet.matches(item.getFilename())) {
+ if (fileTypeSet.matches(item.filePathForFileTypeMatcher())) {
result.add(item);
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/util/FileTypeSet.java b/src/main/java/com/google/devtools/build/lib/util/FileTypeSet.java
index d708526387..9b3b2e46fe 100644
--- a/src/main/java/com/google/devtools/build/lib/util/FileTypeSet.java
+++ b/src/main/java/com/google/devtools/build/lib/util/FileTypeSet.java
@@ -17,11 +17,9 @@ import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
-
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
-
import javax.annotation.concurrent.Immutable;
/**
@@ -38,23 +36,26 @@ public class FileTypeSet implements Predicate<String> {
public String toString() {
return "any files";
}
+
@Override
public boolean matches(String filename) {
return true;
}
+
@Override
public List<String> getExtensions() {
- return ImmutableList.<String>of();
+ return ImmutableList.of();
}
};
/** A predicate that matches no files. */
public static final FileTypeSet NO_FILE =
- new FileTypeSet(ImmutableList.<FileType>of()) {
+ new FileTypeSet(ImmutableList.of()) {
@Override
public String toString() {
return "no files";
}
+
@Override
public boolean matches(String filename) {
return false;
@@ -105,13 +106,9 @@ public class FileTypeSet implements Predicate<String> {
}
/** Returns true if the filename can be matched by any FileType in this set. */
- public boolean matches(String filename) {
- int slashIndex = filename.lastIndexOf('/');
- if (slashIndex != -1) {
- filename = filename.substring(slashIndex + 1);
- }
+ public boolean matches(String path) {
for (FileType type : types) {
- if (type.apply(filename)) {
+ if (type.apply(path)) {
return true;
}
}
@@ -124,8 +121,8 @@ public class FileTypeSet implements Predicate<String> {
}
@Override
- public boolean apply(String filename) {
- return matches(filename);
+ public boolean apply(String path) {
+ return matches(path);
}
/** Returns the list of possible file extensions for this file type. Can be empty. */
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/BUILD b/src/main/java/com/google/devtools/build/lib/vfs/BUILD
index 88c7c86490..f6d2eb427b 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/vfs/BUILD
@@ -19,6 +19,7 @@ java_library(
srcs = PATH_FRAGMENT_SOURCES,
deps = [
"//src/main/java/com/google/devtools/build/lib:base-util",
+ "//src/main/java/com/google/devtools/build/lib:filetype",
"//src/main/java/com/google/devtools/build/lib:os_util",
"//src/main/java/com/google/devtools/build/lib:skylarkinterface",
"//src/main/java/com/google/devtools/build/lib/concurrent",
@@ -44,6 +45,7 @@ java_library(
deps = [
":pathfragment",
"//src/main/java/com/google/devtools/build/lib:base-util",
+ "//src/main/java/com/google/devtools/build/lib:filetype",
"//src/main/java/com/google/devtools/build/lib:os_util",
"//src/main/java/com/google/devtools/build/lib:skylarkinterface",
"//src/main/java/com/google/devtools/build/lib/clock",
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/Path.java b/src/main/java/com/google/devtools/build/lib/vfs/Path.java
index 06a884b4a9..1d6c57c730 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/Path.java
+++ b/src/main/java/com/google/devtools/build/lib/vfs/Path.java
@@ -18,6 +18,7 @@ import com.google.common.base.Predicate;
import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
import com.google.devtools.build.lib.skylarkinterface.SkylarkPrintable;
import com.google.devtools.build.lib.skylarkinterface.SkylarkPrinter;
+import com.google.devtools.build.lib.util.FileType;
import com.google.devtools.build.lib.util.StringCanonicalizer;
import com.google.devtools.build.lib.vfs.FileSystem.HashFunction;
import java.io.File;
@@ -52,7 +53,8 @@ import java.util.Objects;
* <p>{@link FileSystem} implementations maintain pointers into this graph.
*/
@ThreadSafe
-public class Path implements Comparable<Path>, Serializable, SkylarkPrintable {
+public class Path
+ implements Comparable<Path>, Serializable, SkylarkPrintable, FileType.HasFileType {
/** Filesystem-specific factory for {@link Path} objects. */
public static interface PathFactory {
@@ -428,6 +430,11 @@ public class Path implements Comparable<Path>, Serializable, SkylarkPrintable {
printer.append(getPathString());
}
+ @Override
+ public String filePathForFileTypeMatcher() {
+ return name;
+ }
+
/**
* Returns the path as a string.
*/
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/PathFragment.java b/src/main/java/com/google/devtools/build/lib/vfs/PathFragment.java
index 05e4ee86c8..6f6dfbf819 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/PathFragment.java
+++ b/src/main/java/com/google/devtools/build/lib/vfs/PathFragment.java
@@ -26,6 +26,7 @@ import com.google.devtools.build.lib.skyframe.serialization.SerializationExcepti
import com.google.devtools.build.lib.skyframe.serialization.strings.StringCodecs;
import com.google.devtools.build.lib.skylarkinterface.SkylarkPrintable;
import com.google.devtools.build.lib.skylarkinterface.SkylarkPrinter;
+import com.google.devtools.build.lib.util.FileType;
import com.google.devtools.build.lib.util.OS;
import com.google.devtools.build.lib.util.StringCanonicalizer;
import com.google.protobuf.CodedInputStream;
@@ -55,7 +56,7 @@ import java.util.Set;
@javax.annotation.concurrent.Immutable
@ThreadSafe
public abstract class PathFragment
- implements Comparable<PathFragment>, Serializable, SkylarkPrintable {
+ implements Comparable<PathFragment>, Serializable, SkylarkPrintable, FileType.HasFileType {
private static final Helper HELPER =
OS.getCurrent() == OS.WINDOWS ? WindowsPathFragment.HELPER : UnixPathFragment.HELPER;
@@ -751,6 +752,11 @@ public abstract class PathFragment
printer.append(getPathString());
}
+ @Override
+ public String filePathForFileTypeMatcher() {
+ return getBaseName();
+ }
+
private static class PathFragmentCodec implements ObjectCodec<PathFragment> {
private final ObjectCodec<String> stringCodec = StringCodecs.asciiOptimized();