├── .github └── workflows │ └── ci.yml ├── .gitignore ├── CHANGELOG.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── appveyor.yml ├── assets └── xargo.png ├── bors.toml ├── build.rs ├── src ├── bin │ ├── xargo-check.rs │ └── xargo.rs ├── cargo.rs ├── cli.rs ├── errors.rs ├── extensions.rs ├── flock.rs ├── lib.rs ├── rustc.rs ├── sysroot.rs ├── util.rs └── xargo.rs └── tests └── smoke.rs /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Continuous Integration 2 | on: 3 | pull_request: 4 | push: 5 | branches: 6 | - master 7 | - staging 8 | - trying 9 | 10 | jobs: 11 | # NOTE all these jobs need to be added to the `needs` section of the ci-* jobs! 12 | build: 13 | name: build 14 | 15 | strategy: 16 | matrix: 17 | os: 18 | - ubuntu-20.04 19 | target: 20 | - i686-unknown-linux-gnu 21 | - i686-unknown-linux-musl 22 | - x86_64-unknown-linux-gnu 23 | - x86_64-unknown-linux-musl 24 | include: 25 | - os: macos-latest 26 | target: x86_64-apple-darwin 27 | - os: windows-latest 28 | target: x86_64-pc-windows-gnu 29 | 30 | runs-on: ${{ matrix.os }} 31 | 32 | steps: 33 | - name: Checkout 34 | uses: actions/checkout@v2 35 | 36 | - name: Install Rust 37 | uses: actions-rs/toolchain@v1 38 | with: 39 | target: ${{ matrix.target }} 40 | toolchain: stable 41 | override: true 42 | 43 | - name: Build 44 | uses: actions-rs/cargo@v1 45 | with: 46 | use-cross: ${{ matrix.os == 'ubuntu-20.04' }} 47 | command: build 48 | args: --target ${{ matrix.target }} --locked 49 | 50 | - name: Run 51 | uses: actions-rs/cargo@v1 52 | with: 53 | use-cross: ${{ matrix.os == 'ubuntu-20.04' }} 54 | command: run 55 | args: --target ${{ matrix.target }} -- -V 56 | 57 | test: 58 | name: test 59 | 60 | strategy: 61 | matrix: 62 | toolchain: 63 | - nightly 64 | # Don't remove this target; test coverage in `smoke.rs` relies on us 65 | # running at least one pinned toolchain. 66 | - nightly-2021-08-01 67 | 68 | runs-on: ubuntu-20.04 69 | 70 | steps: 71 | - name: Checkout 72 | uses: actions/checkout@v2 73 | 74 | - name: Install Rust 75 | uses: actions-rs/toolchain@v1 76 | with: 77 | components: rust-src 78 | toolchain: ${{ matrix.toolchain }} 79 | override: true 80 | 81 | - name: Test 82 | uses: actions-rs/cargo@v1 83 | with: 84 | command: test 85 | 86 | # Refs: https://github.com/rust-lang/crater/blob/9ab6f9697c901c4a44025cf0a39b73ad5b37d198/.github/workflows/bors.yml#L125-L149 87 | # bors.tech integration 88 | ci-success: 89 | name: ci 90 | if: ${{ success() }} 91 | needs: 92 | - build 93 | - test 94 | runs-on: ubuntu-20.04 95 | steps: 96 | - name: CI succeeded 97 | run: exit 0 98 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.org 2 | *.rs.bk 3 | target 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | This project adheres to [Semantic Versioning](http://semver.org/). 5 | 6 | ## [v0.3.26] - 2022-06-01 7 | 8 | ### Fixed 9 | 10 | - Fix handling spaces in the sysroot path. 11 | 12 | ## [v0.3.25] - 2022-03-26 13 | 14 | ### Changed 15 | 16 | - No longer enable the `backtrace` feature of `error-chain` by default to reduce 17 | build times. Install xargo with `--features backtrace` to re-enable 18 | backtraces. 19 | 20 | ## [v0.3.24] - 2021-09-21 21 | 22 | ### Fixed 23 | 24 | - Fix `compiler_builtins` error when building without explicit dependencies in 25 | the `Xargo.toml` (thanks to @anickol). 26 | 27 | ## [v0.3.23] - 2021-05-08 28 | 29 | ### Fixed 30 | 31 | - Allow running xargo from outside project root (thanks to @priyasiddharth). 32 | - Fix xargo when a target_dir is set for cargo (thanks to @oxalica). 33 | - Fix xargo for Windows targets. 34 | 35 | ## [v0.3.22] - 2020-07-29 36 | 37 | ### Fixed 38 | 39 | - Fix for changed rustc directory layout. 40 | - Support relative XARGO_RUST_SRC. 41 | 42 | ## [v0.3.21] - 2020-07-11 43 | 44 | ### Fixed 45 | 46 | - Make xargo fail on non-nightly, instead of continuing with the wrong sysroot. 47 | - Avoid adding '/' or '..' to paths for better Windows compatibility. 48 | 49 | ## [v0.3.20] - 2020-03-25 50 | 51 | ### Fixed 52 | 53 | - Check-only builds of `windows-gnu` targets now work without that target being 54 | installed for the toolchain. 55 | 56 | ## [v0.3.19] - 2019-12-31 57 | 58 | ### Added 59 | 60 | - Added a `xargo-check` binary that can be used for check-only builds of libstd 61 | (thanks to @Aaron1011). 62 | 63 | ## [v0.3.18] - 2019-12-08 64 | 65 | ### Added 66 | 67 | - Xargo now forces metadata for sysroot crates to be different from "normal" 68 | crates to avoid issues with custom sysroots that share crates with the 69 | project (thanks to @roblabla). 70 | 71 | ### Fixed 72 | 73 | - Build could fail when the rust-src directory was read-only 74 | (thanks to @leo60228). 75 | 76 | ## [v0.3.17] - 2019-11-08 77 | 78 | ### Added 79 | 80 | - Xargo now honors the `Cargo.lock` file shipped in the `rust-src` component, to 81 | make builds reproducible even when new versions of libraries are released on 82 | `crates.io`. 83 | - Xargo now searches for the `Xargo.toml` file in parent directories as well, 84 | not just in the current directory (thanks to @Nils-TUD and @Thog). 85 | 86 | ### Fixed 87 | 88 | - The `--message-format` was only forwarded to the project build, not the 89 | sysroot build (thanks to @roblabla). 90 | 91 | ## [v0.3.16] - 2019-08-26 92 | 93 | ### Fixed 94 | 95 | - Xargo now works again with recent nightlies. 96 | 97 | ## [v0.3.15] - 2019-08-08 98 | 99 | ### Added 100 | 101 | - The cargo binary used by Xargo can be overwritten using the `CARGO` environment variable. 102 | (The same was already possible for rustc before, using `RUSTC`.) 103 | 104 | ### Fixed 105 | 106 | - Xargo now works again with recent nightlies. 107 | 108 | ## [v0.3.14] - 2019-05-09 109 | 110 | ### Fixed 111 | 112 | - Xargo now works again with recent nightlies. 113 | - Fixed Xargo for targets that do not have a `bin` directory in their rustlib folder. 114 | 115 | ### Added 116 | 117 | - Allow specifying `[patch]` section in `Xargo.toml`. 118 | 119 | ## [v0.3.13] - 2018-12-18 120 | 121 | ### Fixed 122 | 123 | - Xargo now works again with recent nightlies. 124 | 125 | ### Added 126 | 127 | - When the `XARGO_KEEP_TEMP` env variable is set Xargo will keep the temporary 128 | directory used to build the sysroot. This is useful for debugging problems in 129 | Xargo. 130 | 131 | ## [v0.3.12] - 2018-04-08 132 | 133 | ### Changed 134 | 135 | - The `core` and `compiler_builtins` crates are built when no Xargo.toml is present. 136 | 137 | ## [v0.3.11] - 2018-03-09 138 | 139 | ### Added 140 | 141 | - Xargo now copies the `bin` directory from the original sysroot, the host sysroot, into its own. 142 | This lets you use binaries shipped with the Rust toolchain, like LLD. 143 | 144 | ## [v0.3.10] - 2017-12-28 145 | 146 | ### Added 147 | 148 | - Print a warning when the stable or beta toolchain, which are not supported, is used. 149 | 150 | ### Changed 151 | 152 | - Set RUST_TARGET_PATH when building the sysroot. This fixes builds when using custom targets with a 153 | recent toolchain. 154 | 155 | ### Removed 156 | 157 | - The lock file included in the rust-src component is no longer used when building the sysroot. This 158 | fixes building a sysroot that contains the compiler-builtins crate. 159 | 160 | ## [v0.3.9] - 2017-09-06 161 | 162 | ### Added 163 | 164 | - Use Cargo.lock from the `rust-src` component if available. With this change 165 | the Xargo sysroot will be built using the exact same set of dependencies that 166 | the official sysroot distributed via rustup uses. 167 | 168 | - The `RUSTFLAGS` variable internally used by Xargo is now printed when verbose 169 | (`-v`) mode is enabled. 170 | 171 | ### Changed 172 | 173 | - Updated the documentation about building `std` with recent nightlies. 174 | 175 | ## [v0.3.8] - 2017-05-30 176 | 177 | ### Changed 178 | 179 | - Improved the error message when `--target foo.json` is used. 180 | 181 | ## [v0.3.7] - 2017-05-13 182 | 183 | ### Changed 184 | 185 | - Improved the error message when the `rust-src` component is missing. 186 | 187 | ## [v0.3.6] - 2017-04-07 188 | 189 | ### Fixed 190 | 191 | - Xargo on Windows. The layout of the default / rustc sysroot recently changed 192 | on Windows on broke the code that copied the host part of the rustc sysroot 193 | into the Xargo sysroot. 194 | 195 | ## [v0.3.5] - 2017-01-20 196 | 197 | ### Fixed 198 | 199 | - Relative paths in `dependencies.{}.path` were not being correctly handled. 200 | 201 | ## [v0.3.4] - 2017-01-18 202 | 203 | ### Added 204 | 205 | - A `[dependencies.{}.stage]` (or `[target.{}.dependencies.{}.stage]`) entry in 206 | Xargo.toml. This lets you build a sysroot in "stages". This is required, for 207 | instance, to build the `test` crate whose dependency on the `std` crate is not 208 | explicitly listed in its Cargo.toml. Example: 209 | 210 | To make `xargo test` work 211 | 212 | ``` toml 213 | # Xargo.toml 214 | [dependencies.std] 215 | features = ["panic_unwind"] # `test` depends on this `std` feature 216 | # stage = 0 # implicit 217 | 218 | [dependencies.test] 219 | stage = 1 220 | ``` 221 | 222 | - Support for `[dependencies.{}.git]` or `[dependencies.{}.path]` (and their 223 | `target.{}.dependencies` variants) in Xargo.toml. With this feature you can 224 | inject foreign crates (crates which are not part of the `rust-src` component) 225 | into the sysroot. The main use case is replacing the `std` crate with a drop 226 | in replacement. Example: 227 | 228 | Replace `std` with [`steed`](https://github.com/japaric/steed) 229 | 230 | ``` toml 231 | [dependencies.collections] # `steed` depends on `collections` 232 | 233 | [dependencies.std] 234 | git = "https://github.com/japaric/steed" 235 | stage = 1 236 | ``` 237 | 238 | ## [v0.3.3] - 2017-01-09 239 | 240 | ### Added 241 | 242 | - Support for building a custom sysroot when compiling natively. 243 | - Support for specifying dependencies as `[dependencies]` in Xargo.toml. 244 | 245 | ## [v0.3.2] - 2017-01-03 246 | 247 | ### Changed 248 | 249 | - `XARGO_RUST_SRC` is now used when working with nightly Rust and it has 250 | precedence over the `rust-src` component. 251 | 252 | ## [v0.3.1] - 2016-12-30 253 | 254 | ### Added 255 | 256 | - You can now specify the location where Xargo stores the sysroots via the 257 | `XARGO_HOME` environment variable. If unspecified, the sysroots will be stored 258 | in `$HOME/.xargo` 259 | 260 | ## [v0.3.0] - 2016-12-28 261 | 262 | ### Changed 263 | 264 | - [breaking-change] By default, Xargo now only compiles the `core` crate. To 265 | build more crates, use a `Xargo.toml` file 266 | 267 | - [breaking-change] Xargo will now build a sysroot for any target that's not the 268 | host. 269 | 270 | - The verbose flag, `-v`, makes Xargo print all the shell commands it invokes 271 | to stderr. 272 | 273 | ## [v0.2.3] - 2016-12-19 274 | 275 | ### Added 276 | 277 | - Support for the 'dev' channel. When using the dev channel, you must specify 278 | the path to the Rust source directory via the XARGO_RUST_SRC environment 279 | variable. 280 | 281 | ### Changed 282 | 283 | - The rust-src search logic to account for recent changes in the Rust 284 | distribution. 285 | 286 | ## [v0.2.2] - 2016-12-12 287 | 288 | ### Changed 289 | 290 | - Xargo will now try to build every crate "below" `std`, i.e. all its 291 | dependencies, in topological order. This makes Xargo robust against changes in 292 | the `std` facade as it no longer depends on hard coded crate names like 293 | `rustc_unicode`. 294 | 295 | - Xargo won't rebuild the sysroot if the only thing that changed in Cargo.toml 296 | is profile.*.lto. Enabling/disabling LTO doesn't change how dependencies are 297 | compiled. 298 | 299 | - Xargo won't rebuild the sysroot if the linker flags (`-C link-arg`) have 300 | changed. Those don't affect how the dependencies are compiled. 301 | 302 | ## [v0.2.1] - 2016-10-22 303 | 304 | ### Changed 305 | 306 | - No weird `()` output in `xargo -V` if Xargo was built via `cargo install` 307 | - Better formatted error messages. Mention the RUST_BACKTRACE env variable which 308 | is used to get backtraces on errors. 309 | 310 | ## [v0.2.0] - 2016-10-16 311 | 312 | ### Added 313 | 314 | - Statically linked binary releases for Linux (x86 musl targets) 315 | - `xargo -V` output now includes the commit hash and date 316 | 317 | ### Changed 318 | 319 | - Xargo now depends on the `rust-src` component being installed. Install it with 320 | `rustup component add rust-src`. 321 | - Xargo no longer depends on libcurl, libssh or libssl and, therefore, it's now 322 | much easier to build. 323 | - Xargo now respects the existing rustdocflags (RUSTDOCFLAGS env var, 324 | build.rustdocflags, etc) when passing --sysroot to rustdoc. 325 | - File locking logic has been revised/simplied and now lock periods are shorter 326 | 327 | ## [v0.1.14] - 2016-10-09 328 | 329 | ### Added 330 | 331 | - `xargo -V` and `xargo --version` now report Xargo's version as well as 332 | Cargo's. 333 | 334 | ## [v0.1.13] - 2016-10-06 335 | 336 | ### Added 337 | 338 | - Xargo now builds a sysroot for the new built-in `thumbv*-none-eabi*` targets 339 | which don't ship with a binary release of the standard crates. 340 | 341 | ## [v0.1.12] - 2016-10-04 342 | 343 | ### Added 344 | 345 | - Xargo now supports per-target rustflags: 346 | `target.thumbv7em-none-eabihf.rustflags` in .cargo/config. 347 | 348 | ## [v0.1.11] - 2016-09-30 349 | 350 | ### Fixed 351 | 352 | - `xargo clean` and other commands not associated to building stuff no longer 353 | trigger a sysroot rebuild. 354 | 355 | ## [v0.1.10] - 2016-09-28 356 | 357 | ### Fixed 358 | 359 | - `xargo doc`, which wasn't working because we didn't pass --sysroot to rustdoc. 360 | Note that rustdoc gained support for '--sysroot' as of nightly-2016-06-28, so 361 | that version or newer is required to use `xargo doc`. 362 | 363 | ## [v0.1.9] - 2016-09-27 364 | 365 | ### Fixed 366 | 367 | - "error: Invalid cross-device link (os error 18)" which occurred when 368 | `$CARGO_HOME` was mounted in a different device than "`$XARGO_HOME`" 369 | (~/.xargo). The solution was to stop using hard links to place the host 370 | libraries in the Xargo sysroot and instead just copy them. This is a 371 | regression in disk usage but this problem was coming up in common Docker usage 372 | patterns (-v A:B). 373 | 374 | ## [v0.1.8] - 2016-09-04 375 | 376 | ### Changed 377 | 378 | - All the status messages are now printed to stderr instead of to stdout. Cargo 379 | did the same change (from stdout to stderr) a while ago. Let's follow suit. 380 | 381 | ### Fixed 382 | 383 | - When compiling crate `foo` with Xargo, the profile section of `foo`'s 384 | Cargo.toml is also "taken into account" when compiling the sysroot. For 385 | example, if `foo` has set `panic = "abort"` for all its profiles, then the 386 | sysroot will also be compiled with `-C panic=abort`. Previously, this wasn't 387 | the case. 388 | 389 | ## [v0.1.7] - 2016-09-03 390 | 391 | ### Fixed 392 | 393 | - The sysroot now gets rebuilt when rust-src changes. 394 | 395 | ## [v0.1.6] - 2016-08-29 396 | 397 | ### Added 398 | 399 | - Xargo can now use the source code installed by rustup. When available, this is 400 | the preferred way to fetch the source code and saves network bandwidth by not 401 | having to fetch the source tarball. 402 | 403 | ## [v0.1.5] - 2016-08-11 404 | 405 | ### Fixed 406 | 407 | - Xargo now works properly when called from a `rustup override`n directory. 408 | 409 | ## [v0.1.4] - 2016-08-06 410 | 411 | ### Added 412 | 413 | - Support targets that don't support atomics (`"max-atomic-width": 0`). For 414 | these targets, Xargo only compiles the `core` and `rustc_unicode` crates as 415 | the other crates depend on atomics (e.g. `alloc::Arc`). 416 | 417 | ## [v0.1.3] - 2016-04-24 418 | 419 | ### Added 420 | 421 | - `xargo (..) --verbose` passes `--verbose` to the `cargo` call that builds the 422 | sysroot. 423 | - the sysroot now gets rebuilt when RUSTFLAGS or build.rustflags is modified. 424 | 425 | ### Fixed 426 | 427 | - Xargo now respects the build.rustflags value set in .cargo/config. 428 | - A bug where the hash/date file didn't get properly truncated before updating 429 | it leading to Xargo to *always* trigger a sysroot rebuild. 430 | 431 | ## [v0.1.2] - 2016-04-24 [YANKED] 432 | 433 | ### Added 434 | 435 | - Xargo now uses file locking and can be executed concurrently. 436 | - Xargo now print its current status to the console while building a sysroot. 437 | - Xargo now reports errors to the console instead of panicking. 438 | 439 | ### Removed 440 | 441 | - Logging via `RUST_LOG` has been removed now that Xargo prints its status to 442 | the console. 443 | 444 | ## v0.1.1 - 2016-04-10 445 | 446 | - Initial release 447 | 448 | [Unreleased]: https://github.com/japaric/xargo/compare/v0.3.15...HEAD 449 | [v0.3.15]: https://github.com/japaric/xargo/compare/v0.3.14...v0.3.15 450 | [v0.3.14]: https://github.com/japaric/xargo/compare/v0.3.13...v0.3.14 451 | [v0.3.13]: https://github.com/japaric/xargo/compare/v0.3.12...v0.3.13 452 | [v0.3.12]: https://github.com/japaric/xargo/compare/v0.3.11...v0.3.12 453 | [v0.3.11]: https://github.com/japaric/xargo/compare/v0.3.10...v0.3.11 454 | [v0.3.10]: https://github.com/japaric/xargo/compare/v0.3.9...v0.3.10 455 | [v0.3.9]: https://github.com/japaric/xargo/compare/v0.3.8...v0.3.9 456 | [v0.3.8]: https://github.com/japaric/xargo/compare/v0.3.7...v0.3.8 457 | [v0.3.7]: https://github.com/japaric/xargo/compare/v0.3.6...v0.3.7 458 | [v0.3.6]: https://github.com/japaric/xargo/compare/v0.3.5...v0.3.6 459 | [v0.3.5]: https://github.com/japaric/xargo/compare/v0.3.4...v0.3.5 460 | [v0.3.4]: https://github.com/japaric/xargo/compare/v0.3.3...v0.3.4 461 | [v0.3.3]: https://github.com/japaric/xargo/compare/v0.3.2...v0.3.3 462 | [v0.3.2]: https://github.com/japaric/xargo/compare/v0.3.1...v0.3.2 463 | [v0.3.1]: https://github.com/japaric/xargo/compare/v0.3.0...v0.3.1 464 | [v0.3.0]: https://github.com/japaric/xargo/compare/v0.2.3...v0.3.0 465 | [v0.2.3]: https://github.com/japaric/xargo/compare/v0.2.2...v0.2.3 466 | [v0.2.2]: https://github.com/japaric/xargo/compare/v0.2.1...v0.2.2 467 | [v0.2.1]: https://github.com/japaric/xargo/compare/v0.2.0...v0.2.1 468 | [v0.2.0]: https://github.com/japaric/xargo/compare/v0.1.14...v0.2.0 469 | [v0.1.14]: https://github.com/japaric/xargo/compare/v0.1.13...v0.1.14 470 | [v0.1.13]: https://github.com/japaric/xargo/compare/v0.1.12...v0.1.13 471 | [v0.1.12]: https://github.com/japaric/xargo/compare/v0.1.11...v0.1.12 472 | [v0.1.11]: https://github.com/japaric/xargo/compare/v0.1.10...v0.1.11 473 | [v0.1.10]: https://github.com/japaric/xargo/compare/v0.1.9...v0.1.10 474 | [v0.1.9]: https://github.com/japaric/xargo/compare/v0.1.8...v0.1.9 475 | [v0.1.8]: https://github.com/japaric/xargo/compare/v0.1.7...v0.1.8 476 | [v0.1.7]: https://github.com/japaric/xargo/compare/v0.1.6...v0.1.7 477 | [v0.1.6]: https://github.com/japaric/xargo/compare/v0.1.5...v0.1.6 478 | [v0.1.5]: https://github.com/japaric/xargo/compare/v0.1.4...v0.1.5 479 | [v0.1.4]: https://github.com/japaric/xargo/compare/v0.1.3...v0.1.4 480 | [v0.1.3]: https://github.com/japaric/xargo/compare/v0.1.2...v0.1.3 481 | [v0.1.2]: https://github.com/japaric/xargo/compare/v0.1.1...v0.1.2 482 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "addr2line" 7 | version = "0.17.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler" 16 | version = "1.0.2" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 19 | 20 | [[package]] 21 | name = "autocfg" 22 | version = "1.1.0" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 25 | 26 | [[package]] 27 | name = "backtrace" 28 | version = "0.3.64" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "5e121dee8023ce33ab248d9ce1493df03c3b38a659b240096fcbd7048ff9c31f" 31 | dependencies = [ 32 | "addr2line", 33 | "cc", 34 | "cfg-if", 35 | "libc", 36 | "miniz_oxide", 37 | "object", 38 | "rustc-demangle", 39 | ] 40 | 41 | [[package]] 42 | name = "bitflags" 43 | version = "1.3.2" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 46 | 47 | [[package]] 48 | name = "cc" 49 | version = "1.0.73" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" 52 | 53 | [[package]] 54 | name = "cfg-if" 55 | version = "1.0.0" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 58 | 59 | [[package]] 60 | name = "dirs" 61 | version = "4.0.0" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" 64 | dependencies = [ 65 | "dirs-sys", 66 | ] 67 | 68 | [[package]] 69 | name = "dirs-sys" 70 | version = "0.3.7" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" 73 | dependencies = [ 74 | "libc", 75 | "redox_users", 76 | "winapi", 77 | ] 78 | 79 | [[package]] 80 | name = "error-chain" 81 | version = "0.12.4" 82 | source = "registry+https://github.com/rust-lang/crates.io-index" 83 | checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" 84 | dependencies = [ 85 | "backtrace", 86 | "version_check", 87 | ] 88 | 89 | [[package]] 90 | name = "fs2" 91 | version = "0.4.3" 92 | source = "registry+https://github.com/rust-lang/crates.io-index" 93 | checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" 94 | dependencies = [ 95 | "libc", 96 | "winapi", 97 | ] 98 | 99 | [[package]] 100 | name = "fuchsia-cprng" 101 | version = "0.1.1" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" 104 | 105 | [[package]] 106 | name = "getrandom" 107 | version = "0.2.5" 108 | source = "registry+https://github.com/rust-lang/crates.io-index" 109 | checksum = "d39cd93900197114fa1fcb7ae84ca742095eed9442088988ae74fa744e930e77" 110 | dependencies = [ 111 | "cfg-if", 112 | "libc", 113 | "wasi", 114 | ] 115 | 116 | [[package]] 117 | name = "gimli" 118 | version = "0.26.1" 119 | source = "registry+https://github.com/rust-lang/crates.io-index" 120 | checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4" 121 | 122 | [[package]] 123 | name = "itoa" 124 | version = "1.0.1" 125 | source = "registry+https://github.com/rust-lang/crates.io-index" 126 | checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" 127 | 128 | [[package]] 129 | name = "lazy_static" 130 | version = "1.4.0" 131 | source = "registry+https://github.com/rust-lang/crates.io-index" 132 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 133 | 134 | [[package]] 135 | name = "libc" 136 | version = "0.2.121" 137 | source = "registry+https://github.com/rust-lang/crates.io-index" 138 | checksum = "efaa7b300f3b5fe8eb6bf21ce3895e1751d9665086af2d64b42f19701015ff4f" 139 | 140 | [[package]] 141 | name = "lock_api" 142 | version = "0.4.6" 143 | source = "registry+https://github.com/rust-lang/crates.io-index" 144 | checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b" 145 | dependencies = [ 146 | "scopeguard", 147 | ] 148 | 149 | [[package]] 150 | name = "memchr" 151 | version = "2.4.1" 152 | source = "registry+https://github.com/rust-lang/crates.io-index" 153 | checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" 154 | 155 | [[package]] 156 | name = "miniz_oxide" 157 | version = "0.4.4" 158 | source = "registry+https://github.com/rust-lang/crates.io-index" 159 | checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" 160 | dependencies = [ 161 | "adler", 162 | "autocfg", 163 | ] 164 | 165 | [[package]] 166 | name = "object" 167 | version = "0.27.1" 168 | source = "registry+https://github.com/rust-lang/crates.io-index" 169 | checksum = "67ac1d3f9a1d3616fd9a60c8d74296f22406a238b6a72f5cc1e6f314df4ffbf9" 170 | dependencies = [ 171 | "memchr", 172 | ] 173 | 174 | [[package]] 175 | name = "parking_lot" 176 | version = "0.12.0" 177 | source = "registry+https://github.com/rust-lang/crates.io-index" 178 | checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58" 179 | dependencies = [ 180 | "lock_api", 181 | "parking_lot_core", 182 | ] 183 | 184 | [[package]] 185 | name = "parking_lot_core" 186 | version = "0.9.1" 187 | source = "registry+https://github.com/rust-lang/crates.io-index" 188 | checksum = "28141e0cc4143da2443301914478dc976a61ffdb3f043058310c70df2fed8954" 189 | dependencies = [ 190 | "cfg-if", 191 | "libc", 192 | "redox_syscall", 193 | "smallvec", 194 | "windows-sys", 195 | ] 196 | 197 | [[package]] 198 | name = "proc-macro2" 199 | version = "1.0.36" 200 | source = "registry+https://github.com/rust-lang/crates.io-index" 201 | checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" 202 | dependencies = [ 203 | "unicode-xid", 204 | ] 205 | 206 | [[package]] 207 | name = "quote" 208 | version = "1.0.16" 209 | source = "registry+https://github.com/rust-lang/crates.io-index" 210 | checksum = "b4af2ec4714533fcdf07e886f17025ace8b997b9ce51204ee69b6da831c3da57" 211 | dependencies = [ 212 | "proc-macro2", 213 | ] 214 | 215 | [[package]] 216 | name = "rand" 217 | version = "0.4.6" 218 | source = "registry+https://github.com/rust-lang/crates.io-index" 219 | checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" 220 | dependencies = [ 221 | "fuchsia-cprng", 222 | "libc", 223 | "rand_core 0.3.1", 224 | "rdrand", 225 | "winapi", 226 | ] 227 | 228 | [[package]] 229 | name = "rand_core" 230 | version = "0.3.1" 231 | source = "registry+https://github.com/rust-lang/crates.io-index" 232 | checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" 233 | dependencies = [ 234 | "rand_core 0.4.2", 235 | ] 236 | 237 | [[package]] 238 | name = "rand_core" 239 | version = "0.4.2" 240 | source = "registry+https://github.com/rust-lang/crates.io-index" 241 | checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" 242 | 243 | [[package]] 244 | name = "rdrand" 245 | version = "0.4.0" 246 | source = "registry+https://github.com/rust-lang/crates.io-index" 247 | checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" 248 | dependencies = [ 249 | "rand_core 0.3.1", 250 | ] 251 | 252 | [[package]] 253 | name = "redox_syscall" 254 | version = "0.2.11" 255 | source = "registry+https://github.com/rust-lang/crates.io-index" 256 | checksum = "8380fe0152551244f0747b1bf41737e0f8a74f97a14ccefd1148187271634f3c" 257 | dependencies = [ 258 | "bitflags", 259 | ] 260 | 261 | [[package]] 262 | name = "redox_users" 263 | version = "0.4.2" 264 | source = "registry+https://github.com/rust-lang/crates.io-index" 265 | checksum = "7776223e2696f1aa4c6b0170e83212f47296a00424305117d013dfe86fb0fe55" 266 | dependencies = [ 267 | "getrandom", 268 | "redox_syscall", 269 | "thiserror", 270 | ] 271 | 272 | [[package]] 273 | name = "remove_dir_all" 274 | version = "0.5.3" 275 | source = "registry+https://github.com/rust-lang/crates.io-index" 276 | checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" 277 | dependencies = [ 278 | "winapi", 279 | ] 280 | 281 | [[package]] 282 | name = "rustc-demangle" 283 | version = "0.1.21" 284 | source = "registry+https://github.com/rust-lang/crates.io-index" 285 | checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" 286 | 287 | [[package]] 288 | name = "rustc_version" 289 | version = "0.4.0" 290 | source = "registry+https://github.com/rust-lang/crates.io-index" 291 | checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" 292 | dependencies = [ 293 | "semver", 294 | ] 295 | 296 | [[package]] 297 | name = "ryu" 298 | version = "1.0.9" 299 | source = "registry+https://github.com/rust-lang/crates.io-index" 300 | checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" 301 | 302 | [[package]] 303 | name = "same-file" 304 | version = "1.0.6" 305 | source = "registry+https://github.com/rust-lang/crates.io-index" 306 | checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 307 | dependencies = [ 308 | "winapi-util", 309 | ] 310 | 311 | [[package]] 312 | name = "scopeguard" 313 | version = "1.1.0" 314 | source = "registry+https://github.com/rust-lang/crates.io-index" 315 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 316 | 317 | [[package]] 318 | name = "semver" 319 | version = "1.0.6" 320 | source = "registry+https://github.com/rust-lang/crates.io-index" 321 | checksum = "a4a3381e03edd24287172047536f20cabde766e2cd3e65e6b00fb3af51c4f38d" 322 | 323 | [[package]] 324 | name = "serde" 325 | version = "1.0.136" 326 | source = "registry+https://github.com/rust-lang/crates.io-index" 327 | checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" 328 | 329 | [[package]] 330 | name = "serde_json" 331 | version = "1.0.79" 332 | source = "registry+https://github.com/rust-lang/crates.io-index" 333 | checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95" 334 | dependencies = [ 335 | "itoa", 336 | "ryu", 337 | "serde", 338 | ] 339 | 340 | [[package]] 341 | name = "smallvec" 342 | version = "1.8.0" 343 | source = "registry+https://github.com/rust-lang/crates.io-index" 344 | checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" 345 | 346 | [[package]] 347 | name = "syn" 348 | version = "1.0.89" 349 | source = "registry+https://github.com/rust-lang/crates.io-index" 350 | checksum = "ea297be220d52398dcc07ce15a209fce436d361735ac1db700cab3b6cdfb9f54" 351 | dependencies = [ 352 | "proc-macro2", 353 | "quote", 354 | "unicode-xid", 355 | ] 356 | 357 | [[package]] 358 | name = "tempdir" 359 | version = "0.3.7" 360 | source = "registry+https://github.com/rust-lang/crates.io-index" 361 | checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" 362 | dependencies = [ 363 | "rand", 364 | "remove_dir_all", 365 | ] 366 | 367 | [[package]] 368 | name = "thiserror" 369 | version = "1.0.30" 370 | source = "registry+https://github.com/rust-lang/crates.io-index" 371 | checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" 372 | dependencies = [ 373 | "thiserror-impl", 374 | ] 375 | 376 | [[package]] 377 | name = "thiserror-impl" 378 | version = "1.0.30" 379 | source = "registry+https://github.com/rust-lang/crates.io-index" 380 | checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" 381 | dependencies = [ 382 | "proc-macro2", 383 | "quote", 384 | "syn", 385 | ] 386 | 387 | [[package]] 388 | name = "toml" 389 | version = "0.5.8" 390 | source = "registry+https://github.com/rust-lang/crates.io-index" 391 | checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" 392 | dependencies = [ 393 | "serde", 394 | ] 395 | 396 | [[package]] 397 | name = "unicode-xid" 398 | version = "0.2.2" 399 | source = "registry+https://github.com/rust-lang/crates.io-index" 400 | checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" 401 | 402 | [[package]] 403 | name = "version_check" 404 | version = "0.9.4" 405 | source = "registry+https://github.com/rust-lang/crates.io-index" 406 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 407 | 408 | [[package]] 409 | name = "walkdir" 410 | version = "2.3.2" 411 | source = "registry+https://github.com/rust-lang/crates.io-index" 412 | checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" 413 | dependencies = [ 414 | "same-file", 415 | "winapi", 416 | "winapi-util", 417 | ] 418 | 419 | [[package]] 420 | name = "wasi" 421 | version = "0.10.2+wasi-snapshot-preview1" 422 | source = "registry+https://github.com/rust-lang/crates.io-index" 423 | checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" 424 | 425 | [[package]] 426 | name = "winapi" 427 | version = "0.3.9" 428 | source = "registry+https://github.com/rust-lang/crates.io-index" 429 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 430 | dependencies = [ 431 | "winapi-i686-pc-windows-gnu", 432 | "winapi-x86_64-pc-windows-gnu", 433 | ] 434 | 435 | [[package]] 436 | name = "winapi-i686-pc-windows-gnu" 437 | version = "0.4.0" 438 | source = "registry+https://github.com/rust-lang/crates.io-index" 439 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 440 | 441 | [[package]] 442 | name = "winapi-util" 443 | version = "0.1.5" 444 | source = "registry+https://github.com/rust-lang/crates.io-index" 445 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 446 | dependencies = [ 447 | "winapi", 448 | ] 449 | 450 | [[package]] 451 | name = "winapi-x86_64-pc-windows-gnu" 452 | version = "0.4.0" 453 | source = "registry+https://github.com/rust-lang/crates.io-index" 454 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 455 | 456 | [[package]] 457 | name = "windows-sys" 458 | version = "0.32.0" 459 | source = "registry+https://github.com/rust-lang/crates.io-index" 460 | checksum = "3df6e476185f92a12c072be4a189a0210dcdcf512a1891d6dff9edb874deadc6" 461 | dependencies = [ 462 | "windows_aarch64_msvc", 463 | "windows_i686_gnu", 464 | "windows_i686_msvc", 465 | "windows_x86_64_gnu", 466 | "windows_x86_64_msvc", 467 | ] 468 | 469 | [[package]] 470 | name = "windows_aarch64_msvc" 471 | version = "0.32.0" 472 | source = "registry+https://github.com/rust-lang/crates.io-index" 473 | checksum = "d8e92753b1c443191654ec532f14c199742964a061be25d77d7a96f09db20bf5" 474 | 475 | [[package]] 476 | name = "windows_i686_gnu" 477 | version = "0.32.0" 478 | source = "registry+https://github.com/rust-lang/crates.io-index" 479 | checksum = "6a711c68811799e017b6038e0922cb27a5e2f43a2ddb609fe0b6f3eeda9de615" 480 | 481 | [[package]] 482 | name = "windows_i686_msvc" 483 | version = "0.32.0" 484 | source = "registry+https://github.com/rust-lang/crates.io-index" 485 | checksum = "146c11bb1a02615db74680b32a68e2d61f553cc24c4eb5b4ca10311740e44172" 486 | 487 | [[package]] 488 | name = "windows_x86_64_gnu" 489 | version = "0.32.0" 490 | source = "registry+https://github.com/rust-lang/crates.io-index" 491 | checksum = "c912b12f7454c6620635bbff3450962753834be2a594819bd5e945af18ec64bc" 492 | 493 | [[package]] 494 | name = "windows_x86_64_msvc" 495 | version = "0.32.0" 496 | source = "registry+https://github.com/rust-lang/crates.io-index" 497 | checksum = "504a2476202769977a040c6364301a3f65d0cc9e3fb08600b2bda150a0488316" 498 | 499 | [[package]] 500 | name = "xargo" 501 | version = "0.3.26" 502 | dependencies = [ 503 | "dirs", 504 | "error-chain", 505 | "fs2", 506 | "lazy_static", 507 | "libc", 508 | "parking_lot", 509 | "rustc_version", 510 | "serde_json", 511 | "tempdir", 512 | "toml", 513 | "walkdir", 514 | ] 515 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Jorge Aparicio "] 3 | build = "build.rs" 4 | categories = ["command-line-utilities", "development-tools", "embedded"] 5 | description = "The sysroot manager that lets you build and customize `std`" 6 | documentation = "https://github.com/japaric/xargo#xargo" 7 | keywords = ["cli", "cross", "compilation", "std"] 8 | license = "MIT OR Apache-2.0" 9 | name = "xargo" 10 | repository = "https://github.com/japaric/xargo" 11 | version = "0.3.26" 12 | default-run = "xargo" 13 | 14 | [dependencies] 15 | error-chain = { version = "0.12", default-features = false } 16 | fs2 = "0.4.1" 17 | libc = "0.2.18" 18 | rustc_version = "0.4" 19 | serde_json = "1.0" 20 | tempdir = "0.3.5" 21 | toml = "0.5.6" 22 | walkdir = "2.3" 23 | dirs = "4.0" 24 | 25 | [dev-dependencies] 26 | lazy_static = "1.0.0" 27 | parking_lot = "0.12" 28 | 29 | [features] 30 | backtrace = ["error-chain/backtrace"] 31 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Jorge Aparicio 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [PSA: Xargo is in maintenance mode](https://github.com/japaric/xargo/issues/193) 2 | 3 | [![crates.io](https://img.shields.io/crates/v/xargo.svg)](https://crates.io/crates/xargo) 4 | [![crates.io](https://img.shields.io/crates/d/xargo.svg)](https://crates.io/crates/xargo) 5 | 6 | # `xargo` 7 | 8 | > The sysroot manager that lets you build and customize `std` 9 | 10 |

