From b16498671f82f3af1ae3c765fa64369753f6e08f Mon Sep 17 00:00:00 2001 From: Tobbe Gyllebring Date: Tue, 13 Aug 2019 17:04:41 +0200 Subject: [PATCH] XmlLogger now correctly closes its stream and the worker trampolines the config as approprite. --- Build.ps1 | 2 +- Cone.nuspec | 4 +- Run-Tests.bat | 2 +- Source/Cone.Build/Cone.Build.csproj | 2 +- .../Cone.TestAdapter/Cone.TestAdapter.csproj | 4 +- Source/Cone.Worker/Cone.Worker.csproj | 4 +- Source/Cone.Worker/Program.cs | 4 +- Source/Cone/Cone.csproj | 3 +- Source/Cone/Core/AssemblyLoader.cs | 67 +++++-- Source/Cone/Core/Invokable.cs | 174 +++++++++--------- Source/Cone/Runners/ConesoleRunner.cs | 2 +- Source/Cone/Runners/XmlLogger.cs | 4 +- Source/Cone/Runtime/AssemblyDepsJson.cs | 39 ++-- Source/Conesole/Conesole.csproj | 4 +- Source/Version.cs | 4 +- Source/dotnet-conesole/Program.cs | 7 +- Source/dotnet-conesole/dotnet-conesole.csproj | 2 +- Specs/Cone.Specs/Cone.Specs.csproj | 6 +- dotnet-conesole.nuspec | 2 + 19 files changed, 195 insertions(+), 141 deletions(-) diff --git a/Build.ps1 b/Build.ps1 index 671d29e..059f425 100644 --- a/Build.ps1 +++ b/Build.ps1 @@ -1,7 +1,7 @@ dotnet restore Cone.sln dotnet msbuild /p:Configuration=Release -$Version = [System.Reflection.AssemblyName]::GetAssemblyName("Build\Cone\Release\net45\Cone.dll").Version.ToString(3) +$Version = [System.Reflection.AssemblyName]::GetAssemblyName("Build\Cone\Release\net452\Cone.dll").Version.ToString(3) Write-Host Packing Version $Version Tools\nuget pack Cone.nuspec -Properties Configuration=Release -Version $Version -OutputDirectory Build diff --git a/Cone.nuspec b/Cone.nuspec index c28c87b..28206f6 100644 --- a/Cone.nuspec +++ b/Cone.nuspec @@ -23,8 +23,10 @@ Say no to FluentCobol, say hello to Check.That(() => actual == expected); - + + diff --git a/Run-Tests.bat b/Run-Tests.bat index 94e3240..caaf3ff 100644 --- a/Run-Tests.bat +++ b/Run-Tests.bat @@ -1,4 +1,4 @@ @echo off pushd Specs\Cone.Specs -dotnet ..\..\Build\dotnet-conesole\Debug\netcoreapp2.0\dotnet-conesole.dll +dotnet ..\..\Build\dotnet-conesole\Debug\netcoreapp2.2\dotnet-conesole.dll popd \ No newline at end of file diff --git a/Source/Cone.Build/Cone.Build.csproj b/Source/Cone.Build/Cone.Build.csproj index 2bab9d6..35b651d 100644 --- a/Source/Cone.Build/Cone.Build.csproj +++ b/Source/Cone.Build/Cone.Build.csproj @@ -10,7 +10,7 @@ Properties Cone.Build Cone.Build - v4.5 + v4.6.1 512 $(SolutionDir) $(BuildPath)Bin\$(MSBuildProjectName) diff --git a/Source/Cone.TestAdapter/Cone.TestAdapter.csproj b/Source/Cone.TestAdapter/Cone.TestAdapter.csproj index 81ff0fd..ffd8fed 100644 --- a/Source/Cone.TestAdapter/Cone.TestAdapter.csproj +++ b/Source/Cone.TestAdapter/Cone.TestAdapter.csproj @@ -1,13 +1,13 @@  - net452 + net452;net462;net472 - + diff --git a/Source/Cone.Worker/Cone.Worker.csproj b/Source/Cone.Worker/Cone.Worker.csproj index 426d2bd..5cc1bba 100644 --- a/Source/Cone.Worker/Cone.Worker.csproj +++ b/Source/Cone.Worker/Cone.Worker.csproj @@ -2,7 +2,9 @@ Exe - net45;net462;netcoreapp2.0;netcoreapp2.1;netcoreapp2.2 + net452;net462;net472;netcoreapp2.0;netcoreapp2.2 + NETFX + $(DefineConstants);$(TargetFrameworkFamily) diff --git a/Source/Cone.Worker/Program.cs b/Source/Cone.Worker/Program.cs index 3decbce..81b1486 100644 --- a/Source/Cone.Worker/Program.cs +++ b/Source/Cone.Worker/Program.cs @@ -28,7 +28,7 @@ static int Main(string[] args) static string GetConePath(string workingDir) => Path.Combine(workingDir, "Cone.dll"); static bool RanInTestDomain(string target, string workingDir, string[] args, out int result) { -#if NET45 || NET462 +#if NETFX var targetConfig = target + ".config"; if(!File.Exists(targetConfig) || !AppDomain.CurrentDomain.IsDefaultAppDomain()) { result = 0; @@ -46,7 +46,7 @@ static int Main(string[] args) new System.Security.PermissionSet(System.Security.Permissions.PermissionState.Unrestricted)); result = testDomain.ExecuteAssembly(new Uri(typeof(Program).Assembly.CodeBase).LocalPath, args); return true; -#elif NETCOREAPP2_0 || NETCOREAPP2_1 || NETCOREAPP2_2 +#else result = 0; return false; #endif diff --git a/Source/Cone/Cone.csproj b/Source/Cone/Cone.csproj index 3dba8a7..161308e 100644 --- a/Source/Cone/Cone.csproj +++ b/Source/Cone/Cone.csproj @@ -1,7 +1,7 @@  - net45;netstandard2.0 + net452;net462;net472;netstandard2.0 NETSTANDARD NETFX $(DefineConstants);$(TargetFrameworkFamily) @@ -11,7 +11,6 @@ - true diff --git a/Source/Cone/Core/AssemblyLoader.cs b/Source/Cone/Core/AssemblyLoader.cs index bbd6a33..94bee00 100644 --- a/Source/Cone/Core/AssemblyLoader.cs +++ b/Source/Cone/Core/AssemblyLoader.cs @@ -6,6 +6,7 @@ namespace Cone.Core { + using System.Diagnostics; using System.IO; #if NETFX static class AssemblyLoader @@ -23,28 +24,28 @@ static class AssemblyLoader public class ConeLoadContext : AssemblyLoadContext { - readonly TextWriter LoadingLog = TextWriter.Null; + public TextWriter LoadingLog = TextWriter.Null; readonly Dictionary knownAssemblies = new Dictionary(); - public Assembly ResolveKnownAssembly(AssemblyLoadContext parent, AssemblyName assemblyName) { - LoadingLog?.WriteLine($"Resolving {assemblyName}"); - knownAssemblies.TryGetValue(assemblyName, out var found); - return found; + public Assembly ResolveKnownAssembly(AssemblyLoadContext _, AssemblyName assemblyName) { + if(knownAssemblies.TryGetValue(assemblyName, out var found)) + return found; + return null; } public void Load(DependencyItem dep) { + LoadingLog.WriteLine($"-> {dep.Path}"); if(dep.IsManaged) { try { - var loaded = Default.LoadFromAssemblyPath(dep.Path); - knownAssemblies.Add(loaded.GetName(), loaded); - } catch { - LoadingLog?.WriteLine($"Ooops when loading {dep.Path}"); + Add(Default.LoadFromAssemblyPath(dep.Path)); + } catch(Exception ex) { + LoadingLog?.WriteLine($"!!! Managed Load Failure {dep.Path}: {ex}"); } } else try { LoadUnmanagedDllFromPath(dep.Path); - }catch { - LoadingLog?.WriteLine($"Ooops when loading {dep.Path}"); + }catch(Exception ex) { + LoadingLog?.WriteLine($"!!! Native Load Failure {dep.Path}: {ex}"); } } @@ -69,14 +70,18 @@ public static class AssemblyLoader public static void InitDeps(string mainAssembly) { var deps = AssemblyJsonDeps.LoadFrom(mainAssembly); + var os = Environment.GetEnvironmentVariable("CONE_RUNTIME_OS") ?? GetOs(); var runtime = new RuntimeInfo { - OS = Environment.GetEnvironmentVariable("CONE_RUNTIME_OS") ?? (string)typeof(RuntimeEnvironment).GetMethod("GetRIDOS", BindingFlags.NonPublic | BindingFlags.Static).Invoke(null, null), + OS = os, Arch = RuntimeEnvironment.RuntimeArchitecture, //FrameworkVersion = "netstandard2.0", FrameworkVersion = Environment.GetEnvironmentVariable("CONE_TARGET_FRAMEWORK"), - PackageProbePaths = AppContext.GetData("PROBING_DIRECTORIES").ToString().Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries), + ApplicationBase = Path.GetDirectoryName(mainAssembly), + PackageProbePaths = GetProbePaths(os), }; - LoadContext = new ConeLoadContext(); + //foreach(var path in runtime.PackageProbePaths) + // Console.WriteLine($"Probing: {path}"); + LoadContext = new ConeLoadContext { LoadingLog = TextWriter.Null }; var seen = new HashSet(); foreach(var dep in deps.GetRuntimeDependencies() .SelectMany(x => x.GetLoadOrder()) @@ -87,6 +92,40 @@ public static class AssemblyLoader AssemblyLoadContext.Default.Resolving += LoadContext.ResolveKnownAssembly; } + + static string GetOs() { + switch(RuntimeEnvironment.OperatingSystemPlatform) { + case Platform.Windows: return "win"; + case Platform.Linux: return "linux"; + case Platform.Darwin: return "osx"; + default: throw new NotSupportedException(); + } + } + + static string[] GetProbePaths(string os) + { + var probeDirs = new HashSet( + AppContext.GetData("PROBING_DIRECTORIES") + .ToString() + .Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)); + + + var packageDirectory = Environment.GetEnvironmentVariable("NUGET_PACKAGES"); + if(!string.IsNullOrEmpty(packageDirectory)) + probeDirs.Add(packageDirectory); + + string basePath; + if (os == "win") { + basePath = Environment.GetEnvironmentVariable("USERPROFILE"); + } + else { + basePath = Environment.GetEnvironmentVariable("HOME"); + } + if(basePath != null) + probeDirs.Add(Path.Combine(basePath, ".nuget", "packages")); + + return probeDirs.ToArray(); + } } #endif } diff --git a/Source/Cone/Core/Invokable.cs b/Source/Cone/Core/Invokable.cs index 37047a5..57f08b2 100644 --- a/Source/Cone/Core/Invokable.cs +++ b/Source/Cone/Core/Invokable.cs @@ -1,92 +1,92 @@ -using System; -using System.Collections.Concurrent; -using System.Linq.Expressions; -using System.Reflection; -using System.Threading.Tasks; -using System.Linq; - -namespace Cone.Core -{ - public struct Invokable : ICustomAttributeProvider - { - static Func TaskAwait = obj => { - var awaiter = ((Task)obj).GetAwaiter(); - awaiter.GetResult(); - return null; - }; - - static readonly ConcurrentDictionary> awaiterCache = new ConcurrentDictionary>(); - - readonly MethodInfo method; - readonly Func awaitAction; - - public Invokable(MethodInfo method) { - this.method = method; - this.awaitAction = GetAwaiterOrDefault(method.ReturnType); +using System; +using System.Collections.Concurrent; +using System.Linq.Expressions; +using System.Reflection; +using System.Threading.Tasks; +using System.Linq; + +namespace Cone.Core +{ + public struct Invokable : ICustomAttributeProvider + { + static Func TaskAwait = obj => { + var awaiter = ((Task)obj).GetAwaiter(); + awaiter.GetResult(); + return null; + }; + + static readonly ConcurrentDictionary> awaiterCache = new ConcurrentDictionary>(); + + readonly MethodInfo method; + readonly Func awaitAction; + + public Invokable(MethodInfo method) { + this.method = method; + this.awaitAction = GetAwaiterOrDefault(method.ReturnType); } public bool IsAsync => GetCustomAttributes(true).Any(x => x.GetType().FullName == "System.Runtime.CompilerServices.AsyncStateMachineAttribute"); public string Location => $"{method.ReturnType} {method.DeclaringType}.{method.Name}({method.GetParameters().Select(x => x.ToString()).Join(", ")})"; - - internal MethodInfo Target => method; - public Type ReturnType => method.ReturnType; - public Type DeclaringType => method.DeclaringType; - public string Name => method.Name; - public bool IsWaitable => awaitAction != null; - public bool IsStatic => method.IsStatic; - - public object[] GetCustomAttributes(bool inherit) => - method.GetCustomAttributes(inherit); - - public object[] GetCustomAttributes(Type attributeType, bool inherit) => - method.GetCustomAttributes(attributeType, inherit); - - public bool IsDefined(Type attributeType, bool inherit) => - method.IsDefined(attributeType, inherit); - - public ParameterInfo[] GetParameters() => - method.GetParameters(); - - public object Invoke(object target, object[] args) => - method.Invoke(target, args); - - public object Await(object target, object[] args) { - var r = Invoke(target, args); - return IsWaitable ? awaitAction(r) : r; - } - - static Func GetAwaiterOrDefault(Type type) { - if(type == typeof(void)) - return null; - - if(type == typeof(Task)) - return TaskAwait; - - return awaiterCache.GetOrAdd(type, CreateAwaiterOrDefault); - } - - static Func CreateAwaiterOrDefault(Type type) { - var getAwaiter = type.GetMethod("GetAwaiter", Type.EmptyTypes); - if(getAwaiter != null) { - var getResult = getAwaiter.ReturnType.GetMethod("GetResult") ?? - throw new InvalidOperationException("Can't GetResult on " + getAwaiter.ReturnType); - - var awaitable = Expression.Parameter(typeof(object)); - var getUnboxedResult = Expression.Call( - Expression.Call( - Expression.Convert(awaitable, type), - getAwaiter), - getResult); - var body = getUnboxedResult.Type == typeof(void) - ? Expression.Block(getUnboxedResult, Expression.Constant(null)) - : getUnboxedResult.Box(); - return Expression.Lambda>(body, awaitable) - .Compile(); - } - return null; - } - - public static void Await(object obj) => - GetAwaiterOrDefault(obj.GetType())?.DynamicInvoke(obj); - } + + internal MethodInfo Target => method; + public Type ReturnType => method.ReturnType; + public Type DeclaringType => method.DeclaringType; + public string Name => method.Name; + public bool IsWaitable => awaitAction != null; + public bool IsStatic => method.IsStatic; + + public object[] GetCustomAttributes(bool inherit) => + method.GetCustomAttributes(inherit); + + public object[] GetCustomAttributes(Type attributeType, bool inherit) => + method.GetCustomAttributes(attributeType, inherit); + + public bool IsDefined(Type attributeType, bool inherit) => + method.IsDefined(attributeType, inherit); + + public ParameterInfo[] GetParameters() => + method.GetParameters(); + + public object Invoke(object target, object[] args) => + method.Invoke(target, args); + + public object Await(object target, object[] args) { + var r = Invoke(target, args); + return IsWaitable ? awaitAction(r) : r; + } + + static Func GetAwaiterOrDefault(Type type) { + if(type == typeof(void)) + return null; + + if(type == typeof(Task)) + return TaskAwait; + + return awaiterCache.GetOrAdd(type, CreateAwaiterOrDefault); + } + + static Func CreateAwaiterOrDefault(Type type) { + var getAwaiter = type.GetMethod("GetAwaiter", Type.EmptyTypes); + if(getAwaiter != null) { + var getResult = getAwaiter.ReturnType.GetMethod("GetResult") ?? + throw new InvalidOperationException("Can't GetResult on " + getAwaiter.ReturnType); + + var awaitable = Expression.Parameter(typeof(object)); + var getUnboxedResult = Expression.Call( + Expression.Call( + Expression.Convert(awaitable, type), + getAwaiter), + getResult); + var body = getUnboxedResult.Type == typeof(void) + ? Expression.Block(getUnboxedResult, Expression.Constant(null)) + : getUnboxedResult.Box(); + return Expression.Lambda>(body, awaitable) + .Compile(); + } + return null; + } + + public static void Await(object obj) => + GetAwaiterOrDefault(obj.GetType())?.DynamicInvoke(obj); + } } \ No newline at end of file diff --git a/Source/Cone/Runners/ConesoleRunner.cs b/Source/Cone/Runners/ConesoleRunner.cs index 319668f..67e40b4 100644 --- a/Source/Cone/Runners/ConesoleRunner.cs +++ b/Source/Cone/Runners/ConesoleRunner.cs @@ -3,7 +3,7 @@ using System.IO; using System.Linq; using System.Reflection; -#if NET45 +#if NETFX #else using System.Runtime.Loader; #endif diff --git a/Source/Cone/Runners/XmlLogger.cs b/Source/Cone/Runners/XmlLogger.cs index 12ba79b..3c2691d 100644 --- a/Source/Cone/Runners/XmlLogger.cs +++ b/Source/Cone/Runners/XmlLogger.cs @@ -101,9 +101,9 @@ class XmlTestLogger : ITestLogger xml.WriteAttributeString("assembly", new Uri(test.Assembly.Location).LocalPath); } + xml.WriteStartElement("failure"); xml.WriteAttributeString("type", failure.FailureType.ToString()); - xml.WriteAttributeString("context", failure.Context); xml.WriteAttributeString("file", failure.File); xml.WriteAttribute("line", failure.Line); xml.WriteAttribute("column", failure.Column); @@ -179,7 +179,7 @@ class XmlTestLogger : ITestLogger xml.WriteAttributeString("pending", summary.Pending.ToString()); xml.WriteAttributeString("skipped", summary.Skipped.ToString()); xml.WriteEndDocument(); - xml.Flush(); + xml.Close(); SessionEnded.Raise(this, EventArgs.Empty); } diff --git a/Source/Cone/Runtime/AssemblyDepsJson.cs b/Source/Cone/Runtime/AssemblyDepsJson.cs index 18252f5..1f59cf4 100644 --- a/Source/Cone/Runtime/AssemblyDepsJson.cs +++ b/Source/Cone/Runtime/AssemblyDepsJson.cs @@ -128,6 +128,7 @@ public class RuntimeInfo public DependencyResolutionItem ResolveDependency(RuntimeDependencyInfo dep) { var runtimeTargets = dep .RuntimeTarget + .Where(x => !x.Path.EndsWith("_._")) .Join(new[] { new { Rid = RID, SortOrder = 1 }, new { Rid = OS, SortOrder = 2 }, @@ -140,27 +141,33 @@ public class RuntimeInfo if(!loadTargets.Any()) return new DependencyResolutionItem { Name = dep.Name, Items = new DependencyItem[0] }; - var items = new DependencyItem[loadTargets.Count]; - var itemIndex = 0; + var items = new List(); foreach (var candidate in loadTargets) - if (string.IsNullOrEmpty(candidate.Path)) { - var managedPath = $@"{ApplicationBase}\{candidate.Path}"; + if (string.IsNullOrEmpty(dep.Path)) { + var managedPath = Path.Combine(ApplicationBase, candidate.Path); var found = File.Exists(managedPath); if (found) { - items[itemIndex++] = new DependencyItem(managedPath, candidate.IsManaged); + items.Add(new DependencyItem(managedPath, candidate.IsManaged)); } } - else - foreach (var probePath in PackageProbePaths) { - var managedPath = $@"{probePath}\{dep.Path}\{candidate.Path}"; - var found = File.Exists(managedPath); - if (found) { - items[itemIndex++] = new DependencyItem(managedPath, candidate.IsManaged); - break; -} - } - Array.Resize(ref items, itemIndex); - return new DependencyResolutionItem { Name = dep.Name, Items = items, }; + else if(TryResolve(dep, candidate, out var found)) { + items.Add(found); + } else { + //Console.Error.WriteLine($"Failed to bind {RID} \"{dep.Path}\", \"{candidate.Path}\" given \"{ApplicationBase}\",\"{string.Join(",\"", PackageProbePaths)}\""); + } + return new DependencyResolutionItem { Name = dep.Name, Items = items.ToArray(), }; + } + + bool TryResolve(RuntimeDependencyInfo dep, DependencyItem item, out DependencyItem found) { + foreach (var probePath in PackageProbePaths) { + var managedPath = Path.Combine(probePath, dep.Path, item.Path); + if (File.Exists(managedPath)) { + found = new DependencyItem(managedPath, item.IsManaged); + return true; + } + } + found = default(DependencyItem); + return false; } } diff --git a/Source/Conesole/Conesole.csproj b/Source/Conesole/Conesole.csproj index 2b18fbe..6547203 100644 --- a/Source/Conesole/Conesole.csproj +++ b/Source/Conesole/Conesole.csproj @@ -1,7 +1,9 @@  - net45 + net452 + NETFX + $(DefineConstants);$(TargetFrameworkFamily) Exe diff --git a/Source/Version.cs b/Source/Version.cs index c516301..62ac696 100644 --- a/Source/Version.cs +++ b/Source/Version.cs @@ -2,5 +2,5 @@ using System.Runtime.InteropServices; [assembly: ComVisible(false)] -[assembly: AssemblyVersion("2019.05.07")] -[assembly: AssemblyInformationalVersion("2019.05.07")] \ No newline at end of file +[assembly: AssemblyVersion("2019.08.13")] +[assembly: AssemblyInformationalVersion("2019.08.13")] \ No newline at end of file diff --git a/Source/dotnet-conesole/Program.cs b/Source/dotnet-conesole/Program.cs index 1a1e1e3..f053635 100644 --- a/Source/dotnet-conesole/Program.cs +++ b/Source/dotnet-conesole/Program.cs @@ -110,17 +110,18 @@ static int RunConesole(string fxVersion, IEnumerable args) case "net45": case "net451": case "net452": + return (Path.Combine(probePath, "net452", "Cone.Worker.exe"), false); case "net46": case "net461": - return (Path.Combine(probePath, "net45", "Cone.Worker.exe"), false); case "net462": + return (Path.Combine(probePath, "net462", "Cone.Worker.exe"), false); case "net47": case "net471": case "net472": - return (Path.Combine(probePath, "net462", "Cone.Worker.exe"), false); + return (Path.Combine(probePath, "net472", "Cone.Worker.exe"), false); case "netcoreapp2.0": case "netcoreapp2.1": - return (Path.Combine(probePath, fxVersion, "Cone.Worker.dll"), true); + return (Path.Combine(probePath, "netcoreapp2.1", "Cone.Worker.dll"), true); } return (Path.Combine(probePath, "netcoreapp2.2", "Cone.Worker.dll"), true); } diff --git a/Source/dotnet-conesole/dotnet-conesole.csproj b/Source/dotnet-conesole/dotnet-conesole.csproj index bde785b..f0b7cd1 100644 --- a/Source/dotnet-conesole/dotnet-conesole.csproj +++ b/Source/dotnet-conesole/dotnet-conesole.csproj @@ -1,7 +1,7 @@  Exe - netcoreapp2.0;netcoreapp2.1 + netcoreapp2.1;netcoreapp2.2 false diff --git a/Specs/Cone.Specs/Cone.Specs.csproj b/Specs/Cone.Specs/Cone.Specs.csproj index 7b8e3fd..f7ca1c2 100644 --- a/Specs/Cone.Specs/Cone.Specs.csproj +++ b/Specs/Cone.Specs/Cone.Specs.csproj @@ -1,15 +1,15 @@  - net452;netcoreapp2.1 + net452;;net462;netcoreapp2.2 false true - + - + diff --git a/dotnet-conesole.nuspec b/dotnet-conesole.nuspec index 106f90b..72bbec3 100644 --- a/dotnet-conesole.nuspec +++ b/dotnet-conesole.nuspec @@ -19,5 +19,7 @@ The easiest way to execute Cone specs. + +