private static bool TryCreate(IDictionary env, out Runfiles runfiles) { if (IsManifestOnly(env)) { // On Windows, the launcher sets RUNFILES_MANIFEST_ONLY=1. // On every platform, the launcher also sets RUNFILES_MANIFEST_FILE, but on Linux and macOS it's // faster to use RUNFILES_DIR. { runfiles = new ManifestBased(GetManifestPath(env)); return(true); } } var value = env["RUNFILES_DIR"] as string; if (!string.IsNullOrEmpty(value)) { // bazel, a launcher, or another process has told us where the runfiles are runfiles = new DirectoryBased(value); return(true); } runfiles = null; return(false); }
/// <summary> /// Returns a new <see cref="Runfiles"/> instance. /// <para>The returned object is either:</para> /// <list type="bullet"> /// <item>manifest-based, meaning it looks up runfile paths from a manifest file</item> /// <item>directory-based, meaning it looks up runfile paths under a given directory path</item> /// </list> /// /// <para>If <paramref name="env"/> contains a "RUNFILES_MANIFEST_ONLY" with value "1", this method returns a /// manifest based implementation. The manifest's path is defined by the "RUNFILES_MANIFEST_FILE" key's value /// in <paramref name="env"/></para> /// <para>Otherwise this method returns a directory-based implementation. The directory's path is defined by /// the value in <paramref name="env"/> under the "RUNFILES_DIR" key.</para> /// /// <para>Performance note: the manifest-based implementation eagerly reads and caches the whole manifest file /// on instantiation.</para> /// </summary> /// <param name="env">A dictionary of environment variables</param> /// <param name="b"></param> /// <returns></returns> public static LabelRunfiles Create(IDictionary env) { const string infoName = "runfiles.info"; var assemblyLocation = AppDomain.CurrentDomain.BaseDirectory; var assemblyDirectory = Path.GetDirectoryName(assemblyLocation); var infoFile = new FileInfo( Path.Combine(assemblyDirectory !, infoName)); if (!infoFile.Exists) { throw new IOException($"Could not find `{infoName}` file next to {assemblyLocation}"); } var lines = File.ReadAllLines(infoFile.FullName); if (lines.Length != 4) { throw new IOException($"Unexpected `{infoName}` format, expected three lines, got: {lines.Length}"); } var expectedLocation = lines[0]; var defaultPackage = new Label(lines[1], lines[2]); var strategy = lines[3]; string GetExpectedLocation() { // expected location is a relative path to <EntryAssembly>.dll.runfiles, as calculated by the builder var expectedDir = Path.GetFullPath(Path.Combine(assemblyDirectory, expectedLocation)); if (!Directory.Exists(expectedDir)) { throw new IOException($"Failed to find runfiles. No environment variables were set and " + $"{infoFile.FullName} pointed to {expectedDir} which does not exist."); } return(expectedDir); } Runfiles runfiles; if (strategy == "selfish") { runfiles = new DirectoryBased(GetExpectedLocation()); } else if (!TryCreate(env, out runfiles)) { // no one has told us where the runfiles are, this means: // 1) the user ran a .dll with `dotnet <AssemblyName>.dll` // in this case, the runfiles dir is at <AssemblyName>[.exe].runfiles // 2) An ide is executing this assembly, perhaps via debugging // if it's debugging, we most likely can't divine runfiles from Environment.CommandLine because that // will be the debugger arguments // since no environment variables were set, we just have to take our best guess at which method to use // on non-windows, there *should* be a runfiles tree, so use a directory based. if (Path.DirectorySeparatorChar != '\\') { runfiles = new DirectoryBased(GetExpectedLocation()); } else { // on windows, there *may* be a runfiles tree, but there will always be a manifest, so its safe to // use that runfiles = new ManifestBased(Path.Combine(GetExpectedLocation(), "MANIFEST")); } } return(new LabelRunfiles(runfiles, defaultPackage)); }