async fn make(
    config_path: &Path,
    config: &Config,
    ditto_version: &Version,
    include_test_sources: bool,
) -> Result<WhatHappened> {
    let (build_ninja, get_warnings) =
        generate_build_ninja(config_path, config, ditto_version, include_test_sources).map_err(
            |err| {
                // This is a bit brittle, but we want parse errors encountered during
                // build planning to be indistinguishable from parse errors encountered
                // during the actual build
                if err.root_cause().to_string() == "syntax error" {
                    //                                  ^^ BEWARE relying on this string is brittle!
                    err
                } else {
                    err.wrap_err("error generating build.ninja")
                }
            },
        )?;

    trace!("build.ninja generated");

    let mut build_ninja_path = config.ditto_dir.to_path_buf();
    build_ninja_path.push("build");
    build_ninja_path.set_extension("ninja");

    {
        if !config.ditto_dir.exists() {
            fs::create_dir_all(&config.ditto_dir)
                .into_diagnostic()
                .wrap_err(format!(
                    "error creating {}",
                    config.ditto_dir.to_string_lossy()
                ))?;
        }

        let mut handle = fs::File::create(&build_ninja_path)
            .into_diagnostic()
            .wrap_err(format!(
                "error creating ninja build file: {:?}",
                build_ninja_path.to_string_lossy()
            ))?;

        handle
            .write_all(build_ninja.into_syntax().as_bytes())
            .into_diagnostic()
            .wrap_err(format!(
                "error writing {:?}",
                build_ninja_path.to_string_lossy()
            ))?;

        debug!(
            "build.ninja written to {:?}",
            build_ninja_path.to_string_lossy()
        );
    }

    static NINJA_STATUS_MESSAGE: &str = "__NINJA";

    let ninja_exe = get_ninja_exe().await?;
    let mut child = process::Command::new(&ninja_exe)
        .arg("-f")
        .arg(&build_ninja_path)
        .stdout(Stdio::piped())
        // Mark ninja status messages so we can push them to our own progress spinner
        .env("NINJA_STATUS", NINJA_STATUS_MESSAGE)
        // Don't strip color codes, we'll handle that
        // https://github.com/ninja-build/ninja/commit/bf7107bb864d0383028202e3f4a4228c02302961
        .env("CLICOLOR_FORCE", "1")
        // Pass `is_plain` logic down to CLI calls made by ninja
        .env("DITTO_PLAIN", common::is_plain().to_string())
        .envs(if let Ok(log_dir) = std::env::var("DITTO_LOG_DIR") {
            vec![("DITTO_LOG_DIR", log_dir)]
        } else {
            vec![]
        })
        .spawn()
        .into_diagnostic()
        .wrap_err(format!(
            "error running ninja: {} -f {}",
            ninja_exe,
            build_ninja_path.to_string_lossy()
        ))?;

    let stdout = child.stdout.as_mut().unwrap();
    let stdout_reader = BufReader::new(stdout);
    let mut stdout_lines = stdout_reader.lines();
    if let Some(Ok(first_line)) = stdout_lines.next() {
        // NOTE relying on the format of ninja messages like this could break
        // if DITTO_NINJA is set to a ninja version with a different format
        if first_line.starts_with("ninja: no work to do") {
            // Nothing to do,
            // still need to print warnings though
            let warnings = get_warnings()?;
            let warnings_printed = print_warnings(warnings);

            let ninja_exit_status = child
                .wait()
                .into_diagnostic()
                .wrap_err("ninja wasn't running?")?;

            return Ok(WhatHappened::Nothing {
                ninja_exit_status,
                warnings_printed,
            });
        } else {
            let mut spinner = Spinner::new();
            spinner.set_message(
                first_line
                    .trim_start_matches(NINJA_STATUS_MESSAGE)
                    .to_owned(),
            );

            // Our error/warning reports generally start with a blank line,
            // so we need to replicate that behavior when forwarding ninja
            // output for a consistent experience.
            let mut printed_initial_newline = false;
            while let Some(Ok(line)) = stdout_lines.next() {
                if line.starts_with(NINJA_STATUS_MESSAGE) {
                    spinner.set_message(line.trim_start_matches(NINJA_STATUS_MESSAGE).to_owned());
                } else if line.starts_with("ninja: build stopped: subcommand failed") {
                } else if console::strip_ansi_codes(&line).starts_with("FAILED") {
                    // The following line prints the command that was run (and failed)
                    // so swallow it
                    stdout_lines.next();
                } else {
                    if !printed_initial_newline {
                        spinner.println("\n");
                        printed_initial_newline = true
                    }
                    spinner.println(line);
                }
            }

            let ninja_exit_status = child.wait().expect("error waiting for ninja to exit");
            spinner.finish();
            if ninja_exit_status.success() {
                // Only print warnings if there wasn't an error
                let warnings = get_warnings()?;
                let warnings_printed = print_warnings(warnings);
                return Ok(WhatHappened::Success { warnings_printed });
            } else {
                return Ok(WhatHappened::Error { ninja_exit_status });
            }
        }
    } else {
        unreachable!()
    }

    fn print_warnings(warnings: Vec<miette::Report>) -> bool {
        if !warnings.is_empty() {
            let warnings_len = warnings.len();
            for (i, warning) in warnings.into_iter().enumerate() {
                if i == warnings_len - 1 {
                    eprintln!("{:?}", warning);
                } else {
                    eprint!("{:?}", warning);
                }
            }
            true
        } else {
            false
        }
    }
}
/// If successful returns the exit status of `ninja` and whether anything actually happened.
If successful returns the exit status of `ninja` and whether anything actually happened.
If successful returns the exit status of ` ninja ` and whether anything actually happened .
async fn make(
    config_path: &Path,
    config: &Config,
    ditto_version: &Version,
    include_test_sources: bool,
) -> Result<WhatHappened> {
    let (build_ninja, get_warnings) =
        generate_build_ninja(config_path, config, ditto_version, include_test_sources).map_err(
            |err| {
                // This is a bit brittle, but we want parse errors encountered during
                // build planning to be indistinguishable from parse errors encountered
                // during the actual build
                if err.root_cause().to_string() == "syntax error" {
                    //                                  ^^ BEWARE relying on this string is brittle!
                    err
                } else {
                    err.wrap_err("error generating build.ninja")
                }
            },
        )?;
    trace!("build.ninja generated");
    let mut build_ninja_path = config.ditto_dir.to_path_buf();
    build_ninja_path.push("build");
    build_ninja_path.set_extension("ninja");
    {
        if !config.ditto_dir.exists() {
            fs::create_dir_all(&config.ditto_dir)
                .into_diagnostic()
                .wrap_err(format!(
                    "error creating {}",
                    config.ditto_dir.to_string_lossy()
                ))?;
        }
        let mut handle = fs::File::create(&build_ninja_path)
            .into_diagnostic()
            .wrap_err(format!(
                "error creating ninja build file: {:?}",
                build_ninja_path.to_string_lossy()
            ))?;
        handle
            .write_all(build_ninja.into_syntax().as_bytes())
            .into_diagnostic()
            .wrap_err(format!(
                "error writing {:?}",
                build_ninja_path.to_string_lossy()
            ))?;
        debug!(
            "build.ninja written to {:?}",
            build_ninja_path.to_string_lossy()
        );
    }
    static NINJA_STATUS_MESSAGE: &str = "__NINJA";
    let ninja_exe = get_ninja_exe().await?;
    let mut child = process::Command::new(&ninja_exe)
        .arg("-f")
        .arg(&build_ninja_path)
        .stdout(Stdio::piped())
        // Mark ninja status messages so we can push them to our own progress spinner
        .env("NINJA_STATUS", NINJA_STATUS_MESSAGE)
        // Don't strip color codes, we'll handle that
        // https://github.com/ninja-build/ninja/commit/bf7107bb864d0383028202e3f4a4228c02302961
        .env("CLICOLOR_FORCE", "1")
        // Pass `is_plain` logic down to CLI calls made by ninja
        .env("DITTO_PLAIN", common::is_plain().to_string())
        .envs(if let Ok(log_dir) = std::env::var("DITTO_LOG_DIR") {
            vec![("DITTO_LOG_DIR", log_dir)]
        } else {
            vec![]
        })
        .spawn()
        .into_diagnostic()
        .wrap_err(format!(
            "error running ninja: {} -f {}",
            ninja_exe,
            build_ninja_path.to_string_lossy()
        ))?;
    let stdout = child.stdout.as_mut().unwrap();
    let stdout_reader = BufReader::new(stdout);
    let mut stdout_lines = stdout_reader.lines();
    if let Some(Ok(first_line)) = stdout_lines.next() {
        // NOTE relying on the format of ninja messages like this could break
        // if DITTO_NINJA is set to a ninja version with a different format
        if first_line.starts_with("ninja: no work to do") {
            // Nothing to do,
            // still need to print warnings though
            let warnings = get_warnings()?;
            let warnings_printed = print_warnings(warnings);
            let ninja_exit_status = child
                .wait()
                .into_diagnostic()
                .wrap_err("ninja wasn't running?")?;
            return Ok(WhatHappened::Nothing {
                ninja_exit_status,
                warnings_printed,
            });
        } else {
            let mut spinner = Spinner::new();
            spinner.set_message(
                first_line
                    .trim_start_matches(NINJA_STATUS_MESSAGE)
                    .to_owned(),
            );
            // Our error/warning reports generally start with a blank line,
            // so we need to replicate that behavior when forwarding ninja
            // output for a consistent experience.
            let mut printed_initial_newline = false;
            while let Some(Ok(line)) = stdout_lines.next() {
                if line.starts_with(NINJA_STATUS_MESSAGE) {
                    spinner.set_message(line.trim_start_matches(NINJA_STATUS_MESSAGE).to_owned());
                } else if line.starts_with("ninja: build stopped: subcommand failed") {
                } else if console::strip_ansi_codes(&line).starts_with("FAILED") {
                    // The following line prints the command that was run (and failed)
                    // so swallow it
                    stdout_lines.next();
                } else {
                    if !printed_initial_newline {
                        spinner.println("\n");
                        printed_initial_newline = true
                    }
                    spinner.println(line);
                }
            }
            let ninja_exit_status = child.wait().expect("error waiting for ninja to exit");
            spinner.finish();
            if ninja_exit_status.success() {
                // Only print warnings if there wasn't an error
                let warnings = get_warnings()?;
                let warnings_printed = print_warnings(warnings);
                return Ok(WhatHappened::Success { warnings_printed });
            } else {
                return Ok(WhatHappened::Error { ninja_exit_status });
            }
        }
    } else {
        unreachable!()
    }
    fn print_warnings(warnings: Vec<miette::Report>) -> bool {
        if !warnings.is_empty() {
            let warnings_len = warnings.len();
            for (i, warning) in warnings.into_iter().enumerate() {
                if i == warnings_len - 1 {
                    eprintln!("{:?}", warning);
                } else {
                    eprint!("{:?}", warning);
                }
            }
            true
        } else {
            false
        }
    }
}
async fn make ( config_path : & Path , config : & Config , ditto_version : & Version , include_test_sources : bool , ) -> Result < WhatHappened > { let ( build_ninja , get_warnings ) = generate_build_ninja ( config_path , config , ditto_version , include_test_sources ) . map_err ( | err | { // This is a bit brittle, but we want parse errors encountered during // build planning to be indistinguishable from parse errors encountered // during the actual build if err . root_cause ( ) . to_string ( ) == " " { //                                  ^^ BEWARE relying on this string is brittle! err } else { err . wrap_err ( " " ) } } , ) ? ; trace ! ( " " ) ; let mut build_ninja_path = config . ditto_dir . to_path_buf ( ) ; build_ninja_path . push ( " " ) ; build_ninja_path . set_extension ( " " ) ; { if ! config . ditto_dir . exists ( ) { fs :: create_dir_all ( & config . ditto_dir ) . into_diagnostic ( ) . wrap_err ( format ! ( " " config ditto_dir to_string_lossy ( ) ) ) ? ; } let mut handle = fs :: File :: create ( & build_ninja_path ) . into_diagnostic ( ) . wrap_err ( format ! ( " " build_ninja_path to_string_lossy ( ) ) ) ? ; handle . write_all ( build_ninja . into_syntax ( ) . as_bytes ( ) ) . into_diagnostic ( ) . wrap_err ( format ! ( " " build_ninja_path to_string_lossy ( ) ) ) ? ; debug ! ( " " build_ninja_path to_string_lossy ( ) ) ; } static NINJA_STATUS_MESSAGE : & str = " " ; let ninja_exe = get_ninja_exe ( ) . await ? ; let mut child = process :: Command :: new ( & ninja_exe ) . arg ( " " ) . arg ( & build_ninja_path ) . stdout ( Stdio :: piped ( ) ) // Mark ninja status messages so we can push them to our own progress spinner . env ( " " , NINJA_STATUS_MESSAGE ) // Don't strip color codes, we'll handle that // https://github.com/ninja-build/ninja/commit/bf7107bb864d0383028202e3f4a4228c02302961 . env ( " " , " " ) // Pass `is_plain` logic down to CLI calls made by ninja . env ( " " , common :: is_plain ( ) . to_string ( ) ) . envs ( if let Ok ( log_dir ) = std :: env :: var ( " " ) { vec ! [ ( " " log_dir ) ] } else { vec ! [ ] } ) . spawn ( ) . into_diagnostic ( ) . wrap_err ( format ! ( " " ninja_exe build_ninja_path to_string_lossy ( ) ) ) ? ; let stdout = child . stdout . as_mut ( ) . unwrap ( ) ; let stdout_reader = BufReader :: new ( stdout ) ; let mut stdout_lines = stdout_reader . lines ( ) ; if let Some ( Ok ( first_line ) ) = stdout_lines . next ( ) { // NOTE relying on the format of ninja messages like this could break // if DITTO_NINJA is set to a ninja version with a different format if first_line . starts_with ( " " ) { // Nothing to do, // still need to print warnings though let warnings = get_warnings ( ) ? ; let warnings_printed = print_warnings ( warnings ) ; let ninja_exit_status = child . wait ( ) . into_diagnostic ( ) . wrap_err ( " " ) ? ; return Ok ( WhatHappened :: Nothing { ninja_exit_status , warnings_printed , } ) ; } else { let mut spinner = Spinner :: new ( ) ; spinner . set_message ( first_line . trim_start_matches ( NINJA_STATUS_MESSAGE ) . to_owned ( ) , ) ; // Our error/warning reports generally start with a blank line, // so we need to replicate that behavior when forwarding ninja // output for a consistent experience. let mut printed_initial_newline = false ; while let Some ( Ok ( line ) ) = stdout_lines . next ( ) { if line . starts_with ( NINJA_STATUS_MESSAGE ) { spinner . set_message ( line . trim_start_matches ( NINJA_STATUS_MESSAGE ) . to_owned ( ) ) ; } else if line . starts_with ( " " ) { } else if console :: strip_ansi_codes ( & line ) . starts_with ( " " ) { // The following line prints the command that was run (and failed) // so swallow it stdout_lines . next ( ) ; } else { if ! printed_initial_newline { spinner . println ( " \n " ) ; printed_initial_newline = true } spinner . println ( line ) ; } } let ninja_exit_status = child . wait ( ) . expect ( " " ) ; spinner . finish ( ) ; if ninja_exit_status . success ( ) { // Only print warnings if there wasn't an error let warnings = get_warnings ( ) ? ; let warnings_printed = print_warnings ( warnings ) ; return Ok ( WhatHappened :: Success { warnings_printed } ) ; } else { return Ok ( WhatHappened :: Error { ninja_exit_status } ) ; } } } else { unreachable ! ( ) } fn print_warnings ( warnings : Vec < miette :: Report > ) -> bool { if ! warnings . is_empty ( ) { let warnings_len = warnings . len ( ) ; for ( i , warning ) in warnings . into_iter ( ) . enumerate ( ) { if i == warnings_len - 1 { eprintln ! ( " " warning ) ; } else { eprint ! ( " " warning ) ; } } true } else { false } } }
If successful returns the exit status of `ninja` and whether anything actually happened.
If successful returns the exit status of ` ninja ` and whether anything actually happened .
config_path
&Path
config
&Config
ditto_version
&Version
include_test_sources
bool
config_path
&Path
config
&Config
ditto_version
&Version
include_test_sources
bool