<commit_before>extern crate llvm_tools;
extern crate target_build_utils;
extern crate walkdir;

use std::env;
use std::ffi::OsStr;
use std::ffi::OsString;
use std::fs::File;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::process;
use std::process::Command;
use target_build_utils::TargetInfo;
use walkdir::{DirEntry, WalkDir};

fn build_hermit(src_dir: &Path, target_dir_opt: Option<&Path>) {
	assert!(
		src_dir.exists(),
		"rusty_hermit source folder does not exist"
	);
	let target = TargetInfo::new().expect("Could not get target info");
	let profile = env::var("PROFILE").expect("PROFILE was not set");
	let mut cmd = Command::new(env!("CARGO"));

	cmd.env("CARGO_TERM_COLOR", "always");

	if target.target_arch() == "x86_64" {
		cmd.current_dir(src_dir)
			.arg("build")
			.arg("-Z")
			.arg("build-std=core,alloc")
			.arg("--target")
			.arg("x86_64-unknown-none-hermitkernel");
	} else if target.target_arch() == "aarch64" {
		cmd.current_dir(src_dir)
			.arg("build")
			.arg("-Z")
			.arg("build-std=core,alloc")
			.arg("--target")
			.arg("aarch64-unknown-hermit");
	} else {
		panic!("Try to build for an unsupported platform");
	}

	if let Some(target_dir) = target_dir_opt {
		cmd.arg("--target-dir").arg(target_dir);
	}
	let target_dir = match target_dir_opt {
		Some(target_dir) => target_dir.to_path_buf(),
		None => src_dir.join("target"),
	};

	if profile == "release" {
		cmd.arg("--release");
	}

	// disable all default features
	cmd.arg("--no-default-features");

	if target.target_arch() == "aarch64" {
		cmd.arg("--features");
		cmd.arg("aarch64-qemu-stdout");
	}

	// do we have to enable PCI support?
	#[cfg(feature = "pci")]
	{
		cmd.arg("--features");
		cmd.arg("pci");
	}

	// do we have to enable acpi support?
	#[cfg(feature = "acpi")]
	{
		cmd.arg("--features");
		cmd.arg("acpi");
	}

	// do we have to enable FSGSBASE support?
	#[cfg(feature = "fsgs_base")]
	{
		cmd.arg("--features");
		cmd.arg("fsgs_base");
	}

	// do we support multi-processor systems?
	#[cfg(feature = "smp")]
	{
		cmd.arg("--features");
		cmd.arg("smp");
	}

	// do we have to enable VGA support
	#[cfg(feature = "vga")]
	{
		cmd.arg("--features");
		cmd.arg("vga");
	}

	let mut rustflags = vec!["-Zmutable-noalias=no".to_string()];
	let outer_rustflags = env::var("CARGO_ENCODED_RUSTFLAGS").unwrap();

	#[cfg(feature = "instrument")]
	{
		rustflags.push("-Zinstrument-mcount".to_string());
		// Add outer rustflags to command
		rustflags.push(outer_rustflags);
	}

	#[cfg(not(feature = "instrument"))]
	{
		// If the `instrument` feature feature is not enabled,
		// filter it from outer rustflags before adding them to the command.
		if !outer_rustflags.is_empty() {
			let flags = outer_rustflags
				.split('\x1f')
				.filter(|&flag| !flag.contains("instrument-mcount"))
				.map(String::from);
			rustflags.extend(flags);
		}
	}

	cmd.env("CARGO_ENCODED_RUSTFLAGS", rustflags.join("\x1f"));

	let status = cmd.status().expect("failed to start kernel build");
	assert!(status.success());

	let lib_location = if target.target_arch() == "x86_64" {
		target_dir
			.join("x86_64-unknown-none-hermitkernel")
			.join(&profile)
			.canonicalize()
			.unwrap() // Must exist after building
	} else if target.target_arch() == "aarch64" {
		target_dir
			.join("aarch64-unknown-hermit")
			.join(&profile)
			.canonicalize()
			.unwrap() // Must exist after building
	} else {
		panic!("Try to build for an unsupported platform");
	};
	println!("Lib location: {}", lib_location.display());

	let lib = lib_location.join("libhermit.a");

	rename_symbol("rust_begin_unwind", &lib);
	rename_symbol("rust_oom", &lib);

	#[cfg(feature = "mem")]
	{
		for symbol in ["memcpy", "memmove", "memset", "memcmp", "bcmp"] {
			rename_symbol(symbol, &lib);
		}
	}

	println!("cargo:rustc-link-search=native={}", lib_location.display());
	println!("cargo:rustc-link-lib=static=hermit");

	// HERMIT_LOG_LEVEL_FILTER sets the log level filter at compile time
	println!("cargo:rerun-if-env-changed=HERMIT_LOG_LEVEL_FILTER");
}

/// Kernel and user space has its own versions of panic handler, oom handler, memcpy, memset, etc,
/// Consequently, we rename the functions in the libos to avoid collisions.
/// In addition, it provides us the offer to create a optimized version of memcpy
/// in user space.
fn rename_symbol(symbol: impl AsRef<OsStr>, lib: impl AsRef<Path>) {
	// Get access to llvm tools shipped in the llvm-tools-preview rustup component
	let llvm_tools = match llvm_tools::LlvmTools::new() {
		Ok(tools) => tools,
		Err(llvm_tools::Error::NotFound) => {
			eprintln!("Error: llvm-tools not found");
			eprintln!("Maybe the rustup component `llvm-tools-preview` is missing?");
			eprintln!("  Install it through: `rustup component add llvm-tools-preview`");
			process::exit(1);
		}
		Err(err) => {
			eprintln!("Failed to retrieve llvm-tools component: {:?}", err);
			process::exit(1);
		}
	};

	// Retrieve path of llvm-objcopy
	let llvm_objcopy = llvm_tools
		.tool(&llvm_tools::exe("llvm-objcopy"))
		.expect("llvm-objcopy not found in llvm-tools");

	// Rename symbols
	let arg = IntoIterator::into_iter([symbol.as_ref(), "=kernel-".as_ref(), symbol.as_ref()])
		.collect::<OsString>();
	let status = Command::new(llvm_objcopy)
		.arg("--redefine-sym")
		.arg(arg)
		.arg(lib.as_ref())
		.status()
		.expect("failed to execute llvm-objcopy");
	assert!(status.success(), "llvm-objcopy was not successful");
}

#[cfg(all(not(feature = "rustc-dep-of-std"), not(feature = "with_submodule")))]
fn build() {
	#[cfg(windows)]
	let out_dir = env::temp_dir();
	#[cfg(not(windows))]
	let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
	let src_dir = out_dir.join("rusty-hermit");

	if !src_dir.as_path().exists() {
		let status = Command::new(env!("CARGO"))
			.current_dir(out_dir)
			.arg("download")
			.arg("--output")
			.arg(src_dir.clone().into_os_string())
			.arg("--extract")
			.arg("rusty-hermit")
			.status()
			.expect("failed to start kernel download");
		assert!(
			status.success(),
			"Unable to download rusty-hermit. Is cargo-download installed?"
		);
	}

	build_hermit(src_dir.as_ref(), None);
}

#[cfg(all(not(feature = "rustc-dep-of-std"), feature = "with_submodule"))]
fn build() {
	let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
	let target_dir = out_dir.join("target");
	let src_dir = env::current_dir()
		.unwrap()
		.parent()
		.unwrap()
		.join("libhermit-rs");

	build_hermit(src_dir.as_ref(), Some(target_dir.as_ref()));
	configure_cargo_rerun_if_changed(src_dir.as_ref());
}

#[cfg(all(not(feature = "rustc-dep-of-std"), feature = "with_submodule"))]
fn configure_cargo_rerun_if_changed(src_dir: &Path) {
	fn is_not_ignored(entry: &DirEntry) -> bool {
		// Ignore .git .vscode and target directories, but not .cargo or .github
		if entry.depth() == 1
			&& entry.path().is_dir()
			&& (entry.path().ends_with("target")
				|| entry.path().ends_with(".git")
				|| entry.path().ends_with(".vscode"))
		{
			return false;
		}
		true
	}

	WalkDir::new(src_dir)
		.into_iter()
		.filter_entry(|e| is_not_ignored(e))
		.filter_map(|v| v.ok())
		.filter_map(|v| v.path().canonicalize().ok())
		.for_each(|x| println!("cargo:rerun-if-changed={}", x.display()));
}

#[cfg(not(feature = "rustc-dep-of-std"))]
fn create_constants() {
	let out_dir = env::var("OUT_DIR").expect("No out dir");
	let dest_path = Path::new(&out_dir).join("constants.rs");
	let mut f = File::create(&dest_path).expect("Could not create file");

	let ip = option_env!("HERMIT_IP");
	let ip = ip.map_or("10.0.5.3", |v| v);

	let gateway = option_env!("HERMIT_GATEWAY");
	let gateway = gateway.map_or("10.0.5.1", |v| v);

	let mask = option_env!("HERMIT_MASK");
	let mask = mask.map_or("255.255.255.0", |v| v);

	writeln!(&mut f, "const HERMIT_IP: &str = \"{}\";", ip).expect("Could not write file");
	println!("cargo:rerun-if-env-changed=HERMIT_IP");

	writeln!(&mut f, "const HERMIT_GATEWAY: &str = \"{}\";", gateway)
		.expect("Could not write file");
	println!("cargo:rerun-if-env-changed=HERMIT_GATEWAY");

	writeln!(&mut f, "const HERMIT_MASK: &str = \"{}\";", mask).expect("Could not write file");
	println!("cargo:rerun-if-env-changed=HERMIT_MASK");
}

fn main() {
	#[cfg(not(feature = "rustc-dep-of-std"))]
	create_constants();
	#[cfg(not(feature = "rustc-dep-of-std"))]
	build();
}
<commit_msg>remove clippy warnings<commit_after>extern crate llvm_tools;
extern crate target_build_utils;
extern crate walkdir;

use std::env;
use std::ffi::OsStr;
use std::ffi::OsString;
use std::fs::File;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::process;
use std::process::Command;
use target_build_utils::TargetInfo;
use walkdir::{DirEntry, WalkDir};

