# # File:: build_max_plugins.rb # Description:: # # Author:: Derek Ward # 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