522 lines
18 KiB
Ruby
Executable File
522 lines
18 KiB
Ruby
Executable File
#
|
|
# File:: build_max_plugins.rb
|
|
# Description::
|
|
#
|
|
# Author:: Derek Ward <derek.ward@rockstarnorth.com>
|
|
# Date:: 08th July 2010
|
|
#
|
|
# Passed in :- see OPTIONS ...
|
|
# Passed out :- stderr contains all errors
|
|
# stdout for all other output.
|
|
# Returns :- returns non zero upon detecting any errors
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Uses / Requires
|
|
#-----------------------------------------------------------------------------
|
|
require 'pipeline/config/projects'
|
|
require 'pipeline/os/getopt'
|
|
require 'pipeline/os/file'
|
|
include Pipeline
|
|
require 'systemu'
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Constants
|
|
#-----------------------------------------------------------------------------
|
|
OPTIONS = [
|
|
[ "--help", "-h", OS::Getopt::BOOLEAN, "display usage information." ],
|
|
[ "--dev", "-d", OS::Getopt::BOOLEAN, "build plugins for dev branch" ],
|
|
[ '--buildconfig', '-bc', OS::Getopt::REQUIRED, 'build config (ToolDebugMax2011|x64)' ],
|
|
[ '--buildfolder', '-bf', OS::Getopt::REQUIRED, "debug or current" ],
|
|
[ '--current_local_path','-cp', OS::Getopt::REQUIRED, 'current local path - where plugins are built to' ],
|
|
[ '--dest_local_path', '-dp', OS::Getopt::REQUIRED, 'dest local path where p-lugins are copied to for subsequent checkin' ],
|
|
[ '--project', '-p', OS::Getopt::REQUIRED, 'specify project key (e.g. gta5, jimmy)' ],
|
|
[ '--branch', '-b', OS::Getopt::REQUIRED, 'specify project branch (e.g. dev, dev_migrate)' ]
|
|
]
|
|
|
|
|
|
TRAILING_DESC = 'solution filenames separated by spaces.'
|
|
|
|
REGEXP_ERROR = /^(.+)(Error\s:|Error:\s|Error\s|failed )(.+)$/i
|
|
|
|
INFO = "[colourise=black]INFO_MSG: "
|
|
INFO_BLUE = "[colourise=blue]INFO_MSG: "
|
|
|
|
IB_EXE_86 = "c:/Program Files (x86)/Xoreax/IncrediBuild/BuildConsole.exe"
|
|
IB_EXE = "c:/Program Files/Xoreax/IncrediBuild/BuildConsole.exe"
|
|
|
|
|
|
class PluginBuilder
|
|
|
|
#
|
|
#
|
|
#
|
|
def initialize( current_local_path, dest_local_path, build_config, build_folder, solutions, dev_branch )
|
|
@current_local_path = current_local_path
|
|
@dest_local_path = dest_local_path
|
|
@build_config = build_config
|
|
@solutions = solutions
|
|
@dev_branch = dev_branch
|
|
@build_folder = build_folder
|
|
|
|
puts "#{INFO_BLUE} PluginBuilder: dev_branch #{@dev_branch}"
|
|
puts "#{INFO_BLUE} PluginBuilder: current_local_path #{@current_local_path}"
|
|
puts "#{INFO_BLUE} PluginBuilder: dest_local_path #{@dest_local_path}"
|
|
puts "#{INFO_BLUE} PluginBuilder: build_config #{@build_config}"
|
|
puts "#{INFO_BLUE} PluginBuilder: build_folder #{@build_folder}"
|
|
end
|
|
|
|
#
|
|
#
|
|
#
|
|
def sync( )
|
|
# --- SYNC CODE ---
|
|
# later can provide functionality for this much like the data_get_latest_all
|
|
puts "#{INFO_BLUE} Syncing to all in rage, so we can build the code with the latest src & deps."
|
|
@p4_rage.run_sync( )
|
|
|
|
# --- SYNC PLUGINS ---
|
|
puts "#{INFO_BLUE} Syncing to current plugins."
|
|
@p4.run_sync( @current_depot_path )
|
|
|
|
puts "#{INFO_BLUE} Syncing to destination plugins."
|
|
@p4.run_sync( @dest_depot_path )
|
|
rescue Exception => ex
|
|
$stderr.puts "Error: Unhandled exception in PluginBuilder.sync: #{ex.message}"
|
|
$stderr.puts "Backtrace:"
|
|
ex.backtrace.each { |m| $stderr.puts "\t#{m}" }
|
|
end
|
|
|
|
#
|
|
#
|
|
#
|
|
def p4_connect( config )
|
|
# --- CONNECT TO P4 ---
|
|
if (@dev_branch)
|
|
puts "building plugins on the dev branch"
|
|
@p4 = SCM::Perforce::create( config.sc_rage_server, config.sc_rage_username, config.sc_rage_workspace )
|
|
else
|
|
puts "building plugins on the release branch"
|
|
@p4 = SCM::Perforce::create( config.sc_server, config.sc_username, config.sc_workspace )
|
|
end
|
|
@p4_rage = SCM::Perforce::create( config.sc_rage_server, config.sc_rage_username, config.sc_rage_workspace )
|
|
|
|
@p4.connect( )
|
|
raise Exception if not @p4.connected?
|
|
|
|
@p4_rage.connect()
|
|
raise Exception if not @p4_rage.connected?
|
|
rescue Exception => ex
|
|
$stderr.puts "Error: Unhandled exception in PluginBuilder.p4_connect: #{ex.message}"
|
|
$stderr.puts "Backtrace:"
|
|
ex.backtrace.each { |m| $stderr.puts "\t#{m}" }
|
|
end
|
|
|
|
#
|
|
#
|
|
#
|
|
def get_paths( )
|
|
# --- CALC PATHS ---
|
|
dcc_path = OS::Path::normalise( "#{Pipeline::Config::instance().toolsroot}/dcc" )
|
|
@current_local_path = OS::Path::combine( dcc_path, @current_local_path )
|
|
@dest_local_path = OS::Path::combine( dcc_path, @dest_local_path )
|
|
|
|
@current_depot_path = @p4.local2depot( "#{@current_local_path}..." )
|
|
@dest_depot_path = @p4.local2depot( "#{@dest_local_path}..." )
|
|
|
|
raise Exception if @current_depot_path.nil?
|
|
raise Exception if @dest_depot_path.nil?
|
|
|
|
puts "Plugins will be built to #{@current_depot_path}"
|
|
puts "Plugins will be then be moved to #{@dest_depot_path} for subsequent checkin."
|
|
rescue Exception => ex
|
|
$stderr.puts "Error: Unhandled exception in PluginBuilder.get_paths: #{ex.message}"
|
|
$stderr.puts "Backtrace:"
|
|
ex.backtrace.each { |m| $stderr.puts "\t#{m}" }
|
|
end
|
|
|
|
#
|
|
# --- GET FILES IN p4 TO BUILD ---
|
|
#
|
|
def get_current_files( )
|
|
# We assume that these are the only files required in the dest configuration also.
|
|
@current_depot_filenames, @current_local_filenames = [], []
|
|
|
|
fstat_results = @p4.run_fstat( @current_depot_path )
|
|
raise Exception if fstat_results.nil?
|
|
|
|
fstat_results.each do |fstat|
|
|
if not fstat['headAction'].include?("delete")
|
|
@current_depot_filenames << fstat['depotFile']
|
|
end
|
|
end
|
|
|
|
raise Exception if @current_depot_filenames.length==0
|
|
@current_depot_filenames.each { |f| puts f }
|
|
|
|
@current_depot_filenames.each do |current_depot_filename|
|
|
current_local_filename = OS::Path::normalise( @p4.depot2local( current_depot_filename ) )
|
|
@current_local_filenames << current_local_filename
|
|
end
|
|
|
|
rescue Exception => ex
|
|
$stderr.puts "Error: Unhandled exception in PluginBuilder.get_currrent_files: #{ex.message}"
|
|
$stderr.puts "Backtrace:"
|
|
ex.backtrace.each { |m| $stderr.puts "\t#{m}" }
|
|
end
|
|
|
|
#
|
|
# --- GET FILES IN p4 TO UPDATE ---
|
|
#
|
|
def get_dest_files( )
|
|
|
|
# --- check out destination files ---
|
|
@dest_depot_filenames, @dest_local_filenames = [], []
|
|
|
|
fstat_results = @p4.run_fstat( @dest_depot_path )
|
|
raise Exception if fstat_results.nil?
|
|
|
|
fstat_results.each { |fstat| @dest_depot_filenames << fstat['depotFile'] }
|
|
raise Exception if @dest_depot_filenames.length==0
|
|
|
|
@dest_depot_filenames.each do |dest_depot_filename|
|
|
dest_local_filename = OS::Path::normalise( @p4.depot2local( dest_depot_filename ) )
|
|
@dest_local_filenames << dest_local_filename
|
|
end
|
|
rescue Exception => ex
|
|
$stderr.puts "Error: Unhandled exception in PluginBuilder.get_dest_files: #{ex.message}"
|
|
$stderr.puts "Backtrace:"
|
|
ex.backtrace.each { |m| $stderr.puts "\t#{m}" }
|
|
end
|
|
|
|
#
|
|
#
|
|
#
|
|
def create_dest_changelist( )
|
|
|
|
@change_id_dest = @p4.create_changelist( "Automated Build of Plugins @ #{Time.now}" )
|
|
|
|
raise Exception if @change_id_dest.nil?
|
|
puts "#{INFO_BLUE}Changelist #{@change_id_dest} is created"
|
|
|
|
puts "#{INFO_BLUE}Checking out #{@dest_depot_filenames.length} files"
|
|
@dest_depot_filenames.each do |dest_depot_filename|
|
|
puts "#{INFO_BLUE}Checking out #{dest_depot_filename} in CL #{@change_id_dest.to_s}"
|
|
puts "Checking out #{dest_depot_filename} in CL #{@change_id_dest.to_s}"
|
|
@p4.run_edit( '-c', @change_id_dest.to_s, dest_depot_filename )
|
|
end
|
|
rescue Exception => ex
|
|
$stderr.puts "Error: Unhandled exception in PluginBuilder.create_dest_changelist: #{ex.message}"
|
|
$stderr.puts "Backtrace:"
|
|
ex.backtrace.each { |m| $stderr.puts "\t#{m}" }
|
|
end
|
|
|
|
#
|
|
#
|
|
#
|
|
def create_current_changelist( )
|
|
if ( @current_local_path == @dest_local_path )
|
|
puts "#{INFO_BLUE} The source path is the same as the destination - no extra CL required."
|
|
return nil
|
|
end
|
|
|
|
# --- CREATE CHANGELIST ---
|
|
@change_id_current = @p4.create_changelist( "DO NOT SUBMIT DO_NOT_SUBMIT - current plugins built with debug." )
|
|
raise Exception if @change_id_current.nil?
|
|
puts "#{INFO_BLUE}Changelist #{@change_id_current} is created for current plugins."
|
|
|
|
# --- CHECKOUT FILES ---
|
|
puts "#{INFO_BLUE}Checking out #{@current_depot_filenames.length} files"
|
|
@current_depot_filenames.each do |current_depot_filename|
|
|
puts "#{INFO_BLUE}Checking out #{current_depot_filename} in CL #{@change_id_current.to_s}"
|
|
puts "Checking out #{current_depot_filename} in CL #{@change_id_current.to_s}"
|
|
@p4.run_edit( '-c', @change_id_current.to_s, current_depot_filename )
|
|
end
|
|
rescue Exception => ex
|
|
$stderr.puts "Error: Unhandled exception in PluginBuilder.create_current_changelist: #{ex.message}"
|
|
$stderr.puts "Backtrace:"
|
|
ex.backtrace.each { |m| $stderr.puts "\t#{m}" }
|
|
end
|
|
|
|
#
|
|
# --- REVERT UNCHANGED FILES ---
|
|
#
|
|
def revert_unchanged( )
|
|
puts "#{INFO_BLUE}Reverting unchanged files #{@change_id_dest}"
|
|
@p4.run_revert( '-a', '-c', @change_id_dest.to_s, '//...')
|
|
rescue Exception => ex
|
|
$stderr.puts "Error: Unhandled exception in PluginBuilder.revert_unchanged: #{ex.message}"
|
|
$stderr.puts "Backtrace:"
|
|
ex.backtrace.each { |m| $stderr.puts "\t#{m}" }
|
|
end
|
|
|
|
#
|
|
# --- DELETE OR SUBMIT CHANGELIST ---
|
|
#
|
|
def submit_changelist( )
|
|
files = @p4.run_opened( '-c', @change_id_dest.to_s )
|
|
raise Exception if files.nil?
|
|
|
|
puts "#{INFO_BLUE}There are #{files.size} files to submit."
|
|
files.each do |file|
|
|
puts "#{INFO_BLUE}#{file['depotFile']} has been updated and will be submitted."
|
|
end
|
|
|
|
if ( files.size > 0 ) then
|
|
puts "#{INFO_BLUE}Submitting File currently in #{@change_id_dest}"
|
|
submit_result = @p4.run_submit( '-c', @change_id_dest.to_s )
|
|
puts submit_result.to_s
|
|
# submit_result.each do |sr|
|
|
# puts "Checking in files (#{files.size}) in CL #{sr['submittedChange'].to_s}..." if sr.has_key?('submittedChange')
|
|
# end
|
|
elsif ( 0 == files.size ) then
|
|
puts "#{INFO_BLUE}Deleting #{@change_id_dest} no files changed."
|
|
@p4.run_change('-d', @change_id_dest.to_s)
|
|
end
|
|
rescue Exception => ex
|
|
$stderr.puts "Error: Unhandled exception in PluginBuilder.submit_changelist: #{ex.message}"
|
|
$stderr.puts "Backtrace:"
|
|
ex.backtrace.each { |m| $stderr.puts "\t#{m}" }
|
|
end
|
|
|
|
#
|
|
#
|
|
#
|
|
def revert_changelist( change_id )
|
|
if (change_id)
|
|
|
|
# --- REVERT DEST FILES ---
|
|
puts "#{INFO_BLUE}Reverting files #{change_id}"
|
|
@p4.run_revert( '-c', change_id.to_s, '//...')
|
|
|
|
# --- REVERT DEST CHANGELIST ---
|
|
puts "#{INFO_BLUE}Delete CL #{change_id}"
|
|
@p4.run_change('-d', change_id.to_s)
|
|
end
|
|
rescue Exception => ex
|
|
$stderr.puts "Error: Unhandled exception in PluginBuilder.revert_changelist: #{ex.message}"
|
|
$stderr.puts "Backtrace:"
|
|
ex.backtrace.each { |m| $stderr.puts "\t#{m}" }
|
|
|
|
end
|
|
|
|
#
|
|
# --- BUILD ---
|
|
#
|
|
def build( )
|
|
|
|
built_ok = true
|
|
error_count = 0
|
|
|
|
puts "#{INFO_BLUE}Building #{@current_depot_filenames.length} files @ #{Time.now}..."
|
|
|
|
exe = File.exist?( IB_EXE ) ? IB_EXE : IB_EXE_86
|
|
|
|
|
|
if (not File.exist?(exe) )
|
|
$stderr.puts "IB does not exists #{exe}"
|
|
end
|
|
|
|
@solutions.each do |solution|
|
|
args = "/rebuild /cfg=\"#{@build_config}\""
|
|
cmd = "\"#{exe}\" #{solution} #{args}"
|
|
puts "#{INFO_BLUE}#{Time.now} #{cmd}"
|
|
|
|
status, stdout, stderr = systemu(cmd)
|
|
# Grrr Incredibuild doesn't output to stderr! - nor is the return code of any sensible value I can determine :(
|
|
|
|
#built_ok = false if ( status != 0 )
|
|
|
|
$stderr.puts( cmd ) if ( stderr.length > 0 )
|
|
|
|
stderr.each do |err|
|
|
error_count += 1
|
|
$stderr.puts "#{solution} #{@build_config} Error: #{err}"
|
|
end
|
|
|
|
stdout.each do |out|
|
|
puts out
|
|
if (out =~ REGEXP_ERROR)
|
|
error_count += 1
|
|
$stderr.puts "#{solution} #{@build_config} Error: #{out}"
|
|
end
|
|
end
|
|
|
|
puts "#{INFO_BLUE}#{Time.now} result status #{status} Errors #{error_count} Output Lines #{stdout.length}"
|
|
end
|
|
|
|
puts "#{INFO_BLUE}Built All. @ #{Time.now} ( #{error_count} total errors encountered )"
|
|
|
|
return built_ok, error_count
|
|
rescue Exception => ex
|
|
$stderr.puts "Error: Unhandled exception in PluginBuilder.build: #{ex.message}"
|
|
$stderr.puts "Backtrace:"
|
|
ex.backtrace.each { |m| $stderr.puts "\t#{m}" }
|
|
end
|
|
|
|
#
|
|
#
|
|
#
|
|
def copy_and_add_to_changelist( )
|
|
puts "#{INFO_BLUE} current local path #{@current_local_path}"
|
|
puts "#{INFO_BLUE} dest local path #{@dest_local_path}"
|
|
if ( @current_local_path != @dest_local_path )
|
|
# --- COPY CURRENT(RELEASE) PLUGINS OVER THE DEBUG ONES ---
|
|
# IF this is a new file add it to changelist.
|
|
puts "#{INFO_BLUE} paths differ so copy will take place."
|
|
@current_local_filenames.each do |current_local_filename|
|
|
dest_local_filename = current_local_filename.sub( "current", @build_folder )
|
|
puts "#{INFO_BLUE}copy #{current_local_filename} => {dest_local_filename}"
|
|
FileUtils.copy( current_local_filename, dest_local_filename )
|
|
|
|
puts "#{INFO_BLUE}Adding #{dest_local_filename} to CL #{@change_id_dest.to_s} as it is new."
|
|
@p4.run_add( '-c', @change_id_dest.to_s, dest_local_filename )
|
|
end
|
|
end
|
|
|
|
# --- GET A LIST OF ALL THE FILES IN DEST AND DECIDE IF TO ADD THESE TO THE CL ---
|
|
#puts "#{INFO_BLUE} Getting a list of files"
|
|
#found_files = OS::FindEx::find_files( OS::Path::combine( current_local_path, "*.d*") )
|
|
#found_files.each do |file|
|
|
# puts "#{INFO_BLUE} Adding #{file} to CL change_id.to_s"
|
|
# p4.run_add( '-c', change_id.to_s, file )
|
|
#end
|
|
rescue Exception => ex
|
|
$stderr.puts "Error: Unhandled exception in PluginBuilder.copy_and_add_to_changelist: #{ex.message}"
|
|
$stderr.puts "Backtrace:"
|
|
ex.backtrace.each { |m| $stderr.puts "\t#{m}" }
|
|
end
|
|
|
|
#
|
|
#
|
|
#
|
|
def process( config )
|
|
built_ok = false
|
|
error_count = 0
|
|
|
|
#---------------------------------------------------------------------
|
|
#--- MAIN BIT ---
|
|
#---------------------------------------------------------------------
|
|
puts "#{INFO_BLUE} Building Max Plugins."
|
|
|
|
p4_connect( config )
|
|
|
|
get_paths( )
|
|
|
|
sync( )
|
|
|
|
get_current_files( )
|
|
|
|
create_current_changelist( )
|
|
|
|
get_dest_files( )
|
|
|
|
create_dest_changelist( )
|
|
|
|
built_ok, error_count = build( )
|
|
|
|
if ( error_count == 0 and built_ok )
|
|
|
|
puts "#{INFO_BLUE} The plugins are to be copied as there where no errors and it built ok."
|
|
|
|
copy_and_add_to_changelist( )
|
|
|
|
revert_unchanged( )
|
|
|
|
submit_changelist( )
|
|
|
|
else
|
|
puts "#{INFO_BLUE} The plugins are NOT copied as there where either errors or it did not build ok."
|
|
revert_changelist( @change_id_dest )
|
|
end
|
|
|
|
revert_changelist( @change_id_current )
|
|
|
|
puts "#{INFO_BLUE} Finished building Max Plugins."
|
|
|
|
return built_ok, error_count
|
|
rescue Exception => ex
|
|
$stderr.puts "Error: Unhandled exception in PluginBuilder.process: #{ex.message}"
|
|
$stderr.puts "Backtrace:"
|
|
ex.backtrace.each { |m| $stderr.puts "\t#{m}" }
|
|
end
|
|
end
|
|
#-----------------------------------------------------------------------------
|
|
# Application entry point
|
|
#-----------------------------------------------------------------------------
|
|
|
|
begin
|
|
g_AppName = File::basename( __FILE__, '.rb' )
|
|
g_ProjectName = ''
|
|
g_BranchName = ''
|
|
g_Project = nil
|
|
g_Config = Pipeline::Config.instance()
|
|
|
|
#---------------------------------------------------------------------
|
|
# --- PARSER COMMAND LINE ---
|
|
#---------------------------------------------------------------------
|
|
opts, trailing = OS::Getopt.getopts( OPTIONS )
|
|
if ( opts['help'] )
|
|
puts OS::Getopt.usage( OPTIONS )
|
|
puts ("Press Enter to continue...")
|
|
$stdin.getc( )
|
|
Process.exit!( 1 )
|
|
end
|
|
|
|
if ( 0 == trailing.size ) then
|
|
puts 'No trailing file arguments specified. Exiting.'
|
|
puts OS::Getopt.usage( OPTIONS, { 'files' => TRAILING_DESC } )
|
|
Process.exit!( 2 )
|
|
end
|
|
|
|
dev_branch = opts['dev']
|
|
current_local_path = opts['current_local_path']
|
|
dest_local_path = opts['dest_local_path']
|
|
build_config = opts['buildconfig']
|
|
build_folder = opts['buildfolder']
|
|
solutions = trailing
|
|
|
|
puts "#{INFO_BLUE} dev_branch #{dev_branch}"
|
|
puts "#{INFO_BLUE} current_local_path #{current_local_path}"
|
|
puts "#{INFO_BLUE} dest_local_path #{dest_local_path}"
|
|
puts "#{INFO_BLUE} build_config #{build_config}"
|
|
puts "#{INFO_BLUE} build_folder #{build_folder}"
|
|
|
|
g_ProjectName = ( nil == opts['project'] ) ? '' : opts['project']
|
|
project_exists = ( g_Config.projects.has_key?( g_ProjectName ) )
|
|
if ( not project_exists ) then
|
|
puts OS::Getopt.usage( OPTIONS )
|
|
puts "\nError project: #{g_ProjectName} does not exist or its configuration is unreadable."
|
|
Process.exit!( 2 )
|
|
end
|
|
g_Project = g_Config.projects[ g_ProjectName ]
|
|
g_Project.load_config( )
|
|
if ( not g_Project.enabled ) then
|
|
puts "\nError project: #{g_ProjectName} is not enabled on this machine. Re-run installer."
|
|
Process.exit!( 3 )
|
|
end
|
|
g_BranchName = ( nil == opts['branch'] ) ? g_Project.default_branch : opts['branch']
|
|
if ( not g_Project.branches.has_key?( g_BranchName ) ) then
|
|
puts "\nError project: #{g_ProjectName} does not have branch #{g_BranchName} defined."
|
|
Process.exit!( 4 )
|
|
end
|
|
|
|
# ------------ The juicy bits ---------------
|
|
plugin_builder = PluginBuilder.new( current_local_path, dest_local_path, build_config, build_folder, solutions, dev_branch )
|
|
built_ok, error_count = plugin_builder.process( g_Config )
|
|
|
|
if error_count > 0 or not built_ok
|
|
puts "#{INFO_BLUE} The ruby process is returning -1 built ok #{built_ok} error_count #{error_count}"
|
|
Process.exit! -1
|
|
end
|
|
|
|
puts "#{INFO_BLUE} The ruby process is returning 0 built ok #{built_ok} error_count #{error_count}"
|
|
Process.exit! 0
|
|
# ------------ End juicy bits ---------------
|
|
|
|
rescue Exception => ex
|
|
$stderr.puts "Error: Unhandled exception: #{ex.message}"
|
|
$stderr.puts "Backtrace:"
|
|
ex.backtrace.each { |m| $stderr.puts "\t#{m}" }
|
|
Process.exit! -1
|
|
end
|