Skip to content

Commit 481ebee

Browse files
committed
Fix #2342 and add tests to make sure we have good error messages.
1 parent 746b1b2 commit 481ebee

File tree

4 files changed

+87
-33
lines changed

4 files changed

+87
-33
lines changed

integrationtests/i002007-native-libs/before/build.fsx

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,31 @@ let openConn (path:string) =
1212
let conn = new SQLiteConnection(builder.ToString())
1313
conn.OpenAndReturn()
1414

15+
module Imports =
16+
open System.Runtime.InteropServices
17+
[<DllImport("kernel32.dll")>]
18+
extern uint32 GetCurrentProcessId()
19+
[<DllImport("unknown_dependency.dll")>]
20+
extern uint32 UnknownFunctionInDll()
21+
[<DllImport("SQLite.Interop.dll")>]
22+
extern uint32 Fake_ShouldNotExistExtryPoint()
23+
1524
// Default target
1625
Target.create "Default" (fun _ ->
1726
Trace.trace "Hello World from FAKE"
27+
if Environment.isWindows then
28+
// #2342: make sure defaults PATH dependencies still work, see https://github.com/fsharp/FAKE/issues/2342
29+
printfn "Current process: %d" (Imports.GetCurrentProcessId())
30+
1831
use conn = openConn "temp.db"
1932
()
2033
)
34+
Target.create "FailWithUnknown" (fun _ ->
35+
printfn "UnknownFunctionInDll: %d" (Imports.UnknownFunctionInDll())
36+
)
37+
Target.create "FailWithMissingEntry" (fun _ ->
38+
printfn "Fake_ShouldNotExistExtryPoint: %d" (Imports.Fake_ShouldNotExistExtryPoint())
39+
)
2140

2241
// start build
2342
Target.runOrDefault "Default"

src/app/Fake.Runtime/CoreCache.fs

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,7 @@ This can happen for various reasons:
331331
tracefn "Redirect assembly load to previously loaded assembly: %A" loadedName
332332
result
333333

