Skip to content

Commit c5a5cbe

Browse files
committed
feat: add Darwin-style framework search paths (-F, -iframework)
- Introduce DUI::SearchPath with PathKind {Include, Framework, SystemFramework} to model GCC/Clang behavior on Darwin. - Extend openHeader() to search left-to-right across mixed -I/-F/-iframework paths, preserving order. - Implement toAppleFrameworkRelatives() returning both Headers and PrivateHeaders candidates for a <Pkg/Hdr.h> include. - Preserve backward compatibility: if searchPaths is empty, legacy includePaths are mirrored as Include paths. - Update tests to use PathKind::Framework for framework-based includes. This brings simplecpp closer to GCC/Clang behavior on macOS and enables robust resolution of framework headers like Foundation/Foundation.h. Suggested-by: glank <[email protected]>
1 parent 2c718ab commit c5a5cbe

File tree

3 files changed

+54
-24
lines changed

3 files changed

+54
-24
lines changed

simplecpp.cpp

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "simplecpp.h"
1414

1515
#include <algorithm>
16+
#include <array>
1617
#include <cassert>
1718
#include <cctype>
1819
#include <climits>
@@ -2432,16 +2433,18 @@ static bool isAbsolutePath(const std::string &path)
24322433
#endif
24332434

24342435
namespace {
2435-
// "<Pkg/Hdr.h>" -> "<Pkg.framework/Headers/Hdr.h>"
2436-
static inline std::string
2437-
toAppleFrameworkRelative(const std::string& header)
2436+
// "<Pkg/Hdr.h>" -> "<Pkg.framework/Headers/Hdr.h>" (and PrivateHeaders variant).
2437+
// Returns candidates in priority order (Headers, then PrivateHeaders).
2438+
static inline std::array<std::string,2>
2439+
toAppleFrameworkRelatives(const std::string& header)
24382440
{
24392441
const std::size_t slash = header.find('/');
24402442
if (slash == std::string::npos)
2441-
return header; // no transformation applicable
2443+
return { header, header }; // no transformation applicable
24422444
const std::string pkg = header.substr(0, slash);
24432445
const std::string tail = header.substr(slash); // includes '/'
2444-
return pkg + ".framework/Headers" + tail;
2446+
return { pkg + ".framework/Headers" + tail,
2447+
pkg + ".framework/PrivateHeaders" + tail };
24452448
}
24462449
}
24472450

@@ -3015,23 +3018,34 @@ static std::string openHeader(std::ifstream &f, const simplecpp::DUI &dui, const
30153018
}
30163019
}
30173020

