Merge branch 'trunk' into transcripts-cleanup

This commit is contained in:
Arya Irani 2024-04-28 22:34:49 -04:00
commit d0bdd55ace
40 changed files with 1349 additions and 1037 deletions

View File

@ -159,14 +159,15 @@ jobs:
distribution: "full"
variant: "CS"
version: ${{env.racket_version}}
- uses: awalsh128/cache-apt-pkgs-action@latest
if: runner.os == 'Linux'
with:
packages: libb2-dev
version: 1.0 # cache key version afaik
- name: install unison racket lib
if: steps.cache-racket-deps.outputs.cache-hit != 'true'
run: raco pkg install --auto scheme-libs/racket/unison.zip
- name: install libb2 (macos)
if: runner.os == 'macOS'
run: |
brew install libb2
ln -s "$(brew --prefix)"/lib/libb2.*.dylib \
"$(dirname "$(readlink -f "$(which raco)")")"/../lib/
- name: build unison-runtime
run: |
raco exe --embed-dlls --orig-exe scheme-libs/racket/unison-runtime.rkt

View File

@ -0,0 +1,116 @@
name: build jit binary
on:
workflow_call:
defaults:
run:
shell: bash
env:
jit_src: unison-jit-src/
jit_dist: unison-jit-dist/
racket_version: "8.7"
jobs:
build-jit-binary:
name: build jit binary
strategy:
fail-fast: false
matrix:
os: [ubuntu-20.04, macOS-12, windows-2019]
runs-on: ${{matrix.os}}
steps:
- name: set up environment
run: |
jit_src="$GITHUB_WORKSPACE/${{ env.jit_src }}" # scheme source
jit_exe="${jit_src}/unison-runtime" # initially built jit
jit_dist="${{ runner.temp }}/${{ env.jit_dist }}" # jit binary with libraries destination
jit_dist_exe="${jit_dist}/bin/unison-runtime" # jit binary itself
ucm="${{ runner.temp }}/unison"
if [[ ${{runner.os}} = "Windows" ]]; then
jit_src="${jit_src//\\//}"
jit_dist="${jit_dist//\\//}"
jit_exe="${jit_exe//\\//}.exe"
jit_dist_exe="${jit_dist//\\//}/unison-runtime.exe"
ucm="${ucm//\\//}.exe"
fi
echo "jit_src=$jit_src" >> $GITHUB_ENV
echo "jit_exe=$jit_exe" >> $GITHUB_ENV
echo "jit_dist=$jit_dist" >> $GITHUB_ENV
echo "jit_dist_exe=$jit_dist_exe" >> $GITHUB_ENV
echo "ucm=$ucm" >> $GITHUB_ENV
- name: get workflow files, for checking hashes
uses: actions/checkout@v4
with:
sparse-checkout: .github
- name: download jit source
uses: actions/download-artifact@v4
with:
name: jit-source
path: ${{ env.jit_src }}
- name: cache/restore jit binaries
id: cache-jit-binaries
uses: actions/cache/restore@v4
with:
path: ${{ env.jit_dist }}
key: jit-dist_${{matrix.os}}.racket_${{env.racket_version}}.jit-src_${{hashFiles(format('{0}/**.rkt',env.jit_src),format('{0}/**.ss',env.jit_src))}}.yaml_${{hashFiles('**/ci-build-jit-binary.yaml')}}
- name: cache racket dependencies
if: steps.cache-jit-binaries.outputs.cache-hit != 'true'
uses: actions/cache@v4
with:
path: |
~/.cache/racket
~/.local/share/racket
key: ${{ matrix.os }}.racket_${{env.racket_version}}.yaml_${{hashFiles('**/ci-build-jit-binary.yaml')}}
- name: install racket
if: steps.cache-jit-binaries.outputs.cache-hit != 'true'
uses: unisonweb/actions/racket/install@buildjet-cache
with:
version: ${{env.racket_version}}
- name: set up redistributables (macos)
if: runner.os == 'macOS' && steps.cache-jit-binaries.outputs.cache-hit != 'true'
run: |
brew install libb2
racket_lib_dir="$(dirname "$(readlink -f "$(which raco)")")/../lib"
ln -s "$(brew --prefix)"/lib/libb2.*.dylib "$racket_lib_dir/"
- name: build jit binary
if: steps.cache-jit-binaries.outputs.cache-hit != 'true'
shell: bash
run: |
if [[ ${{runner.os}} = "Windows" ]]; then
raco pkg install --auto --skip-installed --scope installation x509-lib
elif [[ ${{runner.os}} = "macOS" ]]; then
raco pkg install --auto --skip-installed --scope installation x509-lib
elif [[ ${{runner.os}} = "Linux" ]]; then
sudo raco pkg install --auto --skip-installed --scope installation x509-lib
fi
raco pkg install --auto --skip-installed "$jit_src"/unison
raco exe --embed-dlls "$jit_src"/unison-runtime.rkt
raco distribute -v "$jit_dist" "$jit_exe"
- name: cache/save jit binaries
if: steps.cache-jit-binaries.outputs.cache-hit != 'true'
uses: actions/cache/save@v4
with:
path: ${{ env.jit_dist }}
key: jit-dist_${{matrix.os}}.racket_${{env.racket_version}}.jit-src_${{hashFiles(format('{0}/**.rkt',env.jit_src),format('{0}/**.ss',env.jit_src))}}.yaml_${{hashFiles('**/ci-build-jit-binary.yaml')}}
- name: save jit binary
uses: actions/upload-artifact@v4
with:
name: jit-binary-${{ matrix.os }}
path: ${{ env.jit_dist }}/**
# - name: setup tmate session
# uses: mxschmitt/action-tmate@v3

129
.github/workflows/ci-test-jit.yaml vendored Normal file
View File

@ -0,0 +1,129 @@
name: test jit
on:
workflow_call:
env:
runtime_tests_version: "@unison/runtime-tests/main"
# for best results, this should match the path in ci.yaml too; but GH doesn't make it easy to share them.
runtime_tests_codebase: "~/.cache/unisonlanguage/runtime-tests.unison"
jit_src_rel: unison-jit-src
jit_dist_rel: unison-jit-dist
jit_test_results: jit-test-results
defaults:
run:
shell: bash
jobs:
build-jit-binary:
name: test jit
strategy:
fail-fast: false
matrix:
os:
- ubuntu-20.04
- macOS-12
# - windows-2019
runs-on: ${{matrix.os}}
steps:
- name: set up environment
run: |
jit_src="$GITHUB_WORKSPACE/${{ env.jit_src_rel }}" # scheme source, for hashing
jit_dist="$GITHUB_WORKSPACE/${{ env.jit_dist_rel }}" # jit binary with libraries destination
jit_dist_exe="${jit_dist}/bin/unison-runtime" # jit binary itself
jit_dist_rel_exe="${jit_dist_rel}/bin/unison-runtime" # jit binary itself
ucm="${{ runner.temp }}/unison"
if [[ ${{runner.os}} = "Windows" ]]; then
jit_src="${jit_src//\\//}"
jit_dist="${jit_dist//\\//}"
jit_dist_exe="${jit_dist//\\//}/unison-runtime.exe"
jit_dist_rel_exe="${jit_dist_rel//\\//}/unison-runtime.exe"
ucm="${ucm//\\//}.exe"
fi
echo "jit_src=$jit_src" >> $GITHUB_ENV
echo "jit_dist=$jit_dist" >> $GITHUB_ENV
echo "jit_dist_exe=$jit_dist_exe" >> $GITHUB_ENV
echo "jit_dist_rel_exe=$jit_dist_rel_exe" >> $GITHUB_ENV
echo "ucm=$ucm" >> $GITHUB_ENV
- uses: actions/checkout@v4
with:
sparse-checkout: |
.github
scripts/get-share-hash.sh
unison-src/builtin-tests/jit-tests.tpl.md
unison-src/transcripts-using-base/serialized-cases/case-00.v4.ser
- name: download jit binaries
uses: actions/download-artifact@v4
with:
name: jit-binary-${{ matrix.os }}
path: ${{ env.jit_dist }}
- name: look up hash for runtime tests
run: |
echo "runtime_tests_causalhash=$(scripts/get-share-hash.sh ${{ env.runtime_tests_version }})" >> $GITHUB_ENV
- name: cache jit test results
id: cache-jit-test-results
uses: actions/cache@v4
with:
path: ${{env.jit_test_results}}
key: jit-test-results.dist-exe_${{ hashFiles(env.jit_dist_rel_exe) }}.tests_${{ env.runtime_tests_causalhash }}.yaml_${{ hashFiles('**/ci-test-jit.yaml') }}
- name: install libb2 (linux)
uses: awalsh128/cache-apt-pkgs-action@latest
if: runner.os == 'Linux' && steps.cache-jit-test-results.outputs.cache-hit != 'true'
with:
packages: libb2-1
version: 1.0 # cache key version afaik
- name: cache testing codebase
id: cache-testing-codebase
if: steps.cache-jit-test-results.outputs.cache-hit != 'true'
uses: actions/cache@v4
with:
path: ${{ env.runtime_tests_codebase }}
key: runtime-tests-codebase-${{ matrix.os }}-${{env.runtime_tests_causalhash}}
restore-keys: runtime-tests-codebase-${{ matrix.os }}-
- name: download ucm
if: steps.cache-jit-test-results.outputs.cache-hit != 'true'
uses: actions/download-artifact@v4
with:
name: unison-${{ matrix.os }}
path: ${{ runner.temp }}
- name: set ucm & runtime permissions
if: steps.cache-jit-test-results.outputs.cache-hit != 'true'
run: |
chmod +x ${{ env.ucm }}
chmod +x ${{ env.jit_dist_exe }}
if [[ ${{runner.os}} = "Linux" ]]; then
chmod +x ${{env.jit_dist}}/lib/plt/*
fi
- name: jit integration test ${{ matrix.os }}
if: steps.cache-jit-test-results.outputs.cache-hit != 'true'
run: |
envsubst '${runtime_tests_version}' \
< unison-src/builtin-tests/jit-tests.tpl.md \
> unison-src/builtin-tests/jit-tests.md
${{ env.ucm }} transcript.fork --runtime-path ${{ env.jit_dist_exe }} -C ${{env.runtime_tests_codebase}} unison-src/builtin-tests/jit-tests.md
cat unison-src/builtin-tests/jit-tests.output.md
git diff --exit-code unison-src/builtin-tests/jit-tests.output.md
- name: mark jit tests as passing
if: steps.cache-jit-test-results.outputs.cache-hit != 'true'
run: |
echo "passing=true" >> "${{env.jit_test_results}}"
# - name: Setup tmate session
# uses: mxschmitt/action-tmate@v3
# if: ${{ failure() }}
# timeout-minutes: 15

View File

@ -18,20 +18,18 @@ on:
workflow_dispatch:
env:
ormolu_version: "0.5.2.0"
racket_version: "8.7"
ucm_local_bin: "ucm-local-bin"
ormolu_version: 0.5.2.0
ucm_local_bin: ucm-local-bin
jit_version: "@unison/internal/releases/0.0.15"
jit_src_scheme: "unison-jit-src/scheme-libs/racket"
jit_dist: "unison-jit-dist"
jit_src_scheme: unison-jit-src/scheme-libs/racket
jit_dist: unison-jit-dist
jit_generator_os: ubuntu-20.04
runtime_tests_version: "@unison/runtime-tests/main"
runtime_tests_codebase: "~/.cache/unisonlanguage/runtime-tests.unison"
# locations of some files that will indicate whether we need to re-run certain steps
transcript_test_results: "transcript-test-results"
interpreter_test_results: "interpreter-test-results"
jit_test_results: "jit-test-results"
transcript_test_results: transcript-test-results
interpreter_test_results: interpreter-test-results
jobs:
ormolu:
@ -354,38 +352,25 @@ jobs:
steps:
- name: set up environment
run: |
echo "jit_generated_src_scheme=${{ runner.temp }}/jit-generated-src" >> $GITHUB_ENV
echo "jit_src_scheme=${{ runner.temp }}/${{ env.jit_src_scheme }}" >> $GITHUB_ENV
echo "ucm=${{ runner.temp }}/unison" >> $GITHUB_ENV
- uses: actions/cache@v4
name: cache jit source
- name: download scheme-libs
uses: actions/checkout@v4
with:
path: ${{ env.jit_src_scheme }}
key: jit_src_scheme-racket_${{env.racket_version}}.jit_${{env.jit_version}}-${{hashFiles('**/scheme-libs/**')}}
- name: check source exists
id: jit_src_exists
if: steps.cache-jit-source.outputs.cache-hit != 'true'
run: |
files=(data-info boot-generated simple-wrappers builtin-generated compound-wrappers)
all_exist=true
for file in "${files[@]}"; do
if [[ ! -f "${{ env.jit_src_scheme }}/unison/$file.ss" ]]; then
echo "$file does not exist."
all_exist=false
# Uncomment the next line if you want to stop checking after the first missing file
# break
fi
done
if $all_exist; then
echo "files_exists=true" >> $GITHUB_OUTPUT
else
echo "files_exists=false" >> $GITHUB_OUTPUT
fi
sparse-checkout: |
scripts/get-share-hash.sh
scheme-libs
- name: look up hash for jit source generator
run: echo "jit_causalhash=$(scripts/get-share-hash.sh ${{env.jit_version}})" >> $GITHUB_ENV
- uses: actions/cache@v4
name: cache auto-generated jit source
id: cache-generated-source
with:
path: ${{ env.jit_generated_src_scheme }}
key: jit_generated_src_scheme-jit_${{env.jit_version}}-${{env.jit_causalhash}}
- name: create transcript
if: steps.jit_src_exists.outputs.files_exists == 'false'
if: steps.cache-generated-source.outputs.cache-hit != 'true'
uses: DamianReeves/write-file-action@v1.3
with:
path: ${{ runner.temp }}/setup-jit.md
@ -396,34 +381,31 @@ jobs:
jit-setup/main> pull ${{ env.jit_version }} lib.jit
```
```unison
go = generateSchemeBoot "${{ env.jit_src_scheme }}"
go = generateSchemeBoot "${{ env.jit_generated_src_scheme }}"
```
```ucm
jit-setup/main> run go
```
- name: download ucm artifact
if: steps.jit_src_exists.outputs.files_exists == 'false'
if: steps.cache-generated-source.outputs.cache-hit != 'true'
uses: actions/download-artifact@v4
with:
name: unison-${{ env.jit_generator_os }}
path: ${{ runner.temp }}
- name: set ucm permissions
if: steps.jit_src_exists.outputs.files_exists == 'false'
if: steps.cache-generated-source.outputs.cache-hit != 'true'
run: chmod +x ${{ env.ucm }}
- name: download scheme-libs
if: steps.jit_src_exists.outputs.files_exists == 'false'
uses: actions/checkout@v4
- name: generate source
if: steps.jit_src_exists.outputs.files_exists == 'false'
if: steps.cache-generated-source.outputs.cache-hit != 'true'
run: |
mkdir -p ${{ env.jit_generated_src_scheme }}
${{ env.ucm }} transcript ${{ runner.temp }}/setup-jit.md
- name: bundle source
run: |
mkdir -p ${{ env.jit_src_scheme }}
cp -R scheme-libs/racket/* ${{ env.jit_src_scheme }}
${{ env.ucm }} transcript ${{ runner.temp }}/setup-jit.md
cp -R "${{ env.jit_generated_src_scheme }}"/* ${{ env.jit_src_scheme }}
- name: save jit source
if: always()
uses: actions/upload-artifact@v4
@ -433,162 +415,11 @@ jobs:
if-no-files-found: error
build-jit-binary:
if: always() && needs.generate-jit-source.result == 'success'
name: build jit binary
needs: generate-jit-source
runs-on: ${{ matrix.os }}
strategy:
# Run each build to completion, regardless of if any have failed
fail-fast: false
matrix:
os:
# While iterating on this file, you can disable one or more of these to speed things up
- ubuntu-20.04
- macOS-12
- windows-2019
steps:
- name: set up environment
id: checks
run: |
jit_src_scheme="${{ runner.temp }}/${{ env.jit_src_scheme }}" # scheme source
jit_exe="${jit_src_scheme}/unison-runtime" # initially built jit
jit_dist="${{ runner.temp }}/${{ env.jit_dist }}" # jit binary with libraries destination
jit_dist_exe="${jit_dist}/bin/unison-runtime" # jit binary itself
ucm="${{ runner.temp }}/unison"
uses: ./.github/workflows/ci-build-jit-binary.yaml
if [[ ${{runner.os}} = "Windows" ]]; then
jit_src_scheme="${jit_src_scheme//\\//}"
jit_dist="${jit_dist//\\//}"
jit_exe="${jit_exe//\\//}.exe"
jit_dist_exe="${jit_dist//\\//}/unison-runtime.exe"
ucm="${ucm//\\//}.exe"
fi
echo "jit_src_scheme=$jit_src_scheme" >> $GITHUB_ENV
echo "jit_exe=$jit_exe" >> $GITHUB_ENV
echo "jit_dist=$jit_dist" >> $GITHUB_ENV
echo "jit_dist_exe=$jit_dist_exe" >> $GITHUB_ENV
echo "ucm=$ucm" >> $GITHUB_ENV
- name: cache jit binaries
id: cache-jit-binaries
uses: actions/cache@v4
with:
path: ${{ env.jit_dist }}
key: jit_dist-${{ matrix.os }}.racket_${{ env.racket_version }}.jit_${{ env.jit_version }}
- name: Cache Racket dependencies
if: steps.cache-jit-binaries.outputs.cache-hit != 'true'
uses: actions/cache@v4
with:
path: |
~/.cache/racket
~/.local/share/racket
key: ${{ matrix.os }}-racket-${{env.racket_version}}
- uses: Bogdanp/setup-racket@v1.11
if: steps.cache-jit-binaries.outputs.cache-hit != 'true'
with:
architecture: x64
distribution: full
variant: CS
version: ${{env.racket_version}}
- uses: actions/checkout@v4
with:
sparse-checkout: |
scripts/get-share-hash.sh
scheme-libs
unison-src/builtin-tests/jit-tests.tpl.md
unison-src/transcripts-using-base/serialized-cases/case-00.v4.ser
- name: look up hash for runtime tests
run: |
echo "runtime_tests_causalhash=$(scripts/get-share-hash.sh ${{env.runtime_tests_version}})" >> $GITHUB_ENV
- name: cache jit test results
id: cache-jit-test-results
uses: actions/cache@v4
with:
path: ${{env.jit_test_results}}
key: jit-test-results.${{ matrix.os }}.racket_${{ env.racket_version }}.jit_${{ env.jit_version }}.tests_${{env.runtime_tests_causalhash}}
- name: install libb2 (linux)
uses: awalsh128/cache-apt-pkgs-action@latest
if: |
runner.os == 'Linux'
&& steps.cache-jit-test-results.outputs.cache-hit != 'true'
# read this if a package isn't installing correctly
# https://github.com/awalsh128/cache-apt-pkgs-action#caveats
with:
packages: libb2-dev
version: 1.0 # cache key version afaik
- name: install libb2 (macos)
if: |
runner.os == 'macOS'
&& steps.cache-jit-test-results.outputs.cache-hit != 'true'
run: brew install libb2
- name: download jit source
if: steps.cache-jit-binaries.outputs.cache-hit != 'true'
uses: actions/download-artifact@v4
with:
name: jit-source
path: ${{ env.jit_src_scheme }}
- name: build jit binary
if: steps.cache-jit-binaries.outputs.cache-hit != 'true'
shell: bash
run: |
cp -R scheme-libs/racket/* "$jit_src_scheme"
raco pkg install --auto --skip-installed "$jit_src_scheme"/unison
raco exe --embed-dlls "$jit_src_scheme"/unison-runtime.rkt
raco distribute "$jit_dist" "$jit_exe"
- name: save jit binary
uses: actions/upload-artifact@v4
with:
name: jit-binary-${{ matrix.os }}
path: ${{ env.jit_dist }}/**
- name: cache testing codebase
id: cache-testing-codebase
if: steps.cache-jit-test-results.outputs.cache-hit != 'true'
uses: actions/cache@v4
with:
path: ${{ env.runtime_tests_codebase }}
key: runtime-tests-codebase-${{ matrix.os }}-${{env.runtime_tests_causalhash}}
restore-keys: runtime-tests-codebase-${{ matrix.os }}-
- name: download ucm
if: steps.cache-jit-test-results.outputs.cache-hit != 'true'
uses: actions/download-artifact@v4
with:
name: unison-${{ matrix.os }}
path: ${{ runner.temp }}
- name: set ucm permissions
if: steps.cache-jit-test-results.outputs.cache-hit != 'true'
run: chmod +x ${{ env.ucm }}
- name: jit integration test ${{ matrix.os }}
if: runner.os != 'Windows' && steps.cache-jit-test-results.outputs.cache-hit != 'true'
run: |
envsubst '${runtime_tests_version}' \
< unison-src/builtin-tests/jit-tests.tpl.md \
> unison-src/builtin-tests/jit-tests.md
${{ env.ucm }} transcript.fork --runtime-path ${{ env.jit_dist_exe }} -C ${{env.runtime_tests_codebase}} unison-src/builtin-tests/jit-tests.md
cat unison-src/builtin-tests/jit-tests.output.md
git diff --exit-code unison-src/builtin-tests/jit-tests.output.md
- name: mark jit tests as passing
if: runner.os != 'Windows' && steps.cache-jit-test-results.outputs.cache-hit != 'true'
run: |
echo "passing=true" >> "${{env.jit_test_results}}"
# - name: Setup tmate session
# uses: mxschmitt/action-tmate@v3
# if: ${{ failure() }}
# timeout-minutes: 15
test-jit:
name: test jit
needs: build-jit-binary
uses: ./.github/workflows/ci-test-jit.yaml

View File

@ -26,6 +26,9 @@ e.g.
It's not clear to me when to use `$GITHUB_OUTPUT` vs `$GITHUB_ENV`, but I have been favoring `$GITHUB_ENV` because it requires fewer characters to access.
However, it seems a little wrong.
### `hashFiles()`
`hashFiles()` can only access files inside of and relative to `$GITHUB_WORKSPACE`.
### `if:`
Although the type rules don't totally make sense in Github Actions, `if:` takes a Boolean.

View File

@ -96,6 +96,7 @@
{
packages = nixpkgs-packages // {
haskell-nix = haskell-nix-flake.packages;
docker = import ./nix/docker.nix { inherit pkgs; haskell-nix = haskell-nix-flake.packages; };
build-tools = pkgs.symlinkJoin {
name = "build-tools";
paths = self.devShells."${system}".only-tools-nixpkgs.buildInputs;

10
nix/docker.nix Normal file
View File

@ -0,0 +1,10 @@
{ pkgs, haskell-nix }:
{
ucm = pkgs.dockerTools.buildLayeredImage {
name = "ucm";
tag = "latest";
contents = with pkgs; [ cacert fzf ];
config.Cmd = [ "${haskell-nix."unison-cli:exe:unison"}/bin/unison" ];
};
}

View File

@ -24,6 +24,7 @@ module Unison.Codebase.Path
prefix,
unprefix,
prefixName,
prefixName2,
unprefixName,
HQSplit,
Split,
@ -192,6 +193,11 @@ prefix (Absolute (Path prefix)) = \case
AbsolutePath' abs -> unabsolute abs
RelativePath' rel -> Path $ prefix <> toSeq (unrelative rel)
prefix2 :: Path -> Path' -> Path
prefix2 (Path prefix) = \case
AbsolutePath' abs -> unabsolute abs
RelativePath' rel -> Path $ prefix <> toSeq (unrelative rel)
-- | Finds the longest shared path prefix of two paths.
-- Returns (shared prefix, path to first location from shared prefix, path to second location from shared prefix)
--
@ -273,6 +279,9 @@ unprefixName prefix = toName . unprefix prefix . fromName'
prefixName :: Absolute -> Name -> Name
prefixName p n = fromMaybe n . toName . prefix p . fromName' $ n
prefixName2 :: Path -> Name -> Name
prefixName2 p n = fromMaybe n . toName . prefix2 p . fromName' $ n
singleton :: NameSegment -> Path
singleton n = fromList [n]

View File

@ -962,8 +962,8 @@ serializeGroupForRehash fops (Derived h _) sg =
f _ = Nothing
refrep = Map.fromList . mapMaybe f $ groupTermLinks sg
deserializeValue :: ByteString -> Either String Value
deserializeValue bs = runGetS (getVersion >>= getValue) bs
getVersionedValue :: MonadGet m => m Value
getVersionedValue = getVersion >>= getValue
where
getVersion =
getWord32be >>= \case
@ -973,6 +973,9 @@ deserializeValue bs = runGetS (getVersion >>= getValue) bs
| n <= 4 -> pure n
| otherwise -> fail $ "deserializeValue: unknown version: " ++ show n
deserializeValue :: ByteString -> Either String Value
deserializeValue bs = runGetS getVersionedValue bs
serializeValue :: Value -> ByteString
serializeValue v = runPutS (putVersion *> putValue v)
where

View File

@ -11,6 +11,7 @@
-- Checking is toggled using the `arraychecks` flag.
module Unison.Runtime.Array
( module EPA,
byteArrayToList,
readArray,
writeArray,
copyArray,
@ -54,6 +55,9 @@ import Data.Primitive.PrimArray as EPA hiding
)
import Data.Primitive.PrimArray qualified as PA
import Data.Primitive.Types
import Data.Word (Word8)
import GHC.Exts (toList)
#ifdef ARRAY_CHECK
import GHC.Stack
@ -376,3 +380,6 @@ indexPrimArray ::
a
indexPrimArray = checkIPArray "indexPrimArray" PA.indexPrimArray
{-# INLINE indexPrimArray #-}
byteArrayToList :: ByteArray -> [Word8]
byteArrayToList = toList

View File

@ -20,12 +20,18 @@ import Unison.Prelude
import Unison.Reference (Reference, pattern Builtin)
import Unison.Referent (pattern Ref)
import Unison.Runtime.ANF (maskTags)
import Unison.Runtime.Array
( Array
, ByteArray
, byteArrayToList
)
import Unison.Runtime.Foreign
( Foreign (..),
HashAlgorithm (..),
maybeUnwrapBuiltin,
maybeUnwrapForeign,
)
import Unison.Runtime.IOSource (iarrayFromListRef, ibarrayFromBytesRef)
import Unison.Runtime.MCode (CombIx (..))
import Unison.Runtime.Stack
( Closure (..),
@ -63,6 +69,8 @@ import Unison.Type
natRef,
termLinkRef,
typeLinkRef,
iarrayRef,
ibytearrayRef,
)
import Unison.Util.Bytes qualified as By
import Unison.Util.Pretty (indentN, lines, lit, syntaxToColor, wrap)
@ -155,6 +163,8 @@ decompile backref topTerms (DataC rf (maskTags -> ct) [] bs) =
apps' (con rf ct) <$> traverse (decompile backref topTerms) bs
decompile backref topTerms (PApV (CIx rf rt k) [] bs)
| rf == Builtin "jumpCont" = err Cont $ bug "<Continuation>"
| Builtin nm <- rf =
apps' (builtin () nm) <$> traverse (decompile backref topTerms) bs
| Just t <- topTerms rt k =
Term.etaReduceEtaVars . substitute t
<$> traverse (decompile backref topTerms) bs
@ -208,6 +218,15 @@ decompileForeign backref topTerms f
_ -> l
| Just l <- maybeUnwrapForeign typeLinkRef f =
pure $ typeLink () l
| Just (a :: Array Closure) <- maybeUnwrapForeign iarrayRef f =
app () (ref () iarrayFromListRef) . list () <$>
traverse (decompile backref topTerms) (toList a)
| Just (a :: ByteArray) <- maybeUnwrapForeign ibytearrayRef f =
pure $
app
()
(ref () ibarrayFromBytesRef)
(decompileBytes . By.fromWord8s $ byteArrayToList a)
| Just s <- unwrapSeq f =
list' () <$> traverse (decompile backref topTerms) s
decompileForeign _ _ (Wrap r _) =

View File

@ -486,6 +486,12 @@ pattern ConsoleTextUnderline ct <- Term.App' (Term.Constructor' (ConstructorRefe
pattern ConsoleTextInvert ct <- Term.App' (Term.Constructor' (ConstructorReference ConsoleTextRef ((==) consoleTextInvertId -> True))) ct
iarrayFromListRef :: R.Reference
iarrayFromListRef = termNamed "ImmutableArray.fromList"
ibarrayFromBytesRef :: R.Reference
ibarrayFromBytesRef = termNamed "ImmutableByteArray.fromBytes"
constructorNamed :: R.Reference -> Text -> DD.ConstructorId
constructorNamed ref name =
case runIdentity . getTypeDeclaration codeLookup $ R.unsafeId ref of
@ -986,6 +992,35 @@ syntax.docFormatConsole d =
Image alt _link None -> go alt
Special sf -> Pretty.lit (Left sf)
go d
ImmutableArray.fromList l = Scope.run do
sz = List.size l
dst = Scope.array sz
go i = cases
[] -> ()
x +: xs ->
MutableArray.write dst i x
go (i+1) xs
handle go 0 l with cases
{ r } -> ()
{ raise _ -> _ } -> ()
MutableArray.freeze! dst
ImmutableByteArray.fromBytes : Bytes -> ImmutableByteArray
ImmutableByteArray.fromBytes bs = Scope.run do
sz = Bytes.size bs
arr = Scope.bytearray sz
fill i =
match Bytes.at i bs with
Some b ->
MutableByteArray.write8 arr i b
fill (i + 1)
None -> ()
handle fill 0
with cases
{ _ } -> ()
{ raise _ -> _ } -> ()
MutableByteArray.freeze! arr
|]
type Note = Result.Note Symbol Ann

View File

@ -27,10 +27,11 @@ import Data.Binary.Get (runGetOrFail)
-- import Data.Bits (shiftL)
import Data.ByteString qualified as BS
import Data.ByteString.Lazy qualified as BL
import Data.Bytes.Get (MonadGet)
import Data.Bytes.Get (MonadGet, getWord8, runGetS)
import Data.Bytes.Put (MonadPut, putWord32be, runPutL, runPutS)
import Data.Bytes.Serial
import Data.Foldable
import Data.Function (on)
import Data.IORef
import Data.List qualified as L
import Data.Map.Strict qualified as Map
@ -44,9 +45,11 @@ import Data.Set as Set
(\\),
)
import Data.Set qualified as Set
import Data.Text (isPrefixOf, unpack)
import Data.Text as Text (isPrefixOf, pack, unpack)
import GHC.IO.Exception (IOErrorType (NoSuchThing, OtherError, PermissionDenied), IOException (ioe_description, ioe_type))
import GHC.Stack (callStack)
import Network.Simple.TCP (Socket, acceptFork, listen, recv, send)
import Network.Socket (PortNumber, socketPort)
import System.Directory
( XdgDirectory (XdgCache),
createDirectoryIfMissing,
@ -85,6 +88,7 @@ import Unison.Runtime.ANF as ANF
import Unison.Runtime.ANF.Rehash as ANF (rehashGroups)
import Unison.Runtime.ANF.Serialize as ANF
( getGroup,
getVersionedValue,
putGroup,
serializeValue,
)
@ -460,7 +464,18 @@ nativeEval executable ctxVar cl ppe tm = catchInternalErrors $ do
(ctx, codes) <- loadDeps cl ppe ctx tyrs tmrs
(ctx, tcodes, base) <- prepareEvaluation ppe tm ctx
writeIORef ctxVar ctx
nativeEvalInContext executable ppe ctx (codes ++ tcodes) base
-- Note: port 0 mean choosing an arbitrary available port.
-- We then ask what port was actually chosen.
listen "127.0.0.1" "0" $ \(serv, _) ->
socketPort serv >>= \port ->
nativeEvalInContext
executable
ppe
ctx
serv
port
(L.nubBy ((==) `on` fst) $ tcodes ++ codes)
base
interpEval ::
ActiveThreads ->
@ -790,14 +805,46 @@ backReferenceTm ws frs irs dcm c i = do
bs <- Map.lookup r dcm
Map.lookup i bs
ucrProc :: FilePath -> [String] -> CreateProcess
ucrProc executable args =
ucrEvalProc :: FilePath -> [String] -> CreateProcess
ucrEvalProc executable args =
(proc executable args)
{ std_in = Inherit,
std_out = Inherit,
std_err = Inherit
}
ucrCompileProc :: FilePath -> [String] -> CreateProcess
ucrCompileProc executable args =
(proc executable args)
{ std_in = CreatePipe,
std_out = Inherit,
std_err = Inherit
}
receiveAll :: Socket -> IO ByteString
receiveAll sock = read []
where
read acc =
recv sock 4096 >>= \case
Just chunk -> read (chunk : acc)
Nothing -> pure . BS.concat $ reverse acc
data NativeResult
= Success Value
| Bug Text Value
| Error Text
deserializeNativeResponse :: ByteString -> NativeResult
deserializeNativeResponse =
run $
getWord8 >>= \case
0 -> Success <$> getVersionedValue
1 -> Bug <$> getText <*> getVersionedValue
2 -> Error <$> getText
_ -> pure $ Error "Unexpected result bytes tag"
where
run e bs = either (Error . pack) id (runGetS e bs)
-- Note: this currently does not support yielding values; instead it
-- just produces a result appropriate for unitary `run` commands. The
-- reason is that the executed code can cause output to occur, which
@ -813,37 +860,45 @@ nativeEvalInContext ::
FilePath ->
PrettyPrintEnv ->
EvalCtx ->
Socket ->
PortNumber ->
[(Reference, SuperGroup Symbol)] ->
Reference ->
IO (Either Error ([Error], Term Symbol))
nativeEvalInContext executable _ ctx codes base = do
nativeEvalInContext executable ppe ctx serv port codes base = do
ensureRuntimeExists executable
let cc = ccache ctx
crs <- readTVarIO $ combRefs cc
let bytes = serializeValue . compileValue base $ codes
decodeResult (Left msg) = pure . Left $ fromString msg
decodeResult (Right val) =
decodeResult (Error msg) = pure . Left $ text msg
decodeResult (Bug msg val) =
reifyValue cc val >>= \case
Left _ -> pure . Left $ "missing references from bug result"
Right cl ->
pure . Left . bugMsg ppe [] msg $ decompileCtx crs ctx cl
decodeResult (Success val) =
reifyValue cc val >>= \case
Left _ -> pure . Left $ "missing references from result"
Right cl -> case decompileCtx crs ctx cl of
(errs, dv) -> pure $ Right (listErrors errs, dv)
callout (Just pin) _ _ ph = do
BS.hPut pin . runPutS . putWord32be . fromIntegral $ BS.length bytes
BS.hPut pin bytes
UnliftIO.hClose pin
let unit = Data RF.unitRef 0 [] []
sunit = Data RF.pairRef 0 [] [unit, unit]
comm mv (sock, _) = do
send sock . runPutS . putWord32be . fromIntegral $ BS.length bytes
send sock bytes
UnliftIO.putMVar mv =<< receiveAll sock
callout _ _ _ ph = do
mv <- UnliftIO.newEmptyMVar
tid <- acceptFork serv $ comm mv
waitForProcess ph >>= \case
ExitSuccess -> decodeResult $ Right sunit
ExitFailure _ ->
ExitSuccess ->
decodeResult . deserializeNativeResponse
=<< UnliftIO.takeMVar mv
ExitFailure _ -> do
UnliftIO.killThread tid
pure . Left $ "native evaluation failed"
-- TODO: actualy receive output from subprocess
-- decodeResult . deserializeValue =<< BS.hGetContents pout
callout _ _ _ _ =
pure . Left $ "withCreateProcess didn't provide handles"
p = ucrProc executable []
p = ucrEvalProc executable ["-p", show port]
ucrError (e :: IOException) = pure $ Left (runtimeErrMsg (cmdspec p) (Right e))
withCreateProcess p callout
`UnliftIO.catch` ucrError
@ -872,7 +927,7 @@ nativeCompileCodes executable codes base path = do
throwIO $ PE callStack (runtimeErrMsg (cmdspec p) (Right e))
racoError (e :: IOException) =
throwIO $ PE callStack (racoErrMsg (makeRacoCmd RawCommand) (Right e))
p = ucrProc executable ["-G", srcPath]
p = ucrCompileProc executable ["-G", srcPath]
makeRacoCmd :: (FilePath -> [String] -> a) -> a
makeRacoCmd f = f "raco" ["exe", "-o", path, srcPath]
withCreateProcess p callout
@ -953,7 +1008,7 @@ bugMsg ::
Pretty ColorText
bugMsg ppe tr name (errs, tm)
| name == "blank expression" =
P.callout icon . P.lines $
P.callout icon . P.linesNonEmpty $
[ P.wrap
( "I encountered a"
<> P.red (P.text name)
@ -965,7 +1020,7 @@ bugMsg ppe tr name (errs, tm)
stackTrace ppe tr
]
| "pattern match failure" `isPrefixOf` name =
P.callout icon . P.lines $
P.callout icon . P.linesNonEmpty $
[ P.wrap
( "I've encountered a"
<> P.red (P.text name)
@ -980,7 +1035,7 @@ bugMsg ppe tr name (errs, tm)
stackTrace ppe tr
]
| name == "builtin.raise" =
P.callout icon . P.lines $
P.callout icon . P.linesNonEmpty $
[ P.wrap ("The program halted with an unhandled exception:"),
"",
P.indentN 2 $ pretty ppe tm,
@ -990,7 +1045,7 @@ bugMsg ppe tr name (errs, tm)
| name == "builtin.bug",
RF.TupleTerm' [Tm.Text' msg, x] <- tm,
"pattern match failure" `isPrefixOf` msg =
P.callout icon . P.lines $
P.callout icon . P.linesNonEmpty $
[ P.wrap
( "I've encountered a"
<> P.red (P.text msg)
@ -1005,7 +1060,7 @@ bugMsg ppe tr name (errs, tm)
stackTrace ppe tr
]
bugMsg ppe tr name (errs, tm) =
P.callout icon . P.lines $
P.callout icon . P.linesNonEmpty $
[ P.wrap
( "I've encountered a call to"
<> P.red (P.text name)
@ -1018,7 +1073,8 @@ bugMsg ppe tr name (errs, tm) =
]
stackTrace :: PrettyPrintEnv -> [(Reference, Int)] -> Pretty ColorText
stackTrace ppe tr = "Stack trace:\n" <> P.indentN 2 (P.lines $ f <$> tr)
stackTrace _ [] = mempty
stackTrace ppe tr = "\nStack trace:\n" <> P.indentN 2 (P.lines $ f <$> tr)
where
f (rf, n) = name <> count
where
@ -1165,10 +1221,11 @@ listErrors :: Set DecompError -> [Error]
listErrors = fmap (P.indentN 2 . renderDecompError) . toList
tabulateErrors :: Set DecompError -> Error
tabulateErrors errs | null errs = "\n"
tabulateErrors errs | null errs = mempty
tabulateErrors errs =
P.indentN 2 . P.lines $
P.wrap "The following errors occured while decompiling:"
""
: P.wrap "The following errors occured while decompiling:"
: (listErrors errs)
restoreCache :: StoredCache -> IO CCache

View File

@ -37,10 +37,10 @@
;
; - 4 bytes indicating how many bytes follow
; - the actual payload, with size matching the above
(define (grab-bytes)
(let* ([size-bytes (read-bytes 4)]
(define (grab-bytes port)
(let* ([size-bytes (read-bytes 4 port)]
[size (integer-bytes->integer size-bytes #f #t 0 4)])
(read-bytes size)))
(read-bytes size port)))
; Reads and decodes the input. First uses `grab-bytes` to read the
; payload, then uses unison functions to deserialize the `Value` that
@ -50,8 +50,8 @@
; definition should be executed. In unison types, it is:
;
; ([(Link.Term, Code)], Link.Term)
(define (decode-input)
(let ([bs (grab-bytes)])
(define (decode-input port)
(let ([bs (grab-bytes port)])
(match (builtin-Value.deserialize (bytes->chunked-bytes bs))
[(unison-data _ t (list q))
(= t ref-either-right:tag)
@ -61,15 +61,65 @@
[else
(raise "unexpected input")])))
(define (natural->bytes/variable n)
(let rec ([i n] [acc '()])
(cond
[(< i #x80) (list->bytes (reverse (cons i acc)))]
[else
(rec (arithmetic-shift i -7)
(cons (bitwise-and i #x7f) acc))])))
(define (write-string-bytes str port)
(define bs (string->bytes/utf-8 str))
(write-bytes (natural->bytes/variable (bytes-length bs)) port)
(write-bytes bs port))
(define (write-value-bytes val port)
(define qval (unison-quote (reflect-value val)))
(define bs (chunked-bytes->bytes (builtin-Value.serialize qval)))
(write-bytes bs port))
(define (encode-success result port)
(write-bytes #"\0" port)
(write-value-bytes result port)
(void))
(define (encode-error ex port)
(match ex
[(exn:bug msg val)
(write-bytes #"\1" port)
(write-string-bytes msg port)
(write-value-bytes val port)]
[else
(write-bytes #"\2" port)
(write-string-bytes (exception->string ex) port)])
(void))
(define (encode-exception fail port)
(write-bytes #"\1" port)
(write-string-bytes "builtin.raise" port)
(write-value-bytes fail port)
(void))
(define ((eval-exn-handler port) rq)
(request-case rq
[pure (result) (encode-success result port)]
[ref-exception:typelink
[0 (fail)
(control ref-exception:typelink k
(encode-exception fail port))]]))
; Implements the evaluation mode of operation. First decodes the
; input. Then uses the dynamic loading machinery to add the code to
; the runtime. Finally executes a specified main reference.
(define (do-evaluate)
(let-values ([(code main-ref) (decode-input)])
(define (do-evaluate in out)
(let-values ([(code main-ref) (decode-input in)])
(add-runtime-code 'unison-main code)
(handle [ref-exception:typelink] top-exn-handler
((termlink->proc main-ref))
(data 'unit 0))))
(with-handlers
([exn:bug? (lambda (e) (encode-error e out))])
(handle [ref-exception:typelink] (eval-exn-handler out)
((termlink->proc main-ref))))))
; Uses racket pretty printing machinery to instead generate a file
; containing the given code, and which executes the main definition on
@ -89,11 +139,12 @@
; Decodes input and writes a module to the specified file.
(define (do-generate srcf)
(define-values (icode main-ref) (decode-input))
(define-values (icode main-ref) (decode-input (current-input-port)))
(write-module srcf main-ref icode))
(define generate-to (make-parameter #f))
(define show-version (make-parameter #f))
(define use-port-num (make-parameter #f))
(define (handle-command-line)
(command-line
@ -102,14 +153,36 @@
["--version"
"display version"
(show-version #t)]
[("-p" "--port")
port-num
"runtime communication port"
(use-port-num port-num)]
[("-G" "--generate-file")
file
"generate code to <file>"
(generate-to file)]))
(generate-to file)]
#:args remaining
(list->vector remaining)))
(begin
(handle-command-line)
(let ([sub-args (handle-command-line)])
(current-command-line-arguments sub-args))
(cond
[(show-version) (displayln "unison-runtime version 0.0.11")]
[(generate-to) (do-generate (generate-to))]
[else (do-evaluate)]))
[(use-port-num)
(match (string->number (use-port-num))
[port
#:when (port-number? port)
(let-values ([(in out) (tcp-connect "localhost" port)])
(do-evaluate in out)
(close-output-port out)
(close-input-port in))]
[#f
(displayln "could not parse port number")
(exit 1)]
[port
(displayln "bad port number")
(exit 1)])]
[else
(do-evaluate (current-input-port) (open-output-bytes))]))

View File

@ -78,7 +78,7 @@
declare-function-link
declare-code
exn:bug?
(struct-out exn:bug)
exn:bug->exception
exception->string
raise-unison-exception
@ -568,7 +568,7 @@
(let ([disp (describe-value f)])
(raise
(make-exn:bug
(string->chunked-string "builtin.bug")
(string->chunked-string "unhandled top level exception")
disp))))]]))
(begin-encourage-inline

View File

@ -2,6 +2,8 @@
(require ffi/unsafe
ffi/unsafe/define
racket/exn
racket/runtime-path
(for-syntax racket/base)
openssl/libcrypto
unison/chunked-seq)
@ -19,13 +21,11 @@
hashBytes
hmacBytes)))
(define libcrypto
(with-handlers [[exn:fail? exn->string]]
(ffi-lib "libcrypto" openssl-lib-versions)))
(define-runtime-path libb2-so '(so "libb2" ("1" #f)))
(define libb2
(with-handlers [[exn:fail? exn->string]]
(ffi-lib "libb2" '("" "1"))))
(ffi-lib libb2-so '("1" #f))))
(define _EVP-pointer (_cpointer 'EVP))

View File

@ -46,6 +46,7 @@
; some exports of internal machinery for use elsewhere
gen-code
reify-value
reflect-value
termlink->name
add-runtime-code

View File

@ -34,9 +34,9 @@ main = do
I found and typechecked these definitions in scratch.u. If you
do an `add` or `update`, here's how your codebase would
change:
⍟ These new definitions are ok to `add`:
structural ability Break
type MyBool
main : '{IO, Exception} ()
@ -47,7 +47,7 @@ main = do
.> add
⍟ I've added these definitions:
structural ability Break
type MyBool
main : '{IO, Exception} ()

View File

@ -1429,30 +1429,28 @@ handleFindI ::
Cli ()
handleFindI isVerbose fscope ws input = do
Cli.Env {codebase} <- ask
currentBranch0 <- Cli.getCurrentBranch0
(pped, names) <- case fscope of
FindLocal -> do
let names = Branch.toNames (Branch.withoutLib currentBranch0)
(pped, names, searchRoot, branch0) <- case fscope of
FindLocal p -> do
searchRoot <- Cli.resolvePath p
branch0 <- Cli.getBranch0At searchRoot
let names = Branch.toNames (Branch.withoutLib branch0)
-- Don't exclude anything from the pretty printer, since the type signatures we print for
-- results may contain things in lib.
pped <- Cli.currentPrettyPrintEnvDecl
pure (pped, names)
FindLocalSubnamespace p -> do
path <- Cli.resolvePath p
branch0 <- Cli.getBranch0At path
let names = Branch.toNames branch0
pped <- Cli.currentPrettyPrintEnvDecl
pure (pped, names)
FindLocalAndDeps -> do
let names = Branch.toNames (Branch.withoutTransitiveLibs currentBranch0)
pure (pped, names, Just p, branch0)
FindLocalAndDeps p -> do
searchRoot <- Cli.resolvePath p
branch0 <- Cli.getBranch0At searchRoot
let names = Branch.toNames (Branch.withoutTransitiveLibs branch0)
-- Don't exclude anything from the pretty printer, since the type signatures we print for
-- results may contain things in lib.
pped <- Cli.currentPrettyPrintEnvDecl
pure (pped, names)
pure (pped, names, Just p, branch0)
FindGlobal -> do
globalNames <- Names.makeAbsolute . Branch.toNames <$> Cli.getRootBranch0
pped <- Cli.prettyPrintEnvDeclFromNames globalNames
pure (pped, globalNames)
currentBranch0 <- Cli.getCurrentBranch0
pure (pped, globalNames, Nothing, currentBranch0)
let suffixifiedPPE = PPED.suffixifiedPPE pped
let getResults :: Names -> Cli [SearchResult]
getResults names =
@ -1461,7 +1459,7 @@ handleFindI isVerbose fscope ws input = do
-- type query
":" : ws -> do
typ <- parseSearchType (show input) (unwords ws)
let keepNamed = Set.intersection (Branch.deepReferents currentBranch0)
let keepNamed = Set.intersection (Branch.deepReferents branch0)
(noExactTypeMatches, matches) <- do
Cli.runTransaction do
matches <- keepNamed <$> Codebase.termsOfType codebase typ
@ -1487,16 +1485,16 @@ handleFindI isVerbose fscope ws input = do
(mapMaybe (HQ.parseTextWith anythingBeforeHash . Text.pack) qs)
pure $ uniqueBy SR.toReferent srs
let respondResults results = do
Cli.setNumberedArgs $ fmap searchResultToHQString results
Cli.setNumberedArgs $ fmap (searchResultToHQString searchRoot) results
results' <- Cli.runTransaction (Backend.loadSearchResults codebase results)
Cli.respond $ ListOfDefinitions fscope suffixifiedPPE isVerbose results'
results <- getResults names
case (results, fscope) of
([], FindLocal) -> do
([], FindLocal {}) -> do
Cli.respond FindNoLocalMatches
-- We've already searched everything else, so now we search JUST the
-- names in lib.
let mayOnlyLibBranch = currentBranch0 & Branch.children %%~ \cs -> Map.singleton NameSegment.libSegment <$> Map.lookup NameSegment.libSegment cs
let mayOnlyLibBranch = branch0 & Branch.children %%~ \cs -> Map.singleton NameSegment.libSegment <$> Map.lookup NameSegment.libSegment cs
case mayOnlyLibBranch of
Nothing -> respondResults []
Just onlyLibBranch -> do
@ -1810,11 +1808,14 @@ confirmedCommand i = do
pure $ Just i == (loopState ^. #lastInput)
-- | restores the full hash to these search results, for _numberedArgs purposes
searchResultToHQString :: SearchResult -> String
searchResultToHQString = \case
SR.Tm' n r _ -> Text.unpack $ HQ.toText $ HQ.requalify n r
SR.Tp' n r _ -> Text.unpack $ HQ.toText $ HQ.requalify n (Referent.Ref r)
searchResultToHQString :: Maybe Path -> SearchResult -> String
searchResultToHQString oprefix = \case
SR.Tm' n r _ -> Text.unpack $ HQ.toText $ HQ.requalify (addPrefix <$> n) r
SR.Tp' n r _ -> Text.unpack $ HQ.toText $ HQ.requalify (addPrefix <$> n) (Referent.Ref r)
_ -> error "impossible match failure"
where
addPrefix :: Name -> Name
addPrefix = maybe id Path.prefixName2 oprefix
-- return `name` and `name.<everything>...`
_searchBranchPrefix :: Branch m -> Name -> [SearchResult]

View File

@ -8,7 +8,6 @@ where
import Control.Concurrent.STM (atomically, modifyTVar', newTVarIO, readTVar, readTVarIO)
import Control.Lens (over, view, (.~), (^.), _1, _2)
import Control.Monad.Reader (ask)
import Data.List.NonEmpty qualified as Nel
import Data.Set.NonEmpty qualified as Set.NonEmpty
import Data.Text as Text
import Data.These (These (..))
@ -18,7 +17,6 @@ import Text.Builder qualified
import U.Codebase.HashTags (CausalHash (..))
import U.Codebase.Sqlite.DbId
import U.Codebase.Sqlite.Operations qualified as Operations
import U.Codebase.Sqlite.Operations qualified as Ops
import U.Codebase.Sqlite.Project qualified as Sqlite (Project)
import U.Codebase.Sqlite.ProjectBranch qualified as Sqlite (ProjectBranch)
import U.Codebase.Sqlite.Queries qualified as Queries
@ -32,7 +30,6 @@ import Unison.Codebase (PushGitBranchOpts (..))
import Unison.Codebase qualified as Codebase
import Unison.Codebase.Branch (Branch (..))
import Unison.Codebase.Branch qualified as Branch
import Unison.Codebase.Editor.HandleInput.AuthLogin (ensureAuthenticatedWithCodeserver)
import Unison.Codebase.Editor.HandleInput.AuthLogin qualified as AuthLogin
import Unison.Codebase.Editor.Input
( GistInput (..),
@ -46,13 +43,11 @@ import Unison.Codebase.Editor.Output.PushPull (PushPull (Push))
import Unison.Codebase.Editor.RemoteRepo
( ReadGitRemoteNamespace (..),
ReadRemoteNamespace (..),
ShareUserHandle (..),
WriteGitRemoteNamespace (..),
WriteRemoteNamespace (..),
WriteShareRemoteNamespace (..),
writeToReadGit,
)
import Unison.Codebase.Path (Path)
import Unison.Codebase.Path qualified as Path
import Unison.Codebase.PushBehavior (PushBehavior)
import Unison.Codebase.PushBehavior qualified as PushBehavior
@ -231,77 +226,8 @@ pushLooseCodeToGitLooseCode localPath gitRemotePath pushBehavior syncMode = do
-- Push a local namespace ("loose code") to a Share-hosted remote namespace ("loose code").
pushLooseCodeToShareLooseCode :: Path.Absolute -> WriteShareRemoteNamespace -> PushBehavior -> Cli ()
pushLooseCodeToShareLooseCode localPath remote@WriteShareRemoteNamespace {server, repo, path = remotePath} behavior = do
let codeserver = Codeserver.resolveCodeserver server
let baseURL = codeserverBaseURL codeserver
let sharePath = Share.Path (shareUserHandleToText repo Nel.:| pathToSegments remotePath)
_ <- ensureAuthenticatedWithCodeserver codeserver
localCausalHash <-
Cli.runTransaction (Ops.loadCausalHashAtPath Nothing (pathToSegments (Path.unabsolute localPath))) & onNothingM do
Cli.returnEarly (EmptyLooseCodePush (Path.absoluteToPath' localPath))
let checkAndSetPush :: Maybe Hash32 -> Cli (Maybe Int)
checkAndSetPush remoteHash =
if Just (Hash32.fromHash (unCausalHash localCausalHash)) == remoteHash
then pure Nothing
else do
let push =
Cli.with withEntitiesUploadedProgressCallback \(uploadedCallback, getNumUploaded) -> do
result <-
Share.checkAndSetPush
baseURL
sharePath
remoteHash
localCausalHash
uploadedCallback
numUploaded <- liftIO getNumUploaded
pure (result, numUploaded)
push >>= \case
(Left err, _) -> pushError ShareErrorCheckAndSetPush err
(Right (), numUploaded) -> pure (Just numUploaded)
case behavior of
PushBehavior.ForcePush -> do
maybeHashJwt <-
Share.getCausalHashByPath baseURL sharePath & onLeftM \err0 ->
(Cli.returnEarly . Output.ShareError) case err0 of
Share.SyncError err -> ShareErrorGetCausalHashByPath err
Share.TransportError err -> ShareErrorTransport err
maybeNumUploaded <- checkAndSetPush (Share.API.hashJWTHash <$> maybeHashJwt)
whenJust maybeNumUploaded (Cli.respond . Output.UploadedEntities)
Cli.respond (ViewOnShare (Left remote))
PushBehavior.RequireEmpty -> do
maybeNumUploaded <- checkAndSetPush Nothing
whenJust maybeNumUploaded (Cli.respond . Output.UploadedEntities)
Cli.respond (ViewOnShare (Left remote))
PushBehavior.RequireNonEmpty -> do
let push :: Cli (Either (Share.SyncError Share.FastForwardPushError) (), Int)
push =
Cli.with withEntitiesUploadedProgressCallback \(uploadedCallback, getNumUploaded) -> do
result <-
Share.fastForwardPush
baseURL
sharePath
localCausalHash
uploadedCallback
numUploaded <- liftIO getNumUploaded
pure (result, numUploaded)
push >>= \case
(Left err, _) -> pushError ShareErrorFastForwardPush err
(Right (), numUploaded) -> do
Cli.respond (UploadedEntities numUploaded)
Cli.respond (ViewOnShare (Left remote))
where
pathToSegments :: Path -> [Text]
pathToSegments =
coerce Path.toList
pushError :: (a -> Output.ShareError) -> Share.SyncError a -> Cli b
pushError f err0 = do
Cli.returnEarly case err0 of
Share.SyncError err -> Output.ShareError (f err)
Share.TransportError err -> Output.ShareError (ShareErrorTransport err)
pushLooseCodeToShareLooseCode _ _ _ = do
Cli.returnEarly LooseCodePushDeprecated
-- Push a local namespace ("loose code") to a remote project branch.
pushLooseCodeToProjectBranch :: Bool -> Path.Absolute -> ProjectAndBranch ProjectName ProjectBranchName -> Cli ()

View File

@ -313,9 +313,8 @@ data OutputLocation
deriving (Eq, Show)
data FindScope
= FindLocal
| FindLocalAndDeps
| FindLocalSubnamespace Path
= FindLocal Path
| FindLocalAndDeps Path
| FindGlobal
deriving stock (Eq, Show)

View File

@ -394,6 +394,7 @@ data Output
| UpdateIncompleteConstructorSet UpdateOrUpgrade Name (Map ConstructorId Name) (Maybe Int)
| UpgradeFailure !FilePath !NameSegment !NameSegment
| UpgradeSuccess !NameSegment !NameSegment
| LooseCodePushDeprecated
data UpdateOrUpgrade = UOUUpdate | UOUUpgrade
@ -622,6 +623,7 @@ isFailure o = case o of
ProjectHasNoReleases {} -> True
UpgradeFailure {} -> True
UpgradeSuccess {} -> False
LooseCodePushDeprecated -> True
isNumberedFailure :: NumberedOutput -> Bool
isNumberedFailure = \case

View File

@ -30,7 +30,7 @@ import Unison.Cli.ProjectUtils qualified as ProjectUtils
import Unison.Codebase (Codebase)
import Unison.Codebase qualified as Codebase
import Unison.Codebase.Branch.Merge qualified as Branch
import Unison.Codebase.Editor.Input (DeleteOutput (..), DeleteTarget (..), FindScope (FindLocalSubnamespace), Input)
import Unison.Codebase.Editor.Input (DeleteOutput (..), DeleteTarget (..), Input)
import Unison.Codebase.Editor.Input qualified as Input
import Unison.Codebase.Editor.Output.PushPull (PushPull (Pull, Push))
import Unison.Codebase.Editor.Output.PushPull qualified as PushPull
@ -531,7 +531,7 @@ sfind :: InputPattern
sfind =
InputPattern "rewrite.find" ["sfind"] I.Visible [("rewrite-rule definition", Required, definitionQueryArg)] msg parse
where
parse [q] = Input.StructuredFindI Input.FindLocal <$> parseHashQualifiedName q
parse [q] = Input.StructuredFindI (Input.FindLocal Path.empty) <$> parseHashQualifiedName q
parse _ = Left "expected exactly one argument"
msg =
P.lines
@ -589,27 +589,31 @@ sfindReplace =
]
find :: InputPattern
find = find' "find" Input.FindLocal
find = find' "find" (Input.FindLocal Path.empty)
findAll :: InputPattern
findAll = find' "find.all" Input.FindLocalAndDeps
findAll = find' "find.all" (Input.FindLocalAndDeps Path.empty)
findGlobal :: InputPattern
findGlobal = find' "find.global" Input.FindGlobal
findIn :: InputPattern
findIn =
findIn, findInAll :: InputPattern
findIn = findIn' "find-in" Input.FindLocal
findInAll = findIn' "find-in.all" Input.FindLocalAndDeps
findIn' :: String -> (Path.Path -> Input.FindScope) -> InputPattern
findIn' cmd mkfscope =
InputPattern
"find-in"
cmd
[]
I.Visible
[("namespace", Required, namespaceArg)]
[("namespace", Required, namespaceArg), ("query", ZeroPlus, exactDefinitionArg)]
findHelp
\case
p : args -> first P.text do
p <- Path.parsePath p
pure (Input.FindI False (FindLocalSubnamespace p) args)
_ -> Left (I.help findIn)
pure (Input.FindI False (mkfscope p) args)
_ -> Left findHelp
findHelp :: P.Pretty CT.ColorText
findHelp =
@ -623,7 +627,9 @@ findHelp =
"lists all definitions with a name similar to 'foo' or 'bar' in the "
<> "current namespace (excluding those under 'lib')."
),
("`find-in namespace`", "lists all definitions in the specified subnamespace."),
( "`find-in namespace`",
"lists all definitions in the specified subnamespace."
),
( "`find-in namespace foo bar`",
"lists all definitions with a name similar to 'foo' or 'bar' in the "
<> "specified subnamespace."
@ -632,6 +638,13 @@ findHelp =
"lists all definitions with a name similar to 'foo' in the current "
<> "namespace (including one level of 'lib')."
),
( "`find-in.all namespace`",
"lists all definitions in the specified subnamespace (including one level of its 'lib')."
),
( "`find-in.all namespace foo bar`",
"lists all definitions with a name similar to 'foo' or 'bar' in the "
<> "specified subnamespace (including one level of its 'lib')."
),
( "find.global foo",
"lists all definitions with a name similar to 'foo' in any namespace"
)
@ -679,7 +692,7 @@ findVerbose =
( "`find.verbose` searches for definitions like `find`, but includes hashes "
<> "and aliases in the results."
)
(pure . Input.FindI True Input.FindLocal)
(pure . Input.FindI True (Input.FindLocal Path.empty))
findVerboseAll :: InputPattern
findVerboseAll =
@ -691,7 +704,7 @@ findVerboseAll =
( "`find.all.verbose` searches for definitions like `find.all`, but includes hashes "
<> "and aliases in the results."
)
(pure . Input.FindI True Input.FindLocalAndDeps)
(pure . Input.FindI True (Input.FindLocalAndDeps Path.empty))
findPatch :: InputPattern
findPatch =
@ -993,7 +1006,7 @@ up =
InputPattern
"deprecated.up"
[]
I.Visible
I.Hidden
[]
(P.wrapColumn2 [(makeExample up [], "move current path up one level (deprecated)")])
( \case
@ -1043,7 +1056,7 @@ back =
[]
( P.wrapColumn2
[ ( makeExample back [],
"undoes the last" <> makeExample' projectSwitch <> "or" <> makeExample' cd <> "command."
"undoes the last" <> makeExample' projectSwitch <> "command."
)
]
)
@ -1254,12 +1267,12 @@ reset =
resetRoot :: InputPattern
resetRoot =
InputPattern
"deprecated.reset-root"
"reset-root"
[]
I.Visible
I.Hidden
[("namespace or hash to reset to", Required, namespaceArg)]
( P.lines
[ "Deprecated because it's not compatible with the introduction of projects.",
[ "Deprecated because it's incompatible with projects. ⚠️ Warning, this command can cause codebase corruption.",
P.wrapColumn2
[ ( makeExample resetRoot [".foo"],
"Reset the root namespace (along with its history) to that of the `.foo` namespace. Deprecated"
@ -3010,6 +3023,7 @@ validInputs =
find,
findIn,
findAll,
findInAll,
findGlobal,
findPatch,
findShallow,

View File

@ -2223,8 +2223,16 @@ notifyUser dir = \case
<> P.group (P.text (NameSegment.toEscapedText new) <> ",")
<> "and removed"
<> P.group (P.text (NameSegment.toEscapedText old) <> ".")
where
_nameChange _cmd _pastTenseCmd _oldName _newName _r = error "todo"
LooseCodePushDeprecated ->
pure . P.warnCallout $
P.lines $
[ P.wrap $ "Unison Share's projects are now the new preferred way to store code, and storing code outside of a project has been deprecated.",
"",
P.wrap $ "Learn how to convert existing code into a project using this guide: ",
"https://www.unison-lang.org/docs/tooling/projects-library-migration/",
"",
"Your non-project code is still available to pull from Share, and you can pull it into a local namespace using `pull myhandle.public`"
]
expectedEmptyPushDest :: WriteRemoteNamespace Void -> Pretty
expectedEmptyPushDest namespace =

View File

@ -286,7 +286,7 @@ test =
```ucm
.> pull git(${repo})
.> history
.> deprecated.reset-root #l43v9nr16v
.> reset-root #l43v9nr16v
.> history
```
|] -- Not sure why this hash is here.

File diff suppressed because it is too large Load Diff

View File

@ -53,7 +53,6 @@ test2 = do
"Cannot decode byte '\\xee': Data.Text.Internal.Encoding.decodeUtf8: Invalid UTF-8 stream"
(Any ())
Stack trace:
##raise
@ -67,7 +66,6 @@ test2 = do
Failure (typeLink RuntimeFailure) "builtin.bug" (Any "whoa")
Stack trace:
##raise

View File

@ -35,7 +35,7 @@ And for a limited time, you can get even more builtin goodies:
.foo> ls
1. builtin/ (628 terms, 89 types)
1. builtin/ (630 terms, 89 types)
```
More typically, you'd start out by pulling `base.

View File

@ -1,23 +1,44 @@
```unison
```ucm:hide
.> builtins.merge
.> move builtin lib.builtin
```
```unison:hide
foo = 1
lib.foo = 2
lib.bar = 3
cat.foo = 4
cat.lib.foo = 5
cat.lib.bar = 6
somewhere.bar = 7
```
```ucm
```ucm:hide
.> add
```
```ucm
.> find foo
.> view 1
.> find.all foo
.> view 1
```
```ucm
.somewhere> find.global foo
.> find-in cat foo
.> view 1
.> find-in.all cat foo
.> view 1
```
```ucm
.somewhere> find bar
.somewhere> find.global bar
```
```ucm
.> find bar
.> find-in somewhere bar
```
```ucm:error
@ -25,5 +46,5 @@ lib.bar = 3
```
```ucm:error
.> find.global nothere
.> find.global notHere
```

View File

@ -2,59 +2,84 @@
foo = 1
lib.foo = 2
lib.bar = 3
cat.foo = 4
cat.lib.foo = 5
cat.lib.bar = 6
somewhere.bar = 7
```
```ucm
Loading changes detected in scratch.u.
I found and typechecked these definitions in scratch.u. If you
do an `add` or `update`, here's how your codebase would
change:
⍟ These new definitions are ok to `add`:
foo : ##Nat
lib.bar : ##Nat
lib.foo : ##Nat
```
```ucm
.> add
⍟ I've added these definitions:
foo : ##Nat
lib.bar : ##Nat
lib.foo : ##Nat
```
```ucm
.> find foo
1. foo : ##Nat
1. cat.foo : Nat
2. foo : Nat
.> view 1
cat.foo : Nat
cat.foo = 4
.> find.all foo
1. cat.foo : Nat
2. cat.lib.foo : Nat
3. lib.foo : Nat
4. foo : Nat
.> view 1
cat.foo : Nat
cat.foo = 4
```
```ucm
☝️ The namespace .somewhere is empty.
.> find-in cat foo
.somewhere> find.global foo
1. foo : Nat
1. .foo : ##Nat
2. .lib.foo : ##Nat
.> view 1
cat.foo : Nat
cat.foo = 4
.> find-in.all cat foo
1. lib.foo : Nat
2. foo : Nat
.> view 1
cat.lib.foo : Nat
cat.lib.foo = 5
```
```ucm
.somewhere> find bar
1. bar : ##Nat
.somewhere> find.global bar
1. .cat.lib.bar : Nat
2. .lib.bar : Nat
3. .somewhere.bar : Nat
```
```ucm
.> find bar
☝️
1. somewhere.bar : Nat
I couldn't find matches in this namespace, searching in
'lib'...
1. lib.bar : ##Nat
.> find-in somewhere bar
1. bar : Nat
```
@ -76,7 +101,7 @@ lib.bar = 3
```
```ucm
.> find.global nothere
.> find.global notHere
😶

View File

@ -89,7 +89,6 @@ myServer = unsafeRun! '(hello "127.0.0.1" "0")
Failure (typeLink IOFailure) "problem" (Any ())
Stack trace:
bug
#8ppr1tt4q2

View File

@ -0,0 +1,10 @@
```ucm:hide
.> builtins.merge
```
Just a simple test case to see whether partially applied
builtins decompile properly.
```unison
> (+) 2
```

View File

@ -0,0 +1,23 @@
Just a simple test case to see whether partially applied
builtins decompile properly.
```unison
> (+) 2
```
```ucm
Loading changes detected in scratch.u.
scratch.u changed.
Now evaluating any watch expressions (lines starting with
`>`)... Ctrl+C cancels.
1 | > (+) 2
(Nat.+) 2
```

View File

@ -614,7 +614,6 @@ Calling our examples with the wrong number of args will error.
Failure (typeLink IOFailure) "called with args" (Any ())
Stack trace:
##raise
@ -628,7 +627,6 @@ Calling our examples with the wrong number of args will error.
Failure (typeLink IOFailure) "called with no args" (Any ())
Stack trace:
##raise
@ -643,7 +641,6 @@ Calling our examples with the wrong number of args will error.
Failure
(typeLink IOFailure) "called with too many args" (Any ())
Stack trace:
##raise
@ -657,7 +654,6 @@ Calling our examples with the wrong number of args will error.
Failure (typeLink IOFailure) "called with no args" (Any ())
Stack trace:
##raise

View File

@ -24,7 +24,7 @@ y = 2
If we `reset-root` to its previous value, `y` disappears.
```ucm
.> deprecated.reset-root 2
.> reset-root 2
```
```ucm:error
.> view y

View File

@ -62,15 +62,13 @@ y = 2
Here is a log of the root namespace hashes, starting with the
most recent, along with the command that got us there. Try:
`fork 2 .old`
`fork #mq4oqhiuuq .old` to make an old
namespace accessible
again,
`deprecated.reset-root #mq4oqhiuuq` to reset the root
namespace and its
history to that of the
specified namespace.
`fork 2 .old`
`fork #mq4oqhiuuq .old` to make an old namespace
accessible again,
`reset-root #mq4oqhiuuq` to reset the root namespace and
its history to that of the
specified namespace.
When Root Hash Action
1. now #1n5tjujeu7 add
@ -84,7 +82,7 @@ y = 2
```
If we `reset-root` to its previous value, `y` disappears.
```ucm
.> deprecated.reset-root 2
.> reset-root 2
Done.

View File

@ -23,7 +23,6 @@
"implement me later"
Stack trace:
todo
#qe5e1lcfn8
@ -51,7 +50,6 @@
"there's a bug in my code"
Stack trace:
bug
#m67hcdcoda

View File

@ -98,7 +98,6 @@ unique type RuntimeError =
Failure (typeLink RuntimeError) "oh noes!" (Any ())
Stack trace:
##raise

View File

@ -1,5 +1,5 @@
```ucm
.> builtins.merge
.> builtins.mergeio
```
```unison
@ -20,7 +20,6 @@ test> pass = [Ok "Passed"]
```
```unison
> Scope.run do
freeze! (Scope.arrayOf 0 0)
> ImmutableArray.fromList [?a, ?b, ?c]
> ImmutableByteArray.fromBytes 0xs123456
```

View File

@ -1,5 +1,5 @@
```ucm
.> builtins.merge
.> builtins.mergeio
Done.
@ -72,9 +72,8 @@ test> pass = [Ok "Passed"]
```
```unison
> Scope.run do
freeze! (Scope.arrayOf 0 0)
> ImmutableArray.fromList [?a, ?b, ?c]
> ImmutableByteArray.fromBytes 0xs123456
```
```ucm
@ -88,17 +87,12 @@ test> pass = [Ok "Passed"]
Now evaluating any watch expressions (lines starting with
`>`)... Ctrl+C cancels.
⚠️
I had trouble decompiling some results.
The following errors were encountered:
A foreign value with no decompiled representation was
encountered:
##ImmutableArray
1 | > Scope.run do
1 | > ImmutableArray.fromList [?a, ?b, ?c]
bug "<Foreign>"
ImmutableArray.fromList [?a, ?b, ?c]
2 | > ImmutableByteArray.fromBytes 0xs123456
fromBytes 0xs123456
```