cc-1.1.14/.cargo_vcs_info.json0000644000000001360000000000100115050ustar { "git": { "sha1": "ff5fc7a69a9e41fd979073fdf8851a2b05306bd9" }, "path_in_vcs": "" }cc-1.1.14/.gitignore000064400000000000000000000000361046102023000122640ustar 00000000000000target Cargo.lock .idea *.iml cc-1.1.14/CHANGELOG.md000064400000000000000000000164401046102023000121130ustar 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.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.1.14/Cargo.lock0000644000000117620000000000100074670ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "bitflags" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "cc" version = "1.1.14" dependencies = [ "jobserver", "libc", "shlex", "tempfile", ] [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "errno" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", "windows-sys 0.52.0", ] [[package]] name = "fastrand" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" [[package]] name = "jobserver" version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" dependencies = [ "libc", ] [[package]] name = "libc" version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" [[package]] name = "linux-raw-sys" version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "once_cell" version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "rustix" version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", "windows-sys 0.52.0", ] [[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "tempfile" version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" dependencies = [ "cfg-if", "fastrand", "once_cell", "rustix", "windows-sys 0.59.0", ] [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ "windows-targets", ] [[package]] name = "windows-sys" version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_gnullvm", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" cc-1.1.14/Cargo.toml0000644000000030700000000000100075030ustar # 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.1.14" authors = ["Alex Crichton "] build = false exclude = [ "/.github", "tests", "src/bin", ] 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" [lib] name = "cc" path = "src/lib.rs" [dependencies.jobserver] version = "0.1.30" optional = true default-features = false [dependencies.shlex] version = "1.3.0" [dev-dependencies.tempfile] version = "3" [features] jobserver = [] parallel = [ "dep:libc", "dep:jobserver", ] [target."cfg(unix)".dependencies.libc] version = "0.2.62" optional = true default-features = false cc-1.1.14/Cargo.toml.orig000064400000000000000000000026561046102023000131750ustar 00000000000000[package] name = "cc" version = "1.1.14" 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" [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 = [ "dev-tools/cc-test", "dev-tools/gen-target-info", "dev-tools/gen-windows-sys-binding", ] cc-1.1.14/LICENSE-APACHE000064400000000000000000000251371046102023000122310ustar 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.1.14/LICENSE-MIT000064400000000000000000000020411046102023000117260ustar 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.1.14/README.md000064400000000000000000000020471046102023000115570ustar 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/licenses/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.1.14/clippy.toml000064400000000000000000000003311046102023000124670ustar 00000000000000disallowed-methods = [ { path = "std::env::var_os", reason = "Please use Build::getenv" }, { path = "std::env::var", reason = "Please use Build::getenv" }, ] doc-valid-idents = ["AppleClang", "OpenBSD", ".."] cc-1.1.14/src/command_helpers.rs000064400000000000000000000362521046102023000146020ustar 00000000000000//! Miscellaneous helpers for running commands use std::{ collections::hash_map, ffi::OsString, fmt::Display, fs, hash::Hasher, io::{self, Read, Write}, path::Path, process::{Child, ChildStderr, Command, Stdio}, sync::{ atomic::{AtomicBool, Ordering}, Arc, }, }; use crate::{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: std::env::var_os("CC_ENABLE_DEBUG_OUTPUT").is_some(), 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.load(Ordering::Relaxed) { self.checked_dbg_var.store(true, Ordering::Relaxed); 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, } 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))), #[cfg(feature = "parallel")] is_non_blocking: false, #[cfg(feature = "parallel")] bytes_available_failed: false, } } fn forward_available(&mut self) -> bool { if let Some((stderr, buffer)) = self.inner.as_mut() { loop { let old_data_end = buffer.len(); // 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 }; buffer.reserve(to_reserve); // Safety: stderr.read only writes to the spare part of the buffer, it never reads from it match stderr .read(unsafe { &mut *(buffer.spare_capacity_mut() as *mut _ as *mut [u8]) }) { 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 => { // Safety: bytes_read bytes is written to spare part of the buffer unsafe { buffer.set_len(old_data_end + bytes_read) }; let mut consumed = 0; for line in buffer.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); } } buffer.drain(..consumed); } res => { // End of stream: flush remaining data and bail. if old_data_end > 0 { write_warning(&buffer[..old_data_end]); } 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")] 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, program: &Path, 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, command {:?} with args {}: {}.", cmd, program.display(), e ), )); } }; cargo_output.print_debug(&status); if status.success() { Ok(()) } else { Err(Error::new( ErrorKind::ToolExecError, format!( "Command {:?} with args {} did not execute successfully (status code {}).", cmd, program.display(), status ), )) } } /// 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(); hasher.write(dirname.to_string().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, program: impl AsRef, cargo_output: &CargoOutput, ) -> Result<(), Error> { let program = program.as_ref(); let mut child = spawn(cmd, program, cargo_output)?; wait_on_child(cmd, program, &mut child, cargo_output) } pub(crate) fn run_output( cmd: &mut Command, program: impl AsRef, cargo_output: &CargoOutput, ) -> Result, Error> { let program = program.as_ref(); // 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; let mut child = spawn(cmd, program, &captured_cargo_output)?; let mut stdout = vec![]; child .stdout .take() .unwrap() .read_to_end(&mut stdout) .unwrap(); // Don't care about this output, use the normal settings wait_on_child(cmd, program, &mut child, cargo_output)?; Ok(stdout) } pub(crate) fn spawn( cmd: &mut Command, program: &Path, 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. Is `{}` installed?{}", program.display(), extra ), )) } Err(e) => Err(Error::new( ErrorKind::ToolExecError, format!( "Command {:?} with args {} failed to start: {:?}", cmd.0, program.display(), e ), )), } } 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); } } #[cfg(feature = "parallel")] pub(crate) fn try_wait_on_child( cmd: &Command, program: &Path, child: &mut Child, stdout: &mut dyn 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 {:?} with args {} did not execute successfully (status code {}).", cmd, program.display(), status ), )) } } Ok(None) => Ok(None), Err(e) => { stderr_forwarder.forward_all(); Err(Error::new( ErrorKind::ToolExecError, format!( "Failed to wait on spawned child process, command {:?} with args {}: {}.", cmd, program.display(), e ), )) } } } cc-1.1.14/src/detect_compiler_family.c000064400000000000000000000002321046102023000157300ustar 00000000000000#ifdef __clang__ #pragma message "clang" #endif #ifdef __GNUC__ #pragma message "gcc" #endif #ifdef __EMSCRIPTEN__ #pragma message "emscripten" #endif cc-1.1.14/src/lib.rs000064400000000000000000005157601046102023000122160ustar 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 is used as an exact //! executable name, so (for example) no extra flags can be passed inside //! this variable, and the builder must ensure that there aren't any //! trailing spaces. This compiler must understand the `-c` flag. For //! certain `TARGET`s, it also is assumed to know about other flags (most //! common is `-fPIC`). //! * `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). //! //! 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` //! 2. `_` - for example, `CC_x86_64_unknown_linux_gnu` //! 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 triple 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. //! * Windows platforms targeting MinGW (e.g. your target triple 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"); //! ``` #![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, Formatter}; use std::fs; use std::io::{self, Write}; use std::path::{Component, Path, PathBuf}; #[cfg(feature = "parallel")] use std::process::Child; use std::process::Command; use std::sync::{Arc, RwLock}; use shlex::Shlex; #[cfg(feature = "parallel")] mod parallel; mod windows; // Regardless of whether this should be in this crate's public API, // it has been since 2015, so don't break it. pub use windows::find_tools as windows_registry; mod command_helpers; use command_helpers::*; mod tool; pub use tool::Tool; use tool::ToolFamily; mod target_info; mod tempfile; mod utilities; use utilities::*; #[derive(Debug, Eq, PartialEq, Hash)] struct CompilerFlag { compiler: Box, flag: Box, } type Env = Option>; /// 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>, known_flag_support_status_cache: Arc>>, ar_flags: Vec>, asm_flags: Vec>, no_default_flags: bool, files: Vec>, cpp: bool, cpp_link_stdlib: Option>>, cpp_set_stdlib: Option>, cuda: bool, cudart: Option>, ccbin: bool, std: Option>, target: Option>, 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, env_cache: Arc, Env>>>, apple_sdk_root_cache: Arc, Arc>>>, apple_versions_cache: Arc, Arc>>>, emit_rerun_if_env_changed: bool, cached_compiler_family: Arc, ToolFamily>>>, shell_escaped_flags: Option, } /// Represents the types of errors that may occur while using cc-rs. #[derive(Clone, Debug)] enum ErrorKind { /// Error occurred while performing I/O. IOError, /// Invalid architecture supplied. ArchitectureInvalid, /// 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, #[cfg(feature = "parallel")] /// jobserver helpthread failure JobserverHelpThreadError, } /// 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 } } } 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(), known_flag_support_status_cache: Arc::new(RwLock::new(HashMap::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_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, env_cache: Arc::new(RwLock::new(HashMap::new())), apple_sdk_root_cache: Arc::new(RwLock::new(HashMap::new())), apple_versions_cache: Arc::new(RwLock::new(HashMap::new())), emit_rerun_if_env_changed: true, cached_compiler_family: Arc::default(), shell_escaped_flags: None, } } /// 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 } /// 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 } 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) } /// 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()?.path(), &self.get_target()?, ) } fn is_flag_supported_inner( &self, flag: &OsStr, compiler_path: &Path, target: &str, ) -> Result { let compiler_flag = CompilerFlag { compiler: compiler_path.into(), flag: flag.into(), }; if let Some(is_supported) = self .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(compiler_path) .cargo_metadata(self.cargo_output.metadata) .target(target) .opt_level(0) .host(&self.get_host()?) .debug(false) .cpp(self.cpp) .cuda(self.cuda) .emit_rerun_if_env_changed(self.emit_rerun_if_env_changed); 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(); let is_arm = target.contains("aarch64") || target.contains("arm"); let clang = compiler.is_like_clang(); let gnu = compiler.family == ToolFamily::Gnu; command_add_output_file( &mut cmd, &obj, CmdAddOutputFileArgs { cuda: self.cuda, is_assembler_msvc: false, msvc: compiler.is_like_msvc(), clang, gnu, is_asm: false, is_arm, }, ); // Checking for compiler flags does not require linking cmd.arg("-c"); cmd.arg(&src); let output = cmd.output()?; let is_supported = output.status.success() && output.stderr.is_empty(); self.known_flag_support_status_cache .write() .unwrap() .insert(compiler_flag, is_supported); Ok(is_supported) } /// 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)?; self.flags.extend( flags .into_iter() .map(|flag| Arc::from(OsString::from(flag).as_os_str())), ); Ok(self) } /// Set the `-shared` flag. /// /// When enabled, the compiler will produce a shared object which can /// then be linked with other objects to form an executable. /// /// # Example /// /// ```no_run /// cc::Build::new() /// .file("src/foo.c") /// .shared_flag(true) /// .compile("libfoo.so"); /// ``` pub fn shared_flag(&mut self, shared_flag: bool) -> &mut Build { self.shared_flag = Some(shared_flag); self } /// Set the `-static` flag. /// /// When enabled on systems that support dynamic linking, this prevents /// linking with the shared libraries. /// /// # Example /// /// ```no_run /// cc::Build::new() /// .file("src/foo.c") /// .shared_flag(true) /// .static_flag(true) /// .compile("foo"); /// ``` 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 `libc++` for OS X and BSDs, `libc++_shared` for Android, /// `None` for MSVC and `libstdc++` 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 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 target this configuration will be compiling for. /// /// This option is automatically scraped from the `TARGET` environment /// variable by build scripts, so it's not required to call this function. /// /// # 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); 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 detect environment /// changes that should trigger a rebuild. /// /// 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 } #[doc(hidden)] pub fn __set_env(&mut self, a: A, b: B) -> &mut Build where A: AsRef, B: AsRef, { self.env.push((a.as_ref().into(), b.as_ref().into())); self } /// 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.contains("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()? { self.cargo_output .print_metadata(&format_args!("cargo:rustc-link-lib={}", stdlib.display())); } // Link c++ lib from WASI sysroot if Build::is_wasi_target(target.as_ref()) { 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(), target )); } } } 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(".."); let target_arch = self.getenv_unwrap_str("CARGO_CFG_TARGET_ARCH")?; if cfg!(target_os = "linux") { libdir.push("targets"); libdir.push(target_arch.to_owned() + "-linux"); libdir.push("lib"); libtst = true; } else if cfg!(target_env = "msvc") { libdir.push("lib"); match target_arch.as_str() { "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()) } #[cfg(feature = "parallel")] fn compile_objects(&self, objs: &[Object]) -> Result<(), Error> { use std::cell::Cell; use parallel::async_executor::{block_on, YieldOnce}; if objs.len() <= 1 { for obj in objs { let (mut cmd, name) = self.create_compile_object_cmd(obj)?; run(&mut cmd, &name, &self.cargo_output)?; } return Ok(()); } // Limit our parallelism globally with a jobserver. let mut tokens = parallel::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, Cow<'static, Path>, KillOnDrop, parallel::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, program, child, _token)| { match try_wait_on_child( cmd, program, &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 self.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 obj in objs { let (mut cmd, program) = self.create_compile_object_cmd(obj)?; let token = tokens.acquire().await?; let mut child = spawn(&mut cmd, &program, &self.cargo_output)?; let mut stderr_forwarder = StderrForwarder::new(&mut child); stderr_forwarder.set_non_blocking()?; cell_update(&pendings, |mut pendings| { pendings.push((cmd, program, KillOnDrop(child, stderr_forwarder), token)); pendings }); has_made_progress.set(true); } is_disconnected.set(true); Ok::<_, Error>(()) }; return block_on(wait_future, spawn_future, &has_made_progress); 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); } } #[cfg(not(feature = "parallel"))] fn compile_objects(&self, objs: &[Object]) -> Result<(), Error> { for obj in objs { let (mut cmd, name) = self.create_compile_object_cmd(obj)?; run(&mut cmd, &name, &self.cargo_output)?; } Ok(()) } fn create_compile_object_cmd( &self, obj: &Object, ) -> Result<(Command, Cow<'static, Path>), Error> { let asm_ext = AsmFileExt::from_path(&obj.src); let is_asm = asm_ext.is_some(); let target = self.get_target()?; let msvc = target.contains("msvc"); let compiler = self.try_get_compiler()?; let clang = compiler.is_like_clang(); let gnu = compiler.family == ToolFamily::Gnu; let is_assembler_msvc = msvc && asm_ext == Some(AsmFileExt::DotAsm); let (mut cmd, name) = if is_assembler_msvc { let (cmd, name) = self.msvc_macro_assembler()?; (cmd, Cow::Borrowed(Path::new(name))) } else { let mut cmd = compiler.to_command(); for (a, b) in self.env.iter() { cmd.env(a, b); } ( cmd, compiler .path .file_name() .ok_or_else(|| Error::new(ErrorKind::IOError, "Failed to get compiler path.")) .map(PathBuf::from) .map(Cow::Owned)?, ) }; let is_arm = target.contains("aarch64") || target.contains("arm"); command_add_output_file( &mut cmd, &obj.dst, CmdAddOutputFileArgs { cuda: self.cuda, is_assembler_msvc, msvc: compiler.is_like_msvc(), clang, gnu, is_asm, is_arm, }, ); // armasm and armasm64 don't requrie -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.family == (ToolFamily::Msvc { clang_cl: true }) && !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, name)) } /// 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(); for (a, b) in self.env.iter() { cmd.env(a, b); } 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)); let name = compiler .path .file_name() .ok_or_else(|| Error::new(ErrorKind::IOError, "Failed to get compiler path."))?; run_output(&mut cmd, name, &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()?; // Disable default flag generation via `no_default_flags` or environment variable let no_defaults = self.no_default_flags || self.getenv("CRATE_CC_NO_DEFAULTS").is_some(); if !no_defaults { self.add_default_flags(&mut cmd, &target, &opt_level)?; } 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 let Ok(flags) = self.envflags(if self.cpp { "CXXFLAGS" } else { "CFLAGS" }) { for arg in flags { cmd.push_cc_arg(arg.into()); } } // 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. if self.warnings.unwrap_or(!self.has_flags()) { let wflags = cmd.family.warnings_flags().into(); cmd.push_cc_arg(wflags); } if self.extra_warnings.unwrap_or(!self.has_flags()) { if let Some(wflags) = cmd.family.extra_warnings_flags() { cmd.push_cc_arg(wflags.into()); } } 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.path, &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()); } } 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); } Ok(cmd) } fn add_default_flags( &self, cmd: &mut Tool, target: &str, opt_level: &str, ) -> Result<(), Error> { // 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 = self.getenv("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.contains("windows") { // Disambiguate mingw and msvc on Windows. Problem is that // depending on the origin clang can default to a mismatchig // run-time. cmd.push_cc_arg(format!("--target={}", target).into()); } if cmd.is_like_clang() && target.contains("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.contains("apple-ios") && !target.contains("apple-watchos") && !target.contains("apple-tvos") && !target.contains("apple-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 if self.pic.unwrap_or( !target.contains("windows") && !target.contains("-none-") && !target.contains("uefi"), ) { cmd.push_cc_arg("-fPIC".into()); // PLT only applies if code is compiled with PIC support, // and only for ELF targets. if target.contains("linux") && !self.use_plt.unwrap_or(true) { cmd.push_cc_arg("-fno-plt".into()); } } if Build::is_wasi_target(target) { // WASI does not support exceptions yet. // https://github.com/WebAssembly/exception-handling cmd.push_cc_arg("-fno-exceptions".into()); // Link clang sysroot if let Ok(wasi_sysroot) = self.wasi_sysroot() { cmd.push_cc_arg( format!("--sysroot={}", Path::new(&wasi_sysroot).display()).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_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.contains("i686") || target.contains("i586") { cmd.args.push("-m32".into()); } else if target == "x86_64-unknown-linux-gnux32" { cmd.args.push("-mx32".into()); } else if target.contains("x86_64") || target.contains("powerpc64") { cmd.args.push("-m64".into()); } } // Target flags match cmd.family { ToolFamily::Clang { .. } => { if !(cmd.has_internal_target_arg || (target.contains("android") && android_clang_compiler_uses_target_arg_internally(&cmd.path))) { let (arch, rest) = target.split_once('-').ok_or_else(|| { Error::new( ErrorKind::InvalidTarget, format!("Invalid target `{}`: no `-` in it", target), ) })?; if target.contains("darwin") { if let Some(arch) = map_darwin_target_from_rust_to_compiler_architecture(target) { cmd.args .push(format!("--target={}-apple-darwin", arch).into()); } } else if target.contains("macabi") { if let Some(arch) = map_darwin_target_from_rust_to_compiler_architecture(target) { cmd.args .push(format!("--target={}-apple-ios-macabi", arch).into()); } } else if target.contains("ios-sim") { if let Some(arch) = map_darwin_target_from_rust_to_compiler_architecture(target) { let sdk_details = apple_os_sdk_parts(AppleOs::Ios, &AppleArchSpec::Simulator("")); let deployment_target = self.apple_deployment_version(AppleOs::Ios, None, &sdk_details.sdk); cmd.args.push( format!( "--target={}-apple-ios{}-simulator", arch, deployment_target ) .into(), ); } } else if target.contains("watchos-sim") { if let Some(arch) = map_darwin_target_from_rust_to_compiler_architecture(target) { let sdk_details = apple_os_sdk_parts(AppleOs::WatchOs, &AppleArchSpec::Simulator("")); let deployment_target = self.apple_deployment_version( AppleOs::WatchOs, None, &sdk_details.sdk, ); cmd.args.push( format!( "--target={}-apple-watchos{}-simulator", arch, deployment_target ) .into(), ); } } else if target.contains("tvos-sim") || target.contains("x86_64-apple-tvos") { if let Some(arch) = map_darwin_target_from_rust_to_compiler_architecture(target) { let sdk_details = apple_os_sdk_parts(AppleOs::TvOs, &AppleArchSpec::Simulator("")); let deployment_target = self.apple_deployment_version( AppleOs::TvOs, None, &sdk_details.sdk, ); cmd.args.push( format!( "--target={}-apple-tvos{}-simulator", arch, deployment_target ) .into(), ); } } else if target.contains("aarch64-apple-tvos") { if let Some(arch) = map_darwin_target_from_rust_to_compiler_architecture(target) { let sdk_details = apple_os_sdk_parts(AppleOs::TvOs, &AppleArchSpec::Device("")); let deployment_target = self.apple_deployment_version( AppleOs::TvOs, None, &sdk_details.sdk, ); cmd.args.push( format!("--target={}-apple-tvos{}", arch, deployment_target).into(), ); } } else if target.contains("visionos-sim") { if let Some(arch) = map_darwin_target_from_rust_to_compiler_architecture(target) { let sdk_details = apple_os_sdk_parts( AppleOs::VisionOS, &AppleArchSpec::Simulator(""), ); let deployment_target = self.apple_deployment_version( AppleOs::VisionOS, None, &sdk_details.sdk, ); cmd.args.push( format!( "--target={}-apple-xros{}-simulator", arch, deployment_target ) .into(), ); } } else if target.contains("visionos") { if let Some(arch) = map_darwin_target_from_rust_to_compiler_architecture(target) { let sdk_details = apple_os_sdk_parts(AppleOs::VisionOS, &AppleArchSpec::Device("")); let deployment_target = self.apple_deployment_version( AppleOs::VisionOS, None, &sdk_details.sdk, ); cmd.args.push( format!("--target={}-apple-xros{}", arch, deployment_target).into(), ); } } else if let Ok(index) = target_info::RISCV_ARCH_MAPPING .binary_search_by_key(&arch, |(arch, _)| arch) { cmd.args.push( format!( "--target={}-{}", target_info::RISCV_ARCH_MAPPING[index].1, rest ) .into(), ); } else if target.contains("uefi") { if target.contains("x86_64") { cmd.args.push("--target=x86_64-unknown-windows-gnu".into()); } else if target.contains("i686") { cmd.args.push("--target=i686-unknown-windows-gnu".into()) } else if target.contains("aarch64") { cmd.args.push("--target=aarch64-unknown-windows-gnu".into()) } } else if target.ends_with("-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 triple 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 triple // 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()); } cmd.push_cc_arg(format!("--target={}", target).into()); } else if let Ok(index) = target_info::WINDOWS_TRIPLE_MAPPING .binary_search_by_key(&target, |(target, _)| target) { cmd.args.push( format!( "--target={}-{}", target_info::WINDOWS_TRIPLE_MAPPING[index].1, rest ) .into(), ) } else { cmd.push_cc_arg(format!("--target={}", 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 { if target.contains("x86_64") { cmd.push_cc_arg("-m64".into()); } else if target.contains("86") { cmd.push_cc_arg("-m32".into()); cmd.push_cc_arg("-arch:IA32".into()); } else { cmd.push_cc_arg(format!("--target={}", target).into()); } } else if target.contains("i586") { cmd.push_cc_arg("-arch:IA32".into()); } else if target.contains("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.contains("arm") || target.contains("thumb") { cmd.args .push("-D_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1".into()); } } ToolFamily::Gnu => { if target.contains("darwin") { if let Some(arch) = map_darwin_target_from_rust_to_compiler_architecture(target) { cmd.args.push("-arch".into()); cmd.args.push(arch.into()); } } if target.contains("-kmc-solid_") { cmd.args.push("-finput-charset=utf-8".into()); } if self.static_flag.is_none() { let features = self.getenv("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.starts_with("armv7") || target.starts_with("thumbv7")) && (target.contains("-linux-") || target.contains("-kmc-solid_")) { cmd.args.push("-march=armv7-a".into()); if target.ends_with("eabihf") { // lowest common denominator FPU cmd.args.push("-mfpu=vfpv3-d16".into()); } } // (x86 Android doesn't say "eabi") if target.contains("-androideabi") && target.contains("v7") { // -march=armv7-a handled above cmd.args.push("-mthumb".into()); if !target.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.contains("neon") { cmd.args.push("-mfpu=neon-vfpv4".into()); } if target.starts_with("armv4t-unknown-linux-") { cmd.args.push("-march=armv4t".into()); cmd.args.push("-marm".into()); cmd.args.push("-mfloat-abi=soft".into()); } if target.starts_with("armv5te-unknown-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.starts_with("arm-unknown-linux-") { cmd.args.push("-march=armv6".into()); cmd.args.push("-marm".into()); if target.ends_with("hf") { cmd.args.push("-mfpu=vfp".into()); } else { cmd.args.push("-mfloat-abi=soft".into()); } } // We can guarantee some settings for FRC if target.starts_with("arm-frc-") { cmd.args.push("-march=armv7-a".into()); cmd.args.push("-mcpu=cortex-a9".into()); cmd.args.push("-mfpu=vfpv3".into()); cmd.args.push("-mfloat-abi=softfp".into()); cmd.args.push("-marm".into()); } // Turn codegen down on i586 to avoid some instructions. if target.starts_with("i586-unknown-linux-") { cmd.args.push("-march=pentium".into()); } // Set codegen level for i686 correctly if target.starts_with("i686-unknown-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 == "i686-unknown-linux-musl" || target == "i586-unknown-linux-musl" { cmd.args.push("-Wl,-melf_i386".into()); } if target.starts_with("thumb") { cmd.args.push("-mthumb".into()); if target.ends_with("eabihf") { cmd.args.push("-mfloat-abi=hard".into()) } } if target.starts_with("thumbv6m") { cmd.args.push("-march=armv6s-m".into()); } if target.starts_with("thumbv7em") { cmd.args.push("-march=armv7e-m".into()); if target.ends_with("eabihf") { cmd.args.push("-mfpu=fpv4-sp-d16".into()) } } if target.starts_with("thumbv7m") { cmd.args.push("-march=armv7-m".into()); } if target.starts_with("thumbv8m.base") { cmd.args.push("-march=armv8-m.base".into()); } if target.starts_with("thumbv8m.main") { cmd.args.push("-march=armv8-m.main".into()); if target.ends_with("eabihf") { cmd.args.push("-mfpu=fpv5-sp-d16".into()) } } if target.starts_with("armebv7r") | target.starts_with("armv7r") { if target.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.ends_with("eabihf") { // Calling convention cmd.args.push("-mfloat-abi=hard".into()); // lowest common denominator FPU // (see Cortex-R4 technical reference manual) cmd.args.push("-mfpu=vfpv3-d16".into()) } else { // Calling convention cmd.args.push("-mfloat-abi=soft".into()); } } if target.starts_with("armv7a") { cmd.args.push("-march=armv7-a".into()); if target.ends_with("eabihf") { // lowest common denominator FPU cmd.args.push("-mfpu=vfpv3-d16".into()); } } if target.starts_with("riscv32") || target.starts_with("riscv64") { // get the 32i/32imac/32imc/64gc/64imac/... part let mut parts = target.split('-'); if let Some(arch) = parts.next() { let arch = &arch[5..]; if arch.starts_with("64") { if target.contains("linux") | target.contains("freebsd") | target.contains("netbsd") | target.contains("linux") { 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.contains("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 target.contains("-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 has_flags(&self) -> bool { let flags_env_var_name = if self.cpp { "CXXFLAGS" } else { "CFLAGS" }; let flags_env_var_value = self.getenv_with_target_prefixes(flags_env_var_name); flags_env_var_value.is_ok() } fn msvc_macro_assembler(&self) -> Result<(Command, &'static str), Error> { let target = self.get_target()?; let tool = if target.contains("x86_64") { "ml64.exe" } else if target.contains("arm") { "armasm.exe" } else if target.contains("aarch64") { "armasm64.exe" } else { "ml.exe" }; let mut cmd = self .windows_registry_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 target.contains("aarch64") || target.contains("arm") { if self.get_debug() { cmd.arg("-g"); } 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!("{} SETA {}", key, i)); } else if value.starts_with('"') && value.ends_with('"') { cmd.arg(format!("{} SETS {}", key, value)); } else { cmd.arg(format!("{} SETS \"{}\"", key, 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.contains("i686") || target.contains("i586") { cmd.arg("-safeseh"); } Ok((cmd, tool)) } 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. let objs: Vec<_> = objs .iter() .map(|o| o.dst.as_path()) .chain(self.objects.iter().map(std::ops::Deref::deref)) .collect(); for chunk in objs.chunks(100) { self.assemble_progressive(dst, chunk)?; } 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, "nvcc", &self.cargo_output)?; self.assemble_progressive(dst, &[dlink.as_path()])?; } let target = self.get_target()?; if target.contains("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", lib_name)); 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, cmd, _any_flags) = self.get_ar()?; // 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. run(ar.arg("s").arg(dst), &cmd, &self.cargo_output)?; } Ok(()) } fn assemble_progressive(&self, dst: &Path, objs: &[&Path]) -> Result<(), Error> { let target = self.get_target()?; let (mut cmd, program, any_flags) = self.get_ar()?; if target.contains("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, &program, &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. run( cmd.arg("cq").arg(dst).args(objs), &program, &self.cargo_output, )?; } Ok(()) } fn apple_flags(&self, cmd: &mut Tool) -> Result<(), Error> { let target = self.get_target()?; let os = if target.contains("-darwin") { AppleOs::MacOs } else if target.contains("-watchos") { AppleOs::WatchOs } else if target.contains("-tvos") { AppleOs::TvOs } else if target.contains("-visionos") { AppleOs::VisionOS } else { AppleOs::Ios }; let is_mac = matches!(os, AppleOs::MacOs); let arch_str = target.split('-').nth(0).ok_or_else(|| { Error::new( ErrorKind::ArchitectureInvalid, format!("Unknown architecture for {:?} target.", os), ) })?; let is_catalyst = match target.split('-').nth(3) { Some(v) => v == "macabi", None => false, }; let is_arm_sim = match target.split('-').nth(3) { Some(v) => v == "sim", None => false, }; let arch = if is_mac { match arch_str { "i686" => AppleArchSpec::Device("-m32"), "x86_64" | "x86_64h" | "aarch64" => AppleArchSpec::Device("-m64"), _ => { return Err(Error::new( ErrorKind::ArchitectureInvalid, "Unknown architecture for macOS target.", )); } } } else if is_catalyst { match arch_str { "arm64e" => AppleArchSpec::Catalyst("arm64e"), "arm64" | "aarch64" => AppleArchSpec::Catalyst("arm64"), "x86_64" | "x86_64h" => AppleArchSpec::Catalyst("-m64"), _ => { return Err(Error::new( ErrorKind::ArchitectureInvalid, "Unknown architecture for iOS target.", )); } } } else if is_arm_sim { match arch_str { "arm64" | "aarch64" => AppleArchSpec::Simulator("arm64"), "x86_64" | "x86_64h" => AppleArchSpec::Simulator("-m64"), _ => { return Err(Error::new( ErrorKind::ArchitectureInvalid, "Unknown architecture for simulator target.", )); } } } else { match arch_str { "arm" | "armv7" | "thumbv7" => AppleArchSpec::Device("armv7"), "armv7k" => AppleArchSpec::Device("armv7k"), "armv7s" | "thumbv7s" => AppleArchSpec::Device("armv7s"), "arm64e" => AppleArchSpec::Device("arm64e"), "arm64" | "aarch64" => AppleArchSpec::Device("arm64"), "arm64_32" => AppleArchSpec::Device("arm64_32"), "i386" | "i686" => AppleArchSpec::Simulator("-m32"), "x86_64" | "x86_64h" => AppleArchSpec::Simulator("-m64"), _ => { return Err(Error::new( ErrorKind::ArchitectureInvalid, format!("Unknown architecture for {:?} target.", os), )); } } }; let sdk_details = apple_os_sdk_parts(os, &arch); let min_version = self.apple_deployment_version(os, Some(arch_str), &sdk_details.sdk); match arch { AppleArchSpec::Device(_) if is_mac => { cmd.args .push(format!("-mmacosx-version-min={}", min_version).into()); } AppleArchSpec::Device(arch) => { cmd.args.push("-arch".into()); cmd.args.push(arch.into()); // `-mxros-version-min` does not exist // https://github.com/llvm/llvm-project/issues/88271 if os != AppleOs::VisionOS { cmd.args.push( format!("-m{}os-version-min={}", sdk_details.sdk_prefix, min_version) .into(), ); } } AppleArchSpec::Simulator(arch) => { if arch.starts_with('-') { // -m32 or -m64 cmd.args.push(arch.into()); } else { cmd.args.push("-arch".into()); cmd.args.push(arch.into()); } if os != AppleOs::VisionOS { cmd.args.push( format!( "-m{}simulator-version-min={}", sdk_details.sim_prefix, min_version ) .into(), ); } } AppleArchSpec::Catalyst(_) => {} }; // AppleClang sometimes requires sysroot even for darwin if cmd.is_xctoolchain_clang() || !target.ends_with("-darwin") { self.cargo_output.print_metadata(&format_args!( "Detecting {:?} SDK path for {}", os, sdk_details.sdk )); let sdk_path = self.apple_sdk_root(&sdk_details.sdk)?; cmd.args.push("-isysroot".into()); cmd.args.push(OsStr::new(&sdk_path).to_owned()); if let AppleArchSpec::Catalyst(_) = arch { // 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 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.cached_compiler_family, &self.cargo_output, out_dir, )); } let host = self.get_host()?; let target = self.get_target()?; let target = &*target; let (env, msvc, gnu, traditional, clang) = if self.cpp { ("CXX", "cl.exe", "g++", "c++", "clang++") } else { ("CC", "cl.exe", "gcc", "cc", "clang") }; // 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". let default = if host.contains("solaris") || host.contains("illumos") { gnu } else { traditional }; let cl_exe = self.windows_registry_find_tool(target, "cl.exe"); let tool_opt: Option = self .env_tool(env) .map(|(tool, wrapper, args)| { // find the driver mode, if any const DRIVER_MODE: &str = "--driver-mode="; let driver_mode = args .iter() .find(|a| a.starts_with(DRIVER_MODE)) .map(|a| &a[DRIVER_MODE.len()..]); // 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_clang_driver( tool, driver_mode, &self.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.contains("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!("{}.bat", tool).into()); Some(t) } else { Some(Tool::new( PathBuf::from(tool), &self.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 = if host.contains("windows") && target.contains("windows") { if target.contains("msvc") { msvc.to_string() } else { let cc = if target.contains("llvm") { clang } else { gnu }; format!("{}.exe", cc) } } else if target.contains("apple-ios") | target.contains("apple-watchos") | target.contains("apple-tvos") | target.contains("apple-visionos") { clang.to_string() } else if target.contains("android") { autodetect_android_compiler(target, &host, gnu, clang) } else if target.contains("cloudabi") { format!("{}-{}", target, traditional) } else if Build::is_wasi_target(target) { if self.cpp { "clang++".to_string() } else { "clang".to_string() } } else if target.contains("vxworks") { if self.cpp { "wr-c++".to_string() } else { "wr-cc".to_string() } } else if target.starts_with("armv7a-kmc-solid_") { format!("arm-kmc-eabi-{}", gnu) } else if target.starts_with("aarch64-kmc-solid_") { format!("aarch64-kmc-elf-{}", gnu) } else if &*self.get_host()? != target { let prefix = self.prefix_for_target(target); match prefix { Some(prefix) => { let cc = if target.contains("llvm") { clang } else { gnu }; format!("{}-{}", prefix, cc) } None => default.to_string(), } } else { default.to_string() }; let mut t = Tool::new( PathBuf::from(compiler), &self.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, None, self.cuda, &self.cached_compiler_family, &self.cargo_output, out_dir, ); if self.ccbin { nvcc_tool .args .push(format!("-ccbin={}", tool.path.display()).into()); } 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 host.contains("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()); } } } }; } // 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.contains("msvc") { for (k, v) in cl_exe.env.iter() { tool.env.push((k.to_owned(), v.to_owned())); } } } if target.contains("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"]; let rustc_wrapper = self.getenv("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(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 Path::new(tool).exists() { return Some(( PathBuf::from(tool), 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"]; let custom_wrapper = self.getenv("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 = match parts.next() { Some(s) => s, None => return None, }; 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(Arc::::from(OsStr::new(&maybe_wrapper))), 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 `libc++` for OS X and BSDs, `libc++_shared` for Android, /// `None` for MSVC and `libstdc++` 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.contains("msvc") { Ok(None) } else if target.contains("apple") | target.contains("freebsd") | target.contains("openbsd") | target.contains("aix") | target.contains("linux-ohos") | target.contains("-wasi") { Ok(Some(Cow::Borrowed(Path::new("c++")))) } else if target.contains("android") { Ok(Some(Cow::Borrowed(Path::new("c++_shared")))) } else { Ok(Some(Cow::Borrowed(Path::new("stdc++")))) } } } } } fn get_ar(&self) -> Result<(Command, PathBuf, bool), Error> { self.try_get_archiver_and_flags() } /// 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 Ok(flags) = self.envflags("ARFLAGS") { any_flags |= !flags.is_empty(); 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 Ok(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.contains("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{}.bat", tool).into(); cmd.arg("/c").arg(&name); Some(cmd) } else { name = format!("em{}", tool).into(); Some(self.cmd(&name)) } } else if target.starts_with("wasm32") { // 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( &mut self.cmd(&compiler.path), &name, &self.cargo_output, ) .map(|name| self.cmd(name)) } else { None } } else { None } }); let default = tool.to_string(); let tool = match tool_opt { Some(t) => t, None => { if target.contains("android") { name = format!("llvm-{}", tool).into(); match Command::new(&name).arg("--version").status() { Ok(status) if status.success() => (), _ => name = format!("{}-{}", target.replace("armv7", "arm"), tool).into(), } self.cmd(&name) } else if target.contains("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 mut lib = String::new(); if compiler.family == (ToolFamily::Msvc { clang_cl: true }) { // See if there is 'llvm-lib' next to 'clang-cl' // Another possibility could be to see if there is 'clang' // next to 'clang-cl' and use 'search_programs()' to locate // 'llvm-lib'. This is because 'clang-cl' doesn't support // the -print-search-dirs option. if let Some(mut cmd) = self.which(&compiler.path, None) { cmd.pop(); cmd.push("llvm-lib.exe"); if let Some(llvm_lib) = self.which(&cmd, None) { llvm_lib.to_str().unwrap().clone_into(&mut lib); } } } if lib.is_empty() { name = PathBuf::from("lib.exe"); let mut cmd = match self.windows_registry_find(&target, "lib.exe") { Some(t) => t, None => self.cmd("lib.exe"), }; if target.contains("arm64ec") { cmd.arg("/machine:arm64ec"); } cmd } else { name = lib.into(); self.cmd(&name) } } else if target.contains("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 self.get_host()? != target { match self.prefix_for_target(&target) { Some(p) => { // 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 mut chosen = default; for &infix in &["", "-gcc"] { let target_p = format!("{}{}-{}", p, infix, tool); if Command::new(&target_p).output().is_ok() { chosen = target_p; break; } } name = chosen.into(); self.cmd(&name) } None => { name = default.into(); self.cmd(&name) } } } else { name = default.into(); self.cmd(&name) } } }; Ok((tool, name)) } fn prefix_for_target(&self, target: &str) -> Option> { // CROSS_COMPILE is of the form: "arm-linux-gnueabi-" self.getenv("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 self.getenv("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-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-linux-gnueabi" => Some("arm-linux-gnueabi"), "armv5te-unknown-linux-musleabi" => Some("arm-linux-gnueabi"), "arm-frc-linux-gnueabi" => Some("arm-frc-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-uwp-windows-gnu" => Some("i686-w64-mingw32"), "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"), "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-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("powerpc-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", ]), "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", ]), "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"), "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-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-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" => Some("musl"), "x86_64-unknown-netbsd" => Some("x86_64--netbsd"), _ => 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.getenv("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) => Ok(Cow::Borrowed(t)), None => self.getenv_unwrap_str("TARGET").map(Cow::Owned), } } fn get_host(&self) -> Result, Error> { match &self.host { Some(h) => Ok(Cow::Borrowed(h)), None => self.getenv_unwrap_str("HOST").map(Cow::Owned), } } fn get_opt_level(&self) -> Result, Error> { match &self.opt_level { Some(ol) => Ok(Cow::Borrowed(ol)), None => self.getenv_unwrap_str("OPT_LEVEL").map(Cow::Owned), } } fn get_debug(&self) -> bool { self.debug.unwrap_or_else(|| match self.getenv("DEBUG") { Some(s) => &*s != "false", None => false, }) } fn get_shell_escaped_flags(&self) -> bool { self.shell_escaped_flags .unwrap_or_else(|| self.getenv("CC_SHELL_ESCAPED_FLAGS").is_some()) } fn get_dwarf_version(&self) -> Option { // Tentatively matches the DWARF version defaults as of rustc 1.62. let target = self.get_target().ok()?; if target.contains("android") || target.contains("apple") || target.contains("dragonfly") || target.contains("freebsd") || target.contains("netbsd") || target.contains("openbsd") || target.contains("windows-gnu") { Some(2) } else if target.contains("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 => self .getenv("OUT_DIR") .as_deref() .map(PathBuf::from) .map(Cow::Owned) .ok_or_else(|| { Error::new( ErrorKind::EnvVarNotFound, "Environment variable OUT_DIR not defined.", ) }), } } #[allow(clippy::disallowed_methods)] fn getenv(&self, v: &str) -> Option> { // Returns true for environment variables cargo sets for build scripts: // https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-build-scripts // // This handles more of the vars than we actually use (it tries to check // complete-ish set), just to avoid needing maintenance if/when new // calls to `getenv`/`getenv_unwrap` are added. fn provided_by_cargo(envvar: &str) -> bool { match envvar { v if v.starts_with("CARGO") || v.starts_with("RUSTC") => true, "HOST" | "TARGET" | "RUSTDOC" | "OUT_DIR" | "OPT_LEVEL" | "DEBUG" | "PROFILE" | "NUM_JOBS" | "RUSTFLAGS" => true, _ => false, } } if let Some(val) = self.env_cache.read().unwrap().get(v).cloned() { return val; } if self.emit_rerun_if_env_changed && !provided_by_cargo(v) { self.cargo_output .print_metadata(&format_args!("cargo:rerun-if-env-changed={}", v)); } let r = env::var_os(v).map(Arc::from); self.cargo_output.print_metadata(&format_args!( "{} = {}", v, OptionOsStrDisplay(r.as_deref()) )); self.env_cache.write().unwrap().insert(v.into(), r.clone()); r } fn getenv_unwrap(&self, v: &str) -> Result, Error> { match self.getenv(v) { Some(s) => Ok(s), None => Err(Error::new( ErrorKind::EnvVarNotFound, format!("Environment variable {} not defined.", v), )), } } fn getenv_unwrap_str(&self, v: &str) -> Result { let env = self.getenv_unwrap(v)?; env.to_str().map(String::from).ok_or_else(|| { Error::new( ErrorKind::EnvVarNotFound, format!("Environment variable {} is not valid utf-8.", v), ) }) } fn getenv_with_target_prefixes(&self, var_base: &str) -> Result, Error> { let target = self.get_target()?; let host = self.get_host()?; let kind = if host == target { "HOST" } else { "TARGET" }; let target_u = target.replace('-', "_"); let res = self .getenv(&format!("{}_{}", var_base, target)) .or_else(|| self.getenv(&format!("{}_{}", var_base, target_u))) .or_else(|| self.getenv(&format!("{}_{}", kind, var_base))) .or_else(|| self.getenv(var_base)); match res { Some(res) => Ok(res), None => Err(Error::new( ErrorKind::EnvVarNotFound, format!("Could not find environment variable {}.", var_base), )), } } fn envflags(&self, name: &str) -> Result, Error> { let env_os = self.getenv_with_target_prefixes(name)?; let env = env_os.to_string_lossy(); if self.get_shell_escaped_flags() { Ok(Shlex::new(&env).collect()) } else { Ok(env .split_ascii_whitespace() .map(ToString::to_string) .collect()) } } fn fix_env_for_apple_os(&self, cmd: &mut Command) -> Result<(), Error> { let target = self.get_target()?; let host = self.get_host()?; if host.contains("apple-darwin") && target.contains("apple-darwin") { // 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.getenv("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), "xcrun", &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(Arc::from(OsStr::new(sdk_path.trim()))) } fn apple_sdk_root(&self, sdk: &str) -> Result, Error> { if let Some(ret) = self .apple_sdk_root_cache .read() .expect("apple_sdk_root_cache lock failed") .get(sdk) .cloned() { return Ok(ret); } let sdk_path = self.apple_sdk_root_inner(sdk)?; self.apple_sdk_root_cache .write() .expect("apple_sdk_root_cache lock failed") .insert(sdk.into(), sdk_path.clone()); Ok(sdk_path) } fn apple_deployment_version(&self, os: AppleOs, arch_str: Option<&str>, sdk: &str) -> Arc { if let Some(ret) = self .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), "xcrun", &self.cargo_output, ) .ok()?; Some(Arc::from(std::str::from_utf8(&version).ok()?.trim())) }; let deployment_from_env = |name: &str| -> Option> { // note that self.env isn't hit in production codepaths, its mostly just for tests which don't // set the real env self.env .iter() .find(|(k, _)| &**k == OsStr::new(name)) .map(|(_, v)| v) .cloned() .or_else(|| self.getenv(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 os { AppleOs::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 ({}) too low, it will be increased", deployment_target_ver )); return None; } } AppleOs::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 ({}) too low, it will be increased", deployment_target_ver )); 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 os { AppleOs::MacOs => deployment_from_env("MACOSX_DEPLOYMENT_TARGET") .and_then(maybe_cpp_version_baseline) .or_else(default_deployment_from_sdk) .unwrap_or_else(|| { if arch_str == Some("aarch64") { "11.0".into() } else { let default: Arc = Arc::from("10.7"); maybe_cpp_version_baseline(default.clone()).unwrap_or(default) } }), AppleOs::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()), AppleOs::WatchOs => deployment_from_env("WATCHOS_DEPLOYMENT_TARGET") .or_else(default_deployment_from_sdk) .unwrap_or_else(|| "5.0".into()), AppleOs::TvOs => deployment_from_env("TVOS_DEPLOYMENT_TARGET") .or_else(default_deployment_from_sdk) .unwrap_or_else(|| "9.0".into()), AppleOs::VisionOS => deployment_from_env("XROS_DEPLOYMENT_TARGET") .or_else(default_deployment_from_sdk) .unwrap_or_else(|| "1.0".into()), }; self.apple_versions_cache .write() .expect("apple_versions_cache lock failed") .insert(sdk.into(), version.clone()); version } fn wasi_sysroot(&self) -> Result, Error> { if let Some(wasi_sysroot_path) = self.getenv("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 is_wasi_target(target: &str) -> bool { const TARGETS: [&str; 7] = [ "wasm32-wasi", "wasm32-wasip1", "wasm32-wasip1-threads", "wasm32-wasip2", "wasm32-wasi-threads", "wasm32-unknown-wasi", "wasm32-unknown-unknown", ]; TARGETS.contains(&target) } 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 { 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) } // 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.getenv("PATH")?)) } } /// search for |prog| on 'programs' path in '|cc| -print-search-dirs' output fn search_programs( &self, cc: &mut Command, prog: &Path, cargo_output: &CargoOutput, ) -> Option { let search_dirs = run_output( cc.arg("-print-search-dirs"), "cc", // 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 windows_registry_find(&self, target: &str, tool: &str) -> Option { self.windows_registry_find_tool(target, tool) .map(|c| c.to_command()) } fn windows_registry_find_tool(&self, target: &str, tool: &str) -> Option { struct BuildEnvGetter<'s>(&'s Build); impl windows_registry::EnvGetter for BuildEnvGetter<'_> { fn get_env(&self, name: &str) -> Option { self.0.getenv(name).map(windows_registry::Env::Arced) } } windows_registry::find_tool_inner(target, tool, &BuildEnvGetter(self)) } } impl Default for Build { fn default() -> Build { Build::new() } } fn fail(s: &str) -> ! { eprintln!("\n\nerror occurred: {}\n\n", s); std::process::exit(1); } #[derive(Clone, Copy, PartialEq)] enum AppleOs { MacOs, Ios, WatchOs, TvOs, VisionOS, } impl std::fmt::Debug for AppleOs { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { AppleOs::MacOs => f.write_str("macOS"), AppleOs::Ios => f.write_str("iOS"), AppleOs::WatchOs => f.write_str("WatchOS"), AppleOs::TvOs => f.write_str("AppleTVOS"), AppleOs::VisionOS => f.write_str("visionOS"), } } } struct AppleSdkTargetParts { sdk_prefix: &'static str, sim_prefix: &'static str, sdk: Cow<'static, str>, } fn apple_os_sdk_parts(os: AppleOs, arch: &AppleArchSpec) -> AppleSdkTargetParts { let (sdk_prefix, sim_prefix) = match os { AppleOs::MacOs => ("macosx", ""), AppleOs::Ios => ("iphone", "ios-"), AppleOs::WatchOs => ("watch", "watch"), AppleOs::TvOs => ("appletv", "appletv"), AppleOs::VisionOS => ("xr", "xr"), }; let sdk = match arch { AppleArchSpec::Device(_) if os == AppleOs::MacOs => Cow::Borrowed("macosx"), AppleArchSpec::Device(_) => format!("{}os", sdk_prefix).into(), AppleArchSpec::Simulator(_) => format!("{}simulator", sdk_prefix).into(), AppleArchSpec::Catalyst(_) => Cow::Borrowed("macosx"), }; AppleSdkTargetParts { sdk_prefix, sim_prefix, sdk, } } #[allow(dead_code)] enum AppleArchSpec { Device(&'static str), Simulator(&'static str), #[allow(dead_code)] Catalyst(&'static str), } // 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 autodetect_android_compiler(target: &str, host: &str, gnu: &str, clang: &str) -> String { let new_clang_key = match 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 = 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!("{}-{}.cmd", target, clang); // Check if gnu compiler is present // if not, use clang if Command::new(&gnu_compiler).output().is_ok() { gnu_compiler } else if host.contains("windows") && Command::new(&clang_compiler_cmd).output().is_ok() { clang_compiler_cmd } else { clang_compiler } } // Rust and clang/cc don't agree on how to name the target. fn map_darwin_target_from_rust_to_compiler_architecture(target: &str) -> Option<&'static str> { if target.contains("x86_64h") { Some("x86_64h") } else if target.contains("x86_64") { Some("x86_64") } else if target.contains("arm64e") { Some("arm64e") } else if target.contains("aarch64") { Some("arm64") } else if target.contains("i686") { Some("i386") } else if target.contains("powerpc") { Some("ppc") } else if target.contains("powerpc64") { Some("ppc64") } else { None } } #[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 } } #[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.1.14/src/parallel/async_executor.rs000064400000000000000000000067761046102023000163010ustar 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.1.14/src/parallel/job_token.rs000064400000000000000000000227061046102023000152070ustar 00000000000000use std::marker::PhantomData; use crate::Error; use super::once_lock::OnceLock; 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<'a> ActiveJobServer<'a> { 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; use std::{ env::var, 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 a semi-reasonable number. // // Note that we could use `num_cpus` here but it's an extra // dependency that will almost never be used, so // it's generally not too worth it. let mut parallelism = 4; // TODO: Use std::thread::available_parallelism as an upper bound // when MSRV is bumped. if let Ok(amt) = var("NUM_JOBS") { if let Ok(amt) = amt.parse() { parallelism = amt; } } 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.1.14/src/parallel/mod.rs000064400000000000000000000001521046102023000140030ustar 00000000000000pub(crate) mod async_executor; pub(crate) mod job_token; pub(crate) mod once_lock; pub(crate) mod stderr; cc-1.1.14/src/parallel/once_lock.rs000064400000000000000000000023231046102023000151620ustar 00000000000000use std::{ cell::UnsafeCell, marker::PhantomData, mem::MaybeUninit, panic::{RefUnwindSafe, UnwindSafe}, sync::Once, }; pub(crate) struct OnceLock { once: Once, value: UnsafeCell>, _marker: PhantomData, } impl OnceLock { pub(crate) const fn new() -> Self { Self { once: Once::new(), value: UnsafeCell::new(MaybeUninit::uninit()), _marker: PhantomData, } } 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.value.get()).assume_init_ref() } } } 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() }; } } } cc-1.1.14/src/parallel/stderr.rs000064400000000000000000000052521046102023000145350ustar 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 crate::windows::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.1.14/src/target_info.rs000064400000000000000000000021061046102023000137320ustar 00000000000000//! This file is generated code. Please edit the generator //! in dev-tools/gen-target-info if you need to make changes. pub const RISCV_ARCH_MAPPING: &[(&str, &str)] = &[ ("riscv32gc", "riscv32"), ("riscv32i", "riscv32"), ("riscv32im", "riscv32"), ("riscv32ima", "riscv32"), ("riscv32imac", "riscv32"), ("riscv32imafc", "riscv32"), ("riscv32imc", "riscv32"), ("riscv64gc", "riscv64"), ("riscv64imac", "riscv64"), ]; pub const WINDOWS_TRIPLE_MAPPING: &[(&str, &str)] = &[ ("aarch64-pc-windows-gnullvm", "aarch64-pc-windows-gnu"), ("aarch64-uwp-windows-msvc", "aarch64-pc-windows-msvc"), ("i686-pc-windows-gnullvm", "i686-pc-windows-gnu"), ("i686-uwp-windows-gnu", "i686-pc-windows-gnu"), ("i686-uwp-windows-msvc", "i686-pc-windows-msvc"), ("i686-win7-windows-msvc", "i686-pc-windows-msvc"), ("thumbv7a-uwp-windows-msvc", "thumbv7a-pc-windows-msvc"), ("x86_64-pc-windows-gnullvm", "x86_64-pc-windows-gnu"), ("x86_64-uwp-windows-gnu", "x86_64-pc-windows-gnu"), ("x86_64-uwp-windows-msvc", "x86_64-pc-windows-msvc"), ]; cc-1.1.14/src/tempfile.rs000064400000000000000000000043401046102023000132400ustar 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, crate::windows::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.1.14/src/tool.rs000064400000000000000000000410741046102023000124150ustar 00000000000000use std::{ borrow::Cow, collections::HashMap, env, ffi::{OsStr, OsString}, io::Write, path::{Path, PathBuf}, process::Command, sync::RwLock, }; use crate::{ command_helpers::{run_output, CargoOutput}, run, tempfile::NamedTempfile, Error, ErrorKind, OutputKind, }; /// 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 new( path: PathBuf, cached_compiler_family: &RwLock, ToolFamily>>, cargo_output: &CargoOutput, out_dir: Option<&Path>, ) -> Self { Self::with_features( path, None, false, cached_compiler_family, cargo_output, out_dir, ) } pub(crate) fn with_clang_driver( path: PathBuf, clang_driver: Option<&str>, cached_compiler_family: &RwLock, ToolFamily>>, cargo_output: &CargoOutput, out_dir: Option<&Path>, ) -> Self { Self::with_features( path, clang_driver, 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, clang_driver: Option<&str>, cuda: bool, cached_compiler_family: &RwLock, ToolFamily>>, 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"), path, // tool detection issues should always be shown as warnings cargo_output, ) .map(|o| String::from_utf8_lossy(&o).contains("ziglang")) .unwrap_or_default() } fn detect_family_inner( path: &Path, 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); let stdout = run_output( Command::new(path).arg("-E").arg(tmp.path()), path, // 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 cargo_output = cargo_output.clone(); cargo_output.warnings = cargo_output.debug; cargo_output }, )?; let stdout = String::from_utf8_lossy(&stdout); cargo_output.print_debug(&stdout); // https://gitlab.kitware.com/cmake/cmake/-/blob/69a2eeb9dff5b60f2f1e5b425002a0fd45b7cadb/Modules/CMakeDetermineCompilerId.cmake#L267-271 let accepts_cl_style_flags = run(Command::new(path).arg("-?"), path, &{ // 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""#); match (clang, accepts_cl_style_flags, gcc, emscripten) { (clang_cl, true, _, false) => Ok(ToolFamily::Msvc { clang_cl }), (true, _, _, _) | (_, _, _, true) => Ok(ToolFamily::Clang { zig_cc: is_zig_cc(path, cargo_output), }), (false, false, true, _) => Ok(ToolFamily::Gnu), (false, false, false, false) => { cargo_output.print_warning(&"Compiler family detection failed since it does not define `__clang__`, `__GNUC__` or `__EMSCRIPTEN__`, also does not accept cl style flag `-?`, fallback to treating it as GNU"); Err(Error::new( ErrorKind::ToolFamilyMacroNotFound, "Expects macro `__clang__`, `__GNUC__` or `__EMSCRIPTEN__`, or accepts cl style flag `-?`, but found none", )) } } } let detect_family = |path: &Path| -> Result { if let Some(family) = cached_compiler_family.read().unwrap().get(path) { return Ok(*family); } let family = detect_family_inner(path, cargo_output, out_dir)?; cached_compiler_family .write() .unwrap() .insert(path.into(), family); Ok(family) }; let family = detect_family(&path).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") => match clang_driver { Some("cl") => ToolFamily::Msvc { clang_cl: true }, _ => 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); let value = self .args .iter() .filter(|a| !self.removed_args.contains(a)) .collect::>(); cmd.args(&value); 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 { .. }) } } /// 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, dwarf_version: Option) { match *self { ToolFamily::Msvc { .. } => { cmd.push_cc_arg("-Z7".into()); } ToolFamily::Gnu | ToolFamily::Clang { .. } => { cmd.push_cc_arg( dwarf_version .map_or_else(|| "-g".into(), |v| 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", } } /// 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.1.14/src/utilities.rs000064400000000000000000000022701046102023000134460ustar 00000000000000use std::{ ffi::OsStr, fmt::{self, Write}, path::Path, }; 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") } } } cc-1.1.14/src/windows/com.rs000064400000000000000000000054311046102023000137050ustar 00000000000000// Copyright © 2017 winapi-rs developers // Licensed under the Apache License, Version 2.0 // or the MIT license // , at your option. // All files in the project carrying such notice may not be copied, modified, or distributed // except according to those terms. use crate::windows::{ winapi::{IUnknown, Interface}, windows_sys::{ CoInitializeEx, SysFreeString, SysStringLen, BSTR, COINIT_MULTITHREADED, HRESULT, S_FALSE, S_OK, }, }; use std::{ convert::TryInto, ffi::OsString, ops::Deref, os::windows::ffi::OsStringExt, ptr::{null, null_mut}, slice::from_raw_parts, }; pub fn initialize() -> Result<(), HRESULT> { let err = unsafe { CoInitializeEx(null(), COINIT_MULTITHREADED.try_into().unwrap()) }; if err != S_OK && err != S_FALSE { // S_FALSE just means COM is already initialized Err(err) } else { Ok(()) } } pub struct ComPtr(*mut T) where T: Interface; impl ComPtr where T: Interface, { /// Creates a `ComPtr` to wrap a raw pointer. /// It takes ownership over the pointer which means it does __not__ call `AddRef`. /// `T` __must__ be a COM interface that inherits from `IUnknown`. pub unsafe fn from_raw(ptr: *mut T) -> ComPtr { assert!(!ptr.is_null()); ComPtr(ptr) } /// For internal use only. fn as_unknown(&self) -> &IUnknown { unsafe { &*(self.0 as *mut IUnknown) } } /// Performs `QueryInterface` fun. pub fn cast(&self) -> Result, i32> where U: Interface, { let mut obj = null_mut(); let err = unsafe { self.as_unknown().QueryInterface(&U::uuidof(), &mut obj) }; if err < 0 { return Err(err); } Ok(unsafe { ComPtr::from_raw(obj as *mut U) }) } } impl Deref for ComPtr where T: Interface, { type Target = T; fn deref(&self) -> &T { unsafe { &*self.0 } } } impl Clone for ComPtr where T: Interface, { fn clone(&self) -> Self { unsafe { self.as_unknown().AddRef(); ComPtr::from_raw(self.0) } } } impl Drop for ComPtr where T: Interface, { fn drop(&mut self) { unsafe { self.as_unknown().Release(); } } } pub struct BStr(BSTR); impl BStr { pub unsafe fn from_raw(s: BSTR) -> BStr { BStr(s) } pub fn to_osstring(&self) -> OsString { let len = unsafe { SysStringLen(self.0) }; let slice = unsafe { from_raw_parts(self.0, len as usize) }; OsStringExt::from_wide(slice) } } impl Drop for BStr { fn drop(&mut self) { unsafe { SysFreeString(self.0) }; } } cc-1.1.14/src/windows/find_tools.rs000064400000000000000000001265441046102023000153000ustar 00000000000000// Copyright 2015 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! 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. #![allow(clippy::upper_case_acronyms)] use std::{ env, ffi::{OsStr, OsString}, ops::Deref, path::PathBuf, process::Command, sync::Arc, }; use crate::Tool; use crate::ToolFamily; const MSVC_FAMILY: ToolFamily = ToolFamily::Msvc { clang_cl: false }; #[derive(Copy, Clone)] struct TargetArch<'a>(pub &'a str); impl PartialEq<&str> for TargetArch<'_> { fn eq(&self, other: &&str) -> bool { self.0 == *other } } impl<'a> From> for &'a str { fn from(target: TargetArch<'a>) -> Self { target.0 } } pub(crate) enum Env { Owned(OsString), Arced(Arc), } impl AsRef for Env { fn as_ref(&self) -> &OsStr { self.deref() } } impl Deref for Env { type Target = OsStr; fn deref(&self) -> &Self::Target { match self { Env::Owned(os_str) => os_str, Env::Arced(os_str) => os_str, } } } impl From for PathBuf { fn from(env: Env) -> Self { match env { Env::Owned(os_str) => PathBuf::from(os_str), Env::Arced(os_str) => PathBuf::from(os_str.deref()), } } } pub(crate) trait EnvGetter { fn get_env(&self, name: &'static str) -> Option; } struct StdEnvGetter; impl EnvGetter for StdEnvGetter { #[allow(clippy::disallowed_methods)] fn get_env(&self, name: &'static str) -> Option { env::var_os(name).map(Env::Owned) } } /// Attempts to find a tool within an MSVC installation using the Windows /// registry as a point to search from. /// /// The `target` argument is the target that the tool should work for (e.g. /// compile or link for) and the `tool` argument is the tool to find (e.g. /// `cl.exe` or `link.exe`). /// /// 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. pub fn find(target: &str, tool: &str) -> Option { find_tool(target, tool).map(|c| c.to_command()) } /// 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` which may be introspected. pub fn find_tool(target: &str, tool: &str) -> Option { find_tool_inner(target, tool, &StdEnvGetter) } pub(crate) fn find_tool_inner( target: &str, tool: &str, env_getter: &dyn EnvGetter, ) -> Option { // This logic is all tailored for MSVC, if we're not that then bail out // early. if !target.contains("msvc") { return None; } // Split the target to get the arch. let target = TargetArch(target.split_once('-')?.0); // Looks like msbuild isn't located in the same location as other tools like // cl.exe and lib.exe. if tool.contains("msbuild") { return impl_::find_msbuild(target, env_getter); } // Looks like devenv isn't located in the same location as other tools like // cl.exe and lib.exe. if tool.contains("devenv") { return impl_::find_devenv(target, env_getter); } // Ok, if we're here, now comes the fun part of the probing. Default shells // or shells like MSYS aren't really configured to execute `cl.exe` and the // various compiler tools shipped as part of Visual Studio. Here we try to // first find the relevant tool, then we also have to be sure to fill in // environment variables like `LIB`, `INCLUDE`, and `PATH` to ensure that // the tool is actually usable. impl_::find_msvc_environment(tool, target, env_getter) .or_else(|| impl_::find_msvc_15plus(tool, target, env_getter)) .or_else(|| impl_::find_msvc_14(tool, target, env_getter)) } /// A version of Visual Studio #[derive(Debug, PartialEq, Eq, Copy, Clone)] #[non_exhaustive] pub enum VsVers { /// Visual Studio 12 (2013) #[deprecated( note = "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, } /// Find the most recent installed version of Visual Studio /// /// This is used by the cmake crate to figure out the correct /// generator. #[allow(clippy::disallowed_methods)] pub fn find_vs_version() -> Result { fn has_msbuild_version(version: &str) -> bool { impl_::has_msbuild_version(version, &StdEnvGetter) } match std::env::var("VisualStudioVersion") { Ok(version) => match &version[..] { "17.0" => Ok(VsVers::Vs17), "16.0" => Ok(VsVers::Vs16), "15.0" => Ok(VsVers::Vs15), "14.0" => Ok(VsVers::Vs14), vers => Err(format!( "\n\n\ unsupported or unknown VisualStudio version: {}\n\ if another version is installed consider running \ the appropriate vcvars script before building this \ crate\n\ ", vers )), }, _ => { // Check for the presence of a specific registry key // that indicates visual studio is installed. if has_msbuild_version("17.0") { Ok(VsVers::Vs17) } else if has_msbuild_version("16.0") { Ok(VsVers::Vs16) } else if has_msbuild_version("15.0") { Ok(VsVers::Vs15) } else if has_msbuild_version("14.0") { Ok(VsVers::Vs14) } else { Err("\n\n\ couldn't determine visual studio generator\n\ if VisualStudio is installed, however, consider \ running the appropriate vcvars script before building \ this crate\n\ " .to_string()) } } } } /// Windows Implementation. #[cfg(windows)] mod impl_ { use crate::windows::com; use crate::windows::registry::{RegistryKey, LOCAL_MACHINE}; use crate::windows::setup_config::SetupConfiguration; use crate::windows::vs_instances::{VsInstances, VswhereInstance}; use crate::windows::windows_sys::{ GetMachineTypeAttributes, GetProcAddress, LoadLibraryA, UserEnabled, HMODULE, IMAGE_FILE_MACHINE_AMD64, MACHINE_ATTRIBUTES, S_OK, }; use std::convert::TryFrom; use std::env; use std::ffi::OsString; use std::fs::File; use std::io::Read; use std::iter; use std::mem; use std::path::{Path, PathBuf}; use std::process::Command; use std::str::FromStr; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Once; use super::{EnvGetter, TargetArch, MSVC_FAMILY}; use crate::Tool; struct MsvcTool { tool: PathBuf, libs: Vec, path: Vec, include: Vec, } struct LibraryHandle(HMODULE); impl LibraryHandle { fn new(name: &[u8]) -> Option { let handle = unsafe { LoadLibraryA(name.as_ptr() as _) }; (!handle.is_null()).then_some(Self(handle)) } /// Get a function pointer to a function in the library. /// # SAFETY /// /// The caller must ensure that the function signature matches the actual function. /// The easiest way to do this is to add an entry to windows_sys_no_link.list and use the /// generated function for `func_signature`. /// /// The function returned cannot be used after the handle is dropped. unsafe fn get_proc_address(&self, name: &[u8]) -> Option { let symbol = GetProcAddress(self.0, name.as_ptr() as _); symbol.map(|symbol| mem::transmute_copy(&symbol)) } } type GetMachineTypeAttributesFuncType = unsafe extern "system" fn(u16, *mut MACHINE_ATTRIBUTES) -> i32; const _: () = { // Ensure that our hand-written signature matches the actual function signature. // We can't use `GetMachineTypeAttributes` outside of a const scope otherwise we'll end up statically linking to // it, which will fail to load on older versions of Windows. let _: GetMachineTypeAttributesFuncType = GetMachineTypeAttributes; }; fn is_amd64_emulation_supported_inner() -> Option { // GetMachineTypeAttributes is only available on Win11 22000+, so dynamically load it. let kernel32 = LibraryHandle::new(b"kernel32.dll\0")?; // SAFETY: GetMachineTypeAttributesFuncType is checked to match the real function signature. let get_machine_type_attributes = unsafe { kernel32 .get_proc_address::(b"GetMachineTypeAttributes\0") }?; let mut attributes = Default::default(); if unsafe { get_machine_type_attributes(IMAGE_FILE_MACHINE_AMD64, &mut attributes) } == S_OK { Some((attributes & UserEnabled) != 0) } else { Some(false) } } fn is_amd64_emulation_supported() -> bool { // TODO: Replace with a OnceLock once MSRV is 1.70. static LOAD_VALUE: Once = Once::new(); static IS_SUPPORTED: AtomicBool = AtomicBool::new(false); // Using Relaxed ordering since the Once is providing synchronization. LOAD_VALUE.call_once(|| { IS_SUPPORTED.store( is_amd64_emulation_supported_inner().unwrap_or(false), Ordering::Relaxed, ); }); IS_SUPPORTED.load(Ordering::Relaxed) } impl MsvcTool { fn new(tool: PathBuf) -> MsvcTool { MsvcTool { tool, libs: Vec::new(), path: Vec::new(), include: Vec::new(), } } fn into_tool(self, env_getter: &dyn EnvGetter) -> Tool { let MsvcTool { tool, libs, path, include, } = self; let mut tool = Tool::with_family(tool, MSVC_FAMILY); add_env(&mut tool, "LIB", libs, env_getter); add_env(&mut tool, "PATH", path, env_getter); add_env(&mut tool, "INCLUDE", include, env_getter); tool } } /// Checks to see if the `VSCMD_ARG_TGT_ARCH` environment variable matches the /// given target's arch. Returns `None` if the variable does not exist. fn is_vscmd_target(target: TargetArch<'_>, env_getter: &dyn EnvGetter) -> Option { let vscmd_arch = env_getter.get_env("VSCMD_ARG_TGT_ARCH")?; // Convert the Rust target arch to its VS arch equivalent. let arch = match target.into() { "x86_64" => "x64", "aarch64" | "arm64ec" => "arm64", "i686" | "i586" => "x86", "thumbv7a" => "arm", // An unrecognized arch. _ => return Some(false), }; Some(vscmd_arch.as_ref() == arch) } /// Attempt to find the tool using environment variables set by vcvars. pub(super) fn find_msvc_environment( tool: &str, target: TargetArch<'_>, env_getter: &dyn EnvGetter, ) -> Option { // Early return if the environment isn't one that is known to have compiler toolsets in PATH // `VCINSTALLDIR` is set from vcvarsall.bat (developer command prompt) // `VisualStudioDir` is set by msbuild when invoking custom build steps if env_getter.get_env("VCINSTALLDIR").is_none() && env_getter.get_env("VisualStudioDir").is_none() { return None; } // If the vscmd target differs from the requested target then // attempt to get the tool using the VS install directory. if is_vscmd_target(target, env_getter) == Some(false) { // We will only get here with versions 15+. let vs_install_dir: PathBuf = env_getter.get_env("VSINSTALLDIR")?.into(); tool_from_vs15plus_instance(tool, target, &vs_install_dir, env_getter) } else { // Fallback to simply using the current environment. env_getter .get_env("PATH") .and_then(|path| { env::split_paths(&path) .map(|p| p.join(tool)) .find(|p| p.exists()) }) .map(|path| Tool::with_family(path, MSVC_FAMILY)) } } fn find_msbuild_vs17(target: TargetArch<'_>, env_getter: &dyn EnvGetter) -> Option { find_tool_in_vs16plus_path(r"MSBuild\Current\Bin\MSBuild.exe", target, "17", env_getter) } #[allow(bare_trait_objects)] fn vs16plus_instances( target: TargetArch<'_>, version: &'static str, env_getter: &dyn EnvGetter, ) -> Box> { let instances = if let Some(instances) = vs15plus_instances(target, env_getter) { instances } else { return Box::new(iter::empty()); }; Box::new(instances.into_iter().filter_map(move |instance| { let installation_name = instance.installation_name()?; if installation_name.starts_with(&format!("VisualStudio/{}.", version)) || installation_name.starts_with(&format!("VisualStudioPreview/{}.", version)) { Some(instance.installation_path()?) } else { None } })) } fn find_tool_in_vs16plus_path( tool: &str, target: TargetArch<'_>, version: &'static str, env_getter: &dyn EnvGetter, ) -> Option { vs16plus_instances(target, version, env_getter) .filter_map(|path| { let path = path.join(tool); if !path.is_file() { return None; } let mut tool = Tool::with_family(path, MSVC_FAMILY); if target == "x86_64" { tool.env.push(("Platform".into(), "X64".into())); } if target == "aarch64" || target == "arm64ec" { tool.env.push(("Platform".into(), "ARM64".into())); } Some(tool) }) .next() } fn find_msbuild_vs16(target: TargetArch<'_>, env_getter: &dyn EnvGetter) -> Option { find_tool_in_vs16plus_path(r"MSBuild\Current\Bin\MSBuild.exe", target, "16", env_getter) } // In MSVC 15 (2017) MS once again changed the scheme for locating // the tooling. Now we must go through some COM interfaces, which // is super fun for Rust. // // Note that much of this logic can be found [online] wrt paths, COM, etc. // // [online]: https://blogs.msdn.microsoft.com/vcblog/2017/03/06/finding-the-visual-c-compiler-tools-in-visual-studio-2017/ // // Returns MSVC 15+ instances (15, 16 right now), the order should be consider undefined. // // However, on ARM64 this method doesn't work because VS Installer fails to register COM component on ARM64. // Hence, as the last resort we try to use vswhere.exe to list available instances. fn vs15plus_instances( target: TargetArch<'_>, env_getter: &dyn EnvGetter, ) -> Option { vs15plus_instances_using_com() .or_else(|| vs15plus_instances_using_vswhere(target, env_getter)) } fn vs15plus_instances_using_com() -> Option { com::initialize().ok()?; let config = SetupConfiguration::new().ok()?; let enum_setup_instances = config.enum_all_instances().ok()?; Some(VsInstances::ComBased(enum_setup_instances)) } fn vs15plus_instances_using_vswhere( target: TargetArch<'_>, env_getter: &dyn EnvGetter, ) -> Option { let program_files_path = env_getter .get_env("ProgramFiles(x86)") .or_else(|| env_getter.get_env("ProgramFiles"))?; let program_files_path = Path::new(program_files_path.as_ref()); let vswhere_path = program_files_path.join(r"Microsoft Visual Studio\Installer\vswhere.exe"); if !vswhere_path.exists() { return None; } let tools_arch = match target.into() { "i586" | "i686" | "x86_64" => Some("x86.x64"), "arm" | "thumbv7a" => Some("ARM"), "aarch64" | "arm64ec" => Some("ARM64"), _ => None, }; let vswhere_output = Command::new(vswhere_path) .args([ "-latest", "-products", "*", "-requires", &format!("Microsoft.VisualStudio.Component.VC.Tools.{}", tools_arch?), "-format", "text", "-nologo", ]) .stderr(std::process::Stdio::inherit()) .output() .ok()?; let vs_instances = VsInstances::VswhereBased(VswhereInstance::try_from(&vswhere_output.stdout).ok()?); Some(vs_instances) } // Inspired from official microsoft/vswhere ParseVersionString // i.e. at most four u16 numbers separated by '.' fn parse_version(version: &str) -> Option> { version .split('.') .map(|chunk| u16::from_str(chunk).ok()) .collect() } pub(super) fn find_msvc_15plus( tool: &str, target: TargetArch<'_>, env_getter: &dyn EnvGetter, ) -> Option { let iter = vs15plus_instances(target, env_getter)?; iter.into_iter() .filter_map(|instance| { let version = parse_version(&instance.installation_version()?)?; let instance_path = instance.installation_path()?; let tool = tool_from_vs15plus_instance(tool, target, &instance_path, env_getter)?; Some((version, tool)) }) .max_by(|(a_version, _), (b_version, _)| a_version.cmp(b_version)) .map(|(_version, tool)| tool) } // While the paths to Visual Studio 2017's devenv and MSBuild could // potentially be retrieved from the registry, finding them via // SetupConfiguration has shown to be [more reliable], and is preferred // according to Microsoft. To help head off potential regressions though, // we keep the registry method as a fallback option. // // [more reliable]: https://github.com/rust-lang/cc-rs/pull/331 fn find_tool_in_vs15_path( tool: &str, target: TargetArch<'_>, env_getter: &dyn EnvGetter, ) -> Option { let mut path = match vs15plus_instances(target, env_getter) { Some(instances) => instances .into_iter() .filter_map(|instance| instance.installation_path()) .map(|path| path.join(tool)) .find(|path| path.is_file()), None => None, }; if path.is_none() { let key = r"SOFTWARE\WOW6432Node\Microsoft\VisualStudio\SxS\VS7"; path = LOCAL_MACHINE .open(key.as_ref()) .ok() .and_then(|key| key.query_str("15.0").ok()) .map(|path| PathBuf::from(path).join(tool)) .and_then(|path| if path.is_file() { Some(path) } else { None }); } path.map(|path| { let mut tool = Tool::with_family(path, MSVC_FAMILY); if target == "x86_64" { tool.env.push(("Platform".into(), "X64".into())); } else if target == "aarch64" { tool.env.push(("Platform".into(), "ARM64".into())); } tool }) } fn tool_from_vs15plus_instance( tool: &str, target: TargetArch<'_>, instance_path: &Path, env_getter: &dyn EnvGetter, ) -> Option { let (root_path, bin_path, host_dylib_path, lib_path, alt_lib_path, include_path) = vs15plus_vc_paths(target, instance_path, env_getter)?; let tool_path = bin_path.join(tool); if !tool_path.exists() { return None; }; let mut tool = MsvcTool::new(tool_path); tool.path.push(bin_path.clone()); tool.path.push(host_dylib_path); if let Some(alt_lib_path) = alt_lib_path { tool.libs.push(alt_lib_path); } tool.libs.push(lib_path); tool.include.push(include_path); if let Some((atl_lib_path, atl_include_path)) = atl_paths(target, &root_path) { tool.libs.push(atl_lib_path); tool.include.push(atl_include_path); } add_sdks(&mut tool, target, env_getter)?; Some(tool.into_tool(env_getter)) } fn vs15plus_vc_paths( target: TargetArch<'_>, instance_path: &Path, env_getter: &dyn EnvGetter, ) -> Option<(PathBuf, PathBuf, PathBuf, PathBuf, Option, PathBuf)> { let version = vs15plus_vc_read_version(instance_path)?; let hosts = match host_arch() { X86 => &["X86"], X86_64 => &["X64"], // Starting with VS 17.4, there is a natively hosted compiler on ARM64: // https://devblogs.microsoft.com/visualstudio/arm64-visual-studio-is-officially-here/ // On older versions of VS, we use x64 if running under emulation is supported, // otherwise use x86. AARCH64 => { if is_amd64_emulation_supported() { &["ARM64", "X64", "X86"][..] } else { &["ARM64", "X86"] } } _ => return None, }; let target = lib_subdir(target)?; // The directory layout here is MSVC/bin/Host$host/$target/ let path = instance_path.join(r"VC\Tools\MSVC").join(version); // We use the first available host architecture that can build for the target let (host_path, host) = hosts.iter().find_map(|&x| { let candidate = path.join("bin").join(format!("Host{}", x)); if candidate.join(target).exists() { Some((candidate, x)) } else { None } })?; // This is the path to the toolchain for a particular target, running // on a given host let bin_path = host_path.join(target); // But! we also need PATH to contain the target directory for the host // architecture, because it contains dlls like mspdb140.dll compiled for // the host architecture. let host_dylib_path = host_path.join(host.to_lowercase()); let lib_fragment = if use_spectre_mitigated_libs(env_getter) { r"lib\spectre" } else { "lib" }; let lib_path = path.join(lib_fragment).join(target); let alt_lib_path = (target == "arm64ec").then(|| path.join(lib_fragment).join("arm64ec")); let include_path = path.join("include"); Some(( path, bin_path, host_dylib_path, lib_path, alt_lib_path, include_path, )) } fn vs15plus_vc_read_version(dir: &Path) -> Option { // Try to open the default version file. let mut version_path: PathBuf = dir.join(r"VC\Auxiliary\Build\Microsoft.VCToolsVersion.default.txt"); let mut version_file = if let Ok(f) = File::open(&version_path) { f } else { // If the default doesn't exist, search for other version files. // These are in the form Microsoft.VCToolsVersion.v143.default.txt // where `143` is any three decimal digit version number. // This sorts versions by lexical order and selects the highest version. let mut version_file = String::new(); version_path.pop(); for file in version_path.read_dir().ok()? { let name = file.ok()?.file_name(); let name = name.to_str()?; if name.starts_with("Microsoft.VCToolsVersion.v") && name.ends_with(".default.txt") && name > &version_file { version_file.replace_range(.., name); } } if version_file.is_empty() { return None; } version_path.push(version_file); File::open(version_path).ok()? }; // Get the version string from the file we found. let mut version = String::new(); version_file.read_to_string(&mut version).ok()?; version.truncate(version.trim_end().len()); Some(version) } fn use_spectre_mitigated_libs(env_getter: &dyn EnvGetter) -> bool { env_getter .get_env("VSCMD_ARG_VCVARS_SPECTRE") .map(|env| env.as_ref() == "spectre") .unwrap_or_default() } fn atl_paths(target: TargetArch<'_>, path: &Path) -> Option<(PathBuf, PathBuf)> { let atl_path = path.join("atlmfc"); let sub = lib_subdir(target)?; if atl_path.exists() { Some((atl_path.join("lib").join(sub), atl_path.join("include"))) } else { None } } // For MSVC 14 we need to find the Universal CRT as well as either // the Windows 10 SDK or Windows 8.1 SDK. pub(super) fn find_msvc_14( tool: &str, target: TargetArch<'_>, env_getter: &dyn EnvGetter, ) -> Option { let vcdir = get_vc_dir("14.0")?; let mut tool = get_tool(tool, &vcdir, target)?; add_sdks(&mut tool, target, env_getter)?; Some(tool.into_tool(env_getter)) } fn add_sdks( tool: &mut MsvcTool, target: TargetArch<'_>, env_getter: &dyn EnvGetter, ) -> Option<()> { let sub = lib_subdir(target)?; let (ucrt, ucrt_version) = get_ucrt_dir()?; let host = match host_arch() { X86 => "x86", X86_64 => "x64", AARCH64 => "arm64", _ => return None, }; tool.path .push(ucrt.join("bin").join(&ucrt_version).join(host)); let ucrt_include = ucrt.join("include").join(&ucrt_version); tool.include.push(ucrt_include.join("ucrt")); let ucrt_lib = ucrt.join("lib").join(&ucrt_version); tool.libs.push(ucrt_lib.join("ucrt").join(sub)); if let Some((sdk, version)) = get_sdk10_dir(env_getter) { tool.path.push(sdk.join("bin").join(host)); let sdk_lib = sdk.join("lib").join(&version); tool.libs.push(sdk_lib.join("um").join(sub)); let sdk_include = sdk.join("include").join(&version); tool.include.push(sdk_include.join("um")); tool.include.push(sdk_include.join("cppwinrt")); tool.include.push(sdk_include.join("winrt")); tool.include.push(sdk_include.join("shared")); } else if let Some(sdk) = get_sdk81_dir() { tool.path.push(sdk.join("bin").join(host)); let sdk_lib = sdk.join("lib").join("winv6.3"); tool.libs.push(sdk_lib.join("um").join(sub)); let sdk_include = sdk.join("include"); tool.include.push(sdk_include.join("um")); tool.include.push(sdk_include.join("winrt")); tool.include.push(sdk_include.join("shared")); } Some(()) } fn add_env( tool: &mut Tool, env: &'static str, paths: Vec, env_getter: &dyn EnvGetter, ) { let prev = env_getter.get_env(env); let prev = prev.as_ref().map(AsRef::as_ref).unwrap_or_default(); let prev = env::split_paths(&prev); let new = paths.into_iter().chain(prev); tool.env .push((env.to_string().into(), env::join_paths(new).unwrap())); } // Given a possible MSVC installation directory, we look for the linker and // then add the MSVC library path. fn get_tool(tool: &str, path: &Path, target: TargetArch<'_>) -> Option { bin_subdir(target) .into_iter() .map(|(sub, host)| { ( path.join("bin").join(sub).join(tool), path.join("bin").join(host), ) }) .filter(|(path, _)| path.is_file()) .map(|(path, host)| { let mut tool = MsvcTool::new(path); tool.path.push(host); tool }) .filter_map(|mut tool| { let sub = vc_lib_subdir(target)?; tool.libs.push(path.join("lib").join(sub)); tool.include.push(path.join("include")); let atlmfc_path = path.join("atlmfc"); if atlmfc_path.exists() { tool.libs.push(atlmfc_path.join("lib").join(sub)); tool.include.push(atlmfc_path.join("include")); } Some(tool) }) .next() } // To find MSVC we look in a specific registry key for the version we are // trying to find. fn get_vc_dir(ver: &str) -> Option { let key = r"SOFTWARE\Microsoft\VisualStudio\SxS\VC7"; let key = LOCAL_MACHINE.open(key.as_ref()).ok()?; let path = key.query_str(ver).ok()?; Some(path.into()) } // To find the Universal CRT we look in a specific registry key for where // all the Universal CRTs are located and then sort them asciibetically to // find the newest version. While this sort of sorting isn't ideal, it is // what vcvars does so that's good enough for us. // // Returns a pair of (root, version) for the ucrt dir if found fn get_ucrt_dir() -> Option<(PathBuf, String)> { let key = r"SOFTWARE\Microsoft\Windows Kits\Installed Roots"; let key = LOCAL_MACHINE.open(key.as_ref()).ok()?; let root = key.query_str("KitsRoot10").ok()?; let readdir = Path::new(&root).join("lib").read_dir().ok()?; let max_libdir = readdir .filter_map(|dir| dir.ok()) .map(|dir| dir.path()) .filter(|dir| { dir.components() .last() .and_then(|c| c.as_os_str().to_str()) .map(|c| c.starts_with("10.") && dir.join("ucrt").is_dir()) .unwrap_or(false) }) .max()?; let version = max_libdir.components().last().unwrap(); let version = version.as_os_str().to_str().unwrap().to_string(); Some((root.into(), version)) } // Vcvars finds the correct version of the Windows 10 SDK by looking // for the include `um\Windows.h` because sometimes a given version will // only have UCRT bits without the rest of the SDK. Since we only care about // libraries and not includes, we instead look for `um\x64\kernel32.lib`. // Since the 32-bit and 64-bit libraries are always installed together we // only need to bother checking x64, making this code a tiny bit simpler. // Like we do for the Universal CRT, we sort the possibilities // asciibetically to find the newest one as that is what vcvars does. // Before doing that, we check the "WindowsSdkDir" and "WindowsSDKVersion" // environment variables set by vcvars to use the environment sdk version // if one is already configured. fn get_sdk10_dir(env_getter: &dyn EnvGetter) -> Option<(PathBuf, String)> { if let (Some(root), Some(version)) = ( env_getter.get_env("WindowsSdkDir"), env_getter .get_env("WindowsSDKVersion") .as_ref() .and_then(|version| version.as_ref().to_str()), ) { return Some(( PathBuf::from(root), version.trim_end_matches('\\').to_string(), )); } let key = r"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0"; let key = LOCAL_MACHINE.open(key.as_ref()).ok()?; let root = key.query_str("InstallationFolder").ok()?; let readdir = Path::new(&root).join("lib").read_dir().ok()?; let mut dirs = readdir .filter_map(|dir| dir.ok()) .map(|dir| dir.path()) .collect::>(); dirs.sort(); let dir = dirs .into_iter() .rev() .find(|dir| dir.join("um").join("x64").join("kernel32.lib").is_file())?; let version = dir.components().last().unwrap(); let version = version.as_os_str().to_str().unwrap().to_string(); Some((root.into(), version)) } // Interestingly there are several subdirectories, `win7` `win8` and // `winv6.3`. Vcvars seems to only care about `winv6.3` though, so the same // applies to us. Note that if we were targeting kernel mode drivers // instead of user mode applications, we would care. fn get_sdk81_dir() -> Option { let key = r"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v8.1"; let key = LOCAL_MACHINE.open(key.as_ref()).ok()?; let root = key.query_str("InstallationFolder").ok()?; Some(root.into()) } const PROCESSOR_ARCHITECTURE_INTEL: u16 = 0; const PROCESSOR_ARCHITECTURE_AMD64: u16 = 9; const PROCESSOR_ARCHITECTURE_ARM64: u16 = 12; const X86: u16 = PROCESSOR_ARCHITECTURE_INTEL; const X86_64: u16 = PROCESSOR_ARCHITECTURE_AMD64; const AARCH64: u16 = PROCESSOR_ARCHITECTURE_ARM64; // When choosing the tool to use, we have to choose the one which matches // the target architecture. Otherwise we end up in situations where someone // on 32-bit Windows is trying to cross compile to 64-bit and it tries to // invoke the native 64-bit compiler which won't work. // // For the return value of this function, the first member of the tuple is // the folder of the tool we will be invoking, while the second member is // the folder of the host toolchain for that tool which is essential when // using a cross linker. We return a Vec since on x64 there are often two // linkers that can target the architecture we desire. The 64-bit host // linker is preferred, and hence first, due to 64-bit allowing it more // address space to work with and potentially being faster. fn bin_subdir(target: TargetArch<'_>) -> Vec<(&'static str, &'static str)> { match (target.into(), host_arch()) { ("i586", X86) | ("i686", X86) => vec![("", "")], ("i586", X86_64) | ("i686", X86_64) => vec![("amd64_x86", "amd64"), ("", "")], ("x86_64", X86) => vec![("x86_amd64", "")], ("x86_64", X86_64) => vec![("amd64", "amd64"), ("x86_amd64", "")], ("arm", X86) | ("thumbv7a", X86) => vec![("x86_arm", "")], ("arm", X86_64) | ("thumbv7a", X86_64) => vec![("amd64_arm", "amd64"), ("x86_arm", "")], _ => vec![], } } fn lib_subdir(target: TargetArch<'_>) -> Option<&'static str> { match target.into() { "i586" | "i686" => Some("x86"), "x86_64" => Some("x64"), "arm" | "thumbv7a" => Some("arm"), "aarch64" | "arm64ec" => Some("arm64"), _ => None, } } // MSVC's x86 libraries are not in a subfolder fn vc_lib_subdir(target: TargetArch<'_>) -> Option<&'static str> { match target.into() { "i586" | "i686" => Some(""), "x86_64" => Some("amd64"), "arm" | "thumbv7a" => Some("arm"), "aarch64" => Some("arm64"), _ => None, } } #[allow(bad_style)] fn host_arch() -> u16 { type DWORD = u32; type WORD = u16; type LPVOID = *mut u8; type DWORD_PTR = usize; #[repr(C)] struct SYSTEM_INFO { wProcessorArchitecture: WORD, _wReserved: WORD, _dwPageSize: DWORD, _lpMinimumApplicationAddress: LPVOID, _lpMaximumApplicationAddress: LPVOID, _dwActiveProcessorMask: DWORD_PTR, _dwNumberOfProcessors: DWORD, _dwProcessorType: DWORD, _dwAllocationGranularity: DWORD, _wProcessorLevel: WORD, _wProcessorRevision: WORD, } extern "system" { fn GetNativeSystemInfo(lpSystemInfo: *mut SYSTEM_INFO); } unsafe { let mut info = mem::zeroed(); GetNativeSystemInfo(&mut info); info.wProcessorArchitecture } } // Given a registry key, look at all the sub keys and find the one which has // the maximal numeric value. // // Returns the name of the maximal key as well as the opened maximal key. fn max_version(key: &RegistryKey) -> Option<(OsString, RegistryKey)> { let mut max_vers = 0; let mut max_key = None; for subkey in key.iter().filter_map(|k| k.ok()) { let val = subkey .to_str() .and_then(|s| s.trim_start_matches('v').replace('.', "").parse().ok()); let val = match val { Some(s) => s, None => continue, }; if val > max_vers { if let Ok(k) = key.open(&subkey) { max_vers = val; max_key = Some((subkey, k)); } } } max_key } #[inline(always)] pub(super) fn has_msbuild_version(version: &str, env_getter: &dyn EnvGetter) -> bool { match version { "17.0" => { find_msbuild_vs17(TargetArch("x86_64"), env_getter).is_some() || find_msbuild_vs17(TargetArch("i686"), env_getter).is_some() || find_msbuild_vs17(TargetArch("aarch64"), env_getter).is_some() } "16.0" => { find_msbuild_vs16(TargetArch("x86_64"), env_getter).is_some() || find_msbuild_vs16(TargetArch("i686"), env_getter).is_some() || find_msbuild_vs16(TargetArch("aarch64"), env_getter).is_some() } "15.0" => { find_msbuild_vs15(TargetArch("x86_64"), env_getter).is_some() || find_msbuild_vs15(TargetArch("i686"), env_getter).is_some() || find_msbuild_vs15(TargetArch("aarch64"), env_getter).is_some() } "14.0" => LOCAL_MACHINE .open(&OsString::from(format!( "SOFTWARE\\Microsoft\\MSBuild\\ToolsVersions\\{}", version ))) .is_ok(), _ => false, } } pub(super) fn find_devenv(target: TargetArch<'_>, env_getter: &dyn EnvGetter) -> Option { find_devenv_vs15(target, env_getter) } fn find_devenv_vs15(target: TargetArch<'_>, env_getter: &dyn EnvGetter) -> Option { find_tool_in_vs15_path(r"Common7\IDE\devenv.exe", target, env_getter) } // see http://stackoverflow.com/questions/328017/path-to-msbuild pub(super) fn find_msbuild(target: TargetArch<'_>, env_getter: &dyn EnvGetter) -> Option { // VS 15 (2017) changed how to locate msbuild if let Some(r) = find_msbuild_vs17(target, env_getter) { Some(r) } else if let Some(r) = find_msbuild_vs16(target, env_getter) { return Some(r); } else if let Some(r) = find_msbuild_vs15(target, env_getter) { return Some(r); } else { find_old_msbuild(target) } } fn find_msbuild_vs15(target: TargetArch<'_>, env_getter: &dyn EnvGetter) -> Option { find_tool_in_vs15_path(r"MSBuild\15.0\Bin\MSBuild.exe", target, env_getter) } fn find_old_msbuild(target: TargetArch<'_>) -> Option { let key = r"SOFTWARE\Microsoft\MSBuild\ToolsVersions"; LOCAL_MACHINE .open(key.as_ref()) .ok() .and_then(|key| { max_version(&key).and_then(|(_vers, key)| key.query_str("MSBuildToolsPath").ok()) }) .map(|path| { let mut path = PathBuf::from(path); path.push("MSBuild.exe"); let mut tool = Tool::with_family(path, MSVC_FAMILY); if target == "x86_64" { tool.env.push(("Platform".into(), "X64".into())); } tool }) } } /// Non-Windows Implementation. #[cfg(not(windows))] mod impl_ { use std::{env, ffi::OsStr}; use super::{EnvGetter, TargetArch, MSVC_FAMILY}; use crate::Tool; /// Finding msbuild.exe tool under unix system is not currently supported. /// Maybe can check it using an environment variable looks like `MSBUILD_BIN`. #[inline(always)] pub(super) fn find_msbuild(_target: TargetArch<'_>, _: &dyn EnvGetter) -> Option { None } // Finding devenv.exe tool under unix system is not currently supported. // Maybe can check it using an environment variable looks like `DEVENV_BIN`. #[inline(always)] pub(super) fn find_devenv(_target: TargetArch<'_>, _: &dyn EnvGetter) -> Option { None } /// Attempt to find the tool using environment variables set by vcvars. pub(super) fn find_msvc_environment( tool: &str, _target: TargetArch<'_>, env_getter: &dyn EnvGetter, ) -> Option { // Early return if the environment doesn't contain a VC install. let vc_install_dir = env_getter.get_env("VCINSTALLDIR")?; let vs_install_dir = env_getter.get_env("VSINSTALLDIR")?; let get_tool = |install_dir: &OsStr| { env::split_paths(install_dir) .map(|p| p.join(tool)) .find(|p| p.exists()) .map(|path| Tool::with_family(path, MSVC_FAMILY)) }; // Take the path of tool for the vc install directory. get_tool(vc_install_dir.as_ref()) // Take the path of tool for the vs install directory. .or_else(|| get_tool(vs_install_dir.as_ref())) // Take the path of tool for the current path environment. .or_else(|| { env_getter .get_env("PATH") .as_ref() .map(|path| path.as_ref()) .and_then(get_tool) }) } #[inline(always)] pub(super) fn find_msvc_15plus( _tool: &str, _target: TargetArch<'_>, _: &dyn EnvGetter, ) -> Option { None } // For MSVC 14 we need to find the Universal CRT as well as either // the Windows 10 SDK or Windows 8.1 SDK. #[inline(always)] pub(super) fn find_msvc_14( _tool: &str, _target: TargetArch<'_>, _: &dyn EnvGetter, ) -> Option { None } #[inline(always)] pub(super) fn has_msbuild_version(_version: &str, _: &dyn EnvGetter) -> bool { false } } cc-1.1.14/src/windows/mod.rs000064400000000000000000000007061046102023000137060ustar 00000000000000//! These modules are all glue to support reading the MSVC version from //! the registry and from COM interfaces. // This is used in the crate's public API, so don't use #[cfg(windows)] pub mod find_tools; #[cfg(windows)] pub(crate) mod windows_sys; #[cfg(windows)] mod windows_targets; #[cfg(windows)] mod registry; #[cfg(windows)] #[macro_use] mod winapi; #[cfg(windows)] mod com; #[cfg(windows)] mod setup_config; #[cfg(windows)] mod vs_instances; cc-1.1.14/src/windows/registry.rs000064400000000000000000000144511046102023000150010ustar 00000000000000// Copyright 2015 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use crate::windows::windows_sys::{ RegCloseKey, RegEnumKeyExW, RegOpenKeyExW, RegQueryValueExW, ERROR_NO_MORE_ITEMS, ERROR_SUCCESS, HKEY, HKEY_LOCAL_MACHINE, KEY_READ, KEY_WOW64_32KEY, REG_SZ, }; use std::{ ffi::{OsStr, OsString}, io, ops::RangeFrom, os::windows::prelude::*, ptr::null_mut, }; /// Must never be `HKEY_PERFORMANCE_DATA`. pub(crate) struct RegistryKey(Repr); #[allow(clippy::upper_case_acronyms)] type DWORD = u32; struct OwnedKey(HKEY); /// Note: must not encode `HKEY_PERFORMANCE_DATA` or one of its subkeys. enum Repr { /// `HKEY_LOCAL_MACHINE`. LocalMachine, /// A subkey of `HKEY_LOCAL_MACHINE`. Owned(OwnedKey), } pub struct Iter<'a> { idx: RangeFrom, key: &'a RegistryKey, } unsafe impl Sync for Repr {} unsafe impl Send for Repr {} pub(crate) const LOCAL_MACHINE: RegistryKey = RegistryKey(Repr::LocalMachine); impl RegistryKey { fn raw(&self) -> HKEY { match self.0 { Repr::LocalMachine => HKEY_LOCAL_MACHINE, Repr::Owned(ref val) => val.0, } } /// Open a sub-key of `self`. pub fn open(&self, key: &OsStr) -> io::Result { let key = key.encode_wide().chain(Some(0)).collect::>(); let mut ret = null_mut(); let err = unsafe { RegOpenKeyExW( self.raw(), key.as_ptr(), 0, KEY_READ | KEY_WOW64_32KEY, &mut ret, ) }; if err == ERROR_SUCCESS { Ok(RegistryKey(Repr::Owned(OwnedKey(ret)))) } else { Err(io::Error::from_raw_os_error(err as i32)) } } pub fn iter(&self) -> Iter { Iter { idx: 0.., key: self, } } pub fn query_str(&self, name: &str) -> io::Result { let name: &OsStr = name.as_ref(); let name = name.encode_wide().chain(Some(0)).collect::>(); let mut len = 0; let mut kind = 0; unsafe { let err = RegQueryValueExW( self.raw(), name.as_ptr(), null_mut(), &mut kind, null_mut(), &mut len, ); if err != ERROR_SUCCESS { return Err(io::Error::from_raw_os_error(err as i32)); } if kind != REG_SZ { return Err(io::Error::new( io::ErrorKind::Other, "registry key wasn't a string", )); } // The length here is the length in bytes, but we're using wide // characters so we need to be sure to halve it for the length // passed in. assert!(len % 2 == 0, "impossible wide string size: {} bytes", len); let vlen = len as usize / 2; // Defensively initialized, see comment about // `HKEY_PERFORMANCE_DATA` below. let mut v = vec![0u16; vlen]; let err = RegQueryValueExW( self.raw(), name.as_ptr(), null_mut(), null_mut(), v.as_mut_ptr() as *mut _, &mut len, ); // We don't check for `ERROR_MORE_DATA` (which would if the value // grew between the first and second call to `RegQueryValueExW`), // both because it's extremely unlikely, and this is a bit more // defensive more defensive against weird types of registry keys. if err != ERROR_SUCCESS { return Err(io::Error::from_raw_os_error(err as i32)); } // The length is allowed to change, but should still be even, as // well as smaller. assert!(len % 2 == 0, "impossible wide string size: {} bytes", len); // If the length grew but returned a success code, it *probably* // indicates we're `HKEY_PERFORMANCE_DATA` or a subkey(?). We // consider this UB, since those keys write "undefined" or // "unpredictable" values to len, and need to use a completely // different loop structure. This should be impossible (and enforce // it in the API to the best of our ability), but to mitigate the // damage we do some smoke-checks on the len, and ensure `v` has // been fully initialized (rather than trusting the result of // `RegQueryValueExW`). let actual_len = len as usize / 2; assert!(actual_len <= v.len()); v.truncate(actual_len); // Some registry keys may have a terminating nul character, but // we're not interested in that, so chop it off if it's there. if !v.is_empty() && v[v.len() - 1] == 0 { v.pop(); } Ok(OsString::from_wide(&v)) } } } impl Drop for OwnedKey { fn drop(&mut self) { unsafe { RegCloseKey(self.0); } } } impl<'a> Iterator for Iter<'a> { type Item = io::Result; fn next(&mut self) -> Option> { self.idx.next().and_then(|i| unsafe { let mut v = Vec::with_capacity(256); let mut len = v.capacity() as DWORD; let ret = RegEnumKeyExW( self.key.raw(), i, v.as_mut_ptr(), &mut len, null_mut(), null_mut(), null_mut(), null_mut(), ); if ret == ERROR_NO_MORE_ITEMS { None } else if ret != ERROR_SUCCESS { Some(Err(io::Error::from_raw_os_error(ret as i32))) } else { v.set_len(len as usize); Some(Ok(OsString::from_wide(&v))) } }) } } cc-1.1.14/src/windows/setup_config.rs000064400000000000000000000212771046102023000156220ustar 00000000000000// Copyright © 2017 winapi-rs developers // Licensed under the Apache License, Version 2.0 // or the MIT license // , at your option. // All files in the project carrying such notice may not be copied, modified, or distributed // except according to those terms. #![allow(bad_style)] #![allow(unused)] use crate::windows::{ com::{BStr, ComPtr}, winapi::{ IUnknown, IUnknownVtbl, Interface, LCID, LPCOLESTR, LPCWSTR, LPFILETIME, LPSAFEARRAY, PULONGLONG, ULONG, }, windows_sys::{CoCreateInstance, BSTR, CLSCTX_ALL, HRESULT, S_FALSE}, }; use std::{ ffi::OsString, ptr::{null, null_mut}, }; // Bindings to the Setup.Configuration stuff pub type InstanceState = u32; pub const eNone: InstanceState = 0; pub const eLocal: InstanceState = 1; pub const eRegistered: InstanceState = 2; pub const eNoRebootRequired: InstanceState = 4; pub const eComplete: InstanceState = -1i32 as u32; RIDL! {#[uuid(0xb41463c3, 0x8866, 0x43b5, 0xbc, 0x33, 0x2b, 0x06, 0x76, 0xf7, 0xf4, 0x2e)] interface ISetupInstance(ISetupInstanceVtbl): IUnknown(IUnknownVtbl) { fn GetInstanceId( pbstrInstanceId: *mut BSTR, ) -> HRESULT, fn GetInstallDate( pInstallDate: LPFILETIME, ) -> HRESULT, fn GetInstallationName( pbstrInstallationName: *mut BSTR, ) -> HRESULT, fn GetInstallationPath( pbstrInstallationPath: *mut BSTR, ) -> HRESULT, fn GetInstallationVersion( pbstrInstallationVersion: *mut BSTR, ) -> HRESULT, fn GetDisplayName( lcid: LCID, pbstrDisplayName: *mut BSTR, ) -> HRESULT, fn GetDescription( lcid: LCID, pbstrDescription: *mut BSTR, ) -> HRESULT, fn ResolvePath( pwszRelativePath: LPCOLESTR, pbstrAbsolutePath: *mut BSTR, ) -> HRESULT, }} RIDL! {#[uuid(0x89143c9a, 0x05af, 0x49b0, 0xb7, 0x17, 0x72, 0xe2, 0x18, 0xa2, 0x18, 0x5c)] interface ISetupInstance2(ISetupInstance2Vtbl): ISetupInstance(ISetupInstanceVtbl) { fn GetState( pState: *mut InstanceState, ) -> HRESULT, fn GetPackages( ppsaPackages: *mut LPSAFEARRAY, ) -> HRESULT, fn GetProduct( ppPackage: *mut *mut ISetupPackageReference, ) -> HRESULT, fn GetProductPath( pbstrProductPath: *mut BSTR, ) -> HRESULT, }} RIDL! {#[uuid(0x6380bcff, 0x41d3, 0x4b2e, 0x8b, 0x2e, 0xbf, 0x8a, 0x68, 0x10, 0xc8, 0x48)] interface IEnumSetupInstances(IEnumSetupInstancesVtbl): IUnknown(IUnknownVtbl) { fn Next( celt: ULONG, rgelt: *mut *mut ISetupInstance, pceltFetched: *mut ULONG, ) -> HRESULT, fn Skip( celt: ULONG, ) -> HRESULT, fn Reset() -> HRESULT, fn Clone( ppenum: *mut *mut IEnumSetupInstances, ) -> HRESULT, }} RIDL! {#[uuid(0x42843719, 0xdb4c, 0x46c2, 0x8e, 0x7c, 0x64, 0xf1, 0x81, 0x6e, 0xfd, 0x5b)] interface ISetupConfiguration(ISetupConfigurationVtbl): IUnknown(IUnknownVtbl) { fn EnumInstances( ppEnumInstances: *mut *mut IEnumSetupInstances, ) -> HRESULT, fn GetInstanceForCurrentProcess( ppInstance: *mut *mut ISetupInstance, ) -> HRESULT, fn GetInstanceForPath( wzPath: LPCWSTR, ppInstance: *mut *mut ISetupInstance, ) -> HRESULT, }} RIDL! {#[uuid(0x26aab78c, 0x4a60, 0x49d6, 0xaf, 0x3b, 0x3c, 0x35, 0xbc, 0x93, 0x36, 0x5d)] interface ISetupConfiguration2(ISetupConfiguration2Vtbl): ISetupConfiguration(ISetupConfigurationVtbl) { fn EnumAllInstances( ppEnumInstances: *mut *mut IEnumSetupInstances, ) -> HRESULT, }} RIDL! {#[uuid(0xda8d8a16, 0xb2b6, 0x4487, 0xa2, 0xf1, 0x59, 0x4c, 0xcc, 0xcd, 0x6b, 0xf5)] interface ISetupPackageReference(ISetupPackageReferenceVtbl): IUnknown(IUnknownVtbl) { fn GetId( pbstrId: *mut BSTR, ) -> HRESULT, fn GetVersion( pbstrVersion: *mut BSTR, ) -> HRESULT, fn GetChip( pbstrChip: *mut BSTR, ) -> HRESULT, fn GetLanguage( pbstrLanguage: *mut BSTR, ) -> HRESULT, fn GetBranch( pbstrBranch: *mut BSTR, ) -> HRESULT, fn GetType( pbstrType: *mut BSTR, ) -> HRESULT, fn GetUniqueId( pbstrUniqueId: *mut BSTR, ) -> HRESULT, }} RIDL! {#[uuid(0x42b21b78, 0x6192, 0x463e, 0x87, 0xbf, 0xd5, 0x77, 0x83, 0x8f, 0x1d, 0x5c)] interface ISetupHelper(ISetupHelperVtbl): IUnknown(IUnknownVtbl) { fn ParseVersion( pwszVersion: LPCOLESTR, pullVersion: PULONGLONG, ) -> HRESULT, fn ParseVersionRange( pwszVersionRange: LPCOLESTR, pullMinVersion: PULONGLONG, pullMaxVersion: PULONGLONG, ) -> HRESULT, }} DEFINE_GUID! {CLSID_SetupConfiguration, 0x177f0c4a, 0x1cd3, 0x4de7, 0xa3, 0x2c, 0x71, 0xdb, 0xbb, 0x9f, 0xa3, 0x6d} // Safe wrapper around the COM interfaces pub struct SetupConfiguration(ComPtr); impl SetupConfiguration { pub fn new() -> Result { let mut obj = null_mut(); let err = unsafe { CoCreateInstance( &CLSID_SetupConfiguration, null_mut(), CLSCTX_ALL, &ISetupConfiguration::uuidof(), &mut obj, ) }; if err < 0 { return Err(err); } let obj = unsafe { ComPtr::from_raw(obj as *mut ISetupConfiguration) }; Ok(SetupConfiguration(obj)) } pub fn get_instance_for_current_process(&self) -> Result { let mut obj = null_mut(); let err = unsafe { self.0.GetInstanceForCurrentProcess(&mut obj) }; if err < 0 { return Err(err); } Ok(unsafe { SetupInstance::from_raw(obj) }) } pub fn enum_instances(&self) -> Result { let mut obj = null_mut(); let err = unsafe { self.0.EnumInstances(&mut obj) }; if err < 0 { return Err(err); } Ok(unsafe { EnumSetupInstances::from_raw(obj) }) } pub fn enum_all_instances(&self) -> Result { let mut obj = null_mut(); let this = self.0.cast::()?; let err = unsafe { this.EnumAllInstances(&mut obj) }; if err < 0 { return Err(err); } Ok(unsafe { EnumSetupInstances::from_raw(obj) }) } } pub struct SetupInstance(ComPtr); impl SetupInstance { pub unsafe fn from_raw(obj: *mut ISetupInstance) -> SetupInstance { SetupInstance(ComPtr::from_raw(obj)) } pub fn instance_id(&self) -> Result { let mut s = null(); let err = unsafe { self.0.GetInstanceId(&mut s) }; let bstr = unsafe { BStr::from_raw(s) }; if err < 0 { return Err(err); } Ok(bstr.to_osstring()) } pub fn installation_name(&self) -> Result { let mut s = null(); let err = unsafe { self.0.GetInstallationName(&mut s) }; let bstr = unsafe { BStr::from_raw(s) }; if err < 0 { return Err(err); } Ok(bstr.to_osstring()) } pub fn installation_path(&self) -> Result { let mut s = null(); let err = unsafe { self.0.GetInstallationPath(&mut s) }; let bstr = unsafe { BStr::from_raw(s) }; if err < 0 { return Err(err); } Ok(bstr.to_osstring()) } pub fn installation_version(&self) -> Result { let mut s = null(); let err = unsafe { self.0.GetInstallationVersion(&mut s) }; let bstr = unsafe { BStr::from_raw(s) }; if err < 0 { return Err(err); } Ok(bstr.to_osstring()) } pub fn product_path(&self) -> Result { let mut s = null(); let this = self.0.cast::()?; let err = unsafe { this.GetProductPath(&mut s) }; let bstr = unsafe { BStr::from_raw(s) }; if err < 0 { return Err(err); } Ok(bstr.to_osstring()) } } pub struct EnumSetupInstances(ComPtr); impl EnumSetupInstances { pub unsafe fn from_raw(obj: *mut IEnumSetupInstances) -> EnumSetupInstances { EnumSetupInstances(ComPtr::from_raw(obj)) } } impl Iterator for EnumSetupInstances { type Item = Result; fn next(&mut self) -> Option> { let mut obj = null_mut(); let err = unsafe { self.0.Next(1, &mut obj, null_mut()) }; if err < 0 { return Some(Err(err)); } if err == S_FALSE { return None; } Some(Ok(unsafe { SetupInstance::from_raw(obj) })) } } cc-1.1.14/src/windows/vs_instances.rs000064400000000000000000000145331046102023000156310ustar 00000000000000use std::borrow::Cow; use std::collections::HashMap; use std::convert::TryFrom; use std::io::BufRead; use std::path::PathBuf; use crate::windows::setup_config::{EnumSetupInstances, SetupInstance}; pub enum VsInstance { Com(SetupInstance), Vswhere(VswhereInstance), } impl VsInstance { pub fn installation_name(&self) -> Option> { match self { VsInstance::Com(s) => s .installation_name() .ok() .and_then(|s| s.into_string().ok()) .map(Cow::from), VsInstance::Vswhere(v) => v.map.get("installationName").map(Cow::from), } } pub fn installation_path(&self) -> Option { match self { VsInstance::Com(s) => s.installation_path().ok().map(PathBuf::from), VsInstance::Vswhere(v) => v.map.get("installationPath").map(PathBuf::from), } } pub fn installation_version(&self) -> Option> { match self { VsInstance::Com(s) => s .installation_version() .ok() .and_then(|s| s.into_string().ok()) .map(Cow::from), VsInstance::Vswhere(v) => v.map.get("installationVersion").map(Cow::from), } } } pub enum VsInstances { ComBased(EnumSetupInstances), VswhereBased(VswhereInstance), } impl IntoIterator for VsInstances { type Item = VsInstance; #[allow(bare_trait_objects)] type IntoIter = Box>; fn into_iter(self) -> Self::IntoIter { match self { VsInstances::ComBased(e) => { Box::new(e.into_iter().filter_map(Result::ok).map(VsInstance::Com)) } VsInstances::VswhereBased(v) => Box::new(std::iter::once(VsInstance::Vswhere(v))), } } } #[derive(Debug)] pub struct VswhereInstance { map: HashMap, } impl TryFrom<&Vec> for VswhereInstance { type Error = &'static str; fn try_from(output: &Vec) -> Result { let map: HashMap<_, _> = output .lines() .map_while(Result::ok) .filter_map(|s| { let mut splitn = s.splitn(2, ": "); Some((splitn.next()?.to_owned(), splitn.next()?.to_owned())) }) .collect(); if !map.contains_key("installationName") || !map.contains_key("installationPath") || !map.contains_key("installationVersion") { return Err("required properties not found"); } Ok(Self { map }) } } #[cfg(test)] mod tests_ { use std::borrow::Cow; use std::convert::TryFrom; use std::path::PathBuf; #[test] fn it_parses_vswhere_output_correctly() { let output = br"instanceId: 58104422 installDate: 21/02/2021 21:50:33 installationName: VisualStudio/16.9.2+31112.23 installationPath: C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools installationVersion: 16.9.31112.23 productId: Microsoft.VisualStudio.Product.BuildTools productPath: C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\Common7\Tools\LaunchDevCmd.bat state: 4294967295 isComplete: 1 isLaunchable: 1 isPrerelease: 0 isRebootRequired: 0 displayName: Visual Studio Build Tools 2019 description: The Visual Studio Build Tools allows you to build native and managed MSBuild-based applications without requiring the Visual Studio IDE. There are options to install the Visual C++ compilers and libraries, MFC, ATL, and C++/CLI support. channelId: VisualStudio.16.Release channelUri: https://aka.ms/vs/16/release/channel enginePath: C:\Program Files (x86)\Microsoft Visual Studio\Installer\resources\app\ServiceHub\Services\Microsoft.VisualStudio.Setup.Service releaseNotes: https://docs.microsoft.com/en-us/visualstudio/releases/2019/release-notes-v16.9#16.9.2 thirdPartyNotices: https://go.microsoft.com/fwlink/?LinkId=660909 updateDate: 2021-03-17T21:16:46.5963702Z catalog_buildBranch: d16.9 catalog_buildVersion: 16.9.31112.23 catalog_id: VisualStudio/16.9.2+31112.23 catalog_localBuild: build-lab catalog_manifestName: VisualStudio catalog_manifestType: installer catalog_productDisplayVersion: 16.9.2 catalog_productLine: Dev16 catalog_productLineVersion: 2019 catalog_productMilestone: RTW catalog_productMilestoneIsPreRelease: False catalog_productName: Visual Studio catalog_productPatchVersion: 2 catalog_productPreReleaseMilestoneSuffix: 1.0 catalog_productSemanticVersion: 16.9.2+31112.23 catalog_requiredEngineVersion: 2.9.3365.38425 properties_campaignId: 156063665.1613940062 properties_channelManifestId: VisualStudio.16.Release/16.9.2+31112.23 properties_nickname: properties_setupEngineFilePath: C:\Program Files (x86)\Microsoft Visual Studio\Installer\vs_installershell.exe " .to_vec(); let vswhere_instance = super::VswhereInstance::try_from(&output); assert!(vswhere_instance.is_ok()); let vs_instance = super::VsInstance::Vswhere(vswhere_instance.unwrap()); assert_eq!( vs_instance.installation_name(), Some(Cow::from("VisualStudio/16.9.2+31112.23")) ); assert_eq!( vs_instance.installation_path(), Some(PathBuf::from( r"C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools" )) ); assert_eq!( vs_instance.installation_version(), Some(Cow::from("16.9.31112.23")) ); } #[test] fn it_returns_an_error_for_empty_output() { let output = b"".to_vec(); let vswhere_instance = super::VswhereInstance::try_from(&output); assert!(vswhere_instance.is_err()); } #[test] fn it_returns_an_error_for_output_consisting_of_empty_lines() { let output = br" " .to_vec(); let vswhere_instance = super::VswhereInstance::try_from(&output); assert!(vswhere_instance.is_err()); } #[test] fn it_returns_an_error_for_output_without_required_properties() { let output = br"instanceId: 58104422 installDate: 21/02/2021 21:50:33 productId: Microsoft.VisualStudio.Product.BuildTools productPath: C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\Common7\Tools\LaunchDevCmd.bat " .to_vec(); let vswhere_instance = super::VswhereInstance::try_from(&output); assert!(vswhere_instance.is_err()); } } cc-1.1.14/src/windows/winapi.rs000064400000000000000000000107761046102023000144260ustar 00000000000000// Copyright © 2015-2017 winapi-rs developers // Licensed under the Apache License, Version 2.0 // or the MIT license // , at your option. // All files in the project carrying such notice may not be copied, modified, or distributed // except according to those terms. #![allow(bad_style, clippy::upper_case_acronyms)] use std::os::raw; pub type wchar_t = u16; pub use crate::windows::windows_sys::{FILETIME, GUID, HRESULT, SAFEARRAY}; pub type REFIID = *const IID; pub type IID = GUID; pub type ULONG = raw::c_ulong; pub type DWORD = u32; pub type LPFILETIME = *mut FILETIME; pub type OLECHAR = WCHAR; pub type WCHAR = wchar_t; pub type LPCOLESTR = *const OLECHAR; pub type LCID = DWORD; pub type LPCWSTR = *const WCHAR; pub type PULONGLONG = *mut ULONGLONG; pub type ULONGLONG = u64; pub trait Interface { fn uuidof() -> GUID; } pub type LPSAFEARRAY = *mut SAFEARRAY; macro_rules! DEFINE_GUID { ( $name:ident, $l:expr, $w1:expr, $w2:expr, $b1:expr, $b2:expr, $b3:expr, $b4:expr, $b5:expr, $b6:expr, $b7:expr, $b8:expr ) => { pub const $name: $crate::windows::winapi::GUID = $crate::windows::winapi::GUID { data1: $l, data2: $w1, data3: $w2, data4: [$b1, $b2, $b3, $b4, $b5, $b6, $b7, $b8], }; }; } macro_rules! RIDL { (#[uuid($($uuid:expr),+)] interface $interface:ident ($vtbl:ident) {$( fn $method:ident($($p:ident : $t:ty,)*) -> $rtr:ty, )+}) => ( #[repr(C)] pub struct $vtbl { $(pub $method: unsafe extern "system" fn( This: *mut $interface, $($p: $t),* ) -> $rtr,)+ } #[repr(C)] pub struct $interface { pub lpVtbl: *const $vtbl, } RIDL!{@impl $interface {$(fn $method($($p: $t,)*) -> $rtr,)+}} RIDL!{@uuid $interface $($uuid),+} ); (#[uuid($($uuid:expr),+)] interface $interface:ident ($vtbl:ident) : $pinterface:ident ($pvtbl:ident) { }) => ( #[repr(C)] pub struct $vtbl { pub parent: $pvtbl, } #[repr(C)] pub struct $interface { pub lpVtbl: *const $vtbl, } RIDL!{@deref $interface $pinterface} RIDL!{@uuid $interface $($uuid),+} ); (#[uuid($($uuid:expr),+)] interface $interface:ident ($vtbl:ident) : $pinterface:ident ($pvtbl:ident) {$( fn $method:ident($($p:ident : $t:ty,)*) -> $rtr:ty, )+}) => ( #[repr(C)] pub struct $vtbl { pub parent: $pvtbl, $(pub $method: unsafe extern "system" fn( This: *mut $interface, $($p: $t,)* ) -> $rtr,)+ } #[repr(C)] pub struct $interface { pub lpVtbl: *const $vtbl, } RIDL!{@impl $interface {$(fn $method($($p: $t,)*) -> $rtr,)+}} RIDL!{@deref $interface $pinterface} RIDL!{@uuid $interface $($uuid),+} ); (@deref $interface:ident $pinterface:ident) => ( impl ::std::ops::Deref for $interface { type Target = $pinterface; #[inline] fn deref(&self) -> &$pinterface { unsafe { &*(self as *const $interface as *const $pinterface) } } } ); (@impl $interface:ident {$( fn $method:ident($($p:ident : $t:ty,)*) -> $rtr:ty, )+}) => ( impl $interface { $(#[inline] pub unsafe fn $method(&self, $($p: $t,)*) -> $rtr { ((*self.lpVtbl).$method)(self as *const _ as *mut _, $($p,)*) })+ } ); (@uuid $interface:ident $l:expr, $w1:expr, $w2:expr, $b1:expr, $b2:expr, $b3:expr, $b4:expr, $b5:expr, $b6:expr, $b7:expr, $b8:expr ) => ( impl $crate::windows::winapi::Interface for $interface { #[inline] fn uuidof() -> $crate::windows::winapi::GUID { $crate::windows::winapi::GUID { data1: $l, data2: $w1, data3: $w2, data4: [$b1, $b2, $b3, $b4, $b5, $b6, $b7, $b8], } } } ); } RIDL! {#[uuid(0x00000000, 0x0000, 0x0000, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46)] interface IUnknown(IUnknownVtbl) { fn QueryInterface( riid: REFIID, ppvObject: *mut *mut raw::c_void, ) -> HRESULT, fn AddRef() -> ULONG, fn Release() -> ULONG, }} cc-1.1.14/src/windows/windows_sys.rs000064400000000000000000000124721046102023000155220ustar 00000000000000// This file is autogenerated. // // To add bindings, edit windows_sys.lst then run: // // ``` // cd generate-windows-sys/ // cargo run // ``` // Bindings generated by `windows-bindgen` 0.58.0 #![allow( non_snake_case, non_upper_case_globals, non_camel_case_types, dead_code, clippy::all )] windows_targets::link!("advapi32.dll" "system" fn RegCloseKey(hkey : HKEY) -> WIN32_ERROR); windows_targets::link!("advapi32.dll" "system" fn RegEnumKeyExW(hkey : HKEY, dwindex : u32, lpname : PWSTR, lpcchname : *mut u32, lpreserved : *const u32, lpclass : PWSTR, lpcchclass : *mut u32, lpftlastwritetime : *mut FILETIME) -> WIN32_ERROR); windows_targets::link!("advapi32.dll" "system" fn RegOpenKeyExW(hkey : HKEY, lpsubkey : PCWSTR, uloptions : u32, samdesired : REG_SAM_FLAGS, phkresult : *mut HKEY) -> WIN32_ERROR); windows_targets::link!("advapi32.dll" "system" fn RegQueryValueExW(hkey : HKEY, lpvaluename : PCWSTR, lpreserved : *const u32, lptype : *mut REG_VALUE_TYPE, lpdata : *mut u8, lpcbdata : *mut u32) -> WIN32_ERROR); windows_targets::link!("kernel32.dll" "system" fn FreeLibrary(hlibmodule : HMODULE) -> BOOL); windows_targets::link!("kernel32.dll" "system" fn GetMachineTypeAttributes(machine : u16, machinetypeattributes : *mut MACHINE_ATTRIBUTES) -> HRESULT); windows_targets::link!("kernel32.dll" "system" fn GetProcAddress(hmodule : HMODULE, lpprocname : PCSTR) -> FARPROC); windows_targets::link!("kernel32.dll" "system" fn LoadLibraryA(lplibfilename : PCSTR) -> HMODULE); windows_targets::link!("kernel32.dll" "system" fn OpenSemaphoreA(dwdesiredaccess : u32, binherithandle : BOOL, lpname : PCSTR) -> HANDLE); windows_targets::link!("kernel32.dll" "system" fn PeekNamedPipe(hnamedpipe : HANDLE, lpbuffer : *mut core::ffi::c_void, nbuffersize : u32, lpbytesread : *mut u32, lptotalbytesavail : *mut u32, lpbytesleftthismessage : *mut u32) -> BOOL); windows_targets::link!("kernel32.dll" "system" fn ReleaseSemaphore(hsemaphore : HANDLE, lreleasecount : i32, lppreviouscount : *mut i32) -> BOOL); windows_targets::link!("kernel32.dll" "system" fn WaitForSingleObject(hhandle : HANDLE, dwmilliseconds : u32) -> WAIT_EVENT); windows_targets::link!("ole32.dll" "system" fn CoCreateInstance(rclsid : *const GUID, punkouter : * mut core::ffi::c_void, dwclscontext : CLSCTX, riid : *const GUID, ppv : *mut *mut core::ffi::c_void) -> HRESULT); windows_targets::link!("ole32.dll" "system" fn CoInitializeEx(pvreserved : *const core::ffi::c_void, dwcoinit : u32) -> HRESULT); windows_targets::link!("oleaut32.dll" "system" fn SysFreeString(bstrstring : BSTR)); windows_targets::link!("oleaut32.dll" "system" fn SysStringLen(pbstr : BSTR) -> u32); pub type ADVANCED_FEATURE_FLAGS = u16; pub type BOOL = i32; pub type BSTR = *const u16; pub type CLSCTX = u32; pub const CLSCTX_ALL: CLSCTX = 23u32; pub type COINIT = i32; pub const COINIT_MULTITHREADED: COINIT = 0i32; pub const ERROR_NO_MORE_ITEMS: WIN32_ERROR = 259u32; pub const ERROR_SUCCESS: WIN32_ERROR = 0u32; pub const FALSE: BOOL = 0i32; pub type FARPROC = Option isize>; #[repr(C)] #[derive(Clone, Copy)] pub struct FILETIME { pub dwLowDateTime: u32, pub dwHighDateTime: u32, } pub const FILE_ATTRIBUTE_TEMPORARY: FILE_FLAGS_AND_ATTRIBUTES = 256u32; pub type FILE_FLAGS_AND_ATTRIBUTES = u32; #[repr(C)] #[derive(Clone, Copy)] pub struct GUID { pub data1: u32, pub data2: u16, pub data3: u16, pub data4: [u8; 8], } impl GUID { pub const fn from_u128(uuid: u128) -> Self { Self { data1: (uuid >> 96) as u32, data2: (uuid >> 80 & 0xffff) as u16, data3: (uuid >> 64 & 0xffff) as u16, data4: (uuid as u64).to_be_bytes(), } } } pub type HANDLE = *mut core::ffi::c_void; pub type HKEY = *mut core::ffi::c_void; pub const HKEY_LOCAL_MACHINE: HKEY = -2147483646i32 as _; pub type HMODULE = *mut core::ffi::c_void; pub type HRESULT = i32; pub type IMAGE_FILE_MACHINE = u16; pub const IMAGE_FILE_MACHINE_AMD64: IMAGE_FILE_MACHINE = 34404u16; pub const KEY_READ: REG_SAM_FLAGS = 131097u32; pub const KEY_WOW64_32KEY: REG_SAM_FLAGS = 512u32; pub type MACHINE_ATTRIBUTES = i32; pub type PCSTR = *const u8; pub type PCWSTR = *const u16; pub type PWSTR = *mut u16; pub type REG_SAM_FLAGS = u32; pub const REG_SZ: REG_VALUE_TYPE = 1u32; pub type REG_VALUE_TYPE = u32; #[repr(C)] #[derive(Clone, Copy)] pub struct SAFEARRAY { pub cDims: u16, pub fFeatures: ADVANCED_FEATURE_FLAGS, pub cbElements: u32, pub cLocks: u32, pub pvData: *mut core::ffi::c_void, pub rgsabound: [SAFEARRAYBOUND; 1], } #[repr(C)] #[derive(Clone, Copy)] pub struct SAFEARRAYBOUND { pub cElements: u32, pub lLbound: i32, } pub const SEMAPHORE_MODIFY_STATE: SYNCHRONIZATION_ACCESS_RIGHTS = 2u32; pub type SYNCHRONIZATION_ACCESS_RIGHTS = u32; pub const S_FALSE: HRESULT = 0x1_u32 as _; pub const S_OK: HRESULT = 0x0_u32 as _; pub type THREAD_ACCESS_RIGHTS = u32; pub const THREAD_SYNCHRONIZE: THREAD_ACCESS_RIGHTS = 1048576u32; pub const UserEnabled: MACHINE_ATTRIBUTES = 1i32; pub const WAIT_ABANDONED: WAIT_EVENT = 128u32; pub type WAIT_EVENT = u32; pub const WAIT_FAILED: WAIT_EVENT = 4294967295u32; pub const WAIT_OBJECT_0: WAIT_EVENT = 0u32; pub const WAIT_TIMEOUT: WAIT_EVENT = 258u32; pub type WIN32_ERROR = u32; #[link(name = "advapi32")] #[link(name = "ole32")] #[link(name = "oleaut32")] extern "C" {} use super::windows_targets; cc-1.1.14/src/windows/windows_targets.rs000064400000000000000000000016061046102023000163520ustar 00000000000000//! Provides the `link!` macro used by the generated windows bindings. //! //! This is a simple wrapper around an `extern` block with a `#[link]` attribute. //! It's very roughly equivalent to the windows-targets crate. macro_rules! link_macro { ($library:literal $abi:literal $($link_name:literal)? $(#[$doc:meta])? fn $($function:tt)*) => ( // Note: the windows-targets crate uses a pre-built Windows.lib import library which we don't // have in this repo. So instead we always link kernel32.lib and add the rest of the import // libraries below by using an empty extern block. This works because extern blocks are not // connected to the library given in the #[link] attribute. #[link(name = "kernel32")] extern $abi { $(#[link_name=$link_name])? pub fn $($function)*; } ) } pub(crate) use link_macro as link;