Showing preview only (326K chars total). Download the full file or copy to clipboard to get everything.
Repository: sonos/dinghy
Branch: main
Commit: b898dfb241ea
Files: 77
Total size: 305.0 KB
Directory structure:
gitextract_xzzdib82/
├── .github/
│ └── workflows/
│ ├── binaries.yml
│ ├── release.yml
│ └── test.yml
├── .gitignore
├── .travis.apple-third-tier.sh
├── .travis.sh
├── .travis.yml
├── CHANGELOG.md
├── Cargo.toml
├── LICENSE
├── LICENSE-APACHE
├── LICENSE-MIT
├── README.md
├── cargo-dinghy/
│ ├── Cargo.toml
│ └── src/
│ ├── cli.rs
│ └── main.rs
├── dinghy-build/
│ ├── Cargo.toml
│ └── src/
│ ├── bindgen_macros.rs
│ ├── build.rs
│ ├── build_env.rs
│ ├── lib.rs
│ └── utils.rs
├── dinghy-lib/
│ ├── Cargo.toml
│ ├── build.rs
│ └── src/
│ ├── android/
│ │ ├── device.rs
│ │ ├── mod.rs
│ │ └── platform.rs
│ ├── apple/
│ │ ├── device.rs
│ │ ├── helpers.py
│ │ ├── mod.rs
│ │ ├── platform.rs
│ │ └── xcode.rs
│ ├── config.rs
│ ├── device.rs
│ ├── host/
│ │ ├── mod.rs
│ │ └── platform.rs
│ ├── lib.rs
│ ├── overlay.rs
│ ├── platform/
│ │ ├── mod.rs
│ │ └── regular_platform.rs
│ ├── plugin/
│ │ └── mod.rs
│ ├── project.rs
│ ├── script/
│ │ ├── device.rs
│ │ └── mod.rs
│ ├── ssh/
│ │ ├── device.rs
│ │ └── mod.rs
│ ├── toolchain.rs
│ └── utils.rs
├── dinghy-test/
│ ├── Cargo.toml
│ └── src/
│ └── lib.rs
├── docs/
│ ├── android.md
│ ├── dinghy-build.md
│ ├── files.md
│ ├── filter.md
│ ├── ios.md
│ ├── overlay.md
│ ├── ssh.md
│ └── vars.md
├── legacy/
│ ├── Cargo.toml
│ ├── README.md
│ ├── build.rs
│ └── src/
│ └── main.rs
├── musl_build.sh
├── post-release.sh
├── pre-release.sh
├── release.sh
├── rust-toolchain
└── test-ws/
├── .dinghy.toml
├── Cargo.toml
├── rust-toolchain
├── test-app/
│ ├── .dinghy.toml
│ ├── Cargo.toml
│ └── src/
│ └── lib.rs
├── test-bin/
│ ├── Cargo.toml
│ └── src/
│ └── main.rs
└── test-proc-macro/
├── Cargo.toml
└── src/
└── lib.rs
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/workflows/binaries.yml
================================================
on:
release:
types:
- created
name: Upload Release Assets
jobs:
assets:
name: Upload Release Assets
strategy:
matrix:
os: [ ubuntu-latest, macOS-latest ]
include:
- os: ubuntu-latest
name: linux
- os: macOS-latest
name: macos
runs-on: ${{ matrix.os }}
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Extract version tag
id: version
run: echo ::set-output name=value::$(echo ${{ github.ref }} | cut -f 3 -d / )
- name: Build cargo-dinghy
run: |
set -ex
ZIPDIR=cargo-dinghy-${{ steps.version.outputs.value }}
mkdir $ZIPDIR
if [ ${{matrix.name}} == "macos" ]
then
# Let's ensure that both aarch64 and x86_64 targets are installed
rustup target add aarch64-apple-darwin x86_64-apple-darwin
brew install coreutils gnu-tar
PATH=$(brew --prefix)/opt/coreutils/libexec/gnubin:$PATH
PATH=$(brew --prefix)/opt/gnu-tar/libexec/gnubin:$PATH
tar --version
cargo build --target aarch64-apple-darwin --target x86_64-apple-darwin --release -p cargo-dinghy
lipo -create -output $ZIPDIR/cargo-dinghy target/aarch64-apple-darwin/release/cargo-dinghy target/x86_64-apple-darwin/release/cargo-dinghy
else
./musl_build.sh
mv target/cargo-dinghy $ZIPDIR
fi
ls -al $ZIPDIR
$ZIPDIR/cargo-dinghy --version
file $ZIPDIR/cargo-dinghy
md5sum $ZIPDIR/cargo-dinghy
tar vczf cargo-dinghy-${{matrix.name}}-${{ steps.version.outputs.value }}.tgz $ZIPDIR
md5sum cargo-dinghy-${{matrix.name}}-${{ steps.version.outputs.value }}.tgz
mkdir test
cd test
tar zxf ../cargo-dinghy-${{matrix.name}}-${{ steps.version.outputs.value }}.tgz
md5sum $ZIPDIR/cargo-dinghy
- name: Upload asset
uses: softprops/action-gh-release@v1
with:
files: cargo-dinghy-${{matrix.name}}-${{ steps.version.outputs.value }}.tgz
name: ${{ steps.version.outputs.value }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
================================================
FILE: .github/workflows/release.yml
================================================
on:
push:
tags:
- 'cargo-dinghy/*'
name: Create release
jobs:
release:
name: Create release
runs-on: ubuntu-latest
steps:
- name: Extract version tag
id: version
run: echo ::set-output name=value::$(echo ${{ github.ref }} | cut -f 4 -d /)
- name: Create Release
id: create_release
uses: actions/create-release@latest
env:
GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN_RELEASE }}
with:
tag_name: ${{ steps.version.outputs.value }}
release_name: ${{ steps.version.outputs.value }}
================================================
FILE: .github/workflows/test.yml
================================================
name: Build and test
on:
pull_request:
schedule:
- cron: '0 5 * * *'
jobs:
linux:
strategy:
fail-fast: false
matrix:
rust: [ 1.85.0, beta, nightly ]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: build and test
env:
RUST_VERSION: ${{matrix.rust}}
run: ./.travis.sh
macos:
strategy:
fail-fast: false
matrix:
os: [ macOS-14, macOS-15 ]
runs-on: ${{matrix.os}}
steps:
- uses: actions/checkout@v1
- name: build and test
run: ./.travis.sh
# windows:
# runs-on: windows-2019
#
# steps:
# - uses: actions/checkout@v1
# - name: cargo test
# run: cargo test --all
apple-3rd-tier:
strategy:
fail-fast: false
matrix:
os: [ macOS-14, macOS-15 ]
sim: [ tvOS, watchOS ]
runs-on: ${{matrix.os}}
steps:
- uses: actions/checkout@v1
- name: build and test
run: ./.travis.apple-third-tier.sh ${{matrix.sim}}
env:
RUST_VERSION: 1.85.0
linux-musl:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: build and test
run: ./musl_build.sh
================================================
FILE: .gitignore
================================================
target
.idea/
*.iml
*.back
================================================
FILE: .travis.apple-third-tier.sh
================================================
#!/bin/bash
set -e
set -x
SIM_TARGET=$1
title() {
set +x
echo -e '\n\033[1;33m '$@' \033[0m\n'
set -x
}
if [ -z "$CARGO_DINGHY" ]
then
title "••• build cargo-dinghy •••"
cargo build -p cargo-dinghy
CARGO_DINGHY="`pwd`/target/debug/cargo-dinghy -vv"
fi
echo RUST_VERSION: ${RUST_VERSION:=1.70.0}
rustup toolchain add $RUST_VERSION
export RUSTUP_TOOLCHAIN=$RUST_VERSION
rustup toolchain add nightly --component rust-src;
tests_sequence_unstable_target() {
# There's something odd with using the .cargo/config runner attribute and
# workspaces when the runner uses `cargo run --manifest-path ../Cargo.toml
# --bin cargo-dinghy ...`
title "testing from project directory for rust target $1 on device $2"
title "testing from workspace directory"
( \
cd test-ws \
&& cargo clean \
&& $CARGO_DINGHY -d $1 -p $2 +nightly test -Zbuild-std pass \
&& ! $CARGO_DINGHY -d $1 -p $2 +nightly test -Zbuild-std fails \
&& ! $CARGO_DINGHY -d $1 -p $2 +nightly test -Zbuild-std \
)
title "testing from project directory"
( \
cd test-ws/test-app \
&& cargo clean \
&& $CARGO_DINGHY -d $1 -p $2 +nightly test -Zbuild-std pass \
&& ! $CARGO_DINGHY -d $1 -p $2 +nightly test -Zbuild-std fails \
&& ! $CARGO_DINGHY -d $1 -p $2 +nightly test -Zbuild-std \
)
title "test from workspace directory with project filter"
( \
cd test-ws \
&& cargo clean \
&& $CARGO_DINGHY -d $1 -p $2 +nightly test -p test-app -Zbuild-std pass \
&& ! $CARGO_DINGHY -d $1 -p $2 +nightly test -p test-app -Zbuild-std fails \
&& ! $CARGO_DINGHY -d $1 -p $2 +nightly test -p test-app -Zbuild-std \
)
}
if [ $SIM_TARGET = tvOS ]
then
title "••••• Darwin: tvos simulator tests •••••"
title "boot a simulator"
# *-apple-{tvos,watchos}[-sim] require `-Zbuild-std`
TVOS_RUNTIME_ID=$(xcrun simctl list runtimes | grep tvOS | cut -d ' ' -f 7 | tail -1)
export TV_SIM_ID=$(xcrun simctl create My-4ktv com.apple.CoreSimulator.SimDeviceType.Apple-TV-4K-3rd-generation-4K $TVOS_RUNTIME_ID)
xcrun simctl boot $TV_SIM_ID
if [ "$(uname -m)" = "arm64" ]; then
tests_sequence_unstable_target ${TV_SIM_ID} auto-tvos-aarch64-sim
else
# The x86 tvOS simulator tripple does not end in -sim.
tests_sequence_unstable_target ${TV_SIM_ID} auto-tvos-x86_64
fi
xcrun simctl delete $TV_SIM_ID
fi
if [ $SIM_TARGET = watchOS ]
then
title "••••• Darwin: watchvos simulator tests •••••"
title "boot a simulator"
WATCHOS_RUNTIME_ID=$(xcrun simctl list runtimes | grep watchOS | cut -d ' ' -f 7 | tail -1)
export WATCHOS_SIM_ID=$(xcrun simctl create My-apple-watch com.apple.CoreSimulator.SimDeviceType.Apple-Watch-SE-44mm-2nd-generation $WATCHOS_RUNTIME_ID)
xcrun simctl boot $WATCHOS_SIM_ID
if [ "$(uname -m)" = "arm64" ]; then
tests_sequence_unstable_target ${WATCHOS_SIM_ID} auto-watchos-aarch64-sim
else
tests_sequence_unstable_target ${WATCHOS_SIM_ID} auto-watchos-x86_64-sim
fi
xcrun simctl delete $WATCHOS_SIM_ID
fi
# This depends on https://github.com/sonos/dinghy/pull/223
if [ $SIM_TARGET = visionOS ]
then
if [ "$(uname -m)" = "arm64" ]; then
title "••••• Darwin: visionOS simulator tests •••••"
title "boot a simulator"
xcrun simctl list devicetypes vision
VISIONOS_DEVICE_TYPE=$(xcrun simctl list devicetypes vision -j | jq -r '.devicetypes[0].identifier')
VISIONOS_RUNTIME_ID=$(xcrun simctl list runtimes | grep visionOS | cut -d ' ' -f 7 | tail -1)
export VISIONOS_SIM_ID=$(xcrun simctl create My-apple-vision-pro $VISIONOS_DEVICE_TYPE $VISIONOS_RUNTIME_ID)
xcrun simctl boot $VISIONOS_SIM_ID
tests_sequence_unstable_target ${VISIONOS_SIM_ID} auto-visionos-aarch64-sim
xcrun simctl delete $VISIONOS_SIM_ID
fi
fi
rustup default stable
================================================
FILE: .travis.sh
================================================
#!/bin/bash
set -e
set -x
title() {
set +x
echo -e '\n\033[1;33m '$@' \033[0m\n'
set -x
}
if [ ! cargo --version ]
then
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
PATH=$PATH:$HOME/.cargo/bin
fi
if [ -z "$CARGO_DINGHY" ]
then
title "••• build cargo-dinghy •••"
cargo build -p cargo-dinghy
CARGO_DINGHY="`pwd`/target/debug/cargo-dinghy -vv"
fi
echo RUST_VERSION: ${RUST_VERSION:=1.85.0}
rustup toolchain add $RUST_VERSION
export RUSTUP_TOOLCHAIN=$RUST_VERSION
title "••• test original cargo build •••"
cargo build
cargo test
( \
cd test-ws/test-app \
&& export NOT_BUILT_WITH_DINGHY=1 \
&& cargo test pass \
&& ! NOT_BUILT_WITH_DINGHY=1 cargo test fails \
)
tests_sequence() {
title "testing from workspace directory"
( \
cd test-ws \
&& cargo clean \
&& $CARGO_DINGHY -d $1 test pass \
&& $CARGO_DINGHY -d $1 test --doc \
&& ! $CARGO_DINGHY -d $1 test fails \
&& ! $CARGO_DINGHY -d $1 test \
)
title "testing from project directory"
( \
cd test-ws/test-app \
&& cargo clean \
&& $CARGO_DINGHY -d $1 test pass \
&& $CARGO_DINGHY -d $1 test --doc \
&& ! $CARGO_DINGHY -d $1 test fails \
&& ! $CARGO_DINGHY -d $1 test \
)
title "test from workspace directory with project filter"
( \
cd test-ws \
&& cargo clean \
&& $CARGO_DINGHY -d $1 test -p test-app pass \
&& $CARGO_DINGHY -d $1 test -p test-app --doc \
&& ! $CARGO_DINGHY -d $1 test -p test-app fails \
&& ! $CARGO_DINGHY -d $1 test -p test-app \
)
}
tests_sequence_aarch64_ios_sim() {
title "testing from workspace directory"
( \
cd test-ws \
&& cargo clean \
&& $CARGO_DINGHY -d $1 -p auto-ios-aarch64-sim test pass \
&& $CARGO_DINGHY -d $1 -p auto-ios-aarch64-sim test --doc \
&& ! $CARGO_DINGHY -d $1 -p auto-ios-aarch64-sim test fails \
&& ! $CARGO_DINGHY -d $1 -p auto-ios-aarch64-sim test \
)
title "testing from project directory"
( \
cd test-ws/test-app \
&& cargo clean \
&& $CARGO_DINGHY -d $1 -p auto-ios-aarch64-sim test pass \
&& $CARGO_DINGHY -d $1 -p auto-ios-aarch64-sim test --doc \
&& ! $CARGO_DINGHY -d $1 -p auto-ios-aarch64-sim test fails \
&& ! $CARGO_DINGHY -d $1 -p auto-ios-aarch64-sim test \
)
title "test from workspace directory with project filter"
( \
cd test-ws \
&& cargo clean \
&& $CARGO_DINGHY -d $1 -p auto-ios-aarch64-sim test -p test-app pass \
&& $CARGO_DINGHY -d $1 -p auto-ios-aarch64-sim test -p test-app --doc \
&& ! $CARGO_DINGHY -d $1 -p auto-ios-aarch64-sim test -p test-app fails \
&& ! $CARGO_DINGHY -d $1 -p auto-ios-aarch64-sim test -p test-app \
)
}
if [ `uname` = Darwin ]
then
title "••••• Darwin: ios simulator tests •••••"
title "boot a simulator"
RUNTIME_ID=$(xcrun simctl list runtimes | grep iOS | cut -d ' ' -f 7 | tail -1)
# Installed simulators on github runners differ depending on the version
# of macos. When the simulator device type ID needs to be updated, select
# a new one:
# https://github.com/actions/runner-images/blob/main/images/macos/macos-12-Readme.md#installed-simulators
# https://github.com/actions/runner-images/blob/main/images/macos/macos-13-Readme.md#installed-simulators
# https://github.com/actions/runner-images/blob/main/images/macos/macos-14-arm64-Readme.md#installed-simulators
export SIM_ID=$(xcrun simctl create My-iphone-se com.apple.CoreSimulator.SimDeviceType.iPhone-SE-3rd-generation $RUNTIME_ID)
xcrun simctl boot $SIM_ID
# The x86_64-apple-ios target seems to not work on an ARM64 host,
# and the aarch64-apple-ios-sim target doesn't work on an x86-64 host.
if [ "$(uname -m)" = "arm64" ]; then
rustup target add aarch64-apple-ios-sim;
tests_sequence_aarch64_ios_sim $SIM_ID
else
rustup target add x86_64-apple-ios;
tests_sequence $SIM_ID
fi
xcrun simctl delete $SIM_ID
if ios-deploy -c -t 1 > /tmp/ios_devices
then
device=$(grep "Found" /tmp/ios_devices | head -1 | cut -d " " -f 3)
title "••••• Darwin: ios-deploy detected a device •••••"
rustup target add aarch64-apple-ios
tests_sequence $device
fi
else
echo $ANDROID_SDK_ROOT
if [ -n "$ANDROID_SDK_ROOT" ]
then
title "••••• Linux: android tests •••••"
title "setup simulator"
rustup target add armv7-linux-androideabi
## BEGIN FIX-EMULATOR
# Use emulator version 32.1.15 as latest version (36.3.10.0 as of writing)
# doesn't support arm images anymore
EMULATOR="$(pwd)/target/emulator/emulator"
[ -e $EMULATOR ] || ( \
cd target/ \
&& wget -q https://redirector.gvt1.com/edgedl/android/repository/emulator-linux_x64-10696886.zip \
&& unzip emulator-linux_x64-10696886.zip \
&& rm emulator-linux_x64-10696886.zip
)
# to use the bundled emulator instead (if we choose to go the x86 route at some point)
# use the using the following line instead
# EMULATOR="$ANDROID_SDK_ROOT/emulator/emulator"
# END FIX-EMULATOR
yes | $ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager --licenses > /dev/null
$ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager --install "system-images;android-24;default;armeabi-v7a" "ndk;27.3.13750724" "emulator" "platform-tools" # "cmdline-tools;latest"
echo no | $ANDROID_SDK_ROOT/cmdline-tools/latest/bin/avdmanager create avd -c 1000M --force -n testdinghy -k "system-images;android-24;default;armeabi-v7a"
ANDROID_AVD_HOME=$HOME/.config/.android/avd $EMULATOR @testdinghy -partition-size 1024 -no-audio -no-boot-anim -no-window -accel on -gpu off &
timeout 180 $ANDROID_SDK_ROOT/platform-tools/adb wait-for-device
export ANDROID_NDK_HOME=$ANDROID_SDK_ROOT/ndk/27.3.13750724
tests_sequence android
fi
title "••••• Linux: script tests (with qemu) •••••"
title "setup qemu"
rustup target add aarch64-unknown-linux-musl
sudo apt-get update
sudo apt-get -y install --no-install-recommends qemu-system-arm qemu-user binutils-aarch64-linux-gnu gcc-aarch64-linux-gnu
echo -e "[platforms.qemu]\nrustc_triple='aarch64-unknown-linux-musl'\ndeb_multiarch='aarch64-linux-gnu'" > .dinghy.toml
echo -e "[script_devices.qemu]\nplatform='qemu'\npath='/tmp/qemu'" >> .dinghy.toml
echo -e "#!/bin/sh\nexe=\$1\nshift\n/usr/bin/qemu-aarch64 -L /usr/aarch64-linux-gnu/ \$exe --test-threads 1 \"\$@\"" > /tmp/qemu
chmod +x /tmp/qemu
tests_sequence qemu
fi
if [ -n "$DEPLOY" ]
then
if [ `uname` = Linux ]
then
export OPENSSL_STATIC=yes
export OPENSSL_INCLUDE_DIR=/usr/include
export OPENSSL_LIB_DIR=$(dirname `find /usr -name libssl.a`)
cargo clean
fi
cargo build --release -p cargo-dinghy
mkdir -p cargo-dinghy-$DEPLOY
cp target/release/cargo-dinghy cargo-dinghy-$DEPLOY
tar vczf cargo-dinghy-$DEPLOY.tgz cargo-dinghy-$DEPLOY
fi
================================================
FILE: .travis.yml
================================================
sudo: false
language: rust
matrix:
allow_failures:
- rust: nightly
include:
- os: osx
osx_image: xcode11.2
env: DEPLOY=macos
- os: linux
dist: bionic
- rust: stable
dist: bionic
env: DEPLOY=travis
- rust: beta
dist: bionic
- rust: nightly
dist: bionic
script: sh .travis.sh
deploy:
provider: releases
skip_cleanup: true
api_key:
secure: YJlTT/zACnSNi3jFVXF+pPx615hxUrRe5ZCLLd3bgek/daEI4FIYJQIbO8ZlL8ufx7/SVKg8gjGxNYqDUsd3dozjVlALibogi6XBZfoAWZBkCUQNyoqALzNnTP+PbKme5mRQbtNPp5X0g7KDYoJGUHsvD6FUD7rxYe6QMpbGUvaa89cwFguNvWsjZgGBYtf5HO42u+JE6hXhmO8WGyK0cQYZEdXWn9kUBgQD1ua4+rqn9h35IFveEG0QAq59HNDTfmg67okXJINqYlJ0DzGJM4yN8BH9nSsBD/vGn79Z7nLp6PXOBIvwBY2sRUt3WwVNLGAVCqlmIeRdioQbUvJm09uqgNt8O9vKZYcAxUYja1xfCO5lyUDQ8IYJ5EZU9lSpW3wvalLKswL6bvLQ1ckI2ASe8rYMxsbUZ5TE3fJWuHpuGkaoJnvIvefrR4orB/Gi5cSJ708TBtUwS/Law95hUHPbCKs6nsC1vsoLLj33xZqpoNCZH8BSWyDWsfSuDdiMrSW68LeYe/DxWFuSg/AWfqDXi5xY5CMFzRJ5TgxhZ7wGVvrUTAvripVyPQkewb3fThgLDGqAwboPq9wq7UqiJKT5l8/BOHdlKTEeX5AKlaMCERZtupM6LT/2v3YuaBZpiVnsDW8nWAz0DyEcFcWo/UFl44Njrah9f/t4tJXuF7k=
file_glob: true
file: cargo-dinghy-*.tgz
on:
tags: true
repo: sonos/dinghy
condition: "$TRAVIS_TAG =~ ^.*$"
================================================
FILE: CHANGELOG.md
================================================
# Changelog
## [0.8.4](https://github.com/sonos/dinghy/tree/0.8.4) (2025-12-12)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.8.2...0.8.4)
**Merged pull requests:**
- bump macos [\#261](https://github.com/sonos/dinghy/pull/261) ([kali](https://github.com/kali))
- use 1.85 for iDevices [\#259](https://github.com/sonos/dinghy/pull/259) ([kali](https://github.com/kali))
## [0.8.2](https://github.com/sonos/dinghy/tree/0.8.2) (2025-10-07)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.8.1...0.8.2)
**Closed issues:**
- Doctests freeze on Rust 1.89 [\#255](https://github.com/sonos/dinghy/issues/255)
**Merged pull requests:**
- 1.85.0 [\#258](https://github.com/sonos/dinghy/pull/258) ([kali](https://github.com/kali))
- ignore devices with no udid [\#257](https://github.com/sonos/dinghy/pull/257) ([kali](https://github.com/kali))
- 1.89 doctests support [\#256](https://github.com/sonos/dinghy/pull/256) ([fredszaq](https://github.com/fredszaq))
- fix ci by holding back some deps [\#254](https://github.com/sonos/dinghy/pull/254) ([kali](https://github.com/kali))
- Fix display of android platforms in cargo dinghy all-devices [\#250](https://github.com/sonos/dinghy/pull/250) ([fredszaq](https://github.com/fredszaq))
## [0.8.1](https://github.com/sonos/dinghy/tree/0.8.1) (2025-06-30)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.8.0...0.8.1)
**Closed issues:**
- No devices found for name hint `my\_android' [\#252](https://github.com/sonos/dinghy/issues/252)
**Merged pull requests:**
- Use VersionReq api to support X.Y versions \(needed for iOS 18\) [\#253](https://github.com/sonos/dinghy/pull/253) ([hdlj](https://github.com/hdlj))
- fix workspace resolver warning in test-ws [\#249](https://github.com/sonos/dinghy/pull/249) ([fredszaq](https://github.com/fredszaq))
- search ndk in default install location in linux [\#248](https://github.com/sonos/dinghy/pull/248) ([fredszaq](https://github.com/fredszaq))
- debug ci [\#247](https://github.com/sonos/dinghy/pull/247) ([kali](https://github.com/kali))
- Fix provisioning profile path detection [\#246](https://github.com/sonos/dinghy/pull/246) ([emricksinisonos](https://github.com/emricksinisonos))
- Revert "Disable proc macro tests for tvOS and watchOS" [\#244](https://github.com/sonos/dinghy/pull/244) ([fredszaq](https://github.com/fredszaq))
## [0.8.0](https://github.com/sonos/dinghy/tree/0.8.0) (2024-11-18)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.7.3...0.8.0)
**Closed issues:**
- WathOS CI broken [\#238](https://github.com/sonos/dinghy/issues/238)
**Merged pull requests:**
- Disable proc macro tests for tvOS and watchOS [\#243](https://github.com/sonos/dinghy/pull/243) ([fredszaq](https://github.com/fredszaq))
- Introduce plugin platform [\#242](https://github.com/sonos/dinghy/pull/242) ([fredszaq](https://github.com/fredszaq))
- add `skip_source_copy` flag [\#241](https://github.com/sonos/dinghy/pull/241) ([fredszaq](https://github.com/fredszaq))
## [0.7.3](https://github.com/sonos/dinghy/tree/0.7.3) (2024-10-16)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.7.2...0.7.3)
**Merged pull requests:**
- XCode 16: profiles location has changed [\#239](https://github.com/sonos/dinghy/pull/239) ([kali](https://github.com/kali))
- accept licenses [\#236](https://github.com/sonos/dinghy/pull/236) ([kali](https://github.com/kali))
## [0.7.2](https://github.com/sonos/dinghy/tree/0.7.2) (2024-06-17)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.7.1...0.7.2)
**Closed issues:**
- About the build performance [\#232](https://github.com/sonos/dinghy/issues/232)
- xcrun: error: unable to find utility "devicectl", not a developer tool or in PATH [\#225](https://github.com/sonos/dinghy/issues/225)
**Merged pull requests:**
- remove deprecated atty and tempdir dependencies [\#235](https://github.com/sonos/dinghy/pull/235) ([fredszaq](https://github.com/fredszaq))
- cargo fmt [\#234](https://github.com/sonos/dinghy/pull/234) ([ThibautLorrainSonos](https://github.com/ThibautLorrainSonos))
- libclang path is in lib and not lib64 in ndk 26+ [\#233](https://github.com/sonos/dinghy/pull/233) ([ThibautLorrainSonos](https://github.com/ThibautLorrainSonos))
- Update github org [\#231](https://github.com/sonos/dinghy/pull/231) ([jayvdb](https://github.com/jayvdb))
- show id of ssh devices \(in addtion to their ip\) in all-devices command [\#230](https://github.com/sonos/dinghy/pull/230) ([fredszaq](https://github.com/fredszaq))
- Use matrix in CI for third tier apple simulators [\#228](https://github.com/sonos/dinghy/pull/228) ([simlay](https://github.com/simlay))
## [0.7.1](https://github.com/sonos/dinghy/tree/0.7.1) (2024-04-11)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.7.0...0.7.1)
**Merged pull requests:**
- fix macos binary upload [\#227](https://github.com/sonos/dinghy/pull/227) ([kali](https://github.com/kali))
- only warn if xcrun is missing [\#226](https://github.com/sonos/dinghy/pull/226) ([kali](https://github.com/kali))
- Bump msrv to 1.74 [\#224](https://github.com/sonos/dinghy/pull/224) ([simlay](https://github.com/simlay))
## [0.7.0](https://github.com/sonos/dinghy/tree/0.7.0) (2024-04-09)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.6.8...0.7.0)
**Closed issues:**
- Run test on iOS failed: No such file or directory \(os error 2\) [\#216](https://github.com/sonos/dinghy/issues/216)
- Support cargo-dinghy as a cargo test runner [\#210](https://github.com/sonos/dinghy/issues/210)
- Dinghy hangs on macOS-14 runner [\#207](https://github.com/sonos/dinghy/issues/207)
- Use `xcrun devicectl` to deploy to iOS devices. [\#204](https://github.com/sonos/dinghy/issues/204)
- Faiiled to build what use ndk\_context project [\#201](https://github.com/sonos/dinghy/issues/201)
- Add macOS M1 binary to release CI [\#200](https://github.com/sonos/dinghy/issues/200)
- Getting files generated by tests back? [\#199](https://github.com/sonos/dinghy/issues/199)
- `undefined symbol: ANativeWindow_setBuffersGeometry` with visual tests using `wgpu` [\#198](https://github.com/sonos/dinghy/issues/198)
- watchOS simulator support [\#194](https://github.com/sonos/dinghy/issues/194)
- tvOS simulator support [\#193](https://github.com/sonos/dinghy/issues/193)
- How to build with non-default crate features? [\#129](https://github.com/sonos/dinghy/issues/129)
**Merged pull requests:**
- use fs\_err when it make sense in dinghy\_lib [\#222](https://github.com/sonos/dinghy/pull/222) ([kali](https://github.com/kali))
- follow symlinks while walking dirs \(and improve error messages\) [\#221](https://github.com/sonos/dinghy/pull/221) ([kali](https://github.com/kali))
- fix when multiple Dinghy app are installed [\#220](https://github.com/sonos/dinghy/pull/220) ([kali](https://github.com/kali))
- add changelog generation script in ./pre-release.sh [\#219](https://github.com/sonos/dinghy/pull/219) ([fredszaq](https://github.com/fredszaq))
- better error message on pymobiledevice3 failure [\#218](https://github.com/sonos/dinghy/pull/218) ([kali](https://github.com/kali))
- android: fix SDK detection on mac. [\#217](https://github.com/sonos/dinghy/pull/217) ([ashdnazg](https://github.com/ashdnazg))
- Support for ios17 [\#215](https://github.com/sonos/dinghy/pull/215) ([kali](https://github.com/kali))
- Infer the platform when runner is called in standalone mode [\#213](https://github.com/sonos/dinghy/pull/213) ([fredszaq](https://github.com/fredszaq))
- Universal macos binary in CI [\#209](https://github.com/sonos/dinghy/pull/209) ([simlay](https://github.com/simlay))
- Test on macOS-13 \(x86\) and macOS-14 \(arm\) [\#206](https://github.com/sonos/dinghy/pull/206) ([simlay](https://github.com/simlay))
- android emulator needs to be installed for avdmanager to work [\#205](https://github.com/sonos/dinghy/pull/205) ([ThibautLorrainSonos](https://github.com/ThibautLorrainSonos))
- Support tvOS and watchOS simulators [\#203](https://github.com/sonos/dinghy/pull/203) ([simlay](https://github.com/simlay))
- use android emulator 32.1.15 in ci as 33.1.23 segfaults [\#202](https://github.com/sonos/dinghy/pull/202) ([fredszaq](https://github.com/fredszaq))
- Bump rustix from 0.38.14 to 0.38.19 [\#196](https://github.com/sonos/dinghy/pull/196) ([dependabot[bot]](https://github.com/apps/dependabot))
- Fix preferences path after Ventura [\#195](https://github.com/sonos/dinghy/pull/195) ([ldm0](https://github.com/ldm0))
## [0.6.8](https://github.com/sonos/dinghy/tree/0.6.8) (2023-09-29)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.6.7...0.6.8)
**Merged pull requests:**
- search android ndk also in ANDROID\_NDK env var and pass ANDROID\_NDK var to build if not set \(cmake compat\) [\#192](https://github.com/sonos/dinghy/pull/192) ([fredszaq](https://github.com/fredszaq))
## [0.6.7](https://github.com/sonos/dinghy/tree/0.6.7) (2023-09-14)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.6.6...0.6.7)
**Closed issues:**
- Maintain CHANGELOG [\#187](https://github.com/sonos/dinghy/issues/187)
**Merged pull requests:**
- Do not crash dinghy\_bindgen macro if DINGHY\_BUILD\_LIBCLANG\_PATH is not present [\#191](https://github.com/sonos/dinghy/pull/191) ([fredszaq](https://github.com/fredszaq))
## [0.6.6](https://github.com/sonos/dinghy/tree/0.6.6) (2023-09-14)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.6.5...0.6.6)
**Merged pull requests:**
- msrv 1.70 [\#190](https://github.com/sonos/dinghy/pull/190) ([fredszaq](https://github.com/fredszaq))
- fix bindgen config when using android ndk [\#189](https://github.com/sonos/dinghy/pull/189) ([fredszaq](https://github.com/fredszaq))
## [0.6.5](https://github.com/sonos/dinghy/tree/0.6.5) (2023-09-11)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.6.4...0.6.5)
**Closed issues:**
- `catch_unwind` broken on `armv7-apple-ios` [\#156](https://github.com/sonos/dinghy/issues/156)
**Merged pull requests:**
- properly setup clang++ as CXX in android auto platforms [\#188](https://github.com/sonos/dinghy/pull/188) ([fredszaq](https://github.com/fredszaq))
- cargo update \(procmacro2 failing to compile on nightly\) [\#186](https://github.com/sonos/dinghy/pull/186) ([fredszaq](https://github.com/fredszaq))
- make RUST\_BACKTRACE overwritable by --env [\#185](https://github.com/sonos/dinghy/pull/185) ([bestouff](https://github.com/bestouff))
- add legacy dinghy crate [\#184](https://github.com/sonos/dinghy/pull/184) ([fredszaq](https://github.com/fredszaq))
- bump msrv [\#183](https://github.com/sonos/dinghy/pull/183) ([kali](https://github.com/kali))
## [0.6.4](https://github.com/sonos/dinghy/tree/0.6.4) (2023-04-13)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.6.3...0.6.4)
**Merged pull requests:**
- use llvm-ar instead of the binutils one for android ndk 23+ [\#182](https://github.com/sonos/dinghy/pull/182) ([fredszaq](https://github.com/fredszaq))
## [0.6.3](https://github.com/sonos/dinghy/tree/0.6.3) (2022-11-18)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.6.2...0.6.3)
**Closed issues:**
- On the use `std::fs::ReadDir.next()` [\#177](https://github.com/sonos/dinghy/issues/177)
- failed to compile cargo-dinghy v0.3.2 - multiple packages link to native library git2 [\#66](https://github.com/sonos/dinghy/issues/66)
**Merged pull requests:**
- add all shared libs as args in run-with [\#181](https://github.com/sonos/dinghy/pull/181) ([fredszaq](https://github.com/fredszaq))
- bump iphone min version [\#180](https://github.com/sonos/dinghy/pull/180) ([kali](https://github.com/kali))
- add apt update [\#179](https://github.com/sonos/dinghy/pull/179) ([kali](https://github.com/kali))
## [0.6.2](https://github.com/sonos/dinghy/tree/0.6.2) (2022-08-09)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.6.1...0.6.2)
**Closed issues:**
- No more working on iOS devices 14.x [\#135](https://github.com/sonos/dinghy/issues/135)
- Cannot run tests on iOS 14 device [\#131](https://github.com/sonos/dinghy/issues/131)
- test --no-fail-fast is not recognized [\#127](https://github.com/sonos/dinghy/issues/127)
- Old android device using ro.product.cpu.abi rather than ro.product.cpu.abilist [\#109](https://github.com/sonos/dinghy/issues/109)
- ImportError: No module named six [\#6](https://github.com/sonos/dinghy/issues/6)
**Merged pull requests:**
- fix android toolchain discovery on macOS [\#178](https://github.com/sonos/dinghy/pull/178) ([fredszaq](https://github.com/fredszaq))
- Mention ios-deploy in docu [\#176](https://github.com/sonos/dinghy/pull/176) ([umgefahren](https://github.com/umgefahren))
## [0.6.1](https://github.com/sonos/dinghy/tree/0.6.1) (2022-08-04)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.6.0...0.6.1)
**Closed issues:**
- deploy to iOS devices with ios-deploy [\#166](https://github.com/sonos/dinghy/issues/166)
- No device support directory for iOS version 12.5 [\#165](https://github.com/sonos/dinghy/issues/165)
- lldb output is variable [\#158](https://github.com/sonos/dinghy/issues/158)
**Merged pull requests:**
- musl build fix [\#175](https://github.com/sonos/dinghy/pull/175) ([fredszaq](https://github.com/fredszaq))
- display compile errors and check cargo exit status in run-with [\#174](https://github.com/sonos/dinghy/pull/174) ([fredszaq](https://github.com/fredszaq))
- Dependency cleanup [\#172](https://github.com/sonos/dinghy/pull/172) ([madsmtm](https://github.com/madsmtm))
- Support newer versions of the OpenSSL x509 text format [\#171](https://github.com/sonos/dinghy/pull/171) ([madsmtm](https://github.com/madsmtm))
## [0.6.0](https://github.com/sonos/dinghy/tree/0.6.0) (2022-07-27)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.5.1...0.6.0)
**Closed issues:**
- Share resource files between tests [\#161](https://github.com/sonos/dinghy/issues/161)
**Merged pull requests:**
- some ios signing logging adjusts [\#170](https://github.com/sonos/dinghy/pull/170) ([kali](https://github.com/kali))
- Logging overhaul [\#169](https://github.com/sonos/dinghy/pull/169) ([fredszaq](https://github.com/fredszaq))
- use ios-deploy, remove in-house rust partial port [\#168](https://github.com/sonos/dinghy/pull/168) ([kali](https://github.com/kali))
- introduce run-with subcommand and transparent copy of files in runner args [\#167](https://github.com/sonos/dinghy/pull/167) ([fredszaq](https://github.com/fredszaq))
- do not copy ad-hoc rsync on device if file exists [\#164](https://github.com/sonos/dinghy/pull/164) ([fredszaq](https://github.com/fredszaq))
- Use package name instead of runnable id for dir on target [\#163](https://github.com/sonos/dinghy/pull/163) ([fredszaq](https://github.com/fredszaq))
- support android ndk 23 and up [\#162](https://github.com/sonos/dinghy/pull/162) ([fredszaq](https://github.com/fredszaq))
- Broken implicit wp dep [\#160](https://github.com/sonos/dinghy/pull/160) ([kali](https://github.com/kali))
## [0.5.1](https://github.com/sonos/dinghy/tree/0.5.1) (2022-07-08)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.5.0...0.5.1)
**Merged pull requests:**
- try to make the excluded bug appear in CI [\#159](https://github.com/sonos/dinghy/pull/159) ([kali](https://github.com/kali))
## [0.5.0](https://github.com/sonos/dinghy/tree/0.5.0) (2022-07-06)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.4.71...0.5.0)
**Closed issues:**
- cargo install dinghy takes too long [\#39](https://github.com/sonos/dinghy/issues/39)
**Merged pull requests:**
- Remove bundled cargo [\#157](https://github.com/sonos/dinghy/pull/157) ([fredszaq](https://github.com/fredszaq))
- Update cargo to v0.62 [\#154](https://github.com/sonos/dinghy/pull/154) ([madsmtm](https://github.com/madsmtm))
## [0.4.71](https://github.com/sonos/dinghy/tree/0.4.71) (2022-03-21)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.4.70...0.4.71)
**Closed issues:**
- Support for aarch64-apple-ios-sim in dinghy [\#147](https://github.com/sonos/dinghy/issues/147)
**Merged pull requests:**
- Fix iOS bundle to make app use the full screen [\#152](https://github.com/sonos/dinghy/pull/152) ([simlay](https://github.com/simlay))
- Initial stuff for aarch64 ios simulator support [\#151](https://github.com/sonos/dinghy/pull/151) ([simlay](https://github.com/simlay))
## [0.4.70](https://github.com/sonos/dinghy/tree/0.4.70) (2022-03-18)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.4.69...0.4.70)
**Merged pull requests:**
- bump cargo [\#150](https://github.com/sonos/dinghy/pull/150) ([kali](https://github.com/kali))
## [0.4.69](https://github.com/sonos/dinghy/tree/0.4.69) (2022-03-17)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.4.68...0.4.69)
**Closed issues:**
- Rust 2021 Edition [\#148](https://github.com/sonos/dinghy/issues/148)
**Merged pull requests:**
- Fix iOS simulator tests with lldb 13 [\#149](https://github.com/sonos/dinghy/pull/149) ([simlay](https://github.com/simlay))
- tests are actually runnable on host platform [\#146](https://github.com/sonos/dinghy/pull/146) ([kali](https://github.com/kali))
- Fix ci weirdness [\#145](https://github.com/sonos/dinghy/pull/145) ([kali](https://github.com/kali))
- Fix iOS 9.3 [\#143](https://github.com/sonos/dinghy/pull/143) ([madsmtm](https://github.com/madsmtm))
- Fix "Developer" typo [\#142](https://github.com/sonos/dinghy/pull/142) ([madsmtm](https://github.com/madsmtm))
## [0.4.68](https://github.com/sonos/dinghy/tree/0.4.68) (2022-01-19)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.4.67...0.4.68)
**Merged pull requests:**
- Dont run tests for `proc-macro` crates [\#144](https://github.com/sonos/dinghy/pull/144) ([madsmtm](https://github.com/madsmtm))
## [0.4.67](https://github.com/sonos/dinghy/tree/0.4.67) (2021-12-06)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.4.66...0.4.67)
**Merged pull requests:**
- add support for custom profiles [\#140](https://github.com/sonos/dinghy/pull/140) ([fredszaq](https://github.com/fredszaq))
## [0.4.66](https://github.com/sonos/dinghy/tree/0.4.66) (2021-11-29)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.4.63...0.4.66)
**Closed issues:**
- test-app build fails with unable to find library -lgcc [\#138](https://github.com/sonos/dinghy/issues/138)
**Merged pull requests:**
- manual import from anyhow [\#139](https://github.com/sonos/dinghy/pull/139) ([kali](https://github.com/kali))
## [0.4.63](https://github.com/sonos/dinghy/tree/0.4.63) (2021-11-04)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.4.62...0.4.63)
**Closed issues:**
- Support building crates that use resolver 2 [\#133](https://github.com/sonos/dinghy/issues/133)
**Merged pull requests:**
- update cargo and other dependencies [\#137](https://github.com/sonos/dinghy/pull/137) ([fredszaq](https://github.com/fredszaq))
## [0.4.62](https://github.com/sonos/dinghy/tree/0.4.62) (2021-07-28)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.4.61...0.4.62)
**Merged pull requests:**
- Bump cargo [\#134](https://github.com/sonos/dinghy/pull/134) ([kali](https://github.com/kali))
- Fixes the iOS test command [\#132](https://github.com/sonos/dinghy/pull/132) ([Robert-Steiner](https://github.com/Robert-Steiner))
## [0.4.61](https://github.com/sonos/dinghy/tree/0.4.61) (2021-02-15)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.4.60...0.4.61)
## [0.4.60](https://github.com/sonos/dinghy/tree/0.4.60) (2021-02-15)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.4.59...0.4.60)
## [0.4.59](https://github.com/sonos/dinghy/tree/0.4.59) (2021-02-15)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.4.57...0.4.59)
## [0.4.57](https://github.com/sonos/dinghy/tree/0.4.57) (2021-02-15)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.4.55...0.4.57)
## [0.4.55](https://github.com/sonos/dinghy/tree/0.4.55) (2021-02-15)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.4.54...0.4.55)
## [0.4.54](https://github.com/sonos/dinghy/tree/0.4.54) (2021-02-15)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.4.53...0.4.54)
## [0.4.53](https://github.com/sonos/dinghy/tree/0.4.53) (2021-02-15)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.4.52...0.4.53)
## [0.4.52](https://github.com/sonos/dinghy/tree/0.4.52) (2021-02-15)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.4.51...0.4.52)
## [0.4.51](https://github.com/sonos/dinghy/tree/0.4.51) (2021-02-15)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.4.50...0.4.51)
## [0.4.50](https://github.com/sonos/dinghy/tree/0.4.50) (2021-02-12)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.4.48...0.4.50)
## [0.4.48](https://github.com/sonos/dinghy/tree/0.4.48) (2021-02-09)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.4.47...0.4.48)
## [0.4.47](https://github.com/sonos/dinghy/tree/0.4.47) (2021-02-09)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.4.46...0.4.47)
## [0.4.46](https://github.com/sonos/dinghy/tree/0.4.46) (2021-02-09)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.4.45...0.4.46)
## [0.4.45](https://github.com/sonos/dinghy/tree/0.4.45) (2021-02-09)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.4.43...0.4.45)
## [0.4.43](https://github.com/sonos/dinghy/tree/0.4.43) (2021-02-09)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.4.41...0.4.43)
**Closed issues:**
- don't work for ios 13.3 on osx 10.15.5 [\#116](https://github.com/sonos/dinghy/issues/116)
**Merged pull requests:**
- Maintenance [\#130](https://github.com/sonos/dinghy/pull/130) ([kali](https://github.com/kali))
- Better error message on rsync exec [\#128](https://github.com/sonos/dinghy/pull/128) ([fredszaq](https://github.com/fredszaq))
- Fix some grammar and typos in README.md [\#126](https://github.com/sonos/dinghy/pull/126) ([geophree](https://github.com/geophree))
## [0.4.41](https://github.com/sonos/dinghy/tree/0.4.41) (2020-10-16)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.4.40...0.4.41)
**Closed issues:**
- Copies libdl.so to Android, causing failure to run [\#124](https://github.com/sonos/dinghy/issues/124)
- Dinghy on Linux [\#121](https://github.com/sonos/dinghy/issues/121)
**Merged pull requests:**
- Prevent deployment of libdl.so on Android [\#125](https://github.com/sonos/dinghy/pull/125) ([tom-bowles](https://github.com/tom-bowles))
## [0.4.40](https://github.com/sonos/dinghy/tree/0.4.40) (2020-09-10)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.4.39...0.4.40)
**Merged pull requests:**
- Check Android NDK in non legacy path [\#123](https://github.com/sonos/dinghy/pull/123) ([kafji](https://github.com/kafji))
- Log on Android NDK not found [\#122](https://github.com/sonos/dinghy/pull/122) ([kafji](https://github.com/kafji))
## [0.4.39](https://github.com/sonos/dinghy/tree/0.4.39) (2020-08-05)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.4.38...0.4.39)
**Merged pull requests:**
- iOS environment variable support [\#120](https://github.com/sonos/dinghy/pull/120) ([Jasper-Bekkers](https://github.com/Jasper-Bekkers))
- Add arm64e support [\#119](https://github.com/sonos/dinghy/pull/119) ([Jasper-Bekkers](https://github.com/Jasper-Bekkers))
## [0.4.38](https://github.com/sonos/dinghy/tree/0.4.38) (2020-06-18)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.4.37...0.4.38)
**Merged pull requests:**
- Ssh arg port order [\#118](https://github.com/sonos/dinghy/pull/118) ([kali](https://github.com/kali))
- Features bugfix [\#117](https://github.com/sonos/dinghy/pull/117) ([kali](https://github.com/kali))
## [0.4.37](https://github.com/sonos/dinghy/tree/0.4.37) (2020-05-29)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.4.35...0.4.37)
**Closed issues:**
- Long term solution for URL regression [\#101](https://github.com/sonos/dinghy/issues/101)
**Merged pull requests:**
- add missing x86\_64 android device [\#115](https://github.com/sonos/dinghy/pull/115) ([MarcTreySonos](https://github.com/MarcTreySonos))
- experimental shell expansion in run [\#114](https://github.com/sonos/dinghy/pull/114) ([kali](https://github.com/kali))
- avoid createing huge and useless PKG\_CONFIG\_LIBDIR [\#113](https://github.com/sonos/dinghy/pull/113) ([kali](https://github.com/kali))
- Allow SSH devices to use the host toolchain [\#112](https://github.com/sonos/dinghy/pull/112) ([fredszaq](https://github.com/fredszaq))
## [0.4.35](https://github.com/sonos/dinghy/tree/0.4.35) (2020-04-09)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.4.34...0.4.35)
**Merged pull requests:**
- bump cargo, and switch to anyhow [\#111](https://github.com/sonos/dinghy/pull/111) ([kali](https://github.com/kali))
- eradicate two warnings [\#108](https://github.com/sonos/dinghy/pull/108) ([kali](https://github.com/kali))
## [0.4.34](https://github.com/sonos/dinghy/tree/0.4.34) (2020-04-07)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.4.33...0.4.34)
## [0.4.33](https://github.com/sonos/dinghy/tree/0.4.33) (2020-04-07)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.4.31...0.4.33)
**Merged pull requests:**
- build bench in release mode [\#110](https://github.com/sonos/dinghy/pull/110) ([kali](https://github.com/kali))
- some android auto target fixes [\#91](https://github.com/sonos/dinghy/pull/91) ([kali](https://github.com/kali))
## [0.4.31](https://github.com/sonos/dinghy/tree/0.4.31) (2020-04-07)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.4.30...0.4.31)
## [0.4.30](https://github.com/sonos/dinghy/tree/0.4.30) (2020-04-06)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.4.29...0.4.30)
## [0.4.29](https://github.com/sonos/dinghy/tree/0.4.29) (2020-04-06)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.4.28...0.4.29)
## [0.4.28](https://github.com/sonos/dinghy/tree/0.4.28) (2020-04-06)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.4.27...0.4.28)
## [0.4.27](https://github.com/sonos/dinghy/tree/0.4.27) (2020-04-06)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.4.26...0.4.27)
## [0.4.26](https://github.com/sonos/dinghy/tree/0.4.26) (2020-04-06)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.4.25...0.4.26)
## [0.4.25](https://github.com/sonos/dinghy/tree/0.4.25) (2020-04-06)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.4.24...0.4.25)
**Merged pull requests:**
- Daxpedia windows fix [\#107](https://github.com/sonos/dinghy/pull/107) ([kali](https://github.com/kali))
- Fix targets which does not have sysroot [\#106](https://github.com/sonos/dinghy/pull/106) ([MarcTreySonos](https://github.com/MarcTreySonos))
- add upload rsync binary step for the ssh devices [\#105](https://github.com/sonos/dinghy/pull/105) ([MarcTreySonos](https://github.com/MarcTreySonos))
- Android ndk 19+ documentation [\#86](https://github.com/sonos/dinghy/pull/86) ([Deluvi](https://github.com/Deluvi))
## [0.4.24](https://github.com/sonos/dinghy/tree/0.4.24) (2020-02-13)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.4.23...0.4.24)
**Merged pull requests:**
- libc++\_shared.so must be included for android deployment [\#103](https://github.com/sonos/dinghy/pull/103) ([MarcTreySonos](https://github.com/MarcTreySonos))
## [0.4.23](https://github.com/sonos/dinghy/tree/0.4.23) (2020-02-13)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.4.22...0.4.23)
**Fixed bugs:**
- Revert `url` lib bump to 2.1.0 [\#104](https://github.com/sonos/dinghy/pull/104) ([Deluvi](https://github.com/Deluvi))
## [0.4.22](https://github.com/sonos/dinghy/tree/0.4.22) (2020-01-30)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.4.21...0.4.22)
**Closed issues:**
- dinghy\_test::test\_file\_path doesn't work when running tests natively on desktop platform [\#99](https://github.com/sonos/dinghy/issues/99)
**Merged pull requests:**
- fix ci [\#102](https://github.com/sonos/dinghy/pull/102) ([kali](https://github.com/kali))
- use crate url 2.0.0 [\#97](https://github.com/sonos/dinghy/pull/97) ([MarcTreySonos](https://github.com/MarcTreySonos))
## [0.4.21](https://github.com/sonos/dinghy/tree/0.4.21) (2020-01-27)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.4.20...0.4.21)
**Merged pull requests:**
- Refactor bindgen related code [\#98](https://github.com/sonos/dinghy/pull/98) ([Deluvi](https://github.com/Deluvi))
## [0.4.20](https://github.com/sonos/dinghy/tree/0.4.20) (2020-01-16)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.4.19...0.4.20)
**Merged pull requests:**
- Change IosSimDevice to use xcrun simctl launch rather than lldb [\#96](https://github.com/sonos/dinghy/pull/96) ([simlay](https://github.com/simlay))
## [0.4.19](https://github.com/sonos/dinghy/tree/0.4.19) (2020-01-13)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.4.18...0.4.19)
**Merged pull requests:**
- Updated dependencies [\#95](https://github.com/sonos/dinghy/pull/95) ([simlay](https://github.com/simlay))
## [0.4.18](https://github.com/sonos/dinghy/tree/0.4.18) (2019-10-28)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.4.17...0.4.18)
**Merged pull requests:**
- add a "private" framework search path [\#94](https://github.com/sonos/dinghy/pull/94) ([kali](https://github.com/kali))
## [0.4.17](https://github.com/sonos/dinghy/tree/0.4.17) (2019-10-24)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.4.16...0.4.17)
## [0.4.16](https://github.com/sonos/dinghy/tree/0.4.16) (2019-09-16)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.4.15...0.4.16)
## [0.4.15](https://github.com/sonos/dinghy/tree/0.4.15) (2019-09-16)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.4.14...0.4.15)
**Merged pull requests:**
- try on bionic [\#93](https://github.com/sonos/dinghy/pull/93) ([kali](https://github.com/kali))
## [0.4.14](https://github.com/sonos/dinghy/tree/0.4.14) (2019-09-16)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.4.13...0.4.14)
## [0.4.13](https://github.com/sonos/dinghy/tree/0.4.13) (2019-09-08)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.4.12...0.4.13)
## [0.4.12](https://github.com/sonos/dinghy/tree/0.4.12) (2019-08-29)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.4.11...0.4.12)
## [0.4.11](https://github.com/sonos/dinghy/tree/0.4.11) (2019-05-09)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.4.10...0.4.11)
**Closed issues:**
- Add android app platform version to specify/match a minSdk Version [\#85](https://github.com/sonos/dinghy/issues/85)
**Merged pull requests:**
- bump osx [\#90](https://github.com/sonos/dinghy/pull/90) ([kali](https://github.com/kali))
- Prevent library copy of lib file in the toolchain's sysroot when running on a remote device [\#88](https://github.com/sonos/dinghy/pull/88) ([Deluvi](https://github.com/Deluvi))
- Make automatic platform decision for a given device deterministic [\#84](https://github.com/sonos/dinghy/pull/84) ([Deluvi](https://github.com/Deluvi))
## [0.4.10](https://github.com/sonos/dinghy/tree/0.4.10) (2019-02-26)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.4.8...0.4.10)
**Closed issues:**
- Fails to sign when there is any non .mobileprovision file in Profiles folder [\#77](https://github.com/sonos/dinghy/issues/77)
**Merged pull requests:**
- Android ndk 19+ API level choice [\#83](https://github.com/sonos/dinghy/pull/83) ([Deluvi](https://github.com/Deluvi))
- Fix multiple versions of the same dynamic lib used [\#82](https://github.com/sonos/dinghy/pull/82) ([Deluvi](https://github.com/Deluvi))
## [0.4.8](https://github.com/sonos/dinghy/tree/0.4.8) (2019-02-14)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.4.7...0.4.8)
**Merged pull requests:**
- reactivate toolchain binary shims as they are needed by some projects [\#79](https://github.com/sonos/dinghy/pull/79) ([fredszaq](https://github.com/fredszaq))
## [0.4.7](https://github.com/sonos/dinghy/tree/0.4.7) (2019-02-13)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.4.6...0.4.7)
## [0.4.6](https://github.com/sonos/dinghy/tree/0.4.6) (2019-02-12)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.4.5...0.4.6)
**Merged pull requests:**
- various fix for ios [\#78](https://github.com/sonos/dinghy/pull/78) ([kali](https://github.com/kali))
## [0.4.5](https://github.com/sonos/dinghy/tree/0.4.5) (2019-02-09)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.2.16...0.4.5)
**Closed issues:**
- Failure linking backtrace-sys on android: undefined reference to 'getpagesize' [\#61](https://github.com/sonos/dinghy/issues/61)
- Error when I try to use dinghy [\#48](https://github.com/sonos/dinghy/issues/48)
- Should benchmarks return performance numbers? [\#44](https://github.com/sonos/dinghy/issues/44)
- MacOS: Unexpected end of JSON with Xcode CommandLineTools [\#32](https://github.com/sonos/dinghy/issues/32)
**Merged pull requests:**
- more android fixes and debug info [\#76](https://github.com/sonos/dinghy/pull/76) ([kali](https://github.com/kali))
- refactor + android clang support [\#75](https://github.com/sonos/dinghy/pull/75) ([kali](https://github.com/kali))
- Script device [\#74](https://github.com/sonos/dinghy/pull/74) ([kali](https://github.com/kali))
- update cargo to 0.32 - edition 2018 support [\#73](https://github.com/sonos/dinghy/pull/73) ([fredszaq](https://github.com/fredszaq))
- Fix typos in documentation [\#69](https://github.com/sonos/dinghy/pull/69) ([adrienball](https://github.com/adrienball))
- support --no-run [\#67](https://github.com/sonos/dinghy/pull/67) ([kali](https://github.com/kali))
- filename conflict in "run" [\#65](https://github.com/sonos/dinghy/pull/65) ([kali](https://github.com/kali))
- some trace! level info, plus move exe to target/ in bundle [\#64](https://github.com/sonos/dinghy/pull/64) ([kali](https://github.com/kali))
- Bump dependencies [\#63](https://github.com/sonos/dinghy/pull/63) ([Eijebong](https://github.com/Eijebong))
- add static linking helper [\#62](https://github.com/sonos/dinghy/pull/62) ([MarcTreySonos](https://github.com/MarcTreySonos))
- Define defualt toolchain directory in .dinghy [\#60](https://github.com/sonos/dinghy/pull/60) ([rtmvc](https://github.com/rtmvc))
- do not copy target in target [\#58](https://github.com/sonos/dinghy/pull/58) ([kali](https://github.com/kali))
- Update build\_env.rs [\#57](https://github.com/sonos/dinghy/pull/57) ([warent](https://github.com/warent))
- Update dinghy crate to cargo-dinghy [\#56](https://github.com/sonos/dinghy/pull/56) ([nebuto](https://github.com/nebuto))
- Allow debug build mode arg [\#55](https://github.com/sonos/dinghy/pull/55) ([rtmvc](https://github.com/rtmvc))
- Set permissions before copy [\#54](https://github.com/sonos/dinghy/pull/54) ([rtmvc](https://github.com/rtmvc))
- Copy libs for host platform too [\#53](https://github.com/sonos/dinghy/pull/53) ([rtmvc](https://github.com/rtmvc))
- Warn if package filtered on platform [\#52](https://github.com/sonos/dinghy/pull/52) ([rtmvc](https://github.com/rtmvc))
- Copy ios libs [\#51](https://github.com/sonos/dinghy/pull/51) ([rtmvc](https://github.com/rtmvc))
- Copy .so dependencies in target directory for builds [\#50](https://github.com/sonos/dinghy/pull/50) ([rtmvc](https://github.com/rtmvc))
- Strip executable copy and not original as cargo might not regenerate … [\#49](https://github.com/sonos/dinghy/pull/49) ([rtmvc](https://github.com/rtmvc))
- Copy .so dependencies in target directory for builds [\#47](https://github.com/sonos/dinghy/pull/47) ([kali](https://github.com/kali))
- Strip [\#46](https://github.com/sonos/dinghy/pull/46) ([kali](https://github.com/kali))
- Fix nightly compiler error [\#45](https://github.com/sonos/dinghy/pull/45) ([pitdicker](https://github.com/pitdicker))
- Task/cc rs compat [\#42](https://github.com/sonos/dinghy/pull/42) ([kali](https://github.com/kali))
- 0.3 [\#40](https://github.com/sonos/dinghy/pull/40) ([rtmvc](https://github.com/rtmvc))
## [0.2.16](https://github.com/sonos/dinghy/tree/0.2.16) (2018-02-05)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.2.15...0.2.16)
**Closed issues:**
- Android emulator support? [\#37](https://github.com/sonos/dinghy/issues/37)
- Error installing dinghy [\#36](https://github.com/sonos/dinghy/issues/36)
## [0.2.15](https://github.com/sonos/dinghy/tree/0.2.15) (2017-11-23)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.2.14...0.2.15)
**Closed issues:**
- MacOS Cannot build with Xcode 9 \(9A235\) [\#29](https://github.com/sonos/dinghy/issues/29)
**Merged pull requests:**
- Update to cargo 0.22, fix a few warnings [\#33](https://github.com/sonos/dinghy/pull/33) ([ryanpresciense](https://github.com/ryanpresciense))
## [0.2.14](https://github.com/sonos/dinghy/tree/0.2.14) (2017-10-05)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.2.13...0.2.14)
**Closed issues:**
- Refactoring some common logic into a library [\#4](https://github.com/sonos/dinghy/issues/4)
**Merged pull requests:**
- Bump to xcode 9 [\#31](https://github.com/sonos/dinghy/pull/31) ([klefevre](https://github.com/klefevre))
- Bump dependencies [\#30](https://github.com/sonos/dinghy/pull/30) ([klefevre](https://github.com/klefevre))
## [0.2.13](https://github.com/sonos/dinghy/tree/0.2.13) (2017-07-04)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.2.12...0.2.13)
## [0.2.12](https://github.com/sonos/dinghy/tree/0.2.12) (2017-07-04)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.2.11...0.2.12)
**Closed issues:**
- Allow specifying port number for ssh [\#26](https://github.com/sonos/dinghy/issues/26)
- Need some way of deploying test\_data even if they are included in .gitignore [\#24](https://github.com/sonos/dinghy/issues/24)
**Merged pull requests:**
- Bump dependencies [\#28](https://github.com/sonos/dinghy/pull/28) ([klefevre](https://github.com/klefevre))
- Implement clean\_app in ssh, add path and port options [\#27](https://github.com/sonos/dinghy/pull/27) ([azdlowry](https://github.com/azdlowry))
- Allow copy\_git\_ignored to be added to test data [\#25](https://github.com/sonos/dinghy/pull/25) ([azdlowry](https://github.com/azdlowry))
- Android Improvements [\#23](https://github.com/sonos/dinghy/pull/23) ([azdlowry](https://github.com/azdlowry))
## [0.2.11](https://github.com/sonos/dinghy/tree/0.2.11) (2017-06-09)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.2.10...0.2.11)
**Merged pull requests:**
- add DINGHY=1 env var when running on Android [\#22](https://github.com/sonos/dinghy/pull/22) ([fredszaq](https://github.com/fredszaq))
## [0.2.10](https://github.com/sonos/dinghy/tree/0.2.10) (2017-05-29)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.2.9...0.2.10)
**Merged pull requests:**
- More options [\#21](https://github.com/sonos/dinghy/pull/21) ([klefevre](https://github.com/klefevre))
## [0.2.9](https://github.com/sonos/dinghy/tree/0.2.9) (2017-04-21)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.2.8...0.2.9)
**Closed issues:**
- make\_linux\_app is creates more and more files each time [\#19](https://github.com/sonos/dinghy/issues/19)
**Merged pull requests:**
- support more android architechtures [\#20](https://github.com/sonos/dinghy/pull/20) ([dten](https://github.com/dten))
## [0.2.8](https://github.com/sonos/dinghy/tree/0.2.8) (2017-04-21)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.2.7...0.2.8)
## [0.2.7](https://github.com/sonos/dinghy/tree/0.2.7) (2017-04-18)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.2.6...0.2.7)
**Merged pull requests:**
- Automatic versioning from Cargo.toml [\#18](https://github.com/sonos/dinghy/pull/18) ([klefevre](https://github.com/klefevre))
## [0.2.6](https://github.com/sonos/dinghy/tree/0.2.6) (2017-04-18)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.2.5...0.2.6)
**Closed issues:**
- consider hiding scp/rsyinc output [\#16](https://github.com/sonos/dinghy/issues/16)
- paths for .dinghy.toml [\#14](https://github.com/sonos/dinghy/issues/14)
## [0.2.5](https://github.com/sonos/dinghy/tree/0.2.5) (2017-04-15)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.2.4...0.2.5)
**Closed issues:**
- run cargo examples? [\#11](https://github.com/sonos/dinghy/issues/11)
**Merged pull requests:**
- Workspace test data [\#17](https://github.com/sonos/dinghy/pull/17) ([kali](https://github.com/kali))
## [0.2.4](https://github.com/sonos/dinghy/tree/0.2.4) (2017-04-11)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.2.3...0.2.4)
**Merged pull requests:**
- Add spec support for build, test and bench subcommands [\#15](https://github.com/sonos/dinghy/pull/15) ([klefevre](https://github.com/klefevre))
## [0.2.3](https://github.com/sonos/dinghy/tree/0.2.3) (2017-03-31)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.2.2...0.2.3)
**Merged pull requests:**
- fix ssh target triple that was hardcoded instead of read from config [\#12](https://github.com/sonos/dinghy/pull/12) ([fredszaq](https://github.com/fredszaq))
## [0.2.2](https://github.com/sonos/dinghy/tree/0.2.2) (2017-03-31)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.2.1...0.2.2)
## [0.2.1](https://github.com/sonos/dinghy/tree/0.2.1) (2017-03-29)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.2.0...0.2.1)
**Merged pull requests:**
- chmod from windows needs fixing [\#13](https://github.com/sonos/dinghy/pull/13) ([dten](https://github.com/dten))
## [0.2.0](https://github.com/sonos/dinghy/tree/0.2.0) (2017-02-10)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.1.13...0.2.0)
**Merged pull requests:**
- Ssh support [\#10](https://github.com/sonos/dinghy/pull/10) ([kali](https://github.com/kali))
- make dinghy work on linux [\#9](https://github.com/sonos/dinghy/pull/9) ([fredszaq](https://github.com/fredszaq))
## [0.1.13](https://github.com/sonos/dinghy/tree/0.1.13) (2017-01-31)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.1.12...0.1.13)
**Merged pull requests:**
- Support for multiple travis configurations [\#8](https://github.com/sonos/dinghy/pull/8) ([kali](https://github.com/kali))
## [0.1.12](https://github.com/sonos/dinghy/tree/0.1.12) (2017-01-26)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.1.11...0.1.12)
## [0.1.11](https://github.com/sonos/dinghy/tree/0.1.11) (2017-01-26)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.1.10...0.1.11)
**Closed issues:**
- unresolved name `ensure_shim` when trying to cargo install [\#5](https://github.com/sonos/dinghy/issues/5)
**Merged pull requests:**
- accept any android device name that isn't whitespace [\#7](https://github.com/sonos/dinghy/pull/7) ([dten](https://github.com/dten))
## [0.1.10](https://github.com/sonos/dinghy/tree/0.1.10) (2017-01-24)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.1.9...0.1.10)
## [0.1.9](https://github.com/sonos/dinghy/tree/0.1.9) (2017-01-23)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.1.8...0.1.9)
## [0.1.8](https://github.com/sonos/dinghy/tree/0.1.8) (2017-01-23)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.1.7...0.1.8)
## [0.1.7](https://github.com/sonos/dinghy/tree/0.1.7) (2017-01-23)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.1.6...0.1.7)
## [0.1.6](https://github.com/sonos/dinghy/tree/0.1.6) (2017-01-18)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.1.5...0.1.6)
**Closed issues:**
- Link error when executing 'cargo install dinghy' [\#3](https://github.com/sonos/dinghy/issues/3)
## [0.1.5](https://github.com/sonos/dinghy/tree/0.1.5) (2017-01-03)
[Full Changelog](https://github.com/sonos/dinghy/compare/0.1.4...0.1.5)
## [0.1.4](https://github.com/sonos/dinghy/tree/0.1.4) (2016-12-16)
[Full Changelog](https://github.com/sonos/dinghy/compare/29e2f6c0b21a11575a4af2a9aba993ab3e2bf549...0.1.4)
**Merged pull requests:**
- missing env error message is wrong [\#2](https://github.com/sonos/dinghy/pull/2) ([dten](https://github.com/dten))
- Windows build [\#1](https://github.com/sonos/dinghy/pull/1) ([dten](https://github.com/dten))
\* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)*
================================================
FILE: Cargo.toml
================================================
[workspace]
resolver = "2"
members = [
"cargo-dinghy",
"dinghy-build",
"dinghy-lib",
"dinghy-test",
]
[workspace.dependencies]
cargo_metadata = "=0.14.0"
cargo-platform = "=0.1.8"
================================================
FILE: LICENSE
================================================
## License
Licensed under either of
* Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option.
### Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you, as defined in the Apache-2.0 license, shall
be dual licensed as above, without any additional terms or conditions.
================================================
FILE: LICENSE-APACHE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: LICENSE-MIT
================================================
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
================================================
FILE: README.md
================================================
# Dinghy



## What?
Dinghy is a `cargo` extension to bring cargo workflow to cross-compilation situations.
Dinghy is specifically useful with "small" processor-based devices, like
Android and iOS phones, or small single board computers like the Raspberry Pi.
Situations where native compilation is not possible, or not practical.
Initially tests and benches were the primary objective of Dinghy, but now
at Snips we use it to cross-compile our entire platform. This includes setting
up the stage for `cc` and `pkg-config` crates in one single place.
If you are a Rust library author, **you can run your tests and benches on
your smartphone in minutes.** And you should, at least once in a while.
## Demo
Let's try how BurntSushi's byteorder handles f32 on a few arm devices, two
smartphones, and a Raspberry Pi.

Phew. It works.
## How?
Once dinghy knows about your toolchains and devices, you will be able to run
tests and benches from a simple cargo command **in any cargo project**, most of
the time without altering them.
Just add `dinghy -d some_device` between `cargo` and its subcommand:
```
cargo dinghy -d my_android test
cargo dinghy -d my_raspberry bench
```
By default, without `-d`, Dinghy will make a native build, just like `cargo` would do.
## Getting started
Depending on your targets and your workstation, the ease of setting
up Dinghy can vary.
* [Android](docs/android.md) is relatively easy, specifically if you already are
a mobile developer.
* [iOS](docs/ios.md) setup has a lot of steps, but at least Apple provides everything
you will need. Once again, if you are an iOS developer, most of the heavy lifting has
been already done. And if you are not, be aware that you won't have to pay anything.
* [other remote ssh-accessible devices](docs/ssh.md) are the easiest from dinghy point of view,
but you will be on your own to obtain the toolchain for your device architecture and
operating system. If your device is a Raspberry Pi running raspbian, we can help. :)
## Advanced topics and features
* Some projects need [resources files](docs/files.md) for running their tests or benches. Dinghy
tries its best to make it work in as many project/target configurations as
possible but some projects need a bit of help.
* In some bigger projects, you may need to [filter](docs/filter.md) your project's members depending
on the platform you want to test.
* Passing [environment](docs/vars.md) variables to your executable may sometimes be useful.
* Dinghy offers an [overlay](docs/overlay.md) system to "add" stuff to your toolchain
sysroot. This allows you to add "stuff" to your build dependencies, like static libraries or headers
without altering the sysroot toolchain.
* The [`dinghy-build` crate](docs/dinghy-build.md) offers some `build.rs` features that are useful in
the context of cross-compilation.
## Using dinghy as a runner only
If your project already build for the target platform without dinghy and you only want to use dinghy to run code on a
device, you can use dinghy's bundled runner directly. You simply need to register the dinghy as a runner in `.cargo/config`.
Here's an example for all apple targets
```toml
[target.'cfg(all(any(target_arch="aarch64",target_arch="x86_64"),target_vendor="apple",any(target_os="ios",target_os="tvos",target_os="apple-watchos")))']
runner = "cargo dinghy runner --"
```
You can then run your tests directly with `cargo test --target aarch64-apple-ios-sim` for example.
Please note that the recommended way to use dinghy is as a cargo subcommand as it will set up quite a few things
automatically for your project to even build.
The runner will try to auto-detect the platform if it is not passed (as in the above example)
# License
Licensed under either of
* Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option.
## Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you, as defined in the Apache-2.0 license, shall
be dual licensed as above, without any additional terms or conditions.
================================================
FILE: cargo-dinghy/Cargo.toml
================================================
[package]
name = "cargo-dinghy"
version = "0.8.5-pre"
authors = ["Mathieu Poumeyrol <mathieu.poumeyrol@sonos>", "Thibaut Lorrain <thibaut.lorrain@sonos.com>"]
license = "MIT/Apache-2.0"
description = "Cross-compilation made easier"
homepage = "https://medium.com/snips-ai/dinghy-painless-rust-tests-and-benches-on-ios-and-android-c9f94f81d305#.c2sx7two8"
repository = "https://github.com/sonos/dinghy"
keywords = [
"tests", "mobile", "ios", "android", "cargo"
]
categories = [ "development-tools::cargo-plugins", "development-tools::testing" , "development-tools::profiling" ]
readme = "../README.md"
edition = "2021"
[badges]
travis-ci = { repository = "sonos/dinghy" }
[dependencies]
dinghy-lib = { path = "../dinghy-lib" }
log = "0.4"
clap = { version = "4", features = ["derive"] }
env_logger = "0.10"
anyhow = "1.0.57"
cargo_metadata.workspace=true
================================================
FILE: cargo-dinghy/src/cli.rs
================================================
use clap::{CommandFactory, Parser, Subcommand};
use std::collections::HashSet;
#[derive(Parser, Debug, Clone)]
#[command(author, version, about, long_about = None)]
pub struct DinghyGeneralArgs {
/// Use a specific platform
#[arg(long, short)]
pub platform: Option<String>,
/// Make output more verbose, can be passed multiple times
#[arg(long, short, action = clap::ArgAction::Count)]
pub verbose: u8,
/// Make output less verbose, can be passed multiple times
#[arg(long, short, action = clap::ArgAction::Count)]
pub quiet: u8,
/// Force the use of an overlay during project build, can be passed multiple times
#[arg(long, short)]
pub overlay: Vec<String>,
/// Env variables to set on target device e.g. RUST_TRACE=trace, can be passed multiple times
#[arg(long, short)]
pub env: Vec<String>,
/// Cleanup target device after completion
#[arg(long, short)]
pub cleanup: bool,
/// Strip executable before running it on target
#[arg(long, short)]
pub strip: bool,
/// Device hint
#[arg(long, short)]
pub device: Option<String>,
/// Either a dinghy subcommand (see cargo dinghy all-dinghy-subcommands) or a
/// cargo one (see cargo --list)
// this one is here so that the help generated by clap makes sense
pub subcommand: Vec<String>,
}
#[derive(Parser, Debug)]
pub struct SubCommandWrapper {
#[command(subcommand)]
subcommand: DinghySubcommand,
}
#[derive(Subcommand, Debug)]
pub enum DinghySubcommand {
/// List devices that can be used with Dinghy for the selected platform
Devices {},
/// List all devices that can be used with Dinghy
AllDevices {},
/// List all platforms known to dinghy
AllPlatforms {},
/// List all available dinghy subcommands
AllDinghySubcommands {},
/// Dinghy runner, used internally to run executables on targets
Runner { args: Vec<String> },
/// Build an artifact and run it on a target device using the provided wrapper
RunWith {
/// Wrapper crate to use to run the lib
#[arg(long, short('c'))]
wrapper_crate: String,
// TODO support executables / scripts as wrappers
// /// Wrapper executable to use to run the lib
// #[clap(long, short('e'))]
// wrapper_executable: Option<String>,
/// Arguments to cargo build for the artifact
lib_build_args: Vec<String>,
},
}
#[derive(Debug)]
pub enum DinghyMode {
DinghySubcommand(DinghySubcommand),
CargoSubcommand { args: Vec<String> },
Naked,
}
#[derive(Debug)]
pub struct DinghyCli {
pub args: DinghyGeneralArgs,
pub mode: DinghyMode,
}
impl DinghyCli {
pub fn parse() -> Self {
log::debug!("args {:?}", std::env::args().collect::<Vec<_>>());
let args = std::env::args().skip(1).skip_while(|it| it == "dinghy");
#[derive(Debug, Default)]
struct SplitArgs {
general_args: Vec<String>,
subcommand: Vec<String>,
}
let args_taking_value = DinghyGeneralArgs::command()
.get_arguments()
.filter_map(|arg| {
if arg.get_value_names().is_some() {
let mut values = vec![];
if let Some(shorts) = arg.get_short_and_visible_aliases() {
values
.append(&mut shorts.iter().map(|short| format!("-{}", short)).collect())
}
if let Some(longs) = arg.get_long_and_visible_aliases() {
values.append(&mut longs.iter().map(|long| format!("--{}", long)).collect())
}
if values.is_empty() {
None
} else {
Some(values)
}
} else {
None
}
})
.flatten()
.collect::<HashSet<_>>();
let split_args = args.fold(SplitArgs::default(), |mut split_args, elem| {
if !split_args.subcommand.is_empty() {
// we've started putting args in the sub command, let's continue
split_args.subcommand.push(elem)
} else {
if elem.starts_with("-") /* This is a new option */
|| split_args.general_args
.last()
.map(|it| args_taking_value.contains(it))
.unwrap_or(false)
/* value for the previous option */
{
split_args.general_args.push(elem)
} else {
// leve the start of the subcommand here so that clap can verify it is there
split_args.general_args.push(elem.clone());
// this is the start of the sub command
split_args.subcommand.push(elem)
}
}
split_args
});
let cli = DinghyCli {
args: Parser::parse_from(
vec!["dinghy".to_string()]
.into_iter()
.chain(split_args.general_args),
),
mode: split_args
.subcommand
.first()
.cloned()
.map(|subcommand| {
if DinghySubcommand::has_subcommand(&subcommand) {
DinghyMode::DinghySubcommand(
SubCommandWrapper::parse_from(
vec!["dinghy".to_string()]
.into_iter()
.chain(split_args.subcommand),
)
.subcommand,
)
} else {
DinghyMode::CargoSubcommand {
args: split_args.subcommand,
}
}
})
.unwrap_or(DinghyMode::Naked),
};
log::debug!("cli {:?}", cli);
cli
}
}
================================================
FILE: cargo-dinghy/src/main.rs
================================================
use std::convert::identity;
use std::env;
use std::env::current_dir;
use std::io::BufReader;
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use std::sync::Arc;
use cargo_metadata::camino::Utf8PathBuf;
use cargo_metadata::Message;
use log::{debug, error, info};
use dinghy_lib::config::dinghy_config;
use dinghy_lib::errors::*;
use dinghy_lib::project::Project;
use dinghy_lib::utils::{set_current_verbosity, user_facing_log, LogCommandExt};
use dinghy_lib::Dinghy;
use dinghy_lib::Platform;
use dinghy_lib::{Build, SetupArgs};
use dinghy_lib::{Device, Runnable};
use crate::cli::{DinghyCli, DinghyMode, DinghySubcommand, SubCommandWrapper};
mod cli;
fn main() {
env_logger::init_from_env(
env_logger::Env::new()
.filter("DINGHY_LOG")
.write_style("DINGHY_LOG_STYLE"),
);
let cli = DinghyCli::parse();
set_current_verbosity(cli.args.verbose as i8);
if let Err(e) = run_command(cli) {
error!("{:?}", e);
// positively ugly.
if e.to_string().contains("are filtered out on platform") {
std::process::exit(3)
} else {
std::process::exit(1)
}
}
}
fn run_command(cli: DinghyCli) -> Result<()> {
let conf = Arc::new(dinghy_config(current_dir()?)?);
let metadata = cargo_metadata::MetadataCommand::new().exec()?;
let project = Project::new(&conf, metadata);
let dinghy = Dinghy::probe(&conf)?;
let (platform, device) = select_platform_and_device_from_cli(&cli, &dinghy)?;
let setup_args = SetupArgs {
verbosity: cli.args.verbose as i8 - cli.args.quiet as i8,
forced_overlays: cli.args.overlay.clone(),
envs: cli.args.env.clone(),
cleanup: cli.args.cleanup,
strip: cli.args.strip, // TODO this should probably be configurable in the config as well
device_id: device.as_ref().map(|d| d.id().to_string()),
};
match cli.mode {
DinghyMode::CargoSubcommand { ref args } => {
let mut cmd = create_cargo_subcomand(&platform, &device, &project, &setup_args, args)?;
log::debug!("Launching {:?}", cmd);
let status = cmd.log_invocation(2).status()?;
log::debug!("done");
std::process::exit(status.code().unwrap_or_else(|| {
log::error!("Could not get cargo exit code");
-1
}));
}
DinghyMode::DinghySubcommand(DinghySubcommand::Runner { ref args }) => {
debug!("starting dinghy runner, args {:?}", args);
let exe = args.first().cloned().unwrap();
let exe_path = PathBuf::from(&exe);
let inferred_target = exe_path
.parent() // build type
.and_then(|path| {
if path
.file_name()
.map(|name| name.to_string_lossy() == "deps")
.unwrap_or(false)
{
path.parent()
} else {
Some(path)
}
})
.and_then(Path::parent) // either "target" for the host or the actual target name if we're cross compiling
.and_then(Path::file_name)
.map(|it| it.to_string_lossy());
debug!("inferred target {:?}", inferred_target);
let (mut final_platform, mut final_device) = (platform, device);
if let Some(inferred_target) = inferred_target {
if final_device.is_none()
&& final_platform.rustc_triple() != inferred_target
&& cli.args.platform.is_none()
{
let platform = dinghy
.platforms()
.into_iter()
.find(|p| p.rustc_triple() == inferred_target);
if let Some(platform) = platform {
let device = find_first_device_for_platform(&cli, &dinghy, &platform);
if let Some(device) = device {
info!("Runner was called without explicit platform, we found {} and device {}", platform.id(), device.id());
final_device = Some(device)
}
final_platform = platform;
}
}
};
if let Some(device) = final_device {
user_facing_log(
"Targeting",
&format!(
"platform {} and device {}",
final_platform.id(),
device.id()
),
0,
);
let exe_name = exe_path.file_name().unwrap().to_str().unwrap().to_string();
let exe_id = if exe_name == "rust_out" {
// rustdoc may run concurent runners all with the same exe name, fortunately the parent dir is
// different in that case so lets use that instead as an id
exe_path.parent().unwrap().file_name().unwrap().to_str().unwrap().to_string()
} else {
exe_name
};
let (args, files_in_run_args): (Vec<String>, Vec<Option<PathBuf>>) = args
.into_iter()
.skip(1)
.map(|arg| {
if arg.contains(std::path::MAIN_SEPARATOR) {
let path_buf = PathBuf::from(&arg);
if path_buf.exists() {
(
PathBuf::from(".")
.join(path_buf.file_name().unwrap())
.to_str()
.unwrap()
.to_string(),
Some(path_buf),
)
} else {
(arg.clone(), None)
}
} else {
(arg.clone(), None)
}
})
.unzip();
let files_in_run_args =
files_in_run_args.into_iter().filter_map(identity).collect();
let args_ref = args.iter().map(|s| &s[..]).collect::<Vec<_>>();
let envs_ref = cli.args.env.iter().map(|s| &s[..]).collect::<Vec<_>>();
final_platform.setup_env(&project, &setup_args)?;
let mut build = Build {
setup_args,
// TODO these should be probably read from the executable file
dynamic_libraries: vec![],
runnable: Runnable {
id: exe_id,
package_name: std::env::var("CARGO_PKG_NAME")?,
exe: PathBuf::from(exe).canonicalize()?,
// cargo launches the runner inside the dir of the crate
source: PathBuf::from(".").canonicalize()?,
skip_source_copy: conf.skip_source_copy,
},
target_path: project.metadata.target_directory.clone().into(),
files_in_run_args,
};
if cli.args.strip {
final_platform.strip(&mut build)?;
}
let bundle = device.run_app(
&project, &build, &args_ref,
&envs_ref, // TODO these are also in the SetupArgs
)?;
// TODO this is not done if the run fails
if cli.args.cleanup {
device.clean_app(&bundle)?;
}
} else {
bail!("No device for platform {}", final_platform.id())
}
Ok(())
}
DinghyMode::DinghySubcommand(DinghySubcommand::Devices {}) => {
match cli
.args
.platform
.as_ref()
.map(|name| dinghy.platform_by_name(name))
{
None => anyhow::bail!("No platform provided"),
Some(None) => anyhow::bail!("Unknown platform"),
Some(Some(platform)) => show_all_devices_for_platform(&dinghy, platform),
}
}
DinghyMode::DinghySubcommand(DinghySubcommand::AllDevices {}) => show_all_devices(&dinghy),
DinghyMode::DinghySubcommand(DinghySubcommand::AllPlatforms {}) => {
show_all_platforms(&dinghy)
}
DinghyMode::DinghySubcommand(DinghySubcommand::AllDinghySubcommands {}) => {
use clap::CommandFactory;
for sub in SubCommandWrapper::command().get_subcommands() {
println!(
"{}\n\t{}",
sub.get_name(),
sub.get_about().unwrap_or_default()
);
}
Ok(())
}
DinghyMode::DinghySubcommand(DinghySubcommand::RunWith {
wrapper_crate,
mut lib_build_args,
}) => {
let mut build_command = vec!["build".to_string(), "--message-format=json".to_string()];
build_command.append(&mut lib_build_args);
let mut build_cargo_cmd =
create_cargo_subcomand(&platform, &device, &project, &setup_args, &build_command)?;
log::debug!("Launching {:?}", build_cargo_cmd);
let mut child = build_cargo_cmd
.stdout(Stdio::piped())
.stderr(Stdio::inherit())
.log_invocation(2)
.spawn()?;
log::debug!("done");
let mut extra_libs = vec![];
let mut lib_file =
cargo_metadata::Message::parse_stream(BufReader::new(child.stdout.take().unwrap()))
.filter_map(|message| match message {
Ok(Message::CompilerArtifact(artifact)) => {
if artifact.target.kind.contains(&"dylib".to_string()) {
extra_libs.append(&mut artifact.filenames.clone())
}
Some(artifact)
}
Ok(Message::CompilerMessage(message)) => {
// TODO would be really nice to get color there but current version of
// TODO cargo-metadata doesn't seem to support it
eprintln!("{}", message.message);
None
}
Ok(Message::BuildFinished(build)) => {
if !build.success {
log::debug!("cargo reported a build failure");
}
None
}
Ok(Message::TextLine(text)) => {
eprintln!("{}", text);
None
}
_ => None,
})
.last()
.ok_or_else(|| anyhow!("cargo did not produce an artifact"))?
.filenames
.into_iter()
.next()
.ok_or_else(|| anyhow!("no file in cargo artifact"))?;
if !extra_libs.is_empty() {
// if we have some extra libs, this means we're dealing with a dynamically linked
// lib and we need to include the rust stdlib as well, let's try and find it in the
// rustup home
if let (Ok(rustup_home), Ok(rustup_toolchain)) = (
std::env::var("RUSTUP_HOME"),
std::env::var("RUSTUP_TOOLCHAIN"),
) {
let stdlib_dir = PathBuf::from(rustup_home)
.join("toolchains")
.join(rustup_toolchain)
.join("lib")
.join("rustlib")
.join(platform.rustc_triple())
.join("lib");
if let Ok(Some(stdlib)) = stdlib_dir.read_dir().map(|dir| {
dir.filter_map(|entry| {
entry
.and_then(|entry| {
entry.file_type().map(|file_type| {
if file_type.is_file()
&& entry
.path()
.file_stem()
.map(|stem| {
stem.to_string_lossy().starts_with("libstd-")
})
.unwrap_or(false)
&& entry
.path()
.extension()
.map(|ext| {
["so", "dll", "dylib"]
.contains(&ext.to_string_lossy().as_ref())
})
.unwrap_or(false)
{
Some(entry.path())
} else {
None
}
})
})
.unwrap_or(None)
})
.next()
}) {
if let Ok(stdlib_path_buf) = Utf8PathBuf::from_path_buf(stdlib) {
extra_libs.push(stdlib_path_buf)
}
}
}
}
let code = child.wait()?.code();
match code {
Some(0) => { /*expected*/ }
Some(c) => std::process::exit(c),
None => std::process::exit(-1),
}
if cli.args.strip {
let stripped_dir = lib_file
.parent()
.ok_or_else(|| anyhow!("failed to get lib dir"))?
.join("stripped");
std::fs::create_dir_all(&stripped_dir)?;
let stripped_lib_file = stripped_dir.join(
lib_file
.file_name()
.ok_or_else(|| anyhow!("failed to get lib name"))?,
);
std::fs::copy(lib_file, &stripped_lib_file)?;
let mut lib_build = Build {
setup_args: setup_args.clone(),
dynamic_libraries: vec![],
runnable: Runnable {
id: "".to_string(),
package_name: "".to_string(),
exe: stripped_lib_file.to_path_buf().into(),
source: Default::default(),
skip_source_copy: conf.skip_source_copy,
},
target_path: Default::default(),
files_in_run_args: vec![],
};
platform.strip(&mut lib_build)?;
std::fs::copy(lib_build.runnable.exe, &stripped_lib_file)?;
lib_file = stripped_lib_file;
}
let mut args = vec![
"run".to_string(),
"-p".to_string(),
wrapper_crate,
"--release".to_string(),
"--".to_string(),
lib_file.to_string(),
];
for extra_lib in extra_libs {
args.push(extra_lib.to_string())
}
let mut run_cargo_cmd =
create_cargo_subcomand(&platform, &device, &project, &setup_args, &args)?;
log::debug!("Launching {:?}", run_cargo_cmd);
let status = run_cargo_cmd.log_invocation(2).status()?;
log::debug!("done");
std::process::exit(status.code().unwrap_or_else(|| {
log::error!("Could not get cargo exit code");
-1
}));
}
DinghyMode::Naked => {
anyhow::bail!("Naked mode") // what should we do?
}
}
}
fn create_cargo_subcomand(
platform: &Arc<Box<dyn Platform>>,
device: &Option<Arc<Box<dyn Device>>>,
project: &Project,
setup_args: &SetupArgs,
args: &Vec<String>,
) -> Result<Command> {
info!(
"Targeting platform '{}' and device '{}'",
platform.id(),
device.as_ref().map(|it| it.id()).unwrap_or("<none>")
);
user_facing_log(
"Targeting",
&format!(
"platform {} and device {}",
platform.id(),
device.as_ref().map(|it| it.id()).unwrap_or("<none>")
),
0,
);
let cargo = env::var("CARGO")
.map(PathBuf::from)
.ok()
.unwrap_or_else(|| PathBuf::from("cargo"));
let mut cmd = Command::new(cargo);
for arg in args {
cmd.arg(arg);
}
platform.setup_env(&project, &setup_args)?;
Ok(cmd)
}
fn show_all_platforms(dinghy: &Dinghy) -> Result<()> {
let mut platforms = dinghy.platforms();
platforms.sort_by(|str1, str2| str1.id().cmp(&str2.id()));
for pf in platforms.iter() {
println!("* {} {}", pf.id(), pf.rustc_triple());
}
Ok(())
}
fn show_all_devices(dinghy: &Dinghy) -> Result<()> {
println!("List of available devices for all platforms:");
show_devices(&dinghy, None)
}
fn show_all_devices_for_platform(dinghy: &Dinghy, platform: Arc<Box<dyn Platform>>) -> Result<()> {
println!(
"List of available devices for platform '{}':",
platform.id()
);
show_devices(&dinghy, Some(platform))
}
fn show_devices(dinghy: &Dinghy, platform: Option<Arc<Box<dyn Platform>>>) -> Result<()> {
let devices = dinghy
.devices()
.into_iter()
.filter(|device| {
platform
.as_ref()
.map_or(true, |it| it.is_compatible_with(&***device))
})
.collect::<Vec<_>>();
if devices.is_empty() {
error!("No matching device found");
println!("No matching device found");
} else {
for device in devices {
let pf: Vec<_> = dinghy
.platforms()
.iter()
.filter(|pf| pf.is_compatible_with(&**device))
.cloned()
.collect();
println!("{}: {:?}", device, pf);
}
}
Ok(())
}
fn select_platform_and_device_from_cli(
cli: &DinghyCli,
dinghy: &Dinghy,
) -> Result<(Arc<Box<dyn Platform>>, Option<Arc<Box<dyn Device>>>)> {
if let Some(platform_name) = cli.args.platform.as_ref() {
let platform = dinghy
.platform_by_name(platform_name)
.ok_or_else(|| anyhow!("No '{}' platform found", platform_name))?;
let device = find_first_device_for_platform(cli, dinghy, &platform);
Ok((platform, device))
} else if let Some(device_filter) = cli.args.device.as_ref() {
let is_banned_auto_platform_id = |id: &str| -> bool {
id.contains("auto-android")
&& (id.contains("min") || id.contains("latest") || id.contains("api"))
};
let devices = dinghy
.devices()
.into_iter()
.filter(move |it| {
format!("{:?}", it)
.to_lowercase()
.contains(&device_filter.to_lowercase())
})
.collect::<Vec<_>>();
if devices.len() == 0 {
bail!("No devices found for name hint `{}'", device_filter)
}
devices
.into_iter()
.filter_map(|d| {
let pf = dinghy
.platforms()
.iter()
.filter(|pf| !is_banned_auto_platform_id(&pf.id()))
.find(|pf| pf.is_compatible_with(&**d))
.cloned();
debug!(
"Looking for platform for {}: found {:?}",
d.id(),
pf.as_ref().map(|p| p.id())
);
pf.map(|it| (it, Some(d)))
})
.next()
.ok_or_else(|| {
anyhow!(
"No device and platform combination found for device hint `{}'",
device_filter
)
})
} else {
Ok((dinghy.host_platform(), None))
}
}
fn find_first_device_for_platform(
cli: &DinghyCli,
dinghy: &Dinghy,
platform: &Arc<Box<dyn Platform>>,
) -> Option<Arc<Box<dyn Device>>> {
dinghy
.devices()
.into_iter()
.filter(|device| {
cli.args
.device
.as_ref()
.map(|filter| {
format!("{:?}", device)
.to_lowercase()
.contains(&filter.to_lowercase())
})
.unwrap_or(true)
})
.filter(|it| platform.is_compatible_with(&**it.as_ref()))
.next()
}
================================================
FILE: dinghy-build/Cargo.toml
================================================
[package]
name = "dinghy-build"
version = "0.8.5-pre"
authors = ["Mathieu Poumeyrol <mathieu.poumeyrol@snips.ai>"]
license = "MIT/Apache-2.0"
description = "Cross-compilation made easier - helpers for build.rs scripts"
homepage = "https://medium.com/snips-ai/dinghy-painless-rust-tests-and-benches-on-ios-and-android-c9f94f81d305#.c2sx7two8"
repository = "https://github.com/sonos/dinghy"
keywords = [
"tests", "mobile", "ios", "android", "cargo"
]
categories = [ "development-tools::cargo-plugins", "development-tools::testing" , "development-tools::profiling" ]
edition = "2018"
[dependencies]
anyhow = "1.0.58"
cc = "1"
log="0.4"
================================================
FILE: dinghy-build/src/bindgen_macros.rs
================================================
/// Create a new `bindgen::Builder` set up and ready for cross-compilation.
///
/// This macro should be used for bindgen versions from 0.49 and above.
#[macro_export]
macro_rules! dinghy_bindgen {
() => {{
let bindgen = $crate::dinghy_bindgen_pre_0_49!();
if $crate::build::is_cross_compiling().expect("Couldn't determine if it is cross-compiling")
{
bindgen.detect_include_paths(false)
} else {
bindgen
}
}};
}
/// Compatibility macro for bindgen versions below 0.49
#[macro_export]
macro_rules! dinghy_bindgen_pre_0_49 {
() => {{
use $crate::build::is_cross_compiling;
use $crate::build_env::sysroot_path;
use $crate::utils::path_to_str;
use $crate::{Result, Context};
fn apple_patch(builder: bindgen::Builder) -> Result<bindgen::Builder> {
if is_cross_compiling()? {
let target = env::var("TARGET")?;
if target.contains("apple") && target.contains("aarch64") {
// The official Apple tools use "-arch arm64" instead of specifying
// -target directly; -arch only works when the default target is
// Darwin-based to put Clang into "Apple mode" as it were. But it does
// sort of explain why arm64 works better than aarch64, which is the
// preferred name everywhere else.
return Ok(builder
.clang_arg(format!("-arch"))
.clang_arg(format!("arm64")));
}
}
Ok(builder)
}
fn libclang_path_patch(builder: bindgen::Builder) -> Result<bindgen::Builder> {
if is_cross_compiling()? {
if let Ok(libclang_path) = env::var("DINGHY_BUILD_LIBCLANG_PATH") {
env::set_var("LIBCLANG_PATH", libclang_path)
}
}
Ok(builder)
}
fn detect_toolchain(builder: bindgen::Builder) -> Result<bindgen::Builder> {
if is_cross_compiling()? {
let target = env::var("TARGET")?;
let builder = if let Ok(_) = env::var("TARGET_SYSROOT") {
builder.clang_arg(format!("--sysroot={}", path_to_str(&sysroot_path()?)?))
} else {
println!("cargo:warning=No Sysroot detected, assuming the target is baremetal. If you have a sysroot, you must either define a TARGET_SYSROOT or use Dinghy to build your project.");
builder
};
Ok(builder.clang_arg(format!("--target={}", target)))
} else {
Ok(builder)
}
}
fn include_gcc_system_headers(builder: bindgen::Builder) -> Result<bindgen::Builder> {
if is_cross_compiling()? {
// Add a path to the private headers for the target compiler. Borderline,
// as we are likely using a gcc header with clang frontend.
let path = cc::Build::new()
.get_compiler()
.to_command()
.arg("--print-file-name=include")
.output()
.with_context(|| "Couldn't find target GCC executable.")
.and_then(|output| {
if output.status.success() {
Ok(String::from_utf8(output.stdout)?)
} else {
panic!("Couldn't determine target GCC include dir.")
}
})?;
Ok(builder.clang_arg("-isystem").clang_arg(path.trim()))
} else {
Ok(builder)
}
}
libclang_path_patch(
apple_patch(
include_gcc_system_headers(
detect_toolchain(bindgen::Builder::default().clang_arg("--verbose")).unwrap(),
)
.unwrap(),
)
.unwrap()
)
.unwrap()
}};
}
/// Generate a file containing the bindgen bindings in a standard path.
///
/// The standard path is `${OUT_DIR}/bindings.rs`.
///
/// To use it, simply perform the call like
/// `generate_default_bindgen_bindings!(bindgen_builder)`
#[macro_export]
macro_rules! generate_bindgen_bindings {
($builder:expr) => {{
let out_path = env::var("OUT_DIR")
.map(PathBuf::from)
.expect("Couldn't convert OUT_DIR var into a path")
.join("bindings.rs");
$builder
.generate()
.expect("Unable to generate bindings")
.write_to_file(out_path)
.expect("Unable to write the bindings in the file")
}};
}
================================================
FILE: dinghy-build/src/build.rs
================================================
use super::Result;
///! Helpers functions to output `cargo:` lines suitable for build.rs output.
use std::env;
use std::path::Path;
/// Find out if we are cross-compiling.
pub fn is_cross_compiling() -> Result<bool> {
Ok(env::var("TARGET")? != env::var("HOST")?)
}
/// Adds a `cargo:rustc-link-lib=` line.
pub fn include_path<P: AsRef<Path>>(lib_dir_path: P) -> Result<()> {
println!("cargo:rustc-link-lib={}", lib_dir_path.as_ref().display());
Ok(())
}
/// Adds a `cargo:rustc-link-search=` and `cargo:rustc-link-lib=static=` line.
pub fn link_static<P: AsRef<Path>>(lib_name: &str, lib_dir_path: P) -> Result<()> {
println!(
"cargo:rustc-link-search={}",
lib_dir_path.as_ref().display()
);
println!("cargo:rustc-link-lib=static={}", lib_name);
Ok(())
}
/// Adds a `cargo:rustc-link-search=` and `cargo:rustc-link-lib=dylib=` line.
pub fn link_dylib<P: AsRef<Path>>(lib_name: &str, lib_dir_path: P) -> Result<()> {
println!(
"cargo:rustc-link-search={}",
lib_dir_path.as_ref().display()
);
println!("cargo:rustc-link-lib=dylib={}", lib_name);
Ok(())
}
/// Adds a `cargo:rustc-link-search` and `cargo:rustc-link-lib=` line.
pub fn link_lib<P: AsRef<Path>>(lib_name: &str, lib_dir_path: P) -> Result<()> {
println!(
"cargo:rustc-link-search={}",
lib_dir_path.as_ref().display()
);
println!("cargo:rustc-link-lib={}", lib_name);
Ok(())
}
/// Adds a `cargo:rustc-link-lib=dylib=` line.
pub fn link_system_dylib(lib_name: &str) -> Result<()> {
println!("cargo:rustc-link-lib=dylib={}", lib_name);
Ok(())
}
/// Adds a `cargo:rustc-link-lib=` line.
pub fn link_system_lib(lib_name: &str) -> Result<()> {
println!("cargo:rustc-link-lib={}", lib_name);
Ok(())
}
/// Adds a `cargo:rerun-if-changed=` line.
pub fn rerun_if_changed<P: AsRef<Path>>(filepath: P) {
println!("cargo:rerun-if-changed={}", filepath.as_ref().display());
}
================================================
FILE: dinghy-build/src/build_env.rs
================================================
//! Target-aware environment manipulations.
//!
//! cc-rs and pkg-config-rs use a similar convention where some environment
//! variables (like CC, CFLAGS or PKG_CONFIG_PATH) can be tagged with the
//! current rustc target to distinguish a native build environment and one
//! or several cross-compilation ones.
//!
//! For instance, while compiling for Android arm, `cc-rs` looks first at
//! CC_arm-linux-androideabi, then CC_arm_linux_androideabi, the TARGET_CC
//! and finally CC.
//!
//! This crates implements some of the same logic and also helps generating
//! these variables names. It also notify all environment lookup "back" to
//! cargo using `cargo:rerun-if-env-changed` markup.
use anyhow::{Context, Result};
use std::env;
use std::ffi::OsStr;
use std::ffi::OsString;
use std::path::PathBuf;
/// Append a value to a PATH-like (`:`-separated) environment variable.
pub fn append_path_to_env<K: AsRef<OsStr>, V: AsRef<OsStr>>(key: K, value: V) {
let mut formatted_value = OsString::new();
if let Ok(initial_value) = env::var(key.as_ref()) {
formatted_value.push(initial_value);
formatted_value.push(":");
}
formatted_value.push(value);
env::set_var(key.as_ref(), formatted_value);
}
/// Append a value to a PATH-like (`:`-separated) environment variable taking
/// target scoping rules into consideration.
pub fn append_path_to_target_env<K: AsRef<OsStr>, R: AsRef<str>, V: AsRef<OsStr>>(
k: K,
rustc_triple: Option<R>,
v: V,
) {
append_path_to_env(target_key_from_triple(k, rustc_triple), v.as_ref())
}
/// Build-context aware environment variable access.
///
/// If we are running in a build.rs context, register the var to cargo using
/// `cargo:rerun-if-env-changed`.
pub fn build_env(name: &str) -> Result<String> {
let is_build_rs = env::var("CARGO_PKG_NAME").is_ok() && env::var("OUT_DIR").is_ok();
if is_build_rs {
println!("cargo:rerun-if-env-changed={}", name);
}
Ok(env::var(name)?)
}
/// Capitalize and replace `-` by `_`.
pub fn envify<S: AsRef<str>>(name: S) -> String {
name.as_ref()
.chars()
.map(|c| c.to_ascii_uppercase())
.map(|c| if c == '-' || c == '.' { '_' } else { c })
.collect()
}
/// Set a bunch of environment variables.
pub fn set_all_env<K: AsRef<OsStr>, V: AsRef<OsStr>>(env: &[(K, V)]) {
for env_var in env {
set_env(env_var.0.as_ref(), env_var.1.as_ref())
}
}
/// Set one environment variable.
pub fn set_env<K: AsRef<OsStr>, V: AsRef<OsStr>>(k: K, v: V) {
log::debug!(
"Setting environment variable {:?}={:?}",
k.as_ref(),
v.as_ref()
);
env::set_var(k, v);
}
/// Set one environment variable if not set yet.
pub fn set_env_ifndef<K: AsRef<OsStr>, V: AsRef<OsStr>>(k: K, v: V) {
if let Ok(current_env_value) = env::var(k.as_ref()) {
log::debug!(
"Ignoring value {:?} as environment variable {:?} already defined with value {:?}",
k.as_ref(),
v.as_ref(),
current_env_value
);
} else {
log::debug!(
"Setting environment variable {:?}={:?}",
k.as_ref(),
v.as_ref()
);
env::set_var(k, v);
}
}
/// Set one environment variable with target-scoping rules.
pub fn set_target_env<K: AsRef<OsStr>, R: AsRef<str>, V: AsRef<OsStr>>(
k: K,
rustc_triple: Option<R>,
v: V,
) {
set_env(target_key_from_triple(k, rustc_triple), v);
}
/// Access a required TARGET_SYSROOT variable, suggesting to define it or use
/// Dinghy.
pub fn sysroot_path() -> Result<PathBuf> {
env::var_os("TARGET_SYSROOT")
.map(PathBuf::from)
.context("You must either define a TARGET_SYSROOT or use Dinghy to build your project.")
}
/// Access `var_base` directly, or use targetting rules depending on the build
/// being native or cross.
pub fn target_env(var_base: &str) -> Result<String> {
if let Ok(target) = env::var("TARGET") {
let is_host = env::var("HOST")? == target;
target_env_from_triple(var_base, target.as_str(), is_host)
} else {
build_env(var_base)
}
}
/// Access `var_base` directly, using targetting rules.
pub fn target_env_from_triple(var_base: &str, triple: &str, is_host: bool) -> Result<String> {
build_env(&format!("{}_{}", var_base, triple))
.or_else(|_| build_env(&format!("{}_{}", var_base, triple.replace("-", "_"))))
.or_else(|_| {
build_env(&format!(
"{}_{}",
if is_host { "HOST" } else { "TARGET" },
var_base
))
})
.or_else(|_| build_env(var_base))
}
fn target_key_from_triple<K: AsRef<OsStr>, R: AsRef<str>>(
k: K,
rustc_triple: Option<R>,
) -> OsString {
let mut target_key = OsString::new();
target_key.push(k);
if let Some(rustc_triple) = rustc_triple {
target_key.push("_");
target_key.push(rustc_triple.as_ref().replace("-", "_"));
}
target_key
}
================================================
FILE: dinghy-build/src/lib.rs
================================================
//! Helpers for build.rs scripts.
//!
//! This library is meant to be used in build.rs scripts context.
//!
//! It contains a set of standalone functions that encodes some of the
//! shared wisdom and conventions across build.rs scripts, cargo, dinghy,
//! cc-rs, pkg-config-rs, bindgen, and others. It also helps providing
//! cross-compilation arguments to autotools `./configure` scripts.
mod bindgen_macros;
pub mod build;
pub mod build_env;
pub mod utils;
use crate::build::is_cross_compiling;
use crate::build_env::sysroot_path;
use crate::build_env::target_env;
use crate::utils::path_between;
use crate::utils::path_to_str;
use std::env;
use std::ffi::OsStr;
use std::path::Path;
use std::path::PathBuf;
use std::process::Command;
#[doc(hidden)]
pub use anyhow::{Context, Result};
/// Decorator for the std::process::Command adding a some chainable helpers.
///
/// Mostly useful for calling `./configure` scripts.
pub trait CommandExt {
/// Add this argument to the commands, but only on macos.
fn arg_for_macos<S: AsRef<OsStr>>(&mut self, arg: S) -> Result<&mut Command>;
/// Add a `--prefix` to point to a toolchain sysroot or the /, depending on
/// dinghy environment.
fn configure_prefix<P: AsRef<Path>>(&mut self, path: P) -> Result<&mut Command>;
/// Adds pkgconfig environment variables to point to an eventual cross compiling sysroot.
///
/// Usefull for compatibilty with pkg-config-rs up to 0.3.9 or to deal with
/// `./configure` scripts.
fn with_pkgconfig(&mut self) -> Result<&mut Command>;
/// Propagate TARGET, TARGET_CC, TARGET_AR and TARGET_SYSROOT to a
/// `./configure` script.
fn with_toolchain(&mut self) -> Result<&mut Command>;
}
impl CommandExt for Command {
fn arg_for_macos<S: AsRef<OsStr>>(&mut self, arg: S) -> Result<&mut Command> {
if env::var("TARGET")
.map(|target| target.contains("-apple-darwin"))
.unwrap_or(false)
{
self.arg(arg.as_ref());
}
Ok(self)
}
fn configure_prefix<P: AsRef<Path>>(&mut self, prefix_dir: P) -> Result<&mut Command> {
self.args(&[
"--prefix",
path_to_str(&path_between(
sysroot_path().unwrap_or(PathBuf::from("/")),
prefix_dir,
))?,
]);
Ok(self)
}
fn with_pkgconfig(&mut self) -> Result<&mut Command> {
if is_cross_compiling()? {
if let Ok(value) = target_env("PKG_CONFIG_PATH") {
log::info!("Running command with PKG_CONFIG_PATH:{:?}", value);
self.env("PKG_CONFIG_PATH", value);
}
if let Ok(value) = target_env("PKG_CONFIG_LIBDIR") {
log::info!("Running command with PKG_CONFIG_LIBDIR:{:?}", value);
self.env("PKG_CONFIG_LIBDIR", value);
}
if let Ok(value) = target_env("PKG_CONFIG_SYSROOT_DIR") {
log::info!("Running command with PKG_CONFIG_SYSROOT_DIR:{:?}", value);
self.env("PKG_CONFIG_SYSROOT_DIR", value);
}
}
Ok(self)
}
fn with_toolchain(&mut self) -> Result<&mut Command> {
if is_cross_compiling()? {
if let Ok(target) = env::var("TARGET") {
self.arg(format!("--host={}", target));
}
if let Ok(cc) = env::var("TARGET_CC") {
self.arg(format!("CC={}", cc));
}
if let Ok(ar) = env::var("TARGET_AR") {
self.arg(format!("AR={}", ar));
}
if let Ok(sysroot) = env::var("TARGET_SYSROOT") {
self.arg(format!("--with-sysroot={}", &sysroot));
}
}
Ok(self)
}
}
================================================
FILE: dinghy-build/src/utils.rs
================================================
//! Some helpers around Path and PathBuf manipulations.
use super::Result;
use anyhow::anyhow;
use std::path::Path;
use std::path::PathBuf;
/// Wraps the annoying PathBuf to string conversion in one single call.
pub fn path_to_str(path: &PathBuf) -> Result<&str> {
Ok(path
.to_str()
.ok_or(anyhow!("Not a valid UTF-8 path ({})", path.display()))?)
}
/// Finds the path to `to` relative from `from`.
pub fn path_between<P1: AsRef<Path>, P2: AsRef<Path>>(from: P1, to: P2) -> PathBuf {
let mut path = PathBuf::from("/");
for _ in from.as_ref() {
path.push("..");
}
for dir in to.as_ref().iter().skip(1) {
path.push(dir);
}
path
}
================================================
FILE: dinghy-lib/Cargo.toml
================================================
[package]
name = "dinghy-lib"
version = "0.8.5-pre"
authors = ["Mathieu Poumeyrol <mathieu.poumeyrol@snips.ai>"]
license = "MIT/Apache-2.0"
description = "Cross-compilation made easier - see main crate cargo-dinghy"
homepage = "https://medium.com/snips-ai/dinghy-painless-rust-tests-and-benches-on-ios-and-android-c9f94f81d305#.c2sx7two8"
repository = "https://github.com/sonos/dinghy"
keywords = [
"tests", "mobile", "ios", "android", "cargo"
]
build="build.rs"
categories = [ "development-tools::cargo-plugins", "development-tools::testing" , "development-tools::profiling" ]
readme = "../README.md"
edition = "2018"
[dependencies]
anyhow = "1"
dinghy-build = { path = "../dinghy-build" }
dirs = "5"
filetime = "0.2"
log = "0.4"
itertools = "0.10"
plist = "1"
regex = "1.0"
json = "0.12"
ignore = "0.4"
serde = { version = "1.0", features = ["derive"] }
toml = "0.7"
shell-escape = "0.1"
walkdir = "2.0"
which = "=4.0"
shellexpand="3"
semver = "1"
cargo_metadata.workspace=true
cargo-platform.workspace=true
colored = "2.0.0"
lazy_static = "1.4.0"
dyn-clone = "1.0.8"
fs-err = "2.11.0"
tempfile = "3.1"
[dev-dependencies]
tempfile = "3.1"
================================================
FILE: dinghy-lib/build.rs
================================================
fn main() {
println!(
"cargo:rustc-env=TARGET={}",
std::env::var("TARGET").unwrap()
);
}
================================================
FILE: dinghy-lib/src/android/device.rs
================================================
use crate::device::make_remote_app;
use crate::errors::*;
use crate::platform::regular_platform::RegularPlatform;
use crate::project::Project;
use crate::utils::{get_current_verbosity, path_to_str, user_facing_log, LogCommandExt};
use crate::Build;
use crate::BuildBundle;
use crate::Device;
use crate::DeviceCompatibility;
use log::{debug, info, log_enabled};
use std::io::Write;
use std::{fmt, io, path, process};
static ANDROID_WORK_DIR: &str = "/data/local/tmp/dinghy";
#[derive(Clone)]
pub struct AndroidDevice {
pub adb: path::PathBuf,
pub id: String,
pub supported_targets: Vec<&'static str>,
}
impl AndroidDevice {
pub fn from_id(adb: path::PathBuf, id: &str) -> Result<AndroidDevice> {
for prop in &[
"ro.product.cpu.abilist",
"ro.product.cpu.abi",
"ro.product.cpu.abi2",
] {
let getprop_output = process::Command::new(&adb)
.args(&["-s", id, "shell", "getprop", prop])
.log_invocation(3)
.output()?;
let abilist = String::from_utf8(getprop_output.stdout)?;
debug!(
"Android device {}, getprop {} returned {}",
id,
prop,
abilist.trim()
);
if abilist.trim().len() > 0 {
let supported_targets = abilist
.trim()
.split(",")
.filter_map(|abi| {
Some(match abi {
"arm64-v8a" => "aarch64-linux-android",
"armeabi-v7a" => "armv7-linux-androideabi",
"armeabi" => "arm-linux-androideabi",
"x86" => "i686-linux-android",
"x86_64" => "x86_64-linux-android",
_ => return None,
})
})
.collect::<Vec<_>>();
return Ok(AndroidDevice {
adb,
id: id.into(),
supported_targets: supported_targets,
});
}
}
bail!("Could not match a platform to the device")
}
fn adb(&self) -> Result<process::Command> {
let mut command = process::Command::new(&self.adb);
command.arg("-s").arg(&self.id);
Ok(command)
}
fn install_app(&self, project: &Project, build: &Build) -> Result<(BuildBundle, BuildBundle)> {
info!("Install {} to {}", build.runnable.id, self.id);
user_facing_log(
"Installing",
&format!("{} to {}", build.runnable.id, self.id),
0,
);
if !self
.adb()?
.arg("shell")
.arg("mkdir")
.arg("-p")
.arg(ANDROID_WORK_DIR)
.log_invocation(2)
.status()?
.success()
{
bail!(
"Failure to create dinghy work dir '{:?}' on target android device",
ANDROID_WORK_DIR
)
}
let build_bundle = make_remote_app(project, build)?;
let remote_bundle = AndroidDevice::to_remote_bundle(&build_bundle)?;
self.sync(
&build_bundle.bundle_dir,
&remote_bundle
.bundle_dir
.parent()
.ok_or_else(|| anyhow!("Invalid path {}", remote_bundle.bundle_dir.display()))?,
)?;
self.sync(
&build_bundle.lib_dir,
&remote_bundle
.lib_dir
.parent()
.ok_or_else(|| anyhow!("Invalid path {}", remote_bundle.lib_dir.display()))?,
)?;
debug!("Chmod target exe {}", remote_bundle.bundle_exe.display());
if !self
.adb()?
.arg("shell")
.arg("chmod")
.arg("755")
.arg(&remote_bundle.bundle_exe)
.log_invocation(2)
.status()?
.success()
{
bail!("Failure in android install");
}
Ok((build_bundle, remote_bundle))
}
fn sync<FP: AsRef<path::Path>, TP: AsRef<path::Path>>(
&self,
from_path: FP,
to_path: TP,
) -> Result<()> {
// Seems overkill...
// let _ = self.adb()?.arg("shell").arg("rm").arg("-rf").arg(to_path.as_ref()).status()?;
// Need parent as adb
let mut command = self.adb()?;
command
.arg("push")
.arg("--sync")
.arg(from_path.as_ref())
.arg(to_path.as_ref());
if !log_enabled!(::log::Level::Debug) {
command.stdout(::std::process::Stdio::null());
command.stderr(::std::process::Stdio::null());
}
debug!("Running {:?}", command);
if !command.log_invocation(2).status()?.success() {
bail!("Error syncing android directory ({:?})", command)
} else {
Ok(())
}
}
fn to_remote_bundle(build_bundle: &BuildBundle) -> Result<BuildBundle> {
build_bundle.replace_prefix_with(ANDROID_WORK_DIR)
}
}
impl DeviceCompatibility for AndroidDevice {
fn is_compatible_with_regular_platform(&self, platform: &RegularPlatform) -> bool {
if platform.id.starts_with("auto-android") {
let cpu = platform.id.split("-").nth(2).unwrap();
self.supported_targets
.iter()
.any(|target| target.starts_with(cpu))
} else {
self.supported_targets
.contains(&&*platform.toolchain.binutils_prefix)
}
}
}
impl Device for AndroidDevice {
fn clean_app(&self, build_bundle: &BuildBundle) -> Result<()> {
let remote_bundle = AndroidDevice::to_remote_bundle(build_bundle)?;
debug!("Cleaup device");
if !self
.adb()?
.arg("shell")
.arg("rm")
.arg("-rf")
.arg(&remote_bundle.bundle_dir)
.log_invocation(1)
.status()?
.success()
{
bail!("Failure in android clean")
}
if !self
.adb()?
.arg("shell")
.arg("rm")
.arg("-rf")
.arg(&remote_bundle.lib_dir)
.log_invocation(1)
.status()?
.success()
{
bail!("Failure in android clean")
}
Ok(())
}
fn debug_app(
&self,
_project: &Project,
_build: &Build,
_args: &[&str],
_envs: &[&str],
) -> Result<BuildBundle> {
unimplemented!()
}
fn id(&self) -> &str {
&self.id
}
fn name(&self) -> &str {
"android device"
}
fn run_app(
&self,
project: &Project,
build: &Build,
args: &[&str],
envs: &[&str],
) -> Result<BuildBundle> {
let args: Vec<String> = args
.iter()
.map(|&a| ::shell_escape::escape(a.into()).to_string())
.collect();
let (build_bundle, remote_bundle) = self.install_app(&project, &build)?;
let command = format!(
"cd '{}'; RUST_BACKTRACE=1 {} DINGHY=1 LD_LIBRARY_PATH=\"{}:$LD_LIBRARY_PATH\" {} {} ; echo FORWARD_RESULT_TO_DINGHY_BECAUSE_ADB_DOES_NOT=$?",
path_to_str(&remote_bundle.bundle_dir)?,
envs.join(" "),
path_to_str(&remote_bundle.lib_dir)?,
path_to_str(&remote_bundle.bundle_exe)?,
args.join(" "));
info!("Run {} on {}", build.runnable.id, self.id);
if get_current_verbosity() < 1 {
// we log the full command for verbosity > 1, just log a short message when the user
// didn't ask for verbose output
user_facing_log(
"Running",
&format!("{} on {}", build.runnable.id, self.id),
0,
);
}
if !self
.adb()?
.arg("shell")
.arg(&command)
.log_invocation(1)
.output()
.with_context(|| format!("Couldn't run {} using adb.", build.runnable.exe.display()))
.and_then(|output| {
if output.status.success() {
let _ = io::stdout().write(output.stdout.as_slice());
let _ = io::stderr().write(output.stderr.as_slice());
String::from_utf8(output.stdout).with_context(|| {
format!("Couldn't run {} using adb.", build.runnable.exe.display())
})
} else {
bail!("Couldn't run {} using adb.", build.runnable.exe.display())
}
})
.map(|output| output.lines().last().unwrap_or("").to_string())
.map(|last_line| {
last_line.contains("FORWARD_RESULT_TO_DINGHY_BECAUSE_ADB_DOES_NOT=0")
})?
{
bail!("Failed")
}
Ok(build_bundle)
}
}
impl fmt::Display for AndroidDevice {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "Android/{}", self.id)
}
}
impl fmt::Debug for AndroidDevice {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
Ok(fmt.write_str(
format!(
"Android {{ \"id\": \"{}\", \"supported_targets\": {:?} }}",
self.id, self.supported_targets
)
.as_str(),
)?)
}
}
================================================
FILE: dinghy-lib/src/android/mod.rs
================================================
use crate::config::PlatformConfiguration;
use crate::toolchain::ToolchainConfig;
use crate::{Device, Platform, PlatformManager, Result};
use fs_err as fs;
use std::fs::FileType;
use std::{env, path, process};
pub use self::device::AndroidDevice;
use crate::android::platform::AndroidPlatform;
use crate::utils::LogCommandExt;
use anyhow::{anyhow, bail, Context};
use log::debug;
mod device;
mod platform;
pub struct AndroidManager {
adb: path::PathBuf,
}
impl PlatformManager for AndroidManager {
fn devices(&self) -> Result<Vec<Box<dyn Device>>> {
let result = process::Command::new(&self.adb)
.arg("devices")
.log_invocation(3)
.output()?;
let mut devices = vec![];
let device_regex = ::regex::Regex::new(r#"^(\S+)\tdevice\r?$"#)?;
for line in String::from_utf8(result.stdout)?.split("\n").skip(1) {
if let Some(caps) = device_regex.captures(line) {
let d = AndroidDevice::from_id(self.adb.clone(), &caps[1])?;
debug!(
"Discovered Android device {} ({:?})",
d, d.supported_targets
);
devices.push(Box::new(d) as Box<dyn Device>);
}
}
Ok(devices)
}
fn platforms(&self) -> Result<Vec<Box<dyn Platform>>> {
if let Some(ndk) = ndk()? {
let default_api_level = "21";
debug!("Android NDK: {:?}", ndk);
let version = ndk_version(&ndk)?;
let major = version
.split(".")
.next()
.ok_or_else(|| anyhow!("Invalid version found for ndk {:?}", &ndk))?;
let major: usize = major
.parse()
.with_context(|| format!("Invalid version found for ndk {:?}", &ndk))?;
debug!(
"Android ndk: {:?}, ndk version: {}, major: {}",
ndk, version, major
);
if major >= 19 {
let mut platforms = vec![];
let prebuilt = ndk.join("toolchains/llvm/prebuilt");
let tools = prebuilt
.read_dir()?
.filter(|it| {
// ensure we only check dirs as macOS may add pesky .DS_Store files...
it.as_ref()
.ok()
.and_then(|it| it.file_type().ok().as_ref().map(FileType::is_dir))
.unwrap_or(false)
})
.next()
.ok_or_else(|| anyhow!("No tools in toolchain"))??;
let bin = tools.path().join("bin");
debug!("Android tools bin: {:?}", bin);
let libclang_path = tools.path().join(if major >= 26 { "lib" } else { "lib64" });
for (rustc_cpu, cc_cpu, binutils_cpu, abi_kind) in &[
("aarch64", "aarch64", "aarch64", "android"),
("armv7", "armv7a", "arm", "androideabi"),
("i686", "i686", "i686", "android"),
("x86_64", "x86_64", "x86_64", "android"),
] {
let mut api_levels: Vec<String> = Vec::new();
for entry in fs::read_dir(tools.path().join(format!(
"sysroot/usr/lib/{}-linux-{}",
binutils_cpu, abi_kind
)))? {
let entry = entry?;
if entry.file_type()?.is_dir() {
let folder_name = entry.file_name().into_string().unwrap();
match folder_name.parse::<u32>() {
Ok(_) => api_levels.push(folder_name),
Err(_) => {}
}
}
}
api_levels.sort();
let create_platform = |api: &str, suffix: &str| {
let id = format!("auto-android-{}{}", rustc_cpu, suffix);
let tc = ToolchainConfig {
bin_dir: bin.clone(),
rustc_triple: format!("{}-linux-{}", rustc_cpu, abi_kind),
root: prebuilt.clone(),
sysroot: Some(tools.path().join("sysroot")),
cc: "clang".to_string(),
cxx: "clang++".to_string(),
binutils_prefix: format!("{}-linux-{}", binutils_cpu, abi_kind),
cc_prefix: format!("{}-linux-{}{}", cc_cpu, abi_kind, api),
};
AndroidPlatform::new(
PlatformConfiguration::default(),
id,
tc,
major,
ndk.clone(),
libclang_path.clone(),
)
};
for api in api_levels.iter() {
platforms.push(create_platform(&api, &format!("-api{}", api))?);
}
if !api_levels.is_empty() {
platforms.push(create_platform(
api_levels
.first()
.expect("The api level vector shouldn't be empty"),
"-min",
)?);
platforms.push(create_platform(
api_levels
.last()
.expect("The api level vector shouldn't be empty"),
"-latest",
)?);
}
platforms.push(create_platform(default_api_level, "")?);
}
return Ok(platforms);
}
}
return Ok(vec![]);
}
}
impl AndroidManager {
pub fn probe() -> Option<AndroidManager> {
match adb() {
Ok(adb) => {
debug!("ADB found: {:?}", adb);
Some(AndroidManager { adb })
}
Err(_) => {
debug!("adb not found in path, android disabled");
None
}
}
}
}
fn probable_sdk_locs() -> Result<Vec<path::PathBuf>> {
let mut v = vec![];
for var in &[
"ANDROID_HOME",
"ANDROID_SDK",
"ANDROID_SDK_ROOT",
"ANDROID_SDK_HOME",
] {
if let Ok(path) = env::var(var) {
let path = path::Path::new(&path);
if path.is_dir() {
v.push(path.to_path_buf())
}
}
}
if let Ok(home) = env::var("HOME") {
let mac = path::Path::new(&home).join("Library/Android/sdk");
if mac.is_dir() {
v.push(mac);
}
let linux = path::Path::new(&home).join("Android/Sdk");
if linux.is_dir() {
v.push(linux);
}
}
let casks = path::PathBuf::from("/usr/local/Caskroom/android-sdk");
if casks.is_dir() {
for kid in casks.read_dir()? {
let kid = kid?;
if kid.file_name() != ".metadata" {
v.push(kid.path());
}
}
}
debug!("Candidates SDK: {:?}", v);
Ok(v)
}
fn ndk() -> Result<Option<path::PathBuf>> {
if let Ok(path) = env::var("ANDROID_NDK_HOME") {
return Ok(Some(path.into()));
}
if let Ok(path) = env::var("ANDROID_NDK") {
return Ok(Some(path.into()));
}
for sdk in probable_sdk_locs()? {
if sdk.join("ndk-bundle/source.properties").is_file() {
return Ok(Some(sdk.join("ndk-bundle")));
}
if let Some(ndk) = find_non_legacy_ndk(&sdk)? {
return Ok(Some(ndk));
}
}
debug!("Android NDK not found");
Ok(None)
}
fn ndk_version(ndk: &path::Path) -> Result<String> {
let sources_prop_file = ndk.join("source.properties");
let props = fs::read_to_string(&sources_prop_file)
.with_context(|| format!("Reading prop file {sources_prop_file:?}"))?;
let revision_line = props
.split("\n")
.find(|l| l.starts_with("Pkg.Revision"))
.with_context(|| {
format!(
"{:?} does not contain a Pkg.Revision line. Invalid SDK?",
sources_prop_file
)
})?;
Ok(revision_line.split(" ").last().unwrap().to_string())
}
fn adb() -> Result<path::PathBuf> {
fn try_out(command: &path::Path) -> bool {
match process::Command::new(command)
.arg("--version")
.stdout(process::Stdio::null())
.stderr(process::Stdio::null())
.log_invocation(3)
.status()
{
Ok(_) => true,
Err(_) => false,
}
}
if let Ok(adb) = env::var("DINGHY_ANDROID_ADB") {
return Ok(adb.into());
}
if let Ok(adb) = ::which::which("adb") {
return Ok(adb);
}
for loc in probable_sdk_locs()? {
let adb = loc.join("platform-tools/adb");
if try_out(&adb) {
return Ok(adb.into());
}
}
bail!("Adb could be found")
}
fn find_non_legacy_ndk(sdk: &path::Path) -> Result<Option<path::PathBuf>> {
let ndk_root = sdk.join("ndk");
if !ndk_root.is_dir() {
return Ok(None);
}
let ndk = fs::read_dir(ndk_root)?
.filter_map(Result::ok)
.filter_map(|directory| {
directory
.path()
.file_name()
.and_then(|name| {
let name = name.to_string_lossy();
// Filter out directory if we fail to parse directory name to semver
semver::Version::parse(&name).ok()
})
.map(|version| (directory, version))
})
.max_by(|left, right| {
let left_version: &semver::Version = &left.1;
let right_version: &semver::Version = &right.1;
left_version.cmp(right_version)
})
.map(|tuple| tuple.0.path());
Ok(ndk)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_find_non_legacy_ndk() {
let sdk_dir = tempfile::tempdir().unwrap();
let sdk = sdk_dir.path();
let ndk_root = sdk.join("ndk");
let ndk_versions = ["21.1.123456", "21.3.6528147", "20.1.5948944"];
for version in &ndk_versions {
let path = ndk_root.join(version);
fs::create_dir_all(path).unwrap();
}
let ndk = find_non_legacy_ndk(sdk).unwrap();
let expected = ndk_root.join("21.3.6528147");
assert_eq!(Some(expected), ndk);
}
#[test]
fn test_find_non_legacy_ndk_on_non_existing_directory() {
let sdk = tempfile::tempdir().unwrap();
let ndk = find_non_legacy_ndk(sdk.path()).unwrap();
assert_eq!(None, ndk);
}
}
================================================
FILE: dinghy-lib/src/android/platform.rs
================================================
use std::fmt::Formatter;
use crate::platform::regular_platform::RegularPlatform;
use crate::toolchain::ToolchainConfig;
use crate::{platform, Result};
use crate::{Build, Device, Platform, PlatformConfiguration, Project, SetupArgs};
use dinghy_build::build_env::set_env;
use std::io::Write;
use std::path::PathBuf;
use std::process::Command;
pub struct AndroidPlatform {
regular_platform: Box<dyn Platform>,
toolchain_config: ToolchainConfig,
ndk_major_version: usize,
ndk_path: PathBuf,
libclang_path: PathBuf,
}
impl std::fmt::Debug for AndroidPlatform {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.id())
}
}
impl AndroidPlatform {
pub fn new(
configuration: PlatformConfiguration,
id: String,
toolchain_config: ToolchainConfig,
ndk_major_version: usize,
ndk_path: PathBuf,
libclang_path: PathBuf,
) -> Result<Box<dyn Platform>> {
Ok(Box::new(Self {
regular_platform: RegularPlatform::new_with_tc(
configuration,
id,
toolchain_config.clone(),
)?,
toolchain_config,
ndk_major_version,
ndk_path,
libclang_path,
}))
}
}
impl Platform for AndroidPlatform {
fn setup_env(&self, project: &Project, setup_args: &SetupArgs) -> anyhow::Result<()> {
self.regular_platform.setup_env(project, setup_args)?;
if self.ndk_major_version >= 23 {
log::trace!("Setup linker with android ndk23+ hack...");
let hack_dir = project
.metadata
.target_directory
.join(self.rustc_triple())
.join(self.id())
.join("ndk23-hack");
std::fs::create_dir_all(&hack_dir)?;
let mut hack_file = std::fs::File::create(hack_dir.join("libgcc.a"))?;
hack_file.write_all("INPUT(-lunwind)".as_bytes())?;
let mut linker_cmd = self.toolchain_config.generate_linker_command(&setup_args);
linker_cmd.push_str(" -L");
linker_cmd.push_str(hack_dir.canonicalize()?.to_str().unwrap());
self.toolchain_config.setup_linker(
&self.id(),
&linker_cmd,
&project.metadata.workspace_root,
)?;
self.toolchain_config
.setup_tool("AR", &self.toolchain_config.naked_executable("llvm-ar"))?;
}
if self.ndk_major_version >= 17 {
// bindgen need this to use the proper imports
set_env("DINGHY_BUILD_LIBCLANG_PATH", &self.libclang_path)
}
if std::env::var("ANDROID_NDK").is_err() {
set_env("ANDROID_NDK", self.ndk_path.canonicalize()?)
}
if std::env::var("ANDROID_NDK_HOME").is_err() {
set_env("ANDROID_NDK_HOME", self.ndk_path.canonicalize()?)
}
Ok(())
}
fn id(&self) -> String {
self.regular_platform.id()
}
fn is_compatible_with(&self, device: &dyn Device) -> bool {
self.regular_platform.is_compatible_with(device)
}
fn is_host(&self) -> bool {
self.regular_platform.is_host()
}
fn rustc_triple(&self) -> &str {
self.regular_platform.rustc_triple()
}
fn strip(&self, build: &mut Build) -> anyhow::Result<()> {
if self.ndk_major_version >= 23 {
build.runnable = platform::strip_runnable(
&build.runnable,
Command::new(self.toolchain_config.naked_executable("llvm-strip")),
)?;
Ok(())
} else {
self.regular_platform.strip(build)
}
}
fn sysroot(&self) -> anyhow::Result<Option<PathBuf>> {
self.regular_platform.sysroot()
}
}
================================================
FILE: dinghy-lib/src/apple/device.rs
================================================
use super::{xcode, AppleSimulatorType};
use crate::apple::AppleDevicePlatform;
use crate::device::make_remote_app_with_name;
use crate::errors::*;
use crate::project::Project;
use crate::utils::LogCommandExt;
use crate::utils::{get_current_verbosity, user_facing_log};
use crate::Build;
use crate::BuildBundle;
use crate::Device;
use crate::DeviceCompatibility;
use crate::Runnable;
use colored::Colorize;
use fs_err as fs;
use itertools::Itertools;
use log::debug;
use std::fmt;
use std::fmt::Display;
use std::fmt::Formatter;
use std::io::{BufRead, BufReader};
use std::path::Path;
use std::process::{self, Stdio};
use std::time::Duration;
#[derive(Clone, Debug)]
pub struct IosDevice {
pub id: String,
pub name: String,
pub arch_cpu: &'static str,
rustc_triple: String,
pub os: String,
}
#[derive(Clone, Debug)]
pub struct AppleSimDevice {
pub id: String,
pub name: String,
pub os: String,
pub sim_type: AppleSimulatorType,
}
unsafe impl Send for IosDevice {}
impl IosDevice {
pub fn new(name: String, id: String, arch_cpu: &str, os: String) -> Result<IosDevice> {
let cpu = match &*arch_cpu {
"arm64" | "arm64e" => "aarch64",
_ => "armv7",
};
Ok(IosDevice {
name,
id,
os,
arch_cpu: cpu.into(),
rustc_triple: format!("{}-apple-ios", cpu),
})
}
fn is_pre_ios_17(&self) -> Result<bool> {
Ok(semver::VersionReq::parse(&self.os)?
.comparators
.get(0)
.ok_or_else(|| anyhow!("Invalid iOS version: {}", self.os))?
.major
< 17)
}
fn is_locked(&self) -> Result<bool> {
let result = process::Command::new("xcrun")
.args(
"devicectl device info lockState --quiet --json-output /dev/stdout --device"
.split_whitespace(),
)
.arg(&self.id)
.log_invocation(1)
.output()
.context("Failed to run devicectl device info lockState")?;
if !result.status.success() {
bail!("Device lock query failed\n",)
}
Ok(
json::parse(std::str::from_utf8(&result.stdout)?)?["result"]["passcodeRequired"]
.as_bool()
.unwrap(),
)
}
fn make_app(
&self,
project: &Project,
build: &Build,
runnable: &Runnable,
) -> Result<BuildBundle> {
let signing = xcode::look_for_signature_settings(&self.id)?
.pop()
.ok_or_else(|| anyhow!("no signing identity found"))?;
let app_id = signing
.name
.split(" ")
.last()
.ok_or_else(|| anyhow!("no app id ?"))?;
let mut build_bundle = make_apple_app(project, build, runnable, &app_id, None)?;
build_bundle.app_id = Some(app_id.to_owned());
super::xcode::sign_app(&build_bundle, &signing)?;
Ok(build_bundle)
}
fn install_app(
&self,
project: &Project,
build: &Build,
runnable: &Runnable,
) -> Result<BuildBundle> {
user_facing_log(
"Installing",
&format!("{} to {} ({})", build.runnable.id, self.id, self.name),
0,
);
let build_bundle = self.make_app(project, build, runnable)?;
let bundle = build_bundle.bundle_dir.to_string_lossy();
if self.is_pre_ios_17()? {
self.install_app_with_ios_deploy(&bundle)?;
return Ok(build_bundle);
}
// xcrun devicectl device install app --device 00008110-001XXXXXXXXXX ./xgen/Build/Products/Release-iphoneos/nilo.app
let result = process::Command::new("xcrun")
.args("devicectl device install app --device".split_whitespace())
.arg(&self.id)
.arg(&*bundle)
.log_invocation(1)
.status()
.context("Failed to run devicectl device install app")?;
if !result.success() {
bail!("Installation on device failed\n",)
}
Ok(build_bundle)
}
fn run_remote(
&self,
build_bundle: &BuildBundle,
args: &[&str],
envs: &[&str],
debugger: bool,
) -> Result<()> {
if self.is_pre_ios_17()? {
return self.run_remote_with_ios_deploy(build_bundle, args, envs, debugger);
}
let app_list = process::Command::new("pymobiledevice3")
.args("apps list --no-color --udid".split_whitespace())
.arg(&self.id)
.output()?;
let app_list = json::parse(std::str::from_utf8(&app_list.stdout)?).with_context(|| {
format!(
"Ran `pymobiledevice3 apps list --no-color --udid {}`, could not parse expected JSON output.", self.id,
)
})?;
let app_path = build_bundle.bundle_dir.to_string_lossy();
let app = app_list
.entries()
.find(|e| e.0 == build_bundle.app_id.as_ref().unwrap())
.unwrap()
.1;
let remote_path = app["Path"].to_string();
let tunnel = process::Command::new("sudo")
.arg("-p")
.arg(format!(
"Please enter %p's password on %h to start a tunnel to '{}' (sudo):",
self.name
))
.args("pymobiledevice3 remote start-tunnel --script-mode --udid".split_whitespace())
.arg(&self.id)
.stderr(Stdio::inherit())
.stdin(Stdio::inherit())
.stdout(Stdio::piped())
.spawn()?;
let mut rsd = String::new();
BufReader::new(tunnel.stdout.unwrap()).read_line(&mut rsd)?;
debug!("iOS RSD tunnel started: {rsd}");
// start the debugserver
let server = process::Command::new("pymobiledevice3")
.args("developer debugserver start-server --rsd".split_whitespace())
.args(rsd.trim().split_whitespace())
.stderr(Stdio::inherit())
.stdout(Stdio::piped())
.spawn()?;
let lldb_connection_string = BufReader::new(server.stdout.unwrap())
.lines()
.find(|l| l.as_ref().unwrap().contains("process connect connect://"))
.unwrap()
.unwrap();
let connection_details = lldb_connection_string.split_whitespace().nth(3).unwrap();
debug!("iOS debugserver started: {connection_details}");
if self.is_locked()? {
eprint!(
"{}",
format!("\n\n Please unlock {}! ", &self.name).bright_yellow()
);
loop {
std::thread::sleep(Duration::from_millis(300));
if !self.is_locked()? {
eprintln!("{}", " All good, yay!\n".bright_green());
break;
}
}
}
let tempdir = tempfile::TempDir::with_prefix("dinghy-lldb")?;
let script_path = tempdir.path().join("run.lldb");
// see https://stackoverflow.com/questions/77865860/lldb-hangs-when-trying-to-execute-command-with-o
// for the terrible async thing
std::fs::write(
&script_path,
format!(
"
platform select remote-ios
target create {app_path}
script lldb.target.module[0].SetPlatformFileSpec(lldb.SBFileSpec('{remote_path}'))
script old_debug = lldb.debugger.GetAsync()
script lldb.debugger.SetAsync(True)
process connect {connection_details}
script lldb.debugger.SetAsync(old_debug)
run {}
exit
",
args.iter()
.map(|&s| shell_escape::escape(s.into()))
.join(" ")
),
)?;
let lldb = process::Command::new("lldb")
.arg("--batch")
.arg("-s")
.arg(script_path)
.stderr(Stdio::inherit())
.stdout(Stdio::piped())
.spawn()?;
let mut lines = BufReader::new(lldb.stdout.unwrap()).lines();
while !lines.next().unwrap()?.starts_with("(lldb) run") {}
for line in lines {
let line = line?;
println!("{}", line);
if line.contains("exited with status = ") {
let rv = line.split_whitespace().nth(6).unwrap();
println!("returns: {rv}");
if rv == "0" {
return Ok(());
} else {
bail!("Failed")
}
}
}
Ok(())
}
// LEGACY IOS-DEPLOY BASED WORKFLOW (iOS<17)
fn install_app_with_ios_deploy(&self, bundle: &str) -> Result<()> {
process::Command::new("ios-deploy")
.args(&["-i", &self.id, "-b", &bundle, "-n"])
.log_invocation(1)
.output()
.context("Failed to run ios-deploy")?
.status;
Ok(())
}
fn run_remote_with_ios_deploy(
&self,
build_bundle: &BuildBundle,
args: &[&str],
envs: &[&str],
debugger: bool,
) -> Result<()> {
let bundle = build_bundle.bundle_dir.to_string_lossy();
let mut command = process::Command::new("ios-deploy");
command.args(&["-i", &self.id, "-b", &bundle, "-m"]);
command.args(&["-a", &args.join(" ")]);
command.args(&["-s", &envs.join(" ")]);
command.arg(if debugger { "-d" } else { "-I" });
command.stderr(process::Stdio::inherit());
command.stdout(process::Stdio::inherit());
let status = command
.log_invocation(1)
.output()
.context("Failed to run ios-deploy")?
.status;
if !status.success() {
bail!("Run on device failed")
}
Ok(())
}
}
impl Device for IosDevice {
fn clean_app(&self, _build_bundle: &BuildBundle) -> Result<()> {
unimplemented!()
}
fn debug_app(
&self,
project: &Project,
build: &Build,
args: &[&str],
envs: &[&str],
) -> Result<BuildBundle> {
let build_bundle = self.install_app(project, build, &build.runnable)?;
if get_current_verbosity() < 1 {
// we log the full command for verbosity > 1, just log a short message when the user
// didn't ask for verbose output
user_facing_log(
"Debugging",
&format!("{} on {}", build.runnable.id, self.id),
0,
);
}
self.run_remote(&build_bundle, args, envs, true)?;
Ok(build_bundle)
}
fn id(&self) -> &str {
&self.id
}
fn name(&self) -> &str {
&self.name
}
fn run_app(
&self,
project: &Project,
build: &Build,
args: &[&str],
envs: &[&str],
) -> Result<BuildBundle> {
let build_bundle = self.install_app(project, build, &build.runnable)?;
if get_current_verbosity() < 1 {
// we log the full command for verbosity > 1, just log a short message when the user
// didn't ask for verbose output
user_facing_log(
"Running",
&format!("{} on {}", build.runnable.id, self.id),
0,
);
}
self.run_remote(&build_bundle, args, envs, false)?;
Ok(build_bundle)
}
}
impl AppleSimDevice {
fn install_app(
&self,
project: &Project,
build: &Build,
runnable: &Runnable,
) -> Result<BuildBundle> {
user_facing_log(
"Installing",
&format!("{} to {}", build.runnable.id, self.id),
0,
);
let build_bundle = self.make_app(project, build, runnable)?;
let _ = process::Command::new("xcrun")
.args(&["simctl", "uninstall", &self.id, "Dinghy"])
.log_invocation(2)
.status()?;
let stat = process::Command::new("xcrun")
.args(&[
"simctl",
"install",
&self.id,
build_bundle
.bundle_dir
.to_str()
.ok_or_else(|| anyhow!("conversion to string"))?,
])
.log_invocation(1)
.status()?;
if stat.success() {
Ok(build_bundle)
} else {
bail!(
"Failed to install {} for {}",
runnable.exe.display(),
self.id
)
}
}
fn make_app(
&self,
project: &Project,
build: &Build,
runnable: &Runnable,
) -> Result<BuildBundle> {
make_apple_app(project, build, runnable, "Dinghy", Some(&self.sim_type))
}
}
impl Device for AppleSimDevice {
fn clean_app(&self, _build_bundle: &BuildBundle) -> Result<()> {
unimplemented!()
}
fn debug_app(
&self,
project: &Project,
build: &Build,
args: &[&str],
envs: &[&str],
) -> Result<BuildBundle> {
let runnable = &build.runnable;
let build_bundle = self.install_app(project, build, runnable)?;
let install_path = String::from_utf8(
process::Command::new("xcrun")
.args(&["simctl", "get_app_container", &self.id, "Dinghy"])
.log_invocation(2)
.output()?
.stdout,
)?;
if get_current_verbosity() < 1 {
// we log the full command for verbosity > 1, just log a short message when the user
// didn't ask for verbose output
user_facing_log(
"Debugging",
&format!("{} on {}", build.runnable.id, self.id),
0,
);
}
launch_lldb_simulator(&self, &install_path, args, envs, true)?;
Ok(build_bundle)
}
fn id(&self) -> &str {
&self.id
}
fn name(&self) -> &str {
&self.name
}
fn run_app(
&self,
project: &Project,
build: &Build,
args: &[&str],
envs: &[&str],
) -> Result<BuildBundle> {
let build_bundle = self.install_app(&project, &build, &build.runnable)?;
if get_current_verbosity() < 1 {
// we log the full command for verbosity > 1, just log a short message when the user
// didn't ask for verbose output
user_facing_log(
"Running",
&format!("{} on {}", build.runnable.id, self.id),
0,
);
}
launch_app(&self, args, envs)?;
Ok(build_bundle)
}
}
impl Display for IosDevice {
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
write!(
fmt,
"{} ({} {} {})",
self.name, self.id, self.arch_cpu, self.os
)
}
}
impl Display for AppleSimDevice {
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
write!(fmt, "{} ({} sim {})", self.name, self.id, self.os)
}
}
impl DeviceCompatibility for IosDevice {
fn is_compatible_with_simulator_platform(&self, platform: &AppleDevicePlatform) -> bool {
if platform.sim.is_some() {
return false;
}
if platform.toolchain.rustc_triple == self.rustc_triple.as_str() {
return true;
}
return false;
}
}
impl DeviceCompatibility for AppleSimDevice {
fn is_compatible_with_simulator_platform(&self, platform: &AppleDevicePlatform) -> bool {
if let Some(sim) = &platform.sim {
self.sim_type == *sim
} else {
false
}
}
}
fn make_apple_app(
project: &Project,
build: &Build,
runnable: &Runnable,
app_id: &str,
sim_type: Option<&AppleSimulatorType>,
) -> Result<BuildBundle> {
use crate::project;
let build_bundle = make_remote_app_with_name(project, build, Some("Dinghy.app"))?;
project::rec_copy(&runnable.exe, build_bundle.bundle_dir.join("Dinghy"), false)?;
let magic = process::Command::new("file")
.arg(
runnable
.exe
.to_str()
.ok_or_else(|| anyhow!("path conversion to string: {:?}", runnable.exe))?,
)
.log_invocation(3)
.output()?;
let magic = String::from_utf8(magic.stdout)?;
let target = magic
.split(" ")
.last()
.ok_or_else(|| anyhow!("empty magic"))?;
xcode::add_plist_to_app(&build_bundle, target, app_id, sim_type)?;
Ok(build_bundle)
}
fn launch_app(dev: &AppleSimDevice, app_args: &[&str], _envs: &[&str]) -> Result<()> {
use std::io::Write;
let dir = tempfile::TempDir::with_prefix("mobiledevice-rs-lldb")?;
let tmppath = dir.path();
let mut install_path = String::from_utf8(
process::Command::new("xcrun")
.args(&["simctl", "get_app_container", &dev.id, "Dinghy"])
.log_invocation(2)
.output()?
.stdout,
)?;
install_path.pop();
let stdout = Path::new(&install_path)
.join("stdout")
.to_string_lossy()
.into_owned();
let stdout_param = &format!("--stdout={}", stdout);
let mut xcrun_args: Vec<&str> = vec![
"simctl",
"launch",
"--wait-for-debugger",
stdout_param,
&dev.id,
"Dinghy",
];
xcrun_args.extend(app_args);
debug!("Launching app via xcrun using args: {:?}", xcrun_args);
let launch_output = process::Command::new("xcrun")
.args(&xcrun_args)
.log_invocation(1)
.output()?;
let launch_output = String::from_utf8_lossy(&launch_output.stdout);
debug!("xcrun simctl launch output: {:?}", launch_output);
// Output from the launch command should be "Dinghy: $PID" which is after the 8th character.
let dinghy_pid = launch_output.split_at(8).1;
// Attaching to the processes needs to be done in a script, not a commandline parameter or
// lldb will say "no simulators found".
let lldb_script_filename = tmppath.join("lldb-script");
let mut script = fs::File::create(&lldb_script_filename)?;
write!(script, "attach {}\n", dinghy_pid)?;
write!(script, "continue\n")?;
write!(script, "quit\n")?;
let output = process::Command::new("lldb")
.arg("")
.arg("-s")
.arg(lldb_script_filename)
.output()?;
let test_contents = std::fs::read_to_string(&stdout)
.with_context(|| format!("Reading llvm stdout from {stdout}"))?;
println!("{}", test_contents);
let output: String = String::from_utf8_lossy(&output.stdout).to_string();
debug!("lldb script: \n{}", output);
// The stdout from lldb is something like:
//
// (lldb) attach 34163
// Process 34163 stopped
// * thread #1, stop reason = signal SIGSTOP
// frame #0: 0x00000001019cd000 dyld`_dyld_start
// dyld`_dyld_start:
// -> 0x1019cd000 <+0>: popq %rdi
// 0x1019cd001 <+1>: pushq $0x0
// 0x1019cd003 <+3>: movq %rsp, %rbp
// 0x1019cd006 <+6>: andq $-0x10, %rsp
// Target 0: (Dinghy) stopped.
// Executable module set to .....
// Architecture set to: x86_64h-apple-ios-.
// (lldb) continue
// Process 34163 resuming
// Process 34163 exited with status = 101 (0x00000065)
// (lldb) quit
//
// We need the "exit with status" line which is the 3rd from the last
let exit_status_line = output
.lines()
.rev()
.find(|line| line.contains("exited with status"));
if let Some(exit_status_line) = exit_status_line {
let words: Vec<&str> = exit_status_line.split_whitespace().rev().collect();
if let Some(exit_status) = words.get(1) {
let exit_status = exit_status.parse::<u32>()?;
if exit_status == 0 {
Ok(())
} else {
bail!("Test failure, exit code: {}", exit_status)
}
} else {
panic!(
"Failed to parse lldb exit line for an exit status. {:?}",
words
);
}
} else {
panic!("Failed to get the exit status line from lldb: {}", output);
}
}
fn launch_lldb_simulator(
dev: &AppleSimDevice,
installed: &str,
args: &[&str],
envs: &[&str],
debugger: bool,
) -> Result<()> {
use std::io::Write;
use std::process::Command;
let dir = tempfile::TempDir::with_prefix("mobiledevice-rs-lldb")?;
let tmppath = dir.path();
let lldb_script_filename = tmppath.join("lldb-script");
{
let python_lldb_support = tmppath.join("helpers.py");
let helper_py = include_str!("helpers.py");
let helper_py = helper_py.replace("ENV_VAR_PLACEHOLDER", &envs.join("\", \""));
fs::File::create(&python_lldb_support)?.write_fmt(format_args!("{}", &helper_py))?;
let mut script = fs::File::create(&lldb_script_filename)?;
writeln!(script, "platform select ios-simulator")?;
writeln!(script, "target create {}", installed)?;
writeln!(script, "script pass")?;
writeln!(script, "command script import {:?}", python_lldb_support)?;
writeln!(
script,
"command script add -s synchronous -f helpers.start start"
)?;
writeln!(
script,
"command script add -f helpers.connect_command connect"
)?;
writeln!(script, "connect connect://{}", dev.id)?;
if !debugger {
writeln!(script, "start {}", args.join(" "))?;
writeln!(script, "quit")?;
}
}
let stat = Command::new("xcrun")
.arg("lldb")
.arg("-Q")
.arg("-s")
.arg(lldb_script_filename)
.log_invocation(1)
.status()?;
if stat.success() {
Ok(())
} else {
bail!("LLDB returned error code {:?}", stat.code())
}
}
================================================
FILE: dinghy-lib/src/apple/helpers.py
================================================
import os
import lldb
import shlex
def connect_command(debugger, command, result, internal_dict):
connect_url = command
error = lldb.SBError()
process = lldb.target.ConnectRemote(lldb.target.GetDebugger().GetListener(), connect_url, None, error)
def set_remote_path(debugger, command, result, internal_dict):
device_app = command
error = lldb.SBError()
lldb.target.modules[0].SetPlatformFileSpec(lldb.SBFileSpec(device_app))
def start(debugger, command, result, internal_dict):
error = lldb.SBError()
info = lldb.SBLaunchInfo(shlex.split(command))
info.SetEnvironmentEntries(["ENV_VAR_PLACEHOLDER"], True)
proc = lldb.target.Launch(info, error)
lockedstr = ': Locked'
if proc.GetState() != lldb.eStateExited:
print("process left in lldb state: %s"%(debugger.StateAsCString(proc.GetState())))
if lockedstr in str(error):
print('\nDevice Locked\n')
os._exit(254)
elif proc.GetState() == lldb.eStateStopped:
thread = proc.GetSelectedThread()
print(thread)
for frame in thread:
print(" %s"%(frame))
os._exit(-1)
elif not error.Success():
print(str(error))
if proc.exit_state != 0:
os._exit(proc.exit_state)
================================================
FILE: dinghy-lib/src/apple/mod.rs
================================================
use std::collections::HashMap;
use std::fmt::Display;
pub use self::device::{AppleSimDevice, IosDevice};
pub use self::platform::AppleDevicePlatform;
use crate::{Device, Platform, PlatformManager, Result};
use itertools::Itertools;
mod device;
mod platform;
mod xcode;
use anyhow::{anyhow, bail, Context};
use log::info;
#[derive(Debug, Clone)]
pub struct SignatureSettings {
pub identity: SigningIdentity,
pub file: String,
pub entitlements: String,
pub name: String,
#[allow(dead_code)]
pub profile: String,
}
#[derive(Debug, Clone)]
pub struct SigningIdentity {
#[allow(dead_code)]
pub id: String,
pub name: String,
pub team: String,
}
#[derive(Debug, Clone, PartialEq)]
pub enum AppleSimulatorType {
Ios,
Watchos,
Tvos,
}
impl Display for AppleSimulatorType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let val = match self {
AppleSimulatorType::Ios => "ios",
AppleSimulatorType::Watchos => "watchos",
AppleSimulatorType::Tvos => "tvos",
};
f.write_str(val)
}
}
pub struct IosManager {
devices: Vec<Box<dyn Device>>,
}
impl IosManager {
pub fn new() -> Result<Option<IosManager>> {
let devices = devices()
.context("Could not list iOS devices")?
.into_iter()
.chain(
simulators(AppleSimulatorType::Ios)
.context("Could not list iOS simulators")?
.into_iter(),
)
.collect();
Ok(Some(IosManager { devices }))
}
}
impl PlatformManager for IosManager {
fn devices(&self) -> Result<Vec<Box<dyn Device>>> {
Ok(self.devices.clone())
}
fn platforms(&self) -> Result<Vec<Box<dyn Platform>>> {
[
"armv7",
"armv7s",
"aarch64",
"i386",
"x86_64",
"aarch64-sim",
]
.iter()
.map(|arch| {
let id = format!("auto-ios-{}", arch);
let rustc_triple = if *arch != "aarch64-sim" {
format!("{}-apple-ios", arch)
} else {
format!("aarch64-apple-ios-sim")
};
let simulator = if *arch == "x86_64" || *arch == "aarch64-sim" {
Some(AppleSimulatorType::Ios)
} else {
None
};
AppleDevicePlatform::new(
id,
&rustc_triple,
simulator,
crate::config::PlatformConfiguration::default(),
)
.map(|pf| pf as Box<dyn Platform>)
})
.collect()
}
}
pub struct WatchosManager {
devices: Vec<Box<dyn Device>>,
}
impl WatchosManager {
pub fn new() -> Result<Option<Self>> {
let devices = simulators(AppleSimulatorType::Watchos)?;
Ok(Some(Self { devices }))
}
}
impl PlatformManager for WatchosManager {
fn devices(&self) -> Result<Vec<Box<dyn Device>>> {
Ok(self.devices.clone())
}
fn platforms(&self) -> Result<Vec<Box<dyn Platform>>> {
["arm64_32", "aarch64", "x86_64-sim", "aarch64-sim"]
.iter()
.map(|arch| {
let id = format!("auto-watchos-{}", arch);
// Apple watch simulator targets are x86_64-apple-watchos-sim or
// aarch64-apple-watchos-sim
let rustc_triple = if *arch == "aarch64-sim" {
format!("aarch64-apple-watchos-sim")
} else if *arch == "x86_64-sim" {
format!("x86_64-apple-watchos-sim")
} else {
format!("{}-apple-watchos", arch)
};
let simulator = if *arch == "x86_64-sim" || *arch == "aarch64-sim" {
Some(AppleSimulatorType::Watchos)
} else {
None
};
AppleDevicePlatform::new(
id,
&rustc_triple,
simulator,
crate::config::PlatformConfiguration::default(),
)
.map(|pf| pf as Box<dyn Platform>)
})
.collect()
}
}
pub struct TvosManager {
devices: Vec<Box<dyn Device>>,
}
impl TvosManager {
pub fn new() -> Result<Option<Self>> {
let devices = simulators(AppleSimulatorType::Tvos)?;
Ok(Some(Self { devices }))
}
}
impl PlatformManager for TvosManager {
fn devices(&self) -> Result<Vec<Box<dyn Device>>> {
Ok(self.devices.clone())
}
fn platforms(&self) -> Result<Vec<Box<dyn Platform>>> {
["aarch64", "x86_64", "aarch64-sim"]
.iter()
.map(|arch| {
let id = format!("auto-tvos-{}", arch);
let rustc_triple = if *arch != "aarch64-sim" {
format!("{}-apple-tvos", arch)
} else {
format!("aarch64-apple-tvos-sim")
};
let simulator = if *arch == "x86_64" || *arch == "aarch64-sim" {
Some(AppleSimulatorType::Tvos)
} else {
None
};
AppleDevicePlatform::new(
id,
&rustc_triple,
simulator,
crate::config::PlatformConfiguration::default(),
)
.map(|pf| pf as Box<dyn Platform>)
})
.collect()
}
}
fn simulators(sim_type: AppleSimulatorType) -> Result<Vec<Box<dyn Device>>> {
let sims_list = ::std::process::Command::new("xcrun")
.args(&[
"simctl",
"list",
"--json",
"devices",
sim_type.to_string().as_str(),
])
.output()?;
if !sims_list.status.success() {
info!(
"Failed while looking for ios simulators. It this is not expected, you need to make sure `xcrun simctl list --json` works."
);
return Ok(vec![]);
}
let sims_list = String::from_utf8(sims_list.stdout)?;
let sims_list = json::parse(&sims_list)
.with_context(|| "Could not parse output for: `xcrun simctl list --json devices` as json. Please try to make this command work and retry.")?;
let mut sims: Vec<Box<dyn Device>> = vec![];
for (ref k, ref v) in sims_list["devices"].entries() {
for ref sim in v.members() {
if sim["state"] == "Booted" {
sims.push(Box::new(AppleSimDevice {
name: sim["name"]
.as_str()
.ok_or_else(|| anyhow!("unexpected simulator list format (missing name)"))?
.to_string(),
id: sim["udid"]
.as_str()
.ok_or_else(|| anyhow!("unexpected simulator list format (missing udid)"))?
.to_string(),
os: k.split(" ").last().unwrap().to_string(),
sim_type: sim_type.clone(),
}))
}
}
}
Ok(sims)
}
fn devices() -> Result<Vec<Box<dyn Device>>> {
let mut devices: HashMap<String, IosDevice> = Default::default();
if which::which("xcrun").is_err() {
log::warn!("xcrun not found. Apple devices support disabled. Consider installing XCode and its command line tools.");
return Ok(vec![]);
}
if !std::process::Command::new("xcrun")
.arg("--find")
.arg("devicectl")
.output()?
.status
.success()
{
log::warn!("xcrun devicectl not found. Apple devices support disabled. Consider updating XCode and its command line tools.");
return Ok(vec![]);
}
devices_from_devicectl(&mut devices)?;
devices_from_ios_deploy(&mut devices)?;
Ok(devices
.into_values()
.map(|d| Box::new(d) as _)
.collect_vec())
}
fn devices_from_devicectl(devices: &mut HashMap<String, IosDevice>) -> Result<()> {
let tempdir = tempfile::TempDir::with_prefix("dinghy-ios")?;
let tmpjson = tempdir.path().join("json");
let devicectl = std::process::Command::new("xcrun")
.args("devicectl list devices --quiet --json-output".split_whitespace().collect_vec())
.arg(&tmpjson)
.stderr(std::process::Stdio::inherit())
.output()
.context("Failed to launch xcrun command. Please check that \"xcrun devicectl list devices\" works")?;
if !devicectl.status.success() {
bail!("xcrun command failed. Please check that \"xcrun devicectl list devices\" works.\n{devicectl:?}");
}
let txt = std::fs::read_to_string(&tmpjson)
.with_context(|| format!("Reading devicectl json output {tmpjson:?}"))?;
for device in json::parse(&txt)?["result"]["devices"].members() {
let Some(udid) = device["hardwareProperties"]["udid"]
.as_str()
.map(|s| s.to_string())
else {
continue;
};
let device = IosDevice::new(
device["deviceProperties"]["name"]
.as_str()
.context("no name in device json")?
.to_string(),
udid.clone(),
device["hardwareProperties"]["cpuType"]["name"]
.as_str()
.context("no cpuType in device json")?,
device["deviceProperties"]["osVersionNumber"]
.as_str()
.context("no osVersionNumber")?
.to_string(),
)?;
devices.insert(udid, device);
}
Ok(())
}
fn devices_from_ios_deploy(devices: &mut HashMap<String, IosDevice>) -> Result<()> {
let list = ::std::process::Command::new("ios-deploy")
.stderr(std::process::Stdio::inherit())
.args(&["-c", "--json", "-t", "1"])
.output();
let list = match list {
Ok(l) => l,
Err(e) => {
info!(
"Could not execute ios-deploy to look for legacy (before iOS 17) iOS devices ({}). Consider installing ios-deploy (`brew install ios-deploy`...) for legacy iOS support.", e);
return Ok(());
}
};
if !list.status.success() {
info!(
"ios-deploy returned an error while listing devices. It this is not expected, you need to make sure `ios-deploy --json -c -t 1` works as expected. ios-deploy is needed for pre-ios17 devices."
);
return Ok(());
}
// ios-deploy outputs each device as a multiline json dict, with separator or delimiter. make
// it a json array.
let list = String::from_utf8(list.stdout)?.replace("}{", "},{");
let list = format!("[{}]", list);
let list = ::json::parse(&list)
.with_context(|| "Could not parse output for: `ios-deploy --json -c -t 1` as json. Please try to make this command work and retry.")?;
for json in list.members() {
let device = &json["Device"];
let id = device["DeviceIdentifier"]
.as_str()
.context("DeviceIdentifier expected to be a string")?
.to_owned();
let name = device["DeviceName"]
.as_str()
.context("DeviceName expected to be a string")?
.to_owned();
let arch_cpu = device["modelArch"].as_str().unwrap_or("arm64");
let ios_version = device["ProductVersion"]
.as_str()
.context("ProductVersion expected to be a string")?
.to_string();
devices.insert(
name.clone(),
IosDevice::new(name, id, &arch_cpu, ios_version)?,
);
}
Ok(())
}
================================================
FILE: dinghy-lib/src/apple/platform.rs
================================================
use crate::config::PlatformConfiguration;
use crate::errors::*;
use crate::overlay::Overlayer;
use crate::project::Project;
use crate::toolchain::Toolchain;
use crate::Build;
use crate::Device;
use crate::Platform;
use crate::SetupArgs;
use dinghy_build::build_env::set_env;
use std::fmt::{Debug, Display, Formatter};
use std::process;
use super::AppleSimulatorType;
pub struct AppleDevicePlatform {
id: String,
pub sim: Option<AppleSimulatorType>,
pub toolchain: Toolchain,
pub configuration: PlatformConfiguration,
}
impl Debug for AppleDevicePlatform {
fn fmt(&self, fmt: &mut Formatter) -> ::std::fmt::Result {
write!(fmt, "{}", self.id)
}
}
impl AppleDevicePlatform {
pub fn new(
id: String,
rustc_triple: &str,
simulator: Option<AppleSimulatorType>,
configuration: PlatformConfiguration,
) -> Result<Box<dyn Platform>> {
Ok(Box::new(AppleDevicePlatform {
id,
sim: simulator,
toolchain: Toolchain {
rustc_triple: rustc_triple.to_string(),
},
configuration,
}))
}
fn sysroot_path(&self) -> Result<String> {
let sdk_name = match self.sim {
Some(AppleSimulatorType::Ios) => "iphonesimulator",
Some(AppleSimulatorType::Tvos) => "appletvsimulator",
Some(AppleSimulatorType::Watchos) => "watchsimulator",
None => "iphoneos",
};
let xcrun = process::Command::new("xcrun")
.args(&["--sdk", sdk_name, "--show-sdk-path"])
.output()?;
Ok(String::from_utf8(xcrun.stdout)?.trim_end().to_string())
}
}
impl Platform for AppleDevicePlatform {
fn setup_env(&self, project: &Project, setup_args: &SetupArgs) -> Result<()> {
let sysroot = self.sysroot_path()?;
Overlayer::overlay(&self.configuration, self, project, &self.sysroot_path()?)?;
self.toolchain.setup_cc(self.id().as_str(), "gcc")?;
set_env("TARGET_SYSROOT", &sysroot);
self.toolchain.setup_linker(
&self.id(),
&format!("cc -isysroot {}", sysroot),
&project.metadata.workspace_root,
)?;
self.toolchain.setup_runner(&self.id(), setup_args)?;
self.toolchain.setup_target()?;
self.toolchain.setup_pkg_config()?;
Ok(())
}
fn id(&self) -> String {
self.id.to_string()
}
fn is_compatible_with(&self, device: &dyn Device) -> bool {
device.is_compatible_with_simulator_platform(self)
}
fn is_host(&self) -> bool {
false
}
fn rustc_triple(&self) -> &str {
&self.toolchain.rustc_triple
}
fn strip(&self, build: &mut Build) -> Result<()> {
let mut command = ::std::process::Command::new("xcrun");
command.arg("strip");
build.runnable = crate::platform::strip_runnable(&build.runnable, command)?;
Ok(())
}
fn sysroot(&self) -> Result<Option<std::path::PathBuf>> {
self.sysroot_path().map(|s| Some(s.into()))
}
}
impl Display for AppleDevicePlatform {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::result::Result<(), ::std::fmt::Error> {
if self.sim.is_some() {
write!(f, "XCode targetting Apple device Simulator")
} else {
write!(f, "XCode targetting Ios Device")
}
}
}
================================================
FILE: dinghy-lib/src/apple/xcode.rs
================================================
use super::{AppleSimulatorType, SignatureSettings, SigningIdentity};
use crate::errors::*;
use fs_err as fs;
use log::{debug, trace};
use std::io::Write;
use std::{io, process};
use crate::utils::LogCommandExt;
use crate::BuildBundle;
pub fn add_plist_to_app(
bundle: &BuildBundle,
arch: &str,
app_bundle_id: &str,
sim_type: Option<&AppleSimulatorType>,
) -> Result<()> {
let mut plist = fs::File::create(bundle.bundle_dir.join("Info.plist"))?;
writeln!(plist, r#"<?xml version="1.0" encoding="UTF-8"?>"#)?;
writeln!(
plist,
r#"<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">"#
)?;
writeln!(plist, r#"<plist version="1.0"><dict>"#)?;
writeln!(plist, "<key>CFBundleName</key><string>Dinghy</string>",)?;
writeln!(
plist,
"<key>CFBundleExecutable</key><string>Dinghy</string>",
)?;
writeln!(
plist,
"<key>CFBundleIdentifier</key><string>{}</string>",
app_bundle_id
)?;
writeln!(plist, "<key>CFBundleVersion</key>")?;
writeln!(plist, "<string>{}</string>", arch)?;
writeln!(plist, "<key>CFBundleShortVersionString</key>")?;
writeln!(plist, "<string>{}</string>", arch)?;
match sim_type {
// The iOS/tvOS simulator have the same plist as an iOS device.
Some(AppleSimulatorType::Ios) | Some(AppleSimulatorType::Tvos) | None => {
writeln!(plist, "<key>UIRequiredDeviceCapabilities</key>")?;
writeln!(plist, "<array><string>{}</string></array>", arch)?;
writeln!(plist, "<key>UILaunchStoryboardName</key>")?;
writeln!(plist, "<string></string>")?;
}
Some(AppleSimulatorType::Watchos) => {
writeln!(plist, "<key>MinimumOSVersion</key><string>8.0</string>",)?;
writeln!(plist, "<key>WKApplication</key><true/>",)?;
writeln!(plist, "<key>WKWatchOnly</key><true/>")?;
}
}
writeln!(plist, r#"</dict></plist>"#)?;
Ok(())
}
pub fn sign_app(bundle: &BuildBundle, settings: &SignatureSettings) -> Result<()> {
debug!(
"Will sign {:?} with team: {} using key: {} and profile: {}",
bundle.bundle_dir, settings.identity.team, settings.identity.name, settings.file
);
let entitlements = bundle.root_dir.join("entitlements.xcent");
debug!("entitlements file: {}", entitlements.to_str().unwrap_or(""));
let mut plist = fs::File::create(&entitlements)?;
writeln!(plist, r#"<?xml version="1.0" encoding="UTF-8"?>"#)?;
writeln!(
plist,
r#"<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">"#
)?;
writeln!(plist, r#"<plist version="1.0"><dict>"#)?;
writeln!(plist, "{}", settings.entitlements)?;
writeln!(plist, r#"</dict></plist>"#)?;
let result = process::Command::new("codesign")
.args(&["-s", &*settings.identity.name, "--entitlements"])
.arg(entitlements)
.arg(&bundle.bundle_dir)
.log_invocation(2)
.status()?;
if !result.success() {
bail!("Failure to sign application: codesign utility returned non-zero");
}
Ok(())
}
pub fn look_for_signature_settings(device_id: &str) -> Result<Vec<SignatureSettings>> {
let identity_regex = ::regex::Regex::new(r#"^ *[0-9]+\) ([A-Z0-9]{40}) "(.+)"$"#)?;
let subject_regex = ::regex::Regex::new(r#"OU *= *([^,]+)"#)?;
let mut identities: Vec<SigningIdentity> = vec![];
let find_identities = process::Command::new("security")
.args(&["find-identity", "-v", "-p", "codesigning"])
.log_invocation(3)
.output()?;
for line in String::from_utf8(find_identities.stdout)?.split("\n") {
if let Some(caps) = identity_regex.captures(&line) {
let name: String = caps[2].into();
if !name.starts_with("iPhone Developer: ") && !name.starts_with("Apple Development:") {
continue;
}
let subject = process::Command::new("sh")
.arg("-c")
.arg(format!(
"security find-certificate -a -c \"{}\" -p | openssl x509 -text | \
grep Subject:",
name
))
.log_invocation(3)
.output()?;
let subject = String::from_utf8(subject.stdout)?;
if let Some(ou) = subject_regex.captures(&subject) {
identities.push(SigningIdentity {
id: caps[1].into(),
name: caps[2].into(),
team: ou[1].into(),
})
}
}
}
debug!("Possible signing identities: {:?}", identities);
let mut settings = vec![];
let home = dirs::home_dir().expect("can't get HOME dir");
let profiles_dir = [
"Library/MobileDevice/Provisioning Profiles",
"Library/Developer/Xcode/UserData/Provisioning Profiles", // xcode 16 and above
];
for file in profiles_dir
.iter()
.filter_map(|path| fs::read_dir(home.join(path)).ok())
.flatten()
{
let file = file?;
if file.path().starts_with(".")
|| file
.path()
.extension()
.map(|ext| ext.to_string_lossy() != "mobileprovision")
.unwrap_or(true)
{
trace!(
" - skipping {:?} (not a mobileprovision profile)",
file.path()
);
continue;
}
let decoded = process::Command::new("security")
.arg("cms")
.arg("-D")
.arg("-i")
.arg(file.path())
.log_invocation(3)
.output()?;
let plist = plist::Value::from_reader(io::Cursor::new(&decoded.stdout))
.with_context(|| format!("While reading profile {:?}", file.path()))?;
let dict = plist
.as_dictionary()
.ok_or_else(|| anyhow!("plist root should be a dictionary"))?;
let devices = if let Some(d) = dict.get("ProvisionedDevices") {
d
} else {
trace!(" - skipping {:?} (no devices)", file.path());
continue;
};
let devices = if let Some(ds) = devices.as_array() {
ds
} else {
bail!("ProvisionedDevices expected to be array")
};
if !devices.contains(&plist::Value::String(device_id.into())) {
trace!(" - skipping {:?} (not matching target device)", file.path());
continue;
}
let name = dict
.get("Name")
.ok_or_else(|| anyhow!(format!("No name in profile {:?}", file.path())))?;
let name = name
.as_string()
.ok_or_else(|| anyhow!("Name should have been a string in {:?}", file.path()))?;
if !name.ends_with("Dinghy") && !name.ends_with(" *") {
trace!(" - skipping {:?} (wrong app)", file.path());
continue;
}
// TODO: check date in future
let team = dict
.get("TeamIdentifier")
.ok_or_else(|| anyhow!("no TeamIdentifier"))?;
let team = team
.as_array()
.ok_or_else(|| anyhow!("TeamIdentifier should be an array"))?;
let team = team
.first()
.ok_or_else(|| anyhow!("empty TeamIdentifier"))?
.as_string()
.ok_
gitextract_xzzdib82/
├── .github/
│ └── workflows/
│ ├── binaries.yml
│ ├── release.yml
│ └── test.yml
├── .gitignore
├── .travis.apple-third-tier.sh
├── .travis.sh
├── .travis.yml
├── CHANGELOG.md
├── Cargo.toml
├── LICENSE
├── LICENSE-APACHE
├── LICENSE-MIT
├── README.md
├── cargo-dinghy/
│ ├── Cargo.toml
│ └── src/
│ ├── cli.rs
│ └── main.rs
├── dinghy-build/
│ ├── Cargo.toml
│ └── src/
│ ├── bindgen_macros.rs
│ ├── build.rs
│ ├── build_env.rs
│ ├── lib.rs
│ └── utils.rs
├── dinghy-lib/
│ ├── Cargo.toml
│ ├── build.rs
│ └── src/
│ ├── android/
│ │ ├── device.rs
│ │ ├── mod.rs
│ │ └── platform.rs
│ ├── apple/
│ │ ├── device.rs
│ │ ├── helpers.py
│ │ ├── mod.rs
│ │ ├── platform.rs
│ │ └── xcode.rs
│ ├── config.rs
│ ├── device.rs
│ ├── host/
│ │ ├── mod.rs
│ │ └── platform.rs
│ ├── lib.rs
│ ├── overlay.rs
│ ├── platform/
│ │ ├── mod.rs
│ │ └── regular_platform.rs
│ ├── plugin/
│ │ └── mod.rs
│ ├── project.rs
│ ├── script/
│ │ ├── device.rs
│ │ └── mod.rs
│ ├── ssh/
│ │ ├── device.rs
│ │ └── mod.rs
│ ├── toolchain.rs
│ └── utils.rs
├── dinghy-test/
│ ├── Cargo.toml
│ └── src/
│ └── lib.rs
├── docs/
│ ├── android.md
│ ├── dinghy-build.md
│ ├── files.md
│ ├── filter.md
│ ├── ios.md
│ ├── overlay.md
│ ├── ssh.md
│ └── vars.md
├── legacy/
│ ├── Cargo.toml
│ ├── README.md
│ ├── build.rs
│ └── src/
│ └── main.rs
├── musl_build.sh
├── post-release.sh
├── pre-release.sh
├── release.sh
├── rust-toolchain
└── test-ws/
├── .dinghy.toml
├── Cargo.toml
├── rust-toolchain
├── test-app/
│ ├── .dinghy.toml
│ ├── Cargo.toml
│ └── src/
│ └── lib.rs
├── test-bin/
│ ├── Cargo.toml
│ └── src/
│ └── main.rs
└── test-proc-macro/
├── Cargo.toml
└── src/
└── lib.rs
SYMBOL INDEX (338 symbols across 36 files)
FILE: cargo-dinghy/src/cli.rs
type DinghyGeneralArgs (line 6) | pub struct DinghyGeneralArgs {
type SubCommandWrapper (line 46) | pub struct SubCommandWrapper {
type DinghySubcommand (line 52) | pub enum DinghySubcommand {
type DinghyMode (line 78) | pub enum DinghyMode {
type DinghyCli (line 85) | pub struct DinghyCli {
method parse (line 91) | pub fn parse() -> Self {
FILE: cargo-dinghy/src/main.rs
function main (line 26) | fn main() {
function run_command (line 48) | fn run_command(cli: DinghyCli) -> Result<()> {
function create_cargo_subcomand (line 419) | fn create_cargo_subcomand(
function show_all_platforms (line 456) | fn show_all_platforms(dinghy: &Dinghy) -> Result<()> {
function show_all_devices (line 465) | fn show_all_devices(dinghy: &Dinghy) -> Result<()> {
function show_all_devices_for_platform (line 470) | fn show_all_devices_for_platform(dinghy: &Dinghy, platform: Arc<Box<dyn ...
function show_devices (line 478) | fn show_devices(dinghy: &Dinghy, platform: Option<Arc<Box<dyn Platform>>...
function select_platform_and_device_from_cli (line 506) | fn select_platform_and_device_from_cli(
function find_first_device_for_platform (line 563) | fn find_first_device_for_platform(
FILE: dinghy-build/src/build.rs
function is_cross_compiling (line 7) | pub fn is_cross_compiling() -> Result<bool> {
function include_path (line 12) | pub fn include_path<P: AsRef<Path>>(lib_dir_path: P) -> Result<()> {
function link_static (line 18) | pub fn link_static<P: AsRef<Path>>(lib_name: &str, lib_dir_path: P) -> R...
function link_dylib (line 28) | pub fn link_dylib<P: AsRef<Path>>(lib_name: &str, lib_dir_path: P) -> Re...
function link_lib (line 38) | pub fn link_lib<P: AsRef<Path>>(lib_name: &str, lib_dir_path: P) -> Resu...
function link_system_dylib (line 48) | pub fn link_system_dylib(lib_name: &str) -> Result<()> {
function link_system_lib (line 54) | pub fn link_system_lib(lib_name: &str) -> Result<()> {
function rerun_if_changed (line 60) | pub fn rerun_if_changed<P: AsRef<Path>>(filepath: P) {
FILE: dinghy-build/src/build_env.rs
function append_path_to_env (line 23) | pub fn append_path_to_env<K: AsRef<OsStr>, V: AsRef<OsStr>>(key: K, valu...
function append_path_to_target_env (line 35) | pub fn append_path_to_target_env<K: AsRef<OsStr>, R: AsRef<str>, V: AsRe...
function build_env (line 47) | pub fn build_env(name: &str) -> Result<String> {
function envify (line 57) | pub fn envify<S: AsRef<str>>(name: S) -> String {
function set_all_env (line 66) | pub fn set_all_env<K: AsRef<OsStr>, V: AsRef<OsStr>>(env: &[(K, V)]) {
function set_env (line 73) | pub fn set_env<K: AsRef<OsStr>, V: AsRef<OsStr>>(k: K, v: V) {
function set_env_ifndef (line 83) | pub fn set_env_ifndef<K: AsRef<OsStr>, V: AsRef<OsStr>>(k: K, v: V) {
function set_target_env (line 102) | pub fn set_target_env<K: AsRef<OsStr>, R: AsRef<str>, V: AsRef<OsStr>>(
function sysroot_path (line 112) | pub fn sysroot_path() -> Result<PathBuf> {
function target_env (line 120) | pub fn target_env(var_base: &str) -> Result<String> {
function target_env_from_triple (line 130) | pub fn target_env_from_triple(var_base: &str, triple: &str, is_host: boo...
function target_key_from_triple (line 143) | fn target_key_from_triple<K: AsRef<OsStr>, R: AsRef<str>>(
FILE: dinghy-build/src/lib.rs
type CommandExt (line 32) | pub trait CommandExt {
method arg_for_macos (line 34) | fn arg_for_macos<S: AsRef<OsStr>>(&mut self, arg: S) -> Result<&mut Co...
method configure_prefix (line 38) | fn configure_prefix<P: AsRef<Path>>(&mut self, path: P) -> Result<&mut...
method with_pkgconfig (line 44) | fn with_pkgconfig(&mut self) -> Result<&mut Command>;
method with_toolchain (line 48) | fn with_toolchain(&mut self) -> Result<&mut Command>;
method arg_for_macos (line 52) | fn arg_for_macos<S: AsRef<OsStr>>(&mut self, arg: S) -> Result<&mut Co...
method configure_prefix (line 62) | fn configure_prefix<P: AsRef<Path>>(&mut self, prefix_dir: P) -> Resul...
method with_pkgconfig (line 73) | fn with_pkgconfig(&mut self) -> Result<&mut Command> {
method with_toolchain (line 91) | fn with_toolchain(&mut self) -> Result<&mut Command> {
FILE: dinghy-build/src/utils.rs
function path_to_str (line 9) | pub fn path_to_str(path: &PathBuf) -> Result<&str> {
function path_between (line 16) | pub fn path_between<P1: AsRef<Path>, P2: AsRef<Path>>(from: P1, to: P2) ...
FILE: dinghy-lib/build.rs
function main (line 1) | fn main() {
FILE: dinghy-lib/src/android/device.rs
type AndroidDevice (line 17) | pub struct AndroidDevice {
method from_id (line 24) | pub fn from_id(adb: path::PathBuf, id: &str) -> Result<AndroidDevice> {
method adb (line 67) | fn adb(&self) -> Result<process::Command> {
method install_app (line 73) | fn install_app(&self, project: &Project, build: &Build) -> Result<(Bui...
method sync (line 130) | fn sync<FP: AsRef<path::Path>, TP: AsRef<path::Path>>(
method to_remote_bundle (line 157) | fn to_remote_bundle(build_bundle: &BuildBundle) -> Result<BuildBundle> {
method fmt (line 287) | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
method fmt (line 293) | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
method is_compatible_with_regular_platform (line 163) | fn is_compatible_with_regular_platform(&self, platform: &RegularPlatform...
method clean_app (line 177) | fn clean_app(&self, build_bundle: &BuildBundle) -> Result<()> {
method debug_app (line 207) | fn debug_app(
method id (line 217) | fn id(&self) -> &str {
method name (line 221) | fn name(&self) -> &str {
method run_app (line 225) | fn run_app(
FILE: dinghy-lib/src/android/mod.rs
type AndroidManager (line 18) | pub struct AndroidManager {
method probe (line 144) | pub fn probe() -> Option<AndroidManager> {
method devices (line 23) | fn devices(&self) -> Result<Vec<Box<dyn Device>>> {
method platforms (line 42) | fn platforms(&self) -> Result<Vec<Box<dyn Platform>>> {
function probable_sdk_locs (line 158) | fn probable_sdk_locs() -> Result<Vec<path::PathBuf>> {
function ndk (line 196) | fn ndk() -> Result<Option<path::PathBuf>> {
function ndk_version (line 215) | fn ndk_version(ndk: &path::Path) -> Result<String> {
function adb (line 231) | fn adb() -> Result<path::PathBuf> {
function find_non_legacy_ndk (line 259) | fn find_non_legacy_ndk(sdk: &path::Path) -> Result<Option<path::PathBuf>> {
function test_find_non_legacy_ndk (line 292) | fn test_find_non_legacy_ndk() {
function test_find_non_legacy_ndk_on_non_existing_directory (line 309) | fn test_find_non_legacy_ndk_on_non_existing_directory() {
FILE: dinghy-lib/src/android/platform.rs
type AndroidPlatform (line 11) | pub struct AndroidPlatform {
method fmt (line 20) | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
method new (line 26) | pub fn new(
method setup_env (line 49) | fn setup_env(&self, project: &Project, setup_args: &SetupArgs) -> anyhow...
method id (line 99) | fn id(&self) -> String {
method is_compatible_with (line 103) | fn is_compatible_with(&self, device: &dyn Device) -> bool {
method is_host (line 107) | fn is_host(&self) -> bool {
method rustc_triple (line 111) | fn rustc_triple(&self) -> &str {
method strip (line 115) | fn strip(&self, build: &mut Build) -> anyhow::Result<()> {
method sysroot (line 127) | fn sysroot(&self) -> anyhow::Result<Option<PathBuf>> {
FILE: dinghy-lib/src/apple/device.rs
type IosDevice (line 26) | pub struct IosDevice {
method new (line 45) | pub fn new(name: String, id: String, arch_cpu: &str, os: String) -> Re...
method is_pre_ios_17 (line 59) | fn is_pre_ios_17(&self) -> Result<bool> {
method is_locked (line 68) | fn is_locked(&self) -> Result<bool> {
method make_app (line 88) | fn make_app(
method install_app (line 110) | fn install_app(
method run_remote (line 142) | fn run_remote(
method install_app_with_ios_deploy (line 264) | fn install_app_with_ios_deploy(&self, bundle: &str) -> Result<()> {
method run_remote_with_ios_deploy (line 274) | fn run_remote_with_ios_deploy(
type AppleSimDevice (line 35) | pub struct AppleSimDevice {
method install_app (line 358) | fn install_app(
method make_app (line 397) | fn make_app(
method clean_app (line 302) | fn clean_app(&self, _build_bundle: &BuildBundle) -> Result<()> {
method debug_app (line 306) | fn debug_app(
method id (line 327) | fn id(&self) -> &str {
method name (line 331) | fn name(&self) -> &str {
method run_app (line 335) | fn run_app(
method clean_app (line 408) | fn clean_app(&self, _build_bundle: &BuildBundle) -> Result<()> {
method debug_app (line 412) | fn debug_app(
method id (line 441) | fn id(&self) -> &str {
method name (line 445) | fn name(&self) -> &str {
method run_app (line 449) | fn run_app(
method fmt (line 472) | fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
method fmt (line 482) | fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
method is_compatible_with_simulator_platform (line 488) | fn is_compatible_with_simulator_platform(&self, platform: &AppleDevicePl...
method is_compatible_with_simulator_platform (line 501) | fn is_compatible_with_simulator_platform(&self, platform: &AppleDevicePl...
function make_apple_app (line 510) | fn make_apple_app(
function launch_app (line 538) | fn launch_app(dev: &AppleSimDevice, app_args: &[&str], _envs: &[&str]) -...
function launch_lldb_simulator (line 637) | fn launch_lldb_simulator(
FILE: dinghy-lib/src/apple/helpers.py
function connect_command (line 5) | def connect_command(debugger, command, result, internal_dict):
function set_remote_path (line 10) | def set_remote_path(debugger, command, result, internal_dict):
function start (line 15) | def start(debugger, command, result, internal_dict):
FILE: dinghy-lib/src/apple/mod.rs
type SignatureSettings (line 17) | pub struct SignatureSettings {
type SigningIdentity (line 27) | pub struct SigningIdentity {
type AppleSimulatorType (line 35) | pub enum AppleSimulatorType {
method fmt (line 41) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
type IosManager (line 51) | pub struct IosManager {
method new (line 56) | pub fn new() -> Result<Option<IosManager>> {
method devices (line 71) | fn devices(&self) -> Result<Vec<Box<dyn Device>>> {
method platforms (line 75) | fn platforms(&self) -> Result<Vec<Box<dyn Platform>>> {
type WatchosManager (line 111) | pub struct WatchosManager {
method new (line 116) | pub fn new() -> Result<Option<Self>> {
method devices (line 122) | fn devices(&self) -> Result<Vec<Box<dyn Device>>> {
method platforms (line 126) | fn platforms(&self) -> Result<Vec<Box<dyn Platform>>> {
type TvosManager (line 159) | pub struct TvosManager {
method new (line 164) | pub fn new() -> Result<Option<Self>> {
method devices (line 171) | fn devices(&self) -> Result<Vec<Box<dyn Device>>> {
method platforms (line 175) | fn platforms(&self) -> Result<Vec<Box<dyn Platform>>> {
function simulators (line 202) | fn simulators(sim_type: AppleSimulatorType) -> Result<Vec<Box<dyn Device...
function devices (line 243) | fn devices() -> Result<Vec<Box<dyn Device>>> {
function devices_from_devicectl (line 267) | fn devices_from_devicectl(devices: &mut HashMap<String, IosDevice>) -> R...
function devices_from_ios_deploy (line 307) | fn devices_from_ios_deploy(devices: &mut HashMap<String, IosDevice>) -> ...
FILE: dinghy-lib/src/apple/platform.rs
type AppleDevicePlatform (line 16) | pub struct AppleDevicePlatform {
method new (line 30) | pub fn new(
method sysroot_path (line 46) | fn sysroot_path(&self) -> Result<String> {
method fmt (line 24) | fn fmt(&self, fmt: &mut Formatter) -> ::std::fmt::Result {
method setup_env (line 61) | fn setup_env(&self, project: &Project, setup_args: &SetupArgs) -> Result...
method id (line 77) | fn id(&self) -> String {
method is_compatible_with (line 81) | fn is_compatible_with(&self, device: &dyn Device) -> bool {
method is_host (line 85) | fn is_host(&self) -> bool {
method rustc_triple (line 89) | fn rustc_triple(&self) -> &str {
method strip (line 93) | fn strip(&self, build: &mut Build) -> Result<()> {
method sysroot (line 100) | fn sysroot(&self) -> Result<Option<std::path::PathBuf>> {
method fmt (line 106) | fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::result::Result<()...
FILE: dinghy-lib/src/apple/xcode.rs
function add_plist_to_app (line 11) | pub fn add_plist_to_app(
function sign_app (line 56) | pub fn sign_app(bundle: &BuildBundle, settings: &SignatureSettings) -> R...
function look_for_signature_settings (line 86) | pub fn look_for_signature_settings(device_id: &str) -> Result<Vec<Signat...
FILE: dinghy-lib/src/config.rs
type TestData (line 11) | pub struct TestData {
type TestDataConfiguration (line 20) | pub struct TestDataConfiguration {
method deserialize (line 34) | fn deserialize<D>(deserializer: D) -> result::Result<Self, D::Error>
type DetailedTestDataConfiguration (line 27) | pub struct DetailedTestDataConfiguration {
type Configuration (line 81) | pub struct Configuration {
method merge (line 160) | pub fn merge(&mut self, file: &path::Path) -> Result<()> {
type ConfigurationFileContent (line 90) | struct ConfigurationFileContent {
type PlatformConfiguration (line 99) | pub struct PlatformConfiguration {
method empty (line 109) | pub fn empty() -> Self {
method env (line 120) | pub fn env(&self) -> Vec<(String, String)> {
type OverlayConfiguration (line 133) | pub struct OverlayConfiguration {
type SshDeviceConfiguration (line 139) | pub struct SshDeviceConfiguration {
type ScriptDeviceConfiguration (line 154) | pub struct ScriptDeviceConfiguration {
function read_config_file (line 186) | fn read_config_file<P: AsRef<path::Path>>(file: P) -> Result<Configurati...
function dinghy_config (line 192) | pub fn dinghy_config<P: AsRef<path::Path>>(dir: P) -> Result<Configurati...
function load_config_with_str_test_data (line 231) | fn load_config_with_str_test_data() {
FILE: dinghy-lib/src/device.rs
function make_remote_app (line 11) | pub fn make_remote_app(project: &Project, build: &Build) -> Result<Build...
function make_remote_app_with_name (line 15) | pub fn make_remote_app_with_name(
FILE: dinghy-lib/src/host/mod.rs
type HostManager (line 7) | pub struct HostManager {
method probe (line 12) | pub fn probe(conf: &Configuration) -> Option<HostManager> {
method platform (line 21) | fn platform(&self) -> Result<HostPlatform> {
method devices (line 27) | fn devices(&self) -> Result<Vec<Box<dyn Device>>> {
method platforms (line 31) | fn platforms(&self) -> Result<Vec<Box<dyn Platform>>> {
FILE: dinghy-lib/src/host/platform.rs
type HostPlatform (line 18) | pub struct HostPlatform {
method new (line 24) | pub fn new(configuration: PlatformConfiguration) -> Result<HostPlatfor...
method fmt (line 33) | fn fmt(&self, fmt: &mut Formatter) -> ::std::fmt::Result {
method setup_env (line 39) | fn setup_env(&self, project: &Project, setup_args: &SetupArgs) -> Result...
method id (line 66) | fn id(&self) -> String {
method is_compatible_with (line 70) | fn is_compatible_with(&self, device: &dyn Device) -> bool {
method is_host (line 74) | fn is_host(&self) -> bool {
method rustc_triple (line 78) | fn rustc_triple(&self) -> &str {
method strip (line 82) | fn strip(&self, build: &mut Build) -> Result<()> {
method sysroot (line 89) | fn sysroot(&self) -> Result<Option<std::path::PathBuf>> {
FILE: dinghy-lib/src/lib.rs
type Dinghy (line 36) | pub struct Dinghy {
method probe (line 42) | pub fn probe(conf: &sync::Arc<Configuration>) -> Result<Dinghy> {
method devices (line 115) | pub fn devices(&self) -> Vec<sync::Arc<Box<dyn Device>>> {
method host_platform (line 119) | pub fn host_platform(&self) -> sync::Arc<Box<dyn Platform>> {
method platforms (line 123) | pub fn platforms(&self) -> Vec<sync::Arc<Box<dyn Platform>>> {
method platform_by_name (line 130) | pub fn platform_by_name(
type Device (line 142) | pub trait Device: std::fmt::Debug + Display + DeviceCompatibility + DynC...
method clean_app (line 143) | fn clean_app(&self, build_bundle: &BuildBundle) -> Result<()>;
method debug_app (line 145) | fn debug_app(
method id (line 153) | fn id(&self) -> &str;
method name (line 155) | fn name(&self) -> &str;
method run_app (line 157) | fn run_app(
type DeviceCompatibility (line 168) | pub trait DeviceCompatibility {
method is_compatible_with_regular_platform (line 169) | fn is_compatible_with_regular_platform(&self, _platform: &RegularPlatf...
method is_compatible_with_host_platform (line 173) | fn is_compatible_with_host_platform(&self, _platform: &host::HostPlatf...
method is_compatible_with_simulator_platform (line 178) | fn is_compatible_with_simulator_platform(
type Platform (line 186) | pub trait Platform: std::fmt::Debug {
method setup_env (line 187) | fn setup_env(&self, project: &Project, setup_args: &SetupArgs) -> Resu...
method id (line 189) | fn id(&self) -> String;
method is_compatible_with (line 191) | fn is_compatible_with(&self, device: &dyn Device) -> bool;
method is_host (line 193) | fn is_host(&self) -> bool;
method rustc_triple (line 194) | fn rustc_triple(&self) -> &str;
method strip (line 196) | fn strip(&self, build: &mut Build) -> Result<()>;
method sysroot (line 197) | fn sysroot(&self) -> Result<Option<path::PathBuf>>;
method fmt (line 201) | fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
type PlatformManager (line 206) | pub trait PlatformManager {
method devices (line 207) | fn devices(&self) -> Result<Vec<Box<dyn Device>>>;
method platforms (line 208) | fn platforms(&self) -> Result<Vec<Box<dyn Platform>>>;
type Build (line 212) | pub struct Build {
type SetupArgs (line 221) | pub struct SetupArgs {
method get_runner_command (line 231) | pub fn get_runner_command(&self, platform_id: &str) -> String {
type BuildBundle (line 270) | pub struct BuildBundle {
method replace_prefix_with (line 280) | fn replace_prefix_with<P: AsRef<path::Path>>(&self, path: P) -> Result...
type Runnable (line 307) | pub struct Runnable {
FILE: dinghy-lib/src/overlay.rs
type OverlayScope (line 22) | pub enum OverlayScope {
type Overlay (line 28) | pub struct Overlay {
type Overlayer (line 35) | pub struct Overlayer {
method overlay (line 43) | pub fn overlay<P: AsRef<Path>>(
method from_conf (line 95) | fn from_conf(configuration: &PlatformConfiguration) -> Result<Vec<Over...
method from_directory (line 109) | fn from_directory<P: AsRef<Path>>(overlay_root_dir: P) -> Result<Vec<O...
method apply_overlay (line 123) | fn apply_overlay<I>(&self, overlays: I) -> Result<()>
method generate_pkg_config_file (line 194) | fn generate_pkg_config_file(&self, overlay: &Overlay) -> Result<()> {
FILE: dinghy-lib/src/platform/mod.rs
function strip_runnable (line 12) | pub fn strip_runnable(runnable: &Runnable, mut command: Command) -> Resu...
FILE: dinghy-lib/src/platform/regular_platform.rs
type RegularPlatform (line 21) | pub struct RegularPlatform {
method new (line 34) | pub fn new<P: AsRef<Path>>(
method new_with_tc (line 95) | pub fn new_with_tc(
method fmt (line 28) | fn fmt(&self, fmt: &mut Formatter) -> ::std::fmt::Result {
method fmt (line 109) | fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::result::Result<()...
method setup_env (line 115) | fn setup_env(&self, project: &Project, setup_args: &SetupArgs) -> Result...
method id (line 169) | fn id(&self) -> String {
method is_compatible_with (line 173) | fn is_compatible_with(&self, device: &dyn Device) -> bool {
method is_host (line 177) | fn is_host(&self) -> bool {
method rustc_triple (line 181) | fn rustc_triple(&self) -> &str {
method strip (line 185) | fn strip(&self, build: &mut Build) -> Result<()> {
method sysroot (line 194) | fn sysroot(&self) -> Result<Option<std::path::PathBuf>> {
function find_sysroot (line 199) | fn find_sysroot<P: AsRef<Path>>(toolchain_path: P) -> Result<Option<Path...
FILE: dinghy-lib/src/plugin/mod.rs
type PluginManager (line 36) | pub struct PluginManager {
method probe (line 42) | pub fn probe(conf: Arc<Configuration>) -> Option<PluginManager> {
method create_script_devices (line 56) | fn create_script_devices(
method create_ssh_devices (line 75) | fn create_ssh_devices(
method devices (line 96) | fn devices(&self) -> Result<Vec<Box<dyn Device>>> {
method platforms (line 122) | fn platforms(&self) -> anyhow::Result<Vec<Box<dyn Platform>>> {
type DevicePluginOutput (line 153) | pub struct DevicePluginOutput {
function get_devices_from_plugin (line 158) | fn get_devices_from_plugin(plugin: &str) -> Result<DevicePluginOutput> {
function get_platforms_from_plugin (line 172) | fn get_platforms_from_plugin(plugin: &str) -> Result<BTreeMap<String, Bo...
function auto_detect_plugins (line 206) | fn auto_detect_plugins() -> Vec<String> {
FILE: dinghy-lib/src/project.rs
type Project (line 19) | pub struct Project {
method new (line 25) | pub fn new(conf: &Arc<Configuration>, metadata: Metadata) -> Project {
method project_dir (line 32) | pub fn project_dir(&self) -> Result<PathBuf> {
method overlay_work_dir (line 36) | pub fn overlay_work_dir(&self, platform: &dyn Platform) -> Result<Path...
method target_dir (line 42) | pub fn target_dir(&self, triple: &str) -> Result<PathBuf> {
method link_test_data (line 51) | pub fn link_test_data(&self, runnable: &Runnable) -> Result<PathBuf> {
method copy_test_data (line 84) | pub fn copy_test_data<T: AsRef<Path>>(&self, app_path: T) -> Result<()> {
function rec_copy (line 114) | pub fn rec_copy<P1: AsRef<Path>, P2: AsRef<Path>>(
function rec_copy_excl (line 123) | pub fn rec_copy_excl<P1: AsRef<Path>, P2: AsRef<Path>, P3: AsRef<Path> +...
FILE: dinghy-lib/src/script/device.rs
type ScriptDevice (line 8) | pub struct ScriptDevice {
method command (line 14) | fn command(&self, _build: &Build) -> Result<process::Command> {
method clean_app (line 29) | fn clean_app(&self, _build_bundle: &BuildBundle) -> Result<()> {
method debug_app (line 33) | fn debug_app(
method id (line 43) | fn id(&self) -> &str {
method name (line 47) | fn name(&self) -> &str {
method run_app (line 51) | fn run_app(
method is_compatible_with_regular_platform (line 102) | fn is_compatible_with_regular_platform(&self, platform: &RegularPlatform...
method fmt (line 111) | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
FILE: dinghy-lib/src/script/mod.rs
type ScriptDeviceManager (line 8) | pub struct ScriptDeviceManager {
method probe (line 13) | pub fn probe(conf: sync::Arc<Configuration>) -> Option<ScriptDeviceMan...
method devices (line 19) | fn devices(&self) -> Result<Vec<Box<dyn Device>>> {
method platforms (line 32) | fn platforms(&self) -> Result<Vec<Box<dyn Platform>>> {
FILE: dinghy-lib/src/ssh/device.rs
type SshDevice (line 21) | pub struct SshDevice {
method install_app (line 27) | fn install_app(&self, project: &Project, build: &Build) -> Result<(Bui...
method ssh_command (line 55) | fn ssh_command(&self) -> Result<Command> {
method sync_rsync (line 67) | fn sync_rsync(&self) -> Result<String> {
method sync (line 113) | fn sync<FP: AsRef<Path>, TP: AsRef<Path>>(&self, from_path: FP, to_pat...
method to_remote_bundle (line 150) | fn to_remote_bundle(&self, build_bundle: &BuildBundle) -> Result<Build...
method is_compatible_with_regular_platform (line 158) | fn is_compatible_with_regular_platform(&self, platform: &RegularPlatform...
method is_compatible_with_host_platform (line 165) | fn is_compatible_with_host_platform(&self, platform: &HostPlatform) -> b...
method clean_app (line 174) | fn clean_app(&self, build_bundle: &BuildBundle) -> Result<()> {
method debug_app (line 189) | fn debug_app(
method id (line 199) | fn id(&self) -> &str {
method name (line 203) | fn name(&self) -> &str {
method run_app (line 207) | fn run_app(
method fmt (line 265) | fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
method fmt (line 275) | fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
FILE: dinghy-lib/src/ssh/mod.rs
type SshDeviceManager (line 7) | pub struct SshDeviceManager {
method probe (line 12) | pub fn probe(conf: sync::Arc<Configuration>) -> Option<SshDeviceManage...
method devices (line 18) | fn devices(&self) -> Result<Vec<Box<dyn Device>>> {
method platforms (line 31) | fn platforms(&self) -> Result<Vec<Box<dyn Platform>>> {
FILE: dinghy-lib/src/toolchain.rs
type Toolchain (line 22) | pub struct Toolchain {
method setup_tool (line 27) | pub fn setup_tool(&self, var: &str, exe: &str) -> Result<()> {
method setup_cc (line 33) | pub fn setup_cc(&self, _id: &str, compiler_command: &str) -> Result<()> {
method setup_linker (line 39) | pub fn setup_linker<P: AsRef<path::Path>>(
method setup_pkg_config (line 59) | pub fn setup_pkg_config(&self) -> Result<()> {
method setup_runner (line 65) | pub fn setup_runner(&self, platform_id: &str, setup_args: &SetupArgs) ...
method setup_target (line 73) | pub fn setup_target(&self) -> Result<()> {
type ToolchainConfig (line 80) | pub struct ToolchainConfig {
method cc_executable (line 92) | pub fn cc_executable(&self, name_without_triple: &str) -> String {
method binutils_executable (line 99) | pub fn binutils_executable(&self, name_without_triple: &str) -> String {
method naked_executable (line 106) | pub fn naked_executable(&self, name: &str) -> String {
method setup_pkg_config (line 110) | pub fn setup_pkg_config(&self) -> Result<()> {
method setup_sysroot (line 132) | pub fn setup_sysroot(&self) {
method setup_tool (line 138) | pub fn setup_tool(&self, var: &str, command: &str) -> Result<()> {
method setup_cc (line 142) | pub fn setup_cc(&self, id: &str, compiler_command: &str) -> Result<()> {
method generate_linker_command (line 146) | pub fn generate_linker_command(&self, setup_args: &SetupArgs) -> String {
method setup_linker (line 164) | pub fn setup_linker<P: AsRef<path::Path>>(
method setup_runner (line 174) | pub fn setup_runner(&self, platform_id: &str, setup_args: &SetupArgs) ...
method setup_target (line 178) | pub fn setup_target(&self) -> Result<()> {
method shim_executables (line 182) | pub fn shim_executables<P: AsRef<path::Path>>(
method as_toolchain (line 216) | fn as_toolchain(&self) -> Toolchain {
function create_shim (line 223) | fn create_shim<P: AsRef<path::Path>>(
FILE: dinghy-lib/src/utils.rs
function copy_and_sync_file (line 12) | pub fn copy_and_sync_file<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q...
function path_to_str (line 45) | pub fn path_to_str<'a>(path: &'a Path) -> Result<&'a str> {
function normalize_path (line 51) | pub fn normalize_path(path: &Path) -> PathBuf {
function contains_file_with_ext (line 55) | pub fn contains_file_with_ext(dir_path: &Path, ext: &str) -> bool {
function destructure_path (line 71) | pub fn destructure_path<P: AsRef<Path>>(path: P) -> Option<(PathBuf, Str...
function file_has_ext (line 78) | pub fn file_has_ext(file_path: &Path, ext: &str) -> bool {
function is_library (line 87) | pub fn is_library(file_path: &Path) -> bool {
function lib_name_from (line 101) | pub fn lib_name_from(file_path: &Path) -> Result<String> {
function file_name_as_str (line 127) | pub fn file_name_as_str(file_path: &Path) -> Result<&str> {
function set_current_verbosity (line 138) | pub fn set_current_verbosity(verbosity: i8) {
function get_current_verbosity (line 141) | pub fn get_current_verbosity() -> i8 {
function user_facing_log (line 145) | pub fn user_facing_log(category: &str, message: &str, verbosity: i8) {
type LogCommandExt (line 152) | pub trait LogCommandExt {
method log_invocation (line 153) | fn log_invocation(&mut self, verbosity: i8) -> &mut Self;
method log_invocation (line 157) | fn log_invocation(&mut self, verbosity: i8) -> &mut Self {
FILE: dinghy-test/src/lib.rs
function test_project_path (line 6) | pub fn test_project_path() -> PathBuf {
function test_file_path (line 28) | pub fn test_file_path(test_data_id: &str) -> PathBuf {
function try_test_file_path (line 32) | pub fn try_test_file_path(test_data_id: &str) -> Option<PathBuf> {
FILE: legacy/build.rs
function main (line 1) | fn main() {
FILE: legacy/src/main.rs
function main (line 1) | fn main() {}
FILE: test-ws/test-app/src/lib.rs
function example_function (line 31) | pub fn example_function() -> i32 {
function it_finds_source_files (line 44) | fn it_finds_source_files() {
function it_finds_test_data_files (line 51) | fn it_finds_test_data_files() {
function it_works (line 80) | fn it_works() {}
function it_fails (line 85) | fn it_fails() {
function has_feature (line 92) | fn has_feature() {
FILE: test-ws/test-bin/src/main.rs
function main (line 1) | fn main() {
Condensed preview — 77 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (329K chars).
[
{
"path": ".github/workflows/binaries.yml",
"chars": 2270,
"preview": "on:\n release:\n types:\n - created\n\nname: Upload Release Assets\n\njobs:\n assets:\n name: Upload Release Assets\n"
},
{
"path": ".github/workflows/release.yml",
"chars": 590,
"preview": "on:\n push:\n tags:\n - 'cargo-dinghy/*'\n\nname: Create release\n\njobs:\n release:\n name: Create release\n runs-o"
},
{
"path": ".github/workflows/test.yml",
"chars": 1231,
"preview": "name: Build and test\n\non:\n pull_request:\n schedule:\n - cron: '0 5 * * *'\n\njobs:\n linux:\n strategy:\n fail-"
},
{
"path": ".gitignore",
"chars": 27,
"preview": "target\n.idea/\n*.iml\n*.back\n"
},
{
"path": ".travis.apple-third-tier.sh",
"chars": 4013,
"preview": "#!/bin/bash\nset -e\nset -x\n\nSIM_TARGET=$1\n\ntitle() {\n set +x\n echo -e '\\n\\033[1;33m '$@' \\033[0m\\n'\n set -x\n}\nif"
},
{
"path": ".travis.sh",
"chars": 7280,
"preview": "#!/bin/bash\nset -e\nset -x\n\ntitle() {\n set +x\n echo -e '\\n\\033[1;33m '$@' \\033[0m\\n'\n set -x\n}\n\nif [ ! cargo --v"
},
{
"path": ".travis.yml",
"chars": 1202,
"preview": "sudo: false\nlanguage: rust\nmatrix:\n allow_failures:\n - rust: nightly\n include:\n - os: osx\n osx_image: xcode11.2\n "
},
{
"path": "CHANGELOG.md",
"chars": 44648,
"preview": "# Changelog\n\n## [0.8.4](https://github.com/sonos/dinghy/tree/0.8.4) (2025-12-12)\n\n[Full Changelog](https://github.com/so"
},
{
"path": "Cargo.toml",
"chars": 197,
"preview": "[workspace]\nresolver = \"2\"\nmembers = [\n \"cargo-dinghy\",\n \"dinghy-build\",\n \"dinghy-lib\",\n \"dinghy-test\",\n]\n\n["
},
{
"path": "LICENSE",
"chars": 495,
"preview": "## License\n\nLicensed under either of\n * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apac"
},
{
"path": "LICENSE-APACHE",
"chars": 10847,
"preview": " Apache License\n Version 2.0, January 2004\n http"
},
{
"path": "LICENSE-MIT",
"chars": 1023,
"preview": "Permission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentati"
},
{
"path": "README.md",
"chars": 4463,
"preview": "# Dinghy\n\n\n]\n#[comman"
},
{
"path": "cargo-dinghy/src/main.rs",
"chars": 21919,
"preview": "use std::convert::identity;\nuse std::env;\nuse std::env::current_dir;\nuse std::io::BufReader;\nuse std::path::{Path, PathB"
},
{
"path": "dinghy-build/Cargo.toml",
"chars": 638,
"preview": "[package]\nname = \"dinghy-build\"\nversion = \"0.8.5-pre\"\nauthors = [\"Mathieu Poumeyrol <mathieu.poumeyrol@snips.ai>\"]\nlicen"
},
{
"path": "dinghy-build/src/bindgen_macros.rs",
"chars": 4810,
"preview": "/// Create a new `bindgen::Builder` set up and ready for cross-compilation.\n///\n/// This macro should be used for bindge"
},
{
"path": "dinghy-build/src/build.rs",
"chars": 1959,
"preview": "use super::Result;\n///! Helpers functions to output `cargo:` lines suitable for build.rs output.\nuse std::env;\nuse std:"
},
{
"path": "dinghy-build/src/build_env.rs",
"chars": 5026,
"preview": "//! Target-aware environment manipulations.\n//!\n//! cc-rs and pkg-config-rs use a similar convention where some environm"
},
{
"path": "dinghy-build/src/lib.rs",
"chars": 3759,
"preview": "//! Helpers for build.rs scripts.\n//!\n//! This library is meant to be used in build.rs scripts context.\n//!\n//! It conta"
},
{
"path": "dinghy-build/src/utils.rs",
"chars": 691,
"preview": "//! Some helpers around Path and PathBuf manipulations.\n\nuse super::Result;\nuse anyhow::anyhow;\nuse std::path::Path;\nuse"
},
{
"path": "dinghy-lib/Cargo.toml",
"chars": 1147,
"preview": "[package]\nname = \"dinghy-lib\"\nversion = \"0.8.5-pre\"\nauthors = [\"Mathieu Poumeyrol <mathieu.poumeyrol@snips.ai>\"]\nlicense"
},
{
"path": "dinghy-lib/build.rs",
"chars": 113,
"preview": "fn main() {\n println!(\n \"cargo:rustc-env=TARGET={}\",\n std::env::var(\"TARGET\").unwrap()\n );\n}\n"
},
{
"path": "dinghy-lib/src/android/device.rs",
"chars": 9609,
"preview": "use crate::device::make_remote_app;\nuse crate::errors::*;\nuse crate::platform::regular_platform::RegularPlatform;\nuse cr"
},
{
"path": "dinghy-lib/src/android/mod.rs",
"chars": 11113,
"preview": "use crate::config::PlatformConfiguration;\nuse crate::toolchain::ToolchainConfig;\nuse crate::{Device, Platform, PlatformM"
},
{
"path": "dinghy-lib/src/android/platform.rs",
"chars": 3856,
"preview": "use std::fmt::Formatter;\nuse crate::platform::regular_platform::RegularPlatform;\nuse crate::toolchain::ToolchainConfig;\n"
},
{
"path": "dinghy-lib/src/apple/device.rs",
"chars": 22021,
"preview": "use super::{xcode, AppleSimulatorType};\nuse crate::apple::AppleDevicePlatform;\nuse crate::device::make_remote_app_with_n"
},
{
"path": "dinghy-lib/src/apple/helpers.py",
"chars": 1256,
"preview": "import os\nimport lldb\nimport shlex\n\ndef connect_command(debugger, command, result, internal_dict):\n connect_url = com"
},
{
"path": "dinghy-lib/src/apple/mod.rs",
"chars": 11742,
"preview": "use std::collections::HashMap;\nuse std::fmt::Display;\n\npub use self::device::{AppleSimDevice, IosDevice};\npub use self::"
},
{
"path": "dinghy-lib/src/apple/platform.rs",
"chars": 3408,
"preview": "use crate::config::PlatformConfiguration;\nuse crate::errors::*;\nuse crate::overlay::Overlayer;\nuse crate::project::Proje"
},
{
"path": "dinghy-lib/src/apple/xcode.rs",
"chars": 8774,
"preview": "use super::{AppleSimulatorType, SignatureSettings, SigningIdentity};\nuse crate::errors::*;\nuse fs_err as fs;\nuse log::{d"
},
{
"path": "dinghy-lib/src/config.rs",
"chars": 7770,
"preview": "use itertools::Itertools;\nuse serde::de;\nuse serde::{Deserialize, Serialize};\nuse std::fmt;\nuse std::result;\nuse std::{c"
},
{
"path": "dinghy-lib/src/device.rs",
"chars": 4558,
"preview": "use crate::errors::*;\nuse crate::project;\nuse crate::project::{rec_copy, Project};\nuse crate::utils::copy_and_sync_file;"
},
{
"path": "dinghy-lib/src/host/mod.rs",
"chars": 867,
"preview": "use crate::{Configuration, Device, Platform, PlatformConfiguration, PlatformManager, Result};\n\nmod platform;\n\npub use se"
},
{
"path": "dinghy-lib/src/host/platform.rs",
"chars": 2468,
"preview": "use crate::config::PlatformConfiguration;\nuse crate::overlay::Overlayer;\nuse crate::platform;\nuse crate::project::Projec"
},
{
"path": "dinghy-lib/src/lib.rs",
"chars": 9173,
"preview": "#![type_length_limit = \"2149570\"]\npub mod errors {\n pub use anyhow::{anyhow, bail, Context, Error, Result};\n}\n\nmod an"
},
{
"path": "dinghy-lib/src/overlay.rs",
"chars": 8316,
"preview": "use crate::config::PlatformConfiguration;\nuse crate::errors::*;\nuse crate::project::Project;\nuse crate::utils::contains_"
},
{
"path": "dinghy-lib/src/platform/mod.rs",
"chars": 1323,
"preview": "use crate::utils::{file_name_as_str, LogCommandExt};\nuse crate::Result;\nuse crate::Runnable;\nuse std::fs;\nuse std::proce"
},
{
"path": "dinghy-lib/src/platform/regular_platform.rs",
"chars": 7104,
"preview": "use crate::config::PlatformConfiguration;\nuse crate::overlay::Overlayer;\nuse crate::platform;\nuse crate::project::Projec"
},
{
"path": "dinghy-lib/src/plugin/mod.rs",
"chars": 8720,
"preview": "use crate::config::{PlatformConfiguration, ScriptDeviceConfiguration, SshDeviceConfiguration};\nuse crate::platform::regu"
},
{
"path": "dinghy-lib/src/project.rs",
"chars": 6476,
"preview": "use crate::config::Configuration;\nuse crate::utils::copy_and_sync_file;\nuse crate::Platform;\nuse crate::Result;\nuse crat"
},
{
"path": "dinghy-lib/src/script/device.rs",
"chars": 3176,
"preview": "use crate::config::ScriptDeviceConfiguration;\nuse crate::utils::LogCommandExt;\nuse crate::*;\nuse anyhow::bail;\nuse std::"
},
{
"path": "dinghy-lib/src/script/mod.rs",
"chars": 857,
"preview": "use crate::{Configuration, Device, Platform, PlatformManager, Result};\nuse std::sync;\n\nmod device;\n\npub use self::device"
},
{
"path": "dinghy-lib/src/ssh/device.rs",
"chars": 9465,
"preview": "use crate::config::SshDeviceConfiguration;\nuse crate::device::make_remote_app;\nuse crate::errors::*;\nuse crate::host::Ho"
},
{
"path": "dinghy-lib/src/ssh/mod.rs",
"chars": 832,
"preview": "mod device;\nuse crate::{Configuration, Device, Platform, PlatformManager, Result};\nuse std::sync;\n\npub use self::device:"
},
{
"path": "dinghy-lib/src/toolchain.rs",
"chars": 7520,
"preview": "use crate::errors::*;\nuse crate::SetupArgs;\nuse dinghy_build::build_env::append_path_to_env;\nuse dinghy_build::build_env"
},
{
"path": "dinghy-lib/src/utils.rs",
"chars": 5324,
"preview": "use crate::errors::Result;\nuse anyhow::{anyhow, bail};\nuse filetime::set_file_times;\nuse filetime::FileTime;\nuse fs_err "
},
{
"path": "dinghy-test/Cargo.toml",
"chars": 607,
"preview": "[package]\nname = \"dinghy-test\"\nversion = \"0.8.5-pre\"\nauthors = [\"Mathieu Poumeyrol <mathieu.poumeyrol@snips.ai>\"]\nlicens"
},
{
"path": "dinghy-test/src/lib.rs",
"chars": 2278,
"preview": "use std::env;\nuse std::fs::File;\nuse std::io::prelude::*;\nuse std::path::PathBuf;\n\npub fn test_project_path() -> PathBuf"
},
{
"path": "docs/android.md",
"chars": 5307,
"preview": "## Getting started - Android phone\n\n### Dinghy setup\n\nAssuming [rustup](http://rustup.rs) is already installed...\n\n```\nc"
},
{
"path": "docs/dinghy-build.md",
"chars": 811,
"preview": "## Build script helpers [WIP]\n\nDinghy also provides a dinghy-helper crate to help with the writing of build scripts that"
},
{
"path": "docs/files.md",
"chars": 2493,
"preview": "## Sending project files to the devices\n\nSome tests are relying on the presence of files at relative paths to be able\nto"
},
{
"path": "docs/filter.md",
"chars": 913,
"preview": "\n### Package filtering\n\nRust workspace builds all contained packages by default. However, when a worspace project builds"
},
{
"path": "docs/ios.md",
"chars": 5611,
"preview": "## Getting started - iOS phone\n\n### Dinghy setup\n\nAssuming [rustup](http://rustup.rs) is already installed...\n\n```\ncargo"
},
{
"path": "docs/overlay.md",
"chars": 2868,
"preview": "### Overlays\n\nA toolchain might not contains all the required dependencies for your project. To help with situation, Din"
},
{
"path": "docs/ssh.md",
"chars": 2182,
"preview": "## Getting started - Ssh device\n\nSsh setup is useful if you want to target small-ish devices that can host an\noperating "
},
{
"path": "docs/vars.md",
"chars": 772,
"preview": "\n### Environment variables\n\nDinghy allows defining environment variables per-platform. These variables will be set durin"
},
{
"path": "legacy/Cargo.toml",
"chars": 374,
"preview": "[package]\nname = \"dinghy\"\nversion = \"0.3.0\"\nedition = \"2021\"\nauthors = [\"Mathieu Poumeyrol <mathieu.poumeyrol@sonos>\", \""
},
{
"path": "legacy/README.md",
"chars": 457,
"preview": "# Deprecated\n\nDinghy used to be deployed on the `dinghy` name on crates.io, we've since moved to `cargo-dinghy` to have "
},
{
"path": "legacy/build.rs",
"chars": 182,
"preview": "fn main() {\n panic!(\"\n\n\n\nThe use of the dinghy crate is deprecated, use cargo-dinghy instead.\n\nInstall it with the fo"
},
{
"path": "legacy/src/main.rs",
"chars": 13,
"preview": "fn main() {}\n"
},
{
"path": "musl_build.sh",
"chars": 218,
"preview": "#!/bin/sh\n\nset -ex\n\nRUST_TRIPLE=x86_64-unknown-linux-musl\n\nrustup target add $RUST_TRIPLE\n\ncargo build --target $RUST_TR"
},
{
"path": "post-release.sh",
"chars": 570,
"preview": "#!/bin/sh\n\nVERSION=$1\nCRATES=\"dinghy-build dinghy-test dinghy-lib cargo-dinghy\"\n\nif [ -z \"$VERSION\" ]\nthen\n echo \"Usa"
},
{
"path": "pre-release.sh",
"chars": 1325,
"preview": "#!/usr/bin/env bash\n\nset -e\n\nNEW_VERSION=$1\nif [[ -z \"${NEW_VERSION}\" ]]; then\n echo \"usage: $0 <new version>\"\n exit 1"
},
{
"path": "release.sh",
"chars": 1006,
"preview": "#!/usr/bin/env bash\n\nCRATE=$1\nVERSION=$2\nCRATES=\"dinghy-build dinghy-test dinghy-lib cargo-dinghy\"\n\nif [ -z \"$VERSION\" ]"
},
{
"path": "rust-toolchain",
"chars": 7,
"preview": "1.85.0\n"
},
{
"path": "test-ws/.dinghy.toml",
"chars": 80,
"preview": "[test_data]\ndinghy_license = { source = \"../LICENSE\", copy_git_ignored = true }\n"
},
{
"path": "test-ws/Cargo.toml",
"chars": 81,
"preview": "[workspace]\nmembers = [\"test-app\", \"test-proc-macro\", \"test-bin\"]\nresolver = \"2\"\n"
},
{
"path": "test-ws/rust-toolchain",
"chars": 7,
"preview": "1.89.0\n"
},
{
"path": "test-ws/test-app/.dinghy.toml",
"chars": 36,
"preview": "[test_data]\ndinghy_source = \"../..\"\n"
},
{
"path": "test-ws/test-app/Cargo.toml",
"chars": 221,
"preview": "[package]\nname = \"test-app\"\nversion = \"0.1.0\"\nauthors = [\"Mathieu Poumeyrol <kali@zoy.org>\"]\nedition = \"2021\"\nworkspace "
},
{
"path": "test-ws/test-app/src/lib.rs",
"chars": 2358,
"preview": "#[cfg(test)]\nextern crate dinghy_test;\n\n\n/// this is an example doctest with lots of code blocks that will each generate"
},
{
"path": "test-ws/test-bin/Cargo.toml",
"chars": 195,
"preview": "[package]\nname = \"test-bin\"\nversion = \"0.1.0\"\nedition = \"2021\"\nworkspace = \"../\"\n\n# See more keys and their definitions "
},
{
"path": "test-ws/test-bin/src/main.rs",
"chars": 143,
"preview": "fn main() {\n for (name, value) in std::env::vars() {\n println!(\"env {} -> {}\", name, value);\n }\n\n printl"
},
{
"path": "test-ws/test-proc-macro/Cargo.toml",
"chars": 113,
"preview": "[package]\nname = \"test-proc-macro\"\nversion = \"0.1.0\"\nedition = \"2021\"\nworkspace = \"../\"\n\n[lib]\nproc-macro = true\n"
},
{
"path": "test-ws/test-proc-macro/src/lib.rs",
"chars": 0,
"preview": ""
}
]
About this extraction
This page contains the full source code of the sonos/dinghy GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 77 files (305.0 KB), approximately 81.6k tokens, and a symbol index with 338 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.