diff --git a/Cargo.lock b/Cargo.lock index 51e8e6eac..c7aac58d6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1310,6 +1310,7 @@ dependencies = [ "stacker", "strum", "tar", + "tempfile", "termcolor", "thiserror 2.0.18", "time", diff --git a/compiler-core/Cargo.toml b/compiler-core/Cargo.toml index 5a121450a..c85ba87aa 100644 --- a/compiler-core/Cargo.toml +++ b/compiler-core/Cargo.toml @@ -53,6 +53,7 @@ wit-parser = "0.239.0" wit-component = { version = "0.239.0", features = ["dummy-module"] } wat = "1" anyhow = "1" +tempfile = "3" num-bigint.workspace = true num-traits.workspace = true diff --git a/compiler-core/src/wasm/zed.rs b/compiler-core/src/wasm/zed.rs index d7f77e804..b6cd36980 100644 --- a/compiler-core/src/wasm/zed.rs +++ b/compiler-core/src/wasm/zed.rs @@ -40,16 +40,85 @@ pub fn compile_extension_component( package_component(core, &resolve, world) } +/// Vendored `zed:extension` 0.7.0 WIT (embedded so installed binaries work +/// without a source tree; `CARGO_MANIFEST_DIR` alone breaks under Nix). +fn zed_wit_files() -> &'static [(&'static str, &'static str)] { + &[ + ( + "common.wit", + include_str!("../../wit/zed_extension/0.7.0/common.wit"), + ), + ( + "context-server.wit", + include_str!("../../wit/zed_extension/0.7.0/context-server.wit"), + ), + ( + "dap.wit", + include_str!("../../wit/zed_extension/0.7.0/dap.wit"), + ), + ( + "extension.wit", + include_str!("../../wit/zed_extension/0.7.0/extension.wit"), + ), + ( + "github.wit", + include_str!("../../wit/zed_extension/0.7.0/github.wit"), + ), + ( + "http-client.wit", + include_str!("../../wit/zed_extension/0.7.0/http-client.wit"), + ), + ( + "lsp.wit", + include_str!("../../wit/zed_extension/0.7.0/lsp.wit"), + ), + ( + "nodejs.wit", + include_str!("../../wit/zed_extension/0.7.0/nodejs.wit"), + ), + ( + "platform.wit", + include_str!("../../wit/zed_extension/0.7.0/platform.wit"), + ), + ( + "process.wit", + include_str!("../../wit/zed_extension/0.7.0/process.wit"), + ), + ( + "slash-command.wit", + include_str!("../../wit/zed_extension/0.7.0/slash-command.wit"), + ), + ] +} + fn load_zed_world() -> Result<(Resolve, WorldId)> { let mut resolve = Resolve::default(); - // Vendored next to this source tree. - let wit_dir = concat!(env!("CARGO_MANIFEST_DIR"), "/wit/zed_extension/0.7.0"); + + // Optional override for packaging / tests. + if let Ok(dir) = std::env::var("GLEAM_ZED_WIT") { + let (pkg, _) = resolve + .push_dir(&dir) + .with_context(|| format!("failed to parse WIT at GLEAM_ZED_WIT={dir}"))?; + let world = resolve + .select_world(&[pkg], Some("extension")) + .context("zed:extension world not found")?; + return Ok((resolve, world)); + } + + // Materialise embedded WIT into a temp dir for wit-parser's push_dir. + let tmp = tempfile::tempdir().context("temp dir for embedded zed WIT")?; + for (name, contents) in zed_wit_files() { + std::fs::write(tmp.path().join(name), contents) + .with_context(|| format!("write embedded wit {name}"))?; + } let (pkg, _) = resolve - .push_dir(wit_dir) - .with_context(|| format!("failed to parse vendored WIT at {wit_dir}"))?; + .push_dir(tmp.path()) + .with_context(|| format!("failed to parse embedded zed WIT at {}", tmp.path().display()))?; let world = resolve .select_world(&[pkg], Some("extension")) .context("zed:extension world not found")?; + // Keep tmp alive until resolve is done parsing — package is fully loaded. + std::mem::forget(tmp); Ok((resolve, world)) } diff --git a/flake.nix b/flake.nix index 7290d4911..8e7f51579 100644 --- a/flake.nix +++ b/flake.nix @@ -1,27 +1,123 @@ { + description = "Gleam compiler (wasm / Zed extension support fork)"; + inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; + systems.url = "github:nix-systems/default"; fenix.url = "github:nix-community/fenix"; fenix.inputs.nixpkgs.follows = "nixpkgs"; }; outputs = - { nixpkgs, fenix, ... }: + { + self, + nixpkgs, + systems, + fenix, + ... + }: let - forAllSystems = - function: - nixpkgs.lib.genAttrs [ - "aarch64-darwin" - ] (system: function nixpkgs.legacyPackages.${system}); + forEachSystem = nixpkgs.lib.genAttrs (import systems); in { - devShells = forAllSystems (pkgs: { - default = pkgs.mkShell { - packages = [ - fenix.packages.${pkgs.system}.stable.toolchain - pkgs.capnproto - ]; + packages = forEachSystem ( + system: + let + pkgs = nixpkgs.legacyPackages.${system}; + inherit (pkgs) lib; + + # Recent stable for edition 2024 workspace. + rustToolchain = + fenix.packages.${system}.stable.toolchain + or fenix.packages.${system}.complete.toolchain; + + rustPlatform = pkgs.makeRustPlatform { + cargo = rustToolchain; + rustc = rustToolchain; + }; + + gleam = rustPlatform.buildRustPackage { + pname = "gleam"; + version = "1.13.0-rc1"; + src = lib.cleanSourceWith { + src = ./.; + filter = + path: type: + let + base = baseNameOf path; + in + lib.cleanSourceFilter path type + && base != "target" + && base != "result" + && !(lib.hasSuffix ".wasm" base) + && !(lib.hasSuffix ".cwasm" base); + }; + + cargoLock = { + lockFile = ./Cargo.lock; + }; + + nativeBuildInputs = [ + pkgs.capnproto + pkgs.pkg-config + ]; + + # Workspace: only build/install the CLI crate. + buildAndTestSubdir = "gleam-bin"; + + # Full test suite is heavy and needs network / many backends. + doCheck = false; + + meta = { + description = "Gleam compiler with Wasm / Zed extension export"; + mainProgram = "gleam"; + homepage = "https://tangled.org/nandi.uk/gleam"; + license = lib.licenses.asl20; + }; + }; + in + { + default = gleam; + inherit gleam; + } + ); + + apps = forEachSystem (system: { + default = { + type = "app"; + program = "${self.packages.${system}.gleam}/bin/gleam"; }; }); + + devShells = forEachSystem ( + system: + let + pkgs = nixpkgs.legacyPackages.${system}; + rustToolchain = + fenix.packages.${system}.stable.toolchain + or fenix.packages.${system}.complete.toolchain; + in + { + default = pkgs.mkShell { + packages = [ + rustToolchain + pkgs.capnproto + pkgs.pkg-config + self.packages.${system}.gleam + ]; + shellHook = '' + echo "gleam wasm fork dev shell" + echo " gleam --version # store build when packages.gleam is built" + echo " cargo build -p gleam" + ''; + }; + } + ); + + checks = forEachSystem (system: { + package = self.packages.${system}.gleam; + }); + + formatter = forEachSystem (system: nixpkgs.legacyPackages.${system}.nixfmt-rfc-style); }; }