395 lines
12 KiB
Ruby
Executable File
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 |