MayaFlux 0.2.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
5#include <llvm/ExecutionEngine/Orc/LLJIT.h>
6#include <llvm/ExecutionEngine/Orc/ThreadSafeModule.h>
7
8#include <llvm/Support/TargetSelect.h>
9#include <llvm/TargetParser/Host.h>
10
11#include <clang/AST/Type.h>
12#include <clang/Frontend/CompilerInstance.h>
13#include <clang/Interpreter/Interpreter.h>
14
15#ifdef MAYAFLUX_PLATFORM_MACOS
16#include <dispatch/dispatch.h>
17#endif
18
19namespace Lila {
20
22 std::unique_ptr<clang::Interpreter> interpreter;
23
24 std::vector<std::string> include_paths;
25 std::vector<std::string> compile_flags;
26 std::string target_triple;
27
28 std::unordered_map<std::string, void*> symbol_table;
29 int eval_counter = 0;
30};
31
33 : m_impl(std::make_unique<Impl>())
34{
35 m_impl->target_triple = llvm::sys::getDefaultTargetTriple();
36}
37
42
44ClangInterpreter& ClangInterpreter::operator=(ClangInterpreter&&) noexcept = default;
45
47{
48 LILA_INFO(Emitter::INTERPRETER, "Initializing Clang interpreter");
49
50#ifdef MAYAFLUX_PLATFORM_WINDOWS
51 llvm::sys::DynamicLibrary::LoadLibraryPermanently("msvcp140.dll");
52 llvm::sys::DynamicLibrary::LoadLibraryPermanently("vcruntime140.dll");
53 llvm::sys::DynamicLibrary::LoadLibraryPermanently("ucrtbase.dll");
54 llvm::sys::DynamicLibrary::LoadLibraryPermanently("MayaFluxLib.dll");
55#endif
56
57 llvm::InitializeNativeTarget();
58 llvm::InitializeNativeTargetAsmPrinter();
59 llvm::InitializeNativeTargetAsmParser();
60
61 m_impl->compile_flags.clear();
62 m_impl->compile_flags.emplace_back("-std=c++23");
63 m_impl->compile_flags.emplace_back("-DMAYASIMPLE");
64
65#ifdef MAYAFLUX_PLATFORM_LINUX
66 m_impl->compile_flags.emplace_back("-mcmodel=large");
67 m_impl->compile_flags.emplace_back("-fPIC");
68 m_impl->compile_flags.emplace_back("-fPIE");
69#endif
70
71 std::string pch_dir;
72 if (std::filesystem::exists(MayaFlux::Config::PCH_RUNTIME_PATH)) {
73 pch_dir = MayaFlux::Config::RUNTIME_DATA_DIR;
74 LILA_DEBUG(Emitter::INTERPRETER,
75 std::string("Using installed PCH from: ") + std::string(MayaFlux::Config::PCH_RUNTIME_PATH));
76
77 } else if (std::filesystem::exists(MayaFlux::Config::PCH_SOURCE_PATH)) {
78 pch_dir = std::string(MayaFlux::Config::SOURCE_DIR) + "/cmake";
79 LILA_DEBUG(Emitter::INTERPRETER,
80 std::string("Using source PCH from: ") + std::string(MayaFlux::Config::PCH_SOURCE_PATH));
81
82 } else {
83 m_last_error = "Cannot find pch.h in runtime or source locations";
84 LILA_ERROR(Emitter::INTERPRETER, m_last_error);
85 return false;
86 }
87
88 m_impl->compile_flags.push_back("-I" + pch_dir);
89
90 std::string resource_dir = MayaFlux::Platform::SystemConfig::get_clang_resource_dir();
91 if (!resource_dir.empty()) {
92 m_impl->compile_flags.push_back("-resource-dir=" + resource_dir);
93 LILA_DEBUG(Emitter::INTERPRETER,
94 std::string("Using clang resource dir: ") + resource_dir);
95 } else {
96 m_impl->compile_flags.emplace_back("-resource-dir=/usr/lib/clang/21");
97 LILA_WARN(Emitter::INTERPRETER,
98 "Using default clang resource dir: /usr/lib/clang/21");
99 }
100
101 auto system_includes = MayaFlux::Platform::SystemConfig::get_system_includes();
102 for (const auto& include : system_includes) {
103 m_impl->compile_flags.push_back("-isystem" + include);
104 }
105
106#ifdef MAYAFLUX_PLATFORM_MACOS
107 // CRITICAL: JIT uses Homebrew LLVM but needs macOS SDK for system headers
108 // Homebrew LLVM's libc++ requires pthread.h, sched.h, time.h, etc. from SDK
109 std::string sdk_path = MayaFlux::Platform::SystemConfig::get_macos_sdk_path();
110 if (!sdk_path.empty()) {
111 m_impl->compile_flags.push_back("-isysroot" + sdk_path);
112 LILA_DEBUG(Emitter::INTERPRETER, "Using macOS SDK: " + sdk_path);
113 } else {
114 LILA_WARN(Emitter::INTERPRETER,
115 "Could not find macOS SDK - JIT may fail to find system headers");
116 }
117#endif
118
119 for (const auto& path : m_impl->include_paths) {
120 m_impl->compile_flags.push_back("-I" + path);
121 }
122
123#ifdef MAYAFLUX_PLATFORM_WINDOWS
124 m_impl->compile_flags.emplace_back("-fno-function-sections");
125 m_impl->compile_flags.emplace_back("-fno-data-sections");
126 m_impl->compile_flags.emplace_back("-fno-unique-section-names");
127#endif
128
129 std::vector<const char*> args;
130 for (const auto& flag : m_impl->compile_flags) {
131 args.push_back(flag.c_str());
132 }
133
134 clang::IncrementalCompilerBuilder ICB;
135 ICB.SetCompilerArgs(args);
136
137 auto CI = ICB.CreateCpp();
138 if (!CI) {
139 m_last_error = "Failed to create CompilerInstance: " + llvm::toString(CI.takeError());
140 LILA_ERROR(Emitter::INTERPRETER, m_last_error);
141 return false;
142 }
143
144 auto interp = clang::Interpreter::create(std::move(*CI));
145 if (!interp) {
146 m_last_error = "Failed to create interpreter: " + llvm::toString(interp.takeError());
147 LILA_ERROR(Emitter::INTERPRETER, m_last_error);
148 return false;
149 }
150
151 m_impl->interpreter = std::move(*interp);
152
153 LILA_INFO(Emitter::INTERPRETER, "Clang interpreter created successfully");
154
155 auto result = m_impl->interpreter->ParseAndExecute(
156 "#include \"pch.h\"\n"
157 "#include \"Lila/LiveAid.hpp\"\n");
158
159 if (result) {
160 std::string warning = "Failed to load MayaFlux headers: " + llvm::toString(std::move(result));
161 LILA_WARN(Emitter::INTERPRETER, warning);
162 } else {
163 LILA_INFO(Emitter::INTERPRETER, "MayaFlux headers loaded successfully");
164 }
165
166 result = m_impl->interpreter->ParseAndExecute("std::cout << \"Ready for Live\" << std::flush;");
167
168 return true;
169}
170
172{
173#ifdef MAYAFLUX_PLATFORM_MACOS
174 __block EvalResult result;
175#else
176 EvalResult result;
177#endif
178
179 if (!m_impl->interpreter) {
180 result.error = "Interpreter not initialized";
181 LILA_ERROR(Emitter::INTERPRETER, result.error);
182 return result;
183 }
184
185 LILA_DEBUG(Emitter::INTERPRETER, "Evaluating code...");
186
187#ifdef MAYAFLUX_PLATFORM_MACOS
188 dispatch_sync(dispatch_get_main_queue(), ^{
189 auto eval_result = m_impl->interpreter->ParseAndExecute(code);
190
191 if (!eval_result) {
192 result.success = true;
193 LILA_DEBUG(Emitter::INTERPRETER, "Code evaluation succeeded");
194 } else {
195 result.success = false;
196 result.error = "Execution failed: " + llvm::toString(std::move(eval_result));
197 LILA_ERROR(Emitter::INTERPRETER, result.error);
198 }
199 });
200#else
201 auto eval_result = m_impl->interpreter->ParseAndExecute(code);
202
203 if (!eval_result) {
204 result.success = true;
205 LILA_DEBUG(Emitter::INTERPRETER, "Code evaluation succeeded");
206 } else {
207 result.success = false;
208 result.error = "Execution failed: " + llvm::toString(std::move(eval_result));
209 LILA_ERROR(Emitter::INTERPRETER, result.error);
210 }
211#endif
212
213 m_impl->eval_counter++;
214 return result;
215}
216
218{
219 EvalResult result;
220
221 if (!std::filesystem::exists(filepath)) {
222 result.error = "File does not exist: " + filepath;
223 LILA_ERROR(Emitter::INTERPRETER, result.error);
224 return result;
225 }
226
227 LILA_INFO(Emitter::INTERPRETER, std::string("Evaluating file: ") + filepath);
228
229 std::string code = "#include \"" + filepath + "\"\n";
230 return eval(code);
231}
232
233void* ClangInterpreter::get_symbol_address(const std::string& name)
234{
235 if (!m_impl->interpreter) {
236 LILA_WARN(Emitter::INTERPRETER, "Cannot get symbol: interpreter not initialized");
237 return nullptr;
238 }
239
240 auto symbol = m_impl->interpreter->getSymbolAddress(name);
241 if (symbol) {
242 LILA_DEBUG(Emitter::INTERPRETER, std::string("Found symbol: ") + name);
243 return reinterpret_cast<void*>(symbol->getValue());
244 }
245
246 LILA_WARN(Emitter::INTERPRETER, std::string("Symbol not found: ") + name);
247 return nullptr;
248}
249
251{
252 std::vector<std::string> symbols;
253 for (const auto& pair : m_impl->symbol_table) {
254 symbols.push_back(pair.first);
255 }
256 return symbols;
257}
258
259void ClangInterpreter::add_include_path(const std::string& path)
260{
261 if (std::filesystem::exists(path)) {
262 m_impl->include_paths.push_back(path);
263 LILA_DEBUG(Emitter::INTERPRETER, std::string("Added include path: ") + path);
264 } else {
265 LILA_WARN(Emitter::INTERPRETER,
266 std::string("Include path does not exist: ") + path);
267 }
268}
269
270void ClangInterpreter::add_library_path(const std::string& path)
271{
272 LILA_DEBUG(Emitter::INTERPRETER,
273 std::string("Library path noted (not yet implemented): ") + path);
274}
275
276void ClangInterpreter::add_compile_flag(const std::string& flag)
277{
278 m_impl->compile_flags.push_back(flag);
279 LILA_DEBUG(Emitter::INTERPRETER, std::string("Added compile flag: ") + flag);
280}
281
282void ClangInterpreter::set_target_triple(const std::string& triple)
283{
284 m_impl->target_triple = triple;
285 LILA_INFO(Emitter::INTERPRETER, std::string("Target triple set to: ") + triple);
286}
287
289{
290 LILA_INFO(Emitter::INTERPRETER, "Resetting interpreter");
291 shutdown();
292 m_impl = std::make_unique<Impl>();
293}
294
296{
297 if (m_impl->interpreter) {
298 LILA_INFO(Emitter::INTERPRETER, "Shutting down interpreter");
299 m_impl->interpreter.reset();
300 }
301}
302
303} // 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.
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