Skip to content

Commit 6cfd1b4

Browse files
authored
Add support for installing on NixOS (rust-lang#1298)
* Also added dockerfiles for AL2, Ubuntu 22.04. Not in CI yet.
1 parent 698c06b commit 6cfd1b4

8 files changed

+168
-4
lines changed

Diff for: .github/workflows/kani.yml

+4-2
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,10 @@ jobs:
117117
- name: Build container test
118118
if: ${{ matrix.os == 'ubuntu-18.04' }}
119119
run: |
120-
docker build -t kani-20-04 -f scripts/ci/Dockerfile.bundle-test-20-04 .
121-
docker build -t kani-18-04 -f scripts/ci/Dockerfile.bundle-test-18-04 .
120+
docker build -t kani-20-04 -f scripts/ci/Dockerfile.bundle-test-ubuntu-20-04 .
121+
docker build -t kani-18-04 -f scripts/ci/Dockerfile.bundle-test-ubuntu-18-04 .
122+
# this one does its tests automatically, for reasons documented in the file:
123+
docker build -t kani-nixos -f scripts/ci/Dockerfile.bundle-test-nixos .
122124
123125
- name: Run installed tests
124126
if: ${{ matrix.os == 'ubuntu-18.04' }}

Diff for: scripts/ci/Dockerfile.bundle-test-al2

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Copyright Kani Contributors
2+
# SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
# Note: this file is intended only for testing the kani release bundle
5+
6+
FROM amazonlinux:2
7+
RUN yum install -y gcc python3 python3-pip curl ctags tar gzip && \
8+
curl -sSf https://sh.rustup.rs | sh -s -- -y
9+
ENV PATH="/root/.cargo/bin:${PATH}"
10+
11+
WORKDIR /tmp/kani
12+
COPY ./tests ./tests
13+
COPY ./kani-latest-x86_64-unknown-linux-gnu.tar.gz ./
14+
# Very awkward glob (not regex!) to get `kani-verifier-*` and not `kani-verifier-*.crate`
15+
COPY ./target/package/kani-verifier-*[^e] ./kani-verifier
16+
#RUN cargo install --path ./kani-verifier
17+
#RUN cargo-kani setup --use-local-bundle ./kani-latest-x86_64-unknown-linux-gnu.tar.gz
18+
RUN cargo install kani-verifier
19+
RUN cargo-kani setup

Diff for: scripts/ci/Dockerfile.bundle-test-nixos

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Copyright Kani Contributors
2+
# SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
# Note: this file is intended only for testing the kani release bundle
5+
6+
FROM nixos/nix
7+
RUN nix-channel --update
8+
WORKDIR /tmp/kani
9+
RUN echo $' \n\
10+
with import <nixpkgs> {}; \n\
11+
mkShell { \n\
12+
packages = [ \n\
13+
ctags \n\
14+
curl \n\
15+
gcc \n\
16+
patchelf \n\
17+
python310 \n\
18+
python310Packages.pip \n\
19+
rustup \n\
20+
]; \n\
21+
}' >> ./default.nix
22+
# we need to switch to nix-shell to get proper support for e.g. pip
23+
SHELL ["nix-shell", "--command"]
24+
ENTRYPOINT ["nix-shell"]
25+
RUN rustup toolchain add stable
26+
ENV PATH="/root/.cargo/bin:${PATH}"
27+
28+
WORKDIR /tmp/kani
29+
COPY ./tests ./tests
30+
COPY ./kani-latest-x86_64-unknown-linux-gnu.tar.gz ./
31+
# Very awkward glob (not regex!) to get `kani-verifier-*` and not `kani-verifier-*.crate`
32+
COPY ./target/package/kani-verifier-*[^e] ./kani-verifier
33+
RUN cargo install --path ./kani-verifier
34+
RUN cargo-kani setup --use-local-bundle ./kani-latest-x86_64-unknown-linux-gnu.tar.gz
35+
36+
# Temporary hack: nix-shell causes problems when trying to run these with 'docker run'
37+
# like we do for other tests, so we've imported these into the dockerfile for now
38+
# until everything can be replaced with 'self-test':
39+
# https://github.com/model-checking/kani/issues/1246
40+
RUN cargo kani --version
41+
RUN (cd /tmp/kani/tests/cargo-kani/simple-lib && cargo kani)
42+
RUN (cd /tmp/kani/tests/cargo-kani/simple-visualize && cargo kani)
43+
RUN (cd /tmp/kani/tests/cargo-kani/build-rs-works && cargo kani)
44+
RUN cargo-kani setup --use-local-bundle ./kani-latest-x86_64-unknown-linux-gnu.tar.gz

Diff for: scripts/ci/Dockerfile.bundle-test-ubuntu-22-04

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Copyright Kani Contributors
2+
# SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
# Note: this file is intended only for testing the kani release bundle
5+
6+
FROM ubuntu:22.04
7+
ENV DEBIAN_FRONTEND=noninteractive \
8+
DEBCONF_NONINTERACTIVE_SEEN=true
9+
RUN apt-get update && \
10+
apt-get install -y python3 python3-pip curl universal-ctags && \
11+
curl -sSf https://sh.rustup.rs | sh -s -- -y
12+
ENV PATH="/root/.cargo/bin:${PATH}"
13+
14+
WORKDIR /tmp/kani
15+
COPY ./tests ./tests
16+
COPY ./kani-latest-x86_64-unknown-linux-gnu.tar.gz ./
17+
# Very awkward glob (not regex!) to get `kani-verifier-*` and not `kani-verifier-*.crate`
18+
COPY ./target/package/kani-verifier-*[^e] ./kani-verifier
19+
RUN cargo install --path ./kani-verifier
20+
RUN cargo-kani setup --use-local-bundle ./kani-latest-x86_64-unknown-linux-gnu.tar.gz

Diff for: src/os_hacks.rs

+77-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ use std::ffi::OsString;
99
use std::path::Path;
1010
use std::process::Command;
1111

12-
use anyhow::Result;
12+
use anyhow::{bail, Context, Result};
13+
use os_info::Info;
1314

1415
use crate::cmd::AutoRun;
1516

@@ -45,3 +46,78 @@ pub fn setup_python_deps_on_ubuntu_18_04(pyroot: &Path, pkg_versions: &[&str]) -
4546

4647
Ok(())
4748
}
49+
50+
/// This is the final step of setup, where we look for OSes that require additional setup steps
51+
/// beyond the usual ones that we have done already.
52+
pub fn setup_os_hacks(kani_dir: &Path, os: &Info) -> Result<()> {
53+
match os.os_type() {
54+
os_info::Type::NixOS => setup_nixos_patchelf(kani_dir),
55+
os_info::Type::Linux => {
56+
// NixOs containers are detected as Unknown Linux, so use a fallback hack:
57+
if std::env::var_os("NIX_CC").is_some() && Path::new("/etc/nix").exists() {
58+
return setup_nixos_patchelf(kani_dir);
59+
}
60+
Ok(())
61+
}
62+
_ => Ok(()),
63+
}
64+
}
65+
66+
/// On NixOS, the dynamic linker does not live at the standard path, and so our downloaded
67+
/// pre-built binaries need patching.
68+
/// In addition, the C++ standard library (needed by the CBMC binaries we ship) also does not
69+
/// have a standard path, and so we need to inject an rpath into those binaries to get them
70+
/// to successfully link at runtime.
71+
fn setup_nixos_patchelf(kani_dir: &Path) -> Result<()> {
72+
// Encode our assumption that we're working on x86 here, because when we add ARM
73+
// support, we need to look for a different path.
74+
assert!(env!("TARGET") == "x86_64-unknown-linux-gnu");
75+
if Path::new("/lib64/ld-linux-x86-64.so.2").exists() {
76+
// if the expected path exists, I guess things are fine?
77+
return Ok(());
78+
}
79+
80+
println!("[NixOS detected] Applying 'patchelf' to downloaded binaries");
81+
82+
// Find the correct dynamic linker:
83+
// `interp=$(cat $NIX_CC/nix-support/dynamic-linker)`
84+
let nix_cc = std::env::var_os("NIX_CC")
85+
.context("On NixOS but 'NIX_CC` environment variable not set, couldn't apply patchelf.")?;
86+
let path = Path::new(&nix_cc).join("nix-support/dynamic-linker");
87+
let interp_raw = std::fs::read_to_string(path)
88+
.context("Couldn't read $NIX_CC/nix-support/dynamic-linker")?;
89+
let interp = interp_raw.trim();
90+
91+
// Find the correct path to link C++ stdlib:
92+
// `rpath=$(nix-instantiate --eval -E "(import <nixpkgs> {}).stdenv.cc.cc.lib.outPath")/lib`
93+
let rpath_output = Command::new("nix-instantiate")
94+
.args(&["--eval", "-E", "(import <nixpkgs> {}).stdenv.cc.cc.lib.outPath"])
95+
.output()?;
96+
if !rpath_output.status.success() {
97+
bail!("Failed to find C++ standard library with `nix-instantiate`");
98+
}
99+
let rpath_raw = std::str::from_utf8(&rpath_output.stdout)?;
100+
// The output is in quotes, remove them:
101+
let rpath_prefix = rpath_raw.trim().trim_matches('"');
102+
let rpath = format!("{}/lib", rpath_prefix);
103+
104+
let patch_interp = |file: &Path| -> Result<()> {
105+
Command::new("patchelf").args(&["--set-interpreter", interp]).arg(file).run()
106+
};
107+
let patch_rpath = |file: &Path| -> Result<()> {
108+
Command::new("patchelf").args(&["--set-rpath", &rpath]).arg(file).run()
109+
};
110+
111+
let bin = kani_dir.join("bin");
112+
113+
for filename in &["kani-compiler", "kani-driver"] {
114+
patch_interp(&bin.join(filename))?;
115+
}
116+
for filename in &["cbmc", "goto-analyzer", "goto-cc", "goto-instrument", "symtab2gb"] {
117+
let file = bin.join(filename);
118+
patch_interp(&file)?;
119+
patch_rpath(&file)?;
120+
}
121+
122+
Ok(())
123+
}

