in Sharpmake.Application/Program.cs [149:387]
private static int Main()
{
if (CommandLine.ContainParameter("breakintodebugger"))
{
Console.WriteLine("Debugger requested. Please attach a debugger and press ENTER to continue");
while (Console.ReadKey(true).Key != ConsoleKey.Enter)
{
Console.WriteLine("Press ENTER to continue");
}
Debugger.Break();
}
// This GC gives a little bit better results than the other ones. "LowLatency" is giving really bad results(twice slower than the other ones).
System.Runtime.GCSettings.LatencyMode = System.Runtime.GCLatencyMode.SustainedLowLatency;
Trace.Assert(System.Runtime.GCSettings.IsServerGC, "Server GC is not active! Sharpmake will be much slower!");
AppDomain currentDomain = AppDomain.CurrentDomain;
Mutex oneInstanceMutex = null;
Argument parameters = new Argument();
ExitCode exitCode = ExitCode.Success;
try
{
DebugEnable = CommandLine.ContainParameter("verbose") || CommandLine.ContainParameter("debug") || CommandLine.ContainParameter("diagnostics");
GetAssemblyInfo(Assembly.GetExecutingAssembly(), out var _, out var version, out var versionString, out var _);
LogWriteLine($"sharpmake {versionString}");
LogWriteLine(" arguments: {0}", CommandLine.GetProgramCommandLine());
LogWriteLine(" directory: {0}", Directory.GetCurrentDirectory());
LogWriteLine(" platform: {0} - {1}", Util.GetExecutingPlatform().ToString(), RuntimeInformation.OSDescription);
LogWriteLine(" compiled with framework: {0}", Util.FrameworkDisplayName());
LogWriteLine(" running on framework: {0}", RuntimeInformation.FrameworkDescription);
LogWriteLine(string.Empty);
// display help if wanted and quit
if ((CommandLine.GetProgramCommandLine().Length == 0) || CommandLine.ContainParameter("help"))
{
LogWriteLine(CommandLine.GetCommandLineHelp(typeof(Argument), false));
return CommandLine.ContainParameter("help") ? (int)ExitCode.Success : (int)ExitCode.Error;
}
AppDomain.CurrentDomain.AssemblyLoad += AppDomain_AssemblyLoad;
// Log warnings and errors from builder
Assembler.EventOutputError += ErrorWrite;
Assembler.EventOutputWarning += WarningWrite;
CommandLine.ExecuteOnObject(parameters);
if (parameters.Exit)
return (int)ExitCode.Success;
const string sharpmakeSymbolPrefix = "_SHARPMAKE";
List<string> invalidSymbols = parameters.Defines.Where(define => define.StartsWith(sharpmakeSymbolPrefix)).ToList();
if (invalidSymbols.Any())
{
string invalidSymbolsString = string.Join(", ", invalidSymbols);
throw new Error($"Only Sharpmake process can define symbols starting with {sharpmakeSymbolPrefix}. Invalid symbols defined: {invalidSymbolsString}");
}
parameters.Defines.Add(sharpmakeSymbolPrefix); // A generic sharpmake define to allow scripts to exclude part of code if not used with sharpmake
parameters.Defines.Add($"{sharpmakeSymbolPrefix}_{version.Major}_{version.Minor}_X");
parameters.Defines.Add($"{sharpmakeSymbolPrefix}_{version.Major}_{version.Minor}_{version.Build}");
parameters.Validate();
// CommonPlatforms.dll is always loaded by default because those are shipped with
// the Sharpmake package.
PlatformRegistry.RegisterExtensionAssembly(typeof(Windows.Win32Platform).Assembly);
// If any platform declares its own command line options, execute and validate
// them as well.
IEnumerable<Platform> platformsCmdLines = PlatformRegistry.GetAvailablePlatforms<ICommandLineInterface>();
foreach (var platform in platformsCmdLines)
{
var platformCmdLine = PlatformRegistry.Get<ICommandLineInterface>(platform);
CommandLine.ExecuteOnObject(platformCmdLine);
platformCmdLine.Validate();
}
bool oneInstanceMutexCreated;
string mutexName = string.Format("SharpmakeSingleInstanceMutex{0}", parameters.MutexSuffix); // Allow custom mutex name suffix. Useful to debug concurrently multiple sharpmake running from different branches
oneInstanceMutex = new Mutex(true, mutexName, out oneInstanceMutexCreated);
if (!oneInstanceMutexCreated)
{
try
{
if (!oneInstanceMutex.WaitOne(0))
{
LogWriteLine("wait for another instance(s) of sharpmake to terminate...");
oneInstanceMutex.WaitOne();
}
}
catch (AbandonedMutexException)
{
// This occurs if another sharpmake is killed in the debugger
}
finally
{
LogWriteLine("waiting done.");
}
}
if (parameters.RegexMatchCacheEnabled)
{
GlobalRegexMatchCache.Init(parameters.RegexMatchCacheInitialCapacity);
}
switch (parameters.TestOption)
{
case TestOptions.Regression:
{
var regressionTest = new BuildContext.RegressionTest(parameters.OutputDirectory, parameters.ReferenceDirectory, parameters.RemapRoot);
GenerateAll(regressionTest, parameters);
exitCode = ExitCode.Success;
var regressions = regressionTest.GetRegressions().ToList();
if (regressions.Count > 0)
{
exitCode = ExitCode.Error;
DebugWriteLine($"{regressions.Count} Regressions detected:");
List<BuildContext.RegressionTest.OutputInfo> fileChanges = regressions.Where(x => x.FileStatus == BuildContext.RegressionTest.FileStatus.Different).ToList();
LogFileChanges(fileChanges, parameters.RegressionDiff);
var fileMissing = regressions.Where(x => x.FileStatus == BuildContext.RegressionTest.FileStatus.NotGenerated).Select(x => x.ReferencePath).ToList();
if (fileMissing.Count > 0)
{
fileMissing.Sort();
DebugWriteLine($" {fileMissing.Count} files are missing from the output:");
fileMissing.ForEach(x => DebugWriteLine($" {x}"));
}
}
}
break;
case TestOptions.QuickConfigure:
{
exitCode = AnalyzeConfigureOrder(parameters, true);
}
break;
case TestOptions.Configure:
{
exitCode = AnalyzeConfigureOrder(parameters, false);
}
break;
case TestOptions.None:
default:
{
if (parameters.OutputDirectory != null)
{
// output redirect mode
var redirectOutput = new BuildContext.RedirectOutput(parameters.OutputDirectory, parameters.RemapRoot);
GenerateAll(redirectOutput, parameters);
exitCode = ExitCode.Success;
}
else
{
var generateAll = new BuildContext.GenerateAll(parameters.DebugLog, parameters.WriteFiles);
GenerateAll(generateAll, parameters);
exitCode = ExitCode.Success;
Util.ExecuteFilesAutoCleanup();
}
}
break;
}
if (CSproj.AllCsProjSubTypesInfos.Any())
Util.SerializeAllCsprojSubTypes(CSproj.AllCsProjSubTypesInfos);
if (parameters.RegexMatchCacheEnabled)
{
int regexMatchCacheInitialCapacity = parameters.RegexMatchCacheInitialCapacity;
int regexMatchCacheSize = GlobalRegexMatchCache.Count;
if (regexMatchCacheInitialCapacity < regexMatchCacheSize)
{
WarningWriteLine("Warning (perf): Consider increasing regex match cache initial capacity from {0} to at least {1} ( /regexMatchCacheInitialCapacity({1}) ).", regexMatchCacheInitialCapacity, regexMatchCacheSize);
}
GlobalRegexMatchCache.UnInit();
}
}
catch (Error e)
{
// Log error message
Exception innerException = e;
while (innerException.InnerException != null)
innerException = innerException.InnerException;
ErrorWriteLine(Environment.NewLine + "Error:" + Environment.NewLine + innerException.Message);
// Then log details
LogWriteLine(Util.GetCompleteExceptionMessage(e, "\t"));
exitCode = ExitCode.Error;
}
catch (InternalError e)
{
ErrorWriteLine(Environment.NewLine + "Internal Error:");
LogWriteLine(Util.GetCompleteExceptionMessage(e, "\t"));
exitCode = ExitCode.InternalError;
}
catch (Exception e)
{
LogWriteLine(Environment.NewLine + "Exception Error:");
LogWriteLine(Util.GetCompleteExceptionMessage(e, "\t"));
exitCode = ExitCode.UnknownError;
}
finally
{
if (oneInstanceMutex != null)
{
oneInstanceMutex.ReleaseMutex();
GC.KeepAlive(oneInstanceMutex);
}
if (parameters.Debug)
{
Console.WriteLine("DEBUG Sharpmake.Application: Press any key to exit...");
Console.ReadKey();
}
}
if (exitCode <= ExitCode.Error) // Do not display summary in case of unknown exception or internal error
LogWriteLine(@"{0} errors, {1} warnings", s_errorCount, s_warningCount);
if (s_errorCount != 0)
{
if (Debugger.IsAttached)
{
LogWriteLine("Please look at the errors.");
Debugger.Break();
}
}
// Always return the same error code no matter the number of errors.
if (exitCode == ExitCode.Success && s_errorCount != 0)
{
exitCode = ExitCode.GenerationError;
}
return (int)exitCode;
}