Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added service_slim #2

Draft
wants to merge 13 commits into
base: main
Choose a base branch
from
12 changes: 11 additions & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,16 @@
"enableStepFiltering": false,
"requireExactSource": false,
"allowFastEvaluate": true
},
{
"name": "FCS Test",
"type": "coreclr",
"request": "launch",
"program": "${workspaceFolder}/artifacts/bin/fcs-test/Debug/net8.0/fcs-test.dll",
"args": [],
"cwd": "${workspaceFolder}/fcs/fcs-test",
"console": "internalConsole",
"stopAtEntry": false
}
]
}
}
4 changes: 2 additions & 2 deletions buildtools/buildtools.targets
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
BeforeTargets="CoreCompile">

<PropertyGroup>
<FsLexPath Condition="'$(FsLexPath)' == ''">$(ArtifactsDir)\Bootstrap\fslex\fslex.dll</FsLexPath>
<FsLexPath Condition="'$(FsLexPath)' == ''">$(ArtifactsDir)\bin\fslex\Release\net9.0\linux-x64\fslex.dll</FsLexPath>
</PropertyGroup>

<!-- Create the output directory -->
Expand All @@ -44,7 +44,7 @@
BeforeTargets="CoreCompile">

<PropertyGroup>
<FsYaccPath Condition="'$(FsYaccPath)' == ''">$(ArtifactsDir)\Bootstrap\fsyacc\fsyacc.dll</FsYaccPath>
<FsYaccPath Condition="'$(FsYaccPath)' == ''">$(ArtifactsDir)\bin\fsyacc\Release\net9.0\linux-x64\fsyacc.dll</FsYaccPath>
</PropertyGroup>

<!-- Create the output directory -->
Expand Down
7 changes: 7 additions & 0 deletions fcs/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/usr/bin/env bash

dotnet build -c Release buildtools/fslex
dotnet build -c Release buildtools/fsyacc
dotnet build -c Release src/Compiler
dotnet run -c Release --project fcs/fcs-test
echo "Binaries can be found here: /artifacts/bin/FSharp.Compiler.Service/Release/netstandard2.0/"
10 changes: 10 additions & 0 deletions fcs/fcs-test/NuGet.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<clear />
<add key="NuGet.org" value="https://api.nuget.org/v3/index.json" />
</packageSources>
<disabledPackageSources>
<clear />
</disabledPackageSources>
</configuration>
178 changes: 178 additions & 0 deletions fcs/fcs-test/Program.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
open System.IO
open System.Text.RegularExpressions
open FSharp.Compiler
open FSharp.Compiler.CodeAnalysis
open FSharp.Compiler.SourceCodeServices
open FSharp.Compiler.EditorServices
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 fileName = "fcs-test.fsproj"
let projDir = __SOURCE_DIRECTORY__
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"
yield "--noframework"
yield "--debug:full"
yield "--define:DEBUG"
yield "--optimize-"
yield "--out:" + dllName
yield "--doc:test.xml"
yield "--warn:3"
yield "--fullpaths"
yield "--flaterrors"
yield "--target:library"
for x in fileNames do
yield x
let references = mkStandardProjectReferences ()
for r in references do
yield r
|]

let getProjectOptionsFromCommandLineArgs(projName, argv): FSharpProjectOptions =
{ ProjectFileName = projName
ProjectId = None
SourceFiles = [| |]
OtherOptions = argv
ReferencedProjects = [| |]
IsIncompleteTypeCheckEnvironment = false
UseScriptResolutionRules = false
LoadTime = System.DateTime.MaxValue
UnresolvedReferences = None
OriginalLoadReferences = []
Stamp = None }

let printAst title (projectResults: FSharpCheckProjectResults) =
let implFiles = projectResults.AssemblyContents.ImplementationFiles
let decls = implFiles
|> Seq.collect (fun file -> AstPrint.printFSharpDecls "" file.Declarations)
|> String.concat "\n"
printfn "%s Typed AST:" title
decls |> printfn "%s"

[<EntryPoint>]
let main argv =
let projName = "Project.fsproj"
let fileName = __SOURCE_DIRECTORY__ + "/test_script.fsx"
let fileNames = [| fileName |]
let source = File.ReadAllText (fileName, System.Text.Encoding.UTF8)

let dllName = Path.ChangeExtension(fileName, ".dll")
let args = mkProjectCommandLineArgsForScript (dllName, fileNames)
// for arg in args do printfn "%s" arg

let projectOptions = getProjectOptionsFromCommandLineArgs (projName, args)
let checker = InteractiveChecker.Create(projectOptions)
let sourceReader _fileName = (hash source, lazy source)

// parse and typecheck a project
let projectResults =
checker.ParseAndCheckProject(projName, fileNames, sourceReader)
|> Async.RunSynchronously
projectResults.Diagnostics |> Array.iter (fun e -> printfn "%A: %A" (e.Severity) e)
printAst "ParseAndCheckProject" projectResults

// or just parse and typecheck a file in project
let (parseResults, typeCheckResults, projectResults) =
checker.ParseAndCheckFileInProject(projName, fileNames, sourceReader, fileName)
|> Async.RunSynchronously
projectResults.Diagnostics |> Array.iter (fun e -> printfn "%A: %A" (e.Severity) e)

printAst "ParseAndCheckFileInProject" projectResults

let inputLines = source.Split('\n')

// Get tool tip at the specified location
let tip = typeCheckResults.GetToolTip(4, 7, inputLines.[3], ["foo"], Tokenization.FSharpTokenTag.IDENT)
(sprintf "%A" tip).Replace("\n","") |> printfn "\n---> ToolTip Text = %A" // should print "FSharpToolTipText [...]"

// Get declarations (autocomplete) for msg
let partialName = { QualifyingIdents = []; PartialIdent = "msg"; EndColumn = 17; LastDotPos = None }
let decls = typeCheckResults.GetDeclarationListInfo(Some parseResults, 6, inputLines.[5], partialName, (fun _ -> []))
[ for item in decls.Items -> item.NameInList ] |> printfn "\n---> msg AutoComplete = %A" // should print string methods

// Get declarations (autocomplete) for canvas
let partialName = { QualifyingIdents = []; PartialIdent = "canvas"; EndColumn = 10; LastDotPos = None }
let decls = typeCheckResults.GetDeclarationListInfo(Some parseResults, 8, inputLines.[7], partialName, (fun _ -> []))
[ for item in decls.Items -> item.NameInList ] |> printfn "\n---> canvas AutoComplete = %A"

printfn "Done."
0
101 changes: 101 additions & 0 deletions fcs/fcs-test/ast_print.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

module AstPrint

open FSharp.Compiler.Symbols

//-------------------------------------------------------------------------
// AstPrint
//-------------------------------------------------------------------------

let attribsOfSymbol (s: FSharpSymbol) =
[ match s with
| :? FSharpField as v ->
yield "field"
if v.IsCompilerGenerated then yield "compgen"
if v.IsDefaultValue then yield "default"
if v.IsMutable then yield "mutable"
if v.IsVolatile then yield "volatile"
if v.IsStatic then yield "static"
if v.IsLiteral then yield sprintf "%A" v.LiteralValue.Value

| :? FSharpEntity as v ->
v.TryFullName |> ignore // check there is no failure here
match v.BaseType with
| Some t when t.HasTypeDefinition && t.TypeDefinition.TryFullName.IsSome ->
yield sprintf "inherits %s" t.TypeDefinition.FullName
| _ -> ()
if v.IsNamespace then yield "namespace"
if v.IsFSharpModule then yield "module"
if v.IsByRef then yield "byref"
if v.IsClass then yield "class"
if v.IsDelegate then yield "delegate"
if v.IsEnum then yield "enum"
if v.IsFSharpAbbreviation then yield "abbrev"
if v.IsFSharpExceptionDeclaration then yield "exception"
if v.IsFSharpRecord then yield "record"
if v.IsFSharpUnion then yield "union"
if v.IsInterface then yield "interface"
if v.IsMeasure then yield "measure"
#if !NO_EXTENSIONTYPING
if v.IsProvided then yield "provided"
if v.IsStaticInstantiation then yield "static_inst"
if v.IsProvidedAndErased then yield "erased"
if v.IsProvidedAndGenerated then yield "generated"
#endif
if v.IsUnresolved then yield "unresolved"
if v.IsValueType then yield "valuetype"

| :? FSharpMemberOrFunctionOrValue as v ->
yield "owner: " + match v.DeclaringEntity with | Some e -> e.CompiledName | _ -> "<unknown>"
if v.IsActivePattern then yield "active_pattern"
if v.IsDispatchSlot then yield "dispatch_slot"
if v.IsModuleValueOrMember && not v.IsMember then yield "val"
if v.IsMember then yield "member"
if v.IsProperty then yield "property"
if v.IsExtensionMember then yield "extension_member"
if v.IsPropertyGetterMethod then yield "property_getter"
if v.IsPropertySetterMethod then yield "property_setter"
if v.IsEvent then yield "event"
if v.EventForFSharpProperty.IsSome then yield "property_event"
if v.IsEventAddMethod then yield "event_add"
if v.IsEventRemoveMethod then yield "event_remove"
if v.IsTypeFunction then yield "type_func"
if v.IsCompilerGenerated then yield "compiler_gen"
if v.IsImplicitConstructor then yield "implicit_ctor"
if v.IsMutable then yield "mutable"
if v.IsOverrideOrExplicitInterfaceImplementation then yield "override_impl"
if not v.IsInstanceMember then yield "static"
if v.IsInstanceMember && not v.IsInstanceMemberInCompiledCode && not v.IsExtensionMember then yield "funky"
if v.IsExplicitInterfaceImplementation then yield "interface_impl"
yield sprintf "%A" v.InlineAnnotation
// if v.IsConstructorThisValue then yield "ctorthis"
// if v.IsMemberThisValue then yield "this"
// if v.LiteralValue.IsSome then yield "literal"
| _ -> () ]

let rec printFSharpDecls prefix decls = seq {
let mutable i = 0
for decl in decls do
i <- i + 1
match decl with
| FSharpImplementationFileDeclaration.Entity (e, sub) ->
yield sprintf "%s%i) ENTITY: %s %A" prefix i e.CompiledName (attribsOfSymbol e)
if not (Seq.isEmpty e.Attributes) then
yield sprintf "%sattributes: %A" prefix (Seq.toList e.Attributes)
if not (Seq.isEmpty e.DeclaredInterfaces) then
yield sprintf "%sinterfaces: %A" prefix (Seq.toList e.DeclaredInterfaces)
yield ""
yield! printFSharpDecls (prefix + "\t") sub
| FSharpImplementationFileDeclaration.MemberOrFunctionOrValue (meth, args, body) ->
yield sprintf "%s%i) METHOD: %s %A" prefix i meth.CompiledName (attribsOfSymbol meth)
yield sprintf "%stype: %A" prefix meth.FullType
yield sprintf "%sargs: %A" prefix args
// if not meth.IsCompilerGenerated then
yield sprintf "%sbody: %A" prefix body
yield ""
| FSharpImplementationFileDeclaration.InitAction (expr) ->
yield sprintf "%s%i) ACTION" prefix i
yield sprintf "%s%A" prefix expr
yield ""
}
27 changes: 27 additions & 0 deletions fcs/fcs-test/fcs-test.fsproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<DisableImplicitFSharpCoreReference>true</DisableImplicitFSharpCoreReference>
</PropertyGroup>

<ItemGroup>
<Compile Include="ast_print.fs" />
<Compile Include="Program.fs" />
</ItemGroup>

<ItemGroup>
<!-- <ProjectReference Include="../../src/Compiler/FSharp.Compiler.Service.fsproj" /> -->
<!-- <ProjectReference Include="../../src/Compiler/FSharp.Core/FSharp.Core.fsproj" /> -->
<Reference Include="../../artifacts/bin/FSharp.Compiler.Service/Release/netstandard2.0/FSharp.Core.dll" />
<Reference Include="../../artifacts/bin/FSharp.Compiler.Service/Release/netstandard2.0/FSharp.Compiler.Service.dll" />
</ItemGroup>

<ItemGroup>
<!-- <PackageReference Include="FSharp.Core" Version="9.0.0" /> -->
<PackageReference Include="Buildalyzer" Version="*" />
<PackageReference Include="Fable.Core" Version="*" />
<PackageReference Include="Fable.Import.Browser" Version="*" />
</ItemGroup>
</Project>
8 changes: 8 additions & 0 deletions fcs/fcs-test/test_script.fsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
open System
open Fable.Import

let foo() =
let msg = String.Concat("Hello"," ","world")
let len = msg.Length
let canvas = Browser.document.createElement_canvas ()
canvas.width <- 1000.
Loading
Loading