Merge branch 'main' into specialize-exprs

This commit is contained in:
Agus Zubiaga 2024-11-23 01:48:51 -03:00
commit 2e96aca0fd
No known key found for this signature in database
797 changed files with 17394 additions and 12632 deletions

1
.gitattributes vendored
View file

@ -1,4 +1,5 @@
# Require roc files to be checked out with Unix line endings, even on windows # Require roc files to be checked out with Unix line endings, even on windows
*.roc text eol=lf *.roc text eol=lf
* text=auto eol=lf
crates/compiler/test_mono/generated/* linguist-generated=true crates/compiler/test_mono/generated/* linguist-generated=true

View file

@ -1,5 +1,5 @@
on: on:
#pull_request: # pull_request:
workflow_dispatch: workflow_dispatch:
# this cancels workflows currently in progress if you start a new one # this cancels workflows currently in progress if you start a new one
@ -11,7 +11,7 @@ env:
# use .tar.gz for quick testing # use .tar.gz for quick testing
ARCHIVE_FORMAT: .tar.br ARCHIVE_FORMAT: .tar.br
# Make a new basic-cli git tag and set it here before starting this workflow # Make a new basic-cli git tag and set it here before starting this workflow
RELEASE_TAG: 0.14.0 RELEASE_TAG: 0.16.0
jobs: jobs:
prepare: prepare:
@ -34,14 +34,14 @@ jobs:
fi fi
# get latest nightly releases # get latest nightly releases
#- run: curl -fOL https://github.com/roc-lang/roc/releases/download/nightly/roc_nightly-linux_x86_64-latest.tar.gz - run: curl -fOL https://github.com/roc-lang/roc/releases/download/nightly/roc_nightly-linux_x86_64-latest.tar.gz
#- run: curl -fOL https://github.com/roc-lang/roc/releases/download/nightly/roc_nightly-linux_arm64-latest.tar.gz - run: curl -fOL https://github.com/roc-lang/roc/releases/download/nightly/roc_nightly-linux_arm64-latest.tar.gz
#- run: curl -fOL https://github.com/roc-lang/roc/releases/download/nightly/roc_nightly-macos_x86_64-latest.tar.gz - run: curl -fOL https://github.com/roc-lang/roc/releases/download/nightly/roc_nightly-macos_x86_64-latest.tar.gz
#- run: curl -fOL https://github.com/roc-lang/roc/releases/download/nightly/roc_nightly-macos_apple_silicon-latest.tar.gz - run: curl -fOL https://github.com/roc-lang/roc/releases/download/nightly/roc_nightly-macos_apple_silicon-latest.tar.gz
- run: curl -fOL https://github.com/roc-lang/roc/releases/download/nightly/roc_nightly-linux_x86_64-TESTING.tar.gz #- run: curl -fOL https://github.com/roc-lang/roc/releases/download/nightly/roc_nightly-linux_x86_64-TESTING.tar.gz
- run: curl -fOL https://github.com/roc-lang/roc/releases/download/nightly/roc_nightly-linux_arm64-TESTING.tar.gz #- run: curl -fOL https://github.com/roc-lang/roc/releases/download/nightly/roc_nightly-linux_arm64-TESTING.tar.gz
- run: curl -fOL https://github.com/roc-lang/roc/releases/download/nightly/roc_nightly-macos_x86_64-TESTING.tar.gz #- run: curl -fOL https://github.com/roc-lang/roc/releases/download/nightly/roc_nightly-macos_x86_64-TESTING.tar.gz
- run: curl -fOL https://github.com/roc-lang/roc/releases/download/nightly/roc_nightly-macos_apple_silicon-TESTING.tar.gz #- run: curl -fOL https://github.com/roc-lang/roc/releases/download/nightly/roc_nightly-macos_apple_silicon-TESTING.tar.gz
- name: Save roc_nightly archives - name: Save roc_nightly archives
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4

View file

@ -124,6 +124,11 @@ jobs:
if: needs.check-changes.outputs.run_tests == 'full' if: needs.check-changes.outputs.run_tests == 'full'
uses: ./.github/workflows/benchmarks.yml uses: ./.github/workflows/benchmarks.yml
start-panic-check:
needs: check-changes
if: needs.check-changes.outputs.run_tests == 'full'
uses: ./.github/workflows/improve_panics.yml
ran-full: ran-full:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
needs: [ needs: [
@ -136,7 +141,8 @@ jobs:
start-ubuntu-x86-64-nix-tests-debug, start-ubuntu-x86-64-nix-tests-debug,
start-windows-release-build-test, start-windows-release-build-test,
start-windows-tests, start-windows-tests,
start-roc-benchmarks start-roc-benchmarks,
start-panic-check
] ]
steps: steps:
- run: echo "all workflows succeeded!" - run: echo "all workflows succeeded!"

View file

@ -1,7 +1,6 @@
on: on:
workflow_dispatch: workflow_dispatch:
# pull_request: #pull_request:
# TODO remove pull_request trigger
name: Docker images tests name: Docker images tests
@ -19,8 +18,10 @@ jobs:
- name: Build image - name: Build image
run: docker compose -f docker/nightly-ubuntu-latest/docker-compose.yml build run: docker compose -f docker/nightly-ubuntu-latest/docker-compose.yml build
- name: Run hello world test - name: Test with hello world
run: docker compose -f docker/nightly-ubuntu-latest/docker-compose.yml run roc examples/helloWorld.roc run: |
curl -OL https://raw.githubusercontent.com/roc-lang/examples/refs/heads/main/examples/HelloWorld/main.roc
docker compose -f docker/nightly-ubuntu-latest/docker-compose.yml run roc main.roc
nightly-ubuntu-2204: nightly-ubuntu-2204:
@ -36,8 +37,10 @@ jobs:
- name: Build image - name: Build image
run: docker compose -f docker/nightly-ubuntu-2204/docker-compose.yml build run: docker compose -f docker/nightly-ubuntu-2204/docker-compose.yml build
- name: Run hello world test - name: Test with hello world
run: docker compose -f docker/nightly-ubuntu-2204/docker-compose.yml run roc examples/helloWorld.roc run: |
curl -OL https://raw.githubusercontent.com/roc-lang/examples/refs/heads/main/examples/HelloWorld/main.roc
docker compose -f docker/nightly-ubuntu-2204/docker-compose.yml run roc main.roc
nightly-ubuntu-2004: nightly-ubuntu-2004:
name: nightly-ubuntu-2004 name: nightly-ubuntu-2004
@ -52,8 +55,10 @@ jobs:
- name: Build image - name: Build image
run: docker compose -f docker/nightly-ubuntu-2004/docker-compose.yml build run: docker compose -f docker/nightly-ubuntu-2004/docker-compose.yml build
- name: Run hello world test - name: Test with hello world
run: docker compose -f docker/nightly-ubuntu-2004/docker-compose.yml run roc examples/helloWorld.roc run: |
curl -OL https://raw.githubusercontent.com/roc-lang/examples/refs/heads/main/examples/HelloWorld/main.roc
docker compose -f docker/nightly-ubuntu-2004/docker-compose.yml run roc main.roc
nightly-debian-latest: nightly-debian-latest:
name: nightly-debian-latest name: nightly-debian-latest
@ -68,8 +73,10 @@ jobs:
- name: Build image - name: Build image
run: docker compose -f docker/nightly-debian-latest/docker-compose.yml build run: docker compose -f docker/nightly-debian-latest/docker-compose.yml build
- name: Run hello world test - name: Test with hello world
run: docker compose -f docker/nightly-debian-latest/docker-compose.yml run roc examples/helloWorld.roc run: |
curl -OL https://raw.githubusercontent.com/roc-lang/examples/refs/heads/main/examples/HelloWorld/main.roc
docker compose -f docker/nightly-debian-latest/docker-compose.yml run roc main.roc
nightly-debian-bookworm: nightly-debian-bookworm:
name: nightly-debian-bookworm name: nightly-debian-bookworm
@ -84,8 +91,10 @@ jobs:
- name: Build image - name: Build image
run: docker compose -f docker/nightly-debian-bookworm/docker-compose.yml build run: docker compose -f docker/nightly-debian-bookworm/docker-compose.yml build
- name: Run hello world test - name: Test with hello world
run: docker compose -f docker/nightly-debian-bookworm/docker-compose.yml run roc examples/helloWorld.roc run: |
curl -OL https://raw.githubusercontent.com/roc-lang/examples/refs/heads/main/examples/HelloWorld/main.roc
docker compose -f docker/nightly-debian-bookworm/docker-compose.yml run roc main.roc
nightly-debian-buster: nightly-debian-buster:
name: nightly-debian-buster name: nightly-debian-buster
@ -100,5 +109,7 @@ jobs:
- name: Build image - name: Build image
run: docker compose -f docker/nightly-debian-buster/docker-compose.yml build run: docker compose -f docker/nightly-debian-buster/docker-compose.yml build
- name: Run hello world test - name: Test with hello world
run: docker compose -f docker/nightly-debian-buster/docker-compose.yml run roc examples/helloWorld.roc run: |
curl -OL https://raw.githubusercontent.com/roc-lang/examples/refs/heads/main/examples/HelloWorld/main.roc
docker compose -f docker/nightly-debian-buster/docker-compose.yml run roc main.roc

56
.github/workflows/improve_panics.yml vendored Normal file
View file

@ -0,0 +1,56 @@
on:
workflow_call:
# Check that the number of panicking function/macro calls has not increased
# https://github.com/roc-lang/roc/issues/2046
name: Improve panics/unwrap/expect
jobs:
improve-panics:
name: improve panics
runs-on: [ubuntu-22.04]
timeout-minutes: 15
env:
FORCE_COLOR: 1
RUST_BACKTRACE: 1
steps:
# install nix
- uses: cachix/install-nix-action@v23
with:
nix_path: nixpkgs=channel:nixos-unstable
- name: checkout source branch in separate directory
uses: actions/checkout@v4
with:
clean: true
path: source_branch_dir
ref: ${{ github.ref }}
- name: checkout target branch in separate directory
uses: actions/checkout@v4
with:
clean: false
path: target_branch_dir
ref: ${{ github.base_ref }}
- name: Compare panics, expects, and unwraps between source and target brach
run: |
for branch_dir in source_branch_dir target_branch_dir; do
cd $branch_dir
echo "Calculating violations in &branch_dir..."
VIOLATIONS=$(nix develop -c cargo clippy --no-deps -- -W clippy::unwrap_used -W clippy::expect_used -W clippy::panic 2> >(grep -e "warning: \`panic\`" -e "warning: used" -e "warning: usage of" | wc -l ))
echo $VIOLATIONS > violations
cd ..
done
SOURCE_VIOLATIONS=$(cat source_branch_dir/violations)
TARGET_VIOLATIONS=$(cat target_branch_dir/violations)
if [ "$SOURCE_VIOLATIONS" -gt "$TARGET_VIOLATIONS" ]; then
echo "You added panic/unwrap/expect, in this PR their count increased from $TARGET_VIOLATIONS to $SOURCE_VIOLATIONS."
echo "These calls can kill the REPL, try alternative error handling."
echo ""
echo "TIP: Ask AI \"In rust, how can I rewrite code that contains panic or unwrap so it doesn't crash?\""
echo ""
echo "If you believe your panic/unwrap/expect is justified, ask Anton-4, rtfeldman or lukewilliamboswell to bypass this workflow failure."
exit 1
fi

View file

@ -2,7 +2,7 @@ on:
#pull_request: #pull_request:
workflow_dispatch: workflow_dispatch:
schedule: schedule:
- cron: '0 9 * * *' - cron: "0 9 * * *"
name: Nightly Release macOS Apple Silicon name: Nightly Release macOS Apple Silicon
@ -30,15 +30,15 @@ jobs:
run: cargo test --locked --release run: cargo test --locked --release
- name: get commit SHA - name: get commit SHA
run: echo "SHA=$(git rev-parse --short "$GITHUB_SHA")" >> $GITHUB_ENV run: echo "SHA=$(git rev-parse --short "$GITHUB_SHA")" >> $GITHUB_ENV
- name: get date - name: get date
run: echo "DATE=$(date "+%Y-%m-%d")" >> $GITHUB_ENV run: echo "DATE=$(date "+%Y-%m-%d")" >> $GITHUB_ENV
- name: build file name - name: build file name
env: env:
DATE: ${{ env.DATE }} DATE: ${{ env.DATE }}
SHA: ${{ env.SHA }} SHA: ${{ env.SHA }}
run: echo "RELEASE_FOLDER_NAME=roc_nightly-macos_apple_silicon-$DATE-$SHA" >> $GITHUB_ENV run: echo "RELEASE_FOLDER_NAME=roc_nightly-macos_apple_silicon-$DATE-$SHA" >> $GITHUB_ENV
- name: write version to file - name: write version to file
@ -56,8 +56,9 @@ jobs:
- name: extract tar for a quick test - name: extract tar for a quick test
run: ls | grep tar | xargs tar -xf run: ls | grep tar | xargs tar -xf
- name: test with rust platform - name: test with zig platform
run: cd ${{ env.RELEASE_FOLDER_NAME }} && ./roc examples/platform-switching/rocLovesRust.roc run: |
cd ${{ env.RELEASE_FOLDER_NAME }} && ./roc --build-host --suppress-build-host-warning examples/platform-switching/rocLovesZig.roc
- name: print short commit SHA - name: print short commit SHA
run: git rev-parse --short "$GITHUB_SHA" run: git rev-parse --short "$GITHUB_SHA"
@ -66,6 +67,6 @@ jobs:
- name: Upload artifact Actually uploading to github releases has to be done manually - name: Upload artifact Actually uploading to github releases has to be done manually
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: ${{ env.RELEASE_FOLDER_NAME }}.tar.gz name: ${{ env.RELEASE_FOLDER_NAME }}.tar.gz
path: ${{ env.RELEASE_FOLDER_NAME }}.tar.gz path: ${{ env.RELEASE_FOLDER_NAME }}.tar.gz
retention-days: 4 retention-days: 4

View file

@ -23,7 +23,7 @@ jobs:
- name: roc test all builtins - name: roc test all builtins
run: nix develop -c ./ci/roc_test_builtins.sh run: nix develop -c ./ci/roc_test_builtins.sh
- name: test wasm32 cli_run - name: test wasm32 cli_tests
run: nix develop -c cargo test --locked --release --features="wasm32-cli-run" run: nix develop -c cargo test --locked --release --features="wasm32-cli-run"
- name: test the dev backend # these tests require an explicit feature flag - name: test the dev backend # these tests require an explicit feature flag

View file

@ -34,17 +34,11 @@ jobs:
# for skipped tests: see issue 6274 # for skipped tests: see issue 6274
- name: execute tests with --release - name: execute tests with --release
run: nix develop -c cargo test --locked --release -- --skip cli_run::inspect_gui --skip cli_run::hello_gui run: nix develop -c cargo test --locked --release -- --skip cli_tests::inspect_gui --skip cli_tests::hello_gui
- name: roc test all builtins - name: roc test all builtins
run: nix develop -c ./ci/roc_test_builtins.sh run: nix develop -c ./ci/roc_test_builtins.sh
- name: make a libapp.so for the next step
run: nix develop -c cargo run -- gen-stub-lib examples/platform-switching/rocLovesRust.roc
- name: check that the platform`s produced dylib is loadable
run: cd examples/platform-switching/rust-platform && nix develop -c cargo test --release --locked
- name: test aarch64 dev backend - name: test aarch64 dev backend
run: nix develop -c cargo nextest-gen-dev --locked --release --no-fail-fast run: nix develop -c cargo nextest-gen-dev --locked --release --no-fail-fast

View file

@ -9,22 +9,16 @@ env:
jobs: jobs:
nix-macos-x86-64: nix-macos-x86-64:
name: nix-macos-x86-64 name: nix-macos-x86-64
runs-on: [macos-12] runs-on: [macos-13]
timeout-minutes: 90 timeout-minutes: 90
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: cachix/install-nix-action@v22 - uses: cachix/install-nix-action@v22
- name: execute cli_run tests only, the full tests take too long but are run nightly - name: execute cli_tests tests only, the full tests take too long but are run nightly
run: nix develop -c cargo test --locked --release -p roc_cli -- --skip hello_gui run: nix develop -c cargo test --locked --release -p roc_cli -- --skip hello_gui
# see 5932 for hello_gui # see 5932 for hello_gui
- name: roc test all builtins - name: roc test all builtins
run: nix develop -c ./ci/roc_test_builtins.sh run: nix develop -c ./ci/roc_test_builtins.sh
- name: make a libapp.so for the next step
run: nix develop -c cargo run -- gen-stub-lib examples/platform-switching/rocLovesRust.roc
- name: check that the platform`s produced dylib is loadable
run: cd examples/platform-switching/rust-platform && nix develop -c cargo test --release --locked

View file

@ -26,17 +26,11 @@ jobs:
- name: rename nightly folder - name: rename nightly folder
run: mv roc_nightly* roc_nightly run: mv roc_nightly* roc_nightly
- name: test roc hello world
run: cd roc_nightly && ./roc examples/helloWorld.roc
- name: test platform switching rust
run: cd roc_nightly && ./roc examples/platform-switching/rocLovesRust.roc
- name: test platform switching zig - name: test platform switching zig
run: cd roc_nightly && ./roc examples/platform-switching/rocLovesZig.roc run: cd roc_nightly && ./roc examples/platform-switching/rocLovesZig.roc --build-host --suppress-build-host-warning
- name: test platform switching c - name: test platform switching c
run: cd roc_nightly && ./roc examples/platform-switching/rocLovesC.roc run: cd roc_nightly && ./roc examples/platform-switching/rocLovesC.roc --build-host --suppress-build-host-warning
- name: test repl - name: test repl
run: | run: |

View file

@ -5,11 +5,11 @@ name: Test latest nightly releases for macOS and Linux x86_64
jobs: jobs:
test-nightly: test-nightly:
name: test nightly macos 11/12/13, ubuntu 20.04/22.04 name: test nightly macos 13 (x64), ubuntu 20.04/22.04
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
os: [ macos-12, macos-13, ubuntu-20.04, ubuntu-22.04, ubuntu-24.04] os: [ macos-13, ubuntu-20.04, ubuntu-22.04, ubuntu-24.04]
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
timeout-minutes: 90 timeout-minutes: 90
steps: steps:

View file

@ -37,13 +37,10 @@ jobs:
- name: regular rust tests - name: regular rust tests
# see #5904 for skipped test # see #5904 for skipped test
run: cargo test --locked --release -- --skip cli_run::expects_dev_and_test run: cargo test --locked --release -- --skip cli_tests::expects_dev_and_test
- name: tests examples in docs - name: tests examples in docs
run: cargo test --doc --release run: cargo test --doc --release
- name: check that the platform`s produced dylib is loadable
run: cd examples/platform-switching/rust-platform && LD_LIBRARY_PATH=. cargo test --release --locked
- name: test the dev backend # these tests require an explicit feature flag - name: test the dev backend # these tests require an explicit feature flag
run: cargo test --locked --release --package test_gen --no-default-features --features gen-dev run: cargo test --locked --release --package test_gen --no-default-features --features gen-dev

View file

@ -24,4 +24,4 @@ jobs:
# for skipped tests; see #6946, #6947 # for skipped tests; see #6946, #6947
- name: cargo test without --release - name: cargo test without --release
run: nix develop -c sh -c 'export ROC_CHECK_MONO_IR=1 && cargo test -- --skip tests/exhaustive/match_on_result_with_uninhabited_error_destructuring_in_lambda_syntax.txt --skip tests::identity_lambda --skip tests::issue_2300 --skip tests::issue_2582_specialize_result_value --skip tests::sum_lambda' run: nix develop -c sh -c 'export ROC_CHECK_MONO_IR=1 && cargo test'

3
.gitignore vendored
View file

@ -119,3 +119,6 @@ crates/glue/tests/fixtures/*/build.rs
crates/glue/tests/fixtures/*/host.c crates/glue/tests/fixtures/*/host.c
crates/glue/tests/fixtures/*/src/main.rs crates/glue/tests/fixtures/*/src/main.rs
crates/glue/tests/fixtures/*/test_glue/ crates/glue/tests/fixtures/*/test_glue/
# ignore the zig glue files copied into test platforms
**/*platform/glue/*

81
Cargo.lock generated
View file

@ -384,11 +384,11 @@ dependencies = [
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.0.83" version = "1.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" checksum = "57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6"
dependencies = [ dependencies = [
"libc", "shlex",
] ]
[[package]] [[package]]
@ -463,20 +463,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961"
[[package]] [[package]]
name = "cli_utils" name = "cli_test_utils"
version = "0.0.1" version = "0.0.1"
dependencies = [ dependencies = [
"bumpalo", "bumpalo",
"const_format",
"criterion", "criterion",
"lazy_static",
"regex", "regex",
"rlimit", "rlimit",
"roc_cli",
"roc_collections", "roc_collections",
"roc_command_utils", "roc_command_utils",
"roc_load", "roc_load",
"roc_module", "roc_module",
"roc_reporting", "roc_reporting",
"serde", "serde",
"serde-xml-rs",
"tempfile", "tempfile",
] ]
@ -1665,6 +1667,12 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "num-conv"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
[[package]] [[package]]
name = "num-traits" name = "num-traits"
version = "0.2.17" version = "0.2.17"
@ -1873,9 +1881,9 @@ dependencies = [
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.69" version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
@ -1941,9 +1949,9 @@ dependencies = [
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.33" version = "1.0.37"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
] ]
@ -2344,6 +2352,7 @@ dependencies = [
"roc_region", "roc_region",
"roc_serialize", "roc_serialize",
"roc_types", "roc_types",
"soa",
"static_assertions", "static_assertions",
"test_compile", "test_compile",
"ven_pretty", "ven_pretty",
@ -2375,14 +2384,16 @@ name = "roc_cli"
version = "0.0.1" version = "0.0.1"
dependencies = [ dependencies = [
"bumpalo", "bumpalo",
"chrono",
"clap 4.4.6", "clap 4.4.6",
"cli_utils", "cli_test_utils",
"const_format", "const_format",
"criterion", "criterion",
"distance", "distance",
"errno", "errno",
"indoc", "indoc",
"inkwell", "inkwell",
"insta",
"libc", "libc",
"libloading", "libloading",
"mimalloc", "mimalloc",
@ -2411,10 +2422,8 @@ dependencies = [
"roc_repl_expect", "roc_repl_expect",
"roc_reporting", "roc_reporting",
"roc_target", "roc_target",
"roc_test_utils",
"roc_tracing", "roc_tracing",
"roc_wasm_interp", "roc_wasm_interp",
"serial_test",
"signal-hook", "signal-hook",
"strum", "strum",
"target-lexicon", "target-lexicon",
@ -2433,6 +2442,7 @@ dependencies = [
"im", "im",
"im-rc", "im-rc",
"smallvec", "smallvec",
"soa",
"wyhash", "wyhash",
] ]
@ -2453,6 +2463,7 @@ dependencies = [
"roc_problem", "roc_problem",
"roc_region", "roc_region",
"roc_types", "roc_types",
"soa",
] ]
[[package]] [[package]]
@ -2617,7 +2628,7 @@ name = "roc_glue"
version = "0.0.1" version = "0.0.1"
dependencies = [ dependencies = [
"bumpalo", "bumpalo",
"cli_utils", "cli_test_utils",
"dircpy", "dircpy",
"fnv", "fnv",
"indexmap", "indexmap",
@ -2893,6 +2904,7 @@ dependencies = [
"roc_error_macros", "roc_error_macros",
"roc_module", "roc_module",
"roc_region", "roc_region",
"soa",
"tempfile", "tempfile",
] ]
@ -2996,7 +3008,6 @@ dependencies = [
"roc_std", "roc_std",
"roc_target", "roc_target",
"roc_types", "roc_types",
"signal-hook",
"strip-ansi-escapes", "strip-ansi-escapes",
"target-lexicon", "target-lexicon",
"tempfile", "tempfile",
@ -3105,6 +3116,7 @@ dependencies = [
"roc_target", "roc_target",
"roc_types", "roc_types",
"roc_unify", "roc_unify",
"soa",
"tempfile", "tempfile",
"test_compile", "test_compile",
"test_solve_helpers", "test_solve_helpers",
@ -3227,6 +3239,7 @@ dependencies = [
"roc_parse", "roc_parse",
"roc_region", "roc_region",
"roc_serialize", "roc_serialize",
"soa",
"static_assertions", "static_assertions",
"ven_pretty", "ven_pretty",
] ]
@ -3346,9 +3359,9 @@ dependencies = [
[[package]] [[package]]
name = "rustversion" name = "rustversion"
version = "1.0.14" version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6"
[[package]] [[package]]
name = "rusty-fork" name = "rusty-fork"
@ -3464,18 +3477,6 @@ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]]
name = "serde-xml-rs"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb3aa78ecda1ebc9ec9847d5d3aba7d618823446a049ba2491940506da6e2782"
dependencies = [
"log",
"serde",
"thiserror",
"xml-rs",
]
[[package]] [[package]]
name = "serde_cbor" name = "serde_cbor"
version = "0.11.2" version = "0.11.2"
@ -3576,6 +3577,12 @@ dependencies = [
"lazy_static", "lazy_static",
] ]
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]] [[package]]
name = "signal-hook" name = "signal-hook"
version = "0.3.17" version = "0.3.17"
@ -4018,12 +4025,13 @@ dependencies = [
[[package]] [[package]]
name = "time" name = "time"
version = "0.3.30" version = "0.3.36"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
dependencies = [ dependencies = [
"deranged", "deranged",
"itoa", "itoa",
"num-conv",
"powerfmt", "powerfmt",
"serde", "serde",
"time-core", "time-core",
@ -4038,10 +4046,11 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
[[package]] [[package]]
name = "time-macros" name = "time-macros"
version = "0.2.15" version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"
dependencies = [ dependencies = [
"num-conv",
"time-core", "time-core",
] ]
@ -4398,7 +4407,7 @@ name = "valgrind"
version = "0.0.1" version = "0.0.1"
dependencies = [ dependencies = [
"bumpalo", "bumpalo",
"cli_utils", "cli_test_utils",
"indoc", "indoc",
"roc_build", "roc_build",
"roc_command_utils", "roc_command_utils",
@ -4864,12 +4873,6 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "xml-rs"
version = "0.8.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fcb9cbac069e033553e8bb871be2fbdffcab578eb25bd0f7c508cedc6dcd75a"
[[package]] [[package]]
name = "yaml-rust" name = "yaml-rust"
version = "0.4.5" version = "0.4.5"

View file

@ -5,7 +5,7 @@ members = [
"crates/fs", "crates/fs",
"crates/glue", "crates/glue",
"crates/cli", "crates/cli",
"crates/cli_utils", "crates/cli_test_utils",
"crates/highlight", "crates/highlight",
"crates/error_macros", "crates/error_macros",
"crates/reporting", "crates/reporting",
@ -20,9 +20,10 @@ members = [
"crates/test_compile", "crates/test_compile",
"crates/test_utils", "crates/test_utils",
"crates/test_utils_dir", "crates/test_utils_dir",
"crates/valgrind", "crates/valgrind_tests",
"crates/tracing", "crates/tracing",
"crates/utils/*", "crates/utils/*",
"crates/soa",
"crates/docs", "crates/docs",
"crates/docs_cli", "crates/docs_cli",
"crates/linker", "crates/linker",
@ -75,10 +76,8 @@ version = "0.0.1"
inkwell = { git = "https://github.com/roc-lang/inkwell", branch = "inkwell-llvm-16", features = [ inkwell = { git = "https://github.com/roc-lang/inkwell", branch = "inkwell-llvm-16", features = [
"llvm16-0", "llvm16-0",
] } ] }
soa = { path = "crates/soa" } soa = { path = "crates/soa" }
roc_specialize_types = { path = "crates/compiler/specialize_types" } roc_specialize_types = { path = "crates/compiler/specialize_types" }
arrayvec = "0.7.2" # update roc_std/Cargo.toml on change arrayvec = "0.7.2" # update roc_std/Cargo.toml on change
backtrace = "0.3.67" backtrace = "0.3.67"
base64-url = "1.4.13" base64-url = "1.4.13"
@ -174,7 +173,6 @@ schemars = "0.8.12"
serde = { version = "1.0.153", features = [ serde = { version = "1.0.153", features = [
"derive", "derive",
] } # update roc_std/Cargo.toml on change ] } # update roc_std/Cargo.toml on change
serde-xml-rs = "0.6.0"
serde_json = "1.0.94" # update roc_std/Cargo.toml on change serde_json = "1.0.94" # update roc_std/Cargo.toml on change
serial_test = "1.0.0" serial_test = "1.0.0"
signal-hook = "0.3.15" signal-hook = "0.3.15"

View file

@ -28,17 +28,16 @@ mv roc_nightly* roc_nightly
cd roc_nightly cd roc_nightly
# test roc hello world # test rust platform (first prebuild the host)
./roc examples/helloWorld.roc # temp disabled
# examples/platform-switching/rust-platform/build.sh
# test rust platform # ./roc examples/platform-switching/rocLovesRust.roc
./roc examples/platform-switching/rocLovesRust.roc
# test zig platform # test zig platform
./roc examples/platform-switching/rocLovesZig.roc ./roc --build-host --suppress-build-host-warning examples/platform-switching/rocLovesZig.roc
# test C platform # test C platform
./roc examples/platform-switching/rocLovesC.roc ./roc --build-host --suppress-build-host-warning examples/platform-switching/rocLovesC.roc
# test repl # test repl
cd ../ci/repl_basic_test cd ../ci/repl_basic_test

View file

@ -54,6 +54,6 @@ fi
./jump-start.sh ./jump-start.sh
# build the basic cli platform # build the basic cli platform
roc build.roc --prebuilt-platform roc build.roc
cd .. cd ..

View file

@ -47,6 +47,6 @@ cd ..
cd basic-webserver cd basic-webserver
roc build.roc --prebuilt-platform roc build.roc
cd .. cd ..

View file

@ -12,7 +12,7 @@ mkdir -p $1 $1/examples $1/crates/compiler/builtins/bitcode
mv target/release-with-lto/{roc,roc_language_server,lib} $1 mv target/release-with-lto/{roc,roc_language_server,lib} $1
mv LICENSE LEGAL_DETAILS $1 mv LICENSE LEGAL_DETAILS $1
mv examples/{helloWorld.roc,platform-switching,cli} $1/examples mv examples/{platform-switching,cli} $1/examples
mv crates/roc_std $1/crates mv crates/roc_std $1/crates
mv crates/compiler/builtins/bitcode/src $1/crates/compiler/builtins/bitcode mv crates/compiler/builtins/bitcode/src $1/crates/compiler/builtins/bitcode

View file

@ -12,9 +12,9 @@ cargo doc --package roc_ast --open
The `roc` binary that brings together all functionality in the Roc toolset. The `roc` binary that brings together all functionality in the Roc toolset.
## `cli_utils/` - `cli_utils` ## `cli_test_utils/` - `cli_test_utils`
Provides shared code for cli tests and benchmarks. Provides shared code for cli tests, cli benchmarks, glue tests, valgrind crate.
## `compiler/` ## `compiler/`

View file

@ -2,6 +2,7 @@
name = "roc_cli" name = "roc_cli"
description = "The Roc binary that brings together all functionality in the Roc toolset." description = "The Roc binary that brings together all functionality in the Roc toolset."
default-run = "roc" default-run = "roc"
build = "build.rs"
authors.workspace = true authors.workspace = true
edition.workspace = true edition.workspace = true
@ -30,7 +31,13 @@ target-wasm32 = ["roc_build/target-wasm32"]
target-x86 = ["roc_build/target-x86", "roc_repl_cli/target-x86"] target-x86 = ["roc_build/target-x86", "roc_repl_cli/target-x86"]
target-x86_64 = ["roc_build/target-x86_64", "roc_repl_cli/target-x86_64"] target-x86_64 = ["roc_build/target-x86_64", "roc_repl_cli/target-x86_64"]
target-all = ["target-aarch64", "target-arm", "target-x86", "target-x86_64", "target-wasm32"] target-all = [
"target-aarch64",
"target-arm",
"target-x86",
"target-x86_64",
"target-wasm32",
]
sanitizers = ["roc_build/sanitizers"] sanitizers = ["roc_build/sanitizers"]
@ -83,15 +90,17 @@ roc_repl_expect = { path = "../repl_expect" }
[dev-dependencies] [dev-dependencies]
cli_utils = { path = "../cli_utils" } cli_test_utils = { path = "../cli_test_utils" }
roc_test_utils = { path = "../test_utils" }
roc_command_utils = { path = "../utils/command" } roc_command_utils = { path = "../utils/command" }
criterion.workspace = true criterion.workspace = true
indoc.workspace = true indoc.workspace = true
parking_lot.workspace = true parking_lot.workspace = true
pretty_assertions.workspace = true pretty_assertions.workspace = true
serial_test.workspace = true insta.workspace = true
[build-dependencies]
chrono.workspace = true
[[bench]] [[bench]]
name = "time_bench" name = "time_bench"

View file

@ -1,6 +1,6 @@
use std::time::Duration; use std::time::Duration;
use cli_utils::bench_utils::{ use cli_test_utils::bench_utils::{
bench_cfold, bench_deriv, bench_nqueens, bench_quicksort, bench_rbtree_ck, bench_cfold, bench_deriv, bench_nqueens, bench_quicksort, bench_rbtree_ck,
}; };
use criterion::{measurement::WallTime, BenchmarkGroup, Criterion, SamplingMode}; use criterion::{measurement::WallTime, BenchmarkGroup, Criterion, SamplingMode};

71
crates/cli/build.rs Normal file
View file

@ -0,0 +1,71 @@
use chrono::prelude::*;
use std::fs;
use std::process::Command;
use std::str;
fn main() {
// Rebuild if this build.rs file changes
println!("cargo:rerun-if-changed=build.rs");
// The version file is located at the root of the repository
let version_file_path = "../../version.txt";
// Rebuild if version file changes
println!("cargo:rerun-if-changed={}", version_file_path);
// Read the version file
let version_file_contents = fs::read_to_string(version_file_path).unwrap();
// If the version is "built-from-source", replace it with the git commit information
let version = match version_file_contents.trim() {
"built-from-source" => {
let git_head_file_path = "../../.git/HEAD";
// Rebuild if a new Git commit is made
println!("cargo:rerun-if-changed={}", git_head_file_path);
// Check if the .git/HEAD file exists
let git_head_exists = fs::metadata(git_head_file_path).is_ok();
if git_head_exists {
// Get the hash of the current commit
let git_describe_output = Command::new("git")
.arg("describe")
.arg("--always")
.arg("--dirty= with additional changes") // Add a suffix if the working directory is dirty
.output()
.expect("Failed to execute git describe command");
println!("git_describe_output: {:?}", git_describe_output);
let git_commit_hash = str::from_utf8(&git_describe_output.stdout)
.expect("Failed to parse git describe output")
.trim();
// Get the datetime of the last commit
let git_show_output = Command::new("git")
.arg("show")
.arg("--no-patch")
.arg("--format=%ct") // Outputting a UNIX timestamp is the only way to always use UTC
.output()
.expect("Failed to execute git show command");
println!("git_show_output: {:?}", git_show_output);
let git_commit_timestamp = {
let timestamp = str::from_utf8(&git_show_output.stdout)
.expect("Failed to parse git show output as a string")
.trim()
.parse::<i64>()
.expect("Failed to parse timestamp as an integer");
DateTime::from_timestamp(timestamp, 0)
.expect("Failed to parse timestamp")
.format("%Y-%m-%d %H:%M:%S")
};
format!(
"built from commit {git_commit_hash}, committed at {git_commit_timestamp} UTC"
)
} else {
// If the .git/HEAD file does not exist, e.g. in a Nix build, use a generic message
"built from source".to_string()
}
}
_ => version_file_contents.trim().to_string(),
};
// Emit the version to a build-time environment variable
println!("cargo:rustc-env=ROC_VERSION={}", version);
}

View file

@ -263,7 +263,7 @@ mod tests {
use std::io::Write; use std::io::Write;
use tempfile::{tempdir, TempDir}; use tempfile::{tempdir, TempDir};
const FORMATTED_ROC: &str = r#"app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br" } const FORMATTED_ROC: &str = r#"app [main] { pf: platform "platform/main.roc" }
import pf.Stdout import pf.Stdout
import pf.Stdin import pf.Stdin
@ -273,11 +273,7 @@ main =
name = Stdin.line! name = Stdin.line!
Stdout.line! "Hi $(name)!""#; Stdout.line! "Hi $(name)!""#;
const UNFORMATTED_ROC: &str = r#"app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br" } const UNFORMATTED_ROC: &str = r#"app [main] { pf: platform "platform/main.roc" }
import pf.Stdout
import pf.Stdin
main = main =
Stdout.line! "What's your name?" Stdout.line! "What's your name?"

View file

@ -54,7 +54,6 @@ pub const CMD_VERSION: &str = "version";
pub const CMD_FORMAT: &str = "format"; pub const CMD_FORMAT: &str = "format";
pub const CMD_TEST: &str = "test"; pub const CMD_TEST: &str = "test";
pub const CMD_GLUE: &str = "glue"; pub const CMD_GLUE: &str = "glue";
pub const CMD_GEN_STUB_LIB: &str = "gen-stub-lib";
pub const CMD_PREPROCESS_HOST: &str = "preprocess-host"; pub const CMD_PREPROCESS_HOST: &str = "preprocess-host";
pub const FLAG_EMIT_LLVM_IR: &str = "emit-llvm-ir"; pub const FLAG_EMIT_LLVM_IR: &str = "emit-llvm-ir";
@ -72,7 +71,8 @@ pub const FLAG_VERBOSE: &str = "verbose";
pub const FLAG_NO_COLOR: &str = "no-color"; pub const FLAG_NO_COLOR: &str = "no-color";
pub const FLAG_NO_HEADER: &str = "no-header"; pub const FLAG_NO_HEADER: &str = "no-header";
pub const FLAG_LINKER: &str = "linker"; pub const FLAG_LINKER: &str = "linker";
pub const FLAG_PREBUILT: &str = "prebuilt-platform"; pub const FLAG_BUILD_HOST: &str = "build-host";
pub const FLAG_SUPPRESS_BUILD_HOST_WARNING: &str = "suppress-build-host-warning";
pub const FLAG_CHECK: &str = "check"; pub const FLAG_CHECK: &str = "check";
pub const FLAG_STDIN: &str = "stdin"; pub const FLAG_STDIN: &str = "stdin";
pub const FLAG_STDOUT: &str = "stdout"; pub const FLAG_STDOUT: &str = "stdout";
@ -90,7 +90,7 @@ pub const FLAG_PP_HOST: &str = "host";
pub const FLAG_PP_PLATFORM: &str = "platform"; pub const FLAG_PP_PLATFORM: &str = "platform";
pub const FLAG_PP_DYLIB: &str = "lib"; pub const FLAG_PP_DYLIB: &str = "lib";
const VERSION: &str = include_str!("../../../version.txt"); pub const VERSION: &str = env!("ROC_VERSION");
const DEFAULT_GENERATED_DOCS_DIR: &str = "generated-docs"; const DEFAULT_GENERATED_DOCS_DIR: &str = "generated-docs";
pub fn build_app() -> Command { pub fn build_app() -> Command {
@ -142,9 +142,15 @@ pub fn build_app() -> Command {
.value_parser(["surgical", "legacy"]) .value_parser(["surgical", "legacy"])
.required(false); .required(false);
let flag_prebuilt = Arg::new(FLAG_PREBUILT) let flag_build_host = Arg::new(FLAG_BUILD_HOST)
.long(FLAG_PREBUILT) .long(FLAG_BUILD_HOST)
.help("Assume the platform has been prebuilt and skip rebuilding the platform\n(This is enabled implicitly when using `roc build` with a --target other than `--target <current machine>`, unless the target is wasm.)") .help("WARNING: platforms are responsible for building hosts, this flag will be removed when internal test platforms have a build script")
.action(ArgAction::SetTrue)
.required(false);
let flag_suppress_build_host_warning = Arg::new(FLAG_SUPPRESS_BUILD_HOST_WARNING)
.long(FLAG_SUPPRESS_BUILD_HOST_WARNING)
.help("WARNING: platforms are responsible for building hosts, this flag will be removed when internal test platforms have a build script")
.action(ArgAction::SetTrue) .action(ArgAction::SetTrue)
.required(false); .required(false);
@ -182,7 +188,7 @@ pub fn build_app() -> Command {
PossibleValuesParser::new(Target::iter().map(Into::<&'static str>::into)); PossibleValuesParser::new(Target::iter().map(Into::<&'static str>::into));
Command::new("roc") Command::new("roc")
.version(concatcp!(VERSION, "\n")) .version(VERSION)
.about("Run the given .roc file, if there are no compilation errors.\nYou can use one of the SUBCOMMANDS below to do something else!") .about("Run the given .roc file, if there are no compilation errors.\nYou can use one of the SUBCOMMANDS below to do something else!")
.args_conflicts_with_subcommands(true) .args_conflicts_with_subcommands(true)
.subcommand(Command::new(CMD_BUILD) .subcommand(Command::new(CMD_BUILD)
@ -201,7 +207,8 @@ pub fn build_app() -> Command {
.arg(flag_profiling.clone()) .arg(flag_profiling.clone())
.arg(flag_time.clone()) .arg(flag_time.clone())
.arg(flag_linker.clone()) .arg(flag_linker.clone())
.arg(flag_prebuilt.clone()) .arg(flag_build_host.clone())
.arg(flag_suppress_build_host_warning.clone())
.arg(flag_fuzz.clone()) .arg(flag_fuzz.clone())
.arg(flag_wasm_stack_size_kb) .arg(flag_wasm_stack_size_kb)
.arg( .arg(
@ -253,7 +260,8 @@ pub fn build_app() -> Command {
.arg(flag_profiling.clone()) .arg(flag_profiling.clone())
.arg(flag_time.clone()) .arg(flag_time.clone())
.arg(flag_linker.clone()) .arg(flag_linker.clone())
.arg(flag_prebuilt.clone()) .arg(flag_build_host.clone())
.arg(flag_suppress_build_host_warning.clone())
.arg(flag_fuzz.clone()) .arg(flag_fuzz.clone())
.arg( .arg(
Arg::new(FLAG_VERBOSE) Arg::new(FLAG_VERBOSE)
@ -266,6 +274,7 @@ pub fn build_app() -> Command {
Arg::new(ROC_FILE) Arg::new(ROC_FILE)
.help("The .roc file to test") .help("The .roc file to test")
.value_parser(value_parser!(PathBuf)) .value_parser(value_parser!(PathBuf))
.num_args(0..)
.required(false) .required(false)
.default_value(DEFAULT_ROC_FILENAME) .default_value(DEFAULT_ROC_FILENAME)
) )
@ -298,7 +307,8 @@ pub fn build_app() -> Command {
.arg(flag_profiling.clone()) .arg(flag_profiling.clone())
.arg(flag_time.clone()) .arg(flag_time.clone())
.arg(flag_linker.clone()) .arg(flag_linker.clone())
.arg(flag_prebuilt.clone()) .arg(flag_build_host.clone())
.arg(flag_suppress_build_host_warning.clone())
.arg(flag_fuzz.clone()) .arg(flag_fuzz.clone())
.arg(roc_file_to_run.clone()) .arg(roc_file_to_run.clone())
.arg(args_for_app.clone().last(true)) .arg(args_for_app.clone().last(true))
@ -313,7 +323,8 @@ pub fn build_app() -> Command {
.arg(flag_profiling.clone()) .arg(flag_profiling.clone())
.arg(flag_time.clone()) .arg(flag_time.clone())
.arg(flag_linker.clone()) .arg(flag_linker.clone())
.arg(flag_prebuilt.clone()) .arg(flag_build_host.clone())
.arg(flag_suppress_build_host_warning.clone())
.arg(flag_fuzz.clone()) .arg(flag_fuzz.clone())
.arg(roc_file_to_run.clone()) .arg(roc_file_to_run.clone())
.arg(args_for_app.clone().last(true)) .arg(args_for_app.clone().last(true))
@ -404,23 +415,6 @@ pub fn build_app() -> Command {
.default_value(DEFAULT_ROC_FILENAME) .default_value(DEFAULT_ROC_FILENAME)
) )
) )
.subcommand(Command::new(CMD_GEN_STUB_LIB)
.about("Generate a stubbed shared library that can be used for linking a platform binary.\nThe stubbed library has prototypes, but no function bodies.\n\nNote: This command will be removed in favor of just using `roc build` once all platforms support the surgical linker")
.arg(
Arg::new(ROC_FILE)
.help("The .roc file for an app using the platform")
.value_parser(value_parser!(PathBuf))
.required(true)
)
.arg(
Arg::new(FLAG_TARGET)
.long(FLAG_TARGET)
.help("Choose a different target")
.default_value(Into::<&'static str>::into(Target::default()))
.value_parser(build_target_values_parser.clone())
.required(false),
)
)
.subcommand(Command::new(CMD_PREPROCESS_HOST) .subcommand(Command::new(CMD_PREPROCESS_HOST)
.about("Runs the surgical linker preprocessor to generate `.rh` and `.rm` files.") .about("Runs the surgical linker preprocessor to generate `.rh` and `.rm` files.")
.arg( .arg(
@ -465,7 +459,8 @@ pub fn build_app() -> Command {
.arg(flag_profiling) .arg(flag_profiling)
.arg(flag_time) .arg(flag_time)
.arg(flag_linker) .arg(flag_linker)
.arg(flag_prebuilt) .arg(flag_build_host)
.arg(flag_suppress_build_host_warning)
.arg(flag_fuzz) .arg(flag_fuzz)
.arg(roc_file_to_run) .arg(roc_file_to_run)
.arg(args_for_app.trailing_var_arg(true)) .arg(args_for_app.trailing_var_arg(true))
@ -522,18 +517,21 @@ pub fn test(matches: &ArgMatches, target: Target) -> io::Result<i32> {
Some(n) => Threading::AtMost(*n), Some(n) => Threading::AtMost(*n),
}; };
let path = matches.get_one::<PathBuf>(ROC_FILE).unwrap(); let paths: Vec<_> = matches.get_many::<PathBuf>(ROC_FILE).unwrap().collect();
// Spawn the root task let paths: Vec<_> = {
if !path.exists() { let mut flatten_paths: Vec<_> = vec![];
let current_dir = env::current_dir().unwrap(); for path in paths.into_iter() {
let expected_file_path = current_dir.join(path); // Spawn the root task
if !path.exists() {
let current_dir = env::current_dir().unwrap();
let expected_file_path = current_dir.join(path);
let current_dir_string = current_dir.display(); let current_dir_string = current_dir.display();
let expected_file_path_string = expected_file_path.display(); let expected_file_path_string = expected_file_path.display();
// TODO these should use roc_reporting to display nicer error messages. // TODO these should use roc_reporting to display nicer error messages.
match matches.value_source(ROC_FILE) { match matches.value_source(ROC_FILE) {
Some(ValueSource::DefaultValue) => { Some(ValueSource::DefaultValue) => {
eprintln!( eprintln!(
"\nThe current directory ({current_dir_string}) does not contain a {DEFAULT_ROC_FILENAME} file to use as a default.\n\nYou can run `roc help` for more information on how to provide a .roc file.\n" "\nThe current directory ({current_dir_string}) does not contain a {DEFAULT_ROC_FILENAME} file to use as a default.\n\nYou can run `roc help` for more information on how to provide a .roc file.\n"
@ -541,116 +539,141 @@ pub fn test(matches: &ArgMatches, target: Target) -> io::Result<i32> {
} }
_ => eprintln!("\nThis file was not found: {expected_file_path_string}\n\nYou can run `roc help` for more information on how to provide a .roc file.\n"), _ => eprintln!("\nThis file was not found: {expected_file_path_string}\n\nYou can run `roc help` for more information on how to provide a .roc file.\n"),
} }
process::exit(1);
process::exit(1); } else if path.is_dir() {
} find_all_roc_files(path, &mut flatten_paths);
} else {
let arena = &arena; flatten_paths.push(path.clone());
let function_kind = FunctionKind::from_env(); }
let opt_main_path = matches.get_one::<PathBuf>(FLAG_MAIN);
// Step 1: compile the app and generate the .o file
let load_config = LoadConfig {
target,
function_kind,
// TODO: expose this from CLI?
render: roc_reporting::report::RenderTarget::ColorTerminal,
palette: roc_reporting::report::DEFAULT_PALETTE,
threading,
exec_mode: ExecutionMode::Test,
};
let load_result = roc_load::load_and_monomorphize(
arena,
path.to_path_buf(),
opt_main_path.cloned(),
RocCacheDir::Persistent(cache::roc_cache_dir().as_path()),
load_config,
);
let mut loaded = match load_result {
Ok(loaded) => loaded,
Err(LoadMonomorphizedError::LoadingProblem(problem)) => {
return handle_loading_problem(problem);
}
Err(LoadMonomorphizedError::ErrorModule(module)) => {
return handle_error_module(module, start_time.elapsed(), path.as_os_str(), false);
} }
flatten_paths
}; };
let problems = report_problems_monomorphized(&mut loaded);
let mut expectations = std::mem::take(&mut loaded.expectations); let mut all_files_total_failed_count = 0;
let mut all_files_total_passed_count = 0;
let interns = loaded.interns.clone(); for path in paths.iter() {
let sources = loaded.sources.clone(); let arena = &arena;
let function_kind = FunctionKind::from_env();
let (dyn_lib, expects_by_module, layout_interner) = let opt_main_path = matches.get_one::<PathBuf>(FLAG_MAIN);
roc_repl_expect::run::expect_mono_module_to_dylib(
arena, // Step 1: compile the app and generate the .o file
let load_config = LoadConfig {
target, target,
loaded, function_kind,
opt_level, // TODO: expose this from CLI?
LlvmBackendMode::CliTest, render: roc_reporting::report::RenderTarget::ColorTerminal,
) palette: roc_reporting::report::DEFAULT_PALETTE,
.unwrap(); threading,
exec_mode: ExecutionMode::Test,
// Print warnings before running tests. };
{ let load_result = roc_load::load_and_monomorphize(
debug_assert_eq!( arena,
problems.errors, 0, path.to_path_buf(),
"if there were errors, we would have already exited." opt_main_path.cloned(),
RocCacheDir::Persistent(cache::roc_cache_packages_dir().as_path()),
load_config,
); );
if problems.warnings > 0 {
problems.print_error_warning_count(start_time.elapsed()); let mut loaded = match load_result {
println!(".\n\nRunning tests…\n\n\x1B[36m{}\x1B[39m", "".repeat(80)); Ok(loaded) => loaded,
Err(LoadMonomorphizedError::LoadingProblem(problem)) => {
return handle_loading_problem(problem);
}
Err(LoadMonomorphizedError::ErrorModule(module)) => {
return handle_error_module(module, start_time.elapsed(), path.as_os_str(), false);
}
};
let problems = report_problems_monomorphized(&mut loaded);
let mut expectations = std::mem::take(&mut loaded.expectations);
let interns = loaded.interns.clone();
let sources = loaded.sources.clone();
let (dyn_lib, expects_by_module, layout_interner) =
roc_repl_expect::run::expect_mono_module_to_dylib(
arena,
target,
loaded,
opt_level,
LlvmBackendMode::CliTest,
)
.unwrap();
// Print warnings before running tests.
{
debug_assert_eq!(
problems.errors, 0,
"if there were errors, we would have already exited."
);
if problems.warnings > 0 {
problems.print_error_warning_count(start_time.elapsed());
println!(".\n\nRunning tests…\n\n\x1B[36m{}\x1B[39m", "".repeat(80));
}
}
// Run the tests.
let arena = &bumpalo::Bump::new();
let interns = arena.alloc(interns);
let mut writer = std::io::stdout();
let mut total_failed_count = 0;
let mut total_passed_count = 0;
let mut results_by_module = Vec::new();
let global_layout_interner = layout_interner.into_global();
let compilation_duration = start_time.elapsed();
for (module_id, expects) in expects_by_module.into_iter() {
let test_start_time = Instant::now();
let (failed_count, passed_count) = roc_repl_expect::run::run_toplevel_expects(
&mut writer,
roc_reporting::report::RenderTarget::ColorTerminal,
arena,
interns,
&global_layout_interner,
&dyn_lib,
&mut expectations,
expects,
)
.unwrap();
let tests_duration = test_start_time.elapsed();
results_by_module.push(ModuleTestResults {
module_id,
failed_count,
passed_count,
tests_duration,
});
total_failed_count += failed_count;
total_passed_count += passed_count;
}
let total_duration = start_time.elapsed();
all_files_total_failed_count += total_failed_count;
all_files_total_passed_count += total_passed_count;
if total_failed_count == 0 && total_passed_count == 0 {
// Only report no expectations found once.
continue;
} else if matches.get_flag(FLAG_VERBOSE) {
println!("Compiled in {} ms.", compilation_duration.as_millis());
for module_test_results in results_by_module {
print_test_results(module_test_results, &sources);
}
} else {
let test_summary_str =
test_summary(total_failed_count, total_passed_count, total_duration);
println!("{test_summary_str}");
} }
} }
if all_files_total_failed_count == 0 && all_files_total_passed_count == 0 {
// Run the tests.
let arena = &bumpalo::Bump::new();
let interns = arena.alloc(interns);
let mut writer = std::io::stdout();
let mut total_failed_count = 0;
let mut total_passed_count = 0;
let mut results_by_module = Vec::new();
let global_layout_interner = layout_interner.into_global();
let compilation_duration = start_time.elapsed();
for (module_id, expects) in expects_by_module.into_iter() {
let test_start_time = Instant::now();
let (failed_count, passed_count) = roc_repl_expect::run::run_toplevel_expects(
&mut writer,
roc_reporting::report::RenderTarget::ColorTerminal,
arena,
interns,
&global_layout_interner,
&dyn_lib,
&mut expectations,
expects,
)
.unwrap();
let tests_duration = test_start_time.elapsed();
results_by_module.push(ModuleTestResults {
module_id,
failed_count,
passed_count,
tests_duration,
});
total_failed_count += failed_count;
total_passed_count += passed_count;
}
let total_duration = start_time.elapsed();
if total_failed_count == 0 && total_passed_count == 0 {
// TODO print this in a more nicely formatted way! // TODO print this in a more nicely formatted way!
println!("No expectations were found."); println!("No expectations were found.");
@ -661,18 +684,32 @@ pub fn test(matches: &ArgMatches, target: Target) -> io::Result<i32> {
// running tests altogether! // running tests altogether!
Ok(2) Ok(2)
} else { } else {
if matches.get_flag(FLAG_VERBOSE) { Ok((all_files_total_failed_count > 0) as i32)
println!("Compiled in {} ms.", compilation_duration.as_millis()); }
for module_test_results in results_by_module { }
print_test_results(module_test_results, &sources);
}
} else {
let test_summary_str =
test_summary(total_failed_count, total_passed_count, total_duration);
println!("{test_summary_str}");
}
Ok((total_failed_count > 0) as i32) fn find_all_roc_files(path: &PathBuf, flatten_paths: &mut Vec<PathBuf>) {
if path.is_dir() {
if let Ok(entries) = std::fs::read_dir(path) {
entries.for_each(|entry| {
if let Ok(entry) = entry {
let entry_path = entry.path();
find_all_roc_files(&entry_path, flatten_paths);
}
});
} else {
eprintln!(
"\nSomething went wrong opening the directory {}\n",
path.display()
);
}
} else if path.is_file() {
match path.extension() {
Some(extension) if extension == "roc" => {
flatten_paths.push(path.clone());
}
_ => {}
}
} }
} }
@ -731,7 +768,6 @@ pub fn build(
roc_cache_dir: RocCacheDir<'_>, roc_cache_dir: RocCacheDir<'_>,
link_type: LinkType, link_type: LinkType,
) -> io::Result<i32> { ) -> io::Result<i32> {
use roc_build::program::build_file;
use BuildConfig::*; use BuildConfig::*;
let path = matches.get_one::<PathBuf>(ROC_FILE).unwrap(); let path = matches.get_one::<PathBuf>(ROC_FILE).unwrap();
@ -879,17 +915,10 @@ pub fn build(
LinkingStrategy::Surgical LinkingStrategy::Surgical
}; };
let prebuilt = { // All hosts should be prebuilt, this flag keeps the rebuilding behvaiour
let cross_compile = target != Target::default(); // as required for internal tests
let targeting_wasm = matches!(target.architecture(), Architecture::Wasm32); let build_host = matches.get_flag(FLAG_BUILD_HOST);
let suppress_build_host_warning = matches.get_flag(FLAG_SUPPRESS_BUILD_HOST_WARNING);
matches.get_flag(FLAG_PREBUILT) ||
// When compiling for a different target, assume a prebuilt platform.
// Otherwise compilation would most likely fail because many toolchains
// assume you're compiling for the current machine. We make an exception
// for Wasm, because cross-compiling is the norm in that case.
(cross_compile && !targeting_wasm)
};
let fuzz = matches.get_flag(FLAG_FUZZ); let fuzz = matches.get_flag(FLAG_FUZZ);
if fuzz && !matches!(code_gen_backend, CodeGenBackend::Llvm(_)) { if fuzz && !matches!(code_gen_backend, CodeGenBackend::Llvm(_)) {
@ -917,7 +946,7 @@ pub fn build(
let load_config = standard_load_config(target, build_ordering, threading); let load_config = standard_load_config(target, build_ordering, threading);
let res_binary_path = build_file( let res_binary_path = roc_build::program::build_file(
&arena, &arena,
target, target,
path.to_owned(), path.to_owned(),
@ -925,7 +954,8 @@ pub fn build(
emit_timings, emit_timings,
link_type, link_type,
linking_strategy, linking_strategy,
prebuilt, build_host,
suppress_build_host_warning,
wasm_dev_stack_bytes, wasm_dev_stack_bytes,
roc_cache_dir, roc_cache_dir,
load_config, load_config,
@ -986,7 +1016,15 @@ pub fn build(
// ManuallyDrop will leak the bytes because we don't drop manually // ManuallyDrop will leak the bytes because we don't drop manually
let bytes = &ManuallyDrop::new(std::fs::read(&binary_path).unwrap()); let bytes = &ManuallyDrop::new(std::fs::read(&binary_path).unwrap());
roc_run(&arena, opt_level, target, args, bytes, expect_metadata) roc_run(
&arena,
path,
opt_level,
target,
args,
bytes,
expect_metadata,
)
} }
BuildAndRunIfNoErrors => { BuildAndRunIfNoErrors => {
if problems.fatally_errored { if problems.fatally_errored {
@ -1021,7 +1059,15 @@ pub fn build(
// ManuallyDrop will leak the bytes because we don't drop manually // ManuallyDrop will leak the bytes because we don't drop manually
let bytes = &ManuallyDrop::new(std::fs::read(&binary_path).unwrap()); let bytes = &ManuallyDrop::new(std::fs::read(&binary_path).unwrap());
roc_run(&arena, opt_level, target, args, bytes, expect_metadata) roc_run(
&arena,
path,
opt_level,
target,
args,
bytes,
expect_metadata,
)
} }
} }
} }
@ -1034,6 +1080,7 @@ pub fn build(
fn roc_run<'a, I: IntoIterator<Item = &'a OsStr>>( fn roc_run<'a, I: IntoIterator<Item = &'a OsStr>>(
arena: &Bump, arena: &Bump,
script_path: &Path,
opt_level: OptLevel, opt_level: OptLevel,
target: Target, target: Target,
args: I, args: I,
@ -1073,7 +1120,14 @@ fn roc_run<'a, I: IntoIterator<Item = &'a OsStr>>(
Ok(0) Ok(0)
} }
_ => roc_run_native(arena, opt_level, args, binary_bytes, expect_metadata), _ => roc_run_native(
arena,
script_path,
opt_level,
args,
binary_bytes,
expect_metadata,
),
} }
} }
@ -1090,7 +1144,7 @@ fn os_str_as_utf8_bytes(os_str: &OsStr) -> &[u8] {
fn make_argv_envp<'a, I: IntoIterator<Item = S>, S: AsRef<OsStr>>( fn make_argv_envp<'a, I: IntoIterator<Item = S>, S: AsRef<OsStr>>(
arena: &'a Bump, arena: &'a Bump,
executable: &ExecutableFile, script_path: &Path,
args: I, args: I,
) -> ( ) -> (
bumpalo::collections::Vec<'a, CString>, bumpalo::collections::Vec<'a, CString>,
@ -1098,8 +1152,7 @@ fn make_argv_envp<'a, I: IntoIterator<Item = S>, S: AsRef<OsStr>>(
) { ) {
use bumpalo::collections::CollectIn; use bumpalo::collections::CollectIn;
let path = executable.as_path(); let path_cstring = CString::new(os_str_as_utf8_bytes(script_path.as_os_str())).unwrap();
let path_cstring = CString::new(os_str_as_utf8_bytes(path.as_os_str())).unwrap();
// argv is an array of pointers to strings passed to the new program // argv is an array of pointers to strings passed to the new program
// as its command-line arguments. By convention, the first of these // as its command-line arguments. By convention, the first of these
@ -1137,6 +1190,7 @@ fn make_argv_envp<'a, I: IntoIterator<Item = S>, S: AsRef<OsStr>>(
#[cfg(target_family = "unix")] #[cfg(target_family = "unix")]
fn roc_run_native<I: IntoIterator<Item = S>, S: AsRef<OsStr>>( fn roc_run_native<I: IntoIterator<Item = S>, S: AsRef<OsStr>>(
arena: &Bump, arena: &Bump,
script_path: &Path,
opt_level: OptLevel, opt_level: OptLevel,
args: I, args: I,
binary_bytes: &[u8], binary_bytes: &[u8],
@ -1145,7 +1199,7 @@ fn roc_run_native<I: IntoIterator<Item = S>, S: AsRef<OsStr>>(
use bumpalo::collections::CollectIn; use bumpalo::collections::CollectIn;
let executable = roc_run_executable_file_path(binary_bytes)?; let executable = roc_run_executable_file_path(binary_bytes)?;
let (argv_cstrings, envp_cstrings) = make_argv_envp(arena, &executable, args); let (argv_cstrings, envp_cstrings) = make_argv_envp(arena, script_path, args);
let argv: bumpalo::collections::Vec<*const c_char> = argv_cstrings let argv: bumpalo::collections::Vec<*const c_char> = argv_cstrings
.iter() .iter()
@ -1400,6 +1454,7 @@ fn roc_run_executable_file_path(binary_bytes: &[u8]) -> std::io::Result<Executab
#[cfg(not(target_family = "unix"))] #[cfg(not(target_family = "unix"))]
fn roc_run_native<I: IntoIterator<Item = S>, S: AsRef<OsStr>>( fn roc_run_native<I: IntoIterator<Item = S>, S: AsRef<OsStr>>(
arena: &Bump, // This should be passed an owned value, not a reference, so we can usefully mem::forget it! arena: &Bump, // This should be passed an owned value, not a reference, so we can usefully mem::forget it!
script_path: &Path,
opt_level: OptLevel, opt_level: OptLevel,
args: I, args: I,
binary_bytes: &[u8], binary_bytes: &[u8],
@ -1411,7 +1466,7 @@ fn roc_run_native<I: IntoIterator<Item = S>, S: AsRef<OsStr>>(
let executable = roc_run_executable_file_path(binary_bytes)?; let executable = roc_run_executable_file_path(binary_bytes)?;
// TODO forward the arguments // TODO forward the arguments
let (argv_cstrings, envp_cstrings) = make_argv_envp(&arena, &executable, args); let (argv_cstrings, envp_cstrings) = make_argv_envp(&arena, script_path, args);
let argv: bumpalo::collections::Vec<*const c_char> = argv_cstrings let argv: bumpalo::collections::Vec<*const c_char> = argv_cstrings
.iter() .iter()

View file

@ -4,27 +4,25 @@ use roc_build::link::LinkType;
use roc_build::program::{check_file, CodeGenBackend}; use roc_build::program::{check_file, CodeGenBackend};
use roc_cli::{ use roc_cli::{
build_app, format_files, format_src, test, BuildConfig, FormatMode, CMD_BUILD, CMD_CHECK, build_app, format_files, format_src, test, BuildConfig, FormatMode, CMD_BUILD, CMD_CHECK,
CMD_DEV, CMD_DOCS, CMD_FORMAT, CMD_GEN_STUB_LIB, CMD_GLUE, CMD_PREPROCESS_HOST, CMD_REPL, CMD_DEV, CMD_DOCS, CMD_FORMAT, CMD_GLUE, CMD_PREPROCESS_HOST, CMD_REPL, CMD_RUN, CMD_TEST,
CMD_RUN, CMD_TEST, CMD_VERSION, DIRECTORY_OR_FILES, FLAG_CHECK, FLAG_DEV, FLAG_LIB, FLAG_MAIN, CMD_VERSION, DIRECTORY_OR_FILES, FLAG_CHECK, FLAG_DEV, FLAG_LIB, FLAG_MAIN, FLAG_NO_COLOR,
FLAG_NO_COLOR, FLAG_NO_HEADER, FLAG_NO_LINK, FLAG_OUTPUT, FLAG_PP_DYLIB, FLAG_PP_HOST, FLAG_NO_HEADER, FLAG_NO_LINK, FLAG_OUTPUT, FLAG_PP_DYLIB, FLAG_PP_HOST, FLAG_PP_PLATFORM,
FLAG_PP_PLATFORM, FLAG_STDIN, FLAG_STDOUT, FLAG_TARGET, FLAG_TIME, GLUE_DIR, GLUE_SPEC, FLAG_STDIN, FLAG_STDOUT, FLAG_TARGET, FLAG_TIME, GLUE_DIR, GLUE_SPEC, ROC_FILE, VERSION,
ROC_FILE,
}; };
use roc_docs::generate_docs_html; use roc_docs::generate_docs_html;
use roc_error_macros::user_error; use roc_error_macros::user_error;
use roc_gen_dev::AssemblyBackendMode; use roc_gen_dev::AssemblyBackendMode;
use roc_gen_llvm::llvm::build::LlvmBackendMode; use roc_gen_llvm::llvm::build::LlvmBackendMode;
use roc_load::{FunctionKind, LoadingProblem, Threading}; use roc_load::{LoadingProblem, Threading};
use roc_packaging::cache::{self, RocCacheDir}; use roc_packaging::cache::{self, RocCacheDir};
use roc_target::Target; use roc_target::Target;
use std::fs::{self, FileType}; use std::fs::{self, FileType};
use std::io::BufRead;
use std::io::{self, Read, Write}; use std::io::{self, Read, Write};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::str::FromStr; use std::str::FromStr;
use target_lexicon::Triple; use target_lexicon::Triple;
use tempfile::Builder;
#[macro_use]
extern crate const_format;
#[global_allocator] #[global_allocator]
static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc; static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc;
@ -52,7 +50,7 @@ fn main() -> io::Result<()> {
BuildConfig::BuildAndRunIfNoErrors, BuildConfig::BuildAndRunIfNoErrors,
Triple::host().into(), Triple::host().into(),
None, None,
RocCacheDir::Persistent(cache::roc_cache_dir().as_path()), RocCacheDir::Persistent(cache::roc_cache_packages_dir().as_path()),
LinkType::Executable, LinkType::Executable,
) )
} else { } else {
@ -67,7 +65,7 @@ fn main() -> io::Result<()> {
BuildConfig::BuildAndRun, BuildConfig::BuildAndRun,
Triple::host().into(), Triple::host().into(),
None, None,
RocCacheDir::Persistent(cache::roc_cache_dir().as_path()), RocCacheDir::Persistent(cache::roc_cache_packages_dir().as_path()),
LinkType::Executable, LinkType::Executable,
) )
} else { } else {
@ -93,7 +91,7 @@ fn main() -> io::Result<()> {
BuildConfig::BuildAndRunIfNoErrors, BuildConfig::BuildAndRunIfNoErrors,
Triple::host().into(), Triple::host().into(),
None, None,
RocCacheDir::Persistent(cache::roc_cache_dir().as_path()), RocCacheDir::Persistent(cache::roc_cache_packages_dir().as_path()),
LinkType::Executable, LinkType::Executable,
) )
} else { } else {
@ -121,21 +119,6 @@ fn main() -> io::Result<()> {
Ok(1) Ok(1)
} }
} }
Some((CMD_GEN_STUB_LIB, matches)) => {
let input_path = matches.get_one::<PathBuf>(ROC_FILE).unwrap();
let target = matches
.get_one::<String>(FLAG_TARGET)
.and_then(|s| Target::from_str(s).ok())
.unwrap_or_default();
let function_kind = FunctionKind::from_env();
roc_linker::generate_stub_lib(
input_path,
RocCacheDir::Persistent(cache::roc_cache_dir().as_path()),
target,
function_kind,
);
Ok(0)
}
Some((CMD_PREPROCESS_HOST, matches)) => { Some((CMD_PREPROCESS_HOST, matches)) => {
let preprocess_host_err = let preprocess_host_err =
{ |msg: String| user_error!("\n\n ERROR PRE-PROCESSING HOST: {}\n\n", msg) }; { |msg: String| user_error!("\n\n ERROR PRE-PROCESSING HOST: {}\n\n", msg) };
@ -170,10 +153,14 @@ fn main() -> io::Result<()> {
let verbose_and_time = matches.get_one::<bool>(roc_cli::FLAG_VERBOSE).unwrap(); let verbose_and_time = matches.get_one::<bool>(roc_cli::FLAG_VERBOSE).unwrap();
let preprocessed_path = platform_path.with_file_name(target.prebuilt_surgical_host());
let metadata_path = platform_path.with_file_name(target.metadata_file_name());
roc_linker::preprocess_host( roc_linker::preprocess_host(
target, target,
host_path, host_path,
platform_path, metadata_path.as_path(),
preprocessed_path.as_path(),
dylib_path, dylib_path,
*verbose_and_time, *verbose_and_time,
*verbose_and_time, *verbose_and_time,
@ -202,7 +189,7 @@ fn main() -> io::Result<()> {
BuildConfig::BuildOnly, BuildConfig::BuildOnly,
target, target,
out_path, out_path,
RocCacheDir::Persistent(cache::roc_cache_dir().as_path()), RocCacheDir::Persistent(cache::roc_cache_packages_dir().as_path()),
link_type, link_type,
)?) )?)
} }
@ -220,26 +207,89 @@ fn main() -> io::Result<()> {
let opt_main_path = matches.get_one::<PathBuf>(FLAG_MAIN); let opt_main_path = matches.get_one::<PathBuf>(FLAG_MAIN);
match check_file( match roc_file_path.extension().and_then(OsStr::to_str) {
&arena, Some("md") => {
roc_file_path.to_owned(), // Extract the blocks of roc code
opt_main_path.cloned(), let file = fs::File::open(roc_file_path.as_path())?;
emit_timings, let markdown_file_reader = io::BufReader::new(file);
RocCacheDir::Persistent(cache::roc_cache_dir().as_path()), let mut roc_blocks: Vec<String> = Vec::new();
threading, let mut in_roc_block: bool = false;
) { let mut current_block = String::new();
Ok((problems, total_time)) => {
problems.print_error_warning_count(total_time);
Ok(problems.exit_code())
}
Err(LoadingProblem::FormattedReport(report)) => { for line in markdown_file_reader.lines() {
print!("{report}"); let line = line.unwrap();
if line == "```roc" {
in_roc_block = true;
} else if (line == "```") & in_roc_block {
in_roc_block = false;
roc_blocks.push(current_block);
current_block = String::new();
} else if in_roc_block {
current_block.push_str(&line);
current_block.push('\n');
}
}
Ok(1) // now check each block, we exit early if any single block does not check
let mut exit_code = 0;
for block in roc_blocks.iter() {
let mut file = Builder::new().suffix(".roc").tempfile()?;
write!(file, "{}", block)?;
match check_file(
&arena,
file.path().to_owned(),
opt_main_path.cloned(),
emit_timings,
RocCacheDir::Persistent(cache::roc_cache_packages_dir().as_path()),
threading,
) {
Ok((problems, total_time)) => {
problems.print_error_warning_count(total_time);
exit_code = problems.exit_code();
}
Err(LoadingProblem::FormattedReport(report)) => {
print!("{report}");
exit_code = 1;
}
Err(other) => {
panic!("build_file failed with error:\n{other:?}");
}
}
if exit_code != 0 {
break;
}
}
Ok(exit_code)
} }
Err(other) => { _ => {
panic!("build_file failed with error:\n{other:?}"); match check_file(
&arena,
roc_file_path.to_owned(),
opt_main_path.cloned(),
emit_timings,
RocCacheDir::Persistent(cache::roc_cache_packages_dir().as_path()),
threading,
) {
Ok((problems, total_time)) => {
problems.print_error_warning_count(total_time);
Ok(problems.exit_code())
}
Err(LoadingProblem::FormattedReport(report)) => {
print!("{report}");
Ok(1)
}
Err(other) => {
panic!("build_file failed with error:\n{other:?}");
}
}
} }
} }
} }
@ -364,11 +414,7 @@ fn main() -> io::Result<()> {
Ok(format_exit_code) Ok(format_exit_code)
} }
Some((CMD_VERSION, _)) => { Some((CMD_VERSION, _)) => {
print!( println!("roc {}", VERSION);
"{}",
concatcp!("roc ", include_str!("../../../version.txt"))
);
Ok(0) Ok(0)
} }
_ => unreachable!(), _ => unreachable!(),

View file

@ -1,11 +1,11 @@
app [main] { pf: platform "platform/main.roc" } app [main] { pf: platform "platform/main.roc" }
# see https://github.com/roc-lang/roc/issues/985
main : Task {} [] main : Task {} []
main = closure1 {} main =
# |> Task.after (\_ -> closure2 {}) closure1 {}
# |> Task.after (\_ -> closure3 {}) |> Task.await (\_ -> closure2 {})
# |> Task.after (\_ -> closure4 {}) |> Task.await (\_ -> closure3 {})
|> Task.await (\_ -> closure4 {})
# --- # ---
closure1 : {} -> Task {} [] closure1 : {} -> Task {} []
closure1 = \_ -> closure1 = \_ ->
@ -17,32 +17,32 @@ toUnitBorrowed = \x -> Str.countUtf8Bytes x
foo = \f, x -> f x foo = \f, x -> f x
# --- # ---
# closure2 : {} -> Task.Task {} [] closure2 : {} -> Task {} []
# closure2 = \_ -> closure2 = \_ ->
# x : Str x : Str
# x = "a long string such that it's malloced" x = "a long string such that it's malloced"
#
# Task.succeed {} Task.ok {}
# |> Task.map (\_ -> x) |> Task.map (\_ -> x)
# |> Task.map toUnit |> Task.map toUnit
#
# toUnit = \_ -> {} toUnit = \_ -> {}
#
# # --- # # ---
# closure3 : {} -> Task.Task {} [] closure3 : {} -> Task {} []
# closure3 = \_ -> closure3 = \_ ->
# x : Str x : Str
# x = "a long string such that it's malloced" x = "a long string such that it's malloced"
#
# Task.succeed {} Task.ok {}
# |> Task.after (\_ -> Task.succeed x |> Task.map (\_ -> {})) |> Task.await (\_ -> Task.ok x |> Task.map (\_ -> {}))
#
# # --- # # ---
# closure4 : {} -> Task.Task {} [] closure4 : {} -> Task {} []
# closure4 = \_ -> closure4 = \_ ->
# x : Str x : Str
# x = "a long string such that it's malloced" x = "a long string such that it's malloced"
#
# Task.succeed {} Task.ok {}
# |> Task.after (\_ -> Task.succeed x) |> Task.await (\_ -> Task.ok x)
# |> Task.map (\_ -> {}) |> Task.map (\_ -> {})

View file

@ -0,0 +1,3 @@
app [main] { pf: platform "main.roc" }
main = Task.ok {}

View file

@ -112,8 +112,6 @@ comptime {
const Unit = extern struct {}; const Unit = extern struct {};
pub fn main() !u8 { pub fn main() !u8 {
const stderr = std.io.getStdErr().writer();
// The size might be zero; if so, make it at least 8 so that we don't have a nullptr // The size might be zero; if so, make it at least 8 so that we don't have a nullptr
const size = @max(@as(usize, @intCast(roc__mainForHost_1_exposed_size())), 8); const size = @max(@as(usize, @intCast(roc__mainForHost_1_exposed_size())), 8);
const raw_output = roc_alloc(@as(usize, @intCast(size)), @alignOf(u64)).?; const raw_output = roc_alloc(@as(usize, @intCast(size)), @alignOf(u64)).?;
@ -123,26 +121,15 @@ pub fn main() !u8 {
roc_dealloc(raw_output, @alignOf(u64)); roc_dealloc(raw_output, @alignOf(u64));
} }
var timer = std.time.Timer.start() catch unreachable;
roc__mainForHost_1_exposed_generic(output); roc__mainForHost_1_exposed_generic(output);
const closure_data_pointer = @as([*]u8, @ptrCast(output)); const closure_data_pointer = @as([*]u8, @ptrCast(output));
call_the_closure(closure_data_pointer); call_the_closure(closure_data_pointer);
const nanos = timer.read();
const seconds = (@as(f64, @floatFromInt(nanos)) / 1_000_000_000.0);
stderr.print("runtime: {d:.3}ms\n", .{seconds * 1000}) catch unreachable;
return 0; return 0;
} }
fn to_seconds(tms: std.os.timespec) f64 {
return @as(f64, @floatFromInt(tms.tv_sec)) + (@as(f64, @floatFromInt(tms.tv_nsec)) / 1_000_000_000.0);
}
fn call_the_closure(closure_data_pointer: [*]u8) void { fn call_the_closure(closure_data_pointer: [*]u8) void {
const allocator = std.heap.page_allocator; const allocator = std.heap.page_allocator;

View file

@ -1,255 +0,0 @@
app [main] { pf: platform "platform/main.roc" }
import pf.PlatformTasks
Color : [Red, Black]
Tree a b : [Leaf, Node Color (Tree a b) a b (Tree a b)]
Map : Tree I64 Bool
ConsList a : [Nil, Cons a (ConsList a)]
main : Task {} []
main =
{ value, isError } = PlatformTasks.getInt!
inputResult =
if isError then
Err GetIntError
else
Ok value
when inputResult is
Ok n ->
m = makeMap n # koka original n = 4_200_000
val = fold (\_, v, r -> if v then r + 1 else r) m 0
val
|> Num.toStr
|> PlatformTasks.putLine
Err GetIntError ->
PlatformTasks.putLine "Error: Failed to get Integer from stdin."
boom : Str -> a
boom = \_ -> boom ""
makeMap : I64 -> Map
makeMap = \n ->
makeMapHelp n n Leaf
makeMapHelp : I64, I64, Map -> Map
makeMapHelp = \total, n, m ->
when n is
0 -> m
_ ->
n1 = n - 1
powerOf10 =
n |> Num.isMultipleOf 10
t1 = insert m n powerOf10
isFrequency =
n |> Num.isMultipleOf 4
key = n1 + ((total - n1) // 5)
t2 = if isFrequency then delete t1 key else t1
makeMapHelp total n1 t2
fold : (a, b, omega -> omega), Tree a b, omega -> omega
fold = \f, tree, b ->
when tree is
Leaf -> b
Node _ l k v r -> fold f r (f k v (fold f l b))
depth : Tree * * -> I64
depth = \tree ->
when tree is
Leaf -> 1
Node _ l _ _ r -> 1 + depth l + depth r
insert : Map, I64, Bool -> Map
insert = \t, k, v -> if isRed t then setBlack (ins t k v) else ins t k v
setBlack : Tree a b -> Tree a b
setBlack = \tree ->
when tree is
Node _ l k v r -> Node Black l k v r
_ -> tree
isRed : Tree a b -> Bool
isRed = \tree ->
when tree is
Node Red _ _ _ _ -> Bool.true
_ -> Bool.false
ins : Tree I64 Bool, I64, Bool -> Tree I64 Bool
ins = \tree, kx, vx ->
when tree is
Leaf ->
Node Red Leaf kx vx Leaf
Node Red a ky vy b ->
when Num.compare kx ky is
LT -> Node Red (ins a kx vx) ky vy b
GT -> Node Red a ky vy (ins b kx vx)
EQ -> Node Red a ky vy (ins b kx vx)
Node Black a ky vy b ->
when Num.compare kx ky is
LT ->
when isRed a is
Bool.true -> balanceLeft (ins a kx vx) ky vy b
Bool.false -> Node Black (ins a kx vx) ky vy b
GT ->
when isRed b is
Bool.true -> balanceRight a ky vy (ins b kx vx)
Bool.false -> Node Black a ky vy (ins b kx vx)
EQ ->
Node Black a kx vx b
balanceLeft : Tree a b, a, b, Tree a b -> Tree a b
balanceLeft = \l, k, v, r ->
when l is
Leaf ->
Leaf
Node _ (Node Red lx kx vx rx) ky vy ry ->
Node Red (Node Black lx kx vx rx) ky vy (Node Black ry k v r)
Node _ ly ky vy (Node Red lx kx vx rx) ->
Node Red (Node Black ly ky vy lx) kx vx (Node Black rx k v r)
Node _ lx kx vx rx ->
Node Black (Node Red lx kx vx rx) k v r
balanceRight : Tree a b, a, b, Tree a b -> Tree a b
balanceRight = \l, k, v, r ->
when r is
Leaf ->
Leaf
Node _ (Node Red lx kx vx rx) ky vy ry ->
Node Red (Node Black l k v lx) kx vx (Node Black rx ky vy ry)
Node _ lx kx vx (Node Red ly ky vy ry) ->
Node Red (Node Black l k v lx) kx vx (Node Black ly ky vy ry)
Node _ lx kx vx rx ->
Node Black l k v (Node Red lx kx vx rx)
isBlack : Color -> Bool
isBlack = \c ->
when c is
Black -> Bool.true
Red -> Bool.false
Del a b : [Del (Tree a b) Bool]
setRed : Map -> Map
setRed = \t ->
when t is
Node _ l k v r ->
Node Red l k v r
_ ->
t
makeBlack : Map -> Del I64 Bool
makeBlack = \t ->
when t is
Node Red l k v r ->
Del (Node Black l k v r) Bool.false
_ ->
Del t Bool.true
rebalanceLeft = \c, l, k, v, r ->
when l is
Node Black _ _ _ _ ->
Del (balanceLeft (setRed l) k v r) (isBlack c)
Node Red lx kx vx rx ->
Del (Node Black lx kx vx (balanceLeft (setRed rx) k v r)) Bool.false
_ ->
boom "unreachable"
rebalanceRight = \c, l, k, v, r ->
when r is
Node Black _ _ _ _ ->
Del (balanceRight l k v (setRed r)) (isBlack c)
Node Red lx kx vx rx ->
Del (Node Black (balanceRight l k v (setRed lx)) kx vx rx) Bool.false
_ ->
boom "unreachable"
delMin = \t ->
when t is
Node Black Leaf k v r ->
when r is
Leaf ->
Delmin (Del Leaf Bool.true) k v
_ ->
Delmin (Del (setBlack r) Bool.false) k v
Node Red Leaf k v r ->
Delmin (Del r Bool.false) k v
Node c l k v r ->
when delMin l is
Delmin (Del lx Bool.true) kx vx ->
Delmin (rebalanceRight c lx k v r) kx vx
Delmin (Del lx Bool.false) kx vx ->
Delmin (Del (Node c lx k v r) Bool.false) kx vx
Leaf ->
Delmin (Del t Bool.false) 0 Bool.false
delete : Tree I64 Bool, I64 -> Tree I64 Bool
delete = \t, k ->
when del t k is
Del tx _ ->
setBlack tx
del : Tree I64 Bool, I64 -> Del I64 Bool
del = \t, k ->
when t is
Leaf ->
Del Leaf Bool.false
Node cx lx kx vx rx ->
if (k < kx) then
when del lx k is
Del ly Bool.true ->
rebalanceRight cx ly kx vx rx
Del ly Bool.false ->
Del (Node cx ly kx vx rx) Bool.false
else if (k > kx) then
when del rx k is
Del ry Bool.true ->
rebalanceLeft cx lx kx vx ry
Del ry Bool.false ->
Del (Node cx lx kx vx ry) Bool.false
else
when rx is
Leaf ->
if isBlack cx then makeBlack lx else Del lx Bool.false
Node _ _ _ _ _ ->
when delMin rx is
Delmin (Del ry Bool.true) ky vy ->
rebalanceLeft cx lx ky vy ry
Delmin (Del ry Bool.false) ky vy ->
Del (Node cx lx ky vy ry) Bool.false

View file

@ -3,7 +3,6 @@ app [main] { pf: platform "platform/main.roc" }
import pf.PlatformTasks import pf.PlatformTasks
import AStar import AStar
#main : Task {} *
main = main =
PlatformTasks.putLine! (showBool test1) PlatformTasks.putLine! (showBool test1)

View file

@ -1,18 +0,0 @@
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br" }
import pf.Stdin
import pf.Stdout
main =
Stdout.line! "\nLet's count down from 3 together - all you have to do is press <ENTER>."
_ = Stdin.line!
Task.loop 3 tick
tick = \n ->
if n == 0 then
Stdout.line! "🎉 SURPRISE! Happy Birthday! 🎂"
Task.ok (Done {})
else
Stdout.line! (n |> Num.toStr |> \s -> "$(s)...")
_ = Stdin.line!
Task.ok (Step (n - 1))

View file

@ -1,35 +0,0 @@
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br" }
import pf.Stdin
import pf.Stdout
main =
Stdout.line! "🗣 Shout into this cave and hear the echo! 👂👂👂"
Task.loop {} tick
tick : {} -> Task [Step {}, Done {}] _
tick = \{} ->
when Stdin.line |> Task.result! is
Ok str -> Stdout.line (echo str) |> Task.map Step
Err (StdinErr EndOfFile) -> Stdout.line (echo "Received end of input (EOF).") |> Task.map Done
Err (StdinErr err) -> Stdout.line (echo "Unable to read input $(Inspect.toStr err)") |> Task.map Done
echo : Str -> Str
echo = \shout ->
silence = \length ->
spaceInUtf8 = 32
List.repeat spaceInUtf8 length
shout
|> Str.toUtf8
|> List.mapWithIndex
(\_, i ->
length = (List.len (Str.toUtf8 shout) - i)
phrase = (List.split (Str.toUtf8 shout) length).before
List.concat (silence (if i == 0 then 2 * length else length)) phrase)
|> List.join
|> Str.fromUtf8
|> Result.withDefault ""

View file

@ -1,30 +0,0 @@
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br" }
import pf.Stdout
import pf.Stderr
import pf.Env
main =
task =
Env.decode "EDITOR"
|> Task.await (\editor -> Stdout.line "Your favorite editor is $(editor)!")
|> Task.await (\{} -> Env.decode "SHLVL")
|> Task.await
(\lvl ->
when lvl is
1u8 -> Stdout.line "You're running this in a root shell!"
n ->
lvlStr = Num.toStr n
Stdout.line "Your current shell level is $(lvlStr)!")
|> Task.await \{} -> Env.decode "LETTERS"
Task.attempt task \result ->
when result is
Ok letters ->
joinedLetters = Str.joinWith letters " "
Stdout.line "Your favorite letters are: $(joinedLetters)"
Err _ ->
Stderr.line "I couldn't find your favorite letters in the environment variables!"

View file

@ -1,36 +0,0 @@
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br" }
import pf.Stdout
import pf.File
import pf.Path
import pf.Env
main : Task {} [Exit I32 Str]_
main =
pathStr = "out.txt"
task =
cwdPath = Env.cwd!
cwdStr = Path.display cwdPath
Stdout.line! "Current working directory: $(cwdStr)"
dirEntries = Path.listDir! cwdPath
contentsStr = Str.joinWith (List.map dirEntries Path.display) "\n "
Stdout.line! "Directory contents:\n $(contentsStr)\n"
Stdout.line! "Writing a string to out.txt"
File.writeUtf8! pathStr "a string!"
contents = File.readUtf8! pathStr
Stdout.line! "I read the file back. Its contents: \"$(contents)\""
when Task.result! task is
Ok {} -> Stdout.line! "Successfully wrote a string to out.txt"
Err err ->
msg =
when err is
FileWriteErr _ PermissionDenied -> "PermissionDenied"
FileWriteErr _ Unsupported -> "Unsupported"
FileWriteErr _ (Unrecognized _ other) -> other
FileReadErr _ _ -> "Error reading file"
_ -> "Uh oh, there was an error!"
Task.err (Exit 1 msg)

View file

@ -1,12 +0,0 @@
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br" }
import pf.Stdin
import pf.Stdout
main =
Stdout.line! "What's your first name?"
firstName = Stdin.line!
Stdout.line! "What's your last name?"
lastName = Stdin.line!
Stdout.line "Hi, $(firstName) $(lastName)! 👋"

View file

@ -1,23 +0,0 @@
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br" }
import pf.Http
import pf.Stdout
main =
request = {
method: Get,
headers: [],
url: "http://www.example.com",
mimeType: "",
body: [],
timeout: TimeoutMilliseconds 5000,
}
resp = Http.send! request
output =
when resp |> Http.handleStringResponse is
Err err -> crash (Http.errorToString err)
Ok body -> body
Stdout.line output

View file

@ -1,12 +0,0 @@
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br" }
import pf.Stdout
import "test-file.txt" as testFile
main =
# Due to the functions we apply on testFile, it will be inferred as a List U8.
testFile
|> List.map Num.toU64
|> List.sum
|> Num.toStr
|> Stdout.line!

View file

@ -1,12 +0,0 @@
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br" }
import pf.Stdout
import "test-file.txt" as testFile : _ # the _ is optional
main =
# Due to the functions we apply on testFile, it will be inferred as a List U8.
testFile
|> List.map Num.toU64
|> List.sum
|> Num.toStr
|> Stdout.line!

View file

@ -1,7 +0,0 @@
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br" }
import pf.Stdout
import "ingested-file.roc" as ownCode : Str
main =
Stdout.line! "\nThis roc file can print its own source code. The source is:\n\n$(ownCode)"

View file

@ -1,85 +0,0 @@
app [main] {
pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br",
}
import pf.Stdout
main =
file = strParam { name: "file" }
argParser =
{ cliBuild <-
file,
count: numParam { name: "count" },
doubled: numParam { name: "doubled" }
|> cliMap \d -> d * 2,
}
args = ["parse-args", "file.txt", "5", "7"]
when argParser |> parseArgs args is
Ok data -> Stdout.line "Success: $(Inspect.toStr data)"
Err (FailedToParse message) -> Stdout.line "Failed: $(message)"
ArgParseErr : [NoMoreArgs, InvalidParam ParamConfig]
ParamConfig : {
name : Str,
type : [Num, Str],
}
ArgParser out : {
params : List ParamConfig,
parser : List Str -> Result (out, List Str) ArgParseErr,
}
strParam : { name : Str } -> ArgParser Str
strParam = \{ name } ->
parser = \args ->
when args is
[] -> Err NoMoreArgs
[first, .. as rest] -> Ok (first, rest)
{ params: [{ name, type: Str }], parser }
numParam : { name : Str } -> ArgParser U64
numParam = \{ name } ->
param = { name, type: Num }
parser = \args ->
when args is
[] -> Err NoMoreArgs
[first, .. as rest] ->
when Str.toU64 first is
Ok num -> Ok (num, rest)
Err InvalidNumStr -> Err (InvalidParam param)
{ params: [param], parser }
cliMap : ArgParser a, (a -> b) -> ArgParser b
cliMap = \{ params, parser }, mapper ->
mappedParser = \args ->
(data, afterData) = parser? args
Ok (mapper data, afterData)
{
params,
parser: mappedParser,
}
cliBuild : ArgParser a, ArgParser b, (a, b -> c) -> ArgParser c
cliBuild = \firstWeaver, secondWeaver, combine ->
allParams = List.concat firstWeaver.params secondWeaver.params
combinedParser = \args ->
(firstValue, afterFirst) = firstWeaver.parser? args
(secondValue, afterSecond) = secondWeaver.parser? afterFirst
Ok (combine firstValue secondValue, afterSecond)
{ params: allParams, parser: combinedParser }
parseArgs : ArgParser a, List Str -> Result a [FailedToParse Str]
parseArgs = \{ params: _, parser }, args ->
when parser (List.dropFirst args 1) is
Ok (data, []) -> Ok data
Ok (_data, extraArgs) -> Err (FailedToParse "Got $(List.len extraArgs |> Inspect.toStr) extra args")
Err NoMoreArgs -> Err (FailedToParse "I needed more args")
Err (InvalidParam param) -> Err (FailedToParse "Parameter '$(param.name)' needed a $(Inspect.toStr param.type)")

View file

@ -1,51 +0,0 @@
app [main] {
cli: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br",
parser: "https://github.com/lukewilliamboswell/roc-parser/releases/download/0.5.2/9VrPjwfQQ1QeSL3CfmWr2Pr9DESdDIXy97pwpuq84Ck.tar.br",
}
import cli.Stdout
import cli.Stderr
import parser.Core exposing [Parser, buildPrimitiveParser, many]
import parser.String exposing [parseStr]
main =
lettersInput = "AAAiBByAABBwBtCCCiAyArBBx"
ifLetterA = \l -> l == A
when parseStr (many letterParser) lettersInput is
Ok letters ->
letters
|> List.keepIf ifLetterA
|> List.map \_ -> 1
|> List.sum
|> Num.toStr
|> \countLetterA -> Stdout.line "I counted $(countLetterA) letter A's!"
Err _ -> Stderr.line "Ooops, something went wrong parsing letters"
Letter : [A, B, C, Other]
letterParser : Parser (List U8) Letter
letterParser =
buildPrimitiveParser \input ->
valResult =
when input is
[] -> Err (ParsingFailure "Nothing to parse")
['A', ..] -> Ok A
['B', ..] -> Ok B
['C', ..] -> Ok C
_ -> Ok Other
valResult
|> Result.map \val -> { val, input: List.dropFirst input 1 }
expect
input = "B"
parser = letterParser
result = parseStr parser input
result == Ok B
expect
input = "BCXA"
parser = many letterParser
result = parseStr parser input
result == Ok [B, C, Other, A]

View file

@ -1,63 +0,0 @@
app [main] {
pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br",
parser: "https://github.com/lukewilliamboswell/roc-parser/releases/download/0.5.2/9VrPjwfQQ1QeSL3CfmWr2Pr9DESdDIXy97pwpuq84Ck.tar.br",
}
import pf.Stdout
import pf.Stderr
import parser.Core exposing [map, keep]
import parser.String exposing [strFromUtf8]
import parser.CSV
input : Str
input = "Airplane!,1980,\"Robert Hays,Julie Hagerty\"\r\nCaddyshack,1980,\"Chevy Chase,Rodney Dangerfield,Ted Knight,Michael O'Keefe,Bill Murray\""
main =
when CSV.parseStr movieInfoParser input is
Ok movies ->
moviesString =
movies
|> List.map movieInfoExplanation
|> Str.joinWith ("\n")
nMovies = List.len movies |> Num.toStr
Stdout.line "$(nMovies) movies were found:\n\n$(moviesString)\n\nParse success!\n"
Err problem ->
when problem is
ParsingFailure failure ->
Stderr.line "Parsing failure: $(failure)\n"
ParsingIncomplete leftover ->
leftoverStr = leftover |> List.map strFromUtf8 |> List.map (\val -> "\"$(val)\"") |> Str.joinWith ", "
Stderr.line "Parsing incomplete. Following leftover fields while parsing a record: $(leftoverStr)\n"
SyntaxError error ->
Stderr.line "Parsing failure. Syntax error in the CSV: $(error)"
MovieInfo := { title : Str, releaseYear : U64, actors : List Str }
movieInfoParser =
CSV.record (\title -> \releaseYear -> \actors -> @MovieInfo { title, releaseYear, actors })
|> keep (CSV.field CSV.string)
|> keep (CSV.field CSV.u64)
|> keep (CSV.field actorsParser)
actorsParser =
CSV.string
|> map \val -> Str.split val ","
movieInfoExplanation = \@MovieInfo { title, releaseYear, actors } ->
enumeratedActors = enumerate actors
releaseYearStr = Num.toStr releaseYear
"The movie '$(title)' was released in $(releaseYearStr) and stars $(enumeratedActors)"
enumerate : List Str -> Str
enumerate = \elements ->
{ before: inits, others: last } = List.split elements (List.len elements - 1)
last
|> List.prepend (inits |> Str.joinWith ", ")
|> Str.joinWith " and "

View file

@ -1 +0,0 @@
Used by ingested-file-bytes.roc and ingested-file-bytes-no-ann.roc

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,129 +0,0 @@
const std = @import("std");
const builtin = @import("builtin");
const str = @import("glue").str;
const RocStr = str.RocStr;
const testing = std.testing;
const expectEqual = testing.expectEqual;
const expect = testing.expect;
const Align = 2 * @alignOf(usize);
extern fn malloc(size: usize) callconv(.C) ?*align(Align) anyopaque;
extern fn realloc(c_ptr: [*]align(Align) u8, size: usize) callconv(.C) ?*anyopaque;
extern fn free(c_ptr: [*]align(Align) u8) callconv(.C) void;
extern fn memcpy(dst: [*]u8, src: [*]u8, size: usize) callconv(.C) void;
extern fn memset(dst: [*]u8, value: i32, size: usize) callconv(.C) void;
const DEBUG: bool = false;
export fn roc_alloc(size: usize, alignment: u32) callconv(.C) ?*anyopaque {
if (DEBUG) {
var ptr = malloc(size);
const stdout = std.io.getStdOut().writer();
stdout.print("alloc: {d} (alignment {d}, size {d})\n", .{ ptr, alignment, size }) catch unreachable;
return ptr;
} else {
return malloc(size);
}
}
export fn roc_realloc(c_ptr: *anyopaque, new_size: usize, old_size: usize, alignment: u32) callconv(.C) ?*anyopaque {
if (DEBUG) {
const stdout = std.io.getStdOut().writer();
stdout.print("realloc: {d} (alignment {d}, old_size {d})\n", .{ c_ptr, alignment, old_size }) catch unreachable;
}
return realloc(@as([*]align(Align) u8, @alignCast(@ptrCast(c_ptr))), new_size);
}
export fn roc_dealloc(c_ptr: *anyopaque, alignment: u32) callconv(.C) void {
if (DEBUG) {
const stdout = std.io.getStdOut().writer();
stdout.print("dealloc: {d} (alignment {d})\n", .{ c_ptr, alignment }) catch unreachable;
}
free(@as([*]align(Align) u8, @alignCast(@ptrCast(c_ptr))));
}
export fn roc_panic(msg: *RocStr, tag_id: u32) callconv(.C) void {
_ = tag_id;
const stderr = std.io.getStdErr().writer();
stderr.print("Application crashed with message\n\n {s}\n\nShutting down\n", .{msg.asSlice()}) catch unreachable;
std.process.exit(1);
}
export fn roc_dbg(loc: *RocStr, msg: *RocStr, src: *RocStr) callconv(.C) void {
// This platform uses stdout for testing purposes instead of the normal stderr.
const stdout = std.io.getStdOut().writer();
stdout.print("[{s}] {s} = {s}\n", .{ loc.asSlice(), src.asSlice(), msg.asSlice() }) catch unreachable;
}
export fn roc_memset(dst: [*]u8, value: i32, size: usize) callconv(.C) void {
return memset(dst, value, size);
}
extern fn kill(pid: c_int, sig: c_int) c_int;
extern fn shm_open(name: *const i8, oflag: c_int, mode: c_uint) c_int;
extern fn mmap(addr: ?*anyopaque, length: c_uint, prot: c_int, flags: c_int, fd: c_int, offset: c_uint) *anyopaque;
extern fn getppid() c_int;
fn roc_getppid() callconv(.C) c_int {
return getppid();
}
fn roc_getppid_windows_stub() callconv(.C) c_int {
return 0;
}
fn roc_send_signal(pid: c_int, sig: c_int) callconv(.C) c_int {
return kill(pid, sig);
}
fn roc_shm_open(name: *const i8, oflag: c_int, mode: c_uint) callconv(.C) c_int {
return shm_open(name, oflag, mode);
}
fn roc_mmap(addr: ?*anyopaque, length: c_uint, prot: c_int, flags: c_int, fd: c_int, offset: c_uint) callconv(.C) *anyopaque {
return mmap(addr, length, prot, flags, fd, offset);
}
comptime {
if (builtin.os.tag == .macos or builtin.os.tag == .linux) {
@export(roc_getppid, .{ .name = "roc_getppid", .linkage = .Strong });
@export(roc_mmap, .{ .name = "roc_mmap", .linkage = .Strong });
@export(roc_send_signal, .{ .name = "roc_send_signal", .linkage = .Strong });
@export(roc_shm_open, .{ .name = "roc_shm_open", .linkage = .Strong });
}
if (builtin.os.tag == .windows) {
@export(roc_getppid_windows_stub, .{ .name = "roc_getppid", .linkage = .Strong });
}
}
const mem = std.mem;
const Allocator = mem.Allocator;
extern fn roc__mainForHost_1_exposed_generic(*RocStr) void;
const Unit = extern struct {};
pub fn main() u8 {
const stdout = std.io.getStdOut().writer();
const stderr = std.io.getStdErr().writer();
var timer = std.time.Timer.start() catch unreachable;
// actually call roc to populate the callresult
var callresult = RocStr.empty();
roc__mainForHost_1_exposed_generic(&callresult);
const nanos = timer.read();
const seconds = (@as(f64, @floatFromInt(nanos)) / 1_000_000_000.0);
// stdout the result
stdout.print("{s}", .{callresult.asSlice()}) catch unreachable;
callresult.decref();
stderr.print("runtime: {d:.3}ms\n", .{seconds * 1000}) catch unreachable;
return 0;
}

View file

@ -1,9 +0,0 @@
interface Transitive
exposes [
add,
]
imports []
add = \num1, num2 -> (num1 + num2)
expect add 1 2 == 3

View file

@ -1,5 +0,0 @@
package "transitive-tests"
exposes [
Direct,
]
packages {}

View file

@ -1,4 +0,0 @@
interface Dep1 exposes [str1] imports [Dep2]
str1 : Str
str1 = Dep2.str2

View file

@ -1,4 +0,0 @@
interface Dep2 exposes [str2] imports []
str2 : Str
str2 = "I am Dep2.str2"

View file

@ -1,7 +0,0 @@
app "multi-dep-str"
packages { pf: "platform/main.roc" }
imports [Dep1]
provides [main] to pf
main : Str
main = Dep1.str1

View file

@ -1,9 +0,0 @@
platform "multi-module"
requires {}{ main : Str }
exposes []
packages {}
imports []
provides [mainForHost]
mainForHost : Str
mainForHost = main

View file

@ -1,7 +0,0 @@
app "multi-dep-thunk"
packages { pf: "platform/main.roc" }
imports [Dep1]
provides [main] to pf
main : Str
main = Dep1.value1 {}

View file

@ -1,119 +0,0 @@
const std = @import("std");
const builtin = @import("builtin");
const str = @import("glue").str;
const RocStr = str.RocStr;
const testing = std.testing;
const expectEqual = testing.expectEqual;
const expect = testing.expect;
const mem = std.mem;
const Allocator = mem.Allocator;
extern fn roc__mainForHost_1_exposed_generic(*RocStr) void;
const Align = 2 * @alignOf(usize);
extern fn malloc(size: usize) callconv(.C) ?*anyopaque;
extern fn realloc(c_ptr: [*]align(@alignOf(u128)) u8, size: usize) callconv(.C) ?*anyopaque;
extern fn free(c_ptr: [*]align(@alignOf(u128)) u8) callconv(.C) void;
extern fn memcpy(dst: [*]u8, src: [*]u8, size: usize) callconv(.C) void;
extern fn memset(dst: [*]u8, value: i32, size: usize) callconv(.C) void;
export fn roc_alloc(size: usize, alignment: u32) callconv(.C) ?*anyopaque {
_ = alignment;
return malloc(size);
}
export fn roc_realloc(c_ptr: *anyopaque, new_size: usize, old_size: usize, alignment: u32) callconv(.C) ?*anyopaque {
_ = old_size;
_ = alignment;
return realloc(@as([*]align(Align) u8, @alignCast(@ptrCast(c_ptr))), new_size);
}
export fn roc_dealloc(c_ptr: *anyopaque, alignment: u32) callconv(.C) void {
_ = alignment;
free(@as([*]align(Align) u8, @alignCast(@ptrCast(c_ptr))));
}
export fn roc_memset(dst: [*]u8, value: i32, size: usize) callconv(.C) void {
return memset(dst, value, size);
}
export fn roc_panic(msg: *RocStr, tag_id: u32) callconv(.C) void {
const stderr = std.io.getStdErr().writer();
switch (tag_id) {
0 => {
stderr.print("Roc standard library crashed with message\n\n {s}\n\nShutting down\n", .{msg.asSlice()}) catch unreachable;
},
1 => {
stderr.print("Application crashed with message\n\n {s}\n\nShutting down\n", .{msg.asSlice()}) catch unreachable;
},
else => unreachable,
}
std.process.exit(1);
}
export fn roc_dbg(loc: *RocStr, msg: *RocStr, src: *RocStr) callconv(.C) void {
const stderr = std.io.getStdErr().writer();
stderr.print("[{s}] {s} = {s}\n", .{ loc.asSlice(), src.asSlice(), msg.asSlice() }) catch unreachable;
}
extern fn kill(pid: c_int, sig: c_int) c_int;
extern fn shm_open(name: *const i8, oflag: c_int, mode: c_uint) c_int;
extern fn mmap(addr: ?*anyopaque, length: c_uint, prot: c_int, flags: c_int, fd: c_int, offset: c_uint) *anyopaque;
extern fn getppid() c_int;
fn roc_getppid() callconv(.C) c_int {
return getppid();
}
fn roc_getppid_windows_stub() callconv(.C) c_int {
return 0;
}
fn roc_shm_open(name: *const i8, oflag: c_int, mode: c_uint) callconv(.C) c_int {
return shm_open(name, oflag, mode);
}
fn roc_mmap(addr: ?*anyopaque, length: c_uint, prot: c_int, flags: c_int, fd: c_int, offset: c_uint) callconv(.C) *anyopaque {
return mmap(addr, length, prot, flags, fd, offset);
}
comptime {
if (builtin.os.tag == .macos or builtin.os.tag == .linux) {
@export(roc_getppid, .{ .name = "roc_getppid", .linkage = .Strong });
@export(roc_mmap, .{ .name = "roc_mmap", .linkage = .Strong });
@export(roc_shm_open, .{ .name = "roc_shm_open", .linkage = .Strong });
}
if (builtin.os.tag == .windows) {
@export(roc_getppid_windows_stub, .{ .name = "roc_getppid", .linkage = .Strong });
}
}
const Unit = extern struct {};
pub export fn main() i32 {
const stdout = std.io.getStdOut().writer();
const stderr = std.io.getStdErr().writer();
var timer = std.time.Timer.start() catch unreachable;
// actually call roc to populate the callresult
var callresult = RocStr.empty();
roc__mainForHost_1_exposed_generic(&callresult);
const nanos = timer.read();
const seconds = (@as(f64, @floatFromInt(nanos)) / 1_000_000_000.0);
// stdout the result
stdout.print("{s}\n", .{callresult.asSlice()}) catch unreachable;
callresult.decref();
stderr.print("runtime: {d:.3}ms\n", .{seconds * 1000}) catch unreachable;
return 0;
}
fn to_seconds(tms: std.os.timespec) f64 {
return @as(f64, @floatFromInt(tms.tv_sec)) + (@as(f64, @floatFromInt(tms.tv_nsec)) / 1_000_000_000.0);
}

View file

@ -1,9 +0,0 @@
platform "multi-dep-thunk"
requires {}{ main : Str }
exposes []
packages {}
imports []
provides [mainForHost]
mainForHost : Str
mainForHost = main

View file

@ -1,6 +0,0 @@
app "packages-test"
packages { pf: "platform/main.roc", json: "json/main.roc", csv: "csv/main.roc" }
imports [json.JsonParser, csv.Csv]
provides [main] to pf
main = "Hello, World! $(JsonParser.example) $(Csv.example)"

View file

@ -1,6 +0,0 @@
interface Csv
exposes [example]
imports []
example : Str
example = "This text came from a CSV package!"

View file

@ -1,6 +0,0 @@
interface JsonParser
exposes [example]
imports []
example : Str
example = "This text came from a package!"

View file

@ -1,10 +0,0 @@
app [main] {
pf: platform "../packages/platform/main.roc",
one: "one/main.roc",
two: "two/main.roc",
}
import one.One
import two.Two
main = "$(One.example) | $(Two.example)"

View file

@ -1,8 +0,0 @@
app [main] {
pf: platform "../packages/platform/main.roc",
one: "one/main.roc",
}
import one.One
main = One.example

View file

@ -1,8 +0,0 @@
app [main] {
pf: platform "../packages/platform/main.roc",
zero: "zero/main.roc",
}
import zero.Zero
main = Zero.example

View file

@ -1,3 +0,0 @@
package [Zero] {
one: "../one/main.roc"
}

View file

@ -1,3 +0,0 @@
interface ExposedNotDefined
exposes [bar]
imports []

View file

@ -1,7 +0,0 @@
interface UnusedImport
exposes [plainText, emText]
imports [Symbol.{ Ident }]
plainText = \str -> PlainText str
emText = \str -> EmText str

View file

@ -1,7 +0,0 @@
interface UnusedImportButWithALongFileNameForTesting
exposes [plainText, emText]
imports [Symbol.{ Ident }]
plainText = \str -> PlainText str
emText = \str -> EmText str

View file

@ -0,0 +1,17 @@
# A Simple Markdown Example
This file contains `form.roc` embedded as a block in Markdown. It lets us test that `roc check` works with Markdown.
```roc
module [foo]
foo = "Foo"
```
Excitingly, we can have another block of Roc code as well! (In this case it is the same one...)
```roc
module [bar]
bar = "Bar"
```

View file

@ -1,8 +0,0 @@
module {
sendHttpReq,
getEnvVar
} -> [hi]
hi : Str
hi =
"hi"

View file

@ -1,8 +0,0 @@
app [main] {
pf: platform "../fixtures/multi-dep-str/platform/main.roc",
}
import BadAnn { appId: "one" }
main =
""

View file

@ -1,7 +0,0 @@
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br" }
import pf.Stdout
import Menu { echo: Stdout.line }
main =
Menu.menu "Agus"

View file

@ -1,6 +0,0 @@
app [main] {
pf: platform "./platform/main.roc"
}
main =
"from app"

View file

@ -0,0 +1,15 @@
---
source: crates/cli/tests/cli_tests.rs
expression: cli_check_out.normalize_stdout_and_stderr()
---
── MISSING DEFINITION in tests/test-projects/known_bad/ExposedNotDefined.roc ───
bar is listed as exposed, but it isn't defined in this module.
You can fix this by adding a definition for bar, or by removing it
from exposes.
────────────────────────────────────────────────────────────────────────────────
1 error and 0 warning found in <ignored for test> ms

View file

@ -0,0 +1,28 @@
---
source: crates/cli/tests/cli_tests.rs
expression: cli_check_out.normalize_stdout_and_stderr()
---
── TYPE MISMATCH in tests/test-projects/known_bad/TypeError.roc ────────────────
Something is off with the body of the main definition:
3│ main : Str -> Task {} []
4│ main = \_ ->
5│ "this is a string, not a Task {} [] function like the platform expects."
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The body is a string of type:
Str
But the type annotation on main says it should be:
Task {} []
Tip: Add type annotations to functions or values to help you figure
this out.
────────────────────────────────────────────────────────────────────────────────
1 error and 0 warning found in <ignored for test> ms

View file

@ -0,0 +1,17 @@
---
source: crates/cli/tests/cli_tests.rs
expression: cli_check_out.normalize_stdout_and_stderr()
---
── UNUSED IMPORT in ...nown_bad/UnusedImportButWithALongFileNameForTesting.roc ─
Symbol is imported but not used.
3│ import Symbol exposing [Ident]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Since Symbol isn't used, you don't need to import it.
────────────────────────────────────────────────────────────────────────────────
0 error and 1 warning found in <ignored for test> ms

View file

@ -0,0 +1,18 @@
---
source: crates/cli/tests/cli_tests.rs
expression: cli_test_out.normalize_stdout_and_stderr()
---
── UNRECOGNIZED PACKAGE in tests/test-projects/module_imports_pkg/Module.roc ───
This module is trying to import from `pkg`:
3│ import pkg.Foo
^^^^^^^
A lowercase name indicates a package shorthand, but I don't know which
packages are available.
When checking a module directly, I look for a `main.roc` app or
package to resolve shorthands from.
You can create it, or specify an existing one with the --main flag.

View file

@ -0,0 +1,21 @@
---
source: crates/cli/tests/cli_tests.rs
expression: cli_test_out.normalize_stdout_and_stderr()
---
── UNRECOGNIZED PACKAGE in ...rojects/module_imports_pkg/ImportsUnknownPkg.roc ─
This module is trying to import from `cli`:
3│ import cli.Foo
^^^^^^^
A lowercase name indicates a package shorthand, but I don't recognize
this one. Did you mean one of these?
pkg
Note: I'm using the following module to resolve package shorthands:
tests/test-projects/module_imports_pkg/app.roc
You can specify a different one with the --main flag.

View file

@ -0,0 +1,5 @@
---
source: crates/cli/tests/cli_tests.rs
expression: out.normalize_stdout_and_stderr()
---
(@Community {friends: [{2}, {2}, {0, 1}], people: [(@Person {age: 27, favoriteColor: Blue, firstName: \"John\", hasBeard: Bool.true, lastName: \"Smith\"}), (@Person {age: 47, favoriteColor: Green, firstName: \"Debby\", hasBeard: Bool.false, lastName: \"Johnson\"}), (@Person {age: 33, favoriteColor: (RGB (255, 255, 0)), firstName: \"Jane\", hasBeard: Bool.false, lastName: \"Doe\"})]})

View file

@ -0,0 +1,24 @@
---
source: crates/cli/tests/cli_tests.rs
expression: cli_dev_out.normalize_stdout_and_stderr()
---
── EXPECT FAILED in tests/test-projects/expects/expects.roc ────────────────────
This expectation failed:
25│ expect words == []
^^^^^^^^^^^
When it failed, these variables had these values:
words : List Str
words = ["this", "will", "for", "sure", "be", "a", "large", "string", "so", "when", "we", "split", "it", "it", "will", "use", "seamless", "slices", "which", "affect", "printing"]
Program finished!
[<ignored for tests>:28] x = 42
[<ignored for tests>:30] "Fjoer en ferdjer frieten oan dyn geve lea" = "Fjoer en ferdjer frieten oan dyn geve lea"
[<ignored for tests>:32] "this is line 24" = "this is line 24"
[<ignored for tests>:18] x = "abc"
[<ignored for tests>:18] x = 10
[<ignored for tests>:18] x = (A (B C))

View file

@ -0,0 +1,48 @@
---
source: crates/cli/tests/cli_tests.rs
expression: cli_test_out.normalize_stdout_and_stderr()
---
── EXPECT FAILED in tests/test-projects/expects/expects.roc ────────────────────
This expectation failed:
6│ expect a == 2
^^^^^^
When it failed, these variables had these values:
a : Num *
a = 1
── EXPECT FAILED in tests/test-projects/expects/expects.roc ────────────────────
This expectation failed:
7│ expect a == 3
^^^^^^
When it failed, these variables had these values:
a : Num *
a = 1
── EXPECT FAILED in tests/test-projects/expects/expects.roc ────────────────────
This expectation failed:
11│> expect
12│> a = makeA
13│> b = 2i64
14│>
15│> a == b
When it failed, these variables had these values:
a : Int Signed64
a = 1
b : I64
b = 2
1 failed and 0 passed in <ignored for test> ms.

View file

@ -0,0 +1,33 @@
---
source: crates/cli/tests/cli_tests.rs
expression: cli_dev_out.normalize_stdout_and_stderr()
---
App1.baseUrl: https://api.example.com/one
App2.baseUrl: http://api.example.com/two
App3.baseUrl: https://api.example.com/three
App1.getUser 1: https://api.example.com/one/users/1
App2.getUser 2: http://api.example.com/two/users/2
App3.getUser 3: https://api.example.com/three/users/3
App1.getPost 1: https://api.example.com/one/posts/1
App2.getPost 2: http://api.example.com/two/posts/2
App3.getPost 3: https://api.example.com/three/posts/3
App1.getPosts [1, 2]: ["https://api.example.com/one/posts/1", "https://api.example.com/one/posts/2"]
App2.getPosts [3, 4]: ["http://api.example.com/two/posts/3", "http://api.example.com/two/posts/4"]
App2.getPosts [5, 6]: ["http://api.example.com/two/posts/5", "http://api.example.com/two/posts/6"]
App1.getPostComments 1: https://api.example.com/one/posts/1/comments
App2.getPostComments 2: http://api.example.com/two/posts/2/comments
App2.getPostComments 3: http://api.example.com/two/posts/3/comments
App1.getCompanies [1, 2]: ["https://api.example.com/one/companies/1", "https://api.example.com/one/companies/2"]
App2.getCompanies [3, 4]: ["http://api.example.com/two/companies/3", "http://api.example.com/two/companies/4"]
App2.getCompanies [5, 6]: ["http://api.example.com/two/companies/5", "http://api.example.com/two/companies/6"]
App1.getPostAliased 1: https://api.example.com/one/posts/1
App2.getPostAliased 2: http://api.example.com/two/posts/2
App3.getPostAliased 3: https://api.example.com/three/posts/3
App1.baseUrlAliased: https://api.example.com/one
App2.baseUrlAliased: http://api.example.com/two
App3.baseUrlAliased: https://api.example.com/three
App1.getUserSafe 1: https://api.example.com/one/users/1
Prod.getUserSafe 2: http://api.example.com/prod_1/users/2?safe=true
usersApp1: ["https://api.example.com/one/users/1", "https://api.example.com/one/users/2", "https://api.example.com/one/users/3"]
getUserApp3Nested 3: https://api.example.com/three/users/3
usersApp3Passed: ["https://api.example.com/three/users/1", "https://api.example.com/three/users/2", "https://api.example.com/three/users/3"]

View file

@ -0,0 +1,43 @@
---
source: crates/cli/tests/cli_tests.rs
assertion_line: 429
expression: cli_dev_out.normalize_stdout_and_stderr()
---
── TOO MANY ARGS in tests/test-projects/module_params/arity_mismatch.roc ───────
The getUser function expects 1 argument, but it got 2 instead:
12│ $(Api.getUser 1 2)
^^^^^^^^^^^
Are there any missing commas? Or missing parentheses?
── TOO MANY ARGS in tests/test-projects/module_params/arity_mismatch.roc ───────
This value is not a function, but it was given 1 argument:
13│ $(Api.baseUrl 1)
^^^^^^^^^^^
Are there any missing commas? Or missing parentheses?
── TOO FEW ARGS in tests/test-projects/module_params/arity_mismatch.roc ────────
The getPostComment function expects 2 arguments, but it got only 1:
16│ $(Api.getPostComment 1)
^^^^^^^^^^^^^^^^^^
Roc does not allow functions to be partially applied. Use a closure to
make partial application explicit.
────────────────────────────────────────────────────────────────────────────────
3 error and 0 warning found in <ignored for test> ms
.
You can run <ignored for tests>

View file

@ -0,0 +1,48 @@
---
source: crates/cli/tests/cli_tests.rs
assertion_line: 445
expression: cli_dev_out.normalize_stdout_and_stderr()
---
── TYPE MISMATCH in tests/test-projects/module_params/BadAnn.roc ───────────────
Something is off with the body of the fnAnnotatedAsValue definition:
3│ fnAnnotatedAsValue : Str
4│> fnAnnotatedAsValue = \postId, commentId ->
5│> "/posts/$(postId)/comments/$(Num.toStr commentId)"
The body is an anonymous function of type:
Str, Num * -> Str
But the type annotation on fnAnnotatedAsValue says it should be:
Str
── TYPE MISMATCH in tests/test-projects/module_params/BadAnn.roc ───────────────
Something is off with the body of the missingArg definition:
7│ missingArg : Str -> Str
8│> missingArg = \postId, _ ->
9│> "/posts/$(postId)/comments"
The body is an anonymous function of type:
(Str, ? -> Str)
But the type annotation on missingArg says it should be:
(Str -> Str)
Tip: It looks like it takes too many arguments. I'm seeing 1 extra.
────────────────────────────────────────────────────────────────────────────────
2 error and 1 warning found in <ignored for test> ms
.
You can run <ignored for tests>

View file

@ -0,0 +1,28 @@
---
source: crates/cli/tests/cli_tests.rs
assertion_line: 476
expression: cli_dev_out.normalize_stdout_and_stderr()
---
── TYPE MISMATCH in tests/test-projects/module_params/unexpected_fn.roc ────────
This argument to this string interpolation has an unexpected type:
11│ $(Api.getPost)
^^^^^^^^^^^
The argument is an anonymous function of type:
U32 -> Str
But this string interpolation needs its argument to be:
Str
────────────────────────────────────────────────────────────────────────────────
1 error and 0 warning found in <ignored for test> ms
.
You can run <ignored for tests>

View file

@ -0,0 +1,11 @@
---
source: crates/cli/tests/cli_tests.rs
expression: cli_test_out.normalize_stdout_and_stderr()
---
Compiled in <ignored for test> ms.
Direct.roc:
0 failed and 2 passed in <ignored for test> ms.
Transitive.roc:
0 failed and 1 passed in <ignored for test> ms.

View file

@ -0,0 +1,17 @@
---
source: crates/cli/tests/cli_tests.rs
expression: cli_check_out.normalize_stdout_and_stderr()
---
── UNUSED IMPORT in tests/test-projects/known_bad/UnusedImport.roc ─────────────
Symbol is imported but not used.
3│ import Symbol exposing [Ident]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Since Symbol isn't used, you don't need to import it.
────────────────────────────────────────────────────────────────────────────────
0 error and 1 warning found in <ignored for test> ms

View file

@ -110,18 +110,10 @@ comptime {
pub export fn main() u8 { pub export fn main() u8 {
const stdout = std.io.getStdOut().writer(); const stdout = std.io.getStdOut().writer();
var timer = std.time.Timer.start() catch unreachable;
const result = roc__mainForHost_1_exposed(10); const result = roc__mainForHost_1_exposed(10);
const nanos = timer.read();
const seconds = (@as(f64, @floatFromInt(nanos)) / 1_000_000_000.0);
stdout.print("{d}\n", .{result}) catch unreachable; stdout.print("{d}\n", .{result}) catch unreachable;
const stderr = std.io.getStdErr().writer();
stderr.print("runtime: {d:.3}ms\n", .{seconds * 1000}) catch unreachable;
return 0; return 0;
} }

View file

@ -1,7 +1,4 @@
app "fibonacci" app [main] { pf: platform "fibonacci-platform/main.roc" }
packages { pf: "fibonacci-platform/main.roc" }
imports []
provides [main] to pf
main = \n -> fib n 0 1 main = \n -> fib n 0 1

View file

@ -127,8 +127,6 @@ pub export fn main() u8 {
var roc_list = RocList{ .elements = numbers, .length = NUM_NUMS, .capacity = NUM_NUMS }; var roc_list = RocList{ .elements = numbers, .length = NUM_NUMS, .capacity = NUM_NUMS };
var timer = std.time.Timer.start() catch unreachable;
// actually call roc to populate the callresult // actually call roc to populate the callresult
const callresult: RocList = roc__mainForHost_1_exposed(roc_list); const callresult: RocList = roc__mainForHost_1_exposed(roc_list);
@ -136,9 +134,6 @@ pub export fn main() u8 {
const length = @min(20, callresult.length); const length = @min(20, callresult.length);
var result = callresult.elements[0..length]; var result = callresult.elements[0..length];
const nanos = timer.read();
const seconds = (@as(f64, @floatFromInt(nanos)) / 1_000_000_000.0);
for (result, 0..) |x, i| { for (result, 0..) |x, i| {
if (i == 0) { if (i == 0) {
stdout.print("[{}, ", .{x}) catch unreachable; stdout.print("[{}, ", .{x}) catch unreachable;
@ -149,12 +144,5 @@ pub export fn main() u8 {
} }
} }
const stderr = std.io.getStdErr().writer();
stderr.print("runtime: {d:.3}ms\n", .{seconds * 1000}) catch unreachable;
return 0; return 0;
} }
fn to_seconds(tms: std.os.timespec) f64 {
return @as(f64, @floatFromInt(tms.tv_sec)) + (@as(f64, @floatFromInt(tms.tv_nsec)) / 1_000_000_000.0);
}

View file

@ -1,7 +1,4 @@
app "quicksort" app [quicksort] { pf: platform "quicksort-platform/main.roc" }
packages { pf: "quicksort-platform/main.roc" }
imports []
provides [quicksort] to pf
quicksort = \originalList -> quicksort = \originalList ->
n = List.len originalList n = List.len originalList

View file

@ -1,4 +1,4 @@
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br" } app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.16.0/O00IPk-Krg_diNS2dVWlI0ZQP794Vctxzv0ha96mK0E.tar.br" }
import pf.Stdout import pf.Stdout

View file

@ -0,0 +1,35 @@
app [main!] { pf: platform "../test-platform-effects-zig/main.roc" }
import pf.Effect
main! : {} => {}
main! = \{} -> tick! {}
tick! = \{} ->
line = Effect.getLine! {}
if !(Str.isEmpty line) then
Effect.putLine! (echo line)
else
Effect.putLine! "Received no input."
echo : Str -> Str
echo = \shout ->
silence = \length -> List.repeat ' ' length
shout
|> Str.toUtf8
|> List.mapWithIndex \_, i ->
length = (List.len (Str.toUtf8 shout) - i)
phrase = (List.splitAt (Str.toUtf8 shout) length).before
List.concat (silence (if i == 0 then 2 * length else length)) phrase
|> List.join
|> Str.fromUtf8
|> Result.withDefault ""
expect
message = "hello!"
echoedMessage = echo message
echoedMessage == " hello! hello hell hel he h"

View file

@ -0,0 +1,23 @@
app [main!] { pf: platform "../test-platform-effects-zig/main.roc" }
import pf.Effect
main! : {} => {}
main! = \{} ->
good = [0, 2, 4] |> List.forEachTry! validate!
expect good == Ok {}
bad = [6, 8, 9, 10] |> List.forEachTry! validate!
expect bad == Err 9
{}
validate! : U32 => Result {} U32
validate! = \x ->
if Num.isEven x then
Effect.putLine! "✅ $(Num.toStr x)"
Ok {}
else
Effect.putLine! "$(Num.toStr x) is not even! ABORT!"
Err x

View file

@ -0,0 +1,27 @@
app [main!] { pf: platform "../test-platform-effects-zig/main.roc" }
import pf.Effect
main! : {} => {}
main! = \{} ->
first = ask! "What's your first name?"
last = ask! "What's your last name?"
Effect.putLine! "\nHi, $(first) $(last)!\n"
when Str.toU8 (ask! "How old are you?") is
Err InvalidNumStr ->
Effect.putLine! "Enter a valid number"
Ok age if age >= 18 ->
Effect.putLine! "\nNice! You can vote!"
Ok age ->
Effect.putLine! "\nYou'll be able to vote in $(Num.toStr (18 - age)) years"
Effect.putLine! "\nBye! 👋"
ask! : Str => Str
ask! = \question ->
Effect.putLine! question
Effect.getLine! {}

View file

@ -0,0 +1,7 @@
app [main!] { pf: platform "../test-platform-effects-zig/main.roc" }
import pf.Effect
main! : {} => {}
main! = \{} ->
Effect.putLine! "I'm an effect 👻"

View file

@ -0,0 +1,8 @@
app [main!] { pf: platform "../test-platform-effects-zig/main.roc" }
import pf.Effect
main! : {} => {}
main! = \{} ->
_ = Effect.getLine! {}
Effect.putLine! "I asked for input and I ignored it. Deal with it! 😎"

Some files were not shown because too many files have changed in this diff Show more