25 const std::vector<std::string> eigen_includes = {
26 "/usr/include/eigen3",
27 "/usr/local/include/eigen3",
28 "/opt/homebrew/include/eigen3"
31 const std::vector<std::string> freetype_includes = {
32 "/usr/include/freetype2",
33 "/usr/local/include/freetype2",
34 "/opt/homebrew/include/freetype2"
40#ifdef MAYAFLUX_PLATFORM_WINDOWS
41 char*
value =
nullptr;
43 if (_dupenv_s(&
value, &len, var) == 0 &&
value !=
nullptr) {
44 std::string result(
value);
50 const char*
value = std::getenv(var);
57 static std::string cached_resource_dir;
59 if (cached_resource_dir.empty()) {
60 const char*
cmd =
"clang -print-resource-dir";
65 && (result.find(
"error:") == std::string::npos)
66 && (result.find(
"clang") != std::string::npos
67 || result.find(
"lib") != std::string::npos
68 || fs::exists(result))) {
70 cached_resource_dir = std::move(result);
75 return cached_resource_dir;
80 static std::vector<std::string> includes;
82 if (includes.empty()) {
83#ifdef MAYAFLUX_PLATFORM_WINDOWS
84 auto msvc_includes = get_msvc_includes();
85 includes.insert(includes.end(), msvc_includes.begin(), msvc_includes.end());
87 auto sdk_includes = get_windows_sdk_includes();
88 includes.insert(includes.end(), sdk_includes.begin(), sdk_includes.end());
92 includes.insert(includes.end(), clang_includes.begin(), clang_includes.end());
94#ifdef MAYAFLUX_PLATFORM_MACOS
95 std::string xcode_includes = SystemConfig::get_xcode_system_includes();
96 if (!xcode_includes.empty()) {
97 includes.push_back(xcode_includes);
100 std::string homebrew_includes =
"/opt/homebrew/include";
101 if (fs::exists(homebrew_includes)) {
102 includes.push_back(homebrew_includes);
112 static std::vector<std::string> lib_paths;
114 if (lib_paths.empty()) {
115#ifdef MAYAFLUX_PLATFORM_WINDOWS
116 auto msvc_libs = get_msvc_libraries();
117 lib_paths.insert(lib_paths.end(), msvc_libs.begin(), msvc_libs.end());
119 auto sdk_libs = get_windows_sdk_libraries();
120 lib_paths.insert(lib_paths.end(), sdk_libs.begin(), sdk_libs.end());
123 lib_paths.insert(lib_paths.end(), system_libs.begin(), system_libs.end());
132 static std::unordered_map<std::string, std::string> cache;
134 auto it = cache.find(library_name);
135 if (it != cache.end()) {
139 std::string& result = cache[library_name];
142 const fs::path resolved_prefix(prefix);
143 for (
const auto& subdir : {
"lib",
"lib64",
"bin" }) {
144 fs::path candidate = resolved_prefix / subdir / search_name;
145 if (fs::exists(candidate)) {
146 result = candidate.string();
152 fs::path candidate = fs::path(lib_path) / search_name;
153 if (fs::exists(candidate)) {
154 result = candidate.string();
164 static std::unordered_map<std::string, std::string> cache;
166 auto it = cache.find(library_name);
167 if (it != cache.end()) {
171 std::string& result = cache[library_name];
172 if (library_name ==
"Eigen" || library_name ==
"eigen") {
173 if (!Config::EIGEN_HINT.empty() && fs::exists(Config::EIGEN_HINT)) {
174 result = Config::EIGEN_HINT;
176 for (
const auto& path : eigen_includes) {
177 if (std::filesystem::exists(path)) {
183 }
else if (library_name ==
"freetype2" || library_name ==
"freetype") {
184 if (!Config::FREETYPE_HINT.empty() && fs::exists(Config::FREETYPE_HINT)) {
185 result = Config::FREETYPE_HINT;
187 for (
const auto& path : freetype_includes) {
188 if (std::filesystem::exists(path)) {
202 std::array<char, 128>
buffer {};
205 using PipeCloser = int (*)(FILE*);
206#ifdef MAYAFLUX_PLATFORM_WINDOWS
207 std::unique_ptr<FILE, PipeCloser> pipe(_popen(
cmd,
"r"), _pclose);
209 std::unique_ptr<FILE, PipeCloser> pipe(popen(
cmd,
"r"), pclose);
215 while (fgets(
buffer.data(),
static_cast<int>(
buffer.size()), pipe.get())) {
223 str.erase(std::remove(str.begin(), str.end(),
'\r'), str.end());
224 str.erase(std::remove(str.begin(), str.end(),
'\n'), str.end());
225 str.erase(str.find_last_not_of(
" \t") + 1);
230#ifdef MAYAFLUX_PLATFORM_WINDOWS
232 if (library_name.find(ext) == std::string::npos)
233 return library_name + ext;
234#elif defined(MAYAFLUX_PLATFORM_MACOS)
236 if (library_name.find(ext) == std::string::npos && library_name.find(
"lib") != 0)
237 return "lib" + library_name + ext;
240 if (library_name.find(ext) == std::string::npos && library_name.find(
".a") == std::string::npos)
241 return "lib" + library_name + ext;
248#ifdef MAYAFLUX_PLATFORM_WINDOWS
249 const char*
cmd =
"echo. | clang -v -E -x c++ - 2>&1";
251 const char*
cmd =
"clang -v -E -x c++ - 2>&1 < /dev/null";
260 std::vector<std::string> paths;
261 bool in_search_paths =
false;
263 std::istringstream
stream(output);
266 while (std::getline(
stream, line)) {
267 if (line.find(
"#include <...> search starts here:") != std::string::npos) {
268 in_search_paths =
true;
271 if (line.find(
"End of search list.") != std::string::npos) {
274 if (in_search_paths) {
275 auto start = line.find_first_not_of(
" \t");
276 if (start != std::string::npos) {
277 auto end = line.find_last_not_of(
" \t");
278 std::string path = line.substr(start, end - start + 1);
280 paths.push_back(path);
290 if (!fs::exists(base)) {
294 std::string latest_version;
295 for (
const auto& entry : fs::directory_iterator(base)) {
296 if (entry.is_directory()) {
297 std::string
name = entry.path().filename().string();
298 if (!
name.empty() && std::isdigit(
name[0])) {
299 if (latest_version.empty() ||
name > latest_version) {
300 latest_version =
name;
305 return latest_version;
308#ifdef MAYAFLUX_PLATFORM_WINDOWS
310std::string SystemConfig::find_latest_vs_installation()
312 std::vector<std::string> vswhere_paths = {
313 "C:\\Program Files (x86)\\Microsoft Visual Studio\\Installer\\vswhere.exe",
314 "C:\\Program Files\\Microsoft Visual Studio\\Installer\\vswhere.exe"
317 for (
const auto& path : vswhere_paths) {
318 if (fs::exists(path)) {
319 std::string
cmd =
"\"" + path +
"\" -latest -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath";
321 if (!vs_path.empty()) {
323 if (fs::exists(vs_path)) {
333std::string SystemConfig::find_latest_msvc_version(
const fs::path& msvc_base)
335 if (!fs::exists(msvc_base)) {
339 std::string latest_version;
340 for (
const auto& entry : fs::directory_iterator(msvc_base)) {
341 if (entry.is_directory()) {
342 std::string
name = entry.path().filename().string();
343 if (!
name.empty() && std::isdigit(
name[0])) {
344 if (latest_version.empty() ||
name > latest_version) {
345 latest_version =
name;
350 return latest_version;
353std::vector<std::string> SystemConfig::get_msvc_includes()
355 std::vector<std::string> includes;
357 std::string vs_path = find_latest_vs_installation();
358 if (!vs_path.empty()) {
359 fs::path msvc_base = fs::path(vs_path) /
"VC" /
"Tools" /
"MSVC";
360 std::string version = find_latest_msvc_version(msvc_base);
362 if (!version.empty()) {
363 fs::path include_path = msvc_base / version /
"include";
364 if (fs::exists(include_path)) {
365 includes.push_back(include_path.string());
370 if (includes.empty()) {
372 if (!vc_dir.empty() && fs::exists(vc_dir)) {
373 fs::path include_path = fs::path(vc_dir) /
"include";
374 if (fs::exists(include_path)) {
375 includes.push_back(include_path.string());
383std::vector<std::string> SystemConfig::get_msvc_libraries()
385 std::vector<std::string> lib_paths;
387 std::string vs_path = find_latest_vs_installation();
388 if (!vs_path.empty()) {
389 fs::path msvc_base = fs::path(vs_path) /
"VC" /
"Tools" /
"MSVC";
390 std::string version = find_latest_msvc_version(msvc_base);
392 if (!version.empty()) {
393 fs::path lib_path = msvc_base / version /
"lib" /
"x64";
394 if (fs::exists(lib_path)) {
395 lib_paths.push_back(lib_path.string());
400 if (lib_paths.empty()) {
402 if (!vc_dir.empty() && fs::exists(vc_dir)) {
403 fs::path lib_path = fs::path(vc_dir) /
"lib" /
"x64";
404 if (fs::exists(lib_path)) {
405 lib_paths.push_back(lib_path.string());
413std::vector<std::string> SystemConfig::get_windows_sdk_includes()
415 std::vector<std::string> includes;
417 std::string sdk_dir =
safe_getenv(
"WindowsSdkDir");
418 std::string sdk_ver =
safe_getenv(
"WindowsSDKVersion");
420 if (!sdk_dir.empty() && !sdk_ver.empty() && fs::exists(sdk_dir)) {
421 fs::path base = fs::path(sdk_dir);
422 std::string version = std::string(sdk_ver);
423 if (!version.empty() && version.back() ==
'\\') {
427 std::vector<std::string> subdirs = {
"ucrt",
"shared",
"um",
"winrt",
"cppwinrt" };
428 for (
const auto& subdir : subdirs) {
429 fs::path include_path = base /
"Include" / version / subdir;
430 if (fs::exists(include_path)) {
431 includes.push_back(include_path.string());
436 if (includes.empty()) {
437 includes = probe_sdk_paths(
"Include",
438 std::vector<std::string> {
"ucrt",
"shared",
"um",
"winrt",
"cppwinrt" });
444std::vector<std::string> SystemConfig::get_windows_sdk_libraries()
446 std::vector<std::string> lib_paths;
448 std::string sdk_dir =
safe_getenv(
"WindowsSdkDir");
449 std::string sdk_ver =
safe_getenv(
"WindowsSDKVersion");
451 if (!sdk_dir.empty() && !sdk_ver.empty() && fs::exists(sdk_dir)) {
452 fs::path base = fs::path(sdk_dir);
453 std::string version = std::string(sdk_ver);
454 if (!version.empty() && version.back() ==
'\\') {
458 std::vector<std::string> subdirs = {
"ucrt",
"um" };
459 for (
const auto& subdir : subdirs) {
460 fs::path lib_path = base /
"Lib" / version / subdir /
"x64";
461 if (fs::exists(lib_path)) {
462 lib_paths.push_back(lib_path.string());
467 if (lib_paths.empty()) {
468 lib_paths = probe_sdk_paths(
"Lib",
469 std::vector<std::string> {
"ucrt",
"um" },
"x64");
475std::vector<std::string> SystemConfig::probe_sdk_paths(
const std::string& subpath,
476 const std::vector<std::string>& subdirs,
477 const std::string& arch)
479 std::vector<std::string> paths;
480 std::vector<fs::path> sdk_base_paths = {
481 "C:\\Program Files (x86)\\Windows Kits\\10",
482 "C:\\Program Files\\Windows Kits\\10"
485 for (
const auto& base_path : sdk_base_paths) {
486 if (fs::exists(base_path)) {
487 fs::path search_dir = fs::path(base_path) / subpath;
490 if (!version.empty()) {
491 for (
const auto& subdir : subdirs) {
492 fs::path final_path = fs::path(base_path) / subpath / version / subdir;
494 final_path = final_path / arch;
496 if (fs::exists(final_path)) {
497 paths.push_back(final_path.string());
512 std::vector<std::string> lib_paths;
514 std::string ld_library_path =
safe_getenv(
"LD_LIBRARY_PATH");
515 if (!ld_library_path.empty()) {
516 std::istringstream
stream(ld_library_path);
518 while (std::getline(
stream, path,
':')) {
519 if (!path.empty() && fs::exists(path)) {
520 lib_paths.push_back(path);
525 std::vector<std::string> default_paths = {
526#if defined(MAYAFLUX_PLATFORM_MACOS)
537 for (
const auto& path : default_paths) {
538 if (fs::exists(path)) {
539 lib_paths.push_back(path);
546#ifdef MAYAFLUX_PLATFORM_MACOS
548std::string SystemConfig::get_macos_sdk_path()
550 const char*
cmd =
"xcrun --show-sdk-path 2>/dev/null";
554 if (!sdk_path.empty() && fs::exists(sdk_path)) {
558 std::vector<std::string> possible_paths = {
559 "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk",
560 "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk"
563 for (
const auto& path : possible_paths) {
564 if (fs::exists(path)) {
572std::string SystemConfig::get_xcode_system_includes()
574 std::string sdk_path = get_macos_sdk_path();
575 if (sdk_path.empty()) {
579 std::string include_path = sdk_path +
"/usr/include";
580 if (fs::exists(include_path)) {