1 # @file ConvertMasmToNasm.py
2 # This script assists with conversion of MASM assembly syntax to NASM
4 # Copyright (c) 2007 - 2016, Intel Corporation. All rights reserved.<BR>
6 # SPDX-License-Identifier: BSD-2-Clause-Patent
9 from __future__
import print_function
22 class UnsupportedConversion(Exception):
26 class NoSourceFile(Exception):
30 class UnsupportedArch(Exception):
31 unsupported
= ('aarch64', 'arm', 'ebc', 'ipf')
36 # Version and Copyright
37 VersionNumber
= "0.01"
38 __version__
= "%prog Version " + VersionNumber
39 __copyright__
= "Copyright (c) 2007 - 2014, Intel Corporation. All rights reserved."
40 __usage__
= "%prog [options] source.asm [destination.nasm]"
42 def __init__(self
, clone
=None):
44 self
.args
= self
.ProcessCommandLine()
46 self
.args
= clone
.args
48 self
.unsupportedSyntaxSeen
= False
49 self
.src
= self
.args
.source
50 self
.keep
= self
.args
.keep
51 assert(os
.path
.exists(self
.src
))
52 self
.dirmode
= os
.path
.isdir(self
.src
)
53 srcExt
= os
.path
.splitext(self
.src
)[1]
54 assert (self
.dirmode
or srcExt
!= '.nasm')
55 self
.infmode
= not self
.dirmode
and srcExt
== '.inf'
56 self
.diff
= self
.args
.diff
57 self
.git
= self
.args
.git
58 self
.force
= self
.args
.force
61 self
.rootdir
= os
.getcwd()
64 self
.rootdir
= clone
.rootdir
65 self
.gitdir
= clone
.gitdir
66 self
.gitemail
= clone
.gitemail
68 def ProcessCommandLine(self
):
69 parser
= argparse
.ArgumentParser(description
=self
.__copyright
__)
70 parser
.add_argument('--version', action
='version',
71 version
='%(prog)s ' + self
.VersionNumber
)
72 parser
.add_argument("-q", "--quiet", action
="store_true",
73 help="Disable all messages except FATAL ERRORS.")
74 parser
.add_argument("--git", action
="store_true",
75 help="Use git to create commits for each file converted")
76 parser
.add_argument("--keep", action
="append", choices
=('asm', 's'),
78 help="Don't remove files with this extension")
79 parser
.add_argument("--diff", action
="store_true",
80 help="Show diff of conversion")
81 parser
.add_argument("-f", "--force", action
="store_true",
82 help="Force conversion even if unsupported")
83 parser
.add_argument('source', help='MASM input file')
84 parser
.add_argument('dest', nargs
='?',
85 help='NASM output file (default=input.nasm; - for stdout)')
87 return parser
.parse_args()
89 def RootRelative(self
, path
):
91 if result
.startswith(self
.rootdir
):
92 result
= result
[len(self
.rootdir
):]
93 while len(result
) > 0 and result
[0] in '/\\':
97 def MatchAndSetMo(self
, regexp
, string
):
98 self
.mo
= regexp
.match(string
)
99 return self
.mo
is not None
101 def SearchAndSetMo(self
, regexp
, string
):
102 self
.mo
= regexp
.search(string
)
103 return self
.mo
is not None
105 def ReplacePreserveSpacing(self
, string
, find
, replace
):
106 if len(find
) >= len(replace
):
107 padded
= replace
+ (' ' * (len(find
) - len(replace
)))
108 return string
.replace(find
, padded
)
109 elif find
.find(replace
) >= 0:
110 return string
.replace(find
, replace
)
112 lenDiff
= len(replace
) - len(find
)
114 for i
in range(lenDiff
, -1, -1):
115 padded
= find
+ (' ' * i
)
116 result
= result
.replace(padded
, replace
)
120 lastpath
= os
.path
.realpath(self
.src
)
123 path
= os
.path
.split(lastpath
)[0]
127 candidate
= os
.path
.join(path
, '.git')
128 if os
.path
.isdir(candidate
):
129 self
.gitdir
= candidate
130 self
.gitemail
= self
.FormatGitEmailAddress()
134 def FormatGitEmailAddress(self
):
135 if not self
.git
or not self
.gitdir
:
138 cmd
= ('git', 'config', 'user.name')
139 name
= self
.RunAndCaptureOutput(cmd
).strip()
140 cmd
= ('git', 'config', 'user.email')
141 email
= self
.RunAndCaptureOutput(cmd
).strip()
142 if name
.find(',') >= 0:
143 name
= '"' + name
+ '"'
144 return name
+ ' <' + email
+ '>'
146 def RunAndCaptureOutput(self
, cmd
, checkExitCode
=True, pipeIn
=None):
148 subpStdin
= subprocess
.PIPE
151 p
= subprocess
.Popen(args
=cmd
, stdout
=subprocess
.PIPE
, stdin
=subpStdin
)
152 (stdout
, stderr
) = p
.communicate(pipeIn
)
154 if p
.returncode
!= 0:
155 print('command:', ' '.join(cmd
))
156 print('stdout:', stdout
)
157 print('stderr:', stderr
)
158 print('return:', p
.returncode
)
159 assert p
.returncode
== 0
160 return stdout
.decode('utf-8', 'ignore')
162 def FileUpdated(self
, path
):
163 if not self
.git
or not self
.gitdir
:
166 cmd
= ('git', 'add', path
)
167 self
.RunAndCaptureOutput(cmd
)
169 def FileAdded(self
, path
):
170 self
.FileUpdated(path
)
172 def RemoveFile(self
, path
):
173 if not self
.git
or not self
.gitdir
:
176 if self
.ShouldKeepFile(path
):
179 cmd
= ('git', 'rm', path
)
180 self
.RunAndCaptureOutput(cmd
)
182 def ShouldKeepFile(self
, path
):
183 ext
= os
.path
.splitext(path
)[1].lower()
184 if ext
.startswith('.'):
186 return ext
in self
.keep
188 def FileConversionFinished(self
, pkg
, module
, src
, dst
):
189 if not self
.git
or not self
.gitdir
:
192 if not self
.args
.quiet
:
193 print('Committing: Conversion of', dst
)
195 prefix
= ' '.join(filter(lambda a
: a
, [pkg
, module
]))
197 if self
.unsupportedSyntaxSeen
:
199 message
+= '%s: Convert %s to NASM\n' % (prefix
, src
)
201 message
+= 'The %s script was used to convert\n' % sys
.argv
[0]
202 message
+= '%s to %s\n' % (src
, dst
)
204 message
+= 'Contributed-under: TianoCore Contribution Agreement 1.0\n'
205 assert(self
.gitemail
is not None)
206 message
+= 'Signed-off-by: %s\n' % self
.gitemail
207 message
= message
.encode('utf-8', 'ignore')
209 cmd
= ('git', 'commit', '-F', '-')
210 self
.RunAndCaptureOutput(cmd
, pipeIn
=message
)
213 class ConvertAsmFile(CommonUtils
):
215 def __init__(self
, src
, dst
, clone
):
216 CommonUtils
.__init
__(self
, clone
)
217 self
.ConvertAsmFile(src
, dst
)
221 def ConvertAsmFile(self
, inputFile
, outputFile
=None):
223 self
.unsupportedSyntaxSeen
= False
224 self
.inputFilename
= inputFile
226 outputFile
= os
.path
.splitext(inputFile
)[0] + '.nasm'
227 self
.outputFilename
= outputFile
229 fullSrc
= os
.path
.realpath(inputFile
)
230 srcParentDir
= os
.path
.basename(os
.path
.split(fullSrc
)[0])
231 maybeArch
= srcParentDir
.lower()
232 if maybeArch
in UnsupportedArch
.unsupported
:
233 raise UnsupportedArch
234 self
.ia32
= maybeArch
== 'ia32'
235 self
.x64
= maybeArch
== 'x64'
237 self
.inputFileBase
= os
.path
.basename(self
.inputFilename
)
238 self
.outputFileBase
= os
.path
.basename(self
.outputFilename
)
239 self
.output
= io
.BytesIO()
240 if not self
.args
.quiet
:
241 dirpath
, src
= os
.path
.split(self
.inputFilename
)
242 dirpath
= self
.RootRelative(dirpath
)
243 dst
= os
.path
.basename(self
.outputFilename
)
244 print('Converting:', dirpath
, src
, '->', dst
)
245 lines
= io
.open(self
.inputFilename
).readlines()
247 if self
.outputFilename
== '-' and not self
.diff
:
248 output_data
= self
.output
.getvalue()
249 if sys
.version_info
>= (3, 0):
250 output_data
= output_data
.decode('utf-8', 'ignore')
251 sys
.stdout
.write(output_data
)
254 f
= io
.open(self
.outputFilename
, 'wb')
255 f
.write(self
.output
.getvalue())
259 endOfLineRe
= re
.compile(r
'''
262 re
.VERBOSE | re
.MULTILINE
264 begOfLineRe
= re
.compile(r
'''
270 def Convert(self
, lines
):
272 self
.anonLabelCount
= -1
274 self
.oldAsmEmptyLineCount
= 0
275 self
.newAsmEmptyLineCount
= 0
277 mo
= self
.begOfLineRe
.search(line
)
278 assert mo
is not None
279 self
.indent
= mo
.group()
280 lineWithoutBeginning
= line
[len(self
.indent
):]
281 mo
= self
.endOfLineRe
.search(lineWithoutBeginning
)
285 endOfLine
= mo
.group()
286 oldAsm
= line
[len(self
.indent
):len(line
) - len(endOfLine
)]
287 self
.originalLine
= line
.rstrip()
288 if line
.strip() == '':
289 self
.oldAsmEmptyLineCount
+= 1
290 self
.TranslateAsm(oldAsm
, endOfLine
)
291 if line
.strip() != '':
292 self
.oldAsmEmptyLineCount
= 0
294 procDeclRe
= re
.compile(r
'''
295 (?: ASM_PFX \s* [(] \s* )?
299 (?: \s+ NEAR | FAR )?
301 (?: \s+ (PUBLIC | PRIVATE) )?
302 (?: \s+ USES ( (?: \s+ \w[\w0-9]* )+ ) )?
305 re
.VERBOSE | re
.IGNORECASE
308 procEndRe
= re
.compile(r
'''
313 re
.VERBOSE | re
.IGNORECASE
316 varAndTypeSubRe
= r
' (?: [\w@][\w@0-9]* ) (?: \s* : \s* \w+ )? '
317 publicRe
= re
.compile(r
'''
319 ( %s (?: \s* , \s* %s )* )
321 ''' % (varAndTypeSubRe
, varAndTypeSubRe
),
322 re
.VERBOSE | re
.IGNORECASE
325 varAndTypeSubRe
= re
.compile(varAndTypeSubRe
, re
.VERBOSE | re
.IGNORECASE
)
327 macroDeclRe
= re
.compile(r
'''
332 re
.VERBOSE | re
.IGNORECASE
335 sectionDeclRe
= re
.compile(r
'''
340 re
.VERBOSE | re
.IGNORECASE
343 externRe
= re
.compile(r
'''
344 EXTE?RN \s+ (?: C \s+ )?
345 ([\w@][\w@0-9]*) \s* : \s* (\w+)
348 re
.VERBOSE | re
.IGNORECASE
351 externdefRe
= re
.compile(r
'''
352 EXTERNDEF \s+ (?: C \s+ )?
353 ([\w@][\w@0-9]*) \s* : \s* (\w+)
356 re
.VERBOSE | re
.IGNORECASE
359 protoRe
= re
.compile(r
'''
365 re
.VERBOSE | re
.IGNORECASE
368 defineDataRe
= re
.compile(r
'''
370 ( db | dw | dd | dq ) \s+
374 re
.VERBOSE | re
.IGNORECASE
377 equRe
= re
.compile(r
'''
378 ([\w@][\w@0-9]*) \s+ EQU \s+ (\S.*?)
381 re
.VERBOSE | re
.IGNORECASE
384 ignoreRe
= re
.compile(r
'''
396 re
.VERBOSE | re
.IGNORECASE
399 whitespaceRe
= re
.compile(r
'\s+', re
.MULTILINE
)
401 def TranslateAsm(self
, oldAsm
, endOfLine
):
402 assert(oldAsm
.strip() == oldAsm
)
404 endOfLine
= endOfLine
.replace(self
.inputFileBase
, self
.outputFileBase
)
406 oldOp
= oldAsm
.split()
414 self
.EmitAsmWithComment(oldAsm
, newAsm
, endOfLine
)
415 elif oldOp
in ('#include', ):
417 self
.EmitLine(oldAsm
+ endOfLine
)
418 elif oldOp
.lower() in ('end', 'title', 'text'):
420 self
.EmitAsmWithComment(oldAsm
, newAsm
, endOfLine
)
421 elif oldAsm
.lower() == '@@:':
422 self
.anonLabelCount
+= 1
423 self
.EmitLine(self
.anonLabel(self
.anonLabelCount
) + ':')
424 elif self
.MatchAndSetMo(self
.ignoreRe
, oldAsm
):
426 self
.EmitAsmWithComment(oldAsm
, newAsm
, endOfLine
)
427 elif oldAsm
.lower() == 'ret':
428 for i
in range(len(self
.uses
) - 1, -1, -1):
429 register
= self
.uses
[i
]
430 self
.EmitNewContent('pop ' + register
)
432 self
.EmitAsmWithComment(oldAsm
, newAsm
, endOfLine
)
434 elif oldOp
.lower() == 'lea':
435 newAsm
= self
.ConvertLea(oldAsm
)
436 self
.EmitAsmWithComment(oldAsm
, newAsm
, endOfLine
)
437 elif oldAsm
.lower() == 'end':
439 self
.EmitAsmWithComment(oldAsm
, newAsm
, endOfLine
)
441 elif self
.MatchAndSetMo(self
.equRe
, oldAsm
):
442 equ
= self
.mo
.group(1)
443 newAsm
= '%%define %s %s' % (equ
, self
.mo
.group(2))
444 self
.EmitAsmWithComment(oldAsm
, newAsm
, endOfLine
)
445 elif self
.MatchAndSetMo(self
.externRe
, oldAsm
) or \
446 self
.MatchAndSetMo(self
.protoRe
, oldAsm
):
447 extern
= self
.mo
.group(1)
448 self
.NewGlobal(extern
)
449 newAsm
= 'extern ' + extern
450 self
.EmitAsmWithComment(oldAsm
, newAsm
, endOfLine
)
451 elif self
.MatchAndSetMo(self
.externdefRe
, oldAsm
):
453 self
.EmitAsmWithComment(oldAsm
, newAsm
, endOfLine
)
454 elif self
.MatchAndSetMo(self
.macroDeclRe
, oldAsm
):
455 newAsm
= '%%macro %s 0' % self
.mo
.group(1)
456 self
.EmitAsmWithComment(oldAsm
, newAsm
, endOfLine
)
457 elif oldOp
.lower() == 'endm':
458 newAsm
= r
'%endmacro'
459 self
.EmitAsmWithComment(oldAsm
, newAsm
, endOfLine
)
460 elif self
.MatchAndSetMo(self
.sectionDeclRe
, oldAsm
):
461 name
= self
.mo
.group(1)
462 ty
= self
.mo
.group(2)
463 if ty
.lower() == 'section':
467 self
.EmitAsmWithComment(oldAsm
, newAsm
, endOfLine
)
468 elif self
.MatchAndSetMo(self
.procDeclRe
, oldAsm
):
469 proc
= self
.proc
= self
.mo
.group(1)
470 visibility
= self
.mo
.group(2)
471 if visibility
is None:
474 visibility
= visibility
.lower()
475 if visibility
!= 'private':
476 self
.NewGlobal(self
.proc
)
477 proc
= 'ASM_PFX(' + proc
+ ')'
478 self
.EmitNewContent('global ' + proc
)
480 self
.EmitAsmWithComment(oldAsm
, newAsm
, endOfLine
)
481 uses
= self
.mo
.group(3)
483 uses
= tuple(filter(None, uses
.split()))
487 for register
in self
.uses
:
488 self
.EmitNewContent(' push ' + register
)
489 elif self
.MatchAndSetMo(self
.procEndRe
, oldAsm
):
491 self
.EmitAsmWithComment(oldAsm
, newAsm
, endOfLine
)
492 elif self
.MatchAndSetMo(self
.publicRe
, oldAsm
):
493 publics
= re
.findall(self
.varAndTypeSubRe
, self
.mo
.group(1))
494 publics
= tuple(map(lambda p
: p
.split(':')[0].strip(), publics
))
495 for i
in range(len(publics
) - 1):
497 self
.EmitNewContent('global ASM_PFX(%s)' % publics
[i
])
501 newAsm
= 'global ASM_PFX(%s)' % name
502 self
.EmitAsmWithComment(oldAsm
, newAsm
, endOfLine
)
503 elif self
.MatchAndSetMo(self
.defineDataRe
, oldAsm
):
504 name
= self
.mo
.group(1)
505 ty
= self
.mo
.group(2)
506 value
= self
.mo
.group(3)
509 newAsm
= '%s: %s %s' % (name
, ty
, value
)
510 newAsm
= self
.CommonConversions(newAsm
)
511 self
.EmitAsmWithComment(oldAsm
, newAsm
, endOfLine
)
513 newAsm
= self
.CommonConversions(oldAsm
)
514 self
.EmitAsmWithComment(oldAsm
, newAsm
, endOfLine
)
516 def NewGlobal(self
, name
):
517 regex
= re
.compile(r
'(?<![_\w\d])(?<!ASM_PFX\()(' + re
.escape(name
) +
519 self
.globals.add(regex
)
521 def ConvertAnonymousLabels(self
, oldAsm
):
523 anonLabel
= self
.anonLabel(self
.anonLabelCount
)
524 newAsm
= newAsm
.replace('@b', anonLabel
)
525 newAsm
= newAsm
.replace('@B', anonLabel
)
526 anonLabel
= self
.anonLabel(self
.anonLabelCount
+ 1)
527 newAsm
= newAsm
.replace('@f', anonLabel
)
528 newAsm
= newAsm
.replace('@F', anonLabel
)
531 def anonLabel(self
, count
):
534 def EmitString(self
, string
):
535 self
.output
.write(string
.encode('utf-8', 'ignore'))
537 def EmitLineWithDiff(self
, old
, new
):
538 newLine
= (self
.indent
+ new
).rstrip()
541 print('+%s' % newLine
)
544 print('+%s' % newLine
)
548 self
.newAsmEmptyLineCount
= 0
549 self
.EmitString(newLine
+ '\r\n')
551 def EmitLine(self
, string
):
552 self
.EmitLineWithDiff(self
.originalLine
, string
)
554 def EmitNewContent(self
, string
):
555 self
.EmitLineWithDiff(None, string
)
557 def EmitAsmReplaceOp(self
, oldAsm
, oldOp
, newOp
, endOfLine
):
558 newAsm
= oldAsm
.replace(oldOp
, newOp
, 1)
559 self
.EmitAsmWithComment(oldAsm
, newAsm
, endOfLine
)
561 hexNumRe
= re
.compile(r
'0*((?=[\da-f])\d*(?<=\d)[\da-f]*)h', re
.IGNORECASE
)
563 def EmitAsmWithComment(self
, oldAsm
, newAsm
, endOfLine
):
564 for glblRe
in self
.globals:
565 newAsm
= glblRe
.sub(r
'ASM_PFX(\1)', newAsm
)
567 newAsm
= self
.hexNumRe
.sub(r
'0x\1', newAsm
)
569 newLine
= newAsm
+ endOfLine
570 emitNewLine
= ((newLine
.strip() != '') or
571 ((oldAsm
+ endOfLine
).strip() == ''))
572 if emitNewLine
and newLine
.strip() == '':
573 self
.newAsmEmptyLineCount
+= 1
574 if self
.newAsmEmptyLineCount
> 1:
577 self
.EmitLine(newLine
.rstrip())
579 print('-%s' % self
.originalLine
)
581 leaRe
= re
.compile(r
'''
582 (lea \s+) ([\w@][\w@0-9]*) \s* , \s* (\S (?:.*\S)?)
585 re
.VERBOSE | re
.IGNORECASE
588 def ConvertLea(self
, oldAsm
):
590 if self
.MatchAndSetMo(self
.leaRe
, oldAsm
):
591 lea
= self
.mo
.group(1)
592 dst
= self
.mo
.group(2)
593 src
= self
.mo
.group(3)
594 if src
.find('[') < 0:
595 src
= '[' + src
+ ']'
596 newAsm
= lea
+ dst
+ ', ' + src
597 newAsm
= self
.CommonConversions(newAsm
)
600 ptrRe
= re
.compile(r
'''
602 ([dfq]?word|byte) \s+ (?: ptr ) (\s*)
605 re
.VERBOSE | re
.IGNORECASE
608 def ConvertPtr(self
, oldAsm
):
610 while self
.SearchAndSetMo(self
.ptrRe
, newAsm
):
611 ty
= self
.mo
.group(1)
612 if ty
.lower() == 'fword':
615 ty
+= self
.mo
.group(2)
616 newAsm
= newAsm
[:self
.mo
.start(0)] + ty
+ newAsm
[self
.mo
.end(0):]
619 labelByteRe
= re
.compile(r
'''
620 (?: \s+ label \s+ (?: [dfq]?word | byte ) )
623 re
.VERBOSE | re
.IGNORECASE
626 def ConvertLabelByte(self
, oldAsm
):
628 if self
.SearchAndSetMo(self
.labelByteRe
, newAsm
):
629 newAsm
= newAsm
[:self
.mo
.start(0)] + ':' + newAsm
[self
.mo
.end(0):]
632 unaryBitwiseOpRe
= re
.compile(r
'''
636 re
.VERBOSE | re
.IGNORECASE
638 binaryBitwiseOpRe
= re
.compile(r
'''
640 ( AND | OR | SHL | SHR )
643 re
.VERBOSE | re
.IGNORECASE
645 bitwiseOpReplacements
= {
653 def ConvertBitwiseOp(self
, oldAsm
):
655 while self
.SearchAndSetMo(self
.binaryBitwiseOpRe
, newAsm
):
656 prefix
= self
.mo
.group(1)
657 op
= self
.bitwiseOpReplacements
[self
.mo
.group(2).lower()]
658 newAsm
= newAsm
[:self
.mo
.start(0)] + prefix
+ op
+ \
659 newAsm
[self
.mo
.end(0):]
660 while self
.SearchAndSetMo(self
.unaryBitwiseOpRe
, newAsm
):
661 op
= self
.bitwiseOpReplacements
[self
.mo
.group(1).lower()]
662 newAsm
= newAsm
[:self
.mo
.start(0)] + op
+ newAsm
[self
.mo
.end(0):]
665 sectionRe
= re
.compile(r
'''
672 re
.VERBOSE | re
.IGNORECASE
675 segmentRe
= re
.compile(r
'''
682 re
.VERBOSE | re
.IGNORECASE
685 def ConvertSection(self
, oldAsm
):
687 if self
.MatchAndSetMo(self
.sectionRe
, newAsm
) or \
688 self
.MatchAndSetMo(self
.segmentRe
, newAsm
):
689 name
= self
.mo
.group(1).lower()
692 self
.EmitLine('DEFAULT REL')
694 newAsm
= 'SECTION .' + name
697 fwordRe
= re
.compile(r
'''
702 re
.VERBOSE | re
.IGNORECASE
705 def FwordUnsupportedCheck(self
, oldAsm
):
707 if self
.SearchAndSetMo(self
.fwordRe
, newAsm
):
708 newAsm
= self
.Unsupported(newAsm
, 'fword used')
711 __common_conversion_routines__
= (
712 ConvertAnonymousLabels
,
714 FwordUnsupportedCheck
,
720 def CommonConversions(self
, oldAsm
):
722 for conv
in self
.__common
_conversion
_routines
__:
723 newAsm
= conv(self
, newAsm
)
726 def Unsupported(self
, asm
, message
=None):
728 raise UnsupportedConversion
730 self
.unsupportedSyntaxSeen
= True
731 newAsm
= '%error conversion unsupported'
733 newAsm
+= '; ' + message
738 class ConvertInfFile(CommonUtils
):
740 def __init__(self
, inf
, clone
):
741 CommonUtils
.__init
__(self
, clone
)
743 self
.ScanInfAsmFiles()
745 self
.ConvertInfAsmFiles()
747 infSrcRe
= re
.compile(r
'''
749 ( [\w@][\w@0-9/]* \.(asm|s) )
754 re
.VERBOSE | re
.IGNORECASE
757 def GetInfAsmFileMapping(self
):
758 srcToDst
= {'order': []}
759 for line
in self
.lines
:
761 if self
.MatchAndSetMo(self
.infSrcRe
, line
):
762 src
= self
.mo
.group(1)
763 srcExt
= self
.mo
.group(2)
764 dst
= os
.path
.splitext(src
)[0] + '.nasm'
765 fullDst
= os
.path
.join(self
.dir, dst
)
766 if src
not in srcToDst
and not os
.path
.exists(fullDst
):
768 srcToDst
['order'].append(src
)
771 def ScanInfAsmFiles(self
):
773 assert os
.path
.isfile(src
)
774 f
= io
.open(src
, 'rt')
775 self
.lines
= f
.readlines()
778 path
= os
.path
.realpath(self
.inf
)
779 (self
.dir, inf
) = os
.path
.split(path
)
780 parent
= os
.path
.normpath(self
.dir)
781 (lastpath
, self
.moduleName
) = os
.path
.split(parent
)
782 self
.packageName
= None
784 lastpath
= os
.path
.normpath(lastpath
)
785 (parent
, basename
) = os
.path
.split(lastpath
)
786 if parent
== lastpath
:
788 if basename
.endswith('Pkg'):
789 self
.packageName
= basename
793 self
.srcToDst
= self
.GetInfAsmFileMapping()
795 self
.dstToSrc
= {'order': []}
796 for src
in self
.srcToDst
['order']:
797 srcExt
= os
.path
.splitext(src
)[1]
798 dst
= self
.srcToDst
[src
]
799 if dst
not in self
.dstToSrc
:
800 self
.dstToSrc
[dst
] = [src
]
801 self
.dstToSrc
['order'].append(dst
)
803 self
.dstToSrc
[dst
].append(src
)
806 return len(self
.dstToSrc
['order'])
809 return iter(self
.dstToSrc
['order'])
811 def ConvertInfAsmFiles(self
):
813 unsupportedArchCount
= 0
817 self
.UpdateInfAsmFile(dst
)
819 except UnsupportedConversion
:
820 if not self
.args
.quiet
:
821 print('MASM=>NASM conversion unsupported for', dst
)
822 notConverted
.append(dst
)
824 if not self
.args
.quiet
:
825 print('Source file missing for', reldst
)
826 notConverted
.append(dst
)
827 except UnsupportedArch
:
828 unsupportedArchCount
+= 1
831 self
.ConversionFinished(dst
)
832 if len(notConverted
) > 0 and not self
.args
.quiet
:
833 for dst
in notConverted
:
834 reldst
= self
.RootRelative(dst
)
835 print('Unabled to convert', reldst
)
836 if unsupportedArchCount
> 0 and not self
.args
.quiet
:
837 print('Skipped', unsupportedArchCount
, 'files based on architecture')
839 def UpdateInfAsmFile(self
, dst
, IgnoreMissingAsm
=False):
840 infPath
= os
.path
.split(os
.path
.realpath(self
.inf
))[0]
841 asmSrc
= os
.path
.splitext(dst
)[0] + '.asm'
842 fullSrc
= os
.path
.join(infPath
, asmSrc
)
843 fullDst
= os
.path
.join(infPath
, dst
)
844 srcParentDir
= os
.path
.basename(os
.path
.split(fullSrc
)[0])
845 if srcParentDir
.lower() in UnsupportedArch
.unsupported
:
846 raise UnsupportedArch
847 elif not os
.path
.exists(fullSrc
):
848 if not IgnoreMissingAsm
:
850 else: # not os.path.exists(fullDst):
851 conv
= ConvertAsmFile(fullSrc
, fullDst
, self
)
852 self
.unsupportedSyntaxSeen
= conv
.unsupportedSyntaxSeen
855 recentSources
= list()
857 while i
< len(self
.lines
):
858 line
= self
.lines
[i
].rstrip()
861 preserveOldSource
= False
862 for src
in self
.dstToSrc
[dst
]:
863 assert self
.srcToDst
[src
] == dst
864 updatedLine
= self
.ReplacePreserveSpacing(
865 updatedLine
, src
, dst
)
866 lineChanged
= updatedLine
!= line
868 preserveOldSource
= self
.ShouldKeepFile(src
)
872 if preserveOldSource
:
873 if updatedLine
.strip() not in recentSources
:
874 self
.lines
.insert(i
, updatedLine
+ '\n')
875 recentSources
.append(updatedLine
.strip())
878 print('+%s' % updatedLine
)
884 if updatedLine
.strip() in recentSources
:
887 self
.lines
[i
] = updatedLine
+ '\n'
888 recentSources
.append(updatedLine
.strip())
890 print('+%s' % updatedLine
)
892 if len(recentSources
) > 0:
893 recentSources
= list()
897 fileChanged |
= lineChanged
901 self
.lines
= list(filter(lambda l
: l
is not None, self
.lines
))
903 for src
in self
.dstToSrc
[dst
]:
904 if not src
.endswith('.asm'):
905 fullSrc
= os
.path
.join(infPath
, src
)
906 if os
.path
.exists(fullSrc
):
907 self
.RemoveFile(fullSrc
)
910 f
= io
.open(self
.inf
, 'w', newline
='\r\n')
911 f
.writelines(self
.lines
)
913 self
.FileUpdated(self
.inf
)
915 def ConversionFinished(self
, dst
):
916 asmSrc
= os
.path
.splitext(dst
)[0] + '.asm'
917 self
.FileConversionFinished(
918 self
.packageName
, self
.moduleName
, asmSrc
, dst
)
921 class ConvertInfFiles(CommonUtils
):
923 def __init__(self
, infs
, clone
):
924 CommonUtils
.__init
__(self
, clone
)
925 infs
= map(lambda i
: ConvertInfFile(i
, self
), infs
)
926 infs
= filter(lambda i
: len(i
) > 0, infs
)
927 dstToInfs
= {'order': []}
930 fulldst
= os
.path
.realpath(os
.path
.join(inf
.dir, dst
))
932 if fulldst
in dstToInfs
:
933 dstToInfs
[fulldst
].append(pair
)
935 dstToInfs
['order'].append(fulldst
)
936 dstToInfs
[fulldst
] = [pair
]
939 unsupportedArchCount
= 0
940 for dst
in dstToInfs
['order']:
943 for inf
, reldst
in dstToInfs
[dst
]:
944 inf
.UpdateInfAsmFile(reldst
, IgnoreMissingAsm
=didSomething
)
946 except UnsupportedConversion
:
947 if not self
.args
.quiet
:
948 print('MASM=>NASM conversion unsupported for', reldst
)
949 notConverted
.append(dst
)
951 if not self
.args
.quiet
:
952 print('Source file missing for', reldst
)
953 notConverted
.append(dst
)
954 except UnsupportedArch
:
955 unsupportedArchCount
+= 1
958 inf
.ConversionFinished(reldst
)
959 if len(notConverted
) > 0 and not self
.args
.quiet
:
960 for dst
in notConverted
:
961 reldst
= self
.RootRelative(dst
)
962 print('Unabled to convert', reldst
)
963 if unsupportedArchCount
> 0 and not self
.args
.quiet
:
964 print('Skipped', unsupportedArchCount
, 'files based on architecture')
967 class ConvertDirectories(CommonUtils
):
969 def __init__(self
, paths
, clone
):
970 CommonUtils
.__init
__(self
, clone
)
972 self
.ConvertInfAndAsmFiles()
974 def ConvertInfAndAsmFiles(self
):
976 for path
in self
.paths
:
977 assert(os
.path
.exists(path
))
978 for path
in self
.paths
:
979 for root
, dirs
, files
in os
.walk(path
):
980 for d
in ('.svn', '.git'):
984 if f
.lower().endswith('.inf'):
985 inf
= os
.path
.realpath(os
.path
.join(root
, f
))
988 ConvertInfFiles(infs
, self
)
991 class ConvertAsmApp(CommonUtils
):
994 CommonUtils
.__init
__(self
)
996 src
= self
.args
.source
999 ConvertInfFiles((src
,), self
)
1001 ConvertDirectories((src
,), self
)
1002 elif not self
.dirmode
:
1003 ConvertAsmFile(src
, dst
, self
)