fn build_hermit(src_dir: &Path, target_dir_opt: Option<&Path>) {
	assert!(
		src_dir.exists(),
		"rusty_hermit source folder does not exist"
	);
	let target = TargetInfo::new().expect("Could not get target info");
	let profile = env::var("PROFILE").expect("PROFILE was not set");
	let mut cmd = Command::new(env!("CARGO"));

	cmd.env("CARGO_TERM_COLOR", "always");

	if target.target_arch() == "x86_64" {
		cmd.current_dir(src_dir)
			.arg("build")
			.arg("-Z")
			.arg("build-std=core,alloc")
			.arg("--target")
			.arg("x86_64-unknown-none-hermitkernel");
	} else if target.target_arch() == "aarch64" {
		cmd.current_dir(src_dir)
			.arg("build")
			.arg("-Z")
			.arg("build-std=core,alloc")
			.arg("--target")
			.arg("aarch64-unknown-hermit");
	} else {
		panic!("Try to build for an unsupported platform");
	}

	if let Some(target_dir) = target_dir_opt {
		cmd.arg("--target-dir").arg(target_dir);
	}
	let target_dir = match target_dir_opt {
		Some(target_dir) => target_dir.to_path_buf(),
		None => src_dir.join("target"),
	};

	if profile == "release" {
		cmd.arg("--release");
	}

	// disable all default features
	cmd.arg("--no-default-features");

	if target.target_arch() == "aarch64" {
		cmd.arg("--features");
		cmd.arg("aarch64-qemu-stdout");
	}

	// do we have to enable PCI support?
	#[cfg(feature = "pci")]
	{
		cmd.arg("--features");
		cmd.arg("pci");
	}

	// do we have to enable acpi support?
	#[cfg(feature = "acpi")]
	{
		cmd.arg("--features");
		cmd.arg("acpi");
	}

	// do we have to enable FSGSBASE support?
	#[cfg(feature = "fsgs_base")]
	{
		cmd.arg("--features");
		cmd.arg("fsgs_base");
	}

	// do we support multi-processor systems?
	#[cfg(feature = "smp")]
	{
		cmd.arg("--features");
		cmd.arg("smp");
	}

	// do we have to enable VGA support
	#[cfg(feature = "vga")]
	{
		cmd.arg("--features");
		cmd.arg("vga");
	}

	let mut rustflags = vec!["-Zmutable-noalias=no".to_string()];
	let outer_rustflags = env::var("CARGO_ENCODED_RUSTFLAGS").unwrap();

	#[cfg(feature = "instrument")]
	{
		rustflags.push("-Zinstrument-mcount".to_string());
		// Add outer rustflags to command
		rustflags.push(outer_rustflags);
	}

	#[cfg(not(feature = "instrument"))]
	{
		// If the `instrument` feature feature is not enabled,
		// filter it from outer rustflags before adding them to the command.
		if !outer_rustflags.is_empty() {
			let flags = outer_rustflags
				.split('\x1f')
				.filter(|&flag| !flag.contains("instrument-mcount"))
				.map(String::from);
			rustflags.extend(flags);
		}
	}

	cmd.env("CARGO_ENCODED_RUSTFLAGS", rustflags.join("\x1f"));

	let status = cmd.status().expect("failed to start kernel build");
	assert!(status.success());

	let lib_location = if target.target_arch() == "x86_64" {
		target_dir
			.join("x86_64-unknown-none-hermitkernel")
			.join(&profile)
			.canonicalize()
			.unwrap() // Must exist after building
	} else if target.target_arch() == "aarch64" {
		target_dir
			.join("aarch64-unknown-hermit")
			.join(&profile)
			.canonicalize()
			.unwrap() // Must exist after building
	} else {
		panic!("Try to build for an unsupported platform");
	};
	println!("Lib location: {}", lib_location.display());

	let lib = lib_location.join("libhermit.a");

	rename_symbol("rust_begin_unwind", &lib);
	rename_symbol("rust_oom", &lib);

	#[cfg(feature = "mem")]
	{
		for symbol in ["memcpy", "memmove", "memset", "memcmp", "bcmp"] {
			rename_symbol(symbol, &lib);
		}
	}

	println!("cargo:rustc-link-search=native={}", lib_location.display());
	println!("cargo:rustc-link-lib=static=hermit");

	// HERMIT_LOG_LEVEL_FILTER sets the log level filter at compile time
	println!("cargo:rerun-if-env-changed=HERMIT_LOG_LEVEL_FILTER");
}

/// Kernel and user space has its own versions of panic handler, oom handler, memcpy, memset, etc,
/// Consequently, we rename the functions in the libos to avoid collisions.
/// In addition, it provides us the offer to create a optimized version of memcpy
/// in user space.
fn rename_symbol(symbol: impl AsRef<OsStr>, lib: impl AsRef<Path>) {
	// Get access to llvm tools shipped in the llvm-tools-preview rustup component
	let llvm_tools = match llvm_tools::LlvmTools::new() {
		Ok(tools) => tools,
		Err(llvm_tools::Error::NotFound) => {
			eprintln!("Error: llvm-tools not found");
			eprintln!("Maybe the rustup component `llvm-tools-preview` is missing?");
			eprintln!("  Install it through: `rustup component add llvm-tools-preview`");
			process::exit(1);
		}
		Err(err) => {
			eprintln!("Failed to retrieve llvm-tools component: {:?}", err);
			process::exit(1);
		}
	};

	// Retrieve path of llvm-objcopy
	let llvm_objcopy = llvm_tools
		.tool(&llvm_tools::exe("llvm-objcopy"))
		.expect("llvm-objcopy not found in llvm-tools");

	// Rename symbols
	let arg = IntoIterator::into_iter([symbol.as_ref(), "=kernel-".as_ref(), symbol.as_ref()])
		.collect::<OsString>();
	let status = Command::new(llvm_objcopy)
		.arg("--redefine-sym")
		.arg(arg)
		.arg(lib.as_ref())
		.status()
		.expect("failed to execute llvm-objcopy");
	assert!(status.success(), "llvm-objcopy was not successful");
}

#[cfg(all(not(feature = "rustc-dep-of-std"), not(feature = "with_submodule")))]
fn build() {
	#[cfg(windows)]
	let out_dir = env::temp_dir();
	#[cfg(not(windows))]
	let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
	let src_dir = out_dir.join("rusty-hermit");

	if !src_dir.as_path().exists() {
		let status = Command::new(env!("CARGO"))
			.current_dir(out_dir)
			.arg("download")
			.arg("--output")
			.arg(src_dir.clone().into_os_string())
			.arg("--extract")
			.arg("rusty-hermit")
			.status()
			.expect("failed to start kernel download");
		assert!(
			status.success(),
			"Unable to download rusty-hermit. Is cargo-download installed?"
		);
	}

	build_hermit(src_dir.as_ref(), None);
}

#[cfg(all(not(feature = "rustc-dep-of-std"), feature = "with_submodule"))]
fn build() {
	let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
	let target_dir = out_dir.join("target");
	let src_dir = env::current_dir()
		.unwrap()
		.parent()
		.unwrap()
		.join("libhermit-rs");

	build_hermit(src_dir.as_ref(), Some(target_dir.as_ref()));
	configure_cargo_rerun_if_changed(src_dir.as_ref());
}

#[cfg(all(not(feature = "rustc-dep-of-std"), feature = "with_submodule"))]
fn configure_cargo_rerun_if_changed(src_dir: &Path) {
	fn is_not_ignored(entry: &DirEntry) -> bool {
		// Ignore .git .vscode and target directories, but not .cargo or .github
		if entry.depth() == 1
			&& entry.path().is_dir()
			&& (entry.path().ends_with("target")
				|| entry.path().ends_with(".git")
				|| entry.path().ends_with(".vscode"))
		{
			return false;
		}
		true
	}

	WalkDir::new(src_dir)
		.into_iter()
		.filter_entry(is_not_ignored)
		.filter_map(|v| v.ok())
		.filter_map(|v| v.path().canonicalize().ok())
		.for_each(|x| println!("cargo:rerun-if-changed={}", x.display()));
}

#[cfg(not(feature = "rustc-dep-of-std"))]
fn create_constants() {
	let out_dir = env::var("OUT_DIR").expect("No out dir");
	let dest_path = Path::new(&out_dir).join("constants.rs");
	let mut f = File::create(&dest_path).expect("Could not create file");

	let ip = option_env!("HERMIT_IP");
	let ip = ip.map_or("10.0.5.3", |v| v);

	let gateway = option_env!("HERMIT_GATEWAY");
	let gateway = gateway.map_or("10.0.5.1", |v| v);

	let mask = option_env!("HERMIT_MASK");
	let mask = mask.map_or("255.255.255.0", |v| v);

	writeln!(&mut f, "const HERMIT_IP: &str = \"{}\";", ip).expect("Could not write file");
	println!("cargo:rerun-if-env-changed=HERMIT_IP");

	writeln!(&mut f, "const HERMIT_GATEWAY: &str = \"{}\";", gateway)
		.expect("Could not write file");
	println!("cargo:rerun-if-env-changed=HERMIT_GATEWAY");

	writeln!(&mut f, "const HERMIT_MASK: &str = \"{}\";", mask).expect("Could not write file");
	println!("cargo:rerun-if-env-changed=HERMIT_MASK");
}

fn main() {
	#[cfg(not(feature = "rustc-dep-of-std"))]
	create_constants();
	#[cfg(not(feature = "rustc-dep-of-std"))]
	build();
}
<|endoftext|>


<commit_before>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

use libimagstore::store::Entry;

use toml::encode_str;

use viewer::Viewer;
use result::Result;

pub struct StdoutViewer {
    view_header: bool,
    view_content: bool,
}

impl StdoutViewer {

    pub fn new(view_header: bool, view_content: bool) -> StdoutViewer {
        StdoutViewer {
            view_header: view_header,
            view_content: view_content,
        }
    }

}

impl Viewer for StdoutViewer {

    fn view_entry(&self, e: &Entry) -> Result<()> {
        if self.view_header {
            println!("{}", encode_str(e.get_header()));
        }

        if self.view_content {
            println!("{}", e.get_content());
        }

        Ok(())
    }

}
<commit_msg>Fix libimagentryview for toml 0.4<commit_after>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

use libimagstore::store::Entry;

use toml::ser::to_string;

use viewer::Viewer;
use result::Result;

pub struct StdoutViewer {
    view_header: bool,
    view_content: bool,
}

impl StdoutViewer {

    pub fn new(view_header: bool, view_content: bool) -> StdoutViewer {
        StdoutViewer {
            view_header: view_header,
            view_content: view_content,
        }
    }

}

impl Viewer for StdoutViewer {

    fn view_entry(&self, e: &Entry) -> Result<()> {
        if self.view_header {
            println!("{}", to_string(e.get_header()).unwrap_or(String::from("TOML Parser error")));
        }

        if self.view_content {
            println!("{}", e.get_content());
        }

        Ok(())
    }

}
<|endoftext|>


<commit_before>use std::error::Error;
use std::fs;
use std::io::stdin;
use std::path::Path;

use backend::download;
use backend::extract;
use operation::selection::select_package;
use package::find;
use package::pkg::Package;
use paths::{ self, PathAppend };

fn install_package(package: &Package, base_path: &Path) -> Result<(), String> {
    let mut base_pathbuf = base_path.to_path_buf();
    let tarball_path = base_pathbuf.append(&(paths::DOWNLOAD_DIR.to_string() + get_option!(package.url.split('/').last(), "")));
    let gz_path = base_pathbuf.append(&(paths::DOWNLOAD_DIR.to_string() + &package.name + "/"));
    let install_path = base_pathbuf.append(&(paths::PACKAGE_INSTALL_DIR.to_string() + &package.name + "/"));

    try!(download::download_file_while(
            &package.url,
            &tarball_path,
            |all, current|{
                println!("Downloaded {} bytes, out of {} bytes.", current, all);
            }
        )
    );

    try!(extract::extract_tar(&tarball_path, &gz_path));
    try!(extract::extract_gz(&gz_path, &install_path));

    Ok(())
}

