Repository: cross-rs/cross
Pull request #683 (2022-04-06 08:32:01)

fix env
Fixes a wrong assumption done in #670 where we went from

```toml
[target.aarch64-unknown-linux-gnu.env]
volumes = [
    "BUILD_DIR",
]
```

to

```toml
[target.aarch64-unknown-linux-gnu]
volumes = [
    "BUILD_DIR",
]
```

this fixes that, and makes sure that all markdown toml fences are checked in CI for correctness.

This pr also "un-opts" what was previously `CrossBuildConfig` to simplify a bit, see https://github.com/cross-rs/cross/pull/670#discussion_r832571356

Emilgardis: we don't have any tests for these, should we?

Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 362000f..bbc967c 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -74,7 +74,7 @@ jobs:
         uses: actions-rs/cargo@v1
         with:
           command: test
-          args: --locked
+          args: --locked --all-targets --workspace
         timeout-minutes: 5
 
   generate-matrix:
diff --git a/.gitignore b/.gitignore
index 1de5659..e333188 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,1 +1,5 @@
-target
\ No newline at end of file
+**/target/
+**/.idea/
+**/.vscode/*.*
+**/*.log
+/cargo-timing*.html
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 29e881f..0992bd4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,7 +5,6 @@ This project adheres to [Semantic Versioning](http://semver.org/).
 
 ## [Unreleased]
 
-- #629 - Update Android NDK version and API version
 - #681 - Warn on unknown fields and confusable targets
 - #665 - when not using [env.volumes](https://github.com/cross-rs/cross#mounting-volumes-into-the-build-environment), mount project in /project
 - #624 - Add `build.default-target`
diff --git a/Cargo.lock b/Cargo.lock
index ffe034f..e3b216b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -17,6 +17,15 @@ version = "1.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
 
+[[package]]
+name = "aho-corasick"
+version = "0.7.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
+dependencies = [
+ "memchr",
+]
+
 [[package]]
 name = "atty"
 version = "0.2.14"
@@ -103,15 +112,17 @@ dependencies = [
  "dunce",
  "eyre",
  "home",
- "lazy_static",
  "libc",
  "nix",
+ "once_cell",
+ "regex",
  "rustc_version",
  "serde",
  "serde_ignored",
  "serde_json",
  "shell-escape",
  "toml",
+ "walkdir",
  "which",
  "winapi",
 ]
@@ -269,6 +280,23 @@ dependencies = [
  "proc-macro2",
 ]
 
+[[package]]
+name = "regex"
+version = "1.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.6.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
+
 [[package]]
 name = "rustc-demangle"
 version = "0.1.21"
@@ -290,6 +318,15 @@ version = "1.0.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
 
+[[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
+
 [[package]]
 name = "semver"
 version = "1.0.6"
@@ -434,6 +471,17 @@ version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
 
+[[package]]
+name = "walkdir"
+version = "2.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
+dependencies = [
+ "same-file",
+ "winapi",
+ "winapi-util",
+]
+
 [[package]]
 name = "which"
 version = "4.2.4"
@@ -461,6 +509,15 @@ version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
 
+[[package]]
+name = "winapi-util"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
+dependencies = [
+ "winapi",
+]
+
 [[package]]
 name = "winapi-x86_64-pc-windows-gnu"
 version = "0.4.0"
diff --git a/Cargo.toml b/Cargo.toml
index 14e3ba4..f765996 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -14,7 +14,6 @@ atty = "0.2"
 color-eyre = "0.6"
 eyre = "0.6"
 home = "0.5"
-lazy_static = "1"
 rustc_version = "0.4"
 toml = "0.5"
 which = { version = "4", default_features = false }
@@ -33,3 +32,8 @@ dunce = "1"
 
 [profile.release]
 lto = true
+
+[dev-dependencies]
+regex = "1"
+once_cell = "1"
+walkdir = "2"
diff --git a/README.md b/README.md
index c1e0974..9ae5418 100644
--- a/README.md
+++ b/README.md
@@ -226,7 +226,7 @@ non-standard targets (i.e. something not reported by rustc/rustup). However,
 you can use the `build.xargo` or `target.{{TARGET}}.xargo` field in
 `Cross.toml` to force the use of `xargo`:
 
-``` toml
+```toml
 # all the targets will use `xargo`
 [build]
 xargo = true
@@ -234,7 +234,7 @@ xargo = true
 
 Or,
 
-``` toml
+```toml
 # only this target will use `xargo`
 [target.aarch64-unknown-linux-gnu]
 xargo = true
diff --git a/docker/Dockerfile.aarch64-linux-android b/docker/Dockerfile.aarch64-linux-android
index a477198..9357160 100644
--- a/docker/Dockerfile.aarch64-linux-android
+++ b/docker/Dockerfile.aarch64-linux-android
@@ -10,7 +10,7 @@ COPY xargo.sh /
 RUN /xargo.sh
 
 COPY android-ndk.sh /
-RUN /android-ndk.sh arm64 28
+RUN /android-ndk.sh arm64 21
 ENV PATH=$PATH:/android-ndk/bin
 
 COPY android-system.sh /
@@ -19,7 +19,7 @@ RUN /android-system.sh arm64
 COPY qemu.sh /
 RUN /qemu.sh aarch64
 
-RUN cp /android-ndk/sysroot/usr/lib/aarch64-linux-android/28/libz.so /system/lib/
+RUN cp /android-ndk/sysroot/usr/lib/libz.so /system/lib/
 
 # Libz is distributed in the android ndk, but for some unknown reason it is not
 # found in the build process of some crates, so we explicit set the DEP_Z_ROOT
diff --git a/docker/Dockerfile.arm-linux-androideabi b/docker/Dockerfile.arm-linux-androideabi
index 35ff082..bc8ed90 100644
--- a/docker/Dockerfile.arm-linux-androideabi
+++ b/docker/Dockerfile.arm-linux-androideabi
@@ -10,7 +10,7 @@ COPY xargo.sh /
 RUN /xargo.sh
 
 COPY android-ndk.sh /
-RUN /android-ndk.sh arm 28
+RUN /android-ndk.sh arm 21
 ENV PATH=$PATH:/android-ndk/bin
 
 COPY android-system.sh /
@@ -19,7 +19,7 @@ RUN /android-system.sh arm
 COPY qemu.sh /
 RUN /qemu.sh arm
 
-RUN cp /android-ndk/sysroot/usr/lib/arm-linux-androideabi/28/libz.so /system/lib/
+RUN cp /android-ndk/sysroot/usr/lib/libz.so /system/lib/
 
 # Libz is distributed in the android ndk, but for some unknown reason it is not
 # found in the build process of some crates, so we explicit set the DEP_Z_ROOT
diff --git a/docker/Dockerfile.armv7-linux-androideabi b/docker/Dockerfile.armv7-linux-androideabi
index 8fbeb41..79a900d 100644
--- a/docker/Dockerfile.armv7-linux-androideabi
+++ b/docker/Dockerfile.armv7-linux-androideabi
@@ -10,7 +10,7 @@ COPY xargo.sh /
 RUN /xargo.sh
 
 COPY android-ndk.sh /
-RUN /android-ndk.sh arm 28
+RUN /android-ndk.sh arm 21
 ENV PATH=$PATH:/android-ndk/bin
 
 COPY android-system.sh /
@@ -19,7 +19,7 @@ RUN /android-system.sh arm
 COPY qemu.sh /
 RUN /qemu.sh arm
 
-RUN cp /android-ndk/sysroot/usr/lib/arm-linux-androideabi/28/libz.so /system/lib/
+RUN cp /android-ndk/sysroot/usr/lib/libz.so /system/lib/
 
 # Libz is distributed in the android ndk, but for some unknown reason it is not
 # found in the build process of some crates, so we explicit set the DEP_Z_ROOT
diff --git a/docker/Dockerfile.i686-linux-android b/docker/Dockerfile.i686-linux-android
index 04ae677..be8f68e 100644
--- a/docker/Dockerfile.i686-linux-android
+++ b/docker/Dockerfile.i686-linux-android
@@ -10,7 +10,7 @@ COPY xargo.sh /
 RUN /xargo.sh
 
 COPY android-ndk.sh /
-RUN /android-ndk.sh x86 28
+RUN /android-ndk.sh x86 21
 ENV PATH=$PATH:/android-ndk/bin
 
 COPY android-system.sh /
@@ -26,7 +26,7 @@ RUN /android-system.sh x86
 COPY qemu.sh /
 RUN /qemu.sh i386
 
-RUN cp /android-ndk/sysroot/usr/lib/i686-linux-android/28/libz.so /system/lib/
+RUN cp /android-ndk/sysroot/usr/lib/libz.so /system/lib/
 
 # Libz is distributed in the android ndk, but for some unknown reason it is not
 # found in the build process of some crates, so we explicit set the DEP_Z_ROOT
diff --git a/docker/Dockerfile.x86_64-linux-android b/docker/Dockerfile.x86_64-linux-android
index 5e72462..b46e7a2 100644
--- a/docker/Dockerfile.x86_64-linux-android
+++ b/docker/Dockerfile.x86_64-linux-android
@@ -10,7 +10,7 @@ COPY xargo.sh /
 RUN /xargo.sh
 
 COPY android-ndk.sh /
-RUN /android-ndk.sh x86_64 28
+RUN /android-ndk.sh x86_64 21
 ENV PATH=$PATH:/android-ndk/bin
 
 COPY android-system.sh /
@@ -20,7 +20,7 @@ RUN /android-system.sh x86_64
 COPY qemu.sh /
 RUN /qemu.sh x86_64
 
-RUN cp /android-ndk/sysroot/usr/lib/x86_64-linux-android/28/libz.so /system/lib/
+RUN cp /android-ndk/sysroot/usr/lib/libz.so /system/lib/
 
 # Libz is distributed in the android ndk, but for some unknown reason it is not
 # found in the build process of some crates, so we explicit set the DEP_Z_ROOT
diff --git a/docker/android-ndk.sh b/docker/android-ndk.sh
index b24d28e..34182e7 100755
--- a/docker/android-ndk.sh
+++ b/docker/android-ndk.sh
@@ -3,7 +3,7 @@
 set -x
 set -euo pipefail
 
-NDK_URL=https://dl.google.com/android/repository/android-ndk-r21d-linux-x86_64.zip
+NDK_URL=https://dl.google.com/android/repository/android-ndk-r13b-linux-x86_64.zip
 
 main() {
     local arch="${1}" \
diff --git a/docker/android-system.sh b/docker/android-system.sh
index 9abc65d..db1c434 100755
--- a/docker/android-system.sh
+++ b/docker/android-system.sh
@@ -15,13 +15,10 @@ main() {
         gcc-multilib
         git
         g++-multilib
-        libncurses5
-        libtinfo5
         make
         openssh-client
         python
         python3
-        xz-utils
     )
 
     # fake java and javac, it is not necessary for what we build, but the build
@@ -60,78 +57,41 @@ EOF
 
     # this is the minimum set of modules that are need to build bionic
     # this was created by trial and error
-    python3 ./repo init -u https://android.googlesource.com/platform/manifest -b android-9.0.0_r1
-
-    python3 ./repo sync -c art
+    python3 ./repo init -u https://android.googlesource.com/platform/manifest -b android-5.0.0_r1
     python3 ./repo sync -c bionic
-    python3 ./repo sync -c build/make
-    python3 ./repo sync -c build/blueprint
-    python3 ./repo sync -c build/soong
-    python3 ./repo sync -c external/clang
+    python3 ./repo sync -c build
     python3 ./repo sync -c external/compiler-rt
-    python3 ./repo sync -c external/elfutils
     python3 ./repo sync -c external/jemalloc
     python3 ./repo sync -c external/libcxx
     python3 ./repo sync -c external/libcxxabi
-    python3 ./repo sync -c external/libunwind
-    python3 ./repo sync -c external/libunwind_llvm
-    python3 ./repo sync -c external/llvm
-    python3 ./repo sync -c external/lzma
+    python3 ./repo sync -c external/libselinux
     python3 ./repo sync -c external/mksh
-    python3 ./repo sync -c external/safe-iop
-    python3 ./repo sync -c external/valgrind
-    python3 ./repo sync -c external/vixl
-    python3 ./repo sync -c external/zlib
-    python3 ./repo sync -c frameworks/hardware/interfaces
-    python3 ./repo sync -c hardware/interfaces
-    python3 ./repo sync -c libnativehelper
-    python3 ./repo sync -c prebuilts/build-tools
-    python3 ./repo sync -c prebuilts/clang/host/linux-x86
-    python3 ./repo sync -c prebuilts/clang-tools
-    #python3 ./repo sync -c prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9
-    #python3 ./repo sync -c prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9
-    python3 ./repo sync -c prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8
-    python3 ./repo sync -c prebuilts/go/linux-x86
-    python3 ./repo sync -c prebuilts/misc
-    python3 ./repo sync -c prebuilts/sdk
+    python3 ./repo sync -c external/openssl
+    python3 ./repo sync -c external/stlport
+    python3 ./repo sync -c prebuilts/clang/linux-x86/host/3.5
     python3 ./repo sync -c system/core
-    python3 ./repo sync -c system/libhidl
-    python3 ./repo sync -c system/tools/hidl
-
     case "${arch}" in
         arm)
-            python3 ./repo sync prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9
+            python3 ./repo sync prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.8
         ;;
         arm64)
-            python3 ./repo sync prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9
+            python3 ./repo sync prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.8
             python3 ./repo sync prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9
         ;;
         x86)
-            python3 ./repo sync prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9
+            python3 ./repo sync prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.8
         ;;
         x86_64)
