# @file ConvertMasmToNasm.py\r
# This script assists with conversion of MASM assembly syntax to NASM\r
#\r
-# Copyright (c) 2007 - 2014, Intel Corporation. All rights reserved.<BR>\r
+# Copyright (c) 2007 - 2016, Intel Corporation. All rights reserved.<BR>\r
#\r
# This program and the accompanying materials\r
# are licensed and made available under the terms and conditions of the BSD License\r
# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
#\r
\r
+from __future__ import print_function\r
+\r
#\r
# Import Modules\r
#\r
+import argparse\r
+import io\r
import os.path\r
import re\r
-import StringIO\r
import subprocess\r
import sys\r
-from optparse import OptionParser\r
\r
\r
class UnsupportedConversion(Exception):\r
\r
def __init__(self, clone=None):\r
if clone is None:\r
- (self.Opt, self.Args) = self.ProcessCommandLine()\r
+ self.args = self.ProcessCommandLine()\r
else:\r
- (self.Opt, self.Args) = (clone.Opt, clone.Args)\r
+ self.args = clone.args\r
\r
self.unsupportedSyntaxSeen = False\r
- self.src = self.Args[0]\r
+ self.src = self.args.source\r
+ self.keep = self.args.keep\r
assert(os.path.exists(self.src))\r
self.dirmode = os.path.isdir(self.src)\r
srcExt = os.path.splitext(self.src)[1]\r
assert (self.dirmode or srcExt != '.nasm')\r
self.infmode = not self.dirmode and srcExt == '.inf'\r
- self.diff = self.Opt.diff\r
- self.git = self.Opt.git\r
- self.force = self.Opt.force\r
+ self.diff = self.args.diff\r
+ self.git = self.args.git\r
+ self.force = self.args.force\r
\r
if clone is None:\r
self.rootdir = os.getcwd()\r
self.gitemail = clone.gitemail\r
\r
def ProcessCommandLine(self):\r
- Parser = OptionParser(description=self.__copyright__,\r
- version=self.__version__,\r
- prog=sys.argv[0],\r
- usage=self.__usage__\r
- )\r
- Parser.add_option("-q", "--quiet", action="store_true", type=None,\r
- help="Disable all messages except FATAL ERRORS.")\r
- Parser.add_option("--git", action="store_true", type=None,\r
- help="Use git to create commits for each file converted")\r
- Parser.add_option("--diff", action="store_true", type=None,\r
- help="Show diff of conversion")\r
- Parser.add_option("-f", "--force", action="store_true", type=None,\r
- help="Force conversion even if unsupported")\r
-\r
- (Opt, Args) = Parser.parse_args()\r
-\r
- if not Opt.quiet:\r
- print self.__copyright__\r
- Parser.print_version()\r
-\r
- return (Opt, Args)\r
+ parser = argparse.ArgumentParser(description=self.__copyright__)\r
+ parser.add_argument('--version', action='version',\r
+ version='%(prog)s ' + self.VersionNumber)\r
+ parser.add_argument("-q", "--quiet", action="store_true",\r
+ help="Disable all messages except FATAL ERRORS.")\r
+ parser.add_argument("--git", action="store_true",\r
+ help="Use git to create commits for each file converted")\r
+ parser.add_argument("--keep", action="append", choices=('asm', 's'),\r
+ default=[],\r
+ help="Don't remove files with this extension")\r
+ parser.add_argument("--diff", action="store_true",\r
+ help="Show diff of conversion")\r
+ parser.add_argument("-f", "--force", action="store_true",\r
+ help="Force conversion even if unsupported")\r
+ parser.add_argument('source', help='MASM input file')\r
+ parser.add_argument('dest', nargs='?',\r
+ help='NASM output file (default=input.nasm; - for stdout)')\r
+\r
+ return parser.parse_args()\r
\r
def RootRelative(self, path):\r
result = path\r
while True:\r
path = os.path.split(lastpath)[0]\r
if path == lastpath:\r
+ self.gitemail = None\r
return\r
candidate = os.path.join(path, '.git')\r
if os.path.isdir(candidate):\r
(stdout, stderr) = p.communicate(pipeIn)\r
if checkExitCode:\r
if p.returncode != 0:\r
- print 'command:', ' '.join(cmd)\r
- print 'stdout:', stdout\r
- print 'stderr:', stderr\r
- print 'return:', p.returncode\r
+ print('command:', ' '.join(cmd))\r
+ print('stdout:', stdout)\r
+ print('stderr:', stderr)\r
+ print('return:', p.returncode)\r
assert p.returncode == 0\r
- return stdout\r
+ return stdout.decode('utf-8', 'ignore')\r
\r
def FileUpdated(self, path):\r
if not self.git or not self.gitdir:\r
if not self.git or not self.gitdir:\r
return\r
\r
+ if self.ShouldKeepFile(path):\r
+ return\r
+\r
cmd = ('git', 'rm', path)\r
self.RunAndCaptureOutput(cmd)\r
\r
+ def ShouldKeepFile(self, path):\r
+ ext = os.path.splitext(path)[1].lower()\r
+ if ext.startswith('.'):\r
+ ext = ext[1:]\r
+ return ext in self.keep\r
+\r
def FileConversionFinished(self, pkg, module, src, dst):\r
if not self.git or not self.gitdir:\r
return\r
\r
- if not self.Opt.quiet:\r
- print 'Committing: Conversion of', dst\r
+ if not self.args.quiet:\r
+ print('Committing: Conversion of', dst)\r
\r
prefix = ' '.join(filter(lambda a: a, [pkg, module]))\r
message = ''\r
message += '%s to %s\n' % (src, dst)\r
message += '\n'\r
message += 'Contributed-under: TianoCore Contribution Agreement 1.0\n'\r
+ assert(self.gitemail is not None)\r
message += 'Signed-off-by: %s\n' % self.gitemail\r
+ message = message.encode('utf-8', 'ignore')\r
\r
cmd = ('git', 'commit', '-F', '-')\r
self.RunAndCaptureOutput(cmd, pipeIn=message)\r
\r
self.inputFileBase = os.path.basename(self.inputFilename)\r
self.outputFileBase = os.path.basename(self.outputFilename)\r
- if self.outputFilename == '-' and not self.diff:\r
- self.output = sys.stdout\r
- else:\r
- self.output = StringIO.StringIO()\r
- if not self.Opt.quiet:\r
+ self.output = io.BytesIO()\r
+ if not self.args.quiet:\r
dirpath, src = os.path.split(self.inputFilename)\r
dirpath = self.RootRelative(dirpath)\r
dst = os.path.basename(self.outputFilename)\r
- print 'Converting:', dirpath, src, '->', dst\r
- lines = open(self.inputFilename).readlines()\r
+ print('Converting:', dirpath, src, '->', dst)\r
+ lines = io.open(self.inputFilename).readlines()\r
self.Convert(lines)\r
- if self.outputFilename == '-':\r
- if self.diff:\r
- sys.stdout.write(self.output.getvalue())\r
- self.output.close()\r
+ if self.outputFilename == '-' and not self.diff:\r
+ output_data = self.output.getvalue()\r
+ if sys.version_info >= (3, 0):\r
+ output_data = output_data.decode('utf-8', 'ignore')\r
+ sys.stdout.write(output_data)\r
+ self.output.close()\r
else:\r
- f = open(self.outputFilename, 'wb')\r
+ f = io.open(self.outputFilename, 'wb')\r
f.write(self.output.getvalue())\r
f.close()\r
self.output.close()\r
self.oldAsmEmptyLineCount = 0\r
\r
procDeclRe = re.compile(r'''\r
- ([\w@][\w@0-9]*) \s+\r
+ (?: ASM_PFX \s* [(] \s* )?\r
+ ([\w@][\w@0-9]*) \s*\r
+ [)]? \s+\r
PROC\r
(?: \s+ NEAR | FAR )?\r
(?: \s+ C )?\r
self.EmitAsmWithComment(oldAsm, newAsm, endOfLine)\r
uses = self.mo.group(3)\r
if uses is not None:\r
- uses = filter(None, uses.split())\r
+ uses = tuple(filter(None, uses.split()))\r
else:\r
uses = tuple()\r
self.uses = uses\r
self.EmitAsmWithComment(oldAsm, newAsm, endOfLine)\r
elif self.MatchAndSetMo(self.publicRe, oldAsm):\r
publics = re.findall(self.varAndTypeSubRe, self.mo.group(1))\r
- publics = map(lambda p: p.split(':')[0].strip(), publics)\r
+ publics = tuple(map(lambda p: p.split(':')[0].strip(), publics))\r
for i in range(len(publics) - 1):\r
name = publics[i]\r
self.EmitNewContent('global ASM_PFX(%s)' % publics[i])\r
return '.%d' % count\r
\r
def EmitString(self, string):\r
- self.output.write(string)\r
+ self.output.write(string.encode('utf-8', 'ignore'))\r
\r
def EmitLineWithDiff(self, old, new):\r
newLine = (self.indent + new).rstrip()\r
if self.diff:\r
if old is None:\r
- print '+%s' % newLine\r
+ print('+%s' % newLine)\r
elif newLine != old:\r
- print '-%s' % old\r
- print '+%s' % newLine\r
+ print('-%s' % old)\r
+ print('+%s' % newLine)\r
else:\r
- print '', newLine\r
+ print('', newLine)\r
if newLine != '':\r
self.newAsmEmptyLineCount = 0\r
self.EmitString(newLine + '\r\n')\r
if emitNewLine:\r
self.EmitLine(newLine.rstrip())\r
elif self.diff:\r
- print '-%s' % self.originalLine\r
+ print('-%s' % self.originalLine)\r
\r
leaRe = re.compile(r'''\r
(lea \s+) ([\w@][\w@0-9]*) \s* , \s* (\S (?:.*\S)?)\r
src = self.mo.group(1)\r
srcExt = self.mo.group(2)\r
dst = os.path.splitext(src)[0] + '.nasm'\r
- if src not in srcToDst:\r
+ fullDst = os.path.join(self.dir, dst)\r
+ if src not in srcToDst and not os.path.exists(fullDst):\r
srcToDst[src] = dst\r
srcToDst['order'].append(src)\r
return srcToDst\r
def ScanInfAsmFiles(self):\r
src = self.inf\r
assert os.path.isfile(src)\r
- f = open(src)\r
+ f = io.open(src, 'rt')\r
self.lines = f.readlines()\r
f.close()\r
\r
unsupportedArchCount = 0\r
for dst in self:\r
didSomething = False\r
- fileChanged = self.UpdateInfAsmFile(dst)\r
try:\r
self.UpdateInfAsmFile(dst)\r
didSomething = True\r
except UnsupportedConversion:\r
- if not self.Opt.quiet:\r
- print 'MASM=>NASM conversion unsupported for', dst\r
+ if not self.args.quiet:\r
+ print('MASM=>NASM conversion unsupported for', dst)\r
notConverted.append(dst)\r
except NoSourceFile:\r
- if not self.Opt.quiet:\r
- print 'Source file missing for', reldst\r
+ if not self.args.quiet:\r
+ print('Source file missing for', reldst)\r
notConverted.append(dst)\r
except UnsupportedArch:\r
unsupportedArchCount += 1\r
else:\r
if didSomething:\r
self.ConversionFinished(dst)\r
- if len(notConverted) > 0 and not self.Opt.quiet:\r
+ if len(notConverted) > 0 and not self.args.quiet:\r
for dst in notConverted:\r
reldst = self.RootRelative(dst)\r
- print 'Unabled to convert', reldst\r
- if unsupportedArchCount > 0 and not self.Opt.quiet:\r
- print 'Skipped', unsupportedArchCount, 'files based on architecture'\r
+ print('Unabled to convert', reldst)\r
+ if unsupportedArchCount > 0 and not self.args.quiet:\r
+ print('Skipped', unsupportedArchCount, 'files based on architecture')\r
\r
def UpdateInfAsmFile(self, dst, IgnoreMissingAsm=False):\r
infPath = os.path.split(os.path.realpath(self.inf))[0]\r
conv = ConvertAsmFile(fullSrc, fullDst, self)\r
self.unsupportedSyntaxSeen = conv.unsupportedSyntaxSeen\r
\r
- lastLine = ''\r
fileChanged = False\r
- for i in range(len(self.lines)):\r
+ recentSources = list()\r
+ i = 0\r
+ while i < len(self.lines):\r
line = self.lines[i].rstrip()\r
updatedLine = line\r
+ lineChanged = False\r
+ preserveOldSource = False\r
for src in self.dstToSrc[dst]:\r
assert self.srcToDst[src] == dst\r
updatedLine = self.ReplacePreserveSpacing(\r
updatedLine, src, dst)\r
+ lineChanged = updatedLine != line\r
+ if lineChanged:\r
+ preserveOldSource = self.ShouldKeepFile(src)\r
+ break\r
\r
- lineChanged = updatedLine != line\r
if lineChanged:\r
- if lastLine.strip() == updatedLine.strip():\r
- self.lines[i] = None\r
+ if preserveOldSource:\r
+ if updatedLine.strip() not in recentSources:\r
+ self.lines.insert(i, updatedLine + '\n')\r
+ recentSources.append(updatedLine.strip())\r
+ i += 1\r
+ if self.diff:\r
+ print('+%s' % updatedLine)\r
+ if self.diff:\r
+ print('', line)\r
else:\r
- self.lines[i] = updatedLine + '\r\n'\r
-\r
- if self.diff:\r
- if lineChanged:\r
- print '-%s' % line\r
- if self.lines[i] is not None:\r
- print '+%s' % updatedLine\r
- else:\r
- print '', line\r
+ if self.diff:\r
+ print('-%s' % line)\r
+ if updatedLine.strip() in recentSources:\r
+ self.lines[i] = None\r
+ else:\r
+ self.lines[i] = updatedLine + '\n'\r
+ recentSources.append(updatedLine.strip())\r
+ if self.diff:\r
+ print('+%s' % updatedLine)\r
+ else:\r
+ if len(recentSources) > 0:\r
+ recentSources = list()\r
+ if self.diff:\r
+ print('', line)\r
\r
fileChanged |= lineChanged\r
- if self.lines[i] is not None:\r
- lastLine = self.lines[i]\r
+ i += 1\r
\r
if fileChanged:\r
- self.lines = filter(lambda l: l is not None, self.lines)\r
+ self.lines = list(filter(lambda l: l is not None, self.lines))\r
\r
for src in self.dstToSrc[dst]:\r
if not src.endswith('.asm'):\r
self.RemoveFile(fullSrc)\r
\r
if fileChanged:\r
- f = open(self.inf, 'wb')\r
+ f = io.open(self.inf, 'w', newline='\r\n')\r
f.writelines(self.lines)\r
f.close()\r
self.FileUpdated(self.inf)\r
inf.UpdateInfAsmFile(reldst, IgnoreMissingAsm=didSomething)\r
didSomething = True\r
except UnsupportedConversion:\r
- if not self.Opt.quiet:\r
- print 'MASM=>NASM conversion unsupported for', reldst\r
+ if not self.args.quiet:\r
+ print('MASM=>NASM conversion unsupported for', reldst)\r
notConverted.append(dst)\r
except NoSourceFile:\r
- if not self.Opt.quiet:\r
- print 'Source file missing for', reldst\r
+ if not self.args.quiet:\r
+ print('Source file missing for', reldst)\r
notConverted.append(dst)\r
except UnsupportedArch:\r
unsupportedArchCount += 1\r
else:\r
if didSomething:\r
inf.ConversionFinished(reldst)\r
- if len(notConverted) > 0 and not self.Opt.quiet:\r
+ if len(notConverted) > 0 and not self.args.quiet:\r
for dst in notConverted:\r
reldst = self.RootRelative(dst)\r
- print 'Unabled to convert', reldst\r
- if unsupportedArchCount > 0 and not self.Opt.quiet:\r
- print 'Skipped', unsupportedArchCount, 'files based on architecture'\r
+ print('Unabled to convert', reldst)\r
+ if unsupportedArchCount > 0 and not self.args.quiet:\r
+ print('Skipped', unsupportedArchCount, 'files based on architecture')\r
\r
\r
class ConvertDirectories(CommonUtils):\r
def __init__(self):\r
CommonUtils.__init__(self)\r
\r
- numArgs = len(self.Args)\r
- assert(numArgs >= 1)\r
+ src = self.args.source\r
+ dst = self.args.dest\r
if self.infmode:\r
- ConvertInfFiles(self.Args, self)\r
+ ConvertInfFiles((src,), self)\r
elif self.dirmode:\r
- ConvertDirectories(self.Args, self)\r
+ ConvertDirectories((src,), self)\r
elif not self.dirmode:\r
- assert(numArgs <= 2)\r
- src = self.Args[0]\r
- if numArgs > 1:\r
- dst = self.Args[1]\r
- else:\r
- dst = None\r
ConvertAsmFile(src, dst, self)\r
\r
ConvertAsmApp()\r