Mercurial > hg > sonic-visualiser
comparison vext.sml @ 1772:128c4544036d
Update Vext
author | Chris Cannam |
---|---|
date | Fri, 09 Mar 2018 09:00:48 +0000 |
parents | 762ef5d2722a |
children | 316c4fd7e7bc |
comparison
equal
deleted
inserted
replaced
1771:bd14a0f69b60 | 1772:128c4544036d |
---|---|
36 shall not be used in advertising or otherwise to promote the sale, | 36 shall not be used in advertising or otherwise to promote the sale, |
37 use or other dealings in this Software without prior written | 37 use or other dealings in this Software without prior written |
38 authorization. | 38 authorization. |
39 *) | 39 *) |
40 | 40 |
41 val vext_version = "0.9.95" | 41 val vext_version = "0.9.97" |
42 | 42 |
43 | 43 |
44 datatype vcs = | 44 datatype vcs = |
45 HG | | 45 HG | |
46 GIT | | 46 GIT | |
140 val archive_dir = ".vext-archive" | 140 val archive_dir = ".vext-archive" |
141 end | 141 end |
142 | 142 |
143 signature VCS_CONTROL = sig | 143 signature VCS_CONTROL = sig |
144 | 144 |
145 (** Check whether the given VCS is installed and working *) | |
146 val is_working : context -> bool result | |
147 | |
145 (** Test whether the library is present locally at all *) | 148 (** Test whether the library is present locally at all *) |
146 val exists : context -> libname -> bool result | 149 val exists : context -> libname -> bool result |
147 | 150 |
148 (** Return the id (hash) of the current revision for the library *) | 151 (** Return the id (hash) of the current revision for the library *) |
149 val id_of : context -> libname -> id_or_tag result | 152 val id_of : context -> libname -> id_or_tag result |
192 signature LIB_CONTROL = sig | 195 signature LIB_CONTROL = sig |
193 val review : context -> libspec -> (libstate * localstate) result | 196 val review : context -> libspec -> (libstate * localstate) result |
194 val status : context -> libspec -> (libstate * localstate) result | 197 val status : context -> libspec -> (libstate * localstate) result |
195 val update : context -> libspec -> unit result | 198 val update : context -> libspec -> unit result |
196 val id_of : context -> libspec -> id_or_tag result | 199 val id_of : context -> libspec -> id_or_tag result |
200 val is_working : context -> vcs -> bool result | |
197 end | 201 end |
198 | 202 |
199 structure FileBits :> sig | 203 structure FileBits :> sig |
200 val extpath : context -> string | 204 val extpath : context -> string |
201 val libpath : context -> libname -> string | 205 val libpath : context -> libname -> string |
578 | OK () => update' () | 582 | OK () => update' () |
579 end | 583 end |
580 | 584 |
581 fun id_of context ({ libname, ... } : libspec) = | 585 fun id_of context ({ libname, ... } : libspec) = |
582 V.id_of context libname | 586 V.id_of context libname |
587 | |
588 fun is_working context vcs = | |
589 V.is_working context | |
583 | 590 |
584 end | 591 end |
585 | 592 |
586 (* Simple Standard ML JSON parser | 593 (* Simple Standard ML JSON parser |
587 https://bitbucket.org/cannam/sml-simplejson | 594 https://bitbucket.org/cannam/sml-simplejson |
1160 given in the project file changes. *) | 1167 given in the project file changes. *) |
1161 | 1168 |
1162 type vcsstate = { id: string, modified: bool, | 1169 type vcsstate = { id: string, modified: bool, |
1163 branch: string, tags: string list } | 1170 branch: string, tags: string list } |
1164 | 1171 |
1172 val hg_program = "hg" | |
1173 | |
1165 val hg_args = [ "--config", "ui.interactive=true", | 1174 val hg_args = [ "--config", "ui.interactive=true", |
1166 "--config", "ui.merge=:merge" ] | 1175 "--config", "ui.merge=:merge" ] |
1167 | 1176 |
1168 fun hg_command context libname args = | 1177 fun hg_command context libname args = |
1169 FileBits.command context libname ("hg" :: hg_args @ args) | 1178 FileBits.command context libname (hg_program :: hg_args @ args) |
1170 | 1179 |
1171 fun hg_command_output context libname args = | 1180 fun hg_command_output context libname args = |
1172 FileBits.command_output context libname ("hg" :: hg_args @ args) | 1181 FileBits.command_output context libname (hg_program :: hg_args @ args) |
1173 | 1182 |
1183 fun is_working context = | |
1184 case hg_command_output context "" ["--version"] of | |
1185 OK "" => OK false | |
1186 | OK _ => OK true | |
1187 | ERROR e => ERROR e | |
1188 | |
1174 fun exists context libname = | 1189 fun exists context libname = |
1175 OK (OS.FileSys.isDir (FileBits.subpath context libname ".hg")) | 1190 OK (OS.FileSys.isDir (FileBits.subpath context libname ".hg")) |
1176 handle _ => OK false | 1191 handle _ => OK false |
1177 | 1192 |
1178 fun remote_for context (libname, source) = | 1193 fun remote_for context (libname, source) = |
1311 (vext/master). The remote we use is always named vext, and we | 1326 (vext/master). The remote we use is always named vext, and we |
1312 update it to the expected URL each time we fetch, in order to | 1327 update it to the expected URL each time we fetch, in order to |
1313 ensure we update properly if the location given in the project | 1328 ensure we update properly if the location given in the project |
1314 file changes. The origin remote is unused. *) | 1329 file changes. The origin remote is unused. *) |
1315 | 1330 |
1331 val git_program = "git" | |
1332 | |
1316 fun git_command context libname args = | 1333 fun git_command context libname args = |
1317 FileBits.command context libname ("git" :: args) | 1334 FileBits.command context libname (git_program :: args) |
1318 | 1335 |
1319 fun git_command_output context libname args = | 1336 fun git_command_output context libname args = |
1320 FileBits.command_output context libname ("git" :: args) | 1337 FileBits.command_output context libname (git_program :: args) |
1338 | |
1339 fun is_working context = | |
1340 case git_command_output context "" ["--version"] of | |
1341 OK "" => OK false | |
1342 | OK _ => OK true | |
1343 | ERROR e => ERROR e | |
1321 | 1344 |
1322 fun exists context libname = | 1345 fun exists context libname = |
1323 OK (OS.FileSys.isDir (FileBits.subpath context libname ".git")) | 1346 OK (OS.FileSys.isDir (FileBits.subpath context libname ".git")) |
1324 handle _ => OK false | 1347 handle _ => OK false |
1325 | 1348 |
1495 OK (FileBits.file_url (FileBits.libpath context libname)) | 1518 OK (FileBits.file_url (FileBits.libpath context libname)) |
1496 | 1519 |
1497 end | 1520 end |
1498 | 1521 |
1499 (* SubXml - A parser for a subset of XML | 1522 (* SubXml - A parser for a subset of XML |
1500 https://bitbucket.org/cannam/sml-simplexml | 1523 https://bitbucket.org/cannam/sml-subxml |
1501 Copyright 2018 Chris Cannam. BSD licence. | 1524 Copyright 2018 Chris Cannam. BSD licence. |
1502 *) | 1525 *) |
1503 | 1526 |
1504 signature SUBXML = sig | 1527 signature SUBXML = sig |
1505 | 1528 |
1832 end | 1855 end |
1833 | 1856 |
1834 | 1857 |
1835 structure SvnControl :> VCS_CONTROL = struct | 1858 structure SvnControl :> VCS_CONTROL = struct |
1836 | 1859 |
1860 val svn_program = "svn" | |
1861 | |
1837 fun svn_command context libname args = | 1862 fun svn_command context libname args = |
1838 FileBits.command context libname ("svn" :: args) | 1863 FileBits.command context libname (svn_program :: args) |
1839 | 1864 |
1840 fun svn_command_output context libname args = | 1865 fun svn_command_output context libname args = |
1841 FileBits.command_output context libname ("svn" :: args) | 1866 FileBits.command_output context libname (svn_program :: args) |
1842 | 1867 |
1843 fun svn_command_lines context libname args = | 1868 fun svn_command_lines context libname args = |
1844 case svn_command_output context libname args of | 1869 case svn_command_output context libname args of |
1845 ERROR e => ERROR e | 1870 ERROR e => ERROR e |
1846 | OK s => OK (String.tokens (fn c => c = #"\n" orelse c = #"\r") s) | 1871 | OK s => OK (String.tokens (fn c => c = #"\n" orelse c = #"\r") s) |
1853 case String.tokens (fn c => c = #":") line of | 1878 case String.tokens (fn c => c = #":") line of |
1854 [] => ("", "") | 1879 [] => ("", "") |
1855 | first::rest => | 1880 | first::rest => |
1856 (first, strip_leading_ws (String.concatWith ":" rest)) | 1881 (first, strip_leading_ws (String.concatWith ":" rest)) |
1857 end | 1882 end |
1883 | |
1884 fun is_working context = | |
1885 case svn_command_output context "" ["--version"] of | |
1886 OK "" => OK false | |
1887 | OK _ => OK true | |
1888 | ERROR e => ERROR e | |
1858 | 1889 |
1859 structure X = SubXml | 1890 structure X = SubXml |
1860 | 1891 |
1861 fun svn_info context libname route = | 1892 fun svn_info context libname route = |
1862 (* SVN 1.9 has info --show-item which is just what we need, | 1893 (* SVN 1.9 has info --show-item which is just what we need, |
2011 fun update context (spec as { vcs, ... } : libspec) = | 2042 fun update context (spec as { vcs, ... } : libspec) = |
2012 (fn HG => H.update | GIT => G.update | SVN => S.update) vcs context spec | 2043 (fn HG => H.update | GIT => G.update | SVN => S.update) vcs context spec |
2013 | 2044 |
2014 fun id_of context (spec as { vcs, ... } : libspec) = | 2045 fun id_of context (spec as { vcs, ... } : libspec) = |
2015 (fn HG => H.id_of | GIT => G.id_of | SVN => S.id_of) vcs context spec | 2046 (fn HG => H.id_of | GIT => G.id_of | SVN => S.id_of) vcs context spec |
2047 | |
2048 fun is_working context vcs = | |
2049 (fn HG => H.is_working | GIT => G.is_working | SVN => S.is_working) | |
2050 vcs context vcs | |
2016 | 2051 |
2017 end | 2052 end |
2018 | 2053 |
2019 | 2054 |
2020 type exclusions = string list | 2055 type exclusions = string list |
2415 "Notes" ^ "\n " ^ | 2450 "Notes" ^ "\n " ^ |
2416 hline_to libname_width ^ "-+-" ^ | 2451 hline_to libname_width ^ "-+-" ^ |
2417 hline_to libstate_width ^ "-+-" ^ | 2452 hline_to libstate_width ^ "-+-" ^ |
2418 hline_to notes_width ^ "\n") | 2453 hline_to notes_width ^ "\n") |
2419 | 2454 |
2420 fun print_status with_network (libname, status) = | 2455 fun print_status with_network (lib : libspec, status) = |
2421 let val libstate_str = | 2456 let val libstate_str = |
2422 case status of | 2457 case status of |
2423 OK (ABSENT, _) => "Absent" | 2458 OK (ABSENT, _) => "Absent" |
2424 | OK (CORRECT, _) => if with_network then "Correct" else "Present" | 2459 | OK (CORRECT, _) => if with_network then "Correct" else "Present" |
2425 | OK (SUPERSEDED, _) => "Superseded" | 2460 | OK (SUPERSEDED, _) => "Superseded" |
2435 case status of | 2470 case status of |
2436 ERROR e => e | 2471 ERROR e => e |
2437 | _ => "" | 2472 | _ => "" |
2438 in | 2473 in |
2439 print (" " ^ | 2474 print (" " ^ |
2440 pad_to libname_width libname ^ divider ^ | 2475 pad_to libname_width (#libname lib) ^ divider ^ |
2441 pad_to libstate_width libstate_str ^ divider ^ | 2476 pad_to libstate_width libstate_str ^ divider ^ |
2442 pad_to localstate_width localstate_str ^ divider ^ | 2477 pad_to localstate_width localstate_str ^ divider ^ |
2443 error_str ^ "\n") | 2478 error_str ^ "\n") |
2444 end | 2479 end |
2445 | 2480 |
2446 fun print_update_outcome (libname, outcome) = | 2481 fun print_update_outcome (lib : libspec, outcome) = |
2447 let val outcome_str = | 2482 let val outcome_str = |
2448 case outcome of | 2483 case outcome of |
2449 OK id => "Ok" | 2484 OK id => "Ok" |
2450 | ERROR e => "Failed" | 2485 | ERROR e => "Failed" |
2451 val error_str = | 2486 val error_str = |
2452 case outcome of | 2487 case outcome of |
2453 ERROR e => e | 2488 ERROR e => e |
2454 | _ => "" | 2489 | _ => "" |
2455 in | 2490 in |
2456 print (" " ^ | 2491 print (" " ^ |
2457 pad_to libname_width libname ^ divider ^ | 2492 pad_to libname_width (#libname lib) ^ divider ^ |
2458 pad_to libstate_width outcome_str ^ divider ^ | 2493 pad_to libstate_width outcome_str ^ divider ^ |
2459 error_str ^ "\n") | 2494 error_str ^ "\n") |
2460 end | 2495 end |
2461 | 2496 |
2462 fun act_and_print action print_header print_line (libs : libspec list) = | 2497 fun vcs_name HG = ("Mercurial", "hg") |
2463 let val lines = map (fn lib => (#libname lib, action lib)) libs | 2498 | vcs_name GIT = ("Git", "git") |
2499 | vcs_name SVN = ("Subversion", "svn") | |
2500 | |
2501 fun print_problem_summary context lines = | |
2502 let val failed_vcs = | |
2503 foldl (fn (({ vcs, ... } : libspec, ERROR _), acc) => vcs::acc | |
2504 | (_, acc) => acc) [] lines | |
2505 fun report_nonworking vcs error = | |
2506 print ((if error = "" then "" else error ^ "\n\n") ^ | |
2507 "Error: The project uses the " ^ (#1 (vcs_name vcs)) ^ | |
2508 " version control system, but its\n" ^ | |
2509 "executable program (" ^ (#2 (vcs_name vcs)) ^ | |
2510 ") does not appear to be installed in the program path\n\n") | |
2511 fun check_working [] checked = () | |
2512 | check_working (vcs::rest) checked = | |
2513 if List.exists (fn v => vcs = v) checked | |
2514 then check_working rest checked | |
2515 else | |
2516 case AnyLibControl.is_working context vcs of | |
2517 OK true => check_working rest checked | |
2518 | OK false => (report_nonworking vcs ""; | |
2519 check_working rest (vcs::checked)) | |
2520 | ERROR e => (report_nonworking vcs e; | |
2521 check_working rest (vcs::checked)) | |
2522 in | |
2523 print "\nError: Some operations failed\n\n"; | |
2524 check_working failed_vcs [] | |
2525 end | |
2526 | |
2527 fun act_and_print action print_header print_line context (libs : libspec list) = | |
2528 let val lines = map (fn lib => (lib, action lib)) libs | |
2529 val imperfect = List.exists (fn (_, ERROR _) => true | _ => false) lines | |
2464 val _ = print_header () | 2530 val _ = print_header () |
2465 in | 2531 in |
2466 app print_line lines; | 2532 app print_line lines; |
2533 if imperfect then print_problem_summary context lines else (); | |
2467 lines | 2534 lines |
2468 end | 2535 end |
2469 | 2536 |
2470 fun return_code_for outcomes = | 2537 fun return_code_for outcomes = |
2471 foldl (fn ((_, result), acc) => | 2538 foldl (fn ((_, result), acc) => |
2476 outcomes | 2543 outcomes |
2477 | 2544 |
2478 fun status_of_project ({ context, libs } : project) = | 2545 fun status_of_project ({ context, libs } : project) = |
2479 return_code_for (act_and_print (AnyLibControl.status context) | 2546 return_code_for (act_and_print (AnyLibControl.status context) |
2480 print_status_header (print_status false) | 2547 print_status_header (print_status false) |
2481 libs) | 2548 context libs) |
2482 | 2549 |
2483 fun review_project ({ context, libs } : project) = | 2550 fun review_project ({ context, libs } : project) = |
2484 return_code_for (act_and_print (AnyLibControl.review context) | 2551 return_code_for (act_and_print (AnyLibControl.review context) |
2485 print_status_header (print_status true) | 2552 print_status_header (print_status true) |
2486 libs) | 2553 context libs) |
2487 | 2554 |
2488 fun lock_project ({ context, libs } : project) = | 2555 fun lock_project ({ context, libs } : project) = |
2489 let val _ = if FileBits.verbose () | 2556 let val _ = if FileBits.verbose () |
2490 then print ("Scanning IDs for lock file...\n") | 2557 then print ("Scanning IDs for lock file...\n") |
2491 else () | 2558 else () |
2492 val outcomes = map (fn lib => | 2559 val outcomes = map (fn lib => (lib, AnyLibControl.id_of context lib)) |
2493 (#libname lib, AnyLibControl.id_of context lib)) | |
2494 libs | 2560 libs |
2495 val locks = | 2561 val locks = |
2496 List.concat | 2562 List.concat |
2497 (map (fn (libname, result) => | 2563 (map (fn (lib : libspec, result) => |
2498 case result of | 2564 case result of |
2499 ERROR _ => [] | 2565 ERROR _ => [] |
2500 | OK id => [{ libname = libname, id_or_tag = id }]) | 2566 | OK id => [{ libname = #libname lib, |
2567 id_or_tag = id }]) | |
2501 outcomes) | 2568 outcomes) |
2502 val return_code = return_code_for outcomes | 2569 val return_code = return_code_for outcomes |
2503 val _ = print clear_line | 2570 val _ = print clear_line |
2504 in | 2571 in |
2505 if OS.Process.isSuccess return_code | 2572 if OS.Process.isSuccess return_code |
2509 end | 2576 end |
2510 | 2577 |
2511 fun update_project (project as { context, libs }) = | 2578 fun update_project (project as { context, libs }) = |
2512 let val outcomes = act_and_print | 2579 let val outcomes = act_and_print |
2513 (AnyLibControl.update context) | 2580 (AnyLibControl.update context) |
2514 print_outcome_header print_update_outcome libs | 2581 print_outcome_header print_update_outcome |
2582 context libs | |
2515 val _ = if List.exists (fn (_, OK _) => true | _ => false) outcomes | 2583 val _ = if List.exists (fn (_, OK _) => true | _ => false) outcomes |
2516 then lock_project project | 2584 then lock_project project |
2517 else OS.Process.success | 2585 else OS.Process.success |
2518 in | 2586 in |
2519 return_code_for outcomes | 2587 return_code_for outcomes |
2526 load_project userconfig rootpath pintype | 2594 load_project userconfig rootpath pintype |
2527 end | 2595 end |
2528 | 2596 |
2529 fun with_local_project pintype f = | 2597 fun with_local_project pintype f = |
2530 let val return_code = f (load_local_project pintype) | 2598 let val return_code = f (load_local_project pintype) |
2599 handle Fail msg => (print ("Error: " ^ msg); | |
2600 OS.Process.failure) | |
2531 handle e => (print ("Error: " ^ exnMessage e); | 2601 handle e => (print ("Error: " ^ exnMessage e); |
2532 OS.Process.failure) | 2602 OS.Process.failure) |
2533 val _ = print "\n"; | 2603 val _ = print "\n"; |
2534 in | 2604 in |
2535 return_code | 2605 return_code |
2578 | ["install"] => install () | 2648 | ["install"] => install () |
2579 | ["update"] => update () | 2649 | ["update"] => update () |
2580 | ["lock"] => lock () | 2650 | ["lock"] => lock () |
2581 | ["version"] => version () | 2651 | ["version"] => version () |
2582 | "archive"::target::args => archive target args | 2652 | "archive"::target::args => archive target args |
2653 | arg::_ => (print ("Error: unknown argument \"" ^ arg ^ "\"\n"); | |
2654 usage ()) | |
2583 | _ => usage () | 2655 | _ => usage () |
2584 in | 2656 in |
2585 OS.Process.exit return_code; | 2657 OS.Process.exit return_code; |
2586 () | 2658 () |
2587 end | 2659 end |