pub fn install(input: &str, base_path: &Path) -> Result<(), String> {
    let package_list_dir = get!(fs::read_dir(base_path.to_path_buf().append(paths::PACKAGE_LIST_DIR)), "");
    for list_path in package_list_dir {
        let result = find::select_package(input, list_path.unwrap().path().as_path(), select_package);
        if result.is_ok() {
            let result = result.unwrap();
            for dep in &result.dependencies {
                try!(install_package(&dep, base_path));
            }
            try!(install_package(&result, base_path));
        }
    }
    Err("Specified package not found".to_string())
}<commit_msg>Fixed: function never returns Ok(())<commit_after>use std::error::Error;
use std::fs;
use std::io::stdin;
use std::path::Path;

use backend::download;
use backend::extract;
use operation::selection::select_package;
use package::find;
use package::pkg::Package;
use paths::{ self, PathAppend };

fn install_package(package: &Package, base_path: &Path) -> Result<(), String> {
    let mut base_pathbuf = base_path.to_path_buf();
    let tarball_path = base_pathbuf.append(&(paths::DOWNLOAD_DIR.to_string() + get_option!(package.url.split('/').last(), "")));
    let gz_path = base_pathbuf.append(&(paths::DOWNLOAD_DIR.to_string() + &package.name + "/"));
    let install_path = base_pathbuf.append(&(paths::PACKAGE_INSTALL_DIR.to_string() + &package.name + "/"));

    try!(download::download_file_while(
            &package.url,
            &tarball_path,
            |all, current|{
                println!("Downloaded {} bytes, out of {} bytes.", current, all);
            }
        )
    );

    try!(extract::extract_tar(&tarball_path, &gz_path));
    try!(extract::extract_gz(&gz_path, &install_path));

    Ok(())
}

pub fn install(input: &str, base_path: &Path) -> Result<(), String> {
    let package_list_dir = get!(fs::read_dir(base_path.to_path_buf().append(paths::PACKAGE_LIST_DIR)), "");
    for list_path in package_list_dir {
        let result = find::select_package(input, list_path.unwrap().path().as_path(), select_package);
        if result.is_ok() {
            let result = result.unwrap();
            for dep in &result.dependencies {
                try!(install_package(&dep, base_path));
            }
            return install_package(&result, base_path)
        }
    }
    Err("Specified package not found".to_string())
}<|endoftext|>


<commit_before>use winapi::*;
use core::DXCore;
use create_device;
use utils;
use dx_safe::*;
use dx_safe::structwrappers::*;
use dxsems::VertexFormat;
use std::marker::PhantomData;

// Point Light Shadow
// The name isn't quite correct. This module just fills depth cubemap.
pub struct PLShadow<T: VertexFormat> {
    pub pso: D3D12PipelineState,
    pub root_sig: D3D12RootSignature,
    luid: Luid,
    _phd: PhantomData<T>,
}

impl<T: VertexFormat> PLShadow<T> {
    pub fn new(dev: &D3D12Device) -> HResult<PLShadow<T>> {
        let mut vshader_bc = vec![];
        let mut gshader_bc = vec![];
        let mut pshader_bc = vec![];
        let mut rsig_bc = vec![];
        trace!("Compiling 'plshadow.hlsl'...");
        match create_device::compile_shaders("plshadow.hlsl",&mut[
                    ("VSMain", "vs_5_0", &mut vshader_bc),
                    ("GSMain", "gs_5_0", &mut gshader_bc),
                    ("PSMain", "ps_5_0", &mut pshader_bc),
                    ("RSD", "rootsig_1_0", &mut rsig_bc),
                ], D3DCOMPILE_OPTIMIZATION_LEVEL3) {
            Err(err) => {
                error!("Error compiling 'plshadow.hlsl': {}", err);
                return Err(E_FAIL);
            },
            Ok(_) => {},
        };
        trace!("Done");

        trace!("Root signature creation");
        let root_sig = try!(dev.create_root_signature(0, &rsig_bc[..]));

        let input_elts_desc = T::generate(0);

        // pso_desc contains pointers to local data, so I mustn't pass it around, but I can. Unsafe.
        let pso_desc = D3D12_GRAPHICS_PIPELINE_STATE_DESC {
            pRootSignature: root_sig.iptr() as *mut _,
            VS: ShaderBytecode::from_vec(&vshader_bc).get(),
            GS: ShaderBytecode::from_vec(&gshader_bc).get(),
            PS: ShaderBytecode::from_vec(&pshader_bc).get(),
            RasterizerState: D3D12_RASTERIZER_DESC {
                CullMode: D3D12_CULL_MODE_NONE,
                DepthBias: 1,
                SlopeScaledDepthBias: 1.0,
                ..rasterizer_desc_default()
            },
            InputLayout: D3D12_INPUT_LAYOUT_DESC {
                pInputElementDescs: input_elts_desc.as_ptr(),
                NumElements: input_elts_desc.len() as u32,
            },
            PrimitiveTopologyType: D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE,
            NumRenderTargets: 0, // Pixel shader writes depth buffer only
            DSVFormat: DXGI_FORMAT_D32_FLOAT,
            Flags: D3D12_PIPELINE_STATE_FLAG_NONE,
            // Take other fields from default gpsd
            ..graphics_pipeline_state_desc_default()
        };
        //pso_desc.RTVFormats[0] = DXGI_FORMAT_D32_FLOAT;

        trace!("Graphics pipeline state creation");
        let pso = try!(dev.create_graphics_pipeline_state(&pso_desc));
        Ok(PLShadow::<T> {
            pso: pso,
            root_sig: root_sig,
            luid: Luid(dev.get_adapter_luid()),
            _phd: PhantomData,
        })
    }

}
<commit_msg>Increase depth bias<commit_after>use winapi::*;
use core::DXCore;
use create_device;
use utils;
use dx_safe::*;
use dx_safe::structwrappers::*;
use dxsems::VertexFormat;
use std::marker::PhantomData;

// Point Light Shadow
// The name isn't quite correct. This module just fills depth cubemap.
pub struct PLShadow<T: VertexFormat> {
    pub pso: D3D12PipelineState,
    pub root_sig: D3D12RootSignature,
    luid: Luid,
    _phd: PhantomData<T>,
}

impl<T: VertexFormat> PLShadow<T> {
    pub fn new(dev: &D3D12Device) -> HResult<PLShadow<T>> {
        let mut vshader_bc = vec![];
        let mut gshader_bc = vec![];
        let mut pshader_bc = vec![];
        let mut rsig_bc = vec![];
        trace!("Compiling 'plshadow.hlsl'...");
        match create_device::compile_shaders("plshadow.hlsl",&mut[
                    ("VSMain", "vs_5_0", &mut vshader_bc),
                    ("GSMain", "gs_5_0", &mut gshader_bc),
                    ("PSMain", "ps_5_0", &mut pshader_bc),
                    ("RSD", "rootsig_1_0", &mut rsig_bc),
                ], D3DCOMPILE_OPTIMIZATION_LEVEL3) {
            Err(err) => {
                error!("Error compiling 'plshadow.hlsl': {}", err);
                return Err(E_FAIL);
            },
            Ok(_) => {},
        };
        trace!("Done");

        trace!("Root signature creation");
        let root_sig = try!(dev.create_root_signature(0, &rsig_bc[..]));

        let input_elts_desc = T::generate(0);

        // pso_desc contains pointers to local data, so I mustn't pass it around, but I can. Unsafe.
        let pso_desc = D3D12_GRAPHICS_PIPELINE_STATE_DESC {
            pRootSignature: root_sig.iptr() as *mut _,
            VS: ShaderBytecode::from_vec(&vshader_bc).get(),
            GS: ShaderBytecode::from_vec(&gshader_bc).get(),
            PS: ShaderBytecode::from_vec(&pshader_bc).get(),
            RasterizerState: D3D12_RASTERIZER_DESC {
                CullMode: D3D12_CULL_MODE_NONE,
                DepthBias: 10,
                SlopeScaledDepthBias: 1.5,
                ..rasterizer_desc_default()
            },
            InputLayout: D3D12_INPUT_LAYOUT_DESC {
                pInputElementDescs: input_elts_desc.as_ptr(),
                NumElements: input_elts_desc.len() as u32,
            },
            PrimitiveTopologyType: D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE,
            NumRenderTargets: 0, // Pixel shader writes depth buffer only
            DSVFormat: DXGI_FORMAT_D32_FLOAT,
            Flags: D3D12_PIPELINE_STATE_FLAG_NONE,
            // Take other fields from default gpsd
            ..graphics_pipeline_state_desc_default()
        };
        //pso_desc.RTVFormats[0] = DXGI_FORMAT_D32_FLOAT;

        trace!("Graphics pipeline state creation");
        let pso = try!(dev.create_graphics_pipeline_state(&pso_desc));
        Ok(PLShadow::<T> {
            pso: pso,
            root_sig: root_sig,
            luid: Luid(dev.get_adapter_luid()),
            _phd: PhantomData,
        })
    }

}
<|endoftext|>


<commit_before>// Copyright (c) 2017-2018 Rene van der Meer
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

#![allow(unused_imports)]

use std::io;

use libc::{c_int, termios};
use libc::{cfgetospeed, cfmakeraw, cfsetispeed, cfsetospeed, tcgetattr, tcsetattr};
use libc::{B0, B110, B134, B150, B200, B300, B50, B75};
use libc::{B1000000, B1152000, B460800, B500000, B576000, B921600};
use libc::{B115200, B19200, B230400, B38400, B57600};
use libc::{B1200, B1800, B2400, B4800, B600, B9600};
use libc::{B1500000, B2000000, B2500000, B3000000, B3500000, B4000000};

use libc::{CLOCAL, CMSPAR, CREAD, CRTSCTS, TCSANOW};
use libc::{CS5, CS6, CS7, CS8, CSIZE, CSTOPB, PARENB, PARODD};
use libc::{VMIN, VTIME};

use uart::{Error, Parity, Result};

fn parse_retval(retval: c_int) -> Result<i32> {
    if retval == -1 {
        Err(Error::Io(io::Error::last_os_error()))
    } else {
        Ok(retval)
    }
}

pub fn attributes(fd: c_int) -> Result<termios> {
    let mut attr = termios {
        c_iflag: 0,
        c_oflag: 0,
        c_cflag: 0,
        c_lflag: 0,
        c_line: 0,
        c_cc: [0u8; 32],
        c_ispeed: 0,
        c_ospeed: 0,
    };

    parse_retval(unsafe { tcgetattr(fd, &mut attr) })?;

    Ok(attr)
}

pub fn set_attributes(fd: c_int, attr: &termios) -> Result<()> {
    parse_retval(unsafe { tcsetattr(fd, TCSANOW, attr) })?;

    Ok(())
}

