├── .gitignore ├── ci ├── before_deploy.sh ├── script.sh └── install.sh ├── Cargo.toml ├── LICENSE-MIT ├── appveyor.yml ├── .travis.yml ├── tests └── smoke.rs ├── README.md ├── LICENSE-APACHE └── src └── main.rs /.gitignore: -------------------------------------------------------------------------------- 1 | *.bk 2 | Cargo.lock 3 | target 4 | -------------------------------------------------------------------------------- /ci/before_deploy.sh: -------------------------------------------------------------------------------- 1 | # `before_deploy` phase: here we package the build artifacts 2 | 3 | set -ex 4 | 5 | cargo build --target $TARGET --release 6 | 7 | mkdir staging 8 | 9 | cp target/$TARGET/release/cargo-sysroot staging 10 | 11 | cd staging 12 | 13 | tar czf ../${PROJECT_NAME}-${TRAVIS_TAG}-${TARGET}.tar.gz * 14 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Jorge Aparicio "] 3 | license = "MIT/Apache-2.0" 4 | name = "cargo-sysroot" 5 | version = "0.1.0" 6 | 7 | [dependencies] 8 | chrono = "0.2.19" 9 | clap = "2.0.2" 10 | curl = "0.2" 11 | fern = "0.3.5" 12 | flate2 = "0.2" 13 | log = "0.3.5" 14 | rustc_version = "0.1.6" 15 | tar = "0.4" 16 | tempdir = "0.3.4" 17 | toml = "0.1.27" 18 | -------------------------------------------------------------------------------- /ci/script.sh: -------------------------------------------------------------------------------- 1 | # `script` phase: you usually build, test and generate docs in this phase 2 | 3 | set -ex 4 | 5 | case "$TRAVIS_OS_NAME" in 6 | linux) 7 | host=x86_64-unknown-linux-gnu 8 | ;; 9 | osx) 10 | host=x86_64-apple-darwin 11 | ;; 12 | esac 13 | 14 | # NOTE Workaround for rust-lang/rust#31907 - disable doc tests when cross compiling 15 | # This has been fixed in the nightly channel but it would take a while to reach the other channels 16 | if [ "$host" != "$TARGET" ] && [ "$CHANNEL" != "nightly" ]; then 17 | if [ "$TRAVIS_OS_NAME" = "osx" ]; then 18 | brew install gnu-sed --default-names 19 | fi 20 | 21 | find src -name '*.rs' -type f | xargs sed -i -e 's:\(//.\s*```\):\1 ignore,:g' 22 | fi 23 | 24 | cargo build --target $TARGET --verbose 25 | 26 | if [ "$CHANNEL" = "nightly" ]; then 27 | cargo test --target $TARGET 28 | fi 29 | -------------------------------------------------------------------------------- /ci/install.sh: -------------------------------------------------------------------------------- 1 | # `install` phase: install stuff needed for the `script` phase 2 | 3 | set -ex 4 | 5 | # Install multirust 6 | git clone https://github.com/brson/multirust 7 | pushd multirust 8 | ./build.sh 9 | ./install.sh --prefix=~/multirust 10 | multirust default $CHANNEL 11 | rustc -V 12 | cargo -V 13 | popd 14 | 15 | case "$TRAVIS_OS_NAME" in 16 | linux) 17 | host=x86_64-unknown-linux-gnu 18 | ;; 19 | osx) 20 | host=x86_64-apple-darwin 21 | ;; 22 | esac 23 | 24 | # Install standard libraries needed for cross compilation 25 | if [ "$host" != "$TARGET" ]; then 26 | if [ "$CHANNEL" = "nightly" ]; then 27 | multirust add-target nightly $TARGET 28 | else 29 | if [ "$CHANNEL" = "stable" ]; then 30 | # e.g. 1.6.0 31 | version=$(rustc -V | cut -d' ' -f2) 32 | else 33 | version=beta 34 | fi 35 | 36 | tarball=rust-std-${version}-${TARGET} 37 | 38 | curl -Os http://static.rust-lang.org/dist/${tarball}.tar.gz 39 | 40 | tar xzf ${tarball}.tar.gz 41 | 42 | ${tarball}/install.sh --prefix=$(rustc --print sysroot) 43 | 44 | rm -r ${tarball} 45 | rm ${tarball}.tar.gz 46 | fi 47 | fi 48 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | global: 3 | PROJECT_NAME: cargo-sysroot 4 | matrix: 5 | # Stable channel 6 | - TARGET: i686-pc-windows-msvc 7 | CHANNEL: stable 8 | - TARGET: x86_64-pc-windows-msvc 9 | CHANNEL: stable 10 | # Beta channel 11 | - TARGET: i686-pc-windows-msvc 12 | CHANNEL: beta 13 | - TARGET: x86_64-pc-windows-msvc 14 | CHANNEL: beta 15 | # Nightly channel 16 | - TARGET: i686-pc-windows-msvc 17 | CHANNEL: nightly 18 | - TARGET: x86_64-pc-windows-msvc 19 | CHANNEL: nightly 20 | 21 | install: 22 | - ps: Start-FileDownload "https://static.rust-lang.org/dist/channel-rust-stable" 23 | - ps: $env:RUST_VERSION = Get-Content channel-rust-stable | select -first 1 | %{$_.split('-')[1]} 24 | - if NOT "%CHANNEL%" == "stable" set RUST_VERSION=%CHANNEL% 25 | - ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-${env:RUST_VERSION}-${env:TARGET}.exe" 26 | - rust-%RUST_VERSION%-%TARGET%.exe /VERYSILENT /NORESTART /DIR="C:\Program Files (x86)\Rust" 27 | - SET PATH=%PATH%;C:\Program Files (x86)\Rust\bin 28 | - rustc -V 29 | - cargo -V 30 | 31 | build: false 32 | 33 | test_script: 34 | - cargo build --verbose 35 | - if "%CHANNEL%" == "nightly" cargo test 36 | 37 | before_deploy: 38 | - cargo build --release 39 | - mkdir staging 40 | - copy target\release\cargo-sysroot.exe staging 41 | - cd staging 42 | - 7z a ../%PROJECT_NAME%-%APPVEYOR_REPO_TAG_NAME%-%TARGET%.zip * 43 | - appveyor PushArtifact ../%PROJECT_NAME%-%APPVEYOR_REPO_TAG_NAME%-%TARGET%.zip 44 | 45 | deploy: 46 | description: 'Windows release' 47 | artifact: /.*\.zip/ 48 | auth_token: 49 | secure: IUR2A67gxVGUC68HYN3F6QoOEsTqiSMj8AkazZRi+6Xiu9T8sIS55rrDSCpXmHXh 50 | provider: GitHub 51 | on: 52 | CHANNEL: stable 53 | appveyor_repo_tag: true 54 | 55 | branches: 56 | only: 57 | - master 58 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | 3 | language: generic 4 | 5 | env: 6 | global: 7 | - PROJECT_NAME=cargo-sysroot 8 | 9 | matrix: 10 | include: 11 | # Stable channel 12 | - os: osx 13 | env: TARGET=i686-apple-darwin CHANNEL=stable 14 | - os: linux 15 | env: TARGET=i686-unknown-linux-gnu CHANNEL=stable 16 | addons: 17 | apt: 18 | packages: &i686_unknown_linux_gnu 19 | - gcc-multilib 20 | - libssl-dev:i386 21 | - os: osx 22 | env: TARGET=x86_64-apple-darwin CHANNEL=stable 23 | - os: linux 24 | env: TARGET=x86_64-unknown-linux-gnu CHANNEL=stable 25 | # Beta channel 26 | - os: osx 27 | env: TARGET=i686-apple-darwin CHANNEL=beta 28 | - os: linux 29 | env: TARGET=i686-unknown-linux-gnu CHANNEL=beta 30 | addons: 31 | apt: 32 | packages: *i686_unknown_linux_gnu 33 | - os: osx 34 | env: TARGET=x86_64-apple-darwin CHANNEL=beta 35 | - os: linux 36 | env: TARGET=x86_64-unknown-linux-gnu CHANNEL=beta 37 | # Nightly channel 38 | - os: osx 39 | env: TARGET=i686-apple-darwin CHANNEL=nightly 40 | - os: linux 41 | env: TARGET=i686-unknown-linux-gnu CHANNEL=nightly 42 | addons: 43 | apt: 44 | packages: *i686_unknown_linux_gnu 45 | - os: osx 46 | env: TARGET=x86_64-apple-darwin CHANNEL=nightly 47 | - os: linux 48 | env: TARGET=x86_64-unknown-linux-gnu CHANNEL=nightly 49 | 50 | install: 51 | - export PATH="$PATH:~/multirust/bin" 52 | - bash ci/install.sh 53 | 54 | script: 55 | - bash ci/script.sh 56 | 57 | before_deploy: 58 | - bash ci/before_deploy.sh 59 | 60 | deploy: 61 | provider: releases 62 | api_key: 63 | secure: kx8JlEAEMXT7AvrFA/0K3xCNTjblmqyZ14QTySTI5lquuueRWuTceLhow5x3Du4pVdz5zxs8rBg6XpWbWa9bt+hyyosZ7ikG5RKNzFne14KMGuNII2pb8wRGOy1A/vThdibYuSac8d5+6OvzT0F6/om9GU5hJIqVd/vmTEtUMHFmyADu9KXOXd3ePg/lv70punKKWm2NpCh7u78FgVnZDTLM0teEEfZnScW4jSHhoXlxMfq841EFhNeLlZBYdVWhp+bBccBV3v/pESSlQSha0N5D0x7c4Cvvq0YoZj6ZMJq5kAaysWML8BLIoRapKpZkIoEp+QM7D1Lq5GsW1WLwyjCWVBiFzhoFleEylgvYVOFExnKlcUL+hivMOfu1KDkgK8fg+gawNzozmcsPFFVRks/xBo/mXzwbq7/8y77QrlXDcYREtpQAvzuj9aXpZ12pu9CmYOhk4GjA+6yHHRzZnsOwUm9r6FzHDAg3OysCbUNAeKMlsqEOhTgF8henhCqCq+j4sNFJ3Z8GBlnctq7ckqYwpZqSVp30b/FSwuZfrztx1tlZLXU9PQszFAbgRD2w+ENeu7K7v8W45mQuomc/amoL4VhWVdUY0Gm4KP8FG8FA1C9ZJbuOf5vEBi8D82YG1AyvaZgHjXW2+TwNNQduUiSCYPon239V+nEOYeI8kNU= 64 | file: ${PROJECT_NAME}-${TRAVIS_TAG}-${TARGET}.tar.gz 65 | skip_cleanup: true 66 | on: 67 | condition: $CHANNEL = stable 68 | tags: true 69 | 70 | branches: 71 | only: 72 | - master 73 | - /^v\d+\.\d+\.\d+.*$/ 74 | 75 | notifications: 76 | email: 77 | on_success: never 78 | -------------------------------------------------------------------------------- /tests/smoke.rs: -------------------------------------------------------------------------------- 1 | extern crate tempdir; 2 | 3 | use std::fs::File; 4 | use std::io::prelude::*; 5 | use std::path::Path; 6 | use std::process::Command; 7 | use std::{env, fs}; 8 | 9 | use tempdir::TempDir; 10 | 11 | macro_rules! t { 12 | ($e:expr) => (match $e { 13 | Ok(e) => e, 14 | Err(e) => panic!("{} failed with {}", stringify!($e), e), 15 | }) 16 | } 17 | 18 | fn cargo_sysroot() -> Command { 19 | let mut me = t!(env::current_exe()); 20 | me.pop(); 21 | me.push("cargo-sysroot"); 22 | let mut cmd = Command::new(me); 23 | cmd.arg("sysroot"); 24 | return cmd 25 | } 26 | 27 | fn exists_rlib(krate: &str, profile: &str, target: &str, sysroot: &Path) -> bool { 28 | for entry in t!(fs::read_dir(sysroot.join(format!("{}/lib/rustlib/{}/lib", profile, target)))) { 29 | let path = t!(entry).path(); 30 | let filename = path.file_stem().unwrap().to_str().unwrap(); 31 | let extension = path.extension().unwrap().to_str().unwrap(); 32 | 33 | if filename.starts_with(&format!("lib{}", krate)) && extension == "rlib" && path.is_file() { 34 | return true; 35 | } 36 | } 37 | 38 | false 39 | } 40 | 41 | #[test] 42 | fn supported_triple() { 43 | let triple = "arm-unknown-linux-gnueabihf"; 44 | let td = t!(TempDir::new("cargo-sysroot")); 45 | 46 | run(cargo_sysroot().arg("--target") 47 | .arg(triple) 48 | .arg(td.path()) 49 | .arg("--verbose")); 50 | 51 | assert!(exists_rlib("core", "debug", triple, td.path())); 52 | 53 | run(cargo_sysroot().arg("--target") 54 | .arg(triple) 55 | .arg(td.path()) 56 | .arg("--verbose") 57 | .arg("--release")); 58 | 59 | assert!(exists_rlib("core", "debug", triple, td.path())); 60 | assert!(exists_rlib("core", "release", triple, td.path())); 61 | } 62 | 63 | #[test] 64 | fn custom_target() { 65 | let spec = r#" 66 | { 67 | "arch": "arm", 68 | "llvm-target": "thumbv7m-none-eabi", 69 | "os": "none", 70 | "target-endian": "little", 71 | "target-pointer-width": "32", 72 | "archive-format": "gnu" 73 | } 74 | "#; 75 | let td = t!(TempDir::new("cargo-sysroot")); 76 | t!(t!(File::create(td.path().join("custom.json"))).write_all(spec.as_bytes())); 77 | 78 | // test --target triple 79 | run(cargo_sysroot().arg("--target=custom") 80 | .arg(td.path().join("target")) 81 | .arg("--verbose") 82 | .current_dir(td.path())); 83 | 84 | assert!(exists_rlib("core", "debug", "custom", &td.path().join("target"))); 85 | 86 | // test /path/to/target.json 87 | run(cargo_sysroot().arg("--target") 88 | .arg(td.path().join("custom.json")) 89 | .arg(td.path().join("other")) 90 | .arg("--verbose")); 91 | 92 | assert!(exists_rlib("core", "debug", "custom", &td.path().join("other"))); 93 | 94 | // make sure the original spec is there but the copied version is gone 95 | assert!(td.path().join("custom.json").is_file()); 96 | assert!(!td.path().join("other/src/libcore/custom.json").is_file()); 97 | } 98 | 99 | #[test] 100 | fn sysroot_toml() { 101 | let toml = r#" 102 | [target.arm-unknown-linux-gnueabihf] 103 | crates = ["collections"] 104 | "#; 105 | let triple = "arm-unknown-linux-gnueabihf"; 106 | 107 | let td = t!(TempDir::new("cargo-sysroot")); 108 | t!(t!(File::create(td.path().join("sysroot.toml"))).write_all(toml.as_bytes())); 109 | 110 | run(cargo_sysroot().args(&["--target", triple]) 111 | .arg(td.path()) 112 | .arg("--verbose") 113 | .current_dir(td.path())); 114 | 115 | assert!(exists_rlib("core", "debug", triple, td.path())); 116 | assert!(exists_rlib("collections", "debug", triple, td.path())); 117 | } 118 | 119 | fn run(cmd: &mut Command) { 120 | println!("running: {:?}", cmd); 121 | let output = t!(cmd.output()); 122 | if !output.status.success() { 123 | println!("--- stdout:\n{}", String::from_utf8_lossy(&output.stdout)); 124 | println!("--- stderr:\n{}", String::from_utf8_lossy(&output.stderr)); 125 | panic!("expected success, got: {}", output.status); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Status 2 | 3 | This project has been **SUPERSEDED** by [xargo][0]. `xargo` is a transparent Cargo wrapper that 4 | builds and handles a sysroot when you cross compile to custom targets. It's much easier to use 5 | than `cargo-sysroot`, instead of: 6 | 7 | ``` 8 | $ cargo sysroot --target $custom target/sysroot 9 | $ RUSTFLAGS='--sysroot target/sysroot/debug' cargo build --target $custom 10 | ``` 11 | 12 | you simply do: 13 | 14 | ``` 15 | $ xargo build --target $custom 16 | ``` 17 | 18 | Also you can use `xargo` wherever you used `cargo`: `xargo clean`, `xargo fmt`, etc. just work. 19 | 20 | [Check it out][0]. 21 | 22 | -- @japaric 2016/04/10 23 | 24 | [0]: https://github.com/japaric/xargo 25 | 26 | --- 27 | 28 | [![Travis](https://travis-ci.org/japaric/cargo-sysroot.svg?branch=master)](https://travis-ci.org/japaric/cargo-sysroot) 29 | [![Appveyor](https://ci.appveyor.com/api/projects/status/rm0cymdvbu5a89ja/branch/master?svg=true)](https://ci.appveyor.com/project/japaric/cargo-sysroot) 30 | 31 | # `cargo-sysroot` 32 | 33 | > Builds a sysroot with cross compiled standard crates 34 | 35 | ## The problem 36 | 37 | Let's say you are building a `no_std` crate for a custom target, e.g. some Cortex M microcontroller. 38 | You make that crate depend on [`rust-libcore`] to have Cargo cross compile the `core` crate as 39 | part of the build process. `cargo build --target=$triple` works fine, and you get your cross 40 | compiled crate. 41 | 42 | [`rust-libcore`]: https://crates.io/crates/rust-libcore 43 | 44 | Now let's say you want to depend on another `no_std` crate, like [`spin`], so you add it to your 45 | dependencies, and call `cargo build --target=$target`. But you get: 46 | 47 | [`spin`]: https://crates.io/crates/spin/ 48 | 49 | ``` rust 50 | Compiling spin v0.3.5 51 | error: can't find crate for `core` [E0463] 52 | error: aborting due to previous error 53 | ``` 54 | 55 | `rust-libcore` builds a `core` crate that can be used by your crate, but not by `spin`. When Cargo 56 | builds `spin`, it looks for the `core` crate in your Rust installation but there's none in it. 57 | 58 | You have two alternatives: 59 | 60 | You make the `spin` crate depend on `rust-libcore` and things just work. But, IMO, this is a bad 61 | approach because (1) it's "viral", you'll have to make all the crates you want to use depend on 62 | `rust-libcore`, and (2) if your crate depends on any other standard crate then more crates like 63 | `rust-libstd` would need to be created. 64 | 65 | Or you could install the cross compiled `core` crate in your Rust installation path. But, you may 66 | not be able to due to permissions, or may not want to pollute your Rust installation. 67 | 68 | ## The solution 69 | 70 | Enter `--sysroot`, this undocumented `rustc` feature let's you override the library search path. 71 | This means you can create a "sysroot", which is like a minimal Rust installation with only 72 | libraries, populate it with cross compiled crates and instruct `rustc` to use that sysroot instead 73 | of the default Rust installation. 74 | 75 | And `crate sysroot` does this for you. It takes cares of creating a sysroot with cross compiled 76 | standard crates: 77 | 78 | **NOTE** `cargo-sysroot` only works with nightly newer than 2016-02-28. 79 | 80 | ``` rust 81 | # install the cargo sysroot subcommand 82 | $ cargo install --git https://github.com/japaric/cargo-sysroot 83 | 84 | # create a sysroot for $target in the directory target/sysroot 85 | $ cargo sysroot --target $target target/sysroot 86 | INFO: fetching source tarball 87 | INFO: unpacking source tarball 88 | INFO: creating .commit-hash file 89 | INFO: symlinking host crates 90 | INFO: no sysroot.toml found, using default configuration 91 | INFO: will build the following crates: ["core"] 92 | INFO: building the target crates 93 | Compiling core v0.0.0 (file://...) 94 | INFO: copy the core crate to the sysroot 95 | 96 | # check the sysroot 97 | $ tree target/sysroot/debug 98 | target/sysroot/debug 99 | ├── lib 100 | │   └── rustlib 101 | │   ├── $target 102 | │   │   └── lib 103 | │   │   └── libcore.rlib 104 | │   └── $host -> $RUST_INSTALLATION/lib/rustlib/$host 105 | └── src 106 | ├── libcore 107 | │   │── lib.rs 108 | │   └── (...) 109 | ├── libstd 110 | │   │── lib.rs 111 | │   └── (...) 112 | └── (...) 113 | ``` 114 | 115 | With the sysroot in place you can use (*) the `RUSTFLAGS` env variable to make Cargo use the 116 | sysroot: 117 | 118 | (*) Support for `RUSTFLAGS` has not yet landed. See [rust-lang/cargo#2241]. 119 | 120 | [rust-lang/cargo#2241]: https://github.com/rust-lang/cargo/pull/2241 121 | 122 | ``` 123 | $ RUSTFLAGS='--sysroot target/sysroot/debug' cargo build --target $triple 124 | Compiling spin v0.3.5 125 | Compiling $crate v0.1.0 (file://...) 126 | ``` 127 | 128 | ## Building more than just the `core` crate 129 | 130 | By default, `cargo-sysroot` only builds the `core` crate. But you can ask `cargo-sysroot` to compile 131 | other crates using a `sysroot.toml` manifest. The syntax is like this: 132 | 133 | ``` 134 | [target.$target] 135 | crates = ["$crate1", "$crate2", ...] 136 | ``` 137 | 138 | For example you can compile the collections crate for a "bare metal" target: 139 | 140 | ``` 141 | $ cat sysroot.toml 142 | [target.thumbv7m-none-eabi] 143 | crates = ["collections"] 144 | 145 | $ cargo sysroot --target thumbv7m-none-eabi.json sysroot 146 | INFO: source up to date 147 | INFO: symlinking host crates 148 | INFO: will build the following crates: ["collections", "core"] 149 | INFO: copy target specification file 150 | INFO: building the target crates 151 | Compiling core v0.0.0 (file:///tmp/sysroot.Aq4Lamrp2F7K/sysroot) 152 | Compiling rustc_unicode v0.0.0 (file:///tmp/sysroot.Aq4Lamrp2F7K/sysroot) 153 | Compiling alloc v0.0.0 (file:///tmp/sysroot.Aq4Lamrp2F7K/sysroot) 154 | Compiling collections v0.0.0 (file:///tmp/sysroot.Aq4Lamrp2F7K/sysroot) 155 | Compiling sysroot v0.1.0 (file:///tmp/sysroot.Aq4Lamrp2F7K/sysroot) 156 | INFO: copy the target crates to the sysroot 157 | ``` 158 | 159 | Cross compiling crates that depend on C libraries like jemalloc have yet to be implemented. 160 | 161 | ## Future 162 | 163 | Right now `cargo sysroot` only cross compiles the `core` crate, but in the future it will be able 164 | to compile any standard crate. This would make it easier to target systems where the Rust team 165 | doesn't [provide] cross compiled standard crates. Rust's [new Cargo-based build system] will make 166 | this easier to implement. (This has been implemented!) 167 | 168 | [provide]: http://static.rust-lang.org/dist/ 169 | [new Cargo-based build system]: https://github.com/rust-lang/rust/pull/31123 170 | 171 | Once the standard crates become build-able with Cargo, I expect that they'll start exposing Cargo 172 | features to allow customization. I'm envisioning having `cargo sysroot` look at a `sysroot.toml` 173 | file that specifies the Cargo features each standard crate will be compiled with. Something like 174 | this: 175 | 176 | ``` toml 177 | # Customize the std crate for the mips-musl target 178 | [target.mips-unknown-linux-musl.std] 179 | # disables both backtraces, and statically linked jemalloc 180 | default-features = false 181 | # enables dynamically linked jemalloc 182 | features = ["dynamic-jemalloc"] 183 | ``` 184 | 185 | `cargo-sysroot` will also need to cross compile C dependencies like `compiler-rt` and `jemalloc`. 186 | The `sysroot.toml` could also expose keys that specify which cross compiler tools to use: 187 | 188 | ``` toml 189 | [mips-unknown-linux-musl.build] 190 | ar = "mips-openwrt-linux-ar" 191 | gcc = "mips-openwrt-linux-gcc" 192 | ranlib = "mips-openwrt-linux-ranlib" 193 | ``` 194 | 195 | Because `cargo-sysroot` is not coupled to the `cargo build` process, it would be possible to build 196 | the sysroot using the nightly channel, and then use the sysroot with the stable channel. 197 | `cargo-sysroot` would need to grow an option to build an specific checkout of Rust source, right now 198 | it always checkout the source at the commit-hash provided by `rustc -Vv`. 199 | 200 | I'd like to better integrate `cargo-sysroot` with other tools. I know that `multirust` [plans] to 201 | provide a command to fetch Rust source code, `cargo-sysroot` could use the source code fetched by 202 | `multirust` instead of fetching the code itself. And I'd like `cargo-sysroot` to become more 203 | "transparent", instead of having to set an environment variable, I'd like to integrate it with Cargo 204 | via `.cargo/config` to make Cargo pass `--sysroot` to `rustc` automatically, and even pick a 205 | different sysroot based on the profile (debug vs release). 206 | 207 | [plans]: https://github.com/brson/multirust/issues/77 208 | 209 | ## License 210 | 211 | Licensed under either of 212 | 213 | - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or 214 | http://www.apache.org/licenses/LICENSE-2.0) 215 | - MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 216 | 217 | at your option. 218 | 219 | ### Contribution 220 | 221 | Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the 222 | work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any 223 | additional terms or conditions. 224 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #![deny(warnings)] 2 | #![cfg_attr(clippy, allow(block_in_if_condition_stmt))] 3 | 4 | extern crate chrono; 5 | extern crate clap; 6 | extern crate curl; 7 | extern crate fern; 8 | extern crate flate2; 9 | extern crate rustc_version; 10 | extern crate tar; 11 | extern crate tempdir; 12 | extern crate toml; 13 | 14 | #[macro_use] 15 | extern crate log; 16 | 17 | use std::env; 18 | use std::fs::{self, File, OpenOptions}; 19 | use std::io::{Read, Write}; 20 | use std::path::{Path, PathBuf}; 21 | use std::process::Command; 22 | 23 | use chrono::NaiveDate; 24 | use clap::{App, AppSettings, Arg, SubCommand}; 25 | use curl::http; 26 | use rustc_version::Channel; 27 | 28 | // TODO proper error reporting 29 | macro_rules! try { 30 | ($e:expr) => { 31 | $e.unwrap_or_else(|e| panic!("{} with {}", stringify!($e), e)) 32 | } 33 | } 34 | 35 | enum Target<'a> { 36 | /// `path/to/some/target.json` 37 | Spec(&'a Path), 38 | /// `arm-unknown-linux-gnueabihf` 39 | Triple(&'a str), 40 | } 41 | 42 | struct Context<'a> { 43 | commit_date: NaiveDate, 44 | commit_hash: &'a str, 45 | host: &'a str, 46 | out_dir: &'a Path, 47 | profile: &'static str, 48 | target: Target<'a>, 49 | verbose: bool, 50 | } 51 | 52 | struct Config { 53 | table: Option, 54 | } 55 | 56 | impl Config { 57 | /// Parses `sysroot.toml` 58 | fn parse() -> Config { 59 | let path = Path::new("sysroot.toml"); 60 | 61 | let table = if path.exists() { 62 | let toml = &mut String::new(); 63 | 64 | try!(try!(File::open(path)).read_to_string(toml)); 65 | 66 | toml::Parser::new(toml).parse().map(toml::Value::Table) 67 | } else { 68 | info!("no sysroot.toml found, using default configuration"); 69 | 70 | None 71 | }; 72 | 73 | Config { table: table } 74 | } 75 | 76 | /// Crates to build for this target, returns `["core"]` if not specified 77 | fn crates(&self, target: &str) -> Vec { 78 | let key = &format!("target.{}.crates", target); 79 | 80 | let mut crates = self.table 81 | .as_ref() 82 | .and_then(|t| t.lookup(key)) 83 | .and_then(|v| v.as_slice()) 84 | .and_then(|vs| { 85 | vs.iter().map(|v| v.as_str().map(|s| s.to_owned())).collect() 86 | }) 87 | .unwrap_or_else(|| vec!["core".to_owned()]); 88 | 89 | crates.push("core".to_owned()); 90 | crates.sort(); 91 | crates.dedup(); 92 | crates 93 | } 94 | } 95 | 96 | fn main() { 97 | let rustc_version::VersionMeta { ref host, ref commit_date, ref commit_hash, ref channel, .. } = 98 | rustc_version::version_meta(); 99 | let commit_hash = commit_hash.as_ref().unwrap(); 100 | 101 | match *channel { 102 | Channel::Nightly => {} 103 | _ => panic!("only the nightly channel is supported at this time (see issue #5)"), 104 | } 105 | 106 | let matches = &App::new("cargo-sysroot") 107 | .bin_name("cargo") 108 | .settings(&[AppSettings::SubcommandRequired]) 109 | .subcommand(SubCommand::with_name("sysroot") 110 | .about("Builds a sysroot with cross compiled standard \ 111 | crates") 112 | .arg(Arg::with_name("triple") 113 | .help("Target triple to compile for") 114 | .long("target") 115 | .required(true) 116 | .takes_value(true)) 117 | .arg(Arg::with_name("release") 118 | .help("Build artifacts in release mode, with \ 119 | optimizations") 120 | .long("release")) 121 | .arg(Arg::with_name("out_dir") 122 | .help("Output directory") 123 | .required(true)) 124 | .arg(Arg::with_name("verbose") 125 | .help("Verbose cargo builds") 126 | .long("verbose")) 127 | .author("Jorge Aparicio ")) 128 | .version(env!("CARGO_PKG_VERSION")) 129 | .get_matches(); 130 | 131 | if let Some(matches) = matches.subcommand_matches("sysroot") { 132 | if let (Some(target), Some(out_dir)) = (matches.value_of("triple"), 133 | matches.value_of("out_dir")) { 134 | if host == target { 135 | panic!("`cargo sysroot` for host has not been implement yet"); 136 | } 137 | 138 | let target = if target.ends_with("json") { 139 | Target::Spec(Path::new(target)) 140 | } else { 141 | Target::Triple(target) 142 | }; 143 | 144 | let ctx = &Context { 145 | commit_date: NaiveDate::parse_from_str(commit_date.as_ref().unwrap(), "%Y-%m-%d") 146 | .unwrap(), 147 | commit_hash: commit_hash, 148 | host: host, 149 | out_dir: Path::new(out_dir), 150 | profile: if matches.is_present("release") { 151 | "release" 152 | } else { 153 | "debug" 154 | }, 155 | target: target, 156 | verbose: matches.is_present("verbose"), 157 | }; 158 | 159 | init_logger(); 160 | fetch_source(ctx); 161 | symlink_host_crates(ctx); 162 | build_target_crates(ctx); 163 | } 164 | } 165 | } 166 | 167 | fn init_logger() { 168 | let config = fern::DispatchConfig { 169 | format: Box::new(|msg, level, _| format!("{}: {}", level, msg)), 170 | output: vec![fern::OutputConfig::stderr()], 171 | level: log::LogLevelFilter::Trace, 172 | }; 173 | 174 | try!(fern::init_global_logger(config, log::LogLevelFilter::Trace)); 175 | } 176 | 177 | // TODO Ultimately, we want to use `multirust fetch-source` for 100% correctness 178 | fn fetch_source(ctx: &Context) { 179 | // XXX There doesn't seem to be way to get the _nightly_ date from the output of `rustc -Vv` 180 | // So we _assume_ the nightly day is the day after the commit-date found in `rustc -Vv` which 181 | // seems to be the common case, but it could be wrong and we'll end up building unusable crates 182 | let date = ctx.commit_date.succ(); 183 | let hash = ctx.commit_hash; 184 | let src_dir = &ctx.out_dir.join("src"); 185 | let hash_file = &src_dir.join(".commit-hash"); 186 | 187 | if hash_file.exists() { 188 | let old_hash = &mut String::with_capacity(hash.len()); 189 | try!(try!(File::open(hash_file)).read_to_string(old_hash)); 190 | 191 | if old_hash == hash { 192 | info!("source up to date"); 193 | return; 194 | } 195 | } 196 | 197 | if src_dir.exists() { 198 | info!("purging the src directory"); 199 | try!(fs::remove_dir_all(src_dir)); 200 | } 201 | 202 | try!(fs::create_dir_all(src_dir)); 203 | 204 | info!("fetching source tarball"); 205 | let handle = http::Handle::new(); 206 | let url = format!("http://static.rust-lang.org/dist/{}/rustc-nightly-src.tar.gz", 207 | date.format("%Y-%m-%d")); 208 | let resp = try!(handle.timeout(300_000).get(&url[..]).follow_redirects(true).exec()); 209 | 210 | assert_eq!(resp.get_code(), 200); 211 | 212 | info!("unpacking source tarball"); 213 | let decoder = try!(flate2::read::GzDecoder::new(resp.get_body())); 214 | let mut archive = tar::Archive::new(decoder); 215 | for entry in try!(archive.entries()) { 216 | let mut entry = try!(entry); 217 | let path = { 218 | let path = try!(entry.path()); 219 | let mut components = path.components(); 220 | components.next(); // skip rust-lang-rust-<...> 221 | let next = components.next().and_then(|s| s.as_os_str().to_str()); 222 | if next != Some("src") { 223 | continue; 224 | } 225 | components.as_path().to_path_buf() 226 | }; 227 | try!(entry.unpack(&src_dir.join(path))); 228 | } 229 | 230 | info!("creating .commit-hash file"); 231 | try!(try!(File::create(hash_file)).write_all(hash.as_bytes())); 232 | } 233 | 234 | fn symlink_host_crates(ctx: &Context) { 235 | info!("symlinking host crates"); 236 | 237 | let sys_root = try!(String::from_utf8(try!(Command::new("rustc") 238 | .args(&["--print", "sysroot"]) 239 | .output()) 240 | .stdout)); 241 | let src = &Path::new(sys_root.trim_right()).join(format!("lib/rustlib/{}", ctx.host)); 242 | let rustlib_dir = &ctx.out_dir.join(format!("{}/lib/rustlib", ctx.profile)); 243 | let dst = &rustlib_dir.join(ctx.host); 244 | 245 | try!(fs::create_dir_all(rustlib_dir)); 246 | 247 | if dst.exists() { 248 | try!(fs::remove_dir_all(dst)); 249 | } 250 | 251 | link_dirs(&src, &dst); 252 | } 253 | 254 | fn link_dirs(src: &Path, dst: &Path) { 255 | try!(fs::create_dir(&dst)); 256 | 257 | for entry in try!(src.read_dir()) { 258 | let entry = try!(entry); 259 | let src = entry.path(); 260 | let dst = dst.join(entry.file_name()); 261 | if try!(entry.file_type()).is_dir() { 262 | link_dirs(&src, &dst); 263 | } else { 264 | try!(fs::hard_link(&src, &dst).or_else(|_| fs::copy(&src, &dst).map(|_| ()))); 265 | } 266 | } 267 | } 268 | 269 | fn build_target_crates(ctx: &Context) { 270 | const LIB_RS: &'static [u8] = b"#![no_std]"; 271 | 272 | let config = &Config::parse(); 273 | 274 | let temp_dir = try!(tempdir::TempDir::new("sysroot")); 275 | let temp_dir = temp_dir.path(); 276 | 277 | // Create Cargo project 278 | let cargo = &mut Command::new("cargo"); 279 | cargo.current_dir(temp_dir); 280 | cargo.args(&["new", "--vcs", "none"]); 281 | if ctx.verbose { 282 | cargo.arg("--verbose"); 283 | } 284 | assert!(try!(cargo.arg("sysroot").status()).success()); 285 | 286 | let cargo_dir = &temp_dir.join("sysroot"); 287 | let src_dir = &env::current_dir().unwrap().join(ctx.out_dir).join("src"); 288 | 289 | let (ref triple, ref spec_file): (String, _) = match ctx.target { 290 | Target::Spec(path) => { 291 | let path = try!(fs::canonicalize(path)); 292 | let triple = path.file_stem().unwrap().to_str().unwrap().into(); 293 | 294 | (triple, path) 295 | } 296 | Target::Triple(triple) => (triple.into(), PathBuf::from(format!("{}.json", triple))), 297 | }; 298 | 299 | // Add crates to build as dependencies 300 | let toml = &mut try!(OpenOptions::new() 301 | .write(true) 302 | .append(true) 303 | .open(cargo_dir.join("Cargo.toml"))); 304 | let crates = &config.crates(triple); 305 | info!("will build the following crates: {:?}", crates); 306 | for ref krate in crates { 307 | try!(writeln!(toml, 308 | "{} = {{ path = '{}' }}", 309 | krate, 310 | src_dir.join(format!("lib{}", krate)).display())) 311 | } 312 | 313 | { 314 | let toml = &mut String::new(); 315 | try!(try!(File::open(cargo_dir.join("Cargo.toml"))).read_to_string(toml)); 316 | debug!("sysroot's Cargo.toml: {}", toml); 317 | } 318 | 319 | // Rewrite lib.rs to only depend on libcore 320 | try!(try!(OpenOptions::new().write(true).truncate(true).open(cargo_dir.join("src/lib.rs"))) 321 | .write_all(LIB_RS)); 322 | 323 | if spec_file.exists() { 324 | info!("copy target specification file"); 325 | try!(fs::copy(spec_file, cargo_dir.join(format!("{}.json", triple)))); 326 | } 327 | 328 | info!("building the target crates"); 329 | let cargo = &mut Command::new("cargo"); 330 | cargo.current_dir(cargo_dir); 331 | cargo.args(&["build", "--target", triple]); 332 | if ctx.profile == "release" { 333 | cargo.arg("--release"); 334 | } 335 | if ctx.verbose { 336 | cargo.arg("--verbose"); 337 | } 338 | assert!(try!(cargo.status()).success()); 339 | 340 | info!("copy the target crates to the sysroot"); 341 | let libdir = &ctx.out_dir.join(format!("{}/lib/rustlib/{}/lib", ctx.profile, triple)); 342 | let deps_dir = &cargo_dir.join(format!("target/{}/{}/deps", triple, ctx.profile)); 343 | 344 | try!(fs::create_dir_all(libdir)); 345 | for entry in try!(fs::read_dir(deps_dir)) { 346 | let entry = try!(entry); 347 | 348 | try!(fs::copy(entry.path(), libdir.join(entry.file_name()))); 349 | } 350 | } 351 | --------------------------------------------------------------------------------