final 0.7 fixes: xcode 15/16 hybrid, auto openssldir for android, include_*! tracking hotpatch, fix /assets/ brick at high opt levels, windows linker file, etc (#4376)
* wip: openssl autodetection * add voltaile read inside of asset fn * support xcode 15 for --device * vendor openssl on android * Smaller width TUI * switch to anyhow for the entire CLI * add backtrace and print double printing of cargo_metadata errors * add some padding to multi-line anyhow error * check semvers * don't open if not interactive * set the tailwind current_dir so it doesn't twitch * get the oboe demo working * copy the c++ library on android * cleanup env vars * add depinfo watcher * fix stale hotpatches and crate count * add musl support * remove outdated autofmt test * update scanner app * add hotdog app and bundle to ci * upload dx on every branch * collapse mobile into desktop * `dx doctor` command * windows command files * no pipe on doctor * fix ci? * fix links * fix ci * use main instead of nightly * bump node * build cli for host * cargo doc * prep dog_app for bundle * package hotdog * no bundle or nightly cli * comment out nightly cli * add quotes to linker files
This commit is contained in:
1 parent
97947b169b
commit
727752a965
78 files changed
+2270
-817
No files matched your search
+21
-9
@@ -47,19 +47,31 @@ success() {
|
|||||||
command -v unzip >/dev/null ||
|
command -v unzip >/dev/null ||
|
||||||
error 'unzip is required to install dx'
|
error 'unzip is required to install dx'
|
||||||
|
|
||||||
if [[ $# -gt 1 ]]; then
|
if [[ $# -gt 2 ]]; then
|
||||||
error 'Too many arguments, only 1 are allowed. The first can be a specific tag of dx to install. (e.g. "dx-v0.7.1")'
|
error 'Too many arguments, only 2 are allowed. The first can be a specific tag of dx to install. (e.g. "dx-v0.7.1") or `nightly` or `pr <PR_NUMBER>` to install the latest nightly or PR build.'
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$OS" = "Windows_NT" ]; then
|
if [ "$OS" = "Windows_NT" ]; then
|
||||||
target="x86_64-pc-windows-msvc"
|
target="x86_64-pc-windows-msvc"
|
||||||
else
|
else
|
||||||
case $(uname -sm) in
|
case $(uname -sm) in
|
||||||
"Darwin x86_64") target="x86_64-apple-darwin" ;;
|
"Darwin x86_64") target="x86_64-apple-darwin" ;;
|
||||||
"Darwin arm64") target="aarch64-apple-darwin" ;;
|
"Darwin arm64") target="aarch64-apple-darwin" ;;
|
||||||
"Linux aarch64") target="aarch64-unknown-linux-gnu" ;;
|
"Linux aarch64")
|
||||||
*) target="x86_64-unknown-linux-gnu" ;;
|
if [ -f /etc/alpine-release ]; then
|
||||||
esac
|
target="aarch64-unknown-linux-musl"
|
||||||
|
else
|
||||||
|
target="aarch64-unknown-linux-gnu"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
if [ -f /etc/alpine-release ]; then
|
||||||
|
target="x86_64-unknown-linux-musl"
|
||||||
|
else
|
||||||
|
target="x86_64-unknown-linux-gnu"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
||||||
GITHUB=${GITHUB-"https://github.com"}
|
GITHUB=${GITHUB-"https://github.com"}
|
||||||
|
|||||||
+107
-5
@@ -82,7 +82,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
cache-all-crates: "true"
|
cache-all-crates: "true"
|
||||||
- uses: browser-actions/setup-firefox@latest
|
- uses: browser-actions/setup-firefox@latest
|
||||||
- run: cargo test --lib --bins --tests --examples --workspace --exclude dioxus-desktop --exclude dioxus-mobile
|
- run: cargo test --lib --bins --tests --examples --workspace --exclude dioxus-desktop
|
||||||
|
|
||||||
release-test:
|
release-test:
|
||||||
if: github.event.pull_request.draft == false
|
if: github.event.pull_request.draft == false
|
||||||
@@ -103,7 +103,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
cache-all-crates: "true"
|
cache-all-crates: "true"
|
||||||
- uses: browser-actions/setup-firefox@latest
|
- uses: browser-actions/setup-firefox@latest
|
||||||
- run: cargo test --lib --bins --tests --examples --workspace --exclude dioxus-desktop --exclude dioxus-mobile --profile release-unoptimized
|
- run: cargo test --lib --bins --tests --examples --workspace --exclude dioxus-desktop --profile release-unoptimized
|
||||||
|
|
||||||
fmt:
|
fmt:
|
||||||
if: github.event.pull_request.draft == false
|
if: github.event.pull_request.draft == false
|
||||||
@@ -210,7 +210,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 16
|
node-version: 24
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@master
|
uses: dtolnay/rust-toolchain@master
|
||||||
with:
|
with:
|
||||||
@@ -256,6 +256,7 @@ jobs:
|
|||||||
cross: false,
|
cross: false,
|
||||||
command: "test",
|
command: "test",
|
||||||
args: "--all --tests",
|
args: "--all --tests",
|
||||||
|
platform: "desktop",
|
||||||
}
|
}
|
||||||
- {
|
- {
|
||||||
target: x86_64-apple-darwin,
|
target: x86_64-apple-darwin,
|
||||||
@@ -264,6 +265,7 @@ jobs:
|
|||||||
cross: false,
|
cross: false,
|
||||||
command: "test",
|
command: "test",
|
||||||
args: "--all --tests",
|
args: "--all --tests",
|
||||||
|
platform: "desktop",
|
||||||
}
|
}
|
||||||
- {
|
- {
|
||||||
target: aarch64-apple-ios,
|
target: aarch64-apple-ios,
|
||||||
@@ -271,7 +273,8 @@ jobs:
|
|||||||
toolchain: "1.86.0",
|
toolchain: "1.86.0",
|
||||||
cross: false,
|
cross: false,
|
||||||
command: "build",
|
command: "build",
|
||||||
args: "--package dioxus-mobile",
|
args: "--package dioxus-desktop",
|
||||||
|
platform: "ios",
|
||||||
}
|
}
|
||||||
- {
|
- {
|
||||||
target: x86_64-unknown-linux-gnu,
|
target: x86_64-unknown-linux-gnu,
|
||||||
@@ -280,6 +283,7 @@ jobs:
|
|||||||
cross: false,
|
cross: false,
|
||||||
command: "build",
|
command: "build",
|
||||||
args: "--all --tests",
|
args: "--all --tests",
|
||||||
|
platform: "desktop",
|
||||||
}
|
}
|
||||||
- {
|
- {
|
||||||
target: aarch64-unknown-linux-gnu,
|
target: aarch64-unknown-linux-gnu,
|
||||||
@@ -288,6 +292,7 @@ jobs:
|
|||||||
cross: false,
|
cross: false,
|
||||||
command: "build",
|
command: "build",
|
||||||
args: "--all --tests",
|
args: "--all --tests",
|
||||||
|
platform: "desktop",
|
||||||
}
|
}
|
||||||
- {
|
- {
|
||||||
target: aarch64-linux-android,
|
target: aarch64-linux-android,
|
||||||
@@ -295,7 +300,8 @@ jobs:
|
|||||||
toolchain: "1.86.0",
|
toolchain: "1.86.0",
|
||||||
cross: true,
|
cross: true,
|
||||||
command: "build",
|
command: "build",
|
||||||
args: "--package dioxus-mobile",
|
args: "--package dioxus-desktop",
|
||||||
|
platform: "android",
|
||||||
}
|
}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
@@ -386,3 +392,99 @@ jobs:
|
|||||||
# with:
|
# with:
|
||||||
# manifest-path: ./Cargo.toml
|
# manifest-path: ./Cargo.toml
|
||||||
# exclude: "dioxus-cli, dioxus-ext"
|
# exclude: "dioxus-cli, dioxus-ext"
|
||||||
|
|
||||||
|
# Releases for the CLI on a per-branch basis
|
||||||
|
# nightly-cli:
|
||||||
|
# if: github.event.pull_request.draft == false
|
||||||
|
# runs-on: ${{ matrix.platform.os }}
|
||||||
|
# strategy:
|
||||||
|
# matrix:
|
||||||
|
# platform:
|
||||||
|
# - {
|
||||||
|
# target: x86_64-pc-windows-msvc,
|
||||||
|
# os: windows-latest
|
||||||
|
# }
|
||||||
|
# - {
|
||||||
|
# target: aarch64-pc-windows-msvc,
|
||||||
|
# os: windows-latest
|
||||||
|
# }
|
||||||
|
# - {
|
||||||
|
# target: x86_64-apple-darwin,
|
||||||
|
# os: macos-13
|
||||||
|
# }
|
||||||
|
# - {
|
||||||
|
# target: aarch64-apple-darwin,
|
||||||
|
# os: macos-latest
|
||||||
|
# }
|
||||||
|
# - {
|
||||||
|
# target: x86_64-unknown-linux-gnu,
|
||||||
|
# os: ubuntu-24.04
|
||||||
|
# }
|
||||||
|
# - {
|
||||||
|
# target: aarch64-unknown-linux-gnu,
|
||||||
|
# os: ubuntu-24.04-arm
|
||||||
|
# }
|
||||||
|
# - {
|
||||||
|
# target: x86_64-unknown-linux-musl,
|
||||||
|
# os: ubuntu-24.04
|
||||||
|
# }
|
||||||
|
# - {
|
||||||
|
# target: aarch64-unknown-linux-musl,
|
||||||
|
# os: ubuntu-24.04-arm
|
||||||
|
# }
|
||||||
|
# steps:
|
||||||
|
# - name: Checkout
|
||||||
|
# uses: actions/checkout@v4
|
||||||
|
|
||||||
|
# - name: Install openssl on macos
|
||||||
|
# if: matrix.platform.os == 'macos-latest'
|
||||||
|
# run: brew install openssl
|
||||||
|
|
||||||
|
# - name: Install nasm for windows (tls)
|
||||||
|
# if: ${{ matrix.platform.target == 'x86_64-pc-windows-msvc' }}
|
||||||
|
# uses: ilammy/setup-nasm@v1
|
||||||
|
|
||||||
|
# - name: Free Disk Space
|
||||||
|
# if: ${{ matrix.platform.os == 'ubuntu-24.04' || matrix.platform.os == 'ubuntu-24.04-arm' }}
|
||||||
|
# uses: ./.github/actions/free-disk-space
|
||||||
|
|
||||||
|
# - uses: awalsh128/cache-apt-pkgs-action@latest
|
||||||
|
# if: ${{ matrix.platform.os == 'ubuntu-24.04' || matrix.platform.os == 'ubuntu-24.04-arm' }}
|
||||||
|
# with:
|
||||||
|
# packages: libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev libxdo-dev libglib2.0-dev musl-tools
|
||||||
|
# version: 1.0
|
||||||
|
|
||||||
|
# - name: Install stable
|
||||||
|
# uses: dtolnay/rust-toolchain@master
|
||||||
|
# with:
|
||||||
|
# toolchain: "1.86.0"
|
||||||
|
# targets: ${{ matrix.platform.target }}
|
||||||
|
|
||||||
|
# - uses: Swatinem/rust-cache@v2
|
||||||
|
# with:
|
||||||
|
# cache-all-crates: "true"
|
||||||
|
# save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||||
|
|
||||||
|
# - name: Free Disk Space
|
||||||
|
# uses: jlumbroso/free-disk-space@v1.3.1
|
||||||
|
# with: # speed things up a bit
|
||||||
|
# large-packages: false
|
||||||
|
# docker-images: false
|
||||||
|
# swap-storage: false
|
||||||
|
|
||||||
|
# # Todo: we want `cargo install dx` to actually just use a prebuilt binary instead of building it
|
||||||
|
# - name: Build CLI
|
||||||
|
# run: |
|
||||||
|
# cargo build --release --package dioxus-cli --target ${{ matrix.platform.target }}
|
||||||
|
|
||||||
|
# - name: Upload CLI binaries
|
||||||
|
# uses: actions/upload-artifact@v4
|
||||||
|
# if: always()
|
||||||
|
# with:
|
||||||
|
# name: dx-${{ matrix.platform.target }}-${{ github.event.number || github.ref_name }}
|
||||||
|
# path: ./target/${{ matrix.platform.target }}/release/dx
|
||||||
|
# retention-days: ${{ github.ref == 'refs/heads/main' && 90 || 30 }}
|
||||||
|
# overwrite: true
|
||||||
|
|
||||||
|
# - name: Output artifact ID
|
||||||
|
# run: echo 'Artifact ID is ${{ steps.artifact-upload-step.outputs.artifact-id }}'
|
||||||
@@ -61,6 +61,10 @@ jobs:
|
|||||||
os: ubuntu-24.04
|
os: ubuntu-24.04
|
||||||
- target: aarch64-unknown-linux-gnu
|
- target: aarch64-unknown-linux-gnu
|
||||||
os: ubuntu-24.04-arm
|
os: ubuntu-24.04-arm
|
||||||
|
- target: x86_64-unknown-linux-musl
|
||||||
|
os: ubuntu-24.04
|
||||||
|
- target: aarch64-unknown-linux-musl
|
||||||
|
os: ubuntu-24.04-arm
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -80,7 +84,7 @@ jobs:
|
|||||||
- uses: awalsh128/cache-apt-pkgs-action@latest
|
- uses: awalsh128/cache-apt-pkgs-action@latest
|
||||||
if: ${{ matrix.platform.os == 'ubuntu-24.04' || matrix.platform.os == 'ubuntu-24.04-arm' }}
|
if: ${{ matrix.platform.os == 'ubuntu-24.04' || matrix.platform.os == 'ubuntu-24.04-arm' }}
|
||||||
with:
|
with:
|
||||||
packages: libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev libxdo-dev libglib2.0-dev
|
packages: libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev libxdo-dev libglib2.0-dev musl-tools
|
||||||
version: 1.0
|
version: 1.0
|
||||||
|
|
||||||
- name: Install stable
|
- name: Install stable
|
||||||
|
|||||||
Generated
+212
-55
@@ -340,7 +340,7 @@ dependencies = [
|
|||||||
"bitflags 2.9.1",
|
"bitflags 2.9.1",
|
||||||
"cc",
|
"cc",
|
||||||
"cesu8",
|
"cesu8",
|
||||||
"jni",
|
"jni 0.21.1",
|
||||||
"jni-sys",
|
"jni-sys",
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
@@ -623,7 +623,7 @@ dependencies = [
|
|||||||
"flate2",
|
"flate2",
|
||||||
"scroll 0.12.0",
|
"scroll 0.12.0",
|
||||||
"serde",
|
"serde",
|
||||||
"serde-xml-rs",
|
"serde-xml-rs 0.6.0",
|
||||||
"thiserror 1.0.69",
|
"thiserror 1.0.69",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -646,7 +646,7 @@ dependencies = [
|
|||||||
"reqwest 0.11.27",
|
"reqwest 0.11.27",
|
||||||
"scroll 0.12.0",
|
"scroll 0.12.0",
|
||||||
"serde",
|
"serde",
|
||||||
"serde-xml-rs",
|
"serde-xml-rs 0.6.0",
|
||||||
"sha1",
|
"sha1",
|
||||||
"sha2",
|
"sha2",
|
||||||
"signature",
|
"signature",
|
||||||
@@ -2960,6 +2960,46 @@ dependencies = [
|
|||||||
"cipher",
|
"cipher",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bluetooth-scanner"
|
||||||
|
version = "0.1.1"
|
||||||
|
dependencies = [
|
||||||
|
"btleplug",
|
||||||
|
"dioxus",
|
||||||
|
"futures",
|
||||||
|
"futures-channel",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bluez-async"
|
||||||
|
version = "0.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "50ce74d1f22603b879c3a99bd30eddc423460fb73a802655ca615b623ff3a838"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.9.1",
|
||||||
|
"bluez-generated",
|
||||||
|
"dbus",
|
||||||
|
"dbus-tokio",
|
||||||
|
"futures",
|
||||||
|
"itertools 0.14.0",
|
||||||
|
"log",
|
||||||
|
"serde",
|
||||||
|
"serde-xml-rs 0.8.1",
|
||||||
|
"thiserror 2.0.12",
|
||||||
|
"tokio",
|
||||||
|
"uuid",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bluez-generated"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9676783265eadd6f11829982792c6f303f3854d014edfba384685dcf237dd062"
|
||||||
|
dependencies = [
|
||||||
|
"dbus",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "borsh"
|
name = "borsh"
|
||||||
version = "1.5.7"
|
version = "1.5.7"
|
||||||
@@ -3071,6 +3111,34 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "btleplug"
|
||||||
|
version = "0.11.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c9a11621cb2c8c024e444734292482b1ad86fb50ded066cf46252e46643c8748"
|
||||||
|
dependencies = [
|
||||||
|
"async-trait",
|
||||||
|
"bitflags 2.9.1",
|
||||||
|
"bluez-async",
|
||||||
|
"dashmap 6.1.0",
|
||||||
|
"dbus",
|
||||||
|
"futures",
|
||||||
|
"jni 0.19.0",
|
||||||
|
"jni-utils",
|
||||||
|
"log",
|
||||||
|
"objc2 0.5.2",
|
||||||
|
"objc2-core-bluetooth",
|
||||||
|
"objc2-foundation 0.2.2",
|
||||||
|
"once_cell",
|
||||||
|
"static_assertions",
|
||||||
|
"thiserror 2.0.12",
|
||||||
|
"tokio",
|
||||||
|
"tokio-stream",
|
||||||
|
"uuid",
|
||||||
|
"windows 0.61.3",
|
||||||
|
"windows-future",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "buffer-redux"
|
name = "buffer-redux"
|
||||||
version = "1.0.2"
|
version = "1.0.2"
|
||||||
@@ -4134,7 +4202,7 @@ dependencies = [
|
|||||||
"core-foundation-sys",
|
"core-foundation-sys",
|
||||||
"coreaudio-rs",
|
"coreaudio-rs",
|
||||||
"dasp_sample",
|
"dasp_sample",
|
||||||
"jni",
|
"jni 0.21.1",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"libc",
|
"libc",
|
||||||
"mach2",
|
"mach2",
|
||||||
@@ -4723,6 +4791,30 @@ dependencies = [
|
|||||||
"generic-array 0.14.7",
|
"generic-array 0.14.7",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dbus"
|
||||||
|
version = "0.9.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1bb21987b9fb1613058ba3843121dd18b163b254d8a6e797e144cbac14d96d1b"
|
||||||
|
dependencies = [
|
||||||
|
"futures-channel",
|
||||||
|
"futures-util",
|
||||||
|
"libc",
|
||||||
|
"libdbus-sys",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dbus-tokio"
|
||||||
|
version = "0.7.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "007688d459bc677131c063a3a77fb899526e17b7980f390b69644bdbc41fad13"
|
||||||
|
dependencies = [
|
||||||
|
"dbus",
|
||||||
|
"libc",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "debugid"
|
name = "debugid"
|
||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
@@ -4924,7 +5016,6 @@ dependencies = [
|
|||||||
"dioxus-html",
|
"dioxus-html",
|
||||||
"dioxus-liveview",
|
"dioxus-liveview",
|
||||||
"dioxus-logger",
|
"dioxus-logger",
|
||||||
"dioxus-mobile",
|
|
||||||
"dioxus-native",
|
"dioxus-native",
|
||||||
"dioxus-router",
|
"dioxus-router",
|
||||||
"dioxus-server",
|
"dioxus-server",
|
||||||
@@ -4952,7 +5043,7 @@ dependencies = [
|
|||||||
"dioxus-cli-config",
|
"dioxus-cli-config",
|
||||||
"http 1.3.1",
|
"http 1.3.1",
|
||||||
"infer",
|
"infer",
|
||||||
"jni",
|
"jni 0.21.1",
|
||||||
"ndk 0.9.0",
|
"ndk 0.9.0",
|
||||||
"ndk-context",
|
"ndk-context",
|
||||||
"ndk-sys 0.6.0+11769913",
|
"ndk-sys 0.6.0+11769913",
|
||||||
@@ -5257,7 +5348,7 @@ dependencies = [
|
|||||||
"global-hotkey",
|
"global-hotkey",
|
||||||
"http-range",
|
"http-range",
|
||||||
"infer",
|
"infer",
|
||||||
"jni",
|
"jni 0.21.1",
|
||||||
"lazy-js-bundle",
|
"lazy-js-bundle",
|
||||||
"libc",
|
"libc",
|
||||||
"muda",
|
"muda",
|
||||||
@@ -5267,6 +5358,7 @@ dependencies = [
|
|||||||
"objc",
|
"objc",
|
||||||
"objc_id",
|
"objc_id",
|
||||||
"open",
|
"open",
|
||||||
|
"openssl",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"pollster",
|
"pollster",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
@@ -5399,7 +5491,6 @@ dependencies = [
|
|||||||
"dioxus-interpreter-js",
|
"dioxus-interpreter-js",
|
||||||
"dioxus-isrg",
|
"dioxus-isrg",
|
||||||
"dioxus-lib",
|
"dioxus-lib",
|
||||||
"dioxus-mobile",
|
|
||||||
"dioxus-router",
|
"dioxus-router",
|
||||||
"dioxus-server",
|
"dioxus-server",
|
||||||
"dioxus-web",
|
"dioxus-web",
|
||||||
@@ -5607,17 +5698,6 @@ dependencies = [
|
|||||||
"tracing-wasm",
|
"tracing-wasm",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "dioxus-mobile"
|
|
||||||
version = "0.7.0-alpha.2"
|
|
||||||
dependencies = [
|
|
||||||
"dioxus-cli-config",
|
|
||||||
"dioxus-desktop",
|
|
||||||
"dioxus-lib",
|
|
||||||
"jni",
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dioxus-native"
|
name = "dioxus-native"
|
||||||
version = "0.7.0-alpha.2"
|
version = "0.7.0-alpha.2"
|
||||||
@@ -6608,6 +6688,18 @@ version = "0.2.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
|
checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fallible-iterator"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fallible-streaming-iterator"
|
||||||
|
version = "0.1.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "faster-hex"
|
name = "faster-hex"
|
||||||
version = "0.9.0"
|
version = "0.9.0"
|
||||||
@@ -7391,7 +7483,7 @@ version = "0.26.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d"
|
checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"fallible-iterator",
|
"fallible-iterator 0.2.0",
|
||||||
"indexmap 1.9.3",
|
"indexmap 1.9.3",
|
||||||
"stable_deref_trait",
|
"stable_deref_trait",
|
||||||
]
|
]
|
||||||
@@ -8318,6 +8410,15 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashlink"
|
||||||
|
version = "0.9.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af"
|
||||||
|
dependencies = [
|
||||||
|
"hashbrown 0.14.5",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashlink"
|
name = "hashlink"
|
||||||
version = "0.10.0"
|
version = "0.10.0"
|
||||||
@@ -8458,6 +8559,17 @@ dependencies = [
|
|||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hotdog"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"dioxus",
|
||||||
|
"reqwest 0.12.22",
|
||||||
|
"rusqlite",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hstr"
|
name = "hstr"
|
||||||
version = "0.2.17"
|
version = "0.2.17"
|
||||||
@@ -9322,15 +9434,6 @@ version = "1.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "071ed4cc1afd86650602c7b11aa2e1ce30762a1c27193201cb5cee9c6ebb1294"
|
checksum = "071ed4cc1afd86650602c7b11aa2e1ce30762a1c27193201cb5cee9c6ebb1294"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "itertools"
|
|
||||||
version = "0.8.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f56a2d0bc861f9165be4eb3442afd3c236d8a98afd426f65d92324ae1091a484"
|
|
||||||
dependencies = [
|
|
||||||
"either",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itertools"
|
name = "itertools"
|
||||||
version = "0.10.5"
|
version = "0.10.5"
|
||||||
@@ -9437,6 +9540,20 @@ dependencies = [
|
|||||||
"jiff-tzdb",
|
"jiff-tzdb",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jni"
|
||||||
|
version = "0.19.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c6df18c2e3db7e453d3c6ac5b3e9d5182664d28788126d39b91f2d1e22b017ec"
|
||||||
|
dependencies = [
|
||||||
|
"cesu8",
|
||||||
|
"combine",
|
||||||
|
"jni-sys",
|
||||||
|
"log",
|
||||||
|
"thiserror 1.0.69",
|
||||||
|
"walkdir",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jni"
|
name = "jni"
|
||||||
version = "0.21.1"
|
version = "0.21.1"
|
||||||
@@ -9459,6 +9576,21 @@ version = "0.3.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
|
checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jni-utils"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "259e9f2c3ead61de911f147000660511f07ab00adeed1d84f5ac4d0386e7a6c4"
|
||||||
|
dependencies = [
|
||||||
|
"dashmap 5.5.3",
|
||||||
|
"futures",
|
||||||
|
"jni 0.19.0",
|
||||||
|
"log",
|
||||||
|
"once_cell",
|
||||||
|
"static_assertions",
|
||||||
|
"uuid",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jobserver"
|
name = "jobserver"
|
||||||
version = "0.1.33"
|
version = "0.1.33"
|
||||||
@@ -9765,6 +9897,15 @@ version = "0.2.174"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
|
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libdbus-sys"
|
||||||
|
version = "0.2.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "06085512b750d640299b79be4bad3d2fa90a9c00b1fd9e1b46364f66f0485c72"
|
||||||
|
dependencies = [
|
||||||
|
"pkg-config",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libfuzzer-sys"
|
name = "libfuzzer-sys"
|
||||||
version = "0.4.9"
|
version = "0.4.9"
|
||||||
@@ -11108,6 +11249,17 @@ dependencies = [
|
|||||||
"objc2-foundation 0.2.2",
|
"objc2-foundation 0.2.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "objc2-core-bluetooth"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5a644b62ffb826a5277f536cf0f701493de420b13d40e700c452c36567771111"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.9.1",
|
||||||
|
"objc2 0.5.2",
|
||||||
|
"objc2-foundation 0.2.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "objc2-core-data"
|
name = "objc2-core-data"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
@@ -11386,7 +11538,7 @@ version = "0.6.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e8b61bebd49e5d43f5f8cc7ee2891c16e0f41ec7954d36bcb6c14c5e0de867fb"
|
checksum = "e8b61bebd49e5d43f5f8cc7ee2891c16e0f41ec7954d36bcb6c14c5e0de867fb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"jni",
|
"jni 0.21.1",
|
||||||
"ndk 0.8.0",
|
"ndk 0.8.0",
|
||||||
"ndk-context",
|
"ndk-context",
|
||||||
"num-derive",
|
"num-derive",
|
||||||
@@ -11865,7 +12017,7 @@ version = "0.8.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "82040a392923abe6279c00ab4aff62d5250d1c8555dc780e4b02783a7aa74863"
|
checksum = "82040a392923abe6279c00ab4aff62d5250d1c8555dc780e4b02783a7aa74863"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"fallible-iterator",
|
"fallible-iterator 0.2.0",
|
||||||
"scroll 0.11.0",
|
"scroll 0.11.0",
|
||||||
"uuid",
|
"uuid",
|
||||||
]
|
]
|
||||||
@@ -13505,6 +13657,20 @@ dependencies = [
|
|||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rusqlite"
|
||||||
|
version = "0.32.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7753b721174eb8ff87a9a0e799e2d7bc3749323e773db92e0984debb00019d6e"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.9.1",
|
||||||
|
"fallible-iterator 0.3.0",
|
||||||
|
"fallible-streaming-iterator",
|
||||||
|
"hashlink 0.9.1",
|
||||||
|
"libsqlite3-sys",
|
||||||
|
"smallvec",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rust_decimal"
|
name = "rust_decimal"
|
||||||
version = "1.37.2"
|
version = "1.37.2"
|
||||||
@@ -14136,6 +14302,18 @@ dependencies = [
|
|||||||
"xml-rs",
|
"xml-rs",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde-xml-rs"
|
||||||
|
version = "0.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "53630160a98edebde0123eb4dfd0fce6adff091b2305db3154a9e920206eb510"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"serde",
|
||||||
|
"thiserror 1.0.69",
|
||||||
|
"xml-rs",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.219"
|
version = "1.0.219"
|
||||||
@@ -14790,7 +14968,7 @@ dependencies = [
|
|||||||
"futures-io",
|
"futures-io",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"hashbrown 0.15.4",
|
"hashbrown 0.15.4",
|
||||||
"hashlink",
|
"hashlink 0.10.0",
|
||||||
"indexmap 2.10.0",
|
"indexmap 2.10.0",
|
||||||
"ipnet",
|
"ipnet",
|
||||||
"ipnetwork",
|
"ipnetwork",
|
||||||
@@ -16045,7 +16223,7 @@ dependencies = [
|
|||||||
"gdkwayland-sys",
|
"gdkwayland-sys",
|
||||||
"gdkx11-sys",
|
"gdkx11-sys",
|
||||||
"gtk",
|
"gtk",
|
||||||
"jni",
|
"jni 0.21.1",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
@@ -18508,27 +18686,6 @@ version = "1.2.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dd7cf3379ca1aac9eea11fba24fd7e315d621f8dfe35c8d7d2be8b793726e07d"
|
checksum = "dd7cf3379ca1aac9eea11fba24fd7e315d621f8dfe35c8d7d2be8b793726e07d"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wifi-scanner"
|
|
||||||
version = "0.1.1"
|
|
||||||
dependencies = [
|
|
||||||
"dioxus",
|
|
||||||
"futures",
|
|
||||||
"futures-channel",
|
|
||||||
"tokio",
|
|
||||||
"wifiscanner",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wifiscanner"
|
|
||||||
version = "0.5.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d74e667d34abc74f1e5b509ae3d24641f56514ba2c5a64f9d733ae2061aef856"
|
|
||||||
dependencies = [
|
|
||||||
"itertools 0.8.2",
|
|
||||||
"regex",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
version = "0.3.9"
|
version = "0.3.9"
|
||||||
@@ -19257,7 +19414,7 @@ dependencies = [
|
|||||||
"html5ever",
|
"html5ever",
|
||||||
"http 1.3.1",
|
"http 1.3.1",
|
||||||
"javascriptcore-rs",
|
"javascriptcore-rs",
|
||||||
"jni",
|
"jni 0.21.1",
|
||||||
"kuchikiki",
|
"kuchikiki",
|
||||||
"libc",
|
"libc",
|
||||||
"ndk 0.9.0",
|
"ndk 0.9.0",
|
||||||
|
|||||||
+2
-4
@@ -19,7 +19,6 @@ members = [
|
|||||||
"packages/web",
|
"packages/web",
|
||||||
"packages/ssr",
|
"packages/ssr",
|
||||||
"packages/desktop",
|
"packages/desktop",
|
||||||
"packages/mobile",
|
|
||||||
"packages/interpreter",
|
"packages/interpreter",
|
||||||
"packages/liveview",
|
"packages/liveview",
|
||||||
"packages/autofmt",
|
"packages/autofmt",
|
||||||
@@ -48,7 +47,6 @@ members = [
|
|||||||
"packages/interpreter",
|
"packages/interpreter",
|
||||||
"packages/lazy-js-bundle",
|
"packages/lazy-js-bundle",
|
||||||
"packages/liveview",
|
"packages/liveview",
|
||||||
"packages/mobile",
|
|
||||||
"packages/router-macro",
|
"packages/router-macro",
|
||||||
"packages/router",
|
"packages/router",
|
||||||
"packages/rsx-hotreload",
|
"packages/rsx-hotreload",
|
||||||
@@ -102,8 +100,9 @@ members = [
|
|||||||
# Full project examples
|
# Full project examples
|
||||||
"example-projects/fullstack-hackernews",
|
"example-projects/fullstack-hackernews",
|
||||||
"example-projects/ecommerce-site",
|
"example-projects/ecommerce-site",
|
||||||
"example-projects/wifi-scanner",
|
"example-projects/bluetooth-scanner",
|
||||||
"example-projects/file-explorer",
|
"example-projects/file-explorer",
|
||||||
|
"example-projects/hotdog",
|
||||||
|
|
||||||
# Simple examples that require a crate
|
# Simple examples that require a crate
|
||||||
"examples/tailwind",
|
"examples/tailwind",
|
||||||
@@ -155,7 +154,6 @@ dioxus-web = { path = "packages/web", version = "0.7.0-alpha.2", default-feature
|
|||||||
dioxus-isrg = { path = "packages/isrg", version = "0.7.0-alpha.2" }
|
dioxus-isrg = { path = "packages/isrg", version = "0.7.0-alpha.2" }
|
||||||
dioxus-ssr = { path = "packages/ssr", version = "0.7.0-alpha.2", default-features = false }
|
dioxus-ssr = { path = "packages/ssr", version = "0.7.0-alpha.2", default-features = false }
|
||||||
dioxus-desktop = { path = "packages/desktop", version = "0.7.0-alpha.2", default-features = false }
|
dioxus-desktop = { path = "packages/desktop", version = "0.7.0-alpha.2", default-features = false }
|
||||||
dioxus-mobile = { path = "packages/mobile", version = "0.7.0-alpha.2" }
|
|
||||||
dioxus-interpreter-js = { path = "packages/interpreter", version = "0.7.0-alpha.2" }
|
dioxus-interpreter-js = { path = "packages/interpreter", version = "0.7.0-alpha.2" }
|
||||||
dioxus-liveview = { path = "packages/liveview", version = "0.7.0-alpha.2" }
|
dioxus-liveview = { path = "packages/liveview", version = "0.7.0-alpha.2" }
|
||||||
dioxus-autofmt = { path = "packages/autofmt", version = "0.7.0-alpha.2" }
|
dioxus-autofmt = { path = "packages/autofmt", version = "0.7.0-alpha.2" }
|
||||||
|
|||||||
File renamed without changes.
+2
-2
@@ -1,5 +1,5 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "wifi-scanner"
|
name = "bluetooth-scanner"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
publish = false
|
publish = false
|
||||||
@@ -11,4 +11,4 @@ tokio = { workspace = true, features = ["full"] }
|
|||||||
dioxus = { workspace = true, features = ["desktop"] }
|
dioxus = { workspace = true, features = ["desktop"] }
|
||||||
futures-channel = { workspace = true }
|
futures-channel = { workspace = true }
|
||||||
futures = { workspace = true }
|
futures = { workspace = true }
|
||||||
wifiscanner = "0.5.1"
|
btleplug = "0.11.8"
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
# WiFi scanner app
|
# Bluetooth scanner app
|
||||||
|
|
||||||
This desktop app showcases the use of background threads.
|
This desktop app showcases the use of background threads.
|
||||||
|
|
||||||
@@ -0,0 +1,418 @@
|
|||||||
|
/*! tailwindcss v4.1.5 | MIT License | https://tailwindcss.com */
|
||||||
|
@layer properties;
|
||||||
|
@layer theme, base, components, utilities;
|
||||||
|
@layer theme {
|
||||||
|
:root, :host {
|
||||||
|
--font-sans: ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
|
||||||
|
'Noto Color Emoji';
|
||||||
|
--font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New',
|
||||||
|
monospace;
|
||||||
|
--color-green-500: oklch(72.3% 0.219 149.579);
|
||||||
|
--color-indigo-500: oklch(58.5% 0.233 277.117);
|
||||||
|
--color-indigo-600: oklch(51.1% 0.262 276.966);
|
||||||
|
--color-purple-50: oklch(97.7% 0.014 308.299);
|
||||||
|
--color-purple-500: oklch(62.7% 0.265 303.9);
|
||||||
|
--color-gray-50: oklch(98.5% 0.002 247.839);
|
||||||
|
--color-gray-500: oklch(55.1% 0.027 264.364);
|
||||||
|
--color-white: #fff;
|
||||||
|
--spacing: 0.25rem;
|
||||||
|
--text-xs: 0.75rem;
|
||||||
|
--text-xs--line-height: calc(1 / 0.75);
|
||||||
|
--text-2xl: 1.5rem;
|
||||||
|
--text-2xl--line-height: calc(2 / 1.5);
|
||||||
|
--font-weight-medium: 500;
|
||||||
|
--font-weight-bold: 700;
|
||||||
|
--default-transition-duration: 150ms;
|
||||||
|
--default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
--default-font-family: var(--font-sans);
|
||||||
|
--default-mono-font-family: var(--font-mono);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@layer base {
|
||||||
|
*, ::after, ::before, ::backdrop, ::file-selector-button {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
border: 0 solid;
|
||||||
|
}
|
||||||
|
html, :host {
|
||||||
|
line-height: 1.5;
|
||||||
|
-webkit-text-size-adjust: 100%;
|
||||||
|
tab-size: 4;
|
||||||
|
font-family: var(--default-font-family, ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji');
|
||||||
|
font-feature-settings: var(--default-font-feature-settings, normal);
|
||||||
|
font-variation-settings: var(--default-font-variation-settings, normal);
|
||||||
|
-webkit-tap-highlight-color: transparent;
|
||||||
|
}
|
||||||
|
hr {
|
||||||
|
height: 0;
|
||||||
|
color: inherit;
|
||||||
|
border-top-width: 1px;
|
||||||
|
}
|
||||||
|
abbr:where([title]) {
|
||||||
|
-webkit-text-decoration: underline dotted;
|
||||||
|
text-decoration: underline dotted;
|
||||||
|
}
|
||||||
|
h1, h2, h3, h4, h5, h6 {
|
||||||
|
font-size: inherit;
|
||||||
|
font-weight: inherit;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
color: inherit;
|
||||||
|
-webkit-text-decoration: inherit;
|
||||||
|
text-decoration: inherit;
|
||||||
|
}
|
||||||
|
b, strong {
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
code, kbd, samp, pre {
|
||||||
|
font-family: var(--default-mono-font-family, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace);
|
||||||
|
font-feature-settings: var(--default-mono-font-feature-settings, normal);
|
||||||
|
font-variation-settings: var(--default-mono-font-variation-settings, normal);
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
small {
|
||||||
|
font-size: 80%;
|
||||||
|
}
|
||||||
|
sub, sup {
|
||||||
|
font-size: 75%;
|
||||||
|
line-height: 0;
|
||||||
|
position: relative;
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
sub {
|
||||||
|
bottom: -0.25em;
|
||||||
|
}
|
||||||
|
sup {
|
||||||
|
top: -0.5em;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
text-indent: 0;
|
||||||
|
border-color: inherit;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
:-moz-focusring {
|
||||||
|
outline: auto;
|
||||||
|
}
|
||||||
|
progress {
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
summary {
|
||||||
|
display: list-item;
|
||||||
|
}
|
||||||
|
ol, ul, menu {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
img, svg, video, canvas, audio, iframe, embed, object {
|
||||||
|
display: block;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
img, video {
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
button, input, select, optgroup, textarea, ::file-selector-button {
|
||||||
|
font: inherit;
|
||||||
|
font-feature-settings: inherit;
|
||||||
|
font-variation-settings: inherit;
|
||||||
|
letter-spacing: inherit;
|
||||||
|
color: inherit;
|
||||||
|
border-radius: 0;
|
||||||
|
background-color: transparent;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
:where(select:is([multiple], [size])) optgroup {
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
:where(select:is([multiple], [size])) optgroup option {
|
||||||
|
padding-inline-start: 20px;
|
||||||
|
}
|
||||||
|
::file-selector-button {
|
||||||
|
margin-inline-end: 4px;
|
||||||
|
}
|
||||||
|
::placeholder {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
@supports (not (-webkit-appearance: -apple-pay-button)) or (contain-intrinsic-size: 1px) {
|
||||||
|
::placeholder {
|
||||||
|
color: currentcolor;
|
||||||
|
@supports (color: color-mix(in lab, red, red)) {
|
||||||
|
color: color-mix(in oklab, currentcolor 50%, transparent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
textarea {
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
|
::-webkit-search-decoration {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
}
|
||||||
|
::-webkit-date-and-time-value {
|
||||||
|
min-height: 1lh;
|
||||||
|
text-align: inherit;
|
||||||
|
}
|
||||||
|
::-webkit-datetime-edit {
|
||||||
|
display: inline-flex;
|
||||||
|
}
|
||||||
|
::-webkit-datetime-edit-fields-wrapper {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
::-webkit-datetime-edit, ::-webkit-datetime-edit-year-field, ::-webkit-datetime-edit-month-field, ::-webkit-datetime-edit-day-field, ::-webkit-datetime-edit-hour-field, ::-webkit-datetime-edit-minute-field, ::-webkit-datetime-edit-second-field, ::-webkit-datetime-edit-millisecond-field, ::-webkit-datetime-edit-meridiem-field {
|
||||||
|
padding-block: 0;
|
||||||
|
}
|
||||||
|
:-moz-ui-invalid {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
button, input:where([type='button'], [type='reset'], [type='submit']), ::file-selector-button {
|
||||||
|
appearance: button;
|
||||||
|
}
|
||||||
|
::-webkit-inner-spin-button, ::-webkit-outer-spin-button {
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
[hidden]:where(:not([hidden='until-found'])) {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@layer utilities {
|
||||||
|
.container {
|
||||||
|
width: 100%;
|
||||||
|
@media (width >= 40rem) {
|
||||||
|
max-width: 40rem;
|
||||||
|
}
|
||||||
|
@media (width >= 48rem) {
|
||||||
|
max-width: 48rem;
|
||||||
|
}
|
||||||
|
@media (width >= 64rem) {
|
||||||
|
max-width: 64rem;
|
||||||
|
}
|
||||||
|
@media (width >= 80rem) {
|
||||||
|
max-width: 80rem;
|
||||||
|
}
|
||||||
|
@media (width >= 96rem) {
|
||||||
|
max-width: 96rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.mx-auto {
|
||||||
|
margin-inline: auto;
|
||||||
|
}
|
||||||
|
.mb-6 {
|
||||||
|
margin-bottom: calc(var(--spacing) * 6);
|
||||||
|
}
|
||||||
|
.flex {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.inline-block {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
.table {
|
||||||
|
display: table;
|
||||||
|
}
|
||||||
|
.w-full {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.table-auto {
|
||||||
|
table-layout: auto;
|
||||||
|
}
|
||||||
|
.overflow-x-auto {
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
.rounded {
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
}
|
||||||
|
.rounded-full {
|
||||||
|
border-radius: calc(infinity * 1px);
|
||||||
|
}
|
||||||
|
.bg-gray-50 {
|
||||||
|
background-color: var(--color-gray-50);
|
||||||
|
}
|
||||||
|
.bg-green-500 {
|
||||||
|
background-color: var(--color-green-500);
|
||||||
|
}
|
||||||
|
.bg-indigo-500 {
|
||||||
|
background-color: var(--color-indigo-500);
|
||||||
|
}
|
||||||
|
.bg-purple-50 {
|
||||||
|
background-color: var(--color-purple-50);
|
||||||
|
}
|
||||||
|
.bg-white {
|
||||||
|
background-color: var(--color-white);
|
||||||
|
}
|
||||||
|
.p-4 {
|
||||||
|
padding: calc(var(--spacing) * 4);
|
||||||
|
}
|
||||||
|
.px-2 {
|
||||||
|
padding-inline: calc(var(--spacing) * 2);
|
||||||
|
}
|
||||||
|
.px-4 {
|
||||||
|
padding-inline: calc(var(--spacing) * 4);
|
||||||
|
}
|
||||||
|
.px-6 {
|
||||||
|
padding-inline: calc(var(--spacing) * 6);
|
||||||
|
}
|
||||||
|
.py-1 {
|
||||||
|
padding-block: calc(var(--spacing) * 1);
|
||||||
|
}
|
||||||
|
.py-3 {
|
||||||
|
padding-block: calc(var(--spacing) * 3);
|
||||||
|
}
|
||||||
|
.py-5 {
|
||||||
|
padding-block: calc(var(--spacing) * 5);
|
||||||
|
}
|
||||||
|
.py-8 {
|
||||||
|
padding-block: calc(var(--spacing) * 8);
|
||||||
|
}
|
||||||
|
.pb-3 {
|
||||||
|
padding-bottom: calc(var(--spacing) * 3);
|
||||||
|
}
|
||||||
|
.pl-6 {
|
||||||
|
padding-left: calc(var(--spacing) * 6);
|
||||||
|
}
|
||||||
|
.text-left {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
.text-2xl {
|
||||||
|
font-size: var(--text-2xl);
|
||||||
|
line-height: var(--tw-leading, var(--text-2xl--line-height));
|
||||||
|
}
|
||||||
|
.text-xs {
|
||||||
|
font-size: var(--text-xs);
|
||||||
|
line-height: var(--tw-leading, var(--text-xs--line-height));
|
||||||
|
}
|
||||||
|
.font-bold {
|
||||||
|
--tw-font-weight: var(--font-weight-bold);
|
||||||
|
font-weight: var(--font-weight-bold);
|
||||||
|
}
|
||||||
|
.font-medium {
|
||||||
|
--tw-font-weight: var(--font-weight-medium);
|
||||||
|
font-weight: var(--font-weight-medium);
|
||||||
|
}
|
||||||
|
.text-gray-500 {
|
||||||
|
color: var(--color-gray-500);
|
||||||
|
}
|
||||||
|
.text-purple-500 {
|
||||||
|
color: var(--color-purple-500);
|
||||||
|
}
|
||||||
|
.text-white {
|
||||||
|
color: var(--color-white);
|
||||||
|
}
|
||||||
|
.shadow {
|
||||||
|
--tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 1px 2px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
|
||||||
|
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
||||||
|
}
|
||||||
|
.transition {
|
||||||
|
transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, box-shadow, transform, translate, scale, rotate, filter, -webkit-backdrop-filter, backdrop-filter, display, visibility, content-visibility, overlay, pointer-events;
|
||||||
|
transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
|
||||||
|
transition-duration: var(--tw-duration, var(--default-transition-duration));
|
||||||
|
}
|
||||||
|
.duration-200 {
|
||||||
|
--tw-duration: 200ms;
|
||||||
|
transition-duration: 200ms;
|
||||||
|
}
|
||||||
|
.hover\:bg-indigo-600 {
|
||||||
|
&:hover {
|
||||||
|
@media (hover: hover) {
|
||||||
|
background-color: var(--color-indigo-600);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.md\:w-auto {
|
||||||
|
@media (width >= 48rem) {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@property --tw-font-weight {
|
||||||
|
syntax: "*";
|
||||||
|
inherits: false;
|
||||||
|
}
|
||||||
|
@property --tw-shadow {
|
||||||
|
syntax: "*";
|
||||||
|
inherits: false;
|
||||||
|
initial-value: 0 0 #0000;
|
||||||
|
}
|
||||||
|
@property --tw-shadow-color {
|
||||||
|
syntax: "*";
|
||||||
|
inherits: false;
|
||||||
|
}
|
||||||
|
@property --tw-shadow-alpha {
|
||||||
|
syntax: "<percentage>";
|
||||||
|
inherits: false;
|
||||||
|
initial-value: 100%;
|
||||||
|
}
|
||||||
|
@property --tw-inset-shadow {
|
||||||
|
syntax: "*";
|
||||||
|
inherits: false;
|
||||||
|
initial-value: 0 0 #0000;
|
||||||
|
}
|
||||||
|
@property --tw-inset-shadow-color {
|
||||||
|
syntax: "*";
|
||||||
|
inherits: false;
|
||||||
|
}
|
||||||
|
@property --tw-inset-shadow-alpha {
|
||||||
|
syntax: "<percentage>";
|
||||||
|
inherits: false;
|
||||||
|
initial-value: 100%;
|
||||||
|
}
|
||||||
|
@property --tw-ring-color {
|
||||||
|
syntax: "*";
|
||||||
|
inherits: false;
|
||||||
|
}
|
||||||
|
@property --tw-ring-shadow {
|
||||||
|
syntax: "*";
|
||||||
|
inherits: false;
|
||||||
|
initial-value: 0 0 #0000;
|
||||||
|
}
|
||||||
|
@property --tw-inset-ring-color {
|
||||||
|
syntax: "*";
|
||||||
|
inherits: false;
|
||||||
|
}
|
||||||
|
@property --tw-inset-ring-shadow {
|
||||||
|
syntax: "*";
|
||||||
|
inherits: false;
|
||||||
|
initial-value: 0 0 #0000;
|
||||||
|
}
|
||||||
|
@property --tw-ring-inset {
|
||||||
|
syntax: "*";
|
||||||
|
inherits: false;
|
||||||
|
}
|
||||||
|
@property --tw-ring-offset-width {
|
||||||
|
syntax: "<length>";
|
||||||
|
inherits: false;
|
||||||
|
initial-value: 0px;
|
||||||
|
}
|
||||||
|
@property --tw-ring-offset-color {
|
||||||
|
syntax: "*";
|
||||||
|
inherits: false;
|
||||||
|
initial-value: #fff;
|
||||||
|
}
|
||||||
|
@property --tw-ring-offset-shadow {
|
||||||
|
syntax: "*";
|
||||||
|
inherits: false;
|
||||||
|
initial-value: 0 0 #0000;
|
||||||
|
}
|
||||||
|
@property --tw-duration {
|
||||||
|
syntax: "*";
|
||||||
|
inherits: false;
|
||||||
|
}
|
||||||
|
@layer properties {
|
||||||
|
@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {
|
||||||
|
*, ::before, ::after, ::backdrop {
|
||||||
|
--tw-font-weight: initial;
|
||||||
|
--tw-shadow: 0 0 #0000;
|
||||||
|
--tw-shadow-color: initial;
|
||||||
|
--tw-shadow-alpha: 100%;
|
||||||
|
--tw-inset-shadow: 0 0 #0000;
|
||||||
|
--tw-inset-shadow-color: initial;
|
||||||
|
--tw-inset-shadow-alpha: 100%;
|
||||||
|
--tw-ring-color: initial;
|
||||||
|
--tw-ring-shadow: 0 0 #0000;
|
||||||
|
--tw-inset-ring-color: initial;
|
||||||
|
--tw-inset-ring-shadow: 0 0 #0000;
|
||||||
|
--tw-ring-inset: initial;
|
||||||
|
--tw-ring-offset-width: 0px;
|
||||||
|
--tw-ring-offset-color: #fff;
|
||||||
|
--tw-ring-offset-shadow: 0 0 #0000;
|
||||||
|
--tw-duration: initial;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File renamed without changes.
+38
-35
@@ -1,38 +1,45 @@
|
|||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
use wifiscanner::Wifi;
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
dioxus::launch(app)
|
dioxus::launch(app)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn perform_scan() -> Status {
|
|
||||||
if let Ok(devices) = wifiscanner::scan() {
|
|
||||||
if devices.is_empty() {
|
|
||||||
Status::NoneFound
|
|
||||||
} else {
|
|
||||||
Status::Found(devices)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Status::NoneFound
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum Status {
|
|
||||||
NoneFound,
|
|
||||||
Found(Vec<Wifi>),
|
|
||||||
}
|
|
||||||
|
|
||||||
fn app() -> Element {
|
fn app() -> Element {
|
||||||
let mut status =
|
let mut status = use_resource(|| async {
|
||||||
use_resource(|| async { tokio::task::spawn_blocking(perform_scan).await.unwrap() });
|
use btleplug::api::{Central, Manager as _, Peripheral, ScanFilter};
|
||||||
|
|
||||||
|
let manager = btleplug::platform::Manager::new().await.unwrap();
|
||||||
|
|
||||||
|
// get the first bluetooth adapter
|
||||||
|
let adapters = manager.adapters().await.unwrap();
|
||||||
|
let central = adapters.into_iter().next().unwrap();
|
||||||
|
|
||||||
|
// start scanning for devices
|
||||||
|
central.start_scan(ScanFilter::default()).await.unwrap();
|
||||||
|
|
||||||
|
tokio::time::sleep(std::time::Duration::from_secs(2)).await;
|
||||||
|
|
||||||
|
// Return the list of peripherals after scanning
|
||||||
|
let mut devices = vec![];
|
||||||
|
for p in central.peripherals().await.unwrap() {
|
||||||
|
if let Some(p) = p.properties().await.unwrap() {
|
||||||
|
devices.push(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Found {} Bluetooth devices", devices.len());
|
||||||
|
|
||||||
|
devices
|
||||||
|
});
|
||||||
|
|
||||||
let scanning = !status.finished();
|
let scanning = !status.finished();
|
||||||
|
|
||||||
rsx! {
|
rsx! {
|
||||||
link { rel: "stylesheet", href: "https://unpkg.com/tailwindcss@^2.0/dist/tailwind.min.css" },
|
link { rel: "stylesheet", href: asset!("/assets/tailwind.css") },
|
||||||
div {
|
div {
|
||||||
div { class: "py-8 px-6",
|
div { class: "py-8 px-6",
|
||||||
div { class: "container px-4 mx-auto",
|
div { class: "container px-4 mx-auto",
|
||||||
h2 { class: "text-2xl font-bold", "Scan for WiFi Networks" }
|
h2 { class: "text-2xl font-bold", "Scan for Bluetooth Devices" }
|
||||||
button {
|
button {
|
||||||
class: "inline-block w-full md:w-auto px-6 py-3 font-medium text-white bg-indigo-500 hover:bg-indigo-600 rounded transition duration-200",
|
class: "inline-block w-full md:w-auto px-6 py-3 font-medium text-white bg-indigo-500 hover:bg-indigo-600 rounded transition duration-200",
|
||||||
disabled: scanning,
|
disabled: scanning,
|
||||||
@@ -58,24 +65,20 @@ fn app() -> Element {
|
|||||||
}
|
}
|
||||||
|
|
||||||
match &*status.read() {
|
match &*status.read() {
|
||||||
None => rsx!(""),
|
None => rsx!("no devices yet!"),
|
||||||
Some(Status::NoneFound) => rsx!("No networks found. Try scanning again"),
|
Some(peripherals) => {
|
||||||
Some(Status::Found(wifis)) => {
|
|
||||||
// Create vector of tuples of (signal_level, wifi) for sorting by signal_level
|
// Create vector of tuples of (signal_level, wifi) for sorting by signal_level
|
||||||
let mut sorted_wifis = wifis
|
let mut sorted_devices = peripherals.clone();
|
||||||
.iter()
|
sorted_devices.sort_by(|a, b| a.rssi.partial_cmp(&b.rssi).unwrap());
|
||||||
.map(|wif: &Wifi| (wif, wif.signal_level.parse::<f32>().unwrap()))
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
sorted_wifis.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap());
|
|
||||||
|
|
||||||
rsx! {
|
rsx! {
|
||||||
tbody {
|
tbody {
|
||||||
for (Wifi { mac: _, ssid, channel, signal_level, security }, _) in sorted_wifis.into_iter().rev() {
|
for peripheral in sorted_devices.into_iter().rev() {
|
||||||
tr { class: "text-xs bg-gray-50",
|
tr { class: "text-xs bg-gray-50",
|
||||||
td { class: "py-5 px-6 font-medium", "{signal_level}" }
|
td { class: "py-5 px-6 font-medium", "{peripheral.rssi.unwrap_or(-100)}" }
|
||||||
td { class: "flex py-3 font-medium", "{ssid}" }
|
td { class: "flex py-3 font-medium", "{peripheral.local_name.clone().unwrap_or_default()}" }
|
||||||
td { span { class: "inline-block py-1 px-2 text-white bg-green-500 rounded-full", "{channel}" } }
|
td { span { class: "inline-block py-1 px-2 text-white bg-green-500 rounded-full", "{peripheral.address}" } }
|
||||||
td { span { class: "inline-block py-1 px-2 text-purple-500 bg-purple-50 rounded-full", "{security}" } }
|
td { span { class: "inline-block py-1 px-2 text-purple-500 bg-purple-50 rounded-full", "{peripheral.tx_power_level.unwrap_or_default()}" } }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
@import "tailwindcss";
|
||||||
|
@source "./src/**/*.{rs,html,css}";
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
[package]
|
||||||
|
name = "hotdog"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Dioxus Labs"]
|
||||||
|
edition = "2021"
|
||||||
|
publish = false
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
dioxus = { workspace = true, features = ["fullstack", "router"] }
|
||||||
|
reqwest = { workspace = true, features = ["json"] }
|
||||||
|
serde = { workspace = true, features = ["derive"] }
|
||||||
|
serde_json = { workspace = true }
|
||||||
|
rusqlite = { version = "0.32.0", optional = true }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["web"]
|
||||||
|
web = ["dioxus/web"]
|
||||||
|
desktop = ["dioxus/desktop"]
|
||||||
|
mobile = ["dioxus/mobile"]
|
||||||
|
server = ["dioxus/server", "dep:rusqlite"]
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
[application]
|
||||||
|
|
||||||
|
# App (Project) Name
|
||||||
|
name = "hot_dog"
|
||||||
|
|
||||||
|
[bundle]
|
||||||
|
identifier = "com.dioxuslabs"
|
||||||
|
publisher = "Dioxus Labs"
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
FROM rust:1 AS chef
|
||||||
|
RUN cargo install cargo-chef
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
FROM chef AS planner
|
||||||
|
COPY . .
|
||||||
|
RUN cargo chef prepare --recipe-path recipe.json
|
||||||
|
|
||||||
|
FROM chef AS builder
|
||||||
|
COPY --from=planner /app/recipe.json recipe.json
|
||||||
|
RUN cargo chef cook --release --recipe-path recipe.json
|
||||||
|
COPY . .
|
||||||
|
RUN curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/DioxusLabs/dioxus/refs/heads/main/.github/install.sh | bash
|
||||||
|
RUN /.cargo/bin/dx bundle --platform web
|
||||||
|
|
||||||
|
FROM chef AS runtime
|
||||||
|
COPY --from=builder /app/target/dx/hotdog/release/web/ /usr/local/app
|
||||||
|
|
||||||
|
ENV PORT=8080
|
||||||
|
ENV IP=0.0.0.0
|
||||||
|
EXPOSE 8080
|
||||||
|
|
||||||
|
WORKDIR /usr/local/app
|
||||||
|
ENTRYPOINT [ "/usr/local/app/server" ]
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
# Hot diggity dog!
|
||||||
|
|
||||||
|
A Dioxus demo app for the new tutorial!
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## To run
|
||||||
|
|
||||||
|
Make sure you cd to this directory (dioxus/hotdog) and then `serve` any platform:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
dx serve --platform web
|
||||||
|
dx serve --platform desktop
|
||||||
|
dx serve --platform ios
|
||||||
|
dx serve --platform android
|
||||||
|
```
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 130 KiB |
@@ -0,0 +1,20 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1007 197"><style>
|
||||||
|
@keyframes a0_t { 0% { transform: translate(225px,339.9px) scale(0,1) translate(-89.5px,-24px); } 35% { transform: translate(225px,339.9px) scale(0,1) translate(-89.5px,-24px); animation-timing-function: cubic-bezier(.6,0,.4,1); } 45% { transform: translate(225px,339.9px) scale(1,1) translate(-89.5px,-24px); } 50% { transform: translate(225px,339.9px) scale(1,1) translate(-89.5px,-24px); animation-timing-function: cubic-bezier(.6,0,.4,1); } 60% { transform: translate(225px,339.9px) scale(0,1) translate(-89.5px,-24px); } 100% { transform: translate(225px,339.9px) scale(0,1) translate(-89.5px,-24px); } }
|
||||||
|
@keyframes a1_t { 0% { transform: translate(225px,348.9px) scale(0,1) translate(-41.5px,-11px); } 20% { transform: translate(225px,348.9px) scale(0,1) translate(-41.5px,-11px); animation-timing-function: cubic-bezier(.6,0,.4,1); } 30% { transform: translate(225px,348.9px) scale(1,1) translate(-41.5px,-11px); } 35% { transform: translate(225px,348.9px) scale(1,1) translate(-41.5px,-11px); } 45% { transform: translate(225px,348.9px) scale(0,1) translate(-41.5px,-11px); } 100% { transform: translate(225px,348.9px) scale(0,1) translate(-41.5px,-11px); } }
|
||||||
|
@keyframes a3_t { 0% { transform: translate(225px,198.9px) rotate(-90deg); } 10% { transform: translate(225px,198.9px) rotate(-90deg); } 20% { transform: translate(225px,198.9px) rotate(-90deg); animation-timing-function: cubic-bezier(.6,0,.4,1); } 30% { transform: translate(225px,198.9px) rotate(0deg); } 35% { transform: translate(225px,198.9px) rotate(0deg); } 45% { transform: translate(225px,198.9px) rotate(0deg); } 50% { transform: translate(225px,198.9px) rotate(0deg); animation-timing-function: cubic-bezier(.6,0,.4,1); } 60% { transform: translate(225px,198.9px) rotate(-90deg); } 65% { transform: translate(225px,198.9px) rotate(-90deg); } 75% { transform: translate(225px,198.9px) rotate(-90deg); } 100% { transform: translate(225px,198.9px) rotate(-90deg); } }
|
||||||
|
@keyframes a2_t { 0% { transform: translate(-200px,-167px); animation-timing-function: cubic-bezier(.6,0,.4,1); } 10% { transform: translate(-150px,-80px); } 20% { transform: translate(-150px,-80px); animation-timing-function: cubic-bezier(.6,0,.4,1); } 30% { transform: translate(-200px,-123px); } 35% { transform: translate(-200px,-123px); animation-timing-function: cubic-bezier(.6,0,.4,1); } 45% { transform: translate(-150px,-100px); } 50% { transform: translate(-150px,-100px); animation-timing-function: cubic-bezier(.6,0,.4,1); } 60% { transform: translate(-150px,-80px); } 65% { transform: translate(-150px,-80px); animation-timing-function: cubic-bezier(.6,0,.4,1); } 75% { transform: translate(-217px,-200px); } 100% { transform: translate(-217px,-200px); } }
|
||||||
|
@keyframes a2_o { 0% { opacity: 0; animation-timing-function: cubic-bezier(.6,0,.4,1); } 10% { opacity: 1; } 65% { opacity: 1; animation-timing-function: cubic-bezier(.6,0,.4,1); } 75% { opacity: 0; } 100% { opacity: 0; } }
|
||||||
|
@keyframes a2_sw { 0% { stroke-width: 30px; animation-timing-function: cubic-bezier(.6,0,.4,1); } 10% { stroke-width: 8px; } 65% { stroke-width: 8px; animation-timing-function: cubic-bezier(.6,0,.4,1); } 75% { stroke-width: 30px; } 100% { stroke-width: 30px; } }
|
||||||
|
@keyframes a2_w { 0% { width: 400px; animation-timing-function: cubic-bezier(.6,0,.4,1); } 10% { width: 300px; } 20% { width: 300px; animation-timing-function: cubic-bezier(.6,0,.4,1); } 30% { width: 400px; } 35% { width: 400px; animation-timing-function: cubic-bezier(.6,0,.4,1); } 45% { width: 300px; } 50% { width: 300px; } 60% { width: 300px; } 65% { width: 300px; animation-timing-function: cubic-bezier(.6,0,.4,1); } 75% { width: 434px; } 100% { width: 434px; } }
|
||||||
|
@keyframes a2_h { 0% { height: 334px; animation-timing-function: cubic-bezier(.6,0,.4,1); } 10% { height: 160px; } 20% { height: 160px; animation-timing-function: cubic-bezier(.6,0,.4,1); } 30% { height: 246px; } 35% { height: 246px; animation-timing-function: cubic-bezier(.6,0,.4,1); } 45% { height: 200px; } 50% { height: 200px; animation-timing-function: cubic-bezier(.6,0,.4,1); } 60% { height: 160px; } 65% { height: 160px; animation-timing-function: cubic-bezier(.6,0,.4,1); } 75% { height: 400px; } 100% { height: 400px; } }
|
||||||
|
@keyframes a4_t { 0% { transform: translate(143.5px,48.9px); } 10% { transform: translate(180.5px,98.9px); } 100% { transform: translate(180.5px,98.9px); } }
|
||||||
|
@keyframes a4_w { 0% { width: 168.5px; } 10% { width: 94.5px; } 100% { width: 94.5px; } }
|
||||||
|
@keyframes a4_h { 0% { height: 302px; } 10% { height: 202px; } 100% { height: 202px; } }
|
||||||
|
@keyframes a5_t { 0% { transform: translate(217px,324.9px); animation-timing-function: cubic-bezier(.6,0,.4,1); } 10% { transform: translate(190px,324.9px); } 20% { transform: translate(190px,324.9px); } 30% { transform: translate(190px,324.9px); } 100% { transform: translate(190px,324.9px); } }
|
||||||
|
@keyframes a5_o { 0% { opacity: 0; animation-timing-function: cubic-bezier(.6,0,.4,1); } 10% { opacity: 1; } 20% { opacity: 1; } 30% { opacity: 0; } 100% { opacity: 0; } }
|
||||||
|
@keyframes a5_w { 0% { width: 16px; animation-timing-function: cubic-bezier(.6,0,.4,1); } 10% { width: 70px; } 20% { width: 70px; } 30% { width: 70px; } 100% { width: 70px; } }
|
||||||
|
@keyframes a6_t { 0% { transform: translate(217px,324.9px); } 50% { transform: translate(217px,324.9px); animation-timing-function: cubic-bezier(.6,0,.4,1); } 60% { transform: translate(190px,324.9px); } 65% { transform: translate(190px,324.9px); } 75% { transform: translate(190px,324.9px); } 100% { transform: translate(190px,324.9px); } }
|
||||||
|
@keyframes a6_o { 0% { opacity: 0; } 50% { opacity: 0; animation-timing-function: cubic-bezier(.6,0,.4,1); } 60% { opacity: 1; } 65% { opacity: 1; animation-timing-function: cubic-bezier(.6,0,.4,1); } 75% { opacity: 0; } 100% { opacity: 0; } }
|
||||||
|
@keyframes a6_w { 0% { width: 16px; } 50% { width: 16px; animation-timing-function: cubic-bezier(.6,0,.4,1); } 60% { width: 70px; } 65% { width: 70px; } 75% { width: 70px; } 100% { width: 70px; } }
|
||||||
|
@keyframes a7_t { 0% { transform: translate(225px,198.9px) scale(1.5,1.5) translate(-42.3px,-93px); animation-timing-function: cubic-bezier(.6,0,.4,1); } 10% { transform: translate(225px,198.9px) scale(1,1) translate(-42.3px,-93px); } 20% { transform: translate(225px,198.9px) scale(1,1) translate(-42.3px,-93px); animation-timing-function: cubic-bezier(.6,0,.4,1); } 25% { transform: translate(225px,198.9px) scale(.8,.8) translate(-42.3px,-93px); animation-timing-function: cubic-bezier(.6,0,.4,1); } 30% { transform: translate(225px,198.9px) scale(1,1) translate(-42.3px,-93px); } 35% { transform: translate(225px,198.9px) scale(1,1) translate(-42.3px,-93px); animation-timing-function: cubic-bezier(.4,0,.6,1); } 45% { transform: translate(225px,198.9px) scale(.8,.8) translate(-42.3px,-93px); } 50% { transform: translate(225px,198.9px) scale(.8,.8) translate(-42.3px,-93px); animation-timing-function: cubic-bezier(.4,0,.6,1); } 55% { transform: translate(225px,198.9px) scale(.7,.7) translate(-42.3px,-93px); animation-timing-function: cubic-bezier(.4,0,.6,1); } 60% { transform: translate(225px,198.9px) scale(1,1) translate(-42.3px,-93px); } 65% { transform: translate(225px,198.9px) scale(1,1) translate(-42.3px,-93px); animation-timing-function: cubic-bezier(.4,0,.6,1); } 75% { transform: translate(225px,198.9px) scale(1.5,1.5) translate(-42.3px,-93px); } 100% { transform: translate(225px,198.9px) scale(1.5,1.5) translate(-42.3px,-93px); } }
|
||||||
|
</style><defs><symbol id="Symbol-2" preserveAspectRatio="none" width="111.5" height="186" viewBox="0 0 111.5 186" overflow="visible"><g transform="translate(8.5,4.5)"><rect x="4" width="85" height="8" fill="#d9d9d9"/><rect x="4" y="26" width="85" height="8" fill="#d9d9d9"/><rect x="25" y="56" width="43" height="8" fill="#d9d9d9"/><rect width="85" height="8" fill="#d9d9d9" transform="translate(46.5,175) rotate(180) scale(-1,1) translate(-42.5,-4)"/><rect width="85" height="8" fill="#d9d9d9" transform="translate(46.5,149) rotate(180) scale(-1,1) translate(-42.5,-4)"/><rect width="43" height="8" fill="#d9d9d9" transform="translate(46.5,119) rotate(180) scale(-1,1) translate(-21.5,-4)"/><path d="M0 4c0 53 94.5 115 94.5 169" stroke="#3cc4dc" stroke-width="17" stroke-linecap="square" fill="none"/><path d="M94.5 4c0 53-94.5 115-94.5 169" stroke="#fb422d" stroke-width="17" stroke-linecap="square" fill="none"/></g></symbol><symbol id="Symbol-5" preserveAspectRatio="none" width="84.5" height="186" viewBox="0 0 84.5 186" overflow="visible"><g transform="translate(42.3,93) scale(5.352,6.9) translate(-16,60)"><g transform="translate(-34.9,-125.8)"><g transform="translate(24,5)"><path d="M33 46.5c0 3.1-0.8 5.5-2.2 7.4c-1.4 1.9-3.3 3.6-5.4 5.4c-2.2 1.7-4.5 3.6-6.3 6.2c-1.8 2.5-3.1 5.5-3.1 9.5h4.7c0-3.1 .9-5.4 2.2-7.3c1.4-1.9 3.3-3.5 5.5-5.3c2.1-1.8 4.4-3.7 6.2-6.2c1.8-2.6 3.1-5.6 3.1-9.7Z" fill="#e96020"/><path d="M20.4 70.7c-0.6 0-1 .5-1 1.1c0 .7 .4 1.2 1 1.2h12.9c.7 0 1.2-0.5 1.2-1.2c0-0.6-0.5-1.1-1.2-1.1Z" fill="#2d323b"/><path d="M21.8 66.6c-0.7 0-1 .6-1 1.2c0 .6 .3 1 .9 1h10.3c.6 0 1.1-0.5 1.1-1.1c0-0.6-0.5-1.1-1.1-1.1Z" fill="#2d323b"/><path d="M21.8 53c-0.7 0-1.2 .5-1.1 1.1c0 .6 .4 1.1 1.1 1.1h10.2c.6 0 .9-0.6 .9-1.2c0-0.6-0.3-1-0.9-1Z" fill="#2d323b"/><path d="M20.4 48.8c-0.6 0-1.2 .5-1.2 1.1c0 .7 .6 1.2 1.2 1.2h12.9c.7 0 1-0.5 1-1.1c0-0.7-0.3-1.2-1-1.2Z" fill="#2d323b"/><path d="M16 46.5c0 4.1 1.3 7.1 3.1 9.7c1.8 2.5 4.1 4.4 6.3 6.2c2.1 1.8 4 3.6 5.4 5.5c1.4 1.9 2.2 4 2.2 7.1h4.7c0-4-1.3-7.1-3.1-9.6c-1.8-2.5-4.1-4.4-6.2-6.2c-2.2-1.8-4.1-3.4-5.5-5.3c-1.3-1.9-2.2-4.3-2.2-7.4Z" fill="#00a8d6"/></g></g></g></symbol></defs><rect width="1007" height="197" stroke="#0f1116" fill="#0f1116" stroke-width="0"/><path fill="#fff" d="M83.3 106.1v-65.7h17c4.2 0 7.9 .8 11 2.4c3.1 1.6 5.5 3.9 7.2 6.9c1.7 2.9 2.6 6.4 2.6 10.4v26.2c0 4-0.9 7.5-2.6 10.5c-1.7 3-4.1 5.3-7.2 6.9c-3.1 1.6-6.8 2.4-11 2.4Zm8.1-7.2h8.9c4 0 7.1-1.1 9.3-3.3c2.3-2.3 3.4-5.3 3.4-9.3v-26.2c0-3.9-1.1-7-3.4-9.2c-2.2-2.2-5.3-3.3-9.3-3.3h-8.9Zm45.3 7.2v-7.4h17.5v-34.7h-15.7v-7.4h23.8v42.1h16.7v7.4Zm20.7-58.4c-2 0-3.6-0.5-4.7-1.5c-1.2-1.1-1.7-2.4-1.7-4.2c0-1.8 .5-3.2 1.7-4.3c1.1-1 2.7-1.5 4.7-1.5c1.9 0 3.5 .5 4.6 1.5c1.2 1.1 1.7 2.5 1.7 4.3c0 1.8-0.5 3.1-1.7 4.2c-1.1 1-2.7 1.5-4.6 1.5Zm52.6 59.1c-6 0-10.7-1.7-14.2-5.1c-3.4-3.4-5.1-8-5.1-13.8v-13.1c0-5.9 1.7-10.5 5.1-13.9c3.5-3.3 8.2-5 14.2-5c6 0 10.7 1.7 14.2 5c3.4 3.4 5.2 8 5.2 13.8v13.2c0 5.8-1.8 10.4-5.2 13.8c-3.5 3.4-8.2 5.1-14.2 5.1Zm0-7.2c3.5 0 6.3-1 8.3-3c2-1.9 3-4.8 3-8.7v-13.1c0-3.9-1-6.8-3-8.8c-2-1.9-4.8-2.9-8.3-2.9c-3.5 0-6.2 1-8.2 2.9c-2 2-3 4.9-3 8.8v13.1c0 3.9 1 6.8 3 8.7c2 2 4.7 3 8.2 3Zm31.5 6.5l17.6-25.5l-16.5-24h9.5l9.8 15.3c.5 .7 .9 1.4 1.2 2.2c.4 .7 .8 1.4 1 1.8c.2-0.4 .5-1.1 .8-1.8c.4-0.8 .8-1.5 1.3-2.2l9.9-15.3h9.4l-16.5 24.1l17.5 25.4h-9.5l-10.7-16.2c-0.4-0.7-0.8-1.4-1.2-2.3c-0.4-0.9-0.7-1.6-1-2.1c-0.2 .5-0.6 1.2-1 2.1c-0.5 .9-1 1.6-1.4 2.3l-10.7 16.2Zm76.4 .9c-5.7 0-10.2-1.7-13.7-5.1c-3.4-3.4-5.1-8-5.1-13.8v-31.5h8.1v31.5c0 3.7 1 6.6 2.9 8.7c1.9 2.1 4.5 3.1 7.8 3.1c3.4 0 6-1 8-3.1c1.9-2.1 2.9-5 2.9-8.7v-31.5h8.1v31.5c0 5.8-1.7 10.4-5.2 13.8c-3.5 3.4-8.1 5.1-13.8 5.1Zm52.6-0.2c-5.1 0-9.3-1.2-12.5-3.5c-3.3-2.3-4.9-5.5-4.9-9.6h8.3c0 1.8 .9 3.3 2.6 4.3c1.8 1.1 4 1.6 6.6 1.6h3.8c3.2 0 5.6-0.6 7.1-1.9c1.6-1.3 2.4-3 2.4-5.2c0-2.1-0.8-3.8-2.2-5c-1.5-1.2-3.6-2-6.4-2.5l-6.6-1c-5-0.9-8.6-2.4-10.9-4.7c-2.4-2.3-3.5-5.5-3.5-9.5c0-4.5 1.4-7.9 4.3-10.3c2.9-2.4 7.1-3.6 12.6-3.6h3.4c5 0 9.1 1.1 12.1 3.4c3 2.2 4.6 5.2 4.6 8.9h-8.3c0-1.6-0.8-2.8-2.3-3.7c-1.5-1-3.6-1.4-6.3-1.4h-3.4c-2.8 0-5 .6-6.5 1.7c-1.5 1.2-2.3 2.9-2.3 5c0 3.6 2.5 5.8 7.6 6.7l6.7 1.1c5.3 .8 9.2 2.4 11.7 4.6c2.4 2.3 3.6 5.6 3.6 9.9c0 4.6-1.5 8.2-4.4 10.8c-2.9 2.6-7.3 3.9-13.2 3.9Z"/><path fill="#fff" d="M91 164c-2 0-3.6-0.6-4.7-1.7c-1.1-1.1-1.6-2.7-1.6-4.6v-15.9h2.7v15.9c0 1.2 .3 2.2 .9 2.8c.6 .7 1.5 1.1 2.7 1.1c1.2 0 2.1-0.4 2.7-1.1c.6-0.7 .9-1.6 .9-2.8v-15.9h2.7v15.9c0 2-0.5 3.5-1.6 4.6c-1.1 1.1-2.6 1.7-4.7 1.7Zm17.5-0.1c-1.7 0-3.1-0.3-4.2-1.1c-1.1-0.8-1.6-1.9-1.6-3.2h2.8c0 .6 .3 1.1 .8 1.4c.6 .4 1.4 .5 2.2 .5h1.3c1.1 0 1.9-0.2 2.4-0.6c.5-0.4 .8-1 .8-1.7c0-0.7-0.3-1.3-0.8-1.7c-0.5-0.4-1.2-0.7-2.1-0.8l-2.2-0.4c-1.7-0.3-2.9-0.8-3.6-1.5c-0.8-0.8-1.2-1.9-1.2-3.2c0-1.5 .5-2.6 1.4-3.4c1-0.8 2.4-1.2 4.2-1.2h1.2c1.6 0 3 .3 4 1.1c1 .7 1.5 1.7 1.5 3h-2.7c0-0.6-0.3-1-0.8-1.3c-0.5-0.3-1.2-0.4-2.1-0.4h-1.1c-1 0-1.7 .2-2.2 .5c-0.5 .4-0.8 1-0.8 1.7c0 1.2 .9 1.9 2.6 2.2l2.2 .4c1.8 .3 3.1 .8 3.9 1.5c.8 .8 1.2 1.9 1.2 3.3c0 1.6-0.5 2.8-1.5 3.6c-0.9 .9-2.4 1.3-Line truncated
|
||||||
|
After Width: | Height: | Size: 23 KiB |
@@ -0,0 +1,149 @@
|
|||||||
|
/* App-wide styling */
|
||||||
|
html, body {
|
||||||
|
background-color: #0e0e0e;
|
||||||
|
color: white;
|
||||||
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#main {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
#dogview {
|
||||||
|
max-height: 80vh;
|
||||||
|
flex-grow: 1;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#dogimg {
|
||||||
|
display: block;
|
||||||
|
max-width: 50%;
|
||||||
|
max-height: 50%;
|
||||||
|
transform: scale(1.8);
|
||||||
|
border-radius: 5px;
|
||||||
|
border: 1px solid rgb(233, 233, 233);
|
||||||
|
box-shadow: 0px 0px 5px 1px rgb(216, 216, 216, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
#title {
|
||||||
|
text-align: center;
|
||||||
|
padding-top: 10px;
|
||||||
|
border-bottom: 1px solid #a8a8a8;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-evenly;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#title a {
|
||||||
|
text-decoration: none;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
#heart {
|
||||||
|
background-color: white;
|
||||||
|
padding: 5px;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#title span {
|
||||||
|
width: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#title h1 {
|
||||||
|
margin: 0.25em;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
#buttons {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 20px;
|
||||||
|
/* padding-top: 20px; */
|
||||||
|
padding-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#skip { background-color: gray }
|
||||||
|
#save { background-color: green; }
|
||||||
|
|
||||||
|
#skip, #save {
|
||||||
|
padding: 5px 30px 5px 30px;
|
||||||
|
border-radius: 3px;
|
||||||
|
font-size: 2rem;
|
||||||
|
font-weight: bold;
|
||||||
|
color: rgb(230, 230, 230)
|
||||||
|
}
|
||||||
|
|
||||||
|
#navbar {
|
||||||
|
border: 1px solid rgb(233, 233, 233);
|
||||||
|
border-width: 1px 0px 0px 0px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-evenly;
|
||||||
|
padding: 20px;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#navbar a {
|
||||||
|
background-color: #a8a8a8;
|
||||||
|
border-radius: 5px;
|
||||||
|
border: 1px solid black;
|
||||||
|
text-decoration: none;
|
||||||
|
color: black;
|
||||||
|
padding: 10px 30px 10px 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#favorites {
|
||||||
|
flex-grow: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#favorites-container {
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.favorite-dog {
|
||||||
|
max-height: 180px;
|
||||||
|
max-width: 60%;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.favorite-dog img {
|
||||||
|
max-height: 150px;
|
||||||
|
border-radius: 5px;
|
||||||
|
margin: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.favorite-dog:hover button {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.favorite-dog button {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 10px;
|
||||||
|
left: 10px;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 1.4 MiB |
@@ -0,0 +1,26 @@
|
|||||||
|
# fly.toml app configuration file generated for hot-dog on 2024-12-19T18:23:45-08:00
|
||||||
|
#
|
||||||
|
# See https://fly.io/docs/reference/configuration/ for information about how to use this file.
|
||||||
|
#
|
||||||
|
|
||||||
|
app = 'hot-dog'
|
||||||
|
primary_region = 'sjc'
|
||||||
|
|
||||||
|
[build]
|
||||||
|
|
||||||
|
[http_service]
|
||||||
|
internal_port = 8080
|
||||||
|
force_https = true
|
||||||
|
auto_stop_machines = 'stop'
|
||||||
|
auto_start_machines = true
|
||||||
|
min_machines_running = 0
|
||||||
|
processes = ['app']
|
||||||
|
|
||||||
|
[[vm]]
|
||||||
|
memory = '1gb'
|
||||||
|
cpu_kind = 'shared'
|
||||||
|
cpus = 1
|
||||||
|
|
||||||
|
[mounts]
|
||||||
|
source = "hotdogdb"
|
||||||
|
destination = "/usr/local/app/hotdogdb"
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
|
#[cfg(feature = "server")]
|
||||||
|
thread_local! {
|
||||||
|
static DB: rusqlite::Connection = {
|
||||||
|
let conn = rusqlite::Connection::open("hotdogdb/hotdog.db").expect("Failed to open database");
|
||||||
|
|
||||||
|
conn.execute_batch(
|
||||||
|
"CREATE TABLE IF NOT EXISTS dogs (
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
url TEXT NOT NULL
|
||||||
|
);",
|
||||||
|
).unwrap();
|
||||||
|
|
||||||
|
conn
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[server(endpoint = "list_dogs")]
|
||||||
|
pub async fn list_dogs() -> Result<Vec<(usize, String)>, ServerFnError> {
|
||||||
|
let dogs = DB.with(|f| {
|
||||||
|
f.prepare("SELECT id, url FROM dogs ORDER BY id DESC LIMIT 10")
|
||||||
|
.unwrap()
|
||||||
|
.query_map([], |row| Ok((row.get(0)?, row.get(1)?)))
|
||||||
|
.unwrap()
|
||||||
|
.map(|r| r.unwrap())
|
||||||
|
.collect()
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(dogs)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[server(endpoint = "remove_dog")]
|
||||||
|
pub async fn remove_dog(id: usize) -> Result<(), ServerFnError> {
|
||||||
|
DB.with(|f| f.execute("DELETE FROM dogs WHERE id = ?1", [&id]))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[server(endpoint = "save_dog")]
|
||||||
|
pub async fn save_dog(image: String) -> Result<(), ServerFnError> {
|
||||||
|
_ = DB.with(|f| f.execute("INSERT INTO dogs (url) VALUES (?1)", [&image]));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn Favorites() -> Element {
|
||||||
|
let mut favorites = use_resource(crate::backend::list_dogs);
|
||||||
|
|
||||||
|
rsx! {
|
||||||
|
div { id: "favorites",
|
||||||
|
div { id: "favorites-container",
|
||||||
|
for (id , url) in favorites.suspend()?.cloned().unwrap() {
|
||||||
|
div { class: "favorite-dog", key: "{id}",
|
||||||
|
img { src: "{url}" }
|
||||||
|
button {
|
||||||
|
onclick: move |_| async move {
|
||||||
|
crate::backend::remove_dog(id).await.unwrap();
|
||||||
|
favorites.restart();
|
||||||
|
},
|
||||||
|
"❌"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
mod favorites;
|
||||||
|
mod nav;
|
||||||
|
mod view;
|
||||||
|
|
||||||
|
pub use favorites::*;
|
||||||
|
pub use nav::*;
|
||||||
|
pub use view::*;
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
use crate::Route;
|
||||||
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn NavBar() -> Element {
|
||||||
|
rsx! {
|
||||||
|
div { id: "title",
|
||||||
|
span {}
|
||||||
|
|
||||||
|
Link { to: Route::DogView,
|
||||||
|
h1 { "🌭 HotDog! " }
|
||||||
|
}
|
||||||
|
|
||||||
|
Link { to: Route::Favorites, id: "heart", "♥️" }
|
||||||
|
}
|
||||||
|
|
||||||
|
Outlet::<Route> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn DogView() -> Element {
|
||||||
|
let mut img_src = use_resource(|| async move {
|
||||||
|
#[derive(serde::Deserialize)]
|
||||||
|
struct DogApi {
|
||||||
|
message: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
reqwest::get("https://dog.ceo/api/breeds/image/random")
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.json::<DogApi>()
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.message
|
||||||
|
});
|
||||||
|
|
||||||
|
rsx! {
|
||||||
|
div { id: "dogview",
|
||||||
|
img { id: "dogimg", src: "{img_src().unwrap_or_default()}" }
|
||||||
|
}
|
||||||
|
div { id: "buttons",
|
||||||
|
button {
|
||||||
|
id: "skip",
|
||||||
|
onclick: move |_| {
|
||||||
|
img_src.restart();
|
||||||
|
},
|
||||||
|
"skip"
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
id: "save",
|
||||||
|
onclick: move |_| async move {
|
||||||
|
img_src.restart();
|
||||||
|
crate::backend::save_dog(img_src().unwrap()).await.unwrap();
|
||||||
|
},
|
||||||
|
"save!"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
mod backend;
|
||||||
|
mod components;
|
||||||
|
|
||||||
|
use components::{DogView, Favorites, NavBar};
|
||||||
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Routable, PartialEq, Clone)]
|
||||||
|
enum Route {
|
||||||
|
#[layout(NavBar)]
|
||||||
|
#[route("/")]
|
||||||
|
DogView,
|
||||||
|
|
||||||
|
#[route("/favorites")]
|
||||||
|
Favorites,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
#[cfg(not(feature = "server"))]
|
||||||
|
server_fn::client::set_server_url("https://hot-dog.fly.dev");
|
||||||
|
|
||||||
|
dioxus::launch(app);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn app() -> Element {
|
||||||
|
rsx! {
|
||||||
|
document::Stylesheet { href: asset!("/assets/main.css") }
|
||||||
|
Router::<Route> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -103,7 +103,7 @@ There are a number of crates that are part of the Dioxus ecosystem that we usual
|
|||||||
|
|
||||||
Verify everything works and is ready to go.
|
Verify everything works and is ready to go.
|
||||||
- [ ] crates.io is up to date
|
- [ ] crates.io is up to date
|
||||||
- [ ] docs.rs is up to date and has no failing builds for major crates (dioxus, dioxus-desktop, dioxus-mobile, etc)
|
- [ ] docs.rs is up to date and has no failing builds for major crates (dioxus, dioxus-desktop, etc)
|
||||||
- [ ] github release post is out
|
- [ ] github release post is out
|
||||||
- [ ] The relevant GitHub tag is created
|
- [ ] The relevant GitHub tag is created
|
||||||
- [ ] Any fixes are backported to the relevant branches.
|
- [ ] Any fixes are backported to the relevant branches.
|
||||||
|
|||||||
@@ -1,13 +1,7 @@
|
|||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn Explainer<'a>(
|
pub fn Explainer(invert: bool, title: &'static str, content: Element, flasher: Element) -> Element {
|
||||||
cx: Scope<'a>,
|
|
||||||
invert: bool,
|
|
||||||
title: &'static str,
|
|
||||||
content: Element,
|
|
||||||
flasher: Element,
|
|
||||||
) -> Element {
|
|
||||||
// pt-5 sm:pt-24 lg:pt-24
|
// pt-5 sm:pt-24 lg:pt-24
|
||||||
|
|
||||||
let mut right = rsx! {
|
let mut right = rsx! {
|
||||||
|
|||||||
@@ -149,9 +149,7 @@ built = { version = "0.7.5", features = ["git2"] }
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
plugin = []
|
|
||||||
tokio-console = ["dep:console-subscriber", "tokio/tracing"]
|
tokio-console = ["dep:console-subscriber", "tokio/tracing"]
|
||||||
bundle = []
|
|
||||||
no-downloads = []
|
no-downloads = []
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::Result;
|
use crate::Result;
|
||||||
use anyhow::Context;
|
use anyhow::{bail, Context};
|
||||||
use const_serialize::{ConstVec, SerializeConst};
|
use const_serialize::{ConstVec, SerializeConst};
|
||||||
use dioxus_cli_opt::AssetManifest;
|
use dioxus_cli_opt::AssetManifest;
|
||||||
use manganis::BundledAsset;
|
use manganis::BundledAsset;
|
||||||
@@ -352,11 +352,10 @@ pub(crate) fn extract_assets_from_file(path: impl AsRef<Path>) -> Result<AssetMa
|
|||||||
.output()?;
|
.output()?;
|
||||||
|
|
||||||
if !output.status.success() {
|
if !output.status.success() {
|
||||||
return Err(anyhow::anyhow!(
|
bail!(
|
||||||
"Failed to re-sign the binary with codesign after finalizing the assets: {}",
|
"Failed to re-sign the binary with codesign after finalizing the assets: {}",
|
||||||
String::from_utf8_lossy(&output.stderr)
|
String::from_utf8_lossy(&output.stderr)
|
||||||
)
|
);
|
||||||
.into());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use crate::{
|
|||||||
serve::WebServer, BuildArtifacts, BuildRequest, BuildStage, BuilderUpdate, Platform,
|
serve::WebServer, BuildArtifacts, BuildRequest, BuildStage, BuilderUpdate, Platform,
|
||||||
ProgressRx, ProgressTx, Result, StructuredOutput,
|
ProgressRx, ProgressTx, Result, StructuredOutput,
|
||||||
};
|
};
|
||||||
use anyhow::Context;
|
use anyhow::{bail, Context};
|
||||||
use dioxus_cli_opt::process_file_to;
|
use dioxus_cli_opt::process_file_to;
|
||||||
use futures_util::{future::OptionFuture, pin_mut, FutureExt};
|
use futures_util::{future::OptionFuture, pin_mut, FutureExt};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
@@ -122,25 +122,13 @@ impl AppBuilder {
|
|||||||
/// updates (e.g., `wait`, `finish_build`).
|
/// updates (e.g., `wait`, `finish_build`).
|
||||||
/// - The build process is designed to be cancellable and restartable using methods like `abort_all`
|
/// - The build process is designed to be cancellable and restartable using methods like `abort_all`
|
||||||
/// or `rebuild`.
|
/// or `rebuild`.
|
||||||
pub(crate) fn start(request: &BuildRequest, mode: BuildMode) -> Result<Self> {
|
pub(crate) fn new(request: &BuildRequest) -> Result<Self> {
|
||||||
let (tx, rx) = futures_channel::mpsc::unbounded();
|
let (tx, rx) = futures_channel::mpsc::unbounded();
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
build: request.clone(),
|
build: request.clone(),
|
||||||
stage: BuildStage::Initializing,
|
stage: BuildStage::Initializing,
|
||||||
build_task: tokio::spawn({
|
build_task: tokio::task::spawn(std::future::pending()),
|
||||||
let request = request.clone();
|
|
||||||
let tx = tx.clone();
|
|
||||||
async move {
|
|
||||||
let ctx = BuildContext {
|
|
||||||
mode,
|
|
||||||
tx: tx.clone(),
|
|
||||||
};
|
|
||||||
request.verify_tooling(&ctx).await?;
|
|
||||||
request.prepare_build_dir()?;
|
|
||||||
request.build(&ctx).await
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
tx,
|
tx,
|
||||||
rx,
|
rx,
|
||||||
patches: vec![],
|
patches: vec![],
|
||||||
@@ -164,6 +152,29 @@ impl AppBuilder {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new `AppBuilder` and immediately start a build process.
|
||||||
|
pub fn started(request: &BuildRequest, mode: BuildMode) -> Result<Self> {
|
||||||
|
let mut builder = Self::new(request)?;
|
||||||
|
builder.start(mode);
|
||||||
|
Ok(builder)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn start(&mut self, mode: BuildMode) {
|
||||||
|
self.build_task = tokio::spawn({
|
||||||
|
let request = self.build.clone();
|
||||||
|
let tx = self.tx.clone();
|
||||||
|
async move {
|
||||||
|
let ctx = BuildContext {
|
||||||
|
mode,
|
||||||
|
tx: tx.clone(),
|
||||||
|
};
|
||||||
|
request.verify_tooling(&ctx).await?;
|
||||||
|
request.prepare_build_dir()?;
|
||||||
|
request.build(&ctx).await
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/// Wait for any new updates to the builder - either it completed or gave us a message etc
|
/// Wait for any new updates to the builder - either it completed or gave us a message etc
|
||||||
pub(crate) async fn wait(&mut self) -> BuilderUpdate {
|
pub(crate) async fn wait(&mut self) -> BuilderUpdate {
|
||||||
use futures_util::StreamExt;
|
use futures_util::StreamExt;
|
||||||
@@ -178,7 +189,7 @@ impl AppBuilder {
|
|||||||
match bundle {
|
match bundle {
|
||||||
Ok(Ok(bundle)) => BuilderUpdate::BuildReady { bundle },
|
Ok(Ok(bundle)) => BuilderUpdate::BuildReady { bundle },
|
||||||
Ok(Err(err)) => BuilderUpdate::BuildFailed { err },
|
Ok(Err(err)) => BuilderUpdate::BuildFailed { err },
|
||||||
Err(err) => BuilderUpdate::BuildFailed { err: crate::Error::Runtime(format!("Build panicked! {:#?}", err)) },
|
Err(err) => BuilderUpdate::BuildFailed { err: anyhow::anyhow!("Build panicked! {:#?}", err) },
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Some(Ok(Some(msg))) = OptionFuture::from(self.stdout.as_mut().map(|f| f.next_line())) => {
|
Some(Ok(Some(msg))) = OptionFuture::from(self.stdout.as_mut().map(|f| f.next_line())) => {
|
||||||
@@ -596,8 +607,12 @@ impl AppBuilder {
|
|||||||
let triple = self.build.triple.clone();
|
let triple = self.build.triple.clone();
|
||||||
let asset_dir = self.build.asset_dir();
|
let asset_dir = self.build.asset_dir();
|
||||||
|
|
||||||
|
// Hotpatch asset!() calls
|
||||||
for bundled in res.assets.assets() {
|
for bundled in res.assets.assets() {
|
||||||
let original_artifacts = self.artifacts.as_mut().unwrap();
|
let original_artifacts = self
|
||||||
|
.artifacts
|
||||||
|
.as_mut()
|
||||||
|
.context("No artifacts to hotpatch")?;
|
||||||
|
|
||||||
if original_artifacts.assets.contains(bundled) {
|
if original_artifacts.assets.contains(bundled) {
|
||||||
continue;
|
continue;
|
||||||
@@ -623,6 +638,18 @@ impl AppBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make sure to add `include!()` calls to the watcher so we can watch changes as they evolve
|
||||||
|
for file in res.depinfo.files.iter() {
|
||||||
|
let original_artifacts = self
|
||||||
|
.artifacts
|
||||||
|
.as_mut()
|
||||||
|
.context("No artifacts to hotpatch")?;
|
||||||
|
|
||||||
|
if !original_artifacts.depinfo.files.contains(file) {
|
||||||
|
original_artifacts.depinfo.files.push(file.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
tracing::debug!("Patching {} -> {}", original.display(), new.display());
|
tracing::debug!("Patching {} -> {}", original.display(), new.display());
|
||||||
|
|
||||||
let mut jump_table = crate::build::create_jump_table(&new, &triple, cache)?;
|
let mut jump_table = crate::build::create_jump_table(&new, &triple, cache)?;
|
||||||
@@ -891,7 +918,7 @@ impl AppBuilder {
|
|||||||
.context("Failed to parse xcrun output")?;
|
.context("Failed to parse xcrun output")?;
|
||||||
let device_uuid = json["result"]["devices"][0]["identifier"]
|
let device_uuid = json["result"]["devices"][0]["identifier"]
|
||||||
.as_str()
|
.as_str()
|
||||||
.ok_or("Failed to extract device UUID")?
|
.context("Failed to extract device UUID")?
|
||||||
.to_string();
|
.to_string();
|
||||||
|
|
||||||
Ok(device_uuid)
|
Ok(device_uuid)
|
||||||
@@ -918,14 +945,14 @@ impl AppBuilder {
|
|||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
if !output.status.success() {
|
if !output.status.success() {
|
||||||
return Err(format!("Failed to install app: {:?}", output).into());
|
bail!("Failed to install app: {:?}", output);
|
||||||
}
|
}
|
||||||
|
|
||||||
let json: Value = serde_json::from_str(&std::fs::read_to_string(tmpfile.path())?)
|
let json: Value = serde_json::from_str(&std::fs::read_to_string(tmpfile.path())?)
|
||||||
.context("Failed to parse xcrun output")?;
|
.context("Failed to parse xcrun output")?;
|
||||||
let installation_url = json["result"]["installedApplications"][0]["installationURL"]
|
let installation_url = json["result"]["installedApplications"][0]["installationURL"]
|
||||||
.as_str()
|
.as_str()
|
||||||
.ok_or("Failed to extract installation URL")?
|
.context("Failed to extract installation URL from xcrun output")?
|
||||||
.to_string();
|
.to_string();
|
||||||
|
|
||||||
Ok(installation_url)
|
Ok(installation_url)
|
||||||
@@ -953,7 +980,7 @@ impl AppBuilder {
|
|||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
if !output.status.success() {
|
if !output.status.success() {
|
||||||
return Err(format!("Failed to launch app: {:?}", output).into());
|
bail!("Failed to launch app: {:?}", output);
|
||||||
}
|
}
|
||||||
|
|
||||||
let json: Value = serde_json::from_str(&std::fs::read_to_string(tmpfile.path())?)
|
let json: Value = serde_json::from_str(&std::fs::read_to_string(tmpfile.path())?)
|
||||||
@@ -961,7 +988,7 @@ impl AppBuilder {
|
|||||||
|
|
||||||
let status_pid = json["result"]["process"]["processIdentifier"]
|
let status_pid = json["result"]["process"]["processIdentifier"]
|
||||||
.as_u64()
|
.as_u64()
|
||||||
.ok_or("Failed to extract process identifier")?;
|
.context("Failed to extract process identifier")?;
|
||||||
|
|
||||||
let output = Command::new("xcrun")
|
let output = Command::new("xcrun")
|
||||||
.args([
|
.args([
|
||||||
@@ -978,7 +1005,7 @@ impl AppBuilder {
|
|||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
if !output.status.success() {
|
if !output.status.success() {
|
||||||
return Err(format!("Failed to resume app: {:?}", output).into());
|
bail!("Failed to resume app: {:?}", output);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -998,18 +1025,27 @@ To accept the agreement, go to https://developer.apple.com/account
|
|||||||
To create a provisioning profile, follow the instructions here:
|
To create a provisioning profile, follow the instructions here:
|
||||||
https://developer.apple.com/documentation/xcode/sharing-your-teams-signing-certificates"#;
|
https://developer.apple.com/documentation/xcode/sharing-your-teams-signing-certificates"#;
|
||||||
|
|
||||||
let profiles_folder = dirs::home_dir()
|
// Check the xcode 16 location first
|
||||||
|
let mut profiles_folder = dirs::home_dir()
|
||||||
.context("Your machine has no home-dir")?
|
.context("Your machine has no home-dir")?
|
||||||
.join("Library/Developer/Xcode/UserData/Provisioning Profiles");
|
.join("Library/Developer/Xcode/UserData/Provisioning Profiles");
|
||||||
|
|
||||||
|
// If it doesn't exist, check the old location
|
||||||
|
if !profiles_folder.exists() {
|
||||||
|
profiles_folder = dirs::home_dir()
|
||||||
|
.context("Your machine has no home-dir")?
|
||||||
|
.join("Library/MobileDevice/Provisioning Profiles");
|
||||||
|
}
|
||||||
|
|
||||||
if !profiles_folder.exists() || profiles_folder.read_dir()?.next().is_none() {
|
if !profiles_folder.exists() || profiles_folder.read_dir()?.next().is_none() {
|
||||||
tracing::error!(
|
tracing::error!(
|
||||||
r#"No provisioning profiles found when trying to codesign the app.
|
r#"No provisioning profiles found when trying to codesign the app.
|
||||||
We checked the folder: {}
|
We checked the folders:
|
||||||
|
- XCode16: ~/Library/Developer/Xcode/UserData/Provisioning Profiles
|
||||||
|
- XCode15: ~/Library/MobileDevice/Provisioning Profiles
|
||||||
|
|
||||||
{CODESIGN_ERROR}
|
{CODESIGN_ERROR}
|
||||||
"#,
|
"#
|
||||||
profiles_folder.display()
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1125,8 +1161,10 @@ We checked the folder: {}
|
|||||||
.context("Failed to codesign the app")?;
|
.context("Failed to codesign the app")?;
|
||||||
|
|
||||||
if !output.status.success() {
|
if !output.status.success() {
|
||||||
let stderr = String::from_utf8(output.stderr).unwrap_or_default();
|
bail!(
|
||||||
return Err(format!("Failed to codesign the app: {stderr}").into());
|
"Failed to codesign the app: {}",
|
||||||
|
String::from_utf8(output.stderr).unwrap_or_default()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ impl BuildContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn status_build_error(&self, line: String) {
|
pub(crate) fn status_build_error(&self, line: String) {
|
||||||
tracing::error!(dx_src = ?TraceSrc::Cargo, "{line}");
|
tracing::debug!(dx_src = ?TraceSrc::Cargo, "{line}");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn status_build_message(&self, line: String) {
|
pub(crate) fn status_build_message(&self, line: String) {
|
||||||
|
|||||||
+356
-128
@@ -320,8 +320,9 @@ use crate::{
|
|||||||
RustcArgs, TargetArgs, TraceSrc, WasmBindgen, WasmOptConfig, Workspace,
|
RustcArgs, TargetArgs, TraceSrc, WasmBindgen, WasmOptConfig, Workspace,
|
||||||
DX_RUSTC_WRAPPER_ENV_VAR,
|
DX_RUSTC_WRAPPER_ENV_VAR,
|
||||||
};
|
};
|
||||||
use anyhow::Context;
|
use anyhow::{bail, Context};
|
||||||
use cargo_metadata::diagnostic::Diagnostic;
|
use cargo_metadata::diagnostic::Diagnostic;
|
||||||
|
use depinfo::RustcDepInfo;
|
||||||
use dioxus_cli_config::format_base_path_meta_element;
|
use dioxus_cli_config::format_base_path_meta_element;
|
||||||
use dioxus_cli_config::{APP_TITLE_ENV, ASSET_ROOT_ENV};
|
use dioxus_cli_config::{APP_TITLE_ENV, ASSET_ROOT_ENV};
|
||||||
use dioxus_cli_opt::{process_file_to, AssetManifest};
|
use dioxus_cli_opt::{process_file_to, AssetManifest};
|
||||||
@@ -331,7 +332,7 @@ use manganis::AssetOptions;
|
|||||||
use manganis_core::AssetVariant;
|
use manganis_core::AssetVariant;
|
||||||
use rayon::prelude::{IntoParallelRefIterator, ParallelIterator};
|
use rayon::prelude::{IntoParallelRefIterator, ParallelIterator};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::borrow::Cow;
|
use std::{borrow::Cow, ffi::OsString};
|
||||||
use std::{
|
use std::{
|
||||||
collections::{BTreeMap, HashSet},
|
collections::{BTreeMap, HashSet},
|
||||||
io::Write,
|
io::Write,
|
||||||
@@ -391,6 +392,7 @@ pub(crate) struct BuildRequest {
|
|||||||
pub(crate) link_args_file: Arc<NamedTempFile>,
|
pub(crate) link_args_file: Arc<NamedTempFile>,
|
||||||
pub(crate) link_err_file: Arc<NamedTempFile>,
|
pub(crate) link_err_file: Arc<NamedTempFile>,
|
||||||
pub(crate) rustc_wrapper_args_file: Arc<NamedTempFile>,
|
pub(crate) rustc_wrapper_args_file: Arc<NamedTempFile>,
|
||||||
|
pub(crate) command_file: Arc<NamedTempFile>,
|
||||||
pub(crate) base_path: Option<String>,
|
pub(crate) base_path: Option<String>,
|
||||||
pub(crate) using_dioxus_explicitly: bool,
|
pub(crate) using_dioxus_explicitly: bool,
|
||||||
}
|
}
|
||||||
@@ -443,6 +445,7 @@ pub struct BuildArtifacts {
|
|||||||
pub(crate) assets: AssetManifest,
|
pub(crate) assets: AssetManifest,
|
||||||
pub(crate) mode: BuildMode,
|
pub(crate) mode: BuildMode,
|
||||||
pub(crate) patch_cache: Option<Arc<HotpatchModuleCache>>,
|
pub(crate) patch_cache: Option<Arc<HotpatchModuleCache>>,
|
||||||
|
pub(crate) depinfo: RustcDepInfo,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BuildRequest {
|
impl BuildRequest {
|
||||||
@@ -569,13 +572,10 @@ impl BuildRequest {
|
|||||||
},
|
},
|
||||||
None if !using_dioxus_explicitly => Platform::autodetect_from_cargo_feature("desktop").unwrap(),
|
None if !using_dioxus_explicitly => Platform::autodetect_from_cargo_feature("desktop").unwrap(),
|
||||||
None => match enabled_platforms.len() {
|
None => match enabled_platforms.len() {
|
||||||
0 => return Err(anyhow::anyhow!("No platform specified and no platform marked as default in Cargo.toml. Try specifying a platform with `--platform`").into()),
|
0 => bail!("No platform specified and no platform marked as default in Cargo.toml. Try specifying a platform with `--platform`"),
|
||||||
1 => enabled_platforms[0],
|
1 => enabled_platforms[0],
|
||||||
_ => {
|
_ => {
|
||||||
return Err(anyhow::anyhow!(
|
bail!("Multiple platforms enabled in Cargo.toml. Please specify a platform with `--platform` or set a default platform in Cargo.toml")
|
||||||
"Multiple platforms enabled in Cargo.toml. Please specify a platform with `--platform` or set a default platform in Cargo.toml"
|
|
||||||
)
|
|
||||||
.into())
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -662,7 +662,13 @@ impl BuildRequest {
|
|||||||
"-Clink-arg=-landroid".to_string(),
|
"-Clink-arg=-landroid".to_string(),
|
||||||
"-Clink-arg=-llog".to_string(),
|
"-Clink-arg=-llog".to_string(),
|
||||||
"-Clink-arg=-lOpenSLES".to_string(),
|
"-Clink-arg=-lOpenSLES".to_string(),
|
||||||
|
"-Clink-arg=-lc++".to_string(),
|
||||||
|
"-Clink-arg=-lc++abi".to_string(),
|
||||||
"-Clink-arg=-Wl,--export-dynamic".to_string(),
|
"-Clink-arg=-Wl,--export-dynamic".to_string(),
|
||||||
|
format!(
|
||||||
|
"-Clink-arg=-Wl,--sysroot={}",
|
||||||
|
workspace.android_tools()?.sysroot().display()
|
||||||
|
),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -712,6 +718,9 @@ impl BuildRequest {
|
|||||||
let session_cache_dir = Arc::new(
|
let session_cache_dir = Arc::new(
|
||||||
TempDir::new().context("Failed to create temporary directory for session cache")?,
|
TempDir::new().context("Failed to create temporary directory for session cache")?,
|
||||||
);
|
);
|
||||||
|
let command_file = Arc::new(
|
||||||
|
NamedTempFile::new().context("Failed to create temporary file for linker args")?,
|
||||||
|
);
|
||||||
|
|
||||||
let extra_rustc_args = shell_words::split(&args.rustc_args.clone().unwrap_or_default())
|
let extra_rustc_args = shell_words::split(&args.rustc_args.clone().unwrap_or_default())
|
||||||
.context("Failed to parse rustc args")?;
|
.context("Failed to parse rustc args")?;
|
||||||
@@ -752,6 +761,7 @@ impl BuildRequest {
|
|||||||
custom_linker,
|
custom_linker,
|
||||||
link_args_file,
|
link_args_file,
|
||||||
link_err_file,
|
link_err_file,
|
||||||
|
command_file,
|
||||||
session_cache_dir,
|
session_cache_dir,
|
||||||
rustc_wrapper_args_file,
|
rustc_wrapper_args_file,
|
||||||
extra_rustc_args,
|
extra_rustc_args,
|
||||||
@@ -774,6 +784,9 @@ impl BuildRequest {
|
|||||||
// We need to make sure to not react to this though, so the filemap must cache it
|
// We need to make sure to not react to this though, so the filemap must cache it
|
||||||
_ = self.bust_fingerprint(ctx);
|
_ = self.bust_fingerprint(ctx);
|
||||||
|
|
||||||
|
// Run any pre-build steps like tailwind, etc
|
||||||
|
self.prebuild().await?;
|
||||||
|
|
||||||
// Run the cargo build to produce our artifacts
|
// Run the cargo build to produce our artifacts
|
||||||
let mut artifacts = self.cargo_build(ctx).await?;
|
let mut artifacts = self.cargo_build(ctx).await?;
|
||||||
|
|
||||||
@@ -793,12 +806,23 @@ impl BuildRequest {
|
|||||||
ctx.status_start_bundle();
|
ctx.status_start_bundle();
|
||||||
|
|
||||||
self.write_executable(ctx, &artifacts.exe, &mut artifacts.assets)
|
self.write_executable(ctx, &artifacts.exe, &mut artifacts.assets)
|
||||||
.await?;
|
.await
|
||||||
self.write_frameworks(ctx, &artifacts.direct_rustc).await?;
|
.context("Failed to write executable")?;
|
||||||
self.write_assets(ctx, &artifacts.assets).await?;
|
self.write_frameworks(ctx, &artifacts.direct_rustc)
|
||||||
self.write_metadata().await?;
|
.await
|
||||||
self.optimize(ctx).await?;
|
.context("Failed to write frameworks")?;
|
||||||
self.assemble(ctx).await?;
|
self.write_assets(ctx, &artifacts.assets)
|
||||||
|
.await
|
||||||
|
.context("Failed to write assets")?;
|
||||||
|
self.write_metadata()
|
||||||
|
.await
|
||||||
|
.context("Failed to write metadata")?;
|
||||||
|
self.optimize(ctx)
|
||||||
|
.await
|
||||||
|
.context("Failed to optimize build")?;
|
||||||
|
self.assemble(ctx)
|
||||||
|
.await
|
||||||
|
.context("Failed to assemble build")?;
|
||||||
|
|
||||||
tracing::debug!("Bundle created at {}", self.root_dir().display());
|
tracing::debug!("Bundle created at {}", self.root_dir().display());
|
||||||
}
|
}
|
||||||
@@ -916,10 +940,7 @@ impl BuildRequest {
|
|||||||
// since that is a really bad user experience.
|
// since that is a really bad user experience.
|
||||||
Message::BuildFinished(finished) => {
|
Message::BuildFinished(finished) => {
|
||||||
if !finished.success {
|
if !finished.success {
|
||||||
return Err(anyhow::anyhow!(
|
bail!("cargo build finished with errors.")
|
||||||
"Cargo build failed, signaled by the compiler. Toggle tracing mode (press `t`) for more information."
|
|
||||||
)
|
|
||||||
.into());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
@@ -971,6 +992,7 @@ impl BuildRequest {
|
|||||||
let time_end = SystemTime::now();
|
let time_end = SystemTime::now();
|
||||||
let mode = ctx.mode.clone();
|
let mode = ctx.mode.clone();
|
||||||
let platform = self.platform;
|
let platform = self.platform;
|
||||||
|
let depinfo = RustcDepInfo::from_file(&exe.with_extension("d")).unwrap_or_default();
|
||||||
|
|
||||||
tracing::debug!(
|
tracing::debug!(
|
||||||
"Build completed successfully in {}us: {:?}",
|
"Build completed successfully in {}us: {:?}",
|
||||||
@@ -986,6 +1008,7 @@ impl BuildRequest {
|
|||||||
time_start,
|
time_start,
|
||||||
assets,
|
assets,
|
||||||
mode,
|
mode,
|
||||||
|
depinfo,
|
||||||
patch_cache: None,
|
patch_cache: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -1079,6 +1102,7 @@ impl BuildRequest {
|
|||||||
// todo - how do we handle windows dlls? we don't want to bundle the system dlls
|
// todo - how do we handle windows dlls? we don't want to bundle the system dlls
|
||||||
// for now, we don't do anything with dlls, and only use .dylibs and .so files
|
// for now, we don't do anything with dlls, and only use .dylibs and .so files
|
||||||
|
|
||||||
|
// Write dylibs and dlls to the frameworks folder
|
||||||
if arg.ends_with(".dylib") | arg.ends_with(".so") {
|
if arg.ends_with(".dylib") | arg.ends_with(".so") {
|
||||||
let from = PathBuf::from(arg);
|
let from = PathBuf::from(arg);
|
||||||
let to = framework_dir.join(from.file_name().unwrap());
|
let to = framework_dir.join(from.file_name().unwrap());
|
||||||
@@ -1104,6 +1128,16 @@ impl BuildRequest {
|
|||||||
std::fs::copy(from, to)?;
|
std::fs::copy(from, to)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// On android, the c++_static flag means we need to copy the libc++_shared.so precompiled
|
||||||
|
// library to the jniLibs folder
|
||||||
|
if arg.contains("-lc++_static") && self.platform == Platform::Android {
|
||||||
|
std::fs::copy(
|
||||||
|
self.workspace.android_tools()?.libcpp_shared(&self.triple),
|
||||||
|
framework_dir.join("libc++_shared.so"),
|
||||||
|
)
|
||||||
|
.with_context(|| "Failed to copy libc++_shared.so into bundle")?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -1115,6 +1149,13 @@ impl BuildRequest {
|
|||||||
self.root_dir().join("Contents").join("Frameworks")
|
self.root_dir().join("Contents").join("Frameworks")
|
||||||
}
|
}
|
||||||
OperatingSystem::IOS(_) => self.root_dir().join("Frameworks"),
|
OperatingSystem::IOS(_) => self.root_dir().join("Frameworks"),
|
||||||
|
OperatingSystem::Linux if self.platform == Platform::Android => self
|
||||||
|
.root_dir()
|
||||||
|
.join("app")
|
||||||
|
.join("src")
|
||||||
|
.join("main")
|
||||||
|
.join("jniLibs")
|
||||||
|
.join("arm64-v8a"),
|
||||||
OperatingSystem::Linux | OperatingSystem::Windows => self.root_dir(),
|
OperatingSystem::Linux | OperatingSystem::Windows => self.root_dir(),
|
||||||
_ => self.root_dir(),
|
_ => self.root_dir(),
|
||||||
}
|
}
|
||||||
@@ -1129,14 +1170,6 @@ impl BuildRequest {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run the tailwind build before bundling anything else
|
|
||||||
crate::TailwindCli::run_once(
|
|
||||||
self.package_manifest_dir(),
|
|
||||||
self.config.application.tailwind_input.clone(),
|
|
||||||
self.config.application.tailwind_output.clone(),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let asset_dir = self.asset_dir();
|
let asset_dir = self.asset_dir();
|
||||||
|
|
||||||
// First, clear the asset dir of any files that don't exist in the new manifest
|
// First, clear the asset dir of any files that don't exist in the new manifest
|
||||||
@@ -1379,15 +1412,28 @@ impl BuildRequest {
|
|||||||
|
|
||||||
tracing::trace!("Linking with {:?} using args: {:#?}", linker, object_files);
|
tracing::trace!("Linking with {:?} using args: {:#?}", linker, object_files);
|
||||||
|
|
||||||
|
let mut out_args: Vec<OsString> = vec![];
|
||||||
|
out_args.extend(object_files.iter().map(Into::into));
|
||||||
|
out_args.extend(dylibs.iter().map(Into::into));
|
||||||
|
out_args.extend(self.thin_link_args(&args)?.iter().map(Into::into));
|
||||||
|
out_args.extend(out_arg.iter().map(Into::into));
|
||||||
|
|
||||||
|
if cfg!(windows) {
|
||||||
|
let cmd_contents: String = out_args
|
||||||
|
.iter()
|
||||||
|
.map(|s| format!("\"{}\"", s.to_string_lossy()))
|
||||||
|
.join(" ");
|
||||||
|
std::fs::write(self.command_file.path(), cmd_contents)
|
||||||
|
.context("Failed to write linker command file")?;
|
||||||
|
out_args = vec![format!("@{}", self.command_file.path().display()).into()];
|
||||||
|
}
|
||||||
|
|
||||||
// Run the linker directly!
|
// Run the linker directly!
|
||||||
//
|
//
|
||||||
// We dump its output directly into the patch exe location which is different than how rustc
|
// We dump its output directly into the patch exe location which is different than how rustc
|
||||||
// does it since it uses llvm-objcopy into the `target/debug/` folder.
|
// does it since it uses llvm-objcopy into the `target/debug/` folder.
|
||||||
let res = Command::new(linker)
|
let res = Command::new(linker)
|
||||||
.args(object_files.iter())
|
.args(out_args)
|
||||||
.args(dylibs.iter())
|
|
||||||
.args(self.thin_link_args(&args)?)
|
|
||||||
.args(out_arg)
|
|
||||||
.env_clear()
|
.env_clear()
|
||||||
.envs(rustc_args.envs.iter().map(|(k, v)| (k, v)))
|
.envs(rustc_args.envs.iter().map(|(k, v)| (k, v)))
|
||||||
.output()
|
.output()
|
||||||
@@ -1561,7 +1607,7 @@ impl BuildRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
LinkerFlavor::Unsupported => {
|
LinkerFlavor::Unsupported => {
|
||||||
return Err(anyhow::anyhow!("Unsupported platform for thin linking").into())
|
bail!("Unsupported platform for thin linking")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1791,7 +1837,7 @@ impl BuildRequest {
|
|||||||
// And then remove the rest of the rlibs
|
// And then remove the rest of the rlibs
|
||||||
//
|
//
|
||||||
// We also need to insert the -force_load flag to force the linker to load the archive
|
// We also need to insert the -force_load flag to force the linker to load the archive
|
||||||
let mut args = rustc_args.link_args.clone();
|
let mut args: Vec<_> = rustc_args.link_args.iter().skip(1).cloned().collect();
|
||||||
if let Some(last_object) = args.iter().rposition(|arg| arg.ends_with(".o")) {
|
if let Some(last_object) = args.iter().rposition(|arg| arg.ends_with(".o")) {
|
||||||
if archive_has_contents {
|
if archive_has_contents {
|
||||||
match self.linker_flavor() {
|
match self.linker_flavor() {
|
||||||
@@ -1879,21 +1925,33 @@ impl BuildRequest {
|
|||||||
args.remove(flavor_idx);
|
args.remove(flavor_idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set the output file
|
||||||
|
match self.triple.operating_system {
|
||||||
|
OperatingSystem::Windows => args.push(format!("/OUT:{}", exe.display())),
|
||||||
|
_ => args.extend(["-o".to_string(), exe.display().to_string()]),
|
||||||
|
}
|
||||||
|
|
||||||
// And now we can run the linker with our new args
|
// And now we can run the linker with our new args
|
||||||
let linker = self.select_linker()?;
|
let linker = self.select_linker()?;
|
||||||
|
|
||||||
tracing::trace!("Fat linking with args: {:?} {:#?}", linker, args);
|
tracing::trace!("Fat linking with args: {:?} {:#?}", linker, args);
|
||||||
tracing::trace!("Fat linking with env: {:#?}", rustc_args.envs);
|
tracing::trace!("Fat linking with env:");
|
||||||
|
for e in rustc_args.envs.iter() {
|
||||||
|
tracing::trace!(" {}={}", e.0, e.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle windows command files
|
||||||
|
let mut out_args = args.clone();
|
||||||
|
if cfg!(windows) {
|
||||||
|
let cmd_contents: String = out_args.iter().map(|f| format!("\"{f}\"")).join(" ");
|
||||||
|
std::fs::write(self.command_file.path(), cmd_contents)
|
||||||
|
.context("Failed to write linker command file")?;
|
||||||
|
out_args = vec![format!("@{}", self.command_file.path().display())];
|
||||||
|
}
|
||||||
|
|
||||||
// Run the linker directly!
|
// Run the linker directly!
|
||||||
let out_arg = match self.triple.operating_system {
|
|
||||||
OperatingSystem::Windows => vec![format!("/OUT:{}", exe.display())],
|
|
||||||
_ => vec!["-o".to_string(), exe.display().to_string()],
|
|
||||||
};
|
|
||||||
|
|
||||||
let res = Command::new(linker)
|
let res = Command::new(linker)
|
||||||
.args(args.iter().skip(1))
|
.args(out_args)
|
||||||
.args(out_arg)
|
|
||||||
.env_clear()
|
.env_clear()
|
||||||
.envs(rustc_args.envs.iter().map(|(k, v)| (k, v)))
|
.envs(rustc_args.envs.iter().map(|(k, v)| (k, v)))
|
||||||
.output()
|
.output()
|
||||||
@@ -2062,12 +2120,8 @@ impl BuildRequest {
|
|||||||
cmd.arg("-Crelocation-model=pic");
|
cmd.arg("-Crelocation-model=pic");
|
||||||
}
|
}
|
||||||
|
|
||||||
tracing::debug!("Direct rustc: {:#?}", cmd);
|
|
||||||
|
|
||||||
cmd.envs(rustc_args.envs.iter().cloned());
|
cmd.envs(rustc_args.envs.iter().cloned());
|
||||||
|
|
||||||
// tracing::trace!("Setting env vars: {:#?}", rustc_args.envs);
|
|
||||||
|
|
||||||
Ok(cmd)
|
Ok(cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2084,16 +2138,24 @@ impl BuildRequest {
|
|||||||
_ => {
|
_ => {
|
||||||
let mut cmd = Command::new("cargo");
|
let mut cmd = Command::new("cargo");
|
||||||
|
|
||||||
|
let env = self.cargo_build_env_vars(ctx)?;
|
||||||
|
let args = self.cargo_build_arguments(ctx);
|
||||||
|
|
||||||
|
tracing::trace!("Building with cargo rustc");
|
||||||
|
for e in env.iter() {
|
||||||
|
tracing::trace!(": {}={}", e.0, e.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
for a in args.iter() {
|
||||||
|
tracing::trace!(": {}", a);
|
||||||
|
}
|
||||||
|
|
||||||
cmd.arg("rustc")
|
cmd.arg("rustc")
|
||||||
.current_dir(self.crate_dir())
|
.current_dir(self.crate_dir())
|
||||||
.arg("--message-format")
|
.arg("--message-format")
|
||||||
.arg("json-diagnostic-rendered-ansi")
|
.arg("json-diagnostic-rendered-ansi")
|
||||||
.args(self.cargo_build_arguments(ctx))
|
.args(args)
|
||||||
.envs(
|
.envs(env.iter().map(|(k, v)| (k.as_ref(), v)));
|
||||||
self.cargo_build_env_vars(ctx)?
|
|
||||||
.iter()
|
|
||||||
.map(|(k, v)| (k.as_ref(), v)),
|
|
||||||
);
|
|
||||||
|
|
||||||
if ctx.mode == BuildMode::Fat {
|
if ctx.mode == BuildMode::Fat {
|
||||||
cmd.env(
|
cmd.env(
|
||||||
@@ -2109,8 +2171,6 @@ impl BuildRequest {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
tracing::debug!("Cargo: {:#?}", cmd);
|
|
||||||
|
|
||||||
Ok(cmd)
|
Ok(cmd)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2351,7 +2411,67 @@ impl BuildRequest {
|
|||||||
Ok(env_vars)
|
Ok(env_vars)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the environment variables required for building on Android.
|
||||||
|
///
|
||||||
|
/// This involves setting sysroots, CC, CXX, AR, and other environment variables along with
|
||||||
|
/// vars that cc-rs uses for its C/C++ compilation.
|
||||||
|
///
|
||||||
|
/// We pulled the environment setup from `cargo ndk` and attempt to mimic its behavior to retain
|
||||||
|
/// compatibility with existing crates that work with `cargo ndk`.
|
||||||
|
///
|
||||||
|
/// <https://github.com/bbqsrc/cargo-ndk/blob/1d1a6dc70a99b7f95bc71ed07bf893ef37966efc/src/cargo.rs#L97-L102>
|
||||||
|
///
|
||||||
|
/// cargo-ndk is MIT licensed.
|
||||||
|
///
|
||||||
|
/// <https://github.com/bbqsrc/cargo-ndk>
|
||||||
fn android_env_vars(&self) -> Result<Vec<(Cow<'static, str>, String)>> {
|
fn android_env_vars(&self) -> Result<Vec<(Cow<'static, str>, String)>> {
|
||||||
|
// Derived from getenv_with_target_prefixes in `cc` crate.
|
||||||
|
fn cc_env(var_base: &str, triple: &str) -> (String, Option<String>) {
|
||||||
|
#[inline]
|
||||||
|
fn env_var_with_key(key: String) -> Option<(String, String)> {
|
||||||
|
std::env::var(&key).map(|value| (key, value)).ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
let triple_u = triple.replace('-', "_");
|
||||||
|
let most_specific_key = format!("{}_{}", var_base, triple);
|
||||||
|
|
||||||
|
env_var_with_key(most_specific_key.to_string())
|
||||||
|
.or_else(|| env_var_with_key(format!("{}_{}", var_base, triple_u)))
|
||||||
|
.or_else(|| env_var_with_key(format!("TARGET_{}", var_base)))
|
||||||
|
.or_else(|| env_var_with_key(var_base.to_string()))
|
||||||
|
.map(|(key, value)| (key, Some(value)))
|
||||||
|
.unwrap_or_else(|| (most_specific_key, None))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cargo_env_target_cfg(triple: &str, key: &str) -> String {
|
||||||
|
format!("CARGO_TARGET_{}_{}", &triple.replace('-', "_"), key).to_uppercase()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clang_target(rust_target: &str, api_level: u8) -> String {
|
||||||
|
let target = match rust_target {
|
||||||
|
"arm-linux-androideabi" => "armv7a-linux-androideabi",
|
||||||
|
"armv7-linux-androideabi" => "armv7a-linux-androideabi",
|
||||||
|
_ => rust_target,
|
||||||
|
};
|
||||||
|
format!("--target={target}{api_level}")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sysroot_target(rust_target: &str) -> &str {
|
||||||
|
(match rust_target {
|
||||||
|
"armv7-linux-androideabi" => "arm-linux-androideabi",
|
||||||
|
_ => rust_target,
|
||||||
|
}) as _
|
||||||
|
}
|
||||||
|
fn rt_builtins(rust_target: &str) -> &str {
|
||||||
|
(match rust_target {
|
||||||
|
"armv7-linux-androideabi" => "arm",
|
||||||
|
"aarch64-linux-android" => "aarch64",
|
||||||
|
"i686-linux-android" => "i686",
|
||||||
|
"x86_64-linux-android" => "x86_64",
|
||||||
|
_ => rust_target,
|
||||||
|
}) as _
|
||||||
|
}
|
||||||
|
|
||||||
let mut env_vars: Vec<(Cow<'static, str>, String)> = vec![];
|
let mut env_vars: Vec<(Cow<'static, str>, String)> = vec![];
|
||||||
|
|
||||||
let tools = self.workspace.android_tools()?;
|
let tools = self.workspace.android_tools()?;
|
||||||
@@ -2361,7 +2481,7 @@ impl BuildRequest {
|
|||||||
let target_cc = tools.target_cc();
|
let target_cc = tools.target_cc();
|
||||||
let target_cxx = tools.target_cxx();
|
let target_cxx = tools.target_cxx();
|
||||||
let java_home = tools.java_home();
|
let java_home = tools.java_home();
|
||||||
let ndk = tools.ndk.clone();
|
let ndk_home = tools.ndk.clone();
|
||||||
tracing::debug!(
|
tracing::debug!(
|
||||||
r#"Using android:
|
r#"Using android:
|
||||||
min_sdk_version: {min_sdk_version}
|
min_sdk_version: {min_sdk_version}
|
||||||
@@ -2372,61 +2492,153 @@ impl BuildRequest {
|
|||||||
java_home: {java_home:?}
|
java_home: {java_home:?}
|
||||||
"#
|
"#
|
||||||
);
|
);
|
||||||
env_vars.push((
|
|
||||||
"ANDROID_NATIVE_API_LEVEL".into(),
|
|
||||||
min_sdk_version.to_string(),
|
|
||||||
));
|
|
||||||
env_vars.push(("TARGET_AR".into(), ar_path.display().to_string()));
|
|
||||||
env_vars.push(("TARGET_CC".into(), target_cc.display().to_string()));
|
|
||||||
env_vars.push(("TARGET_CXX".into(), target_cxx.display().to_string()));
|
|
||||||
env_vars.push((
|
|
||||||
format!(
|
|
||||||
"CARGO_TARGET_{}_LINKER",
|
|
||||||
self.triple
|
|
||||||
.to_string()
|
|
||||||
.to_ascii_uppercase()
|
|
||||||
.replace("-", "_")
|
|
||||||
)
|
|
||||||
.into(),
|
|
||||||
linker.display().to_string(),
|
|
||||||
));
|
|
||||||
env_vars.push(("ANDROID_NDK_ROOT".into(), ndk.display().to_string()));
|
|
||||||
|
|
||||||
if let Some(java_home) = java_home {
|
if let Some(java_home) = java_home {
|
||||||
tracing::debug!("Setting JAVA_HOME to {java_home:?}");
|
tracing::debug!("Setting JAVA_HOME to {java_home:?}");
|
||||||
env_vars.push(("JAVA_HOME".into(), java_home.display().to_string()));
|
env_vars.push(("JAVA_HOME".into(), java_home.display().to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the wry env vars - this is where wry will dump its kotlin files.
|
let triple = self.triple.to_string();
|
||||||
// Their setup is really annyoing and requires us to hardcode `dx` to specific versions of tao/wry.
|
|
||||||
env_vars.push(("WRY_ANDROID_PACKAGE".into(), "dev.dioxus.main".to_string()));
|
|
||||||
env_vars.push(("WRY_ANDROID_LIBRARY".into(), "dioxusmain".to_string()));
|
|
||||||
env_vars.push((
|
|
||||||
"WRY_ANDROID_KOTLIN_FILES_OUT_DIR".into(),
|
|
||||||
self.wry_android_kotlin_files_out_dir()
|
|
||||||
.display()
|
|
||||||
.to_string(),
|
|
||||||
));
|
|
||||||
|
|
||||||
// todo(jon): the guide for openssl recommends extending the path to include the tools dir
|
// Environment variables for the `cc` crate
|
||||||
// in practice I couldn't get this to work, but this might eventually become useful.
|
let (cc_key, _cc_value) = cc_env("CC", &triple);
|
||||||
//
|
let (cflags_key, cflags_value) = cc_env("CFLAGS", &triple);
|
||||||
// https://github.com/openssl/openssl/blob/master/NOTES-ANDROID.md#configuration
|
let (cxx_key, _cxx_value) = cc_env("CXX", &triple);
|
||||||
//
|
let (cxxflags_key, cxxflags_value) = cc_env("CXXFLAGS", &triple);
|
||||||
// They recommend a configuration like this:
|
let (ar_key, _ar_value) = cc_env("AR", &triple);
|
||||||
//
|
let (ranlib_key, _ranlib_value) = cc_env("RANLIB", &triple);
|
||||||
// // export ANDROID_NDK_ROOT=/home/whoever/Android/android-sdk/ndk/20.0.5594570
|
|
||||||
// PATH=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin:$ANDROID_NDK_ROOT/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin:$PATH
|
// Environment variables for cargo
|
||||||
// ./Configure android-arm64 -D__ANDROID_API__=29
|
let cargo_ar_key = cargo_env_target_cfg(&triple, "ar");
|
||||||
// make
|
let cargo_rust_flags_key = cargo_env_target_cfg(&triple, "rustflags");
|
||||||
//
|
let bindgen_clang_args_key =
|
||||||
// let tools_dir = arch.android_tools_dir(&ndk);
|
format!("BINDGEN_EXTRA_CLANG_ARGS_{}", &triple.replace('-', "_"));
|
||||||
// let extended_path = format!(
|
|
||||||
// "{}:{}",
|
let clang_target = clang_target(&self.triple.to_string(), min_sdk_version as _);
|
||||||
// tools_dir.display(),
|
let target_cc = tools.target_cc();
|
||||||
// std::env::var("PATH").unwrap_or_default()
|
let target_cflags = match cflags_value {
|
||||||
// );
|
Some(v) => format!("{clang_target} {v}"),
|
||||||
// env_vars.push(("PATH", extended_path));
|
None => clang_target.to_string(),
|
||||||
|
};
|
||||||
|
let target_cxx = tools.target_cxx();
|
||||||
|
let target_cxxflags = match cxxflags_value {
|
||||||
|
Some(v) => format!("{clang_target} {v}"),
|
||||||
|
None => clang_target.to_string(),
|
||||||
|
};
|
||||||
|
let cargo_ndk_sysroot_path_key = "CARGO_NDK_SYSROOT_PATH";
|
||||||
|
let cargo_ndk_sysroot_path = tools.sysroot();
|
||||||
|
let cargo_ndk_sysroot_target_key = "CARGO_NDK_SYSROOT_TARGET";
|
||||||
|
let cargo_ndk_sysroot_target = sysroot_target(&triple);
|
||||||
|
let cargo_ndk_sysroot_libs_path_key = "CARGO_NDK_SYSROOT_LIBS_PATH";
|
||||||
|
let cargo_ndk_sysroot_libs_path = cargo_ndk_sysroot_path
|
||||||
|
.join("usr")
|
||||||
|
.join("lib")
|
||||||
|
.join(cargo_ndk_sysroot_target);
|
||||||
|
let target_ar = tools.ar_path();
|
||||||
|
let target_ranlib = tools.ranlib();
|
||||||
|
let clang_folder = tools.clang_folder();
|
||||||
|
|
||||||
|
// choose the clang target with the highest version
|
||||||
|
// Should we filter for only numbers?
|
||||||
|
let clang_builtins_target = std::fs::read_dir(clang_folder)
|
||||||
|
.expect("Unable to get clang target directory")
|
||||||
|
.filter_map(|a| a.ok())
|
||||||
|
.max_by(|a, b| a.file_name().cmp(&b.file_name()))
|
||||||
|
.expect("Unable to get clang target")
|
||||||
|
.path();
|
||||||
|
let clang_rt = format!(
|
||||||
|
"-L{} -lstatic=clang_rt.builtins-{}-android",
|
||||||
|
clang_builtins_target.join("lib").join("linux").display(),
|
||||||
|
rt_builtins(&triple)
|
||||||
|
);
|
||||||
|
|
||||||
|
let extra_include: String = format!(
|
||||||
|
"{}/usr/include/{}",
|
||||||
|
&cargo_ndk_sysroot_path.display(),
|
||||||
|
&cargo_ndk_sysroot_target
|
||||||
|
);
|
||||||
|
|
||||||
|
let bindgen_args = format!(
|
||||||
|
"--sysroot={} -I{}",
|
||||||
|
&cargo_ndk_sysroot_path.display(),
|
||||||
|
extra_include
|
||||||
|
);
|
||||||
|
|
||||||
|
for env in [
|
||||||
|
(cc_key, target_cc.clone().into_os_string()),
|
||||||
|
(cflags_key, target_cflags.into()),
|
||||||
|
(cxx_key, target_cxx.into_os_string()),
|
||||||
|
(cxxflags_key, target_cxxflags.into()),
|
||||||
|
(ar_key, target_ar.clone().into()),
|
||||||
|
(ranlib_key, target_ranlib.into_os_string()),
|
||||||
|
(cargo_ar_key, target_ar.into_os_string()),
|
||||||
|
(
|
||||||
|
cargo_ndk_sysroot_path_key.to_string(),
|
||||||
|
cargo_ndk_sysroot_path.clone().into_os_string(),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
cargo_ndk_sysroot_libs_path_key.to_string(),
|
||||||
|
cargo_ndk_sysroot_libs_path.into_os_string(),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
cargo_ndk_sysroot_target_key.to_string(),
|
||||||
|
cargo_ndk_sysroot_target.into(),
|
||||||
|
),
|
||||||
|
(cargo_rust_flags_key, clang_rt.into()),
|
||||||
|
(bindgen_clang_args_key, bindgen_args.into()),
|
||||||
|
(
|
||||||
|
"ANDROID_NATIVE_API_LEVEL".to_string(),
|
||||||
|
min_sdk_version.to_string().into(),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
format!(
|
||||||
|
"CARGO_TARGET_{}_LINKER",
|
||||||
|
self.triple
|
||||||
|
.to_string()
|
||||||
|
.to_ascii_uppercase()
|
||||||
|
.replace("-", "_")
|
||||||
|
),
|
||||||
|
linker.into_os_string(),
|
||||||
|
),
|
||||||
|
("ANDROID_NDK_ROOT".to_string(), ndk_home.into_os_string()),
|
||||||
|
// Set the wry env vars - this is where wry will dump its kotlin files.
|
||||||
|
// Their setup is really annyoing and requires us to hardcode `dx` to specific versions of tao/wry.
|
||||||
|
(
|
||||||
|
"WRY_ANDROID_PACKAGE".to_string(),
|
||||||
|
"dev.dioxus.main".to_string().into(),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"WRY_ANDROID_LIBRARY".to_string(),
|
||||||
|
"dioxusmain".to_string().into(),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"WRY_ANDROID_KOTLIN_FILES_OUT_DIR".to_string(),
|
||||||
|
self.wry_android_kotlin_files_out_dir().into_os_string(),
|
||||||
|
),
|
||||||
|
// Found this through a comment related to bindgen using the wrong clang for cross compiles
|
||||||
|
//
|
||||||
|
// https://github.com/rust-lang/rust-bindgen/issues/2962#issuecomment-2438297124
|
||||||
|
//
|
||||||
|
// https://github.com/KyleMayes/clang-sys?tab=readme-ov-file#environment-variables
|
||||||
|
("CLANG_PATH".into(), target_cc.with_extension("exe").into()),
|
||||||
|
] {
|
||||||
|
env_vars.push((
|
||||||
|
env.0.into(),
|
||||||
|
env.1
|
||||||
|
.to_str()
|
||||||
|
.expect("Failed to convert env var value to string")
|
||||||
|
.to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if std::env::var("MSYSTEM").is_ok() || std::env::var("CYGWIN").is_ok() {
|
||||||
|
for var in env_vars.iter_mut() {
|
||||||
|
// Convert windows paths to unix-style paths
|
||||||
|
// This is a workaround for the fact that the `cc` crate expects unix-style paths
|
||||||
|
// and will fail if it encounters windows-style paths.
|
||||||
|
var.1 = var.1.replace('\\', "/");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(env_vars)
|
Ok(env_vars)
|
||||||
}
|
}
|
||||||
@@ -2465,7 +2677,7 @@ impl BuildRequest {
|
|||||||
|
|
||||||
let output = tokio::process::Command::new("cargo")
|
let output = tokio::process::Command::new("cargo")
|
||||||
.arg("+nightly")
|
.arg("+nightly")
|
||||||
.arg("build")
|
.arg("rustc")
|
||||||
.arg("--unit-graph")
|
.arg("--unit-graph")
|
||||||
.arg("-Z")
|
.arg("-Z")
|
||||||
.arg("unstable-options")
|
.arg("unstable-options")
|
||||||
@@ -2479,7 +2691,11 @@ impl BuildRequest {
|
|||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
if !output.status.success() {
|
if !output.status.success() {
|
||||||
return Err(anyhow::anyhow!("Failed to get unit count").into());
|
tracing::trace!(
|
||||||
|
"Failed to get unit count: {}",
|
||||||
|
String::from_utf8_lossy(&output.stderr)
|
||||||
|
);
|
||||||
|
bail!("Failed to get unit count");
|
||||||
}
|
}
|
||||||
|
|
||||||
let output_text = String::from_utf8(output.stdout).context("Failed to get unit count")?;
|
let output_text = String::from_utf8(output.stdout).context("Failed to get unit count")?;
|
||||||
@@ -2911,15 +3127,6 @@ impl BuildRequest {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the version of the wasm-bindgen crate if it exists
|
|
||||||
fn wasm_bindgen_version(&self) -> Option<String> {
|
|
||||||
self.workspace
|
|
||||||
.krates
|
|
||||||
.krates_by_name("wasm-bindgen")
|
|
||||||
.next()
|
|
||||||
.map(|krate| krate.krate.version.to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return the platforms that are enabled for the package
|
/// Return the platforms that are enabled for the package
|
||||||
///
|
///
|
||||||
/// Ideally only one platform is enabled but we need to be able to
|
/// Ideally only one platform is enabled but we need to be able to
|
||||||
@@ -3224,6 +3431,7 @@ impl BuildRequest {
|
|||||||
let post_bindgen_wasm = self.wasm_bindgen_wasm_output_file();
|
let post_bindgen_wasm = self.wasm_bindgen_wasm_output_file();
|
||||||
let should_bundle_split: bool = self.wasm_split;
|
let should_bundle_split: bool = self.wasm_split;
|
||||||
let bindgen_version = self
|
let bindgen_version = self
|
||||||
|
.workspace
|
||||||
.wasm_bindgen_version()
|
.wasm_bindgen_version()
|
||||||
.expect("this should have been checked by tool verification");
|
.expect("this should have been checked by tool verification");
|
||||||
|
|
||||||
@@ -3291,10 +3499,7 @@ impl BuildRequest {
|
|||||||
ctx.status_splitting_bundle();
|
ctx.status_splitting_bundle();
|
||||||
|
|
||||||
if !will_wasm_opt {
|
if !will_wasm_opt {
|
||||||
return Err(anyhow::anyhow!(
|
bail!("Bundle splitting should automatically enable wasm-opt, but it was not enabled.");
|
||||||
"Bundle splitting should automatically enable wasm-opt, but it was not enabled."
|
|
||||||
)
|
|
||||||
.into());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the contents of these binaries since we need both of them
|
// Load the contents of these binaries since we need both of them
|
||||||
@@ -3549,7 +3754,7 @@ __wbg_init({{module_or_path: "/{}/{wasm_path}"}}).then((wasm) => {{
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
.map_err(|e| e.into()),
|
.map_err(|e| e.into()),
|
||||||
_ => Err(anyhow::anyhow!("Unsupported platform for Info.plist").into()),
|
_ => Err(anyhow::anyhow!("Unsupported platform for Info.plist")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3574,7 +3779,10 @@ __wbg_init({{module_or_path: "/{}/{wasm_path}"}}).then((wasm) => {{
|
|||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
if !output.status.success() {
|
if !output.status.success() {
|
||||||
return Err(anyhow::anyhow!("Failed to assemble apk: {output:?}").into());
|
bail!(
|
||||||
|
"Failed to assemble apk: {}",
|
||||||
|
String::from_utf8_lossy(&output.stderr)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3593,7 +3801,10 @@ __wbg_init({{module_or_path: "/{}/{wasm_path}"}}).then((wasm) => {{
|
|||||||
.context("Failed to run gradle bundleRelease")?;
|
.context("Failed to run gradle bundleRelease")?;
|
||||||
|
|
||||||
if !output.status.success() {
|
if !output.status.success() {
|
||||||
return Err(anyhow::anyhow!("Failed to bundleRelease: {output:?}").into());
|
bail!(
|
||||||
|
"Failed to bundleRelease: {}",
|
||||||
|
String::from_utf8_lossy(&output.stderr)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let app_release = self
|
let app_release = self
|
||||||
@@ -3688,7 +3899,7 @@ __wbg_init({{module_or_path: "/{}/{wasm_path}"}}).then((wasm) => {{
|
|||||||
});
|
});
|
||||||
|
|
||||||
if let Err(e) = success.as_ref() {
|
if let Err(e) = success.as_ref() {
|
||||||
return Err(format!("Failed to initialize build directory: {e}").into());
|
bail!("Failed to initialize build directory: {e}");
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -3816,15 +4027,16 @@ __wbg_init({{module_or_path: "/{}/{wasm_path}"}}).then((wasm) => {{
|
|||||||
|
|
||||||
// Ensure target is installed.
|
// Ensure target is installed.
|
||||||
if !self.workspace.has_wasm32_unknown_unknown() {
|
if !self.workspace.has_wasm32_unknown_unknown() {
|
||||||
return Err(Error::Other(anyhow::anyhow!(
|
bail!("Missing target wasm32-unknown-unknown.");
|
||||||
"Missing target wasm32-unknown-unknown."
|
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wasm bindgen
|
// Wasm bindgen
|
||||||
let krate_bindgen_version = self.wasm_bindgen_version().ok_or(anyhow::anyhow!(
|
let krate_bindgen_version =
|
||||||
"failed to detect wasm-bindgen version, unable to proceed"
|
self.workspace
|
||||||
))?;
|
.wasm_bindgen_version()
|
||||||
|
.ok_or(anyhow::anyhow!(
|
||||||
|
"failed to detect wasm-bindgen version, unable to proceed"
|
||||||
|
))?;
|
||||||
|
|
||||||
WasmBindgen::verify_install(&krate_bindgen_version).await?;
|
WasmBindgen::verify_install(&krate_bindgen_version).await?;
|
||||||
|
|
||||||
@@ -3884,9 +4096,9 @@ __wbg_init({{module_or_path: "/{}/{wasm_path}"}}).then((wasm) => {{
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(anyhow::anyhow!(
|
bail!(
|
||||||
"Android linker not found. Please set the `ANDROID_NDK_HOME` environment variable to the root of your NDK installation."
|
"Android linker not found at {linker:?}. Please set the `ANDROID_NDK_HOME` environment variable to the root of your NDK installation."
|
||||||
).into())
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ensure the right dependencies are installed for linux apps.
|
/// Ensure the right dependencies are installed for linux apps.
|
||||||
@@ -4347,4 +4559,20 @@ __wbg_init({{module_or_path: "/{}/{wasm_path}"}}).then((wasm) => {{
|
|||||||
.flat_map(|arg| ["--config".to_string(), arg])
|
.flat_map(|arg| ["--config".to_string(), arg])
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn prebuild(&self) -> Result<()> {
|
||||||
|
if self.platform == Platform::Server {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the tailwind build before bundling anything else
|
||||||
|
crate::TailwindCli::run_once(
|
||||||
|
self.package_manifest_dir(),
|
||||||
|
self.config.application.tailwind_input.clone(),
|
||||||
|
self.config.application.tailwind_output.clone(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -170,6 +170,13 @@ impl AndroidTools {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn sysroot(&self) -> PathBuf {
|
||||||
|
// The sysroot is usually located in the NDK under:
|
||||||
|
// "~/Library/Android/sdk/ndk/25.2.9519653/toolchains/llvm/prebuilt/darwin-x86_64/sysroot"
|
||||||
|
// or similar, depending on the platform.
|
||||||
|
self.android_tools_dir().parent().unwrap().join("sysroot")
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn sdk(&self) -> PathBuf {
|
pub(crate) fn sdk(&self) -> PathBuf {
|
||||||
// /Users/jonathankelley/Library/Android/sdk/ndk/25.2/... (25.2 is the ndk here)
|
// /Users/jonathankelley/Library/Android/sdk/ndk/25.2/... (25.2 is the ndk here)
|
||||||
// /Users/jonathankelley/Library/Android/sdk/
|
// /Users/jonathankelley/Library/Android/sdk/
|
||||||
@@ -187,6 +194,21 @@ impl AndroidTools {
|
|||||||
24
|
24
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn clang_folder(&self) -> PathBuf {
|
||||||
|
// The clang folder is usually located in the NDK under:
|
||||||
|
// "~/Library/Android/sdk/ndk/25.2.9519653/toolchains/llvm/prebuilt/darwin-x86_64/lib/clang/<version>"
|
||||||
|
// or similar, depending on the platform.
|
||||||
|
self.android_tools_dir()
|
||||||
|
.parent()
|
||||||
|
.unwrap()
|
||||||
|
.join("lib")
|
||||||
|
.join("clang")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn ranlib(&self) -> PathBuf {
|
||||||
|
self.android_tools_dir().join("llvm-ranlib")
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn ar_path(&self) -> PathBuf {
|
pub(crate) fn ar_path(&self) -> PathBuf {
|
||||||
self.android_tools_dir().join("llvm-ar")
|
self.android_tools_dir().join("llvm-ar")
|
||||||
}
|
}
|
||||||
@@ -261,6 +283,24 @@ impl AndroidTools {
|
|||||||
|
|
||||||
triple
|
triple
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn libcpp_shared(&self, triple: &Triple) -> PathBuf {
|
||||||
|
// The libc++_shared.so is usually located in the sysroot under:
|
||||||
|
// "~/Library/Android/sdk/ndk/25.2.9519653/toolchains/llvm/prebuilt/darwin-x86_64/sysroot/usr/lib/<arch>/libc++_shared.so"
|
||||||
|
// or similar, depending on the platform.
|
||||||
|
self.sysroot()
|
||||||
|
.join("usr")
|
||||||
|
.join("lib")
|
||||||
|
.join(Self::sysroot_target(&triple.to_string()))
|
||||||
|
.join("libc++_shared.so")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn sysroot_target(rust_target: &str) -> &str {
|
||||||
|
(match rust_target {
|
||||||
|
"armv7-linux-androideabi" => "arm-linux-androideabi",
|
||||||
|
_ => rust_target,
|
||||||
|
}) as _
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn var_or_debug(name: &str) -> Option<PathBuf> {
|
fn var_or_debug(name: &str) -> Option<PathBuf> {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use super::{check::collect_rs_files, *};
|
use super::{check::collect_rs_files, *};
|
||||||
use crate::Workspace;
|
use crate::Workspace;
|
||||||
use anyhow::Context;
|
use anyhow::{bail, Context};
|
||||||
use dioxus_autofmt::{IndentOptions, IndentType};
|
use dioxus_autofmt::{IndentOptions, IndentType};
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use std::{borrow::Cow, fs, path::Path};
|
use std::{borrow::Cow, fs, path::Path};
|
||||||
@@ -54,11 +54,9 @@ impl Autoformat {
|
|||||||
} else if let Some(raw) = raw {
|
} else if let Some(raw) = raw {
|
||||||
// Format raw text.
|
// Format raw text.
|
||||||
let indent = indentation_for(".", self.split_line_attributes)?;
|
let indent = indentation_for(".", self.split_line_attributes)?;
|
||||||
if let Some(inner) = dioxus_autofmt::fmt_block(&raw, 0, indent) {
|
let formatted =
|
||||||
println!("{}", inner);
|
dioxus_autofmt::fmt_block(&raw, 0, indent).context("error formatting codeblock")?;
|
||||||
} else {
|
println!("{}", formatted);
|
||||||
return Err("error formatting codeblock".into());
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// Default to formatting the project.
|
// Default to formatting the project.
|
||||||
let crate_dir = if let Some(package) = self.package {
|
let crate_dir = if let Some(package) = self.package {
|
||||||
@@ -76,11 +74,8 @@ impl Autoformat {
|
|||||||
Cow::Borrowed(Path::new("."))
|
Cow::Borrowed(Path::new("."))
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Err(e) =
|
autoformat_project(check, split_line_attributes, format_rust_code, crate_dir)
|
||||||
autoformat_project(check, split_line_attributes, format_rust_code, crate_dir)
|
.context("error autoformatting project")?;
|
||||||
{
|
|
||||||
return Err(format!("error formatting project: {}", e).into());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(StructuredOutput::Success)
|
Ok(StructuredOutput::Success)
|
||||||
@@ -100,19 +95,15 @@ fn refactor_file(
|
|||||||
} else {
|
} else {
|
||||||
fs::read_to_string(&file)
|
fs::read_to_string(&file)
|
||||||
};
|
};
|
||||||
let Ok(mut s) = file_content else {
|
let mut s = file_content.context("failed to open file")?;
|
||||||
return Err(format!("failed to open file: {}", file_content.unwrap_err()).into());
|
|
||||||
};
|
|
||||||
|
|
||||||
if format_rust_code {
|
if format_rust_code {
|
||||||
s = format_rust(&s)?;
|
s = format_rust(&s)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let Ok(Ok(edits)) =
|
let parsed = syn::parse_file(&s).context("failed to parse file")?;
|
||||||
syn::parse_file(&s).map(|file| dioxus_autofmt::try_fmt_file(&s, &file, indent))
|
let edits =
|
||||||
else {
|
dioxus_autofmt::try_fmt_file(&s, &parsed, indent).context("failed to format file")?;
|
||||||
return Err(format!("failed to format file: {}", s).into());
|
|
||||||
};
|
|
||||||
|
|
||||||
let out = dioxus_autofmt::apply_formats(&s, edits);
|
let out = dioxus_autofmt::apply_formats(&s, edits);
|
||||||
|
|
||||||
@@ -135,18 +126,16 @@ fn format_file(
|
|||||||
let mut contents = fs::read_to_string(&path)?;
|
let mut contents = fs::read_to_string(&path)?;
|
||||||
let mut if_write = false;
|
let mut if_write = false;
|
||||||
if format_rust_code {
|
if format_rust_code {
|
||||||
let formatted = format_rust(&contents)
|
let formatted = format_rust(&contents).context("Syntax Error")?;
|
||||||
.map_err(|err| Error::Parse(format!("Syntax Error:\n{}", err)))?;
|
|
||||||
if contents != formatted {
|
if contents != formatted {
|
||||||
if_write = true;
|
if_write = true;
|
||||||
contents = formatted;
|
contents = formatted;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let parsed = syn::parse_file(&contents)
|
let parsed = syn::parse_file(&contents).context("Failed to parse file")?;
|
||||||
.map_err(|err| Error::Parse(format!("Failed to parse file: {}", err)))?;
|
|
||||||
let edits = dioxus_autofmt::try_fmt_file(&contents, &parsed, indent)
|
let edits = dioxus_autofmt::try_fmt_file(&contents, &parsed, indent)
|
||||||
.map_err(|err| Error::Parse(format!("Failed to format file: {}", err)))?;
|
.context("Failed to format file")?;
|
||||||
let len = edits.len();
|
let len = edits.len();
|
||||||
|
|
||||||
if !edits.is_empty() {
|
if !edits.is_empty() {
|
||||||
@@ -202,7 +191,7 @@ fn autoformat_project(
|
|||||||
let files_formatted: usize = counts.into_iter().flatten().sum();
|
let files_formatted: usize = counts.into_iter().flatten().sum();
|
||||||
|
|
||||||
if files_formatted > 0 && check {
|
if files_formatted > 0 && check {
|
||||||
return Err(format!("{} files needed formatting", files_formatted).into());
|
bail!("{} files needed formatting", files_formatted);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -220,9 +209,7 @@ fn indentation_for(
|
|||||||
.output()?;
|
.output()?;
|
||||||
|
|
||||||
if !out.status.success() {
|
if !out.status.success() {
|
||||||
return Err(Error::Runtime(format!(
|
bail!("cargo fmt failed with status: {out:?}");
|
||||||
"cargo fmt failed with status: {out:?}"
|
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let config = String::from_utf8_lossy(&out.stdout);
|
let config = String::from_utf8_lossy(&out.stdout);
|
||||||
@@ -232,18 +219,14 @@ fn indentation_for(
|
|||||||
.find(|line| line.starts_with("hard_tabs "))
|
.find(|line| line.starts_with("hard_tabs "))
|
||||||
.and_then(|line| line.split_once('='))
|
.and_then(|line| line.split_once('='))
|
||||||
.map(|(_, value)| value.trim() == "true")
|
.map(|(_, value)| value.trim() == "true")
|
||||||
.ok_or_else(|| {
|
.context("Could not find hard_tabs option in rustfmt config")?;
|
||||||
Error::Runtime("Could not find hard_tabs option in rustfmt config".into())
|
|
||||||
})?;
|
|
||||||
let tab_spaces = config
|
let tab_spaces = config
|
||||||
.lines()
|
.lines()
|
||||||
.find(|line| line.starts_with("tab_spaces "))
|
.find(|line| line.starts_with("tab_spaces "))
|
||||||
.and_then(|line| line.split_once('='))
|
.and_then(|line| line.split_once('='))
|
||||||
.map(|(_, value)| value.trim().parse::<usize>())
|
.map(|(_, value)| value.trim().parse::<usize>())
|
||||||
.ok_or_else(|| Error::Runtime("Could not find tab_spaces option in rustfmt config".into()))?
|
.context("Could not find tab_spaces option in rustfmt config")?
|
||||||
.map_err(|_| {
|
.context("Could not parse tab_spaces option in rustfmt config")?;
|
||||||
Error::Runtime("Could not parse tab_spaces option in rustfmt config".into())
|
|
||||||
})?;
|
|
||||||
|
|
||||||
Ok(IndentOptions::new(
|
Ok(IndentOptions::new(
|
||||||
if hard_tabs {
|
if hard_tabs {
|
||||||
@@ -258,7 +241,9 @@ fn indentation_for(
|
|||||||
|
|
||||||
/// Format rust code using prettyplease
|
/// Format rust code using prettyplease
|
||||||
fn format_rust(input: &str) -> Result<String> {
|
fn format_rust(input: &str) -> Result<String> {
|
||||||
let syntax_tree = syn::parse_file(input).map_err(format_syn_error)?;
|
let syntax_tree = syn::parse_file(input)
|
||||||
|
.map_err(format_syn_error)
|
||||||
|
.context("Failed to parse Rust syntax")?;
|
||||||
let output = prettyplease::unparse(&syntax_tree);
|
let output = prettyplease::unparse(&syntax_tree);
|
||||||
Ok(output)
|
Ok(output)
|
||||||
}
|
}
|
||||||
@@ -267,10 +252,7 @@ fn format_syn_error(err: syn::Error) -> Error {
|
|||||||
let start = err.span().start();
|
let start = err.span().start();
|
||||||
let line = start.line;
|
let line = start.line;
|
||||||
let column = start.column;
|
let column = start.column;
|
||||||
Error::Parse(format!(
|
anyhow::anyhow!("Syntax Error in line {} column {}:\n{}", line, column, err)
|
||||||
"Syntax Error in line {} column {}:\n{}",
|
|
||||||
line, column, err
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ impl CommandWithPlatformOverrides<BuildArgs> {
|
|||||||
let ssg = self.shared.ssg;
|
let ssg = self.shared.ssg;
|
||||||
let targets = self.into_targets().await?;
|
let targets = self.into_targets().await?;
|
||||||
|
|
||||||
AppBuilder::start(&targets.client, BuildMode::Base)?
|
AppBuilder::started(&targets.client, BuildMode::Base)?
|
||||||
.finish_build()
|
.finish_build()
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
@@ -78,7 +78,7 @@ impl CommandWithPlatformOverrides<BuildArgs> {
|
|||||||
|
|
||||||
if let Some(server) = targets.server.as_ref() {
|
if let Some(server) = targets.server.as_ref() {
|
||||||
// If the server is present, we need to build it as well
|
// If the server is present, we need to build it as well
|
||||||
let mut server_build = AppBuilder::start(server, BuildMode::Base)?;
|
let mut server_build = AppBuilder::started(server, BuildMode::Base)?;
|
||||||
server_build.finish_build().await?;
|
server_build.finish_build().await?;
|
||||||
|
|
||||||
// Run SSG and cache static routes
|
// Run SSG and cache static routes
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use crate::{AppBuilder, BuildArgs, BuildMode, BuildRequest, Platform};
|
use crate::{AppBuilder, BuildArgs, BuildMode, BuildRequest, Platform};
|
||||||
use anyhow::{anyhow, Context};
|
use anyhow::{bail, Context};
|
||||||
use path_absolutize::Absolutize;
|
use path_absolutize::Absolutize;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use tauri_bundler::{BundleBinary, BundleSettings, PackageSettings, SettingsBuilder};
|
use tauri_bundler::{BundleBinary, BundleSettings, PackageSettings, SettingsBuilder};
|
||||||
@@ -39,7 +39,7 @@ impl Bundle {
|
|||||||
|
|
||||||
let BuildTargets { client, server } = self.args.into_targets().await?;
|
let BuildTargets { client, server } = self.args.into_targets().await?;
|
||||||
|
|
||||||
AppBuilder::start(&client, BuildMode::Base)?
|
AppBuilder::started(&client, BuildMode::Base)?
|
||||||
.finish_build()
|
.finish_build()
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
@@ -47,7 +47,7 @@ impl Bundle {
|
|||||||
|
|
||||||
if let Some(server) = server.as_ref() {
|
if let Some(server) = server.as_ref() {
|
||||||
// If the server is present, we need to build it as well
|
// If the server is present, we need to build it as well
|
||||||
AppBuilder::start(server, BuildMode::Base)?
|
AppBuilder::started(server, BuildMode::Base)?
|
||||||
.finish_build()
|
.finish_build()
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
@@ -167,10 +167,10 @@ impl Bundle {
|
|||||||
|
|
||||||
// Check if required fields are provided instead of failing silently.
|
// Check if required fields are provided instead of failing silently.
|
||||||
if bundle_settings.identifier.is_none() {
|
if bundle_settings.identifier.is_none() {
|
||||||
return Err(anyhow!("\n\nBundle identifier was not provided in `Dioxus.toml`. Add it as:\n\n[bundle]\nidentifier = \"com.mycompany\"\n\n").into());
|
bail!("\n\nBundle identifier was not provided in `Dioxus.toml`. Add it as:\n\n[bundle]\nidentifier = \"com.mycompany\"\n\n");
|
||||||
}
|
}
|
||||||
if bundle_settings.publisher.is_none() {
|
if bundle_settings.publisher.is_none() {
|
||||||
return Err(anyhow!("\n\nBundle publisher was not provided in `Dioxus.toml`. Add it as:\n\n[bundle]\npublisher = \"MyCompany\"\n\n").into());
|
bail!("\n\nBundle publisher was not provided in `Dioxus.toml`. Add it as:\n\n[bundle]\npublisher = \"MyCompany\"\n\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg!(windows) {
|
if cfg!(windows) {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::BuildRequest;
|
use crate::BuildRequest;
|
||||||
use anyhow::Context;
|
use anyhow::{anyhow, Context};
|
||||||
use futures_util::{stream::FuturesUnordered, StreamExt};
|
use futures_util::{stream::FuturesUnordered, StreamExt};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
@@ -118,8 +118,8 @@ async fn check_files_and_report(files_to_check: Vec<PathBuf>) -> Result<()> {
|
|||||||
tracing::info!("No issues found.");
|
tracing::info!("No issues found.");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
1 => Err("1 issue found.".into()),
|
1 => Err(anyhow!("1 issue found.")),
|
||||||
_ => Err(format!("{} issues found.", total_issues).into()),
|
_ => Err(anyhow!("{} issues found.", total_issues)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
use super::*;
|
|
||||||
use crate::Result;
|
|
||||||
|
|
||||||
/// Clean build artifacts.
|
|
||||||
///
|
|
||||||
/// Simply runs `cargo clean`
|
|
||||||
#[derive(Clone, Debug, Parser)]
|
|
||||||
pub(crate) struct Clean {}
|
|
||||||
|
|
||||||
impl Clean {
|
|
||||||
/// todo(jon): we should add a config option that just wipes target/dx and target/dioxus-client instead of doing a full clean
|
|
||||||
pub(crate) async fn clean(self) -> Result<StructuredOutput> {
|
|
||||||
let output = tokio::process::Command::new("cargo")
|
|
||||||
.arg("clean")
|
|
||||||
.output()
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
if !output.status.success() {
|
|
||||||
return Err(anyhow::anyhow!("Cargo clean failed.").into());
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(StructuredOutput::Success)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::TraceSrc;
|
use crate::TraceSrc;
|
||||||
|
use anyhow::{bail, Context};
|
||||||
use cargo_generate::{GenerateArgs, TemplatePath, Vcs};
|
use cargo_generate::{GenerateArgs, TemplatePath, Vcs};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
@@ -147,9 +148,9 @@ pub(crate) fn name_from_path(path: &Path) -> Result<String> {
|
|||||||
.absolutize()?
|
.absolutize()?
|
||||||
.to_path_buf()
|
.to_path_buf()
|
||||||
.file_name()
|
.file_name()
|
||||||
.ok_or("Current path does not include directory name".to_string())?
|
.context("Current path does not include directory name".to_string())?
|
||||||
.to_str()
|
.to_str()
|
||||||
.ok_or("Current directory name is not a valid UTF-8 string".to_string())?
|
.context("Current directory name is not a valid UTF-8 string".to_string())?
|
||||||
.to_string())
|
.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,10 +168,7 @@ pub(crate) fn post_create(path: &Path, vcs: &Vcs) -> Result<()> {
|
|||||||
// Only 1 error means that CWD isn't a cargo project.
|
// Only 1 error means that CWD isn't a cargo project.
|
||||||
Err(cargo_metadata::Error::CargoMetadata { .. }) => None,
|
Err(cargo_metadata::Error::CargoMetadata { .. }) => None,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
return Err(Error::Other(anyhow::anyhow!(
|
anyhow::bail!("Couldn't retrieve cargo metadata: {:?}", err)
|
||||||
"Couldn't retrieve cargo metadata: {:?}",
|
|
||||||
err
|
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -286,9 +284,9 @@ pub(crate) async fn connectivity_check() -> Result<()> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(Error::Network(
|
bail!(
|
||||||
"Error connecting to template repository. Try cloning the template manually or add `dioxus` to a `cargo new` project.".to_string(),
|
"Error connecting to template repository. Try cloning the template manually or add `dioxus` to a `cargo new` project."
|
||||||
))
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo: re-enable these tests with better parallelization
|
// todo: re-enable these tests with better parallelization
|
||||||
|
|||||||
@@ -0,0 +1,212 @@
|
|||||||
|
use super::*;
|
||||||
|
use crate::{Result, Workspace};
|
||||||
|
use anyhow::{bail, Context};
|
||||||
|
use itertools::Itertools;
|
||||||
|
|
||||||
|
/// Perform a system analysis to verify the system install is working correctly.
|
||||||
|
#[derive(Clone, Debug, Parser)]
|
||||||
|
pub(crate) struct Doctor {}
|
||||||
|
|
||||||
|
impl Doctor {
|
||||||
|
pub async fn doctor(self) -> Result<StructuredOutput> {
|
||||||
|
let Ok(workspace) = Workspace::current().await else {
|
||||||
|
bail!("dx doctor must be run within a cargo workspace!")
|
||||||
|
};
|
||||||
|
|
||||||
|
let rustc_version = &workspace.rustc_version;
|
||||||
|
let rustc_sysroot = &workspace.sysroot.to_string_lossy();
|
||||||
|
let rustlib = workspace.sysroot.join("lib").join("rustlib");
|
||||||
|
|
||||||
|
// wasm-opt
|
||||||
|
let wasm_opt_location = crate::wasm_opt::installed_location();
|
||||||
|
let wasm_opt_message = match wasm_opt_location.clone() {
|
||||||
|
Some(path) => path.to_string_lossy().to_string(),
|
||||||
|
None => "not installed".into(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// wasm-bindgen
|
||||||
|
let wbg_version = workspace.wasm_bindgen_version();
|
||||||
|
let wbg_version_msg = match wbg_version.clone() {
|
||||||
|
Some(msg) => msg,
|
||||||
|
None => "not required".to_string(),
|
||||||
|
};
|
||||||
|
let wasm_bindgen_location = match wbg_version {
|
||||||
|
Some(vers) => match crate::wasm_bindgen::WasmBindgen::new(&vers).get_binary_path() {
|
||||||
|
Ok(path) => path.to_string_lossy().to_string(),
|
||||||
|
Err(err) => err.to_string().lines().join(""),
|
||||||
|
},
|
||||||
|
None => "not required".to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// extensions
|
||||||
|
fn has_dioxus_ext(editor_dir: &str) -> anyhow::Result<PathBuf> {
|
||||||
|
let home = dirs::home_dir().context("no home dir")?;
|
||||||
|
let exts = home.join(editor_dir).join("extensions");
|
||||||
|
for dir in exts.read_dir()?.flatten() {
|
||||||
|
if dir
|
||||||
|
.file_name()
|
||||||
|
.to_string_lossy()
|
||||||
|
.contains("dioxuslabs.dioxus-")
|
||||||
|
{
|
||||||
|
return Ok(dir.path());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bail!("not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Editors
|
||||||
|
let vscode_ext = has_dioxus_ext(".vscode");
|
||||||
|
let vscode_ext_msg = match vscode_ext.as_ref() {
|
||||||
|
Ok(path) => path.to_string_lossy().to_string(),
|
||||||
|
Err(_) => "not found".to_string(),
|
||||||
|
};
|
||||||
|
let cursor_ext = has_dioxus_ext(".cursor");
|
||||||
|
let cursor_ext_msg = match cursor_ext.as_ref() {
|
||||||
|
Ok(path) => path.to_string_lossy().to_string(),
|
||||||
|
Err(_) => "not found".to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Tailwind
|
||||||
|
let mut tailwindcss = "not found".to_string();
|
||||||
|
if let Ok(path) = crate::tailwind::TailwindCli::v3().get_binary_path() {
|
||||||
|
tailwindcss = path.display().to_string();
|
||||||
|
}
|
||||||
|
if let Ok(path) = crate::tailwind::TailwindCli::v4().get_binary_path() {
|
||||||
|
tailwindcss = path.display().to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut adb = "not found".to_string();
|
||||||
|
let mut ndk = "not found".to_string();
|
||||||
|
let mut sdk = "not found".to_string();
|
||||||
|
let mut java_home = "not found".to_string();
|
||||||
|
let mut emulator = "not found".to_string();
|
||||||
|
if let Some(rf) = workspace.android_tools.as_deref() {
|
||||||
|
if rf.adb.exists() {
|
||||||
|
adb = rf.adb.display().to_string();
|
||||||
|
}
|
||||||
|
if rf.ndk.exists() {
|
||||||
|
ndk = rf.ndk.display().to_string();
|
||||||
|
}
|
||||||
|
if let Some(jh) = rf.java_home.as_ref() {
|
||||||
|
java_home = jh.display().to_string();
|
||||||
|
}
|
||||||
|
if rf.sdk().exists() {
|
||||||
|
sdk = rf.sdk().display().to_string();
|
||||||
|
}
|
||||||
|
if let Some(jh) = rf.java_home.as_ref() {
|
||||||
|
java_home = jh.display().to_string();
|
||||||
|
}
|
||||||
|
if rf.emulator().exists() {
|
||||||
|
emulator = rf.emulator().display().to_string();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut simulator_location = "not found".to_string();
|
||||||
|
let mut xcode_install = "not found".to_string();
|
||||||
|
if let Some(xcode) = workspace.xcode.as_ref() {
|
||||||
|
let sim_location = xcode.join("Applications").join("Simulator.app");
|
||||||
|
if sim_location.exists() {
|
||||||
|
simulator_location = sim_location.display().to_string();
|
||||||
|
}
|
||||||
|
if xcode.exists() {
|
||||||
|
xcode_install = xcode.display().to_string();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// toolchains
|
||||||
|
let mut has_wasm32_unknown_unknown = "❌";
|
||||||
|
let mut has_aarch64_android_linux = "❌";
|
||||||
|
let mut has_i686_linux_android = "❌";
|
||||||
|
let mut has_armv7_linux_androideabi = "❌";
|
||||||
|
let mut has_x86_64_android_linux = "❌";
|
||||||
|
let mut has_x86_64_apple_ios = "❌";
|
||||||
|
let mut has_aarch64_apple_ios = "❌";
|
||||||
|
let mut has_aarch64_apple_ios_sim = "❌";
|
||||||
|
let mut has_aarch64_apple_darwin = "❌";
|
||||||
|
if rustlib.join("wasm32-unknown-unknown").exists() {
|
||||||
|
has_wasm32_unknown_unknown = "✅";
|
||||||
|
}
|
||||||
|
if rustlib.join("aarch64-linux-android").exists() {
|
||||||
|
has_aarch64_android_linux = "✅";
|
||||||
|
}
|
||||||
|
if rustlib.join("i686-linux-android").exists() {
|
||||||
|
has_i686_linux_android = "✅";
|
||||||
|
}
|
||||||
|
if rustlib.join("armv7-linux-androideabi").exists() {
|
||||||
|
has_armv7_linux_androideabi = "✅";
|
||||||
|
}
|
||||||
|
if rustlib.join("x86_64-linux-android").exists() {
|
||||||
|
has_x86_64_android_linux = "✅";
|
||||||
|
}
|
||||||
|
if rustlib.join("x86_64-apple-ios").exists() {
|
||||||
|
has_x86_64_apple_ios = "✅";
|
||||||
|
}
|
||||||
|
if rustlib.join("aarch64-apple-ios").exists() {
|
||||||
|
has_aarch64_apple_ios = "✅";
|
||||||
|
}
|
||||||
|
if rustlib.join("aarch64-apple-ios-sim").exists() {
|
||||||
|
has_aarch64_apple_ios_sim = "✅";
|
||||||
|
}
|
||||||
|
if rustlib.join("aarch64-apple-darwin").exists() {
|
||||||
|
has_aarch64_apple_darwin = "✅";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Things to know
|
||||||
|
// - current rust version and rust-related things
|
||||||
|
// - installed toolchains
|
||||||
|
// -
|
||||||
|
use crate::styles::*;
|
||||||
|
println!(
|
||||||
|
r#"{LINK_STYLE}Setup{LINK_STYLE:#}
|
||||||
|
{GLOW_STYLE}Web{GLOW_STYLE:#}: wasm-bindgen, wasm-opt, and TailwindCSS are downloaded automatically
|
||||||
|
{GLOW_STYLE}iOS{GLOW_STYLE:#}: Install iOS SDK and developer tools and through XCode
|
||||||
|
{GLOW_STYLE}Android{GLOW_STYLE:#}: Install Android Studio, NDK, and then set ANDROID_HOME and ANDROID_NDK_HOME
|
||||||
|
{GLOW_STYLE}macOS{GLOW_STYLE:#}: all tools should be installed by default
|
||||||
|
{GLOW_STYLE}Windows{GLOW_STYLE:#}: install the webview2 binary
|
||||||
|
{GLOW_STYLE}Linux{GLOW_STYLE:#}: Install libwebkit2gtk-4.1-dev libgtk-3-dev libasound2-dev libudev-dev libayatana-appindicator3-dev libxdo-dev libglib2.0-dev
|
||||||
|
|
||||||
|
{LINK_STYLE}Rust{LINK_STYLE:#}
|
||||||
|
Rustc version: {HINT_STYLE}{rustc_version}{HINT_STYLE:#}
|
||||||
|
Rustc sysroot: {HINT_STYLE}{rustc_sysroot}{HINT_STYLE:#}
|
||||||
|
|
||||||
|
{LINK_STYLE}Devtools{LINK_STYLE:#}
|
||||||
|
VSCode Extension: {HINT_STYLE}{vscode_ext_msg}{HINT_STYLE:#}
|
||||||
|
Cursor Extension: {HINT_STYLE}{cursor_ext_msg}{HINT_STYLE:#}
|
||||||
|
TailwindCSS: {HINT_STYLE}{tailwindcss}{HINT_STYLE:#}
|
||||||
|
|
||||||
|
{LINK_STYLE}Web{LINK_STYLE:#}
|
||||||
|
wasm-opt: {HINT_STYLE}{wasm_opt_message}{HINT_STYLE:#}
|
||||||
|
wasm-bindgen: {HINT_STYLE}{wasm_bindgen_location}{HINT_STYLE:#}
|
||||||
|
wasm-bindgen version: {HINT_STYLE}{wbg_version_msg}{HINT_STYLE:#}
|
||||||
|
|
||||||
|
{LINK_STYLE}iOS{LINK_STYLE:#}
|
||||||
|
XCode: {HINT_STYLE}{xcode_install}{HINT_STYLE:#}
|
||||||
|
Simulator: {HINT_STYLE}{simulator_location}{HINT_STYLE:#}
|
||||||
|
|
||||||
|
{LINK_STYLE}Android{LINK_STYLE:#}
|
||||||
|
sdk: {HINT_STYLE}{sdk}{HINT_STYLE:#}
|
||||||
|
ndk: {HINT_STYLE}{ndk}{HINT_STYLE:#}
|
||||||
|
adb: {HINT_STYLE}{adb}{HINT_STYLE:#}
|
||||||
|
emulator: {HINT_STYLE}{emulator}{HINT_STYLE:#}
|
||||||
|
java_home: {HINT_STYLE}{java_home}{HINT_STYLE:#}
|
||||||
|
|
||||||
|
{LINK_STYLE}Toolchains{LINK_STYLE:#}
|
||||||
|
{HINT_STYLE}{has_wasm32_unknown_unknown}{HINT_STYLE:#} wasm32-unknown-unknown {HINT_STYLE}(web){HINT_STYLE:#}
|
||||||
|
{HINT_STYLE}{has_aarch64_android_linux}{HINT_STYLE:#} aarch64-android-linux {HINT_STYLE}(android){HINT_STYLE:#}
|
||||||
|
{HINT_STYLE}{has_i686_linux_android}{HINT_STYLE:#} i686-linux-android {HINT_STYLE}(android){HINT_STYLE:#}
|
||||||
|
{HINT_STYLE}{has_armv7_linux_androideabi}{HINT_STYLE:#} armv7-linux-androideabi {HINT_STYLE}(android){HINT_STYLE:#}
|
||||||
|
{HINT_STYLE}{has_x86_64_android_linux}{HINT_STYLE:#} x86_64-android-linux {HINT_STYLE}(android){HINT_STYLE:#}
|
||||||
|
{HINT_STYLE}{has_x86_64_apple_ios}{HINT_STYLE:#} x86_64-apple-ios {HINT_STYLE}(iOS){HINT_STYLE:#}
|
||||||
|
{HINT_STYLE}{has_aarch64_apple_ios}{HINT_STYLE:#} aarch64-apple-ios {HINT_STYLE}(iOS){HINT_STYLE:#}
|
||||||
|
{HINT_STYLE}{has_aarch64_apple_ios_sim}{HINT_STYLE:#} aarch64-apple-ios-sim {HINT_STYLE}(iOS){HINT_STYLE:#}
|
||||||
|
{HINT_STYLE}{has_aarch64_apple_darwin}{HINT_STYLE:#} aarch64-apple-darwin {HINT_STYLE}(iOS){HINT_STYLE:#}
|
||||||
|
|
||||||
|
Get help: {LINK_STYLE}https://discord.gg/XgGxMSkvUM{LINK_STYLE:#}
|
||||||
|
More info: {LINK_STYLE}https://dioxuslabs.com/learn/0.6/{LINK_STYLE:#}
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(StructuredOutput::Success)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,9 +3,9 @@ pub(crate) mod build;
|
|||||||
pub(crate) mod build_assets;
|
pub(crate) mod build_assets;
|
||||||
pub(crate) mod bundle;
|
pub(crate) mod bundle;
|
||||||
pub(crate) mod check;
|
pub(crate) mod check;
|
||||||
pub(crate) mod clean;
|
|
||||||
pub(crate) mod config;
|
pub(crate) mod config;
|
||||||
pub(crate) mod create;
|
pub(crate) mod create;
|
||||||
|
pub(crate) mod doctor;
|
||||||
pub(crate) mod init;
|
pub(crate) mod init;
|
||||||
pub(crate) mod link;
|
pub(crate) mod link;
|
||||||
pub(crate) mod platform_override;
|
pub(crate) mod platform_override;
|
||||||
@@ -75,9 +75,9 @@ pub(crate) enum Commands {
|
|||||||
#[clap(name = "init")]
|
#[clap(name = "init")]
|
||||||
Init(init::Init),
|
Init(init::Init),
|
||||||
|
|
||||||
/// Clean output artifacts.
|
/// Diagnose installed tools and system configuration.
|
||||||
#[clap(name = "clean")]
|
#[clap(name = "doctor")]
|
||||||
Clean(clean::Clean),
|
Doctor(doctor::Doctor),
|
||||||
|
|
||||||
/// Translate a source file into Dioxus code.
|
/// Translate a source file into Dioxus code.
|
||||||
#[clap(name = "translate")]
|
#[clap(name = "translate")]
|
||||||
@@ -121,7 +121,6 @@ impl Display for Commands {
|
|||||||
Commands::Serve(_) => write!(f, "serve"),
|
Commands::Serve(_) => write!(f, "serve"),
|
||||||
Commands::New(_) => write!(f, "create"),
|
Commands::New(_) => write!(f, "create"),
|
||||||
Commands::Init(_) => write!(f, "init"),
|
Commands::Init(_) => write!(f, "init"),
|
||||||
Commands::Clean(_) => write!(f, "clean"),
|
|
||||||
Commands::Config(_) => write!(f, "config"),
|
Commands::Config(_) => write!(f, "config"),
|
||||||
Commands::Autoformat(_) => write!(f, "fmt"),
|
Commands::Autoformat(_) => write!(f, "fmt"),
|
||||||
Commands::Check(_) => write!(f, "check"),
|
Commands::Check(_) => write!(f, "check"),
|
||||||
@@ -129,6 +128,7 @@ impl Display for Commands {
|
|||||||
Commands::Run(_) => write!(f, "run"),
|
Commands::Run(_) => write!(f, "run"),
|
||||||
Commands::SelfUpdate(_) => write!(f, "self-update"),
|
Commands::SelfUpdate(_) => write!(f, "self-update"),
|
||||||
Commands::Tools(_) => write!(f, "tools"),
|
Commands::Tools(_) => write!(f, "tools"),
|
||||||
|
Commands::Doctor(_) => write!(f, "doctor"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -169,4 +169,5 @@ pub mod styles {
|
|||||||
pub(crate) const NOTE_STYLE: Style = AnsiColor::Green.on_default();
|
pub(crate) const NOTE_STYLE: Style = AnsiColor::Green.on_default();
|
||||||
pub(crate) const LINK_STYLE: Style = AnsiColor::Blue.on_default();
|
pub(crate) const LINK_STYLE: Style = AnsiColor::Blue.on_default();
|
||||||
pub(crate) const ERROR_STYLE: Style = AnsiColor::Red.on_default();
|
pub(crate) const ERROR_STYLE: Style = AnsiColor::Red.on_default();
|
||||||
|
pub(crate) const HINT_STYLE: Style = clap::builder::styling::Ansi256Color(244).on_default();
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
serve::{AppServer, ServeUpdate, WebServer},
|
serve::{AppServer, ServeUpdate, WebServer},
|
||||||
BuilderUpdate, Error, Platform, Result,
|
BuilderUpdate, Platform, Result,
|
||||||
};
|
};
|
||||||
|
use anyhow::bail;
|
||||||
use dioxus_dx_wire_format::BuildStage;
|
use dioxus_dx_wire_format::BuildStage;
|
||||||
|
|
||||||
/// Run the project with the given arguments
|
/// Run the project with the given arguments
|
||||||
@@ -29,9 +30,11 @@ impl RunArgs {
|
|||||||
self.args.hot_reload = Some(false);
|
self.args.hot_reload = Some(false);
|
||||||
self.args.watch = Some(false);
|
self.args.watch = Some(false);
|
||||||
|
|
||||||
let mut builder = AppServer::start(self.args).await?;
|
let mut builder = AppServer::new(self.args).await?;
|
||||||
let mut devserver = WebServer::start(&builder)?;
|
let mut devserver = WebServer::start(&builder)?;
|
||||||
|
|
||||||
|
builder.initialize();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let msg = tokio::select! {
|
let msg = tokio::select! {
|
||||||
msg = builder.wait() => msg,
|
msg = builder.wait() => msg,
|
||||||
@@ -109,15 +112,11 @@ impl RunArgs {
|
|||||||
}
|
}
|
||||||
BuildStage::Failed => {
|
BuildStage::Failed => {
|
||||||
tracing::error!("[{platform}] Build failed");
|
tracing::error!("[{platform}] Build failed");
|
||||||
return Err(Error::Cargo(format!(
|
bail!("Build failed for platform: {platform}");
|
||||||
"Build failed for platform: {platform}"
|
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
BuildStage::Aborted => {
|
BuildStage::Aborted => {
|
||||||
tracing::error!("[{platform}] Build aborted");
|
tracing::error!("[{platform}] Build aborted");
|
||||||
return Err(Error::Cargo(format!(
|
bail!("Build aborted for platform: {platform}");
|
||||||
"Build aborted for platform: {platform}"
|
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
@@ -125,7 +124,7 @@ impl RunArgs {
|
|||||||
print!("{}", message);
|
print!("{}", message);
|
||||||
}
|
}
|
||||||
BuilderUpdate::BuildFailed { err } => {
|
BuilderUpdate::BuildFailed { err } => {
|
||||||
tracing::error!("Build failed: {}", err);
|
tracing::error!("❌ Build failed: {}", err);
|
||||||
return Err(err);
|
return Err(err);
|
||||||
}
|
}
|
||||||
BuilderUpdate::StdoutReceived { msg } => {
|
BuilderUpdate::StdoutReceived { msg } => {
|
||||||
@@ -139,9 +138,7 @@ impl RunArgs {
|
|||||||
tracing::error!(
|
tracing::error!(
|
||||||
"Application [{platform}] exited with error: {status}"
|
"Application [{platform}] exited with error: {status}"
|
||||||
);
|
);
|
||||||
return Err(Error::Runtime(format!(
|
bail!("Application [{platform}] exited with error: {status}");
|
||||||
"Application [{platform}] exited with error: {status}"
|
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -181,7 +181,7 @@ impl ServeArgs {
|
|||||||
})
|
})
|
||||||
.unwrap_or_else(|| format!("dx serve panicked: {as_str}"));
|
.unwrap_or_else(|| format!("dx serve panicked: {as_str}"));
|
||||||
|
|
||||||
Err(crate::error::Error::CapturedPanic(message))
|
Err(anyhow::anyhow!(message))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::{Result, StructuredOutput};
|
use crate::{Result, StructuredOutput};
|
||||||
|
use anyhow::bail;
|
||||||
use dioxus_rsx::{BodyNode, CallBody, TemplateBody};
|
use dioxus_rsx::{BodyNode, CallBody, TemplateBody};
|
||||||
|
|
||||||
/// Translate some source file into Dioxus code
|
/// Translate some source file into Dioxus code
|
||||||
@@ -108,7 +109,7 @@ fn determine_input(file: Option<String>, raw: Option<String>) -> Result<String>
|
|||||||
|
|
||||||
// Make sure not both are specified
|
// Make sure not both are specified
|
||||||
if file.is_some() && raw.is_some() {
|
if file.is_some() && raw.is_some() {
|
||||||
return Err("Only one of --file or --raw should be specified.".into());
|
bail!("Only one of --file or --raw should be specified.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(raw) = raw {
|
if let Some(raw) = raw {
|
||||||
@@ -121,7 +122,7 @@ fn determine_input(file: Option<String>, raw: Option<String>) -> Result<String>
|
|||||||
|
|
||||||
// If neither exist, we try to read from stdin
|
// If neither exist, we try to read from stdin
|
||||||
if std::io::stdin().is_terminal() {
|
if std::io::stdin().is_terminal() {
|
||||||
return Err(anyhow::anyhow!("No input file, source, or stdin to translate from.").into());
|
bail!("No input file, source, or stdin to translate from.");
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut buffer = String::new();
|
let mut buffer = String::new();
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::{Result, Workspace};
|
use crate::{Result, Workspace};
|
||||||
use anyhow::Context;
|
use anyhow::{bail, Context};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use self_update::cargo_crate_version;
|
use self_update::cargo_crate_version;
|
||||||
|
|
||||||
@@ -93,7 +93,7 @@ impl SelfUpdate {
|
|||||||
} else if cfg!(target_arch = "aarch64") {
|
} else if cfg!(target_arch = "aarch64") {
|
||||||
"aarch64"
|
"aarch64"
|
||||||
} else {
|
} else {
|
||||||
return Err(Error::Unique("Unsupported architecture".to_string()));
|
bail!("Unsupported architecture");
|
||||||
};
|
};
|
||||||
|
|
||||||
let cur_os = if cfg!(target_os = "windows") {
|
let cur_os = if cfg!(target_os = "windows") {
|
||||||
@@ -103,7 +103,7 @@ impl SelfUpdate {
|
|||||||
} else if cfg!(target_os = "macos") {
|
} else if cfg!(target_os = "macos") {
|
||||||
"darwin"
|
"darwin"
|
||||||
} else {
|
} else {
|
||||||
return Err(Error::Unique("Unsupported OS".to_string()));
|
bail!("Unsupported OS");
|
||||||
};
|
};
|
||||||
|
|
||||||
let zip_ext = "zip";
|
let zip_ext = "zip";
|
||||||
@@ -118,7 +118,7 @@ impl SelfUpdate {
|
|||||||
&& a.name.contains(cur_arch)
|
&& a.name.contains(cur_arch)
|
||||||
&& a.name.ends_with(zip_ext)
|
&& a.name.ends_with(zip_ext)
|
||||||
})
|
})
|
||||||
.ok_or_else(|| Error::Unique("No suitable release found found".to_string()))?;
|
.context("No suitable asset found")?;
|
||||||
|
|
||||||
let install_dir = Workspace::dioxus_home_dir().join("self-update");
|
let install_dir = Workspace::dioxus_home_dir().join("self-update");
|
||||||
std::fs::create_dir_all(&install_dir).context("Failed to create install directory")?;
|
std::fs::create_dir_all(&install_dir).context("Failed to create install directory")?;
|
||||||
@@ -161,10 +161,7 @@ impl SelfUpdate {
|
|||||||
|
|
||||||
let executable = install_dir.join("dx");
|
let executable = install_dir.join("dx");
|
||||||
if !executable.exists() {
|
if !executable.exists() {
|
||||||
return Err(Error::Unique(format!(
|
bail!("Executable not found in {}", install_dir.display());
|
||||||
"Executable not found in {}",
|
|
||||||
install_dir.display()
|
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tracing::info!(
|
tracing::info!(
|
||||||
|
|||||||
+25
-69
@@ -1,77 +1,33 @@
|
|||||||
use std::fmt::Debug;
|
|
||||||
use thiserror::Error as ThisError;
|
|
||||||
|
|
||||||
pub(crate) type Result<T, E = Error> = std::result::Result<T, E>;
|
pub(crate) type Result<T, E = Error> = std::result::Result<T, E>;
|
||||||
|
pub use anyhow::Error;
|
||||||
|
use itertools::Itertools;
|
||||||
|
|
||||||
#[derive(ThisError, Debug)]
|
pub fn log_stacktrace(err: &anyhow::Error, padding: usize) -> String {
|
||||||
pub(crate) enum Error {
|
let mut trace = format!("{err}",);
|
||||||
/// Used when errors need to propagate but are too unique to be typed
|
|
||||||
#[error("{0}")]
|
|
||||||
Unique(String),
|
|
||||||
|
|
||||||
#[error("I/O Error: {0}")]
|
for (idx, cause) in err.chain().enumerate().skip(1) {
|
||||||
IO(#[from] std::io::Error),
|
trace.push_str(&format!(
|
||||||
|
"\n{}{IDX_STYLE}{idx}{IDX_STYLE:#}: {}",
|
||||||
#[error("Format Error: {0}")]
|
" ".repeat(padding),
|
||||||
Format(#[from] std::fmt::Error),
|
cause
|
||||||
|
.to_string()
|
||||||
#[error("Format failed: {0}")]
|
.lines()
|
||||||
Parse(String),
|
.enumerate()
|
||||||
|
.map(|(idx, line)| {
|
||||||
#[error("Runtime Error: {0}")]
|
if idx == 0 {
|
||||||
Runtime(String),
|
line.to_string()
|
||||||
|
} else {
|
||||||
#[error("Cargo Error: {0}")]
|
format!("{}{}", " ".repeat(padding + 3), line)
|
||||||
Cargo(String),
|
}
|
||||||
|
})
|
||||||
#[error("Invalid proxy URL: {0}")]
|
.join("\n"),
|
||||||
InvalidProxy(#[from] hyper::http::uri::InvalidUri),
|
IDX_STYLE = crate::styles::GLOW_STYLE,
|
||||||
|
));
|
||||||
#[error("Establishing proxy: {0}")]
|
|
||||||
ProxySetup(String),
|
|
||||||
|
|
||||||
#[error("Bundling project: {0}")]
|
|
||||||
BundleFailed(#[from] tauri_bundler::Error),
|
|
||||||
|
|
||||||
#[error("Performing hotpatch: {0}")]
|
|
||||||
PatchingFailed(#[from] crate::build::PatchError),
|
|
||||||
|
|
||||||
#[error("Reading object file: {0}")]
|
|
||||||
ObjectReadFailed(#[from] object::Error),
|
|
||||||
|
|
||||||
#[error("{0}")]
|
|
||||||
CapturedPanic(String),
|
|
||||||
|
|
||||||
#[error("Rendering template error: {0}")]
|
|
||||||
TemplateParse(#[from] handlebars::RenderError),
|
|
||||||
|
|
||||||
#[error("Network connectivity error: {0}")]
|
|
||||||
Network(String),
|
|
||||||
|
|
||||||
#[error(transparent)]
|
|
||||||
Other(#[from] anyhow::Error),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&str> for Error {
|
|
||||||
fn from(s: &str) -> Self {
|
|
||||||
Error::Unique(s.to_string())
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl From<String> for Error {
|
if crate::VERBOSITY.get().unwrap().trace {
|
||||||
fn from(s: String) -> Self {
|
trace.push_str(&format!("\nBacktrace:\n{}", err.backtrace()));
|
||||||
Error::Unique(s)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl From<html_parser::Error> for Error {
|
trace
|
||||||
fn from(e: html_parser::Error) -> Self {
|
|
||||||
Self::Parse(e.to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<hyper::Error> for Error {
|
|
||||||
fn from(e: hyper::Error) -> Self {
|
|
||||||
Self::Runtime(e.to_string())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -56,13 +56,13 @@ async fn main() {
|
|||||||
Commands::Config(opts) => opts.config().await,
|
Commands::Config(opts) => opts.config().await,
|
||||||
Commands::Autoformat(opts) => opts.autoformat().await,
|
Commands::Autoformat(opts) => opts.autoformat().await,
|
||||||
Commands::Check(opts) => opts.check().await,
|
Commands::Check(opts) => opts.check().await,
|
||||||
Commands::Clean(opts) => opts.clean().await,
|
|
||||||
Commands::Build(opts) => opts.build().await,
|
Commands::Build(opts) => opts.build().await,
|
||||||
Commands::Serve(opts) => opts.serve().await,
|
Commands::Serve(opts) => opts.serve().await,
|
||||||
Commands::Bundle(opts) => opts.bundle().await,
|
Commands::Bundle(opts) => opts.bundle().await,
|
||||||
Commands::Run(opts) => opts.run().await,
|
Commands::Run(opts) => opts.run().await,
|
||||||
Commands::SelfUpdate(opts) => opts.self_update().await,
|
Commands::SelfUpdate(opts) => opts.self_update().await,
|
||||||
Commands::Tools(BuildTools::BuildAssets(opts)) => opts.run().await,
|
Commands::Tools(BuildTools::BuildAssets(opts)) => opts.run().await,
|
||||||
|
Commands::Doctor(opts) => opts.doctor().await,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Provide a structured output for third party tools that can consume the output of the CLI
|
// Provide a structured output for third party tools that can consume the output of the CLI
|
||||||
@@ -71,17 +71,18 @@ async fn main() {
|
|||||||
tracing::debug!(json = ?output);
|
tracing::debug!(json = ?output);
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
eprintln!(
|
|
||||||
"{ERROR_STYLE}failed:{ERROR_STYLE:#} {err}",
|
|
||||||
ERROR_STYLE = styles::ERROR_STYLE
|
|
||||||
);
|
|
||||||
|
|
||||||
tracing::error!(
|
tracing::error!(
|
||||||
json = ?StructuredOutput::Error {
|
json = ?StructuredOutput::Error {
|
||||||
message: format!("{err:?}"),
|
message: format!("{err:?}"),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
eprintln!(
|
||||||
|
"{ERROR_STYLE}Failed{ERROR_STYLE:#}: {}",
|
||||||
|
crate::error::log_stacktrace(&err, 1),
|
||||||
|
ERROR_STYLE = crate::styles::ERROR_STYLE,
|
||||||
|
);
|
||||||
|
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
styles::{GLOW_STYLE, LINK_STYLE},
|
styles::{GLOW_STYLE, LINK_STYLE},
|
||||||
AppBuilder, BuildId, BuildMode, BuilderUpdate, Error, Platform, Result, ServeArgs,
|
AppBuilder, BuildId, BuildMode, BuilderUpdate, Platform, Result, ServeArgs, TraceController,
|
||||||
TraceController,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
mod ansi_buffer;
|
mod ansi_buffer;
|
||||||
@@ -12,6 +11,7 @@ mod runner;
|
|||||||
mod server;
|
mod server;
|
||||||
mod update;
|
mod update;
|
||||||
|
|
||||||
|
use anyhow::bail;
|
||||||
use dioxus_dx_wire_format::BuildStage;
|
use dioxus_dx_wire_format::BuildStage;
|
||||||
pub(crate) use output::*;
|
pub(crate) use output::*;
|
||||||
pub(crate) use runner::*;
|
pub(crate) use runner::*;
|
||||||
@@ -41,7 +41,7 @@ pub(crate) use update::*;
|
|||||||
pub(crate) async fn serve_all(args: ServeArgs, tracer: &mut TraceController) -> Result<()> {
|
pub(crate) async fn serve_all(args: ServeArgs, tracer: &mut TraceController) -> Result<()> {
|
||||||
// Load the args into a plan, resolving all tooling, build dirs, arguments, decoding the multi-target, etc
|
// Load the args into a plan, resolving all tooling, build dirs, arguments, decoding the multi-target, etc
|
||||||
let exit_on_error = args.exit_on_error;
|
let exit_on_error = args.exit_on_error;
|
||||||
let mut builder = AppServer::start(args).await?;
|
let mut builder = AppServer::new(args).await?;
|
||||||
let mut devserver = WebServer::start(&builder)?;
|
let mut devserver = WebServer::start(&builder)?;
|
||||||
let mut screen = Output::start(builder.interactive).await?;
|
let mut screen = Output::start(builder.interactive).await?;
|
||||||
|
|
||||||
@@ -66,6 +66,8 @@ pub(crate) async fn serve_all(args: ServeArgs, tracer: &mut TraceController) ->
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
builder.initialize();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
// Draw the state of the server to the screen
|
// Draw the state of the server to the screen
|
||||||
screen.render(&builder, &devserver);
|
screen.render(&builder, &devserver);
|
||||||
@@ -145,18 +147,14 @@ pub(crate) async fn serve_all(args: ServeArgs, tracer: &mut TraceController) ->
|
|||||||
stage: BuildStage::Failed,
|
stage: BuildStage::Failed,
|
||||||
} => {
|
} => {
|
||||||
if exit_on_error {
|
if exit_on_error {
|
||||||
return Err(Error::Cargo(format!(
|
bail!("Build failed for platform: {platform}");
|
||||||
"Build failed for platform: {platform}"
|
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BuilderUpdate::Progress {
|
BuilderUpdate::Progress {
|
||||||
stage: BuildStage::Aborted,
|
stage: BuildStage::Aborted,
|
||||||
} => {
|
} => {
|
||||||
if exit_on_error {
|
if exit_on_error {
|
||||||
return Err(Error::Cargo(format!(
|
bail!("Build aborted for platform: {platform}");
|
||||||
"Build aborted for platform: {platform}"
|
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BuilderUpdate::Progress { .. } => {}
|
BuilderUpdate::Progress { .. } => {}
|
||||||
@@ -164,7 +162,12 @@ pub(crate) async fn serve_all(args: ServeArgs, tracer: &mut TraceController) ->
|
|||||||
screen.push_cargo_log(message);
|
screen.push_cargo_log(message);
|
||||||
}
|
}
|
||||||
BuilderUpdate::BuildFailed { err } => {
|
BuilderUpdate::BuildFailed { err } => {
|
||||||
tracing::error!("Build failed: {}", err);
|
tracing::error!(
|
||||||
|
"{ERROR_STYLE}Build failed{ERROR_STYLE:#}: {}",
|
||||||
|
crate::error::log_stacktrace(&err, 15),
|
||||||
|
ERROR_STYLE = crate::styles::ERROR_STYLE,
|
||||||
|
);
|
||||||
|
|
||||||
if exit_on_error {
|
if exit_on_error {
|
||||||
return Err(err);
|
return Err(err);
|
||||||
}
|
}
|
||||||
@@ -184,7 +187,9 @@ pub(crate) async fn serve_all(args: ServeArgs, tracer: &mut TraceController) ->
|
|||||||
Err(err) => {
|
Err(err) => {
|
||||||
tracing::error!("Failed to hot-patch app: {err}");
|
tracing::error!("Failed to hot-patch app: {err}");
|
||||||
|
|
||||||
if matches!(err, crate::Error::PatchingFailed(_)) {
|
if let Some(_patching) =
|
||||||
|
err.downcast_ref::<crate::build::PatchError>()
|
||||||
|
{
|
||||||
tracing::info!("Starting full rebuild: {err}");
|
tracing::info!("Starting full rebuild: {err}");
|
||||||
builder.full_rebuild().await;
|
builder.full_rebuild().await;
|
||||||
devserver.send_reload_start().await;
|
devserver.send_reload_start().await;
|
||||||
@@ -216,9 +221,7 @@ pub(crate) async fn serve_all(args: ServeArgs, tracer: &mut TraceController) ->
|
|||||||
} else {
|
} else {
|
||||||
tracing::error!("Application [{platform}] exited with error: {status}");
|
tracing::error!("Application [{platform}] exited with error: {status}");
|
||||||
if exit_on_error {
|
if exit_on_error {
|
||||||
return Err(Error::Runtime(format!(
|
bail!("Application [{platform}] exited with error: {status}");
|
||||||
"Application [{platform}] exited with error: {status}"
|
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -248,7 +251,10 @@ pub(crate) async fn serve_all(args: ServeArgs, tracer: &mut TraceController) ->
|
|||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
if let Err(err) = builder.open_all(&devserver, true).await {
|
if let Err(err) = builder.open_all(&devserver, true).await {
|
||||||
tracing::error!("Failed to open app: {err}")
|
tracing::error!(
|
||||||
|
"Failed to open app: {}",
|
||||||
|
crate::error::log_stacktrace(&err, 15)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -278,7 +284,7 @@ pub(crate) async fn serve_all(args: ServeArgs, tracer: &mut TraceController) ->
|
|||||||
_ = devserver.shutdown().await;
|
_ = devserver.shutdown().await;
|
||||||
|
|
||||||
match error {
|
match error {
|
||||||
Some(err) => return Err(anyhow::anyhow!("{}", err).into()),
|
Some(err) => return Err(err),
|
||||||
None => return Ok(()),
|
None => return Ok(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ use tracing::Level;
|
|||||||
use super::AppServer;
|
use super::AppServer;
|
||||||
|
|
||||||
const TICK_RATE_MS: u64 = 100;
|
const TICK_RATE_MS: u64 = 100;
|
||||||
const VIEWPORT_MAX_WIDTH: u16 = 100;
|
const VIEWPORT_MAX_WIDTH: u16 = 90;
|
||||||
const VIEWPORT_HEIGHT_SMALL: u16 = 5;
|
const VIEWPORT_HEIGHT_SMALL: u16 = 5;
|
||||||
const VIEWPORT_HEIGHT_BIG: u16 = 13;
|
const VIEWPORT_HEIGHT_BIG: u16 = 13;
|
||||||
|
|
||||||
@@ -204,7 +204,7 @@ impl Output {
|
|||||||
Ok(Some(update)) => return update,
|
Ok(Some(update)) => return update,
|
||||||
Err(ee) => {
|
Err(ee) => {
|
||||||
return ServeUpdate::Exit {
|
return ServeUpdate::Exit {
|
||||||
error: Some(Box::new(ee)),
|
error: Some(anyhow::anyhow!(ee)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(None) => {}
|
Ok(None) => {}
|
||||||
@@ -675,7 +675,7 @@ impl Output {
|
|||||||
if client.build.platform == Platform::Web {
|
if client.build.platform == Platform::Web {
|
||||||
"Serving at: ".gray()
|
"Serving at: ".gray()
|
||||||
} else {
|
} else {
|
||||||
"ServerFns at: ".gray()
|
"Server at: ".gray()
|
||||||
},
|
},
|
||||||
address,
|
address,
|
||||||
])),
|
])),
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use crate::config::WebProxyConfig;
|
|||||||
use crate::TraceSrc;
|
use crate::TraceSrc;
|
||||||
use crate::{Error, Result};
|
use crate::{Error, Result};
|
||||||
|
|
||||||
use anyhow::{anyhow, Context};
|
use anyhow::{bail, Context};
|
||||||
use axum::body::Body;
|
use axum::body::Body;
|
||||||
use axum::http::request::Parts;
|
use axum::http::request::Parts;
|
||||||
use axum::{body::Body as MyBody, response::IntoResponse};
|
use axum::{body::Body as MyBody, response::IntoResponse};
|
||||||
@@ -47,7 +47,7 @@ impl ProxyClient {
|
|||||||
self.inner
|
self.inner
|
||||||
.request(req)
|
.request(req)
|
||||||
.await
|
.await
|
||||||
.map_err(|err| crate::error::Error::Other(anyhow!(err)))
|
.context("Failed to send proxy request")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,11 +64,11 @@ pub(crate) fn add_proxy(mut router: Router, proxy: &WebProxyConfig) -> Result<Ro
|
|||||||
let trimmed_path = path.trim_start_matches('/');
|
let trimmed_path = path.trim_start_matches('/');
|
||||||
|
|
||||||
if trimmed_path.is_empty() {
|
if trimmed_path.is_empty() {
|
||||||
return Err(crate::Error::ProxySetup(format!(
|
bail!(
|
||||||
"Proxy backend URL must have a non-empty path, e.g. {}/api instead of {}",
|
"Proxy backend URL must have a non-empty path, e.g. {}/api instead of {}",
|
||||||
proxy.backend.trim_end_matches('/'),
|
proxy.backend.trim_end_matches('/'),
|
||||||
proxy.backend
|
proxy.backend
|
||||||
)));
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let method_router = proxy_to(url, false, handle_proxy_error);
|
let method_router = proxy_to(url, false, handle_proxy_error);
|
||||||
@@ -268,21 +268,4 @@ mod test {
|
|||||||
async fn add_proxy_trailing_slash() {
|
async fn add_proxy_trailing_slash() {
|
||||||
test_proxy_requests("/api/".to_string()).await;
|
test_proxy_requests("/api/".to_string()).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn add_proxy_empty_path() {
|
|
||||||
let config = WebProxyConfig {
|
|
||||||
backend: "http://localhost:8000".to_string(),
|
|
||||||
};
|
|
||||||
let router = super::add_proxy(Router::new(), &config);
|
|
||||||
match router.unwrap_err() {
|
|
||||||
crate::Error::ProxySetup(e) => {
|
|
||||||
assert_eq!(
|
|
||||||
e,
|
|
||||||
"Proxy backend URL must have a non-empty path, e.g. http://localhost:8000/api instead of http://localhost:8000"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
e => panic!("Unexpected error type: {}", e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
use super::{AppBuilder, ServeUpdate, WebServer};
|
use super::{AppBuilder, ServeUpdate, WebServer};
|
||||||
use crate::{
|
use crate::{
|
||||||
platform_override::CommandWithPlatformOverrides, BuildArtifacts, BuildId, BuildMode,
|
platform_override::CommandWithPlatformOverrides, BuildArtifacts, BuildId, BuildMode,
|
||||||
BuildTargets, BuilderUpdate, Error, HotpatchModuleCache, Platform, Result, ServeArgs,
|
BuildTargets, BuilderUpdate, HotpatchModuleCache, Platform, Result, ServeArgs, TailwindCli,
|
||||||
TailwindCli, TraceSrc, Workspace,
|
TraceSrc, Workspace,
|
||||||
};
|
};
|
||||||
use anyhow::Context;
|
use anyhow::{bail, Context};
|
||||||
use dioxus_core::internal::{
|
use dioxus_core::internal::{
|
||||||
HotReloadTemplateWithLocation, HotReloadedTemplate, TemplateGlobalKey,
|
HotReloadTemplateWithLocation, HotReloadedTemplate, TemplateGlobalKey,
|
||||||
};
|
};
|
||||||
@@ -92,7 +92,7 @@ pub(crate) struct CachedFile {
|
|||||||
|
|
||||||
impl AppServer {
|
impl AppServer {
|
||||||
/// Create the AppRunner and then initialize the filemap with the crate directory.
|
/// Create the AppRunner and then initialize the filemap with the crate directory.
|
||||||
pub(crate) async fn start(args: ServeArgs) -> Result<Self> {
|
pub(crate) async fn new(args: ServeArgs) -> Result<Self> {
|
||||||
let workspace = Workspace::current().await?;
|
let workspace = Workspace::current().await?;
|
||||||
|
|
||||||
// Resolve the simpler args
|
// Resolve the simpler args
|
||||||
@@ -115,7 +115,8 @@ impl AppServer {
|
|||||||
|
|
||||||
let open_browser = args
|
let open_browser = args
|
||||||
.open
|
.open
|
||||||
.unwrap_or_else(|| workspace.settings.always_open_browser.unwrap_or_default());
|
.unwrap_or_else(|| workspace.settings.always_open_browser.unwrap_or(true))
|
||||||
|
&& interactive;
|
||||||
|
|
||||||
let wsl_file_poll_interval = args
|
let wsl_file_poll_interval = args
|
||||||
.wsl_file_poll_interval
|
.wsl_file_poll_interval
|
||||||
@@ -161,15 +162,9 @@ impl AppServer {
|
|||||||
|
|
||||||
let watch_fs = args.watch.unwrap_or(true);
|
let watch_fs = args.watch.unwrap_or(true);
|
||||||
let use_hotpatch_engine = args.hot_patch;
|
let use_hotpatch_engine = args.hot_patch;
|
||||||
let build_mode = match use_hotpatch_engine {
|
|
||||||
true => BuildMode::Fat,
|
|
||||||
false => BuildMode::Base,
|
|
||||||
};
|
|
||||||
|
|
||||||
let client = AppBuilder::start(&client, build_mode.clone())?;
|
let client = AppBuilder::new(&client)?;
|
||||||
let server = server
|
let server = server.map(|server| AppBuilder::new(&server)).transpose()?;
|
||||||
.map(|server| AppBuilder::start(&server, build_mode))
|
|
||||||
.transpose()?;
|
|
||||||
|
|
||||||
let tw_watcher = TailwindCli::serve(
|
let tw_watcher = TailwindCli::serve(
|
||||||
client.build.package_manifest_dir(),
|
client.build.package_manifest_dir(),
|
||||||
@@ -229,6 +224,18 @@ impl AppServer {
|
|||||||
Ok(runner)
|
Ok(runner)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn initialize(&mut self) {
|
||||||
|
let build_mode = match self.use_hotpatch_engine {
|
||||||
|
true => BuildMode::Fat,
|
||||||
|
false => BuildMode::Base,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.client.start(build_mode.clone());
|
||||||
|
if let Some(server) = self.server.as_mut() {
|
||||||
|
server.start(build_mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) async fn rebuild_ssg(&mut self, devserver: &WebServer) {
|
pub(crate) async fn rebuild_ssg(&mut self, devserver: &WebServer) {
|
||||||
if self.client.stage != BuildStage::Success {
|
if self.client.stage != BuildStage::Success {
|
||||||
return;
|
return;
|
||||||
@@ -355,9 +362,10 @@ impl AppServer {
|
|||||||
for path in files {
|
for path in files {
|
||||||
// for various assets that might be linked in, we just try to hotreloading them forcefully
|
// for various assets that might be linked in, we just try to hotreloading them forcefully
|
||||||
// That is, unless they appear in an include! macro, in which case we need to a full rebuild....
|
// That is, unless they appear in an include! macro, in which case we need to a full rebuild....
|
||||||
let Some(ext) = path.extension().and_then(|v| v.to_str()) else {
|
let ext = path
|
||||||
continue;
|
.extension()
|
||||||
};
|
.and_then(|v| v.to_str())
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
// If it's an asset, we want to hotreload it
|
// If it's an asset, we want to hotreload it
|
||||||
// todo(jon): don't hardcode this here
|
// todo(jon): don't hardcode this here
|
||||||
@@ -458,6 +466,21 @@ impl AppServer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If it's not a rust file, then it might be depended on via include! or similar
|
||||||
|
if ext != "rs" {
|
||||||
|
if let Some(artifacts) = self.client.artifacts.as_ref() {
|
||||||
|
if artifacts.depinfo.files.contains(path) {
|
||||||
|
needs_full_rebuild = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the client is in a failed state, any changes to rsx should trigger a rebuild/hotpatch
|
||||||
|
if self.client.stage == BuildStage::Failed && !templates.is_empty() {
|
||||||
|
needs_full_rebuild = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo - we need to distinguish between hotpatchable rebuilds and true full rebuilds.
|
// todo - we need to distinguish between hotpatchable rebuilds and true full rebuilds.
|
||||||
@@ -685,7 +708,7 @@ impl AppServer {
|
|||||||
.hotpatch(res, cache)
|
.hotpatch(res, cache)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
_ => return Err(Error::Runtime("Invalid build id".into())),
|
_ => bail!("Invalid build id"),
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
if id == BuildId::CLIENT {
|
if id == BuildId::CLIENT {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use crate::{
|
|||||||
config::WebHttpsConfig, serve::ServeUpdate, BuildId, BuildStage, BuilderUpdate, Platform,
|
config::WebHttpsConfig, serve::ServeUpdate, BuildId, BuildStage, BuilderUpdate, Platform,
|
||||||
Result, TraceSrc,
|
Result, TraceSrc,
|
||||||
};
|
};
|
||||||
use anyhow::Context;
|
use anyhow::{bail, Context};
|
||||||
use axum::{
|
use axum::{
|
||||||
body::Body,
|
body::Body,
|
||||||
extract::{
|
extract::{
|
||||||
@@ -635,8 +635,7 @@ async fn get_rustls(web_config: &WebHttpsConfig) -> Result<(String, String)> {
|
|||||||
{
|
{
|
||||||
return Ok((cert, key));
|
return Ok((cert, key));
|
||||||
} else {
|
} else {
|
||||||
// missing cert or key
|
bail!("https is enabled but cert or key path is missing");
|
||||||
return Err("https is enabled but cert or key path is missing".into());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -682,7 +681,7 @@ async fn get_rustls(web_config: &WebHttpsConfig) -> Result<(String, String)> {
|
|||||||
tracing::error!(dx_src = ?TraceSrc::Dev, "An error occurred while generating mkcert certificates: {}", e.to_string())
|
tracing::error!(dx_src = ?TraceSrc::Dev, "An error occurred while generating mkcert certificates: {}", e.to_string())
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return Err("failed to generate mkcert certificates".into());
|
bail!("failed to generate mkcert certificates");
|
||||||
}
|
}
|
||||||
Ok(mut cmd) => {
|
Ok(mut cmd) => {
|
||||||
cmd.wait().await?;
|
cmd.wait().await?;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::{BuildId, BuilderUpdate, Platform, TraceMsg};
|
use crate::{BuildId, BuilderUpdate, Error, Platform, TraceMsg};
|
||||||
use axum::extract::ws::Message as WsMessage;
|
use axum::extract::ws::Message as WsMessage;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
@@ -44,6 +44,6 @@ pub(crate) enum ServeUpdate {
|
|||||||
},
|
},
|
||||||
|
|
||||||
Exit {
|
Exit {
|
||||||
error: Option<Box<dyn std::error::Error + Send + Sync>>,
|
error: Option<Error>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
use crate::{Result, TraceSrc};
|
use crate::{Result, TraceSrc};
|
||||||
|
use anyhow::bail;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::sync::LazyLock;
|
use std::sync::LazyLock;
|
||||||
use std::{fs, path::PathBuf, sync::Arc};
|
use std::{fs, path::PathBuf, sync::Arc};
|
||||||
@@ -89,16 +90,14 @@ impl CliSettings {
|
|||||||
?path,
|
?path,
|
||||||
"failed to create directories for settings file"
|
"failed to create directories for settings file"
|
||||||
);
|
);
|
||||||
return Err(
|
bail!("failed to create directories for settings file: {e}");
|
||||||
anyhow::anyhow!("failed to create directories for settings file: {e}").into(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write the data.
|
// Write the data.
|
||||||
let result = fs::write(&path, data.clone());
|
let result = fs::write(&path, data.clone());
|
||||||
if let Err(e) = result {
|
if let Err(e) = result {
|
||||||
error!(?data, ?path, "failed to save global cli settings");
|
error!(?data, ?path, "failed to save global cli settings");
|
||||||
return Err(anyhow::anyhow!("failed to save global cli settings: {e}").into());
|
bail!("failed to save global cli settings: {e}");
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -141,6 +141,7 @@ impl TailwindCli {
|
|||||||
.arg("--output")
|
.arg("--output")
|
||||||
.arg(output_path)
|
.arg(output_path)
|
||||||
.args(watch.then_some("--watch"))
|
.args(watch.then_some("--watch"))
|
||||||
|
.current_dir(manifest_dir)
|
||||||
.kill_on_drop(true)
|
.kill_on_drop(true)
|
||||||
.stdin(Stdio::piped())
|
.stdin(Stdio::piped())
|
||||||
.stdout(Stdio::null())
|
.stdout(Stdio::null())
|
||||||
@@ -150,7 +151,7 @@ impl TailwindCli {
|
|||||||
Ok(proc)
|
Ok(proc)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_binary_path(&self) -> anyhow::Result<PathBuf> {
|
pub fn get_binary_path(&self) -> anyhow::Result<PathBuf> {
|
||||||
if CliSettings::prefer_no_downloads() {
|
if CliSettings::prefer_no_downloads() {
|
||||||
which::which("tailwindcss").map_err(|_| anyhow!("Missing tailwindcss@{}", self.version))
|
which::which("tailwindcss").map_err(|_| anyhow!("Missing tailwindcss@{}", self.version))
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use flate2::read::GzDecoder;
|
|||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use tar::Archive;
|
use tar::Archive;
|
||||||
use tempfile::TempDir;
|
use tempfile::TempDir;
|
||||||
use tokio::{fs, process::Command};
|
use tokio::process::Command;
|
||||||
|
|
||||||
pub(crate) struct WasmBindgen {
|
pub(crate) struct WasmBindgen {
|
||||||
version: String,
|
version: String,
|
||||||
@@ -100,7 +100,7 @@ impl WasmBindgen {
|
|||||||
|
|
||||||
/// Run the bindgen command with the current settings
|
/// Run the bindgen command with the current settings
|
||||||
pub(crate) async fn run(&self) -> Result<std::process::Output> {
|
pub(crate) async fn run(&self) -> Result<std::process::Output> {
|
||||||
let binary = self.get_binary_path().await?;
|
let binary = self.get_binary_path()?;
|
||||||
|
|
||||||
let mut args = Vec::new();
|
let mut args = Vec::new();
|
||||||
|
|
||||||
@@ -253,7 +253,7 @@ impl WasmBindgen {
|
|||||||
})?;
|
})?;
|
||||||
|
|
||||||
// Get the final binary location.
|
// Get the final binary location.
|
||||||
let binary_path = self.get_binary_path().await?;
|
let binary_path = self.get_binary_path()?;
|
||||||
|
|
||||||
// Download then extract wasm-bindgen-cli.
|
// Download then extract wasm-bindgen-cli.
|
||||||
let bytes = reqwest::get(url).await?.bytes().await?;
|
let bytes = reqwest::get(url).await?.bytes().await?;
|
||||||
@@ -300,11 +300,10 @@ impl WasmBindgen {
|
|||||||
.output()
|
.output()
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
fs::copy(
|
std::fs::copy(
|
||||||
tempdir.path().join(self.downloaded_bin_name()),
|
tempdir.path().join(self.downloaded_bin_name()),
|
||||||
self.get_binary_path().await?,
|
self.get_binary_path()?,
|
||||||
)
|
)?;
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -336,11 +335,10 @@ impl WasmBindgen {
|
|||||||
tracing::info!("Copying into path: {}", tempdir.path().display());
|
tracing::info!("Copying into path: {}", tempdir.path().display());
|
||||||
|
|
||||||
// copy the wasm-bindgen out of the tempdir to the final location
|
// copy the wasm-bindgen out of the tempdir to the final location
|
||||||
fs::copy(
|
std::fs::copy(
|
||||||
tempdir.path().join("bin").join(self.downloaded_bin_name()),
|
tempdir.path().join("bin").join(self.downloaded_bin_name()),
|
||||||
self.get_binary_path().await?,
|
self.get_binary_path()?,
|
||||||
)
|
)
|
||||||
.await
|
|
||||||
.context("failed to copy wasm-bindgen binary")?;
|
.context("failed to copy wasm-bindgen binary")?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -352,7 +350,7 @@ impl WasmBindgen {
|
|||||||
self.version
|
self.version
|
||||||
);
|
);
|
||||||
|
|
||||||
let binary = self.get_binary_path().await?;
|
let binary = self.get_binary_path()?;
|
||||||
let output = Command::new(binary)
|
let output = Command::new(binary)
|
||||||
.args(["--version"])
|
.args(["--version"])
|
||||||
.output()
|
.output()
|
||||||
@@ -381,7 +379,7 @@ impl WasmBindgen {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let binary_name = self.installed_bin_name();
|
let binary_name = self.installed_bin_name();
|
||||||
let path = self.install_dir().await?.join(binary_name);
|
let path = self.install_dir()?.join(binary_name);
|
||||||
|
|
||||||
if !path.exists() {
|
if !path.exists() {
|
||||||
self.install().await?;
|
self.install().await?;
|
||||||
@@ -390,20 +388,20 @@ impl WasmBindgen {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_binary_path(&self) -> anyhow::Result<PathBuf> {
|
pub fn get_binary_path(&self) -> anyhow::Result<PathBuf> {
|
||||||
if CliSettings::prefer_no_downloads() {
|
if CliSettings::prefer_no_downloads() {
|
||||||
which::which("wasm-bindgen")
|
which::which("wasm-bindgen")
|
||||||
.map_err(|_| anyhow!("Missing wasm-bindgen-cli@{}", self.version))
|
.map_err(|_| anyhow!("Missing wasm-bindgen-cli@{}", self.version))
|
||||||
} else {
|
} else {
|
||||||
let installed_name = self.installed_bin_name();
|
let installed_name = self.installed_bin_name();
|
||||||
let install_dir = self.install_dir().await?;
|
let install_dir = self.install_dir()?;
|
||||||
Ok(install_dir.join(installed_name))
|
Ok(install_dir.join(installed_name))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn install_dir(&self) -> anyhow::Result<PathBuf> {
|
fn install_dir(&self) -> anyhow::Result<PathBuf> {
|
||||||
let bindgen_dir = Workspace::dioxus_home_dir().join("wasm-bindgen/");
|
let bindgen_dir = Workspace::dioxus_home_dir().join("wasm-bindgen/");
|
||||||
fs::create_dir_all(&bindgen_dir).await?;
|
std::fs::create_dir_all(&bindgen_dir)?;
|
||||||
Ok(bindgen_dir)
|
Ok(bindgen_dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -501,7 +499,7 @@ mod test {
|
|||||||
|
|
||||||
/// Helper to test that the installed binary actually exists.
|
/// Helper to test that the installed binary actually exists.
|
||||||
async fn verify_installation(binary: &WasmBindgen) {
|
async fn verify_installation(binary: &WasmBindgen) {
|
||||||
let path = binary.install_dir().await.unwrap();
|
let path = binary.install_dir().unwrap();
|
||||||
let name = binary.installed_bin_name();
|
let name = binary.installed_bin_name();
|
||||||
let binary_path = path.join(name);
|
let binary_path = path.join(name);
|
||||||
assert!(
|
assert!(
|
||||||
@@ -513,9 +511,9 @@ mod test {
|
|||||||
/// Delete the installed binary. The temp folder should be automatically deleted.
|
/// Delete the installed binary. The temp folder should be automatically deleted.
|
||||||
async fn reset_test() {
|
async fn reset_test() {
|
||||||
let binary = WasmBindgen::new(VERSION);
|
let binary = WasmBindgen::new(VERSION);
|
||||||
let path = binary.install_dir().await.unwrap();
|
let path = binary.install_dir().unwrap();
|
||||||
let name = binary.installed_bin_name();
|
let name = binary.installed_bin_name();
|
||||||
let binary_path = path.join(name);
|
let binary_path = path.join(name);
|
||||||
let _ = tokio::fs::remove_file(binary_path).await;
|
let _ = std::fs::remove_file(binary_path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
use crate::config::WasmOptLevel;
|
use crate::config::WasmOptLevel;
|
||||||
use crate::{CliSettings, Result, WasmOptConfig, Workspace};
|
use crate::{CliSettings, Result, WasmOptConfig, Workspace};
|
||||||
use anyhow::{anyhow, Context};
|
use anyhow::{anyhow, bail, Context};
|
||||||
use flate2::read::GzDecoder;
|
use flate2::read::GzDecoder;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use tar::Archive;
|
use tar::Archive;
|
||||||
@@ -16,8 +16,11 @@ pub async fn write_wasm(bytes: &[u8], output_path: &Path, cfg: &WasmOptConfig) -
|
|||||||
pub async fn optimize(input_path: &Path, output_path: &Path, cfg: &WasmOptConfig) -> Result<()> {
|
pub async fn optimize(input_path: &Path, output_path: &Path, cfg: &WasmOptConfig) -> Result<()> {
|
||||||
let wasm_opt = WasmOpt::new(input_path, output_path, cfg)
|
let wasm_opt = WasmOpt::new(input_path, output_path, cfg)
|
||||||
.await
|
.await
|
||||||
.inspect_err(|err| tracing::error!("Failed to create wasm-opt instance: {}", err))?;
|
.context("Failed to create wasm-opt instance")?;
|
||||||
wasm_opt.optimize().await?;
|
wasm_opt
|
||||||
|
.optimize()
|
||||||
|
.await
|
||||||
|
.context("Failed to run wasm-opt")?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -170,9 +173,7 @@ async fn find_latest_wasm_opt_download_url() -> anyhow::Result<String> {
|
|||||||
} else if cfg!(all(target_os = "macos", target_arch = "aarch64")) {
|
} else if cfg!(all(target_os = "macos", target_arch = "aarch64")) {
|
||||||
"arm64-macos"
|
"arm64-macos"
|
||||||
} else {
|
} else {
|
||||||
return Err(anyhow::anyhow!(
|
bail!("Unknown platform for wasm-opt installation. Please install wasm-opt manually from https://github.com/WebAssembly/binaryen/releases and add it to your PATH.");
|
||||||
"Unknown platform for wasm-opt installation. Please install wasm-opt manually from https://github.com/WebAssembly/binaryen/releases and add it to your PATH."
|
|
||||||
));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Find the first asset with a name that contains the platform string
|
// Find the first asset with a name that contains the platform string
|
||||||
@@ -184,12 +185,10 @@ async fn find_latest_wasm_opt_download_url() -> anyhow::Result<String> {
|
|||||||
.and_then(|name| name.as_str())
|
.and_then(|name| name.as_str())
|
||||||
.is_some_and(|name| name.contains(platform) && !name.ends_with("sha256"))
|
.is_some_and(|name| name.contains(platform) && !name.ends_with("sha256"))
|
||||||
})
|
})
|
||||||
.ok_or_else(|| {
|
.with_context(|| anyhow!(
|
||||||
anyhow::anyhow!(
|
"No suitable wasm-opt binary found for platform: {}. Please install wasm-opt manually from https://github.com/WebAssembly/binaryen/releases and add it to your PATH.",
|
||||||
"No suitable wasm-opt binary found for platform: {}. Please install wasm-opt manually from https://github.com/WebAssembly/binaryen/releases and add it to your PATH.",
|
platform
|
||||||
platform
|
))?;
|
||||||
)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
// Extract the download URL from the asset
|
// Extract the download URL from the asset
|
||||||
let download_url = asset
|
let download_url = asset
|
||||||
@@ -201,7 +200,7 @@ async fn find_latest_wasm_opt_download_url() -> anyhow::Result<String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get the path to the wasm-opt binary, downloading it if necessary
|
/// Get the path to the wasm-opt binary, downloading it if necessary
|
||||||
async fn get_binary_path() -> anyhow::Result<PathBuf> {
|
pub async fn get_binary_path() -> anyhow::Result<PathBuf> {
|
||||||
let install_dir = install_dir();
|
let install_dir = install_dir();
|
||||||
let install_path = installed_bin_path(&install_dir);
|
let install_path = installed_bin_path(&install_dir);
|
||||||
|
|
||||||
@@ -224,6 +223,25 @@ async fn get_binary_path() -> anyhow::Result<PathBuf> {
|
|||||||
Ok(install_path)
|
Ok(install_path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn installed_location() -> Option<PathBuf> {
|
||||||
|
let install_dir = install_dir();
|
||||||
|
let install_path = installed_bin_path(&install_dir);
|
||||||
|
|
||||||
|
if install_path.exists() {
|
||||||
|
return Some(install_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
if CliSettings::prefer_no_downloads() {
|
||||||
|
if let Ok(existing) = which::which("wasm-opt") {
|
||||||
|
return Some(existing);
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn install_dir() -> PathBuf {
|
fn install_dir() -> PathBuf {
|
||||||
Workspace::dioxus_home_dir().join("binaryen")
|
Workspace::dioxus_home_dir().join("binaryen")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use crate::styles::GLOW_STYLE;
|
|||||||
use crate::CliSettings;
|
use crate::CliSettings;
|
||||||
use crate::Result;
|
use crate::Result;
|
||||||
use crate::{config::DioxusConfig, AndroidTools};
|
use crate::{config::DioxusConfig, AndroidTools};
|
||||||
use anyhow::Context;
|
use anyhow::{bail, Context};
|
||||||
use ignore::gitignore::Gitignore;
|
use ignore::gitignore::Gitignore;
|
||||||
use krates::{semver::Version, KrateDetails, LockOptions};
|
use krates::{semver::Version, KrateDetails, LockOptions};
|
||||||
use krates::{Cmd, Krates, NodeId};
|
use krates::{Cmd, Krates, NodeId};
|
||||||
@@ -21,6 +21,7 @@ pub struct Workspace {
|
|||||||
pub(crate) ignore: Gitignore,
|
pub(crate) ignore: Gitignore,
|
||||||
pub(crate) cargo_toml: cargo_toml::Manifest,
|
pub(crate) cargo_toml: cargo_toml::Manifest,
|
||||||
pub(crate) android_tools: Option<Arc<AndroidTools>>,
|
pub(crate) android_tools: Option<Arc<AndroidTools>>,
|
||||||
|
pub(crate) xcode: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Workspace {
|
impl Workspace {
|
||||||
@@ -48,9 +49,7 @@ impl Workspace {
|
|||||||
|
|
||||||
let mut builder = krates::Builder::new();
|
let mut builder = krates::Builder::new();
|
||||||
builder.workspace(true);
|
builder.workspace(true);
|
||||||
let res = builder
|
let res = builder.build(cmd, |_| {})?;
|
||||||
.build(cmd, |_| {})
|
|
||||||
.context("Failed to run cargo metadata");
|
|
||||||
|
|
||||||
if !lock_options.offline {
|
if !lock_options.offline {
|
||||||
if let Ok(res) = std::env::var("SIMULATE_SLOW_NETWORK") {
|
if let Ok(res) = std::env::var("SIMULATE_SLOW_NETWORK") {
|
||||||
@@ -58,7 +57,7 @@ impl Workspace {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
res
|
Ok(res) as Result<Krates, krates::Error>
|
||||||
});
|
});
|
||||||
|
|
||||||
let spin_future = async move {
|
let spin_future = async move {
|
||||||
@@ -76,10 +75,14 @@ impl Workspace {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let krates = tokio::select! {
|
let krates = tokio::select! {
|
||||||
f = krates_future => f.context("failed to run cargo metadata")??,
|
f = krates_future => {
|
||||||
_ = spin_future => return Err(crate::Error::Network(
|
let res = f?;
|
||||||
"cargo metadata took too long to respond, try again with --offline".to_string(),
|
if let Err(krates::Error::Metadata(e)) = res {
|
||||||
)),
|
bail!("{e}");
|
||||||
|
}
|
||||||
|
res?
|
||||||
|
},
|
||||||
|
_ = spin_future => bail!("cargo metadata took too long to respond, try again with --offline"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let settings = CliSettings::global_or_default();
|
let settings = CliSettings::global_or_default();
|
||||||
@@ -107,6 +110,13 @@ impl Workspace {
|
|||||||
|
|
||||||
let android_tools = crate::build::get_android_tools();
|
let android_tools = crate::build::get_android_tools();
|
||||||
|
|
||||||
|
let xcode = Command::new("xcode-select")
|
||||||
|
.arg("-p")
|
||||||
|
.output()
|
||||||
|
.await
|
||||||
|
.ok()
|
||||||
|
.map(|s| String::from_utf8_lossy(&s.stdout).trim().to_string().into());
|
||||||
|
|
||||||
let workspace = Arc::new(Self {
|
let workspace = Arc::new(Self {
|
||||||
krates,
|
krates,
|
||||||
settings,
|
settings,
|
||||||
@@ -116,6 +126,7 @@ impl Workspace {
|
|||||||
ignore,
|
ignore,
|
||||||
cargo_toml,
|
cargo_toml,
|
||||||
android_tools,
|
android_tools,
|
||||||
|
xcode,
|
||||||
});
|
});
|
||||||
|
|
||||||
tracing::debug!(
|
tracing::debug!(
|
||||||
@@ -141,10 +152,10 @@ impl Workspace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn android_tools(&self) -> Result<Arc<AndroidTools>> {
|
pub fn android_tools(&self) -> Result<Arc<AndroidTools>> {
|
||||||
Ok(self
|
self
|
||||||
.android_tools
|
.android_tools
|
||||||
.clone()
|
.clone()
|
||||||
.context("Android not installed properly. Please set the `ANDROID_NDK_HOME` environment variable to the root of your NDK installation.")?)
|
.context("Android not installed properly. Please set the `ANDROID_NDK_HOME` environment variable to the root of your NDK installation.")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_release_profile(&self, profile: &str) -> bool {
|
pub fn is_release_profile(&self, profile: &str) -> bool {
|
||||||
@@ -195,7 +206,10 @@ impl Workspace {
|
|||||||
let max = dioxus_versions.iter().max().unwrap();
|
let max = dioxus_versions.iter().max().unwrap();
|
||||||
|
|
||||||
// If the minimum dioxus version is greater than the current cli version, warn the user
|
// If the minimum dioxus version is greater than the current cli version, warn the user
|
||||||
if min > &dx_semver || max < &dx_semver {
|
if min > &dx_semver
|
||||||
|
|| max < &dx_semver
|
||||||
|
|| dioxus_versions.iter().any(|f| f.pre != dx_semver.pre)
|
||||||
|
{
|
||||||
tracing::error!(
|
tracing::error!(
|
||||||
r#"🚫dx and dioxus versions are incompatible!
|
r#"🚫dx and dioxus versions are incompatible!
|
||||||
• dx version: {dx_semver}
|
• dx version: {dx_semver}
|
||||||
@@ -249,6 +263,14 @@ impl Workspace {
|
|||||||
self.gcc_ld_dir().join("wasm-ld")
|
self.gcc_ld_dir().join("wasm-ld")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the version of the wasm-bindgen crate if it exists
|
||||||
|
pub fn wasm_bindgen_version(&self) -> Option<String> {
|
||||||
|
self.krates
|
||||||
|
.krates_by_name("wasm-bindgen")
|
||||||
|
.next()
|
||||||
|
.map(|krate| krate.krate.version.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
// wasm-ld: ./rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/wasm-ld
|
// wasm-ld: ./rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/wasm-ld
|
||||||
// rust-lld: ./rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/rust-lld
|
// rust-lld: ./rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/rust-lld
|
||||||
fn gcc_ld_dir(&self) -> PathBuf {
|
fn gcc_ld_dir(&self) -> PathBuf {
|
||||||
@@ -377,7 +399,7 @@ impl Workspace {
|
|||||||
|
|
||||||
toml::from_str::<DioxusConfig>(&std::fs::read_to_string(&dioxus_conf_file)?)
|
toml::from_str::<DioxusConfig>(&std::fs::read_to_string(&dioxus_conf_file)?)
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
anyhow::anyhow!("Failed to parse Dioxus.toml at {dioxus_conf_file:?}: {err}").into()
|
anyhow::anyhow!("Failed to parse Dioxus.toml at {dioxus_conf_file:?}: {err}")
|
||||||
})
|
})
|
||||||
.map(Some)
|
.map(Some)
|
||||||
}
|
}
|
||||||
@@ -459,24 +481,18 @@ impl Workspace {
|
|||||||
}
|
}
|
||||||
None
|
None
|
||||||
})
|
})
|
||||||
.ok_or_else(|| {
|
.context("Failed to find directory containing Cargo.toml")
|
||||||
crate::Error::Cargo("Failed to find directory containing Cargo.toml".to_string())
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the properly canonicalized path to the dx executable, used for linking and wrapping rustc
|
/// Returns the properly canonicalized path to the dx executable, used for linking and wrapping rustc
|
||||||
pub(crate) fn path_to_dx() -> Result<PathBuf> {
|
pub(crate) fn path_to_dx() -> Result<PathBuf> {
|
||||||
Ok(
|
dunce::canonicalize(std::env::current_exe().context("Failed to find dx")?)
|
||||||
dunce::canonicalize(std::env::current_exe().context("Failed to find dx")?)
|
.context("Failed to find dx")
|
||||||
.context("Failed to find dx")?,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the path to the dioxus home directory, used to install tools and other things
|
/// Returns the path to the dioxus home directory, used to install tools and other things
|
||||||
pub(crate) fn dioxus_home_dir() -> PathBuf {
|
pub(crate) fn dioxus_home_dir() -> PathBuf {
|
||||||
dirs::data_local_dir()
|
dirs::home_dir().unwrap().join(".dioxus")
|
||||||
.map(|f| f.join("dioxus/"))
|
|
||||||
.unwrap_or_else(|| dirs::home_dir().unwrap().join(".dioxus"))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
//! Used by the hot-reloading engine and other libraries to provide higher quality dependency analysis
|
//! Used by the hot-reloading engine and other libraries to provide higher quality dependency analysis
|
||||||
//! for the user's project.
|
//! for the user's project.
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
@@ -18,7 +18,7 @@ pub enum DepInfoParseError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct RustcDepInfo {
|
pub struct RustcDepInfo {
|
||||||
/// The list of files that the main target in the dep-info file depends on.
|
/// The list of files that the main target in the dep-info file depends on.
|
||||||
pub files: Vec<PathBuf>,
|
pub files: Vec<PathBuf>,
|
||||||
@@ -42,6 +42,12 @@ impl std::str::FromStr for RustcDepInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl RustcDepInfo {
|
impl RustcDepInfo {
|
||||||
|
pub fn from_file(path: &Path) -> Result<RustcDepInfo, DepInfoParseError> {
|
||||||
|
let contents =
|
||||||
|
std::fs::read_to_string(path).map_err(|_| DepInfoParseError::MalformedInput)?;
|
||||||
|
RustcDepInfo::new(&contents)
|
||||||
|
}
|
||||||
|
|
||||||
/// Parse the `.d` dep-info file generated by rustc.
|
/// Parse the `.d` dep-info file generated by rustc.
|
||||||
pub fn new(contents: &str) -> Result<RustcDepInfo, DepInfoParseError> {
|
pub fn new(contents: &str) -> Result<RustcDepInfo, DepInfoParseError> {
|
||||||
let mut ret = RustcDepInfo::default();
|
let mut ret = RustcDepInfo::default();
|
||||||
|
|||||||
@@ -79,6 +79,11 @@ ndk = { version = "0.9.0" }
|
|||||||
ndk-sys = { version = "0.6.0" }
|
ndk-sys = { version = "0.6.0" }
|
||||||
ndk-context = { version = "0.1.1" }
|
ndk-context = { version = "0.1.1" }
|
||||||
|
|
||||||
|
# The `openssl` dependency generally does not know how to cross-compile for Android. To make the lives
|
||||||
|
# of our users easier, we automatically enable the `openssl` feature when building for Android.
|
||||||
|
# Feature are additive, so users will need to "subtract" this feature if they do not want it.
|
||||||
|
openssl = { version = "0.10", features = ["vendored"], optional = true }
|
||||||
|
|
||||||
# use native tls on other platforms
|
# use native tls on other platforms
|
||||||
[target.'cfg(not(target_os = "android"))'.dependencies]
|
[target.'cfg(not(target_os = "android"))'.dependencies]
|
||||||
tungstenite = { workspace = true, features = ["native-tls"] }
|
tungstenite = { workspace = true, features = ["native-tls"] }
|
||||||
@@ -92,11 +97,12 @@ objc = "0.2.7"
|
|||||||
lazy-js-bundle = { workspace = true }
|
lazy-js-bundle = { workspace = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["tokio_runtime", "transparent", "devtools"]
|
default = ["tokio_runtime", "transparent", "devtools", "openssl-vendored-android"]
|
||||||
tokio_runtime = ["dep:tokio"]
|
tokio_runtime = ["dep:tokio"]
|
||||||
fullscreen = ["wry/fullscreen"]
|
fullscreen = ["wry/fullscreen"]
|
||||||
devtools = ["wry/devtools", "dep:dioxus-devtools", "dioxus-signals"]
|
devtools = ["wry/devtools", "dep:dioxus-devtools", "dioxus-signals"]
|
||||||
transparent = ["wry/transparent"]
|
transparent = ["wry/transparent"]
|
||||||
|
openssl-vendored-android = ["openssl"]
|
||||||
gnu = []
|
gnu = []
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ mod file_upload;
|
|||||||
mod hooks;
|
mod hooks;
|
||||||
mod ipc;
|
mod ipc;
|
||||||
mod menubar;
|
mod menubar;
|
||||||
|
mod mobile;
|
||||||
mod protocol;
|
mod protocol;
|
||||||
mod query;
|
mod query;
|
||||||
mod shortcut;
|
mod shortcut;
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
/// Expose the `Java_dev_dioxus_main_WryActivity_create` function to the JNI layer.
|
||||||
|
/// We hardcode these to have a single trampoline for host Java code to call into.
|
||||||
|
///
|
||||||
|
/// This saves us from having to plumb the top-level package name all the way down into
|
||||||
|
/// this file. This is better for modularity (ie just call dioxus' main to run the app) as
|
||||||
|
/// well as cache thrashing since this crate doesn't rely on external env vars.
|
||||||
|
///
|
||||||
|
/// The CLI is expecting to find `dev.dioxus.main` in the final library. If you find a need to
|
||||||
|
/// change this, you'll need to change the CLI as well.
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
#[no_mangle]
|
||||||
|
#[inline(never)]
|
||||||
|
pub extern "C" fn start_app() {
|
||||||
|
use crate::Config;
|
||||||
|
use dioxus_core::{Element, VirtualDom};
|
||||||
|
use std::any::Any;
|
||||||
|
|
||||||
|
tao::android_binding!(dev_dioxus, main, WryActivity, wry::android_setup, root, tao);
|
||||||
|
wry::android_binding!(dev_dioxus, main, wry);
|
||||||
|
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
fn root() {
|
||||||
|
fn stop_unwind<F: FnOnce() -> T, T>(f: F) -> T {
|
||||||
|
match std::panic::catch_unwind(std::panic::AssertUnwindSafe(f)) {
|
||||||
|
Ok(t) => t,
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("attempt to unwind out of `rust` with err: {:?}", err);
|
||||||
|
std::process::abort()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stop_unwind(|| unsafe {
|
||||||
|
let mut main_fn_ptr = libc::dlsym(libc::RTLD_DEFAULT, b"main\0".as_ptr() as _);
|
||||||
|
|
||||||
|
if main_fn_ptr.is_null() {
|
||||||
|
main_fn_ptr = libc::dlsym(libc::RTLD_DEFAULT, b"_main\0".as_ptr() as _);
|
||||||
|
}
|
||||||
|
|
||||||
|
if main_fn_ptr.is_null() {
|
||||||
|
panic!("Failed to find main symbol");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the env vars that rust code might expect, passed off to us by the android app
|
||||||
|
// Doing this before main emulates the behavior of a regular executable
|
||||||
|
if cfg!(target_os = "android") && cfg!(debug_assertions) {
|
||||||
|
// Load the env file from the session cache if we're in debug mode and on android
|
||||||
|
//
|
||||||
|
// This is a slightly hacky way of being able to use std::env::var code in android apps without
|
||||||
|
// going through their custom java-based system.
|
||||||
|
let env_file = dioxus_cli_config::android_session_cache_dir().join(".env");
|
||||||
|
if let Ok(env_file) = std::fs::read_to_string(&env_file) {
|
||||||
|
for line in env_file.lines() {
|
||||||
|
if let Some((key, value)) = line.trim().split_once('=') {
|
||||||
|
std::env::set_var(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let main_fn: extern "C" fn() = std::mem::transmute(main_fn_ptr);
|
||||||
|
main_fn();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,7 +22,6 @@ dioxus-hooks = { workspace = true, optional = true }
|
|||||||
dioxus-signals = { workspace = true, optional = true }
|
dioxus-signals = { workspace = true, optional = true }
|
||||||
dioxus-router = { workspace = true, optional = true }
|
dioxus-router = { workspace = true, optional = true }
|
||||||
dioxus-web = { workspace = true, default-features = false, optional = true }
|
dioxus-web = { workspace = true, default-features = false, optional = true }
|
||||||
dioxus-mobile = { workspace = true, optional = true }
|
|
||||||
dioxus-desktop = { workspace = true, default-features = true, optional = true }
|
dioxus-desktop = { workspace = true, default-features = true, optional = true }
|
||||||
dioxus-fullstack = { workspace = true, default-features = true, optional = true }
|
dioxus-fullstack = { workspace = true, default-features = true, optional = true }
|
||||||
dioxus-liveview = { workspace = true, optional = true }
|
dioxus-liveview = { workspace = true, optional = true }
|
||||||
@@ -80,7 +79,7 @@ router = ["dep:dioxus-router"]
|
|||||||
# Platforms
|
# Platforms
|
||||||
fullstack = ["dep:dioxus-fullstack", "dioxus-config-macro/fullstack", "dep:serde"]
|
fullstack = ["dep:dioxus-fullstack", "dioxus-config-macro/fullstack", "dep:serde"]
|
||||||
desktop = ["dep:dioxus-desktop", "dioxus-fullstack?/desktop", "dioxus-config-macro/desktop"]
|
desktop = ["dep:dioxus-desktop", "dioxus-fullstack?/desktop", "dioxus-config-macro/desktop"]
|
||||||
mobile = ["dep:dioxus-mobile", "dep:dioxus-desktop", "dioxus-fullstack?/mobile", "dioxus-config-macro/mobile"]
|
mobile = ["dep:dioxus-desktop", "dioxus-fullstack?/mobile", "dioxus-config-macro/mobile"]
|
||||||
web = [
|
web = [
|
||||||
"dep:dioxus-web",
|
"dep:dioxus-web",
|
||||||
"dioxus-fullstack?/web",
|
"dioxus-fullstack?/web",
|
||||||
|
|||||||
@@ -332,7 +332,7 @@ impl LaunchBuilder {
|
|||||||
|
|
||||||
#[cfg(feature = "mobile")]
|
#[cfg(feature = "mobile")]
|
||||||
if matches!(platform, KnownPlatform::Mobile) {
|
if matches!(platform, KnownPlatform::Mobile) {
|
||||||
return dioxus_mobile::launch_bindings::launch(app, contexts, configs);
|
return dioxus_desktop::launch::launch(app, contexts, configs);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "desktop")]
|
#[cfg(feature = "desktop")]
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ pub use dioxus_desktop as desktop;
|
|||||||
|
|
||||||
#[cfg(feature = "mobile")]
|
#[cfg(feature = "mobile")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "mobile")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "mobile")))]
|
||||||
pub use dioxus_mobile as mobile;
|
pub use dioxus_desktop as mobile;
|
||||||
|
|
||||||
#[cfg(feature = "liveview")]
|
#[cfg(feature = "liveview")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "liveview")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "liveview")))]
|
||||||
|
|||||||
@@ -36,9 +36,6 @@ dioxus-interpreter-js = { workspace = true, optional = true }
|
|||||||
# Desktop Integration
|
# Desktop Integration
|
||||||
dioxus-desktop = { workspace = true, optional = true }
|
dioxus-desktop = { workspace = true, optional = true }
|
||||||
|
|
||||||
# Mobile Integration
|
|
||||||
dioxus-mobile = { workspace = true, optional = true }
|
|
||||||
|
|
||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
tracing-futures = { workspace = true, optional = true }
|
tracing-futures = { workspace = true, optional = true }
|
||||||
tokio-util = { workspace = true, features = ["rt"], optional = true }
|
tokio-util = { workspace = true, features = ["rt"], optional = true }
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ impl Asset {
|
|||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
/// This should only be called from the macro
|
/// This should only be called from the macro
|
||||||
/// Create a new asset from the bundled form of the asset and the link section
|
/// Create a new asset from the bundled form of the asset and the link section
|
||||||
pub const fn new(bundled: fn() -> &'static [u8]) -> Self {
|
pub const fn new(bundled: extern "Rust" fn() -> &'static [u8]) -> Self {
|
||||||
Self { bundled }
|
Self { bundled }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -120,9 +120,9 @@ impl ToTokens for AssetParser {
|
|||||||
|
|
||||||
static __REFERENCE_TO_LINK_SECTION: &'static [u8] = &__LINK_SECTION;
|
static __REFERENCE_TO_LINK_SECTION: &'static [u8] = &__LINK_SECTION;
|
||||||
|
|
||||||
manganis::Asset::new(
|
|
||||||
|| __REFERENCE_TO_LINK_SECTION
|
|
||||||
)
|
manganis::Asset::new(|| unsafe { std::ptr::read_volatile(&__REFERENCE_TO_LINK_SECTION) })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
[build]
|
|
||||||
target = [
|
|
||||||
"aarch64-apple-ios",
|
|
||||||
"aarch64-linux-android"
|
|
||||||
]
|
|
||||||
-4
@@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
"rust-analyzer.inlayHints.enable": true,
|
|
||||||
"rust-analyzer.checkOnSave.allTargets": true,
|
|
||||||
}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "dioxus-mobile"
|
|
||||||
version = { workspace = true }
|
|
||||||
authors = ["Jonathan Kelley"]
|
|
||||||
edition = "2021"
|
|
||||||
description = "Mobile-compatible renderer for Dioxus"
|
|
||||||
repository = "https://github.com/DioxusLabs/dioxus/"
|
|
||||||
homepage = "https://dioxuslabs.com/learn/0.6/getting_started"
|
|
||||||
keywords = ["dom", "ui", "gui", "react"]
|
|
||||||
license = "MIT OR Apache-2.0"
|
|
||||||
rust-version = "1.71.1"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
dioxus-desktop = { workspace = true }
|
|
||||||
dioxus-lib = { workspace = true }
|
|
||||||
dioxus-cli-config = { workspace = true }
|
|
||||||
libc = "0.2.170"
|
|
||||||
|
|
||||||
[target.'cfg(target_os = "android")'.dependencies]
|
|
||||||
jni = "0.21.1"
|
|
||||||
|
|
||||||
[features]
|
|
||||||
default = ["jnibindings", "tokio_runtime", "devtools"]
|
|
||||||
jnibindings = []
|
|
||||||
tokio_runtime = ["dioxus-desktop/tokio_runtime"]
|
|
||||||
fullscreen = ["dioxus-desktop/fullscreen"]
|
|
||||||
transparent = ["dioxus-desktop/transparent"]
|
|
||||||
devtools = ["dioxus-desktop/devtools"]
|
|
||||||
gnu = ["dioxus-desktop/gnu"]
|
|
||||||
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
doctest = false
|
|
||||||
# tests suspended until package ready
|
|
||||||
test = false
|
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
|
||||||
cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples"]
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
[tasks.test]
|
|
||||||
command = "cargo"
|
|
||||||
args = [
|
|
||||||
"test",
|
|
||||||
"--no-run",
|
|
||||||
]
|
|
||||||
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
# Dioxus Mobile
|
|
||||||
|
|
||||||
[![Crates.io][crates-badge]][crates-url]
|
|
||||||
[![MIT licensed][mit-badge]][mit-url]
|
|
||||||
[![Build Status][actions-badge]][actions-url]
|
|
||||||
[![Discord chat][discord-badge]][discord-url]
|
|
||||||
|
|
||||||
[crates-badge]: https://img.shields.io/crates/v/dioxus-mobile.svg
|
|
||||||
[crates-url]: https://crates.io/crates/dioxus-mobile
|
|
||||||
[mit-badge]: https://img.shields.io/badge/license-MIT-blue.svg
|
|
||||||
[mit-url]: https://github.com/dioxuslabs/dioxus/blob/main/LICENSE-MIT
|
|
||||||
[actions-badge]: https://github.com/dioxuslabs/dioxus/actions/workflows/main.yml/badge.svg
|
|
||||||
[actions-url]: https://github.com/dioxuslabs/dioxus/actions?query=workflow%3ACI+branch%3Amaster
|
|
||||||
[discord-badge]: https://img.shields.io/discord/899851952891002890.svg?logo=discord&style=flat-square
|
|
||||||
[discord-url]: https://discord.gg/XgGxMSkvUM
|
|
||||||
|
|
||||||
[Website](https://dioxuslabs.com) |
|
|
||||||
[Guides](https://dioxuslabs.com/learn/0.6/) |
|
|
||||||
[API Docs](https://docs.rs/dioxus-mobile/latest/dioxus_mobile) |
|
|
||||||
[Chat](https://discord.gg/XgGxMSkvUM)
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
`dioxus-mobile` is a re-export of `dioxus-desktop` with some minor tweaks and documentation changes. As this crate evolves, it will provide some more unique features to mobile, but for now, it's very similar to the desktop crate.
|
|
||||||
|
|
||||||
Dioxus Mobile supports both iOS and Android. However, Android support is still quite experimental and requires a lot of configuration. A good area to contribute here would be to improve the CLI tool to include bundling and mobile configuration.
|
|
||||||
|
|
||||||
## Getting Set up
|
|
||||||
|
|
||||||
Getting set up with mobile can but quite challenging. The tooling here isn't great (yet) and might take some hacking around to get things working. macOS M1 is broadly unexplored and might not work for you.
|
|
||||||
|
|
||||||
You can read [our guide](https://dioxuslabs.com/learn/0.6/getting_started) on mobile development with Dioxus to get started.
|
|
||||||
|
|
||||||
## Contributing
|
|
||||||
|
|
||||||
- Report issues on our [issue tracker](https://github.com/dioxuslabs/dioxus/issues).
|
|
||||||
- Join the discord and ask questions!
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
This project is licensed under the [MIT license].
|
|
||||||
|
|
||||||
[mit license]: https://github.com/dioxuslabs/dioxus/blob/main/LICENSE-MIT
|
|
||||||
|
|
||||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
|
||||||
for inclusion in Dioxus by you shall be licensed as MIT without any additional
|
|
||||||
terms or conditions.
|
|
||||||
@@ -1,105 +0,0 @@
|
|||||||
#![doc = include_str!("../README.md")]
|
|
||||||
#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/79236386")]
|
|
||||||
#![doc(html_favicon_url = "https://avatars.githubusercontent.com/u/79236386")]
|
|
||||||
|
|
||||||
pub use dioxus_desktop::*;
|
|
||||||
// intentionally shadow the launch module as it does not work on mobile.
|
|
||||||
pub mod launch {}
|
|
||||||
|
|
||||||
use dioxus_lib::prelude::*;
|
|
||||||
use std::any::Any;
|
|
||||||
|
|
||||||
pub mod launch_bindings {
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
pub fn launch(
|
|
||||||
root: fn() -> Element,
|
|
||||||
contexts: Vec<Box<dyn Fn() -> Box<dyn Any> + Send + Sync>>,
|
|
||||||
platform_config: Vec<Box<dyn Any>>,
|
|
||||||
) {
|
|
||||||
super::launch_cfg(root, contexts, platform_config);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn launch_virtual_dom(_virtual_dom: VirtualDom, _desktop_config: Config) -> ! {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Launch via the binding API
|
|
||||||
pub fn launch(root: fn() -> Element) {
|
|
||||||
launch_cfg(root, vec![], vec![]);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn launch_cfg(
|
|
||||||
root: fn() -> Element,
|
|
||||||
contexts: Vec<Box<dyn Fn() -> Box<dyn Any> + Send + Sync>>,
|
|
||||||
platform_config: Vec<Box<dyn Any>>,
|
|
||||||
) {
|
|
||||||
dioxus_desktop::launch::launch(root, contexts, platform_config);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub fn root() {
|
|
||||||
fn stop_unwind<F: FnOnce() -> T, T>(f: F) -> T {
|
|
||||||
match std::panic::catch_unwind(std::panic::AssertUnwindSafe(f)) {
|
|
||||||
Ok(t) => t,
|
|
||||||
Err(err) => {
|
|
||||||
eprintln!("attempt to unwind out of `rust` with err: {:?}", err);
|
|
||||||
std::process::abort()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stop_unwind(|| unsafe {
|
|
||||||
let mut main_fn_ptr = libc::dlsym(libc::RTLD_DEFAULT, b"main\0".as_ptr() as _);
|
|
||||||
|
|
||||||
if main_fn_ptr.is_null() {
|
|
||||||
main_fn_ptr = libc::dlsym(libc::RTLD_DEFAULT, b"_main\0".as_ptr() as _);
|
|
||||||
}
|
|
||||||
|
|
||||||
if main_fn_ptr.is_null() {
|
|
||||||
panic!("Failed to find main symbol");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the env vars that rust code might expect, passed off to us by the android app
|
|
||||||
// Doing this before main emulates the behavior of a regular executable
|
|
||||||
if cfg!(target_os = "android") && cfg!(debug_assertions) {
|
|
||||||
load_env_file_from_session_cache();
|
|
||||||
}
|
|
||||||
|
|
||||||
let main_fn: extern "C" fn() = std::mem::transmute(main_fn_ptr);
|
|
||||||
main_fn();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Expose the `Java_dev_dioxus_main_WryActivity_create` function to the JNI layer.
|
|
||||||
/// We hardcode these to have a single trampoline for host Java code to call into.
|
|
||||||
///
|
|
||||||
/// This saves us from having to plumb the top-level package name all the way down into
|
|
||||||
/// this file. This is better for modularity (ie just call dioxus' main to run the app) as
|
|
||||||
/// well as cache thrashing since this crate doesn't rely on external env vars.
|
|
||||||
///
|
|
||||||
/// The CLI is expecting to find `dev.dioxus.main` in the final library. If you find a need to
|
|
||||||
/// change this, you'll need to change the CLI as well.
|
|
||||||
#[cfg(target_os = "android")]
|
|
||||||
#[no_mangle]
|
|
||||||
#[inline(never)]
|
|
||||||
pub extern "C" fn start_app() {
|
|
||||||
tao::android_binding!(dev_dioxus, main, WryActivity, wry::android_setup, root, tao);
|
|
||||||
wry::android_binding!(dev_dioxus, main, wry);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Load the env file from the session cache if we're in debug mode and on android
|
|
||||||
///
|
|
||||||
/// This is a slightly hacky way of being able to use std::env::var code in android apps without
|
|
||||||
/// going through their custom java-based system.
|
|
||||||
fn load_env_file_from_session_cache() {
|
|
||||||
let env_file = dioxus_cli_config::android_session_cache_dir().join(".env");
|
|
||||||
if let Ok(env_file) = std::fs::read_to_string(&env_file) {
|
|
||||||
for line in env_file.lines() {
|
|
||||||
if let Some((key, value)) = line.trim().split_once('=') {
|
|
||||||
std::env::set_var(key, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -13,7 +13,7 @@ tokio = { workspace = true, features = ["full"], optional = true }
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
server = ["dioxus/server", "tokio"]
|
server = ["dioxus/server", "dep:tokio"]
|
||||||
web = ["dioxus/web"]
|
web = ["dioxus/web"]
|
||||||
|
|
||||||
# We need a separate bin for the SSG build to avoid conflicting server caches
|
# We need a separate bin for the SSG build to avoid conflicting server caches
|
||||||
|
|||||||
@@ -95,9 +95,9 @@ Se você conhece React, então você já conhece o Dioxus.
|
|||||||
|
|
||||||
## Projetos de Exemplo:
|
## Projetos de Exemplo:
|
||||||
|
|
||||||
| Navegador de Arquivos (Desktop) | WiFi Scanner (Desktop) | TodoMVC (Todas as Plataformas) | E-commerce com Tailwind (SSR/LiveView) |
|
| Navegador de Arquivos (Desktop) | WiFi Scanner (Desktop) | TodoMVC (Todas as Plataformas) | E-commerce com Tailwind (SSR/LiveView) |
|
||||||
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| [](https://github.com/DioxusLabs/example-projects/blob/master/file-explorer) | [](https://github.com/DioxusLabs/example-projects/blob/master/wifi-scanner) | [](https://github.com/DioxusLabs/example-projects/blob/master/todomvc) | [](https://github.com/DioxusLabs/example-projects/blob/master/ecommerce-site) |
|
| [](https://github.com/DioxusLabs/example-projects/blob/master/file-explorer) | [](/example-projects/bluetooth-scanner/) | [](https://github.com/DioxusLabs/example-projects/blob/master/todomvc) | [](https://github.com/DioxusLabs/example-projects/blob/master/ecommerce-site) |
|
||||||
|
|
||||||
Veja a página [awesome-dioxus](https://github.com/DioxusLabs/awesome-dioxus) para uma lista curada do conteúdo dentro do ecossistema do Dioxus.
|
Veja a página [awesome-dioxus](https://github.com/DioxusLabs/awesome-dioxus) para uma lista curada do conteúdo dentro do ecossistema do Dioxus.
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user