Files
gtav-src/tools_ng/etc/Oozybuild/sga_prebuild_release.csScript
2025-09-29 00:52:08 +02:00

1263 lines
42 KiB
Plaintext
Executable File

//reference * will cause the dll created from this script to inherit all references from the parent app
using OozyBuild; /// reference *
using Microsoft.Win32;
using P4API;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Controls;
using System.Xml.Serialization;
namespace sga_prebuild_release
{
/******************************************************************************************************************
**
******************************************************************************************************************/
public static class ProjectExtension
{
public static SgaPrebuildSettings Settings( this OozyBuild.Project project )
{
return project.customSettings as SgaPrebuildSettings;
}
}
/******************************************************************************************************************
**
******************************************************************************************************************/
public class SgaPrebuildSettings : OozyBuild.CustomSettings
{
public SgaPrebuildSettings()
{
//Defaults. These values are persisted, and so will only be set as default on new projects, or if settings are wiped
visualStudioVersion = "2019";
buildBreakers = new SerializableDictionary<Guid, List<BuildBreaker>>();
packageDir = "";
packageLimit = 5;
transferDir = @"";
logDir = @"tools_ng\script\util\BuildDept\GTA5_GEN9_BuildTool_V2\BuildTool\CI_Logs";
lastCIChangelist = -1;
obscureSlackMessages = true;
lastResult = new CIResult();
lastNightbuildResult = new CIResult();
branchStatus = new SerializableDictionary<string, BranchStatus>();
}
public void Setup( Project inProject )
{
project = inProject;
if( Oozy.continuous )
{
sendEmail = false;
}
if( string.IsNullOrEmpty( Project.IncredibuildExe ) || !File.Exists( Project.IncredibuildExe ) )
{
incredibuild_IsEnabled = false;
}
else
{
incredibuild_IsEnabled = true;
}
}
public void EnableButton( Button button, bool enable )
{
if( button == null )
{
return;
}
if( System.Windows.Application.Current != null )
{
System.Windows.Application.Current.Dispatcher.BeginInvoke( (Action)(() =>
{
button.IsEnabled = enable;
}) );
}
}
/******************************************************************************************************************
** SPECIAL VARIABLES which are used internally if they exist
******************************************************************************************************************/
public bool playBoredMessage { get; set; }
//Don't make public, this should be un-editable. If undefined, the newest version found will be used.
string visualStudioVersion { get; set; }
[PropertyToolTip( "Nightbuild" )]
public bool nightBuild { get; set; }
[PropertyToolTip( "Log dir" )]
public string logDir { get; set; }
[PropertyToolTip("Upload symbols")]
public bool uploadSymbols { get; set; }
[PropertyToolTip( "Create Package" )]
public bool createPackage { get; set; }
[PropertyToolTip( "Package dir" )]
public string packageDir { get; set; }
[PropertyToolTip( "Transfer Package" )]
public bool transferPackage { get; set; }
[PropertyToolTip( "Transfer dir" )]
public string transferDir { get; set; }
[PropertyToolTip( "Keep this many packages in Package Dir. Last nightbuild is always kept" )]
[XmlIgnore]
public int packageLimit { get; set; }
[ContinuousMode]
[PropertyToolTip( "Requires slack.ini containing URL=<url>" )]
public bool postSlackMessages { get; set; }
[ContinuousMode]
[PropertyToolTip( "Don't put identifiable information in the slack messages" )]
public bool obscureSlackMessages { get; set; }
[ContinuousMode]
[PropertyToolTip( "Send email on CI or package" )]
public bool sendEmail { get; set; }
[ContinuousMode]
[PropertyToolTip( "Submit scc outputs defined in NOM to p4" )]
public bool submitP4 { get; set; }
/******************************************************************************************************************
** END OF SPECIAL VARIABLES
******************************************************************************************************************/
[ContinuousMode]
public bool createLabels { get; set; }
public int lastCIChangelist { get; set; }
public SerializableDictionary<Guid, List<BuildBreaker>> buildBreakers { get; set; }
public int filesSyncedToday { get; set; }
public SerializableDictionary<string, BranchStatus> branchStatus { get; set; }
public SerializableDictionary<Guid, BuiltInfo> lastBuildStatus;
public bool incredibuild { get; set; }
bool incredibuild_IsEnabled { get; set; }
public CIResult lastResult { get; set; }
public CIResult lastNightbuildResult { get; set; }
[XmlIgnore]
public Project project;
[XmlIgnore]
public BuildExtension extension;
}
public class FileToMerge
{
public string source;
public string dest;
}
/******************************************************************************************************************
**
******************************************************************************************************************/
public class BuildExtension : GTA5Extension
{
string packageUser;
bool buildOnlyIfSynced;
//Dictionary of branches and whether changelist/bugstar information needs to be stored, along with p4 paths relevant to building
protected override Dictionary<string, BranchSyncInfo> branchPaths { get; set; }
/******************************************************************************************************************
**
******************************************************************************************************************/
public static BuildExtension Init( Project inProject )
{
return new BuildExtension( inProject );
}
/******************************************************************************************************************
**
******************************************************************************************************************/
public BuildExtension( Project inProject )
{
project = inProject;
SgaPrebuildSettings settings = new SgaPrebuildSettings();
settings.Setup( inProject );
settings.extension = this;
branchPaths = new Dictionary<string, BranchSyncInfo>()
{
{ "dev_gen9_sga", new BranchSyncInfo() { storeChanges = true, syncPaths = new List<string>()
{
@"gta5\build\dev_gen9_sga\...",
@"gta5\titleupdate\dev_gen9_sga\...",
@"gta5\src\dev_gen9_sga\...",
@"gta5\script\dev_gen9_sga\...",
@"gta5\assets_gen9_sga\GameText\dev_gen9_sga\...",
@"gta5\assets_gen9_sga\metadata\...",
@"gta5\assets_gen9_sga\titleupdate\dev_gen9_sga\...",
@"gta5\assets_gen9_sga\maps\ParentTxds.xml",
@"gta5\build\disk_images\...",
}
}
},
};
//Setting customSettings will serialize in persisted data
project.customSettings = settings;
//ensure any new or un-persisted branch infos are setup to save having null checks all over the place
if( settings.branchStatus == null )
{
settings.branchStatus = new SerializableDictionary<string, BranchStatus>();
}
foreach( var pair in branchPaths )
{
if( !settings.branchStatus.ContainsKey( pair.Key ) )
{
settings.branchStatus[pair.Key] = new BranchStatus();
}
}
project.OnContinuous += OnContinuous;
project.OnNightbuild += OnNightbuild;
project.AddLog( "sga_prebuild_release loaded" );
}
object PackageBuild( QueueEntry queueEntry )
{
SgaPrebuildSettings settings = project.Settings();
DateTime start = DateTime.Now;
project.builder.buildStartingTime = start;
string logPath = project.builder.GetBuilderLogPath( start );
Program.PushLogPath( logPath );
package = true;
packageUser = queueEntry.testingUser;
string msg = "";
if( !string.IsNullOrEmpty( packageUser ) )
{
msg += DateTime.Now.ToString( "dd/MM/yyyy HH:mm" ) + ": PackageBuild started for " + packageUser + "\n";
}
else
{
msg += DateTime.Now.ToString( "dd/MM/yyyy HH:mm" ) + ": PackageBuild started\n";
}
string silentStr = queueEntry.values["silent"];
bool silent = string.Compare( silentStr, "true", true ) == 0;
bool oldPost = settings.postSlackMessages;
bool oldSubmit = settings.submitP4;
bool oldCreateLabel = settings.createLabels;
bool oldSendEmail = settings.sendEmail;
if( silent )
{
settings.postSlackMessages = false;
settings.submitP4 = false;
settings.createLabels = false;
settings.sendEmail = false;
}
BuildParams buildParams = new BuildParams();
buildParams.rebuild = false;
buildParams.flags = BuildTypeFlags.Packaging;
msg += "Options:\n";
foreach( var pair in queueEntry.values )
{
if( string.IsNullOrEmpty( pair.Value ) )
{
msg += pair.Key + "\n";
}
else
{
msg += pair.Key + " = " + pair.Value + "\n";
}
buildParams[pair.Key] = pair.Value;
}
if( buildParams.ContainsKey( "installer" ) )
{
buildParams["installer"] = "-platformpackage";
}
if( buildParams.ContainsKey( "skipinitial" ) )
{
buildParams["skipinitial"] = "-skipinitpackage";
}
project.StartBuildThread( msg );
project.AddLog( msg );
VariableParser.GetValueDelegate getEnv = ( string var, List<string> values ) =>
{
string val;
if( buildParams.TryGetValue( var, out val ) )
{
values.Add( val );
}
};
VariableParser.GetValue += getEnv;
CIResult result = null;
try
{
int retry = 10;
while( result == null && retry > 0 )
{
result = DoBuild( project.builder, buildParams );
if( result == null )
{
project.AddError( "PackageBuild failed with something more than likely p4 related, waiting to try again" );
Thread.Sleep( 10 * 1000 );
}
}
}
catch( Exception ex )
{
project.AddError( "PackageBuild exception" );
project.PostBuildError( App.LogException( ex ) );
}
VariableParser.GetValue -= getEnv;
if( silent )
{
settings.postSlackMessages = oldPost;
settings.submitP4 = oldSubmit;
settings.createLabels = oldCreateLabel;
settings.sendEmail = oldSendEmail;
}
packageUser = "";
package = false;
bool ret = result != null && result.ranNormally;
project.PostBuildMessage( "Package Complete." );
if( ret )
{
project.StopBuildThreadSuccess();
}
else
{
project.StopBuildThreadError();
}
Program.PopLogPath();
return ret;
}
int Execute( out string outLog, string dir, string command, string args )
{
project.AddLog( "[" + dir + "] " + command + " " + args );
int ret = -1;
try
{
BuildCommand symbol = new BuildCommand( dir, command, args );
Task<int> th = project.builder.Execute( symbol );
th.Wait();
ret = th.Result;
BuildLog log = symbol.log;
StringBuilder buildLog = new StringBuilder( 4096 );
foreach( var l in log.Log() )
{
switch( l.level )
{
case OutputLevel.StdOut:
project.AddLog( l.info );
break;
case OutputLevel.StdErr:
project.AddError( l.info );
break;
case OutputLevel.Info:
case OutputLevel.Warning:
project.AddWarning( l.info );
break;
}
Program.Log( l.info );
buildLog.Append( l.info + "\n" );
}
outLog = buildLog.ToString();
}
catch( Exception ex )
{
ret = -1;
outLog = ex.Message;
}
return ret;
}
static string symbolServer = @"\\rsgdndnas1.rockstar.t2.corp\rsgdnd_symbolserver$\";
int UploadProsperoSymbols( string projectName, int changelist, string branch, out string log )
{
int ret = Execute( out log, project.builder.workingDirectory,
Path.Combine( Environment.ExpandEnvironmentVariables( "%SCE_PROSPERO_SDK_DIR%" ), @"host_tools\bin\prospero-symupload.exe" ),
@"add /f " + Path.Combine( project.builder.workingDirectory, @"titleupdate\" + branch + @"\" ) + @"*.elf /s " + symbolServer + " /compress /tag " + changelist + " /o" );
if( ret == 0 )
{
Program.Log( "Prospero symbols uploaded" );
}
else
{
Program.Log( "Prospero symbols failed" );
}
return ret;
}
int UploadScarlettSymbols( string projectName, int changelist, string branch, out string log )
{
int ret = Execute( out log, project.builder.workingDirectory,
Path.Combine( Environment.ExpandEnvironmentVariables( "%ProgramFiles(x86)%" ), @"Windows Kits\10\Debuggers\x64\symstore.exe" ),
@"add /f " + Path.Combine( project.builder.workingDirectory, @"titleupdate\" + branch + @"\" ) + @"game_scarlett*.exe /s " + symbolServer + " /compress /t " + projectName + " /v " + changelist + " /o" );
if( ret == 0 )
{
ret = Execute( out log, project.builder.workingDirectory,
Path.Combine( Environment.ExpandEnvironmentVariables( "%ProgramFiles(x86)%" ), @"Windows Kits\10\Debuggers\x64\symstore.exe" ),
@"add /f " + Path.Combine( project.builder.workingDirectory, @"titleupdate\" + branch + @"\" ) + @"game_scarlett*.pdb /s " + symbolServer + " /compress /t " + projectName + " /v " + changelist + " /o" );
}
if( ret == 0 )
{
Program.Log( "Scarlett symbols uploaded" );
}
else
{
Program.Log( "Scarlett symbols failed" );
}
return ret;
}
int UploadWin64Symbols( string projectName, int changelist, string branch, out string log )
{
int ret = Execute( out log, project.builder.workingDirectory,
Path.Combine( Environment.ExpandEnvironmentVariables( "%ProgramFiles(x86)%" ), @"Windows Kits\10\Debuggers\x64\symstore.exe" ),
@"add /f " + Path.Combine( project.builder.workingDirectory, @"titleupdate\" + branch + @"\" ) + @"game_win64*.exe /s " + symbolServer + " /compress /t " + projectName + " /v " + changelist + " /o" );
if( ret == 0 )
{
ret = Execute( out log, project.builder.workingDirectory,
Path.Combine( Environment.ExpandEnvironmentVariables( "%ProgramFiles(x86)%" ), @"Windows Kits\10\Debuggers\x64\symstore.exe" ),
@"add /f " + Path.Combine( project.builder.workingDirectory, @"titleupdate\" + branch + @"\" ) + @"game_win64*.pdb /s " + symbolServer + " /compress /t " + projectName + " /v " + changelist + " /o" );
}
if( ret == 0 )
{
Program.Log( "Scarlett symbols uploaded" );
}
else
{
Program.Log( "Scarlett symbols failed" );
}
return ret;
}
//bool UpdateVersion( int cl, string file, string branch, P4 p4 = null )
//{
// string versionFile = Path.Combine( project.builder.workingDirectory, file );
// if( !File.Exists( versionFile ) )
// {
// project.AddError( "Missing " + versionFile );
// return false;
// }
// if( p4 == null )
// {
// p4 = new P4( project.p4IniFile );
// if( !p4.Connect() )
// {
// project.AddError( "Failed to connect to p4" );
// return false;
// }
// }
// project.AddLog( "Sync " + versionFile );
// p4.Sync( versionFile, autoResolve: true, clobberWritable: true );
// project.AddLog( "Checkout " + versionFile );
// p4.OpenForEdit( versionFile );
// string[] lines = File.ReadAllLines( versionFile );
// Regex version = new Regex( @"^\s*\[VERSION_NUMBER\]\s*$" );
// bool found = false;
// for( int i = 0; i < lines.Length; ++i )
// {
// if( version.Match( lines[i] ).Success )
// {
// ++i;
// lines[i] = cl.ToString() + "-" + branch;
// project.AddLog( "Set version to " + lines[i] );
// found = true;
// break;
// }
// }
// if( found )
// {
// project.AddLog( "Writing " + versionFile );
// File.WriteAllLines( versionFile, lines );
// }
// else
// {
// project.AddError( "Failed to find [VERSION_NUMBER] in" );
// for( int i = 0; i < lines.Length; ++i )
// {
// project.AddError( "\t " + lines[i] );
// }
// }
// return found;
//}
//int UpdateVersion( string file, string branch )
//{
// int ret = -1;
// SgaPrebuildSettings settings = project.Settings();
// int cl = currentChangelist;
// cl = cl == 0 ? settings.lastChangelist : cl;
// if( cl != 0 )
// {
// ret = UpdateVersion( cl, file, branch ) ? 0 : 1;
// }
// return ret;
//}
/******************************************************************************************************************
** TODO put this version into the api
******************************************************************************************************************/
public IList<Perforce.P4.Changelist> GetChangelistsAtRevision( P4 p4, IList<Perforce.P4.FileSpec> specs )
{
Perforce.P4.ChangesCmdFlags flags = Perforce.P4.ChangesCmdFlags.None;
Perforce.P4.ChangesCmdOptions options = new Perforce.P4.ChangesCmdOptions( flags, "", 1, Perforce.P4.ChangeListStatus.Submitted, "", 0 );
IList<Perforce.P4.Changelist> changes = p4.rep.GetChangelists( options, specs.ToArray() );
return changes;
}
/******************************************************************************************************************
** Called from .nom. Syncs files relevant to the branch
******************************************************************************************************************/
int SyncBranch( string branchName, string outputFile = "" )
{
P4 p4 = SetupP4();
if (p4 == null)
{
return 1;
}
SgaPrebuildSettings settings = project.Settings();
IList<Perforce.P4.FileSpec> files = SyncBranches( p4, branchName, autoResolve: false );
if( files == null )
{
return 1;
}
BranchStatus branchStatus = settings.branchStatus[branchName];
if( files.Count > 0 )
{
List<string> buildPaths = new List<string>();
AddBuildInfo( GetBuildInfoFromFiles( p4, buildPaths, files, branchStatus.lastChangelist, branchStatus.syncedChangelist ) );
}
//Don't need to do anymore if no outputFile
if( string.IsNullOrEmpty( outputFile ) )
{
return 0;
}
List<string> paths = GetBranchPaths( branchName );
IList<Perforce.P4.FileSpec> specs = new List<Perforce.P4.FileSpec>( paths.Count );
foreach( string path in paths )
{
specs.Add( new Perforce.P4.FileSpec( new Perforce.P4.LocalPath( Path.Combine( p4root, path) ) ) );
}
specs = p4.Where( specs );
specs.SetVersion( new Perforce.P4.HaveRevision() );
IList<Perforce.P4.Changelist> changes = GetChangelistsAtRevision( p4, specs );
StringBuilder sb = new StringBuilder();
foreach( Perforce.P4.Changelist change in changes )
{
sb.Append( "Change " + change.Id + " on " + change.ModifiedDate.ToString( "yyyy/mm/dd" ) + " by " + change.OwnerName + "'" + change.Description.WordWrap( 18 ).Replace('\n', ' ') + "'" + Environment.NewLine);
}
try
{
string dir = Path.GetDirectoryName( outputFile );
Util.MakeDirectory( dir );
File.WriteAllText( outputFile, sb.ToString() );
}
catch( Exception ex )
{
App.LogException( ex );
return ex.HResult;
}
return 0;
}
/******************************************************************************************************************
**
******************************************************************************************************************/
void QuickOnOutput( object sender, string output, OutputLevel level )
{
switch( level )
{
case OutputLevel.Info:
case OutputLevel.StdOut:
project.AddLog( output );
break;
case OutputLevel.Warning:
project.AddWarning( output );
break;
case OutputLevel.StdErr:
project.AddError( output );
break;
}
}
/******************************************************************************************************************
**
******************************************************************************************************************/
public void Activate()
{
if ( Oozy.continuous )
{
SgaPrebuildSettings settings = project.Settings();
project.SetContinuous( new TimeSpan( 0, 0, 0 ) );
int hour = 00;
int min = 00;
DateTime now = DateTime.Now;
project.SetNightbuild( new DateTime( now.Year, now.Month, now.Day, hour, min, 0 ) );
project.builder.builderLogPath = Path.Combine( project.enginePath, settings.logDir );
project.builder.buildQueue.Register( "PackageBuild", BuildTypeFlags.Packaging, PackageBuild );
}
}
/******************************************************************************************************************
**
******************************************************************************************************************/
public void DeActivate()
{
if( Oozy.continuous )
{
project.builder.buildQueue.Unregister( "PackageBuild" );
project.SetContinuous( null );
project.SetNightbuild( null );
}
}
/******************************************************************************************************************
**
******************************************************************************************************************/
private void IPStateChanged( IPInfo info, IPInfo.State oldState, IPInfo.State newState )
{
string user = project.GetSlackUser( info.user );
if( oldState == IPInfo.State.Dead && newState == IPInfo.State.Alive )
{
project.PostSlackMessage( new List<string>() { user }, "Your PC lives." );
}
else if( oldState == IPInfo.State.Alive && newState == IPInfo.State.Dead )
{
project.PostSlackMessage( new List<string>() { user }, "Your PC has gone away." );
}
if( !info.initiallyReported )
{
info.initiallyReported = true;
if( oldState == IPInfo.State.Unknown && newState == IPInfo.State.Alive )
{
project.PostSlackMessage( new List<string>() { user }, "Your PC is currently alive." );
}
else if( oldState == IPInfo.State.Unknown && newState == IPInfo.State.Dead )
{
project.PostSlackMessage( new List<string>() { user }, "Your PC is currently dead." );
}
}
}
/******************************************************************************************************************
**
******************************************************************************************************************/
void ReplaceCommand( Builder builder, string xmlDefinedName, string displayName, string command )
{
BuildEntry build = builder.GetBuildByName( xmlDefinedName );
if( build != null )
{
build.displayName = displayName;
BuildBatchFile batch = build as BuildBatchFile;
if( batch != null )
{
foreach( var subBuild in batch.builds )
{
if( subBuild is BuildCommand )
{
build.displayName = displayName;
(subBuild as BuildCommand).command = command;
return;
}
}
Console.Error.WriteLine( xmlDefinedName + " couldn't find a BuildCommand, you been nom fiddling?" );
return;
}
Console.Error.WriteLine( xmlDefinedName + ": isn't a BuildBatchFile, you been nom fiddling?" );
return;
}
Console.Error.WriteLine( "Couldn't find \"" + xmlDefinedName + "\" in build list" );
}
/******************************************************************************************************************
** This is called when any build related options have changed. "building" is set when GenerateBuild is called for doing the actual build
******************************************************************************************************************/
public bool GeneratePackageBuild( Builder builder, BuildParams buildParams )
{
bool ret = true;
return ret;
}
/******************************************************************************************************************
**
******************************************************************************************************************/
bool OnNightbuild( Builder builder )
{
bool reboot = SystemUpdates.PendingReboot();
if( reboot )
{
project.PostBuildError( "I have pending updates and stuff, looks like I need to be rebooted. If I do it myself I won't auto-restart." );
}
SgaPrebuildSettings settings = project.Settings();
DateTime now = DateTime.Now;
string msg = now.ToString( "dd/MM/yyyy HH:mm" ) + ": Nightbuild started";
project.StartBuildThread( msg );
project.AddLog( msg );
//Friday will have had changes probably, and it's "nightbuild" will happen on saturday. Hence check monday here to cater for no changes on sunday
buildOnlyIfSynced = (now.DayOfWeek == DayOfWeek.Saturday || now.DayOfWeek == DayOfWeek.Sunday || now.DayOfWeek == DayOfWeek.Monday);
nightbuild = true;
CIResult result = null;
BuildParams buildParams = new BuildParams();
buildParams.rebuild = true;
buildParams.flags = BuildTypeFlags.Nightbuild;
try
{
int retry = 10;
while( result == null && retry > 0 )
{
result = DoBuild( builder, buildParams );
if( result == null )
{
project.AddError( "Nightbuild failed with something more than likely p4 related, waiting to try again" );
Thread.Sleep( 10 * 1000 );
}
}
}
catch( Exception ex )
{
project.AddError( "OnNightbuild exception" );
project.PostBuildError( App.LogException( ex ) );
}
settings.filesSyncedToday = 0;
buildOnlyIfSynced = false;
nightbuild = false;
project.PostBuildMessage( "Nightbuild Complete." );
if( result != null && result.ranNormally )
{
project.StopBuildThreadSuccess();
{
OozyBuild.Email email = ((NomProject)project).CreateEmail();
email.To("builds@rockstarnorth.com");
email.subject = "GTAV DEV_GEN9_SGA Prebuild";
email.body = "A new Prebuild is deploying to Resilio and North Distro. \nExternal Studios should check the Resilio portal to ensure the package has finished transfering before deploying to console. Boot checks can be carried out and full prebuild mail will follow once packages are verified. \n\nNorth: \nN:/RSGEDI/Distribution/QA_Build/gta5/dev_ps5/dev_gen9_sga/PREBUILD \nN:/RSGEDI/Distribution/QA_Build/gta5/dev_xbsx/dev_gen9_sga/PREBUILD \n\nAll Studios: \nN:/transfer/[STUDIO]/Resilio/gta5/gen9_packaged_builds/dev_gen9_sga/PREBUILD";
email.Send();
}
}
else
{
project.StopBuildThreadError();
{
OozyBuild.Email email = ((NomProject)project).CreateEmail();
email.To("builds@rockstarnorth.com");
email.subject = "GTAV DEV_GEN9_SGA Prebuild Failed";
email.body = "The latest prebuild generation failed and will need investigated. This will delay the inital prebuild release.";
email.Send();
}
}
if( result == null )
{
return true; //want that log
}
return result.ranNormally;
}
static public int SetupIB( string[] args )
{
try
{
using( RegistryKey keyBase = RegistryKey.OpenBaseKey( RegistryHive.LocalMachine, RegistryView.Registry32 ) )
{
if( null == keyBase )
{
return 1;
}
using( RegistryKey builderKey = keyBase.CreateSubKey( @"SOFTWARE\WOW6432Node\Xoreax\IncrediBuild\Builder" ) )
{
builderKey.SetValue( "UseMSBuild", "1", RegistryValueKind.String );
}
using( RegistryKey buildServiceKey = keyBase.CreateSubKey( @"SOFTWARE\WOW6432Node\Xoreax\IncrediBuild\BuildService" ) )
{
buildServiceKey.SetValue( "CoordHost", "Rsgibc", RegistryValueKind.String );
buildServiceKey.SetValue( "CoordPort", "31104", RegistryValueKind.String );
buildServiceKey.SetValue( "BackupCoordHost", "10.39.18.36", RegistryValueKind.String );
buildServiceKey.SetValue( "BackupCoordPort", "31104", RegistryValueKind.String );
buildServiceKey.SetValue( "BackupCoordDisplay", "Rsgldnibc4", RegistryValueKind.String );
}
}
string exe = @"N:\RSGEDI\Outsource\Ruffian\Ruffian Tools\Utils\IBSetupConsole.exe";
Builder builder = new Builder();
builder.test = false;
BuildEntry entry = new BuildCommand( Path.GetDirectoryName( exe ), exe, "/Install /Components=Agent /Coordinator=Rsgibc" );
Task build = builder.Execute( entry );
build.Wait();
return entry.errorCode;
}
catch( Exception ex )
{
App.LogException( ex );
return ex.HResult;
}
}
string InstallIB()
{
if( 0 != Program.RunLocalFunction( this, SetupIB, new string[1] ) )
{
return "Failed to FixIB";
}
return "";
}
/******************************************************************************************************************
**
******************************************************************************************************************/
bool OnContinuous( Builder builder )
{
CIResult result = null;
SgaPrebuildSettings settings = project.Settings();
try
{
project.AddLog( DateTime.Now.ToString( "dd/MM/yyyy HH:mm" ) + ": Continuous started" );
BuildParams buildParams = new BuildParams();
buildParams.flags = BuildTypeFlags.Continuous;
result = DoBuild( builder, buildParams );
}
catch( Exception ex )
{
project.AddError( "OnContinuous exception\n" + App.LogException( ex ) );
}
if( result == null )
{
return true; //want that log
}
return result.ranNormally;
}
/******************************************************************************************************************
**
******************************************************************************************************************/
CIResult DoBuild( Builder builder, BuildParams buildParams )
{
SgaPrebuildSettings settings = project.Settings();
P4 p4 = SetupP4();
if( p4 == null )
{
return null;
}
bool syncedFiles = false;
int currentChangelist = -1;
foreach( var pair in settings.branchStatus )
{
project.AddLog( "Last changelist for " + pair.Key + " was " + pair.Value.lastChangelist );
}
bool skipForWeekend = false;
IList<Perforce.P4.FileSpec> syncFiles = new List<Perforce.P4.FileSpec>();
IList<Perforce.P4.Changelist> latest = p4.GetChangelists( 1, Perforce.P4.ChangeListStatus.Submitted );
buildInfo = null;
buildPaths = new List<string>();
if( latest.Count > 0 )
{
currentChangelist = latest[0].Id;
bool preview = project.builder.test;
IList<Perforce.P4.FileSpec> files = SyncBranches( p4, currentChangelist: currentChangelist );
if( files == null )
{
//The api will never return null unless p4 has failed somewhere
return null;
}
else
{
if( nightbuild || package )
{
project.PostBuildMessage( "Synced " + files.Count + " files.\nNumber of files synced today is " + settings.filesSyncedToday );
}
if( !preview )
{
p4.RevertUnchangedFiles();
}
settings.filesSyncedToday += files.Count;
if( !(nightbuild || package) && (files == null || files.Count == 0) )
{
project.AddError( "P4: No files synced." );
settings.lastCIChangelist = currentChangelist;
return new CIResult();
}
if( (nightbuild || package) && buildOnlyIfSynced && settings.filesSyncedToday == 0 )
{
project.AddWarning( "It's nightbuild, it's the weekend and nothing has changed today." );
project.PostBuildMessage( "It's nightbuild, it's the weekend and nothing has changed today." );
settings.lastCIChangelist = currentChangelist;
skipForWeekend = true;
}
}
if( files.Count > 0 )
{
syncedFiles = true;
buildInfo = GetBuildInfoFromFiles( p4, buildPaths, files, settings.lastCIChangelist, currentChangelist );
if( buildInfo == null )
{
return null;
}
}
}
//pump it at the project, to handle changes
CIResult result = null;
bool createPackage = settings.createPackage;
if( skipForWeekend )
{
result = settings.lastNightbuildResult;
}
else
{
bool retry = false;
project.buildEnvironment["RSG_AUTOMATION_CODEBUILDER_SRC_CL"] = currentChangelist.ToString();
bool oldCreatePackage = settings.createPackage;
//just make nightbuild act as CI
if( (nightbuild && !package) && !settings.nightBuild )
{
settings.createPackage = false;
}
if( syncedFiles )
{
AddBuildInfo( buildInfo );
}
else
{
buildInfo = new List<BuildInfo>();
}
if( syncedFiles && buildParams.flags.HasFlag( BuildTypeFlags.Packaging ) )
{
project.PostBuildMessage( "Files were synced, doing a CI pass." );
BuildTypeFlags oldFlags = buildParams.flags;
buildParams.flags &= ~BuildTypeFlags.Packaging;
buildParams.flags |= BuildTypeFlags.Continuous;
result = project.HandleFileChanges( buildParams, retry, p4, currentChangelist, buildInfo, buildPaths );
buildParams.flags = oldFlags;
if( result.ranNormally && project.projectStatus == ProjectStatus.Success )
{
project.PostBuildMessage( "Doing package pass." );
result = project.HandleFileChanges( buildParams, retry, p4, currentChangelist, buildInfo, buildPaths );
}
else
{
project.PostBuildError( "Skipping package pass, something went wrong." );
}
}
else
{
result = project.HandleFileChanges( buildParams, retry, p4, currentChangelist, buildInfo, buildPaths );
}
settings.createPackage = oldCreatePackage;
}
if( nightbuild )
{
settings.lastNightbuildResult = result;
}
else
{
settings.lastResult = result;
}
string msg = "";
List<string> sendToUsers = new List<string>();
Stopwatch symbolTime = null;
string tmpLabel = "GTA5_GEN9_PREBUILD_TMP";
string QALabel = "GTA5_GEN9_PREBUILD";
if( result.ranNormally )
{
if( nightbuild || package )
{
List<string> QA = new List<string>()
{
"gen9qa",
};
bool showReport = false;
if( !skipForWeekend )
{
project.PostBuildMessage( "Processing post build." );
if( project.projectStatus == ProjectStatus.Success )
{
//clear out build info, this could cause unwanted growth otherwise
foreach( var pair in branchPaths )
{
//maybe clear out package info after 7 days?
if( nightbuild )
{
settings.branchStatus[pair.Key].sinceNightbuild.Clear();
}
settings.branchStatus[pair.Key].current.Clear();
}
bool uploadSymbols = !builder.test && settings.uploadSymbols;
bool prosperoOk = false;
bool scarlettOk = false;
bool win64Ok = false;
string prosperoOutput;
string scarlettOutput;
string win64Output;
if( uploadSymbols )
{
string branch = "";
if( !buildParams.TryGetValue( "packagename", out branch ) )
{
if( !buildParams.TryGetValue( "branch", out branch ) )
{
branch = "dev_gen9_sga";
}
}
symbolTime = new Stopwatch();
symbolTime.Start();
project.PostBuildMessage( "Uploading prospero symbols for " + branch + "." );
prosperoOk = UploadProsperoSymbols( "GTAO", currentChangelist, branch, out prosperoOutput ) == 0 /*&& UploadProsperoSymbols("GTAO", currentChangelist, "dev_gen9_trailer", out prosperoOutput) == 0*/;
project.PostBuildMessage( "Uploading scarlett symbols for " + branch + "." );
scarlettOk = UploadScarlettSymbols( "GTAO", currentChangelist, branch, out scarlettOutput ) == 0 /*&& UploadScarlettSymbols("GTAO", currentChangelist, "dev_gen9_trailer", out scarlettOutput) == 0*/;
project.PostBuildMessage( "Uploading x64 symbols for " + branch + "." );
win64Ok = UploadWin64Symbols( "GTAO", currentChangelist, branch, out win64Output ) == 0 /*&& UploadWin64Symbols("GTAO", currentChangelist, "dev_gen9_trailer", out win64Output) == 0*/;
symbolTime.Stop();
}
if( createPackage )
{
if( !builder.test )
{
if( settings.createLabels )
{
//Create/get temp label
Perforce.P4.Label label = p4.GetLabel( tmpLabel );
label.Description = "Synced to @" + currentChangelist;
//set the description
p4.UpdateLabel( label );
//Add the range of files we synced to
List<Perforce.P4.FileSpec> specs = new List<Perforce.P4.FileSpec>();
specs.AddRange( syncFiles );
//add files that have been submited due to the build
if( result.filesCheckedIn.Count > 0 )
{
specs.AddRange( result.filesCheckedIn );
}
//Tag all the files in this label
p4.Tag( label.Id, specs );
//Get rid of the previous label
p4.DeleteLabel( QALabel );
p4.CopyLabel( QALabel, label.Id );
p4.DeleteLabel( label.Id );
}
}
sendToUsers = QA;
showReport = true;
}
if( uploadSymbols )
{
if( !prosperoOk )
{
msg += "`Looks like the Prospero symbols didn't upload.`\n";
}
if( !scarlettOk )
{
msg += "`Looks like the Scarlett symbols didn't upload.`\n";
}
if( !win64Ok )
{
msg += "`Looks like the Win64 symbols didn't upload.`\n";
}
}
}
else if( createPackage )
{
showReport = true;
}
}
else
{
if( DateTime.Now.DayOfWeek == DayOfWeek.Monday )
{
sendToUsers = QA;
msg += "Nothing was built as there was no need, previous package details follows:\n";
showReport = true;
}
}
if( showReport )
{
if( project.projectStatus == ProjectStatus.Success )
{
string packages = string.Join( ", ", result.branchInfo.Select( a => a.Value.name ) );
if( package )
{
msg += "Packages " + packages + " complete, sync to *@" + QALabel + "* which has been built to _" + result.builtToChangelist + "_.\n";
}
else
{
msg += "Nightbuild complete, sync to *@" + QALabel + "* which has been built to _" + result.builtToChangelist + "_.\n";
}
if( settings.createPackage )
{
foreach( var pair in result.branchInfo )
{
if( string.IsNullOrEmpty( pair.Value.packageDir ) )
{
msg += "'For some reason it looks like no files were copied for " + pair.Key + "`\n";
}
else
{
msg += "Packages for " + pair.Key + " can be found in ```" + SlackClient.MakeFileHyperLink( pair.Value.packageDir, pair.Value.packageDir ) + "```\n";
string prosperoDeployBat = Path.Combine( pair.Value.packageDir, "CopyAndDeployProspero.bat" );
msg += "Use " + SlackClient.MakeFileHyperLink( prosperoDeployBat, prosperoDeployBat ) + " to copy and deploy.\n";
string scarlettDeployBat = Path.Combine( pair.Value.packageDir, "CopyAndDeployScarlett.bat" );
msg += "Scarlett: " + SlackClient.MakeFileHyperLink( scarlettDeployBat, scarlettDeployBat ) + " \n";
if( pair.Key != result.branchInfo.Last().Key )
{
msg += "-----------------------------";
}
}
}
}
}
else
{
if( package )
{
if( !string.IsNullOrEmpty( packageUser ) )
{
sendToUsers = new List<string>( 1 ) { packageUser };
msg += "`Sorry, package failed, no new build for you yet.`";
}
}
else if( nightbuild )
{
sendToUsers = QA;
msg += "`Sorry, nightbuild failed, no new build for you yet.`";
}
}
}
}
settings.lastCIChangelist = currentChangelist;
settings.lastBuildStatus = project.buildStatus;
}
bool showTimings = !string.IsNullOrEmpty( msg );
if( nightbuild && SystemUpdates.PendingReboot() )
{
msg += "`I have pending updates and stuff, looks like I need to be rebooted. If I do it myself I won't auto-restart.`\n";
}
List<SlackMessageResponse> response = null;
if( showTimings )
{
String addendum = "Build Took: " + result.buildDuration.OozyTime() + "\n";
if( symbolTime != null )
{
addendum += "Upload Symbols Took: " + symbolTime.Elapsed.OozyTime() + "\n";
}
addendum += "Copy Took: " + result.copyDuration.OozyTime() + "\n";
addendum += "Total Copied: " + ((float)(result.copySize) / 1024.0 / 1024.0 / 1024.0).ToString( "0.00" ) + " GB\n";
if( !sendToUsers.Empty() )
{
response = project.NotifySlackUsers( sendToUsers, msg );
}
else
{
response = project.PostSlackMessage( msg );
}
if( response != null && !string.IsNullOrEmpty( addendum ) )
{
project.PostSlackMessage( addendum, response );
}
}
if( nightbuild && settings.transferPackage )
{
try
{
foreach( var pair in result.branchInfo )
{
string dest = Path.GetFileName( pair.Value.packageDir );
dest = Path.Combine( settings.transferDir, pair.Key, dest );
project.PostSlackMessage( "Copying " + pair.Value.packageDir + " to " + dest, response );
Program.CopyFiles( pair.Value.packageDir, dest, throwOnFail: true );
project.PostSlackMessage( "Completed.", response );
}
}
catch( Exception ex )
{
msg = App.LogException( ex );
project.PostSlackMessage( msg, response );
}
}
bool submitProjectFiles = !builder.test;
if( submitProjectFiles && settings.submitP4 )
{
RemoveProjGen( p4, true );
}
project.buildEnvironment["RSG_AUTOMATION_CODEBUILDER_SRC_CL"] = "";
return result;
}
}
}