pub fn line_speed(fd: c_int) -> Result<u32> {
    Ok(match unsafe { cfgetospeed(&attributes(fd)?) } {
        B0 => 0,
        B50 => 50,
        B75 => 75,
        B110 => 110,
        B134 => 134,
        B150 => 150,
        B200 => 200,
        B300 => 300,
        B600 => 600,
        B1200 => 1_200,
        B1800 => 1_800,
        B2400 => 2_400,
        B4800 => 4_800,
        B9600 => 9_600,
        B19200 => 19_200,
        B38400 => 38_400,
        B57600 => 57_600,
        B115200 => 115_200,
        B230400 => 230_400,
        B460800 => 460_800,
        B500000 => 500_000,
        B576000 => 576_000,
        B921600 => 921_600,
        B1000000 => 1_000_000,
        B1152000 => 1_152_000,
        B1500000 => 1_500_000,
        B2000000 => 2_000_000,
        B2500000 => 2_500_000,
        B3000000 => 3_000_000,
        B3500000 => 3_500_000,
        B4000000 => 4_000_000,
        _ => return Err(Error::InvalidValue),
    })
}

pub fn set_line_speed(fd: c_int, line_speed: u32) -> Result<()> {
    let baud = match line_speed {
        0 => B0,
        50 => B50,
        75 => B75,
        110 => B110,
        134 => B134,
        150 => B150,
        200 => B200,
        300 => B300,
        600 => B600,
        1_200 => B1200,
        1_800 => B1800,
        2_400 => B2400,
        4_800 => B4800,
        9_600 => B9600,
        19_200 => B19200,
        38_400 => B38400,
        57_600 => B57600,
        115_200 => B115200,
        230_400 => B230400,
        460_800 => B460800,
        500_000 => B500000,
        576_000 => B576000,
        921_600 => B921600,
        1_000_000 => B1000000,
        1_152_000 => B1152000,
        1_500_000 => B1500000,
        2_000_000 => B2000000,
        2_500_000 => B2500000,
        3_000_000 => B3000000,
        3_500_000 => B3500000,
        4_000_000 => B4000000,
        _ => return Err(Error::InvalidValue),
    };

    let mut attr = attributes(fd)?;
    parse_retval(unsafe { cfsetispeed(&mut attr, baud) })?;
    parse_retval(unsafe { cfsetospeed(&mut attr, baud) })?;
    set_attributes(fd, &attr)?;

    Ok(())
}

pub fn parity(fd: c_int) -> Result<Parity> {
    let attr = attributes(fd)?;

    if (attr.c_cflag & PARENB) == 0 {
        return Ok(Parity::None);
    } else if (attr.c_cflag & PARENB) > 0 && (attr.c_cflag & PARODD) == 0 {
        return Ok(Parity::Even);
    } else if (attr.c_cflag & PARENB) > 0 && (attr.c_cflag & PARODD) > 0 {
        return Ok(Parity::Odd);
    } else if (attr.c_cflag & PARENB) > 0
        && (attr.c_cflag & CMSPAR) > 0
        && (attr.c_cflag & PARODD) > 0
    {
        return Ok(Parity::Mark);
    } else if (attr.c_cflag & PARENB) > 0
        && (attr.c_cflag & CMSPAR) > 0
        && (attr.c_cflag & PARODD) == 0
    {
        return Ok(Parity::Space);
    }

    Err(Error::InvalidValue)
}

pub fn set_parity(fd: c_int, parity: Parity) -> Result<()> {
    let mut attr = attributes(fd)?;

    match parity {
        Parity::None => {
            attr.c_cflag &= !PARENB;
            attr.c_cflag &= !PARODD;
        }
        Parity::Even => {
            attr.c_cflag |= PARENB;
            attr.c_cflag &= !PARODD;
        }
        Parity::Odd => {
            attr.c_cflag |= PARENB | PARODD;
        }
        Parity::Mark => {
            attr.c_cflag |= PARENB | PARODD | CMSPAR;
        }
        Parity::Space => {
            attr.c_cflag |= PARENB | CMSPAR;
            attr.c_cflag &= !PARODD;
        }
    }

    set_attributes(fd, &attr)?;

    Ok(())
}

pub fn data_bits(fd: c_int) -> Result<u8> {
    let attr = attributes(fd)?;

    Ok(match attr.c_cflag & CSIZE {
        CS5 => 5,
        CS6 => 6,
        CS7 => 7,
        CS8 => 8,
        _ => return Err(Error::InvalidValue),
    })
}

pub fn set_data_bits(fd: c_int, data_bits: u8) -> Result<()> {
    let mut attr = attributes(fd)?;

    attr.c_cflag &= !CSIZE;
    match data_bits {
        5 => attr.c_cflag |= CS5,
        6 => attr.c_cflag |= CS6,
        7 => attr.c_cflag |= CS7,
        8 => attr.c_cflag |= CS8,
        _ => return Err(Error::InvalidValue),
    }

    set_attributes(fd, &attr)?;

    Ok(())
}

pub fn stop_bits(fd: c_int) -> Result<u8> {
    let attr = attributes(fd)?;

    if (attr.c_cflag & CSTOPB) > 0 {
        Ok(2)
    } else {
        Ok(1)
    }
}

pub fn set_stop_bits(fd: c_int, stop_bits: u8) -> Result<()> {
    let mut attr = attributes(fd)?;

    match stop_bits {
        1 => attr.c_cflag &= !CSTOPB,
        2 => attr.c_cflag |= CSTOPB,
        _ => return Err(Error::InvalidValue),
    }

    set_attributes(fd, &attr)?;

    Ok(())
}

pub fn set_raw_mode(fd: c_int) -> Result<()> {
    let mut attr = attributes(fd)?;
    // Change flags to enable non-canonical mode
    unsafe {
        cfmakeraw(&mut attr);
    }
    attr.c_cc[VMIN] = 0; // Don't block read() when there's no waiting data
    attr.c_cc[VTIME] = 0; // No timeout needed
    set_attributes(fd, &attr)?;

    Ok(())
}

// If CREAD isn't set, all input is discarded
pub fn enable_read(fd: c_int) -> Result<()> {
    let mut attr = attributes(fd)?;
    attr.c_cflag |= CREAD;
    set_attributes(fd, &attr)?;

    Ok(())
}

// Ignore carrier detect signal
pub fn ignore_carrier_detect(fd: c_int) -> Result<()> {
    let mut attr = attributes(fd)?;
    attr.c_cflag |= CLOCAL;
    set_attributes(fd, &attr)?;

    Ok(())
}
<commit_msg>Provide alternative implementation of termios::attributes function for musl<commit_after>// Copyright (c) 2017-2018 Rene van der Meer
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

#![allow(unused_imports)]

use std::io;

use libc::{c_int, termios};
use libc::{cfgetospeed, cfmakeraw, cfsetispeed, cfsetospeed, tcgetattr, tcsetattr};
use libc::{B0, B110, B134, B150, B200, B300, B50, B75};
use libc::{B1000000, B1152000, B460800, B500000, B576000, B921600};
use libc::{B115200, B19200, B230400, B38400, B57600};
use libc::{B1200, B1800, B2400, B4800, B600, B9600};
use libc::{B1500000, B2000000, B2500000, B3000000, B3500000, B4000000};

use libc::{CLOCAL, CMSPAR, CREAD, CRTSCTS, TCSANOW};
use libc::{CS5, CS6, CS7, CS8, CSIZE, CSTOPB, PARENB, PARODD};
use libc::{VMIN, VTIME};

use uart::{Error, Parity, Result};

fn parse_retval(retval: c_int) -> Result<i32> {
    if retval == -1 {
        Err(Error::Io(io::Error::last_os_error()))
    } else {
        Ok(retval)
    }
}

#[cfg(target_env = "gnu")]
pub fn attributes(fd: c_int) -> Result<termios> {
    let mut attr = termios {
        c_iflag: 0,
        c_oflag: 0,
        c_cflag: 0,
        c_lflag: 0,
        c_line: 0,
        c_cc: [0u8; 32],
        c_ispeed: 0,
        c_ospeed: 0,
    };

    parse_retval(unsafe { tcgetattr(fd, &mut attr) })?;

    Ok(attr)
}

#[cfg(target_env = "musl")]
pub fn attributes(fd: c_int) -> Result<termios> {
    let mut attr = termios {
        c_iflag: 0,
        c_oflag: 0,
        c_cflag: 0,
        c_lflag: 0,
        c_line: 0,
        c_cc: [0u8; 32],
        __c_ispeed: 0,
        __c_ospeed: 0,
    };

    parse_retval(unsafe { tcgetattr(fd, &mut attr) })?;

    Ok(attr)
}

pub fn set_attributes(fd: c_int, attr: &termios) -> Result<()> {
    parse_retval(unsafe { tcsetattr(fd, TCSANOW, attr) })?;

    Ok(())
}

pub fn line_speed(fd: c_int) -> Result<u32> {
    Ok(match unsafe { cfgetospeed(&attributes(fd)?) } {
        B0 => 0,
        B50 => 50,
        B75 => 75,
        B110 => 110,
        B134 => 134,
        B150 => 150,
        B200 => 200,
        B300 => 300,
        B600 => 600,
        B1200 => 1_200,
        B1800 => 1_800,
        B2400 => 2_400,
        B4800 => 4_800,
        B9600 => 9_600,
        B19200 => 19_200,
        B38400 => 38_400,
        B57600 => 57_600,
        B115200 => 115_200,
        B230400 => 230_400,
        B460800 => 460_800,
        B500000 => 500_000,
        B576000 => 576_000,
        B921600 => 921_600,
        B1000000 => 1_000_000,
        B1152000 => 1_152_000,
        B1500000 => 1_500_000,
        B2000000 => 2_000_000,
        B2500000 => 2_500_000,
        B3000000 => 3_000_000,
        B3500000 => 3_500_000,
        B4000000 => 4_000_000,
        _ => return Err(Error::InvalidValue),
    })
}

pub fn set_line_speed(fd: c_int, line_speed: u32) -> Result<()> {
    let baud = match line_speed {
        0 => B0,
        50 => B50,
        75 => B75,
        110 => B110,
        134 => B134,
        150 => B150,
        200 => B200,
        300 => B300,
        600 => B600,
        1_200 => B1200,
        1_800 => B1800,
        2_400 => B2400,
        4_800 => B4800,
        9_600 => B9600,
        19_200 => B19200,
        38_400 => B38400,
        57_600 => B57600,
        115_200 => B115200,
        230_400 => B230400,
        460_800 => B460800,
        500_000 => B500000,
        576_000 => B576000,
        921_600 => B921600,
        1_000_000 => B1000000,
        1_152_000 => B1152000,
        1_500_000 => B1500000,
        2_000_000 => B2000000,
        2_500_000 => B2500000,
        3_000_000 => B3000000,
        3_500_000 => B3500000,
        4_000_000 => B4000000,
        _ => return Err(Error::InvalidValue),
    };

    let mut attr = attributes(fd)?;
    parse_retval(unsafe { cfsetispeed(&mut attr, baud) })?;
    parse_retval(unsafe { cfsetospeed(&mut attr, baud) })?;
    set_attributes(fd, &attr)?;

    Ok(())
}

