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 # This program and the accompanying materials
7 # are licensed and made available under the terms and conditions of the BSD License
8 # which accompanies this distribution. The full text of the license may be found at
9 # http://opensource.org/licenses/bsd-license.php
11 # THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
15 from __future__
import print_function
28 class UnsupportedConversion(Exception):
32 class NoSourceFile(Exception):
36 class UnsupportedArch(Exception):
37 unsupported
= ('aarch64', 'arm', 'ebc', 'ipf')
42 # Version and Copyright
43 VersionNumber
= "0.01"
44 __version__
= "%prog Version " + VersionNumber
45 __copyright__
= "Copyright (c) 2007 - 2014, Intel Corporation. All rights reserved."
46 __usage__
= "%prog [options] source.asm [destination.nasm]"
48 def __init__(self
, clone
=None):
50 self
.args
= self
.ProcessCommandLine()
52 self
.args
= clone
.args
54 self
.unsupportedSyntaxSeen
= False
55 self
.src
= self
.args
.source
56 self
.keep
= self
.args
.keep
57 assert(os
.path
.exists(self
.src
))
58 self
.dirmode
= os
.path
.isdir(self
.src
)
59 srcExt
= os
.path
.splitext(self
.src
)[1]
60 assert (self
.dirmode
or srcExt
!= '.nasm')
61 self
.infmode
= not self
.dirmode
and srcExt
== '.inf'
62 self
.diff
= self
.args
.diff
63 self
.git
= self
.args
.git
64 self
.force
= self
.args
.force
67 self
.rootdir
= os
.getcwd()
70 self
.rootdir
= clone
.rootdir
71 self
.gitdir
= clone
.gitdir
72 self
.gitemail
= clone
.gitemail
74 def ProcessCommandLine(self
):
75 parser
= argparse
.ArgumentParser(description
=self
.__copyright
__)
76 parser
.add_argument('--version', action
='version',
77 version
='%(prog)s ' + self
.VersionNumber
)
78 parser
.add_argument("-q", "--quiet", action
="store_true",
79 help="Disable all messages except FATAL ERRORS.")
80 parser
.add_argument("--git", action
="store_true",
81 help="Use git to create commits for each file converted")
82 parser
.add_argument("--keep", action
="append", choices
=('asm', 's'),
84 help="Don't remove files with this extension")
85 parser
.add_argument("--diff", action
="store_true",
86 help="Show diff of conversion")
87 parser
.add_argument("-f", "--force", action
="store_true",
88 help="Force conversion even if unsupported")
89 parser
.add_argument('source', help='MASM input file')
90 parser
.add_argument('dest', nargs
='?',
91 help='NASM output file (default=input.nasm; - for stdout)')
93 return parser
.parse_args()
95 def RootRelative(self
, path
):
97 if result
.startswith(self
.rootdir
):
98 result
= result
[len(self
.rootdir
):]
99 while len(result
) > 0 and result
[0] in '/\\':
103 def MatchAndSetMo(self
, regexp
, string
):
104 self
.mo
= regexp
.match(string
)
105 return self
.mo
is not None
107 def SearchAndSetMo(self
, regexp
, string
):
108 self
.mo
= regexp
.search(string
)
109 return self
.mo
is not None
111 def ReplacePreserveSpacing(self
, string
, find
, replace
):
112 if len(find
) >= len(replace
):
113 padded
= replace
+ (' ' * (len(find
) - len(replace
)))
114 return string
.replace(find
, padded
)
115 elif find
.find(replace
) >= 0:
116 return string
.replace(find
, replace
)
118 lenDiff
= len(replace
) - len(find
)
120 for i
in range(lenDiff
, -1, -1):
121 padded
= find
+ (' ' * i
)
122 result
= result
.replace(padded
, replace
)
126 lastpath
= os
.path
.realpath(self
.src
)
129 path
= os
.path
.split(lastpath
)[0]
133 candidate
= os
.path
.join(path
, '.git')
134 if os
.path
.isdir(candidate
):
135 self
.gitdir
= candidate
136 self
.gitemail
= self
.FormatGitEmailAddress()
140 def FormatGitEmailAddress(self
):
141 if not self
.git
or not self
.gitdir
:
144 cmd
= ('git', 'config', 'user.name')
145 name
= self
.RunAndCaptureOutput(cmd
).strip()
146 cmd
= ('git', 'config', 'user.email')
147 email
= self
.RunAndCaptureOutput(cmd
).strip()
148 if name
.find(',') >= 0:
149 name
= '"' + name
+ '"'
150 return name
+ ' <' + email
+ '>'
152 def RunAndCaptureOutput(self
, cmd
, checkExitCode
=True, pipeIn
=None):
154 subpStdin
= subprocess
.PIPE
157 p
= subprocess
.Popen(args
=cmd
, stdout
=subprocess
.PIPE
, stdin
=subpStdin
)
158 (stdout
, stderr
) = p
.communicate(pipeIn
)
160 if p
.returncode
!= 0:
161 print('command:', ' '.join(cmd
))
162 print('stdout:', stdout
)
163 print('stderr:', stderr
)
164 print('return:', p
.returncode
)
165 assert p
.returncode
== 0
166 return stdout
.decode('utf-8', 'ignore')
168 def FileUpdated(self
, path
):
169 if not self
.git
or not self
.gitdir
:
172 cmd
= ('git', 'add', path
)
173 self
.RunAndCaptureOutput(cmd
)
175 def FileAdded(self
, path
):
176 self
.FileUpdated(path
)
178 def RemoveFile(self
, path
):
179 if not self
.git
or not self
.gitdir
:
182 if self
.ShouldKeepFile(path
):
185 cmd
= ('git', 'rm', path
)
186 self
.RunAndCaptureOutput(cmd
)
188 def ShouldKeepFile(self
, path
):
189 ext
= os
.path
.splitext(path
)[1].lower()
190 if ext
.startswith('.'):
192 return ext
in self
.keep
194 def FileConversionFinished(self
, pkg
, module
, src
, dst
):
195 if not self
.git
or not self
.gitdir
:
198 if not self
.args
.quiet
:
199 print('Committing: Conversion of', dst
)
201 prefix
= ' '.join(filter(lambda a
: a
, [pkg
, module
]))
203 if self
.unsupportedSyntaxSeen
:
205 message
+= '%s: Convert %s to NASM\n' % (prefix
, src
)
207 message
+= 'The %s script was used to convert\n' % sys
.argv
[0]
208 message
+= '%s to %s\n' % (src
, dst
)
210 message
+= 'Contributed-under: TianoCore Contribution Agreement 1.0\n'
211 assert(self
.gitemail
is not None)
212 message
+= 'Signed-off-by: %s\n' % self
.gitemail
213 message
= message
.encode('utf-8', 'ignore')
215 cmd
= ('git', 'commit', '-F', '-')
216 self
.RunAndCaptureOutput(cmd
, pipeIn
=message
)
219 class ConvertAsmFile(CommonUtils
):
221 def __init__(self
, src
, dst
, clone
):
222 CommonUtils
.__init
__(self
, clone
)
223 self
.ConvertAsmFile(src
, dst
)
227 def ConvertAsmFile(self
, inputFile
, outputFile
=None):
229 self
.unsupportedSyntaxSeen
= False
230 self
.inputFilename
= inputFile
232 outputFile
= os
.path
.splitext(inputFile
)[0] + '.nasm'
233 self
.outputFilename
= outputFile
235 fullSrc
= os
.path
.realpath(inputFile
)
236 srcParentDir
= os
.path
.basename(os
.path
.split(fullSrc
)[0])
237 maybeArch
= srcParentDir
.lower()
238 if maybeArch
in UnsupportedArch
.unsupported
:
239 raise UnsupportedArch
240 self
.ia32
= maybeArch
== 'ia32'
241 self
.x64
= maybeArch
== 'x64'
243 self
.inputFileBase
= os
.path
.basename(self
.inputFilename
)
244 self
.outputFileBase
= os
.path
.basename(self
.outputFilename
)
245 self
.output
= io
.BytesIO()
246 if not self
.args
.quiet
:
247 dirpath
, src
= os
.path
.split(self
.inputFilename
)
248 dirpath
= self
.RootRelative(dirpath
)
249 dst
= os
.path
.basename(self
.outputFilename
)
250 print('Converting:', dirpath
, src
, '->', dst
)
251 lines
= io
.open(self
.inputFilename
).readlines()
253 if self
.outputFilename
== '-' and not self
.diff
:
254 output_data
= self
.output
.getvalue()
255 if sys
.version_info
>= (3, 0):
256 output_data
= output_data
.decode('utf-8', 'ignore')
257 sys
.stdout
.write(output_data
)
260 f
= io
.open(self
.outputFilename
, 'wb')
261 f
.write(self
.output
.getvalue())
265 endOfLineRe
= re
.compile(r
'''
268 re
.VERBOSE | re
.MULTILINE
270 begOfLineRe
= re
.compile(r
'''
276 def Convert(self
, lines
):
278 self
.anonLabelCount
= -1
280 self
.oldAsmEmptyLineCount
= 0
281 self
.newAsmEmptyLineCount
= 0
283 mo
= self
.begOfLineRe
.search(line
)
284 assert mo
is not None
285 self
.indent
= mo
.group()
286 lineWithoutBeginning
= line
[len(self
.indent
):]
287 mo
= self
.endOfLineRe
.search(lineWithoutBeginning
)
291 endOfLine
= mo
.group()
292 oldAsm
= line
[len(self
.indent
):len(line
) - len(endOfLine
)]
293 self
.originalLine
= line
.rstrip()
294 if line
.strip() == '':
295 self
.oldAsmEmptyLineCount
+= 1
296 self
.TranslateAsm(oldAsm
, endOfLine
)
297 if line
.strip() != '':
298 self
.oldAsmEmptyLineCount
= 0
300 procDeclRe
= re
.compile(r
'''
301 (?: ASM_PFX \s* [(] \s* )?
305 (?: \s+ NEAR | FAR )?
307 (?: \s+ (PUBLIC | PRIVATE) )?
308 (?: \s+ USES ( (?: \s+ \w[\w0-9]* )+ ) )?
311 re
.VERBOSE | re
.IGNORECASE
314 procEndRe
= re
.compile(r
'''
319 re
.VERBOSE | re
.IGNORECASE
322 varAndTypeSubRe
= r
' (?: [\w@][\w@0-9]* ) (?: \s* : \s* \w+ )? '
323 publicRe
= re
.compile(r
'''
325 ( %s (?: \s* , \s* %s )* )
327 ''' % (varAndTypeSubRe
, varAndTypeSubRe
),
328 re
.VERBOSE | re
.IGNORECASE
331 varAndTypeSubRe
= re
.compile(varAndTypeSubRe
, re
.VERBOSE | re
.IGNORECASE
)
333 macroDeclRe
= re
.compile(r
'''
338 re
.VERBOSE | re
.IGNORECASE
341 sectionDeclRe
= re
.compile(r
'''
346 re
.VERBOSE | re
.IGNORECASE
349 externRe
= re
.compile(r
'''
350 EXTE?RN \s+ (?: C \s+ )?
351 ([\w@][\w@0-9]*) \s* : \s* (\w+)
354 re
.VERBOSE | re
.IGNORECASE
357 externdefRe
= re
.compile(r
'''
358 EXTERNDEF \s+ (?: C \s+ )?
359 ([\w@][\w@0-9]*) \s* : \s* (\w+)
362 re
.VERBOSE | re
.IGNORECASE
365 protoRe
= re
.compile(r
'''
371 re
.VERBOSE | re
.IGNORECASE
374 defineDataRe
= re
.compile(r
'''
376 ( db | dw | dd | dq ) \s+
380 re
.VERBOSE | re
.IGNORECASE
383 equRe
= re
.compile(r
'''
384 ([\w@][\w@0-9]*) \s+ EQU \s+ (\S.*?)
387 re
.VERBOSE | re
.IGNORECASE
390 ignoreRe
= re
.compile(r
'''
402 re
.VERBOSE | re
.IGNORECASE
405 whitespaceRe
= re
.compile(r
'\s+', re
.MULTILINE
)
407 def TranslateAsm(self
, oldAsm
, endOfLine
):
408 assert(oldAsm
.strip() == oldAsm
)
410 endOfLine
= endOfLine
.replace(self
.inputFileBase
, self
.outputFileBase
)
412 oldOp
= oldAsm
.split()
420 self
.EmitAsmWithComment(oldAsm
, newAsm
, endOfLine
)
421 elif oldOp
in ('#include', ):
423 self
.EmitLine(oldAsm
+ endOfLine
)
424 elif oldOp
.lower() in ('end', 'title', 'text'):
426 self
.EmitAsmWithComment(oldAsm
, newAsm
, endOfLine
)
427 elif oldAsm
.lower() == '@@:':
428 self
.anonLabelCount
+= 1
429 self
.EmitLine(self
.anonLabel(self
.anonLabelCount
) + ':')
430 elif self
.MatchAndSetMo(self
.ignoreRe
, oldAsm
):
432 self
.EmitAsmWithComment(oldAsm
, newAsm
, endOfLine
)
433 elif oldAsm
.lower() == 'ret':
434 for i
in range(len(self
.uses
) - 1, -1, -1):
435 register
= self
.uses
[i
]
436 self
.EmitNewContent('pop ' + register
)
438 self
.EmitAsmWithComment(oldAsm
, newAsm
, endOfLine
)
440 elif oldOp
.lower() == 'lea':
441 newAsm
= self
.ConvertLea(oldAsm
)
442 self
.EmitAsmWithComment(oldAsm
, newAsm
, endOfLine
)
443 elif oldAsm
.lower() == 'end':
445 self
.EmitAsmWithComment(oldAsm
, newAsm
, endOfLine
)
447 elif self
.MatchAndSetMo(self
.equRe
, oldAsm
):
448 equ
= self
.mo
.group(1)
449 newAsm
= '%%define %s %s' % (equ
, self
.mo
.group(2))
450 self
.EmitAsmWithComment(oldAsm
, newAsm
, endOfLine
)
451 elif self
.MatchAndSetMo(self
.externRe
, oldAsm
) or \
452 self
.MatchAndSetMo(self
.protoRe
, oldAsm
):
453 extern
= self
.mo
.group(1)
454 self
.NewGlobal(extern
)
455 newAsm
= 'extern ' + extern
456 self
.EmitAsmWithComment(oldAsm
, newAsm
, endOfLine
)
457 elif self
.MatchAndSetMo(self
.externdefRe
, oldAsm
):
459 self
.EmitAsmWithComment(oldAsm
, newAsm
, endOfLine
)
460 elif self
.MatchAndSetMo(self
.macroDeclRe
, oldAsm
):
461 newAsm
= '%%macro %s 0' % self
.mo
.group(1)
462 self
.EmitAsmWithComment(oldAsm
, newAsm
, endOfLine
)
463 elif oldOp
.lower() == 'endm':
464 newAsm
= r
'%endmacro'
465 self
.EmitAsmWithComment(oldAsm
, newAsm
, endOfLine
)
466 elif self
.MatchAndSetMo(self
.sectionDeclRe
, oldAsm
):
467 name
= self
.mo
.group(1)
468 ty
= self
.mo
.group(2)
469 if ty
.lower() == 'section':
473 self
.EmitAsmWithComment(oldAsm
, newAsm
, endOfLine
)
474 elif self
.MatchAndSetMo(self
.procDeclRe
, oldAsm
):
475 proc
= self
.proc
= self
.mo
.group(1)
476 visibility
= self
.mo
.group(2)
477 if visibility
is None:
480 visibility
= visibility
.lower()
481 if visibility
!= 'private':
482 self
.NewGlobal(self
.proc
)
483 proc
= 'ASM_PFX(' + proc
+ ')'
484 self
.EmitNewContent('global ' + proc
)
486 self
.EmitAsmWithComment(oldAsm
, newAsm
, endOfLine
)
487 uses
= self
.mo
.group(3)
489 uses
= tuple(filter(None, uses
.split()))
493 for register
in self
.uses
:
494 self
.EmitNewContent(' push ' + register
)
495 elif self
.MatchAndSetMo(self
.procEndRe
, oldAsm
):
497 self
.EmitAsmWithComment(oldAsm
, newAsm
, endOfLine
)
498 elif self
.MatchAndSetMo(self
.publicRe
, oldAsm
):
499 publics
= re
.findall(self
.varAndTypeSubRe
, self
.mo
.group(1))
500 publics
= tuple(map(lambda p
: p
.split(':')[0].strip(), publics
))
501 for i
in range(len(publics
) - 1):
503 self
.EmitNewContent('global ASM_PFX(%s)' % publics
[i
])
507 newAsm
= 'global ASM_PFX(%s)' % name
508 self
.EmitAsmWithComment(oldAsm
, newAsm
, endOfLine
)
509 elif self
.MatchAndSetMo(self
.defineDataRe
, oldAsm
):
510 name
= self
.mo
.group(1)
511 ty
= self
.mo
.group(2)
512 value
= self
.mo
.group(3)
515 newAsm
= '%s: %s %s' % (name
, ty
, value
)
516 newAsm
= self
.CommonConversions(newAsm
)
517 self
.EmitAsmWithComment(oldAsm
, newAsm
, endOfLine
)
519 newAsm
= self
.CommonConversions(oldAsm
)
520 self
.EmitAsmWithComment(oldAsm
, newAsm
, endOfLine
)
522 def NewGlobal(self
, name
):
523 regex
= re
.compile(r
'(?<![_\w\d])(?<!ASM_PFX\()(' + re
.escape(name
) +
525 self
.globals.add(regex
)
527 def ConvertAnonymousLabels(self
, oldAsm
):
529 anonLabel
= self
.anonLabel(self
.anonLabelCount
)
530 newAsm
= newAsm
.replace('@b', anonLabel
)
531 newAsm
= newAsm
.replace('@B', anonLabel
)
532 anonLabel
= self
.anonLabel(self
.anonLabelCount
+ 1)
533 newAsm
= newAsm
.replace('@f', anonLabel
)
534 newAsm
= newAsm
.replace('@F', anonLabel
)
537 def anonLabel(self
, count
):
540 def EmitString(self
, string
):
541 self
.output
.write(string
.encode('utf-8', 'ignore'))
543 def EmitLineWithDiff(self
, old
, new
):
544 newLine
= (self
.indent
+ new
).rstrip()
547 print('+%s' % newLine
)
550 print('+%s' % newLine
)
554 self
.newAsmEmptyLineCount
= 0
555 self
.EmitString(newLine
+ '\r\n')
557 def EmitLine(self
, string
):
558 self
.EmitLineWithDiff(self
.originalLine
, string
)
560 def EmitNewContent(self
, string
):
561 self
.EmitLineWithDiff(None, string
)
563 def EmitAsmReplaceOp(self
, oldAsm
, oldOp
, newOp
, endOfLine
):
564 newAsm
= oldAsm
.replace(oldOp
, newOp
, 1)
565 self
.EmitAsmWithComment(oldAsm
, newAsm
, endOfLine
)
567 hexNumRe
= re
.compile(r
'0*((?=[\da-f])\d*(?<=\d)[\da-f]*)h', re
.IGNORECASE
)
569 def EmitAsmWithComment(self
, oldAsm
, newAsm
, endOfLine
):
570 for glblRe
in self
.globals:
571 newAsm
= glblRe
.sub(r
'ASM_PFX(\1)', newAsm
)
573 newAsm
= self
.hexNumRe
.sub(r
'0x\1', newAsm
)
575 newLine
= newAsm
+ endOfLine
576 emitNewLine
= ((newLine
.strip() != '') or
577 ((oldAsm
+ endOfLine
).strip() == ''))
578 if emitNewLine
and newLine
.strip() == '':
579 self
.newAsmEmptyLineCount
+= 1
580 if self
.newAsmEmptyLineCount
> 1:
583 self
.EmitLine(newLine
.rstrip())
585 print('-%s' % self
.originalLine
)
587 leaRe
= re
.compile(r
'''
588 (lea \s+) ([\w@][\w@0-9]*) \s* , \s* (\S (?:.*\S)?)
591 re
.VERBOSE | re
.IGNORECASE
594 def ConvertLea(self
, oldAsm
):
596 if self
.MatchAndSetMo(self
.leaRe
, oldAsm
):
597 lea
= self
.mo
.group(1)
598 dst
= self
.mo
.group(2)
599 src
= self
.mo
.group(3)
600 if src
.find('[') < 0:
601 src
= '[' + src
+ ']'
602 newAsm
= lea
+ dst
+ ', ' + src
603 newAsm
= self
.CommonConversions(newAsm
)
606 ptrRe
= re
.compile(r
'''
608 ([dfq]?word|byte) \s+ (?: ptr ) (\s*)
611 re
.VERBOSE | re
.IGNORECASE
614 def ConvertPtr(self
, oldAsm
):
616 while self
.SearchAndSetMo(self
.ptrRe
, newAsm
):
617 ty
= self
.mo
.group(1)
618 if ty
.lower() == 'fword':
621 ty
+= self
.mo
.group(2)
622 newAsm
= newAsm
[:self
.mo
.start(0)] + ty
+ newAsm
[self
.mo
.end(0):]
625 labelByteRe
= re
.compile(r
'''
626 (?: \s+ label \s+ (?: [dfq]?word | byte ) )
629 re
.VERBOSE | re
.IGNORECASE
632 def ConvertLabelByte(self
, oldAsm
):
634 if self
.SearchAndSetMo(self
.labelByteRe
, newAsm
):
635 newAsm
= newAsm
[:self
.mo
.start(0)] + ':' + newAsm
[self
.mo
.end(0):]
638 unaryBitwiseOpRe
= re
.compile(r
'''
642 re
.VERBOSE | re
.IGNORECASE
644 binaryBitwiseOpRe
= re
.compile(r
'''
646 ( AND | OR | SHL | SHR )
649 re
.VERBOSE | re
.IGNORECASE
651 bitwiseOpReplacements
= {
659 def ConvertBitwiseOp(self
, oldAsm
):
661 while self
.SearchAndSetMo(self
.binaryBitwiseOpRe
, newAsm
):
662 prefix
= self
.mo
.group(1)
663 op
= self
.bitwiseOpReplacements
[self
.mo
.group(2).lower()]
664 newAsm
= newAsm
[:self
.mo
.start(0)] + prefix
+ op
+ \
665 newAsm
[self
.mo
.end(0):]
666 while self
.SearchAndSetMo(self
.unaryBitwiseOpRe
, newAsm
):
667 op
= self
.bitwiseOpReplacements
[self
.mo
.group(1).lower()]
668 newAsm
= newAsm
[:self
.mo
.start(0)] + op
+ newAsm
[self
.mo
.end(0):]
671 sectionRe
= re
.compile(r
'''
678 re
.VERBOSE | re
.IGNORECASE
681 segmentRe
= re
.compile(r
'''
688 re
.VERBOSE | re
.IGNORECASE
691 def ConvertSection(self
, oldAsm
):
693 if self
.MatchAndSetMo(self
.sectionRe
, newAsm
) or \
694 self
.MatchAndSetMo(self
.segmentRe
, newAsm
):
695 name
= self
.mo
.group(1).lower()
698 self
.EmitLine('DEFAULT REL')
700 newAsm
= 'SECTION .' + name
703 fwordRe
= re
.compile(r
'''
708 re
.VERBOSE | re
.IGNORECASE
711 def FwordUnsupportedCheck(self
, oldAsm
):
713 if self
.SearchAndSetMo(self
.fwordRe
, newAsm
):
714 newAsm
= self
.Unsupported(newAsm
, 'fword used')
717 __common_conversion_routines__
= (
718 ConvertAnonymousLabels
,
720 FwordUnsupportedCheck
,
726 def CommonConversions(self
, oldAsm
):
728 for conv
in self
.__common
_conversion
_routines
__:
729 newAsm
= conv(self
, newAsm
)
732 def Unsupported(self
, asm
, message
=None):
734 raise UnsupportedConversion
736 self
.unsupportedSyntaxSeen
= True
737 newAsm
= '%error conversion unsupported'
739 newAsm
+= '; ' + message
744 class ConvertInfFile(CommonUtils
):
746 def __init__(self
, inf
, clone
):
747 CommonUtils
.__init
__(self
, clone
)
749 self
.ScanInfAsmFiles()
751 self
.ConvertInfAsmFiles()
753 infSrcRe
= re
.compile(r
'''
755 ( [\w@][\w@0-9/]* \.(asm|s) )
760 re
.VERBOSE | re
.IGNORECASE
763 def GetInfAsmFileMapping(self
):
764 srcToDst
= {'order': []}
765 for line
in self
.lines
:
767 if self
.MatchAndSetMo(self
.infSrcRe
, line
):
768 src
= self
.mo
.group(1)
769 srcExt
= self
.mo
.group(2)
770 dst
= os
.path
.splitext(src
)[0] + '.nasm'
771 fullDst
= os
.path
.join(self
.dir, dst
)
772 if src
not in srcToDst
and not os
.path
.exists(fullDst
):
774 srcToDst
['order'].append(src
)
777 def ScanInfAsmFiles(self
):
779 assert os
.path
.isfile(src
)
780 f
= io
.open(src
, 'rt')
781 self
.lines
= f
.readlines()
784 path
= os
.path
.realpath(self
.inf
)
785 (self
.dir, inf
) = os
.path
.split(path
)
786 parent
= os
.path
.normpath(self
.dir)
787 (lastpath
, self
.moduleName
) = os
.path
.split(parent
)
788 self
.packageName
= None
790 lastpath
= os
.path
.normpath(lastpath
)
791 (parent
, basename
) = os
.path
.split(lastpath
)
792 if parent
== lastpath
:
794 if basename
.endswith('Pkg'):
795 self
.packageName
= basename
799 self
.srcToDst
= self
.GetInfAsmFileMapping()
801 self
.dstToSrc
= {'order': []}
802 for src
in self
.srcToDst
['order']:
803 srcExt
= os
.path
.splitext(src
)[1]
804 dst
= self
.srcToDst
[src
]
805 if dst
not in self
.dstToSrc
:
806 self
.dstToSrc
[dst
] = [src
]
807 self
.dstToSrc
['order'].append(dst
)
809 self
.dstToSrc
[dst
].append(src
)
812 return len(self
.dstToSrc
['order'])
815 return iter(self
.dstToSrc
['order'])
817 def ConvertInfAsmFiles(self
):
819 unsupportedArchCount
= 0
823 self
.UpdateInfAsmFile(dst
)
825 except UnsupportedConversion
:
826 if not self
.args
.quiet
:
827 print('MASM=>NASM conversion unsupported for', dst
)
828 notConverted
.append(dst
)
830 if not self
.args
.quiet
:
831 print('Source file missing for', reldst
)
832 notConverted
.append(dst
)
833 except UnsupportedArch
:
834 unsupportedArchCount
+= 1
837 self
.ConversionFinished(dst
)
838 if len(notConverted
) > 0 and not self
.args
.quiet
:
839 for dst
in notConverted
:
840 reldst
= self
.RootRelative(dst
)
841 print('Unabled to convert', reldst
)
842 if unsupportedArchCount
> 0 and not self
.args
.quiet
:
843 print('Skipped', unsupportedArchCount
, 'files based on architecture')
845 def UpdateInfAsmFile(self
, dst
, IgnoreMissingAsm
=False):
846 infPath
= os
.path
.split(os
.path
.realpath(self
.inf
))[0]
847 asmSrc
= os
.path
.splitext(dst
)[0] + '.asm'
848 fullSrc
= os
.path
.join(infPath
, asmSrc
)
849 fullDst
= os
.path
.join(infPath
, dst
)
850 srcParentDir
= os
.path
.basename(os
.path
.split(fullSrc
)[0])
851 if srcParentDir
.lower() in UnsupportedArch
.unsupported
:
852 raise UnsupportedArch
853 elif not os
.path
.exists(fullSrc
):
854 if not IgnoreMissingAsm
:
856 else: # not os.path.exists(fullDst):
857 conv
= ConvertAsmFile(fullSrc
, fullDst
, self
)
858 self
.unsupportedSyntaxSeen
= conv
.unsupportedSyntaxSeen
861 recentSources
= list()
863 while i
< len(self
.lines
):
864 line
= self
.lines
[i
].rstrip()
867 preserveOldSource
= False
868 for src
in self
.dstToSrc
[dst
]:
869 assert self
.srcToDst
[src
] == dst
870 updatedLine
= self
.ReplacePreserveSpacing(
871 updatedLine
, src
, dst
)
872 lineChanged
= updatedLine
!= line
874 preserveOldSource
= self
.ShouldKeepFile(src
)
878 if preserveOldSource
:
879 if updatedLine
.strip() not in recentSources
:
880 self
.lines
.insert(i
, updatedLine
+ '\n')
881 recentSources
.append(updatedLine
.strip())
884 print('+%s' % updatedLine
)
890 if updatedLine
.strip() in recentSources
:
893 self
.lines
[i
] = updatedLine
+ '\n'
894 recentSources
.append(updatedLine
.strip())
896 print('+%s' % updatedLine
)
898 if len(recentSources
) > 0:
899 recentSources
= list()
903 fileChanged |
= lineChanged
907 self
.lines
= list(filter(lambda l
: l
is not None, self
.lines
))
909 for src
in self
.dstToSrc
[dst
]:
910 if not src
.endswith('.asm'):
911 fullSrc
= os
.path
.join(infPath
, src
)
912 if os
.path
.exists(fullSrc
):
913 self
.RemoveFile(fullSrc
)
916 f
= io
.open(self
.inf
, 'w', newline
='\r\n')
917 f
.writelines(self
.lines
)
919 self
.FileUpdated(self
.inf
)
921 def ConversionFinished(self
, dst
):
922 asmSrc
= os
.path
.splitext(dst
)[0] + '.asm'
923 self
.FileConversionFinished(
924 self
.packageName
, self
.moduleName
, asmSrc
, dst
)
927 class ConvertInfFiles(CommonUtils
):
929 def __init__(self
, infs
, clone
):
930 CommonUtils
.__init
__(self
, clone
)
931 infs
= map(lambda i
: ConvertInfFile(i
, self
), infs
)
932 infs
= filter(lambda i
: len(i
) > 0, infs
)
933 dstToInfs
= {'order': []}
936 fulldst
= os
.path
.realpath(os
.path
.join(inf
.dir, dst
))
938 if fulldst
in dstToInfs
:
939 dstToInfs
[fulldst
].append(pair
)
941 dstToInfs
['order'].append(fulldst
)
942 dstToInfs
[fulldst
] = [pair
]
945 unsupportedArchCount
= 0
946 for dst
in dstToInfs
['order']:
949 for inf
, reldst
in dstToInfs
[dst
]:
950 inf
.UpdateInfAsmFile(reldst
, IgnoreMissingAsm
=didSomething
)
952 except UnsupportedConversion
:
953 if not self
.args
.quiet
:
954 print('MASM=>NASM conversion unsupported for', reldst
)
955 notConverted
.append(dst
)
957 if not self
.args
.quiet
:
958 print('Source file missing for', reldst
)
959 notConverted
.append(dst
)
960 except UnsupportedArch
:
961 unsupportedArchCount
+= 1
964 inf
.ConversionFinished(reldst
)
965 if len(notConverted
) > 0 and not self
.args
.quiet
:
966 for dst
in notConverted
:
967 reldst
= self
.RootRelative(dst
)
968 print('Unabled to convert', reldst
)
969 if unsupportedArchCount
> 0 and not self
.args
.quiet
:
970 print('Skipped', unsupportedArchCount
, 'files based on architecture')
973 class ConvertDirectories(CommonUtils
):
975 def __init__(self
, paths
, clone
):
976 CommonUtils
.__init
__(self
, clone
)
978 self
.ConvertInfAndAsmFiles()
980 def ConvertInfAndAsmFiles(self
):
982 for path
in self
.paths
:
983 assert(os
.path
.exists(path
))
984 for path
in self
.paths
:
985 for root
, dirs
, files
in os
.walk(path
):
986 for d
in ('.svn', '.git'):
990 if f
.lower().endswith('.inf'):
991 inf
= os
.path
.realpath(os
.path
.join(root
, f
))
994 ConvertInfFiles(infs
, self
)
997 class ConvertAsmApp(CommonUtils
):
1000 CommonUtils
.__init
__(self
)
1002 src
= self
.args
.source
1003 dst
= self
.args
.dest
1005 ConvertInfFiles((src
,), self
)
1007 ConvertDirectories((src
,), self
)
1008 elif not self
.dirmode
:
1009 ConvertAsmFile(src
, dst
, self
)