Vuo  2.4.1
VuoCompiler.cc
Go to the documentation of this file.
1
10#include <string.h>
11#include <sys/param.h>
12#include <sys/stat.h>
13#include <sys/time.h>
14#include <sstream>
15#include <CoreFoundation/CoreFoundation.h>
16#include "VuoCompiler.hh"
23#include "VuoCompilerGraph.hh"
25#include "VuoCompilerIssue.hh"
26#include "VuoCompilerNode.hh"
27#include "VuoCompilerPort.hh"
31#include "VuoCompilerGroup.hh"
33#include "VuoComposition.hh"
34#include "VuoException.hh"
35#include "VuoGenericType.hh"
36#include "VuoModuleCompiler.hh"
38#include "VuoNode.hh"
39#include "VuoNodeClass.hh"
40#include "VuoNodeSet.hh"
41#include "VuoPublishedPort.hh"
42#include "VuoRunner.hh"
44#include "VuoStringUtilities.hh"
45
46#if VUO_PRO
47#include "VuoPro.hh"
48#endif
49
50map<string, VuoFileUtilities::File *> VuoCompiler::Environment::moduleCacheFileForLocking;
51dispatch_queue_t VuoCompiler::Environment::moduleCacheBuildingQueue = dispatch_queue_create("org.vuo.compiler.cache", NULL);
52const string VuoCompiler::Environment::pidCacheDirPrefix = "pid-";
53set<VuoCompiler *> VuoCompiler::allCompilers;
54dispatch_queue_t VuoCompiler::environmentQueue = dispatch_queue_create("org.vuo.compiler.environment", NULL);
55map<string, vector< vector<VuoCompiler::Environment *> > > VuoCompiler::sharedEnvironments;
56map<string, map<string, vector<VuoCompiler::Environment *> > > VuoCompiler::environmentsForCompositionFamily;
57dispatch_group_t VuoCompiler::moduleSourceCompilersExistGlobally = dispatch_group_create();
58string VuoCompiler::vuoFrameworkInProgressPath;
59
60dispatch_queue_t llvmQueue = NULL;
61
62llvm::LLVMContext *VuoCompiler::globalLLVMContext = nullptr;
63
67static void __attribute__((constructor)) VuoCompiler_init(void)
68{
69 // Increase the open-files limit (macOS defaults to 256).
70 struct rlimit rl{OPEN_MAX, OPEN_MAX};
71 getrlimit(RLIMIT_NOFILE, &rl);
72 rl.rlim_cur = MIN(OPEN_MAX, rl.rlim_max);
73 if (setrlimit(RLIMIT_NOFILE, &rl))
74 VUserLog("Warning: Couldn't set open-files limit: %s", strerror(errno));
75
76
77 llvmQueue = dispatch_queue_create("org.vuo.compiler.llvm", NULL);
78
79 dispatch_sync(llvmQueue, ^{
80 // llvm::InitializeNativeTarget();
81 llvm::InitializeAllTargetMCs();
82 llvm::InitializeAllTargets();
83 // TargetRegistry::printRegisteredTargetsForVersion();
84
85 VuoCompiler::globalLLVMContext = new llvm::LLVMContext;
86
87 // Set up a diagnostic handler so that llvm::LLVMContext::diagnose doesn't exit
88 // when a call to llvm::Linker::linkInModule generates an error.
89 VuoCompiler::globalLLVMContext->setDiagnosticHandler([](const DiagnosticInfo &DI, void *context) {
90 string message;
91 raw_string_ostream stream(message);
92 DiagnosticPrinterRawOStream printer(stream);
93 DI.print(printer);
94 stream.flush();
95 if (! message.empty())
96 {
97 const char *severityName;
98 if (DI.getSeverity() == DS_Error)
99 severityName = "error";
100 else if (DI.getSeverity() == DS_Warning)
101 severityName = "warning";
102 else
103 severityName = "note";
104
105 VuoLog(VuoLog_moduleName, __FILE__, __LINE__, "llvmDiagnosticHandler", "%s: %s", severityName, message.c_str());
106 }
107 });
108
109 // If the Vuo compiler/linker...
110 // 1. Loads a node class that uses dispatch_object_t.
111 // 2. Generates code that uses dispatch_object_t.
112 // 3. Links the node class into a composition.
113 // 4. Generates more code that uses dispatch_object_t.
114 // ... then Step 4 ends up with the wrong llvm::Type for dispatch_object_t.
115 //
116 // A workaround is to generate some code that uses dispatch_object_t before doing Step 1.
117 //
118 // https://b33p.net/kosada/node/3845
119 // http://lists.cs.uiuc.edu/pipermail/llvmdev/2012-December/057075.html
120 Module module("", *VuoCompiler::globalLLVMContext);
122
123 // Workaround for a possibly related error where the compiler ends up with the wrong
124 // llvm::Type for dispatch_semaphore_s. https://b33p.net/kosada/node/10545
126
127 // Load the NodeContext and PortContext struct types preemptively to make sure that
128 // their fields get the right dispatch types. If these struct types were to be loaded
129 // first from a subcomposition module, they'd get the wrong dispatch types.
130 // https://b33p.net/kosada/node/11160
133 });
134}
135
139VuoCompiler::ModuleInfo::ModuleInfo(Environment *environment, const string &searchPath, const string &relativePath,
140 bool isSourceFile, bool isSubcomposition)
141{
142 VuoFileUtilities::File *file = new VuoFileUtilities::File(searchPath, relativePath);
143 initialize(environment, searchPath, file, isSourceFile, isSubcomposition);
144}
145
149VuoCompiler::ModuleInfo::ModuleInfo(Environment *environment, const string &searchPath, VuoFileUtilities::File *file,
150 bool isSourceFile, bool isSubcomposition)
151{
152 initialize(environment, searchPath, file, isSourceFile, isSubcomposition);
153}
154
158void VuoCompiler::ModuleInfo::initialize(Environment *environment, const string &searchPath, VuoFileUtilities::File *file,
159 bool isSourceFile, bool isSubcomposition)
160{
161 this->environment = environment;
162 this->searchPath = searchPath;
163 this->file = file;
164 moduleKey = getModuleKeyForPath(file->getRelativePath());
165 sourceCodeOverridden = false;
166 lastModified = VuoFileUtilities::getFileLastModifiedInSeconds(file->isInArchive() ? file->getArchivePath() : file->path());
167 longestDownstreamPath = 0;
168 attempted = false;
169 loading = nullptr;
170
171#if VUO_PRO
172 init_Pro();
173#endif
174
175 if (isSourceFile)
176 {
177 loading = dispatch_group_create();
178 sourceCode = file->getContentsAsString();
179
180 if (isSubcomposition)
181 {
182 try
183 {
185 }
186 catch (...)
187 {
188 // Issues parsing the composition will be caught later when compiling it.
189 }
190 }
191 }
192}
193
194VuoCompiler::ModuleInfo::~ModuleInfo(void)
195{
196#if VUO_PRO
197 fini_Pro();
198#endif
199
200 delete file;
201
202 if (loading)
203 dispatch_release(loading);
204}
205
206VuoCompiler::Environment * VuoCompiler::ModuleInfo::getEnvironment(void) const
207{
208 return environment;
209}
210
211string VuoCompiler::ModuleInfo::getSearchPath(void) const
212{
213 return searchPath;
214}
215
216string VuoCompiler::ModuleInfo::getModuleKey(void) const
217{
218 return moduleKey;
219}
220
221VuoFileUtilities::File * VuoCompiler::ModuleInfo::getFile(void) const
222{
223 return file;
224}
225
226string VuoCompiler::ModuleInfo::getSourceCode(void) const
227{
228 return sourceCode;
229}
230
231void VuoCompiler::ModuleInfo::setSourceCode(const string &sourceCode)
232{
233 this->sourceCode = sourceCode;
234}
235
236void VuoCompiler::ModuleInfo::revertSourceCode(void)
237{
238 sourceCode = file->getContentsAsString();
239}
240
241bool VuoCompiler::ModuleInfo::isSourceCodeOverridden(void) const
242{
243 return sourceCodeOverridden;
244}
245
246void VuoCompiler::ModuleInfo::setSourceCodeOverridden(bool overridden)
247{
248 sourceCodeOverridden = overridden;
249}
250
251bool VuoCompiler::ModuleInfo::isNewerThan(ModuleInfo *other) const
252{
253 return lastModified > other->lastModified;
254}
255
256bool VuoCompiler::ModuleInfo::isNewerThan(unsigned long ageInSeconds) const
257{
258 return lastModified > ageInSeconds;
259}
260
261bool VuoCompiler::ModuleInfo::isOlderThan(unsigned long ageInSeconds) const
262{
263 return lastModified < ageInSeconds;
264}
265
266void VuoCompiler::ModuleInfo::setLastModifiedToNow(void)
267{
268 struct timeval t;
269 gettimeofday(&t, NULL);
270 lastModified = t.tv_sec;
271}
272
273set<string> VuoCompiler::ModuleInfo::getContainedNodeClasses(void) const
274{
275 return containedNodeClasses;
276}
277
278int VuoCompiler::ModuleInfo::getLongestDownstreamPath(void) const
279{
280 return longestDownstreamPath;
281}
282
283void VuoCompiler::ModuleInfo::setLongestDownstreamPath(int pathLength)
284{
285 longestDownstreamPath = pathLength;
286}
287
288bool VuoCompiler::ModuleInfo::getAttempted(void) const
289{
290 return attempted;
291}
292
293void VuoCompiler::ModuleInfo::setAttempted(bool attempted)
294{
295 this->attempted = attempted;
296}
297
298dispatch_group_t VuoCompiler::ModuleInfo::getLoadingGroup(void) const
299{
300 return loading;
301}
302
303void VuoCompiler::ModuleInfo::dump() const
304{
305 fprintf(stderr, "module %s:\n"
306 "\tloadingGroup=%p\n"
307 "\tsearchPath=%s\n"
308 "\tattempted=%d\n"
309 "\tenvironment=%s\n"
310 "\tfile=%s%s%s\n"
311 "\tsourceCodeOverridden=%d\n"
312 "\tsourceCode=%s\n"
313 "\tcontainedNodeClasses:\n",
314 moduleKey.c_str(),
315 loading,
316 searchPath.c_str(),
317 attempted,
318 environment->getCompiledModuleCachePath().c_str(),
319 file->isInArchive() ? file->getArchivePath().c_str() : "",
320 file->isInArchive() ? "::" : "",
321 file->isInArchive() ? file->getRelativePath().c_str() : file->path().c_str(),
322 sourceCodeOverridden,
323 sourceCode.c_str());
324 for (auto i: containedNodeClasses)
325 fprintf(stderr, "\t\t%s\n", i.c_str());
326}
327
328VuoCompiler::DependencyGraphVertex * VuoCompiler::DependencyGraphVertex::vertexForDependency(const string &dependency, VuoDirectedAcyclicGraph *graph)
329{
330 VuoDirectedAcyclicGraph::Vertex *v = graph->findVertex(dependency);
331 if (v)
332 return dynamic_cast<DependencyGraphVertex *>(v);
333
334 DependencyGraphVertex *vv = new DependencyGraphVertex();
335 vv->dependency = dependency;
336 vv->environment = NULL;
337 vv->compatible = true;
338 return vv;
339}
340
341string VuoCompiler::DependencyGraphVertex::getDependency(void)
342{
343 return dependency;
344}
345
346VuoCompiler::Environment * VuoCompiler::DependencyGraphVertex::getEnvironment(void)
347{
348 return environment;
349}
350
351void VuoCompiler::DependencyGraphVertex::setEnvironment(Environment *environment)
352{
353 this->environment = environment;
354}
355
356string VuoCompiler::DependencyGraphVertex::key(void)
357{
358 return dependency;
359}
360
368VuoCompiler::ModuleInfoIterator::ModuleInfoIterator(map<string, map<string, ModuleInfo *> > *allModuleInfos,
369 const string &overriddenCompiledModuleCachePath)
370{
371 this->allModuleInfos = allModuleInfos;
372 this->overriddenCompiledModuleCachePath = overriddenCompiledModuleCachePath;
373 hasSearchModuleKeys = false;
374 initialize();
375}
376
384VuoCompiler::ModuleInfoIterator::ModuleInfoIterator(map<string, map<string, ModuleInfo *> > *allModuleInfos,
385 const string &overriddenCompiledModuleCachePath,
386 const set<string> &searchModuleKeys)
387{
388 this->allModuleInfos = allModuleInfos;
389 this->overriddenCompiledModuleCachePath = overriddenCompiledModuleCachePath;
390 this->searchModuleKeys = searchModuleKeys;
391 hasSearchModuleKeys = true;
392 initialize();
393}
394
400void VuoCompiler::ModuleInfoIterator::initialize(void)
401{
402 currSearchPath = allModuleInfos->begin();
403 hasCurrModuleKey = false;
404
405 if (! overriddenCompiledModuleCachePath.empty())
406 {
407 auto i = allModuleInfos->find(overriddenCompiledModuleCachePath);
408 if (i != allModuleInfos->end())
409 {
410 std::transform(i->second.begin(), i->second.end(),
411 std::inserter(overriddenModuleKeys, overriddenModuleKeys.begin()),
412 [](const pair<string, ModuleInfo *> &p) { return p.first; });
413 }
414 }
415}
416
422VuoCompiler::ModuleInfo * VuoCompiler::ModuleInfoIterator::next(void)
423{
424 for ( ; currSearchPath != allModuleInfos->end(); ++currSearchPath)
425 {
426 if (! hasCurrModuleKey)
427 {
428 currModuleKey = currSearchPath->second.begin();
429 hasCurrModuleKey = true;
430 }
431
432 bool isOverriddenCompiledModuleCachePath = ! overriddenCompiledModuleCachePath.empty() &&
433 VuoFileUtilities::arePathsEqual(currSearchPath->first, overriddenCompiledModuleCachePath);
434
435 for ( ; currModuleKey != currSearchPath->second.end(); ++currModuleKey)
436 {
437 if ((! hasSearchModuleKeys || searchModuleKeys.find(currModuleKey->first) != searchModuleKeys.end()) &&
438 (isOverriddenCompiledModuleCachePath || overriddenModuleKeys.find(currModuleKey->first) == overriddenModuleKeys.end()))
439 {
440 ModuleInfo *moduleInfo = currModuleKey->second;
441 ++currModuleKey;
442 return moduleInfo;
443 }
444 }
445
446 hasCurrModuleKey = false;
447 }
448
449 return NULL;
450}
451
455VuoCompiler::Environment::Environment(string target, bool builtIn, bool generated)
456 : target(target), builtIn(builtIn), generated(generated)
457{
458 compilersToNotifyQueue = dispatch_queue_create("org.vuo.compiler.notify", 0);
459 moduleSearchPathContentsChangedQueue = dispatch_queue_create("org.vuo.compiler.watch", 0);
460 lastModuleCacheRebuild = 0;
461 isModuleCacheableDataDirty = false;
462 isModuleCacheInitialized = false;
463 isModuleCacheAvailable = false;
464 dependencyGraph = new VuoDirectedAcyclicGraph();
465 compositionDependencyGraph = new VuoDirectedAcyclicGraph();
466 moduleCompilationQueue = new VuoModuleCompilationQueue();
467}
468
472VuoCompiler::Environment::~Environment(void)
473{
474 stopWatchingModuleSearchPaths();
475 dispatch_sync(moduleSearchPathContentsChangedQueue, ^{});
476
477 dispatch_release(moduleSearchPathContentsChangedQueue);
478 dispatch_release(compilersToNotifyQueue);
479
480 for (map<string, VuoCompilerNodeClass *>::iterator i = nodeClasses.begin(); i != nodeClasses.end(); ++i)
481 destroyModule(i->second);
482
483 for (map<string, VuoCompilerType *>::iterator i = types.begin(); i != types.end(); ++i)
484 destroyModule(i->second);
485
486 for (map<string, VuoCompilerModule *>::iterator i = libraryModules.begin(); i != libraryModules.end(); ++i)
487 destroyModule(i->second);
488
489 for (set<VuoCompilerGenericType *>::iterator i = genericTypes.begin(); i != genericTypes.end(); ++i)
490 {
491 VuoType *base = (*i)->getBase();
492 delete *i;
493 delete base;
494 }
495
496 for (map<string, VuoNodeSet *>::iterator i = nodeSetForName.begin(); i != nodeSetForName.end(); ++i)
497 delete i->second;
498
499 for (map<string, map<string, ModuleInfo *> >::iterator i = moduleFilesAtSearchPath.begin(); i != moduleFilesAtSearchPath.end(); ++i)
500 for (map<string, ModuleInfo *>::iterator j = i->second.begin(); j != i->second.end(); ++j)
501 delete j->second;
502
503 for (map<string, map<string, ModuleInfo *> >::iterator i = sourceFilesAtSearchPath.begin(); i != sourceFilesAtSearchPath.end(); ++i)
504 for (map<string, ModuleInfo *>::iterator j = i->second.begin(); j != i->second.end(); ++j)
505 delete j->second;
506
507 delete dependencyGraph;
508 delete compositionDependencyGraph;
509}
510
514string VuoCompiler::Environment::getTarget()
515{
516 return target;
517}
518
522void VuoCompiler::Environment::addCompilerToNotify(VuoCompiler *compiler)
523{
524 dispatch_sync(compilersToNotifyQueue, ^{
525 compilersToNotify.insert(compiler);
526 });
527}
528
532void VuoCompiler::Environment::removeCompilerToNotify(VuoCompiler *compiler)
533{
534 dispatch_sync(compilersToNotifyQueue, ^{
535 compilersToNotify.erase(compiler);
536 });
537}
538
544map<string, VuoCompilerNodeClass *> VuoCompiler::Environment::getNodeClasses(void)
545{
546 return nodeClasses;
547}
548
554VuoCompilerNodeClass * VuoCompiler::Environment::getNodeClass(const string &moduleKey)
555{
556 map<string, VuoCompilerNodeClass *>::iterator nodeClassIter = nodeClasses.find(moduleKey);
557 if (nodeClassIter != nodeClasses.end())
558 return nodeClassIter->second;
559
560 return NULL;
561}
562
567VuoCompilerNodeClass * VuoCompiler::Environment::takeNodeClass(const string &moduleKey)
568{
569 VuoCompilerNodeClass *nodeClass = NULL;
570
571 map<string, VuoCompilerNodeClass *>::iterator nodeClassIter = nodeClasses.find(moduleKey);
572 if (nodeClassIter != nodeClasses.end())
573 {
574 nodeClass = nodeClassIter->second;
575
576 nodeClasses.erase(nodeClassIter);
577 removeFromDependencyGraph(nodeClass);
578 modulesChanged();
579 }
580
581 return nodeClass;
582}
583
589map<string, VuoCompilerType *> VuoCompiler::Environment::getTypes(void)
590{
591 return types;
592}
593
599VuoCompilerType * VuoCompiler::Environment::getType(const string &moduleKey)
600{
601 map<string, VuoCompilerType *>::iterator typeIter = types.find(moduleKey);
602 if (typeIter != types.end())
603 return typeIter->second;
604
605 return NULL;
606}
607
613map<string, VuoNodeSet *> VuoCompiler::Environment::getNodeSets(void)
614{
615 return nodeSetForName;
616}
617
623VuoCompilerModule *VuoCompiler::Environment::getLibraryModule(const string &libraryModuleName)
624{
625 map<string, VuoCompilerModule *>::iterator libraryIter = libraryModules.find(libraryModuleName);
626 if (libraryIter != libraryModules.end())
627 return libraryIter->second;
628
629 return nullptr;
630}
631
637map<string, VuoCompilerModule *> VuoCompiler::Environment::getLibraryModules(void)
638{
639 return libraryModules;
640}
641
648VuoCompilerModule * VuoCompiler::Environment::findModule(const string &moduleKey)
649{
650 map<string, VuoCompilerNodeClass *>::iterator nodeClassIter = nodeClasses.find(moduleKey);
651 if (nodeClassIter != nodeClasses.end())
652 return nodeClassIter->second;
653
654 map<string, VuoCompilerType *>::iterator typeIter = types.find(moduleKey);
655 if (typeIter != types.end())
656 return typeIter->second;
657
658 map<string, VuoCompilerModule *>::iterator libraryIter = libraryModules.find(moduleKey);
659 if (libraryIter != libraryModules.end())
660 return libraryIter->second;
661
662 return NULL;
663}
664
670VuoNodeSet * VuoCompiler::Environment::findNodeSet(const string &name)
671{
672
673 map<string, VuoNodeSet *>::iterator nodeSetIter = nodeSetForName.find(name);
674 if (nodeSetIter != nodeSetForName.end())
675 return nodeSetIter->second;
676
677 return NULL;
678}
679
686void VuoCompiler::Environment::addModuleSearchPath(const string &path, bool shouldWatch)
687{
688 moduleSearchPaths.push_back(path);
689
690 updateModulesAtSearchPath(path);
691 updateSourceFilesAtSearchPath(path);
692
693 if (shouldWatch)
694 startWatchingModuleSearchPath(path);
695}
696
702vector<string> VuoCompiler::Environment::getModuleSearchPaths(void)
703{
704 return moduleSearchPaths;
705}
706
712void VuoCompiler::Environment::addHeaderSearchPath(const string &path)
713{
714 headerSearchPaths.push_back(path);
715}
716
722vector<string> VuoCompiler::Environment::getHeaderSearchPaths(void)
723{
724 return headerSearchPaths;
725}
726
732void VuoCompiler::Environment::addLibrarySearchPath(const string &path)
733{
734 librarySearchPaths.push_back(path);
735}
736
742vector<string> VuoCompiler::Environment::getLibrarySearchPaths(void)
743{
744 return librarySearchPaths;
745}
746
752void VuoCompiler::Environment::addFrameworkSearchPath(const string &path)
753{
754 frameworkSearchPaths.push_back(path);
755}
756
762vector<string> VuoCompiler::Environment::getFrameworkSearchPaths(void)
763{
764 return frameworkSearchPaths;
765}
766
767VuoDirectedAcyclicGraph * VuoCompiler::Environment::getDependencyGraph(void)
768{
769 return dependencyGraph;
770}
771
772VuoDirectedAcyclicGraph * VuoCompiler::Environment::getCompositionDependencyGraph(void)
773{
774 return compositionDependencyGraph;
775}
776
782void VuoCompiler::Environment::setModuleCachePath(const string &path, bool shouldAddModuleSearchPath)
783{
784 moduleCachePath = path;
785
786 if (shouldAddModuleSearchPath)
787 {
788 addModuleSearchPath(getCompiledModuleCachePath(), false);
789 addModuleSearchPath(getOverriddenCompiledModuleCachePath(), false);
790 }
791}
792
798string VuoCompiler::Environment::getCompiledModuleCachePath(void)
799{
800 return (moduleCachePath.empty() ? "" : moduleCachePath + "/Modules/" + VuoCompiler::getTargetArch(target));
801}
802
808string VuoCompiler::Environment::getOverriddenCompiledModuleCachePath(void)
809{
810 if (moduleCachePath.empty())
811 return "";
812
813 ostringstream pid;
814 pid << getpid();
815
816 string dir, moduleCacheDirName, ext;
817 VuoFileUtilities::splitPath(moduleCachePath, dir, moduleCacheDirName, ext);
818
819 return (VuoFileUtilities::getCachePath() + "/" + pidCacheDirPrefix + pid.str() + "/" + moduleCacheDirName + "/Modules/" + VuoCompiler::getTargetArch(target));
820}
821
829void VuoCompiler::Environment::addExpatriateSourceFile(const string &sourcePath)
830{
831 expatriateSourceFiles.push_back(sourcePath);
832
833 if (find(moduleSearchPaths.begin(), moduleSearchPaths.end(), "") == moduleSearchPaths.end())
834 moduleSearchPaths.push_back("");
835
836 auto iter = sourceFilesAtSearchPath.find("");
837 if (iter != sourceFilesAtSearchPath.end())
838 sourceFilesAtSearchPath.erase(iter);
839}
840
846void VuoCompiler::Environment::removeExpatriateSourceFile(const string &sourcePath)
847{
848 for (auto i = expatriateSourceFiles.begin(); i != expatriateSourceFiles.end(); ++i)
849 {
850 if (VuoFileUtilities::arePathsEqual(*i, sourcePath))
851 {
852 expatriateSourceFiles.erase(i);
853
854 auto iter = sourceFilesAtSearchPath.find("");
855 if (iter != sourceFilesAtSearchPath.end())
856 sourceFilesAtSearchPath.erase(iter);
857
858 break;
859 }
860 }
861}
862
875void VuoCompiler::Environment::updateModulesAtSearchPath(const string &path)
876{
877 if (moduleFilesAtSearchPath.find(path) != moduleFilesAtSearchPath.end())
878 return;
879
880 set<string> moduleExtensions;
881 moduleExtensions.insert("vuonode");
882 moduleExtensions.insert("vuonode+");
883 moduleExtensions.insert("bc");
884 moduleExtensions.insert("bc+");
885 set<string> archiveExtensions;
886 archiveExtensions.insert("vuonode");
887 set<VuoFileUtilities::File *> moduleFiles = VuoFileUtilities::findFilesInDirectory(path, moduleExtensions, archiveExtensions);
888
889 map<string, ModuleInfo *> fileForModuleKey;
890 for (set<VuoFileUtilities::File *>::iterator i = moduleFiles.begin(); i != moduleFiles.end(); ++i)
891 {
892 VuoFileUtilities::File *moduleFile = *i;
893
894 // Ignore macOS extended attribute storage (a.k.a. xattr, resource fork).
895 if (VuoStringUtilities::beginsWith(moduleFile->basename(), "._"))
896 continue;
897
898 ModuleInfo *m = new ModuleInfo(this, path, moduleFile, false, false);
899 fileForModuleKey[m->getModuleKey()] = m;
900 }
901
902 if (path == getCompiledModuleCachePath())
903 {
904 for (map<string, ModuleInfo *>::iterator i = fileForModuleKey.begin(); i != fileForModuleKey.end(); )
905 {
906 ModuleInfo *sourceInfo = listSourceFile(i->first);
907 if (! sourceInfo || sourceInfo->isNewerThan(i->second))
908 {
909 ModuleInfo *m = i->second;
910 VuoFileUtilities::deleteFile(m->getFile()->path());
911 delete m;
912 fileForModuleKey.erase(i++);
913 }
914 else
915 ++i;
916 }
917 }
918
919 moduleFilesAtSearchPath[path] = fileForModuleKey;
920}
921
927void VuoCompiler::Environment::updateModuleAtSearchPath(const string &moduleSearchPath, const string &moduleRelativePath)
928{
929 string dir, file, ext;
930 VuoFileUtilities::splitPath(moduleRelativePath, dir, file, ext);
931
932 set<string> moduleExtensions;
933 moduleExtensions.insert(ext);
934 set<string> archiveExtensions;
935 archiveExtensions.insert("vuonode");
936
937 set<VuoFileUtilities::File *> moduleFiles = VuoFileUtilities::findFilesInDirectory(moduleSearchPath, moduleExtensions, archiveExtensions);
938
939 for (set<VuoFileUtilities::File *>::iterator i = moduleFiles.begin(); i != moduleFiles.end(); ++i)
940 {
941 VuoFileUtilities::File *moduleFile = *i;
942
943 ModuleInfo *m = new ModuleInfo(this, moduleSearchPath, moduleFile, false, false);
944 moduleFilesAtSearchPath[moduleSearchPath][m->getModuleKey()] = m;
945 }
946}
947
955void VuoCompiler::Environment::updateSourceFilesAtSearchPath(const string &path)
956{
957 map<string, map<string, ModuleInfo *> >::iterator sourceFilesIter = sourceFilesAtSearchPath.find(path);
958 if (sourceFilesIter != sourceFilesAtSearchPath.end())
959 return;
960
961 set<VuoFileUtilities::File *> sourceFiles;
962 if (! path.empty())
963 {
964 set<string> sourceExtensions;
965 sourceExtensions.insert("vuo");
966 sourceExtensions.insert("fs");
968 sourceExtensions.insert(cext.begin(), cext.end());
969 sourceFiles = VuoFileUtilities::findFilesInDirectory(path, sourceExtensions, set<string>());
970 }
971 else
972 {
973 for (const string &sourcePath : expatriateSourceFiles)
974 {
975 string dir, file, ext;
976 VuoFileUtilities::splitPath(sourcePath, dir, file, ext);
977 VuoFileUtilities::File *sourceFile = new VuoFileUtilities::File(dir, file + "." + ext);
978 sourceFiles.insert(sourceFile);
979 }
980 }
981
982 map<string, ModuleInfo *> fileForModuleKey;
983 for (set<VuoFileUtilities::File *>::iterator i = sourceFiles.begin(); i != sourceFiles.end(); ++i)
984 {
985 VuoFileUtilities::File *sourceFile = *i;
986
987 string dir, moduleKey, ext;
988 VuoFileUtilities::splitPath(sourceFile->getRelativePath(), dir, moduleKey, ext);
989 bool isSubcomposition = (ext == "vuo");
990
991 // Ignore missing expatriateSourceFiles — they might have been deleted in the meantime.
992 if (path.empty() && !sourceFile->exists())
993 continue;
994
995 ModuleInfo *m = new ModuleInfo(this, path, sourceFile, true, isSubcomposition);
996 if (fileForModuleKey.find(m->getModuleKey()) != fileForModuleKey.end())
997 VUserLog("Warning: Conflicting source files for module %s are installed at %s", m->getModuleKey().c_str(), path.c_str());
998 fileForModuleKey[m->getModuleKey()] = m;
999
1000 if (isSubcomposition)
1001 {
1002 DependencyGraphVertex *compositionVertex = DependencyGraphVertex::vertexForDependency(moduleKey, compositionDependencyGraph);
1003 compositionDependencyGraph->addVertex(compositionVertex);
1004
1005 compositionVertex->setEnvironment(this);
1006
1007 set<string> dependencies = m->getContainedNodeClasses();
1008 for (set<string>::iterator j = dependencies.begin(); j != dependencies.end(); ++j)
1009 {
1010 DependencyGraphVertex *dependencyVertex = DependencyGraphVertex::vertexForDependency(*j, compositionDependencyGraph);
1011 compositionDependencyGraph->addEdge(compositionVertex, dependencyVertex);
1012 }
1013 }
1014 }
1015
1016 sourceFilesAtSearchPath[path] = fileForModuleKey;
1017}
1018
1024VuoCompiler::ModuleInfo * VuoCompiler::Environment::listModule(const string &moduleKey)
1025{
1026 for (const auto &moduleFiles : moduleFilesAtSearchPath)
1027 {
1028 map<string, ModuleInfo *>::const_iterator foundIter = moduleFiles.second.find(moduleKey);
1029 if (foundIter != moduleFiles.second.end())
1030 return foundIter->second;
1031 }
1032
1033 return NULL;
1034}
1035
1041VuoCompiler::ModuleInfoIterator VuoCompiler::Environment::listModules(const set<string> &moduleKeys)
1042{
1043 for (const string &path : moduleSearchPaths)
1044 updateModulesAtSearchPath(path);
1045
1046 return ModuleInfoIterator(&moduleFilesAtSearchPath, getOverriddenCompiledModuleCachePath(), moduleKeys);
1047}
1048
1054VuoCompiler::ModuleInfoIterator VuoCompiler::Environment::listAllModules(void)
1055{
1056 for (const string &path : moduleSearchPaths)
1057 updateModulesAtSearchPath(path);
1058
1059 return ModuleInfoIterator(&moduleFilesAtSearchPath, getOverriddenCompiledModuleCachePath());
1060}
1061
1067VuoCompiler::ModuleInfo * VuoCompiler::Environment::listSourceFile(const string &moduleKey)
1068{
1069 for (const auto &sourceFiles : sourceFilesAtSearchPath)
1070 {
1071 map<string, ModuleInfo *>::const_iterator foundIter = sourceFiles.second.find(moduleKey);
1072 if (foundIter != sourceFiles.second.end())
1073 return foundIter->second;
1074 }
1075
1076 return NULL;
1077}
1078
1084VuoCompiler::ModuleInfoIterator VuoCompiler::Environment::listSourceFiles(const set<string> &moduleKeys)
1085{
1086 for (const string &path : moduleSearchPaths)
1087 updateSourceFilesAtSearchPath(path);
1088
1089 return ModuleInfoIterator(&sourceFilesAtSearchPath, "", moduleKeys);
1090}
1091
1097VuoCompiler::ModuleInfoIterator VuoCompiler::Environment::listAllSourceFiles(void)
1098{
1099 for (const string &path : moduleSearchPaths)
1100 updateSourceFilesAtSearchPath(path);
1101
1102 return ModuleInfoIterator(&sourceFilesAtSearchPath, "");
1103}
1104
1108vector<string> VuoCompiler::Environment::getBuiltInModuleSearchPaths(void)
1109{
1110 vector<string> builtInModuleSearchPaths;
1111
1112 string vuoFrameworkPath = getVuoFrameworkPath();
1113 if (! vuoFrameworkPath.empty())
1114 {
1115 builtInModuleSearchPaths.push_back(vuoFrameworkPath + "/Modules");
1116 }
1117 else
1118 {
1119 builtInModuleSearchPaths.push_back(VUO_BUILD_DIR "/library");
1120 builtInModuleSearchPaths.push_back(VUO_BUILD_DIR "/node");
1121 builtInModuleSearchPaths.push_back(VUO_BUILD_DIR "/type");
1122 builtInModuleSearchPaths.push_back(VUO_BUILD_DIR "/type/list");
1123 }
1124
1125 return builtInModuleSearchPaths;
1126}
1127
1131vector<string> VuoCompiler::Environment::getBuiltInHeaderSearchPaths(void)
1132{
1133 vector<string> builtInHeaderSearchPaths;
1134
1135 string vuoFrameworkPath = getVuoFrameworkPath();
1136 if (! vuoFrameworkPath.empty())
1137 {
1138 builtInHeaderSearchPaths.push_back(vuoFrameworkPath + "/Frameworks/llvm.framework/Versions/A/Headers/lib/c++/v1");
1139 builtInHeaderSearchPaths.push_back(vuoFrameworkPath + "/Headers");
1140 builtInHeaderSearchPaths.push_back(vuoFrameworkPath + "/Headers/macos"); // system headers installed by Xcode Command Line Tools
1141 builtInHeaderSearchPaths.push_back(vuoFrameworkPath + "/Headers/macos/pthread"); //
1142 }
1143 else
1144 {
1145 builtInHeaderSearchPaths.push_back(LLVM_ROOT "/include/c++/v1");
1146 builtInHeaderSearchPaths.push_back(VUO_SOURCE_DIR "/library");
1147 builtInHeaderSearchPaths.push_back(VUO_SOURCE_DIR "/node");
1148 builtInHeaderSearchPaths.push_back(VUO_SOURCE_DIR "/type");
1149 builtInHeaderSearchPaths.push_back(VUO_SOURCE_DIR "/type/list");
1150 builtInHeaderSearchPaths.push_back(VUO_SOURCE_DIR "/runtime");
1151 builtInHeaderSearchPaths.push_back(JSONC_ROOT "/include");
1152 }
1153
1154 return builtInHeaderSearchPaths;
1155}
1156
1160vector<string> VuoCompiler::Environment::getBuiltInLibrarySearchPaths(void)
1161{
1162 vector<string> builtInLibrarySearchPaths;
1163
1164 string vuoFrameworkPath = getVuoFrameworkPath();
1165 if (! vuoFrameworkPath.empty())
1166 {
1167 builtInLibrarySearchPaths.push_back(vuoFrameworkPath + "/Modules");
1168
1169 // Ensure we (statically) link to our OpenSSL build when generating Vuo.framework's built-in module cache.
1170 builtInLibrarySearchPaths.push_back(OPENSSL_ROOT "/lib");
1171 }
1172 else
1173 {
1174 builtInLibrarySearchPaths.push_back(VUO_BUILD_DIR "/library");
1175 builtInLibrarySearchPaths.push_back(VUO_BUILD_DIR "/node");
1176 builtInLibrarySearchPaths.push_back(VUO_BUILD_DIR "/type");
1177 builtInLibrarySearchPaths.push_back(VUO_BUILD_DIR "/type/list");
1178 builtInLibrarySearchPaths.push_back(VUO_BUILD_DIR "/runtime");
1179
1180 vector<string> conanLibDirs = VuoStringUtilities::split(CONAN_LIBRARY_PATHS, ';');
1181 builtInLibrarySearchPaths.insert(builtInLibrarySearchPaths.end(), conanLibDirs.begin(), conanLibDirs.end());
1182 }
1183
1184 return builtInLibrarySearchPaths;
1185}
1186
1190vector<string> VuoCompiler::Environment::getBuiltInFrameworkSearchPaths(void)
1191{
1192 vector<string> builtInFrameworkSearchPaths;
1193
1194 string vuoFrameworkPath = getVuoFrameworkPath();
1195 if (! vuoFrameworkPath.empty())
1196 {
1197 builtInFrameworkSearchPaths.push_back(vuoFrameworkPath + "/Modules/");
1198 builtInFrameworkSearchPaths.push_back(vuoFrameworkPath + "/Frameworks/");
1199 }
1200
1201 return builtInFrameworkSearchPaths;
1202}
1203
1210void VuoCompiler::Environment::startWatchingModuleSearchPath(const string &moduleSearchPath)
1211{
1212 VuoFileWatcher *watcher = new VuoFileWatcher(this, moduleSearchPath);
1213 moduleSearchPathWatchers.insert(watcher);
1214}
1215
1221void VuoCompiler::Environment::stopWatchingModuleSearchPaths(void)
1222{
1223 for (set<VuoFileWatcher *>::iterator i = moduleSearchPathWatchers.begin(); i != moduleSearchPathWatchers.end(); ++i)
1224 delete *i;
1225
1226 moduleSearchPathWatchers.clear();
1227}
1228
1233void VuoCompiler::Environment::fileChanged(const string &moduleSearchPath)
1234{
1235 dispatch_sync(moduleSearchPathContentsChangedQueue, ^{
1236 moduleSearchPathContentsChanged(moduleSearchPath);
1237 });
1238}
1239
1247void VuoCompiler::Environment::moduleSearchPathContentsChanged(const string &moduleSearchPath)
1248{
1249 //VLog(" E=%p -- %s", this, moduleSearchPath.c_str());
1250 VuoCompiler *compilerForLoading = new VuoCompiler(moduleSearchPath + "/unused");
1251
1252 __block set<string> modulesAdded;
1253 __block set<string> modulesModifed;
1254 __block set<string> modulesRemoved;
1255 __block set<string> compositionsAdded;
1256 __block set<string> compositionsModifed;
1257 __block set<string> compositionsRemoved;
1258
1259 dispatch_sync(environmentQueue, ^{
1260
1261 // Remove the old file records from the environment.
1262
1263 map<string, ModuleInfo *> oldModules;
1264 map<string, ModuleInfo *> oldCompositions;
1265
1266 map<string, map<string, ModuleInfo *> >::iterator mf = moduleFilesAtSearchPath.find(moduleSearchPath);
1267 if (mf != moduleFilesAtSearchPath.end())
1268 {
1269 for (map<string, ModuleInfo *>::iterator j = mf->second.begin(); j != mf->second.end(); ++j)
1270 {
1271 oldModules[j->first] = j->second;
1272 }
1273
1274 moduleFilesAtSearchPath.erase(mf);
1275 }
1276
1277 map<string, map<string, ModuleInfo *> >::iterator cf = sourceFilesAtSearchPath.find(moduleSearchPath);
1278 if (cf != sourceFilesAtSearchPath.end())
1279 {
1280 for (map<string, ModuleInfo *>::iterator j = cf->second.begin(); j != cf->second.end(); ++j)
1281 {
1282 oldCompositions[j->first] = j->second;
1283
1284 VuoDirectedAcyclicGraph::Vertex *vertex = compositionDependencyGraph->findVertex(j->first);
1285 compositionDependencyGraph->removeVertex(vertex);
1286 }
1287
1288 sourceFilesAtSearchPath.erase(cf);
1289 }
1290
1291 // Rebuild the file records based on the directory contents.
1292
1293 updateModulesAtSearchPath(moduleSearchPath);
1294 updateSourceFilesAtSearchPath(moduleSearchPath);
1295
1296 // Remove any overrides for source files that have been modified or removed.
1297
1298 auto omf = moduleFilesAtSearchPath.find(getOverriddenCompiledModuleCachePath());
1299 if (omf != moduleFilesAtSearchPath.end())
1300 {
1301 for (auto oldComposition : oldCompositions)
1302 {
1303 for (auto i = omf->second.begin(); i != omf->second.end(); ++i)
1304 {
1305 if (i->first == oldComposition.first)
1306 {
1307 VuoFileUtilities::deleteFile(i->second->getFile()->path());
1308 omf->second.erase(i);
1309 break;
1310 }
1311 }
1312 }
1313 }
1314
1315 // Compare the old and new file records to see what has changed.
1316
1317 mf = moduleFilesAtSearchPath.find(moduleSearchPath);
1318 if (mf != moduleFilesAtSearchPath.end())
1319 {
1320 for (map<string, ModuleInfo *>::iterator n = mf->second.begin(); n != mf->second.end(); ++n)
1321 {
1322 string moduleKey = n->first;
1323
1324 map<string, ModuleInfo *>::iterator o = oldModules.find(moduleKey);
1325 if (o != oldModules.end())
1326 {
1327 if (n->second->isNewerThan(o->second))
1328 {
1329 modulesModifed.insert(moduleKey);
1330 }
1331 else
1332 {
1333 n->second->setAttempted(o->second->getAttempted());
1334 }
1335
1336 delete o->second;
1337 oldModules.erase(o);
1338 }
1339 else
1340 {
1341 modulesAdded.insert(moduleKey);
1342 }
1343 }
1344 }
1345
1346 cf = sourceFilesAtSearchPath.find(moduleSearchPath);
1347 if (cf != sourceFilesAtSearchPath.end())
1348 {
1349 for (map<string, ModuleInfo *>::iterator n = cf->second.begin(); n != cf->second.end(); ++n)
1350 {
1351 string moduleKey = n->first;
1352
1353 map<string, ModuleInfo *>::iterator o = oldCompositions.find(moduleKey);
1354 if (o != oldCompositions.end())
1355 {
1356 if (n->second->isNewerThan(o->second))
1357 {
1358 compositionsModifed.insert(moduleKey);
1359 }
1360 else
1361 {
1362 n->second->setAttempted(o->second->getAttempted());
1363 n->second->setSourceCode(o->second->getSourceCode());
1364 }
1365
1366 delete o->second;
1367 oldCompositions.erase(o);
1368 }
1369 else
1370 {
1371 compositionsAdded.insert(moduleKey);
1372 }
1373 }
1374 }
1375
1376 for (map<string, ModuleInfo *>::iterator o = oldModules.begin(); o != oldModules.end(); ++o)
1377 {
1378 delete o->second;
1379 modulesRemoved.insert(o->first);
1380 }
1381
1382 for (map<string, ModuleInfo *>::iterator o = oldCompositions.begin(); o != oldCompositions.end(); ++o)
1383 {
1384 delete o->second;
1385 compositionsRemoved.insert(o->first);
1386 }
1387
1388 compilerForLoading->loadModulesAndSources(modulesAdded, modulesModifed, modulesRemoved,
1389 compositionsAdded, compositionsModifed, compositionsRemoved,
1390 false, false, this, nullptr, nullptr, "");
1391 });
1392
1393 delete compilerForLoading;
1394}
1395
1402void VuoCompiler::Environment::moduleFileChanged(const string &modulePath, const string &moduleSourceCode,
1403 std::function<void(void)> moduleLoadedCallback,
1404 VuoCompiler *compiler, VuoCompilerIssues *issues)
1405{
1406 //VLog(" E=%p -- %s", this, modulePath.c_str());
1407 dispatch_sync(environmentQueue, ^{
1408
1409 string moduleDir, moduleKey, ext;
1410 VuoFileUtilities::splitPath(modulePath, moduleDir, moduleKey, ext);
1412
1413 // Remove the old file record from the environment.
1414
1415 bool foundOldModule = false;
1416 auto moduleSearchPathIter = moduleFilesAtSearchPath.find(moduleDir);
1417 if (moduleSearchPathIter != moduleFilesAtSearchPath.end())
1418 {
1419 auto moduleIter = moduleSearchPathIter->second.find(moduleKey);
1420 if (moduleIter != moduleSearchPathIter->second.end())
1421 {
1422 delete moduleIter->second;
1423 moduleSearchPathIter->second.erase(moduleIter);
1424 foundOldModule = true;
1425 }
1426 }
1427
1428 // Update the file record for the module by re-checking the file.
1429
1430 updateModuleAtSearchPath(moduleDir, moduleKey + "." + ext);
1431
1432 // Compare the old and new file records to see how the module has changed.
1433
1434 bool foundNewModule = false;
1435 moduleSearchPathIter = moduleFilesAtSearchPath.find(moduleDir);
1436 if (moduleSearchPathIter != moduleFilesAtSearchPath.end())
1437 {
1438 auto moduleIter = moduleSearchPathIter->second.find(moduleKey);
1439 if (moduleIter != moduleSearchPathIter->second.end())
1440 {
1441 foundNewModule = true;
1442 }
1443 }
1444
1445 set<string> modulesAdded;
1446 set<string> modulesModified;
1447 set<string> modulesRemoved;
1448
1449 if ((foundOldModule || VuoFileUtilities::arePathsEqual(moduleDir, getOverriddenCompiledModuleCachePath())) && foundNewModule)
1450 {
1451 modulesModified.insert(moduleKey);
1452 }
1453 else if (! foundOldModule && foundNewModule)
1454 {
1455 modulesAdded.insert(moduleKey);
1456 }
1457 else if (foundOldModule && ! foundNewModule)
1458 {
1459 modulesRemoved.insert(moduleKey);
1460 }
1461
1462 compiler->loadModulesAndSources(modulesAdded, modulesModified, modulesRemoved,
1463 set<string>(), set<string>(), set<string>(),
1464 false, false, this, issues, moduleLoadedCallback, moduleSourceCode);
1465 });
1466
1467}
1468
1472void VuoCompiler::Environment::notifyCompilers(const set<VuoCompilerModule *> &modulesAdded,
1473 const set< pair<VuoCompilerModule *, VuoCompilerModule *> > &modulesModified,
1474 const set<VuoCompilerModule *> &modulesRemoved, VuoCompilerIssues *issues,
1475 bool oldModulesInvalidated)
1476{
1477 if (! (modulesAdded.empty() && modulesModified.empty() && modulesRemoved.empty() && issues->isEmpty()) )
1478 {
1479 set< pair<VuoCompilerModule *, VuoCompilerModule *> > invalidatedModulesModified;
1480 set<VuoCompilerModule *> invalidatedModulesRemoved;
1481 if (oldModulesInvalidated)
1482 {
1483 invalidatedModulesModified = modulesModified;
1484 invalidatedModulesRemoved = modulesRemoved;
1485 }
1486
1487 VuoCompilerDelegate::LoadedModulesData *delegateData = new VuoCompilerDelegate::LoadedModulesData(invalidatedModulesModified, invalidatedModulesRemoved, issues);
1488
1489 map<string, VuoCompilerModule *> modulesAddedMap;
1490 map<string, pair<VuoCompilerModule *, VuoCompilerModule *> > modulesModifiedMap;
1491 map<string, VuoCompilerModule *> modulesRemovedMap;
1492 for (VuoCompilerModule *m : modulesAdded)
1493 modulesAddedMap[m->getPseudoBase()->getModuleKey()] = m;
1494 for (pair<VuoCompilerModule *, VuoCompilerModule *> m : modulesModified)
1495 modulesModifiedMap[m.first->getPseudoBase()->getModuleKey()] = m;
1496 for (VuoCompilerModule *m : modulesRemoved)
1497 modulesRemovedMap[m->getPseudoBase()->getModuleKey()] = m;
1498
1499 dispatch_sync(compilersToNotifyQueue, ^{
1500 for (set<VuoCompiler *>::iterator i = compilersToNotify.begin(); i != compilersToNotify.end(); ++i) {
1501 delegateData->retain();
1502 }
1503 for (set<VuoCompiler *>::iterator i = compilersToNotify.begin(); i != compilersToNotify.end(); ++i) {
1504 (*i)->loadedModules(modulesAddedMap, modulesModifiedMap, modulesRemovedMap, issues, delegateData, this);
1505 }
1506 });
1507 }
1508 else
1509 {
1510 delete issues;
1511 }
1512}
1513
1524set<VuoCompilerModule *> VuoCompiler::Environment::loadCompiledModules(const set<string> &moduleKeys, const map<string, string> &sourceCodeForModule)
1525{
1526 ModuleInfoIterator modulesToLoadIter = listModules(moduleKeys);
1527 set<VuoCompilerModule *> modulesLoaded;
1528
1529 ModuleInfo *moduleInfo;
1530 while ((moduleInfo = modulesToLoadIter.next()))
1531 {
1532 string moduleKey = moduleInfo->getModuleKey();
1533
1534 // Skip the module if its file is not in this environment, if the module has already been loaded,
1535 // or if the compiler previously tried to load the module and it failed.
1536 if (moduleInfo->getEnvironment() != this || findModule(moduleKey) || moduleInfo->getAttempted())
1537 continue;
1538
1539 moduleInfo->setAttempted(true);
1540
1541 // Actually load the module.
1542 VuoCompilerModule *module = loadModule(moduleInfo);
1543
1544 if (module)
1545 {
1546 modulesLoaded.insert(module);
1547 addToDependencyGraph(module);
1548 modulesChanged();
1549
1550 // For a compiled subcomposition or other source file, store its source code in the VuoCompilerNodeClass.
1551 string searchPath = moduleInfo->getSearchPath();
1552 if (VuoFileUtilities::arePathsEqual(searchPath, getCompiledModuleCachePath()) ||
1553 VuoFileUtilities::arePathsEqual(searchPath, getOverriddenCompiledModuleCachePath()))
1554 {
1555 VuoCompilerNodeClass *nodeClass = dynamic_cast<VuoCompilerNodeClass *>(module);
1556 if (nodeClass)
1557 {
1558 ModuleInfo *sourceInfo = listSourceFile(moduleKey);
1559 if (sourceInfo)
1560 nodeClass->setSourcePath( sourceInfo->getFile()->path() );
1561
1562 auto sourceCodeIter = sourceCodeForModule.find(moduleKey);
1563 if (sourceCodeIter != sourceCodeForModule.end())
1564 nodeClass->setSourceCode( sourceCodeIter->second );
1565 }
1566 }
1567 }
1568 }
1569
1570 return modulesLoaded;
1571}
1572
1584set<dispatch_group_t> VuoCompiler::Environment::loadSpecializedModules(const set<string> &moduleKeys,
1585 VuoCompiler *compiler, dispatch_queue_t llvmQueue)
1586{
1587 set<dispatch_group_t > loadingGroups;
1588
1589 for (string moduleKey : moduleKeys)
1590 {
1591 // Skip if it's not a node class.
1592
1593 if (moduleKey.find(".") == string::npos ||
1594 VuoStringUtilities::endsWith(moduleKey, ".framework") ||
1595 VuoStringUtilities::endsWith(moduleKey, ".dylib"))
1596 continue;
1597
1598 // Skip the module if it's already been loaded.
1599
1600 if (findModule(moduleKey))
1601 continue;
1602
1603 // Skip the module if it's in the process of being loaded, but have the caller wait for it to finish loading.
1604
1605 map<string, dispatch_group_t>::iterator iter = specializedModulesLoading.find(moduleKey);
1606 if (iter != specializedModulesLoading.end())
1607 {
1608 loadingGroups.insert(iter->second);
1609 dispatch_retain(iter->second);
1610 continue;
1611 }
1612
1613 dispatch_group_t loadingGroup = dispatch_group_create();
1614 specializedModulesLoading[moduleKey] = loadingGroup;
1615 loadingGroups.insert(loadingGroup);
1616
1617 // Generate the module.
1618 // This is done asynchronously since VuoCompilerSpecializedNodeClass::newNodeClass() needs to use environmentQueue
1619 // to compile the generated C code.
1620
1621 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
1622 dispatch_group_async(loadingGroup, queue, ^{
1623 VuoNodeClass *baseNodeClass = nullptr;
1624 try
1625 {
1626 baseNodeClass = VuoCompilerSpecializedNodeClass::newNodeClass(moduleKey, compiler, llvmQueue);
1627 }
1628 catch (VuoCompilerException &e)
1629 {
1630 VUserLog("Error: %s", e.what());
1631 }
1632
1633 if (baseNodeClass)
1634 {
1635 VuoCompilerSpecializedNodeClass *specNodeClass = static_cast<VuoCompilerSpecializedNodeClass *>(baseNodeClass->getCompiler());
1636 compiler->loadNodeClassGeneratedAtRuntime(specNodeClass, this);
1637
1638 set<string> dependencies = specNodeClass->getDependencies();
1639 if (!dependencies.empty())
1640 compiler->loadModulesIfNeeded(dependencies);
1641 }
1642
1643 dispatch_sync(environmentQueue, ^{
1644 specializedModulesLoading.erase(moduleKey);
1645 });
1646 });
1647 }
1648
1649 return loadingGroups;
1650}
1651
1664set<dispatch_group_t> VuoCompiler::Environment::compileModulesFromSourceCode(const set<string> &moduleKeys, bool shouldRecompileIfUnchanged)
1665{
1666 ModuleInfoIterator modulesToLoadIter = listSourceFiles(moduleKeys);
1667
1668 int environmentIndex = sharedEnvironments[target].size();
1669 for (int i = 0; i < sharedEnvironments[target].size(); ++i)
1670 if (this == sharedEnvironments[target].at(i).at(0))
1671 environmentIndex = i;
1672
1673 set<dispatch_group_t> sourcesLoading;
1674 int sourcesEnqueued = 0;
1675 ModuleInfo *sourceInfo;
1676 while ((sourceInfo = modulesToLoadIter.next()))
1677 {
1678 string moduleKey = sourceInfo->getModuleKey();
1679
1680 dispatch_group_t loadingGroup = sourceInfo->getLoadingGroup();
1681 sourcesLoading.insert(loadingGroup);
1682
1683 // Skip compiling if the source file has already been scheduled for compilation.
1684 // Either its compilation is in progress or it failed to compile.
1685
1686 if (sourceInfo->getAttempted())
1687 continue;
1688
1689 // Skip compiling if the compiled module is up-to-date.
1690
1691 string sourceCode = sourceInfo->getSourceCode();
1692
1693 if (! shouldRecompileIfUnchanged)
1694 {
1695 VuoCompilerNodeClass *existingNodeClass = getNodeClass(moduleKey);
1696 if (existingNodeClass && existingNodeClass->getSourceCode() == sourceCode)
1697 continue;
1698 }
1699
1700 // Enqueue the source file to be compiled.
1701
1702 sourceInfo->setAttempted(true);
1703
1704 dispatch_group_enter(loadingGroup);
1705 dispatch_group_enter(moduleSourceCompilersExistGlobally);
1706
1708 queueItem->moduleKey = moduleKey;
1709 queueItem->sourcePath = sourceInfo->getFile()->path();
1710 queueItem->sourceCode = sourceCode;
1711 queueItem->sourceFile = sourceInfo->getFile();
1712 queueItem->cachedModulesPath = sourceInfo->isSourceCodeOverridden() ? getOverriddenCompiledModuleCachePath() : getCompiledModuleCachePath();
1713 queueItem->compiledModulePath = queueItem->cachedModulesPath + "/" + moduleKey + ".vuonode";
1714 queueItem->loadingGroup = loadingGroup;
1715 queueItem->priority = { environmentIndex, sourceInfo->getLongestDownstreamPath() };
1716 moduleCompilationQueue->enqueue(queueItem);
1717 ++sourcesEnqueued;
1718 }
1719
1720 // Compile each enqueued source file. This is done asynchronously for several reasons:
1721 // - To avoid environmentQueue calling compiler code calling environmentQueue.
1722 // - To compile dependencies that were enqueued later while a compilation that was enqueued earlier waits.
1723 // - To be more efficient when compiling multiple source files.
1724
1725 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
1726 dispatch_async(queue, ^{
1727 for (int i = 0; i < sourcesEnqueued; ++i)
1728 {
1729 __block VuoModuleCompilationQueue::Item *queueItem = moduleCompilationQueue->next();
1730 VUserLog("Compiling %s", queueItem->moduleKey.c_str());
1731
1732 dispatch_async(queue, ^{
1733 try
1734 {
1735 VuoFileUtilities::makeDir(queueItem->cachedModulesPath);
1736 }
1737 catch (VuoException &e)
1738 {
1739 VuoCompilerIssue issue(VuoCompilerIssue::Error, "compiling subcomposition", "", "Couldn't create cached modules folder", e.what());
1740 VuoCompilerIssues *issues = new VuoCompilerIssues(issue);
1741 notifyCompilers(set<VuoCompilerModule *>(), set< pair<VuoCompilerModule *, VuoCompilerModule *> >(), set<VuoCompilerModule *>(), issues, false);
1742 }
1743
1744 VuoCompiler *compiler = new VuoCompiler(queueItem->sourcePath, target);
1745 compiler->setLoadAllModules(false);
1746
1747 VuoModuleCompilationQueue::Item *queueItemForCallback = queueItem;
1748 auto moduleLoadedCallback = [=](){
1749 dispatch_group_leave(queueItemForCallback->loadingGroup);
1750 moduleCompilationQueue->completed(queueItemForCallback);
1751 };
1752
1753 string ext = queueItem->sourceFile->extension();
1755 {
1756 VuoModuleCompiler *moduleCompiler = NULL;
1757 try
1758 {
1759 moduleCompiler = VuoModuleCompiler::newModuleCompiler("isf", queueItem->moduleKey, queueItem->sourceFile);
1760 }
1761 catch (VuoException &e)
1762 {
1763 VuoCompilerIssues *issues = new VuoCompilerIssues(VuoCompilerIssue(VuoCompilerIssue::Error, "compiling ISF module", queueItem->sourcePath, "", e.what()));
1764 notifyCompilers(set<VuoCompilerModule *>(), set< pair<VuoCompilerModule *, VuoCompilerModule *> >(), set<VuoCompilerModule *>(), issues, false);
1765 }
1766
1767 if (moduleCompiler)
1768 {
1769 moduleCompiler->overrideSourceCode(queueItem->sourceCode, queueItem->sourceFile);
1770
1771 auto getType = [&compiler] (const string &moduleKey) { return compiler->getType(moduleKey); };
1773 Module *module = moduleCompiler->compile(getType, llvmQueue, issues);
1774
1775 if (module)
1776 {
1777 dispatch_sync(llvmQueue, ^{
1778 setTargetForModule(module, target);
1779 writeModuleToBitcode(module, queueItem->compiledModulePath);
1780 });
1781 }
1782 else
1783 {
1784 issues->setFilePathIfEmpty(queueItem->sourcePath);
1785 }
1786
1787 moduleFileChanged(queueItem->compiledModulePath, queueItem->sourceCode, moduleLoadedCallback, compiler, issues);
1788 }
1789 else
1790 moduleLoadedCallback();
1791 }
1792 else if (VuoFileUtilities::isCSourceExtension(ext) && ! queueItem->cachedModulesPath.empty())
1793 {
1795
1796 try
1797 {
1798 compiler->compileModule(queueItem->sourcePath, queueItem->compiledModulePath, vector<string>());
1799 }
1800 catch (VuoCompilerException &e)
1801 {
1802 if (issues != e.getIssues())
1803 issues->append(e.getIssues());
1804
1805 issues->setFilePathIfEmpty(queueItem->sourcePath);
1806 }
1807
1808 moduleFileChanged(queueItem->compiledModulePath, queueItem->sourceCode, moduleLoadedCallback, compiler, issues);
1809 }
1810 else
1811 {
1813
1814 try
1815 {
1816 VuoCompilerComposition *composition = VuoCompilerComposition::newCompositionFromGraphvizDeclaration(queueItem->sourceCode, compiler);
1817
1818 Module *module = compiler->compileCompositionToModule(composition, queueItem->moduleKey, false, issues);
1819 if (! module)
1820 throw VuoCompilerException(issues, false);
1821
1822 dispatch_sync(llvmQueue, ^{
1823 setTargetForModule(module, target);
1824 writeModuleToBitcode(module, queueItem->compiledModulePath);
1825 });
1826
1827 VuoComposition *baseComposition = composition->getBase();
1828 delete composition;
1829 delete baseComposition;
1830 }
1831 catch (VuoCompilerException &e)
1832 {
1833 if (issues != e.getIssues())
1834 issues->append(e.getIssues());
1835
1836 issues->setFilePathIfEmpty(queueItem->sourcePath);
1837 }
1838
1839 moduleFileChanged(queueItem->compiledModulePath, queueItem->sourceCode, moduleLoadedCallback, compiler, issues);
1840 }
1841
1842 delete compiler;
1843 dispatch_group_leave(moduleSourceCompilersExistGlobally);
1844 });
1845 }
1846 });
1847
1848 return sourcesLoading;
1849}
1850
1859set<VuoCompilerModule *> VuoCompiler::Environment::unloadCompiledModules(const set<string> &moduleKeys)
1860{
1861 set<VuoCompilerModule *> modulesUnloaded;
1862
1863 for (set<string>::const_iterator i = moduleKeys.begin(); i != moduleKeys.end(); ++i)
1864 {
1865 string moduleKey = *i;
1866 VuoCompilerModule *module = NULL;
1867
1868 map<string, VuoCompilerNodeClass *>::iterator nodeClassIter = nodeClasses.find(moduleKey);
1869 if (nodeClassIter != nodeClasses.end())
1870 {
1871 module = nodeClassIter->second;
1872 nodeClasses.erase(nodeClassIter);
1873 }
1874 else
1875 {
1876 map<string, VuoCompilerType *>::iterator typeIter = types.find(moduleKey);
1877 if (typeIter != types.end())
1878 {
1879 module = typeIter->second;
1880 types.erase(typeIter);
1881 }
1882 else
1883 {
1884 map<string, VuoCompilerModule *>::iterator libraryModuleIter = libraryModules.find(moduleKey);
1885 if (libraryModuleIter != libraryModules.end())
1886 {
1887 module = libraryModuleIter->second;
1888 libraryModules.erase(libraryModuleIter);
1889 }
1890 }
1891 }
1892
1893 if (module)
1894 {
1895 modulesUnloaded.insert(module);
1896 removeFromDependencyGraph(module);
1897 modulesChanged();
1898 }
1899 }
1900
1901 return modulesUnloaded;
1902}
1903
1909void VuoCompiler::Environment::deleteModulesCompiledFromSourceCode(const set<string> &moduleKeys)
1910{
1911 for (set<string>::const_iterator i = moduleKeys.begin(); i != moduleKeys.end(); ++i)
1912 {
1913 string moduleKey = *i;
1914
1915 map<string, ModuleInfo *>::iterator iter = moduleFilesAtSearchPath[getCompiledModuleCachePath()].find(moduleKey);
1916 if (iter != moduleFilesAtSearchPath[getCompiledModuleCachePath()].end())
1917 VuoFileUtilities::deleteFile(iter->second->getFile()->path());
1918 }
1919}
1920
1928VuoCompilerModule * VuoCompiler::Environment::loadModule(ModuleInfo *moduleInfo)
1929{
1930 string moduleKey = moduleInfo->getModuleKey();
1931
1932 // Skip certain LLVM modules that definitely aren't Vuo modules to avoid adding struct types defined in them to the LLVM
1933 // context, resulting in mismatched struct types in code generation (e.g. %struct.NodeContext and %struct.NodeContext.1).
1934 if (VuoStringUtilities::beginsWith(moduleKey, "libVuo"))
1935 return NULL;
1936
1937 // Don't try to load single-target parts
1938 // (such as those found in `build/test/TestControlAndTelemetry`),
1939 // since they just fail and pollute the logs.
1940 if (VuoStringUtilities::endsWith(moduleKey, "-x86_64")
1941 || VuoStringUtilities::endsWith(moduleKey, "-arm64"))
1942 return nullptr;
1943
1944 __block size_t inputDataBytes;
1945 __block char *rawInputData;
1946 dispatch_sync(llvmQueue, ^{
1947 try
1948 {
1949 rawInputData = moduleInfo->getFile()->getContentsAsRawData(inputDataBytes);
1950 }
1951 catch (VuoException &e)
1952 {
1953 rawInputData = NULL;
1954 VUserLog("Warning: Couldn't load module '%s'. Its file may have been deleted. (%s)", moduleKey.c_str(), e.what());
1955 }
1956 });
1957 if (! rawInputData)
1958 return NULL;
1959
1960 char *processedInputData;
1961#if VUO_PRO
1962 processedInputData = loadModule_Pro0(moduleInfo, moduleKey, inputDataBytes, rawInputData);
1963#else
1964 processedInputData = rawInputData;
1965#endif
1966
1967 Module *module = NULL;
1968 set<string> moduleArchs;
1969 bool moduleParseError = !processedInputData;
1970 if (!moduleParseError)
1971 {
1972 string moduleReadError;
1973 string arch = VuoCompiler::getTargetArch(target);
1974 VuoLog_status("Loading module \"%s\" (%s)", moduleKey.c_str(), arch.c_str());
1975 module = readModuleFromBitcodeData(processedInputData, inputDataBytes, arch, moduleArchs, moduleReadError);
1976 VuoLog_status(NULL);
1977 free(processedInputData);
1978
1979 if (!module)
1980 {
1981 moduleParseError = true;
1982
1983 if (VuoFileUtilities::arePathsEqual(moduleInfo->getSearchPath(), getCompiledModuleCachePath()))
1984 VuoFileUtilities::deleteFile(moduleInfo->getFile()->path());
1985 else
1986 VUserLog("Error: Couldn't load module '%s' into %s environment: %s.", moduleKey.c_str(), arch.c_str(), moduleReadError.c_str());
1987 }
1988 }
1989
1990 if (!moduleParseError)
1991 {
1992 string modulePath;
1993 if (! moduleInfo->getFile()->isInArchive())
1994 modulePath = moduleInfo->getFile()->path();
1995
1996 VuoCompilerCompatibility moduleCompatibility = (VuoFileUtilities::arePathsEqual(moduleInfo->getSearchPath(), getCompiledModuleCachePath()) ?
1998 VuoCompilerCompatibility::compatibilityWithArchitectures(moduleArchs));
1999
2000 __block VuoCompilerModule *compilerModule;
2001 dispatch_sync(llvmQueue, ^{
2002 compilerModule = VuoCompilerModule::newModule(moduleKey, module, modulePath, moduleCompatibility);
2003 });
2004
2005 if (compilerModule)
2006 {
2007 if (dynamic_cast<VuoCompilerNodeClass *>(compilerModule))
2008 nodeClasses[moduleKey] = static_cast<VuoCompilerNodeClass *>(compilerModule);
2009 else if (dynamic_cast<VuoCompilerType *>(compilerModule))
2010 types[moduleKey] = static_cast<VuoCompilerType *>(compilerModule);
2011 else
2012 libraryModules[moduleKey] = compilerModule;
2013
2014 VuoNodeSet *nodeSet = VuoNodeSet::createNodeSetForModule(moduleInfo->getFile());
2015 if (nodeSet)
2016 {
2017 map<string, VuoNodeSet *>::iterator nodeSetIter = nodeSetForName.find(nodeSet->getName());
2018 if (nodeSetIter == nodeSetForName.end())
2019 {
2020 nodeSetForName[nodeSet->getName()] = nodeSet;
2021 }
2022 else
2023 {
2024 delete nodeSet;
2025 nodeSet = nodeSetIter->second;
2026 }
2027 compilerModule->getPseudoBase()->setNodeSet(nodeSet);
2028 }
2029
2030#if VUO_PRO
2031 loadModule_Pro1(rawInputData, processedInputData, compilerModule);
2032#endif
2033
2034 compilerModule->setBuiltIn( isBuiltInOriginal() );
2035
2036 return compilerModule;
2037 }
2038 else
2039 {
2040 destroyLlvmModule(module);
2041 }
2042 }
2043
2044 return NULL;
2045}
2046
2052void VuoCompiler::Environment::replaceNodeClass(VuoCompilerNodeClass *newNodeClass)
2053{
2054 string moduleKey = newNodeClass->getBase()->getModuleKey();
2055
2056 VuoCompilerNodeClass *oldNodeClass = nodeClasses[moduleKey];
2057 if (oldNodeClass)
2058 {
2059 removeFromDependencyGraph(oldNodeClass);
2060 destroyModule(oldNodeClass);
2061 }
2062
2063 nodeClasses[moduleKey] = newNodeClass;
2064 addToDependencyGraph(newNodeClass);
2065 modulesChanged();
2066}
2067
2068void VuoCompiler::Environment::addToDependencyGraph(VuoCompilerModule *module)
2069{
2070 DependencyGraphVertex *vertex = DependencyGraphVertex::vertexForDependency(module->getPseudoBase()->getModuleKey(), dependencyGraph);
2071 dependencyGraph->addVertex(vertex);
2072
2073 vertex->setEnvironment(this);
2074
2075 set<string> dependencies = module->getDependencies();
2076 for (set<string>::iterator i = dependencies.begin(); i != dependencies.end(); ++i)
2077 {
2078 DependencyGraphVertex *depVertex = DependencyGraphVertex::vertexForDependency(*i, dependencyGraph);
2079 dependencyGraph->addEdge(vertex, depVertex);
2080 }
2081}
2082
2083void VuoCompiler::Environment::removeFromDependencyGraph(VuoCompilerModule *module)
2084{
2085 DependencyGraphVertex *vertex = DependencyGraphVertex::vertexForDependency(module->getPseudoBase()->getModuleKey(), dependencyGraph);
2086 dependencyGraph->removeVertex(vertex);
2087}
2088
2099void VuoCompiler::Environment::reifyPortTypes(const map<string, VuoCompilerType *> &inheritedTypes)
2100{
2101 map<string, VuoCompilerType *> availableTypes = inheritedTypes;
2102 availableTypes.insert(types.begin(), types.end());
2103
2104 for (map<string, VuoCompilerNodeClass *>::const_iterator i = nodeClasses.begin(); i != nodeClasses.end(); ++i)
2105 {
2106 VuoNodeClass *nodeClass = i->second->getBase();
2107
2108 vector<VuoPortClass *> inputPortClasses = nodeClass->getInputPortClasses();
2109 vector<VuoPortClass *> outputPortClasses = nodeClass->getOutputPortClasses();
2110 vector<VuoPortClass *> portClasses;
2111 portClasses.insert(portClasses.end(), inputPortClasses.begin(), inputPortClasses.end());
2112 portClasses.insert(portClasses.end(), outputPortClasses.begin(), outputPortClasses.end());
2113
2114 for (vector<VuoPortClass *>::iterator j = portClasses.begin(); j != portClasses.end(); ++j)
2115 {
2116 VuoCompilerPortClass *portClass = static_cast<VuoCompilerPortClass *>((*j)->getCompiler());
2117 VuoType *baseType = portClass->getDataVuoType();
2118
2119 if (baseType && ! baseType->hasCompiler())
2120 {
2121 string typeName = baseType->getModuleKey();
2122 VuoCompilerType *reifiedType = NULL;
2123
2124 VuoGenericType *genericType = dynamic_cast<VuoGenericType *>(baseType);
2125 if (genericType)
2126 {
2127 reifiedType = VuoCompilerGenericType::newGenericType(genericType, availableTypes);
2128 if (reifiedType) {
2129 genericTypes.insert( static_cast<VuoCompilerGenericType *>(reifiedType) );
2130 }
2131 }
2132 else
2133 {
2134 map<string, VuoCompilerType *>::iterator reifiedTypeIter = availableTypes.find(typeName);
2135 if (reifiedTypeIter != availableTypes.end())
2136 {
2137 delete baseType;
2138 reifiedType = reifiedTypeIter->second;
2139 }
2140 }
2141
2142 if (reifiedType) {
2143 portClass->setDataVuoType(reifiedType->getBase());
2144 }
2145 }
2146 }
2147
2148 vector<VuoCompilerTriggerDescription *> triggers = nodeClass->getCompiler()->getTriggerDescriptions();
2149 for (vector<VuoCompilerTriggerDescription *>::iterator j = triggers.begin(); j != triggers.end(); ++j)
2150 {
2151 VuoCompilerTriggerDescription *trigger = *j;
2152 VuoType *baseType = trigger->getDataType();
2153
2154 if (baseType && ! baseType->hasCompiler())
2155 {
2156 string typeName = baseType->getModuleKey();
2157 map<string, VuoCompilerType *>::iterator reifiedTypeIter = availableTypes.find(typeName);
2158 if (reifiedTypeIter != availableTypes.end())
2159 {
2160 delete baseType;
2161 VuoCompilerType *reifiedType = reifiedTypeIter->second;
2162 trigger->setDataType(reifiedType->getBase());
2163 }
2164 }
2165 }
2166 }
2167}
2168
2179void VuoCompiler::Environment::getCacheableModulesAndDependencies(set<string> &cacheableModulesAndDependencies,
2180 set<string> &dylibsNeededToLinkToThisCache,
2181 set<string> &frameworksNeededToLinkToThisCache)
2182{
2183 if (isModuleCacheInitialized && ! isModuleCacheableDataDirty)
2184 {
2185 cacheableModulesAndDependencies = moduleCacheContents;
2186 dylibsNeededToLinkToThisCache = moduleCacheDylibs;
2187 frameworksNeededToLinkToThisCache = moduleCacheFrameworks;
2188 return;
2189 }
2190
2192
2193 // Include all modules…
2194 map<string, VuoCompilerModule *> allModules;
2195 allModules.insert(nodeClasses.begin(), nodeClasses.end());
2196 allModules.insert(types.begin(), types.end());
2197 allModules.insert(libraryModules.begin(), libraryModules.end());
2198
2199 set<string> dependencies;
2200 for (map<string, VuoCompilerModule *>::iterator i = allModules.begin(); i != allModules.end(); ++i)
2201 {
2202 string moduleKey = i->first;
2203 VuoCompilerModule *module = i->second;
2204
2205#if VUO_PRO
2206 // … except Pro modules and modules that depend on them…
2207 if (module->requiresPro())
2208 continue;
2209#endif
2210
2211 // … and incompatible modules.
2212 if (! module->getCompatibleTargets().isCompatibleWith(compositionTargets))
2213 continue;
2214
2215 cacheableModulesAndDependencies.insert(moduleKey);
2216
2217 set<string> moduleDependencies = module->getDependencies();
2218 dependencies.insert(moduleDependencies.begin(), moduleDependencies.end());
2219 }
2220
2221 // For the built-in environment, include Vuo's core dependencies.
2222 if (builtIn && ! generated)
2223 {
2224 vector<string> coreDependencies = getCoreVuoDependencies();
2225 dependencies.insert(coreDependencies.begin(), coreDependencies.end());
2226 }
2227
2228 // Include all dependencies of the included module that are located in this environment.
2229 // (All modules are already included, so we only need to handle non-module dependencies.)
2230 for (set<string>::iterator i = dependencies.begin(); i != dependencies.end(); ++i)
2231 {
2232 string dependency = *i;
2233 if (allModules.find(dependency) == allModules.end())
2234 {
2235 if (VuoStringUtilities::endsWith(dependency, ".framework"))
2236 frameworksNeededToLinkToThisCache.insert(dependency);
2237 else
2238 {
2239 string dependencyPath = VuoCompiler::getLibraryPath(dependency, librarySearchPaths);
2240 if (! dependencyPath.empty())
2241 {
2242 if (VuoStringUtilities::endsWith(dependencyPath, ".dylib"))
2243 dylibsNeededToLinkToThisCache.insert(dependencyPath);
2244 else
2245 cacheableModulesAndDependencies.insert(dependency);
2246 }
2247 }
2248 }
2249 }
2250
2251 moduleCacheDylibs = dylibsNeededToLinkToThisCache;
2252 moduleCacheFrameworks = frameworksNeededToLinkToThisCache;
2253
2254 if (builtIn)
2255 {
2256 currentModuleCacheDylib = VuoFileUtilities::buildModuleCacheDylibPath(moduleCachePath, builtIn, generated);
2257
2258 // Give each built-in cache arch a unique name, so they can be built in parallel.
2259 if (!vuoFrameworkInProgressPath.empty())
2260 currentModuleCacheDylib += "-" + VuoCompiler::getTargetArch(target);
2261 }
2262}
2263
2284void VuoCompiler::Environment::useModuleCache(bool shouldUseExistingCache, VuoCompiler *compiler,
2285 set<string> cacheableModulesAndDependencies,
2286 set<string> dylibsNeededToLinkToCaches, set<string> frameworksNeededToLinkToCaches,
2287 unsigned long lastPrerequisiteModuleCacheRebuild)
2288{
2289 // Ignore the cache if the `prelinkCache` preference is false.
2290
2291 static dispatch_once_t checked = 0;
2292 static bool prelinkCache = true;
2293 dispatch_once(&checked, ^{
2294 Boolean valid;
2295 bool result = CFPreferencesGetAppBooleanValue(CFSTR("prelinkCache"), CFSTR("org.vuo.Editor"), &valid);
2296 if (valid)
2297 prelinkCache = result;
2298 });
2299 if (! prelinkCache)
2300 {
2301 VDebugLog("Ignoring the module cache since the 'prelinkCache' preference is false.");
2302 return;
2303 }
2304
2305 // Don't do anything if this environment doesn't have a cache configured.
2306
2307 if (moduleCachePath.empty())
2308 return;
2309
2310 // Don't bother rechecking the cache if neither the modules in this environment nor the caches that it depends on have changed.
2311
2312 string cacheDescription = VuoFileUtilities::buildModuleCacheDescription(moduleCachePath, generated);
2313 if (isModuleCacheInitialized && ! isModuleCacheableDataDirty &&
2314 (builtIn || lastModuleCacheRebuild >= lastPrerequisiteModuleCacheRebuild))
2315 {
2316 VDebugLog("No need to recheck %s.", cacheDescription.c_str());
2317 return;
2318 }
2319
2320 try
2321 {
2322 VDebugLog("Checking if %s is up-to-date…", cacheDescription.c_str());
2323
2324 bool isCacheUpToDate = true;
2325
2326 if (isModuleCacheInitialized && isModuleCacheableDataDirty)
2327 isCacheUpToDate = false;
2328
2329 isModuleCacheInitialized = true;
2330 isModuleCacheableDataDirty = false;
2331
2332 string indexPath = VuoFileUtilities::buildModuleCacheIndexPath(moduleCachePath, builtIn, generated);
2333 if (!vuoFrameworkInProgressPath.empty())
2334 indexPath += "-" + VuoCompiler::getTargetArch(target);
2335 string dir, file, ext;
2336 VuoFileUtilities::splitPath(indexPath, dir, file, ext);
2337 string indexFileName = file + "." + ext;
2338
2339 // Create the cache directory and index if they don't already exist. (If they do exist, don't affect the last-modified times.)
2340
2341 bool dirExists = VuoFileUtilities::fileExists(moduleCachePath);
2342 if (! dirExists)
2343 {
2344 if (shouldUseExistingCache)
2345 throw VuoException("Trying to use the existing cache, but the cache directory doesn't exist.", false);
2346
2347 VuoFileUtilities::makeDir(moduleCachePath);
2348 isCacheUpToDate = false;
2349 }
2350
2351 bool indexFileExists = VuoFileUtilities::fileExists(indexPath);
2352 if (! indexFileExists)
2353 {
2354 if (shouldUseExistingCache)
2355 throw VuoException("Trying to use the existing cache, but the cache index doesn't exist.", false);
2356
2358 isCacheUpToDate = false;
2359 }
2360
2361 // Lock the cache for reading.
2362
2363 VuoFileUtilities::File *fileForLocking;
2364 {
2365 fileForLocking = moduleCacheFileForLocking[indexPath];
2366 if (! fileForLocking)
2367 {
2368 fileForLocking = new VuoFileUtilities::File(moduleCachePath, indexFileName);
2369 moduleCacheFileForLocking[indexPath] = fileForLocking;
2370 }
2371
2372 if (!fileForLocking->lockForReading())
2373 VDebugLog("\tWarning: Couldn't lock for reading.");
2374 }
2375
2376 // If this is the first time this Environment is using its cache, see if there's a dylib on disk.
2377
2378 if (currentModuleCacheDylib.empty())
2379 currentModuleCacheDylib = VuoFileUtilities::findLatestRevisionOfModuleCacheDylib(moduleCachePath, builtIn, generated, lastModuleCacheRebuild);
2380
2381 if (shouldUseExistingCache && currentModuleCacheDylib.empty())
2382 throw VuoException("Trying to use the existing cache, but the cache dylib doesn't exist.", false);
2383
2384 // Check if the dylib is newer than the other caches that it depends on.
2385
2386 if (isCacheUpToDate)
2387 isCacheUpToDate = lastModuleCacheRebuild >= lastPrerequisiteModuleCacheRebuild;
2388
2389 // Check if the dylib looks remotely valid.
2390
2391 if (isCacheUpToDate)
2392 {
2393 bool dylibHasData = VuoFileUtilities::fileContainsReadableData(currentModuleCacheDylib);
2394 if (! dylibHasData)
2395 {
2396 if (shouldUseExistingCache)
2397 throw VuoException("Trying to use the existing cache, but the cache doesn't contain readable data.", false);
2398 else
2399 isCacheUpToDate = false;
2400 }
2401 }
2402
2403 // List the items actually in the cache, according to its index.
2404
2405 const char separator = '\n';
2406 if (isCacheUpToDate || shouldUseExistingCache)
2407 {
2408 VuoFileUtilities::File indexFile(moduleCachePath, indexFileName);
2409 string index = indexFile.getContentsAsString();
2410 vector<string> actualIndex = VuoStringUtilities::split(index, separator);
2411
2412 moduleCacheContents.clear();
2413 moduleCacheContents.insert(actualIndex.begin(), actualIndex.end());
2414
2415 if (shouldUseExistingCache)
2416 {
2417 isModuleCacheAvailable = true;
2418 return;
2419 }
2420 }
2421
2422 // Check if the list of actual items matches the list of expected items.
2423
2424 if (isCacheUpToDate)
2425 {
2426 if (moduleCacheContents.size() != cacheableModulesAndDependencies.size())
2427 isCacheUpToDate = false;
2428 else
2429 {
2430 set<string> contentsInBoth;
2431 std::set_intersection(moduleCacheContents.begin(), moduleCacheContents.end(),
2432 cacheableModulesAndDependencies.begin(), cacheableModulesAndDependencies.end(),
2433 std::inserter(contentsInBoth, contentsInBoth.begin()));
2434
2435 if (contentsInBoth.size() != cacheableModulesAndDependencies.size())
2436 isCacheUpToDate = false;
2437 }
2438 }
2439
2440 // Check if the cache is newer than all of the modules in it.
2441
2442 if (isCacheUpToDate)
2443 {
2444 unsigned long cacheLastModified = VuoFileUtilities::getFileLastModifiedInSeconds(currentModuleCacheDylib);
2445
2446 for (map<string, map<string, ModuleInfo *> >::iterator i = moduleFilesAtSearchPath.begin(); i != moduleFilesAtSearchPath.end(); ++i)
2447 {
2448 for (map<string, ModuleInfo *>::iterator j = i->second.begin(); j != i->second.end(); ++j)
2449 {
2450 if (! j->second->isOlderThan(cacheLastModified))
2451 {
2452 isCacheUpToDate = false;
2453 break;
2454 }
2455 }
2456 }
2457 }
2458
2459 // If the cache is consistent with this environment, we're done.
2460
2461 if (isCacheUpToDate)
2462 {
2463 VDebugLog("Up-to-date.");
2464
2465 isModuleCacheAvailable = true;
2466 return;
2467 }
2468
2469 // Otherwise, (re)build the cache.
2470
2471 if (! builtIn)
2472 {
2473 currentModuleCacheDylib = VuoFileUtilities::buildModuleCacheDylibPath(moduleCachePath, builtIn, generated);
2474
2475 struct timeval t;
2476 gettimeofday(&t, NULL);
2477 lastModuleCacheRebuild = t.tv_sec;
2478 }
2479
2480 dispatch_async(moduleCacheBuildingQueue, ^{
2481 VUserLog("Rebuilding %s…", cacheDescription.c_str());
2482
2483 set<Module *> modulesToLink;
2484 set<string> librariesToLink;
2485 set<string> frameworksToLink;
2486 {
2487 compiler->getLinkerInputs(cacheableModulesAndDependencies, Optimization_SmallBinary, modulesToLink, librariesToLink, frameworksToLink);
2488
2489 librariesToLink.insert(dylibsNeededToLinkToCaches.begin(), dylibsNeededToLinkToCaches.end());
2490 frameworksToLink.insert(frameworksNeededToLinkToCaches.begin(), frameworksNeededToLinkToCaches.end());
2491 }
2492
2493 bool gotLockForWriting = false;
2494 try
2495 {
2496 // Try to upgrade the file lock for writing.
2497 gotLockForWriting = fileForLocking->lockForWriting(true);
2498 if (! gotLockForWriting)
2499 throw VuoException("The cache file is being used by another process. "
2500 "If any composition windows are open from previous Vuo sessions, quit them. "
2501 "If any processes whose names start with \"VuoComposition\" or one of your composition file names appear in Activity Monitor, force-quit them.",
2502 false);
2503
2504 // Link the dependencies to create the cached resources dylib in a temporary file.
2505 string dir, file, ext;
2506 VuoFileUtilities::splitPath(currentModuleCacheDylib, dir, file, ext);
2507 string tmpPath = VuoFileUtilities::makeTmpFile(file, "dylib");
2508 vector<string> rPaths = compiler->getRunPathSearchPaths(this);
2510 compiler->link(tmpPath, modulesToLink, librariesToLink, frameworksToLink, true, rPaths, false, issues);
2511
2512 if (! issues->isEmpty())
2513 VUserLog("Warning: May not have fully rebuilt %s for the \"faster build\" optimization:\n%s", cacheDescription.c_str(), issues->getLongDescription(false).c_str());
2514
2515 // Move the temporary file into the cache.
2516 VuoFileUtilities::moveFile(tmpPath, currentModuleCacheDylib);
2517
2518 // Change the dylib's ID from the temporary path to the path within the cache.
2520 getVuoFrameworkPath() + "/Helpers/install_name_tool",
2521 "-id",
2522 currentModuleCacheDylib,
2523 currentModuleCacheDylib,
2524 });
2525
2526 // Ad-hoc code-sign the runtime-generated System and User caches,
2527 // but don't ad-hoc code-sign the buildtime-generated Builtin module cache
2528 // since `framework/CMakeLists.txt` later changes its ID/rpath/loads.
2529 if (vuoFrameworkInProgressPath.empty())
2530 adHocCodeSign(currentModuleCacheDylib);
2531
2532 // Write the list of dependencies to the index file.
2533 vector<string> expectedContents(cacheableModulesAndDependencies.begin(), cacheableModulesAndDependencies.end());
2534 string index = VuoStringUtilities::join(expectedContents, separator);
2535 VuoFileUtilities::writeStringToFile(index, indexPath);
2536
2537 // Delete any older revisions of the dylib.
2539
2540 // Downgrade the file lock back to reading.
2541 if (!fileForLocking->lockForReading())
2542 VDebugLog("\tWarning: Couldn't downgrade the lock back to reading.");
2543
2544 dispatch_sync(environmentQueue, ^{
2545 moduleCacheContents = cacheableModulesAndDependencies;
2546 isModuleCacheAvailable = true;
2547 });
2548 }
2549 catch (VuoException &e)
2550 {
2551 // Downgrade the file lock back to reading.
2552 if (gotLockForWriting)
2553 if (!fileForLocking->lockForReading())
2554 VDebugLog("\tWarning: Couldn't downgrade the lock back to reading.");
2555
2556 VUserLog("Warning: Couldn't rebuild %s for the \"faster build\" optimization: %s", cacheDescription.c_str(), e.what());
2557 }
2558
2559 VUserLog("Done.");
2560 });
2561 }
2562 catch (VuoException &e)
2563 {
2564 if (vuoFrameworkInProgressPath.empty())
2565 VUserLog("Warning: Couldn't use %s for the \"faster build\" optimization: %s", cacheDescription.c_str(), e.what());
2566 }
2567}
2568
2572void VuoCompiler::Environment::waitForModuleCachesToBuild(void)
2573{
2574 dispatch_sync(moduleCacheBuildingQueue, ^{});
2575}
2576
2586bool VuoCompiler::Environment::findInModuleCache(const string &moduleOrDependency, string &cachePath)
2587{
2588 if (isModuleCacheAvailable && moduleCacheContents.find(moduleOrDependency) != moduleCacheContents.end())
2589 {
2590 cachePath = currentModuleCacheDylib;
2591 return true;
2592 }
2593
2594 return false;
2595}
2596
2600string VuoCompiler::Environment::getCurrentModuleCacheDylib(void)
2601{
2602 return currentModuleCacheDylib;
2603}
2604
2609unsigned long VuoCompiler::Environment::getLastModuleCacheRebuild(void)
2610{
2611 return lastModuleCacheRebuild;
2612}
2613
2620void VuoCompiler::Environment::modulesChanged(void)
2621{
2622 isModuleCacheableDataDirty = true;
2623 isModuleCacheAvailable = false;
2624}
2625
2629bool VuoCompiler::Environment::isBuiltInOriginal()
2630{
2631 return builtIn && ! generated;
2632}
2633
2637bool VuoCompiler::Environment::isBuiltIn()
2638{
2639 return builtIn;
2640}
2641
2645bool VuoCompiler::Environment::isGenerated()
2646{
2647 return generated;
2648}
2649
2653string VuoCompiler::Environment::getName()
2654{
2655 if (isBuiltInOriginal())
2656 return "builtin";
2657 else if (this == sharedEnvironments[target][0][1])
2658 return "builtin (generated)";
2659 else if (this == sharedEnvironments[target][1][0])
2660 return "system";
2661 else if (this == sharedEnvironments[target][1][1])
2662 return "system (generated)";
2663 else if (this == sharedEnvironments[target][2][0])
2664 return "user";
2665 else if (this == sharedEnvironments[target][2][1])
2666 return "user (generated)";
2667 return "composition-local";
2668}
2669
2673void VuoCompiler::applyToInstalledEnvironments(void (^doForEnvironment)(Environment *))
2674{
2675 dispatch_sync(environmentQueue, ^{
2676 for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i) {
2677 doForEnvironment((*i)[0]);
2678 }
2679 });
2680}
2681
2685void VuoCompiler::applyToInstalledEnvironments(void (^doForEnvironment)(Environment *, bool *, string), bool *result, string arg)
2686{
2687 dispatch_sync(environmentQueue, ^{
2688 for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i) {
2689 doForEnvironment((*i)[0], result, arg);
2690 }
2691 });
2692}
2693
2697void VuoCompiler::applyToAllEnvironments(void (^doForEnvironment)(Environment *environment))
2698{
2699 dispatch_sync(environmentQueue, ^{
2700 for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i) {
2701 for (vector<Environment *>::iterator j = i->begin(); j != i->end(); ++j) {
2702 doForEnvironment(*j);
2703 }
2704 }
2705 });
2706}
2707
2721VuoCompiler::VuoCompiler(const string &compositionPath, string target)
2722{
2723 if (target.empty())
2724 {
2725 this->target = target = getProcessTarget();
2726 VDebugLog("%p target=%s (from current process)", this, this->target.c_str());
2727 }
2728 else
2729 {
2730 this->target = target;
2731 VDebugLog("%p target=%s", this, this->target.c_str());
2732 }
2733
2734#if VUO_PRO
2735 init_Pro();
2736#endif
2737
2738 shouldLoadAllModules = true;
2739 hasLoadedAllModules = false;
2740 modulesToLoadQueue = dispatch_queue_create("org.vuo.compiler.modules", NULL);
2741 moduleSourceCompilersExist = dispatch_group_create();
2742 moduleCacheBuilding = dispatch_group_create();
2743 dependencyGraph = NULL;
2744 compositionDependencyGraph = NULL;
2745 isVerbose = false;
2746 _shouldShowSplashWindow = false;
2747 delegate = NULL;
2748 delegateQueue = dispatch_queue_create("org.vuo.compiler.delegate", NULL);
2749
2750 string vuoFrameworkPath = getVuoFrameworkPath();
2751 if (! vuoFrameworkPath.empty())
2752 clangPath = vuoFrameworkPath + "/Helpers/clang";
2753 else
2754 clangPath = LLVM_ROOT "/bin/clang";
2755
2756 dispatch_sync(environmentQueue, ^{
2757 allCompilers.insert(this);
2758
2759 if (sharedEnvironments[target].empty())
2760 {
2761 sharedEnvironments[target] = vector< vector<Environment *> >(3, vector<Environment *>(2, NULL));
2762 for (vector< vector<Environment *> >::iterator i = sharedEnvironments[target].begin(); i != sharedEnvironments[target].end(); ++i)
2763 for (vector<Environment *>::iterator j = i->begin(); j != i->end(); ++j)
2764 *j = new Environment(this->target, i == sharedEnvironments[target].begin(), j != i->begin());
2765
2766 vector<string> builtInModuleSearchPaths = Environment::getBuiltInModuleSearchPaths();
2767 for (vector<string>::iterator i = builtInModuleSearchPaths.begin(); i != builtInModuleSearchPaths.end(); ++i)
2768 sharedEnvironments[target][0][0]->addModuleSearchPath(*i, false);
2769
2770 vector<string> builtInHeaderSearchPaths = Environment::getBuiltInHeaderSearchPaths();
2771 for (vector<string>::iterator i = builtInHeaderSearchPaths.begin(); i != builtInHeaderSearchPaths.end(); ++i)
2772 sharedEnvironments[target][0][0]->addHeaderSearchPath(*i);
2773
2774 vector<string> builtInLibrarySearchPaths = Environment::getBuiltInLibrarySearchPaths();
2775 for (vector<string>::iterator i = builtInLibrarySearchPaths.begin(); i != builtInLibrarySearchPaths.end(); ++i)
2776 sharedEnvironments[target][0][0]->addLibrarySearchPath(*i);
2777
2778 vector<string> builtInFrameworkSearchPaths = Environment::getBuiltInFrameworkSearchPaths();
2779 for (vector<string>::iterator i = builtInFrameworkSearchPaths.begin(); i != builtInFrameworkSearchPaths.end(); ++i)
2780 sharedEnvironments[target][0][0]->addFrameworkSearchPath(*i);
2781
2782 // Allow system administrator to override Vuo.framework modules
2783 sharedEnvironments[target][1][0]->addModuleSearchPath(VuoFileUtilities::getSystemModulesPath());
2784 sharedEnvironments[target][1][0]->addLibrarySearchPath(VuoFileUtilities::getSystemModulesPath());
2785
2786 // Allow user to override Vuo.framework and system-wide modules
2787 sharedEnvironments[target][2][0]->addModuleSearchPath(VuoFileUtilities::getUserModulesPath());
2788 sharedEnvironments[target][2][0]->addLibrarySearchPath(VuoFileUtilities::getUserModulesPath());
2789
2790 // Set up module cache paths.
2791 // Since the built-in module caches are part of Vuo.framework (put there by `generateBuiltInModuleCaches`),
2792 // only attempt to use module caches if Vuo.framework exists.
2793 string vuoFrameworkPath = getVuoFrameworkPath();
2794 if (! vuoFrameworkPath.empty())
2795 {
2796 vector<string> moduleCachePaths(3);
2797 moduleCachePaths[0] = vuoFrameworkPath + "/Modules/Builtin";
2798 moduleCachePaths[1] = VuoFileUtilities::getCachePath() + "/System";
2799 moduleCachePaths[2] = VuoFileUtilities::getCachePath() + "/User";
2800
2801 for (size_t i = 0; i < moduleCachePaths.size(); ++i)
2802 {
2803 string moduleCachePath = moduleCachePaths[i];
2804
2805 sharedEnvironments[target][i][0]->setModuleCachePath(moduleCachePath, true);
2806 sharedEnvironments[target][i][1]->setModuleCachePath(moduleCachePath, false);
2807 }
2808 }
2809 }
2810 });
2811
2812 setCompositionPath(compositionPath);
2813}
2814
2819{
2820#if VUO_PRO
2821 fini_Pro();
2822#endif
2823
2824 dispatch_sync(environmentQueue, ^{
2825 allCompilers.erase(this);
2826 });
2827
2828 dispatch_group_wait(moduleCacheBuilding, DISPATCH_TIME_FOREVER);
2829 dispatch_release(moduleCacheBuilding);
2830
2831 for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
2832 (*i)[0]->removeCompilerToNotify(this);
2833
2834 dispatch_sync(delegateQueue, ^{});
2835
2836 dispatch_release(modulesToLoadQueue);
2837 dispatch_release(delegateQueue);
2838
2839 delete dependencyGraph;
2840 delete compositionDependencyGraph;
2841}
2842
2846void VuoCompiler::reset(void)
2847{
2848 dispatch_group_wait(moduleSourceCompilersExistGlobally, DISPATCH_TIME_FOREVER);
2849
2850 dispatch_sync(environmentQueue, ^{
2851
2852 for (auto e : sharedEnvironments)
2853 for (vector< vector<Environment *> >::iterator i = e.second.begin(); i != e.second.end(); ++i)
2854 {
2855 (*i)[0]->stopWatchingModuleSearchPaths();
2856 dispatch_sync((*i)[0]->moduleSearchPathContentsChangedQueue, ^{});
2857 }
2858
2859 for (auto e : environmentsForCompositionFamily)
2860 for (map< string, vector<Environment *> >::iterator i = e.second.begin(); i != e.second.end(); ++i)
2861 {
2862 (i->second)[0]->stopWatchingModuleSearchPaths();
2863 dispatch_sync((i->second)[0]->moduleSearchPathContentsChangedQueue, ^{});
2864 }
2865
2866 for (auto e : sharedEnvironments)
2867 for (vector< vector<Environment *> >::iterator i = e.second.begin(); i != e.second.end(); ++i)
2868 for (vector<Environment *>::iterator j = i->begin(); j != i->end(); ++j)
2869 delete *j;
2870
2871 for (auto e : environmentsForCompositionFamily)
2872 for (map< string, vector<Environment *> >::iterator i = e.second.begin(); i != e.second.end(); ++i)
2873 for (vector<Environment *>::iterator j = i->second.begin(); j != i->second.end(); ++j)
2874 delete *j;
2875
2876 allCompilers.clear();
2877 sharedEnvironments.clear();
2878 environmentsForCompositionFamily.clear();
2879
2880 });
2881}
2882
2889{
2890 dispatch_async(delegateQueue, ^{
2891 this->delegate = delegate;
2892 });
2893}
2894
2904void VuoCompiler::setCompositionPath(const string &compositionPath)
2905{
2906 string compositionModulesDir;
2907 string compositionBaseDir;
2908 lastCompositionIsSubcomposition = false;
2909 if (! compositionPath.empty())
2910 {
2911 compositionModulesDir = VuoFileUtilities::getCompositionLocalModulesPath(compositionPath);
2912
2913 string file, ext;
2914 VuoFileUtilities::splitPath(compositionModulesDir, compositionBaseDir, file, ext);
2915 VuoFileUtilities::canonicalizePath(compositionBaseDir);
2916
2917 string compositionDir;
2918 VuoFileUtilities::splitPath(compositionPath, compositionDir, file, ext);
2919 VuoFileUtilities::canonicalizePath(compositionDir);
2920 lastCompositionIsSubcomposition = (compositionDir == compositionModulesDir);
2921 }
2922
2923 // Set up `environments` to contain all environments available to this compiler, in order from broadest to narrowest.
2924
2925 dispatch_sync(environmentQueue, ^{
2926 if (! environments.empty() && compositionBaseDir == lastCompositionBaseDir) {
2927 return;
2928 }
2929 lastCompositionBaseDir = compositionBaseDir;
2930
2931 // Clear out the existing environments for this compiler.
2932
2933 Environment *oldCompositionFamilyInstalledEnvironment = nullptr;
2934 vector<Environment *> compositionEnvironments;
2935 if (! environments.empty())
2936 {
2937 for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i) {
2938 (*i)[0]->removeCompilerToNotify(this);
2939 }
2940
2941 if (environments.size() >= 5) {
2942 oldCompositionFamilyInstalledEnvironment = environments[3][0];
2943 }
2944
2945 compositionEnvironments = environments.back();
2946
2947 environments.clear();
2948 }
2949
2950 // If the composition is located in one of the shared environments (built-in, system, user),
2951 // add that shared environment and any shared environments at broader scope to the compiler.
2952 // If the composition is not in a shared environment, add all of the shared environments to the compiler.
2953
2954 bool isCompositionInSharedEnvironment = false;
2955 for (vector< vector<Environment *> >::iterator i = sharedEnvironments[target].begin(); i != sharedEnvironments[target].end(); ++i)
2956 {
2957 environments.push_back(*i);
2958
2959 vector<string> moduleSearchPaths = (*i)[0]->getModuleSearchPaths();
2960 for (vector<string>::iterator j = moduleSearchPaths.begin(); j != moduleSearchPaths.end(); ++j)
2961 {
2962 string moduleSearchPath = *j;
2963 VuoFileUtilities::canonicalizePath(moduleSearchPath);
2964 if (moduleSearchPath == compositionModulesDir)
2965 {
2966 isCompositionInSharedEnvironment = true;
2967 break;
2968 }
2969 }
2970
2971 if (isCompositionInSharedEnvironment) {
2972 break;
2973 }
2974 }
2975
2976 // If the composition is not in a shared environment, add the composition-family environment to the compiler.
2977
2978 Environment *newCompositionFamilyInstalledEnvironment = nullptr;
2979 if (! isCompositionInSharedEnvironment && ! compositionPath.empty())
2980 {
2981 vector<Environment *> compositionFamilyEnvironments = environmentsForCompositionFamily[target][compositionBaseDir];
2982 if (compositionFamilyEnvironments.empty())
2983 {
2984 compositionFamilyEnvironments = vector<Environment *>(2, NULL);
2985 compositionFamilyEnvironments[0] = new Environment(this->target, false, false);
2986 compositionFamilyEnvironments[1] = new Environment(this->target, false, true);
2987 environmentsForCompositionFamily[target][compositionBaseDir] = compositionFamilyEnvironments;
2988
2989 // Allow the user to place modules/subcompositions in a Modules folder inside the composition folder.
2990
2991 compositionFamilyEnvironments[0]->addModuleSearchPath(compositionModulesDir);
2992
2993 // Locate this environment's cache in a folder whose name is the composition folder path with
2994 // slashes replaced with Unicode Modifier Letter Colon.
2995 string moduleCachePath = getCachePathForComposition(compositionBaseDir);
2996
2997 compositionFamilyEnvironments[0]->setModuleCachePath(moduleCachePath, true);
2998 compositionFamilyEnvironments[1]->setModuleCachePath(moduleCachePath, false);
2999 }
3000 environments.push_back(compositionFamilyEnvironments);
3001
3002 newCompositionFamilyInstalledEnvironment = compositionFamilyEnvironments[0];
3003 }
3004
3005 // Add the composition environment to the compiler (or add it back in if it already existed).
3006
3007 if (compositionEnvironments.empty())
3008 {
3009 compositionEnvironments = vector<Environment *>(2, NULL);
3010 compositionEnvironments[0] = new Environment(this->target, false, false);
3011 compositionEnvironments[1] = new Environment(this->target, false, true);
3012 }
3013 environments.push_back(compositionEnvironments);
3014
3015 for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i) {
3016 (*i)[0]->addCompilerToNotify(this);
3017 }
3018
3019 delete dependencyGraph;
3020 delete compositionDependencyGraph;
3021 dependencyGraph = makeDependencyNetwork(environments, ^VuoDirectedAcyclicGraph * (Environment *env) { return env->getDependencyGraph(); });
3022 compositionDependencyGraph = makeDependencyNetwork(environments, ^VuoDirectedAcyclicGraph * (Environment *env) { return env->getCompositionDependencyGraph(); });
3023
3024 // If the compiler has a different local Modules directory than before, notify the compiler's delegate
3025 // of composition-family modules that are newly available/unavailable.
3026
3027 if (oldCompositionFamilyInstalledEnvironment != newCompositionFamilyInstalledEnvironment)
3028 {
3029 auto getModules = [] (Environment *env)
3030 {
3031 map<string, VuoCompilerModule *> modules;
3032 if (env)
3033 {
3034 for (auto i : env->getNodeClasses()) {
3035 modules.insert(i);
3036 }
3037 for (auto i : env->getTypes()) {
3038 modules.insert(i);
3039 }
3040 for (auto i : env->getLibraryModules()) {
3041 modules.insert(i);
3042 }
3043 }
3044 return modules;
3045 };
3046
3047 map<string, VuoCompilerModule *> modulesAdded = getModules(newCompositionFamilyInstalledEnvironment);
3048 map<string, VuoCompilerModule *> modulesRemoved = getModules(oldCompositionFamilyInstalledEnvironment);
3049
3050 map<string, pair<VuoCompilerModule *, VuoCompilerModule *> > modulesModified;
3051 for (map<string, VuoCompilerModule *>::iterator add = modulesAdded.begin(); add != modulesAdded.end(); )
3052 {
3053 map<string, VuoCompilerModule *>::iterator rem = modulesRemoved.find(add->first);
3054 if (rem != modulesRemoved.end())
3055 {
3056 modulesModified[add->first] = make_pair(rem->second, add->second);
3057 modulesAdded.erase(add++);
3058 modulesRemoved.erase(rem);
3059 }
3060 else
3061 {
3062 ++add;
3063 }
3064 }
3065
3066 if (! (modulesAdded.empty() && modulesModified.empty() && modulesRemoved.empty()) )
3067 {
3068 VuoCompilerIssues *issues = new VuoCompilerIssues();
3069 VuoCompilerDelegate::LoadedModulesData *delegateData = new VuoCompilerDelegate::LoadedModulesData(set< pair<VuoCompilerModule *, VuoCompilerModule *> >(), set<VuoCompilerModule *>(), issues);
3070 delegateData->retain();
3071
3072 Environment *scopeEnvironment = newCompositionFamilyInstalledEnvironment;
3073 if (! scopeEnvironment) {
3074 scopeEnvironment = compositionEnvironments.at(0);
3075 }
3076
3077 loadedModules(modulesAdded, modulesModified, modulesRemoved, issues, delegateData, scopeEnvironment);
3078 }
3079 }
3080 });
3081}
3082
3088VuoDirectedAcyclicNetwork * VuoCompiler::makeDependencyNetwork(const vector< vector<Environment *> > &environments,
3089 VuoDirectedAcyclicGraph * (^graphForEnvironment)(Environment *))
3090{
3091 if (!graphForEnvironment)
3092 return NULL;
3093
3095
3096 for (vector< vector<Environment *> >::const_iterator i = environments.begin(); i != environments.end(); ++i)
3097 {
3098 // Installed environment depends on generated environment in same scope.
3099
3100 network->addEdge(graphForEnvironment(i->at(0)), graphForEnvironment(i->at(1)));
3101
3102 // Installed and generated environments depend on installed environments in broader scopes.
3103
3104 for (vector< vector<Environment *> >::const_iterator ii = environments.begin(); ii != i; ++ii)
3105 {
3106 network->addEdge(graphForEnvironment(i->at(0)), graphForEnvironment(ii->at(0)));
3107 network->addEdge(graphForEnvironment(i->at(1)), graphForEnvironment(ii->at(0)));
3108 }
3109 }
3110
3111 return network;
3112}
3113
3134void VuoCompiler::loadModulesIfNeeded(const set<string> &moduleKeys)
3135{
3136 __block bool willLoadAllModules = false;
3137 if (moduleKeys.empty())
3138 {
3139 dispatch_sync(modulesToLoadQueue, ^{
3140 if (shouldLoadAllModules && ! hasLoadedAllModules) {
3141 willLoadAllModules = true;
3142 hasLoadedAllModules = true;
3143 }
3144 });
3145 }
3146
3147 if (! willLoadAllModules && moduleKeys.empty())
3148 return;
3149
3150 // Load modules and start sources compiling.
3151
3152 dispatch_group_enter(moduleSourceCompilersExist);
3153 __block set<dispatch_group_t> sourcesLoading;
3154 dispatch_sync(environmentQueue, ^{
3155 sourcesLoading = loadModulesAndSources(moduleKeys, set<string>(), set<string>(),
3156 moduleKeys, set<string>(), set<string>(),
3157 willLoadAllModules, false, nullptr, nullptr, nullptr, "");
3158 });
3159
3160 // Wait for subcompositions and specialized node classes to finish compiling and their modules to be loaded
3161 // to ensure that the next call to getNodeClass() will return them.
3162
3163 for (set<dispatch_group_t>::iterator i = sourcesLoading.begin(); i != sourcesLoading.end(); ++i)
3164 {
3165 dispatch_group_wait(*i, DISPATCH_TIME_FOREVER);
3166 dispatch_release(*i);
3167 }
3168 dispatch_group_leave(moduleSourceCompilersExist);
3169}
3170
3179set<dispatch_group_t> VuoCompiler::loadModulesAndSources(const set<string> &modulesAddedKeys, const set<string> &modulesModifiedKeys, const set<string> &modulesRemovedKeys,
3180 const set<string> &sourcesAddedKeys, const set<string> &sourcesModifiedKeys, const set<string> &sourcesRemovedKeys,
3181 bool willLoadAllModules, bool shouldRecompileSourcesIfUnchanged,
3182 Environment *currentEnvironment, VuoCompilerIssues *issuesForCurrentEnvironment,
3183 std::function<void(void)> moduleLoadedCallback, const string &moduleAddedOrModifiedSourceCode)
3184{
3185 //VLog("C=%p E=%p -- %lu %lu %lu %lu %lu %lu %i %i", this, currentEnvironment,
3186 //modulesAddedKeys.size(), modulesModifiedKeys.size(), modulesRemovedKeys.size(),
3187 //sourcesAddedKeys.size(), sourcesModifiedKeys.size(), sourcesRemovedKeys.size(),
3188 //willLoadAllModules, shouldRecompileSourcesIfUnchanged);
3189 //if (modulesAddedKeys.size() == 1) VLog(" %s", modulesAddedKeys.begin()->c_str());
3190 //if (modulesModifiedKeys.size() == 1) VLog(" %s", modulesModifiedKeys.begin()->c_str());
3191 //if (modulesRemovedKeys.size() == 1) VLog(" %s", modulesRemovedKeys.begin()->c_str());
3192
3193 // Organize the modules, source files, and issues by environment.
3194
3195 map<Environment *, set<string> > modulesAdded;
3196 map<Environment *, set<string> > modulesModified;
3197 map<Environment *, set<string> > modulesRemoved;
3198 map<Environment *, set<string> > sourcesAdded;
3199 map<Environment *, set<string> > sourcesModified;
3200 map<Environment *, set<string> > sourcesRemoved;
3201 map<Environment *, set<string> > potentialSpecializedModules;
3202
3203 if (currentEnvironment)
3204 {
3205 modulesAdded[currentEnvironment] = modulesAddedKeys;
3206 modulesModified[currentEnvironment] = modulesModifiedKeys;
3207 modulesRemoved[currentEnvironment] = modulesRemovedKeys;
3208 sourcesAdded[currentEnvironment] = sourcesAddedKeys;
3209 sourcesModified[currentEnvironment] = sourcesModifiedKeys;
3210 sourcesRemoved[currentEnvironment] = sourcesRemovedKeys;
3211 }
3212 else
3213 {
3214 Environment *genEnv = nullptr;
3215 if (! willLoadAllModules)
3216 {
3217 // If compiling a top-level composition, generated modules that were directly requested (in modulesAddedKeys) are
3218 // added at the composition scope so they won't be cached. This prevents link errors when running multiple
3219 // compositions in the current process (https://b33p.net/kosada/node/14317).
3220
3221 int scope = environments.size() - (lastCompositionIsSubcomposition ? 2 : 1);
3222 genEnv = environments.at(scope).at(1);
3223 potentialSpecializedModules[genEnv] = modulesAddedKeys;
3224 }
3225
3226 for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3227 {
3228 Environment *env = (*i).at(0);
3229
3230 ModuleInfoIterator modulesAddedIter = (willLoadAllModules ? env->listAllModules() : env->listModules(modulesAddedKeys));
3231 ModuleInfoIterator sourcesAddedIter = (willLoadAllModules ? env->listAllSourceFiles() : env->listSourceFiles(sourcesAddedKeys));
3232 ModuleInfoIterator sourcesModifiedIter = (willLoadAllModules ? env->listAllSourceFiles() : env->listSourceFiles(sourcesModifiedKeys));
3233
3234 ModuleInfo *moduleInfo;
3235 while ((moduleInfo = modulesAddedIter.next()))
3236 {
3237 string moduleKey = moduleInfo->getModuleKey();
3238
3239 modulesAdded[env].insert(moduleKey);
3240
3241 if (! willLoadAllModules)
3242 {
3243 auto foundIter = potentialSpecializedModules[genEnv].find(moduleKey);
3244 if (foundIter != potentialSpecializedModules[genEnv].end())
3245 potentialSpecializedModules[genEnv].erase(foundIter);
3246 }
3247 }
3248
3249 // If a source file and a compiled file for the same module are in the same folder,
3250 // the compiled file takes precedence.
3251 auto isCompiledModuleAtSameSearchPath = [&env] (ModuleInfo *sourceInfo)
3252 {
3253 ModuleInfo *compiledModuleInfo = env->listModule(sourceInfo->getModuleKey());
3254 return (compiledModuleInfo && compiledModuleInfo->getSearchPath() == sourceInfo->getSearchPath());
3255 };
3256
3257 while ((moduleInfo = sourcesAddedIter.next()))
3258 {
3259 if (isCompiledModuleAtSameSearchPath(moduleInfo))
3260 continue;
3261
3262 sourcesAdded[env].insert( moduleInfo->getModuleKey() );
3263 }
3264
3265 while ((moduleInfo = sourcesModifiedIter.next()))
3266 {
3267 if (isCompiledModuleAtSameSearchPath(moduleInfo))
3268 continue;
3269
3270 sourcesModified[env].insert( moduleInfo->getModuleKey() );
3271 }
3272 }
3273 }
3274
3275 map<Environment *, VuoCompilerIssues *> issues;
3276 for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3277 {
3278 Environment *env = (*i).at(0);
3279 issues[env] = (env == currentEnvironment && issuesForCurrentEnvironment ? issuesForCurrentEnvironment : new VuoCompilerIssues());
3280 }
3281
3282 // Check for circular dependencies in added/modified sources.
3283
3284 for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3285 {
3286 Environment *env = (*i).at(0);
3287
3288 // Check for circular dependencies involving sources being loaded within this environment.
3289 // For circular dependencies involving sources in different environments,
3290 // an error will be reported elsewhere due to one of them being outside of the other's scope.
3291 set<VuoDirectedAcyclicGraph::Vertex *> circularDependencies = env->getCompositionDependencyGraph()->getCycleVertices();
3292
3293 set<string> sourcesAddedModified;
3294 sourcesAddedModified.insert(sourcesAdded[env].begin(), sourcesAdded[env].end());
3295 sourcesAddedModified.insert(sourcesModified[env].begin(), sourcesModified[env].end());
3296
3297 for (set<string>::iterator j = sourcesAddedModified.begin(); j != sourcesAddedModified.end(); ++j)
3298 {
3299 string moduleKey = *j;
3300
3301 bool found = false;
3302 for (set<VuoDirectedAcyclicGraph::Vertex *>::iterator k = circularDependencies.begin(); k != circularDependencies.end(); ++k)
3303 {
3304 DependencyGraphVertex *vertex = static_cast<DependencyGraphVertex *>(*k);
3305 if (vertex->getDependency() == moduleKey)
3306 {
3307 found = true;
3308 break;
3309 }
3310 }
3311
3312 if (found)
3313 {
3314 sourcesAdded[env].erase(moduleKey);
3315 sourcesModified[env].erase(moduleKey);
3316
3317 VuoCompilerIssue issue(VuoCompilerIssue::Error, "compiling subcomposition", moduleKey,
3318 "Subcomposition contains itself",
3319 "%moduleKey contains an instance of itself, "
3320 "or contains another subcomposition that contains an instance of %moduleKey.");
3321 issue.setModuleKey(moduleKey);
3322
3323 ModuleInfo *sourceInfo = env->listSourceFile(moduleKey);
3324 if (sourceInfo)
3325 issue.setFilePath(sourceInfo->getFile()->path());
3326
3327 issues[env]->append(issue);
3328 }
3329 }
3330 }
3331
3332 // Update the longest downstream paths for added/modified sources.
3333
3334 for (const vector<Environment *> &envs : environments)
3335 {
3336 Environment *env = envs.at(0);
3337
3338 set<string> sourcesAddedModified;
3339 sourcesAddedModified.insert(sourcesAdded[env].begin(), sourcesAdded[env].end());
3340 sourcesAddedModified.insert(sourcesModified[env].begin(), sourcesModified[env].end());
3341
3342 for (const string &moduleKey : sourcesAddedModified)
3343 {
3344 VuoDirectedAcyclicGraph::Vertex *vertex = env->getCompositionDependencyGraph()->findVertex(moduleKey);
3345 int pathLength = env->getCompositionDependencyGraph()->getLongestDownstreamPath(vertex);
3346
3347 ModuleInfo *sourceInfo = env->listSourceFile(moduleKey);
3348 sourceInfo->setLongestDownstreamPath(pathLength);
3349 }
3350 }
3351
3352 // Find all modules and sources that depend on the modules and sources being modified or removed.
3353 // Those that belong to one of this compiler's environments are used in subsequent stages.
3354 // Those that belong to some other environment are scheduled to be handled by a separate call to this function.
3355
3356 map<Environment *, set<string> > modulesDepOnModulesModified;
3357 map<Environment *, set<string> > sourcesDepOnModulesModified;
3358 map<Environment *, set<string> > modulesDepOnModulesRemoved;
3359 map<Environment *, set<string> > sourcesDepOnModulesRemoved;
3360
3361 {
3362 __block map<Environment *, set<string> > modulesDepOnModulesModified_otherCompiler;
3363 __block map<Environment *, set<string> > sourcesDepOnModulesModified_otherCompiler;
3364 __block map<Environment *, set<string> > modulesDepOnModulesRemoved_otherCompiler;
3365 __block map<Environment *, set<string> > sourcesDepOnModulesRemoved_otherCompiler;
3366
3367 vector<VuoDirectedAcyclicNetwork *> searchDependencyGraphs;
3368 for (VuoCompiler *compiler : allCompilers)
3369 searchDependencyGraphs.push_back(compiler->dependencyGraph);
3370
3371 VuoDirectedAcyclicGraph *currentEnvironmentDependencyGraph = (currentEnvironment ? currentEnvironment->getDependencyGraph() : nullptr);
3372
3373 findDependentModulesAndSources(modulesModified, searchDependencyGraphs, currentEnvironmentDependencyGraph,
3374 modulesDepOnModulesModified, modulesDepOnModulesModified_otherCompiler,
3375 sourcesDepOnModulesModified, sourcesDepOnModulesModified_otherCompiler);
3376
3377 findDependentModulesAndSources(modulesRemoved, searchDependencyGraphs, currentEnvironmentDependencyGraph,
3378 modulesDepOnModulesRemoved, modulesDepOnModulesRemoved_otherCompiler,
3379 sourcesDepOnModulesRemoved, sourcesDepOnModulesRemoved_otherCompiler);
3380
3381 set<Environment *> otherEnvironments;
3382 for (map<Environment *, set<string> >::iterator i = modulesDepOnModulesModified_otherCompiler.begin(); i != modulesDepOnModulesModified_otherCompiler.end(); ++i)
3383 otherEnvironments.insert(i->first);
3384 for (map<Environment *, set<string> >::iterator i = sourcesDepOnModulesModified_otherCompiler.begin(); i != sourcesDepOnModulesModified_otherCompiler.end(); ++i)
3385 otherEnvironments.insert(i->first);
3386 for (map<Environment *, set<string> >::iterator i = modulesDepOnModulesRemoved_otherCompiler.begin(); i != modulesDepOnModulesRemoved_otherCompiler.end(); ++i)
3387 otherEnvironments.insert(i->first);
3388 for (map<Environment *, set<string> >::iterator i = sourcesDepOnModulesRemoved_otherCompiler.begin(); i != sourcesDepOnModulesRemoved_otherCompiler.end(); ++i)
3389 otherEnvironments.insert(i->first);
3390
3391 for (Environment *env : otherEnvironments)
3392 {
3393 VuoCompiler *otherCompiler = nullptr;
3394 for (VuoCompiler *c : allCompilers)
3395 for (const vector<Environment *> &ee : c->environments)
3396 for (Environment *e : ee)
3397 if (e == env)
3398 {
3399 otherCompiler = c;
3400 goto foundOtherCompiler;
3401 }
3402 foundOtherCompiler:
3403
3404 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
3405 dispatch_sync(environmentQueue, ^{
3406 otherCompiler->loadModulesAndSources(set<string>(), modulesDepOnModulesModified_otherCompiler[env], modulesDepOnModulesRemoved_otherCompiler[env],
3407 set<string>(), sourcesDepOnModulesModified_otherCompiler[env], sourcesDepOnModulesRemoved_otherCompiler[env],
3408 false, true, env, nullptr, nullptr, "");
3409 });
3410 });
3411 }
3412 }
3413
3414 // Unload:
3415 // - modules that have been removed or modified
3416 // - modules that depend on them
3417
3418 map<Environment *, set<VuoCompilerModule *> > actualModulesRemoved;
3419 for (const vector<Environment *> &envs : environments)
3420 {
3421 for (Environment *env : envs)
3422 {
3423 set<string> modulesToUnload;
3424 modulesToUnload.insert(modulesRemoved[env].begin(), modulesRemoved[env].end());
3425 modulesToUnload.insert(modulesModified[env].begin(), modulesModified[env].end());
3426 modulesToUnload.insert(modulesDepOnModulesRemoved[env].begin(), modulesDepOnModulesRemoved[env].end());
3427 modulesToUnload.insert(modulesDepOnModulesModified[env].begin(), modulesDepOnModulesModified[env].end());
3428
3429 actualModulesRemoved[env] = env->unloadCompiledModules(modulesToUnload);
3430
3431 if (!env->isBuiltInOriginal() && !actualModulesRemoved[env].empty())
3432 {
3433 set<string> actualModulesRemovedKeys;
3434 for (auto m : actualModulesRemoved[env])
3435 actualModulesRemovedKeys.insert(m->getPseudoBase()->getModuleKey());
3436 VUserLog("Removed from %s environment: %s", env->getName().c_str(), VuoStringUtilities::join(actualModulesRemovedKeys, ", ").c_str());
3437 }
3438 }
3439 }
3440
3441 // Load:
3442 // - modules that have been added or modified
3443 // - modules that they depend on
3444 // - modules that depend on them that were just unloaded
3445 // Delete:
3446 // - cached module files in `modulesToLoad` whose source files have been removed
3447
3448 map<Environment *, set<string> > modulesToLoad;
3449 map<Environment *, map<string, string> > modulesToLoadSourceCode;
3450 for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3451 {
3452 Environment *env = (*i).at(0);
3453
3454 if (! modulesAdded[env].empty())
3455 modulesToLoad[env].insert(modulesAdded[env].begin(), modulesAdded[env].end());
3456 if (! modulesModified[env].empty())
3457 modulesToLoad[env].insert(modulesModified[env].begin(), modulesModified[env].end());
3458 if (! modulesDepOnModulesModified[env].empty())
3459 modulesToLoad[env].insert(modulesDepOnModulesModified[env].begin(), modulesDepOnModulesModified[env].end());
3460
3461 if (env == currentEnvironment && moduleLoadedCallback)
3462 {
3463 if (modulesAdded[env].size() == 1)
3464 modulesToLoadSourceCode[env][*modulesAdded[env].begin()] = moduleAddedOrModifiedSourceCode;
3465 else if (modulesModified[env].size() == 1)
3466 modulesToLoadSourceCode[env][*modulesModified[env].begin()] = moduleAddedOrModifiedSourceCode;
3467 }
3468 }
3469
3470 map<Environment *, set<VuoCompilerModule *> > actualModulesAdded;
3471 while (! modulesToLoad.empty())
3472 {
3473 set<string> dependenciesToLoad;
3474 map<Environment *, set<string> > potentialSpecializedDependencies;
3475 for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3476 {
3477 Environment *env = (*i).at(0);
3478 Environment *genEnv = (*i).at(1);
3479
3480 set<VuoCompilerModule *> actualModulesLoaded = env->loadCompiledModules(modulesToLoad[env], modulesToLoadSourceCode[env]);
3481
3482 actualModulesAdded[env].insert(actualModulesLoaded.begin(), actualModulesLoaded.end());
3483 modulesToLoad.erase(env);
3484
3485 for (set<VuoCompilerModule *>::iterator j = actualModulesLoaded.begin(); j != actualModulesLoaded.end(); ++j)
3486 {
3487 set<string> dependencies = (*j)->getDependencies();
3488 dependenciesToLoad.insert(dependencies.begin(), dependencies.end());
3489 potentialSpecializedDependencies[genEnv].insert(dependencies.begin(), dependencies.end());
3490 }
3491
3492 if (!env->isBuiltInOriginal() && !actualModulesLoaded.empty())
3493 {
3494 map<string, string> actualFilesAndHashesLoaded;
3495 for (auto module : actualModulesLoaded)
3496 {
3497 string path, hash;
3498 if (module->getPseudoBase()->getNodeSet())
3499 path = module->getPseudoBase()->getNodeSet()->getArchivePath();
3500 else
3501 {
3502 auto cnc = dynamic_cast<VuoCompilerNodeClass *>(module);
3503 if (cnc && !cnc->getSourcePath().empty())
3504 {
3505 path = cnc->getSourcePath();
3506 if (!cnc->getSourceCode().empty())
3507 // Use the latest source code, if it's been modified without saving.
3508 hash = VuoStringUtilities::calculateSHA256(cnc->getSourceCode());
3509 }
3510 else
3511 path = module->getModulePath();
3512 }
3513
3514 if (hash.empty())
3515 try
3516 {
3518 }
3519 catch (VuoException &e) {}
3520
3521 actualFilesAndHashesLoaded[path] = hash;
3522 }
3523
3524 for (pair<string, string> item : actualFilesAndHashesLoaded)
3525 VUserLog("Loaded into %s environment: [%8.8s] %s", env->getName().c_str(), item.second.c_str(), item.first.c_str());
3526 }
3527 }
3528
3529 for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3530 {
3531 Environment *env = (*i).at(0);
3532
3533 ModuleInfoIterator dependenciesInEnv = env->listModules(dependenciesToLoad);
3534 ModuleInfo *moduleInfo;
3535 while ((moduleInfo = dependenciesInEnv.next()))
3536 {
3537 modulesToLoad[env].insert( moduleInfo->getModuleKey() );
3538
3539 for (map<Environment *, set<string> >::iterator j = potentialSpecializedDependencies.begin(); j != potentialSpecializedDependencies.end(); ++j)
3540 {
3541 auto foundIter = j->second.find( moduleInfo->getModuleKey() );
3542 if (foundIter != j->second.end())
3543 j->second.erase(foundIter);
3544 }
3545 }
3546 }
3547
3548 for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3549 {
3550 Environment *genEnv = (*i).at(1);
3551 potentialSpecializedModules[genEnv].insert(potentialSpecializedDependencies[genEnv].begin(), potentialSpecializedDependencies[genEnv].end());
3552 }
3553 }
3554
3555 // Load asynchronously:
3556 // - specializations of generic modules
3557
3558 set<dispatch_group_t> specializedModulesLoading;
3559 for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3560 {
3561 Environment *genEnv = (*i).at(1);
3562 set<dispatch_group_t> s = genEnv->loadSpecializedModules(potentialSpecializedModules[genEnv], this, llvmQueue);
3563 specializedModulesLoading.insert(s.begin(), s.end());
3564 }
3565
3566 // Notify those waiting on a source file to be compiled that its module has now been loaded.
3567
3568 if (moduleLoadedCallback)
3569 moduleLoadedCallback();
3570
3571 // Move modified modules from `actualModulesAdded` and `actualModulesRemoved` to `actualModulesModified`.
3572
3573 map<Environment *, set< pair<VuoCompilerModule *, VuoCompilerModule *> > > actualModulesModified;
3574 for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3575 {
3576 Environment *env = (*i).at(0);
3577
3578 for (set<VuoCompilerModule *>::iterator add = actualModulesAdded[env].begin(); add != actualModulesAdded[env].end(); )
3579 {
3580 set<VuoCompilerModule *>::iterator rem;
3581 for (rem = actualModulesRemoved[env].begin(); rem != actualModulesRemoved[env].end(); ++rem)
3582 if ((*rem)->getPseudoBase()->getModuleKey() == (*add)->getPseudoBase()->getModuleKey())
3583 break;
3584
3585 if (rem != actualModulesRemoved[env].end())
3586 {
3587 actualModulesModified[env].insert( make_pair(*rem, *add) );
3588 actualModulesRemoved[env].erase(rem);
3589 actualModulesAdded[env].erase(add++);
3590 }
3591 else
3592 ++add;
3593 }
3594 }
3595
3596 // Reify port types on node classes (if needed).
3597
3598 bool wereModulesAddedOrModified = false;
3599 for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3600 {
3601 Environment *env = (*i).at(0);
3602 if (! (actualModulesAdded[env].empty() && actualModulesModified[env].empty()) )
3603 {
3604 wereModulesAddedOrModified = true;
3605 break;
3606 }
3607 }
3608
3609 if (wereModulesAddedOrModified)
3610 {
3611 map<string, VuoCompilerType *> inheritedTypes;
3612 for (const vector<Environment *> &envs : environments)
3613 {
3614 for (Environment *env : envs)
3615 {
3616 env->reifyPortTypes(inheritedTypes);
3617 map<string, VuoCompilerType *> envTypes = env->getTypes();
3618 inheritedTypes.insert(envTypes.begin(), envTypes.end());
3619 }
3620 }
3621 }
3622
3623 // Delete cached compiled module files and call this function recursively for:
3624 // - modules whose source files have been removed
3625 // - modules whose source files depend on them
3626
3627 for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3628 {
3629 Environment *env = (*i).at(0);
3630
3631 set<string> sourcesToUnload;
3632 sourcesToUnload.insert(sourcesRemoved[env].begin(), sourcesRemoved[env].end());
3633 sourcesToUnload.insert(sourcesDepOnModulesRemoved[env].begin(), sourcesDepOnModulesRemoved[env].end());
3634 if (! sourcesToUnload.empty())
3635 {
3636 string moduleSearchPath = env->getModuleSearchPaths().front();
3637
3638 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
3639 VuoCompiler *otherCompiler = new VuoCompiler(moduleSearchPath + "/unused", target);
3640
3641 dispatch_sync(environmentQueue, ^{
3642 otherCompiler->loadModulesAndSources(set<string>(), set<string>(), sourcesToUnload,
3643 set<string>(), set<string>(), set<string>(),
3644 false, false, env, nullptr, nullptr, "");
3645 });
3646
3647 delete otherCompiler;
3648 });
3649 }
3650
3651 if (!env->isBuiltInOriginal() && !sourcesToUnload.empty())
3652 VUserLog("Deleting from %s environment: %s", env->getName().c_str(), VuoStringUtilities::join(sourcesToUnload, ", ").c_str());
3653
3654 env->deleteModulesCompiledFromSourceCode(sourcesToUnload);
3655 }
3656
3657 // Compile asynchronously:
3658 // - source files that have been added or modified
3659 // - source files that depend on them
3660 // - source files that depend on modules that have been modified
3661
3662 map<Environment *, set<string> > sourcesDepOnModulesAdded;
3663 {
3664 map<Environment *, set<string> > modulesDepOnModulesAdded; // unused
3665 __block map<Environment *, set<string> > modulesDepOnModulesAdded_otherCompiler; //
3666 __block map<Environment *, set<string> > sourcesDepOnModulesAdded_otherCompiler;
3667
3668 map<Environment *, set<string> > actualModuleKeysAdded;
3669 for (const vector<Environment *> &envs : environments)
3670 {
3671 Environment *env = envs.at(0);
3672 for (VuoCompilerModule *module : actualModulesAdded[env])
3673 actualModuleKeysAdded[env].insert( module->getPseudoBase()->getModuleKey() );
3674 }
3675
3676 vector<VuoDirectedAcyclicNetwork *> searchDependencyGraphs;
3677 searchDependencyGraphs.push_back(compositionDependencyGraph);
3678 for (map<string, vector<Environment *> >::iterator ii = environmentsForCompositionFamily[target].begin(); ii != environmentsForCompositionFamily[target].end(); ++ii)
3679 {
3680 vector< vector<Environment *> > otherEnvs = sharedEnvironments[target];
3681 otherEnvs.push_back(ii->second);
3682 VuoDirectedAcyclicNetwork *other = makeDependencyNetwork(otherEnvs, ^VuoDirectedAcyclicGraph * (Environment *env) { return env->getCompositionDependencyGraph(); });
3683 searchDependencyGraphs.push_back(other);
3684 }
3685
3686 VuoDirectedAcyclicGraph *currentEnvironmentDependencyGraph = (currentEnvironment ? currentEnvironment->getCompositionDependencyGraph() : nullptr);
3687
3688 findDependentModulesAndSources(actualModuleKeysAdded, searchDependencyGraphs, currentEnvironmentDependencyGraph,
3689 modulesDepOnModulesAdded, modulesDepOnModulesAdded_otherCompiler,
3690 sourcesDepOnModulesAdded, sourcesDepOnModulesAdded_otherCompiler);
3691
3692 set<Environment *> otherEnvironments;
3693 for (map<Environment *, set<string> >::iterator i = sourcesDepOnModulesAdded_otherCompiler.begin(); i != sourcesDepOnModulesAdded_otherCompiler.end(); ++i)
3694 otherEnvironments.insert(i->first);
3695
3696 for (Environment *env : otherEnvironments)
3697 {
3698 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
3699 string moduleSearchPath = env->getModuleSearchPaths().front();
3700 VuoCompiler *otherCompiler = new VuoCompiler(moduleSearchPath + "/unused", target);
3701
3702 dispatch_sync(environmentQueue, ^{
3703 otherCompiler->loadModulesAndSources(set<string>(), set<string>(), set<string>(),
3704 sourcesDepOnModulesAdded_otherCompiler[env], set<string>(), set<string>(),
3705 false, true, env, nullptr, nullptr, "");
3706 });
3707
3708 delete otherCompiler;
3709 });
3710 }
3711 }
3712
3713 set<dispatch_group_t> sourcesLoading;
3714 for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3715 {
3716 Environment *env = (*i).at(0);
3717
3718 set<string> sourcesToCompile;
3719 sourcesToCompile.insert(sourcesAdded[env].begin(), sourcesAdded[env].end());
3720 sourcesToCompile.insert(sourcesModified[env].begin(), sourcesModified[env].end());
3721
3722 if (sourcesToCompile.size() == 0)
3723 continue;
3724
3725 set<dispatch_group_t> s = env->compileModulesFromSourceCode(sourcesToCompile, shouldRecompileSourcesIfUnchanged);
3726 sourcesLoading.insert(s.begin(), s.end());
3727 }
3728
3729 for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3730 {
3731 Environment *env = (*i).at(0);
3732
3733 set<string> sourcesToCompile;
3734 sourcesToCompile.insert(sourcesDepOnModulesAdded[env].begin(), sourcesDepOnModulesAdded[env].end());
3735 sourcesToCompile.insert(sourcesDepOnModulesModified[env].begin(), sourcesDepOnModulesModified[env].end());
3736
3737 if (sourcesToCompile.size() == 0)
3738 continue;
3739
3740 env->compileModulesFromSourceCode(sourcesToCompile, true);
3741 }
3742
3743 // Notify compiler delegates.
3744
3745 for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3746 {
3747 Environment *env = (*i).at(0);
3748 env->notifyCompilers(actualModulesAdded[env], actualModulesModified[env], actualModulesRemoved[env], issues[env]);
3749 }
3750
3751 // Since the dispatch groups for specialized modules are temporary (caller is responsible for releasing them)
3752 // but the dispatch groups for module sources should stay alive as long as the ModuleInfo that contains them,
3753 // retain the dispatch groups for module sources so that all dispatch groups can be safely released by the caller.
3754
3755 for (const dispatch_group_t &group : sourcesLoading)
3756 dispatch_retain(group);
3757
3758 set<dispatch_group_t> loadingGroups;
3759 loadingGroups.insert(specializedModulesLoading.begin(), specializedModulesLoading.end());
3760 loadingGroups.insert(sourcesLoading.begin(), sourcesLoading.end());
3761 return loadingGroups;
3762}
3763
3775void VuoCompiler::findDependentModulesAndSources(map<Environment *, set<string> > &changedModules,
3776 const vector<VuoDirectedAcyclicNetwork *> &searchDependencyGraphs,
3777 VuoDirectedAcyclicGraph *currentEnvironmentDependencyGraph,
3778 map<Environment *, set<string> > &modulesDepOnChangedModules_this,
3779 map<Environment *, set<string> > &modulesDepOnChangedModules_other,
3780 map<Environment *, set<string> > &sourcesDepOnChangedModules_this,
3781 map<Environment *, set<string> > &sourcesDepOnChangedModules_other)
3782{
3783 for (const vector<Environment *> &envs : environments)
3784 {
3785 Environment *env = envs.at(0);
3786
3787 for (const string &module : changedModules[env])
3788 {
3789 set<VuoDirectedAcyclicGraph::Vertex *> dependents;
3790 for (VuoDirectedAcyclicNetwork *searchDependencyGraph : searchDependencyGraphs)
3791 {
3792 vector<VuoDirectedAcyclicGraph::Vertex *> moduleVertices;
3793 if (currentEnvironmentDependencyGraph)
3794 {
3795 // If a module with the same module key is installed in multiple locations,
3796 // only consider the one being modified or removed.
3797 VuoDirectedAcyclicGraph::Vertex *mv = currentEnvironmentDependencyGraph->findVertex(module);
3798 if (mv)
3799 moduleVertices.push_back(mv);
3800 }
3801 else
3802 moduleVertices = searchDependencyGraph->findVertex(module);
3803
3804 for (VuoDirectedAcyclicGraph::Vertex *moduleVertexRaw : moduleVertices)
3805 {
3806 DependencyGraphVertex *moduleVertex = static_cast<DependencyGraphVertex *>(moduleVertexRaw);
3807 if (moduleVertex->getEnvironment())
3808 {
3809 vector<VuoDirectedAcyclicGraph::Vertex *> upstream = searchDependencyGraph->getUpstreamVertices(moduleVertex);
3810 dependents.insert(upstream.begin(), upstream.end());
3811 }
3812 }
3813 }
3814
3815 set< pair<Environment *, string> > dependentsMap;
3816 for (VuoDirectedAcyclicGraph::Vertex *dependentVertexRaw : dependents)
3817 {
3818 DependencyGraphVertex *v = static_cast<DependencyGraphVertex *>(dependentVertexRaw);
3819 Environment *dependentEnv = v->getEnvironment();
3820 if (! dependentEnv)
3821 continue;
3822
3823 string dependent = v->getDependency();
3824
3825 dependentsMap.insert({dependentEnv, dependent});
3826 }
3827
3828 // In case `module` is a generic node class, check the generated environment at the same scope for any
3829 // specializations of the node class, and add them to the list of dependencies.
3830 // (They aren't in the dependency graph since the graph edge goes from installed to generated.)
3831 for (auto i : envs.at(1)->getNodeClasses())
3832 {
3833 VuoCompilerSpecializedNodeClass *specializedNodeClass = dynamic_cast<VuoCompilerSpecializedNodeClass *>(i.second);
3834 if (specializedNodeClass->getOriginalGenericNodeClassName() == module)
3835 dependentsMap.insert({envs.at(1), i.first});
3836 }
3837
3838 for (auto i : dependentsMap)
3839 {
3840 Environment *dependentEnv = i.first;
3841 string dependent = i.second;
3842
3843 // Skip if the dependent module is already being modified/removed in its own right
3844 // (e.g. if the module depends on another in the same node set and the node set is being removed).
3845 if (changedModules[dependentEnv].find(dependent) != changedModules[dependentEnv].end())
3846 continue;
3847
3848 ModuleInfo *foundSourceInfo = dependentEnv->listSourceFile(dependent);
3849 ModuleInfo *foundModuleInfo = dependentEnv->listModule(dependent);
3850
3851 bool belongsToCurrentCompiler = false;
3852 for (const vector<Environment *> &envs2 : environments)
3853 {
3854 if (find(envs2.begin(), envs2.end(), dependentEnv) != envs2.end())
3855 {
3856 belongsToCurrentCompiler = true;
3857 break;
3858 }
3859 }
3860
3861 map<Environment *, set<string> > *whicheverDependents = nullptr;
3862 ModuleInfo *moduleInfo = nullptr;
3863 if (foundSourceInfo)
3864 {
3865 moduleInfo = foundSourceInfo;
3866 whicheverDependents = (belongsToCurrentCompiler ? &sourcesDepOnChangedModules_this : &sourcesDepOnChangedModules_other);
3867 }
3868 else if (foundModuleInfo)
3869 {
3870 moduleInfo = foundModuleInfo;
3871 whicheverDependents = (belongsToCurrentCompiler ? &modulesDepOnChangedModules_this : &modulesDepOnChangedModules_other);
3872 }
3873 else // Module in generated environment
3874 {
3875 whicheverDependents = (belongsToCurrentCompiler ? &modulesDepOnChangedModules_this : &modulesDepOnChangedModules_other);
3876 }
3877
3878 (*whicheverDependents)[dependentEnv].insert(dependent);
3879 if (moduleInfo)
3880 moduleInfo->setAttempted(false);
3881 }
3882 }
3883 }
3884}
3885
3889void VuoCompiler::loadedModules(map<string, VuoCompilerModule *> modulesAdded,
3890 map<string, pair<VuoCompilerModule *, VuoCompilerModule *> > modulesModified,
3891 map<string, VuoCompilerModule *> modulesRemoved,
3892 VuoCompilerIssues *issues, void *delegateDataV, Environment *currentEnvironment)
3893{
3894 //VLog("C=%p %lu %lu %lu", this, modulesAdded.size(), modulesModified.size(), modulesRemoved.size());
3895
3896 // If a module is added, superseding a version of the same module installed at a broader scope,
3897 // notify this VuoCompiler that the module has been modified rather than added.
3898 //
3899 // If a module is added, but a version of the same module is already installed at a narrower scope,
3900 // don't notify this VuoCompiler, since it will continue to use the version at the narrower scope.
3901 //
3902 // Same idea when a module is modified or removed while another version is installed at a different scope.
3903
3904 auto findVersionsOfModule = [this, currentEnvironment] (const string &moduleKey)
3905 {
3906 vector< pair<Environment *, VuoCompilerModule *> > moduleVersions;
3907 for (const vector<Environment *> &envs : environments)
3908 {
3909 Environment *env = envs.at(0);
3910 VuoCompilerModule *module = env->findModule(moduleKey);
3911 if (module || env == currentEnvironment)
3912 moduleVersions.push_back( make_pair(env, module) );
3913 }
3914 return moduleVersions;
3915 };
3916
3917 for (map<string, VuoCompilerModule *>::iterator i = modulesAdded.begin(); i != modulesAdded.end(); )
3918 {
3919 string moduleKey = i->first;
3920 VuoCompilerModule *moduleAdded = i->second;
3921
3922 vector< pair<Environment *, VuoCompilerModule *> > moduleVersions = findVersionsOfModule(moduleKey);
3923
3924 if (moduleVersions.size() > 1)
3925 {
3926 modulesAdded.erase(i++);
3927
3928 if (moduleVersions.back().second == moduleAdded)
3929 {
3930 VuoCompilerModule *moduleSuperseded = moduleVersions.at(moduleVersions.size()-2).second;
3931 modulesModified[moduleKey] = make_pair(moduleSuperseded, moduleAdded);
3932 }
3933 }
3934 else
3935 ++i;
3936 }
3937
3938 for (map<string, pair<VuoCompilerModule *, VuoCompilerModule *> >::iterator i = modulesModified.begin(); i != modulesModified.end(); )
3939 {
3940 string moduleKey = i->first;
3941 VuoCompilerModule *moduleModified = i->second.second;
3942
3943 vector< pair<Environment *, VuoCompilerModule *> > moduleVersions = findVersionsOfModule(moduleKey);
3944
3945 if (moduleVersions.size() > 1 && moduleVersions.back().second != moduleModified)
3946 modulesModified.erase(i++);
3947 else
3948 ++i;
3949 }
3950
3951 for (map<string, VuoCompilerModule *>::iterator i = modulesRemoved.begin(); i != modulesRemoved.end(); )
3952 {
3953 string moduleKey = i->first;
3954 VuoCompilerModule *moduleRemoved = i->second;
3955
3956 vector< pair<Environment *, VuoCompilerModule *> > moduleVersions = findVersionsOfModule(moduleKey);
3957
3958 if (moduleVersions.size() > 1)
3959 {
3960 modulesRemoved.erase(i++);
3961
3962 if (moduleVersions.back().first == currentEnvironment)
3963 {
3964 VuoCompilerModule *moduleUnsuperseded = moduleVersions.at(moduleVersions.size()-2).second;
3965 modulesModified[moduleKey] = make_pair(moduleRemoved, moduleUnsuperseded);
3966 }
3967 }
3968 else
3969 ++i;
3970 }
3971
3972 dispatch_async(delegateQueue, ^{
3973 VuoCompilerDelegate::LoadedModulesData *delegateData = static_cast<VuoCompilerDelegate::LoadedModulesData *>(delegateDataV);
3974
3975 if (delegate && ! (modulesAdded.empty() && modulesModified.empty() && modulesRemoved.empty() && issues->isEmpty()))
3976 {
3977 delegate->enqueueData(delegateData);
3978 delegate->loadedModules(modulesAdded, modulesModified, modulesRemoved, issues);
3979 }
3980 else
3981 {
3982 delegateData->release();
3983 }
3984 });
3985}
3986
3992void VuoCompiler::loadNodeClassGeneratedAtRuntime(VuoCompilerNodeClass *nodeClass, Environment *env)
3993{
3994 Module *module = nodeClass->getModule();
3995 if (module)
3996 {
3997 dispatch_sync(llvmQueue, ^{
3998 setTargetForModule(nodeClass->getModule(), env->getTarget());
3999 });
4000 }
4001
4002 dispatch_sync(environmentQueue, ^{
4003 env->replaceNodeClass(nodeClass);
4004 });
4005
4006 __block map<string, VuoCompilerType *> inheritedTypes;
4007 void (^envReifyPortTypes)(Environment *) = ^void (Environment *env) {
4008 env->reifyPortTypes(inheritedTypes);
4009 map<string, VuoCompilerType *> currentTypes = env->getTypes();
4010 inheritedTypes.insert(currentTypes.begin(), currentTypes.end());
4011 };
4012 applyToAllEnvironments(envReifyPortTypes);
4013}
4014
4018void VuoCompiler::reifyGenericPortTypes(VuoCompilerComposition *composition)
4019{
4020 for (VuoCompilerNode *node : composition->getCachedGraph(this)->getNodes())
4021 reifyGenericPortTypes(node->getBase());
4022
4023 composition->invalidateCachedGraph();
4024}
4025
4029void VuoCompiler::reifyGenericPortTypes(VuoNode *node)
4030{
4032 if (! nodeClass)
4033 return;
4034
4035 // Reify any generic types on the node that don't already have a compiler detail.
4036
4037 vector<VuoPort *> inputPorts = node->getInputPorts();
4038 vector<VuoPort *> outputPorts = node->getOutputPorts();
4039 vector<VuoPort *> ports;
4040 ports.insert(ports.end(), inputPorts.begin(), inputPorts.end());
4041 ports.insert(ports.end(), outputPorts.begin(), outputPorts.end());
4042
4043 for (vector<VuoPort *>::iterator j = ports.begin(); j != ports.end(); ++j)
4044 {
4045 VuoCompilerPort *port = static_cast<VuoCompilerPort *>((*j)->getCompiler());
4046 VuoGenericType *genericType = dynamic_cast<VuoGenericType *>(port->getDataVuoType());
4047 if (! genericType)
4048 continue;
4049
4050 if (! genericType->hasCompiler())
4051 {
4052 VuoCompilerType * (^compilerGetType)(string) = ^VuoCompilerType * (string moduleKey) {
4053 return getType(moduleKey);
4054 };
4055
4056 VuoCompilerGenericType *reifiedType = VuoCompilerGenericType::newGenericType(genericType, compilerGetType);
4057 if (reifiedType)
4058 port->setDataVuoType(reifiedType->getBase());
4059 }
4060 }
4061
4062 // Update the node class's backing to match the node's backing.
4063
4064 nodeClass->updateBackingNodeClass(node, this);
4065}
4066
4073void VuoCompiler::compileModule(string inputPath, string outputPath)
4074{
4075 compileModule(inputPath, outputPath, vector<string>());
4076}
4077
4085void VuoCompiler::compileModule(string inputPath, string outputPath, const vector<string> &includePaths)
4086{
4087 if (isVerbose)
4088 print();
4089
4090 vector<string> allIncludePaths = includePaths;
4091 string preprocessedInputPath = inputPath;
4092
4093 string tmpPreprocessedInputDir;
4094 string dir, file, ext;
4095 VuoFileUtilities::splitPath(inputPath, dir, file, ext);
4097 {
4098 string inputContents = VuoFileUtilities::readFileToString(inputPath);
4099 string preprocessedInputContents = inputContents;
4101 if (inputContents != preprocessedInputContents)
4102 {
4103 // Unspecialized generic node class
4104 allIncludePaths.push_back(dir.empty() ? "." : dir);
4105 tmpPreprocessedInputDir = VuoFileUtilities::makeTmpDir(file);
4106 preprocessedInputPath = tmpPreprocessedInputDir + "/" + file + "." + ext;
4107 VuoFileUtilities::preserveOriginalFileName(preprocessedInputContents, file + "." + ext);
4108 VuoFileUtilities::writeStringToFile(preprocessedInputContents, preprocessedInputPath);
4109 }
4110 }
4111 else if (ext == "fs")
4112 {
4113 VuoFileUtilities::File vuf(dir, file + "." + ext);
4114 VuoModuleCompiler *moduleCompiler = VuoModuleCompiler::newModuleCompiler("isf", getModuleKeyForPath(inputPath), &vuf);
4115 if (moduleCompiler)
4116 {
4117 auto getType = [this] (const string &moduleKey) { return this->getType(moduleKey); };
4118 VuoCompilerIssues *issues = new VuoCompilerIssues();
4119 Module *module = moduleCompiler->compile(getType, llvmQueue, issues);
4120 if (module)
4121 dispatch_sync(llvmQueue, ^{
4122 setTargetForModule(module, target);
4123 writeModuleToBitcode(module, outputPath);
4124#if VUO_PRO
4125 string dependencyOutputPath = _dependencyOutput();
4126 if (!dependencyOutputPath.empty())
4127 {
4128 string outputObjectPath = dependencyOutputPath.substr(0, dependencyOutputPath.length() - 2);
4129 VuoFileUtilities::writeStringToFile(outputObjectPath + ": " + inputPath, dependencyOutputPath);
4130 }
4131#endif
4132 });
4133
4134 if (!issues->isEmpty())
4135 throw VuoCompilerException(issues, true);
4136 delete issues;
4137 }
4138 return;
4139 }
4140
4141 vector<string> extraArgs;
4142 for (vector<string>::iterator i = allIncludePaths.begin(); i != allIncludePaths.end(); ++i)
4143 {
4144 extraArgs.push_back("-I");
4145 extraArgs.push_back(*i);
4146 }
4147
4148
4149 // When compiling on a development workstation or Jenkins, use the Conan-packaged macOS SDK, since it includes headers.
4150 // When compiling on an end-user system, no SDK is needed.
4151 string buildTimeMacOSSDKFolder = MACOS_SDK_ROOT;
4152 if (VuoFileUtilities::fileExists(buildTimeMacOSSDKFolder))
4153 {
4154 extraArgs.push_back("-isysroot");
4155 extraArgs.push_back(buildTimeMacOSSDKFolder);
4156 }
4157
4158
4159 __block vector<string> headerSearchPaths;
4160 void (^envGetHeaderSearchPaths)(Environment *) = ^void (Environment *env) {
4161 vector<string> result = env->getHeaderSearchPaths();
4162 headerSearchPaths.insert(headerSearchPaths.end(), result.begin(), result.end());
4163 };
4164 applyToInstalledEnvironments(envGetHeaderSearchPaths);
4165
4166 auto issues = new VuoCompilerIssues;
4167 __block Module *module;
4168 dispatch_sync(llvmQueue, ^{
4169 module = readModuleFromC(preprocessedInputPath, headerSearchPaths, extraArgs, issues);
4170 });
4171 string moduleKey = getModuleKeyForPath(inputPath);
4172 if (! tmpPreprocessedInputDir.empty())
4173 {
4174 remove(tmpPreprocessedInputDir.c_str());
4175 issues->setFilePath(inputPath);
4176 }
4177 if (! module)
4178 throw VuoCompilerException(issues, true);
4179 delete issues;
4180
4181 dispatch_sync(llvmQueue, ^{
4183 if (! compilerModule)
4184 {
4185 VUserLog("Error: Didn't recognize '%s' as a node class, type, or library.", inputPath.c_str());
4186 return;
4187 }
4188
4189 setTargetForModule(module, target);
4190 writeModuleToBitcode(module, outputPath);
4191
4192 delete module;
4193 });
4194}
4195
4199Module * VuoCompiler::compileCompositionToModule(VuoCompilerComposition *composition, const string &moduleKey, bool isTopLevelComposition,
4200 VuoCompilerIssues *issues)
4201{
4202 composition->check(issues);
4203
4204 reifyGenericPortTypes(composition);
4205
4207 isTopLevelComposition,
4208 moduleKey, this);
4209
4210 __block Module *module = nullptr;
4211 dispatch_sync(llvmQueue, ^{
4212 try
4213 {
4214 module = generator->generateBitcode();
4215 setTargetForModule(module, target);
4216 }
4217 catch (VuoCompilerException &e)
4218 {
4219 if (issues)
4220 issues->append(e.getIssues());
4221 else
4222 VUserLog("%s", e.getIssues()->getLongDescription(false).c_str());
4223 }
4224 });
4225
4226 delete generator;
4227
4228 return module;
4229}
4230
4241void VuoCompiler::compileComposition(VuoCompilerComposition *composition, string outputPath, bool isTopLevelComposition,
4242 VuoCompilerIssues *issues)
4243{
4244 string moduleKey = getModuleKeyForPath(outputPath);
4245 Module *module = compileCompositionToModule(composition, moduleKey, isTopLevelComposition, issues);
4246 if (!module)
4247 throw VuoCompilerException(issues, false);
4248
4249 dispatch_sync(llvmQueue, ^{
4250 writeModuleToBitcode(module, outputPath);
4251 });
4252}
4253
4266void VuoCompiler::compileComposition(string inputPath, string outputPath, bool isTopLevelComposition,
4267 VuoCompilerIssues *issues)
4268{
4269 VUserLog("Compiling '%s' (%s)…", inputPath.c_str(), target.c_str());
4270 if (isVerbose)
4271 print();
4272
4273 if (getCompositionLocalPath().empty())
4274 setCompositionPath(inputPath);
4275
4277 {
4278 VuoCompilerIssue issue(VuoCompilerIssue::Error, "opening composition", inputPath,
4279 "", "The composition file couldn't be read or was empty.");
4280 throw VuoCompilerException(issue);
4281 }
4282
4283 try
4284 {
4285 string compositionString = VuoFileUtilities::readFileToString(inputPath);
4286 compileCompositionString(compositionString, outputPath, isTopLevelComposition, issues);
4287 }
4288 catch (VuoCompilerException &e)
4289 {
4290 if (e.getIssues())
4291 e.getIssues()->setFilePathIfEmpty(inputPath);
4292 if (!issues && e.getIssues())
4293 VUserLog("%s", e.getIssues()->getLongDescription(false).c_str());
4294 throw;
4295 }
4296
4297 VUserLog("Done.");
4298}
4299
4310void VuoCompiler::compileCompositionString(const string &compositionString, string outputPath, bool isTopLevelComposition,
4311 VuoCompilerIssues *issues)
4312{
4314 compileComposition(composition, outputPath, isTopLevelComposition, issues);
4315
4316 VuoComposition *baseComposition = composition->getBase();
4317 delete composition;
4318 delete baseComposition;
4319}
4320
4336void VuoCompiler::linkCompositionToCreateExecutable(string inputPath, string outputPath, Optimization optimization, string rPath, bool shouldAdHocCodeSign)
4337{
4338 vector<string> rPaths = ! rPath.empty() ? vector<string>(1, rPath) : getRunPathSearchPaths(environments.back().front());
4339 linkCompositionToCreateExecutableOrDynamicLibrary(inputPath, outputPath, optimization, false, rPaths, shouldAdHocCodeSign);
4340}
4341
4359void VuoCompiler::linkCompositionToCreateDynamicLibrary(string inputPath, string outputPath, Optimization optimization, bool shouldAdHocCodeSign)
4360{
4361 vector<string> rPaths = getRunPathSearchPaths(environments.back().front());
4362 linkCompositionToCreateExecutableOrDynamicLibrary(inputPath, outputPath, optimization, true, rPaths, shouldAdHocCodeSign);
4363}
4364
4379void VuoCompiler::linkCompositionToCreateExecutableOrDynamicLibrary(string compiledCompositionPath, string linkedCompositionPath,
4380 Optimization optimization, bool isDylib, const vector<string> &rPaths,
4381 bool shouldAdHocCodeSign)
4382{
4383 if (isVerbose)
4384 print();
4385
4386 if (optimization == Optimization_FastBuildExistingCache)
4387 shouldLoadAllModules = false;
4388
4389 set<string> dependencies = getDependenciesForComposition(compiledCompositionPath);
4390 dependencies.insert(getRuntimeDependency());
4391 if (! isDylib)
4392 dependencies.insert(getRuntimeMainDependency());
4393
4394 set<Module *> modules;
4395 set<string> libraries;
4396 set<string> frameworks;
4397 getLinkerInputs(dependencies, optimization, modules, libraries, frameworks);
4398
4399 libraries.insert(compiledCompositionPath);
4400
4401 link(linkedCompositionPath, modules, libraries, frameworks, isDylib, rPaths, shouldAdHocCodeSign);
4402}
4403
4418void VuoCompiler::linkCompositionToCreateDynamicLibraries(string compiledCompositionPath, string linkedCompositionPath,
4419 VuoRunningCompositionLibraries *runningCompositionLibraries)
4420{
4421 if (isVerbose)
4422 print();
4423
4424 bool shouldAdHocCodeSign = false;
4425#if __arm64__
4426 shouldAdHocCodeSign = true;
4427#endif
4428
4429 // Get the dependencies used by the new resources and not the previous resources.
4430
4431 set<string> carriedOverDependencies = runningCompositionLibraries->getDependenciesLoaded();
4432 set<string> allDependencies = getDependenciesForComposition(compiledCompositionPath);
4433 set<string> addedDependencies;
4434 std::set_difference(allDependencies.begin(), allDependencies.end(),
4435 carriedOverDependencies.begin(), carriedOverDependencies.end(),
4436 std::inserter(addedDependencies, addedDependencies.end()));
4437
4438 // Get the libraries and frameworks used by the previous resources.
4439
4440 vector<string> carriedOverNonUnloadableLibraries = runningCompositionLibraries->getNonUnloadableLibrariesLoaded();
4441 vector<string> carriedOverUnloadableLibraries = runningCompositionLibraries->getUnloadableLibrariesLoaded();
4442 set<string> carriedOverExternalLibraries = runningCompositionLibraries->getExternalLibraries();
4443 set<string> carriedOverFrameworks = runningCompositionLibraries->getExternalFrameworks();
4444
4445 // Link the new resource dylibs, if needed.
4446
4447 string nonUnloadableResourcePath;
4448 string unloadableResourcePath;
4449 set<string> nonUnloadableDependencies;
4450 set<string> unloadableDependencies;
4451 map<string, set<string> > builtInCacheDependencies;
4452 map<string, set<string> > userCacheDependencies;
4453 set<string> builtInLibraries;
4454 set<string> userLibraries;
4455 set<string> addedExternalLibraries;
4456 set<string> addedFrameworks;
4457 set<string> allFrameworks;
4458 if (! addedDependencies.empty())
4459 {
4460 // Get the modules, libraries, and frameworks that will provide the composition's dependencies.
4461
4462 set<string> builtInModuleAndLibraryDependencies;
4463 set<string> userModuleAndLibraryDependencies;
4464 set<Module *> builtInModules;
4465 set<Module *> userModules;
4466
4467 getLinkerInputs(addedDependencies, Optimization_FastBuild,
4468 builtInModuleAndLibraryDependencies, userModuleAndLibraryDependencies, builtInCacheDependencies, userCacheDependencies,
4469 builtInModules, userModules, builtInLibraries, userLibraries, addedExternalLibraries, addedFrameworks);
4470
4471 allFrameworks.insert(carriedOverFrameworks.begin(), carriedOverFrameworks.end());
4472 allFrameworks.insert(addedFrameworks.begin(), addedFrameworks.end());
4473
4474 string dir, linkedCompositionFile, ext;
4475 VuoFileUtilities::splitPath(linkedCompositionPath, dir, linkedCompositionFile, ext);
4476
4477 // For any module caches that were rebuilt, remove the previous revision from the lists of libraries to link to and load.
4478
4479 vector<string> carriedOverUserCacheLibraries = runningCompositionLibraries->getUnloadableCacheLibrariesLoaded();
4480
4481 __block vector<string> currentCacheLibraries;
4482 applyToAllEnvironments(^void (Environment *env) {
4483 currentCacheLibraries.push_back( env->getCurrentModuleCacheDylib() );
4484 });
4485
4486 for (string cachePath : carriedOverUserCacheLibraries)
4487 {
4488 for (string currentCachePath : currentCacheLibraries)
4489 {
4491 {
4492 set<string> dependenciesInCache = runningCompositionLibraries->enqueueCacheLibraryToUnload(cachePath);
4493
4494 userCacheDependencies[currentCachePath].insert(dependenciesInCache.begin(), dependenciesInCache.end());
4495
4496 auto cacheDependenciesIter = userCacheDependencies.find(cachePath);
4497 if (cacheDependenciesIter != userCacheDependencies.end())
4498 {
4499 userCacheDependencies[currentCachePath].insert(cacheDependenciesIter->second.begin(), cacheDependenciesIter->second.end());
4500 userCacheDependencies.erase(cacheDependenciesIter);
4501 }
4502
4503 auto carriedOverIter = find(carriedOverUnloadableLibraries.begin(), carriedOverUnloadableLibraries.end(), cachePath);
4504 if (carriedOverIter != carriedOverUnloadableLibraries.end())
4505 *carriedOverIter = currentCachePath;
4506 }
4507 }
4508 }
4509
4510 // If any module caches were rebuilt, prepare to replace the existing user resource dylibs with the new resource dylib created below.
4511
4512 bool wasModuleCacheRebuilt = runningCompositionLibraries->hasCacheLibraryEnqueuedToUnload();
4513 if (wasModuleCacheRebuilt)
4514 {
4515 vector<string> carriedOverResourceLibraries = runningCompositionLibraries->getUnloadableResourceLibrariesLoaded();
4516
4517 vector<string> carriedOverUnloadableMinusResourceLibraries;
4518 std::set_difference(carriedOverUnloadableLibraries.begin(), carriedOverUnloadableLibraries.end(),
4519 carriedOverResourceLibraries.begin(), carriedOverResourceLibraries.end(),
4520 std::back_inserter(carriedOverUnloadableMinusResourceLibraries));
4521
4522 carriedOverUnloadableLibraries = carriedOverUnloadableMinusResourceLibraries;
4523
4524 set<string> dependenciesInResourceLibraries = runningCompositionLibraries->enqueueAllUnloadableResourceLibrariesToUnload();
4525 userModuleAndLibraryDependencies.insert(dependenciesInResourceLibraries.begin(), dependenciesInResourceLibraries.end());
4526
4527 set<string> builtInModuleAndLibraryDependencies_tmp;
4528 set<string> userModuleAndLibraryDependencies_tmp;
4529 map<string, set<string> > builtInCacheDependencies_tmp;
4530 set<Module *> builtInModules_tmp;
4531 set<string> builtInLibraries_tmp;
4532 set<string> externalLibraries_tmp;
4533 set<string> externalFrameworks_tmp;
4534
4535 getLinkerInputs(userModuleAndLibraryDependencies, Optimization_FastBuild,
4536 builtInModuleAndLibraryDependencies_tmp, userModuleAndLibraryDependencies_tmp, builtInCacheDependencies_tmp, userCacheDependencies,
4537 builtInModules_tmp, userModules, builtInLibraries_tmp, userLibraries, externalLibraries_tmp, externalFrameworks_tmp);
4538 }
4539
4540 // If built-in dependencies were added, create an additional resource dylib.
4541
4542 if (! builtInModules.empty() || builtInLibraries.size() > builtInCacheDependencies.size())
4543 {
4544 nonUnloadableResourcePath = VuoFileUtilities::makeTmpFile(linkedCompositionFile + "-resource-nonunloadable", "dylib");
4545 nonUnloadableDependencies = builtInModuleAndLibraryDependencies;
4546
4547 set<string> librariesForNonUnloadableResource = builtInLibraries;
4548 librariesForNonUnloadableResource.insert(carriedOverNonUnloadableLibraries.begin(), carriedOverNonUnloadableLibraries.end());
4549 librariesForNonUnloadableResource.insert(carriedOverExternalLibraries.begin(), carriedOverExternalLibraries.end());
4550 librariesForNonUnloadableResource.insert(addedExternalLibraries.begin(), addedExternalLibraries.end());
4551
4552 vector<string> rPaths = getRunPathSearchPaths(environments.front().front());
4553
4554 link(nonUnloadableResourcePath, builtInModules, librariesForNonUnloadableResource, allFrameworks, true, rPaths, shouldAdHocCodeSign);
4555
4556 for (set<string>::iterator i = builtInLibraries.begin(); i != builtInLibraries.end(); )
4557 {
4558 if (! VuoStringUtilities::endsWith(*i, ".dylib"))
4559 builtInLibraries.erase(i++);
4560 else
4561 i++;
4562 }
4563
4564 for (set<string>::iterator i = addedExternalLibraries.begin(); i != addedExternalLibraries.end(); )
4565 {
4566 if (! VuoStringUtilities::endsWith(*i, ".dylib"))
4567 addedExternalLibraries.erase(i++);
4568 else
4569 i++;
4570 }
4571 }
4572
4573 // If user dependencies were added or module caches were rebuilt, create an additional resource dylib.
4574
4575 if (! userModules.empty() || userLibraries.size() > userCacheDependencies.size() || wasModuleCacheRebuilt)
4576 {
4577 unloadableResourcePath = VuoFileUtilities::makeTmpFile(linkedCompositionFile + "-resource-unloadable", "dylib");
4578 unloadableDependencies = userModuleAndLibraryDependencies;
4579
4580 set<string> librariesForUnloadableResource = userLibraries;
4581 librariesForUnloadableResource.insert(builtInLibraries.begin(), builtInLibraries.end());
4582 librariesForUnloadableResource.insert(carriedOverUnloadableLibraries.begin(), carriedOverUnloadableLibraries.end());
4583 librariesForUnloadableResource.insert(carriedOverNonUnloadableLibraries.begin(), carriedOverNonUnloadableLibraries.end());
4584 librariesForUnloadableResource.insert(carriedOverExternalLibraries.begin(), carriedOverExternalLibraries.end());
4585 librariesForUnloadableResource.insert(addedExternalLibraries.begin(), addedExternalLibraries.end());
4586 if (! nonUnloadableResourcePath.empty())
4587 librariesForUnloadableResource.insert(nonUnloadableResourcePath);
4588
4589 // This is usually correct, but may fail in the case where there are two identically-named dylibs at different
4590 // levels of scope. However, it's the best we can do as long as modules at the system, user, and composition
4591 // levels of scope are all combined into one resource dylib. (https://b33p.net/kosada/vuo/vuo/-/merge_requests/196#note_2148884)
4592 vector<string> rPaths = getRunPathSearchPaths(environments.back().front());
4593
4594 link(unloadableResourcePath, userModules, librariesForUnloadableResource, allFrameworks, true, rPaths, shouldAdHocCodeSign);
4595
4596 for (set<string>::iterator i = userLibraries.begin(); i != userLibraries.end(); )
4597 {
4598 if (! VuoStringUtilities::endsWith(*i, ".dylib"))
4599 userLibraries.erase(i++);
4600 else
4601 i++;
4602 }
4603 }
4604 }
4605
4606 // Get the Vuo runtime dependency.
4607
4608 set<string> vuoRuntimePaths;
4609 {
4610 set<Module *> modules;
4611 set<string> libraries;
4612 set<string> frameworks;
4613
4614 set<string> dependencies;
4615 dependencies.insert(getRuntimeDependency());
4616 getLinkerInputs(dependencies, Optimization_FastBuild, modules, libraries, frameworks);
4617 vuoRuntimePaths = libraries;
4618 }
4619
4620 // Link the composition.
4621
4622 {
4623 set<Module *> modules;
4624 set<string> libraries;
4625
4626 libraries.insert(compiledCompositionPath);
4627 libraries.insert(carriedOverExternalLibraries.begin(), carriedOverExternalLibraries.end());
4628 libraries.insert(addedExternalLibraries.begin(), addedExternalLibraries.end());
4629 libraries.insert(carriedOverNonUnloadableLibraries.begin(), carriedOverNonUnloadableLibraries.end());
4630 libraries.insert(carriedOverUnloadableLibraries.begin(), carriedOverUnloadableLibraries.end());
4631 libraries.insert(builtInLibraries.begin(), builtInLibraries.end());
4632 libraries.insert(userLibraries.begin(), userLibraries.end());
4633 if (! nonUnloadableResourcePath.empty())
4634 libraries.insert(nonUnloadableResourcePath);
4635 if (! unloadableResourcePath.empty())
4636 libraries.insert(unloadableResourcePath);
4637 libraries.insert(vuoRuntimePaths.begin(), vuoRuntimePaths.end());
4638 vector<string> rPaths = getRunPathSearchPaths(environments.front().front());
4639 link(linkedCompositionPath, modules, libraries, allFrameworks, true, rPaths, shouldAdHocCodeSign);
4640 }
4641
4642 // Now that we're past the point where an exception can be thrown, update the RunningCompositionLibraries.
4643
4644 if (! nonUnloadableResourcePath.empty())
4645 runningCompositionLibraries->enqueueResourceLibraryToLoad(nonUnloadableResourcePath, nonUnloadableDependencies, false);
4646
4647 if (! unloadableResourcePath.empty())
4648 runningCompositionLibraries->enqueueResourceLibraryToLoad(unloadableResourcePath, unloadableDependencies, true);
4649
4650 for (map<string, set<string> >::iterator i = builtInCacheDependencies.begin(); i != builtInCacheDependencies.end(); ++i)
4651 runningCompositionLibraries->enqueueCacheLibraryToLoad(i->first, i->second, false);
4652
4653 for (map<string, set<string> >::iterator i = userCacheDependencies.begin(); i != userCacheDependencies.end(); ++i)
4654 runningCompositionLibraries->enqueueCacheLibraryToLoad(i->first, i->second, true);
4655
4656 runningCompositionLibraries->addExternalFrameworks(addedFrameworks);
4657 runningCompositionLibraries->addExternalLibraries(addedExternalLibraries);
4658}
4659
4666set<string> VuoCompiler::getDependenciesForComposition(const string &compiledCompositionPath)
4667{
4668 double t0 = VuoLogGetTime();
4669
4670 // Add the node classes in the top-level composition and their dependencies.
4671 __block set<string> directDependencies;
4672 string moduleKey = getModuleKeyForPath(compiledCompositionPath);
4673 Module *module = readModuleFromBitcode(compiledCompositionPath, getTargetArch(target));
4674 dispatch_sync(llvmQueue, ^{
4676 directDependencies = compilerModule->getDependencies();
4677 delete compilerModule;
4678 delete module;
4679 });
4680
4681 try
4682 {
4683 auto deps = getDependenciesForComposition(directDependencies, true);
4684 VUserLog("Gathering dependencies for '%s' took %5.2fs", compiledCompositionPath.c_str(), VuoLogGetTime() - t0);
4685 return deps;
4686 }
4687 catch (VuoCompilerException &e)
4688 {
4689 e.getIssues()->setFilePathIfEmpty(compiledCompositionPath);
4690 throw;
4691 }
4692}
4693
4701{
4702 set<string> directDependencies;
4703
4704 set<VuoCompilerNode *> nodes = composition->getCachedGraph(this)->getNodes();
4705 for (VuoCompilerNode *node : nodes)
4706 if (node->getBase()->getNodeClass()->hasCompiler())
4707 directDependencies.insert( node->getBase()->getNodeClass()->getCompiler()->getDependencyName() );
4708
4709 vector<VuoPublishedPort *> publishedInputPorts = composition->getBase()->getPublishedInputPorts();
4710 vector<VuoPublishedPort *> publishedOutputPorts = composition->getBase()->getPublishedOutputPorts();
4711 vector<VuoPublishedPort *> publishedPorts;
4712 publishedPorts.insert(publishedPorts.end(), publishedInputPorts.begin(), publishedInputPorts.end());
4713 publishedPorts.insert(publishedPorts.end(), publishedOutputPorts.begin(), publishedOutputPorts.end());
4714 for (VuoPublishedPort *publishedPort : publishedPorts)
4715 {
4716 if (publishedPort->getClass()->hasCompiler())
4717 {
4718 VuoType *portType = static_cast<VuoCompilerPortClass *>( publishedPort->getClass()->getCompiler() )->getDataVuoType();
4719 if (portType)
4720 {
4721 string dependency;
4722 VuoGenericType *genericType = dynamic_cast<VuoGenericType *>(portType);
4723 if (genericType)
4724 {
4725 VuoGenericType::Compatibility compatibility;
4726 vector<string> compatibleTypeNames = genericType->getCompatibleSpecializedTypes(compatibility);
4727 dependency = VuoCompilerGenericType::chooseBackingTypeName(portType->getModuleKey(), compatibleTypeNames);
4728 }
4729 else
4730 dependency = portType->getModuleKey();
4731
4732 directDependencies.insert(dependency);
4733 }
4734 }
4735 }
4736
4737 return directDependencies;
4738}
4739
4746set<string> VuoCompiler::getDependenciesForComposition(VuoCompilerComposition *composition)
4747{
4748 set<string> directDependencies = getDirectDependenciesForComposition(composition);
4749 return getDependenciesForComposition(directDependencies, false);
4750}
4751
4758{
4759 __block vector<string> librarySearchPaths;
4760 applyToInstalledEnvironments(^void (Environment *env) {
4761 vector<string> result = env->getLibrarySearchPaths();
4762 librarySearchPaths.insert(librarySearchPaths.end(), result.begin(), result.end());
4763 });
4764
4765 set<string> dylibDeps;
4766 for (string dep : getDependenciesForComposition(composition))
4767 {
4768 string path = getLibraryPath(dep, librarySearchPaths);
4769 if (VuoStringUtilities::endsWith(path, ".dylib"))
4770 dylibDeps.insert(path);
4771 }
4772
4773 return dylibDeps;
4774}
4775
4789set<string> VuoCompiler::getDependenciesForComposition(const set<string> &directDependencies, bool checkCompatibility)
4790{
4791 // Make sure that any compiler-generated node classes have been generated and added to the dependency graph.
4792 for (set<string>::const_iterator i = directDependencies.begin(); i != directDependencies.end(); ++i)
4793 getNodeClass(*i);
4794
4795 set<string> dependencies;
4796 for (set<string>::iterator i = directDependencies.begin(); i != directDependencies.end(); ++i)
4797 {
4798 string moduleKey = *i;
4799
4800 dependencies.insert(moduleKey);
4801
4802 // First pass: Find all dependencies of the direct dependency that have been loaded so far.
4803 vector<VuoDirectedAcyclicGraph::Vertex *> firstPassVertices = dependencyGraph->findVertex(moduleKey);
4804 set<VuoDirectedAcyclicGraph::Vertex *> firstPassDependencies(firstPassVertices.begin(), firstPassVertices.end());
4805 for (vector<VuoDirectedAcyclicGraph::Vertex *>::iterator j = firstPassVertices.begin(); j != firstPassVertices.end(); ++j)
4806 {
4807 vector<VuoDirectedAcyclicGraph::Vertex *> downstream = dependencyGraph->getDownstreamVertices(*j);
4808 firstPassDependencies.insert(downstream.begin(), downstream.end());
4809 }
4810
4811 // Make sure that any compiler-generated node classes in subcompositions have been generated and added to the dependency graph.
4812 for (set<VuoDirectedAcyclicGraph::Vertex *>::iterator j = firstPassDependencies.begin(); j != firstPassDependencies.end(); ++j)
4813 {
4814 DependencyGraphVertex *v = static_cast<DependencyGraphVertex *>(*j);
4815 getNodeClass(v->getDependency());
4816 }
4817
4818 // Second pass: Find all dependencies of the direct dependency, this time including dependencies of the node classes just generated.
4819 vector<VuoDirectedAcyclicGraph::Vertex *> moduleVertices = dependencyGraph->findVertex(moduleKey);
4820 for (vector<VuoDirectedAcyclicGraph::Vertex *>::iterator j = moduleVertices.begin(); j != moduleVertices.end(); ++j)
4821 {
4822 vector<VuoDirectedAcyclicGraph::Vertex *> downstream = dependencyGraph->getDownstreamVertices(*j);
4823 for (VuoDirectedAcyclicGraph::Vertex *v : downstream)
4824 dependencies.insert( static_cast<DependencyGraphVertex *>(v)->getDependency() );
4825 }
4826 }
4827
4828 // Check that the dependencies are compatible with the compiler's target.
4829 if (checkCompatibility)
4830 {
4832 VuoCompilerCompatibility actualCompatibility = getCompatibilityOfDependencies(dependencies);
4833
4834 if (! actualCompatibility.isCompatibleWith(neededCompatibility))
4835 {
4837 set<VuoCompilerNodeClass *> nodeClassesReported;
4838
4839 for (string moduleKey : dependencies)
4840 {
4841 VuoCompilerModule *module = getModule(moduleKey);
4842 if (module && ! module->getCompatibleTargets().isCompatibleWith(neededCompatibility))
4843 {
4844 vector<VuoCompilerNodeClass *> incompatibleNodeClasses;
4845
4846 VuoCompilerNodeClass *moduleAsNodeClass = dynamic_cast<VuoCompilerNodeClass *>(module);
4847 if (moduleAsNodeClass)
4848 incompatibleNodeClasses.push_back(moduleAsNodeClass);
4849 else
4850 {
4851 vector<VuoDirectedAcyclicGraph::Vertex *> moduleVertices = dependencyGraph->findVertex(moduleKey);
4852 for (VuoDirectedAcyclicGraph::Vertex *v : moduleVertices)
4853 {
4854 vector<VuoDirectedAcyclicGraph::Vertex *> upstream = dependencyGraph->getUpstreamVertices(v);
4855
4856 vector<string> upstreamModuleKeys;
4857 std::transform(upstream.begin(), upstream.end(),
4858 std::back_inserter(upstreamModuleKeys),
4859 [](VuoDirectedAcyclicGraph::Vertex *u){ return static_cast<DependencyGraphVertex *>(u)->getDependency(); });
4860
4861 vector<string> potentialNodeClassNames;
4862 std::set_intersection(directDependencies.begin(), directDependencies.end(),
4863 upstreamModuleKeys.begin(), upstreamModuleKeys.end(),
4864 std::back_inserter(potentialNodeClassNames));
4865
4866 for (string nodeClassName : potentialNodeClassNames)
4867 {
4868 VuoCompilerNodeClass *upstreamNodeClass = getNodeClass(nodeClassName);
4869 if (upstreamNodeClass)
4870 incompatibleNodeClasses.push_back(upstreamNodeClass);
4871 }
4872 }
4873 }
4874
4875 if (incompatibleNodeClasses.empty())
4876 {
4877 VuoCompilerIssue issue(VuoCompilerIssue::Error, "linking composition", "",
4878 "Dependencies incompatible with system",
4879 "%module is only compatible with " + module->getCompatibleTargets().toString() +
4880 ", so this composition can't run on " + neededCompatibility.toString() + ".");
4881 issue.setModule(module->getPseudoBase());
4882 issues->append(issue);
4883 }
4884 else
4885 {
4886 for (VuoCompilerNodeClass *nodeClass : incompatibleNodeClasses)
4887 {
4888 if (nodeClassesReported.find(nodeClass) != nodeClassesReported.end())
4889 continue;
4890
4891 nodeClassesReported.insert(nodeClass);
4892
4893 VuoCompilerIssue issue(VuoCompilerIssue::Error, "linking composition", "",
4894 "Nodes incompatible with system",
4895 "%module is only compatible with " + module->getCompatibleTargets().toString() +
4896 ", so this composition can't run on " + neededCompatibility.toString() + ".");
4897 issue.setModule(nodeClass->getPseudoBase());
4898 issues->append(issue);
4899 }
4900 }
4901 }
4902 }
4903
4904 if (issues->isEmpty())
4905 {
4906 VuoCompilerIssue issue(VuoCompilerIssue::Error, "linking composition", "",
4907 "Dependencies incompatible with system",
4908 "Some dependencies of this composition are only compatible with " + actualCompatibility.toString() +
4909 ", so this composition can't run on " + neededCompatibility.toString() + ".");
4910 issues->append(issue);
4911 }
4912
4913 throw VuoCompilerException(issues, true);
4914 }
4915 }
4916
4917 // Add the libraries needed by every linked composition.
4918 vector<string> coreDependencies = getCoreVuoDependencies();
4919 dependencies.insert(coreDependencies.begin(), coreDependencies.end());
4920
4921 return dependencies;
4922}
4923
4928{
4929 VuoCompilerCompatibility compatibility(nullptr);
4930
4931 for (string dependency : dependencies)
4932 {
4933 VuoCompilerModule *module = getModule(dependency);
4934 if (module)
4935 compatibility = compatibility.intersection(module->getCompatibleTargets());
4936 }
4937
4938 return compatibility;
4939}
4940
4945void VuoCompiler::getLinkerInputs(const set<string> &dependencies, Optimization optimization,
4946 set<Module *> &modules, set<string> &libraries, set<string> &frameworks)
4947{
4948 set<string> builtInModuleAndLibraryDependencies;
4949 set<string> userModuleAndLibraryDependencies;
4950 map<string, set<string> > builtInCacheDependencies;
4951 map<string, set<string> > userCacheDependencies;
4952 set<Module *> builtInModules;
4953 set<Module *> userModules;
4954 set<string> builtInLibraries;
4955 set<string> userLibraries;
4956 set<string> externalLibraries;
4957
4958 getLinkerInputs(dependencies, optimization,
4959 builtInModuleAndLibraryDependencies, userModuleAndLibraryDependencies, builtInCacheDependencies, userCacheDependencies,
4960 builtInModules, userModules, builtInLibraries, userLibraries, externalLibraries, frameworks);
4961
4962 modules.insert(builtInModules.begin(), builtInModules.end());
4963 modules.insert(userModules.begin(), userModules.end());
4964 libraries.insert(builtInLibraries.begin(), builtInLibraries.end());
4965 libraries.insert(userLibraries.begin(), userLibraries.end());
4966 libraries.insert(externalLibraries.begin(), externalLibraries.end());
4967}
4968
4982void VuoCompiler::getLinkerInputs(const set<string> &dependencies, Optimization optimization,
4983 set<string> &builtInModuleAndLibraryDependencies, set<string> &userModuleAndLibraryDependencies,
4984 map<string, set<string> > &builtInCacheDependencies, map<string, set<string> > &userCacheDependencies,
4985 set<Module *> &builtInModules, set<Module *> &userModules,
4986 set<string> &builtInLibraries, set<string> &userLibraries,
4987 set<string> &externalLibraries, set<string> &externalFrameworks)
4988{
4989 bool shouldUseModuleCache = (optimization == Optimization_FastBuild || optimization == Optimization_FastBuildExistingCache);
4990 if (shouldUseModuleCache)
4991 useModuleCache(true, optimization == Optimization_FastBuildExistingCache);
4992
4993 __block vector<string> librarySearchPaths;
4994 void (^envGetLibrarySearchPaths)(Environment *) = ^void (Environment *env) {
4995 vector<string> result = env->getLibrarySearchPaths();
4996 librarySearchPaths.insert(librarySearchPaths.end(), result.begin(), result.end());
4997 };
4998 applyToInstalledEnvironments(envGetLibrarySearchPaths);
4999
5000 for (set<string>::iterator i = dependencies.begin(); i != dependencies.end(); ++i)
5001 {
5002 string dependency = *i;
5003
5004 bool foundInCache = false;
5005 string moduleCachePath;
5006 bool isInBuiltInModuleCache = false;
5007 if (shouldUseModuleCache)
5008 foundInCache = findInModuleCache(dependency, moduleCachePath, isInBuiltInModuleCache);
5009
5010 if (foundInCache)
5011 {
5012 if (isInBuiltInModuleCache)
5013 {
5014 builtInLibraries.insert(moduleCachePath);
5015 builtInCacheDependencies[moduleCachePath].insert(dependency);
5016 }
5017 else
5018 {
5019 userLibraries.insert(moduleCachePath);
5020 userCacheDependencies[moduleCachePath].insert(dependency);
5021 }
5022 }
5023 else
5024 {
5025 __block VuoCompilerModule *module = NULL;
5026 void (^envFindModule)(Environment *) = ^void (Environment *env) {
5027 VuoCompilerModule *result = env->findModule(dependency);
5028 if (result)
5029 module = result;
5030 };
5031 applyToAllEnvironments(envFindModule);
5032
5033 if (module)
5034 {
5035 VuoCompilerNodeClass *nodeClass = dynamic_cast<VuoCompilerNodeClass *>(module);
5036 if (! (nodeClass && VuoCompilerSpecializedNodeClass::hasGenericPortTypes(nodeClass)) ) // Skip not-fully-specialized generic modules
5037 {
5038 string modulePath = module->getModulePath();
5039 if (! modulePath.empty() && dynamic_cast<VuoCompilerType *>(module))
5040 {
5041 if (module->isBuiltIn())
5042 builtInLibraries.insert(modulePath);
5043 else
5044 userLibraries.insert(modulePath);
5045 }
5046 else
5047 {
5048 if (module->isBuiltIn())
5049 builtInModules.insert(module->getModule());
5050 else
5051 userModules.insert(module->getModule());
5052 }
5053
5054 if (module->isBuiltIn())
5055 builtInModuleAndLibraryDependencies.insert(dependency);
5056 else
5057 userModuleAndLibraryDependencies.insert(dependency);
5058 }
5059 }
5060 else
5061 {
5062 if (VuoStringUtilities::endsWith(dependency, ".framework"))
5063 externalFrameworks.insert(dependency);
5064 else
5065 {
5066 string dependencyPath = getLibraryPath(dependency, librarySearchPaths);
5067 if (! dependencyPath.empty())
5068 externalLibraries.insert(dependencyPath);
5069
5070 // On macOS 11, libc.dylib and libobjc.dylib are not present,
5071 // but we can still link since Vuo.framework includes the TBDs.
5072 else if (dependency != "c"
5073 && dependency != "objc")
5074 VUserLog("Warning: Could not locate dependency '%s'.", dependency.c_str());
5075 }
5076 }
5077 }
5078 }
5079}
5080
5086string VuoCompiler::getLibraryPath(const string &dependency, vector<string> librarySearchPaths)
5087{
5088 if (dependency[0] == '/' && VuoFileUtilities::fileExists(dependency))
5089 return dependency;
5090
5091 // Put the system library folder last in the list — prefer to use the libraries bundled in Vuo.framework.
5092 // Don't attempt to use OpenSSL from the system library folder, since macOS 10.15 prevents linking to it.
5093 if (dependency != "crypto"
5094 && dependency != "ssl")
5095 librarySearchPaths.push_back("/usr/lib");
5096
5097 for (auto &path : librarySearchPaths)
5098 {
5099 vector<string> variations;
5100 variations.push_back(path + "/" + dependency);
5101 variations.push_back(path + "/lib" + dependency);
5102 variations.push_back(path + "/lib" + dependency + ".dylib");
5103 variations.push_back(path + "/lib" + dependency + ".a");
5104 for (auto &variation : variations)
5105 if (VuoFileUtilities::fileExists(variation))
5106 return variation;
5107 }
5108
5109 return "";
5110}
5111
5119void VuoCompiler::useModuleCache(bool shouldUseExistingBuiltInCaches, bool shouldUseExistingOtherCaches)
5120{
5121 loadModulesIfNeeded();
5122 dispatch_group_wait(moduleSourceCompilersExist, DISPATCH_TIME_FOREVER); // Wait for any previous loadModulesIfNeeded() calls to complete.
5123
5124 // Iterate through the environments in the order that the caches need to be built.
5125
5126 dispatch_sync(environmentQueue, ^{
5127 set<string> dylibsForCachesOfInstalledModules;
5128 set<string> frameworksForCachesOfInstalledModules;
5129 unsigned long lastPrerequisiteModuleCacheRebuild = 0;
5130 for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
5131 {
5132 set<string> dylibsForCacheOfGeneratedModules;
5133 set<string> frameworksForCacheOfGeneratedModules;
5134
5135 for (int j = i->size() - 1; j >= 0; --j)
5136 {
5137 Environment *env = i->at(j);
5138 bool installed = (j == 0);
5139
5140 set<string> cacheableModulesAndDependencies;
5141 set<string> dylibsNeededToLinkToThisCache;
5142 set<string> frameworksNeededToLinkToThisCache;
5143 env->getCacheableModulesAndDependencies(cacheableModulesAndDependencies,
5144 dylibsNeededToLinkToThisCache, frameworksNeededToLinkToThisCache);
5145
5146 set<string> accumulatedDylibs;
5147 accumulatedDylibs.insert(dylibsNeededToLinkToThisCache.begin(), dylibsNeededToLinkToThisCache.end());
5148 accumulatedDylibs.insert(dylibsForCachesOfInstalledModules.begin(), dylibsForCachesOfInstalledModules.end());
5149 accumulatedDylibs.insert(dylibsForCacheOfGeneratedModules.begin(), dylibsForCacheOfGeneratedModules.end());
5150
5151 set<string> accumulatedFrameworks;
5152 accumulatedFrameworks.insert(frameworksNeededToLinkToThisCache.begin(), frameworksNeededToLinkToThisCache.end());
5153 accumulatedFrameworks.insert(frameworksForCachesOfInstalledModules.begin(), frameworksForCachesOfInstalledModules.end());
5154 accumulatedFrameworks.insert(frameworksForCacheOfGeneratedModules.begin(), frameworksForCacheOfGeneratedModules.end());
5155
5156 bool shouldUseExistingCache = (env->isBuiltIn() ? shouldUseExistingBuiltInCaches : shouldUseExistingOtherCaches);
5157 env->useModuleCache(shouldUseExistingCache, this, cacheableModulesAndDependencies,
5158 accumulatedDylibs, accumulatedFrameworks, lastPrerequisiteModuleCacheRebuild);
5159
5160 string cacheDylib = env->getCurrentModuleCacheDylib();
5161 accumulatedDylibs.insert(cacheDylib);
5162 dylibsForCachesOfInstalledModules.insert(cacheDylib);
5163
5164 lastPrerequisiteModuleCacheRebuild = max(lastPrerequisiteModuleCacheRebuild, env->getLastModuleCacheRebuild());
5165
5166 if (installed)
5167 {
5168 dylibsForCachesOfInstalledModules.insert(dylibsNeededToLinkToThisCache.begin(), dylibsNeededToLinkToThisCache.end());
5169 frameworksForCachesOfInstalledModules.insert(frameworksNeededToLinkToThisCache.begin(), frameworksNeededToLinkToThisCache.end());
5170 }
5171 else
5172 {
5173 dylibsForCacheOfGeneratedModules.insert(dylibsNeededToLinkToThisCache.begin(), dylibsNeededToLinkToThisCache.end());
5174 frameworksForCacheOfGeneratedModules.insert(frameworksNeededToLinkToThisCache.begin(), frameworksNeededToLinkToThisCache.end());
5175 }
5176 }
5177 }
5178 });
5179
5180 Environment::waitForModuleCachesToBuild();
5181}
5182
5191bool VuoCompiler::findInModuleCache(const string &moduleOrDependency, string &cachePath, bool &isBuiltinCache)
5192{
5193 __block bool found = false;
5194 __block string outPath;
5195 __block bool outBuiltin;
5196 dispatch_sync(environmentQueue, ^{
5197 for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
5198 {
5199 bool builtin = (i == environments.begin());
5200
5201 for (int j = i->size() - 1; j >= 0; --j)
5202 {
5203 Environment *env = i->at(j);
5204
5205 string resultPath;
5206 bool resultFound = env->findInModuleCache(moduleOrDependency, resultPath);
5207 if (resultFound)
5208 {
5209 found = true;
5210 outPath = resultPath;
5211 outBuiltin = builtin;
5212 }
5213 }
5214 }
5215 });
5216
5217 cachePath = outPath;
5218 isBuiltinCache = outBuiltin;
5219 return found;
5220}
5221
5230{
5231 dispatch_group_async(moduleCacheBuilding, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
5232 useModuleCache(true, false);
5233 });
5234}
5235
5241{
5242 return target;
5243}
5244
5250{
5251 return getTargetArch(target);
5252}
5253
5267void VuoCompiler::generateBuiltInModuleCaches(string vuoFrameworkPath, string target)
5268{
5269 vuoFrameworkInProgressPath = vuoFrameworkPath;
5270
5271 VuoCompiler compiler("", target);
5272 compiler.useModuleCache(false, true);
5273}
5274
5283{
5284 unsigned long maxSeconds = 30 * 24 * 60 * 60; // 30 days
5285
5286 set<VuoFileUtilities::File *> cacheDirs = VuoFileUtilities::findAllFilesInDirectory(VuoFileUtilities::getCachePath());
5287 for (set<VuoFileUtilities::File *>::iterator i = cacheDirs.begin(); i != cacheDirs.end(); ++i)
5288 {
5289 string path = (*i)->path();
5290
5291 string file = (*i)->basename();
5292 if (file != "Builtin" && file != "System" && file != "User")
5293 {
5294 unsigned long fileSeconds = VuoFileUtilities::getSecondsSinceFileLastAccessed(path);
5295 if (fileSeconds > maxSeconds)
5297 }
5298
5299 if (VuoStringUtilities::beginsWith(file, Environment::pidCacheDirPrefix))
5300 {
5301 string pidAsString = file.substr(Environment::pidCacheDirPrefix.length());
5302 int pid = atoi(pidAsString.c_str());
5303 if (kill(pid, 0) != 0) // no running process has this pid
5305 }
5306
5307 delete *i;
5308 }
5309}
5310
5315void VuoCompiler::setLoadAllModules(bool shouldLoadAllModules)
5316{
5317 dispatch_sync(modulesToLoadQueue, ^{
5318 this->shouldLoadAllModules = shouldLoadAllModules;
5319 });
5320}
5321
5334void VuoCompiler::link(string outputPath, const set<Module *> &modules, const set<string> &libraries, const set<string> &frameworks, bool isDylib, const vector<string> &rPaths, bool shouldAdHocCodeSign, VuoCompilerIssues *issues)
5335{
5336 VUserLog("Linking '%s' (%s)…", outputPath.c_str(), getTargetArch(target).c_str());
5337 // https://stackoverflow.com/questions/11657529/how-to-generate-an-executable-from-an-llvmmodule
5338
5339 bool ownsIssues = false;
5340 if (! issues)
5341 {
5342 issues = new VuoCompilerIssues;
5343 ownsIssues = true;
5344 }
5345
5346 // Write all the modules with renamed symbols to a composite module file (since the linker can't operate on in-memory modules).
5347 string compositeModulePath = VuoFileUtilities::makeTmpFile("composite", "bc");
5348 dispatch_sync(llvmQueue, ^{
5349 double t0 = VuoLogGetTime();
5350
5351 unique_ptr<Module> compositeModule(new Module("composite", *globalLLVMContext));
5352 Linker linker(*compositeModule);
5353 setTargetForModule(compositeModule.get(), target);
5354
5355 for (auto i : modules)
5356 {
5357 unique_ptr<Module> upi = llvm::CloneModule(i);
5358 if (linker.linkInModule(std::move(upi)))
5359 {
5360 VuoCompilerIssue issue(VuoCompilerIssue::IssueType::Error, "linking composite module", "",
5361 "", "Failed to link in the module with ID '" + i->getModuleIdentifier() + "'");
5362 issues->append(issue);
5363 }
5364 }
5365
5366 writeModuleToBitcode(compositeModule.get(), compositeModulePath);
5367
5368 VUserLog("\tLinkModules took %5.2fs", VuoLogGetTime() - t0);
5369 });
5370
5371
5372 // llvm-3.1/llvm/tools/clang/tools/driver/driver.cpp
5373
5374 // Invoke clang as `clang++` so it includes the C++ standard libraries.
5375 string clangPath(getClangPath() + "++");
5376
5377 vector<const char *> args;
5378 vector<char *> argsToFree;
5379 args.push_back(clangPath.c_str());
5380
5381 {
5382 char *outputPathZ = strdup(("-o" + outputPath).c_str());
5383 args.push_back(outputPathZ);
5384 argsToFree.push_back(outputPathZ);
5385 }
5386
5387 args.push_back(compositeModulePath.c_str());
5388
5389 vector<string> coreDependencies = getCoreVuoDependencies();
5390 for (set<string>::const_iterator i = libraries.begin(); i != libraries.end(); ++i)
5391 {
5392 string library = *i;
5393
5394 for (vector<string>::iterator j = coreDependencies.begin(); j != coreDependencies.end(); ++j)
5395 {
5396 string coreDependency = *j;
5397 if (VuoStringUtilities::endsWith(library, "lib" + coreDependency + ".a"))
5398 args.push_back("-force_load"); // Load all symbols of static core dependencies, not just those used in the objects.
5399 }
5400
5401 // Use the pre-built native object file if it exists (faster than converting .bc to .o every build).
5402 if (VuoStringUtilities::endsWith(library, ".bc"))
5403 {
5404 string libraryObject = VuoStringUtilities::substrBefore(library, ".bc") + ".o";
5405 if (VuoFileUtilities::fileExists(libraryObject))
5406 library = libraryObject;
5407 }
5408
5409 char *libraryZ = strdup(library.c_str());
5410 args.push_back(libraryZ);
5411 argsToFree.push_back(libraryZ);
5412 }
5413
5414 // Add framework search paths
5415 vector<string> frameworkArguments;
5416
5417 __block vector<string> frameworkSearchPaths;
5418 void (^envGetFrameworkSearchPaths)(Environment *) = ^void (Environment *env) {
5419 vector<string> result = env->getFrameworkSearchPaths();
5420 frameworkSearchPaths.insert(frameworkSearchPaths.end(), result.begin(), result.end());
5421 };
5422 applyToInstalledEnvironments(envGetFrameworkSearchPaths);
5423
5424 for (vector<string>::const_iterator i = frameworkSearchPaths.begin(); i != frameworkSearchPaths.end(); ++i)
5425 {
5426 string a = "-F"+*i;
5427 // 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.
5428 frameworkArguments.push_back(a);
5429 char *frameworkArgument = strdup(a.c_str());
5430 args.push_back(frameworkArgument);
5431 argsToFree.push_back(frameworkArgument);
5432 }
5433
5434 for (set<string>::const_iterator i = frameworks.begin(); i != frameworks.end(); ++i)
5435 {
5436 args.push_back("-framework");
5437
5438 string frameworkName = *i;
5439 frameworkName = frameworkName.substr(0, frameworkName.length() - string(".framework").length());
5440 char *frameworkNameZ = strdup(frameworkName.c_str());
5441 args.push_back(frameworkNameZ);
5442 argsToFree.push_back(frameworkNameZ);
5443 }
5444
5445 // When linking on a development workstation or Jenkins or an end-user system,
5446 // use the partial macOS SDK bundled in Vuo.framework, since it includes all the TBDs we need.
5447 string vuoFrameworkPath = getVuoFrameworkPath();
5448 string frameworkMacOSSDKFolder = vuoFrameworkPath + "/SDKs/MacOSX.sdk";
5449 if (!VuoFileUtilities::fileExists(frameworkMacOSSDKFolder))
5450 throw VuoException("Couldn't find the macOS SDK.");
5451
5452 args.push_back("-Xlinker");
5453 args.push_back("-syslibroot");
5454 args.push_back("-Xlinker");
5455 char *frameworkMacOSSDKFolderZ = strdup(frameworkMacOSSDKFolder.c_str());
5456 args.push_back(frameworkMacOSSDKFolderZ);
5457 argsToFree.push_back(frameworkMacOSSDKFolderZ);
5458
5459 args.push_back("-Xlinker");
5460 args.push_back("-platform_version");
5461 args.push_back("-Xlinker");
5462 args.push_back("macos");
5463 args.push_back("-Xlinker");
5464 char *deploymentTargetZ = strdup(MACOS_DEPLOYMENT_TARGET);
5465 args.push_back(deploymentTargetZ);
5466 argsToFree.push_back(deploymentTargetZ);
5467 args.push_back("-Xlinker");
5468 char *sdkVersionZ = strdup(MACOS_SDK_VERSION);
5469 args.push_back(sdkVersionZ);
5470 argsToFree.push_back(sdkVersionZ);
5471
5472 // Linker option necessary for compatibility with our bundled version of ld64:
5473 args.push_back("-Xlinker");
5474 args.push_back("--no-demangle");
5475
5476 if (isVerbose)
5477 args.push_back("-v");
5478
5479 if (isDylib)
5480 args.push_back("-dynamiclib");
5481
5482 args.push_back("-Xlinker");
5483 args.push_back("-headerpad_max_install_names");
5484
5485 // Tell the built dylib/executable where to find dylibs that it depends on
5486 for (const string &rPath : rPaths)
5487 {
5488 args.push_back("-rpath");
5489 args.push_back(rPath.c_str());
5490 }
5491
5492#ifdef COVERAGE
5493 args.push_back("-rpath");
5494 args.push_back(LLVM_ROOT "/lib");
5495#endif
5496
5497 args.push_back("-target");
5498 args.push_back(target.c_str());
5499
5500 args.push_back("-std=c++14");
5501 args.push_back("-stdlib=libc++");
5502 args.push_back("-mmacosx-version-min=10.10");
5503
5504 // Allow clang to print meaningful error messages.
5505 auto diagnosticConsumer = new VuoCompilerDiagnosticConsumer(issues);
5506 clang::DiagnosticOptions *diagOptions = new clang::DiagnosticOptions();
5507 IntrusiveRefCntPtr<clang::DiagnosticIDs> DiagID(new clang::DiagnosticIDs());
5508 clang::DiagnosticsEngine Diags(DiagID, diagOptions, diagnosticConsumer);
5509
5510 if (isVerbose)
5511 {
5512 ostringstream s;
5513 for (vector<const char *>::iterator i = args.begin(); i != args.end(); ++i)
5514 s << *i << " ";
5515 VUserLog("\t%s", s.str().c_str());
5516 }
5517
5518 // Redirect linker output to a file, so we can feed it through VuoLog.
5519 string stdoutFile = VuoFileUtilities::makeTmpFile("vuo-linker-output", "txt");
5520 const StringRef stdoutPath(stdoutFile);
5521 const StringRef *redirects[] = {
5522 nullptr, // stdin
5523 &stdoutPath, // stdout
5524 &stdoutPath, // stderr
5525 };
5526
5527 // ExecuteAndWait's args needs to be null-terminated.
5528 const char **argsz = (const char **)malloc(sizeof(char *) * args.size() + 1);
5529 for (int i = 0; i < args.size(); ++i)
5530 argsz[i] = args[i];
5531 argsz[args.size()] = nullptr;
5532
5533 string errMsg;
5534 bool executionFailed;
5535 double t0 = VuoLogGetTime();
5536 int ret = llvm::sys::ExecuteAndWait(args[0], argsz, nullptr, redirects, 0, 0, &errMsg, &executionFailed);
5537
5538 for (auto i : argsToFree)
5539 free(i);
5540
5541 // Clean up composite module file.
5542 remove(compositeModulePath.c_str());
5543
5544 if (!isDylib)
5545 // Ensure the linked binary has the execute permission set
5546 // (ld64-242 doesn't reliably set it, whereas ld64-133.3 did).
5547 // https://b33p.net/kosada/node/14152
5548 chmod(outputPath.c_str(), 0755);
5549
5550 if (ret != 0)
5551 {
5552 string details;
5553 if (!errMsg.empty())
5554 {
5555 VUserLog("%s", errMsg.c_str());
5556 details += "\n" + errMsg + "\n";
5557 }
5558 string stdoutFileContents = VuoFileUtilities::readFileToString(stdoutFile);
5559 if (!stdoutFileContents.empty())
5560 {
5561 VUserLog("%s", stdoutFileContents.c_str());
5562 details += "\n" + stdoutFileContents + "\n";
5563 }
5564 VuoFileUtilities::deleteFile(stdoutFile);
5565
5566 VuoCompilerIssue issue(VuoCompilerIssue::Error, "linking composition", outputPath, "", details);
5567 issues->append(issue);
5568 throw VuoCompilerException(issues, ownsIssues);
5569 }
5570
5571 VuoFileUtilities::deleteFile(stdoutFile);
5572 VUserLog("\tLinking took %5.2fs", VuoLogGetTime() - t0);
5573
5574 if (shouldAdHocCodeSign)
5575 adHocCodeSign(outputPath);
5576}
5577
5581void VuoCompiler::adHocCodeSign(string path)
5582{
5584#if VUO_PRO
5585 "CODESIGN_ALLOCATE=" + getCodesignAllocatePath(),
5586#endif
5587 });
5588}
5589
5595Module *VuoCompiler::readModuleFromC(string inputPath, const vector<string> &headerSearchPaths, const vector<string> &extraArgs, VuoCompilerIssues *issues)
5596{
5597 // llvm-3.1/llvm/tools/clang/examples/clang-interpreter/main.cpp
5598
5599 vector<const char *> args;
5600 vector<char *> argsToFree;
5601
5602 args.push_back(inputPath.c_str());
5603 args.push_back("-DVUO_COMPILER");
5604 args.push_back("-fblocks");
5605
5606 // Provide full backtraces, for easier debugging using Instruments and `VuoLog_backtrace()`.
5607 // The Clang driver translates `-fno-omit-frame-pointer` to `clang -cc1`'s `-mdisable-fp-elim`.
5608 // In Clang 10, this was renamed to `-mframe-pointer=all`.
5609 // https://b33p.net/kosada/vuo/vuo/-/issues/19064
5610 args.push_back("-mdisable-fp-elim");
5611
5612 // Sync with /CMakeLists.txt's `commonFlags`.
5613 args.push_back("-Wall");
5614 args.push_back("-Wextra");
5615 args.push_back("-Wimplicit-fallthrough");
5616 args.push_back("-Wno-unused-parameter");
5617 args.push_back("-Wno-sign-compare");
5618 args.push_back("-Werror=implicit");
5619
5620 if (VuoStringUtilities::endsWith(inputPath, ".cc"))
5621 {
5622 args.push_back("-std=c++14");
5623 args.push_back("-stdlib=libc++");
5624 args.push_back("-fexceptions");
5625 args.push_back("-fcxx-exceptions");
5626 }
5627
5628 for (vector<string>::const_iterator i = headerSearchPaths.begin(); i != headerSearchPaths.end(); ++i)
5629 {
5630 args.push_back("-I");
5631 args.push_back(i->c_str());
5632 }
5633
5634#if VUO_PRO
5635 string dependencyOutputPath = _dependencyOutput();
5636 if (!dependencyOutputPath.empty())
5637 {
5638 char *outputObjectPath = strdup(dependencyOutputPath.substr(0, dependencyOutputPath.length() - 2).c_str());
5639 argsToFree.push_back(outputObjectPath);
5640
5641 // https://bugzilla.mozilla.org/show_bug.cgi?id=1340588#c4
5642 // https://lists.llvm.org/pipermail/cfe-users/2018-March/001268.html
5643 args.push_back("-MT");
5644 args.push_back(outputObjectPath);
5645 args.push_back("-dependency-file");
5646 args.push_back(dependencyOutputPath.c_str());
5647 }
5648#endif
5649
5650 if (isVerbose)
5651 args.push_back("-v");
5652
5653 for (vector<string>::const_iterator i = extraArgs.begin(); i != extraArgs.end(); ++i)
5654 args.push_back(i->c_str());
5655
5656 auto diagnosticConsumer = new VuoCompilerDiagnosticConsumer(issues);
5657 clang::DiagnosticOptions * diagOptions = new clang::DiagnosticOptions();
5658 IntrusiveRefCntPtr<clang::DiagnosticIDs> DiagID(new clang::DiagnosticIDs());
5659 clang::DiagnosticsEngine *diags = new clang::DiagnosticsEngine(DiagID, diagOptions, diagnosticConsumer);
5660
5661 // This invokes `clang -cc1`. See `clang -cc1 --help` for available options.
5662 shared_ptr<clang::CompilerInvocation> compilerInvocation(new clang::CompilerInvocation);
5663 clang::CompilerInvocation::CreateFromArgs(*compilerInvocation, &args[0], &args[0] + args.size(), *diags);
5664 compilerInvocation->TargetOpts->Triple = target;
5665
5666 clang::CompilerInstance Clang;
5667 Clang.setInvocation(compilerInvocation);
5668
5669 Clang.setDiagnostics(diags);
5670 if (!Clang.hasDiagnostics())
5671 return NULL;
5672
5673 // See CompilerInvocation::GetResourcesPath -- though we're not calling it because we don't have MainAddr.
5674 string builtinHeaderSearchPath;
5675 string vuoFrameworkPath = getVuoFrameworkPath();
5676 if (vuoFrameworkPath.empty())
5677 {
5678 builtinHeaderSearchPath = getClangPath();
5679 if (VuoStringUtilities::endsWith(builtinHeaderSearchPath, "Helpers/clang"))
5680 builtinHeaderSearchPath = VuoStringUtilities::substrBefore(builtinHeaderSearchPath, "Helpers/clang");
5681 else if (VuoStringUtilities::endsWith(builtinHeaderSearchPath, "bin/clang"))
5682 builtinHeaderSearchPath = VuoStringUtilities::substrBefore(builtinHeaderSearchPath, "bin/clang");
5683 builtinHeaderSearchPath += "lib/clang/" CLANG_VERSION_STRING;
5684 }
5685 else
5686 builtinHeaderSearchPath = vuoFrameworkPath + "/Frameworks/llvm.framework/Versions/A/lib/clang/" CLANG_VERSION_STRING;
5687 Clang.getHeaderSearchOpts().ResourceDir = builtinHeaderSearchPath;
5688
5689// OwningPtr<clang::CodeGenAction> Act(new clang::EmitLLVMOnlyAction()); // @@@ return value of takeModule() is destroyed at the end of this function
5690 clang::CodeGenAction *Act = new clang::EmitLLVMOnlyAction();
5691 if (!Clang.ExecuteAction(*Act))
5692 return NULL;
5693
5694 for (auto i : argsToFree)
5695 free(i);
5696
5697 unique_ptr<Module> module = Act->takeModule();
5698 if (!module)
5699 VUserLog("Error compiling %s: module is null.", inputPath.c_str());
5700 return module.release();
5701}
5702
5708Module *VuoCompiler::readModuleFromBitcode(string inputPath, string arch)
5709{
5710 string dir, file, ext;
5711 VuoFileUtilities::splitPath(inputPath, dir, file, ext);
5712 VuoFileUtilities::File inputFile(dir, file + "." + ext);
5713 return readModuleFromBitcode(&inputFile, arch);
5714}
5715
5723Module *VuoCompiler::readModuleFromBitcode(VuoFileUtilities::File *inputFile, string arch)
5724{
5725 size_t inputDataBytes;
5726 char *inputData = inputFile->getContentsAsRawData(inputDataBytes);
5727
5728 string error;
5729 set<string> availableArchs;
5730 VuoLog_status("Loading module \"%s\" (%s)", inputFile->getRelativePath().c_str(), arch.c_str());
5731 Module *module = readModuleFromBitcodeData(inputData, inputDataBytes, arch, availableArchs, error);
5732 VuoLog_status(NULL);
5733 if (! module)
5734 VUserLog("Error: Couldn't parse module '%s' (%s): %s.", inputFile->getRelativePath().c_str(), arch.c_str(), error.c_str());
5735
5736 free(inputData);
5737
5738 return module;
5739}
5740
5746Module *VuoCompiler::readModuleFromBitcodeData(char *inputData, size_t inputDataBytes, string arch,
5747 set<string> &availableArchs, string &error)
5748{
5749 if (inputDataBytes < sizeof(unsigned int))
5750 return nullptr;
5751
5752 __block Module *module = nullptr;
5753 dispatch_sync(llvmQueue, ^{
5754 StringRef inputDataAsStringRef(inputData, inputDataBytes);
5755 auto mb = MemoryBuffer::getMemBuffer(inputDataAsStringRef, "", false);
5756 if (!mb)
5757 {
5758 error = "Couldn't create MemoryBuffer";
5759 return;
5760 }
5761
5762 MemoryBufferRef bitcodeBuffer;
5763 string moduleArch;
5764 unsigned int fileID = *(unsigned int *)inputData;
5765 if (fileID == 0x0b17c0de)
5766 // This is a single-architecture LLVM bitcode `.bc` file, so read the entire file.
5767 bitcodeBuffer = mb.get()->getMemBufferRef();
5768
5769 else if (fileID == 0xdec04342)
5770 {
5771 // This is a single-architecture raw bitcode file, presumably generated by Vuo 2.2.1 or earlier.
5772 bitcodeBuffer = mb.get()->getMemBufferRef();
5773 moduleArch = "x86_64";
5774 availableArchs.insert(moduleArch);
5775 }
5776
5777 else if (fileID == 0xbebafeca)
5778 {
5779 if (arch.empty())
5780 {
5781 error = "It's a Mach-O universal binary, but this compiler instance's LLVM target isn't set";
5782 return;
5783 }
5784
5785 // This is a Mach-O wrapper around multiple LLVM bitcode files;
5786 // parse the Mach-O header to extract just a single architecture.
5787 auto binary = llvm::object::MachOUniversalBinary::create(mb.get()->getMemBufferRef());
5788 if (!binary)
5789 {
5790 error = "Couldn't read Mach-O universal binary:";
5791 handleAllErrors(binary.takeError(), [&error](const ErrorInfoBase &ei) {
5792 error += " " + ei.message();
5793 });
5794 return;
5795 }
5796
5797 for (auto &o : binary.get()->objects())
5798 {
5799 if (o.getArchFlagName() == arch)
5800 bitcodeBuffer = MemoryBufferRef(mb.get()->getMemBufferRef().getBuffer().slice(o.getOffset(), o.getOffset() + o.getSize()), "");
5801
5802 availableArchs.insert(o.getArchFlagName());
5803 }
5804
5805 if (!bitcodeBuffer.getBufferSize())
5806 {
5807 error = "The Mach-O universal binary doesn't have an \"" + arch + "\" slice";
5808 return;
5809 }
5810 }
5811
5812 auto wrappedModule = llvm::parseBitcodeFile(bitcodeBuffer, *globalLLVMContext);
5813 if (!wrappedModule)
5814 {
5815 error = "Couldn't parse bitcode file:";
5816 handleAllErrors(wrappedModule.takeError(), [&error](const ErrorInfoBase &ei) {
5817 error += " " + ei.message();
5818 });
5819 return;
5820 }
5821
5822 module = wrappedModule.get().release();
5823
5824 if (moduleArch.empty())
5825 moduleArch = getTargetArch(module->getTargetTriple());
5826
5827 if (availableArchs.empty())
5828 availableArchs.insert(moduleArch);
5829
5830 if (moduleArch != arch)
5831 {
5832 error = "The module's CPU architecture \"" + moduleArch + "\" doesn't match the compiler's CPU architecture \"" + arch + "\"";
5833 delete module;
5834 module = nullptr;
5835 return;
5836 }
5837 });
5838 return module;
5839}
5840
5848bool VuoCompiler::writeModuleToBitcode(Module *module, string outputPath)
5849{
5850 string str;
5851 raw_string_ostream verifyOut(str);
5852 if (llvm::verifyModule(*module, &verifyOut))
5853 {
5854 VUserLog("Error: Module verification failed:\n%s", verifyOut.str().c_str());
5855 return true;
5856 }
5857
5858 // Ensure the module gets output in the bitcode wrapper format instead of raw bitcode.
5859 if (module->getTargetTriple().empty())
5860 setTargetForModule(module, getProcessTarget());
5861
5862 std::error_code err;
5863 raw_fd_ostream out(outputPath.c_str(), err, sys::fs::F_None);
5864 if (err)
5865 {
5866 VUserLog("Error: Couldn't open file '%s' for writing: %s", outputPath.c_str(), err.message().c_str());
5867 return true;
5868 }
5869 llvm::WriteBitcodeToFile(module, out);
5870
5871 return false;
5872}
5873
5880void VuoCompiler::setTargetForModule(Module *module, string targetTriple)
5881{
5882 // Replace the OS version in targetTriple to avoid warnings about linking modules of different target triples.
5883 llvm::Triple triple(targetTriple);
5884 if (triple.isMacOSX())
5885 triple.setOSName("macosx10.10.0");
5886
5887 module->setTargetTriple(triple.str());
5888
5889 string error;
5890 auto target = TargetRegistry::lookupTarget(module->getTargetTriple(), error);
5891 if (!target)
5892 {
5893 VUserLog("Error: Couldn't look up target: %s", error.c_str());
5894 return;
5895 }
5896
5897 auto targetMachine = target->createTargetMachine(module->getTargetTriple(), "", "", TargetOptions(), Optional<Reloc::Model>());
5898 if (!targetMachine)
5899 {
5900 VUserLog("Error: Couldn't create targetMachine.");
5901 return;
5902 }
5903
5904 module->setDataLayout(targetMachine->createDataLayout());
5905
5906 delete targetMachine;
5907}
5908
5913string VuoCompiler::getTargetArch(string target)
5914{
5915 auto hyphen = target.find('-');
5916 if (hyphen == string::npos)
5917 return "";
5918
5919 return target.substr(0, hyphen);
5920}
5921
5925string VuoCompiler::getProcessTarget(void)
5926{
5927 // llvm::sys::getProcessTriple() returns `LLVM_HOST_TRIPLE`,
5928 // which is always `x86_64-*` since we built LLVM on x86_64.
5929 // Instead, use llvm::sys::getDefaultTargetTriple()
5930 // which _actually_ returns the current process's target.
5931 llvm::Triple triple(llvm::sys::getDefaultTargetTriple());
5932
5933 // Replace darwin with macosx, e.g. x86_64-apple-darwin18.7.0 -> x86_64-apple-macosx10.14.6
5934 if (triple.isMacOSX())
5935 {
5936 unsigned int major, minor, micro;
5937 if (triple.getMacOSXVersion(major, minor, micro))
5938 {
5939 ostringstream osVersion;
5940 osVersion << "macosx" << major << "." << minor << "." << micro;
5941 triple.setOSName(osVersion.str());
5942 }
5943 }
5944
5945 return triple.str();
5946}
5947
5955{
5956 Module *llvmModule = module->getModule();
5957
5958 // In C++ the return value of a cast may not be the same pointer as the cast arg.
5959 // Because of this, VuoModule::getPseudoBase() returns a different value than VuoNodeClass::getBase().
5960 // Calling delete on VuoModule::getPseudoBase() gives a malloc error: "pointer being freed was not allocated".
5961 // So call it on VuoNodeClass::getBase(). Same idea for VuoType.
5962
5963 VuoNodeClass *baseNodeClass = NULL;
5964 VuoType *baseType = NULL;
5965 VuoModule *baseModule = NULL;
5966 VuoCompilerNodeClass *nodeClass = dynamic_cast<VuoCompilerNodeClass *>(module);
5967 if (nodeClass)
5968 {
5969 baseNodeClass = dynamic_cast<VuoNodeClass *>(nodeClass->getBase());
5970
5972 if (dynamic_cast<VuoCompilerSpecializedNodeClass *>(module))
5973 module = NULL;
5974 }
5975 else
5976 {
5977 VuoCompilerType *type = dynamic_cast<VuoCompilerType *>(module);
5978 if (type)
5979 baseType = dynamic_cast<VuoType *>(type->getBase());
5980 else
5981 baseModule = module->getPseudoBase();
5982 }
5983
5984 delete module;
5985 delete baseNodeClass;
5986 delete baseType;
5987 delete baseModule;
5988 destroyLlvmModule(llvmModule);
5989}
5990
5998{
5999 dispatch_sync(llvmQueue, ^{
6000 delete module;
6001 });
6002}
6003
6015VuoNode * VuoCompiler::createNode(VuoCompilerNodeClass *nodeClass, string title, double x, double y)
6016{
6018 if (nodeClassForNode)
6019 return nodeClassForNode->newNode(title, x, y);
6020
6021 return nullptr;
6022}
6023
6030VuoNode * VuoCompiler::createNode(VuoCompilerNodeClass *nodeClass, VuoNode *nodeToCopyMetadataFrom)
6031{
6033 if (nodeClassForNode)
6034 return nodeClassForNode->newNode(nodeToCopyMetadataFrom);
6035
6036 return nullptr;
6037}
6038
6042VuoNode * VuoCompiler::createPublishedInputNode(vector<VuoPublishedPort *> publishedInputPorts)
6043{
6044 string nodeClassName = VuoCompilerPublishedInputNodeClass::buildNodeClassName(publishedInputPorts);
6045 return createPublishedNode(nodeClassName, publishedInputPorts);
6046}
6047
6051VuoNode * VuoCompiler::createPublishedOutputNode(vector<VuoPublishedPort *> publishedOutputPorts)
6052{
6053 string nodeClassName = VuoCompilerPublishedOutputNodeClass::buildNodeClassName(publishedOutputPorts);
6054 return createPublishedNode(nodeClassName, publishedOutputPorts);
6055}
6056
6060VuoNode * VuoCompiler::createPublishedNode(const string &nodeClassName, const vector<VuoPublishedPort *> &publishedPorts)
6061{
6062 VuoCompilerNodeClass *nodeClass = getNodeClass(nodeClassName);
6063 VuoNode *node = createNode(nodeClass);
6064
6065 // Set the generic port types on the node to match those on the published ports set by VuoCompilerComposition::updateGenericPortTypes().
6066 VuoCompilerPublishedInputNodeClass *inputNodeClass = dynamic_cast<VuoCompilerPublishedInputNodeClass *>(nodeClass);
6067 VuoCompilerPublishedOutputNodeClass *outputNodeClass = dynamic_cast<VuoCompilerPublishedOutputNodeClass *>(nodeClass);
6068 for (size_t i = 0; i < publishedPorts.size(); ++i)
6069 {
6070 VuoType *publishedPortType = static_cast<VuoCompilerPort *>(publishedPorts[i]->getCompiler())->getDataVuoType();
6071 if (! dynamic_cast<VuoGenericType *>(publishedPortType))
6072 continue;
6073
6074 set<VuoPort *> nodePorts;
6075 if (inputNodeClass)
6076 {
6077 VuoPort *inputPort = node->getInputPorts().at( inputNodeClass->getInputPortIndexForPublishedInputPort(i) );
6078 nodePorts.insert(inputPort);
6079
6080 VuoPort *outputPort = node->getOutputPorts().at( inputNodeClass->getOutputPortIndexForPublishedInputPort(i) );
6081 nodePorts.insert(outputPort);
6082 }
6083 else
6084 {
6085 VuoPort *inputPort = node->getInputPorts().at( outputNodeClass->getInputPortIndexForPublishedOutputPort(i) );
6086 nodePorts.insert(inputPort);
6087 }
6088
6089 for (VuoPort *port : nodePorts)
6090 {
6091 VuoCompilerPort *compilerPort = static_cast<VuoCompilerPort *>(port->getCompiler());
6092 compilerPort->setDataVuoType(publishedPortType);
6093 }
6094 }
6095
6096 reifyGenericPortTypes(node);
6097
6098 return node;
6099}
6100
6106{
6107 dispatch_sync(environmentQueue, ^{
6108 if (environments.size() >= 5)
6109 environments.at(3).at(0)->addExpatriateSourceFile(sourcePath);
6110 });
6111}
6112
6118{
6119 dispatch_sync(environmentQueue, ^{
6120 if (environments.size() >= 5)
6121 {
6122 environments.at(3).at(0)->removeExpatriateSourceFile(sourcePath);
6123
6124 set<string> sourcesRemoved;
6125 sourcesRemoved.insert(getModuleKeyForPath(sourcePath));
6126 loadModulesAndSources(set<string>(), set<string>(), set<string>(), set<string>(), set<string>(), sourcesRemoved,
6127 false, false, environments.at(3).at(0), nullptr, nullptr, "");
6128 }
6129 });
6130}
6131
6148void VuoCompiler::overrideInstalledNodeClass(const string &sourcePath, const string &sourceCode)
6149{
6150 string sourcePathCopy = sourcePath;
6151 string sourceCodeCopy = sourceCode;
6152
6153 dispatch_async(environmentQueue, ^{
6154 string nodeClassName = getModuleKeyForPath(sourcePathCopy);
6155 ModuleInfo *sourceInfo = NULL;
6156
6157 for (const vector<Environment *> &envs : environments)
6158 {
6159 for (Environment *env : envs)
6160 {
6161 ModuleInfo *potentialSourceInfo = env->listSourceFile(nodeClassName);
6162 if (potentialSourceInfo && VuoFileUtilities::arePathsEqual(potentialSourceInfo->getFile()->path(), sourcePathCopy))
6163 {
6164 sourceInfo = potentialSourceInfo;
6165 break;
6166 }
6167 }
6168 }
6169
6170 if (! sourceInfo)
6171 return;
6172
6173 bool shouldRecompileSourcesIfUnchanged;
6174 if (! sourceCodeCopy.empty())
6175 {
6176 sourceInfo->setSourceCode(sourceCodeCopy);
6177 sourceInfo->setSourceCodeOverridden(true);
6178
6179 shouldRecompileSourcesIfUnchanged = false;
6180 }
6181 else
6182 {
6183 sourceInfo->revertSourceCode();
6184 sourceInfo->setSourceCodeOverridden(false);
6185
6186 shouldRecompileSourcesIfUnchanged = true;
6187 }
6188 sourceInfo->setAttempted(false);
6189 sourceInfo->setLastModifiedToNow();
6190
6191 set<string> sourcesModified;
6192 sourcesModified.insert(nodeClassName);
6193
6194 loadModulesAndSources(set<string>(), set<string>(), set<string>(), set<string>(), sourcesModified, set<string>(),
6195 false, shouldRecompileSourcesIfUnchanged, nullptr, nullptr, nullptr, "");
6196 });
6197}
6198
6208void VuoCompiler::revertOverriddenNodeClass(const string &sourcePath)
6209{
6210 overrideInstalledNodeClass(sourcePath, "");
6211}
6212
6226{
6227 // For performance, don't bother searching if it's obviously not a node class.
6228
6229 if (VuoStringUtilities::endsWith(nodeClassName, ".framework"))
6230 return NULL;
6231
6232 // Attempt to load the node class (if it hasn't already been loaded).
6233
6234 set<string> nodeClassNameSet;
6235 nodeClassNameSet.insert(nodeClassName);
6236 loadModulesIfNeeded(nodeClassNameSet);
6237
6238 // If the node class has been loaded, return it.
6239
6240 __block VuoCompilerNodeClass *nodeClass = NULL;
6241 void (^envGetNodeClass)(Environment *) = ^void (Environment *env) {
6242 VuoCompilerNodeClass *result = env->getNodeClass(nodeClassName);
6243 if (result)
6244 nodeClass = result;
6245 };
6246 applyToAllEnvironments(envGetNodeClass);
6247 return nodeClass;
6248}
6249
6255map<string, VuoCompilerNodeClass *> VuoCompiler::getNodeClasses()
6256{
6257 loadModulesIfNeeded();
6258
6259 __block map<string, VuoCompilerNodeClass *> nodeClasses;
6260 void (^envGetNodeClasses)(Environment *) = ^void (Environment *env) {
6261 map<string, VuoCompilerNodeClass *> result = env->getNodeClasses();
6262 nodeClasses.insert(result.begin(), result.end());
6263 };
6264 applyToInstalledEnvironments(envGetNodeClasses);
6265 return nodeClasses;
6266}
6267
6273VuoCompilerType * VuoCompiler::getType(const string &typeName)
6274{
6275 set<string> typeNameSet;
6276 typeNameSet.insert(typeName);
6277 loadModulesIfNeeded(typeNameSet);
6278
6279 __block VuoCompilerType *type = NULL;
6280 void (^envGetType)(Environment *) = ^void (Environment *env) {
6281 VuoCompilerType *result = env->getType(typeName);
6282 if (result)
6283 type = result;
6284 };
6285 applyToInstalledEnvironments(envGetType);
6286
6287 if (! type && VuoGenericType::isGenericTypeName(typeName))
6288 {
6289 VuoCompilerType * (^compilerGetType)(string) = ^VuoCompilerType * (string moduleKey) {
6290 return getType(moduleKey);
6291 };
6292
6293 VuoGenericType *genericType = new VuoGenericType(typeName, vector<string>());
6294 type = VuoCompilerGenericType::newGenericType(genericType, compilerGetType);
6295 }
6296
6297 return type;
6298}
6299
6305map<string, VuoCompilerType *> VuoCompiler::getTypes()
6306{
6307 loadModulesIfNeeded();
6308
6309 __block map<string, VuoCompilerType *> types;
6310 void (^envGetTypes)(Environment *) = ^void (Environment *env) {
6311 map<string, VuoCompilerType *> result = env->getTypes();
6312 types.insert(result.begin(), result.end());
6313 };
6314 applyToInstalledEnvironments(envGetTypes);
6315 return types;
6316}
6317
6323VuoCompilerModule *VuoCompiler::getLibraryModule(const string &libraryModuleName)
6324{
6325 set<string> libraryNameSet;
6326 libraryNameSet.insert(libraryModuleName);
6327 loadModulesIfNeeded(libraryNameSet);
6328
6329 __block VuoCompilerModule *module = nullptr;
6330 void (^envGetLibraryModule)(Environment *) = ^void (Environment *env) {
6331 VuoCompilerModule *result = env->getLibraryModule(libraryModuleName);
6332 if (result)
6333 module = result;
6334 };
6335 applyToInstalledEnvironments(envGetLibraryModule);
6336
6337 return module;
6338}
6339
6345map<string, VuoCompilerModule *> VuoCompiler::getLibraryModules()
6346{
6347 loadModulesIfNeeded();
6348
6349 __block map<string, VuoCompilerModule *> libraryModules;
6350 void (^envGetLibraryModules)(Environment *) = ^void (Environment *env) {
6351 map<string, VuoCompilerModule *> result = env->getLibraryModules();
6352 libraryModules.insert(result.begin(), result.end());
6353 };
6354 applyToInstalledEnvironments(envGetLibraryModules);
6355 return libraryModules;
6356}
6357
6363map<string, VuoNodeSet *> VuoCompiler::getNodeSets()
6364{
6365 loadModulesIfNeeded();
6366
6367 __block map<string, VuoNodeSet *> nodeSets;
6368 void (^envGetNodeSets)(Environment *) = ^void (Environment *env) {
6369 map<string, VuoNodeSet *> result = env->getNodeSets();
6370 nodeSets.insert(result.begin(), result.end());
6371 };
6372 applyToInstalledEnvironments(envGetNodeSets);
6373 return nodeSets;
6374}
6375
6382{
6383 loadModulesIfNeeded();
6384
6385 __block VuoNodeSet *nodeSet = NULL;
6386 void (^envFindNodeSet)(Environment *) = ^void (Environment *env) {
6387 VuoNodeSet *result = env->findNodeSet(name);
6388 if (result)
6389 nodeSet = result;
6390 };
6391 applyToInstalledEnvironments(envFindNodeSet);
6392 return nodeSet;
6393}
6394
6398VuoCompilerModule * VuoCompiler::getModule(const string &moduleKey)
6399{
6400 __block VuoCompilerModule *module = NULL;
6401 void (^envFindModule)(Environment *) = ^void (Environment *env) {
6402 VuoCompilerModule *result = env->findModule(moduleKey);
6403 if (result)
6404 module = result;
6405 };
6406 applyToAllEnvironments(envFindModule);
6407 return module;
6408}
6409
6421void VuoCompiler::listNodeClasses(const string &format)
6422{
6423 map<string, VuoCompilerNodeClass *> nodeClasses = getNodeClasses();
6424 for (map<string, VuoCompilerNodeClass *>::const_iterator i = nodeClasses.begin(); i != nodeClasses.end(); ++i)
6425 {
6426 VuoCompilerNodeClass *nodeClass = i->second;
6427 if (format == "")
6428 {
6429 printf("%s\n", nodeClass->getBase()->getClassName().c_str());
6430 }
6431 else if (format == "path")
6432 {
6433 // TODO: If "path", prints the absolute path of each node class, one per line.
6434 }
6435 else if (format == "dot")
6436 {
6437 VuoCompilerNode *node = nodeClass->newNode()->getCompiler();
6438
6439 printf("%s\n", nodeClass->getDoxygenDocumentation().c_str());
6440 printf("%s\n\n", node->getGraphvizDeclaration().c_str());
6441
6442 delete node;
6443 }
6444 }
6445}
6446
6457vector<string> VuoCompiler::getRunPathSearchPaths(Environment *narrowestScope)
6458{
6459 vector<string> rPaths;
6460 for (size_t i = 0; i < environments.size(); ++i)
6461 {
6462 if (i == 0)
6463 {
6464 string vuoFrameworkPath = getVuoFrameworkPath();
6465 if (! vuoFrameworkPath.empty())
6466 rPaths.push_back(vuoFrameworkPath + "/..");
6467 }
6468 else if (i == 1)
6469 rPaths.push_back(VuoFileUtilities::getSystemModulesPath());
6470 else if (i == 2)
6471 rPaths.push_back(VuoFileUtilities::getUserModulesPath());
6472 else if (i == 3)
6473 {
6474 string localModulesPath = getCompositionLocalModulesPath();
6475 if (! localModulesPath.empty())
6476 rPaths.push_back(localModulesPath);
6477 }
6478
6479 if (std::find(environments[i].begin(), environments[i].end(), narrowestScope) != environments[i].end())
6480 break;
6481 }
6482
6483 std::reverse(rPaths.begin(), rPaths.end());
6484 return rPaths;
6485}
6486
6493vector<string> VuoCompiler::getCoreVuoDependencies(void)
6494{
6495 vector<string> dependencies;
6496
6497 dependencies.push_back("VuoHeap");
6498 dependencies.push_back("VuoApp");
6499
6500 dependencies.push_back("zmq");
6501 dependencies.push_back("json-c");
6502 dependencies.push_back("objc");
6503 dependencies.push_back("c");
6504 dependencies.push_back("AppKit.framework");
6505
6506#ifdef COVERAGE
6507 dependencies.push_back(LLVM_ROOT "/lib/libprofile_rt.dylib");
6508#endif
6509 return dependencies;
6510}
6511
6515string VuoCompiler::getRuntimeMainDependency(void)
6516{
6517 return "VuoRuntimeMain.o";
6518}
6519
6527string VuoCompiler::getRuntimeDependency(void)
6528{
6529 return "VuoRuntime.o";
6530}
6531
6539{
6540 if (! vuoFrameworkInProgressPath.empty())
6541 return vuoFrameworkInProgressPath;
6542
6544}
6545
6549string VuoCompiler::getClangPath(void)
6550{
6551 return clangPath;
6552}
6553
6558{
6559 // Start with the file name without extension.
6560 string dir, moduleKey, ext;
6561 VuoFileUtilities::splitPath(path, dir, moduleKey, ext);
6562
6564 {
6565 vector<string> nodeClassNameParts = VuoStringUtilities::split(moduleKey, '.');
6566
6567 // Remove repeated file extensions.
6568 while (nodeClassNameParts.size() > 1 && nodeClassNameParts.back() == ext)
6569 nodeClassNameParts.pop_back();
6570
6571 // Convert each part to lowerCamelCase.
6572 for (string &part : nodeClassNameParts)
6573 part = VuoStringUtilities::convertToCamelCase(part, false, true, false);
6574
6575 // If the node class name has only one part, add a prefix.
6576 if (nodeClassNameParts.size() == 1)
6577 nodeClassNameParts.insert(nodeClassNameParts.begin(), "isf");
6578
6579 moduleKey = VuoStringUtilities::join(nodeClassNameParts, '.');
6580 }
6581
6582 return moduleKey;
6583}
6584
6590{
6591 __block bool isLocal = false;
6592
6593 dispatch_sync(environmentQueue, ^{
6594 if (environments.size() >= 5)
6595 isLocal = environments.at(3).at(0)->findModule(moduleKey);
6596 });
6597
6598 return isLocal;
6599}
6600
6608{
6609 return lastCompositionBaseDir.empty() ? "" : lastCompositionBaseDir + "/Modules";
6610}
6611
6623{
6624 return lastCompositionBaseDir;
6625}
6626
6630void VuoCompiler::addModuleSearchPath(string path)
6631{
6632 dispatch_sync(environmentQueue, ^{
6633 environments.back().at(0)->addModuleSearchPath(path);
6634 environments.back().at(0)->addLibrarySearchPath(path);
6635 });
6636}
6637
6641void VuoCompiler::addHeaderSearchPath(const string &path)
6642{
6643 dispatch_sync(environmentQueue, ^{
6644 environments.back().at(0)->addHeaderSearchPath(path);
6645 });
6646}
6647
6652{
6653 dispatch_sync(environmentQueue, ^{
6654 environments.back().at(0)->addLibrarySearchPath(path);
6655 });
6656}
6657
6662{
6663 dispatch_sync(environmentQueue, ^{
6664 environments.back().at(0)->addFrameworkSearchPath(path);
6665 });
6666}
6667
6671void VuoCompiler::setVerbose(bool isVerbose)
6672{
6673 this->isVerbose = isVerbose;
6674}
6675
6681{
6682#if VUO_PRO
6683 if (VuoPro::getProAccess())
6684 {
6685 _shouldShowSplashWindow = false;
6686 return;
6687 }
6688#endif
6689
6690 _shouldShowSplashWindow = potentiallyShow;
6691}
6692
6698void VuoCompiler::setDependencyOutput(const string &path)
6699{
6700#if VUO_PRO
6701 _setDependencyOutput(path);
6702#endif
6703}
6704
6709{
6710 return _shouldShowSplashWindow;
6711}
6712
6716void VuoCompiler::setClangPath(const string &clangPath)
6717{
6718 this->clangPath = clangPath;
6719}
6720
6725{
6726 string vuoFrameworkPath = getVuoFrameworkPath();
6727 return (vuoFrameworkPath.empty() ?
6728 VUO_BUILD_DIR "/bin/VuoCompositionLoader.app/Contents/MacOS/VuoCompositionLoader" :
6729 vuoFrameworkPath + "/Helpers/VuoCompositionLoader.app/Contents/MacOS/VuoCompositionLoader");
6730}
6731
6735string VuoCompiler::getCompositionStubPath(void)
6736{
6737 string vuoFrameworkPath = getVuoFrameworkPath();
6738 return (vuoFrameworkPath.empty() ?
6739 VUO_BUILD_DIR "/lib/libVuoCompositionStub.dylib" :
6740 vuoFrameworkPath + "/Modules/libVuoCompositionStub.dylib");
6741}
6742
6746string VuoCompiler::getCachePathForComposition(const string compositionDir)
6747{
6748 string modifierLetterColon("꞉");
6749 string cachedModulesName = compositionDir;
6750 VuoStringUtilities::replaceAll(cachedModulesName, "/", modifierLetterColon);
6751 return VuoFileUtilities::getCachePath() + "/" + cachedModulesName;
6752}
6753
6758{
6759 __block vector<string> moduleSearchPaths;
6760 void (^envGetModuleSearchPaths)(Environment *) = ^void (Environment *env) {
6761 vector<string> result = env->getModuleSearchPaths();
6762 moduleSearchPaths.insert(moduleSearchPaths.end(), result.begin(), result.end());
6763 };
6764 applyToInstalledEnvironments(envGetModuleSearchPaths);
6765
6766 __block vector<string> headerSearchPaths;
6767 void (^envGetHeaderSearchPaths)(Environment *) = ^void (Environment *env) {
6768 vector<string> result = env->getHeaderSearchPaths();
6769 headerSearchPaths.insert(headerSearchPaths.end(), result.begin(), result.end());
6770 };
6771 applyToInstalledEnvironments(envGetHeaderSearchPaths);
6772
6773 __block vector<string> librarySearchPaths;
6774 void (^envGetLibrarySearchPaths)(Environment *) = ^void (Environment *env) {
6775 vector<string> result = env->getLibrarySearchPaths();
6776 librarySearchPaths.insert(librarySearchPaths.end(), result.begin(), result.end());
6777 };
6778 applyToInstalledEnvironments(envGetLibrarySearchPaths);
6779
6780 __block vector<string> frameworkSearchPaths;
6781 void (^envGetFrameworkSearchPaths)(Environment *) = ^void (Environment *env) {
6782 vector<string> result = env->getFrameworkSearchPaths();
6783 frameworkSearchPaths.insert(frameworkSearchPaths.end(), result.begin(), result.end());
6784 };
6785 applyToInstalledEnvironments(envGetFrameworkSearchPaths);
6786
6787 fprintf(stderr, "Module (node class, type, library) search paths:\n");
6788 for (vector<string>::iterator i = moduleSearchPaths.begin(); i != moduleSearchPaths.end(); ++i)
6789 fprintf(stderr, " %s\n", (*i).c_str());
6790 fprintf(stderr, "Header search paths:\n");
6791 for (vector<string>::iterator i = headerSearchPaths.begin(); i != headerSearchPaths.end(); ++i)
6792 fprintf(stderr, " %s\n", (*i).c_str());
6793 fprintf(stderr, "Other library search paths:\n");
6794 for (vector<string>::iterator i = librarySearchPaths.begin(); i != librarySearchPaths.end(); ++i)
6795 fprintf(stderr, " %s\n", (*i).c_str());
6796 fprintf(stderr, "Other framework search paths:\n");
6797 for (vector<string>::iterator i = frameworkSearchPaths.begin(); i != frameworkSearchPaths.end(); ++i)
6798 fprintf(stderr, " %s\n", (*i).c_str());
6799 fprintf(stderr, "Framework path:\n");
6800 if (! getVuoFrameworkPath().empty())
6801 fprintf(stderr, " %s\n", getVuoFrameworkPath().c_str());
6802 fprintf(stderr, "Clang path:\n");
6803 if (! getClangPath().empty())
6804 fprintf(stderr, " %s\n", getClangPath().c_str());
6805}
6806
6816{
6817 try
6818 {
6819 VuoCompiler compiler(compositionFilePath);
6820 string directory, file, extension;
6821 VuoFileUtilities::splitPath(compositionFilePath, directory, file, extension);
6822 string compiledCompositionPath = VuoFileUtilities::makeTmpFile(file, "bc");
6823 string linkedCompositionPath = VuoFileUtilities::makeTmpFile(file + "-linked", "");
6824 compiler.compileComposition(compositionFilePath, compiledCompositionPath, true, issues);
6825 compiler.linkCompositionToCreateExecutable(compiledCompositionPath, linkedCompositionPath, Optimization_FastBuild);
6826 remove(compiledCompositionPath.c_str());
6827 return VuoRunner::newSeparateProcessRunnerFromExecutable(linkedCompositionPath, directory, false, true);
6828 }
6829 catch (VuoCompilerException &e)
6830 {
6831 if (issues != e.getIssues())
6832 issues->append(e.getIssues());
6833 return NULL;
6834 }
6835}
6836
6851VuoRunner * VuoCompiler::newSeparateProcessRunnerFromCompositionString(string composition, string processName, string workingDirectory, VuoCompilerIssues *issues)
6852{
6853 try
6854 {
6855 string compositionPath = (workingDirectory.empty() ? "." : workingDirectory) + "/" + processName + ".vuo";
6856 VuoCompiler compiler(compositionPath);
6857 string compiledCompositionPath = VuoFileUtilities::makeTmpFile(processName, "bc");
6858 string linkedCompositionPath = VuoFileUtilities::makeTmpFile(processName, "");
6859 compiler.compileCompositionString(composition, compiledCompositionPath, true, issues);
6860 compiler.linkCompositionToCreateExecutable(compiledCompositionPath, linkedCompositionPath, Optimization_FastBuild);
6861 remove(compiledCompositionPath.c_str());
6862 return VuoRunner::newSeparateProcessRunnerFromExecutable(linkedCompositionPath, workingDirectory, false, true);
6863 }
6864 catch (VuoCompilerException &e)
6865 {
6866 if (issues != e.getIssues())
6867 issues->append(e.getIssues());
6868 return NULL;
6869 }
6870}
6871
6881{
6882 try
6883 {
6884 VuoCompiler compiler(compositionFilePath);
6885 string directory, file, extension;
6886 VuoFileUtilities::splitPath(compositionFilePath, directory, file, extension);
6887 string compiledCompositionPath = VuoFileUtilities::makeTmpFile(file, "bc");
6888 compiler.compileComposition(compositionFilePath, compiledCompositionPath, true, issues);
6889 string linkedCompositionPath = VuoFileUtilities::makeTmpFile(file, "dylib");
6890 compiler.linkCompositionToCreateDynamicLibrary(compiledCompositionPath, linkedCompositionPath, Optimization_FastBuild);
6891 remove(compiledCompositionPath.c_str());
6892 return VuoRunner::newCurrentProcessRunnerFromDynamicLibrary(linkedCompositionPath, directory, true);
6893 }
6894 catch (VuoCompilerException &e)
6895 {
6896 if (issues != e.getIssues())
6897 issues->append(e.getIssues());
6898 return NULL;
6899 }
6900}
6901
6917{
6918 try
6919 {
6920 string compositionPath = (workingDirectory.empty() ? "." : workingDirectory) + "/UntitledComposition.vuo";
6921 VuoCompiler compiler(compositionPath);
6922 string compiledCompositionPath = VuoFileUtilities::makeTmpFile("VuoRunnerComposition", "bc");
6923 compiler.compileCompositionString(composition, compiledCompositionPath, true, issues);
6924 string linkedCompositionPath = VuoFileUtilities::makeTmpFile("VuoRunnerComposition", "dylib");
6925 compiler.linkCompositionToCreateDynamicLibrary(compiledCompositionPath, linkedCompositionPath, Optimization_FastBuild);
6926 remove(compiledCompositionPath.c_str());
6927 return VuoRunner::newCurrentProcessRunnerFromDynamicLibrary(linkedCompositionPath, workingDirectory, true);
6928 }
6929 catch (VuoCompilerException &e)
6930 {
6931 if (issues != e.getIssues())
6932 issues->append(e.getIssues());
6933 return NULL;
6934 }
6935}
6936
6938{
6939 pendingDataQueue = dispatch_queue_create("org.vuo.compiler.delegate.pending", 0);
6940}
6941
6943{
6944 LoadedModulesData *data = dequeueData();
6945 data->release();
6946}
6947
6951void VuoCompilerDelegate::enqueueData(LoadedModulesData *data)
6952{
6953 dispatch_sync(pendingDataQueue, ^{
6954 pendingData.push_back(data);
6955 });
6956}
6957
6961VuoCompilerDelegate::LoadedModulesData * VuoCompilerDelegate::dequeueData(void)
6962{
6963 __block LoadedModulesData *ret;
6964 dispatch_sync(pendingDataQueue, ^{
6965 ret = pendingData.front();
6966 pendingData.pop_front();
6967 });
6968 return ret;
6969}
6970
6975VuoCompilerDelegate::LoadedModulesData::LoadedModulesData(const set< pair<VuoCompilerModule *, VuoCompilerModule *> > &modulesModified,
6976 const set<VuoCompilerModule *> &modulesRemoved, VuoCompilerIssues *issues)
6977{
6978 referenceCountQueue = dispatch_queue_create("org.vuo.compiler.delegate.reference", 0);
6979 referenceCount = 0;
6980
6981 this->modulesModified = modulesModified;
6982 this->modulesRemoved = modulesRemoved;
6983 this->issues = issues;
6984}
6985
6989VuoCompilerDelegate::LoadedModulesData::~LoadedModulesData(void)
6990{
6991 delete issues;
6992
6993 for (set< pair<VuoCompilerModule *, VuoCompilerModule *> >::iterator i = modulesModified.begin(); i != modulesModified.end(); ++i)
6994 VuoCompiler::destroyModule((*i).first);
6995
6996 for (set<VuoCompilerModule *>::iterator i = modulesRemoved.begin(); i != modulesRemoved.end(); ++i)
6998}
6999
7003void VuoCompilerDelegate::LoadedModulesData::retain(void)
7004{
7005 dispatch_sync(referenceCountQueue, ^{
7006 ++referenceCount;
7007 });
7008}
7009
7013void VuoCompilerDelegate::LoadedModulesData::release(void)
7014{
7015 dispatch_sync(referenceCountQueue, ^{
7016 --referenceCount;
7017 if (referenceCount == 0) {
7018 delete this;
7019 }
7020 });
7021}