Skip to content

Commit

Permalink
Merge pull request #2140 from BlythMeister/TargetContext
Browse files Browse the repository at this point in the history
Seperate runInternal & handle error
  • Loading branch information
matthid authored Oct 13, 2018
2 parents 6b9e62e + 47e325d commit fe979ba
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 46 deletions.
37 changes: 37 additions & 0 deletions help/markdown/core-targets.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,43 @@ Everything after the target will be interpreted as argument for the target:
You can access the arguments from every target executed along the way.
## Setting build status
You can set the build status automatically using `Target.updateBuildStatus`
Example:
```fsharp
#r "paket:
nuget Fake.Core.Target //"
open Fake.Core
// *** Define Targets ***
Target.create "Clean" (fun p ->
Trace.trace " --- Cleaning stuff --- "
)
Target.create "Build" (fun _ ->
Trace.trace " --- Building the app --- "
)
Target.create "Deploy" (fun _ ->
Trace.trace " --- Deploying app --- "
)
open Fake.Core.TargetOperators
// *** Define Dependencies ***
"Clean"
==> "Build"
==> "Deploy"
// *** Start Build ***
Target.runOrDefaultAndGetContext "Deploy" //Could also use: Target.runAndGetOptionalContext "Deploy"
|> Target.raiseIfError
```
## Final targets
Final targets can be used for TearDown functionality.
Expand Down
107 changes: 71 additions & 36 deletions src/app/Fake.Core.Target/Target.fs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ and [<NoComparison>] [<NoEquality>] TargetContext =
x.PreviousTargets |> List.tryFind (fun t -> t.Target.Name = name)
member x.TryFindTarget name =
x.AllExecutingTargets |> List.tryFind (fun t -> t.Name = name)
member x.ErrorTargets =
x.PreviousTargets |> List.choose (fun tres -> match tres.Error with
| Some er -> Some (er, tres.Target)
| None -> None)

