DemesneGH commented on code in PR #263:
URL: 
https://github.com/apache/teaclave-trustzone-sdk/pull/263#discussion_r2633462095


##########
cargo-optee/src/config.rs:
##########
@@ -0,0 +1,637 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use anyhow::{bail, Result};
+use cargo_metadata::MetadataCommand;
+use serde_json::Value;
+use std::path::{Path, PathBuf};
+
+use crate::common::Arch;
+
+/// Build configuration that can be discovered from proto metadata
+#[derive(Debug, Clone)]
+pub struct BuildConfig {
+    pub arch: Arch,
+    pub debug: bool,
+    pub std: bool,
+    pub ta_dev_kit_dir: Option<PathBuf>,
+    pub optee_client_export: Option<PathBuf>,
+    pub signing_key: Option<PathBuf>,
+    pub uuid_path: Option<PathBuf>,
+    /// additional environment key-value pairs, that should be passed to 
underlying
+    /// build commands
+    pub env: Vec<(String, String)>,
+}
+
+impl BuildConfig {
+    /// Create a new build config by resolving parameters with priority:
+    /// 1. Command line arguments (highest priority)
+    /// 2. [package.metadata.optee.<component_type>] in Cargo.toml
+    /// 3. Default values or error for mandatory parameters
+    #[allow(clippy::too_many_arguments)]
+    pub fn resolve(
+        project_path: &Path,
+        component_type: &str, // "ta", "ca", or "plugin"

Review Comment:
   component_type: use the enum, not string



##########
cargo-optee/src/main.rs:
##########
@@ -0,0 +1,567 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use anyhow::bail;
+use clap::{Args, Parser, Subcommand};
+use std::env;
+use std::path::PathBuf;
+use std::process;
+
+mod ca_builder;
+mod common;
+mod config;
+mod ta_builder;
+
+use common::Arch;
+
+/// Parse environment variable in KEY=VALUE format
+fn parse_env_var(s: &str) -> Result<(String, String), String> {
+    if let Some(eq_pos) = s.find('=') {
+        let (key, value) = s.split_at(eq_pos);
+        let value = &value[1..]; // Skip the '=' character
+        Ok((key.to_string(), value.to_string()))
+    } else {
+        Err(format!(
+            "Invalid environment variable format: '{}'. Expected 'KEY=VALUE'",
+            s
+        ))
+    }
+}
+
+/// Resolve a potentially relative path to an absolute path based on the 
project directory
+fn resolve_path_relative_to_project(path: &PathBuf, project_path: 
&std::path::Path) -> PathBuf {
+    if path.is_absolute() {
+        path.clone()
+    } else {
+        project_path.join(path)
+    }
+}
+
+/// Execute TA build or install (shared logic)
+fn execute_ta_command(
+    common: CommonBuildArgs,
+    std: Option<bool>,
+    ta_dev_kit_dir: Option<PathBuf>,
+    signing_key: Option<PathBuf>,
+    uuid_path: Option<PathBuf>,
+    install_target_dir: Option<&PathBuf>,
+) -> anyhow::Result<()> {
+    // Resolve project path from manifest or current directory
+    let project_path = if let Some(manifest) = common.manifest_path {
+        let parent = manifest
+            .parent()
+            .ok_or_else(|| anyhow::anyhow!("Invalid manifest path"))?;
+
+        // Normalize: if parent is empty (e.g., manifest is just "Cargo.toml"),
+        // use current directory instead
+        if parent.as_os_str().is_empty() {
+            std::env::current_dir()?
+        } else {
+            // Canonicalize must succeed, otherwise treat as invalid manifest 
path
+            parent
+                .canonicalize()
+                .map_err(|_| anyhow::anyhow!("Invalid manifest path"))?
+        }
+    } else {
+        std::env::current_dir()?
+    };
+
+    // Resolve build configuration with priority: CLI > metadata > error
+    let build_config = config::BuildConfig::resolve(
+        &project_path,
+        "ta", // Component type for TA
+        common.arch,
+        Some(common.debug),
+        std, // None means read from config, Some(true/false) means CLI 
override
+        ta_dev_kit_dir,
+        None, // optee_client_export not needed for TA
+        signing_key,
+        uuid_path.clone(),
+    )?;
+
+    // Print the final configuration being used
+    build_config.print_config("ta", &project_path);
+
+    // Get required ta_dev_kit_dir and resolve relative to project
+    let ta_dev_kit_dir_config = build_config.require_ta_dev_kit_dir()?;
+    let ta_dev_kit_dir = 
resolve_path_relative_to_project(&ta_dev_kit_dir_config, &project_path);
+
+    // Validate that ta_dev_kit_dir exists (print absolute path)
+    if !ta_dev_kit_dir.exists() {
+        bail!(
+            "TA development kit directory does not exist: {:?}",
+            ta_dev_kit_dir
+        );
+    }
+
+    // Resolve signing key relative to project directory
+    let signing_key_config = 
build_config.resolve_signing_key(&ta_dev_kit_dir_config);
+    let signing_key_path = 
resolve_path_relative_to_project(&signing_key_config, &project_path);
+
+    // Validate that signing key exists (print absolute path)
+    if !signing_key_path.exists() {
+        bail!("Signing key file does not exist: {:?}", signing_key_path);
+    }
+
+    // Resolve UUID path: if provided via CLI, it's relative to current dir
+    // if from metadata, it's relative to project dir
+    let resolved_uuid_path = if uuid_path.is_some() {
+        // CLI provided - resolve relative to current directory
+        std::env::current_dir()?.join(build_config.get_uuid_path())
+    } else {
+        // From metadata or default - resolve relative to project directory
+        project_path.join(build_config.get_uuid_path())
+    };
+
+    // Merge env variables: CLI overrides + metadata env
+    let mut merged_env = build_config.env.clone();
+    merged_env.extend(common.env);
+
+    let ta_config = ta_builder::TaBuildConfig {
+        arch: build_config.arch,
+        std: build_config.std,
+        ta_dev_kit_dir,
+        signing_key: signing_key_path,
+        debug: build_config.debug,
+        path: project_path,
+        uuid_path: resolved_uuid_path,
+        env: merged_env,
+        no_default_features: common.no_default_features,
+        features: common.features,
+    };
+
+    ta_builder::build_ta(ta_config, install_target_dir.map(|p| p.as_path()))
+}
+
+/// Execute CA build or install (shared logic)
+fn execute_ca_command(
+    common: CommonBuildArgs,
+    optee_client_export: Option<PathBuf>,
+    uuid_path: Option<PathBuf>,
+    plugin: bool,
+    install_target_dir: Option<&PathBuf>,
+) -> anyhow::Result<()> {
+    // Resolve project path from manifest or current directory
+    let project_path = if let Some(manifest) = common.manifest_path {
+        let parent = manifest
+            .parent()
+            .ok_or_else(|| anyhow::anyhow!("Invalid manifest path"))?;
+
+        // Normalize: if parent is empty (e.g., manifest is just "Cargo.toml"),
+        // use current directory instead
+        if parent.as_os_str().is_empty() {
+            std::env::current_dir()?
+        } else {
+            // Canonicalize must succeed, otherwise treat as invalid manifest 
path
+            parent
+                .canonicalize()
+                .map_err(|_| anyhow::anyhow!("Invalid manifest path"))?
+        }
+    } else {
+        std::env::current_dir()?
+    };
+
+    let component_type = if plugin { "plugin" } else { "ca" };
+
+    // Resolve build configuration
+    let build_config = config::BuildConfig::resolve(
+        &project_path,
+        component_type,
+        common.arch,
+        Some(common.debug),
+        None, // std not applicable for CA/Plugin
+        None, // ta_dev_kit_dir not needed for CA/Plugin
+        optee_client_export,
+        None, // signing_key not needed for CA/Plugin
+        uuid_path,
+    )?;
+
+    // Print the final configuration being used
+    build_config.print_config(component_type, &project_path);
+
+    // Get required optee_client_export and resolve relative to project
+    let optee_client_export_config = 
build_config.require_optee_client_export()?;
+    let optee_client_export =
+        resolve_path_relative_to_project(&optee_client_export_config, 
&project_path);
+
+    // Validate that optee_client_export exists (print absolute path)
+    if !optee_client_export.exists() {
+        bail!(
+            "OP-TEE client export directory does not exist: {:?}",
+            optee_client_export
+        );
+    }
+
+    // Merge env variables: CLI overrides + metadata env
+    let mut merged_env = build_config.env.clone();
+    merged_env.extend(common.env);
+
+    let ca_config = ca_builder::CaBuildConfig {
+        arch: build_config.arch,
+        optee_client_export,
+        debug: build_config.debug,
+        path: project_path,
+        plugin,
+        uuid_path: if plugin {
+            build_config.uuid_path.clone()
+        } else {
+            None
+        },
+        env: merged_env,
+        no_default_features: common.no_default_features,
+        features: common.features,
+    };
+
+    ca_builder::build_ca(ca_config, install_target_dir.map(|p| p.as_path()))
+}
+
+/// Common build command arguments shared across TA, CA, and Plugin builds
+#[derive(Debug, Args)]
+struct CommonBuildArgs {
+    /// Path to the Cargo.toml manifest file
+    #[arg(long = "manifest-path")]
+    manifest_path: Option<PathBuf>,
+
+    /// Target architecture (default: aarch64)
+    #[arg(long = "arch")]
+    arch: Option<Arch>,
+
+    /// Enable debug build (default: false)
+    #[arg(long = "debug")]
+    debug: bool,
+
+    /// Environment overrides in the form of `"KEY=VALUE"` strings. This flag 
can be repeated.
+    ///
+    /// This is generally not needed to be used explicitly during regular 
development.
+    ///
+    /// This makes sense to be used to specify custom var e.g. `RUSTFLAGS`.
+    #[arg(long = "env", value_parser = parse_env_var, action = 
clap::ArgAction::Append)]
+    env: Vec<(String, String)>,
+
+    /// Disable default features (will append --no-default-features to cargo 
build)
+    #[arg(long = "no-default-features")]
+    no_default_features: bool,
+
+    /// Custom features to enable (will append --features to cargo build)
+    #[arg(long = "features")]
+    features: Option<String>,
+}
+
+#[derive(Debug, Parser)]
+#[clap(version = env!("CARGO_PKG_VERSION"))]
+#[clap(about = "Build tool for OP-TEE Rust projects")]
+pub(crate) struct Cli {
+    #[clap(subcommand)]
+    cmd: Command,
+}
+
+#[derive(Debug, Subcommand)]
+enum BuildCommand {
+    /// Build a Trusted Application (TA)
+    #[command(about = "Build a Trusted Application (TA)")]
+    TA {
+        #[command(flatten)]
+        build_cmd: TABuildArgs,
+    },
+    /// Build a Client Application (Host)
+    #[command(about = "Build a Client Application (Host)")]
+    CA {
+        #[command(flatten)]
+        build_cmd: CABuildArgs,
+    },
+    /// Build a Plugin (Shared Library)
+    #[command(about = "Build a Plugin (Shared Library)")]
+    Plugin {
+        #[command(flatten)]
+        build_cmd: PluginBuildArgs,
+    },
+}
+
+#[derive(Debug, Subcommand)]
+enum Command {
+    /// Build OP-TEE components
+    #[clap(name = "build")]
+    #[command(subcommand)]
+    Build(BuildCommand),
+    /// Install OP-TEE components
+    #[clap(name = "install")]
+    #[command(subcommand)]
+    Install(InstallCommand),
+    /// Clean OP-TEE components
+    #[clap(name = "clean")]
+    Clean {
+        #[command(flatten)]
+        clean_cmd: CleanCommand,
+    },
+}
+
+#[derive(Debug, Subcommand)]
+enum InstallCommand {
+    /// Install a Trusted Application (TA)
+    #[command(about = "Install a Trusted Application (TA) to target 
directory")]
+    TA {
+        /// Target directory to install the TA binary (default: "shared")
+        #[arg(long = "target-dir", default_value = "shared")]
+        target_dir: PathBuf,
+
+        #[command(flatten)]
+        build_cmd: TABuildArgs,
+    },
+    /// Install a Client Application (Host)
+    #[command(about = "Install a Client Application (Host) to target 
directory")]
+    CA {
+        /// Target directory to install the CA binary (default: "shared")
+        #[arg(long = "target-dir", default_value = "shared")]
+        target_dir: PathBuf,
+
+        #[command(flatten)]
+        build_cmd: CABuildArgs,
+    },
+    /// Install a Plugin (Shared Library)
+    #[command(about = "Install a Plugin (Shared Library) to target directory")]
+    Plugin {
+        /// Target directory to install the plugin binary (default: "shared")
+        #[arg(long = "target-dir", default_value = "shared")]
+        target_dir: PathBuf,
+
+        #[command(flatten)]
+        build_cmd: PluginBuildArgs,
+    },
+}
+
+/// TA-specific build arguments
+#[derive(Debug, Args)]
+struct TABuildArgs {
+    #[command(flatten)]
+    common: CommonBuildArgs,
+
+    /// Enable std feature for the TA
+    /// If neither --std nor --no-std is specified, the value will be read 
from Cargo.toml metadata
+    #[arg(long = "std", action = clap::ArgAction::SetTrue, conflicts_with = 
"no_std")]
+    std: bool,
+
+    /// Disable std feature for the TA (use no-std mode)
+    /// If neither --std nor --no-std is specified, the value will be read 
from Cargo.toml metadata
+    #[arg(long = "no-std", action = clap::ArgAction::SetTrue, conflicts_with = 
"std")]
+    no_std: bool,
+
+    /// OP-TEE TA development kit export directory
+    #[arg(long = "ta-dev-kit-dir")]
+    ta_dev_kit_dir: Option<PathBuf>,
+
+    /// TA signing key path (default: TA_DEV_KIT_DIR/keys/default_ta.pem)
+    #[arg(long = "signing-key")]
+    signing_key: Option<PathBuf>,
+
+    /// UUID file path (default: "../uuid.txt")
+    #[arg(long = "uuid-path")]
+    uuid_path: Option<PathBuf>,
+}
+
+/// CA-specific build arguments
+#[derive(Debug, Args)]
+struct CABuildArgs {
+    #[command(flatten)]
+    common: CommonBuildArgs,
+
+    /// OP-TEE client export directory
+    #[arg(long = "optee-client-export")]
+    optee_client_export: Option<PathBuf>,
+}
+
+/// Plugin-specific build arguments
+#[derive(Debug, Args)]
+struct PluginBuildArgs {
+    #[command(flatten)]
+    common: CommonBuildArgs,
+
+    /// OP-TEE client export directory
+    #[arg(long = "optee-client-export")]
+    optee_client_export: Option<PathBuf>,
+
+    /// UUID file path (default: "../uuid.txt")
+    #[arg(long = "uuid-path")]
+    uuid_path: Option<PathBuf>,
+}
+
+/// Clean command arguments
+#[derive(Debug, Args)]
+struct CleanCommand {
+    /// Path to the Cargo.toml manifest file
+    #[arg(long = "manifest-path")]
+    manifest_path: Option<PathBuf>,
+}
+
+/// Source cargo environment from a given path
+fn source_cargo_env(env_path: &str) -> bool {
+    if std::path::Path::new(env_path).exists() {
+        std::process::Command::new("bash")
+            .arg("-c")
+            .arg(format!("source {}", env_path))
+            .status()
+            .map(|status| status.success())
+            .unwrap_or(false)
+    } else {
+        false
+    }
+}
+
+/// Setup cargo environment by checking availability and sourcing environment 
if needed
+fn setup_cargo_environment() -> anyhow::Result<()> {
+    // Check if cargo is available
+    let cargo_available = std::process::Command::new("which")
+        .arg("cargo")
+        .output()
+        .map(|output| output.status.success())
+        .unwrap_or(false);
+
+    if cargo_available {
+        return Ok(());
+    }
+
+    // Try to source .cargo/env from ~/.cargo/env or $CARGO_HOME/env
+    let mut sourced = false;
+    if let Ok(home) = env::var("HOME") {
+        sourced = source_cargo_env(&format!("{}/.cargo/env", home));
+    }
+    if !sourced {
+        if let Ok(cargo_home) = env::var("CARGO_HOME") {
+            sourced = source_cargo_env(&format!("{}/env", cargo_home));
+        }
+    }
+
+    if !sourced {
+        anyhow::bail!("cargo command not found. Please install Rust: curl 
--proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh");
+    }
+
+    Ok(())
+}
+
+fn main() {
+    
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info"))
+        .format_timestamp_millis()
+        .init();
+
+    // Setup cargo environment
+    if let Err(e) = setup_cargo_environment() {
+        eprintln!("Error: {}", e);
+        process::exit(1);
+    }
+
+    let cli = Cli::parse();
+    let result = execute_command(cli.cmd);
+
+    if let Err(e) = result {
+        eprintln!("Error: {}", e);
+        process::exit(1);
+    }
+}
+
+fn execute_command(cmd: Command) -> anyhow::Result<()> {
+    match cmd {
+        Command::Build(build_cmd) => match build_cmd {
+            BuildCommand::TA { build_cmd } => {
+                // Convert bool flags to Option<bool>: --std -> Some(true), 
--no-std -> Some(false), neither -> None
+                let std = if build_cmd.std {
+                    Some(true)
+                } else if build_cmd.no_std {
+                    Some(false)
+                } else {
+                    None
+                };

Review Comment:
   Move this into a fn to reduce code, try to simplify like:
   ```
           match (build_cmd.std, build_cmd.no_std) {
               (true, false) => Some(true),
               (false, true) => Some(false),
               _ => None,
           }
   ```



##########
cargo-optee/src/main.rs:
##########
@@ -0,0 +1,567 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use anyhow::bail;
+use clap::{Args, Parser, Subcommand};
+use std::env;
+use std::path::PathBuf;
+use std::process;
+
+mod ca_builder;
+mod common;
+mod config;
+mod ta_builder;
+
+use common::Arch;
+
+/// Parse environment variable in KEY=VALUE format
+fn parse_env_var(s: &str) -> Result<(String, String), String> {
+    if let Some(eq_pos) = s.find('=') {
+        let (key, value) = s.split_at(eq_pos);
+        let value = &value[1..]; // Skip the '=' character
+        Ok((key.to_string(), value.to_string()))
+    } else {
+        Err(format!(
+            "Invalid environment variable format: '{}'. Expected 'KEY=VALUE'",
+            s
+        ))
+    }
+}
+
+/// Resolve a potentially relative path to an absolute path based on the 
project directory
+fn resolve_path_relative_to_project(path: &PathBuf, project_path: 
&std::path::Path) -> PathBuf {
+    if path.is_absolute() {
+        path.clone()
+    } else {
+        project_path.join(path)
+    }
+}
+
+/// Execute TA build or install (shared logic)
+fn execute_ta_command(
+    common: CommonBuildArgs,
+    std: Option<bool>,
+    ta_dev_kit_dir: Option<PathBuf>,
+    signing_key: Option<PathBuf>,
+    uuid_path: Option<PathBuf>,
+    install_target_dir: Option<&PathBuf>,
+) -> anyhow::Result<()> {
+    // Resolve project path from manifest or current directory
+    let project_path = if let Some(manifest) = common.manifest_path {
+        let parent = manifest
+            .parent()
+            .ok_or_else(|| anyhow::anyhow!("Invalid manifest path"))?;
+
+        // Normalize: if parent is empty (e.g., manifest is just "Cargo.toml"),
+        // use current directory instead
+        if parent.as_os_str().is_empty() {
+            std::env::current_dir()?
+        } else {
+            // Canonicalize must succeed, otherwise treat as invalid manifest 
path
+            parent
+                .canonicalize()
+                .map_err(|_| anyhow::anyhow!("Invalid manifest path"))?
+        }
+    } else {
+        std::env::current_dir()?
+    };
+
+    // Resolve build configuration with priority: CLI > metadata > error
+    let build_config = config::BuildConfig::resolve(
+        &project_path,
+        "ta", // Component type for TA
+        common.arch,
+        Some(common.debug),
+        std, // None means read from config, Some(true/false) means CLI 
override
+        ta_dev_kit_dir,
+        None, // optee_client_export not needed for TA
+        signing_key,
+        uuid_path.clone(),
+    )?;
+
+    // Print the final configuration being used
+    build_config.print_config("ta", &project_path);
+
+    // Get required ta_dev_kit_dir and resolve relative to project
+    let ta_dev_kit_dir_config = build_config.require_ta_dev_kit_dir()?;
+    let ta_dev_kit_dir = 
resolve_path_relative_to_project(&ta_dev_kit_dir_config, &project_path);
+
+    // Validate that ta_dev_kit_dir exists (print absolute path)
+    if !ta_dev_kit_dir.exists() {
+        bail!(
+            "TA development kit directory does not exist: {:?}",
+            ta_dev_kit_dir
+        );
+    }

Review Comment:
   Move this check into resolve_path_relative_to_project() to reduce the 
duplicate code, the same case is in line 115-118, please also check and fix 
other similar cases



##########
cargo-optee/src/main.rs:
##########
@@ -0,0 +1,567 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use anyhow::bail;
+use clap::{Args, Parser, Subcommand};
+use std::env;
+use std::path::PathBuf;
+use std::process;
+
+mod ca_builder;
+mod common;
+mod config;
+mod ta_builder;
+
+use common::Arch;
+
+/// Parse environment variable in KEY=VALUE format
+fn parse_env_var(s: &str) -> Result<(String, String), String> {
+    if let Some(eq_pos) = s.find('=') {
+        let (key, value) = s.split_at(eq_pos);
+        let value = &value[1..]; // Skip the '=' character
+        Ok((key.to_string(), value.to_string()))
+    } else {
+        Err(format!(
+            "Invalid environment variable format: '{}'. Expected 'KEY=VALUE'",
+            s
+        ))
+    }
+}
+
+/// Resolve a potentially relative path to an absolute path based on the 
project directory
+fn resolve_path_relative_to_project(path: &PathBuf, project_path: 
&std::path::Path) -> PathBuf {
+    if path.is_absolute() {
+        path.clone()
+    } else {
+        project_path.join(path)
+    }
+}
+
+/// Execute TA build or install (shared logic)
+fn execute_ta_command(
+    common: CommonBuildArgs,
+    std: Option<bool>,
+    ta_dev_kit_dir: Option<PathBuf>,
+    signing_key: Option<PathBuf>,
+    uuid_path: Option<PathBuf>,
+    install_target_dir: Option<&PathBuf>,
+) -> anyhow::Result<()> {
+    // Resolve project path from manifest or current directory
+    let project_path = if let Some(manifest) = common.manifest_path {
+        let parent = manifest
+            .parent()
+            .ok_or_else(|| anyhow::anyhow!("Invalid manifest path"))?;
+
+        // Normalize: if parent is empty (e.g., manifest is just "Cargo.toml"),
+        // use current directory instead
+        if parent.as_os_str().is_empty() {
+            std::env::current_dir()?
+        } else {
+            // Canonicalize must succeed, otherwise treat as invalid manifest 
path
+            parent
+                .canonicalize()
+                .map_err(|_| anyhow::anyhow!("Invalid manifest path"))?
+        }
+    } else {
+        std::env::current_dir()?
+    };
+

Review Comment:
   Move this into a fn for code reuse, same code in line158-175



##########
cargo-optee/src/main.rs:
##########
@@ -0,0 +1,567 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use anyhow::bail;
+use clap::{Args, Parser, Subcommand};
+use std::env;
+use std::path::PathBuf;
+use std::process;
+
+mod ca_builder;
+mod common;
+mod config;
+mod ta_builder;
+
+use common::Arch;
+
+/// Parse environment variable in KEY=VALUE format
+fn parse_env_var(s: &str) -> Result<(String, String), String> {
+    if let Some(eq_pos) = s.find('=') {
+        let (key, value) = s.split_at(eq_pos);
+        let value = &value[1..]; // Skip the '=' character
+        Ok((key.to_string(), value.to_string()))
+    } else {
+        Err(format!(
+            "Invalid environment variable format: '{}'. Expected 'KEY=VALUE'",
+            s
+        ))
+    }

Review Comment:
   Try to use this:
   ```
       s.split_once('=')
           .map(|(key, value)| (key.to_string(), value.to_string()))
           .ok_or_else(|| format!(
               "Invalid environment variable format: '{}'. Expected 
'KEY=VALUE'",
               s
           ))
   ```



##########
cargo-optee/src/ta_builder.rs:
##########
@@ -0,0 +1,505 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use anyhow::{bail, Result};
+use std::env;
+use std::fs;
+use std::path::Path;
+use std::path::PathBuf;
+use std::process::Command;
+use tempfile::TempDir;
+use toml::Value;
+
+use crate::common::{print_cargo_command, print_output_and_bail, 
read_uuid_from_file, Arch};
+
+// Embed the target JSON files at compile time
+const AARCH64_TARGET_JSON: &str = 
include_str!("../aarch64-unknown-optee.json");
+const ARM_TARGET_JSON: &str = include_str!("../arm-unknown-optee.json");
+
+// Target configurations for different architectures and std modes
+const TARGET_CONFIGS: [(Arch, bool, &str, &str); 4] = [
+    // (Architecture, has_std, target, cross_compile_prefix)
+    (
+        Arch::Arm,
+        false,
+        "arm-unknown-linux-gnueabihf",
+        "arm-linux-gnueabihf-",
+    ),
+    (Arch::Arm, true, "arm-unknown-optee", "arm-linux-gnueabihf-"),
+    (
+        Arch::Aarch64,
+        false,
+        "aarch64-unknown-linux-gnu",
+        "aarch64-linux-gnu-",
+    ),
+    (
+        Arch::Aarch64,
+        true,
+        "aarch64-unknown-optee",
+        "aarch64-linux-gnu-",
+    ),
+];
+
+/// Check if the required cross-compile toolchain is available
+fn check_toolchain_exists(cross_compile_prefix: &str) -> Result<()> {
+    let gcc_command = format!("{}gcc", cross_compile_prefix);
+    let objcopy_command = format!("{}objcopy", cross_compile_prefix);
+
+    // Check if gcc exists
+    let gcc_check = Command::new("which").arg(&gcc_command).output();
+
+    // Check if objcopy exists
+    let objcopy_check = Command::new("which").arg(&objcopy_command).output();
+
+    let gcc_exists = gcc_check.map_or(false, |output| output.status.success());
+    let objcopy_exists = objcopy_check.map_or(false, |output| 
output.status.success());
+
+    if !gcc_exists || !objcopy_exists {
+        let missing_tools: Vec<&str> = [
+            if !gcc_exists {
+                Some(gcc_command.as_str())
+            } else {
+                None
+            },
+            if !objcopy_exists {
+                Some(objcopy_command.as_str())
+            } else {
+                None
+            },
+        ]
+        .iter()
+        .filter_map(|&x| x)
+        .collect();
+
+        eprintln!("Error: Required cross-compile toolchain not found!");
+        eprintln!("Missing tools: {}", missing_tools.join(", "));
+        eprintln!();
+        eprintln!("Please install the required toolchain:");
+        eprintln!();
+        eprintln!("# For aarch64 host (ARM64 machine):");
+        eprintln!("apt update && apt -y install gcc gcc-arm-linux-gnueabihf");
+        eprintln!();
+        eprintln!("# For x86_64 host (Intel/AMD machine):");
+        eprintln!("apt update && apt -y install gcc-aarch64-linux-gnu 
gcc-arm-linux-gnueabihf");
+        eprintln!();
+        eprintln!("Or manually install the cross-compilation tools for your 
target architecture.");
+
+        bail!("Cross-compile toolchain not available");
+    }
+
+    Ok(())
+}
+
+#[derive(Clone)]
+pub struct TaBuildConfig {
+    pub arch: Arch,              // Architecture
+    pub std: bool,               // Enable std feature
+    pub ta_dev_kit_dir: PathBuf, // Path to TA dev kit
+    pub signing_key: PathBuf,    // Path to signing key
+    pub debug: bool,             // Debug mode (default false = release)
+    pub path: PathBuf,           // Path to TA directory
+    pub uuid_path: PathBuf,      // Path to UUID file
+    // Customized variables
+    pub env: Vec<(String, String)>, // Custom environment variables for cargo 
build
+    pub no_default_features: bool,  // Disable default features
+    pub features: Option<String>,   // Additional features to enable
+}
+
+// Helper function to derive target and cross-compile from arch and std
+fn get_target_and_cross_compile(arch: Arch, std: bool) -> Result<(String, 
String)> {
+    for &(config_arch, config_std, target, cross_compile_prefix) in 
&TARGET_CONFIGS {
+        if config_arch == arch && config_std == std {
+            return Ok((target.to_string(), cross_compile_prefix.to_string()));
+        }
+    }
+
+    bail!(
+        "No target configuration found for arch: {:?}, std: {}",
+        arch,
+        std
+    );
+}
+
+// Helper function to setup custom target JSONs for std builds
+// Returns TempDir to keep it alive during the build
+fn setup_custom_targets() -> Result<TempDir> {
+    let temp_dir = TempDir::new()?;
+
+    // Write the embedded target JSON files
+    let aarch64_path = temp_dir.path().join("aarch64-unknown-optee.json");
+    let arm_path = temp_dir.path().join("arm-unknown-optee.json");
+
+    fs::write(aarch64_path, AARCH64_TARGET_JSON)?;
+    fs::write(arm_path, ARM_TARGET_JSON)?;
+
+    Ok(temp_dir)
+}
+
+// Helper function to setup base command with common environment variables
+fn setup_build_command(
+    config: &TaBuildConfig,
+    command: &str,
+) -> Result<(Command, Option<TempDir>)> {
+    // Determine target and cross-compile based on arch
+    let (target, _cross_compile) = get_target_and_cross_compile(config.arch, 
config.std)?;
+
+    // Determine builder (cargo or xargo)
+    let builder = if config.std { "xargo" } else { "cargo" };
+
+    // Setup custom targets if using std - keep TempDir alive
+    let temp_dir = if config.std {
+        Some(setup_custom_targets()?)
+    } else {
+        None
+    };
+
+    let mut cmd = Command::new(builder);
+    cmd.arg(command);
+    cmd.arg("--target").arg(&target);
+
+    // Add --no-default-features if specified
+    if config.no_default_features {
+        cmd.arg("--no-default-features");
+    }
+
+    // Build features list
+    let mut features = Vec::new();
+    if config.std {
+        features.push("std".to_string());
+    }
+    if let Some(ref custom_features) = config.features {
+        // Split custom features by comma and add them
+        for feature in custom_features.split(',') {
+            let feature = feature.trim();
+            if !feature.is_empty() {
+                features.push(feature.to_string());
+            }
+        }
+    }
+
+    // Add features if any are specified
+    if !features.is_empty() {
+        cmd.arg("--features").arg(features.join(","));
+    }
+
+    // Add no-std specific flags to avoid the linking error of _Unwind_Resume
+    if !config.std {
+        cmd.arg("-Z").arg("build-std=core,alloc");
+        cmd.arg("-Z")
+            .arg("build-std-features=panic_immediate_abort");
+    }
+
+    // Set RUSTFLAGS - preserve existing ones and add panic=abort
+    let mut rustflags = env::var("RUSTFLAGS").unwrap_or_default();
+    if !rustflags.is_empty() {
+        rustflags.push(' ');
+    }
+    rustflags.push_str("-C panic=abort");
+    cmd.env("RUSTFLAGS", &rustflags);
+
+    // Apply custom environment variables
+    for (key, value) in &config.env {
+        cmd.env(key, value);
+    }
+
+    // Set TA_DEV_KIT_DIR environment variable (use absolute path)
+    let absolute_ta_dev_kit_dir = config
+        .ta_dev_kit_dir
+        .canonicalize()
+        .unwrap_or_else(|_| config.ta_dev_kit_dir.clone());
+    cmd.env("TA_DEV_KIT_DIR", &absolute_ta_dev_kit_dir);
+
+    // Set RUST_TARGET_PATH for custom targets when using std
+    if let Some(ref temp_dir_ref) = temp_dir {
+        cmd.env("RUST_TARGET_PATH", temp_dir_ref.path());
+    }
+
+    Ok((cmd, temp_dir))
+}
+
+// Main function to build the TA, optionally installing to a target directory
+pub fn build_ta(config: TaBuildConfig, install_dir: Option<&Path>) -> 
Result<()> {
+    // Check if required cross-compile toolchain is available
+    let (_, cross_compile_prefix) = get_target_and_cross_compile(config.arch, 
config.std)?;
+    check_toolchain_exists(&cross_compile_prefix)?;
+
+    // Verify we're in a valid Rust project directory
+    let manifest_path = config.path.join("Cargo.toml");
+    if !manifest_path.exists() {
+        bail!(
+            "No Cargo.toml found in TA project directory: {:?}\n\
+            Please run cargo-optee from a TA project directory or specify 
--manifest-path",
+            config.path
+        );
+    }
+    // Get the absolute path for better clarity
+    let absolute_path = std::fs::canonicalize(&config.path).unwrap_or_else(|_| 
config.path.clone());
+    println!("Building TA in directory: {}", absolute_path.display());
+
+    // Step 1: Run clippy for code quality checks
+    run_clippy(&config, &manifest_path)?;
+
+    // Step 2: Build the TA
+    build_binary(&config, &manifest_path)?;
+
+    // Step 3: Strip the binary
+    let (stripped_path, target_dir) = strip_binary(&config, &manifest_path)?;
+
+    // Step 4: Sign the TA
+    sign_ta(&config, &stripped_path, &target_dir)?;
+
+    // Step 5: Install if requested
+    if let Some(install_dir) = install_dir {
+        // Check if install directory exists
+        if !install_dir.exists() {
+            bail!("Install directory does not exist: {:?}", install_dir);
+        }
+
+        let uuid = read_uuid_from_file(&config.uuid_path)?;
+        let ta_file = target_dir.join(format!("{}.ta", uuid));
+
+        if !ta_file.exists() {
+            bail!("Signed TA file not found at {:?}", ta_file);
+        }
+
+        let dest_path = install_dir.join(format!("{}.ta", uuid));
+        fs::copy(&ta_file, &dest_path)?;
+
+        println!(
+            "TA installed to: {:?}",
+            dest_path.canonicalize().unwrap_or(dest_path)
+        );
+    }
+
+    println!("TA build successfully!");
+
+    Ok(())
+}
+
+fn run_clippy(config: &TaBuildConfig, manifest_path: &Path) -> Result<()> {
+    println!("Running cargo fmt and clippy...");
+
+    // Get the project directory from manifest path
+    let project_dir = manifest_path
+        .parent()
+        .ok_or_else(|| anyhow::anyhow!("Invalid manifest path: {:?}", 
manifest_path))?;
+
+    // Change to project directory to respect rust-toolchain.toml
+    let original_dir = std::env::current_dir()?;
+    std::env::set_current_dir(project_dir)?;
+
+    // Run cargo fmt (without --manifest-path since we're in the project dir)
+    let mut fmt_cmd = Command::new("cargo");
+    fmt_cmd.arg("fmt");
+    let fmt_output = fmt_cmd.output();
+
+    // Restore original directory before checking results
+    std::env::set_current_dir(&original_dir)?;
+
+    let fmt_output = fmt_output?;
+    if !fmt_output.status.success() {
+        print_output_and_bail("cargo fmt", &fmt_output)?;
+    }
+
+    // Change back to project directory for clippy
+    std::env::set_current_dir(project_dir)?;
+
+    // Setup clippy command with common environment (without --manifest-path)
+    let (mut clippy_cmd, _temp_dir) = setup_build_command(config, "clippy")?;
+
+    clippy_cmd.arg("--");
+    clippy_cmd.arg("-D").arg("warnings");
+    clippy_cmd.arg("-D").arg("clippy::unwrap_used");
+    clippy_cmd.arg("-D").arg("clippy::expect_used");
+    clippy_cmd.arg("-D").arg("clippy::panic");
+
+    let clippy_output = clippy_cmd.output();
+
+    // Restore original directory before checking results
+    std::env::set_current_dir(&original_dir)?;
+
+    let clippy_output = clippy_output?;
+    if !clippy_output.status.success() {
+        print_output_and_bail("clippy", &clippy_output)?;
+    }
+
+    Ok(())
+}
+
+fn build_binary(config: &TaBuildConfig, manifest_path: &Path) -> Result<()> {
+    // Get the project directory from manifest path
+    let project_dir = manifest_path
+        .parent()
+        .ok_or_else(|| anyhow::anyhow!("Invalid manifest path: {:?}", 
manifest_path))?;
+
+    // Change to project directory to respect rust-toolchain.toml
+    let original_dir = std::env::current_dir()?;
+    std::env::set_current_dir(project_dir)?;
+
+    // Determine target and cross-compile based on arch
+    let (target, cross_compile) = get_target_and_cross_compile(config.arch, 
config.std)?;
+
+    // Setup build command with common environment (without --manifest-path)
+    let (mut build_cmd, _temp_dir) = setup_build_command(config, "build")?;
+
+    if !config.debug {
+        build_cmd.arg("--release");
+    }
+
+    // Configure linker
+    let linker = format!("{}gcc", cross_compile);
+    let linker_cfg = format!("target.{}.linker=\"{}\"", target, linker);
+    build_cmd.arg("--config").arg(&linker_cfg);
+
+    // Print the full cargo build command for debugging
+    print_cargo_command(&build_cmd, "Building TA binary");
+
+    let build_output = build_cmd.output();
+
+    // Restore original directory before checking results
+    std::env::set_current_dir(original_dir)?;
+
+    let build_output = build_output?;
+    if !build_output.status.success() {
+        print_output_and_bail("build", &build_output)?;
+    }
+
+    Ok(())
+}
+
+fn get_package_name(manifest_path: &Path) -> Result<String> {
+    if !manifest_path.exists() {
+        bail!("Cargo.toml not found at: {:?}", manifest_path);
+    }
+
+    let cargo_toml_content = fs::read_to_string(manifest_path)?;
+    let cargo_toml: Value = toml::from_str(&cargo_toml_content)?;
+
+    let package_name = cargo_toml
+        .get("package")
+        .and_then(|p| p.get("name"))
+        .and_then(|n| n.as_str())
+        .ok_or_else(|| anyhow::anyhow!("Could not find package name in 
Cargo.toml"))?;
+
+    Ok(package_name.to_string())
+}
+
+fn strip_binary(config: &TaBuildConfig, manifest_path: &Path) -> 
Result<(PathBuf, PathBuf)> {
+    println!("Stripping binary...");
+
+    // Determine target based on arch
+    let (target, cross_compile) = get_target_and_cross_compile(config.arch, 
config.std)?;
+
+    let profile = if config.debug { "debug" } else { "release" };
+
+    // Get the actual package name from Cargo.toml
+    let package_name = get_package_name(manifest_path)?;
+
+    // Use cargo metadata to get the target directory that cargo is actually 
using
+    let output = Command::new("cargo")
+        .arg("metadata")
+        .arg("--manifest-path")
+        .arg(manifest_path)
+        .arg("--format-version")
+        .arg("1")
+        .arg("--no-deps")
+        .output()?;
+
+    if !output.status.success() {
+        bail!("Failed to get cargo metadata");
+    }
+
+    let metadata: serde_json::Value = serde_json::from_slice(&output.stdout)?;
+    let target_directory = metadata
+        .get("target_directory")
+        .and_then(|v| v.as_str())
+        .ok_or_else(|| anyhow::anyhow!("Could not get target directory from 
cargo metadata"))?;
+
+    let target_dir = PathBuf::from(target_directory);
+    let profile_dir = target_dir.join(target).join(profile);
+    let binary_path = profile_dir.join(&package_name);
+
+    if !binary_path.exists() {
+        bail!("Binary not found at {:?}", binary_path);
+    }

Review Comment:
   All the same pattern can move into a fn like `join_and_check()`, using that 
fn avoids we forget  check the path



##########
cargo-optee/src/main.rs:
##########
@@ -0,0 +1,567 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use anyhow::bail;
+use clap::{Args, Parser, Subcommand};
+use std::env;
+use std::path::PathBuf;
+use std::process;
+
+mod ca_builder;
+mod common;
+mod config;
+mod ta_builder;
+
+use common::Arch;
+
+/// Parse environment variable in KEY=VALUE format
+fn parse_env_var(s: &str) -> Result<(String, String), String> {
+    if let Some(eq_pos) = s.find('=') {
+        let (key, value) = s.split_at(eq_pos);
+        let value = &value[1..]; // Skip the '=' character
+        Ok((key.to_string(), value.to_string()))
+    } else {
+        Err(format!(
+            "Invalid environment variable format: '{}'. Expected 'KEY=VALUE'",
+            s
+        ))
+    }
+}
+
+/// Resolve a potentially relative path to an absolute path based on the 
project directory
+fn resolve_path_relative_to_project(path: &PathBuf, project_path: 
&std::path::Path) -> PathBuf {
+    if path.is_absolute() {
+        path.clone()
+    } else {
+        project_path.join(path)
+    }
+}
+
+/// Execute TA build or install (shared logic)
+fn execute_ta_command(
+    common: CommonBuildArgs,
+    std: Option<bool>,
+    ta_dev_kit_dir: Option<PathBuf>,
+    signing_key: Option<PathBuf>,
+    uuid_path: Option<PathBuf>,
+    install_target_dir: Option<&PathBuf>,
+) -> anyhow::Result<()> {
+    // Resolve project path from manifest or current directory
+    let project_path = if let Some(manifest) = common.manifest_path {
+        let parent = manifest
+            .parent()
+            .ok_or_else(|| anyhow::anyhow!("Invalid manifest path"))?;
+
+        // Normalize: if parent is empty (e.g., manifest is just "Cargo.toml"),
+        // use current directory instead
+        if parent.as_os_str().is_empty() {
+            std::env::current_dir()?
+        } else {
+            // Canonicalize must succeed, otherwise treat as invalid manifest 
path
+            parent
+                .canonicalize()
+                .map_err(|_| anyhow::anyhow!("Invalid manifest path"))?
+        }
+    } else {
+        std::env::current_dir()?
+    };
+
+    // Resolve build configuration with priority: CLI > metadata > error
+    let build_config = config::BuildConfig::resolve(
+        &project_path,
+        "ta", // Component type for TA
+        common.arch,
+        Some(common.debug),
+        std, // None means read from config, Some(true/false) means CLI 
override
+        ta_dev_kit_dir,
+        None, // optee_client_export not needed for TA
+        signing_key,
+        uuid_path.clone(),
+    )?;
+
+    // Print the final configuration being used
+    build_config.print_config("ta", &project_path);
+
+    // Get required ta_dev_kit_dir and resolve relative to project
+    let ta_dev_kit_dir_config = build_config.require_ta_dev_kit_dir()?;
+    let ta_dev_kit_dir = 
resolve_path_relative_to_project(&ta_dev_kit_dir_config, &project_path);
+
+    // Validate that ta_dev_kit_dir exists (print absolute path)
+    if !ta_dev_kit_dir.exists() {
+        bail!(
+            "TA development kit directory does not exist: {:?}",
+            ta_dev_kit_dir
+        );
+    }
+
+    // Resolve signing key relative to project directory
+    let signing_key_config = 
build_config.resolve_signing_key(&ta_dev_kit_dir_config);
+    let signing_key_path = 
resolve_path_relative_to_project(&signing_key_config, &project_path);
+
+    // Validate that signing key exists (print absolute path)
+    if !signing_key_path.exists() {
+        bail!("Signing key file does not exist: {:?}", signing_key_path);
+    }
+
+    // Resolve UUID path: if provided via CLI, it's relative to current dir
+    // if from metadata, it's relative to project dir
+    let resolved_uuid_path = if uuid_path.is_some() {
+        // CLI provided - resolve relative to current directory
+        std::env::current_dir()?.join(build_config.get_uuid_path())
+    } else {
+        // From metadata or default - resolve relative to project directory
+        project_path.join(build_config.get_uuid_path())
+    };
+
+    // Merge env variables: CLI overrides + metadata env
+    let mut merged_env = build_config.env.clone();
+    merged_env.extend(common.env);
+
+    let ta_config = ta_builder::TaBuildConfig {
+        arch: build_config.arch,
+        std: build_config.std,
+        ta_dev_kit_dir,
+        signing_key: signing_key_path,
+        debug: build_config.debug,
+        path: project_path,
+        uuid_path: resolved_uuid_path,
+        env: merged_env,
+        no_default_features: common.no_default_features,
+        features: common.features,
+    };
+
+    ta_builder::build_ta(ta_config, install_target_dir.map(|p| p.as_path()))
+}
+
+/// Execute CA build or install (shared logic)
+fn execute_ca_command(
+    common: CommonBuildArgs,
+    optee_client_export: Option<PathBuf>,
+    uuid_path: Option<PathBuf>,
+    plugin: bool,
+    install_target_dir: Option<&PathBuf>,
+) -> anyhow::Result<()> {
+    // Resolve project path from manifest or current directory
+    let project_path = if let Some(manifest) = common.manifest_path {
+        let parent = manifest
+            .parent()
+            .ok_or_else(|| anyhow::anyhow!("Invalid manifest path"))?;
+
+        // Normalize: if parent is empty (e.g., manifest is just "Cargo.toml"),
+        // use current directory instead
+        if parent.as_os_str().is_empty() {
+            std::env::current_dir()?
+        } else {
+            // Canonicalize must succeed, otherwise treat as invalid manifest 
path
+            parent
+                .canonicalize()
+                .map_err(|_| anyhow::anyhow!("Invalid manifest path"))?
+        }
+    } else {
+        std::env::current_dir()?
+    };
+
+    let component_type = if plugin { "plugin" } else { "ca" };
+
+    // Resolve build configuration
+    let build_config = config::BuildConfig::resolve(
+        &project_path,
+        component_type,
+        common.arch,
+        Some(common.debug),
+        None, // std not applicable for CA/Plugin
+        None, // ta_dev_kit_dir not needed for CA/Plugin
+        optee_client_export,
+        None, // signing_key not needed for CA/Plugin
+        uuid_path,
+    )?;
+
+    // Print the final configuration being used
+    build_config.print_config(component_type, &project_path);
+
+    // Get required optee_client_export and resolve relative to project
+    let optee_client_export_config = 
build_config.require_optee_client_export()?;
+    let optee_client_export =
+        resolve_path_relative_to_project(&optee_client_export_config, 
&project_path);
+
+    // Validate that optee_client_export exists (print absolute path)
+    if !optee_client_export.exists() {
+        bail!(
+            "OP-TEE client export directory does not exist: {:?}",
+            optee_client_export
+        );
+    }
+
+    // Merge env variables: CLI overrides + metadata env
+    let mut merged_env = build_config.env.clone();
+    merged_env.extend(common.env);
+
+    let ca_config = ca_builder::CaBuildConfig {
+        arch: build_config.arch,
+        optee_client_export,
+        debug: build_config.debug,
+        path: project_path,
+        plugin,
+        uuid_path: if plugin {
+            build_config.uuid_path.clone()
+        } else {
+            None
+        },
+        env: merged_env,
+        no_default_features: common.no_default_features,
+        features: common.features,
+    };
+
+    ca_builder::build_ca(ca_config, install_target_dir.map(|p| p.as_path()))
+}
+
+/// Common build command arguments shared across TA, CA, and Plugin builds
+#[derive(Debug, Args)]
+struct CommonBuildArgs {
+    /// Path to the Cargo.toml manifest file
+    #[arg(long = "manifest-path")]
+    manifest_path: Option<PathBuf>,
+
+    /// Target architecture (default: aarch64)
+    #[arg(long = "arch")]
+    arch: Option<Arch>,
+
+    /// Enable debug build (default: false)
+    #[arg(long = "debug")]
+    debug: bool,
+
+    /// Environment overrides in the form of `"KEY=VALUE"` strings. This flag 
can be repeated.
+    ///
+    /// This is generally not needed to be used explicitly during regular 
development.
+    ///
+    /// This makes sense to be used to specify custom var e.g. `RUSTFLAGS`.
+    #[arg(long = "env", value_parser = parse_env_var, action = 
clap::ArgAction::Append)]
+    env: Vec<(String, String)>,
+
+    /// Disable default features (will append --no-default-features to cargo 
build)
+    #[arg(long = "no-default-features")]
+    no_default_features: bool,
+
+    /// Custom features to enable (will append --features to cargo build)
+    #[arg(long = "features")]
+    features: Option<String>,
+}
+
+#[derive(Debug, Parser)]
+#[clap(version = env!("CARGO_PKG_VERSION"))]
+#[clap(about = "Build tool for OP-TEE Rust projects")]
+pub(crate) struct Cli {
+    #[clap(subcommand)]
+    cmd: Command,
+}

Review Comment:
   Recommend to move all cli structs definition to another file `cli.rs` to 
make the `main.rs` simple and clean



##########
cargo-optee/src/main.rs:
##########
@@ -0,0 +1,567 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use anyhow::bail;
+use clap::{Args, Parser, Subcommand};
+use std::env;
+use std::path::PathBuf;
+use std::process;
+
+mod ca_builder;
+mod common;
+mod config;
+mod ta_builder;
+
+use common::Arch;
+
+/// Parse environment variable in KEY=VALUE format
+fn parse_env_var(s: &str) -> Result<(String, String), String> {
+    if let Some(eq_pos) = s.find('=') {
+        let (key, value) = s.split_at(eq_pos);
+        let value = &value[1..]; // Skip the '=' character
+        Ok((key.to_string(), value.to_string()))
+    } else {
+        Err(format!(
+            "Invalid environment variable format: '{}'. Expected 'KEY=VALUE'",
+            s
+        ))
+    }
+}
+
+/// Resolve a potentially relative path to an absolute path based on the 
project directory
+fn resolve_path_relative_to_project(path: &PathBuf, project_path: 
&std::path::Path) -> PathBuf {
+    if path.is_absolute() {
+        path.clone()
+    } else {
+        project_path.join(path)
+    }
+}
+
+/// Execute TA build or install (shared logic)
+fn execute_ta_command(
+    common: CommonBuildArgs,
+    std: Option<bool>,
+    ta_dev_kit_dir: Option<PathBuf>,
+    signing_key: Option<PathBuf>,
+    uuid_path: Option<PathBuf>,
+    install_target_dir: Option<&PathBuf>,
+) -> anyhow::Result<()> {
+    // Resolve project path from manifest or current directory
+    let project_path = if let Some(manifest) = common.manifest_path {
+        let parent = manifest
+            .parent()
+            .ok_or_else(|| anyhow::anyhow!("Invalid manifest path"))?;
+
+        // Normalize: if parent is empty (e.g., manifest is just "Cargo.toml"),
+        // use current directory instead
+        if parent.as_os_str().is_empty() {
+            std::env::current_dir()?
+        } else {
+            // Canonicalize must succeed, otherwise treat as invalid manifest 
path
+            parent
+                .canonicalize()
+                .map_err(|_| anyhow::anyhow!("Invalid manifest path"))?
+        }
+    } else {
+        std::env::current_dir()?
+    };
+
+    // Resolve build configuration with priority: CLI > metadata > error
+    let build_config = config::BuildConfig::resolve(
+        &project_path,
+        "ta", // Component type for TA
+        common.arch,
+        Some(common.debug),
+        std, // None means read from config, Some(true/false) means CLI 
override
+        ta_dev_kit_dir,
+        None, // optee_client_export not needed for TA
+        signing_key,
+        uuid_path.clone(),
+    )?;
+
+    // Print the final configuration being used
+    build_config.print_config("ta", &project_path);
+
+    // Get required ta_dev_kit_dir and resolve relative to project
+    let ta_dev_kit_dir_config = build_config.require_ta_dev_kit_dir()?;
+    let ta_dev_kit_dir = 
resolve_path_relative_to_project(&ta_dev_kit_dir_config, &project_path);
+
+    // Validate that ta_dev_kit_dir exists (print absolute path)
+    if !ta_dev_kit_dir.exists() {
+        bail!(
+            "TA development kit directory does not exist: {:?}",
+            ta_dev_kit_dir
+        );
+    }
+
+    // Resolve signing key relative to project directory
+    let signing_key_config = 
build_config.resolve_signing_key(&ta_dev_kit_dir_config);
+    let signing_key_path = 
resolve_path_relative_to_project(&signing_key_config, &project_path);
+
+    // Validate that signing key exists (print absolute path)
+    if !signing_key_path.exists() {
+        bail!("Signing key file does not exist: {:?}", signing_key_path);
+    }
+
+    // Resolve UUID path: if provided via CLI, it's relative to current dir
+    // if from metadata, it's relative to project dir
+    let resolved_uuid_path = if uuid_path.is_some() {
+        // CLI provided - resolve relative to current directory
+        std::env::current_dir()?.join(build_config.get_uuid_path())
+    } else {
+        // From metadata or default - resolve relative to project directory
+        project_path.join(build_config.get_uuid_path())
+    };
+
+    // Merge env variables: CLI overrides + metadata env
+    let mut merged_env = build_config.env.clone();
+    merged_env.extend(common.env);
+
+    let ta_config = ta_builder::TaBuildConfig {
+        arch: build_config.arch,
+        std: build_config.std,
+        ta_dev_kit_dir,
+        signing_key: signing_key_path,
+        debug: build_config.debug,
+        path: project_path,
+        uuid_path: resolved_uuid_path,
+        env: merged_env,
+        no_default_features: common.no_default_features,
+        features: common.features,
+    };
+
+    ta_builder::build_ta(ta_config, install_target_dir.map(|p| p.as_path()))
+}
+
+/// Execute CA build or install (shared logic)
+fn execute_ca_command(
+    common: CommonBuildArgs,
+    optee_client_export: Option<PathBuf>,
+    uuid_path: Option<PathBuf>,
+    plugin: bool,
+    install_target_dir: Option<&PathBuf>,
+) -> anyhow::Result<()> {
+    // Resolve project path from manifest or current directory
+    let project_path = if let Some(manifest) = common.manifest_path {
+        let parent = manifest
+            .parent()
+            .ok_or_else(|| anyhow::anyhow!("Invalid manifest path"))?;
+
+        // Normalize: if parent is empty (e.g., manifest is just "Cargo.toml"),
+        // use current directory instead
+        if parent.as_os_str().is_empty() {
+            std::env::current_dir()?
+        } else {
+            // Canonicalize must succeed, otherwise treat as invalid manifest 
path
+            parent
+                .canonicalize()
+                .map_err(|_| anyhow::anyhow!("Invalid manifest path"))?
+        }
+    } else {
+        std::env::current_dir()?
+    };
+
+    let component_type = if plugin { "plugin" } else { "ca" };
+
+    // Resolve build configuration
+    let build_config = config::BuildConfig::resolve(
+        &project_path,
+        component_type,
+        common.arch,
+        Some(common.debug),
+        None, // std not applicable for CA/Plugin
+        None, // ta_dev_kit_dir not needed for CA/Plugin
+        optee_client_export,
+        None, // signing_key not needed for CA/Plugin
+        uuid_path,
+    )?;
+
+    // Print the final configuration being used
+    build_config.print_config(component_type, &project_path);
+
+    // Get required optee_client_export and resolve relative to project
+    let optee_client_export_config = 
build_config.require_optee_client_export()?;
+    let optee_client_export =
+        resolve_path_relative_to_project(&optee_client_export_config, 
&project_path);
+
+    // Validate that optee_client_export exists (print absolute path)
+    if !optee_client_export.exists() {
+        bail!(
+            "OP-TEE client export directory does not exist: {:?}",
+            optee_client_export
+        );
+    }
+
+    // Merge env variables: CLI overrides + metadata env
+    let mut merged_env = build_config.env.clone();
+    merged_env.extend(common.env);
+
+    let ca_config = ca_builder::CaBuildConfig {
+        arch: build_config.arch,
+        optee_client_export,
+        debug: build_config.debug,
+        path: project_path,
+        plugin,
+        uuid_path: if plugin {
+            build_config.uuid_path.clone()
+        } else {
+            None
+        },
+        env: merged_env,
+        no_default_features: common.no_default_features,
+        features: common.features,
+    };
+
+    ca_builder::build_ca(ca_config, install_target_dir.map(|p| p.as_path()))
+}
+
+/// Common build command arguments shared across TA, CA, and Plugin builds
+#[derive(Debug, Args)]
+struct CommonBuildArgs {
+    /// Path to the Cargo.toml manifest file
+    #[arg(long = "manifest-path")]
+    manifest_path: Option<PathBuf>,
+
+    /// Target architecture (default: aarch64)
+    #[arg(long = "arch")]
+    arch: Option<Arch>,
+
+    /// Enable debug build (default: false)
+    #[arg(long = "debug")]
+    debug: bool,
+
+    /// Environment overrides in the form of `"KEY=VALUE"` strings. This flag 
can be repeated.
+    ///
+    /// This is generally not needed to be used explicitly during regular 
development.
+    ///
+    /// This makes sense to be used to specify custom var e.g. `RUSTFLAGS`.
+    #[arg(long = "env", value_parser = parse_env_var, action = 
clap::ArgAction::Append)]
+    env: Vec<(String, String)>,
+
+    /// Disable default features (will append --no-default-features to cargo 
build)
+    #[arg(long = "no-default-features")]
+    no_default_features: bool,
+
+    /// Custom features to enable (will append --features to cargo build)
+    #[arg(long = "features")]
+    features: Option<String>,
+}
+
+#[derive(Debug, Parser)]
+#[clap(version = env!("CARGO_PKG_VERSION"))]
+#[clap(about = "Build tool for OP-TEE Rust projects")]
+pub(crate) struct Cli {
+    #[clap(subcommand)]
+    cmd: Command,
+}
+
+#[derive(Debug, Subcommand)]
+enum BuildCommand {
+    /// Build a Trusted Application (TA)
+    #[command(about = "Build a Trusted Application (TA)")]
+    TA {
+        #[command(flatten)]
+        build_cmd: TABuildArgs,
+    },
+    /// Build a Client Application (Host)
+    #[command(about = "Build a Client Application (Host)")]
+    CA {
+        #[command(flatten)]
+        build_cmd: CABuildArgs,
+    },
+    /// Build a Plugin (Shared Library)
+    #[command(about = "Build a Plugin (Shared Library)")]
+    Plugin {
+        #[command(flatten)]
+        build_cmd: PluginBuildArgs,
+    },
+}
+
+#[derive(Debug, Subcommand)]
+enum Command {
+    /// Build OP-TEE components
+    #[clap(name = "build")]
+    #[command(subcommand)]
+    Build(BuildCommand),
+    /// Install OP-TEE components
+    #[clap(name = "install")]
+    #[command(subcommand)]
+    Install(InstallCommand),
+    /// Clean OP-TEE components
+    #[clap(name = "clean")]
+    Clean {
+        #[command(flatten)]
+        clean_cmd: CleanCommand,
+    },
+}
+
+#[derive(Debug, Subcommand)]
+enum InstallCommand {
+    /// Install a Trusted Application (TA)
+    #[command(about = "Install a Trusted Application (TA) to target 
directory")]
+    TA {
+        /// Target directory to install the TA binary (default: "shared")
+        #[arg(long = "target-dir", default_value = "shared")]
+        target_dir: PathBuf,
+
+        #[command(flatten)]
+        build_cmd: TABuildArgs,
+    },
+    /// Install a Client Application (Host)
+    #[command(about = "Install a Client Application (Host) to target 
directory")]
+    CA {
+        /// Target directory to install the CA binary (default: "shared")
+        #[arg(long = "target-dir", default_value = "shared")]
+        target_dir: PathBuf,
+
+        #[command(flatten)]
+        build_cmd: CABuildArgs,
+    },
+    /// Install a Plugin (Shared Library)
+    #[command(about = "Install a Plugin (Shared Library) to target directory")]
+    Plugin {
+        /// Target directory to install the plugin binary (default: "shared")
+        #[arg(long = "target-dir", default_value = "shared")]
+        target_dir: PathBuf,
+
+        #[command(flatten)]
+        build_cmd: PluginBuildArgs,
+    },
+}
+
+/// TA-specific build arguments
+#[derive(Debug, Args)]
+struct TABuildArgs {
+    #[command(flatten)]
+    common: CommonBuildArgs,
+
+    /// Enable std feature for the TA
+    /// If neither --std nor --no-std is specified, the value will be read 
from Cargo.toml metadata
+    #[arg(long = "std", action = clap::ArgAction::SetTrue, conflicts_with = 
"no_std")]
+    std: bool,
+
+    /// Disable std feature for the TA (use no-std mode)
+    /// If neither --std nor --no-std is specified, the value will be read 
from Cargo.toml metadata
+    #[arg(long = "no-std", action = clap::ArgAction::SetTrue, conflicts_with = 
"std")]
+    no_std: bool,
+
+    /// OP-TEE TA development kit export directory
+    #[arg(long = "ta-dev-kit-dir")]
+    ta_dev_kit_dir: Option<PathBuf>,
+
+    /// TA signing key path (default: TA_DEV_KIT_DIR/keys/default_ta.pem)
+    #[arg(long = "signing-key")]
+    signing_key: Option<PathBuf>,
+
+    /// UUID file path (default: "../uuid.txt")
+    #[arg(long = "uuid-path")]
+    uuid_path: Option<PathBuf>,
+}
+
+/// CA-specific build arguments
+#[derive(Debug, Args)]
+struct CABuildArgs {
+    #[command(flatten)]
+    common: CommonBuildArgs,
+
+    /// OP-TEE client export directory
+    #[arg(long = "optee-client-export")]
+    optee_client_export: Option<PathBuf>,
+}
+
+/// Plugin-specific build arguments
+#[derive(Debug, Args)]
+struct PluginBuildArgs {
+    #[command(flatten)]
+    common: CommonBuildArgs,
+
+    /// OP-TEE client export directory
+    #[arg(long = "optee-client-export")]
+    optee_client_export: Option<PathBuf>,
+
+    /// UUID file path (default: "../uuid.txt")
+    #[arg(long = "uuid-path")]
+    uuid_path: Option<PathBuf>,
+}
+
+/// Clean command arguments
+#[derive(Debug, Args)]
+struct CleanCommand {
+    /// Path to the Cargo.toml manifest file
+    #[arg(long = "manifest-path")]
+    manifest_path: Option<PathBuf>,
+}
+
+/// Source cargo environment from a given path
+fn source_cargo_env(env_path: &str) -> bool {
+    if std::path::Path::new(env_path).exists() {
+        std::process::Command::new("bash")
+            .arg("-c")
+            .arg(format!("source {}", env_path))
+            .status()
+            .map(|status| status.success())
+            .unwrap_or(false)
+    } else {
+        false
+    }
+}
+
+/// Setup cargo environment by checking availability and sourcing environment 
if needed
+fn setup_cargo_environment() -> anyhow::Result<()> {
+    // Check if cargo is available
+    let cargo_available = std::process::Command::new("which")
+        .arg("cargo")
+        .output()
+        .map(|output| output.status.success())
+        .unwrap_or(false);
+
+    if cargo_available {
+        return Ok(());
+    }
+
+    // Try to source .cargo/env from ~/.cargo/env or $CARGO_HOME/env
+    let mut sourced = false;
+    if let Ok(home) = env::var("HOME") {
+        sourced = source_cargo_env(&format!("{}/.cargo/env", home));
+    }
+    if !sourced {
+        if let Ok(cargo_home) = env::var("CARGO_HOME") {
+            sourced = source_cargo_env(&format!("{}/env", cargo_home));
+        }
+    }
+
+    if !sourced {
+        anyhow::bail!("cargo command not found. Please install Rust: curl 
--proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh");
+    }
+
+    Ok(())
+}
+
+fn main() {
+    
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info"))
+        .format_timestamp_millis()
+        .init();
+
+    // Setup cargo environment
+    if let Err(e) = setup_cargo_environment() {
+        eprintln!("Error: {}", e);
+        process::exit(1);
+    }
+
+    let cli = Cli::parse();
+    let result = execute_command(cli.cmd);
+
+    if let Err(e) = result {
+        eprintln!("Error: {}", e);
+        process::exit(1);
+    }
+}
+
+fn execute_command(cmd: Command) -> anyhow::Result<()> {
+    match cmd {
+        Command::Build(build_cmd) => match build_cmd {
+            BuildCommand::TA { build_cmd } => {
+                // Convert bool flags to Option<bool>: --std -> Some(true), 
--no-std -> Some(false), neither -> None
+                let std = if build_cmd.std {
+                    Some(true)
+                } else if build_cmd.no_std {
+                    Some(false)
+                } else {
+                    None
+                };
+                execute_ta_command(
+                    build_cmd.common,
+                    std,
+                    build_cmd.ta_dev_kit_dir,
+                    build_cmd.signing_key,
+                    build_cmd.uuid_path,
+                    None,
+                )
+            }
+            BuildCommand::CA { build_cmd } => execute_ca_command(
+                build_cmd.common,
+                build_cmd.optee_client_export,
+                None,
+                false,
+                None,
+            ),
+            BuildCommand::Plugin { build_cmd } => execute_ca_command(
+                build_cmd.common,
+                build_cmd.optee_client_export,
+                build_cmd.uuid_path,
+                true,
+                None,
+            ),
+        },
+        Command::Install(install_cmd) => match install_cmd {
+            InstallCommand::TA {
+                target_dir,
+                build_cmd,
+            } => {
+                // Convert bool flags to Option<bool>: --std -> Some(true), 
--no-std -> Some(false), neither -> None
+                let std = if build_cmd.std {
+                    Some(true)
+                } else if build_cmd.no_std {
+                    Some(false)
+                } else {
+                    None
+                };
+                execute_ta_command(
+                    build_cmd.common,
+                    std,
+                    build_cmd.ta_dev_kit_dir,
+                    build_cmd.signing_key,
+                    build_cmd.uuid_path,
+                    Some(&target_dir),
+                )
+            }
+            InstallCommand::CA {
+                target_dir,
+                build_cmd,
+            } => execute_ca_command(
+                build_cmd.common,
+                build_cmd.optee_client_export,
+                None,
+                false,
+                Some(&target_dir),
+            ),
+            InstallCommand::Plugin {
+                target_dir,
+                build_cmd,
+            } => execute_ca_command(
+                build_cmd.common,
+                build_cmd.optee_client_export,
+                build_cmd.uuid_path,
+                true,
+                Some(&target_dir),
+            ),
+        },
+        Command::Clean { clean_cmd } => {
+            let project_path = if let Some(manifest) = clean_cmd.manifest_path 
{
+                manifest
+                    .parent()
+                    .ok_or_else(|| anyhow::anyhow!("Invalid manifest path"))?
+                    .to_path_buf()
+            } else {
+                std::env::current_dir()?
+            };
+

Review Comment:
   Also can reuse some fn like `resolve_project_path()`



##########
cargo-optee/src/main.rs:
##########
@@ -0,0 +1,567 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use anyhow::bail;
+use clap::{Args, Parser, Subcommand};
+use std::env;
+use std::path::PathBuf;
+use std::process;
+
+mod ca_builder;
+mod common;
+mod config;
+mod ta_builder;
+
+use common::Arch;
+
+/// Parse environment variable in KEY=VALUE format
+fn parse_env_var(s: &str) -> Result<(String, String), String> {
+    if let Some(eq_pos) = s.find('=') {
+        let (key, value) = s.split_at(eq_pos);
+        let value = &value[1..]; // Skip the '=' character
+        Ok((key.to_string(), value.to_string()))
+    } else {
+        Err(format!(
+            "Invalid environment variable format: '{}'. Expected 'KEY=VALUE'",
+            s
+        ))
+    }
+}
+
+/// Resolve a potentially relative path to an absolute path based on the 
project directory
+fn resolve_path_relative_to_project(path: &PathBuf, project_path: 
&std::path::Path) -> PathBuf {
+    if path.is_absolute() {
+        path.clone()
+    } else {
+        project_path.join(path)
+    }
+}
+
+/// Execute TA build or install (shared logic)
+fn execute_ta_command(
+    common: CommonBuildArgs,
+    std: Option<bool>,
+    ta_dev_kit_dir: Option<PathBuf>,
+    signing_key: Option<PathBuf>,
+    uuid_path: Option<PathBuf>,
+    install_target_dir: Option<&PathBuf>,
+) -> anyhow::Result<()> {
+    // Resolve project path from manifest or current directory
+    let project_path = if let Some(manifest) = common.manifest_path {
+        let parent = manifest
+            .parent()
+            .ok_or_else(|| anyhow::anyhow!("Invalid manifest path"))?;
+
+        // Normalize: if parent is empty (e.g., manifest is just "Cargo.toml"),
+        // use current directory instead
+        if parent.as_os_str().is_empty() {
+            std::env::current_dir()?
+        } else {
+            // Canonicalize must succeed, otherwise treat as invalid manifest 
path
+            parent
+                .canonicalize()
+                .map_err(|_| anyhow::anyhow!("Invalid manifest path"))?
+        }
+    } else {
+        std::env::current_dir()?
+    };
+
+    // Resolve build configuration with priority: CLI > metadata > error
+    let build_config = config::BuildConfig::resolve(
+        &project_path,
+        "ta", // Component type for TA
+        common.arch,
+        Some(common.debug),
+        std, // None means read from config, Some(true/false) means CLI 
override
+        ta_dev_kit_dir,
+        None, // optee_client_export not needed for TA
+        signing_key,
+        uuid_path.clone(),
+    )?;
+
+    // Print the final configuration being used
+    build_config.print_config("ta", &project_path);
+
+    // Get required ta_dev_kit_dir and resolve relative to project
+    let ta_dev_kit_dir_config = build_config.require_ta_dev_kit_dir()?;
+    let ta_dev_kit_dir = 
resolve_path_relative_to_project(&ta_dev_kit_dir_config, &project_path);
+
+    // Validate that ta_dev_kit_dir exists (print absolute path)
+    if !ta_dev_kit_dir.exists() {
+        bail!(
+            "TA development kit directory does not exist: {:?}",
+            ta_dev_kit_dir
+        );
+    }
+
+    // Resolve signing key relative to project directory
+    let signing_key_config = 
build_config.resolve_signing_key(&ta_dev_kit_dir_config);
+    let signing_key_path = 
resolve_path_relative_to_project(&signing_key_config, &project_path);
+
+    // Validate that signing key exists (print absolute path)
+    if !signing_key_path.exists() {
+        bail!("Signing key file does not exist: {:?}", signing_key_path);
+    }
+
+    // Resolve UUID path: if provided via CLI, it's relative to current dir
+    // if from metadata, it's relative to project dir
+    let resolved_uuid_path = if uuid_path.is_some() {
+        // CLI provided - resolve relative to current directory
+        std::env::current_dir()?.join(build_config.get_uuid_path())
+    } else {
+        // From metadata or default - resolve relative to project directory
+        project_path.join(build_config.get_uuid_path())
+    };
+
+    // Merge env variables: CLI overrides + metadata env
+    let mut merged_env = build_config.env.clone();
+    merged_env.extend(common.env);
+
+    let ta_config = ta_builder::TaBuildConfig {
+        arch: build_config.arch,
+        std: build_config.std,
+        ta_dev_kit_dir,
+        signing_key: signing_key_path,
+        debug: build_config.debug,
+        path: project_path,
+        uuid_path: resolved_uuid_path,
+        env: merged_env,
+        no_default_features: common.no_default_features,
+        features: common.features,
+    };
+
+    ta_builder::build_ta(ta_config, install_target_dir.map(|p| p.as_path()))
+}
+
+/// Execute CA build or install (shared logic)
+fn execute_ca_command(
+    common: CommonBuildArgs,
+    optee_client_export: Option<PathBuf>,
+    uuid_path: Option<PathBuf>,
+    plugin: bool,
+    install_target_dir: Option<&PathBuf>,
+) -> anyhow::Result<()> {
+    // Resolve project path from manifest or current directory
+    let project_path = if let Some(manifest) = common.manifest_path {
+        let parent = manifest
+            .parent()
+            .ok_or_else(|| anyhow::anyhow!("Invalid manifest path"))?;
+
+        // Normalize: if parent is empty (e.g., manifest is just "Cargo.toml"),
+        // use current directory instead
+        if parent.as_os_str().is_empty() {
+            std::env::current_dir()?
+        } else {
+            // Canonicalize must succeed, otherwise treat as invalid manifest 
path
+            parent
+                .canonicalize()
+                .map_err(|_| anyhow::anyhow!("Invalid manifest path"))?
+        }
+    } else {
+        std::env::current_dir()?
+    };
+
+    let component_type = if plugin { "plugin" } else { "ca" };
+
+    // Resolve build configuration
+    let build_config = config::BuildConfig::resolve(
+        &project_path,
+        component_type,
+        common.arch,
+        Some(common.debug),
+        None, // std not applicable for CA/Plugin
+        None, // ta_dev_kit_dir not needed for CA/Plugin
+        optee_client_export,
+        None, // signing_key not needed for CA/Plugin
+        uuid_path,
+    )?;
+
+    // Print the final configuration being used
+    build_config.print_config(component_type, &project_path);
+
+    // Get required optee_client_export and resolve relative to project
+    let optee_client_export_config = 
build_config.require_optee_client_export()?;
+    let optee_client_export =
+        resolve_path_relative_to_project(&optee_client_export_config, 
&project_path);
+
+    // Validate that optee_client_export exists (print absolute path)
+    if !optee_client_export.exists() {
+        bail!(
+            "OP-TEE client export directory does not exist: {:?}",
+            optee_client_export
+        );
+    }
+
+    // Merge env variables: CLI overrides + metadata env
+    let mut merged_env = build_config.env.clone();
+    merged_env.extend(common.env);
+
+    let ca_config = ca_builder::CaBuildConfig {
+        arch: build_config.arch,
+        optee_client_export,
+        debug: build_config.debug,
+        path: project_path,
+        plugin,
+        uuid_path: if plugin {
+            build_config.uuid_path.clone()
+        } else {
+            None
+        },
+        env: merged_env,
+        no_default_features: common.no_default_features,
+        features: common.features,
+    };
+
+    ca_builder::build_ca(ca_config, install_target_dir.map(|p| p.as_path()))
+}
+
+/// Common build command arguments shared across TA, CA, and Plugin builds
+#[derive(Debug, Args)]
+struct CommonBuildArgs {
+    /// Path to the Cargo.toml manifest file
+    #[arg(long = "manifest-path")]
+    manifest_path: Option<PathBuf>,
+
+    /// Target architecture (default: aarch64)
+    #[arg(long = "arch")]
+    arch: Option<Arch>,
+
+    /// Enable debug build (default: false)
+    #[arg(long = "debug")]
+    debug: bool,
+
+    /// Environment overrides in the form of `"KEY=VALUE"` strings. This flag 
can be repeated.
+    ///
+    /// This is generally not needed to be used explicitly during regular 
development.
+    ///
+    /// This makes sense to be used to specify custom var e.g. `RUSTFLAGS`.
+    #[arg(long = "env", value_parser = parse_env_var, action = 
clap::ArgAction::Append)]
+    env: Vec<(String, String)>,
+
+    /// Disable default features (will append --no-default-features to cargo 
build)
+    #[arg(long = "no-default-features")]
+    no_default_features: bool,
+
+    /// Custom features to enable (will append --features to cargo build)
+    #[arg(long = "features")]
+    features: Option<String>,
+}
+
+#[derive(Debug, Parser)]
+#[clap(version = env!("CARGO_PKG_VERSION"))]
+#[clap(about = "Build tool for OP-TEE Rust projects")]
+pub(crate) struct Cli {
+    #[clap(subcommand)]
+    cmd: Command,
+}
+
+#[derive(Debug, Subcommand)]
+enum BuildCommand {
+    /// Build a Trusted Application (TA)
+    #[command(about = "Build a Trusted Application (TA)")]
+    TA {
+        #[command(flatten)]
+        build_cmd: TABuildArgs,
+    },
+    /// Build a Client Application (Host)
+    #[command(about = "Build a Client Application (Host)")]
+    CA {
+        #[command(flatten)]
+        build_cmd: CABuildArgs,
+    },
+    /// Build a Plugin (Shared Library)
+    #[command(about = "Build a Plugin (Shared Library)")]
+    Plugin {
+        #[command(flatten)]
+        build_cmd: PluginBuildArgs,
+    },
+}
+
+#[derive(Debug, Subcommand)]
+enum Command {
+    /// Build OP-TEE components
+    #[clap(name = "build")]
+    #[command(subcommand)]
+    Build(BuildCommand),
+    /// Install OP-TEE components
+    #[clap(name = "install")]
+    #[command(subcommand)]
+    Install(InstallCommand),
+    /// Clean OP-TEE components
+    #[clap(name = "clean")]
+    Clean {
+        #[command(flatten)]
+        clean_cmd: CleanCommand,
+    },
+}
+
+#[derive(Debug, Subcommand)]
+enum InstallCommand {
+    /// Install a Trusted Application (TA)
+    #[command(about = "Install a Trusted Application (TA) to target 
directory")]
+    TA {
+        /// Target directory to install the TA binary (default: "shared")
+        #[arg(long = "target-dir", default_value = "shared")]
+        target_dir: PathBuf,
+
+        #[command(flatten)]
+        build_cmd: TABuildArgs,
+    },
+    /// Install a Client Application (Host)
+    #[command(about = "Install a Client Application (Host) to target 
directory")]
+    CA {
+        /// Target directory to install the CA binary (default: "shared")
+        #[arg(long = "target-dir", default_value = "shared")]
+        target_dir: PathBuf,
+
+        #[command(flatten)]
+        build_cmd: CABuildArgs,
+    },
+    /// Install a Plugin (Shared Library)
+    #[command(about = "Install a Plugin (Shared Library) to target directory")]
+    Plugin {
+        /// Target directory to install the plugin binary (default: "shared")
+        #[arg(long = "target-dir", default_value = "shared")]
+        target_dir: PathBuf,
+
+        #[command(flatten)]
+        build_cmd: PluginBuildArgs,
+    },
+}
+
+/// TA-specific build arguments
+#[derive(Debug, Args)]
+struct TABuildArgs {
+    #[command(flatten)]
+    common: CommonBuildArgs,
+
+    /// Enable std feature for the TA
+    /// If neither --std nor --no-std is specified, the value will be read 
from Cargo.toml metadata
+    #[arg(long = "std", action = clap::ArgAction::SetTrue, conflicts_with = 
"no_std")]
+    std: bool,
+
+    /// Disable std feature for the TA (use no-std mode)
+    /// If neither --std nor --no-std is specified, the value will be read 
from Cargo.toml metadata
+    #[arg(long = "no-std", action = clap::ArgAction::SetTrue, conflicts_with = 
"std")]
+    no_std: bool,
+
+    /// OP-TEE TA development kit export directory
+    #[arg(long = "ta-dev-kit-dir")]
+    ta_dev_kit_dir: Option<PathBuf>,
+
+    /// TA signing key path (default: TA_DEV_KIT_DIR/keys/default_ta.pem)
+    #[arg(long = "signing-key")]
+    signing_key: Option<PathBuf>,
+
+    /// UUID file path (default: "../uuid.txt")
+    #[arg(long = "uuid-path")]
+    uuid_path: Option<PathBuf>,
+}
+
+/// CA-specific build arguments
+#[derive(Debug, Args)]
+struct CABuildArgs {
+    #[command(flatten)]
+    common: CommonBuildArgs,
+
+    /// OP-TEE client export directory
+    #[arg(long = "optee-client-export")]
+    optee_client_export: Option<PathBuf>,
+}
+
+/// Plugin-specific build arguments
+#[derive(Debug, Args)]
+struct PluginBuildArgs {
+    #[command(flatten)]
+    common: CommonBuildArgs,
+
+    /// OP-TEE client export directory
+    #[arg(long = "optee-client-export")]
+    optee_client_export: Option<PathBuf>,
+
+    /// UUID file path (default: "../uuid.txt")
+    #[arg(long = "uuid-path")]
+    uuid_path: Option<PathBuf>,
+}
+
+/// Clean command arguments
+#[derive(Debug, Args)]
+struct CleanCommand {
+    /// Path to the Cargo.toml manifest file
+    #[arg(long = "manifest-path")]
+    manifest_path: Option<PathBuf>,
+}
+
+/// Source cargo environment from a given path
+fn source_cargo_env(env_path: &str) -> bool {
+    if std::path::Path::new(env_path).exists() {
+        std::process::Command::new("bash")
+            .arg("-c")
+            .arg(format!("source {}", env_path))
+            .status()
+            .map(|status| status.success())
+            .unwrap_or(false)
+    } else {
+        false
+    }
+}
+
+/// Setup cargo environment by checking availability and sourcing environment 
if needed
+fn setup_cargo_environment() -> anyhow::Result<()> {
+    // Check if cargo is available
+    let cargo_available = std::process::Command::new("which")
+        .arg("cargo")
+        .output()
+        .map(|output| output.status.success())
+        .unwrap_or(false);

Review Comment:
   Try this:
   ```
       let cargo_available = std::process::Command::new("which")
           .arg("cargo")
           .output()
           .is_ok_and(|output| output.status.success());
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to