shish / rosettaboy

A gameboy emulator in several different languages
MIT License
469 stars 22 forks source link

Use Nix for reproducible environments? #57

Closed will-ca closed 1 year ago

will-ca commented 1 year ago

I got the below error trying to run the Zig version:

./build.zig:22:8: error: no member named 'addLibraryPath' in struct 'std.build.LibExeObjStep'
    exe.addLibraryPath("/Users/shish2k/homebrew/lib/");
       ^
./lib/sdl/Sdk.zig:139:63: error: expected type 'std.mem.Allocator', found 'std.zig.CrossTarget'
    const target = (std.zig.system.NativeTargetInfo.detect(exe.target) catch @panic("failed to detect native target info!")).target;
                                                              ^
/usr/lib/zig/std/mem/Allocator.zig:1:1: note: std.mem.Allocator declared here
//! The standard memory allocation interface.
^
/usr/lib/zig/std/zig/CrossTarget.zig:1:1: note: std.zig.CrossTarget declared here
//! Contains all the same data as `Target`, additionally introducing the concept of "the native target".
^
./lib/sdl/Sdk.zig:86:9: error: no member named 'source' in struct 'std.build.Pkg'
        .source = .{ .path = sdkPath("/src/wrapper/sdl.zig") },
        ^

Rather than endlessly trying to keep track of libraries, version incompatibilities, etc., I think Nix can be used to declaratively define reproducible, self-contained development environments as needed.

For example, I have the below shell.nix file for the Python version:

{ pkgs ? import <nixpkgs> {} } : pkgs.mkShell {
    buildInputs = with pkgs; [
        (python310.withPackages (pypkgs: with pypkgs; [
            pysdl2
            setuptools

            mypy
            black
        ])).out
    ];
}

(To read this if you haven't looked into Nix before: The language is functional. Colons are function definitions, with signature to the left and the return expression to the right. Calls are names followed by a space and then argument. With makes names from a mapping available in the subsequent scope. Lists are whitespace-separated, and curly braces are mappings. — The root expression is a function that takes the argument pkgs (or actually uses the default value import <nixpkgs> {}) and returns an environment, and the argument passed to .withPackages is a function that takes the argument pypkgs and returns a list of Python packages.)

And then, anyone on any distro of Linux or MacOS can just run $ nix-shell in the terminal to get the exact right development environment. The packages are stored entirely self-contained in /nix, and symlinked in ~/.nix-profile or added to $PATH as requested.

For perfect reproducibility, the packages used can be pinned to a specific version by downloading from a specific commit in the nixpkgs respository. So this way, if you specify a version of a library or language that works on your machine, anyone else running it will get the exact same version.

{ pkgs ? import (fetchTarball "https://github.com/NixOS/nixpkgs/archive/facfd565093c8f410be840dc427975a6d7368f91.tar.gz") {} } : pkgs.mkShell {
    buildInputs = […];
}

More info:

https://nixos.org/guides/declarative-and-reproducible-developer-environments.html

I'm not trying to evangelize. I bring this up as somebody who is reluctant to modify my global computing environment for a local requirement— I.E. I'm not going to change my system Zig, set up venvs everywhere, or install new pip packages just to run this, so I was just thinking it'd be useful if a declarative ad-hoc environment were provided.

I do see there's already something similar with Docker/Podman, which I guess fills the same purpose. But that seems heavier (pulling in a whole Debian, looks like?), and involves more permissions and system configuration to set up. — FWIW, Nix can also be used to generate Docker images, and it looks like there's a Nix image on Docker (though I haven't tried either).

Anyway, it's just an idea.

shish commented 1 year ago

Having nix as an alternative to docker for self-contained reproducible environments seems reasonable - I've no idea how that works, but pull requests would be welcome :)

(I expect we'd still want the Docker image for windows users to have a reproducible dev environment, as it seems nix doesn't work there?)

rrbutani commented 1 year ago

I started putting together a flake for this repo last month; it's since fallen out of date (and I only got as far as doing C++/Rust/Zig/Go) but if there's interest and if it looks like something that upstream would accept I'm happy to update it and add Nim/PHP/Python support.

shish commented 1 year ago

I have interest \o/

Especially if utils/shell.sh was updated to use nix if that's installed, else use docker if that's installed, else error -- then the docs can be "use this script to get a self-contained dev environment in whatever way is appropriate for your system"

shish commented 1 year ago

(There should probably also be a shell.bat for windows users, but that's an issue for another time...)

will-ca commented 1 year ago

Just FYI I've got a branch where I've set up a working shell.nix for all of the implementations except for Zig (which I think I did, but still need to test/finish), as well as a global shell.nix that imports and combines every language implementation together. Will finish off and then PR with details later.

Do all the languages work well on Windows?

shish commented 1 year ago

AFAIK all the languages should work on windows, but I've only personally used linux and macos

shish commented 1 year ago

closing this as nix is now my main development method for mac-based development

(though if somebody wants to put up a PR showing how flakes would work and why they're better, that would be interesting too)