Skip to content

Commit 267c55c

Browse files
committed
feat: Add support for -isystem
This change introduces support for the -isystem flag, allowing users to specify system include directories. The addition includes handling the new flag in both the argument parsing and include resolution logic, as well as updating relevant tests to validate the new functionality.
1 parent 53c3868 commit 267c55c

File tree

5 files changed

+81
-13
lines changed

5 files changed

+81
-13
lines changed

integration_test.py

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from 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
)
399424
def 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"]:

main.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ int main(int argc, char **argv)
5757
if (std::strncmp(arg, "-include=",9)==0) {
5858
dui.includes.push_back(arg+9);
5959
found = true;
60+
} else if (std::strncmp(arg, "-isystem", 8) == 0) {
61+
dui.searchPaths.push_back({arg + 8, simplecpp::DUI::PathKind::SystemInclude});
62+
found = true;
6063
} else if (std::strncmp(arg, "-is",3)==0) {
6164
use_istream = true;
6265
found = true;
@@ -109,6 +112,7 @@ int main(int argc, char **argv)
109112
std::cout << "simplecpp [options] filename" << std::endl;
110113
std::cout << " -DNAME Define NAME." << std::endl;
111114
std::cout << " -IPATH Include path." << std::endl;
115+
std::cout << " -isystemPATH System include path." << std::endl;
112116
std::cout << " -FPATH Framework path." << std::endl;
113117
std::cout << " -iframeworkPATH System framework path." << std::endl;
114118
std::cout << " -include=FILE Include FILE." << std::endl;

simplecpp.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3052,6 +3052,15 @@ static std::string openHeader(std::ifstream &f, const simplecpp::DUI &dui, const
30523052
}
30533053
}
30543054

3055+
// -isystem
3056+
for (const auto &searchPath : searchPaths) {
3057+
if (searchPath.kind == simplecpp::DUI::PathKind::SystemInclude) {
3058+
std::string path = openHeaderDirect(f, simplecpp::simplifyPath(searchPath.path + "/" + header));
3059+
if (!path.empty())
3060+
return path;
3061+
}
3062+
}
3063+
30553064
// -iframework
30563065
for (const auto &searchPath : searchPaths) {
30573066
if (searchPath.kind == simplecpp::DUI::PathKind::SystemFramework) {
@@ -3149,6 +3158,13 @@ std::pair<simplecpp::FileData *, bool> simplecpp::FileDataCache::get(const std::
31493158
}
31503159
}
31513160

3161+
// -isystem
3162+
for (const auto &searchPath : searchPaths) {
3163+
if (searchPath.kind == DUI::PathKind::SystemInclude) {
3164+
push_unique(candidates, simplecpp::simplifyPath(searchPath.path + "/" + header));
3165+
}
3166+
}
3167+
31523168
// -iframework
31533169
for (const auto &searchPath : searchPaths) {
31543170
if (searchPath.kind == simplecpp::DUI::PathKind::SystemFramework) {

simplecpp.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,7 @@ namespace simplecpp {
400400
* -D <name>=<value> Add macro definition
401401
* -U <name> Undefine macro
402402
* -I <dir> Add include search directory
403+
* -isystem <dir> Add system include search directory
403404
* -F <dir> Add framework search directory (Darwin)
404405
* -iframework <dir> Add system framework search directory (Darwin)
405406
* --include <file> Force inclusion of a header
@@ -416,8 +417,8 @@ namespace simplecpp {
416417
struct SIMPLECPP_LIB DUI {
417418
DUI() : clearIncludeCache(false), removeComments(false) {}
418419

419-
// Typed search path entry. Mirrors GCC behavior for -I, -F, -iframework.
420-
enum class PathKind { Include, Framework, SystemFramework };
420+
// Typed search path entry. Mirrors GCC behavior for -I, -isystem, -F, -iframework.
421+
enum class PathKind { Include, SystemInclude, Framework, SystemFramework };
421422
struct SearchPath {
422423
std::string path;
423424
PathKind kind;
@@ -427,6 +428,10 @@ namespace simplecpp {
427428
void addIncludePath(const std::string& path) {
428429
searchPaths.push_back({path, PathKind::Include});
429430
}
431+
// Mirrors compiler option -I<dir>
432+
void addSystemIncludePath(const std::string& path) {
433+
searchPaths.push_back({path, PathKind::SystemInclude});
434+
}
430435
// Mirrors compiler option -F<dir>
431436
void addFrameworkPath(const std::string& path) {
432437
searchPaths.push_back({path, PathKind::Framework});

testutils.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ def simplecpp(args = [], cwd = None):
4242
simplecpp_path = os.environ['SIMPLECPP_EXE_PATH']
4343
else:
4444
simplecpp_path = os.path.join(dir_path, "simplecpp")
45+
46+
print("*" * 20)
47+
print([simplecpp_path] + args)
4548
return __run_subprocess([simplecpp_path] + args, cwd = cwd)
4649

4750
def quoted_string(s):
@@ -50,6 +53,9 @@ def quoted_string(s):
5053
def format_include_path_arg(include_path):
5154
return f"-I{str(include_path)}"
5255

56+
def format_isystem_path_arg(include_path):
57+
return f"-isystem{str(include_path)}"
58+
5359
def format_framework_path_arg(framework_path):
5460
return f"-F{str(framework_path)}"
5561

0 commit comments

Comments
 (0)