为什么有些 FAKE 方法在目标函数中不起作用?

Why Is It That Some FAKE Methods Don't Work Within Target Functions?

考虑以下几点:

#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"

上面的脚本工作正常,但是当我像这样在目标中移动 Shell.Exec 时:

#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"

我遇到了一个错误:

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 Yaaf.FSharp.Scripting.Helper.evalScript@1303.Invoke(String arg00) in C:\code\fake\paket-files\matthid\Yaaf.FSharp.Scripting\src\source\Yaaf.FSharp.Scripting\YaafFSharpScripting.fs:line 1303
   at Yaaf.FSharp.Scripting.Helper.save_@1276-2.Invoke(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 Yaaf.FSharp.Scripting.Helper.save_@1275-1.Invoke(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 Yaaf.FSharp.Scripting.Helper.save_@1275-1.Invoke(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

这里的重点是我试图仅在特定目标下执行 shell 命令。不要注意 mkdir 命令,因为我实际上想使用 schtasks 命令。为了简单起见,我只是使用 mkdir。也许我在这里遗漏了细微差别。我还注意到使用 environVarOrFail 时有同样的行为。预先感谢所有回复的人。

Shell.Exec returns 一个 int,但是 Target 期望一个 returns 一个 unit.

的函数

试试这个:

let returnUnit() = ()
let return42() = 42

Target "One" returnUnit // works
Target "Two" return42 // fails: expected to have type unit, but here has type int

目标 "Two" 编译失败,因为它的第二个参数是 return 一个 int 的函数,而一个 returning [=16= 的函数] 预计。

这个错误是为了防止你忘记函数 return 的值:编译器会告诉你 "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?"

然而,有时丢弃 return 值是有意义的。例如,在这种特殊情况下,您调用 Shell.Exec 是为了它的副作用,而您对它的 return 值不感兴趣。这种情况出奇地经常发生,以至于有一个特殊的功能来应对它 - ignore.

Target "DoStuff" ( fun () -> 
    trace "Doing Stuff..."
    Shell.Exec("mkdir","exampleDirectory") |> ignore // compiles fine
)

这个函数取任何值,把它扔掉,return是一个单位。通过将它应用于 Shell.Exec 的结果,您告诉编译器:"Yes, I'm sure I don't need this value, please ignore it."


也就是说,我强烈建议使用专用函数而不是通用系统调用。这将使您的脚本更具可读性,甚至可以帮助您尽早发现偶尔出现的错误。例如,要创建目录,请使用 FileUtils.mkdir:

Target "DoStuff" ( fun () -> 
    trace "Doing Stuff..."
    FileUtils.mkdir "exampleDirectory"  // no need for `ignore`, mkdir already returns `unit`
)