Skip to content

Commit 830740a

Browse files
committed
Fsc tool
1 parent 81e7f38 commit 830740a

File tree

8 files changed

+306
-24
lines changed

8 files changed

+306
-24
lines changed

build.fsx

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
// xake build file
2+
#r @"bin/Debug/Xake.Core.dll"
3+
4+
open Xake
5+
6+
let fsc = """C:\Program Files (x86)\Microsoft SDKs\F#\3.0\Framework\v4.0\fsc.exe"""
7+
8+
do xake {XakeOptions with FileLog = "build.log"; ConLogLevel = Verbosity.Chatty } {
9+
10+
rules [
11+
"main" <== ["build"]
12+
"build" <== ["bin/Xake.Core.dll"]
13+
14+
"clean" => action {
15+
do! rm ["bin/Xake.Core.dll"]
16+
}
17+
18+
"bin/Xake.Core.dll" *> fun file -> action {
19+
20+
// TODO --doc:..\bin\Xake.Core.XML --- multitarget rule!
21+
22+
let sources = fileset {
23+
basedir "core"
24+
includes "Logging.fs"
25+
includes "Pickler.fs"
26+
includes "Fileset.fs"
27+
includes "Types.fs"
28+
includes "ArtifactUtil.fs"
29+
includes "CommonLib.fs"
30+
includes "Database.fs"
31+
includes "Action.fs"
32+
includes "WorkerPool.fs"
33+
includes "Progress.fs"
34+
includes "XakeScript.fs"
35+
includes "CommonTasks.fs"
36+
includes "FileTasks.fs"
37+
includes "ResourceFileset.fs"
38+
includes "DotNetFwk.fs"
39+
includes "DotnetTasks.fs"
40+
includes "VersionInfo.fs"
41+
includes "AssemblyInfo.fs"
42+
includes "Program.fs"
43+
}
44+
45+
let! options = getCtxOptions()
46+
let getFiles = toFileList options.ProjectRoot
47+
48+
let (Filelist sourceFiles) = sources |> toFileList options.ProjectRoot
49+
50+
do! needFiles (Filelist sourceFiles)
51+
52+
let (settings,files,refs) =
53+
(
54+
["--target:library"; "--define:TRACE"; "--optimize+"; "--warn:3"; "--warnaserror:76"; "--utf8output"],
55+
["Logging.fs"; "Pickler.fs"; "Fileset.fs"; "Types.fs"; "ArtifactUtil.fs"; "CommonLib.fs"; "Database.fs"; "Action.fs"; "WorkerPool.fs"; "Progress.fs"; "XakeScript.fs"; "CommonTasks.fs"; "FileTasks.fs"; "ResourceFileset.fs"; "DotNetFwk.fs"; "DotnetTasks.fs"; "VersionInfo.fs"; "AssemblyInfo.fs"; "Program.fs"],
56+
["System.dll"; "System.Core.dll"; "System.Windows.Forms.dll"]
57+
)
58+
59+
let! exitcode = system fsc <| ["-o:" + file.FullName; ""] @ settings @ (sourceFiles |> List.map (fun f -> f.FullName)) @ (refs |> List.map (fun n -> "/r:" + n))
60+
61+
do! writeLog Command "job done %A" exitcode
62+
}
63+
64+
"bin/Xake.Core1.dll" *> fun file -> action {
65+
66+
// TODO --doc:..\bin\Xake.Core.XML --- multitarget rule!
67+
68+
let sources = fileset {
69+
basedir "core"
70+
includes "Logging.fs"
71+
includes "Pickler.fs"
72+
includes "Fileset.fs"
73+
includes "Types.fs"
74+
includes "ArtifactUtil.fs"
75+
includes "CommonLib.fs"
76+
includes "Database.fs"
77+
includes "Action.fs"
78+
includes "WorkerPool.fs"
79+
includes "Progress.fs"
80+
includes "XakeScript.fs"
81+
includes "CommonTasks.fs"
82+
includes "FileTasks.fs"
83+
includes "ResourceFileset.fs"
84+
includes "DotNetFwk.fs"
85+
includes "DotnetTasks.fs"
86+
includes "VersionInfo.fs"
87+
includes "AssemblyInfo.fs"
88+
includes "Program.fs"
89+
}
90+
91+
do! Fsc {
92+
FscSettings with
93+
Out = file
94+
Src = sources
95+
RefGlobal = ["System.dll"; "System.Core.dll"; "System.Windows.Forms.dll"]
96+
Define = ["TRACE"]
97+
}
98+
99+
}
100+
]
101+
102+
}

