| // Copyright 2024 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| //! `cmc` is the Component Manifest Compiler. |
| |
| use anyhow::{ensure, Error}; |
| use cml::{error, features, Document}; |
| use reference_doc::MarkdownReferenceDocGenerator; |
| use std::fs; |
| use std::path::{Path, PathBuf}; |
| |
| mod compile; |
| mod debug_print_cm; |
| mod format; |
| mod include; |
| mod merge; |
| pub mod opts; |
| mod reference; |
| mod util; |
| |
| pub fn run_cmc(opt: opts::Opt) -> Result<(), Error> { |
| match opt.cmd { |
| // TODO(https://fxbug.dev/42148493): Remove `cmc validate`. |
| opts::Commands::Validate { files, must_offer_protocol, must_use_protocol } => { |
| if files.is_empty() { |
| return Err(error::Error::invalid_args("No files provided").into()); |
| } |
| |
| for file in files { |
| let file = file.as_ref(); |
| cml::compile( |
| &util::read_cml(file)?, |
| cml::CompileOptions::new().file(&file).protocol_requirements( |
| cml::ProtocolRequirements { |
| must_offer: &must_offer_protocol, |
| must_use: &must_use_protocol, |
| }, |
| ), |
| )?; |
| } |
| } |
| opts::Commands::ValidateReferences { component_manifest, package_manifest, context } => { |
| reference::validate(&component_manifest, &package_manifest, context.as_ref())? |
| } |
| opts::Commands::Merge { files, output, fromfile, depfile } => { |
| merge::merge(files, output, fromfile, depfile)? |
| } |
| opts::Commands::Include { file, output, depfile, includepath, includeroot, validate } => { |
| path_exists(&file)?; |
| include::merge_includes( |
| &file, |
| output.as_ref(), |
| depfile.as_ref(), |
| &includepath, |
| &includeroot, |
| validate, |
| )? |
| } |
| opts::Commands::CheckIncludes { |
| file, |
| expected_includes, |
| fromfile, |
| depfile, |
| includepath, |
| includeroot, |
| } => { |
| path_exists(&file)?; |
| optional_path_exists(fromfile.as_ref())?; |
| include::check_includes( |
| &file, |
| expected_includes, |
| fromfile.as_ref(), |
| depfile.as_ref(), |
| opt.stamp.as_ref(), |
| &includepath, |
| &includeroot, |
| )? |
| } |
| opts::Commands::Format { file, pretty, cml, inplace, mut output } => { |
| // TODO(https://fxbug.dev/42060365): stop accepting these flags. |
| let _pretty = pretty; |
| let _cml = cml; |
| |
| path_exists(&file)?; |
| if inplace { |
| output = Some(file.clone()); |
| } |
| format::format(&file, output)?; |
| } |
| opts::Commands::Compile { |
| file, |
| output, |
| depfile, |
| includepath, |
| includeroot, |
| config_package_path, |
| features, |
| experimental_force_runner, |
| must_offer_protocol, |
| must_use_protocol, |
| } => { |
| path_exists(&file)?; |
| compile::compile( |
| &file, |
| &output, |
| depfile, |
| &includepath, |
| &includeroot, |
| config_package_path.as_ref().map(String::as_str), |
| &features.into(), |
| &experimental_force_runner, |
| cml::ProtocolRequirements { |
| must_offer: &must_offer_protocol, |
| must_use: &must_use_protocol, |
| }, |
| )? |
| } |
| opts::Commands::PrintReferenceDocs { output } => { |
| let docs = Document::get_reference_doc_markdown(); |
| match &output { |
| None => println!("{}", docs), |
| Some(path) => fs::write(path, docs)?, |
| } |
| } |
| opts::Commands::DebugPrintCm { file } => { |
| path_exists(&file)?; |
| debug_print_cm::debug_print_cm(&file)? |
| } |
| } |
| if let Some(stamp_path) = opt.stamp { |
| stamp(stamp_path)?; |
| } |
| Ok(()) |
| } |
| |
| fn path_exists(path: &Path) -> Result<(), Error> { |
| ensure!(path.exists(), "{:?} does not exist", path); |
| Ok(()) |
| } |
| |
| fn optional_path_exists(optional_path: Option<&PathBuf>) -> Result<(), Error> { |
| if let Some(path) = optional_path.as_ref() { |
| ensure!(path.exists(), "{:?} does not exist", path); |
| } |
| Ok(()) |
| } |
| |
| fn stamp(stamp_path: PathBuf) -> Result<(), Error> { |
| fs::File::create(stamp_path)?; |
| Ok(()) |
| } |