Consider the following:
#r @"FakeLib.dll"
open Fake
open Fake.StringHelper
open Fake.ProcessHelper
Shell.Exec("mkdir","exampleDirectory")
Target "DoStuff" ( fun () ->
trace "Doing Stuff..."
)
Target "Clean" ( fun () ->
trace "Cleaning..."
)
Target "Deploy" (fun () ->
trace "Deploying..."
)
"DoStuff"
==>"Clean"
==>"Deploy"
RunTargetOrDefault "Deploy"
The above script works fine, but when I move the Shell.Exec within a Target like this:
#r @"FakeLib.dll"
open Fake
open Fake.StringHelper
open Fake.ProcessHelper
Target "DoStuff" ( fun () ->
trace "Doing Stuff..."
Shell.Exec("mkdir","exampleDirectory")
)
Target "Clean" ( fun () ->
trace "Cleaning..."
)
Target "Deploy" (fun () ->
trace "Deploying..."
)
"DoStuff"
==>"Clean"
==>"Deploy"
RunTargetOrDefault "Deploy"
I end up with a an error:
FsiEvaluationException:
Error:
DeployScript.fsx(10,5): error FS0001: This expression was expected to have type
unit
but here has type
int
Output: [Loading C:\Users\myusername\Desktop\SomeFolder\DeployScript.fsx]
Input: C:\Users\myusername\Desktop\SomeFolder\DeployScript.fsx
\Arguments:
C:\fsi.exe
Exception: Yaaf.FSharp.Scripting.FsiEvaluationException: Error while compiling or executing fsharp snippet. ---> System.Exception: Operation failed. The error text has been print the error stream. To return the corresponding FSharpErrorInfo use the EvalInteractionNonThrowing, EvalScriptNonThrowing or EvalExpressionNonThrowing
at Microsoft.FSharp.Compiler.Interactive.Shell.FsiEvaluationSession.commitResult[a,b](FSharpChoice`2 res)
at Microsoft.FSharp.Compiler.Interactive.Shell.FsiEvaluationSession.EvalScript(String filePath)
at [email protected](String arg00) in C:\code\fake\paket-files\matthid\Yaaf.FSharp.Scripting\src\source\Yaaf.FSharp.Scripting\YaafFSharpScripting.fs:line 1303
at [email protected](Unit unitVar0) in C:\code\fake\paket-files\matthid\Yaaf.FSharp.Scripting\src\source\Yaaf.FSharp.Scripting\YaafFSharpScripting.fs:line 1277
at Yaaf.FSharp.Scripting.Helper.consoleCapture[a](TextWriter out, TextWriter err, FSharpFunc`2 f) in C:\code\fake\paket-files\matthid\Yaaf.FSharp.Scripting\src\source\Yaaf.FSharp.Scripting\YaafFSharpScripting.fs:line 1221
at Yaaf.FSharp.Scripting.Helper.redirectOut@1247[a](Boolean preventStdOut, OutStreamHelper out, OutStreamHelper err, FSharpFunc`2 f) in C:\code\fake\paket-files\matthid\Yaaf.FSharp.Scripting\src\source\Yaaf.FSharp.Scripting\YaafFSharpScripting.fs:line 1254
at [email protected](String text) in C:\code\fake\paket-files\matthid\Yaaf.FSharp.Scripting\src\source\Yaaf.FSharp.Scripting\YaafFSharpScripting.fs:line 1276
--- End of inner exception stack trace ---
at [email protected](String text) in C:\code\fake\paket-files\matthid\Yaaf.FSharp.Scripting\src\source\Yaaf.FSharp.Scripting\YaafFSharpScripting.fs:line 1284
at Yaaf.FSharp.Scripting.Helper.session@1306.Yaaf-FSharp-Scripting-IFsiSession-EvalScriptWithOutput(String ) in C:\code\fake\paket-files\matthid\Yaaf.FSharp.Scripting\src\source\Yaaf.FSharp.Scripting\YaafFSharpScripting.fs:line 1308
at Fake.FSIHelper.runScriptUncached(Boolean useCache, String scriptPath, IEnumerable`1 fsiOptions, Boolean printDetails, CacheInfo cacheInfo, TextWriter out, TextWriter err) in C:\code\fake\src\app\FakeLib\FSIHelper.fs:line 471
DeployScript.fsx(10,5): error FS0001: This expression was expected to have type
The point here is I'm trying to do a shell command only under a certain target. Pay no attention to the mkdir command as I actually would like to use the schtasks command. I just used mkdir for simplicity purposes. Maybe there's a nuance that I'm missing here. I've also noticed this same behavior with using environVarOrFail. Thanks in advance to all who reply.
Shell.Exec
returns an int
, but Target
expects a function that returns a unit
.
Try this:
let returnUnit() = ()
let return42() = 42
Target "One" returnUnit // works
Target "Two" return42 // fails: expected to have type unit, but here has type int
The target "Two" fails to compile, because its second argument is a function that returns an int
, whereas a function returning unit
is expected.
This error is there to keep you from forgetting function return values: the compiler helpfully tells you "Hey, are you sure you don't want to do anything with this int? Then why did you call the function in the first place?"
Sometimes, however, it makes sense to throw away the return value. For example, in this particular case, you're calling Shell.Exec
for its side effect, and its return value does not interest you. This situation occurs surprisingly often, so often that there is a special function for it - ignore
.
Target "DoStuff" ( fun () ->
trace "Doing Stuff..."
Shell.Exec("mkdir","exampleDirectory") |> ignore // compiles fine
)
This function takes any value, throws it away, and returns a unit. By applying it to the result of Shell.Exec
, you tell the compiler: "Yes, I'm sure I don't need this value, please ignore
it."
That said, I would strongly recommend using specialized functions instead of generic system calls. This will make your script more readable, and might even help you catch an occasional error early. For example, to create a directory, use FileUtils.mkdir
:
Target "DoStuff" ( fun () ->
trace "Doing Stuff..."
FileUtils.mkdir "exampleDirectory" // no need for `ignore`, mkdir already returns `unit`
)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With