public static BuildResultCode Build(BuilderOptions options) { BuildResultCode result; if (options.IsValidForSlave()) { // Sleeps one second so that debugger can attach //Thread.Sleep(1000); result = BuildSlave(options); } else if (options.IsValidForMaster()) { result = BuildLocal(options); if (!string.IsNullOrWhiteSpace(options.OutputDirectory) && options.BuilderMode == Builder.Mode.Build) { CopyBuildToOutput(options); } } else { throw new OptionException("Insufficient parameters, no action taken", "build-path"); } return(result); }
public static BuildResultCode BuildLocal(BuilderOptions options) { string inputFile = options.InputFiles[0]; string sdkDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "../../.."); BuildScript buildScript = BuildScript.LoadFromFile(sdkDir, inputFile); buildScript.Compile(options.Plugins); if (buildScript.GetWarnings().FirstOrDefault() != null) { foreach (string warning in buildScript.GetWarnings()) { options.Logger.Warning(warning); } } if (buildScript.HasErrors) { foreach (string error in buildScript.GetErrors()) { options.Logger.Error(error); } throw new InvalidOperationException("Can't compile the provided build script."); } string inputDir = Path.GetDirectoryName(inputFile) ?? Environment.CurrentDirectory; options.SourceBaseDirectory = options.SourceBaseDirectory ?? Path.Combine(inputDir, buildScript.SourceBaseDirectory ?? ""); options.BuildDirectory = options.BuildDirectory ?? Path.Combine(inputDir, buildScript.BuildDirectory ?? ""); options.OutputDirectory = options.OutputDirectory ?? (buildScript.OutputDirectory != null ? Path.Combine(inputDir, buildScript.OutputDirectory) : ""); options.MetadataDatabaseDirectory = options.MetadataDatabaseDirectory ?? (buildScript.MetadataDatabaseDirectory != null ? Path.Combine(inputDir, buildScript.MetadataDatabaseDirectory) : ""); if (!string.IsNullOrWhiteSpace(options.SourceBaseDirectory)) { if (!Directory.Exists(options.SourceBaseDirectory)) { string error = string.Format("Source base directory \"{0}\" does not exists.", options.SourceBaseDirectory); options.Logger.Error(error); throw new OptionException(error, "sourcebase"); } Environment.CurrentDirectory = options.SourceBaseDirectory; } if (string.IsNullOrWhiteSpace(options.BuildDirectory)) { throw new OptionException("This tool requires a build path.", "build-path"); } // Mount build path ((FileSystemProvider)VirtualFileSystem.ApplicationData).ChangeBasePath(options.BuildDirectory); options.ValidateOptionsForMaster(); // assets is always added by default //options.Databases.Add(new DatabaseMountInfo("/assets")); PrepareDatabases(options); try { VirtualFileSystem.CreateDirectory("/data/"); VirtualFileSystem.CreateDirectory("/data/db/"); } catch (Exception) { throw new OptionException("Invalid Build database path", "database"); } // Create builder LogMessageType logLevel = options.Debug ? LogMessageType.Debug : (options.Verbose ? LogMessageType.Verbose : LogMessageType.Info); var logger = Logger.GetLogger("builder"); logger.ActivateLog(logLevel); var builder = new Builder("builder", options.BuildDirectory, options.BuilderMode, logger) { ThreadCount = options.ThreadCount }; builder.MonitorPipeNames.AddRange(options.MonitorPipeNames); builder.ActivateConfiguration(options.Configuration); foreach (var sourceFolder in buildScript.SourceFolders) { builder.InitialVariables.Add(("SourceFolder:" + sourceFolder.Key).ToUpperInvariant(), sourceFolder.Value); } Console.CancelKeyPress += (sender, e) => Cancel(builder, e); buildScript.Execute(builder); // Run builder return(builder.Run(options.Append == false)); }
private static void PrepareDatabases(BuilderOptions options) { AssetManager.GetDatabaseFileProvider = () => IndexFileCommand.DatabaseFileProvider.Value; }
public static void CopyBuildToOutput(BuilderOptions options) { throw new InvalidOperationException(); }
public static BuildResultCode BuildSlave(BuilderOptions options) { // Mount build path ((FileSystemProvider)VirtualFileSystem.ApplicationData).ChangeBasePath(options.BuildDirectory); PrepareDatabases(options); try { VirtualFileSystem.CreateDirectory("/data/"); VirtualFileSystem.CreateDirectory("/data/db/"); } catch (Exception) { throw new OptionException("Invalid Build database path", "database"); } // Open WCF channel with master builder var namedPipeBinding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None) { SendTimeout = TimeSpan.FromSeconds(300.0) }; var processBuilderRemote = ChannelFactory <IProcessBuilderRemote> .CreateChannel(namedPipeBinding, new EndpointAddress(options.SlavePipe)); try { RegisterRemoteLogger(processBuilderRemote); // Create scheduler var scheduler = new Scheduler(); var status = ResultStatus.NotProcessed; // Schedule command string buildPath = options.BuildDirectory; Logger logger = options.Logger; MicroThread microthread = scheduler.Add(async() => { // Deserialize command and parameters Command command = processBuilderRemote.GetCommandToExecute(); BuildParameterCollection parameters = processBuilderRemote.GetBuildParameters(); // Run command var inputHashes = new DictionaryStore <InputVersionKey, ObjectId>(VirtualFileSystem.OpenStream("/data/db/InputHashes", VirtualFileMode.OpenOrCreate, VirtualFileAccess.ReadWrite, VirtualFileShare.ReadWrite)); var builderContext = new BuilderContext(buildPath, inputHashes, parameters, 0, null); var commandContext = new RemoteCommandContext(processBuilderRemote, command, builderContext, logger); command.PreCommand(commandContext); status = await command.DoCommand(commandContext); command.PostCommand(commandContext, status); // Returns result to master builder processBuilderRemote.RegisterResult(commandContext.ResultEntry); }); while (true) { scheduler.Run(); // Exit loop if no more micro threads lock (scheduler.MicroThreads) { if (!scheduler.MicroThreads.Any()) { break; } } Thread.Sleep(0); } // Rethrow any exception that happened in microthread if (microthread.Exception != null) { options.Logger.Fatal(microthread.Exception.ToString()); return(BuildResultCode.BuildError); } if (status == ResultStatus.Successful || status == ResultStatus.NotTriggeredWasSuccessful) { return(BuildResultCode.Successful); } return(BuildResultCode.BuildError); } finally { // Close WCF channel // ReSharper disable SuspiciousTypeConversion.Global ((IClientChannel)processBuilderRemote).Close(); // ReSharper restore SuspiciousTypeConversion.Global } }
private static int Main(string[] args) { var exeName = Path.GetFileName(Assembly.GetExecutingAssembly().Location); var showHelp = false; var options = new BuilderOptions(Logger.GetLogger("BuildEngine")); var p = new OptionSet { "Copyright (c) Stride contributors (https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) All Rights Reserved", "Stride Build Tool - Version: " + String.Format( "{0}.{1}.{2}", typeof(Program).Assembly.GetName().Version.Major, typeof(Program).Assembly.GetName().Version.Minor, typeof(Program).Assembly.GetName().Version.Build) + string.Empty, string.Format("Usage: {0} [options]* inputfile -o outputfile", exeName), string.Empty, "=== Options ===", string.Empty, { "h|help", "Show this message and exit", v => showHelp = v != null }, { "v|verbose", "Show more verbose progress logs", v => options.Verbose = v != null }, { "d|debug", "Show debug logs (imply verbose)", v => options.Debug = v != null }, { "c|clean", "Clean the command cache, forcing to rebuild everything at the next build.", v => options.BuilderMode = Builder.Mode.Clean }, { "cd|clean-delete", "Clean the command cache and delete output objects", v => options.BuilderMode = Builder.Mode.CleanAndDelete }, { "b|build-path=", "Build path", v => options.BuildDirectory = v }, { "mdb|metadata-database=", "Optional ; indicate the directory containing the Metadata database, if used.", v => { if (!string.IsNullOrEmpty(v)) { options.MetadataDatabaseDirectory = v; } } }, { "o|output-path=", "Optional ; indicate an output path to copy the built assets in.", v => options.OutputDirectory = v }, { "cfg|config=", "Configuration name", v => options.Configuration = v }, { "log", "Enable file logging", v => options.EnableFileLogging = v != null }, { "log-file=", "Log build in a custom file.", v => { options.EnableFileLogging = v != null; options.CustomLogFileName = v; } }, { "monitor-pipe=", "Monitor pipe.", v => { if (!string.IsNullOrEmpty(v)) { options.MonitorPipeNames.Add(v); } } }, { "slave=", "Slave pipe", v => options.SlavePipe = v }, // Benlitz: I don't think this should be documented { "s|sourcebase=", "Optional ; Set the base directory for the source files. Not required if all source paths are absolute", v => options.SourceBaseDirectory = v }, { "a|append", "If set, the existing asset mappings won't be deleted.", v => options.Append = v != null }, { "t|threads=", "Number of threads to create. Default value is the number of hardware threads available.", v => options.ThreadCount = int.Parse(v) }, { "p|plugin=", "Add plugin directory.", v => { if (!string.IsNullOrEmpty(v)) { options.Plugins.AddPluginFolder(v); } } }, { "test=", "Run a test session.", v => options.TestName = v } }; TextWriterLogListener fileLogListener = null; // Output logs to the console with colored messages if (options.SlavePipe == null) { var consoleLogListener = new ConsoleLogListener { TextFormatter = FormatLog }; GlobalLogger.MessageLogged += consoleLogListener; } // Setting up plugin manager options.Plugins.AddPluginFolder(Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) ?? "", "BuildPlugins")); options.Plugins.Register(); BuildResultCode exitCode; try { options.InputFiles = p.Parse(args); // Also write logs from master process into a file if (options.SlavePipe == null) { if (options.EnableFileLogging) { string logFileName = options.CustomLogFileName; if (string.IsNullOrEmpty(logFileName)) { string inputName = "NoInput"; if (options.InputFiles.Count > 0) { inputName = Path.GetFileNameWithoutExtension(options.InputFiles[0]); } logFileName = "Logs/Build-" + inputName + "-" + DateTime.Now.ToString("yy-MM-dd-HH-mm") + ".txt"; } string dirName = Path.GetDirectoryName(logFileName); if (dirName != null) { Directory.CreateDirectory(dirName); } fileLogListener = new TextWriterLogListener(new FileStream(logFileName, FileMode.Create)) { TextFormatter = FormatLog }; GlobalLogger.MessageLogged += fileLogListener; } options.Logger.Info("BuildEngine arguments: " + string.Join(" ", args)); options.Logger.Info("Starting builder."); } if (showHelp) { p.WriteOptionDescriptions(Console.Out); exitCode = BuildResultCode.Successful; } else if (!string.IsNullOrEmpty(options.TestName)) { var test = new TestSession(); test.RunTest(options.TestName, options.Logger); exitCode = BuildResultCode.Successful; } else { exitCode = BuildEngineCommands.Build(options); } } catch (OptionException e) { options.Logger.Error("{0}", e); exitCode = BuildResultCode.CommandLineError; } catch (Exception e) { options.Logger.Error("{0}", e); exitCode = BuildResultCode.BuildError; } finally { if (fileLogListener != null) { fileLogListener.LogWriter.Close(); } } return((int)exitCode); }