<commit_before>extern crate llvm_sys;

use std::collections::HashMap;
use std::ffi::{CStr, CString};
use std::str;
use self::llvm_sys::core::*;
use self::llvm_sys::analysis::*;
use self::llvm_sys::execution_engine::*;
use self::llvm_sys::prelude::*;
use self::llvm_sys::target::*;
//use self::llvm_sys::transforms::scalar::*;
use syntax::ast::op::*;
use syntax::ast::expr::*;
use syntax::ast::literals::*;

// Struct to keep track of data needed to build IR
pub struct Context {
    context: LLVMContextRef,
    module: LLVMModuleRef,
    builder: LLVMBuilderRef,
    execution_engine: LLVMExecutionEngineRef,
    fn_pass_manager: LLVMPassManagerRef,
    named_values: HashMap<String, LLVMValueRef>,
}

impl Context {
    pub unsafe fn new(module_name: &str) -> Context {
        LLVM_InitializeNativeTarget();
        LLVM_InitializeNativeAsmPrinter();
        LLVM_InitializeNativeAsmParser();
        LLVMLinkInInterpreter();

        let context = LLVMContextCreate();
        let module = LLVMModuleCreateWithNameInContext(c_str_ptr(module_name), context);
        let builder = LLVMCreateBuilderInContext(context);
        let named_values = HashMap::new();
        let mut execution_engine = 0 as LLVMExecutionEngineRef;
        let mut error_msg = 0 as *mut i8;

        if LLVMCreateExecutionEngineForModule(&mut execution_engine, module, &mut error_msg) == 1 {
            panic!("{}", str::from_utf8(CStr::from_ptr(error_msg).to_bytes()).unwrap())
        }

        let fn_pass_manager = LLVMCreateFunctionPassManagerForModule(module);
        let target_data = LLVMGetExecutionEngineTargetData(execution_engine);
        let data_layout = LLVMCopyStringRepOfTargetData(target_data);
        LLVMSetDataLayout(module, data_layout);
        LLVMAddTargetData(target_data, fn_pass_manager);
        LLVMDisposeMessage(data_layout);

        // Add desired passes to pass manager here
//        LLVMAddInstructionCombiningPass(fn_pass_manager);

        LLVMInitializeFunctionPassManager(fn_pass_manager);

        Context {
            context: context,
            module: module,
            builder: builder,
            execution_engine: execution_engine,
            fn_pass_manager: fn_pass_manager,
            named_values: named_values,
        }
    }

    // Dump the IR to stdout
    pub unsafe fn dump(&self) {
        LLVMDumpModule(self.module);
    }

    // Verifies that the llvm code is valid. Optionally prints
    // the error message, else abort.
    pub unsafe fn verify(&self) {
        let action = LLVMVerifierFailureAction::LLVMPrintMessageAction;
        let msg = vec![c_str_ptr("")];

        LLVMVerifyModule(self.module, action, msg.as_ptr() as *mut _);
        LLVMDisposeMessage(msg[0] as *mut _);
    }

    pub unsafe fn run(&self) -> u64 {
        let main = LLVMGetNamedFunction(self.module, c_str_ptr("main"));
        let result = LLVMRunFunction(self.execution_engine, main, 0, 0 as *mut LLVMGenericValueRef);

        LLVMGenericValueToInt(result, 1)
    }

    // Struct getters
    pub fn get_context(&self) -> LLVMContextRef {
        self.context
    }

    pub fn get_module(&self) -> LLVMModuleRef {
        self.module
    }

    pub fn get_builder(&self) -> LLVMBuilderRef {
        self.builder
    }

    pub fn clear_values(&mut self) {
        self.named_values.clear();
    }
}

impl Drop for Context {
    fn drop(&mut self) {
        unsafe {
            LLVMDisposePassManager(self.fn_pass_manager);
//            LLVMDisposeExecutionEngine(self.execution_engine); // signal 4s for some reason
            LLVMDisposeBuilder(self.builder);
            LLVMDisposeModule(self.module);
            LLVMContextDispose(self.context);
        }
    }
}

pub trait CodeGen {
    unsafe fn gen_code(&self, context: &mut Context) -> Option<LLVMValueRef>;
}

impl CodeGen for ExprWrapper {
    unsafe fn gen_code(&self, context: &mut Context) -> Option<LLVMValueRef> {
        self.get_expr().gen_code(context)
    }
}

impl CodeGen for Expr {
    unsafe fn gen_code(&self, context: &mut Context) -> Option<LLVMValueRef> {
        match *self {
            Expr::Block(ref vec) => {
                let mut gen = None;

                for expr in vec {
                    gen = expr.gen_code(context);
                }

                gen
            },
            Expr::Ident(ref name) => {
                match context.named_values.get(name) {
                    Some(val) => Some(*val),
                    None => {
                        println!("Error: Unknown variable named {}", name);

                        None
                    }
                }
            },
            Expr::Literal(ref literal_type) => {
                match literal_type {
                    &Literals::UTF8String(ref val) => {
                        // Types
                        let array_type1 = LLVMArrayType(LLVMInt8TypeInContext(context.get_context()), val.len() as u32);
                        // let string_struct_type = LLVMGetNamedGlobal(context.get_module(), c_str_ptr("struct.string"));
                        // if string_struct_type.is_null() {
                        //     println!("TMP Error: Cannot find builtin string type!");
                        // }
                        let i8_type = LLVMInt8TypeInContext(context.get_context());
                        let i8_ptr_type = LLVMPointerType(i8_type, 0);
                        let i32_type = LLVMInt32TypeInContext(context.get_context());
                        let i64_type = LLVMInt64TypeInContext(context.get_context());
                        // let string_type_fields = vec![LLVMInt32TypeInContext(context.get_context()),
                        //               LLVMPointerType(LLVMInt8TypeInContext(context.get_context()), 0)];

                        // Values
                        let len = LLVMConstInt(i64_type, val.len() as u64, 0);

                        // Make a global string constant and assign the value:
                        let const_str_var = LLVMAddGlobal(context.get_module(), array_type1, c_str_ptr("str"));
                        let mut chars = Vec::new();
                        for chr in val.bytes() {
                            chars.push(LLVMConstInt(i8_type, chr as u64, 0));
                        }

                        let const_str_array = LLVMConstArray(i8_type, chars.as_ptr() as *mut _, chars.len() as u32);
                        LLVMSetInitializer(const_str_var, const_str_array);
                        LLVMSetGlobalConstant(const_str_var, 1);

                        let args = vec![LLVMConstInt(i32_type, 0, 0), LLVMConstInt(i32_type, 0, 0)];
                        let allocated_str_ptr = LLVMBuildAlloca(context.get_builder(), i8_ptr_type, c_str_ptr("a"));
                        let mallocated_ptr = LLVMBuildMalloc(context.get_builder(), i8_type, c_str_ptr("m"));

                        LLVMSetTailCall(mallocated_ptr, 0);
                        LLVMBuildStore(context.get_builder(), mallocated_ptr, allocated_str_ptr);

                        let element_ptr = LLVMBuildGEP(context.get_builder(), const_str_var, args.as_ptr() as *mut _, 2, c_str_ptr(""));
                        LLVMBuildStore(context.get_builder(), element_ptr, allocated_str_ptr);

                        let loaded_ptr = LLVMBuildLoad(context.get_builder(), allocated_str_ptr, c_str_ptr("l"));
                        let struct_fields = vec![LLVMGetUndef(i8_ptr_type), len];
                        let const_struct = LLVMConstStructInContext(context.get_context(), struct_fields.as_ptr() as *mut _, 2, 0);

                        Some(LLVMBuildInsertValue(context.get_builder(), const_struct, loaded_ptr, 0, c_str_ptr("i"))) // Problem
                    },
                    &Literals::I32Num(ref val) => {
                        let ty = LLVMInt32TypeInContext(context.get_context());
                        Some(LLVMConstInt(ty, *val as u64, 1))
                    },
                    &Literals::I64Num(ref val) => {
                        let ty = LLVMInt64TypeInContext(context.get_context());
                        Some(LLVMConstInt(ty, *val as u64, 1))
                    },
                    &Literals::U32Num(ref val) => {
                        let ty = LLVMInt32TypeInContext(context.get_context());
                        Some(LLVMConstInt(ty, *val as u64, 0))
                    },
                    &Literals::U64Num(ref val) => {
                        let ty = LLVMInt64TypeInContext(context.get_context());
                        Some(LLVMConstInt(ty, *val, 0))
                    },
                    &Literals::F32Num(ref val) => {
                        let ty = LLVMFloatTypeInContext(context.get_context());
                        Some(LLVMConstReal(ty, *val as f64))
                    },
                    &Literals::F64Num(ref val) => {
                        let ty = LLVMDoubleTypeInContext(context.get_context());
                        Some(LLVMConstReal(ty, *val))
                    },
                    &Literals::Bool(ref val) => {
                        let ty = LLVMInt1TypeInContext(context.get_context());
                        Some(LLVMConstInt(ty, *val as u64, 0))
                    },
                    _ => {
                        println!("Error: Codegen unimplemented for {:?}", literal_type);
                        None
                    }
                }
            },
            Expr::FnCall(ref name, ref args) => {
                let function = LLVMGetNamedFunction(context.module, c_str_ptr(name));

                if function.is_null() {
                    // TODO: Add standardized error message writing?
                    println!("Error: Function {} not found.", name);
                    return None;
                }

                let arg_count = LLVMCountParams(function) as usize;

                if arg_count != args.len() {
                    println!("Error: Function {} requires {} argument(s), {} given.", name, arg_count, args.len());
                    return None;
                }

                let mut arg_values = Vec::new();

                for arg in args {
                    match arg.gen_code(context) {
                        Some(value) => arg_values.push(value),
                        None => {
                            println!("Fatal Error: Argument {:?} codegen failed!", arg);
                            return None
                        }
                    }
                }

                // Void functions don't get saved return values
                let ret_var = if LLVMGetReturnType(LLVMGetElementType(LLVMTypeOf(function))) == LLVMVoidTypeInContext(context.context) {
                    c_str_ptr("")
                } else {
                    let mut tmp = name.to_string();
                    tmp.push_str("tmp");

                    c_str_ptr(&tmp)
                };

                Some(LLVMBuildCall(context.builder, function, arg_values.as_ptr() as *mut _, arg_values.len() as u32, ret_var))
            },
            Expr::UnaryOp(ref op, ref expr) => {
                match *op {
                    // I think Negate would be easier to handle by replacing it with multiply * -1 in the parser
                    UnaryOp::Negate => panic!("Codegen error: Negation not supported in codegen."),
                    UnaryOp::Not => match expr.gen_code(context) {
                        Some(val) => Some(LLVMBuildNot(context.get_builder(), val, c_str_ptr("nottmp"))),
                        None => None
                    }
                }
            },
            Expr::NoOp => None,
            _ => None
        }
    }
}

