Files
2025-09-29 00:52:08 +02:00

1091 lines
39 KiB
Ruby
Executable File

#
# File:: pipeline/config/projects.rb
# Description:: Global tools configuration classes
#
# Author:: David Muir <david.muir@rockstarnorth.com>
# Author:: Greg Smith <greg@rockstarnorth.com>
# Author:: Luke Openshaw <luke.openshaw@rockstarnorth.com>
#
# Date:: 3 December 2007
#
#-----------------------------------------------------------------------------
# Uses
#-----------------------------------------------------------------------------
require 'pipeline/config/project'
require 'pipeline/config/globals'
require 'pipeline/config/runlines'
require 'pipeline/config/user'
require 'pipeline/gui/messagebox'
require 'pipeline/os/path'
require 'pipeline/scm/perforce'
require 'pipeline/util/autodesk3dsmax'
require 'pipeline/util/autodeskMotionbuilder'
require 'pipeline/os/sysinfo'
require 'singleton'
require 'rexml/document'
require 'ping'
include REXML
#-----------------------------------------------------------------------------
# Implementation
#-----------------------------------------------------------------------------
module Pipeline
#
# == Description
# Platform abstraction class.
#
class Platform
attr_reader :name, :uiname
def initialize( name, uiname )
@name = name
@uiname = uiname
end
def <=>(otherEntry)
return (@name <=> otherEntry.name)
end
end
#
# == Description
# Options per max version
#
class MaxVersionOptions
attr_reader :version, :check_for_install
def initialize( version, check_for_install )
@version = version
@check_for_install = check_for_install
end
end
#
# == Description
# Overall options for all versions of max
#
class MaxOptions
attr_reader :version_options
attr_reader :toolset_mode
attr_reader :toolset_modes
attr_writer :toolset_mode
def initialize()
@toolset_modes = Array.new()
@toolset_modes << :current
@toolset_modes << :debug
@toolset_modes << :outsource
@version_options = Hash.new()
@toolset_mode = :current
end
end
#
# == Description
# The Config class is used to fetch information from the overall Pipeline
# configuration file. This class is a singleton to allow easy access
# throughout the pipeline scripts and is initialised automatically on
# first access. See Example Usage below.
#
# == Example Usage
#
# @p = Pipeline::Config.instance
# @p.projects.each_value do |proj|
#
# # Load project configs for those that exist
# proj.loadConfig() if File.exists?( OS::Path.combine( proj.root, "bin/config.xml" ) )
# next unless proj.loadedConfig
#
# puts "Loaded project: #{proj.uiname}."
#
# puts "Name: #{proj.uiname}"
# puts "Project root: #{proj.root}"
# puts "Number of targets: #{proj.targets.length}"
# puts "Targets:"
# proj.targets.each_value { |target| puts "Target: #{target.platform} #{target.directory}" }
# end
#
class Config
include Singleton
#---------------------------------------------------------------------
# Classes
#---------------------------------------------------------------------
#
# == Description
# Project XMLParseError exception class.
#
class XMLParseError < Exception; end
#---------------------------------------------------------------------
# Attributes
#---------------------------------------------------------------------
# Array of Project objects
attr_reader :project
attr_reader :projects
attr_reader :platforms
attr_reader :logmailtos
attr_reader :mailserver
attr_reader :mailport
attr_reader :maildomain
# Local user information
attr_accessor :user
attr_accessor :sc_enabled
attr_accessor :sc_server, :sc_workspace, :sc_username, :sc_replicaserver
attr_accessor :sc_rage_server, :sc_rage_workspace, :sc_rage_username
attr_accessor :sc_tools_server, :sc_tools_workspace, :sc_tools_username
# Log options
attr_accessor :logmailerrors, :logtostdout, :log_trace, :log_level
attr_accessor :log_generate_html
# Max options
attr_accessor :max_options
# Application Settings (really should abstract this, using IApplication interface or somesuch...)
attr_accessor :use_xge
# File installation
attr_reader :network_version # Version user should be at (from network file)
attr_reader :version # Global version on current machine (from <toolsroot>/config.xml)
attr_accessor :user_version # Local version on current machine (from <toolsroot>/local.xml)
attr_reader :install_lines # Array of RunLine objects to call during install
attr_reader :uninstall_lines # Array of RunLine objects to call during uninstall
attr_reader :sc
#---------------------------------------------------------------------
# Public Methods
#---------------------------------------------------------------------
# Define public methods for all Pipeline::Globals instance variables.
Pipeline::Globals::instance( ).instance_variables.each do |var|
# Add getter method to self.
sym = var.sub( '@', '')
define_method( sym ) {
self.instance_variable_get( var )
}
end
#
# Class constructor. Parses our global configuration and environment
# so we get an entry point to our project configuration. Individual
# project's configuration must be loaded manually.
#
def initialize( )
# Initialise environment
@environment = Environment.new( )
import_globals( )
# Initialise defaults (to be overwritten later by the XML parser)
@user = nil
#@max_version_options = Hash.new()
@max_options = MaxOptions.new()
@sc_enabled = true
@sc_server = ""
@sc_workspace = ""
@sc_username = ENV['USERNAME']
@sc_rage_server = ""
@sc_rage_username = ENV['USERNAME']
@sc_rage_workspace = ""
@sc_tools_server = ""
@sc_tools_username = ENV['USERNAME']
@sc_tools_workspace = ""
@version = 0
@network_version = 0
@user_version = 0
@use_xge = true
@install_time = Time.new
config = OS::Path.combine( @toolsroot, DEFAULT_CONFIG )
localconfig = OS::Path.combine( @toolsroot, DEFAULT_LOCAL_CONFIG )
reset()
# Parse main config XML file
begin
File.open( config ) do |file|
doc = Document.new( file )
load_from( doc, @environment, 'config' )
end
rescue Exception => ex
puts "Exception parsing main config XML file: #{ex.message}"
puts ex.backtrace.join( "\n" )
end
# Parse local config XML file (if it exists)
if ( ::File::exists?( localconfig ) ) then
begin
File.open( localconfig ) do |file|
doc = Document.new( file )
load_from( doc, @environment, 'local' )
end
rescue Exception => ex
puts "Exception parsing local config XML file: #{ex.message}"
puts ex.backtrace.join( "\n" )
end
end
# Parse configuration against to acquire the overriding proxy server.
# This serves as a workaround for studios using the forwarding replica with
# this version of the P4Ruby library. (url:bugstar:1149226)
begin
File.open( config ) do |file|
doc = Document.new( file )
doc.elements.each( "config/studios/studio" ) do |studio_line|
if ( @local_studio == studio_line.attributes['name'] ) then
if ( not studio_line.attributes['perforcereplica'].nil? ) then
@sc_replicaserver = studio_line.attributes['perforcereplica']
end
end
end
end
rescue Exception => ex
puts "Exception parsing main config XML file: #{ex.message}"
puts ex.backtrace.join( "\n" )
end
if ( @sc_replicaserver != nil and @sc_replicaserver.empty? == false ) then
puts "Overriding main server with replica server #{@sc_replicaserver}. Forwarding replica workaround for P4Ruby library incompatibility.\n"
@sc_server = @sc_replicaserver
end
perforce_connect = true
# Validate that the local version of the framework is up-to date.
# Only attempt to do this when the framework has already been installed.
# If it has not been installed yet, the user/local version check will fail.
if ( not Globals::instance().skip_version_check and @sc_enabled)
if @sc_server != nil and @sc_server.empty? == false \
and @sc_workspace != nil and @sc_workspace.empty? == false \
and @sc_username != nil and @sc_username.empty? == false then
config_filename = "$(toolsroot)/" + DEFAULT_CONFIG
config_filename = envsubst(config_filename)
tokens = @sc_server.split(":")
check_server = tokens[0]
check_port = 1666
if tokens.count > 1 then
check_port = tokens[1].to_i
end
if Ping.pingecho(check_server,3,check_port) == true then
p4 = SCM::Perforce.new( )
p4.user = @sc_username
p4.client = @sc_workspace
p4.port = @sc_server
if ( p4.connect(true) == false )
puts "Unable to connect to Perforce to determine network configuration version status.\n"
else
fstat = p4.run_fstat( config_filename ).shift
network_config = p4.run("print", "-q", "#{config_filename}##{fstat['headRev']}")
# Parse main config XML file
begin
doc = Document.new( network_config.to_s )
load_from( doc, @environment, 'network' )
rescue Exception => ex
puts "Exception parsing main config XML file: #{ex.message}"
puts ex.backtrace.join( "\n" )
end
end
else
puts("*** tools version check: failed to connect to perforce to do at #{check_server} on port #{check_port} ***" )
perforce_connect = false
end
end
end
install_app = "#{OS::Path::combine( @toolsroot, 'install.bat' )}"
builder = @user.nil? ? false : ( @user.is_builder_client or @user.is_builder_server )
tools = @user.nil? ? false : @user.is_tools
# Validate we running in the right toolset for the configured
# project.
if ( not OS::Path::normalise( __FILE__.downcase ).starts_with( Globals::instance().toolsroot.downcase ) ) then
msg = "You are running a script from a different project's toolchain\n"
msg += "compared to the one you currently have installed.\n\n"
msg += "Please run the installer at #{install_app} or run the\n"
msg += "equivalent script under #{Globals::instance().toolsroot}.\n\n"
msg += "%RS_TOOLSROOT%: #{Globals::instance().toolsroot}\n"
msg += "__FILE__: #{OS::Path::normalise( __FILE__ )}"
GUI::MessageBox::critical( TITLE, msg )
abort( )
end
# Verify network and config versions; unless you are one of the
# build machines.
if (perforce_connect and ( ( not builder ) or ( tools ) ) ) then
if ( ( not defined? $no_network_check ) or $no_network_check.nil? or $no_network_check == false) then
if ( @network_version > @version ) then
msg = "An important update has been made to the tool chain.\n\n"
msg += "Fetch latest or labelled tools and run #{install_app}."
GUI::MessageBox::critical( TITLE, msg )
abort( )
end
end
# Verify local versions are up-to-date.
if ( ( not defined? $no_project_check ) or $no_project_check.nil? or $no_project_check == false) then
# Verify user and config versions.
if ( @user_version < @version ) then
msg = "An important update has been made to the tool chain.\n\n"
msg += "Fetch latest or labelled tools and run #{install_app}."
GUI::MessageBox::critical( TITLE, msg )
abort( )
end
end
end
@sc = nil
end
#
# Returns true if there is a local.xml for the pipeline
#
def local_config_exists?()
localconfig = OS::Path.combine( @toolsbin, DEFAULT_LOCAL_CONFIG )
( ::File.exists?( localconfig ) )
end
#
# Connect and return SCM::Perforce object for the configured server.
#
def scm()
# Already connected, then return.
return ( @sc ) unless ( @sc.nil? )
# Otherwise create a new connection.
@sc = SCM::Perforce.new( )
@sc.user = @sc_username
@sc.client = @sc_workspace
@sc.port = @sc_server
@sc.connect()
@sc
end
#
# Connect and return SCM::Perforce object for the configured rage server.
#
def ragescm()
# Already connected, then return.
return ( @ragesc ) unless ( @ragesc.nil? )
# Otherwise create a new connection.
@ragesc = SCM::Perforce.new( )
@ragesc.user = @sc_rage_username
@ragesc.client = @sc_rage_workspace
@ragesc.port = @sc_rage_server
@ragesc.connect()
@ragesc
end
#
# Connect and return SCM::Perforce object for the configured server.
# This is a 'connected' version. See SCM::Perforce_Connected
#
def scm_connected()
# Already connected, then return.
return ( @sc ) unless ( @sc.nil? )
# Otherwise create a new connection.
@sc = SCM::Perforce_Connected.new( )
@sc.user = @sc_username
@sc.client = @sc_workspace
@sc.port = @sc_server
@sc.connect()
@sc
end
#
# Pipeline temp folder
#
def temp()
OS::Path.combine( @toolsroot, "tmp" )
end
#
# Use our local project environment to substitute any environment
# variable values and return the resultant string.
#
def envsubst( string )
@environment.subst( string )
end
#
# Save local global configuration data XML.
#
def save_locals( )
localconfig = OS::Path.combine( @toolsroot, DEFAULT_LOCAL_CONFIG )
File.open( localconfig, "w+" ) do |file|
outputXML = REXML::Document.new()
root = outputXML.add_element("local")
root.attributes["version"] = @user_version.to_s
root.attributes["installtime"] = @install_time.strftime("%Y/%m/%d %H:%M:%S")
# USER
root.add_element( @user.to_local_xml() )
# SCM
scmNode = root.add_element('scm')
alienbrainNode = scmNode.add_element('build')
alienbrainNode.attributes['workspace'] = @sc_workspace
alienbrainNode.attributes['username'] = @sc_username
alienbrainNode.attributes['server'] = @sc_server
# RAGE SCM Server
if ( @user.is_programmer() or @user.is_tools() or
@user.is_builder_client() or @user.is_builder_server() ) then
rageNode = scmNode.add_element( 'rage' )
rageNode.attributes['workspace'] = @sc_rage_workspace
rageNode.attributes['username'] = @sc_rage_username
rageNode.attributes['server'] = @sc_rage_server
end
# TOOLS SCM Server
toolsNode = scmNode.add_element( 'tools' )
# There is no doubt a better way of determining the p4 server for tools when in tools development / tools release.
if (@toolsroot.include?("rage"))
toolsNode.attributes['workspace'] = @sc_rage_workspace
toolsNode.attributes['username'] = @sc_rage_username
toolsNode.attributes['server'] = @sc_rage_server
else
toolsNode.attributes['workspace'] = @sc_workspace
toolsNode.attributes['username'] = @sc_username
toolsNode.attributes['server'] = @sc_server
end
# PROJECTS
projectsNode = root.add_element("projects")
projects.each_value do |project|
projectNode = projectsNode.add_element("project")
projectNode.attributes["name"] = project.name
end
# LOGGING
loggingNode = root.add_element("logging")
loggingNode.attributes["stdout"] = @logtostdout.to_s
loggingNode.attributes["level"] = @log_level.to_s
loggingNode.attributes["trace"] = @log_trace.to_s
loggingNode.attributes["generate_html"] = @log_generate_html.to_s
logMailNode = loggingNode.add_element("logmails")
logMailNode.attributes["enabled"] = @logmailerrors.to_s
# APPLICATION
applicationsNode = root.add_element( "applications" )
xgeNode = applicationsNode.add_element( "app" )
xgeNode.attributes['name'] = 'xge'
xgeNode.attributes['enabled'] = @use_xge.to_s
# MAX
maxNode = applicationsNode.add_element( "app" )
maxNode.attributes['name'] = 'max'
maxNode.attributes['toolset_mode'] = @max_options.toolset_mode.to_s
# For tools and artists user we try and find their 3dsmax
# install location. If it exists then we
if ( Autodesk3dsmax::instance().is_installed?() ) then
save_locals_artist( root )
save_locals_3dsmax( )
end
# If Motionbuilder is installed.
autodeskMotionbuilder = AutodeskMotionbuilder::instance()
if ( autodeskMotionbuilder.is_installed?() ) then
moboNode = applicationsNode.add_element( "app" )
moboNode.attributes['name'] = 'motionbuilder'
autodeskMotionbuilder.installdirs.each_pair do |platform, dirs|
dirs.each_pair do |version, installdir|
instanceNode = moboNode.add_element( "instance" )
instanceNode.attributes['version'] = version
instanceNode.attributes['platform'] = platform
instanceNode.attributes['path'] = installdir
end
end
end
fmt = REXML::Formatters::Pretty.new()
fmt.write( outputXML, file )
end
projects.each_value do |project|
project.save_locals()
end
end
def can_install()
(( self.user.nil? or
self.user.username.nil? or self.user.username.empty? or
self.sc_server.nil? or self.sc_username.nil? or self.sc_workspace.nil? or
self.sc_server.empty? or self.sc_username.empty? or self.sc_workspace.empty? ) == false)
end
# Execute our simple install process; run the configured runlines
# and then save our local data to XML.
def do_install( reinstall = false )
autodesk3dsmax = Autodesk3dsmax::instance()
maxuser = autodesk3dsmax.is_installed?()
dirty = ( reinstall or @user_version < @version )
success = true
if ( reinstall ) then
uninstall_lines.each do |run_line|
success = false if run_line.run() == false
end
@user_version = 0
end
# Always execute our runlines. This was initially done to always
# copy across the 3dsmax plugins as we were continually having to
# fixup machines when artists got new c: drives or a new 3dsmax
# install.
install_lines.each do |run_line|
# Skip if it was a one of and we ain't dirty.
next if ( run_line.run_once and not dirty )
next if ( run_line.maxonly and not maxuser )
success = false if run_line.run() == false
end
if ( success ) then
@user_version = @version
save_locals( )
end
success
end
#
# == Description
# Copy the core max plugins that need to be in max's root directory
#
def copy_core_max_plugins( uninstall = false, force_max_run = false )
autodesk3dsmax = Autodesk3dsmax::instance()
if ( autodesk3dsmax.is_installed?() ) then
installed_dirs = autodesk3dsmax.installdirs( )
isX64 = ( :x64 == OS::WinSystemInfo::SysType( ) )
Autodesk3dsmax::log().info( "Processing installed Autodesk 3dsmax versions..." )
installed_dirs[:x64].each_pair do |key, value|
next if ( key.to_f < 11.0 ) # skip < 3dsmax 2009
setup_3dsmax_version( key.to_f, value, true, uninstall, force_max_run )
end
installed_dirs[:x86].each_pair do |key, value|
next if ( key.to_f < 11.0 ) # skip < 3dsmax 2009
setup_3dsmax_version( key.to_f, value, false, uninstall, force_max_run )
end
end
end
#--------------------------------------------------------------------
# Private Constants
#--------------------------------------------------------------------
private
TITLE = 'Rockstar Games Tools Framework'
DEFAULT_CONFIG = "config.xml"
DEFAULT_LOCAL_CONFIG = "local.xml"
#--------------------------------------------------------------------
# Private Methods
#--------------------------------------------------------------------
private
# Function to import all definitions in the Globals class into this
# Config class, and its Environment table. This saves us having to
# maintain this class for additional Global attributes.
def import_globals( )
g = Pipeline::Globals::instance( )
g.instance_variables.each do |var|
sym = var.sub( '@', '')
val = g.instance_variable_get( var )
self.instance_variable_set( var, val )
# Import into our environment.
@environment.add( sym, val.to_s )
end
end
# Return the XML attribute value or the project member variable,
# XML overridding the project member variable.
def get_override_attr_or_member( xml_node, config, member )
return xml_node.attributes[member] unless xml_node.attributes[member].nil?
return config.send(member) if config.methods.include?( member )
throw ArgumentError.new( "#{member} not found in XML or project object." )
end
public
#
# Parse project data from an XML node, updating member data as
# required.
#
# This allows us to have local.xml overwrite anything in config.xml.
#
def load_from( xml_node, env, root = 'config' )
# Parse our version numbers and network configuration file
# depending on what type of configuration file we are parsing.
if ( 'config' == root ) then
@version = xml_node.root.attributes['version'].to_i \
unless ( xml_node.root.attributes['version'].nil? )
elsif ( 'network' == root ) then
@network_version = xml_node.root.attributes['version'].to_i \
unless ( xml_node.root.attributes['version'].nil? )
elsif ( 'local' == root ) then
@user_version = xml_node.root.attributes['version'].to_i \
unless ( xml_node.root.attributes['version'].nil? )
end
#-----------------------------------------------------------------
# GLOBAL-SPECIFIC CONFIGURATION
#-----------------------------------------------------------------
if ( 'config' == root ) then
# USERTYPES CONFIGURATION
pseudoUserMask = 0
xml_node.elements.each( "#{root}/usertypes/usertype" ) do |usertype|
if("true" == usertype.attributes['pseudo'])
pseudoUserMask |= usertype.attributes['flags'].hex
end
end
#invert
pseudoUserMask = pseudoUserMask ^ 0xFFFFFFFF
User::set_pseudoUserMask(pseudoUserMask)
xml_node.elements.each( "#{root}/usertypes/usertype" ) do |usertype|
name = usertype.attributes['name']
uiname = usertype.attributes['uiname']
flags = usertype.attributes['flags'].hex
pseudo = usertype.attributes['pseudo']
User::add_usertype( name, Usertype.new( name, uiname, flags, pseudo ) )
end
# PLATFORM CONFIGURATION
xml_node.elements.each( "#{root}/platforms/platform" ) do |platform|
name = platform.attributes['name']
uiname = platform.attributes['uiname']
if ( @platforms.has_key?( name ) ) then
# Don't permit updating platforms in local.xml.
else
@platforms[name] = Platform.new( name, uiname )
end
end
# MAX INSTALL CONFIGURATION
xml_node.elements.each( "#{root}/install/maxinstalls/max" ) do |runline|
check = true
next if runline.attributes['version'].nil?
version = runline.attributes['version'].to_f
check = false if ((not runline.attributes['check'].nil?) and (runline.attributes['check'].downcase == "false"))
@max_options.version_options[version] = MaxVersionOptions.new(version,check)
end
# RUNLINES CONFIGURATION
xml_node.elements.each( "#{root}/install/runlines/runline" ) do |runline|
if ( not runline.attributes['cmd'].nil? ) then
@install_lines << RunLineCmd::from_xml( runline, @environment )
elsif ( not runline.attributes['scriptcmd'].nil? ) then
@install_lines << RunLineScript::from_xml( runline, @environment )
else
RunLine::log().error( "Unrecognised runline XML: #{runline.to_s}." )
end
end
xml_node.elements.each( 'uninstall/runlines/runline' ) do |runline|
if ( not runline.attributes['cmd'].nil? ) then
@uninstall_lines << RunLineCmd::from_xml( runline, @environment )
elsif ( not runline.attrubutes['scriptcmd'].nil? ) then
@uninstall_lines << RunLineScript::from_xml( runline, @environment )
else
RunLine::log().error( "Unrecognised runline XML: #{runline.to_s}." )
end
end
end
# SCM CONFIGURATION
xml_node.elements.each( "#{root}/scm" ) do |scm|
@sc_enabled = scm.attributes['toolchain_integration'] unless scm.attributes['toolchain_integration'].nil?
end
xml_node.elements.each( "#{root}/scm/build" ) do |scm|
@sc_workspace = scm.attributes['workspace'] unless scm.attributes['workspace'].nil?
@sc_username = scm.attributes['username'] unless scm.attributes['username'].nil?
@sc_server = scm.attributes['server'] unless scm.attributes['server'].nil?
end
# RAGE SCM CONFIGURATION
xml_node.elements.each( "#{root}/scm/rage" ) do |scm|
@sc_rage_workspace = scm.attributes["workspace"] unless scm.attributes["workspace"].nil?
@sc_rage_username = scm.attributes["username"] unless scm.attributes["username"].nil?
@sc_rage_server = scm.attributes["server"] unless scm.attributes["server"].nil?
end
# TOOLS SCM CONFIGURATION
xml_node.elements.each( "#{root}/scm/tools" ) do |scm|
@sc_tools_workspace = scm.attributes["workspace"] unless scm.attributes["workspace"].nil?
@sc_tools_username = scm.attributes["username"] unless scm.attributes["username"].nil?
@sc_tools_server = scm.attributes["server"] unless scm.attributes["server"].nil?
end
# PROJECTS CONFIGURATION
xml_node.elements.each( "#{root}/projects/project" ) do |project|
# Parse all XML attributes
name = project.attributes['name']
throw XmlParseError.new( "No project name attribute." ) \
if ( name.nil? )
uiname = project.attributes['uiname'] unless project.attributes['uiname'].nil?
rootpath = project.attributes['root'] unless project.attributes['root'].nil?
config = project.attributes['config'] unless project.attributes['config'].nil?
sc_proj = name # DHM; obsolete attribute 'sc'
if ( @projects.has_key?( name ) ) then
# UPDATE PROJECT OBJECT
# name and config cannot be changed
@projects[name].uiname = uiname unless uiname.nil?
@projects[name].root = rootpath unless rootpath.nil?
elsif ( 'config' == root )
# CREATE PROJECT OBJECT
# Only if we are parsing the root config. So we can
# safely remove projects from the root even when they
# may exist in users local XML data.
@projects[name] = Project.new( name, uiname, rootpath, config, sc_proj )
end
end
#Local Studio information.
xml_node.elements.each( "#{root}/studio" ) do |studio|
@local_studio = studio.attributes['name']
end
# DLC PROJECTS CONFIGURATION
# DHM: no current support, all support in Asset Pipeline 3.
@project = @projects.values[0]
# LOGGING CONFIGURATION
xml_node.elements.each( "#{root}/logging" ) do |logging|
# Parse all XML attributes
@log_level = logging.attributes['level'].to_i unless logging.attributes['level'].nil?
@logtostdout = ( 'true' == logging.attributes['stdout'] ) unless logging.attributes['stdout'].nil?
@log_trace = ( 'true' == logging.attributes['trace'] ) unless logging.attributes['trace'].nil?
@log_generate_html = ( 'true' == logging.attributes['generate_html'] ) unless logging.attributes['generate_html'].nil?
end
xml_node.elements.each( "#{root}/logging/logmails" ) do |logmails|
# Parse all XML attributes
@mailserver = logmails.attributes['server'] unless logmails.attributes['server'].nil?
@mailport = logmails.attributes['port'] unless logmails.attributes['port'].nil?
@maildomain = logmails.attributes['domain'] unless logmails.attributes['domain'].nil?
@logmailerrors = ( 'true' == logmails.attributes["enabled"] ) unless logmails.attributes['enabled'].nil?
end
xml_node.elements.each( "#{root}/logging/logmails/logmail" ) do |logmail|
# Parse all XML attributes
name = logmail.attributes['name']
email = logmail.attributes['email']
@logmailtos[name] = email
end
#MAX
xml_node.elements.each( "#{root}/max") do |max|
if max.attributes['toolset_mode'].nil? == false then
@max_options.toolset_mode = max.attributes['toolset_mode'].clone.intern
end
end
#-----------------------------------------------------------------
# LOCAL-SPECIFIC CONFIGURATION
#-----------------------------------------------------------------
if ( 'local' == root ) then
xml_node.elements.each( "#{root}/user" ) do |user|
@user = User::from_xml( user )
end
end
#-----------------------------------------------------------------
# USER-SPECIFIC CONFIGURATION
#-----------------------------------------------------------------
# APPLICATIONS CONFIGURATION
xml_node.elements.each( "#{root}/applications/app" ) do |app|
if ( 'xge' == app.attributes['name'] ) then
@use_xge = ( 'true' == app.attributes['enabled'] )
end
end
end
#
# Reset member variables to their default values. Private as only used
# internally.
#
def reset( )
@environment.clear( )
import_globals( )
@projects = {}
@platforms = {}
@usertypes = {}
@logmailtos = {}
@install_lines = []
@uninstall_lines = []
# LOGGING DEFAULTS
@log_level = 2
@logmailerrors = true
@logtostdout = false
@log_trace = false
@log_generate_html = false
#MAX
@max_options = MaxOptions.new()
end
#
# == Description
# This method is invoked from the public +save_locals+ method and it
# updates the 3dsmax plugcfg directory with a small settings file
# so our 3dsmax plugins/script tools can determine the path locations
# for project/global configuration settings.
#
def save_locals_3dsmax( )
# Fix issue with some artists not having 3dsmax installed.
autodesk3dsmax = Autodesk3dsmax::instance( )
return unless ( autodesk3dsmax.is_installed? )
begin
autodesk3dsmax.installdirs.each_pair do |platform, installdirs|
installdirs.each_pair do |max_version, max_root_dir|
dcc_root_dir = autodesk3dsmax.get_root_dir()
next if (@max_options.version_options[max_version.to_f].nil? == false) and (@max_options.version_options[max_version.to_f].check_for_install == false)
max_plugcfg_dir = OS::Path.combine( @toolsroot, dcc_root_dir, Autodesk3dsmax::VERSION_DIRS[max_version.to_f], 'plugcfg' )
pipeline_file = OS::Path.combine( max_plugcfg_dir, 'pipeline.xml' )
if ( File.directory?( max_plugcfg_dir ) ) then
File.open( pipeline_file, 'w' ) do |file|
Autodesk3dsmax::log().info( "Creating pipeline.xml file in #{max_root_dir}" )
max_settings = Document.new
root_elem = max_settings.add_element( 'max_pipeline' )
max_settings.root.add_attribute( 'toolsroot', Globals.instance.toolsroot )
max_settings.root.add_attribute( 'toolsbin', Globals.instance.toolsbin )
# Rather than hardcode which environment variables
# go into pipeline.xml we just take all of the root
# pipeline globals.
g = Pipeline::Globals::instance( )
g.instance_variables.each do |var|
sym = var.sub( '@', '')
val = g.instance_variable_get( var )
root_elem.add_attribute( sym.to_s, val )
end
# Write out the XML.
fmt = REXML::Formatters::Pretty.new( )
fmt.write( max_settings, file )
end
else
Autodesk3dsmax::log().error( "No pipeline.xml file created for #{max_root_dir}. Path #{max_plugcfg_dir} does not exist." )
end
end
end
rescue Exception => ex
Autodesk3dsmax::log().exception(ex, "Max setup failed" )
end
end
#
# This method is invoked from the public +save_locals+ method and it
# updates the artist configuration XML node (storing active project
# for 3dsmax etc.)
#
def save_locals_artist( root )
isX64 = ( :x64 == OS::WinSystemInfo::SysType( ) )
latest_version = Autodesk3dsmax::instance( ).latest_version
installdirs = Autodesk3dsmax::instance( ).installdirs( )
max_install_dir = ''
if ( isX64 and installdirs.has_key?( :x64 ) ) then
max_install_dir = installdirs[:x64][latest_version]
elsif ( installdirs.has_key?( :x86 ) ) then
max_install_dir = installdirs[:x86][latest_version]
end
artistNode = root.add_element( 'artist' )
artistNode.attributes['activeproject'] = ''
artistNode.attributes['maxinstalldir'] = max_install_dir
artistNode.attributes['maxversion'] = latest_version.to_s
end
#
# Setup specific Autodesk 3dsmax version.
#
def setup_3dsmax_version( max_version, installdir, isX64, uninstall, force_max_run )
Autodesk3dsmax::log().info( 'Autodesk 3dsmax Initialisation' )
Autodesk3dsmax::log().info( "Version: #{max_version} 64bit:#{isX64}" )
Autodesk3dsmax::log().info( "Install Dir: #{installdir}" )
begin
autodesk3dsmax = Autodesk3dsmax::instance( )
dcc_root_dir = autodesk3dsmax.get_root_dir( )
if ( Autodesk3dsmax::VERSION_DIRS[max_version].nil? ) then
GUI::MessageBox::warning( "Unsupported 3dsmax Version", "3dsmax version #{max_version} 32-bit is not yet supported by the tool chain and will not be setup." ) unless isX64
GUI::MessageBox::warning( "Unsupported 3dsmax Version", "3dsmax version #{max_version} 64-bit is not yet supported by the tool chain and will not be setup." ) unless isX64
elsif ( (@max_options.version_options[max_version].nil? == false) and (@max_options.version_options[max_version].check_for_install == false) )
Autodesk3dsmax::log().info( "ignoring: #{max_version.to_s}" )
else
plugin_src_dir = OS::Path.combine( @toolsroot, dcc_root_dir, Autodesk3dsmax::VERSION_DIRS[max_version])
plugin_src_dir = OS::Path.combine( plugin_src_dir, 'x64' ) if isX64
filelist = OS::FindEx.find_files( OS::Path::combine( plugin_src_dir, '*.*' ), false )
filelist.each do |file|
fname = OS::Path.get_filename( file )
dst_file = OS::Path.combine( installdir, fname )
Autodesk3dsmax::log().info( "Copying file #{file}" )
Autodesk3dsmax::log().info( "\tDestination: #{dst_file}" )
if uninstall then
OS::FileUtilsEx.delete_files( dst_file ) if File.exist?( dst_file )
else
OS::FileUtilsEx.copy_file( file, dst_file ) if File.exist?( file )
end
end
# This could do with it's own function for clarity but for now it will live here
startup_script_dest_dir = OS::Path.combine( installdir, 'scripts', 'startup' )
OS::FileUtilsEx::delete_files( OS::Path::combine( startup_script_dest_dir, '*.*' ) )
initFilename = OS::Path::combine( startup_script_dest_dir, 'rockstar_max_init.ms' )
config_string = ''
if uninstall then
config_string = "RsUpdateMaxSystemPaths \"UNUSED\" versionSubdir:\"#{Autodesk3dsmax::VERSION_DIRS[0.0]}\" isX64:#{isX64}"
else
config_string = "RsUpdateMaxSystemPaths \"UNUSED\" versionSubdir:\"#{Autodesk3dsmax::VERSION_DIRS[max_version]}\" isX64:#{isX64}"
end
config_string = config_string + " toolset:\"#{@max_options.toolset_mode.to_s}\""
c = Pipeline::Config::instance()
fh = File.new(initFilename, 'w')
fh.write( "filein \"#{OS::Path::normalise( OS::Path::combine( c.toolsroot, 'dcc', 'common', 'configswitch_clean.ms' ) )}\"\n\n" )
fh.write( config_string )
fh.close( )
max_run = false
if force_max_run then
max_run = true
else
ver = Autodesk3dsmax::VERSION_DIRS[max_version].sub( 'max', '' )
msg = "Do you want to start 3dsmax #{ver} 32-bit to complete plugin setup?\n\n" unless isX64
msg = "Do you want to start 3dsmax #{ver} 64-bit to complete plugin setup?\n\n" if isX64
msg += "This only needs to be done the first time the installer is run, when you switch project or when you experience problems with "
msg += "3dsmax #{ver} 32-bit." unless isX64
msg += "3dsmax #{ver} 64-bit." if isX64
res = GUI::MessageBox::question( '3dsmax Plugin Setup', msg )
case res
when GUI::MessageBox::YES
max_run = true
end
end
if max_run then
Autodesk3dsmax::log().info( "Running 3dsmax #{max_version} 32-bit..." ) unless isX64
Autodesk3dsmax::log().info( "Running 3dsmax #{max_version} 64-bit..." ) if isX64
commandline = "#{OS::Path::combine( installdir, '3dsmax.exe')} -U MAXScript \"#{initFilename}\" -mxs \"quitMax #noPrompt\" -silent"
Autodesk3dsmax::log().info( "Command line: #{commandline}" )
system( commandline )
else
Autodesk3dsmax::log().info( "3dsmax #{max_version} 32-bit not setup." ) unless isX64
Autodesk3dsmax::log().info( "3dsmax #{max_version} 64-bit not setup." ) if isX64
end
end
rescue Exception => ex
Autodesk3dsmax::log().exception( ex, "Unhandled exception during 3dsmax #{max_version} 32-bit install" ) unless isX64
Autodesk3dsmax::log().exception( ex, "Unhandled exception during 3dsmax #{max_version} 64-bit install" ) if isX64
end
end
end
end # Pipeline
# pipeline/config/projects.rb