2 people like it.

ContextAwareTask.fs

ContextAwareTask implementation from https://github.com/dotnet/Nerdbank.GitVersioning/blob/master/src/Nerdbank.GitVersioning.Tasks/ContextAwareTask.cs

  1: 
  2: 
  3: 
  4: 
  5: 
  6: 
  7: 
  8: 
  9: 
 10: 
 11: 
 12: 
 13: 
 14: 
 15: 
 16: 
 17: 
 18: 
 19: 
 20: 
 21: 
 22: 
 23: 
 24: 
 25: 
 26: 
 27: 
 28: 
 29: 
 30: 
 31: 
 32: 
 33: 
 34: 
 35: 
 36: 
 37: 
 38: 
 39: 
 40: 
 41: 
 42: 
 43: 
 44: 
 45: 
 46: 
 47: 
 48: 
 49: 
 50: 
 51: 
 52: 
 53: 
 54: 
 55: 
 56: 
 57: 
 58: 
 59: 
 60: 
 61: 
 62: 
 63: 
 64: 
 65: 
 66: 
 67: 
 68: 
 69: 
 70: 
 71: 
 72: 
 73: 
 74: 
 75: 
 76: 
 77: 
 78: 
 79: 
 80: 
 81: 
 82: 
 83: 
 84: 
 85: 
 86: 
 87: 
 88: 
 89: 
 90: 
 91: 
 92: 
 93: 
 94: 
 95: 
 96: 
 97: 
 98: 
 99: 
100: 
101: 
102: 
103: 
104: 
105: 
106: 
107: 
108: 
109: 
open System;
open System.IO;
open System.Linq;
open System.Reflection;
#if NETCOREAPP2_0
open System.Runtime.Loader;
#endif
open Microsoft.Build.Framework;
open Microsoft.Build.Utilities;

[<AbstractClass>]
type public ContextAwareTask () as cat =
    inherit Task()

    let ``type`` = cat.GetType()
    let typeInfo = ``type``.GetTypeInfo()

    abstract ManagedDllDirectory : string with get
    default _.ManagedDllDirectory
        with get() =
            let codeBase = typeInfo.Assembly.CodeBase
            let uri = new Uri(codeBase)
            Path.GetDirectoryName(uri.LocalPath)

    abstract UnmanagedDllDirectory : string with get
    default _.UnmanagedDllDirectory with get () = null

    abstract member ExecuteInner: unit -> bool

    override this.Execute() =
#if NETCOREAPP2_0
        let taskAssemblyPath = Uri(typeInfo.Assembly.CodeBase).LocalPath
        let ctxt = CustomAssemblyLoader(this)
        let inContextAssembly = ctxt.LoadFromAssemblyPath(taskAssemblyPath)
        let innerTaskType = inContextAssembly.GetType(``type``.FullName)
        let innerTask = Activator.CreateInstance(innerTaskType)

        let outerProperties = ``type``.GetRuntimeProperties().ToDictionary(fun i -> i.Name);
        let innerProperties = innerTaskType.GetRuntimeProperties().ToDictionary(fun i -> i.Name);
        let propertiesDiscovery =
            outerProperties.Values
            |> Seq.filter (fun outerProperty -> outerProperty.SetMethod != null && outerProperty.GetMethod != null)
            |> Seq.map
                (fun outerProperty ->
                    let innerProperty = innerProperties.[outerProperty.Name]
                    (outerProperty, innerProperty))
        let propertiesMap = propertiesDiscovery |> Seq.toArray
        let outputPropertiesMap =
            propertiesDiscovery
            |> Seq.filter (fun (outerProperty, _) -> outerProperty.GetCustomAttribute<OutputAttribute>() != null)

        let propertiesMap =
            propertiesMap
            |> Seq.map
                (fun pair ->
                    let outerPropertyValue = (fst pair).GetValue(this)
                    (snd pair).SetValue(innerTask, outerPropertyValue)
                    pair)

        let executeInnerMethod =
            innerTaskType.GetMethod(nameof(this.ExecuteInner), (BindingFlags.Instance ||| BindingFlags.NonPublic))
        let result = executeInnerMethod.Invoke(innerTask, Array.empty) :?> bool

        let outputPropertiesMap =
            outputPropertiesMap
            |> Seq.map
                (fun pair ->
                    (fst pair).SetValue(this, (snd pair).GetValue(innerTask)))

        result
#else
        // On .NET Framework (on Windows), we find native binaries by adding them to our PATH.
        if not (this.UnmanagedDllDirectory = null) then
            let pathEnvVar = Environment.GetEnvironmentVariable("PATH")
            let searchPaths = pathEnvVar.Split(Path.PathSeparator)
            if not (searchPaths.Contains(this.UnmanagedDllDirectory, StringComparer.OrdinalIgnoreCase)) then
                let pathEnvVar = $"{pathEnvVar}{Path.PathSeparator}{this.UnmanagedDllDirectory}"
                Environment.SetEnvironmentVariable("PATH", pathEnvVar)

        this.ExecuteInner()
#endif

