diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0a28d96..5555ac9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,229 +2,130 @@ name: Tests on: push: - branches: - - main - - master + branches: [main, master] pull_request: - branches: - - main - - master - -# cargo error handling is wrapped by ci-cargo, for simplicity and better logging. -# To use ci-cargo in a step, source ci-cargo.ps1 (using `. ./ci-cargo.ps`, note the extra dot in front). -# ci-cargo does not handle correctly -- separator, so wrap it in quotes ('--') -# A special named parameter called `ActionName` can be used to set the name of current ci-cargo action, -# used as `ci-cargo .... -ActionName "Called for documentation purposes"`. + branches: [main, master] + jobs: - # All tests - tests: + Crate-check: runs-on: ${{ matrix.config.os }} - - name: ${{ matrix.config.os }} (R-${{ matrix.config.r }} rust-${{ matrix.config.rust-version }}) - + name: Crate-check ${{ matrix.config.os }} (${{ matrix.config.r }} - ${{ matrix.config.rust-version }}) + strategy: fail-fast: false matrix: config: - - { - os: windows-latest, - r: "release", - rust-version: "stable-msvc", - rtools-version: "44", - features: "full-functionality", - } - - { - os: windows-latest, - r: "devel", - rust-version: "stable-msvc", - rtools-version: "44", - features: "default", - } - - { - os: windows-latest, - r: "oldrel", - rust-version: "stable-msvc", - rtools-version: "43", - features: "default", - } - - - { - os: macOS-latest, - r: "release", - rust-version: "stable", - features: "full-functionality", - } - - { - os: ubuntu-latest, - r: "release", - rust-version: "stable", - features: "full-functionality", - check_fmt: true, - } - - { - os: ubuntu-latest, - r: "release", - rust-version: "nightly", - features: "full-functionality", - extra-args: ["-Zdoctest-xcompile"], - } - # R-devel requires LD_LIBRARY_PATH - - { - os: ubuntu-latest, - r: "devel", - rust-version: "stable", - features: "default", - } - - { - os: ubuntu-latest, - r: "oldrel", - rust-version: "stable", - features: "default", - } + - {os: windows-latest, r: 'release', rust-version: 'stable-msvc', target: 'x86_64-pc-windows-gnu', rtools-version: '44'} + - {os: windows-latest, r: 'devel', rust-version: 'stable-msvc', target: 'x86_64-pc-windows-gnu', rtools-version: '44'} + - {os: macOS-latest, r: 'release', rust-version: 'stable' } + - {os: ubuntu-latest, r: 'release', rust-version: 'stable' } + - {os: ubuntu-latest, r: 'devel', rust-version: 'stable' } env: - R_REMOTES_NO_ERRORS_FROM_WARNINGS: true - - # This environment variable enables support for pseudo multi-target cargo builds. - # Current stable Rust does not support multi-targeting, - # see https://github.com/rust-lang/cargo/issues/8176 - # The variable is treated as a comma-separated list of valid Rust targets. - # 'default' value emits no '--target' flag. - # E.g.: BUILD_TARGETS=i686-pc-windows-gnu,x86_64-pc-windows-gnu builds two times, - # each time providing '--target=*-pc-windows-gnu' flag to cargo. - BUILD_TARGETS: default - + RSPM: ${{ matrix.config.rspm }} + GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} + # PowerShell core is available on all platforms and can be used to unify scripts defaults: run: shell: pwsh steps: - - uses: actions/checkout@v3 - - - name: Set up Rust - uses: dtolnay/rust-toolchain@master - with: - toolchain: ${{ matrix.config.rust-version }} - components: rustfmt, clippy - + - uses: actions/checkout@v4 + - name: Set up R uses: r-lib/actions/setup-r@v2 with: r-version: ${{ matrix.config.r }} + use-public-rspm: true rtools-version: ${{ matrix.config.rtools-version }} - # TODO: enable RSPM when all the packages are available - use-public-rspm: false - - - name: Set up Pandoc - uses: r-lib/actions/setup-pandoc@v2 - - - name: Configure Windows (R >= 4.2) - if: startsWith(runner.os, 'Windows') && matrix.config.r != '4.1' - run: | - if ("${{ matrix.config.rtools-version }}" -eq "42") { - $rtools_home = "C:\rtools42" # for R 4.2 - } else { - $rtools_home = "C:\rtools43" # for R >= 4.3 - } - - # c.f. https://github.com/wch/r-source/blob/f1501504df8df1668a57d3a1b6f80167f24441d3/src/library/profile/Rprofile.windows#L70-L71 - echo "${rtools_home}\x86_64-w64-mingw32.static.posix\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append ; - echo "${rtools_home}\usr\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append ; - echo "$(Rscript.exe -e 'cat(normalizePath(R.home()))')\bin\x64" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append ; - # Add target - rustup target add x86_64-pc-windows-gnu - echo "BUILD_TARGETS=x86_64-pc-windows-gnu" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append ; - - # The following lines add two tweaks: - # - # 1. Change the linker name to "x86_64-w64-mingw32.static.posix-gcc.exe". - # 2. Add empty libgcc_s.a and libgcc_eh.a, and add them to the compiler's - # library search paths via `LIBRARY_PATH` envvar. - # - # The first tweak is needed because Rtools42 doesn't contain - # "x86_64-w64-mingw32-gcc," which `rustc` uses as the default linker - # for the `x86_64-pc-windows-gnu` target. - # - # If we use the Rtools' toolchain, the second tweak is also required. - # `rustc` adds `-lgcc_eh` and `-lgcc_s` flags to the compiler, but - # Rtools' GCC doesn't have `libgcc_eh` or `libgcc_s` due to the - # compilation settings. So, in order to please the compiler, we need - # to add empty `libgcc_eh` or `libgcc_s` to the library search paths. - # - # For more details, please refer to https://github.com/r-windows/rtools-packages/blob/2407b23f1e0925bbb20a4162c963600105236318/mingw-w64-gcc/PKGBUILD#L313-L316 + - name: Set up Rust + uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ matrix.config.rust-version }} + components: rustfmt, clippy + targets: ${{ matrix.config.target }} + + # Stuff here lifted from libR-sys workflows with thanks to authors. + # All configurations for Windows go here + # 1. Configure linker + # 2. Create libgcc_eh mock + # 3. Add R bin path to PATH + # 4. Add Rtools' GCC to PATH (required to find linker) + # 5. Add include path (required to resolve standard headers like stdio.h) + - name: Configure Windows + if: runner.os == 'Windows' + run: | + # Configure linker + echo "RUSTFLAGS=-C linker=x86_64-w64-mingw32.static.posix-gcc.exe" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + # Create libgcc_eh mock New-Item -Path libgcc_mock -Type Directory New-Item -Path libgcc_mock\libgcc_eh.a -Type File New-Item -Path libgcc_mock\libgcc_s.a -Type File - - New-Item -Path .cargo -ItemType Directory -Force $pwd_slash = echo "${PWD}" | % {$_ -replace '\\','/'} - @" - [target.x86_64-pc-windows-gnu] - linker = "x86_64-w64-mingw32.static.posix-gcc.exe" - - [env] - LIBRARY_PATH = "${pwd_slash}/libgcc_mock" - "@ | Out-File -FilePath .cargo/config.toml -Encoding utf8 -Append ; - env: - RUST_TOOLCHAIN: ${{ matrix.config.rust-version }} - - # TODO: Remove this runner when we drop the support for R < 4.2 - - name: Configure Windows (R < 4.2) - if: startsWith(runner.os, 'Windows') && matrix.config.r == '4.1' - # 1. Add rust target - # 2. Add target name to the $targets variable - # 3. Add mingw32/mingw64 bin folders to PATH - # 4. Add R x64/i386 folders to PATH + echo "LIBRARY_PATH=${pwd_slash}/libgcc_mock" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + + # Add R bin path to PATH + echo "$(Rscript.exe -e 'cat(normalizePath(R.home()))')\bin\x64" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append ; + + # Add Rtools' GCC to PATH + if ($env:RTOOLS_VERSION -eq '44') { + $mingw_root = "C:\rtools44\x86_64-w64-mingw32.static.posix" + } elseif ($env:RTOOLS_VERSION -eq '43') { + $mingw_root = "C:\rtools43\x86_64-w64-mingw32.static.posix" + } elseif ($env:RTOOLS_VERSION -eq '42') { + $mingw_root = "C:\rtools42\x86_64-w64-mingw32.static.posix" + } + echo "$mingw_root\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + + # Add include path + echo "LIBRSYS_LIBCLANG_INCLUDE_PATH=$mingw_root\include" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + env: + RUST_TARGET: ${{ matrix.config.target }} + RTOOLS_VERSION: ${{ matrix.config.rtools-version }} + + + # macOS configurations, mainly llvm and path to libclang + # Because of this R installation issue on macOS-11.0 + # https://github.com/r-lib/actions/issues/200 + # Symlinks to R/Rscript are not properly set up, so we do it by hand, using this trick + # https://github.com/r-lib/ps/commit/a24f2c4d1bdba63be14e7729b9ab81d0ed9f719e + # Environment variables are required fir Mac-OS-11.0, see + # https://github.com/extendr/libR-sys/issues/35 + - name: Configure macOS + if: runner.os == 'macOS' run: | - $targets=@() - if ($env:RUST_TOOLCHAIN -notlike "*x86_64*") { - rustup target add i686-pc-windows-gnu ; - $targets+="i686-pc-windows-gnu" - echo "${env:RTOOLS40_HOME}\mingw32\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append ; - echo "$(Rscript.exe -e 'cat(normalizePath(R.home()))')\bin\i386" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append ; + brew install llvm + echo "LIBCLANG_PATH=$(brew --prefix llvm)/lib" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + $env:LLVM_CONFIG_PATH = "$(brew --prefix llvm)/bin/llvm-config" + echo "LLVM_CONFIG_PATH=$env:LLVM_CONFIG_PATH" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + echo "LIBRSYS_LIBCLANG_INCLUDE_PATH=$(. $env:LLVM_CONFIG_PATH --libdir)/clang/$(. $env:LLVM_CONFIG_PATH --version)/include" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + + if ((Get-ChildItem -Path /usr/local/bin -Filter R | Measure-Object).Count -eq 0) { + echo "::warning:: Found no R symlink in /usr/local/bin, setting up manually..." + ln -s /Library/Frameworks/R.framework/Versions/Current/Resources/bin/R /usr/local/bin/ } - if ($env:RUST_TOOLCHAIN -notlike "*i686*") { - rustup target add x86_64-pc-windows-gnu ; - $targets+="x86_64-pc-windows-gnu" - echo "${env:RTOOLS40_HOME}\mingw64\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append ; - echo "$(Rscript.exe -e 'cat(normalizePath(R.home()))')\bin\x64" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append ; + if ((Get-ChildItem -Path /usr/local/bin -Filter Rscript | Measure-Object).Count -eq 0) { + echo "::warning:: Found no Rscript symlink in /usr/local/bin, setting up manually..." + ln -s /Library/Frameworks/R.framework/Versions/Current/Resources/bin/Rscript /usr/local/bin/ } - echo "BUILD_TARGETS=$($targets -join ',')" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append ; - env: - RUST_TOOLCHAIN: ${{ matrix.config.rust-version }} # This is required for ubuntu r-devel - # 'Del alias:R' removes 'R' alias which prevents running R + # 'Del alias:R' removes R alias which prevents running R - name: Configure Linux - if: startsWith(runner.os, 'linux') + if: runner.os == 'linux' run: | Del alias:R echo "LD_LIBRARY_PATH=$(R -s -e 'cat(normalizePath(R.home()))')/lib" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append - # Check code formatting. As this doesn't depend on the platform, do this only on one platform. - - name: Check code formatting - if: matrix.config.check_fmt - run: cargo fmt -- --check + env: + RUST_TARGET: ${{ matrix.config.target }} - # For each target in the BUILD_TARGETS comma-separated list, run cargo build with appropriate target - # Required by Windows builds, does not affect other platforms - - name: Build - run: | - . ./ci-cargo.ps1 - foreach($target in ($env:BUILD_TARGETS).Split(',')) { - ci-cargo build $(if($target -ne 'default') {"--target=$target"} ) -ActionName "Building for $target target" - } - # For each target in the BUILD_TARGETS comma-separated list, run cargo test with appropriate target - # Required by Windows builds, does not affect other platforms - # ! ci-cargo requires '--' to be wrapped in quotes (passed as an explicit string) - - name: Run tests + - name: Run test run: | . ./ci-cargo.ps1 - foreach($target in ($env:BUILD_TARGETS).Split(',')) { - # Note: no feature is specified, which means such features like graphics, serde, ndarray, and num-complex are not tested here. - ci-cargo test $(if($target -ne 'default') {"--target=$target"} ) '--' --nocapture -ActionName "Testing for $target target" - } - + ci-cargo test -vv + env: + RUST_TARGET: ${{ matrix.config.target }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 2fa291c..97da24a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## [0.2.2] - 2025-02-19 +### Fixed +- Rely on `R_HOME` env var first, then try `R RHOME`; panic if both fail. + ## [0.2.1] - 2025-02-02 ### Fixed - Reformatted code to conform to Rust formatting standards (no functional changes). diff --git a/Cargo.toml b/Cargo.toml index 07f52d3..770e09f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "r-src" -version = "0.2.1" +version = "0.2.2" edition = "2021" license = "MIT" authors = ["Balasubramanian Narasimhan "] diff --git a/README.md b/README.md index 28cb4ad..5834eda 100644 --- a/README.md +++ b/README.md @@ -26,9 +26,11 @@ but has extra platform-specific dependencies for `Windows` such as Two components are required to build the library: -1. [`R`](https://cran.r-project.org/): It needs to be installed and - available in the search path. On `Windows`, the `Rtools` binaries - are also expected to be on the search path. +1. [`R`](https://cran.r-project.org/): An environment variable + `R_HOME` indicating the value of `R_HOME` is first looked for and, + if not found, the command `R RHOME` is tried, so `R` must be on the + path for the latter. On `Windows`, the `Rtools` binaries are also + expected to be on the search path. 2. [`Rust`](https://www.rust-lang.org/learn/get-started): It is recommended to install `Rust` using `rustup`; search path should include `Rust` binaries. diff --git a/build.rs b/build.rs index a805636..30fba52 100644 --- a/build.rs +++ b/build.rs @@ -1,17 +1,11 @@ use std::{ collections::HashMap, - ffi::{OsStr, OsString}, - io, - path::Path, + env, io, + path::{Path, PathBuf}, process::Command, }; -#[cfg(target_family = "unix")] -use std::os::unix::ffi::OsStrExt; - -#[cfg(target_family = "windows")] -use std::os::windows::ffi::OsStringExt; - +/// Holds key/value pairs parsed from "R CMD config --all". #[derive(Debug)] struct ConfigVariables { map: HashMap, @@ -19,160 +13,96 @@ struct ConfigVariables { impl ConfigVariables { fn get_r_cmd_config(&self, key: &str) -> String { - match self.map.get(key) { - Some(value) => value.to_string(), - None => String::from(""), - } + self.map.get(key).cloned().unwrap_or_default() } } -// frustratingly, something like the following does not exist in an -// OS-independent way in Rust -#[cfg(target_family = "unix")] -fn byte_array_to_os_string(bytes: &[u8]) -> OsString { - let os_str = OsStr::from_bytes(bytes); - os_str.to_os_string() -} +fn get_r_home() -> String { + // Indicate that we're trying to find R_HOME from the environment. + print!("Trying to find R_HOME in environment..."); + if let Ok(r_home) = env::var("R_HOME") { + println!("{}", r_home); + return r_home; + } else { + println!("unsuccessful!"); + } -#[link(name = "kernel32")] -#[cfg(target_family = "windows")] -extern "system" { - #[link_name = "GetConsoleCP"] - fn get_console_code_page() -> u32; - #[link_name = "MultiByteToWideChar"] - fn multi_byte_to_wide_char( - CodePage: u32, - dwFlags: u32, - lpMultiByteStr: *const u8, - cbMultiByte: i32, - lpWideCharStr: *mut u16, - cchWideChar: i32, - ) -> i32; -} + // Inform that R_HOME was not found in the environment and we're trying `R RHOME`. + print!("R_HOME not found in environment, trying `R RHOME` command..."); -// convert bytes to wide-encoded characters on Windows -// from: https://stackoverflow.com/a/40456495/4975218 -#[cfg(target_family = "windows")] -fn wide_from_console_string(bytes: &[u8]) -> Vec { - assert!(bytes.len() < std::i32::MAX as usize); - let mut wide; - let mut len; - unsafe { - let cp = get_console_code_page(); - len = multi_byte_to_wide_char( - cp, - 0, - bytes.as_ptr() as *const u8, - bytes.len() as i32, - std::ptr::null_mut(), - 0, - ); - wide = Vec::with_capacity(len as usize); - len = multi_byte_to_wide_char( - cp, - 0, - bytes.as_ptr() as *const u8, - bytes.len() as i32, - wide.as_mut_ptr(), - len, - ); - wide.set_len(len as usize); - } - wide -} + let output = Command::new("R") + .arg("RHOME") + .output() + .expect("Failed to execute `R RHOME` command"); -#[cfg(target_family = "windows")] -fn byte_array_to_os_string(bytes: &[u8]) -> OsString { - // first, use Windows API to convert to wide encoded - let wide = wide_from_console_string(bytes); - // then, use `std::os::windows::ffi::OsStringExt::from_wide()` - OsString::from_wide(&wide) -} + if output.status.success() { + let r_home = String::from_utf8_lossy(&output.stdout).trim().to_string(); -/// Runs the command `R RHOME` and returns the trimmed output if successful. -/// Panics with a meaningful error message if the command fails. -fn get_r_home() -> String { - // Attempt to run the command `R RHOME` - let output = Command::new("R").arg("RHOME").output(); // Capture the command's output - - match output { - Ok(output) if output.status.success() => { - // Convert stdout to a String and trim it - String::from_utf8(output.stdout) - .expect("Invalid UTF-8 in RHOME output") - .trim() - .to_string() - } - Ok(output) => { - eprintln!( - "Error: Command `R RHOME` failed with status: {}\nStderr: {}", - output.status, - String::from_utf8_lossy(&output.stderr) - ); - panic!("Failed to detect RHOME using `R RHOME`. Is R installed and in your PATH?"); - } - Err(err) => { - panic!( - "Failed to execute `R RHOME`: {}. Is R installed and in your PATH?", - err - ); + if !r_home.is_empty() { + println!("success: {}", r_home); + return r_home; } } + + // If both methods fail, panic with an error message. + panic!("Could not determine R_HOME: it is not set in the environment and `R RHOME` did not return a valid value."); } -// Execute an R CMD config and return the captured output -fn r_cmd_config>(r_binary: S) -> io::Result { - let out = Command::new(r_binary) +/// Run `R CMD config --all` using the provided R executable path and return its stdout as a String. +fn r_cmd_config(r_binary: &Path) -> io::Result { + let output = Command::new(r_binary) .args(&["CMD", "config", "--all"]) .output()?; - - // if there are any errors we print them out, helps with debugging - if !out.stderr.is_empty() { - println!( - "> {}", - byte_array_to_os_string(&out.stderr) - .as_os_str() - .to_string_lossy() - ); + if !output.stderr.is_empty() { + println!("> {}", String::from_utf8_lossy(&output.stderr)); } - - Ok(byte_array_to_os_string(&out.stdout)) + Ok(String::from_utf8_lossy(&output.stdout).into_owned()) } +/// Build the configuration map by invoking R commands. fn build_r_cmd_configs() -> ConfigVariables { - let r_binary = format!(r"{}/bin/R", get_r_home()); - let r_configs = r_cmd_config(r_binary); + let r_home = get_r_home(); + + // Determine the R executable path. + let r_binary: PathBuf = if cfg!(target_os = "windows") { + // On Windows R is typically installed in a subdirectory. + // Try the "x64" folder first (for 64-bit installations), then fall back. + let candidate = Path::new(&r_home).join("bin").join("x64").join("R.exe"); + if candidate.exists() { + candidate + } else { + Path::new(&r_home).join("bin").join("R.exe") + } + } else { + Path::new(&r_home).join("bin").join("R") + }; + let configs = r_cmd_config(&r_binary).unwrap_or_default(); let mut rcmd_config_map = HashMap::new(); - match r_configs { - Ok(configs) => { - let input = configs.as_os_str().to_string_lossy(); - for line in input.lines() { - // Ignore lines beyond comment marker - if line.starts_with("##") { - break; - } - let parts: Vec<_> = line.split('=').map(str::trim).collect(); - if let [name, value] = parts.as_slice() { - rcmd_config_map.insert(name.to_string(), value.to_string()); - } - } + + // Parse the output, expecting lines of the form KEY=VALUE. + for line in configs.lines() { + // Stop if we reach comments (the R output sometimes appends comments). + if line.starts_with("##") { + break; + } + let parts: Vec<&str> = line.split('=').map(str::trim).collect(); + if parts.len() == 2 { + rcmd_config_map.insert(parts[0].to_string(), parts[1].to_string()); } - _ => (), } - // Return the struct ConfigVariables { map: rcmd_config_map, } } -fn get_libs_and_paths(strings: Vec) -> (Vec, Vec) { - let mut paths: Vec = Vec::new(); - let mut libs: Vec = Vec::new(); - - for s in &strings { - let parts: Vec<&str> = s.split_whitespace().collect(); - for part in parts { +/// Given a list of strings (such as BLAS, LAPACK, etc. flags), +/// extract library paths (starting with "-L") and libraries (starting with "-l"). +fn get_libs_and_paths(strings: &[String]) -> (Vec, Vec) { + let mut paths = Vec::new(); + let mut libs = Vec::new(); + for s in strings { + for part in s.split_whitespace() { if part.starts_with("-L") { paths.push(part[2..].to_string()); } else if part.starts_with("-l") { @@ -185,27 +115,24 @@ fn get_libs_and_paths(strings: Vec) -> (Vec, Vec) { fn main() { let r_configs = build_r_cmd_configs(); - let (lib_paths, libs) = get_libs_and_paths( - [ - r_configs.get_r_cmd_config("BLAS_LIBS"), - r_configs.get_r_cmd_config("LAPACK_LIBS"), - r_configs.get_r_cmd_config("FLIBS"), - ] - .to_vec(), - ); - - for path in lib_paths.iter() { - // Some R builds (e.g. homebrew) contain hardwired gfortran12 - // paths, which may or may not exist if one has upgraded - // gfortran. So filter out non-existent ones, so that cargo - // doesn't complain. - if Path::new(path).exists() { + let config_strings = [ + r_configs.get_r_cmd_config("BLAS_LIBS"), + r_configs.get_r_cmd_config("LAPACK_LIBS"), + r_configs.get_r_cmd_config("FLIBS"), + ]; + let (lib_paths, libs) = get_libs_and_paths(&config_strings); + + // Emit link search paths. Only output those that exist. + for path in lib_paths { + if Path::new(&path).exists() { println!("cargo:rustc-link-search={}", path); + eprintln!("cargo:rustc-link-search={}", path); } } - - for lib in libs.iter() { + // Emit libraries for the linker. + for lib in libs { println!("cargo:rustc-link-lib=dylib={}", lib); + eprintln!("cargo:rustc-link-lib=dylib={}", lib); } println!("cargo:rerun-if-changed=build.rs"); } diff --git a/ci-cargo.ps1 b/ci-cargo.ps1 index 71cc007..0e5adfd 100644 --- a/ci-cargo.ps1 +++ b/ci-cargo.ps1 @@ -9,12 +9,11 @@ function ci-cargo { ) - try { - Write-Output "::group::$ActionName" - $CargoArgs = $CargoArgs | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } - Write-Output "Running cargo $CargoArgs" + try { + echo "::group::$ActionName" + echo "Running cargo $CargoArgs" cargo $CargoArgs - if ($LASTEXITCODE -ne 0) { + if($LASTEXITCODE -ne 0) { throw $LASTEXITCODE } } @@ -22,12 +21,12 @@ function ci-cargo { if ($ActionName -ne $null -and $ActionName -ne "") { $ActionName = "'$ActionName': " } - $errMsg = "$($ActionName)cargo failed with code $LASTEXITCODE (args: $CargoArgs)" - Write-Output "::error::$errMsg" - Write-Error -Message "$errMsg" -ErrorAction Stop + $err_msg = "$($ActionName)cargo failed with code $LASTEXITCODE (args: $CargoArgs)" + echo "::error::$err_msg" + Write-Error -Message "$err_msg" -ErrorAction Stop } finally { - Write-Output "::endgroup::" + echo "::endgroup::" } <#