1+ module FSharp.Compiler.Service.Tests.FscTests
2+
3+ open System
4+ open System.Diagnostics
5+ open System.IO
6+
7+ open Microsoft.Build .Utilities
8+ open Microsoft.FSharp .Compiler
9+
10+ open NUnit.Framework
11+
12+ exception VerificationException of (* assembly:*) string * (* errorCode:*) int * (* output:*) string
13+ with
14+ override e.Message = sprintf " Verification of '%s ' failed with code %d ." e.Data0 e.Data1
15+
16+ exception CompilationError of (* assembly:*) string * (* errorCode:*) int * (* info:*) ErrorInfo []
17+ with
18+ override e.Message = sprintf " Compilation of '%s ' failed with code %d (%A )" e.Data0 e.Data1 e.Data2
19+
20+ type PEVerifier () =
21+
22+ static let expectedExitCode = 0
23+
24+ let runsOnMono = try System.Type.GetType( " Mono.Runtime" ) <> null with _ -> false
25+ let verifierPath , switches =
26+ if runsOnMono then
27+ " pedump" , " --verify all"
28+ else
29+ let path = ToolLocationHelper.GetPathToDotNetFrameworkSdkFile( " peverify.exe" , TargetDotNetFrameworkVersion.VersionLatest)
30+ path, " /UNIQUE /IL /NOLOGO"
31+
32+ static let execute ( fileName : string , arguments : string ) =
33+ let psi = new ProcessStartInfo( fileName, arguments)
34+ psi.UseShellExecute <- false
35+ psi.ErrorDialog <- false
36+ psi.CreateNoWindow <- true
37+ psi.RedirectStandardOutput <- true
38+ psi.RedirectStandardError <- true
39+
40+ use proc = Process.Start( psi)
41+ let stdOut = proc.StandardOutput.ReadToEnd()
42+ let stdErr = proc.StandardError.ReadToEnd()
43+ while not proc.HasExited do ()
44+ proc.ExitCode, stdOut, stdErr
45+
46+ member __.Verify ( assemblyPath : string ) =
47+ let id , stdOut , stdErr = execute( verifierPath, sprintf " %s \" %s \" " switches assemblyPath)
48+ if id = expectedExitCode && String.IsNullOrWhiteSpace stdErr then ()
49+ else
50+ raise <| VerificationException( assemblyPath, id, stdOut + " \n " + stdErr)
51+
52+
53+ type DebugMode =
54+ | Off
55+ | PdbOnly
56+ | Full
57+
58+ let compileAndVerify isDll debugMode ( assemblyName : string ) ( code : string ) ( dependencies : string list ) =
59+ let scs = new Microsoft.FSharp.Compiler.SimpleSourceCodeServices.SimpleSourceCodeServices()
60+ let verifier = new PEVerifier ()
61+ let tmp = Path.GetTempPath()
62+ let sourceFile = Path.Combine( tmp, assemblyName + " .fs" )
63+ let outFile = Path.Combine( tmp, assemblyName + if isDll then " .dll" else " .exe" )
64+ let pdbFile = Path.Combine( tmp, assemblyName + " .pdb" )
65+ do File.WriteAllText( sourceFile, code)
66+ let args =
67+ [|
68+ // fsc parser skips the first argument by default;
69+ // perhaps this shouldn't happen in library code.
70+ yield " fsc.exe"
71+
72+ if isDll then yield " --target:library"
73+
74+ match debugMode with
75+ | Off -> () // might need to include some switches here
76+ | PdbOnly ->
77+ yield " --debug:pdbonly"
78+ yield sprintf " --pdb:%s " pdbFile
79+ | Full ->
80+ yield " --debug:full"
81+ yield sprintf " --pdb:%s " pdbFile
82+
83+ for d in dependencies do
84+ yield sprintf " -r:%s " d
85+
86+ yield sprintf " --out:%s " outFile
87+
88+ yield sourceFile
89+ |]
90+
91+ let errorInfo , id = scs.Compile args
92+ if id <> 0 then raise <| CompilationError( assemblyName, id, errorInfo)
93+ verifier.Verify outFile
94+ outFile
95+
96+
97+ [<Test>]
98+ let ``1. PEVerifier sanity check`` () =
99+ let verifier = new PEVerifier()
100+
101+ let fscorlib = typeof< int option>. Assembly
102+ verifier.Verify fscorlib.Location
103+
104+ let nonAssembly = Path.Combine( Directory.GetCurrentDirectory(), typeof< PEVerifier>. Assembly.GetName() .Name + " .pdb" )
105+ Assert.Throws< VerificationException>( fun () -> verifier.Verify nonAssembly |> ignore) |> ignore
106+
107+
108+ [<Test>]
109+ let ``2. Simple FSC library test`` () =
110+ let code = """
111+ module Foo
112+
113+ let f x = (x,x)
114+
115+ type Foo = class end
116+
117+ exception E of int * string
118+ """
119+
120+ compileAndVerify true PdbOnly " Foo" code [] |> ignore
121+
122+ [<Test>]
123+ let ``3. Simple FSC executable test`` () =
124+ let code = """
125+ module Bar
126+
127+ [<EntryPoint>]
128+ let main _ = printfn "Hello, World!" ; 42
129+
130+ """
131+ let outFile = compileAndVerify false PdbOnly " Bar" code []
132+
133+ use proc = Process.Start( outFile, " " )
134+ while not proc.HasExited do ()
135+ Assert.AreEqual( proc.ExitCode, 42 )
0 commit comments