未验证 提交 fa49dbc3 编写于 作者: M Mateo Torres-Ruiz 提交者: GitHub

Remove unnecessary realpath calls from host probing logic (#50671)

* Skip test on windows

* Delay file existence check

* Check file existence if there are additional probbing paths

* PR feedback

* Add needs_file_existence_checks

* Clean symlinks causing CI problems with stat

* Switch back to libc

* Ignore symlink deletion in osx
上级 a551a99e
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
namespace StandaloneApp
{
public static class Program
{
public static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<AssemblyName>StandaloneApp</AssemblyName>
<TargetFrameworks>net6.0</TargetFrameworks>
<OutputType>Exe</OutputType>
<RuntimeIdentifier>$(TestTargetRid)</RuntimeIdentifier>
</PropertyGroup>
</Project>
......@@ -59,7 +59,7 @@ public void ComponentWithNoDependenciesCaseChangedOnAsm()
// Linux: we fail
// Windows and Mac, probing succeeds but
// Windows: probing returns the original name
// Mac: probing return the new name including 2 assembly probing with the same new name and the changed deps file
// Mac: probing return the new name including 2 assembly probing with the original and new name, and the changed deps file
var component = sharedTestState.ComponentWithNoDependencies.Copy();
......@@ -89,7 +89,7 @@ public void ComponentWithNoDependenciesCaseChangedOnAsm()
sharedTestState.RunComponentResolutionTest(component)
.Should().Pass()
.And.HaveStdOutContaining("corehost_resolve_component_dependencies:Success")
.And.HaveStdOutContaining($"corehost_resolve_component_dependencies assemblies:[{changeFile}{Path.PathSeparator}{changeFile}{Path.PathSeparator}]")
.And.HaveStdOutContaining($"corehost_resolve_component_dependencies assemblies:[{component.AppDll}{Path.PathSeparator}{changeFile}{Path.PathSeparator}]")
.And.HaveStdErrContaining($"app_root='{component.Location}{Path.DirectorySeparatorChar}'")
.And.HaveStdErrContaining($"deps='{changeDepsFile}'")
.And.HaveStdErrContaining($"mgd_app='{changeFile}'");
......@@ -115,7 +115,7 @@ public void ComponentWithNoDependenciesCaseChangedOnDepsAndAsm()
// Linux: we fail
// Windows and Mac, probing succeeds but
// Windows: probing returns the original name
// Mac: probing return the new name including 2 assembly probing with the same new name and the changed deps file
// Mac: probing return the new name including 2 assembly probing with the original and new name, and the changed deps file
var component = sharedTestState.ComponentWithNoDependencies.Copy();
......@@ -146,7 +146,7 @@ public void ComponentWithNoDependenciesCaseChangedOnDepsAndAsm()
sharedTestState.RunComponentResolutionTest(component)
.Should().Pass()
.And.HaveStdOutContaining("corehost_resolve_component_dependencies:Success")
.And.HaveStdOutContaining($"corehost_resolve_component_dependencies assemblies:[{changeFile}{Path.PathSeparator}{changeFile}{Path.PathSeparator}]")
.And.HaveStdOutContaining($"corehost_resolve_component_dependencies assemblies:[{component.AppDll}{Path.PathSeparator}{changeFile}{Path.PathSeparator}]")
.And.HaveStdErrContaining($"app_root='{component.Location}{Path.DirectorySeparatorChar}'")
.And.HaveStdErrContaining($"deps='{changeDepsFile}'")
.And.HaveStdErrContaining($"mgd_app='{changeFile}'");
......@@ -242,7 +242,8 @@ public void ComponentWithDependencies()
$"{Path.Combine(sharedTestState.ComponentWithDependencies.Location, "Newtonsoft.Json.dll")}{Path.PathSeparator}]")
.And.HaveStdOutContaining(
$"corehost_resolve_component_dependencies native_search_paths:[" +
$"{ExpectedProbingPaths(Path.Combine(sharedTestState.ComponentWithDependencies.Location, "runtimes", "win10-x86", "native"))}]");
$"{Path.Combine(sharedTestState.ComponentWithDependencies.Location, "runtimes", "win10-x86", "native")}" +
$"{Path.DirectorySeparatorChar}{Path.PathSeparator}]");
}
[Fact]
......@@ -251,7 +252,6 @@ public void ComponentWithDependenciesAndDependencyRemoved()
var component = sharedTestState.ComponentWithDependencies.Copy();
// Remove a dependency
// This will cause the resolution to fail
File.Delete(Path.Combine(component.Location, "ComponentDependency.dll"));
sharedTestState.RunComponentResolutionTest(component)
......@@ -259,6 +259,7 @@ public void ComponentWithDependenciesAndDependencyRemoved()
.And.HaveStdOutContaining("corehost_resolve_component_dependencies:Success")
.And.HaveStdOutContaining(
$"corehost_resolve_component_dependencies assemblies:[" +
$"{Path.Combine(component.Location, "ComponentDependency.dll")}{Path.PathSeparator}" +
$"{component.AppDll}{Path.PathSeparator}" +
$"{Path.Combine(component.Location, "Newtonsoft.Json.dll")}{Path.PathSeparator}]");
}
......@@ -369,36 +370,8 @@ public void ComponentWithResourcesShouldReportResourceSearchPaths()
.Should().Pass()
.And.HaveStdOutContaining("corehost_resolve_component_dependencies:Success")
.And.HaveStdOutContaining($"corehost_resolve_component_dependencies resource_search_paths:[" +
$"{ExpectedProbingPaths(sharedTestState.ComponentWithResources.Location)}]");
}
private string ExpectedProbingPaths(params string[] paths)
{
string result = string.Empty;
foreach (string path in paths)
{
string expectedPath = path;
if (expectedPath.EndsWith(Path.DirectorySeparatorChar))
{
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
// On non-windows the paths are normalized to not end with a /
expectedPath = expectedPath.Substring(0, expectedPath.Length - 1);
}
}
else
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
// On windows all paths are normalized to end with a \
expectedPath += Path.DirectorySeparatorChar;
}
}
result += expectedPath + Path.PathSeparator;
}
return result;
$"{sharedTestState.ComponentWithResources.Location}" +
$"{Path.DirectorySeparatorChar}{Path.PathSeparator}]");
}
[Fact]
......@@ -425,7 +398,8 @@ public void MultiThreadedComponentDependencyResolutionWhichSucceeeds()
.And.HaveStdOutContaining($"ComponentA: corehost_resolve_component_dependencies assemblies:[{sharedTestState.ComponentWithNoDependencies.AppDll}{Path.PathSeparator}]")
.And.HaveStdOutContaining($"ComponentB: corehost_resolve_component_dependencies:Success")
.And.HaveStdOutContaining($"ComponentB: corehost_resolve_component_dependencies resource_search_paths:[" +
$"{ExpectedProbingPaths(sharedTestState.ComponentWithResources.Location)}]");
$"{sharedTestState.ComponentWithResources.Location}" +
$"{Path.DirectorySeparatorChar}{Path.PathSeparator}]");
}
[Fact]
......
......@@ -40,7 +40,7 @@ public void BasicApp()
CommandResult result = sharedState.CreateNativeHostCommand(args, sharedState.DotNet.BinPath)
.Execute();
string pathSuffix = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? Path.DirectorySeparatorChar.ToString() : string.Empty;
string pathSuffix = Path.DirectorySeparatorChar.ToString();
string expectedSearchDirectories =
Path.GetDirectoryName(sharedState.AppPath) + pathSuffix + Path.PathSeparator +
Path.Combine(sharedState.DotNet.BinPath, "shared", "Microsoft.NETCore.App", SharedTestState.NetCoreAppVersion) + pathSuffix + Path.PathSeparator;
......
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using FluentAssertions;
using Microsoft.DotNet.Cli.Build.Framework;
using Microsoft.DotNet.CoreSetup.Test;
using System;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using Xunit;
namespace Microsoft.NET.HostModel.Tests
{
public class AppHostUsedWithSymbolicLinks : IClassFixture<AppHostUsedWithSymbolicLinks.SharedTestState>
{
private SharedTestState sharedTestState;
private void CreateSymbolicLink(string source, string target)
{
if (!SymbolicLinking.MakeSymbolicLink(source, target, out var errorString))
throw new Exception($"Failed to create symbolic link '{source}' targeting: '{target}': {errorString}");
}
public AppHostUsedWithSymbolicLinks(AppHostUsedWithSymbolicLinks.SharedTestState fixture)
{
sharedTestState = fixture;
}
[Theory]
[InlineData ("a/b/SymlinkToApphost")]
[InlineData ("a/SymlinkToApphost")]
public void Run_apphost_behind_symlink(string symlinkRelativePath)
{
// Creating symbolic links requires administrative privilege on Windows, so skip test.
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
return;
var fixture = sharedTestState.StandaloneAppFixture_Published
.Copy();
var appExe = fixture.TestProject.AppExe;
var testDir = Directory.GetParent(fixture.TestProject.Location).ToString();
Directory.CreateDirectory(Path.Combine(testDir, Path.GetDirectoryName(symlinkRelativePath)));
var symlinkFullPath = Path.Combine(testDir, symlinkRelativePath);
CreateSymbolicLink(symlinkFullPath, appExe);
Command.Create(symlinkFullPath)
.CaptureStdErr()
.CaptureStdOut()
.Execute()
.Should().Pass()
.And.HaveStdOutContaining("Hello World");
}
[Theory]
[InlineData ("a/b/FirstSymlink", "c/d/SecondSymlink")]
[InlineData ("a/b/FirstSymlink", "c/SecondSymlink")]
[InlineData ("a/FirstSymlink", "c/d/SecondSymlink")]
[InlineData ("a/FirstSymlink", "c/SecondSymlink")]
public void Run_apphost_behind_transitive_symlinks(string firstSymlinkRelativePath, string secondSymlinkRelativePath)
{
// Creating symbolic links requires administrative privilege on Windows, so skip test.
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
return;
var fixture = sharedTestState.StandaloneAppFixture_Published
.Copy();
var appExe = fixture.TestProject.AppExe;
var testDir = Directory.GetParent(fixture.TestProject.Location).ToString();
Directory.CreateDirectory(Path.Combine(testDir, Path.GetDirectoryName(firstSymlinkRelativePath)));
Directory.CreateDirectory(Path.Combine(testDir, Path.GetDirectoryName(secondSymlinkRelativePath)));
// second symlink -> apphost
string secondSymbolicLink = Path.Combine(testDir, secondSymlinkRelativePath);
CreateSymbolicLink(secondSymbolicLink, appExe);
// first symlink -> second symlink
string firstSymbolicLink = Path.Combine(testDir, firstSymlinkRelativePath);
CreateSymbolicLink(firstSymbolicLink, secondSymbolicLink);
Command.Create(firstSymbolicLink)
.CaptureStdErr()
.CaptureStdOut()
.Execute()
.Should().Pass()
.And.HaveStdOutContaining("Hello World");
if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
Directory.Delete(firstSymbolicLink);
Directory.Delete(secondSymbolicLink);
}
}
//[Theory]
//[InlineData("a/b/SymlinkToFrameworkDependentApp")]
//[InlineData("a/SymlinkToFrameworkDependentApp")]
[Fact(Skip = "Currently failing in OSX with \"No such file or directory\" when running Command.Create. " +
"CI failing to use stat on symbolic links on Linux (permission denied).")]
public void Run_framework_dependent_app_behind_symlink(/* string symlinkRelativePath */)
{
// Creating symbolic links requires administrative privilege on Windows, so skip test.
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
return;
string symlinkRelativePath = string.Empty;
var fixture = sharedTestState.FrameworkDependentAppFixture_Published
.Copy();
var appExe = fixture.TestProject.AppExe;
var builtDotnet = fixture.BuiltDotnet.BinPath;
var testDir = Directory.GetParent(fixture.TestProject.Location).ToString();
Directory.CreateDirectory(Path.Combine(testDir, Path.GetDirectoryName(symlinkRelativePath)));
var symlinkFullPath = Path.Combine(testDir, symlinkRelativePath);
CreateSymbolicLink(symlinkFullPath, appExe);
Command.Create(symlinkFullPath)
.CaptureStdErr()
.CaptureStdOut()
.EnvironmentVariable("DOTNET_ROOT", builtDotnet)
.EnvironmentVariable("DOTNET_ROOT(x86)", builtDotnet)
.Execute()
.Should().Pass()
.And.HaveStdOutContaining("Hello World");
}
[Fact(Skip = "Currently failing in OSX with \"No such file or directory\" when running Command.Create. " +
"CI failing to use stat on symbolic links on Linux (permission denied).")]
public void Run_framework_dependent_app_with_runtime_behind_symlink()
{
// Creating symbolic links requires administrative privilege on Windows, so skip test.
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
return;
var fixture = sharedTestState.FrameworkDependentAppFixture_Published
.Copy();
var appExe = fixture.TestProject.AppExe;
var testDir = Directory.GetParent(fixture.TestProject.Location).ToString();
var dotnetSymlink = Path.Combine(testDir, "dotnet");
var dotnetDir = fixture.BuiltDotnet.BinPath;
CreateSymbolicLink(dotnetSymlink, dotnetDir);
Command.Create(appExe)
.EnvironmentVariable("DOTNET_ROOT", dotnetSymlink)
.CaptureStdErr()
.CaptureStdOut()
.Execute()
.Should().Pass()
.And.HaveStdOutContaining("Hello World");
}
[Fact]
public void Put_app_directory_behind_symlink()
{
// Creating symbolic links requires administrative privilege on Windows, so skip test.
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
return;
var fixture = sharedTestState.StandaloneAppFixture_Published
.Copy();
var appExe = fixture.TestProject.AppExe;
var binDir = fixture.TestProject.OutputDirectory;
var binDirNewPath = Path.Combine(Directory.GetParent(fixture.TestProject.Location).ToString(), "PutTheBinDirSomewhereElse");
Directory.Move(binDir, binDirNewPath);
CreateSymbolicLink(binDir, binDirNewPath);
Command.Create(appExe)
.CaptureStdErr()
.CaptureStdOut()
.Execute()
.Should().Pass()
.And.HaveStdOutContaining("Hello World");
}
[Fact]
public void Put_dotnet_behind_symlink()
{
// Creating symbolic links requires administrative privilege on Windows, so skip test.
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
return;
var fixture = sharedTestState.StandaloneAppFixture_Published
.Copy();
var appDll = fixture.TestProject.AppDll;
var dotnetExe = fixture.BuiltDotnet.DotnetExecutablePath;
var testDir = Directory.GetParent(fixture.TestProject.Location).ToString();
var dotnetSymlink = Path.Combine(testDir, "dotnet");
CreateSymbolicLink(dotnetSymlink, dotnetExe);
Command.Create(dotnetSymlink, fixture.TestProject.AppDll)
.CaptureStdErr()
.CaptureStdOut()
.Execute()
.Should().Pass()
.And.HaveStdOutContaining("Hello World");
if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
Directory.Delete(dotnetSymlink);
}
}
[Fact]
public void Put_app_directory_behind_symlink_and_use_dotnet()
{
// Creating symbolic links requires administrative privilege on Windows, so skip test.
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
return;
var fixture = sharedTestState.StandaloneAppFixture_Published
.Copy();
var dotnet = fixture.BuiltDotnet;
var binDir = fixture.TestProject.OutputDirectory;
var binDirNewPath = Path.Combine(Directory.GetParent(fixture.TestProject.Location).ToString(), "PutTheBinDirSomewhereElse");
Directory.Move(binDir, binDirNewPath);
CreateSymbolicLink(binDir, binDirNewPath);
dotnet.Exec(fixture.TestProject.AppDll)
.CaptureStdErr()
.CaptureStdOut()
.Execute()
.Should().Pass()
.And.HaveStdOutContaining("Hello World");
}
[Fact]
public void Put_app_directory_behind_symlink_and_use_dotnet_run()
{
// Creating symbolic links requires administrative privilege on Windows, so skip test.
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
return;
var fixture = sharedTestState.StandaloneAppFixture_Published
.Copy();
var dotnet = fixture.SdkDotnet;
var binDir = fixture.TestProject.OutputDirectory;
var binDirNewPath = Path.Combine(Directory.GetParent(fixture.TestProject.Location).ToString(), "PutTheBinDirSomewhereElse");
Directory.Move(binDir, binDirNewPath);
CreateSymbolicLink(binDir, binDirNewPath);
dotnet.Exec("run")
.WorkingDirectory(fixture.TestProject.Location)
.CaptureStdErr()
.CaptureStdOut()
.Execute()
.Should().Pass()
.And.HaveStdOutContaining("Hello World");
}
[Fact]
public void Put_satellite_assembly_behind_symlink()
{
// Creating symbolic links requires administrative privilege on Windows, so skip test.
// If enabled, this tests will need to set the console code page to output unicode characters:
// Command.Create("chcp 65001").Execute();
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
return;
var fixture = sharedTestState.StandaloneAppFixture_Localized
.Copy();
var appExe = fixture.TestProject.AppExe;
var binDir = fixture.TestProject.OutputDirectory;
var satellitesDir = Path.Combine(Directory.GetParent(fixture.TestProject.Location).ToString(), "PutSatellitesSomewhereElse");
Directory.CreateDirectory(satellitesDir);
var firstSatelliteDir = Directory.GetDirectories(binDir).Single(dir => dir.Contains("kn-IN"));
var firstSatelliteNewDir = Path.Combine(satellitesDir, "kn-IN");
Directory.Move(firstSatelliteDir, firstSatelliteNewDir);
CreateSymbolicLink(firstSatelliteDir, firstSatelliteNewDir);
var secondSatelliteDir = Directory.GetDirectories(binDir).Single(dir => dir.Contains("ta-IN"));
var secondSatelliteNewDir = Path.Combine(satellitesDir, "ta-IN");
Directory.Move(secondSatelliteDir, secondSatelliteNewDir);
CreateSymbolicLink(secondSatelliteDir, secondSatelliteNewDir);
Command.Create(appExe)
.CaptureStdErr()
.CaptureStdOut()
.Execute()
.Should().Pass()
.And.HaveStdOutContaining("ನಮಸ್ಕಾರ! வணக்கம்! Hello!");
}
public class SharedTestState : IDisposable
{
public TestProjectFixture StandaloneAppFixture_Localized { get; }
public TestProjectFixture StandaloneAppFixture_Published { get; }
public TestProjectFixture FrameworkDependentAppFixture_Published { get; }
public RepoDirectoriesProvider RepoDirectories { get; }
public SharedTestState()
{
RepoDirectories = new RepoDirectoriesProvider();
var localizedFixture = new TestProjectFixture("LocalizedApp", RepoDirectories);
localizedFixture
.EnsureRestoredForRid(localizedFixture.CurrentRid)
.PublishProject(runtime: localizedFixture.CurrentRid);
var publishFixture = new TestProjectFixture("StandaloneApp", RepoDirectories);
publishFixture
.EnsureRestoredForRid(publishFixture.CurrentRid)
.PublishProject(runtime: publishFixture.CurrentRid);
var fwPublishedFixture = new TestProjectFixture("PortableApp", RepoDirectories);
fwPublishedFixture
.EnsureRestored()
.PublishProject();
StandaloneAppFixture_Localized = localizedFixture;
StandaloneAppFixture_Published = publishFixture;
FrameworkDependentAppFixture_Published = fwPublishedFixture;
}
public void Dispose()
{
StandaloneAppFixture_Localized.Dispose();
StandaloneAppFixture_Published.Dispose();
FrameworkDependentAppFixture_Published.Dispose();
}
}
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Runtime.InteropServices;
namespace Microsoft.DotNet.CoreSetup.Test
{
public static class SymbolicLinking
{
static class Kernel32
{
[Flags]
internal enum SymbolicLinkFlag
{
IsFile = 0x0,
IsDirectory = 0x1,
AllowUnprivilegedCreate = 0x2
}
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool CreateSymbolicLink(
string symbolicLinkName,
string targetFileName,
SymbolicLinkFlag flags);
}
static class libc
{
[DllImport("libc", SetLastError = true)]
internal static extern int symlink(
string targetFileName,
string linkPath);
[DllImport("libc", CharSet = CharSet.Ansi)]
internal static extern IntPtr strerror(int errnum);
}
public static bool MakeSymbolicLink(string symbolicLinkName, string targetFileName, out string errorMessage)
{
errorMessage = string.Empty;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
if (!Kernel32.CreateSymbolicLink(symbolicLinkName, targetFileName, Kernel32.SymbolicLinkFlag.IsFile))
{
int errno = Marshal.GetLastWin32Error();
errorMessage = $"CreateSymbolicLink failed with error number {errno}";
return false;
}
}
else
{
if (libc.symlink(targetFileName, symbolicLinkName) == -1)
{
int errno = Marshal.GetLastWin32Error();
errorMessage = Marshal.PtrToStringAnsi(libc.strerror(errno));
return false;
}
}
return true;
}
}
}
......@@ -30,17 +30,15 @@ static pal::string_t normalize_dir_separator(const pal::string_t& path)
// Parameters:
// base - The base directory to look for the relative path of this entry
// ietf_dir - If this is a resource asset, the IETF intermediate directory
// look_in_base - Whether to search as a relative path
// look_in_bundle - Whether to look within the single-file bundle
// is_servicing - Whether the base directory is the core-servicing directory
// str - (out parameter) If the method returns true, contains the file path for this deps entry
// search_options - Flags to instruct where to look for this deps entry
// found_in_bundle - (out parameter) True if the candidate is located within the single-file bundle.
//
// Returns:
// If the file exists in the path relative to the "base" directory within the
// single-file or on disk.
bool deps_entry_t::to_path(const pal::string_t& base, const pal::string_t& ietf_dir, bool look_in_base, bool look_in_bundle, bool is_servicing, pal::string_t* str, bool &found_in_bundle) const
bool deps_entry_t::to_path(const pal::string_t& base, const pal::string_t& ietf_dir, pal::string_t* str, uint32_t search_options, bool &found_in_bundle) const
{
pal::string_t& candidate = *str;
......@@ -58,6 +56,9 @@ bool deps_entry_t::to_path(const pal::string_t& base, const pal::string_t& ietf_
// Reserve space for the path below
candidate.reserve(base.length() + ietf_dir.length() + normalized_path.length() + 3);
bool look_in_base = search_options & deps_entry_t::search_options::look_in_base;
bool look_in_bundle = search_options & deps_entry_t::search_options::look_in_bundle;
bool is_servicing = search_options & deps_entry_t::search_options::is_servicing;
pal::string_t file_path = look_in_base ? get_filename(normalized_path) : normalized_path;
pal::string_t sub_path = ietf_dir;
append_path(&sub_path, file_path.c_str());
......@@ -94,36 +95,41 @@ bool deps_entry_t::to_path(const pal::string_t& base, const pal::string_t& ietf_
candidate.assign(base);
append_path(&candidate, sub_path.c_str());
bool exists = pal::file_exists(candidate);
const pal::char_t* query_type = look_in_base ? _X("Local") : _X("Relative");
if (!exists)
if (search_options & deps_entry_t::search_options::file_existence)
{
trace::verbose(_X(" %s path query did not exist %s"), query_type, candidate.c_str());
candidate.clear();
if (!pal::file_exists(candidate))
{
trace::verbose(_X(" %s path query did not exist %s"), query_type, candidate.c_str());
candidate.clear();
return false;
}
trace::verbose(_X(" %s path query exists %s"), query_type, candidate.c_str());
}
else
{
trace::verbose(_X(" %s path query exists %s"), query_type, candidate.c_str());
trace::verbose(_X(" %s path query %s (skipped file existence check)"), query_type, candidate.c_str());
}
// If a file is resolved to the servicing directory, mark it as disabled in the bundle.
// This step is necessary because runtime will try to resolve assemblies from the bundle
// before it uses the TPA. So putting the servicing entry into TPA is not enough, since runtime would
// resolve it from the bundle first anyway. Disabling the file's entry in the bundle
// ensures that the servicing entry in the TPA gets priority.
if (is_servicing && bundle::info_t::is_single_file_bundle())
{
bundle::runner_t* app = bundle::runner_t::mutable_app();
assert(!app->has_base(base));
assert(!found_in_bundle);
// If a file is resolved to the servicing directory, mark it as disabled in the bundle.
// This step is necessary because runtime will try to resolve assemblies from the bundle
// before it uses the TPA. So putting the servicing entry into TPA is not enough, since runtime would
// resolve it from the bundle first anyway. Disabling the file's entry in the bundle
// ensures that the servicing entry in the TPA gets priority.
if (is_servicing && bundle::info_t::is_single_file_bundle())
{
bundle::runner_t* app = bundle::runner_t::mutable_app();
assert(!app->has_base(base));
assert(!found_in_bundle);
if (app->disable(sub_path))
{
trace::verbose(_X(" %s disabled in bundle because of servicing override %s"), sub_path.c_str(), candidate.c_str());
}
if (app->disable(sub_path))
{
trace::verbose(_X(" %s disabled in bundle because of servicing override %s"), sub_path.c_str(), candidate.c_str());
}
}
return exists;
return true;
}
// -----------------------------------------------------------------------------
......@@ -132,12 +138,13 @@ bool deps_entry_t::to_path(const pal::string_t& base, const pal::string_t& ietf_
// Parameters:
// base - The base directory to look for the relative path of this entry
// str - If the method returns true, contains the file path for this deps entry
// search_options - Flags to instruct where to look for this deps entry
// look_in_bundle - Whether to look within the single-file bundle
//
// Returns:
// If the file exists in the path relative to the "base" directory.
//
bool deps_entry_t::to_dir_path(const pal::string_t& base, bool look_in_bundle, pal::string_t* str, bool& found_in_bundle) const
bool deps_entry_t::to_dir_path(const pal::string_t& base, pal::string_t* str, uint32_t search_options, bool& found_in_bundle) const
{
pal::string_t ietf_dir;
......@@ -159,7 +166,10 @@ bool deps_entry_t::to_dir_path(const pal::string_t& base, bool look_in_bundle, p
base.c_str(), ietf_dir.c_str(), asset.name.c_str());
}
return to_path(base, ietf_dir, true, look_in_bundle, false, str, found_in_bundle);
search_options |= deps_entry_t::search_options::look_in_base;
search_options &= ~deps_entry_t::search_options::is_servicing;
return to_path(base, ietf_dir, str, search_options, found_in_bundle);
}
// -----------------------------------------------------------------------------
......@@ -169,16 +179,16 @@ bool deps_entry_t::to_dir_path(const pal::string_t& base, bool look_in_bundle, p
// Parameters:
// base - The base directory to look for the relative path of this entry
// str - If the method returns true, contains the file path for this deps entry
// look_in_bundle - Whether to look within the single-file bundle
// is_servicing - Whether the base directory is the core-servicing directory
// search_options - Flags to instruct where to look for this deps entry
//
// Returns:
// If the file exists in the path relative to the "base" directory.
//
bool deps_entry_t::to_rel_path(const pal::string_t& base, bool look_in_bundle, bool is_servicing, pal::string_t* str) const
bool deps_entry_t::to_rel_path(const pal::string_t& base, pal::string_t* str, uint32_t search_options) const
{
bool found_in_bundle;
bool result = to_path(base, _X(""), false, look_in_bundle, is_servicing, str, found_in_bundle);
search_options &= ~deps_entry_t::search_options::look_in_base;
bool result = to_path(base, _X(""), str, search_options, found_in_bundle);
assert(!found_in_bundle);
return result;
}
......@@ -190,12 +200,12 @@ bool deps_entry_t::to_rel_path(const pal::string_t& base, bool look_in_bundle, b
// Parameters:
// base - The base directory to look for the relative path of this entry
// str - If the method returns true, contains the file path for this deps entry
// is_servicing - Whether the base directory is the core-servicing directory
// search_options - Flags to instruct where to look for this deps entry
//
// Returns:
// If the file exists in the path relative to the "base" directory.
//
bool deps_entry_t::to_full_path(const pal::string_t& base, bool is_servicing, pal::string_t* str) const
bool deps_entry_t::to_full_path(const pal::string_t& base, pal::string_t* str, uint32_t search_options) const
{
str->clear();
......@@ -217,5 +227,6 @@ bool deps_entry_t::to_full_path(const pal::string_t& base, bool is_servicing, pa
append_path(&new_base, library_path.c_str());
}
return to_rel_path(new_base, false, is_servicing, str);
search_options &= ~deps_entry_t::search_options::look_in_bundle;
return to_rel_path(new_base, str, search_options);
}
......@@ -36,6 +36,15 @@ struct deps_entry_t
count
};
enum search_options : uint32_t
{
none = 0x0,
look_in_base = 0x1, // Search entry as a relative path
look_in_bundle = 0x2, // Look for entry within the single-file bundle
is_servicing = 0x4, // Whether the base directory is the core-servicing directory
file_existence = 0x8, // Check for entry file existence
};
static const std::array<const pal::char_t*, deps_entry_t::asset_types::count> s_known_asset_types;
pal::string_t deps_file;
......@@ -52,18 +61,19 @@ struct deps_entry_t
bool is_rid_specific;
// Given a "base" dir, yield the file path within this directory or single-file bundle.
bool to_dir_path(const pal::string_t& base, bool look_in_bundle, pal::string_t* str, bool& found_in_bundle) const;
bool to_dir_path(const pal::string_t& base, pal::string_t* str, uint32_t search_options, bool& found_in_bundle) const;
// Given a "base" dir, yield the relative path in the package layout or servicing directory.
bool to_rel_path(const pal::string_t& base, bool look_in_bundle, bool is_servicing, pal::string_t* str) const;
bool to_rel_path(const pal::string_t& base, pal::string_t* str, uint32_t search_options) const;
// Given a "base" dir, yield the relative path with package name/version in the package layout or servicing location.
bool to_full_path(const pal::string_t& base, bool is_servicing, pal::string_t* str) const;
bool to_full_path(const pal::string_t& base, pal::string_t* str, uint32_t search_options) const;
private:
// Given a "base" dir, yield the filepath within this directory or relative to this directory based on "look_in_base"
// flag in "search_options".
// Returns a path within the single-file bundle, or a file on disk,
bool to_path(const pal::string_t& base, const pal::string_t& ietf_code, bool look_in_base, bool look_in_bundle, bool is_servicing, pal::string_t* str, bool & found_in_bundle) const;
bool to_path(const pal::string_t& base, const pal::string_t& ietf_code, pal::string_t* str, uint32_t search_options, bool & found_in_bundle) const;
};
......
......@@ -43,29 +43,28 @@ void add_unique_path(
pal::string_t* non_serviced,
const pal::string_t& svc_dir)
{
// Resolve sym links.
pal::string_t real = path;
pal::realpath(&real);
if (existing->count(real))
// To optimize startup time, we avoid calling realpath here.
// Because of this, there might be duplicates in the output
// whenever path is eiter non-normalized or a symbolic link.
if (existing->count(path))
{
return;
}
trace::verbose(_X("Adding to %s path: %s"), deps_entry_t::s_known_asset_types[asset_type], real.c_str());
trace::verbose(_X("Adding to %s path: %s"), deps_entry_t::s_known_asset_types[asset_type], path.c_str());
if (starts_with(real, svc_dir, false))
if (starts_with(path, svc_dir, false))
{
serviced->append(real);
serviced->append(path);
serviced->push_back(PATH_SEPARATOR);
}
else
{
non_serviced->append(real);
non_serviced->append(path);
non_serviced->push_back(PATH_SEPARATOR);
}
existing->insert(real);
existing->insert(path);
}
// Return the filename from deps path; a deps path always uses a '/' for the separator.
......@@ -185,6 +184,7 @@ void deps_resolver_t::setup_shared_store_probes(
{
// Shared Store probe: DOTNET_SHARED_STORE environment variable
m_probes.push_back(probe_config_t::lookup(shared));
m_needs_file_existence_checks = true;
}
}
......@@ -192,6 +192,7 @@ void deps_resolver_t::setup_shared_store_probes(
{
// Path relative to the location of "dotnet.exe" if it's being used to run the app
m_probes.push_back(probe_config_t::lookup(args.dotnet_shared_store));
m_needs_file_existence_checks = true;
}
for (const auto& global_shared : args.global_shared_stores)
......@@ -200,6 +201,7 @@ void deps_resolver_t::setup_shared_store_probes(
{
// Global store probe: the global location
m_probes.push_back(probe_config_t::lookup(global_shared));
m_needs_file_existence_checks = true;
}
}
}
......@@ -236,6 +238,8 @@ void deps_resolver_t::setup_probe_config(
pal::string_t ext_pkgs = args.core_servicing;
append_path(&ext_pkgs, _X("pkgs"));
m_probes.push_back(probe_config_t::svc(ext_pkgs));
m_needs_file_existence_checks = true;
}
// The published deps directory to be probed: either app or FX directory.
......@@ -253,10 +257,15 @@ void deps_resolver_t::setup_probe_config(
setup_shared_store_probes(args);
for (const auto& probe : m_additional_probes)
if (m_additional_probes.size() > 0)
{
// Additional paths
m_probes.push_back(probe_config_t::lookup(probe));
for (const auto& probe : m_additional_probes)
{
// Additional paths
m_probes.push_back(probe_config_t::lookup(probe));
}
m_needs_file_existence_checks = true;
}
if (trace::is_enabled())
......@@ -304,6 +313,11 @@ bool deps_resolver_t::probe_deps_entry(const deps_entry_t& entry, const pal::str
continue;
}
pal::string_t probe_dir = config.probe_dir;
uint32_t search_options = deps_entry_t::search_options::none;
if (needs_file_existence_checks())
{
search_options |= deps_entry_t::search_options::file_existence;
}
if (config.is_fx())
{
......@@ -318,7 +332,7 @@ bool deps_resolver_t::probe_deps_entry(const deps_entry_t& entry, const pal::str
// If the deps json has the package name and version, then someone has already done rid selection and
// put the right asset in the dir. So checking just package name and version would suffice.
// No need to check further for the exact asset relative sub path.
if (config.probe_deps_json->has_package(entry.library_name, entry.library_version) && entry.to_dir_path(probe_dir, false, candidate, found_in_bundle))
if (config.probe_deps_json->has_package(entry.library_name, entry.library_version) && entry.to_dir_path(probe_dir, candidate, search_options, found_in_bundle))
{
assert(!found_in_bundle);
trace::verbose(_X(" Probed deps json and matched '%s'"), candidate->c_str());
......@@ -337,7 +351,7 @@ bool deps_resolver_t::probe_deps_entry(const deps_entry_t& entry, const pal::str
{
if (entry.is_rid_specific)
{
if (entry.to_rel_path(deps_dir, true, false, candidate))
if (entry.to_rel_path(deps_dir, candidate, search_options | deps_entry_t::search_options::look_in_bundle))
{
trace::verbose(_X(" Probed deps dir and matched '%s'"), candidate->c_str());
return true;
......@@ -346,7 +360,7 @@ bool deps_resolver_t::probe_deps_entry(const deps_entry_t& entry, const pal::str
else
{
// Non-rid assets, lookup in the published dir.
if (entry.to_dir_path(deps_dir, true, candidate, found_in_bundle))
if (entry.to_dir_path(deps_dir, candidate, search_options | deps_entry_t::search_options::look_in_bundle, found_in_bundle))
{
trace::verbose(_X(" Probed deps dir and matched '%s'"), candidate->c_str());
return true;
......@@ -356,7 +370,7 @@ bool deps_resolver_t::probe_deps_entry(const deps_entry_t& entry, const pal::str
trace::verbose(_X(" Skipping... not found in deps dir '%s'"), deps_dir.c_str());
}
else if (entry.to_full_path(probe_dir, config.only_serviceable_assets, candidate))
else if (entry.to_full_path(probe_dir, candidate, search_options | (config.only_serviceable_assets ? deps_entry_t::search_options::is_servicing : 0)))
{
trace::verbose(_X(" Probed package dir and matched '%s'"), candidate->c_str());
return true;
......@@ -588,10 +602,7 @@ bool deps_resolver_t::resolve_tpa_list(
// Convert the paths into a string and return it
for (const auto& item : items)
{
// Workaround for CoreFX not being able to resolve sym links.
pal::string_t real_asset_path = item.second.resolved_path;
pal::realpath(&real_asset_path);
output->append(real_asset_path);
output->append(item.second.resolved_path);
output->push_back(PATH_SEPARATOR);
}
......
......@@ -52,6 +52,7 @@ public:
, m_managed_app(args.managed_application)
, m_core_servicing(args.core_servicing)
, m_is_framework_dependent(is_framework_dependent)
, m_needs_file_existence_checks(false)
{
int lowest_framework = static_cast<int>(m_fx_definitions.size()) - 1;
int root_framework = -1;
......@@ -90,6 +91,11 @@ public:
setup_additional_probes(args.probe_paths);
setup_probe_config(args);
if (m_additional_deps.size() > 0)
{
m_needs_file_existence_checks = true;
}
}
bool valid(pal::string_t* errors)
......@@ -172,6 +178,11 @@ public:
return m_is_framework_dependent;
}
bool needs_file_existence_checks() const
{
return m_needs_file_existence_checks;
}
void get_app_dir(pal::string_t *app_dir) const
{
if (m_host_mode == host_mode_t::libhost)
......@@ -272,6 +283,9 @@ private:
// Is the deps file for an app using shared frameworks?
const bool m_is_framework_dependent;
// File existence checks must be performed for probed paths.This will cause symlinks to be resolved.
bool m_needs_file_existence_checks;
};
#endif // DEPS_RESOLVER_H
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册