306 lines
12 KiB
Python
Executable File
306 lines
12 KiB
Python
Executable File
"""
|
|
Module for accessing configuration and settings.
|
|
|
|
Provides access to the current Project, User, Environment( Motionbuilder ) and installed Tools.
|
|
|
|
!!! THIS FILE SHOULD NOT BE UPDATED FROM WILDWEST !!!
|
|
|
|
This file MUST use RSG.Base.Configuration not RSG.Configuration
|
|
|
|
"""
|
|
|
|
import os
|
|
|
|
# Ensures current thread is set up to run STA COM concurrency model rather than the default MTA.
|
|
# This must be called before clr is imported otherwise Qt application initialisation will fail
|
|
# if it happens after this. e.g.: "Qt: Could not initialize OLE (error 80010106)" (url:bugstar:5722186)
|
|
import ctypes
|
|
ctypes.windll.ole32.CoInitialize(None)
|
|
|
|
import tempfile
|
|
import inspect
|
|
import socket
|
|
import ipaddress
|
|
from xml.etree import cElementTree as xml
|
|
|
|
import clr
|
|
|
|
clr.AddReference("RSG.Base.Configuration")
|
|
from RSG.Base.Configuration import ConfigFactory
|
|
|
|
# Create our .NET config object which we will extract all configuration settings from
|
|
_clrConfig = ConfigFactory.CreateConfig()
|
|
|
|
#Removing technicolor fall back as they are no more and Ravi doens't work there anymore
|
|
#This is a dictionary containing fallback emails if the email is unknown for a studio
|
|
QA_OWNER_EMAIL = {}
|
|
|
|
def FixPath(path):
|
|
return str(path).replace("/", "\\")
|
|
|
|
class ConfigError(Exception):
|
|
pass
|
|
|
|
class RequiredDataMissingError(ConfigError):
|
|
pass
|
|
|
|
class AppConfig(object):
|
|
FFMPEG = "2_7_0"
|
|
|
|
class VirtualProduction(object):
|
|
"""Provides access to Virtual Production Paths etc."""
|
|
|
|
def __init__(self):
|
|
|
|
class Path(object):
|
|
|
|
def __init__(self):
|
|
self.Root = os.path.join('x:\\', 'virtualproduction')
|
|
self.Previz = os.path.join(self.Root, 'PrevizAssets')
|
|
|
|
self.Path = Path()
|
|
|
|
|
|
class Script(object):
|
|
""" Provides access to information about the python script framework for this session of Motionbuilder."""
|
|
|
|
def __init__(self):
|
|
|
|
class Path(object):
|
|
""" Nested singleton which contains relevant paths for scripts."""
|
|
def __init__(self):
|
|
|
|
currentFile = str(os.path.abspath(inspect.getfile(inspect.currentframe()))).lower()
|
|
|
|
self.Root = os.path.abspath(os.path.join(os.path.dirname(currentFile), '..'))
|
|
self.RockstarRoot = os.path.join(self.Root, "RS")
|
|
self.ExternalRoot = os.path.join(self.Root, "External")
|
|
|
|
self.ToolImages = os.path.abspath(os.path.join(self.Root, "..", "images"))
|
|
self.SetupConfig = os.path.join(self.Root, "setup.config")
|
|
# Make temp dir if it is missing
|
|
if tempfile.tempdir is None:
|
|
tempfile.mkdtemp()
|
|
self.WingRemoteFilePath = os.path.join(tempfile.tempdir, "mobuRunRemoteScript.txt")
|
|
self.MenuConfig = os.path.join(self.Root, 'menu.config')
|
|
|
|
_Path = Path()
|
|
|
|
class Exception(object):
|
|
def __init__(self):
|
|
tree = xml.parse(_Path.SetupConfig)
|
|
root = tree.getroot()
|
|
self.IgnorableUsers = [element.text.lower()
|
|
for element in root.findall("ExceptionConfiguration/IgnoreUserList/Item")]
|
|
self.EmailList = [element.text.lower()
|
|
for element in root.findall("ExceptionConfiguration/EmailList/Item")]
|
|
|
|
self.Path = Path()
|
|
self.Exception = Exception()
|
|
self.TargetBuild = os.getenv('MOTIONBUILDER_MAJOR_VERSION', '2016')
|
|
self.TargetCut = os.getenv('MOTIONBUILDER_MINOR_VERSION', '')
|
|
self.Profile = os.getenv('MOTIONBUILDER_PROFILE', '')
|
|
|
|
|
|
class Branch(object):
|
|
"""Represents a branch within a project."""
|
|
def __init__(self, clrBranch ):
|
|
|
|
self.Name = clrBranch.Name
|
|
self.IsDefault = False
|
|
self.ClrBranch = clrBranch
|
|
|
|
class Path:
|
|
def __init__(self):
|
|
self.Common = FixPath(clrBranch.Common)
|
|
self.Build = FixPath(clrBranch.Build)
|
|
self.Audio = FixPath(clrBranch.Audio)
|
|
self.Code = FixPath(clrBranch.Code)
|
|
self.Art = FixPath(clrBranch.Art)
|
|
self.Anim = FixPath(clrBranch.Anim)
|
|
self.Assets = FixPath(clrBranch.Assets)
|
|
self.Processed = FixPath(clrBranch.Processed)
|
|
self.Export = FixPath(clrBranch.Export)
|
|
self.Metadata = FixPath(clrBranch.Metadata)
|
|
|
|
self.Path = Path()
|
|
|
|
|
|
class Project(object):
|
|
"""Provides access to project specific settings and configuration."""
|
|
def __init__(self, clrProject, isCore=False):
|
|
|
|
self.ClrProject = clrProject
|
|
|
|
self.branchPathTokens = ["Common", "Build","Audio", "Code","Art","Anim","Assets","Processed","Export","Metadata"]
|
|
self.Name = clrProject.Name
|
|
self.FriendlyName = clrProject.FriendlyName
|
|
self.IsCore = isCore
|
|
|
|
defaultBranch = None
|
|
self.Branches = list()
|
|
|
|
for clrBranch in clrProject.Branches:
|
|
currentBranch = Branch( clrBranch.Value )
|
|
self.Branches.append(currentBranch)
|
|
if currentBranch.Name == clrProject.DefaultBranchName:
|
|
defaultBranch = currentBranch
|
|
defaultBranch.IsDefault = True
|
|
|
|
if defaultBranch is None:
|
|
raise RequiredDataMissingError("Missing default branch")
|
|
self.DefaultBranch = defaultBranch
|
|
self.Branch = self.DefaultBranch
|
|
|
|
self.AssetPrefix = clrProject.AssetPrefix
|
|
|
|
class Path:
|
|
def __init__(self):
|
|
self.Cache = FixPath(clrProject.Cache)
|
|
self.Root = FixPath(clrProject.Root)
|
|
self.Art = FixPath(defaultBranch.Path.Art)
|
|
self.Anim = FixPath(defaultBranch.Path.Anim)
|
|
self.Assets = FixPath(defaultBranch.Path.Assets)
|
|
self.Processed = FixPath(defaultBranch.Path.Processed)
|
|
self.Export = FixPath(defaultBranch.Path.Export)
|
|
self.Metadata = FixPath(defaultBranch.Path.Metadata)
|
|
|
|
self.Path = Path()
|
|
|
|
def GetBranchByName(self, branchName):
|
|
if not branchName:
|
|
return None
|
|
for branch in self.Branches:
|
|
if branch.Name.lower() == branchName.lower():
|
|
return branch
|
|
return None
|
|
|
|
|
|
class User(object):
|
|
def __init__(self, clrConfig):
|
|
self.Name = str(clrConfig.Username)
|
|
|
|
if clrConfig.Studios.ThisStudio:
|
|
self.Domain = str(clrConfig.Studios.ThisStudio.Domain)
|
|
self.Studio = str(clrConfig.Studios.ThisStudio.FriendlyName)
|
|
self.P4ServerDefault = str(clrConfig.Studios.ThisStudio.DefaultPerforceServer)
|
|
# self.P4ServerSwarm = str(clrConfig.Studios.ThisStudio.PerforceSwarmServer) No swarm server with old config
|
|
self.ExchangeServer = str(clrConfig.Studios.ThisStudio.ExchangeServer)
|
|
|
|
# Not all the studios have this setup. See $(tools_branch)\etc\globals\studios.meta
|
|
try:
|
|
self.P4ServerMocap = str(clrConfig.Studios.ThisStudio.PerforceMocapServer)
|
|
except:
|
|
self.P4ServerMocap = None
|
|
else:
|
|
self.Studio = None
|
|
self.P4ServerDefault = None
|
|
self.P4ServerMocap = None
|
|
self.P4ServerSwarm = None
|
|
self.ExchangeServer = None
|
|
|
|
self.IpAddress = ipaddress.ip_address(unicode(socket.gethostbyname(socket.gethostname())))
|
|
if clrConfig.EmailAddress:
|
|
self.Email = clrConfig.EmailAddress.lower()
|
|
else:
|
|
self.Email = None
|
|
|
|
self.IsExternal = True
|
|
if self.Studio is not None and 'R*' in self.Studio:
|
|
self.IsExternal = False
|
|
self.IsInternal = not self.IsExternal
|
|
|
|
if hasattr(clrConfig, 'UserType') and clrConfig.UserType is not None:
|
|
self.Type = str(clrConfig.UserType.DisplayName)
|
|
else:
|
|
self.Type = "Unknown"
|
|
|
|
@staticmethod
|
|
def tempDir():
|
|
"""
|
|
Wrapper around tempfile function to get the temp directory on Windows.
|
|
|
|
This handle short paths, ie 'c:\\users\\matthe~1.kap\\appdata\\local\\temp'
|
|
|
|
See Also:
|
|
https://web.archive.org/web/20131206010029/http://support.microsoft.com/kb/142982
|
|
|
|
Returns:
|
|
unicode
|
|
"""
|
|
maxWindowsPathSize = 260
|
|
pathBuffer = ctypes.create_unicode_buffer(maxWindowsPathSize)
|
|
shortTempDir = tempfile.gettempdir()
|
|
retVal = ctypes.windll.kernel32.GetLongPathNameW(unicode(shortTempDir), pathBuffer, maxWindowsPathSize)
|
|
if retVal == 0:
|
|
raise OSError("Failed to resolve long path for temp dir: {}".format(shortTempDir))
|
|
return pathBuffer.value
|
|
|
|
|
|
class Tool(object):
|
|
""" Nested singleton which contains relevant paths for tools. """
|
|
|
|
def __init__(self, clrConfig):
|
|
|
|
class Path:
|
|
|
|
def __init__(self):
|
|
self.Bin = FixPath(clrConfig.ToolsBin)
|
|
self.Logs = FixPath(os.path.join(str(clrConfig.ToolsLogs), 'Motionbuilder'))
|
|
self.Root = FixPath(clrConfig.ToolsRoot)
|
|
self.Config = FixPath(clrConfig.ToolsConfig)
|
|
self.Temp = FixPath(clrConfig.ToolsTemp)
|
|
self.TechArt = FixPath(os.path.join(self.Root, "techart"))
|
|
currentFilePath = FixPath(os.path.abspath(inspect.getfile(inspect.currentframe()))).lower()
|
|
if r'x:\wildwest' in currentFilePath:
|
|
self.TechArt = r'x:\wildwest'
|
|
targetBuild = os.getenv('MOTIONBUILDER_MAJOR_VERSION', '2016')
|
|
self.MobuRoot = os.path.join(self.Root,
|
|
"dcc",
|
|
"current",
|
|
"motionbuilder{0}".format(targetBuild))
|
|
self.Assemblies = os.path.join(self.MobuRoot, "assemblies", "x64")
|
|
self.Plugins = os.path.join(self.MobuRoot, "plugins", "x64")
|
|
|
|
self.Path = Path()
|
|
|
|
# Parse core project first
|
|
CoreProject = Project(_clrConfig.Project,True)
|
|
|
|
# Flesh out DLC projects and TitleUpdateProject
|
|
DLCProjects = dict()
|
|
TitleUpdateProject = None
|
|
for _dlcProject in _clrConfig.DLCProjects.Values:
|
|
try:
|
|
dlcProject = Project(_dlcProject)
|
|
DLCProjects[_dlcProject.Name] = dlcProject
|
|
if _dlcProject.Name == "titleupdate":
|
|
TitleUpdateProject = Project(_dlcProject)
|
|
except RequiredDataMissingError:
|
|
# Ignore projects with missing config data
|
|
# e.g. the project's .xml file does not exist (url:bugstar:5370440)
|
|
pass
|
|
|
|
User = User(_clrConfig)
|
|
Tool = Tool(_clrConfig)
|
|
VirtualProduction = VirtualProduction()
|
|
Script = Script()
|
|
|
|
def GetProjectByName(projectName):
|
|
'''Searches through DLC projects and Core project. Returns the project that matches the supplied projectname'''
|
|
for project in [CoreProject] + DLCProjects.values():
|
|
if projectName.lower() == project.Name.lower():
|
|
return project
|
|
return None
|
|
|
|
#We will now attempt to switch the currently targeted project based off environment vars (if they are present)
|
|
Project = CoreProject
|
|
if 'RS_PROJECT' in os.environ:
|
|
#Only searching through the DLC packs. If it doesn't find a pack/project with a matching name it will fall back to core
|
|
for dlcProject in DLCProjects.itervalues():
|
|
if dlcProject.Name == os.environ['RS_PROJECT']:
|
|
Project = dlcProject
|
|
break
|
|
if 'RS_BRANCH_NAME' in os.environ:
|
|
Project.Branch = Project.GetBranchByName(os.environ['RS_BRANCH_NAME'])
|
|
CoreProject.Branch = CoreProject.GetBranchByName(os.environ['RS_BRANCH_NAME']) |