Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 16 additions & 6 deletions pcpp/pcmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,15 @@

__all__ = []

class MacroAction(argparse.Action):
def __call__(self, parser, namespace, values, option_string=None):
items = copy.copy(getattr(namespace, self.dest) or [])
items.append(values)
setattr(namespace, self.dest, items)
actions = copy.copy(getattr(namespace, 'macro_actions', []))
actions.append((option_string, values[0]))
namespace.macro_actions = actions

class FileAction(argparse.Action):
def __init__(self, option_strings, dest, **kwargs):
super(FileAction, self).__init__(option_strings, dest, **kwargs)
Expand All @@ -38,8 +47,8 @@ def __init__(self, argv):
ignores any arguments it does not understand.''')
argp.add_argument('inputs', metavar = 'input', default = [sys.stdin], nargs = '*', action = FileAction, help = 'Files to preprocess (use \'-\' for stdin)')
argp.add_argument('-o', dest = 'output', metavar = 'path', type = argparse.FileType('wt'), default=sys.stdout, nargs = '?', help = 'Output to a file instead of stdout')
argp.add_argument('-D', dest = 'defines', metavar = 'macro[=val]', nargs = 1, action = 'append', help = 'Predefine name as a macro [with value]')
argp.add_argument('-U', dest = 'undefines', metavar = 'macro', nargs = 1, action = 'append', help = 'Pre-undefine name as a macro')
argp.add_argument('-D', dest = 'defines', metavar = 'macro[=val]', nargs = 1, action = MacroAction, help = 'Predefine name as a macro [with value]')
argp.add_argument('-U', dest = 'undefines', metavar = 'macro', nargs = 1, action = MacroAction, help = 'Pre-undefine name as a macro')
argp.add_argument('-N', dest = 'nevers', metavar = 'macro', nargs = 1, action = 'append', help = 'Never define name as a macro, even if defined during the preprocessing.')
argp.add_argument('-I', dest = 'includes', metavar = 'path', nargs = 1, action = 'append', help = "Path to search for unfound #include's")
#argp.add_argument('--passthru', dest = 'passthru', action = 'store_true', help = 'Pass through everything unexecuted except for #include and include guards (which need to be the first thing in an include file')
Expand Down Expand Up @@ -117,14 +126,15 @@ def __init__(self, argv):

if self.args.defines:
self.args.defines = [x[0] for x in self.args.defines]
for d in self.args.defines:
if self.args.undefines:
self.args.undefines = [x[0] for x in self.args.undefines]
for option, d in getattr(self.args, 'macro_actions', []):
if option == '-D':
if '=' not in d:
d += '=1'
d = d.replace('=', ' ', 1)
self.define(d)
if self.args.undefines:
self.args.undefines = [x[0] for x in self.args.undefines]
for d in self.args.undefines:
else:
self.undef(d)
if self.args.nevers:
self.args.nevers = [x[0] for x in self.args.nevers]
Expand Down
68 changes: 68 additions & 0 deletions tests/issue0105.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import unittest
from io import StringIO
from unittest.mock import patch

from pcpp import CmdPreprocessor


class macro_option_order(unittest.TestCase):
def preprocess(self, options, source='FOO\n'):
output = StringIO()
source_file = StringIO(source)
source_file.name = '<stdin>'
with patch('sys.stdin', source_file), patch('sys.stdout', output):
preprocessor = CmdPreprocessor(
['pcpp', '--line-directive', 'none'] + options + ['-'])
self.assertEqual(preprocessor.return_code, 0)
return output.getvalue(), preprocessor.args

def test_undefine_then_define(self):
output, _ = self.preprocess(['-U', 'FOO', '-D', 'FOO=2'])
self.assertEqual(output, '2\n')

def test_redefine_after_undefine(self):
output, args = self.preprocess(
['-D', 'FOO=1', '-U', 'FOO', '-D', 'FOO=2'])
self.assertEqual(output, '2\n')
self.assertEqual(args.defines, ['FOO=1', 'FOO=2'])
self.assertEqual(args.undefines, ['FOO'])

def test_attached_arguments(self):
output, _ = self.preprocess(['-DFOO=1', '-UFOO', '-DFOO=2'])
self.assertEqual(output, '2\n')

def test_default_definition_after_undefine(self):
output, _ = self.preprocess(['-UFOO', '-DFOO'])
self.assertEqual(output, '1\n')

def test_final_undefine(self):
output, _ = self.preprocess(
['-DFOO=1', '-UFOO', '-DFOO=2', '-UFOO'])
self.assertEqual(output, 'FOO\n')

def test_multiple_macros(self):
output, _ = self.preprocess(
['-DFOO=1', '-UBAR', '-DBAR=3', '-UFOO'], 'FOO BAR\n')
self.assertEqual(output, 'FOO 3\n')

def test_only_definitions(self):
output, args = self.preprocess(['-DFOO=1', '-DFOO=2'])
self.assertEqual(output, '2\n')
self.assertIsNone(args.undefines)

def test_only_undefinitions(self):
output, args = self.preprocess(['-UFOO'])
self.assertEqual(output, 'FOO\n')
self.assertIsNone(args.defines)

def test_no_macro_options(self):
output, _ = self.preprocess(['--compress'])
self.assertEqual(output, 'FOO\n')

def test_equals_in_value(self):
output, _ = self.preprocess(['-DFOO=1==1'])
self.assertEqual(output, '1==1\n')


if __name__ == '__main__':
unittest.main()