1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
//! A build dependency for Cargo projects to find the location and compiler flags for linking
//! against `libpcap` using the `pcap-config` utility.
//!
//! This library uses the `pcap-config` utility, which is installed with libpcap, to find the
//! location and compiler flags required by cargo for linking to libpcap. A `Config` structure
//! provides a way to customize the options set for running `pcap-config`.
//!
//! If `pcap-config` runs successfully, the Cargo metadata is printed to stdout.
//!
//! # Examples
//!
//! Using `pcap-config` with the default options:
//!
//! ```no_run
//! extern crate pcap_config;
//!
//! fn main() {
//!     pcap_config::find_library().unwrap();
//! }
//! ```
//!
//! Customizing how `pcap-config` reports the metadata:
//!
//! ```no_run
//! extern crate pcap_config;
//!
//! fn main() {
//!     pcap_config::Config::new().statik(true).find().unwrap();
//! }
//! ```

#![deny(missing_docs)]

use std::process::Command;

/// Run `pcap-config` with default options.
pub fn find_library() -> Result<(), String> {
    Config::new().find()
}

/// A container for `pcap-config` configuration options.
pub struct Config {
    statik: Option<bool>,
}

impl Config {
    /// Creates a new configuration for running `pcap-config` with the default options.
    pub fn new() -> Config {
        Config { statik: None }
    }

    /// Indicate whether the `--static` flag should be passed to `pcap-config`.
    pub fn statik(&mut self, statik: bool) -> &mut Config {
        self.statik = Some(statik);
        self
    }

    /// Run `pcap-config` to look up compiler flags for libpcap.
    pub fn find(&self) -> Result<(), String> {
        let output = try!(self.command(&["--libs", "--cflags"])
                              .output()
                              .map_err(|e| format!("failed to run `pcap-config`: {}", e)));

        let stdout = try!(String::from_utf8(output.stdout).map_err(|e| {
            format!("unable to convert `pcap-config` output: {}", e)
        }));

        let tokens = stdout.split(' ')
                           .filter(|l| l.len() > 2)
                           .map(|t| (&t[0..2], &t[2..]))
                           .collect::<Vec<_>>();
        for &(flag, value) in tokens.iter() {
            match flag {
                "-L" => println!("cargo:rustc-link-search=native={}", value),
                "-l" => {
                    if self.is_static() {
                        println!("cargo:rustc-link-lib=static={}", value);
                    } else {
                        println!("cargo:rustc-link-lib={}", value);
                    }
                }
                _ => {}
            }
        }

        Ok(())
    }

    fn is_static(&self) -> bool {
        self.statik.unwrap_or_else(|| false)
    }

    fn command(&self, args: &[&str]) -> Command {
        let mut cmd = Command::new("pcap-config");

        if self.is_static() {
            cmd.arg("--static");
        }
        cmd.args(args);

        cmd
    }
}