pub fn parity(fd: c_int) -> Result<Parity> {
    let attr = attributes(fd)?;

    if (attr.c_cflag & PARENB) == 0 {
        return Ok(Parity::None);
    } else if (attr.c_cflag & PARENB) > 0 && (attr.c_cflag & PARODD) == 0 {
        return Ok(Parity::Even);
    } else if (attr.c_cflag & PARENB) > 0 && (attr.c_cflag & PARODD) > 0 {
        return Ok(Parity::Odd);
    } else if (attr.c_cflag & PARENB) > 0
        && (attr.c_cflag & CMSPAR) > 0
        && (attr.c_cflag & PARODD) > 0
    {
        return Ok(Parity::Mark);
    } else if (attr.c_cflag & PARENB) > 0
        && (attr.c_cflag & CMSPAR) > 0
        && (attr.c_cflag & PARODD) == 0
    {
        return Ok(Parity::Space);
    }

    Err(Error::InvalidValue)
}

pub fn set_parity(fd: c_int, parity: Parity) -> Result<()> {
    let mut attr = attributes(fd)?;

    match parity {
        Parity::None => {
            attr.c_cflag &= !PARENB;
            attr.c_cflag &= !PARODD;
        }
        Parity::Even => {
            attr.c_cflag |= PARENB;
            attr.c_cflag &= !PARODD;
        }
        Parity::Odd => {
            attr.c_cflag |= PARENB | PARODD;
        }
        Parity::Mark => {
            attr.c_cflag |= PARENB | PARODD | CMSPAR;
        }
        Parity::Space => {
            attr.c_cflag |= PARENB | CMSPAR;
            attr.c_cflag &= !PARODD;
        }
    }

    set_attributes(fd, &attr)?;

    Ok(())
}

pub fn data_bits(fd: c_int) -> Result<u8> {
    let attr = attributes(fd)?;

    Ok(match attr.c_cflag & CSIZE {
        CS5 => 5,
        CS6 => 6,
        CS7 => 7,
        CS8 => 8,
        _ => return Err(Error::InvalidValue),
    })
}

pub fn set_data_bits(fd: c_int, data_bits: u8) -> Result<()> {
    let mut attr = attributes(fd)?;

    attr.c_cflag &= !CSIZE;
    match data_bits {
        5 => attr.c_cflag |= CS5,
        6 => attr.c_cflag |= CS6,
        7 => attr.c_cflag |= CS7,
        8 => attr.c_cflag |= CS8,
        _ => return Err(Error::InvalidValue),
    }

    set_attributes(fd, &attr)?;

    Ok(())
}

pub fn stop_bits(fd: c_int) -> Result<u8> {
    let attr = attributes(fd)?;

    if (attr.c_cflag & CSTOPB) > 0 {
        Ok(2)
    } else {
        Ok(1)
    }
}

pub fn set_stop_bits(fd: c_int, stop_bits: u8) -> Result<()> {
    let mut attr = attributes(fd)?;

    match stop_bits {
        1 => attr.c_cflag &= !CSTOPB,
        2 => attr.c_cflag |= CSTOPB,
        _ => return Err(Error::InvalidValue),
    }

    set_attributes(fd, &attr)?;

    Ok(())
}

pub fn set_raw_mode(fd: c_int) -> Result<()> {
    let mut attr = attributes(fd)?;
    // Change flags to enable non-canonical mode
    unsafe {
        cfmakeraw(&mut attr);
    }
    attr.c_cc[VMIN] = 0; // Don't block read() when there's no waiting data
    attr.c_cc[VTIME] = 0; // No timeout needed
    set_attributes(fd, &attr)?;

    Ok(())
}

// If CREAD isn't set, all input is discarded
pub fn enable_read(fd: c_int) -> Result<()> {
    let mut attr = attributes(fd)?;
    attr.c_cflag |= CREAD;
    set_attributes(fd, &attr)?;

    Ok(())
}

// Ignore carrier detect signal
pub fn ignore_carrier_detect(fd: c_int) -> Result<()> {
    let mut attr = attributes(fd)?;
    attr.c_cflag |= CLOCAL;
    set_attributes(fd, &attr)?;

    Ok(())
}
<|endoftext|>


<commit_before>use std::path::{Path, StripPrefixError, /*MAIN_SEPARATOR*/};
use std::{fs, io, error, fmt};
use std::process::{Command, Stdio};
use std::fs::File;
// use std::io::prelude::*;

// extern crate walkdir;
// use walkdir::WalkDir;

extern crate zip;
// use zip::write::FileOptions;

pub fn zip<P: AsRef<Path>>(dir: P, out_file: P) -> Result<(), ArchiveError> {
    let dir = dir.as_ref();
    let out_file = out_file.as_ref();

    if out_file.exists() {
        fs::remove_file(out_file)?;
    }

    if cfg!(target_os = "windows") {
        let command = format!(
            "& {{ Add-Type -A 'System.IO.Compression.FileSystem'; [IO.Compression.ZipFile]::CreateFromDirectory('{}', '{}'); }}",
            dir.to_string_lossy(),
            out_file.to_string_lossy()
        );

        let child = Command::new("powershell")
            .args(&["-command", &command])
            .stdout(Stdio::piped())
            .stderr(Stdio::piped())
            .spawn()?;

        let output = child
            .wait_with_output()?;

        if !output.status.success() {
            return Err(ArchiveError::Powershell(String::from_utf8(output.stderr).unwrap()))
        }
    } else {
        // TODO Mac
    }

    Ok(())

    // TODO Use zip-rs once https://github.com/mvdnes/zip-rs/issues/23 is resolved
    //
    // let file = fs::File::create(outfile.as_ref())?;
    // let mut zipfile = zip::ZipWriter::new(file);
    // let entries = WalkDir::new(dir.as_ref()).into_iter().filter_map(|e| e.ok());
    //
    // println!("\nZip: {}\n|", outfile.as_ref().display());
    //
    // for entry in entries {
    // 	let path = entry.path();
    // 	let prefix = format!("{}{}", dir.as_ref().to_string_lossy(), MAIN_SEPARATOR);
    // 	let relative = path.strip_prefix(&prefix)?;
    //
    // 	if path.is_dir() {
    // 		let dir_path = format!("{}/", relative.to_string_lossy()).replace("\\", "/");
    // 		let dir_options = FileOptions::default()
    // 			.unix_permissions(0o755);
    //
    // 		if &dir_path == "/" { continue; }
    //
    // 		println!("| {}", dir_path);
    // 		zipfile.add_directory(dir_path, dir_options)?;
    // 	} else {
    // 		let file_path = relative.to_string_lossy().replace("\\", "/");
    // 		let file_options = FileOptions::default()
    // 			.compression_method(zip::CompressionMethod::Deflated)
    // 			.unix_permissions(0o644);
    //
    // 		let mut file = File::open(path.clone())?;
    // 		let mut contents = String::new();
    // 		file.read_to_string(&mut contents)?;
    //
    // 		println!("|   {}", file_path);
    // 		zipfile.start_file(file_path, file_options)?;
    // 		zipfile.write_all(&contents.into_bytes())?;
    // 	}
    // }
    //
    // zipfile.finish()?;
    // println!("|\nDone.\n");
    //
    // Ok(())
}

pub fn unzip<P: AsRef<Path>>(zip_file: P, out_dir: P) -> Result<(), ArchiveError> {
    // TODO powershell -command & { Add-Type -A 'System.IO.Compression.FileSystem'; [IO.Compression.ZipFile]::ExtractToDirectory('foo.zip', 'bar'); }
    let out_dir = out_dir.as_ref();
    let file = File::open(zip_file.as_ref())?;
    let mut unzipped = zip::ZipArchive::new(file)?;

    for i in 0..unzipped.len() {
        let mut file = unzipped.by_index(i)?;
        let out_path = out_dir.join(sanitize_filename(file.name()));

        if out_path.ends_with("/") {
            create_directory(&out_path)?;
        } else {
            create_directory(out_path.parent().unwrap_or(&out_dir.join(Path::new(""))))?;
            create_file(&mut file, &out_path)?;
        }
    }

    Ok(())
}

fn create_file(file: &mut zip::read::ZipFile, out_path: &Path) -> Result<(), io::Error> {
    let mut out_file = File::create(&out_path)?;
    io::copy(file, &mut out_file)?;
    Ok(())
}

fn create_directory(path: &Path) -> Result<(), io::Error> {
    fs::create_dir_all(path)?;
    Ok(())
}

fn sanitize_filename(filename: &str) -> std::path::PathBuf {
    let no_null_filename = match filename.find('\0') {
        Some(index) => &filename[0..index],
        None => filename,
    };

    std::path::Path::new(no_null_filename)
        .components()
        .filter(|component| match *component {
            std::path::Component::Normal(..) => true,
            _ => false
        })
        .fold(std::path::PathBuf::new(), |mut path, ref cur| {
            path.push(cur.as_os_str());
            path
        })
}

#[derive(Debug)]
pub enum ArchiveError {
    Io(io::Error),
    Zip(zip::result::ZipError),
    StripPrefix(StripPrefixError),
    Powershell(String),
}

impl fmt::Display for ArchiveError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ArchiveError::Io(ref err) => write!(f, "IO error: {}", err),
            ArchiveError::Zip(ref err) => write!(f, "Zip error: {}", err),
            ArchiveError::StripPrefix(ref err) => write!(f, "Strip prefix error: {}", err),
            ArchiveError::Powershell(ref err) => write!(f, "Powershell error: {}", err),
        }
    }
}

impl error::Error for ArchiveError {
    fn description(&self) -> &str {
        match *self {
            ArchiveError::Io(ref err) => err.description(),
            ArchiveError::Zip(ref err) => err.description(),
            ArchiveError::StripPrefix(ref err) => err.description(),
            ArchiveError::Powershell(ref err) => err.as_str(),
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            ArchiveError::Io(ref err) => Some(err),
            ArchiveError::Zip(ref err) => Some(err),
            ArchiveError::StripPrefix(ref err) => Some(err),
            ArchiveError::Powershell(_) => None,
        }
    }
}

impl From<io::Error> for ArchiveError {
    fn from(err: io::Error) -> ArchiveError {
        ArchiveError::Io(err)
    }
}

impl From<zip::result::ZipError> for ArchiveError {
    fn from(err: zip::result::ZipError) -> ArchiveError {
        ArchiveError::Zip(err)
    }
}

impl From<StripPrefixError> for ArchiveError {
    fn from(err: StripPrefixError) -> ArchiveError {
        ArchiveError::StripPrefix(err)
    }
}
<commit_msg>Add Mac zip implementation<commit_after>use std::path::{Path, StripPrefixError, /*MAIN_SEPARATOR*/};
use std::{fs, io, error, fmt};
use std::process::{Command, Stdio};
use std::fs::File;
// use std::io::prelude::*;

// extern crate walkdir;
// use walkdir::WalkDir;

extern crate zip;
// use zip::write::FileOptions;

