DEVGUIDE.md 14.0 KB
Newer Older
D
Don Syme 已提交
1
# Development Guide
2

3
This document details more advanced options for developing in this codebase. It is not quite necessary to follow it, but it is likely that you'll find something you'll need from here.
4

5 6 7 8
## Documentation

The compiler is documented in [docs](docs/index.md). This is essential reading.

9
## Recommended workflow
10

11
We recommend the following overall workflow when developing for this repository:
V
Volker Milbrandt 已提交
12

13 14 15
* Fork this repository
* Always work in your fork
* Always keep your fork up to date
16

17
Before updating your fork, run this command:
V
Vlad Zarytovskii 已提交
18 19

```shell
20 21 22
git remote add upstream https://github.com/dotnet/fsharp.git
```

23
This will make management of multiple forks and your own work easier over time.
24

25
## Updating your fork
26

27
We recommend the following commands to update your fork:
28

V
Vlad Zarytovskii 已提交
29
```shell
B
Brett V. Forsgren 已提交
30
git checkout main
31 32
git clean -xdf
git fetch upstream
B
Brett V. Forsgren 已提交
33
git rebase upstream/main
34 35
git push
```
D
Don Syme 已提交
36

37
Or more succinctly:
D
Don Syme 已提交
38

V
Vlad Zarytovskii 已提交
39
```shell
B
Brett V. Forsgren 已提交
40
git checkout main && git clean -xdf && git fetch upstream && git rebase upstream/main && git push
41
```
42

43
This will update your fork with the latest from `dotnet/fsharp` on your machine and push those updates to your remote fork.
D
Don Syme 已提交
44

45
## Developing on Windows
46

