cargo-ohos-app 0.1.81

Cargo subcommand for packaging Rust GUI apps as OHOS applications
Documentation
use std::fs;
use std::io::Write;
use std::path::{Path, PathBuf};

use crate::cli::BuildCommand;
use crate::commands::command_env;
use crate::config::AppContext;
use crate::errors::{HarmonyAppError, Result};
use crate::runner::{CommandRunner, CommandSpec};

pub fn run<R: CommandRunner, W: Write>(
    command: &BuildCommand,
    cwd: &Path,
    runner: &mut R,
    stdout: &mut W,
) -> Result<()> {
    let app = AppContext::load(&command.common, cwd)?;
    let plan = build_plan(&app);

    if command.common.dry_run {
        writeln!(stdout, "[dry-run] {}", plan.command.display())?;
        writeln!(
            stdout,
            "[dry-run] copy {} -> {}",
            plan.source.display(),
            plan.destination.display()
        )?;
        return Ok(());
    }

    runner.run(&plan.command)?;
    if !plan.source.exists() {
        return Err(HarmonyAppError::MissingFile {
            path: plan.source.clone(),
        });
    }
    remove_legacy_shared_artifact(&plan.legacy_shared_destination)?;
    if let Some(parent) = plan.destination.parent() {
        fs::create_dir_all(parent).map_err(|source| HarmonyAppError::io(parent, source))?;
    }
    fs::copy(&plan.source, &plan.destination)
        .map_err(|source| HarmonyAppError::io(&plan.destination, source))?;

    writeln!(
        stdout,
        "Built Rust staticlib and copied it to {}",
        plan.destination.display()
    )?;
    Ok(())
}

pub(crate) struct BuildPlan {
    pub command: CommandSpec,
    pub source: PathBuf,
    pub destination: PathBuf,
    pub legacy_shared_destination: PathBuf,
}

pub(crate) fn build_plan(app: &AppContext) -> BuildPlan {
    let mut args = vec![
        "rustc".to_string(),
        "--lib".to_string(),
        "--manifest-path".to_string(),
        app.project.manifest_path.display().to_string(),
        "--target".to_string(),
        app.config.target.clone(),
    ];
    if app.config.profile_dir == "release" {
        args.push("--release".to_string());
    }
    args.push("--".to_string());
    args.push("--crate-type".to_string());
    args.push("staticlib".to_string());

    let source = app
        .project
        .static_artifact_path(&app.config.target, &app.config.profile_dir);
    let destination = app
        .config
        .output_dir
        .join("entry")
        .join("src")
        .join("main")
        .join("cpp")
        .join("libs")
        .join(&app.config.abi)
        .join(format!("lib{}.a", app.project.lib_name));

    BuildPlan {
        command: CommandSpec {
            program: PathBuf::from("cargo"),
            args,
            cwd: app.project.project_dir.clone(),
            env: command_env(app),
        },
        source,
        destination,
        legacy_shared_destination: app
            .config
            .output_dir
            .join("entry")
            .join("src")
            .join("main")
            .join("cpp")
            .join("libs")
            .join(&app.config.abi)
            .join(format!("lib{}.so", app.project.lib_name)),
    }
}

fn remove_legacy_shared_artifact(path: &Path) -> Result<()> {
    if path.exists() {
        fs::remove_file(path).map_err(|source| HarmonyAppError::io(path, source))?;
    }
    Ok(())
}