pub fn zip<P: AsRef<Path>>(dir: P, out_file: P) -> Result<(), ArchiveError> {
    let dir = dir.as_ref();
    let out_file = out_file.as_ref();

    if out_file.exists() {
        fs::remove_file(out_file)?;
    }

    if cfg!(target_os = "windows") {
        let command = format!(
            "& {{ Add-Type -A 'System.IO.Compression.FileSystem'; [IO.Compression.ZipFile]::CreateFromDirectory('{}', '{}'); }}",
            dir.to_string_lossy(),
            out_file.to_string_lossy()
        );

        let child = Command::new("powershell")
            .args(&["-command", &command])
            .stdout(Stdio::piped())
            .stderr(Stdio::piped())
            .spawn()?;

        let output = child
            .wait_with_output()?;

        if !output.status.success() {
            return Err(ArchiveError::Command(String::from_utf8(output.stderr).unwrap()));
        }
    } else {
        if let (Some(out_file), Some(dir)) = (out_file.to_str(), dir.to_str()) {
            let child = Command::new("zip")
                .args(&["-r", out_file, dir])
                .spawn()?;

            let output = child
                .wait_with_output()?;

            if !output.status.success() {
                return Err(ArchiveError::Command(String::from_utf8(output.stderr).unwrap()));
            }
        }
    }

    Ok(())

    // TODO Use zip-rs once https://github.com/mvdnes/zip-rs/issues/23 is resolved
    //
    // let file = fs::File::create(outfile.as_ref())?;
    // let mut zipfile = zip::ZipWriter::new(file);
    // let entries = WalkDir::new(dir.as_ref()).into_iter().filter_map(|e| e.ok());
    //
    // println!("\nZip: {}\n|", outfile.as_ref().display());
    //
    // for entry in entries {
    // 	let path = entry.path();
    // 	let prefix = format!("{}{}", dir.as_ref().to_string_lossy(), MAIN_SEPARATOR);
    // 	let relative = path.strip_prefix(&prefix)?;
    //
    // 	if path.is_dir() {
    // 		let dir_path = format!("{}/", relative.to_string_lossy()).replace("\\", "/");
    // 		let dir_options = FileOptions::default()
    // 			.unix_permissions(0o755);
    //
    // 		if &dir_path == "/" { continue; }
    //
    // 		println!("| {}", dir_path);
    // 		zipfile.add_directory(dir_path, dir_options)?;
    // 	} else {
    // 		let file_path = relative.to_string_lossy().replace("\\", "/");
    // 		let file_options = FileOptions::default()
    // 			.compression_method(zip::CompressionMethod::Deflated)
    // 			.unix_permissions(0o644);
    //
    // 		let mut file = File::open(path.clone())?;
    // 		let mut contents = String::new();
    // 		file.read_to_string(&mut contents)?;
    //
    // 		println!("|   {}", file_path);
    // 		zipfile.start_file(file_path, file_options)?;
    // 		zipfile.write_all(&contents.into_bytes())?;
    // 	}
    // }
    //
    // zipfile.finish()?;
    // println!("|\nDone.\n");
    //
    // Ok(())
}

pub fn unzip<P: AsRef<Path>>(zip_file: P, out_dir: P) -> Result<(), ArchiveError> {
    let out_dir = out_dir.as_ref();
    let file = File::open(zip_file.as_ref())?;
    let mut unzipped = zip::ZipArchive::new(file)?;

    for i in 0..unzipped.len() {
        let mut file = unzipped.by_index(i)?;
        let out_path = out_dir.join(sanitize_filename(file.name()));

        if out_path.ends_with("/") {
            create_directory(&out_path)?;
        } else {
            create_directory(out_path.parent().unwrap_or(&out_dir.join(Path::new(""))))?;
            create_file(&mut file, &out_path)?;
        }
    }

    Ok(())
}

fn create_file(file: &mut zip::read::ZipFile, out_path: &Path) -> Result<(), io::Error> {
    let mut out_file = File::create(&out_path)?;
    io::copy(file, &mut out_file)?;
    Ok(())
}

fn create_directory(path: &Path) -> Result<(), io::Error> {
    fs::create_dir_all(path)?;
    Ok(())
}

fn sanitize_filename(filename: &str) -> std::path::PathBuf {
    let no_null_filename = match filename.find('\0') {
        Some(index) => &filename[0..index],
        None => filename,
    };

    std::path::Path::new(no_null_filename)
        .components()
        .filter(|component| match *component {
            std::path::Component::Normal(..) => true,
            _ => false
        })
        .fold(std::path::PathBuf::new(), |mut path, ref cur| {
            path.push(cur.as_os_str());
            path
        })
}

#[derive(Debug)]
pub enum ArchiveError {
    Io(io::Error),
    Zip(zip::result::ZipError),
    StripPrefix(StripPrefixError),
    Command(String),
}

impl fmt::Display for ArchiveError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ArchiveError::Io(ref err) => write!(f, "IO error: {}", err),
            ArchiveError::Zip(ref err) => write!(f, "Zip error: {}", err),
            ArchiveError::StripPrefix(ref err) => write!(f, "Strip prefix error: {}", err),
            ArchiveError::Command(ref err) => write!(f, "Command error: {}", err),
        }
    }
}

impl error::Error for ArchiveError {
    fn description(&self) -> &str {
        match *self {
            ArchiveError::Io(ref err) => err.description(),
            ArchiveError::Zip(ref err) => err.description(),
            ArchiveError::StripPrefix(ref err) => err.description(),
            ArchiveError::Command(ref err) => err.as_str(),
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            ArchiveError::Io(ref err) => Some(err),
            ArchiveError::Zip(ref err) => Some(err),
            ArchiveError::StripPrefix(ref err) => Some(err),
            ArchiveError::Command(_) => None,
        }
    }
}

impl From<io::Error> for ArchiveError {
    fn from(err: io::Error) -> ArchiveError {
        ArchiveError::Io(err)
    }
}

impl From<zip::result::ZipError> for ArchiveError {
    fn from(err: zip::result::ZipError) -> ArchiveError {
        ArchiveError::Zip(err)
    }
}

impl From<StripPrefixError> for ArchiveError {
    fn from(err: StripPrefixError) -> ArchiveError {
        ArchiveError::StripPrefix(err)
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use std::sync::{Arc, RwLock};

use common::{PerfLogEvent, PerfLogger};
use crossbeam::Sender;
use dashmap::mapref::entry::Entry;
use fnv::FnvHashMap;
use interner::StringKey;
use log::{debug, info};
use lsp_server::Message;
use rayon::iter::ParallelIterator;
use relay_compiler::{
    build_raw_program, build_schema, compiler_state::CompilerState, compiler_state::SourceSetName,
    config::Config, config::ProjectConfig, errors::BuildProjectError, errors::Error,
    transform_program, validate_program, BuildProjectFailure, FileSource, FileSourceResult,
    FileSourceSubscription, FileSourceSubscriptionNextChange, GraphQLAsts,
    SourceControlUpdateStatus,
};
use schema::SDLSchema;
use tokio::{sync::Notify, task, task::JoinHandle};

use crate::{
    diagnostic_reporter::DiagnosticReporter,
    lsp::set_ready_status,
    lsp::update_in_progress_status,
    lsp_process_error::{LSPProcessError, LSPProcessResult},
};

use super::lsp_state::{ProjectStatus, ProjectStatusMap, Schemas, SourcePrograms};

/// This structure is responsible for keeping schemas/programs in sync with the current state of the world
pub(crate) struct LSPStateResources<TPerfLogger: PerfLogger + 'static> {
    config: Arc<Config>,
    perf_logger: Arc<TPerfLogger>,
    schemas: Schemas,
    source_programs: SourcePrograms,
    errors: Arc<RwLock<Vec<String>>>,
    notify_sender: Arc<Notify>,
    notify_receiver: Arc<Notify>,
    sender: Sender<Message>,
    diagnostic_reporter: Arc<DiagnosticReporter>,
    project_status: ProjectStatusMap,
}

impl<TPerfLogger: PerfLogger + 'static> LSPStateResources<TPerfLogger> {
    pub(crate) fn new(
        config: Arc<Config>,
        perf_logger: Arc<TPerfLogger>,
        schemas: Schemas,
        source_programs: SourcePrograms,
        sender: Sender<Message>,
        diagnostic_reporter: Arc<DiagnosticReporter>,
        notify_sender: Arc<Notify>,
        project_status: ProjectStatusMap,
    ) -> Self {
        let notify_receiver = notify_sender.clone();

        Self {
            config,
            perf_logger,
            schemas,
            source_programs,
            errors: Default::default(),
            sender,
            notify_sender,
            notify_receiver,
            diagnostic_reporter,
            project_status,
        }
    }

