Skip to content

Commit

Permalink
Merge pull request #4629 from jcbrill/jbrill-gh4623-int
Browse files Browse the repository at this point in the history
Fix Issue with SConsCPPConditionalScanner misinterpreting ifdefs suffixed with L or UL [Alternate Implementation]
  • Loading branch information
bdbaddog authored Nov 11, 2024
2 parents 1215e13 + e96cf68 commit b4d72c7
Show file tree
Hide file tree
Showing 4 changed files with 370 additions and 17 deletions.
16 changes: 16 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,22 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
and nodes from the MSVSProject return values. Resolves #4613.
- MSVS: Remove the platform specification (i.e., platform = 'win32') from select
test script environments. The platform specification appears superfluous.
- SCons C preprocessor changes:
- Preserve literals that contain valid integer substring specifications.
Previously, the integer suffix could be stripped from a symbol that contained
an integer and suffix substring.
- Update the optional integer suffixes to include the z|Z and wb|WB suffixes.
- Update the optional integer suffixes to include support for alternate
orderings of unsigned with long or long long as defined in the c/cpp
grammar.
- Update the optional integer suffixes for case insensitive specifications as
defined in the c/cpp grammar.
- Add support for binary integer constants.
- Add support for octal integer constants. Previously, octal integers were
evaluated as decimal integers. A literal zero (0) is treated as an octal
number.
- Change the attempted conversion of a define expansion from using int() to
a constant expression evaluation.

From Alex James:
- On Darwin, PermissionErrors are now handled while trying to access
Expand Down
22 changes: 22 additions & 0 deletions RELEASE.txt
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,23 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY
in the projects argument list rather than manually removing solution file
names and nodes from the MSVSProject return values.

- SCons C preprocessor:
- Update the optional integer suffixes to include the z|Z and wb|WB
suffixes.
- Add support for binary integer constants.
- Add support for octal integer constants. Previously, octal integers
were evaluated as decimal integers. A literal zero (0) is treated as an
octal number.
- Change the method for attempted conversion of a define expansion value
to an integer from a literal to a constant expression evaluation.

- Add a tag to each CacheDir to let systems ignore backing it up
(per https://bford.info/cachedir/). Update the way a CacheDir
is created, since it now has to create two files.

FIXES
-----

- PackageVariable now does what the documentation always said it does
if the variable is used on the command line with one of the enabling
string as the value: the variable's default value is produced (previously
Expand Down Expand Up @@ -104,6 +115,17 @@ FIXES
solution file. User-specified project GUIDs should now be correctly
written to the solution file.

- SCons C preprocessor: Preserve literals that contain valid integer
substring specifications. Previously, the integer suffix could be
stripped from a symbol that contained an integer and suffix substring.

- SCons C preprocessor: Update the optional integer suffixes to include
support for the alternate orderings of unsigned with long or long long as
defined in the c/cpp grammar.

- SCons C preprocessor: Update the optional integer suffixes for case
insensitive specifications as defined in the c/cpp grammar.

- Fix nasm test for missing include file, cleanup.

- Skip running a few validation tests if the user is root and the test is
Expand Down
52 changes: 45 additions & 7 deletions SCons/cpp.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,13 +146,27 @@ def Cleanup_CPP_Expressions(ts):
# ...and compile the expression.
CPP_to_Python_Ops_Expression = re.compile(expr)

# integer specifications
int_suffix_opt = r'(?:[uU](?:l{1,2}|L{1,2}|[zZ]|wb|WB)?|(?:l{1,2}|L{1,2}|[zZ]|wb|WB)[uU]?)?'

hex_integer = fr'(0[xX][0-9A-Fa-f]+){int_suffix_opt}'
bin_integer = fr'(0[bB][01]+){int_suffix_opt}'
oct_integer = fr'(0[0-7]*){int_suffix_opt}'
dec_integer = fr'([1-9][0-9]*){int_suffix_opt}'

int_boundary = r'[a-zA-Z0-9_]'
int_neg_lookbehind = fr'(?<!{int_boundary})'
int_neg_lookahead = fr'(?!{int_boundary})'

# A separate list of expressions to be evaluated and substituted
# sequentially, not all at once.
CPP_to_Python_Eval_List = [
[r'defined\s+(\w+)', '"\\1" in __dict__'],
[r'defined\s*\((\w+)\)', '"\\1" in __dict__'],
[r'(0x[0-9A-Fa-f]+)(?:L|UL)?', '\\1'],
[r'(\d+)(?:L|UL)?', '\\1'],
[r'defined\s+(\w+)', '"\\1" in __dict__'],
[r'defined\s*\((\w+)\)', '"\\1" in __dict__'],
[fr'{int_neg_lookbehind}{hex_integer}{int_neg_lookahead}', '\\1'],
[fr'{int_neg_lookbehind}{bin_integer}{int_neg_lookahead}', '\\1'],
[fr'{int_neg_lookbehind}{oct_integer}{int_neg_lookahead}', '0o\\1'],
[fr'{int_neg_lookbehind}{dec_integer}{int_neg_lookahead}', '\\1'],
]

# Replace the string representations of the regular expressions in the
Expand Down Expand Up @@ -250,6 +264,9 @@ def __init__(self, current=os.curdir, cpppath=(), dict={}, all: int=0, depth=-1)
self.cpp_namespace = dict.copy()
self.cpp_namespace['__dict__'] = self.cpp_namespace

# Namespace for constant expression evaluation (literals only)
self.constant_expression_namespace = {}

# Return all includes without resolving
if all:
self.do_include = self.all_include
Expand Down Expand Up @@ -369,6 +386,25 @@ def do_nothing(self, t) -> None:
def scons_current_file(self, t) -> None:
self.current_file = t[1]

def eval_constant_expression(self, s):
"""
Evaluates a C preprocessor expression.
This is done by converting it to a Python equivalent and
eval()ing it in the C preprocessor namespace we use to
track #define values.
Returns None if the eval() result is not an integer.
"""
s = CPP_to_Python(s)
try:
rval = eval(s, self.constant_expression_namespace)
except (NameError, TypeError, SyntaxError) as e:
rval = None
if not isinstance(rval, int):
rval = None
return rval

def eval_expression(self, t):
"""
Evaluates a C preprocessor expression.
Expand Down Expand Up @@ -507,9 +543,11 @@ def do_define(self, t) -> None:
Default handling of a #define line.
"""
_, name, args, expansion = t
try:
expansion = int(expansion)
except (TypeError, ValueError):

rval = self.eval_constant_expression(expansion)
if rval is not None:
expansion = rval
else:
# handle "defined" chain "! (defined (A) || defined (B)" ...
if "defined " in expansion:
self.cpp_namespace[name] = self.eval_expression(t[2:])
Expand Down
Loading

0 comments on commit b4d72c7

Please sign in to comment.