3018-
// search the header on the include paths (provided by the flags "-I...")
3019-
for (const auto &includePath : dui.includePaths) {
3020-
std::string path = openHeaderDirect(f, simplecpp::simplifyPath(includePath + "/" + header));
3021-
if (!path.empty())
3022-
return path;
3021+
// Build an ordered, typed path list:
3022+
// - Prefer DUI::searchPaths when provided (interleaved -I/-F/-iframework).
3023+
// - Otherwise mirror legacy includePaths into Include entries (back-compat).
3024+
std::vector<simplecpp::DUI::SearchPath> searchPaths;
3025+
if (!dui.searchPaths.empty()) {
3026+
searchPaths = dui.searchPaths;
3027+
} else {
3028+
searchPaths.reserve(dui.includePaths.size());
3029+
for (const auto &includePath : dui.includePaths)
3030+
searchPaths.push_back({includePath, simplecpp::DUI::PathKind::Include});
30233031
}
30243032

3025-
// on Apple, try to find the header in the framework path
3026-
// Convert <includePath>/PKGNAME/myHeader -> <includePath>/PKGNAME.framework/Headers/myHeader
3027-
// Works on any platform, but only relevant when compiling against Apple SDKs.
3028-
const std::string appleFrameworkHeader = toAppleFrameworkRelative(header);
3029-
if (appleFrameworkHeader != header) {
3030-
for (const auto & includePath: dui.includePaths) {
3031-
const std::string frameworkCandidatePath = includePath + '/' + appleFrameworkHeader;
3032-
std::string simplePath = openHeaderDirect(f, simplecpp::simplifyPath(frameworkCandidatePath));
3033-
if (!simplePath.empty())
3034-
return simplePath;
3033+
// Search left-to-right, honoring path kinds.
3034+
for (const auto &searchPath : searchPaths) {
3035+
if (searchPath.kind == simplecpp::DUI::PathKind::Include) {
3036+
const std::string path = openHeaderDirect(f, simplecpp::simplifyPath(searchPath.path + "/" + header));
3037+
if (!path.empty())
3038+
return path;
3039+
} else {
3040+
// Framework & SystemFramework: try Headers then PrivateHeaders
3041+
const auto relatives = toAppleFrameworkRelatives(header);
3042+
if (relatives[0] != header) { // Skip if no framework rewrite was applied.
3043+
for (const auto &rel : relatives) {
3044+
const std::string frameworkPath = openHeaderDirect(f, simplecpp::simplifyPath(searchPath.path + "/" + rel));
3045+
if (!frameworkPath.empty())
3046+
return frameworkPath;
3047+
}
3048+
}
30353049
}
30363050
}
30373051

simplecpp.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,9 +344,21 @@ namespace simplecpp {
344344
*/
345345
struct SIMPLECPP_LIB DUI {
346346
DUI() : clearIncludeCache(false), removeComments(false) {}
347+
// Typed search path entry. Mirrors GCC behavior for -I, -F, -iframework.
348+
enum class PathKind { Include, Framework, SystemFramework };
349+
struct SearchPath {
350+
std::string path;
351+
PathKind kind;
352+
};
347353
std::list<std::string> defines;
348354
std::set<std::string> undefined;
355+
356+
// Back-compat: legacy -I list. If searchPaths is empty at use time,
357+
// consumers should mirror includePaths -> searchPaths as Include.
349358
std::list<std::string> includePaths;
359+
// New: ordered, interleaved search paths with kind.
360+
std::vector<SearchPath> searchPaths;
361+
350362
std::list<std::string> includes;
351363
std::string std;
352364
bool clearIncludeCache;

test.cpp

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2148,9 +2148,11 @@ static void appleFrameworkIncludeTest()
21482148
simplecpp::TokenList tokens2(files);
21492149
simplecpp::DUI dui;
21502150
#ifdef SIMPLECPP_SOURCE_DIR
2151-
dui.includePaths.push_back(std::string(SIMPLECPP_SOURCE_DIR) + "/testsuite");
2151+
dui.searchPaths.push_back({std::string(SIMPLECPP_SOURCE_DIR) + "/testsuite",
2152+
simplecpp::DUI::PathKind::Framework
2153+
});
21522154
#else
2153-
dui.includePaths.push_back("./testsuite");
2155+
dui.searchPaths.push_back({"./testsuite", simplecpp::DUI::PathKind::Framework});
21542156
#endif
21552157
simplecpp::OutputList outputList;
21562158
simplecpp::preprocess(tokens2, rawtokens, files, cache, dui, &outputList);
@@ -2175,9 +2177,11 @@ static void appleFrameworkHasIncludeTest()
21752177
simplecpp::TokenList tokens2(files);
21762178
simplecpp::DUI dui;
21772179
#ifdef SIMPLECPP_SOURCE_DIR
2178-
dui.includePaths.push_back(std::string(SIMPLECPP_SOURCE_DIR) + "/testsuite");
2180+
dui.searchPaths.push_back({std::string(SIMPLECPP_SOURCE_DIR) + "/testsuite",
2181+
simplecpp::DUI::PathKind::Framework
2182+
});
21792183
#else
2180-
dui.includePaths.push_back("./testsuite");
2184+
dui.searchPaths.push_back({"./testsuite", simplecpp::DUI::PathKind::Framework});
21812185
#endif
21822186
dui.std = "c++17"; // enable __has_include
21832187

0 commit comments

Comments
 (0)