// Helper function
pub fn c_str_ptr(rust_str: &str) -> *const i8 {
    match CString::new(rust_str.as_bytes()) {
        Ok(string) => string,
        Err(err) => panic!(err)
    }.as_ptr()
}
<commit_msg>Created a working print statement<commit_after>extern crate llvm_sys;

use std::collections::HashMap;
use std::ffi::{CStr, CString};
use std::str;
use self::llvm_sys::core::*;
use self::llvm_sys::analysis::*;
use self::llvm_sys::execution_engine::*;
use self::llvm_sys::prelude::*;
use self::llvm_sys::target::*;
//use self::llvm_sys::transforms::scalar::*;
use syntax::ast::op::*;
use syntax::ast::expr::*;
use syntax::ast::literals::*;

// Struct to keep track of data needed to build IR
pub struct Context {
    context: LLVMContextRef,
    module: LLVMModuleRef,
    builder: LLVMBuilderRef,
    execution_engine: LLVMExecutionEngineRef,
    fn_pass_manager: LLVMPassManagerRef,
    named_values: HashMap<String, LLVMValueRef>,
}

impl Context {
    pub unsafe fn new(module_name: &str) -> Context {
        LLVM_InitializeNativeTarget();
        LLVM_InitializeNativeAsmPrinter();
        LLVM_InitializeNativeAsmParser();
        LLVMLinkInInterpreter();

        let context = LLVMContextCreate();
        let module = LLVMModuleCreateWithNameInContext(c_str_ptr(module_name), context);
        let builder = LLVMCreateBuilderInContext(context);
        let named_values = HashMap::new();
        let mut execution_engine = 0 as LLVMExecutionEngineRef;
        let mut error_msg = 0 as *mut i8;

        if LLVMCreateExecutionEngineForModule(&mut execution_engine, module, &mut error_msg) == 1 {
            panic!("{}", str::from_utf8(CStr::from_ptr(error_msg).to_bytes()).unwrap())
        }

        let fn_pass_manager = LLVMCreateFunctionPassManagerForModule(module);
        let target_data = LLVMGetExecutionEngineTargetData(execution_engine);
        let data_layout = LLVMCopyStringRepOfTargetData(target_data);
        LLVMSetDataLayout(module, data_layout);
        LLVMAddTargetData(target_data, fn_pass_manager);
        LLVMDisposeMessage(data_layout);

        // Add desired passes to pass manager here
//        LLVMAddInstructionCombiningPass(fn_pass_manager);

        LLVMInitializeFunctionPassManager(fn_pass_manager);

        Context {
            context: context,
            module: module,
            builder: builder,
            execution_engine: execution_engine,
            fn_pass_manager: fn_pass_manager,
            named_values: named_values,
        }
    }

    // Dump the IR to stdout
    pub unsafe fn dump(&self) {
        LLVMDumpModule(self.module);
    }

    // Verifies that the llvm code is valid. Optionally prints
    // the error message, else abort.
    pub unsafe fn verify(&self) {
        let action = LLVMVerifierFailureAction::LLVMPrintMessageAction;
        let msg = vec![c_str_ptr("")];

        LLVMVerifyModule(self.module, action, msg.as_ptr() as *mut _);
        LLVMDisposeMessage(msg[0] as *mut _);
    }

    pub unsafe fn run(&self) -> u64 {
        let main = LLVMGetNamedFunction(self.module, c_str_ptr("main"));
        let result = LLVMRunFunction(self.execution_engine, main, 0, 0 as *mut LLVMGenericValueRef);

        LLVMGenericValueToInt(result, 1)
    }

    // Struct getters
    pub fn get_context(&self) -> LLVMContextRef {
        self.context
    }

    pub fn get_module(&self) -> LLVMModuleRef {
        self.module
    }

    pub fn get_builder(&self) -> LLVMBuilderRef {
        self.builder
    }

    pub fn clear_values(&mut self) {
        self.named_values.clear();
    }
}

impl Drop for Context {
    fn drop(&mut self) {
        unsafe {
            LLVMDisposePassManager(self.fn_pass_manager);
//            LLVMDisposeExecutionEngine(self.execution_engine); // signal 4s for some reason
            LLVMDisposeBuilder(self.builder);
            LLVMDisposeModule(self.module);
            LLVMContextDispose(self.context);
        }
    }
}

pub trait CodeGen {
    unsafe fn gen_code(&self, context: &mut Context) -> Option<LLVMValueRef>;
}

impl CodeGen for ExprWrapper {
    unsafe fn gen_code(&self, context: &mut Context) -> Option<LLVMValueRef> {
        self.get_expr().gen_code(context)
    }
}

impl CodeGen for Expr {
    unsafe fn gen_code(&self, context: &mut Context) -> Option<LLVMValueRef> {
        match *self {
            Expr::Block(ref vec) => {
                let mut gen = None;

                for expr in vec {
                    gen = expr.gen_code(context);
                }

                gen
            },
            Expr::Ident(ref name) => {
                match context.named_values.get(name) {
                    Some(val) => Some(*val),
                    None => {
                        println!("Error: Unknown variable named {}", name);

                        None
                    }
                }
            },
            Expr::Literal(ref literal_type) => {
                match literal_type {
                    &Literals::UTF8String(ref val) => {
                        // Types
                        let array_type1 = LLVMArrayType(LLVMInt8TypeInContext(context.get_context()), val.len() as u32);
                        // let string_struct_type = LLVMGetNamedGlobal(context.get_module(), c_str_ptr("struct.string"));
                        // if string_struct_type.is_null() {
                        //     println!("TMP Error: Cannot find builtin string type!");
                        // }
                        let i8_type = LLVMInt8TypeInContext(context.get_context());
                        let i8_ptr_type = LLVMPointerType(i8_type, 0);
                        let i32_type = LLVMInt32TypeInContext(context.get_context());
                        let i64_type = LLVMInt64TypeInContext(context.get_context());
                        // let string_type_fields = vec![LLVMInt32TypeInContext(context.get_context()),
                        //               LLVMPointerType(LLVMInt8TypeInContext(context.get_context()), 0)];

                        // Values
                        let len = LLVMConstInt(i64_type, val.len() as u64, 0);

                        // Make a global string constant and assign the value:
                        let const_str_var = LLVMAddGlobal(context.get_module(), array_type1, c_str_ptr("str"));
                        let mut chars = Vec::new();
                        for chr in val.bytes() {
                            chars.push(LLVMConstInt(i8_type, chr as u64, 0));
                        }

                        let const_str_array = LLVMConstArray(i8_type, chars.as_ptr() as *mut _, chars.len() as u32);
                        LLVMSetInitializer(const_str_var, const_str_array);
                        LLVMSetGlobalConstant(const_str_var, 1);

                        let args = vec![LLVMConstInt(i32_type, 0, 0), LLVMConstInt(i32_type, 0, 0)];
                        let allocated_str_ptr = LLVMBuildAlloca(context.get_builder(), i8_ptr_type, c_str_ptr("a"));
                        let mallocated_ptr = LLVMBuildMalloc(context.get_builder(), i8_type, c_str_ptr("m"));

                        LLVMSetTailCall(mallocated_ptr, 0);
                        LLVMBuildStore(context.get_builder(), mallocated_ptr, allocated_str_ptr);

                        let element_ptr = LLVMBuildGEP(context.get_builder(), const_str_var, args.as_ptr() as *mut _, 2, c_str_ptr(""));
                        LLVMBuildStore(context.get_builder(), element_ptr, allocated_str_ptr);

                        let loaded_ptr = LLVMBuildLoad(context.get_builder(), allocated_str_ptr, c_str_ptr("l"));
                        // String type generated here should eventually be preloaded type
                        let string_type_fields = vec![LLVMPointerType(i8_type, 0), i64_type];
                        let string_type = LLVMStructTypeInContext(context.get_context(), string_type_fields.as_ptr() as *mut _, 2, 0);
                        let undef = LLVMGetUndef(string_type);

                        let i = LLVMBuildInsertValue(context.get_builder(), undef, loaded_ptr, 0, c_str_ptr("i"));
                        Some(LLVMBuildInsertValue(context.get_builder(), i, len, 1, c_str_ptr("i")))
                    },
                    &Literals::I32Num(ref val) => {
                        let ty = LLVMInt32TypeInContext(context.get_context());
                        Some(LLVMConstInt(ty, *val as u64, 1))
                    },
                    &Literals::I64Num(ref val) => {
                        let ty = LLVMInt64TypeInContext(context.get_context());
                        Some(LLVMConstInt(ty, *val as u64, 1))
                    },
                    &Literals::U32Num(ref val) => {
                        let ty = LLVMInt32TypeInContext(context.get_context());
                        Some(LLVMConstInt(ty, *val as u64, 0))
                    },
                    &Literals::U64Num(ref val) => {
                        let ty = LLVMInt64TypeInContext(context.get_context());
                        Some(LLVMConstInt(ty, *val, 0))
                    },
                    &Literals::F32Num(ref val) => {
                        let ty = LLVMFloatTypeInContext(context.get_context());
                        Some(LLVMConstReal(ty, *val as f64))
                    },
                    &Literals::F64Num(ref val) => {
                        let ty = LLVMDoubleTypeInContext(context.get_context());
                        Some(LLVMConstReal(ty, *val))
                    },
                    &Literals::Bool(ref val) => {
                        let ty = LLVMInt1TypeInContext(context.get_context());
                        Some(LLVMConstInt(ty, *val as u64, 0))
                    },
                    _ => {
                        println!("Error: Codegen unimplemented for {:?}", literal_type);
                        None
                    }
                }
            },
            Expr::FnCall(ref name, ref args) => {
                let function = LLVMGetNamedFunction(context.module, c_str_ptr(name));

                if function.is_null() {
                    // TODO: Add standardized error message writing?
                    println!("Error: Function {} not found.", name);
                    return None;
                }

                let arg_count = LLVMCountParams(function) as usize;

                if arg_count != args.len() {
                    println!("Error: Function {} requires {} argument(s), {} given.", name, arg_count, args.len());
                    return None;
                }

                let mut arg_values = Vec::new();

                for arg in args {
                    match arg.gen_code(context) {
                        Some(value) => arg_values.push(value),
                        None => {
                            println!("Fatal Error: Argument {:?} codegen failed!", arg);
                            return None
                        }
                    }
                }

                // Void functions don't get saved return values
                let ret_var = if LLVMGetReturnType(LLVMGetElementType(LLVMTypeOf(function))) == LLVMVoidTypeInContext(context.context) {
                    c_str_ptr("")
                } else {
                    let mut tmp = name.to_string();
                    tmp.push_str("tmp");

                    c_str_ptr(&tmp)
                };

                Some(LLVMBuildCall(context.builder, function, arg_values.as_ptr() as *mut _, arg_values.len() as u32, ret_var))
            },
            Expr::UnaryOp(ref op, ref expr) => {
                match *op {
                    // I think Negate would be easier to handle by replacing it with multiply * -1 in the parser
                    UnaryOp::Negate => panic!("Codegen error: Negation not supported in codegen."),
                    UnaryOp::Not => match expr.gen_code(context) {
                        Some(val) => Some(LLVMBuildNot(context.get_builder(), val, c_str_ptr("nottmp"))),
                        None => None
                    }
                }
            },
            Expr::NoOp => None,
            _ => None
        }
    }
}

