2406 lines
80 KiB
Plaintext
Executable File
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|