diff --git a/.travis.yml b/.travis.yml index 6f60e681b..ebb9581a0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -45,6 +45,11 @@ matrix: - env: TARGET=thumbv7em-none-eabi RUN=1 - env: TARGET=thumbv7em-none-eabihf RUN=1 - env: TARGET=thumbv7m-none-eabi RUN=1 + + # Other targets + - env: TARGET=asmjs-unknown-emscripten STD=1 + - env: TARGET=wasm32-unknown-emscripten STD=1 + env: TARGET=x86_64-unknown-linux-gnu CPP=1 DYLIB=1 OPENSSL=0.5.5 RUN=1 DEPLOY=1 install: diff --git a/CHANGELOG.md b/CHANGELOG.md index a831dabbb..5ac0904d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +### Added + +- Support for emscripten targets. + ## [v0.1.9] - 2017-02-08 ### Added diff --git a/README.md b/README.md index 8325f2763..428c6c304 100644 --- a/README.md +++ b/README.md @@ -161,6 +161,7 @@ worst, "hang" (never terminate). | `arm-unknown-linux-musleabi` | 1.1.15 | 5.3.1 | N/A | | 2.8.0 | ✓ | | `armv7-unknown-linux-gnueabihf` | 2.15 | 4.6.2 | 1.0.2j | ✓ | 2.8.0 | ✓ | | `armv7-unknown-linux-musleabihf` | 1.1.15 | 5.3.1 | N/A | | 2.8.0 | ✓ | +| `asmjs-unknown-emscripten` | N/A | N/A | N/A | | N/A | | | `i686-unknown-freebsd` [1] | 10.2 | 5.3.0 | 1.0.2j | | N/A | | | `i686-unknown-linux-gnu` | 2.15 | 4.6.2 | 1.0.2j | ✓ | N/A | ✓ | | `i686-unknown-linux-musl` | 1.1.15 | 5.3.1 | N/A | | N/A | ✓ | @@ -177,6 +178,7 @@ worst, "hang" (never terminate). | `thumbv7em-none-eabi` [3] | 2.2.0 | 5.3.1 | N/A | | N/A | | | `thumbv7em-none-eabihf` [3] | 2.2.0 | 5.3.1 | N/A | | N/A | | | `thumbv7m-none-eabi` [3] | 2.2.0 | 5.3.1 | N/A | | N/A | | +| `wasm32-unknown-emscripten` | N/A | N/A | N/A | | N/A | | | `x86_64-pc-windows-gnu`[1] | N/A | 5.3.1 | | ✓ | N/A | | | `x86_64-unknown-dragonfly` [1] [2] | 4.6.0 | 5.3.0 | 1.0.2j | | N/A | ✓ | | `x86_64-unknown-freebsd` [1] | 10.2 | 5.3.0 | 1.0.2j | | N/A | | diff --git a/ci/script.sh b/ci/script.sh index a39dafc39..224f6d400 100644 --- a/ci/script.sh +++ b/ci/script.sh @@ -15,7 +15,7 @@ main() { export QEMU_STRACE=1 - # `cross run` test for thumb targets + # `cross run` test for thumb and emscripten targets case $TARGET in thumb*-none-eabi*) td=$(mktemp -d) @@ -30,7 +30,20 @@ main() { popd rm -rf $td - ;; + ;; + *-emscripten) + td=$(mktemp -d) + + cargo init --bin --name hello $td + + pushd $td + cross build --target $TARGET + popd + + rm -rf $td + + return + ;; esac # `cross build` test for targets where `std` is not available diff --git a/docker/asmjs-unknown-emscripten/Dockerfile b/docker/asmjs-unknown-emscripten/Dockerfile new file mode 100644 index 000000000..471472d5b --- /dev/null +++ b/docker/asmjs-unknown-emscripten/Dockerfile @@ -0,0 +1,19 @@ +FROM ubuntu:16.04 + +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + ca-certificates \ + cmake \ + gcc \ + libc6-dev \ + make \ + pkg-config + +COPY emscripten.sh / +RUN bash /emscripten.sh 1.37.2 + +RUN apt-get install --no-install-recommends -y \ + nodejs \ + python + +ENV NODE=/usr/bin/nodejs diff --git a/docker/binaryen.sh b/docker/binaryen.sh new file mode 100644 index 000000000..cbee79ff6 --- /dev/null +++ b/docker/binaryen.sh @@ -0,0 +1,45 @@ +set -ex + +main() { + local version=$1 + + local dependencies=( + ca-certificates + cmake + curl + g++ + ninja-build + ) + + apt-get update + local purge_list=() + for dep in ${dependencies[@]}; do + dpkg -L $dep || ( + apt-get install --no-install-recommends -y $dep && + purge_list+=( $dep ) + ) + done + + local td=$(mktemp -d) + + curl -L https://github.com/WebAssembly/binaryen/archive/$version.tar.gz | \ + tar -C $td --strip-components=1 -xz + + pushd $td + cmake -G Ninja + nice ninja + + mkdir /binaryen + cp -r bin lib src /binaryen + cp -r src/js /binaryen/src + + # Cleanup + popd + + apt-get purge --auto-remove -y ${purge_list[@]} + + rm -rf $td + rm $0 +} + +main "${@}" diff --git a/docker/emscripten.sh b/docker/emscripten.sh new file mode 100644 index 000000000..028356b0c --- /dev/null +++ b/docker/emscripten.sh @@ -0,0 +1,67 @@ +set -ex + +main() { + local version=$1 + + local dependencies=( + ca-certificates + cmake + curl + g++ + ninja-build + python + ) + + apt-get update + local purge_list=() + for dep in ${dependencies[@]}; do + dpkg -L $dep || ( + apt-get install --no-install-recommends -y $dep && + purge_list+=( $dep ) + ) + done + + local td=$(mktemp -d) + + mkdir $td/{build,fastcomp} + + curl -L https://github.com/kripken/emscripten-fastcomp/archive/$version.tar.gz | + tar --strip-components=1 -C $td/fastcomp -xz + + mkdir $td/fastcomp/tools/clang + curl -L https://github.com/kripken/emscripten-fastcomp-clang/archive/$version.tar.gz | + tar --strip-components=1 -C $td/fastcomp/tools/clang -xz + + pushd $td + cmake \ + -G Ninja \ + -DCLANG_INCLUDE_TESTS=OFF \ + -DCMAKE_BUILD_TYPE=Release \ + -DLLVM_INCLUDE_EXAMPLES=OFF \ + -DLLVM_INCLUDE_TESTS=OFF \ + -DLLVM_TARGETS_TO_BUILD="X86;JSBackend" \ + $td/fastcomp + + nice ninja + ninja install + + mkdir /emscripten + curl -L https://github.com/kripken/emscripten/archive/$version.tar.gz | + tar --strip-components=1 -C /emscripten -xz + + # TODO build tools/optimizer. I have no idea if `rustc` calls `emcc` in + # a way that makes uses of that optimizer though. + + # Put `emcc` in `$PATH` + ln -s /emscripten/emcc /usr/local/bin + + # Cleanup + popd + + apt-get purge --auto-remove -y ${purge_list[@]} + + rm -rf $td + rm $0 +} + +main "${@}" diff --git a/docker/wasm32-unknown-emscripten/Dockerfile b/docker/wasm32-unknown-emscripten/Dockerfile new file mode 100644 index 000000000..57bf91a10 --- /dev/null +++ b/docker/wasm32-unknown-emscripten/Dockerfile @@ -0,0 +1,23 @@ +FROM ubuntu:16.04 + +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + ca-certificates \ + cmake \ + gcc \ + libc6-dev \ + make \ + pkg-config + +COPY emscripten.sh / +RUN bash /emscripten.sh 1.37.2 + +RUN apt-get install --no-install-recommends -y \ + nodejs \ + python + +COPY binaryen.sh / +RUN bash /binaryen.sh version_27 + +ENV BINARYEN=/binaryen \ + NODE=/usr/bin/nodejs diff --git a/src/docker.rs b/src/docker.rs index 990082455..8e97a5217 100644 --- a/src/docker.rs +++ b/src/docker.rs @@ -33,7 +33,8 @@ pub fn run(target: &Target, verbose: bool) -> Result { let root = root.path(); - let home_dir = env::home_dir().ok_or_else(|| "couldn't get home directory. Is $HOME not set?")?; + let home_dir = env::home_dir() + .ok_or_else(|| "couldn't get home directory. Is $HOME not set?")?; let cargo_dir = env::var_os("CARGO_HOME") .map(PathBuf::from) .unwrap_or_else(|| home_dir.join(".cargo")); @@ -48,12 +49,12 @@ pub fn run(target: &Target, fs::create_dir(&cargo_dir).ok(); fs::create_dir(&xargo_dir).ok(); - let mut cmd = if uses_xargo { + let mut cargo = if uses_xargo { Command::new("xargo") } else { Command::new("cargo") }; - cmd.args(args); + cargo.args(args); // We create/regenerate the lockfile on the host system because the Docker // container doesn't have write access to the root of the Cargo project @@ -66,29 +67,61 @@ pub fn run(target: &Target, let mut docker = Command::new("docker"); + docker.arg("run"); + + let must_run_emcc_first = if target.is_emscripten() { + let temp_dir = env::temp_dir(); + let cache_dir = temp_dir.join(format!("cross-{}", target.triple())); + + if !cache_dir.exists() { + fs::create_dir(&cache_dir).chain_err(|| { + format!("couldn't create a directory in {}", + temp_dir.display()) + })?; + } + + docker.args(&["-v", &format!("{}:{}", cache_dir.display(), "/tmp")]); + + !cache_dir.join(".emscripten").exists() + } else { + false + }; + docker - .arg("run") .arg("--rm") .args(&["--user", &format!("{}:{}", id::user(), id::group())]) .args(&["-e", "CARGO_HOME=/cargo"]) .args(&["-e", "CARGO_TARGET_DIR=/target"]) + .args(&["-e", "HOME=/tmp"]) .args(&["-e", &format!("USER={}", id::username())]); if let Some(strace) = env::var("QEMU_STRACE").ok() { docker.args(&["-e", &format!("QEMU_STRACE={}", strace)]); } - docker - .args(&["-e", "XARGO_HOME=/xargo"]) + docker.args(&["-e", "XARGO_HOME=/xargo"]) .args(&["-v", &format!("{}:/xargo", xargo_dir.display())]) .args(&["-v", &format!("{}:/cargo", cargo_dir.display())]) .args(&["-v", &format!("{}:/project:ro", root.display())]) - .args(&["-v", &format!("{}:/rust:ro", rustc::sysroot(verbose)?.display())]) + .args(&["-v", + &format!("{}:/rust:ro", rustc::sysroot(verbose)?.display())]) .args(&["-v", &format!("{}:/target", target_dir.display())]) .args(&["-w", "/project"]) - .args(&["-it", &image(toml, target)?]) - .args(&["sh", "-c", &format!("PATH=$PATH:/rust/bin {:?}", cmd)]) - .run_and_get_status(verbose) + .args(&["-it", &image(toml, target)?]); + + if must_run_emcc_first { + docker.args(&["sh", + "-c", + &format!("emcc 2>/dev/null; \ + PATH=$PATH:/rust/bin {:?}", + cargo)]); + } else { + docker.args(&["sh", + "-c", + &format!("PATH=$PATH:/rust/bin {:?}", cargo)]); + } + + docker.run_and_get_status(verbose) } fn image(toml: Option<&Toml>, target: &Target) -> Result { diff --git a/src/main.rs b/src/main.rs index 85bb41623..4109e7da9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -116,6 +116,10 @@ pub enum Target { Thumbv7emNoneEabi, Thumbv7emNoneEabihf, Thumbv7mNoneEabi, + + // Emscripten + AsmjsUnknownEmscripten, + Wasm32UnknownEmscripten, } impl Target { @@ -177,9 +181,17 @@ impl Target { } } + fn is_emscripten(&self) -> bool { + match *self { + Target::AsmjsUnknownEmscripten | + Target::Wasm32UnknownEmscripten => true, + _ => false, + } + } + fn needs_docker(&self) -> bool { self.is_linux() || self.is_bare_metal() || self.is_bsd() || - !self.is_builtin() || self.is_windows() + !self.is_builtin() || self.is_windows() || self.is_emscripten() } fn needs_qemu(&self) -> bool { @@ -211,6 +223,7 @@ impl Target { ArmUnknownLinuxMusleabi => "arm-unknown-linux-musleabi", Armv7UnknownLinuxGnueabihf => "armv7-unknown-linux-gnueabihf", Armv7UnknownLinuxMusleabihf => "armv7-unknown-linux-musleabihf", + AsmjsUnknownEmscripten => "asmjs-unknown-emscripten", I686AppleDarwin => "i686-apple-darwin", I686UnknownFreebsd => "i686-unknown-freebsd", I686UnknownLinuxGnu => "i686-unknown-linux-gnu", @@ -228,6 +241,7 @@ impl Target { Thumbv7emNoneEabi => "thumbv7em-none-eabi", Thumbv7emNoneEabihf => "thumbv7em-none-eabihf", Thumbv7mNoneEabi => "thumbv7m-none-eabi", + Wasm32UnknownEmscripten => "wasm32-unknown-emscripten", X86_64AppleDarwin => "x86_64-apple-darwin", X86_64PcWindowsGnu => "x86_64-pc-windows-gnu", X86_64UnknownDragonfly => "x86_64-unknown-dragonfly", @@ -253,6 +267,7 @@ impl Target { "arm-unknown-linux-musleabi" => ArmUnknownLinuxMusleabi, "armv7-unknown-linux-gnueabihf" => Armv7UnknownLinuxGnueabihf, "armv7-unknown-linux-musleabihf" => Armv7UnknownLinuxMusleabihf, + "asmjs-unknown-emscripten" => AsmjsUnknownEmscripten, "i686-apple-darwin" => I686AppleDarwin, "i686-unknown-freebsd" => I686UnknownFreebsd, "i686-unknown-linux-gnu" => I686UnknownLinuxGnu, @@ -270,6 +285,7 @@ impl Target { "thumbv7em-none-eabi" => Thumbv7emNoneEabi, "thumbv7em-none-eabihf" => Thumbv7emNoneEabihf, "thumbv7m-none-eabi" => Thumbv7mNoneEabi, + "wasm32-unknown-emscripten" => Wasm32UnknownEmscripten, "x86_64-apple-darwin" => X86_64AppleDarwin, "x86_64-pc-windows-gnu" => X86_64PcWindowsGnu, "x86_64-unknown-dragonfly" => X86_64UnknownDragonfly,