diff --git a/.vscode/launch.json b/.vscode/launch.json
index 6cc703662fb..07c4b527e5e 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -92,7 +92,7 @@
"name": "FCS Test",
"type": "coreclr",
"request": "launch",
- "program": "${workspaceFolder}/artifacts/bin/fcs-test/Debug/net6.0/fcs-test.dll",
+ "program": "${workspaceFolder}/artifacts/bin/fcs-test/Debug/net7.0/fcs-test.dll",
"args": [],
"cwd": "${workspaceFolder}/fcs/fcs-test",
"console": "internalConsole",
diff --git a/buildtools/AssemblyCheck/AssemblyCheck.fsproj b/buildtools/AssemblyCheck/AssemblyCheck.fsproj
index d031ded4c10..bc9584cf701 100644
--- a/buildtools/AssemblyCheck/AssemblyCheck.fsproj
+++ b/buildtools/AssemblyCheck/AssemblyCheck.fsproj
@@ -4,6 +4,7 @@
Exe
net8.0
net6.0
+ net7.0
true
false
diff --git a/buildtools/buildtools.targets b/buildtools/buildtools.targets
index 8332b53a237..3b2fa489c66 100644
--- a/buildtools/buildtools.targets
+++ b/buildtools/buildtools.targets
@@ -20,7 +20,7 @@
BeforeTargets="CoreCompile">
- $(ArtifactsDir)\bin\fslex\Release\net6.0\fslex.dll
+ $(ArtifactsDir)\bin\fslex\Release\net7.0\fslex.dll
@@ -44,7 +44,7 @@
BeforeTargets="CoreCompile">
- $(ArtifactsDir)\bin\fsyacc\Release\net6.0\fsyacc.dll
+ $(ArtifactsDir)\bin\fsyacc\Release\net7.0\fsyacc.dll
diff --git a/buildtools/fslex/fslex.fsproj b/buildtools/fslex/fslex.fsproj
index 907d979eedd..72d3d7d420c 100644
--- a/buildtools/fslex/fslex.fsproj
+++ b/buildtools/fslex/fslex.fsproj
@@ -4,7 +4,6 @@
Exe
net8.0
net7.0
- net6.0
INTERNALIZED_FSLEXYACC_RUNTIME;$(DefineConstants)
true
false
diff --git a/buildtools/fsyacc/fsyacc.fsproj b/buildtools/fsyacc/fsyacc.fsproj
index 9923dc4b29f..3bc4c7c5203 100644
--- a/buildtools/fsyacc/fsyacc.fsproj
+++ b/buildtools/fsyacc/fsyacc.fsproj
@@ -4,7 +4,6 @@
Exe
net8.0
net7.0
- net6.0
INTERNALIZED_FSLEXYACC_RUNTIME;$(DefineConstants)
true
false
diff --git a/fcs/fcs-test/Program.fs b/fcs/fcs-test/Program.fs
index edb6522a71a..fdefd83d449 100644
--- a/fcs/fcs-test/Program.fs
+++ b/fcs/fcs-test/Program.fs
@@ -1,41 +1,92 @@
open System.IO
+open System.Text.RegularExpressions
open FSharp.Compiler
open FSharp.Compiler.CodeAnalysis
open FSharp.Compiler.SourceCodeServices
open FSharp.Compiler.EditorServices
-
-let getProjectOptions (folder: string) (projectFile: string) =
- let runProcess (workingDir: string) (exePath: string) (args: string) =
- let psi = System.Diagnostics.ProcessStartInfo()
- psi.FileName <- exePath
- psi.WorkingDirectory <- workingDir
- psi.RedirectStandardOutput <- false
- psi.RedirectStandardError <- false
- psi.Arguments <- args
- psi.CreateNoWindow <- true
- psi.UseShellExecute <- false
-
- use p = new System.Diagnostics.Process()
- p.StartInfo <- psi
- p.Start() |> ignore
- p.WaitForExit()
-
- let exitCode = p.ExitCode
- exitCode, ()
-
- let runCmd exePath args = runProcess folder exePath (args |> String.concat " ")
- let msbuildExec = Dotnet.ProjInfo.Inspect.dotnetMsbuild runCmd
- let result = Dotnet.ProjInfo.Inspect.getProjectInfo ignore msbuildExec Dotnet.ProjInfo.Inspect.getFscArgs projectFile
- match result with
- | Ok (Dotnet.ProjInfo.Inspect.GetResult.FscArgs x) -> x
- | _ -> []
+open Buildalyzer
+
+let getProjectOptionsFromProjectFile (isMain: bool) (projFile: string) =
+
+ let tryGetResult (isMain: bool) (manager: AnalyzerManager) (maybeCsprojFile: string) =
+
+ let analyzer = manager.GetProject(maybeCsprojFile)
+ let env = analyzer.EnvironmentFactory.GetBuildEnvironment(Environment.EnvironmentOptions(DesignTime=true,Restore=false))
+ // If System.the project targets multiple frameworks, multiple results will be returned
+ // For now we just take the first one with non-empty command
+ let results = analyzer.Build(env)
+ results
+ |> Seq.tryFind (fun r -> System.String.IsNullOrEmpty(r.Command) |> not)
+
+ let manager =
+ let log = new StringWriter()
+ let options = AnalyzerManagerOptions(LogWriter = log)
+ let m = AnalyzerManager(options)
+ m
+
+ // Because Buildalyzer works better with .csproj, we first "dress up" the project as if it were a C# one
+ // and try to adapt the results. If it doesn't work, we try again to analyze the .fsproj directly
+ let csprojResult =
+ let csprojFile = projFile.Replace(".fsproj", ".csproj")
+ if File.Exists(csprojFile) then
+ None
+ else
+ try
+ File.Copy(projFile, csprojFile)
+ tryGetResult isMain manager csprojFile
+ |> Option.map (fun (r: IAnalyzerResult) ->
+ // Careful, options for .csproj start with / but so do root paths in unix
+ let reg = Regex(@"^\/[^\/]+?(:?:|$)")
+ let comArgs =
+ r.CompilerArguments
+ |> Array.map (fun line ->
+ if reg.IsMatch(line) then
+ if line.StartsWith("/reference") then "-r" + line.Substring(10)
+ else "--" + line.Substring(1)
+ else line)
+ let comArgs =
+ match r.Properties.TryGetValue("OtherFlags") with
+ | false, _ -> comArgs
+ | true, otherFlags ->
+ let otherFlags = otherFlags.Split(' ', System.StringSplitOptions.RemoveEmptyEntries)
+ Array.append otherFlags comArgs
+ comArgs, r)
+ finally
+ File.Delete(csprojFile)
+
+ let compilerArgs, result =
+ csprojResult
+ |> Option.orElseWith (fun () ->
+ tryGetResult isMain manager projFile
+ |> Option.map (fun r ->
+ // result.CompilerArguments doesn't seem to work well in Linux
+ let comArgs = Regex.Split(r.Command, @"\r?\n")
+ comArgs, r))
+ |> function
+ | Some result -> result
+ // TODO: Get Buildalyzer errors from the log
+ | None -> failwith $"Cannot parse {projFile}"
+
+ let projDir = Path.GetDirectoryName(projFile)
+ let projOpts =
+ compilerArgs
+ |> Array.skipWhile (fun line -> not(line.StartsWith("-")))
+ |> Array.map (fun f ->
+ if f.EndsWith(".fs") || f.EndsWith(".fsi") then
+ if Path.IsPathRooted f then f else Path.Combine(projDir, f)
+ else f)
+ projOpts,
+ Seq.toArray result.ProjectReferences,
+ result.Properties,
+ result.TargetFramework
let mkStandardProjectReferences () =
- let projFile = "fcs-test.fsproj"
+ let fileName = "fcs-test.fsproj"
let projDir = __SOURCE_DIRECTORY__
- getProjectOptions projDir projFile
- |> List.filter (fun s -> s.StartsWith("-r:"))
- |> List.map (fun s -> s.Replace("-r:", ""))
+ let projFile = Path.Combine(projDir, fileName)
+ let (args, _, _, _) = getProjectOptionsFromProjectFile true projFile
+ args
+ |> Array.filter (fun s -> s.StartsWith("-r:"))
let mkProjectCommandLineArgsForScript (dllName, fileNames) =
[| yield "--simpleresolution"
@@ -53,7 +104,7 @@ let mkProjectCommandLineArgsForScript (dllName, fileNames) =
yield x
let references = mkStandardProjectReferences ()
for r in references do
- yield "-r:" + r
+ yield r
|]
let getProjectOptionsFromCommandLineArgs(projName, argv): FSharpProjectOptions =
diff --git a/fcs/fcs-test/fcs-test.fsproj b/fcs/fcs-test/fcs-test.fsproj
index 839638d2551..3a8c1940791 100644
--- a/fcs/fcs-test/fcs-test.fsproj
+++ b/fcs/fcs-test/fcs-test.fsproj
@@ -2,12 +2,12 @@
Exe
- net6.0
+ net7.0
true
-
+
@@ -19,9 +19,9 @@
-
-
-
+
+
+
diff --git a/fcs/service_slim.fs b/fcs/service_slim.fs
index 7de2cc8dd78..f7c5b9b2343 100644
--- a/fcs/service_slim.fs
+++ b/fcs/service_slim.fs
@@ -90,6 +90,7 @@ module internal ParseAndCheck =
let userOpName = "Unknown"
let suggestNamesForErrors = true
+ let captureIdentifiersWhenParsing = false
let measureTime (f: unit -> 'a) =
let sw = Diagnostics.Stopwatch.StartNew()
@@ -192,7 +193,7 @@ module internal ParseAndCheck =
compilerState.parseCache.GetOrAdd(parseCacheKey, fun _ ->
ClearStaleCache(fileName, parsingOptions, compilerState)
let sourceText = SourceText.ofString source.Value
- let parseErrors, parseTreeOpt, anyErrors = ParseAndCheckFile.parseFile (sourceText, fileName, parsingOptions, userOpName, suggestNamesForErrors)
+ let parseErrors, parseTreeOpt, anyErrors = ParseAndCheckFile.parseFile (sourceText, fileName, parsingOptions, userOpName, suggestNamesForErrors, captureIdentifiersWhenParsing)
let dependencyFiles = [||] // interactions have no dependencies
FSharpParseFileResults (parseErrors, parseTreeOpt, anyErrors, dependencyFiles) )
@@ -208,7 +209,7 @@ module internal ParseAndCheck =
let input, moduleNamesDict = input |> DeduplicateParsedInputModuleName moduleNamesDict
let tcResult, tcState =
- CheckOneInput (checkForErrors, compilerState.tcConfig, compilerState.tcImports, compilerState.tcGlobals, prefixPathOpt, tcSink, tcState, input, false)
+ CheckOneInput (checkForErrors, compilerState.tcConfig, compilerState.tcImports, compilerState.tcGlobals, prefixPathOpt, tcSink, tcState, input)
|> Cancellable.runWithoutCancellation
let fileName = parseResults.FileName