diff --git a/docs/content/getting-started/MSBuild.md b/docs/content/getting-started/MSBuild.md index 694db1a..08aee44 100644 --- a/docs/content/getting-started/MSBuild.md +++ b/docs/content/getting-started/MSBuild.md @@ -6,193 +6,55 @@ index: 4 # MSBuild -## Using Analyzer Build Target in a Project +## Analyzer paths -The path to the analyzer DLL files could be tricky to get right across a wide range of setups. Luckily, we can use a MSBuild custom target to take care of the path construction. Add [FSharp.Analyzers.Build](https://www.nuget.org/packages/FSharp.Analyzers.Build) to your project. This imports a new target to your project file (`AnalyzeFSharpProject`) and will allow us to easily run the analyzers for our project. +Finding the analyzer DLLs can be tricky across different environments. Luckily, MSBuild computes these paths after restore. +We can add a solution-level target that invokes the analyzer CLI with the correct paths. -### Installing Target via Nuget +## Solution targets -If you are using Nuget, add it to your `.fsproj` file: +MSBuild targets defined in `Directory.Solution.targets` can be invoked with `dotnet msbuild /t:YourTargetName`. We can create a target that reads the generated NuGet `*.nuget.g.props` file to discover analyzer package paths and collect all the projects we wish to analyze: -```xml - - all - build - -``` - -### Installing Target via Paket - -If you are using Paket, add it to your `paket.dependencies` - -```paket -group analyzers - source https://api.nuget.org/v3/index.json - - nuget FSharp.Analyzers.Build -``` - -as well as the `paket.references` of your project: - -```paket -group analyzers - FSharp.Analyzers.Build -``` - -### Configuring the Build Target - -Before we can run `dotnet msbuild /t:AnalyzeFSharpProject`, we need to specify our settings in a property called `FSharpAnalyzersOtherFlags`: - -```xml - - --analyzers-path "$(PkgG-Research_FSharp_Analyzers)/analyzers/dotnet/fs" --report "$(MSBuildProjectName)-$(TargetFramework).sarif" --treat-as-warning IONIDE-004 --verbosity d - -``` - -To locate the analyzer DLLs in the filesystem, we use the variable `$(PkgG-Research_FSharp_Analyzers)`. It's produced by NuGet and normalized to be usable by [MSBuild](https://learn.microsoft.com/en-us/nuget/consume-packages/package-references-in-project-files#generatepathproperty). -In general, a `Pkg` prefix is added and dots in the package ID are replaced by underscores. But make sure to look at the [nuget.g.props](https://learn.microsoft.com/en-us/nuget/reference/msbuild-targets#restore-outputs) file in the `obj` folder for the exact string. - -The `/analyzers/dotnet/fs` subpath is a convention analyzer authors should follow when creating their packages. - -### Running the Build Target - -At last, you can run the analyzer from the project folder: - -```shell -dotnet msbuild /t:AnalyzeFSharpProject -``` - -📓 Note: If your project has multiple `TargetFrameworks` the tool will be invoked for each target framework. - -## Using Analyzer Build Target in a Solution - -Adding the custom target from above to all `.fsproj` files of a solution doesn't scale very well. We can use the MSBuild infrastructure to add the needed package reference and the MSBuild target to all projects in one go. - -### Setting up Directory.Build.props - -We start with adding the `PackageReference` pointing to your favorite analyzers to the [Directory.Build.props](https://learn.microsoft.com/en-us/visualstudio/msbuild/customize-by-directory?view=vs-2022) file. -This adds the package reference to all `.fsproj` files that are in a subfolder of the file location of `Directory.Build.props`: - -```xml - - - all - build - - - all - analyzers - - -``` - -### Setting up Directory.Build.targets - -Likewise we add the `FSharpAnalyzersOtherFlags` property to the [Directory.Build.targets](https://learn.microsoft.com/en-us/visualstudio/msbuild/customize-by-directory?view=vs-2022) file. For first time MSBuild users, this is effectively the same as adding a property to each `*proj` file which exists in a subfolder. +_Directory.Solution.targets_ ```xml - - ./ - . - --analyzers-path "$(PkgG-Research_FSharp_Analyzers)/analyzers/dotnet/fs" --report "$(SarifOutput)$(MSBuildProjectName)-$(TargetFramework).sarif" --code-root $(CodeRoot) --treat-as-warning IONIDE-004 --verbosity d - + + + + + + + + $(SolutionDir) + + + --analyzers-path "$(PkgG-Research_FSharp_Analyzers)/analyzers/dotnet/fs" + $(FSharpAnalyzersOtherFlags) --analyzers-path "$(PkgIonide_Analyzers)/analyzers/dotnet/fs" + $(FSharpAnalyzersOtherFlags) --configuration $(Configuration) + $(FSharpAnalyzersOtherFlags) --verbosity d + $(FSharpAnalyzersOtherFlags) --code-root $(CodeRoot) + $(FSharpAnalyzersOtherFlags) --report "$(CodeRoot)/analysis.sarif" + + + + + ``` -⚠️ We are adding the `FSharpAnalyzersOtherFlags` property to our **Directory.Build.targets** and **not to** any **Directory.Build.props** file! MSBuild will first evaluate `Directory.Build.props` which has no access to the generated nuget.g.props. `$(PkgG-Research_FSharp_Analyzers)` won't be known at this point. `Directory.Build.targets` is evaluated after the project file and has access to `Pkg` generated properties. - -### Run Target for All Projects in the Solution - -We can run the `AnalyzeFSharpProject` target against all projects in a solution - -```shell -dotnet msbuild YourSolution.sln /t:AnalyzeFSharpProject -``` - -### Configuring Specific Projects to Run - -As we may not always want to target every project in a solution, we can create a second custom MSBuild target that calls the project-specific target for all relevant projects. -Add the following custom target to the [Directory.Solution.targets](https://learn.microsoft.com/en-us/visualstudio/msbuild/customize-solution-build?view=vs-2022) file to be able to invoke analysis from all selected projects in one simple command: - -```xml - - - - - - - - - -``` - -You can also exclude certain projects from the analysis if they fall within the same pattern - -```xml - - - - - - - - - -``` - -### Running the Solution Target - -At last, you can run the analyzer from the solution folder: +Run it with ```shell dotnet msbuild /t:AnalyzeSolution ``` -Note: we passed the `--code-root` flag so that the `*.sarif` report files will report file paths relative to this root. This can be imported for certain editors to function properly. - -## MSBuild Tips and Tricks - -MSBuild can be overwhelming for the uninitiated. Here are some tricks we've seen in the wild: - -### Use Well-Known Properties - -Checkout the [MSBuild reserved and well-known properties](https://learn.microsoft.com/en-us/visualstudio/msbuild/msbuild-reserved-and-well-known-properties?view=vs-2022) to use existing variables like `$(MSBuildProjectFile)`. - -### Wrap Path Arguments in Quotes - -As MSBuild is all XML, you can use `"` to wrap evaluated values in quotes: - -```xml - - "$(SolutionDir)" - -``` - -### Extend `` in Multiple Lines - -You can extend the value of `$(FSharpAnalyzersOtherFlags)` by setting it again in multiple lines: - -```xml - - --analyzers-path "$(PkgG-Research_FSharp_Analyzers)/analyzers/dotnet/fs" - $(FSharpAnalyzersOtherFlags) --analyzers-path "$(PkgIonide_Analyzers)/analyzers/dotnet/fs" - $(FSharpAnalyzersOtherFlags) --configuration $(Configuration) - $(FSharpAnalyzersOtherFlags) --exclude-analyzers PartialAppAnalyzer - -``` - -### Verify Parameters are Present - -It can be a bit confusing to find out if a variable contains the value you think it does. -We often add a dummy target to a project to print out some values: - -```xml - - - -``` +Notes: -Run `dotnet msbuild YourProject.fsproj /t:Dump` and verify that `CodeRoot` has a value or not. +- `artifacts/obj/Telplin/Telplin.fsproj.nuget.g.props` is generated after `dotnet restore` runs. + The target depends on it, hence the `Condition="Exists('...nuget.g.props')"` check. +- `--code-root` is important for certain tooling to have the correct links from the problems found to your source code. +- `$(PkgG-Research_FSharp_Analyzers)` becomes available when your solution references the [G-Research.FSharp.Analyzers](https://www.nuget.org/packages/G-Research.FSharp.Analyzers) package. Expect a similar `$(Pkg...)` property for whichever analyzer package your project uses. -[Next]({{fsdocs-next-page-link}}) \ No newline at end of file +[Next]({{fsdocs-next-page-link}})