    /// Create an end-less loop of keeping the resources up-to-date with the source control changes
    pub(crate) async fn watch(&self) -> LSPProcessResult<()> {
        'outer: loop {
            info!("Initializing resources for LSP server");

            update_in_progress_status(
                "Relay: watchman...",
                Some("Sending watchman query to get source files and possible saved state"),
                &self.sender,
            );
            let setup_event = self
                .perf_logger
                .create_event("lsp_state_initialize_resources");
            let timer = setup_event.start("lsp_state_initialize_resources_time");

            let file_source = FileSource::connect(&self.config, &setup_event)
                .await
                .map_err(LSPProcessError::CompilerError)?;
            let (mut compiler_state, file_source_subscription) = file_source
                .subscribe(&setup_event, self.perf_logger.as_ref())
                .await
                .map_err(LSPProcessError::CompilerError)?;

            let pending_file_source_changes =
                Arc::clone(&compiler_state.pending_file_source_changes);

            // Start a separate task to receive watchman subscription updates
            // this process will notify, when the change is detected.
            // If watchman detects the source control update, it will update the state
            // of the source_control_update_status, so we can cancel building schemas/
            // and programs earlier
            let subscription_handle = self.watchman_subscription_handler(
                file_source_subscription,
                pending_file_source_changes,
                Arc::clone(&compiler_state.source_control_update_status),
            );
            update_in_progress_status(
                "Relay: creating state...",
                Some("Building schemas and source programs for LSP"),
                &self.sender,
            );

            self.diagnostic_reporter.clear_regular_diagnostics();

            // Run initial build, before entering the watch changes loop
            if let Err(err) = self.build_projects(&mut compiler_state, &setup_event) {
                self.report_error(err);
            }

            setup_event.stop(timer);
            self.perf_logger.complete_event(setup_event);

            self.publish_errors("lsp_state_error");
            set_ready_status(&self.sender);

            info!("LSP server initialization completed!");

            // Here we will wait for changes from watchman
            'inner: loop {
                self.notify_receiver.notified().await;

                // Source control update started, we can ignore all pending changes, and wait for it to complete,
                // we may change the status bar to `Source Control Update...`
                if compiler_state.source_control_update_status.is_started() {
                    update_in_progress_status(
                        "Relay: hg update...",
                        Some("Waiting for source control update"),
                        &self.sender,
                    );
                    continue 'inner;
                }

                // SC Update completed, we need to abort current subscription, and re-initialize resource for LSP
                if compiler_state.source_control_update_status.is_completed() {
                    debug!("Watchman indicated the the source control update has completed!");
                    subscription_handle.abort();
                    continue 'outer;
                }

                let log_event = self.perf_logger.create_event("lsp_state_watchman_event");
                let log_time = log_event.start("lsp_state_watchman_event_time");

                if let Err(error) = self.incremental_build(&mut compiler_state, &log_event) {
                    self.report_error(error);
                }

                log_event.stop(log_time);
                self.perf_logger.complete_event(log_event);
                self.perf_logger.flush();

                self.publish_errors("lsp_state_user_error");

                set_ready_status(&self.sender);
            }
        }
    }

    fn incremental_build(
        &self,
        compiler_state: &mut CompilerState,
        log_event: &impl PerfLogEvent,
    ) -> Result<(), Error> {
        let has_new_changes = compiler_state.merge_file_source_changes(
            &self.config,
            log_event,
            self.perf_logger.as_ref(),
            false,
        )?;

        // Rebuild if there are pending files or if a new project is activated
        if has_new_changes
            || self
                .project_status
                .iter()
                .any(|r| r.value() == &ProjectStatus::Activated)
        {
            info!("LSP server detected changes...");

            self.diagnostic_reporter.clear_regular_diagnostics();

            update_in_progress_status(
                "Relay: checking...",
                Some("Validating changes, and updating source programs with the latest changes."),
                &self.sender,
            );

            self.build_projects(compiler_state, log_event)?;
        }

        Ok(())
    }

    fn build_projects(
        &self,
        compiler_state: &mut CompilerState,
        log_event: &impl PerfLogEvent,
    ) -> Result<(), Error> {
        let graphql_asts = log_event.time("parse_sources_time", || {
            GraphQLAsts::from_graphql_sources_map(
                &compiler_state.graphql_sources,
                &compiler_state.get_dirty_definitions(&self.config),
            )
        })?;

        if compiler_state.should_cancel_current_build() {
            debug!("Build is cancelled: new file changes are pending.");
            return Err(Error::Cancelled);
        }

        let timer = log_event.start("build_lsp_projects");
        let build_results: Vec<_> = self
            .config
            .par_enabled_projects()
            .filter(|project_config| {
                // Filter inactive projects
                if !self.project_status.contains_key(&project_config.name) {
                    return false;
                }
                // When the source programs is empty, we need to compile all source programs once
                if !self.source_programs.contains_key(&project_config.name) {
                    return true;
                }
                if let Some(base) = project_config.base {
                    if compiler_state.project_has_pending_changes(base) {
                        return true;
                    }
                }
                compiler_state.project_has_pending_changes(project_config.name)
            })
            .map(|project_config| self.build_project(project_config, compiler_state, &graphql_asts))
            .collect();
        log_event.stop(timer);

        let mut errors = vec![];
        for build_result in build_results {
            match build_result {
                Err(BuildProjectFailure::Error(err)) => {
                    errors.push(err);
                }
                Ok(project_name) => {
                    compiler_state.complete_project_compilation(&project_name);
                }
                _ => {}
            }
        }
        if errors.is_empty() {
            Ok(())
        } else {
            Err(Error::BuildProjectsErrors { errors })
        }
    }

    fn build_project(
        &self,
        project_config: &ProjectConfig,
        compiler_state: &CompilerState,
        graphql_asts: &FnvHashMap<SourceSetName, GraphQLAsts>,
    ) -> Result<StringKey, BuildProjectFailure> {
        self.project_status
            .insert(project_config.name, ProjectStatus::Completed);
        let log_event = self.perf_logger.create_event("build_lsp_project");
        let project_name = project_config.name;
        let build_time = log_event.start("build_lsp_project_time");
        log_event.string("project", project_name.to_string());

        let schema = log_event.time("build_schema_time", || {
            self.build_schema(compiler_state, project_config)
        })?;

        self.build_programs(
            project_config,
            compiler_state,
            graphql_asts,
            schema,
            &log_event,
        )?;

        log_event.stop(build_time);
        Ok(project_name)
    }

    fn build_schema(
        &self,
        compiler_state: &CompilerState,
        project_config: &ProjectConfig,
    ) -> Result<Arc<SDLSchema>, BuildProjectFailure> {
        match self.schemas.entry(project_config.name) {
            Entry::Vacant(e) => {
                let schema = build_schema(compiler_state, project_config).map_err(|errors| {
                    BuildProjectFailure::Error(BuildProjectError::ValidationErrors { errors })
                })?;
                e.insert(Arc::clone(&schema));
                Ok(schema)
            }
            Entry::Occupied(mut e) => {
                if !compiler_state.project_has_pending_schema_changes(project_config.name) {
                    Ok(Arc::clone(e.get()))
                } else {
                    let schema =
                        build_schema(compiler_state, project_config).map_err(|errors| {
                            debug!("build error");
                            BuildProjectFailure::Error(BuildProjectError::ValidationErrors {
                                errors,
                            })
                        })?;
                    e.insert(Arc::clone(&schema));
                    Ok(schema)
                }
            }
        }
    }

    fn build_programs(
        &self,
        project_config: &ProjectConfig,
        compiler_state: &CompilerState,
        graphql_asts: &FnvHashMap<SourceSetName, GraphQLAsts>,
        schema: Arc<SDLSchema>,
        log_event: &impl PerfLogEvent,
    ) -> Result<(), BuildProjectFailure> {
        let is_incremental_build = self.source_programs.contains_key(&project_config.name)
            && compiler_state.has_processed_changes()
            && !compiler_state.has_breaking_schema_change(project_config.name)
            && if let Some(base) = project_config.base {
                !compiler_state.has_breaking_schema_change(base)
            } else {
                true
            };

        let (base_program, base_fragment_names, _) = build_raw_program(
            project_config,
            graphql_asts,
            schema,
            log_event,
            is_incremental_build,
        )?;

        if compiler_state.should_cancel_current_build() {
            debug!("Build is cancelled: updates in source code/or new file changes are pending.");
            return Err(BuildProjectFailure::Cancelled);
        }

        match self.source_programs.entry(project_config.name) {
            Entry::Vacant(e) => {
                e.insert(base_program.clone());
            }
            Entry::Occupied(mut e) => {
                let program = e.get_mut();
                let removed_definition_names = graphql_asts
                    .get(&project_config.name)
                    .map(|ast| ast.removed_definition_names.as_ref());
                program.merge_program(&base_program, removed_definition_names);
            }
        }

        validate_program(&self.config, &base_program, log_event)?;

        transform_program(
            &self.config,
            project_config,
            Arc::new(base_program),
            Arc::new(base_fragment_names),
            Arc::clone(&self.perf_logger),
            log_event,
        )?;
        Ok(())
    }

    fn watchman_subscription_handler(
        &self,
        mut file_source_subscription: FileSourceSubscription,
        pending_file_source_changes: Arc<RwLock<Vec<FileSourceResult>>>,
        source_code_update_status: Arc<SourceControlUpdateStatus>,
    ) -> JoinHandle<()> {
        let notify_sender = self.notify_sender.clone();
        task::spawn(async move {
            loop {
                match file_source_subscription.next_change().await {
                    Err(_) => {
                        // do nothing? compiler should panic, and restart the lsp
                    }
                    Ok(FileSourceSubscriptionNextChange::None) => {}
                    Ok(FileSourceSubscriptionNextChange::SourceControlUpdateEnter) => {
                        source_code_update_status.mark_as_started();
                    }
                    Ok(FileSourceSubscriptionNextChange::SourceControlUpdateLeave) => {
                        source_code_update_status.set_to_default();
                    }
                    Ok(FileSourceSubscriptionNextChange::SourceControlUpdate) => {
                        source_code_update_status.mark_as_completed();
                        notify_sender.notify_one();
                        break;
                    }
                    Ok(FileSourceSubscriptionNextChange::Result(file_source_changes)) => {
                        pending_file_source_changes
                            .write()
                            .expect("LSPState::watch_and_update_schemas: expect to acquire write lock on pending_file_source_changes")
                            .push(file_source_changes);

                        notify_sender.notify_one();
                    }
                }
            }
        })
    }

    fn report_error(&self, error: Error) {
        self.diagnostic_reporter.report_error(&error);
        self.errors.write().unwrap().push(error.to_string());
    }

    fn log_errors(&self, log_event_name: &str) {
        if let Ok(read_lock) = self.errors.try_read() {
            if !read_lock.is_empty() {
                let error_message = read_lock
                    .iter()
                    .map(|err| format!("{:?}", err))
                    .collect::<Vec<String>>()
                    .join("\n");
                let error_event = self.perf_logger.create_event(log_event_name);
                error_event.string("error", error_message);
                self.perf_logger.complete_event(error_event);
                self.perf_logger.flush();
            }
        }
    }

    fn clear_errors(&self) {
        if let Ok(mut write_lock) = self.errors.try_write() {
            write_lock.clear();
        }
    }

    /// This method, will log errors (if there were errors)
    /// and report the diagnostics to IDE
    fn publish_errors(&self, log_event_name: &str) {
        self.log_errors(log_event_name);
        self.clear_errors();
        self.diagnostic_reporter.commit_diagnostics();
    }
}
<commit_msg>Some cleanup of lsp_state_resources<commit_after>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use std::sync::{Arc, RwLock};

use common::{PerfLogEvent, PerfLogger};
use crossbeam::Sender;
use dashmap::mapref::entry::Entry;
use fnv::FnvHashMap;
use interner::StringKey;
use log::{debug, info};
use lsp_server::Message;
use rayon::iter::ParallelIterator;
use relay_compiler::{
    build_raw_program, build_schema, compiler_state::CompilerState, compiler_state::SourceSetName,
    config::Config, config::ProjectConfig, errors::BuildProjectError, errors::Error,
    transform_program, validate_program, BuildProjectFailure, FileSource, FileSourceResult,
    FileSourceSubscription, FileSourceSubscriptionNextChange, GraphQLAsts,
    SourceControlUpdateStatus,
};
use schema::SDLSchema;
use tokio::{sync::Notify, task, task::JoinHandle};

use crate::{
    diagnostic_reporter::DiagnosticReporter,
    lsp::set_ready_status,
    lsp::update_in_progress_status,
    lsp_process_error::{LSPProcessError, LSPProcessResult},
};

use super::lsp_state::{ProjectStatus, ProjectStatusMap, Schemas, SourcePrograms};

/// This structure is responsible for keeping schemas/programs in sync with the current state of the world
pub(crate) struct LSPStateResources<TPerfLogger: PerfLogger + 'static> {
    config: Arc<Config>,
    perf_logger: Arc<TPerfLogger>,
    schemas: Schemas,
    source_programs: SourcePrograms,
    notify: Arc<Notify>,
    sender: Sender<Message>,
    diagnostic_reporter: Arc<DiagnosticReporter>,
    project_status: ProjectStatusMap,
}

