Mercurial > hg > piper-vamp-js-builds
changeset 26:3a6ebb47393f
Update vext
author | Chris Cannam |
---|---|
date | Mon, 17 Jul 2017 16:07:46 +0100 |
parents | 6bd90426715d |
children | |
files | vext vext-lock.json vext-project.json vext.ps1 vext.sml |
diffstat | 5 files changed, 241 insertions(+), 128 deletions(-) [+] |
line wrap: on
line diff
--- a/vext Mon Jul 17 16:02:19 2017 +0100 +++ b/vext Mon Jul 17 16:07:46 2017 +0100 @@ -11,12 +11,44 @@ mydir=$(dirname "$0") program="$mydir/vext.sml" +hasher= +local_install= +if [ -w "$mydir" ]; then + if echo | sha256sum >/dev/null 2>&1 ; then + hasher=sha256sum + local_install=true + elif echo | shasum >/dev/null 2>&1 ; then + hasher=shasum + local_install=true + else + echo "WARNING: sha256sum or shasum program not found" 1>&2 + fi +fi + +if [ -n "$local_install" ]; then + hash=$(echo "$sml" | cat "$program" - | $hasher | cut -c1-16) + gen_sml=$mydir/.vext-$hash.sml + gen_out=$mydir/.vext-$hash.bin + trap 'rm -f $gen_sml' 0 +else + gen_sml=$(mktemp /tmp/vext-XXXXXXXX.sml) + gen_out=$(mktemp /tmp/vext-XXXXXXXX.bin) + trap 'rm -f $gen_sml $gen_out' 0 +fi + +if [ -x "$gen_out" ]; then + exec "$gen_out" "$@" +fi + # We need one of Poly/ML, SML/NJ, or MLton. Since we're running a # single-file SML program as if it were a script, our order of -# preference is based on startup speed. +# preference is based on startup speed, except in the local_install +# case where we retain a persistent binary. if [ -z "$sml" ]; then - if sml -h 2>&1 | grep -q 'Standard ML of New Jersey'; then + if [ -n "$local_install" ] && mlton 2>&1 | grep -q 'MLton'; then + sml="mlton" + elif sml -h 2>&1 | grep -q 'Standard ML of New Jersey'; then sml="smlnj" # We would prefer Poly/ML to SML/NJ, except that Poly v5.7 has a # nasty bug that occasionally causes it to deadlock on startup. @@ -55,11 +87,6 @@ fi fi -tmp_sml=$(mktemp /tmp/vext-XXXXXXXX.sml) -tmp_out=$(mktemp /tmp/vext-XXXXXXXX.bin) - -trap 'rm -f $tmp_sml $tmp_out' 0 - arglist="" for arg in "$@"; do if [ -n "$arglist" ]; then arglist="$arglist,"; fi @@ -71,13 +98,22 @@ done case "$sml" in - poly) echo 'use "'"$program"'"; vext ['"$arglist"'];' | - poly -q --error-exit ;; + poly) + if [ -n "$local_install" ] && polyc --help >/dev/null 2>&1 ; then + if [ ! -x "$gen_out" ]; then + polyc -o "$gen_out" "$program" + fi + "$gen_out" "$@" + else + echo 'use "'"$program"'"; vext ['"$arglist"'];' | + poly -q --error-exit + fi ;; mlton) - cat "$program" > "$tmp_sml" - echo 'val _ = main ()' >> "$tmp_sml" - mlton -output "$tmp_out" "$tmp_sml" - "$tmp_out" "$@" ;; + if [ ! -x "$gen_out" ]; then + echo "val _ = main ()" | cat "$program" - > "$gen_sml" + mlton -output "$gen_out" "$gen_sml" + fi + "$gen_out" "$@" ;; smlnj) cat "$program" | ( cat <<EOF @@ -102,8 +138,8 @@ val _ = vext [$arglist]; val _ = OS.Process.exit (OS.Process.success); EOF - ) > "$tmp_sml" - CM_VERBOSE=false sml "$tmp_sml" ;; + ) > "$gen_sml" + CM_VERBOSE=false sml "$gen_sml" ;; *) echo "Unknown SML implementation name: $sml"; exit 2 ;;
--- a/vext-lock.json Mon Jul 17 16:02:19 2017 +0100 +++ b/vext-lock.json Mon Jul 17 16:07:46 2017 +0100 @@ -1,16 +1,16 @@ { - "libs": { + "libraries": { "vamp-plugin-sdk": { "pin": "5d9af3140f05" }, "piper": { - "pin": "169626171d22e6c49c6bc4759283406f4336a5cb" + "pin": "1beb47e41a2a02d75d0fb9204e1b7b95440f3ceb" }, "piper-vamp-cpp": { - "pin": "b146634c3686168a9d4bb739986d065b5d2d40a2" + "pin": "85394095a5b04f99a0785ccd32a5a98c90d984b2" }, "piper-vamp-js": { - "pin": "06b800f3176c4a932b0738abf7bec41282d15a1e" + "pin": "8e7ac2e7d31976119205bfdf43008902b5d6c224" }, "vamp-test-plugin": { "pin": "96cb7ef3cc24"
--- a/vext-project.json Mon Jul 17 16:02:19 2017 +0100 +++ b/vext-project.json Mon Jul 17 16:07:46 2017 +0100 @@ -9,7 +9,7 @@ "auth": "https://{account}@code.soundsoftware.ac.uk/{vcs}/{repo}" } }, - "libs": { + "libraries": { "vamp-plugin-sdk": { "vcs": "hg", "service": "soundsoftware"
--- a/vext.ps1 Mon Jul 17 16:02:19 2017 +0100 +++ b/vext.ps1 Mon Jul 17 16:07:46 2017 +0100 @@ -6,6 +6,9 @@ #> +Set-StrictMode -Version 2.0 +$ErrorActionPreference = "Stop" + $sml = $env:VEXT_SML $mydir = Split-Path $MyInvocation.MyCommand.Path -Parent @@ -14,10 +17,10 @@ # We need either Poly/ML or SML/NJ. No great preference as to which. if (!$sml) { - if (Get-Command "polyml" -ErrorAction SilentlyContinue) { + if (Get-Command "sml" -ErrorAction SilentlyContinue) { + $sml = "smlnj" + } elseif (Get-Command "polyml" -ErrorAction SilentlyContinue) { $sml = "poly" - } elseif (Get-Command "sml" -ErrorAction SilentlyContinue) { - $sml = "smlnj" } else { echo @" @@ -29,12 +32,12 @@ Please ensure you have one of the following SML implementations installed and present in your PATH, and try again. - 1. Poly/ML + 1. Standard ML of New Jersey + - executable name: sml + + 2. Poly/ML - executable name: polyml - 2. Standard ML of New Jersey - - executable name: sml - "@ exit 1 } @@ -51,6 +54,10 @@ $program = $program -replace "\\","\\\\" echo "use ""$program""; vext $arglist" | polyml -q --error-exit | Out-Host + if (-not $?) { + exit $LastExitCode + } + } elseif ($sml -eq "smlnj") { $lines = @(Get-Content $program) @@ -74,28 +81,33 @@ }; "@ -split "[\r\n]+" - $outro = @" + $outro = @" val _ = vext $arglist; val _ = OS.Process.exit (OS.Process.success); "@ -split "[\r\n]+" - $script = @() - $script += $intro - $script += $lines - $script += $outro + $script = @() + $script += $intro + $script += $lines + $script += $outro - $tmpfile = ([System.IO.Path]::GetTempFileName()) -replace "[.]tmp",".sml" + $tmpfile = ([System.IO.Path]::GetTempFileName()) -replace "[.]tmp",".sml" - $script | Out-File -Encoding "ASCII" $tmpfile + $script | Out-File -Encoding "ASCII" $tmpfile - $env:CM_VERBOSE="false" + $env:CM_VERBOSE="false" - sml $tmpfile $args[1,$args.Length] + sml $tmpfile - del $tmpfile + if (-not $?) { + del $tmpfile + exit $LastExitCode + } + + del $tmpfile } else { - "Unknown SML implementation name: $sml" - exit 2 + "Unknown SML implementation name: $sml" + exit 2 }
--- a/vext.sml Mon Jul 17 16:02:19 2017 +0100 +++ b/vext.sml Mon Jul 17 16:07:46 2017 +0100 @@ -33,7 +33,7 @@ Software without prior written authorization. *) -val vext_version = "0.9.2" +val vext_version = "0.9.6" datatype vcs = @@ -48,9 +48,11 @@ repo : string option } +type id_or_tag = string + datatype pin = UNPINNED | - PINNED of string + PINNED of id_or_tag datatype libstate = ABSENT | @@ -60,7 +62,8 @@ datatype localstate = MODIFIED | - UNMODIFIED + LOCK_MISMATCHED | + CLEAN datatype branch = BRANCH of string | @@ -77,21 +80,20 @@ type libname = string -type id_or_tag = string - type libspec = { libname : libname, vcs : vcs, source : source, branch : branch, - pin : pin + project_pin : pin, + lock_pin : pin } type lock = { libname : libname, id_or_tag : id_or_tag } - + type remote_spec = { anon : string option, auth : string option @@ -177,6 +179,7 @@ val review : context -> libspec -> (libstate * localstate) result val status : context -> libspec -> (libstate * localstate) result val update : context -> libspec -> id_or_tag result + val id_of : context -> libspec -> id_or_tag result end structure FileBits :> sig @@ -402,7 +405,8 @@ - ABSENT: Repo doesn't exist here at all. *) - fun check with_network context ({ libname, branch, pin, ... } : libspec) = + fun check with_network context + ({ libname, branch, project_pin, lock_pin, ... } : libspec) = let fun check_unpinned () = let val is_newest = if with_network then V.is_newest @@ -422,26 +426,39 @@ ERROR e => ERROR e | OK true => OK CORRECT | OK false => OK WRONG - fun check' () = - case pin of + fun check_remote () = + case project_pin of UNPINNED => check_unpinned () | PINNED target => check_pinned target + fun check_local () = + case V.is_modified_locally context libname of + ERROR e => ERROR e + | OK true => OK MODIFIED + | OK false => + case lock_pin of + UNPINNED => OK CLEAN + | PINNED target => + case V.is_at context (libname, target) of + ERROR e => ERROR e + | OK true => OK CLEAN + | OK false => OK LOCK_MISMATCHED in case V.exists context libname of ERROR e => ERROR e - | OK false => OK (ABSENT, UNMODIFIED) + | OK false => OK (ABSENT, CLEAN) | OK true => - case (check' (), V.is_modified_locally context libname) of + case (check_remote (), check_local ()) of (ERROR e, _) => ERROR e | (_, ERROR e) => ERROR e - | (OK state, OK true) => OK (state, MODIFIED) - | (OK state, OK false) => OK (state, UNMODIFIED) + | (OK r, OK l) => OK (r, l) end val review = check true val status = check false - - fun update context ({ libname, source, branch, pin, ... } : libspec) = + + fun update context + ({ libname, source, branch, + project_pin, lock_pin, ... } : libspec) = let fun update_unpinned () = case V.is_newest context (libname, branch) of ERROR e => ERROR e @@ -453,9 +470,12 @@ | OK true => OK target | OK false => V.update_to context (libname, target) fun update' () = - case pin of - UNPINNED => update_unpinned () - | PINNED target => update_pinned target + case lock_pin of + PINNED target => update_pinned target + | UNPINNED => + case project_pin of + PINNED target => update_pinned target + | UNPINNED => update_unpinned () in case V.exists context libname of ERROR e => ERROR e @@ -465,6 +485,10 @@ ERROR e => ERROR e | OK () => update' () end + + fun id_of context ({ libname, ... } : libspec) = + V.id_of context libname + end (* Simple Standard ML JSON parser @@ -889,11 +913,12 @@ | Json.ERROR e => raise Fail ("Failed to parse file: " ^ e) fun save_json_to filename json = + (* using binary I/O to avoid ever writing CR/LF line endings *) let val jstr = Json.serialiseIndented json - val stream = TextIO.openOut filename + val stream = BinIO.openOut filename in - TextIO.output (stream, jstr); - TextIO.closeOut stream + BinIO.output (stream, Byte.stringToBytes jstr); + BinIO.closeOut stream end fun lookup_optional json kk = @@ -945,16 +970,16 @@ service = "bitbucket", supports = [HG, GIT], remote_spec = { - anon = SOME "https://bitbucket.org/{owner}/{repo}", - auth = SOME "ssh://{vcs}@bitbucket.org/{owner}/{repo}" + anon = SOME "https://bitbucket.org/{owner}/{repository}", + auth = SOME "ssh://{vcs}@bitbucket.org/{owner}/{repository}" } }, { service = "github", supports = [GIT], remote_spec = { - anon = SOME "https://github.com/{owner}/{repo}", - auth = SOME "ssh://{vcs}@github.com/{owner}/{repo}" + anon = SOME "https://github.com/{owner}/{repository}", + auth = SOME "ssh://{vcs}@github.com/{owner}/{repository}" } } ] @@ -981,8 +1006,8 @@ vv | _ => raise Fail "Array expected for vcs", remote_spec = { - anon = lookup_optional_string pjson ["anon"], - auth = lookup_optional_string pjson ["auth"] + anon = lookup_optional_string pjson ["anonymous"], + auth = lookup_optional_string pjson ["authenticated"] } } val loaded = @@ -1013,7 +1038,7 @@ SOME ostr => ostr | NONE => raise Fail ("Owner not specified for service " ^ service)) - | "repo" => repo + | "repository" => repo | "account" => (case login of SOME acc => acc @@ -1059,8 +1084,9 @@ (SOME _, SOME auth, _) => expand_spec auth req login | (SOME _, _, SOME anon) => expand_spec anon req NONE | (NONE, _, SOME anon) => expand_spec anon req NONE - | _ => raise Fail ("No suitable anon/auth URL spec " ^ - "provided for service \"" ^ service ^ "\"") + | _ => raise Fail ("No suitable anonymous or authenticated " ^ + "URL spec provided for service \"" ^ + service ^ "\"") fun login_for ({ accounts, ... } : context) service = case List.find (fn a => service = #service a) accounts of @@ -1206,15 +1232,15 @@ fun update_to context (libname, "") = ERROR "Non-empty id (tag or revision id) required for update_to" | update_to context (libname, id) = - case hg_command context libname ["update", "-r" ^ id] of - OK () => id_of context libname - | ERROR _ => - case pull context libname of - ERROR e => ERROR e - | _ => - case hg_command context libname ["update", "-r" ^ id] of - ERROR e => ERROR e - | _ => id_of context libname + let val pull_result = pull context libname + in + case hg_command context libname ["update", "-r", id] of + OK _ => id_of context libname + | ERROR e => + case pull_result of + ERROR e' => ERROR e' (* this was the ur-error *) + | _ => ERROR e + end end @@ -1270,11 +1296,11 @@ then OK true else case git_command_output context libname - ["rev-list", "-1", id_or_tag] of - ERROR e => OK false (* id_or_tag is not an id or tag, but - that could just mean it hasn't been - fetched *) - | OK tid => OK (tid = id) + ["show-ref", + "refs/tags/" ^ id_or_tag] of + OK "" => OK false + | ERROR _ => OK false + | OK s => OK (id = hd (String.tokens (fn c => c = #" ") s)) fun branch_tip context (libname, branch) = git_command_output context libname @@ -1332,21 +1358,26 @@ | _ => id_of context libname (* This function is dealing with a specific id or tag, so if we - can successfully check it out (detached) then that's all we need - to do. Otherwise we need to fetch and try again *) + can successfully check it out (detached) then that's all we + need to do, regardless of whether fetch succeeded or not. We do + attempt the fetch first, though, purely in order to avoid ugly + error messages in the common case where we're being asked to + update to a new pin (from the lock file) that hasn't been + fetched yet. *) fun update_to context (libname, "") = ERROR "Non-empty id (tag or revision id) required for update_to" | update_to context (libname, id) = - case git_command context libname ["checkout", "--detach", id] of - OK () => id_of context libname - | ERROR _ => - case git_command context libname ["fetch"] of - ERROR e => ERROR e - | _ => - case git_command context libname ["checkout", "--detach", id] of - ERROR e => ERROR e - | _ => id_of context libname + let val fetch_result = git_command context libname ["fetch"] + in + case git_command context libname ["checkout", "--detach", id] of + OK _ => id_of context libname + | ERROR e => + case fetch_result of + ERROR e' => ERROR e' (* this was the ur-error *) + | _ => ERROR e + end + end structure AnyLibControl :> LIB_CONTROL = struct @@ -1362,11 +1393,16 @@ fun update context (spec as { vcs, ... } : libspec) = (fn HG => H.update | GIT => G.update) vcs context spec + + fun id_of context (spec as { vcs, ... } : libspec) = + (fn HG => H.id_of | GIT => G.id_of) vcs context spec end +val libobjname = "libraries" + fun load_libspec spec_json lock_json libname : libspec = let open JsonBits - val libobj = lookup_mandatory spec_json ["libs", libname] + val libobj = lookup_mandatory spec_json [libobjname, libname] val vcs = lookup_mandatory_string libobj ["vcs"] val retrieve = lookup_optional_string libobj val service = retrieve ["service"] @@ -1374,10 +1410,14 @@ val repo = retrieve ["repository"] val url = retrieve ["url"] val branch = retrieve ["branch"] - val user_pin = retrieve ["pin"] - val lock_pin = case lookup_optional lock_json ["libs", libname] of - SOME ll => lookup_optional_string ll ["pin"] - | NONE => NONE + val project_pin = case retrieve ["pin"] of + NONE => UNPINNED + | SOME p => PINNED p + val lock_pin = case lookup_optional lock_json [libobjname, libname] of + NONE => UNPINNED + | SOME ll => case lookup_optional_string ll ["pin"] of + SOME p => PINNED p + | NONE => UNPINNED in { libname = libname, @@ -1392,12 +1432,8 @@ SERVICE_SOURCE { service = ss, owner = owner, repo = repo } | _ => raise Fail ("Must have exactly one of service " ^ "or url string"), - pin = case lock_pin of - SOME p => PINNED p - | NONE => - case user_pin of - SOME p => PINNED p - | NONE => UNPINNED, + project_pin = project_pin, + lock_pin = lock_pin, branch = case branch of SOME b => BRANCH b | NONE => DEFAULT_BRANCH @@ -1427,7 +1463,11 @@ } end -fun load_project (userconfig : userconfig) rootpath use_locks : project = +datatype pintype = + NO_LOCKFILE | + USE_LOCKFILE + +fun load_project (userconfig : userconfig) rootpath pintype : project = let val spec_file = FileBits.project_spec_path rootpath val lock_file = FileBits.project_lock_path rootpath val _ = if OS.FileSys.access (spec_file, [OS.FileSys.A_READ]) @@ -1439,14 +1479,14 @@ ".\nPlease ensure the spec file is in the " ^ "project root and run this from there.") val spec_json = JsonBits.load_json_from spec_file - val lock_json = if use_locks + val lock_json = if pintype = USE_LOCKFILE then JsonBits.load_json_from lock_file handle IO.Io _ => Json.OBJECT [] else Json.OBJECT [] val extdir = JsonBits.lookup_mandatory_string spec_json ["config", "extdir"] - val spec_libs = JsonBits.lookup_optional spec_json ["libs"] - val lock_libs = JsonBits.lookup_optional lock_json ["libs"] + val spec_libs = JsonBits.lookup_optional spec_json [libobjname] + val lock_libs = JsonBits.lookup_optional lock_json [libobjname] val providers = Provider.load_more_providers (#providers userconfig) spec_json val libnames = case spec_libs of @@ -1470,11 +1510,11 @@ open Json val lock_json = OBJECT [ - ("libs", OBJECT - (map (fn { libname, id_or_tag } => - (libname, - OBJECT [ ("pin", STRING id_or_tag) ])) - locks)) + (libobjname, + OBJECT (map (fn { libname, id_or_tag } => + (libname, + OBJECT [ ("pin", STRING id_or_tag) ])) + locks)) ] in JsonBits.save_json_to lock_file lock_json @@ -1489,12 +1529,13 @@ val libname_width = 25 val libstate_width = 11 -val localstate_width = 9 +val localstate_width = 17 val notes_width = 5 val divider = " | " +val clear_line = "\r" ^ pad_to 80 ""; fun print_status_header () = - print ("\r" ^ pad_to 80 "" ^ "\n " ^ + print (clear_line ^ "\n " ^ pad_to libname_width "Library" ^ divider ^ pad_to libstate_width "State" ^ divider ^ pad_to localstate_width "Local" ^ divider ^ @@ -1505,7 +1546,7 @@ hline_to notes_width ^ "\n") fun print_outcome_header () = - print ("\r" ^ pad_to 80 "" ^ "\n " ^ + print (clear_line ^ "\n " ^ pad_to libname_width "Library" ^ divider ^ pad_to libstate_width "Outcome" ^ divider ^ "Notes" ^ "\n " ^ @@ -1524,8 +1565,9 @@ val localstate_str = case status of OK (_, MODIFIED) => "Modified" - | OK (_, UNMODIFIED) => "Clean" - | _ => "" + | OK (_, LOCK_MISMATCHED) => "Differs from Lock" + | OK (_, CLEAN) => "Clean" + | ERROR _ => "" val error_str = case status of ERROR e => e @@ -1599,15 +1641,35 @@ return_code end -fun load_local_project use_locks = +fun lock_project ({ context, libs } : project) = + let val outcomes = map (fn lib => + (#libname lib, AnyLibControl.id_of context lib)) + libs + val locks = + List.concat + (map (fn (libname, result) => + case result of + ERROR _ => [] + | OK id => [{ libname = libname, id_or_tag = id }]) + outcomes) + val return_code = return_code_for outcomes + val _ = print clear_line + in + if OS.Process.isSuccess return_code + then save_lock_file (#rootpath context) locks + else (); + return_code + end + +fun load_local_project pintype = let val userconfig = load_userconfig () val rootpath = OS.FileSys.getDir () in - load_project userconfig rootpath use_locks + load_project userconfig rootpath pintype end -fun with_local_project use_locks f = - let val return_code = f (load_local_project use_locks) +fun with_local_project pintype f = + let val return_code = f (load_local_project pintype) handle e => (print ("Failed with exception: " ^ (exnMessage e) ^ "\n"); @@ -1617,10 +1679,11 @@ return_code end -fun review () = with_local_project false review_project -fun status () = with_local_project false status_of_project -fun update () = with_local_project false update_project -fun install () = with_local_project true update_project +fun review () = with_local_project USE_LOCKFILE review_project +fun status () = with_local_project USE_LOCKFILE status_of_project +fun update () = with_local_project NO_LOCKFILE update_project +fun lock () = with_local_project NO_LOCKFILE lock_project +fun install () = with_local_project USE_LOCKFILE update_project fun version () = (print ("v" ^ vext_version ^ "\n"); @@ -1637,6 +1700,7 @@ ^ " review check configured libraries against their providers, and report\n" ^ " install update configured libraries according to project specs and lock file\n" ^ " update update configured libraries and lock file according to project specs\n" + ^ " lock update lock file to match local library status\n" ^ " version print the Vext version number and exit\n\n"); OS.Process.failure) @@ -1647,6 +1711,7 @@ | ["status"] => status () | ["install"] => install () | ["update"] => update () + | ["lock"] => lock () | ["version"] => version () | _ => usage () in