diff --git a/tools/runfiles/BUILD.bazel b/tools/runfiles/BUILD.bazel
index a715e113..14929c3c 100644
--- a/tools/runfiles/BUILD.bazel
+++ b/tools/runfiles/BUILD.bazel
@@ -7,9 +7,9 @@ csharp_library(
name = "runfiles",
srcs = ["Runfiles.cs"],
private_deps = [
- "@rules_dotnet_nuget_packages//netstandard.library.ref",
+ "@rules_dotnet_nuget_packages//netstandard.library",
],
- target_frameworks = ["netstandard2.1"],
+ target_frameworks = ["netstandard2.0"],
visibility = ["//visibility:public"],
deps = [
],
diff --git a/tools/runfiles/Runfiles.cs b/tools/runfiles/Runfiles.cs
index 0190e3a6..a9bc716e 100644
--- a/tools/runfiles/Runfiles.cs
+++ b/tools/runfiles/Runfiles.cs
@@ -15,7 +15,7 @@ namespace Bazel
/// USAGE:
///
/// 1. Depend on this runfiles library from your build rule:
- ///
+ ///
///
/// csharp_binary(
/// name = "my_binary",
@@ -23,7 +23,7 @@ namespace Bazel
/// deps = ["@bazel_tools//tools/java/runfiles"],
/// )
///
- ///
+ ///
/// 2. Import the runfiles library.
///
///
@@ -47,7 +47,7 @@ namespace Bazel
/// var path = runfiles.Rlocation("path/to/binary");
/// var process = new System.Diagnostics.Process();
/// process.StartInfo.FileName = path;
- /// process.StartInfo.Environment = Runfiles.GetEnvVars();
+ /// process.StartInfo.Environment = Runfiles.GetEnvVars();
/// ...
/// process.Start();
///
@@ -83,10 +83,10 @@ public static Runfiles Create()
/// If contains RUNFILES_MANIFEST_ONLY=1, this method returns a manifest-based implementation.
/// The manifest's path is defined by the RUNFILES_MANIFEST_FILE key's value in .
///
- /// If contains RUNFILES_DIR=SOME_DIRECTORY or JAVA_RUNFILES=SOME_DIRECTORY,
+ /// If contains RUNFILES_DIR=SOME_DIRECTORY or JAVA_RUNFILES=SOME_DIRECTORY,
/// this method returns a directory-based implementation.
///
- /// Otherwise this method tries to find a the manifest file based on the argv0
+ /// Otherwise this method tries to find a the manifest file based on the argv0
/// If argv0 + ".runfiles/MANFIEST" exists RUNFILES_MANIFEST_FILE will be set to to that path
/// else if argv0 + ".runfiles_manifest" exists RUNFILES_MANIFEST_FILE will be set to to that path.
/// If argv0 + ".runfiles" exists RUNFILES_DIR will be set to to that path.
@@ -153,7 +153,7 @@ public static Runfiles Create(string argv0, IDictionary env)
///
/// Returns the runtime path of a runfile (a Bazel-built binary's/test's data-dependency).
- ///
+ ///
/// The returned path may not be valid. The caller should check the path's validity and that the
/// path exists.
///
@@ -178,7 +178,7 @@ public string Rlocation(string path)
throw new ArgumentException($"path is absolute without a drive letter: \"{path}\"");
}
- if (Path.IsPathFullyQualified(path))
+ if (IsPathFullyQualified(path))
{
return path;
}
@@ -193,6 +193,93 @@ public string Rlocation(string path)
///
public abstract IDictionary GetEnvVars();
+ ///
+ /// Returns true if the path is fixed to a specific drive or UNC path. This method does no
+ /// validation of the path (URIs will be returned as relative as a result).
+ /// Returns false if the path specified is relative to the current drive or working directory.
+ ///
+ /// Path to check.
+ ///
+ /// Handles paths that use the alternate directory separator. It is a frequent mistake to
+ /// assume that rooted paths are not relative. This isn't the case.
+ /// "C:a" is drive relative- meaning that it will be resolved against the current directory
+ /// for C: (rooted, but relative). "C:\a" is rooted and not relative (the current directory
+ /// will not be used to modify the path).
+ ///
+ ///
+ /// Thrown if is null.
+ ///
+ private static bool IsPathFullyQualified(string path)
+ {
+ if (path == null)
+ {
+ throw new ArgumentNullException(nameof(path));
+ }
+
+ return !IsPathPartiallyQualified(path);
+ }
+
+ ///
+ /// Returns true if the path specified is relative to the current drive or working directory.
+ /// Returns false if the path is fixed to a specific drive or UNC path. This method does no
+ /// validation of the path (URIs will be returned as relative as a result).
+ ///
+ ///
+ /// Handles paths that use the alternate directory separator. It is a frequent mistake to
+ /// assume that rooted paths (Path.IsPathRooted) are not relative. This isn't the case.
+ /// "C:a" is drive relative- meaning that it will be resolved against the current directory
+ /// for C: (rooted, but relative). "C:\a" is rooted and not relative (the current directory
+ /// will not be used to modify the path).
+ ///
+ private static bool IsPathPartiallyQualified(string path)
+ {
+ if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ return !Path.IsPathRooted(path);
+ }
+ else
+ {
+ if (path.Length < 2)
+ {
+ // It isn't fixed, it must be relative. There is no way to specify a fixed
+ // path with one character (or less).
+ return false;
+ }
+
+ if (IsDirectorySeparator(path[0]))
+ {
+ // There is no valid way to specify a relative path with two initial slashes or
+ // \? as ? isn't valid for drive relative paths and \??\ is equivalent to \\?\
+ return !(path[1] == '?' || IsDirectorySeparator(path[1]));
+ }
+
+ // The only way to specify a fixed path that doesn't begin with two slashes
+ // is the drive, colon, slash format- i.e. C:\
+ return !((path.Length >= 3)
+ && (path[1] == Path.VolumeSeparatorChar)
+ && IsDirectorySeparator(path[2])
+ // To match old behavior we'll check the drive character for validity as the path is technically
+ // not qualified if you don't have a valid drive. "=:\" is the "=" file's default data stream.
+ && IsValidDriveChar(path[0]));
+ }
+ }
+
+ ///
+ /// True if the given character is a directory separator.
+ ///
+ private static bool IsDirectorySeparator(char character)
+ {
+ return character == Path.DirectorySeparatorChar || character == Path.AltDirectorySeparatorChar;
+ }
+
+ ///
+ /// Returns true if the given character is a valid drive letter
+ ///
+ private static bool IsValidDriveChar(char value)
+ {
+ return (uint)((value | 0x20) - 'a') <= (uint)('z' - 'a');
+ }
+
private static Boolean isManifestOnly(IDictionary env)
{
env.TryGetValue("RUNFILES_MANIFEST_ONLY", out var value);
diff --git a/tools/runfiles/Runfiles.csproj b/tools/runfiles/Runfiles.csproj
index 83c3bbf5..83b7ca42 100644
--- a/tools/runfiles/Runfiles.csproj
+++ b/tools/runfiles/Runfiles.csproj
@@ -1,7 +1,7 @@
- netstandard2.1
+ netstandard2.0
false