MayaFlux 0.2.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
HostEnvironment.cpp
Go to the documentation of this file.
1#include "HostEnvironment.hpp"
2
4
5namespace {
6
7 const std::vector<std::string> eigen_includes = {
8 "/usr/include/eigen3",
9 "/usr/local/include/eigen3",
10 "/opt/homebrew/include/eigen3"
11 };
12}
13
14std::string safe_getenv(const char* var)
15{
16#ifdef MAYAFLUX_PLATFORM_WINDOWS
17 char* value = nullptr;
18 size_t len = 0;
19 if (_dupenv_s(&value, &len, var) == 0 && value != nullptr) {
20 std::string result(value);
21 free(value);
22 return result;
23 }
24 return "";
25#else
26 const char* value = std::getenv(var);
27 return value ? std::string(value) : "";
28#endif
29}
30
32{
33 static std::string cached_resource_dir;
34
35 if (cached_resource_dir.empty()) {
36 const char* cmd = "clang -print-resource-dir";
37 auto result = exec_command(cmd);
38 trim_output(result);
39
40 if (!result.empty()
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))) {
45 {
46 cached_resource_dir = std::move(result);
47 }
48 }
49 }
50
51 return cached_resource_dir;
52}
53
54const std::vector<std::string>& SystemConfig::get_system_includes()
55{
56 static std::vector<std::string> includes;
57
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());
62
63 auto sdk_includes = get_windows_sdk_includes();
64 includes.insert(includes.end(), sdk_includes.begin(), sdk_includes.end());
65#endif // MAYAFLUX_PLATFORM_WINDOWS
66
67 auto clang_includes = get_clang_includes();
68 includes.insert(includes.end(), clang_includes.begin(), clang_includes.end());
69
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);
74 }
75
76 std::string homebrew_includes = "/opt/homebrew/include";
77 if (fs::exists(homebrew_includes)) {
78 includes.push_back(homebrew_includes);
79 }
80#endif // MAYAFLUX_PLATFORM_MACOS
81 }
82
83 return includes;
84}
85
86const std::vector<std::string>& SystemConfig::get_system_libraries()
87{
88 static std::vector<std::string> lib_paths;
89
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());
94
95 auto sdk_libs = get_windows_sdk_libraries();
96 lib_paths.insert(lib_paths.end(), sdk_libs.begin(), sdk_libs.end());
97#else
98 auto system_libs = get_unix_library_paths();
99 lib_paths.insert(lib_paths.end(), system_libs.begin(), system_libs.end());
100#endif
101 }
102
103 return lib_paths;
104}
105
106const std::string& SystemConfig::find_library(const std::string& library_name)
107{
108 static std::unordered_map<std::string, std::string> cache;
109
110 auto it = cache.find(library_name);
111 if (it != cache.end()) {
112 return it->second;
113 }
114
115 std::string& result = cache[library_name];
116
117 auto lib_paths = get_system_libraries();
118 std::string search_name = format_library_name(library_name);
119
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();
124 break;
125 }
126 }
127
128 return result;
129}
130
131const std::string& SystemConfig::get_dep_includes(const std::string& library_name)
132{
133 static std::unordered_map<std::string, std::string> cache;
134
135 auto it = cache.find(library_name);
136 if (it != cache.end()) {
137 return it->second;
138 }
139
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;
144 } else {
145 for (const auto& path : eigen_includes) {
146 if (std::filesystem::exists(path)) {
147 result = path;
148 break;
149 }
150 }
151 }
152 }
153
154 return result;
155}
156
157// NOLINTNEXTLINE(cert-env33-c)
158std::string SystemConfig::exec_command(const char* cmd)
159{
160 std::array<char, 128> buffer {};
161 std::string result;
162
163 using PipeCloser = int (*)(FILE*);
164#ifdef MAYAFLUX_PLATFORM_WINDOWS
165 std::unique_ptr<FILE, PipeCloser> pipe(_popen(cmd, "r"), _pclose);
166#else
167 std::unique_ptr<FILE, PipeCloser> pipe(popen(cmd, "r"), pclose);
168#endif
169
170 if (!pipe)
171 return "";
172
173 while (fgets(buffer.data(), static_cast<int>(buffer.size()), pipe.get())) {
174 result += buffer.data();
175 }
176 return result;
177}
178
179void SystemConfig::trim_output(std::string& str)
180{
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);
184}
185
186std::string SystemConfig::format_library_name(const std::string& library_name)
187{
188#ifdef MAYAFLUX_PLATFORM_WINDOWS
189 if (library_name.find(".lib") == std::string::npos) {
190 return library_name + ".lib";
191 }
192#else
193 if (library_name.find(".a") == std::string::npos && library_name.find(".so") == std::string::npos) {
194 return "lib" + library_name + ".a";
195 }
196#endif
197 return library_name;
198}
199
200std::vector<std::string> SystemConfig::get_clang_includes()
201{
202#ifdef MAYAFLUX_PLATFORM_WINDOWS
203 const char* cmd = "echo. | clang -v -E -x c++ - 2>&1";
204#else
205 const char* cmd = "clang -v -E -x c++ - 2>&1 < /dev/null";
206#endif
207
208 auto clang_output = exec_command(cmd);
209 return parse_clang_search_paths(clang_output);
210}
211
212std::vector<std::string> SystemConfig::parse_clang_search_paths(const std::string& output)
213{
214 std::vector<std::string> paths;
215 bool in_search_paths = false;
216
217 std::istringstream stream(output);
218 std::string line;
219
220 while (std::getline(stream, line)) {
221 if (line.find("#include <...> search starts here:") != std::string::npos) {
222 in_search_paths = true;
223 continue;
224 }
225 if (line.find("End of search list.") != std::string::npos) {
226 break;
227 }
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);
233 if (!path.empty()) {
234 paths.push_back(path);
235 }
236 }
237 }
238 }
239 return paths;
240}
241
242std::string SystemConfig::find_latest_sdk_version(const fs::path& base)
243{
244 if (!fs::exists(base)) {
245 return "";
246 }
247
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;
255 }
256 }
257 }
258 }
259 return latest_version;
260}
261
262#ifdef MAYAFLUX_PLATFORM_WINDOWS
263
264std::string SystemConfig::find_latest_vs_installation()
265{
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"
269 };
270
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";
274 std::string vs_path = exec_command(cmd.c_str());
275 if (!vs_path.empty()) {
276 trim_output(vs_path);
277 if (fs::exists(vs_path)) {
278 return vs_path;
279 }
280 }
281 }
282 }
283
284 return "";
285}
286
287std::string SystemConfig::find_latest_msvc_version(const fs::path& msvc_base)
288{
289 if (!fs::exists(msvc_base)) {
290 return "";
291 }
292
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;
300 }
301 }
302 }
303 }
304 return latest_version;
305}
306
307std::vector<std::string> SystemConfig::get_msvc_includes()
308{
309 std::vector<std::string> includes;
310
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);
315
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());
320 }
321 }
322 }
323
324 if (includes.empty()) {
325 std::string vc_dir = safe_getenv("VCINSTALLDIR");
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());
330 }
331 }
332 }
333
334 return includes;
335}
336
337std::vector<std::string> SystemConfig::get_msvc_libraries()
338{
339 std::vector<std::string> lib_paths;
340
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);
345
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());
350 }
351 }
352 }
353
354 if (lib_paths.empty()) {
355 std::string vc_dir = safe_getenv("VCINSTALLDIR");
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());
360 }
361 }
362 }
363
364 return lib_paths;
365}
366
367std::vector<std::string> SystemConfig::get_windows_sdk_includes()
368{
369 std::vector<std::string> includes;
370
371 std::string sdk_dir = safe_getenv("WindowsSdkDir");
372 std::string sdk_ver = safe_getenv("WindowsSDKVersion");
373
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() == '\\') {
378 version.pop_back();
379 }
380
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());
386 }
387 }
388 }
389
390 if (includes.empty()) {
391 includes = probe_sdk_paths("Include",
392 std::vector<std::string> { "ucrt", "shared", "um", "winrt", "cppwinrt" });
393 }
394
395 return includes;
396}
397
398std::vector<std::string> SystemConfig::get_windows_sdk_libraries()
399{
400 std::vector<std::string> lib_paths;
401
402 std::string sdk_dir = safe_getenv("WindowsSdkDir");
403 std::string sdk_ver = safe_getenv("WindowsSDKVersion");
404
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() == '\\') {
409 version.pop_back();
410 }
411
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());
417 }
418 }
419 }
420
421 if (lib_paths.empty()) {
422 lib_paths = probe_sdk_paths("Lib",
423 std::vector<std::string> { "ucrt", "um" }, "x64");
424 }
425
426 return lib_paths;
427}
428
429std::vector<std::string> SystemConfig::probe_sdk_paths(const std::string& subpath,
430 const std::vector<std::string>& subdirs,
431 const std::string& arch)
432{
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"
437 };
438
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;
442 std::string version = find_latest_sdk_version(search_dir);
443
444 if (!version.empty()) {
445 for (const auto& subdir : subdirs) {
446 fs::path final_path = fs::path(base_path) / subpath / version / subdir;
447 if (!arch.empty()) {
448 final_path = final_path / arch;
449 }
450 if (fs::exists(final_path)) {
451 paths.push_back(final_path.string());
452 }
453 }
454 break;
455 }
456 }
457 }
458
459 return paths;
460}
461
462#else
463
464std::vector<std::string> SystemConfig::get_unix_library_paths()
465{
466 std::vector<std::string> lib_paths;
467
468 std::string ld_library_path = safe_getenv("LD_LIBRARY_PATH");
469 if (!ld_library_path.empty()) {
470 std::istringstream stream(ld_library_path);
471 std::string path;
472 while (std::getline(stream, path, ':')) {
473 if (!path.empty() && fs::exists(path)) {
474 lib_paths.push_back(path);
475 }
476 }
477 }
478
479 std::vector<std::string> default_paths = {
480#if defined(MAYAFLUX_PLATFORM_MACOS) && (defined(__aarch64__) || defined(_M_ARM64))
481 "/opt/homebrew/lib",
482#endif
483 "/usr/local/lib",
484 "/usr/lib",
485 "/lib",
486 "/usr/local/lib64",
487 "/usr/lib64",
488 "/lib64"
489 };
490
491 for (const auto& path : default_paths) {
492 if (fs::exists(path)) {
493 lib_paths.push_back(path);
494 }
495 }
496
497 return lib_paths;
498}
499
500#ifdef MAYAFLUX_PLATFORM_MACOS
501
502std::string SystemConfig::get_macos_sdk_path()
503{
504 const char* cmd = "xcrun --show-sdk-path 2>/dev/null";
505 std::string sdk_path = exec_command(cmd);
506 trim_output(sdk_path);
507
508 if (!sdk_path.empty() && fs::exists(sdk_path)) {
509 return sdk_path;
510 }
511
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"
515 };
516
517 for (const auto& path : possible_paths) {
518 if (fs::exists(path)) {
519 return path;
520 }
521 }
522
523 return "";
524}
525
526std::string SystemConfig::get_xcode_system_includes()
527{
528 std::string sdk_path = get_macos_sdk_path();
529 if (sdk_path.empty()) {
530 return "";
531 }
532
533 std::string include_path = sdk_path + "/usr/include";
534 if (fs::exists(include_path)) {
535 return include_path;
536 }
537
538 return "";
539}
540
541#endif // MAYAFLUX_PLATFORM_MACOS
542
543#endif // MAYAFLUX_PLATFORM_WINDOWS
544}
static const std::string & get_clang_resource_dir()
static const std::vector< std::string > & get_system_libraries()
static std::vector< std::string > get_unix_library_paths()
static std::string find_latest_sdk_version(const fs::path &base)
static const std::string & find_library(const std::string &library_name)
static std::vector< std::string > parse_clang_search_paths(const std::string &output)
static void trim_output(std::string &str)
static const std::string & get_dep_includes(const std::string &library_name)
static std::string exec_command(const char *cmd)
static const std::vector< std::string > & get_system_includes()
static std::vector< std::string > get_clang_includes()
static std::string format_library_name(const std::string &library_name)
std::string safe_getenv(const char *var)