Files
2025-09-29 00:52:08 +02:00

2406 lines
80 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;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Remoting.Messaging;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Xml.Serialization;
using System.Xml.Linq;
using System.Xml.XPath;
//using clitest; /// reference E:\src\xbox\TestBed\clitest\bin\Release\clitest.exe
using System.Reflection;
using System.Linq.Expressions;
namespace GTAO
{
/******************************************************************************************************************
**
******************************************************************************************************************/
public static class ProjectExtension
{
public static Settings Settings( this OozyBuild.Project project )
{
return project.customSettings as Settings;
}
}
/******************************************************************************************************************
**
******************************************************************************************************************/
public class Settings : OozyBuild.CustomSettings // : OozyBuild.UEBuildSettings
{
public Settings()
{
//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 = "X:\\Packages";
packageLimit = 5;
transferDir = @"N:\Transfer\RSGDND\Resilio\gta5\gen9_packaged_builds";
logDir = @"N:\RSGDND\CIPackages\Logs";
lastCIChangelist = -1;
obscureSlackMessages = true;
lastResult = new CIResult();
lastNightbuildResult = new CIResult();
branchStatus = new SerializableDictionary<string, BranchStatus>();
PropertyChanged += ( s, e ) =>
{
// if( e.PropertyName == "packageDir" )
// {
// EnablePackageButton( !string.IsNullOrEmpty(packageDir) && !Oozy.packaging );
// }
};
}
public void Setup( Project inProject )
{
project = inProject;
if( Oozy.continuous )
{
project.builder.buildQueue.Register( "RetryContinuous", BuildTypeFlags.Continuous, RetryContinuousQueueEntry );
retryButton = new Button();
retryButton.Content = "Retry";
retryButton.Click += ( s, args ) =>
{
if( /*project.runningContinuous || project.builder.building*/ project.builder.buildQueue.Processing() || Oozy.pendingRestart /*|| Oozy.packaging*/ )
{
return;
}
EnableRetryButton( false );
project.builder.buildQueue.Add( "RetryContinuous" );
// QueueEntry q = new QueueEntry( BuildTypeFlags.Continuous );
// q.action += (QueueEntry e) =>
// (new Thread( () =>
// {
#if false
project.AddLog( DateTime.Now.ToString( "dd/MM/yyyy HH:mm" ) + ": retry started" );
string ini = project.p4IniFile;
if( File.Exists( ini ) )
{
P4 p4 = new P4( ini );
project.AddLog( "Connecting to " + p4.Port );
if( !p4.Connect() )
{
project.AddError( "P4: Failed to connect to perforce" );
EnableRetryButton( true );
}
else
{
project.SetupSCCOutput( p4 );
CIResult result = project.HandleFileChanges( false, true, p4, lastChangelist, lastBuildInfo, lastPaths );
if( result.ranNormally )
{
lastBuildStatus = project.buildStatus;
bool failed = lastBuildStatus.Failed();
EnableRetryButton( failed );
}
}
}
else
{
project.AddLog( "p4ini \"" + ini + "\" not found, aborting retry" );
}
#endif
// project.runningContinuous = false;
// } )).Start();
// };
// project.builder.buildQueue.Add( q );
// };
EnableRetryButton( lastBuildStatus != null && lastBuildStatus.Count > 0 && lastBuildStatus.Failed() );
};
sendEmail = false;
}
if( string.IsNullOrEmpty( Project.IncredibuildExe ) || !File.Exists( Project.IncredibuildExe ) )
{
incredibuild_IsEnabled = false;
}
else
{
incredibuild_IsEnabled = true;
}
}
object RetryContinuousQueueEntry( QueueEntry queueEntry )
{
project.AddLog( DateTime.Now.ToString( "dd/MM/yyyy HH:mm" ) + ": retry started" );
#if false
string ini = project.p4IniFile;
if( File.Exists( ini ) )
{
P4 p4 = new P4( ini );
project.AddLog( "Connecting to " + p4.Port );
if( !p4.Connect() )
{
project.AddError( "P4: Failed to connect to perforce" );
EnableRetryButton( true );
}
else
{
project.SetupSCCOutput( p4 );
project.buildEnvironment["RSG_AUTOMATION_CODEBUILDER_SRC_CL"] = lastCIChangelist.ToString();
project.buildEnvironment["RunProjGen"] = "true";
BuildParams buildParams = new BuildParams();
buildParams.flags = BuildTypeFlags.Continuous;
CIResult result = project.HandleFileChanges( buildParams, true, p4, lastCIChangelist, lastBuildInfo, lastPaths );
if( result.ranNormally )
{
lastBuildStatus = project.buildStatus;
bool failed = lastBuildStatus.Failed();
EnableRetryButton( failed );
}
project.buildEnvironment["RSG_AUTOMATION_CODEBUILDER_SRC_CL"] = "";
return true;
}
}
else
{
project.AddLog( "p4ini \"" + ini + "\" not found, aborting retry" );
}
#endif
return false;
}
// public async System.Threading.Tasks.Task<bool> PerformTestShelve(int cl, string user = "")
public CIResult PerformTestShelve(int cl, string user = "", bool rebuild = true)
{
CIResult ret = null;
// if( project.runningContinuous || project.builder.building || Oozy.pendingRestart || Oozy.packaging )
// {
// return false;
// }
Settings settings = project.Settings();
// project.runningContinuous = true;
// project.testingShelve = true;
// project.testingUser = user;
// project.testingShelveId = cl;
project.AddLog( DateTime.Now.ToString( "dd/MM/yyyy HH:mm" ) + ": testing shelve " + cl );
// (new Thread( () =>
// {
ret = extension.TestShelve(cl, rebuild);
// project.testingShelveId = -1;
// project.testingUser = string.Empty;
// project.testingShelve = false;
// project.runningContinuous = false;
// } )).Start();
return ret;
}
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;
}) );
}
}
public void EnableRetryButton(bool enable)
{
EnableButton( retryButton, 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( "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; }
// bool postSlackMessages_IsEnabled { get { return Oozy.continuous; } 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; }
// bool sendEmail_IsEnabled { get { return Oozy.continuous; } set {} }
[ContinuousMode]
[PropertyToolTip( "Submit scc outputs defined in NOM to p4" )]
public bool submitP4 { get; set; }
// bool submitP4_IsEnabled { get { return Oozy.continuous; } set {} }
/******************************************************************************************************************
** END OF SPECIAL VARIABLES
******************************************************************************************************************/
[ContinuousMode]
public bool tagDataLabel { get; set; }
[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; }
[XmlIgnore]
public Button retryButton { get; set; }
public List<FileToMerge> filesToMerge = new List<FileToMerge>();
public CIResult lastResult { get; set; }
public CIResult lastNightbuildResult { get; set; }
public bool pendingReboot { 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;
iCal calendar = new iCal();
bool buildOnlyIfSynced;
string notifiedIBFailed;
//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 )
{
// string resolveDir = @"X:\gta5\tools_ng\bin\gen9\RageScriptEditor\";
// AppDomain.CurrentDomain.AssemblyResolve += ( s, e ) =>
// {
// string path = Path.Combine( resolveDir, new AssemblyName( e.Name ).Name + ".dll" );
// if( File.Exists( path ) )
// {
// return Assembly.LoadFile( path );
// }
// return null;
// };
return new BuildExtension( inProject );
}
/******************************************************************************************************************
**
******************************************************************************************************************/
public BuildExtension( Project inProject )
{
project = inProject;
Settings settings = new Settings();
settings.Setup( inProject );
settings.extension = this;
branchPaths = new Dictionary<string, BranchSyncInfo>()
{
{ "dev_ng", new BranchSyncInfo() { storeChanges = false, syncPaths = new List<string>()
{
@"gta5\tools_ng\...",
@"gta5\src\dev_ng\...",
@"gta5\src\dev_ng\rage\...",
@"gta5\build\disk_images\gen9\...",
@"x:\3rdParty\...",
}
}
},
{ "dev_ng_live", new BranchSyncInfo() { storeChanges = false, syncPaths = new List<string>()
{
@"gta5\tools_ng\...",
@"gta5\src\dev_ng_live\...",
@"gta5\src\dev_ng_live\rage\...",
@"gta5\build\disk_images\gen9\...",
@"x:\3rdParty\...",
}
}
},
{ "dev_gen9_sga", new BranchSyncInfo() { storeChanges = true, syncPaths = new List<string>()
{
@"gta5\tools_ng\...",
@"gta5\script\dev_gen9_sga\...",
@"gta5\build\dev_gen9_sga\...",
@"gta5\titleupdate\dev_gen9_sga\...",
@"gta5\titleupdate\dev_gen9_sga\common\shaders\...",
@"gta5\titleupdate\dev_gen9_sga\xbsx\audio\...",
@"gta5\titleupdate\dev_gen9_sga\ps5\audio\...",
@"gta5\src\dev_gen9_sga\...",
@"gta5\src\dev_gen9_sga\rage\...",
@"gta5\build\disk_images\gen9\...",
@"x:\3rdParty\...",
}
}
},
{ "dev_gen9_unstable", new BranchSyncInfo() { storeChanges = true, syncPaths = new List<string>()
{
@"gta5\tools_ng\...",
@"gta5\script\dev_gen9_sga\...",
@"gta5\build\dev_gen9_sga\...",
@"gta5\titleupdate\dev_gen9_sga\...",
@"gta5\titleupdate\dev_gen9_sga\common\shaders\...",
@"gta5\titleupdate\dev_gen9_sga\xbsx\audio\...",
@"gta5\titleupdate\dev_gen9_sga\ps5\audio\...",
@"gta5\src\dev_gen9_sga_unstable\...",
@"gta5\src\dev_gen9_sga_unstable\rage\...",
@"gta5\build\disk_images\gen9\...",
@"x:\3rdParty\...",
}
}
},
{ "dev_gen9_sga_live", new BranchSyncInfo() { storeChanges = true, syncPaths = new List<string>()
{
@"gta5\src\dev_gen9_sga_live\...",
@"gta5\src\dev_gen9_sga_live\rage\...",
}
}
},
};
//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( "GTAO loaded" );
}
object PackageBuild( QueueEntry queueEntry )
{
Settings 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 oldTagData = settings.tagDataLabel;
bool oldCreateLabel = settings.createLabels;
bool oldSendEmail = settings.sendEmail;
if( silent )
{
settings.postSlackMessages = false;
settings.submitP4 = false;
settings.tagDataLabel = 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.tagDataLabel = oldTagData;
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;
}
object TestShelveQueueEntry( QueueEntry queueEntry )
{
Settings settings = project.Settings();
string msg = "Testing shelve " + queueEntry.testingShelveId + " against " + settings.lastCIChangelist + " for " + queueEntry.testingUser;
Logger.Log( msg );
project.AddLog( msg );
project.StartBuildThread( msg );
bool ret = true;
OnEntryStartedHandler startedHandler = ( s, build ) =>
{
if( !build.skip && build.status != BuildEntry.Status.Disabled )
{
project.AddWarning( "STARTED: " + build.displayName );
}
};
OnEntryFinishedHandler finishedHandler = ( s, build ) =>
{
if( build.Failed() )
{
project.PostBuildError( "FAILED: " + build.displayName + " ( " + build.logPath + " )" );
project.AddError( "FAILED: " + build.displayName + " ( " + build.logPath + " )" );
// project.RemoveClientLogging( userSocket );
ret = false;
}
else if( build.Succeeded() )
{
project.PostBuildMessage( "OK: " + build.displayName + " ( " + build.logPath + " )" );
project.AddSuccess( "OK: " + build.displayName + " ( " + build.logPath + " )" );
}
};
//project.SetupClientLogging( userSocket );
project.builder.OnEntryStarted += startedHandler;
project.builder.OnEntryFinished += finishedHandler;
/*System.Threading.Tasks.Task<bool> th = settings.PerformTestShelve( q.testingShelveId, userSocket.user );
th.Wait(); */
bool rebuild = queueEntry.values["clean"] != "false";
bool breakOnFirstError = false;
string str;
if( queueEntry.values.TryGetValue( "breakonerror", out str ) )
{
if( !string.IsNullOrEmpty( str ) &&
(string.Compare(str, "yes", true) == 0
|| string.Compare( str, "true", true ) == 0
)
)
{
breakOnFirstError = true;
}
}
bool continueOnError = Oozy.continuousContinuesOnError;
if( breakOnFirstError )
{
Oozy.continuousContinuesOnError = false;
}
bool oldSubmit = settings.submitP4;
settings.submitP4 = false;
CIResult result = settings.PerformTestShelve( queueEntry.testingShelveId, queueEntry.testingUser, rebuild: rebuild );
settings.submitP4 = oldSubmit;
Oozy.continuousContinuesOnError = continueOnError;
if( result == null || !result.ranNormally )
{
ret = false;
}
// for( int i = 0; i < 10; ++i )
// {
// project.AddLog( "Test message " + i );
// Thread.Sleep( 1000 );
// }
project.builder.OnEntryFinished -= finishedHandler;
project.builder.OnEntryStarted -= startedHandler;
msg = "";
if( ret )
{
msg = "Test shelve completed successfully.\n";
}
else
{
msg = "`Test shelve Failed.`\n";
}
DateTime time = project.builder.buildStartingTime ?? DateTime.Now;
string logPath = project.builder.GetBuilderLogPath( time );
if( !string.IsNullOrEmpty( msg ) )
{
msg += "Logs are in " + SlackClient.MakeFileHyperLink( logPath, logPath );
project.PostBuildMessage( msg );
}
if( ret )
{
project.StopBuildThreadSuccess();
}
else
{
project.StopBuildThreadError();
}
// project.RemoveClientLogging( userSocket );
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;
}
string CaptureScriptErrors( string logPath, int maxErrors )
{
//string[] lines = File.ReadAllLines( logPath );
return "";
}
List<string> skippedFiles;
void CheckSkipped( uint cmdId, int msgId, int level, string data )
{
if( msgId == Perforce.P4.P4ClientError.MsgServer_ResolvedSkipped )
{
Regex skipped = new Regex( @"^(?<file>[\s\S]+) - resolve skipped.", RegexOptions.IgnoreCase );
Match m = skipped.Match( data );
if( m.Success )
{
string file = m.Groups["file"].Value;
skippedFiles.Add( file );
}
}
}
int SetupLocalXml(string branchList, string platformList)
{
try
{
string[] branches = branchList.Split( new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries );
string[] platforms = platformList.Split( new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries );
XDocument xmlDoc = new XDocument(
new XDeclaration( "1.0", "UTF-8", "yes" ),
new XElement( "local",
//TODO: DHM FIX ME: obsolete format
new XComment( "Tools Migration: old XAttribute-format but still read by RSG.Base.Configuration." ),
new XElement( "branches",
new XAttribute( "default", branches[0] ),
branches.Select( branch =>
new XElement( "branch",
new XAttribute( "name", branch ),
new XElement( "targets",
platforms.Select( kvp =>
new XElement( "target",
new XAttribute( "platform", kvp ),
new XAttribute( "enabled", true )
)
)
)
)
)
),
//TODO: DHM FIX ME: end obsolete format
// New XElement-format.
new XComment( "Tools Migration: new XElement-format." ),
new XElement( "ConfigFileVersion", 2 ),
new XElement( "Branches",
new XElement( "Default", branches[0] ),
branches.Select( branch =>
new XElement( "Branch",
new XElement( "Name", branch ),
new XElement( "Targets",
platforms.Select( kvp =>
new XElement( "Target",
new XElement( "Platform", kvp ),
new XElement( "Enabled", true )
)
)
)
)
)
),
new XElement( "DefaultPlatforms",
platforms.Select( kvp =>
new XElement( "DefaultPlatform",
new XElement( "Platform", kvp ),
new XElement( "Enabled", true )
)
)
)
)
);
string local = Path.Combine( project.builder.workingDirectory, @"tools_ng\etc\local.xml" );
xmlDoc.Save( local );
}
catch( Exception ex )
{
project.AddError( App.LogException( ex ) );
return ex.HResult;
}
return 0;
}
int UpdateNatives( int changelistId )
{
int ret = 0;
if( changelistId == 0 )
{
changelistId = -1;
}
P4 p4 = currentP4;
if( p4 == null )
{
project.AddError("UpdateNatives: p4 is null");
return 1;
}
Settings settings = project.Settings();
string srcPath = @"src\dev_gen9\game\script_headers";
string dstPath = @"script\dev_gen9\core\common\native";
List<string> paths = buildPaths.Where(a => a.StartsWith(Path.Combine(project.builder.workingDirectory, srcPath), StringComparison.OrdinalIgnoreCase)).ToList();
List<Perforce.P4.FileSpec> dests = new List<Perforce.P4.FileSpec>(paths.Count);
foreach( string src in paths )
{
string dst = src.Replace(srcPath, dstPath);
if( !settings.filesToMerge.Exists(a => string.Compare(a.source, src, true) == 0 && string.Compare(a.dest, dst, true) == 0) )
{
settings.filesToMerge.Add(new FileToMerge() { source = src, dest = dst });
}
// IList<Perforce.P4.FileSpec> merged = p4.Merge(new Perforce.P4.LocalPath(src), new Perforce.P4.LocalPath(dst), changelistId);
// dests.AddRange(merged);
// dst = @"x:\gta5\script\dev_gen9_trailer\core\common\native\commands_graphics.sch";
// merged = p4.Merge(new Perforce.P4.LocalPath(src), new Perforce.P4.LocalPath(dst), changelistId);
// dests.AddRange(merged);
}
foreach( FileToMerge merge in settings.filesToMerge )
{
IList<Perforce.P4.FileSpec> merged = p4.Merge(new Perforce.P4.LocalPath(merge.source), new Perforce.P4.LocalPath(merge.dest), changelistId);
dests.AddRange(merged);
}
StringBuilder msg = new StringBuilder(1024);
if( dests.Count > 0 )
{
int errors = 0;
try
{
skippedFiles = new List<string>();
p4.rep.Connection.InfoResultsReceived += CheckSkipped;
IList<Perforce.P4.FileResolveRecord> records = p4.Resolve(dests, Perforce.P4.ResolveFilesCmdFlags.AutomaticMergeMode, changelistId);
//p4.rep.Connection.InfoResultsReceived -= CheckSkipped;
Dictionary<string, List<string>> userMessages = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);
IList<Perforce.P4.FileSpec> files;
if( skippedFiles.Count > 0 )
{
errors += skippedFiles.Count;
files = new List<Perforce.P4.FileSpec>();
foreach( string file in skippedFiles )
{
files.Add(p4.GetFileSpecFromPath(file));
}
files = p4.Where(files);
foreach( Perforce.P4.FileSpec fileSpec in files )
{
var record = records.Where(a => a.LocalFilePath != null && string.Compare(a.LocalFilePath.Path, fileSpec.LocalPath.Path, true) == 0).ToList();
if( record.Empty() )
{
msg.Append("\t`Natives: Failed to find resolve record for " + fileSpec.DepotPath.Path + "`\n");
continue;
}
records.Remove(record[0]);
var bo = buildInfo.FirstOrDefault(a => a.changes.Exists(b => b.files.FirstOrDefault(c => string.Compare(c, fileSpec.LocalPath.Path, true) == 0) != string.Empty));
if( bo == null )
{
msg.Append("`\tNatives: Failed to find changelist info for " + fileSpec.DepotPath.Path + "`\n");
continue;
}
List<string> messages;
if( !userMessages.TryGetValue(bo.userName, out messages) )
{
messages = new List<string>();
userMessages[bo.userName] = messages;
}
messages.Add("`Natives: Failed to merge " + record[0].BaseFileSpec.DepotPath.Path + " to " + fileSpec.LocalPath.Path + "`\n");
}
}
var successFiles = records.Select(a => p4.GetFileSpecFromPath(a.FromFileSpec.DepotPath.Path)).ToList();
files = p4.Where(successFiles);
int count = 0;
if( files.Count == records.Count )
{
foreach( var record in records )
{
record.LocalFilePath = files[count++].LocalPath;
if( record.Action == Perforce.P4.FileAction.None )
{
continue;
}
List<string> messages;
var bos = buildInfo.Where(a => a.changes.Exists(b => b.files.Exists(c => string.Compare(c, record.LocalFilePath.Path, true) == 0)));
foreach( BuildInfo bo in bos )
{
if( !userMessages.TryGetValue(bo.userName, out messages) )
{
messages = new List<string>();
userMessages[bo.userName] = messages;
}
messages.Add("Natives: merged *" + record.LocalFilePath.Path + "* to *" + dstPath + "* OK\n");
}
}
StringBuilder threadMessage = new StringBuilder();
msg.Append("This is just a test, nothing has actually merged. This is informational only. No hamsters with cat hats, sorry.\n");
foreach( var pair in userMessages )
{
msg.Append(project.GetSlackUser(/*pair.Key*/"Leigh.Bird") + " (but really _" + pair.Key + "_)\n");
threadMessage.Append(string.Join("", pair.Value));
}
List<SlackMessageResponse> response = project.PostSlackMessage(msg.ToString());
project.PostSlackMessage(threadMessage.ToString(), response);
}
else
{
++errors;
string err = project.GetSlackUser("Leigh.Bird") + " `HALP! Natives Merge: file.Count = " + files.Count + " but records.Count = " + records.Count + "`";
project.PostSlackMessage(err);
}
}
catch( Exception ex )
{
++errors;
string err = App.LogException(ex);
err = project.GetSlackUser("Leigh.Bird") + " `HALP! You have made a whoopsie`\n``` " + err + " ```";
project.PostSlackMessage(err);
}
if( errors == 0 )
{
settings.filesToMerge.Clear();
}
else
{
//Add this when temp removed
//p4.Revert(Perforce.P4.FileSpec.UnversionedSpecList(dests));
}
//temp for now
{
settings.filesToMerge.Clear();
p4.Revert(Perforce.P4.FileSpec.UnversionedSpecList(dests));
}
}
return ret;
}
int UpdateNativesGen9(int changelistId)
{
int ret = 0;
if( changelistId == 0 )
{
changelistId = -1;
}
P4 p4 = currentP4;
if( p4 == null )
{
project.AddError("UpdateNatives: p4 is null");
return 1;
}
Settings settings = project.Settings();
string srcPath = @"src\dev_gen9_sga\game\script_headers";
string dstPath = @"script\dev_gen9_sga\core\common\native";
List<string> paths = buildPaths.Where(a => a.StartsWith(Path.Combine(project.builder.workingDirectory, srcPath), StringComparison.OrdinalIgnoreCase)).ToList();
List<Perforce.P4.FileSpec> dests = new List<Perforce.P4.FileSpec>(paths.Count);
foreach( string src in paths )
{
string dst = src.Replace(srcPath, dstPath);
if( !settings.filesToMerge.Exists(a => string.Compare(a.source, src, true) == 0 && string.Compare(a.dest, dst, true) == 0) )
{
settings.filesToMerge.Add(new FileToMerge() { source = src, dest = dst });
}
// IList<Perforce.P4.FileSpec> merged = p4.Merge(new Perforce.P4.LocalPath(src), new Perforce.P4.LocalPath(dst), changelistId);
// dests.AddRange(merged);
//
// dst = @"x:\gta5\script\dev_gen9_trailer\core\common\native\commands_graphics.sch";
// merged = p4.Merge(new Perforce.P4.LocalPath(src), new Perforce.P4.LocalPath(dst), changelistId);
// dests.AddRange(merged);
}
foreach( FileToMerge merge in settings.filesToMerge )
{
IList<Perforce.P4.FileSpec> merged = p4.Merge(new Perforce.P4.LocalPath(merge.source), new Perforce.P4.LocalPath(merge.dest), changelistId);
dests.AddRange(merged);
}
StringBuilder msg = new StringBuilder(1024);
if( dests.Count > 0 )
{
int errors = 0;
try
{
skippedFiles = new List<string>();
p4.rep.Connection.InfoResultsReceived += CheckSkipped;
IList<Perforce.P4.FileResolveRecord> records = p4.Resolve(dests, Perforce.P4.ResolveFilesCmdFlags.AutomaticMergeMode, changelistId);
//p4.rep.Connection.InfoResultsReceived -= CheckSkipped;
Dictionary<string, List<string>> userMessages = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);
IList<Perforce.P4.FileSpec> files;
if( skippedFiles.Count > 0 )
{
errors += skippedFiles.Count;
files = new List<Perforce.P4.FileSpec>();
foreach( string file in skippedFiles )
{
files.Add(p4.GetFileSpecFromPath(file));
}
files = p4.Where(files);
foreach( Perforce.P4.FileSpec fileSpec in files )
{
var record = records.Where(a => a.LocalFilePath != null && string.Compare(a.LocalFilePath.Path, fileSpec.LocalPath.Path, true) == 0).ToList();
if( record.Empty() )
{
msg.Append("\t`Natives: Failed to find resolve record for " + fileSpec.DepotPath.Path + "`\n");
continue;
}
records.Remove(record[0]);
var bo = buildInfo.FirstOrDefault(a => a.changes.Exists(b => b.files.FirstOrDefault(c => string.Compare(c, fileSpec.LocalPath.Path, true) == 0) != string.Empty));
if( bo == null )
{
msg.Append("`\tNatives: Failed to find changelist info for " + fileSpec.DepotPath.Path + "`\n");
continue;
}
List<string> messages;
if( !userMessages.TryGetValue(bo.userName, out messages) )
{
messages = new List<string>();
userMessages[bo.userName] = messages;
}
messages.Add("`Natives: Failed to merge " + record[0].BaseFileSpec.DepotPath.Path + " to " + fileSpec.LocalPath.Path + "`\n");
}
}
var successFiles = records.Select(a => p4.GetFileSpecFromPath(a.FromFileSpec.DepotPath.Path)).ToList();
files = p4.Where(successFiles);
int count = 0;
if( files.Count == records.Count )
{
foreach( var record in records )
{
record.LocalFilePath = files[count++].LocalPath;
if( record.Action == Perforce.P4.FileAction.None )
{
continue;
}
List<string> messages;
var bos = buildInfo.Where(a => a.changes.Exists(b => b.files.Exists(c => string.Compare(c, record.LocalFilePath.Path, true) == 0)));
foreach( BuildInfo bo in bos )
{
if( !userMessages.TryGetValue(bo.userName, out messages) )
{
messages = new List<string>();
userMessages[bo.userName] = messages;
}
messages.Add("Natives: merged *" + record.LocalFilePath.Path + "* to *" + dstPath + "* OK\n");
}
}
StringBuilder threadMessage = new StringBuilder();
msg.Append("This is just a test, nothing has actually merged. This is informational only. No hamsters with cat hats, sorry.\n");
foreach( var pair in userMessages )
{
msg.Append(project.GetSlackUser(/*pair.Key*/"Leigh.Bird") + " (but really _" + pair.Key + "_)\n");
threadMessage.Append(string.Join("", pair.Value));
}
List<SlackMessageResponse> response = project.PostSlackMessage(msg.ToString());
project.PostSlackMessage(threadMessage.ToString(), response);
}
else
{
++errors;
string err = project.GetSlackUser("Leigh.Bird") + " `HALP! Natives Merge: file.Count = " + files.Count + " but records.Count = " + records.Count + "`";
project.PostSlackMessage(err);
}
}
catch( Exception ex )
{
++errors;
string err = App.LogException(ex);
err = project.GetSlackUser("Leigh.Bird") + " `HALP! You have made a whoopsie`\n``` " + err + " ```";
project.PostSlackMessage(err);
}
if( errors == 0 )
{
settings.filesToMerge.Clear();
}
else
{
//Add this when temp removed
//p4.Revert(Perforce.P4.FileSpec.UnversionedSpecList(dests));
}
//temp for now
{
settings.filesToMerge.Clear();
p4.Revert(Perforce.P4.FileSpec.UnversionedSpecList(dests));
}
}
return ret;
}
int MergeShaders( int changelistId, string branch )
{
if( changelistId <= 0 )
{
return 0;
}
if( currentP4 == null )
{
return 0;
}
P4 p4 = currentP4;
List<string> sources = new List<string>()
{
Path.Combine(p4root, @"gta5\titleupdate\" + branch + @"\common\shaders\sga_prospero"),
Path.Combine(p4root, @"gta5\titleupdate\" + branch + @"\common\shaders\sga_prospero_debug"),
Path.Combine(p4root, @"gta5\titleupdate\" + branch + @"\common\shaders\sga_prospero_final"),
Path.Combine(p4root, @"gta5\titleupdate\" + branch + @"\common\shaders\sga_scarlett"),
Path.Combine(p4root, @"gta5\titleupdate\" + branch + @"\common\shaders\sga_scarlett_debug"),
Path.Combine(p4root, @"gta5\titleupdate\" + branch + @"\common\shaders\sga_scarlett_final"),
Path.Combine(p4root, @"gta5\titleupdate\" + branch + @"\common\shaders\sga_win32_50"),
Path.Combine(p4root, @"gta5\titleupdate\" + branch + @"\common\shaders\sga_win32_50_debug"),
Path.Combine(p4root, @"gta5\titleupdate\" + branch + @"\common\shaders\sga_win32_50_final"),
Path.Combine(p4root, @"gta5\titleupdate\" + branch + @"\common\shaders\sga_win32_60"),
Path.Combine(p4root, @"gta5\titleupdate\" + branch + @"\common\shaders\sga_win32_60_debug"),
Path.Combine(p4root, @"gta5\titleupdate\" + branch + @"\common\shaders\sga_win32_60_final"),
};
List<string> dests = new List<string>()
{
Path.Combine(p4root, @"gta5\build\" + branch + @"\common\shaders"),
};
p4.RevertUnchangedFiles( changelistId );
List<Perforce.P4.FileSpec> files = new List<Perforce.P4.FileSpec>();
foreach( string dst in dests )
{
foreach( string src in sources )
{
string dest = Path.Combine(dst, Path.GetFileName(src), "...");
files.AddRange(p4.Merge(new Perforce.P4.FileSpec(new Perforce.P4.LocalPath(Path.Combine(src, "..."))), new Perforce.P4.FileSpec(new Perforce.P4.LocalPath(dest)), changelistId));
}
}
if( files.Count > 0 )
{
p4.Resolve(files, Perforce.P4.ResolveFilesCmdFlags.AutomaticTheirsMode, changelistId);
}
return 0;
}
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;
Settings settings = project.Settings();
SerializableDictionary<string, BranchStatus> branchStatuses = (SerializableDictionary<string, BranchStatus>)project.customSettings["branchStatus"];
if( branchStatuses != null )
{
BranchStatus branchStatus;
int cl = 0;
if( branchStatuses.TryGetValue( branch, out branchStatus ) )
{
cl = branchStatus.syncedChangelist;
}
cl = cl == 0 ? settings.lastCIChangelist : cl;
if( cl != 0 )
{
ret = UpdateVersion( cl, file, branch ) ? 0 : 1;
}
}
return ret;
}
/******************************************************************************************************************
**
******************************************************************************************************************/
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 string CheckIB()
{
string installDir = "";
using( RegistryKey keyBase = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32) )
{
if( null == keyBase )
{
project.AddError("32-bit HKLM registry key open failed.");
return "32-bit HKLM registry key open failed.";
}
using( RegistryKey key = keyBase.OpenSubKey("Software\\Xoreax\\IncrediBuild\\Builder") )
{
string version = key.GetValue("VersionText") as string;
if( string.IsNullOrEmpty(version) )
{
return "IB not installed";
}
installDir = key.GetValue("Folder") as string;
if( string.IsNullOrEmpty(installDir) )
{
return "IB not installed";
}
}
using( RegistryKey key = keyBase.OpenSubKey("Software\\Xoreax\\IncrediBuild\\BuildService") )
{
if( key == null )
{
return "Failed to open BuildService in registry";
}
string coord = key.GetValue("CoordHost") as string;
if( string.IsNullOrEmpty(coord) )
{
return "Coordinator isn't set";
}
}
}
bool gettingLicenses = false;
List<string> licenses = new List<string>();
new BuildCommand(Directory.GetCurrentDirectory(), Path.Combine(installDir, "xgconsole.exe"), "/QUERYLICENSE")
{
OnOutput = (object sender, string output, OutputLevel level)=>
{
if( gettingLicenses )
{
if( !string.IsNullOrEmpty(output) && !output.Contains("-------------------") && !output.Contains("- Helper") )
{
licenses.Add(output.Trim());
}
}
else
{
if( output.Contains("Packages installed:") )
{
gettingLicenses = true;
}
}
}
}.Execute("");
if( licenses.Count == 0 )
{
return "Licenses need updating.";
}
return "";
}
/******************************************************************************************************************
**
******************************************************************************************************************/
public void Activate()
{
if( Oozy.continuous )
{
Settings settings = project.Settings();
project.SetContinuous( new TimeSpan( 0, 5, 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( "TestShelve", BuildTypeFlags.TestingShelve, TestShelveQueueEntry );
project.builder.buildQueue.Register( "PackageBuild", BuildTypeFlags.Packaging, PackageBuild );
project.ipWatcher.OnIPStateChanged += IPStateChanged;
}
}
/******************************************************************************************************************
**
******************************************************************************************************************/
public void DeActivate()
{
if( Oozy.continuous )
{
project.builder.buildQueue.Unregister( "TestShelve" );
project.builder.buildQueue.Unregister( "PackageBuild" );
project.SetContinuous( null );
project.SetNightbuild( null );
project.ipWatcher.OnIPStateChanged += IPStateChanged;
}
}
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 )
{
Settings settings = project.Settings();
settings.pendingReboot = SystemUpdates.PendingReboot();
if( settings.pendingReboot )
{
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." );
}
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;
VariableParser.GetValueDelegate getEnv = ( string var, List<string> values ) =>
{
if( string.Compare( var, "cleanpackage", true ) == 0 )
{
values.Add("-clean");
}
};
VariableParser.GetValue += getEnv;
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 ) );
}
VariableParser.GetValue += getEnv;
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:\RSGDND\Tools\Utils\IB Fix 9.5.9\IBSetupConsole_9.5.9.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;
Settings settings = project.Settings();
if( !settings.pendingReboot )
{
bool pendingReboot = SystemUpdates.PendingReboot(); ;
if( pendingReboot )
{
settings.pendingReboot = true;
project.PostBuildError( Environment.GetEnvironmentVariable( "COMPUTERNAME" ) + ": I have pending updates and stuff, looks like I need to be rebooted.If I do it myself I won't auto-restart." );
}
}
if( settings.incredibuild )
{
string err = CheckIB();
if( !string.IsNullOrEmpty(err) )
{
project.StartBuildThread( "IB is out of date, attempting an update" );
string err2 = InstallIB();
if( !string.IsNullOrEmpty( err2 ) )
{
project.PostBuildError( "Oh noes, it's all bad, someone needs to take a look " + err2 );
err += err2;
if( notifiedIBFailed != err )
{
SlackMessage message = new SlackMessage();
string image = ImageSearch.instance.Search( "hamster+wheel+gif" );
if( !string.IsNullOrEmpty( image ) )
{
SlackImage slackImage = new SlackImage();
slackImage.SetImage( image, "HAMSTER MAN IS UPSET" );
message.Add( "HALP!! IB agent check failed, aborting CI `" + err + "`.", slackImage );
}
else
{
message.AddText( "HALP!! IB agent check failed, aborting CI `" + err + "`." );
}
project.NotifySlackUsers( new List<string>( 1 ) { "gen9coders" }, message );
notifiedIBFailed = err;
project.StopBuildThreadError();
}
project.AddError( "IB agent check failed \"" + err + "\", aborting." );
return true; //yup, badness, keep log
}
else
{
project.StopBuildThreadSuccess();
}
}
notifiedIBFailed = "";
}
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;
}
/******************************************************************************************************************
**
******************************************************************************************************************/
int SubmitProjectGen()
{
Settings settings = project.Settings();
bool submitProjectFiles = true;// !builder.test;
if( currentP4 != null && submitProjectFiles && settings.submitP4 )
{
RemoveProjGen( currentP4, true );
}
return 0;
}
/******************************************************************************************************************
**
******************************************************************************************************************/
CIResult DoBuild(Builder builder, BuildParams buildParams)
{
Settings settings = project.Settings();
P4 p4 = SetupP4();
if( p4 == null )
{
return null;
}
bool syncedFiles = false;
int currentChangelist = -1;
//int workingDataCL = 30794996;
string dataLabelName = ""; //disable tagging
if( settings.createLabels )
{
dataLabelName = "Ruffian_Data";
}
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;
if( nightbuild || package )
{
project.PostBuildMessage( "Syncing..." );
}
IList<Perforce.P4.FileSpec> files = SyncBranches( p4, currentChangelist: currentChangelist, labelName: dataLabelName );
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();
project.buildEnvironment["RunProjGen"] = "true";
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 = "";
calendar.DownloadCalendars();
DayEvents today = calendar.GetTodayEvents();
List<string> sendToUsers = new List<string>();
if( nightbuild && !package && today.Count > 0 )
{
foreach( CalendarEntry e in today )
{
iCal.Locale locale = e.locale;
if( locale.HasFlag( iCal.Locale.Scottish ) &&
locale.HasFlag( iCal.Locale.English ) &&
locale.HasFlag( iCal.Locale.Irish ) )
{
msg += ":flag-gb:";
}
else
{
if( locale.HasFlag( iCal.Locale.Scottish ) ) msg += ":flag-scotland:";
if( locale.HasFlag( iCal.Locale.English ) ) msg += ":flag-england:";
if( locale.HasFlag( iCal.Locale.Irish ) ) msg += ":flag-ie:";
}
msg += " " + e.summary + "\n\n";
}
}
Stopwatch symbolTime = null;
string tmpLabel = "Ruffian_QA_TMP";
string rollbackLabel = "Ruffian_QA_Rollback";
string QALabel = "Ruffian_QA";
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 && createPackage;
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 )
{
bool doRollbackLabel = true;
//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( rollbackLabel );
if( doRollbackLabel )
{
IList<Perforce.P4.Label> nextPrevious = p4.FindLabel( QALabel );
if( nextPrevious.Count > 0 )
{
//We have a "current" build already, copy that to previous
p4.CopyLabel( rollbackLabel, nextPrevious[0].Id );
}
}
p4.DeleteLabel( QALabel );
p4.CopyLabel( QALabel, label.Id );
p4.DeleteLabel( label.Id );
}
}
sendToUsers = QA;
showReport = true;
}
if( settings.filesToMerge.Count > 0 )
{
msg += "There are " + settings.filesToMerge.Count + " scripts to be merged.\n";
foreach( FileToMerge file in settings.filesToMerge )
{
project.AddError( "Merge: " + file.source + " => " + file.dest );
}
}
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 failed = settings.lastBuildStatus.Failed();
// settings.EnableRetryButton( failed );
}
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 );
}
}
project.buildEnvironment["RSG_AUTOMATION_CODEBUILDER_SRC_CL"] = "";
return result;
}
public CIResult TestShelve(int cl, bool rebuild)
{
Settings settings = project.Settings();
DateTime start = DateTime.Now;
project.builder.buildStartingTime = start;
string logPath = project.builder.GetBuilderLogPath(start);
Program.PushLogPath(logPath);
if(project.projectStatus == ProjectStatus.Failed)
{
project.PostBuildError( "Current build isn't in a good state, testing a shelf is a bad idea. Unless this will fix it, then cool." );
project.AddError( "Current build isn't in a good state, testing a shelf is a bad idea. Unless this will fix it, then cool." );
// Program.PopLogPath();
// return false;
}
string ini = project.p4IniFile;
if(!File.Exists( ini ))
{
project.PostBuildError( "p4ini \"" + ini + "\" not found, aborting shelve test" );
project.AddLog( "p4ini \"" + ini + "\" not found, aborting shelve test" );
Program.PopLogPath();
return null;
}
P4 p4 = new P4( ini );
project.AddLog( "Connecting to " + p4.Port );
if(!p4.Connect())
{
project.PostBuildError( "P4: Failed to connect host: " + p4.Port + ", user: " + p4.User + ", workspace: " + p4.Workspace );
project.AddError( "P4: Failed to connect host: " + p4.Port + ", user: " + p4.User + ", workspace: " + p4.Workspace );
Program.PopLogPath();
return null;
}
project.SetupSCCOutput( p4 );
Perforce.P4.Changelist changelist = p4.FindChangelist( "EMPTY" );
string description = "Test " + cl;
if( changelist == null )
{
changelist = p4.CreateChangelist( description );
}
else
{
changelist.Description = description;
p4.UpdateChangelist( changelist );
}
Dictionary<int, List<Perforce.P4.FileSpec>> localFilesShelved = new Dictionary<int, List<Perforce.P4.FileSpec>>();
List<string> filesToDelete = new List<string>();
CIResult ret = null;
bool testMode = project.builder.test;
try
{
IList<Perforce.P4.FileMetaData> fstats = null;
{
IList<Perforce.P4.ShelvedFile> shelved = p4.GetShelvedFiles( cl );
if( shelved.Count > 0 )
{
IList<Perforce.P4.FileSpec> tmp = Perforce.P4.FileSpec.UnversionedSpecList(shelved.Select(a => a.ToFileSpec()).ToList());
fstats = p4.FStat(tmp);
foreach( var fstat in fstats )
{
if( fstat.Action != Perforce.P4.FileAction.None )
{
List<Perforce.P4.FileSpec> clFiles;
if( !localFilesShelved.TryGetValue(fstat.Change, out clFiles) )
{
clFiles = new List<Perforce.P4.FileSpec>();
localFilesShelved[fstat.Change] = clFiles;
}
clFiles.Add(fstat.DepotPath);
}
}
if( !testMode )
{
foreach( var pair in localFilesShelved )
{
if( pair.Key != 0 )
{
p4.rep.Connection.Client.ShelveFiles(pair.Value, new Perforce.P4.ShelveFilesCmdOptions(Perforce.P4.ShelveFilesCmdFlags.Force, null, pair.Key));
}
}
}
}
else
{
project.PostBuildError( "No files shelved in " + cl );
project.AddError( "No files shelved in " + cl );
}
}
IList<Perforce.P4.FileSpec> files = p4.Unshelve( cl, changelist.Id, clobberWritable: true, force: true, preview: testMode );
if(files.Count > 0)
{
files = Perforce.P4.FileSpec.UnversionedSpecList( files );
//p4.Resolve( files );
fstats = p4.FStat( files );
if(fstats.Count == 0)
{
project.PostBuildError( "P4 didn't return any results for fstat when checking " + cl );
project.AddError( "P4 didn't return any results for fstat when checking " + cl );
}
else
{
List<Tuple<string, int, int>> filesNotOnHead = new List<Tuple<string, int, int>>();
bool validated = true;
foreach( var fstat in fstats )
{
bool fileOnHead = (fstat.HeadAction == Perforce.P4.FileAction.Delete && fstat.HaveRev == -1) || (fstat.HaveRev >= fstat.HeadRev);
if( !fileOnHead )
{
filesNotOnHead.Add(new Tuple<string, int, int>(fstat.DepotPath.Path, fstat.HaveRev, fstat.HeadRev));
}
if( fstat.Action == Perforce.P4.FileAction.Add || fstat.Action == Perforce.P4.FileAction.MoveAdd )
{
filesToDelete.Add(fstat.LocalPath.Path);
}
if( fstat.Unresolved )
{
validated = false;
project.PostBuildError( "P4: " + fstat.DepotPath.Path + " is unresolved." );
project.AddError( "P4: " + fstat.DepotPath.Path + " is unresolved." );
}
}
if(filesNotOnHead.Count > 0)
{
validated = false;
foreach(Tuple<string, int, int> path in filesNotOnHead)
{
project.PostBuildError( "P4: File not on head " + path.Item1 + ", is on rev " + path.Item2 + ", should be on " + path.Item3 );
project.AddError( "P4: File not on head " + path.Item1 + ", is on rev " + path.Item2 + ", should be on " + path.Item3 );
}
}
if( validated )
{
//p4.Sync( files, autoResolve: true );
//Get their local paths
IList<Perforce.P4.FileSpec> where2 = p4.Where( files );
if(where2 != null && where2.Count == files.Count)
{
List<BuildInfo> buildInfo = new List<BuildInfo>();
List<string> users = new List<string>();
List<string> paths = new List<string>( files.Count );
Perforce.P4.Changelist shelvedCl = p4.GetChangelist( cl, mineOnly: false );
BuildChange bc = GetBuildInfo( shelvedCl, buildInfo, users );
foreach(var f in where2)
{
paths.Add( f.LocalPath.Path );
bc.files.Add( f.LocalPath.Path );
}
SetupUserInfo( p4, buildInfo, users );
// project.builder.AddEnvironment( environment );
bool oldSubmit = settings.submitP4;
settings.submitP4 = false;
project.buildEnvironment["RSG_AUTOMATION_CODEBUILDER_SRC_CL"] = settings.lastCIChangelist.ToString();
project.buildEnvironment["RunProjGen"] = "true";
BuildParams buildParams = new BuildParams();
buildParams.rebuild = rebuild;
buildParams.flags = BuildTypeFlags.TestingShelve;
ret = project.HandleFileChanges( buildParams, false, p4, settings.lastCIChangelist, buildInfo, paths );
project.buildEnvironment["RSG_AUTOMATION_CODEBUILDER_SRC_CL"] = "";
settings.submitP4 = oldSubmit;
// project.builder.RemoveEnvironment( environment );
}
else
{
project.PostBuildError( "P4: Problem running where" );
project.AddError( "P4: Problem running where" );
}
}
}
Perforce.P4.RevertFilesCmdFlags flags = testMode ? Perforce.P4.RevertFilesCmdFlags.Preview : Perforce.P4.RevertFilesCmdFlags.None;
p4.Revert( files, flags );
files.SetVersion( new Perforce.P4.ChangelistIdVersion( settings.lastCIChangelist ) );
p4.Sync( files, autoResolve: true, clobberWritable: true, preview: testMode );
}
}
catch(Exception ex)
{
project.PostBuildError( "TestShelve " + ex.Message );
project.AddError( "TestShelve " + ex.Message );
}
changelist.Description = "EMPTY";
p4.UpdateChangelist( changelist );
RemoveProjGen( p4, false );
if(!testMode)
{
foreach(var pair in localFilesShelved)
{
if(pair.Key != 0)
{
p4.rep.Connection.Client.UnshelveFiles( pair.Value, new Perforce.P4.UnshelveFilesCmdOptions( Perforce.P4.UnshelveFilesCmdFlags.Force, pair.Key, pair.Key ) );
}
}
foreach(string file in filesToDelete)
{
try
{
if(File.Exists( file ))
{
File.Delete( file );
}
}
catch(Exception)
{
}
}
}
Program.PopLogPath();
return ret;
}
}
}