Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 0 additions & 12 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 0 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@ authors = ["Albert Safin <[email protected]>"]
license = "Unlicense OR MIT"
edition = "2018"

[build-dependencies]
which = "4.4.0"

[dependencies]
blake3 = "1.3.3"
bytelines = "2.4.0"
Expand Down
42 changes: 27 additions & 15 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,44 @@ use std::path::Path;
use std::process::Command;

fn main() {
// Necessary env var to substitute in the final artifact depends on, regardless of the
// build context; either in nix-shell or a basic nix build.

// We need to support non-nix environment anyways, ecpesially for VSCode and rust-analyser that
// is being run by a VSCode extension and it doesn't have access to nix-shell to do a proper build.
if var("IN_NIX").is_err() || var("IN_NIX").unwrap() != "1" {
println!("cargo:rustc-env=CNS_ESSENTIALS=/nix/var/nix/profiles/default/bin:/usr/local/bin:/usr/bin:/usr/sbin:/bin:/sbin:/opt/homebrew/bin:/opt/homebrew/sbin");
println!("cargo:rustc-env=CNS_BASH=bash");
println!("cargo:rustc-env=CNS_NIX=");
} else {
println!(
"cargo:rustc-env=CNS_ESSENTIALS={}",
var("ESSENTIALS")
.expect("Expect to the ESSENTIALS env var to be set.")
);
println!(
"cargo:rustc-env=CNS_BASH={}",
var("BASH").expect("Expect to the BASH env var to be set.")
);
println!(
"cargo:rustc-env=CNS_NIX={}/",
var("NIX_BIN").expect("Expect to the NIX_BIN env var to be set.")
);
}

if var_os("CNS_IN_NIX_SHELL").is_none() {
// Release build triggered by nix-build. Use paths relative to $out.
let out = var("out").unwrap();
println!("cargo:rustc-env=CNS_TRACE_NIX_SO={out}/lib/trace-nix.so");
println!("cargo:rustc-env=CNS_VAR_EMPTY={out}/var/empty");
println!(
// This file is moved to /share/cached-nix-shell in the ./nix/Makefile#post-install
"cargo:rustc-env=CNS_RCFILE={out}/share/cached-nix-shell/rcfile.sh"
);
println!(
// This directory is created in the ./nix/Makefile#post-install
"cargo:rustc-env=CNS_WRAP_PATH={out}/libexec/cached-nix-shell"
);

// Use pinned nix and nix-shell binaries.
println!(
"cargo:rustc-env=CNS_NIX={}/",
which::which("nix")
.expect("command not found: nix")
.parent()
.unwrap()
.as_os_str()
.to_str()
.unwrap()
);
} else {
// Developer build triggered by `nix-shell --run 'cargo build'`.
// Use paths relative to the build directory. Additionally, place
Expand Down Expand Up @@ -59,8 +74,5 @@ fn main() {
)
.unwrap();
println!("cargo:rustc-env=CNS_WRAP_PATH={out_dir}/wrapper");

// Use nix and nix-shell from $PATH at runtime.
println!("cargo:rustc-env=CNS_NIX=");
}
}
50 changes: 37 additions & 13 deletions default.nix
Original file line number Diff line number Diff line change
@@ -1,18 +1,42 @@
{ pkgs ? import <nixpkgs> { }, nix ? pkgs.nix }:
let
sources = import ./nix/sources.nix;
minNixVer = "2.4";
in { pkgs ? import sources.nixpkgs { } }:

assert pkgs.lib.assertMsg
(builtins.compareVersions minNixVer pkgs.nix.version != 1)
"The project doesn't support Nix of version ${pkgs.nix.version}. The minimum required version is ${minNixVer}.";

let
naersk = pkgs.callPackage sources.naersk { };
gitignoreSource = (pkgs.callPackage sources.gitignore { }).gitignoreSource;
blake3-src = sources.BLAKE3;
in (naersk.buildPackage {
root = gitignoreSource ./.;
buildInputs = [ pkgs.openssl nix pkgs.ronn ];
}).overrideAttrs (attrs: {
CNS_GIT_COMMIT = if builtins.pathExists ./.git then
pkgs.lib.commitIdFromGitRepo ./.git
else
"next";
BLAKE3_CSRC = "${blake3-src}/c";
postBuild = "make -f nix/Makefile post-build";
postInstall = "make -f nix/Makefile post-install";
})

ESSENTIALS = with pkgs;
lib.makeBinPath [ bashInteractive coreutils nix gitMinimal gnutar gzip ];
BASH = "${pkgs.bashInteractive}/bin/bash";
NIX_BIN = "${pkgs.nix}/bin";
IN_NIX = true;

