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

1267 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_merge_prebuild
{
/******************************************************************************************************************
**
******************************************************************************************************************/
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_merge", new BranchSyncInfo() { storeChanges = true, syncPaths = new List<string>()
{
@"gta5\titleupdate\dev_gen9_sga_merge\...",
@"gta5\src\dev_gen9_sga_merge\...",
@"gta5\script\dev_gen9_sga_merge\...",
@"gta5\assets_gen9_sga\GameText\dev_gen9_sga_merge\...",
@"gta5\assets_gen9_sga\metadata\...",
@"gta5\assets_gen9_sga\titleupdate\dev_gen9_sga_merge\...",
@"gta5\assets_gen9_sga\maps\ParentTxds.xml",
@"gta5\build\disk_images\...",
@"gta5_dlc\mpPacks\mpSum2\build\dev_gen9_sga\...",
@"gta5_dlc\mpPacks\mpSum2\assets_gen9_disc\gametext\...",
@"gta5_dlc\patchPacks\patchDay27NG\build\dev_gen9_sga\...",
@"gta5\tools_ng\...",
}
}
},
};
//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_merge_prebuild 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();
}
else
{
project.StopBuildThreadError();
}
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;
}
//do a time check, disallow nightbuild but allow hand requested packages
if( !package && nightbuild )
{
DateTime StartHols = new DateTime( 2021, 12, 11, 12, 0, 0 );
DateTime EndHols = new DateTime( 2022, 1, 4, 12, 0, 0 );
DateTime now = DateTime.Now;
if( now > StartHols && now < EndHols )
{
project.AddLog( "Skipping package, it's Christmas" );
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_merge";
}
}
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;
}
}
}