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