MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
HostEnvironment.cpp
Go to the documentation of this file.
1#include "HostEnvironment.hpp"
2
3namespace MayaFlux::Config {
4
5namespace {
6 std::string s_project_root;
7}
8
9std::string project_root()
10{
11 return s_project_root.empty() ? std::string(SOURCE_DIR) : s_project_root;
12}
13
14void set_project_root(std::string path)
15{
16 s_project_root = std::move(path);
17}
18
19}
20
22
23namespace {
24
25 const std::vector<std::string> eigen_includes = {
26 "/usr/include/eigen3",
27 "/usr/local/include/eigen3",
28 "/opt/homebrew/include/eigen3"
29 };
30
31 const std::vector<std::string> freetype_includes = {
32 "/usr/include/freetype2",
33 "/usr/local/include/freetype2",
34 "/opt/homebrew/include/freetype2"
35 };
36}
37
38std::string safe_getenv(const char* var)
39{
40#ifdef MAYAFLUX_PLATFORM_WINDOWS
41 char* value = nullptr;
42 size_t len = 0;
43 if (_dupenv_s(&value, &len, var) == 0 && value != nullptr) {
44 std::string result(value);
45 free(value);
46 return result;
47 }
48 return "";
49#else
50 const char* value = std::getenv(var);
51 return value ? std::string(value) : "";
52#endif
53}
54
56{
57 static std::string cached_resource_dir;
58
59 if (cached_resource_dir.empty()) {
60 const char* cmd = "clang -print-resource-dir";
61 auto result = exec_command(cmd);
62 trim_output(result);
63
64 if (!result.empty()
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))) {
69 {
70 cached_resource_dir = std::move(result);
71 }
72 }
73 }
74
75 return cached_resource_dir;
76}
77
78const std::vector<std::string>& SystemConfig::get_system_includes()
79{
80 static std::vector<std::string> includes;
81
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());
86
87 auto sdk_includes = get_windows_sdk_includes();
88 includes.insert(includes.end(), sdk_includes.begin(), sdk_includes.end());
89#endif // MAYAFLUX_PLATFORM_WINDOWS
90
91 auto clang_includes = get_clang_includes();
92 includes.insert(includes.end(), clang_includes.begin(), clang_includes.end());
93
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);
98 }
99
100 std::string homebrew_includes = "/opt/homebrew/include";
101 if (fs::exists(homebrew_includes)) {
102 includes.push_back(homebrew_includes);
103 }
104#endif // MAYAFLUX_PLATFORM_MACOS
105 }
106
107 return includes;
108}
109
110const std::vector<std::string>& SystemConfig::get_system_libraries()
111{
112 static std::vector<std::string> lib_paths;
113
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());
118
119 auto sdk_libs = get_windows_sdk_libraries();
120 lib_paths.insert(lib_paths.end(), sdk_libs.begin(), sdk_libs.end());
121#else
122 auto system_libs = get_unix_library_paths();
123 lib_paths.insert(lib_paths.end(), system_libs.begin(), system_libs.end());
124#endif
125 }
126
127 return lib_paths;
128}
129
130const std::string& SystemConfig::find_dep_library(const std::string& library_name, const std::string& prefix)
131{
132 static std::unordered_map<std::string, std::string> cache;
133
134 auto it = cache.find(library_name);
135 if (it != cache.end()) {
136 return it->second;
137 }
138
139 std::string& result = cache[library_name];
140 std::string search_name = format_library_name(library_name, LibraryType::Shared);
141
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();
147 return result;
148 }
149 }
150
151 for (const auto& lib_path : get_system_libraries()) {
152 fs::path candidate = fs::path(lib_path) / search_name;
153 if (fs::exists(candidate)) {
154 result = candidate.string();
155 return result;
156 }
157 }
158
159 return result;
160}
161
162const std::string& SystemConfig::get_dep_includes(const std::string& library_name)
163{
164 static std::unordered_map<std::string, std::string> cache;
165
166 auto it = cache.find(library_name);
167 if (it != cache.end()) {
168 return it->second;
169 }
170
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;
175 } else {
176 for (const auto& path : eigen_includes) {
177 if (std::filesystem::exists(path)) {
178 result = path;
179 break;
180 }
181 }
182 }
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;
186 } else {
187 for (const auto& path : freetype_includes) {
188 if (std::filesystem::exists(path)) {
189 result = path;
190 break;
191 }
192 }
193 }
194 }
195
196 return result;
197}
198
199// NOLINTNEXTLINE(cert-env33-c)
200std::string SystemConfig::exec_command(const char* cmd)
201{
202 std::array<char, 128> buffer {};
203 std::string result;
204
205 using PipeCloser = int (*)(FILE*);
206#ifdef MAYAFLUX_PLATFORM_WINDOWS
207 std::unique_ptr<FILE, PipeCloser> pipe(_popen(cmd, "r"), _pclose);
208#else
209 std::unique_ptr<FILE, PipeCloser> pipe(popen(cmd, "r"), pclose);
210#endif
211
212 if (!pipe)
213 return "";
214
215 while (fgets(buffer.data(), static_cast<int>(buffer.size()), pipe.get())) {
216 result += buffer.data();
217 }
218 return result;
219}
220
221void SystemConfig::trim_output(std::string& str)
222{
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);
226}
227
228std::string SystemConfig::format_library_name(const std::string& library_name, LibraryType type)
229{
230#ifdef MAYAFLUX_PLATFORM_WINDOWS
231 const std::string ext = (type == LibraryType::Shared) ? ".dll" : ".lib";
232 if (library_name.find(ext) == std::string::npos)
233 return library_name + ext;
234#elif defined(MAYAFLUX_PLATFORM_MACOS)
235 const std::string ext = (type == LibraryType::Shared) ? ".dylib" : ".a";
236 if (library_name.find(ext) == std::string::npos && library_name.find("lib") != 0)
237 return "lib" + library_name + ext;
238#else
239 const std::string ext = (type == LibraryType::Shared) ? ".so" : ".a";
240 if (library_name.find(ext) == std::string::npos && library_name.find(".a") == std::string::npos)
241 return "lib" + library_name + ext;
242#endif
243 return library_name;
244}
245
246std::vector<std::string> SystemConfig::get_clang_includes()
247{
248#ifdef MAYAFLUX_PLATFORM_WINDOWS
249 const char* cmd = "echo. | clang -v -E -x c++ - 2>&1";
250#else
251 const char* cmd = "clang -v -E -x c++ - 2>&1 < /dev/null";
252#endif
253
254 auto clang_output = exec_command(cmd);
255 return parse_clang_search_paths(clang_output);
256}
257
258std::vector<std::string> SystemConfig::parse_clang_search_paths(const std::string& output)
259{
260 std::vector<std::string> paths;
261 bool in_search_paths = false;
262
263 std::istringstream stream(output);
264 std::string line;
265
266 while (std::getline(stream, line)) {
267 if (line.find("#include <...> search starts here:") != std::string::npos) {
268 in_search_paths = true;
269 continue;
270 }
271 if (line.find("End of search list.") != std::string::npos) {
272 break;
273 }
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);
279 if (!path.empty()) {
280 paths.push_back(path);
281 }
282 }
283 }
284 }
285 return paths;
286}
287
288std::string SystemConfig::find_latest_sdk_version(const fs::path& base)
289{
290 if (!fs::exists(base)) {
291 return "";
292 }
293
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;
301 }
302 }
303 }
304 }
305 return latest_version;
306}
307
308#ifdef MAYAFLUX_PLATFORM_WINDOWS
309
310std::string SystemConfig::find_latest_vs_installation()
311{
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"
315 };
316
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";
320 std::string vs_path = exec_command(cmd.c_str());
321 if (!vs_path.empty()) {
322 trim_output(vs_path);
323 if (fs::exists(vs_path)) {
324 return vs_path;
325 }
326 }
327 }
328 }
329
330 return "";
331}
332
333std::string SystemConfig::find_latest_msvc_version(const fs::path& msvc_base)
334{
335 if (!fs::exists(msvc_base)) {
336 return "";
337 }
338
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;
346 }
347 }
348 }
349 }
350 return latest_version;
351}
352
353std::vector<std::string> SystemConfig::get_msvc_includes()
354{
355 std::vector<std::string> includes;
356
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);
361
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());
366 }
367 }
368 }
369
370 if (includes.empty()) {
371 std::string vc_dir = safe_getenv("VCINSTALLDIR");
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());
376 }
377 }
378 }
379
380 return includes;
381}
382
383std::vector<std::string> SystemConfig::get_msvc_libraries()
384{
385 std::vector<std::string> lib_paths;
386
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);
391
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());
396 }
397 }
398 }
399
400 if (lib_paths.empty()) {
401 std::string vc_dir = safe_getenv("VCINSTALLDIR");
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());
406 }
407 }
408 }
409
410 return lib_paths;
411}
412
413std::vector<std::string> SystemConfig::get_windows_sdk_includes()
414{
415 std::vector<std::string> includes;
416
417 std::string sdk_dir = safe_getenv("WindowsSdkDir");
418 std::string sdk_ver = safe_getenv("WindowsSDKVersion");
419
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() == '\\') {
424 version.pop_back();
425 }
426
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());
432 }
433 }
434 }
435
436 if (includes.empty()) {
437 includes = probe_sdk_paths("Include",
438 std::vector<std::string> { "ucrt", "shared", "um", "winrt", "cppwinrt" });
439 }
440
441 return includes;
442}
443
444std::vector<std::string> SystemConfig::get_windows_sdk_libraries()
445{
446 std::vector<std::string> lib_paths;
447
448 std::string sdk_dir = safe_getenv("WindowsSdkDir");
449 std::string sdk_ver = safe_getenv("WindowsSDKVersion");
450
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() == '\\') {
455 version.pop_back();
456 }
457
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());
463 }
464 }
465 }
466
467 if (lib_paths.empty()) {
468 lib_paths = probe_sdk_paths("Lib",
469 std::vector<std::string> { "ucrt", "um" }, "x64");
470 }
471
472 return lib_paths;
473}
474
475std::vector<std::string> SystemConfig::probe_sdk_paths(const std::string& subpath,
476 const std::vector<std::string>& subdirs,
477 const std::string& arch)
478{
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"
483 };
484
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;
488 std::string version = find_latest_sdk_version(search_dir);
489
490 if (!version.empty()) {
491 for (const auto& subdir : subdirs) {
492 fs::path final_path = fs::path(base_path) / subpath / version / subdir;
493 if (!arch.empty()) {
494 final_path = final_path / arch;
495 }
496 if (fs::exists(final_path)) {
497 paths.push_back(final_path.string());
498 }
499 }
500 break;
501 }
502 }
503 }
504
505 return paths;
506}
507
508#else
509
510std::vector<std::string> SystemConfig::get_unix_library_paths()
511{
512 std::vector<std::string> lib_paths;
513
514 std::string ld_library_path = safe_getenv("LD_LIBRARY_PATH");
515 if (!ld_library_path.empty()) {
516 std::istringstream stream(ld_library_path);
517 std::string path;
518 while (std::getline(stream, path, ':')) {
519 if (!path.empty() && fs::exists(path)) {
520 lib_paths.push_back(path);
521 }
522 }
523 }
524
525 std::vector<std::string> default_paths = {
526#if defined(MAYAFLUX_PLATFORM_MACOS)
527 "/opt/homebrew/lib",
528#endif
529 "/usr/local/lib",
530 "/usr/lib",
531 "/lib",
532 "/usr/local/lib64",
533 "/usr/lib64",
534 "/lib64"
535 };
536
537 for (const auto& path : default_paths) {
538 if (fs::exists(path)) {
539 lib_paths.push_back(path);
540 }
541 }
542
543 return lib_paths;
544}
545
546#ifdef MAYAFLUX_PLATFORM_MACOS
547
548std::string SystemConfig::get_macos_sdk_path()
549{
550 const char* cmd = "xcrun --show-sdk-path 2>/dev/null";
551 std::string sdk_path = exec_command(cmd);
552 trim_output(sdk_path);
553
554 if (!sdk_path.empty() && fs::exists(sdk_path)) {
555 return sdk_path;
556 }
557
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"
561 };
562
563 for (const auto& path : possible_paths) {
564 if (fs::exists(path)) {
565 return path;
566 }
567 }
568
569 return "";
570}
571
572std::string SystemConfig::get_xcode_system_includes()
573{
574 std::string sdk_path = get_macos_sdk_path();
575 if (sdk_path.empty()) {
576 return "";
577 }
578
579 std::string include_path = sdk_path + "/usr/include";
580 if (fs::exists(include_path)) {
581 return include_path;
582 }
583
584 return "";
585}
586
587#endif // MAYAFLUX_PLATFORM_MACOS
588
589#endif // MAYAFLUX_PLATFORM_WINDOWS
590}
vk::CommandBuffer cmd
Core::GlobalStreamInfo stream
Definition Config.cpp:40
std::shared_ptr< NetworkGeometryBuffer > buffer
glm::vec3 value
vk::PhysicalDeviceType type
Definition VKDevice.cpp:126
std::string name
Definition VKDevice.cpp:123
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 project_root()
void set_project_root(std::string path)
Globlal configuration for MayaFlux.
Definition Config.cpp:64
std::string safe_getenv(const char *var)