diff --git a/.github/workflows/web-demos.yml b/.github/workflows/web-demos.yml index 0a171c90..7c097696 100644 --- a/.github/workflows/web-demos.yml +++ b/.github/workflows/web-demos.yml @@ -43,10 +43,8 @@ jobs: ~/.npm ~/.cargo/registry ~/.cargo/git - ~/.ghc-wasm/.cabal target - dist-newstyle - key: web-demos-${{ runner.os }}-${{ hashFiles('flake.lock', 'Cargo.lock', 'cabal.project', 'packages/**/*.cabal', 'packages/coln-js-runtime/package-lock.json', 'examples/sync-demo/pnpm-lock.yaml') }} + key: web-demos-${{ runner.os }}-${{ hashFiles('flake.lock', 'Cargo.lock', 'packages/coln-js-runtime/package-lock.json', 'examples/sync-demo/pnpm-lock.yaml') }} restore-keys: | web-demos-${{ runner.os }}- @@ -55,57 +53,60 @@ jobs: env: VITE_BASE: /sync/ - - name: Build web compiler - run: | - nix develop --accept-flake-config --command wasm32-wasi-cabal update 'hackage.haskell.org,2026-07-15T17:07:49Z' - nix develop --accept-flake-config --command just examples/build-web-compiler + # TODO commented out to prevent CI compiling a GHC from source + # - name: Build web compiler + # run: | + # nix build --accept-flake-config .#web-compiler + # mkdir -p _build/web + # cp -rL result _build/web/compiler + # chmod -R u+w _build/web/compiler - - name: Assemble deploy directory - run: | - cp examples/index.html examples/style.css _build/web/ - cp -r examples/sync-demo/dist _build/web/sync + # - name: Assemble deploy directory + # run: | + # cp examples/index.html examples/style.css _build/web/ + # cp -r examples/sync-demo/dist _build/web/sync - - name: Deploy PR preview to Netlify - id: deploy-preview - if: github.event_name == 'pull_request' && env.NETLIFY_AUTH_TOKEN != '' && env.NETLIFY_SITE_ID != '' - run: | - npx --yes netlify-cli@latest deploy \ - --auth "$NETLIFY_AUTH_TOKEN" \ - --site "$NETLIFY_SITE_ID" \ - --dir _build/web \ - --no-build \ - --alias "pr-${{ github.event.pull_request.number }}" \ - --message "PR #${{ github.event.pull_request.number }} web demos" \ - --json > netlify-deploy.json + # - name: Deploy PR preview to Netlify + # id: deploy-preview + # if: github.event_name == 'pull_request' && env.NETLIFY_AUTH_TOKEN != '' && env.NETLIFY_SITE_ID != '' + # run: | + # npx --yes netlify-cli@latest deploy \ + # --auth "$NETLIFY_AUTH_TOKEN" \ + # --site "$NETLIFY_SITE_ID" \ + # --dir _build/web \ + # --no-build \ + # --alias "pr-${{ github.event.pull_request.number }}" \ + # --message "PR #${{ github.event.pull_request.number }} web demos" \ + # --json > netlify-deploy.json - cat netlify-deploy.json - deploy_url=$(node -e "const d = require('./netlify-deploy.json'); console.log(d.deploy_ssl_url || d.ssl_url || d.deploy_url || d.url)") - echo "deploy-url=$deploy_url" >> "$GITHUB_OUTPUT" + # cat netlify-deploy.json + # deploy_url=$(node -e "const d = require('./netlify-deploy.json'); console.log(d.deploy_ssl_url || d.ssl_url || d.deploy_url || d.url)") + # echo "deploy-url=$deploy_url" >> "$GITHUB_OUTPUT" - - name: Comment with deploy preview URL - if: github.event_name == 'pull_request' && steps.deploy-preview.outputs.deploy-url != '' - uses: actions/github-script@v8 - with: - script: | - const marker = '' - const body = `${marker}\nWeb demo preview: ${{ steps.deploy-preview.outputs.deploy-url }}` - const { owner, repo } = context.repo - const issue_number = context.issue.number - const comments = await github.rest.issues.listComments({ owner, repo, issue_number, per_page: 100 }) - const existing = comments.data.find(comment => comment.body?.includes(marker)) - if (existing) { - await github.rest.issues.updateComment({ owner, repo, comment_id: existing.id, body }) - } else { - await github.rest.issues.createComment({ owner, repo, issue_number, body }) - } + # - name: Comment with deploy preview URL + # if: github.event_name == 'pull_request' && steps.deploy-preview.outputs.deploy-url != '' + # uses: actions/github-script@v8 + # with: + # script: | + # const marker = '' + # const body = `${marker}\nWeb demo preview: ${{ steps.deploy-preview.outputs.deploy-url }}` + # const { owner, repo } = context.repo + # const issue_number = context.issue.number + # const comments = await github.rest.issues.listComments({ owner, repo, issue_number, per_page: 100 }) + # const existing = comments.data.find(comment => comment.body?.includes(marker)) + # if (existing) { + # await github.rest.issues.updateComment({ owner, repo, comment_id: existing.id, body }) + # } else { + # await github.rest.issues.createComment({ owner, repo, issue_number, body }) + # } - - name: Deploy main to Netlify production - if: github.event_name == 'push' && env.NETLIFY_AUTH_TOKEN != '' && env.NETLIFY_SITE_ID != '' - run: | - npx --yes netlify-cli@latest deploy \ - --auth "$NETLIFY_AUTH_TOKEN" \ - --site "$NETLIFY_SITE_ID" \ - --dir _build/web \ - --no-build \ - --prod \ - --message "web demos ${{ github.sha }}" + # - name: Deploy main to Netlify production + # if: github.event_name == 'push' && env.NETLIFY_AUTH_TOKEN != '' && env.NETLIFY_SITE_ID != '' + # run: | + # npx --yes netlify-cli@latest deploy \ + # --auth "$NETLIFY_AUTH_TOKEN" \ + # --site "$NETLIFY_SITE_ID" \ + # --dir _build/web \ + # --no-build \ + # --prod \ + # --message "web demos ${{ github.sha }}" diff --git a/cabal.project b/cabal.project index ed74ba6b..8e09550c 100644 --- a/cabal.project +++ b/cabal.project @@ -18,6 +18,9 @@ jobs: $ncpus semaphore: true tests: True +if os(wasi) + shared: True + constraints: -- Cabal otherwise sometimes picks bad build plans due to faulty metadata in older versions of these libs: -- https://github.com/haskell/lsp/issues/641 diff --git a/flake.lock b/flake.lock index 145fa01a..0b5ef9e6 100644 --- a/flake.lock +++ b/flake.lock @@ -18,78 +18,23 @@ "type": "github" } }, - "flake-utils_2": { - "inputs": { - "systems": "systems_2" - }, - "locked": { - "lastModified": 1731533236, - "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, - "ghc-wasm-meta": { - "inputs": { - "flake-utils": "flake-utils_2", - "nixpkgs": "nixpkgs" - }, - "locked": { - "host": "gitlab.haskell.org", - "lastModified": 1782204359, - "narHash": "sha256-iC+iPfZOCTrB/WZaNHb9CQsZ+9u78UO/dyVZ/3OZB+M=", - "owner": "haskell-wasm", - "repo": "ghc-wasm-meta", - "rev": "9480caba4d6e622a67298d81307086582cc3ffe6", - "type": "gitlab" - }, - "original": { - "host": "gitlab.haskell.org", - "owner": "haskell-wasm", - "repo": "ghc-wasm-meta", - "type": "gitlab" - } - }, "nixpkgs": { "locked": { - "lastModified": 1781472460, - "narHash": "sha256-dqwpb1o0xIwb1rv3PPbpY7RBm8heFiOLAubszNLBefc=", - "owner": "NixOS", + "lastModified": 1784317943, + "narHash": "sha256-TzNcqiT4OtFkM+FTF4bbpZVZ+3x9v7B0mIifqgwyCC4=", + "owner": "georgefst", "repo": "nixpkgs", - "rev": "8c14fa3ccacddaec887134e523083b63c8ea57ac", + "rev": "1a3237b27813e77f360f9877bfae3f7640eeb6db", "type": "github" }, "original": { - "owner": "NixOS", - "ref": "nixpkgs-26.05-darwin", + "owner": "georgefst", + "ref": "ghc-wasm", "repo": "nixpkgs", "type": "github" } }, "nixpkgs_2": { - "locked": { - "lastModified": 1780145889, - "narHash": "sha256-md0zn0RnwNvPyASas1yG5YUuwQ4ALA6ucL50l0DvqCo=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "8c50a710ddca43d7a530fb805ad55bde8d0141c5", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixos-26.05", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs_3": { "locked": { "lastModified": 1744536153, "narHash": "sha256-awS2zRgF4uTwrOKwwiJcByDzDOdo3Q1rPZbiHQg/N38=", @@ -108,14 +53,13 @@ "root": { "inputs": { "flake-utils": "flake-utils", - "ghc-wasm-meta": "ghc-wasm-meta", - "nixpkgs": "nixpkgs_2", + "nixpkgs": "nixpkgs", "rust-overlay": "rust-overlay" } }, "rust-overlay": { "inputs": { - "nixpkgs": "nixpkgs_3" + "nixpkgs": "nixpkgs_2" }, "locked": { "lastModified": 1782184651, @@ -145,21 +89,6 @@ "repo": "default", "type": "github" } - }, - "systems_2": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } } }, "root": "root", diff --git a/flake.nix b/flake.nix index 6713147c..c3558dee 100644 --- a/flake.nix +++ b/flake.nix @@ -1,9 +1,8 @@ { inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixos-26.05"; + nixpkgs.url = "github:georgefst/nixpkgs/ghc-wasm"; flake-utils.url = "github:numtide/flake-utils"; rust-overlay.url = "github:oxalica/rust-overlay"; - ghc-wasm-meta.url = "gitlab:haskell-wasm/ghc-wasm-meta?host=gitlab.haskell.org"; }; outputs = inputs@{ @@ -135,6 +134,29 @@ ''; }; + # TODO find some way to DRY this with `just examples/build-web-compiler` + # TODO do the combined web-demo build in Nix, removing logic from YAML + web-compiler = pkgs.runCommand "coln-web-compiler" + { + nativeBuildInputs = [ + pkgs.nodejs + pkgs.jq + ]; + } + '' + dist=$out/dist + mkdir -p $dist + cp ${wasmColnPackages.coln-compiler-wasm}/bin/coln-compiler-wasm* $dist/coln.wasm + libdir=$(${wasmHaskellPackages.ghc}/bin/${wasmHaskellPackages.ghc.targetPrefix}ghc --print-libdir) + node "$libdir/post-link.mjs" --input $dist/coln.wasm --output $dist/ghc_wasm_jsffi.js + cp ${./packages/coln-compiler-wasm/loadHaskellWasm.js} $dist/loadHaskellWasm.js + cp ${./examples/compiler-demo/index.html} $out/index.html + cp ${./examples/style.css} $out/style.css + mkdir -p $out/examples + cp ${./packages/coln-compiler/test/golden}/*.coln $out/examples/ + ls -1 $out/examples/*.coln | xargs -n1 basename | jq -nR '[inputs]' > $out/examples/index.json + ''; + format-hs = nuShellCheck [pkgs.fourmolu] ./nix/checks/format-hs.nu; format-cabal = nuShellCheck [pkgs.haskellPackages.cabal-gild] ./nix/checks/format-cabal.nu; @@ -189,7 +211,30 @@ inherit (packages) forester coln-manual-dev; - haskell-wasm = inputs.ghc-wasm-meta.packages.${system}; + # TODO DRY with native builds + wasmHaskellPackages = pkgs.pkgsCross.wasi32.haskell.packages.ghc9141.override { + overrides = _: prev: { + # mirrors cabal.project + # TODO these are actually about GHC 9.14 compatibility rather than Wasm + ordered-containers = pkgs.haskell.lib.doJailbreak prev.ordered-containers; + prettyprinter-lucid = pkgs.haskell.lib.doJailbreak prev.prettyprinter-lucid; + }; + }; + wasmColnPackages = rec { + diagnostician = wasmHaskellPackages.callPackage ./packages/diagnostician { }; + diagnostician-html = wasmHaskellPackages.callPackage ./packages/diagnostician-html { + inherit diagnostician; + }; + fnotation = wasmHaskellPackages.callPackage ./packages/fnotation { + inherit diagnostician; + }; + coln-compiler = wasmHaskellPackages.callPackage ./packages/coln-compiler { + inherit diagnostician fnotation; + }; + coln-compiler-wasm = wasmHaskellPackages.callPackage ./packages/coln-compiler-wasm { + inherit coln-compiler diagnostician diagnostician-html fnotation; + }; + }; lsTsDir = ./packages/coln-ls/client; lsClientNpmDeps = pkgs.importNpmLock { npmRoot = lsTsDir; @@ -221,8 +266,10 @@ forester fourmolu esbuild - haskell-wasm.wasm32-wasi-ghc-9_14 - haskell-wasm.wasm32-wasi-cabal-9_14 + wasmHaskellPackages.ghc + (pkgs.writeShellScriptBin "wasm32-unknown-wasi-cabal" '' + exec ${pkgs.cabal-install}/bin/cabal --with-compiler=${wasmHaskellPackages.ghc.unprefixed}/bin/ghc "$@" + '') haskell.compiler.ghc912 haskell.packages.ghc912.haskell-language-server haskellPackages.cabal-gild diff --git a/packages/coln-compiler-wasm/default.nix b/packages/coln-compiler-wasm/default.nix new file mode 100644 index 00000000..b53d8262 --- /dev/null +++ b/packages/coln-compiler-wasm/default.nix @@ -0,0 +1,17 @@ +{ mkDerivation, aeson, base, coln-compiler, containers +, diagnostician, diagnostician-html, fnotation, ghc-experimental +, lib, lucid, ordered-containers, prettyprinter, text +}: +mkDerivation { + pname = "coln-compiler-wasm"; + version = "0.1"; + src = ./.; + isLibrary = false; + isExecutable = true; + executableHaskellDepends = [ + aeson base coln-compiler containers diagnostician + diagnostician-html fnotation ghc-experimental lucid + ordered-containers prettyprinter text + ]; + license = "(Apache-2.0 OR MIT)"; +} diff --git a/packages/coln-compiler-wasm/justfile b/packages/coln-compiler-wasm/justfile index 81e581df..5a9b427c 100644 --- a/packages/coln-compiler-wasm/justfile +++ b/packages/coln-compiler-wasm/justfile @@ -4,8 +4,8 @@ build: #!/usr/bin/env bash set -euxo pipefail mkdir -p {{ dist_dir }} - wasm32-wasi-cabal build coln-compiler-wasm - cp "$(wasm32-wasi-cabal list-bin coln-compiler-wasm)" {{ dist_dir }}/coln.wasm - libdir="$(wasm32-wasi-ghc --print-libdir)" + wasm32-unknown-wasi-cabal build coln-compiler-wasm + cp "$(wasm32-unknown-wasi-cabal list-bin coln-compiler-wasm)" {{ dist_dir }}/coln.wasm + libdir="$(wasm32-unknown-wasi-ghc --print-libdir)" "$libdir/post-link.mjs" --input {{ dist_dir }}/coln.wasm --output {{ dist_dir }}/ghc_wasm_jsffi.js cp loadHaskellWasm.js {{ dist_dir }}/loadHaskellWasm.js