diff --git a/src/app/FakeLib/CscHelper.fs b/src/app/FakeLib/CscHelper.fs new file mode 100644 index 00000000000..4ad05b9ad6d --- /dev/null +++ b/src/app/FakeLib/CscHelper.fs @@ -0,0 +1,121 @@ +/// Contains tasks to compile C# source files with CSC.EXE (C# Compiler). +module Fake.CscHelper +open System + +/// Supported output types +type CscTarget = + | Exe + | Winexe + | Library + | Module + +/// Supported platforms +type CscPlatform = + | X86 + | Itanium + | X64 + | AnyCpu32BitPreferred + | AnyCpu + +/// Compiler parameters +type CscParams = + { /// Specifies the output file name and path. + Output : string + /// Specifies the compiled artifact target type. + Target : CscTarget + /// Specifies the compiled artifact target platform. + Platform : CscPlatform + /// Specifies assemblies to reference for the compilation. + References : string list + /// Specifies whether to emit debug information (default is false). + Debug : bool + /// Specifies other params for the compilation. Freeform strings. + OtherParams : string list } + + /// The default parameters to the compiler. + static member Default = + { Output = "" + Target = Exe + Platform = AnyCpu + References = [] + Debug = false + OtherParams = [] } + +let cscExe (srcFiles : string list) (opts : string list) : int = + let processResult = + ExecProcessAndReturnMessages (fun p -> + p.FileName <- if isMono then "mcs" else "csc" + p.Arguments <- [ + opts |> separated " " + srcFiles |> separated " " + ] |> separated " " + ) (TimeSpan.FromMinutes 10.) + + trace <| sprintf "CSC with args:%A" (Array.ofSeq opts) + + processResult.ExitCode + +/// Compiles the given C# source files with the specified parameters. +/// +/// ## Parameters +/// +/// - `setParams` - Function used to overwrite the default CSC parameters. +/// - `inputFiles` - The C# input files. +/// +/// ## Returns +/// +/// The exit status code of the compile process. +/// +/// ## Sample +/// +/// ["file1.cs"; "file2.cs"] +/// |> csc (fun parameters -> +/// { parameters with Output = ... +/// Target = ... +/// ... }) +let csc (setParams : CscParams -> CscParams) (inputFiles : string list) : int = + let inputFiles = inputFiles |> Seq.toList + let taskDesc = inputFiles |> separated ", " + let cscParams = setParams CscParams.Default + + let output = if cscParams.Output <> "" then [sprintf "/out:%s" cscParams.Output] else [] + let target = + match cscParams.Target with + | Exe -> [ "/target:exe" ] + | Winexe -> [ "/target:winexe" ] + | Library -> [ "/target:library" ] + | Module -> [ "/target:module" ] + let platform = + match cscParams.Platform with + | X86 -> [ "/platform:x86" ] + | Itanium -> [ "/platform:itanium" ] + | X64 -> [ "/platform:x64" ] + | AnyCpu32BitPreferred -> [ "/platform:anycpu32bitpreferred" ] + | AnyCpu -> [ "/platform:anycpu" ] + let references = + cscParams.References + |> List.map (fun r -> sprintf "/reference:%s" r) + let debug = if cscParams.Debug then [ "/debug" ] else [] + let argList = + output @ target @ platform @ references @ debug @ cscParams.OtherParams + traceStartTask "Csc " taskDesc + let res = cscExe inputFiles argList + traceEndTask "Csc " taskDesc + res + +/// Compiles one or more C# source files with the specified parameters. +/// ## Parameters +/// +/// - `setParams` - Function used to overwrite the default CSC parameters. +/// - `inputFiles` - The C# input files. +/// +/// ## Sample +/// +/// ["file1.cs"; "file2.cs"] +/// |> Csc (fun parameters -> +/// { parameters with Output = ... +/// Target = ... +/// ... }) +let Csc (setParams : CscParams -> CscParams) (inputFiles : string list) : unit = + let res = csc setParams inputFiles + if res <> 0 then raise <| BuildException("Csc: compile failed with exit code", [ string res ]) diff --git a/src/app/FakeLib/FakeLib.fsproj b/src/app/FakeLib/FakeLib.fsproj index 09398ec930a..af23dd5c6a4 100644 --- a/src/app/FakeLib/FakeLib.fsproj +++ b/src/app/FakeLib/FakeLib.fsproj @@ -142,6 +142,7 @@ + @@ -637,4 +638,4 @@ - \ No newline at end of file + diff --git a/src/test/Test.FAKECore/CscSpecs.cs b/src/test/Test.FAKECore/CscSpecs.cs new file mode 100644 index 00000000000..8ba1dc83d4f --- /dev/null +++ b/src/test/Test.FAKECore/CscSpecs.cs @@ -0,0 +1,64 @@ +using System; +using System.IO; +using System.Linq; +using Fake; +using Machine.Specifications; +using Microsoft.FSharp.Core; +using Microsoft.FSharp.Collections; + +namespace Test.FAKECore +{ + public class compile_csfiles_to_dll + { + static string tempDir, outFile, csFile1, csFile2; + static FSharpFunc cscParams; + + Establish context = () => + { + tempDir = Path.GetTempPath(); + csFile1 = Path.Combine(tempDir, "test1.cs"); + csFile2 = Path.Combine(tempDir, "test2.cs"); + + File.WriteAllText(csFile1, @" +using System; + +namespace Test { + public class Class1 { + public string Hello(string what) { + return String.Format(""Hello {0}"", what); + } + } +}"); + + File.WriteAllText(csFile2, @" +using System; + +namespace Test { + public class Class2 : Class1 { + public void HelloWorld() { + Console.WriteLine(this.Hello(""World"")); + } + } +}"); + + outFile = Path.Combine(tempDir, "test.dll"); + try { File.Delete(outFile); } catch (FileNotFoundException) {} + + cscParams = FSharpFuncUtil.ToFSharpFunc( + p => new CscHelper.CscParams( + output: outFile, + target: CscHelper.CscTarget.Library, + platform: p.Platform, + references: p.References, + debug: p.Debug, + otherParams: p.OtherParams + ) + ); + }; + + Because of = () => CscHelper.Csc(cscParams, ListModule.OfSeq(new [] { csFile1, csFile2 })); + + It should_compile_to_dll = () => File.Exists(outFile).ShouldBeTrue(); + } +} + diff --git a/src/test/Test.FAKECore/Test.FAKECore.csproj b/src/test/Test.FAKECore/Test.FAKECore.csproj index 4f9d8da6164..c0388ca78c2 100644 --- a/src/test/Test.FAKECore/Test.FAKECore.csproj +++ b/src/test/Test.FAKECore/Test.FAKECore.csproj @@ -119,6 +119,7 @@ + @@ -715,4 +716,4 @@ - \ No newline at end of file +