rust: semantic and combinded mode share code

This commit is contained in:
Laszlo Nagy
2024-12-15 20:33:12 +11:00
parent 6035d2de37
commit a200209f84
2 changed files with 67 additions and 80 deletions

View File

@@ -1,13 +1,9 @@
// SPDX-License-Identifier: GPL-3.0-or-later
use crate::ipc::Envelope;
use crate::modes::intercept::{CollectorService, InterceptEnvironment};
use crate::modes::semantic::Semantic;
use crate::modes::semantic::SemanticFromEnvelopes;
use crate::modes::Mode;
use crate::output::{OutputWriter, OutputWriterImpl};
use crate::semantic::transformation::Transformation;
use crate::semantic::Transform;
use crate::{args, config, semantic};
use crate::{args, config};
use anyhow::Context;
use std::process::ExitCode;
@@ -15,9 +11,7 @@ use std::process::ExitCode;
pub struct Combined {
command: args::BuildCommand,
intercept_config: config::Intercept,
interpreter: Box<dyn semantic::Interpreter>,
semantic_transform: Transformation,
output_writer: OutputWriterImpl,
semantic: SemanticFromEnvelopes,
}
impl Combined {
@@ -27,38 +21,15 @@ impl Combined {
output: args::BuildSemantic,
config: config::Main,
) -> anyhow::Result<Self> {
let interpreter = Semantic::interpreter(&config)?;
let semantic_transform = Transformation::from(&config.output);
let output_writer = OutputWriterImpl::create(&output, &config.output)?;
let semantic = SemanticFromEnvelopes::from(output, &config)?;
let intercept_config = config.intercept;
Ok(Self {
command,
intercept_config,
interpreter,
semantic_transform,
output_writer,
semantic,
})
}
/// Consumer the envelopes for analysis and write the result to the output file.
/// This implements the pipeline of the semantic analysis. Same as the `Semantic` mode.
fn consume_for_analysis(
interpreter: Box<dyn semantic::Interpreter>,
semantic_transform: Transformation,
output_writer: OutputWriterImpl,
envelopes: impl IntoIterator<Item = Envelope>,
) -> anyhow::Result<()> {
let entries = envelopes
.into_iter()
.map(|envelope| envelope.event.execution)
.inspect(|execution| log::debug!("execution: {}", execution))
.flat_map(|execution| interpreter.recognize(&execution))
.inspect(|semantic| log::debug!("semantic: {:?}", semantic))
.flat_map(|semantic| semantic_transform.apply(semantic));
output_writer.run(entries)
}
}
impl Mode for Combined {
@@ -69,13 +40,9 @@ impl Mode for Combined {
///
/// The exit code is based on the result of the build command.
fn run(self) -> anyhow::Result<ExitCode> {
let interpreter = self.interpreter;
let semantic_transform = self.semantic_transform;
let output_writer = self.output_writer;
let service = CollectorService::new(move |envelopes| {
Self::consume_for_analysis(interpreter, semantic_transform, output_writer, envelopes)
})
.with_context(|| "Failed to create the ipc service")?;
let semantic = self.semantic;
let service = CollectorService::new(move |envelopes| semantic.analyze_and_write(envelopes))
.with_context(|| "Failed to create the ipc service")?;
let status = InterceptEnvironment::new(&self.intercept_config, service.address())
.with_context(|| "Failed to create the ipc environment")?

View File

@@ -1,12 +1,12 @@
// SPDX-License-Identifier: GPL-3.0-or-later
use super::super::semantic;
use crate::ipc::Envelope;
use crate::modes::Mode;
use crate::output::event::read;
use crate::output::{OutputWriter, OutputWriterImpl};
use crate::semantic::transformation::Transformation;
use crate::semantic::Transform;
use crate::{args, config};
use crate::{args, config, semantic};
use anyhow::Context;
use std::fs::{File, OpenOptions};
use std::io::BufReader;
@@ -15,40 +15,62 @@ use std::process::ExitCode;
/// The semantic mode we are deduct the semantic meaning of the
/// executed commands from the build process.
pub struct Semantic {
event_source: BufReader<File>,
interpreter: Box<dyn semantic::Interpreter>,
semantic_transform: Transformation,
output_writer: OutputWriterImpl,
event_file: BufReader<File>,
semantic: SemanticFromEnvelopes,
}
impl Semantic {
/// Create a new semantic mode instance.
pub fn from(
input: args::BuildEvents,
output: args::BuildSemantic,
config: config::Main,
) -> anyhow::Result<Self> {
let event_source = Self::open(input.file_name.as_str())?;
let interpreter = Self::interpreter(&config)?;
let semantic_transform = Transformation::from(&config.output);
let output_writer = OutputWriterImpl::create(&output, &config.output)?;
Ok(Self {
event_source,
interpreter,
semantic_transform,
output_writer,
})
}
/// Open the event file for reading.
fn open(file_name: &str) -> anyhow::Result<BufReader<File>> {
let file = OpenOptions::new()
let file_name = input.file_name.as_str();
let event_file = OpenOptions::new()
.read(true)
.open(file_name)
.map(BufReader::new)
.with_context(|| format!("Failed to open file: {:?}", file_name))?;
Ok(file)
let semantic = SemanticFromEnvelopes::from(output, &config)?;
Ok(Self {
event_file,
semantic,
})
}
}
impl Mode for Semantic {
/// Run the semantic mode by reading the event file and analyzing the events.
///
/// The exit code is based on the result of the output writer.
fn run(self) -> anyhow::Result<ExitCode> {
self.semantic
.analyze_and_write(read(self.event_file))
.map(|_| ExitCode::SUCCESS)
}
}
/// The semantic analysis that is independent of the event source.
pub struct SemanticFromEnvelopes {
interpreter: Box<dyn semantic::Interpreter>,
transform: Transformation,
output_writer: OutputWriterImpl,
}
impl SemanticFromEnvelopes {
/// Create a new semantic mode instance.
pub(super) fn from(output: args::BuildSemantic, config: &config::Main) -> anyhow::Result<Self> {
let interpreter = Self::interpreter(config)?;
let transform = Transformation::from(&config.output);
let output_writer = OutputWriterImpl::create(&output, &config.output)?;
Ok(Self {
interpreter,
transform,
output_writer,
})
}
/// Creates an interpreter to recognize the compiler calls.
@@ -59,7 +81,9 @@ impl Semantic {
// TODO: Use the CC or CXX environment variables to detect the compiler to include.
// Use the CC or CXX environment variables and make sure those are not excluded.
// Make sure the environment variables are passed to the method.
pub fn interpreter(config: &config::Main) -> anyhow::Result<Box<dyn semantic::Interpreter>> {
// TODO: Move this method to the `semantic` module. (instead of expose the builder)
// TODO: Take environment variables as input.
fn interpreter(config: &config::Main) -> anyhow::Result<Box<dyn semantic::Interpreter>> {
let compilers_to_include = match &config.intercept {
config::Intercept::Wrapper { executables, .. } => executables.clone(),
_ => vec![],
@@ -79,27 +103,23 @@ impl Semantic {
Ok(Box::new(interpreter))
}
}
impl Mode for Semantic {
/// Run the semantic mode by generating the compilation database entries
/// from the event source. The entries are then processed by the semantic
/// recognition and transformation. The result is written to the output file.
///
/// The exit code is based on the result of the output writer.
fn run(self) -> anyhow::Result<ExitCode> {
/// Consumer the envelopes for analysis and write the result to the output file.
/// This implements the pipeline of the semantic analysis.
pub(super) fn analyze_and_write(
self,
envelopes: impl IntoIterator<Item = Envelope>,
) -> anyhow::Result<()> {
// Set up the pipeline of compilation database entries.
let entries = read(self.event_source)
let entries = envelopes
.into_iter()
.map(|envelope| envelope.event.execution)
.inspect(|execution| log::debug!("execution: {}", execution))
.flat_map(|execution| self.interpreter.recognize(&execution))
.inspect(|semantic| log::debug!("semantic: {:?}", semantic))
.flat_map(|semantic| self.semantic_transform.apply(semantic));
.flat_map(|semantic| self.transform.apply(semantic));
// Consume the entries and write them to the output file.
// The exit code is based on the result of the output writer.
match self.output_writer.run(entries) {
Ok(_) => Ok(ExitCode::SUCCESS),
Err(_) => Ok(ExitCode::FAILURE),
}
self.output_writer.run(entries)
}
}