Skip to content
Merged
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
6 changes: 2 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
build = "build/cargo.rs"
build = "build/main.rs"
categories = ["cryptography", "no-std"]
description = "An experiment."
edition = "2024"
Expand Down Expand Up @@ -41,9 +41,7 @@ include = [

"benches/*.rs",

"build/build.rs",
"build/build/path.rs",
"build/cargo.rs",
"build/**/*.rs",

"crypto/chacha/asm/chacha-armv4.pl",
"crypto/chacha/asm/chacha-armv8.pl",
Expand Down
178 changes: 178 additions & 0 deletions build/build/c.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
// Copyright 2015-2026 Brian Smith.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

//! A wrapper around cc-rs.

// Avoid `std::env` here. All configuration should be done through `Target`,
// `Profile`, and `Tools`.
use super::target::*;
use std::path::{Path, PathBuf};

fn cpp_flags(compiler: &cc::Tool) -> &'static [&'static str] {
if !compiler.is_like_msvc() {
static NON_MSVC_FLAGS: &[&str] = &[
"-fvisibility=hidden",
"-std=c1x", // GCC 4.6 requires "c1x" instead of "c11"
"-Wall",
"-Wbad-function-cast",
"-Wcast-align",
"-Wcast-qual",
"-Wconversion",
"-Wmissing-field-initializers",
"-Wmissing-include-dirs",
"-Wnested-externs",
"-Wredundant-decls",
"-Wshadow",
"-Wsign-compare",
"-Wsign-conversion",
"-Wstrict-prototypes",
"-Wundef",
"-Wuninitialized",
];
NON_MSVC_FLAGS
} else {
static MSVC_FLAGS: &[&str] = &[
"/Gy", // Enable function-level linking.
"/Zc:wchar_t",
"/Zc:forScope",
"/Zc:inline",
// Warnings.
"/W4",
"/wd4127", // C4127: conditional expression is constant
"/wd4464", // C4464: relative include path contains '..'
"/wd5045", /* C5045: Compiler will insert Spectre mitigation for memory load if
* /Qspectre switch specified */
];
MSVC_FLAGS
}
}

pub struct Profile {
/// Is this a debug build? This affects whether assertions might be enabled
/// in the C code. For packaged builds, this should always be `false`.
pub is_debug: bool,

/// true: Force warnings to be treated as errors.
/// false: Use the default behavior (perhaps determined by `$CFLAGS`, etc.)
pub force_warnings_into_errors: bool,

pub is_git: bool,
}

fn new_build(
target: &Target,
profile: &Profile,
c_root_dir: &Path,
include_dir: &Path,
) -> cc::Build {
let mut b = cc::Build::new();
configure_cc(&mut b, target, profile, c_root_dir, include_dir);
b
}

pub fn build_library<'a>(
target: &Target,
profile: &Profile,
c_root_dir: &Path,
lib_name: &str,
srcs: impl Iterator<Item = &'a PathBuf>,
include_dir: &Path,
preassembled_objs: &[PathBuf],
) {
let mut c = new_build(target, profile, c_root_dir, include_dir);

// Compile all the (dirty) source files into object files.
srcs.for_each(|src| {
c.file(c_root_dir.join(src));
});

preassembled_objs.iter().for_each(|obj| {
c.object(obj);
});

// Rebuild the library if necessary.
let lib_path = target.out_dir.join(format!("lib{lib_name}.a"));

// Handled below.
let _ = c.cargo_metadata(false);

c.compile(
lib_path
.file_name()
.and_then(|f| f.to_str())
.expect("No filename"),
);

// Link the library. This works even when the library doesn't need to be
// rebuilt.
println!("cargo:rustc-link-lib=static={lib_name}");
println!(
"cargo:rustc-link-search=native={}",
target.out_dir.to_str().expect("Invalid path")
);
}

fn configure_cc(
c: &mut cc::Build,
target: &Target,
profile: &Profile,
c_root_dir: &Path,
include_dir: &Path,
) {
// FIXME: On Windows AArch64 we currently must use Clang to compile C code.
// clang-cl emulates the cl.exe command line, `$CFLAGS`, etc.
if target.os == WINDOWS && target.arch == AARCH64 {
let _: &_ = c.prefer_clang_cl_over_msvc(true);
};
let compiler = c.get_compiler();

let _ = c.include(c_root_dir.join("include"));
let _ = c.include(include_dir);
for f in cpp_flags(&compiler) {
let _ = c.flag(f);
}

if APPLE_ABI.contains(&target.os.as_str()) {
// ``-gfull`` is required for Darwin's |-dead_strip|.
let _ = c.flag("-gfull");
} else if !compiler.is_like_msvc() {
let _ = c.flag("-g3");
};

if !profile.is_debug {
let _ = c.define("NDEBUG", None);
}

if target.arch == X86 {
let is_msvc_not_clang_cl = compiler.is_like_msvc() && !compiler.is_like_clang_cl();
if !is_msvc_not_clang_cl {
let _ = c.flag("-msse2");
}
}

// Allow cross-compiling without a target sysroot for these targets.
if (target.arch == WASM32)
|| (target.os == "linux" && target.env == "musl" && target.arch != X86_64)
{
// TODO: Expand this to non-clang compilers in 0.17.0 if practical.
if compiler.is_like_clang() {
let _ = c.flag("-nostdlibinc");
let _ = c.define("RING_CORE_NOSTDLIBINC", "1");
}
}

if profile.force_warnings_into_errors {
c.warnings_into_errors(true);
}
}
162 changes: 162 additions & 0 deletions build/build/cargo.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
// Copyright 2015-2016 Brian Smith.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

