pub fn link(
    obj_path: &Path,
    path: &str,
    static_lib: bool, // prefer static library instead of dynamic library
    platform: Platform,
) -> std::result::Result<(), Box<dyn std::error::Error>> {
    // Find the standard library
    let lib_file = match platform {
        Platform::Windows => "chocopy_rs_std.lib",
        Platform::Linux | Platform::Macos => "libchocopy_rs_std.a",
    };

    let mut lib_path = std::env::current_exe()?;
    lib_path.set_file_name(lib_file);

    // Invoke the linker
    let ld_output = match platform {
        Platform::Windows => {
            let vcvarsall = (|| -> Option<PathBuf> {
                let linker = cc::windows_registry::find_tool("x86_64-pc-windows-msvc", "link.exe")?;
                Some(
                    linker
                        .path()
                        .ancestors()
                        .nth(7)?
                        .join("Auxiliary")
                        .join("Build")
                        .join("vcvarsall.bat"),
                )
            })()
            .ok_or(ToolChainError)?;

            let libs = if static_lib {
                "libvcruntime.lib libucrt.lib libcmt.lib"
            } else {
                "vcruntime.lib ucrt.lib msvcrt.lib"
            };

            // We need to execute vcvarsall.bat, then link.exe with the
            // inherited environment variables.
            // However, the syntax for chained execution in `cmd` is not in the
            // standard escaping format, and rust std::process::Command doesn't
            // support it. To work around this, we make a temporary batch file
            // with the commands we want, and execute that batch file.
            let batch_content = format!(
                "@echo off
    call \"{}\" amd64
    link /NOLOGO /NXCOMPAT /OPT:REF,NOICF \
    \"{}\" \"{}\" /OUT:\"{}\" \
    kernel32.lib advapi32.lib ws2_32.lib userenv.lib {} \
    /SUBSYSTEM:CONSOLE /DEBUG",
                windows_path_escape(&vcvarsall)?,
                windows_path_escape(&obj_path)?,
                windows_path_escape(&lib_path)?,
                windows_path_escape(Path::new(path))?,
                libs
            );

            let mut bat_path = std::env::temp_dir();
            let bat_name = format!("chocopy-{}.bat", rand::random::<u32>());
            bat_path.push(bat_name);

            std::fs::write(&bat_path, batch_content)?;

            let ld_output = std::process::Command::new("cmd")
                .args(&[OsStr::new("/c"), bat_path.as_os_str()])
                .output()?;
            std::fs::remove_file(&bat_path)?;
            ld_output
        }
        Platform::Linux | Platform::Macos => {
            let mut command = std::process::Command::new("cc");
            command.args(&[
                OsStr::new("-o"),
                OsStr::new(path),
                obj_path.as_os_str(),
                lib_path.as_os_str(),
                OsStr::new("-pthread"),
                OsStr::new("-ldl"),
            ]);
            if static_lib {
                command.arg("-static");
            }
            command.output()?
        }
    };

    if !ld_output.status.success() {
        eprintln!("Error: Linker returned {}", ld_output.status);
        if !ld_output.stdout.is_empty() {
            eprintln!("STDOUT from linker:");
            std::io::stderr().write_all(&ld_output.stdout).unwrap();
        }
        if !ld_output.stderr.is_empty() {
            eprintln!("STDERR from linker:");
            std::io::stderr().write_all(&ld_output.stderr).unwrap();
        }
    }

    Ok(())
}
// Link the object file with libraries to produce an executable
Link the object file with libraries to produce an executable
Link the object file with libraries to produce an executable
pub fn link(
    obj_path: &Path,
    path: &str,
    static_lib: bool, // prefer static library instead of dynamic library
    platform: Platform,
) -> std::result::Result<(), Box<dyn std::error::Error>> {
    // Find the standard library
    let lib_file = match platform {
        Platform::Windows => "chocopy_rs_std.lib",
        Platform::Linux | Platform::Macos => "libchocopy_rs_std.a",
    };
    let mut lib_path = std::env::current_exe()?;
    lib_path.set_file_name(lib_file);
    // Invoke the linker
    let ld_output = match platform {
        Platform::Windows => {
            let vcvarsall = (|| -> Option<PathBuf> {
                let linker = cc::windows_registry::find_tool("x86_64-pc-windows-msvc", "link.exe")?;
                Some(
                    linker
                        .path()
                        .ancestors()
                        .nth(7)?
                        .join("Auxiliary")
                        .join("Build")
                        .join("vcvarsall.bat"),
                )
            })()
            .ok_or(ToolChainError)?;
            let libs = if static_lib {
                "libvcruntime.lib libucrt.lib libcmt.lib"
            } else {
                "vcruntime.lib ucrt.lib msvcrt.lib"
            };
            // We need to execute vcvarsall.bat, then link.exe with the
            // inherited environment variables.
            // However, the syntax for chained execution in `cmd` is not in the
            // standard escaping format, and rust std::process::Command doesn't
            // support it. To work around this, we make a temporary batch file
            // with the commands we want, and execute that batch file.
            let batch_content = format!(
                "@echo off
    call \"{}\" amd64
    link /NOLOGO /NXCOMPAT /OPT:REF,NOICF \
    \"{}\" \"{}\" /OUT:\"{}\" \
    kernel32.lib advapi32.lib ws2_32.lib userenv.lib {} \
    /SUBSYSTEM:CONSOLE /DEBUG",
                windows_path_escape(&vcvarsall)?,
                windows_path_escape(&obj_path)?,
                windows_path_escape(&lib_path)?,
                windows_path_escape(Path::new(path))?,
                libs
            );
            let mut bat_path = std::env::temp_dir();
            let bat_name = format!("chocopy-{}.bat", rand::random::<u32>());
            bat_path.push(bat_name);
            std::fs::write(&bat_path, batch_content)?;
            let ld_output = std::process::Command::new("cmd")
                .args(&[OsStr::new("/c"), bat_path.as_os_str()])
                .output()?;
            std::fs::remove_file(&bat_path)?;
            ld_output
        }
        Platform::Linux | Platform::Macos => {
            let mut command = std::process::Command::new("cc");
            command.args(&[
                OsStr::new("-o"),
                OsStr::new(path),
                obj_path.as_os_str(),
                lib_path.as_os_str(),
                OsStr::new("-pthread"),
                OsStr::new("-ldl"),
            ]);
            if static_lib {
                command.arg("-static");
            }
            command.output()?
        }
    };
    if !ld_output.status.success() {
        eprintln!("Error: Linker returned {}", ld_output.status);
        if !ld_output.stdout.is_empty() {
            eprintln!("STDOUT from linker:");
            std::io::stderr().write_all(&ld_output.stdout).unwrap();
        }
        if !ld_output.stderr.is_empty() {
            eprintln!("STDERR from linker:");
            std::io::stderr().write_all(&ld_output.stderr).unwrap();
        }
    }
    Ok(())
}
pub fn link ( obj_path : & Path , path : & str , static_lib : bool , // prefer static library instead of dynamic library platform : Platform , ) -> std :: result :: Result < ( ) , Box < dyn std :: error :: Error > > { // Find the standard library let lib_file = match platform { Platform :: Windows => " " , Platform :: Linux | Platform :: Macos => " " , } ; let mut lib_path = std :: env :: current_exe ( ) ? ; lib_path . set_file_name ( lib_file ) ; // Invoke the linker let ld_output = match platform { Platform :: Windows => { let vcvarsall = ( | | -> Option < PathBuf > { let linker = cc :: windows_registry :: find_tool ( " " , " " ) ? ; Some ( linker . path ( ) . ancestors ( ) . nth ( 7 ) ? . join ( " " ) . join ( " " ) . join ( " " ) , ) } ) ( ) . ok_or ( ToolChainError ) ? ; let libs = if static_lib { " " } else { " " } ; // We need to execute vcvarsall.bat, then link.exe with the // inherited environment variables. // However, the syntax for chained execution in `cmd` is not in the // standard escaping format, and rust std::process::Command doesn't // support it. To work around this, we make a temporary batch file // with the commands we want, and execute that batch file. let batch_content = format ! ( " \" \" \
 \" \" \" \" \" \" \
 \
 " windows_path_escape ( vcvarsall ) windows_path_escape ( obj_path ) windows_path_escape ( lib_path ) windows_path_escape ( Path new ( path ) ) libs ) ; let mut bat_path = std :: env :: temp_dir ( ) ; let bat_name = format ! ( " " rand random u32 ( ) ) ; bat_path . push ( bat_name ) ; std :: fs :: write ( & bat_path , batch_content ) ? ; let ld_output = std :: process :: Command :: new ( " " ) . args ( & [ OsStr :: new ( " " ) , bat_path . as_os_str ( ) ] ) . output ( ) ? ; std :: fs :: remove_file ( & bat_path ) ? ; ld_output } Platform :: Linux | Platform :: Macos => { let mut command = std :: process :: Command :: new ( " " ) ; command . args ( & [ OsStr :: new ( " " ) , OsStr :: new ( path ) , obj_path . as_os_str ( ) , lib_path . as_os_str ( ) , OsStr :: new ( " " ) , OsStr :: new ( " " ) , ] ) ; if static_lib { command . arg ( " " ) ; } command . output ( ) ? } } ; if ! ld_output . status . success ( ) { eprintln ! ( " " ld_output status ) ; if ! ld_output . stdout . is_empty ( ) { eprintln ! ( " " ) ; std :: io :: stderr ( ) . write_all ( & ld_output . stdout ) . unwrap ( ) ; } if ! ld_output . stderr . is_empty ( ) { eprintln ! ( " " ) ; std :: io :: stderr ( ) . write_all ( & ld_output . stderr ) . unwrap ( ) ; } } Ok ( ( ) ) }
Link the object file with libraries to produce an executable
Link the object file with libraries to produce an executable
obj_path
&Path
path
&str
static_lib
bool
platform
Platform