B
Brett V. Forsgren 已提交
47
Install the latest released [Visual Studio](https://www.visualstudio.com/downloads/), as that is what the `main` branch's tools are synced with. Select the following workloads:
J
John Wostenberg 已提交
48

49 50
* .NET desktop development (also check F# desktop support, as this will install some legacy templates)
* Visual Studio extension development
J
John Wostenberg 已提交
51

V
Vlad Zarytovskii 已提交
52
You will also need the latest .NET 6 SDK installed from [here](https://dotnet.microsoft.com/download/dotnet/6.0).
B
Brett V. Forsgren 已提交
53

54
Building is simple:
55

V
Vlad Zarytovskii 已提交
56 57 58
```shell
build.cmd
```
D
Don Syme 已提交
59

B
Brett V. Forsgren 已提交
60
Desktop tests can be run with:
D
Don Syme 已提交
61

V
Vlad Zarytovskii 已提交
62 63 64
```shell
build.cmd -test -c Release
```
D
Don Syme 已提交
65

66
After you build the first time you can open and use this solution in Visual Studio:
D
Don Syme 已提交
67

V
Vlad Zarytovskii 已提交
68 69 70 71
```shell
.\VisualFSharp.sln
```

72
If you don't have everything installed yet, you'll get prompted by Visual Studio to install a few more things. This is because we use a `.vsconfig` file that specifies all our dependencies.
D
Don Syme 已提交
73

D
Don Syme 已提交
74
If you are just developing the core compiler and library then building ``FSharp.sln`` will be enough.
D
Don Syme 已提交
75

76
We recommend installing the latest released Visual Studio and using that if you are on Windows. However, if you prefer not to do that, you will need to install the following:
D
Don Syme 已提交
77

78
* [.NET Framework 4.7.2](https://dotnet.microsoft.com/download/dotnet-framework/net472)
V
Vlad Zarytovskii 已提交
79
* [.NET 6](https://dotnet.microsoft.com/download/dotnet/6.0)
D
Don Syme 已提交
80

81
You'll need to pass an additional flag to the build script:
82

V
Vlad Zarytovskii 已提交
83 84 85 86
```shell
build.cmd -noVisualStudio
```

87
You can open `FSharp.sln` in your editor of choice.
88

89
## Developing on Linux or macOS
D
Don Syme 已提交
90

B
Brett V. Forsgren 已提交
91
For Linux/Mac:
D
Don Syme 已提交
92

V
Vlad Zarytovskii 已提交
93 94 95
```shell
./build.sh
```
D
Don Syme 已提交
96

B
Brett V. Forsgren 已提交
97
Running tests:
D
Don Syme 已提交
98

V
Vlad Zarytovskii 已提交
99 100 101 102
```shell
./build.sh --test
```

103
You can then open `FSharp.sln` in your editor of choice.
D
Don Syme 已提交
104

105
## Testing from the command line
D
Don Syme 已提交
106

107
You can find all test options as separate flags. For example `build -testAll`:
D
Don Syme 已提交
108

V
Vlad Zarytovskii 已提交
109
```shell
110 111 112 113 114 115 116 117 118 119
  -testAll                  Run all tests
  -testCambridge            Run Cambridge tests
  -testCompiler             Run FSharpCompiler unit tests
  -testCompilerService      Run FSharpCompilerService unit tests
  -testDesktop              Run tests against full .NET Framework
  -testCoreClr              Run tests against CoreCLR
  -testFSharpCore           Run FSharpCore unit tests
  -testFSharpQA             Run F# Cambridge tests
  -testScripting            Run Scripting tests
  -testVs                   Run F# editor unit tests
120
```
B
Brett V. Forsgren 已提交
121

122
Running any of the above will build the latest changes and run tests against them.
B
Brett V. Forsgren 已提交
123

124
## Updating FSComp.fs, FSComp.resx and XLF
D
Don Syme 已提交
125

126
If your changes involve modifying the list of language keywords in any way, (e.g. when implementing a new keyword), the XLF localization files need to be synced with the corresponding resx files. This can be done automatically by running
D
Don Syme 已提交
127

V
Vlad Zarytovskii 已提交
128
```shell
129
dotnet build src\Compiler /t:UpdateXlf
V
Vlad Zarytovskii 已提交
130
```
D
Don Syme 已提交
131

132
This only works on Windows/.NETStandard framework, so changing this from any other platform requires editing and syncing all of the XLF files manually.
D
Don Syme 已提交
133

D
Don Syme 已提交
134 135 136
## Updating baselines in tests

Some tests use "baseline" files.  There is sometimes a way to update these baselines en-masse in your local build,
137
useful when some change affects many baselines.  For example, in the `fsharpqa` and `FSharp.Compiler.ComponentTests` tests the baselines
D
Don Syme 已提交
138 139
are updated using scripts or utilities that allow the following environment variable to be set:

140 141 142
Windows:

```shell
D
Don Syme 已提交
143 144 145
set TEST_UPDATE_BSL=1
```

146 147 148 149 150
Linux/macOS:

```shell
export TEST_UPDATE_BSL=1
```
D
Don Syme 已提交
151

152 153 154 155 156
## Automated Source Code Formatting

Some of the code in this repository is formatted automatically by [Fantomas](https://github.com/fsprojects/fantomas). To format all files use:

```cmd
157
dotnet fantomas src -r
158 159 160 161 162
```

The formatting is checked automatically by CI:

```cmd
163
dotnet fantomas src -r --check
164 165 166 167
```

At the time of writing only a subset of signature files (`*.fsi`) are formatted. See the settings in `.fantomasignore` and `.editorconfig`.

168
## Developing the F# tools for Visual Studio
D
Don Syme 已提交
169

170
As you would expect, doing this requires both Windows and Visual Studio are installed.
D
Don Syme 已提交
171

172
See [Developing on Windows](#Developing-on-Windows) for instructions to install what is needed; it's the same prerequisites.
D
Don Syme 已提交
173

174
### Quickly see your changes locally
D
Don Syme 已提交
175

K
Kevin Ransom (msft) 已提交
176
First, ensure that `VisualFSharpDebug` is the startup project.
B
Brett V. Forsgren 已提交
177

178
Then, use the **f5** or **ctrl+f5** keyboard shortcuts to test your tooling changes. The former will debug a new instance of Visual Studio. The latter will launch a new instance of Visual Studio, but with your changes installed.
B
Brett V. Forsgren 已提交
179

180
Alternatively, you can do this entirely via the command line if you prefer that:
181

V
Vlad Zarytovskii 已提交
182 183 184
```shell
devenv.exe /rootsuffix RoslynDev
```
185

186
### Deploy your changes into a current Visual Studio installation
D
Don Syme 已提交
187

188
If you'd like to "run with your changes", you can produce a VSIX and install it into your current Visual Studio instance:
D
Don Syme 已提交
189

V
Vlad Zarytovskii 已提交
190
```shell
191
VSIXInstaller.exe /u:"VisualFSharp"
K
Kevin Ransom (msft) 已提交
192
VSIXInstaller.exe artifacts\VSSetup\Release\VisualFSharpDebug.vsix
193
```
D
Don Syme 已提交
194

J
jb 已提交
195
It's important to use `Release` if you want to see if your changes have had a noticeable performance impact.
D
Don Syme 已提交
196

197
### Troubleshooting a failed build of the tools
D
Don Syme 已提交
198

199
You may run into an issue with a somewhat difficult or cryptic error message, like:
D
Don Syme 已提交
200

201
> error VSSDK1077: Unable to locate the extensions directory. "ExternalSettingsManager::GetScopePaths failed to initialize PkgDefManager for C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\IDE\devenv.exe".
D
Don Syme 已提交
202

203
Or hard crash on launch ("Unknown Error").
D
Don Syme 已提交
204

205
To fix this, delete these folders:
D
Don Syme 已提交
206

V
Vlad Zarytovskii 已提交
207 208
* `%localappdata%\Microsoft\VisualStudio\<version>_(some number here)RoslynDev`
* `%localappdata%\Microsoft\VisualStudio\<version>_(some number here)`
D
Don Syme 已提交
209

210
Where `<version>` corresponds to the latest Visual Studio version on your machine.
D
Don Syme 已提交
211

212 213 214 215 216 217 218 219 220 221 222 223 224 225
## Coding conventions

* Coding conventions vary from file to file

* Format using [the F# style guide](https://docs.microsoft.com/en-us/dotnet/fsharp/style-guide/)

* Avoid tick identifiers like `body'`. They are generally harder to read and can't be inspected in the debugger as things stand. Generaly use R suffix instead, e.g. `bodyR`. The R can stand for "rewritten" or "result"

* Avoid abbreviations like `bodyty` that run together lowercase are bad, really hard to head for newcomers. Use `bodyTy` instead.

* See the comiler docs for common abbreviations

* Don't use `List.iter` and `Array.iter` in the compiler, a `for ... do ...` loop is simpler to read and debug

226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243
## Performance and debugging

Use the `Debug` configuration to test your changes locally. It is the default. Do not use the `Release` configuration! Local development and testing of Visual Studio tooling is not designed for the `Release` configuration.

### Writing and running benchmarks

Existing compiler benchmarks can be found in `tests\benchmarks\`.

### Benchmarking and profiling the compiler

**NOTE:** When running benchmarks or profiling compiler, and comparing results with upstream version, make sure:

* Always build both versions of compiler/FCS from source and not use pre-built binaries from SDK (SDK binaries are crossgen'd, which can affect performance).
* To run `Release` build of compiler/FCS.

### Example benchmark setup using [BenchmarkDotNet](https://github.com/dotnet/BenchmarkDotNet)

1. Perform a clean build of the compiler and FCS from source (as described in this document, build can be done with `-noVisualStudio` in case if FCS/FSharp.Core is being benchmarked/profiled).
244

245 246 247 248 249 250 251 252 253 254 255 256
2. Create a benchmark project (in this example, the project will be created in `tests\benchmarks\`).

      ```shell
      cd tests\benchmarks
      dotnet new console -o FcsBench --name FcsBench -lang F#
      ```

3. Add needed packages and project references.

    ```shell
    cd FcsBench
    dotnet add package BenchmarkDotNet
257
    dotnet add reference ..\..\..\src\Compiler\FSharp.Compiler.Service.fsproj
258 259 260 261 262
    ```

4. Additionally, if you want to test changes to the FSharp.Core

     ```shell
263
     dotnet add reference ..\..\..\src\FSharp.Core\FSharp.Core.fsproj
264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309
     ```

    > as well as the following property have to be added to `FcsBench.fsproj`:

    ```xml
    <PropertyGroup>
        <DisableImplicitFSharpCoreReference>true</DisableImplicitFSharpCoreReference>
    </PropertyGroup>
    ```

5. Add a new benchmark for FCS/FSharp.Core by editing `Program.fs`.

      ```fsharp
      open System.IO
      open FSharp.Compiler.CodeAnalysis
      open FSharp.Compiler.Diagnostics
      open FSharp.Compiler.Text
      open BenchmarkDotNet.Attributes
      open BenchmarkDotNet.Running

      [<MemoryDiagnoser>]
      type CompilerService() =
          let mutable checkerOpt = None
          let mutable sourceOpt = None

          let parsingOptions =
              {
                  SourceFiles = [|"CheckExpressions.fs"|]
                  ConditionalDefines = []
                  ErrorSeverityOptions = FSharpDiagnosticOptions.Default
                  LangVersionText = "default"
                  IsInteractive = false
                  LightSyntax = None
                  CompilingFsLib = false
                  IsExe = false
              }

          [<GlobalSetup>]
          member _.Setup() =
              match checkerOpt with
              | None ->
                  checkerOpt <- Some(FSharpChecker.Create(projectCacheSize = 200))
              | _ -> ()

              match sourceOpt with
              | None ->
310
                  sourceOpt <- Some <| SourceText.ofString(File.ReadAllText("""C:\Users\vlza\code\fsharp\src\Compiler\Checking\CheckExpressions.fs"""))
311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385
              | _ -> ()


          [<Benchmark>]
          member _.ParsingTypeCheckerFs() =
              match checkerOpt, sourceOpt with
              | None, _ -> failwith "no checker"
              | _, None -> failwith "no source"
              | Some(checker), Some(source) ->
                  let results = checker.ParseFile("CheckExpressions.fs",  source, parsingOptions) |> Async.RunSynchronously
                  if results.ParseHadErrors then failwithf "parse had errors: %A" results.Diagnostics

          [<IterationCleanup(Target = "ParsingTypeCheckerFs")>]
          member _.ParsingTypeCheckerFsSetup() =
              match checkerOpt with
              | None -> failwith "no checker"
              | Some(checker) ->
                  checker.InvalidateAll()
                  checker.ClearLanguageServiceRootCachesAndCollectAndFinalizeAllTransients()
                  checker.ParseFile("dummy.fs", SourceText.ofString "dummy", parsingOptions) |> Async.RunSynchronously |> ignore

      [<EntryPoint>]
      let main _ =
          BenchmarkRunner.Run<CompilerService>() |> ignore
          0
      ```

      > For more detailed information about available BenchmarkDotNet options, please refer to [BenchmarkDotNet Documentation](https://benchmarkdotnet.org/articles/overview.html).

6. Build and run the benchmark.

      ```shell
      dotnet build -c Release
      dotnet run -c Release
      ```

7. You can find results in `.\BenchmarkDotNet.Artifacts\results\` in the current benchmark project directory.

    ```shell
    > ls .\BenchmarkDotNet.Artifacts\results\

        Directory: C:\Users\vlza\code\fsharp\tests\benchmarks\FcsBench\BenchmarkDotNet.Artifacts\results

    Mode                 LastWriteTime         Length Name
    ----                 -------------         ------ ----
    -a---           4/25/2022  1:42 PM            638 Program.CompilerService-report-github.md
    -a---           4/25/2022  1:42 PM           1050 Program.CompilerService-report.csv
    -a---           4/25/2022  1:42 PM           1169 Program.CompilerService-report.html
    ```

    > *-report-github.md can be used to post benchmark results to GitHub issue/PR/discussion or RFC.
    >
    >*-report.csv can be used for comparison purposes.

    **Example output:**

    ``` ini

    BenchmarkDotNet=v0.13.1, OS=Windows 10.0.25102
    Intel Core i7-8750H CPU 2.20GHz (Coffee Lake), 1 CPU, 12 logical and 6 physical cores
    .NET SDK=6.0.200
      [Host]     : .NET 6.0.3 (6.0.322.12309), X64 RyuJIT DEBUG
      Job-GDIBXX : .NET 6.0.3 (6.0.322.12309), X64 RyuJIT

    InvocationCount=1  UnrollFactor=1

    ```

    |               Method |     Mean |   Error |  StdDev |   Median |     Gen 0 |     Gen 1 | Allocated |
    |--------------------- |---------:|--------:|--------:|---------:|----------:|----------:|----------:|
    | ParsingTypeCheckerFs | 199.4 ms | 3.84 ms | 9.78 ms | 195.5 ms | 4000.0000 | 1000.0000 |     28 MB |

8. Repeat for any number of changes you would like to test.
9. **Optionally:** benchmark code and results can be included as part of the PR for future reference.

386
## Additional resources
D
Don Syme 已提交
387

D
Don Syme 已提交
388
The primary technical guide to the core compiler code is [The F# Compiler Technical Guide](https://github.com/dotnet/fsharp/blob/main/docs/index.md). Please read and contribute to that guide.
D
Don Syme 已提交
389

390
See the "Debugging The Compiler" section of this [article](https://medium.com/@willie.tetlow/f-mentorship-week-1-36f51d3812d4) for some examples.
D
Don Syme 已提交
391

392
## Addendum: configuring a proxy server
D
Don Syme 已提交
393

394
If you are behind a proxy server, NuGet client tool must be configured to use it:
D
Don Syme 已提交
395

396
See the Nuget config file documention for use with a proxy server [https://docs.microsoft.com/en-us/nuget/reference/nuget-config-file](https://docs.microsoft.com/en-us/nuget/reference/nuget-config-file)