PageRenderTime 59ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/compiler/3.1/Nov2013/src/fsharp/vs/IncrementalBuild.fs

#
F# | 1769 lines | 1309 code | 220 blank | 240 comment | 201 complexity | fd86a291fcc725996110a0d919bcd7cc MD5 | raw file
Possible License(s): CC-BY-SA-3.0, Apache-2.0
  1. namespace Microsoft.FSharp.Compiler
  2. #nowarn "57"
  3. open Internal.Utilities.Debug
  4. open Internal.Utilities.FileSystem
  5. open System
  6. open System.IO
  7. open System.Reflection
  8. open System.Diagnostics
  9. open System.Collections.Generic
  10. open System
  11. open Microsoft.FSharp.Compiler
  12. open Microsoft.FSharp.Compiler.Range
  13. open Microsoft.FSharp.Compiler.Build
  14. open Microsoft.FSharp.Compiler.Tastops
  15. open Microsoft.FSharp.Compiler.ErrorLogger
  16. open Microsoft.FSharp.Compiler.Lib
  17. open Microsoft.FSharp.Compiler.AbstractIL
  18. open Microsoft.FSharp.Compiler.AbstractIL.Internal.Library
  19. module internal IncrementalBuild =
  20. /// A particular node in the Expr language. Use an int for keys instead of the entire Expr to avoid extra hashing.
  21. type Id =
  22. | Id of int
  23. static member toInt (Id id) = id
  24. override id.ToString() = match id with Id(n) ->sprintf "Id(%d)" n
  25. [<NoEquality; NoComparison>]
  26. /// A build rule representing a single output
  27. type ScalarBuildRule =
  28. /// ScalarInput (uniqueRuleId, outputName)
  29. ///
  30. /// A build rule representing a single input, producing the input as its single scalar result
  31. | ScalarInput of Id * string
  32. /// ScalarDemultiplex (uniqueRuleId, outputName, input, taskFunction)
  33. ///
  34. /// A build rule representing the merge of a set of inputs to a single output
  35. | ScalarDemultiplex of Id * string * VectorBuildRule * (obj[] -> obj)
  36. /// ScalarMap (uniqueRuleId, outputName, input, taskFunction)
  37. ///
  38. /// A build rule representing the transformation of a single input to a single output
  39. | ScalarMap of Id * string * ScalarBuildRule * (obj->obj)
  40. /// Get the Id for the given ScalarBuildRule.
  41. static member GetId = function
  42. | ScalarInput(id,_) ->id
  43. | ScalarDemultiplex(id,_,_,_) ->id
  44. | ScalarMap(id,_,_,_) ->id
  45. /// Get the Name for the givenScalarExpr.
  46. static member GetName = function
  47. | ScalarInput(_,n) ->n
  48. | ScalarDemultiplex(_,n,_,_) ->n
  49. | ScalarMap(_,n,_,_) ->n
  50. override ve.ToString() =
  51. match ve with
  52. | ScalarInput(Id id,name) ->sprintf "InputScalar(%d,%s)" id name
  53. | ScalarDemultiplex(Id id,name,_,_) ->sprintf "ScalarDemultiplex(%d,%s)" id name
  54. | ScalarMap(Id id,name,_,_) ->sprintf "ScalarMap(%d,%s)" id name
  55. /// A build rule with a vector of outputs
  56. and VectorBuildRule =
  57. /// VectorInput (uniqueRuleId, outputName)
  58. ///
  59. /// A build rule representing the transformation of a single input to a single output
  60. | VectorInput of Id * string
  61. /// VectorInput (uniqueRuleId, outputName, initialAccumulator, inputs, taskFunction)
  62. ///
  63. /// A build rule representing the scan-left combinining a single scalar accumulator input with a vector of inputs
  64. | VectorScanLeft of Id * string * ScalarBuildRule * VectorBuildRule * (obj->obj->Eventually<obj>)
  65. /// VectorMap (uniqueRuleId, outputName, inputs, taskFunction)
  66. ///
  67. /// A build rule representing the parallel map of the inputs to outputs
  68. | VectorMap of Id * string * VectorBuildRule * (obj->obj)
  69. /// VectorStamp (uniqueRuleId, outputName, inputs, stampFunction)
  70. ///
  71. /// A build rule representing pairing the inputs with a timestamp specified by the given function.
  72. | VectorStamp of Id * string * VectorBuildRule * (obj->DateTime)
  73. /// VectorMultiplex (uniqueRuleId, outputName, input, taskFunction)
  74. ///
  75. /// A build rule representing taking a single input and transforming it to a vector of outputs
  76. | VectorMultiplex of Id * string * ScalarBuildRule * (obj->obj[])
  77. /// Get the Id for the given VectorBuildRule.
  78. static member GetId = function
  79. | VectorInput(id,_) ->id
  80. | VectorScanLeft(id,_,_,_,_) ->id
  81. | VectorMap(id,_,_,_) ->id
  82. | VectorStamp(id,_,_,_) ->id
  83. | VectorMultiplex(id,_,_,_) ->id
  84. /// Get the Name for the given VectorBuildRule.
  85. static member GetName = function
  86. | VectorInput(_,n) ->n
  87. | VectorScanLeft(_,n,_,_,_) ->n
  88. | VectorMap(_,n,_,_) ->n
  89. | VectorStamp(_,n,_,_) ->n
  90. | VectorMultiplex(_,n,_,_) ->n
  91. override ve.ToString() =
  92. match ve with
  93. | VectorInput(Id id,name) ->sprintf "VectorInput(%d,%s)" id name
  94. | VectorScanLeft(Id id,name,_,_,_) ->sprintf "VectorScanLeft(%d,%s)" id name
  95. | VectorMap(Id id,name,_,_) ->sprintf "VectorMap(%d,%s)" id name
  96. | VectorStamp(Id id,name,_,_) ->sprintf "VectorStamp(%d,%s)" id name
  97. | VectorMultiplex(Id id,name,_,_) ->sprintf "VectorMultiplex(%d,%s)" id name
  98. [<NoEquality; NoComparison>]
  99. type BuildRuleExpr =
  100. | ScalarBuildRule of ScalarBuildRule
  101. | VectorBuildRule of VectorBuildRule
  102. /// Get the Id for the given Expr.
  103. static member GetId = function
  104. | ScalarBuildRule se ->ScalarBuildRule.GetId se
  105. | VectorBuildRule ve ->VectorBuildRule.GetId ve
  106. /// Get the Name for the given Expr.
  107. static member GetName= function
  108. | ScalarBuildRule se ->ScalarBuildRule.GetName se
  109. | VectorBuildRule ve ->VectorBuildRule.GetName ve
  110. override e.ToString() =
  111. match e with
  112. | ScalarBuildRule _ -> sprintf "ScalarBuildRule se"
  113. | VectorBuildRule _ -> sprintf "VectorBuildRule ve"
  114. // Ids of exprs
  115. let nextid = ref 999 // Number ids starting with 1000 to discern them
  116. let NextId() =
  117. nextid:=!nextid+1
  118. Id(!nextid)
  119. type IScalar =
  120. abstract GetScalarExpr : unit -> ScalarBuildRule
  121. type IVector =
  122. abstract GetVectorExpr : unit-> VectorBuildRule
  123. type Scalar<'T> = interface end
  124. type Vector<'T> = interface end
  125. /// The outputs of a build
  126. [<NoEquality; NoComparison>]
  127. type NamedOutput =
  128. | NamedVectorOutput of string * IVector
  129. | NamedScalarOutput of string * IScalar
  130. type BuildRules = { RuleList : (string * BuildRuleExpr) list }
  131. /// Visit each task and call op with the given accumulator.
  132. let FoldOverBuildRules(rules:BuildRules, op, acc)=
  133. let rec VisitVector (ve:VectorBuildRule) acc =
  134. match ve with
  135. | VectorInput _ ->op (VectorBuildRule ve) acc
  136. | VectorScanLeft(_,_,a,i,_) ->op (VectorBuildRule ve) (VisitVector i (VisitScalar a acc))
  137. | VectorMap(_,_,i,_)
  138. | VectorStamp(_,_,i,_) ->op (VectorBuildRule ve) (VisitVector i acc)
  139. | VectorMultiplex(_,_,i,_) ->op (VectorBuildRule ve) (VisitScalar i acc)
  140. and VisitScalar (se:ScalarBuildRule) acc =
  141. match se with
  142. | ScalarInput _ ->op (ScalarBuildRule se) acc
  143. | ScalarDemultiplex(_,_,i,_) ->op (ScalarBuildRule se) (VisitVector i acc)
  144. | ScalarMap(_,_,i,_) ->op (ScalarBuildRule se) (VisitScalar i acc)
  145. let rec VisitRule (expr:BuildRuleExpr) acc =
  146. match expr with
  147. | ScalarBuildRule se ->VisitScalar se acc
  148. | VectorBuildRule ve ->VisitVector ve acc
  149. List.foldBack VisitRule (rules.RuleList |> List.map snd) acc
  150. /// Convert from interfaces into discriminated union.
  151. let ToBuild (names:NamedOutput list) : BuildRules =
  152. // Create the rules.
  153. let CreateRules() =
  154. { RuleList = names |> List.map(function NamedVectorOutput(n,v) -> n,VectorBuildRule(v.GetVectorExpr())
  155. | NamedScalarOutput(n,s) -> n,ScalarBuildRule(s.GetScalarExpr())) }
  156. // Ensure that all names are unique.
  157. let EnsureUniqueNames (expr:BuildRuleExpr) (acc:Map<string,Id>) =
  158. let AddUniqueIdToNameMapping(id,name)=
  159. match acc.TryFind name with
  160. | Some(priorId) ->
  161. if id<>priorId then failwith (sprintf "Two build expressions had the same name: %s" name)
  162. else acc
  163. | None-> Map.add name id acc
  164. let id = BuildRuleExpr.GetId(expr)
  165. let name = BuildRuleExpr.GetName(expr)
  166. AddUniqueIdToNameMapping(id,name)
  167. // Validate the rule tree
  168. let ValidateRules (rules:BuildRules) =
  169. FoldOverBuildRules(rules,EnsureUniqueNames,Map.empty) |> ignore
  170. // Convert and validate
  171. let rules = CreateRules()
  172. ValidateRules rules
  173. rules
  174. /// These describe the input conditions for a result. If conditions change then the result is invalid.
  175. type InputSignature =
  176. | SingleMappedVectorInput of InputSignature[]
  177. | EmptyTimeStampedInput of DateTime
  178. | BoundInputScalar // An external input into the build
  179. | BoundInputVector // An external input into the build
  180. | IndexedValueElement of DateTime
  181. | UnevaluatedInput
  182. /// Return true if the result is fully evaluated
  183. member is.IsEvaluated() =
  184. let rec IsEvaluated(is) =
  185. match is with
  186. | UnevaluatedInput -> false
  187. | SingleMappedVectorInput iss -> iss |> Array.forall IsEvaluated
  188. | _ -> true
  189. IsEvaluated(is)
  190. override is.ToString() = sprintf "%A" is
  191. /// A slot for holding a single result.
  192. type Result =
  193. | NotAvailable
  194. | InProgress of (unit -> Eventually<obj>) * DateTime
  195. | Available of obj * DateTime * InputSignature
  196. /// Get the available result. Throw an exception if not available.
  197. static member GetAvailable = function Available(o,_,_) ->o | _->failwith "No available result"
  198. /// Get the time stamp if available. Otheriwse MaxValue.
  199. static member Timestamp = function Available(_,ts,_) ->ts | InProgress(_,ts) -> ts | _-> DateTime.MaxValue
  200. /// Get the time stamp if available. Otheriwse MaxValue.
  201. static member InputSignature = function Available(_,_,signature) ->signature | _-> UnevaluatedInput
  202. member x.ResultIsInProgress = match x with | InProgress _ -> true | _ -> false
  203. member x.GetInProgressContinuation() = match x with | InProgress (f,_) -> f() | _ -> failwith "not in progress"
  204. member x.TryGetAvailable() = match x with | InProgress _ | NotAvailable -> None | Available(obj,dt,i) -> Some(obj,dt,i)
  205. override r.ToString() =
  206. match r with
  207. | NotAvailable -> "NotAvailable"
  208. | InProgress _ -> "InProgress"
  209. | Available(o, ts, _) -> sprintf "Available('%s' as of %A)" (o.ToString()) ts
  210. /// An immutable sparse vector of results.
  211. type ResultVector(size,zeroElementTimestamp,map) =
  212. let get slot =
  213. match Map.tryFind slot map with
  214. | Some(result) ->result
  215. | None->NotAvailable
  216. let asList = lazy List.map (fun i->i,get i) [0..size-1]
  217. static member OfSize(size) = ResultVector(size,DateTime.MinValue,Map.empty)
  218. member rv.Size = size
  219. member rv.Get slot = get slot
  220. member rv.Resize(newsize) =
  221. if size<>newsize then
  222. ResultVector(newsize, zeroElementTimestamp, map |> Map.filter(fun s _ -> s < newsize))
  223. else rv
  224. member rv.Set(slot,value) =
  225. #if DEBUG
  226. if slot<0 then failwith "ResultVector slot less than zero"
  227. if slot>=size then failwith "ResultVector slot too big"
  228. #endif
  229. ResultVector(size, zeroElementTimestamp, Map.add slot value map)
  230. member rv.MaxTimestamp() =
  231. // use t = Trace.Call("IncrementalBuildVerbose", "MaxTimestamp", fun _->sprintf "vector of size=%d" size)
  232. let Maximize (lasttimestamp:DateTime) (_,result) =
  233. let thistimestamp = Result.Timestamp result
  234. let m = max lasttimestamp thistimestamp
  235. // use t = Trace.Call("IncrementalBuildVerbose", "Maximize", fun _->sprintf "last=%s this=%s max=%s" (lasttimestamp.ToString()) (thistimestamp.ToString()) (m.ToString()))
  236. m
  237. List.fold Maximize zeroElementTimestamp (asList.Force())
  238. member rv.Signature() =
  239. let l = asList.Force()
  240. let l = l |> List.map(fun (_,result) ->Result.InputSignature result)
  241. SingleMappedVectorInput (l|>List.toArray)
  242. member rv.FoldLeft f s : 'a = List.fold f s (asList.Force())
  243. override rv.ToString() = asList.ToString() // NOTE: Force()ing this inside ToString() leads to StackOverflowException and very undesirable debugging behavior for all of F#
  244. /// A result of performing build actions
  245. [<NoEquality; NoComparison>]
  246. type ResultSet =
  247. | ScalarResult of Result
  248. | VectorResult of ResultVector
  249. override rs.ToString() =
  250. match rs with
  251. | ScalarResult(sr) ->sprintf "ScalarResult(%s)" (sr.ToString())
  252. | VectorResult(rs) ->sprintf "VectorResult(%s)" (rs.ToString())
  253. /// Action timing
  254. module Time =
  255. #if SILVERLIGHT
  256. let Action<'T> taskname slot func : 'T = func()
  257. #else
  258. let sw = new Stopwatch()
  259. let Action<'T> taskname slot func : 'T=
  260. if Trace.ShouldLog("IncrementalBuildWorkUnits") then
  261. let slotMessage =
  262. if slot= -1 then sprintf "%s" taskname
  263. else sprintf "%s over slot %d" taskname slot
  264. // Timings and memory
  265. let maxGen = System.GC.MaxGeneration
  266. let ptime = System.Diagnostics.Process.GetCurrentProcess()
  267. let timePrev = ptime.UserProcessorTime.TotalSeconds
  268. let gcPrev = [| for i in 0 .. maxGen -> System.GC.CollectionCount i |]
  269. let pbPrev = ptime.PrivateMemorySize64 in
  270. // Call the function
  271. let result = func()
  272. // Report.
  273. let timeNow = ptime.UserProcessorTime.TotalSeconds
  274. let pbNow = ptime.PrivateMemorySize64
  275. let spanGC = [| for i in 0 .. maxGen -> System.GC.CollectionCount i - gcPrev.[i] |]
  276. Trace.PrintLine("IncrementalBuildWorkUnits", fun _ ->
  277. sprintf "%s TIME: %4.3f MEM: %3d (delta) G0: %3d G1: %2d G2: %2d"
  278. slotMessage
  279. (timeNow - timePrev)
  280. (pbNow - pbPrev)
  281. spanGC.[min 0 maxGen]
  282. spanGC.[min 1 maxGen]
  283. spanGC.[min 2 maxGen])
  284. result
  285. else func()
  286. #endif
  287. /// Result of a particular action over the bound build tree
  288. [<NoEquality; NoComparison>]
  289. type ActionResult =
  290. | IndexedResult of Id * int * (*slotcount*) int * Eventually<obj> * DateTime
  291. | ScalarValuedResult of Id * obj * DateTime * InputSignature
  292. | VectorValuedResult of Id * obj[] * DateTime * InputSignature
  293. | ResizeResult of Id * (*slotcount*) int
  294. override ar.ToString() =
  295. match ar with
  296. | IndexedResult(id,slot,slotcount,_,dt) ->sprintf "IndexedResult(%d,%d,%d,obj,%A)" (Id.toInt id) slot slotcount dt
  297. | ScalarValuedResult(id,_,dt,inputsig) ->sprintf "ScalarValuedResult(%d,obj,%A,%A)" (Id.toInt id) dt inputsig
  298. | VectorValuedResult(id,_,dt,inputsig) ->sprintf "VectorValuedResult(%d,obj[],%A,%A)" (Id.toInt id) dt inputsig
  299. | ResizeResult(id,slotcount) ->sprintf "ResizeResult(%d,%d)" (Id.toInt id) slotcount
  300. /// A pending action over the bound build tree
  301. [<NoEquality; NoComparison>]
  302. type Action =
  303. | IndexedAction of Id * (*taskname*)string * int * (*slotcount*) int * DateTime * (unit->Eventually<obj>)
  304. | ScalarAction of Id * (*taskname*)string * DateTime * InputSignature * (unit->obj)
  305. | VectorAction of Id * (*taskname*)string * DateTime * InputSignature * (unit->obj[])
  306. | ResizeResultAction of Id * (*slotcount*) int
  307. /// Execute one action and return a corresponding result.
  308. static member Execute action =
  309. match action with
  310. | IndexedAction(id,taskname,slot,slotcount,timestamp,func) -> IndexedResult(id,slot,slotcount,Time.Action taskname slot func,timestamp)
  311. | ScalarAction(id,taskname,timestamp,inputsig,func) -> ScalarValuedResult(id,Time.Action taskname (-1) func,timestamp,inputsig)
  312. | VectorAction(id,taskname,timestamp,inputsig,func) -> VectorValuedResult(id,Time.Action taskname (-1) func,timestamp,inputsig)
  313. | ResizeResultAction(id,slotcount) -> ResizeResult(id,slotcount)
  314. /// String helper functions for when there's no %A
  315. type String =
  316. static member OfList2 l =
  317. " ["^String.Join(",\n ", List.toArray (l|>List.map (fun (v1,v2) ->((box v1).ToString()) + ";" + ((box v2).ToString())))) + " ]"
  318. /// A set of build rules and the corresponding, possibly partial, results from building.
  319. [<Sealed>]
  320. type PartialBuild(rules:BuildRules, results:Map<Id,ResultSet>) =
  321. member bt.Rules = rules
  322. member bt.Results = results
  323. override bt.ToString() =
  324. let sb = new System.Text.StringBuilder()
  325. results |> Map.iter(fun id result->
  326. let id = Id.toInt id
  327. let s = sprintf "\n {Id=%d,ResultSet=%s}" id (result.ToString())
  328. let _ = sb.Append(s)
  329. ())
  330. sprintf "{Rules={%s}\n Results={%s}}" (String.OfList2 rules.RuleList) (sb.ToString())
  331. /// Given an expression, find the expected width.
  332. let rec GetVectorWidthByExpr(bt:PartialBuild,ve:VectorBuildRule) =
  333. let KnownValue ve =
  334. match bt.Results.TryFind(VectorBuildRule.GetId ve) with
  335. | Some(resultSet) ->
  336. match resultSet with
  337. | VectorResult rv ->Some(rv.Size)
  338. | _ -> failwith "Expected vector to have vector result."
  339. | None-> None
  340. match ve with
  341. | VectorScanLeft(_,_,_,i,_)
  342. | VectorMap(_,_,i,_)
  343. | VectorStamp(_,_,i,_) ->
  344. match GetVectorWidthByExpr(bt,i) with
  345. | Some _ as r -> r
  346. | None->KnownValue ve
  347. | VectorInput _
  348. | VectorMultiplex _ -> KnownValue ve
  349. /// Given an expression name, get the corresponding expression.
  350. let GetTopLevelExprByName(bt:PartialBuild, seek:string) =
  351. bt.Rules.RuleList |> List.filter(fun(name,_) ->name=seek) |> List.map(fun(_,root) ->root) |> List.head
  352. /// Get an expression matching the given name.
  353. let GetExprByName(bt:PartialBuild, seek:string) : BuildRuleExpr =
  354. let MatchName (expr:BuildRuleExpr) (acc:BuildRuleExpr option) : BuildRuleExpr option =
  355. let name = BuildRuleExpr.GetName(expr)
  356. if name = seek then Some(expr) else acc
  357. let matchOption = FoldOverBuildRules(bt.Rules,MatchName,None)
  358. Option.get matchOption
  359. // Given an Id, find the corresponding expression.
  360. let GetExprById(bt:PartialBuild, seek:Id) : BuildRuleExpr=
  361. let rec VectorExprOfId ve =
  362. match ve with
  363. | VectorInput(id,_) ->if seek=id then Some(VectorBuildRule ve) else None
  364. | VectorScanLeft(id,_,a,i,_) ->
  365. if seek=id then Some(VectorBuildRule ve) else
  366. let result = ScalarExprOfId(a)
  367. match result with Some _ -> result | None->VectorExprOfId i
  368. | VectorMap(id,_,i,_) ->if seek=id then Some(VectorBuildRule ve) else VectorExprOfId i
  369. | VectorStamp(id,_,i,_) ->if seek=id then Some(VectorBuildRule ve) else VectorExprOfId i
  370. | VectorMultiplex(id,_,i,_) ->if seek=id then Some(VectorBuildRule ve) else ScalarExprOfId i
  371. and ScalarExprOfId se =
  372. match se with
  373. | ScalarInput(id,_) ->if seek=id then Some(ScalarBuildRule se) else None
  374. | ScalarDemultiplex(id,_,i,_) ->if seek=id then Some(ScalarBuildRule se) else VectorExprOfId i
  375. | ScalarMap(id,_,i,_) ->if seek=id then Some(ScalarBuildRule se) else ScalarExprOfId i
  376. let ExprOfId(expr:BuildRuleExpr) =
  377. match expr with
  378. | ScalarBuildRule se ->ScalarExprOfId se
  379. | VectorBuildRule ve ->VectorExprOfId ve
  380. let exprs = bt.Rules.RuleList |> List.map(fun(_,root) ->ExprOfId(root)) |> List.filter Option.isSome
  381. match exprs with
  382. | Some(expr)::_ -> expr
  383. | _ -> failwith (sprintf "GetExprById did not find an expression for Id %d" (Id.toInt seek))
  384. let GetVectorWidthById (bt:PartialBuild) seek =
  385. match GetExprById(bt,seek) with
  386. | ScalarBuildRule _ ->failwith "Attempt to get width of scalar."
  387. | VectorBuildRule ve ->Option.get (GetVectorWidthByExpr(bt,ve))
  388. let GetScalarExprResult(bt:PartialBuild, se:ScalarBuildRule) =
  389. match bt.Results.TryFind(ScalarBuildRule.GetId se) with
  390. | Some(resultSet) ->
  391. match se,resultSet with
  392. | ScalarInput _,ScalarResult(r)
  393. | ScalarMap _,ScalarResult(r)
  394. | ScalarDemultiplex _,ScalarResult(r) ->r
  395. | se,result->failwith (sprintf "GetScalarExprResult had no match for %A,%A" se result)
  396. | None->NotAvailable
  397. let GetVectorExprResultVector(bt:PartialBuild, ve:VectorBuildRule) =
  398. match bt.Results.TryFind(VectorBuildRule.GetId ve) with
  399. | Some(resultSet) ->
  400. match ve,resultSet with
  401. | VectorScanLeft _,VectorResult rv
  402. | VectorMap _,VectorResult rv
  403. | VectorInput _,VectorResult rv
  404. | VectorStamp _,VectorResult rv
  405. | VectorMultiplex _,VectorResult rv -> Some rv
  406. | ve,result->failwith (sprintf "GetVectorExprResultVector had no match for %A,%A" ve result)
  407. | None->None
  408. let GetVectorExprResult(bt:PartialBuild, ve:VectorBuildRule, slot) =
  409. match bt.Results.TryFind(VectorBuildRule.GetId ve) with
  410. | Some(resultSet) ->
  411. match ve,resultSet with
  412. | VectorScanLeft _,VectorResult rv
  413. | VectorMap _,VectorResult rv
  414. | VectorInput _,VectorResult rv
  415. | VectorStamp _,VectorResult rv -> rv.Get slot
  416. | VectorMultiplex _,VectorResult rv -> rv.Get slot
  417. | ve,result->failwith (sprintf "GetVectorExprResult had no match for %A,%A" ve result)
  418. | None->NotAvailable
  419. /// Get the maximum build stamp for an output.
  420. let MaxTimestamp(bt:PartialBuild,id,_inputstamp) =
  421. match bt.Results.TryFind(id) with
  422. | Some(resultset) ->
  423. match resultset with
  424. | ScalarResult(rs) -> Result.Timestamp rs
  425. | VectorResult rv -> rv.MaxTimestamp()
  426. | None -> DateTime.MaxValue
  427. let Signature(bt:PartialBuild,id) =
  428. match bt.Results.TryFind(id) with
  429. | Some(resultset) ->
  430. match resultset with
  431. | ScalarResult(rs) -> Result.InputSignature rs
  432. | VectorResult rv -> rv.Signature()
  433. | None -> UnevaluatedInput
  434. /// Get all the results for the given expr.
  435. let AllResultsOfExpr extractor (bt:PartialBuild) expr =
  436. let GetAvailable (rv:ResultVector) =
  437. let Extract acc (_, result) = (extractor result)::acc
  438. List.rev (rv.FoldLeft Extract [])
  439. let GetVectorResultById id =
  440. match bt.Results.TryFind(id) with
  441. | Some(found) ->
  442. match found with
  443. | VectorResult rv ->GetAvailable rv
  444. | _ -> failwith "wrong result type"
  445. | None -> []
  446. GetVectorResultById(VectorBuildRule.GetId(expr))
  447. let AvailableAllResultsOfExpr bt expr =
  448. let msg = "Expected all results to be available"
  449. AllResultsOfExpr (function Available(o,_,_) -> o | _ -> failwith msg) bt expr
  450. /// Bind a set of build rules to a set of input values.
  451. let ToBound(buildRules:BuildRules, vectorinputs, scalarinputs) =
  452. let now = DateTime.Now
  453. let rec ApplyScalarExpr(se,results) =
  454. match se with
  455. | ScalarInput(id,n) ->
  456. let matches = scalarinputs
  457. |> List.filter (fun (inputname,_) ->inputname=n)
  458. |> List.map (fun (_,inputvalue:obj) -> ScalarResult(Available(inputvalue,now,BoundInputScalar)))
  459. List.foldBack (Map.add id) matches results
  460. | ScalarMap(_,_,se,_) ->ApplyScalarExpr(se,results)
  461. | ScalarDemultiplex(_,_,ve,_) ->ApplyVectorExpr(ve,results)
  462. and ApplyVectorExpr(ve,results) =
  463. match ve with
  464. | VectorInput(id,n) ->
  465. let matches = vectorinputs
  466. |> List.filter (fun (inputname,_,_) ->inputname=n)
  467. |> List.map (fun (_,size,inputvalues:obj list) ->
  468. let results = inputvalues|>List.mapi(fun i value->i,Available(value,now,BoundInputVector))
  469. VectorResult(ResultVector(size,DateTime.MinValue,results|>Map.ofList))
  470. )
  471. List.foldBack (Map.add id) matches results
  472. | VectorScanLeft(_,_,a,i,_) ->ApplyVectorExpr(i,ApplyScalarExpr(a,results))
  473. | VectorMap(_,_,i,_)
  474. | VectorStamp(_,_,i,_) ->ApplyVectorExpr(i,results)
  475. | VectorMultiplex(_,_,i,_) ->ApplyScalarExpr(i,results)
  476. let ApplyExpr expr results =
  477. match expr with
  478. | ScalarBuildRule se ->ApplyScalarExpr(se,results)
  479. | VectorBuildRule ve ->ApplyVectorExpr(ve,results)
  480. // Place vector inputs into results map.
  481. let results = List.foldBack ApplyExpr (buildRules.RuleList |> List.map snd) Map.empty
  482. PartialBuild(buildRules,results)
  483. /// Visit each executable action and call actionFunc with the given accumulator.
  484. let ForeachAction output bt (actionFunc:Action->'acc->'acc) (acc:'acc) =
  485. use t = Trace.Call("IncrementalBuildVerbose", "ForeachAction", fun _->sprintf "name=%s" output)
  486. let seen = Dictionary<_,_>()
  487. let Seen(id) =
  488. if seen.ContainsKey(id) then true
  489. else seen.[id]<-true
  490. false
  491. let HasChanged(inputtimestamp,outputtimestamp) =
  492. if inputtimestamp<>outputtimestamp then
  493. Trace.PrintLine("IncrementalBuildVerbose", fun _ -> sprintf "Input timestamp is %A. Output timestamp is %A." inputtimestamp outputtimestamp)
  494. true
  495. else false
  496. let ShouldEvaluate(bt,currentsig:InputSignature,id) =
  497. let isAvailable = currentsig.IsEvaluated()
  498. if isAvailable then
  499. let priorsig = Signature(bt,id)
  500. currentsig<>priorsig
  501. else false
  502. /// Make sure the result vector saved matches the size of expr
  503. let ResizeVectorExpr(ve:VectorBuildRule,acc) =
  504. let id = VectorBuildRule.GetId ve
  505. match GetVectorWidthByExpr(bt,ve) with
  506. | Some(expectedWidth) ->
  507. match bt.Results.TryFind(id) with
  508. | Some(found) ->
  509. match found with
  510. | VectorResult rv ->
  511. if rv.Size<> expectedWidth then
  512. actionFunc (ResizeResultAction(id,expectedWidth)) acc
  513. else acc
  514. | _ -> acc
  515. | None -> acc
  516. | None -> acc
  517. let rec VisitVector ve acc =
  518. if Seen(VectorBuildRule.GetId ve) then acc
  519. else
  520. Trace.PrintLine("IncrementalBuildVerbose", fun _ -> sprintf "In ForeachAction at vector expression %s" (ve.ToString()))
  521. let acc = ResizeVectorExpr(ve,acc)
  522. match ve with
  523. | VectorInput _ ->acc
  524. | VectorScanLeft(id,taskname,accumulatorExpr,inputExpr,func) ->
  525. let acc =
  526. match GetVectorWidthByExpr(bt,ve) with
  527. | Some(cardinality) ->
  528. let GetInputAccumulator slot =
  529. if slot=0 then GetScalarExprResult(bt,accumulatorExpr)
  530. else GetVectorExprResult(bt,ve,slot-1)
  531. let Scan slot =
  532. let accumulatorResult = GetInputAccumulator slot
  533. let inputResult = GetVectorExprResult(bt,inputExpr,slot)
  534. match accumulatorResult,inputResult with
  535. | Available(accumulator,accumulatortimesamp,_accumulatorInputSig),Available(input,inputtimestamp,_inputSig) ->
  536. let inputtimestamp = max inputtimestamp accumulatortimesamp
  537. let prevoutput = GetVectorExprResult(bt,ve,slot)
  538. let outputtimestamp = Result.Timestamp prevoutput
  539. let scanOp =
  540. if HasChanged(inputtimestamp,outputtimestamp) then
  541. Some (fun () -> func accumulator input)
  542. elif prevoutput.ResultIsInProgress then
  543. Some prevoutput.GetInProgressContinuation
  544. else
  545. // up-to-date and complete, no work required
  546. None
  547. match scanOp with
  548. | Some scanOp -> Some(actionFunc (IndexedAction(id,taskname,slot,cardinality,inputtimestamp,scanOp)) acc)
  549. | None -> None
  550. | _ -> None
  551. match ([0..cardinality-1]|>List.tryPick Scan) with Some(acc) ->acc | None->acc
  552. | None -> acc
  553. // Check each slot for an action that may be performed.
  554. VisitVector inputExpr (VisitScalar accumulatorExpr acc)
  555. | VectorMap(id, taskname, inputExpr, func) ->
  556. let acc =
  557. match GetVectorWidthByExpr(bt,ve) with
  558. | Some(cardinality) ->
  559. if cardinality=0 then
  560. // For vector length zero, just propagate the prior timestamp.
  561. let inputtimestamp = MaxTimestamp(bt,VectorBuildRule.GetId(inputExpr),DateTime.MinValue)
  562. let outputtimestamp = MaxTimestamp(bt,id,DateTime.MinValue)
  563. if HasChanged(inputtimestamp,outputtimestamp) then
  564. Trace.PrintLine("IncrementalBuildVerbose", fun _ -> sprintf "Vector Map with cardinality zero setting output timestamp to %A." inputtimestamp)
  565. actionFunc (VectorAction(id,taskname,inputtimestamp,EmptyTimeStampedInput inputtimestamp, fun _ ->[||])) acc
  566. else acc
  567. else
  568. let MapResults acc slot =
  569. let inputtimestamp = Result.Timestamp (GetVectorExprResult(bt,inputExpr,slot))
  570. let outputtimestamp = Result.Timestamp (GetVectorExprResult(bt,ve,slot))
  571. if HasChanged(inputtimestamp,outputtimestamp) then
  572. let OneToOneOp() =
  573. Eventually.Done (func (Result.GetAvailable (GetVectorExprResult(bt,inputExpr,slot))))
  574. actionFunc (IndexedAction(id,taskname,slot,cardinality,inputtimestamp,OneToOneOp)) acc
  575. else acc
  576. [0..cardinality-1] |> List.fold MapResults acc
  577. | None -> acc
  578. VisitVector inputExpr acc
  579. | VectorStamp(id, taskname, inputExpr, func) ->
  580. // For every result that is available, check time stamps.
  581. let acc =
  582. match GetVectorWidthByExpr(bt,ve) with
  583. | Some(cardinality) ->
  584. if cardinality=0 then
  585. // For vector length zero, just propagate the prior timestamp.
  586. let inputtimestamp = MaxTimestamp(bt,VectorBuildRule.GetId(inputExpr),DateTime.MinValue)
  587. let outputtimestamp = MaxTimestamp(bt,id,DateTime.MinValue)
  588. if HasChanged(inputtimestamp,outputtimestamp) then
  589. Trace.PrintLine("IncrementalBuildVerbose", fun _ -> sprintf "Vector Stamp with cardinality zero setting output timestamp to %A." inputtimestamp)
  590. actionFunc (VectorAction(id,taskname,inputtimestamp,EmptyTimeStampedInput inputtimestamp,fun _ ->[||])) acc
  591. else acc
  592. else
  593. let CheckStamp acc slot =
  594. let inputresult = GetVectorExprResult(bt,inputExpr,slot)
  595. match inputresult with
  596. | Available(ires,_,_) ->
  597. let oldtimestamp = Result.Timestamp (GetVectorExprResult(bt,ve,slot))
  598. let newtimestamp = func ires
  599. if newtimestamp<>oldtimestamp then
  600. Trace.PrintLine("IncrementalBuildVerbose", fun _ -> sprintf "Old timestamp was %A. New timestamp is %A." oldtimestamp newtimestamp)
  601. actionFunc (IndexedAction(id,taskname,slot,cardinality,newtimestamp, fun _ -> Eventually.Done ires)) acc
  602. else acc
  603. | _ -> acc
  604. [0..cardinality-1] |> List.fold CheckStamp acc
  605. | None -> acc
  606. VisitVector inputExpr acc
  607. | VectorMultiplex(id, taskname, inputExpr, func) ->
  608. VisitScalar inputExpr
  609. (match GetScalarExprResult(bt,inputExpr) with
  610. | Available(inp,inputtimestamp,inputsig) ->
  611. let outputtimestamp = MaxTimestamp(bt,id,inputtimestamp)
  612. if HasChanged(inputtimestamp,outputtimestamp) then
  613. let MultiplexOp() = func inp
  614. actionFunc (VectorAction(id,taskname,inputtimestamp,inputsig,MultiplexOp)) acc
  615. else acc
  616. | _->acc)
  617. and VisitScalar se acc =
  618. if Seen(ScalarBuildRule.GetId se) then acc
  619. else
  620. Trace.PrintLine("IncrementalBuildVerbose", fun _ -> sprintf "In ForeachAction at scalar expression %s" (se.ToString()))
  621. match se with
  622. | ScalarInput _ ->acc
  623. | ScalarDemultiplex(id,taskname,inputExpr,func) ->
  624. VisitVector inputExpr
  625. (
  626. match GetVectorExprResultVector(bt,inputExpr) with
  627. | Some(inputresult) ->
  628. let currentsig = inputresult.Signature()
  629. if ShouldEvaluate(bt,currentsig,id) then
  630. let inputtimestamp = MaxTimestamp(bt, VectorBuildRule.GetId(inputExpr), DateTime.MaxValue)
  631. let DemultiplexOp() =
  632. let input = AvailableAllResultsOfExpr bt inputExpr |> List.toArray
  633. func input
  634. actionFunc (ScalarAction(id,taskname,inputtimestamp,currentsig,DemultiplexOp)) acc
  635. else acc
  636. | None -> acc
  637. )
  638. | ScalarMap(id,taskname,inputExpr,func) ->
  639. VisitScalar inputExpr
  640. (match GetScalarExprResult(bt,inputExpr) with
  641. | Available(inp,inputtimestamp,inputsig) ->
  642. let outputtimestamp = MaxTimestamp(bt, id, inputtimestamp)
  643. if HasChanged(inputtimestamp,outputtimestamp) then
  644. let MapOp() = func inp
  645. actionFunc (ScalarAction(id,taskname,inputtimestamp,inputsig,MapOp)) acc
  646. else acc
  647. | _->acc)
  648. let Visit expr acc =
  649. match expr with
  650. | ScalarBuildRule se ->VisitScalar se acc
  651. | VectorBuildRule ve ->VisitVector ve acc
  652. let filtered = bt.Rules.RuleList |> List.filter (fun (s,_) -> s = output) |> List.map snd
  653. List.foldBack Visit filtered acc
  654. /// Given the result of a single action, apply that action to the Build
  655. let ApplyResult(actionResult:ActionResult,bt:PartialBuild) =
  656. use t = Trace.Call("IncrementalBuildVerbose", "ApplyResult", fun _ -> "")
  657. let result =
  658. match actionResult with
  659. | ResizeResult(id,slotcount) ->
  660. match bt.Results.TryFind(id) with
  661. | Some(resultSet) ->
  662. match resultSet with
  663. | VectorResult rv ->
  664. let rv = rv.Resize(slotcount)
  665. let results = Map.add id (VectorResult rv) bt.Results
  666. PartialBuild(bt.Rules,results)
  667. | _ -> failwith "Unexpected"
  668. | None -> failwith "Unexpected"
  669. | ScalarValuedResult(id,value,timestamp,inputsig) ->
  670. PartialBuild(bt.Rules, Map.add id (ScalarResult(Available(value,timestamp,inputsig))) bt.Results)
  671. | VectorValuedResult(id,values,timestamp,inputsig) ->
  672. let Append acc slot =
  673. Map.add slot (Available(values.[slot],timestamp,inputsig)) acc
  674. let results = [0..values.Length-1]|>List.fold Append (Map.empty)
  675. let results = VectorResult(ResultVector(values.Length,timestamp,results))
  676. let bt = PartialBuild(bt.Rules, Map.add id results bt.Results)
  677. bt
  678. | IndexedResult(id,index,slotcount,value,timestamp) ->
  679. let width = (GetVectorWidthById bt id)
  680. let priorResults = bt.Results.TryFind(id)
  681. let prior =
  682. match priorResults with
  683. | Some(prior) ->prior
  684. | None->VectorResult(ResultVector.OfSize width)
  685. match prior with
  686. | VectorResult rv ->
  687. let result =
  688. match value with
  689. | Eventually.Done res ->
  690. Trace.PrintLine("FSharpBackgroundBuildVerbose", fun _ -> "Eventually.Done...")
  691. Available(res,timestamp, IndexedValueElement timestamp)
  692. | Eventually.NotYetDone f ->
  693. Trace.PrintLine("FSharpBackgroundBuildVerbose", fun _ -> "Eventually.NotYetDone...")
  694. InProgress (f,timestamp)
  695. let results = rv.Resize(slotcount).Set(index,result)
  696. PartialBuild(bt.Rules, Map.add id (VectorResult(results)) bt.Results)
  697. | _->failwith "Unexpected"
  698. result
  699. /// Evaluate the result of a single output
  700. let EvalLeafsFirst output bt =
  701. use t = Trace.Call("IncrementalBuildVerbose", "EvalLeafsFirst", fun _->sprintf "name=%s" output)
  702. let ExecuteApply action bt =
  703. let actionResult = Action.Execute(action)
  704. ApplyResult(actionResult,bt)
  705. let rec Eval(bt,gen) =
  706. Trace.PrintLine("FSharpBackgroundBuildVerbose", fun _ -> sprintf "---- Build generation %d ----" gen)
  707. #if DEBUG
  708. // This can happen, for example, if there is a task whose timestamp never stops increasing.
  709. // Possibly could detect this case directly.
  710. if gen>5000 then failwith "Infinite loop in incremental builder?"
  711. #endif
  712. let newBt = ForeachAction output bt ExecuteApply bt
  713. if newBt=bt then bt else Eval(newBt,gen+1)
  714. Eval(bt,0)
  715. let Step output (bt:PartialBuild) =
  716. use t = Trace.Call("IncrementalBuildVerbose", "Step", fun _->sprintf "name=%s" output)
  717. let BuildActionList() =
  718. use t = Trace.Call("IncrementalBuildVerbose", "BuildActionList", fun _->sprintf "name=%s" output)
  719. let Cons action list = action :: list
  720. // Hey look, we're building up the whole list, executing one thing and then throwing
  721. // the list away. What about saving the list inside the Build instance?
  722. ForeachAction output bt Cons []
  723. let ExecuteOneAction(worklist) =
  724. use t = Trace.Call("IncrementalBuildVerbose", "ExecuteOneAction", fun _->sprintf "name=%s" output)
  725. match worklist with
  726. | action::_ ->
  727. let actionResult = Action.Execute(action)
  728. Some(ApplyResult(actionResult,bt))
  729. | _->None
  730. ExecuteOneAction(BuildActionList())
  731. /// Eval by calling step over and over until done.
  732. let rec EvalStepwise output bt =
  733. use t = Trace.Call("IncrementalBuildVerbose", "EvalStepwise", fun _->sprintf "name=%s" output)
  734. let rec Evaluate(output,bt)=
  735. let newBt = Step output bt
  736. match newBt with
  737. | Some(newBt) -> Evaluate(output,newBt)
  738. | None->bt
  739. Evaluate(output,bt)
  740. /// Evaluate a build.
  741. let Eval output bt = EvalLeafsFirst output bt
  742. /// Get a scalar vector. Result must be available
  743. let GetScalarResult<'T>(name,bt) : ('T*DateTime) option =
  744. use t = Trace.Call("IncrementalBuildVerbose", "GetScalarResult", fun _->sprintf "name=%s" name)
  745. match GetTopLevelExprByName(bt,name) with
  746. | ScalarBuildRule se ->
  747. let id = ScalarBuildRule.GetId se
  748. match bt.Results.TryFind(id) with
  749. | Some(result) ->
  750. match result with
  751. | ScalarResult(sr) ->
  752. match sr.TryGetAvailable() with
  753. | Some(r,timestamp,_) -> Some(downcast r, timestamp)
  754. | None -> None
  755. | _ ->failwith "Expected a scalar result."
  756. | None->None
  757. | VectorBuildRule _ -> failwith "Expected scalar."
  758. /// Get a result vector. All results must be available or thrown an exception.
  759. let GetVectorResult<'T>(name,bt) : 'T[] =
  760. match GetTopLevelExprByName(bt,name) with
  761. | ScalarBuildRule _ -> failwith "Expected vector."
  762. | VectorBuildRule ve -> AvailableAllResultsOfExpr bt ve |> List.map(unbox) |> Array.ofList
  763. /// Get an element of vector result or None if there were no results.
  764. let GetVectorResultBySlot<'T>(name,slot,bt) : ('T*DateTime) option =
  765. match GetTopLevelExprByName(bt,name) with
  766. | ScalarBuildRule _ -> failwith "Expected vector expression"
  767. | VectorBuildRule ve ->
  768. match GetVectorExprResult(bt,ve,slot).TryGetAvailable() with
  769. | Some(o,timestamp,_) -> Some(downcast o,timestamp)
  770. | None->None
  771. /// Given an input value, find the corresponding slot.
  772. let GetSlotByInput<'T>(name:string,input:'T,build:PartialBuild,equals:'T->'T->bool) : int =
  773. let expr = GetExprByName(build,name)
  774. let id = BuildRuleExpr.GetId(expr)
  775. let resultSet = Option.get ( build.Results.TryFind(id))
  776. match resultSet with
  777. | VectorResult rv ->
  778. let MatchNames acc (slot,result) =
  779. match result with
  780. | Available(o,_,_) ->
  781. let o = o :?> 'T
  782. if equals o input then Some slot else acc
  783. | _ -> acc
  784. let slotOption = rv.FoldLeft MatchNames None
  785. match slotOption with
  786. | Some slot -> slot
  787. | _ -> failwith (sprintf "Could not find requested input '%A' named '%s' in set %+A" input name rv)
  788. | _ -> failwith (sprintf "Could not find requested input: %A" input)
  789. // Redeclare functions in the incremental build scope-----------------------------------------------------------------------
  790. // Methods for declaring inputs and outputs
  791. /// Declares a vector build input.
  792. let InputVector<'T> name =
  793. let expr = VectorInput(NextId(),name)
  794. { new Vector<'T>
  795. interface IVector with
  796. override pe.GetVectorExpr() = expr }
  797. /// Declares a scalar build input.
  798. let InputScalar<'T> name =
  799. let expr = ScalarInput(NextId(),name)
  800. { new Scalar<'T>
  801. interface IScalar with
  802. override pe.GetScalarExpr() = expr }
  803. module Scalar =
  804. let Map (taskname:string) (task:'I->'O) (input:Scalar<'I>) : Scalar<'O> =
  805. let BoxingMap i = box(task(unbox(i)))
  806. let input = (input:?>IScalar).GetScalarExpr()
  807. let expr = ScalarMap(NextId(),taskname,input,BoxingMap)
  808. { new Scalar<'O>
  809. interface IScalar with
  810. override pe.GetScalarExpr() = expr}
  811. let Multiplex (taskname:string) (task:'I -> 'O array) (input:Scalar<'I>) : Vector<'O> =
  812. let BoxingMultiplex i = Array.map box (task(unbox(i)))
  813. let input = (input:?>IScalar).GetScalarExpr()
  814. let expr = VectorMultiplex(NextId(),taskname,input,BoxingMultiplex)
  815. { new Vector<'O>
  816. interface IVector with
  817. override pe.GetVectorExpr() = expr}
  818. module Vector =
  819. /// Maps one vector to another using the given function.
  820. let Map (taskname:string) (task:'I ->'O) (input:Vector<'I>) : Vector<'O> =
  821. let BoxingMapVector i =
  822. box(task(unbox i))
  823. let input = (input:?>IVector).GetVectorExpr()
  824. let expr = VectorMap(NextId(),taskname,input,BoxingMapVector)
  825. { new Vector<'O>
  826. interface IVector with
  827. override pe.GetVectorExpr() = expr }
  828. /// Apply a function to each element of the vector, threading an accumulator argument
  829. /// through the computation. Returns intermediate results in a vector.
  830. let ScanLeft (taskname:string) (task:'A -> 'I -> Eventually<'A>) (acc:Scalar<'A>) (input:Vector<'I>) : Vector<'A> =
  831. let BoxingScanLeft a i =
  832. Eventually.box(task (unbox a) (unbox i))
  833. let acc = (acc:?>IScalar).GetScalarExpr()
  834. let input = (input:?>IVector).GetVectorExpr()
  835. let expr = VectorScanLeft(NextId(),taskname,acc,input,BoxingScanLeft)
  836. { new Vector<'A>
  837. interface IVector with
  838. override pe.GetVectorExpr() = expr }
  839. /// Apply a function to a vector to get a scalar value.
  840. let Demultiplex (taskname:string) (task:'I[] -> 'O) (input:Vector<'I>) : Scalar<'O> =
  841. let BoxingDemultiplex i =
  842. box(task (Array.map unbox i) )
  843. let input = (input:?>IVector).GetVectorExpr()
  844. let expr = ScalarDemultiplex(NextId(),taskname,input,BoxingDemultiplex)
  845. { new Scalar<'O>
  846. interface IScalar with
  847. override pe.GetScalarExpr() = expr }
  848. /// Creates a new vector with the same items but with
  849. /// timestamp specified by the passed-in function.
  850. let Stamp (taskname:string) (task:'I -> DateTime) (input:Vector<'I>) : Vector<'I> =
  851. let BoxingTouch i =
  852. task(unbox i)
  853. let input = (input:?>IVector).GetVectorExpr()
  854. let expr = VectorStamp(NextId(),taskname,input,BoxingTouch)
  855. { new Vector<'I>
  856. interface IVector with
  857. override pe.GetVectorExpr() = expr }
  858. let AsScalar (taskname:string) (input:Vector<'I>) : Scalar<'I array> =
  859. Demultiplex taskname (fun v->v) input
  860. /// Declare build outputs and bind them to real values.
  861. type BuildDescriptionScope() =
  862. let mutable outputs = []
  863. /// Declare a named scalar output.
  864. member b.DeclareScalarOutput(name,output:Scalar<'t>)=
  865. let output:IScalar = output:?>IScalar
  866. outputs <- NamedScalarOutput(name,output) :: outputs
  867. /// Declare a named vector output.
  868. member b.DeclareVectorOutput(name,output:Vector<'t>)=
  869. let output:IVector = output:?>IVector
  870. outputs <- NamedVectorOutput(name,output) :: outputs
  871. /// Set the conrete inputs for this build
  872. member b.GetInitialPartialBuild(vectorinputs,scalarinputs) =
  873. ToBound(ToBuild outputs,vectorinputs,scalarinputs)
  874. [<RequireQualifiedAccess>]
  875. type Severity =
  876. | Warning
  877. | Error
  878. type ErrorInfo = {
  879. FileName:string
  880. StartLine:int
  881. EndLine:int
  882. StartColumn:int
  883. EndColumn:int
  884. Severity:Severity
  885. Message:string
  886. Subcategory:string } with
  887. override e.ToString()=
  888. sprintf "%s (%d,%d)-(%d,%d) %s %s %s"
  889. e.FileName
  890. e.StartLine e.StartColumn e.EndLine e.EndColumn
  891. e.Subcategory
  892. (if e.Severity=Severity.Warning then "warning" else "error")
  893. e.Message
  894. /// Decompose a warning or error into parts: position, severity, message
  895. static member internal CreateFromExceptionAndAdjustEof(exn,warn,trim:bool,fallbackRange:range, (linesCount:int, lastLength:int)) =
  896. let r = ErrorInfo.CreateFromException(exn,warn,trim,fallbackRange)
  897. // Adjust to make sure that errors reported at Eof are shown at the linesCount
  898. let startline, schange = min (r.StartLine, false) (linesCount, true)
  899. let endline, echange = min (r.EndLine, false) (linesCount, true)
  900. if not (schange || echange) then r
  901. else
  902. let r = if schange then { r with StartLine = startline; StartColumn = lastLength } else r
  903. if echange then { r with EndLine = endline; EndColumn = 1 + lastLength } else r
  904. /// Decompose a warning or error into parts: position, severity, message
  905. static member internal CreateFromException(exn,warn,trim:bool,fallbackRange:range) =
  906. let m = match RangeOfError exn with Some m -> m | None -> fallbackRange
  907. let (s1:int),(s2:int) = Pos.toVS m.Start
  908. let (s3:int),(s4:int) = Pos.toVS (if trim then m.Start else m.End)
  909. let msg = bufs (fun buf -> OutputPhasedError buf exn false)
  910. {FileName=m.FileName; StartLine=s1; StartColumn=s2; EndLine=s3; EndColumn=s4; Severity=(if warn then Severity.Warning else Severity.Error); Subcategory=exn.Subcategory(); Message=msg}
  911. /// Use to reset error and warning handlers
  912. [<Sealed>]
  913. type ErrorScope() =
  914. let mutable errors = []
  915. static let mutable mostRecentError = None
  916. let unwindBP = PushThreadBuildPhaseUntilUnwind (BuildPhase.TypeCheck)
  917. let unwindEL =
  918. PushErrorLoggerPhaseUntilUnwind (fun _oldLogger ->
  919. { new ErrorLogger("ErrorScope") with
  920. member x.WarnSinkImpl(exn) =
  921. errors <- ErrorInfo.CreateFromException(exn,true,false,range.Zero):: errors
  922. member x.ErrorSinkImpl(exn) =
  923. let err = ErrorInfo.CreateFromException(exn,false,false,range.Zero)
  924. errors <- err :: errors
  925. mostRecentError <- Some(err)
  926. member x.ErrorCount = errors.Length })
  927. member x.Errors = errors |> List.filter (fun error -> error.Severity = Severity.Error)
  928. member x.Warnings = errors |> List.filter (fun error -> error.Severity = Severity.Warning)
  929. member x.ErrorsAndWarnings = errors
  930. member x.TryGetFirstErrorText() =
  931. match x.Errors with
  932. | error :: _ -> Some(error.Message)
  933. | [] -> None
  934. interface IDisposable with
  935. member d.Dispose() =
  936. unwindEL.Dispose() (* unwind pushes when ErrorScope disposes *)
  937. unwindBP.Dispose()
  938. static member MostRecentError = mostRecentError
  939. static member Protect<'a> (m:range) (f:unit->'a) (err:string->'a) : 'a =
  940. use errorScope = new ErrorScope()
  941. let res =
  942. try
  943. Some(f())
  944. with e -> errorRecovery e m; None
  945. match res with
  946. | Some(res) ->res
  947. | None ->
  948. match errorScope.TryGetFirstErrorText() with
  949. | Some text -> err text
  950. | None -> err ""
  951. static member ProtectWithDefault m f dflt =
  952. ErrorScope.Protect m f (fun _ -> dflt)
  953. static member ProtectAndDiscard m f =
  954. ErrorScope.Protect m f (fun _ -> ())
  955. // ------------------------------------------------------------------------------------------
  956. // The incremental build definition for parsing and typechecking F#
  957. // ------------------------------------------------------------------------------------------
  958. module internal IncrementalFSharpBuild =
  959. open Internal.Utilities
  960. open Internal.Utilities.Collections
  961. open IncrementalBuild
  962. open Microsoft.FSharp.Compiler.Build
  963. open Microsoft.FSharp.Compiler.Fscopts
  964. open Microsoft.FSharp.Compiler.Ast
  965. open Microsoft.FSharp.Compiler.ErrorLogger
  966. open Microsoft.FSharp.Compiler.Env
  967. open Microsoft.FSharp.Compiler.TypeChecker
  968. open Microsoft.FSharp.Compiler.Tast
  969. open Microsoft.FSharp.Compiler.Range
  970. open Microsoft.FSharp.Compiler
  971. open Microsoft.FSharp.Compiler.AbstractIL.Internal
  972. module Tc = Microsoft.FSharp.Compiler.TypeChecker
  973. open Microsoft.FSharp.Compiler.AbstractIL.Diagnostics
  974. open Internal.Utilities.Debug
  975. // This type is designed to be a lightweight way to instrument the most recent filenames that the
  976. // IncrementalBuilder did a parse/typecheck of, so we can more easily unittest/debug the
  977. // 'incremental' behavior of the product.
  978. type internal FixedLengthMRU<'T>() =
  979. let MAX = 40 // Length of the MRU. For our current unit tests, 40 is enough.
  980. let data : ('T option)[] = Array.create MAX None
  981. let mutable curIndex = 0
  982. let mutable numAdds = 0
  983. // called by the product, to note when a parse/typecheck happens for a file
  984. member this.Add(filename:'T) =
  985. numAdds <- numAdds + 1
  986. data.[curIndex] <- Some filename
  987. curIndex <- curIndex + 1
  988. if curIndex = MAX then
  989. curIndex <- 0
  990. member this.CurrentEventNum = numAdds
  991. // called by unit tests, returns 'n' most recent additions.
  992. member this.MostRecentList(n:int) : list<'T> =
  993. if n < 0 || n > MAX then
  994. raise <| new System.ArgumentOutOfRangeException("n", sprintf "n must be between 0 and %d, inclusive, but got %d" MAX n)
  995. let mutable remaining = n
  996. let mutable s = []
  997. let mutable i = curIndex - 1
  998. while remaining <> 0 do
  999. if i < 0 then
  1000. i <- MAX - 1
  1001. match data.[i] with
  1002. | None -> ()
  1003. | Some x -> s <- x :: s
  1004. i <- i - 1
  1005. remaining <- remaining - 1
  1006. List.rev s
  1007. type IBEvent =
  1008. | IBEParsed of string // filename
  1009. | IBETypechecked of string // filename
  1010. | IBEDeleted
  1011. let IncrementalBuilderEventsMRU = new FixedLengthMRU<IBEvent>()
  1012. let GetMostRecentIncrementalBuildEvents(n) = IncrementalBuilderEventsMRU.MostRecentList(n)
  1013. let GetCurrentIncrementalBuildEventNum() = IncrementalBuilderEventsMRU.CurrentEventNum
  1014. type FileDependency = {
  1015. // Name of the file
  1016. Filename : string
  1017. // If true, then deletion or creation of this file should trigger an entirely fresh build
  1018. ExistenceDependency : bool
  1019. // If true, then changing this file should trigger and call to incrementally build
  1020. IncrementalBuildDependency : bool } with
  1021. override this.ToString() =
  1022. sprintf "FileDependency(%s,existence=%A,incremental=%A)" this.Filename this.ExistenceDependency this.IncrementalBuildDependency
  1023. /// Accumulated results of type checking.
  1024. [<NoEquality; NoComparison>]
  1025. type TypeCheckAccumulator =
  1026. { tcState: TcState
  1027. tcImports:TcImports
  1028. tcGlobals:TcGlobals
  1029. tcConfig:TcConfig
  1030. tcEnv: TcEnv
  1031. topAttribs:TopAttribs option
  1032. typedImplFiles:TypedImplFile list
  1033. errors:(PhasedError * bool) list } // errors=true, warnings=false
  1034. /// Maximum time share for a piece of background work before it should (cooperatively) yield
  1035. /// to enable other requests to be serviced. Yielding means returning a continuation function
  1036. /// (via an Eventually<_> value of case NotYetDone) that can be called as the next piece of work.
  1037. let maxTimeShareMilliseconds =
  1038. #if SILVERLIGHT
  1039. 50L
  1040. #else
  1041. match System.Environment.GetEnvironmentVariable("mFSharp_MaxTimeShare") with
  1042. | null | "" -> 50L
  1043. | s -> int64 s
  1044. #endif
  1045. /// Global service state
  1046. type FrameworkImportsCacheKey = (*resolvedpath*)string list * string * (*ClrRoot*)string list* (*fsharpBinaries*)string
  1047. let private frameworkTcImportsCache = AgedLookup<FrameworkImportsCacheKey,(TcGlobals * TcImports)>(8, areSame=(fun (x,y) -> x = y))
  1048. /// This function strips the "System" assemblies from the tcConfig and returns a age-cached TcImports for them.
  1049. let GetFrameworkTcImports(tcConfig:TcConfig) =
  1050. // Split into installed and not installed.
  1051. let frameworkDLLs,nonFrameworkResolutions,unresolved = TcAssemblyResolutions.SplitNonFoundationalResolutions(tcConfig)
  1052. let frameworkDLLsKey =
  1053. frameworkDLLs
  1054. |> List.map(fun ar->ar.resolvedPath) // The cache key. Just the minimal data.
  1055. |> List.sort // Sort to promote cache hits.
  1056. let tcGlobals,frameworkTcImports =
  1057. // Prepare the frameworkTcImportsCache
  1058. //
  1059. // The data elements in this key are very important. There should be nothing else in the TcConfig that logically affects
  1060. // the import of a set of framework DLLs into F# CCUs. That is, the F# CCUs that result from a set of DLLs (including
  1061. // FSharp.Core.dll andb mscorlib.dll) must be logically invariant of all the other compiler configuration parameters.
  1062. let key = (frameworkDLLsKey,
  1063. tcConfig.primaryAssembly.Name,
  1064. tcConfig.ClrRoot,
  1065. tcConfig.fsharpBinariesDir)
  1066. match frameworkTcImportsCache.TryGet key with
  1067. | Some res -> res
  1068. | None ->
  1069. let tcConfigP = TcConfigProvider.Constant(tcConfig)
  1070. let ((tcGlobals,tcImports) as res) = TcImports.BuildFrameworkTcImports (tcConfigP, frameworkDLLs, nonFrameworkResolutions)
  1071. frameworkTcImportsCache.Put(key,res)
  1072. tcGlobals,tcImports
  1073. tcGlobals,frameworkTcImports,nonFrameworkResolutions,unresolved
  1074. /// An error logger that captures errors and eventually sends a single error or warning for all the errors and warning in a file
  1075. type CompilationErrorLogger (debugName:string, tcConfig:TcConfig, errorLogger:ErrorLogger) =
  1076. inherit ErrorLogger("CompilationErrorLogger("+debugName+")")
  1077. let warningsSeenInScope = new ResizeArray<_>()
  1078. let errorsSeenInScope = new ResizeArray<_>()
  1079. let warningOrError warn exn =
  1080. let warn = warn && not (ReportWarningAsError tcConfig.globalWarnLevel tcConfig.specificWarnOff tcConfig.specificWarnOn tcConfig.specificWarnAsError tcConfig.specificWarnAsWarn tcConfig.globalWarnAsError exn)
  1081. if not warn then
  1082. errorsSeenInScope.Add(exn)
  1083. errorLogger.ErrorSink(exn)
  1084. else if ReportWarning tcConfig.globalWarnLevel tcConfig.specificWarnOff tcConfig.specificWarnOn exn then
  1085. warningsSeenInScope.Add(exn)
  1086. errorLogger.WarnSink(exn)
  1087. override x.WarnSinkImpl(exn) = warningOrError true exn
  1088. override x.ErrorSinkImpl(exn) = warningOrError false exn
  1089. override x.ErrorCount = errorLogger.ErrorCount
  1090. member x.GetErrors() =
  1091. let errorsAndWarnings = (errorsSeenInScope |> ResizeArray.toList |> List.map(fun e->e,true)) @ (warningsSeenInScope |> ResizeArray.toList |> List.map(fun e->e,false))
  1092. errorsAndWarnings
  1093. /// This represents the global state established as each task function runs as part of the build
  1094. ///
  1095. /// Use to reset error and warning handlers
  1096. type CompilationGlobalsScope(errorLogger:ErrorLogger,phase,projectDirectory) =
  1097. do ignore projectDirectory
  1098. let unwindEL = PushErrorLoggerPhaseUntilUnwind(fun _ -> errorLogger)
  1099. let unwindBP = PushThreadBuildPhaseUntilUnwind (phase)
  1100. // Return the disposable object that cleans up
  1101. interface IDisposable with
  1102. member d.Dispose() =
  1103. unwindBP.Dispose()
  1104. unwindEL.Dispose()
  1105. //------------------------------------------------------------------------------------
  1106. // Rules for reactive building.
  1107. //
  1108. // This phrases the compile as a series of vector functions and vector manipulations.
  1109. // Rules written in this language are then transformed into a plan to execute the
  1110. // various steps of the process.
  1111. //-----------------------------------------------------------------------------------
  1112. type IncrementalBuilder(tcConfig : TcConfig, projectDirectory : string, assemblyName, niceNameGen : Ast.NiceNameGenerator, lexResourceManager,
  1113. sourceFiles:string list, ensureReactive, errorLogger:ErrorLogger,
  1114. keepGeneratedTypedAssembly:bool)
  1115. =
  1116. //use t = Trace.Call("IncrementalBuildVerbose", "Create", fun _ -> sprintf " tcConfig.includes = %A" tcConfig.includes)
  1117. let tcConfigP = TcConfigProvider.Constant(tcConfig)
  1118. let importsInvalidated = new Event<string>()
  1119. let beforeTypeCheckFile = new Event<_>()
  1120. // Resolve assemblies and create the framework TcImports. This is done when constructing the
  1121. // builder itself, rather than as an incremental task. This caches a level of "system" references. No type providers are
  1122. // included in these references.
  1123. let (tcGlobals,frameworkTcImports,nonFrameworkResolutions,unresolvedReferences) = GetFrameworkTcImports tcConfig
  1124. // Check for the existence of loaded sources and prepend them to the sources list if present.
  1125. let sourceFiles = tcConfig.GetAvailableLoadedSources() @ (sourceFiles|>List.map(fun s -> rangeStartup,s))
  1126. // Mark up the source files with an indicator flag indicating if they are the last source file in the project
  1127. let sourceFiles =
  1128. let flags = tcConfig.ComputeCanContainEntryPoint(sourceFiles |> List.map snd)
  1129. (sourceFiles,flags) ||> List.map2 (fun (m,nm) flag -> (m,nm,flag))
  1130. // Get the original referenced assembly names
  1131. // do System.Diagnostics.Debug.Assert(not((sprintf "%A" nonFrameworkResolutions).Contains("System.dll")),sprintf "Did not expect a system import here. %A" nonFrameworkResolutions)
  1132. // Get the names and time stamps of all the non-framework referenced assemblies, which will act
  1133. // as inputs to one of the nodes in the build.
  1134. //
  1135. // This operation is done when constructing the builder itself, rather than as an incremental task.
  1136. let nonFrameworkAssemblyInputs =
  1137. // Note we are not calling errorLogger.GetErrors() anywhere for this task.
  1138. // REVIEW: Consider if this is ok. I believe so, because this is a background build and we aren't currently reporting errors from the background build.
  1139. let errorLogger = CompilationErrorLogger("nonFrameworkAssemblyInputs", tcConfig, errorLogger)
  1140. // Return the disposable object that cleans up
  1141. use _holder = new CompilationGlobalsScope(errorLogger,BuildPhase.Parameter, projectDirectory)
  1142. [ for r in nonFrameworkResolutions do
  1143. let originalTimeStamp =
  1144. try
  1145. if FileSystem.SafeExists(r.resolvedPath) then
  1146. let result = FileSystem.GetLastWriteTimeShim(r.resolvedPath)
  1147. Trace.Print("FSharpBackgroundBuildVerbose", fun _ -> sprintf "Found referenced assembly '%s'.\n" r.resolvedPath)
  1148. result
  1149. else
  1150. Trace.Print("FSharpBackgroundBuildVerbose", fun _ -> sprintf "Did not find referenced assembly '%s' on disk.\n" r.resolvedPath)
  1151. DateTime.Now
  1152. with e ->
  1153. Trace.Print("FSharpBackgroundBuildVerbose", fun _ -> sprintf "Did not find referenced assembly '%s' due to exception.\n" r.resolvedPath)
  1154. // Note we are not calling errorLogger.GetErrors() anywhere for this task. This warning will not be reported...
  1155. // REVIEW: Consider if this is ok. I believe so, because this is a background build and we aren't currently reporting errors from the background build.
  1156. errorLogger.Warning(e)
  1157. DateTime.Now
  1158. yield (r.originalReference.Range,r.resolvedPath,originalTimeStamp) ]
  1159. // The IncrementalBuilder needs to hold up to one item that needs to be disposed, which is the tcImports for the incremental
  1160. // build.
  1161. let mutable cleanupItem = None : TcImports option
  1162. let disposeCleanupItem() =
  1163. match cleanupItem with
  1164. | None -> ()
  1165. | Some item ->
  1166. cleanupItem <- None
  1167. dispose item
  1168. let setCleanupItem x =
  1169. assert cleanupItem.IsNone
  1170. cleanupItem <- Some x
  1171. let mutable disposed = false
  1172. let assertNotDisposed() =
  1173. if disposed then
  1174. System.Diagnostics.Debug.Assert(false, "IncrementalBuild object has already been disposed!")
  1175. let mutable referenceCount = 0
  1176. ///----------------------------------------------------
  1177. /// START OF BUILD TASK FUNCTIONS
  1178. /// This is a build task function that gets placed into the build rules as the computation for a VectorStamp
  1179. ///
  1180. /// Get the timestamp of the given file name.
  1181. let StampFileNameTask (_m:range, filename:string, _isLastCompiland:bool) =
  1182. assertNotDisposed()
  1183. FileSystem.GetLastWriteTimeShim(filename)
  1184. /// This is a build task function that gets placed into the build rules as the computation for a VectorMap
  1185. ///
  1186. /// Parse the given files and return the given inputs. This function is expected to be
  1187. /// able to be called with a subset of sourceFiles and return the corresponding subset of
  1188. /// parsed inputs.
  1189. let ParseTask (sourceRange:range,filename:string,isLastCompiland) =
  1190. assertNotDisposed()
  1191. let errorLogger = CompilationErrorLogger("ParseTask", tcConfig, errorLogger)
  1192. // Return the disposable object that cleans up
  1193. use _holder = new CompilationGlobalsScope(errorLogger, BuildPhase.Parse, projectDirectory)
  1194. Trace.Print("FSharpBackgroundBuild", fun _ -> sprintf "Parsing %s..." filename)
  1195. try
  1196. IncrementalBuilderEventsMRU.Add(IBEParsed filename)
  1197. let result = ParseOneInputFile(tcConfig,lexResourceManager, [], filename ,isLastCompiland,errorLogger,(*retryLocked*)true)
  1198. Trace.PrintLine("FSharpBackgroundBuildVerbose", fun _ -> sprintf "done.")
  1199. result,sourceRange,filename,errorLogger.GetErrors ()
  1200. with exn ->
  1201. System.Diagnostics.Debug.Assert(false, sprintf "unexpected failure in IncrementalFSharpBuild.Parse\nerror = %s" (exn.ToString()))
  1202. failwith "last chance failure"
  1203. /// This is a build task function that gets placed into the build rules as the computation for a Vector.Stamp
  1204. ///
  1205. /// Timestamps of referenced assemblies are taken from the file's timestamp.
  1206. let TimestampReferencedAssemblyTask (_range, filename, originalTimeStamp) =
  1207. assertNotDisposed()
  1208. // Note: we are not calling errorLogger.GetErrors() anywhere. Is this a problem?
  1209. let errorLogger = CompilationErrorLogger("TimestampReferencedAssemblyTask", tcConfig, errorLogger)
  1210. // Return the disposable object that cleans up
  1211. use _holder = new CompilationGlobalsScope(errorLogger, BuildPhase.Parameter, projectDirectory) // Parameter because -r reference
  1212. let timestamp =
  1213. try
  1214. if FileSystem.SafeExists(filename) then
  1215. let ts = FileSystem.GetLastWriteTimeShim(filename)
  1216. if ts<>originalTimeStamp then
  1217. Trace.PrintLine("FSharpBackgroundBuildVerbose", fun _ -> sprintf "Noticing change in timestamp of file %s from %A to %A" filename originalTimeStamp ts)
  1218. else
  1219. Trace.PrintLine("FSharpBackgroundBuildVerbose", fun _ -> sprintf "Noticing no change in timestamp of file %s (still %A)" filename originalTimeStamp)
  1220. ts
  1221. else
  1222. Trace.PrintLine("FSharpBackgroundBuildVerbose", fun _ -> sprintf "Noticing that file %s was deleted, but ignoring that for timestamp checking" filename)
  1223. originalTimeStamp
  1224. with exn ->
  1225. // For example, malformed filename
  1226. Trace.PrintLine("FSharpBackgroundBuildVerbose", fun _ -> sprintf "Exception when checking stamp of file %s, using old stamp %A" filename originalTimeStamp)
  1227. // Note we are not calling errorLogger.GetErrors() anywhere for this task. This warning will not be reported...
  1228. // REVIEW: Consider if this is ok. I believe so, because this is a background build and we aren't currently reporting errors from the background build.
  1229. errorLogger.Warning exn
  1230. originalTimeStamp
  1231. timestamp
  1232. /// This is a build task function that gets placed into the build rules as the computation for a Vector.Demultiplex
  1233. ///
  1234. // Link all the assemblies together and produce the input typecheck accumulator
  1235. let CombineImportedAssembliesTask _ : TypeCheckAccumulator =
  1236. assertNotDisposed()
  1237. let errorLogger = CompilationErrorLogger("CombineImportedAssembliesTask", tcConfig, errorLogger)
  1238. // Return the disposable object that cleans up
  1239. use _holder = new CompilationGlobalsScope(errorLogger, BuildPhase.Parameter, projectDirectory)
  1240. let tcImports =
  1241. try
  1242. // We dispose any previous tcImports, for the case where a dependency changed which caused this part
  1243. // of the partial build to be re-evaluated.
  1244. disposeCleanupItem()
  1245. Trace.PrintLine("FSharpBackgroundBuild", fun _ -> "About to (re)create tcImports")
  1246. let tcImports = TcImports.BuildNonFrameworkTcImports(None,tcConfigP,tcGlobals,frameworkTcImports,nonFrameworkResolutions,unresolvedReferences)
  1247. #if EXTENSIONTYPING
  1248. for ccu in tcImports.GetCcusExcludingBase() do
  1249. // When a CCU reports an invalidation, merge them together and just report a
  1250. // general "imports invalidated". This triggers a rebuild.
  1251. ccu.Deref.InvalidateEvent.Add(fun msg -> importsInvalidated.Trigger msg)
  1252. #endif
  1253. Trace.PrintLine("FSharpBackgroundBuild", fun _ -> "(Re)created tcImports")
  1254. // The tcImports must be cleaned up if this builder ever gets disposed. We also dispose any previous
  1255. // tcImports should we be re-creating an entry because a dependency changed which caused this part
  1256. // of the partial build to be re-evaluated.
  1257. setCleanupItem tcImports
  1258. tcImports
  1259. with e ->
  1260. System.Diagnostics.Debug.Assert(false, sprintf "Could not BuildAllReferencedDllTcImports %A" e)
  1261. Trace.PrintLine("FSharpBackgroundBuild", fun _ -> "Failed to recreate tcImports\n %A")
  1262. errorLogger.Warning(e)
  1263. frameworkTcImports
  1264. let tcEnv0 = GetInitialTypecheckerEnv (Some assemblyName) rangeStartup tcConfig tcImports tcGlobals
  1265. let tcState0 = TypecheckInitialState (rangeStartup,assemblyName,tcConfig,tcGlobals,tcImports,niceNameGen,tcEnv0)
  1266. let tcAcc =
  1267. { tcGlobals=tcGlobals
  1268. tcImports=tcImports
  1269. tcState=tcState0
  1270. tcConfig=tcConfig
  1271. tcEnv=tcEnv0
  1272. topAttribs=None
  1273. typedImplFiles=[]
  1274. errors=errorLogger.GetErrors() }
  1275. tcAcc
  1276. /// This is a build task function that gets placed into the build rules as the computation for a Vector.ScanLeft
  1277. ///
  1278. /// Type check all files.
  1279. let TypeCheckTask (tcAcc:TypeCheckAccumulator) input : Eventually<TypeCheckAccumulator> =
  1280. assertNotDisposed()
  1281. match input with
  1282. | Some input, _sourceRange, filename, parseErrors->
  1283. IncrementalBuilderEventsMRU.Add(IBETypechecked filename)
  1284. let capturingErrorLogger = CompilationErrorLogger("TypeCheckTask", tcConfig, errorLogger)
  1285. let errorLogger = GetErrorLoggerFilteringByScopedPragmas(false,GetScopedPragmasForInput(input),capturingErrorLogger)
  1286. let tcAcc = {tcAcc with errors = tcAcc.errors @ parseErrors}
  1287. let fullComputation =
  1288. eventually {
  1289. Trace.PrintLine("FSharpBackgroundBuild", fun _ -> sprintf "Typechecking %s..." filename)
  1290. beforeTypeCheckFile.Trigger filename
  1291. let! (tcEnv,topAttribs,typedImplFiles),tcState =
  1292. TypecheckOneInputEventually ((fun () -> errorLogger.ErrorCount > 0),
  1293. tcConfig,tcAcc.tcImports,
  1294. tcAcc.tcGlobals,
  1295. None,
  1296. Nameres.TcResultsSink.NoSink,
  1297. tcAcc.tcState,input)
  1298. /// Only keep the typed interface files when doing a "full" build for fsc.exe, otherwise just throw them away
  1299. let typedImplFiles = if keepGeneratedTypedAssembly then typedImplFiles else []
  1300. Trace.PrintLine("FSharpBackgroundBuild", fun _ -> sprintf "done.")
  1301. return {tcAcc with tcState=tcState
  1302. tcEnv=tcEnv
  1303. topAttribs=Some topAttribs
  1304. typedImplFiles=typedImplFiles
  1305. errors = tcAcc.errors @ capturingErrorLogger.GetErrors() }
  1306. }
  1307. // Run part of the Eventually<_> computation until a timeout is reached. If not complete,
  1308. // return a new Eventually<_> computation which recursively runs more of the computation.
  1309. // - When the whole thing is finished commit the error results sent through the errorLogger.
  1310. // - Each time we do real work we reinstall the CompilationGlobalsScope
  1311. if ensureReactive then
  1312. let timeSlicedComputation =
  1313. fullComputation |>
  1314. Eventually.repeatedlyProgressUntilDoneOrTimeShareOver
  1315. maxTimeShareMilliseconds
  1316. (fun f ->
  1317. // Reinstall the compilation globals each time we start or restart
  1318. use unwind = new CompilationGlobalsScope (errorLogger, BuildPhase.TypeCheck, projectDirectory)
  1319. Trace.Print("FSharpBackgroundBuildVerbose", fun _ -> sprintf "continuing %s.\n" filename)
  1320. f()
  1321. (* unwind dispose *)
  1322. )
  1323. timeSlicedComputation
  1324. else
  1325. use unwind = new CompilationGlobalsScope (errorLogger, BuildPhase.TypeCheck, projectDirectory)
  1326. fullComputation |> Eventually.force |> Eventually.Done
  1327. | _ ->
  1328. Eventually.Done tcAcc
  1329. /// This is a build task function that gets placed into the build rules as the computation for a Vector.Demultiplex
  1330. ///
  1331. /// Finish up the typechecking to produce outputs for the rest of the compilation process
  1332. let FinalizeTypeCheckTask (tcStates:TypeCheckAccumulator[]) =
  1333. assertNotDisposed()
  1334. Trace.PrintLine("FSharpBackgroundBuildVerbose", fun _ -> sprintf "Finalizing Type Check" )
  1335. let finalAcc = tcStates.[tcStates.Length-1]
  1336. let results = tcStates |> List.ofArray |> List.map (fun acc-> acc.tcEnv, (Option.get acc.topAttribs), acc.typedImplFiles)
  1337. let (tcEnvAtEndOfLastFile,topAttrs,mimpls),tcState = TypecheckMultipleInputsFinish (results,finalAcc.tcState)
  1338. let tcState,tassembly = TypecheckClosedInputSetFinish (mimpls,tcState)
  1339. tcState, topAttrs, tassembly, tcEnvAtEndOfLastFile, finalAcc.tcImports, finalAcc.tcGlobals, finalAcc.tcConfig
  1340. // END OF BUILD TASK FUNCTIONS
  1341. // ---------------------------------------------------------------------------------------------
  1342. // ---------------------------------------------------------------------------------------------
  1343. // START OF BUILD DESCRIPTION
  1344. let buildDescription = new BuildDescriptionScope ()
  1345. // Inputs
  1346. let filenames = InputVector<range*string*bool> "FileNames"
  1347. let referencedAssemblies = InputVector<range*string*DateTime> "ReferencedAssemblies"
  1348. // Build
  1349. let stampedFileNames = Vector.Stamp "SourceFileTimeStamps" StampFileNameTask filenames
  1350. let parseTrees = Vector.Map "Parse" ParseTask stampedFileNames
  1351. let stampedReferencedAssemblies = Vector.Stamp "TimestampReferencedAssembly" TimestampReferencedAssemblyTask referencedAssemblies
  1352. let initialTcAcc = Vector.Demultiplex "CombineImportedAssemblies" CombineImportedAssembliesTask stampedReferencedAssemblies
  1353. let tcStates = Vector.ScanLeft "TypeCheck" TypeCheckTask initialTcAcc parseTrees
  1354. let finalizedTypeCheck = Vector.Demultiplex "FinalizeTypeCheck" FinalizeTypeCheckTask tcStates
  1355. // Outputs
  1356. do buildDescription.DeclareVectorOutput ("ParseTrees", parseTrees)
  1357. do buildDescription.DeclareVectorOutput ("TypeCheckingStates",tcStates)
  1358. do buildDescription.DeclareScalarOutput ("InitialTcAcc", initialTcAcc)
  1359. do buildDescription.DeclareScalarOutput ("FinalizeTypeCheck", finalizedTypeCheck)
  1360. // END OF BUILD DESCRIPTION
  1361. // ---------------------------------------------------------------------------------------------
  1362. let fileDependencies =
  1363. let unresolvedFileDependencies =
  1364. unresolvedReferences
  1365. |> List.map (function Microsoft.FSharp.Compiler.Build.UnresolvedAssemblyReference(referenceText, _) -> referenceText)
  1366. |> List.filter(fun referenceText->not(Path.IsInvalidPath(referenceText))) // Exclude things that are definitely not a file name
  1367. |> List.map(fun referenceText -> if FileSystem.IsPathRootedShim(referenceText) then referenceText else System.IO.Path.Combine(projectDirectory,referenceText))
  1368. |> List.map (fun file->{Filename = file; ExistenceDependency = true; IncrementalBuildDependency = true })
  1369. let resolvedFileDependencies =
  1370. nonFrameworkResolutions |> List.map (fun r -> {Filename = r.resolvedPath ; ExistenceDependency = true; IncrementalBuildDependency = true })
  1371. #if DEBUG
  1372. do resolvedFileDependencies |> List.iter (fun x -> System.Diagnostics.Debug.Assert(FileSystem.IsPathRootedShim(x.Filename), sprintf "file dependency should be absolute path: '%s'" x.Filename))
  1373. #endif
  1374. let sourceFileDependencies =
  1375. sourceFiles |> List.map (fun (_,f,_) -> {Filename = f ; ExistenceDependency = true; IncrementalBuildDependency = true })
  1376. List.concat [unresolvedFileDependencies;resolvedFileDependencies;sourceFileDependencies]
  1377. #if TRACK_DOWN_EXTRA_BACKSLASHES
  1378. do fileDependencies |> List.iter(fun dep ->
  1379. Debug.Assert(not(dep.Filename.Contains(@"\\")), "IncrementalBuild.Create results in a non-canonical filename with extra backslashes: "^dep.Filename)
  1380. )
  1381. #endif
  1382. do IncrementalBuilderEventsMRU.Add(IBEDeleted)
  1383. let buildInputs = ["FileNames", sourceFiles.Length, sourceFiles |> List.map box
  1384. "ReferencedAssemblies", nonFrameworkAssemblyInputs.Length, nonFrameworkAssemblyInputs |> List.map box ]
  1385. // This is the intial representation of progress through the build, i.e. we have made no progress.
  1386. let mutable partialBuild = buildDescription.GetInitialPartialBuild (buildInputs, [])
  1387. member this.IncrementUsageCount() =
  1388. assertNotDisposed()
  1389. referenceCount <- referenceCount + 1
  1390. { new System.IDisposable with member x.Dispose() = this.DecrementUsageCount() }
  1391. member this.DecrementUsageCount() =
  1392. assertNotDisposed()
  1393. referenceCount <- referenceCount - 1
  1394. if referenceCount = 0 then
  1395. disposed <- true
  1396. disposeCleanupItem()
  1397. member __.IsAlive = referenceCount > 0
  1398. member __.TcConfig = tcConfig
  1399. member __.BeforeTypeCheckFile = beforeTypeCheckFile.Publish
  1400. member __.ImportedCcusInvalidated = importsInvalidated.Publish
  1401. member __.Dependencies = fileDependencies
  1402. #if EXTENSIONTYPING
  1403. member __.ThereAreLiveTypeProviders =
  1404. let liveTPs =
  1405. match cleanupItem with
  1406. | None -> []
  1407. | Some tcImports -> [for ia in tcImports.GetImportedAssemblies() do yield! ia.TypeProviders]
  1408. match liveTPs with
  1409. | [] -> false
  1410. | _ -> true
  1411. #endif
  1412. member __.Step () =
  1413. match IncrementalBuild.Step "TypeCheckingStates" partialBuild with
  1414. | None ->
  1415. false
  1416. | Some newPartialBuild ->
  1417. partialBuild <- newPartialBuild
  1418. true
  1419. member __.GetAntecedentTypeCheckResultsBySlot slotOfFile =
  1420. let result =
  1421. match slotOfFile with
  1422. | (*first file*) 0 -> GetScalarResult<TypeCheckAccumulator>("InitialTcAcc",partialBuild)
  1423. | _ -> GetVectorResultBySlot<TypeCheckAccumulator>("TypeCheckingStates",slotOfFile-1,partialBuild)
  1424. match result with
  1425. | Some({tcState=tcState; tcGlobals=tcGlobals; tcConfig=tcConfig; tcImports=tcImports; errors=errors},timestamp) ->
  1426. Some(tcState,tcImports,tcGlobals,tcConfig,errors,timestamp)
  1427. | _->None
  1428. member __.TypeCheck() =
  1429. let newPartialBuild = IncrementalBuild.Eval "FinalizeTypeCheck" partialBuild
  1430. partialBuild <- newPartialBuild
  1431. match GetScalarResult<Build.TcState * TypeChecker.TopAttribs * Tast.TypedAssembly * TypeChecker.TcEnv * Build.TcImports * Env.TcGlobals * Build.TcConfig>("FinalizeTypeCheck",partialBuild) with
  1432. | Some((tcState,topAttribs,typedAssembly,tcEnv,tcImports,tcGlobals,tcConfig),_) -> tcState,topAttribs,typedAssembly,tcEnv,tcImports,tcGlobals,tcConfig
  1433. | None -> failwith "Build was not evaluated."
  1434. member __.GetSlotOfFileName(filename:string) =
  1435. // Get the slot of the given file and force it to build.
  1436. let CompareFileNames (_,f1,_) (_,f2,_) =
  1437. let result =
  1438. System.String.Compare(f1,f2,StringComparison.CurrentCultureIgnoreCase)=0
  1439. || System.String.Compare(FileSystem.GetFullPathShim(f1),FileSystem.GetFullPathShim(f2),StringComparison.CurrentCultureIgnoreCase)=0
  1440. result
  1441. GetSlotByInput("FileNames",(rangeStartup,filename,false),partialBuild,CompareFileNames)
  1442. #if NO_QUICK_SEARCH_HELPERS // only used in QuickSearch prototype
  1443. #else
  1444. member __.GetSlotsCount () =
  1445. let expr = GetExprByName(partialBuild,"FileNames")
  1446. let id = BuildRuleExpr.GetId(expr)
  1447. match partialBuild.Results.TryFind(id) with
  1448. | Some(VectorResult vr) -> vr.Size
  1449. | _ -> failwith "Cannot know sizes"
  1450. member this.GetParseResultsBySlot slot =
  1451. let result = GetVectorResultBySlot<Ast.ParsedInput option * Range.range * string>("ParseTrees",slot,partialBuild)
  1452. match result with
  1453. | Some ((inputOpt,range,fileName), _) -> inputOpt, range, fileName
  1454. | None ->
  1455. let newPartialBuild = IncrementalBuild.Eval "ParseTrees" partialBuild
  1456. partialBuild <- newPartialBuild
  1457. this.GetParseResultsBySlot slot
  1458. #endif //
  1459. //------------------------------------------------------------------------------------
  1460. // CreateIncrementalBuilder (for background type checking). Note that fsc.fs also
  1461. // creates an incremental builder used by the command line compiler.
  1462. //-----------------------------------------------------------------------------------
  1463. static member CreateBackgroundBuilderForProjectOptions (scriptClosureOptions:LoadClosure option, sourceFiles:string list, commandLineArgs:string list, projectDirectory, useScriptResolutionRules, isIncompleteTypeCheckEnvironment) =
  1464. // Trap and report warnings and errors from creation.
  1465. use errorScope = new ErrorScope()
  1466. // Create the builder.
  1467. // Share intern'd strings across all lexing/parsing
  1468. let resourceManager = new Lexhelp.LexResourceManager()
  1469. /// Create a type-check configuration
  1470. let tcConfigB =
  1471. let defaultFSharpBinariesDir = Internal.Utilities.FSharpEnvironment.BinFolderOfDefaultFSharpCompiler.Value
  1472. // see also fsc.fs:runFromCommandLineToImportingAssemblies(), as there are many similarities to where the PS creates a tcConfigB
  1473. let tcConfigB =
  1474. TcConfigBuilder.CreateNew(defaultFSharpBinariesDir, implicitIncludeDir=projectDirectory,
  1475. optimizeForMemory=true, isInteractive=false, isInvalidationSupported=true)
  1476. // The following uses more memory but means we don't take read-exclusions on the DLLs we reference
  1477. // Could detect well-known assemblies--ie System.dll--and open them with read-locks
  1478. tcConfigB.openBinariesInMemory <- true
  1479. tcConfigB.resolutionEnvironment
  1480. <- if useScriptResolutionRules
  1481. then MSBuildResolver.DesigntimeLike
  1482. else MSBuildResolver.CompileTimeLike
  1483. tcConfigB.conditionalCompilationDefines <-
  1484. let define = if useScriptResolutionRules then "INTERACTIVE" else "COMPILED"
  1485. define::tcConfigB.conditionalCompilationDefines
  1486. // Apply command-line arguments.
  1487. try
  1488. ParseCompilerOptions
  1489. (fun _sourceOrDll -> () )
  1490. (Fscopts.GetCoreServiceCompilerOptions tcConfigB)
  1491. commandLineArgs
  1492. with e -> errorRecovery e range0
  1493. // Never open PDB files for the language service, even if --standalone is specified
  1494. tcConfigB.openDebugInformationForLaterStaticLinking <- false
  1495. if tcConfigB.framework then
  1496. // ~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
  1497. // If you see a failure here running unittests consider whether it it caused by
  1498. // a mismatched version of Microsoft.Build.Framework. Run unittests under a debugger. If
  1499. // you see an old version of Microsoft.Build.*.dll getting loaded it it is likely caused by
  1500. // using an old ITask or ITaskItem from some tasks assembly.
  1501. // I solved this problem by adding a Unittests.config.dll which has a binding redirect to
  1502. // the current (right now, 4.0.0.0) version of the tasks assembly.
  1503. // ~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
  1504. System.Diagnostics.Debug.Assert(false, "Language service requires --noframework flag")
  1505. tcConfigB.framework<-false
  1506. tcConfigB
  1507. match scriptClosureOptions with
  1508. | Some closure ->
  1509. let dllReferences =
  1510. [for reference in tcConfigB.referencedDLLs do
  1511. // If there's (one or more) resolutions of closure references then yield them all
  1512. match closure.References |> List.tryFind (fun (resolved,_)->resolved=reference.Text) with
  1513. | Some(resolved,closureReferences) ->
  1514. for closureReference in closureReferences do
  1515. yield AssemblyReference(closureReference.originalReference.Range, resolved)
  1516. | None -> yield reference]
  1517. tcConfigB.referencedDLLs<-[]
  1518. // Add one by one to remove duplicates
  1519. for dllReference in dllReferences do
  1520. tcConfigB.AddReferencedAssemblyByPath(dllReference.Range,dllReference.Text)
  1521. tcConfigB.knownUnresolvedReferences<-closure.UnresolvedReferences
  1522. | None -> ()
  1523. // Make sure System.Numerics is referenced for out-of-project .fs files
  1524. if isIncompleteTypeCheckEnvironment then
  1525. tcConfigB.addVersionSpecificFrameworkReferences <- true
  1526. let _, _, assemblyName = tcConfigB.DecideNames sourceFiles
  1527. let tcConfig = TcConfig.Create(tcConfigB,validate=true)
  1528. let niceNameGen = NiceNameGenerator()
  1529. // Sink internal errors and warnings.
  1530. // Q: Why is it ok to ignore these?
  1531. // jomof: These are errors from the background build of files the user doesn't see. Squiggles will appear in the editted file via the foreground parse\typecheck
  1532. let warnSink (exn:PhasedError) = Trace.PrintLine("IncrementalBuild", (exn.ToString >> sprintf "Background warning: %s"))
  1533. let errorSink (exn:PhasedError) = Trace.PrintLine("IncrementalBuild", (exn.ToString >> sprintf "Background error: %s"))
  1534. let errorLogger =
  1535. { new ErrorLogger("CreateIncrementalBuilder") with
  1536. member x.ErrorCount=0
  1537. member x.WarnSinkImpl e = warnSink e
  1538. member x.ErrorSinkImpl e = errorSink e }
  1539. let builder =
  1540. new IncrementalBuilder
  1541. (tcConfig, projectDirectory, assemblyName, niceNameGen,
  1542. resourceManager, sourceFiles, true, // stay reactive
  1543. errorLogger, false // please discard implementation results
  1544. )
  1545. Trace.PrintLine("IncrementalBuild", fun () -> sprintf "CreateIncrementalBuilder: %A" builder.Dependencies)
  1546. #if DEBUG
  1547. builder.Dependencies|> List.iter (fun df -> System.Diagnostics.Debug.Assert(FileSystem.IsPathRootedShim(df.Filename), sprintf "dependency file was not absolute: '%s'" df.Filename))
  1548. #endif
  1549. (builder, errorScope.ErrorsAndWarnings)