7 const std::vector<std::string> eigen_includes = {
9 "/usr/local/include/eigen3",
10 "/opt/homebrew/include/eigen3"
16#ifdef MAYAFLUX_PLATFORM_WINDOWS
17 char* value =
nullptr;
19 if (_dupenv_s(&value, &len, var) == 0 && value !=
nullptr) {
20 std::string result(value);
26 const char* value = std::getenv(var);
27 return value ? std::string(value) :
"";
33 static std::string cached_resource_dir;
35 if (cached_resource_dir.empty()) {
36 const char* cmd =
"clang -print-resource-dir";
41 && (result.find(
"error:") == std::string::npos)
42 && (result.find(
"clang") != std::string::npos
43 || result.find(
"lib") != std::string::npos
44 || fs::exists(result))) {
46 cached_resource_dir = std::move(result);
51 return cached_resource_dir;
56 static std::vector<std::string> includes;
58 if (includes.empty()) {
59#ifdef MAYAFLUX_PLATFORM_WINDOWS
60 auto msvc_includes = get_msvc_includes();
61 includes.insert(includes.end(), msvc_includes.begin(), msvc_includes.end());
63 auto sdk_includes = get_windows_sdk_includes();
64 includes.insert(includes.end(), sdk_includes.begin(), sdk_includes.end());
68 includes.insert(includes.end(), clang_includes.begin(), clang_includes.end());
70#ifdef MAYAFLUX_PLATFORM_MACOS
71 std::string xcode_includes = SystemConfig::get_xcode_system_includes();
72 if (!xcode_includes.empty()) {
73 includes.push_back(xcode_includes);
76 std::string homebrew_includes =
"/opt/homebrew/include";
77 if (fs::exists(homebrew_includes)) {
78 includes.push_back(homebrew_includes);
88 static std::vector<std::string> lib_paths;
90 if (lib_paths.empty()) {
91#ifdef MAYAFLUX_PLATFORM_WINDOWS
92 auto msvc_libs = get_msvc_libraries();
93 lib_paths.insert(lib_paths.end(), msvc_libs.begin(), msvc_libs.end());
95 auto sdk_libs = get_windows_sdk_libraries();
96 lib_paths.insert(lib_paths.end(), sdk_libs.begin(), sdk_libs.end());
99 lib_paths.insert(lib_paths.end(), system_libs.begin(), system_libs.end());
108 static std::unordered_map<std::string, std::string> cache;
110 auto it = cache.find(library_name);
111 if (it != cache.end()) {
115 std::string& result = cache[library_name];
120 for (
const auto& lib_path : lib_paths) {
121 fs::path full_path = fs::path(lib_path) / search_name;
122 if (fs::exists(full_path)) {
123 result = full_path.string();
133 static std::unordered_map<std::string, std::string> cache;
135 auto it = cache.find(library_name);
136 if (it != cache.end()) {
140 std::string& result = cache[library_name];
141 if (library_name ==
"Eigen" || library_name ==
"eigen") {
142 if (!Config::EIGEN_HINT.empty() && fs::exists(Config::EIGEN_HINT)) {
143 result = Config::EIGEN_HINT;
145 for (
const auto& path : eigen_includes) {
146 if (std::filesystem::exists(path)) {
160 std::array<char, 128> buffer {};
163 using PipeCloser = int (*)(FILE*);
164#ifdef MAYAFLUX_PLATFORM_WINDOWS
165 std::unique_ptr<FILE, PipeCloser> pipe(_popen(cmd,
"r"), _pclose);
167 std::unique_ptr<FILE, PipeCloser> pipe(popen(cmd,
"r"), pclose);
173 while (fgets(buffer.data(),
static_cast<int>(buffer.size()), pipe.get())) {
174 result += buffer.data();
181 str.erase(std::remove(str.begin(), str.end(),
'\r'), str.end());
182 str.erase(std::remove(str.begin(), str.end(),
'\n'), str.end());
183 str.erase(str.find_last_not_of(
" \t") + 1);
188#ifdef MAYAFLUX_PLATFORM_WINDOWS
189 if (library_name.find(
".lib") == std::string::npos) {
190 return library_name +
".lib";
193 if (library_name.find(
".a") == std::string::npos && library_name.find(
".so") == std::string::npos) {
194 return "lib" + library_name +
".a";
202#ifdef MAYAFLUX_PLATFORM_WINDOWS
203 const char* cmd =
"echo. | clang -v -E -x c++ - 2>&1";
205 const char* cmd =
"clang -v -E -x c++ - 2>&1 < /dev/null";
214 std::vector<std::string> paths;
215 bool in_search_paths =
false;
217 std::istringstream stream(output);
220 while (std::getline(stream, line)) {
221 if (line.find(
"#include <...> search starts here:") != std::string::npos) {
222 in_search_paths =
true;
225 if (line.find(
"End of search list.") != std::string::npos) {
228 if (in_search_paths) {
229 auto start = line.find_first_not_of(
" \t");
230 if (start != std::string::npos) {
231 auto end = line.find_last_not_of(
" \t");
232 std::string path = line.substr(start, end - start + 1);
234 paths.push_back(path);
244 if (!fs::exists(base)) {
248 std::string latest_version;
249 for (
const auto& entry : fs::directory_iterator(base)) {
250 if (entry.is_directory()) {
251 std::string name = entry.path().filename().string();
252 if (!name.empty() && std::isdigit(name[0])) {
253 if (latest_version.empty() || name > latest_version) {
254 latest_version = name;
259 return latest_version;
262#ifdef MAYAFLUX_PLATFORM_WINDOWS
264std::string SystemConfig::find_latest_vs_installation()
266 std::vector<std::string> vswhere_paths = {
267 "C:\\Program Files (x86)\\Microsoft Visual Studio\\Installer\\vswhere.exe",
268 "C:\\Program Files\\Microsoft Visual Studio\\Installer\\vswhere.exe"
271 for (
const auto& path : vswhere_paths) {
272 if (fs::exists(path)) {
273 std::string cmd =
"\"" + path +
"\" -latest -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath";
275 if (!vs_path.empty()) {
277 if (fs::exists(vs_path)) {
287std::string SystemConfig::find_latest_msvc_version(
const fs::path& msvc_base)
289 if (!fs::exists(msvc_base)) {
293 std::string latest_version;
294 for (
const auto& entry : fs::directory_iterator(msvc_base)) {
295 if (entry.is_directory()) {
296 std::string name = entry.path().filename().string();
297 if (!name.empty() && std::isdigit(name[0])) {
298 if (latest_version.empty() || name > latest_version) {
299 latest_version = name;
304 return latest_version;
307std::vector<std::string> SystemConfig::get_msvc_includes()
309 std::vector<std::string> includes;
311 std::string vs_path = find_latest_vs_installation();
312 if (!vs_path.empty()) {
313 fs::path msvc_base = fs::path(vs_path) /
"VC" /
"Tools" /
"MSVC";
314 std::string version = find_latest_msvc_version(msvc_base);
316 if (!version.empty()) {
317 fs::path include_path = msvc_base / version /
"include";
318 if (fs::exists(include_path)) {
319 includes.push_back(include_path.string());
324 if (includes.empty()) {
326 if (!vc_dir.empty() && fs::exists(vc_dir)) {
327 fs::path include_path = fs::path(vc_dir) /
"include";
328 if (fs::exists(include_path)) {
329 includes.push_back(include_path.string());
337std::vector<std::string> SystemConfig::get_msvc_libraries()
339 std::vector<std::string> lib_paths;
341 std::string vs_path = find_latest_vs_installation();
342 if (!vs_path.empty()) {
343 fs::path msvc_base = fs::path(vs_path) /
"VC" /
"Tools" /
"MSVC";
344 std::string version = find_latest_msvc_version(msvc_base);
346 if (!version.empty()) {
347 fs::path lib_path = msvc_base / version /
"lib" /
"x64";
348 if (fs::exists(lib_path)) {
349 lib_paths.push_back(lib_path.string());
354 if (lib_paths.empty()) {
356 if (!vc_dir.empty() && fs::exists(vc_dir)) {
357 fs::path lib_path = fs::path(vc_dir) /
"lib" /
"x64";
358 if (fs::exists(lib_path)) {
359 lib_paths.push_back(lib_path.string());
367std::vector<std::string> SystemConfig::get_windows_sdk_includes()
369 std::vector<std::string> includes;
371 std::string sdk_dir =
safe_getenv(
"WindowsSdkDir");
372 std::string sdk_ver =
safe_getenv(
"WindowsSDKVersion");
374 if (!sdk_dir.empty() && !sdk_ver.empty() && fs::exists(sdk_dir)) {
375 fs::path base = fs::path(sdk_dir);
376 std::string version = std::string(sdk_ver);
377 if (!version.empty() && version.back() ==
'\\') {
381 std::vector<std::string> subdirs = {
"ucrt",
"shared",
"um",
"winrt",
"cppwinrt" };
382 for (
const auto& subdir : subdirs) {
383 fs::path include_path = base /
"Include" / version / subdir;
384 if (fs::exists(include_path)) {
385 includes.push_back(include_path.string());
390 if (includes.empty()) {
391 includes = probe_sdk_paths(
"Include",
392 std::vector<std::string> {
"ucrt",
"shared",
"um",
"winrt",
"cppwinrt" });
398std::vector<std::string> SystemConfig::get_windows_sdk_libraries()
400 std::vector<std::string> lib_paths;
402 std::string sdk_dir =
safe_getenv(
"WindowsSdkDir");
403 std::string sdk_ver =
safe_getenv(
"WindowsSDKVersion");
405 if (!sdk_dir.empty() && !sdk_ver.empty() && fs::exists(sdk_dir)) {
406 fs::path base = fs::path(sdk_dir);
407 std::string version = std::string(sdk_ver);
408 if (!version.empty() && version.back() ==
'\\') {
412 std::vector<std::string> subdirs = {
"ucrt",
"um" };
413 for (
const auto& subdir : subdirs) {
414 fs::path lib_path = base /
"Lib" / version / subdir /
"x64";
415 if (fs::exists(lib_path)) {
416 lib_paths.push_back(lib_path.string());
421 if (lib_paths.empty()) {
422 lib_paths = probe_sdk_paths(
"Lib",
423 std::vector<std::string> {
"ucrt",
"um" },
"x64");
429std::vector<std::string> SystemConfig::probe_sdk_paths(
const std::string& subpath,
430 const std::vector<std::string>& subdirs,
431 const std::string& arch)
433 std::vector<std::string> paths;
434 std::vector<fs::path> sdk_base_paths = {
435 "C:\\Program Files (x86)\\Windows Kits\\10",
436 "C:\\Program Files\\Windows Kits\\10"
439 for (
const auto& base_path : sdk_base_paths) {
440 if (fs::exists(base_path)) {
441 fs::path search_dir = fs::path(base_path) / subpath;
444 if (!version.empty()) {
445 for (
const auto& subdir : subdirs) {
446 fs::path final_path = fs::path(base_path) / subpath / version / subdir;
448 final_path = final_path / arch;
450 if (fs::exists(final_path)) {
451 paths.push_back(final_path.string());
466 std::vector<std::string> lib_paths;
468 std::string ld_library_path =
safe_getenv(
"LD_LIBRARY_PATH");
469 if (!ld_library_path.empty()) {
470 std::istringstream stream(ld_library_path);
472 while (std::getline(stream, path,
':')) {
473 if (!path.empty() && fs::exists(path)) {
474 lib_paths.push_back(path);
479 std::vector<std::string> default_paths = {
480#if defined(MAYAFLUX_PLATFORM_MACOS) && (defined(__aarch64__) || defined(_M_ARM64))
491 for (
const auto& path : default_paths) {
492 if (fs::exists(path)) {
493 lib_paths.push_back(path);
500#ifdef MAYAFLUX_PLATFORM_MACOS
502std::string SystemConfig::get_macos_sdk_path()
504 const char* cmd =
"xcrun --show-sdk-path 2>/dev/null";
508 if (!sdk_path.empty() && fs::exists(sdk_path)) {
512 std::vector<std::string> possible_paths = {
513 "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk",
514 "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk"
517 for (
const auto& path : possible_paths) {
518 if (fs::exists(path)) {
526std::string SystemConfig::get_xcode_system_includes()
528 std::string sdk_path = get_macos_sdk_path();
529 if (sdk_path.empty()) {
533 std::string include_path = sdk_path +
"/usr/include";
534 if (fs::exists(include_path)) {