/// <summary> /// Constructor /// </summary> /// <param name="PrevFragmentData">Timing data for the previous fragment</param> /// <param name="Fragment">The fragment to compile</param> /// <param name="CompilerExe">Path to the compiler</param> /// <param name="ResponseFile">Path to the compiler response file</param> /// <param name="LogFile">Path to the log file</param> public FragmentTimingData(string UniqueName, string Digest, FragmentTimingData PrevFragmentData, SourceFragment[] Fragments, List <Tuple <int, SourceFile> > IncludeHistory, FileReference IntermediateFile, CompileEnvironment CompileEnvironment) { this.UniqueName = UniqueName; this.Digest = Digest; this.PrevFragmentData = PrevFragmentData; this.Fragment = Fragments[Fragments.Length - 1]; this.IntermediateFile = IntermediateFile; this.CompileEnvironment = CompileEnvironment; // Write the source file using (StreamWriter Writer = new StreamWriter(IntermediateFile.FullName)) { int IncludeHistoryIdx = 0; List <SourceFile> IncludeStack = new List <SourceFile>(); for (int Idx = 0; Idx < Fragments.Length; Idx++) { SourceFragment Fragment = Fragments[Idx]; // Figure out which tag it's bound to int Tag = (Fragment.File.Counterpart == null)? Idx : -1; // Update the stack for new includes while (IncludeHistoryIdx < IncludeHistory.Count && IncludeHistory[IncludeHistoryIdx].Item1 == Idx) { if (IncludeHistory[IncludeHistoryIdx].Item2 == null) { SourceFile IncludeFile = IncludeStack[IncludeStack.Count - 1]; IncludeStack.RemoveAt(IncludeStack.Count - 1); Writer.WriteLine("{0}// END INCLUDE {1}", new string(' ', IncludeStack.Count * 4), IncludeFile.Location.FullName); IncludeHistoryIdx++; } else if (IncludeHistoryIdx + 1 < IncludeHistory.Count && IncludeHistory[IncludeHistoryIdx + 1].Item2 == null) { IncludeHistoryIdx += 2; } else { SourceFile IncludeFile = IncludeHistory[IncludeHistoryIdx].Item2; Writer.WriteLine("{0}// INCLUDE {1}", new string(' ', IncludeStack.Count * 4), IncludeFile.Location.FullName); IncludeStack.Add(IncludeFile); IncludeHistoryIdx++; } } // Get the text for this node Writer.WriteLine("{0}#include \"{1}\"", new string(' ', (IncludeStack.Count - 0) * 4), Fragment.Location.FullName); } } }
/// <summary> /// Gets include directories that the compiler knows about. /// </summary> public static void GetSystemIncludeDirectories(CompileEnvironment CompileEnv, DirectoryReference InputDir, List <DirectoryReference> ExtraSystemIncludePaths) { // FIXME: assumes clang now, and assumes certain hard-coded dirs. Can be improved by querying the compiler instead. DirectoryReference SysRootDir = CompileEnv.Options.Where(x => x.Name.StartsWith("--sysroot=")).Select(x => new DirectoryReference(x.Name.Substring(10))).First(); string CompilerFullPath = CompileEnv.Compiler.FullName; ExtraSystemIncludePaths.Add(DirectoryReference.Combine(SysRootDir, "usr", "include")); string CompilerVersionString = DetermineClangVersionDir(CompilerFullPath); if (!String.IsNullOrEmpty(CompilerVersionString)) { ExtraSystemIncludePaths.Add(DirectoryReference.Combine(SysRootDir, "lib", "clang", CompilerVersionString, "include")); } ExtraSystemIncludePaths.Add(DirectoryReference.Combine(InputDir, "Engine", "Source", "ThirdParty", "Linux", "LibCxx", "include", "c++", "v1")); }
/// <summary> /// Compiles the fragment and adds the compile time to the list /// </summary> public void Compile(DirectoryReference IntermediateDir, int SampleIdx) { FileReference LogFile = FileReference.Combine(IntermediateDir, String.Format("{0}.sample{1}.txt", UniqueName, SampleIdx + 1)); using (StreamWriter LogWriter = new StreamWriter(LogFile.FullName, true)) { FileReference ResponseFile = FileReference.Combine(IntermediateDir, String.Format("{0}.sample{1}.response", UniqueName, SampleIdx + 1)); // Write the response file CompileEnvironment WorkerCompileEnvironment = new CompileEnvironment(CompileEnvironment); if (WorkerCompileEnvironment.CompilerType == CompilerType.Clang) { WorkerCompileEnvironment.Options.Add(new CompileOption("-o", FileReference.Combine(IntermediateDir, UniqueName + ".o").FullName)); } else if (WorkerCompileEnvironment.CompilerType == CompilerType.VisualC) { WorkerCompileEnvironment.Options.RemoveAll(x => x.Name == "/Z7" || x.Name == "/Zi" || x.Name == "/ZI"); WorkerCompileEnvironment.Options.Add(new CompileOption("/Fo", IntermediateFile.FullName + ".obj")); WorkerCompileEnvironment.Options.Add(new CompileOption("/WX", null)); WorkerCompileEnvironment.Options.Add(new CompileOption("/Bt", null)); } else { throw new NotImplementedException(); } WorkerCompileEnvironment.WriteResponseFile(ResponseFile, IntermediateFile); // Spawn the compiler using (Process NewProcess = new Process()) { List <string> LogLines = new List <string>(); DataReceivedEventHandler OutputHandler = (x, y) => { if (!String.IsNullOrEmpty(y.Data)) { LogWriter.WriteLine(y.Data); LogLines.Add(y.Data); } }; NewProcess.StartInfo.FileName = CompileEnvironment.Compiler.FullName; NewProcess.StartInfo.Arguments = String.Format("\"@{0}\"", ResponseFile); NewProcess.StartInfo.UseShellExecute = false; NewProcess.StartInfo.RedirectStandardOutput = true; NewProcess.StartInfo.RedirectStandardError = true; NewProcess.OutputDataReceived += OutputHandler; NewProcess.ErrorDataReceived += OutputHandler; Stopwatch Timer = Stopwatch.StartNew(); NewProcess.Start(); NewProcess.BeginOutputReadLine(); NewProcess.BeginErrorReadLine(); if (NewProcess.WaitForExit(10 * 60 * 1000)) { // WaitForExit with a timeout does not wait for output data to be flushed, so issue a normal WaitForExit call here too NewProcess.WaitForExit(); Timer.Stop(); FragmentTimingSample Sample = new FragmentTimingSample(); Sample.TotalTime = Timer.Elapsed.TotalSeconds; if (WorkerCompileEnvironment.CompilerType == CompilerType.VisualC) { foreach (string LogLine in LogLines) { if (TryParseCompileTime(LogLine, "c1xx.dll", out Sample.FrontendTime)) { break; } } foreach (string LogLine in LogLines) { if (TryParseCompileTime(LogLine, "c2.dll", out Sample.BackendTime)) { break; } } } Samples.Add(Sample); LogWriter.WriteLine("Compile finished in {0}s", Timer.Elapsed.TotalSeconds); } else { NewProcess.Kill(); NewProcess.WaitForExit(); Timer.Stop(); Samples.Add(new FragmentTimingSample()); LogWriter.WriteLine("Timeout; terminating process after {0}s", Timer.Elapsed.TotalSeconds); } LogWriter.WriteLine(); } } }