Merge changes I5a2e2871,I53b1f37e,I693ea16a into main
* changes: Split cargo_out into three modules. Factor out functions to find package directory. Add more doc comments and break up parse_cargo_out function.
This commit is contained in:
86
tools/cargo_embargo/src/cargo.rs
Normal file
86
tools/cargo_embargo/src/cargo.rs
Normal file
@@ -0,0 +1,86 @@
|
||||
// Copyright (C) 2023 The Android Open Source Project
|
||||
//
|
||||
// Licensed 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.
|
||||
|
||||
//! Types and functions for parsing the output of cargo.
|
||||
|
||||
pub mod cargo_out;
|
||||
pub mod metadata;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// Combined representation of --crate-type and --test flags.
|
||||
#[derive(Copy, Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
|
||||
#[serde[rename_all = "lowercase"]]
|
||||
pub enum CrateType {
|
||||
// --crate-type types
|
||||
Bin,
|
||||
Lib,
|
||||
RLib,
|
||||
DyLib,
|
||||
CDyLib,
|
||||
StaticLib,
|
||||
#[serde(rename = "proc-macro")]
|
||||
ProcMacro,
|
||||
// --test
|
||||
Test,
|
||||
// "--cfg test" without --test. (Assume it is a test with the harness disabled.
|
||||
TestNoHarness,
|
||||
}
|
||||
|
||||
impl CrateType {
|
||||
fn from_str(s: &str) -> CrateType {
|
||||
match s {
|
||||
"bin" => CrateType::Bin,
|
||||
"lib" => CrateType::Lib,
|
||||
"rlib" => CrateType::RLib,
|
||||
"dylib" => CrateType::DyLib,
|
||||
"cdylib" => CrateType::CDyLib,
|
||||
"staticlib" => CrateType::StaticLib,
|
||||
"proc-macro" => CrateType::ProcMacro,
|
||||
_ => panic!("unexpected --crate-type: {}", s),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CrateType {
|
||||
/// Returns whether the crate type is a kind of library.
|
||||
pub fn is_library(self) -> bool {
|
||||
matches!(self, Self::Lib | Self::RLib | Self::DyLib | Self::CDyLib | Self::StaticLib)
|
||||
}
|
||||
}
|
||||
|
||||
/// Info extracted from `CargoOut` for a crate.
|
||||
///
|
||||
/// Note that there is a 1-to-many relationship between a Cargo.toml file and these `Crate`
|
||||
/// objects. For example, a Cargo.toml file might have a bin, a lib, and various tests. Each of
|
||||
/// those will be a separate `Crate`. All of them will have the same `package_name`.
|
||||
#[derive(Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
|
||||
pub struct Crate {
|
||||
pub name: String,
|
||||
pub package_name: String,
|
||||
pub version: Option<String>,
|
||||
pub types: Vec<CrateType>,
|
||||
pub target: Option<String>, // --target
|
||||
pub features: Vec<String>, // --cfg feature=
|
||||
pub cfgs: Vec<String>, // non-feature --cfg
|
||||
pub externs: Vec<(String, Option<String>)>, // name => rlib file
|
||||
pub codegens: Vec<String>, // -C
|
||||
pub cap_lints: String,
|
||||
pub static_libs: Vec<String>,
|
||||
pub shared_libs: Vec<String>,
|
||||
pub edition: String,
|
||||
pub package_dir: PathBuf, // canonicalized
|
||||
pub main_src: PathBuf, // relative to package_dir
|
||||
}
|
||||
@@ -12,91 +12,60 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use super::metadata::WorkspaceMetadata;
|
||||
use super::{Crate, CrateType};
|
||||
use anyhow::anyhow;
|
||||
use anyhow::bail;
|
||||
use anyhow::Context;
|
||||
use anyhow::Result;
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::BTreeMap;
|
||||
use std::env;
|
||||
use std::fs::{read_to_string, File};
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// Combined representation of --crate-type and --test flags.
|
||||
#[derive(Copy, Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
|
||||
#[serde[rename_all = "lowercase"]]
|
||||
pub enum CrateType {
|
||||
// --crate-type types
|
||||
Bin,
|
||||
Lib,
|
||||
RLib,
|
||||
DyLib,
|
||||
CDyLib,
|
||||
StaticLib,
|
||||
ProcMacro,
|
||||
// --test
|
||||
Test,
|
||||
// "--cfg test" without --test. (Assume it is a test with the harness disabled.
|
||||
TestNoHarness,
|
||||
}
|
||||
|
||||
impl CrateType {
|
||||
/// Returns whether the crate type is a kind of library.
|
||||
pub fn is_library(self) -> bool {
|
||||
matches!(self, Self::Lib | Self::RLib | Self::DyLib | Self::CDyLib | Self::StaticLib)
|
||||
}
|
||||
}
|
||||
|
||||
/// Info extracted from `CargoOut` for a crate.
|
||||
/// Reads the given `cargo.out` and `cargo.metadata` files, and generates a list of crates based on
|
||||
/// the rustc invocations.
|
||||
///
|
||||
/// Note that there is a 1-to-many relationship between a Cargo.toml file and these `Crate`
|
||||
/// objects. For example, a Cargo.toml file might have a bin, a lib, and various tests. Each of
|
||||
/// those will be a separate `Crate`. All of them will have the same `package_name`.
|
||||
#[derive(Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
|
||||
pub struct Crate {
|
||||
pub name: String,
|
||||
pub package_name: String,
|
||||
pub version: Option<String>,
|
||||
pub types: Vec<CrateType>,
|
||||
pub target: Option<String>, // --target
|
||||
pub features: Vec<String>, // --cfg feature=
|
||||
pub cfgs: Vec<String>, // non-feature --cfg
|
||||
pub externs: Vec<(String, Option<String>)>, // name => rlib file
|
||||
pub codegens: Vec<String>, // -C
|
||||
pub cap_lints: String,
|
||||
pub static_libs: Vec<String>,
|
||||
pub shared_libs: Vec<String>,
|
||||
pub edition: String,
|
||||
pub package_dir: PathBuf, // canonicalized
|
||||
pub main_src: PathBuf, // relative to package_dir
|
||||
}
|
||||
|
||||
pub fn parse_cargo_out(cargo_out_path: &str, cargo_metadata_path: &str) -> Result<Vec<Crate>> {
|
||||
let metadata: WorkspaceMetadata = serde_json::from_str(
|
||||
&std::fs::read_to_string(cargo_metadata_path).context("failed to read cargo.metadata")?,
|
||||
/// Ignores crates outside the current directory and build script crates.
|
||||
pub fn parse_cargo_out(
|
||||
cargo_out_path: impl AsRef<Path>,
|
||||
cargo_metadata_path: impl AsRef<Path>,
|
||||
) -> Result<Vec<Crate>> {
|
||||
let cargo_out = read_to_string(cargo_out_path).context("failed to read cargo.out")?;
|
||||
let metadata = serde_json::from_reader(
|
||||
File::open(cargo_metadata_path).context("failed to open cargo.metadata")?,
|
||||
)
|
||||
.context("failed to parse cargo.metadata")?;
|
||||
parse_cargo_out_str(&cargo_out, &metadata, env::current_dir().unwrap().canonicalize().unwrap())
|
||||
}
|
||||
|
||||
let cargo_out = CargoOut::parse(
|
||||
&std::fs::read_to_string(cargo_out_path).context("failed to read cargo.out")?,
|
||||
)
|
||||
.context("failed to parse cargo.out")?;
|
||||
/// Parses the given `cargo.out` and `cargo.metadata` file contents and generates a list of crates
|
||||
/// based on the rustc invocations.
|
||||
///
|
||||
/// Ignores crates outside `base_directory` and build script crates.
|
||||
fn parse_cargo_out_str(
|
||||
cargo_out: &str,
|
||||
metadata: &WorkspaceMetadata,
|
||||
base_directory: impl AsRef<Path>,
|
||||
) -> Result<Vec<Crate>> {
|
||||
let cargo_out = CargoOut::parse(cargo_out).context("failed to parse cargo.out")?;
|
||||
|
||||
assert!(cargo_out.cc_invocations.is_empty(), "cc not supported yet");
|
||||
assert!(cargo_out.ar_invocations.is_empty(), "ar not supported yet");
|
||||
|
||||
let mut crates = Vec::new();
|
||||
for rustc in cargo_out.rustc_invocations.iter() {
|
||||
let c = Crate::from_rustc_invocation(rustc, &metadata)
|
||||
let c = Crate::from_rustc_invocation(rustc, metadata)
|
||||
.with_context(|| format!("failed to process rustc invocation: {rustc}"))?;
|
||||
// Ignore build.rs crates.
|
||||
if c.name.starts_with("build_script_") {
|
||||
continue;
|
||||
}
|
||||
// Ignore crates outside the current directory.
|
||||
let cwd = std::env::current_dir().unwrap().canonicalize().unwrap();
|
||||
if !c.package_dir.starts_with(cwd) {
|
||||
// Ignore crates outside the base directory.
|
||||
if !c.package_dir.starts_with(&base_directory) {
|
||||
continue;
|
||||
}
|
||||
crates.push(c);
|
||||
@@ -104,20 +73,6 @@ pub fn parse_cargo_out(cargo_out_path: &str, cargo_metadata_path: &str) -> Resul
|
||||
Ok(crates)
|
||||
}
|
||||
|
||||
/// `cargo metadata` output.
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct WorkspaceMetadata {
|
||||
packages: Vec<PackageMetadata>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct PackageMetadata {
|
||||
name: String,
|
||||
version: String,
|
||||
edition: String,
|
||||
manifest_path: String,
|
||||
}
|
||||
|
||||
/// Raw-ish data extracted from cargo.out file.
|
||||
#[derive(Debug, Default)]
|
||||
struct CargoOut {
|
||||
@@ -240,21 +195,6 @@ impl CargoOut {
|
||||
}
|
||||
}
|
||||
|
||||
impl CrateType {
|
||||
fn from_str(s: &str) -> CrateType {
|
||||
match s {
|
||||
"bin" => CrateType::Bin,
|
||||
"lib" => CrateType::Lib,
|
||||
"rlib" => CrateType::RLib,
|
||||
"dylib" => CrateType::DyLib,
|
||||
"cdylib" => CrateType::CDyLib,
|
||||
"staticlib" => CrateType::StaticLib,
|
||||
"proc-macro" => CrateType::ProcMacro,
|
||||
_ => panic!("unexpected --crate-type: {}", s),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Crate {
|
||||
fn from_rustc_invocation(rustc: &str, metadata: &WorkspaceMetadata) -> Result<Crate> {
|
||||
let mut out = Crate::default();
|
||||
@@ -339,27 +279,7 @@ impl Crate {
|
||||
}
|
||||
}
|
||||
_ if !arg.starts_with('-') => {
|
||||
let src_path = Path::new(arg);
|
||||
// Canonicalize the path because:
|
||||
//
|
||||
// 1. We don't consistently get relative or absolute paths elsewhere. If we
|
||||
// canonicalize everything, it becomes easy to compare paths.
|
||||
//
|
||||
// 2. We don't want to consider symlinks to code outside the cwd as part of the
|
||||
// project (e.g. AOSP's import of crosvm has symlinks from crosvm's own 3p
|
||||
// directory to the android 3p directories).
|
||||
let src_path = src_path
|
||||
.canonicalize()
|
||||
.unwrap_or_else(|e| panic!("failed to canonicalize {src_path:?}: {}", e));
|
||||
out.package_dir = src_path.parent().unwrap().to_path_buf();
|
||||
while !out.package_dir.join("Cargo.toml").try_exists()? {
|
||||
if let Some(parent) = out.package_dir.parent() {
|
||||
out.package_dir = parent.to_path_buf();
|
||||
} else {
|
||||
bail!("No Cargo.toml found in parents of {:?}", src_path);
|
||||
}
|
||||
}
|
||||
out.main_src = src_path.strip_prefix(&out.package_dir).unwrap().to_path_buf();
|
||||
(out.package_dir, out.main_src) = split_src_path(Path::new(arg))?;
|
||||
}
|
||||
|
||||
// ignored flags
|
||||
@@ -426,3 +346,35 @@ impl Crate {
|
||||
Ok(out)
|
||||
}
|
||||
}
|
||||
|
||||
/// Given a path to the main source file of some Rust crate, returns the canonical path to the
|
||||
/// package directory, and the relative path to the source file within that directory.
|
||||
fn split_src_path(src_path: &Path) -> Result<(PathBuf, PathBuf)> {
|
||||
// Canonicalize the path because:
|
||||
//
|
||||
// 1. We don't consistently get relative or absolute paths elsewhere. If we
|
||||
// canonicalize everything, it becomes easy to compare paths.
|
||||
//
|
||||
// 2. We don't want to consider symlinks to code outside the cwd as part of the
|
||||
// project (e.g. AOSP's import of crosvm has symlinks from crosvm's own 3p
|
||||
// directory to the android 3p directories).
|
||||
let src_path = src_path
|
||||
.canonicalize()
|
||||
.unwrap_or_else(|e| panic!("failed to canonicalize {src_path:?}: {}", e));
|
||||
let package_dir = find_cargo_toml(&src_path)?;
|
||||
let main_src = src_path.strip_prefix(&package_dir).unwrap().to_path_buf();
|
||||
|
||||
Ok((package_dir, main_src))
|
||||
}
|
||||
|
||||
/// Given a path to a Rust source file, finds the closest ancestor directory containing a
|
||||
/// `Cargo.toml` file.
|
||||
fn find_cargo_toml(src_path: &Path) -> Result<PathBuf> {
|
||||
let mut package_dir = src_path.parent().unwrap();
|
||||
while !package_dir.join("Cargo.toml").try_exists()? {
|
||||
package_dir = package_dir
|
||||
.parent()
|
||||
.ok_or_else(|| anyhow!("No Cargo.toml found in parents of {:?}", src_path))?;
|
||||
}
|
||||
Ok(package_dir.to_path_buf())
|
||||
}
|
||||
31
tools/cargo_embargo/src/cargo/metadata.rs
Normal file
31
tools/cargo_embargo/src/cargo/metadata.rs
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright (C) 2023 The Android Open Source Project
|
||||
//
|
||||
// Licensed 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.
|
||||
|
||||
//! Types for parsing cargo.metadata JSON files.
|
||||
|
||||
use serde::Deserialize;
|
||||
|
||||
/// `cargo metadata` output.
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct WorkspaceMetadata {
|
||||
pub packages: Vec<PackageMetadata>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct PackageMetadata {
|
||||
pub name: String,
|
||||
pub version: String,
|
||||
pub edition: String,
|
||||
pub manifest_path: String,
|
||||
}
|
||||
@@ -27,7 +27,7 @@
|
||||
//! available to tweak it via a config file.
|
||||
|
||||
mod bp;
|
||||
mod cargo_out;
|
||||
mod cargo;
|
||||
mod config;
|
||||
|
||||
use crate::config::Config;
|
||||
@@ -36,7 +36,7 @@ use anyhow::bail;
|
||||
use anyhow::Context;
|
||||
use anyhow::Result;
|
||||
use bp::*;
|
||||
use cargo_out::{parse_cargo_out, Crate, CrateType};
|
||||
use cargo::{cargo_out::parse_cargo_out, Crate, CrateType};
|
||||
use clap::Parser;
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
@@ -52,6 +52,7 @@ use std::process::Command;
|
||||
// * handle errors, esp. in cargo.out parsing. they should fail the program with an error code
|
||||
// * handle warnings. put them in comments in the android.bp, some kind of report section
|
||||
|
||||
/// Command-line parameters for `cargo_embargo`.
|
||||
#[derive(Parser, Debug)]
|
||||
#[clap()]
|
||||
struct Args {
|
||||
|
||||
Reference in New Issue
Block a user