Vuo 2.4.4
Loading...
Searching...
No Matches
VuoCompiler.cc
Go to the documentation of this file.
1
10#include "VuoCompiler.hh"
11
12#include <dlfcn.h>
13#include <string.h>
14#include <sys/param.h>
15#include <sys/stat.h>
16#include <sstream>
17#include <CoreFoundation/CoreFoundation.h>
18#include "VuoClangIssues.hh"
27#include "VuoCompilerGraph.hh"
28#include "VuoCompilerIssue.hh"
30#include "VuoCompilerNode.hh"
31#include "VuoCompilerPort.hh"
36#include "VuoComposition.hh"
38#include "VuoException.hh"
39#include "VuoGenericType.hh"
41#include "VuoModuleCache.hh"
43#include "VuoModuleCompiler.hh"
45#include "VuoModuleInfo.hh"
47#include "VuoNode.hh"
48#include "VuoNodeClass.hh"
49#include "VuoNodeSet.hh"
50#include "VuoPublishedPort.hh"
51#include "VuoRunner.hh"
53#include "VuoStringUtilities.hh"
54
55#if VUO_PRO
56#include "VuoPro.hh"
57#endif
58
59set<VuoCompiler *> VuoCompiler::allCompilers;
60dispatch_queue_t VuoCompiler::environmentQueue = dispatch_queue_create("org.vuo.compiler.environment", NULL);
61map<string, vector< vector<VuoCompilerEnvironment *> > > VuoCompiler::sharedEnvironments;
62map<string, map<string, vector<VuoCompilerEnvironment *> > > VuoCompiler::environmentsForCompositionFamily;
63map<VuoCompilerEnvironment *, map<string, pair<VuoCompilerModule *, dispatch_group_t>>> VuoCompiler::invalidatedModulesAwaitingRecompilation;
64map<VuoCompilerEnvironment *, set<VuoCompilerModule *>> VuoCompiler::addedModulesAwaitingReification;
65map<VuoCompilerEnvironment *, set<pair<VuoCompilerModule *, VuoCompilerModule *>>> VuoCompiler::modifiedModulesAwaitingReification;
66dispatch_group_t VuoCompiler::moduleSourceCompilersExistGlobally = dispatch_group_create();
67string VuoCompiler::vuoFrameworkInProgressPath;
68
69dispatch_queue_t llvmQueue = NULL;
70
71llvm::LLVMContext *VuoCompiler::globalLLVMContext = nullptr;
72
76static void __attribute__((constructor)) VuoCompiler_init(void)
77{
78 // Increase the open-files limit (macOS defaults to 256).
79 struct rlimit rl{OPEN_MAX, OPEN_MAX};
80 getrlimit(RLIMIT_NOFILE, &rl);
81 rl.rlim_cur = MIN(OPEN_MAX, rl.rlim_max);
82 if (setrlimit(RLIMIT_NOFILE, &rl))
83 VUserLog("Warning: Couldn't set open-files limit: %s", strerror(errno));
84
85
86 llvmQueue = dispatch_queue_create("org.vuo.compiler.llvm", NULL);
87
88 dispatch_sync(llvmQueue, ^{
89 // llvm::InitializeNativeTarget();
90 llvm::InitializeAllTargetMCs();
91 llvm::InitializeAllTargets();
92 // TargetRegistry::printRegisteredTargetsForVersion();
93
94 VuoCompiler::globalLLVMContext = new llvm::LLVMContext;
95
96 // Set up a diagnostic handler so that llvm::LLVMContext::diagnose doesn't exit
97 // when a call to llvm::Linker::linkInModule generates an error.
98 VuoCompiler::globalLLVMContext->setDiagnosticHandler([](const DiagnosticInfo &DI, void *context) {
99 string message;
100 raw_string_ostream stream(message);
101 DiagnosticPrinterRawOStream printer(stream);
102 DI.print(printer);
103 stream.flush();
104 if (! message.empty())
105 {
106 const char *severityName;
107 if (DI.getSeverity() == DS_Error)
108 severityName = "error";
109 else if (DI.getSeverity() == DS_Warning)
110 severityName = "warning";
111 else
112 severityName = "note";
113
114 VuoLog(VuoLog_moduleName, __FILE__, __LINE__, "llvmDiagnosticHandler", "%s: %s", severityName, message.c_str());
115 }
116 });
117
118 // If the Vuo compiler/linker...
119 // 1. Loads a node class that uses dispatch_object_t.
120 // 2. Generates code that uses dispatch_object_t.
121 // 3. Links the node class into a composition.
122 // 4. Generates more code that uses dispatch_object_t.
123 // ... then Step 4 ends up with the wrong llvm::Type for dispatch_object_t.
124 //
125 // A workaround is to generate some code that uses dispatch_object_t before doing Step 1.
126 //
127 // https://b33p.net/kosada/node/3845
128 // http://lists.cs.uiuc.edu/pipermail/llvmdev/2012-December/057075.html
129 Module module("", *VuoCompiler::globalLLVMContext);
131
132 // Workaround for a possibly related error where the compiler ends up with the wrong
133 // llvm::Type for dispatch_semaphore_s. https://b33p.net/kosada/node/10545
135
136 // Load the NodeContext and PortContext struct types preemptively to make sure that
137 // their fields get the right dispatch types. If these struct types were to be loaded
138 // first from a subcomposition module, they'd get the wrong dispatch types.
139 // https://b33p.net/kosada/node/11160
142 });
143}
144
148void VuoCompiler::applyToInstalledEnvironments(std::function<void(VuoCompilerEnvironment *)> doForEnvironment)
149{
150 dispatch_sync(environmentQueue, ^{
151 for (vector< vector<VuoCompilerEnvironment *> >::iterator i = environments.begin(); i != environments.end(); ++i) {
152 doForEnvironment((*i)[0]);
153 }
154 });
155}
156
160void VuoCompiler::applyToAllEnvironments(std::function<void(VuoCompilerEnvironment *)> doForEnvironment)
161{
162 dispatch_sync(environmentQueue, ^{
163 for (vector< vector<VuoCompilerEnvironment *> >::iterator i = environments.begin(); i != environments.end(); ++i) {
164 for (vector<VuoCompilerEnvironment *>::iterator j = i->begin(); j != i->end(); ++j) {
165 doForEnvironment(*j);
166 }
167 }
168 });
169}
170
175VuoCompilerEnvironment * VuoCompiler::environmentAtBroaderScope(VuoCompilerEnvironment *envA, VuoCompilerEnvironment *envB)
176{
177 for (const vector<VuoCompilerEnvironment *> environmentsAtScope : environments)
178 {
179 if (find(environmentsAtScope.begin(), environmentsAtScope.end(), envA) != environmentsAtScope.end())
180 return envA;
181 if (find(environmentsAtScope.begin(), environmentsAtScope.end(), envB) != environmentsAtScope.end())
182 return envB;
183 }
184
185 return nullptr;
186}
187
191VuoCompilerEnvironment * VuoCompiler::generatedEnvironmentAtSameScopeAs(VuoCompilerEnvironment *env)
192{
193 for (const vector<VuoCompilerEnvironment *> environmentsAtScope : environments)
194 if (find(environmentsAtScope.begin(), environmentsAtScope.end(), env) != environmentsAtScope.end())
195 return environmentsAtScope.back();
196
197 return nullptr;
198}
199
200
214VuoCompiler::VuoCompiler(const string &compositionPath, string target)
215{
216 if (target.empty())
217 {
218 target = getProcessTarget();
219
220 // Match the target macOS version of the built-in modules and precompiled headers.
221 llvm::Triple triple(target);
222 triple.setOSName("macosx10.10.0");
223 target = triple.str();
224
225 VDebugLog("%p target=%s (from current process)", this, target.c_str());
226 }
227 else
228 {
229 requestedTarget = target;
230 VDebugLog("%p target=%s", this, target.c_str());
231 }
232 this->target = target;
233
234#if VUO_PRO
235 init_Pro();
236#endif
237
238 shouldLoadAllModules = true;
239 hasLoadedAllModules = false;
240 modulesToLoadQueue = dispatch_queue_create("org.vuo.compiler.modules", NULL);
241 modulesLoading = dispatch_group_create();
242 moduleSourceCompilersExist = dispatch_group_create();
243 moduleCacheBuilding = dispatch_group_create();
244 dependencyGraph = NULL;
245 compositionDependencyGraph = NULL;
246 isVerbose = false;
247 _shouldShowSplashWindow = false;
248 delegate = NULL;
249 delegateQueue = dispatch_queue_create("org.vuo.compiler.delegate", NULL);
250
251 string vuoFrameworkPath = getVuoFrameworkPath();
252 if (! vuoFrameworkPath.empty())
253 clangPath = vuoFrameworkPath + "/Helpers/clang";
254 else
255 clangPath = LLVM_ROOT "/bin/clang";
256
257 dispatch_sync(environmentQueue, ^{
258 allCompilers.insert(this);
259
260 if (sharedEnvironments[target].empty())
261 {
262 sharedEnvironments[target] = vector< vector<VuoCompilerEnvironment *> >(3, vector<VuoCompilerEnvironment *>(2, NULL));
263 for (vector< vector<VuoCompilerEnvironment *> >::iterator i = sharedEnvironments[target].begin(); i != sharedEnvironments[target].end(); ++i)
264 for (vector<VuoCompilerEnvironment *>::iterator j = i->begin(); j != i->end(); ++j)
265 *j = new VuoCompilerEnvironment(this->target, i == sharedEnvironments[target].begin(), j != i->begin());
266
267 vector<string> builtInModuleSearchPaths = VuoCompilerEnvironment::getBuiltInModuleSearchPaths();
268 for (vector<string>::iterator i = builtInModuleSearchPaths.begin(); i != builtInModuleSearchPaths.end(); ++i)
269 sharedEnvironments[target][0][0]->addModuleSearchPath(*i, false);
270
271 vector<string> builtInLibrarySearchPaths = VuoCompilerEnvironment::getBuiltInLibrarySearchPaths();
272 for (vector<string>::iterator i = builtInLibrarySearchPaths.begin(); i != builtInLibrarySearchPaths.end(); ++i)
273 sharedEnvironments[target][0][0]->addLibrarySearchPath(*i);
274
275 vector<string> builtInFrameworkSearchPaths = VuoCompilerEnvironment::getBuiltInFrameworkSearchPaths();
276 for (vector<string>::iterator i = builtInFrameworkSearchPaths.begin(); i != builtInFrameworkSearchPaths.end(); ++i)
277 sharedEnvironments[target][0][0]->addFrameworkSearchPath(*i);
278
279 // Allow system administrator to override Vuo.framework modules
280 sharedEnvironments[target][1][0]->addModuleSearchPath(VuoFileUtilities::getSystemModulesPath());
281 sharedEnvironments[target][1][0]->addLibrarySearchPath(VuoFileUtilities::getSystemModulesPath());
282
283 // Allow user to override Vuo.framework and system-wide modules
284 sharedEnvironments[target][2][0]->addModuleSearchPath(VuoFileUtilities::getUserModulesPath());
285 sharedEnvironments[target][2][0]->addLibrarySearchPath(VuoFileUtilities::getUserModulesPath());
286
287 // Set up module cache paths.
288 // Since the built-in module caches are part of Vuo.framework (put there by `generateBuiltInModuleCaches`),
289 // only attempt to use module caches if Vuo.framework exists.
290 string vuoFrameworkPath = getVuoFrameworkPath();
291 if (! vuoFrameworkPath.empty())
292 {
293 shared_ptr<VuoModuleCache> builtInCache = VuoModuleCache::newBuiltInCache();
294 shared_ptr<VuoModuleCache> systemCache = VuoModuleCache::newSystemCache();
295 shared_ptr<VuoModuleCache> userCache = VuoModuleCache::newUserCache();
296
297 sharedEnvironments[target][0][0]->setModuleCache(builtInCache);
298 sharedEnvironments[target][0][1]->setModuleCache(builtInCache);
299 sharedEnvironments[target][1][0]->setModuleCache(systemCache);
300 sharedEnvironments[target][1][1]->setModuleCache(systemCache);
301 sharedEnvironments[target][2][0]->setModuleCache(userCache);
302 sharedEnvironments[target][2][1]->setModuleCache(userCache);
303 }
304 }
305 });
306
307 setCompositionPath(compositionPath);
308}
309
314{
315#if VUO_PRO
316 fini_Pro();
317#endif
318
319 dispatch_sync(environmentQueue, ^{
320 allCompilers.erase(this);
321 });
322
323 dispatch_group_wait(moduleCacheBuilding, DISPATCH_TIME_FOREVER);
324 dispatch_release(moduleCacheBuilding);
325
326 dispatch_group_wait(moduleSourceCompilersExist, DISPATCH_TIME_FOREVER);
327 dispatch_release(moduleSourceCompilersExist);
328
329 for (const vector<VuoCompilerEnvironment *> environmentsAtScope : environments)
330 for (VuoCompilerEnvironment *env : environmentsAtScope)
331 env->removeCompilerToNotify(this);
332
333 dispatch_sync(delegateQueue, ^{});
334
335 dispatch_release(modulesToLoadQueue);
336 dispatch_release(delegateQueue);
337 dispatch_release(modulesLoading);
338
339 delete dependencyGraph;
340 delete compositionDependencyGraph;
341}
342
346void VuoCompiler::reset(void)
347{
348 dispatch_group_wait(moduleSourceCompilersExistGlobally, DISPATCH_TIME_FOREVER);
349
350 dispatch_sync(environmentQueue, ^{
351
352 for (auto e : sharedEnvironments)
353 for (vector< vector<VuoCompilerEnvironment *> >::iterator i = e.second.begin(); i != e.second.end(); ++i)
354 {
355 (*i)[0]->stopWatchingModuleSearchPaths();
356 dispatch_sync((*i)[0]->moduleSearchPathContentsChangedQueue, ^{});
357 }
358
359 for (auto e : environmentsForCompositionFamily)
360 for (map< string, vector<VuoCompilerEnvironment *> >::iterator i = e.second.begin(); i != e.second.end(); ++i)
361 {
362 (i->second)[0]->stopWatchingModuleSearchPaths();
363 dispatch_sync((i->second)[0]->moduleSearchPathContentsChangedQueue, ^{});
364 }
365
366 for (auto e : sharedEnvironments)
367 for (vector< vector<VuoCompilerEnvironment *> >::iterator i = e.second.begin(); i != e.second.end(); ++i)
368 for (vector<VuoCompilerEnvironment *>::iterator j = i->begin(); j != i->end(); ++j)
369 delete *j;
370
371 for (auto e : environmentsForCompositionFamily)
372 for (map< string, vector<VuoCompilerEnvironment *> >::iterator i = e.second.begin(); i != e.second.end(); ++i)
373 for (vector<VuoCompilerEnvironment *>::iterator j = i->second.begin(); j != i->second.end(); ++j)
374 delete *j;
375
376 allCompilers.clear();
377 sharedEnvironments.clear();
378 environmentsForCompositionFamily.clear();
379
380 });
381}
382
389{
390 dispatch_async(delegateQueue, ^{
391 this->delegate = delegate;
392 });
393}
394
404void VuoCompiler::setCompositionPath(const string &compositionPathOrig)
405{
406 string compositionModulesDir;
407 string compositionBaseDir;
408 bool isSubcomposition = false;
409 string compositionPath = compositionPathOrig;
410 if (! compositionPath.empty())
411 {
412 compositionPath = VuoFileUtilities::getAbsolutePath(compositionPath);
413 VuoFileUtilities::canonicalizePath(compositionPath);
414
415 compositionModulesDir = VuoFileUtilities::getCompositionLocalModulesPath(compositionPath);
416
417 string file, ext;
418 VuoFileUtilities::splitPath(compositionModulesDir, compositionBaseDir, file, ext);
419 VuoFileUtilities::canonicalizePath(compositionBaseDir);
420
421 string compositionDir;
422 VuoFileUtilities::splitPath(compositionPath, compositionDir, file, ext);
424 isSubcomposition = (compositionDir == compositionModulesDir);
425 }
426
427 // Set up `environments` to contain all environments available to this compiler, in order from broadest to narrowest.
428
429 dispatch_sync(environmentQueue, ^{
430 if (! environments.empty() && compositionBaseDir == lastCompositionBaseDir) {
431 return;
432 }
433 lastCompositionBaseDir = compositionBaseDir;
434
435 // Clear out the existing environments for this compiler.
436
437 VuoCompilerEnvironment *oldCompositionFamilyInstalledEnvironment = nullptr;
438 vector<VuoCompilerEnvironment *> compositionEnvironments;
439 if (! environments.empty())
440 {
441 for (const vector<VuoCompilerEnvironment *> environmentsAtScope : environments)
442 for (VuoCompilerEnvironment *env : environmentsAtScope)
443 env->removeCompilerToNotify(this);
444
445 if (environments.size() >= 5) {
446 oldCompositionFamilyInstalledEnvironment = environments[3][0];
447 }
448
449 compositionEnvironments = environments.back();
450
451 environments.clear();
452 }
453
454 // If the composition is located in one of the shared environments (built-in, system, user),
455 // add that shared environment and any shared environments at broader scope to the compiler.
456 // If the composition is not in a shared environment, add all of the shared environments to the compiler.
457
458 bool isCompositionInSharedEnvironment = false;
459 for (vector< vector<VuoCompilerEnvironment *> >::iterator i = sharedEnvironments[target].begin(); i != sharedEnvironments[target].end(); ++i)
460 {
461 environments.push_back(*i);
462
463 vector<string> moduleSearchPaths = (*i)[0]->getModuleSearchPaths();
464 for (vector<string>::iterator j = moduleSearchPaths.begin(); j != moduleSearchPaths.end(); ++j)
465 {
466 string moduleSearchPath = *j;
467 VuoFileUtilities::canonicalizePath(moduleSearchPath);
468 if (moduleSearchPath == compositionModulesDir)
469 {
470 isCompositionInSharedEnvironment = true;
471 break;
472 }
473 }
474
475 if (isCompositionInSharedEnvironment) {
476 break;
477 }
478 }
479
480 // If the composition is not in a shared environment, add the composition-family environment to the compiler.
481
482 VuoCompilerEnvironment *newCompositionFamilyInstalledEnvironment = nullptr;
483 if (! isCompositionInSharedEnvironment && ! compositionPath.empty())
484 {
485 vector<VuoCompilerEnvironment *> compositionFamilyEnvironments = environmentsForCompositionFamily[target][compositionBaseDir];
486 if (compositionFamilyEnvironments.empty())
487 {
488 compositionFamilyEnvironments = vector<VuoCompilerEnvironment *>(2, NULL);
489 compositionFamilyEnvironments[0] = new VuoCompilerEnvironment(this->target, false, false);
490 compositionFamilyEnvironments[1] = new VuoCompilerEnvironment(this->target, false, true);
491 environmentsForCompositionFamily[target][compositionBaseDir] = compositionFamilyEnvironments;
492
493 // Allow the user to place modules/subcompositions in a Modules folder inside the composition folder.
494
495 compositionFamilyEnvironments[0]->addModuleSearchPath(compositionModulesDir);
496
497 shared_ptr<VuoModuleCache> moduleCache = VuoModuleCache::newCache(compositionBaseDir);
498 compositionFamilyEnvironments[0]->setModuleCache(moduleCache);
499 compositionFamilyEnvironments[1]->setModuleCache(moduleCache);
500 }
501 environments.push_back(compositionFamilyEnvironments);
502
503 newCompositionFamilyInstalledEnvironment = compositionFamilyEnvironments[0];
504 }
505
506 // Add the composition environment to the compiler (or add it back in if it already existed).
507
508 if (compositionEnvironments.empty())
509 {
510 compositionEnvironments = vector<VuoCompilerEnvironment *>(2, NULL);
511 compositionEnvironments[0] = new VuoCompilerEnvironment(this->target, false, false);
512 compositionEnvironments[1] = new VuoCompilerEnvironment(this->target, false, true);
513
514 shared_ptr<VuoModuleCache> moduleCache = VuoModuleCache::newCache(compositionPath);
515 compositionEnvironments[0]->setModuleCache(moduleCache);
516 compositionEnvironments[1]->setModuleCache(moduleCache);
517 }
518 environments.push_back(compositionEnvironments);
519
520 // Select the generated environment:
521 // - if compiling a subcomposition in a shared environment, the same shared environment
522 // - if compiling a subcomposition in a custom modules directory, the composition-family environment
523 // - otherwise, the composition environment
524
525 int generatedModulesScope = isCompositionInSharedEnvironment || isSubcomposition ? environments.size() - 2 : environments.size() - 1;
526 generatedEnvironment = environments[generatedModulesScope][1];
527
528 for (auto i : environments)
529 for (VuoCompilerEnvironment *env : i)
530 env->addCompilerToNotify(this);
531
532 delete dependencyGraph;
533 delete compositionDependencyGraph;
534 dependencyGraph = makeDependencyNetwork(environments, ^VuoDirectedAcyclicGraph * (VuoCompilerEnvironment *env) { return env->getDependencyGraph(); });
535 compositionDependencyGraph = makeDependencyNetwork(environments, ^VuoDirectedAcyclicGraph * (VuoCompilerEnvironment *env) { return env->getCompositionDependencyGraph(); });
536
537 // If the compiler has a different local Modules directory than before, notify the compiler's delegate
538 // of composition-family modules that are newly available/unavailable.
539
540 if (oldCompositionFamilyInstalledEnvironment != newCompositionFamilyInstalledEnvironment)
541 {
542 auto getModules = [] (VuoCompilerEnvironment *env)
543 {
544 map<string, VuoCompilerModule *> modules;
545 if (env)
546 {
547 for (auto i : env->getNodeClasses()) {
548 modules.insert(i);
549 }
550 for (auto i : env->getTypes()) {
551 modules.insert(i);
552 }
553 for (auto i : env->getLibraryModules()) {
554 modules.insert(i);
555 }
556 }
557 return modules;
558 };
559
560 map<string, VuoCompilerModule *> modulesAdded = getModules(newCompositionFamilyInstalledEnvironment);
561 map<string, VuoCompilerModule *> modulesRemoved = getModules(oldCompositionFamilyInstalledEnvironment);
562
563 map<string, pair<VuoCompilerModule *, VuoCompilerModule *> > modulesModified;
564 for (map<string, VuoCompilerModule *>::iterator add = modulesAdded.begin(); add != modulesAdded.end(); )
565 {
566 map<string, VuoCompilerModule *>::iterator rem = modulesRemoved.find(add->first);
567 if (rem != modulesRemoved.end())
568 {
569 modulesModified[add->first] = make_pair(rem->second, add->second);
570 modulesAdded.erase(add++);
571 modulesRemoved.erase(rem);
572 }
573 else
574 {
575 ++add;
576 }
577 }
578
579 if (! (modulesAdded.empty() && modulesModified.empty() && modulesRemoved.empty()) )
580 {
581 VuoCompilerIssues *issues = new VuoCompilerIssues();
582 VuoCompilerDelegate::LoadedModulesData *delegateData = new VuoCompilerDelegate::LoadedModulesData(set< pair<VuoCompilerModule *, VuoCompilerModule *> >(), set<VuoCompilerModule *>(), issues);
583 delegateData->retain();
584
585 VuoCompilerEnvironment *scopeEnvironment = newCompositionFamilyInstalledEnvironment;
586 if (! scopeEnvironment) {
587 scopeEnvironment = compositionEnvironments.at(0);
588 }
589
590 loadedModules(modulesAdded, modulesModified, modulesRemoved, issues, delegateData, scopeEnvironment);
591 }
592 }
593 });
594}
595
601VuoDirectedAcyclicNetwork * VuoCompiler::makeDependencyNetwork(const vector< vector<VuoCompilerEnvironment *> > &environments,
602 VuoDirectedAcyclicGraph * (^graphForEnvironment)(VuoCompilerEnvironment *))
603{
604 if (!graphForEnvironment)
605 return NULL;
606
608
609 for (vector< vector<VuoCompilerEnvironment *> >::const_iterator i = environments.begin(); i != environments.end(); ++i)
610 {
611 // Installed environment depends on generated environment in same scope.
612
613 network->addEdge(graphForEnvironment(i->at(0)), graphForEnvironment(i->at(1)));
614
615 // Installed and generated environments depend on installed environments in broader scopes.
616
617 for (vector< vector<VuoCompilerEnvironment *> >::const_iterator ii = environments.begin(); ii != i; ++ii)
618 {
619 network->addEdge(graphForEnvironment(i->at(0)), graphForEnvironment(ii->at(0)));
620 network->addEdge(graphForEnvironment(i->at(1)), graphForEnvironment(ii->at(0)));
621 }
622 }
623
624 return network;
625}
626
632VuoCompilerModule * VuoCompiler::loadModuleIfNeeded(const string &moduleKey)
633{
634 // For performance, first see if the module is already loaded.
635
636 VuoCompilerModule *module = nullptr;
637 bool foundModuleInfoInNarrowerEnvironment = false;
638
639 auto envGetModule = [&moduleKey, &module, &foundModuleInfoInNarrowerEnvironment] (VuoCompilerEnvironment *env)
640 {
641 VuoCompilerModule *foundModule = env->findModule(moduleKey);
642 if (foundModule)
643 {
644 module = foundModule;
645 foundModuleInfoInNarrowerEnvironment = false;
646 }
647 else if (module && ! foundModuleInfoInNarrowerEnvironment && (env->listModule(moduleKey) || env->listSourceFile(moduleKey)))
648 {
649 foundModuleInfoInNarrowerEnvironment = true;
650 }
651 };
652
653 applyToAllEnvironments(envGetModule);
654
655 // If not, try loading it and check again.
656
657 if (! module || foundModuleInfoInNarrowerEnvironment)
658 {
659 loadModulesIfNeeded({moduleKey});
660 applyToAllEnvironments(envGetModule);
661 }
662
663 return module;
664}
665
686void VuoCompiler::loadModulesIfNeeded(const set<string> &moduleKeys)
687{
688 dispatch_group_enter(modulesLoading);
689 VuoDefer(^{ dispatch_group_leave(modulesLoading); });
690
691 __block bool willLoadAllModules = false;
692 dispatch_sync(modulesToLoadQueue, ^{
693 if (shouldLoadAllModules && ! hasLoadedAllModules) {
694 willLoadAllModules = true;
695 hasLoadedAllModules = true;
696 }
697 });
698
699 if (! willLoadAllModules && moduleKeys.empty())
700 return;
701
702 // Load modules, and start sources and specialized modules compiling.
703
704 __block set<dispatch_group_t> sourcesLoading;
705 dispatch_sync(environmentQueue, ^{
706 sourcesLoading = loadModulesAndSources(moduleKeys, set<string>(), set<string>(),
707 moduleKeys, set<string>(), set<string>(),
708 willLoadAllModules, false, nullptr, nullptr, nullptr, "");
709 });
710
711 // Wait for sources and specialized modules to finish compiling and the compiled modules to be loaded
712 // to ensure that the next call to getNodeClass() will return them.
713
714 for (set<dispatch_group_t>::iterator i = sourcesLoading.begin(); i != sourcesLoading.end(); ++i)
715 {
716 dispatch_group_wait(*i, DISPATCH_TIME_FOREVER);
717 dispatch_release(*i);
718 }
719}
720
729set<dispatch_group_t> VuoCompiler::loadModulesAndSources(const set<string> &modulesAddedKeys, const set<string> &modulesModifiedKeys, const set<string> &modulesRemovedKeys,
730 const set<string> &sourcesAddedKeys, const set<string> &sourcesModifiedKeys, const set<string> &sourcesRemovedKeys,
731 bool willLoadAllModules, bool shouldRecompileSourcesIfUnchanged,
732 VuoCompilerEnvironment *currentEnvironment, VuoCompilerIssues *issuesForCurrentEnvironment,
733 std::function<void(void)> moduleLoadedCallback, const string &moduleAddedOrModifiedSourceCode)
734{
735 //VLog("C=%p E=%p -- %lu %lu %lu %lu %lu %lu %i %i", this, currentEnvironment,
736 //modulesAddedKeys.size(), modulesModifiedKeys.size(), modulesRemovedKeys.size(),
737 //sourcesAddedKeys.size(), sourcesModifiedKeys.size(), sourcesRemovedKeys.size(),
738 //willLoadAllModules, shouldRecompileSourcesIfUnchanged);
739 //if (modulesAddedKeys.size() == 1) VLog(" %s", modulesAddedKeys.begin()->c_str());
740 //if (modulesModifiedKeys.size() == 1) VLog(" %s", modulesModifiedKeys.begin()->c_str());
741 //if (modulesRemovedKeys.size() == 1) VLog(" %s", modulesRemovedKeys.begin()->c_str());
742
743 // Organize the modules, source files, and issues by environment.
744
745 map<VuoCompilerEnvironment *, set<string> > modulesAdded;
746 map<VuoCompilerEnvironment *, set<string> > modulesModified;
747 map<VuoCompilerEnvironment *, set<string> > modulesRemoved;
748 map<VuoCompilerEnvironment *, set<string> > sourcesAdded;
749 map<VuoCompilerEnvironment *, set<string> > sourcesModified;
750 map<VuoCompilerEnvironment *, set<string> > sourcesRemoved;
751
752 if (currentEnvironment)
753 {
754 modulesAdded[currentEnvironment] = modulesAddedKeys;
755 modulesModified[currentEnvironment] = modulesModifiedKeys;
756 modulesRemoved[currentEnvironment] = modulesRemovedKeys;
757 sourcesAdded[currentEnvironment] = sourcesAddedKeys;
758 sourcesModified[currentEnvironment] = sourcesModifiedKeys;
759 sourcesRemoved[currentEnvironment] = sourcesRemovedKeys;
760 }
761 else
762 {
763 for (const vector<VuoCompilerEnvironment *> environmentsAtScope : environments)
764 {
765 for (VuoCompilerEnvironment *env : environmentsAtScope)
766 {
767 bool willLoadAllModulesInEnv = willLoadAllModules && ! env->isGenerated();
768
769 VuoModuleInfoIterator modulesAddedIter = (willLoadAllModulesInEnv ? env->listAllModules() : env->listModules(modulesAddedKeys));
770 VuoModuleInfoIterator sourcesAddedIter = (willLoadAllModulesInEnv ? env->listAllSourceFiles() : env->listSourceFiles(sourcesAddedKeys));
771 VuoModuleInfoIterator sourcesModifiedIter = (willLoadAllModulesInEnv ? env->listAllSourceFiles() : env->listSourceFiles(sourcesModifiedKeys));
772
773 VuoModuleInfo *moduleInfo;
774 while ((moduleInfo = modulesAddedIter.next()))
775 {
776 string moduleKey = moduleInfo->getModuleKey();
777
778 modulesAdded[env].insert(moduleKey);
779 }
780
781 // If a source file and a compiled file for the same module are in the same folder,
782 // the compiled file takes precedence.
783 auto isCompiledModuleAtSameSearchPath = [&env] (VuoModuleInfo *sourceInfo)
784 {
785 VuoModuleInfo *compiledModuleInfo = env->listModule(sourceInfo->getModuleKey());
786 return (compiledModuleInfo && compiledModuleInfo->getSearchPath() == sourceInfo->getSearchPath());
787 };
788
789 while ((moduleInfo = sourcesAddedIter.next()))
790 {
791 if (isCompiledModuleAtSameSearchPath(moduleInfo))
792 continue;
793
794 sourcesAdded[env].insert( moduleInfo->getModuleKey() );
795 }
796
797 while ((moduleInfo = sourcesModifiedIter.next()))
798 {
799 if (isCompiledModuleAtSameSearchPath(moduleInfo))
800 continue;
801
802 sourcesModified[env].insert( moduleInfo->getModuleKey() );
803 }
804 }
805 }
806 }
807
808 map<VuoCompilerEnvironment *, VuoCompilerIssues *> issues;
809 for (const vector<VuoCompilerEnvironment *> environmentsAtScope : environments)
810 for (VuoCompilerEnvironment *env : environmentsAtScope)
811 issues[env] = (env == currentEnvironment && issuesForCurrentEnvironment ? issuesForCurrentEnvironment : new VuoCompilerIssues());
812
813 // Check for circular dependencies in added/modified sources.
814
815 for (vector< vector<VuoCompilerEnvironment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
816 {
817 VuoCompilerEnvironment *env = (*i).at(0);
818
819 // Check for circular dependencies involving sources being loaded within this environment.
820 // For circular dependencies involving sources in different environments,
821 // an error will be reported elsewhere due to one of them being outside of the other's scope.
822 set<VuoDirectedAcyclicGraph::Vertex *> circularDependencies = env->getCompositionDependencyGraph()->getCycleVertices();
823
824 set<string> sourcesAddedModified;
825 sourcesAddedModified.insert(sourcesAdded[env].begin(), sourcesAdded[env].end());
826 sourcesAddedModified.insert(sourcesModified[env].begin(), sourcesModified[env].end());
827
828 for (set<string>::iterator j = sourcesAddedModified.begin(); j != sourcesAddedModified.end(); ++j)
829 {
830 string moduleKey = *j;
831
832 bool found = false;
833 for (set<VuoDirectedAcyclicGraph::Vertex *>::iterator k = circularDependencies.begin(); k != circularDependencies.end(); ++k)
834 {
835 VuoDependencyGraphVertex *vertex = static_cast<VuoDependencyGraphVertex *>(*k);
836 if (vertex->getDependency() == moduleKey)
837 {
838 found = true;
839 break;
840 }
841 }
842
843 if (found)
844 {
845 sourcesAdded[env].erase(moduleKey);
846 sourcesModified[env].erase(moduleKey);
847
848 VuoCompilerIssue issue(VuoCompilerIssue::Error, "compiling subcomposition", moduleKey,
849 "Subcomposition contains itself",
850 "%moduleKey contains an instance of itself, "
851 "or contains another subcomposition that contains an instance of %moduleKey.");
852 issue.setModuleKey(moduleKey);
853
854 VuoModuleInfo *sourceInfo = env->listSourceFile(moduleKey);
855 if (sourceInfo)
856 issue.setFilePath(sourceInfo->getFile()->path());
857
858 issues[env]->append(issue);
859 }
860 }
861 }
862
863 // Update the longest downstream paths for added/modified sources.
864
865 for (const vector<VuoCompilerEnvironment *> &envs : environments)
866 {
867 VuoCompilerEnvironment *env = envs.at(0);
868
869 set<string> sourcesAddedModified;
870 sourcesAddedModified.insert(sourcesAdded[env].begin(), sourcesAdded[env].end());
871 sourcesAddedModified.insert(sourcesModified[env].begin(), sourcesModified[env].end());
872
873 for (const string &moduleKey : sourcesAddedModified)
874 {
876 int pathLength = env->getCompositionDependencyGraph()->getLongestDownstreamPath(vertex);
877
878 VuoModuleInfo *sourceInfo = env->listSourceFile(moduleKey);
879 sourceInfo->setLongestDownstreamPath(pathLength);
880 }
881 }
882
883 // Find all modules and sources that depend on the modules and sources being modified or removed.
884 // Those that belong to one of this compiler's environments are used in subsequent stages.
885 // Those that belong to some other environment are scheduled to be handled by a separate call to this function.
886
887 map<VuoCompilerEnvironment *, set<string> > modulesDepOnModulesModified;
888 map<VuoCompilerEnvironment *, set<string> > modulesDepOnModulesRemoved;
889 map<VuoCompilerEnvironment *, set<string> > sourcesDepOnModulesRemoved;
890
891 map<VuoCompilerEnvironment *, set<string> > sourcesDirectDepOnModulesModified;
892
893 {
894 map<VuoCompilerEnvironment *, set<string> > sourcesDepOnModulesModified;
895
896 __block map<VuoCompilerEnvironment *, set<string> > modulesDepOnModulesModified_otherCompiler;
897 __block map<VuoCompilerEnvironment *, set<string> > sourcesDepOnModulesModified_otherCompiler;
898 __block map<VuoCompilerEnvironment *, set<string> > modulesDepOnModulesRemoved_otherCompiler;
899 __block map<VuoCompilerEnvironment *, set<string> > sourcesDepOnModulesRemoved_otherCompiler;
900
901 vector<VuoDirectedAcyclicNetwork *> searchDependencyGraphs;
902 for (VuoCompiler *compiler : allCompilers)
903 searchDependencyGraphs.push_back(compiler->dependencyGraph);
904
905 VuoDirectedAcyclicGraph *currentEnvironmentDependencyGraph = (currentEnvironment ? currentEnvironment->getDependencyGraph() : nullptr);
906
907 findDependentModulesAndSources(modulesModified, searchDependencyGraphs, currentEnvironmentDependencyGraph, true,
908 modulesDepOnModulesModified, modulesDepOnModulesModified_otherCompiler,
909 sourcesDepOnModulesModified, sourcesDepOnModulesModified_otherCompiler);
910
911 findDependentModulesAndSources(modulesRemoved, searchDependencyGraphs, currentEnvironmentDependencyGraph, true,
912 modulesDepOnModulesRemoved, modulesDepOnModulesRemoved_otherCompiler,
913 sourcesDepOnModulesRemoved, sourcesDepOnModulesRemoved_otherCompiler);
914
915 map<VuoCompilerEnvironment *, set<string> > modulesDirectDepOnModulesModified;
916 map<VuoCompilerEnvironment *, set<string> > modulesDirectDepOnModulesRemoved;
917 map<VuoCompilerEnvironment *, set<string> > sourcesDirectDepOnModulesRemoved;
918
919 __block map<VuoCompilerEnvironment *, set<string> > modulesDirectDepOnModulesModified_otherCompiler;
920 __block map<VuoCompilerEnvironment *, set<string> > sourcesDirectDepOnModulesModified_otherCompiler;
921 __block map<VuoCompilerEnvironment *, set<string> > modulesDirectDepOnModulesRemoved_otherCompiler;
922 __block map<VuoCompilerEnvironment *, set<string> > sourcesDirectDepOnModulesRemoved_otherCompiler;
923
924 findDependentModulesAndSources(modulesModified, searchDependencyGraphs, currentEnvironmentDependencyGraph, false,
925 modulesDirectDepOnModulesModified, modulesDirectDepOnModulesModified_otherCompiler,
926 sourcesDirectDepOnModulesModified, sourcesDirectDepOnModulesModified_otherCompiler);
927
928 findDependentModulesAndSources(modulesRemoved, searchDependencyGraphs, currentEnvironmentDependencyGraph, false,
929 modulesDirectDepOnModulesRemoved, modulesDirectDepOnModulesRemoved_otherCompiler,
930 sourcesDirectDepOnModulesRemoved, sourcesDirectDepOnModulesRemoved_otherCompiler);
931
932 auto insertInvalidatedModulesAwaitingRecompilation = [](const map<VuoCompilerEnvironment *, set<string> > &modulesToInsert)
933 {
934 for (auto i : modulesToInsert)
935 {
936 VuoCompilerEnvironment *env = i.first;
937 for (const string &moduleKey : i.second)
938 {
939 VuoCompilerModule *module = env->findModule(moduleKey);
940 invalidatedModulesAwaitingRecompilation[env][moduleKey] = { module, nullptr };
941 }
942 }
943 };
944
945 insertInvalidatedModulesAwaitingRecompilation(sourcesDepOnModulesModified);
946 insertInvalidatedModulesAwaitingRecompilation(sourcesDepOnModulesModified_otherCompiler);
947
948 set<VuoCompilerEnvironment *> otherEnvironments;
949 for (auto i : modulesDepOnModulesModified_otherCompiler)
950 otherEnvironments.insert(i.first);
951 for (auto i : sourcesDepOnModulesModified_otherCompiler)
952 otherEnvironments.insert(i.first);
953 for (auto i : modulesDepOnModulesRemoved_otherCompiler)
954 otherEnvironments.insert(i.first);
955 for (auto i : sourcesDepOnModulesRemoved_otherCompiler)
956 otherEnvironments.insert(i.first);
957
958 for (VuoCompilerEnvironment *env : otherEnvironments)
959 {
960 VuoCompiler *otherCompiler = nullptr;
961 for (VuoCompiler *c : allCompilers)
962 for (const vector<VuoCompilerEnvironment *> &ee : c->environments)
963 for (VuoCompilerEnvironment *e : ee)
964 if (e == env)
965 {
966 otherCompiler = c;
967 goto foundOtherCompiler;
968 }
969 foundOtherCompiler:
970
971 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
972 dispatch_sync(environmentQueue, ^{
973 otherCompiler->loadModulesAndSources(set<string>(), modulesDepOnModulesModified_otherCompiler[env], modulesDepOnModulesRemoved_otherCompiler[env],
974 set<string>(), sourcesDirectDepOnModulesModified_otherCompiler[env], sourcesDirectDepOnModulesRemoved_otherCompiler[env],
975 false, true, env, nullptr, nullptr, "");
976 });
977 });
978 }
979 }
980
981 // Unload:
982 // - modules that have been removed or modified
983 // - modules that depend on them
984
985 map<VuoCompilerEnvironment *, set<VuoCompilerModule *> > actualModulesRemoved;
986 for (const vector<VuoCompilerEnvironment *> environmentsAtScope : environments)
987 {
988 for (VuoCompilerEnvironment *env : environmentsAtScope)
989 {
990 set<string> modulesToUnload;
991 modulesToUnload.insert(modulesRemoved[env].begin(), modulesRemoved[env].end());
992 modulesToUnload.insert(modulesModified[env].begin(), modulesModified[env].end());
993 modulesToUnload.insert(modulesDepOnModulesRemoved[env].begin(), modulesDepOnModulesRemoved[env].end());
994 modulesToUnload.insert(modulesDepOnModulesModified[env].begin(), modulesDepOnModulesModified[env].end());
995
996 actualModulesRemoved[env] = env->unloadCompiledModules(modulesToUnload);
997 }
998 }
999
1000 // Load:
1001 // - modules that have been added or modified
1002 // - modules that they depend on
1003 // - modules that depend on them that were just unloaded and are not awaiting recompilation
1004 // Delete:
1005 // - cached module files in `modulesToLoad` whose source files have been removed
1006
1007 map<VuoCompilerEnvironment *, set<string> > modulesToLoad;
1008 map<VuoCompilerEnvironment *, map<string, string> > modulesToLoadSourceCode;
1009 for (const vector<VuoCompilerEnvironment *> environmentsAtScope : environments)
1010 {
1011 for (VuoCompilerEnvironment *env : environmentsAtScope)
1012 {
1013 auto moduleNotInvalidated = [env] (const string &moduleKey)
1014 {
1015 return invalidatedModulesAwaitingRecompilation[env].find(moduleKey) == invalidatedModulesAwaitingRecompilation[env].end();
1016 };
1017
1018 if (env == currentEnvironment && moduleLoadedCallback)
1019 {
1020 modulesToLoad[env].insert(modulesModified[env].begin(), modulesModified[env].end());
1021 modulesToLoad[env].insert(modulesAdded[env].begin(), modulesAdded[env].end());
1022 }
1023 else
1024 {
1025 std::copy_if(modulesAdded[env].begin(), modulesAdded[env].end(),
1026 std::inserter(modulesToLoad[env], modulesToLoad[env].end()),
1027 moduleNotInvalidated);
1028 std::copy_if(modulesModified[env].begin(), modulesModified[env].end(),
1029 std::inserter(modulesToLoad[env], modulesToLoad[env].end()),
1030 moduleNotInvalidated);
1031 }
1032
1033 std::copy_if(modulesDepOnModulesModified[env].begin(), modulesDepOnModulesModified[env].end(),
1034 std::inserter(modulesToLoad[env], modulesToLoad[env].end()),
1035 moduleNotInvalidated);
1036
1037 if (env == currentEnvironment && moduleLoadedCallback)
1038 {
1039 if (modulesAdded[env].size() == 1)
1040 modulesToLoadSourceCode[env][*modulesAdded[env].begin()] = moduleAddedOrModifiedSourceCode;
1041 else if (modulesModified[env].size() == 1)
1042 modulesToLoadSourceCode[env][*modulesModified[env].begin()] = moduleAddedOrModifiedSourceCode;
1043 }
1044 }
1045 }
1046
1047 set<string> modulesAttempted;
1048 modulesAttempted.insert(modulesAddedKeys.begin(), modulesAddedKeys.end());
1049 modulesAttempted.insert(modulesModifiedKeys.begin(), modulesModifiedKeys.end());
1050
1051 map<VuoCompilerEnvironment *, set<VuoCompilerModule *> > actualModulesAdded;
1052 map<string, VuoCompilerEnvironment *> broadestEnvironmentThatDependsOnModule;
1053 while (! modulesToLoad.empty())
1054 {
1055 map<VuoCompilerEnvironment *, set<string>> dependenciesToLoad;
1056
1057 for (const vector<VuoCompilerEnvironment *> environmentsAtScope : environments)
1058 {
1059 for (VuoCompilerEnvironment *env : environmentsAtScope)
1060 {
1061 set<VuoCompilerModule *> actualModulesLoaded = env->loadCompiledModules(modulesToLoad[env], modulesToLoadSourceCode[env], llvmQueue);
1062
1063 actualModulesAdded[env].insert(actualModulesLoaded.begin(), actualModulesLoaded.end());
1064 modulesToLoad.erase(env);
1065
1066 // List dependencies of the modules just loaded.
1067 for (set<VuoCompilerModule *>::iterator j = actualModulesLoaded.begin(); j != actualModulesLoaded.end(); ++j)
1068 {
1069 set<string> dependencies = (*j)->getDependencies();
1070 dependenciesToLoad[env].insert(dependencies.begin(), dependencies.end());
1071
1072 for (const string &dependency : dependencies)
1073 {
1074 VuoCompilerEnvironment *currEnv = broadestEnvironmentThatDependsOnModule[dependency];
1075 broadestEnvironmentThatDependsOnModule[dependency] = currEnv ? environmentAtBroaderScope(currEnv, env) : env;
1076 }
1077
1078 modulesAttempted.insert(dependencies.begin(), dependencies.end());
1079 }
1080 }
1081 }
1082
1083 // Locate the module info for each dependency. Restrict the search to scopes that the dependent module can access.
1084 for (auto i : dependenciesToLoad)
1085 {
1086 VuoCompilerEnvironment *dependentEnv = i.first;
1087
1088 for (const vector<VuoCompilerEnvironment *> environmentsAtScope : environments)
1089 {
1090 for (VuoCompilerEnvironment *env : environmentsAtScope)
1091 {
1092 VuoModuleInfoIterator dependenciesInEnv = env->listModules(i.second);
1093 VuoModuleInfo *moduleInfo;
1094 while ((moduleInfo = dependenciesInEnv.next()))
1095 {
1096 string moduleKey = moduleInfo->getModuleKey();
1097 modulesToLoad[env].insert(moduleKey);
1098 }
1099 }
1100
1101 if (find(environmentsAtScope.begin(), environmentsAtScope.end(), dependentEnv) != environmentsAtScope.end())
1102 break;
1103 }
1104 }
1105 }
1106
1107 // Notify those waiting on a source file to be compiled that its module has now been loaded.
1108
1109 if (moduleLoadedCallback)
1110 moduleLoadedCallback();
1111
1112 // Load asynchronously:
1113 // - specializations of generic modules
1114 //
1115 // The set of module keys that have been attempted but not yet loaded may include some specializations of
1116 // generic modules (along with things that can't be loaded as modules, e.g. dynamic libraries and frameworks).
1117 // Organize these potential specialized modules by the environment that should load them, then let the
1118 // environment attempt to load them and sort out which are actually specialized modules.
1119
1120 vector<string> actualModulesAddedKeys;
1121 for (auto i : actualModulesAdded)
1122 for (VuoCompilerModule *module : i.second)
1123 actualModulesAddedKeys.push_back(module->getPseudoBase()->getModuleKey());
1124
1125 vector<string> modulesNotAdded;
1126 std::set_difference(modulesAttempted.begin(), modulesAttempted.end(),
1127 actualModulesAddedKeys.begin(), actualModulesAddedKeys.end(),
1128 std::back_inserter(modulesNotAdded));
1129
1130 auto moduleNotAlreadyLoaded = [this] (const string &moduleKey)
1131 {
1132 for (const vector<VuoCompilerEnvironment *> environmentsAtScope : environments)
1133 for (VuoCompilerEnvironment *env : environmentsAtScope)
1134 if (env->findModule(moduleKey))
1135 return false;
1136
1137 return true;
1138 };
1139
1140 set<string> potentialSpecializedModules;
1141 std::copy_if(modulesNotAdded.begin(), modulesNotAdded.end(),
1142 std::inserter(potentialSpecializedModules, potentialSpecializedModules.begin()),
1143 moduleNotAlreadyLoaded);
1144
1145 // If the potential specialized module was enqueued to be loaded because it's a dependency of one or more
1146 // other modules, we'll load the specialized module at a scope that all of those modules can access.
1147 // Otherwise, at the default scope for generated modules.
1148 map<VuoCompilerEnvironment *, set<string>> potentialSpecializedModulesByEnvironment;
1149 for (const string &moduleKey : potentialSpecializedModules)
1150 {
1151 VuoCompilerEnvironment *envForLoadingModule = generatedEnvironment;
1152
1153 auto envIter = broadestEnvironmentThatDependsOnModule.find(moduleKey);
1154 if (envIter != broadestEnvironmentThatDependsOnModule.end())
1155 envForLoadingModule = generatedEnvironmentAtSameScopeAs(envIter->second);
1156
1157 potentialSpecializedModulesByEnvironment[envForLoadingModule].insert(moduleKey);
1158 }
1159
1160 set<dispatch_group_t> specializedModulesLoading;
1161 for (auto i : potentialSpecializedModulesByEnvironment)
1162 {
1163 VuoCompilerEnvironment *env = i.first;
1164 set<dispatch_group_t> s = env->generateSpecializedModules(i.second, this, moduleSourceCompilersExist, llvmQueue);
1165 specializedModulesLoading.insert(s.begin(), s.end());
1166 }
1167
1168 // Delete cached compiled module files and call this function recursively for:
1169 // - modules whose source files have been removed
1170 // - modules whose source files depend on them
1171
1172 for (vector< vector<VuoCompilerEnvironment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
1173 {
1174 VuoCompilerEnvironment *env = (*i).at(0);
1175
1176 set<string> sourcesToUnload;
1177 sourcesToUnload.insert(sourcesRemoved[env].begin(), sourcesRemoved[env].end());
1178 sourcesToUnload.insert(sourcesDepOnModulesRemoved[env].begin(), sourcesDepOnModulesRemoved[env].end());
1179 if (! sourcesToUnload.empty())
1180 {
1181 string moduleSearchPath = env->getModuleSearchPaths().front();
1182
1183 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
1184 VuoCompiler *otherCompiler = new VuoCompiler(moduleSearchPath + "/unused", target);
1185
1186 dispatch_sync(environmentQueue, ^{
1187 otherCompiler->loadModulesAndSources(set<string>(), set<string>(), sourcesToUnload,
1188 set<string>(), set<string>(), set<string>(),
1189 false, false, env, nullptr, nullptr, "");
1190 });
1191
1192 delete otherCompiler;
1193 });
1194 }
1195
1196 if (!env->isBuiltInOriginal() && !sourcesToUnload.empty())
1197 VUserLog("Deleting compiled module from %s cache: %s", env->getName().c_str(), VuoStringUtilities::join(sourcesToUnload, ", ").c_str());
1198
1199 env->deleteFromCompiledModuleCache(sourcesToUnload);
1200 }
1201
1202 // Compile asynchronously:
1203 // - source files that have been added or modified and are not already awaiting recompilation
1204 // - source files that directly depend on them
1205 // - source files that directly depend on modules that have been modified
1206
1207 map<VuoCompilerEnvironment *, set<string> > sourcesDirectDepOnModulesAdded;
1208 {
1209 map<VuoCompilerEnvironment *, set<string> > modulesDirectDepOnModulesAdded; // unused
1210 map<VuoCompilerEnvironment *, set<string> > modulesDirectDepOnModulesAdded_otherCompiler; //
1211 __block map<VuoCompilerEnvironment *, set<string> > sourcesDirectDepOnModulesAdded_otherCompiler;
1212
1213 map<VuoCompilerEnvironment *, set<string> > actualModuleKeysAdded;
1214 for (const vector<VuoCompilerEnvironment *> &envs : environments)
1215 {
1216 VuoCompilerEnvironment *env = envs.at(0);
1217 for (VuoCompilerModule *module : actualModulesAdded[env])
1218 actualModuleKeysAdded[env].insert( module->getPseudoBase()->getModuleKey() );
1219 }
1220
1221 vector<VuoDirectedAcyclicNetwork *> searchDependencyGraphs;
1222 searchDependencyGraphs.push_back(compositionDependencyGraph);
1223 for (map<string, vector<VuoCompilerEnvironment *> >::iterator ii = environmentsForCompositionFamily[target].begin(); ii != environmentsForCompositionFamily[target].end(); ++ii)
1224 {
1225 vector< vector<VuoCompilerEnvironment *> > otherEnvs = sharedEnvironments[target];
1226 otherEnvs.push_back(ii->second);
1227 VuoDirectedAcyclicNetwork *other = makeDependencyNetwork(otherEnvs, ^VuoDirectedAcyclicGraph * (VuoCompilerEnvironment *env) { return env->getCompositionDependencyGraph(); });
1228 searchDependencyGraphs.push_back(other);
1229 }
1230
1231 VuoDirectedAcyclicGraph *currentEnvironmentDependencyGraph = (currentEnvironment ? currentEnvironment->getCompositionDependencyGraph() : nullptr);
1232
1233 findDependentModulesAndSources(actualModuleKeysAdded, searchDependencyGraphs, currentEnvironmentDependencyGraph, false,
1234 modulesDirectDepOnModulesAdded, modulesDirectDepOnModulesAdded_otherCompiler,
1235 sourcesDirectDepOnModulesAdded, sourcesDirectDepOnModulesAdded_otherCompiler);
1236
1237 set<VuoCompilerEnvironment *> otherEnvironments;
1238 for (map<VuoCompilerEnvironment *, set<string> >::iterator i = sourcesDirectDepOnModulesAdded_otherCompiler.begin(); i != sourcesDirectDepOnModulesAdded_otherCompiler.end(); ++i)
1239 otherEnvironments.insert(i->first);
1240
1241 for (VuoCompilerEnvironment *env : otherEnvironments)
1242 {
1243 dispatch_group_enter(moduleSourceCompilersExist);
1244 dispatch_group_enter(moduleSourceCompilersExistGlobally);
1245
1246 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
1247 string moduleSearchPath = env->getModuleSearchPaths().front();
1248 VuoCompiler *otherCompiler = new VuoCompiler(moduleSearchPath + "/unused", target);
1249
1250 dispatch_sync(environmentQueue, ^{
1251 otherCompiler->loadModulesAndSources(set<string>(), set<string>(), set<string>(),
1252 sourcesDirectDepOnModulesAdded_otherCompiler[env], set<string>(), set<string>(),
1253 false, true, env, nullptr, nullptr, "");
1254 });
1255
1256 delete otherCompiler;
1257
1258 dispatch_group_leave(moduleSourceCompilersExist);
1259 dispatch_group_leave(moduleSourceCompilersExistGlobally);
1260 });
1261 }
1262 }
1263
1264 set<dispatch_group_t> sourcesLoading;
1265 for (const vector<VuoCompilerEnvironment *> &envs : environments)
1266 {
1267 VuoCompilerEnvironment *env = envs.at(0);
1268
1269 auto moduleNotInvalidated = [env] (const string &moduleKey)
1270 {
1271 return invalidatedModulesAwaitingRecompilation[env].find(moduleKey) == invalidatedModulesAwaitingRecompilation[env].end();
1272 };
1273
1274 set<string> sourcesToCompile;
1275 set<string> otherSourcesForCallerToWaitOn;
1276 if (env == currentEnvironment)
1277 {
1278 sourcesToCompile.insert(sourcesAdded[env].begin(), sourcesAdded[env].end());
1279 sourcesToCompile.insert(sourcesModified[env].begin(), sourcesModified[env].end());
1280 }
1281 else
1282 {
1283 set<string> addedSourcesToCompile;
1284 set<string> modifiedSourcesToCompile;
1285
1286 std::copy_if(sourcesAdded[env].begin(), sourcesAdded[env].end(),
1287 std::inserter(addedSourcesToCompile, addedSourcesToCompile.end()),
1288 moduleNotInvalidated);
1289 std::copy_if(sourcesModified[env].begin(), sourcesModified[env].end(),
1290 std::inserter(modifiedSourcesToCompile, modifiedSourcesToCompile.end()),
1291 moduleNotInvalidated);
1292
1293 sourcesToCompile.insert(addedSourcesToCompile.begin(), addedSourcesToCompile.end());
1294 sourcesToCompile.insert(modifiedSourcesToCompile.begin(), modifiedSourcesToCompile.end());
1295
1296 std::set_difference(sourcesAdded[env].begin(), sourcesAdded[env].end(),
1297 addedSourcesToCompile.begin(), addedSourcesToCompile.end(),
1298 std::inserter(otherSourcesForCallerToWaitOn, otherSourcesForCallerToWaitOn.end()));
1299 std::set_difference(sourcesModified[env].begin(), sourcesModified[env].end(),
1300 modifiedSourcesToCompile.begin(), modifiedSourcesToCompile.end(),
1301 std::inserter(otherSourcesForCallerToWaitOn, otherSourcesForCallerToWaitOn.end()));
1302 }
1303
1304 if (! sourcesToCompile.empty())
1305 {
1306 set<dispatch_group_t> s = env->compileModulesFromSourceCode(sourcesToCompile, shouldRecompileSourcesIfUnchanged, moduleSourceCompilersExist, llvmQueue);
1307 sourcesLoading.insert(s.begin(), s.end());
1308 }
1309
1310 for (const string &moduleKey : otherSourcesForCallerToWaitOn)
1311 {
1312 dispatch_group_t preloadingGroup = invalidatedModulesAwaitingRecompilation[env][moduleKey].second;
1313 if (! preloadingGroup)
1314 {
1315 preloadingGroup = dispatch_group_create();
1316 dispatch_group_enter(preloadingGroup);
1317 invalidatedModulesAwaitingRecompilation[env][moduleKey].second = preloadingGroup;
1318 }
1319 sourcesLoading.insert(preloadingGroup);
1320 }
1321 }
1322
1323 for (vector< vector<VuoCompilerEnvironment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
1324 {
1325 VuoCompilerEnvironment *env = (*i).at(0);
1326
1327 set<string> sourcesToCompile;
1328 sourcesToCompile.insert(sourcesDirectDepOnModulesAdded[env].begin(), sourcesDirectDepOnModulesAdded[env].end());
1329 sourcesToCompile.insert(sourcesDirectDepOnModulesModified[env].begin(), sourcesDirectDepOnModulesModified[env].end());
1330
1331 if (! sourcesToCompile.empty())
1332 env->compileModulesFromSourceCode(sourcesToCompile, true, moduleSourceCompilersExist, llvmQueue);
1333 }
1334
1335 // Move modified modules from `actualModulesAdded` and `actualModulesRemoved`/`invalidatedModulesAwaitingRecompilation`
1336 // to `actualModulesModified`.
1337
1338 map<VuoCompilerEnvironment *, set< pair<VuoCompilerModule *, VuoCompilerModule *> > > actualModulesModified;
1339 for (const vector<VuoCompilerEnvironment *> environmentsAtScope : environments)
1340 {
1341 for (VuoCompilerEnvironment *env : environmentsAtScope)
1342 {
1343 for (set<VuoCompilerModule *>::iterator add = actualModulesAdded[env].begin(); add != actualModulesAdded[env].end(); )
1344 {
1345 VuoCompilerModule *addedModule = *add;
1346 string moduleKey = addedModule->getPseudoBase()->getModuleKey();
1347
1348 VuoCompilerModule *removedModule = nullptr;
1349 auto rem = std::find_if(actualModulesRemoved[env].begin(), actualModulesRemoved[env].end(),
1350 [moduleKey](VuoCompilerModule *m) { return m->getPseudoBase()->getModuleKey() == moduleKey; });
1351 if (rem != actualModulesRemoved[env].end())
1352 {
1353 removedModule = *rem;
1354 actualModulesRemoved[env].erase(rem);
1355 }
1356 else
1357 {
1358 auto invalidatedIter = invalidatedModulesAwaitingRecompilation[env].find(moduleKey);
1359 if (invalidatedIter != invalidatedModulesAwaitingRecompilation[env].end())
1360 {
1361 removedModule = invalidatedIter->second.first;
1362 dispatch_group_t preloadingGroup = invalidatedIter->second.second;
1363
1364 invalidatedModulesAwaitingRecompilation[env].erase(invalidatedIter);
1365
1366 if (preloadingGroup)
1367 dispatch_group_leave(preloadingGroup);
1368 }
1369 }
1370
1371 if (removedModule)
1372 {
1373 actualModulesModified[env].insert({removedModule, addedModule});
1374 actualModulesAdded[env].erase(add++);
1375 }
1376 else
1377 ++add;
1378 }
1379 }
1380 }
1381
1382 // Move previously invalidated modules whose recompilation has now failed
1383 // from `invalidatedModulesAwaitingRecompilation` to `actualModulesRemoved`.
1384
1385 if (currentEnvironment && moduleLoadedCallback)
1386 {
1387 for (const string &moduleKey : modulesRemoved[currentEnvironment])
1388 {
1389 auto foundIter = invalidatedModulesAwaitingRecompilation[currentEnvironment].find(moduleKey);
1390 if (foundIter != invalidatedModulesAwaitingRecompilation[currentEnvironment].end())
1391 {
1392 VuoCompilerModule *module = foundIter->second.first;
1393 dispatch_group_t preloadingGroup = foundIter->second.second;
1394
1395 actualModulesRemoved[currentEnvironment].insert(module);
1396 invalidatedModulesAwaitingRecompilation[currentEnvironment].erase(foundIter);
1397
1398 if (preloadingGroup)
1399 dispatch_group_leave(preloadingGroup);
1400 }
1401 }
1402 }
1403
1404 // Postpone notifying compiler delegates about invalidated modules that are awaiting recompilation.
1405 // Once they're recompiled, in a later call to this function, they'll be reported as modified.
1406
1407 for (const vector<VuoCompilerEnvironment *> environmentsAtScope : environments)
1408 {
1409 for (VuoCompilerEnvironment *env : environmentsAtScope)
1410 {
1411 for (auto i : invalidatedModulesAwaitingRecompilation[env])
1412 {
1413 VuoCompilerModule *module = i.second.first;
1414
1415 auto foundIter = actualModulesRemoved[env].find(module);
1416 if (foundIter != actualModulesRemoved[env].end())
1417 actualModulesRemoved[env].erase(foundIter);
1418 }
1419 }
1420 }
1421
1422 // Postpone notifying compiler delegates that a node class has been added/modified until its port types and
1423 // generic node class have been loaded — which may not happen until a later call to this function.
1424
1425 for (const vector<VuoCompilerEnvironment *> environmentsAtScope : environments)
1426 {
1427 for (VuoCompilerEnvironment *env : environmentsAtScope)
1428 {
1429 addedModulesAwaitingReification[env].insert(actualModulesAdded[env].begin(), actualModulesAdded[env].end());
1430 modifiedModulesAwaitingReification[env].insert(actualModulesModified[env].begin(), actualModulesModified[env].end());
1431 actualModulesAdded[env].clear();
1432 actualModulesModified[env].clear();
1433 }
1434 }
1435
1436 map<VuoCompilerEnvironment *, set<VuoCompilerModule *>> postponedModulesAdded;
1437 map<VuoCompilerEnvironment *, set<pair<VuoCompilerModule *, VuoCompilerModule *>>> postponedModulesModified;
1438
1439 // For each pending added/modified node class, reify its port types (if they've been loaded).
1440
1441 auto lookUpType = [this] (const string &moduleKey) -> VuoCompilerType *
1442 {
1443 for (auto i = environments.rbegin(); i != environments.rend(); ++i)
1444 {
1445 for (VuoCompilerEnvironment *env : *i)
1446 {
1447 VuoCompilerType *type = env->getType(moduleKey);
1448 if (type)
1449 return type;
1450 }
1451 }
1452
1453 return nullptr;
1454 };
1455
1456 auto reifyPortTypesForModule = [this, lookUpType] (VuoCompilerModule *module)
1457 {
1458 bool allReified = true;
1459
1460 VuoCompilerNodeClass *nodeClass = dynamic_cast<VuoCompilerNodeClass *>(module);
1461 if (nodeClass)
1462 allReified = reifyPortTypes(nodeClass, lookUpType);
1463
1464 return allReified;
1465 };
1466
1467 for (const vector<VuoCompilerEnvironment *> environmentsAtScope : environments)
1468 {
1469 for (VuoCompilerEnvironment *env : environmentsAtScope)
1470 {
1471 for (VuoCompilerModule *module : addedModulesAwaitingReification[env])
1472 if (! reifyPortTypesForModule(module))
1473 postponedModulesAdded[env].insert(module);
1474
1475 for (pair<VuoCompilerModule *, VuoCompilerModule *> i : modifiedModulesAwaitingReification[env])
1476 if (! reifyPortTypesForModule(i.second))
1477 postponedModulesModified[env].insert(i);
1478 }
1479 }
1480
1481 // For each pending added/modified specialized node class, fill in its references to node classes and types
1482 // (if they've been loaded).
1483
1484 auto lookUpGenericNodeClass = [this] (const string &nodeClassName) -> VuoCompilerNodeClass *
1485 {
1486 for (auto i = environments.rbegin(); i != environments.rend(); ++i)
1487 {
1488 VuoCompilerEnvironment *env = (*i).at(0);
1489 VuoCompilerNodeClass *nodeClass = env->getNodeClass(nodeClassName);
1490 if (nodeClass)
1491 return nodeClass;
1492 }
1493
1494 return nullptr;
1495 };
1496
1497 auto fillInModuleReferences = [lookUpGenericNodeClass, lookUpType] (VuoCompilerModule *module)
1498 {
1499 bool allFilledIn = true;
1500
1501 VuoCompilerSpecializedNodeClass *specializedNodeClass = dynamic_cast<VuoCompilerSpecializedNodeClass *>(module);
1502 if (specializedNodeClass)
1503 {
1504 allFilledIn = allFilledIn && specializedNodeClass->updateGenericNodeClass(lookUpGenericNodeClass);
1505
1506 VuoCompilerMakeListNodeClass *makeListNodeClass = dynamic_cast<VuoCompilerMakeListNodeClass *>(module);
1507 if (makeListNodeClass)
1508 allFilledIn = allFilledIn && makeListNodeClass->updateListType(lookUpType);
1509 }
1510
1511 return allFilledIn;
1512 };
1513
1514 for (const vector<VuoCompilerEnvironment *> environmentsAtScope : environments)
1515 {
1516 VuoCompilerEnvironment *env = environmentsAtScope.at(1);
1517
1518 for (VuoCompilerModule *module : addedModulesAwaitingReification[env])
1519 if (! fillInModuleReferences(module))
1520 postponedModulesAdded[env].insert(module);
1521
1522 for (pair<VuoCompilerModule *, VuoCompilerModule *> i : modifiedModulesAwaitingReification[env])
1523 if (! fillInModuleReferences(i.second))
1524 postponedModulesModified[env].insert(i);
1525 }
1526
1527 // Separate modules that are ready to be sent to compiler delegates from those that are being postponed.
1528
1529 for (const vector<VuoCompilerEnvironment *> environmentsAtScope : environments)
1530 {
1531 for (VuoCompilerEnvironment *env : environmentsAtScope)
1532 {
1533 std::set_difference(addedModulesAwaitingReification[env].begin(), addedModulesAwaitingReification[env].end(),
1534 postponedModulesAdded[env].begin(), postponedModulesAdded[env].end(),
1535 std::inserter(actualModulesAdded[env], actualModulesAdded[env].end()));
1536 std::set_difference(modifiedModulesAwaitingReification[env].begin(), modifiedModulesAwaitingReification[env].end(),
1537 postponedModulesModified[env].begin(), postponedModulesModified[env].end(),
1538 std::inserter(actualModulesModified[env], actualModulesModified[env].end()));
1539 addedModulesAwaitingReification[env] = postponedModulesAdded[env];
1540 modifiedModulesAwaitingReification[env] = postponedModulesModified[env];
1541 }
1542 }
1543
1544 // Notify compiler delegates of modules added, modified, and removed.
1545
1546 for (const vector<VuoCompilerEnvironment *> environmentsAtScope : environments)
1547 for (VuoCompilerEnvironment *env : environmentsAtScope)
1548 env->notifyCompilers(actualModulesAdded[env], actualModulesModified[env], actualModulesRemoved[env], issues[env]);
1549
1550 // Log modules added and removed.
1551
1552 auto logModuleUnloaded = [](VuoCompilerEnvironment *env, VuoCompilerModule *module)
1553 {
1554 if (! env->isBuiltInOriginal())
1555 VUserLog("Removed from %s environment: %s", env->getName().c_str(), module->getPseudoBase()->getModuleKey().c_str());
1556 };
1557
1558 for (const vector<VuoCompilerEnvironment *> environmentsAtScope : environments)
1559 {
1560 for (VuoCompilerEnvironment *env : environmentsAtScope)
1561 {
1562 for (pair<VuoCompilerModule *, VuoCompilerModule *> i : actualModulesModified[env])
1563 logModuleUnloaded(env, i.second);
1564
1565 for (VuoCompilerModule *module : actualModulesRemoved[env])
1566 logModuleUnloaded(env, module);
1567 }
1568 }
1569
1570 auto logModuleLoaded = [](VuoCompilerEnvironment *env, VuoCompilerModule *module)
1571 {
1572 if (! env->isBuiltIn())
1573 {
1574 string path, hash;
1575 if (module->getPseudoBase()->getNodeSet())
1576 path = module->getPseudoBase()->getNodeSet()->getArchivePath();
1577 else
1578 {
1579 auto cnc = dynamic_cast<VuoCompilerNodeClass *>(module);
1580 if (cnc && !cnc->getSourcePath().empty())
1581 {
1582 path = cnc->getSourcePath();
1583 if (!cnc->getSourceCode().empty())
1584 // Use the latest source code, if it's been modified without saving.
1585 hash = VuoStringUtilities::calculateSHA256(cnc->getSourceCode());
1586 }
1587 else
1588 path = module->getModulePath();
1589 }
1590
1591 if (hash.empty() && ! path.empty())
1592 {
1593 try
1594 {
1596 }
1597 catch (VuoException &e) {}
1598 }
1599
1600 if (hash.empty() && module->getModule())
1601 {
1602 string bitcode;
1603 raw_string_ostream out(bitcode);
1604 llvm::WriteBitcodeToFile(module->getModule(), out);
1606 }
1607
1608 if (path.empty())
1609 path = module->getPseudoBase()->getModuleKey();
1610
1611 if (env->isGenerated())
1612 VDebugLog("Loaded into %s environment: %s (%s)", env->getName().c_str(), module->getPseudoBase()->getModuleKey().c_str(), path.c_str());
1613 else
1614 VUserLog("Loaded into %s environment: [%8.8s] %s (%s)", env->getName().c_str(), hash.c_str(), module->getPseudoBase()->getModuleKey().c_str(), path.c_str());
1615 }
1616 };
1617
1618 for (const vector<VuoCompilerEnvironment *> environmentsAtScope : environments)
1619 {
1620 for (VuoCompilerEnvironment *env : environmentsAtScope)
1621 {
1622 for (VuoCompilerModule *module : actualModulesAdded[env])
1623 logModuleLoaded(env, module);
1624
1625 for (pair<VuoCompilerModule *, VuoCompilerModule *> i : actualModulesModified[env])
1626 logModuleLoaded(env, i.second);
1627 }
1628 }
1629
1630 // Restore the dependency graph to its acyclic status by removing any edges where a module in a generated environment
1631 // depends on a module in the installed environment at the same level of scope.
1632
1633 if (currentEnvironment == generatedEnvironment)
1634 {
1635 auto removeCyclesForDependencies = [] (VuoDirectedAcyclicGraph *installedDependencyGraph, VuoDirectedAcyclicGraph *generatedDependencyGraph,
1636 VuoCompilerModule *module)
1637 {
1638 for (const string &dependency : module->getDependencies())
1639 {
1640 VuoDirectedAcyclicGraph::Vertex *generatedVertex = generatedDependencyGraph->findVertex(dependency);
1641 for (VuoDirectedAcyclicGraph::Vertex *toVertex : generatedDependencyGraph->getImmediatelyDownstreamVertices(generatedVertex))
1642 {
1643 VuoDirectedAcyclicGraph::Vertex *installedVertex = installedDependencyGraph->findVertex(toVertex->key());
1644 if (installedVertex)
1645 generatedDependencyGraph->removeEdge(generatedVertex, toVertex);
1646 }
1647 }
1648 };
1649
1650 VuoDirectedAcyclicGraph *installedDependencyGraph = nullptr;
1651 for (const vector<VuoCompilerEnvironment *> &environmentsAtScope : environments)
1652 {
1653 if (environmentsAtScope.at(1) == currentEnvironment)
1654 {
1655 installedDependencyGraph = environmentsAtScope.at(0)->getDependencyGraph();
1656 break;
1657 }
1658 }
1659
1660 VuoDirectedAcyclicGraph *generatedDependencyGraph = currentEnvironment->getDependencyGraph();
1661
1662 for (VuoCompilerModule *m : actualModulesAdded[currentEnvironment])
1663 removeCyclesForDependencies(installedDependencyGraph, generatedDependencyGraph, m);
1664 for (pair<VuoCompilerModule *, VuoCompilerModule *> i : actualModulesModified[currentEnvironment])
1665 removeCyclesForDependencies(installedDependencyGraph, generatedDependencyGraph, i.first);
1666 }
1667
1668 // Since the dispatch groups for specialized modules are temporary (caller is responsible for releasing them)
1669 // but the dispatch groups for module sources should stay alive as long as the VuoModuleInfo that contains them,
1670 // retain the dispatch groups for module sources so that all dispatch groups can be safely released by the caller.
1671
1672 for (const dispatch_group_t &group : sourcesLoading)
1673 dispatch_retain(group);
1674
1675 set<dispatch_group_t> loadingGroups;
1676 loadingGroups.insert(specializedModulesLoading.begin(), specializedModulesLoading.end());
1677 loadingGroups.insert(sourcesLoading.begin(), sourcesLoading.end());
1678 return loadingGroups;
1679}
1680
1693void VuoCompiler::findDependentModulesAndSources(map<VuoCompilerEnvironment *, set<string> > &changedModules,
1694 const vector<VuoDirectedAcyclicNetwork *> &searchDependencyGraphs,
1695 VuoDirectedAcyclicGraph *currentEnvironmentDependencyGraph, bool includeIndirectDependents,
1696 map<VuoCompilerEnvironment *, set<string> > &modulesDepOnChangedModules_this,
1697 map<VuoCompilerEnvironment *, set<string> > &modulesDepOnChangedModules_other,
1698 map<VuoCompilerEnvironment *, set<string> > &sourcesDepOnChangedModules_this,
1699 map<VuoCompilerEnvironment *, set<string> > &sourcesDepOnChangedModules_other)
1700{
1701 for (const vector<VuoCompilerEnvironment *> &environmentsAtScope : environments)
1702 {
1703 for (VuoCompilerEnvironment *env : environmentsAtScope)
1704 {
1705 for (const string &module : changedModules[env])
1706 {
1707 // Find dependents in the dependency graph.
1708
1709 set<VuoDirectedAcyclicGraph::Vertex *> dependents;
1710 for (VuoDirectedAcyclicNetwork *searchDependencyGraph : searchDependencyGraphs)
1711 {
1712 vector<VuoDirectedAcyclicGraph::Vertex *> moduleVertices;
1713 if (currentEnvironmentDependencyGraph)
1714 {
1715 // If a module with the same module key is installed in multiple locations,
1716 // only consider the one being modified or removed.
1717 VuoDirectedAcyclicGraph::Vertex *mv = currentEnvironmentDependencyGraph->findVertex(module);
1718 if (mv)
1719 moduleVertices.push_back(mv);
1720 }
1721 else
1722 moduleVertices = searchDependencyGraph->findVertex(module);
1723
1724 for (VuoDirectedAcyclicGraph::Vertex *moduleVertexRaw : moduleVertices)
1725 {
1726 VuoDependencyGraphVertex *moduleVertex = static_cast<VuoDependencyGraphVertex *>(moduleVertexRaw);
1727 if (moduleVertex->getEnvironment())
1728 {
1729 vector<VuoDirectedAcyclicGraph::Vertex *> upstream = includeIndirectDependents ?
1730 searchDependencyGraph->getUpstreamVertices(moduleVertex) :
1731 searchDependencyGraph->getImmediatelyUpstreamVertices(moduleVertex);
1732 dependents.insert(upstream.begin(), upstream.end());
1733 }
1734 }
1735 }
1736
1737 set< pair<VuoCompilerEnvironment *, string> > dependentsMap;
1738 for (VuoDirectedAcyclicGraph::Vertex *dependentVertexRaw : dependents)
1739 {
1740 VuoDependencyGraphVertex *v = static_cast<VuoDependencyGraphVertex *>(dependentVertexRaw);
1741 VuoCompilerEnvironment *dependentEnv = v->getEnvironment();
1742 if (! dependentEnv)
1743 continue;
1744
1745 string dependent = v->getDependency();
1746
1747 dependentsMap.insert({dependentEnv, dependent});
1748 }
1749
1750 // Add in dependents that were omitted from the dependency graph because they would make it cyclic —
1751 // a generated module depends on an installed module at the same scope.
1752
1753 if (! env->isGenerated())
1754 {
1755 VuoCompilerEnvironment *generatedEnvironmentAtScope = environmentsAtScope.at(1);
1756
1757 auto addDependentIfNeeded = [&module, &dependentsMap] (VuoCompilerEnvironment *env, VuoCompilerModule *potentialDependentModule)
1758 {
1759 set<string> dependencies = potentialDependentModule->getDependencies();
1760 if (dependencies.find(module) != dependencies.end())
1761 dependentsMap.insert({env, potentialDependentModule->getPseudoBase()->getModuleKey()});
1762 };
1763
1764 for (auto i : generatedEnvironmentAtScope->getNodeClasses())
1765 addDependentIfNeeded(generatedEnvironmentAtScope, i.second);
1766 for (auto i : generatedEnvironmentAtScope->getTypes())
1767 addDependentIfNeeded(generatedEnvironmentAtScope, i.second);
1768 for (auto i : generatedEnvironmentAtScope->getLibraryModules())
1769 addDependentIfNeeded(generatedEnvironmentAtScope, i.second);
1770 }
1771
1772 // Distribute the dependent modules and sources to the appropriate lists (output parameters).
1773
1774 for (auto i : dependentsMap)
1775 {
1776 VuoCompilerEnvironment *dependentEnv = i.first;
1777 string dependent = i.second;
1778
1779 // Skip if the dependent module is already being modified/removed in its own right
1780 // (e.g. if the module depends on another in the same node set and the node set is being removed).
1781 if (changedModules[dependentEnv].find(dependent) != changedModules[dependentEnv].end())
1782 continue;
1783
1784 VuoModuleInfo *foundSourceInfo = dependentEnv->listSourceFile(dependent);
1785 VuoModuleInfo *foundModuleInfo = dependentEnv->listModule(dependent);
1786
1787 bool belongsToCurrentCompiler = false;
1788 for (const vector<VuoCompilerEnvironment *> &envs2 : environments)
1789 {
1790 if (find(envs2.begin(), envs2.end(), dependentEnv) != envs2.end())
1791 {
1792 belongsToCurrentCompiler = true;
1793 break;
1794 }
1795 }
1796
1797 auto distributeToList = [dependentEnv, dependent] (map<VuoCompilerEnvironment *, set<string>> *dependentsList, VuoModuleInfo *moduleInfo)
1798 {
1799 (*dependentsList)[dependentEnv].insert(dependent);
1800 if (moduleInfo)
1801 moduleInfo->setAttempted(false);
1802 };
1803
1804 if (foundSourceInfo)
1805 distributeToList(belongsToCurrentCompiler ? &sourcesDepOnChangedModules_this : &sourcesDepOnChangedModules_other, foundSourceInfo);
1806
1807 if (foundModuleInfo)
1808 distributeToList(belongsToCurrentCompiler ? &modulesDepOnChangedModules_this : &modulesDepOnChangedModules_other, foundModuleInfo);
1809
1810 if (! foundSourceInfo && ! foundModuleInfo) // module in generated environment
1811 distributeToList(belongsToCurrentCompiler ? &modulesDepOnChangedModules_this : &modulesDepOnChangedModules_other, nullptr);
1812 }
1813 }
1814 }
1815 }
1816}
1817
1821void VuoCompiler::loadedModules(map<string, VuoCompilerModule *> modulesAdded,
1822 map<string, pair<VuoCompilerModule *, VuoCompilerModule *> > modulesModified,
1823 map<string, VuoCompilerModule *> modulesRemoved,
1824 VuoCompilerIssues *issues, void *delegateDataV, VuoCompilerEnvironment *currentEnvironment)
1825{
1826 //VLog("C=%p %lu %lu %lu", this, modulesAdded.size(), modulesModified.size(), modulesRemoved.size());
1827
1828 // If a module is added, superseding a version of the same module installed at a broader scope,
1829 // notify this VuoCompiler that the module has been modified rather than added.
1830 //
1831 // If a module is added, but a version of the same module is already installed at a narrower scope,
1832 // don't notify this VuoCompiler, since it will continue to use the version at the narrower scope.
1833 //
1834 // Same idea when a module is modified or removed while another version is installed at a different scope.
1835
1836 auto findVersionsOfModule = [this, currentEnvironment] (const string &moduleKey)
1837 {
1838 vector< pair<VuoCompilerEnvironment *, VuoCompilerModule *> > moduleVersions;
1839 for (const vector<VuoCompilerEnvironment *> &envs : environments)
1840 {
1841 VuoCompilerEnvironment *env = envs.at(0);
1842 VuoCompilerModule *module = env->findModule(moduleKey);
1843 if (module || env == currentEnvironment)
1844 moduleVersions.push_back( make_pair(env, module) );
1845 }
1846 return moduleVersions;
1847 };
1848
1849 for (map<string, VuoCompilerModule *>::iterator i = modulesAdded.begin(); i != modulesAdded.end(); )
1850 {
1851 string moduleKey = i->first;
1852 VuoCompilerModule *moduleAdded = i->second;
1853
1854 vector< pair<VuoCompilerEnvironment *, VuoCompilerModule *> > moduleVersions = findVersionsOfModule(moduleKey);
1855
1856 if (moduleVersions.size() > 1)
1857 {
1858 modulesAdded.erase(i++);
1859
1860 if (moduleVersions.back().second == moduleAdded)
1861 {
1862 VuoCompilerModule *moduleSuperseded = moduleVersions.at(moduleVersions.size()-2).second;
1863 modulesModified[moduleKey] = make_pair(moduleSuperseded, moduleAdded);
1864 }
1865 }
1866 else
1867 ++i;
1868 }
1869
1870 for (map<string, pair<VuoCompilerModule *, VuoCompilerModule *> >::iterator i = modulesModified.begin(); i != modulesModified.end(); )
1871 {
1872 string moduleKey = i->first;
1873 VuoCompilerModule *moduleModified = i->second.second;
1874
1875 vector< pair<VuoCompilerEnvironment *, VuoCompilerModule *> > moduleVersions = findVersionsOfModule(moduleKey);
1876
1877 if (moduleVersions.size() > 1 && moduleVersions.back().second != moduleModified)
1878 modulesModified.erase(i++);
1879 else
1880 ++i;
1881 }
1882
1883 for (map<string, VuoCompilerModule *>::iterator i = modulesRemoved.begin(); i != modulesRemoved.end(); )
1884 {
1885 string moduleKey = i->first;
1886 VuoCompilerModule *moduleRemoved = i->second;
1887
1888 vector< pair<VuoCompilerEnvironment *, VuoCompilerModule *> > moduleVersions = findVersionsOfModule(moduleKey);
1889
1890 if (moduleVersions.size() > 1)
1891 {
1892 modulesRemoved.erase(i++);
1893
1894 if (moduleVersions.back().first == currentEnvironment)
1895 {
1896 VuoCompilerModule *moduleUnsuperseded = moduleVersions.at(moduleVersions.size()-2).second;
1897 modulesModified[moduleKey] = make_pair(moduleRemoved, moduleUnsuperseded);
1898 }
1899 }
1900 else
1901 ++i;
1902 }
1903
1904 // Synchronously log any issues, to make sure they get recorded before any resulting crashes.
1905
1906 if (! issues->isEmpty())
1907 VUserLog("%s", issues->getLongDescription(false).c_str());
1908
1909 // Asynchronously notify the delegate (if any).
1910
1911 dispatch_async(delegateQueue, ^{
1912 VuoCompilerDelegate::LoadedModulesData *delegateData = static_cast<VuoCompilerDelegate::LoadedModulesData *>(delegateDataV);
1913
1914 if (delegate && ! (modulesAdded.empty() && modulesModified.empty() && modulesRemoved.empty() && issues->isEmpty()))
1915 {
1916 delegate->enqueueData(delegateData);
1917 delegate->loadedModules(modulesAdded, modulesModified, modulesRemoved, issues);
1918 }
1919 else
1920 {
1921 delegateData->release();
1922 }
1923 });
1924}
1925
1933bool VuoCompiler::reifyPortTypes(VuoCompilerNodeClass *nodeClass, std::function<VuoCompilerType *(const string &)> lookUpType)
1934{
1935 bool missingTypes = false;
1936
1937 auto setPortTypesForNodeClass = [lookUpType, &missingTypes] (const vector<VuoPortClass *> portClasses)
1938 {
1939 for (VuoPortClass *portClass : portClasses)
1940 {
1941 VuoCompilerPortClass *compilerPortClass = static_cast<VuoCompilerPortClass *>(portClass->getCompiler());
1942 VuoType *type = compilerPortClass->getDataVuoType();
1943
1944 if (type && ! type->hasCompiler())
1945 {
1946 string typeName = type->getModuleKey();
1947
1948 VuoCompilerType *reifiedType = lookUpType(typeName);
1949 if (! reifiedType)
1950 {
1951 VuoGenericType *genericType = dynamic_cast<VuoGenericType *>(type);
1952 if (genericType)
1953 reifiedType = VuoCompilerGenericType::newGenericType(genericType, lookUpType);
1954 }
1955
1956 if (reifiedType)
1957 compilerPortClass->setDataVuoType(reifiedType->getBase());
1958 }
1959
1960 if (compilerPortClass->getDataVuoType() && ! compilerPortClass->getDataVuoType()->hasCompiler())
1961 missingTypes = true;
1962 }
1963 };
1964 setPortTypesForNodeClass(nodeClass->getBase()->getInputPortClasses());
1965 setPortTypesForNodeClass(nodeClass->getBase()->getOutputPortClasses());
1966
1967 for (VuoCompilerTriggerDescription *trigger : nodeClass->getTriggerDescriptions())
1968 {
1969 VuoType *type = trigger->getDataType();
1970
1971 if (type && ! type->hasCompiler())
1972 {
1973 string typeName = type->getModuleKey();
1974 VuoCompilerType *reifiedType = lookUpType(typeName);
1975 if (reifiedType)
1976 trigger->setDataType(reifiedType->getBase());
1977 }
1978
1979 if (trigger->getDataType() && ! trigger->getDataType()->hasCompiler())
1980 missingTypes = true;
1981 }
1982
1983 return ! missingTypes;
1984}
1985
1989void VuoCompiler::reifyGenericPortTypes(VuoCompilerComposition *composition)
1990{
1991 for (VuoCompilerNode *node : composition->getCachedGraph(this)->getNodes())
1992 reifyGenericPortTypes(node->getBase());
1993
1994 composition->invalidateCachedGraph();
1995}
1996
2000void VuoCompiler::reifyGenericPortTypes(VuoNode *node)
2001{
2003 if (! nodeClass)
2004 return;
2005
2006 // Reify any generic types on the node that don't already have a compiler detail.
2007
2008 vector<VuoPort *> inputPorts = node->getInputPorts();
2009 vector<VuoPort *> outputPorts = node->getOutputPorts();
2010 vector<VuoPort *> ports;
2011 ports.insert(ports.end(), inputPorts.begin(), inputPorts.end());
2012 ports.insert(ports.end(), outputPorts.begin(), outputPorts.end());
2013
2014 for (vector<VuoPort *>::iterator j = ports.begin(); j != ports.end(); ++j)
2015 {
2016 VuoCompilerPort *port = static_cast<VuoCompilerPort *>((*j)->getCompiler());
2017 VuoGenericType *genericType = dynamic_cast<VuoGenericType *>(port->getDataVuoType());
2018 if (! genericType)
2019 continue;
2020
2021 if (! genericType->hasCompiler())
2022 {
2023 VuoCompilerType * (^compilerGetType)(string) = ^VuoCompilerType * (string moduleKey) {
2024 return getType(moduleKey);
2025 };
2026
2027 VuoCompilerGenericType *reifiedType = VuoCompilerGenericType::newGenericType(genericType, compilerGetType);
2028 if (reifiedType)
2029 port->setDataVuoType(reifiedType->getBase());
2030 }
2031 }
2032
2033 // Update the node class's backing to match the node's backing.
2034
2035 nodeClass->updateBackingNodeClass(node, this);
2036}
2037
2042VuoModuleCompiler * VuoCompiler::createModuleCompiler(const string &moduleKey, const string &inputPath,
2043 const map<string, string> &typeNameReplacements)
2044{
2046
2047 settings.target = target;
2048 settings.isVerbose = isVerbose;
2049
2051 if (settings.vuoFrameworkPath.empty())
2052 {
2053 settings.vuoFrameworkPath = VUO_BUILD_DIR "/lib/Vuo.framework";
2054
2055 // When loading compound types that are dependencies of the module being compiled, find their generic source files here.
2056 dispatch_sync(environmentQueue, ^{
2057 environments[0][0]->addModuleSearchPath(VUO_BUILD_DIR "/lib/Vuo.framework/Modules", false);
2058 });
2059 }
2060
2062 settings.macOSSDKPath = MACOS_SDK_ROOT;
2063
2064 vector<string> headerSearchPaths;
2065 applyToInstalledEnvironments([&headerSearchPaths](VuoCompilerEnvironment *env)
2066 {
2067 vector<string> envHeaderSearchPaths = env->getHeaderSearchPaths();
2068 headerSearchPaths.insert(headerSearchPaths.end(), envHeaderSearchPaths.begin(), envHeaderSearchPaths.end());
2069 });
2070 settings.headerSearchPaths = headerSearchPaths;
2071
2072 settings.typeNameReplacements = typeNameReplacements;
2073
2074 auto getVuoType = [this] (const string &moduleKey) { return this->getType(moduleKey); };
2075
2076 VuoModuleCompiler *moduleCompiler = nullptr;
2077 for (string type : {"c", "isf"})
2078 {
2079 moduleCompiler = VuoModuleCompiler::newModuleCompiler(type, moduleKey, inputPath, settings, getVuoType);
2080 if (moduleCompiler)
2081 break;
2082 }
2083
2084 return moduleCompiler;
2085}
2086
2095VuoModuleCompilerResults VuoCompiler::compileModuleInMemory(const string &inputPath, const string &overriddenSourceCode,
2096 const map<string, string> &typeNameReplacements)
2097{
2098 if (isVerbose)
2099 print();
2100
2101 string moduleKey = getModuleKeyForPath(inputPath);
2102
2103 VuoModuleCompiler *moduleCompiler = createModuleCompiler(moduleKey, inputPath, typeNameReplacements);
2104 if (! moduleCompiler)
2105 {
2106 VuoCompilerIssue issue(VuoCompilerIssue::Error, "compiling module", inputPath, "", "Unrecognized source file type.");
2107 throw VuoCompilerException(issue);
2108 }
2109
2110 if (! overriddenSourceCode.empty())
2111 moduleCompiler->overrideSourceCode(overriddenSourceCode, inputPath);
2112
2114 VuoModuleCompilerResults results = moduleCompiler->compile(llvmQueue, issues);
2115
2116 if (! results.module)
2117 throw VuoCompilerException(issues, true);
2118
2119 if (! issues->isEmpty())
2120 VUserLog("%s", issues->getLongDescription(false).c_str());
2121
2122 delete issues;
2123 delete moduleCompiler;
2124
2125 return results;
2126}
2127
2135void VuoCompiler::compileModule(const string &inputPath, const string &outputPath)
2136{
2138 Module *module = results.module;
2139
2140 string moduleKey = getModuleKeyForPath(inputPath);
2141
2142 __block VuoCompilerModule *vuoModule = nullptr;
2143 __block VuoCompilerIssues *issues = new VuoCompilerIssues();
2144 dispatch_sync(llvmQueue, ^{
2146
2147 if (vuoModule)
2148 {
2149 try
2150 {
2151 verifyModule(module, issues);
2152 writeModuleToBitcode(module, target, outputPath, issues);
2153
2154 if (! dependencyOutputPath.empty())
2155 {
2156 shared_ptr<VuoMakeDependencies> makeDependencies = results.makeDependencies;
2157 if (makeDependencies)
2158 makeDependencies->setCompiledFilePath(outputPath);
2159 else
2160 makeDependencies = VuoMakeDependencies::createFromComponents(outputPath, {inputPath});
2161
2162 makeDependencies->writeToFile(dependencyOutputPath);
2163 }
2164 }
2165 catch (VuoCompilerException &e) {}
2166 catch (VuoException &e)
2167 {
2168 VuoCompilerIssue issue(VuoCompilerIssue::Error, "compiling module", inputPath, "", e.what());
2169 issues->append(issue);
2170 }
2171 }
2172
2173 delete module;
2174 });
2175
2176 if (issues->hasErrors())
2177 throw VuoCompilerException(issues, true);
2178
2179 delete issues;
2180
2181 if (! vuoModule)
2182 {
2183 string dir, file, ext;
2184 VuoFileUtilities::splitPath(inputPath, dir, file, ext);
2185 VuoCompilerIssue issue(VuoCompilerIssue::Error, "compiling module", inputPath, "", "Not a node class, type, or library.");
2186 throw VuoCompilerException(issue);
2187 }
2188}
2189
2197void VuoCompiler::generateHeaderForModule(const string &inputPath, const string &outputPath)
2198{
2199 string moduleKey = getModuleKeyForPath(inputPath);
2200
2201 VuoModuleCompiler *moduleCompiler = createModuleCompiler(moduleKey, inputPath);
2202 if (! moduleCompiler)
2203 {
2204 VuoCompilerIssue issue(VuoCompilerIssue::Error, "generating header", inputPath, "", "Unrecognized source file type.");
2205 throw VuoCompilerException(issue);
2206 }
2207
2208 VuoCompilerIssues *issues = new VuoCompilerIssues();
2209 string headerContents = moduleCompiler->generateHeader(issues);
2210
2211 if (issues->hasErrors())
2212 throw VuoCompilerException(issues, true);
2213 else if (! issues->isEmpty())
2214 VUserLog("%s", issues->getLongDescription(false).c_str());
2215
2216 delete issues;
2217 delete moduleCompiler;
2218
2219 VuoFileUtilities::writeStringToFile(headerContents, outputPath);
2220}
2221
2225Module * VuoCompiler::compileCompositionToModule(VuoCompilerComposition *composition, const string &moduleKey, bool isTopLevelComposition,
2226 VuoCompilerIssues *issues)
2227{
2228 composition->check(issues);
2229
2230 reifyGenericPortTypes(composition);
2231
2233 isTopLevelComposition,
2234 moduleKey, this);
2235
2236 __block Module *module = nullptr;
2237 dispatch_sync(llvmQueue, ^{
2238 try
2239 {
2240 module = generator->generateBitcode();
2241 setTargetForModule(module, target);
2242 }
2243 catch (VuoCompilerException &e)
2244 {
2245 if (issues)
2246 issues->append(e.getIssues());
2247 else
2248 VUserLog("%s", e.getIssues()->getLongDescription(false).c_str());
2249 }
2250 });
2251
2252 delete generator;
2253
2254 return module;
2255}
2256
2267void VuoCompiler::compileComposition(VuoCompilerComposition *composition, string outputPath, bool isTopLevelComposition,
2268 VuoCompilerIssues *issues)
2269{
2270 bool ownsIssues = false;
2271 if (! issues)
2272 {
2273 issues = new VuoCompilerIssues;
2274 ownsIssues = true;
2275 }
2276
2277 string moduleKey = getModuleKeyForPath(outputPath);
2278 Module *module = compileCompositionToModule(composition, moduleKey, isTopLevelComposition, issues);
2279 if (!module)
2280 throw VuoCompilerException(issues, ownsIssues);
2281
2282 dispatch_sync(llvmQueue, ^{
2283 try
2284 {
2285 verifyModule(module, issues);
2286 writeModuleToBitcode(module, target, outputPath, issues);
2287 }
2288 catch (VuoCompilerException &e) {}
2289 });
2290
2291 if (issues->hasErrors())
2292 throw VuoCompilerException(issues, ownsIssues);
2293
2294 if (ownsIssues)
2295 delete issues;
2296}
2297
2310void VuoCompiler::compileComposition(string inputPath, string outputPath, bool isTopLevelComposition,
2311 VuoCompilerIssues *issues)
2312{
2313 VUserLog("Compiling '%s' (%s)…", inputPath.c_str(), target.c_str());
2314 if (isVerbose)
2315 print();
2316
2317 if (getCompositionLocalPath().empty())
2318 setCompositionPath(inputPath);
2319
2321 {
2322 VuoCompilerIssue issue(VuoCompilerIssue::Error, "opening composition", inputPath,
2323 "", "The composition file couldn't be read or was empty.");
2324 throw VuoCompilerException(issue);
2325 }
2326
2327 try
2328 {
2329 string compositionString = VuoFileUtilities::readFileToString(inputPath);
2330 compileCompositionString(compositionString, outputPath, isTopLevelComposition, issues);
2331 }
2332 catch (VuoCompilerException &e)
2333 {
2334 if (e.getIssues())
2335 e.getIssues()->setFilePathIfEmpty(inputPath);
2336 if (!issues && e.getIssues())
2337 VUserLog("%s", e.getIssues()->getLongDescription(false).c_str());
2338 throw;
2339 }
2340
2341 VUserLog("Done.");
2342}
2343
2354void VuoCompiler::compileCompositionString(const string &compositionString, string outputPath, bool isTopLevelComposition,
2355 VuoCompilerIssues *issues)
2356{
2358 compileComposition(composition, outputPath, isTopLevelComposition, issues);
2359
2360 VuoComposition *baseComposition = composition->getBase();
2361 delete composition;
2362 delete baseComposition;
2363}
2364
2380void VuoCompiler::linkCompositionToCreateExecutable(string inputPath, string outputPath, Optimization optimization, string rPath, bool shouldAdHocCodeSign)
2381{
2382 vector<string> rPaths = ! rPath.empty() ? vector<string>(1, rPath) : getRunPathSearchPaths(environments.back().front());
2383 linkCompositionToCreateExecutableOrDynamicLibrary(inputPath, outputPath, optimization, false, rPaths, shouldAdHocCodeSign);
2384}
2385
2403void VuoCompiler::linkCompositionToCreateDynamicLibrary(string inputPath, string outputPath, Optimization optimization, bool shouldAdHocCodeSign)
2404{
2405 vector<string> rPaths = getRunPathSearchPaths(environments.back().front());
2406 linkCompositionToCreateExecutableOrDynamicLibrary(inputPath, outputPath, optimization, true, rPaths, shouldAdHocCodeSign);
2407}
2408
2423void VuoCompiler::linkCompositionToCreateExecutableOrDynamicLibrary(string compiledCompositionPath, string linkedCompositionPath,
2424 Optimization optimization, bool isDylib, const vector<string> &rPaths,
2425 bool shouldAdHocCodeSign)
2426{
2427 if (isVerbose)
2428 print();
2429
2430 if (optimization == Optimization_ExistingModuleCaches)
2431 shouldLoadAllModules = false;
2432
2433 vector<shared_ptr<VuoModuleCacheRevision>> currentModuleCaches = useCurrentModuleCaches(optimization);
2434
2435 set<string> dependencies = getDependenciesForComposition(compiledCompositionPath);
2436
2437 VuoLinkerInputs linkerInputs;
2438 linkerInputs.addDependencies(dependencies, currentModuleCaches, this);
2439 linkerInputs.addExternalLibrary(compiledCompositionPath);
2440 linkerInputs.addVuoRuntime(this);
2441 if (! isDylib)
2442 linkerInputs.addVuoRuntimeMain(this);
2443
2444 link(linkedCompositionPath, linkerInputs, isDylib, rPaths, shouldAdHocCodeSign);
2445
2446 for (shared_ptr<VuoModuleCacheRevision> revision : currentModuleCaches)
2447 revision->disuse();
2448}
2449
2466void VuoCompiler::linkCompositionToCreateDynamicLibraries(string compiledCompositionPath, string linkedCompositionPath,
2467 VuoRunningCompositionLibraries *runningCompositionLibraries)
2468{
2469 if (isVerbose)
2470 print();
2471
2472 bool shouldAdHocCodeSign = false;
2473#if __arm64__
2474 shouldAdHocCodeSign = true;
2475#endif
2476
2477 // Get the dependencies used by the new resources and not the previous resources.
2478
2479 set<string> carriedOverDependencies = runningCompositionLibraries->getDependenciesLoaded();
2480 set<string> allDependencies = getDependenciesForComposition(compiledCompositionPath);
2481 set<string> addedDependencies;
2482 std::set_difference(allDependencies.begin(), allDependencies.end(),
2483 carriedOverDependencies.begin(), carriedOverDependencies.end(),
2484 std::inserter(addedDependencies, addedDependencies.end()));
2485
2486 // Get the libraries and frameworks used by the previous resources.
2487
2488 vector<string> carriedOverNonUnloadableLibraries = runningCompositionLibraries->getNonUnloadableLibrariesLoaded();
2489 vector<string> carriedOverUnloadableLibraries = runningCompositionLibraries->getUnloadableLibrariesLoaded();
2490 set<string> carriedOverExternalLibraries = runningCompositionLibraries->getExternalLibraries();
2491 set<string> carriedOverFrameworks = runningCompositionLibraries->getExternalFrameworks();
2492
2493 // Check if any of the module cache revisions used previously are no longer available.
2494 // If so, we'll need to replace the existing unloadable resource dylibs (since they may depend on those module cache revisions)
2495 // with a new resource dylib that provides all of the dependencies from the previous dylibs plus any new dependencies.
2496
2497 vector<shared_ptr<VuoModuleCacheRevision>> currentModuleCaches = useCurrentModuleCaches(Optimization_ModuleCaches);
2498
2499 vector<string> carriedOverModuleCaches = runningCompositionLibraries->getUnloadableCacheLibrariesLoaded();
2500 vector<string> defunctModuleCaches;
2501 set<string> dependenciesInDefunctModuleCaches;
2502 set<string> dependenciesInDefunctResources;
2503
2504 // Identify module cache revisions that `runningCompositionLibraries` has already been told are outdated.
2505 map<string, set<string>> defunctModuleCachesAndDependencies = runningCompositionLibraries->getCacheLibrariesEnqueuedToUnload();
2506 for (auto i : defunctModuleCachesAndDependencies)
2507 {
2508 defunctModuleCaches.push_back(i.first);
2509 dependenciesInDefunctModuleCaches.insert(i.second.begin(), i.second.end());
2510 }
2511
2512 // Identify any other module cache revisions that are no longer available because the module cache has been rebuilt.
2513 for (string carriedOverCachePath : carriedOverModuleCaches)
2514 {
2515 auto sameAsCarriedOverCachePath = [&carriedOverCachePath] (shared_ptr<VuoModuleCacheRevision> revision)
2516 {
2517 return revision->getDylibPath() == carriedOverCachePath;
2518 };
2519
2520 auto foundIter = std::find_if(currentModuleCaches.begin(), currentModuleCaches.end(), sameAsCarriedOverCachePath);
2521 if (foundIter == currentModuleCaches.end())
2522 {
2523 defunctModuleCaches.push_back(carriedOverCachePath);
2524
2525 set<string> dependenciesInCache = runningCompositionLibraries->enqueueCacheLibraryToUnload(carriedOverCachePath);
2526 dependenciesInDefunctModuleCaches.insert(dependenciesInCache.begin(), dependenciesInCache.end());
2527 }
2528 }
2529
2530 if (! defunctModuleCaches.empty())
2531 {
2532 dependenciesInDefunctResources = runningCompositionLibraries->enqueueAllUnloadableResourceLibrariesToUnload();
2533
2534 std::sort(carriedOverModuleCaches.begin(), carriedOverModuleCaches.end());
2535 std::sort(defunctModuleCaches.begin(), defunctModuleCaches.end());
2536
2537 carriedOverUnloadableLibraries.clear();
2538 std::set_difference(carriedOverModuleCaches.begin(), carriedOverModuleCaches.end(),
2539 defunctModuleCaches.begin(), defunctModuleCaches.end(),
2540 std::back_inserter(carriedOverUnloadableLibraries));
2541 }
2542
2543 // Link the new resource dylibs, if needed.
2544
2545 VuoLinkerInputs linkerInputsForAddedDependencies;
2546 VuoLinkerInputs linkerInputsForNonUnloadableResource;
2547 VuoLinkerInputs linkerInputsForUnloadableResource;
2548
2549 string nonUnloadableResourcePath;
2550 string unloadableResourcePath;
2551
2552 if (! addedDependencies.empty() || ! dependenciesInDefunctModuleCaches.empty() || ! dependenciesInDefunctResources.empty())
2553 {
2554 // Look up the linker inputs for dependencies used by the new resources that are not provided by the carried-over resources.
2555
2556 linkerInputsForAddedDependencies.addDependencies(addedDependencies, currentModuleCaches, this);
2557 linkerInputsForAddedDependencies.addDependencies(dependenciesInDefunctModuleCaches, currentModuleCaches, this);
2558 linkerInputsForAddedDependencies.addDependencies(dependenciesInDefunctResources, currentModuleCaches, this);
2559
2560 string dir, linkedCompositionFile, ext;
2561 VuoFileUtilities::splitPath(linkedCompositionPath, dir, linkedCompositionFile, ext);
2562
2563 // If built-in dependencies were added, create an additional resource dylib.
2564
2565 bool wereBuiltInModulesAdded = ! linkerInputsForAddedDependencies.getModulesInBuiltInEnvironments().empty();
2566 bool wereBuiltInLibrariesAdded = ! linkerInputsForAddedDependencies.getLibrariesInBuiltInEnvironments().empty();
2567 if (wereBuiltInModulesAdded || wereBuiltInLibrariesAdded)
2568 {
2569 nonUnloadableResourcePath = VuoFileUtilities::makeTmpFile(linkedCompositionFile + "-resource-nonunloadable", "dylib");
2570
2571 linkerInputsForNonUnloadableResource.addModulesInBuiltInEnvironments( linkerInputsForAddedDependencies.getModulesInBuiltInEnvironments() );
2572 linkerInputsForNonUnloadableResource.addLibrariesInBuiltInEnvironments( linkerInputsForAddedDependencies.getLibrariesInBuiltInEnvironments() );
2573 linkerInputsForNonUnloadableResource.addExternalLibraries( linkerInputsForAddedDependencies.getExternalLibraries() );
2574 linkerInputsForNonUnloadableResource.addFrameworks( linkerInputsForAddedDependencies.getFrameworks() );
2575
2576 linkerInputsForNonUnloadableResource.addExternalLibraries( carriedOverNonUnloadableLibraries );
2577 linkerInputsForNonUnloadableResource.addExternalLibraries( carriedOverExternalLibraries );
2578 linkerInputsForNonUnloadableResource.addFrameworks( carriedOverFrameworks );
2579
2580 vector<string> rPaths = getRunPathSearchPaths(environments.front().front());
2581
2582 link(nonUnloadableResourcePath, linkerInputsForNonUnloadableResource, true, rPaths, shouldAdHocCodeSign);
2583 }
2584
2585 // If non-built-in dependencies were added (or any module caches went away), create an additional (or replacement) resource dylib.
2586
2587 bool wereNonBuiltInModulesAdded = ! linkerInputsForAddedDependencies.getModulesInNonBuiltInEnvironments().empty();
2588 bool wereNonBuiltInLibrariesAdded = ! linkerInputsForAddedDependencies.getLibrariesInNonBuiltInEnvironments().empty();
2589 if (wereNonBuiltInModulesAdded || wereNonBuiltInLibrariesAdded)
2590 {
2591 unloadableResourcePath = VuoFileUtilities::makeTmpFile(linkedCompositionFile + "-resource-unloadable", "dylib");
2592
2593 linkerInputsForUnloadableResource.addModulesInNonBuiltInEnvironments( linkerInputsForAddedDependencies.getModulesInNonBuiltInEnvironments() );
2594 linkerInputsForUnloadableResource.addLibrariesInNonBuiltInEnvironments( linkerInputsForAddedDependencies.getLibrariesInNonBuiltInEnvironments() );
2595 linkerInputsForUnloadableResource.addExternalLibraries( linkerInputsForAddedDependencies.getExternalLibraries() );
2596 linkerInputsForUnloadableResource.addFrameworks( linkerInputsForAddedDependencies.getFrameworks() );
2597
2598 linkerInputsForUnloadableResource.addExternalLibraries( carriedOverNonUnloadableLibraries );
2599 linkerInputsForUnloadableResource.addExternalLibraries( carriedOverUnloadableLibraries );
2600 linkerInputsForUnloadableResource.addExternalLibraries( carriedOverExternalLibraries );
2601 linkerInputsForUnloadableResource.addFrameworks( carriedOverFrameworks );
2602
2603 if (! nonUnloadableResourcePath.empty())
2604 {
2605 linkerInputsForUnloadableResource.addExternalLibrary( nonUnloadableResourcePath );
2606 linkerInputsForUnloadableResource.addLibrariesInBuiltInEnvironments( linkerInputsForNonUnloadableResource.getDylibsInBuiltInEnvironments() );
2607 linkerInputsForUnloadableResource.addExternalLibraries( linkerInputsForNonUnloadableResource.getExternalDylibs() );
2608 }
2609
2610 // This is usually correct, but may fail in the case where there are two identically-named dylibs at different
2611 // levels of scope. However, it's the best we can do as long as modules at the system, user, and composition
2612 // levels of scope are all combined into one resource dylib. (https://b33p.net/kosada/vuo/vuo/-/merge_requests/196#note_2148884)
2613 vector<string> rPaths = getRunPathSearchPaths(environments.back().front());
2614
2615 link(unloadableResourcePath, linkerInputsForUnloadableResource, true, rPaths, shouldAdHocCodeSign);
2616 }
2617 }
2618
2619 // Link the composition.
2620
2621 {
2622 VuoLinkerInputs linkerInputsForComposition;
2623
2624 linkerInputsForComposition.addExternalLibrary(compiledCompositionPath);
2625 linkerInputsForComposition.addVuoRuntime(this);
2626
2627 linkerInputsForComposition.addExternalLibraries( linkerInputsForAddedDependencies.getDylibsInBuiltInEnvironments() );
2628 linkerInputsForComposition.addExternalLibraries( linkerInputsForAddedDependencies.getDylibsInNonBuiltInEnvironments() );
2629 linkerInputsForComposition.addExternalLibraries( linkerInputsForAddedDependencies.getExternalDylibs() );
2630 linkerInputsForComposition.addFrameworks( linkerInputsForAddedDependencies.getFrameworks() );
2631
2632 linkerInputsForComposition.addExternalLibraries( carriedOverNonUnloadableLibraries );
2633 linkerInputsForComposition.addExternalLibraries( carriedOverUnloadableLibraries );
2634 linkerInputsForComposition.addExternalLibraries( carriedOverExternalLibraries );
2635 linkerInputsForComposition.addFrameworks( carriedOverFrameworks );
2636
2637 if (! nonUnloadableResourcePath.empty())
2638 {
2639 linkerInputsForComposition.addExternalLibrary( nonUnloadableResourcePath );
2640 linkerInputsForComposition.addLibrariesInBuiltInEnvironments( linkerInputsForNonUnloadableResource.getDylibsInBuiltInEnvironments() );
2641 linkerInputsForComposition.addExternalLibraries( linkerInputsForNonUnloadableResource.getExternalDylibs() );
2642 }
2643
2644 if (! unloadableResourcePath.empty())
2645 {
2646 linkerInputsForComposition.addExternalLibrary( unloadableResourcePath );
2647 linkerInputsForComposition.addLibrariesInBuiltInEnvironments( linkerInputsForUnloadableResource.getDylibsInBuiltInEnvironments() );
2648 linkerInputsForComposition.addLibrariesInNonBuiltInEnvironments( linkerInputsForUnloadableResource.getDylibsInNonBuiltInEnvironments() );
2649 linkerInputsForComposition.addExternalLibraries( linkerInputsForUnloadableResource.getExternalDylibs() );
2650 }
2651
2652 vector<string> rPaths = getRunPathSearchPaths(environments.front().front());
2653 link(linkedCompositionPath, linkerInputsForComposition, true, rPaths, shouldAdHocCodeSign);
2654 }
2655
2656 // Now that we're past the point where an exception can be thrown, update the RunningCompositionLibraries.
2657
2658 if (! nonUnloadableResourcePath.empty())
2659 {
2660 set<string> nonUnloadableDependencies = linkerInputsForNonUnloadableResource.getNonCachedModuleKeysInBuiltInEnvironments();
2661 runningCompositionLibraries->enqueueResourceLibraryToLoad(nonUnloadableResourcePath, nonUnloadableDependencies, false);
2662 }
2663
2664 if (! unloadableResourcePath.empty())
2665 {
2666 set<string> unloadableDependencies = linkerInputsForUnloadableResource.getNonCachedModuleKeysInNonBuiltInEnvironments();
2667 runningCompositionLibraries->enqueueResourceLibraryToLoad(unloadableResourcePath, unloadableDependencies, true);
2668 }
2669
2670 for (auto i : linkerInputsForAddedDependencies.getCachedDependenciesInBuiltInEnvironments())
2671 {
2672 shared_ptr<VuoModuleCacheRevision> revision = i.first;
2673 revision->use();
2674 runningCompositionLibraries->enqueueCacheLibraryToLoad(revision->getDylibPath(), i.second, false, [revision](void){ revision->disuse(); });
2675 }
2676
2677 for (auto i : linkerInputsForAddedDependencies.getCachedDependenciesInNonBuiltInEnvironments())
2678 {
2679 shared_ptr<VuoModuleCacheRevision> revision = i.first;
2680 revision->use();
2681 runningCompositionLibraries->enqueueCacheLibraryToLoad(revision->getDylibPath(), i.second, true, [revision](void){ revision->disuse(); });
2682 }
2683
2684 runningCompositionLibraries->addExternalLibraries( linkerInputsForAddedDependencies.getExternalDylibs() );
2685 runningCompositionLibraries->addExternalFrameworks( linkerInputsForAddedDependencies.getFrameworks() );
2686
2687 for (shared_ptr<VuoModuleCacheRevision> revision : currentModuleCaches)
2688 revision->disuse();
2689}
2690
2697set<string> VuoCompiler::getDependenciesForComposition(const string &compiledCompositionPath)
2698{
2699 double t0 = VuoLogGetTime();
2700
2701 // Add the node classes in the top-level composition and their dependencies.
2702 __block set<string> directDependencies;
2703 string moduleKey = getModuleKeyForPath(compiledCompositionPath);
2704 Module *module = readModuleFromBitcode(compiledCompositionPath, getTargetArch(target));
2705 dispatch_sync(llvmQueue, ^{
2707 directDependencies = compilerModule->getDependencies();
2708 delete compilerModule;
2709 delete module;
2710 });
2711
2712 try
2713 {
2714 auto deps = getDependenciesForComposition(directDependencies, true);
2715 VUserLog("Gathering dependencies for '%s' took %5.2fs", compiledCompositionPath.c_str(), VuoLogGetTime() - t0);
2716 return deps;
2717 }
2718 catch (VuoCompilerException &e)
2719 {
2720 e.getIssues()->setFilePathIfEmpty(compiledCompositionPath);
2721 throw;
2722 }
2723}
2724
2732{
2733 set<string> directDependencies;
2734
2735 set<VuoCompilerNode *> nodes = composition->getCachedGraph(this)->getNodes();
2736 for (VuoCompilerNode *node : nodes)
2737 if (node->getBase()->getNodeClass()->hasCompiler())
2738 directDependencies.insert( node->getBase()->getNodeClass()->getCompiler()->getDependencyName() );
2739
2740 vector<VuoPublishedPort *> publishedInputPorts = composition->getBase()->getPublishedInputPorts();
2741 vector<VuoPublishedPort *> publishedOutputPorts = composition->getBase()->getPublishedOutputPorts();
2742 vector<VuoPublishedPort *> publishedPorts;
2743 publishedPorts.insert(publishedPorts.end(), publishedInputPorts.begin(), publishedInputPorts.end());
2744 publishedPorts.insert(publishedPorts.end(), publishedOutputPorts.begin(), publishedOutputPorts.end());
2745 for (VuoPublishedPort *publishedPort : publishedPorts)
2746 {
2747 if (publishedPort->getClass()->hasCompiler())
2748 {
2749 VuoType *portType = static_cast<VuoCompilerPortClass *>( publishedPort->getClass()->getCompiler() )->getDataVuoType();
2750 if (portType)
2751 {
2752 string dependency;
2753 VuoGenericType *genericType = dynamic_cast<VuoGenericType *>(portType);
2754 if (genericType)
2755 {
2756 VuoGenericType::Compatibility compatibility;
2757 vector<string> compatibleTypeNames = genericType->getCompatibleSpecializedTypes(compatibility);
2758 dependency = VuoCompilerGenericType::chooseBackingTypeName(portType->getModuleKey(), compatibleTypeNames);
2759 }
2760 else
2761 dependency = portType->getModuleKey();
2762
2763 directDependencies.insert(dependency);
2764 }
2765 }
2766 }
2767
2768 return directDependencies;
2769}
2770
2777set<string> VuoCompiler::getDependenciesForComposition(VuoCompilerComposition *composition)
2778{
2779 set<string> directDependencies = getDirectDependenciesForComposition(composition);
2780 return getDependenciesForComposition(directDependencies, false);
2781}
2782
2789{
2790 vector<string> librarySearchPaths;
2791 applyToInstalledEnvironments([&librarySearchPaths](VuoCompilerEnvironment *env)
2792 {
2793 vector<string> envLibrarySearchPaths = env->getLibrarySearchPaths();
2794 librarySearchPaths.insert(librarySearchPaths.end(), envLibrarySearchPaths.begin(), envLibrarySearchPaths.end());
2795 });
2796
2797 set<string> dylibDeps;
2798 for (string dep : getDependenciesForComposition(composition))
2799 {
2800 string path = getLibraryPath(dep, librarySearchPaths);
2801 if (VuoStringUtilities::endsWith(path, ".dylib"))
2802 dylibDeps.insert(path);
2803 }
2804
2805 return dylibDeps;
2806}
2807
2821set<string> VuoCompiler::getDependenciesForComposition(const set<string> &directDependencies, bool checkCompatibility)
2822{
2823 // Make sure that any compiler-generated node classes have been generated and added to the dependency graph.
2824
2825 for (set<string>::const_iterator i = directDependencies.begin(); i != directDependencies.end(); ++i)
2826 getNodeClass(*i);
2827
2828 set<string> dependencies;
2829 vector<string> dependenciesToVisit(directDependencies.begin(), directDependencies.end());
2830
2831 while (! dependenciesToVisit.empty())
2832 {
2833 string moduleKey = dependenciesToVisit.back();
2834 dependenciesToVisit.pop_back();
2835
2836 set<pair<string, VuoCompilerEnvironment *>> currentDependencies;
2837
2838 // First pass: Find all dependencies of the direct dependency that are in the dependency graph and have been loaded so far.
2839
2840 vector<VuoDirectedAcyclicGraph::Vertex *> firstPassVertices = dependencyGraph->findVertex(moduleKey);
2841 set<VuoDirectedAcyclicGraph::Vertex *> firstPassDependencies(firstPassVertices.begin(), firstPassVertices.end());
2842 for (vector<VuoDirectedAcyclicGraph::Vertex *>::iterator j = firstPassVertices.begin(); j != firstPassVertices.end(); ++j)
2843 {
2844 vector<VuoDirectedAcyclicGraph::Vertex *> downstream = dependencyGraph->getDownstreamVertices(*j);
2845 firstPassDependencies.insert(downstream.begin(), downstream.end());
2846 }
2847
2848 // Make sure that any compiler-generated node classes in subcompositions have been generated and added to the dependency graph.
2849
2850 for (set<VuoDirectedAcyclicGraph::Vertex *>::iterator j = firstPassDependencies.begin(); j != firstPassDependencies.end(); ++j)
2851 {
2852 VuoDependencyGraphVertex *v = static_cast<VuoDependencyGraphVertex *>(*j);
2854 }
2855
2856 // Second pass: Find all dependencies of the direct dependency that are in the dependency graph, this time including
2857 // dependencies of the node classes just generated.
2858
2859 vector<VuoDirectedAcyclicGraph::Vertex *> moduleVertices = dependencyGraph->findVertex(moduleKey);
2860 for (VuoDirectedAcyclicGraph::Vertex *mv : moduleVertices)
2861 {
2862 VuoDependencyGraphVertex *moduleVertex = static_cast<VuoDependencyGraphVertex *>(mv);
2863 currentDependencies.insert({moduleKey, moduleVertex->getEnvironment()});
2864
2865 vector<VuoDirectedAcyclicGraph::Vertex *> downstream = dependencyGraph->getDownstreamVertices(mv);
2866 for (VuoDirectedAcyclicGraph::Vertex *dv : downstream)
2867 {
2868 VuoDependencyGraphVertex *downstreamVertex = static_cast<VuoDependencyGraphVertex *>(dv);
2869 currentDependencies.insert({downstreamVertex->getDependency(), downstreamVertex->getEnvironment()});
2870 }
2871 }
2872
2873 // Third pass: Add in dependencies that were omitted from the dependency graph because they would make it cyclic —
2874 // a generated module depends on an installed module at the same scope.
2875
2876 set<string> extraDependencies;
2877 for (auto i : currentDependencies)
2878 {
2879 VuoCompilerEnvironment *env = i.second;
2880 if (env && env->isGenerated())
2881 {
2882 VuoCompilerModule *dependency = env->findModule(i.first);
2883 set<string> dependenciesOfDependency = dependency->getDependencies();
2884 extraDependencies.insert(dependenciesOfDependency.begin(), dependenciesOfDependency.end());
2885 }
2886 }
2887
2888 vector<string> notYetAdded;
2889 std::set_difference(extraDependencies.begin(), extraDependencies.end(),
2890 dependencies.begin(), dependencies.end(),
2891 std::back_inserter(notYetAdded));
2892
2893 vector<string> notYetAddedOrEnqueued;
2894 std::set_difference(notYetAdded.begin(), notYetAdded.end(),
2895 dependenciesToVisit.begin(), dependenciesToVisit.end(),
2896 std::back_inserter(notYetAddedOrEnqueued));
2897
2898 dependenciesToVisit.insert(dependenciesToVisit.end(), notYetAddedOrEnqueued.begin(), notYetAddedOrEnqueued.end());
2899
2900 for (auto i : currentDependencies)
2901 dependencies.insert(i.first);
2902 }
2903
2904 // Check that the dependencies are compatible with the compiler's target.
2905
2906 if (checkCompatibility)
2907 {
2908 string targetForCompatibility = ! requestedTarget.empty() ? requestedTarget : getProcessTarget();
2909 VuoCompilerCompatibility neededCompatibility = VuoCompilerCompatibility::compatibilityWithTargetTriple(targetForCompatibility);
2910 VuoCompilerCompatibility actualCompatibility = getCompatibilityOfDependencies(dependencies);
2911
2912 if (! actualCompatibility.isCompatibleWith(neededCompatibility))
2913 {
2915 set<VuoCompilerNodeClass *> nodeClassesReported;
2916
2917 for (string moduleKey : dependencies)
2918 {
2919 VuoCompilerModule *module = getModule(moduleKey);
2920 if (module && ! module->getCompatibleTargets().isCompatibleWith(neededCompatibility))
2921 {
2922 vector<VuoCompilerNodeClass *> incompatibleNodeClasses;
2923
2924 VuoCompilerNodeClass *moduleAsNodeClass = dynamic_cast<VuoCompilerNodeClass *>(module);
2925 if (moduleAsNodeClass)
2926 incompatibleNodeClasses.push_back(moduleAsNodeClass);
2927 else
2928 {
2929 vector<VuoDirectedAcyclicGraph::Vertex *> moduleVertices = dependencyGraph->findVertex(moduleKey);
2930 for (VuoDirectedAcyclicGraph::Vertex *v : moduleVertices)
2931 {
2932 vector<VuoDirectedAcyclicGraph::Vertex *> upstream = dependencyGraph->getUpstreamVertices(v);
2933
2934 vector<string> upstreamModuleKeys;
2935 std::transform(upstream.begin(), upstream.end(),
2936 std::back_inserter(upstreamModuleKeys),
2937 [](VuoDirectedAcyclicGraph::Vertex *u){ return static_cast<VuoDependencyGraphVertex *>(u)->getDependency(); });
2938
2939 vector<string> potentialNodeClassNames;
2940 std::set_intersection(directDependencies.begin(), directDependencies.end(),
2941 upstreamModuleKeys.begin(), upstreamModuleKeys.end(),
2942 std::back_inserter(potentialNodeClassNames));
2943
2944 for (string nodeClassName : potentialNodeClassNames)
2945 {
2946 VuoCompilerNodeClass *upstreamNodeClass = getNodeClass(nodeClassName);
2947 if (upstreamNodeClass)
2948 incompatibleNodeClasses.push_back(upstreamNodeClass);
2949 }
2950 }
2951 }
2952
2953 if (incompatibleNodeClasses.empty())
2954 {
2955 VuoCompilerIssue issue(VuoCompilerIssue::Error, "linking composition", "",
2956 "Dependencies incompatible with system",
2957 "%module is only compatible with " + module->getCompatibleTargets().toString() +
2958 ", so this composition can't run on " + neededCompatibility.toString() + ".");
2959 issue.setModule(module->getPseudoBase());
2960 issues->append(issue);
2961 }
2962 else
2963 {
2964 for (VuoCompilerNodeClass *nodeClass : incompatibleNodeClasses)
2965 {
2966 if (nodeClassesReported.find(nodeClass) != nodeClassesReported.end())
2967 continue;
2968
2969 nodeClassesReported.insert(nodeClass);
2970
2971 VuoCompilerIssue issue(VuoCompilerIssue::Error, "linking composition", "",
2972 "Nodes incompatible with system",
2973 "%module is only compatible with " + module->getCompatibleTargets().toString() +
2974 ", so this composition can't run on " + neededCompatibility.toString() + ".");
2975 issue.setModule(nodeClass->getPseudoBase());
2976 issues->append(issue);
2977 }
2978 }
2979 }
2980 }
2981
2982 if (issues->isEmpty())
2983 {
2984 VuoCompilerIssue issue(VuoCompilerIssue::Error, "linking composition", "",
2985 "Dependencies incompatible with system",
2986 "Some dependencies of this composition are only compatible with " + actualCompatibility.toString() +
2987 ", so this composition can't run on " + neededCompatibility.toString() + ".");
2988 issues->append(issue);
2989 }
2990
2991 throw VuoCompilerException(issues, true);
2992 }
2993 }
2994
2995 // Add the libraries needed by every linked composition.
2996
2997 vector<string> coreDependencies = getCoreVuoDependencies();
2998 dependencies.insert(coreDependencies.begin(), coreDependencies.end());
2999
3000 return dependencies;
3001}
3002
3007{
3008 VuoCompilerCompatibility compatibility(nullptr);
3009
3010 for (string dependency : dependencies)
3011 {
3012 VuoCompilerModule *module = getModule(dependency);
3013 if (module)
3014 compatibility = compatibility.intersection(module->getCompatibleTargets());
3015 }
3016
3017 return compatibility;
3018}
3019
3025string VuoCompiler::getLibraryPath(const string &dependency, vector<string> librarySearchPaths)
3026{
3027 if (dependency[0] == '/' && VuoFileUtilities::fileExists(dependency))
3028 return dependency;
3029
3030 // Put the system library folder last in the list — prefer to use the libraries bundled in Vuo.framework.
3031 // Don't attempt to use OpenSSL from the system library folder, since macOS 10.15 prevents linking to it.
3032 if (dependency != "crypto"
3033 && dependency != "ssl")
3034 librarySearchPaths.push_back("/usr/lib");
3035
3036 for (auto &path : librarySearchPaths)
3037 {
3038 vector<string> variations;
3039 variations.push_back(path + "/" + dependency);
3040 variations.push_back(path + "/lib" + dependency);
3041 variations.push_back(path + "/lib" + dependency + ".dylib");
3042 variations.push_back(path + "/lib" + dependency + ".a");
3043 for (auto &variation : variations)
3044 if (VuoFileUtilities::fileExists(variation))
3045 return variation;
3046 }
3047
3048 return "";
3049}
3050
3062void VuoCompiler::makeModuleCachesAvailable(bool shouldUseExistingBuiltInCaches, bool shouldUseExistingOtherCaches, const string &target)
3063{
3064 loadModulesIfNeeded();
3065 dispatch_group_wait(modulesLoading, DISPATCH_TIME_FOREVER); // Wait for any previous loadModulesIfNeeded() calls to complete.
3066
3067 dispatch_sync(environmentQueue, ^{
3068
3069 set<string> accumulatedDylibs;
3070 set<string> accumulatedFrameworks;
3071 vector<shared_ptr<VuoModuleCache>> accumulatedModuleCaches;
3072 double lastPrerequisiteModuleCacheRebuild = 0;
3073
3074 for (const vector<VuoCompilerEnvironment *> &environmentsAtScope : environments)
3075 {
3076 shared_ptr<VuoModuleCache> moduleCache = environmentsAtScope.at(0)->getModuleCache();
3077 if (! moduleCache)
3078 continue;
3079
3080 VuoModuleCacheManifest expectedManifest;
3081 vector<VuoModuleInfoIterator> expectedModules;
3082
3083 for (VuoCompilerEnvironment *env : environmentsAtScope)
3084 {
3085 set<string> dylibsNeededToLinkToCache;
3086 set<string> frameworksNeededToLinkToCache;
3087 VuoModuleCacheManifest envManifest = env->getCacheableModulesAndDependencies(dylibsNeededToLinkToCache, frameworksNeededToLinkToCache);
3088
3089 expectedManifest.addContentsOf(envManifest);
3090 accumulatedDylibs.insert(dylibsNeededToLinkToCache.begin(), dylibsNeededToLinkToCache.end());
3091 accumulatedFrameworks.insert(frameworksNeededToLinkToCache.begin(), frameworksNeededToLinkToCache.end());
3092
3093 expectedModules.push_back(env->listAllModules());
3094 }
3095
3096 vector<string> runPathSearchPaths = getRunPathSearchPaths(environmentsAtScope.at(0));
3097
3098 bool shouldUseExistingCache = (environmentsAtScope.at(0)->isBuiltIn() ? shouldUseExistingBuiltInCaches : shouldUseExistingOtherCaches);
3099
3100 moduleCache->makeAvailable(shouldUseExistingCache, accumulatedModuleCaches, lastPrerequisiteModuleCacheRebuild,
3101 expectedManifest, expectedModules, accumulatedDylibs, accumulatedFrameworks, runPathSearchPaths,
3102 this, getTargetArch(target));
3103
3104 accumulatedModuleCaches.push_back(moduleCache);
3105 }
3106 });
3107
3109}
3110
3115vector<shared_ptr<VuoModuleCacheRevision>> VuoCompiler::useCurrentModuleCaches(Optimization optimization)
3116{
3117 vector<shared_ptr<VuoModuleCacheRevision>> revisions;
3118
3120 {
3121 makeModuleCachesAvailable(true, optimization == Optimization_ExistingModuleCaches);
3122
3123 applyToInstalledEnvironments([&revisions](VuoCompilerEnvironment *env)
3124 {
3125 shared_ptr<VuoModuleCache> moduleCache = env->getModuleCache();
3126 if (moduleCache)
3127 {
3128 shared_ptr<VuoModuleCacheRevision> revision = moduleCache->useCurrentRevision();
3129 if (revision)
3130 revisions.push_back(revision);
3131 }
3132 });
3133 }
3134
3135 return revisions;
3136}
3137
3144{
3145 dispatch_group_async(moduleCacheBuilding, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
3146 makeModuleCachesAvailable(true, false);
3147 });
3148}
3149
3165void VuoCompiler::generateBuiltInModuleCache(string vuoFrameworkPath, string target, bool onlyGenerateModules)
3166{
3167 vuoFrameworkInProgressPath = vuoFrameworkPath;
3168
3169 // Delete the non-built-in caches to ensure that all specialized modules that are dependencies of built-in modules
3170 // will be generated and saved to the built-in cache rather than being loaded from a non-built-in cache.
3171 string cachePath = VuoFileUtilities::getCachePath();
3172 if (! cachePath.empty())
3173 VuoFileUtilities::deleteDir(cachePath);
3174
3175 VuoCompiler compiler("", target);
3176 compiler.generatedEnvironment = compiler.environments.at(0).at(1);
3177
3178 if (onlyGenerateModules)
3179 {
3180 compiler.loadModulesIfNeeded();
3181 dispatch_group_wait(compiler.moduleSourceCompilersExist, DISPATCH_TIME_FOREVER);
3182 }
3183 else
3184 {
3185 compiler.makeModuleCachesAvailable(false, true, target);
3186 }
3187}
3188
3194{
3195 return target;
3196}
3197
3203{
3204 return getTargetArch(target);
3205}
3206
3211void VuoCompiler::setLoadAllModules(bool shouldLoadAllModules)
3212{
3213 dispatch_sync(modulesToLoadQueue, ^{
3214 this->shouldLoadAllModules = shouldLoadAllModules;
3215 });
3216}
3217
3228void VuoCompiler::link(string outputPath, const VuoLinkerInputs &linkerInputs,
3229 bool isDylib, const vector<string> &rPaths, bool shouldAdHocCodeSign, VuoCompilerIssues *issues)
3230{
3231 VUserLog("Linking '%s' (%s)…", outputPath.c_str(), getTargetArch(target).c_str());
3232 // https://stackoverflow.com/questions/11657529/how-to-generate-an-executable-from-an-llvmmodule
3233
3234 bool ownsIssues = false;
3235 if (! issues)
3236 {
3237 issues = new VuoCompilerIssues;
3238 ownsIssues = true;
3239 }
3240
3241 // Write all the modules with renamed symbols to a composite module file (since the linker can't operate on in-memory modules).
3242 string compositeModulePath = VuoFileUtilities::makeTmpFile("composite", "bc");
3243 dispatch_sync(llvmQueue, ^{
3244 double t0 = VuoLogGetTime();
3245
3246 unique_ptr<Module> compositeModule(new Module("composite", *globalLLVMContext));
3247 Linker linker(*compositeModule);
3248 setTargetForModule(compositeModule.get(), target);
3249
3250 for (auto i : linkerInputs.getModules())
3251 {
3252 unique_ptr<Module> upi = llvm::CloneModule(i);
3253 if (linker.linkInModule(std::move(upi)))
3254 {
3255 VuoCompilerIssue issue(VuoCompilerIssue::IssueType::Error, "linking composite module", "",
3256 "", "Failed to link in the module with ID '" + i->getModuleIdentifier() + "'");
3257 issues->append(issue);
3258 }
3259 }
3260
3261 try
3262 {
3263 verifyModule(compositeModule.get(), issues);
3264 writeModuleToBitcode(compositeModule.get(), target, compositeModulePath, issues);
3265 }
3266 catch (VuoCompilerException &e) {}
3267
3268 double linkModulesTime = VuoLogGetTime() - t0;
3269 if (linkModulesTime > 0.1)
3270 VUserLog("\tLinkModules took %5.2fs", linkModulesTime);
3271 });
3272
3273
3274 // llvm-3.1/llvm/tools/clang/tools/driver/driver.cpp
3275
3276 // Invoke clang as `clang++` so it includes the C++ standard libraries.
3277 string clangPath(getClangPath() + "++");
3278
3279 vector<const char *> args;
3280 vector<char *> argsToFree;
3281 args.push_back(clangPath.c_str());
3282
3283 {
3284 char *outputPathZ = strdup(("-o" + outputPath).c_str());
3285 args.push_back(outputPathZ);
3286 argsToFree.push_back(outputPathZ);
3287 }
3288
3289 args.push_back(compositeModulePath.c_str());
3290
3291 vector<string> coreDependencies = getCoreVuoDependencies();
3292 for (string library : linkerInputs.getLibraries())
3293 {
3294 for (vector<string>::iterator j = coreDependencies.begin(); j != coreDependencies.end(); ++j)
3295 {
3296 string coreDependency = *j;
3297 if (VuoStringUtilities::endsWith(library, "lib" + coreDependency + ".a"))
3298 args.push_back("-force_load"); // Load all symbols of static core dependencies, not just those used in the objects.
3299 }
3300
3301 // Use the pre-built native object file if it exists (faster than converting .bc to .o every build).
3302 if (VuoStringUtilities::endsWith(library, ".bc"))
3303 {
3304 string libraryObject = VuoStringUtilities::substrBefore(library, ".bc") + ".o";
3305 if (VuoFileUtilities::fileExists(libraryObject))
3306 library = libraryObject;
3307 }
3308
3309 char *libraryZ = strdup(library.c_str());
3310 args.push_back(libraryZ);
3311 argsToFree.push_back(libraryZ);
3312 }
3313
3314 // Add framework search paths
3315 vector<string> frameworkArguments;
3316
3317 vector<string> frameworkSearchPaths;
3318 applyToInstalledEnvironments([&frameworkSearchPaths](VuoCompilerEnvironment *env)
3319 {
3320 vector<string> envFrameworkSearchPaths = env->getFrameworkSearchPaths();
3321 frameworkSearchPaths.insert(frameworkSearchPaths.end(), envFrameworkSearchPaths.begin(), envFrameworkSearchPaths.end());
3322 });
3323
3324 for (vector<string>::const_iterator i = frameworkSearchPaths.begin(); i != frameworkSearchPaths.end(); ++i)
3325 {
3326 string a = "-F"+*i;
3327 // Keep these std::strings around until after args is done being used, since the std::string::c_str() is freed when the std::string is deleted.
3328 frameworkArguments.push_back(a);
3329 char *frameworkArgument = strdup(a.c_str());
3330 args.push_back(frameworkArgument);
3331 argsToFree.push_back(frameworkArgument);
3332 }
3333
3334 for (const string &framework : linkerInputs.getFrameworks())
3335 {
3336 args.push_back("-framework");
3337
3338 string frameworkName = framework.substr(0, framework.length() - string(".framework").length());
3339 char *frameworkNameZ = strdup(frameworkName.c_str());
3340 args.push_back(frameworkNameZ);
3341 argsToFree.push_back(frameworkNameZ);
3342 }
3343
3344 // When linking on a development workstation or Jenkins or an end-user system,
3345 // use the partial macOS SDK bundled in Vuo.framework, since it includes all the TBDs we need.
3346 string vuoFrameworkPath = getVuoFrameworkPath();
3347 string frameworkMacOSSDKFolder = vuoFrameworkPath + "/SDKs/MacOSX.sdk";
3348 if (!VuoFileUtilities::fileExists(frameworkMacOSSDKFolder))
3349 throw VuoException("Couldn't find the macOS SDK.");
3350
3351 args.push_back("-Xlinker");
3352 args.push_back("-syslibroot");
3353 args.push_back("-Xlinker");
3354 char *frameworkMacOSSDKFolderZ = strdup(frameworkMacOSSDKFolder.c_str());
3355 args.push_back(frameworkMacOSSDKFolderZ);
3356 argsToFree.push_back(frameworkMacOSSDKFolderZ);
3357
3358 args.push_back("-Xlinker");
3359 args.push_back("-platform_version");
3360 args.push_back("-Xlinker");
3361 args.push_back("macos");
3362 args.push_back("-Xlinker");
3363 char *deploymentTargetZ = strdup(MACOS_DEPLOYMENT_TARGET);
3364 args.push_back(deploymentTargetZ);
3365 argsToFree.push_back(deploymentTargetZ);
3366 args.push_back("-Xlinker");
3367 char *sdkVersionZ = strdup(MACOS_SDK_VERSION);
3368 args.push_back(sdkVersionZ);
3369 argsToFree.push_back(sdkVersionZ);
3370
3371 // Linker option necessary for compatibility with our bundled version of ld64:
3372 args.push_back("-Xlinker");
3373 args.push_back("--no-demangle");
3374
3375 if (isVerbose)
3376 args.push_back("-v");
3377
3378 if (isDylib)
3379 args.push_back("-dynamiclib");
3380
3381 args.push_back("-Xlinker");
3382 args.push_back("-headerpad_max_install_names");
3383
3384 // Tell the built dylib/executable where to find dylibs that it depends on
3385 for (const string &rPath : rPaths)
3386 {
3387 args.push_back("-rpath");
3388 args.push_back(rPath.c_str());
3389 }
3390
3391#ifdef COVERAGE
3392 args.push_back("-rpath");
3393 args.push_back(LLVM_ROOT "/lib");
3394#endif
3395
3396 args.push_back("-target");
3397 args.push_back(target.c_str());
3398
3399 args.push_back("-std=c++14");
3400 args.push_back("-stdlib=libc++");
3401 args.push_back("-mmacosx-version-min=10.10");
3402
3403 // Allow clang to print meaningful error messages.
3404 shared_ptr<VuoClangIssues> clangIssues = std::make_shared<VuoClangIssues>();
3405 auto diagnosticConsumer = new VuoCompilerDiagnosticConsumer(clangIssues);
3406 clang::DiagnosticOptions *diagOptions = new clang::DiagnosticOptions();
3407 IntrusiveRefCntPtr<clang::DiagnosticIDs> DiagID(new clang::DiagnosticIDs());
3408 clang::DiagnosticsEngine Diags(DiagID, diagOptions, diagnosticConsumer);
3409
3410 if (isVerbose)
3411 {
3412 ostringstream s;
3413 for (vector<const char *>::iterator i = args.begin(); i != args.end(); ++i)
3414 s << *i << " ";
3415 VUserLog("\t%s", s.str().c_str());
3416 }
3417
3418 // Redirect linker output to a file, so we can feed it through VuoLog.
3419 string stdoutFile = VuoFileUtilities::makeTmpFile("vuo-linker-output", "txt");
3420 const StringRef stdoutPath(stdoutFile);
3421 const StringRef *redirects[] = {
3422 nullptr, // stdin
3423 &stdoutPath, // stdout
3424 &stdoutPath, // stderr
3425 };
3426
3427 // ExecuteAndWait's args needs to be null-terminated.
3428 const char **argsz = (const char **)malloc(sizeof(char *) * args.size() + 1);
3429 for (int i = 0; i < args.size(); ++i)
3430 argsz[i] = args[i];
3431 argsz[args.size()] = nullptr;
3432
3433 string errMsg;
3434 bool executionFailed;
3435 double t0 = VuoLogGetTime();
3436 int ret = llvm::sys::ExecuteAndWait(args[0], argsz, nullptr, redirects, 0, 0, &errMsg, &executionFailed);
3437
3438 for (auto i : argsToFree)
3439 free(i);
3440
3441 // Clean up composite module file.
3442 remove(compositeModulePath.c_str());
3443
3444 if (!isDylib)
3445 // Ensure the linked binary has the execute permission set
3446 // (ld64-242 doesn't reliably set it, whereas ld64-133.3 did).
3447 // https://b33p.net/kosada/node/14152
3448 chmod(outputPath.c_str(), 0755);
3449
3450 if (! clangIssues->isEmpty())
3451 {
3452 VuoCompilerIssue issue(VuoCompilerIssue::Error, "linking composition", outputPath, "", "Failed to link file");
3453 issue.setClangIssues(clangIssues);
3454 issues->append(issue);
3455 }
3456
3457 if (ret != 0)
3458 {
3459 string details;
3460 if (!errMsg.empty())
3461 {
3462 VUserLog("%s", errMsg.c_str());
3463 details += "\n" + errMsg + "\n";
3464 }
3465 string stdoutFileContents = VuoFileUtilities::readFileToString(stdoutFile);
3466 if (!stdoutFileContents.empty())
3467 {
3468 VUserLog("%s", stdoutFileContents.c_str());
3469 details += "\n" + stdoutFileContents + "\n";
3470 }
3471 VuoFileUtilities::deleteFile(stdoutFile);
3472
3473 VuoCompilerIssue issue(VuoCompilerIssue::Error, "linking composition", outputPath, "", details);
3474 issues->append(issue);
3475 throw VuoCompilerException(issues, ownsIssues);
3476 }
3477
3478 if (! issues->isEmpty())
3479 VUserLog("%s", issues->getLongDescription(false).c_str());
3480
3481 VuoFileUtilities::deleteFile(stdoutFile);
3482 VUserLog("\tLinking took %5.2fs", VuoLogGetTime() - t0);
3483
3484 if (shouldAdHocCodeSign)
3485 adHocCodeSign(outputPath);
3486}
3487
3491void VuoCompiler::adHocCodeSign(string path)
3492{
3494#if VUO_PRO
3495 "CODESIGN_ALLOCATE=" + getCodesignAllocatePath(),
3496#endif
3497 });
3498}
3499
3505Module *VuoCompiler::readModuleFromBitcode(string inputPath, string arch)
3506{
3507 string dir, file, ext;
3508 VuoFileUtilities::splitPath(inputPath, dir, file, ext);
3509 VuoFileUtilities::File inputFile(dir, file + "." + ext);
3510 return readModuleFromBitcode(&inputFile, arch);
3511}
3512
3520Module *VuoCompiler::readModuleFromBitcode(VuoFileUtilities::File *inputFile, string arch)
3521{
3522 size_t inputDataBytes;
3523 char *inputData = inputFile->getContentsAsRawData(inputDataBytes);
3524
3525 string error;
3526 set<string> availableArchs;
3527 VuoLog_status("Loading module \"%s\" (%s)", inputFile->getRelativePath().c_str(), arch.c_str());
3528 Module *module = readModuleFromBitcodeData(inputData, inputDataBytes, arch, availableArchs, error);
3529 VuoLog_status(NULL);
3530 if (! module)
3531 VUserLog("Error: Couldn't parse module '%s' (%s): %s.", inputFile->getRelativePath().c_str(), arch.c_str(), error.c_str());
3532
3533 free(inputData);
3534
3535 return module;
3536}
3537
3543Module *VuoCompiler::readModuleFromBitcodeData(char *inputData, size_t inputDataBytes, string arch,
3544 set<string> &availableArchs, string &error)
3545{
3546 if (inputDataBytes < sizeof(unsigned int))
3547 return nullptr;
3548
3549 __block Module *module = nullptr;
3550 dispatch_sync(llvmQueue, ^{
3551 StringRef inputDataAsStringRef(inputData, inputDataBytes);
3552 auto mb = MemoryBuffer::getMemBuffer(inputDataAsStringRef, "", false);
3553 if (!mb)
3554 {
3555 error = "Couldn't create MemoryBuffer";
3556 return;
3557 }
3558
3559 MemoryBufferRef bitcodeBuffer;
3560 string moduleArch;
3561 unsigned int fileID = *(unsigned int *)inputData;
3562 if (fileID == 0x0b17c0de)
3563 // This is a single-architecture LLVM bitcode `.bc` file, so read the entire file.
3564 bitcodeBuffer = mb.get()->getMemBufferRef();
3565
3566 else if (fileID == 0xdec04342)
3567 {
3568 // This is a single-architecture raw bitcode file, presumably generated by Vuo 2.2.1 or earlier.
3569 bitcodeBuffer = mb.get()->getMemBufferRef();
3570 moduleArch = "x86_64";
3571 availableArchs.insert(moduleArch);
3572 }
3573
3574 else if (fileID == 0xbebafeca)
3575 {
3576 if (arch.empty())
3577 {
3578 error = "It's a Mach-O universal binary, but this compiler instance's LLVM target isn't set";
3579 return;
3580 }
3581
3582 // This is a Mach-O wrapper around multiple LLVM bitcode files;
3583 // parse the Mach-O header to extract just a single architecture.
3584 auto binary = llvm::object::MachOUniversalBinary::create(mb.get()->getMemBufferRef());
3585 if (!binary)
3586 {
3587 error = "Couldn't read Mach-O universal binary:";
3588 handleAllErrors(binary.takeError(), [&error](const ErrorInfoBase &ei) {
3589 error += " " + ei.message();
3590 });
3591 return;
3592 }
3593
3594 for (auto &o : binary.get()->objects())
3595 {
3596 if (o.getArchFlagName() == arch)
3597 bitcodeBuffer = MemoryBufferRef(mb.get()->getMemBufferRef().getBuffer().slice(o.getOffset(), o.getOffset() + o.getSize()), "");
3598
3599 availableArchs.insert(o.getArchFlagName());
3600 }
3601
3602 if (!bitcodeBuffer.getBufferSize())
3603 {
3604 error = "The Mach-O universal binary doesn't have an \"" + arch + "\" slice";
3605 return;
3606 }
3607 }
3608
3609 auto wrappedModule = llvm::parseBitcodeFile(bitcodeBuffer, *globalLLVMContext);
3610 if (!wrappedModule)
3611 {
3612 error = "Couldn't parse bitcode file:";
3613 handleAllErrors(wrappedModule.takeError(), [&error](const ErrorInfoBase &ei) {
3614 error += " " + ei.message();
3615 });
3616 return;
3617 }
3618
3619 module = wrappedModule.get().release();
3620
3621 if (moduleArch.empty())
3622 moduleArch = getTargetArch(module->getTargetTriple());
3623
3624 if (availableArchs.empty())
3625 availableArchs.insert(moduleArch);
3626
3627 if (moduleArch != arch)
3628 {
3629 error = "The module's CPU architecture \"" + moduleArch + "\" doesn't match the compiler's CPU architecture \"" + arch + "\"";
3630 delete module;
3631 module = nullptr;
3632 return;
3633 }
3634 });
3635 return module;
3636}
3637
3645void VuoCompiler::verifyModule(Module *module, VuoCompilerIssues *issues)
3646{
3647 string str;
3648 raw_string_ostream verifyOut(str);
3649 if (llvm::verifyModule(*module, &verifyOut))
3650 {
3651 VuoCompilerIssue issue(VuoCompilerIssue::Error, "compiling module", "", "Verification failed for module '" + module->getModuleIdentifier() + "'",
3652 verifyOut.str());
3653 issue.setModuleKey(module->getModuleIdentifier());
3654 issues->append(issue);
3655 throw VuoCompilerException(issues, false);
3656 }
3657}
3658
3666void VuoCompiler::writeModuleToBitcode(Module *module, string target, string outputPath, VuoCompilerIssues *issues)
3667{
3668 // Ensure the module gets output in the bitcode wrapper format instead of raw bitcode.
3669 setTargetForModule(module, target);
3670
3671 std::error_code err;
3672 raw_fd_ostream out(outputPath.c_str(), err, sys::fs::F_None);
3673 if (err)
3674 {
3675 VuoCompilerIssue issue(VuoCompilerIssue::Error, "compiling module", "", "Write failed for module '" + module->getModuleIdentifier() + "'",
3676 "Couldn't write to '" + outputPath + "': " + err.message());
3677 issue.setModuleKey(module->getModuleIdentifier());
3678 issues->append(issue);
3679 throw VuoCompilerException(issues, false);
3680 }
3681 llvm::WriteBitcodeToFile(module, out);
3682}
3683
3690void VuoCompiler::setTargetForModule(Module *module, string targetTriple)
3691{
3692 // Replace the OS version in targetTriple to avoid warnings about linking modules of different target triples.
3693 llvm::Triple triple(targetTriple);
3694 if (triple.isMacOSX())
3695 triple.setOSName("macosx10.10.0");
3696
3697 module->setTargetTriple(triple.str());
3698
3699 string error;
3700 auto target = TargetRegistry::lookupTarget(module->getTargetTriple(), error);
3701 if (!target)
3702 {
3703 VUserLog("Error: Couldn't look up target: %s", error.c_str());
3704 return;
3705 }
3706
3707 auto targetMachine = target->createTargetMachine(module->getTargetTriple(), "", "", TargetOptions(), Optional<Reloc::Model>());
3708 if (!targetMachine)
3709 {
3710 VUserLog("Error: Couldn't create targetMachine.");
3711 return;
3712 }
3713
3714 module->setDataLayout(targetMachine->createDataLayout());
3715
3716 delete targetMachine;
3717}
3718
3723string VuoCompiler::getTargetArch(string target)
3724{
3725 auto hyphen = target.find('-');
3726 if (hyphen == string::npos)
3727 return "";
3728
3729 return target.substr(0, hyphen);
3730}
3731
3735string VuoCompiler::getProcessTarget(void)
3736{
3737 // llvm::sys::getProcessTriple() returns `LLVM_HOST_TRIPLE`,
3738 // which is always `x86_64-*` since we built LLVM on x86_64.
3739 // Instead, use llvm::sys::getDefaultTargetTriple()
3740 // which _actually_ returns the current process's target.
3741 llvm::Triple triple(llvm::sys::getDefaultTargetTriple());
3742
3743 // Replace darwin with macosx, e.g. x86_64-apple-darwin18.7.0 -> x86_64-apple-macosx10.14.6
3744 if (triple.isMacOSX())
3745 {
3746 unsigned int major, minor, micro;
3747 if (triple.getMacOSXVersion(major, minor, micro))
3748 {
3749 ostringstream osVersion;
3750 osVersion << "macosx" << major << "." << minor << "." << micro;
3751 triple.setOSName(osVersion.str());
3752 }
3753 }
3754
3755 return triple.str();
3756}
3757
3765{
3766 Module *llvmModule = module->getModule();
3767
3768 // In C++ the return value of a cast may not be the same pointer as the cast arg.
3769 // Because of this, VuoModule::getPseudoBase() returns a different value than VuoNodeClass::getBase().
3770 // Calling delete on VuoModule::getPseudoBase() gives a malloc error: "pointer being freed was not allocated".
3771 // So call it on VuoNodeClass::getBase(). Same idea for VuoType.
3772
3773 VuoNodeClass *baseNodeClass = NULL;
3774 VuoType *baseType = NULL;
3775 VuoModule *baseModule = NULL;
3776 VuoCompilerNodeClass *nodeClass = dynamic_cast<VuoCompilerNodeClass *>(module);
3777 if (nodeClass)
3778 {
3779 baseNodeClass = dynamic_cast<VuoNodeClass *>(nodeClass->getBase());
3780
3782 if (dynamic_cast<VuoCompilerSpecializedNodeClass *>(module))
3783 module = NULL;
3784 }
3785 else
3786 {
3787 VuoCompilerType *type = dynamic_cast<VuoCompilerType *>(module);
3788 if (type)
3789 baseType = dynamic_cast<VuoType *>(type->getBase());
3790 else
3791 baseModule = module->getPseudoBase();
3792 }
3793
3794 delete module;
3795 delete baseNodeClass;
3796 delete baseType;
3797 delete baseModule;
3798 destroyLlvmModule(llvmModule);
3799}
3800
3808{
3809 dispatch_sync(llvmQueue, ^{
3810 delete module;
3811 });
3812}
3813
3825VuoNode * VuoCompiler::createNode(VuoCompilerNodeClass *nodeClass, string title, double x, double y)
3826{
3828 if (nodeClassForNode)
3829 return nodeClassForNode->newNode(title, x, y);
3830
3831 return nullptr;
3832}
3833
3840VuoNode * VuoCompiler::createNode(VuoCompilerNodeClass *nodeClass, VuoNode *nodeToCopyMetadataFrom)
3841{
3843 if (nodeClassForNode)
3844 return nodeClassForNode->newNode(nodeToCopyMetadataFrom);
3845
3846 return nullptr;
3847}
3848
3852VuoNode * VuoCompiler::createPublishedInputNode(vector<VuoPublishedPort *> publishedInputPorts)
3853{
3854 string nodeClassName = VuoCompilerPublishedInputNodeClass::buildNodeClassName(publishedInputPorts);
3855 return createPublishedNode(nodeClassName, publishedInputPorts);
3856}
3857
3861VuoNode * VuoCompiler::createPublishedOutputNode(vector<VuoPublishedPort *> publishedOutputPorts)
3862{
3863 string nodeClassName = VuoCompilerPublishedOutputNodeClass::buildNodeClassName(publishedOutputPorts);
3864 return createPublishedNode(nodeClassName, publishedOutputPorts);
3865}
3866
3870VuoNode * VuoCompiler::createPublishedNode(const string &nodeClassName, const vector<VuoPublishedPort *> &publishedPorts)
3871{
3872 VuoCompilerNodeClass *nodeClass = getNodeClass(nodeClassName);
3873 VuoNode *node = createNode(nodeClass);
3874
3875 // Set the generic port types on the node to match those on the published ports set by VuoCompilerComposition::updateGenericPortTypes().
3876 VuoCompilerPublishedInputNodeClass *inputNodeClass = dynamic_cast<VuoCompilerPublishedInputNodeClass *>(nodeClass);
3877 VuoCompilerPublishedOutputNodeClass *outputNodeClass = dynamic_cast<VuoCompilerPublishedOutputNodeClass *>(nodeClass);
3878 for (size_t i = 0; i < publishedPorts.size(); ++i)
3879 {
3880 VuoType *publishedPortType = static_cast<VuoCompilerPort *>(publishedPorts[i]->getCompiler())->getDataVuoType();
3881 if (! dynamic_cast<VuoGenericType *>(publishedPortType))
3882 continue;
3883
3884 set<VuoPort *> nodePorts;
3885 if (inputNodeClass)
3886 {
3887 VuoPort *inputPort = node->getInputPorts().at( inputNodeClass->getInputPortIndexForPublishedInputPort(i) );
3888 nodePorts.insert(inputPort);
3889
3890 VuoPort *outputPort = node->getOutputPorts().at( inputNodeClass->getOutputPortIndexForPublishedInputPort(i) );
3891 nodePorts.insert(outputPort);
3892 }
3893 else
3894 {
3895 VuoPort *inputPort = node->getInputPorts().at( outputNodeClass->getInputPortIndexForPublishedOutputPort(i) );
3896 nodePorts.insert(inputPort);
3897 }
3898
3899 for (VuoPort *port : nodePorts)
3900 {
3901 VuoCompilerPort *compilerPort = static_cast<VuoCompilerPort *>(port->getCompiler());
3902 compilerPort->setDataVuoType(publishedPortType);
3903 }
3904 }
3905
3906 reifyGenericPortTypes(node);
3907
3908 return node;
3909}
3910
3916{
3917 dispatch_sync(environmentQueue, ^{
3918 if (environments.size() >= 5)
3919 environments.at(3).at(0)->addExpatriateSourceFile(sourcePath);
3920 });
3921}
3922
3928{
3929 dispatch_sync(environmentQueue, ^{
3930 if (environments.size() >= 5)
3931 {
3932 environments.at(3).at(0)->removeExpatriateSourceFile(sourcePath);
3933
3934 set<string> sourcesRemoved;
3935 sourcesRemoved.insert(getModuleKeyForPath(sourcePath));
3936 loadModulesAndSources(set<string>(), set<string>(), set<string>(), set<string>(), set<string>(), sourcesRemoved,
3937 false, false, environments.at(3).at(0), nullptr, nullptr, "");
3938 }
3939 });
3940}
3941
3958void VuoCompiler::overrideInstalledNodeClass(const string &sourcePath, const string &sourceCode)
3959{
3960 string sourcePathCopy = sourcePath;
3961 string sourceCodeCopy = sourceCode;
3962
3963 dispatch_async(environmentQueue, ^{
3964 string nodeClassName = getModuleKeyForPath(sourcePathCopy);
3965 VuoModuleInfo *sourceInfo = NULL;
3966
3967 for (const vector<VuoCompilerEnvironment *> &envs : environments)
3968 {
3969 for (VuoCompilerEnvironment *env : envs)
3970 {
3971 VuoModuleInfo *potentialSourceInfo = env->listSourceFile(nodeClassName);
3972 if (potentialSourceInfo && VuoFileUtilities::arePathsEqual(potentialSourceInfo->getFile()->path(), sourcePathCopy))
3973 {
3974 sourceInfo = potentialSourceInfo;
3975 break;
3976 }
3977 }
3978 }
3979
3980 if (! sourceInfo)
3981 return;
3982
3983 if (! sourceCodeCopy.empty())
3984 {
3985 sourceInfo->setSourceCode(sourceCodeCopy);
3986 sourceInfo->setSourceCodeOverridden(true);
3987 sourceInfo->setAttempted(false);
3988 sourceInfo->setLastModifiedToNow();
3989
3990 loadModulesAndSources({}, {}, {}, {}, { nodeClassName }, {}, false, false, sourceInfo->getEnvironment(), nullptr, nullptr, "");
3991 }
3992 else
3993 {
3994 sourceInfo->revertSourceCode();
3995 sourceInfo->setSourceCodeOverridden(false);
3996
3997 sourceInfo->getEnvironment()->deleteOverriddenModuleFile(nodeClassName);
3998
3999 VuoModuleInfo *moduleInfo = sourceInfo->getEnvironment()->listModule(nodeClassName);
4000 moduleInfo->setAttempted(false);
4001 moduleInfo->setLastModifiedToNow();
4002
4003 loadModulesAndSources({}, { nodeClassName }, {}, {}, {}, {}, false, false, sourceInfo->getEnvironment(), nullptr, nullptr, "");
4004 }
4005 });
4006}
4007
4017void VuoCompiler::revertOverriddenNodeClass(const string &sourcePath)
4018{
4019 overrideInstalledNodeClass(sourcePath, "");
4020}
4021
4040{
4041 // For performance, don't bother searching if it's obviously not a node class.
4042
4043 if (! VuoNodeClass::isNodeClassName(nodeClassName) ||
4045 return nullptr;
4046
4047 VuoCompilerModule *module = loadModuleIfNeeded(nodeClassName);
4048 return dynamic_cast<VuoCompilerNodeClass *>(module);
4049}
4050
4056map<string, VuoCompilerNodeClass *> VuoCompiler::getNodeClasses()
4057{
4058 loadModulesIfNeeded();
4059
4060 map<string, VuoCompilerNodeClass *> nodeClasses;
4061 applyToInstalledEnvironments([&nodeClasses](VuoCompilerEnvironment *env)
4062 {
4063 map<string, VuoCompilerNodeClass *> envNodeClasses = env->getNodeClasses();
4064 nodeClasses.insert(envNodeClasses.begin(), envNodeClasses.end());
4065 });
4066
4067 return nodeClasses;
4068}
4069
4075VuoCompilerType * VuoCompiler::getType(const string &typeName)
4076{
4077 if (! VuoGenericType::isGenericTypeName(typeName))
4078 {
4079 VuoCompilerModule *module = loadModuleIfNeeded(typeName);
4080 return dynamic_cast<VuoCompilerType *>(module);
4081 }
4082 else
4083 {
4084 VuoCompilerType * (^compilerGetType)(string) = ^VuoCompilerType * (string moduleKey) {
4085 return getType(moduleKey);
4086 };
4087
4088 VuoGenericType *genericType = new VuoGenericType(typeName, vector<string>());
4089 return VuoCompilerGenericType::newGenericType(genericType, compilerGetType);
4090 }
4091}
4092
4098map<string, VuoCompilerType *> VuoCompiler::getTypes()
4099{
4100 loadModulesIfNeeded();
4101
4102 map<string, VuoCompilerType *> types;
4103 applyToInstalledEnvironments([&types](VuoCompilerEnvironment *env)
4104 {
4105 map<string, VuoCompilerType *> envTypes = env->getTypes();
4106 types.insert(envTypes.begin(), envTypes.end());
4107 });
4108
4109 return types;
4110}
4111
4117VuoCompilerModule * VuoCompiler::getLibraryModule(const string &libraryModuleName)
4118{
4119 return loadModuleIfNeeded(libraryModuleName);
4120}
4121
4127map<string, VuoCompilerModule *> VuoCompiler::getLibraryModules()
4128{
4129 loadModulesIfNeeded();
4130
4131 map<string, VuoCompilerModule *> libraryModules;
4132 applyToInstalledEnvironments([&libraryModules](VuoCompilerEnvironment *env)
4133 {
4134 map<string, VuoCompilerModule *> envLibraryModules = env->getLibraryModules();
4135 libraryModules.insert(envLibraryModules.begin(), envLibraryModules.end());
4136 });
4137
4138 return libraryModules;
4139}
4140
4146map<string, VuoNodeSet *> VuoCompiler::getNodeSets()
4147{
4148 loadModulesIfNeeded();
4149
4150 map<string, VuoNodeSet *> nodeSets;
4151 applyToInstalledEnvironments([&nodeSets](VuoCompilerEnvironment *env)
4152 {
4153 map<string, VuoNodeSet *> envNodeSets = env->getNodeSets();
4154 nodeSets.insert(envNodeSets.begin(), envNodeSets.end());
4155 });
4156
4157 return nodeSets;
4158}
4159
4166{
4167 loadModulesIfNeeded();
4168
4169 VuoNodeSet *nodeSet = nullptr;
4170 applyToInstalledEnvironments([&nodeSet, &name](VuoCompilerEnvironment *env)
4171 {
4172 VuoNodeSet *foundNodeSet = env->findNodeSet(name);
4173 if (foundNodeSet)
4174 nodeSet = foundNodeSet;
4175 });
4176
4177 return nodeSet;
4178}
4179
4183VuoCompilerModule * VuoCompiler::getModule(const string &moduleKey)
4184{
4185 VuoCompilerModule *module = nullptr;
4186 applyToAllEnvironments([&module, &moduleKey](VuoCompilerEnvironment *env)
4187 {
4188 VuoCompilerModule *foundModule = env->findModule(moduleKey);
4189 if (foundModule)
4190 module = foundModule;
4191 });
4192
4193 return module;
4194}
4195
4207void VuoCompiler::listNodeClasses(const string &format)
4208{
4209 map<string, VuoCompilerNodeClass *> nodeClasses = getNodeClasses();
4210 for (map<string, VuoCompilerNodeClass *>::const_iterator i = nodeClasses.begin(); i != nodeClasses.end(); ++i)
4211 {
4212 VuoCompilerNodeClass *nodeClass = i->second;
4213 if (format == "")
4214 {
4215 printf("%s\n", nodeClass->getBase()->getClassName().c_str());
4216 }
4217 else if (format == "path")
4218 {
4219 // TODO: If "path", prints the absolute path of each node class, one per line.
4220 }
4221 else if (format == "dot")
4222 {
4223 VuoCompilerNode *node = nodeClass->newNode()->getCompiler();
4224
4225 printf("%s\n", nodeClass->getDoxygenDocumentation().c_str());
4226 printf("%s\n\n", node->getGraphvizDeclaration().c_str());
4227
4228 delete node;
4229 }
4230 }
4231}
4232
4243vector<string> VuoCompiler::getRunPathSearchPaths(VuoCompilerEnvironment *narrowestScope)
4244{
4245 vector<string> rPaths;
4246 for (size_t i = 0; i < environments.size(); ++i)
4247 {
4248 if (i == 0)
4249 {
4250 string vuoFrameworkPath = getVuoFrameworkPath();
4251 if (! vuoFrameworkPath.empty())
4252 rPaths.push_back(vuoFrameworkPath + "/..");
4253 }
4254 else if (i == 1)
4255 rPaths.push_back(VuoFileUtilities::getSystemModulesPath());
4256 else if (i == 2)
4257 rPaths.push_back(VuoFileUtilities::getUserModulesPath());
4258 else if (i == 3)
4259 {
4260 string localModulesPath = getCompositionLocalModulesPath();
4261 if (! localModulesPath.empty())
4262 rPaths.push_back(localModulesPath);
4263 }
4264
4265 if (std::find(environments[i].begin(), environments[i].end(), narrowestScope) != environments[i].end())
4266 break;
4267 }
4268
4269 std::reverse(rPaths.begin(), rPaths.end());
4270 return rPaths;
4271}
4272
4279vector<string> VuoCompiler::getCoreVuoDependencies(void)
4280{
4281 vector<string> dependencies;
4282
4283 dependencies.push_back("VuoHeap");
4284 dependencies.push_back("VuoApp");
4285
4286 dependencies.push_back("zmq");
4287 dependencies.push_back("json-c");
4288 dependencies.push_back("objc");
4289 dependencies.push_back("c");
4290 dependencies.push_back("AppKit.framework");
4291
4292#ifdef COVERAGE
4293 dependencies.push_back(LLVM_ROOT "/lib/libprofile_rt.dylib");
4294#endif
4295 return dependencies;
4296}
4297
4301string VuoCompiler::getRuntimeMainDependency(void)
4302{
4303 return "VuoRuntimeMain.o";
4304}
4305
4313string VuoCompiler::getRuntimeDependency(void)
4314{
4315 return "VuoRuntime.o";
4316}
4317
4325{
4326 if (! vuoFrameworkInProgressPath.empty())
4327 return vuoFrameworkInProgressPath;
4328
4330}
4331
4335string VuoCompiler::getClangPath(void)
4336{
4337 return clangPath;
4338}
4339
4344{
4345 // Start with the file name without extension.
4346 string dir, moduleKey, ext;
4347 VuoFileUtilities::splitPath(path, dir, moduleKey, ext);
4348
4350 {
4351 vector<string> nodeClassNameParts = VuoStringUtilities::split(moduleKey, '.');
4352
4353 // Remove repeated file extensions.
4354 while (nodeClassNameParts.size() > 1 && nodeClassNameParts.back() == ext)
4355 nodeClassNameParts.pop_back();
4356
4357 // Convert each part to lowerCamelCase.
4358 for (string &part : nodeClassNameParts)
4359 part = VuoStringUtilities::convertToCamelCase(part, false, true, false);
4360
4361 // If the node class name has only one part, add a prefix.
4362 if (nodeClassNameParts.size() == 1)
4363 nodeClassNameParts.insert(nodeClassNameParts.begin(), "isf");
4364
4365 moduleKey = VuoStringUtilities::join(nodeClassNameParts, '.');
4366 }
4367
4368 return moduleKey;
4369}
4370
4376{
4377 __block bool isLocal = false;
4378
4379 dispatch_sync(environmentQueue, ^{
4380 if (environments.size() >= 5)
4381 isLocal = environments.at(3).at(0)->findModule(moduleKey);
4382 });
4383
4384 return isLocal;
4385}
4386
4394{
4395 return lastCompositionBaseDir.empty() ? "" : lastCompositionBaseDir + "/Modules";
4396}
4397
4409{
4410 return lastCompositionBaseDir;
4411}
4412
4416void VuoCompiler::addModuleSearchPath(string path)
4417{
4418 dispatch_sync(environmentQueue, ^{
4419 environments.back().at(0)->addModuleSearchPath(path);
4420 environments.back().at(0)->addLibrarySearchPath(path);
4421 });
4422}
4423
4427void VuoCompiler::addHeaderSearchPath(const string &path)
4428{
4429 dispatch_sync(environmentQueue, ^{
4430 environments.back().at(0)->addHeaderSearchPath(path);
4431 });
4432}
4433
4438{
4439 dispatch_sync(environmentQueue, ^{
4440 environments.back().at(0)->addLibrarySearchPath(path);
4441 });
4442}
4443
4448{
4449 dispatch_sync(environmentQueue, ^{
4450 environments.back().at(0)->addFrameworkSearchPath(path);
4451 });
4452}
4453
4457void VuoCompiler::setVerbose(bool isVerbose)
4458{
4459 this->isVerbose = isVerbose;
4460}
4461
4467{
4468#if VUO_PRO
4469 if (VuoPro::getProAccess())
4470 {
4471 _shouldShowSplashWindow = false;
4472 return;
4473 }
4474#endif
4475
4476 _shouldShowSplashWindow = potentiallyShow;
4477}
4478
4483void VuoCompiler::setDependencyOutput(const string &path)
4484{
4485 dependencyOutputPath = path;
4486}
4487
4492{
4493 return _shouldShowSplashWindow;
4494}
4495
4499void VuoCompiler::setClangPath(const string &clangPath)
4500{
4501 this->clangPath = clangPath;
4502}
4503
4508{
4509 string vuoFrameworkPath = getVuoFrameworkPath();
4510 return (vuoFrameworkPath.empty() ?
4511 VUO_BUILD_DIR "/bin/VuoCompositionLoader.app/Contents/MacOS/VuoCompositionLoader" :
4512 vuoFrameworkPath + "/Helpers/VuoCompositionLoader.app/Contents/MacOS/VuoCompositionLoader");
4513}
4514
4518string VuoCompiler::getCompositionStubPath(void)
4519{
4520 string vuoFrameworkPath = getVuoFrameworkPath();
4521 return (vuoFrameworkPath.empty() ?
4522 VUO_BUILD_DIR "/lib/libVuoCompositionStub.dylib" :
4523 vuoFrameworkPath + "/Modules/libVuoCompositionStub.dylib");
4524}
4525
4530{
4531 vector<string> moduleSearchPaths;
4532 applyToInstalledEnvironments([&moduleSearchPaths](VuoCompilerEnvironment *env)
4533 {
4534 vector<string> envModuleSearchPaths = env->getModuleSearchPaths();
4535 moduleSearchPaths.insert(moduleSearchPaths.end(), envModuleSearchPaths.begin(), envModuleSearchPaths.end());
4536 });
4537
4538 vector<string> headerSearchPaths;
4539 applyToInstalledEnvironments([&headerSearchPaths](VuoCompilerEnvironment *env)
4540 {
4541 vector<string> envHeaderSearchPaths = env->getHeaderSearchPaths();
4542 headerSearchPaths.insert(headerSearchPaths.end(), envHeaderSearchPaths.begin(), envHeaderSearchPaths.end());
4543 });
4544
4545 vector<string> librarySearchPaths;
4546 applyToInstalledEnvironments([&librarySearchPaths](VuoCompilerEnvironment *env)
4547 {
4548 vector<string> envLibrarySearchPaths = env->getLibrarySearchPaths();
4549 librarySearchPaths.insert(librarySearchPaths.end(), envLibrarySearchPaths.begin(), envLibrarySearchPaths.end());
4550 });
4551
4552 vector<string> frameworkSearchPaths;
4553 applyToInstalledEnvironments([&frameworkSearchPaths](VuoCompilerEnvironment *env)
4554 {
4555 vector<string> envFrameworkSearchPaths = env->getFrameworkSearchPaths();
4556 frameworkSearchPaths.insert(frameworkSearchPaths.end(), envFrameworkSearchPaths.begin(), envFrameworkSearchPaths.end());
4557 });
4558
4559 fprintf(stderr, "Module (node class, type, library) search paths:\n");
4560 for (vector<string>::iterator i = moduleSearchPaths.begin(); i != moduleSearchPaths.end(); ++i)
4561 fprintf(stderr, " %s\n", (*i).c_str());
4562 fprintf(stderr, "Header search paths:\n");
4563 for (vector<string>::iterator i = headerSearchPaths.begin(); i != headerSearchPaths.end(); ++i)
4564 fprintf(stderr, " %s\n", (*i).c_str());
4565 fprintf(stderr, "Other library search paths:\n");
4566 for (vector<string>::iterator i = librarySearchPaths.begin(); i != librarySearchPaths.end(); ++i)
4567 fprintf(stderr, " %s\n", (*i).c_str());
4568 fprintf(stderr, "Other framework search paths:\n");
4569 for (vector<string>::iterator i = frameworkSearchPaths.begin(); i != frameworkSearchPaths.end(); ++i)
4570 fprintf(stderr, " %s\n", (*i).c_str());
4571 fprintf(stderr, "Framework path:\n");
4572 if (! getVuoFrameworkPath().empty())
4573 fprintf(stderr, " %s\n", getVuoFrameworkPath().c_str());
4574 fprintf(stderr, "Clang path:\n");
4575 if (! getClangPath().empty())
4576 fprintf(stderr, " %s\n", getClangPath().c_str());
4577}
4578
4588{
4589 try
4590 {
4591 VuoCompiler compiler(compositionFilePath);
4592 string directory, file, extension;
4593 VuoFileUtilities::splitPath(compositionFilePath, directory, file, extension);
4594 string compiledCompositionPath = VuoFileUtilities::makeTmpFile(file, "bc");
4595 string linkedCompositionPath = VuoFileUtilities::makeTmpFile(file + "-linked", "");
4596 compiler.compileComposition(compositionFilePath, compiledCompositionPath, true, issues);
4597 compiler.linkCompositionToCreateExecutable(compiledCompositionPath, linkedCompositionPath, Optimization_ModuleCaches);
4598 remove(compiledCompositionPath.c_str());
4599
4600 typedef VuoRunner * (*runnerFunctionType)(const char *, const char *, bool, bool);
4601 runnerFunctionType runnerFunction = (runnerFunctionType)dlsym(RTLD_DEFAULT, "VuoRunner_newSeparateProcessRunnerFromExecutable");
4602 return runnerFunction(linkedCompositionPath.c_str(), directory.c_str(), false, true);
4603 }
4604 catch (VuoCompilerException &e)
4605 {
4606 if (issues != e.getIssues())
4607 issues->append(e.getIssues());
4608 return NULL;
4609 }
4610}
4611
4626VuoRunner * VuoCompiler::newSeparateProcessRunnerFromCompositionString(string composition, string processName, string workingDirectory, VuoCompilerIssues *issues)
4627{
4628 try
4629 {
4630 string compositionPath = (workingDirectory.empty() ? "." : workingDirectory) + "/" + processName + ".vuo";
4631 VuoCompiler compiler(compositionPath);
4632 string compiledCompositionPath = VuoFileUtilities::makeTmpFile(processName, "bc");
4633 string linkedCompositionPath = VuoFileUtilities::makeTmpFile(processName, "");
4634 compiler.compileCompositionString(composition, compiledCompositionPath, true, issues);
4635 compiler.linkCompositionToCreateExecutable(compiledCompositionPath, linkedCompositionPath, Optimization_ModuleCaches);
4636 remove(compiledCompositionPath.c_str());
4637
4638 typedef VuoRunner * (*runnerFunctionType)(const char *, const char *, bool, bool);
4639 runnerFunctionType runnerFunction = (runnerFunctionType)dlsym(RTLD_DEFAULT, "VuoRunner_newSeparateProcessRunnerFromExecutable");
4640 return runnerFunction(linkedCompositionPath.c_str(), workingDirectory.c_str(), false, true);
4641 }
4642 catch (VuoCompilerException &e)
4643 {
4644 if (issues != e.getIssues())
4645 issues->append(e.getIssues());
4646 return NULL;
4647 }
4648}
4649
4659{
4660 try
4661 {
4662 VuoCompiler compiler(compositionFilePath);
4663 string directory, file, extension;
4664 VuoFileUtilities::splitPath(compositionFilePath, directory, file, extension);
4665 string compiledCompositionPath = VuoFileUtilities::makeTmpFile(file, "bc");
4666 compiler.compileComposition(compositionFilePath, compiledCompositionPath, true, issues);
4667 string linkedCompositionPath = VuoFileUtilities::makeTmpFile(file, "dylib");
4668 compiler.linkCompositionToCreateDynamicLibrary(compiledCompositionPath, linkedCompositionPath, Optimization_ModuleCaches);
4669 remove(compiledCompositionPath.c_str());
4670
4671 typedef VuoRunner * (*runnerFunctionType)(const char *, const char *, bool);
4672 runnerFunctionType runnerFunction = (runnerFunctionType)dlsym(RTLD_DEFAULT, "VuoRunner_newCurrentProcessRunnerFromDynamicLibrary");
4673 return runnerFunction(linkedCompositionPath.c_str(), directory.c_str(), true);
4674 }
4675 catch (VuoCompilerException &e)
4676 {
4677 if (issues != e.getIssues())
4678 issues->append(e.getIssues());
4679 return NULL;
4680 }
4681}
4682
4698{
4699 try
4700 {
4701 string compositionPath = (workingDirectory.empty() ? "." : workingDirectory) + "/UntitledComposition.vuo";
4702 VuoCompiler compiler(compositionPath);
4703 string compiledCompositionPath = VuoFileUtilities::makeTmpFile("VuoRunnerComposition", "bc");
4704 compiler.compileCompositionString(composition, compiledCompositionPath, true, issues);
4705 string linkedCompositionPath = VuoFileUtilities::makeTmpFile("VuoRunnerComposition", "dylib");
4706 compiler.linkCompositionToCreateDynamicLibrary(compiledCompositionPath, linkedCompositionPath, Optimization_ModuleCaches);
4707 remove(compiledCompositionPath.c_str());
4708
4709 typedef VuoRunner * (*runnerFunctionType)(const char *, const char *, bool);
4710 runnerFunctionType runnerFunction = (runnerFunctionType)dlsym(RTLD_DEFAULT, "VuoRunner_newCurrentProcessRunnerFromDynamicLibrary");
4711 return runnerFunction(linkedCompositionPath.c_str(), workingDirectory.c_str(), true);
4712 }
4713 catch (VuoCompilerException &e)
4714 {
4715 if (issues != e.getIssues())
4716 issues->append(e.getIssues());
4717 return NULL;
4718 }
4719}