| // 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; | |
| } | |
| } | |
| } |