0.3.1-0-pre.2.fix.1
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<!-- file: crates/apps/game-snake-poc-tauri/README.md -->
|
||||
<!-- version: 1 -->
|
||||
<!-- version: 2 -->
|
||||
|
||||
# game-snake-poc-tauri
|
||||
|
||||
@@ -22,4 +22,6 @@ Le frontend minimal de `0.3.1-0-pre.2` valide uniquement le démarrage Tauri, le
|
||||
|
||||
Les hooks `beforeDevCommand` et `beforeBuildCommand` de Tauri possèdent le build WASM et Vite. Aucun orchestrateur Python n'est utilisé. Les bindings WASM, la distribution Vite, les caches, `node_modules/` et le projet Android généré restent hors des sources livrées ou dans des chemins générés ignorés.
|
||||
|
||||
Pour Tauri CLI `2.11.x`, le projet Android généré utilise encore Gradle `8.14.3`. Le hook `scripts/prepare-android-gradle.mjs` applique avant `dev`/`build` la migration Android déjà retenue upstream par Tauri (`Gradle 9.6.1`, AGP `9.3.1`, Kotlin `2.2.10`) afin de rester exécutable avec Java 25. Cette préparation modifie uniquement `gen/android/`, reste idempotente et ne dépend pas du Gradle global installé sur la machine.
|
||||
|
||||
Voir [`USAGE.md`](USAGE.md) pour l'initialisation locale et les smokes Android.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: crates/apps/game-snake-poc-tauri/USAGE.md -->
|
||||
<!-- version: 1 -->
|
||||
<!-- version: 2 -->
|
||||
|
||||
# Utilisation — Snake Tauri Android
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
|
||||
Le host utilise Cargo/Rust, `cargo tauri`, Node/npm, `wasm-bindgen`, Java et le SDK Android. Le dépôt attend `ANDROID_HOME` vers le SDK Android et des NDK side-by-side sous `$ANDROID_HOME/ndk/<version>`.
|
||||
|
||||
`NDK_HOME` n'est pas imposé globalement par le projet. Si une commande Tauri locale le réclame, définir ponctuellement `NDK_HOME` vers une version NDK exacte installée sous `$ANDROID_HOME/ndk/`; ne pas sélectionner implicitement une version lorsque plusieurs NDK coexistent.
|
||||
`NDK_HOME` n'est pas un prérequis utilisateur du projet. Tauri sélectionne la version NDK side-by-side appropriée sous `$ANDROID_HOME/ndk/<version>` et peut injecter lui-même `NDK_HOME` dans ses sous-processus Android. Une variable globale ne doit donc pas être ajoutée uniquement pour satisfaire la documentation générique de Tauri.
|
||||
|
||||
`JAVA_HOME` n'est pas imposé tant que la CLI Tauri résout correctement `java` depuis l'environnement. En cas de diagnostic explicite de la CLI, fixer alors `JAVA_HOME` vers le JDK réellement choisi.
|
||||
`JAVA_HOME` n'est pas imposé tant que la CLI Tauri résout correctement `java` depuis l'environnement. L'environnement de validation utilise Java 25 ; Tauri CLI `2.11.x` génère encore un wrapper Gradle `8.14.3`, qui ne peut pas exécuter ce JDK. Le host corrige ce scaffold généré avant les hooks frontend vers Gradle `9.6.1`, AGP `9.3.1` et Kotlin `2.2.10`, combinaison déjà adoptée par la migration Android upstream de Tauri. Le Gradle global installé sur la machine n'est pas utilisé par `gradlew` et n'est pas requis par ce correctif.
|
||||
|
||||
Pour les cibles actuellement disponibles, `aarch64-linux-android` permet le smoke sur l'appareil ARM64 et `x86_64-linux-android` celui des AVD x86_64. Les autres ABI ne sont pas requises pour `0-pre.2`.
|
||||
|
||||
@@ -27,7 +27,7 @@ Initialiser une fois le projet Android généré par Tauri :
|
||||
(cd crates/apps/game-snake-poc-tauri && cargo tauri android init)
|
||||
```
|
||||
|
||||
Le répertoire `gen/` produit par Tauri est généré et ignoré ; il n'appartient pas aux deltas.
|
||||
Le répertoire `gen/` produit par Tauri est généré et ignoré ; il n'appartient pas aux deltas. Le correctif Gradle 9 est appliqué à ce répertoire par le hook Tauri au prochain `android dev` ou `android build`, y compris si `gen/android` existe déjà depuis `0-pre.2`. Il n'est donc pas nécessaire de rejouer `android init` pour appliquer le fix.
|
||||
|
||||
## Smoke Android de développement
|
||||
|
||||
|
||||
@@ -1,7 +1,42 @@
|
||||
// file: crates/apps/game-snake-poc-tauri/build.rs
|
||||
// version: 1
|
||||
// version: 2
|
||||
|
||||
//! Build-time integration for the Snake Tauri host.
|
||||
|
||||
const GENERATED_ANDROID_TAURI_BUILD_GRADLE: &str = "gen/android/app/tauri.build.gradle.kts";
|
||||
const GRADLE_8_IMPLEMENTATION_DECLARATION: &str = "val implementation by configurations";
|
||||
const GRADLE_9_IMPLEMENTATION_DECLARATION: &str = "val implementation = configurations.getByName(\"implementation\")";
|
||||
|
||||
fn patch_generated_android_gradle() {
|
||||
let manifest_dir = match std::env::var_os("CARGO_MANIFEST_DIR") {
|
||||
Some(value) => std::path::PathBuf::from(value),
|
||||
None => return,
|
||||
};
|
||||
let path = manifest_dir.join(GENERATED_ANDROID_TAURI_BUILD_GRADLE);
|
||||
let content = match std::fs::read_to_string(&path) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) if error.kind() == std::io::ErrorKind::NotFound => return,
|
||||
std::result::Result::Err(error) => {
|
||||
println!("cargo:warning=failed to inspect {}: {error}", path.display());
|
||||
return;
|
||||
},
|
||||
};
|
||||
if content.contains(GRADLE_9_IMPLEMENTATION_DECLARATION) {
|
||||
return;
|
||||
}
|
||||
if !content.contains(GRADLE_8_IMPLEMENTATION_DECLARATION) {
|
||||
println!("cargo:warning={} does not match the expected Tauri 2.11.x Gradle bridge", path.display());
|
||||
return;
|
||||
}
|
||||
let patched = content.replace(GRADLE_8_IMPLEMENTATION_DECLARATION, GRADLE_9_IMPLEMENTATION_DECLARATION);
|
||||
if let std::result::Result::Err(error) = std::fs::write(&path, patched) {
|
||||
println!("cargo:warning=failed to prepare {} for Gradle 9: {error}", path.display());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
fn main() {
|
||||
tauri_build::build();
|
||||
patch_generated_android_gradle();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -6,8 +6,9 @@
|
||||
"scripts": {
|
||||
"wasm:dev": "cargo build -p game-snake-poc-wasm --target wasm32-unknown-unknown && wasm-bindgen ../../../../builds/sasedev-games/target/wasm32-unknown-unknown/debug/game_snake_poc_wasm.wasm --target web --out-dir ../../../../builds/sasedev-games/game-snake-poc-tauri/wasm --out-name game_snake_poc_wasm",
|
||||
"wasm:build": "cargo build -p game-snake-poc-wasm --release --target wasm32-unknown-unknown && wasm-bindgen ../../../../builds/sasedev-games/target/wasm32-unknown-unknown/release/game_snake_poc_wasm.wasm --target web --out-dir ../../../../builds/sasedev-games/game-snake-poc-tauri/wasm --out-name game_snake_poc_wasm",
|
||||
"dev": "npm run wasm:dev && vite",
|
||||
"build": "npm run wasm:build && tsc && vite build"
|
||||
"dev": "npm run android:prepare && npm run wasm:dev && vite",
|
||||
"build": "npm run android:prepare && npm run wasm:build && tsc && vite build",
|
||||
"android:prepare": "node scripts/prepare-android-gradle.mjs"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fltsci/tauri-plugin-tracing": "^0.3",
|
||||
|
||||
@@ -0,0 +1,219 @@
|
||||
// file: crates/apps/game-snake-poc-tauri/scripts/prepare-android-gradle.mjs
|
||||
// version: 1
|
||||
|
||||
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
||||
import { resolve } from "node:path";
|
||||
import process from "node:process";
|
||||
|
||||
const GRADLE_VERSION = "9.6.1";
|
||||
const AGP_VERSION = "9.3.1";
|
||||
const KOTLIN_VERSION = "2.2.10";
|
||||
|
||||
function fail(message) {
|
||||
process.stderr.write(`Snake Tauri Android Gradle preparation failed: ${message}\n`);
|
||||
process.exitCode = 1;
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
function replaceRequired(content, previous, next, label) {
|
||||
if (content.includes(next)) {
|
||||
return content;
|
||||
}
|
||||
if (!content.includes(previous)) {
|
||||
return fail(`${label}: expected neither the Tauri 2.11.x form nor the prepared form`);
|
||||
}
|
||||
return content.replace(previous, next);
|
||||
}
|
||||
|
||||
function removeLine(content, line) {
|
||||
const withLf = `${line}\n`;
|
||||
if (content.includes(withLf)) {
|
||||
return content.replace(withLf, "");
|
||||
}
|
||||
if (content.endsWith(line)) {
|
||||
return content.slice(0, -line.length);
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
function appendProperty(content, property) {
|
||||
if (content.includes(`${property}\n`) || content.endsWith(property)) {
|
||||
return content;
|
||||
}
|
||||
const separator = content.endsWith("\n") ? "" : "\n";
|
||||
return `${content}${separator}${property}\n`;
|
||||
}
|
||||
|
||||
function updateFile(path, transform) {
|
||||
if (!existsSync(path)) {
|
||||
return fail(`missing generated Android file: ${path}`);
|
||||
}
|
||||
const before = readFileSync(path, "utf8");
|
||||
const after = transform(before);
|
||||
if (after !== before) {
|
||||
writeFileSync(path, after, "utf8");
|
||||
}
|
||||
}
|
||||
|
||||
function patchWrapper(androidRoot) {
|
||||
const path = resolve(androidRoot, "gradle/wrapper/gradle-wrapper.properties");
|
||||
updateFile(path, content => replaceRequired(
|
||||
content,
|
||||
"gradle-8.14.3-bin.zip",
|
||||
`gradle-${GRADLE_VERSION}-bin.zip`,
|
||||
"Gradle wrapper",
|
||||
));
|
||||
}
|
||||
|
||||
function patchRootBuild(androidRoot) {
|
||||
const path = resolve(androidRoot, "build.gradle.kts");
|
||||
updateFile(path, content => {
|
||||
let result = replaceRequired(
|
||||
content,
|
||||
'classpath("com.android.tools.build:gradle:8.11.0")',
|
||||
`classpath("com.android.tools.build:gradle:${AGP_VERSION}")`,
|
||||
"Android Gradle Plugin",
|
||||
);
|
||||
result = replaceRequired(
|
||||
result,
|
||||
'classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.25")',
|
||||
`classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${KOTLIN_VERSION}")`,
|
||||
"Kotlin Gradle Plugin",
|
||||
);
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
function patchBuildSrcManifest(androidRoot) {
|
||||
const path = resolve(androidRoot, "buildSrc/build.gradle.kts");
|
||||
updateFile(path, content => replaceRequired(
|
||||
content,
|
||||
'implementation("com.android.tools.build:gradle:8.11.0")',
|
||||
`implementation("com.android.tools.build:gradle:${AGP_VERSION}")`,
|
||||
"buildSrc Android Gradle Plugin",
|
||||
));
|
||||
}
|
||||
|
||||
function patchBuildTask(androidRoot) {
|
||||
const path = resolve(androidRoot, "buildSrc/src/main/kotlin/BuildTask.kt");
|
||||
updateFile(path, content => {
|
||||
let result = content;
|
||||
if (!result.includes("import javax.inject.Inject")) {
|
||||
result = replaceRequired(
|
||||
result,
|
||||
"import org.gradle.api.tasks.TaskAction\n",
|
||||
"import org.gradle.api.tasks.TaskAction\nimport javax.inject.Inject\nimport org.gradle.process.ExecOperations\n",
|
||||
"BuildTask imports",
|
||||
);
|
||||
}
|
||||
result = replaceRequired(
|
||||
result,
|
||||
"open class BuildTask : DefaultTask() {\n @Input\n var rootDirRel: String? = null\n",
|
||||
"abstract class BuildTask : DefaultTask() {\n @get:Inject\n abstract val execOperations: ExecOperations\n\n @Input\n var rootDirRel: String? = null\n @Input\n var projectDir: String? = null\n",
|
||||
"BuildTask ExecOperations declaration",
|
||||
);
|
||||
result = replaceRequired(
|
||||
result,
|
||||
" project.exec {\n workingDir(File(project.projectDir, rootDirRel))\n",
|
||||
" execOperations.exec {\n workingDir(File(projectDir, rootDirRel))\n",
|
||||
"BuildTask process execution",
|
||||
);
|
||||
result = result.replaceAll("project.logger.isEnabled", "logger.isEnabled");
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
function patchRustPlugin(androidRoot) {
|
||||
const path = resolve(androidRoot, "buildSrc/src/main/kotlin/RustPlugin.kt");
|
||||
updateFile(path, content => {
|
||||
let result = replaceRequired(
|
||||
content,
|
||||
" val targetBuildTask = project.tasks.maybeCreate(\n \"rustBuild$targetArchCapitalized$profileCapitalized\",\n BuildTask::class.java\n ).apply {\n",
|
||||
" val targetBuildTask = project.tasks.register(\n \"rustBuild$targetArchCapitalized$profileCapitalized\",\n BuildTask::class.java\n ) {\n",
|
||||
"RustPlugin task registration",
|
||||
);
|
||||
if (!result.includes(" projectDir = project.projectDir.path\n")) {
|
||||
result = replaceRequired(
|
||||
result,
|
||||
" rootDirRel = config.rootDirRel\n target = targetName\n",
|
||||
" rootDirRel = config.rootDirRel\n projectDir = project.projectDir.path\n target = targetName\n",
|
||||
"RustPlugin project directory",
|
||||
);
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
function patchAppBuild(androidRoot) {
|
||||
const path = resolve(androidRoot, "app/build.gradle.kts");
|
||||
updateFile(path, content => {
|
||||
let result = content;
|
||||
if (!result.includes("import org.jetbrains.kotlin.gradle.dsl.JvmTarget")) {
|
||||
result = `import org.jetbrains.kotlin.gradle.dsl.JvmTarget\n${result}`;
|
||||
}
|
||||
result = replaceRequired(
|
||||
result,
|
||||
" isMinifyEnabled = true\n proguardFiles(\n *fileTree(\".\") { include(\"**/*.pro\") }\n .plus(getDefaultProguardFile(\"proguard-android-optimize.txt\"))\n .toList().toTypedArray()\n )\n",
|
||||
" optimization {\n enable = true\n }\n proguardFiles(\n *fileTree(\".\") {\n include(\"**/*.pro\")\n exclude(\"build/**\")\n }.files.toTypedArray()\n )\n",
|
||||
"Android release optimization DSL",
|
||||
);
|
||||
result = replaceRequired(
|
||||
result,
|
||||
" kotlinOptions {\n jvmTarget = \"1.8\"\n }\n buildFeatures {\n",
|
||||
" compileOptions {\n sourceCompatibility = JavaVersion.VERSION_1_8\n targetCompatibility = JavaVersion.VERSION_1_8\n }\n buildFeatures {\n",
|
||||
"Android compile options",
|
||||
);
|
||||
if (!result.includes("kotlin {\n compilerOptions")) {
|
||||
result = replaceRequired(
|
||||
result,
|
||||
"}\n\nrust {\n",
|
||||
"}\n\nkotlin {\n compilerOptions {\n jvmTarget = JvmTarget.JVM_1_8\n }\n}\n\nrust {\n",
|
||||
"Kotlin compiler options",
|
||||
);
|
||||
}
|
||||
result = result.replace('apply(from = "tauri.build.gradle.kts")', 'apply(from = file("tauri.build.gradle.kts"))');
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
function patchGradleProperties(androidRoot) {
|
||||
const path = resolve(androidRoot, "gradle.properties");
|
||||
updateFile(path, content => {
|
||||
let result = content;
|
||||
result = removeLine(result, "android.useAndroidX=true");
|
||||
result = removeLine(result, "android.nonTransitiveRClass=true");
|
||||
result = removeLine(result, "android.nonFinalResIds=false");
|
||||
result = appendProperty(result, "org.gradle.configuration-cache=true");
|
||||
result = appendProperty(result, "android.proguard.failOnMissingFiles=false");
|
||||
result = appendProperty(result, "android.builtInKotlin=false");
|
||||
result = appendProperty(result, "android.newDsl=false");
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
function resolveAndroidRoot() {
|
||||
const marker = "--android-project";
|
||||
const markerIndex = process.argv.indexOf(marker);
|
||||
if (markerIndex >= 0) {
|
||||
const value = process.argv[markerIndex + 1];
|
||||
if (value === undefined || value.startsWith("--")) {
|
||||
return fail(`${marker} requires a path`);
|
||||
}
|
||||
return resolve(process.cwd(), value);
|
||||
}
|
||||
return resolve(process.cwd(), "gen/android");
|
||||
}
|
||||
|
||||
function main() {
|
||||
const androidRoot = resolveAndroidRoot();
|
||||
patchWrapper(androidRoot);
|
||||
patchRootBuild(androidRoot);
|
||||
patchBuildSrcManifest(androidRoot);
|
||||
patchBuildTask(androidRoot);
|
||||
patchRustPlugin(androidRoot);
|
||||
patchAppBuild(androidRoot);
|
||||
patchGradleProperties(androidRoot);
|
||||
process.stdout.write(`Prepared Tauri Android Gradle ${GRADLE_VERSION} / AGP ${AGP_VERSION} / Kotlin ${KOTLIN_VERSION}.\n`);
|
||||
}
|
||||
|
||||
main();
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/apps/game-snake-poc-tauri/src/tauri.rs
|
||||
// version: 1
|
||||
// version: 2
|
||||
|
||||
//! Tauri runtime assembly and Web/Rust commands for the Snake Android POC.
|
||||
|
||||
@@ -24,9 +24,7 @@ fn configure_commands(builder: tauri::Builder<tauri::Wry>) -> tauri::Builder<tau
|
||||
return builder.invoke_handler(tauri::generate_handler![get_runtime_descriptor, frontend_ready]);
|
||||
}
|
||||
|
||||
/// Runs the Snake Tauri application.
|
||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||
pub fn run() -> std::result::Result<(), String> {
|
||||
fn run_runtime() -> std::result::Result<(), String> {
|
||||
let logging_guard = game_logging_lib::init_console_tracing();
|
||||
let _logging_guard = match logging_guard {
|
||||
std::result::Result::Ok(value) => value,
|
||||
@@ -63,3 +61,25 @@ pub fn run() -> std::result::Result<(), String> {
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/// Runs the Snake Tauri application on a desktop host.
|
||||
#[cfg(not(mobile))]
|
||||
pub fn run() -> std::result::Result<(), String> {
|
||||
return run_runtime();
|
||||
}
|
||||
|
||||
/// Runs the Snake Tauri application from Tauri's mobile entry point.
|
||||
#[cfg(mobile)]
|
||||
#[tauri::mobile_entry_point]
|
||||
pub fn run() {
|
||||
let result = run_runtime();
|
||||
if let std::result::Result::Err(error) = result {
|
||||
tracing::error!(
|
||||
target: "games::tauri::snake",
|
||||
action = "mobile_entrypoint_error",
|
||||
error = error.as_str(),
|
||||
"Snake Tauri mobile entry point terminated after a runtime error"
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user