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

395 lines
12 KiB
Ruby
Executable File

#
# File:: cc_make_assetbuilder_rebuild.rb
# Description:: makes an assetbuilder rebuild file for inclusion in cruise control configs.
#
# Author:: Derek Ward <derek.ward@rockstarnorth.com>
# Date:: 02nd September 2011
#
# Passed in :- see OPTIONS ...
# Passed out :-
#
# Returns :-
#-----------------------------------------------------------------------------
# Uses / Requires
#-----------------------------------------------------------------------------
require 'pipeline/os/path'
require 'pipeline/os/getopt'
require 'pipeline/log/log'
require 'xml'
require "rexml/document"
require 'find'
include REXML
include Pipeline
#-----------------------------------------------------------------------------
# Constants
#-----------------------------------------------------------------------------
INFO_BLUE = "[colourise=blue]INFO_MSG: "
HELP_URL = "https://devstar.rockstargames.com/wiki/index.php/Asset_Builder_-_Rebuilds" # todo - add wiki
REBUILD_OUTPUT_FILE = "assetbuilder_rebuilds"
NAMESPACE = "urn:ccnet.config.builder"
CB_SCOPE = 'cb:scope'
CB_NAMESPACE = 'xmlns:cb'
CB_INCLUDE = "cb:include"
MACHINE = ENV["COMPUTERNAME"]
REMOTING_PORT = "21234"
REMOTING_FILE = "CruiseManager.rem"
SERVER_URL = "tcp://#{MACHINE}:#{REMOTING_PORT}/#{REMOTING_FILE}"
CCTRAY_PROJECTS_XPATH = "Configuration/Projects"
CCTRAY_ALL_PROJECTS_XPATH = 'Configuration/Projects/Project'
KEEP_CCTRAY_PROJECT_REGEXP = /^(assetbuilder)/
ENV_SYMBOLS = [ "RS_EXPORT", "RAGE_DIR", "RS_TOOLSSRC", "RS_TOOLSCONFIG", "RS_CODEBRANCH", "RAGE_3RDPARTY", "RAGE_3RDPARTY", "RS_PROJECT" ]
EXCLUSIONS_XPATH = "//rebuilds/exclusion"
FOLDERS_XPATH = "//rebuilds/folder"
DEFAULT_BRANCH = "dev"
REBUILD_CLASS = "auto"
CCTRAY_TO_RUN = "%RS_TOOLSROOT%/script/util/CruiseControl/CCTray/cctray_assetbuild.bat"
NO_RECURSE_CHARACTER = "<" # TODO: DW - ideally this should be declared in only one place!
#
# Rebuild class
class Rebuild
attr_accessor :folders_with_underbars, :folders, :wildcard, :full_rebuild_name
def initialize(folder, variant="")
puts "create rebuild #{folder}"
folder_names = folder.split("/")
@folders_with_underbars = folder_names.join("_")
@folders ="#{NO_RECURSE_CHARACTER}#{folder}"
@full_rebuild_name = "assetbuilder_#{ENV["RS_PROJECT"]}_#{DEFAULT_BRANCH}_rebuild_#{REBUILD_CLASS}_#{@folders_with_underbars}#{variant}"
@wildcard ="*.*"
end
def to_s()
"#{@folders_with_underbars.ljust(40)} #{@folders.ljust(40)} #{@wildcard.ljust(5)}"
end
end
# CC make
class CCMakerAssetbuildRebuild
def initialize(variant)
@config = Pipeline::Config.instance
@config.project.load_config
@env = Environment.new()
@config.project.fill_env( @env )
ENV_SYMBOLS.each { |sym| @env.add(sym,ENV[sym]) }
@p4 = SCM::Perforce.new()
@p4.port = @config.sc_server
@p4.client = @config.sc_workspace
@p4.user = @config.sc_username
@p4.connect()
@folders = []
@exclusions = []
@rebuilds = []
@variant = variant
end
#
# main control process
def process(filenames, targetDir, cctray_file, submit )
make( filenames )
save( targetDir )
update_cctray( cctray_file )
submit_or_revert(submit)
true
end
#
# process exclusions & folders
def make( filenames )
filenames.each do |filename|
xmldoc = LibXML::XML::Document::file( filename )
exclusion_nodes = xmldoc.find(EXCLUSIONS_XPATH)
exclusion_nodes.each do |node|
exclusion = @env.subst(node.attributes["path"])
exclusion = OS::Path::normalise(exclusion)
@exclusions << exclusion
puts "#{INFO_BLUE} Exclusion registered #{exclusion}"
end
folder_nodes = xmldoc.find(FOLDERS_XPATH)
folder_nodes.each do |node|
folder = @env.subst(node.attributes["path"])
folder = OS::Path::normalise(folder)
@folders << folder
puts "#{INFO_BLUE} Folder registered #{folder}"
end
end
end
#
# save files
def save( target_dir )
#FileUtils::mkdir_p( OS::Path::get_directory( filename ) ) unless ( File::directory?( filename ) )
@change_id = @p4.create_changelist( "Automatically generated by cc_make_assetbuilder_rebuild.rb" )
save_target_list( target_dir )
end
# submit or revert files
def submit_or_revert( submit )
@p4.run_revert( '-a', '-c', @change_id.to_s, '//...')
files = @p4.run_opened( '-c', @change_id.to_s )
if ( files.size > 0 )
if (submit)
puts "#{INFO_BLUE} Submitting #{files.size} files in CL #{@change_id}"
puts "Warning: #{CCTRAY_TO_RUN} should be run to update your cctray."
files.each {|file| puts "#{INFO_BLUE} #{file['depotFile']}" }
submit_result = @p4.run_submit( '-c', @change_id.to_s )
else
puts "#{INFO_BLUE} Pending #{files.size} files in CL #{@change_id}"
end
elsif ( 0 == files.size )
puts "#{INFO_BLUE} No files changed - CL #{@change_id} deleted"
@p4.run_change('-d', @change_id.to_s)
end
end
#
# Update the CCTray config file
def update_cctray( cctray_file )
@p4.run_sync( cctray_file )
fstat = @p4.run_fstat( cctray_file ).shift
basetype, modifiers = fstat['headType'].split( '+' ) if (fstat.key?('headType')) # perforce drives me dippy.
basetype, modifiers = fstat['type'].split( '+' ) if (fstat.key?('type'))
@p4.run_edit_or_add( '-c', @change_id.to_s, cctray_file )
@p4.run_reopen( '-c', @change_id, cctray_file )
@p4.run_reopen( '-t', "#{basetype}+w", '-c', @change_id.to_s, cctray_file )
xmldoc = REXML::Document.new File.open( cctray_file, 'r' )
# new working element store
new_projects_elem = Element.new( "Projects" )
# keep some existing elements
xmldoc.elements.each( CCTRAY_ALL_PROJECTS_XPATH ) do |elem|
if elem.attributes["projectName"] =~ KEEP_CCTRAY_PROJECT_REGEXP
if not elem.attributes["projectName"].include?("_rebuild_#{REBUILD_CLASS}")
puts "#{INFO_BLUE} Keeping CCtray project #{elem.attributes["projectName"]}"
new_projects_elem.add_element(elem.clone)
end
end
end
# add all elements as defined by solutions
@rebuilds.each do |rebuild|
found = false
project_name = rebuild.full_rebuild_name
#puts "#{INFO_BLUE} Adding CCtray project #{project_name}"
elem = Element.new( "Project" )
elem.add_attribute("serverUrl", SERVER_URL)
elem.add_attribute("projectName", project_name)
elem.add_attribute("showProject", "true")
new_projects_elem.add_element(elem)
end
# switch to the new element in the xmldoc
xmldoc.delete_element(CCTRAY_PROJECTS_XPATH)
xmldoc.root.add_element(new_projects_elem)
File.open( cctray_file, 'w' ) do |file|
fmt = REXML::Formatters::Default.new()
fmt.write(xmldoc,file)
end
end
private
#
# Save a list of targets
def save_target_list( target_dir )
puts "#{INFO_BLUE} Generating Rebuilds"
generate_rebuilds()
puts "#{INFO_BLUE} Rebuilds to XML"
xmldoc = rebuilds_to_xml( )
dest_file = OS::Path.combine(target_dir, "#{REBUILD_OUTPUT_FILE}#{@variant}.xml")
puts "#{INFO_BLUE} Writing #{dest_file}"
puts "#{INFO_BLUE} Sync #{dest_file}"
@p4.run_sync( dest_file )
puts "#{INFO_BLUE} Edit or add #{dest_file}"
@p4.run_edit_or_add( '-c', @change_id.to_s, dest_file ) #
puts "#{INFO_BLUE} Reopen #{dest_file}"
@p4.run_reopen( '-c', @change_id.to_s, dest_file )
xmldoc.save( dest_file )
end
#
# Discover folders and files and make rebuild specifications
def generate_rebuilds( )
# for each folder - find folders that have filenames and add just these files to a rebuild, this will also catch leaf directories and non leaf directories with files in them
rebuild_folders = []
@folders.each do |folder|
puts "#{INFO_BLUE} Processing folder #{folder}"
Find.find(folder) do |path|
if FileTest.directory?(path)
prune = false
@exclusions.each do |exclusion|
prune = true if OS::Path.normalise(path).include?(exclusion)
end
if (prune)
puts "#{INFO_BLUE} Pruned #{path}"
Find.prune # Don't look any further into this directory.
else
next
end
else
path_to_add = OS::Path.normalise("#{OS::Path::remove_filename(path)}".downcase)
path_to_add = path_to_add.sub(OS::Path.normalise("#{ENV["RS_EXPORT"].downcase}/"), "")
next if path_to_add.include?(":")# DW - I don;t understand this bug for some reason the RS_EXPORT gsub doesn't work in one occassion
rebuild_folders << path_to_add unless rebuild_folders.include?(path_to_add)
end
end
end
rebuild_folders.each do |rebuild_folder|
rebuild = Rebuild.new(rebuild_folder,@variant)
puts "#{INFO_BLUE} Rebuild : #{rebuild}"
@rebuilds << rebuild
end
end
#
# Get xml doc for target file
def rebuilds_to_xml( )
xmldoc = XML::Document.new
xmldoc.encoding = XML::Encoding::UTF_8
xmldoc.root = XML::Node.new( CB_SCOPE )
xmldoc.root.attributes['xmlns:cb'] = NAMESPACE
@rebuilds.each do |rebuild|
node = XML::Node.new( CB_SCOPE )
node.attributes['subvariant'] = "_#{REBUILD_CLASS}_#{rebuild.folders_with_underbars}"
node.attributes['wildcard'] = "#{rebuild.folders}\\#{rebuild.wildcard}"
include = XML::Node.new( CB_INCLUDE )
include.attributes['href'] = "$(base_file)"
node << include
xmldoc.root << node
end
xmldoc
end
# log
def self.log( )
@@log = Log.new( 'cc_make_assetbuilder_rebuild' ) if ( @@log.nil? )
@@log
end
@@log = nil
end
#----------------------------------------------------------------------------
# Implementation
#----------------------------------------------------------------------------
if ( __FILE__ == $0 ) then
OPTIONS = [
[ "--help", "-h", OS::Getopt::BOOLEAN, "display usage information." ],
[ '--target_dir', '-t', OS::Getopt::REQUIRED, "directory to output results to" ],
[ '--cctray' , '-c', OS::Getopt::OPTIONAL, "cctray file to update" ],
[ '--submit' , '-s', OS::Getopt::BOOLEAN, "submit or revert" ],
[ '--variant' , '-v', OS::Getopt::OPTIONAL, "type of assetbuilder we are making rebuilds for eg. _hw " ],
]
begin
g_AppName = File::basename( __FILE__, '.rb' )
#---------------------------------------------------------------------
# Parse Command Line
#---------------------------------------------------------------------
opts, g_Trailing = OS::Getopt.getopts( OPTIONS )
if ( opts['help'] ) then
puts OS::Getopt.usage( OPTIONS )
exit( 1 )
end
if ( not opts['target_dir'] ) then
puts OS::Getopt.usage( OPTIONS )
exit( 1 )
end
if (g_Trailing.length <= 0)
puts OS::Getopt.usage( OPTIONS )
puts "Supply files to read"
exit( 1 )
end
g_TargetDir = opts['target_dir']
g_CCtrayFile = opts['cctray']
g_Submit = opts["submit"]
g_Variant = opts["variant"] ? opts["variant"] : ""
if (g_CCtrayFile)
filename = File::expand_path( g_CCtrayFile )
if (File.exist? filename)
g_CCtrayFile = filename
puts "#{INFO_BLUE} CCtray file found #{g_CCtrayFile}"
else
g_CCtrayFile = nil
$stderr.puts("Error: filename #{filename} not found")
end
end
g_Filenames = []
Pipeline::OS::Path::set_downcase_on_normalise( false )
g_Trailing.each_with_index do |filename, index|
filename = File::expand_path( filename )
if (File.exist? filename)
g_Filenames << filename
else
$stderr.puts("Error: filename #{filename} not found")
end
end
Pipeline::OS::Path::set_downcase_on_normalise( true )
# Make
puts "#{INFO_BLUE} #{HELP_URL}"
cc_maker = CCMakerAssetbuildRebuild.new(g_Variant)
ret = cc_maker.process( g_Filenames, g_TargetDir, g_CCtrayFile, g_Submit )
Process.exit! -2 unless ret
rescue Exception => ex
$stderr.puts "Error: Unhandled exception: #{ex.message}"
puts ex.backtrace().join("\n")
Process.exit! -1
end
Process.exit! 0
end
# %RS_TOOLSLIB%/util/CruiseControl/cc_make.rb