11// SPDX-License-Identifier: GPL-3.0-or-later
22
33use 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 } ;
65use bear:: output:: OutputWriter ;
76use bear:: recognition:: Recognition ;
87use bear:: transformation:: Transformation ;
98use bear:: { args, config} ;
10- use crossbeam_channel:: { bounded, Receiver } ;
119use 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.
1814fn 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-
8047impl 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- }
0 commit comments