-            python3 ./repo sync prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9
+            python3 ./repo sync prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.8
         ;;
     esac
 
     # avoid build tests
-    rm bionic/linker/tests/Android.mk
-    rm bionic/tests/Android.mk
-    rm bionic/tests/Android.bp
-    rm bionic/benchmarks/Android.bp
-    rm bionic/tests/libs/Android.bp
-    rm bionic/tests/headers/Android.bp
-    rm bionic/tests/headers/posix/Android.bp
-
-    sed -i -z -e 's/cc_test {.*}//g' bionic/libc/malloc_debug/Android.bp
-    sed -i -z -e 's/cc_test {.*}//g' bionic/libc/malloc_hooks/Android.bp
-    sed -i -z -e 's/cc_test_host {.*}//g' bionic/tools/relocation_packer/Android.bp
-
-    export ALLOW_MISSING_DEPENDENCIES=true
+    rm bionic/linker/tests/Android.mk bionic/tests/Android.mk bionic/benchmarks/Android.mk
 
     # patch the linker to avoid the error
     # FATAL: kernel did not supply AT_SECURE
-    #sed -i -e 's/if (!kernel_supplied_AT_SECURE)/if (false)/g' bionic/linker/linker_environ.cpp
+    sed -i -e 's/if (!kernel_supplied_AT_SECURE)/if (false)/g' bionic/linker/linker_environ.cpp
 
     set +u
     # shellcheck disable=SC1091
