From 9752e3eb96578c60732e5e720131bb21d6b1fdb5 Mon Sep 17 00:00:00 2001 From: Maxime Mangel Date: Tue, 6 Mar 2018 16:46:59 +0100 Subject: [PATCH 1/5] Fix #1808: Convert DotNet API to "module based" --- src/app/Fake.DotNet.Cli/DotNet.fs | 1801 +++++++++++++++-------------- 1 file changed, 902 insertions(+), 899 deletions(-) diff --git a/src/app/Fake.DotNet.Cli/DotNet.fs b/src/app/Fake.DotNet.Cli/DotNet.fs index 549f2ddef05..45e7970589f 100644 --- a/src/app/Fake.DotNet.Cli/DotNet.fs +++ b/src/app/Fake.DotNet.Cli/DotNet.fs @@ -5,905 +5,908 @@ /// .NET Core + CLI tools helpers module Fake.DotNet.Cli -// NOTE: The #if can be removed once we have a working release with the "new" API -// Currently we #load this file in build.fsx -#if NO_DOTNETCORE_BOOTSTRAP -open Fake.Core -open Fake.IO -open Fake.IO.FileSystemOperators -#else -open Fake -// Workaround until we have a release with the "new" API. -module Environment = - let environVar = environVar - let isUnix = isUnix -module Trace = - let trace = trace - let traceError = traceError - let traceImportant = traceImportant - let traceTask s proj = - { new System.IDisposable with - member x.Dispose() = () } -module String = - let replace = replace -module Process = - let ExecProcess = ExecProcess - let ExecProcessWithLambdas = ExecProcessWithLambdas - -#endif -open System -open System.IO -open System.Security.Cryptography -open System.Text -open Newtonsoft.Json.Linq -open System - -/// .NET Core SDK default install directory (set to default localappdata dotnet dir). Update this to redirect all tool commands to different location. -let mutable DefaultDotNetCliDir = - if Environment.isUnix - then Environment.environVar "HOME" @@ ".dotnet" - else Environment.environVar "LocalAppData" @@ "Microsoft" @@ "dotnet" - -/// Get dotnet cli executable path -/// ## Parameters -/// -/// - 'dotnetCliDir' - dotnet cli install directory -let private dotnetCliPath dotnetCliDir = dotnetCliDir @@ (if Environment.isUnix then "dotnet" else "dotnet.exe") - -/// Get .NET Core SDK download uri -let private getGenericDotNetCliInstallerUrl branch installerName = - sprintf "https://raw.githubusercontent.com/dotnet/cli/%s/scripts/obtain/%s" branch installerName - -let private getPowershellDotNetCliInstallerUrl branch = getGenericDotNetCliInstallerUrl branch "dotnet-install.ps1" -let private getBashDotNetCliInstallerUrl branch = getGenericDotNetCliInstallerUrl branch "dotnet-install.sh" - - -/// Download .NET Core SDK installer -let private downloadDotNetInstallerFromUrl (url:string) fileName = - //let url = getDotNetCliInstallerUrl branch -#if USE_HTTPCLIENT - let h = new System.Net.Http.HttpClient(); - use f = File.Open(fileName, FileMode.Create); - h.GetStreamAsync(url).Result.CopyTo(f); -#else - use w = new System.Net.WebClient() - w.DownloadFile(url, fileName) // Http.RequestStream url -#endif - //use outFile = File.Open(fileName, FileMode.Create) - //installScript.ResponseStream.CopyTo(outFile) - Trace.trace (sprintf "downloaded dotnet installer (%s) to %s" url fileName) - -/// [omit] -let private md5 (data : byte array) : string = - use md5 = MD5.Create() - (StringBuilder(), md5.ComputeHash(data)) - ||> Array.fold (fun sb b -> sb.Append(b.ToString("x2"))) - |> string - - -/// .NET Core SDK installer download options -type DotNetInstallerOptions = - { - /// Always download install script (otherwise install script is cached in temporary folder) - AlwaysDownload: bool; - /// Download installer from this github branch - Branch: string; - } - - /// Parameter default values. - static member Default = { - AlwaysDownload = false - Branch = "master" - } - -/// Download .NET Core SDK installer -/// ## Parameters -/// -/// - 'setParams' - set download installer options -let DotNetDownloadInstaller setParams = - let param = DotNetInstallerOptions.Default |> setParams - - let ext = if Environment.isUnix then "sh" else "ps1" - let getInstallerUrl = if Environment.isUnix then getBashDotNetCliInstallerUrl else getPowershellDotNetCliInstallerUrl - let scriptName = - sprintf "dotnet_install_%s.%s" (md5 (Encoding.ASCII.GetBytes(param.Branch))) ext - let tempInstallerScript = Path.GetTempPath() @@ scriptName - - // maybe download installer script - match param.AlwaysDownload || not(File.Exists(tempInstallerScript)) with - | true -> - let url = getInstallerUrl param.Branch - downloadDotNetInstallerFromUrl url tempInstallerScript - | _ -> () - - tempInstallerScript - - -/// .NET Core SDK architecture -type DotNetCliArchitecture = - /// this value represents currently running OS architecture - | Auto - | X86 - | X64 - -/// .NET Core SDK version (used to specify version when installing .NET Core SDK) -type DotNetCliVersion = - /// most latest build on specific channel - | Latest - /// last known good version on specific channel (Note: LKG work is in progress. Once the work is finished, this will become new default) - | Lkg - /// 4-part version in a format A.B.C.D - represents specific version of build - | Version of string - -/// .NET Core SDK install options -type DotNetCliInstallOptions = - { - /// Custom installer obtain (download) options - InstallerOptions: DotNetInstallerOptions -> DotNetInstallerOptions - /// .NET Core SDK channel (defaults to normalized installer branch) - Channel: string option; - /// .NET Core SDK version - Version: DotNetCliVersion; - /// Custom installation directory (for local build installation) - CustomInstallDir: string option - /// Architecture - Architecture: DotNetCliArchitecture; - /// Include symbols in the installation (Switch does not work yet. Symbols zip is not being uploaded yet) - DebugSymbols: bool; - /// If set it will not perform installation but instead display what command line to use - DryRun: bool - /// Do not update path variable - NoPath: bool - } - - /// Parameter default values. - static member Default = { - InstallerOptions = id - Channel = None - Version = Latest - CustomInstallDir = None - Architecture = Auto - DebugSymbols = false - DryRun = false - NoPath = true - } - -/// .NET Core SDK install options preconfigured for preview2 tooling -let Preview2ToolingOptions options = - { options with - InstallerOptions = (fun io -> - { io with - Branch = "v1.0.0-preview2" - }) - Channel = Some "preview" - Version = Version "1.0.0-preview2-003121" - } - -/// .NET Core SDK install options preconfigured for preview4 tooling -let LatestPreview4ToolingOptions options = - { options with - InstallerOptions = (fun io -> - { io with - Branch = "rel/1.0.0-preview4" - }) - Channel = None - Version = Latest - } -/// .NET Core SDK install options preconfigured for preview4 tooling -let Preview4_004233ToolingOptions options = - { options with - InstallerOptions = (fun io -> - { io with - Branch = "rel/1.0.0-preview4" - }) - Channel = None - Version = Version "1.0.0-preview4-004233" - } -/// .NET Core SDK install options preconfigured for preview4 tooling -let RC4_004771ToolingOptions options = - { options with - InstallerOptions = (fun io -> - { io with - Branch = "rel/1.0.0-rc3" - }) - Channel = None - Version = Version "1.0.0-rc4-004771" - } - -/// .NET Core SDK install options preconfigured for preview4 tooling, this is marketized as v1.0.1 release of the .NET Core tools -let RC4_004973ToolingOptions options = - { options with - InstallerOptions = (fun io -> - { io with - Branch = "rel/1.0.1" - }) - Channel = None - Version = Version "1.0.3-rc4-004973" - } - -let Release_1_0_4 options = - { options with - InstallerOptions = (fun io -> - { io with - Branch = "release/2.0.0" - }) - Channel = None - Version = Version "1.0.4" - } - -let Release_2_0_0 options = - { options with - InstallerOptions = (fun io -> - { io with - Branch = "release/2.0.0" - }) - Channel = None - Version = Version "2.0.0" - } - -let Release_2_0_3 options = - { options with - InstallerOptions = (fun io -> - { io with - Branch = "release/2.0.0" - }) - Channel = None - Version = Version "2.0.3" - } - -let Release_2_1_4 options = - { options with - InstallerOptions = (fun io -> - { io with - Branch = "release/2.1" - }) - Channel = None - Version = Version "2.1.4" - } - -/// [omit] -let private optionToParam option paramFormat = - match option with - | Some value -> sprintf paramFormat value - | None -> "" - -/// [omit] -let private boolToFlag value flagParam = - match value with - | true -> flagParam - | false -> "" - -/// [omit] -let private buildDotNetCliInstallArgs quoteChar (param: DotNetCliInstallOptions) = - let versionParamValue = - match param.Version with - | Latest -> "latest" - | Lkg -> "lkg" - | Version ver -> ver - - // get channel value from installer branch info - let channelParamValue = - match param.Channel with - | Some ch -> ch - | None -> - let installerOptions = DotNetInstallerOptions.Default |> param.InstallerOptions - installerOptions.Branch |> String.replace "/" "-" - let quoteStr str = sprintf "%c%s%c" quoteChar str quoteChar - let architectureParamValue = - match param.Architecture with - | Auto -> None - | X86 -> Some "x86" - | X64 -> Some "x64" - [ - "-Verbose" - sprintf "-Channel %s" (quoteStr channelParamValue) - sprintf "-Version %s" (quoteStr versionParamValue) - optionToParam architectureParamValue "-Architecture %s" - optionToParam (param.CustomInstallDir |> Option.map quoteStr) "-InstallDir %s" - boolToFlag param.DebugSymbols "-DebugSymbols" - boolToFlag param.DryRun "-DryRun" - boolToFlag param.NoPath "-NoPath" - ] |> Seq.filter (not << String.IsNullOrEmpty) |> String.concat " " - - -/// Install .NET Core SDK if required -/// ## Parameters -/// -/// - 'setParams' - set installation options -let DotNetCliInstall setParams = - let param = DotNetCliInstallOptions.Default |> setParams - let installScript = DotNetDownloadInstaller param.InstallerOptions - - let exitCode = - let args, fileName = - if Environment.isUnix then - // Problem is that argument parsing works differently on dotnetcore than on mono... - // See https://github.com/dotnet/corefx/blob/master/src/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.cs#L437 -#if NO_DOTNETCORE_BOOTSTRAP - let quoteChar = '"' -#else - let quoteChar = '\'' -#endif - let args = sprintf "%s %s" installScript (buildDotNetCliInstallArgs quoteChar param) - args, "bash" // Otherwise we need to set the executable flag! - else - let args = - sprintf - "-ExecutionPolicy Bypass -NoProfile -NoLogo -NonInteractive -Command \"%s %s; if (-not $?) { exit -1 };\"" - installScript - (buildDotNetCliInstallArgs '\'' param) - args, "powershell" - Process.Exec (fun info -> - { info with - FileName = fileName - WorkingDirectory = Path.GetTempPath() - Arguments = args } - ) TimeSpan.MaxValue - - if exitCode <> 0 then - // force download new installer script - Trace.traceError ".NET Core SDK install failed, trying to redownload installer..." - DotNetDownloadInstaller (param.InstallerOptions >> (fun o -> - { o with - AlwaysDownload = true - })) |> ignore - failwithf ".NET Core SDK install failed with code %i" exitCode - -/// dotnet restore verbosity -type DotNetVerbosity = - | Quiet - | Minimal - | Normal - | Detailed - | Diagnostic - -/// dotnet cli command execution options -type DotNetOptions = - { - /// DotNet cli executable path - DotNetCliPath: string - /// Command working directory - WorkingDirectory: string - /// Custom parameters - CustomParams: string option - /// Logging verbosity (--verbosity) - Verbosity: DotNetVerbosity option - /// Restore logging verbosity (--verbosity) - Diagnostics: bool - /// If true the function will redirect the output of the called process (but will disable colors, false by default) - RedirectOutput : bool - /// Gets the environment variables that apply to this process and its child processes. - /// NOTE: Recommendation is to not use this Field, but instead use the helper function in the Proc module (for example Process.setEnvironmentVariable) - /// NOTE: This field is ignored when UseShellExecute is true. - Environment : Map - } - static member Create() = { - DotNetCliPath = dotnetCliPath DefaultDotNetCliDir - WorkingDirectory = Directory.GetCurrentDirectory() - CustomParams = None - Verbosity = None - Diagnostics = false - RedirectOutput = false - Environment = - Process.createEnvironmentMap() - |> Map.remove "MSBUILD_EXE_PATH" - |> Map.remove "MSBuildExtensionsPath" - } - [] - static member Default = DotNetOptions.Create() - - /// Sets the current environment variables. - member x.WithEnvironment map = - { x with Environment = map } - - /// Sets a value indicating whether the output for the given process is redirected. - member x.WithRedirectOutput shouldRedirect = - { x with RedirectOutput = shouldRedirect } - -/// [omit] -let private argList2 name values = - values - |> Seq.collect (fun v -> ["--" + name; sprintf @"""%s""" v]) - |> String.concat " " - -/// [omit] -let private argOption name value = - match value with - | true -> sprintf "--%s" name +[] +module DotNet = + + // NOTE: The #if can be removed once we have a working release with the "new" API + // Currently we #load this file in build.fsx + #if NO_DOTNETCORE_BOOTSTRAP + open Fake.Core + open Fake.IO + open Fake.IO.FileSystemOperators + #else + open Fake + // Workaround until we have a release with the "new" API. + module Environment = + let environVar = environVar + let isUnix = isUnix + module Trace = + let trace = trace + let traceError = traceError + let traceImportant = traceImportant + let traceTask s proj = + { new System.IDisposable with + member x.Dispose() = () } + module String = + let replace = replace + module Process = + let ExecProcess = ExecProcess + let ExecProcessWithLambdas = ExecProcessWithLambdas + + #endif + open System + open System.IO + open System.Security.Cryptography + open System.Text + open Newtonsoft.Json.Linq + open System + + /// .NET Core SDK default install directory (set to default localappdata dotnet dir). Update this to redirect all tool commands to different location. + let mutable DefaultDotNetCliDir = + if Environment.isUnix + then Environment.environVar "HOME" @@ ".dotnet" + else Environment.environVar "LocalAppData" @@ "Microsoft" @@ "dotnet" + + /// Get dotnet cli executable path + /// ## Parameters + /// + /// - 'dotnetCliDir' - dotnet cli install directory + let private dotnetCliPath dotnetCliDir = dotnetCliDir @@ (if Environment.isUnix then "dotnet" else "dotnet.exe") + + /// Get .NET Core SDK download uri + let private getGenericDotNetCliInstallerUrl branch installerName = + sprintf "https://raw.githubusercontent.com/dotnet/cli/%s/scripts/obtain/%s" branch installerName + + let private getPowershellDotNetCliInstallerUrl branch = getGenericDotNetCliInstallerUrl branch "dotnet-install.ps1" + let private getBashDotNetCliInstallerUrl branch = getGenericDotNetCliInstallerUrl branch "dotnet-install.sh" + + + /// Download .NET Core SDK installer + let private downloadDotNetInstallerFromUrl (url:string) fileName = + //let url = getDotNetCliInstallerUrl branch + #if USE_HTTPCLIENT + let h = new System.Net.Http.HttpClient(); + use f = File.Open(fileName, FileMode.Create); + h.GetStreamAsync(url).Result.CopyTo(f); + #else + use w = new System.Net.WebClient() + w.DownloadFile(url, fileName) // Http.RequestStream url + #endif + //use outFile = File.Open(fileName, FileMode.Create) + //installScript.ResponseStream.CopyTo(outFile) + Trace.trace (sprintf "downloaded dotnet installer (%s) to %s" url fileName) + + /// [omit] + let private md5 (data : byte array) : string = + use md5 = MD5.Create() + (StringBuilder(), md5.ComputeHash(data)) + ||> Array.fold (fun sb b -> sb.Append(b.ToString("x2"))) + |> string + + + /// .NET Core SDK installer download options + type InstallerOptions = + { + /// Always download install script (otherwise install script is cached in temporary folder) + AlwaysDownload: bool; + /// Download installer from this github branch + Branch: string; + } + + /// Parameter default values. + static member Default = { + AlwaysDownload = false + Branch = "master" + } + + /// Download .NET Core SDK installer + /// ## Parameters + /// + /// - 'setParams' - set download installer options + let DotNetDownloadInstaller setParams = + let param = InstallerOptions.Default |> setParams + + let ext = if Environment.isUnix then "sh" else "ps1" + let getInstallerUrl = if Environment.isUnix then getBashDotNetCliInstallerUrl else getPowershellDotNetCliInstallerUrl + let scriptName = + sprintf "dotnet_install_%s.%s" (md5 (Encoding.ASCII.GetBytes(param.Branch))) ext + let tempInstallerScript = Path.GetTempPath() @@ scriptName + + // maybe download installer script + match param.AlwaysDownload || not(File.Exists(tempInstallerScript)) with + | true -> + let url = getInstallerUrl param.Branch + downloadDotNetInstallerFromUrl url tempInstallerScript + | _ -> () + + tempInstallerScript + + + /// .NET Core SDK architecture + type CliArchitecture = + /// this value represents currently running OS architecture + | Auto + | X86 + | X64 + + /// .NET Core SDK version (used to specify version when installing .NET Core SDK) + type CliVersion = + /// most latest build on specific channel + | Latest + /// last known good version on specific channel (Note: LKG work is in progress. Once the work is finished, this will become new default) + | Lkg + /// 4-part version in a format A.B.C.D - represents specific version of build + | Version of string + + /// .NET Core SDK install options + type CliInstallOptions = + { + /// Custom installer obtain (download) options + InstallerOptions: InstallerOptions -> InstallerOptions + /// .NET Core SDK channel (defaults to normalized installer branch) + Channel: string option; + /// .NET Core SDK version + Version: CliVersion; + /// Custom installation directory (for local build installation) + CustomInstallDir: string option + /// Architecture + Architecture: CliArchitecture; + /// Include symbols in the installation (Switch does not work yet. Symbols zip is not being uploaded yet) + DebugSymbols: bool; + /// If set it will not perform installation but instead display what command line to use + DryRun: bool + /// Do not update path variable + NoPath: bool + } + + /// Parameter default values. + static member Default = { + InstallerOptions = id + Channel = None + Version = Latest + CustomInstallDir = None + Architecture = Auto + DebugSymbols = false + DryRun = false + NoPath = true + } + + /// .NET Core SDK install options preconfigured for preview2 tooling + let Preview2ToolingOptions options = + { options with + InstallerOptions = (fun io -> + { io with + Branch = "v1.0.0-preview2" + }) + Channel = Some "preview" + Version = Version "1.0.0-preview2-003121" + } + + /// .NET Core SDK install options preconfigured for preview4 tooling + let LatestPreview4ToolingOptions options = + { options with + InstallerOptions = (fun io -> + { io with + Branch = "rel/1.0.0-preview4" + }) + Channel = None + Version = Latest + } + /// .NET Core SDK install options preconfigured for preview4 tooling + let Preview4_004233ToolingOptions options = + { options with + InstallerOptions = (fun io -> + { io with + Branch = "rel/1.0.0-preview4" + }) + Channel = None + Version = Version "1.0.0-preview4-004233" + } + /// .NET Core SDK install options preconfigured for preview4 tooling + let RC4_004771ToolingOptions options = + { options with + InstallerOptions = (fun io -> + { io with + Branch = "rel/1.0.0-rc3" + }) + Channel = None + Version = Version "1.0.0-rc4-004771" + } + + /// .NET Core SDK install options preconfigured for preview4 tooling, this is marketized as v1.0.1 release of the .NET Core tools + let RC4_004973ToolingOptions options = + { options with + InstallerOptions = (fun io -> + { io with + Branch = "rel/1.0.1" + }) + Channel = None + Version = Version "1.0.3-rc4-004973" + } + + let Release_1_0_4 options = + { options with + InstallerOptions = (fun io -> + { io with + Branch = "release/2.0.0" + }) + Channel = None + Version = Version "1.0.4" + } + + let Release_2_0_0 options = + { options with + InstallerOptions = (fun io -> + { io with + Branch = "release/2.0.0" + }) + Channel = None + Version = Version "2.0.0" + } + + let Release_2_0_3 options = + { options with + InstallerOptions = (fun io -> + { io with + Branch = "release/2.0.0" + }) + Channel = None + Version = Version "2.0.3" + } + + let Release_2_1_4 options = + { options with + InstallerOptions = (fun io -> + { io with + Branch = "release/2.1" + }) + Channel = None + Version = Version "2.1.4" + } + + /// [omit] + let private optionToParam option paramFormat = + match option with + | Some value -> sprintf paramFormat value + | None -> "" + + /// [omit] + let private boolToFlag value flagParam = + match value with + | true -> flagParam | false -> "" -/// [omit] -let private buildCommonArgs (param: DotNetOptions) = - [ defaultArg param.CustomParams "" - param.Verbosity |> Option.toList |> Seq.map (fun v -> v.ToString().ToLowerInvariant()) |> argList2 "verbosity" - ] |> Seq.filter (not << String.IsNullOrEmpty) |> String.concat " " - -/// [omit] -let private buildSdkOptionsArgs (param: DotNetOptions) = - [ param.Diagnostics |> argOption "--diagostics" - ] |> Seq.filter (not << String.IsNullOrEmpty) |> String.concat " " - -/// Execute raw dotnet cli command -/// ## Parameters -/// -/// - 'options' - common execution options -/// - 'command' - the sdk command to execute 'test', 'new', 'build', ... -/// - 'args' - command arguments -let DotNet (buildOptions: DotNetOptions -> DotNetOptions) command args = - let errors = new System.Collections.Generic.List() - let messages = new System.Collections.Generic.List() - let timeout = TimeSpan.MaxValue - - let errorF msg = - Trace.traceError msg - errors.Add msg - - let messageF msg = - Trace.trace msg - messages.Add msg - - let options = buildOptions (DotNetOptions.Create()) - let sdkOptions = buildSdkOptionsArgs options - let commonOptions = buildCommonArgs options - let cmdArgs = sprintf "%s %s %s %s" sdkOptions command commonOptions args - - let result = - let f (info:ProcStartInfo) = - let dir = System.IO.Path.GetDirectoryName options.DotNetCliPath - let oldPath = - match options.Environment |> Map.tryFind "PATH" with - | None -> "" - | Some s -> s + /// [omit] + let private buildDotNetCliInstallArgs quoteChar (param: CliInstallOptions) = + let versionParamValue = + match param.Version with + | Latest -> "latest" + | Lkg -> "lkg" + | Version ver -> ver + + // get channel value from installer branch info + let channelParamValue = + match param.Channel with + | Some ch -> ch + | None -> + let installerOptions = InstallerOptions.Default |> param.InstallerOptions + installerOptions.Branch |> String.replace "/" "-" + let quoteStr str = sprintf "%c%s%c" quoteChar str quoteChar + let architectureParamValue = + match param.Architecture with + | Auto -> None + | X86 -> Some "x86" + | X64 -> Some "x64" + [ + "-Verbose" + sprintf "-Channel %s" (quoteStr channelParamValue) + sprintf "-Version %s" (quoteStr versionParamValue) + optionToParam architectureParamValue "-Architecture %s" + optionToParam (param.CustomInstallDir |> Option.map quoteStr) "-InstallDir %s" + boolToFlag param.DebugSymbols "-DebugSymbols" + boolToFlag param.DryRun "-DryRun" + boolToFlag param.NoPath "-NoPath" + ] |> Seq.filter (not << String.IsNullOrEmpty) |> String.concat " " + + + /// Install .NET Core SDK if required + /// ## Parameters + /// + /// - 'setParams' - set installation options + let Install setParams = + let param = CliInstallOptions.Default |> setParams + let installScript = DotNetDownloadInstaller param.InstallerOptions + + let exitCode = + let args, fileName = + if Environment.isUnix then + // Problem is that argument parsing works differently on dotnetcore than on mono... + // See https://github.com/dotnet/corefx/blob/master/src/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.cs#L437 + #if NO_DOTNETCORE_BOOTSTRAP + let quoteChar = '"' + #else + let quoteChar = '\'' + #endif + let args = sprintf "%s %s" installScript (buildDotNetCliInstallArgs quoteChar param) + args, "bash" // Otherwise we need to set the executable flag! + else + let args = + sprintf + "-ExecutionPolicy Bypass -NoProfile -NoLogo -NonInteractive -Command \"%s %s; if (-not $?) { exit -1 };\"" + installScript + (buildDotNetCliInstallArgs '\'' param) + args, "powershell" + Process.Exec (fun info -> { info with - FileName = options.DotNetCliPath - WorkingDirectory = options.WorkingDirectory - Arguments = cmdArgs } - |> Process.setEnvironment options.Environment - |> Process.setEnvironmentVariable "PATH" (sprintf "%s%c%s" dir System.IO.Path.PathSeparator oldPath) - - if options.RedirectOutput then - Process.ExecWithLambdas f timeout true errorF messageF - else Process.Exec f timeout - ProcessResult.New result messages errors - -/// dotnet restore command options -type DotNetRestoreOptions = - { - /// Common tool options - Common: DotNetOptions - /// The runtime to restore for (seems added in RC4). Maybe a bug, but works. - Runtime: string option - /// Nuget feeds to search updates in. Use default if empty. - Sources: string list - /// Directory to install packages in (--packages). - Packages: string list - /// Path to the nuget configuration file (nuget.config). - ConfigFile: string option - /// No cache flag (--no-cache) - NoCache: bool - /// Only warning failed sources if there are packages meeting version requirement (--ignore-failed-sources) - IgnoreFailedSources: bool - /// Disables restoring multiple projects in parallel (--disable-parallel) - DisableParallel: bool - } - - /// Parameter default values. - static member Create() = { - Common = DotNetOptions.Create() - Sources = [] - Runtime = None - Packages = [] - ConfigFile = None - NoCache = false - IgnoreFailedSources = false - DisableParallel = false - } - [] - static member Default = DotNetOptions.Create() - - /// Gets the current environment - member x.Environment = x.Common.Environment - /// Sets the current environment variables. - member x.WithEnvironment map = - { x with Common = { x.Common with Environment = map } } - - /// Sets a value indicating whether the output for the given process is redirected. - member x.WithRedirectOutput shouldRedirect = - { x with Common = x.Common.WithRedirectOutput shouldRedirect } - -/// [omit] -let private buildRestoreArgs (param: DotNetRestoreOptions) = - [ param.Sources |> argList2 "source" - param.Packages |> argList2 "packages" - param.ConfigFile |> Option.toList |> argList2 "configFile" - param.NoCache |> argOption "no-cache" - param.Runtime |> Option.toList |> argList2 "runtime" - param.IgnoreFailedSources |> argOption "ignore-failed-sources" - param.DisableParallel |> argOption "disable-parallel" - ] |> Seq.filter (not << String.IsNullOrEmpty) |> String.concat " " - - -/// Execute dotnet restore command -/// ## Parameters -/// -/// - 'setParams' - set restore command parameters -/// - 'project' - project to restore packages -let DotNetRestore setParams project = - use __ = Trace.traceTask "DotNet:restore" project - let param = DotNetRestoreOptions.Create() |> setParams - let args = sprintf "%s %s" project (buildRestoreArgs param) - let result = DotNet (fun _ -> param.Common) "restore" args - if not result.OK then failwithf "dotnet restore failed with code %i" result.ExitCode - -/// build configuration -type BuildConfiguration = - | Debug - | Release - | Custom of string - -/// [omit] -let private buildConfigurationArg (param: BuildConfiguration) = - sprintf "--configuration %s" - (match param with - | Debug -> "Debug" - | Release -> "Release" - | Custom config -> config) - -/// dotnet pack command options -type DotNetPackOptions = - { - /// Common tool options - Common: DotNetOptions; - /// Pack configuration (--configuration) - Configuration: BuildConfiguration; - /// Version suffix to use - VersionSuffix: string option; - /// Build base path (--build-base-path) - BuildBasePath: string option; - /// Output path (--output) - OutputPath: string option; - /// No build flag (--no-build) - NoBuild: bool; - } - - /// Parameter default values. - static member Create() = { - Common = DotNetOptions.Create() - Configuration = Release - VersionSuffix = None - BuildBasePath = None - OutputPath = None - NoBuild = false - } - [] - static member Default = DotNetPackOptions.Create() - /// Gets the current environment - member x.Environment = x.Common.Environment - /// Sets the current environment variables. - member x.WithEnvironment map = - { x with Common = { x.Common with Environment = map } } - /// Sets a value indicating whether the output for the given process is redirected. - member x.WithRedirectOutput shouldRedirect = - { x with Common = x.Common.WithRedirectOutput shouldRedirect } - -/// [omit] -let private buildPackArgs (param: DotNetPackOptions) = - [ - buildConfigurationArg param.Configuration - param.VersionSuffix |> Option.toList |> argList2 "version-suffix" - param.BuildBasePath |> Option.toList |> argList2 "build-base-path" - param.OutputPath |> Option.toList |> argList2 "output" - param.NoBuild |> argOption "no-build" - ] |> Seq.filter (not << String.IsNullOrEmpty) |> String.concat " " - - -/// Execute dotnet pack command -/// ## Parameters -/// -/// - 'setParams' - set pack command parameters -/// - 'project' - project to pack -let DotNetPack setParams project = - use __ = Trace.traceTask "DotNet:pack" project - let param = DotNetPackOptions.Create() |> setParams - let args = sprintf "%s %s" project (buildPackArgs param) - let result = DotNet (fun _ -> param.Common) "pack" args - if not result.OK then failwithf "dotnet pack failed with code %i" result.ExitCode - -/// dotnet --info command options -type DotNetInfoOptions = - { - /// Common tool options - Common: DotNetOptions; - } - /// Parameter default values. - static member Create() = { - Common = DotNetOptions.Create().WithRedirectOutput true - } - [] - static member Default = DotNetPackOptions.Create() - /// Gets the current environment - member x.Environment = x.Common.Environment - /// Sets the current environment variables. - member x.WithEnvironment map = - { x with Common = { x.Common with Environment = map } } - /// Sets a value indicating whether the output for the given process is redirected. - member x.WithRedirectOutput shouldRedirect = - { x with Common = x.Common.WithRedirectOutput shouldRedirect } - -/// dotnet info result -type DotNetInfoResult = - { - /// Common tool options - RID: string; - } -/// Execute dotnet --info command -/// ## Parameters -/// -/// - 'setParams' - set info command parameters -let DotNetInfo setParams = - use __ = Trace.traceTask "DotNet:info" "running dotnet --info" - let param = DotNetInfoOptions.Create() |> setParams - let args = "--info" // project (buildPackArgs param) - let result = DotNet (fun _ -> param.Common) "" args - if not result.OK then failwithf "dotnet --info failed with code %i" result.ExitCode - - let rid = - result.Messages - |> Seq.tryFind (fun m -> m.Contains "RID:") - |> Option.map (fun line -> line.Split([|':'|]).[1].Trim()) - - if rid.IsNone then failwithf "could not read rid from output: \n%s" (System.String.Join("\n", result.Messages)) - - { RID = rid.Value } - -/// dotnet publish command options -type DotNetPublishOptions = - { - /// Common tool options - Common: DotNetOptions; - /// Pack configuration (--configuration) - Configuration: BuildConfiguration; - /// Target framework to compile for (--framework) - Framework: string option; - /// Target runtime to publish for (--runtime) - Runtime: string option; - /// Build base path (--build-base-path) - BuildBasePath: string option; - /// Output path (--output) - OutputPath: string option; - /// Defines what `*` should be replaced with in version field in project.json (--version-suffix) - VersionSuffix: string option; - /// No build flag (--no-build) - NoBuild: bool; - } - - /// Parameter default values. - static member Create() = { - Common = DotNetOptions.Create() - Configuration = Release - Framework = None - Runtime = None - BuildBasePath = None - OutputPath = None - VersionSuffix = None - NoBuild = false - } - [] - static member Default = DotNetPublishOptions.Create() - /// Gets the current environment - member x.Environment = x.Common.Environment - /// Sets the current environment variables. - member x.WithEnvironment map = - { x with Common = { x.Common with Environment = map } } - /// Sets a value indicating whether the output for the given process is redirected. - member x.WithRedirectOutput shouldRedirect = - { x with Common = x.Common.WithRedirectOutput shouldRedirect } - -/// [omit] -let private buildPublishArgs (param: DotNetPublishOptions) = - [ - buildConfigurationArg param.Configuration - param.Framework |> Option.toList |> argList2 "framework" - param.Runtime |> Option.toList |> argList2 "runtime" - param.BuildBasePath |> Option.toList |> argList2 "build-base-path" - param.OutputPath |> Option.toList |> argList2 "output" - param.VersionSuffix |> Option.toList |> argList2 "version-suffix" - param.NoBuild |> argOption "no-build" - ] |> Seq.filter (not << String.IsNullOrEmpty) |> String.concat " " - - -/// Execute dotnet publish command -/// ## Parameters -/// -/// - 'setParams' - set publish command parameters -/// - 'project' - project to publish -let DotNetPublish setParams project = - use __ = Trace.traceTask "DotNet:publish" project - let param = DotNetPublishOptions.Create() |> setParams - let args = sprintf "%s %s" project (buildPublishArgs param) - let result = DotNet (fun _ -> param.Common) "publish" args - if not result.OK then failwithf "dotnet publish failed with code %i" result.ExitCode - -/// dotnet build command options -type DotNetBuildOptions = - { - /// Common tool options - Common: DotNetOptions; - /// Pack configuration (--configuration) - Configuration: BuildConfiguration; - /// Target framework to compile for (--framework) - Framework: string option; - /// Target runtime to publish for (--runtime) - Runtime: string option; - /// Build base path (--build-base-path) - BuildBasePath: string option; - /// Output path (--output) - OutputPath: string option; - /// Native flag (--native) - Native: bool; - } - - /// Parameter default values. - static member Create() = { - Common = DotNetOptions.Create() - Configuration = Release - Framework = None - Runtime = None - BuildBasePath = None - OutputPath = None - Native = false - } - [] - static member Default = DotNetBuildOptions.Create() - /// Gets the current environment - member x.Environment = x.Common.Environment - /// Sets the current environment variables. - member x.WithEnvironment map = - { x with Common = { x.Common with Environment = map } } - /// Sets a value indicating whether the output for the given process is redirected. - member x.WithRedirectOutput shouldRedirect = - { x with Common = x.Common.WithRedirectOutput shouldRedirect } - - -/// [omit] -let private buildBuildArgs (param: DotNetBuildOptions) = - [ - buildConfigurationArg param.Configuration - param.Framework |> Option.toList |> argList2 "framework" - param.Runtime |> Option.toList |> argList2 "runtime" - param.BuildBasePath |> Option.toList |> argList2 "build-base-path" - param.OutputPath |> Option.toList |> argList2 "output" - (if param.Native then "--native" else "") - ] |> Seq.filter (not << String.IsNullOrEmpty) |> String.concat " " - - -/// Execute dotnet build command -/// ## Parameters -/// -/// - 'setParams' - set compile command parameters -/// - 'project' - project to compile -let DotNetCompile setParams project = - use __ = Trace.traceTask "DotNet:build" project - let param = DotNetBuildOptions.Create() |> setParams - let args = sprintf "%s %s" project (buildBuildArgs param) - let result = DotNet (fun _ -> param.Common) "build" args - if not result.OK then failwithf "dotnet build failed with code %i" result.ExitCode - -let DotNetBuild = DotNetCompile - - -/// dotnet build command options -type DotNetTestOptions = - { - /// Common tool options - Common: DotNetOptions - /// Settings to use when running tests (--settings) - Settings: string option - /// Lists discovered tests (--list-tests) - ListTests: bool - /// Run tests that match the given expression. (--filter) - /// Examples: - /// Run tests with priority set to 1: --filter "Priority = 1" - /// Run a test with the specified full name: --filter "FullyQualifiedName=Namespace.ClassName.MethodName" - /// Run tests that contain the specified name: --filter "FullyQualifiedName~Namespace.Class" - /// More info on filtering support: https://aka.ms/vstest-filtering - Filter: string option - /// Use custom adapters from the given path in the test run. (--test-adapter-path) - TestAdapterPath: string option - /// Specify a logger for test results. (--logger) - Logger: string option - ///Configuration to use for building the project. Default for most projects is "Debug". (--configuration) - Configuration: BuildConfiguration - /// Target framework to publish for. The target framework has to be specified in the project file. (--framework) - Framework: string option - /// Directory in which to find the binaries to be run (--output) - Output: string option - /// Enable verbose logs for test platform. Logs are written to the provided file. (--diag) - Diag: string option - /// Do not build project before testing. (--no-build) - NoBuild: bool - /// The directory where the test results are going to be placed. The specified directory will be created if it does not exist. (--results-directory) - ResultsDirectory: string option - /// Enables data collector for the test run. More info here : https://aka.ms/vstest-collect (--collect) - Collect: string option - /// Does not do an implicit restore when executing the command. (--no-restore) - NoRestore: bool - /// Arguments to pass runsettings configurations through commandline. Arguments may be specified as name-value pair of the form [name]=[value] after "-- ". Note the space after --. - RunSettingsArguments : string option - } - - /// Parameter default values. - static member Create() = { - Common = DotNetOptions.Create() - Settings = None - ListTests = false - Filter = None - TestAdapterPath = None - Logger = None - Configuration = BuildConfiguration.Debug - Framework = None - Output = None - Diag = None - NoBuild = false - ResultsDirectory = None - Collect = None - NoRestore = false - RunSettingsArguments = None - } - [] - static member Default = DotNetTestOptions.Create() - /// Gets the current environment - member x.Environment = x.Common.Environment - /// Sets the current environment variables. - member x.WithEnvironment map = - { x with Common = { x.Common with Environment = map } } - /// Sets a value indicating whether the output for the given process is redirected. - member x.WithRedirectOutput shouldRedirect = - { x with Common = x.Common.WithRedirectOutput shouldRedirect } - - -/// [omit] -let private buildTestArgs (param: DotNetTestOptions) = - [ - param.Settings |> Option.toList |> argList2 "settings" - param.ListTests |> argOption "list-tests" - param.Filter |> Option.toList |> argList2 "filter" - param.TestAdapterPath |> Option.toList |> argList2 "test-adapter-path" - param.Logger |> Option.toList |> argList2 "logger" - buildConfigurationArg param.Configuration - param.Framework |> Option.toList |> argList2 "framework" - param.Output |> Option.toList |> argList2 "output" - param.Diag |> Option.toList |> argList2 "diag" - param.NoBuild |> argOption "no-build" - param.ResultsDirectory |> Option.toList |> argList2 "results-directory" - param.Collect |> Option.toList |> argList2 "collect" - param.NoRestore |> argOption "no-restore" - ] |> Seq.filter (not << String.IsNullOrEmpty) |> String.concat " " - - -/// Execute dotnet build command -/// ## Parameters -/// -/// - 'setParams' - set compile command parameters -/// - 'project' - project to compile -let DotNetTest setParams project = - use __ = Trace.traceTask "DotNet:test" project - let param = DotNetTestOptions.Create() |> setParams - let args = sprintf "%s %s" project (buildTestArgs param) - let result = DotNet (fun _ -> param.Common) "test" args - if not result.OK then failwithf "dotnet test failed with code %i" result.ExitCode - - - -/// Gets the DotNet SDK from the global.json -let GetDotNetSDKVersionFromGlobalJson() : string = - if not (File.Exists "global.json") then - failwithf "global.json not found" - try - let content = File.ReadAllText "global.json" - let json = JObject.Parse content - let sdk = json.Item("sdk") :?> JObject - let version = sdk.Property("version").Value.ToString() - version - with - | exn -> failwithf "Could not parse global.json: %s" exn.Message + FileName = fileName + WorkingDirectory = Path.GetTempPath() + Arguments = args } + ) TimeSpan.MaxValue + + if exitCode <> 0 then + // force download new installer script + Trace.traceError ".NET Core SDK install failed, trying to redownload installer..." + DotNetDownloadInstaller (param.InstallerOptions >> (fun o -> + { o with + AlwaysDownload = true + })) |> ignore + failwithf ".NET Core SDK install failed with code %i" exitCode + + /// dotnet restore verbosity + type Verbosity = + | Quiet + | Minimal + | Normal + | Detailed + | Diagnostic + + /// dotnet cli command execution options + type Options = + { + /// DotNet cli executable path + DotNetCliPath: string + /// Command working directory + WorkingDirectory: string + /// Custom parameters + CustomParams: string option + /// Logging verbosity (--verbosity) + Verbosity: Verbosity option + /// Restore logging verbosity (--verbosity) + Diagnostics: bool + /// If true the function will redirect the output of the called process (but will disable colors, false by default) + RedirectOutput : bool + /// Gets the environment variables that apply to this process and its child processes. + /// NOTE: Recommendation is to not use this Field, but instead use the helper function in the Proc module (for example Process.setEnvironmentVariable) + /// NOTE: This field is ignored when UseShellExecute is true. + Environment : Map + } + static member Create() = { + DotNetCliPath = dotnetCliPath DefaultDotNetCliDir + WorkingDirectory = Directory.GetCurrentDirectory() + CustomParams = None + Verbosity = None + Diagnostics = false + RedirectOutput = false + Environment = + Process.createEnvironmentMap() + |> Map.remove "MSBUILD_EXE_PATH" + |> Map.remove "MSBuildExtensionsPath" + } + [] + static member Default = Options.Create() + + /// Sets the current environment variables. + member x.WithEnvironment map = + { x with Environment = map } + + /// Sets a value indicating whether the output for the given process is redirected. + member x.WithRedirectOutput shouldRedirect = + { x with RedirectOutput = shouldRedirect } + + /// [omit] + let private argList2 name values = + values + |> Seq.collect (fun v -> ["--" + name; sprintf @"""%s""" v]) + |> String.concat " " + + /// [omit] + let private argOption name value = + match value with + | true -> sprintf "--%s" name + | false -> "" + + /// [omit] + let private buildCommonArgs (param: Options) = + [ defaultArg param.CustomParams "" + param.Verbosity |> Option.toList |> Seq.map (fun v -> v.ToString().ToLowerInvariant()) |> argList2 "verbosity" + ] |> Seq.filter (not << String.IsNullOrEmpty) |> String.concat " " + + /// [omit] + let private buildSdkOptionsArgs (param: Options) = + [ param.Diagnostics |> argOption "--diagostics" + ] |> Seq.filter (not << String.IsNullOrEmpty) |> String.concat " " + + /// Execute raw dotnet cli command + /// ## Parameters + /// + /// - 'options' - common execution options + /// - 'command' - the sdk command to execute 'test', 'new', 'build', ... + /// - 'args' - command arguments + let Raw (buildOptions: Options -> Options) command args = + let errors = new System.Collections.Generic.List() + let messages = new System.Collections.Generic.List() + let timeout = TimeSpan.MaxValue + + let errorF msg = + Trace.traceError msg + errors.Add msg + + let messageF msg = + Trace.trace msg + messages.Add msg + + let options = buildOptions (Options.Create()) + let sdkOptions = buildSdkOptionsArgs options + let commonOptions = buildCommonArgs options + let cmdArgs = sprintf "%s %s %s %s" sdkOptions command commonOptions args + + let result = + let f (info:ProcStartInfo) = + let dir = System.IO.Path.GetDirectoryName options.DotNetCliPath + let oldPath = + match options.Environment |> Map.tryFind "PATH" with + | None -> "" + | Some s -> s + { info with + FileName = options.DotNetCliPath + WorkingDirectory = options.WorkingDirectory + Arguments = cmdArgs } + |> Process.setEnvironment options.Environment + |> Process.setEnvironmentVariable "PATH" (sprintf "%s%c%s" dir System.IO.Path.PathSeparator oldPath) + + if options.RedirectOutput then + Process.ExecWithLambdas f timeout true errorF messageF + else Process.Exec f timeout + ProcessResult.New result messages errors + + /// dotnet restore command options + type RestoreOptions = + { + /// Common tool options + Common: Options + /// The runtime to restore for (seems added in RC4). Maybe a bug, but works. + Runtime: string option + /// Nuget feeds to search updates in. Use default if empty. + Sources: string list + /// Directory to install packages in (--packages). + Packages: string list + /// Path to the nuget configuration file (nuget.config). + ConfigFile: string option + /// No cache flag (--no-cache) + NoCache: bool + /// Only warning failed sources if there are packages meeting version requirement (--ignore-failed-sources) + IgnoreFailedSources: bool + /// Disables restoring multiple projects in parallel (--disable-parallel) + DisableParallel: bool + } + + /// Parameter default values. + static member Create() = { + Common = Options.Create() + Sources = [] + Runtime = None + Packages = [] + ConfigFile = None + NoCache = false + IgnoreFailedSources = false + DisableParallel = false + } + [] + static member Default = Options.Create() + + /// Gets the current environment + member x.Environment = x.Common.Environment + /// Sets the current environment variables. + member x.WithEnvironment map = + { x with Common = { x.Common with Environment = map } } + + /// Sets a value indicating whether the output for the given process is redirected. + member x.WithRedirectOutput shouldRedirect = + { x with Common = x.Common.WithRedirectOutput shouldRedirect } + + /// [omit] + let private buildRestoreArgs (param: RestoreOptions) = + [ param.Sources |> argList2 "source" + param.Packages |> argList2 "packages" + param.ConfigFile |> Option.toList |> argList2 "configFile" + param.NoCache |> argOption "no-cache" + param.Runtime |> Option.toList |> argList2 "runtime" + param.IgnoreFailedSources |> argOption "ignore-failed-sources" + param.DisableParallel |> argOption "disable-parallel" + ] |> Seq.filter (not << String.IsNullOrEmpty) |> String.concat " " + + + /// Execute dotnet restore command + /// ## Parameters + /// + /// - 'setParams' - set restore command parameters + /// - 'project' - project to restore packages + let Restore setParams project = + use __ = Trace.traceTask "DotNet:restore" project + let param = RestoreOptions.Create() |> setParams + let args = sprintf "%s %s" project (buildRestoreArgs param) + let result = Raw (fun _ -> param.Common) "restore" args + if not result.OK then failwithf "dotnet restore failed with code %i" result.ExitCode + + /// build configuration + type BuildConfiguration = + | Debug + | Release + | Custom of string + + /// [omit] + let private buildConfigurationArg (param: BuildConfiguration) = + sprintf "--configuration %s" + (match param with + | Debug -> "Debug" + | Release -> "Release" + | Custom config -> config) + + /// dotnet pack command options + type PackOptions = + { + /// Common tool options + Common: Options; + /// Pack configuration (--configuration) + Configuration: BuildConfiguration; + /// Version suffix to use + VersionSuffix: string option; + /// Build base path (--build-base-path) + BuildBasePath: string option; + /// Output path (--output) + OutputPath: string option; + /// No build flag (--no-build) + NoBuild: bool; + } + + /// Parameter default values. + static member Create() = { + Common = Options.Create() + Configuration = Release + VersionSuffix = None + BuildBasePath = None + OutputPath = None + NoBuild = false + } + [] + static member Default = PackOptions.Create() + /// Gets the current environment + member x.Environment = x.Common.Environment + /// Sets the current environment variables. + member x.WithEnvironment map = + { x with Common = { x.Common with Environment = map } } + /// Sets a value indicating whether the output for the given process is redirected. + member x.WithRedirectOutput shouldRedirect = + { x with Common = x.Common.WithRedirectOutput shouldRedirect } + + /// [omit] + let private buildPackArgs (param: PackOptions) = + [ + buildConfigurationArg param.Configuration + param.VersionSuffix |> Option.toList |> argList2 "version-suffix" + param.BuildBasePath |> Option.toList |> argList2 "build-base-path" + param.OutputPath |> Option.toList |> argList2 "output" + param.NoBuild |> argOption "no-build" + ] |> Seq.filter (not << String.IsNullOrEmpty) |> String.concat " " + + + /// Execute dotnet pack command + /// ## Parameters + /// + /// - 'setParams' - set pack command parameters + /// - 'project' - project to pack + let Pack setParams project = + use __ = Trace.traceTask "DotNet:pack" project + let param = PackOptions.Create() |> setParams + let args = sprintf "%s %s" project (buildPackArgs param) + let result = Raw (fun _ -> param.Common) "pack" args + if not result.OK then failwithf "dotnet pack failed with code %i" result.ExitCode + + /// dotnet --info command options + type InfoOptions = + { + /// Common tool options + Common: Options; + } + /// Parameter default values. + static member Create() = { + Common = Options.Create().WithRedirectOutput true + } + [] + static member Default = PackOptions.Create() + /// Gets the current environment + member x.Environment = x.Common.Environment + /// Sets the current environment variables. + member x.WithEnvironment map = + { x with Common = { x.Common with Environment = map } } + /// Sets a value indicating whether the output for the given process is redirected. + member x.WithRedirectOutput shouldRedirect = + { x with Common = x.Common.WithRedirectOutput shouldRedirect } + + /// dotnet info result + type InfoResult = + { + /// Common tool options + RID: string; + } + /// Execute dotnet --info command + /// ## Parameters + /// + /// - 'setParams' - set info command parameters + let Info setParams = + use __ = Trace.traceTask "DotNet:info" "running dotnet --info" + let param = InfoOptions.Create() |> setParams + let args = "--info" // project (buildPackArgs param) + let result = Raw (fun _ -> param.Common) "" args + if not result.OK then failwithf "dotnet --info failed with code %i" result.ExitCode + + let rid = + result.Messages + |> Seq.tryFind (fun m -> m.Contains "RID:") + |> Option.map (fun line -> line.Split([|':'|]).[1].Trim()) + + if rid.IsNone then failwithf "could not read rid from output: \n%s" (System.String.Join("\n", result.Messages)) + + { RID = rid.Value } + + /// dotnet publish command options + type PublishOptions = + { + /// Common tool options + Common: Options; + /// Pack configuration (--configuration) + Configuration: BuildConfiguration; + /// Target framework to compile for (--framework) + Framework: string option; + /// Target runtime to publish for (--runtime) + Runtime: string option; + /// Build base path (--build-base-path) + BuildBasePath: string option; + /// Output path (--output) + OutputPath: string option; + /// Defines what `*` should be replaced with in version field in project.json (--version-suffix) + VersionSuffix: string option; + /// No build flag (--no-build) + NoBuild: bool; + } + + /// Parameter default values. + static member Create() = { + Common = Options.Create() + Configuration = Release + Framework = None + Runtime = None + BuildBasePath = None + OutputPath = None + VersionSuffix = None + NoBuild = false + } + [] + static member Default = PublishOptions.Create() + /// Gets the current environment + member x.Environment = x.Common.Environment + /// Sets the current environment variables. + member x.WithEnvironment map = + { x with Common = { x.Common with Environment = map } } + /// Sets a value indicating whether the output for the given process is redirected. + member x.WithRedirectOutput shouldRedirect = + { x with Common = x.Common.WithRedirectOutput shouldRedirect } + + /// [omit] + let private buildPublishArgs (param: PublishOptions) = + [ + buildConfigurationArg param.Configuration + param.Framework |> Option.toList |> argList2 "framework" + param.Runtime |> Option.toList |> argList2 "runtime" + param.BuildBasePath |> Option.toList |> argList2 "build-base-path" + param.OutputPath |> Option.toList |> argList2 "output" + param.VersionSuffix |> Option.toList |> argList2 "version-suffix" + param.NoBuild |> argOption "no-build" + ] |> Seq.filter (not << String.IsNullOrEmpty) |> String.concat " " + + + /// Execute dotnet publish command + /// ## Parameters + /// + /// - 'setParams' - set publish command parameters + /// - 'project' - project to publish + let Publish setParams project = + use __ = Trace.traceTask "DotNet:publish" project + let param = PublishOptions.Create() |> setParams + let args = sprintf "%s %s" project (buildPublishArgs param) + let result = Raw (fun _ -> param.Common) "publish" args + if not result.OK then failwithf "dotnet publish failed with code %i" result.ExitCode + + /// dotnet build command options + type BuildOptions = + { + /// Common tool options + Common: Options; + /// Pack configuration (--configuration) + Configuration: BuildConfiguration; + /// Target framework to compile for (--framework) + Framework: string option; + /// Target runtime to publish for (--runtime) + Runtime: string option; + /// Build base path (--build-base-path) + BuildBasePath: string option; + /// Output path (--output) + OutputPath: string option; + /// Native flag (--native) + Native: bool; + } + + /// Parameter default values. + static member Create() = { + Common = Options.Create() + Configuration = Release + Framework = None + Runtime = None + BuildBasePath = None + OutputPath = None + Native = false + } + [] + static member Default = BuildOptions.Create() + /// Gets the current environment + member x.Environment = x.Common.Environment + /// Sets the current environment variables. + member x.WithEnvironment map = + { x with Common = { x.Common with Environment = map } } + /// Sets a value indicating whether the output for the given process is redirected. + member x.WithRedirectOutput shouldRedirect = + { x with Common = x.Common.WithRedirectOutput shouldRedirect } + + + /// [omit] + let private buildBuildArgs (param: BuildOptions) = + [ + buildConfigurationArg param.Configuration + param.Framework |> Option.toList |> argList2 "framework" + param.Runtime |> Option.toList |> argList2 "runtime" + param.BuildBasePath |> Option.toList |> argList2 "build-base-path" + param.OutputPath |> Option.toList |> argList2 "output" + (if param.Native then "--native" else "") + ] |> Seq.filter (not << String.IsNullOrEmpty) |> String.concat " " + + + /// Execute dotnet build command + /// ## Parameters + /// + /// - 'setParams' - set compile command parameters + /// - 'project' - project to compile + let Compile setParams project = + use __ = Trace.traceTask "DotNet:build" project + let param = BuildOptions.Create() |> setParams + let args = sprintf "%s %s" project (buildBuildArgs param) + let result = Raw (fun _ -> param.Common) "build" args + if not result.OK then failwithf "dotnet build failed with code %i" result.ExitCode + + let Build = Compile + + + /// dotnet build command options + type TestOptions = + { + /// Common tool options + Common: Options + /// Settings to use when running tests (--settings) + Settings: string option + /// Lists discovered tests (--list-tests) + ListTests: bool + /// Run tests that match the given expression. (--filter) + /// Examples: + /// Run tests with priority set to 1: --filter "Priority = 1" + /// Run a test with the specified full name: --filter "FullyQualifiedName=Namespace.ClassName.MethodName" + /// Run tests that contain the specified name: --filter "FullyQualifiedName~Namespace.Class" + /// More info on filtering support: https://aka.ms/vstest-filtering + Filter: string option + /// Use custom adapters from the given path in the test run. (--test-adapter-path) + TestAdapterPath: string option + /// Specify a logger for test results. (--logger) + Logger: string option + ///Configuration to use for building the project. Default for most projects is "Debug". (--configuration) + Configuration: BuildConfiguration + /// Target framework to publish for. The target framework has to be specified in the project file. (--framework) + Framework: string option + /// Directory in which to find the binaries to be run (--output) + Output: string option + /// Enable verbose logs for test platform. Logs are written to the provided file. (--diag) + Diag: string option + /// Do not build project before testing. (--no-build) + NoBuild: bool + /// The directory where the test results are going to be placed. The specified directory will be created if it does not exist. (--results-directory) + ResultsDirectory: string option + /// Enables data collector for the test run. More info here : https://aka.ms/vstest-collect (--collect) + Collect: string option + /// Does not do an implicit restore when executing the command. (--no-restore) + NoRestore: bool + /// Arguments to pass runsettings configurations through commandline. Arguments may be specified as name-value pair of the form [name]=[value] after "-- ". Note the space after --. + RunSettingsArguments : string option + } + + /// Parameter default values. + static member Create() = { + Common = Options.Create() + Settings = None + ListTests = false + Filter = None + TestAdapterPath = None + Logger = None + Configuration = BuildConfiguration.Debug + Framework = None + Output = None + Diag = None + NoBuild = false + ResultsDirectory = None + Collect = None + NoRestore = false + RunSettingsArguments = None + } + [] + static member Default = TestOptions.Create() + /// Gets the current environment + member x.Environment = x.Common.Environment + /// Sets the current environment variables. + member x.WithEnvironment map = + { x with Common = { x.Common with Environment = map } } + /// Sets a value indicating whether the output for the given process is redirected. + member x.WithRedirectOutput shouldRedirect = + { x with Common = x.Common.WithRedirectOutput shouldRedirect } + + + /// [omit] + let private buildTestArgs (param: TestOptions) = + [ + param.Settings |> Option.toList |> argList2 "settings" + param.ListTests |> argOption "list-tests" + param.Filter |> Option.toList |> argList2 "filter" + param.TestAdapterPath |> Option.toList |> argList2 "test-adapter-path" + param.Logger |> Option.toList |> argList2 "logger" + buildConfigurationArg param.Configuration + param.Framework |> Option.toList |> argList2 "framework" + param.Output |> Option.toList |> argList2 "output" + param.Diag |> Option.toList |> argList2 "diag" + param.NoBuild |> argOption "no-build" + param.ResultsDirectory |> Option.toList |> argList2 "results-directory" + param.Collect |> Option.toList |> argList2 "collect" + param.NoRestore |> argOption "no-restore" + ] |> Seq.filter (not << String.IsNullOrEmpty) |> String.concat " " + + + /// Execute dotnet build command + /// ## Parameters + /// + /// - 'setParams' - set compile command parameters + /// - 'project' - project to compile + let Test setParams project = + use __ = Trace.traceTask "DotNet:test" project + let param = TestOptions.Create() |> setParams + let args = sprintf "%s %s" project (buildTestArgs param) + let result = Raw (fun _ -> param.Common) "test" args + if not result.OK then failwithf "dotnet test failed with code %i" result.ExitCode + + + + /// Gets the DotNet SDK from the global.json + let GetSDKVersionFromGlobalJson() : string = + if not (File.Exists "global.json") then + failwithf "global.json not found" + try + let content = File.ReadAllText "global.json" + let json = JObject.Parse content + let sdk = json.Item("sdk") :?> JObject + let version = sdk.Property("version").Value.ToString() + version + with + | exn -> failwithf "Could not parse global.json: %s" exn.Message From 479f450faba487e860664fe7e590e925c4cc1392 Mon Sep 17 00:00:00 2001 From: Maxime Mangel Date: Wed, 7 Mar 2018 10:16:39 +0100 Subject: [PATCH 2/5] Fix Fake.DotNet.Cli tests --- src/test/Fake.Core.UnitTests/Fake.DotNet.Cli.fs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/test/Fake.Core.UnitTests/Fake.DotNet.Cli.fs b/src/test/Fake.Core.UnitTests/Fake.DotNet.Cli.fs index a9058c76eb2..b9cbf2ec424 100644 --- a/src/test/Fake.Core.UnitTests/Fake.DotNet.Cli.fs +++ b/src/test/Fake.Core.UnitTests/Fake.DotNet.Cli.fs @@ -1,17 +1,16 @@ module Fake.DotNet.CliTests open Fake.Core -open Fake.DotNet +open Fake.DotNet.Cli open Expecto [] -let tests = +let tests = testList "Fake.DotNet.Cli.Tests" [ testCase "Test that we can use Process-Helpers on Cli Paramters" <| fun _ -> let cli = - Cli.DotNetOptions.Create() + DotNet.Options.Create() |> Process.setEnvironmentVariable "Somevar" "someval" - + Expect.equal cli.Environment.["Somevar"] "someval" "Retrieving the correct environment variable failed." ] - \ No newline at end of file From b0f65d332f62826ee0af092fc5081efb60670ce1 Mon Sep 17 00:00:00 2001 From: Maxime Mangel Date: Wed, 7 Mar 2018 13:22:25 +0100 Subject: [PATCH 3/5] Bootstrap build.fsx --- build.fsx | 62 ++++++++++++++++++++++++++++++- src/app/Fake.DotNet.Cli/DotNet.fs | 6 +-- 2 files changed, 64 insertions(+), 4 deletions(-) diff --git a/build.fsx b/build.fsx index 5cc3ef887e6..0d68e21fa54 100644 --- a/build.fsx +++ b/build.fsx @@ -59,7 +59,12 @@ open Fake.IO open Fake.IO.FileSystemOperators open Fake.IO.Globbing.Operators open Fake.Windows +#if BOOTSTRAP open Fake.DotNet +open Fake.DotNet.Cli +#else +open Fake.DotNet +#endif open Fake.DotNet.Testing // properties @@ -411,13 +416,22 @@ Target.Create "DotNetCoreIntegrationTests" (fun _ -> |> NUnit3.NUnit3 id ) +#if BOOTSTRAP +let withWorkDir wd (cliOpts: DotNet.Options) = { cliOpts with WorkingDirectory = wd } +#else let withWorkDir wd (cliOpts:Cli.DotNetOptions) = { cliOpts with WorkingDirectory = wd } +#endif Target.Create "DotNetCoreUnitTests" (fun _ -> // dotnet run -p src/test/Fake.Core.UnitTests/Fake.Core.UnitTests.fsproj + #if BOOTSTRAP + let processResult = + DotNet.Raw (withWorkDir root) "src/test/Fake.Core.UnitTests/bin/Release/netcoreapp2.0/Fake.Core.UnitTests.dll" "--summary" + #else let processResult = Cli.DotNet (withWorkDir root) "src/test/Fake.Core.UnitTests/bin/Release/netcoreapp2.0/Fake.Core.UnitTests.dll" "--summary" - if processResult.ExitCode <> 0 then failwithf "Unit-Tests failed." + #endif + if processResult.ExitCode <> 0 then failwithf "Unit-Tests failed." ) Target.Create "BootstrapTest" (fun _ -> @@ -647,6 +661,16 @@ Target.Create "CreateNuGet" (fun _ -> let LatestTooling options = + #if BOOTSTRAP + { options with + DotNet.InstallerOptions = (fun io -> + { io with + Branch = "release/2.1" + }) + DotNet.Channel = None + DotNet.Version = DotNet.Version "2.1.4" + } + #else { options with Cli.InstallerOptions = (fun io -> { io with @@ -655,9 +679,18 @@ let LatestTooling options = Cli.Channel = None Cli.Version = Cli.Version "2.1.4" } + #endif + +#if BOOTSTRAP +Target.Create "InstallDotNetCore" (fun _ -> + DotNet.Install DotNet.Release_2_1_4 +) +#else Target.Create "InstallDotNetCore" (fun _ -> + Cli.DotNetCliInstall Cli.Release_2_1_4 ) +#endif let netCoreProjs = !! (appDir "*/*.fsproj") @@ -687,6 +720,15 @@ Target.Create "DotNetPackage_" (fun _ -> // dotnet pack + #if BOOTSTRAP + DotNet.Pack (fun c -> + { c with + Configuration = DotNet.Release + OutputPath = Some nugetDir + }) "Fake.sln" + + let info = DotNet.Info id + #else Cli.DotNetPack (fun c -> { c with Configuration = Cli.Release @@ -694,6 +736,7 @@ Target.Create "DotNetPackage_" (fun _ -> }) "Fake.sln" let info = Cli.DotNetInfo id + #endif // dotnet publish runtimes @@ -710,12 +753,21 @@ Target.Create "DotNetPackage_" (fun _ -> //DotNetRestore (fun c -> {c with Runtime = Some runtime}) proj let outDir = nugetDir @@ projName @@ runtimeName + #if BOOTSTRAP + DotNet.Publish (fun c -> + { c with + Runtime = Some runtime + Configuration = DotNet.Release + OutputPath = Some outDir + }) proj + #else Cli.DotNetPublish (fun c -> { c with Runtime = Some runtime Configuration = Cli.Release OutputPath = Some outDir }) proj + #endif let source = outDir "dotnet" if File.Exists source then failwithf "Workaround no longer required?" //TODO: If this is not triggered delete this block @@ -729,11 +781,19 @@ Target.Create "DotNetPackage_" (fun _ -> // Publish portable as well (see https://docs.microsoft.com/en-us/dotnet/articles/core/app-types) let netcoreFsproj = appDir "Fake.netcore/Fake.netcore.fsproj" let outDir = nugetDir @@ "Fake.netcore" @@ "portable" + #if BOOTSTRAP + DotNet.Publish (fun c -> + { c with + Framework = Some "netcoreapp2.0" + OutputPath = Some outDir + }) netcoreFsproj + #else Cli.DotNetPublish (fun c -> { c with Framework = Some "netcoreapp2.0" OutputPath = Some outDir }) netcoreFsproj + #endif ) diff --git a/src/app/Fake.DotNet.Cli/DotNet.fs b/src/app/Fake.DotNet.Cli/DotNet.fs index 45e7970589f..cc1115b79fd 100644 --- a/src/app/Fake.DotNet.Cli/DotNet.fs +++ b/src/app/Fake.DotNet.Cli/DotNet.fs @@ -103,7 +103,7 @@ module DotNet = /// ## Parameters /// /// - 'setParams' - set download installer options - let DotNetDownloadInstaller setParams = + let DownloadInstaller setParams = let param = InstallerOptions.Default |> setParams let ext = if Environment.isUnix then "sh" else "ps1" @@ -315,7 +315,7 @@ module DotNet = /// - 'setParams' - set installation options let Install setParams = let param = CliInstallOptions.Default |> setParams - let installScript = DotNetDownloadInstaller param.InstallerOptions + let installScript = DownloadInstaller param.InstallerOptions let exitCode = let args, fileName = @@ -346,7 +346,7 @@ module DotNet = if exitCode <> 0 then // force download new installer script Trace.traceError ".NET Core SDK install failed, trying to redownload installer..." - DotNetDownloadInstaller (param.InstallerOptions >> (fun o -> + DownloadInstaller (param.InstallerOptions >> (fun o -> { o with AlwaysDownload = true })) |> ignore From afc6bba9d87d17206b875ba471b8a6d7496949fa Mon Sep 17 00:00:00 2001 From: Maxime Mangel Date: Thu, 8 Mar 2018 10:51:43 +0100 Subject: [PATCH 4/5] Remove Cli from namespace and try to fix CI --- RELEASE_NOTES.md | 4 ++++ build.fsx | 5 ----- src/app/Fake.DotNet.Cli/DotNet.fs | 2 +- src/test/Fake.Core.UnitTests/Fake.DotNet.Cli.fs | 2 +- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index b9417e98461..2d1dcb6549c 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,3 +1,7 @@ +#### 5.0.0-beta024 + +* ENHANCEMENT: Refactor Dotnet API - https://github.com/fsharp/FAKE/issues/1808 + #### 5.0.0-beta023 * [CORE-PROCESS] ENHANCEMENT: Experiment with new Process API * [CORE-TRACE] ENHANCEMENT: Add `TraceSecrets`-API to prevent FAKE from printing secrets diff --git a/build.fsx b/build.fsx index 0d68e21fa54..0d607d99819 100644 --- a/build.fsx +++ b/build.fsx @@ -59,12 +59,7 @@ open Fake.IO open Fake.IO.FileSystemOperators open Fake.IO.Globbing.Operators open Fake.Windows -#if BOOTSTRAP -open Fake.DotNet -open Fake.DotNet.Cli -#else open Fake.DotNet -#endif open Fake.DotNet.Testing // properties diff --git a/src/app/Fake.DotNet.Cli/DotNet.fs b/src/app/Fake.DotNet.Cli/DotNet.fs index cc1115b79fd..32330f8ab7e 100644 --- a/src/app/Fake.DotNet.Cli/DotNet.fs +++ b/src/app/Fake.DotNet.Cli/DotNet.fs @@ -3,7 +3,7 @@ // This probably needs to stay within Fake to bootstrap? // Currently last file in FakeLib, until all dependencies are available in dotnetcore. /// .NET Core + CLI tools helpers -module Fake.DotNet.Cli +namespace Fake.DotNet [] module DotNet = diff --git a/src/test/Fake.Core.UnitTests/Fake.DotNet.Cli.fs b/src/test/Fake.Core.UnitTests/Fake.DotNet.Cli.fs index b9cbf2ec424..79c6674cc01 100644 --- a/src/test/Fake.Core.UnitTests/Fake.DotNet.Cli.fs +++ b/src/test/Fake.Core.UnitTests/Fake.DotNet.Cli.fs @@ -1,7 +1,7 @@ module Fake.DotNet.CliTests open Fake.Core -open Fake.DotNet.Cli +open Fake.DotNet open Expecto [] From 1650f6611179f27114b13a460c84b8825d583ec9 Mon Sep 17 00:00:00 2001 From: Maxime Mangel Date: Thu, 8 Mar 2018 11:15:11 +0100 Subject: [PATCH 5/5] Rename DotNet.Raw into DotNet.Exec --- build.fsx | 2 +- src/app/Fake.DotNet.Cli/DotNet.fs | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/build.fsx b/build.fsx index 0d607d99819..e051ebeaa6b 100644 --- a/build.fsx +++ b/build.fsx @@ -421,7 +421,7 @@ Target.Create "DotNetCoreUnitTests" (fun _ -> // dotnet run -p src/test/Fake.Core.UnitTests/Fake.Core.UnitTests.fsproj #if BOOTSTRAP let processResult = - DotNet.Raw (withWorkDir root) "src/test/Fake.Core.UnitTests/bin/Release/netcoreapp2.0/Fake.Core.UnitTests.dll" "--summary" + DotNet.Exec (withWorkDir root) "src/test/Fake.Core.UnitTests/bin/Release/netcoreapp2.0/Fake.Core.UnitTests.dll" "--summary" #else let processResult = Cli.DotNet (withWorkDir root) "src/test/Fake.Core.UnitTests/bin/Release/netcoreapp2.0/Fake.Core.UnitTests.dll" "--summary" diff --git a/src/app/Fake.DotNet.Cli/DotNet.fs b/src/app/Fake.DotNet.Cli/DotNet.fs index 32330f8ab7e..2210a17fb1a 100644 --- a/src/app/Fake.DotNet.Cli/DotNet.fs +++ b/src/app/Fake.DotNet.Cli/DotNet.fs @@ -432,7 +432,7 @@ module DotNet = /// - 'options' - common execution options /// - 'command' - the sdk command to execute 'test', 'new', 'build', ... /// - 'args' - command arguments - let Raw (buildOptions: Options -> Options) command args = + let Exec (buildOptions: Options -> Options) command args = let errors = new System.Collections.Generic.List() let messages = new System.Collections.Generic.List() let timeout = TimeSpan.MaxValue @@ -535,7 +535,7 @@ module DotNet = use __ = Trace.traceTask "DotNet:restore" project let param = RestoreOptions.Create() |> setParams let args = sprintf "%s %s" project (buildRestoreArgs param) - let result = Raw (fun _ -> param.Common) "restore" args + let result = Exec (fun _ -> param.Common) "restore" args if not result.OK then failwithf "dotnet restore failed with code %i" result.ExitCode /// build configuration @@ -609,7 +609,7 @@ module DotNet = use __ = Trace.traceTask "DotNet:pack" project let param = PackOptions.Create() |> setParams let args = sprintf "%s %s" project (buildPackArgs param) - let result = Raw (fun _ -> param.Common) "pack" args + let result = Exec (fun _ -> param.Common) "pack" args if not result.OK then failwithf "dotnet pack failed with code %i" result.ExitCode /// dotnet --info command options @@ -647,7 +647,7 @@ module DotNet = use __ = Trace.traceTask "DotNet:info" "running dotnet --info" let param = InfoOptions.Create() |> setParams let args = "--info" // project (buildPackArgs param) - let result = Raw (fun _ -> param.Common) "" args + let result = Exec (fun _ -> param.Common) "" args if not result.OK then failwithf "dotnet --info failed with code %i" result.ExitCode let rid = @@ -724,7 +724,7 @@ module DotNet = use __ = Trace.traceTask "DotNet:publish" project let param = PublishOptions.Create() |> setParams let args = sprintf "%s %s" project (buildPublishArgs param) - let result = Raw (fun _ -> param.Common) "publish" args + let result = Exec (fun _ -> param.Common) "publish" args if not result.OK then failwithf "dotnet publish failed with code %i" result.ExitCode /// dotnet build command options @@ -789,7 +789,7 @@ module DotNet = use __ = Trace.traceTask "DotNet:build" project let param = BuildOptions.Create() |> setParams let args = sprintf "%s %s" project (buildBuildArgs param) - let result = Raw (fun _ -> param.Common) "build" args + let result = Exec (fun _ -> param.Common) "build" args if not result.OK then failwithf "dotnet build failed with code %i" result.ExitCode let Build = Compile @@ -893,7 +893,7 @@ module DotNet = use __ = Trace.traceTask "DotNet:test" project let param = TestOptions.Create() |> setParams let args = sprintf "%s %s" project (buildTestArgs param) - let result = Raw (fun _ -> param.Common) "test" args + let result = Exec (fun _ -> param.Common) "test" args if not result.OK then failwithf "dotnet test failed with code %i" result.ExitCode