# The main cached-nix-shell package. It's subject for override (see below) of the
# underlying derivation attributes so the build works correctly.
package = naersk.buildPackage {
root = gitignoreSource ./.;
buildInputs = with pkgs; [ openssl nix ronn ];
};
# Final overrides.
package' = package.overrideAttrs (_: {
inherit ESSENTIALS BASH NIX_BIN IN_NIX;
CNS_GIT_COMMIT = if builtins.pathExists ./.git then
pkgs.lib.commitIdFromGitRepo ./.git
else
"next";
BLAKE3_CSRC = "${blake3-src}/c";
postBuild = ''
make -f nix/Makefile post-build
'';
postInstall = ''
make -f nix/Makefile post-install
'';
});
in package'
12 changes: 12 additions & 0 deletions nix/sources.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,17 @@
"type": "tarball",
"url": "https://github.com/nmattia/naersk/archive/88cd22380154a2c36799fe8098888f0f59861a15.tar.gz",
"url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
},
"nixpkgs": {
"branch": "nixpkgs-unstable",
"description": "Nix Packages collection & NixOS",
"homepage": "",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "63143ac2c9186be6d9da6035fa22620018c85932",
"sha256": "1qvr6c7sisnzvcnyidllsv0bz0xsjfw3cg5s090y3az6sgrrlss0",
"type": "tarball",
"url": "https://github.com/NixOS/nixpkgs/archive/63143ac2c9186be6d9da6035fa22620018c85932.tar.gz",
"url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
}
}
11 changes: 7 additions & 4 deletions shell.nix
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
{ pkgs ? import <nixpkgs> { } }:
let sources = import ./nix/sources.nix;
in { pkgs ? import sources.nixpkgs { } }:

with pkgs;