@@ -149,9 +109,9 @@ EOF
     fi
 
     # list from https://elinux.org/Android_toolbox
-    for tool in cat chmod chown cmp cp ctrlaltdel date df dmesg du \
-        hd id ifconfig iftop insmod ioctl ionice kill ln log ls \
-        lsmod lsof lsusb md5 mkdir mount mv nandread netstat notify \
+    for tool in cat chmod chown cmp cp ctrlaltdel date dd df dmesg du getevent \
+        getprop grep hd id ifconfig iftop insmod ioctl ionice kill ln log ls \
+        lsmod lsof lsusb md5 mkdir mount mv nandread netstat newfs_msdos notify \
         printenv ps reboot renice rm rmdir rmmod route schedtop sendevent \
         setconsole setprop sleep smd start stop sync top touch umount \
         uptime vmstat watchprops wipe; do
diff --git a/docs/cross_toml.md b/docs/cross_toml.md
index 32e0a39..662c541 100644
--- a/docs/cross_toml.md
+++ b/docs/cross_toml.md
@@ -25,9 +25,17 @@ The `target` key allows you to specify parameters for specific compilation targe
 
 ```toml
 [target.aarch64-unknown-linux-gnu]
-volumes = ["VOL1_ARG", "VOL2_ARG"]
-passthrough = ["VAR1", "VAR2"]
 xargo = false
 image = "test-image"
 runner = "custom-runner"
 ```
