int RealInvokeCompiler(IEnumerable <string> args, Action <string> onStdErr, Action <string> onStdOut, bool showIncludes, List <string> foundIncludes) { int rv = -1; bool retry; do { retry = false; Logging.Emit("invoking real compiler: {0} {1} [{2}]", CompilerExe, WorkingDirectory, string.Join(" ", args.ToArray())); if (string.IsNullOrWhiteSpace(CompilerExe) || !FileUtils.Exists(CompilerExe)) { throw new FileNotFoundException("cant find cl.exe"); } if (string.IsNullOrWhiteSpace(compworkdir)) { throw new InvalidOperationException("no working directory set"); } if (compenvs == null || compenvs.Count == 0) { throw new InvalidOperationException("no environment set"); } var cla = ArgumentUtils.JoinAguments(ArgumentUtils.FixupArgs(args)); var runExe = compilerExe; if (showIncludes) { foundIncludes.Add(SingleSourceFile); if (TrackerEnabled) { runExe = "tracker.exe"; var trackerargs = new List <string> { "/if", Path.Combine(WorkingDirectory, TrackerFolder), "/k", "/t" }; var tcla = ArgumentUtils.JoinAguments(trackerargs); cla = String.Format("{0} /c \"{1}\" {2}", tcla, compilerExe, cla); } else { cla += " /showIncludes"; } } var psi = new ProcessStartInfo(runExe, cla) { UseShellExecute = false, RedirectStandardError = true, RedirectStandardOutput = true, WorkingDirectory = compworkdir, }; psi.EnvironmentVariables.Clear(); foreach (var row in compenvs) { psi.EnvironmentVariables[row.Key] = row.Value; } psi.EnvironmentVariables["PATH"] = Path.GetDirectoryName(CompilerExe) + ";" + psi.EnvironmentVariables["PATH"]; psi.ErrorDialog = true; var p = Process.Start(psi); p.OutputDataReceived += (o, a) => { if (a.Data != null) { if (showIncludes && a.Data.StartsWith("Note: including file:")) { var inc = a.Data.Substring("Note: including file:".Length + 1).TrimStart(' '); if (inc.Contains('/')) { inc = inc.Replace('/', '\\'); } foundIncludes.Add(inc); } else { if (StdOutputCallback != null) { StdOutputCallback(a.Data + Environment.NewLine); } if (onStdOut != null) { onStdOut(a.Data + Environment.NewLine); } if (Settings.DebugEnabled) { Logging.Emit("stdout {0}", a.Data); } } } }; p.ErrorDataReceived += (o, a) => { if (a.Data != null) { if (StdErrorCallback != null) { StdErrorCallback(a.Data + Environment.NewLine); } if (onStdErr != null) { onStdErr(a.Data + Environment.NewLine); } if (Settings.DebugEnabled) { Logging.Emit("stderr {0}", a.Data); } } }; p.BeginErrorReadLine(); p.BeginOutputReadLine(); p.WaitForExit(); rv = p.ExitCode; p.Close(); Logging.Emit("cl exit {0}", rv); if (rv == 0) { if (IsSupported) { if (!string.IsNullOrEmpty(ObjectTarget)) { var sw = new Stopwatch(); sw.Start(); while (!File.Exists(ObjectTarget) && (sw.ElapsedMilliseconds < WaitForSlowObject)) { System.Threading.Thread.Sleep(500); } sw.Stop(); if (!File.Exists(ObjectTarget)) { retry = true; if (sw.ElapsedMilliseconds > 2000) { Logging.Emit("compiler didn't write expected object! {0} after {1}ms", ObjectTarget, (int)sw.Elapsed.TotalMilliseconds); retry = false; } string logmsg = string.Format("cl exited with zero but failed to create the expected object file! {0}", ObjectTarget); // let the retry system have a go with this if (retry) { Logging.Warning("{0}, re-running!", logmsg); } } else { Logging.Emit("output: {0} seen", ObjectTarget); } } } } if (rv != 0) { Logging.Emit("non-zero exit"); } } while (retry); return(rv); }
public bool ProcessArguments(string[] args) { try { DisableTracker(); CommandLine = args; for (int i = 0; i < args.Length; i++) { var opt = getOption(args[i]); var full = getFullOption(args[i]); #region switch process each argument type switch (opt) { case "/c": HasDashC = true; break; case "/o": return(NotSupported("/o")); case "/D": if (opt == full) { // define value is next argument... i++; } break; case "/I": if (opt == full) { // include path is next argument.. // microsoft really dont know how to do command line! i++; if (i > args.Length) { return(NotSupported("-I has no path!")); } full = "/I" + args[i]; goto default; } break; case "/Z7": GeneratePdb = false; PdbFile = null; break; case "/Yu": PrecompiledHeaders = true; return(NotSupported("pre-compiler headers {0}", opt)); case "/FI": return(NotSupported(opt)); case "/Zi": GeneratePdb = true; break; case "/Fd": PdbFile = Path.Combine(WorkingDirectory, full.Substring(3)); // openssl gives us a posix path here.. PdbFile = PdbFile.Replace('/', '\\'); if (!PdbFile.ToLower().EndsWith(".pdb") && !PdbFile.EndsWith("\\")) { PdbFile = PdbFile + ".pdb"; } break; case "/Fo": ObjectTarget = Path.Combine(WorkingDirectory, full.Substring(3)); if (ArgumentUtils.TargetIsFolder(ObjectTarget)) { ObjectTargetIsFolder = true; } else { ObjectTarget = ArgumentUtils.TargetObject(ObjectTarget); } break; case "/Tp": case "/Tc": var srcfile = ArgumentUtils.MakeWindowsPath(full.Substring(3)); if (!Path.IsPathRooted(srcfile)) { srcfile = Path.Combine(WorkingDirectory, srcfile); } if (FileUtils.Exists(srcfile)) { srcs.Add(srcfile); } else { return(NotSupported("cant find file for {0}", full)); } break; case "/E": return(NotSupported(opt)); case "/EP": return(NotSupported(opt)); default: #region positional or other flag options if (full == "/link") { Linking = true; return(NotSupported("/link")); } if (opt.StartsWith("@")) { #region response file ResponseFile = ArgumentUtils.MakeWindowsPath(full.Substring(1)); if (ResponseFile.EndsWith(InternalResponseFileSuffix)) { Logging.Emit("cclash misshelper internal response file"); return(false); } if (!Path.IsPathRooted(ResponseFile)) { ResponseFile = Path.Combine(WorkingDirectory, ResponseFile); } string rsptxt = File.ReadAllText(ResponseFile); if (rsptxt.Length < 2047) // windows max command line, this is why they invented response files { Logging.Emit("response data [{0}]", rsptxt); if (args.Length == 1) { // this only works if it is the one and only arg! args = ArgumentUtils.FixupArgs(CommandLineToArgs(rsptxt).Skip(1)).ToArray(); i = -1; // replace the command line with the response file content // and restart parsing. This does go wrong if the response text is huge continue; } } else { Logging.Emit("response file too large"); } return(NotSupported("response file error")); #endregion } if (!full.StartsWith("/")) { // NOTE, if we ever cache -link calls this will also match input objects and libs var file = ArgumentUtils.MakeWindowsPath(full); if (!Path.IsPathRooted(file)) { file = Path.Combine(WorkingDirectory, file); } if (FileUtils.Exists(file)) { srcs.Add(file); continue; } } if (full.StartsWith("/I")) { var d = ArgumentUtils.MakeWindowsPath(full.Substring(2)); if (d == ".") { d = WorkingDirectory; } if (d == "..") { d = Path.GetDirectoryName(WorkingDirectory); } if (!Path.IsPathRooted(d)) { d = Path.Combine(WorkingDirectory, d); } if (Directory.Exists(d)) { cliincs.Add(d); continue; } } #endregion break; } #endregion } if (SingleSource) { var defaultObj = ArgumentUtils.TargetObject(Path.GetFileNameWithoutExtension(SingleSourceFile)); if (ObjectTarget == null) { if (Path.IsPathRooted(defaultObj)) { ObjectTarget = defaultObj; } else { ObjectTarget = Path.Combine(WorkingDirectory, defaultObj); } } if (ObjectTargetIsFolder) { ObjectTarget = Path.Combine(ObjectTarget, defaultObj); } if (GeneratePdb) { if (Settings.ConvertObjPdbToZ7) { Logging.Emit("converting pdb request to Z7 embedded debug {0}:{1}", WorkingDirectory, Path.GetFileName(ObjectTarget)); // append /Z7 to the arg list and don't generate a pdb var newargs = new List <string>(); foreach (var a in args) { if (!(a.StartsWith("/Zi") || a.StartsWith("/Fd"))) { newargs.Add(a); } } newargs.Add("/Z7"); AttemptPdb = false; PdbFile = null; GeneratePdb = false; PdbExistsAlready = false; args = newargs.ToArray(); } } if (GeneratePdb) { return(NotSupported("PDB file requested")); } } } catch (Exception e) { Console.Error.WriteLine(e); return(NotSupported("option parser exception '{0}'", e)); } CompileArgs = args.ToArray(); return(IsSupported); }