//! Cargo-specific build logic that isn't specific to this project.

use super::{Profile, Target, target::Endian};
// Avoid `std::env` here; use `self::env` instead.
use std::{
fs::{self},
path::{Path, PathBuf},
};

pub mod env {
use std::ffi::OsString;

macro_rules! define_env {
{ $vis:vis $NAME:ident : $ty:ident } => {
$vis const $NAME: EnvVar = EnvVar {
name: stringify!($NAME),
ty: EnvVarTy::$ty,
};
};
}

enum EnvVarTy {
RerunIfChanged,
SetByCargo,
}

pub struct EnvVar {
pub name: &'static str,
ty: EnvVarTy,
}

/// Read an environment variable and optionally tell Cargo that we depend on it.
///
/// The env var is static since we intend to only read a static set of environment
/// variables.
pub fn var_os(env_var: &'static EnvVar) -> Option<OsString> {
match env_var.ty {
EnvVarTy::RerunIfChanged => {
println!("cargo:rerun-if-env-changed={}", env_var.name);
}
EnvVarTy::SetByCargo => {}
}
std::env::var_os(env_var.name)
}

pub fn var(env_var: &'static EnvVar) -> Option<String> {
var_os(env_var).and_then(|value| value.into_string().ok())
}

// In alphabetical order
define_env! { pub(super) CARGO_CFG_TARGET_ARCH: SetByCargo }
define_env! { pub(super) CARGO_CFG_TARGET_ENDIAN: SetByCargo }
define_env! { pub(super) CARGO_CFG_TARGET_ENV: SetByCargo }
define_env! { pub(super) CARGO_CFG_TARGET_OS: SetByCargo }
define_env! { pub(super) CARGO_MANIFEST_DIR: SetByCargo }
define_env! { pub(super) CARGO_MANIFEST_LINKS: SetByCargo }
define_env! { pub(super) CARGO_PKG_NAME: SetByCargo }
define_env! { pub(super) CARGO_PKG_VERSION_MAJOR: SetByCargo }
define_env! { pub(super) CARGO_PKG_VERSION_MINOR: SetByCargo }
define_env! { pub(super) CARGO_PKG_VERSION_PATCH: SetByCargo }
define_env! { pub(super) CARGO_PKG_VERSION_PRE: SetByCargo }
define_env! { pub(super) DEBUG: SetByCargo }
define_env! { pub(super) OUT_DIR: SetByCargo }

// XXX: These don't belong here.
define_env! { pub PERL_EXECUTABLE: RerunIfChanged }
define_env! { pub RING_PREGENERATE_ASM: RerunIfChanged }
}

pub fn root_dir() -> PathBuf {
// Avoid assuming the working directory is the same is the $CARGO_MANIFEST_DIR so that toolchains
// which may assume other working directories can still build this code.
PathBuf::from(
env::var_os(&env::CARGO_MANIFEST_DIR).expect("CARGO_MANIFEST_DIR should always be set"),
)
}

pub fn extern_c_prefix(component: &str) -> String {
// Keep in sync with `core_name_and_version!` in prefixed.rs.
let core_name_and_version = [
&env::var(&env::CARGO_PKG_NAME).unwrap(),
component,
&env::var(&env::CARGO_PKG_VERSION_MAJOR).unwrap(),
&env::var(&env::CARGO_PKG_VERSION_MINOR).unwrap(),
&env::var(&env::CARGO_PKG_VERSION_PATCH).unwrap(),
&env::var(&env::CARGO_PKG_VERSION_PRE).unwrap(), // Often empty
]
.join("_");
// Ensure `links` in Cargo.toml is consistent with the version.
assert_eq!(
&env::var(&env::CARGO_MANIFEST_LINKS).unwrap(),
&core_name_and_version
);
core_name_and_version
}

impl Target {
pub fn new_from_env() -> Self {
let arch = env::var(&env::CARGO_CFG_TARGET_ARCH).unwrap();
let os = env::var(&env::CARGO_CFG_TARGET_OS).unwrap();
let env = env::var(&env::CARGO_CFG_TARGET_ENV).unwrap();
let endian = env::var(&env::CARGO_CFG_TARGET_ENDIAN).unwrap();
let endian = if endian == "little" {
Endian::Little
} else {
Endian::Other
};

let out_dir = PathBuf::from(env::var_os(&env::OUT_DIR).unwrap());

Self {
arch,
os,
env,
endian,
out_dir,
}
}
}

impl Profile {
pub fn new_from_env(root_dir: &Path) -> Self {
let is_git = fs::metadata(root_dir.join(".git")).is_ok();

// Published builds are always built in release mode.
let is_debug = is_git && env::var(&env::DEBUG).unwrap() != "false";

// During local development, force warnings in non-Rust code to be treated
// as errors. Since warnings are highly compiler-dependent and compilers
// don't maintain backward compatibility w.r.t. which warnings they issue,
// don't do this for packaged builds.
let force_warnings_into_errors = is_git;

Self {
is_git,
is_debug,
force_warnings_into_errors,
}
}
}

// TODO: We should emit `cargo:rerun-if-changed-env` for the various
// environment variables that affect the build.
pub fn emit_rerun_if_changed<'a>(paths: impl Iterator<Item = &'a Path>) {
paths.for_each(|path| {
println!("cargo:rerun-if-changed={}", path.to_str().unwrap());
})
}
Loading
Loading