// Helper function
pub fn c_str_ptr(rust_str: &str) -> *const i8 {
    match CString::new(rust_str.as_bytes()) {
        Ok(string) => string,
        Err(err) => panic!(err)
    }.as_ptr()
}
<|endoftext|>


<commit_before>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use rustc::ty::TyCtxt;
use rustc::mir::repr::*;
use rustc::mir::transform::{MirPass, MirSource, Pass};
use rustc_data_structures::indexed_vec::Idx;
use rustc::ty::VariantKind;

pub struct Deaggregator;

impl Pass for Deaggregator {}

impl<'tcx> MirPass<'tcx> for Deaggregator {
    fn run_pass<'a>(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>,
                    source: MirSource, mir: &mut Mir<'tcx>) {
        let node_id = source.item_id();
        let node_path = tcx.item_path_str(tcx.map.local_def_id(node_id));
        debug!("running on: {:?}", node_path);
        // we only run when mir_opt_level > 1
        match tcx.sess.opts.debugging_opts.mir_opt_level {
            Some(0) |
            Some(1) |
            None => { return; },
            _ => {}
        };

        // Do not trigger on constants.  Could be revised in future
        if let MirSource::Fn(_) = source {} else { return; }

        let mut curr: usize = 0;
        for bb in mir.basic_blocks_mut() {
            let idx = match get_aggregate_statement(curr, &bb.statements) {
                Some(idx) => idx,
                None => continue,
            };
            // do the replacement
            debug!("removing statement {:?}", idx);
            let src_info = bb.statements[idx].source_info;
            let suffix_stmts = bb.statements.split_off(idx+1);
            let orig_stmt = bb.statements.pop().unwrap();
            let StatementKind::Assign(ref lhs, ref rhs) = orig_stmt.kind;
            let (agg_kind, operands) = match rhs {
                &Rvalue::Aggregate(ref agg_kind, ref operands) => (agg_kind, operands),
                _ => span_bug!(src_info.span, "expected aggregate, not {:?}", rhs),
            };
            let (adt_def, variant, substs) = match agg_kind {
                &AggregateKind::Adt(adt_def, variant, substs) => (adt_def, variant, substs),
                _ => span_bug!(src_info.span, "expected struct, not {:?}", rhs),
            };
            let n = bb.statements.len();
            bb.statements.reserve(n + operands.len() + suffix_stmts.len());
            for (i, op) in operands.iter().enumerate() {
                let ref variant_def = adt_def.variants[variant];
                let ty = variant_def.fields[i].ty(tcx, substs);
                let rhs = Rvalue::Use(op.clone());

                // since we don't handle enums, we don't need a cast
                let lhs_cast = lhs.clone();

                // FIXME we cannot deaggregate enums issue: #35186

                let lhs_proj = Lvalue::Projection(Box::new(LvalueProjection {
                    base: lhs_cast,
                    elem: ProjectionElem::Field(Field::new(i), ty),
                }));
                let new_statement = Statement {
                    source_info: src_info,
                    kind: StatementKind::Assign(lhs_proj, rhs),
                };
                debug!("inserting: {:?} @ {:?}", new_statement, idx + i);
                bb.statements.push(new_statement);
            }
            curr = bb.statements.len();
            bb.statements.extend(suffix_stmts);
        }
    }
}

fn get_aggregate_statement<'a, 'tcx, 'b>(curr: usize,
                                         statements: &Vec<Statement<'tcx>>)
                                         -> Option<usize> {
    for i in curr..statements.len() {
        let ref statement = statements[i];
        let StatementKind::Assign(_, ref rhs) = statement.kind;
        if let &Rvalue::Aggregate(ref kind, ref operands) = rhs {
            if let &AggregateKind::Adt(adt_def, variant, _) = kind {
                if operands.len() > 0 { // don't deaggregate ()
                    if adt_def.variants.len() > 1 {
                        // only deaggrate structs for now
                        continue;
                    }
                    debug!("getting variant {:?}", variant);
                    debug!("for adt_def {:?}", adt_def);
                    let variant_def = &adt_def.variants[variant];
                    if variant_def.kind == VariantKind::Struct {
                        return Some(i);
                    }
                }
            }
        }
    };
    None
}
<commit_msg>reduce rightward drift, add precondition comment<commit_after>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use rustc::ty::TyCtxt;
use rustc::mir::repr::*;
use rustc::mir::transform::{MirPass, MirSource, Pass};
use rustc_data_structures::indexed_vec::Idx;
use rustc::ty::VariantKind;

pub struct Deaggregator;

impl Pass for Deaggregator {}

impl<'tcx> MirPass<'tcx> for Deaggregator {
    fn run_pass<'a>(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>,
                    source: MirSource, mir: &mut Mir<'tcx>) {
        let node_id = source.item_id();
        let node_path = tcx.item_path_str(tcx.map.local_def_id(node_id));
        debug!("running on: {:?}", node_path);
        // we only run when mir_opt_level > 1
        match tcx.sess.opts.debugging_opts.mir_opt_level {
            Some(0) |
            Some(1) |
            None => { return; },
            _ => {}
        };

        // Do not trigger on constants.  Could be revised in future
        if let MirSource::Fn(_) = source {} else { return; }
        // In fact, we might not want to trigger in other cases.
        // Ex: when we could use SROA.  See issue #35259

        let mut curr: usize = 0;
        for bb in mir.basic_blocks_mut() {
            let idx = match get_aggregate_statement(curr, &bb.statements) {
                Some(idx) => idx,
                None => continue,
            };
            // do the replacement
            debug!("removing statement {:?}", idx);
            let src_info = bb.statements[idx].source_info;
            let suffix_stmts = bb.statements.split_off(idx+1);
            let orig_stmt = bb.statements.pop().unwrap();
            let StatementKind::Assign(ref lhs, ref rhs) = orig_stmt.kind;
            let (agg_kind, operands) = match rhs {
                &Rvalue::Aggregate(ref agg_kind, ref operands) => (agg_kind, operands),
                _ => span_bug!(src_info.span, "expected aggregate, not {:?}", rhs),
            };
            let (adt_def, variant, substs) = match agg_kind {
                &AggregateKind::Adt(adt_def, variant, substs) => (adt_def, variant, substs),
                _ => span_bug!(src_info.span, "expected struct, not {:?}", rhs),
            };
            let n = bb.statements.len();
            bb.statements.reserve(n + operands.len() + suffix_stmts.len());
            for (i, op) in operands.iter().enumerate() {
                let ref variant_def = adt_def.variants[variant];
                let ty = variant_def.fields[i].ty(tcx, substs);
                let rhs = Rvalue::Use(op.clone());

                // since we don't handle enums, we don't need a cast
                let lhs_cast = lhs.clone();

                // FIXME we cannot deaggregate enums issue: #35186

                let lhs_proj = Lvalue::Projection(Box::new(LvalueProjection {
                    base: lhs_cast,
                    elem: ProjectionElem::Field(Field::new(i), ty),
                }));
                let new_statement = Statement {
                    source_info: src_info,
                    kind: StatementKind::Assign(lhs_proj, rhs),
                };
                debug!("inserting: {:?} @ {:?}", new_statement, idx + i);
                bb.statements.push(new_statement);
            }
            curr = bb.statements.len();
            bb.statements.extend(suffix_stmts);
        }
    }
}

fn get_aggregate_statement<'a, 'tcx, 'b>(curr: usize,
                                         statements: &Vec<Statement<'tcx>>)
                                         -> Option<usize> {
    for i in curr..statements.len() {
        let ref statement = statements[i];
        let StatementKind::Assign(_, ref rhs) = statement.kind;
        let (kind, operands) = match rhs {
            &Rvalue::Aggregate(ref kind, ref operands) => (kind, operands),
            _ => continue,
        };
        let (adt_def, variant) = match kind {
            &AggregateKind::Adt(adt_def, variant, _) => (adt_def, variant),
            _ => continue,
        };
        if operands.len() == 0 || adt_def.variants.len() > 1 {
            // don't deaggregate ()
            // don't deaggregate enums ... for now
            continue;
        }
        debug!("getting variant {:?}", variant);
        debug!("for adt_def {:?}", adt_def);
        let variant_def = &adt_def.variants[variant];
        if variant_def.kind == VariantKind::Struct {
            return Some(i);
        }
    };
    None
}
<|endoftext|>