Diff for: src/setup.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use std::process::Command;
1111
use anyhow::{bail, Context, Result};
1212

1313
use crate::cmd::AutoRun;
14+
use crate::os_hacks;
1415

1516
/// Comes from our Cargo.toml manifest file. Must correspond to our release verion.
1617
const VERSION: &str = env!("CARGO_PKG_VERSION");
@@ -48,6 +49,8 @@ pub fn setup(use_local_bundle: Option<OsString>) -> Result<()> {
4849

4950
setup_build_kani_prelude(&kani_dir, toolchain_version)?;
5051

52+
os_hacks::setup_os_hacks(&kani_dir, &os)?;
53+
5154
println!("[6/6] Successfully completed Kani first-time setup.");
5255

5356
Ok(())
@@ -114,7 +117,7 @@ fn setup_python_deps(kani_dir: &Path, os: &os_info::Info) -> Result<()> {
114117
if os.os_type() == os_info::Type::Ubuntu
115118
&& *os.version() == os_info::Version::Semantic(18, 4, 0)
116119
{
117-
crate::os_hacks::setup_python_deps_on_ubuntu_18_04(&pyroot, pkg_versions)?;
120+
os_hacks::setup_python_deps_on_ubuntu_18_04(&pyroot, pkg_versions)?;
118121
return Ok(());
119122
}
120123

0 commit comments

Comments
 (0)