+
+# `target.TARGET.env`
+The `target` key allows you to specify environment variables that should be used for a specific compilation target.
+This is similar to `build.env`, but allows you to be more specific per target.
+
+```toml
+[target.x86_64-unknown-linux-gnu.env]
+volumes = ["VOL1_ARG", "VOL2_ARG"]
+passthrough = ["IMPORTANT_ENV_VARIABLES"]
+```
\ No newline at end of file
diff --git a/src/config.rs b/src/config.rs
index 3ec2ab8..17f810c 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -319,7 +319,7 @@ mod tests {
         use std::matches;
 
         fn toml(content: &str) -> Result<crate::CrossToml> {
-            Ok(CrossToml::from_str(content).wrap_err("couldn't parse toml")?)
+            Ok(CrossToml::parse(content).wrap_err("couldn't parse toml")?.0)
         }
 
         #[test]
diff --git a/src/cross_toml.rs b/src/cross_toml.rs
index f080157..fbddb93 100644
--- a/src/cross_toml.rs
+++ b/src/cross_toml.rs
@@ -3,11 +3,11 @@
 use crate::errors::*;
 use crate::{Target, TargetList};
 use serde::Deserialize;
-use std::collections::HashMap;
+use std::collections::{BTreeSet, HashMap};
 
-/// Build environment configuration
-#[derive(Debug, Deserialize, PartialEq)]
-pub struct CrossBuildEnvConfig {
+/// Environment configuration
+#[derive(Debug, Deserialize, PartialEq, Default)]
+pub struct CrossEnvConfig {
     #[serde(default)]
     volumes: Vec<String>,
     #[serde(default)]
@@ -18,7 +18,8 @@ pub struct CrossBuildEnvConfig {
 #[derive(Debug, Deserialize, PartialEq, Default)]
 #[serde(rename_all = "kebab-case")]
 pub struct CrossBuildConfig {
-    env: Option<CrossBuildEnvConfig>,
+    #[serde(default)]
+    env: CrossEnvConfig,
     xargo: Option<bool>,
     default_target: Option<String>,
 }
@@ -26,13 +27,11 @@ pub struct CrossBuildConfig {
 /// Target configuration
 #[derive(Debug, Deserialize, PartialEq)]
 pub struct CrossTargetConfig {
-    #[serde(default)]
-    passthrough: Vec<String>,
-    #[serde(default)]
-    volumes: Vec<String>,
     xargo: Option<bool>,
     image: Option<String>,
     runner: Option<String>,
+    #[serde(default)]
+    env: CrossEnvConfig,
 }
 
 /// Cross configuration
@@ -40,15 +39,16 @@ pub struct CrossTargetConfig {
 pub struct CrossToml {
     #[serde(default, rename = "target")]
     pub targets: HashMap<Target, CrossTargetConfig>,
-    pub build: Option<CrossBuildConfig>,
+    #[serde(default)]
+    pub build: CrossBuildConfig,
 }
 
 impl CrossToml {
     /// Parses the [`CrossToml`] from a string
-    pub fn from_str(toml_str: &str) -> Result<Self> {
+    pub fn parse(toml_str: &str) -> Result<(Self, BTreeSet<String>)> {
         let tomld = &mut toml::Deserializer::new(toml_str);
 
-        let mut unused = std::collections::BTreeSet::new();
+        let mut unused = BTreeSet::new();
 
         let cfg = serde_ignored::deserialize(tomld, |path| {
             unused.insert(path.to_string());
@@ -57,11 +57,11 @@ impl CrossToml {
         if !unused.is_empty() {
             eprintln!(
                 "Warning: found unused key(s) in Cross configuration:\n > {}",
-                unused.into_iter().collect::<Vec<_>>().join(", ")
+                unused.clone().into_iter().collect::<Vec<_>>().join(", ")
             );
         }
 
-        Ok(cfg)
+        Ok((cfg, unused))
     }
 
     /// Returns the `target.{}.image` part of `Cross.toml`
@@ -76,7 +76,7 @@ impl CrossToml {
 
     /// Returns the `build.xargo` or the `target.{}.xargo` part of `Cross.toml`
     pub fn xargo(&self, target: &Target) -> (Option<bool>, Option<bool>) {
-        let build_xargo = self.build.as_ref().and_then(|b| b.xargo);
+        let build_xargo = self.build.xargo;
         let target_xargo = self.get_target(target).and_then(|t| t.xargo);
 
         (build_xargo, target_xargo)
@@ -84,33 +84,31 @@ impl CrossToml {
 
     /// Returns the list of environment variables to pass through for `build`,
     pub fn env_passthrough_build(&self) -> Vec<String> {
-        self.get_build_env()
-            .map_or(Vec::new(), |e| e.passthrough.clone())
+        self.build.env.passthrough.clone()
     }
 
     /// Returns the list of environment variables to pass through for `target`,
     pub fn env_passthrough_target(&self, target: &Target) -> Vec<String> {
         self.get_target(target)
-            .map_or(Vec::new(), |t| t.passthrough.clone())
+            .map_or(Vec::new(), |t| t.env.passthrough.clone())
     }
 
     /// Returns the list of environment variables to pass through for `build`,
     pub fn env_volumes_build(&self) -> Vec<String> {
-        self.get_build_env()
-            .map_or(Vec::new(), |e| e.volumes.clone())
+        self.build.env.volumes.clone()
     }
 
     /// Returns the list of environment variables to pass through for `target`,
     pub fn env_volumes_target(&self, target: &Target) -> Vec<String> {
         self.get_target(target)
-            .map_or(Vec::new(), |t| t.volumes.clone())
+            .map_or(Vec::new(), |t| t.env.volumes.clone())
     }
 
     /// Returns the default target to build,
     pub fn default_target(&self, target_list: &TargetList) -> Option<Target> {
         self.build
+            .default_target
             .as_ref()
-            .and_then(|b| b.default_target.as_ref())
             .map(|t| Target::from(t, target_list))
     }
 
@@ -118,11 +116,6 @@ impl CrossToml {
     fn get_target(&self, target: &Target) -> Option<&CrossTargetConfig> {
         self.targets.get(target)
     }
-
-    /// Returns a reference to the [`CrossBuildEnvConfig`]
-    fn get_build_env(&self) -> Option<&CrossBuildEnvConfig> {
-        self.build.as_ref().and_then(|b| b.env.as_ref())
-    }
 }
 
 #[cfg(test)]
@@ -133,11 +126,12 @@ mod tests {
     pub fn parse_empty_toml() -> Result<()> {
         let cfg = CrossToml {
             targets: HashMap::new(),
-            build: None,
+            build: CrossBuildConfig::default(),
         };
-        let parsed_cfg = CrossToml::from_str("")?;
+        let (parsed_cfg, unused) = CrossToml::parse("")?;
 
         assert_eq!(parsed_cfg, cfg);
+        assert!(unused.is_empty());
 
         Ok(())
     }
@@ -146,14 +140,14 @@ mod tests {
     pub fn parse_build_toml() -> Result<()> {
         let cfg = CrossToml {
             targets: HashMap::new(),
-            build: Some(CrossBuildConfig {
-                env: Some(CrossBuildEnvConfig {
+            build: CrossBuildConfig {
+                env: CrossEnvConfig {
                     volumes: vec!["VOL1_ARG".to_string(), "VOL2_ARG".to_string()],
                     passthrough: vec!["VAR1".to_string(), "VAR2".to_string()],
-                }),
+                },
                 xargo: Some(true),
                 default_target: None,
-            }),
+            },
         };
 
         let test_str = r#"
@@ -164,9 +158,10 @@ mod tests {
           volumes = ["VOL1_ARG", "VOL2_ARG"]
           passthrough = ["VAR1", "VAR2"]
         "#;
-        let parsed_cfg = CrossToml::from_str(test_str)?;
+        let (parsed_cfg, unused) = CrossToml::parse(test_str)?;
 
         assert_eq!(parsed_cfg, cfg);
+        assert!(unused.is_empty());
 
         Ok(())
     }
@@ -179,8 +174,10 @@ mod tests {
                 triple: "aarch64-unknown-linux-gnu".to_string(),
             },
             CrossTargetConfig {
-                passthrough: vec!["VAR1".to_string(), "VAR2".to_string()],
-                volumes: vec!["VOL1_ARG".to_string(), "VOL2_ARG".to_string()],
+                env: CrossEnvConfig {
+                    passthrough: vec!["VAR1".to_string(), "VAR2".to_string()],
+                    volumes: vec!["VOL1_ARG".to_string(), "VOL2_ARG".to_string()],
+                },
                 xargo: Some(false),
                 image: Some("test-image".to_string()),
                 runner: None,
@@ -189,19 +186,21 @@ mod tests {
 
         let cfg = CrossToml {
             targets: target_map,
-            build: None,
+            build: CrossBuildConfig::default(),
         };
 
         let test_str = r#"
-          [target.aarch64-unknown-linux-gnu]
-          volumes = ["VOL1_ARG", "VOL2_ARG"]
-          passthrough = ["VAR1", "VAR2"]
-          xargo = false
-          image = "test-image"
+            [target.aarch64-unknown-linux-gnu.env]
+            volumes = ["VOL1_ARG", "VOL2_ARG"]
+            passthrough = ["VAR1", "VAR2"]
+            [target.aarch64-unknown-linux-gnu]
+            xargo = false
+            image = "test-image"
         "#;
-        let parsed_cfg = CrossToml::from_str(test_str)?;
+        let (parsed_cfg, unused) = CrossToml::parse(test_str)?;
 
         assert_eq!(parsed_cfg, cfg);
+        assert!(unused.is_empty());
 
         Ok(())
     }
diff --git a/src/docker.rs b/src/docker.rs
index d9e0ba3..580a7b3 100644
--- a/src/docker.rs
+++ b/src/docker.rs
@@ -169,10 +169,6 @@ pub fn run(
 
     docker.arg("--rm");
 
-    if target.needs_docker_privileged() {
-        docker.arg("--privileged");
-    }
-
     // We need to specify the user for Docker, but not for Podman.
     if let Ok(ce) = get_container_engine() {
         if ce.ends_with(DOCKER) {
diff --git a/src/main.rs b/src/main.rs
index 79b6ca8..284c07f 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,5 +1,8 @@
 #![deny(missing_debug_implementations, rust_2018_idioms)]
 
+#[cfg(test)]
+mod tests;
+
 mod cargo;
 mod cli;
 mod config;
@@ -204,14 +207,6 @@ impl Target {
 
         !native && (self.is_linux() || self.is_windows() || self.is_bare_metal())
     }
-
-    fn needs_docker_privileged(&self) -> bool {
-        let arch_32bit = self.triple().starts_with("arm")
-            || self.triple().starts_with("i586")
-            || self.triple().starts_with("i686");
-
-        arch_32bit && self.is_android()
-    }
 }
 
 impl std::fmt::Display for Target {
@@ -413,7 +408,7 @@ fn toml(root: &Root) -> Result<Option<CrossToml>> {
         let content = file::read(&path)
             .wrap_err_with(|| format!("could not read file `{}`", path.display()))?;
 
-        let config = CrossToml::from_str(&content)
+        let (config, _) = CrossToml::parse(&content)
             .wrap_err_with(|| format!("failed to parse file `{}` as TOML", path.display()))?;
 
         Ok(Some(config))


Test patch:
diff --git a/ci/test.sh b/ci/test.sh
index 9931fdd..a7fa98e 100755
--- a/ci/test.sh
+++ b/ci/test.sh
@@ -144,9 +144,10 @@ EOF
     if (( ${CPP:-0} )); then
         td="$(mktemp -d)"
 
-        git clone --depth 1 https://github.com/cross-rs/rust-cpp-hello-word "${td}"
+        git clone --depth 1 https://github.com/japaric/hellopp "${td}"
 
         pushd "${td}"
+        cargo update -p gcc
         retry cargo fetch
         if (( ${RUN:-0} )); then
             cross_run --target "${TARGET}"
diff --git a/src/tests.rs b/src/tests.rs
new file mode 100644
index 0000000..5fa2991
--- /dev/null
+++ b/src/tests.rs
@@ -0,0 +1,53 @@
+mod toml;
+
+use std::{
+    ffi::OsStr,
+    path::{Path, PathBuf},
+};
+
+use once_cell::sync::OnceCell;
+use serde::Deserialize;
+
+static WORKSPACE: OnceCell<PathBuf> = OnceCell::new();
+
+/// Returns the cargo workspace for the manifest
+pub fn get_cargo_workspace() -> &'static Path {
+    let manifest_dir = env!("CARGO_MANIFEST_DIR");
+    WORKSPACE.get_or_init(|| {
+        #[derive(Deserialize)]
+        struct Manifest {
+            workspace_root: PathBuf,
+        }
+        let output = std::process::Command::new(
+            std::env::var("CARGO")
+                .ok()
+                .unwrap_or_else(|| "cargo".to_string()),
+        )
+        .arg("metadata")
+        .arg("--format-version=1")
+        .arg("--no-deps")
+        .current_dir(manifest_dir)
+        .output()
+        .unwrap();
+        let manifest: Manifest = serde_json::from_slice(&output.stdout).unwrap();
+        manifest.workspace_root
+    })
+}
+
+pub fn walk_dir<'a>(
+    root: &'_ Path,
+    skip: &'a [impl AsRef<OsStr>],
+) -> impl Iterator<Item = Result<walkdir::DirEntry, walkdir::Error>> + 'a {
+    walkdir::WalkDir::new(root).into_iter().filter_entry(|e| {
+        if skip
+            .iter()
+            .map(|s| -> &std::ffi::OsStr { s.as_ref() })
+            .any(|dir| e.file_name() == dir)
+        {
+            return false;
+        } else if e.file_type().is_dir() {
+            return true;
+        }
+        e.path().extension() == Some("md".as_ref())
+    })
+}
diff --git a/src/tests/toml.rs b/src/tests/toml.rs
new file mode 100644
index 0000000..e39389e
--- /dev/null
+++ b/src/tests/toml.rs
@@ -0,0 +1,63 @@
+use std::io::Read;
+
+use once_cell::sync::Lazy;
+use regex::{Regex, RegexBuilder};
+
+static TOML_REGEX: Lazy<Regex> = Lazy::new(|| {
+    RegexBuilder::new(r#"```toml\n(.*?)```"#)
+        .multi_line(true)
+        .dot_matches_new_line(true)
+        .build()
+        .unwrap()
+});
+
+#[test]
+fn toml_check() -> Result<(), Box<dyn std::error::Error>> {
+    let workspace_root = super::get_cargo_workspace();
+    let walk = super::walk_dir(
+        workspace_root,
+        &[
+            "target",
+            ".git",
+            "src",
+            "CODE_OF_CONDUCT.md",
+            "CHANGELOG.md",
+        ],
+    );
+
+    for dir_entry in walk {
+        let dir_entry = dir_entry?;
+        if dir_entry.file_type().is_dir() {
+            continue;
+        }
+        eprintln!("File: {:?}", dir_entry.path().display());
+        let mut file = std::fs::File::open(dir_entry.path()).unwrap();
+        let mut contents = String::new();
+        file.read_to_string(&mut contents).unwrap();
+        for matches in TOML_REGEX.captures_iter(&contents) {
+            let fence = matches.get(1).unwrap();
+            eprintln!(
+                "testing snippet at: {}:{:?}",
+                dir_entry.path().display(),
+                text_line_no(&contents, fence.range().start),
+            );
+            assert!(crate::cross_toml::CrossToml::parse(fence.as_str())?
+                .1
+                .is_empty());
+        }
+    }
+    Ok(())
+}
+
+pub fn text_line_no(text: &str, index: usize) -> usize {
+    let mut line_no = 0;
+    let mut count = 0;
+    for line in text.split('\n') {
+        line_no += 1;
+        count += line.as_bytes().len() + 1;
+        if count >= index {
+            break;
+        }
+    }
+    line_no
+}