<commit_before>// Zinc, the bare metal stack for rust.
// Copyright 2014 Ben Gamari <bgamari@gmail.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::iter::FromIterator;
use std::ops::Deref;

use syntax::ast;
use syntax::ptr::P;
use syntax::ast_util::empty_generics;
use syntax::codemap::{respan, mk_sp};
use syntax::ext::base::ExtCtxt;
use syntax::ext::build::AstBuilder;
use syntax::ext::quote::rt::ToTokens;

use super::Builder;
use super::utils;
use super::super::node;

/// A visitor to build the struct for each register
pub struct BuildRegStructs<'a> {
  builder: &'a mut Builder,
  cx: &'a ExtCtxt<'a>,
}

impl<'a> node::RegVisitor for BuildRegStructs<'a> {
  fn visit_prim_reg(&mut self, path: &Vec<String>, reg: &node::Reg,
                    fields: &Vec<node::Field>) {
    let width = match reg.ty {
      node::RegType::RegPrim(ref width, _) => width.node,
      _ => panic!("visit_prim_reg called with non-primitive register"),
    };
    for field in fields.iter() {
      for item in build_field_type(self.cx, path, reg, field).into_iter() {
        self.builder.push_item(item);
      }
    }

    for item in build_reg_struct(self.cx, path, reg, &width).into_iter() {
      self.builder.push_item(item);
    }
  }
}

impl<'a> BuildRegStructs<'a> {
  pub fn new(builder: &'a mut Builder, cx: &'a ExtCtxt<'a>)
             -> BuildRegStructs<'a> {
    BuildRegStructs {builder: builder, cx: cx}
  }
}

/// Build a field type if necessary (e.g. in the case of an `EnumField`)
fn build_field_type(cx: &ExtCtxt, path: &Vec<String>,
                    reg: &node::Reg, field: &node::Field)
                    -> Vec<P<ast::Item>> {
  match field.ty.node {
    node::FieldType::EnumField { ref variants, .. } => {
      // FIXME(bgamari): We construct a path, then only take the last
      // segment, this could be more efficient
      let name: ast::Ident =
        utils::field_type_path(cx, path, reg, field)
        .segments.last().unwrap().identifier;
      let enum_def: ast::EnumDef = ast::EnumDef {
        variants: FromIterator::from_iter(
          variants.iter().map(|v| P(build_enum_variant(cx, v)))),
      };
      let mut attrs: Vec<ast::Attribute> = vec!(
        utils::list_attribute(cx, "derive",
                              vec!("PartialEq"),
                              field.name.span),
        utils::list_attribute(cx, "allow",
                              vec!("dead_code",
                                   "non_camel_case_types",
                                   "missing_docs"),
                              field.name.span));

      // setting the enum's repr to the width of the register
      // (unless there's only 1 variant; in which case we omit
      // the repr attribute due to E0083
      if variants.len() > 1 {
        let enum_repr = utils::reg_primitive_type_name(reg)
          .expect("Unexpected non-primitive reg");

        attrs.push(utils::list_attribute(cx, "repr",
                                         vec!(enum_repr),
                                         field.name.span));
      }

      let ty_item: P<ast::Item> = P(ast::Item {
        ident: name,
        id: ast::DUMMY_NODE_ID,
        node: ast::ItemEnum(enum_def, empty_generics()),
        vis: ast::Public,
        attrs: attrs,
        span: field.ty.span,
      });
      vec!(ty_item)
    },
    _ => Vec::new()
  }
}

/// Produce a register struct if necessary (for primitive typed registers).
/// In this case `None` indicates no struct is necessary, not failure.
/// For instance,
///
///     pub struct REG {_value: u32}
fn build_reg_struct(cx: &ExtCtxt, path: &Vec<String>,
    reg: &node::Reg, _width: &node::RegWidth) -> Vec<P<ast::Item>> {
  let packed_ty =
    utils::reg_primitive_type(cx, reg)
    .expect("Unexpected non-primitive reg");

  let reg_doc = match reg.docstring {
    Some(d) => d.node.name.to_string(),
    None => "no documentation".to_string(),
  };
  let docstring = format!("Register `{}`: {}",
                          reg.name.node,
                          reg_doc);
  let doc_attr = utils::doc_attribute(cx, utils::intern_string(cx, docstring));

  let ty_name = utils::path_ident(cx, path);
  let item = quote_item!(cx,
    $doc_attr
    #[derive(Clone)]
    #[allow(non_camel_case_types)]
    #[repr(C)]
    pub struct $ty_name {
      value: VolatileCell<$packed_ty>,
    }
  );
  let mut item: ast::Item = item.unwrap().deref().clone();
  item.span = reg.name.span;
  let copy_impl = quote_item!(cx, impl ::core::marker::Copy for $ty_name {}).unwrap();
  vec!(P(item), copy_impl)
}

/// Build a variant of an `EnumField`
fn build_enum_variant(cx: &ExtCtxt, variant: &node::Variant)
                      -> ast::Variant {
  let doc = match variant.docstring {
    Some(d) => d.node.name.to_string(),
    None => "no documentation".to_string(),
  };
  let docstring = format!("`0x{:x}`. {}",
                          variant.value.node,
                          doc);
  let doc_attr = utils::doc_attribute(cx, utils::intern_string(cx, docstring));
  respan(
    mk_sp(variant.name.span.lo, variant.value.span.hi),
    ast::Variant_ {
      name: cx.ident_of(variant.name.node.as_str()),
      attrs: vec!(doc_attr),
      kind: ast::TupleVariantKind(Vec::new()),
      id: ast::DUMMY_NODE_ID,
      disr_expr: Some(utils::expr_int(cx, respan(variant.value.span,
                                                 variant.value.node as i64))),
    }
  )
}
<commit_msg>Fixed ast::Variant_ layout<commit_after>// Zinc, the bare metal stack for rust.
// Copyright 2014 Ben Gamari <bgamari@gmail.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::iter::FromIterator;
use std::ops::Deref;

use syntax::ast;
use syntax::ptr::P;
use syntax::ast_util::empty_generics;
use syntax::codemap::{respan, mk_sp};
use syntax::ext::base::ExtCtxt;
use syntax::ext::build::AstBuilder;
use syntax::ext::quote::rt::ToTokens;

use super::Builder;
use super::utils;
use super::super::node;

/// A visitor to build the struct for each register
pub struct BuildRegStructs<'a> {
  builder: &'a mut Builder,
  cx: &'a ExtCtxt<'a>,
}

impl<'a> node::RegVisitor for BuildRegStructs<'a> {
  fn visit_prim_reg(&mut self, path: &Vec<String>, reg: &node::Reg,
                    fields: &Vec<node::Field>) {
    let width = match reg.ty {
      node::RegType::RegPrim(ref width, _) => width.node,
      _ => panic!("visit_prim_reg called with non-primitive register"),
    };
    for field in fields.iter() {
      for item in build_field_type(self.cx, path, reg, field).into_iter() {
        self.builder.push_item(item);
      }
    }

    for item in build_reg_struct(self.cx, path, reg, &width).into_iter() {
      self.builder.push_item(item);
    }
  }
}

impl<'a> BuildRegStructs<'a> {
  pub fn new(builder: &'a mut Builder, cx: &'a ExtCtxt<'a>)
             -> BuildRegStructs<'a> {
    BuildRegStructs {builder: builder, cx: cx}
  }
}

/// Build a field type if necessary (e.g. in the case of an `EnumField`)
fn build_field_type(cx: &ExtCtxt, path: &Vec<String>,
                    reg: &node::Reg, field: &node::Field)
                    -> Vec<P<ast::Item>> {
  match field.ty.node {
    node::FieldType::EnumField { ref variants, .. } => {
      // FIXME(bgamari): We construct a path, then only take the last
      // segment, this could be more efficient
      let name: ast::Ident =
        utils::field_type_path(cx, path, reg, field)
        .segments.last().unwrap().identifier;
      let enum_def: ast::EnumDef = ast::EnumDef {
        variants: FromIterator::from_iter(
          variants.iter().map(|v| P(build_enum_variant(cx, v)))),
      };
      let mut attrs: Vec<ast::Attribute> = vec!(
        utils::list_attribute(cx, "derive",
                              vec!("PartialEq"),
                              field.name.span),
        utils::list_attribute(cx, "allow",
                              vec!("dead_code",
                                   "non_camel_case_types",
                                   "missing_docs"),
                              field.name.span));

      // setting the enum's repr to the width of the register
      // (unless there's only 1 variant; in which case we omit
      // the repr attribute due to E0083
      if variants.len() > 1 {
        let enum_repr = utils::reg_primitive_type_name(reg)
          .expect("Unexpected non-primitive reg");

        attrs.push(utils::list_attribute(cx, "repr",
                                         vec!(enum_repr),
                                         field.name.span));
      }

      let ty_item: P<ast::Item> = P(ast::Item {
        ident: name,
        id: ast::DUMMY_NODE_ID,
        node: ast::ItemEnum(enum_def, empty_generics()),
        vis: ast::Public,
        attrs: attrs,
        span: field.ty.span,
      });
      vec!(ty_item)
    },
    _ => Vec::new()
  }
}

