mesalink 1.1.0-cratesio

MesaLink is a memory-safe and OpenSSL-compatible TLS library based on Rustls and Ring.
Documentation
/*
 *   __  __                 _     _       _
 *  |  \/  | ___  ___  __ _| |   (_)_ __ | | __
 *  | |\/| |/ _ \/ __|/ _` | |   | | '_ \| |/ /
 *  | |  | |  __/\__ \ (_| | |___| | | | |   <
 *  |_|  |_|\___||___/\__,_|_____|_|_| |_|_|\_\
 *
 * Copyright (c) 2017-2018, The MesaLink Authors.
 * All rights reserved.
 *
 * This work is licensed under the terms of the BSD 3-Clause License.
 * For a copy, see the LICENSE file.
 *
 */

use std::env;
use std::fs;
use std::io::prelude::*;
use std::path::PathBuf;
use walkdir::WalkDir;

#[cfg(unix)]
fn generate_la(lib: &str) -> std::io::Result<()> {
    use std::fs::File;

    let self_version = env!("CARGO_PKG_VERSION");
    let top_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
    let profile = env::var("PROFILE").unwrap();
    let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
    let target_dir = match env::var("CARGO_TARGET_SUBDIR") {
        Ok(dir) => format!("{}/target/{}", top_dir, dir),
        Err(_) => format!("{}/target/{}", top_dir, profile),
    };
    let libs_dir = format!("{}/.libs", target_dir);
    let libs_path = PathBuf::from(&libs_dir);
    let la_path = PathBuf::from(format!("{}/{}.la", target_dir, lib));
    let old_lib_path = PathBuf::from(format!("{}/{}.a", target_dir, lib));
    let new_lib_path = PathBuf::from(format!("{}/{}.a", libs_dir, lib));
    if !libs_path.exists() {
        fs::create_dir_all(&libs_path)?;
    }
    if la_path.exists() {
        fs::remove_file(&la_path)?;
    }
    if new_lib_path.exists() {
        fs::remove_file(&new_lib_path)?;
    }
    let mut file = File::create(&la_path)?;
    writeln!(file, "# {}.la - a libtool library file", lib)?;
    writeln!(file, "# Generated by libtool-rust {}", self_version)?;
    writeln!(file, "dlname=''")?;
    writeln!(file, "library_names=''")?;
    writeln!(file, "old_library='{}.a'", lib)?;
    if target_os == "macos" {
        writeln!(
            file,
            "inherited_linker_flags=' -lm -ldl -lresolv -framework Security'"
        )?;
    } else {
        writeln!(file, "inherited_linker_flags=' -pthread -lm -ldl'")?;
    }
    writeln!(file, "installed=no")?;
    writeln!(file, "shouldnotlink=no")?;
    std::os::unix::fs::symlink(&old_lib_path, &new_lib_path)?;
    Ok(())
}

#[cfg(windows)]
fn generate_la(_lib: &str) -> std::io::Result<()> {
    Ok(())
}

fn generate_headers() -> std::io::Result<()> {
    let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
    let include_dir = out_dir.join("include");

    println!("cargo:rerun-if-changed=mesalink");

    let header_files = WalkDir::new("mesalink")
        .into_iter()
        .filter_map(|e| e.ok())
        .filter(|e| e.file_type().is_file())
        .filter(|e| match e.path().extension() {
            Some(extension) => extension == "h",
            None => false,
        });

    for src in header_files {
        let dest = include_dir.join(src.path());
        fs::create_dir_all(dest.parent().unwrap())?;
        fs::copy(src.path(), dest)?;
    }

    let version_h = fs::read_to_string("mesalink/version.h.in")?;
    fs::write(
        include_dir.join("mesalink/version.h"),
        version_h.replace("@VERSION@", env::var("CARGO_PKG_VERSION").unwrap().as_str()),
    )?;

    let mut options_h = fs::File::create(include_dir.join("mesalink/options.h"))?;
    options_h.write_all(
        b"\
        #ifndef MESALINK_OPTIONS_H\n\
        #define MESALINK_OPTIONS_H\n\n\

        #ifdef __cplusplus\n\
        extern \"C\" {\n\
        #endif\n\n\
    ",
    )?;

    fn write_define(mut writer: impl Write, name: &str) -> std::io::Result<()> {
        write!(writer, "\n#undef {}\n#define {}\n", name, name)
    }

    if cfg!(feature = "client_apis") {
        write_define(&mut options_h, "HAVE_CLIENT")?;
    } else {
        write_define(&mut options_h, "NO_CLIENT")?;
    }

    if cfg!(feature = "server_apis") {
        write_define(&mut options_h, "HAVE_SERVER")?;
    } else {
        write_define(&mut options_h, "NO_SERVER")?;
    }

    if cfg!(feature = "error_strings") {
        write_define(&mut options_h, "HAVE_ERROR_STRINGS")?;
    } else {
        write_define(&mut options_h, "NO_ERROR_STRINGS")?;
    }

    if cfg!(feature = "aesgcm") {
        write_define(&mut options_h, "HAVE_AESGCM")?;
    } else {
        write_define(&mut options_h, "NO_AESGCM")?;
    }

    if cfg!(feature = "chachapoly") {
        write_define(&mut options_h, "HAVE_CHACHAPOLY")?;
    } else {
        write_define(&mut options_h, "NO_CHACHAPOLY")?;
    }

    if cfg!(feature = "tls13") {
        write_define(&mut options_h, "HAVE_TLS13")?;
    } else {
        write_define(&mut options_h, "NO_TLS13")?;
    }

    if cfg!(feature = "x25519") {
        write_define(&mut options_h, "HAVE_X25519")?;
    } else {
        write_define(&mut options_h, "NO_X25519")?;
    }

    if cfg!(feature = "ecdh") {
        write_define(&mut options_h, "HAVE_ECDH")?;
    } else {
        write_define(&mut options_h, "NO_ECDH")?;
    }

    if cfg!(feature = "ecdsa") {
        write_define(&mut options_h, "HAVE_ECDSA")?;
    } else {
        write_define(&mut options_h, "NO_ECDSA")?;
    }

    if cfg!(feature = "sgx") {
        write_define(&mut options_h, "HAVE_SGX")?;
    } else {
        write_define(&mut options_h, "NO_SGX")?;
    }

    options_h.write_all(
        b"\n\
        #ifdef __cplusplus\n\
        }\n\
        #endif\n\

        #endif /* MESALINK_OPTIONS_H */\n\
    ",
    )?;

    println!("cargo:include={}", include_dir.display());

    Ok(())
}

fn main() {
    let lib_name = format!("{}{}", "lib", std::env::var("CARGO_PKG_NAME").unwrap(),);
    let _ = generate_la(lib_name.as_str());

    generate_headers().unwrap();
}