7 const std::vector<std::string> eigen_includes = {
9 "/usr/local/include/eigen3",
10 "/opt/homebrew/include/eigen3"
13 const std::vector<std::string> freetype_includes = {
14 "/usr/include/freetype2",
15 "/usr/local/include/freetype2",
16 "/opt/homebrew/include/freetype2"
22#ifdef MAYAFLUX_PLATFORM_WINDOWS
23 char* value =
nullptr;
25 if (_dupenv_s(&value, &len, var) == 0 && value !=
nullptr) {
26 std::string result(value);
32 const char* value = std::getenv(var);
33 return value ? std::string(value) :
"";
39 static std::string cached_resource_dir;
41 if (cached_resource_dir.empty()) {
42 const char*
cmd =
"clang -print-resource-dir";
47 && (result.find(
"error:") == std::string::npos)
48 && (result.find(
"clang") != std::string::npos
49 || result.find(
"lib") != std::string::npos
50 || fs::exists(result))) {
52 cached_resource_dir = std::move(result);
57 return cached_resource_dir;
62 static std::vector<std::string> includes;
64 if (includes.empty()) {
65#ifdef MAYAFLUX_PLATFORM_WINDOWS
66 auto msvc_includes = get_msvc_includes();
67 includes.insert(includes.end(), msvc_includes.begin(), msvc_includes.end());
69 auto sdk_includes = get_windows_sdk_includes();
70 includes.insert(includes.end(), sdk_includes.begin(), sdk_includes.end());
74 includes.insert(includes.end(), clang_includes.begin(), clang_includes.end());
76#ifdef MAYAFLUX_PLATFORM_MACOS
77 std::string xcode_includes = SystemConfig::get_xcode_system_includes();
78 if (!xcode_includes.empty()) {
79 includes.push_back(xcode_includes);
82 std::string homebrew_includes =
"/opt/homebrew/include";
83 if (fs::exists(homebrew_includes)) {
84 includes.push_back(homebrew_includes);
94 static std::vector<std::string> lib_paths;
96 if (lib_paths.empty()) {
97#ifdef MAYAFLUX_PLATFORM_WINDOWS
98 auto msvc_libs = get_msvc_libraries();
99 lib_paths.insert(lib_paths.end(), msvc_libs.begin(), msvc_libs.end());
101 auto sdk_libs = get_windows_sdk_libraries();
102 lib_paths.insert(lib_paths.end(), sdk_libs.begin(), sdk_libs.end());
105 lib_paths.insert(lib_paths.end(), system_libs.begin(), system_libs.end());
114 static std::unordered_map<std::string, std::string> cache;
116 auto it = cache.find(library_name);
117 if (it != cache.end()) {
121 std::string& result = cache[library_name];
124 const fs::path resolved_prefix(prefix);
125 for (
const auto& subdir : {
"lib",
"lib64",
"bin" }) {
126 fs::path candidate = resolved_prefix / subdir / search_name;
127 if (fs::exists(candidate)) {
128 result = candidate.string();
134 fs::path candidate = fs::path(lib_path) / search_name;
135 if (fs::exists(candidate)) {
136 result = candidate.string();
146 static std::unordered_map<std::string, std::string> cache;
148 auto it = cache.find(library_name);
149 if (it != cache.end()) {
153 std::string& result = cache[library_name];
154 if (library_name ==
"Eigen" || library_name ==
"eigen") {
155 if (!Config::EIGEN_HINT.empty() && fs::exists(Config::EIGEN_HINT)) {
156 result = Config::EIGEN_HINT;
158 for (
const auto& path : eigen_includes) {
159 if (std::filesystem::exists(path)) {
165 }
else if (library_name ==
"freetype2" || library_name ==
"freetype") {
166 if (!Config::FREETYPE_HINT.empty() && fs::exists(Config::FREETYPE_HINT)) {
167 result = Config::FREETYPE_HINT;
169 for (
const auto& path : freetype_includes) {
170 if (std::filesystem::exists(path)) {
184 std::array<char, 128> buffer {};
187 using PipeCloser = int (*)(FILE*);
188#ifdef MAYAFLUX_PLATFORM_WINDOWS
189 std::unique_ptr<FILE, PipeCloser> pipe(_popen(
cmd,
"r"), _pclose);
191 std::unique_ptr<FILE, PipeCloser> pipe(popen(
cmd,
"r"), pclose);
197 while (fgets(buffer.data(),
static_cast<int>(buffer.size()), pipe.get())) {
198 result += buffer.data();
205 str.erase(std::remove(str.begin(), str.end(),
'\r'), str.end());
206 str.erase(std::remove(str.begin(), str.end(),
'\n'), str.end());
207 str.erase(str.find_last_not_of(
" \t") + 1);
212#ifdef MAYAFLUX_PLATFORM_WINDOWS
214 if (library_name.find(ext) == std::string::npos)
215 return library_name + ext;
216#elif defined(MAYAFLUX_PLATFORM_MACOS)
218 if (library_name.find(ext) == std::string::npos && library_name.find(
"lib") != 0)
219 return "lib" + library_name + ext;
222 if (library_name.find(ext) == std::string::npos && library_name.find(
".a") == std::string::npos)
223 return "lib" + library_name + ext;
230#ifdef MAYAFLUX_PLATFORM_WINDOWS
231 const char*
cmd =
"echo. | clang -v -E -x c++ - 2>&1";
233 const char*
cmd =
"clang -v -E -x c++ - 2>&1 < /dev/null";
242 std::vector<std::string> paths;
243 bool in_search_paths =
false;
245 std::istringstream stream(output);
248 while (std::getline(stream, line)) {
249 if (line.find(
"#include <...> search starts here:") != std::string::npos) {
250 in_search_paths =
true;
253 if (line.find(
"End of search list.") != std::string::npos) {
256 if (in_search_paths) {
257 auto start = line.find_first_not_of(
" \t");
258 if (start != std::string::npos) {
259 auto end = line.find_last_not_of(
" \t");
260 std::string path = line.substr(start, end - start + 1);
262 paths.push_back(path);
272 if (!fs::exists(base)) {
276 std::string latest_version;
277 for (
const auto& entry : fs::directory_iterator(base)) {
278 if (entry.is_directory()) {
279 std::string name = entry.path().filename().string();
280 if (!name.empty() && std::isdigit(name[0])) {
281 if (latest_version.empty() || name > latest_version) {
282 latest_version = name;
287 return latest_version;
290#ifdef MAYAFLUX_PLATFORM_WINDOWS
292std::string SystemConfig::find_latest_vs_installation()
294 std::vector<std::string> vswhere_paths = {
295 "C:\\Program Files (x86)\\Microsoft Visual Studio\\Installer\\vswhere.exe",
296 "C:\\Program Files\\Microsoft Visual Studio\\Installer\\vswhere.exe"
299 for (
const auto& path : vswhere_paths) {
300 if (fs::exists(path)) {
301 std::string
cmd =
"\"" + path +
"\" -latest -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath";
303 if (!vs_path.empty()) {
305 if (fs::exists(vs_path)) {
315std::string SystemConfig::find_latest_msvc_version(
const fs::path& msvc_base)
317 if (!fs::exists(msvc_base)) {
321 std::string latest_version;
322 for (
const auto& entry : fs::directory_iterator(msvc_base)) {
323 if (entry.is_directory()) {
324 std::string name = entry.path().filename().string();
325 if (!name.empty() && std::isdigit(name[0])) {
326 if (latest_version.empty() || name > latest_version) {
327 latest_version = name;
332 return latest_version;
335std::vector<std::string> SystemConfig::get_msvc_includes()
337 std::vector<std::string> includes;
339 std::string vs_path = find_latest_vs_installation();
340 if (!vs_path.empty()) {
341 fs::path msvc_base = fs::path(vs_path) /
"VC" /
"Tools" /
"MSVC";
342 std::string
version = find_latest_msvc_version(msvc_base);
345 fs::path include_path = msvc_base /
version /
"include";
346 if (fs::exists(include_path)) {
347 includes.push_back(include_path.string());
352 if (includes.empty()) {
354 if (!vc_dir.empty() && fs::exists(vc_dir)) {
355 fs::path include_path = fs::path(vc_dir) /
"include";
356 if (fs::exists(include_path)) {
357 includes.push_back(include_path.string());
365std::vector<std::string> SystemConfig::get_msvc_libraries()
367 std::vector<std::string> lib_paths;
369 std::string vs_path = find_latest_vs_installation();
370 if (!vs_path.empty()) {
371 fs::path msvc_base = fs::path(vs_path) /
"VC" /
"Tools" /
"MSVC";
372 std::string
version = find_latest_msvc_version(msvc_base);
375 fs::path lib_path = msvc_base /
version /
"lib" /
"x64";
376 if (fs::exists(lib_path)) {
377 lib_paths.push_back(lib_path.string());
382 if (lib_paths.empty()) {
384 if (!vc_dir.empty() && fs::exists(vc_dir)) {
385 fs::path lib_path = fs::path(vc_dir) /
"lib" /
"x64";
386 if (fs::exists(lib_path)) {
387 lib_paths.push_back(lib_path.string());
395std::vector<std::string> SystemConfig::get_windows_sdk_includes()
397 std::vector<std::string> includes;
399 std::string sdk_dir =
safe_getenv(
"WindowsSdkDir");
400 std::string sdk_ver =
safe_getenv(
"WindowsSDKVersion");
402 if (!sdk_dir.empty() && !sdk_ver.empty() && fs::exists(sdk_dir)) {
403 fs::path base = fs::path(sdk_dir);
404 std::string
version = std::string(sdk_ver);
409 std::vector<std::string> subdirs = {
"ucrt",
"shared",
"um",
"winrt",
"cppwinrt" };
410 for (
const auto& subdir : subdirs) {
411 fs::path include_path = base /
"Include" /
version / subdir;
412 if (fs::exists(include_path)) {
413 includes.push_back(include_path.string());
418 if (includes.empty()) {
419 includes = probe_sdk_paths(
"Include",
420 std::vector<std::string> {
"ucrt",
"shared",
"um",
"winrt",
"cppwinrt" });
426std::vector<std::string> SystemConfig::get_windows_sdk_libraries()
428 std::vector<std::string> lib_paths;
430 std::string sdk_dir =
safe_getenv(
"WindowsSdkDir");
431 std::string sdk_ver =
safe_getenv(
"WindowsSDKVersion");
433 if (!sdk_dir.empty() && !sdk_ver.empty() && fs::exists(sdk_dir)) {
434 fs::path base = fs::path(sdk_dir);
435 std::string
version = std::string(sdk_ver);
440 std::vector<std::string> subdirs = {
"ucrt",
"um" };
441 for (
const auto& subdir : subdirs) {
442 fs::path lib_path = base /
"Lib" /
version / subdir /
"x64";
443 if (fs::exists(lib_path)) {
444 lib_paths.push_back(lib_path.string());
449 if (lib_paths.empty()) {
450 lib_paths = probe_sdk_paths(
"Lib",
451 std::vector<std::string> {
"ucrt",
"um" },
"x64");
457std::vector<std::string> SystemConfig::probe_sdk_paths(
const std::string& subpath,
458 const std::vector<std::string>& subdirs,
459 const std::string& arch)
461 std::vector<std::string> paths;
462 std::vector<fs::path> sdk_base_paths = {
463 "C:\\Program Files (x86)\\Windows Kits\\10",
464 "C:\\Program Files\\Windows Kits\\10"
467 for (
const auto& base_path : sdk_base_paths) {
468 if (fs::exists(base_path)) {
469 fs::path search_dir = fs::path(base_path) / subpath;
473 for (
const auto& subdir : subdirs) {
474 fs::path final_path = fs::path(base_path) / subpath /
version / subdir;
476 final_path = final_path / arch;
478 if (fs::exists(final_path)) {
479 paths.push_back(final_path.string());
494 std::vector<std::string> lib_paths;
496 std::string ld_library_path =
safe_getenv(
"LD_LIBRARY_PATH");
497 if (!ld_library_path.empty()) {
498 std::istringstream stream(ld_library_path);
500 while (std::getline(stream, path,
':')) {
501 if (!path.empty() && fs::exists(path)) {
502 lib_paths.push_back(path);
507 std::vector<std::string> default_paths = {
508#if defined(MAYAFLUX_PLATFORM_MACOS) && (defined(__aarch64__) || defined(_M_ARM64))
519 for (
const auto& path : default_paths) {
520 if (fs::exists(path)) {
521 lib_paths.push_back(path);
528#ifdef MAYAFLUX_PLATFORM_MACOS
530std::string SystemConfig::get_macos_sdk_path()
532 const char*
cmd =
"xcrun --show-sdk-path 2>/dev/null";
536 if (!sdk_path.empty() && fs::exists(sdk_path)) {
540 std::vector<std::string> possible_paths = {
541 "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk",
542 "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk"
545 for (
const auto& path : possible_paths) {
546 if (fs::exists(path)) {
554std::string SystemConfig::get_xcode_system_includes()
556 std::string sdk_path = get_macos_sdk_path();
557 if (sdk_path.empty()) {
561 std::string include_path = sdk_path +
"/usr/include";
562 if (fs::exists(include_path)) {