feat(cli): add android dev and ios dev commands (#4982)

This commit is contained in:
Lucas Fernandes Nogueira
2022-08-20 16:53:07 -03:00
committed by GitHub
parent f445f374a3
commit 6f0615044d
47 changed files with 1631 additions and 693 deletions

View File

@@ -0,0 +1,6 @@
---
"cli.rs": minor
"cli.js": minor
---
Added `android dev` and `ios dev` commands.

View File

@@ -0,0 +1,5 @@
---
"tauri-codegen": patch
---
Change `devPath` URL to use the local IP address on iOS and Android.

5
.changes/dev-proxy.md Normal file
View File

@@ -0,0 +1,5 @@
---
"tauri": major
---
**Breaking change:** Use the custom protocol as a proxy to the development server on all platforms except Linux.

View File

@@ -29,6 +29,8 @@ semver = "1"
ico = "0.1"
png = "0.17"
json-patch = "0.2"
local-ip-address = "0.4"
url = "2"
[target."cfg(target_os = \"macos\")".dependencies]
plist = "1"

View File

@@ -122,7 +122,7 @@ enum Target {
pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsError> {
let ContextData {
dev,
config,
mut config,
config_parent,
root,
} = data;
@@ -155,6 +155,23 @@ pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsE
panic!("unknown codegen target");
};
if dev && (target == Target::Ios || target == Target::Android) {
if let AppUrl::Url(WindowUrl::External(url)) = &mut config.build.dev_path {
let localhost = match url.host() {
Some(url::Host::Domain(d)) => d == "localhost",
Some(url::Host::Ipv4(i)) => {
i == std::net::Ipv4Addr::LOCALHOST || i == std::net::Ipv4Addr::UNSPECIFIED
}
_ => false,
};
if localhost {
if let Ok(ip) = local_ip_address::local_ip() {
url.set_host(Some(&ip.to_string())).unwrap();
}
}
}
}
let mut options = AssetOptions::new(config.tauri.pattern.clone())
.freeze_prototype(config.tauri.security.freeze_prototype)
.dangerous_disable_asset_csp_modification(

View File

@@ -431,6 +431,7 @@ impl ToTokens for EmbeddedAssets {
// we expect phf related items to be in path when generating the path code
tokens.append_all(quote! {{
#[allow(unused_imports)]
use ::tauri::utils::assets::{CspHash, EmbeddedAssets, phf, phf::phf_map};
EmbeddedAssets::new(phf_map! { #assets }, &[#global_hashes], phf_map! { #html_hashes })
}});

View File

@@ -2364,7 +2364,7 @@ fn default_dialog() -> bool {
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct IosConfig {
/// The development team. This value is required for iOS development because code signing is enforced.
/// The `APPLE_DEVELOPMENT_TEAM` environment variable can be set to overwrite it.
/// The `TAURI_APPLE_DEVELOPMENT_TEAM` environment variable can be set to overwrite it.
#[serde(alias = "development-team")]
pub development_team: Option<String>,
}

View File

@@ -75,7 +75,7 @@ base64 = { version = "0.13", optional = true }
clap = { version = "3", optional = true }
reqwest = { version = "0.11", features = [ "json", "stream" ], optional = true }
bytes = { version = "1", features = [ "serde" ], optional = true }
attohttpc = { version = "0.20", features = [ "compress", "json", "form" ], optional = true }
attohttpc = { version = "0.20", features = [ "compress", "json", "form" ] }
open = { version = "3.0", optional = true }
shared_child = { version = "1.0", optional = true }
os_pipe = { version = "1.0", optional = true }
@@ -146,7 +146,7 @@ updater = [
"dialog-ask",
"fs-extract-api"
]
http-api = [ "attohttpc" ]
http-api = [ ]
http-multipart = [ "attohttpc/multipart-form", "reqwest/multipart" ]
shell-open-api = [ "open", "regex", "tauri-macros/shell-scope" ]
fs-extract-api = [ "zip" ]

View File

@@ -142,11 +142,6 @@ fn set_csp<R: Runtime>(
Csp::DirectiveMap(csp).to_string()
}
#[cfg(target_os = "linux")]
fn set_html_csp(html: &str, csp: &str) -> String {
html.replacen(tauri_utils::html::CSP_TOKEN, csp, 1)
}
// inspired by https://github.com/rust-lang/rust/blob/1be5c8f90912c446ecbdc405cbc4a89f9acd20fd/library/alloc/src/str.rs#L260-L297
fn replace_with_callback<F: FnMut() -> String>(
original: &str,
@@ -374,7 +369,13 @@ impl<R: Runtime> WindowManager<R> {
/// Get the origin as it will be seen in the webview.
fn get_browser_origin(&self) -> String {
match self.base_path() {
AppUrl::Url(WindowUrl::External(url)) => url.origin().ascii_serialization(),
AppUrl::Url(WindowUrl::External(url)) => {
if cfg!(dev) && !cfg!(target_os = "linux") {
format_real_schema("tauri")
} else {
url.origin().ascii_serialization()
}
}
_ => format_real_schema("tauri"),
}
}
@@ -820,8 +821,12 @@ impl<R: Runtime> WindowManager<R> {
>,
) -> Box<dyn Fn(&HttpRequest) -> Result<HttpResponse, Box<dyn std::error::Error>> + Send + Sync>
{
#[cfg(dev)]
let url = self.get_url().into_owned();
#[cfg(not(dev))]
let manager = self.clone();
let window_origin = window_origin.to_string();
Box::new(move |request| {
let path = request
.uri()
@@ -834,32 +839,47 @@ impl<R: Runtime> WindowManager<R> {
// the `strip_prefix` only returns None when a request is made to `https://tauri.$P` on Windows
// where `$P` is not `localhost/*`
.unwrap_or_else(|| "".to_string());
let asset = manager.get_asset(path)?;
let mut builder = HttpResponseBuilder::new()
.header("Access-Control-Allow-Origin", &window_origin)
.mimetype(&asset.mime_type);
if let Some(csp) = &asset.csp_header {
builder = builder.header("Content-Security-Policy", csp);
}
let mut response = builder.body(asset.bytes)?;
if let Some(handler) = &web_resource_request_handler {
handler(request, &mut response);
// if it's an HTML file, we need to set the CSP meta tag on Linux
#[cfg(target_os = "linux")]
if let Some(response_csp) = response.headers().get("Content-Security-Policy") {
let response_csp = String::from_utf8_lossy(response_csp.as_bytes());
let body = set_html_csp(&String::from_utf8_lossy(response.body()), &response_csp);
*response.body_mut() = body.as_bytes().to_vec();
}
} else {
#[cfg(target_os = "linux")]
{
if let Some(csp) = &asset.csp_header {
let body = set_html_csp(&String::from_utf8_lossy(response.body()), csp);
*response.body_mut() = body.as_bytes().to_vec();
let mut builder =
HttpResponseBuilder::new().header("Access-Control-Allow-Origin", &window_origin);
#[cfg(dev)]
let mut response = {
let mut url = url.clone();
url.set_path(&path);
match attohttpc::get(url.as_str()).send() {
Ok(r) => {
for (name, value) in r.headers() {
builder = builder.header(name, value);
}
builder.status(r.status()).body(r.bytes()?)?
}
Err(e) => {
debug_eprintln!("Failed to request {}: {}", url.path(), e);
return Err(Box::new(e));
}
}
};
#[cfg(not(dev))]
let mut response = {
let asset = manager.get_asset(path)?;
builder = builder.mimetype(&asset.mime_type);
if let Some(csp) = &asset.csp_header {
builder = builder.header("Content-Security-Policy", csp);
}
builder.body(asset.bytes)?
};
if let Some(handler) = &web_resource_request_handler {
handler(request, &mut response);
}
// if it's an HTML file, we need to set the CSP meta tag on Linux
#[cfg(all(not(dev), target_os = "linux"))]
if let Some(response_csp) = response.headers().get("Content-Security-Policy") {
let response_csp = String::from_utf8_lossy(response_csp.as_bytes());
let html = String::from_utf8_lossy(response.body());
let body = html.replacen(tauri_utils::html::CSP_TOKEN, &response_csp, 1);
*response.body_mut() = body.as_bytes().to_vec();
}
Ok(response)
})
@@ -1061,7 +1081,10 @@ impl<R: Runtime> WindowManager<R> {
#[allow(unused_mut)] // mut url only for the data-url parsing
let (is_local, mut url) = match &pending.webview_attributes.url {
WindowUrl::App(path) => {
#[cfg(target_os = "linux")]
let url = self.get_url();
#[cfg(not(target_os = "linux"))]
let url: Cow<'_, Url> = Cow::Owned(Url::parse("tauri://localhost").unwrap());
(
true,
// ignore "index.html" just to simplify the url
@@ -1078,7 +1101,13 @@ impl<R: Runtime> WindowManager<R> {
}
WindowUrl::External(url) => {
let config_url = self.get_url();
(config_url.make_relative(url).is_some(), url.clone())
let is_local = config_url.make_relative(url).is_some();
let mut url = url.clone();
if is_local && !cfg!(target_os = "linux") {
url.set_scheme("tauri").unwrap();
url.set_host(Some("localhost")).unwrap();
}
(is_local, url)
}
_ => unimplemented!(),
};

File diff suppressed because one or more lines are too long

View File

@@ -3,7 +3,7 @@
"version": "1.0.0",
"type": "module",
"scripts": {
"dev": "vite --clearScreen false --port 5000",
"dev": "vite --clearScreen false",
"build": "vite build",
"serve": "vite preview",
"tauri": "node ../../tooling/cli/node/tauri.js"
@@ -15,9 +15,10 @@
"devDependencies": {
"@iconify-json/codicon": "^1.1.10",
"@iconify-json/ph": "^1.1.1",
"@sveltejs/vite-plugin-svelte": "^1.0.0-next.49",
"@sveltejs/vite-plugin-svelte": "^1.0.1",
"internal-ip": "^7.0.0",
"svelte": "^3.49.0",
"unocss": "^0.39.3",
"vite": "^2.9.12"
"vite": "^3.0.9"
}
}

View File

@@ -112,9 +112,7 @@ version = "0.1.0"
dependencies = [
"android_logger",
"env_logger 0.9.0",
"jni",
"log",
"mobile-entry-point",
"paste",
"serde",
"serde_json",
@@ -1659,6 +1657,18 @@ dependencies = [
"safemem",
]
[[package]]
name = "local-ip-address"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74d6f43d42d775afa8073bfa6aba569b340a3635efa8c9f7702c9c6ed209692f"
dependencies = [
"libc",
"neli",
"thiserror",
"windows-sys",
]
[[package]]
name = "lock_api"
version = "0.4.7"
@@ -1808,17 +1818,6 @@ dependencies = [
"windows-sys",
]
[[package]]
name = "mobile-entry-point"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81bef5a90018326583471cccca10424d7b3e770397b02f03276543cbb9b6a1a6"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "multipart"
version = "0.18.0"
@@ -1878,6 +1877,16 @@ dependencies = [
"jni-sys",
]
[[package]]
name = "neli"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9053554eb5dcb7e10d9cdab1206965bde870eed5d0d341532ca035e3ba221508"
dependencies = [
"byteorder",
"libc",
]
[[package]]
name = "new_debug_unreachable"
version = "1.0.4"
@@ -3243,6 +3252,7 @@ dependencies = [
"brotli",
"ico",
"json-patch",
"local-ip-address",
"plist",
"png 0.17.5",
"proc-macro2",
@@ -3255,6 +3265,7 @@ dependencies = [
"tauri-utils",
"thiserror",
"time",
"url",
"uuid 1.1.2",
"walkdir",
]

View File

@@ -45,11 +45,9 @@ tauri-runtime-wry = { path = "../../../core/tauri-runtime-wry/" }
[target.'cfg(target_os = "android")'.dependencies]
android_logger = "0.9.0"
jni = "0.19.0"
paste = "1.0"
[target.'cfg(target_os = "ios")'.dependencies]
mobile-entry-point = "0.1.0"
env_logger = "0.9.0"
[features]

View File

@@ -2,8 +2,8 @@
"$schema": "../../../tooling/cli/schema.json",
"build": {
"distDir": "../dist",
"devPath": "http://localhost:5000",
"beforeDevCommand": "yarn dev",
"devPath": "http://localhost:5173",
"beforeDevCommand": "yarn dev --host",
"beforeBuildCommand": "yarn build"
},
"package": {

View File

@@ -1,22 +1,30 @@
import { defineConfig } from 'vite'
import Unocss from 'unocss/vite'
import { svelte } from '@sveltejs/vite-plugin-svelte'
import { internalIpV4 } from 'internal-ip'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [Unocss(), svelte()],
build: {
rollupOptions: {
output: {
entryFileNames: `assets/[name].js`,
chunkFileNames: `assets/[name].js`,
assetFileNames: `assets/[name].[ext]`
export default defineConfig(async ({ command, mode }) => {
const host = await internalIpV4()
return {
plugins: [Unocss(), svelte()],
build: {
rollupOptions: {
output: {
entryFileNames: `assets/[name].js`,
chunkFileNames: `assets/[name].js`,
assetFileNames: `assets/[name].[ext]`
}
}
},
server: {
hmr: {
host,
port: 5183
},
fs: {
allow: ['.', '../../tooling/api/dist']
}
}
},
server: {
fs: {
allow: ['.', '../../tooling/api/dist']
}
}
})

View File

@@ -15,6 +15,11 @@
resolved "https://registry.yarnpkg.com/@antfu/utils/-/utils-0.5.2.tgz#8c2d931ff927be0ebe740169874a3d4004ab414b"
integrity sha512-CQkeV+oJxUazwjlHD0/3ZD08QWKuGQkhnrKo3e6ly5pd48VUpXbb77q0xMU4+vc2CkJnDS02Eq/M9ugyX20XZA==
"@esbuild/linux-loong64@0.14.54":
version "0.14.54"
resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.14.54.tgz#de2a4be678bd4d0d1ffbb86e6de779cde5999028"
integrity sha512-bZBrLAIX1kpWelV0XemxBZllyRmM6vgFQQG2GdNb+r3Fkp0FOh1NJSvekXDs7jq70k4euu1cryLMfU+mTXlEpw==
"@iconify-json/codicon@^1.1.10":
version "1.1.10"
resolved "https://registry.yarnpkg.com/@iconify-json/codicon/-/codicon-1.1.10.tgz#22fee909be51afebfbcc6cd57209064b5363f202"
@@ -80,15 +85,15 @@
estree-walker "^2.0.1"
picomatch "^2.2.2"
"@sveltejs/vite-plugin-svelte@^1.0.0-next.49":
version "1.0.0-next.49"
resolved "https://registry.yarnpkg.com/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-1.0.0-next.49.tgz#44cc00a19c6c23002516b66c5ab90ee66720df57"
integrity sha512-AKh0Ka8EDgidnxWUs8Hh2iZLZovkETkefO99XxZ4sW4WGJ7VFeBx5kH/NIIGlaNHLcrIvK3CK0HkZwC3Cici0A==
"@sveltejs/vite-plugin-svelte@^1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-1.0.1.tgz#7f468f03c933fcdfc60d4773671c73f33b9ef4d6"
integrity sha512-PorCgUounn0VXcpeJu+hOweZODKmGuLHsLomwqSj+p26IwjjGffmYQfVHtiTWq+NqaUuuHWWG7vPge6UFw4Aeg==
dependencies:
"@rollup/pluginutils" "^4.2.1"
debug "^4.3.4"
deepmerge "^4.2.2"
kleur "^4.1.4"
kleur "^4.1.5"
magic-string "^0.26.2"
svelte-hmr "^0.14.12"
@@ -323,6 +328,13 @@ deepmerge@^4.2.2:
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955"
integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==
default-gateway@^6.0.3:
version "6.0.3"
resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-6.0.3.tgz#819494c888053bdb743edbf343d6cdf7f2943a71"
integrity sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==
dependencies:
execa "^5.0.0"
defu@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/defu/-/defu-6.0.0.tgz#b397a6709a2f3202747a3d9daf9446e41ad0c5fc"
@@ -338,138 +350,139 @@ duplexer@^0.1.2:
resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6"
integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==
esbuild-android-64@0.14.47:
version "0.14.47"
resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.14.47.tgz#ef95b42c67bcf4268c869153fa3ad1466c4cea6b"
integrity sha512-R13Bd9+tqLVFndncMHssZrPWe6/0Kpv2/dt4aA69soX4PRxlzsVpCvoJeFE8sOEoeVEiBkI0myjlkDodXlHa0g==
esbuild-android-64@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.14.54.tgz#505f41832884313bbaffb27704b8bcaa2d8616be"
integrity sha512-Tz2++Aqqz0rJ7kYBfz+iqyE3QMycD4vk7LBRyWaAVFgFtQ/O8EJOnVmTOiDWYZ/uYzB4kvP+bqejYdVKzE5lAQ==
esbuild-android-arm64@0.14.47:
version "0.14.47"
resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.47.tgz#4ebd7ce9fb250b4695faa3ee46fd3b0754ecd9e6"
integrity sha512-OkwOjj7ts4lBp/TL6hdd8HftIzOy/pdtbrNA4+0oVWgGG64HrdVzAF5gxtJufAPOsEjkyh1oIYvKAUinKKQRSQ==
esbuild-android-arm64@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.54.tgz#8ce69d7caba49646e009968fe5754a21a9871771"
integrity sha512-F9E+/QDi9sSkLaClO8SOV6etqPd+5DgJje1F9lOWoNncDdOBL2YF59IhsWATSt0TLZbYCf3pNlTHvVV5VfHdvg==
esbuild-darwin-64@0.14.47:
version "0.14.47"
resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.47.tgz#e0da6c244f497192f951807f003f6a423ed23188"
integrity sha512-R6oaW0y5/u6Eccti/TS6c/2c1xYTb1izwK3gajJwi4vIfNs1s8B1dQzI1UiC9T61YovOQVuePDcfqHLT3mUZJA==
esbuild-darwin-64@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.54.tgz#24ba67b9a8cb890a3c08d9018f887cc221cdda25"
integrity sha512-jtdKWV3nBviOd5v4hOpkVmpxsBy90CGzebpbO9beiqUYVMBtSc0AL9zGftFuBon7PNDcdvNCEuQqw2x0wP9yug==
esbuild-darwin-arm64@0.14.47:
version "0.14.47"
resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.47.tgz#cd40fd49a672fca581ed202834239dfe540a9028"
integrity sha512-seCmearlQyvdvM/noz1L9+qblC5vcBrhUaOoLEDDoLInF/VQ9IkobGiLlyTPYP5dW1YD4LXhtBgOyevoIHGGnw==
esbuild-darwin-arm64@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.54.tgz#3f7cdb78888ee05e488d250a2bdaab1fa671bf73"
integrity sha512-OPafJHD2oUPyvJMrsCvDGkRrVCar5aVyHfWGQzY1dWnzErjrDuSETxwA2HSsyg2jORLY8yBfzc1MIpUkXlctmw==
esbuild-freebsd-64@0.14.47:
version "0.14.47"
resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.47.tgz#8da6a14c095b29c01fc8087a16cb7906debc2d67"
integrity sha512-ZH8K2Q8/Ux5kXXvQMDsJcxvkIwut69KVrYQhza/ptkW50DC089bCVrJZZ3sKzIoOx+YPTrmsZvqeZERjyYrlvQ==
esbuild-freebsd-64@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.54.tgz#09250f997a56ed4650f3e1979c905ffc40bbe94d"
integrity sha512-OKwd4gmwHqOTp4mOGZKe/XUlbDJ4Q9TjX0hMPIDBUWWu/kwhBAudJdBoxnjNf9ocIB6GN6CPowYpR/hRCbSYAg==
esbuild-freebsd-arm64@0.14.47:
version "0.14.47"
resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.47.tgz#ad31f9c92817ff8f33fd253af7ab5122dc1b83f6"
integrity sha512-ZJMQAJQsIOhn3XTm7MPQfCzEu5b9STNC+s90zMWe2afy9EwnHV7Ov7ohEMv2lyWlc2pjqLW8QJnz2r0KZmeAEQ==
esbuild-freebsd-arm64@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.54.tgz#bafb46ed04fc5f97cbdb016d86947a79579f8e48"
integrity sha512-sFwueGr7OvIFiQT6WeG0jRLjkjdqWWSrfbVwZp8iMP+8UHEHRBvlaxL6IuKNDwAozNUmbb8nIMXa7oAOARGs1Q==
esbuild-linux-32@0.14.47:
version "0.14.47"
resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.14.47.tgz#de085e4db2e692ea30c71208ccc23fdcf5196c58"
integrity sha512-FxZOCKoEDPRYvq300lsWCTv1kcHgiiZfNrPtEhFAiqD7QZaXrad8LxyJ8fXGcWzIFzRiYZVtB3ttvITBvAFhKw==
esbuild-linux-32@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.14.54.tgz#e2a8c4a8efdc355405325033fcebeb941f781fe5"
integrity sha512-1ZuY+JDI//WmklKlBgJnglpUL1owm2OX+8E1syCD6UAxcMM/XoWd76OHSjl/0MR0LisSAXDqgjT3uJqT67O3qw==
esbuild-linux-64@0.14.47:
version "0.14.47"
resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.14.47.tgz#2a9321bbccb01f01b04cebfcfccbabeba3658ba1"
integrity sha512-nFNOk9vWVfvWYF9YNYksZptgQAdstnDCMtR6m42l5Wfugbzu11VpMCY9XrD4yFxvPo9zmzcoUL/88y0lfJZJJw==
esbuild-linux-64@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.14.54.tgz#de5fdba1c95666cf72369f52b40b03be71226652"
integrity sha512-EgjAgH5HwTbtNsTqQOXWApBaPVdDn7XcK+/PtJwZLT1UmpLoznPd8c5CxqsH2dQK3j05YsB3L17T8vE7cp4cCg==
esbuild-linux-arm64@0.14.47:
version "0.14.47"
resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.47.tgz#b9da7b6fc4b0ca7a13363a0c5b7bb927e4bc535a"
integrity sha512-ywfme6HVrhWcevzmsufjd4iT3PxTfCX9HOdxA7Hd+/ZM23Y9nXeb+vG6AyA6jgq/JovkcqRHcL9XwRNpWG6XRw==
esbuild-linux-arm64@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.54.tgz#dae4cd42ae9787468b6a5c158da4c84e83b0ce8b"
integrity sha512-WL71L+0Rwv+Gv/HTmxTEmpv0UgmxYa5ftZILVi2QmZBgX3q7+tDeOQNqGtdXSdsL8TQi1vIaVFHUPDe0O0kdig==
esbuild-linux-arm@0.14.47:
version "0.14.47"
resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.47.tgz#56fec2a09b9561c337059d4af53625142aded853"
integrity sha512-ZGE1Bqg/gPRXrBpgpvH81tQHpiaGxa8c9Rx/XOylkIl2ypLuOcawXEAo8ls+5DFCcRGt/o3sV+PzpAFZobOsmA==
esbuild-linux-arm@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.54.tgz#a2c1dff6d0f21dbe8fc6998a122675533ddfcd59"
integrity sha512-qqz/SjemQhVMTnvcLGoLOdFpCYbz4v4fUo+TfsWG+1aOu70/80RV6bgNpR2JCrppV2moUQkww+6bWxXRL9YMGw==
esbuild-linux-mips64le@0.14.47:
version "0.14.47"
resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.47.tgz#9db21561f8f22ed79ef2aedb7bbef082b46cf823"
integrity sha512-mg3D8YndZ1LvUiEdDYR3OsmeyAew4MA/dvaEJxvyygahWmpv1SlEEnhEZlhPokjsUMfRagzsEF/d/2XF+kTQGg==
esbuild-linux-mips64le@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.54.tgz#d9918e9e4cb972f8d6dae8e8655bf9ee131eda34"
integrity sha512-qTHGQB8D1etd0u1+sB6p0ikLKRVuCWhYQhAHRPkO+OF3I/iSlTKNNS0Lh2Oc0g0UFGguaFZZiPJdJey3AGpAlw==
esbuild-linux-ppc64le@0.14.47:
version "0.14.47"
resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.47.tgz#dc3a3da321222b11e96e50efafec9d2de408198b"
integrity sha512-WER+f3+szmnZiWoK6AsrTKGoJoErG2LlauSmk73LEZFQ/iWC+KhhDsOkn1xBUpzXWsxN9THmQFltLoaFEH8F8w==
esbuild-linux-ppc64le@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.54.tgz#3f9a0f6d41073fb1a640680845c7de52995f137e"
integrity sha512-j3OMlzHiqwZBDPRCDFKcx595XVfOfOnv68Ax3U4UKZ3MTYQB5Yz3X1mn5GnodEVYzhtZgxEBidLWeIs8FDSfrQ==
esbuild-linux-riscv64@0.14.47:
version "0.14.47"
resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.47.tgz#9bd6dcd3dca6c0357084ecd06e1d2d4bf105335f"
integrity sha512-1fI6bP3A3rvI9BsaaXbMoaOjLE3lVkJtLxsgLHqlBhLlBVY7UqffWBvkrX/9zfPhhVMd9ZRFiaqXnB1T7BsL2g==
esbuild-linux-riscv64@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.54.tgz#618853c028178a61837bc799d2013d4695e451c8"
integrity sha512-y7Vt7Wl9dkOGZjxQZnDAqqn+XOqFD7IMWiewY5SPlNlzMX39ocPQlOaoxvT4FllA5viyV26/QzHtvTjVNOxHZg==
esbuild-linux-s390x@0.14.47:
version "0.14.47"
resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.47.tgz#a458af939b52f2cd32fc561410d441a51f69d41f"
integrity sha512-eZrWzy0xFAhki1CWRGnhsHVz7IlSKX6yT2tj2Eg8lhAwlRE5E96Hsb0M1mPSE1dHGpt1QVwwVivXIAacF/G6mw==
esbuild-linux-s390x@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.54.tgz#d1885c4c5a76bbb5a0fe182e2c8c60eb9e29f2a6"
integrity sha512-zaHpW9dziAsi7lRcyV4r8dhfG1qBidQWUXweUjnw+lliChJqQr+6XD71K41oEIC3Mx1KStovEmlzm+MkGZHnHA==
esbuild-netbsd-64@0.14.47:
version "0.14.47"
resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.47.tgz#6388e785d7e7e4420cb01348d7483ab511b16aa8"
integrity sha512-Qjdjr+KQQVH5Q2Q1r6HBYswFTToPpss3gqCiSw2Fpq/ua8+eXSQyAMG+UvULPqXceOwpnPo4smyZyHdlkcPppQ==
esbuild-netbsd-64@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.54.tgz#69ae917a2ff241b7df1dbf22baf04bd330349e81"
integrity sha512-PR01lmIMnfJTgeU9VJTDY9ZerDWVFIUzAtJuDHwwceppW7cQWjBBqP48NdeRtoP04/AtO9a7w3viI+PIDr6d+w==
esbuild-openbsd-64@0.14.47:
version "0.14.47"
resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.47.tgz#309af806db561aa886c445344d1aacab850dbdc5"
integrity sha512-QpgN8ofL7B9z8g5zZqJE+eFvD1LehRlxr25PBkjyyasakm4599iroUpaj96rdqRlO2ShuyqwJdr+oNqWwTUmQw==
esbuild-openbsd-64@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.54.tgz#db4c8495287a350a6790de22edea247a57c5d47b"
integrity sha512-Qyk7ikT2o7Wu76UsvvDS5q0amJvmRzDyVlL0qf5VLsLchjCa1+IAvd8kTBgUxD7VBUUVgItLkk609ZHUc1oCaw==
esbuild-sunos-64@0.14.47:
version "0.14.47"
resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.47.tgz#3f19612dcdb89ba6c65283a7ff6e16f8afbf8aaa"
integrity sha512-uOeSgLUwukLioAJOiGYm3kNl+1wJjgJA8R671GYgcPgCx7QR73zfvYqXFFcIO93/nBdIbt5hd8RItqbbf3HtAQ==
esbuild-sunos-64@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.54.tgz#54287ee3da73d3844b721c21bc80c1dc7e1bf7da"
integrity sha512-28GZ24KmMSeKi5ueWzMcco6EBHStL3B6ubM7M51RmPwXQGLe0teBGJocmWhgwccA1GeFXqxzILIxXpHbl9Q/Kw==
esbuild-windows-32@0.14.47:
version "0.14.47"
resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.14.47.tgz#a92d279c8458d5dc319abcfeb30aa49e8f2e6f7f"
integrity sha512-H0fWsLTp2WBfKLBgwYT4OTfFly4Im/8B5f3ojDv1Kx//kiubVY0IQunP2Koc/fr/0wI7hj3IiBDbSrmKlrNgLQ==
esbuild-windows-32@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.14.54.tgz#f8aaf9a5667630b40f0fb3aa37bf01bbd340ce31"
integrity sha512-T+rdZW19ql9MjS7pixmZYVObd9G7kcaZo+sETqNH4RCkuuYSuv9AGHUVnPoP9hhuE1WM1ZimHz1CIBHBboLU7w==
esbuild-windows-64@0.14.47:
version "0.14.47"
resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.14.47.tgz#2564c3fcf0c23d701edb71af8c52d3be4cec5f8a"
integrity sha512-/Pk5jIEH34T68r8PweKRi77W49KwanZ8X6lr3vDAtOlH5EumPE4pBHqkCUdELanvsT14yMXLQ/C/8XPi1pAtkQ==
esbuild-windows-64@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.14.54.tgz#bf54b51bd3e9b0f1886ffdb224a4176031ea0af4"
integrity sha512-AoHTRBUuYwXtZhjXZbA1pGfTo8cJo3vZIcWGLiUcTNgHpJJMC1rVA44ZereBHMJtotyN71S8Qw0npiCIkW96cQ==
esbuild-windows-arm64@0.14.47:
version "0.14.47"
resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.47.tgz#86d9db1a22d83360f726ac5fba41c2f625db6878"
integrity sha512-HFSW2lnp62fl86/qPQlqw6asIwCnEsEoNIL1h2uVMgakddf+vUuMcCbtUY1i8sst7KkgHrVKCJQB33YhhOweCQ==
esbuild-windows-arm64@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.54.tgz#937d15675a15e4b0e4fafdbaa3a01a776a2be982"
integrity sha512-M0kuUvXhot1zOISQGXwWn6YtS+Y/1RT9WrVIOywZnJHo3jCDyewAc79aKNQWFCQm+xNHVTq9h8dZKvygoXQQRg==
esbuild@^0.14.27:
version "0.14.47"
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.14.47.tgz#0d6415f6bd8eb9e73a58f7f9ae04c5276cda0e4d"
integrity sha512-wI4ZiIfFxpkuxB8ju4MHrGwGLyp1+awEHAHVpx6w7a+1pmYIq8T9FGEVVwFo0iFierDoMj++Xq69GXWYn2EiwA==
esbuild@^0.14.47:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.14.54.tgz#8b44dcf2b0f1a66fc22459943dccf477535e9aa2"
integrity sha512-Cy9llcy8DvET5uznocPyqL3BFRrFXSVqbgpMJ9Wz8oVjZlh/zUSNbPRbov0VX7VxN2JH1Oa0uNxZ7eLRb62pJA==
optionalDependencies:
esbuild-android-64 "0.14.47"
esbuild-android-arm64 "0.14.47"
esbuild-darwin-64 "0.14.47"
esbuild-darwin-arm64 "0.14.47"
esbuild-freebsd-64 "0.14.47"
esbuild-freebsd-arm64 "0.14.47"
esbuild-linux-32 "0.14.47"
esbuild-linux-64 "0.14.47"
esbuild-linux-arm "0.14.47"
esbuild-linux-arm64 "0.14.47"
esbuild-linux-mips64le "0.14.47"
esbuild-linux-ppc64le "0.14.47"
esbuild-linux-riscv64 "0.14.47"
esbuild-linux-s390x "0.14.47"
esbuild-netbsd-64 "0.14.47"
esbuild-openbsd-64 "0.14.47"
esbuild-sunos-64 "0.14.47"
esbuild-windows-32 "0.14.47"
esbuild-windows-64 "0.14.47"
esbuild-windows-arm64 "0.14.47"
"@esbuild/linux-loong64" "0.14.54"
esbuild-android-64 "0.14.54"
esbuild-android-arm64 "0.14.54"
esbuild-darwin-64 "0.14.54"
esbuild-darwin-arm64 "0.14.54"
esbuild-freebsd-64 "0.14.54"
esbuild-freebsd-arm64 "0.14.54"
esbuild-linux-32 "0.14.54"
esbuild-linux-64 "0.14.54"
esbuild-linux-arm "0.14.54"
esbuild-linux-arm64 "0.14.54"
esbuild-linux-mips64le "0.14.54"
esbuild-linux-ppc64le "0.14.54"
esbuild-linux-riscv64 "0.14.54"
esbuild-linux-s390x "0.14.54"
esbuild-netbsd-64 "0.14.54"
esbuild-openbsd-64 "0.14.54"
esbuild-sunos-64 "0.14.54"
esbuild-windows-32 "0.14.54"
esbuild-windows-64 "0.14.54"
esbuild-windows-arm64 "0.14.54"
estree-walker@^2.0.1:
version "2.0.2"
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac"
integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==
execa@^5.1.1:
execa@^5.0.0, execa@^5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd"
integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==
@@ -558,6 +571,26 @@ human-signals@^2.1.0:
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0"
integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==
internal-ip@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-7.0.0.tgz#5b1c6a9d7e188aa73a1b69717daf50c8d8ed774f"
integrity sha512-qE4TeD4brqC45Vq/+VASeMiS1KRyfBkR6HT2sh9pZVVCzSjPkaCEfKFU+dL0PRv7NHJtvoKN2r82G6wTfzorkw==
dependencies:
default-gateway "^6.0.3"
ipaddr.js "^2.0.1"
is-ip "^3.1.0"
p-event "^4.2.0"
ip-regex@^4.0.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-4.3.0.tgz#687275ab0f57fa76978ff8f4dddc8a23d5990db5"
integrity sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==
ipaddr.js@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.0.1.tgz#eca256a7a877e917aeb368b0a7497ddf42ef81c0"
integrity sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==
is-binary-path@~2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09"
@@ -584,6 +617,13 @@ is-glob@^4.0.1, is-glob@~4.0.1:
dependencies:
is-extglob "^2.1.1"
is-ip@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/is-ip/-/is-ip-3.1.0.tgz#2ae5ddfafaf05cb8008a62093cf29734f657c5d8"
integrity sha512-35vd5necO7IitFPjd/YBeqwWnyDWbuLH9ZXQdMfDA8TEo7pv5X8yfrvVO3xbJbLUlERCMvf6X0hTUamQxCYJ9Q==
dependencies:
ip-regex "^4.0.0"
is-number@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
@@ -604,10 +644,10 @@ jiti@^1.13.0:
resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.14.0.tgz#5350fff532a4d891ca4bcd700c47c1f40e6ee326"
integrity sha512-4IwstlaKQc9vCTC+qUXLM1hajy2ImiL9KnLvVYiaHOtS/v3wRjhLlGl121AmgDgx/O43uKmxownJghS5XMya2A==
kleur@^4.1.4:
version "4.1.4"
resolved "https://registry.yarnpkg.com/kleur/-/kleur-4.1.4.tgz#8c202987d7e577766d039a8cd461934c01cda04d"
integrity sha512-8QADVssbrFjivHWQU7KkMgptGTl6WAcSdlbBPY4uNF+mWr6DGcKrvY2w4FQJoXch7+fKMjj0dRrL75vk3k23OA==
kleur@^4.1.5:
version "4.1.5"
resolved "https://registry.yarnpkg.com/kleur/-/kleur-4.1.5.tgz#95106101795f7050c6c650f350c683febddb1780"
integrity sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==
kolorist@^1.5.1:
version "1.5.1"
@@ -710,6 +750,18 @@ onetime@^5.1.2:
dependencies:
mimic-fn "^2.1.0"
p-event@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/p-event/-/p-event-4.2.0.tgz#af4b049c8acd91ae81083ebd1e6f5cae2044c1b5"
integrity sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ==
dependencies:
p-timeout "^3.1.0"
p-finally@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae"
integrity sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==
p-limit@^3.0.2:
version "3.1.0"
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b"
@@ -724,6 +776,13 @@ p-locate@^5.0.0:
dependencies:
p-limit "^3.0.2"
p-timeout@^3.1.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-3.2.0.tgz#c7e17abc971d2a7962ef83626b35d635acf23dfe"
integrity sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==
dependencies:
p-finally "^1.0.0"
path-exists@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
@@ -764,10 +823,10 @@ picomatch@^2.2.2:
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad"
integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==
postcss@^8.4.13:
version "8.4.14"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.14.tgz#ee9274d5622b4858c1007a74d76e42e56fd21caf"
integrity sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==
postcss@^8.4.16:
version "8.4.16"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.16.tgz#33a1d675fac39941f5f445db0de4db2b6e01d43c"
integrity sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==
dependencies:
nanoid "^3.3.4"
picocolors "^1.0.0"
@@ -785,7 +844,7 @@ readdirp@~3.6.0:
dependencies:
picomatch "^2.2.1"
resolve@^1.22.0:
resolve@^1.22.1:
version "1.22.1"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177"
integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==
@@ -799,10 +858,10 @@ reusify@^1.0.4:
resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
rollup@^2.59.0:
version "2.75.7"
resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.75.7.tgz#221ff11887ae271e37dcc649ba32ce1590aaa0b9"
integrity sha512-VSE1iy0eaAYNCxEXaleThdFXqZJ42qDBatAwrfnPlENEZ8erQ+0LYX4JXOLPceWfZpV1VtZwZ3dFCuOZiSyFtQ==
"rollup@>=2.75.6 <2.77.0 || ~2.77.0":
version "2.77.3"
resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.77.3.tgz#8f00418d3a2740036e15deb653bed1a90ee0cc12"
integrity sha512-/qxNTG7FbmefJWoeeYJFbHehJ2HNWnjkAFRKzWN/45eNBBF/r8lo992CwcJXEzyVxs5FmfId+vTSTQDb+bxA+g==
optionalDependencies:
fsevents "~2.3.2"
@@ -926,15 +985,15 @@ unocss@^0.39.3:
"@unocss/transformer-variant-group" "0.39.3"
"@unocss/vite" "0.39.3"
vite@^2.9.12:
version "2.9.12"
resolved "https://registry.yarnpkg.com/vite/-/vite-2.9.12.tgz#b1d636b0a8ac636afe9d83e3792d4895509a941b"
integrity sha512-suxC36dQo9Rq1qMB2qiRorNJtJAdxguu5TMvBHOc/F370KvqAe9t48vYp+/TbPKRNrMh/J55tOUmkuIqstZaew==
vite@^3.0.9:
version "3.0.9"
resolved "https://registry.yarnpkg.com/vite/-/vite-3.0.9.tgz#45fac22c2a5290a970f23d66c1aef56a04be8a30"
integrity sha512-waYABTM+G6DBTCpYAxvevpG50UOlZuynR0ckTK5PawNVt7ebX6X7wNXHaGIO6wYYFXSM7/WcuFuO2QzhBB6aMw==
dependencies:
esbuild "^0.14.27"
postcss "^8.4.13"
resolve "^1.22.0"
rollup "^2.59.0"
esbuild "^0.14.47"
postcss "^8.4.16"
resolve "^1.22.1"
rollup ">=2.75.6 <2.77.0 || ~2.77.0"
optionalDependencies:
fsevents "~2.3.2"

135
tooling/cli/Cargo.lock generated
View File

@@ -127,18 +127,6 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a32fd6af2b5827bce66c29053ba0e7c42b9dcab01835835058558c10851a46b"
[[package]]
name = "bicycle"
version = "0.1.0"
source = "git+https://github.com/BrainiumLLC/bicycle?rev=28080e0c6fa4067d9dd1b0f2b7322b6b32178e1f#28080e0c6fa4067d9dd1b0f2b7322b6b32178e1f"
dependencies = [
"handlebars 3.5.5",
"log",
"serde",
"serde_json",
"thiserror",
]
[[package]]
name = "bit_field"
version = "0.10.1"
@@ -199,16 +187,6 @@ dependencies = [
"byte-tools",
]
[[package]]
name = "bossy"
version = "0.2.1"
source = "git+https://github.com/lucasfernog/bossy?branch=fix/winapi-features#83ee04daddbc9b985b5f8dcf54d7229f0542d41d"
dependencies = [
"libc",
"log",
"winapi 0.3.9",
]
[[package]]
name = "bstr"
version = "0.2.17"
@@ -272,13 +250,10 @@ dependencies = [
[[package]]
name = "cargo-mobile"
version = "0.1.0"
source = "git+https://github.com/tauri-apps/cargo-mobile?branch=dev#32de2201d4a607c2d4b910cc2a33d052fe852bd8"
source = "git+https://github.com/tauri-apps/cargo-mobile?branch=dev#9733784cbb8af4fe0accc634463ff58424c88e62"
dependencies = [
"bicycle",
"bossy",
"cocoa",
"colored 1.9.3",
"const-utf16",
"core-foundation 0.7.0",
"deunicode",
"dunce",
@@ -286,20 +261,20 @@ dependencies = [
"english-numbers",
"env_logger 0.7.1",
"freedesktop_entry_parser",
"handlebars 3.5.5",
"heck 0.4.0",
"hit",
"home",
"ignore",
"indexmap",
"java-properties",
"lexical-core",
"libc",
"log",
"objc",
"objc_id",
"once-cell-regex",
"openssl",
"path_abs",
"reserved-names",
"serde",
"serde_json",
"structopt",
@@ -307,8 +282,8 @@ dependencies = [
"thiserror",
"toml",
"ureq",
"windows 0.26.0",
"yes-or-no",
"winapi 0.3.9",
"windows 0.39.0",
]
[[package]]
@@ -483,12 +458,6 @@ dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "const-utf16"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90feefab165fe011746e3be2f0708b7b180fcbd9f5054ff81a454d7bd93d8285"
[[package]]
name = "constant_time_eq"
version = "0.1.5"
@@ -1334,18 +1303,6 @@ version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "hit"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a8b280c3c6e2a2a51b3dde2f1071b28afff0ebce59e29443b1b391e7efe32fd"
dependencies = [
"bossy",
"log",
"once-cell-regex",
"thiserror",
]
[[package]]
name = "hmac"
version = "0.12.1"
@@ -2740,14 +2697,6 @@ dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "reserved-names"
version = "0.1.0"
source = "git+https://github.com/BrainiumLLC/reserved-names#cc46545db485b13851e9136280b7d8e5a95a9d18"
dependencies = [
"thiserror",
]
[[package]]
name = "ring"
version = "0.16.20"
@@ -3350,12 +3299,12 @@ version = "1.0.5"
dependencies = [
"anyhow",
"base64",
"bossy",
"cargo-mobile",
"clap 3.2.7",
"colored 2.0.0",
"ctrlc",
"dialoguer",
"dirs-next",
"env_logger 0.9.0",
"glob",
"handlebars 4.3.1",
@@ -4009,19 +3958,6 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows"
version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "347cdcaae1addebdff584aea1f9fbc0426dedbe1315f1dcf30c7a9876401cd25"
dependencies = [
"windows_aarch64_msvc 0.26.0",
"windows_i686_gnu 0.26.0",
"windows_i686_msvc 0.26.0",
"windows_x86_64_gnu 0.26.0",
"windows_x86_64_msvc 0.26.0",
]
[[package]]
name = "windows"
version = "0.37.0"
@@ -4036,6 +3972,19 @@ dependencies = [
"windows_x86_64_msvc 0.37.0",
]
[[package]]
name = "windows"
version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1c4bd0a50ac6020f65184721f758dba47bb9fbc2133df715ec74a237b26794a"
dependencies = [
"windows_aarch64_msvc 0.39.0",
"windows_i686_gnu 0.39.0",
"windows_i686_msvc 0.39.0",
"windows_x86_64_gnu 0.39.0",
"windows_x86_64_msvc 0.39.0",
]
[[package]]
name = "windows-implement"
version = "0.37.0"
@@ -4065,12 +4014,6 @@ version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3263d25f1170419995b78ff10c06b949e8a986c35c208dc24333c64753a87169"
[[package]]
name = "windows_aarch64_msvc"
version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7758986b022add546ae53ccad31f4852ce6bd2e2c2d3cc2b1d7d06dea0b90da"
[[package]]
name = "windows_aarch64_msvc"
version = "0.36.1"
@@ -4084,10 +4027,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2623277cb2d1c216ba3b578c0f3cf9cdebeddb6e66b1b218bb33596ea7769c3a"
[[package]]
name = "windows_i686_gnu"
version = "0.26.0"
name = "windows_aarch64_msvc"
version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29261214caab8e589f61031ba1ccd5c3c25e61db2118a3aec4459f58ff798726"
checksum = "ec7711666096bd4096ffa835238905bb33fb87267910e154b18b44eaabb340f2"
[[package]]
name = "windows_i686_gnu"
@@ -4102,10 +4045,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3925fd0b0b804730d44d4b6278c50f9699703ec49bcd628020f46f4ba07d9e1"
[[package]]
name = "windows_i686_msvc"
version = "0.26.0"
name = "windows_i686_gnu"
version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43984fb3b944743142112ae926e7adeccb60f35bb81d43114f4d0fe2871f60ba"
checksum = "763fc57100a5f7042e3057e7e8d9bdd7860d330070251a73d003563a3bb49e1b"
[[package]]
name = "windows_i686_msvc"
@@ -4120,10 +4063,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce907ac74fe331b524c1298683efbf598bb031bc84d5e274db2083696d07c57c"
[[package]]
name = "windows_x86_64_gnu"
version = "0.26.0"
name = "windows_i686_msvc"
version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a80fc90e1ad19769e596a3f58d0776319059e21cac9069a5a2a791362ce7190"
checksum = "7bc7cbfe58828921e10a9f446fcaaf649204dcfe6c1ddd712c5eebae6bda1106"
[[package]]
name = "windows_x86_64_gnu"
@@ -4138,10 +4081,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2babfba0828f2e6b32457d5341427dcbb577ceef556273229959ac23a10af33d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.26.0"
name = "windows_x86_64_gnu"
version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc24ddac19a0cf02ad2b32d8897f202fc1a13ef285e2d4774e6610783cc8398f"
checksum = "6868c165637d653ae1e8dc4d82c25d4f97dd6605eaa8d784b5c6e0ab2a252b65"
[[package]]
name = "windows_x86_64_msvc"
@@ -4155,6 +4098,12 @@ version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4dd6dc7df2d84cf7b33822ed5b86318fb1781948e9663bacd047fc9dd52259d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e4d40883ae9cae962787ca76ba76390ffa29214667a111db9e0a1ad8377e809"
[[package]]
name = "winreg"
version = "0.10.1"
@@ -4189,16 +4138,6 @@ version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3"
[[package]]
name = "yes-or-no"
version = "0.1.0"
source = "git+https://github.com/BrainiumLLC/yes-or-no#71d932693601fcf93ff906b90ad61f85b52117c5"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "zeroize"
version = "1.5.5"

View File

@@ -26,14 +26,11 @@ include = [
name = "cargo-tauri"
path = "src/main.rs"
[patch.crates-io]
bossy = { git = "https://github.com/lucasfernog/bossy", branch = "fix/winapi-features" }
[dependencies]
# cargo-mobile = { path = "../../../cargo-mobile/", default-features = false }
cargo-mobile = { git = "https://github.com/tauri-apps/cargo-mobile", branch = "dev", default-features = false }
bossy = "0.2"
textwrap = { version = "0.11.0", features = ["term_size"] }
dirs-next = "2.0"
thiserror = "1"
clap = { version = "3.2", features = [ "derive" ] }
anyhow = "1.0"

View File

@@ -2433,7 +2433,7 @@
"type": "object",
"properties": {
"developmentTeam": {
"description": "The development team. This value is required for iOS development because code signing is enforced. The `APPLE_DEVELOPMENT_TEAM` environment variable can be set to overwrite it.",
"description": "The development team. This value is required for iOS development because code signing is enforced. The `TAURI_APPLE_DEVELOPMENT_TEAM` environment variable can be set to overwrite it.",
"type": [
"string",
"null"

View File

@@ -83,6 +83,8 @@ pub fn command(mut options: Options) -> Result<()> {
let config_guard = config.lock().unwrap();
let config_ = config_guard.as_ref().unwrap();
let mut interface = AppInterface::new(config_)?;
let bundle_identifier_source = match config_.find_bundle_identifier_overwriter() {
Some(source) if source == MERGE_CONFIG_EXTENSION_NAME => merge_config_path.unwrap_or(source),
Some(source) => source,
@@ -154,7 +156,6 @@ pub fn command(mut options: Options) -> Result<()> {
list.extend(config_.build.features.clone().unwrap_or_default());
}
let mut interface = AppInterface::new(config_)?;
let app_settings = interface.app_settings();
let interface_options = options.clone().into();

View File

@@ -49,7 +49,7 @@ pub struct Options {
pub features: Option<Vec<String>>,
/// Exit on panic
#[clap(short, long)]
exit_on_panic: bool,
pub exit_on_panic: bool,
/// JSON string or path to JSON file to merge with tauri.conf.json
#[clap(short, long)]
pub config: Option<String>,
@@ -74,6 +74,15 @@ pub fn command(options: Options) -> Result<()> {
}
fn command_internal(mut options: Options) -> Result<()> {
let mut interface = setup(&mut options)?;
let exit_on_panic = options.exit_on_panic;
let no_watch = options.no_watch;
interface.dev(options.into(), move |status, reason| {
on_dev_exit(status, reason, exit_on_panic, no_watch)
})
}
pub fn setup(options: &mut Options) -> Result<AppInterface> {
let tauri_path = tauri_dir();
options.config = if let Some(config) = &options.config {
Some(if config.starts_with('{') {
@@ -89,6 +98,8 @@ fn command_internal(mut options: Options) -> Result<()> {
let config = get_config(options.config.as_deref())?;
let interface = AppInterface::new(config.lock().unwrap().as_ref().unwrap())?;
if let Some(before_dev) = config
.lock()
.unwrap()
@@ -261,13 +272,7 @@ fn command_internal(mut options: Options) -> Result<()> {
}
}
let mut interface = AppInterface::new(config.lock().unwrap().as_ref().unwrap())?;
let exit_on_panic = options.exit_on_panic;
let no_watch = options.no_watch;
interface.dev(options.into(), move |status, reason| {
on_dev_exit(status, reason, exit_on_panic, no_watch)
})
Ok(interface)
}
fn on_dev_exit(status: ExitStatus, reason: ExitReason, exit_on_panic: bool, no_watch: bool) {

View File

@@ -0,0 +1,363 @@
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
// taken from https://github.com/rust-lang/cargo/blob/b0c9586f4cbf426914df47c65de38ea323772c74/src/cargo/util/flock.rs
#![allow(dead_code)]
use std::fs::{create_dir_all, File, OpenOptions};
use std::io;
use std::io::{Read, Seek, SeekFrom, Write};
use std::path::{Path, PathBuf};
use crate::Result;
use anyhow::Context as _;
use sys::*;
#[derive(Debug)]
pub struct FileLock {
f: Option<File>,
path: PathBuf,
state: State,
}
#[derive(PartialEq, Debug)]
enum State {
Unlocked,
Shared,
Exclusive,
}
impl FileLock {
/// Returns the underlying file handle of this lock.
pub fn file(&self) -> &File {
self.f.as_ref().unwrap()
}
/// Returns the underlying path that this lock points to.
///
/// Note that special care must be taken to ensure that the path is not
/// referenced outside the lifetime of this lock.
pub fn path(&self) -> &Path {
assert_ne!(self.state, State::Unlocked);
&self.path
}
/// Returns the parent path containing this file
pub fn parent(&self) -> &Path {
assert_ne!(self.state, State::Unlocked);
self.path.parent().unwrap()
}
}
impl Read for FileLock {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.file().read(buf)
}
}
impl Seek for FileLock {
fn seek(&mut self, to: SeekFrom) -> io::Result<u64> {
self.file().seek(to)
}
}
impl Write for FileLock {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.file().write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.file().flush()
}
}
impl Drop for FileLock {
fn drop(&mut self) {
if self.state != State::Unlocked {
if let Some(f) = self.f.take() {
let _ = unlock(&f);
}
}
}
}
/// Opens exclusive access to a file, returning the locked version of a
/// file.
///
/// This function will create a file at `path` if it doesn't already exist
/// (including intermediate directories), and then it will acquire an
/// exclusive lock on `path`. If the process must block waiting for the
/// lock, the `msg` is logged.
///
/// The returned file can be accessed to look at the path and also has
/// read/write access to the underlying file.
pub fn open_rw<P>(path: P, msg: &str) -> Result<FileLock>
where
P: AsRef<Path>,
{
open(
path.as_ref(),
OpenOptions::new().read(true).write(true).create(true),
State::Exclusive,
msg,
)
}
/// Opens shared access to a file, returning the locked version of a file.
///
/// This function will fail if `path` doesn't already exist, but if it does
/// then it will acquire a shared lock on `path`. If the process must block
/// waiting for the lock, the `msg` is logged.
///
/// The returned file can be accessed to look at the path and also has read
/// access to the underlying file. Any writes to the file will return an
/// error.
pub fn open_ro<P>(path: P, msg: &str) -> Result<FileLock>
where
P: AsRef<Path>,
{
open(
path.as_ref(),
OpenOptions::new().read(true),
State::Shared,
msg,
)
}
fn open(path: &Path, opts: &OpenOptions, state: State, msg: &str) -> Result<FileLock> {
// If we want an exclusive lock then if we fail because of NotFound it's
// likely because an intermediate directory didn't exist, so try to
// create the directory and then continue.
let f = opts
.open(&path)
.or_else(|e| {
if e.kind() == io::ErrorKind::NotFound && state == State::Exclusive {
create_dir_all(path.parent().unwrap())?;
Ok(opts.open(&path)?)
} else {
Err(anyhow::Error::from(e))
}
})
.with_context(|| format!("failed to open: {}", path.display()))?;
match state {
State::Exclusive => {
acquire(msg, path, &|| try_lock_exclusive(&f), &|| {
lock_exclusive(&f)
})?;
}
State::Shared => {
acquire(msg, path, &|| try_lock_shared(&f), &|| lock_shared(&f))?;
}
State::Unlocked => {}
}
Ok(FileLock {
f: Some(f),
path: path.to_path_buf(),
state,
})
}
/// Acquires a lock on a file in a "nice" manner.
///
/// Almost all long-running blocking actions in Cargo have a status message
/// associated with them as we're not sure how long they'll take. Whenever a
/// conflicted file lock happens, this is the case (we're not sure when the lock
/// will be released).
///
/// This function will acquire the lock on a `path`, printing out a nice message
/// to the console if we have to wait for it. It will first attempt to use `try`
/// to acquire a lock on the crate, and in the case of contention it will emit a
/// status message based on `msg` to `config`'s shell, and then use `block` to
/// block waiting to acquire a lock.
///
/// Returns an error if the lock could not be acquired or if any error other
/// than a contention error happens.
fn acquire(
msg: &str,
path: &Path,
lock_try: &dyn Fn() -> io::Result<()>,
lock_block: &dyn Fn() -> io::Result<()>,
) -> Result<()> {
// File locking on Unix is currently implemented via `flock`, which is known
// to be broken on NFS. We could in theory just ignore errors that happen on
// NFS, but apparently the failure mode [1] for `flock` on NFS is **blocking
// forever**, even if the "non-blocking" flag is passed!
//
// As a result, we just skip all file locks entirely on NFS mounts. That
// should avoid calling any `flock` functions at all, and it wouldn't work
// there anyway.
//
// [1]: https://github.com/rust-lang/cargo/issues/2615
if is_on_nfs_mount(path) {
return Ok(());
}
match lock_try() {
Ok(()) => return Ok(()),
// In addition to ignoring NFS which is commonly not working we also
// just ignore locking on filesystems that look like they don't
// implement file locking.
Err(e) if error_unsupported(&e) => return Ok(()),
Err(e) => {
if !error_contended(&e) {
let e = anyhow::Error::from(e);
let cx = format!("failed to lock file: {}", path.display());
return Err(e.context(cx));
}
}
}
let msg = format!("waiting for file lock on {}", msg);
log::info!(action = "Blocking"; "{}", &msg);
lock_block().with_context(|| format!("failed to lock file: {}", path.display()))?;
return Ok(());
#[cfg(all(target_os = "linux", not(target_env = "musl")))]
fn is_on_nfs_mount(path: &Path) -> bool {
use std::ffi::CString;
use std::mem;
use std::os::unix::prelude::*;
let path = match CString::new(path.as_os_str().as_bytes()) {
Ok(path) => path,
Err(_) => return false,
};
unsafe {
let mut buf: libc::statfs = mem::zeroed();
let r = libc::statfs(path.as_ptr(), &mut buf);
r == 0 && buf.f_type as u32 == libc::NFS_SUPER_MAGIC as u32
}
}
#[cfg(any(not(target_os = "linux"), target_env = "musl"))]
fn is_on_nfs_mount(_path: &Path) -> bool {
false
}
}
#[cfg(unix)]
mod sys {
use std::fs::File;
use std::io::{Error, Result};
use std::os::unix::io::AsRawFd;
pub(super) fn lock_shared(file: &File) -> Result<()> {
flock(file, libc::LOCK_SH)
}
pub(super) fn lock_exclusive(file: &File) -> Result<()> {
flock(file, libc::LOCK_EX)
}
pub(super) fn try_lock_shared(file: &File) -> Result<()> {
flock(file, libc::LOCK_SH | libc::LOCK_NB)
}
pub(super) fn try_lock_exclusive(file: &File) -> Result<()> {
flock(file, libc::LOCK_EX | libc::LOCK_NB)
}
pub(super) fn unlock(file: &File) -> Result<()> {
flock(file, libc::LOCK_UN)
}
pub(super) fn error_contended(err: &Error) -> bool {
err.raw_os_error().map_or(false, |x| x == libc::EWOULDBLOCK)
}
pub(super) fn error_unsupported(err: &Error) -> bool {
match err.raw_os_error() {
// Unfortunately, depending on the target, these may or may not be the same.
// For targets in which they are the same, the duplicate pattern causes a warning.
#[allow(unreachable_patterns)]
Some(libc::ENOTSUP | libc::EOPNOTSUPP) => true,
Some(libc::ENOSYS) => true,
_ => false,
}
}
#[cfg(not(target_os = "solaris"))]
fn flock(file: &File, flag: libc::c_int) -> Result<()> {
let ret = unsafe { libc::flock(file.as_raw_fd(), flag) };
if ret < 0 {
Err(Error::last_os_error())
} else {
Ok(())
}
}
#[cfg(target_os = "solaris")]
fn flock(file: &File, flag: libc::c_int) -> Result<()> {
// Solaris lacks flock(), so simply succeed with a no-op
Ok(())
}
}
#[cfg(windows)]
mod sys {
use std::fs::File;
use std::io::{Error, Result};
use std::mem;
use std::os::windows::io::AsRawHandle;
use winapi::shared::minwindef::DWORD;
use winapi::shared::winerror::{ERROR_INVALID_FUNCTION, ERROR_LOCK_VIOLATION};
use winapi::um::fileapi::{LockFileEx, UnlockFile};
use winapi::um::minwinbase::{LOCKFILE_EXCLUSIVE_LOCK, LOCKFILE_FAIL_IMMEDIATELY};
pub(super) fn lock_shared(file: &File) -> Result<()> {
lock_file(file, 0)
}
pub(super) fn lock_exclusive(file: &File) -> Result<()> {
lock_file(file, LOCKFILE_EXCLUSIVE_LOCK)
}
pub(super) fn try_lock_shared(file: &File) -> Result<()> {
lock_file(file, LOCKFILE_FAIL_IMMEDIATELY)
}
pub(super) fn try_lock_exclusive(file: &File) -> Result<()> {
lock_file(file, LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY)
}
pub(super) fn error_contended(err: &Error) -> bool {
err
.raw_os_error()
.map_or(false, |x| x == ERROR_LOCK_VIOLATION as i32)
}
pub(super) fn error_unsupported(err: &Error) -> bool {
err
.raw_os_error()
.map_or(false, |x| x == ERROR_INVALID_FUNCTION as i32)
}
pub(super) fn unlock(file: &File) -> Result<()> {
unsafe {
let ret = UnlockFile(file.as_raw_handle(), 0, 0, !0, !0);
if ret == 0 {
Err(Error::last_os_error())
} else {
Ok(())
}
}
}
fn lock_file(file: &File, flags: DWORD) -> Result<()> {
unsafe {
let mut overlapped = mem::zeroed();
let ret = LockFileEx(file.as_raw_handle(), flags, 0, !0, !0, &mut overlapped);
if ret == 0 {
Err(Error::last_os_error())
} else {
Ok(())
}
}
}
}

View File

@@ -4,6 +4,7 @@
pub mod app_paths;
pub mod config;
pub mod flock;
pub mod framework;
pub mod template;
pub mod updater_signature;

View File

@@ -12,7 +12,12 @@ use std::{
use crate::helpers::config::Config;
use tauri_bundler::bundle::{PackageType, Settings, SettingsBuilder};
pub use rust::{Options, Rust as AppInterface};
pub use rust::{MobileOptions, Options, Rust as AppInterface};
pub trait DevProcess {
fn kill(&mut self) -> std::io::Result<()>;
fn try_wait(&mut self) -> std::io::Result<Option<ExitStatus>>;
}
pub trait AppSettings {
fn get_package_settings(&self) -> tauri_bundler::PackageSettings;
@@ -83,4 +88,9 @@ pub trait Interface: Sized {
options: Options,
on_exit: F,
) -> crate::Result<()>;
fn mobile_dev<R: Fn(MobileOptions) -> crate::Result<Box<dyn DevProcess>>>(
&mut self,
options: MobileOptions,
runner: R,
) -> crate::Result<()>;
}

View File

@@ -3,6 +3,7 @@
// SPDX-License-Identifier: MIT
use std::{
collections::HashMap,
ffi::OsStr,
fs::{File, FileType},
io::{Read, Write},
@@ -10,7 +11,6 @@ use std::{
process::ExitStatus,
str::FromStr,
sync::{
atomic::{AtomicBool, Ordering},
mpsc::{channel, sync_channel},
Arc, Mutex,
},
@@ -24,13 +24,12 @@ use log::warn;
use log::{debug, info};
use notify::{watcher, DebouncedEvent, RecursiveMode, Watcher};
use serde::Deserialize;
use shared_child::SharedChild;
use tauri_bundler::{
AppCategory, BundleBinary, BundleSettings, DebianSettings, MacOsSettings, PackageSettings,
UpdaterSettings, WindowsSettings,
};
use super::{AppSettings, ExitReason, Interface};
use super::{AppSettings, DevProcess, ExitReason, Interface};
use crate::helpers::{
app_paths::tauri_dir,
config::{reload as reload_config, wix_settings, Config},
@@ -40,7 +39,7 @@ mod desktop;
mod manifest;
use manifest::{rewrite_manifest, Manifest};
#[derive(Debug, Clone)]
#[derive(Debug, Default, Clone)]
pub struct Options {
pub runner: Option<String>,
pub debug: bool,
@@ -79,30 +78,13 @@ impl From<crate::dev::Options> for Options {
}
}
pub struct DevChild {
manually_killed_app: Arc<AtomicBool>,
build_child: Arc<SharedChild>,
app_child: Arc<Mutex<Option<Arc<SharedChild>>>>,
}
impl DevChild {
fn kill(&self) -> std::io::Result<()> {
if let Some(child) = &*self.app_child.lock().unwrap() {
child.kill()?;
} else {
self.build_child.kill()?;
}
self.manually_killed_app.store(true, Ordering::Relaxed);
Ok(())
}
fn try_wait(&self) -> std::io::Result<Option<ExitStatus>> {
if let Some(child) = &*self.app_child.lock().unwrap() {
child.try_wait()
} else {
self.build_child.try_wait()
}
}
#[derive(Debug, Clone)]
pub struct MobileOptions {
pub debug: bool,
pub features: Option<Vec<String>>,
pub args: Vec<String>,
pub config: Option<String>,
pub no_watch: bool,
}
#[derive(Debug)]
@@ -144,8 +126,10 @@ impl Interface for Rust {
std::env::set_var("MACOSX_DEPLOYMENT_TARGET", minimum_system_version);
}
let app_settings = RustAppSettings::new(config, manifest)?;
Ok(Self {
app_settings: RustAppSettings::new(config, manifest)?,
app_settings,
config_features: config.build.features.clone().unwrap_or_default(),
product_name: config.package.product_name.clone(),
available_targets: None,
@@ -173,28 +157,56 @@ impl Interface for Rust {
fn dev<F: Fn(ExitStatus, ExitReason) + Send + Sync + 'static>(
&mut self,
options: Options,
mut options: Options,
on_exit: F,
) -> crate::Result<()> {
let on_exit = Arc::new(on_exit);
let on_exit_ = on_exit.clone();
let run_args = dev_options(
&mut options.args,
&mut options.features,
self.app_settings.manifest.features(),
);
if options.no_watch {
let (tx, rx) = sync_channel(1);
self.run_dev(options, move |status, reason| {
self.run_dev(options, run_args, move |status, reason| {
tx.send(()).unwrap();
on_exit_(status, reason)
on_exit(status, reason)
})?;
rx.recv().unwrap();
Ok(())
} else {
let child = self.run_dev(options.clone(), move |status, reason| {
on_exit_(status, reason)
})?;
let config = options.config.clone();
let run = Arc::new(|rust: &mut Rust| {
let on_exit = on_exit.clone();
rust.run_dev(options.clone(), run_args.clone(), move |status, reason| {
on_exit(status, reason)
})
});
self.run_dev_watcher(config, run)
}
}
self.run_dev_watcher(child, options, on_exit)
fn mobile_dev<R: Fn(MobileOptions) -> crate::Result<Box<dyn DevProcess>>>(
&mut self,
mut options: MobileOptions,
runner: R,
) -> crate::Result<()> {
dev_options(
&mut options.args,
&mut options.features,
self.app_settings.manifest.features(),
);
if options.no_watch {
runner(options)?;
Ok(())
} else {
let config = options.config.clone();
let run = Arc::new(|_rust: &mut Rust| runner(options.clone()));
self.run_dev_watcher(config, run)
}
}
}
@@ -223,50 +235,54 @@ fn lookup<F: FnMut(FileType, PathBuf)>(dir: &Path, mut f: F) {
}
}
fn dev_options(
args: &mut Vec<String>,
features: &mut Option<Vec<String>>,
manifest_features: HashMap<String, Vec<String>>,
) -> Vec<String> {
if !args.contains(&"--no-default-features".into()) {
let enable_features: Vec<String> = manifest_features
.get("default")
.cloned()
.unwrap_or_default()
.into_iter()
.filter(|feature| {
if let Some(manifest_feature) = manifest_features.get(feature) {
!manifest_feature.contains(&"tauri/custom-protocol".into())
} else {
feature != "tauri/custom-protocol"
}
})
.collect();
args.push("--no-default-features".into());
if !enable_features.is_empty() {
features.get_or_insert(Vec::new()).extend(enable_features);
}
}
let mut dev_args = Vec::new();
let mut run_args = Vec::new();
let mut reached_run_args = false;
for arg in args.clone() {
if reached_run_args {
run_args.push(arg);
} else if arg == "--" {
reached_run_args = true;
} else {
dev_args.push(arg);
}
}
*args = dev_args;
run_args
}
impl Rust {
fn run_dev<F: Fn(ExitStatus, ExitReason) + Send + Sync + 'static>(
&mut self,
mut options: Options,
options: Options,
run_args: Vec<String>,
on_exit: F,
) -> crate::Result<DevChild> {
if !options.args.contains(&"--no-default-features".into()) {
let manifest_features = self.app_settings.manifest.features();
let enable_features: Vec<String> = manifest_features
.get("default")
.cloned()
.unwrap_or_default()
.into_iter()
.filter(|feature| {
if let Some(manifest_feature) = manifest_features.get(feature) {
!manifest_feature.contains(&"tauri/custom-protocol".into())
} else {
feature != "tauri/custom-protocol"
}
})
.collect();
options.args.push("--no-default-features".into());
if !enable_features.is_empty() {
options
.features
.get_or_insert(Vec::new())
.extend(enable_features);
}
}
let mut args = Vec::new();
let mut run_args = Vec::new();
let mut reached_run_args = false;
for arg in options.args.clone() {
if reached_run_args {
run_args.push(arg);
} else if arg == "--" {
reached_run_args = true;
} else {
args.push(arg);
}
}
options.args = args;
) -> crate::Result<Box<dyn DevProcess>> {
desktop::run_dev(
options,
run_args,
@@ -276,14 +292,16 @@ impl Rust {
self.product_name.clone(),
on_exit,
)
.map(|c| Box::new(c) as Box<dyn DevProcess>)
}
fn run_dev_watcher<F: Fn(ExitStatus, ExitReason) + Send + Sync + 'static>(
fn run_dev_watcher<F: Fn(&mut Rust) -> crate::Result<Box<dyn DevProcess>>>(
&mut self,
child: DevChild,
options: Options,
on_exit: Arc<F>,
config: Option<String>,
run: Arc<F>,
) -> crate::Result<()> {
let child = run(self)?;
let process = Arc::new(Mutex::new(child));
let (tx, rx) = channel();
let tauri_path = tauri_dir();
@@ -326,7 +344,7 @@ impl Rust {
}
loop {
let on_exit = on_exit.clone();
let run = run.clone();
if let Ok(event) = rx.recv() {
let event_path = match event {
DebouncedEvent::Create(path) => Some(path),
@@ -338,7 +356,7 @@ impl Rust {
if let Some(event_path) = event_path {
if event_path.file_name() == Some(OsStr::new("tauri.conf.json")) {
let config = reload_config(options.config.as_deref())?;
let config = reload_config(config.as_deref())?;
self.app_settings.manifest =
rewrite_manifest(config.lock().unwrap().as_ref().unwrap())?;
} else {
@@ -353,9 +371,7 @@ impl Rust {
break;
}
}
*p = self.run_dev(options.clone(), move |status, reason| {
on_exit(status, reason)
})?;
*p = run(self)?;
}
}
}

View File

@@ -1,7 +1,8 @@
use super::{AppSettings, DevChild, ExitReason, Options, RustAppSettings, Target};
use super::{AppSettings, DevProcess, ExitReason, Options, RustAppSettings, Target};
use crate::CommandExt;
use anyhow::Context;
#[cfg(target_os = "linux")]
use heck::ToKebabCase;
use shared_child::SharedChild;
use std::{
@@ -15,6 +16,34 @@ use std::{
},
};
pub struct DevChild {
manually_killed_app: Arc<AtomicBool>,
build_child: Option<Arc<SharedChild>>,
app_child: Arc<Mutex<Option<Arc<SharedChild>>>>,
}
impl DevProcess for DevChild {
fn kill(&mut self) -> std::io::Result<()> {
if let Some(child) = &*self.app_child.lock().unwrap() {
child.kill()?;
} else if let Some(child) = &self.build_child {
child.kill()?;
}
self.manually_killed_app.store(true, Ordering::Relaxed);
Ok(())
}
fn try_wait(&mut self) -> std::io::Result<Option<ExitStatus>> {
if let Some(child) = &*self.app_child.lock().unwrap() {
child.try_wait()
} else if let Some(child) = &self.build_child {
child.try_wait()
} else {
unreachable!()
}
}
}
pub fn run_dev<F: Fn(ExitStatus, ExitReason) + Send + Sync + 'static>(
options: Options,
run_args: Vec<String>,
@@ -23,7 +52,7 @@ pub fn run_dev<F: Fn(ExitStatus, ExitReason) + Send + Sync + 'static>(
app_settings: &RustAppSettings,
product_name: Option<String>,
on_exit: F,
) -> crate::Result<DevChild> {
) -> crate::Result<impl DevProcess> {
let bin_path = app_settings.app_binary_path(&options)?;
let manually_killed_app = Arc::new(AtomicBool::default());
@@ -73,7 +102,7 @@ pub fn run_dev<F: Fn(ExitStatus, ExitReason) + Send + Sync + 'static>(
Ok(DevChild {
manually_killed_app,
build_child,
build_child: Some(build_child),
app_child,
})
}

View File

@@ -14,7 +14,7 @@ mod mobile;
mod plugin;
mod signer;
use clap::{FromArgMatches, IntoApp, Parser, Subcommand};
use clap::{FromArgMatches, IntoApp, Parser, Subcommand, ValueEnum};
use env_logger::fmt::Color;
use env_logger::Builder;
use log::{debug, log_enabled, Level};
@@ -23,9 +23,33 @@ use std::io::{BufReader, Write};
use std::process::{exit, Command, ExitStatus, Output, Stdio};
use std::{
ffi::OsString,
fmt::Display,
sync::{Arc, Mutex},
};
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
pub enum RunMode {
Desktop,
#[cfg(target_os = "macos")]
Ios,
Android,
}
impl Display for RunMode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
Self::Desktop => "desktop",
#[cfg(target_os = "macos")]
Self::Ios => "iOS",
Self::Android => "android",
}
)
}
}
#[derive(Deserialize)]
pub struct VersionMetadata {
tauri: String,

View File

@@ -6,10 +6,11 @@ use cargo_mobile::{
android::{
adb,
config::{Config as AndroidConfig, Metadata as AndroidMetadata},
device::Device,
device::{Device, RunError},
env::{Env, Error as EnvError},
target::{BuildError, Target},
},
config::Config,
device::PromptError,
opts::{NoiseLevel, Profile},
os,
@@ -19,12 +20,15 @@ use cargo_mobile::{
use clap::{Parser, Subcommand};
use super::{
ensure_init, get_config, get_metadata,
ensure_init, get_config,
init::{command as init_command, Options as InitOptions},
Target as MobileTarget,
write_options, CliOptions, DevChild, Target as MobileTarget,
};
use crate::{
helpers::{config::get as get_tauri_config, flock},
interface::{AppSettings, DevProcess, Interface, MobileOptions, Options as InterfaceOptions},
Result,
};
use crate::helpers::config::get as get_tauri_config;
use crate::Result;
pub(crate) mod project;
@@ -32,16 +36,24 @@ pub(crate) mod project;
enum Error {
#[error(transparent)]
EnvInitFailed(EnvError),
#[error(transparent)]
InitDotCargo(super::init::Error),
#[error("invalid tauri configuration: {0}")]
InvalidTauriConfig(String),
#[error("{0}")]
ProjectNotInitialized(String),
#[error(transparent)]
OpenFailed(os::OpenFileError),
#[error("{0}")]
DevFailed(String),
#[error(transparent)]
BuildFailed(BuildError),
#[error(transparent)]
RunFailed(RunError),
#[error("{0}")]
TargetInvalid(String),
#[error(transparent)]
FailedToPromptForDevice(PromptError<adb::device_list::Error>),
}
#[derive(Parser)]
@@ -68,9 +80,44 @@ pub struct BuildOptions {
value_parser(clap::builder::PossibleValuesParser::new(["aarch64", "armv7", "i686", "x86_64"]))
)]
targets: Option<Vec<String>>,
/// Builds with the debug flag
/// Builds with the release flag
#[clap(short, long)]
debug: bool,
release: bool,
}
#[derive(Debug, Clone, Parser)]
#[clap(about = "Android dev")]
pub struct DevOptions {
/// List of cargo features to activate
#[clap(short, long, multiple_occurrences(true), multiple_values(true))]
pub features: Option<Vec<String>>,
/// Exit on panic
#[clap(short, long)]
exit_on_panic: bool,
/// JSON string or path to JSON file to merge with tauri.conf.json
#[clap(short, long)]
pub config: Option<String>,
/// Disable the file watcher
#[clap(long)]
pub no_watch: bool,
/// Open Android Studio instead of trying to run on a connected device
#[clap(short, long)]
pub open: bool,
}
impl From<DevOptions> for crate::dev::Options {
fn from(options: DevOptions) -> Self {
Self {
runner: None,
target: None,
features: options.features,
exit_on_panic: options.exit_on_panic,
config: options.config,
release_mode: false,
args: Vec::new(),
no_watch: options.no_watch,
}
}
}
#[derive(Subcommand)]
@@ -78,6 +125,7 @@ enum Commands {
Init(InitOptions),
/// Open project in Android Studio
Open,
Dev(DevOptions),
#[clap(hide(true))]
Build(BuildOptions),
}
@@ -87,76 +135,182 @@ pub fn command(cli: Cli) -> Result<()> {
Commands::Init(options) => init_command(options, MobileTarget::Android)?,
Commands::Open => open()?,
Commands::Build(options) => build(options)?,
Commands::Dev(options) => dev(options)?,
}
Ok(())
}
fn with_config(
f: impl FnOnce(&AndroidConfig, &AndroidMetadata) -> Result<(), Error>,
) -> Result<(), Error> {
let tauri_config =
get_tauri_config(None).map_err(|e| Error::InvalidTauriConfig(e.to_string()))?;
let tauri_config_guard = tauri_config.lock().unwrap();
let tauri_config_ = tauri_config_guard.as_ref().unwrap();
let config = get_config(tauri_config_);
let metadata = get_metadata(tauri_config_);
f(config.android(), metadata.android())
fn with_config<T>(
f: impl FnOnce(&Config, &AndroidConfig, &AndroidMetadata) -> Result<T, Error>,
) -> Result<T, Error> {
let (config, metadata) = {
let tauri_config =
get_tauri_config(None).map_err(|e| Error::InvalidTauriConfig(e.to_string()))?;
let tauri_config_guard = tauri_config.lock().unwrap();
let tauri_config_ = tauri_config_guard.as_ref().unwrap();
get_config(tauri_config_)
};
f(&config, config.android(), metadata.android())
}
fn device_prompt<'a>(env: &'_ Env) -> Result<Device<'a>, PromptError<adb::device_list::Error>> {
let device_list =
adb::device_list(env).map_err(|cause| PromptError::detection_failed("Android", cause))?;
if !device_list.is_empty() {
let index = if device_list.len() > 1 {
prompt::list(
concat!("Detected ", "Android", " devices"),
device_list.iter(),
"device",
None,
"Device",
)
.map_err(|cause| PromptError::prompt_failed("Android", cause))?
} else {
0
};
let device = device_list.into_iter().nth(index).unwrap();
println!(
"Detected connected device: {} with target {:?}",
device,
device.target().triple,
);
Ok(device)
} else {
Err(PromptError::none_detected("Android"))
}
}
fn dev(options: DevOptions) -> Result<()> {
with_config(|_, config, _metadata| {
run_dev(options, config).map_err(|e| Error::DevFailed(e.to_string()))
})
.map_err(Into::into)
}
fn run_dev(options: DevOptions, config: &AndroidConfig) -> Result<()> {
let mut dev_options = options.clone().into();
let mut interface = crate::dev::setup(&mut dev_options)?;
let bundle_identifier = {
let tauri_config =
get_tauri_config(None).map_err(|e| Error::InvalidTauriConfig(e.to_string()))?;
let tauri_config_guard = tauri_config.lock().unwrap();
let tauri_config_ = tauri_config_guard.as_ref().unwrap();
tauri_config_.tauri.bundle.identifier.clone()
};
let app_settings = interface.app_settings();
let bin_path = app_settings.app_binary_path(&InterfaceOptions {
debug: !dev_options.release_mode,
..Default::default()
})?;
let out_dir = bin_path.parent().unwrap();
let _lock = flock::open_rw(&out_dir.join("lock").with_extension("android"), "Android")?;
let open = options.open;
interface.mobile_dev(
MobileOptions {
debug: true,
features: options.features,
args: Vec::new(),
config: options.config,
no_watch: options.no_watch,
},
|options| {
let cli_options = CliOptions {
features: options.features.clone(),
args: options.args.clone(),
vars: Default::default(),
};
write_options(cli_options, &bundle_identifier, MobileTarget::Android)?;
if open {
open_dev(config)
} else {
match run(options) {
Ok(c) => Ok(Box::new(c) as Box<dyn DevProcess>),
Err(Error::FailedToPromptForDevice(e)) => {
log::error!("{}", e);
open_dev(config)
}
Err(e) => Err(e.into()),
}
}
},
)
}
fn open_dev(config: &AndroidConfig) -> ! {
log::info!("Opening Android Studio");
if let Err(e) = os::open_file_with("Android Studio", config.project_dir()) {
log::error!("{}", e);
}
loop {
std::thread::sleep(std::time::Duration::from_secs(24 * 60 * 60));
}
}
fn open() -> Result<()> {
with_config(|config, _metadata| {
with_config(|_, config, _metadata| {
ensure_init(config.project_dir(), MobileTarget::Android)
.map_err(|e| Error::ProjectNotInitialized(e.to_string()))?;
os::open_file_with("Android Studio", config.project_dir()).map_err(Error::OpenFailed)
})?;
Ok(())
})
.map_err(Into::into)
}
fn build(options: BuildOptions) -> Result<()> {
fn run(options: MobileOptions) -> Result<DevChild, Error> {
let profile = if options.debug {
Profile::Debug
} else {
Profile::Release
};
fn device_prompt<'a>(env: &'_ Env) -> Result<Device<'a>, PromptError<adb::device_list::Error>> {
let device_list =
adb::device_list(env).map_err(|cause| PromptError::detection_failed("Android", cause))?;
if !device_list.is_empty() {
let index = if device_list.len() > 1 {
prompt::list(
concat!("Detected ", "Android", " devices"),
device_list.iter(),
"device",
None,
"Device",
)
.map_err(|cause| PromptError::prompt_failed("Android", cause))?
} else {
0
};
let device = device_list.into_iter().nth(index).unwrap();
println!(
"Detected connected device: {} with target {:?}",
device,
device.target().triple,
);
Ok(device)
} else {
Err(PromptError::none_detected("Android"))
}
}
with_config(|root_conf, config, metadata| {
let build_app_bundle = metadata.asset_packs().is_some();
ensure_init(config.project_dir(), MobileTarget::Android)
.map_err(|e| Error::ProjectNotInitialized(e.to_string()))?;
let env = Env::new().map_err(Error::EnvInitFailed)?;
super::init::init_dot_cargo(root_conf, Some(&env)).map_err(Error::InitDotCargo)?;
device_prompt(&env)
.map_err(Error::FailedToPromptForDevice)?
.run(
config,
&env,
NoiseLevel::Polite,
profile,
None,
build_app_bundle,
false,
".MainActivity".into(),
)
.map_err(Error::RunFailed)
})
.map(|c| DevChild(Some(c)))
}
fn build(options: BuildOptions) -> Result<()> {
let profile = if options.release {
Profile::Release
} else {
Profile::Debug
};
fn detect_target_ok<'a>(env: &Env) -> Option<&'a Target<'a>> {
device_prompt(env).map(|device| device.target()).ok()
}
with_config(|config, metadata| {
with_config(|root_conf, config, metadata| {
ensure_init(config.project_dir(), MobileTarget::Android)
.map_err(|e| Error::ProjectNotInitialized(e.to_string()))?;
let env = Env::new().map_err(Error::EnvInitFailed)?;
super::init::init_dot_cargo(root_conf, Some(&env)).map_err(Error::InitDotCargo)?;
call_for_targets_with_fallback(
options.targets.unwrap_or_default().iter(),
@@ -164,18 +318,11 @@ fn build(options: BuildOptions) -> Result<()> {
&env,
|target: &Target| {
target
.build(
config,
metadata,
&env,
NoiseLevel::Polite,
true.into(),
profile,
)
.build(config, metadata, &env, NoiseLevel::Polite, true, profile)
.map_err(Error::BuildFailed)
},
)
.map_err(|e| Error::TargetInvalid(e.to_string()))?
})?;
Ok(())
})
.map_err(Into::into)
}

View File

@@ -6,11 +6,9 @@ use crate::helpers::template;
use cargo_mobile::{
android::{
config::{Config, Metadata},
env::Env,
ndk,
target::Target,
},
dot_cargo, os,
bossy, os,
target::TargetTrait as _,
util::{
self,
@@ -42,8 +40,6 @@ pub enum Error {
},
#[error("failed to symlink asset directory")]
AssetDirSymlinkFailed,
#[error(transparent)]
DotCargoGenFailed(ndk::MissingToolError),
#[error("failed to copy {src} to {dest}: {cause}")]
FileCopyFailed {
src: PathBuf,
@@ -57,10 +53,8 @@ pub enum Error {
pub fn gen(
config: &Config,
metadata: &Metadata,
env: &Env,
(handlebars, mut map): (Handlebars, template::JsonMap),
wrapper: &TextWrapper,
dot_cargo: &mut dot_cargo::DotCargo,
) -> Result<(), Error> {
println!("Installing Android toolchains...");
Target::install_all().map_err(Error::RustupFailed)?;
@@ -138,7 +132,15 @@ pub fn gen(
created_dirs.push(parent);
}
fs::File::create(path)
let mut options = fs::OpenOptions::new();
#[cfg(unix)]
if path.file_name().unwrap() == OsStr::new("gradlew") {
use std::os::unix::fs::OpenOptionsExt;
options.mode(0o755);
}
options.create_new(true).write(true).open(path)
},
)
.map_err(|e| Error::TemplateProcessingFailed(e.to_string()))?;
@@ -173,16 +175,5 @@ pub fn gen(
os::ln::force_symlink_relative(config.app().asset_dir(), dest, ln::TargetStyle::Directory)
.map_err(|_| Error::AssetDirSymlinkFailed)?;
{
for target in Target::all().values() {
dot_cargo.insert_target(
target.triple.to_owned(),
target
.generate_cargo_config(config, env)
.map_err(Error::DotCargoGenFailed)?,
);
}
}
Ok(())
}

View File

@@ -2,14 +2,16 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use super::{get_config, get_metadata, Target};
use crate::helpers::{app_paths::tauri_dir, config::get as get_tauri_config, template::JsonMap};
use super::{get_config, Target};
use crate::helpers::{config::get as get_tauri_config, template::JsonMap};
use crate::Result;
use cargo_mobile::{
android,
android::{self, env::Env as AndroidEnv, ndk, target::Target as AndroidTarget},
bossy,
config::Config,
dot_cargo, opts,
dot_cargo,
os::code_command,
target::TargetTrait as _,
util::{
self,
cli::{Report, TextWrapper},
@@ -18,12 +20,7 @@ use cargo_mobile::{
use clap::Parser;
use handlebars::{Context, Handlebars, Helper, HelperResult, Output, RenderContext, RenderError};
use std::{
fs, io,
path::{Path, PathBuf},
};
use opts::{NonInteractive, OpenInEditor, ReinstallDeps, SkipDevTools};
use std::{fs, io, path::PathBuf};
#[derive(Debug, Parser)]
#[clap(about = "Initializes a Tauri Android project")]
@@ -37,16 +34,7 @@ pub fn command(mut options: Options, target: Target) -> Result<()> {
options.ci = options.ci || std::env::var("CI").is_ok();
let wrapper = TextWrapper::with_splitter(textwrap::termwidth(), textwrap::NoHyphenation);
exec(
target,
&wrapper,
options.ci.into(),
SkipDevTools::Yes,
ReinstallDeps::Yes,
OpenInEditor::No,
tauri_dir(),
)
.map_err(|e| anyhow::anyhow!("{:#}", e))?;
exec(target, &wrapper, options.ci, true, true).map_err(|e| anyhow::anyhow!("{:#}", e))?;
Ok(())
}
@@ -64,6 +52,8 @@ pub enum Error {
#[error(transparent)]
DotCargoLoad(dot_cargo::LoadError),
#[error(transparent)]
DotCargoGenFailed(ndk::MissingToolError),
#[error(transparent)]
HostTargetTripleDetection(util::HostTargetTripleError),
#[cfg(target_os = "macos")]
#[error(transparent)]
@@ -74,42 +64,9 @@ pub enum Error {
AndroidInit(super::android::project::Error),
#[error(transparent)]
DotCargoWrite(dot_cargo::WriteError),
#[error(transparent)]
OpenInEditor(util::OpenInEditorError),
}
pub fn exec(
target: Target,
wrapper: &TextWrapper,
non_interactive: NonInteractive,
skip_dev_tools: SkipDevTools,
#[allow(unused_variables)] reinstall_deps: ReinstallDeps,
open_in_editor: OpenInEditor,
cwd: impl AsRef<Path>,
) -> Result<Config, Error> {
let cwd = cwd.as_ref();
let tauri_config =
get_tauri_config(None).map_err(|e| Error::InvalidTauriConfig(e.to_string()))?;
let tauri_config_guard = tauri_config.lock().unwrap();
let tauri_config_ = tauri_config_guard.as_ref().unwrap();
let config = get_config(tauri_config_);
let metadata = get_metadata(tauri_config_);
let asset_dir = config.app().asset_dir();
if !asset_dir.is_dir() {
fs::create_dir_all(&asset_dir).map_err(|cause| Error::AssetDirCreation { asset_dir, cause })?;
}
if skip_dev_tools.no() && util::command_present("code").unwrap_or_default() {
let mut command = code_command();
command.add_args(&["--install-extension", "vadimcn.vscode-lldb"]);
if non_interactive.yes() {
command.add_arg("--force");
}
command
.run_and_wait()
.map_err(Error::LldbExtensionInstall)?;
}
pub fn init_dot_cargo(config: &Config, android_env: Option<&AndroidEnv>) -> Result<(), Error> {
let mut dot_cargo = dot_cargo::DotCargo::load(config.app()).map_err(Error::DotCargoLoad)?;
// Mysteriously, builds that don't specify `--target` seem to fight over
// the build cache with builds that use `--target`! This means that
@@ -124,6 +81,49 @@ pub fn exec(
dot_cargo
.set_default_target(util::host_target_triple().map_err(Error::HostTargetTripleDetection)?);
if let Some(env) = android_env {
for target in AndroidTarget::all().values() {
dot_cargo.insert_target(
target.triple.to_owned(),
target
.generate_cargo_config(config.android(), env)
.map_err(Error::DotCargoGenFailed)?,
);
}
}
dot_cargo.write(config.app()).map_err(Error::DotCargoWrite)
}
pub fn exec(
target: Target,
wrapper: &TextWrapper,
non_interactive: bool,
skip_dev_tools: bool,
#[allow(unused_variables)] reinstall_deps: bool,
) -> Result<Config, Error> {
let tauri_config =
get_tauri_config(None).map_err(|e| Error::InvalidTauriConfig(e.to_string()))?;
let tauri_config_guard = tauri_config.lock().unwrap();
let tauri_config_ = tauri_config_guard.as_ref().unwrap();
let (config, metadata) = get_config(tauri_config_);
let asset_dir = config.app().asset_dir();
if !asset_dir.is_dir() {
fs::create_dir_all(&asset_dir).map_err(|cause| Error::AssetDirCreation { asset_dir, cause })?;
}
if !skip_dev_tools && util::command_present("code").unwrap_or_default() {
let mut command = code_command();
command.add_args(&["--install-extension", "vadimcn.vscode-lldb"]);
if non_interactive {
command.add_arg("--force");
}
command
.run_and_wait()
.map_err(Error::LldbExtensionInstall)?;
}
let (handlebars, mut map) = handlebars(&config);
// TODO: make this a relative path
map.insert(
@@ -135,24 +135,26 @@ pub fn exec(
);
// Generate Android Studio project
if target == Target::Android {
match android::env::Env::new() {
Ok(env) => super::android::project::gen(
config.android(),
metadata.android(),
&env,
(handlebars, map),
wrapper,
&mut dot_cargo,
)
.map_err(Error::AndroidInit)?,
let android_env = if target == Target::Android {
match AndroidEnv::new() {
Ok(env) => {
super::android::project::gen(
config.android(),
metadata.android(),
(handlebars, map),
wrapper,
)
.map_err(Error::AndroidInit)?;
Some(env)
}
Err(err) => {
if err.sdk_or_ndk_issue() {
Report::action_request(
" to initialize Android environment; Android support won't be usable until you fix the issue below and re-run `cargo mobile init`!",
" to initialize Android environment; Android support won't be usable until you fix the issue below and re-run `tauri android init`!",
err,
)
.print(wrapper);
None
} else {
return Err(Error::AndroidEnv(err));
}
@@ -173,20 +175,16 @@ pub fn exec(
)
.map_err(Error::IosInit)?;
}
}
None
};
dot_cargo
.write(config.app())
.map_err(Error::DotCargoWrite)?;
init_dot_cargo(&config, android_env.as_ref())?;
Report::victory(
"Project generated successfully!",
"Make cool apps! 🌻 🐕 🎉",
)
.print(wrapper);
if open_in_editor.yes() {
util::open_in_editor(cwd).map_err(Error::OpenInEditor)?;
}
Ok(config)
}

View File

@@ -2,24 +2,33 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use crate::helpers::config::get as get_tauri_config;
use cargo_mobile::{
apple::{
config::{Config as AppleConfig, Metadata as AppleMetadata},
device::{Device, RunError},
ios_deploy,
target::{CompileLibError, Target},
},
config::Config,
device::PromptError,
env::{Env, Error as EnvError},
opts::{NoiseLevel, Profile},
os, util,
util::prompt,
};
use clap::{Parser, Subcommand};
use super::{
ensure_init, get_config, get_metadata,
ensure_init, env_vars, get_config,
init::{command as init_command, Options as InitOptions},
Target as MobileTarget,
write_options, CliOptions, DevChild, Target as MobileTarget,
};
use crate::Result;
use crate::{
helpers::{config::get as get_tauri_config, flock},
interface::{AppSettings, DevProcess, Interface, MobileOptions, Options as InterfaceOptions},
Result,
};
use std::{collections::HashMap, ffi::OsStr, path::PathBuf};
pub(crate) mod project;
@@ -28,12 +37,16 @@ pub(crate) mod project;
enum Error {
#[error(transparent)]
EnvInitFailed(EnvError),
#[error(transparent)]
InitDotCargo(super::init::Error),
#[error("invalid tauri configuration: {0}")]
InvalidTauriConfig(String),
#[error("{0}")]
ProjectNotInitialized(String),
#[error(transparent)]
OpenFailed(os::OpenFileError),
#[error("{0}")]
DevFailed(String),
#[error(transparent)]
NoHomeDir(util::NoHomeDir),
#[error("SDK root provided by Xcode was invalid. {sdk_root} doesn't exist or isn't a directory")]
@@ -46,6 +59,10 @@ enum Error {
ArchInvalid { arch: String },
#[error(transparent)]
CompileLibFailed(CompileLibError),
#[error(transparent)]
FailedToPromptForDevice(PromptError<ios_deploy::DeviceListError>),
#[error(transparent)]
RunFailed(RunError),
}
#[derive(Parser)]
@@ -80,10 +97,49 @@ pub struct XcodeScriptOptions {
arches: Vec<String>,
}
#[derive(Debug, Clone, Parser)]
#[clap(about = "iOS dev")]
pub struct DevOptions {
/// List of cargo features to activate
#[clap(short, long, multiple_occurrences(true), multiple_values(true))]
pub features: Option<Vec<String>>,
/// Exit on panic
#[clap(short, long)]
exit_on_panic: bool,
/// JSON string or path to JSON file to merge with tauri.conf.json
#[clap(short, long)]
pub config: Option<String>,
/// Run the code in release mode
#[clap(long = "release")]
pub release_mode: bool,
/// Disable the file watcher
#[clap(long)]
pub no_watch: bool,
/// Open Xcode instead of trying to run on a connected device
#[clap(short, long)]
pub open: bool,
}
impl From<DevOptions> for crate::dev::Options {
fn from(options: DevOptions) -> Self {
Self {
runner: None,
target: None,
features: options.features,
exit_on_panic: options.exit_on_panic,
config: options.config,
release_mode: options.release_mode,
args: Vec::new(),
no_watch: options.no_watch,
}
}
}
#[derive(Subcommand)]
enum Commands {
Init(InitOptions),
Open,
Dev(DevOptions),
#[clap(hide(true))]
XcodeScript(XcodeScriptOptions),
}
@@ -92,31 +148,159 @@ pub fn command(cli: Cli) -> Result<()> {
match cli.command {
Commands::Init(options) => init_command(options, MobileTarget::Ios)?,
Commands::Open => open()?,
Commands::Dev(options) => dev(options)?,
Commands::XcodeScript(options) => xcode_script(options)?,
}
Ok(())
}
fn with_config(
f: impl FnOnce(&AppleConfig, &AppleMetadata) -> Result<(), Error>,
) -> Result<(), Error> {
let tauri_config =
get_tauri_config(None).map_err(|e| Error::InvalidTauriConfig(e.to_string()))?;
let tauri_config_guard = tauri_config.lock().unwrap();
let tauri_config_ = tauri_config_guard.as_ref().unwrap();
let config = get_config(tauri_config_);
let metadata = get_metadata(tauri_config_);
f(config.apple(), metadata.apple())
fn with_config<T>(
f: impl FnOnce(&Config, &AppleConfig, &AppleMetadata) -> Result<T, Error>,
) -> Result<T, Error> {
let (config, metadata) = {
let tauri_config =
get_tauri_config(None).map_err(|e| Error::InvalidTauriConfig(e.to_string()))?;
let tauri_config_guard = tauri_config.lock().unwrap();
let tauri_config_ = tauri_config_guard.as_ref().unwrap();
get_config(tauri_config_)
};
f(&config, config.apple(), metadata.apple())
}
fn env() -> Result<Env, Error> {
let env = Env::new()
.map_err(Error::EnvInitFailed)?
.explicit_env_vars(env_vars());
Ok(env)
}
fn device_prompt<'a>(env: &'_ Env) -> Result<Device<'a>, PromptError<ios_deploy::DeviceListError>> {
let device_list =
ios_deploy::device_list(env).map_err(|cause| PromptError::detection_failed("iOS", cause))?;
if !device_list.is_empty() {
let index = if device_list.len() > 1 {
prompt::list(
concat!("Detected ", "iOS", " devices"),
device_list.iter(),
"device",
None,
"Device",
)
.map_err(|cause| PromptError::prompt_failed("iOS", cause))?
} else {
0
};
let device = device_list.into_iter().nth(index).unwrap();
println!(
"Detected connected device: {} with target {:?}",
device,
device.target().triple,
);
Ok(device)
} else {
Err(PromptError::none_detected("iOS"))
}
}
fn dev(options: DevOptions) -> Result<()> {
with_config(|_, config, _metadata| {
run_dev(options, config).map_err(|e| Error::DevFailed(e.to_string()))
})
.map_err(Into::into)
}
fn run_dev(options: DevOptions, config: &AppleConfig) -> Result<()> {
let mut dev_options = options.clone().into();
let mut interface = crate::dev::setup(&mut dev_options)?;
let bundle_identifier = {
let tauri_config =
get_tauri_config(None).map_err(|e| Error::InvalidTauriConfig(e.to_string()))?;
let tauri_config_guard = tauri_config.lock().unwrap();
let tauri_config_ = tauri_config_guard.as_ref().unwrap();
tauri_config_.tauri.bundle.identifier.clone()
};
let app_settings = interface.app_settings();
let bin_path = app_settings.app_binary_path(&InterfaceOptions {
debug: !dev_options.release_mode,
..Default::default()
})?;
let out_dir = bin_path.parent().unwrap();
let _lock = flock::open_rw(&out_dir.join("lock").with_extension("ios"), "iOS")?;
let open = options.open;
interface.mobile_dev(
MobileOptions {
debug: true,
features: options.features,
args: Vec::new(),
config: options.config,
no_watch: options.no_watch,
},
|options| {
let cli_options = CliOptions {
features: options.features.clone(),
args: options.args.clone(),
vars: Default::default(),
};
write_options(cli_options, &bundle_identifier, MobileTarget::Ios)?;
if open {
open_dev(config)
} else {
match run(options) {
Ok(c) => Ok(Box::new(c) as Box<dyn DevProcess>),
Err(Error::FailedToPromptForDevice(e)) => {
log::error!("{}", e);
open_dev(config)
}
Err(e) => Err(e.into()),
}
}
},
)
}
fn open_dev(config: &AppleConfig) -> ! {
log::info!("Opening Xcode");
if let Err(e) = os::open_file_with("Xcode", config.project_dir()) {
log::error!("{}", e);
}
loop {
std::thread::sleep(std::time::Duration::from_secs(24 * 60 * 60));
}
}
fn open() -> Result<()> {
with_config(|config, _metadata| {
with_config(|_, config, _metadata| {
ensure_init(config.project_dir(), MobileTarget::Ios)
.map_err(|e| Error::ProjectNotInitialized(e.to_string()))?;
os::open_file_with("Xcode", config.project_dir()).map_err(Error::OpenFailed)
})?;
Ok(())
})
.map_err(Into::into)
}
fn run(options: MobileOptions) -> Result<DevChild, Error> {
let profile = if options.debug {
Profile::Debug
} else {
Profile::Release
};
with_config(|root_conf, config, _| {
ensure_init(config.project_dir(), MobileTarget::Ios)
.map_err(|e| Error::ProjectNotInitialized(e.to_string()))?;
let env = env()?;
super::init::init_dot_cargo(root_conf, None).map_err(Error::InitDotCargo)?;
device_prompt(&env)
.map_err(Error::FailedToPromptForDevice)?
.run(config, &env, NoiseLevel::Polite, false, profile)
.map_err(Error::RunFailed)
})
.map(|c| DevChild(Some(c)))
}
fn xcode_script(options: XcodeScriptOptions) -> Result<()> {
@@ -135,8 +319,9 @@ fn xcode_script(options: XcodeScriptOptions) -> Result<()> {
let profile = profile_from_configuration(&options.configuration);
let macos = macos_from_platform(&options.platform);
with_config(|config, metadata| {
let env = Env::new().map_err(Error::EnvInitFailed)?;
with_config(|root_conf, config, metadata| {
let env = env()?;
super::init::init_dot_cargo(root_conf, None).map_err(Error::InitDotCargo)?;
// The `PATH` env var Xcode gives us is missing any additions
// made by the user's profile, so we'll manually add cargo's
// `PATH`.
@@ -216,7 +401,7 @@ fn xcode_script(options: XcodeScriptOptions) -> Result<()> {
config,
metadata,
NoiseLevel::Polite,
true.into(),
true,
profile,
&env,
target_env,
@@ -224,7 +409,6 @@ fn xcode_script(options: XcodeScriptOptions) -> Result<()> {
.map_err(Error::CompileLibFailed)?;
}
Ok(())
})?;
Ok(())
})
.map_err(Into::into)
}

View File

@@ -9,7 +9,7 @@ use cargo_mobile::{
deps, rust_version_check,
target::Target,
},
opts,
bossy,
target::TargetTrait as _,
util::{self, cli::TextWrapper, ln},
};
@@ -53,9 +53,9 @@ pub fn gen(
metadata: &Metadata,
(handlebars, mut map): (Handlebars, template::JsonMap),
wrapper: &TextWrapper,
non_interactive: opts::NonInteractive,
skip_dev_tools: opts::SkipDevTools,
reinstall_deps: opts::ReinstallDeps,
non_interactive: bool,
skip_dev_tools: bool,
reinstall_deps: bool,
) -> Result<(), Error> {
println!("Installing iOS toolchains...");
Target::install_all().map_err(Error::Rustup)?;

View File

@@ -2,7 +2,10 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use crate::helpers::{app_paths::tauri_dir, config::Config as TauriConfig};
use crate::{
helpers::{app_paths::tauri_dir, config::Config as TauriConfig},
interface::DevProcess,
};
use anyhow::{bail, Result};
#[cfg(target_os = "macos")]
use cargo_mobile::apple::config::{
@@ -10,15 +13,48 @@ use cargo_mobile::apple::config::{
};
use cargo_mobile::{
android::config::{Metadata as AndroidMetadata, Raw as RawAndroidConfig},
bossy,
config::{app::Raw as RawAppConfig, metadata::Metadata, Config, Raw},
};
use std::path::PathBuf;
use serde::{Deserialize, Serialize};
use std::{collections::HashMap, ffi::OsString, path::PathBuf, process::ExitStatus};
pub mod android;
mod init;
#[cfg(target_os = "macos")]
pub mod ios;
pub struct DevChild(Option<bossy::Handle>);
impl Drop for DevChild {
fn drop(&mut self) {
// consume the handle since we're not waiting on it
// just to prevent a log error
// note that this doesn't leak any memory
self.0.take().unwrap().leak();
}
}
impl DevProcess for DevChild {
fn kill(&mut self) -> std::io::Result<()> {
self
.0
.as_mut()
.unwrap()
.kill()
.map_err(|_| std::io::Error::new(std::io::ErrorKind::Other, "failed to kill"))
}
fn try_wait(&mut self) -> std::io::Result<Option<ExitStatus>> {
self
.0
.as_mut()
.unwrap()
.try_wait()
.map_err(|_| std::io::Error::new(std::io::ErrorKind::Other, "failed to wait"))
}
}
#[derive(PartialEq, Eq)]
pub enum Target {
Android,
@@ -44,13 +80,113 @@ impl Target {
}
}
fn get_metadata(_config: &TauriConfig) -> Metadata {
Metadata {
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct CliOptions {
pub features: Option<Vec<String>>,
pub args: Vec<String>,
pub vars: HashMap<String, OsString>,
}
fn options_path(bundle_identifier: &str, target: Target) -> PathBuf {
let out_dir = dirs_next::cache_dir()
.or_else(dirs_next::home_dir)
.unwrap_or_else(std::env::temp_dir);
let out_dir = out_dir.join(".tauri").join(bundle_identifier);
let _ = std::fs::create_dir_all(&out_dir);
out_dir
.join("cli-options")
.with_extension(target.command_name())
}
fn env_vars() -> HashMap<String, OsString> {
let mut vars = HashMap::new();
for (k, v) in std::env::vars_os() {
let k = k.to_string_lossy();
if k.starts_with("TAURI") && k != "TAURI_PRIVATE_KEY" && k != "TAURI_KEY_PASSWORD" {
vars.insert(k.into_owned(), v);
}
}
vars
}
/// Writes CLI options to be used later on the Xcode and Android Studio build commands
pub fn write_options(
mut options: CliOptions,
bundle_identifier: &str,
target: Target,
) -> crate::Result<()> {
options.vars.extend(env_vars());
std::fs::write(
options_path(bundle_identifier, target),
&serde_json::to_string(&options)?,
)?;
Ok(())
}
fn read_options(config: &TauriConfig, target: Target) -> crate::Result<CliOptions> {
let data = std::fs::read_to_string(options_path(&config.tauri.bundle.identifier, target))?;
let options = serde_json::from_str(&data)?;
Ok(options)
}
fn get_config(config: &TauriConfig) -> (Config, Metadata) {
let mut s = config.tauri.bundle.identifier.rsplit('.');
let app_name = s.next().unwrap_or("app").to_string();
let mut domain = String::new();
for w in s {
domain.push_str(w);
domain.push('.');
}
domain.pop();
#[cfg(target_os = "macos")]
let ios_options = read_options(config, Target::Ios).unwrap_or_default();
let android_options = read_options(config, Target::Android).unwrap_or_default();
let raw = Raw {
app: RawAppConfig {
name: app_name,
stylized_name: config.package.product_name.clone(),
domain,
asset_dir: None,
template_pack: None,
},
#[cfg(target_os = "macos")]
apple: Some(RawAppleConfig {
development_team: std::env::var("TAURI_APPLE_DEVELOPMENT_TEAM")
.ok()
.or_else(|| config.tauri.ios.development_team.clone())
.expect("you must set `tauri > iOS > developmentTeam` config value or the `TAURI_APPLE_DEVELOPMENT_TEAM` environment variable"),
project_dir: None,
ios_no_default_features: None,
ios_features: ios_options.features.clone(),
macos_no_default_features: None,
macos_features: None,
bundle_version: None,
bundle_version_short: None,
ios_version: None,
macos_version: None,
use_legacy_build_system: None,
plist_pairs: None,
}),
android: Some(RawAndroidConfig {
min_sdk_version: None,
vulkan_validation: None,
project_dir: None,
no_default_features: None,
features: android_options.features.clone(),
}),
};
let config = Config::from_raw(tauri_dir(), raw).unwrap();
let metadata = Metadata {
#[cfg(target_os = "macos")]
apple: AppleMetadata {
supported: true,
ios: ApplePlatform {
features: None,
no_default_features: false,
cargo_args: Some(ios_options.args),
features: ios_options.features,
frameworks: None,
valid_archs: None,
vendor_frameworks: None,
@@ -67,7 +203,9 @@ fn get_metadata(_config: &TauriConfig) -> Metadata {
},
android: AndroidMetadata {
supported: true,
features: None,
no_default_features: false,
cargo_args: Some(android_options.args),
features: android_options.features,
app_sources: None,
app_plugins: None,
project_dependencies: None,
@@ -75,53 +213,9 @@ fn get_metadata(_config: &TauriConfig) -> Metadata {
app_dependencies_platform: None,
asset_packs: None,
},
}
}
fn get_config(config: &TauriConfig) -> Config {
let mut s = config.tauri.bundle.identifier.rsplit('.');
let app_name = s.next().unwrap_or("app").to_string();
let mut domain = String::new();
for w in s {
domain.push_str(w);
domain.push('.');
}
domain.pop();
let raw = Raw {
app: RawAppConfig {
name: app_name,
stylized_name: config.package.product_name.clone(),
domain,
asset_dir: None,
template_pack: None,
},
#[cfg(target_os = "macos")]
apple: Some(RawAppleConfig {
development_team: std::env::var("APPLE_DEVELOPMENT_TEAM")
.ok()
.or_else(|| config.tauri.ios.development_team.clone())
.expect("you must set `tauri > iOS > developmentTeam` config value or the `APPLE_DEVELOPMENT_TEAM` environment variable"),
project_dir: None,
ios_no_default_features: None,
ios_features: None,
macos_no_default_features: None,
macos_features: None,
bundle_version: None,
bundle_version_short: None,
ios_version: None,
macos_version: None,
use_legacy_build_system: None,
plist_pairs: None,
}),
android: Some(RawAndroidConfig {
min_sdk_version: None,
vulkan_validation: None,
project_dir: None,
no_default_features: None,
features: None,
}),
};
Config::from_raw(tauri_dir(), raw).unwrap()
(config, metadata)
}
fn ensure_init(project_dir: PathBuf, target: Target) -> Result<()> {

View File

@@ -40,6 +40,16 @@ android {
}
flavorDimensions.add("abi")
productFlavors {
create("universal") {
dimension = "abi"
ndk {
abiFilters += listOf(
{{~#each targets}}
"{{this.abi}}",{{/each}}
)
}
}
{{~#each targets}}
create("{{this.arch}}") {
@@ -75,7 +85,9 @@ dependencies {
afterEvaluate {
android.applicationVariants.all {
productFlavors.forEach { _ ->
tasks["mergeUniversalReleaseJniLibFolders"].dependsOn(tasks["rustBuildRelease"])
tasks["mergeUniversalDebugJniLibFolders"].dependsOn(tasks["rustBuildDebug"])
productFlavors.filter{ it.name != "universal" }.forEach { _ ->
val archAndBuildType = name.capitalize()
tasks["merge${archAndBuildType}JniLibFolders"].dependsOn(tasks["rustBuild${archAndBuildType}"])
}

View File

@@ -3,11 +3,8 @@
package="{{reverse-domain app.domain}}.{{snake-case app.name}}">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.{{snake-case app.name}}">
<activity
android:name=".MainActivity"

View File

@@ -1,3 +1,3 @@
package {{reverse-domain app.domain}}.{{snake-case app.name}}
class MainActivity : TauriActivity() {}
class MainActivity : TauriActivity()

View File

@@ -10,7 +10,7 @@ class RustWebChromeClient: WebChromeClient() {
if (url.endsWith("##")) {
url = url.dropLast(2)
}
if (loadedUrl != url) {
if (loadedUrl != url && progress >= 20) {
loadedUrl = url
runInitializationScripts()
}

View File

@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

View File

@@ -1,3 +1,3 @@
<resources>
<string name="app_name">qoo</string>
<string name="app_name">{{app.stylized-name}}</string>
</resources>

View File

@@ -2,10 +2,6 @@ plugins {
`kotlin-dsl`
}
kotlinDslPluginOptions {
experimentalWarning.set(false)
}
gradlePlugin {
plugins {
create("pluginsForCoolKids") {

View File

@@ -1,10 +1,8 @@
package {{reverse-domain app.domain}}
import com.android.build.gradle.*
import java.io.File
import org.gradle.api.DefaultTask
import org.gradle.api.GradleException
import org.gradle.api.Project
import org.gradle.api.logging.LogLevel
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputDirectory
@@ -23,20 +21,11 @@ open class BuildTask : DefaultTask() {
@TaskAction
fun build() {
val rootDirRel = rootDirRel
if (rootDirRel == null) {
throw GradleException("rootDirRel cannot be null")
}
val target = target
if (target == null) {
throw GradleException("target cannot be null")
}
val release = release
if (release == null) {
throw GradleException("release cannot be null")
}
val rootDirRel = rootDirRel ?: throw GradleException("rootDirRel cannot be null")
val target = target ?: throw GradleException("target cannot be null")
val release = release ?: throw GradleException("release cannot be null")
project.exec {
workingDir(File(project.getProjectDir(), rootDirRel.getPath()))
workingDir(File(project.projectDir, rootDirRel.path))
executable("{{ tauri-binary }}")
args(listOf("tauri", "android", "build"))
if (project.logger.isEnabled(LogLevel.DEBUG)) {
@@ -47,7 +36,7 @@ open class BuildTask : DefaultTask() {
if (release) {
args("--release")
}
args(listOf("--target", "${target}"))
args(listOf("--target", target))
}.assertNormalExitValue()
}
}

View File

@@ -1,11 +1,11 @@
package {{reverse-domain app.domain}}
import com.android.build.gradle.*
import java.io.File
import org.gradle.api.DefaultTask
import org.gradle.api.GradleException
import org.gradle.api.Plugin
import org.gradle.api.Project
import java.io.File
import java.util.*
const val TASK_GROUP = "rust"
@@ -16,7 +16,7 @@ open class Config {
}
open class RustPlugin : Plugin<Project> {
internal lateinit var config: Config
private lateinit var config: Config
override fun apply(project: Project) {
config = project.extensions.create("rust", Config::class.java)
@@ -28,17 +28,25 @@ open class RustPlugin : Plugin<Project> {
throw GradleException("arches cannot be null")
}
for (profile in listOf("debug", "release")) {
val buildTask = project.tasks.maybeCreate("rustBuild${profile.capitalize()}", DefaultTask::class.java).apply {
val profileCapitalized = profile.capitalize(Locale.ROOT)
val buildTask = project.tasks.maybeCreate(
"rustBuild$profileCapitalized",
DefaultTask::class.java
).apply {
group = TASK_GROUP
description = "Build dynamic library in ${profile} mode for all targets"
description = "Build dynamic library in $profile mode for all targets"
}
for (targetPair in config.targets!!.withIndex()) {
val targetName = targetPair.value
val targetArch = config.arches!![targetPair.index]
val targetBuildTask = project.tasks.maybeCreate("rustBuild${targetArch.capitalize()}${profile.capitalize()}", BuildTask::class.java).apply {
val targetArchCapitalized = targetArch.capitalize(Locale.ROOT)
val targetBuildTask = project.tasks.maybeCreate(
"rustBuild$targetArchCapitalized$profileCapitalized",
BuildTask::class.java
).apply {
group = TASK_GROUP
description = "Build dynamic library in ${profile} mode for $targetArch"
rootDirRel = File(config.rootDirRel)
description = "Build dynamic library in $profile mode for $targetArch"
rootDirRel = config.rootDirRel?.let { File(it) }
target = targetName
release = profile == "release"
}