/// Produce a register struct if necessary (for primitive typed registers).
/// In this case `None` indicates no struct is necessary, not failure.
/// For instance,
///
///     pub struct REG {_value: u32}
fn build_reg_struct(cx: &ExtCtxt, path: &Vec<String>,
    reg: &node::Reg, _width: &node::RegWidth) -> Vec<P<ast::Item>> {
  let packed_ty =
    utils::reg_primitive_type(cx, reg)
    .expect("Unexpected non-primitive reg");

  let reg_doc = match reg.docstring {
    Some(d) => d.node.name.to_string(),
    None => "no documentation".to_string(),
  };
  let docstring = format!("Register `{}`: {}",
                          reg.name.node,
                          reg_doc);
  let doc_attr = utils::doc_attribute(cx, utils::intern_string(cx, docstring));

  let ty_name = utils::path_ident(cx, path);
  let item = quote_item!(cx,
    $doc_attr
    #[derive(Clone)]
    #[allow(non_camel_case_types)]
    #[repr(C)]
    pub struct $ty_name {
      value: VolatileCell<$packed_ty>,
    }
  );
  let mut item: ast::Item = item.unwrap().deref().clone();
  item.span = reg.name.span;
  let copy_impl = quote_item!(cx, impl ::core::marker::Copy for $ty_name {}).unwrap();
  vec!(P(item), copy_impl)
}

/// Build a variant of an `EnumField`
fn build_enum_variant(cx: &ExtCtxt, variant: &node::Variant)
                      -> ast::Variant {
  let doc = match variant.docstring {
    Some(d) => d.node.name.to_string(),
    None => "no documentation".to_string(),
  };
  let docstring = format!("`0x{:x}`. {}",
                          variant.value.node,
                          doc);
  let doc_attr = utils::doc_attribute(cx, utils::intern_string(cx, docstring));
  respan(
    mk_sp(variant.name.span.lo, variant.value.span.hi),
    ast::Variant_ {
      name: cx.ident_of(variant.name.node.as_str()),
      attrs: vec!(doc_attr),
      data: P(ast::VariantData::Tuple(Vec::new(), ast::DUMMY_NODE_ID)),
      disr_expr: Some(utils::expr_int(cx, respan(variant.value.span,
                                                 variant.value.node as i64))),
    }
  )
}
<|endoftext|>


<commit_before>use libc::{c_char, c_uint, c_void};
use CFixedString;

pub struct PDMenuHandle(pub u64);
pub struct PDMenuItem(pub u64);

#[repr(C)]
pub struct CMenuFuncs1 {
    pub private_data: *mut c_void,
    pub create_menu: fn(priv_data: *mut c_void, title: *const c_char) -> *mut c_void,
    pub destroy_menu: fn(priv_data: *mut c_void, handle: *mut c_void),
    pub add_sub_menu: fn(priv_data: *mut c_void,
                         name: *const c_char,
                         parent: u64,
                         child: u64),

    pub add_menu_item: fn(priv_data: *mut c_void,
                          menu: *mut c_void,
                          name: *const c_char,
                          id: c_uint,
                          key: c_uint,
                          modifier: c_uint)
                          -> PDMenuItem,
    pub remove_menu_item: fn(priv_data: *mut c_void, item: u64),
}

pub struct MenuFuncs {
    pub api: *mut CMenuFuncs1,
}

impl MenuFuncs {
    pub fn create_menu(&mut self, name: &str) -> *mut c_void {
        let mut title = CFixedString::from_str(name);
        unsafe {
            let data = (*self.api).private_data;
            ((*self.api).create_menu)(data, title.as_ptr())
        }
    }

    pub fn destroy_menu(&mut self, handle: *mut c_void) {
        unsafe {
            let data = (*self.api).private_data;
            ((*self.api).destroy_menu)(data, handle)
        }
    }

    pub fn add_menu_item(&mut self,
                         item: *mut c_void,
                         name: &str,
                         id: usize,
                         key: u32,
                         modifier: u32)
                         -> PDMenuItem {
        unsafe {
            let data = (*self.api).private_data;
            let mut title = CFixedString::from_str(name);
            ((*self.api).add_menu_item)(data, item, title.as_ptr(), id as c_uint, key, modifier)
        }
    }
}
<commit_msg>fix(cfixed_string): Remove unneeded muts<commit_after>use libc::{c_char, c_uint, c_void};
use CFixedString;

pub struct PDMenuHandle(pub u64);
pub struct PDMenuItem(pub u64);

#[repr(C)]
pub struct CMenuFuncs1 {
    pub private_data: *mut c_void,
    pub create_menu: fn(priv_data: *mut c_void, title: *const c_char) -> *mut c_void,
    pub destroy_menu: fn(priv_data: *mut c_void, handle: *mut c_void),
    pub add_sub_menu: fn(priv_data: *mut c_void,
                         name: *const c_char,
                         parent: u64,
                         child: u64),

    pub add_menu_item: fn(priv_data: *mut c_void,
                          menu: *mut c_void,
                          name: *const c_char,
                          id: c_uint,
                          key: c_uint,
                          modifier: c_uint)
                          -> PDMenuItem,
    pub remove_menu_item: fn(priv_data: *mut c_void, item: u64),
}

pub struct MenuFuncs {
    pub api: *mut CMenuFuncs1,
}

impl MenuFuncs {
    pub fn create_menu(&mut self, name: &str) -> *mut c_void {
        let title = CFixedString::from_str(name);
        unsafe {
            let data = (*self.api).private_data;
            ((*self.api).create_menu)(data, title.as_ptr())
        }
    }

    pub fn destroy_menu(&mut self, handle: *mut c_void) {
        unsafe {
            let data = (*self.api).private_data;
            ((*self.api).destroy_menu)(data, handle)
        }
    }

    pub fn add_menu_item(&mut self,
                         item: *mut c_void,
                         name: &str,
                         id: usize,
                         key: u32,
                         modifier: u32)
                         -> PDMenuItem {
        unsafe {
            let data = (*self.api).private_data;
            let title = CFixedString::from_str(name);
            ((*self.api).add_menu_item)(data, item, title.as_ptr(), id as c_uint, key, modifier)
        }
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate cfg_if;
extern crate gcc;

use std::cmp::Ordering;
use std::env;
use std::ffi::{OsStr, OsString};
use std::fs;
use std::iter;
use std::path::{Path, PathBuf};
use std::process::{self, Child, Command, Stdio};
use std::str;

cfg_if! {
    if #[cfg(feature = "v1_8_0")] {
        const TARGET_VERSION: &'static str = "1.8.0";
    } else if #[cfg(feature = "v1_7_1")] {
        const TARGET_VERSION: &'static str = "1.7.1";
    } else if #[cfg(feature = "v1_7_0")] {
        const TARGET_VERSION: &'static str = "1.7.0";
    } else if #[cfg(feature = "v1_6_0")] {
        const TARGET_VERSION: &'static str = "1.6.0";
    } else if #[cfg(feature = "v1_5_1")] {
        const TARGET_VERSION: &'static str = "1.5.1";
    } else if #[cfg(feature = "v1_5_0")] {
        const TARGET_VERSION: &'static str = "1.5.0";
    } else if #[cfg(feature = "v1_4_3")] {
        const TARGET_VERSION: &'static str = "1.4.3";
    } else if #[cfg(feature = "v1_4_2")] {
        const TARGET_VERSION: &'static str = "1.4.2";
    } else if #[cfg(feature = "v1_4_0")] {
        const TARGET_VERSION: &'static str = "1.4.0";
    } else if #[cfg(feature = "v1_3_1")] {
        const TARGET_VERSION: &'static str = "1.3.1";
    } else if #[cfg(feature = "v1_3_0")] {
        const TARGET_VERSION: &'static str = "1.3.0";
    } else {
        const TARGET_VERSION: &'static str = "1.2.0";
    }
}

fn main() {
    if let Ok(lib) = env::var("GPGME_LIB") {
        let mode = match env::var_os("GPGME_STATIC") {
            Some(_) => "static",
            _ => "dylib",
        };
        println!("cargo:rustc-link-lib={0}={1}", mode, lib);
        return;
    } else if let Some(path) = env::var_os("GPGME_CONFIG") {
        if !try_config(path) {
            process::exit(1);
        }
        return;
    }

    if !Path::new("libassuan/.git").exists() || !Path::new("gpgme/.git").exists() {
        run(Command::new("git").args(&["submodule", "update", "--init"]));
    }

    if try_build() || try_config("gpgme-config") {
        return;
    }
    process::exit(1);
}

fn try_config<S: AsRef<OsStr>>(path: S) -> bool {
    let path = path.as_ref();

    if let Some(output) = output(Command::new(&path).arg("--version")) {
        test_version(&output);
    } else {
        return false;
    }

    if let Some(output) = output(Command::new(&path).arg("--prefix")) {
        println!("cargo:root={}", output);
    }

    let mut command = Command::new(&path);
    if cfg!(unix) {
        command.arg("--thread=pthread");
    }
    command.arg("--libs");
    if let Some(output) = output(&mut command) {
        parse_config_output(&output);
        return true;
    }
    false
}

fn parse_config_output(output: &str) {
    let parts = output.split(|c: char| c.is_whitespace()).filter_map(|p| if p.len() > 2 {
        Some(p.split_at(2))
    } else {
        None
    });

    for (flag, val) in parts {
        match flag {
            "-L" => {
                println!("cargo:rustc-link-search=native={}", val);
            }
            "-F" => {
                println!("cargo:rustc-link-search=framework={}", val);
            }
            "-l" => {
                println!("cargo:rustc-link-lib={}", val);
            }
            _ => {}
        }
    }
}

