diff --git a/flake.nix b/flake.nix index f4918c9..aff7689 100644 --- a/flake.nix +++ b/flake.nix @@ -34,6 +34,10 @@ path = ./direnv; description = "An empty devshell with direnv support"; }; + rust = { + path = ./rust; + description = "Rust specific template providing packaging, dev-shell & cross-compilation; based on fenix."; + }; }; formatter = forAllSystems (pkgs: pkgs.nixfmt-rfc-style); }; diff --git a/rust/.editorconfig b/rust/.editorconfig new file mode 100644 index 0000000..a66cb72 --- /dev/null +++ b/rust/.editorconfig @@ -0,0 +1,38 @@ +root = true + +[*] +indent_style = tab +indent_size = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.nix] +indent_style = space +indent_size = 2 +insert_final_newline = false + +[*.rs,Cargo.toml] +indent_size = 4 +insert_final_newline = false + +[*.md] +indent_size = 2 +trim_trailing_whitespace = false +insert_final_newline = false + +[*.{json,json5,yaml,yml,webmanifest}] +indent_style = space +indent_size = 2 + +[*.toml] +indent_style = unset +indent_size = 0 + +[*.lock] +indent_style = unset +insert_final_newline = unset + +[Makefile] +indent_style = tab diff --git a/rust/.gitignore b/rust/.gitignore new file mode 100644 index 0000000..72e8fc2 --- /dev/null +++ b/rust/.gitignore @@ -0,0 +1,2 @@ +result +target/ diff --git a/rust/Cargo.lock b/rust/Cargo.lock new file mode 100644 index 0000000..da7b39c --- /dev/null +++ b/rust/Cargo.lock @@ -0,0 +1,6 @@ +# placeholder for the real Cargo.lock, +# so that evaluating the flake does not fail due to missing files, +# before using `cargo init` to create the 'real' files +version = 3 + +[[package]] diff --git a/rust/Cargo.toml b/rust/Cargo.toml new file mode 100644 index 0000000..5432b53 --- /dev/null +++ b/rust/Cargo.toml @@ -0,0 +1,9 @@ +# placeholder for the real Cargo.toml, +# so that evaluating the flake does not fail due to missing files, +# before using `cargo init` to create the 'real' files +[package] +name = "bin" +version = "0.1.0" + +[profile.release] +overflow-checks = true diff --git a/rust/default.nix b/rust/default.nix new file mode 100644 index 0000000..6466507 --- /dev/null +++ b/rust/default.nix @@ -0,0 +1,9 @@ +(import ( + let + lock = builtins.fromJSON (builtins.readFile ./flake.lock); + in + fetchTarball { + url = "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz"; + sha256 = lock.nodes.flake-compat.locked.narHash; + } +) { src = ./.; }).defaultNix diff --git a/rust/flake.nix b/rust/flake.nix new file mode 100644 index 0000000..a133586 --- /dev/null +++ b/rust/flake.nix @@ -0,0 +1,164 @@ +{ + inputs = { + flake-compat = { + url = "github:edolstra/flake-compat"; + flake = false; + }; + + nixpkgs.url = "github:NixOs/nixpkgs/nixos-unstable"; + + fenix = { + url = "github:nix-community/fenix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = + { + self, + nixpkgs, + fenix, + ... + }@inputs: + let + cargoMeta = builtins.fromTOML (builtins.readFile ./Cargo.toml); + packageName = cargoMeta.package.name; + + forSystems = + function: + nixpkgs.lib.genAttrs [ "x86_64-linux" ] ( + system: + let + pkgs = import nixpkgs { + inherit system; + + overlays = [ (final: prev: { ${packageName} = self.packages.${system}.${packageName}; }) ]; + }; + + fenixPkgs = fenix.packages.${pkgs.system}; + fenixChannel = fenixPkgs.toolchainOf { + channel = "nightly"; + date = + builtins.replaceStrings [ "nightly-" ] [ "" ] + (builtins.fromTOML (builtins.readFile ./rust-toolchain.toml)).toolchain.channel; + sha256 = "sha256-SzEeSoO54GiBQ2kfANPhWrt0EDRxqEvhIbTt2uJt/TQ="; + }; + + toolchainFor = + pkgs: + with fenixPkgs; + combine [ + minimal.cargo + minimal.rustc + targets.${pkgs.rust.lib.toRustTarget pkgs.stdenv.targetPlatform}.latest.rust-std + ]; + + rustPlatformFor = + pkgs: + let + toolchain = toolchainFor pkgs; + in + pkgs.makeRustPlatform { + cargo = toolchain; + rustc = toolchain; + }; + + crossPackageFor = + pkgs: + let + rustPlatform = rustPlatformFor pkgs; + in + { + "${packageName}-cross-${pkgs.stdenv.hostPlatform.config}${ + if pkgs.stdenv.hostPlatform.isStatic then "-static" else "" + }" = pkgs.callPackage (./. + "/nix/packages/${packageName}.nix") { + inherit cargoMeta rustPlatform; + flake-self = self; + }; + }; + in + function { + inherit + system + pkgs + fenixPkgs + fenixChannel + toolchainFor + rustPlatformFor + crossPackageFor + ; + } + ); + in + { + formatter = forSystems ({ pkgs, ... }: pkgs.alejandra); + + checks = forSystems ( + { pkgs, fenixChannel, ... }: + { + rustfmt = + pkgs.runCommand "check-rustfmt" + { + nativeBuildInputs = + let + fenixRustToolchain = fenixChannel.withComponents [ + "cargo" + "rustfmt-preview" + ]; + in + [ fenixRustToolchain ]; + } + '' + cd ${./.} + cargo fmt -- --check + touch $out + ''; + } + ); + + packages = forSystems ( + { + pkgs, + fenixChannel, + system, + crossPackageFor, + ... + }: + { + ${packageName} = pkgs.callPackage (./. + "/nix/packages/${packageName}.nix") { + inherit cargoMeta; + flake-self = self; + rustPlatform = pkgs.makeRustPlatform { + cargo = fenixChannel.toolchain; + rustc = fenixChannel.toolchain; + }; + }; + default = self.packages.${system}.${packageName}; + } + // crossPackageFor pkgs.pkgsCross.musl64.pkgsStatic + // crossPackageFor pkgs.pkgsCross.musl32.pkgsStatic + // crossPackageFor pkgs.pkgsCross.aarch64-multiplatform-musl.pkgsStatic + // crossPackageFor pkgs.pkgsCross.armv7l-hf-multiplatform.pkgsStatic + // crossPackageFor pkgs.pkgsCross.mingwW64.pkgsStatic + ); + + devShells = forSystems ( + { pkgs, fenixChannel, ... }: + let + fenixRustToolchain = fenixChannel.withComponents [ + "cargo" + "clippy-preview" + "rust-src" + "rustc" + "rustfmt-preview" + ]; + in + { + default = pkgs.callPackage (./. + "/nix/dev-shells/${packageName}.nix") { + inherit fenixRustToolchain cargoMeta; + }; + ci = pkgs.callPackage (./nix/dev-shells/ci.nix) { inherit fenixRustToolchain cargoMeta; }; + } + ); + }; +} diff --git a/rust/justfile b/rust/justfile new file mode 100644 index 0000000..923ca1d --- /dev/null +++ b/rust/justfile @@ -0,0 +1,45 @@ +# move bin.nix files under nix/ +# to the correct place +# for the new package name $packageName +# (i.e. $packageName.nix) +# delete this after running it +setup packageName: + mv nix/dev-shells/bin.nix "nix/dev-shells/{{ packageName }}.nix" + mv nix/packages/bin.nix "nix/packages/{{ packageName }}.nix" + rm Cargo.lock Cargo.toml + # pass --vcs none to disable addition to .gitignore + # (which would add the Cargo.lock to it) + cargo init --vcs none --lib --edition 2021 --name "{{ packageName }}" + # generate inital lockfile + cargo build + # make sure nix sees new *.nix files + git add . + +build: + cargo build + +build-release: + cargo build --release + +run: + cargo run + +run-release: + cargo run --release + +format: + cargo fmt --check + eclint -exclude "{Cargo.lock,flake.lock}" + +format-fix: + cargo fmt + eclint -exclude "{Cargo.lock,flake.lock}" -fix + +lint: + cargo clippy + +lint-fix: + cargo clippy --fix + +reuse: + reuse lint diff --git a/rust/nix/dev-shells/bin.nix b/rust/nix/dev-shells/bin.nix new file mode 100644 index 0000000..0a49188 --- /dev/null +++ b/rust/nix/dev-shells/bin.nix @@ -0,0 +1,34 @@ +{ + cargoMeta, + pkgs, + mkShell, + fenixRustToolchain, + bashInteractive, + cargo-edit, + reuse, + just, + eclint, +}: + +mkShell { + + inputsFrom = [ pkgs.${cargoMeta.package.name} ]; + + packages = [ + fenixRustToolchain + + bashInteractive + + # for upgrading dependencies (i.e. versions in Cargo.toml) + cargo-edit + + reuse + just + eclint + ]; + + shellHook = '' + unset SOURCE_DATE_EPOCH + just --list --list-heading $'just :\n' + ''; +} diff --git a/rust/nix/dev-shells/ci.nix b/rust/nix/dev-shells/ci.nix new file mode 100644 index 0000000..3b1dd03 --- /dev/null +++ b/rust/nix/dev-shells/ci.nix @@ -0,0 +1,37 @@ +{ + cargoMeta, + pkgs, + mkShell, + fenixRustToolchain, + bashInteractive, + reuse, + just, + eclint, + commitlint, +}: + +mkShell { + + inputsFrom = [ pkgs.${cargoMeta.package.name} ]; + + packages = [ + fenixRustToolchain + + bashInteractive + + reuse + just + eclint + # nix develop ".#ci" --command -- + # eclint + # -exclude "Cargo.lock" + # -exclude "flake.lock" + + commitlint + # nix develop ".#ci" --command -- + # commitlint + # --color false --verbose + # --from $(git rev-list --max-parents=0 HEAD | head -n 1) + # --to HEAD + ]; +} diff --git a/rust/nix/packages/bin.nix b/rust/nix/packages/bin.nix new file mode 100644 index 0000000..84289f3 --- /dev/null +++ b/rust/nix/packages/bin.nix @@ -0,0 +1,36 @@ +{ + lib, + flake-self, + cargoMeta, + rustPlatform, +}: +let + fs = lib.fileset; + sourceFiles = fs.unions [ + (fs.maybeMissing ../../src) + (fs.maybeMissing ../../Cargo.toml) + (fs.maybeMissing ../../Cargo.lock) + ]; +in +rustPlatform.buildRustPackage { + inherit (cargoMeta.package) version; + pname = cargoMeta.package.name; + + src = fs.toSource { + root = ../../.; + fileset = sourceFiles; + }; + + cargoLock.lockFile = ../../Cargo.lock; + + VERGEN_IDEMPOTENT = "1"; + VERGEN_GIT_SHA = + if flake-self ? "rev" then + flake-self.rev + else if flake-self ? "dirtyRev" then + flake-self.dirtyRev + else + lib.warn "no git rev available" "NO_GIT_REPO"; + VERGEN_GIT_BRANCH = if flake-self ? "ref" then flake-self.ref else ""; + VERGEN_GIT_COMMIT_TIMESTAMP = flake-self.lastModifiedDate; +} diff --git a/rust/rust-toolchain.toml b/rust/rust-toolchain.toml new file mode 100644 index 0000000..3d80fca --- /dev/null +++ b/rust/rust-toolchain.toml @@ -0,0 +1,4 @@ +[toolchain] +channel = "nightly-2023-12-21" +components = ["rust-analyzer", "rust-src"] +profile = "default" diff --git a/rust/rustfmt.toml b/rust/rustfmt.toml new file mode 100644 index 0000000..76dcf32 --- /dev/null +++ b/rust/rustfmt.toml @@ -0,0 +1,27 @@ +edition = "2021" +version = "Two" + +newline_style = "Unix" +format_generated_files = false + +hard_tabs = true +tab_spaces = 4 + +max_width = 80 +doc_comment_code_block_width = 80 +comment_width = 80 +error_on_line_overflow = true +error_on_unformatted = true + +normalize_comments = true +normalize_doc_attributes = true +use_try_shorthand = true + +imports_granularity = "Crate" +group_imports = "StdExternalCrate" + +reorder_impl_items = true + +match_block_trailing_comma = true + +use_field_init_shorthand = true diff --git a/rust/shell.nix b/rust/shell.nix new file mode 100644 index 0000000..493783d --- /dev/null +++ b/rust/shell.nix @@ -0,0 +1,9 @@ +(import ( + let + lock = builtins.fromJSON (builtins.readFile ./flake.lock); + in + fetchTarball { + url = "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz"; + sha256 = lock.nodes.flake-compat.locked.narHash; + } +) { src = ./.; }).shellNix