1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
//! Build-time evaluated expressions //! //! `cconst` allows defining constants at build time of any type that //! implements the `Copy` trait. Values are generated through `build.rs`: //! //! ``` //! // build.rs //! #[macro_use] //! extern crate cconst; //! //! use std::net::Ipv4Addr; //! //! let mut cs = CopyConsts::new(); //! cs.add_const("default_ns", "::std::net::Ipv4Addr", { //! Ipv4Addr::new(8, 8, 8, 8) //! }); //! cs.write_code().unwrap(); //! //! ``` //! //! Once set up, they can be included using the `cconst!` macro: //! //! ```ignore //! // main.rs //! #[macro_use] //! extern crate cconst; //! //! include!(cconst!(default_ns)); //! //! fn main() { //! println!("default_ns: {:?}", default_ns()); //! } //! ``` //! //! # Internals //! //! `cconst` works by serializing the value defined in `build.rs` into //! byte-slice literals and including those where applicable. The example above //! results in roughly the following generated code: //! //! ```ignore //! #[inline] //! fn default_ns() -> &'static ::std::net::Ipv4Addr { //! const BUF: &[u8] = &[0x08, 0x08, 0x08, 0x08, ]; //! unsafe { &*(BUF.as_ptr() as *const ::std::net::Ipv4Addr) } //! } //! ``` //! //! Calling `default_ns()` should result in an inlined pointer cast and little, //! if any overhead. //! //! ## Caveats //! //! Due to the nature of the code generation used, the type supplied to the //! `add_const!`-macro should be fully qualified, i.e. start with a `::`. If //! not, it must be visible at the `include!` call site. //! //! While `Copy` indicates that the type can freely be copied, if any resources //! are held by the type outside of what the compiler knows, the type cannot be //! compile generated. //! //! Types that have the same names but are different will result in undefined //! behaviour with possibly catastrophic results. This will most likely occur //! when the `build_dependencies` and `dependencies` versions of a required //! crate differ. //! //! ## TODO //! //! * [ ] `#[no_std]` support /// Imports a stored constant #[macro_export] macro_rules! cconst { ($fname:ident) => (concat!(env!("OUT_DIR"), "/cconst-", stringify!($fname), ".rs")) } /// Creates a constant for inclusion using `cconst!`. /// /// This macro should be preferred over `CopyConsts::add_const`, as it provides /// additional type safety. #[macro_export] macro_rules! add_const { ($cconsts:expr, $fname: expr, $ctype:ty, $val:expr) => ( let mat: $ctype = $val; $cconsts.add_const($fname, stringify!($ctype), &mat); ) } use std::{collections, env, fs, io}; use std::io::Write; use std::mem::size_of; fn marshall_value<T: Copy>(val: &T) -> String { let vptr = val as *const _ as *const u8; let mut rexpr = String::new(); rexpr += "&["; for i in 0..size_of::<T>() { rexpr.push_str(&format!("0x{:02X}, ", unsafe { *vptr.offset(i as isize) })); } rexpr += "]"; rexpr } fn create_constant_func<T: Copy>(fname: &str, typename: &str, val: &T) -> String { let sval = marshall_value(val); format!("#[inline]\nfn {}() -> &'static {} {{ const BUF: &[u8] = {}; unsafe {{ &*(BUF.as_ptr() as *const {}) }} }}\n", fname, typename, sval, typename) } /// Manage `build.rs` constructed constants pub struct CopyConsts(collections::HashMap<String, String>); fn build_output_path(fname: &str) -> Result<String, env::VarError> { Ok(env::var("OUT_DIR")? + "/cconst-" + fname + ".rs") } impl CopyConsts { /// Create new set of compile time functions pub fn new() -> CopyConsts { CopyConsts(collections::HashMap::new()) } /// Add constant /// /// Adds a value to be stored as a compile time constant, with an internal /// name of `fname`. /// /// `typename` is required to output generated code, but not checked. For /// this reason using the `add_const!` macro instead of this function /// should be preferred. pub fn add_const<T: Copy>(&mut self, fname: &str, typename: &str, val: &T) { self.0 .insert(fname.to_owned(), create_constant_func(fname, typename, val)); } /// Write out code for compile-time constant generation. pub fn write_code(&self) -> io::Result<()> { for (fname, buf) in &self.0 { let output_path = build_output_path(fname) .map_err(|_| io::Error::new(io::ErrorKind::Other, "missing OUT_PATH"))?; write!(io::stdout(), "OUTPUT PATH {:?}", output_path).unwrap(); let mut fp = fs::File::create(output_path)?; fp.write_all(buf.as_bytes())?; } Ok(()) } }