MayaFlux 0.4.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 const std::vector<std::string> freetype_includes = {
14 "/usr/include/freetype2",
15 "/usr/local/include/freetype2",
16 "/opt/homebrew/include/freetype2"
17 };
18}
19
20std::string safe_getenv(const char* var)
21{
22#ifdef MAYAFLUX_PLATFORM_WINDOWS
23 char* value = nullptr;
24 size_t len = 0;
25 if (_dupenv_s(&value, &len, var) == 0 && value != nullptr) {
26 std::string result(value);
27 free(value);
28 return result;
29 }
30 return "";
31#else
32 const char* value = std::getenv(var);
33 return value ? std::string(value) : "";
34#endif
35}
36
38{
39 static std::string cached_resource_dir;
40
41 if (cached_resource_dir.empty()) {
42 const char* cmd = "clang -print-resource-dir";
43 auto result = exec_command(cmd);
44 trim_output(result);
45
46 if (!result.empty()
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))) {
51 {
52 cached_resource_dir = std::move(result);
53 }
54 }
55 }
56
57 return cached_resource_dir;
58}
59
60const std::vector<std::string>& SystemConfig::get_system_includes()
61{
62 static std::vector<std::string> includes;
63
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());
68
69 auto sdk_includes = get_windows_sdk_includes();
70 includes.insert(includes.end(), sdk_includes.begin(), sdk_includes.end());
71#endif // MAYAFLUX_PLATFORM_WINDOWS
72
73 auto clang_includes = get_clang_includes();
74 includes.insert(includes.end(), clang_includes.begin(), clang_includes.end());
75
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);
80 }
81
82 std::string homebrew_includes = "/opt/homebrew/include";
83 if (fs::exists(homebrew_includes)) {
84 includes.push_back(homebrew_includes);
85 }
86#endif // MAYAFLUX_PLATFORM_MACOS
87 }
88
89 return includes;
90}
91
92const std::vector<std::string>& SystemConfig::get_system_libraries()
93{
94 static std::vector<std::string> lib_paths;
95
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());
100
101 auto sdk_libs = get_windows_sdk_libraries();
102 lib_paths.insert(lib_paths.end(), sdk_libs.begin(), sdk_libs.end());
103#else
104 auto system_libs = get_unix_library_paths();
105 lib_paths.insert(lib_paths.end(), system_libs.begin(), system_libs.end());
106#endif
107 }
108
109 return lib_paths;
110}
111
112const std::string& SystemConfig::find_dep_library(const std::string& library_name, const std::string& prefix)
113{
114 static std::unordered_map<std::string, std::string> cache;
115
116 auto it = cache.find(library_name);
117 if (it != cache.end()) {
118 return it->second;
119 }
120
121 std::string& result = cache[library_name];
122 std::string search_name = format_library_name(library_name, LibraryType::Shared);
123
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();
129 return result;
130 }
131 }
132
133 for (const auto& lib_path : get_system_libraries()) {
134 fs::path candidate = fs::path(lib_path) / search_name;
135 if (fs::exists(candidate)) {
136 result = candidate.string();
137 return result;
138 }
139 }
140
141 return result;
142}
143
144const std::string& SystemConfig::get_dep_includes(const std::string& library_name)
145{
146 static std::unordered_map<std::string, std::string> cache;
147
148 auto it = cache.find(library_name);
149 if (it != cache.end()) {
150 return it->second;
151 }
152
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;
157 } else {
158 for (const auto& path : eigen_includes) {
159 if (std::filesystem::exists(path)) {
160 result = path;
161 break;
162 }
163 }
164 }
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;
168 } else {
169 for (const auto& path : freetype_includes) {
170 if (std::filesystem::exists(path)) {
171 result = path;
172 break;
173 }
174 }
175 }
176 }
177
178 return result;
179}
180
181// NOLINTNEXTLINE(cert-env33-c)
182std::string SystemConfig::exec_command(const char* cmd)
183{
184 std::array<char, 128> buffer {};
185 std::string result;
186
187 using PipeCloser = int (*)(FILE*);
188#ifdef MAYAFLUX_PLATFORM_WINDOWS
189 std::unique_ptr<FILE, PipeCloser> pipe(_popen(cmd, "r"), _pclose);
190#else
191 std::unique_ptr<FILE, PipeCloser> pipe(popen(cmd, "r"), pclose);
192#endif
193
194 if (!pipe)
195 return "";
196
197 while (fgets(buffer.data(), static_cast<int>(buffer.size()), pipe.get())) {
198 result += buffer.data();
199 }
200 return result;
201}
202
203void SystemConfig::trim_output(std::string& str)
204{
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);
208}
209
210std::string SystemConfig::format_library_name(const std::string& library_name, LibraryType type)
211{
212#ifdef MAYAFLUX_PLATFORM_WINDOWS
213 const std::string ext = (type == LibraryType::Shared) ? ".dll" : ".lib";
214 if (library_name.find(ext) == std::string::npos)
215 return library_name + ext;
216#elif defined(MAYAFLUX_PLATFORM_MACOS)
217 const std::string ext = (type == LibraryType::Shared) ? ".dylib" : ".a";
218 if (library_name.find(ext) == std::string::npos && library_name.find("lib") != 0)
219 return "lib" + library_name + ext;
220#else
221 const std::string ext = (type == LibraryType::Shared) ? ".so" : ".a";
222 if (library_name.find(ext) == std::string::npos && library_name.find(".a") == std::string::npos)
223 return "lib" + library_name + ext;
224#endif
225 return library_name;
226}
227
228std::vector<std::string> SystemConfig::get_clang_includes()
229{
230#ifdef MAYAFLUX_PLATFORM_WINDOWS
231 const char* cmd = "echo. | clang -v -E -x c++ - 2>&1";
232#else
233 const char* cmd = "clang -v -E -x c++ - 2>&1 < /dev/null";
234#endif
235
236 auto clang_output = exec_command(cmd);
237 return parse_clang_search_paths(clang_output);
238}
239
240std::vector<std::string> SystemConfig::parse_clang_search_paths(const std::string& output)
241{
242 std::vector<std::string> paths;
243 bool in_search_paths = false;
244
245 std::istringstream stream(output);
246 std::string line;
247
248 while (std::getline(stream, line)) {
249 if (line.find("#include <...> search starts here:") != std::string::npos) {
250 in_search_paths = true;
251 continue;
252 }
253 if (line.find("End of search list.") != std::string::npos) {
254 break;
255 }
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);
261 if (!path.empty()) {
262 paths.push_back(path);
263 }
264 }
265 }
266 }
267 return paths;
268}
269
270std::string SystemConfig::find_latest_sdk_version(const fs::path& base)
271{
272 if (!fs::exists(base)) {
273 return "";
274 }
275
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;
283 }
284 }
285 }
286 }
287 return latest_version;
288}
289
290#ifdef MAYAFLUX_PLATFORM_WINDOWS
291
292std::string SystemConfig::find_latest_vs_installation()
293{
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"
297 };
298
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";
302 std::string vs_path = exec_command(cmd.c_str());
303 if (!vs_path.empty()) {
304 trim_output(vs_path);
305 if (fs::exists(vs_path)) {
306 return vs_path;
307 }
308 }
309 }
310 }
311
312 return "";
313}
314
315std::string SystemConfig::find_latest_msvc_version(const fs::path& msvc_base)
316{
317 if (!fs::exists(msvc_base)) {
318 return "";
319 }
320
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;
328 }
329 }
330 }
331 }
332 return latest_version;
333}
334
335std::vector<std::string> SystemConfig::get_msvc_includes()
336{
337 std::vector<std::string> includes;
338
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);
343
344 if (!version.empty()) {
345 fs::path include_path = msvc_base / version / "include";
346 if (fs::exists(include_path)) {
347 includes.push_back(include_path.string());
348 }
349 }
350 }
351
352 if (includes.empty()) {
353 std::string vc_dir = safe_getenv("VCINSTALLDIR");
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());
358 }
359 }
360 }
361
362 return includes;
363}
364
365std::vector<std::string> SystemConfig::get_msvc_libraries()
366{
367 std::vector<std::string> lib_paths;
368
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);
373
374 if (!version.empty()) {
375 fs::path lib_path = msvc_base / version / "lib" / "x64";
376 if (fs::exists(lib_path)) {
377 lib_paths.push_back(lib_path.string());
378 }
379 }
380 }
381
382 if (lib_paths.empty()) {
383 std::string vc_dir = safe_getenv("VCINSTALLDIR");
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());
388 }
389 }
390 }
391
392 return lib_paths;
393}
394
395std::vector<std::string> SystemConfig::get_windows_sdk_includes()
396{
397 std::vector<std::string> includes;
398
399 std::string sdk_dir = safe_getenv("WindowsSdkDir");
400 std::string sdk_ver = safe_getenv("WindowsSDKVersion");
401
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);
405 if (!version.empty() && version.back() == '\\') {
406 version.pop_back();
407 }
408
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());
414 }
415 }
416 }
417
418 if (includes.empty()) {
419 includes = probe_sdk_paths("Include",
420 std::vector<std::string> { "ucrt", "shared", "um", "winrt", "cppwinrt" });
421 }
422
423 return includes;
424}
425
426std::vector<std::string> SystemConfig::get_windows_sdk_libraries()
427{
428 std::vector<std::string> lib_paths;
429
430 std::string sdk_dir = safe_getenv("WindowsSdkDir");
431 std::string sdk_ver = safe_getenv("WindowsSDKVersion");
432
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);
436 if (!version.empty() && version.back() == '\\') {
437 version.pop_back();
438 }
439
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());
445 }
446 }
447 }
448
449 if (lib_paths.empty()) {
450 lib_paths = probe_sdk_paths("Lib",
451 std::vector<std::string> { "ucrt", "um" }, "x64");
452 }
453
454 return lib_paths;
455}
456
457std::vector<std::string> SystemConfig::probe_sdk_paths(const std::string& subpath,
458 const std::vector<std::string>& subdirs,
459 const std::string& arch)
460{
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"
465 };
466
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;
470 std::string version = find_latest_sdk_version(search_dir);
471
472 if (!version.empty()) {
473 for (const auto& subdir : subdirs) {
474 fs::path final_path = fs::path(base_path) / subpath / version / subdir;
475 if (!arch.empty()) {
476 final_path = final_path / arch;
477 }
478 if (fs::exists(final_path)) {
479 paths.push_back(final_path.string());
480 }
481 }
482 break;
483 }
484 }
485 }
486
487 return paths;
488}
489
490#else
491
492std::vector<std::string> SystemConfig::get_unix_library_paths()
493{
494 std::vector<std::string> lib_paths;
495
496 std::string ld_library_path = safe_getenv("LD_LIBRARY_PATH");
497 if (!ld_library_path.empty()) {
498 std::istringstream stream(ld_library_path);
499 std::string path;
500 while (std::getline(stream, path, ':')) {
501 if (!path.empty() && fs::exists(path)) {
502 lib_paths.push_back(path);
503 }
504 }
505 }
506
507 std::vector<std::string> default_paths = {
508#if defined(MAYAFLUX_PLATFORM_MACOS) && (defined(__aarch64__) || defined(_M_ARM64))
509 "/opt/homebrew/lib",
510#endif
511 "/usr/local/lib",
512 "/usr/lib",
513 "/lib",
514 "/usr/local/lib64",
515 "/usr/lib64",
516 "/lib64"
517 };
518
519 for (const auto& path : default_paths) {
520 if (fs::exists(path)) {
521 lib_paths.push_back(path);
522 }
523 }
524
525 return lib_paths;
526}
527
528#ifdef MAYAFLUX_PLATFORM_MACOS
529
530std::string SystemConfig::get_macos_sdk_path()
531{
532 const char* cmd = "xcrun --show-sdk-path 2>/dev/null";
533 std::string sdk_path = exec_command(cmd);
534 trim_output(sdk_path);
535
536 if (!sdk_path.empty() && fs::exists(sdk_path)) {
537 return sdk_path;
538 }
539
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"
543 };
544
545 for (const auto& path : possible_paths) {
546 if (fs::exists(path)) {
547 return path;
548 }
549 }
550
551 return "";
552}
553
554std::string SystemConfig::get_xcode_system_includes()
555{
556 std::string sdk_path = get_macos_sdk_path();
557 if (sdk_path.empty()) {
558 return "";
559 }
560
561 std::string include_path = sdk_path + "/usr/include";
562 if (fs::exists(include_path)) {
563 return include_path;
564 }
565
566 return "";
567}
568
569#endif // MAYAFLUX_PLATFORM_MACOS
570
571#endif // MAYAFLUX_PLATFORM_WINDOWS
572}
vk::CommandBuffer cmd
uint32_t version
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_dep_library(const std::string &library_name, const std::string &prefix="")
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 std::string format_library_name(const std::string &library_name, LibraryType type=LibraryType::Static)
static const std::vector< std::string > & get_system_includes()
static std::vector< std::string > get_clang_includes()
std::string safe_getenv(const char *var)