fn try_build() -> bool {
    let src = PathBuf::from(env::current_dir().unwrap()).join("libassuan");
    let dst = env::var("OUT_DIR").unwrap();
    let build = PathBuf::from(&dst).join("build/assuan");
    let target = env::var("TARGET").unwrap();
    let host = env::var("HOST").unwrap();
    let gpgerror_root = env::var("DEP_GPG_ERROR_ROOT").unwrap();
    let compiler = gcc::Config::new().get_compiler();
    let cflags = compiler.args().iter().fold(OsString::new(), |mut c, a| {
        c.push(a);
        c.push(" ");
        c
    });

    let _ = fs::create_dir_all(&build);

    if !run(Command::new("sh").current_dir(&src).arg("autogen.sh")) {
        return false;
    }
    if !run(Command::new("sh")
        .current_dir(&build)
        .env("CC", compiler.path())
        .env("CFLAGS", &cflags)
        .arg(src.join("configure"))
        .args(&["--enable-maintainer-mode",
                "--build",
                &host, "--host", &target,
                "--enable-static",
                "--disable-shared",
                "--with-pic",
                &format!("--with-libgpg-error-prefix={}", &gpgerror_root),
                &format!("--prefix={}", &dst)])) {
        return false;
    }
    if !run(Command::new("make")
        .current_dir(&build)
        .arg("-j").arg(env::var("NUM_JOBS").unwrap())) {
        return false;
    }
    if !run(Command::new("make")
        .current_dir(&build)
        .arg("install")) {
        return false;
    }

    let src = PathBuf::from(env::current_dir().unwrap()).join("gpgme");
    let build = PathBuf::from(&dst).join("build/gpgme");
    let _ = fs::create_dir_all(&build);

    if !run(Command::new("sh").current_dir(&src).arg("autogen.sh")) {
        return false;
    }
    if !run(Command::new("sh")
        .current_dir(&build)
        .env("CC", compiler.path())
        .env("CFLAGS", &cflags)
        .arg(src.join("configure"))
        .args(&["--enable-maintainer-mode",
                "--build", &host,
                "--host", &target,
                "--enable-static",
                "--disable-shared",
                "--disable-languages",
                "--with-pic",
                &format!("--with-libgpg-error-prefix={}", &gpgerror_root),
                &format!("--with-libassuan-prefix={}", &dst),
                &format!("--prefix={}", &dst)])) {
        return false;
    }
    if !run(Command::new("make")
        .current_dir(&build)
        .arg("-j").arg(env::var("NUM_JOBS").unwrap())) {
        return false;
    }
    if !run(Command::new("make").current_dir(&build).arg("install")) {
        return false;
    }

    println!("cargo:rustc-link-search=native={}",
             PathBuf::from(dst.clone()).join("lib").display());
    println!("cargo:rustc-link-lib=static=assuan");
    println!("cargo:rustc-link-lib=static=gpgme");
    println!("cargo:root={}", &dst);
    true
}

fn test_version(version: &str) {
    let version = version.trim();
    for (x, y) in TARGET_VERSION.split('.').zip(version.split('.').chain(iter::repeat("0"))) {
        let (x, y): (u8, u8) = (x.parse().unwrap(), y.parse().unwrap());
        match x.cmp(&y) {
            Ordering::Less => break,
            Ordering::Greater => {
                panic!("GPGME version `{}` is less than requested `{}`",
                       version,
                       TARGET_VERSION)
            }
            _ => (),
        }
    }
}

fn spawn(cmd: &mut Command) -> Option<Child> {
    println!("running: {:?}", cmd);
    match cmd.stdin(Stdio::null()).spawn() {
        Ok(child) => Some(child),
        Err(e) => {
            println!("failed to execute command: {:?}\nerror: {}", cmd, e);
            None
        }
    }
}

fn run(cmd: &mut Command) -> bool {
    if let Some(mut child) = spawn(cmd) {
        match child.wait() {
            Ok(status) => {
                if !status.success() {
                    println!("command did not execute successfully: {:?}\n\
                       expected success, got: {}", cmd, status);
                } else {
                    return true;
                }
            }
            Err(e) => {
                println!("failed to execute command: {:?}\nerror: {}", cmd, e);
            }
        }
    }
    false
}

fn output(cmd: &mut Command) -> Option<String> {
    if let Some(child) = spawn(cmd.stdout(Stdio::piped())) {
        match child.wait_with_output() {
            Ok(output) => {
                if !output.status.success() {
                    println!("command did not execute successfully: {:?}\n\
                       expected success, got: {}", cmd, output.status);
                } else {
                    return String::from_utf8(output.stdout).ok();
                }
            }
            Err(e) => {
                println!("failed to execute command: {:?}\nerror: {}", cmd, e);
            }
        }
    }
    None
}
<commit_msg>Remove hardcoded pic flag<commit_after>#[macro_use]
extern crate cfg_if;
extern crate gcc;

use std::cmp::Ordering;
use std::env;
use std::ffi::{OsStr, OsString};
use std::fs;
use std::iter;
use std::path::{Path, PathBuf};
use std::process::{self, Child, Command, Stdio};
use std::str;

cfg_if! {
    if #[cfg(feature = "v1_8_0")] {
        const TARGET_VERSION: &'static str = "1.8.0";
    } else if #[cfg(feature = "v1_7_1")] {
        const TARGET_VERSION: &'static str = "1.7.1";
    } else if #[cfg(feature = "v1_7_0")] {
        const TARGET_VERSION: &'static str = "1.7.0";
    } else if #[cfg(feature = "v1_6_0")] {
        const TARGET_VERSION: &'static str = "1.6.0";
    } else if #[cfg(feature = "v1_5_1")] {
        const TARGET_VERSION: &'static str = "1.5.1";
    } else if #[cfg(feature = "v1_5_0")] {
        const TARGET_VERSION: &'static str = "1.5.0";
    } else if #[cfg(feature = "v1_4_3")] {
        const TARGET_VERSION: &'static str = "1.4.3";
    } else if #[cfg(feature = "v1_4_2")] {
        const TARGET_VERSION: &'static str = "1.4.2";
    } else if #[cfg(feature = "v1_4_0")] {
        const TARGET_VERSION: &'static str = "1.4.0";
    } else if #[cfg(feature = "v1_3_1")] {
        const TARGET_VERSION: &'static str = "1.3.1";
    } else if #[cfg(feature = "v1_3_0")] {
        const TARGET_VERSION: &'static str = "1.3.0";
    } else {
        const TARGET_VERSION: &'static str = "1.2.0";
    }
}

fn main() {
    if let Ok(lib) = env::var("GPGME_LIB") {
        let mode = match env::var_os("GPGME_STATIC") {
            Some(_) => "static",
            _ => "dylib",
        };
        println!("cargo:rustc-link-lib={0}={1}", mode, lib);
        return;
    } else if let Some(path) = env::var_os("GPGME_CONFIG") {
        if !try_config(path) {
            process::exit(1);
        }
        return;
    }

    if !Path::new("libassuan/.git").exists() || !Path::new("gpgme/.git").exists() {
        run(Command::new("git").args(&["submodule", "update", "--init"]));
    }

    if try_build() || try_config("gpgme-config") {
        return;
    }
    process::exit(1);
}

fn try_config<S: AsRef<OsStr>>(path: S) -> bool {
    let path = path.as_ref();

    if let Some(output) = output(Command::new(&path).arg("--version")) {
        test_version(&output);
    } else {
        return false;
    }

    if let Some(output) = output(Command::new(&path).arg("--prefix")) {
        println!("cargo:root={}", output);
    }

    let mut command = Command::new(&path);
    if cfg!(unix) {
        command.arg("--thread=pthread");
    }
    command.arg("--libs");
    if let Some(output) = output(&mut command) {
        parse_config_output(&output);
        return true;
    }
    false
}

fn parse_config_output(output: &str) {
    let parts = output.split(|c: char| c.is_whitespace()).filter_map(|p| if p.len() > 2 {
        Some(p.split_at(2))
    } else {
        None
    });

    for (flag, val) in parts {
        match flag {
            "-L" => {
                println!("cargo:rustc-link-search=native={}", val);
            }
            "-F" => {
                println!("cargo:rustc-link-search=framework={}", val);
            }
            "-l" => {
                println!("cargo:rustc-link-lib={}", val);
            }
            _ => {}
        }
    }
}

fn try_build() -> bool {
    let src = PathBuf::from(env::current_dir().unwrap()).join("libassuan");
    let dst = env::var("OUT_DIR").unwrap();
    let build = PathBuf::from(&dst).join("build/assuan");
    let target = env::var("TARGET").unwrap();
    let host = env::var("HOST").unwrap();
    let gpgerror_root = env::var("DEP_GPG_ERROR_ROOT").unwrap();
    let compiler = gcc::Config::new().get_compiler();
    let cflags = compiler.args().iter().fold(OsString::new(), |mut c, a| {
        c.push(a);
        c.push(" ");
        c
    });

    let _ = fs::create_dir_all(&build);

    if !run(Command::new("sh").current_dir(&src).arg("autogen.sh")) {
        return false;
    }
    if !run(Command::new("sh")
        .current_dir(&build)
        .env("CC", compiler.path())
        .env("CFLAGS", &cflags)
        .arg(src.join("configure"))
        .args(&["--enable-maintainer-mode",
                "--build",
                &host, "--host", &target,
                "--enable-static",
                "--disable-shared",
                &format!("--with-libgpg-error-prefix={}", &gpgerror_root),
                &format!("--prefix={}", &dst)])) {
        return false;
    }
    if !run(Command::new("make")
        .current_dir(&build)
        .arg("-j").arg(env::var("NUM_JOBS").unwrap())) {
        return false;
    }
    if !run(Command::new("make")
        .current_dir(&build)
        .arg("install")) {
        return false;
    }

    let src = PathBuf::from(env::current_dir().unwrap()).join("gpgme");
    let build = PathBuf::from(&dst).join("build/gpgme");
    let _ = fs::create_dir_all(&build);

    if !run(Command::new("sh").current_dir(&src).arg("autogen.sh")) {
        return false;
    }
    if !run(Command::new("sh")
        .current_dir(&build)
        .env("CC", compiler.path())
        .env("CFLAGS", &cflags)
        .arg(src.join("configure"))
        .args(&["--enable-maintainer-mode",
                "--build", &host,
                "--host", &target,
                "--enable-static",
                "--disable-shared",
                "--disable-languages",
                &format!("--with-libgpg-error-prefix={}", &gpgerror_root),
                &format!("--with-libassuan-prefix={}", &dst),
                &format!("--prefix={}", &dst)])) {
        return false;
    }
    if !run(Command::new("make")
        .current_dir(&build)
        .arg("-j").arg(env::var("NUM_JOBS").unwrap())) {
        return false;
    }
    if !run(Command::new("make").current_dir(&build).arg("install")) {
        return false;
    }

    println!("cargo:rustc-link-search=native={}",
             PathBuf::from(dst.clone()).join("lib").display());
    println!("cargo:rustc-link-lib=static=assuan");
    println!("cargo:rustc-link-lib=static=gpgme");
    println!("cargo:root={}", &dst);
    true
}

