77from testutils import (
88 simplecpp ,
99 format_include_path_arg ,
10+ format_isystem_path_arg ,
1011 format_framework_path_arg ,
1112 format_iframework_path_arg ,
1213 format_include ,
@@ -346,15 +347,16 @@ def test_framework_lookup(record_property, tmpdir, is_sys, is_iframework, is_pri
346347 "order,expected" ,
347348 [
348349 # Note:
349- # - `I1` / `F1` / `IFW1` point to distinct directories and contain `Component_1.h` (a decoy).
350- # - `I` / `F` / `IFW` point to directories that contain `Component.h`, which the
350+ # - `I1` / `ISYS1` / ` F1` / `IFW1` point to distinct directories and contain `Component_1.h` (a decoy).
351+ # - `I` / `ISYS` / ` F` / `IFW` point to directories that contain `Component.h`, which the
351352 # translation unit (TU) includes via `#include "MyKit/Component.h"`.
352353 #
353- # This makes the winning flag (-I, -F, or -iframework) uniquely identifiable
354+ # This makes the winning flag (-I, -isystem, - F, or -iframework) uniquely identifiable
354355 # in the preprocessor `#line` output.
355356
356357 # Sanity checks
357358 (("I" ,), "I" ),
359+ (("ISYS" ,), "ISYS" ),
358360 (("F" ,), "F" ),
359361 (("IFW" ,), "IFW" ),
360362
@@ -365,6 +367,13 @@ def test_framework_lookup(record_property, tmpdir, is_sys, is_iframework, is_pri
365367 (("I1" , "I" , "I1" ), "I" ),
366368 (("I" , "I1" , "I" ), "I" ),
367369
370+ # System includes (-isystem)
371+ (("ISYS1" , "ISYS" ), "ISYS" ),
372+ (("ISYS" , "ISYS1" ), "ISYS" ),
373+ # System includes (-isystem) duplicates
374+ (("ISYS1" , "ISYS" , "ISYS1" ), "ISYS" ),
375+ (("ISYS" , "ISYS1" , "ISYS" ), "ISYS" ),
376+
368377 # Framework (-F)
369378 (("F1" , "F" ), "F" ),
370379 (("F" , "F1" ), "F" ),
@@ -385,6 +394,16 @@ def test_framework_lookup(record_property, tmpdir, is_sys, is_iframework, is_pri
385394 (("F" , "I" ), "F" ),
386395 (("F1" , "F" , "I" ), "F" ),
387396
397+ # -I and -F takes precedence over -isystem
398+ (("I" , "ISYS" ), "I" ),
399+ (("F" , "ISYS" ), "F" ),
400+ (("ISYS" , "F" ), "F" ),
401+ (("ISYS" , "I" , "F" ), "I" ),
402+ (("ISYS" , "I1" , "F1" , "I" , "F" ), "I" ),
403+ (("ISYS" , "I" ), "I" ),
404+ (("ISYS" , "F" , "I" ), "F" ),
405+ (("ISYS" , "F1" , "I1" , "F" , "I" ), "F" ),
406+
388407 # -I and -F beat system framework (-iframework)
389408 (("I" , "IFW" ), "I" ),
390409 (("F" , "IFW" ), "F" ),
@@ -394,16 +413,24 @@ def test_framework_lookup(record_property, tmpdir, is_sys, is_iframework, is_pri
394413 (("IFW" , "I" ), "I" ),
395414 (("IFW" , "F" , "I" ), "F" ),
396415 (("IFW" , "F1" , "I1" , "F" , "I" ), "F" ),
416+
417+ # system include (-isystem) beats system framework (-iframework)
418+ (("ISYS" , "IFW" ), "ISYS" ),
419+ (("IFW" , "ISYS" ), "ISYS" ),
420+ (("IFW1" , "ISYS1" , "IFW" , "ISYS" ), "ISYS" ),
421+ (("I1" , "F1" , "IFW1" , "ISYS1" , "IFW" , "ISYS" ), "ISYS" ),
397422 ],
398423)
399424def test_searchpath_order (record_property , tmpdir , is_sys , order , expected ):
400425 """
401- Validate include resolution order across -I (user include), -F (user framework),
402- and -iframework (system framework) using a minimal file layout, asserting which
403- physical header path appears in the preprocessor #line output.
426+ Validate include resolution order across -I (user include),
427+ -isystem (system include), -F (user framework), and
428+ -iframework (system framework) using a minimal file layout,
429+ asserting which physical header path appears in the preprocessor #line output.
404430
405- The test constructs three parallel trees (two entries per kind):
431+ The test constructs four parallel trees (two entries per kind):
406432 - inc{,_1}/MyKit/Component{,_1}.h # for -I
433+ - isys{,_1}/MyKit/Component{,_1}.h # for -isystem
407434 - Fw{,_1}/MyKit.framework/Headers/Component{,_1}.h # for -F
408435 - SysFw{,_1}/MyKit.framework/Headers/Component{,_1}.h # for -iframework
409436
@@ -418,7 +445,7 @@ def test_searchpath_order(record_property, tmpdir, is_sys, order, expected):
418445 """
419446
420447 # Create two include dirs, two user framework dirs, and two system framework dirs
421- inc_dirs , fw_dirs , sysfw_dirs = [], [], []
448+ inc_dirs , isys_dirs , fw_dirs , sysfw_dirs = [], [], [], []
422449
423450 def _suffix (idx : int ) -> str :
424451 return f"_{ idx } " if idx > 0 else ""
@@ -429,6 +456,11 @@ def _suffix(idx: int) -> str:
429456 __test_create_header (inc_dir , hdr_relpath = f"MyKit/Component{ _suffix (idx )} .h" )
430457 inc_dirs .append (inc_dir )
431458
459+ # -isystem paths (system includes)
460+ isys_dir = os .path .join (tmpdir , f"isys{ _suffix (idx )} " )
461+ __test_create_header (isys_dir , hdr_relpath = f"MyKit/Component{ _suffix (idx )} .h" )
462+ isys_dirs .append (isys_dir )
463+
432464 # -F paths (user frameworks)
433465 fw_dir = os .path .join (tmpdir , f"Fw{ _suffix (idx )} " )
434466 __test_create_framework (fw_dir , "MyKit" , f"Component{ _suffix (idx )} .h" )
@@ -443,15 +475,17 @@ def _suffix(idx: int) -> str:
443475 test_file = __test_create_source (tmpdir , "MyKit/Component.h" , is_include_sys = is_sys )
444476
445477 def idx_from_flag (prefix : str , flag : str ) -> int :
446- """Extract numeric suffix from tokens like 'I1', 'F1', 'IFW1'.
447- Returns 0 when no suffix is present (e.g., 'I', 'F', 'IFW')."""
478+ """Extract numeric suffix from tokens like 'I1', 'ISYS1', ' F1', 'IFW1'.
479+ Returns 0 when no suffix is present (e.g., 'I', 'ISYS', ' F', 'IFW')."""
448480 return int (flag [len (prefix ):]) if len (flag ) > len (prefix ) else 0
449481
450482 # Build argv in the exact order requested by `order`
451483 args = []
452484 for flag in order :
453485 if flag in ["I" , "I1" ]:
454486 args .append (format_include_path_arg (inc_dirs [idx_from_flag ("I" , flag )]))
487+ elif flag in ["ISYS" , "ISYS1" ]:
488+ args .append (format_isystem_path_arg (isys_dirs [idx_from_flag ("ISYS" , flag )]))
455489 elif flag in ["F" , "F1" ]:
456490 args .append (format_framework_path_arg (fw_dirs [idx_from_flag ("F" , flag )]))
457491 elif flag in ["IFW" , "IFW1" ]:
@@ -469,14 +503,17 @@ def idx_from_flag(prefix: str, flag: str) -> int:
469503 root = pathlib .PurePath (tmpdir ).as_posix ()
470504
471505 inc_paths = [f"{ root } /inc{ _suffix (idx )} /MyKit/Component{ _suffix (idx )} .h" for idx in range (2 )]
506+ isys_paths = [f"{ root } /isys{ _suffix (idx )} /MyKit/Component{ _suffix (idx )} .h" for idx in range (2 )]
472507 fw_paths = [f"{ root } /Fw{ _suffix (idx )} /MyKit.framework/Headers/Component{ _suffix (idx )} .h" for idx in range (2 )]
473508 ifw_paths = [f"{ root } /SysFw{ _suffix (idx )} /MyKit.framework/Headers/Component{ _suffix (idx )} .h" for idx in range (2 )]
474- all_candidate_paths = [* inc_paths , * fw_paths , * ifw_paths ]
509+ all_candidate_paths = [* inc_paths , * isys_paths , * fw_paths , * ifw_paths ]
475510
476511 # Compute the single path we expect to appear
477512 expected_path = None
478513 if expected in ["I" , "I1" ]:
479514 expected_path = inc_paths [idx_from_flag ("I" , expected )]
515+ elif expected in ["ISYS" , "ISYS1" ]:
516+ expected_path = isys_paths [idx_from_flag ("ISYS" , expected )]
480517 elif expected in ["F" , "F1" ]:
481518 expected_path = fw_paths [idx_from_flag ("F" , expected )]
482519 elif expected in ["IFW" , "IFW1" ]:
0 commit comments