1+ using System . Diagnostics ;
2+ using System . Runtime . InteropServices ;
3+ using ebuild . api ;
4+ using ebuild . api . Compiler ;
5+
6+ namespace ebuild . Compilers
7+ {
8+ public class GccCompiler : CompilerBase
9+ {
10+ private readonly Architecture _targetArchitecture ;
11+ private readonly string _gccExecutablePath ;
12+ private readonly string _gxxExecutablePath ;
13+
14+ public GccCompiler ( Architecture targetArchitecture )
15+ {
16+ _targetArchitecture = targetArchitecture ;
17+ _gccExecutablePath = FindExecutable ( "gcc" ) ?? throw new Exception ( "GCC compiler not found in PATH" ) ;
18+ _gxxExecutablePath = FindExecutable ( "g++" ) ?? throw new Exception ( "G++ compiler not found in PATH" ) ;
19+ }
20+
21+ private static string ? FindExecutable ( string executableName )
22+ {
23+ var pathEnv = Environment . GetEnvironmentVariable ( "PATH" ) ;
24+ if ( string . IsNullOrEmpty ( pathEnv ) )
25+ return null ;
26+
27+ var paths = pathEnv . Split ( Path . PathSeparator ) ;
28+ foreach ( var path in paths )
29+ {
30+ var fullPath = Path . Combine ( path , executableName ) ;
31+ if ( File . Exists ( fullPath ) )
32+ return fullPath ;
33+
34+ // Try with .exe extension on Windows
35+ var exePath = fullPath + ".exe" ;
36+ if ( File . Exists ( exePath ) )
37+ return exePath ;
38+ }
39+ return null ;
40+ }
41+
42+ public override Task < bool > Generate ( CompilerSettings settings , CancellationToken cancellationToken , string type , object ? data = null )
43+ {
44+ //TODO: Implement this method to generate compile commands or other artifacts as needed.
45+ throw new NotImplementedException ( ) ;
46+ }
47+
48+ public override async Task < bool > Compile ( CompilerSettings settings , CancellationToken cancellationToken )
49+ {
50+ // Use the output file as provided by the platform
51+ var outputFile = settings . OutputFile ;
52+
53+ // Create output directory if it doesn't exist (both the final dir and any intermediate directories)
54+ var outputDir = Path . GetDirectoryName ( outputFile ) ;
55+ if ( ! string . IsNullOrEmpty ( outputDir ) && ! Directory . Exists ( outputDir ) )
56+ {
57+ Console . WriteLine ( $ "Creating directory: { outputDir } ") ;
58+ Directory . CreateDirectory ( outputDir ) ;
59+ }
60+
61+
62+
63+ // Determine whether to use gcc or g++ based on settings
64+ var compilerPath = settings . CStandard != null ? _gccExecutablePath : _gxxExecutablePath ;
65+
66+ // Prepare the command line arguments for gcc/g++
67+ var args = new ArgumentBuilder ( ) ;
68+
69+ // Basic compilation flags
70+ args . Add ( "-c" ) ; // Compile only, do not link
71+ args . Add ( "-o" ) ;
72+ args . Add ( outputFile ) ; // Specify output file
73+
74+ // Language standard
75+ if ( settings . CStandard != null )
76+ {
77+ args . Add ( $ "-std={ settings . CStandard switch
78+ {
79+ CStandards . C89 => "c89" ,
80+ CStandards . C99 => "c99" ,
81+ CStandards . C11 => "c11" ,
82+ CStandards . C17 => "c17" ,
83+ _ => "c17"
84+ } } " ) ;
85+ }
86+ else
87+ {
88+ args . Add ( $ "-std={ settings . CppStandard switch
89+ {
90+ CppStandards . Cpp98 => "c++98" ,
91+ CppStandards . Cpp03 => "c++03" ,
92+ CppStandards . Cpp11 => "c++11" ,
93+ CppStandards . Cpp14 => "c++14" ,
94+ CppStandards . Cpp17 => "c++17" ,
95+ CppStandards . Cpp20 => "c++20" ,
96+ CppStandards . Cpp23 => "c++23" ,
97+ _ => "c++20"
98+ } } " ) ;
99+
100+ if ( settings . EnableExceptions )
101+ {
102+ args . Add ( "-fexceptions" ) ; // Enable C++ exceptions
103+ }
104+ else
105+ {
106+ args . Add ( "-fno-exceptions" ) ; // Disable C++ exceptions
107+ }
108+
109+ if ( settings . EnableRTTI )
110+ {
111+ args . Add ( "-frtti" ) ; // Enable RTTI
112+ }
113+ else
114+ {
115+ args . Add ( "-fno-rtti" ) ; // Disable RTTI
116+ }
117+ }
118+
119+ // Architecture-specific flags
120+ if ( _targetArchitecture == Architecture . X86 )
121+ {
122+ args . Add ( "-m32" ) ;
123+ }
124+ else if ( _targetArchitecture == Architecture . X64 )
125+ {
126+ args . Add ( "-m64" ) ;
127+ }
128+ else if ( _targetArchitecture == Architecture . Arm )
129+ {
130+ args . Add ( "-march=armv7-a" ) ;
131+ }
132+ else if ( _targetArchitecture == Architecture . Arm64 )
133+ {
134+ args . Add ( "-march=armv8-a" ) ;
135+ }
136+ else if ( _targetArchitecture == Architecture . Armv6 )
137+ {
138+ args . Add ( "-march=armv6" ) ;
139+ }
140+ else if ( _targetArchitecture == Architecture . Wasm )
141+ {
142+ // WebAssembly target would need special handling
143+ args . Add ( "-target" ) ;
144+ args . Add ( "wasm32" ) ;
145+ }
146+ else if ( _targetArchitecture == Architecture . S390x )
147+ {
148+ args . Add ( "-march=z196" ) ;
149+ }
150+ else if ( _targetArchitecture == Architecture . LoongArch64 )
151+ {
152+ args . Add ( "-march=loongarch64" ) ;
153+ }
154+ else if ( _targetArchitecture == Architecture . Ppc64le )
155+ {
156+ args . Add ( "-mcpu=power8" ) ;
157+ }
158+
159+ // CPU extensions
160+ if ( settings . CPUExtension != CPUExtensions . Default )
161+ {
162+ args . Add ( $ "-march={ settings . CPUExtension switch
163+ {
164+ CPUExtensions . SSE => "pentium3" ,
165+ CPUExtensions . SSE2 => "pentium4" ,
166+ CPUExtensions . AVX => "sandybridge" ,
167+ CPUExtensions . AVX2 => "haswell" ,
168+ CPUExtensions . AVX512 => "skylake-avx512" ,
169+ CPUExtensions . armv8_0 => "armv8-a" ,
170+ CPUExtensions . armv8_1 => "armv8.1-a" ,
171+ CPUExtensions . armv8_2 => "armv8.2-a" ,
172+ CPUExtensions . armv8_3 => "armv8.3-a" ,
173+ CPUExtensions . armv8_4 => "armv8.4-a" ,
174+ CPUExtensions . armv8_5 => "armv8.5-a" ,
175+ CPUExtensions . armv8_6 => "armv8.6-a" ,
176+ CPUExtensions . armv8_7 => "armv8.7-a" ,
177+ CPUExtensions . armv8_8 => "armv8.8-a" ,
178+ CPUExtensions . armv8_9 => "armv8.9-a" ,
179+ CPUExtensions . armv9_0 => "armv9-a" ,
180+ CPUExtensions . armv9_1 => "armv9.1-a" ,
181+ CPUExtensions . armv9_2 => "armv9.2-a" ,
182+ CPUExtensions . armv9_3 => "armv9.3-a" ,
183+ CPUExtensions . armv9_4 => "armv9.4-a" ,
184+ _ => "native"
185+ } } " ) ;
186+ }
187+
188+ // Optimization level
189+ args . Add ( settings . Optimization switch
190+ {
191+ OptimizationLevel . None => "-O0" ,
192+ OptimizationLevel . Size => "-Os" ,
193+ OptimizationLevel . Speed => "-O2" ,
194+ OptimizationLevel . Max => "-O3" ,
195+ _ => "-O2"
196+ } ) ;
197+
198+ // Debug information - setup debug file names like MSVC does
199+ if ( settings . EnableDebugFileCreation )
200+ {
201+ args . Add ( "-g" ) ; // Generate debug information
202+
203+ // For GCC, debug info is embedded in the object file by default
204+ // But we can specify a separate debug file path if needed
205+ var debugFile = Path . ChangeExtension ( outputFile , ".debug" ) ;
206+ if ( ! string . IsNullOrEmpty ( Path . GetDirectoryName ( debugFile ) ) )
207+ {
208+ Directory . CreateDirectory ( Path . GetDirectoryName ( debugFile ) ! ) ;
209+ }
210+ }
211+
212+ // Preprocessor definitions
213+ foreach ( var def in settings . Definitions )
214+ {
215+ if ( def . HasValue ( ) )
216+ args . Add ( $ "-D{ def . GetName ( ) } ={ def . GetValue ( ) } ") ;
217+ else
218+ args . Add ( $ "-D{ def . GetName ( ) } ") ;
219+ }
220+
221+ // Include paths
222+ foreach ( var includePath in settings . IncludePaths )
223+ {
224+ args . Add ( "-I" ) ;
225+ args . Add ( includePath ) ;
226+ }
227+
228+ // Force includes
229+ foreach ( var forceInclude in settings . ForceIncludes )
230+ {
231+ args . Add ( "-include" ) ;
232+ args . Add ( forceInclude ) ;
233+ }
234+
235+ // Fast floating point operations
236+ if ( settings . EnableFastFloatingPointOperations )
237+ {
238+ args . Add ( "-ffast-math" ) ;
239+ }
240+
241+ // Position independent code for shared libraries (required on many platforms)
242+ if ( settings . ModuleType == ModuleType . SharedLibrary )
243+ {
244+ args . Add ( "-fPIC" ) ;
245+ }
246+
247+ // Additional custom flags
248+ args . AddRange ( settings . OtherFlags ) ;
249+
250+ // Source file (must be last)
251+ args . Add ( settings . SourceFile ) ;
252+
253+
254+ var startInfo = new ProcessStartInfo
255+ {
256+ FileName = compilerPath ,
257+ Arguments = args . ToString ( ) ,
258+ RedirectStandardOutput = true ,
259+ RedirectStandardError = true ,
260+ UseShellExecute = false ,
261+ CreateNoWindow = true ,
262+ StandardErrorEncoding = System . Text . Encoding . UTF8 ,
263+ StandardOutputEncoding = System . Text . Encoding . UTF8 ,
264+ WorkingDirectory = Path . GetDirectoryName ( settings . SourceFile ) ?? Environment . CurrentDirectory
265+ } ;
266+
267+ var process = new Process { StartInfo = startInfo } ;
268+ process . ErrorDataReceived += ( sender , e ) =>
269+ {
270+ if ( ! string . IsNullOrEmpty ( e . Data ) )
271+ {
272+ Console . Error . WriteLine ( e . Data ) ;
273+ }
274+ } ;
275+ process . OutputDataReceived += ( sender , e ) =>
276+ {
277+ if ( ! string . IsNullOrEmpty ( e . Data ) )
278+ {
279+ Console . Out . WriteLine ( e . Data ) ;
280+ }
281+ } ;
282+
283+ process . Start ( ) ;
284+ process . BeginErrorReadLine ( ) ;
285+ process . BeginOutputReadLine ( ) ;
286+ await process . WaitForExitAsync ( cancellationToken ) ;
287+ return process . ExitCode == 0 ;
288+ }
289+ }
290+ }
0 commit comments