334-
let findUnmanagedInRuntimeDeps (loadFromPath:string -> nativeint) (unmanagedDllName:string) (logLevel:Trace.VerboseLevel) (nativeLibraries:NativeLibrary list) =
334+
let findUnmanagedInRuntimeDeps (unmanagedDllName:string) (logLevel:Trace.VerboseLevel) (nativeLibraries:NativeLibrary list) =
335335
if logLevel.PrintVerbose then tracefn "Trying to resolve native library: %s" unmanagedDllName
336336
match nativeLibraries |> Seq.tryFind (fun l ->
337337
let fnExt = Path.GetFileName l.File
@@ -343,7 +343,7 @@ let findUnmanagedInRuntimeDeps (loadFromPath:string -> nativeint) (unmanagedDllN
343343
path
344344
| None ->
345345
// will failwithf later anyway.
346-
if logLevel.PrintVerbose then tracefn "Could not resolve native library '%s'!" unmanagedDllName
346+
if logLevel.PrintVerbose then tracefn "Could not resolve native library '%s', fallback to CLR-host!" unmanagedDllName
347347
null
348348

349349
let resolveUnmanagedDependencyCached =
@@ -352,17 +352,19 @@ let resolveUnmanagedDependencyCached =
352352
let mutable wasCalled = false
353353
let path = libCache.GetOrAdd(unmanagedDllName, (fun _ ->
354354
wasCalled <- true
355-
findUnmanagedInRuntimeDeps loadFromPath unmanagedDllName logLevel nativeLibraries))
356-
357-
if wasCalled && isNull path then
358-
let available =
359-
if nativeLibraries.Length > 0 then
360-
"Available native libraries:\n - " + String.Join("\n - ", nativeLibraries |> Seq.map (fun n -> n.File))
361-
else "No native libraries found!"
362-
failwithf """Could not resolve native library '%s'.
355+
findUnmanagedInRuntimeDeps unmanagedDllName logLevel nativeLibraries))
356+
357+
//if wasCalled && not wasFound then
358+
//let available =
359+
// if nativeLibraries.Length > 0 then
360+
// "Available native libraries:\n - " + String.Join("\n - ", nativeLibraries |> Seq.map (fun n -> n.File))
361+
// else "No native libraries found!"
362+
// TODO: In the future use the unmanaged assembly resolve event to throw this.
363+
(*failwithf """Could not resolve native library '%s'.
363364
%s
364365
365366
This can happen for various reasons:
367+
- The file '%s' could not be found in the PATH environment variable.
366368
- The nuget cache (or packages folder) might be broken.
367369
-> Please save your state, open an issue and then
368370
- delete the source package of '%s' from the '~/.nuget' cache (and the 'packages' folder)
@@ -371,11 +373,12 @@ This can happen for various reasons:
371373
- try running fake again
372374
- the package should be downloaded again
373375
374-
-> If the above doesn't apply or you need help please open an issue!""" unmanagedDllName available unmanagedDllName
376+
-> If the above doesn't apply or you need help please open an issue!""" unmanagedDllName available unmanagedDllName unmanagedDllName*)
375377
if not wasCalled && not (isNull path) then
376378
if logLevel.PrintVerbose then
377379
tracefn "Redirect native library '%s' to previous loaded library '%s'" unmanagedDllName path
378-
loadFromPath path
380+
// Zero means use CLR-Host strategy, see https://github.com/fsharp/FAKE/issues/2342
381+
if isNull path then IntPtr.Zero else loadFromPath path
379382

380383
#if NETSTANDARD1_6
381384
// See https://github.com/dotnet/coreclr/issues/6411

src/test/Fake.Core.IntegrationTests/SimpleHelloWorldTests.fs

Lines changed: 43 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -48,29 +48,27 @@ let tests =
4848
stdErr.Trim() |> Expect.equal "empty exected" ""
4949

5050
testCase "simple failed to compile" <| fun _ ->
51-
try
52-
fakeRunAndCheck "fail-to-compile.fsx" "fail-to-compile.fsx" "core-simple-failed-to-compile" |> ignProc
53-
fail "Expected an compilation error and a nonzero exit code!"
54-
with
55-
| FakeExecutionFailed(result) ->
56-
let stdOut = String.Join("\n", result.Messages)
57-
let stdErr = String.Join("\n", result.Errors)
58-
stdErr.Contains("klajsdhgfasjkhd")
59-
|> Expect.isTrue (sprintf "Standard Error Output should contain 'klajsdhgfasjkhd', but was: '%s', Out: '%s'" stdErr stdOut)
51+
let result =
52+
expectFailure "Expected an compilation error and a nonzero exit code!" (fun _ ->
53+
fakeRunAndCheck "fail-to-compile.fsx" "fail-to-compile.fsx" "core-simple-failed-to-compile" |> ignProc)
54+
55+
let stdOut = String.Join("\n", result.Messages)
56+
let stdErr = String.Join("\n", result.Errors)
57+
stdErr.Contains("klajsdhgfasjkhd")
58+
|> Expect.isTrue (sprintf "Standard Error Output should contain 'klajsdhgfasjkhd', but was: '%s', Out: '%s'" stdErr stdOut)
6059

61-
checkIntellisense "fail-to-compile.fsx" "core-simple-failed-to-compile"
60+
checkIntellisense "fail-to-compile.fsx" "core-simple-failed-to-compile"
6261

6362
testCase "simple runtime error" <| fun _ ->
64-
try
65-
fakeRunAndCheck "runtime-error.fsx" "runtime-error.fsx" "core-simple-runtime-error" |> ignProc
66-
fail "Expected an runtime error and a nonzero exit code!"
67-
with
68-
| FakeExecutionFailed(result) ->
69-
let stdOut = String.Join("\n", result.Messages)
70-
let stdErr = String.Join("\n", result.Errors)
71-
stdErr.Contains("runtime error")
72-
|> Expect.isTrue (sprintf "Standard Error Output should contain 'runtime error', but was: '%s', Out: '%s'" stdErr stdOut)
73-
checkIntellisense "runtime-error.fsx" "core-simple-runtime-error"
63+
let result =
64+
expectFailure "Expected an runtime error and a nonzero exit code!" (fun _ ->
65+
fakeRunAndCheck "runtime-error.fsx" "runtime-error.fsx" "core-simple-runtime-error" |> ignProc)
66+
67+
let stdOut = String.Join("\n", result.Messages)
68+
let stdErr = String.Join("\n", result.Errors)
69+
stdErr.Contains("runtime error")
70+
|> Expect.isTrue (sprintf "Standard Error Output should contain 'runtime error', but was: '%s', Out: '%s'" stdErr stdOut)
71+
checkIntellisense "runtime-error.fsx" "core-simple-runtime-error"
7472

7573
testCase "reference fake runtime" <| fun _ ->
7674
handleAndFormat <| fun () ->
@@ -155,6 +153,30 @@ let tests =
155153
fakeRunAndCheckInPath "build.fsx" "build.fsx" "i002025" "script" |> ignProc
156154

157155
testCase "issue #2007 - native libs work" <| fun _ ->
156+
// should "just" work
158157
handleAndFormat <| fun () ->
159-
fakeRunAndCheck "build.fsx" "build.fsx" "i002007-native-libs" |> ignore
158+
fakeRunAndCheck "build.fsx" "build.fsx" "i002007-native-libs" |> ignProc
159+
160+
// Should tell FAKE error story
161+
let result =
162+
expectFailure "Expected missing entrypoint error" <| fun () ->
163+
directFake (sprintf "run build.fsx -t FailWithMissingEntry") "i002007-native-libs" |> ignProc
164+
let stdOut = String.Join("\n", result.Messages).Trim()
165+
let stdErr = String.Join("\n", result.Errors)
166+
(stdErr.Contains "Fake_ShouldNotExistExtryPoint" && stdErr.Contains "EntryPointNotFoundException:")
167+
|> Expect.isTrue (sprintf "Standard Error Output should contain 'Fake_ShouldNotExistExtryPoint' and 'EntryPointNotFoundException:', but was: '%s', Out: '%s'" stdErr stdOut)
168+
169+
let result =
170+
expectFailure "Expected missing entrypoint error" <| fun () ->
171+
directFake (sprintf "run build.fsx -t FailWithUnknown") "i002007-native-libs" |> ignProc
172+
let stdOut = String.Join("\n", result.Messages).Trim()
173+
let stdErr = String.Join("\n", result.Errors)
174+
(stdErr.Contains "unknown_dependency.dll" && stdErr.Contains "DllNotFoundException:")
175+
|> Expect.isTrue (sprintf "Standard Error Output should contain 'unknown_dependency.dll' and 'DllNotFoundException:', but was: '%s', Out: '%s'" stdErr stdOut)
176+
// TODO: enable instead of the above
177+
//stdErr.Contains("Could not resolve native library 'unknown_dependency.dll'")
178+
// |> Expect.isTrue (sprintf "Standard Error Output should contain \"Could not resolve native library 'unknown_dependency.dll'\", but was: '%s', Out: '%s'" stdErr stdOut)
179+
//stdErr.Contains("This can happen for various reasons")
180+
// |> Expect.isTrue (sprintf "Standard Error Output should contain \"This can happen for various reasons\", but was: '%s', Out: '%s'" stdErr stdOut)
181+
160182
]

src/test/Fake.Core.IntegrationTests/TestHelpers.fs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,16 @@ let handleAndFormat f =
8282
let stdErr = String.Join("\n", result.Errors)
8383
Expect.isTrue (sprintf "fake.exe failed with code %d\nOut: %s\nError: %s" result.ExitCode stdOut stdErr) false
8484
reraise() // for return value
85+
86+
let expectFailure msg f =
87+
try
88+
f()
89+
Expect.isTrue msg false
90+
failwithf "%s" msg
91+
with FakeExecutionFailed(result) ->
92+
result
93+
94+
8595
let directFake command scenario =
8696
directFakeInPath command (scenarioTempPath scenario) null
8797

0 commit comments

Comments
 (0)