DEVGUIDE.md 15.4 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

V
Vlad Zarytovskii 已提交
47
Install the latest released [Visual Studio](https://visualstudio.microsoft.com/vs/preview/) preview, 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 7 SDK installed from [here](https://dotnet.microsoft.com/download/dotnet/7.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

V
Vlad Zarytovskii 已提交
76
We recommend installing the latest Visual Studio preview 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 7](https://dotnet.microsoft.com/download/dotnet/7.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
  -testAll                  Run all tests
P
Petr 已提交
111
  -testAllButIntegration    Run all but integration tests
112 113 114 115 116 117 118 119 120
  -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
121
```
B
Brett V. Forsgren 已提交
122

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

125 126 127 128 129 130 131 132 133 134 135 136
## Using your custom compiler to build other projects

Building the compiler using `build.cmd` or `build.sh` will output artifacts in `artifacts\bin`. 

To use your custom build of `Fsc`, add the `DotnetFscCompilerPath` property to your project's `.fsproj` file, adjusted to point at your local build directory, build configuration, and target framework as appropriate:

```xml
<PropertyGroup>
    <DotnetFscCompilerPath>D:\Git\fsharp\artifacts\bin\fsc\Debug\net7.0\fsc.dll</DotnetFscCompilerPath>
</PropertyGroup>
```

137
## Updating FSComp.fs, FSComp.resx and XLF
D
Don Syme 已提交
138

139
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 已提交
140

V
Vlad Zarytovskii 已提交
141
```shell
142
dotnet build src\Compiler /t:UpdateXlf
V
Vlad Zarytovskii 已提交
143
```
144
If you are on a Mac, you can run this command from the root of the repository:
D
Don Syme 已提交
145

146 147 148
```shell
sh build.sh -c Release
```
D
Don Syme 已提交
149

150 151 152 153 154 155
Or if you are on Linux:

```shell
./build.sh -c Release
```

D
Don Syme 已提交
156 157
## Updating baselines in tests

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

162 163
Windows:

164 165
CMD:

166
```shell
D
Don Syme 已提交
167 168 169
set TEST_UPDATE_BSL=1
```

170 171 172 173 174 175
PowerShell:

```shell
$env:TEST_UPDATE_BSL=1
```

176 177 178 179 180
Linux/macOS:

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

182 183 184 185 186 187 188 189 190
Next, run a build script build (debug or release, desktop or coreclr, depending which baselines you need to update), and test as described [above](#Testing-from-the-command-line). For example:

`./Build.cmd -c Release -testCoreClr` to update Release CoreCLR baselines.

or

`./Build.cmd -c Release -testDesktop` to update Release .NET Framework baselines.

> **Note**
191
> Please note, that by default, **Release** version of IL baseline tests will be running in CI, so when updating baseline (.bsl) files, make sure to add `-c Release` flag to the build command.
192

193 194 195 196 197
## 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
P
Petr 已提交
198
dotnet fantomas . -r
199 200 201 202 203
```

The formatting is checked automatically by CI:

```cmd
P
Petr 已提交
204
dotnet fantomas . -r --check
205 206 207 208
```

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

209
## Developing the F# tools for Visual Studio
D
Don Syme 已提交
210

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

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

215
### Quickly see your changes locally
D
Don Syme 已提交
216

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

219
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 已提交
220

221
Alternatively, you can do this entirely via the command line if you prefer that:
222

V
Vlad Zarytovskii 已提交
223 224 225
```shell
devenv.exe /rootsuffix RoslynDev
```
226

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

P
Petr 已提交
229
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 已提交
230

P
Petr 已提交
231
For this, run the following using the VS Developer PowerShell from the repo root:
V
Vlad Zarytovskii 已提交
232
```shell
233
VSIXInstaller.exe /u:"VisualFSharp"
K
Kevin Ransom (msft) 已提交
234
VSIXInstaller.exe artifacts\VSSetup\Release\VisualFSharpDebug.vsix
235
```
D
Don Syme 已提交
236

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

239
### Troubleshooting a failed build of the tools
D
Don Syme 已提交
240

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

243
> 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 已提交
244

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

247
To fix this, delete these folders:
D
Don Syme 已提交
248

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

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

254 255 256 257
## Coding conventions

* Coding conventions vary from file to file

258
* Format using [the F# style guide](https://learn.microsoft.com/dotnet/fsharp/style-guide/)
259

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

P
Petr Pokorny 已提交
262
* Avoid abbreviations like `bodyty` that are all lowercase. They are really hard to read for newcomers. Use `bodyTy` instead.
263

P
Petr Semkin 已提交
264
* See the compiler docs for common abbreviations
265 266 267

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

268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
## 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).
286

287
2. Create a benchmark project (in this example, the project will be created in `tests\benchmarks\FCSBenchmarks`).
288 289

      ```shell
290
      cd tests\benchmarks\FCSBenchmarks
291 292 293 294 295 296 297 298
      dotnet new console -o FcsBench --name FcsBench -lang F#
      ```

3. Add needed packages and project references.

    ```shell
    cd FcsBench
    dotnet add package BenchmarkDotNet
299
    dotnet add reference ..\..\..\src\Compiler\FSharp.Compiler.Service.fsproj
300 301
    ```

302
4. Additionally, if you want to test changes to the FSharp.Core (note that the relative path can be different)
303 304

     ```shell
305
     dotnet add reference ..\..\..\src\FSharp.Core\FSharp.Core.fsproj
306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334
     ```

    > 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 = []
D
Don Syme 已提交
335
                  DiagnosticOptions = FSharpDiagnosticOptions.Default
336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351
                  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 ->
352
                  sourceOpt <- Some <| SourceText.ofString(File.ReadAllText("""C:\Users\vlza\code\fsharp\src\Compiler\Checking\CheckExpressions.fs"""))
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 386 387 388 389 390 391 392 393
              | _ -> ()


          [<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\

394
        Directory: C:\Users\vlza\code\fsharp\tests\benchmarks\FCSBenchmarks\FcsBench\BenchmarkDotNet.Artifacts\results
395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427

    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.

428
## Additional resources
D
Don Syme 已提交
429

D
Don Syme 已提交
430
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 已提交
431

432
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 已提交
433

434
## Addendum: configuring a proxy server
D
Don Syme 已提交
435

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

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