core/CommonTasks.fs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ let system cmd args =
9191
do! writeLog Info "[system] starting '%s'" cmd
9292
let! exitCode = _system SystemOptions cmd (args |> String.concat " ")
9393
do! writeLog Info "[system] сompleted '%s' exitcode: %d" cmd exitCode
94+
9495
return exitCode
9596
}
9697

core/DotNetFwk.fs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ module DotNetFwk =
4444
AssemblyDirs: string list
4545
ToolDir: string
4646
CscTool: string
47+
FscTool: string option
4748
MsbuildTool: string
4849
EnvVars: (string* string) list
4950
}
@@ -107,6 +108,7 @@ module DotNetFwk =
107108
ToolDir = libdir </> "mono" </> libpath
108109
Version = ver
109110
CscTool = csc_tool
111+
FscTool = Some "fsharpi"
110112
MsbuildTool = "xbuild"
111113
EnvVars =["PATH", sdkroot </> "bin" + ";" + (%"PATH")]
112114
}
@@ -124,6 +126,11 @@ module DotNetFwk =
124126
module internal MsImpl =
125127
open registry
126128

129+
let fscTool =
130+
registry.open_subkey registry.HKLM @"SOFTWARE\Wow6432Node\Microsoft\FSharp\3.0\Runtime\v4.0"
131+
|> Option.bind (registry.get_value_str "")
132+
|> Option.bind (fun p -> System.IO.Path.Combine (p, "fsc.exe") |> Some)
133+
127134
let tryLocateFwk fwk =
128135
let fwkKey = open_subkey HKLM @"SOFTWARE\Microsoft\.NETFramework"
129136
let installRoot_ = fwkKey |> Option.bind (get_value_str "InstallRoot")
@@ -163,6 +170,7 @@ module DotNetFwk =
163170
Version = version
164171
AssemblyDirs = asmpaths
165172
CscTool = fwkdir </> "csc.exe"
173+
FscTool = fscTool
166174
MsbuildTool = fwkdir </> "msbuild.exe"
167175
EnvVars = vars
168176
}, null

core/DotnetTasks.fs

Lines changed: 179 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -74,14 +74,46 @@ module DotNetTaskTypes =
7474
FailOnError: bool
7575
}
7676

77+
/// <summary>
78+
/// Fsc (F# compiler) task settings.
79+
/// </summary>
80+
type FscSettingsType = {
81+
/// Limits which platforms this code can run on. The default is anycpu.
82+
Platform: TargetPlatform
83+
/// Specifies the format of the output file.
84+
Target: TargetType
85+
/// Specifies the output file name (default: base name of file with main class or first file).
86+
Out: Artifact
87+
/// Source files.
88+
Src: Fileset
89+
/// References metadata from the specified assembly files.
90+
Ref: Fileset
91+
/// References the specified assemblies from GAC.
92+
RefGlobal: string list
93+
/// Embeds the specified resource.
94+
Resources: ResourceFileset list
95+
/// Defines conditional compilation symbols.
96+
Define: string list
97+
/// Allows unsafe code.
98+
Unsafe: bool
99+
/// Target .NET framework
100+
TargetFramework: string
101+
/// Custom command-line arguments
102+
CommandArgs: string list
103+
/// Build fails on compile error.
104+
FailOnError: bool
105+
106+
Tailcalls: bool
107+
}
108+
77109
[<AutoOpen>]
78110
module DotnetTasks =
79111

80112
open CommonTasks.impl
81113

82114
/// Default setting for CSC task so that you could only override required settings
83115
let CscSettings = {
84-
Platform = AnyCpu
116+
CscSettingsType.Platform = AnyCpu
85117
Target = Auto // try to resolve the type from name etc
86118
Out = Artifact.Undefined
87119
Src = Fileset.Empty
@@ -96,6 +128,7 @@ module DotnetTasks =
96128
}
97129

