Skip to content

Commit bcfbe95

Browse files
committed
rust: bear application code migrated to library
1 parent 27555a8 commit bcfbe95

File tree

4 files changed

+296
-242
lines changed

4 files changed

+296
-242
lines changed

rust/bear/src/bin/bear.rs

Lines changed: 7 additions & 242 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,14 @@
11
// SPDX-License-Identifier: GPL-3.0-or-later
22

33
use bear::input::EventFileReader;
4-
use bear::intercept::collector::{EventCollector, EventCollectorOnTcp};
5-
use bear::intercept::{Envelope, KEY_DESTINATION, KEY_PRELOAD_PATH};
4+
use bear::modes::{All, Intercept, Mode, Semantic};
65
use bear::output::OutputWriter;
76
use bear::recognition::Recognition;
87
use bear::transformation::Transformation;
98
use bear::{args, config};
10-
use crossbeam_channel::{bounded, Receiver};
119
use log;
12-
use std::path::{Path, PathBuf};
13-
use std::process::{Command, ExitCode};
14-
use std::sync::Arc;
15-
use std::{env, thread};
10+
use std::env;
11+
use std::process::ExitCode;
1612

1713
/// Driver function of the application.
1814
fn main() -> anyhow::Result<ExitCode> {
@@ -48,35 +44,6 @@ enum Application {
4844
All(All),
4945
}
5046

51-
/// The intercept mode we are only capturing the build commands.
52-
struct Intercept {
53-
input: args::BuildCommand,
54-
output: args::BuildEvents,
55-
config: config::Intercept,
56-
}
57-
58-
/// The semantic mode we are deduct the semantic meaning of the
59-
/// executed commands from the build process.
60-
struct Semantic {
61-
event_source: EventFileReader,
62-
semantic_recognition: Recognition,
63-
semantic_transform: Transformation,
64-
output_writer: OutputWriter,
65-
}
66-
67-
/// The all model is combining the intercept and semantic modes.
68-
struct All {
69-
input: args::BuildCommand,
70-
output: args::BuildSemantic,
71-
intercept_config: config::Intercept,
72-
output_config: config::Output,
73-
}
74-
75-
/// The mode trait is used to run the application in different modes.
76-
trait Mode {
77-
fn run(self) -> ExitCode;
78-
}
79-
8047
impl Application {
8148
/// Configure the application based on the command line arguments and the configuration.
8249
///
@@ -87,42 +54,31 @@ impl Application {
8754
match args.mode {
8855
args::Mode::Intercept { input, output } => {
8956
let intercept_config = config.intercept;
90-
let mode = Intercept {
91-
input,
92-
output,
93-
config: intercept_config,
94-
};
57+
let mode = Intercept::new(input, output, intercept_config);
9558
Ok(Application::Intercept(mode))
9659
}
9760
args::Mode::Semantic { input, output } => {
9861
let event_source = EventFileReader::try_from(input)?;
9962
let semantic_recognition = Recognition::try_from(&config)?;
10063
let semantic_transform = Transformation::from(&config.output);
10164
let output_writer = OutputWriter::configure(&output, &config.output)?;
102-
let mode = Semantic {
65+
let mode = Semantic::new(
10366
event_source,
10467
semantic_recognition,
10568
semantic_transform,
10669
output_writer,
107-
};
70+
);
10871
Ok(Application::Semantic(mode))
10972
}
11073
args::Mode::All { input, output } => {
11174
let intercept_config = config.intercept;
11275
let output_config = config.output;
113-
let mode = All {
114-
input,
115-
output,
116-
intercept_config,
117-
output_config,
118-
};
76+
let mode = All::new(input, output, intercept_config, output_config);
11977
Ok(Application::All(mode))
12078
}
12179
}
12280
}
123-
}
12481

125-
impl Mode for Application {
12682
fn run(self) -> ExitCode {
12783
match self {
12884
Application::Intercept(intercept) => intercept.run(),
@@ -131,194 +87,3 @@ impl Mode for Application {
13187
}
13288
}
13389
}
134-
135-
impl Mode for Intercept {
136-
fn run(self) -> ExitCode {
137-
match &self.config {
138-
config::Intercept::Wrapper { .. } => {
139-
let service = InterceptService::new()
140-
.expect("Failed to create the intercept service");
141-
let environment = InterceptEnvironment::new(&self.config, service.address())
142-
.expect("Failed to create the intercept environment");
143-
144-
// start writer thread
145-
let writer_thread = thread::spawn(move || {
146-
let mut writer = std::fs::File::create(self.output.file_name)
147-
.expect("Failed to create the output file");
148-
for envelope in service.receiver().iter() {
149-
envelope
150-
.write_into(&mut writer)
151-
.expect("Failed to write the envelope");
152-
}
153-
});
154-
155-
let status = environment.execute_build_command(self.input);
156-
157-
writer_thread
158-
.join()
159-
.expect("Failed to join the writer thread");
160-
161-
status.unwrap_or(ExitCode::FAILURE)
162-
}
163-
config::Intercept::Preload { .. } => {
164-
todo!()
165-
}
166-
}
167-
}
168-
}
169-
170-
impl Mode for Semantic {
171-
fn run(self) -> ExitCode {
172-
// Set up the pipeline of compilation database entries.
173-
let entries = self
174-
.event_source
175-
.generate()
176-
.flat_map(|execution| self.semantic_recognition.apply(execution))
177-
.flat_map(|semantic| self.semantic_transform.apply(semantic));
178-
// Consume the entries and write them to the output file.
179-
// The exit code is based on the result of the output writer.
180-
match self.output_writer.run(entries) {
181-
Ok(_) => ExitCode::SUCCESS,
182-
Err(_) => ExitCode::FAILURE,
183-
}
184-
}
185-
}
186-
187-
impl Mode for All {
188-
fn run(self) -> ExitCode {
189-
// TODO: Implement the all mode.
190-
ExitCode::FAILURE
191-
}
192-
}
193-
194-
struct InterceptService {
195-
collector: Arc<EventCollectorOnTcp>,
196-
receiver: Receiver<Envelope>,
197-
collector_thread: Option<thread::JoinHandle<()>>,
198-
}
199-
200-
impl InterceptService {
201-
pub fn new() -> anyhow::Result<Self> {
202-
let collector = EventCollectorOnTcp::new()?;
203-
let collector_arc = Arc::new(collector);
204-
let (sender, receiver) = bounded(32);
205-
206-
let collector_in_thread = collector_arc.clone();
207-
let collector_thread = thread::spawn(move || {
208-
collector_in_thread.collect(sender).unwrap();
209-
});
210-
211-
Ok(InterceptService {
212-
collector: collector_arc,
213-
receiver,
214-
collector_thread: Some(collector_thread),
215-
})
216-
}
217-
218-
pub fn receiver(&self) -> Receiver<Envelope> {
219-
self.receiver.clone()
220-
}
221-
222-
pub fn address(&self) -> String {
223-
self.collector.address()
224-
}
225-
}
226-
227-
impl Drop for InterceptService {
228-
fn drop(&mut self) {
229-
self.collector.stop().expect("Failed to stop the collector");
230-
if let Some(thread) = self.collector_thread.take() {
231-
thread.join().expect("Failed to join the collector thread");
232-
}
233-
}
234-
}
235-
236-
enum InterceptEnvironment {
237-
Wrapper {
238-
bin_dir: tempfile::TempDir,
239-
address: String,
240-
},
241-
Preload {
242-
path: PathBuf,
243-
address: String,
244-
},
245-
}
246-
247-
impl InterceptEnvironment {
248-
pub fn new(config: &config::Intercept, address: String) -> anyhow::Result<Self> {
249-
let result = match config {
250-
config::Intercept::Wrapper {
251-
path,
252-
directory,
253-
executables,
254-
} => {
255-
// Create a temporary directory and populate it with the executables.
256-
let bin_dir = tempfile::TempDir::with_prefix_in(directory, "bear-")?;
257-
for executable in executables {
258-
std::fs::hard_link(&executable, &path)?;
259-
}
260-
InterceptEnvironment::Wrapper { bin_dir, address }
261-
}
262-
config::Intercept::Preload { path } => InterceptEnvironment::Preload {
263-
path: path.clone(),
264-
address,
265-
},
266-
};
267-
Ok(result)
268-
}
269-
270-
pub fn execute_build_command(self, input: args::BuildCommand) -> anyhow::Result<ExitCode> {
271-
let environment = self.environment();
272-
let mut child = Command::new(input.arguments[0].clone())
273-
.args(input.arguments)
274-
.envs(environment)
275-
.spawn()?;
276-
277-
let result = child.wait()?;
278-
279-
if result.success() {
280-
Ok(ExitCode::SUCCESS)
281-
} else {
282-
result
283-
.code()
284-
.map_or(Ok(ExitCode::FAILURE), |code| Ok(ExitCode::from(code as u8)))
285-
}
286-
}
287-
288-
fn environment(&self) -> Vec<(String, String)> {
289-
match self {
290-
InterceptEnvironment::Wrapper {
291-
bin_dir, address, ..
292-
} => {
293-
let path_original = env::var("PATH").unwrap_or_else(|_| String::new());
294-
let path_updated = InterceptEnvironment::insert_to_path(
295-
&path_original,
296-
Self::to_string(bin_dir.path()),
297-
);
298-
vec![
299-
("PATH".to_string(), path_updated),
300-
(KEY_DESTINATION.to_string(), address.clone()),
301-
]
302-
}
303-
InterceptEnvironment::Preload { path, address, .. } => {
304-
let path_original = env::var(KEY_PRELOAD_PATH).unwrap_or_else(|_| String::new());
305-
let path_updated =
306-
InterceptEnvironment::insert_to_path(&path_original, Self::to_string(path));
307-
vec![
308-
(KEY_PRELOAD_PATH.to_string(), path_updated),
309-
(KEY_DESTINATION.to_string(), address.clone()),
310-
]
311-
}
312-
}
313-
}
314-
315-
fn insert_to_path(original: &str, first: String) -> String {
316-
let mut paths: Vec<_> = original.split(':').filter(|it| it != &first).collect();
317-
paths.insert(0, first.as_str());
318-
paths.join(":")
319-
}
320-
321-
fn to_string(path: &Path) -> String {
322-
path.to_str().unwrap_or("").to_string()
323-
}
324-
}

rust/bear/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ pub mod config;
55
mod fixtures;
66
pub mod input;
77
pub mod intercept;
8+
pub mod modes;
89
pub mod output;
910
pub mod recognition;
1011
pub mod semantic;

0 commit comments

Comments
 (0)