11 | Cross compiling `std` for i686-unknown-linux-gnu 16 |
17 | Cross compiling `std` for i686-unknown-linux-gnu 18 |

19 | 20 | Xargo builds and manages "sysroots" (cf. `rustc --print sysroot`). Making it 21 | easy to cross compile Rust crates for targets that *don't* have binary 22 | releases of the standard crates, like the `thumbv*m-none-eabi*` targets. And 23 | it also lets you build a customized `std` crate, e.g. compiled with `-C 24 | panic=abort`, for your target. 25 | 26 | ## Dependencies 27 | 28 | - The `rust-src` component, which you can install with `rustup component add 29 | rust-src`. 30 | 31 | - Rust and Cargo. 32 | 33 | ## Installation 34 | 35 | ``` 36 | $ cargo install xargo 37 | ``` 38 | 39 | ## Usage 40 | 41 | ### `no_std` 42 | 43 | `xargo` has the exact same CLI as `cargo`. 44 | 45 | ``` 46 | # This Just Works 47 | $ xargo build --target thumbv6m-none-eabi 48 | Compiling core v0.0.0 (file://$SYSROOT/lib/rustlib/src/rust/src/libcore) 49 | Finished release [optimized] target(s) in 11.61 secs 50 | Compiling lib v0.1.0 (file://$PWD) 51 | Finished debug [unoptimized + debuginfo] target(s) in 0.5 secs 52 | ``` 53 | 54 | `xargo` will cache the sysroot, in this case the `core` crate, so the next 55 | `build` command will be (very) fast. 56 | 57 | ``` 58 | $ xargo build --target thumbv6m-none-eabi 59 | Finished debug [unoptimized + debuginfo] target(s) in 0.0 secs 60 | ``` 61 | 62 | By default, `xargo` will only compile the `core` crate for the target. If you 63 | need a bigger subset of the standard crates, specify the dependencies in a 64 | `Xargo.toml` at the root of your Cargo project (right next to `Cargo.toml`). 65 | 66 | ``` 67 | $ cat Xargo.toml 68 | # Alternatively you can use [build.dependencies] 69 | # the syntax is the same as Cargo.toml's; you don't need to specify path or git 70 | [target.thumbv6m-none-eabi.dependencies] 71 | collections = {} 72 | 73 | $ xargo build --target thumbv6m-none-eabi 74 | Compiling core v0.0.0 (file://$SYSROOT/lib/rustlib/src/rust/src/libcore) 75 | Compiling alloc v0.0.0 (file://$SYSROOT/lib/rustlib/src/rust/src/liballoc) 76 | Compiling std_unicode v0.0.0 (file://$SYSROOT/lib/rustlib/src/rust/src/libstd_unicode) 77 | Compiling collections v0.0.0 (file://$SYSROOT/lib/rustlib/src/rust/src/libcollections) 78 | Finished release [optimized] target(s) in 15.26 secs 79 | Compiling lib v0.1.0 (file://$PWD) 80 | Finished debug [unoptimized + debuginfo] target(s) in 0.5 secs 81 | ``` 82 | 83 | ### `std` 84 | 85 | You can compile a customized `std` crate as well, just specify which Cargo 86 | features to enable. 87 | 88 | ``` 89 | # Build `std` with `-C panic=abort` (default) and with jemalloc as the default 90 | # allocator 91 | $ cat Xargo.toml 92 | [target.i686-unknown-linux-gnu.dependencies.std] 93 | features = ["jemalloc"] 94 | 95 | # Needed to compile `std` with `-C panic=abort` 96 | $ tail -n2 Cargo.toml 97 | [profile.release] 98 | panic = "abort" 99 | 100 | $ xargo run --target i686-unknown-linux-gnu --release 101 | Updating registry `https://github.com/rust-lang/crates.io-index` 102 | Compiling libc v0.0.0 (file://$SYSROOT/lib/rustlib/src/rust/src/rustc/libc_shim) 103 | Compiling core v0.0.0 (file://$SYSROOT/lib/rustlib/src/rust/src/libcore) 104 | Compiling build_helper v0.1.0 (file://$SYSROOT/lib/rustlib/src/rust/src/build_helper) 105 | Compiling gcc v0.3.41 106 | Compiling unwind v0.0.0 (file://$SYSROOT/lib/rustlib/src/rust/src/libunwind) 107 | Compiling std v0.0.0 (file://$SYSROOT/lib/rustlib/src/rust/src/libstd) 108 | Compiling compiler_builtins v0.0.0 (file://$SYSROOT/lib/rustlib/src/rust/src/libcompiler_builtins) 109 | Compiling alloc_jemalloc v0.0.0 (file://$SYSROOT/lib/rustlib/src/rust/src/liballoc_jemalloc) 110 | Compiling alloc v0.0.0 (file://$SYSROOT/lib/rustlib/src/rust/src/liballoc) 111 | Compiling rand v0.0.0 (file://$SYSROOT/lib/rustlib/src/rust/src/librand) 112 | Compiling std_unicode v0.0.0 (file://$SYSROOT/lib/rustlib/src/rust/src/libstd_unicode) 113 | Compiling alloc_system v0.0.0 (file://$SYSROOT/lib/rustlib/src/rust/src/liballoc_system) 114 | Compiling panic_abort v0.0.0 (file://$SYSROOT/lib/rustlib/src/rust/src/libpanic_abort) 115 | Compiling collections v0.0.0 (file://$SYSROOT/lib/rustlib/src/rust/src/libcollections) 116 | Finished release [optimized] target(s) in 33.49 secs 117 | Compiling hello v0.1.0 (file://$PWD) 118 | Finished release [optimized] target(s) in 0.28 secs 119 | Running `target/i686-unknown-linux-gnu/release/hello` 120 | Hello, world! 121 | ``` 122 | 123 | If you'd like to know what `xargo` is doing under the hood, pass the verbose, 124 | `-v`, flag to it. 125 | 126 | ``` 127 | $ xargo build --target thumbv6m-none-eabi -v 128 | + "rustc" "--print" "target-list" 129 | + "rustc" "--print" "sysroot" 130 | + "cargo" "build" "--release" "--manifest-path" "/tmp/xargo.lTBXKnaUGicV/Cargo.toml" "--target" "thumbv6m-none-eabi" "-v" "-p" "core" 131 | Compiling core v0.0.0 (file://$SYSROOT/lib/rustlib/src/rust/src/libcore) 132 | Running `rustc --crate-name core $SYSROOT/lib/rustlib/src/rust/src/libcore/lib.rs --crate-type lib -C opt-level=3 -C metadata=a5c596f87f7d486b -C extra-filename=-a5c596f87f7d486b --out-dir /tmp/xargo.lTBXKnaUGicV/target/thumbv6m-none-eabi/release/deps --emit=dep-info,link --target thumbv6m-none-eabi -L dependency=/tmp/xargo.lTBXKnaUGicV/target/thumbv6m-none-eabi/release/deps -L dependency=/tmp/xargo.lTBXKnaUGicV/target/release/deps` 133 | Finished release [optimized] target(s) in 11.50 secs 134 | + "cargo" "build" "--target" "thumbv6m-none-eabi" "-v" 135 | Compiling lib v0.1.0 (file://$PWD) 136 | Running `rustc --crate-name lib src/lib.rs --crate-type lib -g -C metadata=461fd0b398821543 -C extra-filename=-461fd0b398821543 --out-dir $PWD/target/thumbv6m-none-eabi/debug/deps --emit=dep-info,link --target thumbv6m-none-eabi -L dependency=$PWD/target/thumbv6m-none-eabi/debug/deps -L dependency=$PWD/lib/target/debug/deps --sysroot $HOME/.xargo` 137 | Finished debug [unoptimized + debuginfo] target(s) in 0.5 secs 138 | ``` 139 | 140 | ### Dev channel 141 | 142 | Oh, and if you want to use `xargo` to compile `std` using a "dev" `rustc`, a 143 | rust compiled from source, you can use the `XARGO_RUST_SRC` environment variable 144 | to tell `xargo` where the Rust source is. 145 | 146 | ``` 147 | # `$XARGO_RUST_SRC` must point to the `library` subfolder of a Rust checkout. 148 | $ export XARGO_RUST_SRC=/path/to/rust/library 149 | 150 | $ xargo build --target msp430-none-elf 151 | ``` 152 | 153 | **NOTE** This also works with the nightly channel but it's not recommended as 154 | the Rust source may diverge from what your compiler is able to compile as it may 155 | make use of newer features that your compiler doesn't understand. 156 | 157 | ### Compiling the sysroot with custom rustc flags 158 | 159 | Xargo uses the same custom rustc flags that apply to the target Cargo project. 160 | So you can use either the `RUSTFLAGS` env variable or a `.cargo/config` 161 | configuration file to specify custom rustc flags. 162 | 163 | ``` 164 | # build the sysroot with debug information 165 | $ RUSTFLAGS='-g' xargo build --target x86_64-unknown-linux-gnu 166 | 167 | # Alternatively 168 | $ edit .cargo/config && cat $_ 169 | [build] 170 | rustflags = ["-g"] 171 | 172 | # Then you can omit RUSTFLAGS 173 | $ xargo build --target x86_64-unknown-linux-gnu 174 | ``` 175 | 176 | ### Compiling the sysroot for a custom target 177 | 178 | At some point you may want to develop a program for a target that's not 179 | officially supported by rustc. Xargo's got your back! It supports custom targets 180 | via target specifications files, which are not really documented anywhere other 181 | than in the [compiler source code][spec-docs]. Luckily you don't need to write 182 | a specification file from scratch; you can start from an existing one. 183 | 184 | [spec-docs]: https://github.com/rust-lang/rust/blob/256e497fe63bf4b13f7c0b58fa17360ca849c54d/src/librustc_back/target/mod.rs#L228-L409 185 | 186 | For example, let's say that you want to cross compile a program for a PowerPC 187 | Linux systems that uses uclibc instead of glibc. There's a similarly looking 188 | target in the list of targets supported by the compiler -- see `rustc --print 189 | target-list` -- and that is `powerpc-unknown-linux-gnu`. So you can start by 190 | dumping the specification of that target into a file: 191 | 192 | ``` console 193 | $ rustc -Z unstable-options --print target-spec-json --target powerpc-unknown-linux-gnu | tee powerpc-unknown-linux-uclibc.json 194 | ``` 195 | 196 | ``` js 197 | { 198 | "arch": "powerpc", 199 | "data-layout": "E-m:e-p:32:32-i64:64-n32", 200 | "dynamic-linking": true, 201 | "env": "gnu", 202 | "executables": true, 203 | "has-elf-tls": true, 204 | "has-rpath": true, 205 | "is-builtin": true, 206 | "linker-flavor": "gcc", 207 | "linker-is-gnu": true, 208 | "llvm-target": "powerpc-unknown-linux-gnu", 209 | "max-atomic-width": 32, 210 | "os": "linux", 211 | "position-independent-executables": true, 212 | "pre-link-args": { 213 | "gcc": [ 214 | "-Wl,--as-needed", 215 | "-Wl,-z,noexecstack", 216 | "-m32" 217 | ] 218 | }, 219 | "target-endian": "big", 220 | "target-family": "unix", 221 | "target-pointer-width": "32", 222 | "vendor": "unknown" 223 | } 224 | ``` 225 | 226 | One of the things you'll definitively want to do is drop the `is-builtin` field 227 | as that's reserved for targets that are defined in the compiler itself. Apart 228 | from that the only modification you would have to in this case is change the 229 | `env` field from `gnu` (glibc) to `uclibc`. 230 | 231 | ``` diff 232 | "arch": "powerpc", 233 | "data-layout": "E-m:e-p:32:32-i64:64-n32", 234 | "dynamic-linking": true, 235 | - "env": "gnu", 236 | + "env": "uclibc", 237 | "executables": true, 238 | "has-elf-tls": true, 239 | "has-rpath": true, 240 | - "is-builtin": true, 241 | "linker-flavor": "gcc", 242 | "linker-is-gnu": true, 243 | "llvm-target": "powerpc-unknown-linux-gnu", 244 | ``` 245 | 246 | Once you have your target specification file you only have to call Xargo with 247 | the right target triple; make sure that the specification file is the same 248 | folder from where you invoke Xargo because that's where rustc expects it to be. 249 | 250 | ``` console 251 | $ ls powerpc-unknown-linux-uclibc.json 252 | powerpc-unknown-linux-uclibc.json 253 | 254 | $ xargo build --target powerpc-unknown-linux-uclibc 255 | ``` 256 | 257 | Your build may fail because if rustc doesn't support your target then it's 258 | likely that the standard library doesn't support it either. In that case you 259 | will have to modify the source of the standard library. Xargo helps with that 260 | too because you can make a copy of the original source -- see `rustc --print 261 | sysroot`, modify it and then point Xargo to it using the `XARGO_RUST_SRC` env 262 | variable. 263 | 264 | ### Multi-stage builds 265 | 266 | Some standard crates have implicit dependencies between them. For example, the 267 | `test` crate implicitly depends on the `std`. Implicit here means that the test 268 | crate Cargo.toml [doesn't list std as its dependency][test]. To compile a 269 | sysroot that contains such crates you can perform the build in stages by 270 | specifying which crates belong to each stage in the Xargo.toml file: 271 | 272 | [test]: https://github.com/rust-lang/rust/blob/1.17.0/src/libtest/Cargo.toml 273 | 274 | ``` toml 275 | [dependencies.std] 276 | stage = 0 277 | 278 | [dependencies.test] 279 | stage = 1 280 | ``` 281 | 282 | This will compile an intermediate sysroot, the stage 0 sysroot, containing the 283 | `std` crate, and then it will compile the `test` crate against that intermediate 284 | sysroot. The final sysroot, the stage 1 sysroot, will contain both the `std` and 285 | `test` crates, and their dependencies. 286 | 287 | ### Creating a sysroot with custom crates 288 | 289 | Xargo lets you create a sysroot with custom crates. You can virtually put any 290 | crate in the sysroot. However, this feature is mainly used to create [alternative 291 | `std` facades][rust-3ds], and to replace the `test` crate with [one that supports 292 | `no_std` targets][utest]. To specify the contents of the sysroot simply list the 293 | dependencies in the Xargo.toml file as you would do with Cargo.toml: 294 | 295 | [steed]: https://github.com/rust3ds/rust3ds-template 296 | [utest]: https://github.com/japaric/utest 297 | 298 | ``` toml 299 | # First build some standard crates. 300 | [dependencies.alloc] 301 | [dependencies.panic_abort] 302 | [dependencies.panic_unwind] 303 | 304 | # Then build our custom facade. It (implicitly) requires the crates above to 305 | # already be in the sysroot, so we need to set the `stage`. 306 | [dependencies.std] 307 | git = "https://github.com/rust3ds/ctru-rs" 308 | stage = 1 309 | ``` 310 | 311 | ### Patching sysroot crates 312 | 313 | Xargo also supports the `patch` feature from Cargo. This allows you to force the use 314 | of a custom crate throughout your sysroot's dependency tree. This can be especially 315 | useful to force the use of a custom `libc` or `compiler_builtins` without having to 316 | do intrusive changes to every transitive dependency. 317 | 318 | 319 | ``` toml 320 | [patch.crates-io.libc] 321 | path = "path/to/custom/libc" 322 | ``` 323 | 324 | Notice that you should not list patched crates as `[dependencies]`! 325 | `[dependencies]` determines which crates are built in the first place; `[patch]` 326 | lets you replace some of their (transitive) dependencies with your own choice. 327 | Having a crate listed in both will likely lead to crate duplication. 328 | 329 | ### Check-only sysroot build 330 | 331 | Xargo supports performing a 'check build' of the syroot 332 | via the `xargo-check` command. This command is invoked exactly 333 | like `xargo`, but will invoke `cargo check` instead of `cargo build` 334 | when building the sysroot. 335 | 336 | This is only useful for very specialized applicationsm like Miri. 337 | The resulting libstd will *not* be useable in a normal build, since codegen 338 | will not be performed. You should almost always run `xargo check` (note the space), 339 | which will perform a normal sysroot build, followed by a 'check' build of *your application* 340 | 341 | ## Caveats / gotchas 342 | 343 | - Xargo won't build a sysroot when used with stable or beta Rust. This is 344 | because `std` and other standard crates depend on unstable features so it's 345 | not possible to build the sysroot with stable or beta. 346 | 347 | - `std` is built as rlib *and* dylib. The dylib needs a panic library and an 348 | allocator. If you do not specify the `panic-unwind` feature, you have to set 349 | `panic = "abort"` in `Cargo.toml`. 350 | 351 | - To build without the `jemalloc` feature include the following in `Xargo.toml`: 352 | 353 | ``` toml 354 | [dependencies.std] 355 | features = ["force_alloc_system"] 356 | ``` 357 | 358 | What this flag means is that every program compiled with this libstd can only use the system 359 | allocator. If your program tries to set its own allocator, compilation will fail because now two 360 | allocators are set (one by libstd, one by your program). For some further information on this 361 | issue, see 362 | [rust-lang/rust#43637](https://github.com/rust-lang/rust/issues/43637#issuecomment-320463578). 363 | 364 | - It's recommended that the `--target` option is always used for `xargo`. This is because it must 365 | be provided even when compiling for the host platform due to the way cargo handles compiler 366 | plugins (e.g. `serde_derive`) and build scripts (`build.rs`). This also applies to how all of the 367 | dependant crates get compiled that use compiler plugins or build scripts. You can determine your 368 | host's target triple with `rustc -vV`. On *nix, the following rune will extract the triple: 369 | `rustc -vV | egrep '^host: ' | sed 's/^host: //'`. 370 | 371 | - Remember that `core` and `std` will get implicitly linked to your crate but *all the other sysroot 372 | crates* will *not*. This means that if your Xargo.toml contains a crate like `alloc` then you will 373 | have to add a `extern crate alloc` *somewhere* in your dependency graph (either in your current 374 | crate or in some of its dependencies). 375 | 376 | - Remember that rustc will always implicitly link `compiler_builtins` into your final binary, but 377 | won't make it available for `use` the same way `core` and `std` are. So if you need to manually 378 | call a `compiler_builtins` function, you will still need to manually add an 379 | `extern crate compiler_builtins` within your crate. 380 | 381 | - Care must be taken not to end up with any "top-level" crates (`core`, `std`, `compiler-builtins`) 382 | twice in the sysroot. Doing so will cause cargo to error on build with a message like 383 | `multiple matching crates for core`. Duplicate crates in the sysroot generally occur when the same 384 | crate is built twice with different features as part of a multi-stage build. 385 | 386 | ## License 387 | 388 | Licensed under either of 389 | 390 | - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or 391 | http://www.apache.org/licenses/LICENSE-2.0) 392 | - MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 393 | 394 | at your option. 395 | 396 | ### Contribution 397 | 398 | Unless you explicitly state otherwise, any contribution intentionally submitted 399 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 400 | dual licensed as above, without any additional terms or conditions. 401 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | global: 3 | CRATE_NAME: xargo 4 | RUST_VERSION: stable 5 | 6 | matrix: 7 | # MinGW 8 | - TARGET: i686-pc-windows-gnu 9 | - TARGET: x86_64-pc-windows-gnu 10 | 11 | # MSVC 12 | - TARGET: i686-pc-windows-msvc 13 | - TARGET: x86_64-pc-windows-msvc 14 | 15 | - TARGET: i686-pc-windows-gnu 16 | RUST_VERSION: nightly 17 | - TARGET: x86_64-pc-windows-gnu 18 | RUST_VERSION: nightly 19 | - TARGET: i686-pc-windows-msvc 20 | RUST_VERSION: nightly 21 | - TARGET: x86_64-pc-windows-msvc 22 | RUST_VERSION: nightly 23 | 24 | install: 25 | - ps: >- 26 | If ($Env:TARGET -eq 'x86_64-pc-windows-gnu') { 27 | $Env:PATH += ';C:\msys64\mingw64\bin' 28 | } ElseIf ($Env:TARGET -eq 'i686-pc-windows-gnu') { 29 | $Env:PATH += ';C:\msys64\mingw32\bin' 30 | } 31 | - curl -sSf -o rustup-init.exe https://win.rustup.rs/ 32 | - rustup-init.exe -y --default-host %TARGET% --default-toolchain %RUST_VERSION% 33 | - set PATH=%PATH%;C:\Users\appveyor\.cargo\bin 34 | - rustup component add rust-src 35 | - rustc -Vv 36 | - cargo -V 37 | 38 | test_script: 39 | - if [%APPVEYOR_REPO_TAG%]==[false] ( 40 | cargo build --target %TARGET% && 41 | cargo run --target %TARGET% -- -V && 42 | if [%RUST_VERSION%]==[nightly] ( 43 | cargo test --target %TARGET% --features dev && 44 | cargo test --target %TARGET% 45 | ) 46 | ) 47 | 48 | before_deploy: 49 | - cargo rustc --target %TARGET% --release --bin xargo -- -C lto 50 | - ps: ci\before_deploy.ps1 51 | 52 | deploy: 53 | artifact: /.*\.zip/ 54 | auth_token: 55 | secure: eKL1bZzuKQzvnpQZhujz1FJaeBp7Y9fHDbDeumve4lyREn68CTsusQDezg0lB4Bv 56 | description: '' 57 | on: 58 | RUST_VERSION: stable 59 | appveyor_repo_tag: true 60 | provider: GitHub 61 | 62 | cache: 63 | - C:\Users\appveyor\.cargo\registry 64 | - target 65 | 66 | branches: 67 | only: 68 | - /^v\d+\.\d+\.\d+.*$/ 69 | - auto 70 | - try 71 | 72 | notifications: 73 | - provider: Email 74 | on_build_success: false 75 | 76 | build: false 77 | -------------------------------------------------------------------------------- /assets/xargo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/japaric/xargo/68e0ca57cd90837fe02f262f074182f9cfeb6227/assets/xargo.png -------------------------------------------------------------------------------- /bors.toml: -------------------------------------------------------------------------------- 1 | delete_merged_branches = true 2 | status = ["ci"] 3 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::error::Error; 3 | use std::fs::File; 4 | use std::io::Write; 5 | use std::path::PathBuf; 6 | use std::process::Command; 7 | 8 | struct Some {} 9 | 10 | impl From for Some 11 | where 12 | E: Error, 13 | { 14 | fn from(_: E) -> Some { 15 | Some {} 16 | } 17 | } 18 | 19 | fn main() { 20 | let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap()); 21 | 22 | File::create(out_dir.join("commit-info.txt")) 23 | .unwrap() 24 | .write_all(commit_info().as_bytes()) 25 | .unwrap(); 26 | } 27 | 28 | fn commit_info() -> String { 29 | match (commit_hash(), commit_date()) { 30 | (Ok(hash), Ok(date)) => format!(" ({} {})", hash.trim(), date.trim()), 31 | _ => String::new(), 32 | } 33 | } 34 | 35 | fn commit_hash() -> Result { 36 | let output = Command::new("git") 37 | .args(&["rev-parse", "--short", "HEAD"]) 38 | .output()?; 39 | 40 | if output.status.success() { 41 | Ok(String::from_utf8(output.stdout)?) 42 | } else { 43 | Err(Some {}) 44 | } 45 | } 46 | 47 | fn commit_date() -> Result { 48 | let output = Command::new("git") 49 | .args(&["log", "-1", "--date=short", "--pretty=format:%cd"]) 50 | .output()?; 51 | 52 | if output.status.success() { 53 | Ok(String::from_utf8(output.stdout)?) 54 | } else { 55 | Err(Some {}) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/bin/xargo-check.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | xargo::main_inner(xargo::XargoMode::Check); 3 | } 4 | -------------------------------------------------------------------------------- /src/bin/xargo.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | xargo::main_inner(xargo::XargoMode::Build); 3 | } 4 | -------------------------------------------------------------------------------- /src/cargo.rs: -------------------------------------------------------------------------------- 1 | use std::hash::{Hash, Hasher}; 2 | use std::path::{Path, PathBuf}; 3 | use std::process::{Command, ExitStatus}; 4 | use std::{env, fmt}; 5 | 6 | use toml::{Value, map::Map}; 7 | 8 | use cli::Args; 9 | use errors::*; 10 | use extensions::CommandExt; 11 | use util; 12 | use sysroot::XargoMode; 13 | use xargo::Home; 14 | 15 | #[derive(Clone)] 16 | pub struct Rustflags { 17 | flags: Vec, 18 | } 19 | 20 | impl Rustflags { 21 | pub fn hash(&self, hasher: &mut H) 22 | where 23 | H: Hasher, 24 | { 25 | let mut flags = self.flags.iter(); 26 | 27 | while let Some(flag) = flags.next() { 28 | if flag == "-C" { 29 | if let Some(next) = flags.next() { 30 | if next.starts_with("link-arg=") || next.starts_with("link-args=") { 31 | // don't hash linker arguments 32 | } else { 33 | flag.hash(hasher); 34 | next.hash(hasher); 35 | } 36 | } else { 37 | flag.hash(hasher); 38 | } 39 | } else { 40 | flag.hash(hasher); 41 | } 42 | } 43 | } 44 | 45 | pub fn push(&mut self, flags: &[&str]) { 46 | self.flags.extend(flags.iter().map(|w| w.to_string())); 47 | } 48 | 49 | /// Stringifies the default flags for Xargo consumption 50 | pub fn encode(mut self, home: &Home) -> String { 51 | self.flags.push("--sysroot".to_owned()); 52 | self.flags.push(home.display().to_string()); // FIXME: we shouldn't use display, we should keep the OsString 53 | // As per CARGO_ENCODED_RUSTFLAGS docs, the separator is `0x1f`. 54 | self.flags.join("\x1f") 55 | } 56 | } 57 | 58 | impl fmt::Display for Rustflags { 59 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 60 | fmt::Display::fmt(&self.flags.join(" "), f) 61 | } 62 | } 63 | 64 | pub fn rustflags(config: Option<&Config>, target: &str) -> Result { 65 | flags(config, target, "rustflags").map(|fs| Rustflags { flags: fs }) 66 | } 67 | 68 | #[derive(Clone)] 69 | pub struct Rustdocflags { 70 | flags: Vec, 71 | } 72 | 73 | impl Rustdocflags { 74 | /// Stringifies these flags for Xargo consumption 75 | pub fn encode(mut self, home: &Home) -> String { 76 | self.flags.push("--sysroot".to_owned()); 77 | self.flags.push(home.display().to_string()); // FIXME: we shouldn't use display, we should keep the OsString 78 | // As per CARGO_ENCODED_RUSTFLAGS docs, the separator is `0x1f`. 79 | self.flags.join("\x1f") 80 | } 81 | } 82 | 83 | pub fn rustdocflags(config: Option<&Config>, target: &str) -> Result { 84 | flags(config, target, "rustdocflags").map(|fs| Rustdocflags { flags: fs }) 85 | } 86 | 87 | 88 | /// Returns the flags for `tool` (e.g. rustflags) 89 | /// 90 | /// This looks into the environment and into `.cargo/config` 91 | fn flags(config: Option<&Config>, target: &str, tool: &str) -> Result> { 92 | // TODO: would be nice to also support the CARGO_ENCODED_ env vars 93 | if let Some(t) = env::var_os(tool.to_uppercase()) { 94 | return Ok( 95 | t.to_string_lossy() 96 | .split_whitespace() 97 | .map(|w| w.to_owned()) 98 | .collect(), 99 | ); 100 | } 101 | 102 | if let Some(config) = config.as_ref() { 103 | let mut build = false; 104 | if let Some(array) = config 105 | .table 106 | .get("target") 107 | .and_then(|t| t.get(target)) 108 | .and_then(|t| t.get(tool)) 109 | .or_else(|| { 110 | build = true; 111 | config.table.get("build").and_then(|t| t.get(tool)) 112 | }) { 113 | let mut flags = vec![]; 114 | 115 | let mut error = false; 116 | if let Some(array) = array.as_array() { 117 | for value in array { 118 | if let Some(flag) = value.as_str() { 119 | flags.push(flag.to_owned()); 120 | } else { 121 | error = true; 122 | break; 123 | } 124 | } 125 | } else { 126 | error = true; 127 | } 128 | 129 | if error { 130 | if build { 131 | Err(format!( 132 | ".cargo/config: build.{} must be an array \ 133 | of strings", 134 | tool 135 | ))? 136 | } else { 137 | Err(format!( 138 | ".cargo/config: target.{}.{} must be an \ 139 | array of strings", 140 | target, 141 | tool 142 | ))? 143 | } 144 | } else { 145 | Ok(flags) 146 | } 147 | } else { 148 | Ok(vec![]) 149 | } 150 | } else { 151 | Ok(vec![]) 152 | } 153 | } 154 | 155 | pub fn command() -> Command { 156 | env::var_os("CARGO") 157 | .map(Command::new) 158 | .unwrap_or_else(|| Command::new("cargo")) 159 | } 160 | 161 | pub fn run(args: &Args, verbose: bool) -> Result { 162 | command() 163 | .args(args.all()) 164 | .run_and_get_status(verbose) 165 | } 166 | 167 | pub struct Config { 168 | table: Value, 169 | } 170 | 171 | impl Config { 172 | pub fn target(&self) -> Result> { 173 | if let Some(v) = self.table.get("build").and_then(|t| t.get("target")) { 174 | Ok(Some(v.as_str() 175 | .ok_or_else(|| format!(".cargo/config: build.target must be a string"))?)) 176 | } else { 177 | Ok(None) 178 | } 179 | } 180 | } 181 | 182 | pub fn config() -> Result> { 183 | let cd = env::current_dir().chain_err(|| "couldn't get the current directory")?; 184 | 185 | if let Some(p) = util::search(&cd, ".cargo/config") { 186 | Ok(Some(Config { 187 | table: util::parse(&p.join(".cargo/config"))?, 188 | })) 189 | } else { 190 | Ok(None) 191 | } 192 | } 193 | 194 | pub struct Profile<'t> { 195 | table: &'t Value, 196 | } 197 | 198 | impl<'t> Profile<'t> { 199 | pub fn hash(&self, hasher: &mut H) 200 | where 201 | H: Hasher, 202 | { 203 | let mut v = self.table.clone(); 204 | 205 | // Don't include `lto` in the hash because it doesn't affect compilation 206 | // of `.rlib`s 207 | if let Value::Table(ref mut table) = v { 208 | table.remove("lto"); 209 | 210 | // don't hash an empty map 211 | if table.is_empty() { 212 | return; 213 | } 214 | } 215 | 216 | v.to_string().hash(hasher); 217 | } 218 | } 219 | 220 | impl<'t> fmt::Display for Profile<'t> { 221 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 222 | let mut map = Map::new(); 223 | map.insert("profile".to_owned(), { 224 | let mut map = Map::new(); 225 | map.insert("release".to_owned(), self.table.clone()); 226 | Value::Table(map) 227 | }); 228 | 229 | fmt::Display::fmt(&Value::Table(map), f) 230 | } 231 | } 232 | 233 | pub struct Toml { 234 | table: Value, 235 | } 236 | 237 | impl Toml { 238 | /// `profile.release` part of `Cargo.toml` 239 | pub fn profile(&self) -> Option { 240 | self.table 241 | .get("profile") 242 | .and_then(|t| t.get("release")) 243 | .map(|t| Profile { table: t }) 244 | } 245 | } 246 | 247 | pub fn toml(root: &Root) -> Result { 248 | util::parse(&root.path().join("Cargo.toml")).map(|t| Toml { table: t }) 249 | } 250 | 251 | pub struct Root { 252 | path: PathBuf, 253 | } 254 | 255 | impl Root { 256 | pub fn path(&self) -> &Path { 257 | &self.path 258 | } 259 | } 260 | 261 | pub fn root(mode: XargoMode, manifest_path: Option<&str>) -> Result> { 262 | // Don't require a 'Cargo.toml' to exist when 'xargo-check' is used 263 | let name = match mode { 264 | XargoMode::Build => "Cargo.toml", 265 | XargoMode::Check => "Xargo.toml" 266 | }; 267 | 268 | let cd = match manifest_path { 269 | None => env::current_dir().chain_err(|| "couldn't get the current directory")?, 270 | Some(p) => { 271 | let mut pb = PathBuf::from(p); 272 | pb.pop(); // strip filename, keep directory containing Cargo.toml 273 | pb 274 | } 275 | }; 276 | Ok(util::search(&cd, name).map(|p| Root { path: p.to_owned() })) 277 | } 278 | 279 | #[derive(Clone, Copy, PartialEq)] 280 | pub enum Subcommand { 281 | Clean, 282 | Doc, 283 | Init, 284 | New, 285 | Other, 286 | Search, 287 | Update, 288 | } 289 | 290 | impl Subcommand { 291 | pub fn needs_sysroot(&self) -> bool { 292 | use self::Subcommand::*; 293 | 294 | match *self { 295 | Clean | Init | New | Search | Update => false, 296 | _ => true, 297 | } 298 | } 299 | } 300 | 301 | impl<'a> From<&'a str> for Subcommand { 302 | fn from(s: &str) -> Subcommand { 303 | match s { 304 | "clean" => Subcommand::Clean, 305 | "doc" => Subcommand::Doc, 306 | "init" => Subcommand::Init, 307 | "new" => Subcommand::New, 308 | "search" => Subcommand::Search, 309 | "update" => Subcommand::Update, 310 | _ => Subcommand::Other, 311 | } 312 | } 313 | } 314 | -------------------------------------------------------------------------------- /src/cli.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | 3 | use cargo::Subcommand; 4 | 5 | pub struct Args { 6 | all: Vec, 7 | subcommand: Option, 8 | target: Option, 9 | message_format: Option, 10 | manifest_path: Option, // path to the Cargo toml file given in --manifest-path 11 | } 12 | 13 | impl Args { 14 | pub fn all(&self) -> &[String] { 15 | &self.all 16 | } 17 | 18 | pub fn subcommand(&self) -> Option { 19 | self.subcommand 20 | } 21 | 22 | pub fn target(&self) -> Option<&str> { 23 | self.target.as_ref().map(|s| &**s) 24 | } 25 | 26 | pub fn message_format(&self) -> Option<&str> { 27 | self.message_format.as_ref().map(|s| &**s) 28 | } 29 | 30 | pub fn verbose(&self) -> bool { 31 | self.all 32 | .iter() 33 | .any(|a| a == "--verbose" || a == "-v" || a == "-vv") 34 | } 35 | 36 | pub fn version(&self) -> bool { 37 | self.all.iter().any(|a| a == "--version" || a == "-V") 38 | } 39 | 40 | pub fn manifest_path(&self) -> Option<&str> { 41 | self.manifest_path.as_ref().map(|s| &**s) 42 | } 43 | } 44 | 45 | pub fn args() -> Args { 46 | let all = env::args().skip(1).collect::>(); 47 | 48 | let mut subcommand = None; 49 | let mut target = None; 50 | let mut message_format = None; 51 | let mut manifest_path = None; 52 | { 53 | let mut args = all.iter(); 54 | while let Some(arg) = args.next() { 55 | if !arg.starts_with("-") { 56 | subcommand = subcommand.or_else(|| Some(Subcommand::from(&**arg))); 57 | } 58 | 59 | if arg == "--target" { 60 | target = args.next().map(|s| s.to_owned()); 61 | } else if arg.starts_with("--target=") { 62 | target = arg.splitn(2, '=').nth(1).map(|s| s.to_owned()); 63 | } else if arg == "--message-format" { 64 | message_format = args.next().map(|s| s.to_owned()); 65 | } else if arg.starts_with("--message-format=") { 66 | message_format = arg.splitn(2, '=').nth(1).map(|s| s.to_owned()); 67 | } else if arg.starts_with("--manifest-path") { 68 | manifest_path = args.next().map(|s| s.to_owned()); 69 | } 70 | } 71 | } 72 | 73 | Args { 74 | all, 75 | subcommand, 76 | target, 77 | message_format, 78 | manifest_path, 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/errors.rs: -------------------------------------------------------------------------------- 1 | #![allow(unknown_lints)] 2 | #![allow(unused_doc_comments)] 3 | error_chain!(); 4 | -------------------------------------------------------------------------------- /src/extensions.rs: -------------------------------------------------------------------------------- 1 | use std::io::Write; 2 | use std::io; 3 | use std::process::{Command, ExitStatus}; 4 | 5 | use errors::*; 6 | 7 | pub trait CommandExt { 8 | fn run(&mut self, verbose: bool) -> Result<()>; 9 | fn run_and_get_status(&mut self, verbose: bool) -> Result; 10 | fn run_and_get_stdout(&mut self, verbose: bool) -> Result; 11 | } 12 | 13 | impl CommandExt for Command { 14 | /// Runs the command to completion 15 | fn run(&mut self, verbose: bool) -> Result<()> { 16 | let status = self.run_and_get_status(verbose)?; 17 | 18 | if status.success() { 19 | Ok(()) 20 | } else { 21 | Err(format!( 22 | "`{:?}` failed with exit code: {:?}", 23 | self, 24 | status.code() 25 | ))? 26 | } 27 | } 28 | 29 | /// Runs the command to completion 30 | fn run_and_get_status(&mut self, verbose: bool) -> Result { 31 | if verbose { 32 | writeln!(io::stderr(), "+ {:?}", self).ok(); 33 | } 34 | 35 | self.status() 36 | .chain_err(|| format!("couldn't execute `{:?}`", self)) 37 | } 38 | 39 | /// Runs the command to completion and returns its stdout 40 | fn run_and_get_stdout(&mut self, verbose: bool) -> Result { 41 | if verbose { 42 | writeln!(io::stderr(), "+ {:?}", self).ok(); 43 | } 44 | 45 | let out = self.output() 46 | .chain_err(|| format!("couldn't execute `{:?}`", self))?; 47 | 48 | if out.status.success() { 49 | Ok(String::from_utf8(out.stdout) 50 | .chain_err(|| format!("`{:?}` output was not UTF-8", self))?) 51 | } else { 52 | Err(format!( 53 | "`{:?}` failed with exit code: {:?}", 54 | self, 55 | out.status.code() 56 | ))? 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/flock.rs: -------------------------------------------------------------------------------- 1 | //! Copy paste of Cargo's src/util/flock.rs with modifications to not depend on 2 | //! other Cargo stuff 3 | 4 | use std::fs::{File, OpenOptions}; 5 | use std::io::Write; 6 | use std::path::{Display, Path, PathBuf}; 7 | use std::{fs, io}; 8 | 9 | use fs2::FileExt; 10 | use fs2; 11 | 12 | #[derive(PartialEq)] 13 | enum State { 14 | Exclusive, 15 | Shared, 16 | } 17 | 18 | pub struct FileLock { 19 | file: File, 20 | path: PathBuf, 21 | } 22 | 23 | impl FileLock { 24 | pub fn parent(&self) -> &Path { 25 | self.path.parent().unwrap() 26 | } 27 | 28 | pub fn path(&self) -> &Path { 29 | &self.path 30 | } 31 | 32 | pub fn remove_siblings(&self) -> io::Result<()> { 33 | let path = self.path(); 34 | for entry in path.parent().unwrap().read_dir()? { 35 | let entry = entry?; 36 | if Some(&entry.file_name()[..]) == path.file_name() { 37 | continue; 38 | } 39 | let kind = entry.file_type()?; 40 | if kind.is_dir() { 41 | fs::remove_dir_all(entry.path())?; 42 | } else { 43 | fs::remove_file(entry.path())?; 44 | } 45 | } 46 | Ok(()) 47 | } 48 | } 49 | 50 | pub struct Filesystem { 51 | path: PathBuf, 52 | } 53 | 54 | impl Filesystem { 55 | pub fn new(path: PathBuf) -> Filesystem { 56 | Filesystem { path: path } 57 | } 58 | 59 | pub fn join(&self, other: T) -> Filesystem 60 | where 61 | T: AsRef, 62 | { 63 | Filesystem::new(self.path.join(other)) 64 | } 65 | 66 | pub fn open_ro