fn test_version(version: &str) {
    let version = version.trim();
    for (x, y) in TARGET_VERSION.split('.').zip(version.split('.').chain(iter::repeat("0"))) {
        let (x, y): (u8, u8) = (x.parse().unwrap(), y.parse().unwrap());
        match x.cmp(&y) {
            Ordering::Less => break,
            Ordering::Greater => {
                panic!("GPGME version `{}` is less than requested `{}`",
                       version,
                       TARGET_VERSION)
            }
            _ => (),
        }
    }
}

fn spawn(cmd: &mut Command) -> Option<Child> {
    println!("running: {:?}", cmd);
    match cmd.stdin(Stdio::null()).spawn() {
        Ok(child) => Some(child),
        Err(e) => {
            println!("failed to execute command: {:?}\nerror: {}", cmd, e);
            None
        }
    }
}

fn run(cmd: &mut Command) -> bool {
    if let Some(mut child) = spawn(cmd) {
        match child.wait() {
            Ok(status) => {
                if !status.success() {
                    println!("command did not execute successfully: {:?}\n\
                       expected success, got: {}", cmd, status);
                } else {
                    return true;
                }
            }
            Err(e) => {
                println!("failed to execute command: {:?}\nerror: {}", cmd, e);
            }
        }
    }
    false
}

fn output(cmd: &mut Command) -> Option<String> {
    if let Some(child) = spawn(cmd.stdout(Stdio::piped())) {
        match child.wait_with_output() {
            Ok(output) => {
                if !output.status.success() {
                    println!("command did not execute successfully: {:?}\n\
                       expected success, got: {}", cmd, output.status);
                } else {
                    return String::from_utf8(output.stdout).ok();
                }
            }
            Err(e) => {
                println!("failed to execute command: {:?}\nerror: {}", cmd, e);
            }
        }
    }
    None
}
<|endoftext|>


<commit_before>// Copyright 2014 Johannes Köster.
// Licensed under the MIT license (http://opensource.org/licenses/MIT)
// This file may not be copied, modified, or distributed
// except according to those terms.

#[cfg(feature = "serde")]
use bindgen;
use cc;
use fs_utils::copy::copy_directory;
use glob::glob;

use std::env;
use std::fs;
use std::io::Write;
use std::path::PathBuf;

// these need to be kept in sync with the htslib Makefile
const FILES: &[&str] = &[
    "kfunc.c",
    "knetfile.c",
    "kstring.c",
    "bcf_sr_sort.c",
    "bgzf.c",
    "errmod.c",
    "faidx.c",
    "header.c",
    "hfile.c",
    "hfile_net.c",
    "hts.c",
    "hts_os.c",
    "md5.c",
    "multipart.c",
    "probaln.c",
    "realn.c",
    "regidx.c",
    "region.c",
    "sam.c",
    "synced_bcf_reader.c",
    "vcf_sweep.c",
    "tbx.c",
    "textutils.c",
    "thread_pool.c",
    "vcf.c",
    "vcfutils.c",
    "cram/cram_codecs.c",
    "cram/cram_decode.c",
    "cram/cram_encode.c",
    "cram/cram_external.c",
    "cram/cram_index.c",
    "cram/cram_io.c",
    "cram/cram_samtools.c",
    "cram/cram_stats.c",
    "cram/mFILE.c",
    "cram/open_trace_file.c",
    "cram/pooled_alloc.c",
    "cram/rANS_static.c",
    "cram/string_alloc.c",
];