#if NETCOREAPP2_0
type private CustomAssemblyLoader(loaderTask: ContextAwareTask) =
    inherit AssemblyLoadContext

    let loaderTask = loaderTask

    override this.Load(assemblyName: AssemblyName) : Assembly =
        let assemblyPath = Path.Combine(this.loaderTask.ManagedDllDirectory, assemblyName.Name) + ".dll"
        if File.Exists(assemblyPath) then
            LoadFromAssemblyPath(assemblyPath)
        Default.LoadFromAssemblyName(assemblyName)

    override LoadUnmanagedDll(unmanagedDllName: string) : IntPtr =
        let unmanagedDllPath =
             Directory.EnumerateFiles(
                this.loaderTask.UnmanagedDllDirectory,
                $"{unmanagedDllName}.*").Concat(
                    Directory.EnumerateFiles(
                        this.loaderTask.UnmanagedDllDirectory,
                        $"lib{unmanagedDllName}.*"))
                .FirstOrDefault()

        if unmanagedDllPath != null then
            this.LoadUnmanagedDllFromPath(unmanagedDllPath)

        base.LoadUnmanagedDll(unmanagedDllName)
#endif
namespace System
namespace System.IO
namespace System.Linq
namespace System.Reflection
namespace Microsoft
namespace Microsoft.Build
namespace Microsoft.Build.Framework
Multiple items
type AbstractClassAttribute =
  inherit Attribute
  new : unit -> AbstractClassAttribute

--------------------
new : unit -> AbstractClassAttribute
Multiple items
type ContextAwareTask =
  inherit obj
  new : unit -> ContextAwareTask
  abstract member ExecuteInner : unit -> bool
  abstract member ManagedDllDirectory : string
  abstract member UnmanagedDllDirectory : string
  override Execute : unit -> 'a
  override ManagedDllDirectory : string
  override UnmanagedDllDirectory : string

--------------------
new : unit -> ContextAwareTask
val cat : ContextAwareTask
Multiple items
val string : value:'T -> string

--------------------
type string = String
Multiple items
type Uri =
  new : uriString:string -> Uri + 5 overloads
  member AbsolutePath : string
  member AbsoluteUri : string
  member Authority : string
  member DnsSafeHost : string
  member Equals : comparand:obj -> bool
  member Fragment : string
  member GetComponents : components:UriComponents * format:UriFormat -> string
  member GetHashCode : unit -> int
  member GetLeftPart : part:UriPartial -> string
  ...

--------------------
Uri(uriString: string) : Uri
Uri(uriString: string, uriKind: UriKind) : Uri
Uri(baseUri: Uri, relativeUri: string) : Uri
Uri(baseUri: Uri, relativeUri: Uri) : Uri
type Path =
  static val DirectorySeparatorChar : char
  static val AltDirectorySeparatorChar : char
  static val VolumeSeparatorChar : char
  static val PathSeparator : char
  static val InvalidPathChars : char[]
  static member ChangeExtension : path:string * extension:string -> string
  static member Combine : [<ParamArray>] paths:string[] -> string + 3 overloads
  static member EndsInDirectorySeparator : path:ReadOnlySpan<char> -> bool + 1 overload
  static member GetDirectoryName : path:string -> string + 1 overload
  static member GetExtension : path:string -> string + 1 overload
  ...
Path.GetDirectoryName(path: ReadOnlySpan<char>) : ReadOnlySpan<char>
Path.GetDirectoryName(path: string) : string
type unit = Unit
type bool = Boolean
val not : value:bool -> bool
type Environment =
  static member CommandLine : string
  static member CurrentDirectory : string with get, set
  static member CurrentManagedThreadId : int
  static member Exit : exitCode:int -> unit
  static member ExitCode : int with get, set
  static member ExpandEnvironmentVariables : name:string -> string
  static member FailFast : message:string -> unit + 2 overloads
  static member GetCommandLineArgs : unit -> string[]
  static member GetEnvironmentVariable : variable:string -> string + 1 overload
  static member GetEnvironmentVariables : unit -> IDictionary + 1 overload
  ...
  nested type SpecialFolder
  nested type SpecialFolderOption
Environment.GetEnvironmentVariable(variable: string) : string
Environment.GetEnvironmentVariable(variable: string, target: EnvironmentVariableTarget) : string
field Path.PathSeparator: char
type StringComparer =
  member Compare : x:obj * y:obj -> int + 1 overload
  member Equals : x:obj * y:obj -> bool + 1 overload
  member GetHashCode : obj:obj -> int + 1 overload
  static member Create : culture:CultureInfo * ignoreCase:bool -> StringComparer + 1 overload
  static member CurrentCulture : StringComparer
  static member CurrentCultureIgnoreCase : StringComparer
  static member FromComparison : comparisonType:StringComparison -> StringComparer
  static member InvariantCulture : StringComparer
  static member InvariantCultureIgnoreCase : StringComparer
  static member Ordinal : StringComparer
  ...
property StringComparer.OrdinalIgnoreCase: StringComparer with get
Raw view Test code New version

More information

Link:http://fssnip.net/83v
Posted:3 years ago
Author:anthony-mi
Tags: #contextawaretask , #msbuild