and [<NoComparison>] [<NoEquality>] TargetParameter =
{ TargetInfo : Target
Expand Down Expand Up @@ -446,17 +450,13 @@ module Target =
aligned "Total:" total null
if not context.HasError then
aligned "Status:" "Ok" null
//Trace.setBuildState TagStatus.Success
else
alignedError "Status:" "Failure" null
//Trace.setBuildState TagStatus.Failed
else
Trace.traceError "No target was successfully completed"
//Trace.setBuildState TagStatus.Warning

Trace.traceLine()


/// Determines a parallel build order for the given set of targets
let internal determineBuildOrder (target : string) =
let _ = get target
Expand Down Expand Up @@ -652,6 +652,11 @@ module Target =
|> Observable.subscribe (fun _ -> Environment.Exit 1)
Process.killAllCreatedProcesses() |> ignore
cts.Cancel()

/// Optional `TargetContext`
type OptionalTargetContext =
| Set of TargetContext
| MaybeSet of TargetContext option

/// Runs a target and its dependencies.
let internal runInternal singleTarget parallelJobs targetName args =
Expand Down Expand Up @@ -705,29 +710,12 @@ module Target =

if context.HasError && not context.CancellationToken.IsCancellationRequested then
runBuildFailureTargets context
else context
else
context

let context = runFinalTargets {context with IsRunningFinalTargets=true}
writeTaskTimeSummary watch.Elapsed context
if context.HasError && not context.CancellationToken.IsCancellationRequested then
let errorTargets =
context.PreviousTargets
|> List.choose (fun tres ->
match tres.Error with
| Some er -> Some (er, tres.Target)
| None -> None)
let targets = errorTargets |> Seq.map (fun (_er, target) -> target.Name) |> Seq.distinct
let targetStr = String.Join(", ", targets)
let errorMsg =
if errorTargets.Length = 1 then
sprintf "Target '%s' failed." targetStr
else
sprintf "Targets '%s' failed." targetStr
let inner = AggregateException(AggregateException().Message, errorTargets |> Seq.map fst)
BuildFailedException(context, errorMsg, inner)
|> raise

context
context

/// Creates a target in case of build failure (not activated).
let createBuildFailure name body =
Expand Down Expand Up @@ -759,13 +747,40 @@ module Target =
let t = get name // test if target is defined
getFinalTargets().[name] <- false

/// Runs a target and its dependencies, used for testing - usually not called in scripts.
let internal getBuildFailedException (context:TargetContext) =
let targets = context.ErrorTargets |> Seq.map (fun (_er, target) -> target.Name) |> Seq.distinct
let targetStr = String.Join(", ", targets)
let errorMsg =
if context.ErrorTargets.Length = 1 then
sprintf "Target '%s' failed." targetStr
else
sprintf "Targets '%s' failed." targetStr
let inner = AggregateException(AggregateException().Message, context.ErrorTargets |> Seq.map fst)
BuildFailedException(context, errorMsg, inner)

let private getTargetContext (context:OptionalTargetContext) =
match context with
| Set c -> Some(c)
| MaybeSet c -> c

/// If `TargetContext option` is Some and has error, raise it as a BuildFailedException
let raiseIfError (context:OptionalTargetContext) =
let c = getTargetContext(context)
if c.IsSome && c.Value.HasError && not c.Value.CancellationToken.IsCancellationRequested then
getBuildFailedException c.Value
|> raise
context

/// Runs a target and its dependencies and returns a `TargetContext`
let runAndGetContext parallelJobs targetName args = runInternal false parallelJobs targetName args

/// Runs a target and its dependencies
let run parallelJobs targetName args = runInternal false parallelJobs targetName args |> ignore
/// Runs a target and its dependencies and returns an `OptionalTargetContext`
let runAndGetOptionalContext parallelJobs targetName args = runAndGetContext parallelJobs targetName args |> OptionalTargetContext.Set

let internal runWithDefault allowArgs fDefault =
/// Runs a target and its dependencies
let run parallelJobs targetName args = runAndGetOptionalContext parallelJobs targetName args |> raiseIfError |> ignore

let internal getRunFunction allowArgs defaultTarget =
let ctx = Fake.Core.Context.forceFakeContext ()
let trySplitEnvArg (arg:string) =
let idx = arg.IndexOf('=')
Expand All @@ -790,11 +805,14 @@ module Target =

if DocoptResult.hasFlag "--list" results then
listAvailable()
None
elif DocoptResult.hasFlag "-h" results || DocoptResult.hasFlag "--help" results then
printfn "%s" TargetCli.targetCli
printfn "Hint: Run 'fake run <build.fsx> target <target> --help' to get help from your target."
None
elif DocoptResult.hasFlag "--version" results then
printfn "Target Module Version: %s" AssemblyVersionInformation.AssemblyInformationalVersion
None
else
let target =
match DocoptResult.tryGetArgument "<target>" results with
Expand Down Expand Up @@ -833,23 +851,40 @@ module Target =
| None -> []
if not allowArgs && arguments <> [] then
failwithf "The following arguments could not be parsed: %A\nTo forward arguments to your targets you need to use \nTarget.runOrDefaultWithArguments instead of Target.runOrDefault" arguments
match target with
| Some t -> runInternal singleTarget parallelJobs t arguments |> ignore
| None -> fDefault singleTarget parallelJobs arguments
match target, defaultTarget with
| Some t, _ -> Some(fun () -> Some(runInternal singleTarget parallelJobs t arguments))
| None, Some t -> Some(fun () -> Some(runInternal singleTarget parallelJobs t arguments))
| None, None -> Some (fun () -> listAvailable()
None)
| Choice2Of2 e ->
// To ensure exit code.
raise <| exn (sprintf "Usage error: %s\n%s" e.Message TargetCli.targetCli, e)

let private runFunction (targetFunction:(unit -> TargetContext option) Option) =
match targetFunction with
| Some f -> OptionalTargetContext.MaybeSet(f())
| _ -> OptionalTargetContext.MaybeSet(None)

/// Runs the command given on the command line or the given target when no target is given & get context
let runOrDefaultAndGetContext defaultTarget =
getRunFunction false (Some(defaultTarget)) |> runFunction

/// Runs the command given on the command line or the given target when no target is given
let runOrDefault defaultTarget =
runWithDefault false (fun singleTarget parallelJobs arguments ->
runInternal singleTarget parallelJobs defaultTarget arguments |> ignore)
runOrDefaultAndGetContext defaultTarget |> raiseIfError |> ignore

/// Runs the command given on the command line or the given target when no target is given & get context
let runOrDefaultWithArgumentsAndGetContext defaultTarget =
getRunFunction true (Some(defaultTarget)) |> runFunction

/// Runs the command given on the command line or the given target when no target is given
let runOrDefaultWithArguments defaultTarget =
runWithDefault true (fun singleTarget parallelJobs arguments ->
runInternal singleTarget parallelJobs defaultTarget arguments |> ignore)
runOrDefaultWithArgumentsAndGetContext defaultTarget |> raiseIfError |> ignore

/// Runs the target given by the target parameter or lists the available targets & get context
let runOrListAndGetContext() =
getRunFunction false None |> runFunction

/// Runs the target given by the target parameter or lists the available targets
let runOrList() =
runWithDefault false (fun _ _ _ -> listAvailable())
runOrListAndGetContext() |> raiseIfError |> ignore
12 changes: 2 additions & 10 deletions src/test/Fake.Core.UnitTests/Fake.Core.Target.fs
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,10 @@ open Fake.Core
open Expecto

let run targetName =
try Target.runAndGetContext 1 targetName []
with | :? BuildFailedException as bfe ->
match bfe.Info with
| Some context -> context
| None -> failwithf "No context given!"
Target.runAndGetContext 1 targetName []

let runParallel targetName =
try Target.runAndGetContext 3 targetName []
with | :? BuildFailedException as bfe ->
match bfe.Info with
| Some context -> context
| None -> failwithf "No context given!"
Target.runAndGetContext 3 targetName []

open Fake.Core.TargetOperators

Expand Down

0 comments on commit fe979ba

Please sign in to comment.