Skip to content

Commit 4a0b7c6

Browse files
fix: try different approach
1 parent 5383f1f commit 4a0b7c6

File tree

1 file changed

+49
-56
lines changed

1 file changed

+49
-56
lines changed

sciencemode/_cffi.py

Lines changed: 49 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,12 @@ def process_typedecl(self, node):
291291
if node.coord is None or coord.find(include_dir) != -1:
292292
typedecl = f"{self.generator.visit(node)};"
293293
typedecl = ARRAY_SIZEOF_PATTERN.sub("[...]", typedecl)
294+
295+
# Fix _Bool array compatibility issues
296+
# Replace _Bool arrays with unsigned char arrays for CFFI compatibility
297+
typedecl = re.sub(r"\b_Bool\s*\[([^\]]*)\]", r"unsigned char[\1]", typedecl)
298+
typedecl = re.sub(r"\bbool\s*\[([^\]]*)\]", r"unsigned char[\1]", typedecl)
299+
294300
if typedecl not in self.typedecls:
295301
self.typedecls.append(typedecl)
296302

@@ -572,6 +578,7 @@ def preprocess_header_manually(header_path):
572578
"""Manually preprocess a header file by expanding simple #define statements.
573579
574580
This is a fallback for when no C preprocessor is available.
581+
This version is much more conservative to preserve header structure.
575582
"""
576583
with open(header_path, "r", encoding="utf-8", errors="ignore") as f:
577584
content = f.read()
@@ -585,54 +592,30 @@ def preprocess_header_manually(header_path):
585592
# Remove // style comments
586593
content = re.sub(r"//.*", "", content)
587594

588-
# Simple define replacements for common patterns
589-
defines = {
590-
"SMPT_EXPORTS": "",
591-
"SMPT_DLL": "",
592-
"UC_MAIN": "",
593-
"__cplusplus": "",
594-
"_Bool": "unsigned char", # Use unsigned char for better compatibility
595-
"bool": "unsigned char",
596-
"true": "1",
597-
"false": "0",
598-
}
595+
# Very conservative preprocessing - only fix things that definitely break pycparser
596+
# Don't mess with complex #define statements or conditional compilation
599597

