blob: 0248da76bac5130c1f2cc9bc5f5e2d7c69eb4848 [file] [log] [blame]
// Copyright 2019 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.skyframe;
import com.google.common.base.Preconditions;
import com.google.common.collect.Interner;
import com.google.devtools.build.lib.concurrent.BlazeInterners;
import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
import com.google.devtools.build.lib.vfs.RootedPath;
import com.google.devtools.build.lib.vfs.RootedPathAndCasing;
import com.google.devtools.build.skyframe.AbstractSkyKey;
import com.google.devtools.build.skyframe.SkyFunctionName;
import com.google.devtools.build.skyframe.SkyKey;
import com.google.devtools.build.skyframe.SkyValue;
/**
* Value that represents whether a certain path is correctly cased.
*
* <p>Most filesystems preserve uppercase and lowercase letters when creating entries: {@code
* mkdir("Abc1")} creates the directory "Abc1", and not "ABC1" nor "abc1".
*
* <p>Some filesystems differentiate casing when looking up entries, but some don't. Suppose we have
* an empty directory and create the file "Abc1" underneath it. Then, calling {@code exists("Abc1")}
* succeeds and {@code exists("ABC1")} fails on ext4 (Linux) but both calls succeed on APFS (macOS)
* and NTFS (Windows). Reason for this difference is that ext4 is case-sensitive, so "Abc1" and
* "ABC1" mean different files, but APFS and NTFS are case-ignoring (or case-insensitive), so both
* paths mean the same file.
*
* <p>This object represents whether an existing path on a case-ignoring filesystem (APFS and NTFS)
* is correctly or incorrectly cased, i.e. whether the exact use of upper and lower case letters
* matches the entry on disk. In the previous example, "Abc1" is correctly cased while "ABC1" is
* incorrectly cased.
*
* <p>Paths on case-sensitive filesystems (ext4) are always correctly cased, because the filesystem
* requires exact case matching when accessing files.
*/
public abstract class PathCasingLookupValue implements SkyValue {
@AutoCodec public static final BadPathCasing BAD = new BadPathCasing();
@AutoCodec public static final CorrectPathCasing GOOD = new CorrectPathCasing();
/** Singleton {@link PathCasingLookupValue} instance for incorrectly cased paths. */
public static class BadPathCasing extends PathCasingLookupValue {
@Override
public boolean isCorrect() {
return false;
}
}
/** Singleton {@link PathCasingLookupValue} instance for correctly cased paths. */
public static class CorrectPathCasing extends PathCasingLookupValue {
@Override
public boolean isCorrect() {
return true;
}
}
/**
* Creates a {@code SkyKey} to request this {@code PathCasingLookupValue} from Skyframe.
*
* <p>The argument is a {@link RootedPath} and not a {@link Path} for two reasons:
*
* <ul>
* <li>as of 2019-12-04 the {@code PathCasingLookupFunction} depends on {@code
* DirectoryListingValue} whose {@code SkyKey} requires a {@code RootedPath}
* <li>as of 2019-12-04 the {@code PathCasingLookupValue} is only used to validate that a
* package label is correctly cased, and package labels are always relative to a package
* root, so using a {@code RootedPath} is adequate and the Root part of it doesn't even have
* to be correctly cased.
* </ul>
*/
public static SkyKey key(RootedPath path) {
return Key.create(RootedPathAndCasing.create(path));
}
private PathCasingLookupValue() {}
public abstract boolean isCorrect();
/** {@link SkyKey} for {@link PathCasingLookupValue} computation. */
@AutoCodec.VisibleForSerialization
@AutoCodec
public static class Key extends AbstractSkyKey<RootedPathAndCasing> {
private static final Interner<Key> interner = BlazeInterners.newWeakInterner();
private Key(RootedPathAndCasing arg) {
super(arg);
}
@AutoCodec.VisibleForSerialization
@AutoCodec.Instantiator
static Key create(RootedPathAndCasing arg) {
Preconditions.checkNotNull(arg);
return interner.intern(new Key(arg));
}
@Override
public SkyFunctionName functionName() {
return SkyFunctions.PATH_CASING_LOOKUP;
}
}
}