BaseTools: Add ConvertMasmToNasm.py script

This script is intended to assist with MASM to NASM syntax
conversions.

The output should be manually inspected and adjusted as needed, since
this script does not provide a perfect conversion.

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Jordan Justen <jordan.l.justen@intel.com>
Acked-by: Yingke D Liu <yingke.d.liu@intel.com>

git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@16285 6f19259b-4bc3-4df7-8a09-765794883524
This commit is contained in:
Jordan Justen 2014-10-31 19:55:15 +00:00 committed by jljusten
parent beaaa3b715
commit fe7ad7f6af
1 changed files with 986 additions and 0 deletions

View File

@ -0,0 +1,986 @@
# @file ConvertMasmToNasm.py
# This script assists with conversion of MASM assembly syntax to NASM
#
# Copyright (c) 2007 - 2014, Intel Corporation. All rights reserved.<BR>
#
# This program and the accompanying materials
# are licensed and made available under the terms and conditions of the BSD License
# which accompanies this distribution. The full text of the license may be found at
# http://opensource.org/licenses/bsd-license.php
#
# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
#
#
# Import Modules
#
import os.path
import re
import StringIO
import subprocess
import sys
from optparse import OptionParser
class UnsupportedConversion(Exception):
pass
class NoSourceFile(Exception):
pass
class UnsupportedArch(Exception):
unsupported = ('aarch64', 'arm', 'ebc', 'ipf')
class CommonUtils:
# Version and Copyright
VersionNumber = "0.01"
__version__ = "%prog Version " + VersionNumber
__copyright__ = "Copyright (c) 2007 - 2014, Intel Corporation. All rights reserved."
__usage__ = "%prog [options] source.asm [destination.nasm]"
def __init__(self, clone=None):
if clone is None:
(self.Opt, self.Args) = self.ProcessCommandLine()
else:
(self.Opt, self.Args) = (clone.Opt, clone.Args)
self.unsupportedSyntaxSeen = False
self.src = self.Args[0]
assert(os.path.exists(self.src))
self.dirmode = os.path.isdir(self.src)
srcExt = os.path.splitext(self.src)[1]
assert (self.dirmode or srcExt != '.nasm')
self.infmode = not self.dirmode and srcExt == '.inf'
self.diff = self.Opt.diff
self.git = self.Opt.git
self.force = self.Opt.force
if clone is None:
self.rootdir = os.getcwd()
self.DetectGit()
else:
self.rootdir = clone.rootdir
self.gitdir = clone.gitdir
self.gitemail = clone.gitemail
def ProcessCommandLine(self):
Parser = OptionParser(description=self.__copyright__,
version=self.__version__,
prog=sys.argv[0],
usage=self.__usage__
)
Parser.add_option("-q", "--quiet", action="store_true", type=None,
help="Disable all messages except FATAL ERRORS.")
Parser.add_option("--git", action="store_true", type=None,
help="Use git to create commits for each file converted")
Parser.add_option("--diff", action="store_true", type=None,
help="Show diff of conversion")
Parser.add_option("-f", "--force", action="store_true", type=None,
help="Force conversion even if unsupported")
(Opt, Args) = Parser.parse_args()
if not Opt.quiet:
print self.__copyright__
Parser.print_version()
return (Opt, Args)
def RootRelative(self, path):
result = path
if result.startswith(self.rootdir):
result = result[len(self.rootdir):]
while len(result) > 0 and result[0] in '/\\':
result = result[1:]
return result
def MatchAndSetMo(self, regexp, string):
self.mo = regexp.match(string)
return self.mo is not None
def SearchAndSetMo(self, regexp, string):
self.mo = regexp.search(string)
return self.mo is not None
def ReplacePreserveSpacing(self, string, find, replace):
if len(find) >= len(replace):
padded = replace + (' ' * (len(find) - len(replace)))
return string.replace(find, padded)
elif find.find(replace) >= 0:
return string.replace(find, replace)
else:
lenDiff = len(replace) - len(find)
result = string
for i in range(lenDiff, -1, -1):
padded = find + (' ' * i)
result = result.replace(padded, replace)
return result
def DetectGit(self):
lastpath = os.path.realpath(self.src)
self.gitdir = None
while True:
path = os.path.split(lastpath)[0]
if path == lastpath:
return
candidate = os.path.join(path, '.git')
if os.path.isdir(candidate):
self.gitdir = candidate
self.gitemail = self.FormatGitEmailAddress()
return
lastpath = path
def FormatGitEmailAddress(self):
if not self.git or not self.gitdir:
return ''
cmd = ('git', 'config', 'user.name')
name = self.RunAndCaptureOutput(cmd).strip()
cmd = ('git', 'config', 'user.email')
email = self.RunAndCaptureOutput(cmd).strip()
if name.find(',') >= 0:
name = '"' + name + '"'
return name + ' <' + email + '>'
def RunAndCaptureOutput(self, cmd, checkExitCode=True, pipeIn=None):
if pipeIn:
subpStdin = subprocess.PIPE
else:
subpStdin = None
p = subprocess.Popen(args=cmd, stdout=subprocess.PIPE, stdin=subpStdin)
(stdout, stderr) = p.communicate(pipeIn)
if checkExitCode:
if p.returncode != 0:
print 'command:', ' '.join(cmd)
print 'stdout:', stdout
print 'stderr:', stderr
print 'return:', p.returncode
assert p.returncode == 0
return stdout
def FileUpdated(self, path):
if not self.git or not self.gitdir:
return
cmd = ('git', 'add', path)
self.RunAndCaptureOutput(cmd)
def FileAdded(self, path):
self.FileUpdated(path)
def RemoveFile(self, path):
if not self.git or not self.gitdir:
return
cmd = ('git', 'rm', path)
self.RunAndCaptureOutput(cmd)
def FileConversionFinished(self, pkg, module, src, dst):
if not self.git or not self.gitdir:
return
if not self.Opt.quiet:
print 'Committing: Conversion of', dst
prefix = ' '.join(filter(lambda a: a, [pkg, module]))
message = ''
if self.unsupportedSyntaxSeen:
message += 'ERROR! '
message += '%s: Convert %s to NASM\n' % (prefix, src)
message += '\n'
message += 'The %s script was used to convert\n' % sys.argv[0]
message += '%s to %s\n' % (src, dst)
message += '\n'
message += 'Contributed-under: TianoCore Contribution Agreement 1.0\n'
message += 'Signed-off-by: %s\n' % self.gitemail
cmd = ('git', 'commit', '-F', '-')
self.RunAndCaptureOutput(cmd, pipeIn=message)
class ConvertAsmFile(CommonUtils):
def __init__(self, src, dst, clone):
CommonUtils.__init__(self, clone)
self.ConvertAsmFile(src, dst)
self.FileAdded(dst)
self.RemoveFile(src)
def ConvertAsmFile(self, inputFile, outputFile=None):
self.globals = set()
self.unsupportedSyntaxSeen = False
self.inputFilename = inputFile
if not outputFile:
outputFile = os.path.splitext(inputFile)[0] + '.nasm'
self.outputFilename = outputFile
fullSrc = os.path.realpath(inputFile)
srcParentDir = os.path.basename(os.path.split(fullSrc)[0])
maybeArch = srcParentDir.lower()
if maybeArch in UnsupportedArch.unsupported:
raise UnsupportedArch
self.ia32 = maybeArch == 'ia32'
self.x64 = maybeArch == 'x64'
self.inputFileBase = os.path.basename(self.inputFilename)
self.outputFileBase = os.path.basename(self.outputFilename)
if self.outputFilename == '-' and not self.diff:
self.output = sys.stdout
else:
self.output = StringIO.StringIO()
if not self.Opt.quiet:
dirpath, src = os.path.split(self.inputFilename)
dirpath = self.RootRelative(dirpath)
dst = os.path.basename(self.outputFilename)
print 'Converting:', dirpath, src, '->', dst
lines = open(self.inputFilename).readlines()
self.Convert(lines)
if self.outputFilename == '-':
if self.diff:
sys.stdout.write(self.output.getvalue())
self.output.close()
else:
f = open(self.outputFilename, 'wb')
f.write(self.output.getvalue())
f.close()
self.output.close()
endOfLineRe = re.compile(r'''
\s* ( ; .* )? \n $
''',
re.VERBOSE | re.MULTILINE
)
begOfLineRe = re.compile(r'''
\s*
''',
re.VERBOSE
)
def Convert(self, lines):
self.proc = None
self.anonLabelCount = -1
output = self.output
self.oldAsmEmptyLineCount = 0
self.newAsmEmptyLineCount = 0
for line in lines:
mo = self.begOfLineRe.search(line)
assert mo is not None
self.indent = mo.group()
lineWithoutBeginning = line[len(self.indent):]
mo = self.endOfLineRe.search(lineWithoutBeginning)
if mo is None:
endOfLine = ''
else:
endOfLine = mo.group()
oldAsm = line[len(self.indent):len(line) - len(endOfLine)]
self.originalLine = line.rstrip()
if line.strip() == '':
self.oldAsmEmptyLineCount += 1
self.TranslateAsm(oldAsm, endOfLine)
if line.strip() != '':
self.oldAsmEmptyLineCount = 0
procDeclRe = re.compile(r'''
([\w@][\w@0-9]*) \s+
PROC
(?: \s+ NEAR | FAR )?
(?: \s+ C )?
(?: \s+ (PUBLIC | PRIVATE) )?
(?: \s+ USES ( (?: \s+ \w[\w0-9]* )+ ) )?
\s* $
''',
re.VERBOSE | re.IGNORECASE
)
procEndRe = re.compile(r'''
([\w@][\w@0-9]*) \s+
ENDP
\s* $
''',
re.VERBOSE | re.IGNORECASE
)
varAndTypeSubRe = r' (?: [\w@][\w@0-9]* ) (?: \s* : \s* \w+ )? '
publicRe = re.compile(r'''
PUBLIC \s+
( %s (?: \s* , \s* %s )* )
\s* $
''' % (varAndTypeSubRe, varAndTypeSubRe),
re.VERBOSE | re.IGNORECASE
)
varAndTypeSubRe = re.compile(varAndTypeSubRe, re.VERBOSE | re.IGNORECASE)
macroDeclRe = re.compile(r'''
([\w@][\w@0-9]*) \s+
MACRO
\s* $
''',
re.VERBOSE | re.IGNORECASE
)
sectionDeclRe = re.compile(r'''
([\w@][\w@0-9]*) \s+
( SECTION | ENDS )
\s* $
''',
re.VERBOSE | re.IGNORECASE
)
externRe = re.compile(r'''
EXTE?RN \s+ (?: C \s+ )?
([\w@][\w@0-9]*) \s* : \s* (\w+)
\s* $
''',
re.VERBOSE | re.IGNORECASE
)
externdefRe = re.compile(r'''
EXTERNDEF \s+ (?: C \s+ )?
([\w@][\w@0-9]*) \s* : \s* (\w+)
\s* $
''',
re.VERBOSE | re.IGNORECASE
)
protoRe = re.compile(r'''
([\w@][\w@0-9]*) \s+
PROTO
(?: \s+ .* )?
\s* $
''',
re.VERBOSE | re.IGNORECASE
)
defineDataRe = re.compile(r'''
([\w@][\w@0-9]*) \s+
( db | dw | dd | dq ) \s+
( .*? )
\s* $
''',
re.VERBOSE | re.IGNORECASE
)
equRe = re.compile(r'''
([\w@][\w@0-9]*) \s+ EQU \s+ (\S.*?)
\s* $
''',
re.VERBOSE | re.IGNORECASE
)
ignoreRe = re.compile(r'''
\. (?: const |
mmx |
model |
xmm |
x?list |
[3-6]86p?
) |
page
(?: \s+ .* )?
\s* $
''',
re.VERBOSE | re.IGNORECASE
)
whitespaceRe = re.compile(r'\s+', re.MULTILINE)
def TranslateAsm(self, oldAsm, endOfLine):
assert(oldAsm.strip() == oldAsm)
endOfLine = endOfLine.replace(self.inputFileBase, self.outputFileBase)
oldOp = oldAsm.split()
if len(oldOp) >= 1:
oldOp = oldOp[0]
else:
oldOp = ''
if oldAsm == '':
newAsm = oldAsm
self.EmitAsmWithComment(oldAsm, newAsm, endOfLine)
elif oldOp in ('#include', ):
newAsm = oldAsm
self.EmitLine(oldAsm + endOfLine)
elif oldOp.lower() in ('end', 'title', 'text'):
newAsm = ''
self.EmitAsmWithComment(oldAsm, newAsm, endOfLine)
elif oldAsm.lower() == '@@:':
self.anonLabelCount += 1
self.EmitLine(self.anonLabel(self.anonLabelCount) + ':')
elif self.MatchAndSetMo(self.ignoreRe, oldAsm):
newAsm = ''
self.EmitAsmWithComment(oldAsm, newAsm, endOfLine)
elif oldAsm.lower() == 'ret':
for i in range(len(self.uses) - 1, -1, -1):
register = self.uses[i]
self.EmitNewContent('pop ' + register)
newAsm = 'ret'
self.EmitAsmWithComment(oldAsm, newAsm, endOfLine)
self.uses = tuple()
elif oldOp.lower() == 'lea':
newAsm = self.ConvertLea(oldAsm)
self.EmitAsmWithComment(oldAsm, newAsm, endOfLine)
elif oldAsm.lower() == 'end':
newAsm = ''
self.EmitAsmWithComment(oldAsm, newAsm, endOfLine)
self.uses = tuple()
elif self.MatchAndSetMo(self.equRe, oldAsm):
equ = self.mo.group(1)
newAsm = '%%define %s %s' % (equ, self.mo.group(2))
self.EmitAsmWithComment(oldAsm, newAsm, endOfLine)
elif self.MatchAndSetMo(self.externRe, oldAsm) or \
self.MatchAndSetMo(self.protoRe, oldAsm):
extern = self.mo.group(1)
self.NewGlobal(extern)
newAsm = 'extern ' + extern
self.EmitAsmWithComment(oldAsm, newAsm, endOfLine)
elif self.MatchAndSetMo(self.externdefRe, oldAsm):
newAsm = ''
self.EmitAsmWithComment(oldAsm, newAsm, endOfLine)
elif self.MatchAndSetMo(self.macroDeclRe, oldAsm):
newAsm = '%%macro %s 0' % self.mo.group(1)
self.EmitAsmWithComment(oldAsm, newAsm, endOfLine)
elif oldOp.lower() == 'endm':
newAsm = r'%endmacro'
self.EmitAsmWithComment(oldAsm, newAsm, endOfLine)
elif self.MatchAndSetMo(self.sectionDeclRe, oldAsm):
name = self.mo.group(1)
ty = self.mo.group(2)
if ty.lower() == 'section':
newAsm = '.' + name
else:
newAsm = ''
self.EmitAsmWithComment(oldAsm, newAsm, endOfLine)
elif self.MatchAndSetMo(self.procDeclRe, oldAsm):
proc = self.proc = self.mo.group(1)
visibility = self.mo.group(2)
if visibility is None:
visibility = ''
else:
visibility = visibility.lower()
if visibility != 'private':
self.NewGlobal(self.proc)
proc = 'ASM_PFX(' + proc + ')'
self.EmitNewContent('global ' + proc)
newAsm = proc + ':'
self.EmitAsmWithComment(oldAsm, newAsm, endOfLine)
uses = self.mo.group(3)
if uses is not None:
uses = filter(None, uses.split())
else:
uses = tuple()
self.uses = uses
for register in self.uses:
self.EmitNewContent(' push ' + register)
elif self.MatchAndSetMo(self.procEndRe, oldAsm):
newAsm = ''
self.EmitAsmWithComment(oldAsm, newAsm, endOfLine)
elif self.MatchAndSetMo(self.publicRe, oldAsm):
publics = re.findall(self.varAndTypeSubRe, self.mo.group(1))
publics = map(lambda p: p.split(':')[0].strip(), publics)
for i in range(len(publics) - 1):
name = publics[i]
self.EmitNewContent('global ASM_PFX(%s)' % publics[i])
self.NewGlobal(name)
name = publics[-1]
self.NewGlobal(name)
newAsm = 'global ASM_PFX(%s)' % name
self.EmitAsmWithComment(oldAsm, newAsm, endOfLine)
elif self.MatchAndSetMo(self.defineDataRe, oldAsm):
name = self.mo.group(1)
ty = self.mo.group(2)
value = self.mo.group(3)
if value == '?':
value = 0
newAsm = '%s: %s %s' % (name, ty, value)
newAsm = self.CommonConversions(newAsm)
self.EmitAsmWithComment(oldAsm, newAsm, endOfLine)
else:
newAsm = self.CommonConversions(oldAsm)
self.EmitAsmWithComment(oldAsm, newAsm, endOfLine)
def NewGlobal(self, name):
regex = re.compile(r'(?<![_\w\d])(?<!ASM_PFX\()(' + re.escape(name) +
r')(?![_\w\d])')
self.globals.add(regex)
def ConvertAnonymousLabels(self, oldAsm):
newAsm = oldAsm
anonLabel = self.anonLabel(self.anonLabelCount)
newAsm = newAsm.replace('@b', anonLabel)
newAsm = newAsm.replace('@B', anonLabel)
anonLabel = self.anonLabel(self.anonLabelCount + 1)
newAsm = newAsm.replace('@f', anonLabel)
newAsm = newAsm.replace('@F', anonLabel)
return newAsm
def anonLabel(self, count):
return '.%d' % count
def EmitString(self, string):
self.output.write(string)
def EmitLineWithDiff(self, old, new):
newLine = (self.indent + new).rstrip()
if self.diff:
if old is None:
print '+%s' % newLine
elif newLine != old:
print '-%s' % old
print '+%s' % newLine
else:
print '', newLine
if newLine != '':
self.newAsmEmptyLineCount = 0
self.EmitString(newLine + '\r\n')
def EmitLine(self, string):
self.EmitLineWithDiff(self.originalLine, string)
def EmitNewContent(self, string):
self.EmitLineWithDiff(None, string)
def EmitAsmReplaceOp(self, oldAsm, oldOp, newOp, endOfLine):
newAsm = oldAsm.replace(oldOp, newOp, 1)
self.EmitAsmWithComment(oldAsm, newAsm, endOfLine)
hexNumRe = re.compile(r'0*((?=[\da-f])\d*(?<=\d)[\da-f]*)h', re.IGNORECASE)
def EmitAsmWithComment(self, oldAsm, newAsm, endOfLine):
for glblRe in self.globals:
newAsm = glblRe.sub(r'ASM_PFX(\1)', newAsm)
newAsm = self.hexNumRe.sub(r'0x\1', newAsm)
newLine = newAsm + endOfLine
emitNewLine = ((newLine.strip() != '') or
((oldAsm + endOfLine).strip() == ''))
if emitNewLine and newLine.strip() == '':
self.newAsmEmptyLineCount += 1
if self.newAsmEmptyLineCount > 1:
emitNewLine = False
if emitNewLine:
self.EmitLine(newLine.rstrip())
elif self.diff:
print '-%s' % self.originalLine
leaRe = re.compile(r'''
(lea \s+) ([\w@][\w@0-9]*) \s* , \s* (\S (?:.*\S)?)
\s* $
''',
re.VERBOSE | re.IGNORECASE
)
def ConvertLea(self, oldAsm):
newAsm = oldAsm
if self.MatchAndSetMo(self.leaRe, oldAsm):
lea = self.mo.group(1)
dst = self.mo.group(2)
src = self.mo.group(3)
if src.find('[') < 0:
src = '[' + src + ']'
newAsm = lea + dst + ', ' + src
newAsm = self.CommonConversions(newAsm)
return newAsm
ptrRe = re.compile(r'''
(?<! \S )
([dfq]?word|byte) \s+ (?: ptr ) (\s*)
(?= [[\s] )
''',
re.VERBOSE | re.IGNORECASE
)
def ConvertPtr(self, oldAsm):
newAsm = oldAsm
while self.SearchAndSetMo(self.ptrRe, newAsm):
ty = self.mo.group(1)
if ty.lower() == 'fword':
ty = ''
else:
ty += self.mo.group(2)
newAsm = newAsm[:self.mo.start(0)] + ty + newAsm[self.mo.end(0):]
return newAsm
labelByteRe = re.compile(r'''
(?: \s+ label \s+ (?: [dfq]?word | byte ) )
(?! \S )
''',
re.VERBOSE | re.IGNORECASE
)
def ConvertLabelByte(self, oldAsm):
newAsm = oldAsm
if self.SearchAndSetMo(self.labelByteRe, newAsm):
newAsm = newAsm[:self.mo.start(0)] + ':' + newAsm[self.mo.end(0):]
return newAsm
unaryBitwiseOpRe = re.compile(r'''
( NOT )
(?= \s+ \S )
''',
re.VERBOSE | re.IGNORECASE
)
binaryBitwiseOpRe = re.compile(r'''
( \S \s+ )
( AND | OR | SHL | SHR )
(?= \s+ \S )
''',
re.VERBOSE | re.IGNORECASE
)
bitwiseOpReplacements = {
'not': '~',
'and': '&',
'shl': '<<',
'shr': '>>',
'or': '|',
}
def ConvertBitwiseOp(self, oldAsm):
newAsm = oldAsm
while self.SearchAndSetMo(self.binaryBitwiseOpRe, newAsm):
prefix = self.mo.group(1)
op = self.bitwiseOpReplacements[self.mo.group(2).lower()]
newAsm = newAsm[:self.mo.start(0)] + prefix + op + \
newAsm[self.mo.end(0):]
while self.SearchAndSetMo(self.unaryBitwiseOpRe, newAsm):
op = self.bitwiseOpReplacements[self.mo.group(1).lower()]
newAsm = newAsm[:self.mo.start(0)] + op + newAsm[self.mo.end(0):]
return newAsm
sectionRe = re.compile(r'''
\. ( code |
data
)
(?: \s+ .* )?
\s* $
''',
re.VERBOSE | re.IGNORECASE
)
segmentRe = re.compile(r'''
( code |
data )
(?: \s+ SEGMENT )
(?: \s+ .* )?
\s* $
''',
re.VERBOSE | re.IGNORECASE
)
def ConvertSection(self, oldAsm):
newAsm = oldAsm
if self.MatchAndSetMo(self.sectionRe, newAsm) or \
self.MatchAndSetMo(self.segmentRe, newAsm):
name = self.mo.group(1).lower()
if name == 'code':
if self.x64:
self.EmitLine('DEFAULT REL')
name = 'text'
newAsm = 'SECTION .' + name
return newAsm
fwordRe = re.compile(r'''
(?<! \S )
fword
(?! \S )
''',
re.VERBOSE | re.IGNORECASE
)
def FwordUnsupportedCheck(self, oldAsm):
newAsm = oldAsm
if self.SearchAndSetMo(self.fwordRe, newAsm):
newAsm = self.Unsupported(newAsm, 'fword used')
return newAsm
__common_conversion_routines__ = (
ConvertAnonymousLabels,
ConvertPtr,
FwordUnsupportedCheck,
ConvertBitwiseOp,
ConvertLabelByte,
ConvertSection,
)
def CommonConversions(self, oldAsm):
newAsm = oldAsm
for conv in self.__common_conversion_routines__:
newAsm = conv(self, newAsm)
return newAsm
def Unsupported(self, asm, message=None):
if not self.force:
raise UnsupportedConversion
self.unsupportedSyntaxSeen = True
newAsm = '%error conversion unsupported'
if message:
newAsm += '; ' + message
newAsm += ': ' + asm
return newAsm
class ConvertInfFile(CommonUtils):
def __init__(self, inf, clone):
CommonUtils.__init__(self, clone)
self.inf = inf
self.ScanInfAsmFiles()
if self.infmode:
self.ConvertInfAsmFiles()
infSrcRe = re.compile(r'''
\s*
( [\w@][\w@0-9/]* \.(asm|s) )
\s* (?: \| [^#]* )?
\s* (?: \# .* )?
$
''',
re.VERBOSE | re.IGNORECASE
)
def GetInfAsmFileMapping(self):
srcToDst = {'order': []}
for line in self.lines:
line = line.rstrip()
if self.MatchAndSetMo(self.infSrcRe, line):
src = self.mo.group(1)
srcExt = self.mo.group(2)
dst = os.path.splitext(src)[0] + '.nasm'
if src not in srcToDst:
srcToDst[src] = dst
srcToDst['order'].append(src)
return srcToDst
def ScanInfAsmFiles(self):
src = self.inf
assert os.path.isfile(src)
f = open(src)
self.lines = f.readlines()
f.close()
path = os.path.realpath(self.inf)
(self.dir, inf) = os.path.split(path)
parent = os.path.normpath(self.dir)
(lastpath, self.moduleName) = os.path.split(parent)
self.packageName = None
while True:
lastpath = os.path.normpath(lastpath)
(parent, basename) = os.path.split(lastpath)
if parent == lastpath:
break
if basename.endswith('Pkg'):
self.packageName = basename
break
lastpath = parent
self.srcToDst = self.GetInfAsmFileMapping()
self.dstToSrc = {'order': []}
for src in self.srcToDst['order']:
srcExt = os.path.splitext(src)[1]
dst = self.srcToDst[src]
if dst not in self.dstToSrc:
self.dstToSrc[dst] = [src]
self.dstToSrc['order'].append(dst)
else:
self.dstToSrc[dst].append(src)
def __len__(self):
return len(self.dstToSrc['order'])
def __iter__(self):
return iter(self.dstToSrc['order'])
def ConvertInfAsmFiles(self):
notConverted = []
unsupportedArchCount = 0
for dst in self:
didSomething = False
fileChanged = self.UpdateInfAsmFile(dst)
try:
self.UpdateInfAsmFile(dst)
didSomething = True
except UnsupportedConversion:
if not self.Opt.quiet:
print 'MASM=>NASM conversion unsupported for', dst
notConverted.append(dst)
except NoSourceFile:
if not self.Opt.quiet:
print 'Source file missing for', reldst
notConverted.append(dst)
except UnsupportedArch:
unsupportedArchCount += 1
else:
if didSomething:
self.ConversionFinished(dst)
if len(notConverted) > 0 and not self.Opt.quiet:
for dst in notConverted:
reldst = self.RootRelative(dst)
print 'Unabled to convert', reldst
if unsupportedArchCount > 0 and not self.Opt.quiet:
print 'Skipped', unsupportedArchCount, 'files based on architecture'
def UpdateInfAsmFile(self, dst, IgnoreMissingAsm=False):
infPath = os.path.split(os.path.realpath(self.inf))[0]
asmSrc = os.path.splitext(dst)[0] + '.asm'
fullSrc = os.path.join(infPath, asmSrc)
fullDst = os.path.join(infPath, dst)
srcParentDir = os.path.basename(os.path.split(fullSrc)[0])
if srcParentDir.lower() in UnsupportedArch.unsupported:
raise UnsupportedArch
elif not os.path.exists(fullSrc):
if not IgnoreMissingAsm:
raise NoSourceFile
else: # not os.path.exists(fullDst):
conv = ConvertAsmFile(fullSrc, fullDst, self)
self.unsupportedSyntaxSeen = conv.unsupportedSyntaxSeen
lastLine = ''
fileChanged = False
for i in range(len(self.lines)):
line = self.lines[i].rstrip()
updatedLine = line
for src in self.dstToSrc[dst]:
assert self.srcToDst[src] == dst
updatedLine = self.ReplacePreserveSpacing(
updatedLine, src, dst)
lineChanged = updatedLine != line
if lineChanged:
if lastLine.strip() == updatedLine.strip():
self.lines[i] = None
else:
self.lines[i] = updatedLine + '\r\n'
if self.diff:
if lineChanged:
print '-%s' % line
if self.lines[i] is not None:
print '+%s' % updatedLine
else:
print '', line
fileChanged |= lineChanged
if self.lines[i] is not None:
lastLine = self.lines[i]
if fileChanged:
self.lines = filter(lambda l: l is not None, self.lines)
for src in self.dstToSrc[dst]:
if not src.endswith('.asm'):
fullSrc = os.path.join(infPath, src)
if os.path.exists(fullSrc):
self.RemoveFile(fullSrc)
if fileChanged:
f = open(self.inf, 'wb')
f.writelines(self.lines)
f.close()
self.FileUpdated(self.inf)
def ConversionFinished(self, dst):
asmSrc = os.path.splitext(dst)[0] + '.asm'
self.FileConversionFinished(
self.packageName, self.moduleName, asmSrc, dst)
class ConvertInfFiles(CommonUtils):
def __init__(self, infs, clone):
CommonUtils.__init__(self, clone)
infs = map(lambda i: ConvertInfFile(i, self), infs)
infs = filter(lambda i: len(i) > 0, infs)
dstToInfs = {'order': []}
for inf in infs:
for dst in inf:
fulldst = os.path.realpath(os.path.join(inf.dir, dst))
pair = (inf, dst)
if fulldst in dstToInfs:
dstToInfs[fulldst].append(pair)
else:
dstToInfs['order'].append(fulldst)
dstToInfs[fulldst] = [pair]
notConverted = []
unsupportedArchCount = 0
for dst in dstToInfs['order']:
didSomething = False
try:
for inf, reldst in dstToInfs[dst]:
inf.UpdateInfAsmFile(reldst, IgnoreMissingAsm=didSomething)
didSomething = True
except UnsupportedConversion:
if not self.Opt.quiet:
print 'MASM=>NASM conversion unsupported for', reldst
notConverted.append(dst)
except NoSourceFile:
if not self.Opt.quiet:
print 'Source file missing for', reldst
notConverted.append(dst)
except UnsupportedArch:
unsupportedArchCount += 1
else:
if didSomething:
inf.ConversionFinished(reldst)
if len(notConverted) > 0 and not self.Opt.quiet:
for dst in notConverted:
reldst = self.RootRelative(dst)
print 'Unabled to convert', reldst
if unsupportedArchCount > 0 and not self.Opt.quiet:
print 'Skipped', unsupportedArchCount, 'files based on architecture'
class ConvertDirectories(CommonUtils):
def __init__(self, paths, clone):
CommonUtils.__init__(self, clone)
self.paths = paths
self.ConvertInfAndAsmFiles()
def ConvertInfAndAsmFiles(self):
infs = list()
for path in self.paths:
assert(os.path.exists(path))
for path in self.paths:
for root, dirs, files in os.walk(path):
for d in ('.svn', '.git'):
if d in dirs:
dirs.remove(d)
for f in files:
if f.lower().endswith('.inf'):
inf = os.path.realpath(os.path.join(root, f))
infs.append(inf)
ConvertInfFiles(infs, self)
class ConvertAsmApp(CommonUtils):
def __init__(self):
CommonUtils.__init__(self)
numArgs = len(self.Args)
assert(numArgs >= 1)
if self.infmode:
ConvertInfFiles(self.Args, self)
elif self.dirmode:
ConvertDirectories(self.Args, self)
elif not self.dirmode:
assert(numArgs <= 2)
src = self.Args[0]
if numArgs > 1:
dst = self.Args[1]
else:
dst = None
ConvertAsmFile(src, dst, self)
ConvertAsmApp()