#![allow(non_camel_case_types, non_upper_case_globals)]
extern crate libc;
extern crate mktemp;
extern crate target_build_utils;
use std::path::Path;
use std::ffi::{CString, CStr, OsString, OsStr};
use std::sync::{Once, ONCE_INIT};
type LLVMBool = libc::c_uint;
const LLVMTrue: LLVMBool = 1;
const LLVMFalse: LLVMBool = 0;
#[allow(missing_copy_implementations)]
enum LLVMContext_opaque {}
type LLVMContextRef = *mut LLVMContext_opaque;
#[allow(missing_copy_implementations)]
enum LLVMMemoryBuffer_opaque {}
type LLVMMemoryBufferRef = *mut LLVMMemoryBuffer_opaque;
#[allow(missing_copy_implementations)]
enum LLVMModule_opaque {}
type LLVMModuleRef = *mut LLVMModule_opaque;
#[allow(missing_copy_implementations)]
enum LLVMTarget_opaque {}
type LLVMTargetRef = *mut LLVMTarget_opaque;
enum LLVMTargetMachine_opaque {}
type LLVMTargetMachineRef = *mut LLVMTargetMachine_opaque;
enum LLVMArchiveChild_opaque {}
type LLVMArchiveChildRef = *mut LLVMArchiveChild_opaque;
#[allow(missing_copy_implementations)]
enum LLVMRustArchiveMember_opaque {}
type LLVMRustArchiveMemberRef = *mut LLVMRustArchiveMember_opaque;
extern {
fn LLVMContextCreate() -> LLVMContextRef;
fn LLVMContextDispose(C: LLVMContextRef);
fn LLVMParseIRInContext(context: LLVMContextRef,
buf: LLVMMemoryBufferRef,
om: *mut LLVMModuleRef,
msg: *mut *mut libc::c_char) -> LLVMBool;
fn LLVMSetTarget(M: LLVMModuleRef, Triple: *const libc::c_char);
fn LLVMDisposeModule(M: LLVMModuleRef);
fn LLVMVerifyModule(_: LLVMModuleRef, _: VerifierFailureAction, _: *mut *mut libc::c_char)
-> LLVMBool;
fn LLVMDisposeMessage(_: *mut libc::c_char);
fn LLVMCreateTargetMachine(tr: LLVMTargetRef,
triple: *const libc::c_char,
cpu: *const libc::c_char,
features: *const libc::c_char,
lvl: Optimisation,
reloc: Relocations,
cm: CodegenModel) -> LLVMTargetMachineRef;
fn LLVMDisposeTargetMachine(_: LLVMTargetMachineRef);
fn LLVMTargetMachineEmitToFile (_: LLVMTargetMachineRef,
_: LLVMModuleRef,
filename: *const libc::c_char,
_: CodeGenFileType,
err: *mut *mut libc::c_char) -> LLVMBool;
fn LLVMGetTargetFromTriple(triple: *const libc::c_char,
_: *mut LLVMTargetRef,
err: *mut *mut libc::c_char) -> LLVMBool;
fn LLVMRustCreateMemoryBufferWithContentsOfFile(Path: *const libc::c_char)
-> LLVMMemoryBufferRef;
fn LLVMRustGetLastError() -> *const libc::c_char;
fn LLVMRustArchiveMemberNew(_: *const libc::c_char,
_: *const libc::c_char,
_: LLVMArchiveChildRef) -> LLVMRustArchiveMemberRef;
fn LLVMRustArchiveMemberFree(_: LLVMRustArchiveMemberRef);
fn LLVMRustWriteArchive(Dst: *const libc::c_char,
NumMembers: libc::size_t,
Members: *const LLVMRustArchiveMemberRef,
WriteSymbtab: bool,
Kind: ArchiveKind) -> libc::c_int;
}
#[allow(dead_code)]
#[repr(C)]
enum VerifierFailureAction {
AbortProcess = 0,
PrintMessage = 1,
ReturnStatus = 2,
}
#[allow(dead_code)]
#[repr(C)]
enum CodeGenFileType {
Assembly = 0,
Object = 1,
}
#[derive(Copy, Clone, PartialEq, Debug)]
#[repr(C)]
pub enum Relocations {
Default = 0,
Static = 1,
PIC = 2,
DynamicNoPic = 3,
}
#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub enum CodegenModel {
Default = 0,
Small = 2,
Kernel = 3,
Medium = 4,
Large = 5,
}
#[derive(Copy, Clone, PartialEq, Debug)]
#[repr(C)]
pub enum Optimisation {
O0 = 0,
O1 = 1,
O2 = 2,
O3 = 3,
}
#[repr(C)]
#[derive(Copy, Clone)]
pub enum ArchiveKind {
Gnu,
Mips64,
Bsd,
Coff,
}
impl Default for ArchiveKind {
fn default() -> ArchiveKind {
let target_info = target_build_utils::TargetInfo::new()
.expect("could not parse target info");
match target_info.target_os() {
"macos" | "ios" => ArchiveKind::Bsd,
_ => ArchiveKind::Gnu
}
}
}
#[derive(Debug)]
pub struct BuildOptions {
pub triple: String,
pub cpu: String,
pub attr: String,
pub model: CodegenModel,
pub reloc: Relocations,
pub opt: Optimisation,
pub ar_section_name: String,
}
impl Default for BuildOptions {
fn default() -> BuildOptions {
use std::env::var;
BuildOptions {
triple: var("TARGET").unwrap_or(String::new()),
cpu: String::new(),
attr: String::new(),
model: CodegenModel::Default,
reloc: Relocations::Default,
opt: match var("OPT_LEVEL").ok().and_then(|v| v.parse().ok()).unwrap_or(0u64) {
0 => Optimisation::O0,
1 => Optimisation::O1,
2 => Optimisation::O2,
3 | _ => Optimisation::O3,
},
ar_section_name: String::new(),
}
}
}
pub struct Printout {
libname: String,
outdir: OsString,
deps: Vec<String>
}
impl Printout {
pub fn print(mut self) {
self.print_link();
self.print_path();
self.print_deps();
}
pub fn print_link(&mut self) {
let name = ::std::mem::replace(&mut self.libname, String::new());
if !name.is_empty() {
println!("cargo:rustc-link-lib={}", name);
}
}
pub fn print_path(&mut self) {
let outdir = ::std::mem::replace(&mut self.outdir, OsString::new());
if !outdir.is_empty() {
let od = outdir.into_string().expect("outdir contains invalid unicode");
println!("cargo:rustc-link-search=native={}", od);
}
}
pub fn print_deps(&mut self) {
let deps = ::std::mem::replace(&mut self.deps, Vec::new());
for dep in deps {
println!("cargo:rerun-if-changed={}", dep);
}
}
}
fn initialize_llvm() {
static ONCE: Once = ONCE_INIT;
macro_rules! init_target(
($($method:ident),*) => { {
extern { $(fn $method();)* }
$($method();)*
} }
);
ONCE.call_once(|| unsafe {
init_target!(LLVMInitializeX86TargetInfo,
LLVMInitializeX86Target,
LLVMInitializeX86TargetMC,
LLVMInitializeX86AsmPrinter,
LLVMInitializeX86AsmParser,
LLVMInitializeARMTargetInfo,
LLVMInitializeARMTarget,
LLVMInitializeARMTargetMC,
LLVMInitializeARMAsmPrinter,
LLVMInitializeARMAsmParser,
LLVMInitializeAArch64TargetInfo,
LLVMInitializeAArch64Target,
LLVMInitializeAArch64TargetMC,
LLVMInitializeAArch64AsmPrinter,
LLVMInitializeAArch64AsmParser,
LLVMInitializeMipsTargetInfo,
LLVMInitializeMipsTarget,
LLVMInitializeMipsTargetMC,
LLVMInitializeMipsAsmPrinter,
LLVMInitializeMipsAsmParser,
LLVMInitializePowerPCTargetInfo,
LLVMInitializePowerPCTarget,
LLVMInitializePowerPCTargetMC,
LLVMInitializePowerPCAsmPrinter,
LLVMInitializePowerPCAsmParser);
});
}
macro_rules! fail_if {
($ex: expr, $($args: tt)*) => {
if $ex { return Err(format!($($args)*)) }
}
}
pub fn build_archive<'a, P: 'a, I>(archive: P, iter: I)
-> Result<Printout, String>
where P: AsRef<Path>, I: IntoIterator<Item=&'a (P, BuildOptions)> {
build_archive_kind(ArchiveKind::default(), archive, iter)
}
pub fn build_archive_kind<'a, P: 'a, I>(format: ArchiveKind, archive: P, iter: I)
-> Result<Printout, String>
where P: AsRef<Path>, I: IntoIterator<Item=&'a (P, BuildOptions)>
{
initialize_llvm();
let mut strings = vec![];
let mut members = vec![];
let mut temps = vec![];
let mut deps = vec![];
let outpath = ::std::env::var_os("OUT_DIR").unwrap_or_default();
let libstem = {
fail_if!(archive.as_ref().extension() != Some(OsStr::new("a")), "extension must be .a");
let libstem = try!(archive.as_ref().file_stem().and_then(|s| s.to_str()).ok_or_else(||
String::from("output filename has invalid stem")));
fail_if!(!libstem.starts_with("lib"), "output filename must start with lib");
String::from(&libstem[3..])
};
unsafe {
let ctx = LLVMContextCreate();
fail_if!(ctx.is_null(), "could not create the context");
for &(ref p, ref opt) in iter {
let mut module = ::std::ptr::null_mut();
let mut msg = ::std::ptr::null_mut();
let input_str = try!(p.as_ref().to_str().ok_or_else(||
String::from("input filename is not utf-8")));
deps.push(String::from(input_str));
let path = try!(CString::new(input_str).map_err(|_|
String::from("input filename contains nulls")));
let buf = LLVMRustCreateMemoryBufferWithContentsOfFile(path.as_ptr());
fail_if!(buf.is_null(), "could not open input file {:?}: {:?}",
p.as_ref(), CStr::from_ptr(LLVMRustGetLastError()));
LLVMParseIRInContext(ctx, buf, &mut module, &mut msg);
fail_if!(module.is_null(), "module could not be parsed successfully: {:?}",
CStr::from_ptr(msg));
if LLVMVerifyModule(module, VerifierFailureAction::ReturnStatus, &mut msg) == LLVMTrue {
let ret = Err(format!("Module is not valid: {:?}", CStr::from_ptr(msg)));
LLVMDisposeMessage(msg);
return ret;
}
let triple = CString::new(opt.triple.clone()).expect("triple contains null bytes");
let cpu = CString::new(opt.cpu.clone()).expect("cpu contains null bytes");
let attr = CString::new(opt.attr.clone()).expect("attr contains null bytes");
if !opt.triple.is_empty() {
LLVMSetTarget(module, triple.as_ptr());
}
let mut target = ::std::ptr::null_mut();
let status = LLVMGetTargetFromTriple(triple.as_ptr(), &mut target, &mut msg);
fail_if!(status != LLVMFalse, "could not generate target from triple {}: {:?}",
opt.triple, CStr::from_ptr(msg));
let machine = LLVMCreateTargetMachine(target,
triple.as_ptr(),
cpu.as_ptr(),
attr.as_ptr(),
opt.opt,
opt.reloc,
opt.model);
fail_if!(machine.is_null(), "could not create the target machine \
(likely invalid BuildOptions {:?})", opt);
let tmp = try!(mktemp::Temp::new_file_in(
p.as_ref().parent().expect("cannot get basename of input filename")
).map_err(|e| format!("could not create temp file: {}", e)));
let object_file = try!(CString::new(
try!(tmp.as_ref().to_str().ok_or_else(|| String::from("object path is not utf-8")))
).map_err(|_| String::from("object filename contains nulls")));
temps.push(tmp);
let status = LLVMTargetMachineEmitToFile(machine,
module,
object_file.as_ptr(),
CodeGenFileType::Object,
&mut msg);
fail_if!(status == LLVMTrue, "could not generate object file: {:?}",
CStr::from_ptr(msg));
let name = try!(CString::new(opt.ar_section_name.clone())
.map_err(|_| String::from("archive member name contains nulls")));
members.push(LLVMRustArchiveMemberNew(object_file.as_ptr(),
name.as_ptr(),
std::ptr::null_mut()));
strings.push(name);
strings.push(object_file);
LLVMDisposeTargetMachine(machine);
LLVMDisposeModule(module);
}
let out_target = Path::new(&outpath).join(archive);
let libname_str = try!(out_target.to_str().ok_or_else(||
String::from("archive filename is not utf-8")));
let dest = try!(CString::new(libname_str).map_err(|_|
String::from("output file has interior nulls")));
let r = LLVMRustWriteArchive(dest.as_ptr(),
members.len() as libc::size_t,
members.as_ptr(),
true,
format);
fail_if!(r != 0, "{:?}", {
let err = LLVMRustGetLastError();
if err.is_null() {
"failed to write archive".to_string()
} else {
String::from_utf8_lossy(CStr::from_ptr(err).to_bytes())
.into_owned()
}
});
for member in members {
LLVMRustArchiveMemberFree(member);
}
LLVMContextDispose(ctx);
Ok(Printout {
libname: libstem,
outdir: outpath,
deps: deps
})
}
}