Mercurial > hg > sonic-visualiser
comparison vext.sml @ 1756:edaa018a3731
Update Vext
author | Chris Cannam |
---|---|
date | Mon, 11 Dec 2017 08:13:32 +0000 |
parents | 716e13004b19 |
children | 42d57c382e56 |
comparison
equal
deleted
inserted
replaced
1755:c866f9a77b23 | 1756:edaa018a3731 |
---|---|
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.91" | 41 val vext_version = "0.9.92" |
42 | 42 |
43 | 43 |
44 datatype vcs = | 44 datatype vcs = |
45 HG | | 45 HG | |
46 GIT | 46 GIT | |
47 SVN | |
47 | 48 |
48 datatype source = | 49 datatype source = |
49 URL_SOURCE of string | | 50 URL_SOURCE of string | |
50 SERVICE_SOURCE of { | 51 SERVICE_SOURCE of { |
51 service : string, | 52 service : string, |
172 | 173 |
173 (** Check out, i.e. clone a fresh copy of, the repo for the given | 174 (** Check out, i.e. clone a fresh copy of, the repo for the given |
174 library on the given branch *) | 175 library on the given branch *) |
175 val checkout : context -> libname * source * branch -> unit result | 176 val checkout : context -> libname * source * branch -> unit result |
176 | 177 |
177 (** Update the library to the given branch tip *) | 178 (** Update the library to the given branch tip. Assumes that a |
179 local copy of the library already exists. Return the new id *) | |
178 val update : context -> libname * source * branch -> id_or_tag result | 180 val update : context -> libname * source * branch -> id_or_tag result |
179 | 181 |
180 (** Update the library to the given specific id or tag *) | 182 (** Update the library to the given specific id or tag *) |
181 val update_to : context -> libname * source * id_or_tag -> id_or_tag result | 183 val update_to : context -> libname * source * id_or_tag -> id_or_tag result |
184 | |
185 (** Return a URL from which the library can be cloned, given that | |
186 the local copy already exists. For a DVCS this can be the | |
187 local copy, but for a centralised VCS it will have to be the | |
188 remote repository URL. Used for archiving *) | |
189 val copy_url_for : context -> libname -> string result | |
182 end | 190 end |
183 | 191 |
184 signature LIB_CONTROL = sig | 192 signature LIB_CONTROL = sig |
185 val review : context -> libspec -> (libstate * localstate) result | 193 val review : context -> libspec -> (libstate * localstate) result |
186 val status : context -> libspec -> (libstate * localstate) result | 194 val status : context -> libspec -> (libstate * localstate) result |
192 val extpath : context -> string | 200 val extpath : context -> string |
193 val libpath : context -> libname -> string | 201 val libpath : context -> libname -> string |
194 val subpath : context -> libname -> string -> string | 202 val subpath : context -> libname -> string -> string |
195 val command_output : context -> libname -> string list -> string result | 203 val command_output : context -> libname -> string list -> string result |
196 val command : context -> libname -> string list -> unit result | 204 val command : context -> libname -> string list -> unit result |
205 val file_url : string -> string | |
197 val file_contents : string -> string | 206 val file_contents : string -> string |
198 val mydir : unit -> string | 207 val mydir : unit -> string |
199 val homedir : unit -> string | 208 val homedir : unit -> string |
200 val mkpath : string -> unit result | 209 val mkpath : string -> unit result |
201 val rmpath : string -> unit result | 210 val rmpath : string -> unit result |
211 val nonempty_dir_exists : string -> bool | |
202 val project_spec_path : string -> string | 212 val project_spec_path : string -> string |
203 val project_lock_path : string -> string | 213 val project_lock_path : string -> string |
204 val verbose : unit -> bool | 214 val verbose : unit -> bool |
205 end = struct | 215 end = struct |
206 | 216 |
258 fun project_lock_path rootpath = | 268 fun project_lock_path rootpath = |
259 project_file_path rootpath (VextFilenames.project_lock_file) | 269 project_file_path rootpath (VextFilenames.project_lock_file) |
260 | 270 |
261 fun trim str = | 271 fun trim str = |
262 hd (String.fields (fn x => x = #"\n" orelse x = #"\r") str) | 272 hd (String.fields (fn x => x = #"\n" orelse x = #"\r") str) |
273 | |
274 fun file_url path = | |
275 let val forward_path = | |
276 String.translate (fn #"\\" => "/" | | |
277 c => Char.toString c) | |
278 (OS.Path.mkCanonical path) | |
279 in | |
280 (* Path is expected to be absolute already, but if it | |
281 starts with a drive letter, we'll need an extra slash *) | |
282 case explode forward_path of | |
283 #"/"::rest => "file:///" ^ implode rest | |
284 | _ => "file:///" ^ forward_path | |
285 end | |
263 | 286 |
264 fun file_contents filename = | 287 fun file_contents filename = |
265 let val stream = TextIO.openIn filename | 288 let val stream = TextIO.openIn filename |
266 fun read_all str acc = | 289 fun read_all str acc = |
267 case TextIO.inputLine str of | 290 case TextIO.inputLine str of |
348 fun command_output context libname cmdlist = | 371 fun command_output context libname cmdlist = |
349 let open OS | 372 let open OS |
350 val tmpFile = FileSys.tmpName () | 373 val tmpFile = FileSys.tmpName () |
351 val result = run_command context libname cmdlist (SOME tmpFile) | 374 val result = run_command context libname cmdlist (SOME tmpFile) |
352 val contents = file_contents tmpFile | 375 val contents = file_contents tmpFile |
376 val _ = if verbose () | |
377 then print ("Output was:\n\"" ^ contents ^ "\"\n") | |
378 else () | |
353 in | 379 in |
354 FileSys.remove tmpFile handle _ => (); | 380 FileSys.remove tmpFile handle _ => (); |
355 case result of | 381 case result of |
356 OK () => OK contents | 382 OK () => OK contents |
357 | ERROR e => ERROR e | 383 | ERROR e => ERROR e |
393 ERROR ("Directory creation failed: " ^ e)) | 419 ERROR ("Directory creation failed: " ^ e)) |
394 | 420 |
395 fun mkpath path = | 421 fun mkpath path = |
396 mkpath' (OS.Path.mkCanonical path) | 422 mkpath' (OS.Path.mkCanonical path) |
397 | 423 |
398 fun rmpath' path = | 424 fun dir_contents dir = |
399 let open OS | 425 let open OS |
400 fun files_from dirstream = | 426 fun files_from dirstream = |
401 case FileSys.readDir dirstream of | 427 case FileSys.readDir dirstream of |
402 NONE => [] | 428 NONE => [] |
403 | SOME file => | 429 | SOME file => |
404 (* readDir is supposed to filter these, | 430 (* readDir is supposed to filter these, |
405 but let's be extra cautious: *) | 431 but let's be extra cautious: *) |
406 if file = Path.parentArc orelse file = Path.currentArc | 432 if file = Path.parentArc orelse file = Path.currentArc |
407 then files_from dirstream | 433 then files_from dirstream |
408 else file :: files_from dirstream | 434 else file :: files_from dirstream |
409 fun contents dir = | 435 val stream = FileSys.openDir dir |
410 let val stream = FileSys.openDir dir | 436 val files = map (fn f => Path.joinDirFile |
411 val files = map (fn f => Path.joinDirFile | 437 { dir = dir, file = f }) |
412 { dir = dir, file = f }) | 438 (files_from stream) |
413 (files_from stream) | 439 val _ = FileSys.closeDir stream |
414 val _ = FileSys.closeDir stream | 440 in |
415 in files | 441 files |
416 end | 442 end |
443 | |
444 fun rmpath' path = | |
445 let open OS | |
417 fun remove path = | 446 fun remove path = |
418 if FileSys.isLink path (* dangling links bother isDir *) | 447 if FileSys.isLink path (* dangling links bother isDir *) |
419 then FileSys.remove path | 448 then FileSys.remove path |
420 else if FileSys.isDir path | 449 else if FileSys.isDir path |
421 then (app remove (contents path); FileSys.rmDir path) | 450 then (app remove (dir_contents path); FileSys.rmDir path) |
422 else FileSys.remove path | 451 else FileSys.remove path |
423 in | 452 in |
424 (remove path; OK ()) | 453 (remove path; OK ()) |
425 handle SysErr (e, _) => ERROR ("Path removal failed: " ^ e) | 454 handle SysErr (e, _) => ERROR ("Path removal failed: " ^ e) |
426 end | 455 end |
427 | 456 |
428 fun rmpath path = | 457 fun rmpath path = |
429 rmpath' (OS.Path.mkCanonical path) | 458 rmpath' (OS.Path.mkCanonical path) |
430 | 459 |
460 fun nonempty_dir_exists path = | |
461 let open OS.FileSys | |
462 in | |
463 (not (isLink path) andalso | |
464 isDir path andalso | |
465 dir_contents path <> []) | |
466 handle _ => false | |
467 end | |
468 | |
431 end | 469 end |
432 | 470 |
433 functor LibControlFn (V: VCS_CONTROL) :> LIB_CONTROL = struct | 471 functor LibControlFn (V: VCS_CONTROL) :> LIB_CONTROL = struct |
434 | 472 |
435 (* Valid states for unpinned libraries: | 473 (* Valid states for unpinned libraries: |
1037 } | 1075 } |
1038 } | 1076 } |
1039 ] | 1077 ] |
1040 | 1078 |
1041 fun vcs_name vcs = | 1079 fun vcs_name vcs = |
1042 case vcs of GIT => "git" | | 1080 case vcs of HG => "hg" |
1043 HG => "hg" | 1081 | GIT => "git" |
1082 | SVN => "svn" | |
1044 | 1083 |
1045 fun vcs_from_name name = | 1084 fun vcs_from_name name = |
1046 case name of "git" => GIT | 1085 case name of "hg" => HG |
1047 | "hg" => HG | 1086 | "git" => GIT |
1087 | "svn" => SVN | |
1048 | other => raise Fail ("Unknown vcs name \"" ^ name ^ "\"") | 1088 | other => raise Fail ("Unknown vcs name \"" ^ name ^ "\"") |
1049 | 1089 |
1050 fun load_more_providers previously_loaded json = | 1090 fun load_more_providers previously_loaded json = |
1051 let open JsonBits | 1091 let open JsonBits |
1052 fun load pjson pname : provider = | 1092 fun load pjson pname : provider = |
1173 given in the project file changes. *) | 1213 given in the project file changes. *) |
1174 | 1214 |
1175 type vcsstate = { id: string, modified: bool, | 1215 type vcsstate = { id: string, modified: bool, |
1176 branch: string, tags: string list } | 1216 branch: string, tags: string list } |
1177 | 1217 |
1178 val hg_args = [ "--config", "ui.interactive=true" ] | 1218 val hg_args = [ "--config", "ui.interactive=true", |
1219 "--config", "ui.merge=:merge" ] | |
1179 | 1220 |
1180 fun hg_command context libname args = | 1221 fun hg_command context libname args = |
1181 FileBits.command context libname ("hg" :: hg_args @ args) | 1222 FileBits.command context libname ("hg" :: hg_args @ args) |
1182 | 1223 |
1183 fun hg_command_output context libname args = | 1224 fun hg_command_output context libname args = |
1308 | ERROR e => | 1349 | ERROR e => |
1309 case pull_result of | 1350 case pull_result of |
1310 ERROR e' => ERROR e' (* this was the ur-error *) | 1351 ERROR e' => ERROR e' (* this was the ur-error *) |
1311 | _ => ERROR e | 1352 | _ => ERROR e |
1312 end | 1353 end |
1313 | 1354 |
1355 fun copy_url_for context libname = | |
1356 OK (FileBits.file_url (FileBits.libpath context libname)) | |
1357 | |
1314 end | 1358 end |
1315 | 1359 |
1316 structure GitControl :> VCS_CONTROL = struct | 1360 structure GitControl :> VCS_CONTROL = struct |
1317 | 1361 |
1318 (* With Git repos we always operate in detached HEAD state. Even | 1362 (* With Git repos we always operate in detached HEAD state. Even |
1483 | ERROR e => | 1527 | ERROR e => |
1484 case fetch_result of | 1528 case fetch_result of |
1485 ERROR e' => ERROR e' (* this was the ur-error *) | 1529 ERROR e' => ERROR e' (* this was the ur-error *) |
1486 | _ => ERROR e | 1530 | _ => ERROR e |
1487 end | 1531 end |
1532 | |
1533 fun copy_url_for context libname = | |
1534 OK (FileBits.file_url (FileBits.libpath context libname)) | |
1488 | 1535 |
1489 end | 1536 end |
1490 | 1537 |
1538 structure SvnControl :> VCS_CONTROL = struct | |
1539 | |
1540 fun svn_command context libname args = | |
1541 FileBits.command context libname ("svn" :: args) | |
1542 | |
1543 fun svn_command_output context libname args = | |
1544 FileBits.command_output context libname ("svn" :: args) | |
1545 | |
1546 fun svn_command_lines context libname args = | |
1547 case svn_command_output context libname args of | |
1548 ERROR e => ERROR e | |
1549 | OK s => OK (String.tokens (fn c => c = #"\n" orelse c = #"\r") s) | |
1550 | |
1551 fun split_line_pair line = | |
1552 let fun strip_leading_ws str = case explode str of | |
1553 #" "::rest => implode rest | |
1554 | _ => str | |
1555 in | |
1556 case String.tokens (fn c => c = #":") line of | |
1557 [] => ("", "") | |
1558 | first::rest => | |
1559 (first, strip_leading_ws (String.concatWith ":" rest)) | |
1560 end | |
1561 | |
1562 fun svn_info_item context libname key = | |
1563 (* SVN 1.9 has info --show-item which is what we need, but at | |
1564 this point we still have 1.8 on the CI boxes so we might as | |
1565 well aim to support it *) | |
1566 case svn_command_lines context libname ["info"] of | |
1567 ERROR e => ERROR e | |
1568 | OK lines => | |
1569 case List.find (fn (k, v) => k = key) (map split_line_pair lines) of | |
1570 NONE => ERROR ("Key \"" ^ key ^ "\" not found in output") | |
1571 | SOME (_, v) => OK v | |
1572 | |
1573 fun exists context libname = | |
1574 OK (OS.FileSys.isDir (FileBits.subpath context libname ".svn")) | |
1575 handle _ => OK false | |
1576 | |
1577 fun remote_for context (libname, source) = | |
1578 Provider.remote_url context SVN source libname | |
1579 | |
1580 fun id_of context libname = | |
1581 svn_info_item context libname "Revision" (*!!! check: does svn localise this? should we ensure C locale? *) | |
1582 | |
1583 fun is_at context (libname, id_or_tag) = | |
1584 case id_of context libname of | |
1585 ERROR e => ERROR e | |
1586 | OK id => OK (id = id_or_tag) | |
1587 | |
1588 fun is_on_branch context (libname, b) = | |
1589 OK (b = DEFAULT_BRANCH) | |
1590 | |
1591 fun is_newest context (libname, source, branch) = | |
1592 case svn_command_lines context libname ["status", "--show-updates"] of | |
1593 ERROR e => ERROR e | |
1594 | OK lines => | |
1595 case rev lines of | |
1596 [] => ERROR "No result returned for server status" | |
1597 | last_line::_ => | |
1598 case rev (String.tokens (fn c => c = #" ") last_line) of | |
1599 [] => ERROR "No revision field found in server status" | |
1600 | server_id::_ => is_at context (libname, server_id) | |
1601 | |
1602 fun is_newest_locally context (libname, branch) = | |
1603 OK true (* no local history *) | |
1604 | |
1605 fun is_modified_locally context libname = | |
1606 case svn_command_output context libname ["status"] of | |
1607 ERROR e => ERROR e | |
1608 | OK "" => OK false | |
1609 | OK _ => OK true | |
1610 | |
1611 fun checkout context (libname, source, branch) = | |
1612 let val url = remote_for context (libname, source) | |
1613 val path = FileBits.libpath context libname | |
1614 in | |
1615 if FileBits.nonempty_dir_exists path | |
1616 then (* Surprisingly, SVN itself has no problem with | |
1617 this. But for consistency with other VCSes we | |
1618 don't allow it *) | |
1619 ERROR ("Refusing checkout to nonempty dir \"" ^ path ^ "\"") | |
1620 else | |
1621 (* make the lib dir rather than just the ext dir, since | |
1622 the lib dir might be nested and svn will happily check | |
1623 out into an existing empty dir anyway *) | |
1624 case FileBits.mkpath (FileBits.libpath context libname) of | |
1625 ERROR e => ERROR e | |
1626 | _ => svn_command context "" ["checkout", url, libname] | |
1627 end | |
1628 | |
1629 fun update context (libname, source, branch) = | |
1630 case svn_command context libname | |
1631 ["update", "--accept", "postpone"] of | |
1632 ERROR e => ERROR e | |
1633 | _ => id_of context libname | |
1634 | |
1635 fun update_to context (libname, _, "") = | |
1636 ERROR "Non-empty id (tag or revision id) required for update_to" | |
1637 | update_to context (libname, source, id) = | |
1638 case svn_command context libname | |
1639 ["update", "-r", id, "--accept", "postpone"] of | |
1640 ERROR e => ERROR e | |
1641 | OK _ => id_of context libname | |
1642 | |
1643 fun copy_url_for context libname = | |
1644 svn_info_item context libname "URL" | |
1645 | |
1646 end | |
1647 | |
1491 structure AnyLibControl :> LIB_CONTROL = struct | 1648 structure AnyLibControl :> LIB_CONTROL = struct |
1492 | 1649 |
1493 structure H = LibControlFn(HgControl) | 1650 structure H = LibControlFn(HgControl) |
1494 structure G = LibControlFn(GitControl) | 1651 structure G = LibControlFn(GitControl) |
1652 structure S = LibControlFn(SvnControl) | |
1495 | 1653 |
1496 fun review context (spec as { vcs, ... } : libspec) = | 1654 fun review context (spec as { vcs, ... } : libspec) = |
1497 (fn HG => H.review | GIT => G.review) vcs context spec | 1655 (fn HG => H.review | GIT => G.review | SVN => S.review) vcs context spec |
1498 | 1656 |
1499 fun status context (spec as { vcs, ... } : libspec) = | 1657 fun status context (spec as { vcs, ... } : libspec) = |
1500 (fn HG => H.status | GIT => G.status) vcs context spec | 1658 (fn HG => H.status | GIT => G.status | SVN => S.status) vcs context spec |
1501 | 1659 |
1502 fun update context (spec as { vcs, ... } : libspec) = | 1660 fun update context (spec as { vcs, ... } : libspec) = |
1503 (fn HG => H.update | GIT => G.update) vcs context spec | 1661 (fn HG => H.update | GIT => G.update | SVN => S.update) vcs context spec |
1504 | 1662 |
1505 fun id_of context (spec as { vcs, ... } : libspec) = | 1663 fun id_of context (spec as { vcs, ... } : libspec) = |
1506 (fn HG => H.id_of | GIT => G.id_of) vcs context spec | 1664 (fn HG => H.id_of | GIT => G.id_of | SVN => S.id_of) vcs context spec |
1665 | |
1507 end | 1666 end |
1508 | 1667 |
1509 | 1668 |
1510 type exclusions = string list | 1669 type exclusions = string list |
1511 | 1670 |
1556 does not. End users shouldn't get to see Vext) | 1715 does not. End users shouldn't get to see Vext) |
1557 | 1716 |
1558 - Clean up by deleting the new copy | 1717 - Clean up by deleting the new copy |
1559 *) | 1718 *) |
1560 | 1719 |
1561 fun project_vcs_and_id dir = | 1720 fun project_vcs_id_and_url dir = |
1562 let val context = { | 1721 let val context = { |
1563 rootpath = dir, | 1722 rootpath = dir, |
1564 extdir = ".", | 1723 extdir = ".", |
1565 providers = [], | 1724 providers = [], |
1566 accounts = [] | 1725 accounts = [] |
1567 } | 1726 } |
1568 val vcs_maybe = | 1727 val vcs_maybe = |
1569 case [HgControl.exists context ".", | 1728 case [HgControl.exists context ".", |
1570 GitControl.exists context "."] of | 1729 GitControl.exists context ".", |
1571 [OK true, OK false] => OK HG | 1730 SvnControl.exists context "."] of |
1572 | [OK false, OK true] => OK GIT | 1731 [OK true, OK false, OK false] => OK HG |
1732 | [OK false, OK true, OK false] => OK GIT | |
1733 | [OK false, OK false, OK true] => OK SVN | |
1573 | _ => ERROR ("Unable to identify VCS for directory " ^ dir) | 1734 | _ => ERROR ("Unable to identify VCS for directory " ^ dir) |
1574 in | 1735 in |
1575 case vcs_maybe of | 1736 case vcs_maybe of |
1576 ERROR e => ERROR e | 1737 ERROR e => ERROR e |
1577 | OK vcs => | 1738 | OK vcs => |
1578 case (fn HG => HgControl.id_of | GIT => GitControl.id_of) | 1739 case (fn HG => HgControl.id_of |
1740 | GIT => GitControl.id_of | |
1741 | SVN => SvnControl.id_of) | |
1579 vcs context "." of | 1742 vcs context "." of |
1580 ERROR e => ERROR ("Unable to obtain id of project repo: " | 1743 ERROR e => ERROR ("Unable to find id of project repo: " ^ e) |
1581 ^ e) | 1744 | OK id => |
1582 | OK id => OK (vcs, id) | 1745 case (fn HG => HgControl.copy_url_for |
1746 | GIT => GitControl.copy_url_for | |
1747 | SVN => SvnControl.copy_url_for) | |
1748 vcs context "." of | |
1749 ERROR e => ERROR ("Unable to find URL of project repo: " | |
1750 ^ e) | |
1751 | OK url => OK (vcs, id, url) | |
1583 end | 1752 end |
1584 | 1753 |
1585 fun make_archive_root (context : context) = | 1754 fun make_archive_root (context : context) = |
1586 let val path = OS.Path.joinDirFile { | 1755 let val path = OS.Path.joinDirFile { |
1587 dir = #rootpath context, | 1756 dir = #rootpath context, |
1603 fun check_nonexistent path = | 1772 fun check_nonexistent path = |
1604 case SOME (OS.FileSys.fileSize path) handle OS.SysErr _ => NONE of | 1773 case SOME (OS.FileSys.fileSize path) handle OS.SysErr _ => NONE of |
1605 NONE => () | 1774 NONE => () |
1606 | _ => raise Fail ("Path " ^ path ^ " exists, not overwriting") | 1775 | _ => raise Fail ("Path " ^ path ^ " exists, not overwriting") |
1607 | 1776 |
1608 fun file_url path = | 1777 fun make_archive_copy target_name (vcs, project_id, source_url) |
1609 let val forward_path = | |
1610 String.translate (fn #"\\" => "/" | | |
1611 c => Char.toString c) path | |
1612 in | |
1613 (* Path is expected to be absolute already, but if it | |
1614 starts with a drive letter, we'll need an extra slash *) | |
1615 case explode forward_path of | |
1616 #"/"::rest => "file:///" ^ implode rest | |
1617 | _ => "file:///" ^ forward_path | |
1618 end | |
1619 | |
1620 fun make_archive_copy target_name (vcs, project_id) | |
1621 ({ context, ... } : project) = | 1778 ({ context, ... } : project) = |
1622 let val archive_root = make_archive_root context | 1779 let val archive_root = make_archive_root context |
1623 val synthetic_context = { | 1780 val synthetic_context = { |
1624 rootpath = archive_root, | 1781 rootpath = archive_root, |
1625 extdir = ".", | 1782 extdir = ".", |
1627 accounts = [] | 1784 accounts = [] |
1628 } | 1785 } |
1629 val synthetic_library = { | 1786 val synthetic_library = { |
1630 libname = target_name, | 1787 libname = target_name, |
1631 vcs = vcs, | 1788 vcs = vcs, |
1632 source = URL_SOURCE (file_url (#rootpath context)), | 1789 source = URL_SOURCE source_url, |
1633 branch = DEFAULT_BRANCH, (* overridden by pinned id below *) | 1790 branch = DEFAULT_BRANCH, (* overridden by pinned id below *) |
1634 project_pin = PINNED project_id, | 1791 project_pin = PINNED project_id, |
1635 lock_pin = PINNED project_id | 1792 lock_pin = PINNED project_id |
1636 } | 1793 } |
1637 val path = archive_path archive_root target_name | 1794 val path = archive_path archive_root target_name |
1701 | TAR_BZ2 => "cjf" | 1858 | TAR_BZ2 => "cjf" |
1702 | TAR_XZ => "cJf", | 1859 | TAR_XZ => "cJf", |
1703 target_path, | 1860 target_path, |
1704 "--exclude=.hg", | 1861 "--exclude=.hg", |
1705 "--exclude=.git", | 1862 "--exclude=.git", |
1863 "--exclude=.svn", | |
1706 "--exclude=vext", | 1864 "--exclude=vext", |
1707 "--exclude=vext.sml", | 1865 "--exclude=vext.sml", |
1708 "--exclude=vext.ps1", | 1866 "--exclude=vext.ps1", |
1709 "--exclude=vext.bat", | 1867 "--exclude=vext.bat", |
1710 "--exclude=vext-project.json", | 1868 "--exclude=vext-project.json", |
1721 case packer_and_basename target_path of | 1879 case packer_and_basename target_path of |
1722 NONE => raise Fail ("Unsupported archive file extension in " | 1880 NONE => raise Fail ("Unsupported archive file extension in " |
1723 ^ target_path) | 1881 ^ target_path) |
1724 | SOME pn => pn | 1882 | SOME pn => pn |
1725 val details = | 1883 val details = |
1726 case project_vcs_and_id (#rootpath (#context project)) of | 1884 case project_vcs_id_and_url (#rootpath (#context project)) of |
1727 ERROR e => raise Fail e | 1885 ERROR e => raise Fail e |
1728 | OK details => details | 1886 | OK details => details |
1729 val archive_root = | 1887 val archive_root = |
1730 case make_archive_copy name details project of | 1888 case make_archive_copy name details project of |
1731 ERROR e => raise Fail e | 1889 ERROR e => raise Fail e |
1770 { | 1928 { |
1771 libname = libname, | 1929 libname = libname, |
1772 vcs = case vcs of | 1930 vcs = case vcs of |
1773 "hg" => HG | 1931 "hg" => HG |
1774 | "git" => GIT | 1932 | "git" => GIT |
1933 | "svn" => SVN | |
1775 | other => raise Fail ("Unknown version-control system \"" ^ | 1934 | other => raise Fail ("Unknown version-control system \"" ^ |
1776 other ^ "\""), | 1935 other ^ "\""), |
1777 source = case (url, service, owner, repo) of | 1936 source = case (url, service, owner, repo) of |
1778 (SOME u, NONE, _, _) => URL_SOURCE u | 1937 (SOME u, NONE, _, _) => URL_SOURCE u |
1779 | (NONE, SOME ss, owner, repo) => | 1938 | (NONE, SOME ss, owner, repo) => |
1781 | _ => raise Fail ("Must have exactly one of service " ^ | 1940 | _ => raise Fail ("Must have exactly one of service " ^ |
1782 "or url string"), | 1941 "or url string"), |
1783 project_pin = project_pin, | 1942 project_pin = project_pin, |
1784 lock_pin = lock_pin, | 1943 lock_pin = lock_pin, |
1785 branch = case branch of | 1944 branch = case branch of |
1786 SOME b => BRANCH b | 1945 NONE => DEFAULT_BRANCH |
1787 | NONE => DEFAULT_BRANCH | 1946 | SOME b => |
1947 case vcs of | |
1948 "svn" => raise Fail ("Branches not supported for " ^ | |
1949 "svn repositories; change " ^ | |
1950 "URL instead") | |
1951 | _ => BRANCH b | |
1788 } | 1952 } |
1789 end | 1953 end |
1790 | 1954 |
1791 fun load_userconfig () : userconfig = | 1955 fun load_userconfig () : userconfig = |
1792 let val home = FileBits.homedir () | 1956 let val home = FileBits.homedir () |
1968 fun review_project ({ context, libs } : project) = | 2132 fun review_project ({ context, libs } : project) = |
1969 return_code_for (act_and_print (AnyLibControl.review context) | 2133 return_code_for (act_and_print (AnyLibControl.review context) |
1970 print_status_header (print_status true) | 2134 print_status_header (print_status true) |
1971 libs) | 2135 libs) |
1972 | 2136 |
1973 fun update_project ({ context, libs } : project) = | |
1974 let val outcomes = act_and_print | |
1975 (AnyLibControl.update context) | |
1976 print_outcome_header print_update_outcome libs | |
1977 val locks = | |
1978 List.concat | |
1979 (map (fn (libname, result) => | |
1980 case result of | |
1981 ERROR _ => [] | |
1982 | OK id => [{ libname = libname, id_or_tag = id }]) | |
1983 outcomes) | |
1984 val return_code = return_code_for outcomes | |
1985 in | |
1986 if OS.Process.isSuccess return_code | |
1987 then save_lock_file (#rootpath context) locks | |
1988 else (); | |
1989 return_code | |
1990 end | |
1991 | |
1992 fun lock_project ({ context, libs } : project) = | 2137 fun lock_project ({ context, libs } : project) = |
1993 let val outcomes = map (fn lib => | 2138 let val _ = if FileBits.verbose () |
2139 then print ("Scanning IDs for lock file...\n") | |
2140 else () | |
2141 val outcomes = map (fn lib => | |
1994 (#libname lib, AnyLibControl.id_of context lib)) | 2142 (#libname lib, AnyLibControl.id_of context lib)) |
1995 libs | 2143 libs |
1996 val locks = | 2144 val locks = |
1997 List.concat | 2145 List.concat |
1998 (map (fn (libname, result) => | 2146 (map (fn (libname, result) => |
2005 in | 2153 in |
2006 if OS.Process.isSuccess return_code | 2154 if OS.Process.isSuccess return_code |
2007 then save_lock_file (#rootpath context) locks | 2155 then save_lock_file (#rootpath context) locks |
2008 else (); | 2156 else (); |
2009 return_code | 2157 return_code |
2158 end | |
2159 | |
2160 fun update_project (project as { context, libs }) = | |
2161 let val outcomes = act_and_print | |
2162 (AnyLibControl.update context) | |
2163 print_outcome_header print_update_outcome libs | |
2164 val _ = if List.exists (fn (_, OK _) => true | _ => false) outcomes | |
2165 then lock_project project | |
2166 else OS.Process.success | |
2167 in | |
2168 return_code_for outcomes | |
2010 end | 2169 end |
2011 | 2170 |
2012 fun load_local_project pintype = | 2171 fun load_local_project pintype = |
2013 let val userconfig = load_userconfig () | 2172 let val userconfig = load_userconfig () |
2014 val rootpath = OS.FileSys.getDir () | 2173 val rootpath = OS.FileSys.getDir () |