alder_lake_bios/Intel/AlderLake/AlderLakePlatSamplePkg/Tools/RomImage/BuildRomImages/BuildRomImages.py

868 lines
38 KiB
Python

#!/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()