(&self, path: P, msg: &str) -> io::Result 67 | where 68 | P: AsRef, 69 | { 70 | self.open( 71 | path.as_ref(), 72 | OpenOptions::new().read(true), 73 | State::Shared, 74 | msg, 75 | ) 76 | } 77 | 78 | pub fn open_rw

(&self, path: P, msg: &str) -> io::Result 79 | where 80 | P: AsRef, 81 | { 82 | self.open( 83 | path.as_ref(), 84 | OpenOptions::new().read(true).write(true).create(true), 85 | State::Exclusive, 86 | msg, 87 | ) 88 | } 89 | 90 | fn open( 91 | &self, 92 | path: &Path, 93 | opts: &OpenOptions, 94 | state: State, 95 | msg: &str, 96 | ) -> io::Result { 97 | let path = self.path.join(path); 98 | 99 | let f = opts.open(&path).or_else(|e| { 100 | if e.kind() == io::ErrorKind::NotFound && state == State::Exclusive { 101 | create_dir_all(path.parent().unwrap())?; 102 | opts.open(&path) 103 | } else { 104 | Err(e) 105 | } 106 | })?; 107 | 108 | match state { 109 | State::Exclusive => { 110 | acquire( 111 | msg, 112 | &path, 113 | &|| f.try_lock_exclusive(), 114 | &|| f.lock_exclusive(), 115 | )?; 116 | } 117 | State::Shared => { 118 | acquire(msg, &path, &|| f.try_lock_shared(), &|| f.lock_shared())?; 119 | } 120 | } 121 | 122 | Ok(FileLock { 123 | file: f, 124 | path: path, 125 | }) 126 | } 127 | 128 | pub fn display(&self) -> Display { 129 | self.path.display() 130 | } 131 | } 132 | 133 | impl Drop for FileLock { 134 | fn drop(&mut self) { 135 | self.file.unlock().ok(); 136 | } 137 | } 138 | 139 | fn acquire( 140 | msg: &str, 141 | path: &Path, 142 | try: &dyn Fn() -> io::Result<()>, 143 | block: &dyn Fn() -> io::Result<()>, 144 | ) -> io::Result<()> { 145 | #[cfg(all(target_os = "linux", not(target_env = "musl")))] 146 | fn is_on_nfs_mount(path: &Path) -> bool { 147 | use std::ffi::CString; 148 | use std::mem; 149 | use std::os::unix::prelude::*; 150 | 151 | let path = match CString::new(path.as_os_str().as_bytes()) { 152 | Ok(path) => path, 153 | Err(_) => return false, 154 | }; 155 | 156 | unsafe { 157 | let mut buf: ::libc::statfs = mem::zeroed(); 158 | let r = ::libc::statfs(path.as_ptr(), &mut buf); 159 | 160 | r == 0 && buf.f_type as u32 == ::libc::NFS_SUPER_MAGIC as u32 161 | } 162 | } 163 | 164 | #[cfg(any(not(target_os = "linux"), target_env = "musl"))] 165 | fn is_on_nfs_mount(_path: &Path) -> bool { 166 | false 167 | } 168 | 169 | if is_on_nfs_mount(path) { 170 | return Ok(()); 171 | } 172 | 173 | match try() { 174 | Ok(_) => return Ok(()), 175 | #[cfg(target_os = "macos")] 176 | Err(ref e) if e.raw_os_error() == Some(::libc::ENOTSUP) => 177 | { 178 | return Ok(()) 179 | } 180 | Err(e) => if e.raw_os_error() != fs2::lock_contended_error().raw_os_error() { 181 | return Err(e); 182 | }, 183 | } 184 | 185 | writeln!( 186 | io::stderr(), 187 | "{:>12} waiting for file lock on {}", 188 | "Blocking", 189 | msg 190 | ).ok(); 191 | 192 | block() 193 | } 194 | 195 | fn create_dir_all(path: &Path) -> io::Result<()> { 196 | match create_dir(path) { 197 | Ok(()) => Ok(()), 198 | Err(e) => { 199 | if e.kind() == io::ErrorKind::NotFound { 200 | if let Some(p) = path.parent() { 201 | return create_dir_all(p).and_then(|()| create_dir(path)); 202 | } 203 | } 204 | Err(e) 205 | } 206 | } 207 | } 208 | 209 | fn create_dir(path: &Path) -> io::Result<()> { 210 | match fs::create_dir(path) { 211 | Ok(()) => Ok(()), 212 | Err(ref e) if e.kind() == io::ErrorKind::AlreadyExists => Ok(()), 213 | Err(e) => Err(e), 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![deny(warnings)] 2 | 3 | #[macro_use] 4 | extern crate error_chain; 5 | extern crate fs2; 6 | #[cfg(any(all(target_os = "linux", not(target_env = "musl")), target_os = "macos"))] 7 | extern crate libc; 8 | extern crate rustc_version; 9 | extern crate serde_json; 10 | extern crate tempdir; 11 | extern crate toml; 12 | extern crate walkdir; 13 | extern crate dirs; 14 | 15 | use std::hash::{Hash, Hasher}; 16 | use std::io::Write; 17 | use std::path::{Path}; 18 | use std::process::ExitStatus; 19 | use std::{env, io, process}; 20 | 21 | use rustc_version::Channel; 22 | 23 | use errors::*; 24 | use rustc::Target; 25 | 26 | mod cargo; 27 | mod cli; 28 | mod errors; 29 | mod extensions; 30 | mod flock; 31 | mod rustc; 32 | mod sysroot; 33 | mod util; 34 | mod xargo; 35 | 36 | pub use sysroot::XargoMode; 37 | 38 | // We use a different sysroot for Native compilation to avoid file locking 39 | // 40 | // Cross compilation requires `lib/rustlib/$HOST` to match `rustc`'s sysroot, 41 | // whereas Native compilation wants to use a custom `lib/rustlib/$HOST`. If each 42 | // mode has its own sysroot then we avoid sharing that directory and thus file 43 | // locking it. 44 | pub enum CompilationMode { 45 | Cross(Target), 46 | Native(String), 47 | } 48 | 49 | impl CompilationMode { 50 | fn hash(&self, hasher: &mut H) -> Result<()> 51 | where 52 | H: Hasher, 53 | { 54 | match *self { 55 | CompilationMode::Cross(ref target) => target.hash(hasher)?, 56 | CompilationMode::Native(ref triple) => triple.hash(hasher), 57 | } 58 | 59 | Ok(()) 60 | } 61 | 62 | fn triple(&self) -> &str { 63 | match *self { 64 | CompilationMode::Cross(ref target) => target.triple(), 65 | CompilationMode::Native(ref triple) => triple, 66 | } 67 | } 68 | 69 | fn is_native(&self) -> bool { 70 | match *self { 71 | CompilationMode::Native(_) => true, 72 | _ => false, 73 | } 74 | } 75 | } 76 | 77 | pub fn main_inner(xargo_mode: XargoMode) { 78 | fn show_backtrace() -> bool { 79 | env::var("RUST_BACKTRACE").as_ref().map(|s| &s[..]) == Ok("1") 80 | } 81 | 82 | match run(xargo_mode) { 83 | Err(e) => { 84 | let stderr = io::stderr(); 85 | let mut stderr = stderr.lock(); 86 | 87 | writeln!(stderr, "error: {}", e).ok(); 88 | 89 | for e in e.iter().skip(1) { 90 | writeln!(stderr, "caused by: {}", e).ok(); 91 | } 92 | 93 | if show_backtrace() { 94 | if let Some(backtrace) = e.backtrace() { 95 | writeln!(stderr, "{:?}", backtrace).ok(); 96 | } 97 | } else { 98 | writeln!(stderr, "note: run with `RUST_BACKTRACE=1` for a backtrace").ok(); 99 | } 100 | 101 | process::exit(1) 102 | } 103 | Ok(Some(status)) => if !status.success() { 104 | process::exit(status.code().unwrap_or(1)) 105 | }, 106 | Ok(None) => {} 107 | } 108 | } 109 | 110 | fn run(cargo_mode: XargoMode) -> Result> { 111 | let args = cli::args(); 112 | let verbose = args.verbose(); 113 | 114 | let meta = rustc::version().map_err(|_| "could not determine rustc version")?; 115 | 116 | if let Some(sc) = args.subcommand() { 117 | if !sc.needs_sysroot() { 118 | return cargo::run(&args, verbose).map(Some); 119 | } 120 | } else if args.version() { 121 | writeln!( 122 | io::stderr(), 123 | concat!("xargo ", env!("CARGO_PKG_VERSION"), "{}"), 124 | include_str!(concat!(env!("OUT_DIR"), "/commit-info.txt")) 125 | ).ok(); 126 | 127 | return cargo::run(&args, verbose).map(Some); 128 | } 129 | 130 | let config = cargo::config()?; 131 | if let Some(root) = cargo::root(cargo_mode, args.manifest_path())? { 132 | // We can't build sysroot with stable or beta due to unstable features 133 | let sysroot = rustc::sysroot(verbose)?; 134 | let src = match meta.channel { 135 | Channel::Dev => rustc::Src::from_env().ok_or( 136 | "The XARGO_RUST_SRC env variable must be set and point to the \ 137 | Rust source directory when working with the 'dev' channel", 138 | )?, 139 | Channel::Nightly => if let Some(src) = rustc::Src::from_env() { 140 | src 141 | } else { 142 | sysroot.src()? 143 | }, 144 | Channel::Stable | Channel::Beta => { 145 | eprintln!( 146 | "ERROR: the sysroot can't be built for the {:?} channel. \ 147 | Switch to nightly.", 148 | meta.channel 149 | ); 150 | process::exit(1); 151 | } 152 | }; 153 | let cmode = if let Some(triple) = args.target() { 154 | if Path::new(triple).is_file() { 155 | bail!( 156 | "Xargo doesn't support files as an argument to --target. \ 157 | Use `--target foo` instead of `--target foo.json`." 158 | ) 159 | } else if triple == meta.host { 160 | Some(CompilationMode::Native(meta.host.clone())) 161 | } else { 162 | Target::new(triple, &root, verbose)?.map(CompilationMode::Cross) 163 | } 164 | } else { 165 | if let Some(ref config) = config { 166 | if let Some(triple) = config.target()? { 167 | Target::new(triple, &root, verbose)?.map(CompilationMode::Cross) 168 | } else { 169 | Some(CompilationMode::Native(meta.host.clone())) 170 | } 171 | } else { 172 | Some(CompilationMode::Native(meta.host.clone())) 173 | } 174 | }; 175 | 176 | if let Some(cmode) = cmode { 177 | let home = xargo::home(&cmode)?; 178 | let rustflags = cargo::rustflags(config.as_ref(), cmode.triple())?; 179 | 180 | sysroot::update( 181 | &cmode, 182 | &home, 183 | &root, 184 | &rustflags, 185 | &meta, 186 | &src, 187 | &sysroot, 188 | verbose, 189 | args.message_format(), 190 | cargo_mode, 191 | )?; 192 | 193 | if args.subcommand().is_some() || cargo_mode == XargoMode::Build { 194 | return xargo::run( 195 | &args, 196 | &cmode, 197 | rustflags, 198 | &home, 199 | &meta, 200 | config.as_ref(), 201 | verbose, 202 | ).map(Some); 203 | } else { 204 | return Ok(None) 205 | } 206 | } 207 | } 208 | 209 | cargo::run(&args, verbose).map(Some) 210 | } 211 | 212 | -------------------------------------------------------------------------------- /src/rustc.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::hash::{Hash, Hasher}; 3 | use std::path::{Path, PathBuf}; 4 | use std::process::Command; 5 | 6 | pub use rustc_version::version_meta as version; 7 | 8 | use serde_json::Value; 9 | use serde_json; 10 | 11 | use errors::*; 12 | use extensions::CommandExt; 13 | use {rustc, util}; 14 | use cargo::Root; 15 | 16 | fn command() -> Command { 17 | env::var_os("RUSTC") 18 | .map(Command::new) 19 | .unwrap_or_else(|| Command::new("rustc")) 20 | } 21 | 22 | /// `rustc --print target-list` 23 | pub fn targets(verbose: bool) -> Result> { 24 | command() 25 | .args(&["--print", "target-list"]) 26 | .run_and_get_stdout(verbose) 27 | .map(|t| t.lines().map(|l| l.to_owned()).collect()) 28 | } 29 | 30 | /// `rustc --print sysroot` 31 | pub fn sysroot(verbose: bool) -> Result { 32 | command() 33 | .args(&["--print", "sysroot"]) 34 | .run_and_get_stdout(verbose) 35 | .map(|l| { 36 | Sysroot { 37 | path: PathBuf::from(l.trim()), 38 | } 39 | }) 40 | } 41 | /// Path to Rust source 42 | pub struct Src { 43 | path: PathBuf, 44 | } 45 | 46 | impl Src { 47 | pub fn from_env() -> Option { 48 | env::var_os("XARGO_RUST_SRC").map(|s| { 49 | let path = PathBuf::from(s); 50 | // To support relative paths, we have to make sure we canonicalize 51 | // before changing the working directory. 52 | let path = path.canonicalize().unwrap_or(path); 53 | Src { path } 54 | }) 55 | } 56 | 57 | pub fn path(&self) -> &Path { 58 | &self.path 59 | } 60 | } 61 | 62 | /// Path to `rustc`'s sysroot 63 | pub struct Sysroot { 64 | path: PathBuf, 65 | } 66 | 67 | impl Sysroot { 68 | pub fn path(&self) -> &Path { 69 | &self.path 70 | } 71 | 72 | /// Returns the path to Rust source, `$SRC`, where `$SRC/libstd/Cargo.toml` 73 | /// or `$SRC/std/Cargo.toml` exists. 74 | pub fn src(&self) -> Result { 75 | let src = self.path().join("lib").join("rustlib").join("src"); 76 | 77 | if src.join("rust").join("src").join("libstd").join("Cargo.toml").is_file() { 78 | return Ok(Src { 79 | path: src.join("rust").join("src"), 80 | }); 81 | } 82 | 83 | if src.join("rust").join("library").join("std").join("Cargo.toml").is_file() { 84 | return Ok(Src { 85 | path: src.join("rust").join("library"), 86 | }); 87 | } 88 | 89 | Err( 90 | "`rust-src` component not found. Run `rustup component add \ 91 | rust-src`.", 92 | )? 93 | } 94 | } 95 | 96 | #[derive(Debug)] 97 | pub enum Target { 98 | Builtin { triple: String }, 99 | Custom { json: PathBuf, triple: String }, 100 | } 101 | 102 | impl Target { 103 | pub fn new(triple: &str, root: &Root, verbose: bool) -> Result> { 104 | let triple = triple.to_owned(); 105 | 106 | if rustc::targets(verbose)?.iter().any(|t| t == &triple) { 107 | Ok(Some(Target::Builtin { triple: triple })) 108 | } else { 109 | let mut json = root.path().join(&triple); 110 | json.set_extension("json"); 111 | 112 | if json.exists() { 113 | return Ok(Some(Target::Custom { 114 | json: json, 115 | triple: triple, 116 | })); 117 | } else { 118 | if let Some(p) = env::var_os("RUST_TARGET_PATH") { 119 | let mut json = PathBuf::from(p); 120 | json.push(&triple); 121 | json.set_extension("json"); 122 | 123 | if json.exists() { 124 | return Ok(Some(Target::Custom { 125 | json: json, 126 | triple: triple, 127 | })); 128 | } 129 | } 130 | } 131 | 132 | Ok(None) 133 | } 134 | } 135 | 136 | pub fn triple(&self) -> &str { 137 | match *self { 138 | Target::Builtin { ref triple } => triple, 139 | Target::Custom { ref triple, .. } => triple, 140 | } 141 | } 142 | 143 | pub fn hash(&self, hasher: &mut H) -> Result<()> 144 | where 145 | H: Hasher, 146 | { 147 | if let Target::Custom { ref json, .. } = *self { 148 | // Here we roundtrip to/from JSON to get the same hash when some 149 | // fields of the JSON file has been shuffled around 150 | serde_json::from_str::(&util::read(json)?) 151 | .chain_err(|| format!("{} is not valid JSON", json.display()))? 152 | .to_string() 153 | .hash(hasher); 154 | } 155 | 156 | Ok(()) 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /src/sysroot.rs: -------------------------------------------------------------------------------- 1 | use std::collections::BTreeMap; 2 | use std::collections::hash_map::DefaultHasher; 3 | use std::hash::{Hash, Hasher}; 4 | use std::io::{self, Write}; 5 | use std::path::{Path, PathBuf}; 6 | use std::{env, fs}; 7 | 8 | use rustc_version::VersionMeta; 9 | use tempdir::TempDir; 10 | use toml::{value::Table, Value, map::Map}; 11 | 12 | use CompilationMode; 13 | use cargo::{Root, Rustflags}; 14 | use errors::*; 15 | use extensions::CommandExt; 16 | use rustc::{Src, Sysroot, Target}; 17 | use util; 18 | use xargo::Home; 19 | use {cargo, xargo}; 20 | 21 | fn profile() -> &'static str { 22 | "release" 23 | } 24 | 25 | fn build( 26 | cmode: &CompilationMode, 27 | blueprint: Blueprint, 28 | ctoml: &Option, 29 | home: &Home, 30 | rustflags: &Rustflags, 31 | src: &Src, 32 | sysroot: &Sysroot, 33 | hash: u64, 34 | verbose: bool, 35 | message_format: Option<&str>, 36 | cargo_mode: XargoMode, 37 | ) -> Result<()> { 38 | const TOML: &'static str = r#" 39 | [package] 40 | authors = ["The Rust Project Developers"] 41 | name = "sysroot" 42 | version = "0.0.0" 43 | "#; 44 | 45 | let rustlib = home.lock_rw(cmode.triple())?; 46 | rustlib 47 | .remove_siblings() 48 | .chain_err(|| format!("couldn't clear {}", rustlib.path().display()))?; 49 | let dst = rustlib.parent().join("lib"); 50 | util::mkdir(&dst)?; 51 | 52 | if cmode.triple().contains("pc-windows-gnu") && cargo_mode == XargoMode::Build { 53 | let src = &sysroot 54 | .path() 55 | .join("lib") 56 | .join("rustlib") 57 | .join(cmode.triple()) 58 | .join("lib"); 59 | 60 | // Some extra files are required for linking executables/dlls -- but they moved, so we have 61 | // to support both locations. 62 | let mut files = vec![PathBuf::from("rsbegin.o"), PathBuf::from("rsend.o")]; 63 | if src.join("crt2.o").exists() { 64 | files.push(PathBuf::from("crt2.o")); 65 | files.push(PathBuf::from("dllcrt2.o")); 66 | } else { 67 | let base = PathBuf::from("self-contained"); 68 | fs::create_dir(dst.join("self-contained")).chain_err(|| { 69 | format!( 70 | "couldn't create directory \"self-contained\" in {}", 71 | dst.display() 72 | ) 73 | })?; 74 | files.push(base.join("crt2.o")); 75 | files.push(base.join("dllcrt2.o")); 76 | } 77 | 78 | for file in files.iter() { 79 | let file_src = src.join(file); 80 | let file_dst = dst.join(file); 81 | fs::copy(&file_src, &file_dst).chain_err(|| { 82 | format!( 83 | "couldn't copy {} to {}", 84 | file_src.display(), 85 | file_dst.display() 86 | ) 87 | })?; 88 | } 89 | } 90 | 91 | for (_, stage) in blueprint.stages { 92 | let td = TempDir::new("xargo").chain_err(|| "couldn't create a temporary directory")?; 93 | let tdp; 94 | let td = if env::var_os("XARGO_KEEP_TEMP").is_some() { 95 | tdp = td.into_path(); 96 | &tdp 97 | } else { 98 | td.path() 99 | }; 100 | 101 | let mut stoml = TOML.to_owned(); 102 | { 103 | let mut map = Table::new(); 104 | 105 | map.insert("dependencies".to_owned(), Value::Table(stage.dependencies)); 106 | map.insert("patch".to_owned(), Value::Table(stage.patch)); 107 | 108 | stoml.push_str(&Value::Table(map).to_string()); 109 | } 110 | 111 | if let Some(ctoml) = ctoml { 112 | if let Some(profile) = ctoml.profile() { 113 | stoml.push_str(&profile.to_string()) 114 | } 115 | } 116 | 117 | // rust-src comes with a lockfile for libstd. Use it. 118 | let src_parent = src.path().parent().map(Path::to_path_buf).unwrap_or_else(|| src.path().join("..")); 119 | let lockfile = src_parent.join("Cargo.lock"); 120 | let target_lockfile = td.join("Cargo.lock"); 121 | fs::copy(lockfile, &target_lockfile).chain_err(|| "Cargo.lock file is missing from source dir")?; 122 | 123 | let mut perms = fs::metadata(&target_lockfile) 124 | .chain_err(|| "Cargo.lock file is missing from target dir")? 125 | .permissions(); 126 | perms.set_readonly(false); 127 | fs::set_permissions(&target_lockfile, perms) 128 | .chain_err(|| "Cargo.lock file is missing from target dir")?; 129 | 130 | util::write(&td.join("Cargo.toml"), &stoml)?; 131 | util::mkdir(&td.join("src"))?; 132 | util::write(&td.join("src").join("lib.rs"), "")?; 133 | 134 | let cargo = || { 135 | let mut cmd = cargo::command(); 136 | let mut flags = rustflags.clone(); 137 | flags.push(&["-Z", "force-unstable-if-unmarked"]); 138 | if verbose { 139 | writeln!(io::stderr(), "+ RUSTFLAGS={}", flags).ok(); 140 | } 141 | cmd.env("CARGO_ENCODED_RUSTFLAGS", flags.encode(home)); 142 | 143 | // Since we currently don't want to respect `.cargo/config` or `CARGO_TARGET_DIR`, 144 | // we need to force the target directory to match the `cp_r` below. 145 | cmd.env("CARGO_TARGET_DIR", td.join("target")); 146 | 147 | // Workaround #261. 148 | // 149 | // If a crate is shared between the sysroot and a binary, we might 150 | // end up with conflicting symbols. This is because both versions 151 | // of the crate would get linked, and their metadata hash would be 152 | // exactly the same. 153 | // 154 | // To avoid this, we need to inject some data that modifies the 155 | // metadata hash. Fortunately, cargo already has a mechanism for 156 | // this, the __CARGO_DEFAULT_LIB_METADATA environment variable. 157 | // Unsurprisingly, rust's bootstrap (which has basically the same 158 | // role as xargo of building the libstd) makes use of this 159 | // environment variable to avoid exactly this problem. See here: 160 | // https://github.com/rust-lang/rust/blob/73369f32621f6a844a80a8513ae3ded901e4a406/src/bootstrap/builder.rs#L876 161 | // 162 | // This relies on an **unstable cargo feature** that isn't meant to 163 | // be used outside the bootstrap. This is explicitly stated in 164 | // cargo's source: 165 | // https://github.com/rust-lang/cargo/blob/14654f38d0819c47d7a605d6f1797ffbcdc65000/src/cargo/core/compiler/context/compilation_files.rs#L496 166 | // Unfortunately, I don't see any other way out. We need to have a 167 | // way to modify the crate's hash, and from the outside this is the 168 | // only way to do so. 169 | cmd.env("__CARGO_DEFAULT_LIB_METADATA", "xargo"); 170 | 171 | // As of rust-lang/cargo#4788 Cargo invokes rustc with a changed "current directory" so 172 | // we can't assume that such directory will be the same as the directory from which 173 | // Xargo was invoked. This is specially true when compiling the sysroot as the std 174 | // source is provided as a workspace and Cargo will change the current directory to the 175 | // root of the workspace when building one. To ensure rustc finds a target specification 176 | // file stored in the current directory we'll set `RUST_TARGET_PATH` to the current 177 | // directory. 178 | if env::var_os("RUST_TARGET_PATH").is_none() { 179 | if let CompilationMode::Cross(ref target) = *cmode { 180 | if let Target::Custom { ref json, .. } = *target { 181 | cmd.env("RUST_TARGET_PATH", json.parent().unwrap()); 182 | } 183 | } 184 | } 185 | 186 | match cargo_mode { 187 | XargoMode::Build => cmd.arg("build"), 188 | XargoMode::Check => cmd.arg("check") 189 | }; 190 | 191 | cmd.arg("--release"); 192 | cmd.arg("--manifest-path"); 193 | cmd.arg(td.join("Cargo.toml")); 194 | cmd.args(&["--target", cmode.triple()]); 195 | if let Some(format) = message_format { 196 | cmd.args(&["--message-format", format]); 197 | } 198 | 199 | if verbose { 200 | cmd.arg("-v"); 201 | } 202 | 203 | cmd 204 | }; 205 | 206 | for krate in stage.crates { 207 | cargo().arg("-p").arg(krate).run(verbose)?; 208 | } 209 | 210 | // Copy artifacts to Xargo sysroot 211 | util::cp_r( 212 | &td.join("target") 213 | .join(cmode.triple()) 214 | .join(profile()) 215 | .join("deps"), 216 | &dst, 217 | )?; 218 | } 219 | 220 | // Create hash file 221 | util::write(&rustlib.parent().join(".hash"), &hash.to_string())?; 222 | 223 | Ok(()) 224 | } 225 | 226 | fn old_hash(cmode: &CompilationMode, home: &Home) -> Result> { 227 | // FIXME this should be `lock_ro` 228 | let lock = home.lock_rw(cmode.triple())?; 229 | let hfile = lock.parent().join(".hash"); 230 | 231 | if hfile.exists() { 232 | Ok(util::read(&hfile)?.parse().ok()) 233 | } else { 234 | Ok(None) 235 | } 236 | } 237 | 238 | /// Computes the hash of the would-be target sysroot 239 | /// 240 | /// This information is used to compute the hash 241 | /// 242 | /// - Dependencies in `Xargo.toml` for a specific target 243 | /// - RUSTFLAGS / build.rustflags / target.*.rustflags 244 | /// - The target specification file, is any 245 | /// - `[profile.release]` in `Cargo.toml` 246 | /// - `rustc` commit hash 247 | fn hash( 248 | cmode: &CompilationMode, 249 | blueprint: &Blueprint, 250 | rustflags: &Rustflags, 251 | ctoml: &Option, 252 | meta: &VersionMeta, 253 | ) -> Result { 254 | let mut hasher = DefaultHasher::new(); 255 | 256 | blueprint.hash(&mut hasher); 257 | 258 | rustflags.hash(&mut hasher); 259 | 260 | cmode.hash(&mut hasher)?; 261 | 262 | if let Some(ctoml) = ctoml { 263 | if let Some(profile) = ctoml.profile() { 264 | profile.hash(&mut hasher); 265 | } 266 | } 267 | 268 | if let Some(ref hash) = meta.commit_hash { 269 | hash.hash(&mut hasher); 270 | } 271 | 272 | Ok(hasher.finish()) 273 | } 274 | 275 | pub fn update( 276 | cmode: &CompilationMode, 277 | home: &Home, 278 | root: &Root, 279 | rustflags: &Rustflags, 280 | meta: &VersionMeta, 281 | src: &Src, 282 | sysroot: &Sysroot, 283 | verbose: bool, 284 | message_format: Option<&str>, 285 | cargo_mode: XargoMode, 286 | ) -> Result<()> { 287 | let ctoml = match cargo_mode { 288 | XargoMode::Build => Some(cargo::toml(root)?), 289 | XargoMode::Check => { 290 | if root.path().join("Cargo.toml").exists() { 291 | Some(cargo::toml(root)?) 292 | } else { 293 | None 294 | } 295 | } 296 | }; 297 | 298 | let (xtoml_parent, xtoml) = xargo::toml(root)?; 299 | 300 | // As paths in the 'Xargo.toml' can be relative to the directory containing 301 | // the 'Xargo.toml', we need to pass the path containing it to the 302 | // Blueprint. Otherwise, if no 'Xargo.toml' is found, we use the regular 303 | // root path. 304 | let base_path: &Path = xtoml_parent.unwrap_or_else(|| root.path()); 305 | 306 | let blueprint = Blueprint::from(xtoml.as_ref(), cmode.triple(), &base_path, &src)?; 307 | 308 | let hash = hash(cmode, &blueprint, rustflags, &ctoml, meta)?; 309 | 310 | if old_hash(cmode, home)? != Some(hash) { 311 | build( 312 | cmode, 313 | blueprint, 314 | &ctoml, 315 | home, 316 | rustflags, 317 | src, 318 | sysroot, 319 | hash, 320 | verbose, 321 | message_format, 322 | cargo_mode, 323 | )?; 324 | } 325 | 326 | // copy host artifacts into the sysroot, if necessary 327 | if cmode.is_native() { 328 | return Ok(()); 329 | } 330 | 331 | let lock = home.lock_rw(&meta.host)?; 332 | let hfile = lock.parent().join(".hash"); 333 | 334 | let hash = meta.commit_hash.as_ref().map(|s| &**s).unwrap_or(""); 335 | if hfile.exists() { 336 | if util::read(&hfile)? == hash { 337 | return Ok(()); 338 | } 339 | } 340 | 341 | lock.remove_siblings() 342 | .chain_err(|| format!("couldn't clear {}", lock.path().display()))?; 343 | let dst = lock.parent().join("lib"); 344 | util::mkdir(&dst)?; 345 | util::cp_r( 346 | &sysroot 347 | .path() 348 | .join("lib") 349 | .join("rustlib") 350 | .join(&meta.host) 351 | .join("lib"), 352 | &dst, 353 | )?; 354 | 355 | let bin_src = sysroot.path().join("lib").join("rustlib").join(&meta.host).join("bin"); 356 | // copy the Rust linker if it exists 357 | if bin_src.exists() { 358 | let bin_dst = lock.parent().join("bin"); 359 | util::mkdir(&bin_dst)?; 360 | util::cp_r(&bin_src, &bin_dst)?; 361 | } 362 | 363 | util::write(&hfile, hash)?; 364 | 365 | Ok(()) 366 | } 367 | 368 | /// Per stage dependencies 369 | #[derive(Debug)] 370 | pub struct Stage { 371 | crates: Vec, 372 | dependencies: Table, 373 | patch: Table, 374 | } 375 | 376 | /// Which mode to invoke `cargo` in when building the sysroot 377 | /// Can be either `cargo build` or `cargo check` 378 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] 379 | pub enum XargoMode { 380 | Build, 381 | Check, 382 | } 383 | 384 | /// A sysroot that will be built in "stages" 385 | #[derive(Debug)] 386 | pub struct Blueprint { 387 | stages: BTreeMap, 388 | } 389 | 390 | trait AsTableMut { 391 | fn as_table_mut_or_err(&mut self, on_error_path: F) -> Result<&mut Table> 392 | where 393 | F: FnOnce() -> R, 394 | R: ::std::fmt::Display; 395 | } 396 | 397 | impl AsTableMut for Value { 398 | /// If the `self` is a Value::Table, return `Ok` with mutable reference to 399 | /// the contained table. If it's not return `Err` with an error message. 400 | /// The result of `on_error_path` will be inserted in the error message and 401 | /// should indicate the TOML path of `self`. 402 | fn as_table_mut_or_err(&mut self, on_error_path: F) -> Result<&mut Table> 403 | where 404 | F: FnOnce() -> R, 405 | R: ::std::fmt::Display, 406 | { 407 | match self { 408 | Value::Table(table) => Ok(table), 409 | _ => Err(format!("Xargo.toml: `{}` must be a table", on_error_path()).into()), 410 | } 411 | } 412 | } 413 | 414 | impl Blueprint { 415 | fn new() -> Self { 416 | Blueprint { 417 | stages: BTreeMap::new(), 418 | } 419 | } 420 | 421 | /// Add $CRATE to `patch` section, as needed to build libstd. 422 | fn add_patch(patch: &mut Table, src_path: &Path, crate_: &str) -> Result<()> { 423 | // Old sysroots have this in `src/tools/$CRATE`, new sysroots in `library/$CRATE`. 424 | let paths = [ 425 | src_path.join(crate_), 426 | src_path.join("tools").join(crate_), 427 | ]; 428 | if let Some(path) = paths.iter().find(|p| p.exists()) { 429 | // add crate to patch section (if not specified) 430 | fn table_entry<'a>(table: &'a mut Table, key: &str) -> Result<&'a mut Table> { 431 | table 432 | .entry(key) 433 | .or_insert_with(|| Value::Table(Table::new())) 434 | .as_table_mut_or_err(|| key) 435 | } 436 | 437 | let crates_io = table_entry(patch, "crates-io")?; 438 | if !crates_io.contains_key(crate_) { 439 | table_entry(crates_io, crate_)? 440 | .insert("path".into(), Value::String(path.display().to_string())); 441 | } 442 | } 443 | Ok(()) 444 | } 445 | 446 | fn from(toml: Option<&xargo::Toml>, target: &str, base_path: &Path, src: &Src) -> Result { 447 | fn make_path_absolute( 448 | crate_spec: &mut Table, 449 | base_path: &Path, 450 | on_error_path: F, 451 | ) -> Result<()> 452 | where 453 | F: FnOnce() -> R, 454 | R: ::std::fmt::Display, 455 | { 456 | if let Some(path) = crate_spec.get_mut("path") { 457 | let p = PathBuf::from( 458 | path.as_str() 459 | .ok_or_else(|| format!("`{}.path` must be a string", on_error_path()))?, 460 | ); 461 | 462 | if !p.is_absolute() { 463 | *path = Value::String( 464 | base_path 465 | .join(&p) 466 | .canonicalize() 467 | .chain_err(|| format!("couldn't canonicalize {}", p.display()))? 468 | .display() 469 | .to_string(), 470 | ); 471 | } 472 | } 473 | Ok(()) 474 | } 475 | 476 | // Compose patch section 477 | let mut patch = match toml.and_then(xargo::Toml::patch) { 478 | Some(value) => value 479 | .as_table() 480 | .cloned() 481 | .ok_or_else(|| format!("Xargo.toml: `patch` must be a table"))?, 482 | None => Table::new() 483 | }; 484 | 485 | for (k1, v) in patch.iter_mut() { 486 | for (k2, v) in v.as_table_mut_or_err(|| format!("patch.{}", k1))?.iter_mut() { 487 | let krate = v.as_table_mut_or_err(|| format!("patch.{}.{}", k1, k2))?; 488 | 489 | make_path_absolute(krate, base_path, || format!("patch.{}.{}", k1, k2))?; 490 | } 491 | } 492 | 493 | Blueprint::add_patch(&mut patch, src.path(), "rustc-std-workspace-core")?; 494 | Blueprint::add_patch(&mut patch, src.path(), "rustc-std-workspace-alloc")?; 495 | Blueprint::add_patch(&mut patch, src.path(), "rustc-std-workspace-std")?; 496 | 497 | // Compose dependency sections 498 | let deps = match ( 499 | toml.and_then(|t| t.dependencies()), 500 | toml.and_then(|t| t.target_dependencies(target)), 501 | ) { 502 | (Some(value), Some(tvalue)) => { 503 | let mut deps = value 504 | .as_table() 505 | .cloned() 506 | .ok_or_else(|| format!("Xargo.toml: `dependencies` must be a table"))?; 507 | 508 | let more_deps = tvalue.as_table().ok_or_else(|| { 509 | format!( 510 | "Xargo.toml: `target.{}.dependencies` must be \ 511 | a table", 512 | target 513 | ) 514 | })?; 515 | for (k, v) in more_deps { 516 | if deps.insert(k.to_owned(), v.clone()).is_some() { 517 | Err(format!( 518 | "found duplicate dependency name {}, \ 519 | but all dependencies must have a \ 520 | unique name", 521 | k 522 | ))? 523 | } 524 | } 525 | 526 | deps 527 | } 528 | (Some(value), None) | (None, Some(value)) => if let Some(table) = value.as_table() { 529 | table.clone() 530 | } else { 531 | Err(format!( 532 | "Xargo.toml: target.{}.dependencies must be \ 533 | a table", 534 | target 535 | ))? 536 | }, 537 | (None, None) => { 538 | // If no dependencies were listed, we assume `core` and `compiler_builtins` as the 539 | // dependencies 540 | let mut t = Map::new(); 541 | let mut core = Map::new(); 542 | core.insert("stage".to_owned(), Value::Integer(0)); 543 | t.insert("core".to_owned(), Value::Table(core)); 544 | let mut cb = Map::new(); 545 | cb.insert( 546 | "features".to_owned(), 547 | Value::Array(vec![Value::String("mem".to_owned())]), 548 | ); 549 | // reference compiler-builtins with `version = "*"`, 550 | // the corresponding version of compiler_builtins matching the used std 551 | // is selected because of the copied `Cargo.lock`-file from std. 552 | cb.insert("version".to_owned(), 553 | Value::String("*".to_owned())); 554 | cb.insert("stage".to_owned(), Value::Integer(1)); 555 | t.insert( 556 | "compiler_builtins".to_owned(), 557 | Value::Table(cb), 558 | ); 559 | t 560 | } 561 | }; 562 | 563 | let mut blueprint = Blueprint::new(); 564 | for (k, v) in deps { 565 | if let Value::Table(mut map) = v { 566 | let stage = if let Some(value) = map.remove("stage") { 567 | value 568 | .as_integer() 569 | .ok_or_else(|| format!("dependencies.{}.stage must be an integer", k))? 570 | } else { 571 | 0 572 | }; 573 | 574 | make_path_absolute(&mut map, base_path, || format!("dependencies.{}", k))?; 575 | 576 | if !map.contains_key("path") && !map.contains_key("git") { 577 | // No path and no git given. This might be in the sysroot, but if we don't find it there we assume it comes from crates.io. 578 | // Current sysroots call it just "std" (etc), but older sysroots use "libstd" (etc), 579 | // so we check both. 580 | let paths = [ 581 | src.path().join(&k), 582 | src.path().join(format!("lib{}", k)), 583 | ]; 584 | if let Some(path) = paths.iter().find(|p| p.exists()) { 585 | map.insert("path".to_owned(), Value::String(path.display().to_string())); 586 | } 587 | } 588 | 589 | blueprint.push(stage, k, map, &patch); 590 | } else { 591 | Err(format!( 592 | "Xargo.toml: target.{}.dependencies.{} must be \ 593 | a table", 594 | target, k 595 | ))? 596 | } 597 | } 598 | 599 | Ok(blueprint) 600 | } 601 | 602 | fn push(&mut self, stage: i64, krate: String, toml: Table, patch: &Table) { 603 | let stage = self.stages.entry(stage).or_insert_with(|| Stage { 604 | crates: vec![], 605 | dependencies: Table::new(), 606 | patch: patch.clone(), 607 | }); 608 | 609 | stage.dependencies.insert(krate.clone(), Value::Table(toml)); 610 | stage.crates.push(krate); 611 | } 612 | 613 | fn hash(&self, hasher: &mut H) 614 | where 615 | H: Hasher, 616 | { 617 | for stage in self.stages.values() { 618 | for (k, v) in stage.dependencies.iter() { 619 | k.hash(hasher); 620 | v.to_string().hash(hasher); 621 | } 622 | } 623 | } 624 | } 625 | -------------------------------------------------------------------------------- /src/util.rs: -------------------------------------------------------------------------------- 1 | use std::fs::File; 2 | use std::io::{Read, Write}; 3 | use std::path::Path; 4 | use std::fs; 5 | 6 | use toml::Value; 7 | use walkdir::WalkDir; 8 | 9 | use errors::*; 10 | 11 | pub fn cp_r(src: &Path, dst: &Path) -> Result<()> { 12 | for e in WalkDir::new(src) { 13 | // This is only an error when there's some sort of intermittent IO error 14 | // during iteration. 15 | // see https://doc.rust-lang.org/std/fs/struct.ReadDir.html 16 | let e = e.chain_err(|| { 17 | format!( 18 | "intermittent IO error while iterating directory `{}`", 19 | src.display() 20 | ) 21 | })?; 22 | 23 | let src_file = e.path(); 24 | let relative_path = src_file.strip_prefix(src).chain_err(|| { 25 | format!( 26 | "Could not retrieve relative path of child directory or \ 27 | file `{}` with regards to parent directory `{}`", 28 | src_file.display(), 29 | src.display() 30 | ) 31 | })?; 32 | 33 | let dst_file = dst.join(relative_path); 34 | let metadata = e.metadata().chain_err(|| { 35 | format!("Could not retrieve metadata of `{}`", e.path().display()) 36 | })?; 37 | 38 | if metadata.is_dir() { 39 | // ensure the destination directory exists 40 | fs::create_dir_all(&dst_file).chain_err(|| { 41 | format!("Could not create directory `{}`", dst_file.display()) 42 | })?; 43 | } else { 44 | // else copy the file 45 | fs::copy(&src_file, &dst_file).chain_err(|| { 46 | format!( 47 | "copying files from `{}` to `{}` failed", 48 | src_file.display(), 49 | dst_file.display() 50 | ) 51 | })?; 52 | }; 53 | } 54 | 55 | Ok(()) 56 | } 57 | 58 | pub fn mkdir(path: &Path) -> Result<()> { 59 | fs::create_dir(path).chain_err(|| format!("couldn't create directory {}", path.display())) 60 | } 61 | 62 | /// Parses `path` as TOML 63 | pub fn parse(path: &Path) -> Result { 64 | Ok(toml::from_str(&read(path)?) 65 | .map_err(|_| format!("{} is not valid TOML", path.display()))?) 66 | } 67 | 68 | pub fn read(path: &Path) -> Result { 69 | let mut s = String::new(); 70 | 71 | let p = path.display(); 72 | File::open(path) 73 | .chain_err(|| format!("couldn't open {}", p))? 74 | .read_to_string(&mut s) 75 | .chain_err(|| format!("couldn't read {}", p))?; 76 | 77 | Ok(s) 78 | } 79 | 80 | /// Search for `file` in `path` and its parent directories 81 | pub fn search<'p>(mut path: &'p Path, file: &str) -> Option<&'p Path> { 82 | loop { 83 | if path.join(file).exists() { 84 | return Some(path); 85 | } 86 | 87 | if let Some(p) = path.parent() { 88 | path = p; 89 | } else { 90 | return None; 91 | } 92 | } 93 | } 94 | 95 | pub fn write(path: &Path, contents: &str) -> Result<()> { 96 | let p = path.display(); 97 | File::create(path) 98 | .chain_err(|| format!("couldn't open {}", p))? 99 | .write_all(contents.as_bytes()) 100 | .chain_err(|| format!("couldn't write to {}", p)) 101 | } 102 | -------------------------------------------------------------------------------- /src/xargo.rs: -------------------------------------------------------------------------------- 1 | use std::path::{Display, Path, PathBuf}; 2 | use std::process::ExitStatus; 3 | use std::{env, mem}; 4 | use std::io::{self, Write}; 5 | 6 | use toml::Value; 7 | use rustc_version::VersionMeta; 8 | 9 | use CompilationMode; 10 | use cargo::{Config, Root, Rustflags, Subcommand}; 11 | use cli::Args; 12 | use errors::*; 13 | use extensions::CommandExt; 14 | use flock::{FileLock, Filesystem}; 15 | use {cargo, util}; 16 | 17 | pub fn run( 18 | args: &Args, 19 | cmode: &CompilationMode, 20 | rustflags: Rustflags, 21 | home: &Home, 22 | meta: &VersionMeta, 23 | config: Option<&Config>, 24 | verbose: bool, 25 | ) -> Result { 26 | let mut cmd = cargo::command(); 27 | cmd.args(args.all()); 28 | 29 | if args.subcommand() == Some(Subcommand::Doc) { 30 | cmd.env( 31 | "CARGO_ENCODED_RUSTDOCFLAGS", 32 | cargo::rustdocflags(config, cmode.triple())?.encode(home), 33 | ); 34 | } 35 | 36 | if verbose { 37 | writeln!(io::stderr(), "+ RUSTFLAGS={}", rustflags).ok(); 38 | } 39 | cmd.env("CARGO_ENCODED_RUSTFLAGS", rustflags.encode(home)); 40 | 41 | let locks = (home.lock_ro(&meta.host), home.lock_ro(cmode.triple())); 42 | 43 | let status = cmd.run_and_get_status(verbose)?; 44 | 45 | mem::drop(locks); 46 | 47 | Ok(status) 48 | } 49 | 50 | pub struct Home { 51 | path: Filesystem, 52 | } 53 | 54 | impl Home { 55 | pub fn display(&self) -> Display { 56 | self.path.display() 57 | } 58 | 59 | fn path(&self, triple: &str) -> Filesystem { 60 | self.path.join("lib").join("rustlib").join(triple) 61 | } 62 | 63 | pub fn lock_ro(&self, triple: &str) -> Result { 64 | let fs = self.path(triple); 65 | 66 | fs.open_ro(".sentinel", &format!("{}'s sysroot", triple)) 67 | .chain_err(|| { 68 | format!("couldn't lock {}'s sysroot as read-only", triple) 69 | }) 70 | } 71 | 72 | pub fn lock_rw(&self, triple: &str) -> Result { 73 | let fs = self.path(triple); 74 | 75 | fs.open_rw(".sentinel", &format!("{}'s sysroot", triple)) 76 | .chain_err(|| { 77 | format!("couldn't lock {}'s sysroot as read-only", triple) 78 | }) 79 | } 80 | } 81 | 82 | pub fn home(cmode: &CompilationMode) -> Result { 83 | let mut p = if let Some(h) = env::var_os("XARGO_HOME") { 84 | PathBuf::from(h) 85 | } else { 86 | dirs::home_dir() 87 | .ok_or_else(|| "couldn't find your home directory. Is $HOME set?")? 88 | .join(".xargo") 89 | }; 90 | 91 | if cmode.is_native() { 92 | p.push("HOST"); 93 | } 94 | 95 | Ok(Home { 96 | path: Filesystem::new(p), 97 | }) 98 | } 99 | 100 | pub struct Toml { 101 | table: Value, 102 | } 103 | 104 | impl Toml { 105 | /// Returns the `dependencies` part of `Xargo.toml` 106 | pub fn dependencies(&self) -> Option<&Value> { 107 | self.table.get("dependencies") 108 | } 109 | 110 | /// Returns the `target.{}.dependencies` part of `Xargo.toml` 111 | pub fn target_dependencies(&self, target: &str) -> Option<&Value> { 112 | self.table 113 | .get("target") 114 | .and_then(|t| t.get(target)) 115 | .and_then(|t| t.get("dependencies")) 116 | } 117 | 118 | /// Returns the `patch` part of `Xargo.toml` 119 | pub fn patch(&self) -> Option<&Value> { 120 | self.table.get("patch") 121 | } 122 | } 123 | 124 | /// Returns the closest directory containing a 'Xargo.toml' and the parsed 125 | /// content of this 'Xargo.toml' 126 | pub fn toml(root: &Root) -> Result<(Option<&Path>, Option)> { 127 | if let Some(p) = util::search(root.path(), "Xargo.toml") { 128 | Ok((Some(p), util::parse(&p.join("Xargo.toml")).map(|t| Some(Toml { table: t }))?)) 129 | } 130 | else { 131 | Ok((None, None)) 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /tests/smoke.rs: -------------------------------------------------------------------------------- 1 | #![deny(warnings)] 2 | 3 | #[macro_use] 4 | extern crate error_chain; 5 | #[macro_use] 6 | extern crate lazy_static; 7 | extern crate parking_lot; 8 | extern crate rustc_version; 9 | extern crate tempdir; 10 | extern crate dirs; 11 | 12 | use std::fs::OpenOptions; 13 | use std::io::Write; 14 | use std::path::{Path, PathBuf}; 15 | use std::process::Command; 16 | use std::{env, fs}; 17 | 18 | use parking_lot::{Mutex, MutexGuard}; 19 | use tempdir::TempDir; 20 | 21 | use errors::*; 22 | 23 | mod errors { 24 | #![allow(unused_doc_comments)] 25 | error_chain!(); 26 | } 27 | 28 | macro_rules! run { 29 | () => { 30 | if let Err(e) = run() { 31 | panic!("{}", e) 32 | } 33 | } 34 | } 35 | 36 | // Returns Xargo's "home" 37 | fn home() -> Result { 38 | if let Some(h) = env::var_os("XARGO_HOME") { 39 | Ok(PathBuf::from(h)) 40 | } else { 41 | Ok( 42 | dirs::home_dir() 43 | .ok_or_else(|| "couldn't find your home directory. Is $HOME set?")? 44 | .join(".xargo"), 45 | ) 46 | } 47 | } 48 | 49 | fn cleanup(target: &str) -> Result<()> { 50 | let p = home()?.join("lib/rustlib").join(target); 51 | 52 | if p.exists() { 53 | fs::remove_dir_all(&p).chain_err(|| format!("couldn't clean sysroot for {}", target)) 54 | } else { 55 | Ok(()) 56 | } 57 | } 58 | 59 | fn exists(krate: &str, target: &str) -> Result { 60 | let p = home()?.join("lib/rustlib").join(target).join("lib"); 61 | 62 | for e in fs::read_dir(&p).chain_err(|| format!("couldn't read the directory {}", p.display()))? 63 | { 64 | let e = e.chain_err(|| { 65 | format!( 66 | "error reading the contents of the directory {}", 67 | p.display() 68 | ) 69 | })?; 70 | 71 | if e.file_name().to_string_lossy().contains(krate) { 72 | return Ok(true); 73 | } 74 | } 75 | 76 | Ok(false) 77 | } 78 | 79 | fn host() -> String { 80 | rustc_version::version_meta().unwrap().host 81 | } 82 | 83 | fn mkdir(path: &Path) -> Result<()> { 84 | fs::create_dir(path).chain_err(|| { 85 | format!("couldn't create the directory {}", path.display()) 86 | }) 87 | } 88 | 89 | fn sysroot_was_built(stderr: &str, target: &str) -> bool { 90 | stderr.lines().filter(|l| l.starts_with("+")).any(|l| { 91 | l.contains("cargo") && l.contains("build") && l.contains("--target") && l.contains(target) 92 | && l.contains("-p") && l.contains("core") 93 | }) 94 | } 95 | 96 | fn write(path: &Path, append: bool, contents: &str) -> Result<()> { 97 | let p = path.display(); 98 | let mut opts = OpenOptions::new(); 99 | 100 | if append { 101 | opts.append(true); 102 | } else { 103 | opts.create(true); 104 | opts.truncate(true); 105 | } 106 | 107 | opts.write(true) 108 | .open(path) 109 | .chain_err(|| format!("couldn't open {}", p))? 110 | .write_all(contents.as_bytes()) 111 | .chain_err(|| format!("couldn't write to {}", p)) 112 | } 113 | 114 | fn xargo() -> Result { 115 | let mut p = env::current_exe().chain_err(|| "couldn't get path to current executable")?; 116 | p.pop(); 117 | p.pop(); 118 | p.push("xargo"); 119 | Ok(Command::new(p)) 120 | } 121 | 122 | fn xargo_check() -> Result { 123 | let mut p = env::current_exe().chain_err(|| "couldn't get path to current executable")?; 124 | p.pop(); 125 | p.pop(); 126 | p.push("xargo-check"); 127 | Ok(Command::new(p)) 128 | } 129 | 130 | trait CommandExt { 131 | fn run(&mut self) -> Result<()>; 132 | fn run_and_get_stderr(&mut self) -> Result; 133 | } 134 | 135 | impl CommandExt for Command { 136 | fn run(&mut self) -> Result<()> { 137 | let status = self.status() 138 | .chain_err(|| format!("couldn't execute `{:?}`", self))?; 139 | 140 | if status.success() { 141 | Ok(()) 142 | } else { 143 | Err(format!( 144 | "`{:?}` failed with exit code: {:?}", 145 | self, 146 | status.code() 147 | ))? 148 | } 149 | } 150 | 151 | fn run_and_get_stderr(&mut self) -> Result { 152 | let out = self.output() 153 | .chain_err(|| format!("couldn't execute `{:?}`", self))?; 154 | 155 | let stderr = String::from_utf8(out.stderr) 156 | .chain_err(|| format!("`{:?}` output was not UTF-8", self)); 157 | 158 | if out.status.success() { 159 | stderr 160 | } else { 161 | match stderr { 162 | Ok(e) => print!("{}", e), 163 | Err(e) => print!("{}", e), 164 | } 165 | Err(format!( 166 | "`{:?}` failed with exit code: {:?}", 167 | self, 168 | out.status.code() 169 | ))? 170 | } 171 | } 172 | } 173 | 174 | struct Project { 175 | name: &'static str, 176 | td: TempDir, 177 | } 178 | 179 | /// Create a simple project 180 | fn create_simple_project(project_path: &Path, name: &'static str, library_source: &'static str) -> Result<()> { 181 | xargo()? 182 | .args(&["init", "-q", "--lib", "--vcs", "none", "--name", name]) 183 | .current_dir(project_path) 184 | .run()?; 185 | 186 | write(&project_path.join("src/lib.rs"), false, library_source)?; 187 | 188 | Ok(()) 189 | } 190 | 191 | impl Project { 192 | /// Creates a new project with given name in a temporary directory. 193 | fn new(name: &'static str) -> Result { 194 | Self::new_in(std::env::temp_dir(), name) 195 | } 196 | 197 | /// Creates a new project with given name in a sub directory of `dir`. 198 | fn new_in(dir: PathBuf, name: &'static str) -> Result { 199 | const JSON: &'static str = r#" 200 | { 201 | "arch": "arm", 202 | "data-layout": "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64", 203 | "linker-flavor": "gcc", 204 | "llvm-target": "thumbv6m-none-eabi", 205 | "max-atomic-width": 0, 206 | "os": "none", 207 | "target-c-int-width": "32", 208 | "target-endian": "little", 209 | "target-pointer-width": "32" 210 | } 211 | "#; 212 | 213 | let td = TempDir::new_in(dir, "xargo").chain_err(|| "couldn't create a temporary directory")?; 214 | 215 | create_simple_project(td.path(), name, "#![no_std]")?; 216 | write(&td.path().join(format!("{}.json", name)), false, JSON)?; 217 | Ok(Project { name: name, td: td }) 218 | } 219 | 220 | /// Calls `xargo build` 221 | fn build(&self, target: &str) -> Result<()> { 222 | // Be less verbose 223 | self.build_and_get_stderr(Some(target))?; 224 | Ok(()) 225 | } 226 | 227 | /// Calls `xargo build` 228 | fn build_from_workdir(&self, target: &str, working_dir: &Path) -> Result<()> { 229 | // Be less verbose 230 | self.build_from_workdir_and_get_stderr(Some(target), working_dir)?; 231 | Ok(()) 232 | } 233 | 234 | /// Calls `xargo build` and collects STDERR 235 | fn build_and_get_stderr(&self, target: Option<&str>) -> Result { 236 | let mut cmd = xargo()?; 237 | cmd.arg("build"); 238 | 239 | if let Some(target) = target { 240 | cmd.args(&["--target", target]); 241 | } 242 | 243 | cmd.arg("-v") 244 | .current_dir(self.td.path()) 245 | .run_and_get_stderr() 246 | } 247 | 248 | fn build_from_workdir_and_get_stderr(&self, target: Option<&str>, working_dir: &Path) -> Result { 249 | let mut cmd = xargo()?; 250 | // set RUST_TARGET_PATH since target json file not in working dir 251 | cmd.env("RUST_TARGET_PATH", &self.td.path()); 252 | cmd.arg("build"); 253 | 254 | cmd.args(&["--manifest-path", &self.td.path().join("Cargo.toml").to_str().unwrap()]); 255 | 256 | if let Some(target) = target { 257 | cmd.args(&["--target", target]); 258 | } 259 | 260 | cmd.arg("-v") 261 | .current_dir(working_dir) 262 | .run_and_get_stderr() 263 | } 264 | 265 | /// Appends a string to the project `Cargo.toml` 266 | fn cargo_toml(&self, contents: &str) -> Result<()> { 267 | write(&self.td.path().join("Cargo.toml"), true, contents) 268 | } 269 | 270 | /// Adds a `.cargo/config` to the project 271 | fn config(&self, contents: &str) -> Result<()> { 272 | mkdir(&self.td.path().join(".cargo"))?; 273 | 274 | write(&self.td.path().join(".cargo/config"), false, contents) 275 | } 276 | 277 | /// Calls `xargo doc` 278 | fn doc(&self, target: &str) -> Result<()> { 279 | xargo()? 280 | .args(&["doc", "--target", target]) 281 | .current_dir(self.td.path()) 282 | .run_and_get_stderr()?; 283 | Ok(()) 284 | } 285 | 286 | 287 | /// Adds a `Xargo.toml` to the project 288 | fn xargo_toml(&self, toml: &str) -> Result<()> { 289 | write(&self.td.path().join("Xargo.toml"), false, toml) 290 | } 291 | } 292 | 293 | impl Drop for Project { 294 | fn drop(&mut self) { 295 | cleanup(self.name).unwrap() 296 | } 297 | } 298 | 299 | fn hcleanup(triple: &str) -> Result<()> { 300 | let p = home()?.join("HOST/lib/rustlib").join(triple); 301 | 302 | if p.exists() { 303 | fs::remove_dir_all(&p).chain_err(|| format!("couldn't clean sysroot for {}", triple)) 304 | } else { 305 | Ok(()) 306 | } 307 | } 308 | 309 | struct HProject { 310 | _guard: MutexGuard<'static, ()>, 311 | host: String, 312 | td: TempDir, 313 | } 314 | 315 | impl HProject { 316 | fn new(test: bool) -> Result { 317 | // There can only be one instance of this type at any point in time. 318 | // Needed to make sure we don't try to build multiple HOST libstds in parallel. 319 | lazy_static! { 320 | static ref ONCE: Mutex<()> = Mutex::new(()); 321 | } 322 | 323 | let guard = ONCE.lock(); 324 | 325 | // Put a space into the working directory name to also test that case. 326 | let td = TempDir::new("xar go").chain_err(|| "couldn't create a temporary directory")?; 327 | 328 | xargo()? 329 | .args(&["init", "-q", "--lib", "--vcs", "none", "--name", "host"]) 330 | .current_dir(td.path()) 331 | .run()?; 332 | 333 | if test { 334 | write( 335 | &td.path().join("src/lib.rs"), 336 | false, 337 | "fn _f(_: Vec) {}", 338 | )?; 339 | } else { 340 | write(&td.path().join("src/lib.rs"), false, "#![no_std]")?; 341 | } 342 | 343 | Ok(HProject { 344 | _guard: guard, 345 | host: host(), 346 | td: td, 347 | }) 348 | } 349 | 350 | fn build(&self, verb: &str) -> Result<()> { 351 | // Calling "run_and_get_stderr" to be less verbose 352 | xargo()?.arg(verb).current_dir(self.td.path()).run_and_get_stderr()?; 353 | Ok(()) 354 | } 355 | 356 | /// Calls `xargo build` and collects STDERR 357 | fn build_and_get_stderr(&self) -> Result { 358 | let mut cmd = xargo()?; 359 | cmd.arg("build"); 360 | 361 | cmd.arg("-v") 362 | .current_dir(self.td.path()) 363 | .run_and_get_stderr() 364 | } 365 | 366 | /// Adds a `Xargo.toml` to the project 367 | fn xargo_toml(&self, toml: &str) -> Result<()> { 368 | write(&self.td.path().join("Xargo.toml"), false, toml) 369 | } 370 | 371 | /// Runs `xargo-check` with the specified subcommand 372 | fn xargo_check_subcommand(&self, subcommand: Option<&str>, target: Option<&str>) -> Result { 373 | let mut cmd = xargo_check()?; 374 | if let Some(subcommand) = subcommand { 375 | cmd.arg(subcommand); 376 | } 377 | if let Some(target) = target { 378 | cmd.args(&["--target", target]); 379 | } 380 | cmd 381 | .current_dir(self.td.path()) 382 | .run_and_get_stderr() 383 | } 384 | 385 | } 386 | 387 | impl Drop for HProject { 388 | fn drop(&mut self) { 389 | hcleanup(&self.host).unwrap() 390 | } 391 | } 392 | 393 | /// Test vanilla `xargo build` 394 | #[test] 395 | fn simple() { 396 | fn run() -> Result<()> { 397 | const TARGET: &'static str = "thumbv6m-simple-eabi"; 398 | 399 | let project = Project::new(TARGET)?; 400 | project.build(TARGET)?; 401 | assert!(exists("core", TARGET)?); 402 | 403 | Ok(()) 404 | } 405 | 406 | run!() 407 | } 408 | 409 | /// Test building a dependency specified as `target.{}.dependencies` in 410 | /// ../Xargo.toml 411 | #[test] 412 | fn target_dependencies() { 413 | fn run() -> Result<()> { 414 | // need this exact target name to get the right gcc flags 415 | const TARGET: &'static str = "thumbv7m-none-eabi"; 416 | const STAGE1: &'static str = "stage1"; 417 | 418 | let td = TempDir::new("xargo").chain_err(|| "couldn't create a temporary directory")?; 419 | let project = Project::new_in(td.path().to_path_buf(), TARGET)?; 420 | 421 | let stage1_path = td.path().join(STAGE1); 422 | 423 | mkdir(stage1_path.as_path())?; 424 | create_simple_project(stage1_path.as_path(), STAGE1, "#![no_std]")?; 425 | write(&td.path().join("Xargo.toml"), false, 426 | r#" 427 | [target.thumbv7m-none-eabi.dependencies.alloc] 428 | 429 | [target.thumbv7m-none-eabi.dependencies.stage1] 430 | stage = 1 431 | path = "stage1" 432 | "#, 433 | )?; 434 | project.build(TARGET)?; 435 | assert!(exists("core", TARGET)?); 436 | assert!(exists("alloc", TARGET)?); 437 | assert!(exists("stage1", TARGET)?); 438 | Ok(()) 439 | } 440 | 441 | run!() 442 | } 443 | 444 | /// Test building a dependency specified as `dependencies` in Xargo.toml 445 | #[test] 446 | fn dependencies() { 447 | fn run() -> Result<()> { 448 | // need this exact target name to get the right gcc flags 449 | const TARGET: &'static str = "thumbv6m-none-eabi"; 450 | 451 | let project = Project::new(TARGET)?; 452 | project.xargo_toml( 453 | r#" 454 | [dependencies.alloc] 455 | "#, 456 | )?; 457 | project.build(TARGET)?; 458 | assert!(exists("core", TARGET)?); 459 | assert!(exists("alloc", TARGET)?); 460 | 461 | Ok(()) 462 | } 463 | 464 | run!() 465 | } 466 | 467 | /// Test that `xargo build` can be executed from outside project dir 468 | #[test] 469 | fn build_outside_project_dir() { 470 | fn run() -> Result<()> { 471 | // need this exact target name to get the right gcc flags 472 | const TARGET: &'static str = "thumbv6m-workdir-eabi"; 473 | 474 | let project = Project::new(TARGET)?; 475 | project.xargo_toml( 476 | r#" 477 | [dependencies.alloc] 478 | "#, 479 | )?; 480 | // This `workdir` is from where `xargo build` is invoked. It is different from the project dir. 481 | let workdir = TempDir::new("xargo_workdir").chain_err(|| "couldn't create a temporary directory")?; 482 | 483 | project.build_from_workdir(TARGET, &workdir.path())?; 484 | 485 | Ok(()) 486 | } 487 | let r = run(); 488 | assert!(r.is_ok()); 489 | } 490 | 491 | /// Test `xargo doc` 492 | #[test] 493 | fn doc() { 494 | fn run() -> Result<()> { 495 | const TARGET: &'static str = "thumbv6m-doc-eabi"; 496 | 497 | let project = Project::new(TARGET)?; 498 | project.doc(TARGET)?; 499 | assert!(exists("core", TARGET)?); 500 | 501 | Ok(()) 502 | } 503 | 504 | run!() 505 | } 506 | 507 | /// Check that calling `xargo build` a second time doesn't rebuild the sysroot 508 | #[test] 509 | fn twice() { 510 | fn run() -> Result<()> { 511 | const TARGET: &'static str = "thumbv6m-twice-eabi"; 512 | 513 | let project = Project::new(TARGET)?; 514 | let stderr = project.build_and_get_stderr(Some(TARGET))?; 515 | 516 | assert!(sysroot_was_built(&stderr, TARGET)); 517 | 518 | let stderr = project.build_and_get_stderr(Some(TARGET))?; 519 | 520 | assert!(!sysroot_was_built(&stderr, TARGET)); 521 | 522 | Ok(()) 523 | } 524 | 525 | run!() 526 | } 527 | 528 | /// Check that if `build.target` is set in `.cargo/config`, that target will be 529 | /// used to build the sysroot 530 | #[test] 531 | fn build_target() { 532 | fn run() -> Result<()> { 533 | const TARGET: &'static str = "thumbv6m-build_target-eabi"; 534 | 535 | let project = Project::new(TARGET)?; 536 | project.config( 537 | r#" 538 | [build] 539 | target = "thumbv6m-build_target-eabi" 540 | "#, 541 | )?; 542 | 543 | let stderr = project.build_and_get_stderr(None)?; 544 | 545 | assert!(sysroot_was_built(&stderr, TARGET)); 546 | 547 | Ok(()) 548 | } 549 | 550 | run!() 551 | } 552 | 553 | /// Check that `--target` overrides `build.target` 554 | #[test] 555 | fn override_build_target() { 556 | fn run() -> Result<()> { 557 | const TARGET: &'static str = "thumbv6m-override_build_target-eabi"; 558 | 559 | let project = Project::new(TARGET)?; 560 | project.config( 561 | r#" 562 | [build] 563 | target = "BAD" 564 | "#, 565 | )?; 566 | 567 | let stderr = project.build_and_get_stderr(Some(TARGET))?; 568 | 569 | assert!(sysroot_was_built(&stderr, TARGET)); 570 | 571 | Ok(()) 572 | } 573 | 574 | run!() 575 | } 576 | 577 | /// We shouldn't rebuild the sysroot if `profile.release.lto` changed 578 | #[test] 579 | fn lto_changed() { 580 | fn run() -> Result<()> { 581 | const TARGET: &'static str = "thumbv6m-lto_changed-eabi"; 582 | 583 | let project = Project::new(TARGET)?; 584 | let stderr = project.build_and_get_stderr(Some(TARGET))?; 585 | 586 | assert!(sysroot_was_built(&stderr, TARGET)); 587 | 588 | project.cargo_toml( 589 | r#" 590 | [profile.release] 591 | lto = true 592 | "#, 593 | )?; 594 | 595 | let stderr = project.build_and_get_stderr(Some(TARGET))?; 596 | 597 | assert!(!sysroot_was_built(&stderr, TARGET)); 598 | 599 | Ok(()) 600 | } 601 | 602 | run!() 603 | } 604 | 605 | /// Modifying RUSTFLAGS should trigger a rebuild of the sysroot 606 | #[test] 607 | fn rustflags_changed() { 608 | fn run() -> Result<()> { 609 | const TARGET: &'static str = "thumbv6m-rustflags_changed-eabi"; 610 | 611 | let project = Project::new(TARGET)?; 612 | let stderr = project.build_and_get_stderr(Some(TARGET))?; 613 | 614 | assert!(sysroot_was_built(&stderr, TARGET)); 615 | 616 | project.config( 617 | r#" 618 | [build] 619 | rustflags = ["--cfg", "xargo"] 620 | "#, 621 | )?; 622 | 623 | let stderr = project.build_and_get_stderr(Some(TARGET))?; 624 | 625 | assert!(sysroot_was_built(&stderr, TARGET)); 626 | 627 | Ok(()) 628 | } 629 | 630 | run!() 631 | } 632 | 633 | /// Check that RUSTFLAGS are passed to all `rustc`s, and that they can contain spaces. 634 | #[test] 635 | fn rustflags() { 636 | fn run() -> Result<()> { 637 | const TARGET: &'static str = "thumbv6m-rustflags-eabi"; 638 | 639 | let project = Project::new(TARGET)?; 640 | 641 | project.config( 642 | r#" 643 | [build] 644 | rustflags = ["--cfg", 'xargo="y e s"'] 645 | "#, 646 | )?; 647 | 648 | let stderr = project.build_and_get_stderr(Some(TARGET))?; 649 | 650 | assert!( 651 | stderr.contains(r#"'xargo="y e s"'"#), 652 | "unexpected stderr:\n{}", stderr 653 | ); 654 | 655 | Ok(()) 656 | } 657 | 658 | run!() 659 | } 660 | 661 | /// Check that `-C panic=abort` is passed to `rustc` when `panic = "abort"` is 662 | /// set in `profile.release` 663 | #[test] 664 | fn panic_abort() { 665 | fn run() -> Result<()> { 666 | const TARGET: &'static str = "thumbv6m-panic_abort-eabi"; 667 | 668 | let project = Project::new(TARGET)?; 669 | 670 | project.cargo_toml( 671 | r#" 672 | [profile.release] 673 | panic = "abort" 674 | "#, 675 | )?; 676 | 677 | let stderr = project.build_and_get_stderr(Some(TARGET))?; 678 | 679 | assert!( 680 | stderr 681 | .lines() 682 | .filter(|l| !l.starts_with("+") && l.contains("--release")) 683 | .all(|l| l.contains("-C") && l.contains("panic=abort")) 684 | ); 685 | 686 | Ok(()) 687 | } 688 | 689 | run!() 690 | } 691 | 692 | /// Check that adding linker arguments doesn't trigger a sysroot rebuild 693 | #[test] 694 | fn link_arg() { 695 | fn run() -> Result<()> { 696 | const TARGET: &'static str = "thumbv6m-link_arg-eabi"; 697 | 698 | let project = Project::new(TARGET)?; 699 | 700 | let stderr = project.build_and_get_stderr(Some(TARGET))?; 701 | 702 | assert!(sysroot_was_built(&stderr, TARGET)); 703 | 704 | project.config( 705 | r#" 706 | [target.__link_arg] 707 | rustflags = ["-C", "link-arg=-lfoo"] 708 | "#, 709 | )?; 710 | 711 | let stderr = project.build_and_get_stderr(Some(TARGET))?; 712 | 713 | assert!(!sysroot_was_built(&stderr, TARGET)); 714 | 715 | Ok(()) 716 | } 717 | 718 | run!() 719 | } 720 | 721 | /// The sysroot should be rebuilt if the target specification changed 722 | #[test] 723 | fn specification_changed() { 724 | fn run() -> Result<()> { 725 | const JSON: &'static str = r#" 726 | { 727 | "arch": "arm", 728 | "data-layout": "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64", 729 | "linker-flavor": "gcc", 730 | "llvm-target": "thumbv6m-none-eabi", 731 | "max-atomic-width": 0, 732 | "os": "none", 733 | "panic-strategy": "abort", 734 | "target-c-int-width": "32", 735 | "target-endian": "little", 736 | "target-pointer-width": "32" 737 | } 738 | "#; 739 | const TARGET: &'static str = "thumbv6m-specification_changed-eabi"; 740 | 741 | let project = Project::new(TARGET)?; 742 | 743 | let stderr = project.build_and_get_stderr(Some(TARGET))?; 744 | 745 | assert!(sysroot_was_built(&stderr, TARGET)); 746 | 747 | write( 748 | &project.td.path().join("thumbv6m-specification_changed-eabi.json"), 749 | false, 750 | JSON, 751 | )?; 752 | 753 | let stderr = project.build_and_get_stderr(Some(TARGET))?; 754 | 755 | assert!(sysroot_was_built(&stderr, TARGET)); 756 | 757 | Ok(()) 758 | } 759 | 760 | run!() 761 | } 762 | 763 | /// The sysroot should NOT be rebuilt if the target specification didn't really 764 | /// changed, e.g. some fields were moved around 765 | #[test] 766 | fn unchanged_specification() { 767 | fn run() -> Result<()> { 768 | const JSON: &'static str = r#" 769 | { 770 | "arch": "arm", 771 | "data-layout": "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64", 772 | "linker-flavor": "gcc", 773 | "llvm-target": "thumbv6m-none-eabi", 774 | "os": "none", 775 | "max-atomic-width": 0, 776 | "target-c-int-width": "32", 777 | "target-endian": "little", 778 | "target-pointer-width": "32" 779 | } 780 | "#; 781 | const TARGET: &'static str = "thumbv6m-unchanged_specification-eabi"; 782 | 783 | let project = Project::new(TARGET)?; 784 | 785 | let stderr = project.build_and_get_stderr(Some(TARGET))?; 786 | 787 | assert!(sysroot_was_built(&stderr, TARGET)); 788 | 789 | write( 790 | &project.td.path().join("thumbv6m-unchanged_specification-eabi.json"), 791 | false, 792 | JSON, 793 | )?; 794 | 795 | let stderr = project.build_and_get_stderr(Some(TARGET))?; 796 | 797 | assert!(!sysroot_was_built(&stderr, TARGET)); 798 | 799 | Ok(()) 800 | } 801 | 802 | run!() 803 | } 804 | 805 | /// Check that a sysroot is built for the host 806 | #[test] 807 | fn host_once() { 808 | fn run() -> Result<()> { 809 | let target = host(); 810 | let project = HProject::new(false)?; 811 | 812 | let stderr = project.build_and_get_stderr()?; 813 | 814 | assert!(sysroot_was_built(&stderr, &target)); 815 | 816 | Ok(()) 817 | } 818 | 819 | run!() 820 | } 821 | 822 | /// Check that the sysroot is not rebuilt when `xargo build` is called a second 823 | /// time 824 | #[test] 825 | fn host_twice() { 826 | fn run() -> Result<()> { 827 | let target = host(); 828 | let project = HProject::new(false)?; 829 | 830 | let stderr = project.build_and_get_stderr()?; 831 | 832 | assert!(sysroot_was_built(&stderr, &target)); 833 | 834 | let stderr = project.build_and_get_stderr()?; 835 | 836 | assert!(!sysroot_was_built(&stderr, &target)); 837 | 838 | Ok(()) 839 | } 840 | 841 | run!() 842 | } 843 | 844 | /// Check we can build+run `xargo test` 845 | #[test] 846 | fn host_libtest() { 847 | fn run() -> Result<()> { 848 | let project = HProject::new(true)?; 849 | 850 | project.xargo_toml( 851 | " 852 | [dependencies.std] 853 | features = [\"panic_unwind\"] 854 | 855 | [dependencies.test] 856 | ", 857 | )?; 858 | 859 | project.build("test") 860 | } 861 | 862 | run!() 863 | } 864 | 865 | /// Check multi stage sysroot builds with `xargo build` 866 | #[test] 867 | fn host_liballoc() { 868 | fn run() -> Result<()> { 869 | let project = HProject::new(false)?; 870 | 871 | project.xargo_toml( 872 | " 873 | [dependencies.core] 874 | stage = 0 875 | 876 | [dependencies.alloc] 877 | stage = 1 878 | ", 879 | )?; 880 | 881 | project.build("build") 882 | } 883 | 884 | run!() 885 | } 886 | 887 | /// Test having a `[patch]` section. 888 | /// The tag in the toml file needs to be updated any time the version of 889 | /// cc used by rustc is updated. 890 | #[test] 891 | fn host_patch() { 892 | fn run() -> Result<()> { 893 | let project = HProject::new(false)?; 894 | project.xargo_toml( 895 | r#" 896 | [dependencies.std] 897 | features = ["panic_unwind"] 898 | 899 | [patch.crates-io.cc] 900 | git = "https://github.com/alexcrichton/cc-rs" 901 | tag = "1.0.47" 902 | "#, 903 | )?; 904 | let stderr = project.build_and_get_stderr()?; 905 | 906 | assert!(stderr 907 | .lines() 908 | .any(|line| line.contains("Compiling cc ") 909 | && line.contains("https://github.com/alexcrichton/cc-rs")), 910 | "Looks like patching did not work. stderr:\n{}", stderr 911 | ); 912 | 913 | Ok(()) 914 | } 915 | 916 | // Only run this on pinned nightlies, to avoid having to update the version number all the time. 917 | let is_pinned = std::env::var("TRAVIS_RUST_VERSION").ok().map_or(false, 918 | |var| var.starts_with("nightly-")); 919 | if is_pinned { 920 | run!() 921 | } 922 | } 923 | 924 | #[test] 925 | fn cargo_check_check() { 926 | fn run() -> Result<()> { 927 | let project = HProject::new(false)?; 928 | project.xargo_check_subcommand(Some("check"), None)?; 929 | 930 | Ok(()) 931 | } 932 | run!() 933 | } 934 | 935 | #[test] 936 | fn cargo_check_check_no_ctoml() { 937 | fn run() -> Result<()> { 938 | const TARGET: &str = "i686-pc-windows-gnu"; 939 | let project = HProject::new(false)?; 940 | // this `cleanup` is required becase `HProject` doesn't clean up the target sysroot on drop 941 | // should be OK as long as other tests don't use the same target 942 | cleanup(TARGET)?; 943 | // Make sure that 'Xargo.toml` exists 944 | project.xargo_toml("")?; 945 | std::fs::remove_file(project.td.path().join("Cargo.toml")) 946 | .chain_err(|| format!("Could not remove Cargo.toml"))?; 947 | 948 | // windows-gnu specifically needs some extra files to be copied for full builds; 949 | // make sure check-builds work without those files. 950 | let stderr = project.xargo_check_subcommand(None, Some(TARGET))?; 951 | assert!(stderr.contains("Checking core"), "Looks like checking did not work. stderr:\n{}", stderr); 952 | 953 | Ok(()) 954 | } 955 | run!() 956 | } 957 | --------------------------------------------------------------------------------