Skip to content

Commit b61e914

Browse files
committed
Merge branch 'dev'
2 parents 96c56fa + 1f85a65 commit b61e914

File tree

15 files changed

+847
-657
lines changed

15 files changed

+847
-657
lines changed

XakeLibTests/MiscTests.fs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ let ``WhenError handler intercepts the error``() =
151151
]
152152
}
153153
// intercept error for resultless action
154-
do xake {xakeOptions with Threads = 1; FileLog="failf.log"} {
154+
do xake {xakeOptions with Threads = 1; FileLog="failf2.log"} {
155155
rules [
156156
"main" => action {
157157
do! taskReturn 3

XakeLibTests/ProgressTests.fs

Lines changed: 29 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
module ``Progress estimator``
22

33
open NUnit.Framework
4+
open Xake.DomainTypes
45
open Xake.Progress.Estimate
56

67
type TaskDeps = string list
7-
type Task = | Task of string * int * TaskDeps
8+
type Task = | Task of string * int<ms> * TaskDeps
89

910
let internal estimate threadCount completed_tasks tasks goals =
1011

1112
let getTaskName (Task (name,_,_)) = name
1213

13-
let tasks_map = completed_tasks |> List.map (fun t -> (t, 0)) |> Map.ofList
14-
let machine_state = {Cpu = BusyUntil 0 |> List.replicate threadCount; Tasks = tasks_map}
14+
let tasks_map = completed_tasks |> List.map (fun t -> (t, 0<ms>)) |> Map.ofList
15+
let machine_state = {Cpu = BusyUntil 0<ms> |> List.replicate threadCount; Tasks = tasks_map}
1516
let taskMap = tasks |> List.map (fun task -> getTaskName task, task) |> Map.ofList
1617

1718
let taskByName name = Map.find name taskMap
@@ -20,63 +21,63 @@ let internal estimate threadCount completed_tasks tasks goals =
2021
in
2122
endTime
2223

23-
[<TestCase(1, Result = 8)>]
24-
[<TestCase(6, Result = 8)>]
24+
[<TestCase(1, Result = 8<ms>)>]
25+
[<TestCase(6, Result = 8<ms>)>]
2526
let Test1(threads) =
2627

2728
let tasks1 =
2829
[
29-
Task ("build", 1, ["link"])
30-
Task ("link", 2, ["compile"])
31-
Task ("compile", 5, [])
30+
Task ("build", 1<ms>, ["link"])
31+
Task ("link", 2<ms>, ["compile"])
32+
Task ("compile", 5<ms>, [])
3233
]
3334

3435
estimate threads [] tasks1 ["build"]
3536

36-
[<TestCase(1, Result = 12)>]
37-
[<TestCase(2, Result = 10)>]
37+
[<TestCase(1, Result = 12<ms>)>]
38+
[<TestCase(2, Result = 10<ms>)>]
3839
let TestPara(threads) =
3940

4041
let tasks1 =
4142
[
42-
Task ("build", 1, ["link1"; "link2"])
43-
Task ("link1", 2, ["compile"])
44-
Task ("link2", 2, ["compile"])
45-
Task ("compile", 7, [])
43+
Task ("build", 1<ms>, ["link1"; "link2"])
44+
Task ("link1", 2<ms>, ["compile"])
45+
Task ("link2", 2<ms>, ["compile"])
46+
Task ("compile", 7<ms>, [])
4647
]
4748

4849
estimate threads [] tasks1 ["build"]
4950

50-
[<TestCase(6, Result = 11)>]
51-
[<TestCase(1, Result = 21)>]
51+
[<TestCase(6, Result = 11<ms>)>]
52+
[<TestCase(1, Result = 21<ms>)>]
5253
let ComplexCase(threads) =
5354
let tasks1 =
5455
[
55-
Task ("build", 1, ["compile"])
56-
Task ("compile", 5,
56+
Task ("build", 1<ms>, ["compile"])
57+
Task ("compile", 5<ms>,
5758
[
5859
"version.h"
5960
"commonheader.h"
6061
"resources"
6162
"resources-ru"
6263
])
63-
Task ("version.h", 4, [])
64-
Task ("commonheader.h", 4, [])
65-
Task ("resources", 2, ["strings"])
66-
Task ("resources-ru", 3, ["strings"])
67-
Task ("strings", 2, [])
64+
Task ("version.h", 4<ms>, [])
65+
Task ("commonheader.h", 4<ms>, [])
66+
Task ("resources", 2<ms>, ["strings"])
67+
Task ("resources-ru", 3<ms>, ["strings"])
68+
Task ("strings", 2<ms>, [])
6869
]
6970
estimate threads [] tasks1 ["build"]
7071

71-
[<TestCase(1, Result = 9)>]
72-
[<TestCase(2, Result = 5)>]
72+
[<TestCase(1, Result = 9<ms>)>]
73+
[<TestCase(2, Result = 5<ms>)>]
7374
let TestPara2(threads) =
7475

7576
let tasks1 =
7677
[
77-
Task ("main", 0, ["t1"; "t2"])
78-
Task ("t1", 4, [])
79-
Task ("t2", 5, [])
78+
Task ("main", 0<ms>, ["t1"; "t2"])
79+
Task ("t1", 4<ms>, [])
80+
Task ("t2", 5<ms>, [])
8081
]
8182

8283
estimate threads [] tasks1 ["main"]

XakeLibTests/XakeScriptTests.fs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ let ``allows to define target in parameters``() =
202202
Assert.AreEqual(0, !mainCount)
203203
Assert.AreEqual(1, !xxxCount)
204204

205-
[<Test; Platform("Win")>]
205+
[<Test; Platform("Win"); Explicit("Won't run in console nunit")>]
206206
let ``target could be a relative``() =
207207

208208
let needExecuteCount = ref 0
@@ -435,4 +435,24 @@ let ``writes a build stats to a database``() =
435435
printfn "%A" raaa
436436
finally
437437
db.PostAndReply CloseWait
438-
438+
439+
[<Test>]
440+
let ``dryrun for not executing``() =
441+
442+
let count = ref 0
443+
444+
do xake XakeOptions {
445+
dryrun
446+
filelog "errors.log" Verbosity.Chatty
447+
rules [
448+
"main" <== ["rule1"; "rule2"]
449+
"rule1" => action {
450+
count := !count + 1
451+
}
452+
"rule2" => action {
453+
count := !count + 10
454+
}
455+
]
456+
}
457+
458+
Assert.AreEqual(0, !count)

build.fsx

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,18 @@ System.Environment.CurrentDirectory <- __SOURCE_DIRECTORY__
55
let file = System.IO.Path.Combine("packages", "Xake.Core.dll")
66
if not (System.IO.File.Exists file) then
77
printf "downloading xake.core assembly..."; System.IO.Directory.CreateDirectory("packages") |> ignore
8-
let url = "https://github.com/OlegZee/Xake/releases/download/v0.6.26/Xake.Core.dll"
8+
let url = "https://github.com/OlegZee/Xake/releases/download/v0.6.27/Xake.Core.dll"
99
use wc = new System.Net.WebClient() in wc.DownloadFile(url, file + "__"); System.IO.File.Move(file + "__", file)
1010
printfn ""
1111

1212
#r @"packages/Xake.Core.dll"
1313
//#r @"bin/Debug/Xake.Core.dll"
1414

1515
open Xake
16+
open Xake.SystemTasks
1617

1718
let TestsAssembly = "bin/XakeLibTests.dll"
1819

19-
let systemClr cmd args =
20-
let cmd',args' = if Xake.Env.isUnix then "mono", cmd::args else cmd,args
21-
in system cmd' args'
22-
2320
do xake {ExecOptions.Default with Vars = ["NETFX-TARGET", "4.5"]; FileLog = "build.log"; ConLogLevel = Verbosity.Chatty } {
2421

2522
rules [
@@ -34,19 +31,18 @@ do xake {ExecOptions.Default with Vars = ["NETFX-TARGET", "4.5"]; FileLog = "bui
3431
do! rm ["bin/*.*"]
3532
}
3633

37-
"get-deps" => action {
38-
let! exit_code1 = systemClr ".paket/paket.bootstrapper.exe" []
39-
let! exit_code2 = systemClr ".paket/paket.exe" ["install"]
40-
41-
if exit_code1 <> 0 || exit_code2 <> 0 then
42-
failwith "Failed to install packages"
34+
"get-deps" =>
35+
action {
36+
try
37+
do! system (useClr >> checkErrorLevel) ".paket/paket.bootstrapper.exe" [] |> Action.Ignore
38+
do! system (useClr >> checkErrorLevel) ".paket/paket.exe" ["install"] |> Action.Ignore
39+
with e ->
40+
failwithf "Failed to install packages. Error is %s" e.Message
4341
}
4442

4543
"test" => action {
4644
do! need[TestsAssembly]
47-
let! exit_code = systemClr "packages/NUnit.Runners/tools/nunit-console.exe" [TestsAssembly]
48-
if exit_code <> 0 then
49-
failwith "Failed to test"
45+
do! system (useClr >> checkErrorLevel) "packages/NUnit.Runners/tools/nunit-console.exe" [TestsAssembly] |> Action.Ignore
5046
}
5147

5248
("bin/FSharp.Core.dll") *> fun outfile ->
@@ -80,7 +76,11 @@ do xake {ExecOptions.Default with Vars = ["NETFX-TARGET", "4.5"]; FileLog = "bui
8076
includes "ActionFunctions.fs"
8177
includes "WorkerPool.fs"
8278
includes "Progress.fs"
79+
includes "ExecTypes.fs"
80+
includes "DependencyAnalysis.fs"
81+
includes "ExecCore.fs"
8382
includes "XakeScript.fs"
83+
includes "ScriptFuncs.fs"
8484
includes "SystemTasks.fs"
8585
includes "FileTasks.fs"
8686
includes "ResourceFileset.fs"

core/DependencyAnalysis.fs

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
module internal Xake.DependencyAnalysis
2+
3+
open Xake
4+
open Storage
5+
6+
/// <summary>
7+
/// Dependency state.
8+
/// </summary>
9+
type ChangeReason =
10+
| NotChanged
11+
| Depends of Target
12+
| DependsMissingTarget of Target
13+
| Refs of string list
14+
| FilesChanged of string list
15+
| Other of string
16+
17+
let TimeCompareToleranceMs = 10.0
18+
19+
/// <summary>
20+
/// Gets target execution time in the last run
21+
/// </summary>
22+
/// <param name="ctx"></param>
23+
/// <param name="target"></param>
24+
let getExecTime ctx target =
25+
(fun ch -> Storage.GetResult(target, ch)) |> ctx.Db.PostAndReply
26+
|> Option.fold (fun _ r -> r.Steps |> List.sumBy (fun s -> s.OwnTime)) 0<ms>
27+
28+
let targetName = function
29+
| PhonyAction a -> a
30+
| FileTarget file -> file.Name
31+
32+
/// Gets single dependency state and reason of a change.
33+
let getDepState getVar getFileList (getChangedDeps: Target -> ChangeReason list) = function
34+
| FileDep (a:File, wrtime) when not((File.exists a) && abs((File.getLastWriteTime a - wrtime).TotalMilliseconds) < TimeCompareToleranceMs) ->
35+
let dbgInfo = File.exists a |> function
36+
| false -> "file does not exists"
37+
| _ -> sprintf "write time: %A vs %A" (File.getLastWriteTime a) wrtime
38+
ChangeReason.FilesChanged [a.Name], Some dbgInfo
39+
40+
| ArtifactDep (FileTarget file) when not (File.exists file) ->
41+
ChangeReason.DependsMissingTarget (FileTarget file), None
42+
43+
| ArtifactDep dependeeTarget ->
44+
dependeeTarget |> getChangedDeps |> List.filter ((<>) ChangeReason.NotChanged)
45+
|> function
46+
| [] -> NotChanged, None
47+
|item::_ ->
48+
ChangeReason.Depends dependeeTarget, Some (sprintf "E.g. %A..." item)
49+
50+
| EnvVar (name,value) when value <> Util.getEnvVar name ->
51+
ChangeReason.Other <| sprintf "Environment variable %s was changed from '%A' to '%A'" name value (Util.getEnvVar name), None
52+
53+
| Var (name,value) when value <> getVar name ->
54+
ChangeReason.Other <| sprintf "Global script variable %s was changed '%A'->'%A'" name value (getVar name), None
55+
56+
| AlwaysRerun ->
57+
ChangeReason.Other <| "AlwaysRerun rule", Some "Rule indicating target has to be run regardless dependencies state"
58+
59+
| GetFiles (fileset,files) ->
60+
let newfiles = getFileList fileset
61+
let diff = compareFileList files newfiles
62+
63+
if List.isEmpty diff then
64+
NotChanged, None
65+
else
66+
Other <| sprintf "File list is changed for fileset %A" fileset, Some (sprintf "The diff list is %A" diff)
67+
| _ -> NotChanged, None
68+
69+
70+
/// <summary>
71+
/// Gets the list of reasons to rebuilt the target. Empty list means target is not changed.
72+
/// </summary>
73+
/// <param name="ctx"></param>
74+
/// <param name="getTargetDeps">gets state for nested dependency</param>
75+
/// <param name="target">The target to analyze</param>
76+
let getChangeReasons ctx getTargetDeps target =
77+
78+
// separates change reason into two lists and collabses FilesChanged all into one
79+
let collapseFilesChanged reasons =
80+
let files, other = reasons |> List.partition (fst >> function | ChangeReason.FilesChanged _ -> true | _ -> false)
81+
let filesChangedDbg = files |> List.collect (snd >> Option.toList)
82+
let filesChanged = files |> List.collect (fst >> fun (FilesChanged files) -> files) |> function | [] -> [] | ls -> [FilesChanged ls, Some (sprintf "%A" filesChangedDbg)]
83+
in
84+
filesChanged @ other |> List.rev
85+
86+
87+
let lastBuild = (fun ch -> GetResult(target, ch)) |> ctx.Db.PostAndReply
88+
89+
match lastBuild with
90+
| Some {BuildResult.Depends = []} ->
91+
[ChangeReason.Other "No dependencies", Some "It means target is not \"pure\" and depends on something beyond our control (oracle)"]
92+
93+
| Some {BuildResult.Depends = depends; Result = result} ->
94+
let dep_state = getDepState (Util.getVar ctx.Options) (toFileList ctx.Options.ProjectRoot) getTargetDeps
95+
96+
depends
97+
|> List.map dep_state
98+
|> List.filter (fst >> (<>) ChangeReason.NotChanged)
99+
|> collapseFilesChanged
100+
|> function
101+
| [] ->
102+
match result with
103+
| FileTarget file when not (File.exists file) ->
104+
[ChangeReason.Other "target file does not exist", Some "The file has to be rebuilt regardless all its dependencies were not changed"]
105+
| _ -> []
106+
| ls -> ls
107+
108+
| _ ->
109+
[ChangeReason.Other "Not built yet", Some "Target was not built before or build results were cleaned so we don't know dependencies."]
110+
|> List.map fst
111+
112+
// gets task duration and list of targets it depends on. No clue why one method does both.
113+
let getDurationDeps ctx getDeps t =
114+
let collectTargets = List.collect (function |Depends t |DependsMissingTarget t -> [t] | _ -> [])
115+
getExecTime ctx t, getDeps t |> collectTargets
116+
117+
/// Dumps all dependencies for particular target
118+
let dumpDeps (ctx: ExecContext) (target: Target list) =
119+
120+
let rec getDeps = getChangeReasons ctx (fun x -> getDeps x) |> memoize
121+
let doneTargets = new System.Collections.Hashtable()
122+
let indent i = String.replicate i " "
123+
124+
let rec displayNestedDeps ii =
125+
function
126+
| ArtifactDep dependeeTarget ->
127+
printfn "%sArtifact: %A" (indent ii) dependeeTarget.FullName
128+
showTargetStatus (ii+1) dependeeTarget
129+
| _ -> ()
130+
and showDepStatus ii (d: Dependency) =
131+
match d with
132+
| AlwaysRerun ->
133+
printfn "%sAlways Rerun" (indent ii)
134+
| FileDep (a:File, wrtime) ->
135+
let changed = File.exists a |> function
136+
| true when abs((File.getLastWriteTime a - wrtime).TotalMilliseconds) >= TimeCompareToleranceMs ->
137+
sprintf "CHANGED (%A <> %A)" wrtime (File.getLastWriteTime a)
138+
| false ->
139+
"NOT EXISTS"
140+
| _ -> ""
141+
printfn "%sFile '%s' %A %s" (indent ii) a.Name wrtime changed
142+
143+
| EnvVar (name,value) ->
144+
let newValue = Util.getEnvVar name
145+
let changed = if value <> newValue then sprintf "CHANGED %A => %A" value newValue else ""
146+
printfn "%sENV Var: '%s' = %A %s" (indent ii) name value changed
147+
148+
| Var (name,value) ->
149+
printfn "%sScript var: '%s' = %A" (indent ii) name value
150+
151+
| GetFiles (fileset,files) ->
152+
printfn "%sGetFiles: %A" (indent ii) fileset
153+
| _ ->
154+
()
155+
and showTargetStatus ii (target: Target) =
156+
if not <| doneTargets.ContainsKey(target) then
157+
doneTargets.Add(target, 1)
158+
159+
printfn "%sTarget %A" (indent ii) target.ShortName
160+
161+
let lastResult = (fun ch -> GetResult(target, ch)) |> ctx.Db.PostAndReply
162+
match lastResult with
163+
| Some {BuildResult.Depends = []} ->
164+
printfn "%sno dependencies" (indent ii)
165+
| Some {BuildResult.Depends = deps} ->
166+
deps |> List.iter (showDepStatus (ii+1))
167+
deps |> List.iter (displayNestedDeps (ii+1))
168+
| None ->
169+
printfn "%sno built yet (no stats)" (indent ii)
170+
171+
target |> List.iter (showTargetStatus 0)

0 commit comments

Comments
 (0)