cc-1.2.60/.cargo_vcs_info.json0000644000000001361046102023000115310ustar { "git": { "sha1": "7cad9f5b10c33fa969a13ead70b43b64380cc9e2" }, "path_in_vcs": "" }cc-1.2.60/.gitignore000064400000000000000000000000371046102023000122670ustar 00000000000000/target Cargo.lock .idea *.iml cc-1.2.60/CHANGELOG.md000064400000000000000000001101451046102023000121120ustar 00000000000000# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] ## [1.2.60](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.59...cc-v1.2.60) - 2026-04-10 ### Fixed - *(ar)* suppress warnings from `D` modifier probe ([#1700](https://github.com/rust-lang/cc-rs/pull/1700)) ## [1.2.59](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.58...cc-v1.2.59) - 2026-04-03 ### Fixed - *(ar)* deterministic archives with `D` modifier ([#1697](https://github.com/rust-lang/cc-rs/pull/1697)) ### Other - Regenerate target info ([#1698](https://github.com/rust-lang/cc-rs/pull/1698)) - Fix target abi parsing for sanitiser targets ([#1695](https://github.com/rust-lang/cc-rs/pull/1695)) ## [1.2.58](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.57...cc-v1.2.58) - 2026-03-27 ### Other - Update Compile-time Requirements to add info about clang-cl.exe ([#1693](https://github.com/rust-lang/cc-rs/pull/1693)) ## [1.2.57](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.56...cc-v1.2.57) - 2026-03-13 ### Other - Size archiver batches according to argument length not argument count ([#1689](https://github.com/rust-lang/cc-rs/pull/1689)) - Added `Build::env` for setting environment variables of compiler invocations and other child processes ([#1656](https://github.com/rust-lang/cc-rs/pull/1656) [#1682](https://github.com/rust-lang/cc-rs/pull/1682)) ## [1.2.56](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.55...cc-v1.2.56) - 2026-02-13 ### Other - Regenerate target info ([#1676](https://github.com/rust-lang/cc-rs/pull/1676)) - Fix `clang-cl` target when cross-compiling ([#1670](https://github.com/rust-lang/cc-rs/pull/1670)) ## [1.2.55](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.54...cc-v1.2.55) - 2026-01-30 ### Other - Regenerate target info ([#1667](https://github.com/rust-lang/cc-rs/pull/1667)) - Fix RUSTFLAGS typo in test-linker-plugin-lto ([#1665](https://github.com/rust-lang/cc-rs/pull/1665)) - Disable PIC for armv7-sony-vita-newlibeabihf ([#1664](https://github.com/rust-lang/cc-rs/pull/1664)) ## [1.2.54](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.53...cc-v1.2.54) - 2026-01-23 ### Other - Fix x86_64-unknown-linux-gnuasan parsing ([#1661](https://github.com/rust-lang/cc-rs/pull/1661)) - Regenerate target info ([#1660](https://github.com/rust-lang/cc-rs/pull/1660)) ## [1.2.53](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.52...cc-v1.2.53) - 2026-01-16 ### Other - Add missing RISC-V targets ([#1657](https://github.com/rust-lang/cc-rs/pull/1657)) ## [1.2.52](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.51...cc-v1.2.52) - 2026-01-09 ### Other - Fix contradictory doc for CC compiler in crate doc ([#1650](https://github.com/rust-lang/cc-rs/pull/1650)) - Have CUDA compilaion check for sbsa-linux when targeting aarch64. ([#1647](https://github.com/rust-lang/cc-rs/pull/1647)) - Update link for -Cdwarf-version; Remove -Z (stabilized in 1.88) ([#1648](https://github.com/rust-lang/cc-rs/pull/1648)) - Fix Build::env_tool to check for .exe on windows ([#1646](https://github.com/rust-lang/cc-rs/pull/1646)) ## [1.2.51](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.50...cc-v1.2.51) - 2025-12-26 ### Other - Regenerate target info ([#1642](https://github.com/rust-lang/cc-rs/pull/1642)) - Update Readmes ([#1641](https://github.com/rust-lang/cc-rs/pull/1641)) ## [1.2.50](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.49...cc-v1.2.50) - 2025-12-19 ### Other - Add tests for `OUT_DIR` escape for '..' file paths (#1631) - Fix #283: Make warnings(false) actually suppress compiler warnings ([#1633](https://github.com/rust-lang/cc-rs/pull/1633)) ## [1.2.49](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.48...cc-v1.2.49) - 2025-12-06 ### Other - Fix run_output to prevent infinite blocking ([#1627](https://github.com/rust-lang/cc-rs/pull/1627)) - Fix detect_family deadlock ([#1626](https://github.com/rust-lang/cc-rs/pull/1626)) - Fix link in new debug_str doc comment ([#1625](https://github.com/rust-lang/cc-rs/pull/1625)) - Support more of Cargo's debug levels with Build::debug_str ([#1624](https://github.com/rust-lang/cc-rs/pull/1624)) ## [1.2.48](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.47...cc-v1.2.48) - 2025-11-28 ### Other - Regenerate target info ([#1620](https://github.com/rust-lang/cc-rs/pull/1620)) ## [1.2.47](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.46...cc-v1.2.47) - 2025-11-21 ### Other - add helenos linker identifications ([#1615](https://github.com/rust-lang/cc-rs/pull/1615)) ## [1.2.46](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.45...cc-v1.2.46) - 2025-11-14 ### Other - Add Visual Studio 2026 support ([#1609](https://github.com/rust-lang/cc-rs/pull/1609)) ## [1.2.45](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.44...cc-v1.2.45) - 2025-11-07 ### Other - Regenerate target info ([#1606](https://github.com/rust-lang/cc-rs/pull/1606)) - Use a default check for the "env" variable in apple_sdk_name ([#1605](https://github.com/rust-lang/cc-rs/pull/1605)) ## [1.2.44](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.43...cc-v1.2.44) - 2025-10-31 ### Other - Fix debug assertion for env/abi mismatch ([#1604](https://github.com/rust-lang/cc-rs/pull/1604)) - Update CHANGELOG for version 1.2.43 ([#1602](https://github.com/rust-lang/cc-rs/pull/1602)) - Stop passing an invalid target to `llvm-mingw`'s cross-compilation wrappers ([#1495](https://github.com/rust-lang/cc-rs/pull/1495)) ## [1.2.43](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.42...cc-v1.2.43) - 2025-10-24 ### Other - Mark `static_flag` and `shared_flag` as deprecated ([#1582](https://github.com/rust-lang/cc-rs/pull/1582)) ## [1.2.42](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.41...cc-v1.2.42) - 2025-10-24 ### Other - Fix check-semver-checks ([#1600](https://github.com/rust-lang/cc-rs/pull/1600)) - minor improvement for docs ([#1598](https://github.com/rust-lang/cc-rs/pull/1598)) - Fix linker-plugin-lto: use `-flto=thin` ([#1594](https://github.com/rust-lang/cc-rs/pull/1594)) - Disable check-buildstd for armv7k-apple-watchos ([#1599](https://github.com/rust-lang/cc-rs/pull/1599)) - Add elf abi to ppc64 targets ([#1596](https://github.com/rust-lang/cc-rs/pull/1596)) ## [1.2.41](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.40...cc-v1.2.41) - 2025-10-10 ### Other - Allow using VCToolsVersion to request a specific msvc version ([#1589](https://github.com/rust-lang/cc-rs/pull/1589)) - Regenerate target info ([#1592](https://github.com/rust-lang/cc-rs/pull/1592)) - Regenerate windows sys bindings ([#1591](https://github.com/rust-lang/cc-rs/pull/1591)) - Update windows-bindgen requirement from 0.64 to 0.65 ([#1590](https://github.com/rust-lang/cc-rs/pull/1590)) - Fix `get_base_archiver_variant` for clang-cl: use `--print-search-dirs` ([#1587](https://github.com/rust-lang/cc-rs/pull/1587)) ## [1.2.40](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.39...cc-v1.2.40) - 2025-10-03 ### Other - Reorder changelog and remove duplicate Unreleased section ([#1579](https://github.com/rust-lang/cc-rs/pull/1579)) - Prefer clang if linker-plugin-lto specified ([#1573](https://github.com/rust-lang/cc-rs/pull/1573)) - Fix building for Mac Catalyst ([#1577](https://github.com/rust-lang/cc-rs/pull/1577)) - Improve ESP microcontroller targets ([#1574](https://github.com/rust-lang/cc-rs/pull/1574)) ## [1.2.39](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.38...cc-v1.2.39) - 2025-09-26 ### Other - Fix cross compilation to xtensa-esp32s3-espidf ([#1569](https://github.com/rust-lang/cc-rs/pull/1569)) - Fix autodetect_wasi_compiler: support non utf-8 path ([#1568](https://github.com/rust-lang/cc-rs/pull/1568)) - Regenerate target info ([#1567](https://github.com/rust-lang/cc-rs/pull/1567)) - Fix rustcflags mapping: require -Clinker-plugin-lto for -flto ([#1564](https://github.com/rust-lang/cc-rs/pull/1564)) - Use `$WASI_SDK_PATH` on WASI targets by default ([#1562](https://github.com/rust-lang/cc-rs/pull/1562)) - Fix atomicity violations in concurrent cache operations ([#1559](https://github.com/rust-lang/cc-rs/pull/1559)) ## [1.2.38](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.37...cc-v1.2.38) - 2025-09-19 ### Other - updated the following local packages: find-msvc-tools ## [1.2.37](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.36...cc-v1.2.37) - 2025-09-12 ### Other - Fix errmsg in RustcCodegenFlags::set_rustc_flag ([#1551](https://github.com/rust-lang/cc-rs/pull/1551)) - propagate stack protector to Linux C compilers ([#1550](https://github.com/rust-lang/cc-rs/pull/1550)) - Extract new fn `run_commands_in_parallel` ([#1549](https://github.com/rust-lang/cc-rs/pull/1549)) ## [1.2.36](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.35...cc-v1.2.36) - 2025-09-05 ### Other - Regenerate windows sys bindings ([#1548](https://github.com/rust-lang/cc-rs/pull/1548)) - Update windows-bindgen requirement from 0.62 to 0.63 ([#1547](https://github.com/rust-lang/cc-rs/pull/1547)) - Add fn get_ucrt_dir for find-msvc-tools ([#1546](https://github.com/rust-lang/cc-rs/pull/1546)) - Regenerate target info ([#1544](https://github.com/rust-lang/cc-rs/pull/1544)) - fix publish.yml ([#1543](https://github.com/rust-lang/cc-rs/pull/1543)) - Replace periods with underscores as well when parsing env variables ([#1541](https://github.com/rust-lang/cc-rs/pull/1541)) ## [1.2.35](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.34...cc-v1.2.35) - 2025-09-01 ### Fixed - fix building for aarch64-apple-visionos-sim on nightly ([#1534](https://github.com/rust-lang/cc-rs/pull/1534)) - fix tests apple_sdkroot_wrong ([#1530](https://github.com/rust-lang/cc-rs/pull/1530)) ### Other - Regenerate target info ([#1536](https://github.com/rust-lang/cc-rs/pull/1536)) - Optimize Tool::to_command ([#1535](https://github.com/rust-lang/cc-rs/pull/1535)) - Extract find-msvc-tools ([#1531](https://github.com/rust-lang/cc-rs/pull/1531)) - Add prefer_clang_cl_over_msvc ([#1516](https://github.com/rust-lang/cc-rs/pull/1516)) ## [1.2.34](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.33...cc-v1.2.34) - 2025-08-22 ### Fixed - add `-mcpu=mvp` and `-mmutable-globals` for `wasm32v1-none` ([#1524](https://github.com/rust-lang/cc-rs/pull/1524)) ### Other - Optimize parse_version in find_tools.rs ([#1527](https://github.com/rust-lang/cc-rs/pull/1527)) - Fallback to manually searching for tool dir ([#1526](https://github.com/rust-lang/cc-rs/pull/1526)) ## [1.2.33](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.32...cc-v1.2.33) - 2025-08-15 ### Other - Regenerate target info ([#1521](https://github.com/rust-lang/cc-rs/pull/1521)) - [win][arm64ec] Add testing for Arm64EC Windows ([#1512](https://github.com/rust-lang/cc-rs/pull/1512)) - Fix parsing of nigthly targets ([#1517](https://github.com/rust-lang/cc-rs/pull/1517)) - [win][arm64ec] Fix finding assembler and setting is_arm for Arm64EC ([#1511](https://github.com/rust-lang/cc-rs/pull/1511)) ## [1.2.32](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.31...cc-v1.2.32) - 2025-08-08 ### Fixed - fix new clippy lint introduced in rust 1.89.0 ([#1509](https://github.com/rust-lang/cc-rs/pull/1509)) ### Other - clarify cargo default if no rerun emitted ([#1508](https://github.com/rust-lang/cc-rs/pull/1508)) - extract compile_objects_sequential ([#1507](https://github.com/rust-lang/cc-rs/pull/1507)) - Windows `find_tools`: add support for finding Clang ([#1506](https://github.com/rust-lang/cc-rs/pull/1506)) - Add m68k-unknown-linux-gnu cross-compile target ([#1505](https://github.com/rust-lang/cc-rs/pull/1505)) ## [1.2.31](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.30...cc-v1.2.31) - 2025-08-01 ### Other - Add doc for using sccache/ccache etc ([#1502](https://github.com/rust-lang/cc-rs/pull/1502)) - ability to statically link against C++ stdlib ([#1497](https://github.com/rust-lang/cc-rs/pull/1497)) - Add instructions on using sccache ([#1503](https://github.com/rust-lang/cc-rs/pull/1503)) - Add support for recognizing some architectures supported by GCC, but not LLVM. ([#1500](https://github.com/rust-lang/cc-rs/pull/1500)) ## [1.2.30](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.29...cc-v1.2.30) - 2025-07-18 ### Other - define _REENTRANT by default ([#1496](https://github.com/rust-lang/cc-rs/pull/1496)) ## [1.2.29](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.28...cc-v1.2.29) - 2025-07-05 ### Other - Fix target parsing for powerpc ([#1490](https://github.com/rust-lang/cc-rs/pull/1490)) ## [1.2.28](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.27...cc-v1.2.28) - 2025-07-04 ### Other - Recognize `mlibc` environment ([#1488](https://github.com/rust-lang/cc-rs/pull/1488)) - Fix clippy warnings about not using variables in `format!` strings ([#1489](https://github.com/rust-lang/cc-rs/pull/1489)) ## [1.2.27](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.26...cc-v1.2.27) - 2025-06-13 ### Other - Regenerate windows sys bindings ([#1485](https://github.com/rust-lang/cc-rs/pull/1485)) - Update windows-bindgen requirement from 0.61 to 0.62 ([#1484](https://github.com/rust-lang/cc-rs/pull/1484)) - Regenerate target info ([#1483](https://github.com/rust-lang/cc-rs/pull/1483)) ## [1.2.26](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.25...cc-v1.2.26) - 2025-06-06 ### Other - Also set `SDKROOT` when building apple platforms ([#1475](https://github.com/rust-lang/cc-rs/pull/1475)) - use windows 2022 in CI ([#1479](https://github.com/rust-lang/cc-rs/pull/1479)) - Detect -Wslash-u-filename warning on clang-cl ([#1477](https://github.com/rust-lang/cc-rs/pull/1477)) ## [1.2.25](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.24...cc-v1.2.25) - 2025-05-30 ### Other - make `powerp64` use `powerpc64-linux-gnu` prefix ([#1474](https://github.com/rust-lang/cc-rs/pull/1474)) ## [1.2.24](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.23...cc-v1.2.24) - 2025-05-23 ### Other - Regenerate windows sys bindings ([#1471](https://github.com/rust-lang/cc-rs/pull/1471)) ## [1.2.23](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.22...cc-v1.2.23) - 2025-05-16 ### Other - support "vxworks" and "nto" OSes on `get_base_archiver_variant` ([#1456](https://github.com/rust-lang/cc-rs/pull/1456)) ## [1.2.22](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.21...cc-v1.2.22) - 2025-05-09 ### Other - Add `flags` method to `cc::Build` for adding multiple flags ([#1466](https://github.com/rust-lang/cc-rs/pull/1466)) ## [1.2.21](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.20...cc-v1.2.21) - 2025-05-02 ### Other - Fix wasm32-unknown-unknown by passing -c ([#1424](https://github.com/rust-lang/cc-rs/pull/1424)) ## [1.2.20](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.19...cc-v1.2.20) - 2025-04-25 ### Other - Regenerate target info ([#1461](https://github.com/rust-lang/cc-rs/pull/1461)) - Fix parser.rs on latest rustc nightly ([#1459](https://github.com/rust-lang/cc-rs/pull/1459)) ## [1.2.19](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.18...cc-v1.2.19) - 2025-04-11 ### Other - Fix musl compilation: Add musl as a prefix fallback ([#1455](https://github.com/rust-lang/cc-rs/pull/1455)) ## [1.2.18](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.17...cc-v1.2.18) - 2025-04-04 ### Other - Regenerate target info ([#1450](https://github.com/rust-lang/cc-rs/pull/1450)) - Use `std::thread::available_parallelism` for determining the default number of jobs ([#1447](https://github.com/rust-lang/cc-rs/pull/1447)) - Fix mips64-openwrt-linux-musl parsing ([#1449](https://github.com/rust-lang/cc-rs/pull/1449)) - Use compiler prefix `x86_64-linux-musl` ([#1443](https://github.com/rust-lang/cc-rs/pull/1443)) ## [1.2.17](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.16...cc-v1.2.17) - 2025-03-21 ### Other - Regenerate target info ([#1439](https://github.com/rust-lang/cc-rs/pull/1439)) - Regenerate windows sys bindings ([#1437](https://github.com/rust-lang/cc-rs/pull/1437)) - Fix wasm32-wali-linux-musl target parsing ([#1434](https://github.com/rust-lang/cc-rs/pull/1434)) - Parse `rustc` target names ([#1413](https://github.com/rust-lang/cc-rs/pull/1413)) - Regenerate target info ([#1429](https://github.com/rust-lang/cc-rs/pull/1429)) - Added base support for `wasm32-wali-linux-musl` target ([#1373](https://github.com/rust-lang/cc-rs/pull/1373)) ## [1.2.16](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.15...cc-v1.2.16) - 2025-02-28 ### Fixed - force windows compiler to run in `out_dir` to prevent artifacts in cwd (#1415) ### Other - use `/arch:SSE2` for `x86` target arch (#1425) - Regenerate windows-sys binding ([#1422](https://github.com/rust-lang/cc-rs/pull/1422)) - Regenerate target info ([#1418](https://github.com/rust-lang/cc-rs/pull/1418)) - Add LIB var when compiling flag_check (#1417) - Change flag ordering ([#1403](https://github.com/rust-lang/cc-rs/pull/1403)) - Fix archiver detection for musl cross compilation ([#1404](https://github.com/rust-lang/cc-rs/pull/1404)) ## [1.2.15](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.14...cc-v1.2.15) - 2025-02-21 ### Other - Regenerate target info ([#1406](https://github.com/rust-lang/cc-rs/pull/1406)) - Always read from all `CFLAGS`-style flags ([#1401](https://github.com/rust-lang/cc-rs/pull/1401)) - Simplify the error output on failed `Command` invocation ([#1397](https://github.com/rust-lang/cc-rs/pull/1397)) ## [1.2.14](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.13...cc-v1.2.14) - 2025-02-14 ### Other - Regenerate target info ([#1398](https://github.com/rust-lang/cc-rs/pull/1398)) - Add support for setting `-gdwarf-{version}` based on RUSTFLAGS ([#1395](https://github.com/rust-lang/cc-rs/pull/1395)) - Add support for alternative network stack io-sock on QNX 7.1 aarch64 and x86_64 ([#1312](https://github.com/rust-lang/cc-rs/pull/1312)) ## [1.2.13](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.12...cc-v1.2.13) - 2025-02-08 ### Other - Fix cross-compiling for Apple platforms ([#1389](https://github.com/rust-lang/cc-rs/pull/1389)) ## [1.2.12](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.11...cc-v1.2.12) - 2025-02-04 ### Other - Split impl Build ([#1382](https://github.com/rust-lang/cc-rs/pull/1382)) - Don't specify both `-target` and `-mtargetos=` on Apple targets ([#1384](https://github.com/rust-lang/cc-rs/pull/1384)) ## [1.2.11](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.10...cc-v1.2.11) - 2025-01-31 ### Other - Fix more flag inheritance ([#1380](https://github.com/rust-lang/cc-rs/pull/1380)) - Include wrapper args. in `stdout` family heuristics to restore classifying `clang --driver-mode=cl` as `Msvc { clang_cl: true }` ([#1378](https://github.com/rust-lang/cc-rs/pull/1378)) - Constrain `-Clto` and `-Cembed-bitcode` flag inheritance to be `clang`-only ([#1379](https://github.com/rust-lang/cc-rs/pull/1379)) - Pass deployment target with `-m*-version-min=` ([#1339](https://github.com/rust-lang/cc-rs/pull/1339)) - Regenerate target info ([#1376](https://github.com/rust-lang/cc-rs/pull/1376)) ## [1.2.10](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.9...cc-v1.2.10) - 2025-01-17 ### Other - Fix CC_FORCE_DISABLE=0 evaluating to true ([#1371](https://github.com/rust-lang/cc-rs/pull/1371)) - Regenerate target info ([#1369](https://github.com/rust-lang/cc-rs/pull/1369)) - Make hidden lifetimes explicit. ([#1366](https://github.com/rust-lang/cc-rs/pull/1366)) ## [1.2.9](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.8...cc-v1.2.9) - 2025-01-12 ### Other - Don't pass inherited PGO flags to GNU compilers (#1363) - Adjusted zig cc judgment and avoided zigbuild errors([#1360](https://github.com/rust-lang/cc-rs/pull/1360)) ([#1361](https://github.com/rust-lang/cc-rs/pull/1361)) - Fix compilation on macOS using clang and fix compilation using zig-cc ([#1364](https://github.com/rust-lang/cc-rs/pull/1364)) ## [1.2.8](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.7...cc-v1.2.8) - 2025-01-11 ### Other - Add `is_like_clang_cl()` getter (#1357) - Fix clippy error in lib.rs ([#1356](https://github.com/rust-lang/cc-rs/pull/1356)) - Regenerate target info ([#1352](https://github.com/rust-lang/cc-rs/pull/1352)) - Fix compiler family detection issue with clang-cl on macOS ([#1328](https://github.com/rust-lang/cc-rs/pull/1328)) - Update `windows-bindgen` dependency ([#1347](https://github.com/rust-lang/cc-rs/pull/1347)) - Fix clippy warnings ([#1346](https://github.com/rust-lang/cc-rs/pull/1346)) ## [1.2.7](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.6...cc-v1.2.7) - 2025-01-03 ### Other - Regenerate target info ([#1342](https://github.com/rust-lang/cc-rs/pull/1342)) - Document new supported architecture names in windows::find - Make is_flag_supported_inner take an &Tool ([#1337](https://github.com/rust-lang/cc-rs/pull/1337)) - Fix is_flag_supported on msvc ([#1336](https://github.com/rust-lang/cc-rs/pull/1336)) - Allow using Visual Studio target names in `find_tool` ([#1335](https://github.com/rust-lang/cc-rs/pull/1335)) ## [1.2.6](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.5...cc-v1.2.6) - 2024-12-27 ### Other - Don't inherit the `/Oy` flag for 64-bit targets ([#1330](https://github.com/rust-lang/cc-rs/pull/1330)) ## [1.2.5](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.4...cc-v1.2.5) - 2024-12-19 ### Other - Check linking when testing if compiler flags are supported ([#1322](https://github.com/rust-lang/cc-rs/pull/1322)) ## [1.2.4](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.3...cc-v1.2.4) - 2024-12-13 ### Other - Add support for C/C++ compiler for Neutrino QNX: `qcc` ([#1319](https://github.com/rust-lang/cc-rs/pull/1319)) - use -maix64 instead of -m64 ([#1307](https://github.com/rust-lang/cc-rs/pull/1307)) ## [1.2.3](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.2...cc-v1.2.3) - 2024-12-06 ### Other - Improve detection of environment when compiling from msbuild or msvc ([#1310](https://github.com/rust-lang/cc-rs/pull/1310)) - Better error message when failing on unknown targets ([#1313](https://github.com/rust-lang/cc-rs/pull/1313)) - Optimize RustcCodegenFlags ([#1305](https://github.com/rust-lang/cc-rs/pull/1305)) ## [1.2.2](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.1...cc-v1.2.2) - 2024-11-29 ### Other - Inherit flags from rustc ([#1279](https://github.com/rust-lang/cc-rs/pull/1279)) - Add support for using sccache wrapper with cuda/nvcc ([#1304](https://github.com/rust-lang/cc-rs/pull/1304)) - Fix msvc stdout not shown on error ([#1303](https://github.com/rust-lang/cc-rs/pull/1303)) - Regenerate target info ([#1301](https://github.com/rust-lang/cc-rs/pull/1301)) - Fix compilation of C++ code for armv7-unknown-linux-gnueabihf ([#1298](https://github.com/rust-lang/cc-rs/pull/1298)) - Fetch target info from Cargo even if `Build::target` is manually set ([#1299](https://github.com/rust-lang/cc-rs/pull/1299)) - Fix two files with different extensions having the same object name ([#1295](https://github.com/rust-lang/cc-rs/pull/1295)) - Allow disabling cc's ability to compile via env var CC_FORCE_DISABLE ([#1292](https://github.com/rust-lang/cc-rs/pull/1292)) - Regenerate target info ([#1293](https://github.com/rust-lang/cc-rs/pull/1293)) ## [1.2.1](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.0...cc-v1.2.1) - 2024-11-14 ### Other - When invoking `cl -?`, set stdin to null ([#1288](https://github.com/rust-lang/cc-rs/pull/1288)) ## [1.2.0](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.37...cc-v1.2.0) - 2024-11-11 ### Added - add i686-pc-windows-gnullvm prefix detection ([#1283](https://github.com/rust-lang/cc-rs/pull/1283)) ### Other - Allow only specifying the architecture ([#1285](https://github.com/rust-lang/cc-rs/pull/1285)) - Fix WASM vs. WASI options ([#1284](https://github.com/rust-lang/cc-rs/pull/1284)) ## [1.1.37](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.36...cc-v1.1.37) - 2024-11-08 ### Other - Use relative directory for obj files hash ([#1270](https://github.com/rust-lang/cc-rs/pull/1270)) - Regenerate target info ([#1280](https://github.com/rust-lang/cc-rs/pull/1280)) ## [1.1.36](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.35...cc-v1.1.36) - 2024-11-05 ### Other - Fix CUDA build with clang++. ([#1273](https://github.com/rust-lang/cc-rs/pull/1273)) ## [1.1.35](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.34...cc-v1.1.35) - 2024-11-04 ### Other - Remove support for FRC ([#1268](https://github.com/rust-lang/cc-rs/pull/1268)) - Do not add -fPIC by default on UEFI targets ([#1263](https://github.com/rust-lang/cc-rs/pull/1263)) - Use -windows-gnu for all UEFI targets ([#1264](https://github.com/rust-lang/cc-rs/pull/1264)) ## [1.1.34](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.33...cc-v1.1.34) - 2024-11-02 ### Other - Remove redundant flags ([#1256](https://github.com/rust-lang/cc-rs/pull/1256)) ## [1.1.33](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.32...cc-v1.1.33) - 2024-11-02 ### Other - Reduce size of `cc::Build` and size of generated targets ([#1257](https://github.com/rust-lang/cc-rs/pull/1257)) ## [1.1.32](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.31...cc-v1.1.32) - 2024-11-02 ### Other - Use `rustc`'s knowledge of LLVM/Clang target triples ([#1252](https://github.com/rust-lang/cc-rs/pull/1252)) - Use Cargo's target information when possible ([#1225](https://github.com/rust-lang/cc-rs/pull/1225)) ## [1.1.31](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.30...cc-v1.1.31) - 2024-10-19 ### Other - Add comment explaining why cc does not rebuild on env PATH change ([#1247](https://github.com/rust-lang/cc-rs/pull/1247)) ## [1.1.30](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.29...cc-v1.1.30) - 2024-10-11 ### Other - Don't pass -fPIC by default on wasm ([#1245](https://github.com/rust-lang/cc-rs/pull/1245)) ## [1.1.29](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.28...cc-v1.1.29) - 2024-10-11 ### Other - Regenerate target info ([#1243](https://github.com/rust-lang/cc-rs/pull/1243)) ## [1.1.28](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.27...cc-v1.1.28) - 2024-10-06 ### Other - Environment variables: For one accepting boolean, treat "0", "false" and empty env as false ([#1238](https://github.com/rust-lang/cc-rs/pull/1238)) ## [1.1.27](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.26...cc-v1.1.27) - 2024-10-06 ### Other - Revert "Use debug version of MSVC runtime library on debug ([#1231](https://github.com/rust-lang/cc-rs/pull/1231))" ([#1237](https://github.com/rust-lang/cc-rs/pull/1237)) - Disable `CC_ENABLE_DEBUG_OUTPUT` if it is set to "0" ([#1234](https://github.com/rust-lang/cc-rs/pull/1234)) ## [1.1.26](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.25...cc-v1.1.26) - 2024-10-06 ### Other - Use debug version of MSVC runtime library on debug ([#1231](https://github.com/rust-lang/cc-rs/pull/1231)) ## [1.1.25](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.24...cc-v1.1.25) - 2024-10-05 ### Other - Remove incorrect "lib" prefixes in CXXSTDLIB doc comments ([#1228](https://github.com/rust-lang/cc-rs/pull/1228)) ## [1.1.24](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.23...cc-v1.1.24) - 2024-10-01 ### Other - Fix wasm32-wasip1-threads: shared-memory disallowed due to not compiled with 'atomics' or 'bulk-memory' features ([#1221](https://github.com/rust-lang/cc-rs/pull/1221)) - Reduce the need for the host target triple ([#1224](https://github.com/rust-lang/cc-rs/pull/1224)) - Add auto cancellation for CI jobs ([#1222](https://github.com/rust-lang/cc-rs/pull/1222)) ## [1.1.23](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.22...cc-v1.1.23) - 2024-09-30 ### Other - Update doc for detecting changes/upgrades of compilers ([#1218](https://github.com/rust-lang/cc-rs/pull/1218)) ## [1.1.22](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.21...cc-v1.1.22) - 2024-09-27 ### Other - Don't rerun if PATH changes ([#1215](https://github.com/rust-lang/cc-rs/pull/1215)) ## [1.1.21](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.20...cc-v1.1.21) - 2024-09-18 ### Other - disable pic for targets that end in `-none` ([#1212](https://github.com/rust-lang/cc-rs/pull/1212)) ## [1.1.20](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.19...cc-v1.1.20) - 2024-09-17 ### Other - Add buildcache as known Rust and C/C++ compiler wrapper ([#1209](https://github.com/rust-lang/cc-rs/pull/1209)) ## [1.1.19](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.18...cc-v1.1.19) - 2024-09-15 ### Other - Add support arm64e-apple-darwin ([#1207](https://github.com/rust-lang/cc-rs/pull/1207)) ## [1.1.18](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.17...cc-v1.1.18) - 2024-09-07 ### Other - Fixed unsoundness in `StderrForwarder::forward_available` ([#1203](https://github.com/rust-lang/cc-rs/pull/1203)) ## [1.1.17](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.16...cc-v1.1.17) - 2024-09-06 ### Fixed - fix finding toolchains when invoked by msbuild ([#1201](https://github.com/rust-lang/cc-rs/pull/1201)) ## [1.1.16](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.15...cc-v1.1.16) - 2024-09-04 ### Other - Treat VxWorks wr-cc as a Gnu compiler ([#1198](https://github.com/rust-lang/cc-rs/pull/1198)) ## [1.1.15](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.14...cc-v1.1.15) - 2024-08-26 ### Other - Add -mfloat-abi=hard as a default argument when using any arm/thumb-none-eabihf target ([#1194](https://github.com/rust-lang/cc-rs/pull/1194)) ## [1.1.14](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.13...cc-v1.1.14) - 2024-08-23 ### Other - allow finding tools from path if VisualStudioDir is set ## [1.1.13](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.12...cc-v1.1.13) - 2024-08-16 ### Other - Fix detect family: should detect emscripten as clang, closes [#1185](https://github.com/rust-lang/cc-rs/pull/1185) ([#1186](https://github.com/rust-lang/cc-rs/pull/1186)) ## [1.1.12](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.11...cc-v1.1.12) - 2024-08-15 ### Other - improve docs ([#1183](https://github.com/rust-lang/cc-rs/pull/1183)) ## [1.1.11](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.10...cc-v1.1.11) - 2024-08-14 ### Other - Add support for parsing shell encoded `*FLAGS` ([#1181](https://github.com/rust-lang/cc-rs/pull/1181)) - Replace vector of tuples with BTreeMap which already is sorted and free of duplicates ([#1177](https://github.com/rust-lang/cc-rs/pull/1177)) ## [1.1.10](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.9...cc-v1.1.10) - 2024-08-11 ### Other - Remap Windows targets triples to their LLVM counterparts ([#1176](https://github.com/rust-lang/cc-rs/pull/1176)) ## [1.1.9](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.8...cc-v1.1.9) - 2024-08-11 ### Other - Add custom CC wrapper to the wrapper whitelist ([#1175](https://github.com/rust-lang/cc-rs/pull/1175)) ## [1.1.8](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.7...cc-v1.1.8) - 2024-08-06 ### Other - Fix broken link in docs.rs ([#1173](https://github.com/rust-lang/cc-rs/pull/1173)) ## [1.1.7](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.6...cc-v1.1.7) - 2024-07-29 ### Other - add `.objects` ([#1166](https://github.com/rust-lang/cc-rs/pull/1166)) ## [1.1.6](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.5...cc-v1.1.6) - 2024-07-19 ### Other - Clippy fixes ([#1163](https://github.com/rust-lang/cc-rs/pull/1163)) ## [1.1.5](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.4...cc-v1.1.5) - 2024-07-15 ### Other - Fix cyclic compilation: Use vendored once_cell ([#1154](https://github.com/rust-lang/cc-rs/pull/1154)) ## [1.1.4](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.3...cc-v1.1.4) - 2024-07-14 ### Other - Support compiling on wasm targets (Supersede [#1068](https://github.com/rust-lang/cc-rs/pull/1068)) ([#1160](https://github.com/rust-lang/cc-rs/pull/1160)) ## [1.1.3](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.2...cc-v1.1.3) - 2024-07-14 ### Other - Reduce msrv to 1.63 ([#1158](https://github.com/rust-lang/cc-rs/pull/1158)) - Revert "Use raw-dylib for windows-sys ([#1137](https://github.com/rust-lang/cc-rs/pull/1137))" ([#1157](https://github.com/rust-lang/cc-rs/pull/1157)) - Fix typos ([#1152](https://github.com/rust-lang/cc-rs/pull/1152)) - Fix `doc_lazy_continuation` lints ([#1153](https://github.com/rust-lang/cc-rs/pull/1153)) ## [1.1.2](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.1...cc-v1.1.2) - 2024-07-12 ### Other - Add empty `jobserver` feature. ([#1150](https://github.com/rust-lang/cc-rs/pull/1150)) ## [1.1.1](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.0...cc-v1.1.1) - 2024-07-12 ### Other - Fix is_flag_supported not respecting emit_rerun_if_env_changed ([#1147](https://github.com/rust-lang/cc-rs/pull/1147)) ([#1148](https://github.com/rust-lang/cc-rs/pull/1148)) ## [1.1.0](https://github.com/rust-lang/cc-rs/compare/cc-v1.0.106...cc-v1.1.0) - 2024-07-08 ### Added - add cargo_output to eliminate last vestiges of stdout pollution ([#1141](https://github.com/rust-lang/cc-rs/pull/1141)) ## [1.0.106](https://github.com/rust-lang/cc-rs/compare/cc-v1.0.105...cc-v1.0.106) - 2024-07-08 ### Other - Drop support for Visual Studio 12 (2013) ([#1046](https://github.com/rust-lang/cc-rs/pull/1046)) - Use raw-dylib for windows-sys ([#1137](https://github.com/rust-lang/cc-rs/pull/1137)) - Bump msrv to 1.67 ([#1143](https://github.com/rust-lang/cc-rs/pull/1143)) - Bump msrv to 1.65 ([#1140](https://github.com/rust-lang/cc-rs/pull/1140)) - Fix clippy warnings ([#1138](https://github.com/rust-lang/cc-rs/pull/1138)) ## [1.0.105](https://github.com/rust-lang/cc-rs/compare/cc-v1.0.104...cc-v1.0.105) - 2024-07-07 ### Other - Regenerate windows sys bindings ([#1132](https://github.com/rust-lang/cc-rs/pull/1132)) - Fix generate-windows-sys-bindings ([#1133](https://github.com/rust-lang/cc-rs/pull/1133)) - Fix gen-windows-sys-binding ([#1130](https://github.com/rust-lang/cc-rs/pull/1130)) - Fix gen-windows-sys-binding ([#1127](https://github.com/rust-lang/cc-rs/pull/1127)) - Update windows-bindgen requirement from 0.57 to 0.58 ([#1123](https://github.com/rust-lang/cc-rs/pull/1123)) ## [1.0.104](https://github.com/rust-lang/cc-rs/compare/cc-v1.0.103...cc-v1.0.104) - 2024-07-01 ### Other - Fixed link break about compile-time-requirements ([#1118](https://github.com/rust-lang/cc-rs/pull/1118)) ## [1.0.103](https://github.com/rust-lang/cc-rs/compare/cc-v1.0.102...cc-v1.0.103) - 2024-06-30 ### Other - Fix compilation for wasm: env WASI_SYSROOT should be optional ([#1114](https://github.com/rust-lang/cc-rs/pull/1114)) ## [1.0.102](https://github.com/rust-lang/cc-rs/compare/cc-v1.0.101...cc-v1.0.102) - 2024-06-29 ### Other - Fix invalid wasi targets compatibility ([#1105](https://github.com/rust-lang/cc-rs/pull/1105)) - Speedup regenerate-target-info and regenerate-windows-sys ([#1110](https://github.com/rust-lang/cc-rs/pull/1110)) ## [1.0.101](https://github.com/rust-lang/cc-rs/compare/cc-v1.0.100...cc-v1.0.101) - 2024-06-25 ### Other - Use `Build::getenv` instead of `env::var*` in anywhere that makes sense ([#1103](https://github.com/rust-lang/cc-rs/pull/1103)) ## [1.0.100](https://github.com/rust-lang/cc-rs/compare/cc-v1.0.99...cc-v1.0.100) - 2024-06-23 ### Other - Update publish.yml to use release-plz ([#1101](https://github.com/rust-lang/cc-rs/pull/1101)) - Accept `OsStr` instead of `str` for flags ([#1100](https://github.com/rust-lang/cc-rs/pull/1100)) - Use `dep:` syntax to avoid implicit features. ([#1099](https://github.com/rust-lang/cc-rs/pull/1099)) - Minor clippy fixes. ([#1098](https://github.com/rust-lang/cc-rs/pull/1098)) - Fix WASI compilation for C++ ([#1083](https://github.com/rust-lang/cc-rs/pull/1083)) - Regenerate windows sys bindings ([#1096](https://github.com/rust-lang/cc-rs/pull/1096)) - Rename regenerate-windows-sys to regenerate-windows-sys.yml ([#1095](https://github.com/rust-lang/cc-rs/pull/1095)) - Create regenerate-windows-sys.yml ([#1094](https://github.com/rust-lang/cc-rs/pull/1094)) - Update windows-bindgen requirement from 0.56 to 0.57 ([#1091](https://github.com/rust-lang/cc-rs/pull/1091)) - Eagerly close tempfile to fix [#1082](https://github.com/rust-lang/cc-rs/pull/1082) ([#1087](https://github.com/rust-lang/cc-rs/pull/1087)) - Output msvc.exe in the output directory ([#1090](https://github.com/rust-lang/cc-rs/pull/1090)) - Fix clippy warnings on Windows ([#1088](https://github.com/rust-lang/cc-rs/pull/1088)) - Don't try to free DLL on drop ([#1089](https://github.com/rust-lang/cc-rs/pull/1089)) - Fix panic safety issue in StderrForwarder ([#1079](https://github.com/rust-lang/cc-rs/pull/1079)) cc-1.2.60/Cargo.lock0000644000000302001046102023000074770ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "anyhow" version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] name = "bitflags" version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" [[package]] name = "cc" version = "1.2.60" dependencies = [ "find-msvc-tools", "jobserver", "libc", "shlex", "tempfile", ] [[package]] name = "cfg-if" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "equivalent" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", "windows-sys", ] [[package]] name = "fastrand" version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" [[package]] name = "find-msvc-tools" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" [[package]] name = "foldhash" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] name = "getrandom" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", "libc", "r-efi 5.3.0", "wasip2", ] [[package]] name = "getrandom" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" dependencies = [ "cfg-if", "libc", "r-efi 6.0.0", "wasip2", "wasip3", ] [[package]] name = "hashbrown" version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "foldhash", ] [[package]] name = "hashbrown" version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" [[package]] name = "heck" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "id-arena" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" [[package]] name = "indexmap" version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", "hashbrown 0.17.0", "serde", "serde_core", ] [[package]] name = "itoa" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "jobserver" version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" dependencies = [ "getrandom 0.3.4", "libc", ] [[package]] name = "leb128fmt" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" [[package]] name = "libc" version = "0.2.184" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af" [[package]] name = "linux-raw-sys" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "log" version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "memchr" version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "once_cell" version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" [[package]] name = "prettyplease" version = "0.2.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", "syn", ] [[package]] name = "proc-macro2" version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" dependencies = [ "proc-macro2", ] [[package]] name = "r-efi" version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] name = "r-efi" version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" [[package]] name = "rustix" version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", "windows-sys", ] [[package]] name = "semver" version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" [[package]] name = "serde" version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ "serde_core", ] [[package]] name = "serde_core" version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "serde_json" version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ "itoa", "memchr", "serde", "serde_core", "zmij", ] [[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "syn" version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "tempfile" version = "3.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", "getrandom 0.4.2", "once_cell", "rustix", "windows-sys", ] [[package]] name = "unicode-ident" version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] name = "unicode-xid" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "wasip2" version = "1.0.2+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" dependencies = [ "wit-bindgen", ] [[package]] name = "wasip3" version = "0.4.0+wasi-0.3.0-rc-2026-01-06" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" dependencies = [ "wit-bindgen", ] [[package]] name = "wasm-encoder" version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" dependencies = [ "leb128fmt", "wasmparser", ] [[package]] name = "wasm-metadata" version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" dependencies = [ "anyhow", "indexmap", "wasm-encoder", "wasmparser", ] [[package]] name = "wasmparser" version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" dependencies = [ "bitflags", "hashbrown 0.15.5", "indexmap", "semver", ] [[package]] name = "windows-link" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] name = "windows-sys" version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ "windows-link", ] [[package]] name = "wit-bindgen" version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" dependencies = [ "wit-bindgen-rust-macro", ] [[package]] name = "wit-bindgen-core" version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" dependencies = [ "anyhow", "heck", "wit-parser", ] [[package]] name = "wit-bindgen-rust" version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" dependencies = [ "anyhow", "heck", "indexmap", "prettyplease", "syn", "wasm-metadata", "wit-bindgen-core", "wit-component", ] [[package]] name = "wit-bindgen-rust-macro" version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" dependencies = [ "anyhow", "prettyplease", "proc-macro2", "quote", "syn", "wit-bindgen-core", "wit-bindgen-rust", ] [[package]] name = "wit-component" version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" dependencies = [ "anyhow", "bitflags", "indexmap", "log", "serde", "serde_derive", "serde_json", "wasm-encoder", "wasm-metadata", "wasmparser", "wit-parser", ] [[package]] name = "wit-parser" version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" dependencies = [ "anyhow", "id-arena", "indexmap", "log", "semver", "serde", "serde_derive", "serde_json", "unicode-xid", "wasmparser", ] [[package]] name = "zmij" version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" cc-1.2.60/Cargo.toml0000644000000034151046102023000075320ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" rust-version = "1.63" name = "cc" version = "1.2.60" authors = ["Alex Crichton "] build = false exclude = [ "/.github", "tests", "src/bin", ] autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = """ A build-time dependency for Cargo build scripts to assist in invoking the native C compiler to compile native C code into a static archive to be linked into Rust code. """ homepage = "https://github.com/rust-lang/cc-rs" documentation = "https://docs.rs/cc" readme = "README.md" keywords = ["build-dependencies"] categories = ["development-tools::build-utils"] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/cc-rs" [features] jobserver = [] parallel = [ "dep:libc", "dep:jobserver", ] [lib] name = "cc" path = "src/lib.rs" [dependencies.find-msvc-tools] version = "0.1.9" [dependencies.jobserver] version = "0.1.30" optional = true default-features = false [dependencies.shlex] version = "1.3.0" [dev-dependencies.tempfile] version = "3" [target."cfg(unix)".dependencies.libc] version = "0.2.62" optional = true default-features = false [lints.rust.unexpected_cfgs] level = "allow" priority = 0 check-cfg = ["cfg(disable_clang_cl_tests)"] [profile.release] opt-level = 3 lto = true cc-1.2.60/Cargo.toml.orig000064400000000000000000000034101046102023000131640ustar 00000000000000[package] name = "cc" version = "1.2.60" authors = ["Alex Crichton "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/cc-rs" homepage = "https://github.com/rust-lang/cc-rs" documentation = "https://docs.rs/cc" description = """ A build-time dependency for Cargo build scripts to assist in invoking the native C compiler to compile native C code into a static archive to be linked into Rust code. """ keywords = ["build-dependencies"] readme = "README.md" categories = ["development-tools::build-utils"] # The binary target is only used by tests. exclude = ["/.github", "tests", "src/bin"] edition = "2018" rust-version = "1.63" [dependencies] jobserver = { version = "0.1.30", default-features = false, optional = true } shlex = "1.3.0" find-msvc-tools = { version = "0.1.9", path = "find-msvc-tools" } [target.'cfg(unix)'.dependencies] # Don't turn on the feature "std" for this, see https://github.com/rust-lang/cargo/issues/4866 # which is still an issue with `resolver = "1"`. libc = { version = "0.2.62", default-features = false, optional = true } [features] parallel = ["dep:libc", "dep:jobserver"] # This is a placeholder feature for people who incorrectly used `cc` with `features = ["jobserver"]` # so that they aren't broken. This has never enabled `parallel`, so we won't do that. jobserver = [] [dev-dependencies] tempfile = "3" [workspace] members = [ "find-msvc-tools", "dev-tools/cc-test", "dev-tools/gen-target-info", "dev-tools/gen-windows-sys-binding", "dev-tools/wasi-test", ] [patch.crates-io] cc = { path = "." } [lints.rust] unexpected_cfgs = { level = "allow", check-cfg = ['cfg(disable_clang_cl_tests)'] } [profile.release] opt-level = 3 # Or "s" or "z" for different optimization goals lto = true cc-1.2.60/LICENSE-APACHE000064400000000000000000000251371046102023000122330ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. cc-1.2.60/LICENSE-MIT000064400000000000000000000020411046102023000117300ustar 00000000000000Copyright (c) 2014 Alex Crichton 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. cc-1.2.60/README.md000064400000000000000000000020461046102023000115600ustar 00000000000000# cc-rs A library for [Cargo build scripts](https://doc.rust-lang.org/cargo/reference/build-scripts.html) to compile a set of C/C++/assembly/CUDA files into a static archive for Cargo to link into the crate being built. This crate does not compile code itself; it calls out to the default compiler for the platform. This crate will automatically detect situations such as cross compilation and various environment variables and will build code appropriately. Refer to the [documentation](https://docs.rs/cc) for detailed usage instructions. ## License This project is licensed under either of * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or https://www.apache.org/licenses/LICENSE-2.0) * MIT license ([LICENSE-MIT](LICENSE-MIT) or https://opensource.org/license/mit) at your option. ### Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in cc-rs by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. cc-1.2.60/clippy.toml000064400000000000000000000005671046102023000125040ustar 00000000000000disallowed-methods = [ { path = "std::env::var_os", reason = "Please use Build::getenv" }, { path = "std::env::var", reason = "Please use Build::getenv" }, { path = "std::env::set_var", reason = "use `GlobalEnv::lock().set`" }, { path = "std::env::remove_var", reason = "use `GlobalEnv::lock().remove`" }, ] doc-valid-idents = ["AppleClang", "OpenBSD", ".."] cc-1.2.60/src/command_helpers.rs000064400000000000000000000372201046102023000146000ustar 00000000000000//! Miscellaneous helpers for running commands use std::{ borrow::Cow, collections::hash_map, ffi::OsString, fmt::Display, fs, hash::Hasher, io::{self, Read, Write}, path::Path, process::{Child, ChildStderr, Command, Output, Stdio}, sync::{ atomic::{AtomicBool, Ordering}, Arc, }, }; use crate::{utilities::cargo_env_var_os, Error, ErrorKind, Object}; #[derive(Clone, Debug)] pub(crate) struct CargoOutput { pub(crate) metadata: bool, pub(crate) warnings: bool, pub(crate) debug: bool, pub(crate) output: OutputKind, checked_dbg_var: Arc, } /// Different strategies for handling compiler output (to stdout) #[derive(Clone, Debug)] pub(crate) enum OutputKind { /// Forward the output to this process' stdout ([`Stdio::inherit()`]) Forward, /// Discard the output ([`Stdio::null()`]) Discard, /// Capture the result (`[Stdio::piped()`]) Capture, } impl CargoOutput { pub(crate) fn new() -> Self { #[allow(clippy::disallowed_methods)] Self { metadata: true, warnings: true, output: OutputKind::Forward, debug: match std::env::var_os("CC_ENABLE_DEBUG_OUTPUT") { Some(v) => v != "0" && v != "false" && !v.is_empty(), None => false, }, checked_dbg_var: Arc::new(AtomicBool::new(false)), } } pub(crate) fn print_metadata(&self, s: &dyn Display) { if self.metadata { println!("{s}"); } } pub(crate) fn print_warning(&self, arg: &dyn Display) { if self.warnings { println!("cargo:warning={arg}"); } } pub(crate) fn print_debug(&self, arg: &dyn Display) { if self.metadata && self .checked_dbg_var .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed) .is_ok() { println!("cargo:rerun-if-env-changed=CC_ENABLE_DEBUG_OUTPUT"); } if self.debug { println!("{arg}"); } } fn stdio_for_warnings(&self) -> Stdio { if self.warnings { Stdio::piped() } else { Stdio::null() } } fn stdio_for_output(&self) -> Stdio { match self.output { OutputKind::Capture => Stdio::piped(), OutputKind::Forward => Stdio::inherit(), OutputKind::Discard => Stdio::null(), } } } pub(crate) struct StderrForwarder { inner: Option<(ChildStderr, Vec)>, #[cfg(feature = "parallel")] is_non_blocking: bool, #[cfg(feature = "parallel")] bytes_available_failed: bool, /// number of bytes buffered in inner bytes_buffered: usize, } const MIN_BUFFER_CAPACITY: usize = 100; impl StderrForwarder { pub(crate) fn new(child: &mut Child) -> Self { Self { inner: child .stderr .take() .map(|stderr| (stderr, Vec::with_capacity(MIN_BUFFER_CAPACITY))), bytes_buffered: 0, #[cfg(feature = "parallel")] is_non_blocking: false, #[cfg(feature = "parallel")] bytes_available_failed: false, } } pub(crate) fn forward_available(&mut self) -> bool { if let Some((stderr, buffer)) = self.inner.as_mut() { loop { // For non-blocking we check to see if there is data available, so we should try to // read at least that much. For blocking, always read at least the minimum amount. #[cfg(not(feature = "parallel"))] let to_reserve = MIN_BUFFER_CAPACITY; #[cfg(feature = "parallel")] let to_reserve = if self.is_non_blocking && !self.bytes_available_failed { match crate::parallel::stderr::bytes_available(stderr) { #[cfg(windows)] Ok(0) => break false, #[cfg(unix)] Ok(0) => { // On Unix, depending on the implementation, we may sometimes get 0 in a // loop (either there is data available or the pipe is broken), so // continue with the non-blocking read anyway. MIN_BUFFER_CAPACITY } #[cfg(windows)] Err(_) => { // On Windows, if we get an error then the pipe is broken, so flush // the buffer and bail. if !buffer.is_empty() { write_warning(&buffer[..]); } self.inner = None; break true; } #[cfg(unix)] Err(_) => { // On Unix, depending on the implementation, we may get spurious // errors so make a note not to use bytes_available again and try // the non-blocking read anyway. self.bytes_available_failed = true; MIN_BUFFER_CAPACITY } #[cfg(target_family = "wasm")] Err(_) => panic!("bytes_available should always succeed on wasm"), Ok(bytes_available) => MIN_BUFFER_CAPACITY.max(bytes_available), } } else { MIN_BUFFER_CAPACITY }; if self.bytes_buffered + to_reserve > buffer.len() { buffer.resize(self.bytes_buffered + to_reserve, 0); } match stderr.read(&mut buffer[self.bytes_buffered..]) { Err(err) if err.kind() == std::io::ErrorKind::WouldBlock => { // No data currently, yield back. break false; } Err(err) if err.kind() == std::io::ErrorKind::Interrupted => { // Interrupted, try again. continue; } Ok(bytes_read) if bytes_read != 0 => { self.bytes_buffered += bytes_read; let mut consumed = 0; for line in buffer[..self.bytes_buffered].split_inclusive(|&b| b == b'\n') { // Only forward complete lines, leave the rest in the buffer. if let Some((b'\n', line)) = line.split_last() { consumed += line.len() + 1; write_warning(line); } } if consumed > 0 && consumed < self.bytes_buffered { // Remove the consumed bytes from buffer buffer.copy_within(consumed.., 0); } self.bytes_buffered -= consumed; } res => { // End of stream: flush remaining data and bail. if self.bytes_buffered > 0 { write_warning(&buffer[..self.bytes_buffered]); } if let Err(err) = res { write_warning( format!("Failed to read from child stderr: {err}").as_bytes(), ); } self.inner.take(); break true; } } } } else { true } } #[cfg(feature = "parallel")] pub(crate) fn set_non_blocking(&mut self) -> Result<(), Error> { assert!(!self.is_non_blocking); #[cfg(unix)] if let Some((stderr, _)) = self.inner.as_ref() { crate::parallel::stderr::set_non_blocking(stderr)?; } self.is_non_blocking = true; Ok(()) } #[cfg(feature = "parallel")] pub(crate) fn forward_all(&mut self) { while !self.forward_available() {} } #[cfg(not(feature = "parallel"))] fn forward_all(&mut self) { let forward_result = self.forward_available(); assert!(forward_result, "Should have consumed all data"); } } fn write_warning(line: &[u8]) { let stdout = io::stdout(); let mut stdout = stdout.lock(); stdout.write_all(b"cargo:warning=").unwrap(); stdout.write_all(line).unwrap(); stdout.write_all(b"\n").unwrap(); } fn wait_on_child( cmd: &Command, child: &mut Child, cargo_output: &CargoOutput, ) -> Result<(), Error> { StderrForwarder::new(child).forward_all(); let status = match child.wait() { Ok(s) => s, Err(e) => { return Err(Error::new( ErrorKind::ToolExecError, format!("failed to wait on spawned child process `{cmd:?}`: {e}"), )); } }; cargo_output.print_debug(&status); if status.success() { Ok(()) } else { Err(Error::new( ErrorKind::ToolExecError, format!("command did not execute successfully (status code {status}): {cmd:?}"), )) } } /// Find the destination object path for each file in the input source files, /// and store them in the output Object. pub(crate) fn objects_from_files(files: &[Arc], dst: &Path) -> Result, Error> { let mut objects = Vec::with_capacity(files.len()); for file in files { let basename = file .file_name() .ok_or_else(|| { Error::new( ErrorKind::InvalidArgument, "No file_name for object file path!", ) })? .to_string_lossy(); let dirname = file .parent() .ok_or_else(|| { Error::new( ErrorKind::InvalidArgument, "No parent for object file path!", ) })? .to_string_lossy(); // Hash the dirname. This should prevent conflicts if we have multiple // object files with the same filename in different subfolders. let mut hasher = hash_map::DefaultHasher::new(); // Make the dirname relative (if possible) to avoid full system paths influencing the sha // and making the output system-dependent let dirname = if let Some(root) = cargo_env_var_os("CARGO_MANIFEST_DIR") { let root = root.to_string_lossy(); Cow::Borrowed(dirname.strip_prefix(&*root).unwrap_or(&dirname)) } else { dirname }; hasher.write(dirname.as_bytes()); if let Some(extension) = file.extension() { hasher.write(extension.to_string_lossy().as_bytes()); } let obj = dst .join(format!("{:016x}-{}", hasher.finish(), basename)) .with_extension("o"); match obj.parent() { Some(s) => fs::create_dir_all(s)?, None => { return Err(Error::new( ErrorKind::InvalidArgument, "dst is an invalid path with no parent", )); } }; objects.push(Object::new(file.to_path_buf(), obj)); } Ok(objects) } pub(crate) fn run(cmd: &mut Command, cargo_output: &CargoOutput) -> Result<(), Error> { let mut child = spawn(cmd, cargo_output)?; wait_on_child(cmd, &mut child, cargo_output) } /// Like [`run`], but stderr is only forwarded as `cargo:warning=` when the /// command succeeds. On failure, stderr is silently discarded. /// /// Useful for probe commands where failure is expected and the error /// message is not actionable. pub(crate) fn run_silent_on_error( cmd: &mut Command, cargo_output: &CargoOutput, ) -> Result<(), Error> { let Output { status, stdout: _, stderr, } = spawn_and_wait_for_output(cmd, cargo_output)?; cargo_output.print_debug(&status); if status.success() { if cargo_output.warnings { stderr .split(|&b| b == b'\n') .map(|line| line.strip_suffix(b"\r").unwrap_or(line)) .filter(|line| !line.is_empty()) .for_each(write_warning); } Ok(()) } else { Err(Error::new( ErrorKind::ToolExecError, format!("command did not execute successfully (status code {status}): {cmd:?}"), )) } } pub(crate) fn spawn_and_wait_for_output( cmd: &mut Command, cargo_output: &CargoOutput, ) -> Result { // We specifically need the output to be captured, so override default let mut captured_cargo_output = cargo_output.clone(); captured_cargo_output.output = OutputKind::Capture; spawn(cmd, &captured_cargo_output)? .wait_with_output() .map_err(|e| { Error::new( ErrorKind::ToolExecError, format!("failed to wait on spawned child process `{cmd:?}`: {e}"), ) }) } pub(crate) fn run_output(cmd: &mut Command, cargo_output: &CargoOutput) -> Result, Error> { let Output { status, stdout, stderr, } = spawn_and_wait_for_output(cmd, cargo_output)?; stderr .split(|&b| b == b'\n') .filter(|part| !part.is_empty()) .for_each(write_warning); cargo_output.print_debug(&status); if status.success() { Ok(stdout) } else { Err(Error::new( ErrorKind::ToolExecError, format!("command did not execute successfully (status code {status}): {cmd:?}"), )) } } pub(crate) fn spawn(cmd: &mut Command, cargo_output: &CargoOutput) -> Result { struct ResetStderr<'cmd>(&'cmd mut Command); impl Drop for ResetStderr<'_> { fn drop(&mut self) { // Reset stderr to default to release pipe_writer so that print thread will // not block forever. self.0.stderr(Stdio::inherit()); } } cargo_output.print_debug(&format_args!("running: {cmd:?}")); let cmd = ResetStderr(cmd); let child = cmd .0 .stderr(cargo_output.stdio_for_warnings()) .stdout(cargo_output.stdio_for_output()) .spawn(); match child { Ok(child) => Ok(child), Err(ref e) if e.kind() == io::ErrorKind::NotFound => { let extra = if cfg!(windows) { " (see https://docs.rs/cc/latest/cc/#compile-time-requirements for help)" } else { "" }; Err(Error::new( ErrorKind::ToolNotFound, format!("failed to find tool {:?}: {e}{extra}", cmd.0.get_program()), )) } Err(e) => Err(Error::new( ErrorKind::ToolExecError, format!("command `{:?}` failed to start: {e}", cmd.0), )), } } pub(crate) struct CmdAddOutputFileArgs { pub(crate) cuda: bool, pub(crate) is_assembler_msvc: bool, pub(crate) msvc: bool, pub(crate) clang: bool, pub(crate) gnu: bool, pub(crate) is_asm: bool, pub(crate) is_arm: bool, } pub(crate) fn command_add_output_file(cmd: &mut Command, dst: &Path, args: CmdAddOutputFileArgs) { if args.is_assembler_msvc || !(!args.msvc || args.clang || args.gnu || args.cuda || (args.is_asm && args.is_arm)) { let mut s = OsString::from("-Fo"); s.push(dst); cmd.arg(s); } else { cmd.arg("-o").arg(dst); } } cc-1.2.60/src/detect_compiler_family.c000064400000000000000000000003161046102023000157350ustar 00000000000000#ifdef __clang__ #pragma message "clang" #endif #ifdef __GNUC__ #pragma message "gcc" #endif #ifdef __EMSCRIPTEN__ #pragma message "emscripten" #endif #ifdef __VXWORKS__ #pragma message "VxWorks" #endif cc-1.2.60/src/flags.rs000064400000000000000000000573231046102023000125420ustar 00000000000000use crate::target::TargetInfo; use crate::{Build, Error, ErrorKind, Tool, ToolFamily}; use std::borrow::Cow; use std::ffi::OsString; #[derive(Debug, PartialEq, Default)] pub(crate) struct RustcCodegenFlags<'a> { branch_protection: Option<&'a str>, code_model: Option<&'a str>, no_vectorize_loops: bool, no_vectorize_slp: bool, profile_generate: Option<&'a str>, profile_use: Option<&'a str>, control_flow_guard: Option<&'a str>, lto: Option<&'a str>, relocation_model: Option<&'a str>, embed_bitcode: Option, force_frame_pointers: Option, no_redzone: Option, soft_float: Option, dwarf_version: Option, stack_protector: Option<&'a str>, linker_plugin_lto: Option, } impl<'this> RustcCodegenFlags<'this> { // Parse flags obtained from CARGO_ENCODED_RUSTFLAGS pub(crate) fn parse(rustflags_env: &'this str) -> Result { fn is_flag_prefix(flag: &str) -> bool { [ "-Z", "-C", "--codegen", "-L", "-l", "-o", "-W", "--warn", "-A", "--allow", "-D", "--deny", "-F", "--forbid", ] .contains(&flag) } fn handle_flag_prefix<'a>(prev: &'a str, curr: &'a str) -> (&'a str, &'a str) { match prev { "--codegen" | "-C" => ("-C", curr), // Handle flags passed like --codegen=code-model=small _ if curr.starts_with("--codegen=") => ("-C", &curr[10..]), "-Z" => ("-Z", curr), "-L" | "-l" | "-o" => (prev, curr), // Handle lint flags "-W" | "--warn" => ("-W", curr), "-A" | "--allow" => ("-A", curr), "-D" | "--deny" => ("-D", curr), "-F" | "--forbid" => ("-F", curr), _ => ("", curr), } } let mut codegen_flags = Self::default(); let mut prev_prefix = None; for curr in rustflags_env.split("\u{1f}") { let prev = prev_prefix.take().unwrap_or(""); if prev.is_empty() && is_flag_prefix(curr) { prev_prefix = Some(curr); continue; } let (prefix, rustc_flag) = handle_flag_prefix(prev, curr); codegen_flags.set_rustc_flag(prefix, rustc_flag)?; } Ok(codegen_flags) } fn set_rustc_flag(&mut self, prefix: &str, flag: &'this str) -> Result<(), Error> { // Convert a textual representation of a bool-like rustc flag argument into an actual bool fn arg_to_bool(arg: impl AsRef) -> Option { match arg.as_ref() { "y" | "yes" | "on" | "true" => Some(true), "n" | "no" | "off" | "false" => Some(false), _ => None, } } fn arg_to_u32(arg: impl AsRef) -> Option { arg.as_ref().parse().ok() } let (flag, value) = if let Some((flag, value)) = flag.split_once('=') { (flag, Some(value)) } else { (flag, None) }; let flag = if prefix.is_empty() { Cow::Borrowed(flag) } else { Cow::Owned(format!("{prefix}{flag}")) }; let flag = flag.as_ref(); fn flag_not_empty_generic( flag: &str, flag_value: Option, ) -> Result, Error> { if let Some(flag_value) = flag_value { Ok(Some(flag_value)) } else { Err(Error::new( ErrorKind::InvalidFlag, format!("{flag} must have a value"), )) } } let flag_not_empty = |flag_value| flag_not_empty_generic(flag, flag_value); match flag { // https://doc.rust-lang.org/rustc/codegen-options/index.html#code-model "-Ccode-model" => { self.code_model = flag_not_empty(value)?; } // https://doc.rust-lang.org/rustc/codegen-options/index.html#no-vectorize-loops "-Cno-vectorize-loops" => self.no_vectorize_loops = true, // https://doc.rust-lang.org/rustc/codegen-options/index.html#no-vectorize-slp "-Cno-vectorize-slp" => self.no_vectorize_slp = true, // https://doc.rust-lang.org/rustc/codegen-options/index.html#profile-generate "-Cprofile-generate" => { self.profile_generate = flag_not_empty(value)?; } // https://doc.rust-lang.org/rustc/codegen-options/index.html#profile-use "-Cprofile-use" => { self.profile_use = flag_not_empty(value)?; } // https://doc.rust-lang.org/rustc/codegen-options/index.html#control-flow-guard "-Ccontrol-flow-guard" => self.control_flow_guard = value.or(Some("true")), // https://doc.rust-lang.org/rustc/codegen-options/index.html#lto // // This variable is currently unused, we just keep it in case we need it in future "-Clto" => self.lto = value.or(Some("true")), // https://doc.rust-lang.org/rustc/linker-plugin-lto.html "-Clinker-plugin-lto" => self.linker_plugin_lto = Some(true), // https://doc.rust-lang.org/rustc/codegen-options/index.html#relocation-model "-Crelocation-model" => { self.relocation_model = flag_not_empty(value)?; } // https://doc.rust-lang.org/rustc/codegen-options/index.html#embed-bitcode "-Cembed-bitcode" => self.embed_bitcode = value.map_or(Some(true), arg_to_bool), // https://doc.rust-lang.org/rustc/codegen-options/index.html#force-frame-pointers "-Cforce-frame-pointers" => { self.force_frame_pointers = value.map_or(Some(true), arg_to_bool) } // https://doc.rust-lang.org/rustc/codegen-options/index.html#no-redzone "-Cno-redzone" => self.no_redzone = value.map_or(Some(true), arg_to_bool), // https://doc.rust-lang.org/rustc/codegen-options/index.html#soft-float // Note: This flag is now deprecated in rustc. "-Csoft-float" => self.soft_float = value.map_or(Some(true), arg_to_bool), // https://doc.rust-lang.org/beta/unstable-book/compiler-flags/branch-protection.html // FIXME: Drop the -Z variant and update the doc link once the option is stabilised "-Zbranch-protection" | "-Cbranch-protection" => { self.branch_protection = flag_not_empty(value)?; } // https://doc.rust-lang.org/beta/rustc/codegen-options/index.html#dwarf-version "-Cdwarf-version" => { self.dwarf_version = flag_not_empty_generic(flag, value.and_then(arg_to_u32))?; } // https://github.com/rust-lang/rust/issues/114903 // FIXME: Drop the -Z variant and update the doc link once the option is stabilized "-Zstack-protector" | "-Cstack-protector" => { self.stack_protector = flag_not_empty(value)?; } _ => {} } Ok(()) } // Rust and clang/cc don't agree on what equivalent flags should look like. pub(crate) fn cc_flags(&self, build: &Build, tool: &mut Tool, target: &TargetInfo<'_>) { let family = tool.family; // Push `flag` to `flags` if it is supported by the currently used CC let mut push_if_supported = |flag: OsString| { if build .is_flag_supported_inner(&flag, tool, target) .unwrap_or(false) { tool.args.push(flag); } else { build.cargo_output.print_warning(&format!( "Inherited flag {flag:?} is not supported by the currently used CC" )); } }; let clang_or_gnu = matches!(family, ToolFamily::Clang { .. }) || matches!(family, ToolFamily::Gnu); // Flags shared between clang and gnu if clang_or_gnu { // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-mbranch-protection // https://gcc.gnu.org/onlinedocs/gcc/AArch64-Options.html#index-mbranch-protection (Aarch64) // https://gcc.gnu.org/onlinedocs/gcc/ARM-Options.html#index-mbranch-protection-1 (ARM) // https://developer.arm.com/documentation/101754/0619/armclang-Reference/armclang-Command-line-Options/-mbranch-protection if let Some(value) = self.branch_protection { push_if_supported( format!("-mbranch-protection={}", value.replace(",", "+")).into(), ); } // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-mcmodel // https://gcc.gnu.org/onlinedocs/gcc/Option-Summary.html (several archs, search for `-mcmodel=`). // FIXME(madsmtm): Parse the model, to make sure we pass the correct value (depending on arch). if let Some(value) = self.code_model { push_if_supported(format!("-mcmodel={value}").into()); } // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fno-vectorize // https://gcc.gnu.org/onlinedocs/gnat_ugn/Vectorization-of-loops.html if self.no_vectorize_loops { push_if_supported("-fno-vectorize".into()); } // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fno-slp-vectorize // https://gcc.gnu.org/onlinedocs/gnat_ugn/Vectorization-of-loops.html if self.no_vectorize_slp { push_if_supported("-fno-slp-vectorize".into()); } if let Some(value) = self.relocation_model { let cc_flag = match value { // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fPIC // https://gcc.gnu.org/onlinedocs/gcc/Code-Gen-Options.html#index-fPIC "pic" => Some("-fPIC"), // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fPIE // https://gcc.gnu.org/onlinedocs/gcc/Code-Gen-Options.html#index-fPIE "pie" => Some("-fPIE"), // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-mdynamic-no-pic // https://gcc.gnu.org/onlinedocs/gcc/RS_002f6000-and-PowerPC-Options.html#index-mdynamic-no-pic "dynamic-no-pic" => Some("-mdynamic-no-pic"), _ => None, }; if let Some(cc_flag) = cc_flag { push_if_supported(cc_flag.into()); } } // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fno-omit-frame-pointer // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fomit-frame-pointer // https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#index-fomit-frame-pointer if let Some(value) = self.force_frame_pointers { let cc_flag = if value { "-fno-omit-frame-pointer" } else { "-fomit-frame-pointer" }; push_if_supported(cc_flag.into()); } // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-mno-red-zone // https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html#index-mno-red-zone // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-mred-zone // https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html#index-mred-zone if let Some(value) = self.no_redzone { let cc_flag = if value { "-mno-red-zone" } else { "-mred-zone" }; push_if_supported(cc_flag.into()); } // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-msoft-float // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-mhard-float // https://gcc.gnu.org/onlinedocs/gcc/Option-Summary.html (several archs, search for `-msoft-float`). // https://gcc.gnu.org/onlinedocs/gcc/Option-Summary.html (several archs, search for `-mhard-float`). if let Some(value) = self.soft_float { let cc_flag = if value { "-msoft-float" } else { // Do not use -mno-soft-float, that's basically just an alias for -mno-implicit-float. "-mhard-float" }; push_if_supported(cc_flag.into()); } // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-gdwarf-2 // https://gcc.gnu.org/onlinedocs/gcc/Debugging-Options.html#index-gdwarf if let Some(value) = self.dwarf_version { push_if_supported(format!("-gdwarf-{value}").into()); } // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fstack-protector // https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html#index-fstack-protector if let Some(value) = self.stack_protector { // don't need to propagate stack-protector on MSVC since /GS is already the default // https://learn.microsoft.com/en-us/cpp/build/reference/gs-buffer-security-check?view=msvc-170 // // Do NOT `stack-protector=none` since it weakens security for C code, // and `-Zstack-protector=basic` is deprecated and will be removed soon. let cc_flag = match value { "strong" => Some("-fstack-protector-strong"), "all" => Some("-fstack-protector-all"), _ => None, }; if let Some(cc_flag) = cc_flag { push_if_supported(cc_flag.into()); } } } // Compiler-exclusive flags match family { ToolFamily::Clang { .. } => { // GNU and Clang compilers both support the same PGO flags, but they use different libraries and // different formats for the profile files which are not compatible. // clang and rustc both internally use llvm, so we want to inherit the PGO flags only for clang. // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fprofile-generate if let Some(value) = self.profile_generate { push_if_supported(format!("-fprofile-generate={value}").into()); } // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fprofile-use if let Some(value) = self.profile_use { push_if_supported(format!("-fprofile-use={value}").into()); } // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fembed-bitcode if let Some(value) = self.embed_bitcode { let cc_val = if value { "all" } else { "off" }; push_if_supported(format!("-fembed-bitcode={cc_val}").into()); } // https://doc.rust-lang.org/rustc/linker-plugin-lto.html if self.linker_plugin_lto.unwrap_or(false) { // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-flto // In order to use linker-plugin-lto to achieve cross-lang lto, cc has to use thin LTO // to compile the c/c++ libraries because llvm linker plugin/lld uses thin LTO by default. // And for thin LTO in linker plugin to work, the archive also has to be compiled using thin LTO, // since thin LTO generates extra information that fat LTO does not generate that // is required for thin LTO process. push_if_supported("-flto=thin".into()); } // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-mguard if let Some(value) = self.control_flow_guard { let cc_val = match value { "y" | "yes" | "on" | "true" | "checks" => Some("cf"), "nochecks" => Some("cf-nochecks"), "n" | "no" | "off" | "false" => Some("none"), _ => None, }; if let Some(cc_val) = cc_val { push_if_supported(format!("-mguard={cc_val}").into()); } } } ToolFamily::Gnu => {} ToolFamily::Msvc { .. } => { // https://learn.microsoft.com/en-us/cpp/build/reference/guard-enable-control-flow-guard if let Some(value) = self.control_flow_guard { let cc_val = match value { "y" | "yes" | "on" | "true" | "checks" => Some("cf"), "n" | "no" | "off" | "false" => Some("cf-"), _ => None, }; if let Some(cc_val) = cc_val { push_if_supported(format!("/guard:{cc_val}").into()); } } // https://learn.microsoft.com/en-us/cpp/build/reference/oy-frame-pointer-omission if let Some(value) = self.force_frame_pointers { // Flag is unsupported on 64-bit arches if !target.arch.contains("64") { let cc_flag = if value { "/Oy-" } else { "/Oy" }; push_if_supported(cc_flag.into()); } } } } } } #[cfg(test)] mod tests { use super::*; #[track_caller] fn check(env: &str, expected: &RustcCodegenFlags) { let actual = RustcCodegenFlags::parse(env).unwrap(); assert_eq!(actual, *expected); } #[test] fn codegen_type() { let expected = RustcCodegenFlags { code_model: Some("tiny"), ..RustcCodegenFlags::default() }; check("-Ccode-model=tiny", &expected); check("-C\u{1f}code-model=tiny", &expected); check("--codegen\u{1f}code-model=tiny", &expected); check("--codegen=code-model=tiny", &expected); } #[test] fn precedence() { check( "-ccode-model=tiny\u{1f}-Ccode-model=small", &RustcCodegenFlags { code_model: Some("small"), ..RustcCodegenFlags::default() }, ); } #[test] fn two_valid_prefixes() { let expected = RustcCodegenFlags::default(); check("-L\u{1f}-Clto", &expected); } #[test] fn stack_protector() { let expected = RustcCodegenFlags { stack_protector: Some("strong"), ..RustcCodegenFlags::default() }; check("-Zstack-protector=strong", &expected); check("-Cstack-protector=strong", &expected); } #[test] fn three_valid_prefixes() { let expected = RustcCodegenFlags { lto: Some("true"), ..RustcCodegenFlags::default() }; check("-L\u{1f}-L\u{1f}-Clto", &expected); } #[test] fn all_rustc_flags() { // Throw all possible flags at the parser to catch false positives let flags = [ // Set all the flags we recognise first "-Ccode-model=tiny", "-Ccontrol-flow-guard=yes", "-Cembed-bitcode=no", "-Cforce-frame-pointers=yes", "-Clto=false", "-Clink-dead-code=yes", "-Cno-redzone=yes", "-Cno-vectorize-loops", "-Cno-vectorize-slp", "-Cprofile-generate=fooprofile", "-Cprofile-use=fooprofile", "-Crelocation-model=pic", "-Csoft-float=yes", "-Zbranch-protection=bti,pac-ret,leaf", "-Cdwarf-version=5", "-Zstack-protector=strong", // Set flags we don't recognise but rustc supports next // rustc flags "--cfg", "a", "--check-cfg 'cfg(verbose)", "-L", "/usr/lib/foo", "-l", "static:+whole-archive=mylib", "--crate-type=dylib", "--crate-name=foo", "--edition=2021", "--emit=asm", "--print=crate-name", "-g", "-O", "-o", "foooutput", "--out-dir", "foooutdir", "--target", "aarch64-unknown-linux-gnu", "-W", "missing-docs", "-D", "unused-variables", "--force-warn", "dead-code", "-A", "unused", "-F", "unused", "--cap-lints", "warn", "--version", "--verbose", "-v", "--extern", "foocrate", "--sysroot", "fooroot", "--error-format", "human", "--color", "auto", "--diagnostic-width", "80", "--remap-path-prefix", "foo=bar", "--json=artifact", // Codegen flags "-Car", "-Ccodegen-units=1", "-Ccollapse-macro-debuginfo=yes", "-Cdebug-assertions=yes", "-Cdebuginfo=1", "-Cdefault-linker-libraries=yes", "-Cdlltool=foo", "-Cextra-filename=foo", "-Cforce-unwind-tables=yes", "-Cincremental=foodir", "-Cinline-threshold=6", "-Cinstrument-coverage", "-Clink-arg=-foo", "-Clink-args=-foo", "-Clink-self-contained=yes", "-Clinker=lld", "-Clinker-flavor=ld.lld", "-Clinker-plugin-lto=/path", "-Cllvm-args=foo", "-Cmetadata=foo", "-Cno-prepopulate-passes", "-Cno-stack-check", "-Copt-level=3", "-Coverflow-checks=yes", "-Cpanic=abort", "-Cpasses=foopass", "-Cprefer-dynamic=yes", "-Crelro-level=partial", "-Cremark=all", "-Crpath=yes", "-Csave-temps=yes", "-Csplit-debuginfo=packed", "-Cstrip=symbols", "-Csymbol-mangling-version=v0", "-Ctarget-cpu=native", "-Ctarget-feature=+sve", // Unstable options "-Ztune-cpu=machine", ]; check( &flags.join("\u{1f}"), &RustcCodegenFlags { code_model: Some("tiny"), control_flow_guard: Some("yes"), embed_bitcode: Some(false), force_frame_pointers: Some(true), lto: Some("false"), no_redzone: Some(true), no_vectorize_loops: true, no_vectorize_slp: true, profile_generate: Some("fooprofile"), profile_use: Some("fooprofile"), relocation_model: Some("pic"), soft_float: Some(true), branch_protection: Some("bti,pac-ret,leaf"), dwarf_version: Some(5), stack_protector: Some("strong"), linker_plugin_lto: Some(true), }, ); } } cc-1.2.60/src/lib.rs000064400000000000000000005440721046102023000122160ustar 00000000000000//! A library for [Cargo build scripts](https://doc.rust-lang.org/cargo/reference/build-scripts.html) //! to compile a set of C/C++/assembly/CUDA files into a static archive for Cargo //! to link into the crate being built. This crate does not compile code itself; //! it calls out to the default compiler for the platform. This crate will //! automatically detect situations such as cross compilation and //! [various environment variables](#external-configuration-via-environment-variables) and will build code appropriately. //! //! # Example //! //! First, you'll want to both add a build script for your crate (`build.rs`) and //! also add this crate to your `Cargo.toml` via: //! //! ```toml //! [build-dependencies] //! cc = "1.0" //! ``` //! //! Next up, you'll want to write a build script like so: //! //! ```rust,no_run //! // build.rs //! cc::Build::new() //! .file("foo.c") //! .file("bar.c") //! .compile("foo"); //! ``` //! //! And that's it! Running `cargo build` should take care of the rest and your Rust //! application will now have the C files `foo.c` and `bar.c` compiled into a file //! named `libfoo.a`. If the C files contain //! //! ```c //! void foo_function(void) { ... } //! ``` //! //! and //! //! ```c //! int32_t bar_function(int32_t x) { ... } //! ``` //! //! you can call them from Rust by declaring them in //! your Rust code like so: //! //! ```rust,no_run //! extern "C" { //! fn foo_function(); //! fn bar_function(x: i32) -> i32; //! } //! //! pub fn call() { //! unsafe { //! foo_function(); //! bar_function(42); //! } //! } //! //! fn main() { //! call(); //! } //! ``` //! //! See [the Rustonomicon](https://doc.rust-lang.org/nomicon/ffi.html) for more details. //! //! # External configuration via environment variables //! //! To control the programs and flags used for building, the builder can set a //! number of different environment variables. //! //! * `CFLAGS` - a series of space separated flags passed to compilers. Note that //! individual flags cannot currently contain spaces, so doing //! something like: `-L=foo\ bar` is not possible. //! * `CC` - the actual C compiler used. Note that this supports passing a known //! wrapper via `sccache cc`. This compiler must understand the `-c` flag. For //! certain `TARGET`s, it also is assumed to know about other flags (most //! common is `-fPIC`). //! ccache, distcc, sccache, icecc, cachepot and buildcache are supported, //! for sccache, simply set `CC` to `sccache cc`. //! For other custom `CC` wrapper, just set `CC_KNOWN_WRAPPER_CUSTOM` //! to the custom wrapper used in `CC`. //! * `AR` - the `ar` (archiver) executable to use to build the static library. //! * `CRATE_CC_NO_DEFAULTS` - the default compiler flags may cause conflicts in //! some cross compiling scenarios. Setting this variable //! will disable the generation of default compiler //! flags. //! * `CC_ENABLE_DEBUG_OUTPUT` - if set, compiler command invocations and exit codes will //! be logged to stdout. This is useful for debugging build script issues, but can be //! overly verbose for normal use. //! * `CC_SHELL_ESCAPED_FLAGS` - if set, `*FLAGS` will be parsed as if they were shell //! arguments (similar to `make` and `cmake`) rather than splitting them on each space. //! For example, with `CFLAGS='a "b c"'`, the compiler will be invoked with 2 arguments - //! `a` and `b c` - rather than 3: `a`, `"b` and `c"`. //! * `CXX...` - see [C++ Support](#c-support). //! * `CC_FORCE_DISABLE` - If set, `cc` will never run any [`Command`]s, and methods that //! would return an [`Error`]. This is intended for use by third-party build systems //! which want to be absolutely sure that they are in control of building all //! dependencies. Note that operations that return [`Tool`]s such as //! [`Build::get_compiler`] may produce less accurate results as in some cases `cc` runs //! commands in order to locate compilers. Additionally, this does nothing to prevent //! users from running [`Tool::to_command`] and executing the [`Command`] themselves. //! * `RUSTC_WRAPPER` - If set, the specified command will be prefixed to the compiler //! command. This is useful for projects that want to use //! [sccache](https://github.com/mozilla/sccache), //! [buildcache](https://gitlab.com/bits-n-bites/buildcache), or //! [cachepot](https://github.com/paritytech/cachepot). //! //! Furthermore, projects using this crate may specify custom environment variables //! to be inspected, for example via the `Build::try_flags_from_environment` //! function. Consult the project’s own documentation or its use of the `cc` crate //! for any additional variables it may use. //! //! Each of these variables can also be supplied with certain prefixes and suffixes, //! in the following prioritized order: //! //! 1. `_` - for example, `CC_x86_64-unknown-linux-gnu` or `CC_thumbv8m.main-none-eabi` //! 2. `_` - for example, `CC_x86_64_unknown_linux_gnu` or `CC_thumbv8m_main_none_eabi` (both periods and underscores are replaced) //! 3. `_` - for example, `HOST_CC` or `TARGET_CFLAGS` //! 4. `` - a plain `CC`, `AR` as above. //! //! If none of these variables exist, cc-rs uses built-in defaults. //! //! In addition to the above optional environment variables, `cc-rs` has some //! functions with hard requirements on some variables supplied by [cargo's //! build-script driver][cargo] that it has the `TARGET`, `OUT_DIR`, `OPT_LEVEL`, //! and `HOST` variables. //! //! [cargo]: https://doc.rust-lang.org/cargo/reference/build-scripts.html#inputs-to-the-build-script //! //! # Optional features //! //! ## Parallel //! //! Currently cc-rs supports parallel compilation (think `make -jN`) but this //! feature is turned off by default. To enable cc-rs to compile C/C++ in parallel, //! you can change your dependency to: //! //! ```toml //! [build-dependencies] //! cc = { version = "1.0", features = ["parallel"] } //! ``` //! //! By default cc-rs will limit parallelism to `$NUM_JOBS`, or if not present it //! will limit it to the number of cpus on the machine. If you are using cargo, //! use `-jN` option of `build`, `test` and `run` commands as `$NUM_JOBS` //! is supplied by cargo. //! //! # Compile-time Requirements //! //! To work properly this crate needs access to a C compiler when the build script //! is being run. This crate does not ship a C compiler with it. The compiler //! required varies per platform, but there are three broad categories: //! //! * Unix platforms require `cc` to be the C compiler. This can be found by //! installing cc/clang on Linux distributions and Xcode on macOS, for example. //! * Windows platforms targeting MSVC (e.g. your target name ends in `-msvc`) //! require Visual Studio to be installed. `cc-rs` attempts to locate it, and //! if it fails, `cl.exe` is expected to be available in `PATH`. This can be //! set up by running the appropriate developer tools shell. //! * When using `prefer_clang_cl_over_msvc`, make sure that the `C++ Clang compiler for Windows` component //! is installed through the Visual Studio Installer, so that `cc-rs` can find `clang-cl.exe`. //! * Windows platforms targeting MinGW (e.g. your target name ends in `-gnu`) //! require `cc` to be available in `PATH`. We recommend the //! [MinGW-w64](https://www.mingw-w64.org/) distribution. //! You may also acquire it via //! [MSYS2](https://www.msys2.org/), as explained [here][msys2-help]. Make sure //! to install the appropriate architecture corresponding to your installation of //! rustc. GCC from older [MinGW](http://www.mingw.org/) project is compatible //! only with 32-bit rust compiler. //! //! [msys2-help]: https://github.com/rust-lang/rust/blob/master/INSTALL.md#building-on-windows //! //! # C++ support //! //! `cc-rs` supports C++ libraries compilation by using the `cpp` method on //! `Build`: //! //! ```rust,no_run //! cc::Build::new() //! .cpp(true) // Switch to C++ library compilation. //! .file("foo.cpp") //! .compile("foo"); //! ``` //! //! For C++ libraries, the `CXX` and `CXXFLAGS` environment variables are used instead of `CC` and `CFLAGS`. //! //! The C++ standard library may be linked to the crate target. By default it's `libc++` for macOS, FreeBSD, and OpenBSD, `libc++_shared` for Android, nothing for MSVC, and `libstdc++` for anything else. It can be changed in one of two ways: //! //! 1. by using the `cpp_link_stdlib` method on `Build`: //! ```rust,no_run //! cc::Build::new() //! .cpp(true) //! .file("foo.cpp") //! .cpp_link_stdlib("stdc++") // use libstdc++ //! .compile("foo"); //! ``` //! 2. by setting the `CXXSTDLIB` environment variable. //! //! In particular, for Android you may want to [use `c++_static` if you have at most one shared library](https://developer.android.com/ndk/guides/cpp-support). //! //! Remember that C++ does name mangling so `extern "C"` might be required to enable Rust linker to find your functions. //! //! # CUDA C++ support //! //! `cc-rs` also supports compiling CUDA C++ libraries by using the `cuda` method //! on `Build`: //! //! ```rust,no_run //! cc::Build::new() //! // Switch to CUDA C++ library compilation using NVCC. //! .cuda(true) //! .cudart("static") //! // Generate code for Maxwell (GTX 970, 980, 980 Ti, Titan X). //! .flag("-gencode").flag("arch=compute_52,code=sm_52") //! // Generate code for Maxwell (Jetson TX1). //! .flag("-gencode").flag("arch=compute_53,code=sm_53") //! // Generate code for Pascal (GTX 1070, 1080, 1080 Ti, Titan Xp). //! .flag("-gencode").flag("arch=compute_61,code=sm_61") //! // Generate code for Pascal (Tesla P100). //! .flag("-gencode").flag("arch=compute_60,code=sm_60") //! // Generate code for Pascal (Jetson TX2). //! .flag("-gencode").flag("arch=compute_62,code=sm_62") //! // Generate code in parallel //! .flag("-t0") //! .file("bar.cu") //! .compile("bar"); //! ``` //! //! # Speed up compilation with sccache //! //! `cc-rs` does not handle incremental compilation like `make` or `ninja`. It //! always compiles the all sources, no matter if they have changed or not. //! This would be time-consuming in large projects. To save compilation time, //! you can use [sccache](https://github.com/mozilla/sccache) by setting //! environment variable `RUSTC_WRAPPER=sccache`, which will use cached `.o` //! files if the sources are unchanged. #![doc(html_root_url = "https://docs.rs/cc/1.0")] #![deny(warnings)] #![deny(missing_docs)] #![deny(clippy::disallowed_methods)] #![warn(clippy::doc_markdown)] use std::borrow::Cow; use std::collections::HashMap; use std::env; use std::ffi::{OsStr, OsString}; use std::fmt::{self, Display}; use std::fs; use std::io::{self, Write}; use std::path::{Component, Path, PathBuf}; use std::process::{Command, Stdio}; use std::sync::{Arc, RwLock}; use shlex::Shlex; #[cfg(feature = "parallel")] mod parallel; mod target; use self::target::*; /// A helper module to looking for windows-specific tools: /// 1. On Windows host, probe the Windows Registry if needed; /// 2. On non-Windows host, check specified environment variables. pub mod windows_registry { // Regardless of whether this should be in this crate's public API, // it has been since 2015, so don't break it. /// Attempts to find a tool within an MSVC installation using the Windows /// registry as a point to search from. /// /// The `arch_or_target` argument is the architecture or the Rust target name /// that the tool should work for (e.g. compile or link for). The supported /// architecture names are: /// - `"x64"` or `"x86_64"` /// - `"arm64"` or `"aarch64"` /// - `"arm64ec"` /// - `"x86"`, `"i586"` or `"i686"` /// - `"arm"` or `"thumbv7a"` /// /// The `tool` argument is the tool to find. Supported tools include: /// - MSVC tools: `cl.exe`, `link.exe`, `lib.exe`, etc. /// - `MSBuild`: `msbuild.exe` /// - Visual Studio IDE: `devenv.exe` /// - Clang/LLVM tools: `clang.exe`, `clang++.exe`, `clang-*.exe`, `llvm-*.exe`, `lld.exe`, etc. /// /// This function will return `None` if the tool could not be found, or it will /// return `Some(cmd)` which represents a command that's ready to execute the /// tool with the appropriate environment variables set. /// /// Note that this function always returns `None` for non-MSVC targets (if a /// full target name was specified). pub fn find(arch_or_target: &str, tool: &str) -> Option { ::find_msvc_tools::find(arch_or_target, tool) } /// A version of Visual Studio #[derive(Debug, PartialEq, Eq, Copy, Clone)] #[non_exhaustive] pub enum VsVers { /// Visual Studio 12 (2013) #[deprecated = "Visual Studio 12 is no longer supported. cc will never return this value."] Vs12, /// Visual Studio 14 (2015) Vs14, /// Visual Studio 15 (2017) Vs15, /// Visual Studio 16 (2019) Vs16, /// Visual Studio 17 (2022) Vs17, /// Visual Studio 18 (2026) Vs18, } /// Find the most recent installed version of Visual Studio /// /// This is used by the cmake crate to figure out the correct /// generator. pub fn find_vs_version() -> Result { ::find_msvc_tools::find_vs_version().map(|vers| match vers { #[allow(deprecated)] ::find_msvc_tools::VsVers::Vs12 => VsVers::Vs12, ::find_msvc_tools::VsVers::Vs14 => VsVers::Vs14, ::find_msvc_tools::VsVers::Vs15 => VsVers::Vs15, ::find_msvc_tools::VsVers::Vs16 => VsVers::Vs16, ::find_msvc_tools::VsVers::Vs17 => VsVers::Vs17, ::find_msvc_tools::VsVers::Vs18 => VsVers::Vs18, _ => unreachable!("unknown VS version"), }) } /// Similar to the `find` function above, this function will attempt the same /// operation (finding a MSVC tool in a local install) but instead returns a /// [`Tool`](crate::Tool) which may be introspected. pub fn find_tool(arch_or_target: &str, tool: &str) -> Option { ::find_msvc_tools::find_tool(arch_or_target, tool).map(crate::Tool::from_find_msvc_tools) } } mod command_helpers; use command_helpers::*; mod tool; pub use tool::Tool; use tool::{CompilerFamilyLookupCache, ToolFamily}; mod tempfile; mod utilities; use utilities::*; mod flags; use flags::*; #[derive(Debug, Eq, PartialEq, Hash)] struct CompilerFlag { compiler: Box, flag: Box, } #[derive(Debug, Default)] struct BuildCache { apple_sdk_root_cache: RwLock, Arc>>, apple_versions_cache: RwLock, Arc>>, cached_compiler_family: RwLock, known_flag_support_status_cache: RwLock>, target_info_parser: target::TargetInfoParser, } /// A builder for compilation of a native library. /// /// A `Build` is the main type of the `cc` crate and is used to control all the /// various configuration options and such of a compile. You'll find more /// documentation on each method itself. #[derive(Clone, Debug)] pub struct Build { include_directories: Vec>, definitions: Vec<(Arc, Option>)>, objects: Vec>, flags: Vec>, flags_supported: Vec>, ar_flags: Vec>, asm_flags: Vec>, no_default_flags: bool, files: Vec>, cpp: bool, cpp_link_stdlib: Option>>, cpp_link_stdlib_static: bool, cpp_set_stdlib: Option>, cuda: bool, cudart: Option>, ccbin: bool, std: Option>, target: Option>, /// The host compiler. /// /// Try to not access this directly, and instead prefer `cfg!(...)`. host: Option>, out_dir: Option>, opt_level: Option>, debug: Option>, force_frame_pointer: Option, env: Vec<(Arc, Arc)>, compiler: Option>, archiver: Option>, ranlib: Option>, cargo_output: CargoOutput, link_lib_modifiers: Vec>, pic: Option, use_plt: Option, static_crt: Option, shared_flag: Option, static_flag: Option, warnings_into_errors: bool, warnings: Option, extra_warnings: Option, emit_rerun_if_env_changed: bool, shell_escaped_flags: Option, build_cache: Arc, inherit_rustflags: bool, prefer_clang_cl_over_msvc: bool, } /// Represents the types of errors that may occur while using cc-rs. #[derive(Clone, Debug)] enum ErrorKind { /// Error occurred while performing I/O. IOError, /// Environment variable not found, with the var in question as extra info. EnvVarNotFound, /// Error occurred while using external tools (ie: invocation of compiler). ToolExecError, /// Error occurred due to missing external tools. ToolNotFound, /// One of the function arguments failed validation. InvalidArgument, /// No known macro is defined for the compiler when discovering tool family. ToolFamilyMacroNotFound, /// Invalid target. InvalidTarget, /// Unknown target. UnknownTarget, /// Invalid rustc flag. InvalidFlag, #[cfg(feature = "parallel")] /// jobserver helpthread failure JobserverHelpThreadError, /// `cc` has been disabled by an environment variable. Disabled, } /// Represents an internal error that occurred, with an explanation. #[derive(Clone, Debug)] pub struct Error { /// Describes the kind of error that occurred. kind: ErrorKind, /// More explanation of error that occurred. message: Cow<'static, str>, } impl Error { fn new(kind: ErrorKind, message: impl Into>) -> Error { Error { kind, message: message.into(), } } } impl From for Error { fn from(e: io::Error) -> Error { Error::new(ErrorKind::IOError, format!("{e}")) } } impl Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:?}: {}", self.kind, self.message) } } impl std::error::Error for Error {} /// Represents an object. /// /// This is a source file -> object file pair. #[derive(Clone, Debug)] struct Object { src: PathBuf, dst: PathBuf, } impl Object { /// Create a new source file -> object file pair. fn new(src: PathBuf, dst: PathBuf) -> Object { Object { src, dst } } } /// Configure the builder. impl Build { /// Construct a new instance of a blank set of configuration. /// /// This builder is finished with the [`compile`] function. /// /// [`compile`]: struct.Build.html#method.compile pub fn new() -> Build { Build { include_directories: Vec::new(), definitions: Vec::new(), objects: Vec::new(), flags: Vec::new(), flags_supported: Vec::new(), ar_flags: Vec::new(), asm_flags: Vec::new(), no_default_flags: false, files: Vec::new(), shared_flag: None, static_flag: None, cpp: false, cpp_link_stdlib: None, cpp_link_stdlib_static: false, cpp_set_stdlib: None, cuda: false, cudart: None, ccbin: true, std: None, target: None, host: None, out_dir: None, opt_level: None, debug: None, force_frame_pointer: None, env: Vec::new(), compiler: None, archiver: None, ranlib: None, cargo_output: CargoOutput::new(), link_lib_modifiers: Vec::new(), pic: None, use_plt: None, static_crt: None, warnings: None, extra_warnings: None, warnings_into_errors: false, emit_rerun_if_env_changed: true, shell_escaped_flags: None, build_cache: Arc::default(), inherit_rustflags: true, prefer_clang_cl_over_msvc: false, } } /// Add a directory to the `-I` or include path for headers /// /// # Example /// /// ```no_run /// use std::path::Path; /// /// let library_path = Path::new("/path/to/library"); /// /// cc::Build::new() /// .file("src/foo.c") /// .include(library_path) /// .include("src") /// .compile("foo"); /// ``` pub fn include>(&mut self, dir: P) -> &mut Build { self.include_directories.push(dir.as_ref().into()); self } /// Add multiple directories to the `-I` include path. /// /// # Example /// /// ```no_run /// # use std::path::Path; /// # let condition = true; /// # /// let mut extra_dir = None; /// if condition { /// extra_dir = Some(Path::new("/path/to")); /// } /// /// cc::Build::new() /// .file("src/foo.c") /// .includes(extra_dir) /// .compile("foo"); /// ``` pub fn includes

(&mut self, dirs: P) -> &mut Build where P: IntoIterator, P::Item: AsRef, { for dir in dirs { self.include(dir); } self } /// Specify a `-D` variable with an optional value. /// /// # Example /// /// ```no_run /// cc::Build::new() /// .file("src/foo.c") /// .define("FOO", "BAR") /// .define("BAZ", None) /// .compile("foo"); /// ``` pub fn define<'a, V: Into>>(&mut self, var: &str, val: V) -> &mut Build { self.definitions .push((var.into(), val.into().map(Into::into))); self } /// Add an arbitrary object file to link in pub fn object>(&mut self, obj: P) -> &mut Build { self.objects.push(obj.as_ref().into()); self } /// Add arbitrary object files to link in pub fn objects

(&mut self, objs: P) -> &mut Build where P: IntoIterator, P::Item: AsRef, { for obj in objs { self.object(obj); } self } /// Add an arbitrary flag to the invocation of the compiler /// /// # Example /// /// ```no_run /// cc::Build::new() /// .file("src/foo.c") /// .flag("-ffunction-sections") /// .compile("foo"); /// ``` pub fn flag(&mut self, flag: impl AsRef) -> &mut Build { self.flags.push(flag.as_ref().into()); self } /// Add multiple flags to the invocation of the compiler. /// This is equivalent to calling [`flag`](Self::flag) for each item in the iterator. /// /// # Example /// ```no_run /// cc::Build::new() /// .file("src/foo.c") /// .flags(["-Wall", "-Wextra"]) /// .compile("foo"); /// ``` pub fn flags(&mut self, flags: Iter) -> &mut Build where Iter: IntoIterator, Iter::Item: AsRef, { for flag in flags { self.flag(flag); } self } /// Removes a compiler flag that was added by [`Build::flag`]. /// /// Will not remove flags added by other means (default flags, /// flags from env, and so on). /// /// # Example /// ``` /// cc::Build::new() /// .file("src/foo.c") /// .flag("unwanted_flag") /// .remove_flag("unwanted_flag"); /// ``` pub fn remove_flag(&mut self, flag: &str) -> &mut Build { self.flags.retain(|other_flag| &**other_flag != flag); self } /// Add a flag to the invocation of the ar /// /// # Example /// /// ```no_run /// cc::Build::new() /// .file("src/foo.c") /// .file("src/bar.c") /// .ar_flag("/NODEFAULTLIB:libc.dll") /// .compile("foo"); /// ``` pub fn ar_flag(&mut self, flag: impl AsRef) -> &mut Build { self.ar_flags.push(flag.as_ref().into()); self } /// Add a flag that will only be used with assembly files. /// /// The flag will be applied to input files with either a `.s` or /// `.asm` extension (case insensitive). /// /// # Example /// /// ```no_run /// cc::Build::new() /// .asm_flag("-Wa,-defsym,abc=1") /// .file("src/foo.S") // The asm flag will be applied here /// .file("src/bar.c") // The asm flag will not be applied here /// .compile("foo"); /// ``` pub fn asm_flag(&mut self, flag: impl AsRef) -> &mut Build { self.asm_flags.push(flag.as_ref().into()); self } /// Add an arbitrary flag to the invocation of the compiler if it supports it /// /// # Example /// /// ```no_run /// cc::Build::new() /// .file("src/foo.c") /// .flag_if_supported("-Wlogical-op") // only supported by GCC /// .flag_if_supported("-Wunreachable-code") // only supported by clang /// .compile("foo"); /// ``` pub fn flag_if_supported(&mut self, flag: impl AsRef) -> &mut Build { self.flags_supported.push(flag.as_ref().into()); self } /// Add flags from the specified environment variable. /// /// Normally the `cc` crate will consult with the standard set of environment /// variables (such as `CFLAGS` and `CXXFLAGS`) to construct the compiler invocation. Use of /// this method provides additional levers for the end user to use when configuring the build /// process. /// /// Just like the standard variables, this method will search for an environment variable with /// appropriate target prefixes, when appropriate. /// /// # Examples /// /// This method is particularly beneficial in introducing the ability to specify crate-specific /// flags. /// /// ```no_run /// cc::Build::new() /// .file("src/foo.c") /// .try_flags_from_environment(concat!(env!("CARGO_PKG_NAME"), "_CFLAGS")) /// .expect("the environment variable must be specified and UTF-8") /// .compile("foo"); /// ``` /// pub fn try_flags_from_environment(&mut self, environ_key: &str) -> Result<&mut Build, Error> { let flags = self.envflags(environ_key)?.ok_or_else(|| { Error::new( ErrorKind::EnvVarNotFound, format!("could not find environment variable {environ_key}"), ) })?; self.flags.extend( flags .into_iter() .map(|flag| Arc::from(OsString::from(flag).as_os_str())), ); Ok(self) } /// Set the `-shared` flag. /// /// This will typically be ignored by the compiler when calling [`Self::compile()`] since it only /// produces static libraries. /// /// # Example /// /// ```no_run /// // This will create a library named "liblibfoo.so.a" /// cc::Build::new() /// .file("src/foo.c") /// .shared_flag(true) /// .compile("libfoo.so"); /// ``` #[deprecated = "cc only creates static libraries, setting this does nothing"] pub fn shared_flag(&mut self, shared_flag: bool) -> &mut Build { self.shared_flag = Some(shared_flag); self } /// Set the `-static` flag. /// /// This will typically be ignored by the compiler when calling [`Self::compile()`] since it only /// produces static libraries. /// /// # Example /// /// ```no_run /// cc::Build::new() /// .file("src/foo.c") /// .shared_flag(true) /// .static_flag(true) /// .compile("foo"); /// ``` #[deprecated = "cc only creates static libraries, setting this does nothing"] pub fn static_flag(&mut self, static_flag: bool) -> &mut Build { self.static_flag = Some(static_flag); self } /// Disables the generation of default compiler flags. The default compiler /// flags may cause conflicts in some cross compiling scenarios. /// /// Setting the `CRATE_CC_NO_DEFAULTS` environment variable has the same /// effect as setting this to `true`. The presence of the environment /// variable and the value of `no_default_flags` will be OR'd together. pub fn no_default_flags(&mut self, no_default_flags: bool) -> &mut Build { self.no_default_flags = no_default_flags; self } /// Add a file which will be compiled pub fn file>(&mut self, p: P) -> &mut Build { self.files.push(p.as_ref().into()); self } /// Add files which will be compiled pub fn files

(&mut self, p: P) -> &mut Build where P: IntoIterator, P::Item: AsRef, { for file in p.into_iter() { self.file(file); } self } /// Get the files which will be compiled pub fn get_files(&self) -> impl Iterator { self.files.iter().map(AsRef::as_ref) } /// Set C++ support. /// /// The other `cpp_*` options will only become active if this is set to /// `true`. /// /// The name of the C++ standard library to link is decided by: /// 1. If [`cpp_link_stdlib`](Build::cpp_link_stdlib) is set, use its value. /// 2. Else if the `CXXSTDLIB` environment variable is set, use its value. /// 3. Else the default is `c++` for OS X and BSDs, `c++_shared` for Android, /// `None` for MSVC and `stdc++` for anything else. pub fn cpp(&mut self, cpp: bool) -> &mut Build { self.cpp = cpp; self } /// Set CUDA C++ support. /// /// Enabling CUDA will invoke the CUDA compiler, NVCC. While NVCC accepts /// the most common compiler flags, e.g. `-std=c++17`, some project-specific /// flags might have to be prefixed with "-Xcompiler" flag, for example as /// `.flag("-Xcompiler").flag("-fpermissive")`. See the documentation for /// `nvcc`, the CUDA compiler driver, at /// for more information. /// /// If enabled, this also implicitly enables C++ support. pub fn cuda(&mut self, cuda: bool) -> &mut Build { self.cuda = cuda; if cuda { self.cpp = true; self.cudart = Some("static".into()); } self } /// Link CUDA run-time. /// /// This option mimics the `--cudart` NVCC command-line option. Just like /// the original it accepts `{none|shared|static}`, with default being /// `static`. The method has to be invoked after `.cuda(true)`, or not /// at all, if the default is right for the project. pub fn cudart(&mut self, cudart: &str) -> &mut Build { if self.cuda { self.cudart = Some(cudart.into()); } self } /// Set CUDA host compiler. /// /// By default, a `-ccbin` flag will be passed to NVCC to specify the /// underlying host compiler. The value of `-ccbin` is the same as the /// chosen C++ compiler. This is not always desired, because NVCC might /// not support that compiler. In this case, you can remove the `-ccbin` /// flag so that NVCC will choose the host compiler by itself. pub fn ccbin(&mut self, ccbin: bool) -> &mut Build { self.ccbin = ccbin; self } /// Specify the C or C++ language standard version. /// /// These values are common to modern versions of GCC, Clang and MSVC: /// - `c11` for ISO/IEC 9899:2011 /// - `c17` for ISO/IEC 9899:2018 /// - `c++14` for ISO/IEC 14882:2014 /// - `c++17` for ISO/IEC 14882:2017 /// - `c++20` for ISO/IEC 14882:2020 /// /// Other values have less broad support, e.g. MSVC does not support `c++11` /// (`c++14` is the minimum), `c89` (omit the flag instead) or `c99`. /// /// For compiling C++ code, you should also set `.cpp(true)`. /// /// The default is that no standard flag is passed to the compiler, so the /// language version will be the compiler's default. /// /// # Example /// /// ```no_run /// cc::Build::new() /// .file("src/modern.cpp") /// .cpp(true) /// .std("c++17") /// .compile("modern"); /// ``` pub fn std(&mut self, std: &str) -> &mut Build { self.std = Some(std.into()); self } /// Set warnings into errors flag. /// /// Disabled by default. /// /// Warning: turning warnings into errors only make sense /// if you are a developer of the crate using cc-rs. /// Some warnings only appear on some architecture or /// specific version of the compiler. Any user of this crate, /// or any other crate depending on it, could fail during /// compile time. /// /// # Example /// /// ```no_run /// cc::Build::new() /// .file("src/foo.c") /// .warnings_into_errors(true) /// .compile("libfoo.a"); /// ``` pub fn warnings_into_errors(&mut self, warnings_into_errors: bool) -> &mut Build { self.warnings_into_errors = warnings_into_errors; self } /// Set warnings flags. /// /// Adds some flags: /// - "-Wall" for MSVC. /// - "-Wall", "-Wextra" for GNU and Clang. /// /// Enabled by default. /// /// # Example /// /// ```no_run /// cc::Build::new() /// .file("src/foo.c") /// .warnings(false) /// .compile("libfoo.a"); /// ``` pub fn warnings(&mut self, warnings: bool) -> &mut Build { self.warnings = Some(warnings); self.extra_warnings = Some(warnings); self } /// Set extra warnings flags. /// /// Adds some flags: /// - nothing for MSVC. /// - "-Wextra" for GNU and Clang. /// /// Enabled by default. /// /// # Example /// /// ```no_run /// // Disables -Wextra, -Wall remains enabled: /// cc::Build::new() /// .file("src/foo.c") /// .extra_warnings(false) /// .compile("libfoo.a"); /// ``` pub fn extra_warnings(&mut self, warnings: bool) -> &mut Build { self.extra_warnings = Some(warnings); self } /// Set the standard library to link against when compiling with C++ /// support. /// /// If the `CXXSTDLIB` environment variable is set, its value will /// override the default value, but not the value explicitly set by calling /// this function. /// /// A value of `None` indicates that no automatic linking should happen, /// otherwise cargo will link against the specified library. /// /// The given library name must not contain the `lib` prefix. /// /// Common values: /// - `stdc++` for GNU /// - `c++` for Clang /// - `c++_shared` or `c++_static` for Android /// /// # Example /// /// ```no_run /// cc::Build::new() /// .file("src/foo.c") /// .shared_flag(true) /// .cpp_link_stdlib("stdc++") /// .compile("libfoo.so"); /// ``` pub fn cpp_link_stdlib<'a, V: Into>>( &mut self, cpp_link_stdlib: V, ) -> &mut Build { self.cpp_link_stdlib = Some(cpp_link_stdlib.into().map(Arc::from)); self } /// Force linker to statically link C++ stdlib. By default cc-rs will emit /// rustc-link flag to link against system C++ stdlib (e.g. libstdc++.so, libc++.so) /// Provide value of `true` if linking against system library is not desired /// /// Note that for `wasm32` target C++ stdlib will always be linked statically /// /// # Example /// /// ```no_run /// cc::Build::new() /// .file("src/foo.cpp") /// .cpp(true) /// .cpp_link_stdlib("stdc++") /// .cpp_link_stdlib_static(true) /// .compile("foo"); /// ``` pub fn cpp_link_stdlib_static(&mut self, is_static: bool) -> &mut Build { self.cpp_link_stdlib_static = is_static; self } /// Force the C++ compiler to use the specified standard library. /// /// Setting this option will automatically set `cpp_link_stdlib` to the same /// value. /// /// The default value of this option is always `None`. /// /// This option has no effect when compiling for a Visual Studio based /// target. /// /// This option sets the `-stdlib` flag, which is only supported by some /// compilers (clang, icc) but not by others (gcc). The library will not /// detect which compiler is used, as such it is the responsibility of the /// caller to ensure that this option is only used in conjunction with a /// compiler which supports the `-stdlib` flag. /// /// A value of `None` indicates that no specific C++ standard library should /// be used, otherwise `-stdlib` is added to the compile invocation. /// /// The given library name must not contain the `lib` prefix. /// /// Common values: /// - `stdc++` for GNU /// - `c++` for Clang /// /// # Example /// /// ```no_run /// cc::Build::new() /// .file("src/foo.c") /// .cpp_set_stdlib("c++") /// .compile("libfoo.a"); /// ``` pub fn cpp_set_stdlib<'a, V: Into>>( &mut self, cpp_set_stdlib: V, ) -> &mut Build { let cpp_set_stdlib = cpp_set_stdlib.into().map(Arc::from); self.cpp_set_stdlib.clone_from(&cpp_set_stdlib); self.cpp_link_stdlib = Some(cpp_set_stdlib); self } /// Configures the `rustc` target this configuration will be compiling /// for. /// /// This will fail if using a target not in a pre-compiled list taken from /// `rustc +nightly --print target-list`. The list will be updated /// periodically. /// /// You should avoid setting this in build scripts, target information /// will instead be retrieved from the environment variables `TARGET` and /// `CARGO_CFG_TARGET_*` that Cargo sets. /// /// # Example /// /// ```no_run /// cc::Build::new() /// .file("src/foo.c") /// .target("aarch64-linux-android") /// .compile("foo"); /// ``` pub fn target(&mut self, target: &str) -> &mut Build { self.target = Some(target.into()); self } /// Configures the host assumed by this configuration. /// /// This option is automatically scraped from the `HOST` environment /// variable by build scripts, so it's not required to call this function. /// /// # Example /// /// ```no_run /// cc::Build::new() /// .file("src/foo.c") /// .host("arm-linux-gnueabihf") /// .compile("foo"); /// ``` pub fn host(&mut self, host: &str) -> &mut Build { self.host = Some(host.into()); self } /// Configures the optimization level of the generated object files. /// /// This option is automatically scraped from the `OPT_LEVEL` environment /// variable by build scripts, so it's not required to call this function. pub fn opt_level(&mut self, opt_level: u32) -> &mut Build { self.opt_level = Some(opt_level.to_string().into()); self } /// Configures the optimization level of the generated object files. /// /// This option is automatically scraped from the `OPT_LEVEL` environment /// variable by build scripts, so it's not required to call this function. pub fn opt_level_str(&mut self, opt_level: &str) -> &mut Build { self.opt_level = Some(opt_level.into()); self } /// Configures whether the compiler will emit debug information when /// generating object files. /// /// This option is automatically scraped from the `DEBUG` environment /// variable by build scripts, so it's not required to call this function. pub fn debug(&mut self, debug: bool) -> &mut Build { self.debug = Some(debug.to_string().into()); self } /// Configures whether the compiler will emit debug information when /// generating object files. /// /// This should be one of the values accepted by Cargo's [`debug`][1] /// profile setting, which cc-rs will try to map to the appropriate C /// compiler flag. /// /// This option is automatically scraped from the `DEBUG` environment /// variable by build scripts, so it's not required to call this function. /// /// [1]: https://doc.rust-lang.org/cargo/reference/profiles.html#debug pub fn debug_str(&mut self, debug: &str) -> &mut Build { self.debug = Some(debug.into()); self } /// Configures whether the compiler will emit instructions to store /// frame pointers during codegen. /// /// This option is automatically enabled when debug information is emitted. /// Otherwise the target platform compiler's default will be used. /// You can use this option to force a specific setting. pub fn force_frame_pointer(&mut self, force: bool) -> &mut Build { self.force_frame_pointer = Some(force); self } /// Configures the output directory where all object files and static /// libraries will be located. /// /// This option is automatically scraped from the `OUT_DIR` environment /// variable by build scripts, so it's not required to call this function. pub fn out_dir>(&mut self, out_dir: P) -> &mut Build { self.out_dir = Some(out_dir.as_ref().into()); self } /// Configures the compiler to be used to produce output. /// /// This option is automatically determined from the target platform or a /// number of environment variables, so it's not required to call this /// function. pub fn compiler>(&mut self, compiler: P) -> &mut Build { self.compiler = Some(compiler.as_ref().into()); self } /// Configures the tool used to assemble archives. /// /// This option is automatically determined from the target platform or a /// number of environment variables, so it's not required to call this /// function. pub fn archiver>(&mut self, archiver: P) -> &mut Build { self.archiver = Some(archiver.as_ref().into()); self } /// Configures the tool used to index archives. /// /// This option is automatically determined from the target platform or a /// number of environment variables, so it's not required to call this /// function. pub fn ranlib>(&mut self, ranlib: P) -> &mut Build { self.ranlib = Some(ranlib.as_ref().into()); self } /// Define whether metadata should be emitted for cargo allowing it to /// automatically link the binary. Defaults to `true`. /// /// The emitted metadata is: /// /// - `rustc-link-lib=static=`*compiled lib* /// - `rustc-link-search=native=`*target folder* /// - When target is MSVC, the ATL-MFC libs are added via `rustc-link-search=native=` /// - When C++ is enabled, the C++ stdlib is added via `rustc-link-lib` /// - If `emit_rerun_if_env_changed` is not `false`, `rerun-if-env-changed=`*env* /// pub fn cargo_metadata(&mut self, cargo_metadata: bool) -> &mut Build { self.cargo_output.metadata = cargo_metadata; self } /// Define whether compile warnings should be emitted for cargo. Defaults to /// `true`. /// /// If disabled, compiler messages will not be printed. /// Issues unrelated to the compilation will always produce cargo warnings regardless of this setting. pub fn cargo_warnings(&mut self, cargo_warnings: bool) -> &mut Build { self.cargo_output.warnings = cargo_warnings; self } /// Define whether debug information should be emitted for cargo. Defaults to whether /// or not the environment variable `CC_ENABLE_DEBUG_OUTPUT` is set. /// /// If enabled, the compiler will emit debug information when generating object files, /// such as the command invoked and the exit status. pub fn cargo_debug(&mut self, cargo_debug: bool) -> &mut Build { self.cargo_output.debug = cargo_debug; self } /// Define whether compiler output (to stdout) should be emitted. Defaults to `true` /// (forward compiler stdout to this process' stdout) /// /// Some compilers emit errors to stdout, so if you *really* need stdout to be clean /// you should also set this to `false`. pub fn cargo_output(&mut self, cargo_output: bool) -> &mut Build { self.cargo_output.output = if cargo_output { OutputKind::Forward } else { OutputKind::Discard }; self } /// Adds a native library modifier that will be added to the /// `rustc-link-lib=static:MODIFIERS=LIBRARY_NAME` metadata line /// emitted for cargo if `cargo_metadata` is enabled. /// See /// for the list of modifiers accepted by rustc. pub fn link_lib_modifier(&mut self, link_lib_modifier: impl AsRef) -> &mut Build { self.link_lib_modifiers .push(link_lib_modifier.as_ref().into()); self } /// Configures whether the compiler will emit position independent code. /// /// This option defaults to `false` for `windows-gnu` and bare metal targets and /// to `true` for all other targets. pub fn pic(&mut self, pic: bool) -> &mut Build { self.pic = Some(pic); self } /// Configures whether the Procedure Linkage Table is used for indirect /// calls into shared libraries. /// /// The PLT is used to provide features like lazy binding, but introduces /// a small performance loss due to extra pointer indirection. Setting /// `use_plt` to `false` can provide a small performance increase. /// /// Note that skipping the PLT requires a recent version of GCC/Clang. /// /// This only applies to ELF targets. It has no effect on other platforms. pub fn use_plt(&mut self, use_plt: bool) -> &mut Build { self.use_plt = Some(use_plt); self } /// Define whether metadata should be emitted for cargo to only trigger /// rebuild when detected environment changes, by default build script is /// always run on every compilation if no rerun cargo metadata is emitted. /// /// NOTE that cc does not emit metadata to detect changes for `PATH`, since it could /// be changed every compilation yet does not affect the result of compilation /// (i.e. rust-analyzer adds temporary directory to `PATH`). /// /// cc in general, has no way detecting changes to compiler, as there are so many ways to /// change it and sidestep the detection, for example the compiler might be wrapped in a script /// so detecting change of the file, or using checksum won't work. /// /// We recommend users to decide for themselves, if they want rebuild if the compiler has been upgraded /// or changed, and how to detect that. /// /// This has no effect if the `cargo_metadata` option is `false`. /// /// This option defaults to `true`. pub fn emit_rerun_if_env_changed(&mut self, emit_rerun_if_env_changed: bool) -> &mut Build { self.emit_rerun_if_env_changed = emit_rerun_if_env_changed; self } /// Configures whether the /MT flag or the /MD flag will be passed to msvc build tools. /// /// This option defaults to `false`, and affect only msvc targets. pub fn static_crt(&mut self, static_crt: bool) -> &mut Build { self.static_crt = Some(static_crt); self } /// Configure whether *FLAGS variables are parsed using `shlex`, similarly to `make` and /// `cmake`. /// /// This option defaults to `false`. pub fn shell_escaped_flags(&mut self, shell_escaped_flags: bool) -> &mut Build { self.shell_escaped_flags = Some(shell_escaped_flags); self } /// Configure whether cc should automatically inherit compatible flags passed to rustc /// from `CARGO_ENCODED_RUSTFLAGS`. /// /// This option defaults to `true`. pub fn inherit_rustflags(&mut self, inherit_rustflags: bool) -> &mut Build { self.inherit_rustflags = inherit_rustflags; self } /// Prefer to use clang-cl over msvc. /// /// This option defaults to `false`. pub fn prefer_clang_cl_over_msvc(&mut self, prefer_clang_cl_over_msvc: bool) -> &mut Build { self.prefer_clang_cl_over_msvc = prefer_clang_cl_over_msvc; self } /// Set an environment variable for compiler invocations and other child processes. /// /// `cc` reads a lot of different variables from the current process' environment. It currently /// allows the following standard environment variables to be overwritten by this function: /// - `SDKROOT` /// - `*_DEPLOYMENT_TARGET` /// - `WASI_SDK_ROOT` /// /// The logic here is "environment variables that the C compiler could itself reasonably have /// read". pub fn env(&mut self, key: K, val: V) -> &mut Build where K: AsRef, V: AsRef, { self.env.push((key.as_ref().into(), val.as_ref().into())); self } // retained for backwards compatibility only #[doc(hidden)] #[deprecated = "use `env` instead"] pub fn __set_env(&mut self, key: K, val: V) -> &mut Build where K: AsRef, V: AsRef, { self.env(key, val) } } /// Invoke or fetch the compiler or archiver. impl Build { /// Run the compiler to test if it accepts the given flag. /// /// For a convenience method for setting flags conditionally, /// see `flag_if_supported()`. /// /// It may return error if it's unable to run the compiler with a test file /// (e.g. the compiler is missing or a write to the `out_dir` failed). /// /// Note: Once computed, the result of this call is stored in the /// `known_flag_support` field. If `is_flag_supported(flag)` /// is called again, the result will be read from the hash table. pub fn is_flag_supported(&self, flag: impl AsRef) -> Result { self.is_flag_supported_inner( flag.as_ref(), &self.get_base_compiler()?, &self.get_target()?, ) } fn ensure_check_file(&self) -> Result { let out_dir = self.get_out_dir()?; let src = if self.cuda { assert!(self.cpp); out_dir.join("flag_check.cu") } else if self.cpp { out_dir.join("flag_check.cpp") } else { out_dir.join("flag_check.c") }; if !src.exists() { let mut f = fs::File::create(&src)?; write!(f, "int main(void) {{ return 0; }}")?; } Ok(src) } fn is_flag_supported_inner( &self, flag: &OsStr, tool: &Tool, target: &TargetInfo<'_>, ) -> Result { let compiler_flag = CompilerFlag { compiler: tool.path().into(), flag: flag.into(), }; if let Some(is_supported) = self .build_cache .known_flag_support_status_cache .read() .unwrap() .get(&compiler_flag) .cloned() { return Ok(is_supported); } let out_dir = self.get_out_dir()?; let src = self.ensure_check_file()?; let obj = out_dir.join("flag_check"); let mut compiler = { let mut cfg = Build::new(); cfg.flag(flag) .compiler(tool.path()) .cargo_metadata(self.cargo_output.metadata) .opt_level(0) .debug(false) .cpp(self.cpp) .cuda(self.cuda) .inherit_rustflags(false) .emit_rerun_if_env_changed(self.emit_rerun_if_env_changed); if let Some(target) = &self.target { cfg.target(target); } if let Some(host) = &self.host { cfg.host(host); } cfg.try_get_compiler()? }; // Clang uses stderr for verbose output, which yields a false positive // result if the CFLAGS/CXXFLAGS include -v to aid in debugging. if compiler.family.verbose_stderr() { compiler.remove_arg("-v".into()); } if compiler.is_like_clang() { // Avoid reporting that the arg is unsupported just because the // compiler complains that it wasn't used. compiler.push_cc_arg("-Wno-unused-command-line-argument".into()); } let mut cmd = compiler.to_command(); command_add_output_file( &mut cmd, &obj, CmdAddOutputFileArgs { cuda: self.cuda, is_assembler_msvc: false, msvc: compiler.is_like_msvc(), clang: compiler.is_like_clang(), gnu: compiler.is_like_gnu(), is_asm: false, is_arm: is_arm(target), }, ); // Checking for compiler flags does not require linking (and we _must_ // avoid making it do so, since it breaks cross-compilation when the C // compiler isn't configured to be able to link). // https://github.com/rust-lang/cc-rs/issues/1423 cmd.arg("-c"); if compiler.supports_path_delimiter() { cmd.arg("--"); } cmd.arg(&src); if compiler.is_like_msvc() { // On MSVC we need to make sure the LIB directory is included // so the CRT can be found. for (key, value) in &tool.env { if key == "LIB" { cmd.env("LIB", value); break; } } } let output = cmd.current_dir(out_dir).output()?; let is_supported = output.status.success() && output.stderr.is_empty(); self.build_cache .known_flag_support_status_cache .write() .unwrap() .insert(compiler_flag, is_supported); Ok(is_supported) } /// Run the compiler, generating the file `output` /// /// This will return a result instead of panicking; see [`Self::compile()`] for /// the complete description. pub fn try_compile(&self, output: &str) -> Result<(), Error> { let mut output_components = Path::new(output).components(); match (output_components.next(), output_components.next()) { (Some(Component::Normal(_)), None) => {} _ => { return Err(Error::new( ErrorKind::InvalidArgument, "argument of `compile` must be a single normal path component", )); } } let (lib_name, gnu_lib_name) = if output.starts_with("lib") && output.ends_with(".a") { (&output[3..output.len() - 2], output.to_owned()) } else { let mut gnu = String::with_capacity(5 + output.len()); gnu.push_str("lib"); gnu.push_str(output); gnu.push_str(".a"); (output, gnu) }; let dst = self.get_out_dir()?; let objects = objects_from_files(&self.files, &dst)?; self.compile_objects(&objects)?; self.assemble(lib_name, &dst.join(gnu_lib_name), &objects)?; let target = self.get_target()?; if target.env == "msvc" { let compiler = self.get_base_compiler()?; let atlmfc_lib = compiler .env() .iter() .find(|&(var, _)| var.as_os_str() == OsStr::new("LIB")) .and_then(|(_, lib_paths)| { env::split_paths(lib_paths).find(|path| { let sub = Path::new("atlmfc/lib"); path.ends_with(sub) || path.parent().map_or(false, |p| p.ends_with(sub)) }) }); if let Some(atlmfc_lib) = atlmfc_lib { self.cargo_output.print_metadata(&format_args!( "cargo:rustc-link-search=native={}", atlmfc_lib.display() )); } } if self.link_lib_modifiers.is_empty() { self.cargo_output .print_metadata(&format_args!("cargo:rustc-link-lib=static={lib_name}")); } else { self.cargo_output.print_metadata(&format_args!( "cargo:rustc-link-lib=static:{}={}", JoinOsStrs { slice: &self.link_lib_modifiers, delimiter: ',' }, lib_name )); } self.cargo_output.print_metadata(&format_args!( "cargo:rustc-link-search=native={}", dst.display() )); // Add specific C++ libraries, if enabled. if self.cpp { if let Some(stdlib) = self.get_cpp_link_stdlib()? { if self.cpp_link_stdlib_static { self.cargo_output.print_metadata(&format_args!( "cargo:rustc-link-lib=static={}", stdlib.display() )); } else { self.cargo_output .print_metadata(&format_args!("cargo:rustc-link-lib={}", stdlib.display())); } } // Link c++ lib from WASI sysroot if target.arch == "wasm32" { if target.os == "wasi" { if let Ok(wasi_sysroot) = self.wasi_sysroot() { self.cargo_output.print_metadata(&format_args!( "cargo:rustc-flags=-L {}/lib/{} -lstatic=c++ -lstatic=c++abi", Path::new(&wasi_sysroot).display(), self.get_raw_target()? )); } } else if target.os == "linux" { let musl_sysroot = self.wasm_musl_sysroot().unwrap(); self.cargo_output.print_metadata(&format_args!( "cargo:rustc-flags=-L {}/lib -lstatic=c++ -lstatic=c++abi", Path::new(&musl_sysroot).display(), )); } } } let cudart = match &self.cudart { Some(opt) => opt, // {none|shared|static} None => "none", }; if cudart != "none" { if let Some(nvcc) = self.which(&self.get_compiler().path, None) { // Try to figure out the -L search path. If it fails, // it's on user to specify one by passing it through // RUSTFLAGS environment variable. let mut libtst = false; let mut libdir = nvcc; libdir.pop(); // remove 'nvcc' libdir.push(".."); if cfg!(target_os = "linux") { libdir.push("targets"); libdir.push(format!("{}-linux", target.arch)); if !libdir.exists() && target.arch == "aarch64" { libdir.pop(); libdir.push("sbsa-linux"); } libdir.push("lib"); libtst = true; } else if cfg!(target_env = "msvc") { libdir.push("lib"); match target.arch { "x86_64" => { libdir.push("x64"); libtst = true; } "x86" => { libdir.push("Win32"); libtst = true; } _ => libtst = false, } } if libtst && libdir.is_dir() { self.cargo_output.print_metadata(&format_args!( "cargo:rustc-link-search=native={}", libdir.to_str().unwrap() )); } // And now the -l flag. let lib = match cudart { "shared" => "cudart", "static" => "cudart_static", bad => panic!("unsupported cudart option: {}", bad), }; self.cargo_output .print_metadata(&format_args!("cargo:rustc-link-lib={lib}")); } } Ok(()) } /// Run the compiler, generating the file `output` /// /// # Library name /// /// The `output` string argument determines the file name for the compiled /// library. The Rust compiler will create an assembly named "lib"+output+".a". /// MSVC will create a file named output+".lib". /// /// The choice of `output` is close to arbitrary, but: /// /// - must be nonempty, /// - must not contain a path separator (`/`), /// - must be unique across all `compile` invocations made by the same build /// script. /// /// If your build script compiles a single source file, the base name of /// that source file would usually be reasonable: /// /// ```no_run /// cc::Build::new().file("blobstore.c").compile("blobstore"); /// ``` /// /// Compiling multiple source files, some people use their crate's name, or /// their crate's name + "-cc". /// /// Otherwise, please use your imagination. /// /// For backwards compatibility, if `output` starts with "lib" *and* ends /// with ".a", a second "lib" prefix and ".a" suffix do not get added on, /// but this usage is deprecated; please omit `lib` and `.a` in the argument /// that you pass. /// /// # Panics /// /// Panics if `output` is not formatted correctly or if one of the underlying /// compiler commands fails. It can also panic if it fails reading file names /// or creating directories. pub fn compile(&self, output: &str) { if let Err(e) = self.try_compile(output) { fail(&e.message); } } /// Run the compiler, generating intermediate files, but without linking /// them into an archive file. /// /// This will return a list of compiled object files, in the same order /// as they were passed in as `file`/`files` methods. pub fn compile_intermediates(&self) -> Vec { match self.try_compile_intermediates() { Ok(v) => v, Err(e) => fail(&e.message), } } /// Run the compiler, generating intermediate files, but without linking /// them into an archive file. /// /// This will return a result instead of panicking; see `compile_intermediates()` for the complete description. pub fn try_compile_intermediates(&self) -> Result, Error> { let dst = self.get_out_dir()?; let objects = objects_from_files(&self.files, &dst)?; self.compile_objects(&objects)?; Ok(objects.into_iter().map(|v| v.dst).collect()) } fn compile_objects(&self, objs: &[Object]) -> Result<(), Error> { if self.is_disabled() { return Err(Error::new( ErrorKind::Disabled, "the `cc` crate's functionality has been disabled by the `CC_FORCE_DISABLE` environment variable.", )); } #[cfg(feature = "parallel")] if objs.len() > 1 { return parallel::run_commands_in_parallel( &self.cargo_output, &mut objs.iter().map(|obj| self.create_compile_object_cmd(obj)), ); } for obj in objs { let mut cmd = self.create_compile_object_cmd(obj)?; run(&mut cmd, &self.cargo_output)?; } Ok(()) } fn create_compile_object_cmd(&self, obj: &Object) -> Result { let asm_ext = AsmFileExt::from_path(&obj.src); let is_asm = asm_ext.is_some(); let target = self.get_target()?; let msvc = target.env == "msvc"; let compiler = self.try_get_compiler()?; let is_assembler_msvc = msvc && asm_ext == Some(AsmFileExt::DotAsm); let mut cmd = if is_assembler_msvc { self.msvc_macro_assembler()? } else { compiler.to_command() }; let is_arm = is_arm(&target); command_add_output_file( &mut cmd, &obj.dst, CmdAddOutputFileArgs { cuda: self.cuda, is_assembler_msvc, msvc: compiler.is_like_msvc(), clang: compiler.is_like_clang(), gnu: compiler.is_like_gnu(), is_asm, is_arm, }, ); // armasm and armasm64 don't require -c option if !is_assembler_msvc || !is_arm { cmd.arg("-c"); } if self.cuda && self.cuda_file_count() > 1 { cmd.arg("--device-c"); } if is_asm { cmd.args(self.asm_flags.iter().map(std::ops::Deref::deref)); } if compiler.supports_path_delimiter() && !is_assembler_msvc { // #513: For `clang-cl`, separate flags/options from the input file. // When cross-compiling macOS -> Windows, this avoids interpreting // common `/Users/...` paths as the `/U` flag and triggering // `-Wslash-u-filename` warning. cmd.arg("--"); } cmd.arg(&obj.src); if cfg!(target_os = "macos") { self.fix_env_for_apple_os(&mut cmd)?; } Ok(cmd) } /// This will return a result instead of panicking; see [`Self::expand()`] for /// the complete description. pub fn try_expand(&self) -> Result, Error> { let compiler = self.try_get_compiler()?; let mut cmd = compiler.to_command(); cmd.arg("-E"); assert!( self.files.len() <= 1, "Expand may only be called for a single file" ); let is_asm = self .files .iter() .map(std::ops::Deref::deref) .find_map(AsmFileExt::from_path) .is_some(); if compiler.family == (ToolFamily::Msvc { clang_cl: true }) && !is_asm { // #513: For `clang-cl`, separate flags/options from the input file. // When cross-compiling macOS -> Windows, this avoids interpreting // common `/Users/...` paths as the `/U` flag and triggering // `-Wslash-u-filename` warning. cmd.arg("--"); } cmd.args(self.files.iter().map(std::ops::Deref::deref)); run_output(&mut cmd, &self.cargo_output) } /// Run the compiler, returning the macro-expanded version of the input files. /// /// This is only relevant for C and C++ files. /// /// # Panics /// Panics if more than one file is present in the config, or if compiler /// path has an invalid file name. /// /// # Example /// ```no_run /// let out = cc::Build::new().file("src/foo.c").expand(); /// ``` pub fn expand(&self) -> Vec { match self.try_expand() { Err(e) => fail(&e.message), Ok(v) => v, } } /// Get the compiler that's in use for this configuration. /// /// This function will return a `Tool` which represents the culmination /// of this configuration at a snapshot in time. The returned compiler can /// be inspected (e.g. the path, arguments, environment) to forward along to /// other tools, or the `to_command` method can be used to invoke the /// compiler itself. /// /// This method will take into account all configuration such as debug /// information, optimization level, include directories, defines, etc. /// Additionally, the compiler binary in use follows the standard /// conventions for this path, e.g. looking at the explicitly set compiler, /// environment variables (a number of which are inspected here), and then /// falling back to the default configuration. /// /// # Panics /// /// Panics if an error occurred while determining the architecture. pub fn get_compiler(&self) -> Tool { match self.try_get_compiler() { Ok(tool) => tool, Err(e) => fail(&e.message), } } /// Get the compiler that's in use for this configuration. /// /// This will return a result instead of panicking; see /// [`get_compiler()`](Self::get_compiler) for the complete description. pub fn try_get_compiler(&self) -> Result { let opt_level = self.get_opt_level()?; let target = self.get_target()?; let mut cmd = self.get_base_compiler()?; // The flags below are added in roughly the following order: // 1. Default flags // - Controlled by `cc-rs`. // 2. `rustc`-inherited flags // - Controlled by `rustc`. // 3. Builder flags // - Controlled by the developer using `cc-rs` in e.g. their `build.rs`. // 4. Environment flags // - Controlled by the end user. // // This is important to allow later flags to override previous ones. // Copied from // // Disables non-English messages from localized linkers. // Such messages may cause issues with text encoding on Windows // and prevent inspection of msvc output in case of errors, which we occasionally do. // This should be acceptable because other messages from rustc are in English anyway, // and may also be desirable to improve searchability of the compiler diagnostics. if matches!(cmd.family, ToolFamily::Msvc { clang_cl: false }) { cmd.env.push(("VSLANG".into(), "1033".into())); } else { cmd.env.push(("LC_ALL".into(), "C".into())); } // Disable default flag generation via `no_default_flags` or environment variable let no_defaults = self.no_default_flags || self.get_env_boolean("CRATE_CC_NO_DEFAULTS"); if !no_defaults { self.add_default_flags(&mut cmd, &target, &opt_level)?; } // Specify various flags that are not considered part of the default flags above. // FIXME(madsmtm): Should these be considered part of the defaults? If no, why not? if let Some(ref std) = self.std { let separator = match cmd.family { ToolFamily::Msvc { .. } => ':', ToolFamily::Gnu | ToolFamily::Clang { .. } => '=', }; cmd.push_cc_arg(format!("-std{separator}{std}").into()); } for directory in self.include_directories.iter() { cmd.args.push("-I".into()); cmd.args.push(directory.as_os_str().into()); } if self.warnings_into_errors { let warnings_to_errors_flag = cmd.family.warnings_to_errors_flag().into(); cmd.push_cc_arg(warnings_to_errors_flag); } // If warnings and/or extra_warnings haven't been explicitly set, // then we set them only if the environment doesn't already have // CFLAGS/CXXFLAGS, since those variables presumably already contain // the desired set of warnings flags. let envflags = self.envflags(if self.cpp { "CXXFLAGS" } else { "CFLAGS" })?; match self.warnings { Some(true) => { let wflags = cmd.family.warnings_flags().into(); cmd.push_cc_arg(wflags); } Some(false) => { let wflags = cmd.family.warnings_suppression_flags().into(); cmd.push_cc_arg(wflags); } None => { if envflags.is_none() { let wflags = cmd.family.warnings_flags().into(); cmd.push_cc_arg(wflags); } } } if self.extra_warnings.unwrap_or(envflags.is_none()) { if let Some(wflags) = cmd.family.extra_warnings_flags() { cmd.push_cc_arg(wflags.into()); } } // Add cc flags inherited from matching rustc flags. if self.inherit_rustflags { self.add_inherited_rustflags(&mut cmd, &target)?; } // Set flags configured in the builder (do this second-to-last, to allow these to override // everything above). for flag in self.flags.iter() { cmd.args.push((**flag).into()); } for flag in self.flags_supported.iter() { if self .is_flag_supported_inner(flag, &cmd, &target) .unwrap_or(false) { cmd.push_cc_arg((**flag).into()); } } for (key, value) in self.definitions.iter() { if let Some(ref value) = *value { cmd.args.push(format!("-D{key}={value}").into()); } else { cmd.args.push(format!("-D{key}").into()); } } // Set flags from the environment (do this last, to allow these to override everything else). if let Some(flags) = &envflags { for arg in flags { cmd.push_cc_arg(arg.into()); } } // Set custom env vars that the user specified with `Build::env`. // // Do this last, to allow overwriting the other values above. for (key, val) in &self.env { cmd.env.push((key.into(), val.into())); } Ok(cmd) } fn add_default_flags( &self, cmd: &mut Tool, target: &TargetInfo<'_>, opt_level: &str, ) -> Result<(), Error> { let raw_target = self.get_raw_target()?; // Non-target flags // If the flag is not conditioned on target variable, it belongs here :) match cmd.family { ToolFamily::Msvc { .. } => { cmd.push_cc_arg("-nologo".into()); let crt_flag = match self.static_crt { Some(true) => "-MT", Some(false) => "-MD", None => { let features = cargo_env_var_os("CARGO_CFG_TARGET_FEATURE"); let features = features.as_deref().unwrap_or_default(); if features.to_string_lossy().contains("crt-static") { "-MT" } else { "-MD" } } }; cmd.push_cc_arg(crt_flag.into()); match opt_level { // Msvc uses /O1 to enable all optimizations that minimize code size. "z" | "s" | "1" => cmd.push_opt_unless_duplicate("-O1".into()), // -O3 is a valid value for gcc and clang compilers, but not msvc. Cap to /O2. "2" | "3" => cmd.push_opt_unless_duplicate("-O2".into()), _ => {} } } ToolFamily::Gnu | ToolFamily::Clang { .. } => { // arm-linux-androideabi-gcc 4.8 shipped with Android NDK does // not support '-Oz' if opt_level == "z" && !cmd.is_like_clang() { cmd.push_opt_unless_duplicate("-Os".into()); } else { cmd.push_opt_unless_duplicate(format!("-O{opt_level}").into()); } if cmd.is_like_clang() && target.os == "android" { // For compatibility with code that doesn't use pre-defined `__ANDROID__` macro. // If compiler used via ndk-build or cmake (officially supported build methods) // this macros is defined. // See https://android.googlesource.com/platform/ndk/+/refs/heads/ndk-release-r21/build/cmake/android.toolchain.cmake#456 // https://android.googlesource.com/platform/ndk/+/refs/heads/ndk-release-r21/build/core/build-binary.mk#141 cmd.push_opt_unless_duplicate("-DANDROID".into()); } if target.os != "ios" && target.os != "watchos" && target.os != "tvos" && target.os != "visionos" { cmd.push_cc_arg("-ffunction-sections".into()); cmd.push_cc_arg("-fdata-sections".into()); } // Disable generation of PIC on bare-metal for now: rust-lld doesn't support this yet // // `rustc` also defaults to disable PIC on WASM: // if self.pic.unwrap_or( target.os != "windows" && target.os != "none" && target.os != "uefi" && target.os != "vita" && target.arch != "wasm32" && target.arch != "wasm64", ) { cmd.push_cc_arg("-fPIC".into()); // PLT only applies if code is compiled with PIC support, // and only for ELF targets. if (target.os == "linux" || target.os == "android") && !self.use_plt.unwrap_or(true) { cmd.push_cc_arg("-fno-plt".into()); } } if target.arch == "wasm32" || target.arch == "wasm64" { // WASI does not support exceptions yet. // https://github.com/WebAssembly/exception-handling // // `rustc` also defaults to (currently) disable exceptions // on all WASM targets: // cmd.push_cc_arg("-fno-exceptions".into()); } if target.os == "wasi" { // Link clang sysroot if let Ok(wasi_sysroot) = self.wasi_sysroot() { cmd.push_cc_arg( format!("--sysroot={}", Path::new(&wasi_sysroot).display()).into(), ); } // FIXME(madsmtm): Read from `target_features` instead? if raw_target.contains("threads") { cmd.push_cc_arg("-pthread".into()); } } if target.os == "nto" { // Select the target with `-V`, see qcc documentation: // QNX 7.1: https://www.qnx.com/developers/docs/7.1/index.html#com.qnx.doc.neutrino.utilities/topic/q/qcc.html // QNX 8.0: https://www.qnx.com/developers/docs/8.0/com.qnx.doc.neutrino.utilities/topic/q/qcc.html // This assumes qcc/q++ as compiler, which is currently the only supported compiler for QNX. // See for details: https://github.com/rust-lang/cc-rs/pull/1319 let arg = match target.full_arch { "x86" | "i586" => "-Vgcc_ntox86_cxx", "aarch64" => "-Vgcc_ntoaarch64le_cxx", "x86_64" => "-Vgcc_ntox86_64_cxx", _ => { return Err(Error::new( ErrorKind::InvalidTarget, format!("Unknown architecture for Neutrino QNX: {}", target.arch), )) } }; cmd.push_cc_arg(arg.into()); } } } if self.get_debug() { if self.cuda { // NVCC debug flag cmd.args.push("-G".into()); } let family = cmd.family; family.add_debug_flags( cmd, self.get_debug_str().as_deref().unwrap_or_default(), self.get_dwarf_version(), ); } if self.get_force_frame_pointer() { let family = cmd.family; family.add_force_frame_pointer(cmd); } if !cmd.is_like_msvc() { if target.arch == "x86" { cmd.args.push("-m32".into()); } else if target.abi == "x32" { cmd.args.push("-mx32".into()); } else if target.os == "aix" { if cmd.family == ToolFamily::Gnu { cmd.args.push("-maix64".into()); } else { cmd.args.push("-m64".into()); } } else if target.arch == "x86_64" || target.arch == "powerpc64" { cmd.args.push("-m64".into()); } } // Target flags match cmd.family { ToolFamily::Clang { .. } => { if !(cmd.has_internal_target_arg || (target.os == "android" && android_clang_compiler_uses_target_arg_internally(&cmd.path))) { if target.os == "freebsd" { // FreeBSD only supports C++11 and above when compiling against libc++ // (available from FreeBSD 10 onwards). Under FreeBSD, clang uses libc++ by // default on FreeBSD 10 and newer unless `--target` is manually passed to // the compiler, in which case its default behavior differs: // * If --target=xxx-unknown-freebsdX(.Y) is specified and X is greater than // or equal to 10, clang++ uses libc++ // * If --target=xxx-unknown-freebsd is specified (without a version), // clang++ cannot assume libc++ is available and reverts to a default of // libstdc++ (this behavior was changed in llvm 14). // // This breaks C++11 (or greater) builds if targeting FreeBSD with the // generic xxx-unknown-freebsd target on clang 13 or below *without* // explicitly specifying that libc++ should be used. // When cross-compiling, we can't infer from the rust/cargo target name // which major version of FreeBSD we are targeting, so we need to make sure // that libc++ is used (unless the user has explicitly specified otherwise). // There's no compelling reason to use a different approach when compiling // natively. if self.cpp && self.cpp_set_stdlib.is_none() { cmd.push_cc_arg("-stdlib=libc++".into()); } } else if target.arch == "wasm32" && target.os == "linux" { for x in &[ "atomics", "bulk-memory", "mutable-globals", "sign-ext", "exception-handling", ] { cmd.push_cc_arg(format!("-m{x}").into()); } for x in &["wasm-exceptions", "declspec"] { cmd.push_cc_arg(format!("-f{x}").into()); } let musl_sysroot = self.wasm_musl_sysroot().unwrap(); cmd.push_cc_arg( format!("--sysroot={}", Path::new(&musl_sysroot).display()).into(), ); cmd.push_cc_arg("-pthread".into()); } // Pass `--target` with the LLVM target to configure Clang for cross-compiling. // // This is **required** for cross-compilation, as it's the only flag that // consistently forces Clang to change the "toolchain" that is responsible for // parsing target-specific flags: // https://github.com/rust-lang/cc-rs/issues/1388 // https://github.com/llvm/llvm-project/blob/llvmorg-19.1.7/clang/lib/Driver/Driver.cpp#L1359-L1360 // https://github.com/llvm/llvm-project/blob/llvmorg-19.1.7/clang/lib/Driver/Driver.cpp#L6347-L6532 // // This can be confusing, because on e.g. host macOS, you can usually get by // with `-arch` and `-mtargetos=`. But that only works because the _default_ // toolchain is `Darwin`, which enables parsing of darwin-specific options. // // NOTE: In the past, we passed the deployment version in here on all Apple // targets, but versioned targets were found to have poor compatibility with // older versions of Clang, especially when it comes to configuration files: // https://github.com/rust-lang/cc-rs/issues/1278 // // So instead, we pass the deployment target with `-m*-version-min=`, and only // pass it here on visionOS and Mac Catalyst where that option does not exist: // https://github.com/rust-lang/cc-rs/issues/1383 let version = if target.os == "visionos" || target.env == "macabi" { Some(self.apple_deployment_target(target)) } else { None }; let clang_target = target.llvm_target(&self.get_raw_target()?, version.as_deref()); cmd.push_cc_arg(format!("--target={clang_target}").into()); } } ToolFamily::Msvc { clang_cl } => { // This is an undocumented flag from MSVC but helps with making // builds more reproducible by avoiding putting timestamps into // files. cmd.push_cc_arg("-Brepro".into()); if clang_cl { cmd.push_cc_arg( format!( "--target={}", target.llvm_target(&self.get_raw_target()?, None) ) .into(), ); if target.arch == "x86" { // See // . // // NOTE: Rust officially supported Windows targets all require SSE2 as part // of baseline target features. // // NOTE: The same applies for STL. See: - // , and - // . cmd.push_cc_arg("-arch:SSE2".into()); } } else if target.full_arch == "i586" { cmd.push_cc_arg("-arch:IA32".into()); } else if target.full_arch == "arm64ec" { cmd.push_cc_arg("-arm64EC".into()); } // There is a check in corecrt.h that will generate a // compilation error if // _ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE is // not defined to 1. The check was added in Windows // 8 days because only store apps were allowed on ARM. // This changed with the release of Windows 10 IoT Core. // The check will be going away in future versions of // the SDK, but for all released versions of the // Windows SDK it is required. if target.arch == "arm" { cmd.args .push("-D_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1".into()); } } ToolFamily::Gnu => { if target.vendor == "kmc" { cmd.args.push("-finput-charset=utf-8".into()); } if self.static_flag.is_none() { let features = cargo_env_var_os("CARGO_CFG_TARGET_FEATURE"); let features = features.as_deref().unwrap_or_default(); if features.to_string_lossy().contains("crt-static") { cmd.args.push("-static".into()); } } // armv7 targets get to use armv7 instructions if (target.full_arch.starts_with("armv7") || target.full_arch.starts_with("thumbv7")) && (target.os == "linux" || target.vendor == "kmc") { cmd.args.push("-march=armv7-a".into()); if target.abi == "eabihf" { // lowest common denominator FPU cmd.args.push("-mfpu=vfpv3-d16".into()); cmd.args.push("-mfloat-abi=hard".into()); } } // (x86 Android doesn't say "eabi") if target.os == "android" && target.full_arch.contains("v7") { cmd.args.push("-march=armv7-a".into()); cmd.args.push("-mthumb".into()); if !target.full_arch.contains("neon") { // On android we can guarantee some extra float instructions // (specified in the android spec online) // NEON guarantees even more; see below. cmd.args.push("-mfpu=vfpv3-d16".into()); } cmd.args.push("-mfloat-abi=softfp".into()); } if target.full_arch.contains("neon") { cmd.args.push("-mfpu=neon-vfpv4".into()); } if target.full_arch == "armv4t" && target.os == "linux" { cmd.args.push("-march=armv4t".into()); cmd.args.push("-marm".into()); cmd.args.push("-mfloat-abi=soft".into()); } if target.full_arch == "armv5te" && target.os == "linux" { cmd.args.push("-march=armv5te".into()); cmd.args.push("-marm".into()); cmd.args.push("-mfloat-abi=soft".into()); } // For us arm == armv6 by default if target.full_arch == "arm" && target.os == "linux" { cmd.args.push("-march=armv6".into()); cmd.args.push("-marm".into()); if target.abi == "eabihf" { cmd.args.push("-mfpu=vfp".into()); } else { cmd.args.push("-mfloat-abi=soft".into()); } } // Turn codegen down on i586 to avoid some instructions. if target.full_arch == "i586" && target.os == "linux" { cmd.args.push("-march=pentium".into()); } // Set codegen level for i686 correctly if target.full_arch == "i686" && target.os == "linux" { cmd.args.push("-march=i686".into()); } // Looks like `musl-gcc` makes it hard for `-m32` to make its way // all the way to the linker, so we need to actually instruct the // linker that we're generating 32-bit executables as well. This'll // typically only be used for build scripts which transitively use // these flags that try to compile executables. if target.arch == "x86" && target.env == "musl" { cmd.args.push("-Wl,-melf_i386".into()); } if target.arch == "arm" && target.os == "none" && target.abi == "eabihf" { cmd.args.push("-mfloat-abi=hard".into()) } if target.full_arch.starts_with("thumb") { cmd.args.push("-mthumb".into()); } if target.full_arch.starts_with("thumbv6m") { cmd.args.push("-march=armv6s-m".into()); } if target.full_arch.starts_with("thumbv7em") { cmd.args.push("-march=armv7e-m".into()); if target.abi == "eabihf" { cmd.args.push("-mfpu=fpv4-sp-d16".into()) } } if target.full_arch.starts_with("thumbv7m") { cmd.args.push("-march=armv7-m".into()); } if target.full_arch.starts_with("thumbv8m.base") { cmd.args.push("-march=armv8-m.base".into()); } if target.full_arch.starts_with("thumbv8m.main") { cmd.args.push("-march=armv8-m.main".into()); if target.abi == "eabihf" { cmd.args.push("-mfpu=fpv5-sp-d16".into()) } } if target.full_arch.starts_with("armebv7r") | target.full_arch.starts_with("armv7r") { if target.full_arch.starts_with("armeb") { cmd.args.push("-mbig-endian".into()); } else { cmd.args.push("-mlittle-endian".into()); } // ARM mode cmd.args.push("-marm".into()); // R Profile cmd.args.push("-march=armv7-r".into()); if target.abi == "eabihf" { // lowest common denominator FPU // (see Cortex-R4 technical reference manual) cmd.args.push("-mfpu=vfpv3-d16".into()) } } if target.full_arch.starts_with("armv7a") { cmd.args.push("-march=armv7-a".into()); if target.abi == "eabihf" { // lowest common denominator FPU cmd.args.push("-mfpu=vfpv3-d16".into()); } } if target.arch == "riscv32" || target.arch == "riscv64" { // get the 32i/32imac/32imc/64gc/64imac/... part let arch = &target.full_arch[5..]; if arch.starts_with("64") { if matches!(target.os, "linux" | "freebsd" | "netbsd") { cmd.args.push(("-march=rv64gc").into()); cmd.args.push("-mabi=lp64d".into()); } else { cmd.args.push(("-march=rv".to_owned() + arch).into()); cmd.args.push("-mabi=lp64".into()); } } else if arch.starts_with("32") { if target.os == "linux" { cmd.args.push(("-march=rv32gc").into()); cmd.args.push("-mabi=ilp32d".into()); } else { cmd.args.push(("-march=rv".to_owned() + arch).into()); cmd.args.push("-mabi=ilp32".into()); } } else { cmd.args.push("-mcmodel=medany".into()); } } } } if raw_target == "wasm32v1-none" { // `wasm32v1-none` target only exists in `rustc`, so we need to change the compilation flags: // https://doc.rust-lang.org/rustc/platform-support/wasm32v1-none.html cmd.push_cc_arg("-mcpu=mvp".into()); cmd.push_cc_arg("-mmutable-globals".into()); } if target.os == "solaris" || target.os == "illumos" { // On Solaris and illumos, multi-threaded C programs must be built with `_REENTRANT` // defined. This configures headers to define APIs appropriately for multi-threaded // use. This is documented in threads(7), see also https://illumos.org/man/7/threads. // // If C code is compiled without multi-threading support but does use multiple threads, // incorrect behavior may result. One extreme example is that on some systems the // global errno may be at the same address as the process' first thread's errno; errno // clobbering may occur to disastrous effect. Conversely, if _REENTRANT is defined // while it is not actually needed, system headers may define some APIs suboptimally // but will not result in incorrect behavior. Other code *should* be reasonable under // such conditions. // // We're typically building C code to eventually link into a Rust program. Many Rust // programs are multi-threaded in some form. So, set the flag by default. cmd.args.push("-D_REENTRANT".into()); } if target.vendor == "apple" { self.apple_flags(cmd)?; } if self.static_flag.unwrap_or(false) { cmd.args.push("-static".into()); } if self.shared_flag.unwrap_or(false) { cmd.args.push("-shared".into()); } if self.cpp { match (self.cpp_set_stdlib.as_ref(), cmd.family) { (None, _) => {} (Some(stdlib), ToolFamily::Gnu) | (Some(stdlib), ToolFamily::Clang { .. }) => { cmd.push_cc_arg(format!("-stdlib=lib{stdlib}").into()); } _ => { self.cargo_output.print_warning(&format_args!("cpp_set_stdlib is specified, but the {:?} compiler does not support this option, ignored", cmd.family)); } } } Ok(()) } fn add_inherited_rustflags( &self, cmd: &mut Tool, target: &TargetInfo<'_>, ) -> Result<(), Error> { let env_os = match cargo_env_var_os("CARGO_ENCODED_RUSTFLAGS") { Some(env) => env, // No encoded RUSTFLAGS -> nothing to do None => return Ok(()), }; let env = env_os.to_string_lossy(); let codegen_flags = RustcCodegenFlags::parse(&env)?; codegen_flags.cc_flags(self, cmd, target); Ok(()) } fn msvc_macro_assembler(&self) -> Result { let target = self.get_target()?; let tool = match target.arch { "x86_64" => "ml64.exe", "arm" => "armasm.exe", "aarch64" | "arm64ec" => "armasm64.exe", _ => "ml.exe", }; let mut cmd = self .find_msvc_tools_find(&target, tool) .unwrap_or_else(|| self.cmd(tool)); cmd.arg("-nologo"); // undocumented, yet working with armasm[64] for directory in self.include_directories.iter() { cmd.arg("-I").arg(&**directory); } if is_arm(&target) { if self.get_debug() { cmd.arg("-g"); } if target.arch == "arm64ec" { cmd.args(["-machine", "ARM64EC"]); } for (key, value) in self.definitions.iter() { cmd.arg("-PreDefine"); if let Some(ref value) = *value { if let Ok(i) = value.parse::() { cmd.arg(format!("{key} SETA {i}")); } else if value.starts_with('"') && value.ends_with('"') { cmd.arg(format!("{key} SETS {value}")); } else { cmd.arg(format!("{key} SETS \"{value}\"")); } } else { cmd.arg(format!("{} SETL {}", key, "{TRUE}")); } } } else { if self.get_debug() { cmd.arg("-Zi"); } for (key, value) in self.definitions.iter() { if let Some(ref value) = *value { cmd.arg(format!("-D{key}={value}")); } else { cmd.arg(format!("-D{key}")); } } } if target.arch == "x86" { cmd.arg("-safeseh"); } Ok(cmd) } fn assemble(&self, lib_name: &str, dst: &Path, objs: &[Object]) -> Result<(), Error> { // Delete the destination if it exists as we want to // create on the first iteration instead of appending. let _ = fs::remove_file(dst); // Add objects to the archive in limited-length batches. This helps keep // the length of the command line within a reasonable length to avoid // blowing system limits on limiting platforms like Windows. // // Optimistically try the `D` (deterministic) ar modifier, which zeros // out timestamps, UIDs, and GIDs. If the archiver doesn't support it, // we remember and stop trying for subsequent batches. // (`None` -> haven't probed yet) let mut deterministic_ar: Option = None; let mut objs = objs .iter() .map(|o| o.dst.as_path()) .chain(self.objects.iter().map(std::ops::Deref::deref)) .peekable(); let mut batch = Vec::new(); while objs.peek().is_some() { let mut remaining_len = 4000; while let Some(path) = objs.next_if(|peek| batch.is_empty() || peek.as_os_str().len() <= remaining_len) { batch.push(path); remaining_len = remaining_len.saturating_sub(path.as_os_str().len()); } self.assemble_progressive(dst, &batch, &mut deterministic_ar)?; batch.clear(); } if self.cuda && self.cuda_file_count() > 0 { // Link the device-side code and add it to the target library, // so that non-CUDA linker can link the final binary. let out_dir = self.get_out_dir()?; let dlink = out_dir.join(lib_name.to_owned() + "_dlink.o"); let mut nvcc = self.get_compiler().to_command(); nvcc.arg("--device-link").arg("-o").arg(&dlink).arg(dst); run(&mut nvcc, &self.cargo_output)?; self.assemble_progressive(dst, &[dlink.as_path()], &mut deterministic_ar)?; } let target = self.get_target()?; if target.env == "msvc" { // The Rust compiler will look for libfoo.a and foo.lib, but the // MSVC linker will also be passed foo.lib, so be sure that both // exist for now. let lib_dst = dst.with_file_name(format!("{lib_name}.lib")); let _ = fs::remove_file(&lib_dst); match fs::hard_link(dst, &lib_dst).or_else(|_| { // if hard-link fails, just copy (ignoring the number of bytes written) fs::copy(dst, &lib_dst).map(|_| ()) }) { Ok(_) => (), Err(_) => { return Err(Error::new( ErrorKind::IOError, "Could not copy or create a hard-link to the generated lib file.", )); } }; } else { // Non-msvc targets (those using `ar`) need a separate step to add // the symbol table to archives since our construction command of // `cq` doesn't add it for us. let mut ar = self.try_get_archiver()?; // NOTE: We add `s` even if flags were passed using $ARFLAGS/ar_flag, because `s` // here represents a _mode_, not an arbitrary flag. Further discussion of this choice // can be seen in https://github.com/rust-lang/cc-rs/pull/763. match deterministic_ar { Some(false) => { // See comment in `assemble_progressive` for more on ZERO_AR_DATE. ar.env("ZERO_AR_DATE", "1"); run(ar.arg("s").arg(dst), &self.cargo_output)?; } Some(true) => { run(ar.arg("sD").arg(dst), &self.cargo_output)?; } None => { if run_silent_on_error(ar.arg("sD").arg(dst), &self.cargo_output).is_err() { let mut ar = self.try_get_archiver()?; ar.env("ZERO_AR_DATE", "1"); run(ar.arg("s").arg(dst), &self.cargo_output)?; } } } } Ok(()) } fn assemble_progressive( &self, dst: &Path, objs: &[&Path], deterministic_ar: &mut Option, ) -> Result<(), Error> { let target = self.get_target()?; let (mut cmd, program, any_flags) = self.try_get_archiver_and_flags()?; if target.env == "msvc" && !program.to_string_lossy().contains("llvm-ar") { // NOTE: -out: here is an I/O flag, and so must be included even if $ARFLAGS/ar_flag is // in use. -nologo on the other hand is just a regular flag, and one that we'll skip if // the caller has explicitly dictated the flags they want. See // https://github.com/rust-lang/cc-rs/pull/763 for further discussion. let mut out = OsString::from("-out:"); out.push(dst); cmd.arg(out); if !any_flags { cmd.arg("-nologo"); } // If the library file already exists, add the library name // as an argument to let lib.exe know we are appending the objs. if dst.exists() { cmd.arg(dst); } cmd.args(objs); run(&mut cmd, &self.cargo_output)?; } else { // Set an environment variable to tell the OSX archiver to ensure // that all dates listed in the archive are zero, improving // determinism of builds. AFAIK there's not really official // documentation of this but there's a lot of references to it if // you search google. // // You can reproduce this locally on a mac with: // // $ touch foo.c // $ cc -c foo.c -o foo.o // // # Notice that these two checksums are different // $ ar crus libfoo1.a foo.o && sleep 2 && ar crus libfoo2.a foo.o // $ md5sum libfoo*.a // // # Notice that these two checksums are the same // $ export ZERO_AR_DATE=1 // $ ar crus libfoo1.a foo.o && sleep 2 && touch foo.o && ar crus libfoo2.a foo.o // $ md5sum libfoo*.a // // In any case if this doesn't end up getting read, it shouldn't // cause that many issues! cmd.env("ZERO_AR_DATE", "1"); // NOTE: We add cq here regardless of whether $ARFLAGS/ar_flag have been used because // it dictates the _mode_ ar runs in, which the setter of $ARFLAGS/ar_flag can't // dictate. See https://github.com/rust-lang/cc-rs/pull/763 for further discussion. match *deterministic_ar { Some(false) => { run(cmd.arg("cq").arg(dst).args(objs), &self.cargo_output)?; } Some(true) => { run(cmd.arg("cqD").arg(dst).args(objs), &self.cargo_output)?; } None => { // Probe: try `D` and remember the result for later batches. if run_silent_on_error(cmd.arg("cqD").arg(dst).args(objs), &self.cargo_output) .is_ok() { *deterministic_ar = Some(true); } else { *deterministic_ar = Some(false); let (mut cmd, _, _) = self.try_get_archiver_and_flags()?; cmd.env("ZERO_AR_DATE", "1"); run(cmd.arg("cq").arg(dst).args(objs), &self.cargo_output)?; } } } } Ok(()) } fn apple_flags(&self, cmd: &mut Tool) -> Result<(), Error> { let target = self.get_target()?; // This is a Darwin/Apple-specific flag that works both on GCC and Clang, but it is only // necessary on GCC since we specify `-target` on Clang. // https://gcc.gnu.org/onlinedocs/gcc/Darwin-Options.html#:~:text=arch // https://clang.llvm.org/docs/CommandGuide/clang.html#cmdoption-arch if cmd.is_like_gnu() { let arch = map_darwin_target_from_rust_to_compiler_architecture(&target); cmd.args.push("-arch".into()); cmd.args.push(arch.into()); } // Pass the deployment target via `-mmacosx-version-min=`, `-miphoneos-version-min=` and // similar. Also necessary on GCC, as it forces a compilation error if the compiler is not // configured for Darwin: https://gcc.gnu.org/onlinedocs/gcc/Darwin-Options.html // // On visionOS and Mac Catalyst, there is no -m*-version-min= flag: // https://github.com/llvm/llvm-project/issues/88271 // And the workaround to use `-mtargetos=` cannot be used with the `--target` flag that we // otherwise specify. So we avoid emitting that, and put the version in `--target` instead. if cmd.is_like_gnu() || !(target.os == "visionos" || target.env == "macabi") { let min_version = self.apple_deployment_target(&target); cmd.args .push(target.apple_version_flag(&min_version).into()); } // AppleClang sometimes requires sysroot even on macOS if cmd.is_xctoolchain_clang() || target.os != "macos" { self.cargo_output.print_metadata(&format_args!( "Detecting {:?} SDK path for {}", target.os, target.apple_sdk_name(), )); let sdk_path = self.apple_sdk_root(&target)?; cmd.args.push("-isysroot".into()); cmd.args.push(OsStr::new(&sdk_path).to_owned()); cmd.env .push(("SDKROOT".into(), OsStr::new(&sdk_path).to_owned())); if target.env == "macabi" { // Mac Catalyst uses the macOS SDK, but to compile against and // link to iOS-specific frameworks, we should have the support // library stubs in the include and library search path. let ios_support = Path::new(&sdk_path).join("System/iOSSupport"); cmd.args.extend([ // Header search path OsString::from("-isystem"), ios_support.join("usr/include").into(), // Framework header search path OsString::from("-iframework"), ios_support.join("System/Library/Frameworks").into(), // Library search path { let mut s = OsString::from("-L"); s.push(ios_support.join("usr/lib")); s }, // Framework linker search path { // Technically, we _could_ avoid emitting `-F`, as // `-iframework` implies it, but let's keep it in for // clarity. let mut s = OsString::from("-F"); s.push(ios_support.join("System/Library/Frameworks")); s }, ]); } } Ok(()) } fn cmd>(&self, prog: P) -> Command { let mut cmd = Command::new(prog); for (a, b) in self.env.iter() { cmd.env(a, b); } cmd } fn prefer_clang(&self) -> bool { if let Some(env) = cargo_env_var_os("CARGO_ENCODED_RUSTFLAGS") { env.to_string_lossy().contains("linker-plugin-lto") } else { false } } fn get_base_compiler(&self) -> Result { let out_dir = self.get_out_dir().ok(); let out_dir = out_dir.as_deref(); if let Some(c) = &self.compiler { return Ok(Tool::new( (**c).to_owned(), &self.build_cache.cached_compiler_family, &self.cargo_output, out_dir, )); } let target = self.get_target()?; let raw_target = self.get_raw_target()?; let msvc = if self.prefer_clang_cl_over_msvc { "clang-cl.exe" } else { "cl.exe" }; let (env, gnu, traditional, clang) = if self.cpp { ("CXX", "g++", "c++", "clang++") } else { ("CC", "gcc", "cc", "clang") }; let fallback = Cow::Borrowed(Path::new(traditional)); let default = if cfg!(target_os = "solaris") || cfg!(target_os = "illumos") { // On historical Solaris systems, "cc" may have been Sun Studio, which // is not flag-compatible with "gcc". This history casts a long shadow, // and many modern illumos distributions today ship GCC as "gcc" without // also making it available as "cc". Cow::Borrowed(Path::new(gnu)) } else if self.prefer_clang() { self.which(Path::new(clang), None) .map(Cow::Owned) .unwrap_or(fallback) } else { fallback }; let cl_exe = self.find_msvc_tools_find_tool(&target, msvc); let tool_opt: Option = self .env_tool(env) .map(|(tool, wrapper, args)| { // Chop off leading/trailing whitespace to work around // semi-buggy build scripts which are shared in // makefiles/configure scripts (where spaces are far more // lenient) let mut t = Tool::with_args( tool, args.clone(), &self.build_cache.cached_compiler_family, &self.cargo_output, out_dir, ); if let Some(cc_wrapper) = wrapper { t.cc_wrapper_path = Some(Path::new(&cc_wrapper).to_owned()); } for arg in args { t.cc_wrapper_args.push(arg.into()); } t }) .or_else(|| { if target.os == "emscripten" { let tool = if self.cpp { "em++" } else { "emcc" }; // Windows uses bat file so we have to be a bit more specific if cfg!(windows) { let mut t = Tool::with_family( PathBuf::from("cmd"), ToolFamily::Clang { zig_cc: false }, ); t.args.push("/c".into()); t.args.push(format!("{tool}.bat").into()); Some(t) } else { Some(Tool::new( PathBuf::from(tool), &self.build_cache.cached_compiler_family, &self.cargo_output, out_dir, )) } } else { None } }) .or_else(|| cl_exe.clone()); let tool = match tool_opt { Some(t) => t, None => { let compiler: PathBuf = if cfg!(windows) && target.os == "windows" { if target.env == "msvc" { msvc.into() } else { let cc = if target.abi == "llvm" { clang } else { gnu }; format!("{cc}.exe").into() } } else if target.os == "ios" || target.os == "watchos" || target.os == "tvos" || target.os == "visionos" { clang.into() } else if target.os == "android" { autodetect_android_compiler(&raw_target, gnu, clang) } else if target.os == "cloudabi" { format!( "{}-{}-{}-{}", target.full_arch, target.vendor, target.os, traditional ) .into() } else if target.os == "wasi" { self.autodetect_wasi_compiler(&raw_target, clang) } else if target.arch == "wasm32" || target.arch == "wasm64" { // Compiling WASM is not currently supported by GCC, so // let's default to Clang. clang.into() } else if target.os == "vxworks" { if self.cpp { "wr-c++" } else { "wr-cc" }.into() } else if target.arch == "arm" && target.vendor == "kmc" { format!("arm-kmc-eabi-{gnu}").into() } else if target.arch == "aarch64" && target.vendor == "kmc" { format!("aarch64-kmc-elf-{gnu}").into() } else if target.os == "nto" { // See for details: https://github.com/rust-lang/cc-rs/pull/1319 if self.cpp { "q++" } else { "qcc" }.into() } else if self.get_is_cross_compile()? { let prefix = self.prefix_for_target(&raw_target); match prefix { Some(prefix) => { let cc = if target.abi == "llvm" { clang } else { gnu }; format!("{prefix}-{cc}").into() } None => default.into(), } } else { default.into() }; let mut t = Tool::new( compiler, &self.build_cache.cached_compiler_family, &self.cargo_output, out_dir, ); if let Some(cc_wrapper) = self.rustc_wrapper_fallback() { t.cc_wrapper_path = Some(Path::new(&cc_wrapper).to_owned()); } t } }; let mut tool = if self.cuda { assert!( tool.args.is_empty(), "CUDA compilation currently assumes empty pre-existing args" ); let nvcc = match self.getenv_with_target_prefixes("NVCC") { Err(_) => PathBuf::from("nvcc"), Ok(nvcc) => PathBuf::from(&*nvcc), }; let mut nvcc_tool = Tool::with_features( nvcc, vec![], self.cuda, &self.build_cache.cached_compiler_family, &self.cargo_output, out_dir, ); if self.ccbin { nvcc_tool .args .push(format!("-ccbin={}", tool.path.display()).into()); } if let Some(cc_wrapper) = self.rustc_wrapper_fallback() { nvcc_tool.cc_wrapper_path = Some(Path::new(&cc_wrapper).to_owned()); } nvcc_tool.family = tool.family; nvcc_tool } else { tool }; // New "standalone" C/C++ cross-compiler executables from recent Android NDK // are just shell scripts that call main clang binary (from Android NDK) with // proper `--target` argument. // // For example, armv7a-linux-androideabi16-clang passes // `--target=armv7a-linux-androideabi16` to clang. // // As the shell script calls the main clang binary, the command line limit length // on Windows is restricted to around 8k characters instead of around 32k characters. // To remove this limit, we call the main clang binary directly and construct the // `--target=` ourselves. if cfg!(windows) && android_clang_compiler_uses_target_arg_internally(&tool.path) { if let Some(path) = tool.path.file_name() { let file_name = path.to_str().unwrap().to_owned(); let (target, clang) = file_name.split_at(file_name.rfind('-').unwrap()); tool.has_internal_target_arg = true; tool.path.set_file_name(clang.trim_start_matches('-')); tool.path.set_extension("exe"); tool.args.push(format!("--target={target}").into()); // Additionally, shell scripts for target i686-linux-android versions 16 to 24 // pass the `mstackrealign` option so we do that here as well. if target.contains("i686-linux-android") { let (_, version) = target.split_at(target.rfind('d').unwrap() + 1); if let Ok(version) = version.parse::() { if version > 15 && version < 25 { tool.args.push("-mstackrealign".into()); } } } }; } // Under cross-compilation scenarios, llvm-mingw's clang executable is just a // wrapper script that calls the actual clang binary with a suitable `--target` // argument, much like the Android NDK case outlined above. Passing a target // argument ourselves in this case will result in an error, as they expect // targets like `x86_64-w64-mingw32`, and we can't always set such a target // string because it is specific to this MinGW cross-compilation toolchain. // // For example, the following command will always fail due to using an unsuitable // `--target` argument we'd otherwise pass: // $ /opt/llvm-mingw-20250613-ucrt-ubuntu-22.04-x86_64/bin/x86_64-w64-mingw32-clang --target=x86_64-pc-windows-gnu dummy.c // // Code reference: // https://github.com/mstorsjo/llvm-mingw/blob/a1f6413e5c21fd74b64137b56167f4fba500d1d8/wrappers/clang-target-wrapper.sh#L31 if !cfg!(windows) && target.os == "windows" && is_llvm_mingw_wrapper(&tool.path) { tool.has_internal_target_arg = true; } // If we found `cl.exe` in our environment, the tool we're returning is // an MSVC-like tool, *and* no env vars were set then set env vars for // the tool that we're returning. // // Env vars are needed for things like `link.exe` being put into PATH as // well as header include paths sometimes. These paths are automatically // included by default but if the `CC` or `CXX` env vars are set these // won't be used. This'll ensure that when the env vars are used to // configure for invocations like `clang-cl` we still get a "works out // of the box" experience. if let Some(cl_exe) = cl_exe { if tool.family == (ToolFamily::Msvc { clang_cl: true }) && tool.env.is_empty() && target.env == "msvc" { for (k, v) in cl_exe.env.iter() { tool.env.push((k.to_owned(), v.to_owned())); } } } if target.env == "msvc" && tool.family == ToolFamily::Gnu { self.cargo_output .print_warning(&"GNU compiler is not supported for this target"); } Ok(tool) } /// Returns a fallback `cc_compiler_wrapper` by introspecting `RUSTC_WRAPPER` fn rustc_wrapper_fallback(&self) -> Option> { // No explicit CC wrapper was detected, but check if RUSTC_WRAPPER // is defined and is a build accelerator that is compatible with // C/C++ compilers (e.g. sccache) const VALID_WRAPPERS: &[&str] = &["sccache", "cachepot", "buildcache"]; let rustc_wrapper = cargo_env_var_os("RUSTC_WRAPPER")?; let wrapper_path = Path::new(&rustc_wrapper); let wrapper_stem = wrapper_path.file_stem()?; if VALID_WRAPPERS.contains(&wrapper_stem.to_str()?) { Some(Cow::Owned(rustc_wrapper)) } else { None } } /// Returns compiler path, optional modifier name from whitelist, and arguments vec fn env_tool(&self, name: &str) -> Option<(PathBuf, Option>, Vec)> { let tool = self.getenv_with_target_prefixes(name).ok()?; let tool = tool.to_string_lossy(); let tool = tool.trim(); if tool.is_empty() { return None; } // If this is an exact path on the filesystem we don't want to do any // interpretation at all, just pass it on through. This'll hopefully get // us to support spaces-in-paths. if let Some(exe) = check_exe(Path::new(tool).into()) { return Some((exe, self.rustc_wrapper_fallback(), Vec::new())); } // Ok now we want to handle a couple of scenarios. We'll assume from // here on out that spaces are splitting separate arguments. Two major // features we want to support are: // // CC='sccache cc' // // aka using `sccache` or any other wrapper/caching-like-thing for // compilations. We want to know what the actual compiler is still, // though, because our `Tool` API support introspection of it to see // what compiler is in use. // // additionally we want to support // // CC='cc -flag' // // where the CC env var is used to also pass default flags to the C // compiler. // // It's true that everything here is a bit of a pain, but apparently if // you're not literally make or bash then you get a lot of bug reports. let mut known_wrappers = vec![ "ccache", "distcc", "sccache", "icecc", "cachepot", "buildcache", ]; let custom_wrapper = self.get_env("CC_KNOWN_WRAPPER_CUSTOM"); if custom_wrapper.is_some() { known_wrappers.push(custom_wrapper.as_deref().unwrap().to_str().unwrap()); } let mut parts = tool.split_whitespace(); let maybe_wrapper = parts.next()?; let file_stem = Path::new(maybe_wrapper).file_stem()?.to_str()?; if known_wrappers.contains(&file_stem) { if let Some(compiler) = parts.next() { return Some(( compiler.into(), Some(Cow::Owned(maybe_wrapper.into())), parts.map(|s| s.to_string()).collect(), )); } } Some(( maybe_wrapper.into(), self.rustc_wrapper_fallback(), parts.map(|s| s.to_string()).collect(), )) } /// Returns the C++ standard library: /// 1. If [`cpp_link_stdlib`](cc::Build::cpp_link_stdlib) is set, uses its value. /// 2. Else if the `CXXSTDLIB` environment variable is set, uses its value. /// 3. Else the default is `c++` for OS X and BSDs, `c++_shared` for Android, /// `None` for MSVC and `stdc++` for anything else. fn get_cpp_link_stdlib(&self) -> Result>, Error> { match &self.cpp_link_stdlib { Some(s) => Ok(s.as_deref().map(Path::new).map(Cow::Borrowed)), None => { if let Ok(stdlib) = self.getenv_with_target_prefixes("CXXSTDLIB") { if stdlib.is_empty() { Ok(None) } else { Ok(Some(Cow::Owned(Path::new(&stdlib).to_owned()))) } } else { let target = self.get_target()?; if target.env == "msvc" { Ok(None) } else if target.vendor == "apple" || target.os == "freebsd" || target.os == "openbsd" || target.os == "aix" || (target.os == "linux" && target.env == "ohos") || target.os == "wasi" { Ok(Some(Cow::Borrowed(Path::new("c++")))) } else if target.os == "android" { Ok(Some(Cow::Borrowed(Path::new("c++_shared")))) } else { Ok(Some(Cow::Borrowed(Path::new("stdc++")))) } } } } } /// Get the archiver (ar) that's in use for this configuration. /// /// You can use [`Command::get_program`] to get just the path to the command. /// /// This method will take into account all configuration such as debug /// information, optimization level, include directories, defines, etc. /// Additionally, the compiler binary in use follows the standard /// conventions for this path, e.g. looking at the explicitly set compiler, /// environment variables (a number of which are inspected here), and then /// falling back to the default configuration. /// /// # Panics /// /// Panics if an error occurred while determining the architecture. pub fn get_archiver(&self) -> Command { match self.try_get_archiver() { Ok(tool) => tool, Err(e) => fail(&e.message), } } /// Get the archiver that's in use for this configuration. /// /// This will return a result instead of panicking; /// see [`Self::get_archiver`] for the complete description. pub fn try_get_archiver(&self) -> Result { Ok(self.try_get_archiver_and_flags()?.0) } fn try_get_archiver_and_flags(&self) -> Result<(Command, PathBuf, bool), Error> { let (mut cmd, name) = self.get_base_archiver()?; let mut any_flags = false; if let Some(flags) = self.envflags("ARFLAGS")? { any_flags = true; cmd.args(flags); } for flag in &self.ar_flags { any_flags = true; cmd.arg(&**flag); } Ok((cmd, name, any_flags)) } fn get_base_archiver(&self) -> Result<(Command, PathBuf), Error> { if let Some(ref a) = self.archiver { let archiver = &**a; return Ok((self.cmd(archiver), archiver.into())); } self.get_base_archiver_variant("AR", "ar") } /// Get the ranlib that's in use for this configuration. /// /// You can use [`Command::get_program`] to get just the path to the command. /// /// This method will take into account all configuration such as debug /// information, optimization level, include directories, defines, etc. /// Additionally, the compiler binary in use follows the standard /// conventions for this path, e.g. looking at the explicitly set compiler, /// environment variables (a number of which are inspected here), and then /// falling back to the default configuration. /// /// # Panics /// /// Panics if an error occurred while determining the architecture. pub fn get_ranlib(&self) -> Command { match self.try_get_ranlib() { Ok(tool) => tool, Err(e) => fail(&e.message), } } /// Get the ranlib that's in use for this configuration. /// /// This will return a result instead of panicking; /// see [`Self::get_ranlib`] for the complete description. pub fn try_get_ranlib(&self) -> Result { let mut cmd = self.get_base_ranlib()?; if let Some(flags) = self.envflags("RANLIBFLAGS")? { cmd.args(flags); } Ok(cmd) } fn get_base_ranlib(&self) -> Result { if let Some(ref r) = self.ranlib { return Ok(self.cmd(&**r)); } Ok(self.get_base_archiver_variant("RANLIB", "ranlib")?.0) } fn get_base_archiver_variant( &self, env: &str, tool: &str, ) -> Result<(Command, PathBuf), Error> { let target = self.get_target()?; let mut name = PathBuf::new(); let tool_opt: Option = self .env_tool(env) .map(|(tool, _wrapper, args)| { name.clone_from(&tool); let mut cmd = self.cmd(tool); cmd.args(args); cmd }) .or_else(|| { if target.os == "emscripten" { // Windows use bat files so we have to be a bit more specific if cfg!(windows) { let mut cmd = self.cmd("cmd"); name = format!("em{tool}.bat").into(); cmd.arg("/c").arg(&name); Some(cmd) } else { name = format!("em{tool}").into(); Some(self.cmd(&name)) } } else if target.arch == "wasm32" || target.arch == "wasm64" { // Formally speaking one should be able to use this approach, // parsing -print-search-dirs output, to cover all clang targets, // including Android SDKs and other cross-compilation scenarios... // And even extend it to gcc targets by searching for "ar" instead // of "llvm-ar"... let compiler = self.get_base_compiler().ok()?; if compiler.is_like_clang() { name = format!("llvm-{tool}").into(); self.search_programs(&compiler.path, &name, &self.cargo_output) .map(|name| self.cmd(name)) } else { None } } else { None } }); let tool = match tool_opt { Some(t) => t, None => { if target.os == "android" { name = format!("llvm-{tool}").into(); match Command::new(&name).arg("--version").status() { Ok(status) if status.success() => (), _ => { // FIXME: Use parsed target. let raw_target = self.get_raw_target()?; name = format!("{}-{}", raw_target.replace("armv7", "arm"), tool).into() } } self.cmd(&name) } else if target.env == "msvc" { // NOTE: There isn't really a ranlib on msvc, so arguably we should return // `None` somehow here. But in general, callers will already have to be aware // of not running ranlib on Windows anyway, so it feels okay to return lib.exe // here. let compiler = self.get_base_compiler()?; let lib = if compiler.family == (ToolFamily::Msvc { clang_cl: true }) { self.search_programs( &compiler.path, Path::new("llvm-lib"), &self.cargo_output, ) .or_else(|| { // See if there is 'llvm-lib' next to 'clang-cl' if let Some(mut cmd) = self.which(&compiler.path, None) { cmd.pop(); cmd.push("llvm-lib"); self.which(&cmd, None) } else { None } }) } else { None }; if let Some(lib) = lib { name = lib; self.cmd(&name) } else { name = PathBuf::from("lib.exe"); let mut cmd = match self.find_msvc_tools_find(&target, "lib.exe") { Some(t) => t, None => self.cmd("lib.exe"), }; if target.full_arch == "arm64ec" { cmd.arg("/machine:arm64ec"); } cmd } } else if target.os == "illumos" { // The default 'ar' on illumos uses a non-standard flags, // but the OS comes bundled with a GNU-compatible variant. // // Use the GNU-variant to match other Unix systems. name = format!("g{tool}").into(); self.cmd(&name) } else if target.os == "vxworks" { name = format!("wr-{tool}").into(); self.cmd(&name) } else if target.os == "nto" { // Ref: https://www.qnx.com/developers/docs/8.0/com.qnx.doc.neutrino.utilities/topic/a/ar.html name = match target.full_arch { "i586" => format!("ntox86-{tool}").into(), "x86" | "aarch64" | "x86_64" => { format!("nto{}-{}", target.arch, tool).into() } _ => { return Err(Error::new( ErrorKind::InvalidTarget, format!("Unknown architecture for Neutrino QNX: {}", target.arch), )) } }; self.cmd(&name) } else if self.get_is_cross_compile()? { match self.prefix_for_target(&self.get_raw_target()?) { Some(prefix) => { // GCC uses $target-gcc-ar, whereas binutils uses $target-ar -- try both. // Prefer -ar if it exists, as builds of `-gcc-ar` have been observed to be // outright broken (such as when targeting freebsd with `--disable-lto` // toolchain where the archiver attempts to load the LTO plugin anyway but // fails to find one). // // The same applies to ranlib. let chosen = ["", "-gcc"] .iter() .filter_map(|infix| { let target_p = format!("{prefix}{infix}-{tool}"); let status = Command::new(&target_p) .arg("--version") .stdin(Stdio::null()) .stdout(Stdio::null()) .stderr(Stdio::null()) .status() .ok()?; status.success().then_some(target_p) }) .next() .unwrap_or_else(|| tool.to_string()); name = chosen.into(); self.cmd(&name) } None => { name = tool.into(); self.cmd(&name) } } } else { name = tool.into(); self.cmd(&name) } } }; Ok((tool, name)) } // FIXME: Use parsed target instead of raw target. fn prefix_for_target(&self, target: &str) -> Option> { // CROSS_COMPILE is of the form: "arm-linux-gnueabi-" self.get_env("CROSS_COMPILE") .as_deref() .map(|s| s.to_string_lossy().trim_end_matches('-').to_owned()) .map(Cow::Owned) .or_else(|| { // Put aside RUSTC_LINKER's prefix to be used as second choice, after CROSS_COMPILE cargo_env_var_os("RUSTC_LINKER").and_then(|var| { var.to_string_lossy() .strip_suffix("-gcc") .map(str::to_string) .map(Cow::Owned) }) }) .or_else(|| { match target { // Note: there is no `aarch64-pc-windows-gnu` target, only `-gnullvm` "aarch64-pc-windows-gnullvm" => Some("aarch64-w64-mingw32"), "aarch64-uwp-windows-gnu" => Some("aarch64-w64-mingw32"), "aarch64-unknown-helenos" => Some("aarch64-helenos"), "aarch64-unknown-linux-gnu" => Some("aarch64-linux-gnu"), "aarch64-unknown-linux-musl" => Some("aarch64-linux-musl"), "aarch64-unknown-netbsd" => Some("aarch64--netbsd"), "arm-unknown-linux-gnueabi" => Some("arm-linux-gnueabi"), "armv4t-unknown-linux-gnueabi" => Some("arm-linux-gnueabi"), "armv5te-unknown-helenos-eabi" => Some("arm-helenos"), "armv5te-unknown-linux-gnueabi" => Some("arm-linux-gnueabi"), "armv5te-unknown-linux-musleabi" => Some("arm-linux-gnueabi"), "arm-unknown-linux-gnueabihf" => Some("arm-linux-gnueabihf"), "arm-unknown-linux-musleabi" => Some("arm-linux-musleabi"), "arm-unknown-linux-musleabihf" => Some("arm-linux-musleabihf"), "arm-unknown-netbsd-eabi" => Some("arm--netbsdelf-eabi"), "armv6-unknown-netbsd-eabihf" => Some("armv6--netbsdelf-eabihf"), "armv7-unknown-linux-gnueabi" => Some("arm-linux-gnueabi"), "armv7-unknown-linux-gnueabihf" => Some("arm-linux-gnueabihf"), "armv7-unknown-linux-musleabihf" => Some("arm-linux-musleabihf"), "armv7neon-unknown-linux-gnueabihf" => Some("arm-linux-gnueabihf"), "armv7neon-unknown-linux-musleabihf" => Some("arm-linux-musleabihf"), "thumbv7-unknown-linux-gnueabihf" => Some("arm-linux-gnueabihf"), "thumbv7-unknown-linux-musleabihf" => Some("arm-linux-musleabihf"), "thumbv7neon-unknown-linux-gnueabihf" => Some("arm-linux-gnueabihf"), "thumbv7neon-unknown-linux-musleabihf" => Some("arm-linux-musleabihf"), "armv7-unknown-netbsd-eabihf" => Some("armv7--netbsdelf-eabihf"), "hexagon-unknown-linux-musl" => Some("hexagon-linux-musl"), "i586-unknown-linux-musl" => Some("musl"), "i686-pc-windows-gnu" => Some("i686-w64-mingw32"), "i686-pc-windows-gnullvm" => Some("i686-w64-mingw32"), "i686-uwp-windows-gnu" => Some("i686-w64-mingw32"), "i686-unknown-helenos" => Some("i686-helenos"), "i686-unknown-linux-gnu" => self.find_working_gnu_prefix(&[ "i686-linux-gnu", "x86_64-linux-gnu", // transparently support gcc-multilib ]), // explicit None if not found, so caller knows to fall back "i686-unknown-linux-musl" => Some("musl"), "i686-unknown-netbsd" => Some("i486--netbsdelf"), "loongarch64-unknown-linux-gnu" => Some("loongarch64-linux-gnu"), "m68k-unknown-linux-gnu" => Some("m68k-linux-gnu"), "mips-unknown-linux-gnu" => Some("mips-linux-gnu"), "mips-unknown-linux-musl" => Some("mips-linux-musl"), "mipsel-unknown-linux-gnu" => Some("mipsel-linux-gnu"), "mipsel-unknown-linux-musl" => Some("mipsel-linux-musl"), "mips64-unknown-linux-gnuabi64" => Some("mips64-linux-gnuabi64"), "mips64el-unknown-linux-gnuabi64" => Some("mips64el-linux-gnuabi64"), "mipsisa32r6-unknown-linux-gnu" => Some("mipsisa32r6-linux-gnu"), "mipsisa32r6el-unknown-linux-gnu" => Some("mipsisa32r6el-linux-gnu"), "mipsisa64r6-unknown-linux-gnuabi64" => Some("mipsisa64r6-linux-gnuabi64"), "mipsisa64r6el-unknown-linux-gnuabi64" => Some("mipsisa64r6el-linux-gnuabi64"), "powerpc-unknown-helenos" => Some("ppc-helenos"), "powerpc-unknown-linux-gnu" => Some("powerpc-linux-gnu"), "powerpc-unknown-linux-gnuspe" => Some("powerpc-linux-gnuspe"), "powerpc-unknown-netbsd" => Some("powerpc--netbsd"), "powerpc64-unknown-linux-gnu" => Some("powerpc64-linux-gnu"), "powerpc64le-unknown-linux-gnu" => Some("powerpc64le-linux-gnu"), "riscv32i-unknown-none-elf" => self.find_working_gnu_prefix(&[ "riscv32-unknown-elf", "riscv64-unknown-elf", "riscv-none-embed", ]), "riscv32im-unknown-none-elf" => self.find_working_gnu_prefix(&[ "riscv32-unknown-elf", "riscv64-unknown-elf", "riscv-none-embed", ]), "riscv32imac-esp-espidf" => Some("riscv32-esp-elf"), "riscv32imac-unknown-none-elf" => self.find_working_gnu_prefix(&[ "riscv32-unknown-elf", "riscv64-unknown-elf", "riscv-none-embed", ]), "riscv32imafc-unknown-none-elf" => self.find_working_gnu_prefix(&[ "riscv32-unknown-elf", "riscv64-unknown-elf", "riscv-none-embed", ]), "riscv32imac-unknown-xous-elf" => self.find_working_gnu_prefix(&[ "riscv32-unknown-elf", "riscv64-unknown-elf", "riscv-none-embed", ]), "riscv32imc-esp-espidf" => Some("riscv32-esp-elf"), "riscv32imc-unknown-none-elf" => self.find_working_gnu_prefix(&[ "riscv32-unknown-elf", "riscv64-unknown-elf", "riscv-none-embed", ]), "riscv64gc-unknown-none-elf" => self.find_working_gnu_prefix(&[ "riscv64-unknown-elf", "riscv32-unknown-elf", "riscv-none-embed", ]), "riscv64imac-unknown-none-elf" => self.find_working_gnu_prefix(&[ "riscv64-unknown-elf", "riscv32-unknown-elf", "riscv-none-embed", ]), "riscv64gc-unknown-linux-gnu" => Some("riscv64-linux-gnu"), "riscv64a23-unknown-linux-gnu" => Some("riscv64-linux-gnu"), "riscv32gc-unknown-linux-gnu" => Some("riscv32-linux-gnu"), "riscv64gc-unknown-linux-musl" => Some("riscv64-linux-musl"), "riscv32gc-unknown-linux-musl" => Some("riscv32-linux-musl"), "riscv64gc-unknown-netbsd" => Some("riscv64--netbsd"), "s390x-unknown-linux-gnu" => Some("s390x-linux-gnu"), "sparc-unknown-linux-gnu" => Some("sparc-linux-gnu"), "sparc64-unknown-helenos" => Some("sparc64-helenos"), "sparc64-unknown-linux-gnu" => Some("sparc64-linux-gnu"), "sparc64-unknown-netbsd" => Some("sparc64--netbsd"), "sparcv9-sun-solaris" => Some("sparcv9-sun-solaris"), "armv7a-none-eabi" => Some("arm-none-eabi"), "armv7a-none-eabihf" => Some("arm-none-eabi"), "armebv7r-none-eabi" => Some("arm-none-eabi"), "armebv7r-none-eabihf" => Some("arm-none-eabi"), "armv7r-none-eabi" => Some("arm-none-eabi"), "armv7r-none-eabihf" => Some("arm-none-eabi"), "armv8r-none-eabihf" => Some("arm-none-eabi"), "thumbv6m-none-eabi" => Some("arm-none-eabi"), "thumbv7em-none-eabi" => Some("arm-none-eabi"), "thumbv7em-none-eabihf" => Some("arm-none-eabi"), "thumbv7m-none-eabi" => Some("arm-none-eabi"), "thumbv8m.base-none-eabi" => Some("arm-none-eabi"), "thumbv8m.main-none-eabi" => Some("arm-none-eabi"), "thumbv8m.main-none-eabihf" => Some("arm-none-eabi"), "x86_64-pc-windows-gnu" => Some("x86_64-w64-mingw32"), "x86_64-pc-windows-gnullvm" => Some("x86_64-w64-mingw32"), "x86_64-uwp-windows-gnu" => Some("x86_64-w64-mingw32"), "x86_64-rumprun-netbsd" => Some("x86_64-rumprun-netbsd"), "x86_64-unknown-helenos" => Some("amd64-helenos"), "x86_64-unknown-linux-gnu" => self.find_working_gnu_prefix(&[ "x86_64-linux-gnu", // rustfmt wrap ]), // explicit None if not found, so caller knows to fall back "x86_64-unknown-linux-musl" => { self.find_working_gnu_prefix(&["x86_64-linux-musl", "musl"]) } "x86_64-unknown-netbsd" => Some("x86_64--netbsd"), "xtensa-esp32-espidf" | "xtensa-esp32-none-elf" | "xtensa-esp32s2-espidf" | "xtensa-esp32s2-none-elf" | "xtensa-esp32s3-espidf" | "xtensa-esp32s3-none-elf" => Some("xtensa-esp-elf"), _ => None, } .map(Cow::Borrowed) }) } /// Some platforms have multiple, compatible, canonical prefixes. Look through /// each possible prefix for a compiler that exists and return it. The prefixes /// should be ordered from most-likely to least-likely. fn find_working_gnu_prefix(&self, prefixes: &[&'static str]) -> Option<&'static str> { let suffix = if self.cpp { "-g++" } else { "-gcc" }; let extension = std::env::consts::EXE_SUFFIX; // Loop through PATH entries searching for each toolchain. This ensures that we // are more likely to discover the toolchain early on, because chances are good // that the desired toolchain is in one of the higher-priority paths. self.get_env("PATH") .as_ref() .and_then(|path_entries| { env::split_paths(path_entries).find_map(|path_entry| { for prefix in prefixes { let target_compiler = format!("{prefix}{suffix}{extension}"); if path_entry.join(&target_compiler).exists() { return Some(prefix); } } None }) }) .copied() // If no toolchain was found, provide the first toolchain that was passed in. // This toolchain has been shown not to exist, however it will appear in the // error that is shown to the user which should make it easier to search for // where it should be obtained. .or_else(|| prefixes.first().copied()) } fn get_target(&self) -> Result, Error> { match &self.target { Some(t) if Some(OsStr::new(&**t)) != cargo_env_var_os("TARGET").as_deref() => { TargetInfo::from_rustc_target(t) } // Fetch target information from environment if not set, or if the // target was the same as the TARGET environment variable, in // case the user did `build.target(&env::var("TARGET").unwrap())`. _ => self .build_cache .target_info_parser .parse_from_cargo_environment_variables(), } } fn get_raw_target(&self) -> Result, Error> { match &self.target { Some(t) => Ok(Cow::Borrowed(t)), None => cargo_env_var("TARGET").map(Cow::Owned), } } fn get_is_cross_compile(&self) -> Result { let target = self.get_raw_target()?; let host: Cow<'_, str> = match &self.host { Some(h) => Cow::Borrowed(h), None => Cow::Owned(cargo_env_var("HOST")?), }; Ok(host != target) } fn get_opt_level(&self) -> Result, Error> { match &self.opt_level { Some(ol) => Ok(Cow::Borrowed(ol)), None => cargo_env_var("OPT_LEVEL").map(Cow::Owned), } } /// Returns true if *any* debug info is enabled. /// /// [`get_debug_str`] provides more detail. fn get_debug(&self) -> bool { match self.get_debug_str() { Err(_) => false, Ok(d) => match &*d { // From https://doc.rust-lang.org/cargo/reference/profiles.html#debug "" | "0" | "false" | "none" => false, _ => true, }, } } fn get_debug_str(&self) -> Result, Error> { match &self.debug { Some(d) => Ok(Cow::Borrowed(d)), None => cargo_env_var("DEBUG").map(Cow::Owned), } } fn get_shell_escaped_flags(&self) -> bool { self.shell_escaped_flags .unwrap_or_else(|| self.get_env_boolean("CC_SHELL_ESCAPED_FLAGS")) } fn get_dwarf_version(&self) -> Option { // Tentatively matches the DWARF version defaults as of rustc 1.62. let target = self.get_target().ok()?; if matches!( target.os, "android" | "dragonfly" | "freebsd" | "netbsd" | "openbsd" ) || target.vendor == "apple" || (target.os == "windows" && target.env == "gnu") { Some(2) } else if target.os == "linux" { Some(4) } else { None } } fn get_force_frame_pointer(&self) -> bool { self.force_frame_pointer.unwrap_or_else(|| self.get_debug()) } fn get_out_dir(&self) -> Result, Error> { match &self.out_dir { Some(p) => Ok(Cow::Borrowed(&**p)), None => cargo_env_var_os("OUT_DIR") .map(PathBuf::from) .map(Cow::Owned) .ok_or_else(|| { Error::new( ErrorKind::EnvVarNotFound, "Environment variable OUT_DIR not defined.", ) }), } } /// Look up an environment variable, and tell Cargo that we used it. fn get_env(&self, v: &str) -> Option { // Excluding `PATH` prevents spurious rebuilds on Windows, see // for details. if self.emit_rerun_if_env_changed && v != "PATH" { self.cargo_output .print_metadata(&format_args!("cargo:rerun-if-env-changed={v}")); } #[allow(clippy::disallowed_methods)] // We emit rerun-if-env-changed above let r = env::var_os(v); self.cargo_output.print_metadata(&format_args!( "{} = {}", v, OptionOsStrDisplay(r.as_deref()) )); r } /// Look up an environment variable that's allowed to be overwritten by /// [`Build::env`]. /// /// This is useful for environment variables that the compiler could /// reasonably read, such as `SDKROOT` and `WASI_SDK_PATH` - for these, we /// generally want to allow build scripts to overwrite them. /// /// On the other hand, we don't want to allow overwriting environment /// variables that are `CC`-specific such as `CC_FORCE_DISABLE` /// (`Build::env` applies to child processes, not to `cc` itself). fn get_env_overridable(&self, key: &str) -> Option> { // Try to look up in overrides first. if let Some((_key, val)) = self.env.iter().find(|(k, _)| k.as_ref() == key) { return Some(Cow::Borrowed(&**val)); } // If not found in overrides, look up from environment. self.get_env(key).map(Cow::Owned) } /// Get boolean flag that is either true or false. /// /// Used for `CC_*`-style flags. fn get_env_boolean(&self, key: &str) -> bool { match self.get_env(key) { // Set -> `true`, unless set to `""`, `"0"`, `"no"` `"false"` Some(s) => &*s != "0" && &*s != "false" && &*s != "no" && !s.is_empty(), // Not set -> default to `false`. None => false, } } /// The list of environment variables to check for a given env, in order of priority. fn target_envs(&self, env: &str) -> Result<[String; 4], Error> { let target = self.get_raw_target()?; let kind = if self.get_is_cross_compile()? { "TARGET" } else { "HOST" }; let target_u = target.replace(['-', '.'], "_"); Ok([ format!("{env}_{target}"), format!("{env}_{target_u}"), format!("{kind}_{env}"), env.to_string(), ]) } /// Get a single-valued environment variable with target variants. fn getenv_with_target_prefixes(&self, env: &str) -> Result { // Take from first environment variable in the environment. let res = self .target_envs(env)? .iter() .filter_map(|env| self.get_env(env)) .next(); match res { Some(res) => Ok(res), None => Err(Error::new( ErrorKind::EnvVarNotFound, format!("could not find environment variable {env}"), )), } } /// Get values from CFLAGS-style environment variable. fn envflags(&self, env: &str) -> Result>, Error> { // Collect from all environment variables, in reverse order as in // `getenv_with_target_prefixes` precedence (so that `CFLAGS_$TARGET` // can override flags in `TARGET_CFLAGS`, which overrides those in // `CFLAGS`). let mut any_set = false; let mut res = vec![]; for env in self.target_envs(env)?.iter().rev() { if let Some(var) = self.get_env(env) { any_set = true; let var = var.to_string_lossy(); if self.get_shell_escaped_flags() { res.extend(Shlex::new(&var)); } else { res.extend(var.split_ascii_whitespace().map(ToString::to_string)); } } } Ok(if any_set { Some(res) } else { None }) } /// Returns true if `cc` has been disabled by `CC_FORCE_DISABLE`. fn is_disabled(&self) -> bool { self.get_env_boolean("CC_FORCE_DISABLE") } fn fix_env_for_apple_os(&self, cmd: &mut Command) -> Result<(), Error> { let target = self.get_target()?; if cfg!(target_os = "macos") && target.os == "macos" { // Additionally, `IPHONEOS_DEPLOYMENT_TARGET` must not be set when using the Xcode linker at // "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld", // although this is apparently ignored when using the linker at "/usr/bin/ld". cmd.env_remove("IPHONEOS_DEPLOYMENT_TARGET"); } Ok(()) } fn apple_sdk_root_inner(&self, sdk: &str) -> Result, Error> { // Code copied from rustc's compiler/rustc_codegen_ssa/src/back/link.rs. if let Some(sdkroot) = self.get_env_overridable("SDKROOT") { let p = Path::new(&sdkroot); let does_sdkroot_contain = |strings: &[&str]| { let sdkroot_str = p.to_string_lossy(); strings.iter().any(|s| sdkroot_str.contains(s)) }; match sdk { // Ignore `SDKROOT` if it's clearly set for the wrong platform. "appletvos" if does_sdkroot_contain(&["TVSimulator.platform", "MacOSX.platform"]) => {} "appletvsimulator" if does_sdkroot_contain(&["TVOS.platform", "MacOSX.platform"]) => {} "iphoneos" if does_sdkroot_contain(&["iPhoneSimulator.platform", "MacOSX.platform"]) => {} "iphonesimulator" if does_sdkroot_contain(&["iPhoneOS.platform", "MacOSX.platform"]) => {} "macosx10.15" if does_sdkroot_contain(&["iPhoneOS.platform", "iPhoneSimulator.platform"]) => { } "watchos" if does_sdkroot_contain(&["WatchSimulator.platform", "MacOSX.platform"]) => {} "watchsimulator" if does_sdkroot_contain(&["WatchOS.platform", "MacOSX.platform"]) => {} "xros" if does_sdkroot_contain(&["XRSimulator.platform", "MacOSX.platform"]) => {} "xrsimulator" if does_sdkroot_contain(&["XROS.platform", "MacOSX.platform"]) => {} // Ignore `SDKROOT` if it's not a valid path. _ if !p.is_absolute() || p == Path::new("/") || !p.exists() => {} _ => return Ok(sdkroot), } } let sdk_path = run_output( self.cmd("xcrun") .arg("--show-sdk-path") .arg("--sdk") .arg(sdk), &self.cargo_output, )?; let sdk_path = match String::from_utf8(sdk_path) { Ok(p) => p, Err(_) => { return Err(Error::new( ErrorKind::IOError, "Unable to determine Apple SDK path.", )); } }; Ok(Cow::Owned(sdk_path.trim().into())) } fn apple_sdk_root(&self, target: &TargetInfo<'_>) -> Result, Error> { let sdk = target.apple_sdk_name(); if let Some(ret) = self .build_cache .apple_sdk_root_cache .read() .expect("apple_sdk_root_cache lock failed") .get(sdk) .cloned() { return Ok(ret); } let sdk_path: Arc = self.apple_sdk_root_inner(sdk)?.into(); self.build_cache .apple_sdk_root_cache .write() .expect("apple_sdk_root_cache lock failed") .insert(sdk.into(), sdk_path.clone()); Ok(sdk_path) } fn apple_deployment_target(&self, target: &TargetInfo<'_>) -> Arc { let sdk = target.apple_sdk_name(); if let Some(ret) = self .build_cache .apple_versions_cache .read() .expect("apple_versions_cache lock failed") .get(sdk) .cloned() { return ret; } let default_deployment_from_sdk = || -> Option> { let version = run_output( self.cmd("xcrun") .arg("--show-sdk-version") .arg("--sdk") .arg(sdk), &self.cargo_output, ) .ok()?; Some(Arc::from(std::str::from_utf8(&version).ok()?.trim())) }; let deployment_from_env = |name: &str| -> Option> { self.get_env_overridable(name)?.to_str().map(Arc::from) }; // Determines if the acquired deployment target is too low to support modern C++ on some Apple platform. // // A long time ago they used libstdc++, but since macOS 10.9 and iOS 7 libc++ has been the library the SDKs provide to link against. // If a `cc`` config wants to use C++, we round up to these versions as the baseline. let maybe_cpp_version_baseline = |deployment_target_ver: Arc| -> Option> { if !self.cpp { return Some(deployment_target_ver); } let mut deployment_target = deployment_target_ver .split('.') .map(|v| v.parse::().expect("integer version")); match target.os { "macos" => { let major = deployment_target.next().unwrap_or(0); let minor = deployment_target.next().unwrap_or(0); // If below 10.9, we ignore it and let the SDK's target definitions handle it. if major == 10 && minor < 9 { self.cargo_output.print_warning(&format_args!( "macOS deployment target ({deployment_target_ver}) too low, it will be increased" )); return None; } } "ios" => { let major = deployment_target.next().unwrap_or(0); // If below 10.7, we ignore it and let the SDK's target definitions handle it. if major < 7 { self.cargo_output.print_warning(&format_args!( "iOS deployment target ({deployment_target_ver}) too low, it will be increased" )); return None; } } // watchOS, tvOS, visionOS, and others are all new enough that libc++ is their baseline. _ => {} } // If the deployment target met or exceeded the C++ baseline Some(deployment_target_ver) }; // The hardcoded minimums here are subject to change in a future compiler release, // and only exist as last resort fallbacks. Don't consider them stable. // `cc` doesn't use rustc's `--print deployment-target`` because the compiler's defaults // don't align well with Apple's SDKs and other third-party libraries that require ~generally~ higher // deployment targets. rustc isn't interested in those by default though so its fine to be different here. // // If no explicit target is passed, `cc` defaults to the current Xcode SDK's `DefaultDeploymentTarget` for better // compatibility. This is also the crate's historical behavior and what has become a relied-on value. // // The ordering of env -> XCode SDK -> old rustc defaults is intentional for performance when using // an explicit target. let version: Arc = match target.os { "macos" => deployment_from_env("MACOSX_DEPLOYMENT_TARGET") .and_then(maybe_cpp_version_baseline) .or_else(default_deployment_from_sdk) .unwrap_or_else(|| { if target.arch == "aarch64" { "11.0".into() } else { let default: Arc = Arc::from("10.7"); maybe_cpp_version_baseline(default.clone()).unwrap_or(default) } }), "ios" => deployment_from_env("IPHONEOS_DEPLOYMENT_TARGET") .and_then(maybe_cpp_version_baseline) .or_else(default_deployment_from_sdk) .unwrap_or_else(|| "7.0".into()), "watchos" => deployment_from_env("WATCHOS_DEPLOYMENT_TARGET") .or_else(default_deployment_from_sdk) .unwrap_or_else(|| "5.0".into()), "tvos" => deployment_from_env("TVOS_DEPLOYMENT_TARGET") .or_else(default_deployment_from_sdk) .unwrap_or_else(|| "9.0".into()), "visionos" => deployment_from_env("XROS_DEPLOYMENT_TARGET") .or_else(default_deployment_from_sdk) .unwrap_or_else(|| "1.0".into()), os => unreachable!("unknown Apple OS: {}", os), }; self.build_cache .apple_versions_cache .write() .expect("apple_versions_cache lock failed") .insert(sdk.into(), version.clone()); version } fn wasm_musl_sysroot(&self) -> Result { if let Some(musl_sysroot_path) = self.get_env("WASM_MUSL_SYSROOT") { Ok(musl_sysroot_path) } else { Err(Error::new( ErrorKind::EnvVarNotFound, "Environment variable WASM_MUSL_SYSROOT not defined for wasm32. Download sysroot from GitHub & setup environment variable MUSL_SYSROOT targeting the folder.", )) } } fn wasi_sysroot(&self) -> Result { if let Some(wasi_sysroot_path) = self.get_env("WASI_SYSROOT") { Ok(wasi_sysroot_path) } else { Err(Error::new( ErrorKind::EnvVarNotFound, "Environment variable WASI_SYSROOT not defined. Download sysroot from GitHub & setup environment variable WASI_SYSROOT targeting the folder.", )) } } fn cuda_file_count(&self) -> usize { self.files .iter() .filter(|file| file.extension() == Some(OsStr::new("cu"))) .count() } fn which(&self, tool: &Path, path_entries: Option<&OsStr>) -> Option { // Loop through PATH entries searching for the |tool|. let find_exe_in_path = |path_entries: &OsStr| -> Option { env::split_paths(path_entries).find_map(|path_entry| check_exe(path_entry.join(tool))) }; // If |tool| is not just one "word," assume it's an actual path... if tool.components().count() > 1 { check_exe(PathBuf::from(tool)) } else { path_entries .and_then(find_exe_in_path) .or_else(|| find_exe_in_path(&self.get_env("PATH")?)) } } /// search for |prog| on 'programs' path in '|cc| --print-search-dirs' output fn search_programs( &self, cc: &Path, prog: &Path, cargo_output: &CargoOutput, ) -> Option { let search_dirs = run_output( self.cmd(cc).arg("--print-search-dirs"), // this doesn't concern the compilation so we always want to show warnings. cargo_output, ) .ok()?; // clang driver appears to be forcing UTF-8 output even on Windows, // hence from_utf8 is assumed to be usable in all cases. let search_dirs = std::str::from_utf8(&search_dirs).ok()?; for dirs in search_dirs.split(['\r', '\n']) { if let Some(path) = dirs.strip_prefix("programs: =") { return self.which(prog, Some(OsStr::new(path))); } } None } fn find_msvc_tools_find(&self, target: &TargetInfo<'_>, tool: &str) -> Option { self.find_msvc_tools_find_tool(target, tool) .map(|c| c.to_command()) } fn find_msvc_tools_find_tool(&self, target: &TargetInfo<'_>, tool: &str) -> Option { struct BuildEnvGetter<'s>(&'s Build); impl ::find_msvc_tools::EnvGetter for BuildEnvGetter<'_> { fn get_env(&self, name: &str) -> Option<::find_msvc_tools::Env> { // TODO: Should we allow overriding these with `Build::env`? // self.0.get_env(name).map(::find_msvc_tools::Env::Owned) } } if target.env != "msvc" { return None; } ::find_msvc_tools::find_tool_with_env(target.full_arch, tool, &BuildEnvGetter(self)) .map(Tool::from_find_msvc_tools) } /// Compiling for WASI targets typically uses the [wasi-sdk] project and /// installations of wasi-sdk are typically indicated with the /// `WASI_SDK_PATH` environment variable. Check to see if that environment /// variable exists, and check to see if an appropriate compiler is located /// there. If that all passes then use that compiler by default, but /// otherwise fall back to whatever the clang default is since gcc doesn't /// have support for compiling to wasm. /// /// [wasi-sdk]: https://github.com/WebAssembly/wasi-sdk fn autodetect_wasi_compiler(&self, raw_target: &str, clang: &str) -> PathBuf { if let Some(path) = self.get_env_overridable("WASI_SDK_PATH") { let target_clang = Path::new(&path) .join("bin") .join(format!("{raw_target}-clang")); if let Some(path) = self.which(&target_clang, None) { return path; } } clang.into() } } impl Default for Build { fn default() -> Build { Build::new() } } fn fail(s: &str) -> ! { eprintln!("\n\nerror occurred in cc-rs: {s}\n\n"); std::process::exit(1); } // Use by default minimum available API level // See note about naming here // https://android.googlesource.com/platform/ndk/+/refs/heads/ndk-release-r21/docs/BuildSystemMaintainers.md#Clang static NEW_STANDALONE_ANDROID_COMPILERS: [&str; 4] = [ "aarch64-linux-android21-clang", "armv7a-linux-androideabi16-clang", "i686-linux-android16-clang", "x86_64-linux-android21-clang", ]; // New "standalone" C/C++ cross-compiler executables from recent Android NDK // are just shell scripts that call main clang binary (from Android NDK) with // proper `--target` argument. // // For example, armv7a-linux-androideabi16-clang passes // `--target=armv7a-linux-androideabi16` to clang. // So to construct proper command line check if // `--target` argument would be passed or not to clang fn android_clang_compiler_uses_target_arg_internally(clang_path: &Path) -> bool { if let Some(filename) = clang_path.file_name() { if let Some(filename_str) = filename.to_str() { if let Some(idx) = filename_str.rfind('-') { return filename_str.split_at(idx).0.contains("android"); } } } false } fn is_llvm_mingw_wrapper(clang_path: &Path) -> bool { if let Some(filename) = clang_path .file_name() .and_then(|file_name| file_name.to_str()) { filename.ends_with("-w64-mingw32-clang") || filename.ends_with("-w64-mingw32-clang++") } else { false } } // FIXME: Use parsed target. fn autodetect_android_compiler(raw_target: &str, gnu: &str, clang: &str) -> PathBuf { let new_clang_key = match raw_target { "aarch64-linux-android" => Some("aarch64"), "armv7-linux-androideabi" => Some("armv7a"), "i686-linux-android" => Some("i686"), "x86_64-linux-android" => Some("x86_64"), _ => None, }; let new_clang = new_clang_key .map(|key| { NEW_STANDALONE_ANDROID_COMPILERS .iter() .find(|x| x.starts_with(key)) }) .unwrap_or(None); if let Some(new_clang) = new_clang { if Command::new(new_clang).output().is_ok() { return (*new_clang).into(); } } let target = raw_target .replace("armv7neon", "arm") .replace("armv7", "arm") .replace("thumbv7neon", "arm") .replace("thumbv7", "arm"); let gnu_compiler = format!("{target}-{gnu}"); let clang_compiler = format!("{target}-{clang}"); // On Windows, the Android clang compiler is provided as a `.cmd` file instead // of a `.exe` file. `std::process::Command` won't run `.cmd` files unless the // `.cmd` is explicitly appended to the command name, so we do that here. let clang_compiler_cmd = format!("{target}-{clang}.cmd"); // Check if gnu compiler is present // if not, use clang if Command::new(&gnu_compiler).output().is_ok() { gnu_compiler } else if cfg!(windows) && Command::new(&clang_compiler_cmd).output().is_ok() { clang_compiler_cmd } else { clang_compiler } .into() } // Rust and clang/cc don't agree on how to name the target. fn map_darwin_target_from_rust_to_compiler_architecture<'a>(target: &TargetInfo<'a>) -> &'a str { match target.full_arch { "aarch64" => "arm64", "arm64_32" => "arm64_32", "arm64e" => "arm64e", "armv7k" => "armv7k", "armv7s" => "armv7s", "i386" => "i386", "i686" => "i386", "powerpc" => "ppc", "powerpc64" => "ppc64", "x86_64" => "x86_64", "x86_64h" => "x86_64h", arch => arch, } } fn is_arm(target: &TargetInfo<'_>) -> bool { matches!(target.arch, "aarch64" | "arm64ec" | "arm") } #[derive(Clone, Copy, PartialEq)] enum AsmFileExt { /// `.asm` files. On MSVC targets, we assume these should be passed to MASM /// (`ml{,64}.exe`). DotAsm, /// `.s` or `.S` files, which do not have the special handling on MSVC targets. DotS, } impl AsmFileExt { fn from_path(file: &Path) -> Option { if let Some(ext) = file.extension() { if let Some(ext) = ext.to_str() { let ext = ext.to_lowercase(); match &*ext { "asm" => return Some(AsmFileExt::DotAsm), "s" => return Some(AsmFileExt::DotS), _ => return None, } } } None } } fn check_exe(mut exe: PathBuf) -> Option { let exe_ext = std::env::consts::EXE_EXTENSION; let check = exe.exists() || (!exe_ext.is_empty() && exe.set_extension(exe_ext) && exe.exists()); check.then_some(exe) } #[cfg(test)] mod tests { use super::*; #[test] fn test_android_clang_compiler_uses_target_arg_internally() { for version in 16..21 { assert!(android_clang_compiler_uses_target_arg_internally( &PathBuf::from(format!("armv7a-linux-androideabi{}-clang", version)) )); assert!(android_clang_compiler_uses_target_arg_internally( &PathBuf::from(format!("armv7a-linux-androideabi{}-clang++", version)) )); } assert!(!android_clang_compiler_uses_target_arg_internally( &PathBuf::from("clang-i686-linux-android") )); assert!(!android_clang_compiler_uses_target_arg_internally( &PathBuf::from("clang") )); assert!(!android_clang_compiler_uses_target_arg_internally( &PathBuf::from("clang++") )); } } cc-1.2.60/src/parallel/async_executor.rs000064400000000000000000000067761046102023000163030ustar 00000000000000use std::{ cell::Cell, future::Future, pin::Pin, ptr, task::{Context, Poll, RawWaker, RawWakerVTable, Waker}, thread, time::Duration, }; use crate::Error; const NOOP_WAKER_VTABLE: RawWakerVTable = RawWakerVTable::new( // Cloning just returns a new no-op raw waker |_| NOOP_RAW_WAKER, // `wake` does nothing |_| {}, // `wake_by_ref` does nothing |_| {}, // Dropping does nothing as we don't allocate anything |_| {}, ); const NOOP_RAW_WAKER: RawWaker = RawWaker::new(ptr::null(), &NOOP_WAKER_VTABLE); #[derive(Default)] pub(crate) struct YieldOnce(bool); impl Future for YieldOnce { type Output = (); fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<()> { let flag = &mut std::pin::Pin::into_inner(self).0; if !*flag { *flag = true; Poll::Pending } else { Poll::Ready(()) } } } /// Execute the futures and return when they are all done. /// /// Here we use our own homebrew async executor since cc is used in the build /// script of many popular projects, pulling in additional dependencies would /// significantly slow down its compilation. pub(crate) fn block_on( mut fut1: Fut1, mut fut2: Fut2, has_made_progress: &Cell, ) -> Result<(), Error> where Fut1: Future>, Fut2: Future>, { // Shadows the future so that it can never be moved and is guaranteed // to be pinned. // // The same trick used in `pin!` macro. // // TODO: Once MSRV is bumped to 1.68, replace this with `std::pin::pin!` let mut fut1 = Some(unsafe { Pin::new_unchecked(&mut fut1) }); let mut fut2 = Some(unsafe { Pin::new_unchecked(&mut fut2) }); // TODO: Once `Waker::noop` stablised and our MSRV is bumped to the version // which it is stablised, replace this with `Waker::noop`. let waker = unsafe { Waker::from_raw(NOOP_RAW_WAKER) }; let mut context = Context::from_waker(&waker); let mut backoff_cnt = 0; loop { has_made_progress.set(false); if let Some(fut) = fut2.as_mut() { if let Poll::Ready(res) = fut.as_mut().poll(&mut context) { fut2 = None; res?; } } if let Some(fut) = fut1.as_mut() { if let Poll::Ready(res) = fut.as_mut().poll(&mut context) { fut1 = None; res?; } } if fut1.is_none() && fut2.is_none() { return Ok(()); } if !has_made_progress.get() { if backoff_cnt > 3 { // We have yielded at least three times without making' // any progress, so we will sleep for a while. let duration = Duration::from_millis(100 * (backoff_cnt - 3).min(10)); thread::sleep(duration); } else { // Given that we spawned a lot of compilation tasks, it is unlikely // that OS cannot find other ready task to execute. // // If all of them are done, then we will yield them and spawn more, // or simply return. // // Thus this will not be turned into a busy-wait loop and it will not // waste CPU resource. thread::yield_now(); } } backoff_cnt = if has_made_progress.get() { 0 } else { backoff_cnt + 1 }; } } cc-1.2.60/src/parallel/command_runner.rs000064400000000000000000000137331046102023000162460ustar 00000000000000use std::{ cell::Cell, io::{self, Write as _}, process::{Child, Command}, }; use crate::{ parallel::{ async_executor::{block_on, YieldOnce}, job_token, }, spawn, CargoOutput, Error, ErrorKind, StderrForwarder, }; struct KillOnDrop(Child, StderrForwarder); impl Drop for KillOnDrop { fn drop(&mut self) { let child = &mut self.0; child.kill().ok(); } } fn cell_update(cell: &Cell, f: F) where T: Default, F: FnOnce(T) -> T, { let old = cell.take(); let new = f(old); cell.set(new); } fn try_wait_on_child( cmd: &Command, child: &mut Child, mut stdout: impl io::Write, stderr_forwarder: &mut StderrForwarder, ) -> Result, Error> { stderr_forwarder.forward_available(); match child.try_wait() { Ok(Some(status)) => { stderr_forwarder.forward_all(); let _ = writeln!(stdout, "{}", status); if status.success() { Ok(Some(())) } else { Err(Error::new( ErrorKind::ToolExecError, format!("command did not execute successfully (status code {status}): {cmd:?}"), )) } } Ok(None) => Ok(None), Err(e) => { stderr_forwarder.forward_all(); Err(Error::new( ErrorKind::ToolExecError, format!("failed to wait on spawned child process `{cmd:?}`: {e}"), )) } } } pub(crate) fn run_commands_in_parallel( cargo_output: &CargoOutput, cmds: &mut dyn Iterator>, ) -> Result<(), Error> { // Limit our parallelism globally with a jobserver. let mut tokens = job_token::ActiveJobTokenServer::new(); // When compiling objects in parallel we do a few dirty tricks to speed // things up: // // * First is that we use the `jobserver` crate to limit the parallelism // of this build script. The `jobserver` crate will use a jobserver // configured by Cargo for build scripts to ensure that parallelism is // coordinated across C compilations and Rust compilations. Before we // compile anything we make sure to wait until we acquire a token. // // Note that this jobserver is cached globally so we only used one per // process and only worry about creating it once. // // * Next we use spawn the process to actually compile objects in // parallel after we've acquired a token to perform some work // // With all that in mind we compile all objects in a loop here, after we // acquire the appropriate tokens, Once all objects have been compiled // we wait on all the processes and propagate the results of compilation. let pendings = Cell::new(Vec::<(Command, KillOnDrop, job_token::JobToken)>::new()); let is_disconnected = Cell::new(false); let has_made_progress = Cell::new(false); let wait_future = async { let mut error = None; // Buffer the stdout let mut stdout = io::BufWriter::with_capacity(128, io::stdout()); loop { // If the other end of the pipe is already disconnected, then we're not gonna get any new jobs, // so it doesn't make sense to reuse the tokens; in fact, // releasing them as soon as possible (once we know that the other end is disconnected) is beneficial. // Imagine that the last file built takes an hour to finish; in this scenario, // by not releasing the tokens before that last file is done we would effectively block other processes from // starting sooner - even though we only need one token for that last file, not N others that were acquired. let mut pendings_is_empty = false; cell_update(&pendings, |mut pendings| { // Try waiting on them. pendings.retain_mut(|(cmd, child, _token)| { match try_wait_on_child(cmd, &mut child.0, &mut stdout, &mut child.1) { Ok(Some(())) => { // Task done, remove the entry has_made_progress.set(true); false } Ok(None) => true, // Task still not finished, keep the entry Err(err) => { // Task fail, remove the entry. // Since we can only return one error, log the error to make // sure users always see all the compilation failures. has_made_progress.set(true); if cargo_output.warnings { let _ = writeln!(stdout, "cargo:warning={}", err); } error = Some(err); false } } }); pendings_is_empty = pendings.is_empty(); pendings }); if pendings_is_empty && is_disconnected.get() { break if let Some(err) = error { Err(err) } else { Ok(()) }; } YieldOnce::default().await; } }; let spawn_future = async { for res in cmds { let mut cmd = res?; let token = tokens.acquire().await?; let mut child = spawn(&mut cmd, cargo_output)?; let mut stderr_forwarder = StderrForwarder::new(&mut child); stderr_forwarder.set_non_blocking()?; cell_update(&pendings, |mut pendings| { pendings.push((cmd, KillOnDrop(child, stderr_forwarder), token)); pendings }); has_made_progress.set(true); } is_disconnected.set(true); Ok::<_, Error>(()) }; block_on(wait_future, spawn_future, &has_made_progress) } cc-1.2.60/src/parallel/job_token.rs000064400000000000000000000223641046102023000152110ustar 00000000000000use std::marker::PhantomData; use crate::{utilities::OnceLock, Error}; pub(crate) struct JobToken(PhantomData<()>); impl JobToken { fn new() -> Self { Self(PhantomData) } } impl Drop for JobToken { fn drop(&mut self) { match JobTokenServer::new() { JobTokenServer::Inherited(jobserver) => jobserver.release_token_raw(), JobTokenServer::InProcess(jobserver) => jobserver.release_token_raw(), } } } enum JobTokenServer { Inherited(inherited_jobserver::JobServer), InProcess(inprocess_jobserver::JobServer), } impl JobTokenServer { /// This function returns a static reference to the jobserver because /// - creating a jobserver from env is a bit fd-unsafe (e.g. the fd might /// be closed by other jobserver users in the process) and better do it /// at the start of the program. /// - in case a jobserver cannot be created from env (e.g. it's not /// present), we will create a global in-process only jobserver /// that has to be static so that it will be shared by all cc /// compilation. fn new() -> &'static Self { // TODO: Replace with a OnceLock once MSRV is 1.70 static JOBSERVER: OnceLock = OnceLock::new(); JOBSERVER.get_or_init(|| { unsafe { inherited_jobserver::JobServer::from_env() } .map(Self::Inherited) .unwrap_or_else(|| Self::InProcess(inprocess_jobserver::JobServer::new())) }) } } pub(crate) enum ActiveJobTokenServer { Inherited(inherited_jobserver::ActiveJobServer<'static>), InProcess(&'static inprocess_jobserver::JobServer), } impl ActiveJobTokenServer { pub(crate) fn new() -> Self { match JobTokenServer::new() { JobTokenServer::Inherited(inherited_jobserver) => { Self::Inherited(inherited_jobserver.enter_active()) } JobTokenServer::InProcess(inprocess_jobserver) => Self::InProcess(inprocess_jobserver), } } pub(crate) async fn acquire(&mut self) -> Result { match self { Self::Inherited(jobserver) => jobserver.acquire().await, Self::InProcess(jobserver) => Ok(jobserver.acquire().await), } } } mod inherited_jobserver { use super::JobToken; use crate::{parallel::async_executor::YieldOnce, Error, ErrorKind}; use std::{ io, mem, sync::{mpsc, Mutex, MutexGuard, PoisonError}, }; pub(super) struct JobServer { /// Implicit token for this process which is obtained and will be /// released in parent. Since `JobTokens` only give back what they got, /// there should be at most one global implicit token in the wild. /// /// Since Rust does not execute any `Drop` for global variables, /// we can't just put it back to jobserver and then re-acquire it at /// the end of the process. /// /// Use `Mutex` to avoid race between acquire and release. /// If an `AtomicBool` is used, then it's possible for: /// - `release_token_raw`: Tries to set `global_implicit_token` to true, but it is already /// set to `true`, continue to release it to jobserver /// - `acquire` takes the global implicit token, set `global_implicit_token` to false /// - `release_token_raw` now writes the token back into the jobserver, while /// `global_implicit_token` is `false` /// /// If the program exits here, then cc effectively increases parallelism by one, which is /// incorrect, hence we use a `Mutex` here. global_implicit_token: Mutex, inner: jobserver::Client, } impl JobServer { pub(super) unsafe fn from_env() -> Option { jobserver::Client::from_env().map(|inner| Self { inner, global_implicit_token: Mutex::new(true), }) } fn get_global_implicit_token(&self) -> MutexGuard<'_, bool> { self.global_implicit_token .lock() .unwrap_or_else(PoisonError::into_inner) } /// All tokens except for the global implicit token will be put back into the jobserver /// immediately and they cannot be cached, since Rust does not call `Drop::drop` on /// global variables. pub(super) fn release_token_raw(&self) { let mut global_implicit_token = self.get_global_implicit_token(); if *global_implicit_token { // There's already a global implicit token, so this token must // be released back into jobserver. // // `release_raw` should not block let _ = self.inner.release_raw(); } else { *global_implicit_token = true; } } pub(super) fn enter_active(&self) -> ActiveJobServer<'_> { ActiveJobServer { jobserver: self, helper_thread: None, } } } struct HelperThread { inner: jobserver::HelperThread, /// When rx is dropped, all the token stored within it will be dropped. rx: mpsc::Receiver>, } impl HelperThread { fn new(jobserver: &JobServer) -> Result { let (tx, rx) = mpsc::channel(); Ok(Self { rx, inner: jobserver.inner.clone().into_helper_thread(move |res| { let _ = tx.send(res); })?, }) } } pub(crate) struct ActiveJobServer<'a> { jobserver: &'a JobServer, helper_thread: Option, } impl ActiveJobServer<'_> { pub(super) async fn acquire(&mut self) -> Result { let mut has_requested_token = false; loop { // Fast path if mem::replace(&mut *self.jobserver.get_global_implicit_token(), false) { break Ok(JobToken::new()); } match self.jobserver.inner.try_acquire() { Ok(Some(acquired)) => { acquired.drop_without_releasing(); break Ok(JobToken::new()); } Ok(None) => YieldOnce::default().await, Err(err) if err.kind() == io::ErrorKind::Unsupported => { // Fallback to creating a help thread with blocking acquire let helper_thread = if let Some(thread) = self.helper_thread.as_ref() { thread } else { self.helper_thread .insert(HelperThread::new(self.jobserver)?) }; match helper_thread.rx.try_recv() { Ok(res) => { let acquired = res?; acquired.drop_without_releasing(); break Ok(JobToken::new()); } Err(mpsc::TryRecvError::Disconnected) => break Err(Error::new( ErrorKind::JobserverHelpThreadError, "jobserver help thread has returned before ActiveJobServer is dropped", )), Err(mpsc::TryRecvError::Empty) => { if !has_requested_token { helper_thread.inner.request_token(); has_requested_token = true; } YieldOnce::default().await } } } Err(err) => break Err(err.into()), } } } } } mod inprocess_jobserver { use super::JobToken; use crate::{parallel::async_executor::YieldOnce, utilities::cargo_env_var_os}; use std::sync::atomic::{ AtomicU32, Ordering::{AcqRel, Acquire}, }; pub(crate) struct JobServer(AtomicU32); impl JobServer { pub(super) fn new() -> Self { // Use `NUM_JOBS` if set (it's configured by Cargo) and otherwise // just fall back to the number of cores on the local machine, or a reasonable // default if that cannot be determined. let parallelism = cargo_env_var_os("NUM_JOBS") .and_then(|j| j.to_str()?.parse::().ok()) .or_else(|| Some(std::thread::available_parallelism().ok()?.get() as u32)) .unwrap_or(4); Self(AtomicU32::new(parallelism)) } pub(super) async fn acquire(&self) -> JobToken { loop { let res = self .0 .fetch_update(AcqRel, Acquire, |tokens| tokens.checked_sub(1)); if res.is_ok() { break JobToken::new(); } YieldOnce::default().await } } pub(super) fn release_token_raw(&self) { self.0.fetch_add(1, AcqRel); } } } cc-1.2.60/src/parallel/mod.rs000064400000000000000000000002101046102023000140000ustar 00000000000000mod async_executor; mod command_runner; mod job_token; pub(crate) mod stderr; pub(crate) use command_runner::run_commands_in_parallel; cc-1.2.60/src/parallel/stderr.rs000064400000000000000000000052571046102023000145440ustar 00000000000000#![cfg_attr(target_family = "wasm", allow(unused))] /// Helpers functions for [`ChildStderr`]. use std::{convert::TryInto, process::ChildStderr}; use crate::{Error, ErrorKind}; #[cfg(all(not(unix), not(windows), not(target_family = "wasm")))] compile_error!("Only unix and windows support non-blocking pipes! For other OSes, disable the parallel feature."); #[cfg(unix)] fn get_flags(fd: std::os::unix::io::RawFd) -> Result { let flags = unsafe { libc::fcntl(fd, libc::F_GETFL, 0) }; if flags == -1 { Err(Error::new( ErrorKind::IOError, format!( "Failed to get flags for pipe {}: {}", fd, std::io::Error::last_os_error() ), )) } else { Ok(flags) } } #[cfg(unix)] fn set_flags(fd: std::os::unix::io::RawFd, flags: std::os::raw::c_int) -> Result<(), Error> { if unsafe { libc::fcntl(fd, libc::F_SETFL, flags) } == -1 { Err(Error::new( ErrorKind::IOError, format!( "Failed to set flags for pipe {}: {}", fd, std::io::Error::last_os_error() ), )) } else { Ok(()) } } #[cfg(unix)] pub fn set_non_blocking(pipe: &impl std::os::unix::io::AsRawFd) -> Result<(), Error> { // On Unix, switch the pipe to non-blocking mode. // On Windows, we have a different way to be non-blocking. let fd = pipe.as_raw_fd(); let flags = get_flags(fd)?; set_flags(fd, flags | libc::O_NONBLOCK) } pub fn bytes_available(stderr: &mut ChildStderr) -> Result { let mut bytes_available = 0; #[cfg(windows)] { use ::find_msvc_tools::windows_sys::PeekNamedPipe; use std::os::windows::io::AsRawHandle; use std::ptr::null_mut; if unsafe { PeekNamedPipe( stderr.as_raw_handle(), null_mut(), 0, null_mut(), &mut bytes_available, null_mut(), ) } == 0 { return Err(Error::new( ErrorKind::IOError, format!( "PeekNamedPipe failed with {}", std::io::Error::last_os_error() ), )); } } #[cfg(unix)] { use std::os::unix::io::AsRawFd; if unsafe { libc::ioctl(stderr.as_raw_fd(), libc::FIONREAD, &mut bytes_available) } != 0 { return Err(Error::new( ErrorKind::IOError, format!("ioctl failed with {}", std::io::Error::last_os_error()), )); } } Ok(bytes_available.try_into().unwrap()) } cc-1.2.60/src/target/apple.rs000064400000000000000000000061271046102023000140310ustar 00000000000000use super::TargetInfo; impl TargetInfo<'_> { pub(crate) fn apple_sdk_name(&self) -> &'static str { match (self.os, self.env) { // The target_env variable, as written here: // https://doc.rust-lang.org/reference/conditional-compilation.html#target_env // is only really used for disambiguation, so we use a "default" case instead of // checking for a blank string. ("macos", _) => "macosx", ("ios", "sim") => "iphonesimulator", ("ios", "macabi") => "macosx", ("ios", _) => "iphoneos", ("tvos", "sim") => "appletvsimulator", ("tvos", _) => "appletvos", ("watchos", "sim") => "watchsimulator", ("watchos", _) => "watchos", ("visionos", "sim") => "xrsimulator", ("visionos", _) => "xros", (os, _) => panic!("invalid Apple target OS {}", os), } } pub(crate) fn apple_version_flag(&self, min_version: &str) -> String { // There are many aliases for these, and `-mtargetos=` is preferred on Clang nowadays, but // for compatibility with older Clang, we use the earliest supported name here. // // NOTE: GCC does not support `-miphoneos-version-min=` etc. (because it does not support // iOS in general), but we specify them anyhow in case we actually have a Clang-like // compiler disguised as a GNU-like compiler, or in case GCC adds support for these in the // future. // // See also: // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-mmacos-version-min // https://clang.llvm.org/docs/AttributeReference.html#availability // https://gcc.gnu.org/onlinedocs/gcc/Darwin-Options.html#index-mmacosx-version-min match (self.os, self.env) { // The target_env variable, as written here: // https://doc.rust-lang.org/reference/conditional-compilation.html#target_env // is only really used for disambiguation, so we use a "default" case instead of // checking for a blank string. ("macos", _) => format!("-mmacosx-version-min={min_version}"), ("ios", "sim") => format!("-mios-simulator-version-min={min_version}"), ("ios", "macabi") => format!("-mtargetos=ios{min_version}-macabi"), ("ios", _) => format!("-miphoneos-version-min={min_version}"), ("tvos", "sim") => format!("-mappletvsimulator-version-min={min_version}"), ("tvos", _) => format!("-mappletvos-version-min={min_version}"), ("watchos", "sim") => format!("-mwatchsimulator-version-min={min_version}"), ("watchos", _) => format!("-mwatchos-version-min={min_version}"), // `-mxros-version-min` does not exist // https://github.com/llvm/llvm-project/issues/88271 ("visionos", "sim") => format!("-mtargetos=xros{min_version}-simulator"), ("visionos", _) => format!("-mtargetos=xros{min_version}"), (os, _) => panic!("invalid Apple target OS {}", os), } } } cc-1.2.60/src/target/generated.rs000064400000000000000000000461021046102023000146630ustar 00000000000000//! This file is generated code. Please edit the generator in //! dev-tools/gen-target-info if you need to make changes, or see //! src/target/llvm.rs if you need to configure a specific LLVM triple. #[rustfmt::skip] pub(crate) const LLVM_TARGETS: &[(&str, &str)] = &[ ("aarch64-apple-darwin", "arm64-apple-macosx"), ("aarch64-apple-ios", "arm64-apple-ios"), ("aarch64-apple-ios-macabi", "arm64-apple-ios-macabi"), ("aarch64-apple-ios-sim", "arm64-apple-ios-simulator"), ("aarch64-apple-tvos", "arm64-apple-tvos"), ("aarch64-apple-tvos-sim", "arm64-apple-tvos-simulator"), ("aarch64-apple-visionos", "arm64-apple-xros"), ("aarch64-apple-visionos-sim", "arm64-apple-xros-simulator"), ("aarch64-apple-watchos", "arm64-apple-watchos"), ("aarch64-apple-watchos-sim", "arm64-apple-watchos-simulator"), ("aarch64-fuchsia", "aarch64-fuchsia"), ("aarch64-kmc-solid_asp3", "aarch64-unknown-none"), ("aarch64-linux-android", "aarch64-linux-android"), ("aarch64-nintendo-switch-freestanding", "aarch64-unknown-none"), ("aarch64-pc-windows-gnullvm", "aarch64-pc-windows-gnu"), ("aarch64-pc-windows-msvc", "aarch64-pc-windows-msvc"), ("aarch64-unknown-freebsd", "aarch64-unknown-freebsd"), ("aarch64-unknown-fuchsia", "aarch64-unknown-fuchsia"), ("aarch64-unknown-helenos", "aarch64-unknown-helenos"), ("aarch64-unknown-hermit", "aarch64-unknown-hermit"), ("aarch64-unknown-illumos", "aarch64-unknown-solaris2.11"), ("aarch64-unknown-linux-gnu", "aarch64-unknown-linux-gnu"), ("aarch64-unknown-linux-gnu_ilp32", "aarch64-unknown-linux-gnu_ilp32"), ("aarch64-unknown-linux-musl", "aarch64-unknown-linux-musl"), ("aarch64-unknown-linux-ohos", "aarch64-unknown-linux-ohos"), ("aarch64-unknown-managarm-mlibc", "aarch64-unknown-managarm-mlibc"), ("aarch64-unknown-netbsd", "aarch64-unknown-netbsd"), ("aarch64-unknown-none", "aarch64-unknown-none"), ("aarch64-unknown-none-softfloat", "aarch64-unknown-none"), ("aarch64-unknown-nto-qnx700", "aarch64-unknown-unknown"), ("aarch64-unknown-nto-qnx710", "aarch64-unknown-unknown"), ("aarch64-unknown-nto-qnx710_iosock", "aarch64-unknown-unknown"), ("aarch64-unknown-nto-qnx800", "aarch64-unknown-unknown"), ("aarch64-unknown-nuttx", "aarch64-unknown-none"), ("aarch64-unknown-openbsd", "aarch64-unknown-openbsd"), ("aarch64-unknown-redox", "aarch64-unknown-redox"), ("aarch64-unknown-teeos", "aarch64-unknown-none"), ("aarch64-unknown-trusty", "aarch64-unknown-unknown-musl"), ("aarch64-unknown-uefi", "aarch64-unknown-windows"), ("aarch64-uwp-windows-msvc", "aarch64-pc-windows-msvc"), ("aarch64-wrs-vxworks", "aarch64-unknown-linux-gnu"), ("aarch64_be-unknown-hermit", "aarch64_be-unknown-hermit"), ("aarch64_be-unknown-linux-gnu", "aarch64_be-unknown-linux-gnu"), ("aarch64_be-unknown-linux-gnu_ilp32", "aarch64_be-unknown-linux-gnu_ilp32"), ("aarch64_be-unknown-linux-musl", "aarch64_be-unknown-linux-musl"), ("aarch64_be-unknown-netbsd", "aarch64_be-unknown-netbsd"), ("aarch64_be-unknown-none-softfloat", "aarch64_be-unknown-none"), ("aarch64v8r-unknown-none", "aarch64-unknown-none"), ("aarch64v8r-unknown-none-softfloat", "aarch64-unknown-none"), ("amdgcn-amd-amdhsa", "amdgcn-amd-amdhsa"), ("arm-linux-androideabi", "arm-linux-androideabi"), ("arm-unknown-linux-gnueabi", "arm-unknown-linux-gnueabi"), ("arm-unknown-linux-gnueabihf", "arm-unknown-linux-gnueabihf"), ("arm-unknown-linux-musleabi", "arm-unknown-linux-musleabi"), ("arm-unknown-linux-musleabihf", "arm-unknown-linux-musleabihf"), ("arm64_32-apple-watchos", "arm64_32-apple-watchos"), ("arm64e-apple-darwin", "arm64e-apple-macosx"), ("arm64e-apple-ios", "arm64e-apple-ios"), ("arm64e-apple-tvos", "arm64e-apple-tvos"), ("arm64ec-pc-windows-msvc", "arm64ec-pc-windows-msvc"), ("armeb-unknown-linux-gnueabi", "armeb-unknown-linux-gnueabi"), ("armebv7r-none-eabi", "armebv7r-none-eabi"), ("armebv7r-none-eabihf", "armebv7r-none-eabihf"), ("armv4t-none-eabi", "armv4t-none-eabi"), ("armv4t-unknown-linux-gnueabi", "armv4t-unknown-linux-gnueabi"), ("armv5te-none-eabi", "armv5te-none-eabi"), ("armv5te-unknown-linux-gnueabi", "armv5te-unknown-linux-gnueabi"), ("armv5te-unknown-linux-musleabi", "armv5te-unknown-linux-musleabi"), ("armv5te-unknown-linux-uclibceabi", "armv5te-unknown-linux-gnueabi"), ("armv6-none-eabi", "armv6-none-eabi"), ("armv6-none-eabihf", "armv6-none-eabihf"), ("armv6-unknown-freebsd", "armv6-unknown-freebsd-gnueabihf"), ("armv6-unknown-netbsd-eabihf", "armv6-unknown-netbsdelf-eabihf"), ("armv6k-nintendo-3ds", "armv6k-none-eabihf"), ("armv7-apple-ios", "armv7-apple-ios7.0.0"), ("armv7-linux-androideabi", "armv7-none-linux-android"), ("armv7-rtems-eabihf", "armv7-unknown-none-eabihf"), ("armv7-sony-vita-newlibeabihf", "thumbv7a-sony-vita-eabihf"), ("armv7-unknown-freebsd", "armv7-unknown-freebsd-gnueabihf"), ("armv7-unknown-linux-gnueabi", "armv7-unknown-linux-gnueabi"), ("armv7-unknown-linux-gnueabihf", "armv7-unknown-linux-gnueabihf"), ("armv7-unknown-linux-musleabi", "armv7-unknown-linux-musleabi"), ("armv7-unknown-linux-musleabihf", "armv7-unknown-linux-musleabihf"), ("armv7-unknown-linux-ohos", "armv7-unknown-linux-ohos"), ("armv7-unknown-linux-uclibceabi", "armv7-unknown-linux-gnueabi"), ("armv7-unknown-linux-uclibceabihf", "armv7-unknown-linux-gnueabihf"), ("armv7-unknown-netbsd-eabihf", "armv7-unknown-netbsdelf-eabihf"), ("armv7-unknown-trusty", "armv7-unknown-unknown-gnueabi"), ("armv7-wrs-vxworks-eabihf", "armv7-unknown-linux-gnueabihf"), ("armv7a-kmc-solid_asp3-eabi", "armv7a-none-eabi"), ("armv7a-kmc-solid_asp3-eabihf", "armv7a-none-eabihf"), ("armv7a-none-eabi", "armv7a-none-eabi"), ("armv7a-none-eabihf", "armv7a-none-eabihf"), ("armv7a-nuttx-eabi", "armv7a-none-eabi"), ("armv7a-nuttx-eabihf", "armv7a-none-eabihf"), ("armv7a-vex-v5", "armv7a-none-eabihf"), ("armv7k-apple-watchos", "armv7k-apple-watchos"), ("armv7r-none-eabi", "armv7r-none-eabi"), ("armv7r-none-eabihf", "armv7r-none-eabihf"), ("armv7s-apple-ios", "armv7s-apple-ios"), ("armv8r-none-eabihf", "armv8r-none-eabihf"), ("asmjs-unknown-emscripten", "wasm32-unknown-emscripten"), ("avr-none", "avr-unknown-unknown"), ("avr-unknown-gnu-atmega328", "avr-unknown-unknown"), ("bpfeb-unknown-none", "bpfeb"), ("bpfel-unknown-none", "bpfel"), ("csky-unknown-linux-gnuabiv2", "csky-unknown-linux-gnuabiv2"), ("csky-unknown-linux-gnuabiv2hf", "csky-unknown-linux-gnuabiv2"), ("hexagon-unknown-linux-musl", "hexagon-unknown-linux-musl"), ("hexagon-unknown-none-elf", "hexagon-unknown-none-elf"), ("hexagon-unknown-qurt", "hexagon-unknown-elf"), ("i386-apple-ios", "i386-apple-ios-simulator"), ("i586-pc-windows-msvc", "i586-pc-windows-msvc"), ("i586-unknown-linux-gnu", "i586-unknown-linux-gnu"), ("i586-unknown-linux-musl", "i586-unknown-linux-musl"), ("i586-unknown-netbsd", "i586-unknown-netbsdelf"), ("i586-unknown-redox", "i586-unknown-redox"), ("i686-apple-darwin", "i686-apple-macosx"), ("i686-linux-android", "i686-linux-android"), ("i686-pc-nto-qnx700", "i586-pc-unknown"), ("i686-pc-windows-gnu", "i686-pc-windows-gnu"), ("i686-pc-windows-gnullvm", "i686-pc-windows-gnu"), ("i686-pc-windows-msvc", "i686-pc-windows-msvc"), ("i686-unknown-freebsd", "i686-unknown-freebsd"), ("i686-unknown-haiku", "i686-unknown-haiku"), ("i686-unknown-helenos", "i686-unknown-helenos"), ("i686-unknown-hurd-gnu", "i686-unknown-hurd-gnu"), ("i686-unknown-linux-gnu", "i686-unknown-linux-gnu"), ("i686-unknown-linux-musl", "i686-unknown-linux-musl"), ("i686-unknown-netbsd", "i686-unknown-netbsdelf"), ("i686-unknown-openbsd", "i686-unknown-openbsd"), ("i686-unknown-uefi", "i686-unknown-windows-gnu"), ("i686-uwp-windows-gnu", "i686-pc-windows-gnu"), ("i686-uwp-windows-msvc", "i686-pc-windows-msvc"), ("i686-win7-windows-gnu", "i686-pc-windows-gnu"), ("i686-win7-windows-msvc", "i686-pc-windows-msvc"), ("i686-wrs-vxworks", "i686-unknown-linux-gnu"), ("loongarch32-unknown-none", "loongarch32-unknown-none"), ("loongarch32-unknown-none-softfloat", "loongarch32-unknown-none"), ("loongarch64-unknown-linux-gnu", "loongarch64-unknown-linux-gnu"), ("loongarch64-unknown-linux-musl", "loongarch64-unknown-linux-musl"), ("loongarch64-unknown-linux-ohos", "loongarch64-unknown-linux-ohos"), ("loongarch64-unknown-none", "loongarch64-unknown-none"), ("loongarch64-unknown-none-softfloat", "loongarch64-unknown-none"), ("m68k-unknown-linux-gnu", "m68k-unknown-linux-gnu"), ("m68k-unknown-none-elf", "m68k"), ("mips-mti-none-elf", "mips"), ("mips-unknown-linux-gnu", "mips-unknown-linux-gnu"), ("mips-unknown-linux-musl", "mips-unknown-linux-musl"), ("mips-unknown-linux-uclibc", "mips-unknown-linux-gnu"), ("mips64-openwrt-linux-musl", "mips64-unknown-linux-musl"), ("mips64-unknown-linux-gnuabi64", "mips64-unknown-linux-gnuabi64"), ("mips64-unknown-linux-muslabi64", "mips64-unknown-linux-musl"), ("mips64el-unknown-linux-gnuabi64", "mips64el-unknown-linux-gnuabi64"), ("mips64el-unknown-linux-muslabi64", "mips64el-unknown-linux-musl"), ("mipsel-mti-none-elf", "mipsel"), ("mipsel-sony-psp", "mipsel-sony-psp"), ("mipsel-sony-psx", "mipsel-sony-psx"), ("mipsel-unknown-linux-gnu", "mipsel-unknown-linux-gnu"), ("mipsel-unknown-linux-musl", "mipsel-unknown-linux-musl"), ("mipsel-unknown-linux-uclibc", "mipsel-unknown-linux-gnu"), ("mipsel-unknown-netbsd", "mipsel-unknown-netbsd"), ("mipsel-unknown-none", "mipsel-unknown-none"), ("mipsisa32r6-unknown-linux-gnu", "mipsisa32r6-unknown-linux-gnu"), ("mipsisa32r6el-unknown-linux-gnu", "mipsisa32r6el-unknown-linux-gnu"), ("mipsisa64r6-unknown-linux-gnuabi64", "mipsisa64r6-unknown-linux-gnuabi64"), ("mipsisa64r6el-unknown-linux-gnuabi64", "mipsisa64r6el-unknown-linux-gnuabi64"), ("msp430-none-elf", "msp430-none-elf"), ("nvptx64-nvidia-cuda", "nvptx64-nvidia-cuda"), ("powerpc-unknown-freebsd", "powerpc-unknown-freebsd13.0"), ("powerpc-unknown-helenos", "powerpc-unknown-helenos"), ("powerpc-unknown-linux-gnu", "powerpc-unknown-linux-gnu"), ("powerpc-unknown-linux-gnuspe", "powerpc-unknown-linux-gnuspe"), ("powerpc-unknown-linux-musl", "powerpc-unknown-linux-musl"), ("powerpc-unknown-linux-muslspe", "powerpc-unknown-linux-muslspe"), ("powerpc-unknown-netbsd", "powerpc-unknown-netbsd"), ("powerpc-unknown-openbsd", "powerpc-unknown-openbsd"), ("powerpc-wrs-vxworks", "powerpc-unknown-linux-gnu"), ("powerpc-wrs-vxworks-spe", "powerpc-unknown-linux-gnuspe"), ("powerpc64-ibm-aix", "powerpc64-ibm-aix"), ("powerpc64-unknown-freebsd", "powerpc64-unknown-freebsd"), ("powerpc64-unknown-linux-gnu", "powerpc64-unknown-linux-gnu"), ("powerpc64-unknown-linux-musl", "powerpc64-unknown-linux-musl"), ("powerpc64-unknown-openbsd", "powerpc64-unknown-openbsd"), ("powerpc64-wrs-vxworks", "powerpc64-unknown-linux-gnu"), ("powerpc64le-unknown-freebsd", "powerpc64le-unknown-freebsd"), ("powerpc64le-unknown-linux-gnu", "powerpc64le-unknown-linux-gnu"), ("powerpc64le-unknown-linux-musl", "powerpc64le-unknown-linux-musl"), ("riscv32-wrs-vxworks", "riscv32-unknown-linux-gnu"), ("riscv32e-unknown-none-elf", "riscv32"), ("riscv32em-unknown-none-elf", "riscv32"), ("riscv32emc-unknown-none-elf", "riscv32"), ("riscv32gc-unknown-linux-gnu", "riscv32-unknown-linux-gnu"), ("riscv32gc-unknown-linux-musl", "riscv32-unknown-linux-musl"), ("riscv32i-unknown-none-elf", "riscv32"), ("riscv32im-risc0-zkvm-elf", "riscv32"), ("riscv32im-unknown-none-elf", "riscv32"), ("riscv32ima-unknown-none-elf", "riscv32"), ("riscv32imac-esp-espidf", "riscv32"), ("riscv32imac-unknown-none-elf", "riscv32"), ("riscv32imac-unknown-nuttx-elf", "riscv32"), ("riscv32imac-unknown-xous-elf", "riscv32"), ("riscv32imafc-esp-espidf", "riscv32"), ("riscv32imafc-unknown-none-elf", "riscv32"), ("riscv32imafc-unknown-nuttx-elf", "riscv32"), ("riscv32imc-esp-espidf", "riscv32"), ("riscv32imc-unknown-none-elf", "riscv32"), ("riscv32imc-unknown-nuttx-elf", "riscv32"), ("riscv64-linux-android", "riscv64-linux-android"), ("riscv64-wrs-vxworks", "riscv64-unknown-linux-gnu"), ("riscv64a23-unknown-linux-gnu", "riscv64-unknown-linux-gnu"), ("riscv64gc-unknown-freebsd", "riscv64-unknown-freebsd"), ("riscv64gc-unknown-fuchsia", "riscv64-unknown-fuchsia"), ("riscv64gc-unknown-hermit", "riscv64-unknown-hermit"), ("riscv64gc-unknown-linux-gnu", "riscv64-unknown-linux-gnu"), ("riscv64gc-unknown-linux-musl", "riscv64-unknown-linux-musl"), ("riscv64gc-unknown-managarm-mlibc", "riscv64-unknown-managarm-mlibc"), ("riscv64gc-unknown-netbsd", "riscv64-unknown-netbsd"), ("riscv64gc-unknown-none-elf", "riscv64"), ("riscv64gc-unknown-nuttx-elf", "riscv64"), ("riscv64gc-unknown-openbsd", "riscv64-unknown-openbsd"), ("riscv64gc-unknown-redox", "riscv64-unknown-redox"), ("riscv64im-unknown-none-elf", "riscv64"), ("riscv64imac-unknown-none-elf", "riscv64"), ("riscv64imac-unknown-nuttx-elf", "riscv64"), ("s390x-unknown-linux-gnu", "s390x-unknown-linux-gnu"), ("s390x-unknown-linux-musl", "s390x-unknown-linux-musl"), ("s390x-unknown-none-softfloat", "s390x-unknown-linux-gnu"), ("sparc-unknown-linux-gnu", "sparc-unknown-linux-gnu"), ("sparc-unknown-none-elf", "sparc-unknown-none-elf"), ("sparc64-unknown-helenos", "sparc64-unknown-helenos"), ("sparc64-unknown-linux-gnu", "sparc64-unknown-linux-gnu"), ("sparc64-unknown-netbsd", "sparc64-unknown-netbsd"), ("sparc64-unknown-openbsd", "sparc64-unknown-openbsd"), ("sparcv9-sun-solaris", "sparcv9-sun-solaris"), ("thumbv4t-none-eabi", "thumbv4t-none-eabi"), ("thumbv5te-none-eabi", "thumbv5te-none-eabi"), ("thumbv6-none-eabi", "thumbv6-none-eabi"), ("thumbv6m-none-eabi", "thumbv6m-none-eabi"), ("thumbv6m-nuttx-eabi", "thumbv6m-none-eabi"), ("thumbv7a-none-eabi", "thumbv7a-none-eabi"), ("thumbv7a-none-eabihf", "thumbv7a-none-eabihf"), ("thumbv7a-nuttx-eabi", "thumbv7a-none-eabi"), ("thumbv7a-nuttx-eabihf", "thumbv7a-none-eabihf"), ("thumbv7a-pc-windows-msvc", "thumbv7a-pc-windows-msvc"), ("thumbv7a-uwp-windows-msvc", "thumbv7a-pc-windows-msvc"), ("thumbv7em-none-eabi", "thumbv7em-none-eabi"), ("thumbv7em-none-eabihf", "thumbv7em-none-eabihf"), ("thumbv7em-nuttx-eabi", "thumbv7em-none-eabi"), ("thumbv7em-nuttx-eabihf", "thumbv7em-none-eabihf"), ("thumbv7m-none-eabi", "thumbv7m-none-eabi"), ("thumbv7m-nuttx-eabi", "thumbv7m-none-eabi"), ("thumbv7neon-linux-androideabi", "armv7-none-linux-android"), ("thumbv7neon-unknown-linux-gnueabihf", "armv7-unknown-linux-gnueabihf"), ("thumbv7neon-unknown-linux-musleabihf", "armv7-unknown-linux-musleabihf"), ("thumbv7r-none-eabi", "thumbv7r-none-eabi"), ("thumbv7r-none-eabihf", "thumbv7r-none-eabihf"), ("thumbv8m.base-none-eabi", "thumbv8m.base-none-eabi"), ("thumbv8m.base-nuttx-eabi", "thumbv8m.base-none-eabi"), ("thumbv8m.main-none-eabi", "thumbv8m.main-none-eabi"), ("thumbv8m.main-none-eabihf", "thumbv8m.main-none-eabihf"), ("thumbv8m.main-nuttx-eabi", "thumbv8m.main-none-eabi"), ("thumbv8m.main-nuttx-eabihf", "thumbv8m.main-none-eabihf"), ("thumbv8r-none-eabihf", "thumbv8r-none-eabihf"), ("wasm32-unknown-emscripten", "wasm32-unknown-emscripten"), ("wasm32-unknown-unknown", "wasm32-unknown-unknown"), ("wasm32-wali-linux-musl", "wasm32-wasi"), ("wasm32-wasi", "wasm32-wasi"), ("wasm32-wasip1", "wasm32-wasip1"), ("wasm32-wasip1-threads", "wasm32-wasi"), ("wasm32-wasip2", "wasm32-wasip2"), ("wasm32-wasip3", "wasm32-wasip3"), ("wasm32v1-none", "wasm32-unknown-unknown"), ("wasm64-unknown-unknown", "wasm64-unknown-unknown"), ("x86_64-apple-darwin", "x86_64-apple-macosx"), ("x86_64-apple-ios", "x86_64-apple-ios-simulator"), ("x86_64-apple-ios-macabi", "x86_64-apple-ios-macabi"), ("x86_64-apple-tvos", "x86_64-apple-tvos-simulator"), ("x86_64-apple-watchos-sim", "x86_64-apple-watchos-simulator"), ("x86_64-fortanix-unknown-sgx", "x86_64-elf"), ("x86_64-fuchsia", "x86_64-fuchsia"), ("x86_64-linux-android", "x86_64-linux-android"), ("x86_64-lynx-lynxos178", "x86_64-unknown-unknown-gnu"), ("x86_64-pc-cygwin", "x86_64-pc-cygwin"), ("x86_64-pc-nto-qnx710", "x86_64-pc-unknown"), ("x86_64-pc-nto-qnx710_iosock", "x86_64-pc-unknown"), ("x86_64-pc-nto-qnx800", "x86_64-pc-unknown"), ("x86_64-pc-solaris", "x86_64-pc-solaris"), ("x86_64-pc-windows-gnu", "x86_64-pc-windows-gnu"), ("x86_64-pc-windows-gnullvm", "x86_64-pc-windows-gnu"), ("x86_64-pc-windows-msvc", "x86_64-pc-windows-msvc"), ("x86_64-sun-solaris", "x86_64-pc-solaris"), ("x86_64-unikraft-linux-musl", "x86_64-unknown-linux-musl"), ("x86_64-unknown-dragonfly", "x86_64-unknown-dragonfly"), ("x86_64-unknown-freebsd", "x86_64-unknown-freebsd"), ("x86_64-unknown-fuchsia", "x86_64-unknown-fuchsia"), ("x86_64-unknown-haiku", "x86_64-unknown-haiku"), ("x86_64-unknown-helenos", "x86_64-unknown-helenos"), ("x86_64-unknown-hermit", "x86_64-unknown-hermit"), ("x86_64-unknown-hurd-gnu", "x86_64-unknown-hurd-gnu"), ("x86_64-unknown-illumos", "x86_64-pc-solaris"), ("x86_64-unknown-l4re-uclibc", "x86_64-unknown-l4re-gnu"), ("x86_64-unknown-linux-gnu", "x86_64-unknown-linux-gnu"), ("x86_64-unknown-linux-gnuasan", "x86_64-unknown-linux-gnu"), ("x86_64-unknown-linux-gnumsan", "x86_64-unknown-linux-gnu"), ("x86_64-unknown-linux-gnutsan", "x86_64-unknown-linux-gnu"), ("x86_64-unknown-linux-gnux32", "x86_64-unknown-linux-gnux32"), ("x86_64-unknown-linux-musl", "x86_64-unknown-linux-musl"), ("x86_64-unknown-linux-none", "x86_64-unknown-linux-none"), ("x86_64-unknown-linux-ohos", "x86_64-unknown-linux-ohos"), ("x86_64-unknown-managarm-mlibc", "x86_64-unknown-managarm-mlibc"), ("x86_64-unknown-motor", "x86_64-unknown-none-elf"), ("x86_64-unknown-netbsd", "x86_64-unknown-netbsd"), ("x86_64-unknown-none", "x86_64-unknown-none-elf"), ("x86_64-unknown-none-linuxkernel", "x86_64-unknown-none-elf"), ("x86_64-unknown-openbsd", "x86_64-unknown-openbsd"), ("x86_64-unknown-redox", "x86_64-unknown-redox"), ("x86_64-unknown-trusty", "x86_64-unknown-unknown-musl"), ("x86_64-unknown-uefi", "x86_64-unknown-windows"), ("x86_64-uwp-windows-gnu", "x86_64-pc-windows-gnu"), ("x86_64-uwp-windows-msvc", "x86_64-pc-windows-msvc"), ("x86_64-win7-windows-gnu", "x86_64-pc-windows-gnu"), ("x86_64-win7-windows-msvc", "x86_64-pc-windows-msvc"), ("x86_64-wrs-vxworks", "x86_64-unknown-linux-gnu"), ("x86_64h-apple-darwin", "x86_64h-apple-macosx"), ("xtensa-esp32-espidf", "xtensa-none-elf"), ("xtensa-esp32-none-elf", "xtensa-none-elf"), ("xtensa-esp32s2-espidf", "xtensa-none-elf"), ("xtensa-esp32s2-none-elf", "xtensa-none-elf"), ("xtensa-esp32s3-espidf", "xtensa-none-elf"), ("xtensa-esp32s3-none-elf", "xtensa-none-elf"), ]; cc-1.2.60/src/target/llvm.rs000064400000000000000000000241641046102023000137030ustar 00000000000000use std::borrow::Cow; use super::{generated, TargetInfo}; impl TargetInfo<'_> { /// The LLVM/Clang target triple. /// /// See . /// /// Rust and Clang don't really agree on target naming, so we first try to /// find the matching trible based on `rustc`'s output, but if no such /// triple exists, we attempt to construct the triple from scratch. /// /// NOTE: You should never need to match on this explicitly, use the /// fields on [`TargetInfo`] instead. pub(crate) fn llvm_target( &self, rustc_target: &str, version: Option<&str>, ) -> Cow<'static, str> { if rustc_target == "armv7-apple-ios" { // FIXME(madsmtm): Unnecessary once we bump MSRV to Rust 1.74 return Cow::Borrowed("armv7-apple-ios"); } else if self.os == "uefi" { // Override the UEFI LLVM targets. // // The rustc mappings (as of 1.82) for the UEFI targets are: // * i686-unknown-uefi -> i686-unknown-windows-gnu // * x86_64-unknown-uefi -> x86_64-unknown-windows // * aarch64-unknown-uefi -> aarch64-unknown-windows // // However, in cc-rs all the UEFI targets use // -windows-gnu. This has been the case since 2021 [1]. // * i686-unknown-uefi -> i686-unknown-windows-gnu // * x86_64-unknown-uefi -> x86_64-unknown-windows-gnu // * aarch64-unknown-uefi -> aarch64-unknown-windows-gnu // // For now, override the UEFI mapping to keep the behavior // of cc-rs unchanged. // // TODO: as discussed in [2], it may be possible to switch // to new UEFI targets added to clang, and regardless it // would be good to have consistency between rustc and // cc-rs. // // [1]: https://github.com/rust-lang/cc-rs/pull/623 // [2]: https://github.com/rust-lang/cc-rs/pull/1264 return Cow::Owned(format!("{}-unknown-windows-gnu", self.full_arch)); } // If no version is requested, let's take the triple directly from // `rustc` (the logic below is not yet good enough for most targets). // // FIXME(madsmtm): This should ideally be removed. if version.is_none() { if let Ok(index) = generated::LLVM_TARGETS .binary_search_by_key(&rustc_target, |(rustc_target, _)| rustc_target) { let (_, llvm_target) = &generated::LLVM_TARGETS[index]; return Cow::Borrowed(llvm_target); } } // Otherwise, attempt to construct the triple from the target info. let arch = match self.full_arch { riscv32 if riscv32.starts_with("riscv32") => "riscv32", riscv64 if riscv64.starts_with("riscv64") => "riscv64", "aarch64" if self.vendor == "apple" => "arm64", "armv7" if self.vendor == "sony" => "thumbv7a", // FIXME arch => arch, }; let vendor = match self.vendor { "kmc" | "nintendo" => "unknown", "unknown" if self.os == "android" => "linux", "uwp" => "pc", "espressif" => "", _ if self.arch == "msp430" => "", vendor => vendor, }; let os = match self.os { "macos" => "macosx", "visionos" => "xros", "uefi" => "windows", "solid_asp3" | "horizon" | "teeos" | "nuttx" | "espidf" => "none", "nto" => "unknown", // FIXME "trusty" => "unknown", // FIXME os => os, }; let version = version.unwrap_or(""); let env = match self.env { "newlib" | "nto70" | "nto71" | "nto71_iosock" | "p1" | "p2" | "relibc" | "sgx" | "uclibc" => "", "sim" => "simulator", env => env, }; let abi = match self.abi { "llvm" | "softfloat" | "uwp" | "vec-extabi" => "", "ilp32" => "_ilp32", "abi64" => "", "elfv1" | "elfv2" => "", abi => abi, }; Cow::Owned(match (vendor, env, abi) { ("", "", "") => format!("{arch}-{os}{version}"), ("", env, abi) => format!("{arch}-{os}{version}-{env}{abi}"), (vendor, "", "") => format!("{arch}-{vendor}-{os}{version}"), (vendor, env, abi) => format!("{arch}-{vendor}-{os}{version}-{env}{abi}"), }) } } #[cfg(test)] mod tests { use std::process::Command; use crate::TargetInfo; #[test] fn test_old_ios_target() { assert_eq!( TargetInfo { full_arch: "armv7", arch: "armv7", vendor: "apple", os: "ios", env: "", abi: "", } .llvm_target("armv7-apple-ios", None), "armv7-apple-ios" ); } #[test] fn basic_llvm_triple_guessing() { assert_eq!( TargetInfo { full_arch: "aarch64", arch: "aarch64", vendor: "unknown", os: "linux", env: "", abi: "", } .llvm_target("invalid", None), "aarch64-unknown-linux" ); assert_eq!( TargetInfo { full_arch: "x86_64", arch: "x86_64", vendor: "unknown", os: "linux", env: "gnu", abi: "", } .llvm_target("invalid", None), "x86_64-unknown-linux-gnu" ); assert_eq!( TargetInfo { full_arch: "x86_64", arch: "x86_64", vendor: "unknown", os: "linux", env: "gnu", abi: "eabi", } .llvm_target("invalid", None), "x86_64-unknown-linux-gnueabi" ); assert_eq!( TargetInfo { full_arch: "x86_64", arch: "x86_64", vendor: "apple", os: "macos", env: "", abi: "", } .llvm_target("invalid", None), "x86_64-apple-macosx" ); } #[test] fn llvm_version() { assert_eq!( TargetInfo { full_arch: "aarch64", arch: "aarch64", vendor: "apple", os: "ios", env: "sim", abi: "", } .llvm_target("aarch64-apple-ios-sim", Some("14.0")), "arm64-apple-ios14.0-simulator" ); assert_eq!( TargetInfo { full_arch: "aarch64", arch: "aarch64", vendor: "apple", os: "visionos", env: "", abi: "", } .llvm_target("aarch64-apple-visionos", Some("2.0")), "arm64-apple-xros2.0" ); assert_eq!( TargetInfo { full_arch: "aarch64", arch: "aarch64", vendor: "apple", os: "ios", env: "", abi: "macabi", } .llvm_target("aarch64-apple-ios-macabi", Some("13.1")), "arm64-apple-ios13.1-macabi" ); } #[test] fn uefi() { assert_eq!( TargetInfo { full_arch: "i686", arch: "x86", vendor: "unknown", os: "uefi", env: "", abi: "", } .llvm_target("i686-unknown-uefi", None), "i686-unknown-windows-gnu" ); assert_eq!( TargetInfo { full_arch: "x86_64", arch: "x86_64", vendor: "unknown", os: "uefi", env: "", abi: "", } .llvm_target("x86_64-unknown-uefi", None), "x86_64-unknown-windows-gnu" ); assert_eq!( TargetInfo { full_arch: "aarch64", arch: "aarch64", vendor: "unknown", os: "uefi", env: "", abi: "", } .llvm_target("aarch64-unknown-uefi", None), "aarch64-unknown-windows-gnu" ); } #[test] #[ignore = "not yet done"] fn llvm_for_all_rustc_targets() { let rustc = std::env::var("RUSTC").unwrap_or_else(|_| "rustc".to_string()); let target_list = Command::new(&rustc) .arg("--print=target-list") .output() .unwrap() .stdout; let target_list = String::from_utf8(target_list).unwrap(); let mut has_failure = false; for target in target_list.lines() { let spec_json = Command::new(&rustc) .arg("--target") .arg(target) .arg("-Zunstable-options") .arg("--print=target-spec-json") .env("RUSTC_BOOTSTRAP", "1") // Crimes .output() .unwrap() .stdout; let spec_json = String::from_utf8(spec_json).unwrap(); // JSON crimes let expected = spec_json .split_once("llvm-target\": \"") .unwrap() .1 .split_once("\"") .unwrap() .0; let actual = TargetInfo::from_rustc_target(target) .map(|target| target.llvm_target("invalid", None)); if Some(expected) != actual.as_deref().ok() { eprintln!("failed comparing {target}:"); eprintln!(" expected: Ok({expected:?})"); eprintln!(" actual: {actual:?}"); eprintln!(); has_failure = true; } } if has_failure { panic!("failed comparing targets"); } } } cc-1.2.60/src/target/parser.rs000064400000000000000000000543171046102023000142300ustar 00000000000000use std::mem; use crate::{ target::TargetInfo, utilities::{cargo_env_var, OnceLock}, Error, ErrorKind, }; #[derive(Debug)] struct TargetInfoParserInner { full_arch: Box, arch: Box, vendor: Box, os: Box, env: Box, abi: Box, } impl TargetInfoParserInner { fn from_cargo_environment_variables() -> Result { // `TARGET` must be present. let target_name = cargo_env_var("TARGET")?; // Parse the full architecture name from the target name. let (full_arch, _rest) = target_name.split_once('-').ok_or(Error::new( ErrorKind::InvalidTarget, format!("target `{target_name}` only had a single component (at least two required)"), ))?; let cargo_env = |key, fallback: Option<&str>| match cargo_env_var(key) { Ok(var) => Ok(var.into_boxed_str()), Err(err) => match fallback { Some(fallback) => Ok(fallback.into()), None => Err(Error::new( ErrorKind::EnvVarNotFound, format!( "did not find fallback information for target `{target_name}`: {}", err.message ), )), }, }; // Prefer to use `CARGO_ENV_*` if set, since these contain the most // correct information relative to the current `rustc`, and makes it // possible to support custom target JSON specs unknown to `rustc`. // // NOTE: If the user is using an older `rustc`, that data may be older // than our pre-generated data, but we still prefer Cargo's view of // the world, since at least `cc` won't differ from `rustc` in that // case. // // These may not be set in case the user depended on being able to // just set `TARGET` outside of build scripts; in those cases, fall // back back to data from the known set of target names instead. // // See discussion in #1225 for further details. let fallback_target = TargetInfo::from_rustc_target(&target_name).ok(); let ft = fallback_target.as_ref(); let arch = cargo_env("CARGO_CFG_TARGET_ARCH", ft.map(|t| t.arch))?; let vendor = cargo_env("CARGO_CFG_TARGET_VENDOR", ft.map(|t| t.vendor))?; let os = cargo_env("CARGO_CFG_TARGET_OS", ft.map(|t| t.os))?; let mut env = cargo_env("CARGO_CFG_TARGET_ENV", ft.map(|t| t.env))?; // `target_abi` was stabilized in Rust 1.78, which is higher than our // MSRV, so it may not always be available; In that case, fall back to // `""`, which is _probably_ correct for unknown target names. let mut abi = cargo_env("CARGO_CFG_TARGET_ABI", ft.map(|t| t.abi)) .unwrap_or_else(|_| String::default().into_boxed_str()); // Remove `macabi` and `sim` from `target_abi` (if present), it's been moved to `target_env`. // TODO: Remove once MSRV is bumped to 1.91 and `rustc` removes these from `target_abi`. if matches!(&*abi, "macabi" | "sim") { debug_assert!( matches!(&*env, "" | "macabi" | "sim"), "env/abi mismatch: {:?}, {:?}", env, abi, ); env = mem::replace(&mut abi, String::default().into_boxed_str()); } Ok(Self { full_arch: full_arch.to_string().into_boxed_str(), arch, vendor, os, env, abi, }) } } /// Parser for [`TargetInfo`], contains cached information. #[derive(Default, Debug)] pub(crate) struct TargetInfoParser(OnceLock>); impl TargetInfoParser { pub fn parse_from_cargo_environment_variables(&self) -> Result, Error> { match self .0 .get_or_init(TargetInfoParserInner::from_cargo_environment_variables) { Ok(TargetInfoParserInner { full_arch, arch, vendor, os, env, abi, }) => Ok(TargetInfo { full_arch, arch, vendor, os, env, abi, }), Err(e) => Err(e.clone()), } } } /// Parse the full architecture in the target name into the simpler /// `cfg(target_arch = "...")` that `rustc` exposes. fn parse_arch(full_arch: &str) -> Option<&str> { // NOTE: Some of these don't necessarily match an existing target in // `rustc`. They're parsed anyhow to be as forward-compatible as possible, // while still being correct. // // See also: // https://docs.rs/cfg-expr/0.18.0/cfg_expr/targets/index.html // https://docs.rs/target-lexicon/0.13.2/target_lexicon/enum.Architecture.html // https://gcc.gnu.org/onlinedocs/gcc/Submodel-Options.html // `clang -print-targets` Some(match full_arch { arch if arch.starts_with("mipsisa32r6") => "mips32r6", // mipsisa32r6 | mipsisa32r6el arch if arch.starts_with("mipsisa64r6") => "mips64r6", // mipsisa64r6 | mipsisa64r6el arch if arch.starts_with("mips64") => "mips64", // mips64 | mips64el arch if arch.starts_with("mips") => "mips", // mips | mipsel arch if arch.starts_with("loongarch64") => "loongarch64", arch if arch.starts_with("loongarch32") => "loongarch32", arch if arch.starts_with("powerpc64") => "powerpc64", // powerpc64 | powerpc64le arch if arch.starts_with("powerpc") => "powerpc", arch if arch.starts_with("ppc64") => "powerpc64", arch if arch.starts_with("ppc") => "powerpc", arch if arch.starts_with("x86_64") => "x86_64", // x86_64 | x86_64h arch if arch.starts_with("i") && arch.ends_with("86") => "x86", // i386 | i586 | i686 "arm64ec" => "arm64ec", // https://github.com/rust-lang/rust/issues/131172 arch if arch.starts_with("aarch64") => "aarch64", // arm64e | arm64_32 arch if arch.starts_with("arm64") => "aarch64", // aarch64 | aarch64_be arch if arch.starts_with("arm") => "arm", // arm | armv7s | armeb | ... arch if arch.starts_with("thumb") => "arm", // thumbv4t | thumbv7a | thumbv8m | ... arch if arch.starts_with("riscv64") => "riscv64", arch if arch.starts_with("riscv32") => "riscv32", arch if arch.starts_with("wasm64") => "wasm64", arch if arch.starts_with("wasm32") => "wasm32", // wasm32 | wasm32v1 "asmjs" => "wasm32", arch if arch.starts_with("nvptx64") => "nvptx64", arch if arch.starts_with("nvptx") => "nvptx", arch if arch.starts_with("bpf") => "bpf", // bpfeb | bpfel // https://github.com/bytecodealliance/wasmtime/tree/v30.0.1/pulley arch if arch.starts_with("pulley64") => "pulley64", arch if arch.starts_with("pulley32") => "pulley32", // https://github.com/Clever-ISA/Clever-ISA arch if arch.starts_with("clever") => "clever", "sparc" | "sparcv7" | "sparcv8" => "sparc", "sparc64" | "sparcv9" => "sparc64", "amdgcn" => "amdgpu", "avr" => "avr", "csky" => "csky", "hexagon" => "hexagon", "m68k" => "m68k", "msp430" => "msp430", "r600" => "r600", "s390x" => "s390x", "xtensa" => "xtensa", // Arches supported by gcc, but not LLVM. arch if arch.starts_with("alpha") => "alpha", // DEC Alpha "hppa" => "hppa", // https://en.wikipedia.org/wiki/PA-RISC, also known as HPPA arch if arch.starts_with("sh") => "sh", // SuperH _ => return None, }) } /// Parse environment and ABI from the last component of the target name. fn parse_envabi(last_component: &str) -> Option<(&str, &str)> { let (env, abi) = match last_component { // Combined environment and ABI // gnullvm | gnueabi | gnueabihf | gnuabiv2 | gnuabi64 | gnuspe | gnux32 | gnu_ilp32 env_and_abi if env_and_abi.starts_with("gnu") => { let abi = env_and_abi.strip_prefix("gnu").unwrap(); let abi = abi.strip_prefix("_").unwrap_or(abi); ("gnu", abi) } // musl | musleabi | musleabihf | muslabi64 | muslspe env_and_abi if env_and_abi.starts_with("musl") => { ("musl", env_and_abi.strip_prefix("musl").unwrap()) } // uclibc | uclibceabi | uclibceabihf env_and_abi if env_and_abi.starts_with("uclibc") => { ("uclibc", env_and_abi.strip_prefix("uclibc").unwrap()) } // newlib | newlibeabihf env_and_abi if env_and_abi.starts_with("newlib") => { ("newlib", env_and_abi.strip_prefix("newlib").unwrap()) } // Environments "msvc" => ("msvc", ""), "ohos" => ("ohos", ""), "qnx700" => ("nto70", ""), "qnx710_iosock" => ("nto71_iosock", ""), "qnx710" => ("nto71", ""), "qnx800" => ("nto80", ""), "sgx" => ("sgx", ""), "threads" => ("threads", ""), "mlibc" => ("mlibc", ""), // ABIs "abi64" => ("", "abi64"), "abiv2" => ("", "spe"), "eabi" => ("", "eabi"), "eabihf" => ("", "eabihf"), "macabi" => ("macabi", ""), "sim" => ("sim", ""), "softfloat" => ("", "softfloat"), "spe" => ("", "spe"), "x32" => ("", "x32"), // Badly named targets, ELF is already known from target OS. // Probably too late to fix now though. "elf" => ("", ""), // Undesirable to expose to user code (yet): // https://github.com/rust-lang/rust/pull/131166#issuecomment-2389541917 "freestanding" => ("", ""), _ => return None, }; Some((env, abi)) } impl<'a> TargetInfo<'a> { pub(crate) fn from_rustc_target(target: &'a str) -> Result { // FIXME(madsmtm): This target should be renamed, cannot be parsed // with the means we do below (since `none` must not be interpreted // as an env/ABI). if target == "x86_64-unknown-linux-none" { return Ok(Self { full_arch: "x86_64", arch: "x86_64", vendor: "unknown", os: "linux", env: "", abi: "", }); } if target == "armv7a-vex-v5" { return Ok(Self { full_arch: "armv7a", arch: "arm", vendor: "vex", os: "vexos", env: "v5", abi: "eabihf", }); } let mut components = target.split('-'); // Insist that the target name contains at least a valid architecture. let full_arch = components.next().ok_or(Error::new( ErrorKind::InvalidTarget, "target was empty".to_string(), ))?; let arch = parse_arch(full_arch).ok_or_else(|| { Error::new( ErrorKind::UnknownTarget, format!("target `{target}` had an unknown architecture"), ) })?; // Newer target names have begun omitting the vendor, so the only // component we know must be there is the OS name. let components: Vec<_> = components.collect(); let (vendor, os, mut env, mut abi) = match &*components { [] => { return Err(Error::new( ErrorKind::InvalidTarget, format!("target `{target}` must have at least two components"), )) } // Two components; format is `arch-os`. [os] => ("unknown", *os, "", ""), // The three-component case is a bit tricky to handle, it could // either have the format `arch-vendor-os` or `arch-os-env+abi`. [vendor_or_os, os_or_envabi] => { // We differentiate between these by checking if the last // component is an env/ABI; if it isn't, then it's probably // an OS instead. if let Some((env, abi)) = parse_envabi(os_or_envabi) { ("unknown", *vendor_or_os, env, abi) } else { (*vendor_or_os, *os_or_envabi, "", "") } } // Four components; format is `arch-vendor-os-env+abi`. [vendor, os, envabi] => { let (env, abi) = parse_envabi(envabi).ok_or_else(|| { Error::new( ErrorKind::UnknownTarget, format!("unknown environment/ABI `{envabi}` in target `{target}`"), ) })?; (*vendor, *os, env, abi) } _ => { return Err(Error::new( ErrorKind::InvalidTarget, format!("too many components in target `{target}`"), )) } }; // Part of the architecture name is carried over into the ABI. match full_arch { // https://github.com/rust-lang/compiler-team/issues/830 arch if arch.starts_with("riscv32e") => { abi = "ilp32e"; } _ => {} } // Various environment/ABIs are determined based on OS name. match os { "3ds" | "rtems" | "espidf" => env = "newlib", "vxworks" => env = "gnu", "redox" => env = "relibc", "aix" => abi = "vec-extabi", _ => {} } // Extra overrides for badly named targets. match target { // Actually simulator targets. "i386-apple-ios" | "x86_64-apple-ios" | "x86_64-apple-tvos" => { env = "sim"; } // Name should've contained `muslabi64`. "mips64-openwrt-linux-musl" => { abi = "abi64"; } // Specifies abi even though not in name. "armv6-unknown-freebsd" | "armv6k-nintendo-3ds" | "armv7-unknown-freebsd" => { abi = "eabihf"; } // Specifies abi even though not in name. "armv7-unknown-linux-ohos" | "armv7-unknown-trusty" => { abi = "eabi"; } _ => {} } let os = match os { // Horizon is the common/internal OS name for 3DS and the Switch. "3ds" | "switch" => "horizon", // FIXME(madsmtm): macOS targets are badly named. "darwin" => "macos", // WASI targets contain the preview version in them too. Should've // been `wasi-p1`/`wasi-p2`, but that's probably too late now. os if os.starts_with("wasi") => { env = os.strip_prefix("wasi").unwrap(); "wasi" } // FIXME(madsmtm): Badly named targets `*-linux-androideabi`, // should be `*-android-eabi`. "androideabi" => { abi = "eabi"; "android" } os => os, }; let vendor = match vendor { // esp, esp32, esp32s2 etc. vendor if vendor.starts_with("esp") => "espressif", // FIXME(madsmtm): Badly named targets `*-linux-android*`, // "linux" makes no sense as the vendor name. "linux" if os == "android" || os == "androideabi" => "unknown", // FIXME(madsmtm): Fix in `rustc` after // https://github.com/rust-lang/compiler-team/issues/850. "wali" => "unknown", "lynx" => "unknown", // Some Linux distributions set their name as the target vendor, // so we have to assume that it can be an arbitary string. vendor => vendor, }; // Intentionally also marked as an ABI: // https://github.com/rust-lang/rust/pull/86922 if vendor == "fortanix" { abi = "fortanix"; } if vendor == "uwp" { abi = "uwp"; } if ["powerpc64-unknown-linux-gnu", "powerpc64-wrs-vxworks"].contains(&target) { abi = "elfv1"; } if [ "powerpc64-unknown-freebsd", "powerpc64-unknown-linux-musl", "powerpc64-unknown-openbsd", "powerpc64le-unknown-freebsd", "powerpc64le-unknown-linux-gnu", "powerpc64le-unknown-linux-musl", ] .contains(&target) { abi = "elfv2"; } if ["asan", "msan", "tsan"].contains(&abi) { abi = ""; } Ok(Self { full_arch, arch, vendor, os, env, abi, }) } } #[cfg(test)] #[allow(unexpected_cfgs)] mod tests { use std::process::Command; use super::TargetInfo; use crate::ErrorKind; // Test tier 1 targets. #[test] fn tier1() { let targets = [ "aarch64-unknown-linux-gnu", "aarch64-apple-darwin", "i686-pc-windows-gnu", "i686-pc-windows-msvc", "i686-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-pc-windows-gnu", "x86_64-pc-windows-msvc", "x86_64-unknown-linux-gnu", ]; for target in targets { // Check that they parse. let _ = TargetInfo::from_rustc_target(target).unwrap(); } } // Various custom target names not (or no longer) known by `rustc`. #[test] fn parse_extra() { let targets = [ "aarch64-unknown-none-gnu", "aarch64-uwp-windows-gnu", "arm-frc-linux-gnueabi", "arm-unknown-netbsd-eabi", "armv7neon-unknown-linux-gnueabihf", "armv7neon-unknown-linux-musleabihf", "thumbv7-unknown-linux-gnueabihf", "thumbv7-unknown-linux-musleabihf", "armv7-apple-ios", "wasm32-wasi", "x86_64-rumprun-netbsd", "x86_64-unknown-linux", "x86_64-alpine-linux-musl", "x86_64-chimera-linux-musl", "x86_64-foxkit-linux-musl", "arm-poky-linux-gnueabi", "x86_64-unknown-moturus", "x86_64-unknown-managarm-mlibc", ]; for target in targets { // Check that they parse. let _ = TargetInfo::from_rustc_target(target).unwrap(); } } fn target_from_rustc_cfgs<'a>(target: &'a str, cfgs: &'a str) -> TargetInfo<'a> { // Cannot determine full architecture from cfgs. let (full_arch, _rest) = target.split_once('-').expect("target to have arch"); let mut target = TargetInfo { full_arch, arch: "invalid-none-set", vendor: "invalid-none-set", os: "invalid-none-set", env: "invalid-none-set", // Not set in older Rust versions abi: "", }; for cfg in cfgs.lines() { if let Some((name, value)) = cfg.split_once('=') { // Remove whitespace, if `rustc` decided to insert any. let name = name.trim(); let value = value.trim(); // Remove quotes around value. let value = value.strip_prefix('"').unwrap_or(value); let value = value.strip_suffix('"').unwrap_or(value); match name { "target_arch" => target.arch = value, "target_vendor" => target.vendor = value, "target_os" => target.os = value, "target_env" => target.env = value, "target_abi" => target.abi = value, _ => {} } } else { // Skip cfgs like `debug_assertions` and `unix`. } } if matches!(target.abi, "macabi" | "sim") { assert_eq!(target.env, target.abi); target.abi = ""; } target } #[test] fn unknown_env_determined_as_unknown() { let err = TargetInfo::from_rustc_target("aarch64-unknown-linux-bogus").unwrap_err(); assert!(matches!(err.kind, ErrorKind::UnknownTarget)); } // Used in .github/workflows/test-rustc-targets.yml #[test] #[cfg_attr( not(rustc_target_test), ignore = "must enable explicitly with --cfg=rustc_target_test" )] fn parse_rustc_targets() { let rustc = std::env::var("RUSTC").unwrap_or_else(|_| "rustc".to_string()); let target_list = Command::new(&rustc) .arg("--print=target-list") .output() .unwrap() .stdout; let target_list = String::from_utf8(target_list).unwrap(); let mut has_failure = false; for target in target_list.lines() { let cfgs = Command::new(&rustc) .arg("--target") .arg(target) .arg("--print=cfg") .output() .unwrap() .stdout; let cfgs = String::from_utf8(cfgs).unwrap(); let expected = target_from_rustc_cfgs(target, &cfgs); let actual = TargetInfo::from_rustc_target(target); if Some(&expected) != actual.as_ref().ok() { eprintln!("failed comparing {target}:"); eprintln!(" expected: Ok({expected:?})"); eprintln!(" actual: {actual:?}"); eprintln!(); has_failure = true; } } if has_failure { panic!("failed comparing targets"); } } #[test] fn parses_apple_envs_correctly() { assert_eq!( TargetInfo::from_rustc_target("aarch64-apple-ios-macabi").unwrap(), TargetInfo { full_arch: "aarch64", arch: "aarch64", vendor: "apple", os: "ios", env: "macabi", abi: "", } ); assert_eq!( TargetInfo::from_rustc_target("aarch64-apple-ios-sim").unwrap(), TargetInfo { full_arch: "aarch64", arch: "aarch64", vendor: "apple", os: "ios", env: "sim", abi: "", } ); } } cc-1.2.60/src/target.rs000064400000000000000000000024621046102023000127260ustar 00000000000000//! Parsing of `rustc` target names to match the values exposed to Cargo //! build scripts (`CARGO_CFG_*`). mod apple; mod generated; mod llvm; mod parser; pub(crate) use parser::TargetInfoParser; /// Information specific to a `rustc` target. /// /// See . #[derive(Debug, PartialEq, Clone)] pub(crate) struct TargetInfo<'a> { /// The full architecture, including the subarchitecture. /// /// This differs from `cfg!(target_arch)`, which only specifies the /// overall architecture, which is too coarse for certain cases. pub full_arch: &'a str, /// The overall target architecture. /// /// This is the same as the value of `cfg!(target_arch)`. pub arch: &'a str, /// The target vendor. /// /// This is the same as the value of `cfg!(target_vendor)`. pub vendor: &'a str, /// The operating system, or `none` on bare-metal targets. /// /// This is the same as the value of `cfg!(target_os)`. pub os: &'a str, /// The environment on top of the operating system. /// /// This is the same as the value of `cfg!(target_env)`. pub env: &'a str, /// The ABI on top of the operating system. /// /// This is the same as the value of `cfg!(target_abi)`. pub abi: &'a str, } cc-1.2.60/src/tempfile.rs000064400000000000000000000043431046102023000132450ustar 00000000000000#![cfg_attr(target_family = "wasm", allow(unused))] use std::{ collections::hash_map::RandomState, fs::{remove_file, File, OpenOptions}, hash::{BuildHasher, Hasher}, io, os, path::{Path, PathBuf}, }; #[cfg(not(any(unix, target_family = "wasm", windows)))] compile_error!("Your system is not supported since cc cannot create named tempfile"); fn rand() -> u64 { RandomState::new().build_hasher().finish() } fn tmpname(suffix: &str) -> String { format!("{}{}", rand(), suffix) } fn create_named(path: &Path) -> io::Result { let mut open_options = OpenOptions::new(); open_options.read(true).write(true).create_new(true); #[cfg(all(unix, not(target_os = "wasi")))] ::mode(&mut open_options, 0o600); #[cfg(windows)] ::custom_flags( &mut open_options, ::find_msvc_tools::windows_sys::FILE_ATTRIBUTE_TEMPORARY, ); open_options.open(path) } pub(super) struct NamedTempfile { path: PathBuf, file: Option, } impl NamedTempfile { pub(super) fn new(base: &Path, suffix: &str) -> io::Result { for _ in 0..10 { let path = base.join(tmpname(suffix)); match create_named(&path) { Ok(file) => { return Ok(Self { file: Some(file), path, }) } Err(e) if e.kind() == io::ErrorKind::AlreadyExists => continue, Err(e) => return Err(e), }; } Err(io::Error::new( io::ErrorKind::AlreadyExists, format!( "too many temporary files exist in base `{}` with suffix `{}`", base.display(), suffix ), )) } pub(super) fn path(&self) -> &Path { &self.path } pub(super) fn take_file(&mut self) -> Option { self.file.take() } } impl Drop for NamedTempfile { fn drop(&mut self) { // On Windows you have to close all handle to it before // removing the file. self.file.take(); let _ = remove_file(&self.path); } } cc-1.2.60/src/tool.rs000064400000000000000000000525141046102023000124200ustar 00000000000000use crate::{ command_helpers::{run_output, spawn_and_wait_for_output, CargoOutput}, run, tempfile::NamedTempfile, Error, ErrorKind, OutputKind, }; use std::{ borrow::Cow, collections::HashMap, env, ffi::{OsStr, OsString}, io::Write, path::{Path, PathBuf}, process::{Command, Output, Stdio}, sync::RwLock, }; pub(crate) type CompilerFamilyLookupCache = HashMap]>, ToolFamily>; /// Configuration used to represent an invocation of a C compiler. /// /// This can be used to figure out what compiler is in use, what the arguments /// to it are, and what the environment variables look like for the compiler. /// This can be used to further configure other build systems (e.g. forward /// along CC and/or CFLAGS) or the `to_command` method can be used to run the /// compiler itself. #[derive(Clone, Debug)] #[allow(missing_docs)] pub struct Tool { pub(crate) path: PathBuf, pub(crate) cc_wrapper_path: Option, pub(crate) cc_wrapper_args: Vec, pub(crate) args: Vec, pub(crate) env: Vec<(OsString, OsString)>, pub(crate) family: ToolFamily, pub(crate) cuda: bool, pub(crate) removed_args: Vec, pub(crate) has_internal_target_arg: bool, } impl Tool { pub(crate) fn from_find_msvc_tools(tool: ::find_msvc_tools::Tool) -> Self { let mut cc_tool = Self::with_family( tool.path().into(), ToolFamily::Msvc { clang_cl: tool.is_clang_cl(), }, ); cc_tool.env = tool .env() .into_iter() .map(|(k, v)| (k.clone(), v.clone())) .collect(); cc_tool } pub(crate) fn new( path: PathBuf, cached_compiler_family: &RwLock, cargo_output: &CargoOutput, out_dir: Option<&Path>, ) -> Self { Self::with_features( path, vec![], false, cached_compiler_family, cargo_output, out_dir, ) } pub(crate) fn with_args( path: PathBuf, args: Vec, cached_compiler_family: &RwLock, cargo_output: &CargoOutput, out_dir: Option<&Path>, ) -> Self { Self::with_features( path, args, false, cached_compiler_family, cargo_output, out_dir, ) } /// Explicitly set the `ToolFamily`, skipping name-based detection. pub(crate) fn with_family(path: PathBuf, family: ToolFamily) -> Self { Self { path, cc_wrapper_path: None, cc_wrapper_args: Vec::new(), args: Vec::new(), env: Vec::new(), family, cuda: false, removed_args: Vec::new(), has_internal_target_arg: false, } } pub(crate) fn with_features( path: PathBuf, args: Vec, cuda: bool, cached_compiler_family: &RwLock, cargo_output: &CargoOutput, out_dir: Option<&Path>, ) -> Self { fn is_zig_cc(path: &Path, cargo_output: &CargoOutput) -> bool { run_output( Command::new(path).arg("--version"), // tool detection issues should always be shown as warnings cargo_output, ) .map(|o| String::from_utf8_lossy(&o).contains("ziglang")) .unwrap_or_default() || { match path.file_name().map(OsStr::to_string_lossy) { Some(fname) => fname.contains("zig"), _ => false, } } } fn guess_family_from_stdout( stdout: &str, path: &Path, args: &[String], cargo_output: &CargoOutput, ) -> Result { cargo_output.print_debug(&stdout); // https://gitlab.kitware.com/cmake/cmake/-/blob/69a2eeb9dff5b60f2f1e5b425002a0fd45b7cadb/Modules/CMakeDetermineCompilerId.cmake#L267-271 // stdin is set to null to ensure that the help output is never paginated. let accepts_cl_style_flags = run( Command::new(path).args(args).arg("-?").stdin(Stdio::null()), &{ // the errors are not errors! let mut cargo_output = cargo_output.clone(); cargo_output.warnings = cargo_output.debug; cargo_output.output = OutputKind::Discard; cargo_output }, ) .is_ok(); let clang = stdout.contains(r#""clang""#); let gcc = stdout.contains(r#""gcc""#); let emscripten = stdout.contains(r#""emscripten""#); let vxworks = stdout.contains(r#""VxWorks""#); match (clang, accepts_cl_style_flags, gcc, emscripten, vxworks) { (clang_cl, true, _, false, false) => Ok(ToolFamily::Msvc { clang_cl }), (true, _, _, _, false) | (_, _, _, true, false) => Ok(ToolFamily::Clang { zig_cc: is_zig_cc(path, cargo_output), }), (false, false, true, _, false) | (_, _, _, _, true) => Ok(ToolFamily::Gnu), (false, false, false, false, false) => { cargo_output.print_warning(&"Compiler family detection failed since it does not define `__clang__`, `__GNUC__`, `__EMSCRIPTEN__` or `__VXWORKS__`, also does not accept cl style flag `-?`, fallback to treating it as GNU"); Err(Error::new( ErrorKind::ToolFamilyMacroNotFound, "Expects macro `__clang__`, `__GNUC__` or `__EMSCRIPTEN__`, `__VXWORKS__` or accepts cl style flag `-?`, but found none", )) } } } fn detect_family_inner( path: &Path, args: &[String], cargo_output: &CargoOutput, out_dir: Option<&Path>, ) -> Result { let out_dir = out_dir .map(Cow::Borrowed) .unwrap_or_else(|| Cow::Owned(env::temp_dir())); // Ensure all the parent directories exist otherwise temp file creation // will fail std::fs::create_dir_all(&out_dir).map_err(|err| Error { kind: ErrorKind::IOError, message: format!("failed to create OUT_DIR '{}': {}", out_dir.display(), err) .into(), })?; let mut tmp = NamedTempfile::new(&out_dir, "detect_compiler_family.c").map_err(|err| Error { kind: ErrorKind::IOError, message: format!( "failed to create detect_compiler_family.c temp file in '{}': {}", out_dir.display(), err ) .into(), })?; let mut tmp_file = tmp.take_file().unwrap(); tmp_file.write_all(include_bytes!("detect_compiler_family.c"))?; // Close the file handle *now*, otherwise the compiler may fail to open it on Windows // (#1082). The file stays on disk and its path remains valid until `tmp` is dropped. tmp_file.flush()?; tmp_file.sync_data()?; drop(tmp_file); // When expanding the file, the compiler prints a lot of information to stderr // that it is not an error, but related to expanding itself. // // cc would have to disable warning here to prevent generation of too many warnings. let mut compiler_detect_output = cargo_output.clone(); compiler_detect_output.warnings = compiler_detect_output.debug; let mut cmd = Command::new(path); cmd.arg("-E").arg(tmp.path()); // The -Wslash-u-filename warning is normally part of stdout. // But with clang-cl it can be part of stderr instead and exit with a // non-zero exit code. let mut captured_cargo_output = compiler_detect_output.clone(); captured_cargo_output.warnings = true; let Output { status, stdout, stderr, } = spawn_and_wait_for_output(&mut cmd, &captured_cargo_output)?; let stdout = if [&stdout, &stderr] .iter() .any(|o| String::from_utf8_lossy(o).contains("-Wslash-u-filename")) { run_output( Command::new(path).arg("-E").arg("--").arg(tmp.path()), &compiler_detect_output, )? } else { if !status.success() { return Err(Error::new( ErrorKind::ToolExecError, format!( "command did not execute successfully (status code {status}): {cmd:?}" ), )); } stdout }; let stdout = String::from_utf8_lossy(&stdout); guess_family_from_stdout(&stdout, path, args, cargo_output) } let detect_family = |path: &Path, args: &[String]| -> Result { let cache_key = [path.as_os_str()] .iter() .cloned() .chain(args.iter().map(OsStr::new)) .map(Into::into) .collect(); if let Some(family) = cached_compiler_family.read().unwrap().get(&cache_key) { return Ok(*family); } let family = detect_family_inner(path, args, cargo_output, out_dir)?; cached_compiler_family .write() .unwrap() .insert(cache_key, family); Ok(family) }; let family = detect_family(&path, &args).unwrap_or_else(|e| { cargo_output.print_warning(&format_args!( "Compiler family detection failed due to error: {e}" )); match path.file_name().map(OsStr::to_string_lossy) { Some(fname) if fname.contains("clang-cl") => ToolFamily::Msvc { clang_cl: true }, Some(fname) if fname.ends_with("cl") || fname == "cl.exe" => { ToolFamily::Msvc { clang_cl: false } } Some(fname) if fname.contains("clang") => { let is_clang_cl = args .iter() .any(|a| a.strip_prefix("--driver-mode=") == Some("cl")); if is_clang_cl { ToolFamily::Msvc { clang_cl: true } } else { ToolFamily::Clang { zig_cc: is_zig_cc(&path, cargo_output), } } } Some(fname) if fname.contains("zig") => ToolFamily::Clang { zig_cc: true }, _ => ToolFamily::Gnu, } }); Tool { path, cc_wrapper_path: None, cc_wrapper_args: Vec::new(), args: Vec::new(), env: Vec::new(), family, cuda, removed_args: Vec::new(), has_internal_target_arg: false, } } /// Add an argument to be stripped from the final command arguments. pub(crate) fn remove_arg(&mut self, flag: OsString) { self.removed_args.push(flag); } /// Push an "exotic" flag to the end of the compiler's arguments list. /// /// Nvidia compiler accepts only the most common compiler flags like `-D`, /// `-I`, `-c`, etc. Options meant specifically for the underlying /// host C++ compiler have to be prefixed with `-Xcompiler`. /// [Another possible future application for this function is passing /// clang-specific flags to clang-cl, which otherwise accepts only /// MSVC-specific options.] pub(crate) fn push_cc_arg(&mut self, flag: OsString) { if self.cuda { self.args.push("-Xcompiler".into()); } self.args.push(flag); } /// Checks if an argument or flag has already been specified or conflicts. /// /// Currently only checks optimization flags. pub(crate) fn is_duplicate_opt_arg(&self, flag: &OsString) -> bool { let flag = flag.to_str().unwrap(); let mut chars = flag.chars(); // Only duplicate check compiler flags if self.is_like_msvc() { if chars.next() != Some('/') { return false; } } else if (self.is_like_gnu() || self.is_like_clang()) && chars.next() != Some('-') { return false; } // Check for existing optimization flags (-O, /O) if chars.next() == Some('O') { return self .args() .iter() .any(|a| a.to_str().unwrap_or("").chars().nth(1) == Some('O')); } // TODO Check for existing -m..., -m...=..., /arch:... flags false } /// Don't push optimization arg if it conflicts with existing args. pub(crate) fn push_opt_unless_duplicate(&mut self, flag: OsString) { if self.is_duplicate_opt_arg(&flag) { eprintln!("Info: Ignoring duplicate arg {:?}", &flag); } else { self.push_cc_arg(flag); } } /// Converts this compiler into a `Command` that's ready to be run. /// /// This is useful for when the compiler needs to be executed and the /// command returned will already have the initial arguments and environment /// variables configured. pub fn to_command(&self) -> Command { let mut cmd = match self.cc_wrapper_path { Some(ref cc_wrapper_path) => { let mut cmd = Command::new(cc_wrapper_path); cmd.arg(&self.path); cmd } None => Command::new(&self.path), }; cmd.args(&self.cc_wrapper_args); cmd.args(self.args.iter().filter(|a| !self.removed_args.contains(a))); for (k, v) in self.env.iter() { cmd.env(k, v); } cmd } /// Returns the path for this compiler. /// /// Note that this may not be a path to a file on the filesystem, e.g. "cc", /// but rather something which will be resolved when a process is spawned. pub fn path(&self) -> &Path { &self.path } /// Returns the default set of arguments to the compiler needed to produce /// executables for the target this compiler generates. pub fn args(&self) -> &[OsString] { &self.args } /// Returns the set of environment variables needed for this compiler to /// operate. /// /// This is typically only used for MSVC compilers currently. pub fn env(&self) -> &[(OsString, OsString)] { &self.env } /// Returns the compiler command in format of CC environment variable. /// Or empty string if CC env was not present /// /// This is typically used by configure script pub fn cc_env(&self) -> OsString { match self.cc_wrapper_path { Some(ref cc_wrapper_path) => { let mut cc_env = cc_wrapper_path.as_os_str().to_owned(); cc_env.push(" "); cc_env.push(self.path.to_path_buf().into_os_string()); for arg in self.cc_wrapper_args.iter() { cc_env.push(" "); cc_env.push(arg); } cc_env } None => OsString::from(""), } } /// Returns the compiler flags in format of CFLAGS environment variable. /// Important here - this will not be CFLAGS from env, its internal gcc's flags to use as CFLAGS /// This is typically used by configure script pub fn cflags_env(&self) -> OsString { let mut flags = OsString::new(); for (i, arg) in self.args.iter().enumerate() { if i > 0 { flags.push(" "); } flags.push(arg); } flags } /// Whether the tool is GNU Compiler Collection-like. pub fn is_like_gnu(&self) -> bool { self.family == ToolFamily::Gnu } /// Whether the tool is Clang-like. pub fn is_like_clang(&self) -> bool { matches!(self.family, ToolFamily::Clang { .. }) } /// Whether the tool is AppleClang under .xctoolchain #[cfg(target_vendor = "apple")] pub(crate) fn is_xctoolchain_clang(&self) -> bool { let path = self.path.to_string_lossy(); path.contains(".xctoolchain/") } #[cfg(not(target_vendor = "apple"))] pub(crate) fn is_xctoolchain_clang(&self) -> bool { false } /// Whether the tool is MSVC-like. pub fn is_like_msvc(&self) -> bool { matches!(self.family, ToolFamily::Msvc { .. }) } /// Whether the tool is `clang-cl`-based MSVC-like. pub fn is_like_clang_cl(&self) -> bool { matches!(self.family, ToolFamily::Msvc { clang_cl: true }) } /// Supports using `--` delimiter to separate arguments and path to source files. pub(crate) fn supports_path_delimiter(&self) -> bool { // homebrew clang and zig-cc does not support this while stock version does matches!(self.family, ToolFamily::Msvc { clang_cl: true }) && !self.cuda } } /// Represents the family of tools this tool belongs to. /// /// Each family of tools differs in how and what arguments they accept. /// /// Detection of a family is done on best-effort basis and may not accurately reflect the tool. #[derive(Copy, Clone, Debug, PartialEq)] pub enum ToolFamily { /// Tool is GNU Compiler Collection-like. Gnu, /// Tool is Clang-like. It differs from the GCC in a sense that it accepts superset of flags /// and its cross-compilation approach is different. Clang { zig_cc: bool }, /// Tool is the MSVC cl.exe. Msvc { clang_cl: bool }, } impl ToolFamily { /// What the flag to request debug info for this family of tools look like pub(crate) fn add_debug_flags( &self, cmd: &mut Tool, debug_opt: &str, dwarf_version: Option, ) { match *self { ToolFamily::Msvc { .. } => { cmd.push_cc_arg("-Z7".into()); } ToolFamily::Gnu | ToolFamily::Clang { .. } => { match debug_opt { // From https://doc.rust-lang.org/cargo/reference/profiles.html#debug "" | "0" | "false" | "none" => { debug_assert!( false, "earlier check should have avoided calling add_debug_flags" ); } // line-directives-only is LLVM-specific; for GCC we have to treat it like "1" "line-directives-only" if cmd.is_like_clang() => { cmd.push_cc_arg("-gline-directives-only".into()); } // Clang has -gline-tables-only, but it's an alias for -g1 anyway. // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-gline-tables-only "1" | "limited" | "line-tables-only" | "line-directives-only" => { cmd.push_cc_arg("-g1".into()); } "2" | "true" | "full" => { cmd.push_cc_arg("-g".into()); } _ => { // Err on the side of including too much info rather than too little. cmd.push_cc_arg("-g".into()); } } if let Some(v) = dwarf_version { cmd.push_cc_arg(format!("-gdwarf-{v}").into()); } } } } /// What the flag to force frame pointers. pub(crate) fn add_force_frame_pointer(&self, cmd: &mut Tool) { match *self { ToolFamily::Gnu | ToolFamily::Clang { .. } => { cmd.push_cc_arg("-fno-omit-frame-pointer".into()); } _ => (), } } /// What the flags to enable all warnings pub(crate) fn warnings_flags(&self) -> &'static str { match *self { ToolFamily::Msvc { .. } => "-W4", ToolFamily::Gnu | ToolFamily::Clang { .. } => "-Wall", } } pub(crate) fn warnings_suppression_flags(&self) -> &'static str { match *self { ToolFamily::Msvc { .. } => "-W0", ToolFamily::Gnu | ToolFamily::Clang { .. } => "-w", } } /// What the flags to enable extra warnings pub(crate) fn extra_warnings_flags(&self) -> Option<&'static str> { match *self { ToolFamily::Msvc { .. } => None, ToolFamily::Gnu | ToolFamily::Clang { .. } => Some("-Wextra"), } } /// What the flag to turn warning into errors pub(crate) fn warnings_to_errors_flag(&self) -> &'static str { match *self { ToolFamily::Msvc { .. } => "-WX", ToolFamily::Gnu | ToolFamily::Clang { .. } => "-Werror", } } pub(crate) fn verbose_stderr(&self) -> bool { matches!(*self, ToolFamily::Clang { .. }) } } cc-1.2.60/src/utilities.rs000064400000000000000000000106001046102023000134440ustar 00000000000000use std::{ cell::UnsafeCell, ffi::{OsStr, OsString}, fmt::{self, Write}, marker::PhantomData, mem::MaybeUninit, panic::{RefUnwindSafe, UnwindSafe}, path::Path, sync::Once, }; use crate::{Error, ErrorKind}; pub(super) struct JoinOsStrs<'a, T> { pub(super) slice: &'a [T], pub(super) delimiter: char, } impl fmt::Display for JoinOsStrs<'_, T> where T: AsRef, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let len = self.slice.len(); for (index, os_str) in self.slice.iter().enumerate() { // TODO: Use OsStr::display once it is stablised, // Path and OsStr has the same `Display` impl write!(f, "{}", Path::new(os_str).display())?; if index + 1 < len { f.write_char(self.delimiter)?; } } Ok(()) } } pub(super) struct OptionOsStrDisplay(pub(super) Option); impl fmt::Display for OptionOsStrDisplay where T: AsRef, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // TODO: Use OsStr::display once it is stablised // Path and OsStr has the same `Display` impl if let Some(os_str) = self.0.as_ref() { write!(f, "Some({})", Path::new(os_str).display()) } else { f.write_str("None") } } } pub(crate) struct OnceLock { once: Once, value: UnsafeCell>, _marker: PhantomData, } impl Default for OnceLock { fn default() -> Self { Self::new() } } impl OnceLock { pub(crate) const fn new() -> Self { Self { once: Once::new(), value: UnsafeCell::new(MaybeUninit::uninit()), _marker: PhantomData, } } #[inline] fn is_initialized(&self) -> bool { self.once.is_completed() } unsafe fn get_unchecked(&self) -> &T { debug_assert!(self.is_initialized()); #[allow(clippy::needless_borrow)] #[allow(unused_unsafe)] unsafe { (&*self.value.get()).assume_init_ref() } } pub(crate) fn get_or_init(&self, f: impl FnOnce() -> T) -> &T { self.once.call_once(|| { unsafe { &mut *self.value.get() }.write(f()); }); unsafe { self.get_unchecked() } } pub(crate) fn get(&self) -> Option<&T> { if self.is_initialized() { // Safe b/c checked is_initialized Some(unsafe { self.get_unchecked() }) } else { None } } } impl fmt::Debug for OnceLock { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut d = f.debug_tuple("OnceLock"); match self.get() { Some(v) => d.field(v), None => d.field(&format_args!("")), }; d.finish() } } unsafe impl Sync for OnceLock {} unsafe impl Send for OnceLock {} impl RefUnwindSafe for OnceLock {} impl UnwindSafe for OnceLock {} impl Drop for OnceLock { #[inline] fn drop(&mut self) { if self.once.is_completed() { // SAFETY: The cell is initialized and being dropped, so it can't // be accessed again. unsafe { self.value.get_mut().assume_init_drop() }; } } } /// Access an environment variable that's set by Cargo. /// /// /// /// Cargo doesn't need to be told about these with `rerun-if-env-changed`, and /// that we don't want to allow overwriting them with `Build::env`. #[allow(clippy::disallowed_methods)] // Cargo env, no need for cache busting. pub(crate) fn cargo_env_var_os(key: &str) -> Option { std::env::var_os(key) } pub(crate) fn cargo_env_var(key: &str) -> Result { if let Some(value) = cargo_env_var_os(key) { match value.into_string() { Ok(value) => Ok(value), Err(value) => Err(Error::new( ErrorKind::EnvVarNotFound, format!("environment variable {key} is not valid utf-8: {value:?}"), )), } } else { Err(Error::new( ErrorKind::EnvVarNotFound, format!("environment variable {key} not defined"), )) } }