MayaFlux 0.3.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
ClangInterpreter.cpp
Go to the documentation of this file.
2
3#include "Commentator.hpp"
4
6
7#ifdef MAYAFLUX_COMPILER_MSVC
8#pragma warning(push)
9#pragma warning(disable : 4291) // no matching operator delete found
10#pragma warning(disable : 4805) // unsafe mix of type 'unsigned int' and type 'bool' in operation
11#endif
12
13#include <llvm/ExecutionEngine/Orc/LLJIT.h>
14#include <llvm/ExecutionEngine/Orc/ThreadSafeModule.h>
15
16#include <llvm/Support/TargetSelect.h>
17#include <llvm/TargetParser/Host.h>
18
19#include <clang/AST/Type.h>
20#include <clang/Frontend/CompilerInstance.h>
21#include <clang/Interpreter/Interpreter.h>
22
23#ifdef MAYAFLUX_COMPILER_MSVC
24#pragma warning(pop)
25#endif
26
27#ifdef MAYAFLUX_PLATFORM_MACOS
28#include <dispatch/dispatch.h>
29#endif
30
31#ifdef MAYAFLUX_PLATFORM_WINDOWS
33#endif
34
35namespace Lila {
36
38 std::unique_ptr<clang::Interpreter> interpreter;
39
40 std::vector<std::string> include_paths;
41 std::vector<std::string> compile_flags;
42 std::string target_triple;
43
44 std::unordered_map<std::string, void*> symbol_table;
45 int eval_counter = 0;
46};
47
49 : m_impl(std::make_unique<Impl>())
50{
51 m_impl->target_triple = llvm::sys::getDefaultTargetTriple();
52}
53
58
60ClangInterpreter& ClangInterpreter::operator=(ClangInterpreter&&) noexcept = default;
61
63{
64 LILA_INFO(Emitter::INTERPRETER, "Initializing Clang interpreter");
65
66#ifdef MAYAFLUX_PLATFORM_WINDOWS
67 llvm::sys::DynamicLibrary::LoadLibraryPermanently("msvcp140.dll");
68 llvm::sys::DynamicLibrary::LoadLibraryPermanently("vcruntime140.dll");
69 llvm::sys::DynamicLibrary::LoadLibraryPermanently("ucrtbase.dll");
70 llvm::sys::DynamicLibrary::LoadLibraryPermanently("MayaFluxLib.dll");
71#endif
72
73 llvm::InitializeNativeTarget();
74 llvm::InitializeNativeTargetAsmPrinter();
75 llvm::InitializeNativeTargetAsmParser();
76
77 m_impl->compile_flags.clear();
78 m_impl->compile_flags.emplace_back("-std=c++23");
79 m_impl->compile_flags.emplace_back("-DMAYASIMPLE");
80
81#ifdef MAYAFLUX_PLATFORM_LINUX
82 m_impl->compile_flags.emplace_back("-mcmodel=large");
83 m_impl->compile_flags.emplace_back("-fPIC");
84 m_impl->compile_flags.emplace_back("-fPIE");
85#endif
86
87 std::string pch_dir;
88 if (std::filesystem::exists(MayaFlux::Config::PCH_RUNTIME_PATH)) {
89 pch_dir = MayaFlux::Config::RUNTIME_DATA_DIR;
90 LILA_DEBUG(Emitter::INTERPRETER,
91 std::string("Using installed PCH from: ") + std::string(MayaFlux::Config::PCH_RUNTIME_PATH));
92
93 } else if (std::filesystem::exists(MayaFlux::Config::PCH_SOURCE_PATH)) {
94 pch_dir = std::string(MayaFlux::Config::SOURCE_DIR) + "/cmake";
95 LILA_DEBUG(Emitter::INTERPRETER,
96 std::string("Using source PCH from: ") + std::string(MayaFlux::Config::PCH_SOURCE_PATH));
97
98 } else {
99 m_last_error = "Cannot find pch.h in runtime or source locations";
100 LILA_ERROR(Emitter::INTERPRETER, m_last_error);
101 return false;
102 }
103
104 m_impl->compile_flags.push_back("-I" + pch_dir);
105
106 const std::string& resource_dir = MayaFlux::Platform::SystemConfig::get_clang_resource_dir();
107 if (!resource_dir.empty()) {
108 m_impl->compile_flags.push_back("-resource-dir=" + resource_dir);
109 LILA_DEBUG(Emitter::INTERPRETER,
110 std::string("Using clang resource dir: ") + resource_dir);
111 } else {
112 m_impl->compile_flags.emplace_back("-resource-dir=/usr/lib/clang/21");
113 LILA_WARN(Emitter::INTERPRETER,
114 "Using default clang resource dir: /usr/lib/clang/21");
115 }
116
117 const auto& system_includes = MayaFlux::Platform::SystemConfig::get_system_includes();
118 for (const auto& include : system_includes) {
119 m_impl->compile_flags.push_back("-isystem" + include);
120 }
121
122#ifndef MAYAFLUX_PLATFORM_WINDOWS
123 const auto& eigen_include = MayaFlux::Platform::SystemConfig::get_dep_includes("eigen");
124 if (!eigen_include.empty()) {
125 m_impl->compile_flags.push_back("-isystem" + eigen_include);
126 LILA_DEBUG(Emitter::INTERPRETER,
127 std::string("Added Eigen include path: ") + eigen_include);
128 } else {
129 LILA_WARN(Emitter::INTERPRETER,
130 "Could not find Eigen include path - some features may not work");
131 }
132#endif
133
134#ifdef MAYAFLUX_PLATFORM_MACOS
135 // CRITICAL: JIT uses Homebrew LLVM but needs macOS SDK for system headers
136 // Homebrew LLVM's libc++ requires pthread.h, sched.h, time.h, etc. from SDK
137 std::string sdk_path = MayaFlux::Platform::SystemConfig::get_macos_sdk_path();
138 if (!sdk_path.empty()) {
139 m_impl->compile_flags.push_back("-isysroot" + sdk_path);
140 LILA_DEBUG(Emitter::INTERPRETER, "Using macOS SDK: " + sdk_path);
141 } else {
142 LILA_WARN(Emitter::INTERPRETER,
143 "Could not find macOS SDK - JIT may fail to find system headers");
144 }
145#endif
146
147 for (const auto& path : m_impl->include_paths) {
148 m_impl->compile_flags.push_back("-I" + path);
149 }
150
151#ifdef MAYAFLUX_PLATFORM_WINDOWS
152 m_impl->compile_flags.emplace_back("-fno-function-sections");
153 m_impl->compile_flags.emplace_back("-fno-data-sections");
154 m_impl->compile_flags.emplace_back("-fno-unique-section-names");
155#endif
156
157 std::vector<const char*> args;
158 for (const auto& flag : m_impl->compile_flags) {
159 args.push_back(flag.c_str());
160 }
161
162 clang::IncrementalCompilerBuilder ICB;
163 ICB.SetCompilerArgs(args);
164
165 auto CI = ICB.CreateCpp();
166 if (!CI) {
167 m_last_error = "Failed to create CompilerInstance: " + llvm::toString(CI.takeError());
168 LILA_ERROR(Emitter::INTERPRETER, m_last_error);
169 return false;
170 }
171
172 auto interp = clang::Interpreter::create(std::move(*CI));
173 if (!interp) {
174 m_last_error = "Failed to create interpreter: " + llvm::toString(interp.takeError());
175 LILA_ERROR(Emitter::INTERPRETER, m_last_error);
176 return false;
177 }
178
179 m_impl->interpreter = std::move(*interp);
180
181 LILA_INFO(Emitter::INTERPRETER, "Clang interpreter created successfully");
182
183 auto result = m_impl->interpreter->ParseAndExecute(
184 "#include \"pch.h\"\n"
185 "#include \"Lila/LiveAid.hpp\"\n");
186
187 if (result) {
188 std::string warning = "Failed to load MayaFlux headers: " + llvm::toString(std::move(result));
189 LILA_WARN(Emitter::INTERPRETER, warning);
190 } else {
191 LILA_INFO(Emitter::INTERPRETER, "MayaFlux headers loaded successfully");
192 }
193
194 result = m_impl->interpreter->ParseAndExecute("std::cout << \"Ready for Live\" << std::flush;");
195
196 return true;
197}
198
200{
201#ifdef MAYAFLUX_PLATFORM_MACOS
202 __block EvalResult result;
203#else
204 EvalResult result;
205#endif
206
207 if (!m_impl->interpreter) {
208 result.error = "Interpreter not initialized";
209 LILA_ERROR(Emitter::INTERPRETER, result.error);
210 return result;
211 }
212
213 LILA_DEBUG(Emitter::INTERPRETER, "Evaluating code...");
214
215#ifdef MAYAFLUX_PLATFORM_MACOS
216 dispatch_sync(dispatch_get_main_queue(), ^{
217 auto eval_result = m_impl->interpreter->ParseAndExecute(code);
218
219 if (!eval_result) {
220 result.success = true;
221 LILA_DEBUG(Emitter::INTERPRETER, "Code evaluation succeeded");
222 } else {
223 result.success = false;
224 result.error = "Execution failed: " + llvm::toString(std::move(eval_result));
225 LILA_ERROR(Emitter::INTERPRETER, result.error);
226 }
227 });
228
229#elif defined(MAYAFLUX_PLATFORM_WINDOWS)
230 auto completed = std::make_shared<std::atomic<bool>>(false);
231
232 auto* task = new std::function<void()>([&, completed]() {
233 auto eval_result = m_impl->interpreter->ParseAndExecute(code);
234
235 if (!eval_result) {
236 result.success = true;
237 LILA_DEBUG(Emitter::INTERPRETER, "Code evaluation succeeded");
238 } else {
239 result.success = false;
240 result.error = "Execution failed: " + llvm::toString(std::move(eval_result));
241 LILA_ERROR(Emitter::INTERPRETER, result.error);
242 }
243
244 completed->store(true, std::memory_order_release);
245 });
246
247 PostThreadMessage(MayaFlux::Parallel::g_MainThreadId, MAYAFLUX_WM_DISPATCH, 0, (LPARAM)task);
248
249 while (!completed->load(std::memory_order_acquire)) {
250 std::this_thread::sleep_for(std::chrono::microseconds(100));
251 }
252
253#else
254 auto eval_result = m_impl->interpreter->ParseAndExecute(code);
255
256 if (!eval_result) {
257 result.success = true;
258 LILA_DEBUG(Emitter::INTERPRETER, "Code evaluation succeeded");
259 } else {
260 result.success = false;
261 result.error = "Execution failed: " + llvm::toString(std::move(eval_result));
262 LILA_ERROR(Emitter::INTERPRETER, result.error);
263 }
264#endif
265
266 m_impl->eval_counter++;
267 return result;
268}
269
271{
272 EvalResult result;
273
274 if (!std::filesystem::exists(filepath)) {
275 result.error = "File does not exist: " + filepath;
276 LILA_ERROR(Emitter::INTERPRETER, result.error);
277 return result;
278 }
279
280 LILA_INFO(Emitter::INTERPRETER, std::string("Evaluating file: ") + filepath);
281
282 std::string code = "#include \"" + filepath + "\"\n";
283 return eval(code);
284}
285
286void* ClangInterpreter::get_symbol_address(const std::string& name)
287{
288 if (!m_impl->interpreter) {
289 LILA_WARN(Emitter::INTERPRETER, "Cannot get symbol: interpreter not initialized");
290 return nullptr;
291 }
292
293 auto symbol = m_impl->interpreter->getSymbolAddress(name);
294 if (symbol) {
295 LILA_DEBUG(Emitter::INTERPRETER, std::string("Found symbol: ") + name);
296 return reinterpret_cast<void*>(symbol->getValue());
297 }
298
299 LILA_WARN(Emitter::INTERPRETER, std::string("Symbol not found: ") + name);
300 return nullptr;
301}
302
304{
305 std::vector<std::string> symbols;
306 for (const auto& pair : m_impl->symbol_table) {
307 symbols.push_back(pair.first);
308 }
309 return symbols;
310}
311
312void ClangInterpreter::add_include_path(const std::string& path)
313{
314 if (std::filesystem::exists(path)) {
315 m_impl->include_paths.push_back(path);
316 LILA_DEBUG(Emitter::INTERPRETER, std::string("Added include path: ") + path);
317 } else {
318 LILA_WARN(Emitter::INTERPRETER,
319 std::string("Include path does not exist: ") + path);
320 }
321}
322
323void ClangInterpreter::add_library_path(const std::string& path)
324{
325 LILA_DEBUG(Emitter::INTERPRETER,
326 std::string("Library path noted (not yet implemented): ") + path);
327}
328
329void ClangInterpreter::add_compile_flag(const std::string& flag)
330{
331 m_impl->compile_flags.push_back(flag);
332 LILA_DEBUG(Emitter::INTERPRETER, std::string("Added compile flag: ") + flag);
333}
334
335void ClangInterpreter::set_target_triple(const std::string& triple)
336{
337 m_impl->target_triple = triple;
338 LILA_INFO(Emitter::INTERPRETER, std::string("Target triple set to: ") + triple);
339}
340
342{
343 LILA_INFO(Emitter::INTERPRETER, "Resetting interpreter");
344 shutdown();
345 m_impl = std::make_unique<Impl>();
346}
347
349{
350 if (m_impl->interpreter) {
351 LILA_INFO(Emitter::INTERPRETER, "Shutting down interpreter");
352 m_impl->interpreter.reset();
353 }
354}
355
356} // namespace Lila
#define LILA_WARN(emitter, msg)
#define LILA_ERROR(emitter, msg)
#define LILA_DEBUG(emitter, msg)
#define LILA_INFO(emitter, msg)
void add_library_path(const std::string &path)
Adds a library path (not yet implemented)
~ClangInterpreter()
Destructor; shuts down the interpreter if active.
EvalResult eval_file(const std::string &filepath)
Evaluates a code file by including it.
EvalResult eval(const std::string &code)
Evaluates a code snippet.
void set_target_triple(const std::string &triple)
Sets the target triple for code generation.
void * get_symbol_address(const std::string &name)
Gets the address of a symbol defined in the interpreter.
ClangInterpreter()
Constructs a ClangInterpreter instance.
std::unique_ptr< Impl > m_impl
Internal implementation details.
std::vector< std::string > get_defined_symbols()
Gets a list of all defined symbols.
void add_compile_flag(const std::string &flag)
Adds a compile flag for code evaluation.
void add_include_path(const std::string &path)
Adds an include path for code evaluation.
void shutdown()
Shuts down the interpreter and releases resources.
void reset()
Resets the interpreter, clearing all state.
Embedded Clang interpreter for live code evaluation in MayaFlux.
static const std::string & get_clang_resource_dir()
static const std::string & get_dep_includes(const std::string &library_name)
static const std::vector< std::string > & get_system_includes()
void initialize()
Definition main.cpp:11
std::string error
Error message if evaluation failed.
std::unordered_map< std::string, void * > symbol_table
std::unique_ptr< clang::Interpreter > interpreter
std::vector< std::string > compile_flags
std::vector< std::string > include_paths