blob: 7820d9e2f5756c338d4a4e058c6af92eed957d82 [file] [log] [blame]
// 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(())
}