#!/usr/bin/python ################################################################################ ## @file ## SPI Images Build Script ## ## @copyright ## Copyright (c) 2016 Intel Corporation. All rights reserved ## This software and associated documentation (if any) is furnished ## under a license and may only be used or copied in accordance ## with the terms of the license. Except as permitted by the ## license, no part of this software or documentation may be ## reproduced, stored in a retrieval system, or transmitted in any ## form or by any means without the express written consent of ## Intel Corporation. ## This file contains 'Framework Code' and is licensed as such ## under the terms of your license agreement with Intel or your ## vendor. This file may not be modified, except as allowed by ## additional terms of your license agreement. ################################################################################ import os import sys import subprocess from subprocess import call, check_call import json from xml.etree import ElementTree import fnmatch import traceback # # Constant Strings # WIN = "win" MAC = "mac" LINUX = "linux" DEFAULT_CONFIG_FILE = "RomImageBuildConfig.xml" BIOS_VERSION = "BiosVersion" INCLUDES = "Includes" FIT_DEFINITIONS = "FitDefinitions" POST_PROCESSOR_DEFINITIONS = "PostProcessorDefinitions" FIT_IMAGE_BUILDS = "FitImageBuilds" ROM_IMAGE_BUILDS = "RomImageBuilds" NAME = "Name" COMMANDS = "Commands" OS = "Os" PATH = "Path" FILE_PATH = "FilePath" PRE_ARGUMENTS = "PreArguments" POST_ARGUMENTS = "PostArguments" OUTPUT_FILES = "OutputFiles" FIT_DEFINITION = "FitDefinition" PATCH_FILE = "PatchFile" BIOS_ROM = "BiosRom" FIT_OUTPUT_RENAME = "FitOutputRename" SRC = "Src" DEST = "Dest" FIT_OUTPUT_DISCARD = "FitOutputDiscard" FIT_IMAGE_BUILD = "FitImageBuild" FILE_NAME = "FileName" FIT_OUTPUT_FILE = "FitOutputFile" POST_PROCESSING = "PostProcessing" POST_PROCESSOR_DEFINITION = "PostProcessorDefinition" ARGUMENTS = "Arguments" #XML Specific Strings XML_ROM_IMAGE_BUILD_CONFIG = "RomImageBuildConfig" XML_INCLUDE = "Include" XML_COMMAND = "Command" XML_ROM_IMAGE_BUILD = "RomImageBuild" XML_OUTPUT_FILE = "OutputFile" XML_POST_PROCESSOR = "PostProcessor" #String Macros NAME_MACRO = "$NAME" BIOSVER_MACRO = "$BIOSVER" FIT_BUILD_NAME_MACRO = "$FIT_BUILD_NAME" OUTPUT_FILE_MACRO = "$OUTPUT_FILE" ScriptFilePath = os.path.abspath(__file__) projectdir = os.path.dirname(ScriptFilePath) # # Environment detection # if sys.platform == "win32": ostype = WIN elif sys.platform == "darwin": ostype = MAC elif sys.platform.startswith("linux"): ostype = LINUX elif os.name == "posix": print("Warning: Unrecognized UNIX OS... treating as Linux") ostype = LINUX else: raise EnvironmentError("Unsupported OS") if not os.path.isfile(os.path.join(projectdir, DEFAULT_CONFIG_FILE)): if os.path.isfile(os.path.join(projectdir, "%s%sjson"%(os.path.splitext(DEFAULT_CONFIG_FILE)[0], os.extsep))): DEFAULT_CONFIG_FILE = "%s%sjson"%(os.path.splitext(DEFAULT_CONFIG_FILE)[0], os.extsep) try: basestring except NameError: basestring = str try: xrange except NameError: xrange = range # # Build Logic Classes # class FitImageBuild(object): def __init__(self, Config, FitImageBuildName): fitImageBuild = _GetFitImageConfig(Config, FitImageBuildName) self.Name = FitImageBuildName self._BiosRom = fitImageBuild[BIOS_ROM] self._PatchFile = fitImageBuild[PATCH_FILE] self.OutputFiles = fitImageBuild[OUTPUT_FILES] self._FileRenames = fitImageBuild[FIT_OUTPUT_RENAME] self._FileDeletes = fitImageBuild[FIT_OUTPUT_DISCARD] fitDef = fitImageBuild[FIT_DEFINITION] fitDefinition = None for definition in Config[FIT_DEFINITIONS]: if definition[NAME] == fitDef: fitDefinition = definition if fitDefinition is None: raise ValueError("No FitDefinition with name \"%s\""%fitDef) winIndex = -1 nativeIndex = -1 for i in xrange(len(fitDefinition[COMMANDS])): if fitDefinition[COMMANDS][i][OS] == ostype: nativeIndex = i if fitDefinition[COMMANDS][i][OS] == WIN: winIndex = i if nativeIndex == -1: if winIndex != -1: self._UseWine = True self._FitPath = fitDefinition[COMMANDS][winIndex][PATH] else: raise EnvironmentError("FitDefinition with name \"%s\" is not compatible with this platform."%fitDef) else: self._UseWine = False self._FitPath = fitDefinition[COMMANDS][nativeIndex][PATH] if os.path.isfile(os.path.join(projectdir, "PatchXml.js")): self._PatcherPath = projectdir elif os.path.isfile(os.path.join(os.path.dirname(os.path.dirname(self._FitPath)), "PatchXml.js")): self._PatcherPath = os.path.dirname(os.path.dirname(self._FitPath)) def Build(self): #Actual FIT Build Logic imgDir = os.path.dirname(self.OutputFiles[0]) xmlDir = os.path.join(imgDir, "Xml") templateXmlPath = os.path.join(xmlDir, "newfiletmpl_gen.xml") if not os.path.isdir(xmlDir): os.mkdir(xmlDir) if os.path.isfile(templateXmlPath): _DeleteFile(templateXmlPath) #Get blank XML template if not self._UseWine: check_call([self._FitPath, "/save", templateXmlPath], cwd=imgDir) else: fitPath = self._FitPath fitPath = _GetWinePath(fitPath) wineXmlPath = _GetWinePath(templateXmlPath) check_call("wine '%s' /save '%s'"%(fitPath, wineXmlPath), shell=True, cwd=imgDir) #Update OS environment variables so the patcher tool will work os.environ["ROM_DIR"] = ".%s%s"%(os.sep, os.path.relpath(os.path.dirname(self._PatchFile), imgDir)) os.environ["IMG_DIR"] = "." #Patch the FIT XML template to generate platform XML xmlPath = os.path.join(xmlDir, "%s.xml"%self.Name) if os.path.isfile(xmlPath): _DeleteFile(xmlPath) patchToolPath = os.path.join(self._PatcherPath, "PatchXml.js") if ostype==WIN: check_call("cscript //E:javascript %s %s %s -patch %s"%(patchToolPath, templateXmlPath, xmlPath, self._PatchFile), shell=True) else: winePatchToolPath = _GetWinePath(patchToolPath) wineTemplateXmlPath = _GetWinePath(templateXmlPath) wineXmlPath = _GetWinePath(xmlPath) winePatchFilePath = _GetWinePath(self._PatchFile) check_call("wine cscript //E:javascript '%s' '%s' '%s' -patch '%s'"%(winePatchToolPath, wineTemplateXmlPath, wineXmlPath, winePatchFilePath), shell=True) #Delete any pre-existing files for outputFile in self.OutputFiles: if os.path.isfile(outputFile): _DeleteFile(outputFile) for fileDelete in self._FileDeletes: if os.path.isfile(fileDelete): _DeleteFile(fileDelete) for fileRename in self._FileRenames: if os.path.isfile(fileRename[SRC]): _DeleteFile(fileRename[SRC]) if os.path.isfile(fileRename[DEST]): _DeleteFile(fileRename[DEST]) #Compute files that FIT should generate fitOutput = [] renameDests = [x[DEST] for x in self._FileRenames] for outputFile in self.OutputFiles: if outputFile in renameDests: for fileRename in self._FileRenames: if fileRename[DEST] == outputFile: fitOutput.append(fileRename[SRC]) break else: fitOutput.append(outputFile) #Copy BIOS _CopyFile(self._BiosRom, os.path.join(imgDir, "bios.rom")) #Run FIT if not self._UseWine: check_call([self._FitPath, "/f", xmlPath, "/b"], cwd=imgDir) else: fitPath = self._FitPath fitPath = _GetWinePath(fitPath) wineXmlPath = _GetWinePath(xmlPath) check_call("wine '%s' /f '%s' /b"%(fitPath, wineXmlPath), shell=True, cwd=imgDir) #Check to make sure files exist for outputFile in fitOutput: if not os.path.isfile(outputFile): raise ValueError("Fit did not generate file \"%s\" as expected."%outputFile) #Do Renames for fileRename in self._FileRenames: _RenameFile(fileRename[SRC], fileRename[DEST]) #Do Deletes for fileDelete in self._FileDeletes: _DeleteFile(fileDelete) _DeleteFile(os.path.join(imgDir, "bios.rom")) _DeleteFile(os.path.join(imgDir, "fit.log")) class PostProcessor(object): def __init__(self, Config, PostProcessorConfig, OutputFile): postProcessorDef = PostProcessorConfig[POST_PROCESSOR_DEFINITION] postProcessorDefinition = None for definition in Config[POST_PROCESSOR_DEFINITIONS]: if definition[NAME] == postProcessorDef: postProcessorDefinition = definition break if postProcessorDefinition is None: raise ValueError("No PostProcessorDefinition with name \"%s\""%postProcessorDef) winIndex = -1 nativeIndex = -1 for i in xrange(len(postProcessorDefinition[COMMANDS])): if postProcessorDefinition[COMMANDS][i][OS] == ostype: nativeIndex = i if postProcessorDefinition[COMMANDS][i][OS] == WIN: winIndex = i if nativeIndex == -1: if winIndex != -1: self._UseWine = True self._ToolPath = postProcessorDefinition[COMMANDS][winIndex][FILE_PATH] preArgs = postProcessorDefinition[COMMANDS][winIndex][PRE_ARGUMENTS] postArgs = postProcessorDefinition[COMMANDS][winIndex][POST_ARGUMENTS] else: raise EnvironmentError("PostProcessorDefinition with name \"%s\" is not compatible with this platform."%postProcessorDef) else: self._UseWine = False self._ToolPath = postProcessorDefinition[COMMANDS][nativeIndex][FILE_PATH] preArgs = postProcessorDefinition[COMMANDS][nativeIndex][PRE_ARGUMENTS] postArgs = postProcessorDefinition[COMMANDS][nativeIndex][POST_ARGUMENTS] self._Args = preArgs + " " + PostProcessorConfig[ARGUMENTS] + " " + postArgs #Replace the $OUTPUT_FILE macro if self._Args.find(OUTPUT_FILE_MACRO) != -1: if self._UseWine: self._Args = self._Args.replace(OUTPUT_FILE_MACRO, "'%s'"%_GetWinePath(OutputFile)) else: self._Args = self._Args.replace(OUTPUT_FILE_MACRO, OutputFile) self.OutputFile = OutputFile def Build(self): imgDir = os.path.dirname(self.OutputFile) if not self._UseWine: check_call("%s %s"%(self._ToolPath, self._Args), shell=True, cwd=imgDir) else: toolPath = _GetWinePath(self._ToolPath) check_call("wine '%s' %s"%(toolPath, self._Args), shell=True, cwd=imgDir) class RomImageOutputFile(object): def __init__(self, Config, OutputFileConfig): self.OutputFile = OutputFileConfig[FILE_NAME] self.InputFile = OutputFileConfig[FIT_OUTPUT_FILE] self._PostProcessing = [] for postProcessor in OutputFileConfig[POST_PROCESSING]: self._PostProcessing.append(PostProcessor(Config, postProcessor, self.OutputFile)) def Build(self): #Copy FIT output file _CopyFile(self.InputFile, self.OutputFile) #Run all the Post Processors for postProcessor in self._PostProcessing: postProcessor.Build() class RomImageBuild(object): def __init__(self, Config, RomImageBuildName): romImageBuild = _GetRomImageConfig(Config, RomImageBuildName) self.Name = RomImageBuildName self.FitBuild = romImageBuild[FIT_IMAGE_BUILD] self.OutputFiles = [] for outputFile in romImageBuild[OUTPUT_FILES]: self.OutputFiles.append(RomImageOutputFile(Config, outputFile)) def Build(self): for outputFile in self.OutputFiles: outputFile.Build() def GenerateShellScript(self, ConfigFile, BashScript=True): if len(self.OutputFiles) <= 0: return imgDir = os.path.dirname(self.OutputFiles[0].OutputFile) if BashScript: extension = "sh" else: extension = "bat" scriptFile = "%s%s%s"%(os.path.splitext(self.OutputFiles[0].OutputFile)[0], os.extsep, extension) pyRelativePath = os.path.relpath(ScriptFilePath, imgDir) if BashScript: if os.path.splitext(pyRelativePath)[1].lower() == "%spyc"%os.extsep: pyRelativePath = "%s%s%s"%(os.path.splitext(pyRelativePath)[0], os.extsep, "py") pyRelativePath = pyRelativePath.replace(os.sep, '/') f = open(scriptFile, 'wb') if ConfigFile == DEFAULT_CONFIG_FILE: if sys.version_info.major == 2: f.write('#!/bin/bash\n') f.write('%s %s\n'%(pyRelativePath, self.Name)) else: f.write(('#!/bin/bash\n').encode('utf-8')) f.write(('%s %s\n'%(pyRelativePath, self.Name)).encode('utf-8')) else: if sys.version_info.major == 2: f.write('#!/bin/bash\n') f.write('%s -c %s %s\n'%(pyRelativePath, ConfigFile, self.Name)) else: f.write(('#!/bin/bash\n').encode('utf-8')) f.write(('%s -c %s %s\n'%(pyRelativePath, ConfigFile, self.Name)).encode('utf-8')) f.close() if ostype != WIN: check_call("chmod +x %s"%scriptFile, shell=True) else: pyRelativePath = os.path.splitext(pyRelativePath)[0] pyRelativePath = pyRelativePath.replace(os.sep, '\\') f = open(scriptFile, 'wb') if ConfigFile == DEFAULT_CONFIG_FILE: if sys.version_info.major == 2: f.write('%s %s\r\n'%(pyRelativePath, self.Name)) else: f.write(('%s %s\r\n'%(pyRelativePath, self.Name)).encode('utf-8')) else: if sys.version_info.major == 2: f.write('%s -c %s %s\r\n'%(pyRelativePath, ConfigFile, self.Name)) else: f.write(('%s -c %s %s\r\n'%(pyRelativePath, ConfigFile, self.Name)).encode('utf-8')) f.close() # # Build Logic Functions # def _GetRomImageConfig(Config, RomImageBuildName): romImageBuild = None for build in Config[ROM_IMAGE_BUILDS]: if build[NAME] == RomImageBuildName: romImageBuild = build break if romImageBuild is None: raise ValueError("No RomImageBuild with name \"%s\""%RomImageBuildName) return romImageBuild def _GetFitImageConfig(Config, FitImageBuildName): fitImageBuild = None for build in Config[FIT_IMAGE_BUILDS]: if build[NAME] == FitImageBuildName: fitImageBuild = build break if fitImageBuild is None: raise ValueError("No FitImageBuild with name \"%s\""%FitImageBuildName) return fitImageBuild def GetListOfRomImageBuilds(Config): return [x[NAME] for x in Config[ROM_IMAGE_BUILDS]] ###################### ## Build ROM Images ## ###################### def BuildRomImages(Config, RomImageBuildNames): RomImageBuilds = {} FitImageBuilds = {} FitImageBuildNames = [] FitOutputFileNames = [] #Create ROM Image Build data structures for romImageBuildName in RomImageBuildNames: RomImageBuilds[romImageBuildName] = RomImageBuild(Config, romImageBuildName) #Determine which FIT Builds we need to run for romImageBuild in RomImageBuilds.values(): if romImageBuild.FitBuild not in FitImageBuildNames: FitImageBuildNames.append(romImageBuild.FitBuild) #Create FIT Build data structures for fitImageBuildName in FitImageBuildNames: FitImageBuilds[fitImageBuildName] = FitImageBuild(Config, fitImageBuildName) #Make a list of all the files the FIT build will generate for fitImageBuild in FitImageBuilds.values(): for outputFile in fitImageBuild.OutputFiles: if outputFile not in FitOutputFileNames: FitOutputFileNames.append(outputFile) #Run the FIT Builds for fitImageBuild in FitImageBuilds.values(): fitImageBuild.Build() #Run the ROM Image Builds for romImageBuild in RomImageBuilds.values(): romImageBuild.Build() #Delete FIT build output files for outputFile in FitOutputFileNames: _DeleteFile(outputFile) def GenerateShellScripts(Config, RomImageBuildNames, ConfigFile, BashScript=True): RomImageBuilds = {} #Create ROM Image Build data structures for romImageBuildName in RomImageBuildNames: RomImageBuilds[romImageBuildName] = RomImageBuild(Config, romImageBuildName) #Generate the Shell Scripts for romImageBuild in RomImageBuilds.values(): romImageBuild.GenerateShellScript(ConfigFile, BashScript) # # Config Parsing Routines # def LoadConfig(ConfigPath=DEFAULT_CONFIG_FILE): if not os.path.isabs(ConfigPath): ConfigPath = os.path.abspath(os.path.join(projectdir, ConfigPath)) baseDir = os.path.dirname(ConfigPath) config = _LoadConfigFile(ConfigPath) _ProcessIncludes(config, baseDir) _PopulateOptionalParameters(config) _CreateAbsolutePaths(config, baseDir, '') _ProcessStringReplacements(config) _CheckForDuplicates(config) _CheckReferences(config) return config def _ProcessIncludes(Config, BaseDir, AlreadyIncluded=[]): if INCLUDES not in Config: return for includeFile in Config[INCLUDES]: filePath = _TranslatePath(includeFile, BaseDir) if filePath in AlreadyIncluded: continue AlreadyIncluded.append(filePath) #Calculate relative path between the includer and the includee newBaseDir = os.path.commonprefix([filePath, BaseDir]) relativePath = os.path.dirname(os.path.relpath(filePath, newBaseDir)) #Load the included file subConfig = _LoadConfigFile(filePath) #Add default values for optional parameters _PopulateOptionalParameters(subConfig) #Translate all relative paths in the file in to absolute paths _CreateAbsolutePaths(subConfig, newBaseDir, relativePath) #Add the content from any files that the included file included, #Use the included file's directory as the base directory this time instead of the includee's directory _ProcessIncludes(subConfig, os.path.dirname(filePath), AlreadyIncluded) #Merge the content from the included file in to the includee if BIOS_VERSION in subConfig: Config[BIOS_VERSION] = subConfig[BIOS_VERSION] for constant in [FIT_DEFINITIONS, POST_PROCESSOR_DEFINITIONS, FIT_IMAGE_BUILDS, ROM_IMAGE_BUILDS]: if constant in subConfig: if constant not in Config: Config[constant] = [] Config[constant].extend(subConfig[constant]) #Delete the include directives since they have been processed del Config[INCLUDES] #Converts all relative paths in the given data structure to absolute paths def _CreateAbsolutePaths(Config, BasePath, RelativePath=''): if FIT_DEFINITIONS in Config: for fitDefinition in Config[FIT_DEFINITIONS]: for command in fitDefinition[COMMANDS]: command[PATH] = _TranslatePath(command[PATH], BasePath, RelativePath) if POST_PROCESSOR_DEFINITIONS in Config: for postProcessorDefinition in Config[POST_PROCESSOR_DEFINITIONS]: for command in postProcessorDefinition[COMMANDS]: command[FILE_PATH] = _TranslatePath(command[FILE_PATH], BasePath, RelativePath) if FIT_IMAGE_BUILDS in Config: for fitImageBuild in Config[FIT_IMAGE_BUILDS]: newOutputFiles = [] for outputFile in fitImageBuild[OUTPUT_FILES]: newOutputFiles.append(_TranslatePath(outputFile, BasePath, RelativePath)) fitImageBuild[OUTPUT_FILES] = newOutputFiles fitImageBuild[PATCH_FILE] = _TranslatePath(fitImageBuild[PATCH_FILE], BasePath, RelativePath) fitImageBuild[BIOS_ROM] = _TranslatePath(fitImageBuild[BIOS_ROM], BasePath, RelativePath) for fitOutputRename in fitImageBuild[FIT_OUTPUT_RENAME]: fitOutputRename[SRC] = _TranslatePath(fitOutputRename[SRC], BasePath, RelativePath) fitOutputRename[DEST] = _TranslatePath(fitOutputRename[DEST], BasePath, RelativePath) newFitOutputDiscard = [] for fitOutputDiscard in fitImageBuild[FIT_OUTPUT_DISCARD]: newFitOutputDiscard.append(_TranslatePath(fitOutputDiscard, BasePath, RelativePath)) fitImageBuild[FIT_OUTPUT_DISCARD] = newFitOutputDiscard if ROM_IMAGE_BUILDS in Config: for romImageBuild in Config[ROM_IMAGE_BUILDS]: for outputFile in romImageBuild[OUTPUT_FILES]: outputFile[FILE_NAME] = _TranslatePath(outputFile[FILE_NAME], BasePath, RelativePath) outputFile[FIT_OUTPUT_FILE] = _TranslatePath(outputFile[FIT_OUTPUT_FILE], BasePath, RelativePath) #Converts a relative path to an absolute path def _TranslatePath(Path, BasePath, RelativePath=''): if os.path.isabs(Path): return Path.replace('/', os.sep) path = Path.replace('/', os.sep) path = os.path.join(os.path.join(BasePath, RelativePath), path) path = os.path.abspath(path) return path #Inserts default values for optional parameters when the parameter isn't given def _PopulateOptionalParameters(Config): if FIT_IMAGE_BUILDS in Config: for fitImageBuild in Config[FIT_IMAGE_BUILDS]: if OUTPUT_FILES not in fitImageBuild: fitImageBuild[OUTPUT_FILES] = ["$NAME.bin"] if FIT_OUTPUT_RENAME not in fitImageBuild: fitImageBuild[FIT_OUTPUT_RENAME] = [] if FIT_OUTPUT_DISCARD not in fitImageBuild: fitImageBuild[FIT_OUTPUT_DISCARD] = [] if ROM_IMAGE_BUILDS in Config: for romImageBuild in Config[ROM_IMAGE_BUILDS]: for outputFile in romImageBuild[OUTPUT_FILES]: if FIT_OUTPUT_FILE not in outputFile: outputFile[FIT_OUTPUT_FILE] = "$FIT_BUILD_NAME.bin" for postProcessor in outputFile[POST_PROCESSING]: if ARGUMENTS not in postProcessor: postProcessor[ARGUMENTS] = "" #Replaces macros with the string they expand to def _ProcessStringReplacements(Config): #Replaces the $NAME and $BIOSVER macro def FixupString(subConfig, key, string, parentName, fitBuild): returnValue = string if string.find(NAME_MACRO) != -1: if key == NAME: raise ValueError("Name parameter cannot use the $NAME macro") if NAME in subConfig: returnValue = returnValue.replace(NAME_MACRO, subConfig[NAME]) else: if parentName is not None: returnValue = returnValue.replace(NAME_MACRO, parentName) else: raise ValueError("Missing required parameter \"Name\"") if string.find(BIOSVER_MACRO) != -1: if BIOS_VERSION not in Config: raise ValueError("Missing required parameter \"%s\""%BIOS_VERSION) returnValue = returnValue.replace(BIOSVER_MACRO, Config[BIOS_VERSION]) if fitBuild is not None and string.find(FIT_BUILD_NAME_MACRO) != -1: returnValue = returnValue.replace(FIT_BUILD_NAME_MACRO, fitBuild) return returnValue #Enumerates elements in a list and applies macro replacement to them def FixupList(subConfig, key, lst, parentName, fitBuild): newList = [] for item in subConfig[key]: if isinstance(item, basestring): newList.append(FixupString(subConfig, key, item, parentName, fitBuild)) if isinstance(item, dict): DoReplacements(item, parentName, fitBuild) newList.append(item) if isinstance(item, list): newList.append(FixupList(subConfig, key, item, parentName, fitBuild)) return newList #Enumerates elements in a dictionary and applies macro replacement to them def DoReplacements(subConfig, parentName, fitBuild): if parentName is None: if NAME in subConfig: if not isinstance(subConfig[NAME], basestring): raise ValueError("Name parameter must be a string") if subConfig[NAME].find(NAME_MACRO) != -1: raise ValueError("Name parameter cannot use the $NAME macro") parentName = subConfig[NAME] for key in subConfig: if key == NAME and not isinstance(subConfig[key], basestring): raise ValueError("Name parameter must be a string") if isinstance(subConfig[key], basestring): subConfig[key] = FixupString(subConfig, key, subConfig[key], parentName, fitBuild) elif isinstance(subConfig[key], dict): DoReplacements(subConfig, parentName) elif isinstance(subConfig[key], list): subConfig[key] = FixupList(subConfig, key, subConfig[key], parentName, fitBuild) #This is the actual implementation for _ProcessStringReplacements DoReplacements(Config, None, None) if ROM_IMAGE_BUILDS in Config: for subConfig in Config[ROM_IMAGE_BUILDS]: fitBuild = None if FIT_IMAGE_BUILD in subConfig: if not isinstance(subConfig[FIT_IMAGE_BUILD], basestring): raise ValueError("FitBuild parameter must be a string") if subConfig[NAME].find(FIT_BUILD_NAME_MACRO) != -1: raise ValueError("FitBuild parameter cannot use the $FIT_BUILD_NAME macro") fitBuild = subConfig[FIT_IMAGE_BUILD] if fitBuild is not None: DoReplacements(subConfig, None, fitBuild) def _CheckForDuplicates(Config): for (constant, errorName) in [(FIT_DEFINITIONS, "FitDefinitions"), (POST_PROCESSOR_DEFINITIONS, "PostProcessorDefinitions"), (FIT_IMAGE_BUILDS, "FitImageBuilds"), (ROM_IMAGE_BUILDS, "RomImageBuilds")]: if constant in Config: names = [x[NAME] for x in Config[constant]] if len(names) != len(set(names)): unique = set() for name in names: if name in unique: raise ValueError("Duplicate name \"%s\" in %s"%(name, errorName)) unique.add(name) raise ValueError("Duplicate in %s"%errorName) def _CheckReferences(Config): data = {} for (constant, variableName) in [(FIT_DEFINITIONS, "fitDefinitionNames"), (POST_PROCESSOR_DEFINITIONS, "postProcessorNames"), (FIT_IMAGE_BUILDS, "fitImageBuildNames")]: if constant in Config: exec("data['%s'] = [x[NAME] for x in Config[constant]]"%variableName) else: exec("data['%s'] = []"%variableName) if FIT_IMAGE_BUILDS in Config: for fitImageBuild in Config[FIT_IMAGE_BUILDS]: if fitImageBuild[FIT_DEFINITION] not in data["fitDefinitionNames"]: raise ValueError("FitDefinition with name \"%s\" does not exist. Referenced by \"%s\""%(fitImageBuild[FIT_DEFINITION], fitImageBuild[NAME])) if ROM_IMAGE_BUILDS in Config: for romImageBuild in Config[ROM_IMAGE_BUILDS]: if romImageBuild[FIT_IMAGE_BUILD] not in data["fitImageBuildNames"]: raise ValueError("FitImageBuild with name \"%s\" does not exist. Referenced by \"%s\""%(romImageBuild[FIT_IMAGE_BUILD], romImageBuild[NAME])) for outputFile in romImageBuild[OUTPUT_FILES]: for postProcess in outputFile[POST_PROCESSING]: if postProcess[POST_PROCESSOR_DEFINITION] not in data["postProcessorNames"]: raise ValueError("PostProcessorDefinition with name \"%\" does not exist. Referenced by \"%s\"."%(postProcess[POST_PROCESSOR_DEFINITION], romImageBuild[NAME])) def _LoadConfigFile(FilePath): if os.path.splitext(FilePath)[1].replace(os.extsep, "").lower() == "json": return _LoadJsonFile(FilePath) else: return _LoadXmlFile(FilePath) def _LoadJsonFile(FilePath): with open(FilePath) as dataFile: data = json.load(dataFile) return data def _LoadXmlFile(FilePath): data = {} tree = ElementTree.parse(FilePath) root = tree.getroot() if root.tag != XML_ROM_IMAGE_BUILD_CONFIG: raise ValueError("Invalid XML File, root element is not %s"%XML_ROM_IMAGE_BUILD_CONFIG) #Search for BiosVersion tags for biosVersion in root.findall(BIOS_VERSION): data[BIOS_VERSION] = biosVersion.text.strip() #Search for Include tags for include in root.findall(XML_INCLUDE): if INCLUDES not in data: data[INCLUDES] = [] data[INCLUDES].append(include.get(PATH)) #Search for FitDefinition tags for fitDefinition in root.findall(FIT_DEFINITION): newFitDefinition = {} newFitDefinition[NAME] = fitDefinition.get(NAME) newFitDefinition[COMMANDS] = [] for command in fitDefinition.findall(XML_COMMAND): newCommand = {} newCommand[OS] = command.get(OS) newCommand[PATH] = command.get(PATH) newFitDefinition[COMMANDS].append(newCommand) if FIT_DEFINITIONS not in data: data[FIT_DEFINITIONS] = [] data[FIT_DEFINITIONS].append(newFitDefinition) #Search for PostProcessorDefinition tags for postProcessorDefinition in root.findall(POST_PROCESSOR_DEFINITION): newPostProcessor = {} newPostProcessor[NAME] = postProcessorDefinition.get(NAME) newPostProcessor[COMMANDS] = [] for command in postProcessorDefinition.findall(XML_COMMAND): newCommand = {} newCommand[OS] = command.get(OS) newCommand[FILE_PATH] = command.get(FILE_PATH) newCommand[PRE_ARGUMENTS] = command.get(PRE_ARGUMENTS) newCommand[POST_ARGUMENTS] = command.get(POST_ARGUMENTS) newPostProcessor[COMMANDS].append(newCommand) if POST_PROCESSOR_DEFINITIONS not in data: data[POST_PROCESSOR_DEFINITIONS] = [] data[POST_PROCESSOR_DEFINITIONS].append(newPostProcessor) #Search for FitImageBuild tags for fitImageBuild in root.findall(FIT_IMAGE_BUILD): newFitImageBuild = {} newFitImageBuild[NAME] = fitImageBuild.get(NAME) newFitImageBuild[FIT_DEFINITION] = fitImageBuild.get(FIT_DEFINITION) newFitImageBuild[PATCH_FILE] = fitImageBuild.get(PATCH_FILE) newFitImageBuild[BIOS_ROM] = fitImageBuild.get(BIOS_ROM) for outputFile in fitImageBuild.findall(XML_OUTPUT_FILE): if OUTPUT_FILES not in newFitImageBuild: newFitImageBuild[OUTPUT_FILES] = [] newFitImageBuild[OUTPUT_FILES].append(outputFile.text.strip()) for fitOutputRename in fitImageBuild.findall(FIT_OUTPUT_RENAME): newFitOutputRename = {} newFitOutputRename[SRC] = fitOutputRename.get(SRC) newFitOutputRename[DEST] = fitOutputRename.get(DEST) if FIT_OUTPUT_RENAME not in newFitImageBuild: newFitImageBuild[FIT_OUTPUT_RENAME] = [] newFitImageBuild[FIT_OUTPUT_RENAME].append(newFitOutputRename) for fitOutputDiscard in fitImageBuild.findall(FIT_OUTPUT_DISCARD): if FIT_OUTPUT_DISCARD not in newFitImageBuild: newFitImageBuild[FIT_OUTPUT_DISCARD] = [] newFitImageBuild[FIT_OUTPUT_DISCARD].append(fitOutputDiscard.text.strip()) if FIT_IMAGE_BUILDS not in data: data[FIT_IMAGE_BUILDS] = [] data[FIT_IMAGE_BUILDS].append(newFitImageBuild) #Search for RomImageBuild tags for romImageBuild in root.findall(XML_ROM_IMAGE_BUILD): newRomImageBuild = {} newRomImageBuild[NAME] = romImageBuild.get(NAME) newRomImageBuild[FIT_IMAGE_BUILD] = romImageBuild.get(FIT_IMAGE_BUILD) for outputFile in romImageBuild.findall(XML_OUTPUT_FILE): newOutputFile = {} newOutputFile[FILE_NAME] = outputFile.get(FILE_NAME) for postProcessor in outputFile.findall(XML_POST_PROCESSOR): newPostProcessor = {} newPostProcessor[POST_PROCESSOR_DEFINITION] = postProcessor.get(POST_PROCESSOR_DEFINITION) newPostProcessor[ARGUMENTS] = postProcessor.text.strip() if POST_PROCESSING not in newOutputFile: newOutputFile[POST_PROCESSING] = [] newOutputFile[POST_PROCESSING].append(newPostProcessor) if OUTPUT_FILES not in newRomImageBuild: newRomImageBuild[OUTPUT_FILES] = [] newRomImageBuild[OUTPUT_FILES].append(newOutputFile) if ROM_IMAGE_BUILDS not in data: data[ROM_IMAGE_BUILDS] = [] data[ROM_IMAGE_BUILDS].append(newRomImageBuild) return data # # Filesystem Functions # def _DeleteFile(FilePath): if ostype == WIN: check_call("del /F %s"%FilePath, shell=True) else: check_call("rm -f %s"%FilePath, shell=True) def _RenameFile(Source, Destination): if ostype == WIN: check_call("move /Y %s %s"%(Source, Destination), shell=True) else: check_call("mv -f %s %s"%(Source, Destination), shell=True) def _CopyFile(Source, Destination): if ostype == WIN: check_call("copy /B /Y %s %s"%(Source, Destination), shell=True) else: check_call("cp -f %s %s"%(Source, Destination), shell=True) def _GetWinePath(FilePath): p = subprocess.Popen("winepath -w %s"%FilePath, shell=True, stdout=subprocess.PIPE) returnValue = p.stdout.read() p.wait() if p.returncode != 0: raise subprocess.CalledProcessError(p.returncode, "winepath -w %s"%FilePath) return returnValue.rstrip() def PrintUsage(CommandName=os.path.basename(__file__)): if ostype == WIN: quote = "" else: quote = "'" print("%s [-c configfile.json] [-l] [%sROMBUILDNAME1%s %sROMBUILDNAME2%s] ..."%(CommandName, quote, quote, quote, quote)) print("") print("ROMBUILDNAME1, ROMBUILDNAME2, etc. are names of RomImageBuild definitions") print("in the JSON configuration file. All specified ROMs will be generated.") print("To generate all possible ROMs, pass %s*%s as the build name. You can also"%(quote, quote)) print("use wildcards within ROM names, for example %sRVP*_Bx%s"%(quote, quote)) if ostype != WIN: print("") print("If wildcards (aka *) are used in the ROMBUILDNAME then enclose in it quotes (')") print("") print("-c Specify JSON config file, if omitted RomImageBuildConfig.json is used") print("-l List all available ROM Build Names and exit (does not run any builds)") print("-gu Generate Bash scripts to run all the builds") print("-gw Generate Batch (.bat) scripts to run all the builds") print("") print("Examples:") print("") print("%s 16MB_RVP3_Cx"%CommandName) print("%s -c MyConfig.json 16MB_RVP3_Cx 16MB_RVP7_Cx 16MB_RVP10_Cx"%CommandName) print("%s %s16MB_RVP*_Cx%s"%(CommandName, quote, quote)) print("%s -l"%CommandName) print("%s -gu %s*%s"%(CommandName, quote, quote)) ################### ## Main Function ## ################### def main(): try: if len(sys.argv) <= 1: PrintUsage() sys.exit(1) configFile = DEFAULT_CONFIG_FILE romBuilds = [] allBuilds = False listBuilds = False makeBashScripts = False makeBatScripts = False i = 1 while i < len(sys.argv): if sys.argv[i].strip().lower() == "-c": if i + 1 >= len(sys.argv): print("Error: Config file not specified") sys.exit(1) configFile = sys.argv[i+1] i += 1 elif sys.argv[i].strip().lower() == "-l": listBuilds = True elif sys.argv[i].strip() == "*": allBuilds = True elif sys.argv[i].strip().lower() == "-gu": makeBashScripts = True elif sys.argv[i].strip().lower() == "-gw": makeBatScripts = True else: romBuilds.append(sys.argv[i]) i += 1 if allBuilds and len(romBuilds) > 0: print("Error: \"*\" and specific ROM Build names were specified, this is redundant.") sys.exit(1) if listBuilds and (allBuilds or len(romBuilds) > 0): print("Error: Can not list build names and specify builds to be run") sys.exit(1) if len(romBuilds) != len(set(romBuilds)): unique = set() for build in romBuilds: if build in unique: print("Error: ROM Build \"%s\" specified twice"%build) sys.exit(1) unique.add(build) config = LoadConfig(configFile) allBuildNames = GetListOfRomImageBuilds(config) buildsToRun = [] if listBuilds: print(", ".join(allBuildNames)) sys.exit(0) if allBuilds: buildsToRun = allBuildNames else: for romBuild in romBuilds: for build in allBuildNames: if fnmatch.fnmatch(build, romBuild): if build not in buildsToRun: buildsToRun.append(build) if makeBashScripts: GenerateShellScripts(config, buildsToRun, configFile, True) if makeBatScripts: GenerateShellScripts(config, buildsToRun, configFile, False) if (not makeBashScripts) and (not makeBatScripts): BuildRomImages(config, buildsToRun) except Exception as e: print("Error: %s"%str(e)) print("") print("") print("Detailed Error Data:") print("-------------------------------------------------------------------------------") traceback.print_exc() print("-------------------------------------------------------------------------------") sys.exit(1) if __name__ == "__main__": main()