""" 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'])