Files
gtav-src/tools_ng/lib/util/build_max_plugins.rb
T
2025-09-29 00:52:08 +02:00

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