Merge branch 'master' of https://github.com/dotnet/cli into centos-builds
Conflicts: scripts/compile.sh scripts/test/smoke-test.sh
This commit is contained in:
commit
3363707704
102 changed files with 3847 additions and 823 deletions
|
@ -5,7 +5,7 @@ using System.IO;
|
|||
|
||||
namespace Microsoft.DotNet.Cli.Utils
|
||||
{
|
||||
internal struct CommandResult
|
||||
public struct CommandResult
|
||||
{
|
||||
public static readonly CommandResult Empty = new CommandResult();
|
||||
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
{
|
||||
"version": "1.0.0-*",
|
||||
|
||||
"compilationOptions": {
|
||||
"keyFile": "../../tools/Key.snk"
|
||||
},
|
||||
|
||||
"dependencies": {
|
||||
"System.Reflection": "4.0.10-rc2-23616",
|
||||
"NETStandard.Library": "1.0.0-rc2-23616",
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
{
|
||||
"version": "1.0.0-*",
|
||||
"compilationOptions": {
|
||||
"keyFile": "../../tools/Key.snk"
|
||||
},
|
||||
"dependencies": {
|
||||
"NETStandard.Library": "1.0.0-rc2-23616",
|
||||
"Microsoft.DotNet.ProjectModel": "1.0.0-*",
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
{
|
||||
"version": "1.0.0-*",
|
||||
"compilationOptions": {
|
||||
"keyFile": "../../tools/Key.snk"
|
||||
},
|
||||
"dependencies": {
|
||||
"NETStandard.Library": "1.0.0-rc2-23616",
|
||||
"Microsoft.DotNet.ProjectModel": "1.0.0-*",
|
||||
|
|
|
@ -29,6 +29,8 @@ namespace Microsoft.DotNet.ProjectModel
|
|||
|
||||
public bool? EmitEntryPoint { get; set; }
|
||||
|
||||
public bool? PreserveCompilationContext { get; set; }
|
||||
|
||||
public static CommonCompilerOptions Combine(params CommonCompilerOptions[] options)
|
||||
{
|
||||
var result = new CommonCompilerOptions();
|
||||
|
@ -91,6 +93,11 @@ namespace Microsoft.DotNet.ProjectModel
|
|||
{
|
||||
result.EmitEntryPoint = option.EmitEntryPoint;
|
||||
}
|
||||
|
||||
if (option.PreserveCompilationContext != null)
|
||||
{
|
||||
result.PreserveCompilationContext = option.PreserveCompilationContext;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.DotNet.ProjectModel;
|
||||
using Microsoft.DotNet.ProjectModel.Compilation;
|
||||
using Microsoft.DotNet.ProjectModel.Graph;
|
||||
|
||||
namespace Microsoft.Extensions.DependencyModel
|
||||
{
|
||||
public static class DependencyContextBuilder
|
||||
{
|
||||
public static DependencyContext FromLibraryExporter(LibraryExporter libraryExporter, string target, string runtime)
|
||||
{
|
||||
var dependencies = libraryExporter.GetAllExports();
|
||||
|
||||
return new DependencyContext(target, runtime,
|
||||
GetLibraries(dependencies, export => export.CompilationAssemblies),
|
||||
GetLibraries(dependencies, export => export.RuntimeAssemblies));
|
||||
}
|
||||
|
||||
private static Library[] GetLibraries(IEnumerable<LibraryExport> dependencies, Func<LibraryExport, IEnumerable<LibraryAsset>> assemblySelector)
|
||||
{
|
||||
return dependencies.Select(export => GetLibrary(export, assemblySelector(export), dependencies)).ToArray();
|
||||
}
|
||||
|
||||
private static Library GetLibrary(LibraryExport export, IEnumerable<LibraryAsset> libraryAssets, IEnumerable<LibraryExport> dependencies)
|
||||
{
|
||||
var serviceable = (export.Library as PackageDescription)?.Library.IsServiceable ?? false;
|
||||
var version = dependencies.Where(dependency => dependency.Library.Identity == export.Library.Identity);
|
||||
|
||||
var libraryDependencies = export.Library.Dependencies.Select(libraryRange => GetDependency(libraryRange, dependencies)).ToArray();
|
||||
|
||||
return new Library(
|
||||
export.Library.Identity.Type.ToString().ToLowerInvariant(),
|
||||
export.Library.Identity.Name,
|
||||
export.Library.Identity.Version.ToString(),
|
||||
export.Library.Hash,
|
||||
libraryAssets.Select(libraryAsset => libraryAsset.RelativePath).ToArray(),
|
||||
libraryDependencies,
|
||||
serviceable
|
||||
);
|
||||
}
|
||||
|
||||
private static Dependency GetDependency(LibraryRange libraryRange, IEnumerable<LibraryExport> dependencies)
|
||||
{
|
||||
var version =
|
||||
dependencies.First(d => d.Library.Identity.Name == libraryRange.Name)
|
||||
.Library.Identity.Version.ToString();
|
||||
return new Dependency(libraryRange.Name, version);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -525,7 +525,8 @@ namespace Microsoft.DotNet.ProjectModel
|
|||
KeyFile = rawOptions.ValueAsString("keyFile"),
|
||||
DelaySign = rawOptions.ValueAsNullableBoolean("delaySign"),
|
||||
PublicSign = rawOptions.ValueAsNullableBoolean("publicSign"),
|
||||
EmitEntryPoint = rawOptions.ValueAsNullableBoolean("emitEntryPoint")
|
||||
EmitEntryPoint = rawOptions.ValueAsNullableBoolean("emitEntryPoint"),
|
||||
PreserveCompilationContext = rawOptions.ValueAsNullableBoolean("preserveCompilationContext")
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
{
|
||||
"version": "1.0.0-*",
|
||||
"compilationOptions": {
|
||||
"keyFile": "../../tools/Key.snk"
|
||||
},
|
||||
"description": "Types to model a .NET Project",
|
||||
"dependencies": {
|
||||
"NETStandard.Library": "1.0.0-rc2-23616",
|
||||
|
@ -19,6 +22,10 @@
|
|||
"Microsoft.Extensions.HashCodeCombiner.Sources": {
|
||||
"type": "build",
|
||||
"version": "1.0.0-*"
|
||||
},
|
||||
"Microsoft.Extensions.DependencyModel": {
|
||||
"type": "build",
|
||||
"version": "1.0.0-*"
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"version": "1.0.0-*",
|
||||
"compilationOptions": {
|
||||
"emitEntryPoint": true
|
||||
"emitEntryPoint": true
|
||||
},
|
||||
|
||||
"dependencies": {
|
||||
|
|
|
@ -16,6 +16,8 @@ namespace Microsoft.DotNet.Tools.Compiler.Native
|
|||
public IEnumerable<string> LinkLibPaths { get; set; }
|
||||
public string AppDepSDKPath { get; set; }
|
||||
public string IlcPath { get; set; }
|
||||
public string IlcSdkPath { get; set; }
|
||||
public string CppCompilerFlags { get; set; }
|
||||
|
||||
public bool IsHelp { get; set; }
|
||||
public int ReturnCode { get; set; }
|
||||
|
@ -55,6 +57,12 @@ namespace Microsoft.DotNet.Tools.Compiler.Native
|
|||
if (!string.IsNullOrEmpty(IlcPath))
|
||||
{
|
||||
config.IlcPath = IlcPath;
|
||||
config.IlcSdkPath = IlcPath;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(IlcSdkPath))
|
||||
{
|
||||
config.IlcSdkPath = IlcSdkPath;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(LogPath))
|
||||
|
@ -67,6 +75,11 @@ namespace Microsoft.DotNet.Tools.Compiler.Native
|
|||
config.IlcArgs = IlcArgs;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(CppCompilerFlags))
|
||||
{
|
||||
config.CppCompilerFlags = CppCompilerFlags;
|
||||
}
|
||||
|
||||
foreach (var reference in ReferencePaths)
|
||||
{
|
||||
config.AddReference(reference);
|
||||
|
|
|
@ -17,11 +17,13 @@ namespace Microsoft.DotNet.Tools.Compiler.Native
|
|||
NativeIntermediateMode? nativeMode = null;
|
||||
string ilcArgs = null;
|
||||
string ilcPath = null;
|
||||
string ilcSdkPath = null;
|
||||
string appDepSdk = null;
|
||||
string logPath = null;
|
||||
var help = false;
|
||||
string helpText = null;
|
||||
var returnCode = 0;
|
||||
string cppCompilerFlags = null;
|
||||
|
||||
IReadOnlyList<string> references = Array.Empty<string>();
|
||||
IReadOnlyList<string> linklib = Array.Empty<string>();
|
||||
|
@ -45,7 +47,9 @@ namespace Microsoft.DotNet.Tools.Compiler.Native
|
|||
|
||||
// Custom Extensibility Points to support CoreRT workflow TODO better descriptions
|
||||
syntax.DefineOption("ilcargs", ref ilcArgs, "Use to specify custom arguments for the IL Compiler.");
|
||||
syntax.DefineOption("ilcpath", ref ilcPath, "Use to plug in a custom built ilc.exe");
|
||||
syntax.DefineOption("ilcpath", ref ilcPath, "Use to specify a custom build of IL Compiler.");
|
||||
syntax.DefineOption("ilcsdkpath", ref ilcSdkPath, "Use to specify a custom build of IL Compiler SDK");
|
||||
|
||||
syntax.DefineOptionList("linklib", ref linklib, "Use to link in additional static libs");
|
||||
|
||||
// TEMPORARY Hack until CoreRT compatible Framework Libs are available
|
||||
|
@ -54,6 +58,9 @@ namespace Microsoft.DotNet.Tools.Compiler.Native
|
|||
// Optional Log Path
|
||||
syntax.DefineOption("logpath", ref logPath, "Use to dump Native Compilation Logs to a file.");
|
||||
|
||||
// Optional flags to be passed to the native compiler
|
||||
syntax.DefineOption("cppcompilerflags", ref cppCompilerFlags, "Additional flags to be passed to the native compiler.");
|
||||
|
||||
syntax.DefineOption("h|help", ref help, "Help for compile native.");
|
||||
|
||||
syntax.DefineParameter("INPUT_ASSEMBLY", ref inputAssembly,
|
||||
|
@ -125,9 +132,11 @@ namespace Microsoft.DotNet.Tools.Compiler.Native
|
|||
ReferencePaths = references,
|
||||
IlcArgs = ilcArgs,
|
||||
IlcPath = ilcPath,
|
||||
IlcSdkPath = ilcSdkPath,
|
||||
LinkLibPaths = linklib,
|
||||
AppDepSDKPath = appDepSdk,
|
||||
LogPath = logPath
|
||||
LogPath = logPath,
|
||||
CppCompilerFlags = cppCompilerFlags
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,11 +46,10 @@ namespace Microsoft.DotNet.Tools.Compiler.Native
|
|||
argsList.Add($"\"{inputFilePath}\"");
|
||||
|
||||
// System.Private.CoreLib Reference
|
||||
String[] coreLibs = new String[] { "System.Private.CoreLib.dll", "System.Private.Corelib.dll" };
|
||||
var coreLibPath = Path.Combine(config.IlcPath, Array.Find(coreLibs, lib => File.Exists(Path.Combine(config.IlcPath, lib))));
|
||||
var coreLibPath = Path.Combine(config.IlcSdkPath, "System.Private.CoreLib.dll");
|
||||
argsList.Add($"-r \"{coreLibPath}\"");
|
||||
|
||||
// Dependency References
|
||||
// AppDep References
|
||||
foreach (var reference in config.ReferencePaths)
|
||||
{
|
||||
argsList.Add($"-r \"{reference}\"");
|
||||
|
|
|
@ -19,7 +19,7 @@ namespace Microsoft.DotNet.Tools.Compiler.Native
|
|||
private readonly string cLibsFlags = "-lm -ldl";
|
||||
private readonly string cflags = "-g -lstdc++ -lrt -Wno-invalid-offsetof -pthread";
|
||||
|
||||
private readonly string[] libs = new string[]
|
||||
private readonly string[] IlcSdkLibs = new string[]
|
||||
{
|
||||
"libbootstrappercpp.a",
|
||||
"libPortableRuntime.a",
|
||||
|
@ -65,24 +65,29 @@ namespace Microsoft.DotNet.Tools.Compiler.Native
|
|||
// Flags
|
||||
argsList.Add(cflags);
|
||||
|
||||
// Add Includes
|
||||
// TODO: Enable this when https://github.com/dotnet/cli/pull/469 goes through.
|
||||
// var ilcSdkIncPath = Path.Combine(config.IlcSdkPath, "inc");
|
||||
//
|
||||
// Get the directory name to ensure there are no trailing slashes as they may conflict
|
||||
// with the terminating " we suffix to account for paths with spaces in them.
|
||||
var ilcSdkIncPath = Path.GetDirectoryName(config.IlcSdkPath);
|
||||
argsList.Add("-I");
|
||||
argsList.Add(Path.Combine(config.AppDepSDKPath, "CPPSdk/ubuntu.14.04"));
|
||||
|
||||
argsList.Add("-I");
|
||||
argsList.Add(Path.Combine(config.AppDepSDKPath, "CPPSdk"));
|
||||
argsList.Add($"\"{ilcSdkIncPath}\"");
|
||||
|
||||
// Input File
|
||||
var inCppFile = DetermineInFile(config);
|
||||
argsList.Add(inCppFile);
|
||||
|
||||
// Add Stubs
|
||||
argsList.Add(Path.Combine(config.AppDepSDKPath, "CPPSdk/ubuntu.14.04/lxstubs.cpp"));
|
||||
|
||||
// Libs
|
||||
foreach (var lib in libs)
|
||||
// Pass the optional native compiler flags if specified
|
||||
if (!string.IsNullOrWhiteSpace(config.CppCompilerFlags))
|
||||
{
|
||||
var libPath = Path.Combine(config.IlcPath, lib);
|
||||
argsList.Add(config.CppCompilerFlags);
|
||||
}
|
||||
|
||||
// ILC SDK Libs
|
||||
foreach (var lib in IlcSdkLibs)
|
||||
{
|
||||
var libPath = Path.Combine(config.IlcSdkPath, lib);
|
||||
argsList.Add(libPath);
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ namespace Microsoft.DotNet.Tools.Compiler.Native
|
|||
// TODO: debug/release support
|
||||
private readonly string cflags = "-lstdc++ -lpthread -ldl -lm -lrt";
|
||||
|
||||
private readonly string[] libs = new string[]
|
||||
private readonly string[] IlcSdkLibs = new string[]
|
||||
{
|
||||
"libbootstrapper.a",
|
||||
"libRuntime.a",
|
||||
|
@ -70,10 +70,16 @@ namespace Microsoft.DotNet.Tools.Compiler.Native
|
|||
var inLibFile = DetermineInFile(config);
|
||||
argsList.Add(inLibFile);
|
||||
|
||||
// Libs
|
||||
foreach (var lib in libs)
|
||||
// Pass the optional native compiler flags if specified
|
||||
if (!string.IsNullOrWhiteSpace(config.CppCompilerFlags))
|
||||
{
|
||||
var libPath = Path.Combine(config.IlcPath, lib);
|
||||
argsList.Add(config.CppCompilerFlags);
|
||||
}
|
||||
|
||||
// ILC SDK Libs
|
||||
foreach (var lib in IlcSdkLibs)
|
||||
{
|
||||
var libPath = Path.Combine(config.IlcSdkPath, lib);
|
||||
argsList.Add(libPath);
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ namespace Microsoft.DotNet.Tools.Compiler.Native
|
|||
// Link to iconv APIs
|
||||
private readonly string libFlags = "-liconv";
|
||||
|
||||
private readonly string[] libs = new string[]
|
||||
private readonly string[] IlcSdkLibs = new string[]
|
||||
{
|
||||
"libbootstrappercpp.a",
|
||||
"libPortableRuntime.a",
|
||||
|
@ -67,27 +67,32 @@ namespace Microsoft.DotNet.Tools.Compiler.Native
|
|||
// Flags
|
||||
argsList.Add(cflags);
|
||||
|
||||
// Add Includes
|
||||
// TODO: Enable this when https://github.com/dotnet/cli/pull/469 goes through.
|
||||
// var ilcSdkIncPath = Path.Combine(config.IlcSdkPath, "inc");
|
||||
//
|
||||
// Get the directory name to ensure there are no trailing slashes as they may conflict
|
||||
// with the terminating " we suffix to account for paths with spaces in them.
|
||||
var ilcSdkIncPath = Path.GetDirectoryName(config.IlcSdkPath);
|
||||
argsList.Add("-I");
|
||||
argsList.Add(Path.Combine(config.AppDepSDKPath, "CPPSdk/osx.10.10"));
|
||||
|
||||
argsList.Add("-I");
|
||||
argsList.Add(Path.Combine(config.AppDepSDKPath, "CPPSdk"));
|
||||
argsList.Add($"\"{ilcSdkIncPath}\"");
|
||||
|
||||
// Input File
|
||||
var inCppFile = DetermineInFile(config);
|
||||
argsList.Add(inCppFile);
|
||||
|
||||
// Add Stubs
|
||||
argsList.Add(Path.Combine(config.AppDepSDKPath, "CPPSdk/osx.10.10/osxstubs.cpp"));
|
||||
|
||||
// Lib flags
|
||||
argsList.Add(libFlags);
|
||||
|
||||
// Libs
|
||||
foreach (var lib in libs)
|
||||
// Pass the optional native compiler flags if specified
|
||||
if (!string.IsNullOrWhiteSpace(config.CppCompilerFlags))
|
||||
{
|
||||
var libPath = Path.Combine(config.IlcPath, lib);
|
||||
argsList.Add(config.CppCompilerFlags);
|
||||
}
|
||||
|
||||
// ILC SDK Libs
|
||||
foreach (var lib in IlcSdkLibs)
|
||||
{
|
||||
var libPath = Path.Combine(config.IlcSdkPath, lib);
|
||||
|
||||
// Forward the library to linked to the linker
|
||||
argsList.Add("-Xlinker");
|
||||
|
|
|
@ -20,7 +20,7 @@ namespace Microsoft.DotNet.Tools.Compiler.Native
|
|||
// TODO: debug/release support
|
||||
private readonly string cflags = "-g -lstdc++ -Wno-invalid-offsetof -pthread -ldl -lm -liconv";
|
||||
|
||||
private readonly string[] libs = new string[]
|
||||
private readonly string[] IlcSdkLibs = new string[]
|
||||
{
|
||||
"libbootstrapper.a",
|
||||
"libRuntime.a",
|
||||
|
@ -66,19 +66,20 @@ namespace Microsoft.DotNet.Tools.Compiler.Native
|
|||
// Flags
|
||||
argsList.Add(cflags);
|
||||
|
||||
// Add Stubs
|
||||
argsList.Add("-I "+Path.Combine(config.AppDepSDKPath, "CPPSdk/osx.10.10"));
|
||||
argsList.Add("-I "+Path.Combine(config.AppDepSDKPath, "CPPSdk"));
|
||||
argsList.Add(Path.Combine(config.AppDepSDKPath, "CPPSdk/osx.10.10/osxstubs.cpp"));
|
||||
|
||||
// Pass the optional native compiler flags if specified
|
||||
if (!string.IsNullOrWhiteSpace(config.CppCompilerFlags))
|
||||
{
|
||||
argsList.Add(config.CppCompilerFlags);
|
||||
}
|
||||
|
||||
// Input File
|
||||
var inLibFile = DetermineInFile(config);
|
||||
argsList.Add("-Xlinker "+inLibFile);
|
||||
|
||||
// Libs
|
||||
foreach (var lib in libs)
|
||||
// ILC SDK Libs
|
||||
foreach (var lib in IlcSdkLibs)
|
||||
{
|
||||
var libPath = Path.Combine(config.IlcPath, lib);
|
||||
var libPath = Path.Combine(config.IlcSdkPath, lib);
|
||||
argsList.Add("-Xlinker "+libPath);
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ namespace Microsoft.DotNet.Tools.Compiler.Native
|
|||
|
||||
private static readonly Dictionary<BuildConfiguration, string> ConfigurationCompilerOptionsMap = new Dictionary<BuildConfiguration, string>
|
||||
{
|
||||
{ BuildConfiguration.debug, "/ZI /nologo /W3 /WX- /sdl /Od /D CPPCODEGEN /D WIN32 /D _DEBUG /D _CONSOLE /D _LIB /D _UNICODE /D UNICODE /Gm /EHsc /RTC1 /MDd /GS /fp:precise /Zc:wchar_t /Zc:forScope /Zc:inline /Gd /TP /wd4477 /errorReport:prompt" },
|
||||
{ BuildConfiguration.debug, "/ZI /nologo /W3 /WX- /sdl /Od /D CPPCODEGEN /D WIN32 /D _CONSOLE /D _LIB /D _UNICODE /D UNICODE /Gm /EHsc /RTC1 /MD /GS /fp:precise /Zc:wchar_t /Zc:forScope /Zc:inline /Gd /TP /wd4477 /errorReport:prompt" },
|
||||
{ BuildConfiguration.release, "/Zi /nologo /W3 /WX- /sdl /O2 /Oi /GL /D CPPCODEGEN /D WIN32 /D NDEBUG /D _CONSOLE /D _LIB /D _UNICODE /D UNICODE /Gm- /EHsc /MD /GS /Gy /fp:precise /Zc:wchar_t /Zc:forScope /Zc:inline /Gd /TP /wd4477 /errorReport:prompt" }
|
||||
};
|
||||
|
||||
|
@ -68,17 +68,26 @@ namespace Microsoft.DotNet.Tools.Compiler.Native
|
|||
argsList.Add("/c");
|
||||
|
||||
// Add Includes
|
||||
var win7CppSdkPath = Path.Combine(config.AppDepSDKPath, "CPPSdk\\win7");
|
||||
//
|
||||
// TODO: Enable this when https://github.com/dotnet/cli/pull/469 goes through.
|
||||
// var ilcSdkIncPath = Path.Combine(config.IlcSdkPath, "inc");
|
||||
//
|
||||
// Get the directory name to ensure there are no trailing slashes as they may conflict
|
||||
// with the terminating " we suffix to account for paths with spaces in them.
|
||||
var ilcSdkIncPath = config.IlcSdkPath;
|
||||
ilcSdkIncPath = ilcSdkIncPath.TrimEnd(new char[] {'\\'});
|
||||
argsList.Add("/I");
|
||||
argsList.Add($"\"{win7CppSdkPath}\"");
|
||||
|
||||
var cppSdkPath = Path.Combine(config.AppDepSDKPath, "CPPSdk");
|
||||
argsList.Add("/I");
|
||||
argsList.Add($"\"{cppSdkPath}\"");
|
||||
argsList.Add($"\"{ilcSdkIncPath}\"");
|
||||
|
||||
// Configuration Based Compiler Options
|
||||
argsList.Add(ConfigurationCompilerOptionsMap[config.BuildType]);
|
||||
|
||||
// Pass the optional native compiler flags if specified
|
||||
if (!string.IsNullOrWhiteSpace(config.CppCompilerFlags))
|
||||
{
|
||||
argsList.Add(config.CppCompilerFlags);
|
||||
}
|
||||
|
||||
// Output
|
||||
var objOut = DetermineOutputFile(config);
|
||||
argsList.Add($"/Fo\"{objOut}\"");
|
||||
|
|
|
@ -24,7 +24,7 @@ namespace Microsoft.DotNet.Tools.Compiler.Native
|
|||
{ BuildConfiguration.release, "/NOLOGO /ERRORREPORT:PROMPT /INCREMENTAL:NO /OPT:REF /OPT:ICF /LTCG:incremental /MANIFEST /MANIFESTUAC:\"level='asInvoker' uiAccess='false'\" /manifest:embed /Debug /SUBSYSTEM:CONSOLE /TLBID:1 /DYNAMICBASE /NXCOMPAT" }
|
||||
};
|
||||
|
||||
private static readonly Dictionary<NativeIntermediateMode, string[]> ModeLibMap = new Dictionary<NativeIntermediateMode, string[]>
|
||||
private static readonly Dictionary<NativeIntermediateMode, string[]> IlcSdkLibMap = new Dictionary<NativeIntermediateMode, string[]>
|
||||
{
|
||||
{ NativeIntermediateMode.cpp, new string[] { "PortableRuntime.lib", "bootstrappercpp.lib" } },
|
||||
{ NativeIntermediateMode.ryujit, new string[] { "Runtime.lib", "bootstrapper.lib" } }
|
||||
|
@ -46,9 +46,11 @@ namespace Microsoft.DotNet.Tools.Compiler.Native
|
|||
"odbccp32.lib"
|
||||
};
|
||||
|
||||
// We will always link against msvcrt.lib since the runtime libraries are also built against msvcrt.lib as we are not interested in assertions
|
||||
// from CRT code.
|
||||
private static readonly Dictionary<BuildConfiguration, string[]> ConfigurationLinkLibMap = new Dictionary<BuildConfiguration, string[]>()
|
||||
{
|
||||
{ BuildConfiguration.debug , new string[] { "msvcrtd.lib" } },
|
||||
{ BuildConfiguration.debug , new string[] { "msvcrt.lib" } },
|
||||
{ BuildConfiguration.release , new string[] { "msvcrt.lib" } }
|
||||
};
|
||||
|
||||
|
@ -98,11 +100,11 @@ namespace Microsoft.DotNet.Tools.Compiler.Native
|
|||
// Constant Libs
|
||||
argsList.Add(string.Join(" ", ConstantLinkLibs));
|
||||
|
||||
// SDK Libs
|
||||
var SDKLibs = ModeLibMap[config.NativeMode];
|
||||
// ILC SDK Libs
|
||||
var SDKLibs = IlcSdkLibMap[config.NativeMode];
|
||||
foreach (var lib in SDKLibs)
|
||||
{
|
||||
var sdkLibPath = Path.Combine(config.IlcPath, lib);
|
||||
var sdkLibPath = Path.Combine(config.IlcSdkPath, lib);
|
||||
argsList.Add($"\"{sdkLibPath}\"");
|
||||
}
|
||||
|
||||
|
|
|
@ -14,12 +14,14 @@ namespace Microsoft.DotNet.Tools.Compiler.Native
|
|||
private string _inputManagedAssemblyPath;
|
||||
private string _appDepSdkPath;
|
||||
private string _ilcPath;
|
||||
private string _ilcSdkPath;
|
||||
private string _outputDirectory;
|
||||
private string _intermediateDirectory;
|
||||
private string _logPath;
|
||||
private string _ilcArgs;
|
||||
private readonly List<string> _referencePaths;
|
||||
private readonly List<string> _linkLibPaths;
|
||||
private string _cppCompilerFlags;
|
||||
|
||||
public string LogPath
|
||||
{
|
||||
|
@ -129,12 +131,44 @@ namespace Microsoft.DotNet.Tools.Compiler.Native
|
|||
}
|
||||
}
|
||||
|
||||
public string IlcSdkPath
|
||||
{
|
||||
get
|
||||
{
|
||||
return _ilcSdkPath;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (!Directory.Exists(value))
|
||||
{
|
||||
throw new Exception($"ILC SDK Directory does not exist: {value}.");
|
||||
}
|
||||
|
||||
_ilcSdkPath = value;
|
||||
}
|
||||
}
|
||||
|
||||
public string CppCompilerFlags
|
||||
{
|
||||
get
|
||||
{
|
||||
return _cppCompilerFlags;
|
||||
}
|
||||
set
|
||||
{
|
||||
_cppCompilerFlags = value;
|
||||
}
|
||||
}
|
||||
|
||||
private NativeCompileSettings()
|
||||
{
|
||||
_linkLibPaths = new List<string>();
|
||||
_referencePaths = new List<string>();
|
||||
|
||||
IlcPath = AppContext.BaseDirectory;
|
||||
|
||||
// By default, ILC SDK Path is assumed to be the same folder as ILC path
|
||||
IlcSdkPath = IlcPath;
|
||||
Architecture = DefaultArchitectureMode;
|
||||
BuildType = DefaultBuiltType;
|
||||
NativeMode = DefaultNativeModel;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
"emitEntryPoint": true
|
||||
},
|
||||
"dependencies": {
|
||||
"Microsoft.DotNet.AppDep":"1.0.2-*"
|
||||
"Microsoft.DotNet.AppDep":"1.0.3-*"
|
||||
},
|
||||
"frameworks": {
|
||||
"dnxcore50": { }
|
||||
|
|
|
@ -15,8 +15,8 @@
|
|||
"type": "build",
|
||||
"version": "1.0.0-*"
|
||||
},
|
||||
"Microsoft.DotNet.ILCompiler": "1.0.3-*",
|
||||
"Microsoft.DotNet.ILCompiler.SDK": "1.0.3-*",
|
||||
"Microsoft.DotNet.ILCompiler": "1.0.4-*",
|
||||
"Microsoft.DotNet.ILCompiler.SDK": "1.0.4-*",
|
||||
"Microsoft.DotNet.Compiler.Common": "1.0.0-*"
|
||||
},
|
||||
"frameworks": {
|
||||
|
|
|
@ -16,6 +16,7 @@ using Microsoft.DotNet.ProjectModel;
|
|||
using Microsoft.DotNet.ProjectModel.Compilation;
|
||||
using Microsoft.DotNet.ProjectModel.Utilities;
|
||||
using NuGet.Frameworks;
|
||||
using Microsoft.Extensions.DependencyModel;
|
||||
|
||||
namespace Microsoft.DotNet.Tools.Compiler
|
||||
{
|
||||
|
@ -44,8 +45,10 @@ namespace Microsoft.DotNet.Tools.Compiler
|
|||
var arch = app.Option("-a|--arch <ARCH>", "The architecture for which to compile. x64 only currently supported.", CommandOptionType.SingleValue);
|
||||
var ilcArgs = app.Option("--ilcargs <ARGS>", "Command line arguments to be passed directly to ILCompiler.", CommandOptionType.SingleValue);
|
||||
var ilcPath = app.Option("--ilcpath <PATH>", "Path to the folder containing custom built ILCompiler.", CommandOptionType.SingleValue);
|
||||
var ilcSdkPath = app.Option("--ilcsdkpath <PATH>", "Path to the folder containing ILCompiler application dependencies.", CommandOptionType.SingleValue);
|
||||
var ilcSdkPath = app.Option("--ilcsdkpath <PATH>", "Path to the folder containing custom built ILCompiler SDK.", CommandOptionType.SingleValue);
|
||||
var appDepSdkPath = app.Option("--appdepsdkpath <PATH>", "Path to the folder containing ILCompiler application dependencies.", CommandOptionType.SingleValue);
|
||||
var cppMode = app.Option("--cpp", "Flag to do native compilation with C++ code generator.", CommandOptionType.NoValue);
|
||||
var cppCompilerFlags = app.Option("--cppcompilerflags <flags>", "Additional flags to be passed to the native compiler.", CommandOptionType.SingleValue);
|
||||
|
||||
app.OnExecute(() =>
|
||||
{
|
||||
|
@ -63,9 +66,11 @@ namespace Microsoft.DotNet.Tools.Compiler
|
|||
var ilcArgsValue = ilcArgs.Value();
|
||||
var ilcPathValue = ilcPath.Value();
|
||||
var ilcSdkPathValue = ilcSdkPath.Value();
|
||||
var appDepSdkPathValue = appDepSdkPath.Value();
|
||||
var configValue = configuration.Value() ?? Constants.DefaultConfiguration;
|
||||
var outputValue = output.Value();
|
||||
var intermediateValue = intermediateOutput.Value();
|
||||
var cppCompilerFlagsValue = cppCompilerFlags.Value();
|
||||
|
||||
// Load project contexts for each framework and compile them
|
||||
bool success = true;
|
||||
|
@ -77,7 +82,7 @@ namespace Microsoft.DotNet.Tools.Compiler
|
|||
success &= Compile(context, configValue, outputValue, intermediateValue, buildProjectReferences, noHost.HasValue());
|
||||
if (isNative && success)
|
||||
{
|
||||
success &= CompileNative(context, configValue, outputValue, buildProjectReferences, intermediateValue, archValue, ilcArgsValue, ilcPathValue, ilcSdkPathValue, isCppMode);
|
||||
success &= CompileNative(context, configValue, outputValue, buildProjectReferences, intermediateValue, archValue, ilcArgsValue, ilcPathValue, ilcSdkPathValue, appDepSdkPathValue, isCppMode, cppCompilerFlagsValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -109,7 +114,9 @@ namespace Microsoft.DotNet.Tools.Compiler
|
|||
string ilcArgsValue,
|
||||
string ilcPathValue,
|
||||
string ilcSdkPathValue,
|
||||
bool isCppMode)
|
||||
string appDepSdkPathValue,
|
||||
bool isCppMode,
|
||||
string cppCompilerFlagsValue)
|
||||
{
|
||||
var outputPath = GetOutputPath(context, configuration, outputOptionValue);
|
||||
var nativeOutputPath = Path.Combine(GetOutputPath(context, configuration, outputOptionValue), "native");
|
||||
|
@ -145,10 +152,17 @@ namespace Microsoft.DotNet.Tools.Compiler
|
|||
// ILC SDK Path
|
||||
if (!string.IsNullOrWhiteSpace(ilcSdkPathValue))
|
||||
{
|
||||
nativeArgs.Add("--appdepsdk");
|
||||
nativeArgs.Add("--ilcsdkpath");
|
||||
nativeArgs.Add(ilcSdkPathValue);
|
||||
}
|
||||
|
||||
// AppDep SDK Path
|
||||
if (!string.IsNullOrWhiteSpace(appDepSdkPathValue))
|
||||
{
|
||||
nativeArgs.Add("--appdepsdk");
|
||||
nativeArgs.Add(appDepSdkPathValue);
|
||||
}
|
||||
|
||||
// CodeGen Mode
|
||||
if(isCppMode)
|
||||
{
|
||||
|
@ -156,6 +170,12 @@ namespace Microsoft.DotNet.Tools.Compiler
|
|||
nativeArgs.Add("cpp");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(cppCompilerFlagsValue))
|
||||
{
|
||||
nativeArgs.Add("--cppcompilerflags");
|
||||
nativeArgs.Add(cppCompilerFlagsValue);
|
||||
}
|
||||
|
||||
// Configuration
|
||||
if (configuration != null)
|
||||
{
|
||||
|
@ -304,6 +324,8 @@ namespace Microsoft.DotNet.Tools.Compiler
|
|||
compilationOptions.KeyFile = Path.GetFullPath(Path.Combine(context.ProjectFile.ProjectDirectory, compilationOptions.KeyFile));
|
||||
}
|
||||
|
||||
var references = new List<string>();
|
||||
|
||||
// Add compilation options to the args
|
||||
compilerArgs.AddRange(compilationOptions.SerializeToArgs());
|
||||
|
||||
|
@ -319,16 +341,49 @@ namespace Microsoft.DotNet.Tools.Compiler
|
|||
if (projectDependency.Project.Files.SourceFiles.Any())
|
||||
{
|
||||
var projectOutputPath = GetProjectOutput(projectDependency.Project, projectDependency.Framework, configuration, outputPath);
|
||||
compilerArgs.Add($"--reference:{projectOutputPath}");
|
||||
references.Add(projectOutputPath);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
compilerArgs.AddRange(dependency.CompilationAssemblies.Select(r => $"--reference:{r.ResolvedPath}"));
|
||||
references.AddRange(dependency.CompilationAssemblies.Select(r => r.ResolvedPath));
|
||||
}
|
||||
|
||||
compilerArgs.AddRange(dependency.SourceReferences);
|
||||
}
|
||||
|
||||
compilerArgs.AddRange(references.Select(r => $"--reference:{r}"));
|
||||
|
||||
var runtimeContext = ProjectContext.Create(context.ProjectDirectory, context.TargetFramework, new[] { RuntimeIdentifier.Current });
|
||||
var libraryExporter = runtimeContext.CreateExporter(configuration);
|
||||
|
||||
if (compilationOptions.PreserveCompilationContext == true)
|
||||
{
|
||||
var dependencyContext = DependencyContextBuilder.FromLibraryExporter(
|
||||
libraryExporter, context.TargetFramework.DotNetFrameworkName, context.RuntimeIdentifier);
|
||||
|
||||
var writer = new DependencyContextWriter();
|
||||
var depsJsonFile = Path.Combine(intermediateOutputPath, context.ProjectFile.Name + "dotnet-compile.deps.json");
|
||||
using (var fileStream = File.Create(depsJsonFile))
|
||||
{
|
||||
writer.Write(dependencyContext, fileStream);
|
||||
}
|
||||
|
||||
compilerArgs.Add($"--resource:\"{depsJsonFile}\",{context.ProjectFile.Name}.deps.json");
|
||||
|
||||
var refsFolder = Path.Combine(outputPath, "refs");
|
||||
if (Directory.Exists(refsFolder))
|
||||
{
|
||||
Directory.Delete(refsFolder, true);
|
||||
}
|
||||
|
||||
Directory.CreateDirectory(refsFolder);
|
||||
foreach (var reference in references)
|
||||
{
|
||||
File.Copy(reference, Path.Combine(refsFolder, Path.GetFileName(reference)));
|
||||
}
|
||||
}
|
||||
|
||||
if (!AddResources(context.ProjectFile, compilerArgs, intermediateOutputPath))
|
||||
{
|
||||
return false;
|
||||
|
@ -394,10 +449,9 @@ namespace Microsoft.DotNet.Tools.Compiler
|
|||
|
||||
if (success && !noHost && compilationOptions.EmitEntryPoint.GetValueOrDefault())
|
||||
{
|
||||
var runtimeContext = ProjectContext.Create(context.ProjectDirectory, context.TargetFramework, new[] { RuntimeIdentifier.Current });
|
||||
MakeRunnable(runtimeContext,
|
||||
outputPath,
|
||||
runtimeContext.CreateExporter(configuration));
|
||||
libraryExporter);
|
||||
}
|
||||
|
||||
return PrintSummary(diagnostics, sw, success);
|
||||
|
|
|
@ -16,6 +16,10 @@
|
|||
"Microsoft.Extensions.CommandLineUtils.Sources": {
|
||||
"type": "build",
|
||||
"version": "1.0.0-*"
|
||||
},
|
||||
"Microsoft.Extensions.DependencyModel": {
|
||||
"type": "build",
|
||||
"version": "1.0.0-*"
|
||||
}
|
||||
},
|
||||
"frameworks": {
|
||||
|
|
|
@ -28,7 +28,9 @@ namespace Microsoft.DotNet.Tools.New
|
|||
{
|
||||
var thisAssembly = typeof(Program).GetTypeInfo().Assembly;
|
||||
var resources = from resourceName in thisAssembly.GetManifestResourceNames()
|
||||
where resourceName.ToLowerInvariant().EndsWith(".cs") || resourceName.ToLowerInvariant().EndsWith(".json")
|
||||
where resourceName.ToLowerInvariant().EndsWith(".cs")
|
||||
|| resourceName.ToLowerInvariant().EndsWith(".json")
|
||||
|| resourceName.ToLowerInvariant().EndsWith(".config")
|
||||
select resourceName;
|
||||
|
||||
var resourceNameToFileName = new Dictionary<string, string>();
|
||||
|
|
9
src/Microsoft.DotNet.Tools.New/Template/NuGet.Config
Normal file
9
src/Microsoft.DotNet.Tools.New/Template/NuGet.Config
Normal file
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<packageSources>
|
||||
<!--To inherit the global NuGet package sources remove the <clear/> line below -->
|
||||
<clear />
|
||||
<add key="dotnet-core" value="https://www.myget.org/F/dotnet-core/api/v3/index.json" />
|
||||
<add key="api.nuget.org" value="https://api.nuget.org/v3/index.json" />
|
||||
</packageSources>
|
||||
</configuration>
|
|
@ -13,20 +13,46 @@ namespace Microsoft.DotNet.Tools.Run
|
|||
{
|
||||
DebugHelper.HandleDebugSwitch(ref args);
|
||||
|
||||
var help = false;
|
||||
string helpText = null;
|
||||
var returnCode = 0;
|
||||
|
||||
RunCommand runCmd = new RunCommand();
|
||||
|
||||
ArgumentSyntax.Parse(args, syntax =>
|
||||
try
|
||||
{
|
||||
syntax.HandleErrors = false;
|
||||
syntax.DefineOption("f|framework", ref runCmd.Framework, "Compile a specific framework");
|
||||
syntax.DefineOption("c|configuration", ref runCmd.Configuration, "Configuration under which to build");
|
||||
syntax.DefineOption("t|preserve-temporary", ref runCmd.PreserveTemporary, "Keep the output's temporary directory around");
|
||||
syntax.DefineOption("p|project", ref runCmd.Project, "The path to the project to run (defaults to the current directory). Can be a path to a project.json or a project directory");
|
||||
ArgumentSyntax.Parse(args, syntax =>
|
||||
{
|
||||
syntax.HandleHelp = false;
|
||||
syntax.HandleErrors = false;
|
||||
|
||||
// TODO: this is not supporting args which can be switches (i.e. --test)
|
||||
// TODO: we need to make a change in System.CommandLine or parse args ourselves.
|
||||
syntax.DefineParameterList("args", ref runCmd.Args, "Arguments to pass to the executable or script");
|
||||
});
|
||||
syntax.DefineOption("f|framework", ref runCmd.Framework, "Compile a specific framework");
|
||||
syntax.DefineOption("c|configuration", ref runCmd.Configuration, "Configuration under which to build");
|
||||
syntax.DefineOption("t|preserve-temporary", ref runCmd.PreserveTemporary, "Keep the output's temporary directory around");
|
||||
syntax.DefineOption("p|project", ref runCmd.Project, "The path to the project to run (defaults to the current directory). Can be a path to a project.json or a project directory");
|
||||
|
||||
syntax.DefineOption("h|help", ref help, "Help for compile native.");
|
||||
|
||||
// TODO: this is not supporting args which can be switches (i.e. --test)
|
||||
// TODO: we need to make a change in System.CommandLine or parse args ourselves.
|
||||
syntax.DefineParameterList("args", ref runCmd.Args, "Arguments to pass to the executable or script");
|
||||
|
||||
helpText = syntax.GetHelpText();
|
||||
});
|
||||
}
|
||||
catch (ArgumentSyntaxException exception)
|
||||
{
|
||||
Console.Error.WriteLine(exception.Message);
|
||||
help = true;
|
||||
returnCode = 1;
|
||||
}
|
||||
|
||||
if (help)
|
||||
{
|
||||
Console.WriteLine(helpText);
|
||||
|
||||
return returnCode;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
|
|
17
src/Microsoft.Extensions.DependencyModel/Dependency.cs
Normal file
17
src/Microsoft.Extensions.DependencyModel/Dependency.cs
Normal file
|
@ -0,0 +1,17 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
namespace Microsoft.Extensions.DependencyModel
|
||||
{
|
||||
public struct Dependency
|
||||
{
|
||||
public Dependency(string name, string version)
|
||||
{
|
||||
Name = name;
|
||||
Version = version;
|
||||
}
|
||||
|
||||
public string Name { get; }
|
||||
public string Version { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.Extensions.DependencyModel
|
||||
{
|
||||
public class DependencyContext
|
||||
{
|
||||
private const string DepsResourceSufix = ".deps.json";
|
||||
|
||||
public DependencyContext(string target, string runtime, Library[] compileLibraries, Library[] runtimeLibraries)
|
||||
{
|
||||
Target = target;
|
||||
Runtime = runtime;
|
||||
CompileLibraries = compileLibraries;
|
||||
RuntimeLibraries = runtimeLibraries;
|
||||
}
|
||||
|
||||
public string Target { get; set; }
|
||||
|
||||
public string Runtime { get; set; }
|
||||
|
||||
public IReadOnlyList<Library> CompileLibraries { get; }
|
||||
|
||||
public IReadOnlyList<Library> RuntimeLibraries { get; }
|
||||
|
||||
public static DependencyContext Load()
|
||||
{
|
||||
var entryAssembly = (Assembly)typeof(Assembly).GetTypeInfo().GetDeclaredMethod("GetEntryAssembly").Invoke(null, null);
|
||||
var stream = entryAssembly.GetManifestResourceStream(entryAssembly.GetName().Name + DepsResourceSufix);
|
||||
|
||||
if (stream == null)
|
||||
{
|
||||
throw new InvalidOperationException("Entry assembly was compiled without `preserveCompilationContext` enabled");
|
||||
}
|
||||
|
||||
using (stream)
|
||||
{
|
||||
return Load(stream);
|
||||
}
|
||||
}
|
||||
|
||||
public static DependencyContext Load(Stream stream)
|
||||
{
|
||||
return new DependencyContextReader().Read(stream);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Microsoft.Extensions.DependencyModel
|
||||
{
|
||||
public class DependencyContextReader
|
||||
{
|
||||
public DependencyContext Read(Stream stream)
|
||||
{
|
||||
using (var streamReader = new StreamReader(stream))
|
||||
{
|
||||
using (var reader = new JsonTextReader(streamReader))
|
||||
{
|
||||
var root = JObject.Load(reader);
|
||||
return Read(root);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsRuntimeTarget(string name) => name.Contains(DependencyContextStrings.VersionSeperator);
|
||||
|
||||
private DependencyContext Read(JObject root)
|
||||
{
|
||||
var libraryStubs = ReadLibraryStubs((JObject) root[DependencyContextStrings.LibrariesPropertyName]);
|
||||
var targetsObject = (IEnumerable<KeyValuePair<string, JToken>>) root[DependencyContextStrings.TargetsPropertyName];
|
||||
|
||||
var runtimeTargetProperty = targetsObject.First(target => IsRuntimeTarget(target.Key));
|
||||
var compileTargetProperty = targetsObject.First(target => !IsRuntimeTarget(target.Key));
|
||||
|
||||
return new DependencyContext(
|
||||
compileTargetProperty.Key,
|
||||
runtimeTargetProperty.Key.Substring(compileTargetProperty.Key.Length + 1),
|
||||
ReadLibraries((JObject)runtimeTargetProperty.Value, true, libraryStubs),
|
||||
ReadLibraries((JObject)compileTargetProperty.Value, false, libraryStubs)
|
||||
);
|
||||
}
|
||||
|
||||
private Library[] ReadLibraries(JObject librariesObject, bool runtime, Dictionary<string, DependencyContextReader.LibraryStub> libraryStubs)
|
||||
{
|
||||
return librariesObject.Properties().Select(property => ReadLibrary(property, runtime, libraryStubs)).ToArray();
|
||||
}
|
||||
|
||||
private Library ReadLibrary(JProperty property, bool runtime, Dictionary<string, DependencyContextReader.LibraryStub> libraryStubs)
|
||||
{
|
||||
var nameWithVersion = property.Name;
|
||||
DependencyContextReader.LibraryStub stub;
|
||||
|
||||
if (!libraryStubs.TryGetValue(nameWithVersion, out stub))
|
||||
{
|
||||
throw new InvalidOperationException($"Cannot find library information for {nameWithVersion}");
|
||||
}
|
||||
|
||||
var seperatorPosition = nameWithVersion.IndexOf(DependencyContextStrings.VersionSeperator);
|
||||
|
||||
var name = nameWithVersion.Substring(0, seperatorPosition);
|
||||
var version = nameWithVersion.Substring(seperatorPosition + 1);
|
||||
|
||||
var libraryObject = (JObject) property.Value;
|
||||
|
||||
var dependencies = ReadDependencies(libraryObject);
|
||||
var assemblies = ReadAssemblies(libraryObject, runtime);
|
||||
|
||||
return new Library(stub.Type, name, version, stub.Hash, assemblies, dependencies, stub.Serviceable);
|
||||
}
|
||||
|
||||
private static string[] ReadAssemblies(JObject libraryObject, bool runtime)
|
||||
{
|
||||
var assembliesObject = (JObject) libraryObject[runtime ? DependencyContextStrings.RunTimeAssembliesKey : DependencyContextStrings.CompileTimeAssembliesKey];
|
||||
|
||||
if (assembliesObject == null)
|
||||
{
|
||||
return new string[] {};
|
||||
}
|
||||
|
||||
return assembliesObject.Properties().Select(property => property.Name).ToArray();
|
||||
}
|
||||
|
||||
private static Dependency[] ReadDependencies(JObject libraryObject)
|
||||
{
|
||||
var dependenciesObject = ((JObject) libraryObject[DependencyContextStrings.DependenciesPropertyName]);
|
||||
|
||||
if (dependenciesObject == null)
|
||||
{
|
||||
return new Dependency[]{ };
|
||||
}
|
||||
|
||||
return dependenciesObject.Properties()
|
||||
.Select(property => new Dependency(property.Name, (string) property.Value)).ToArray();
|
||||
}
|
||||
|
||||
private Dictionary<string, LibraryStub> ReadLibraryStubs(JObject librariesObject)
|
||||
{
|
||||
var libraries = new Dictionary<string, LibraryStub>();
|
||||
foreach (var libraryProperty in librariesObject)
|
||||
{
|
||||
var value = (JObject) libraryProperty.Value;
|
||||
var stub = new LibraryStub
|
||||
{
|
||||
Name = libraryProperty.Key,
|
||||
Hash = value[DependencyContextStrings.Sha512PropertyName]?.Value<string>(),
|
||||
Type = value[DependencyContextStrings.TypePropertyName].Value<string>(),
|
||||
Serviceable = value[DependencyContextStrings.ServiceablePropertyName]?.Value<bool>() == true
|
||||
};
|
||||
libraries.Add(stub.Name, stub);
|
||||
}
|
||||
return libraries;
|
||||
}
|
||||
|
||||
private struct LibraryStub
|
||||
{
|
||||
public string Name;
|
||||
|
||||
public string Hash;
|
||||
|
||||
public string Type;
|
||||
|
||||
public bool Serviceable;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
namespace Microsoft.Extensions.DependencyModel
|
||||
{
|
||||
internal class DependencyContextStrings
|
||||
{
|
||||
internal const char VersionSeperator = '/';
|
||||
|
||||
internal const string CompileTimeAssembliesKey = "compile";
|
||||
|
||||
internal const string RunTimeAssembliesKey = "runtime";
|
||||
|
||||
internal const string LibrariesPropertyName = "libraries";
|
||||
|
||||
internal const string TargetsPropertyName = "targets";
|
||||
|
||||
internal const string DependenciesPropertyName = "dependencies";
|
||||
|
||||
internal const string Sha512PropertyName = "sha512";
|
||||
|
||||
internal const string TypePropertyName = "type";
|
||||
|
||||
internal const string ServiceablePropertyName = "serviceable";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Microsoft.Extensions.DependencyModel
|
||||
{
|
||||
public class DependencyContextWriter
|
||||
{
|
||||
public void Write(DependencyContext context, Stream stream)
|
||||
{
|
||||
using (var writer = new StreamWriter(stream))
|
||||
{
|
||||
using (var jsonWriter = new JsonTextWriter(writer))
|
||||
{
|
||||
Write(context).WriteTo(jsonWriter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private JObject Write(DependencyContext context)
|
||||
{
|
||||
return new JObject(
|
||||
new JProperty(DependencyContextStrings.TargetsPropertyName, WriteTargets(context)),
|
||||
new JProperty(DependencyContextStrings.LibrariesPropertyName, WriteLibraries(context))
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
private JObject WriteTargets(DependencyContext context)
|
||||
{
|
||||
return new JObject(
|
||||
new JProperty(context.Target, WriteTarget(context.CompileLibraries, false)),
|
||||
new JProperty(context.Target + DependencyContextStrings.VersionSeperator + context.Runtime,
|
||||
WriteTarget(context.RuntimeLibraries, true))
|
||||
);
|
||||
}
|
||||
|
||||
private JObject WriteTarget(IReadOnlyList<Library> libraries, bool runtime)
|
||||
{
|
||||
return new JObject(
|
||||
libraries.Select(library =>
|
||||
new JProperty(library.PackageName + DependencyContextStrings.VersionSeperator + library.Version, WriteTargetLibrary(library, runtime))));
|
||||
}
|
||||
|
||||
private JObject WriteTargetLibrary(Library library, bool runtime)
|
||||
{
|
||||
return new JObject(
|
||||
new JProperty(DependencyContextStrings.DependenciesPropertyName, WriteDependencies(library.Dependencies)),
|
||||
new JProperty(runtime ? DependencyContextStrings.RunTimeAssembliesKey : DependencyContextStrings.CompileTimeAssembliesKey,
|
||||
WriteAssemblies(library.Assemblies))
|
||||
);
|
||||
}
|
||||
|
||||
private JObject WriteAssemblies(IReadOnlyList<string> assemblies)
|
||||
{
|
||||
return new JObject(assemblies.Select(assembly => new JProperty(assembly, new JObject())));
|
||||
}
|
||||
|
||||
private JObject WriteDependencies(IReadOnlyList<Dependency> dependencies)
|
||||
{
|
||||
return new JObject(
|
||||
dependencies.Select(dependency => new JProperty(dependency.Name, dependency.Version))
|
||||
);
|
||||
}
|
||||
|
||||
private JObject WriteLibraries(DependencyContext context)
|
||||
{
|
||||
var allLibraries =
|
||||
context.RuntimeLibraries.Concat(context.CompileLibraries)
|
||||
.GroupBy(library => library.PackageName + DependencyContextStrings.VersionSeperator + library.Version);
|
||||
|
||||
return new JObject(allLibraries.Select(libraries=> new JProperty(libraries.Key, WriteLibrary(libraries.First()))));
|
||||
}
|
||||
|
||||
private JObject WriteLibrary(Library library)
|
||||
{
|
||||
return new JObject(
|
||||
new JProperty(DependencyContextStrings.TypePropertyName, library.LibraryType),
|
||||
new JProperty(DependencyContextStrings.ServiceablePropertyName, library.Serviceable),
|
||||
new JProperty(DependencyContextStrings.Sha512PropertyName, library.Hash)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
36
src/Microsoft.Extensions.DependencyModel/Library.cs
Normal file
36
src/Microsoft.Extensions.DependencyModel/Library.cs
Normal file
|
@ -0,0 +1,36 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.Extensions.DependencyModel
|
||||
{
|
||||
public struct Library
|
||||
{
|
||||
public Library(string libraryType, string packageName, string version, string hash, string[] assemblies, Dependency[] dependencies, bool serviceable)
|
||||
{
|
||||
LibraryType = libraryType;
|
||||
PackageName = packageName;
|
||||
Version = version;
|
||||
Hash = hash;
|
||||
Assemblies = assemblies;
|
||||
Dependencies = dependencies;
|
||||
Serviceable = serviceable;
|
||||
}
|
||||
|
||||
public string LibraryType { get; }
|
||||
|
||||
public string PackageName { get; }
|
||||
|
||||
public string Version { get; }
|
||||
|
||||
public string Hash { get; }
|
||||
|
||||
public IReadOnlyList<string> Assemblies { get; }
|
||||
|
||||
public IReadOnlyList<Dependency> Dependencies { get; }
|
||||
|
||||
public bool Serviceable { get; }
|
||||
}
|
||||
}
|
|
@ -6,12 +6,11 @@
|
|||
</PropertyGroup>
|
||||
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>482b1045-a1fa-4063-a0d9-a8107a91a016</ProjectGuid>
|
||||
<ProjectGuid>688870c8-9843-4f9e-8576-d39290ad0f25</ProjectGuid>
|
||||
<RootNamespace>Microsoft.Extensions.DependencyModel</RootNamespace>
|
||||
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
|
||||
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
</PropertyGroup>
|
||||
|
|
30
src/Microsoft.Extensions.DependencyModel/project.json
Normal file
30
src/Microsoft.Extensions.DependencyModel/project.json
Normal file
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
"description": "Abstractions for reading `.deps` files.",
|
||||
"version": "1.0.0-*",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/dotnet/cli"
|
||||
},
|
||||
"compilationOptions": {
|
||||
"warningsAsErrors": true,
|
||||
"keyFile": "../../tools/Key.snk"
|
||||
},
|
||||
"dependencies": {
|
||||
"Newtonsoft.Json": "7.0.1"
|
||||
},
|
||||
"frameworks": {
|
||||
"dnx451": {},
|
||||
"dnxcore50": {
|
||||
"dependencies": {
|
||||
"System.Runtime": "4.0.21-rc2-23618",
|
||||
"System.Dynamic.Runtime": "4.0.11-rc2-23616"
|
||||
}
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"postcompile": [
|
||||
"../../scripts/build/place-binary \"%compile:OutputDir%/%project:Name%.dll\"",
|
||||
"../../scripts/build/place-binary \"%compile:OutputDir%/%project:Name%.pdb\""
|
||||
]
|
||||
}
|
||||
}
|
|
@ -6,7 +6,8 @@
|
|||
"url": "git://github.com/dotnet/cli"
|
||||
},
|
||||
"compilationOptions": {
|
||||
"warningsAsErrors": true
|
||||
"warningsAsErrors": true,
|
||||
"keyFile": "../../tools/Key.snk"
|
||||
},
|
||||
"dependencies": {
|
||||
"Newtonsoft.Json": "7.0.1",
|
||||
|
|
|
@ -10,14 +10,16 @@ include_directories(inc)
|
|||
set(SOURCES
|
||||
src/args.cpp
|
||||
src/main.cpp
|
||||
src/tpafile.cpp
|
||||
src/deps_resolver.cpp
|
||||
src/trace.cpp
|
||||
src/utils.cpp
|
||||
src/coreclr.cpp
|
||||
src/servicing_index.cpp
|
||||
|
||||
inc/args.h
|
||||
inc/pal.h
|
||||
inc/tpafile.h
|
||||
inc/servicing_index.h
|
||||
inc/deps_resolver.h
|
||||
inc/trace.h
|
||||
inc/coreclr.h
|
||||
inc/utils.h)
|
||||
|
|
|
@ -4,14 +4,20 @@
|
|||
#ifndef ARGS_H
|
||||
#define ARGS_H
|
||||
|
||||
#include "utils.h"
|
||||
#include "pal.h"
|
||||
#include "trace.h"
|
||||
|
||||
struct arguments_t
|
||||
{
|
||||
pal::string_t own_path;
|
||||
pal::string_t app_dir;
|
||||
pal::string_t dotnet_servicing;
|
||||
pal::string_t dotnet_runtime_servicing;
|
||||
pal::string_t dotnet_home;
|
||||
pal::string_t dotnet_packages;
|
||||
pal::string_t dotnet_packages_cache;
|
||||
pal::string_t managed_application;
|
||||
pal::string_t clr_path;
|
||||
|
||||
int app_argc;
|
||||
const pal::char_t** app_argv;
|
||||
|
|
102
src/corehost/inc/deps_resolver.h
Normal file
102
src/corehost/inc/deps_resolver.h
Normal file
|
@ -0,0 +1,102 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#ifndef DEPS_RESOLVER_H
|
||||
#define DEPS_RESOLVER_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "pal.h"
|
||||
#include "trace.h"
|
||||
|
||||
#include "servicing_index.h"
|
||||
|
||||
struct deps_entry_t
|
||||
{
|
||||
pal::string_t library_type;
|
||||
pal::string_t library_name;
|
||||
pal::string_t library_version;
|
||||
pal::string_t library_hash;
|
||||
pal::string_t asset_type;
|
||||
pal::string_t asset_name;
|
||||
pal::string_t relative_path;
|
||||
bool is_serviceable;
|
||||
|
||||
// Given a "base" dir, yield the relative path in the package layout.
|
||||
bool to_full_path(const pal::string_t& root, pal::string_t* str) const;
|
||||
|
||||
// Given a "base" dir, yield the relative path in the package layout only if
|
||||
// the hash matches contents of the hash file.
|
||||
bool to_hash_matched_path(const pal::string_t& root, pal::string_t* str) const;
|
||||
};
|
||||
|
||||
// Probe paths to be resolved for ordering
|
||||
struct probe_paths_t
|
||||
{
|
||||
pal::string_t tpa;
|
||||
pal::string_t native;
|
||||
pal::string_t culture;
|
||||
};
|
||||
|
||||
class deps_resolver_t
|
||||
{
|
||||
public:
|
||||
deps_resolver_t(const arguments_t& args)
|
||||
: m_svc(args.dotnet_servicing)
|
||||
{
|
||||
m_deps_valid = parse_deps_file(args);
|
||||
}
|
||||
|
||||
bool valid() { return m_deps_valid; }
|
||||
|
||||
bool resolve_probe_paths(
|
||||
const pal::string_t& app_dir,
|
||||
const pal::string_t& package_dir,
|
||||
const pal::string_t& package_cache_dir,
|
||||
const pal::string_t& clr_dir,
|
||||
probe_paths_t* probe_paths);
|
||||
|
||||
private:
|
||||
|
||||
bool load();
|
||||
|
||||
bool parse_deps_file(const arguments_t& args);
|
||||
|
||||
// Resolve order for TPA lookup.
|
||||
void resolve_tpa_list(
|
||||
const pal::string_t& app_dir,
|
||||
const pal::string_t& package_dir,
|
||||
const pal::string_t& package_cache_dir,
|
||||
const pal::string_t& clr_dir,
|
||||
pal::string_t* output);
|
||||
|
||||
// Resolve order for culture and native DLL lookup.
|
||||
void resolve_probe_dirs(
|
||||
const pal::string_t& asset_type,
|
||||
const pal::string_t& app_dir,
|
||||
const pal::string_t& package_dir,
|
||||
const pal::string_t& package_cache_dir,
|
||||
const pal::string_t& clr_dir,
|
||||
pal::string_t* output);
|
||||
|
||||
// Populate local assemblies from app_dir listing.
|
||||
void get_local_assemblies(const pal::string_t& dir);
|
||||
|
||||
// Servicing index to resolve serviced assembly paths.
|
||||
servicing_index_t m_svc;
|
||||
|
||||
// Map of simple name -> full path of local assemblies populated in priority
|
||||
// order of their extensions.
|
||||
std::unordered_map<pal::string_t, pal::string_t> m_local_assemblies;
|
||||
|
||||
// Entries in the dep file
|
||||
std::vector<deps_entry_t> m_deps_entries;
|
||||
|
||||
// The dep file path
|
||||
pal::string_t m_deps_path;
|
||||
|
||||
// Is the deps file valid
|
||||
bool m_deps_valid;
|
||||
};
|
||||
|
||||
#endif // DEPS_RESOLVER_H
|
|
@ -12,6 +12,9 @@
|
|||
#include <cstring>
|
||||
#include <cstdarg>
|
||||
#include <tuple>
|
||||
#include <unordered_map>
|
||||
#include <memory>
|
||||
#include <algorithm>
|
||||
|
||||
#if defined(_WIN32)
|
||||
|
||||
|
@ -65,7 +68,13 @@ namespace pal
|
|||
typedef wchar_t char_t;
|
||||
typedef std::wstring string_t;
|
||||
typedef std::wstringstream stringstream_t;
|
||||
typedef std::ifstream ifstream_t;
|
||||
// TODO: Agree on the correct encoding of the files: The PoR for now is to
|
||||
// temporarily wchar for Windows and char for Unix. Current implementation
|
||||
// implicitly expects the contents on both Windows and Unix as char and
|
||||
// converts them to wchar in code for Windows. This line should become:
|
||||
// typedef std::basic_ifstream<pal::char_t> ifstream_t.
|
||||
typedef std::basic_ifstream<char> ifstream_t;
|
||||
typedef std::istreambuf_iterator<ifstream_t::char_type> istreambuf_iterator_t;
|
||||
typedef HRESULT hresult_t;
|
||||
typedef HMODULE dll_t;
|
||||
typedef FARPROC proc_t;
|
||||
|
@ -77,11 +86,14 @@ namespace pal
|
|||
|
||||
pal::string_t to_palstring(const std::string& str);
|
||||
std::string to_stdstring(const pal::string_t& str);
|
||||
void to_palstring(const char* str, pal::string_t* out);
|
||||
void to_stdstring(const pal::char_t* str, std::string* out);
|
||||
#else
|
||||
typedef char char_t;
|
||||
typedef std::string string_t;
|
||||
typedef std::stringstream stringstream_t;
|
||||
typedef std::ifstream ifstream_t;
|
||||
typedef std::basic_ifstream<char> ifstream_t;
|
||||
typedef std::istreambuf_iterator<ifstream_t::char_type> istreambuf_iterator_t;
|
||||
typedef int hresult_t;
|
||||
typedef void* dll_t;
|
||||
typedef void* proc_t;
|
||||
|
@ -92,24 +104,26 @@ namespace pal
|
|||
inline void err_vprintf(const char_t* format, va_list vl) { ::vfprintf(stderr, format, vl); ::fputc('\n', stderr); }
|
||||
inline pal::string_t to_palstring(const std::string& str) { return str; }
|
||||
inline std::string to_stdstring(const pal::string_t& str) { return str; }
|
||||
inline void to_palstring(const char* str, pal::string_t* out) { out->assign(str); }
|
||||
inline void to_stdstring(const pal::char_t* str, std::string* out) { out->assign(str); }
|
||||
#endif
|
||||
|
||||
bool realpath(string_t& path);
|
||||
bool realpath(string_t* path);
|
||||
bool file_exists(const string_t& path);
|
||||
std::vector<pal::string_t> readdir(const string_t& path);
|
||||
inline bool directory_exists(const string_t& path) { return file_exists(path); }
|
||||
void readdir(const string_t& path, std::vector<pal::string_t>* list);
|
||||
|
||||
bool get_own_executable_path(string_t& recv);
|
||||
bool getenv(const char_t* name, string_t& recv);
|
||||
bool get_default_packages_directory(string_t& recv);
|
||||
bool get_own_executable_path(string_t* recv);
|
||||
bool getenv(const char_t* name, string_t* recv);
|
||||
bool get_default_packages_directory(string_t* recv);
|
||||
bool is_path_rooted(const string_t& path);
|
||||
|
||||
int xtoi(const char_t* input);
|
||||
|
||||
bool load_library(const char_t* path, dll_t& dll);
|
||||
bool load_library(const char_t* path, dll_t* dll);
|
||||
proc_t get_symbol(dll_t library, const char* name);
|
||||
void unload_library(dll_t library);
|
||||
|
||||
bool find_coreclr(pal::string_t& recv);
|
||||
bool find_coreclr(pal::string_t* recv);
|
||||
}
|
||||
|
||||
#endif // PAL_H
|
||||
|
|
24
src/corehost/inc/servicing_index.h
Normal file
24
src/corehost/inc/servicing_index.h
Normal file
|
@ -0,0 +1,24 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#include "utils.h"
|
||||
#include "args.h"
|
||||
|
||||
class servicing_index_t
|
||||
{
|
||||
public:
|
||||
servicing_index_t(const pal::string_t& svc_dir);
|
||||
|
||||
bool find_redirection(const pal::string_t& package_name,
|
||||
const pal::string_t& package_version,
|
||||
const pal::string_t& package_relative,
|
||||
pal::string_t* redirection);
|
||||
|
||||
private:
|
||||
void ensure_redirections();
|
||||
|
||||
std::unordered_map<pal::string_t, pal::string_t> m_redirections;
|
||||
pal::string_t m_patch_root;
|
||||
pal::string_t m_index_file;
|
||||
bool m_parsed;
|
||||
};
|
|
@ -1,41 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#ifndef TPAFILE_H
|
||||
#define TPAFILE_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "pal.h"
|
||||
#include "trace.h"
|
||||
|
||||
struct tpaentry_t
|
||||
{
|
||||
pal::string_t library_type;
|
||||
pal::string_t library_name;
|
||||
pal::string_t library_version;
|
||||
pal::string_t library_hash;
|
||||
pal::string_t asset_type;
|
||||
pal::string_t asset_name;
|
||||
pal::string_t relative_path;
|
||||
};
|
||||
|
||||
class tpafile
|
||||
{
|
||||
public:
|
||||
bool load(pal::string_t path);
|
||||
|
||||
void add_from_local_dir(const pal::string_t& dir);
|
||||
void add_package_dir(pal::string_t dir);
|
||||
void add_native_search_path(pal::string_t dir);
|
||||
|
||||
void write_tpa_list(pal::string_t& output);
|
||||
void write_native_paths(pal::string_t& output);
|
||||
|
||||
private:
|
||||
std::vector<tpaentry_t> m_entries;
|
||||
std::vector<pal::string_t> m_native_search_paths;
|
||||
std::vector<pal::string_t> m_package_search_paths;
|
||||
};
|
||||
|
||||
#endif // TPAFILE_H
|
|
@ -10,7 +10,7 @@ bool ends_with(const pal::string_t& value, const pal::string_t& suffix);
|
|||
pal::string_t get_executable(const pal::string_t& filename);
|
||||
pal::string_t get_directory(const pal::string_t& path);
|
||||
pal::string_t get_filename(const pal::string_t& path);
|
||||
void append_path(pal::string_t& path1, const pal::char_t* path2);
|
||||
void append_path(pal::string_t* path1, const pal::char_t* path2);
|
||||
bool coreclr_exists_in_dir(const pal::string_t& candidate);
|
||||
|
||||
void replace_char(pal::string_t* path, pal::char_t match, pal::char_t repl);
|
||||
#endif
|
||||
|
|
|
@ -3,10 +3,12 @@
|
|||
|
||||
#include "args.h"
|
||||
#include "utils.h"
|
||||
#include "coreclr.h"
|
||||
|
||||
arguments_t::arguments_t() :
|
||||
managed_application(_X("")),
|
||||
clr_path(_X("")),
|
||||
own_path(_X("")),
|
||||
app_dir(_X("")),
|
||||
app_argc(0),
|
||||
app_argv(nullptr)
|
||||
{
|
||||
|
@ -24,10 +26,22 @@ void display_help()
|
|||
|
||||
bool parse_arguments(const int argc, const pal::char_t* argv[], arguments_t& args)
|
||||
{
|
||||
// Get the full name of the application
|
||||
if (!pal::get_own_executable_path(args.own_path) || !pal::realpath(args.own_path))
|
||||
// Read trace environment variable
|
||||
pal::string_t trace_str;
|
||||
if (pal::getenv(_X("COREHOST_TRACE"), &trace_str))
|
||||
{
|
||||
trace::error(_X("failed to locate current executable"));
|
||||
auto trace_val = pal::xtoi(trace_str.c_str());
|
||||
if (trace_val > 0)
|
||||
{
|
||||
trace::enable();
|
||||
trace::info(_X("Tracing enabled"));
|
||||
}
|
||||
}
|
||||
|
||||
// Get the full name of the application
|
||||
if (!pal::get_own_executable_path(&args.own_path) || !pal::realpath(&args.own_path))
|
||||
{
|
||||
trace::error(_X("Failed to locate current executable"));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -43,6 +57,12 @@ bool parse_arguments(const int argc, const pal::char_t* argv[], arguments_t& arg
|
|||
return false;
|
||||
}
|
||||
args.managed_application = pal::string_t(argv[1]);
|
||||
if (!pal::realpath(&args.managed_application))
|
||||
{
|
||||
trace::error(_X("Failed to locate managed application: %s"), args.managed_application.c_str());
|
||||
return false;
|
||||
}
|
||||
args.app_dir = get_directory(args.managed_application);
|
||||
args.app_argc = argc - 2;
|
||||
args.app_argv = &argv[2];
|
||||
}
|
||||
|
@ -54,39 +74,20 @@ bool parse_arguments(const int argc, const pal::char_t* argv[], arguments_t& arg
|
|||
managed_app.append(get_executable(own_name));
|
||||
managed_app.append(_X(".dll"));
|
||||
args.managed_application = managed_app;
|
||||
if (!pal::realpath(&args.managed_application))
|
||||
{
|
||||
trace::error(_X("Failed to locate managed application: %s"), args.managed_application.c_str());
|
||||
return false;
|
||||
}
|
||||
args.app_dir = own_dir;
|
||||
args.app_argv = &argv[1];
|
||||
args.app_argc = argc - 1;
|
||||
}
|
||||
|
||||
// Read trace environment variable
|
||||
pal::string_t trace_str;
|
||||
if (pal::getenv(_X("COREHOST_TRACE"), trace_str))
|
||||
{
|
||||
auto trace_val = pal::xtoi(trace_str.c_str());
|
||||
if (trace_val > 0)
|
||||
{
|
||||
trace::enable();
|
||||
trace::info(_X("tracing enabled"));
|
||||
}
|
||||
}
|
||||
|
||||
// Read CLR path from environment variable
|
||||
pal::string_t home_str;
|
||||
pal::getenv(_X("DOTNET_HOME"), home_str);
|
||||
if (!home_str.empty())
|
||||
{
|
||||
append_path(home_str, _X("runtime"));
|
||||
append_path(home_str, _X("coreclr"));
|
||||
args.clr_path.assign(home_str);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use platform-specific search algorithm
|
||||
if (pal::find_coreclr(home_str))
|
||||
{
|
||||
args.clr_path.assign(home_str);
|
||||
}
|
||||
}
|
||||
|
||||
pal::getenv(_X("DOTNET_PACKAGES"), &args.dotnet_packages);
|
||||
pal::getenv(_X("DOTNET_PACKAGES_CACHE"), &args.dotnet_packages_cache);
|
||||
pal::getenv(_X("DOTNET_SERVICING"), &args.dotnet_servicing);
|
||||
pal::getenv(_X("DOTNET_RUNTIME_SERVICING"), &args.dotnet_runtime_servicing);
|
||||
pal::getenv(_X("DOTNET_HOME"), &args.dotnet_home);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -41,9 +41,9 @@ bool coreclr::bind(const pal::string_t& libcoreclr_path)
|
|||
assert(g_coreclr == nullptr);
|
||||
|
||||
pal::string_t coreclr_dll_path(libcoreclr_path);
|
||||
append_path(coreclr_dll_path, LIBCORECLR_NAME);
|
||||
append_path(&coreclr_dll_path, LIBCORECLR_NAME);
|
||||
|
||||
if (!pal::load_library(coreclr_dll_path.c_str(), g_coreclr))
|
||||
if (!pal::load_library(coreclr_dll_path.c_str(), &g_coreclr))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
597
src/corehost/src/deps_resolver.cpp
Normal file
597
src/corehost/src/deps_resolver.cpp
Normal file
|
@ -0,0 +1,597 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#include <set>
|
||||
#include <functional>
|
||||
#include <cassert>
|
||||
|
||||
#include "trace.h"
|
||||
#include "deps_resolver.h"
|
||||
#include "utils.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
// -----------------------------------------------------------------------------
|
||||
// Read a single field from the deps entry
|
||||
//
|
||||
// Parameters:
|
||||
// line - A deps file entry line
|
||||
// buf - The temporary buffer to use while parsing (with size to contain "line")
|
||||
// ofs - The offset that this method will read from "line" on invocation and
|
||||
// the offset that has been consumed by this method upon successful exit
|
||||
// field - The current field read from the line
|
||||
//
|
||||
// Assumption:
|
||||
// The line should be in a CSV format, with commas separating the fields.
|
||||
// The fields themselves will be quoted. The escape character is '\\'
|
||||
//
|
||||
// Returns:
|
||||
// True if parsed successfully. Else, false
|
||||
//
|
||||
// Note:
|
||||
// Callers cannot call with the same "line" upon an unsuccessful exit.
|
||||
bool read_field(const pal::string_t& line, pal::char_t* buf, unsigned* ofs, pal::string_t* field)
|
||||
{
|
||||
unsigned& offset = *ofs;
|
||||
pal::string_t& value_recv = *field;
|
||||
|
||||
// The first character should be a '"'
|
||||
if (line[offset] != '"')
|
||||
{
|
||||
trace::error(_X("Error reading TPA file"));
|
||||
return false;
|
||||
}
|
||||
offset++;
|
||||
|
||||
auto buf_offset = 0;
|
||||
|
||||
// Iterate through characters in the string
|
||||
for (; offset < line.length(); offset++)
|
||||
{
|
||||
// Is this a '\'?
|
||||
if (line[offset] == '\\')
|
||||
{
|
||||
// Skip this character and read the next character into the buffer
|
||||
offset++;
|
||||
buf[buf_offset] = line[offset];
|
||||
}
|
||||
// Is this a '"'?
|
||||
else if (line[offset] == '\"')
|
||||
{
|
||||
// Done! Advance to the pointer after the input
|
||||
offset++;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Take the character
|
||||
buf[buf_offset] = line[offset];
|
||||
}
|
||||
buf_offset++;
|
||||
}
|
||||
buf[buf_offset] = '\0';
|
||||
value_recv.assign(buf);
|
||||
|
||||
// Consume the ',' if we have one
|
||||
if (line[offset] == ',')
|
||||
{
|
||||
offset++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// A uniqifying append helper that doesn't let two entries with the same
|
||||
// "asset_name" be part of the "output" paths.
|
||||
//
|
||||
void add_tpa_asset(
|
||||
const pal::string_t& asset_name,
|
||||
const pal::string_t& asset_path,
|
||||
std::set<pal::string_t>* items,
|
||||
pal::string_t* output)
|
||||
{
|
||||
if (items->count(asset_name))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
trace::verbose(_X("Adding tpa entry: %s"), asset_path.c_str());
|
||||
output->append(asset_path);
|
||||
output->push_back(PATH_SEPARATOR);
|
||||
items->insert(asset_name);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Add mscorlib from the CLR directory. Even if CLR is serviced, we should pick
|
||||
// mscorlib from the CLR directory. If mscorlib could not be found in the CLR
|
||||
// location, then leave it to the CLR to pick the right mscorlib.
|
||||
//
|
||||
void add_mscorlib_to_tpa(const pal::string_t& clr_dir, std::set<pal::string_t>* items, pal::string_t* output)
|
||||
{
|
||||
pal::string_t mscorlib_ni_path = clr_dir + DIR_SEPARATOR + _X("mscorlib.ni.dll");
|
||||
if (pal::file_exists(mscorlib_ni_path))
|
||||
{
|
||||
add_tpa_asset(_X("mscorlib"), mscorlib_ni_path, items, output);
|
||||
return;
|
||||
}
|
||||
|
||||
pal::string_t mscorlib_path = clr_dir + DIR_SEPARATOR + _X("mscorlib.dll");
|
||||
if (pal::file_exists(mscorlib_path))
|
||||
{
|
||||
add_tpa_asset(_X("mscorlib"), mscorlib_ni_path, items, output);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// A uniqifying append helper that doesn't let two "paths" to be identical in
|
||||
// the "output" string.
|
||||
//
|
||||
void add_unique_path(
|
||||
const pal::string_t& type,
|
||||
const pal::string_t& path,
|
||||
std::set<pal::string_t>* existing,
|
||||
pal::string_t* output)
|
||||
{
|
||||
if (existing->count(path))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
trace::verbose(_X("Adding to %s path: %s"), type.c_str(), path.c_str());
|
||||
output->append(path);
|
||||
output->push_back(PATH_SEPARATOR);
|
||||
existing->insert(path);
|
||||
}
|
||||
|
||||
} // end of anonymous namespace
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Given a "base" directory, yield the relative path of this file in the package
|
||||
// layout.
|
||||
//
|
||||
// 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 relative to the "base" directory
|
||||
//
|
||||
// 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, pal::string_t* str) const
|
||||
{
|
||||
pal::string_t& candidate = *str;
|
||||
|
||||
candidate.clear();
|
||||
|
||||
// Entry relative path contains '/' separator, sanitize it to use
|
||||
// platform separator. Perf: avoid extra copy if it matters.
|
||||
pal::string_t pal_relative_path = relative_path;
|
||||
if (_X('/') != DIR_SEPARATOR)
|
||||
{
|
||||
replace_char(&pal_relative_path, _X('/'), DIR_SEPARATOR);
|
||||
}
|
||||
|
||||
// Reserve space for the path below
|
||||
candidate.reserve(base.length() +
|
||||
library_name.length() +
|
||||
library_version.length() +
|
||||
pal_relative_path.length() + 3);
|
||||
|
||||
candidate.assign(base);
|
||||
append_path(&candidate, library_name.c_str());
|
||||
append_path(&candidate, library_version.c_str());
|
||||
append_path(&candidate, pal_relative_path.c_str());
|
||||
|
||||
bool exists = pal::file_exists(candidate);
|
||||
if (!exists)
|
||||
{
|
||||
candidate.clear();
|
||||
}
|
||||
return exists;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Given a "base" directory, yield the relative path of this file in the package
|
||||
// layout if the entry hash matches the hash file in the "base" directory
|
||||
//
|
||||
// Parameters:
|
||||
// base - The base directory to look for the relative path of this entry and
|
||||
// the hash file.
|
||||
// str - If the method returns true, contains the file path for this deps
|
||||
// entry relative to the "base" directory
|
||||
//
|
||||
// Description:
|
||||
// Looks for a file named "{PackageName}.{PackageVersion}.nupkg.{HashAlgorithm}"
|
||||
// If the deps entry's {HashAlgorithm}-{HashValue} matches the contents then
|
||||
// yields the relative path of this entry in the "base" dir.
|
||||
//
|
||||
// Returns:
|
||||
// If the file exists in the path relative to the "base" directory and there
|
||||
// was hash file match with this deps entry.
|
||||
//
|
||||
// See: to_full_path(base, str)
|
||||
//
|
||||
bool deps_entry_t::to_hash_matched_path(const pal::string_t& base, pal::string_t* str) const
|
||||
{
|
||||
pal::string_t& candidate = *str;
|
||||
|
||||
candidate.clear();
|
||||
|
||||
// Base directory must be present to perform hash lookup.
|
||||
if (base.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// First detect position of hyphen in [Algorithm]-[Hash] in the string.
|
||||
size_t pos = library_hash.find(_X("-"));
|
||||
if (pos == 0 || pos == pal::string_t::npos)
|
||||
{
|
||||
trace::verbose(_X("Invalid hash %s value for deps file entry: %s"), library_hash.c_str(), library_name.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Build the nupkg file name. Just reserve approx 8 char_t's for the algorithm name.
|
||||
pal::string_t nupkg_filename;
|
||||
nupkg_filename.reserve(library_name.length() + 1 + library_version.length() + 16);
|
||||
nupkg_filename.append(library_name);
|
||||
nupkg_filename.append(_X("."));
|
||||
nupkg_filename.append(library_version);
|
||||
nupkg_filename.append(_X(".nupkg."));
|
||||
nupkg_filename.append(library_hash.substr(0, pos));
|
||||
|
||||
// Build the hash file path str.
|
||||
pal::string_t hash_file;
|
||||
hash_file.reserve(base.length() + library_name.length() + library_version.length() + nupkg_filename.length() + 3);
|
||||
hash_file.assign(base);
|
||||
append_path(&hash_file, library_name.c_str());
|
||||
append_path(&hash_file, library_version.c_str());
|
||||
append_path(&hash_file, nupkg_filename.c_str());
|
||||
|
||||
// Read the contents of the hash file.
|
||||
pal::ifstream_t fstream(hash_file);
|
||||
if (!fstream.good())
|
||||
{
|
||||
trace::verbose(_X("The hash file is invalid [%s]"), hash_file.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Obtain the hash from the file.
|
||||
std::string hash;
|
||||
hash.assign(pal::istreambuf_iterator_t(fstream),
|
||||
pal::istreambuf_iterator_t());
|
||||
pal::string_t pal_hash;
|
||||
pal::to_palstring(hash.c_str(), &pal_hash);
|
||||
|
||||
// Check if contents match deps entry.
|
||||
pal::string_t entry_hash = library_hash.substr(pos + 1);
|
||||
if (entry_hash != pal_hash)
|
||||
{
|
||||
trace::verbose(_X("The file hash [%s][%d] did not match entry hash [%s][%d]"),
|
||||
pal_hash.c_str(), pal_hash.length(), entry_hash.c_str(), entry_hash.length());
|
||||
return false;
|
||||
}
|
||||
|
||||
// All good, just append the relative dir to base.
|
||||
return to_full_path(base, &candidate);
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Load the deps file and parse its "entry" lines which contain the "fields" of
|
||||
// the entry. Populate an array of these entries.
|
||||
//
|
||||
bool deps_resolver_t::load()
|
||||
{
|
||||
// If file doesn't exist, then assume parsed.
|
||||
if (!pal::file_exists(m_deps_path))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Somehow the file stream could not be opened. This is an error.
|
||||
pal::ifstream_t file(m_deps_path);
|
||||
if (!file.good())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parse the "entry" lines of the deps file.
|
||||
std::string stdline;
|
||||
while (std::getline(file, stdline))
|
||||
{
|
||||
pal::string_t line;
|
||||
pal::to_palstring(stdline.c_str(), &line);
|
||||
|
||||
deps_entry_t entry;
|
||||
pal::string_t is_serviceable;
|
||||
pal::string_t* fields[] = {
|
||||
&entry.library_type,
|
||||
&entry.library_name,
|
||||
&entry.library_version,
|
||||
&entry.library_hash,
|
||||
&entry.asset_type,
|
||||
&entry.asset_name,
|
||||
&entry.relative_path,
|
||||
// TODO: Add when the deps file support is enabled.
|
||||
// &is_serviceable
|
||||
};
|
||||
|
||||
std::vector<pal::char_t> buf(line.length());
|
||||
|
||||
for (unsigned i = 0, offset = 0; i < sizeof(fields) / sizeof(fields[0]); ++i)
|
||||
{
|
||||
if (!(read_field(line, buf.data(), &offset, fields[i])))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Serviceable, if not false, default is true.
|
||||
entry.is_serviceable = pal::strcasecmp(is_serviceable.c_str(), _X("false")) != 0;
|
||||
|
||||
// TODO: Deps file does not follow spec. It uses '\\', should use '/'
|
||||
replace_char(&entry.relative_path, _X('\\'), _X('/'));
|
||||
|
||||
m_deps_entries.push_back(entry);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Resolve path to the deps file from "args" and parse the deps file.
|
||||
//
|
||||
// Returns:
|
||||
// True if the file parse is successful or if file doesn't exist. False,
|
||||
// when there is an error parsing the file.
|
||||
//
|
||||
bool deps_resolver_t::parse_deps_file(const arguments_t& args)
|
||||
{
|
||||
const auto& app_base = args.app_dir;
|
||||
auto app_name = get_filename(args.managed_application);
|
||||
|
||||
m_deps_path.reserve(app_base.length() + 1 + app_name.length() + 5);
|
||||
m_deps_path.append(app_base);
|
||||
m_deps_path.push_back(DIR_SEPARATOR);
|
||||
m_deps_path.append(app_name, 0, app_name.find_last_of(_X(".")));
|
||||
m_deps_path.append(_X(".deps"));
|
||||
|
||||
return load();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Load local assemblies by priority order of their file extensions and
|
||||
// unique-fied by their simple name.
|
||||
//
|
||||
void deps_resolver_t::get_local_assemblies(const pal::string_t& dir)
|
||||
{
|
||||
trace::verbose(_X("Adding files from dir %s"), dir.c_str());
|
||||
|
||||
// Managed extensions in priority order, pick DLL over EXE and NI over IL.
|
||||
const pal::string_t managed_ext[] = { _X(".ni.dll"), _X(".dll"), _X(".ni.exe"), _X(".exe") };
|
||||
|
||||
// List of files in the dir
|
||||
std::vector<pal::string_t> files;
|
||||
pal::readdir(dir, &files);
|
||||
|
||||
for (const auto& file : files)
|
||||
{
|
||||
for (const auto& ext : managed_ext)
|
||||
{
|
||||
// Nothing to do if file length is smaller than expected ext.
|
||||
if (file.length() <= ext.length())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
auto file_name = file.substr(0, file.length() - ext.length());
|
||||
auto file_ext = file.substr(file_name.length());
|
||||
|
||||
// Ext did not match expected ext, skip this file.
|
||||
if (pal::strcasecmp(file_ext.c_str(), ext.c_str()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO: Do a case insensitive lookup.
|
||||
// Already added entry for this asset, by priority order skip this ext
|
||||
if (m_local_assemblies.count(file_name))
|
||||
{
|
||||
trace::verbose(_X("Skipping %s because the %s already exists in local assemblies"), file.c_str(), m_local_assemblies.find(file_name)->second.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add entry for this asset
|
||||
pal::string_t file_path = dir + DIR_SEPARATOR + file;
|
||||
trace::verbose(_X("Adding %s to local assembly set from %s"), file_name.c_str(), file_path.c_str());
|
||||
m_local_assemblies.emplace(file_name, file_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Resolve the TPA list order.
|
||||
//
|
||||
// Description:
|
||||
// First, add mscorlib to the TPA. Then for each deps entry, check if they
|
||||
// are serviced. If they are not serviced, then look if they are present
|
||||
// app local. Worst case, default to the primary and seconday package
|
||||
// caches. Finally, for cases where deps file may not be present or if deps
|
||||
// did not have an entry for an app local assembly, just use them from the
|
||||
// app dir in the TPA path.
|
||||
//
|
||||
// Parameters:
|
||||
// app_dir - The application local directory
|
||||
// package_dir - The directory path to where packages are restored
|
||||
// package_cache_dir - The directory path to secondary cache for packages
|
||||
// clr_dir - The directory where the host loads the CLR
|
||||
//
|
||||
// Returns:
|
||||
// output - Pointer to a string that will hold the resolved TPA paths
|
||||
//
|
||||
void deps_resolver_t::resolve_tpa_list(
|
||||
const pal::string_t& app_dir,
|
||||
const pal::string_t& package_dir,
|
||||
const pal::string_t& package_cache_dir,
|
||||
const pal::string_t& clr_dir,
|
||||
pal::string_t* output)
|
||||
{
|
||||
// Obtain the local assemblies in the app dir.
|
||||
get_local_assemblies(app_dir);
|
||||
|
||||
std::set<pal::string_t> items;
|
||||
|
||||
add_mscorlib_to_tpa(clr_dir, &items, output);
|
||||
|
||||
for (const deps_entry_t& entry : m_deps_entries)
|
||||
{
|
||||
// Is this asset a "runtime" type?
|
||||
if (entry.asset_type != _X("runtime") || items.count(entry.asset_name))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
pal::string_t candidate;
|
||||
|
||||
// Is this a serviceable entry and is there an entry in the servicing index?
|
||||
if (entry.is_serviceable && entry.library_type == _X("Package") &&
|
||||
m_svc.find_redirection(entry.library_name, entry.library_version, entry.relative_path, &candidate))
|
||||
{
|
||||
add_tpa_asset(entry.asset_name, candidate, &items, output);
|
||||
}
|
||||
// Is this entry present locally?
|
||||
else if (m_local_assemblies.count(entry.asset_name))
|
||||
{
|
||||
// TODO: Case insensitive look up?
|
||||
add_tpa_asset(entry.asset_name, m_local_assemblies.find(entry.asset_name)->second, &items, output);
|
||||
}
|
||||
// Is this entry present in the package restore dir?
|
||||
else if (entry.to_full_path(package_dir, &candidate))
|
||||
{
|
||||
add_tpa_asset(entry.asset_name, candidate, &items, output);
|
||||
}
|
||||
// Is this entry present in the secondary package cache?
|
||||
else if (entry.to_hash_matched_path(package_cache_dir, &candidate))
|
||||
{
|
||||
add_tpa_asset(entry.asset_name, candidate, &items, output);
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, if the deps file wasn't present or has missing entries, then
|
||||
// add the app local assemblies to the TPA.
|
||||
for (const auto& kv : m_local_assemblies)
|
||||
{
|
||||
add_tpa_asset(kv.first, kv.second, &items, output);
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Resolve the directories order for culture/native lookup
|
||||
//
|
||||
// Description:
|
||||
// This general purpose function specifies priority order of directory lookup
|
||||
// for both native images and culture specific resource images. Lookup for
|
||||
// culture assemblies is done by looking up two levels above from the file
|
||||
// path. Lookup for native images is done by looking up one level from the
|
||||
// file path.
|
||||
//
|
||||
// Parameters:
|
||||
// asset_type - The type of the asset that needs lookup, currently
|
||||
// supports "culture" and "native"
|
||||
// app_dir - The application local directory
|
||||
// package_dir - The directory path to where packages are restored
|
||||
// package_cache_dir - The directory path to secondary cache for packages
|
||||
// clr_dir - The directory where the host loads the CLR
|
||||
//
|
||||
// Returns:
|
||||
// output - Pointer to a string that will hold the resolved lookup dirs
|
||||
//
|
||||
void deps_resolver_t::resolve_probe_dirs(
|
||||
const pal::string_t& asset_type,
|
||||
const pal::string_t& app_dir,
|
||||
const pal::string_t& package_dir,
|
||||
const pal::string_t& package_cache_dir,
|
||||
const pal::string_t& clr_dir,
|
||||
pal::string_t* output)
|
||||
{
|
||||
assert(asset_type == _X("culture") || asset_type == _X("native"));
|
||||
|
||||
// For culture assemblies, we need to provide the base directory of the culture path.
|
||||
// For example: .../Foo/en-US/Bar.dll, then, the resolved path is .../Foo
|
||||
std::function<pal::string_t(const pal::string_t&)> culture = [] (const pal::string_t& str) {
|
||||
return get_directory(get_directory(str));
|
||||
};
|
||||
// For native assemblies, obtain the directory path from the file path
|
||||
std::function<pal::string_t(const pal::string_t&)> native = [] (const pal::string_t& str) {
|
||||
return get_directory(str);
|
||||
};
|
||||
std::function<pal::string_t(const pal::string_t&)>& action = (asset_type == _X("culture")) ? culture : native;
|
||||
|
||||
std::set<pal::string_t> items;
|
||||
|
||||
// Fill the "output" with serviced DLL directories if they are serviceable
|
||||
// and have an entry present.
|
||||
for (const deps_entry_t& entry : m_deps_entries)
|
||||
{
|
||||
pal::string_t redirection_path;
|
||||
if (entry.is_serviceable && entry.asset_type == asset_type && entry.library_type == _X("Package") &&
|
||||
m_svc.find_redirection(entry.library_name, entry.library_version, entry.relative_path, &redirection_path))
|
||||
{
|
||||
add_unique_path(asset_type, action(redirection_path), &items, output);
|
||||
}
|
||||
}
|
||||
|
||||
// App local path
|
||||
add_unique_path(asset_type, app_dir, &items, output);
|
||||
|
||||
// Take care of the packages cached path
|
||||
for (const deps_entry_t& entry : m_deps_entries)
|
||||
{
|
||||
if (entry.asset_type != asset_type)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
pal::string_t candidate;
|
||||
// Package restore directory
|
||||
if (entry.to_full_path(package_dir, &candidate))
|
||||
{
|
||||
add_unique_path(asset_type, action(candidate), &items, output);
|
||||
}
|
||||
// Secondary cache
|
||||
else if (entry.to_hash_matched_path(package_cache_dir, &candidate))
|
||||
{
|
||||
add_unique_path(asset_type, action(candidate), &items, output);
|
||||
}
|
||||
}
|
||||
|
||||
// CLR path
|
||||
add_unique_path(asset_type, clr_dir, &items, output);
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Entrypoint to resolve TPA, native and culture path ordering to pass to CoreCLR.
|
||||
//
|
||||
// Parameters:
|
||||
// app_dir - The application local directory
|
||||
// package_dir - The directory path to where packages are restored
|
||||
// package_cache_dir - The directory path to secondary cache for packages
|
||||
// clr_dir - The directory where the host loads the CLR
|
||||
// probe_paths - Pointer to struct containing fields that will contain
|
||||
// resolved path ordering.
|
||||
//
|
||||
//
|
||||
bool deps_resolver_t::resolve_probe_paths(
|
||||
const pal::string_t& app_dir,
|
||||
const pal::string_t& package_dir,
|
||||
const pal::string_t& package_cache_dir,
|
||||
const pal::string_t& clr_dir,
|
||||
probe_paths_t* probe_paths)
|
||||
{
|
||||
resolve_tpa_list(app_dir, package_dir, package_cache_dir, clr_dir, &probe_paths->tpa);
|
||||
resolve_probe_dirs(_X("native"), app_dir, package_dir, package_cache_dir, clr_dir, &probe_paths->native);
|
||||
resolve_probe_dirs(_X("culture"), app_dir, package_dir, package_cache_dir, clr_dir, &probe_paths->culture);
|
||||
return true;
|
||||
}
|
|
@ -4,121 +4,191 @@
|
|||
#include "pal.h"
|
||||
#include "args.h"
|
||||
#include "trace.h"
|
||||
#include "tpafile.h"
|
||||
#include "deps_resolver.h"
|
||||
#include "utils.h"
|
||||
#include "coreclr.h"
|
||||
|
||||
void get_tpafile_path(const pal::string_t& app_base, const pal::string_t& app_name, pal::string_t& tpapath)
|
||||
enum StatusCode
|
||||
{
|
||||
tpapath.reserve(app_base.length() + app_name.length() + 5);
|
||||
Failure = 0x87FF0000,
|
||||
InvalidArgFailure = Failure | 0x1,
|
||||
CoreClrResolveFailure = Failure | 0x2,
|
||||
CoreClrBindFailure = Failure | 0x3,
|
||||
CoreClrInitFailure = Failure | 0x4,
|
||||
CoreClrExeFailure = Failure | 0x5,
|
||||
ResolverInitFailure = Failure | 0x6,
|
||||
ResolverResolveFailure = Failure | 0x7,
|
||||
};
|
||||
|
||||
tpapath.append(app_base);
|
||||
tpapath.push_back(DIR_SEPARATOR);
|
||||
// ----------------------------------------------------------------------
|
||||
// resolve_clr_path: Resolve CLR Path in priority order
|
||||
//
|
||||
// Description:
|
||||
// Check if CoreCLR library exists in runtime servicing dir or app
|
||||
// local or DOTNET_HOME directory in that order of priority. If these
|
||||
// fail to locate CoreCLR, then check platform-specific search.
|
||||
//
|
||||
// Returns:
|
||||
// "true" if path to the CoreCLR dir can be resolved in "clr_path"
|
||||
// parameter. Else, returns "false" with "clr_path" unmodified.
|
||||
//
|
||||
bool resolve_clr_path(const arguments_t& args, pal::string_t* clr_path)
|
||||
{
|
||||
const pal::string_t* dirs[] = {
|
||||
&args.dotnet_runtime_servicing, // DOTNET_RUNTIME_SERVICING
|
||||
&args.app_dir, // APP LOCAL
|
||||
&args.dotnet_home // DOTNET_HOME
|
||||
};
|
||||
for (int i = 0; i < sizeof(dirs) / sizeof(dirs[0]); ++i)
|
||||
{
|
||||
if (dirs[i]->empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Remove the extension from the app_name
|
||||
auto ext_location = app_name.find_last_of('.');
|
||||
if (ext_location != std::string::npos)
|
||||
{
|
||||
tpapath.append(app_name.substr(0, ext_location));
|
||||
// App dir should contain coreclr, so skip appending path.
|
||||
pal::string_t cur_dir = *dirs[i];
|
||||
if (dirs[i] != &args.app_dir)
|
||||
{
|
||||
append_path(&cur_dir, _X("runtime"));
|
||||
append_path(&cur_dir, _X("coreclr"));
|
||||
}
|
||||
|
||||
// Found coreclr in priority order.
|
||||
if (coreclr_exists_in_dir(cur_dir))
|
||||
{
|
||||
clr_path->assign(cur_dir);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
// Use platform-specific search algorithm
|
||||
pal::string_t home_dir = args.dotnet_home;
|
||||
if (pal::find_coreclr(&home_dir))
|
||||
{
|
||||
tpapath.append(app_name);
|
||||
clr_path->assign(home_dir);
|
||||
return true;
|
||||
}
|
||||
tpapath.append(_X(".deps"));
|
||||
return false;
|
||||
}
|
||||
|
||||
int run(arguments_t args, pal::string_t app_base, tpafile tpa)
|
||||
int run(const arguments_t& args, const pal::string_t& clr_path)
|
||||
{
|
||||
tpa.add_from_local_dir(app_base);
|
||||
// Load the deps resolver
|
||||
deps_resolver_t resolver(args);
|
||||
if (!resolver.valid())
|
||||
{
|
||||
trace::error(_X("Invalid .deps file"));
|
||||
return StatusCode::ResolverInitFailure;
|
||||
}
|
||||
|
||||
// Add packages directory
|
||||
pal::string_t packages_dir;
|
||||
if (!pal::get_default_packages_directory(packages_dir))
|
||||
pal::string_t packages_dir = args.dotnet_packages;
|
||||
if (!pal::directory_exists(packages_dir))
|
||||
{
|
||||
trace::info(_X("did not find local packages directory"));
|
||||
|
||||
// We can continue, the app may have it's dependencies locally
|
||||
(void)pal::get_default_packages_directory(&packages_dir);
|
||||
}
|
||||
else
|
||||
trace::info(_X("Package directory: %s"), packages_dir.empty() ? _X("not specified") : packages_dir.c_str());
|
||||
|
||||
probe_paths_t probe_paths;
|
||||
if (!resolver.resolve_probe_paths(args.app_dir, packages_dir, args.dotnet_packages_cache, clr_path, &probe_paths))
|
||||
{
|
||||
trace::info(_X("using packages directory: %s"), packages_dir.c_str());
|
||||
tpa.add_package_dir(packages_dir);
|
||||
return StatusCode::ResolverResolveFailure;
|
||||
}
|
||||
|
||||
// Add native search path
|
||||
trace::info(_X("using native search path: %s"), packages_dir.c_str());
|
||||
tpa.add_native_search_path(args.clr_path);
|
||||
|
||||
// Build TPA list and search paths
|
||||
pal::string_t tpalist;
|
||||
tpa.write_tpa_list(tpalist);
|
||||
|
||||
pal::string_t search_paths;
|
||||
tpa.write_native_paths(search_paths);
|
||||
|
||||
// Build CoreCLR properties
|
||||
const char* property_keys[] = {
|
||||
"TRUSTED_PLATFORM_ASSEMBLIES",
|
||||
"APP_PATHS",
|
||||
"APP_NI_PATHS",
|
||||
"NATIVE_DLL_SEARCH_DIRECTORIES",
|
||||
"AppDomainCompatSwitch"
|
||||
"PLATFORM_RESOURCE_ROOTS",
|
||||
"AppDomainCompatSwitch",
|
||||
// TODO: pipe this from corehost.json
|
||||
"SERVER_GC"
|
||||
};
|
||||
|
||||
auto tpa_cstr = pal::to_stdstring(tpalist);
|
||||
auto app_base_cstr = pal::to_stdstring(app_base);
|
||||
auto search_paths_cstr = pal::to_stdstring(search_paths);
|
||||
auto tpa_paths_cstr = pal::to_stdstring(probe_paths.tpa);
|
||||
auto app_base_cstr = pal::to_stdstring(args.app_dir);
|
||||
auto native_dirs_cstr = pal::to_stdstring(probe_paths.native);
|
||||
auto culture_dirs_cstr = pal::to_stdstring(probe_paths.culture);
|
||||
|
||||
const char* property_values[] = {
|
||||
// TRUSTED_PLATFORM_ASSEMBLIES
|
||||
tpa_cstr.c_str(),
|
||||
tpa_paths_cstr.c_str(),
|
||||
// APP_PATHS
|
||||
app_base_cstr.c_str(),
|
||||
// APP_NI_PATHS
|
||||
app_base_cstr.c_str(),
|
||||
// NATIVE_DLL_SEARCH_DIRECTORIES
|
||||
search_paths_cstr.c_str(),
|
||||
native_dirs_cstr.c_str(),
|
||||
// PLATFORM_RESOURCE_ROOTS
|
||||
culture_dirs_cstr.c_str(),
|
||||
// AppDomainCompatSwitch
|
||||
"UseLatestBehaviorWhenTFMNotSpecified"
|
||||
"UseLatestBehaviorWhenTFMNotSpecified",
|
||||
// SERVER_GC
|
||||
"1"
|
||||
};
|
||||
|
||||
// Dump TPA list
|
||||
trace::verbose(_X("TPA List: %s"), tpalist.c_str());
|
||||
|
||||
// Dump native search paths
|
||||
trace::verbose(_X("Native Paths: %s"), search_paths.c_str());
|
||||
size_t property_size = sizeof(property_keys) / sizeof(property_keys[0]);
|
||||
|
||||
// Bind CoreCLR
|
||||
if (!coreclr::bind(args.clr_path))
|
||||
if (!coreclr::bind(clr_path))
|
||||
{
|
||||
trace::error(_X("failed to bind to coreclr"));
|
||||
return 1;
|
||||
trace::error(_X("Failed to bind to coreclr"));
|
||||
return StatusCode::CoreClrBindFailure;
|
||||
}
|
||||
|
||||
// Verbose logging
|
||||
if (trace::is_enabled())
|
||||
{
|
||||
for (size_t i = 0; i < property_size; ++i)
|
||||
{
|
||||
pal::string_t key, val;
|
||||
pal::to_palstring(property_keys[i], &key);
|
||||
pal::to_palstring(property_values[i], &val);
|
||||
trace::verbose(_X("Property %s = %s"), key.c_str(), val.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
std::string own_path;
|
||||
pal::to_stdstring(args.own_path.c_str(), &own_path);
|
||||
|
||||
// Initialize CoreCLR
|
||||
coreclr::host_handle_t host_handle;
|
||||
coreclr::domain_id_t domain_id;
|
||||
auto hr = coreclr::initialize(
|
||||
pal::to_stdstring(args.own_path).c_str(),
|
||||
own_path.c_str(),
|
||||
"clrhost",
|
||||
property_keys,
|
||||
property_values,
|
||||
sizeof(property_keys) / sizeof(property_keys[0]),
|
||||
property_size,
|
||||
&host_handle,
|
||||
&domain_id);
|
||||
if (!SUCCEEDED(hr))
|
||||
{
|
||||
trace::error(_X("failed to initialize CoreCLR, HRESULT: 0x%X"), hr);
|
||||
return 1;
|
||||
trace::error(_X("Failed to initialize CoreCLR, HRESULT: 0x%X"), hr);
|
||||
return StatusCode::CoreClrInitFailure;
|
||||
}
|
||||
|
||||
// Convert the args (probably not the most performant way to do this...)
|
||||
auto argv_strs = new std::string[args.app_argc];
|
||||
auto argv = new const char*[args.app_argc];
|
||||
if (trace::is_enabled())
|
||||
{
|
||||
pal::string_t arg_str;
|
||||
for (int i = 0; i < args.app_argc; i++)
|
||||
{
|
||||
arg_str.append(args.app_argv[i]);
|
||||
arg_str.append(_X(","));
|
||||
}
|
||||
trace::info(_X("Launch host: %s app: %s, argc: %d args: %s"), args.own_path.c_str(),
|
||||
args.managed_application.c_str(), args.app_argc, arg_str.c_str());
|
||||
}
|
||||
|
||||
// Initialize with empty strings
|
||||
std::vector<std::string> argv_strs(args.app_argc);
|
||||
std::vector<const char*> argv(args.app_argc);
|
||||
for (int i = 0; i < args.app_argc; i++)
|
||||
{
|
||||
argv_strs[i] = pal::to_stdstring(pal::string_t(args.app_argv[i]));
|
||||
pal::to_stdstring(args.app_argv[i], &argv_strs[i]);
|
||||
argv[i] = argv_strs[i].c_str();
|
||||
}
|
||||
|
||||
|
@ -127,21 +197,21 @@ int run(arguments_t args, pal::string_t app_base, tpafile tpa)
|
|||
hr = coreclr::execute_assembly(
|
||||
host_handle,
|
||||
domain_id,
|
||||
args.app_argc,
|
||||
argv,
|
||||
argv.size(),
|
||||
argv.data(),
|
||||
pal::to_stdstring(args.managed_application).c_str(),
|
||||
&exit_code);
|
||||
if (!SUCCEEDED(hr))
|
||||
{
|
||||
trace::error(_X("failed to execute managed app, HRESULT: 0x%X"), hr);
|
||||
return 1;
|
||||
trace::error(_X("Failed to execute managed app, HRESULT: 0x%X"), hr);
|
||||
return StatusCode::CoreClrExeFailure;
|
||||
}
|
||||
|
||||
// Shut down the CoreCLR
|
||||
hr = coreclr::shutdown(host_handle, domain_id);
|
||||
if (!SUCCEEDED(hr))
|
||||
{
|
||||
trace::warning(_X("failed to shut down CoreCLR, HRESULT: 0x%X"), hr);
|
||||
trace::warning(_X("Failed to shut down CoreCLR, HRESULT: 0x%X"), hr);
|
||||
}
|
||||
|
||||
coreclr::unload();
|
||||
|
@ -155,70 +225,19 @@ int __cdecl wmain(const int argc, const pal::char_t* argv[])
|
|||
int main(const int argc, const pal::char_t* argv[])
|
||||
#endif
|
||||
{
|
||||
// Take care of arguments
|
||||
arguments_t args;
|
||||
if (!parse_arguments(argc, argv, args))
|
||||
{
|
||||
return 1;
|
||||
return StatusCode::InvalidArgFailure;
|
||||
}
|
||||
|
||||
// Resolve paths
|
||||
if (!pal::realpath(args.managed_application))
|
||||
// Resolve CLR path
|
||||
pal::string_t clr_path;
|
||||
if (!resolve_clr_path(args, &clr_path))
|
||||
{
|
||||
trace::error(_X("failed to locate managed application: %s"), args.managed_application.c_str());
|
||||
return 1;
|
||||
trace::error(_X("Could not resolve coreclr path"));
|
||||
return StatusCode::CoreClrResolveFailure;
|
||||
}
|
||||
trace::info(_X("preparing to launch managed application: %s"), args.managed_application.c_str());
|
||||
trace::info(_X("host path: %s"), args.own_path.c_str());
|
||||
|
||||
pal::string_t argstr;
|
||||
for (int i = 0; i < args.app_argc; i++)
|
||||
{
|
||||
argstr.append(args.app_argv[i]);
|
||||
argstr.append(_X(","));
|
||||
}
|
||||
trace::info(_X("App argc: %d"), args.app_argc);
|
||||
trace::info(_X("App argv: %s"), argstr.c_str());
|
||||
|
||||
auto app_base = get_directory(args.managed_application);
|
||||
auto app_name = get_filename(args.managed_application);
|
||||
|
||||
// App-local coreclr wins
|
||||
{
|
||||
pal::string_t candidate;
|
||||
candidate.assign(app_base);
|
||||
append_path(candidate, LIBCORECLR_NAME);
|
||||
|
||||
if (pal::file_exists(candidate))
|
||||
{
|
||||
args.clr_path.assign(app_base);
|
||||
}
|
||||
}
|
||||
|
||||
if (args.clr_path.empty())
|
||||
{
|
||||
trace::error(_X("failed to locate CLR files, set the DOTNET_HOME environment variable"));
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!pal::realpath(args.clr_path))
|
||||
{
|
||||
trace::error(_X("failed to locate CLR files at %s"), args.clr_path.c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
trace::info(_X("using CLR files from: %s"), args.clr_path.c_str());
|
||||
trace::info(_X("preparing to launch: %s"), app_name.c_str());
|
||||
trace::info(_X("using app base: %s"), app_base.c_str());
|
||||
|
||||
// Check for and load deps file
|
||||
pal::string_t tpafile_path;
|
||||
get_tpafile_path(app_base, app_name, tpafile_path);
|
||||
trace::info(_X("checking for .deps File at: %s"), tpafile_path.c_str());
|
||||
tpafile tpa;
|
||||
if (!tpa.load(tpafile_path))
|
||||
{
|
||||
trace::error(_X("invalid .deps file"));
|
||||
return 1;
|
||||
}
|
||||
return run(args, app_base, tpa);
|
||||
return run(args, clr_path);
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "utils.h"
|
||||
#include "trace.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <dlfcn.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
|
@ -19,7 +20,7 @@
|
|||
#define symlinkEntrypointExecutable "/proc/curproc/exe"
|
||||
#endif
|
||||
|
||||
bool pal::find_coreclr(pal::string_t& recv)
|
||||
bool pal::find_coreclr(pal::string_t* recv)
|
||||
{
|
||||
pal::string_t candidate;
|
||||
pal::string_t test;
|
||||
|
@ -28,24 +29,24 @@ bool pal::find_coreclr(pal::string_t& recv)
|
|||
// TODO: These paths should be consistent
|
||||
candidate.assign("/usr/share/dotnet/runtime/coreclr");
|
||||
if (coreclr_exists_in_dir(candidate)) {
|
||||
recv.assign(candidate);
|
||||
recv->assign(candidate);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
candidate.assign("/usr/local/share/dotnet/runtime/coreclr");
|
||||
if (coreclr_exists_in_dir(candidate)) {
|
||||
recv.assign(candidate);
|
||||
recv->assign(candidate);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool pal::load_library(const char_t* path, dll_t& dll)
|
||||
bool pal::load_library(const char_t* path, dll_t* dll)
|
||||
{
|
||||
dll = dlopen(path, RTLD_LAZY);
|
||||
if (dll == nullptr)
|
||||
*dll = dlopen(path, RTLD_LAZY);
|
||||
if (*dll == nullptr)
|
||||
{
|
||||
trace::error(_X("failed to load %s, error: %s"), path, dlerror());
|
||||
trace::error(_X("Failed to load %s, error: %s"), path, dlerror());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -56,7 +57,7 @@ pal::proc_t pal::get_symbol(dll_t library, const char* name)
|
|||
auto result = dlsym(library, name);
|
||||
if (result == nullptr)
|
||||
{
|
||||
trace::error(_X("failed to resolve library symbol %s, error: %s"), name, dlerror());
|
||||
trace::error(_X("Failed to resolve library symbol %s, error: %s"), name, dlerror());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -65,7 +66,7 @@ void pal::unload_library(dll_t library)
|
|||
{
|
||||
if (dlclose(library) != 0)
|
||||
{
|
||||
trace::warning(_X("failed to unload library, error: %s"), dlerror());
|
||||
trace::warning(_X("Failed to unload library, error: %s"), dlerror());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -79,19 +80,20 @@ bool pal::is_path_rooted(const pal::string_t& path)
|
|||
return path.front() == '/';
|
||||
}
|
||||
|
||||
bool pal::get_default_packages_directory(pal::string_t& recv)
|
||||
bool pal::get_default_packages_directory(pal::string_t* recv)
|
||||
{
|
||||
recv->clear();
|
||||
if (!pal::getenv("HOME", recv))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
append_path(recv, _X(".dnx"));
|
||||
append_path(recv, _X("packages"));
|
||||
append_path(&*recv, _X(".dnx"));
|
||||
append_path(&*recv, _X("packages"));
|
||||
return true;
|
||||
}
|
||||
|
||||
#if defined(__APPLE__)
|
||||
bool pal::get_own_executable_path(pal::string_t& recv)
|
||||
bool pal::get_own_executable_path(pal::string_t* recv)
|
||||
{
|
||||
uint32_t path_length = 0;
|
||||
if (_NSGetExecutablePath(nullptr, &path_length) == -1)
|
||||
|
@ -99,28 +101,28 @@ bool pal::get_own_executable_path(pal::string_t& recv)
|
|||
char path_buf[path_length];
|
||||
if (_NSGetExecutablePath(path_buf, &path_length) == 0)
|
||||
{
|
||||
recv.assign(path_buf);
|
||||
recv->assign(path_buf);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
bool pal::get_own_executable_path(pal::string_t& recv)
|
||||
bool pal::get_own_executable_path(pal::string_t* recv)
|
||||
{
|
||||
// Just return the symlink to the exe from /proc
|
||||
// We'll call realpath on it later
|
||||
recv.assign(symlinkEntrypointExecutable);
|
||||
recv->assign(symlinkEntrypointExecutable);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool pal::getenv(const pal::char_t* name, pal::string_t& recv)
|
||||
bool pal::getenv(const pal::char_t* name, pal::string_t* recv)
|
||||
{
|
||||
auto result = ::getenv(name);
|
||||
if (result != nullptr)
|
||||
{
|
||||
recv.assign(result);
|
||||
recv->assign(result);
|
||||
}
|
||||
|
||||
// We don't return false. Windows does have a concept of an error reading the variable,
|
||||
|
@ -128,10 +130,10 @@ bool pal::getenv(const pal::char_t* name, pal::string_t& recv)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool pal::realpath(pal::string_t& path)
|
||||
bool pal::realpath(pal::string_t* path)
|
||||
{
|
||||
pal::char_t buf[PATH_MAX];
|
||||
auto resolved = ::realpath(path.c_str(), buf);
|
||||
auto resolved = ::realpath(path->c_str(), buf);
|
||||
if (resolved == nullptr)
|
||||
{
|
||||
if (errno == ENOENT)
|
||||
|
@ -141,19 +143,25 @@ bool pal::realpath(pal::string_t& path)
|
|||
perror("realpath()");
|
||||
return false;
|
||||
}
|
||||
path.assign(resolved);
|
||||
path->assign(resolved);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool pal::file_exists(const pal::string_t& path)
|
||||
{
|
||||
if (path.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
struct stat buffer;
|
||||
return (::stat(path.c_str(), &buffer) == 0);
|
||||
}
|
||||
|
||||
std::vector<pal::string_t> pal::readdir(const pal::string_t& path)
|
||||
void pal::readdir(const pal::string_t& path, std::vector<pal::string_t>* list)
|
||||
{
|
||||
std::vector<pal::string_t> files;
|
||||
assert(list != nullptr);
|
||||
|
||||
std::vector<pal::string_t>& files = *list;
|
||||
|
||||
auto dir = opendir(path.c_str());
|
||||
if (dir != nullptr)
|
||||
|
@ -197,6 +205,4 @@ std::vector<pal::string_t> pal::readdir(const pal::string_t& path)
|
|||
files.push_back(pal::string_t(entry->d_name));
|
||||
}
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
|
|
@ -5,23 +5,24 @@
|
|||
#include "trace.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <locale>
|
||||
#include <codecvt>
|
||||
|
||||
static std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> g_converter;
|
||||
|
||||
bool pal::find_coreclr(pal::string_t& recv)
|
||||
bool pal::find_coreclr(pal::string_t* recv)
|
||||
{
|
||||
pal::string_t candidate;
|
||||
pal::string_t test;
|
||||
|
||||
// Try %LocalAppData%\dotnet
|
||||
if (pal::getenv(_X("LocalAppData"), candidate)) {
|
||||
append_path(candidate, _X("dotnet"));
|
||||
append_path(candidate, _X("runtime"));
|
||||
append_path(candidate, _X("coreclr"));
|
||||
if (pal::getenv(_X("LocalAppData"), &candidate)) {
|
||||
append_path(&candidate, _X("dotnet"));
|
||||
append_path(&candidate, _X("runtime"));
|
||||
append_path(&candidate, _X("coreclr"));
|
||||
if (coreclr_exists_in_dir(candidate)) {
|
||||
recv.assign(candidate);
|
||||
recv->assign(candidate);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -30,12 +31,12 @@ bool pal::find_coreclr(pal::string_t& recv)
|
|||
return false;
|
||||
}
|
||||
|
||||
bool pal::load_library(const char_t* path, dll_t& dll)
|
||||
bool pal::load_library(const char_t* path, dll_t* dll)
|
||||
{
|
||||
dll = ::LoadLibraryW(path);
|
||||
if (dll == nullptr)
|
||||
*dll = ::LoadLibraryW(path);
|
||||
if (*dll == nullptr)
|
||||
{
|
||||
trace::error(_X("failed to load coreclr.dll from %s, HRESULT: 0x%X"), path, HRESULT_FROM_WIN32(GetLastError()));
|
||||
trace::error(_X("Failed to load coreclr.dll from %s, HRESULT: 0x%X"), path, HRESULT_FROM_WIN32(GetLastError()));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -43,15 +44,15 @@ bool pal::load_library(const char_t* path, dll_t& dll)
|
|||
HMODULE dummy_module;
|
||||
if (!::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN, path, &dummy_module))
|
||||
{
|
||||
trace::error(_X("failed to pin library: %s"));
|
||||
trace::error(_X("Failed to pin library: %s"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (trace::is_enabled())
|
||||
{
|
||||
pal::char_t buf[PATH_MAX];
|
||||
::GetModuleFileNameW(dll, buf, PATH_MAX);
|
||||
trace::info(_X("loaded library from %s"), buf);
|
||||
::GetModuleFileNameW(*dll, buf, PATH_MAX);
|
||||
trace::info(_X("Loaded library from %s"), buf);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -67,14 +68,15 @@ void pal::unload_library(dll_t library)
|
|||
// No-op. On windows, we pin the library, so it can't be unloaded.
|
||||
}
|
||||
|
||||
bool pal::get_default_packages_directory(string_t& recv)
|
||||
bool pal::get_default_packages_directory(string_t* recv)
|
||||
{
|
||||
recv->clear();
|
||||
if (!pal::getenv(_X("USERPROFILE"), recv))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
append_path(recv, _X(".dnx"));
|
||||
append_path(recv, _X("packages"));
|
||||
append_path(&*recv, _X(".dnx"));
|
||||
append_path(&*recv, _X("packages"));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -83,7 +85,7 @@ bool pal::is_path_rooted(const string_t& path)
|
|||
return path.length() >= 2 && path[1] == L':';
|
||||
}
|
||||
|
||||
bool pal::getenv(const char_t* name, string_t& recv)
|
||||
bool pal::getenv(const char_t* name, string_t* recv)
|
||||
{
|
||||
auto length = ::GetEnvironmentVariableW(name, nullptr, 0);
|
||||
if (length == 0)
|
||||
|
@ -94,17 +96,17 @@ bool pal::getenv(const char_t* name, string_t& recv)
|
|||
// Leave the receiver empty and return success
|
||||
return true;
|
||||
}
|
||||
trace::error(_X("failed to read enviroment variable '%s', HRESULT: 0x%X"), name, HRESULT_FROM_WIN32(GetLastError()));
|
||||
trace::error(_X("Failed to read enviroment variable '%s', HRESULT: 0x%X"), name, HRESULT_FROM_WIN32(GetLastError()));
|
||||
return false;
|
||||
}
|
||||
auto buf = new char_t[length];
|
||||
if (::GetEnvironmentVariableW(name, buf, length) == 0)
|
||||
{
|
||||
trace::error(_X("failed to read enviroment variable '%s', HRESULT: 0x%X"), name, HRESULT_FROM_WIN32(GetLastError()));
|
||||
trace::error(_X("Failed to read enviroment variable '%s', HRESULT: 0x%X"), name, HRESULT_FROM_WIN32(GetLastError()));
|
||||
return false;
|
||||
}
|
||||
|
||||
recv.assign(buf);
|
||||
recv->assign(buf);
|
||||
delete[] buf;
|
||||
|
||||
return true;
|
||||
|
@ -115,14 +117,14 @@ int pal::xtoi(const char_t* input)
|
|||
return ::_wtoi(input);
|
||||
}
|
||||
|
||||
bool pal::get_own_executable_path(string_t& recv)
|
||||
bool pal::get_own_executable_path(string_t* recv)
|
||||
{
|
||||
char_t program_path[MAX_PATH];
|
||||
DWORD dwModuleFileName = ::GetModuleFileNameW(NULL, program_path, MAX_PATH);
|
||||
if (dwModuleFileName == 0 || dwModuleFileName >= MAX_PATH) {
|
||||
return false;
|
||||
}
|
||||
recv.assign(program_path);
|
||||
recv->assign(program_path);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -136,21 +138,36 @@ pal::string_t pal::to_palstring(const std::string& str)
|
|||
return g_converter.from_bytes(str);
|
||||
}
|
||||
|
||||
bool pal::realpath(string_t& path)
|
||||
void pal::to_palstring(const char* str, pal::string_t* out)
|
||||
{
|
||||
out->assign(g_converter.from_bytes(str));
|
||||
}
|
||||
|
||||
void pal::to_stdstring(const pal::char_t* str, std::string* out)
|
||||
{
|
||||
out->assign(g_converter.to_bytes(str));
|
||||
}
|
||||
|
||||
bool pal::realpath(string_t* path)
|
||||
{
|
||||
char_t buf[MAX_PATH];
|
||||
auto res = ::GetFullPathNameW(path.c_str(), MAX_PATH, buf, nullptr);
|
||||
auto res = ::GetFullPathNameW(path->c_str(), MAX_PATH, buf, nullptr);
|
||||
if (res == 0 || res > MAX_PATH)
|
||||
{
|
||||
trace::error(_X("error resolving path: %s"), path.c_str());
|
||||
trace::error(_X("Error resolving path: %s"), path->c_str());
|
||||
return false;
|
||||
}
|
||||
path.assign(buf);
|
||||
path->assign(buf);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool pal::file_exists(const string_t& path)
|
||||
{
|
||||
if (path.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
WIN32_FIND_DATAW data;
|
||||
auto find_handle = ::FindFirstFileW(path.c_str(), &data);
|
||||
bool found = find_handle != INVALID_HANDLE_VALUE;
|
||||
|
@ -158,9 +175,11 @@ bool pal::file_exists(const string_t& path)
|
|||
return found;
|
||||
}
|
||||
|
||||
std::vector<pal::string_t> pal::readdir(const string_t& path)
|
||||
void pal::readdir(const string_t& path, std::vector<pal::string_t>* list)
|
||||
{
|
||||
std::vector<string_t> files;
|
||||
assert(list != nullptr);
|
||||
|
||||
std::vector<string_t>& files = *list;
|
||||
|
||||
string_t search_string(path);
|
||||
search_string.push_back(DIR_SEPARATOR);
|
||||
|
@ -174,6 +193,4 @@ std::vector<pal::string_t> pal::readdir(const string_t& path)
|
|||
files.push_back(filepath);
|
||||
} while (::FindNextFileW(handle, &data));
|
||||
::FindClose(handle);
|
||||
|
||||
return files;
|
||||
}
|
||||
|
|
128
src/corehost/src/servicing_index.cpp
Normal file
128
src/corehost/src/servicing_index.cpp
Normal file
|
@ -0,0 +1,128 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#include "trace.h"
|
||||
#include "servicing_index.h"
|
||||
|
||||
static const pal::char_t* DOTNET_SERVICING_INDEX_TXT = _X("dotnet_servicing_index.txt");
|
||||
|
||||
servicing_index_t::servicing_index_t(const pal::string_t& svc_dir)
|
||||
{
|
||||
m_patch_root = svc_dir;
|
||||
if (!m_patch_root.empty())
|
||||
{
|
||||
m_index_file.assign(m_patch_root);
|
||||
append_path(&m_index_file, DOTNET_SERVICING_INDEX_TXT);
|
||||
}
|
||||
m_parsed = m_index_file.empty() || !pal::file_exists(m_index_file);
|
||||
}
|
||||
|
||||
bool servicing_index_t::find_redirection(
|
||||
const pal::string_t& package_name,
|
||||
const pal::string_t& package_version,
|
||||
const pal::string_t& package_relative,
|
||||
pal::string_t* redirection)
|
||||
{
|
||||
ensure_redirections();
|
||||
|
||||
redirection->clear();
|
||||
|
||||
if (m_redirections.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
pal::stringstream_t stream;
|
||||
stream << package_name << _X("|") << package_version << _X("|") << package_relative;
|
||||
|
||||
auto iter = m_redirections.find(stream.str());
|
||||
if (iter != m_redirections.end())
|
||||
{
|
||||
pal::string_t full_path = m_patch_root;
|
||||
append_path(&full_path, iter->second.c_str());
|
||||
if (pal::file_exists(full_path))
|
||||
{
|
||||
*redirection = full_path;
|
||||
trace::verbose(_X("Servicing %s with %s"), stream.str().c_str(), redirection->c_str());
|
||||
return true;
|
||||
}
|
||||
trace::verbose(_X("Serviced file %s doesn't exist"), full_path.c_str());
|
||||
}
|
||||
|
||||
trace::verbose(_X("Entry %s not serviced or file doesn't exist"), stream.str().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
void servicing_index_t::ensure_redirections()
|
||||
{
|
||||
if (m_parsed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
pal::ifstream_t fstream(m_index_file);
|
||||
if (!fstream.good())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
pal::stringstream_t sstream;
|
||||
std::string line;
|
||||
while (std::getline(fstream, line))
|
||||
{
|
||||
pal::string_t str;
|
||||
pal::to_palstring(line.c_str(), &str);
|
||||
|
||||
// Can interpret line as "package"?
|
||||
pal::string_t prefix = _X("package|");
|
||||
if (str.find(prefix) != 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
pal::string_t name, version, relative;
|
||||
pal::string_t* tokens[] = { &name, &version, &relative };
|
||||
pal::string_t delim[] = { pal::string_t(_X("|")), pal::string_t(_X("|")), pal::string_t(_X("=")) };
|
||||
|
||||
bool bad_line = false;
|
||||
|
||||
size_t from = prefix.length();
|
||||
for (size_t cur = 0; cur < (sizeof(delim) / sizeof(delim[0])); ++cur)
|
||||
{
|
||||
size_t pos = str.find(delim[cur], from);
|
||||
if (pos == pal::string_t::npos)
|
||||
{
|
||||
bad_line = true;
|
||||
break;
|
||||
}
|
||||
|
||||
tokens[cur]->assign(str.substr(from, pos - from));
|
||||
from = pos + 1;
|
||||
}
|
||||
|
||||
if (bad_line)
|
||||
{
|
||||
trace::error(_X("Invalid line in servicing index. Skipping..."));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Save redirection for this package.
|
||||
sstream.str(_X(""));
|
||||
sstream << name << _X("|") << version << _X("|") << relative;
|
||||
|
||||
if (trace::is_enabled())
|
||||
{
|
||||
trace::verbose(_X("Adding servicing entry %s => %s"), sstream.str().c_str(), str.substr(from).c_str());
|
||||
}
|
||||
|
||||
// Store just the filename.
|
||||
pal::string_t redir = str.substr(from);
|
||||
if (_X('/') != DIR_SEPARATOR)
|
||||
{
|
||||
replace_char(&redir, _X('/'), DIR_SEPARATOR);
|
||||
}
|
||||
m_redirections.emplace(sstream.str(), redir);
|
||||
}
|
||||
|
||||
m_parsed = true;
|
||||
}
|
|
@ -1,251 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#include <set>
|
||||
#include <functional>
|
||||
|
||||
#include "trace.h"
|
||||
#include "tpafile.h"
|
||||
#include "utils.h"
|
||||
|
||||
bool read_field(pal::string_t line, int& offset, pal::string_t& value_recv)
|
||||
{
|
||||
// The first character should be a '"'
|
||||
if (line[offset] != '"')
|
||||
{
|
||||
trace::error(_X("error reading TPA file"));
|
||||
return false;
|
||||
}
|
||||
offset++;
|
||||
|
||||
// Set up destination buffer (it can't be bigger than the original line)
|
||||
pal::char_t buf[PATH_MAX];
|
||||
auto buf_offset = 0;
|
||||
|
||||
// Iterate through characters in the string
|
||||
for (; offset < line.length(); offset++)
|
||||
{
|
||||
// Is this a '\'?
|
||||
if (line[offset] == '\\')
|
||||
{
|
||||
// Skip this character and read the next character into the buffer
|
||||
offset++;
|
||||
buf[buf_offset] = line[offset];
|
||||
}
|
||||
// Is this a '"'?
|
||||
else if (line[offset] == '\"')
|
||||
{
|
||||
// Done! Advance to the pointer after the input
|
||||
offset++;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Take the character
|
||||
buf[buf_offset] = line[offset];
|
||||
}
|
||||
buf_offset++;
|
||||
}
|
||||
buf[buf_offset] = '\0';
|
||||
value_recv.assign(buf);
|
||||
|
||||
// Consume the ',' if we have one
|
||||
if (line[offset] == ',')
|
||||
{
|
||||
offset++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tpafile::load(pal::string_t path)
|
||||
{
|
||||
// Check if the file exists, if not, there is nothing to add
|
||||
if (!pal::file_exists(path))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Open the file
|
||||
pal::ifstream_t file(path);
|
||||
if (!file.good())
|
||||
{
|
||||
// Failed to open the file!
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read lines from the file
|
||||
while (true)
|
||||
{
|
||||
std::string line;
|
||||
std::getline(file, line);
|
||||
auto line_palstr = pal::to_palstring(line);
|
||||
if (file.eof())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
auto offset = 0;
|
||||
|
||||
tpaentry_t entry;
|
||||
|
||||
// Read fields
|
||||
if (!(read_field(line_palstr, offset, entry.library_type))) return false;
|
||||
if (!(read_field(line_palstr, offset, entry.library_name))) return false;
|
||||
if (!(read_field(line_palstr, offset, entry.library_version))) return false;
|
||||
if (!(read_field(line_palstr, offset, entry.library_hash))) return false;
|
||||
if (!(read_field(line_palstr, offset, entry.asset_type))) return false;
|
||||
if (!(read_field(line_palstr, offset, entry.asset_name))) return false;
|
||||
if (!(read_field(line_palstr, offset, entry.relative_path))) return false;
|
||||
|
||||
m_entries.push_back(entry);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void tpafile::add_from_local_dir(const pal::string_t& dir)
|
||||
{
|
||||
trace::verbose(_X("adding files from %s to TPA"), dir.c_str());
|
||||
const pal::char_t * const tpa_extensions[] = {
|
||||
_X(".ni.dll"), // Probe for .ni.dll first so that it's preferred if ni and il coexist in the same dir
|
||||
_X(".dll"),
|
||||
_X(".ni.exe"),
|
||||
_X(".exe"),
|
||||
};
|
||||
|
||||
std::set<pal::string_t> added_assemblies;
|
||||
|
||||
// Get directory entries
|
||||
auto files = pal::readdir(dir);
|
||||
for (auto ext : tpa_extensions)
|
||||
{
|
||||
auto len = pal::strlen(ext);
|
||||
for (auto file : files)
|
||||
{
|
||||
// Can't be a match if it's the same length as the extension :)
|
||||
if (file.length() > len)
|
||||
{
|
||||
// Extract the same amount of text from the end of file name
|
||||
auto file_ext = file.substr(file.length() - len, len);
|
||||
|
||||
// Check if this file name matches
|
||||
if (pal::strcasecmp(ext, file_ext.c_str()) == 0)
|
||||
{
|
||||
// Get the assembly name by stripping the extension
|
||||
// and add it to the set so we can de-dupe
|
||||
auto asm_name = file.substr(0, file.length() - len);
|
||||
|
||||
// TODO(anurse): Also check if already in TPA file
|
||||
if (added_assemblies.find(asm_name) == added_assemblies.end())
|
||||
{
|
||||
added_assemblies.insert(asm_name);
|
||||
|
||||
tpaentry_t entry;
|
||||
entry.asset_type = pal::string_t(_X("runtime"));
|
||||
entry.library_name = pal::string_t(asm_name);
|
||||
entry.library_version = pal::string_t(_X(""));
|
||||
|
||||
pal::string_t relpath(dir);
|
||||
relpath.push_back(DIR_SEPARATOR);
|
||||
relpath.append(file);
|
||||
entry.relative_path = relpath;
|
||||
entry.asset_name = asm_name;
|
||||
|
||||
trace::verbose(_X("adding %s to TPA list from %s"), asm_name.c_str(), relpath.c_str());
|
||||
m_entries.push_back(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void tpafile::write_tpa_list(pal::string_t& output)
|
||||
{
|
||||
std::set<pal::string_t> items;
|
||||
for (auto entry : m_entries)
|
||||
{
|
||||
if (pal::strcmp(entry.asset_type.c_str(), _X("runtime")) == 0 && items.find(entry.asset_name) == items.end())
|
||||
{
|
||||
// Resolve the full path
|
||||
for (auto search_path : m_package_search_paths)
|
||||
{
|
||||
pal::string_t candidate;
|
||||
candidate.reserve(search_path.length() +
|
||||
entry.library_name.length() +
|
||||
entry.library_version.length() +
|
||||
entry.relative_path.length() + 3);
|
||||
candidate.append(search_path);
|
||||
|
||||
append_path(candidate, entry.library_name.c_str());
|
||||
append_path(candidate, entry.library_version.c_str());
|
||||
append_path(candidate, entry.relative_path.c_str());
|
||||
if (pal::file_exists(candidate))
|
||||
{
|
||||
trace::verbose(_X("adding tpa entry: %s"), candidate.c_str());
|
||||
|
||||
output.append(candidate);
|
||||
output.push_back(PATH_SEPARATOR);
|
||||
items.insert(entry.asset_name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void tpafile::write_native_paths(pal::string_t& output)
|
||||
{
|
||||
std::set<pal::string_t> items;
|
||||
for (auto search_path : m_native_search_paths)
|
||||
{
|
||||
if (items.find(search_path) == items.end())
|
||||
{
|
||||
trace::verbose(_X("adding native search path: %s"), search_path.c_str());
|
||||
output.append(search_path);
|
||||
output.push_back(PATH_SEPARATOR);
|
||||
items.insert(search_path);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto entry : m_entries)
|
||||
{
|
||||
auto dir = entry.relative_path.substr(0, entry.relative_path.find_last_of(DIR_SEPARATOR));
|
||||
if (pal::strcmp(entry.asset_type.c_str(), _X("native")) == 0 && items.find(dir) == items.end())
|
||||
{
|
||||
// Resolve the full path
|
||||
for (auto search_path : m_package_search_paths)
|
||||
{
|
||||
pal::string_t candidate;
|
||||
candidate.reserve(search_path.length() +
|
||||
entry.library_name.length() +
|
||||
entry.library_version.length() +
|
||||
dir.length() + 3);
|
||||
candidate.append(search_path);
|
||||
|
||||
append_path(candidate, entry.library_name.c_str());
|
||||
append_path(candidate, entry.library_version.c_str());
|
||||
append_path(candidate, get_directory(entry.relative_path).c_str());
|
||||
|
||||
if (pal::file_exists(candidate))
|
||||
{
|
||||
trace::verbose(_X("adding native search path: %s"), candidate.c_str());
|
||||
output.append(candidate);
|
||||
output.push_back(PATH_SEPARATOR);
|
||||
items.insert(dir);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void tpafile::add_package_dir(pal::string_t dir)
|
||||
{
|
||||
m_package_search_paths.push_back(dir);
|
||||
}
|
||||
|
||||
void tpafile::add_native_search_path(pal::string_t dir)
|
||||
{
|
||||
m_native_search_paths.push_back(dir);
|
||||
}
|
|
@ -7,7 +7,7 @@
|
|||
bool coreclr_exists_in_dir(const pal::string_t& candidate)
|
||||
{
|
||||
pal::string_t test(candidate);
|
||||
append_path(test, LIBCORECLR_NAME);
|
||||
append_path(&test, LIBCORECLR_NAME);
|
||||
trace::verbose(_X("checking for CoreCLR in default location: %s"), test.c_str());
|
||||
return pal::file_exists(test);
|
||||
}
|
||||
|
@ -18,19 +18,19 @@ bool ends_with(const pal::string_t& value, const pal::string_t& suffix)
|
|||
(0 == value.compare(value.length() - suffix.length(), suffix.length(), suffix));
|
||||
}
|
||||
|
||||
void append_path(pal::string_t& path1, const pal::char_t* path2)
|
||||
void append_path(pal::string_t* path1, const pal::char_t* path2)
|
||||
{
|
||||
if (pal::is_path_rooted(path2))
|
||||
{
|
||||
path1.assign(path2);
|
||||
path1->assign(path2);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (path1.back() != DIR_SEPARATOR)
|
||||
if (path1->empty() || path1->back() != DIR_SEPARATOR)
|
||||
{
|
||||
path1.push_back(DIR_SEPARATOR);
|
||||
path1->push_back(DIR_SEPARATOR);
|
||||
}
|
||||
path1.append(path2);
|
||||
path1->append(path2);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -70,3 +70,12 @@ pal::string_t get_directory(const pal::string_t& path)
|
|||
|
||||
return path.substr(0, path_sep);
|
||||
}
|
||||
|
||||
void replace_char(pal::string_t* path, pal::char_t match, pal::char_t repl)
|
||||
{
|
||||
int pos = 0;
|
||||
while ((pos = path->find(match, pos)) != pal::string_t::npos)
|
||||
{
|
||||
(*path)[pos] = repl;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue