Compiler Services: Virtualized File System
The FSharp.Compiler.Service
component has a global variable
representing the file system. By setting this variable you can host the compiler in situations where a file system
is not available.
NOTE: The FSharp.Compiler.Service API is subject to change when later versions of the nuget package are published.
Setting the FileSystem
In the example below, we set the file system to an implementation which reads from disk
#r "FSharp.Compiler.Service.dll"
open System.IO
open System.Text
open FSharp.Compiler.CodeAnalysis
open FSharp.Compiler.IO
let defaultFileSystem = FileSystem
let fileName1 = @"c:\mycode\test1.fs" // note, the path doesn't exist
let fileName2 = @"c:\mycode\test2.fs" // note, the path doesn't exist
type MyFileSystem() =
let file1 =
"""
module File1
let A = 1"""
let file2 =
"""
module File2
let B = File1.A + File1.A"""
let files =
dict [ (fileName1, file1)
(fileName2, file2) ]
interface IFileSystem with
// Implement the service to open files for reading and writing
member _.OpenFileForReadShim(fileName, ?useMemoryMappedFile: bool, ?shouldShadowCopy: bool) =
match files.TryGetValue fileName with
| true, text -> new MemoryStream(Encoding.UTF8.GetBytes(text)) :> Stream
| _ ->
defaultFileSystem.OpenFileForReadShim(
fileName,
?useMemoryMappedFile = useMemoryMappedFile,
?shouldShadowCopy = shouldShadowCopy
)
member _.OpenFileForWriteShim(fileName, ?fileMode: FileMode, ?fileAccess: FileAccess, ?fileShare: FileShare) =
defaultFileSystem.OpenFileForWriteShim(
fileName,
?fileMode = fileMode,
?fileAccess = fileAccess,
?fileShare = fileShare
)
// Implement the service related to file existence and deletion
member _.FileExistsShim(fileName) =
files.ContainsKey(fileName)
|| defaultFileSystem.FileExistsShim(fileName)
// Implement the service related to temporary paths and file time stamps
member _.GetTempPathShim() = defaultFileSystem.GetTempPathShim()
member _.GetLastWriteTimeShim(fileName) =
defaultFileSystem.GetLastWriteTimeShim(fileName)
member _.GetFullPathShim(fileName) =
defaultFileSystem.GetFullPathShim(fileName)
member _.IsInvalidPathShim(fileName) =
defaultFileSystem.IsInvalidPathShim(fileName)
member _.IsPathRootedShim(fileName) =
defaultFileSystem.IsPathRootedShim(fileName)
member _.FileDeleteShim(fileName) =
defaultFileSystem.FileDeleteShim(fileName)
member _.AssemblyLoader = defaultFileSystem.AssemblyLoader
member _.GetFullFilePathInDirectoryShim dir fileName =
defaultFileSystem.GetFullFilePathInDirectoryShim dir fileName
member _.NormalizePathShim(path) =
defaultFileSystem.NormalizePathShim(path)
member _.GetDirectoryNameShim(path) =
defaultFileSystem.GetDirectoryNameShim(path)
member _.GetCreationTimeShim(path) =
defaultFileSystem.GetCreationTimeShim(path)
member _.CopyShim(src, dest, overwrite) =
defaultFileSystem.CopyShim(src, dest, overwrite)
member _.DirectoryCreateShim(path) =
defaultFileSystem.DirectoryCreateShim(path)
member _.DirectoryExistsShim(path) =
defaultFileSystem.DirectoryExistsShim(path)
member _.DirectoryDeleteShim(path) =
defaultFileSystem.DirectoryDeleteShim(path)
member _.EnumerateFilesShim(path, pattern) =
defaultFileSystem.EnumerateFilesShim(path, pattern)
member _.EnumerateDirectoriesShim(path) =
defaultFileSystem.EnumerateDirectoriesShim(path)
member _.IsStableFileHeuristic(path) =
defaultFileSystem.IsStableFileHeuristic(path)
member this.ChangeExtensionShim(path, extension) =
defaultFileSystem.ChangeExtensionShim(path, extension)
let myFileSystem = MyFileSystem()
FileSystem <- MyFileSystem()
Doing a compilation with the FileSystem
let checker = FSharpChecker.Create()
let projectOptions =
let sysLib nm =
if System.Environment.OSVersion.Platform = System.PlatformID.Win32NT then // file references only valid on Windows
System.Environment.GetFolderPath(System.Environment.SpecialFolder.ProgramFilesX86)
+ @"\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\"
+ nm
+ ".dll"
else
let sysDir =
System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory()
let (++) a b = System.IO.Path.Combine(a, b)
sysDir ++ nm + ".dll"
let fsCore4300 () =
if System.Environment.OSVersion.Platform = System.PlatformID.Win32NT then // file references only valid on Windows
System.Environment.GetFolderPath(System.Environment.SpecialFolder.ProgramFilesX86)
+ @"\Reference Assemblies\Microsoft\FSharp\.NETFramework\v4.0\4.3.0.0\FSharp.Core.dll"
else
sysLib "FSharp.Core"
let allFlags =
[| "--simpleresolution"
"--noframework"
"--debug:full"
"--define:DEBUG"
"--optimize-"
"--doc:test.xml"
"--warn:3"
"--fullpaths"
"--flaterrors"
"--target:library"
let references =
[ sysLib "mscorlib"
sysLib "System"
sysLib "System.Core"
fsCore4300 () ]
for r in references do
"-r:" + r |]
{ ProjectFileName = @"c:\mycode\compilation.fsproj" // Make a name that is unique in this directory.
ProjectId = None
SourceFiles = [| fileName1; fileName2 |]
OriginalLoadReferences = []
Stamp = None
OtherOptions = allFlags
ReferencedProjects = [||]
IsIncompleteTypeCheckEnvironment = false
UseScriptResolutionRules = true
LoadTime = System.DateTime.Now // Note using 'Now' forces reloading
UnresolvedReferences = None }
let results =
checker.ParseAndCheckProject(projectOptions)
|> Async.RunSynchronously
results.Diagnostics
results.AssemblySignature.Entities.Count //2
results.AssemblySignature.Entities.[0]
.MembersFunctionsAndValues
.Count
results.AssemblySignature.Entities.[0]
.MembersFunctionsAndValues.[0]
.DisplayName
Summary
In this tutorial, we've seen how to globally customize the view of the file system used by the FSharp.Compiler.Service component.
At the time of writing, the following System.IO operations are not considered part of the virtualized file system API. Future iterations on the compiler service implementation may add these to the API.
- Path.Combine
- Path.DirectorySeparatorChar
- Path.GetDirectoryName
- Path.GetFileName
- Path.GetFileNameWithoutExtension
- Path.HasExtension
- Path.GetRandomFileName (used only in generation compiled win32 resources in assemblies)
NOTE: Several operations in the SourceCodeServices
API accept the contents of a file to parse
or check as a parameter, in addition to a file name. In these cases, the file name is only used for
error reporting.
NOTE: Type provider components do not use the virtualized file system.
NOTE: The compiler service may use MSBuild for assembly resolutions unless --simpleresolution
is
provided. When using the FileSystem
API you will normally want to specify --simpleresolution
as one
of your compiler flags. Also specify --noframework
. You will need to supply explicit resolutions of all
referenced .NET assemblies.
namespace FSharp
--------------------
namespace Microsoft.FSharp
<summary> The global hook into the file system </summary>
type MyFileSystem = interface IFileSystem new: unit -> MyFileSystem
--------------------
new: unit -> MyFileSystem
<summary> Represents a shim for the file system </summary>
type MemoryStream = inherit Stream new: unit -> unit + 6 overloads member BeginRead: buffer: byte array * offset: int * count: int * callback: AsyncCallback * state: obj -> IAsyncResult member BeginWrite: buffer: byte array * offset: int * count: int * callback: AsyncCallback * state: obj -> IAsyncResult member CopyTo: destination: Stream * bufferSize: int -> unit member CopyToAsync: destination: Stream * bufferSize: int * cancellationToken: CancellationToken -> Task member EndRead: asyncResult: IAsyncResult -> int member EndWrite: asyncResult: IAsyncResult -> unit member Flush: unit -> unit member FlushAsync: cancellationToken: CancellationToken -> Task ...
<summary>Creates a stream whose backing store is memory.</summary>
--------------------
MemoryStream() : MemoryStream
MemoryStream(buffer: byte array) : MemoryStream
MemoryStream(capacity: int) : MemoryStream
MemoryStream(buffer: byte array, writable: bool) : MemoryStream
MemoryStream(buffer: byte array, index: int, count: int) : MemoryStream
MemoryStream(buffer: byte array, index: int, count: int, writable: bool) : MemoryStream
MemoryStream(buffer: byte array, index: int, count: int, writable: bool, publiclyVisible: bool) : MemoryStream
<summary>Represents a character encoding.</summary>
<summary>Gets an encoding for the UTF-8 format.</summary>
<returns>An encoding for the UTF-8 format.</returns>
(+0 other overloads)
Encoding.GetBytes(s: string) : byte array
(+0 other overloads)
Encoding.GetBytes(chars: char array) : byte array
(+0 other overloads)
(extension) Encoding.GetBytes(chars: inref<System.Buffers.ReadOnlySequence<char>>, writer: System.Buffers.IBufferWriter<byte>) : int64
(+0 other overloads)
(extension) Encoding.GetBytes(chars: inref<System.Buffers.ReadOnlySequence<char>>, bytes: System.Span<byte>) : int
(+0 other overloads)
(extension) Encoding.GetBytes(chars: System.ReadOnlySpan<char>, writer: System.Buffers.IBufferWriter<byte>) : int64
(+0 other overloads)
Encoding.GetBytes(chars: System.ReadOnlySpan<char>, bytes: System.Span<byte>) : int
(+0 other overloads)
Encoding.GetBytes(s: string, index: int, count: int) : byte array
(+0 other overloads)
Encoding.GetBytes(chars: char array, index: int, count: int) : byte array
(+0 other overloads)
Encoding.GetBytes(chars: nativeptr<char>, charCount: int, bytes: nativeptr<byte>, byteCount: int) : int
(+0 other overloads)
<summary>Provides a generic view of a sequence of bytes. This is an abstract class.</summary>
<summary>Specifies how the operating system should open a file.</summary>
<summary>Defines constants for read, write, or read/write access to a file.</summary>
<summary>Contains constants for controlling the kind of access other operations can have to the same file.</summary>
<summary> Used to parse and check F# source code. </summary>
<summary>Provides information about, and means to manipulate, the current environment and platform. This class cannot be inherited.</summary>
<summary>Gets the current platform identifier and version number.</summary>
<exception cref="T:System.InvalidOperationException">This property was unable to obtain the system version. -or- The obtained platform identifier is not a member of <see cref="T:System.PlatformID" /></exception>
<returns>The platform identifier and version number.</returns>
<summary>Gets a <see cref="T:System.PlatformID" /> enumeration value that identifies the operating system platform.</summary>
<returns>One of the <see cref="T:System.PlatformID" /> values.</returns>
<summary>Identifies the operating system, or platform, supported by an assembly.</summary>
System.Environment.GetFolderPath(folder: System.Environment.SpecialFolder, option: System.Environment.SpecialFolderOption) : string
<summary>Specifies enumerated constants used to retrieve directory paths to system special folders.</summary>
<summary>Provides a collection of <see langword="static" /> methods that return information about the common language runtime environment.</summary>
<summary>Performs operations on <see cref="T:System.String" /> instances that contain file or directory path information. These operations are performed in a cross-platform manner.</summary>
Path.Combine([<System.ParamArray>] paths: string array) : string
Path.Combine(path1: string, path2: string) : string
Path.Combine(path1: string, path2: string, path3: string) : string
Path.Combine(path1: string, path2: string, path3: string, path4: string) : string
[<Struct>] type DateTime = new: date: DateOnly * time: TimeOnly -> unit + 16 overloads member Add: value: TimeSpan -> DateTime member AddDays: value: float -> DateTime member AddHours: value: float -> DateTime member AddMicroseconds: value: float -> DateTime member AddMilliseconds: value: float -> DateTime member AddMinutes: value: float -> DateTime member AddMonths: months: int -> DateTime member AddSeconds: value: float -> DateTime member AddTicks: value: int64 -> DateTime ...
<summary>Represents an instant in time, typically expressed as a date and time of day.</summary>
--------------------
System.DateTime ()
(+0 other overloads)
System.DateTime(ticks: int64) : System.DateTime
(+0 other overloads)
System.DateTime(date: System.DateOnly, time: System.TimeOnly) : System.DateTime
(+0 other overloads)
System.DateTime(ticks: int64, kind: System.DateTimeKind) : System.DateTime
(+0 other overloads)
System.DateTime(date: System.DateOnly, time: System.TimeOnly, kind: System.DateTimeKind) : System.DateTime
(+0 other overloads)
System.DateTime(year: int, month: int, day: int) : System.DateTime
(+0 other overloads)
System.DateTime(year: int, month: int, day: int, calendar: System.Globalization.Calendar) : System.DateTime
(+0 other overloads)
System.DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int) : System.DateTime
(+0 other overloads)
System.DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, kind: System.DateTimeKind) : System.DateTime
(+0 other overloads)
System.DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, calendar: System.Globalization.Calendar) : System.DateTime
(+0 other overloads)
<summary>Gets a <see cref="T:System.DateTime" /> object that is set to the current date and time on this computer, expressed as the local time.</summary>
<returns>An object whose value is the current local date and time.</returns>
member FSharpChecker.ParseAndCheckProject: options: FSharpProjectOptions * ?userOpName: string -> Async<FSharpCheckProjectResults>
type Async = static member AsBeginEnd: computation: ('Arg -> Async<'T>) -> ('Arg * AsyncCallback * objnull -> IAsyncResult) * (IAsyncResult -> 'T) * (IAsyncResult -> unit) static member AwaitEvent: event: IEvent<'Del,'T> * ?cancelAction: (unit -> unit) -> Async<'T> (requires delegate and 'Del :> Delegate) static member AwaitIAsyncResult: iar: IAsyncResult * ?millisecondsTimeout: int -> Async<bool> static member AwaitTask: task: Task<'T> -> Async<'T> + 1 overload static member AwaitWaitHandle: waitHandle: WaitHandle * ?millisecondsTimeout: int -> Async<bool> static member CancelDefaultToken: unit -> unit static member Catch: computation: Async<'T> -> Async<Choice<'T,exn>> static member Choice: computations: Async<'T option> seq -> Async<'T option> static member FromBeginEnd: beginAction: (AsyncCallback * objnull -> IAsyncResult) * endAction: (IAsyncResult -> 'T) * ?cancelAction: (unit -> unit) -> Async<'T> + 3 overloads static member FromContinuations: callback: (('T -> unit) * (exn -> unit) * (OperationCanceledException -> unit) -> unit) -> Async<'T> ...
--------------------
type Async<'T>
<summary> The errors returned by processing the project </summary>
<summary> Get a view of the overall signature of the assembly. Only valid to use if HasCriticalErrors is false. </summary>
<summary> The (non-nested) module and type definitions in this signature </summary>
<summary>Gets the number of elements contained in the <see cref="T:System.Collections.Generic.ICollection`1" />.</summary>
<returns>The number of elements contained in the <see cref="T:System.Collections.Generic.ICollection`1" />.</returns>