diff --git a/.envrc b/.envrc new file mode 100644 index 00000000..3550a30f --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.gitignore b/.gitignore index 51f08051..06a6806c 100644 --- a/.gitignore +++ b/.gitignore @@ -14,7 +14,6 @@ target generated-docs generated-docs-* .direnv -.envrc *.rs.bk *.o *.so diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3d5df55d..9a568cd4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -7,6 +7,35 @@ For local work, use any recent `roc` on `PATH`, or download the latest archive for your operating system from the [`roc-lang/nightlies` releases](https://github.com/roc-lang/nightlies/releases/latest). +## Nix Development Environment + +With Nix's `nix-command` and `flakes` features enabled, the flake provides Roc +nightly, the Rust toolchain and cross-compilation standard libraries, Zig, +Python, and the documentation preview server on supported Linux and macOS +systems. Enter it with: + +```sh +nix develop +``` + +To load the shell automatically, install [`direnv`](https://direnv.net/) and +hook it into your shell. The checked-in `.envrc` uses direnv's `use flake`; +install [`nix-direnv`](https://github.com/nix-community/nix-direnv) as well if +your direnv version does not provide it. Then simply approve `.envrc` once: + +```sh +direnv allow +``` + +The lock file pins every flake input, including the Roc nightly supplied by +[`roc-overlay`](https://github.com/thebrandonlucas/roc-overlay). CI deliberately +tracks the current nightly, while local updates are explicit. Update the pinned +compiler with: + +```sh +nix flake update roc-overlay +``` + ## Code of Conduct We are committed to providing a friendly, safe, and welcoming environment for all. See the [Code of Conduct](https://github.com/roc-lang/roc/blob/main/CODE_OF_CONDUCT.md) for details. diff --git a/flake.lock b/flake.lock new file mode 100644 index 00000000..1cb17e8b --- /dev/null +++ b/flake.lock @@ -0,0 +1,89 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1784007870, + "narHash": "sha256-djcLt/JJphyNt4eDY9XTly+/WbCK5lqWq9lSgCmJkkQ=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "18b9261cb3294b6d2a06d03f96872827b8fe2698", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-x86-darwin": { + "locked": { + "lastModified": 1783945712, + "narHash": "sha256-ePHSbY7/7fNdOA2CE8J4T9Jt7xRrX87N2mlKd8EsYdM=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "293d01ba488ddb4c3560ba7c8de1fbec7047f78c", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-26.05-darwin", + "repo": "nixpkgs", + "type": "github" + } + }, + "roc-overlay": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ], + "nixpkgs-darwin": [ + "nixpkgs-x86-darwin" + ] + }, + "locked": { + "lastModified": 1784073071, + "narHash": "sha256-tmJuQY9Kr4yFTnA56pdTbyExRfd94NTj7jRBtpSozCc=", + "owner": "thebrandonlucas", + "repo": "roc-overlay", + "rev": "a9afdcfed9bf90c53e6b4b1443e00676a939e971", + "type": "github" + }, + "original": { + "owner": "thebrandonlucas", + "repo": "roc-overlay", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs", + "nixpkgs-x86-darwin": "nixpkgs-x86-darwin", + "roc-overlay": "roc-overlay", + "rust-overlay": "rust-overlay" + } + }, + "rust-overlay": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1784178955, + "narHash": "sha256-YtXHVFBzzqSidcEhPhfTcF2yzzu0p0yA7x07LOR+LaY=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "1785b85aeccca381caf8777133410f577b8f2f59", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 00000000..88f4b577 --- /dev/null +++ b/flake.nix @@ -0,0 +1,78 @@ +{ + description = "basic-cli development environment"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + # nixos-unstable no longer supports Intel macOS. + nixpkgs-x86-darwin.url = "github:NixOS/nixpkgs/nixpkgs-26.05-darwin"; + roc-overlay = { + url = "github:thebrandonlucas/roc-overlay"; + inputs.nixpkgs.follows = "nixpkgs"; + inputs.nixpkgs-darwin.follows = "nixpkgs-x86-darwin"; + }; + rust-overlay = { + url = "github:oxalica/rust-overlay"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = + { + nixpkgs, + nixpkgs-x86-darwin, + roc-overlay, + rust-overlay, + ... + }: + let + inherit (nixpkgs) lib; + + supportedSystems = [ + "x86_64-linux" + "aarch64-linux" + "x86_64-darwin" + "aarch64-darwin" + ]; + forAllSystems = lib.genAttrs supportedSystems; + rustToolchainConfig = (builtins.fromTOML (builtins.readFile ./rust-toolchain.toml)).toolchain; + rustTargets = [ + "x86_64-apple-darwin" + "aarch64-apple-darwin" + "x86_64-unknown-linux-musl" + "aarch64-unknown-linux-musl" + ]; + pkgsFor = + system: + import (if system == "x86_64-darwin" then nixpkgs-x86-darwin else nixpkgs) { + inherit system; + overlays = [ + roc-overlay.overlays.default + rust-overlay.overlays.default + ]; + }; + in + { + formatter = forAllSystems (system: (pkgsFor system).nixfmt); + + devShells = forAllSystems ( + system: + let + pkgs = pkgsFor system; + rustToolchain = pkgs.rust-bin.fromRustupToolchain ( + rustToolchainConfig // { targets = rustTargets; } + ); + in + { + default = pkgs.mkShell { + packages = [ + pkgs.rocpkgs.nightly + pkgs.python3 + rustToolchain + pkgs.simple-http-server + pkgs.zig_0_16 + ]; + }; + } + ); + }; +} diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 9e701eb6..c0e44280 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -6,6 +6,6 @@ # b) update `channel = "nightly-OLD_DATE"` below channel = "1.83.0" # check ^^^ when changing this -components = ["rust-analyzer"] +components = ["rust-analyzer", "llvm-tools-preview"] # # channel = "nightly-2024-04-28" # 1.79.0 nightly to be able to use unstable features diff --git a/scripts/build.py b/scripts/build.py index 890c0f42..542d5dca 100755 --- a/scripts/build.py +++ b/scripts/build.py @@ -117,7 +117,31 @@ def musl_build_env(rust_target: str) -> dict[str, str]: return env +def rust_target_is_installed(rust_target: str) -> bool: + result = subprocess.run( + ["rustc", "--print", "target-libdir", "--target", rust_target], + check=False, + stdout=subprocess.PIPE, + stderr=subprocess.DEVNULL, + text=True, + ) + return result.returncode == 0 and Path(result.stdout.strip()).is_dir() + + def install_rust_target(rust_target: str, *, required: bool = False) -> None: + if rust_target_is_installed(rust_target): + return + + if shutil.which("rustup") is None: + message = ( + f"Rust target {rust_target} is not installed and rustup is unavailable; " + "enter `nix develop` or install the target manually" + ) + if required: + raise SystemExit(message) + print(f"warning: {message}") + return + run("rustup", "target", "add", rust_target, check=required)