600-
# Add platform-specific defines
601-
if platform.system() == "Windows":
602-
defines["_WIN32"] = "1"
603-
defines["_MSC_VER"] = "1900"
604-
defines["_WINDOWS"] = "1"
605-
elif platform.system() == "Linux":
606-
defines["__linux__"] = "1"
607-
defines["__unix__"] = "1"
608-
else: # Darwin/macOS
609-
defines["__APPLE__"] = "1"
610-
defines["__MACH__"] = "1"
611-
612-
# Replace simple #define statements and clean up
613-
for define, value in defines.items():
614-
# Add proper #define if not already present
615-
if f"#define {define}" not in content:
616-
content = f"#define {define} {value}\n" + content
617-
# Also handle direct usage
618-
content = content.replace(f"#{define}", f"#define {define} {value}")
619-
620-
# Add essential type definitions that might be missing
621-
essential_types = """
622-
#ifndef __STDC_VERSION__
623-
#define __STDC_VERSION__ 199901L
624-
#endif
598+
# Handle _Bool arrays specifically for compatibility
599+
# Replace _Bool arrays with unsigned char arrays to fix CFFI type issues
600+
content = re.sub(r"\b_Bool\s*\[([^\]]*)\]", r"unsigned char[\1]", content)
601+
content = re.sub(r"\bbool\s*\[([^\]]*)\]", r"unsigned char[\1]", content)
625602

603+
# Only add the most essential type definitions that pycparser needs
604+
# Don't redefine __STDC_VERSION__ - that causes the macOS redefinition warning
605+
essential_types = """
606+
/* Minimal essential types for pycparser - don't redefine __STDC_VERSION__ */
626607
#ifndef __bool_true_false_are_defined
627608
#define __bool_true_false_are_defined 1
628609
typedef unsigned char _Bool;
629610
#endif
630611
631612
#ifndef __cplusplus
613+
#ifndef bool
632614
#define bool _Bool
633615
#define true 1
634616
#define false 0
635617
#endif
618+
#endif
636619
637620
/* Define common GCC attributes away */
638621
#define __attribute__(x)
@@ -642,9 +625,6 @@ def preprocess_header_manually(header_path):
642625
#define __always_inline
643626
#define __builtin_va_list char*
644627
#define __asm__(x)
645-
#define __signed__ signed
646-
#define __const const
647-
#define __volatile__ volatile
648628
649629
"""
650630
content = essential_types + content
@@ -695,9 +675,10 @@ def try_parse_with_better_args(header_path, header_name):
695675
"-DSMPT_EXPORTS=",
696676
"-DSMPT_DLL=",
697677
"-DUC_MAIN=",
698-
# Add stdbool.h compatibility
678+
# Add stdbool.h compatibility - but don't override built-in __STDC_VERSION__
699679
"-D__bool_true_false_are_defined=1",
700-
"-D__STDC_VERSION__=199901L",
680+
# Don't redefine __STDC_VERSION__ - let the compiler use its built-in value
681+
# This prevents the "macro redefined" warning on macOS
701682
],
702683
},
703684
# Fallback 1: cpp with minimal args only
@@ -714,6 +695,7 @@ def try_parse_with_better_args(header_path, header_name):
714695
"-DSMPT_API=", # Define away SMPT_API
715696
"-DSMPT_EXPORTS=",
716697
"-DSMPT_DLL=",
698+
# Don't add any manual constants here - let the headers define them naturally
717699
],
718700
},
719701
# Fallback 2: no cpp at all
@@ -840,29 +822,35 @@ def try_parse_with_better_args(header_path, header_name):
840822
)
841823

842824
defines = set()
843-
for header_path in HEADERS:
844-
with open(os.sep.join([include_dir, header_path])) as header_file:
845-
header = header_file.read()
846-
for match in DEFINE_PATTERN.finditer(header):
847-
if (
848-
match.group(1) in DEFINE_BLACKLIST
849-
or match.group(1) in collector.typedecls
850-
or match.group(1) in collector.functions
851-
):
852-
continue
853-
try:
854-
int(match.group(2), 0)
855-
defines.add(f"#define {match.group(1)} {match.group(2)}")
856-
except Exception:
857-
defines.add(f"#define {match.group(1)} ...")
858825

826+
# Don't manually add constants - let them be parsed from headers naturally
827+
# This prevents issues with conditional compilation and macro redefinition
828+
829+
for header_path in HEADERS:
830+
header_full_path = os.sep.join([include_dir, header_path])
831+
if os.path.exists(header_full_path):
832+
with open(header_full_path, encoding="utf-8", errors="ignore") as header_file:
833+
header = header_file.read()
834+
for match in DEFINE_PATTERN.finditer(header):
835+
if (
836+
match.group(1) in DEFINE_BLACKLIST
837+
or match.group(1) in collector.typedecls
838+
or match.group(1) in collector.functions
839+
):
840+
continue
841+
try:
842+
int(match.group(2), 0)
843+
defines.add(f"#define {match.group(1)} {match.group(2)}")
844+
except Exception:
845+
defines.add(f"#define {match.group(1)} ...")
859846
print(
860847
f"Processing {len(defines)} defines, {len(collector.typedecls)} types, "
861848
f"{len(collector.functions)} functions"
862849
)
863850

864851
cdef = "\n".join(itertools.chain(*[defines, collector.typedecls, collector.functions]))
865852

853+
# Post-process the cdef to fix any remaining compatibility issues
866854
cdef = cdef.replace("[Smpt_Length_Max_Packet_Size]", "[1200]")
867855
cdef = cdef.replace("[Smpt_Length_Packet_Input_Buffer_Rows]", "[100]")
868856
cdef = cdef.replace(
@@ -874,6 +862,11 @@ def try_parse_with_better_args(header_path, header_name):
874862
cdef = cdef.replace("[Smpt_Length_Points]", "[16]")
875863
cdef = cdef.replace("[Smpt_Length_Number_Of_Channels]", "[8]")
876864

865+
# Final cleanup: ensure all _Bool arrays are converted to unsigned char arrays
866+
# This handles any cases that might have been missed during parsing
867+
cdef = re.sub(r"\b_Bool\s*\[([^\]]*)\]", r"unsigned char[\1]", cdef)
868+
cdef = re.sub(r"\bbool\s*\[([^\]]*)\]", r"unsigned char[\1]", cdef)
869+
877870
# Fix platform-specific field errors
878871
if "serial_port_handle_" in cdef and not sys.platform.startswith("win"):
879872
# Remove the Windows-specific field for non-Windows platforms

0 commit comments

Comments
 (0)