1
1
mirror of https://github.com/tweag/nickel.git synced 2024-10-04 07:07:10 +03:00
nickel/flake.nix

418 lines
14 KiB
Nix
Raw Normal View History

2020-07-03 15:22:21 +03:00
{
inputs = {
nixpkgs.url = "nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
pre-commit-hooks = {
url = "github:cachix/pre-commit-hooks.nix";
inputs.nixpkgs.follows = "nixpkgs";
inputs.flake-utils.follows = "flake-utils";
};
rust-overlay = {
url = "github:oxalica/rust-overlay";
inputs.nixpkgs.follows = "nixpkgs";
inputs.flake-utils.follows = "flake-utils";
};
crane = {
url = "github:ipetkov/crane";
inputs.nixpkgs.follows = "nixpkgs";
};
};
2020-07-03 15:22:21 +03:00
nixConfig = {
2022-08-01 16:51:45 +03:00
extra-substituters = [ "https://tweag-nickel.cachix.org" ];
extra-trusted-public-keys = [ "tweag-nickel.cachix.org-1:GIthuiK4LRgnW64ALYEoioVUQBWs0jexyoYVeLDBwRA=" ];
};
outputs =
{ self
, nixpkgs
, flake-utils
, pre-commit-hooks
, rust-overlay
, crane
}:
2020-07-03 15:22:21 +03:00
let
2021-01-15 16:23:16 +03:00
SYSTEMS = [
2021-09-13 22:20:54 +03:00
"aarch64-darwin"
"aarch64-linux"
2021-01-15 16:23:16 +03:00
"x86_64-darwin"
2021-09-13 22:20:54 +03:00
"x86_64-linux"
2021-01-15 16:23:16 +03:00
];
RUST_CHANNELS = [
"stable"
"beta"
];
forEachRustChannel = fn: builtins.listToAttrs (builtins.map fn RUST_CHANNELS);
2022-01-15 00:47:05 +03:00
cargoTOML = builtins.fromTOML (builtins.readFile ./Cargo.toml);
2022-01-17 01:17:41 +03:00
version = "${cargoTOML.package.version}_${builtins.substring 0 8 self.lastModifiedDate}_${self.shortRev or "dirty"}";
customOverlay = final: prev: {
# The version of `wasm-bindgen` CLI *must* be the same as the `wasm-bindgen` Rust dependency in `Cargo.toml`.
# The definition of `wasm-bindgen-cli` in Nixpkgs does not allow overriding directly the attrset passed to `buildRustPackage`.
# We instead override the attrset that `buildRustPackage` generates and passes to `mkDerivation`.
# See https://discourse.nixos.org/t/is-it-possible-to-override-cargosha256-in-buildrustpackage/4393
wasm-bindgen-cli = prev.wasm-bindgen-cli.overrideAttrs (oldAttrs:
let
wasmBindgenCargoVersion = cargoTOML.dependencies.wasm-bindgen.version;
# Remove the pinning `=` prefix of the version
wasmBindgenVersion = builtins.substring 1 (builtins.stringLength wasmBindgenCargoVersion) wasmBindgenCargoVersion;
in
rec {
pname = "wasm-bindgen-cli";
version = wasmBindgenVersion;
src = final.fetchCrate {
inherit pname version;
sha256 = "sha256-+PWxeRL5MkIfJtfN3/DjaDlqRgBgWZMa6dBt1Q+lpd0=";
};
cargoDeps = oldAttrs.cargoDeps.overrideAttrs (final.lib.const {
# This `inherit src` is important, otherwise, the old `src` would be used here
inherit src;
outputHash = "sha256-GwLeA6xLt7I+NzRaqjwVpt1pzRex1/snq30DPv4FR+g=";
});
2022-01-15 00:47:05 +03:00
});
};
in
flake-utils.lib.eachSystem SYSTEMS (system:
let
pkgs = import nixpkgs {
inherit system;
overlays = [
(import rust-overlay)
customOverlay
];
};
# Additional packages required to build Nickel on Darwin
missingSysPkgs =
if pkgs.stdenv.isDarwin then
[
pkgs.darwin.apple_sdk.frameworks.Security
pkgs.darwin.libiconv
]
else
[ ];
mkRust =
{ rustProfile ? "minimal"
, rustExtensions ? [
"rust-src"
"rust-analysis"
"rustfmt"
"clippy"
]
, channel ? "stable"
, target ? pkgs.rust.toRustTarget pkgs.stdenv.hostPlatform
}:
if channel == "nightly" then
pkgs.rust-bin.selectLatestNightlyWith
(toolchain: toolchain.${rustProfile}.override {
extensions = rustExtensions;
targets = [ target ];
})
else
pkgs.rust-bin.${channel}.latest.${rustProfile}.override {
extensions = rustExtensions;
targets = [ target ];
};
# A note on check_format: the way we invoke rustfmt here works locally but fails on CI.
# Since the formatting is checked on CI anyway - as part of the rustfmt check - we
# disable rustfmt in the pre-commit hook when running checks, but enable it when
# running in a dev shell.
pre-commit-builder = { rust ? mkRust { }, checkFormat ? false }: pre-commit-hooks.lib.${system}.run {
src = self;
hooks = {
nixpkgs-fmt = {
enable = true;
# Excluded because they are generated by Node2nix
excludes = [
"lsp/client-extension/default.nix"
"lsp/client-extension/node-env.nix"
"lsp/client-extension/node-packages.nix"
];
};
rustfmt = {
enable = checkFormat;
entry = pkgs.lib.mkForce "${rust}/bin/cargo-fmt fmt -- --check --color always";
};
markdownlint = {
enable = true;
excludes = [
"notes/(.+)\\.md$"
"^RELEASES\\.md$"
];
};
};
};
# Customize source filtering for Crane as Nickel uses non-standard-Rust
# files like `*.lalrpop`.
filterNickelSrc = filterCargoSources:
let
mkFilter = regexp: path: _type: builtins.match regexp path != null;
lalrpopFilter = mkFilter ".*lalrpop$";
nclFilter = mkFilter ".*ncl$";
txtFilter = mkFilter ".*txt$";
snapFilter = mkFilter ".*snap$";
in
pkgs.lib.cleanSourceWith {
src = pkgs.lib.cleanSource ./.;
# Combine our custom filters with the default one from Crane
# See https://github.com/ipetkov/crane/blob/master/docs/API.md#libfiltercargosources
filter = path: type:
builtins.any (filter: filter path type) [
lalrpopFilter
nclFilter
txtFilter
snapFilter
filterCargoSources
];
};
# Given a rust toolchain, provide Nickel's Rust dependencies, Nickel, as
# well as rust tools (like clippy)
mkCraneArtifacts = { rust ? mkRust { } }:
let
craneLib = crane.lib.${system}.overrideToolchain rust;
# Customize source filtering as Nickel uses non-standard-Rust files like `*.lalrpop`.
src = filterNickelSrc craneLib.filterCargoSources;
2023-01-14 18:03:32 +03:00
# set of cargo args common to all builds
cargoBuildExtraArgs = "--frozen --offline";
# Build *just* the cargo dependencies, so we can reuse all of that work (e.g. via cachix) when running in CI
cargoArtifacts = craneLib.buildDepsOnly {
2023-01-14 18:03:32 +03:00
inherit src;
cargoExtraArgs = "${cargoBuildExtraArgs} --workspace";
# pyo3 needs a Python interpreter in the build environment
# https://pyo3.rs/v0.17.3/building_and_distribution#configuring-the-python-version
2023-01-14 18:03:32 +03:00
buildInputs = [ pkgs.python3 ];
2022-01-17 01:17:41 +03:00
};
2021-08-31 12:05:36 +03:00
2023-01-14 18:03:32 +03:00
buildPackage = packageName:
craneLib.buildPackage {
inherit
src
cargoArtifacts;
cargoExtraArgs = "${cargoBuildExtraArgs} --package ${packageName}";
};
in
rec {
nickel = buildPackage "nickel-lang";
lsp-nls = buildPackage "nickel-lang-lsp";
rustfmt = craneLib.cargoFmt {
# Notice that unlike other Crane derivations, we do not pass `cargoArtifacts` to `cargoFmt`, because it does not need access to dependencies to format the code.
inherit src;
2022-01-17 01:17:41 +03:00
cargoExtraArgs = "--all";
2022-01-17 01:17:41 +03:00
# `-- --check` is automatically prepended by Crane
rustFmtExtraArgs = "--color always";
};
2022-01-17 01:17:41 +03:00
clippy = craneLib.cargoClippy {
inherit
src
cargoArtifacts;
2022-01-17 01:17:41 +03:00
2023-01-14 18:03:32 +03:00
cargoExtraArgs = cargoBuildExtraArgs;
cargoClippyExtraArgs = "--all-targets -- --deny warnings --allow clippy::new-without-default --allow clippy::match_like_matches_macro";
};
};
2021-08-31 12:05:36 +03:00
makeDevShell = { rust }: pkgs.mkShell {
# Trick found in Crane's examples to get a nice dev shell
# See https://github.com/ipetkov/crane/blob/master/examples/quick-start/flake.nix
inputsFrom = builtins.attrValues (mkCraneArtifacts { inherit rust; });
buildInputs = [
pkgs.rust-analyzer
pkgs.cargo-insta
pkgs.nixpkgs-fmt
pkgs.nodejs
pkgs.node2nix
pkgs.nodePackages.markdownlint-cli
2023-01-08 21:45:01 +03:00
pkgs.python3
];
shellHook = (pre-commit-builder { inherit rust; checkFormat = true; }).shellHook + ''
echo "=== Nickel development shell ==="
echo "Info: Git hooks can be installed using \`pre-commit install\`"
'';
RUST_SRC_PATH = "${rust}/lib/rustlib/src/rust/library";
};
# Profile is passed to `wasm-pack`, and is either "dev" (with debug
# symbols and no optimization), "release" (with optimization and without
# debug symbols) or "profiling". Right now only dev and release are used:
# - release for the production build
# - dev for checks, as the code isn't optimized, and WASM optimization
# takes time
buildNickelWasm =
{ rust ? mkRust { target = "wasm32-unknown-unknown"; }
, profile ? "release"
}:
let
# Build the various Crane artifacts (dependencies, packages, rustfmt, clippy) for a given Rust toolchain
craneLib = crane.lib.${system}.overrideToolchain rust;
2021-09-27 18:02:07 +03:00
# Customize source filtering as Nickel uses non-standard-Rust files like `*.lalrpop`.
src = filterNickelSrc craneLib.filterCargoSources;
2021-09-27 18:02:07 +03:00
cargoExtraArgs = "-p nickel-repl --target wasm32-unknown-unknown --frozen --offline";
2022-12-25 14:17:39 +03:00
# * --mode no-install prevents wasm-pack from trying to download and
# vendor tools like wasm-bindgen, wasm-opt, etc. but use the one
# provided by Nix
# * --no-default-features disable some default features of Nickel that
2022-12-25 14:17:39 +03:00
# aren't useful for the WASM REPL (and possibly incompatible with
# WASM build)
wasmPackExtraArgs = "--${profile} --mode no-install -- --no-default-features --frozen --offline";
# Build *just* the cargo dependencies, so we can reuse all of that work (e.g. via cachix) when running in CI
cargoArtifacts = craneLib.buildDepsOnly {
inherit
src
cargoExtraArgs;
doCheck = false;
};
in
craneLib.mkCargoDerivation {
inherit cargoArtifacts src;
buildPhaseCargoCommand = ''
wasm-pack build nickel-wasm-repl ${wasmPackExtraArgs}
'';
2021-12-10 14:07:47 +03:00
# nickel-lang.org expects an interface `nickel-repl.wasm`, hence the
# `ln`
installPhaseCommand = ''
mkdir -p $out
cp -r nickel-wasm-repl/pkg $out/nickel-repl
ln -s $out/nickel-repl/nickel_repl_bg.wasm $out/nickel-repl/nickel_repl.wasm
'';
nativeBuildInputs = [
rust
pkgs.wasm-pack
pkgs.wasm-bindgen-cli
pkgs.binaryen
] ++ missingSysPkgs;
};
2021-09-27 18:02:07 +03:00
buildDocker = nickel: pkgs.dockerTools.buildLayeredImage {
name = "nickel";
tag = version;
contents = [
nickel
pkgs.bashInteractive
];
config = {
Cmd = "bash";
};
};
2021-09-27 18:02:07 +03:00
# Build the Nickel VSCode extension. The extension seems to be required
# for the LSP to work.
vscodeExtension =
let node-package = (pkgs.callPackage ./lsp/client-extension { }).package;
in
(node-package.override rec {
pname = "nls-client";
outputs = [ "vsix" "out" ];
nativeBuildInputs = with pkgs; [
# `vsce` depends on `keytar`, which depends on `pkg-config` and `libsecret`
pkg-config
libsecret
];
postInstall = ''
npm run compile
mkdir -p $vsix
echo y | npx vsce package -o $vsix/${pname}.vsix
'';
}).vsix;
# Copy the markdown user manual to $out.
userManual = pkgs.stdenv.mkDerivation {
name = "nickel-user-manual-${version}";
src = ./doc/manual;
installPhase = ''
mkdir -p $out
2022-06-21 20:32:33 +03:00
cp -r ./ $out
'';
};
# Generate the stdlib documentation from `nickel doc`.
2022-06-21 20:32:33 +03:00
stdlibDoc = pkgs.stdenv.mkDerivation {
name = "nickel-stdlib-doc-${version}";
src = ./stdlib;
installPhase = ''
mkdir -p $out
for file in *
do
module=$(basename $file .ncl)
${self.packages."${system}".default}/bin/nickel doc -f "$module.ncl" \
2022-06-21 20:32:33 +03:00
--output "$out/$module.md"
done
'';
};
in
rec {
packages = {
nickel = (mkCraneArtifacts { }).nickel;
default = packages.nickel;
nickelWasm = buildNickelWasm { };
dockerImage = buildDocker packages.nickel; # TODO: docker image should be a passthru
inherit vscodeExtension;
inherit userManual;
2022-06-21 20:32:33 +03:00
inherit stdlibDoc;
};
2021-09-27 18:02:07 +03:00
2022-12-23 12:57:27 +03:00
apps = {
default = {
type = "app";
program = "${packages.nickel}/bin/nickel";
};
};
devShells = (forEachRustChannel (channel: {
name = channel;
value = makeDevShell { rust = mkRust { inherit channel; rustProfile = "default"; }; };
})) // {
default = devShells.stable;
};
checks = {
inherit (mkCraneArtifacts { })
nickel
2023-01-14 18:03:32 +03:00
lsp-nls
clippy
rustfmt;
# An optimizing release build is long: eschew optimizations in checks by
# building a dev profile
nickelWasm = buildNickelWasm { profile = "dev"; };
2022-11-21 14:56:42 +03:00
inherit vscodeExtension;
pre-commit = pre-commit-builder { };
};
}
2022-01-17 01:17:41 +03:00
);
2020-07-03 15:22:21 +03:00
}