1010//
1111
1212mod arch;
13+ mod builtin_headers;
1314mod diag;
1415mod ir;
1516mod opt;
@@ -85,6 +86,14 @@ struct Args {
8586 #[ arg( short = 'I' , action = clap:: ArgAction :: Append , value_name = "dir" ) ]
8687 include_paths : Vec < String > ,
8788
89+ /// Disable all standard include paths (system and builtin)
90+ #[ arg( long = "nostdinc" , help = gettext( "Disable standard system include paths" ) ) ]
91+ no_std_inc : bool ,
92+
93+ /// Disable builtin include paths only (keep system paths)
94+ #[ arg( long = "nobuiltininc" , help = gettext( "Disable builtin #include directories" ) ) ]
95+ no_builtin_inc : bool ,
96+
8897 /// Compile and assemble, but do not link
8998 #[ arg( short = 'c' , help = gettext( "Compile and assemble, but do not link" ) ) ]
9099 compile_only : bool ,
@@ -124,6 +133,14 @@ struct Args {
124133 /// Pedantic mode (compatibility, currently no-op)
125134 #[ arg( long = "pedantic" , hide = true ) ]
126135 pedantic : bool ,
136+
137+ /// Add library search path (passed to linker)
138+ #[ arg( short = 'L' , action = clap:: ArgAction :: Append , value_name = "dir" ) ]
139+ lib_paths : Vec < String > ,
140+
141+ /// Link library (passed to linker)
142+ #[ arg( short = 'l' , action = clap:: ArgAction :: Append , value_name = "library" ) ]
143+ libraries : Vec < String > ,
127144}
128145
129146fn process_file (
@@ -194,6 +211,8 @@ fn process_file(
194211 & args. defines ,
195212 & args. undefines ,
196213 & args. include_paths ,
214+ args. no_std_inc ,
215+ args. no_builtin_inc ,
197216 ) ;
198217
199218 if args. preprocess_only {
@@ -348,9 +367,17 @@ fn process_file(
348367 }
349368
350369 // Link
351- let status = Command :: new ( "cc" )
352- . args ( [ "-o" , & exe_file, & temp_obj] )
353- . status ( ) ?;
370+ let mut link_cmd = Command :: new ( "cc" ) ;
371+ link_cmd. args ( [ "-o" , & exe_file, & temp_obj] ) ;
372+ // Add library search paths
373+ for lib_path in & args. lib_paths {
374+ link_cmd. arg ( format ! ( "-L{}" , lib_path) ) ;
375+ }
376+ // Add libraries
377+ for lib in & args. libraries {
378+ link_cmd. arg ( format ! ( "-l{}" , lib) ) ;
379+ }
380+ let status = link_cmd. status ( ) ?;
354381
355382 let _ = std:: fs:: remove_file ( & temp_obj) ;
356383
@@ -365,19 +392,59 @@ fn process_file(
365392 Ok ( ( ) )
366393}
367394
395+ /// Check if a string is a valid optimization level for -O
396+ fn is_valid_opt_level ( s : & str ) -> bool {
397+ matches ! ( s, "0" | "1" | "2" | "3" | "s" | "z" | "fast" | "g" )
398+ }
399+
368400/// Preprocess command-line arguments for gcc compatibility.
369- /// Converts -Wall → -W all, -Wextra → -W extra, etc.
401+ /// - Converts -Wall → -W all, -Wextra → -W extra, etc.
402+ /// - Handles -O flag: standalone -O followed by non-level becomes -O1
370403fn preprocess_args ( ) -> Vec < String > {
371- std:: env:: args ( )
372- . flat_map ( |arg| {
373- if arg. starts_with ( "-W" ) && arg. len ( ) > 2 {
374- // -Wall → -W all, -Wextra → -W extra, etc.
375- vec ! [ "-W" . to_string( ) , arg[ 2 ..] . to_string( ) ]
376- } else {
377- vec ! [ arg]
404+ let raw_args: Vec < String > = std:: env:: args ( ) . collect ( ) ;
405+ let mut result = Vec :: with_capacity ( raw_args. len ( ) ) ;
406+ let mut i = 0 ;
407+
408+ while i < raw_args. len ( ) {
409+ let arg = & raw_args[ i] ;
410+
411+ if arg == "-O" {
412+ // Standalone -O: check if next arg is a valid optimization level
413+ if i + 1 < raw_args. len ( ) && is_valid_opt_level ( & raw_args[ i + 1 ] ) {
414+ // Next arg is a level like "1", "2", etc. - merge them
415+ result. push ( format ! ( "-O{}" , raw_args[ i + 1 ] ) ) ;
416+ i += 2 ;
417+ continue ;
378418 }
379- } )
380- . collect ( )
419+ // Next arg is not a valid level (or no next arg) - use default -O1
420+ result. push ( "-O1" . to_string ( ) ) ;
421+ i += 1 ;
422+ } else if arg. starts_with ( "-O" ) && arg. len ( ) > 2 {
423+ // -O0, -O1, -O2, -O3, -Os, -Oz, -Ofast, -Og - pass through
424+ result. push ( arg. clone ( ) ) ;
425+ i += 1 ;
426+ } else if arg. starts_with ( "-W" ) && arg. len ( ) > 2 {
427+ // -Wall → -W all, -Wextra → -W extra, etc.
428+ result. push ( "-W" . to_string ( ) ) ;
429+ result. push ( arg[ 2 ..] . to_string ( ) ) ;
430+ i += 1 ;
431+ } else if arg. starts_with ( "-L" ) && arg. len ( ) > 2 {
432+ // -L. → -L .
433+ result. push ( "-L" . to_string ( ) ) ;
434+ result. push ( arg[ 2 ..] . to_string ( ) ) ;
435+ i += 1 ;
436+ } else if arg. starts_with ( "-l" ) && arg. len ( ) > 2 {
437+ // -lz → -l z
438+ result. push ( "-l" . to_string ( ) ) ;
439+ result. push ( arg[ 2 ..] . to_string ( ) ) ;
440+ i += 1 ;
441+ } else {
442+ result. push ( arg. clone ( ) ) ;
443+ i += 1 ;
444+ }
445+ }
446+
447+ result
381448}
382449
383450/// Check if a file is a C source file (by extension)
@@ -437,6 +504,14 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
437504 for obj in & object_files {
438505 link_cmd. arg ( * obj) ;
439506 }
507+ // Add library search paths
508+ for lib_path in & args. lib_paths {
509+ link_cmd. arg ( format ! ( "-L{}" , lib_path) ) ;
510+ }
511+ // Add libraries
512+ for lib in & args. libraries {
513+ link_cmd. arg ( format ! ( "-l{}" , lib) ) ;
514+ }
440515 let status = link_cmd. status ( ) ?;
441516 if !status. success ( ) {
442517 eprintln ! ( "pcc: linker failed" ) ;
0 commit comments