let main = import ./default.nix { inherit pkgs; };
in with pkgs;
mkShell {
in mkShell {
buildInputs = main.buildInputs ++ main.nativeBuildInputs ++ [
cargo-edit
clippy
Expand All @@ -15,7 +18,7 @@ mkShell {
git
openssh
];
inherit (main) BLAKE3_CSRC;
inherit (main) ESSENTIALS BASH NIX_BIN IN_NIX BLAKE3_CSRC;
CNS_IN_NIX_SHELL = "1";
RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}";
}
9 changes: 2 additions & 7 deletions src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@
//! compatible way, so it is appropriate to code this explicitly rather than use
//! such libraries.

use log::info;
use std::collections::VecDeque;
use std::ffi::{OsStr, OsString};
use std::io::Write;
use std::os::unix::ffi::OsStrExt;
use std::os::unix::process::CommandExt;
use std::process::{exit, Command};
use ufcs::Pipe;
use log::info;

pub enum RunMode {
/// no arg
Expand Down Expand Up @@ -97,7 +97,7 @@ impl Args {
packages_or_expr: false,
pure: false,
include_nix_path: Vec::new(),
interpreter: OsString::from("bash"),
interpreter: env!("CNS_BASH").into(),
run: RunMode::InteractiveShell,
keep: Vec::new(),
rest: Vec::new(),
Expand Down Expand Up @@ -204,11 +204,6 @@ fn exit_version() {
.map(|x| format!("-{x}"))
.unwrap_or("".into())
);
if env!("CNS_NIX").is_empty() {
info!("Using nix-shell from $PATH");
} else {
info!("Using {}nix-shell", env!("CNS_NIX"));
}
std::io::stdout().flush().unwrap();
Command::new(concat!(env!("CNS_NIX"), "nix-shell"))
.arg("--version")
Expand Down
89 changes: 17 additions & 72 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ use crate::args::Args;
use crate::bash::is_literal_bash_string;
use crate::path_clean::PathClean;
use crate::trace::Trace;
use itertools::{chain, Itertools};
use env_logger::{Builder, Env};
use itertools::chain;
use log::{debug, error, info, warn};
use nix::unistd::{access, AccessFlags};
use once_cell::sync::Lazy;
use std::collections::{BTreeMap, HashSet};
use std::collections::BTreeMap;
use std::env::current_dir;
use std::ffi::{OsStr, OsString};
use std::fs::{read, read_link, File};
Expand All @@ -19,8 +21,6 @@ use std::process::{exit, Command, Stdio};
use std::time::Instant;
use tempfile::NamedTempFile;
use ufcs::Pipe;
use log::{debug, error, warn, info};
use env_logger::{Builder, Env};

mod args;
mod bash;
Expand Down Expand Up @@ -109,50 +109,6 @@ struct NixShellOutput {
drv: String,
}

fn minimal_essential_path() -> OsString {
let required_binaries = ["tar", "gzip", "git", "nix-shell", "rm"];

fn which_dir(binary: &&str) -> Option<PathBuf> {
std::env::var_os("PATH")
.as_ref()
.unwrap()
.pipe(std::env::split_paths)
.find(|dir| {
if access(&dir.join(binary), AccessFlags::X_OK).is_err() {
return false;
}

if binary == &"nix-shell" {
// Ignore our fake nix-shell.
return !dir
.join(binary)
.canonicalize()
.ok()
.and_then(|x| x.file_name().map(|x| x.to_os_string()))
.map(|x| x == "cached-nix-shell")
.unwrap_or(true);
}

true
})
}

let required_paths = required_binaries
.iter()
.filter_map(which_dir)
.collect::<HashSet<PathBuf>>();

// We can't just join_paths(required_paths) -- we need to preserve order
std::env::var_os("PATH")
.as_ref()
.unwrap()
.pipe(std::env::split_paths)
.filter(|path_item| required_paths.contains(path_item))
.unique()
.pipe(std::env::join_paths)
.unwrap()
}

fn absolute_dirname(script_fname: &OsStr) -> PathBuf {
Path::new(&script_fname)
.parent()
Expand Down Expand Up @@ -209,14 +165,16 @@ fn args_to_inp(pwd: PathBuf, x: &Args) -> NixShellInput {
args.push(var.clone());
}
}
clean_env.insert(OsString::from("PATH"), minimal_essential_path());
clean_env.insert(OsString::from("PATH"), env!("CNS_ESSENTIALS").into());
clean_env
};

args.extend(x.other_kw.clone());
args.push(OsString::from("--"));
args.extend(x.rest.clone());

debug!("env: {:?}", &env);

NixShellInput {
pwd,
env,
Expand Down Expand Up @@ -287,30 +245,17 @@ fn run_nix_shell(inp: &NixShellInput) -> NixShellOutput {
std::mem::drop(trace_file);

let drv: String = {
// nix 2.3
let mut exec = Command::new(concat!(env!("CNS_NIX"), "nix"))
// nix 2.4
let exec = Command::new(concat!(env!("CNS_NIX"), "nix"))
.arg("show-derivation")
.arg("--extra-experimental-features")
.arg("nix-command")
.arg(env_out)
.output()
.expect("failed to execute nix show-derivation");
let mut stderr = exec.stderr.clone();
if !exec.status.success() {
// nix 2.4
exec = Command::new(concat!(env!("CNS_NIX"), "nix"))
.arg("show-derivation")
.arg("--extra-experimental-features")
.arg("nix-command")
.arg(env_out)
.output()
.expect("failed to execute nix show-derivation");
stderr.extend(b"\n");
stderr.extend(exec.stderr);
}
if !exec.status.success() {
error!(
"failed to execute nix show-derivation"
);
let _ = std::io::stderr().write_all(&stderr);
error!("failed to execute nix show-derivation");
let _ = std::io::stderr().write_all(&exec.stderr);
exit(1);
}

Expand Down Expand Up @@ -350,7 +295,7 @@ fn run_script(
exec_string.push("exec ");
exec_string.push(nix_shell_args.interpreter);
exec_string.push(r#" "$@""#);
Command::new("bash")
Command::new(env!("CNS_BASH"))
.arg("-c")
.arg(exec_string)
.arg("cached-nix-shell-bash") // corresponds to "$0" inside '-i'
Expand Down Expand Up @@ -428,12 +373,12 @@ fn run_from_args(args: Vec<OsString>) {
args::RunMode::InteractiveShell => {
let mut args = vec!["--rcfile".into(), env!("CNS_RCFILE").into()];
args.append(build_bash_options(&env).as_mut());
("bash".into(), args)
(env!("CNS_BASH").into(), args)
}
args::RunMode::Shell(cmd) => {
let mut args = build_bash_options(&env);
args.extend_from_slice(&["-c".into(), cmd]);
("bash".into(), args)
(env!("CNS_BASH").into(), args)
}
args::RunMode::Exec(cmd, cmd_args) => (cmd, cmd_args),
};
Expand Down Expand Up @@ -636,7 +581,7 @@ fn wrap(cmd: Vec<OsString>) {
.args(&cmd[1..])
.env("PATH", OsStr::from_bytes(&new_path))
.exec();
error!("couldn't run: {exec}");
error!("couldn't execute: {exec}");
exit(1);
}

Expand Down
Loading