fn main() {
    let out = PathBuf::from(env::var("OUT_DIR").unwrap());
    let htslib_copy = out.join("htslib");

    if htslib_copy.exists() {
        std::fs::remove_dir_all(htslib_copy).unwrap();
    }
    copy_directory("htslib", &out).unwrap();

    let mut cfg = cc::Build::new();

    // default files
    let out_htslib = out.join("htslib");
    let htslib = PathBuf::from("htslib");
    for f in FILES {
        let c_file = out_htslib.join(f);
        cfg.file(&c_file);
        println!("cargo:rerun-if-changed={}", htslib.join(f).display());
    }

    cfg.include(out.join("htslib"));

    let want_static = cfg!(feature = "static") || env::var("HTS_STATIC").is_ok();

    if want_static {
        cfg.warnings(false).static_flag(true).pic(true);
    } else {
        cfg.warnings(false).static_flag(false).pic(true);
    }

    if let Ok(z_inc) = env::var("DEP_Z_INCLUDE") {
        cfg.include(z_inc);
    }

    // We build a config.h ourselves, rather than rely on Makefile or ./configure
    let mut config_lines = vec![
        "/* Default config.h generated by build.rs */",
        "#define HAVE_DRAND48 1",
    ];

    let use_bzip2 = env::var("CARGO_FEATURE_BZIP2").is_ok();
    if use_bzip2 {
        if let Ok(inc) = env::var("DEP_BZIP2_ROOT")
            .map(PathBuf::from)
            .map(|path| path.join("include"))
        {
            cfg.include(inc);
            config_lines.push("#define HAVE_LIBBZ2 1");
        }
    }

    let use_libdeflate = env::var("CARGO_FEATURE_LIBDEFLATE").is_ok();
    if use_libdeflate {
        if let Ok(inc) = env::var("DEP_LIBDEFLATE_INCLUDE").map(PathBuf::from) {
            cfg.include(inc);
            config_lines.push("#define HAVE_LIBDEFLATE 1");
        } else {
            panic!("no DEP_LIBDEFLATE_INCLUDE");
        }
    }

    let use_lzma = env::var("CARGO_FEATURE_LZMA").is_ok();
    if use_lzma {
        if let Ok(inc) = env::var("DEP_LZMA_INCLUDE").map(PathBuf::from) {
            cfg.include(inc);
            config_lines.push("#define HAVE_LIBBZ2 1");
            config_lines.push("#ifndef __APPLE__");
            config_lines.push("#define HAVE_LZMA_H 1");
            config_lines.push("#endif");
        }
    }

    let use_curl = env::var("CARGO_FEATURE_CURL").is_ok();
    if use_curl {
        if let Ok(inc) = env::var("DEP_CURL_INCLUDE").map(PathBuf::from) {
            cfg.include(inc);
            config_lines.push("#define HAVE_LIBCURL 1");
            cfg.file("htslib/hfile_libcurl.c");
            println!("cargo:rerun-if-changed=htslib/hfile_libcurl.c");

            #[cfg(target_os = "macos")]
            {
                // Use builtin MacOS CommonCrypto HMAC
                config_lines.push("#define HAVE_COMMONCRYPTO 1");
            }

            #[cfg(not(target_os = "macos"))]
            {
                if let Ok(inc) = env::var("DEP_OPENSSL_INCLUDE").map(PathBuf::from) {
                    // Must use hmac from libcrypto in openssl
                    cfg.include(inc);
                    config_lines.push("#define HAVE_HMAC 1");
                } else {
                    panic!("No OpenSSL dependency -- need OpenSSL includes");
                }
            }
        }
    }

    let use_gcs = env::var("CARGO_FEATURE_GCS").is_ok();
    if use_gcs {
        config_lines.push("#define ENABLE_GCS 1");
        cfg.file("htslib/hfile_gcs.c");
        println!("cargo:rerun-if-changed=htslib/hfile_gcs.c");
    }

    let use_s3 = env::var("CARGO_FEATURE_S3").is_ok();
    if use_s3 {
        config_lines.push("#define ENABLE_S3 1");
        cfg.file("htslib/hfile_s3.c");
        println!("cargo:rerun-if-changed=htslib/hfile_s3.c");
        cfg.file("htslib/hfile_s3_write.c");
        println!("cargo:rerun-if-changed=htslib/hfile_s3_write.c");
    }

    // write out config.h which controls the options htslib will use
    {
        let mut f = std::fs::File::create(out.join("htslib").join("config.h")).unwrap();
        for l in config_lines {
            writeln!(&mut f, "{}", l).unwrap();
        }
    }

    // write out version.h
    {
        let version = std::process::Command::new(out.join("htslib").join("version.sh"))
            .output()
            .expect("failed to execute process");
        let version_str = std::str::from_utf8(&version.stdout).unwrap().trim();

        let mut f = std::fs::File::create(out.join("htslib").join("version.h")).unwrap();
        writeln!(&mut f, "#define HTS_VERSION_TEXT \"{}\"", version_str).unwrap();
    }

    cfg.file("wrapper.c");
    cfg.compile("hts");

    // If bindgen is enabled, use it
    #[cfg(feature = "bindgen")]
    {
        bindgen::Builder::default()
            .header("wrapper.h")
            .generate_comments(false)
            .blacklist_function("strtold")
            .blacklist_type("max_align_t")
            .generate()
            .expect("Unable to generate bindings.")
            .write_to_file(out.join("bindings.rs"))
            .expect("Could not write bindings.");
    }

    // If no bindgen, use pre-built bindings
    #[cfg(all(not(feature = "bindgen"), target_os = "macos"))]
    {
        fs::copy("osx_prebuilt_bindings.rs", out.join("bindings.rs"))
            .expect("couldn't copy prebuilt bindings");
        println!("cargo:rerun-if-changed=osx_prebuilt_bindings.rs");
    }

    #[cfg(all(not(feature = "bindgen"), target_os = "linux"))]
    {
        fs::copy("linux_prebuilt_bindings.rs", out.join("bindings.rs"))
            .expect("couldn't copy prebuilt bindings");
        println!("cargo:rerun-if-changed=linux_prebuilt_bindings.rs");
    }

    let include = out.join("include");
    fs::create_dir_all(&include).unwrap();
    if include.join("htslib").exists() {
        fs::remove_dir_all(include.join("htslib")).expect("remove exist include dir");
    }
    copy_directory(out.join("htslib").join("htslib"), &include).unwrap();

    println!("cargo:root={}", out.display());
    println!("cargo:include={}", include.display());
    println!("cargo:libdir={}", out.display());
    println!("cargo:rerun-if-changed=wrapper.c");
    println!("cargo:rerun-if-changed=wrapper.h");

    let globs = std::iter::empty()
        .chain(glob("htslib/*.[h]").unwrap())
        .chain(glob("htslib/cram/*.[h]").unwrap())
        .chain(glob("htslib/htslib/*.h").unwrap())
        .chain(glob("htslib/os/*.[h]").unwrap())
        .filter_map(Result::ok);
    for htsfile in globs {
        println!("cargo:rerun-if-changed={}", htsfile.display());
    }

    // Note: config.h is a function of the cargo features. Any feature change will
    // cause build.rs to re-run, so don't re-run on that change.
    //println!("cargo:rerun-if-changed=htslib/config.h");
}
<commit_msg>fix: Configuration when cross-compiling. Even when cross-compiling, build.rs runs on the build host. Hence within build.rs `#[cfg(target_os)]` always reflects the host, not the target. Use $CARGO_CFG_TARGET_OS instead to query target properties. (#329)<commit_after>// Copyright 2014 Johannes Köster.
// Licensed under the MIT license (http://opensource.org/licenses/MIT)
// This file may not be copied, modified, or distributed
// except according to those terms.

#[cfg(feature = "serde")]
use bindgen;
use cc;
use fs_utils::copy::copy_directory;
use glob::glob;

use std::env;
use std::fs;
use std::io::Write;
use std::path::PathBuf;

// these need to be kept in sync with the htslib Makefile
const FILES: &[&str] = &[
    "kfunc.c",
    "knetfile.c",
    "kstring.c",
    "bcf_sr_sort.c",
    "bgzf.c",
    "errmod.c",
    "faidx.c",
    "header.c",
    "hfile.c",
    "hfile_net.c",
    "hts.c",
    "hts_os.c",
    "md5.c",
    "multipart.c",
    "probaln.c",
    "realn.c",
    "regidx.c",
    "region.c",
    "sam.c",
    "synced_bcf_reader.c",
    "vcf_sweep.c",
    "tbx.c",
    "textutils.c",
    "thread_pool.c",
    "vcf.c",
    "vcfutils.c",
    "cram/cram_codecs.c",
    "cram/cram_decode.c",
    "cram/cram_encode.c",
    "cram/cram_external.c",
    "cram/cram_index.c",
    "cram/cram_io.c",
    "cram/cram_samtools.c",
    "cram/cram_stats.c",
    "cram/mFILE.c",
    "cram/open_trace_file.c",
    "cram/pooled_alloc.c",
    "cram/rANS_static.c",
    "cram/string_alloc.c",
];

fn main() {
    let out = PathBuf::from(env::var("OUT_DIR").unwrap());
    let htslib_copy = out.join("htslib");

    if htslib_copy.exists() {
        std::fs::remove_dir_all(htslib_copy).unwrap();
    }
    copy_directory("htslib", &out).unwrap();

    // In build.rs cfg(target_os) does not give the target when cross-compiling;
    // instead use the environment variable supplied by cargo, which does.
    let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();

    let mut cfg = cc::Build::new();

    // default files
    let out_htslib = out.join("htslib");
    let htslib = PathBuf::from("htslib");
    for f in FILES {
        let c_file = out_htslib.join(f);
        cfg.file(&c_file);
        println!("cargo:rerun-if-changed={}", htslib.join(f).display());
    }

    cfg.include(out.join("htslib"));

    let want_static = cfg!(feature = "static") || env::var("HTS_STATIC").is_ok();

    if want_static {
        cfg.warnings(false).static_flag(true).pic(true);
    } else {
        cfg.warnings(false).static_flag(false).pic(true);
    }

    if let Ok(z_inc) = env::var("DEP_Z_INCLUDE") {
        cfg.include(z_inc);
    }

    // We build a config.h ourselves, rather than rely on Makefile or ./configure
    let mut config_lines = vec![
        "/* Default config.h generated by build.rs */",
        "#define HAVE_DRAND48 1",
    ];

    let use_bzip2 = env::var("CARGO_FEATURE_BZIP2").is_ok();
    if use_bzip2 {
        if let Ok(inc) = env::var("DEP_BZIP2_ROOT")
            .map(PathBuf::from)
            .map(|path| path.join("include"))
        {
            cfg.include(inc);
            config_lines.push("#define HAVE_LIBBZ2 1");
        }
    }

    let use_libdeflate = env::var("CARGO_FEATURE_LIBDEFLATE").is_ok();
    if use_libdeflate {
        if let Ok(inc) = env::var("DEP_LIBDEFLATE_INCLUDE").map(PathBuf::from) {
            cfg.include(inc);
            config_lines.push("#define HAVE_LIBDEFLATE 1");
        } else {
            panic!("no DEP_LIBDEFLATE_INCLUDE");
        }
    }

    let use_lzma = env::var("CARGO_FEATURE_LZMA").is_ok();
    if use_lzma {
        if let Ok(inc) = env::var("DEP_LZMA_INCLUDE").map(PathBuf::from) {
            cfg.include(inc);
            config_lines.push("#define HAVE_LIBBZ2 1");
            config_lines.push("#ifndef __APPLE__");
            config_lines.push("#define HAVE_LZMA_H 1");
            config_lines.push("#endif");
        }
    }

    let use_curl = env::var("CARGO_FEATURE_CURL").is_ok();
    if use_curl {
        if let Ok(inc) = env::var("DEP_CURL_INCLUDE").map(PathBuf::from) {
            cfg.include(inc);
            config_lines.push("#define HAVE_LIBCURL 1");
            cfg.file("htslib/hfile_libcurl.c");
            println!("cargo:rerun-if-changed=htslib/hfile_libcurl.c");

            if target_os == "macos" {
                // Use builtin MacOS CommonCrypto HMAC
                config_lines.push("#define HAVE_COMMONCRYPTO 1");
            } else {
                if let Ok(inc) = env::var("DEP_OPENSSL_INCLUDE").map(PathBuf::from) {
                    // Must use hmac from libcrypto in openssl
                    cfg.include(inc);
                    config_lines.push("#define HAVE_HMAC 1");
                } else {
                    panic!("No OpenSSL dependency -- need OpenSSL includes");
                }
            }
        }
    }

    let use_gcs = env::var("CARGO_FEATURE_GCS").is_ok();
    if use_gcs {
        config_lines.push("#define ENABLE_GCS 1");
        cfg.file("htslib/hfile_gcs.c");
        println!("cargo:rerun-if-changed=htslib/hfile_gcs.c");
    }

    let use_s3 = env::var("CARGO_FEATURE_S3").is_ok();
    if use_s3 {
        config_lines.push("#define ENABLE_S3 1");
        cfg.file("htslib/hfile_s3.c");
        println!("cargo:rerun-if-changed=htslib/hfile_s3.c");
        cfg.file("htslib/hfile_s3_write.c");
        println!("cargo:rerun-if-changed=htslib/hfile_s3_write.c");
    }

    // write out config.h which controls the options htslib will use
    {
        let mut f = std::fs::File::create(out.join("htslib").join("config.h")).unwrap();
        for l in config_lines {
            writeln!(&mut f, "{}", l).unwrap();
        }
    }

    // write out version.h
    {
        let version = std::process::Command::new(out.join("htslib").join("version.sh"))
            .output()
            .expect("failed to execute process");
        let version_str = std::str::from_utf8(&version.stdout).unwrap().trim();

        let mut f = std::fs::File::create(out.join("htslib").join("version.h")).unwrap();
        writeln!(&mut f, "#define HTS_VERSION_TEXT \"{}\"", version_str).unwrap();
    }

    cfg.file("wrapper.c");
    cfg.compile("hts");

    // If bindgen is enabled, use it
    #[cfg(feature = "bindgen")]
    {
        bindgen::Builder::default()
            .header("wrapper.h")
            .generate_comments(false)
            .blacklist_function("strtold")
            .blacklist_type("max_align_t")
            .generate()
            .expect("Unable to generate bindings.")
            .write_to_file(out.join("bindings.rs"))
            .expect("Could not write bindings.");
    }

    // If no bindgen, use pre-built bindings
    #[cfg(not(feature = "bindgen"))]
    if target_os == "macos" {
        fs::copy("osx_prebuilt_bindings.rs", out.join("bindings.rs"))
            .expect("couldn't copy prebuilt bindings");
        println!("cargo:rerun-if-changed=osx_prebuilt_bindings.rs");
    } else {
        fs::copy("linux_prebuilt_bindings.rs", out.join("bindings.rs"))
            .expect("couldn't copy prebuilt bindings");
        println!("cargo:rerun-if-changed=linux_prebuilt_bindings.rs");
    }

    let include = out.join("include");
    fs::create_dir_all(&include).unwrap();
    if include.join("htslib").exists() {
        fs::remove_dir_all(include.join("htslib")).expect("remove exist include dir");
    }
    copy_directory(out.join("htslib").join("htslib"), &include).unwrap();

    println!("cargo:root={}", out.display());
    println!("cargo:include={}", include.display());
    println!("cargo:libdir={}", out.display());
    println!("cargo:rerun-if-changed=wrapper.c");
    println!("cargo:rerun-if-changed=wrapper.h");

    let globs = std::iter::empty()
        .chain(glob("htslib/*.[h]").unwrap())
        .chain(glob("htslib/cram/*.[h]").unwrap())
        .chain(glob("htslib/htslib/*.h").unwrap())
        .chain(glob("htslib/os/*.[h]").unwrap())
        .filter_map(Result::ok);
    for htsfile in globs {
        println!("cargo:rerun-if-changed={}", htsfile.display());
    }

    // Note: config.h is a function of the cargo features. Any feature change will
    // cause build.rs to re-run, so don't re-run on that change.
    //println!("cargo:rerun-if-changed=htslib/config.h");
}
<|endoftext|>