1structure Holmake_tools :> Holmake_tools = 2struct 3 4 5open Systeml 6open Holmake_tools_dtype 7 8fun K x y = x 9 10structure Path = OS.Path 11structure FileSys = OS.FileSys 12 13val DEFAULT_OVERLAY = "Overlay.ui" 14 15fun normPath s = OS.Path.toString(OS.Path.fromString s) 16fun itstrings f [] = raise Fail "itstrings: empty list" 17 | itstrings f [x] = x 18 | itstrings f (h::t) = f h (itstrings f t); 19fun fullPath slist = normPath 20 (itstrings (fn chunk => fn path => OS.Path.concat (chunk,path)) slist); 21 22val spacify = String.concatWith " " 23fun nspaces f n = if n <= 0 then () else (f " "; nspaces f (n - 1)) 24fun collapse_bslash_lines s = let 25 val charlist = explode s 26 fun trans [] = [] 27 | trans (#"\\"::(#"\n"::rest)) = trans rest 28 | trans (x::xs) = x :: trans xs 29in 30 implode (trans charlist) 31end 32 33val kernelid_fname = Path.concat(HOLDIR, ".kernelidstr") 34fun checkterm pfx s = 35 case OS.Process.getEnv "TERM" of 36 NONE => s 37 | SOME term => 38 if String.isPrefix "xterm" term orelse term = "screen" then 39 pfx ^ s ^ "\027[0m" 40 else 41 s 42 43val bold = checkterm "\027[1m" 44val boldred = checkterm "\027[31m\027[1m" 45val boldgreen = checkterm "\027[32m\027[1m" 46val boldyellow = checkterm "\027[33m\027[1m" 47val red = checkterm "\027[31m" 48val dim = checkterm "\027[2m" 49val CLR_EOL = "\027[0K" (* ANSI clear to EOL code *) 50 51fun optbind x f = 52 case x of 53 SOME y => f y 54 | _ => NONE 55fun optchoice (opt1, opt2) = 56 case opt1 of 57 NONE => opt2 58 | _ => opt1 59infix ++ 60val op++ = optchoice 61fun isc r = Int.scan StringCvt.DEC r 62fun char_reader (s, i) = if size s <= i then NONE 63 else SOME (String.sub(s,i), (s,i+1)) 64fun estbind m f s = 65 case m s of 66 NONE => NONE 67 | SOME(r,s') => f r s' 68 69fun run s = 70 let 71 val outfile = OS.FileSys.tmpName() 72 val res = OS.Process.system (String.concatWith " " [s,"1>",outfile,"2>&1"]) 73 val output = 74 let 75 val istrm = TextIO.openIn outfile 76 in 77 TextIO.inputAll istrm before TextIO.closeIn istrm 78 end handle IO.Io _ => "" 79 in 80 if OS.Process.isSuccess res then SOME output else NONE 81 end 82 83fun optassert P x = if P x then SOME x else NONE 84 85fun getWidth0 () = 86 let 87 fun tputresult istr = Int.fromString istr 88 fun sttyresult i2str = 89 Option.map #1 90 (estbind (isc char_reader) (fn _ => isc char_reader) 91 (i2str, 0)) 92 fun positive m x = optbind (m x) (optassert (fn i => i > 0)) 93 in 94 optbind (run "stty size") (positive sttyresult) ++ 95 optbind (run "tput cols") (positive tputresult) ++ 96 SOME 80 97 end 98 99fun getWidth() = valOf (getWidth0()) 100 101fun realspace_delimited_fields s = let 102 open Substring 103 fun inword cword words ss = 104 case getc ss of 105 NONE => List.rev (implode (List.rev cword) :: words) 106 | SOME (c,ss') => let 107 in 108 case c of 109 #" " => outword (implode (List.rev cword) :: words) ss' 110 | #"\\" => let 111 in 112 case getc ss' of 113 NONE => List.rev (implode (List.rev (c::cword)) :: words) 114 | SOME (c',ss'') => inword (c'::cword) words ss'' 115 end 116 | _ => inword (c::cword) words ss' 117 end 118 and outword words ss = 119 case getc ss of 120 NONE => List.rev words 121 | SOME(c, ss') => let 122 in 123 case c of 124 #" " => outword words ss' 125 | _ => inword [] words ss 126 end 127in 128 outword [] (full s) 129end 130 131type output_functions = {warn : string -> unit, 132 info : string -> unit, 133 chatty : string -> unit, 134 tgtfatal : string -> unit, 135 diag : (unit -> string) -> unit} 136 137fun die_with message = let 138 open TextIO 139in 140 output(stdErr, message ^ "\n"); 141 flushOut stdErr; 142 OS.Process.exit OS.Process.failure 143end 144 145fun shorten_name name = 146 if OS.Path.file name = "Holmake" then "Holmake" else name 147 148fun output_functions {usepfx,chattiness=n} = let 149 val execname = if usepfx then shorten_name (CommandLine.name()) ^ ": " else "" 150 open TextIO 151 fun msg strm s = 152 if s = "" then () 153 else 154 let 155 val ss = Substring.full s 156 val (pfx_ss,sfx_ss) = Substring.splitl Char.isSpace ss 157 val pfx = Substring.string pfx_ss 158 val sfx = Substring.string sfx_ss 159 in 160 output(strm, pfx ^ execname ^ sfx^"\n"); 161 flushOut strm 162 end 163 fun donothing _ = () 164 val warn = if n >= 1 then msg stdErr else donothing 165 val info = if n >= 1 then msg stdOut else donothing 166 val chatty = if n >= 2 then msg stdOut else donothing 167 val tgtfatal = msg stdErr 168 val diag = if n >= 3 then (fn sf => msg stdErr (sf())) else donothing 169in 170 {warn = warn, diag = diag, tgtfatal = tgtfatal, info = info, chatty = chatty} 171end 172 173fun exists_readable s = OS.FileSys.access(s, [OS.FileSys.A_READ]) 174 175fun check_distrib toolname = let 176 val fpath = fullPath 177 open FileSys 178 fun checkdir () = 179 access ("sigobj", [A_READ, A_EXEC]) andalso 180 isDir "sigobj" andalso 181 access (fpath ["bin", "Holmake"], [A_READ, A_EXEC]) 182 fun traverse () = let 183 val d = getDir() 184 in 185 if checkdir() then SOME (fpath [d, "bin", toolname]) 186 else if Path.isRoot d then NONE 187 else (chDir Path.parentArc; traverse()) 188 end 189 val start = getDir() 190in 191 traverse() before chDir start 192end 193 194fun do_lastmade_checks (ofns : output_functions) {no_lastmakercheck} = let 195 val {warn,diag,...} = ofns 196 val mypath = find_my_path() 197 val _ = diag (K ("running "^mypath)) 198 fun write_lastmaker_file () = let 199 val outstr = TextIO.openOut ".HOLMK/lastmaker" 200 in 201 TextIO.output(outstr, mypath ^ "\n"); 202 TextIO.closeOut outstr 203 end handle IO.Io _ => () 204 205 fun lmfile() = 206 if not no_lastmakercheck andalso 207 FileSys.access (".HOLMK/lastmaker", [FileSys.A_READ]) 208 then let 209 val _ = diag (K "Found a lastmaker file to look at.") 210 val istrm = TextIO.openIn ".HOLMK/lastmaker" 211 in 212 case TextIO.inputLine istrm of 213 NONE => (warn "Empty Last Maker file"; 214 TextIO.closeIn istrm; 215 write_lastmaker_file()) 216 | SOME s => let 217 open Substring 218 val path = string (dropr Char.isSpace (full s)) 219 val _ = TextIO.closeIn istrm 220 in 221 if FileSys.access (path, [FileSys.A_READ, FileSys.A_EXEC]) 222 then 223 if mypath = path then () 224 else 225 (warn ("*** Switching to execute "^path); 226 warn ("*** (Honouring last Holmake call in this directory)"); 227 Systeml.exec(path, 228 path::"--nolmbc"::CommandLine.arguments())) 229 else (warn "Garbage in Last Maker file"; 230 write_lastmaker_file()) 231 end 232 end 233 else write_lastmaker_file() 234in 235 diag (K "Looking to see if I am in a HOL distribution."); 236 case check_distrib "Holmake" of 237 NONE => let 238 in 239 diag (K "Not in a HOL distribution"); 240 lmfile() 241 end 242 | SOME p => 243 if p = mypath then diag (K "In the right HOL distribution") 244 else if no_lastmakercheck then 245 diag (K "In the wrong distribution, but --nolmbc takes precedence.") 246 else 247 (warn ("*** Switching to execute "^p); 248 warn ("*** (As we are in/under its HOLDIR)"); 249 Systeml.exec (p, p::"--nolmbc"::CommandLine.arguments())) 250end 251 252fun string_part0 (Theory s) = s 253 | string_part0 (Script s) = s 254 | string_part0 (Other s) = s 255fun string_part1 (RawArticle s) = s 256 | string_part1 (ProcessedArticle s) = s 257fun string_part (UO c) = string_part0 c 258 | string_part (UI c) = string_part0 c 259 | string_part (SML c) = string_part0 c 260 | string_part (SIG c) = string_part0 c 261 | string_part (ART c) = string_part1 c 262 | string_part (DAT s) = s 263 | string_part (Unhandled s) = s 264 265fun isProperSuffix s1 s2 = 266 if size s1 < size s2 andalso String.isSuffix s1 s2 then 267 SOME (String.substring(s2,0,size s2 - size s1)) 268 else NONE 269 270fun toCodeType s = let 271 val possprefix = isProperSuffix "Theory" s 272in 273 if (isSome possprefix) then Theory (valOf possprefix) 274 else let 275 val possprefix = isProperSuffix "Script" s 276 in 277 if isSome possprefix then Script (valOf possprefix) 278 else Other s 279 end 280end 281 282fun toArticleType s = let 283 val possprefix = isProperSuffix ".ot" s 284in 285 if (isSome possprefix) then ProcessedArticle (valOf possprefix) 286 else RawArticle s 287end 288 289fun toFile s0 = let 290 val {base = s, ext} = OS.Path.splitBaseExt s0 291in 292 case ext of 293 SOME "sml" => SML (toCodeType s) 294 | SOME "sig" => SIG (toCodeType s) 295 | SOME "uo" => UO (toCodeType s) 296 | SOME "ui" => UI (toCodeType s) 297 | SOME "art" => ART (toArticleType s) 298 | SOME "dat" => if String.isSuffix "Theory" s then 299 DAT (String.extract(s,0,SOME(size s - 6))) 300 else Unhandled s0 301 | _ => Unhandled s0 302end 303 304fun extract_theory slist = 305 case slist of 306 [] => NONE 307 | s :: rest => (case toFile s of 308 SML (Theory thy) => SOME thy 309 | _ => extract_theory rest) 310 311fun codeToString c = 312 case c of 313 Theory s => s ^ "Theory" 314 | Script s => s ^ "Script" 315 | Other s => s 316 317fun articleToString c = 318 case c of 319 RawArticle s => s 320 | ProcessedArticle s => s ^ ".ot" 321 322fun fromFile f = 323 case f of 324 UO c => codeToString c ^ ".uo" 325 | UI c => codeToString c ^ ".ui" 326 | SIG c => codeToString c ^ ".sig" 327 | SML c => codeToString c ^ ".sml" 328 | ART c => articleToString c ^ ".art" 329 | DAT s => s ^ "Theory.dat" 330 | Unhandled s => s 331 332fun fromFileNoSuf f = 333 case f of 334 UO c => codeToString c 335 | UI c => codeToString c 336 | SIG c => codeToString c 337 | SML c => codeToString c 338 | ART a => articleToString a 339 | DAT s => s 340 | Unhandled s => s 341 342fun member m [] = false 343 | member m (x::xs) = if x = m then true else member m xs 344fun set_union s1 s2 = 345 case s1 of 346 [] => s2 347 | (e::es) => let 348 val s' = set_union es s2 349 in 350 if member e s' then s' else e::s' 351 end 352fun delete m [] = [] 353 | delete m (x::xs) = if m = x then delete m xs else x::delete m xs 354fun set_diff s1 s2 = foldl (fn (s2e, s1') => delete s2e s1') s1 s2 355fun remove_duplicates [] = [] 356 | remove_duplicates (x::xs) = x::(remove_duplicates (delete x xs)) 357 358 359 360fun file_compare (f1, f2) = String.compare (fromFile f1, fromFile f2) 361 362fun primary_dependent f = 363 case f of 364 UO c => SOME (SML c) 365 | UI c => SOME (SIG c) 366 | SML (Theory s) => SOME (SML (Script s)) 367 | SIG (Theory s) => SOME (SML (Script s)) 368 | DAT s => SOME (SML (Script s)) 369 | ART (RawArticle s) => SOME (SML (Script s)) 370 | ART (ProcessedArticle s) => SOME (ART (RawArticle s)) 371 | _ => NONE 372 373fun read_files ds P action = 374 case OS.FileSys.readDir ds of 375 NONE => OS.FileSys.closeDir ds 376 | SOME nextfile => 377 (if P nextfile then action nextfile else (); 378 read_files ds P action) 379 380fun quiet_remove s = OS.FileSys.remove s handle e => () 381 382fun clean_dir {extra_cleans} = let 383 val cdstream = OS.FileSys.openDir "." 384 fun to_delete f = 385 case (toFile f) of 386 UO _ => true 387 | UI _ => true 388 | SIG (Theory _) => true 389 | SML (Theory _) => true 390 | SML (Script s) => 391 (case OS.Path.ext s of SOME "art" => true | _ => false) 392 | DAT _ => true 393 | ART _ => true 394 | _ => false 395in 396 read_files cdstream to_delete quiet_remove; 397 app quiet_remove extra_cleans 398end 399 400fun clean_forReloc {holheap} = 401 if Systeml.ML_SYSNAME = "poly" then 402 case holheap of SOME s => quiet_remove s | _ => () 403 else () 404 405exception DirNotFound 406fun clean_depdir {depdirname} = let 407 val depds = OS.FileSys.openDir depdirname handle 408 OS.SysErr _ => raise DirNotFound 409in 410 read_files depds 411 (fn _ => true) 412 (fn s => OS.FileSys.remove (fullPath [depdirname, s])); 413 OS.FileSys.rmDir depdirname; 414 true 415end handle OS.SysErr (mesg, _) => let 416 in 417 print ("make cleanDeps failed with message: "^mesg^"\n"); 418 false 419 end 420 | DirNotFound => true 421 422val nice_dir = 423 case OS.Process.getEnv "HOME" of 424 SOME h => (fn s => if String.isPrefix h s then 425 "~" ^ String.extract(s,size h,NONE) 426 else s) 427 | NONE => (fn s => s) 428 429fun xterm_log s = 430 ignore (OS.Process.system ("/bin/sh -c 'printf \"\\033]0;" ^ s ^ "\\007\"'")) 431 432val terminal_log = 433 if Systeml.isUnix then xterm_log 434 else (fn s => ()) 435 436structure hmdir = 437struct 438 439type t = {absdir : string, relpath : string option} 440 441fun op+ (d, e) = Path.mkCanonical (Path.concat(d, e)) 442 443fun curdir () = {relpath = SOME (OS.Path.currentArc), 444 absdir = OS.FileSys.getDir()} 445 446fun compare ({absdir = d1, ...} : t, {absdir = d2, ...} : t) = 447 String.compare (d1, d2) 448 449fun toString {relpath,absdir} = 450 case relpath of 451 NONE => absdir 452 | SOME p => p 453 454fun toAbsPath {relpath,absdir} = absdir 455 456fun pretty_dir d = 457 let 458 val abs = toAbsPath d 459 val abs' = holpathdb.reverse_lookup {path=abs} 460 in 461 if abs = abs' then toString d else abs' 462 end 463 464fun fromPath {origin,path} = 465 if Path.isAbsolute path then 466 {relpath = NONE, absdir = Path.mkCanonical path} 467 else 468 {relpath = SOME path, absdir = origin + path} 469 470fun extendp {base = {relpath, absdir}, extension} = let 471 val relpath_str = case relpath of NONE => "NONE" 472 | SOME s => "SOME("^s^")" 473in 474 if Path.isAbsolute extension then 475 {relpath = NONE, absdir = Path.mkCanonical extension} 476 else 477 case relpath of 478 NONE => {absdir = absdir + extension, relpath = NONE} 479 | SOME p => {relpath = SOME (p + extension), 480 absdir = absdir + extension} 481end 482 483fun extend {base, extension} = 484 extendp {base = base, extension = toString extension} 485 486fun sort l = let 487 fun foldthis1 ({absdir,relpath}, acc) = 488 case Binarymap.peek (acc, absdir) of 489 NONE => Binarymap.insert(acc, absdir, relpath) 490 | SOME NONE => Binarymap.insert(acc, absdir, relpath) 491 | _ => acc 492 val m = foldl foldthis1 (Binarymap.mkDict String.compare) l 493 fun foldthis2 (abs,rel,acc) = {absdir = abs, relpath = rel} :: acc 494in 495 Binarymap.foldr foldthis2 [] m 496end 497 498end (* hmdir struct *) 499 500fun process_hypat_options s = let 501 open Substring 502 val ss = full s 503 fun recurse (noecho, ignore_error, ss) = 504 if noecho andalso ignore_error then 505 {noecho = true, ignore_error = true, 506 command = string (dropl (fn c => c = #"@" orelse c = #"-") ss)} 507 else 508 case getc ss of 509 NONE => {noecho = noecho, ignore_error = ignore_error, 510 command = ""} 511 | SOME (c, ss') => 512 if c = #"@" then recurse (true, ignore_error, ss') 513 else if c = #"-" then recurse (noecho, true, ss') 514 else { noecho = noecho, ignore_error = ignore_error, 515 command = string ss } 516in 517 recurse (false, false, ss) 518end 519 520local 521 fun split p = let val {base, ext} = OS.Path.splitBaseExt p in (base, ext) end 522in 523 fun target_string l = 524 let 525 val (names, e) = ListPair.unzip (List.map split l) 526 val exts = List.mapPartial (fn x => x) e 527 val n = List.length exts 528 in 529 case names of 530 [] => "" 531 | [_] => List.hd l 532 | h :: t => if List.all (fn x => x = h) t andalso List.length e = n 533 then if n = 2 andalso String.isSuffix "Theory" h 534 then h 535 else h ^ ".{" ^ String.concatWith "," exts ^ "}" 536 else String.concatWith " " l 537 end 538end 539 540type include_info = {includes : string list, preincludes : string list } 541 542type include_info = {includes : string list, preincludes : string list} 543type dirset = hmdir.t Binaryset.set 544 545val empty_dirset = Binaryset.empty hmdir.compare 546type incset_pair = {pres : dirset, incs : dirset} 547type incdirmap = (hmdir.t,incset_pair) Binarymap.dict 548val empty_incdirmap = Binarymap.mkDict hmdir.compare 549 550type holmake_dirinfo = { 551 visited : hmdir.t Binaryset.set, 552 incdirmap : incdirmap 553} 554type holmake_result = holmake_dirinfo option 555 556fun find_files ds P = 557 let 558 fun recurse acc = 559 case OS.FileSys.readDir ds of 560 NONE => (OS.FileSys.closeDir ds; List.rev acc) 561 | SOME fname => if P fname then recurse (fname::acc) 562 else recurse acc 563 in 564 recurse [] 565 end 566 567fun generate_all_plausible_targets warn first_target = 568 case first_target of 569 SOME s => [toFile s] 570 | NONE => 571 let 572 val cds = OS.FileSys.openDir "." 573 fun not_a_dot f = not (String.isPrefix "." f) 574 fun ok_file f = 575 case (toFile f) of 576 SIG (Theory _) => false 577 | SIG _ => true 578 | SML (Script s) => 579 (case OS.Path.ext s of 580 SOME "art" => false 581 (* can be generated as temporary by opentheory 582 machinery *) 583 | SOME _ => 584 (warn ("Theory names (e.g., "^f^ 585 ") can't include '.' characters"); 586 false) 587 | NONE => true) 588 | SML (Theory _) => false 589 | SML _ => true 590 | _ => false 591 val src_files = find_files cds (fn s => ok_file s andalso not_a_dot s) 592 fun src_to_target (SIG (Script s)) = UO (Theory s) 593 | src_to_target (SML (Script s)) = UO (Theory s) 594 | src_to_target (SML s) = (UO s) 595 | src_to_target (SIG s) = (UI s) 596 | src_to_target _ = raise Fail "Can't happen" 597 val initially = map (src_to_target o toFile) src_files 598 fun remove_sorted_dups [] = [] 599 | remove_sorted_dups [x] = [x] 600 | remove_sorted_dups (x::y::z) = if x = y then remove_sorted_dups (y::z) 601 else x :: remove_sorted_dups (y::z) 602 in 603 remove_sorted_dups (Listsort.sort file_compare initially) 604 end 605 606(* dependency analysis *) 607exception HolDepFailed 608fun runholdep {ofs, extras, includes, arg, destination} = let 609 val {chatty, diag, warn, ...} : output_functions = ofs 610 val diagK = diag o K 611 val _ = chatty ("Analysing "^fromFile arg) 612 fun buildables s = let 613 val f = toFile s 614 val files = 615 case f of 616 SML (ss as Script t) => [UI ss, UO ss, SML (Theory t), DAT t, 617 SIG (Theory t), UI (Theory t), 618 UO (Theory t), ART (RawArticle t), f] 619 | SML ss => [UI ss, UO ss, f] 620 | SIG ss => [UI ss, f] 621 | ART (RawArticle s) => [ART (ProcessedArticle s), f] 622 | x => [x] 623 in 624 map fromFile files 625 end 626 val buildable_extras = List.concat (map buildables extras) 627 val _ = diagK ("Running Holdep on "^fromFile arg^" with includes = [" ^ 628 String.concatWith ", " includes ^ "], assumes = [" ^ 629 String.concatWith ", " buildable_extras ^"]") 630 val holdep_result = 631 Holdep.main {assumes = buildable_extras, diag = diag, 632 includes = includes, fname = fromFile arg} 633 handle Holdep.Holdep_Error s => 634 (warn ("Holdep failed: "^s); raise HolDepFailed) 635 | e => (warn ("Holdep exception: "^General.exnMessage e); 636 raise HolDepFailed) 637 fun myopen s = 638 if FileSys.access(DEPDIR, []) then 639 if FileSys.isDir DEPDIR then TextIO.openOut s 640 else die_with ("Want to put dependency information in directory "^ 641 DEPDIR^", but it already exists as a file") 642 else 643 (chatty ("Trying to create directory "^DEPDIR^" for dependency files"); 644 FileSys.mkDir DEPDIR; 645 TextIO.openOut s 646 ) 647 open TextIO 648 val outstr = myopen (normPath destination) 649in 650 output(outstr, Holdep.encode_for_HOLMKfile holdep_result); 651 closeOut outstr 652end 653 654(* pull out a list of files that target depends on from depfile. *) 655(* All files on the right of a colon are assumed to be dependencies. 656 This is despite the fact that holdep produces two entries when run 657 on fooScript.sml files, one for fooScript.uo, and another for fooScript 658 itself, we actually want all of those dependencies in one big chunk 659 because the production of fooTheory.{sig,sml} is done as one 660 atomic step from fooScript.sml. *) 661fun first f [] = NONE 662 | first f (x::xs) = case f x of NONE => first f xs | res => res 663 664fun get_dependencies_from_file depfile = let 665 fun get_whole_file s = let 666 open TextIO 667 val instr = openIn (normPath s) 668 in 669 inputAll instr before closeIn instr 670 end 671 fun parse_result s = let 672 val lines = String.fields (fn c => c = #"\n") (collapse_bslash_lines s) 673 fun process_line line = let 674 val (lhs0, rhs0) = Substring.splitl (fn c => c <> #":") 675 (Substring.full line) 676 val lhs = Substring.string lhs0 677 val rhs = Substring.string (Substring.slice(rhs0, 1, NONE)) 678 handle Subscript => "" 679 in 680 realspace_delimited_fields rhs 681 end 682 val result = List.concat (map process_line lines) 683 in 684 List.map toFile result 685 end 686in 687 parse_result (get_whole_file depfile) 688end 689 690(* a function that given a product file, figures out the argument that 691 should be passed to runholdep in order to get back secondary 692 dependencies. *) 693 694fun holdep_arg (UO c) = SOME (SML c) 695 | holdep_arg (UI c) = SOME (SIG c) 696 | holdep_arg (SML (Theory s)) = SOME (SML (Script s)) 697 | holdep_arg (SIG (Theory s)) = SOME (SML (Script s)) 698 | holdep_arg (DAT s) = SOME (SML (Script s)) 699 | holdep_arg (ART (RawArticle s)) = SOME (SML (Script s)) 700 | holdep_arg (ART (ProcessedArticle s)) = SOME (ART (RawArticle s)) 701 | holdep_arg _ = NONE 702 703fun mk_depfile_name DEPDIR s = fullPath [DEPDIR, s^".d"] 704 705infix forces_update_of 706fun (f1 forces_update_of f2) = let 707 open Time 708in 709 FileSys.access(f1, []) andalso 710 (not (FileSys.access(f2, [])) orelse FileSys.modTime f1 > FileSys.modTime f2) 711end 712 713 714fun get_direct_dependencies {incinfo,DEPDIR,output_functions,extra_targets} f = 715let 716 val fname = fromFile f 717 val arg = holdep_arg f (* arg is file to analyse for dependencies *) 718 val {includes,preincludes} = incinfo 719in 720 if isSome arg then let 721 val arg = valOf arg 722 val argname = fromFile arg 723 val depfile = mk_depfile_name DEPDIR argname 724 val _ = 725 if argname forces_update_of depfile then 726 runholdep {ofs = output_functions, extras = extra_targets, 727 includes = preincludes @ includes, arg = arg, 728 destination = depfile} 729 else () 730 val phase1 = 731 (* circumstances can arise in which the dependency file won't be 732 built, and won't exist; mainly because the file we're trying to 733 compute dependencies for doesn't exist either. In this case, we 734 can only return the empty list *) 735 if exists_readable depfile then 736 get_dependencies_from_file depfile 737 else 738 [] 739 in 740 case f of 741 UO (Theory s) => UI (Theory s) :: DAT s :: phase1 742 | UO x => 743 if FileSys.access(fromFile (SIG x), []) andalso 744 List.all (fn f => f <> SIG x) phase1 745 then 746 UI x :: phase1 747 else 748 phase1 749 | _ => phase1 750 end 751 else 752 [] 753end 754 755 756 757end (* struct *) 758