impl<TPerfLogger: PerfLogger + 'static> LSPStateResources<TPerfLogger> {
    pub(crate) fn new(
        config: Arc<Config>,
        perf_logger: Arc<TPerfLogger>,
        schemas: Schemas,
        source_programs: SourcePrograms,
        sender: Sender<Message>,
        diagnostic_reporter: Arc<DiagnosticReporter>,
        notify: Arc<Notify>,
        project_status: ProjectStatusMap,
    ) -> Self {
        Self {
            config,
            perf_logger,
            schemas,
            source_programs,
            sender,
            notify,
            diagnostic_reporter,
            project_status,
        }
    }

    /// Create an end-less loop of keeping the resources up-to-date with the source control changes
    pub(crate) async fn watch(&self) -> LSPProcessResult<()> {
        'outer: loop {
            info!("Initializing resources for LSP server");

            update_in_progress_status(
                "Relay: watchman...",
                Some("Sending watchman query to get source files and possible saved state"),
                &self.sender,
            );
            let setup_event = self
                .perf_logger
                .create_event("lsp_state_initialize_resources");
            let timer = setup_event.start("lsp_state_initialize_resources_time");

            let file_source = FileSource::connect(&self.config, &setup_event)
                .await
                .map_err(LSPProcessError::CompilerError)?;
            let (mut compiler_state, file_source_subscription) = file_source
                .subscribe(&setup_event, self.perf_logger.as_ref())
                .await
                .map_err(LSPProcessError::CompilerError)?;

            let pending_file_source_changes =
                Arc::clone(&compiler_state.pending_file_source_changes);

            // Start a separate task to receive watchman subscription updates
            // this process will notify, when the change is detected.
            // If watchman detects the source control update, it will update the state
            // of the source_control_update_status, so we can cancel building schemas/
            // and programs earlier
            let subscription_handle = self.watchman_subscription_handler(
                file_source_subscription,
                pending_file_source_changes,
                Arc::clone(&compiler_state.source_control_update_status),
            );
            update_in_progress_status(
                "Relay: creating state...",
                Some("Building schemas and source programs for LSP"),
                &self.sender,
            );

            self.diagnostic_reporter.clear_regular_diagnostics();

            // Run initial build, before entering the watch changes loop
            if let Err(error) = self.build_projects(&mut compiler_state, &setup_event) {
                self.publish_errors(&error, "lsp_state_error");
            }
            set_ready_status(&self.sender);

            setup_event.stop(timer);
            self.perf_logger.complete_event(setup_event);
            self.perf_logger.flush();

            info!("LSP server initialization completed!");

            // Here we will wait for changes from watchman
            'inner: loop {
                self.notify.notified().await;

                // Source control update started, we can ignore all pending changes, and wait for it to complete,
                // we may change the status bar to `Source Control Update...`
                if compiler_state.source_control_update_status.is_started() {
                    update_in_progress_status(
                        "Relay: hg update...",
                        Some("Waiting for source control update"),
                        &self.sender,
                    );
                    continue 'inner;
                }

                // SC Update completed, we need to abort current subscription, and re-initialize resource for LSP
                if compiler_state.source_control_update_status.is_completed() {
                    debug!("Watchman indicated the the source control update has completed!");
                    subscription_handle.abort();
                    continue 'outer;
                }

                let log_event = self.perf_logger.create_event("lsp_state_watchman_event");
                let log_time = log_event.start("lsp_state_watchman_event_time");

                if let Err(error) = self.incremental_build(&mut compiler_state, &log_event) {
                    self.publish_errors(&error, "lsp_state_user_error");
                }
                set_ready_status(&self.sender);

                log_event.stop(log_time);
                self.perf_logger.complete_event(log_event);
                self.perf_logger.flush();
            }
        }
    }

    fn incremental_build(
        &self,
        compiler_state: &mut CompilerState,
        log_event: &impl PerfLogEvent,
    ) -> Result<(), Error> {
        let has_new_changes = compiler_state.merge_file_source_changes(
            &self.config,
            log_event,
            self.perf_logger.as_ref(),
            false,
        )?;

        // Rebuild if there are pending files or if a new project is activated
        if has_new_changes
            || self
                .project_status
                .iter()
                .any(|r| r.value() == &ProjectStatus::Activated)
        {
            info!("LSP server detected changes...");

            self.diagnostic_reporter.clear_regular_diagnostics();

            update_in_progress_status(
                "Relay: checking...",
                Some("Validating changes, and updating source programs with the latest changes."),
                &self.sender,
            );

            self.build_projects(compiler_state, log_event)?;
        }

        Ok(())
    }

    fn build_projects(
        &self,
        compiler_state: &mut CompilerState,
        log_event: &impl PerfLogEvent,
    ) -> Result<(), Error> {
        let graphql_asts = log_event.time("parse_sources_time", || {
            GraphQLAsts::from_graphql_sources_map(
                &compiler_state.graphql_sources,
                &compiler_state.get_dirty_definitions(&self.config),
            )
        })?;

        if compiler_state.should_cancel_current_build() {
            debug!("Build is cancelled: new file changes are pending.");
            return Err(Error::Cancelled);
        }

        let timer = log_event.start("build_lsp_projects");
        let build_results: Vec<_> = self
            .config
            .par_enabled_projects()
            .filter(|project_config| {
                // Filter inactive projects
                if !self.project_status.contains_key(&project_config.name) {
                    return false;
                }
                // When the source programs is empty, we need to compile all source programs once
                if !self.source_programs.contains_key(&project_config.name) {
                    return true;
                }
                if let Some(base) = project_config.base {
                    if compiler_state.project_has_pending_changes(base) {
                        return true;
                    }
                }
                compiler_state.project_has_pending_changes(project_config.name)
            })
            .map(|project_config| self.build_project(project_config, compiler_state, &graphql_asts))
            .collect();
        log_event.stop(timer);

        let mut errors = vec![];
        for build_result in build_results {
            match build_result {
                Err(BuildProjectFailure::Error(err)) => {
                    errors.push(err);
                }
                Ok(project_name) => {
                    compiler_state.complete_project_compilation(&project_name);
                }
                _ => {}
            }
        }
        if errors.is_empty() {
            Ok(())
        } else {
            Err(Error::BuildProjectsErrors { errors })
        }
    }

    fn build_project(
        &self,
        project_config: &ProjectConfig,
        compiler_state: &CompilerState,
        graphql_asts: &FnvHashMap<SourceSetName, GraphQLAsts>,
    ) -> Result<StringKey, BuildProjectFailure> {
        self.project_status
            .insert(project_config.name, ProjectStatus::Completed);
        let log_event = self.perf_logger.create_event("build_lsp_project");
        let project_name = project_config.name;
        let build_time = log_event.start("build_lsp_project_time");
        log_event.string("project", project_name.to_string());

        let schema = log_event.time("build_schema_time", || {
            self.build_schema(compiler_state, project_config)
        })?;

        self.build_programs(
            project_config,
            compiler_state,
            graphql_asts,
            schema,
            &log_event,
        )?;

        log_event.stop(build_time);
        Ok(project_name)
    }

    fn build_schema(
        &self,
        compiler_state: &CompilerState,
        project_config: &ProjectConfig,
    ) -> Result<Arc<SDLSchema>, BuildProjectFailure> {
        match self.schemas.entry(project_config.name) {
            Entry::Vacant(e) => {
                let schema = build_schema(compiler_state, project_config).map_err(|errors| {
                    BuildProjectFailure::Error(BuildProjectError::ValidationErrors { errors })
                })?;
                e.insert(Arc::clone(&schema));
                Ok(schema)
            }
            Entry::Occupied(mut e) => {
                if !compiler_state.project_has_pending_schema_changes(project_config.name) {
                    Ok(Arc::clone(e.get()))
                } else {
                    let schema =
                        build_schema(compiler_state, project_config).map_err(|errors| {
                            debug!("build error");
                            BuildProjectFailure::Error(BuildProjectError::ValidationErrors {
                                errors,
                            })
                        })?;
                    e.insert(Arc::clone(&schema));
                    Ok(schema)
                }
            }
        }
    }

    fn build_programs(
        &self,
        project_config: &ProjectConfig,
        compiler_state: &CompilerState,
        graphql_asts: &FnvHashMap<SourceSetName, GraphQLAsts>,
        schema: Arc<SDLSchema>,
        log_event: &impl PerfLogEvent,
    ) -> Result<(), BuildProjectFailure> {
        let is_incremental_build = self.source_programs.contains_key(&project_config.name)
            && compiler_state.has_processed_changes()
            && !compiler_state.has_breaking_schema_change(project_config.name)
            && if let Some(base) = project_config.base {
                !compiler_state.has_breaking_schema_change(base)
            } else {
                true
            };

        let (base_program, base_fragment_names, _) = build_raw_program(
            project_config,
            graphql_asts,
            schema,
            log_event,
            is_incremental_build,
        )?;

        if compiler_state.should_cancel_current_build() {
            debug!("Build is cancelled: updates in source code/or new file changes are pending.");
            return Err(BuildProjectFailure::Cancelled);
        }

        match self.source_programs.entry(project_config.name) {
            Entry::Vacant(e) => {
                e.insert(base_program.clone());
            }
            Entry::Occupied(mut e) => {
                let program = e.get_mut();
                let removed_definition_names = graphql_asts
                    .get(&project_config.name)
                    .map(|ast| ast.removed_definition_names.as_ref());
                program.merge_program(&base_program, removed_definition_names);
            }
        }

        validate_program(&self.config, &base_program, log_event)?;

        transform_program(
            &self.config,
            project_config,
            Arc::new(base_program),
            Arc::new(base_fragment_names),
            Arc::clone(&self.perf_logger),
            log_event,
        )?;
        Ok(())
    }

    fn watchman_subscription_handler(
        &self,
        mut file_source_subscription: FileSourceSubscription,
        pending_file_source_changes: Arc<RwLock<Vec<FileSourceResult>>>,
        source_code_update_status: Arc<SourceControlUpdateStatus>,
    ) -> JoinHandle<()> {
        let notify_sender = self.notify.clone();
        task::spawn(async move {
            loop {
                match file_source_subscription.next_change().await {
                    Err(_) => {
                        // do nothing? compiler should panic, and restart the lsp
                    }
                    Ok(FileSourceSubscriptionNextChange::None) => {}
                    Ok(FileSourceSubscriptionNextChange::SourceControlUpdateEnter) => {
                        source_code_update_status.mark_as_started();
                    }
                    Ok(FileSourceSubscriptionNextChange::SourceControlUpdateLeave) => {
                        source_code_update_status.set_to_default();
                    }
                    Ok(FileSourceSubscriptionNextChange::SourceControlUpdate) => {
                        source_code_update_status.mark_as_completed();
                        notify_sender.notify_one();
                        break;
                    }
                    Ok(FileSourceSubscriptionNextChange::Result(file_source_changes)) => {
                        pending_file_source_changes
                            .write()
                            .expect("LSPState::watch_and_update_schemas: expect to acquire write lock on pending_file_source_changes")
                            .push(file_source_changes);

                        notify_sender.notify_one();
                    }
                }
            }
        })
    }

    fn log_errors(&self, log_event_name: &str, error: &Error) {
        let error_event = self.perf_logger.create_event(log_event_name);
        error_event.string("error", error.to_string());
        self.perf_logger.complete_event(error_event);
    }

    /// Log errors and report the diagnostics to IDE
    fn publish_errors(&self, error: &Error, log_event_name: &str) {
        self.diagnostic_reporter.report_error(&error);
        self.diagnostic_reporter.commit_diagnostics();
        self.log_errors(log_event_name, error)
    }
}
<|endoftext|>