98130
module internal Impl =
131+
begin
99132
/// Escapes argument according to CSC.exe rules (see http://msdn.microsoft.com/en-us/library/78f4aasd.aspx)
100133
let escapeArgument (str:string) =
101134
let escape c s =
@@ -179,24 +212,26 @@ module DotnetTasks =
179212
(Path.ChangeExtension(res,".resources"),tempfile,true)
180213
| (res,file) ->
181214
(res,file,false)
182-
// end of Impl module
183-
184-
/// C# compiler task
185-
let Csc settings =
186-
187-
let outFile = settings.Out
188215

189216
let resolveTarget (name:string) =
190217
if name.EndsWith (".dll", System.StringComparison.OrdinalIgnoreCase) then Library else
191218
if name.EndsWith (".exe", System.StringComparison.OrdinalIgnoreCase) then Exe else
192219
Library
193220

194-
let rec targetStr = function
221+
let rec targetStr fileName = function
195222
|AppContainerExe -> "appcontainerexe" |Exe -> "exe" |Library -> "library" |Module -> "module" |WinExe -> "winexe" |WinmdObj -> "winmdobj"
196-
|Auto -> outFile.Name |> resolveTarget |> targetStr
223+
|Auto -> fileName |> resolveTarget |> targetStr fileName
224+
197225
let platformStr = function
198226
|AnyCpu -> "anycpu" |AnyCpu32Preferred -> "anycpu32preferred" |ARM -> "arm" | X64 -> "x64" | X86 -> "x86" |Itanium -> "itanium"
199-
227+
228+
end // end of Impl module
229+
230+
/// C# compiler task
231+
let Csc (settings:CscSettingsType) =
232+
233+
let outFile = settings.Out
234+
200235
action {
201236
let! options = getCtxOptions()
202237
let getFiles = toFileList options.ProjectRoot
@@ -241,8 +276,8 @@ module DotnetTasks =
241276
seq {
242277
yield "/nologo"
243278

244-
yield "/target:" + targetStr settings.Target
245-
yield "/platform:" + platformStr settings.Platform
279+
yield "/target:" + Impl.targetStr outFile.Name settings.Target
280+
yield "/platform:" + Impl.platformStr settings.Platform
246281

247282
if settings.Unsafe then
248283
yield "/unsafe"
@@ -396,7 +431,7 @@ module DotnetTasks =
396431
let! dotnetFwk = getVar "NETFX"
397432
let fwkInfo = DotNetFwk.locateFramework dotnetFwk
398433

399-
let pfx = "msbuild" // TODO thread/index
434+
let pfx = "msbuild"
400435

401436
let verbosityKey = function | Quiet -> "q" | Minimal -> "m" | Normal -> "n" | Detailed -> "d" | Diag -> "diag"
402437

@@ -443,3 +478,134 @@ module DotnetTasks =
443478
if settings.FailOnError then failwithf "Exiting due to FailOnError set on '%s'" pfx
444479
()
445480
}
481+
482+
/// <summary>
483+
/// Default settings for Fsc task.
484+
/// </summary>
485+
let FscSettings = {
486+
FscSettingsType.Platform = AnyCpu
487+
FscSettingsType.Target = Auto
488+
Out = Artifact.Undefined
489+
Src = Fileset.Empty
490+
Ref = Fileset.Empty
491+
RefGlobal = []
492+
Resources = []
493+
Define = []
494+
Unsafe = false
495+
TargetFramework = null
496+
CommandArgs = []
497+
FailOnError = true
498+
499+
Tailcalls = true
500+
}
501+
502+
/// F# compiler task
503+
let Fsc (settings:FscSettingsType) =
504+
505+
let outFile = settings.Out
506+
507+
action {
508+
let! options = getCtxOptions()
509+
let getFiles = toFileList options.ProjectRoot
510+
511+
let resinfos = settings.Resources |> List.collect (Impl.collectResInfo options.ProjectRoot) |> List.map Impl.compileResxFiles
512+
let resfiles =
513+
List.ofSeq <|
514+
query {
515+
for (_,file,istemp) in resinfos do
516+
where (not istemp)
517+
select (file)
518+
}
519+
520+
let (Filelist src) = settings.Src |> getFiles
521+
let (Filelist refs) = settings.Ref |> getFiles
522+
523+
do! needFiles (Filelist (src @ refs @ resfiles))
524+
525+
// TODO implement support for targeting various frameworks
526+
let! globalTargetFwk = getVar "NETFX-TARGET"
527+
let targetFramework =
528+
match settings.TargetFramework, globalTargetFwk with
529+
| s, _ when s <> null && s <> "" -> s
530+
| _, Some s when s <> "" -> s
531+
| _ -> null
532+
533+
let (globalRefs,nostdlib,noconfig) =
534+
match targetFramework with
535+
| null ->
536+
let mapfn = (+) "/r:"
537+
// TODO provide an option for user to explicitly specify all grefs (currently csc.rsp is used)
538+
(settings.RefGlobal |> List.map mapfn), false, false
539+
| tgt ->
540+
let fwk = Some tgt |> DotNetFwk.locateFramework in
541+
let lookup = DotNetFwk.locateAssembly fwk
542+
let mapfn = (fun name -> "/r:" + (lookup name))
543+
544+
//do! writeLog Info "Using libraries from %A" fwk.AssemblyDirs
545+
546+
("mscorlib.dll" :: settings.RefGlobal |> List.map mapfn), true, true
547+
548+
let args =
549+
seq {
550+
if noconfig then
551+
yield "/noconfig"
552+
yield "/nologo"
553+
554+
yield "/target:" + Impl.targetStr outFile.Name settings.Target
555+
//yield "/platform:" + Impl.platformStr settings.Platform
556+
557+
if settings.Unsafe then
558+
yield "/unsafe"
559+
560+
if nostdlib then
561+
yield "/nostdlib+"
562+
563+
if not outFile.IsUndefined then
564+
yield sprintf "/out:%s" outFile.FullName
565+
566+
if not (List.isEmpty settings.Define) then
567+
yield "/define:" + (settings.Define |> String.concat ";")
568+
569+
yield! src |> List.map (fun f -> f.FullName)
570+
571+
yield! refs |> List.map ((fun f -> f.FullName) >> (+) "/r:")
572+
yield! globalRefs
573+
574+
yield! resinfos |> List.map (fun(name,file,_) -> sprintf "/res:%s,%s" file.FullName name)
575+
yield! settings.CommandArgs
576+
}
577+
578+
let! dotnetFwk = getVar "NETFX"
579+
let fwkInfo = DotNetFwk.locateFramework dotnetFwk
580+
581+
if Option.isNone fwkInfo.FscTool then
582+
do! writeLog Error "('%s') failed: F# compiler not found" outFile.Name
583+
if settings.FailOnError then failwithf "Exiting due to FailOnError set on '%s'" outFile.Name
584+
585+
let (Some fsc) = fwkInfo.FscTool
586+
587+
do! writeLog Info "compiling '%s' using framework '%s'" outFile.Name fwkInfo.Version
588+
do! writeLog Debug "Command line: '%s %s'" fsc (args |> Seq.map Impl.escapeArgument |> String.concat "\r\n\t")
589+
590+
let options = {
591+
SystemOptions with
592+
LogPrefix = "[FSC] "
593+
StdOutLevel = Level.Verbose // consider standard compiler output too noisy
594+
EnvVars = fwkInfo.EnvVars
595+
}
596+
let! exitCode = _system options fsc (args |> String.concat " ")
597+
598+
do! writeLog Level.Verbose "Deleting temporary files"
599+
seq {
600+
yield! query {
601+
for (_,file,istemp) in resinfos do
602+
where istemp
603+
select file.FullName
604+
}
605+
}
606+
|> Seq.iter File.Delete
607+
608+
if exitCode <> 0 then
609+
do! writeLog Error "('%s') failed with exit code '%i'" outFile.Name exitCode
610+
if settings.FailOnError then failwithf "Exiting due to FailOnError set on '%s'" outFile.Name
611+
}

0 commit comments

Comments
 (0)