868 lines
38 KiB
Python
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()
|