From 05a5e894fd23335a8e2c72d86b804dcf498c4a21 Mon Sep 17 00:00:00 2001 From: dawe Date: Thu, 9 Nov 2023 16:23:17 +0100 Subject: [PATCH 1/8] Support MSBuild properties on the CLI --- src/FSharp.Analyzers.Cli/Program.fs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/FSharp.Analyzers.Cli/Program.fs b/src/FSharp.Analyzers.Cli/Program.fs index cb1b63b..e65036d 100644 --- a/src/FSharp.Analyzers.Cli/Program.fs +++ b/src/FSharp.Analyzers.Cli/Program.fs @@ -13,6 +13,7 @@ open Ionide.ProjInfo type Arguments = | Project of string list | Analyzers_Path of string list + | [] Property of string * string | [] Treat_As_Info of string list | [] Treat_As_Hint of string list | [] Treat_As_Warning of string list @@ -28,6 +29,7 @@ type Arguments = match s with | Project _ -> "Path to your .fsproj file." | Analyzers_Path _ -> "Path to a folder where your analyzers are located." + | Property _ -> "A key=value pair of an MSBuild property." | Treat_As_Info _ -> "List of analyzer codes that should be treated as severity Info by the tool. Regardless of the original severity." | Treat_As_Hint _ -> @@ -108,9 +110,9 @@ let printError (text: string) : unit = Console.WriteLine(text) Console.ForegroundColor <- origForegroundColor -let loadProject toolsPath projPath = +let loadProject toolsPath properties projPath = async { - let loader = WorkspaceLoader.Create(toolsPath) + let loader = WorkspaceLoader.Create(toolsPath, properties) let parsed = loader.LoadProjects [ projPath ] |> Seq.toList if parsed.IsEmpty then @@ -165,13 +167,14 @@ let runProjectAux let runProject (client: Client) toolsPath + properties proj (globs: Glob list) (mappings: SeverityMappings) = async { let path = Path.Combine(Environment.CurrentDirectory, proj) |> Path.GetFullPath - let! option = loadProject toolsPath path + let! option = loadProject toolsPath properties path return! runProjectAux client option globs mappings } @@ -436,6 +439,7 @@ let main argv = printInfo "Registered %d analyzers from %d dlls" analyzers dlls + let properties = results.GetResults <@ Property @> let projOpts = results.GetResults <@ Project @> |> List.concat let fscArgs = results.TryGetResult <@ FSC_Args @> let report = results.TryGetResult <@ Report @> @@ -460,7 +464,9 @@ let main argv = exit 1 projects - |> List.map (fun projPath -> runProject client toolsPath projPath ignoreFiles severityMapping) + |> List.map (fun projPath -> + runProject client toolsPath properties projPath ignoreFiles severityMapping + ) |> Async.Sequential |> Async.RunSynchronously |> Array.choose id From e3769839e25a63b8a19ddb149a68b21f378df1de Mon Sep 17 00:00:00 2001 From: dawe Date: Fri, 10 Nov 2023 10:13:58 +0100 Subject: [PATCH 2/8] Also support MSBuild style "-p:" properties on the CLI --- src/FSharp.Analyzers.Cli/Program.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FSharp.Analyzers.Cli/Program.fs b/src/FSharp.Analyzers.Cli/Program.fs index e65036d..af30ad8 100644 --- a/src/FSharp.Analyzers.Cli/Program.fs +++ b/src/FSharp.Analyzers.Cli/Program.fs @@ -13,7 +13,7 @@ open Ionide.ProjInfo type Arguments = | Project of string list | Analyzers_Path of string list - | [] Property of string * string + | [] Property of string * string | [] Treat_As_Info of string list | [] Treat_As_Hint of string list | [] Treat_As_Warning of string list From 026417010d6237b4d4fabf52a41e4034eaa79419 Mon Sep 17 00:00:00 2001 From: dawe Date: Fri, 10 Nov 2023 15:39:02 +0100 Subject: [PATCH 3/8] support multiple MSBuild properties in one -p CLI flag --- src/FSharp.Analyzers.Cli/Program.fs | 40 +++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/src/FSharp.Analyzers.Cli/Program.fs b/src/FSharp.Analyzers.Cli/Program.fs index af30ad8..a2f893a 100644 --- a/src/FSharp.Analyzers.Cli/Program.fs +++ b/src/FSharp.Analyzers.Cli/Program.fs @@ -1,6 +1,7 @@ open System open System.IO open System.Runtime.Loader +open System.Text.RegularExpressions open FSharp.Compiler.CodeAnalysis open FSharp.Compiler.Text open Argu @@ -356,6 +357,33 @@ let calculateExitCode (msgs: AnalyzerMessage list option) : int = if check then -2 else 0 +/// If multiple MSBuild properties are given in one -p flag like -p:prop1="val1a;val1b;val1c";prop2="1;2;3";prop3=val3 +/// argu will think it means prop1 has the value: "val1a;val1b;val1c";prop2="1;2;3";prop3=val3 +/// so this function expands the value into multiple key-value properties +let expandMultiProperties (properties: (string * string) list) = + properties + |> List.map (fun (k, v) -> + if not (v.Contains('=')) then // no multi properties given to expand + [ (k, v) ] + else + let regex = Regex(";([a-z,A-Z,0-9,_,-]*)=") + let splits = regex.Split(v) + + [ + yield (k, splits[0]) + + for pair in splits.[1..] |> Seq.chunkBySize 2 do + match pair with + | [| k; v |] when String.IsNullOrWhiteSpace(v) -> + printError $"Missing property value for '{k}'" + exit 1 + | [| k; v |] -> yield (k, v) + | _ -> () + + ] + ) + |> List.concat + [] let main argv = let toolsPath = Init.init (DirectoryInfo Environment.CurrentDirectory) None @@ -382,9 +410,16 @@ let main argv = exit 1 + let projOpts = results.GetResults <@ Project @> |> List.concat + let fscArgs = results.TryGetResult <@ FSC_Args @> + let report = results.TryGetResult <@ Report @> let ignoreFiles = results.GetResult(<@ Ignore_Files @>, []) printInfo "Ignore Files: [%s]" (ignoreFiles |> String.concat ", ") let ignoreFiles = ignoreFiles |> List.map Glob + let properties = results.GetResults <@ Property @> |> expandMultiProperties + + if verbose then + properties |> List.iter (fun (k, v) -> printInfo $"Property %s{k}=%s{v}") let analyzersPaths = results.GetResults(<@ Analyzers_Path @>) @@ -439,11 +474,6 @@ let main argv = printInfo "Registered %d analyzers from %d dlls" analyzers dlls - let properties = results.GetResults <@ Property @> - let projOpts = results.GetResults <@ Project @> |> List.concat - let fscArgs = results.TryGetResult <@ FSC_Args @> - let report = results.TryGetResult <@ Report @> - let results = if analyzers = 0 then Some [] From b5b867d3af9a87122167ef241c3205753474a25f Mon Sep 17 00:00:00 2001 From: dawe Date: Fri, 10 Nov 2023 17:07:43 +0100 Subject: [PATCH 4/8] support .NET CLI -c for the configuration property --- src/FSharp.Analyzers.Cli/Program.fs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/FSharp.Analyzers.Cli/Program.fs b/src/FSharp.Analyzers.Cli/Program.fs index a2f893a..6de4135 100644 --- a/src/FSharp.Analyzers.Cli/Program.fs +++ b/src/FSharp.Analyzers.Cli/Program.fs @@ -15,6 +15,7 @@ type Arguments = | Project of string list | Analyzers_Path of string list | [] Property of string * string + | [] Configuration of string | [] Treat_As_Info of string list | [] Treat_As_Hint of string list | [] Treat_As_Warning of string list @@ -31,6 +32,7 @@ type Arguments = | Project _ -> "Path to your .fsproj file." | Analyzers_Path _ -> "Path to a folder where your analyzers are located." | Property _ -> "A key=value pair of an MSBuild property." + | Configuration _ -> "The configuration to use, e.g. Debug or Release." | Treat_As_Info _ -> "List of analyzer codes that should be treated as severity Info by the tool. Regardless of the original severity." | Treat_As_Hint _ -> @@ -416,7 +418,15 @@ let main argv = let ignoreFiles = results.GetResult(<@ Ignore_Files @>, []) printInfo "Ignore Files: [%s]" (ignoreFiles |> String.concat ", ") let ignoreFiles = ignoreFiles |> List.map Glob - let properties = results.GetResults <@ Property @> |> expandMultiProperties + let configuration = results.TryGetResult <@ Configuration @> + + let properties = + results.GetResults <@ Property @> + |> expandMultiProperties + |> fun props -> + configuration + |> Option.map (fun c -> List.append props [ "Configuration", c ]) + |> Option.defaultValue props if verbose then properties |> List.iter (fun (k, v) -> printInfo $"Property %s{k}=%s{v}") From f6fa96dd7a5d3d5f0cb0a2c25a3a4aa09fa3c9fb Mon Sep 17 00:00:00 2001 From: dawe Date: Fri, 10 Nov 2023 18:11:52 +0100 Subject: [PATCH 5/8] support -r -a -os .NET CLI flags --- src/FSharp.Analyzers.Cli/Program.fs | 40 ++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/src/FSharp.Analyzers.Cli/Program.fs b/src/FSharp.Analyzers.Cli/Program.fs index 6de4135..550de99 100644 --- a/src/FSharp.Analyzers.Cli/Program.fs +++ b/src/FSharp.Analyzers.Cli/Program.fs @@ -16,6 +16,9 @@ type Arguments = | Analyzers_Path of string list | [] Property of string * string | [] Configuration of string + | [] Runtime of string + | [] Arch of string + | [] Os of string | [] Treat_As_Info of string list | [] Treat_As_Hint of string list | [] Treat_As_Warning of string list @@ -33,6 +36,9 @@ type Arguments = | Analyzers_Path _ -> "Path to a folder where your analyzers are located." | Property _ -> "A key=value pair of an MSBuild property." | Configuration _ -> "The configuration to use, e.g. Debug or Release." + | Runtime _ -> "The runtime identifier (RID)." + | Arch _ -> "The target architecture." + | Os _ -> "The target operating system." | Treat_As_Info _ -> "List of analyzer codes that should be treated as severity Info by the tool. Regardless of the original severity." | Treat_As_Hint _ -> @@ -386,6 +392,30 @@ let expandMultiProperties (properties: (string * string) list) = ) |> List.concat +let getProperties (results: ParseResults) = + results.GetResults <@ Property @> + |> expandMultiProperties + |> fun props -> + [ + yield! props + + match results.TryGetResult <@ Configuration @> with + | (Some x) -> yield ("Configuration", x) + | _ -> () + + match results.TryGetResult <@ Runtime @> with + | (Some x) -> yield ("RuntimeIdentifier", x) + | _ -> () + + match results.TryGetResult <@ Arch @> with + | (Some x) -> yield ("Platform", x) + | _ -> () + + match results.TryGetResult <@ Os @> with + | (Some x) -> yield ("OS", x) + | _ -> () + ] + [] let main argv = let toolsPath = Init.init (DirectoryInfo Environment.CurrentDirectory) None @@ -418,15 +448,7 @@ let main argv = let ignoreFiles = results.GetResult(<@ Ignore_Files @>, []) printInfo "Ignore Files: [%s]" (ignoreFiles |> String.concat ", ") let ignoreFiles = ignoreFiles |> List.map Glob - let configuration = results.TryGetResult <@ Configuration @> - - let properties = - results.GetResults <@ Property @> - |> expandMultiProperties - |> fun props -> - configuration - |> Option.map (fun c -> List.append props [ "Configuration", c ]) - |> Option.defaultValue props + let properties = getProperties results if verbose then properties |> List.iter (fun (k, v) -> printInfo $"Property %s{k}=%s{v}") From 46b45ed41012678e8f66e6952783c8dd2cbc52f1 Mon Sep 17 00:00:00 2001 From: dawe Date: Fri, 10 Nov 2023 19:38:42 +0100 Subject: [PATCH 6/8] construct RuntimeIdentifier from arch and os if given --- src/FSharp.Analyzers.Cli/Program.fs | 40 ++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/src/FSharp.Analyzers.Cli/Program.fs b/src/FSharp.Analyzers.Cli/Program.fs index 550de99..c2e806d 100644 --- a/src/FSharp.Analyzers.Cli/Program.fs +++ b/src/FSharp.Analyzers.Cli/Program.fs @@ -1,6 +1,7 @@ open System open System.IO open System.Runtime.Loader +open System.Runtime.InteropServices open System.Text.RegularExpressions open FSharp.Compiler.CodeAnalysis open FSharp.Compiler.Text @@ -392,7 +393,36 @@ let expandMultiProperties (properties: (string * string) list) = ) |> List.concat +let validateRuntimeOsArchCombination (runtime, arch, os) = + match runtime, os, arch with + | Some _, Some _, _ -> + printError "Specifying both the `-r|--runtime` and `-os` options is not supported." + exit 1 + | Some _, _, Some _ -> + printError "Specifying both the `-r|--runtime` and `-a|--arch` options is not supported." + exit 1 + | _ -> () + let getProperties (results: ParseResults) = + let runtime = results.TryGetResult <@ Runtime @> + let arch = results.TryGetResult <@ Arch @> + let os = results.TryGetResult <@ Os @> + validateRuntimeOsArchCombination (runtime, os, arch) + + let runtimeProp = + let rid = RuntimeInformation.RuntimeIdentifier // assuming we always get something like 'linux-x64' + + match runtime, os, arch with + | Some r, _, _ -> Some r + | None, Some o, Some a -> Some $"{o}-{a}" + | None, Some o, None -> + let archOfRid = rid.Substring(rid.LastIndexOf('-') + 1) + Some $"{o}-{archOfRid}" + | None, None, Some a -> + let osOfRid = rid.Substring(0, rid.LastIndexOf('-')) + Some $"{osOfRid}-{a}" + | _ -> None + results.GetResults <@ Property @> |> expandMultiProperties |> fun props -> @@ -403,17 +433,9 @@ let getProperties (results: ParseResults) = | (Some x) -> yield ("Configuration", x) | _ -> () - match results.TryGetResult <@ Runtime @> with + match runtimeProp with | (Some x) -> yield ("RuntimeIdentifier", x) | _ -> () - - match results.TryGetResult <@ Arch @> with - | (Some x) -> yield ("Platform", x) - | _ -> () - - match results.TryGetResult <@ Os @> with - | (Some x) -> yield ("OS", x) - | _ -> () ] [] From 5dedd2ed1d06f18087e303ef996adab8fb9c63af Mon Sep 17 00:00:00 2001 From: dawe Date: Mon, 13 Nov 2023 12:10:33 +0100 Subject: [PATCH 7/8] exit if fsc-args are combined with properties --- src/FSharp.Analyzers.Cli/Program.fs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/FSharp.Analyzers.Cli/Program.fs b/src/FSharp.Analyzers.Cli/Program.fs index c2e806d..03dd856 100644 --- a/src/FSharp.Analyzers.Cli/Program.fs +++ b/src/FSharp.Analyzers.Cli/Program.fs @@ -472,6 +472,10 @@ let main argv = let ignoreFiles = ignoreFiles |> List.map Glob let properties = getProperties results + if Option.isSome fscArgs && not properties.IsEmpty then + printError "fsc-args can't be combined with MSBuild properties." + exit 1 + if verbose then properties |> List.iter (fun (k, v) -> printInfo $"Property %s{k}=%s{v}") From 755ace8f3370c536a42cd4e1e850d2f32d76d2b0 Mon Sep 17 00:00:00 2001 From: dawe Date: Mon, 13 Nov 2023 14:00:22 +0100 Subject: [PATCH 8/8] add changelog entry --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d3b172..d1a4fb8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased] +## [0.20.0] - 2023-11-13 ### Fixed * [--project value should be tested if path exists](https://github.com/ionide/FSharp.Analyzers.SDK/issues/141) (thanks @dawedawe!) @@ -13,6 +13,7 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). * [Hint is mapped as note in sarif export](https://github.com/ionide/FSharp.Analyzers.SDK/pull/148) (thanks @nojaf!) * [Properly walk SynModuleSigDecl.Val](https://github.com/ionide/FSharp.Analyzers.SDK/pull/156) (thanks @nojaf!) * [Sarif file should not report absolute file paths](https://github.com/ionide/FSharp.Analyzers.SDK/issues/154) (thanks @nojaf!) +* [Add a -p flag to allow passing MSBuild properties through to the MSBuild evaluation portion of checking.](https://github.com/ionide/FSharp.Analyzers.SDK/issues/84) (thanks @dawedawe!) ### Added * [Add code-root flag](https://github.com/ionide/FSharp.Analyzers.SDK/pull/157) (thanks @nojaf!)