Vuo 2.4.2
Loading...
Searching...
No Matches
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, dispatch_group_t moduleSourceCompilersExist,
1665 bool shouldRecompileIfUnchanged)
1666{
1667 ModuleInfoIterator modulesToLoadIter = listSourceFiles(moduleKeys);
1668
1669 int environmentIndex = sharedEnvironments[target].size();
1670 for (int i = 0; i < sharedEnvironments[target].size(); ++i)
1671 if (this == sharedEnvironments[target].at(i).at(0))
1672 environmentIndex = i;
1673
1674 set<dispatch_group_t> sourcesLoading;
1675 int sourcesEnqueued = 0;
1676 ModuleInfo *sourceInfo;
1677 while ((sourceInfo = modulesToLoadIter.next()))
1678 {
1679 string moduleKey = sourceInfo->getModuleKey();
1680
1681 dispatch_group_t loadingGroup = sourceInfo->getLoadingGroup();
1682 sourcesLoading.insert(loadingGroup);
1683
1684 // Skip compiling if the source file has already been scheduled for compilation.
1685 // Either its compilation is in progress or it failed to compile.
1686
1687 if (sourceInfo->getAttempted())
1688 continue;
1689
1690 // Skip compiling if the compiled module is up-to-date.
1691
1692 string sourceCode = sourceInfo->getSourceCode();
1693
1694 if (! shouldRecompileIfUnchanged)
1695 {
1696 VuoCompilerNodeClass *existingNodeClass = getNodeClass(moduleKey);
1697 if (existingNodeClass && existingNodeClass->getSourceCode() == sourceCode)
1698 continue;
1699 }
1700
1701 // Enqueue the source file to be compiled.
1702
1703 sourceInfo->setAttempted(true);
1704
1705 dispatch_group_enter(loadingGroup);
1706 dispatch_group_enter(moduleSourceCompilersExist);
1707 dispatch_group_enter(moduleSourceCompilersExistGlobally);
1708
1710 queueItem->moduleKey = moduleKey;
1711 queueItem->sourcePath = sourceInfo->getFile()->path();
1712 queueItem->sourceCode = sourceCode;
1713 queueItem->sourceFile = sourceInfo->getFile();
1714 queueItem->cachedModulesPath = sourceInfo->isSourceCodeOverridden() ? getOverriddenCompiledModuleCachePath() : getCompiledModuleCachePath();
1715 queueItem->compiledModulePath = queueItem->cachedModulesPath + "/" + moduleKey + ".vuonode";
1716 queueItem->loadingGroup = loadingGroup;
1717 queueItem->priority = { environmentIndex, sourceInfo->getLongestDownstreamPath() };
1718 moduleCompilationQueue->enqueue(queueItem);
1719 ++sourcesEnqueued;
1720 }
1721
1722 // Compile each enqueued source file. This is done asynchronously for several reasons:
1723 // - To avoid environmentQueue calling compiler code calling environmentQueue.
1724 // - To compile dependencies that were enqueued later while a compilation that was enqueued earlier waits.
1725 // - To be more efficient when compiling multiple source files.
1726
1727 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
1728 dispatch_async(queue, ^{
1729 for (int i = 0; i < sourcesEnqueued; ++i)
1730 {
1731 __block VuoModuleCompilationQueue::Item *queueItem = moduleCompilationQueue->next();
1732 VUserLog("Compiling %s", queueItem->moduleKey.c_str());
1733
1734 dispatch_async(queue, ^{
1735 try
1736 {
1737 VuoFileUtilities::makeDir(queueItem->cachedModulesPath);
1738 }
1739 catch (VuoException &e)
1740 {
1741 VuoCompilerIssue issue(VuoCompilerIssue::Error, "compiling subcomposition", "", "Couldn't create cached modules folder", e.what());
1742 VuoCompilerIssues *issues = new VuoCompilerIssues(issue);
1743 notifyCompilers(set<VuoCompilerModule *>(), set< pair<VuoCompilerModule *, VuoCompilerModule *> >(), set<VuoCompilerModule *>(), issues, false);
1744 }
1745
1746 VuoCompiler *compiler = new VuoCompiler(queueItem->sourcePath, target);
1747 compiler->setLoadAllModules(false);
1748
1749 VuoModuleCompilationQueue::Item *queueItemForCallback = queueItem;
1750 auto moduleLoadedCallback = [=](){
1751 dispatch_group_leave(queueItemForCallback->loadingGroup);
1752 moduleCompilationQueue->completed(queueItemForCallback);
1753 };
1754
1755 string ext = queueItem->sourceFile->extension();
1757 {
1758 VuoModuleCompiler *moduleCompiler = NULL;
1759 try
1760 {
1761 moduleCompiler = VuoModuleCompiler::newModuleCompiler("isf", queueItem->moduleKey, queueItem->sourceFile);
1762 }
1763 catch (VuoException &e)
1764 {
1765 VuoCompilerIssues *issues = new VuoCompilerIssues(VuoCompilerIssue(VuoCompilerIssue::Error, "compiling ISF module", queueItem->sourcePath, "", e.what()));
1766 notifyCompilers(set<VuoCompilerModule *>(), set< pair<VuoCompilerModule *, VuoCompilerModule *> >(), set<VuoCompilerModule *>(), issues, false);
1767 }
1768
1769 if (moduleCompiler)
1770 {
1771 moduleCompiler->overrideSourceCode(queueItem->sourceCode, queueItem->sourceFile);
1772
1773 auto getType = [&compiler] (const string &moduleKey) { return compiler->getType(moduleKey); };
1775 Module *module = moduleCompiler->compile(getType, llvmQueue, issues);
1776
1777 if (module)
1778 {
1779 dispatch_sync(llvmQueue, ^{
1780 setTargetForModule(module, target);
1781 writeModuleToBitcode(module, queueItem->compiledModulePath);
1782 });
1783 }
1784 else
1785 {
1786 issues->setFilePathIfEmpty(queueItem->sourcePath);
1787 }
1788
1789 moduleFileChanged(queueItem->compiledModulePath, queueItem->sourceCode, moduleLoadedCallback, compiler, issues);
1790 }
1791 else
1792 moduleLoadedCallback();
1793 }
1794 else if (VuoFileUtilities::isCSourceExtension(ext) && ! queueItem->cachedModulesPath.empty())
1795 {
1797
1798 try
1799 {
1800 compiler->compileModule(queueItem->sourcePath, queueItem->compiledModulePath, vector<string>());
1801 }
1802 catch (VuoCompilerException &e)
1803 {
1804 if (issues != e.getIssues())
1805 issues->append(e.getIssues());
1806
1807 issues->setFilePathIfEmpty(queueItem->sourcePath);
1808 }
1809
1810 moduleFileChanged(queueItem->compiledModulePath, queueItem->sourceCode, moduleLoadedCallback, compiler, issues);
1811 }
1812 else
1813 {
1815
1816 try
1817 {
1818 VuoCompilerComposition *composition = VuoCompilerComposition::newCompositionFromGraphvizDeclaration(queueItem->sourceCode, compiler);
1819
1820 Module *module = compiler->compileCompositionToModule(composition, queueItem->moduleKey, false, issues);
1821 if (! module)
1822 throw VuoCompilerException(issues, false);
1823
1824 dispatch_sync(llvmQueue, ^{
1825 setTargetForModule(module, target);
1826 writeModuleToBitcode(module, queueItem->compiledModulePath);
1827 });
1828
1829 VuoComposition *baseComposition = composition->getBase();
1830 delete composition;
1831 delete baseComposition;
1832 }
1833 catch (VuoCompilerException &e)
1834 {
1835 if (issues != e.getIssues())
1836 issues->append(e.getIssues());
1837
1838 issues->setFilePathIfEmpty(queueItem->sourcePath);
1839 }
1840
1841 moduleFileChanged(queueItem->compiledModulePath, queueItem->sourceCode, moduleLoadedCallback, compiler, issues);
1842 }
1843
1844 delete compiler;
1845 dispatch_group_leave(moduleSourceCompilersExist);
1846 dispatch_group_leave(moduleSourceCompilersExistGlobally);
1847 });
1848 }
1849 });
1850
1851 return sourcesLoading;
1852}
1853
1862set<VuoCompilerModule *> VuoCompiler::Environment::unloadCompiledModules(const set<string> &moduleKeys)
1863{
1864 set<VuoCompilerModule *> modulesUnloaded;
1865
1866 for (set<string>::const_iterator i = moduleKeys.begin(); i != moduleKeys.end(); ++i)
1867 {
1868 string moduleKey = *i;
1869 VuoCompilerModule *module = NULL;
1870
1871 map<string, VuoCompilerNodeClass *>::iterator nodeClassIter = nodeClasses.find(moduleKey);
1872 if (nodeClassIter != nodeClasses.end())
1873 {
1874 module = nodeClassIter->second;
1875 nodeClasses.erase(nodeClassIter);
1876 }
1877 else
1878 {
1879 map<string, VuoCompilerType *>::iterator typeIter = types.find(moduleKey);
1880 if (typeIter != types.end())
1881 {
1882 module = typeIter->second;
1883 types.erase(typeIter);
1884 }
1885 else
1886 {
1887 map<string, VuoCompilerModule *>::iterator libraryModuleIter = libraryModules.find(moduleKey);
1888 if (libraryModuleIter != libraryModules.end())
1889 {
1890 module = libraryModuleIter->second;
1891 libraryModules.erase(libraryModuleIter);
1892 }
1893 }
1894 }
1895
1896 if (module)
1897 {
1898 modulesUnloaded.insert(module);
1899 removeFromDependencyGraph(module);
1900 modulesChanged();
1901 }
1902 }
1903
1904 return modulesUnloaded;
1905}
1906
1912void VuoCompiler::Environment::deleteModulesCompiledFromSourceCode(const set<string> &moduleKeys)
1913{
1914 for (set<string>::const_iterator i = moduleKeys.begin(); i != moduleKeys.end(); ++i)
1915 {
1916 string moduleKey = *i;
1917
1918 map<string, ModuleInfo *>::iterator iter = moduleFilesAtSearchPath[getCompiledModuleCachePath()].find(moduleKey);
1919 if (iter != moduleFilesAtSearchPath[getCompiledModuleCachePath()].end())
1920 VuoFileUtilities::deleteFile(iter->second->getFile()->path());
1921 }
1922}
1923
1931VuoCompilerModule * VuoCompiler::Environment::loadModule(ModuleInfo *moduleInfo)
1932{
1933 string moduleKey = moduleInfo->getModuleKey();
1934
1935 // Skip certain LLVM modules that definitely aren't Vuo modules to avoid adding struct types defined in them to the LLVM
1936 // context, resulting in mismatched struct types in code generation (e.g. %struct.NodeContext and %struct.NodeContext.1).
1937 if (VuoStringUtilities::beginsWith(moduleKey, "libVuo"))
1938 return NULL;
1939
1940 // Don't try to load single-target parts
1941 // (such as those found in `build/test/TestControlAndTelemetry`),
1942 // since they just fail and pollute the logs.
1943 if (VuoStringUtilities::endsWith(moduleKey, "-x86_64")
1944 || VuoStringUtilities::endsWith(moduleKey, "-arm64"))
1945 return nullptr;
1946
1947 __block size_t inputDataBytes;
1948 __block char *rawInputData;
1949 dispatch_sync(llvmQueue, ^{
1950 try
1951 {
1952 rawInputData = moduleInfo->getFile()->getContentsAsRawData(inputDataBytes);
1953 }
1954 catch (VuoException &e)
1955 {
1956 rawInputData = NULL;
1957 VUserLog("Warning: Couldn't load module '%s'. Its file may have been deleted. (%s)", moduleKey.c_str(), e.what());
1958 }
1959 });
1960 if (! rawInputData)
1961 return NULL;
1962
1963 char *processedInputData;
1964#if VUO_PRO
1965 processedInputData = loadModule_Pro0(moduleInfo, moduleKey, inputDataBytes, rawInputData);
1966#else
1967 processedInputData = rawInputData;
1968#endif
1969
1970 Module *module = NULL;
1971 set<string> moduleArchs;
1972 bool moduleParseError = !processedInputData;
1973 if (!moduleParseError)
1974 {
1975 string moduleReadError;
1976 string arch = VuoCompiler::getTargetArch(target);
1977 VuoLog_status("Loading module \"%s\" (%s)", moduleKey.c_str(), arch.c_str());
1978 module = readModuleFromBitcodeData(processedInputData, inputDataBytes, arch, moduleArchs, moduleReadError);
1979 VuoLog_status(NULL);
1980 free(processedInputData);
1981
1982 if (!module)
1983 {
1984 moduleParseError = true;
1985
1986 if (VuoFileUtilities::arePathsEqual(moduleInfo->getSearchPath(), getCompiledModuleCachePath()))
1987 VuoFileUtilities::deleteFile(moduleInfo->getFile()->path());
1988 else
1989 VUserLog("Error: Couldn't load module '%s' into %s environment: %s.", moduleKey.c_str(), arch.c_str(), moduleReadError.c_str());
1990 }
1991 }
1992
1993 if (!moduleParseError)
1994 {
1995 string modulePath;
1996 if (! moduleInfo->getFile()->isInArchive())
1997 modulePath = moduleInfo->getFile()->path();
1998
1999 VuoCompilerCompatibility moduleCompatibility = (VuoFileUtilities::arePathsEqual(moduleInfo->getSearchPath(), getCompiledModuleCachePath()) ?
2001 VuoCompilerCompatibility::compatibilityWithArchitectures(moduleArchs));
2002
2003 __block VuoCompilerModule *compilerModule;
2004 dispatch_sync(llvmQueue, ^{
2005 compilerModule = VuoCompilerModule::newModule(moduleKey, module, modulePath, moduleCompatibility);
2006 });
2007
2008 if (compilerModule)
2009 {
2010 if (dynamic_cast<VuoCompilerNodeClass *>(compilerModule))
2011 nodeClasses[moduleKey] = static_cast<VuoCompilerNodeClass *>(compilerModule);
2012 else if (dynamic_cast<VuoCompilerType *>(compilerModule))
2013 types[moduleKey] = static_cast<VuoCompilerType *>(compilerModule);
2014 else
2015 libraryModules[moduleKey] = compilerModule;
2016
2017 VuoNodeSet *nodeSet = VuoNodeSet::createNodeSetForModule(moduleInfo->getFile());
2018 if (nodeSet)
2019 {
2020 map<string, VuoNodeSet *>::iterator nodeSetIter = nodeSetForName.find(nodeSet->getName());
2021 if (nodeSetIter == nodeSetForName.end())
2022 {
2023 nodeSetForName[nodeSet->getName()] = nodeSet;
2024 }
2025 else
2026 {
2027 delete nodeSet;
2028 nodeSet = nodeSetIter->second;
2029 }
2030 compilerModule->getPseudoBase()->setNodeSet(nodeSet);
2031 }
2032
2033#if VUO_PRO
2034 loadModule_Pro1(rawInputData, processedInputData, compilerModule);
2035#endif
2036
2037 compilerModule->setBuiltIn( isBuiltInOriginal() );
2038
2039 return compilerModule;
2040 }
2041 else
2042 {
2043 destroyLlvmModule(module);
2044 }
2045 }
2046
2047 return NULL;
2048}
2049
2055void VuoCompiler::Environment::replaceNodeClass(VuoCompilerNodeClass *newNodeClass)
2056{
2057 string moduleKey = newNodeClass->getBase()->getModuleKey();
2058
2059 VuoCompilerNodeClass *oldNodeClass = nodeClasses[moduleKey];
2060 if (oldNodeClass)
2061 {
2062 removeFromDependencyGraph(oldNodeClass);
2063 destroyModule(oldNodeClass);
2064 }
2065
2066 nodeClasses[moduleKey] = newNodeClass;
2067 addToDependencyGraph(newNodeClass);
2068 modulesChanged();
2069}
2070
2071void VuoCompiler::Environment::addToDependencyGraph(VuoCompilerModule *module)
2072{
2073 DependencyGraphVertex *vertex = DependencyGraphVertex::vertexForDependency(module->getPseudoBase()->getModuleKey(), dependencyGraph);
2074 dependencyGraph->addVertex(vertex);
2075
2076 vertex->setEnvironment(this);
2077
2078 set<string> dependencies = module->getDependencies();
2079 for (set<string>::iterator i = dependencies.begin(); i != dependencies.end(); ++i)
2080 {
2081 DependencyGraphVertex *depVertex = DependencyGraphVertex::vertexForDependency(*i, dependencyGraph);
2082 dependencyGraph->addEdge(vertex, depVertex);
2083 }
2084}
2085
2086void VuoCompiler::Environment::removeFromDependencyGraph(VuoCompilerModule *module)
2087{
2088 DependencyGraphVertex *vertex = DependencyGraphVertex::vertexForDependency(module->getPseudoBase()->getModuleKey(), dependencyGraph);
2089 dependencyGraph->removeVertex(vertex);
2090}
2091
2102void VuoCompiler::Environment::reifyPortTypes(const map<string, VuoCompilerType *> &inheritedTypes)
2103{
2104 map<string, VuoCompilerType *> availableTypes = inheritedTypes;
2105 availableTypes.insert(types.begin(), types.end());
2106
2107 for (map<string, VuoCompilerNodeClass *>::const_iterator i = nodeClasses.begin(); i != nodeClasses.end(); ++i)
2108 {
2109 VuoNodeClass *nodeClass = i->second->getBase();
2110
2111 vector<VuoPortClass *> inputPortClasses = nodeClass->getInputPortClasses();
2112 vector<VuoPortClass *> outputPortClasses = nodeClass->getOutputPortClasses();
2113 vector<VuoPortClass *> portClasses;
2114 portClasses.insert(portClasses.end(), inputPortClasses.begin(), inputPortClasses.end());
2115 portClasses.insert(portClasses.end(), outputPortClasses.begin(), outputPortClasses.end());
2116
2117 for (vector<VuoPortClass *>::iterator j = portClasses.begin(); j != portClasses.end(); ++j)
2118 {
2119 VuoCompilerPortClass *portClass = static_cast<VuoCompilerPortClass *>((*j)->getCompiler());
2120 VuoType *baseType = portClass->getDataVuoType();
2121
2122 if (baseType && ! baseType->hasCompiler())
2123 {
2124 string typeName = baseType->getModuleKey();
2125 VuoCompilerType *reifiedType = NULL;
2126
2127 VuoGenericType *genericType = dynamic_cast<VuoGenericType *>(baseType);
2128 if (genericType)
2129 {
2130 reifiedType = VuoCompilerGenericType::newGenericType(genericType, availableTypes);
2131 if (reifiedType) {
2132 genericTypes.insert( static_cast<VuoCompilerGenericType *>(reifiedType) );
2133 }
2134 }
2135 else
2136 {
2137 map<string, VuoCompilerType *>::iterator reifiedTypeIter = availableTypes.find(typeName);
2138 if (reifiedTypeIter != availableTypes.end())
2139 {
2140 delete baseType;
2141 reifiedType = reifiedTypeIter->second;
2142 }
2143 }
2144
2145 if (reifiedType) {
2146 portClass->setDataVuoType(reifiedType->getBase());
2147 }
2148 }
2149 }
2150
2151 vector<VuoCompilerTriggerDescription *> triggers = nodeClass->getCompiler()->getTriggerDescriptions();
2152 for (vector<VuoCompilerTriggerDescription *>::iterator j = triggers.begin(); j != triggers.end(); ++j)
2153 {
2154 VuoCompilerTriggerDescription *trigger = *j;
2155 VuoType *baseType = trigger->getDataType();
2156
2157 if (baseType && ! baseType->hasCompiler())
2158 {
2159 string typeName = baseType->getModuleKey();
2160 map<string, VuoCompilerType *>::iterator reifiedTypeIter = availableTypes.find(typeName);
2161 if (reifiedTypeIter != availableTypes.end())
2162 {
2163 delete baseType;
2164 VuoCompilerType *reifiedType = reifiedTypeIter->second;
2165 trigger->setDataType(reifiedType->getBase());
2166 }
2167 }
2168 }
2169 }
2170}
2171
2182void VuoCompiler::Environment::getCacheableModulesAndDependencies(set<string> &cacheableModulesAndDependencies,
2183 set<string> &dylibsNeededToLinkToThisCache,
2184 set<string> &frameworksNeededToLinkToThisCache)
2185{
2186 if (isModuleCacheInitialized && ! isModuleCacheableDataDirty)
2187 {
2188 cacheableModulesAndDependencies = moduleCacheContents;
2189 dylibsNeededToLinkToThisCache = moduleCacheDylibs;
2190 frameworksNeededToLinkToThisCache = moduleCacheFrameworks;
2191 return;
2192 }
2193
2195
2196 // Include all modules…
2197 map<string, VuoCompilerModule *> allModules;
2198 allModules.insert(nodeClasses.begin(), nodeClasses.end());
2199 allModules.insert(types.begin(), types.end());
2200 allModules.insert(libraryModules.begin(), libraryModules.end());
2201
2202 set<string> dependencies;
2203 for (map<string, VuoCompilerModule *>::iterator i = allModules.begin(); i != allModules.end(); ++i)
2204 {
2205 string moduleKey = i->first;
2206 VuoCompilerModule *module = i->second;
2207
2208#if VUO_PRO
2209 // … except Pro modules and modules that depend on them…
2210 if (module->requiresPro())
2211 continue;
2212#endif
2213
2214 // … and incompatible modules.
2215 if (! module->getCompatibleTargets().isCompatibleWith(compositionTargets))
2216 continue;
2217
2218 cacheableModulesAndDependencies.insert(moduleKey);
2219
2220 set<string> moduleDependencies = module->getDependencies();
2221 dependencies.insert(moduleDependencies.begin(), moduleDependencies.end());
2222 }
2223
2224 // For the built-in environment, include Vuo's core dependencies.
2225 if (builtIn && ! generated)
2226 {
2227 vector<string> coreDependencies = getCoreVuoDependencies();
2228 dependencies.insert(coreDependencies.begin(), coreDependencies.end());
2229 }
2230
2231 // Include all dependencies of the included module that are located in this environment.
2232 // (All modules are already included, so we only need to handle non-module dependencies.)
2233 for (set<string>::iterator i = dependencies.begin(); i != dependencies.end(); ++i)
2234 {
2235 string dependency = *i;
2236 if (allModules.find(dependency) == allModules.end())
2237 {
2238 if (VuoStringUtilities::endsWith(dependency, ".framework"))
2239 frameworksNeededToLinkToThisCache.insert(dependency);
2240 else
2241 {
2242 string dependencyPath = VuoCompiler::getLibraryPath(dependency, librarySearchPaths);
2243 if (! dependencyPath.empty())
2244 {
2245 if (VuoStringUtilities::endsWith(dependencyPath, ".dylib"))
2246 dylibsNeededToLinkToThisCache.insert(dependencyPath);
2247 else
2248 cacheableModulesAndDependencies.insert(dependency);
2249 }
2250 }
2251 }
2252 }
2253
2254 moduleCacheDylibs = dylibsNeededToLinkToThisCache;
2255 moduleCacheFrameworks = frameworksNeededToLinkToThisCache;
2256
2257 if (builtIn)
2258 {
2259 currentModuleCacheDylib = VuoFileUtilities::buildModuleCacheDylibPath(moduleCachePath, builtIn, generated);
2260
2261 // Give each built-in cache arch a unique name, so they can be built in parallel.
2262 if (!vuoFrameworkInProgressPath.empty())
2263 currentModuleCacheDylib += "-" + VuoCompiler::getTargetArch(target);
2264 }
2265}
2266
2287void VuoCompiler::Environment::useModuleCache(bool shouldUseExistingCache, VuoCompiler *compiler,
2288 set<string> cacheableModulesAndDependencies,
2289 set<string> dylibsNeededToLinkToCaches, set<string> frameworksNeededToLinkToCaches,
2290 unsigned long lastPrerequisiteModuleCacheRebuild)
2291{
2292 // Ignore the cache if the `prelinkCache` preference is false.
2293
2294 static dispatch_once_t checked = 0;
2295 static bool prelinkCache = true;
2296 dispatch_once(&checked, ^{
2297 Boolean valid;
2298 bool result = CFPreferencesGetAppBooleanValue(CFSTR("prelinkCache"), CFSTR("org.vuo.Editor"), &valid);
2299 if (valid)
2300 prelinkCache = result;
2301 });
2302 if (! prelinkCache)
2303 {
2304 VDebugLog("Ignoring the module cache since the 'prelinkCache' preference is false.");
2305 return;
2306 }
2307
2308 // Don't do anything if this environment doesn't have a cache configured.
2309
2310 if (moduleCachePath.empty())
2311 return;
2312
2313 // Don't bother rechecking the cache if neither the modules in this environment nor the caches that it depends on have changed.
2314
2315 string cacheDescription = VuoFileUtilities::buildModuleCacheDescription(moduleCachePath, generated);
2316 if (isModuleCacheInitialized && ! isModuleCacheableDataDirty &&
2317 (builtIn || lastModuleCacheRebuild >= lastPrerequisiteModuleCacheRebuild))
2318 {
2319 VDebugLog("No need to recheck %s.", cacheDescription.c_str());
2320 return;
2321 }
2322
2323 try
2324 {
2325 VDebugLog("Checking if %s is up-to-date…", cacheDescription.c_str());
2326
2327 bool isCacheUpToDate = true;
2328
2329 if (isModuleCacheInitialized && isModuleCacheableDataDirty)
2330 isCacheUpToDate = false;
2331
2332 isModuleCacheInitialized = true;
2333 isModuleCacheableDataDirty = false;
2334
2335 string indexPath = VuoFileUtilities::buildModuleCacheIndexPath(moduleCachePath, builtIn, generated);
2336 if (!vuoFrameworkInProgressPath.empty())
2337 indexPath += "-" + VuoCompiler::getTargetArch(target);
2338 string dir, file, ext;
2339 VuoFileUtilities::splitPath(indexPath, dir, file, ext);
2340 string indexFileName = file + "." + ext;
2341
2342 // Create the cache directory and index if they don't already exist. (If they do exist, don't affect the last-modified times.)
2343
2344 bool dirExists = VuoFileUtilities::fileExists(moduleCachePath);
2345 if (! dirExists)
2346 {
2347 if (shouldUseExistingCache)
2348 throw VuoException("Trying to use the existing cache, but the cache directory doesn't exist.", false);
2349
2350 VuoFileUtilities::makeDir(moduleCachePath);
2351 isCacheUpToDate = false;
2352 }
2353
2354 bool indexFileExists = VuoFileUtilities::fileExists(indexPath);
2355 if (! indexFileExists)
2356 {
2357 if (shouldUseExistingCache)
2358 throw VuoException("Trying to use the existing cache, but the cache index doesn't exist.", false);
2359
2361 isCacheUpToDate = false;
2362 }
2363
2364 // Lock the cache for reading.
2365
2366 VuoFileUtilities::File *fileForLocking;
2367 {
2368 fileForLocking = moduleCacheFileForLocking[indexPath];
2369 if (! fileForLocking)
2370 {
2371 fileForLocking = new VuoFileUtilities::File(moduleCachePath, indexFileName);
2372 moduleCacheFileForLocking[indexPath] = fileForLocking;
2373 }
2374
2375 if (!fileForLocking->lockForReading())
2376 VDebugLog("\tWarning: Couldn't lock for reading.");
2377 }
2378
2379 // If this is the first time this Environment is using its cache, see if there's a dylib on disk.
2380
2381 if (currentModuleCacheDylib.empty())
2382 currentModuleCacheDylib = VuoFileUtilities::findLatestRevisionOfModuleCacheDylib(moduleCachePath, builtIn, generated, lastModuleCacheRebuild);
2383
2384 if (shouldUseExistingCache && currentModuleCacheDylib.empty())
2385 throw VuoException("Trying to use the existing cache, but the cache dylib doesn't exist.", false);
2386
2387 // Check if the dylib is newer than the other caches that it depends on.
2388
2389 if (isCacheUpToDate)
2390 isCacheUpToDate = lastModuleCacheRebuild >= lastPrerequisiteModuleCacheRebuild;
2391
2392 // Check if the dylib looks remotely valid.
2393
2394 if (isCacheUpToDate)
2395 {
2396 bool dylibHasData = VuoFileUtilities::fileContainsReadableData(currentModuleCacheDylib);
2397 if (! dylibHasData)
2398 {
2399 if (shouldUseExistingCache)
2400 throw VuoException("Trying to use the existing cache, but the cache doesn't contain readable data.", false);
2401 else
2402 isCacheUpToDate = false;
2403 }
2404 }
2405
2406 // List the items actually in the cache, according to its index.
2407
2408 const char separator = '\n';
2409 if (isCacheUpToDate || shouldUseExistingCache)
2410 {
2411 VuoFileUtilities::File indexFile(moduleCachePath, indexFileName);
2412 string index = indexFile.getContentsAsString();
2413 vector<string> actualIndex = VuoStringUtilities::split(index, separator);
2414
2415 moduleCacheContents.clear();
2416 moduleCacheContents.insert(actualIndex.begin(), actualIndex.end());
2417
2418 if (shouldUseExistingCache)
2419 {
2420 isModuleCacheAvailable = true;
2421 return;
2422 }
2423 }
2424
2425 // Check if the list of actual items matches the list of expected items.
2426
2427 if (isCacheUpToDate)
2428 {
2429 if (moduleCacheContents.size() != cacheableModulesAndDependencies.size())
2430 isCacheUpToDate = false;
2431 else
2432 {
2433 set<string> contentsInBoth;
2434 std::set_intersection(moduleCacheContents.begin(), moduleCacheContents.end(),
2435 cacheableModulesAndDependencies.begin(), cacheableModulesAndDependencies.end(),
2436 std::inserter(contentsInBoth, contentsInBoth.begin()));
2437
2438 if (contentsInBoth.size() != cacheableModulesAndDependencies.size())
2439 isCacheUpToDate = false;
2440 }
2441 }
2442
2443 // Check if the cache is newer than all of the modules in it.
2444
2445 if (isCacheUpToDate)
2446 {
2447 unsigned long cacheLastModified = VuoFileUtilities::getFileLastModifiedInSeconds(currentModuleCacheDylib);
2448
2449 for (map<string, map<string, ModuleInfo *> >::iterator i = moduleFilesAtSearchPath.begin(); i != moduleFilesAtSearchPath.end(); ++i)
2450 {
2451 for (map<string, ModuleInfo *>::iterator j = i->second.begin(); j != i->second.end(); ++j)
2452 {
2453 if (! j->second->isOlderThan(cacheLastModified))
2454 {
2455 isCacheUpToDate = false;
2456 break;
2457 }
2458 }
2459 }
2460 }
2461
2462 // If the cache is consistent with this environment, we're done.
2463
2464 if (isCacheUpToDate)
2465 {
2466 VDebugLog("Up-to-date.");
2467
2468 isModuleCacheAvailable = true;
2469 return;
2470 }
2471
2472 // Otherwise, (re)build the cache.
2473
2474 if (! builtIn)
2475 {
2476 currentModuleCacheDylib = VuoFileUtilities::buildModuleCacheDylibPath(moduleCachePath, builtIn, generated);
2477
2478 struct timeval t;
2479 gettimeofday(&t, NULL);
2480 lastModuleCacheRebuild = t.tv_sec;
2481 }
2482
2483 dispatch_async(moduleCacheBuildingQueue, ^{
2484 VUserLog("Rebuilding %s…", cacheDescription.c_str());
2485
2486 set<Module *> modulesToLink;
2487 set<string> librariesToLink;
2488 set<string> frameworksToLink;
2489 {
2490 compiler->getLinkerInputs(cacheableModulesAndDependencies, Optimization_SmallBinary, modulesToLink, librariesToLink, frameworksToLink);
2491
2492 librariesToLink.insert(dylibsNeededToLinkToCaches.begin(), dylibsNeededToLinkToCaches.end());
2493 frameworksToLink.insert(frameworksNeededToLinkToCaches.begin(), frameworksNeededToLinkToCaches.end());
2494 }
2495
2496 bool gotLockForWriting = false;
2497 try
2498 {
2499 // Try to upgrade the file lock for writing.
2500 gotLockForWriting = fileForLocking->lockForWriting(true);
2501 if (! gotLockForWriting)
2502 throw VuoException("The cache file is being used by another process. "
2503 "If any composition windows are open from previous Vuo sessions, quit them. "
2504 "If any processes whose names start with \"VuoComposition\" or one of your composition file names appear in Activity Monitor, force-quit them.",
2505 false);
2506
2507 // Link the dependencies to create the cached resources dylib in a temporary file.
2508 string dir, file, ext;
2509 VuoFileUtilities::splitPath(currentModuleCacheDylib, dir, file, ext);
2510 string tmpPath = VuoFileUtilities::makeTmpFile(file, "dylib");
2511 vector<string> rPaths = compiler->getRunPathSearchPaths(this);
2513 compiler->link(tmpPath, modulesToLink, librariesToLink, frameworksToLink, true, rPaths, false, issues);
2514
2515 if (! issues->isEmpty())
2516 VUserLog("Warning: May not have fully rebuilt %s for the \"faster build\" optimization:\n%s", cacheDescription.c_str(), issues->getLongDescription(false).c_str());
2517
2518 // Move the temporary file into the cache.
2519 VuoFileUtilities::moveFile(tmpPath, currentModuleCacheDylib);
2520
2521 // Change the dylib's ID from the temporary path to the path within the cache.
2523 getVuoFrameworkPath() + "/Helpers/install_name_tool",
2524 "-id",
2525 currentModuleCacheDylib,
2526 currentModuleCacheDylib,
2527 });
2528
2529 // Ad-hoc code-sign the runtime-generated System and User caches,
2530 // but don't ad-hoc code-sign the buildtime-generated Builtin module cache
2531 // since `framework/CMakeLists.txt` later changes its ID/rpath/loads.
2532 if (vuoFrameworkInProgressPath.empty())
2533 adHocCodeSign(currentModuleCacheDylib);
2534
2535 // Write the list of dependencies to the index file.
2536 vector<string> expectedContents(cacheableModulesAndDependencies.begin(), cacheableModulesAndDependencies.end());
2537 string index = VuoStringUtilities::join(expectedContents, separator);
2538 VuoFileUtilities::writeStringToFile(index, indexPath);
2539
2540 // Delete any older revisions of the dylib.
2542
2543 // Downgrade the file lock back to reading.
2544 if (!fileForLocking->lockForReading())
2545 VDebugLog("\tWarning: Couldn't downgrade the lock back to reading.");
2546
2547 dispatch_sync(environmentQueue, ^{
2548 moduleCacheContents = cacheableModulesAndDependencies;
2549 isModuleCacheAvailable = true;
2550 });
2551 }
2552 catch (VuoException &e)
2553 {
2554 // Downgrade the file lock back to reading.
2555 if (gotLockForWriting)
2556 if (!fileForLocking->lockForReading())
2557 VDebugLog("\tWarning: Couldn't downgrade the lock back to reading.");
2558
2559 VUserLog("Warning: Couldn't rebuild %s for the \"faster build\" optimization: %s", cacheDescription.c_str(), e.what());
2560 }
2561
2562 VUserLog("Done.");
2563 });
2564 }
2565 catch (VuoException &e)
2566 {
2567 if (vuoFrameworkInProgressPath.empty())
2568 VUserLog("Warning: Couldn't use %s for the \"faster build\" optimization: %s", cacheDescription.c_str(), e.what());
2569 }
2570}
2571
2575void VuoCompiler::Environment::waitForModuleCachesToBuild(void)
2576{
2577 dispatch_sync(moduleCacheBuildingQueue, ^{});
2578}
2579
2589bool VuoCompiler::Environment::findInModuleCache(const string &moduleOrDependency, string &cachePath)
2590{
2591 if (isModuleCacheAvailable && moduleCacheContents.find(moduleOrDependency) != moduleCacheContents.end())
2592 {
2593 cachePath = currentModuleCacheDylib;
2594 return true;
2595 }
2596
2597 return false;
2598}
2599
2603string VuoCompiler::Environment::getCurrentModuleCacheDylib(void)
2604{
2605 return currentModuleCacheDylib;
2606}
2607
2612unsigned long VuoCompiler::Environment::getLastModuleCacheRebuild(void)
2613{
2614 return lastModuleCacheRebuild;
2615}
2616
2623void VuoCompiler::Environment::modulesChanged(void)
2624{
2625 isModuleCacheableDataDirty = true;
2626 isModuleCacheAvailable = false;
2627}
2628
2632bool VuoCompiler::Environment::isBuiltInOriginal()
2633{
2634 return builtIn && ! generated;
2635}
2636
2640bool VuoCompiler::Environment::isBuiltIn()
2641{
2642 return builtIn;
2643}
2644
2648bool VuoCompiler::Environment::isGenerated()
2649{
2650 return generated;
2651}
2652
2656string VuoCompiler::Environment::getName()
2657{
2658 if (isBuiltInOriginal())
2659 return "builtin";
2660 else if (this == sharedEnvironments[target][0][1])
2661 return "builtin (generated)";
2662 else if (this == sharedEnvironments[target][1][0])
2663 return "system";
2664 else if (this == sharedEnvironments[target][1][1])
2665 return "system (generated)";
2666 else if (this == sharedEnvironments[target][2][0])
2667 return "user";
2668 else if (this == sharedEnvironments[target][2][1])
2669 return "user (generated)";
2670 return "composition-local";
2671}
2672
2676void VuoCompiler::applyToInstalledEnvironments(void (^doForEnvironment)(Environment *))
2677{
2678 dispatch_sync(environmentQueue, ^{
2679 for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i) {
2680 doForEnvironment((*i)[0]);
2681 }
2682 });
2683}
2684
2688void VuoCompiler::applyToInstalledEnvironments(void (^doForEnvironment)(Environment *, bool *, string), bool *result, string arg)
2689{
2690 dispatch_sync(environmentQueue, ^{
2691 for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i) {
2692 doForEnvironment((*i)[0], result, arg);
2693 }
2694 });
2695}
2696
2700void VuoCompiler::applyToAllEnvironments(void (^doForEnvironment)(Environment *environment))
2701{
2702 dispatch_sync(environmentQueue, ^{
2703 for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i) {
2704 for (vector<Environment *>::iterator j = i->begin(); j != i->end(); ++j) {
2705 doForEnvironment(*j);
2706 }
2707 }
2708 });
2709}
2710
2724VuoCompiler::VuoCompiler(const string &compositionPath, string target)
2725{
2726 if (target.empty())
2727 {
2728 this->target = target = getProcessTarget();
2729 VDebugLog("%p target=%s (from current process)", this, this->target.c_str());
2730 }
2731 else
2732 {
2733 this->target = target;
2734 VDebugLog("%p target=%s", this, this->target.c_str());
2735 }
2736
2737#if VUO_PRO
2738 init_Pro();
2739#endif
2740
2741 shouldLoadAllModules = true;
2742 hasLoadedAllModules = false;
2743 modulesToLoadQueue = dispatch_queue_create("org.vuo.compiler.modules", NULL);
2744 moduleSourceCompilersExist = dispatch_group_create();
2745 moduleCacheBuilding = dispatch_group_create();
2746 dependencyGraph = NULL;
2747 compositionDependencyGraph = NULL;
2748 isVerbose = false;
2749 _shouldShowSplashWindow = false;
2750 delegate = NULL;
2751 delegateQueue = dispatch_queue_create("org.vuo.compiler.delegate", NULL);
2752
2753 string vuoFrameworkPath = getVuoFrameworkPath();
2754 if (! vuoFrameworkPath.empty())
2755 clangPath = vuoFrameworkPath + "/Helpers/clang";
2756 else
2757 clangPath = LLVM_ROOT "/bin/clang";
2758
2759 dispatch_sync(environmentQueue, ^{
2760 allCompilers.insert(this);
2761
2762 if (sharedEnvironments[target].empty())
2763 {
2764 sharedEnvironments[target] = vector< vector<Environment *> >(3, vector<Environment *>(2, NULL));
2765 for (vector< vector<Environment *> >::iterator i = sharedEnvironments[target].begin(); i != sharedEnvironments[target].end(); ++i)
2766 for (vector<Environment *>::iterator j = i->begin(); j != i->end(); ++j)
2767 *j = new Environment(this->target, i == sharedEnvironments[target].begin(), j != i->begin());
2768
2769 vector<string> builtInModuleSearchPaths = Environment::getBuiltInModuleSearchPaths();
2770 for (vector<string>::iterator i = builtInModuleSearchPaths.begin(); i != builtInModuleSearchPaths.end(); ++i)
2771 sharedEnvironments[target][0][0]->addModuleSearchPath(*i, false);
2772
2773 vector<string> builtInHeaderSearchPaths = Environment::getBuiltInHeaderSearchPaths();
2774 for (vector<string>::iterator i = builtInHeaderSearchPaths.begin(); i != builtInHeaderSearchPaths.end(); ++i)
2775 sharedEnvironments[target][0][0]->addHeaderSearchPath(*i);
2776
2777 vector<string> builtInLibrarySearchPaths = Environment::getBuiltInLibrarySearchPaths();
2778 for (vector<string>::iterator i = builtInLibrarySearchPaths.begin(); i != builtInLibrarySearchPaths.end(); ++i)
2779 sharedEnvironments[target][0][0]->addLibrarySearchPath(*i);
2780
2781 vector<string> builtInFrameworkSearchPaths = Environment::getBuiltInFrameworkSearchPaths();
2782 for (vector<string>::iterator i = builtInFrameworkSearchPaths.begin(); i != builtInFrameworkSearchPaths.end(); ++i)
2783 sharedEnvironments[target][0][0]->addFrameworkSearchPath(*i);
2784
2785 // Allow system administrator to override Vuo.framework modules
2786 sharedEnvironments[target][1][0]->addModuleSearchPath(VuoFileUtilities::getSystemModulesPath());
2787 sharedEnvironments[target][1][0]->addLibrarySearchPath(VuoFileUtilities::getSystemModulesPath());
2788
2789 // Allow user to override Vuo.framework and system-wide modules
2790 sharedEnvironments[target][2][0]->addModuleSearchPath(VuoFileUtilities::getUserModulesPath());
2791 sharedEnvironments[target][2][0]->addLibrarySearchPath(VuoFileUtilities::getUserModulesPath());
2792
2793 // Set up module cache paths.
2794 // Since the built-in module caches are part of Vuo.framework (put there by `generateBuiltInModuleCaches`),
2795 // only attempt to use module caches if Vuo.framework exists.
2796 string vuoFrameworkPath = getVuoFrameworkPath();
2797 if (! vuoFrameworkPath.empty())
2798 {
2799 vector<string> moduleCachePaths(3);
2800 moduleCachePaths[0] = vuoFrameworkPath + "/Modules/Builtin";
2801 moduleCachePaths[1] = VuoFileUtilities::getCachePath() + "/System";
2802 moduleCachePaths[2] = VuoFileUtilities::getCachePath() + "/User";
2803
2804 for (size_t i = 0; i < moduleCachePaths.size(); ++i)
2805 {
2806 string moduleCachePath = moduleCachePaths[i];
2807
2808 sharedEnvironments[target][i][0]->setModuleCachePath(moduleCachePath, true);
2809 sharedEnvironments[target][i][1]->setModuleCachePath(moduleCachePath, false);
2810 }
2811 }
2812 }
2813 });
2814
2815 setCompositionPath(compositionPath);
2816}
2817
2822{
2823#if VUO_PRO
2824 fini_Pro();
2825#endif
2826
2827 dispatch_sync(environmentQueue, ^{
2828 allCompilers.erase(this);
2829 });
2830
2831 dispatch_group_wait(moduleCacheBuilding, DISPATCH_TIME_FOREVER);
2832 dispatch_release(moduleCacheBuilding);
2833
2834 dispatch_group_wait(moduleSourceCompilersExist, DISPATCH_TIME_FOREVER);
2835 dispatch_release(moduleSourceCompilersExist);
2836
2837 for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
2838 (*i)[0]->removeCompilerToNotify(this);
2839
2840 dispatch_sync(delegateQueue, ^{});
2841
2842 dispatch_release(modulesToLoadQueue);
2843 dispatch_release(delegateQueue);
2844
2845 delete dependencyGraph;
2846 delete compositionDependencyGraph;
2847}
2848
2852void VuoCompiler::reset(void)
2853{
2854 dispatch_group_wait(moduleSourceCompilersExistGlobally, DISPATCH_TIME_FOREVER);
2855
2856 dispatch_sync(environmentQueue, ^{
2857
2858 for (auto e : sharedEnvironments)
2859 for (vector< vector<Environment *> >::iterator i = e.second.begin(); i != e.second.end(); ++i)
2860 {
2861 (*i)[0]->stopWatchingModuleSearchPaths();
2862 dispatch_sync((*i)[0]->moduleSearchPathContentsChangedQueue, ^{});
2863 }
2864
2865 for (auto e : environmentsForCompositionFamily)
2866 for (map< string, vector<Environment *> >::iterator i = e.second.begin(); i != e.second.end(); ++i)
2867 {
2868 (i->second)[0]->stopWatchingModuleSearchPaths();
2869 dispatch_sync((i->second)[0]->moduleSearchPathContentsChangedQueue, ^{});
2870 }
2871
2872 for (auto e : sharedEnvironments)
2873 for (vector< vector<Environment *> >::iterator i = e.second.begin(); i != e.second.end(); ++i)
2874 for (vector<Environment *>::iterator j = i->begin(); j != i->end(); ++j)
2875 delete *j;
2876
2877 for (auto e : environmentsForCompositionFamily)
2878 for (map< string, vector<Environment *> >::iterator i = e.second.begin(); i != e.second.end(); ++i)
2879 for (vector<Environment *>::iterator j = i->second.begin(); j != i->second.end(); ++j)
2880 delete *j;
2881
2882 allCompilers.clear();
2883 sharedEnvironments.clear();
2884 environmentsForCompositionFamily.clear();
2885
2886 });
2887}
2888
2895{
2896 dispatch_async(delegateQueue, ^{
2897 this->delegate = delegate;
2898 });
2899}
2900
2910void VuoCompiler::setCompositionPath(const string &compositionPath)
2911{
2912 string compositionModulesDir;
2913 string compositionBaseDir;
2914 lastCompositionIsSubcomposition = false;
2915 if (! compositionPath.empty())
2916 {
2917 compositionModulesDir = VuoFileUtilities::getCompositionLocalModulesPath(compositionPath);
2918
2919 string file, ext;
2920 VuoFileUtilities::splitPath(compositionModulesDir, compositionBaseDir, file, ext);
2921 VuoFileUtilities::canonicalizePath(compositionBaseDir);
2922
2923 string compositionDir;
2924 VuoFileUtilities::splitPath(compositionPath, compositionDir, file, ext);
2925 VuoFileUtilities::canonicalizePath(compositionDir);
2926 lastCompositionIsSubcomposition = (compositionDir == compositionModulesDir);
2927 }
2928
2929 // Set up `environments` to contain all environments available to this compiler, in order from broadest to narrowest.
2930
2931 dispatch_sync(environmentQueue, ^{
2932 if (! environments.empty() && compositionBaseDir == lastCompositionBaseDir) {
2933 return;
2934 }
2935 lastCompositionBaseDir = compositionBaseDir;
2936
2937 // Clear out the existing environments for this compiler.
2938
2939 Environment *oldCompositionFamilyInstalledEnvironment = nullptr;
2940 vector<Environment *> compositionEnvironments;
2941 if (! environments.empty())
2942 {
2943 for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i) {
2944 (*i)[0]->removeCompilerToNotify(this);
2945 }
2946
2947 if (environments.size() >= 5) {
2948 oldCompositionFamilyInstalledEnvironment = environments[3][0];
2949 }
2950
2951 compositionEnvironments = environments.back();
2952
2953 environments.clear();
2954 }
2955
2956 // If the composition is located in one of the shared environments (built-in, system, user),
2957 // add that shared environment and any shared environments at broader scope to the compiler.
2958 // If the composition is not in a shared environment, add all of the shared environments to the compiler.
2959
2960 bool isCompositionInSharedEnvironment = false;
2961 for (vector< vector<Environment *> >::iterator i = sharedEnvironments[target].begin(); i != sharedEnvironments[target].end(); ++i)
2962 {
2963 environments.push_back(*i);
2964
2965 vector<string> moduleSearchPaths = (*i)[0]->getModuleSearchPaths();
2966 for (vector<string>::iterator j = moduleSearchPaths.begin(); j != moduleSearchPaths.end(); ++j)
2967 {
2968 string moduleSearchPath = *j;
2969 VuoFileUtilities::canonicalizePath(moduleSearchPath);
2970 if (moduleSearchPath == compositionModulesDir)
2971 {
2972 isCompositionInSharedEnvironment = true;
2973 break;
2974 }
2975 }
2976
2977 if (isCompositionInSharedEnvironment) {
2978 break;
2979 }
2980 }
2981
2982 // If the composition is not in a shared environment, add the composition-family environment to the compiler.
2983
2984 Environment *newCompositionFamilyInstalledEnvironment = nullptr;
2985 if (! isCompositionInSharedEnvironment && ! compositionPath.empty())
2986 {
2987 vector<Environment *> compositionFamilyEnvironments = environmentsForCompositionFamily[target][compositionBaseDir];
2988 if (compositionFamilyEnvironments.empty())
2989 {
2990 compositionFamilyEnvironments = vector<Environment *>(2, NULL);
2991 compositionFamilyEnvironments[0] = new Environment(this->target, false, false);
2992 compositionFamilyEnvironments[1] = new Environment(this->target, false, true);
2993 environmentsForCompositionFamily[target][compositionBaseDir] = compositionFamilyEnvironments;
2994
2995 // Allow the user to place modules/subcompositions in a Modules folder inside the composition folder.
2996
2997 compositionFamilyEnvironments[0]->addModuleSearchPath(compositionModulesDir);
2998
2999 // Locate this environment's cache in a folder whose name is the composition folder path with
3000 // slashes replaced with Unicode Modifier Letter Colon.
3001 string moduleCachePath = getCachePathForComposition(compositionBaseDir);
3002
3003 compositionFamilyEnvironments[0]->setModuleCachePath(moduleCachePath, true);
3004 compositionFamilyEnvironments[1]->setModuleCachePath(moduleCachePath, false);
3005 }
3006 environments.push_back(compositionFamilyEnvironments);
3007
3008 newCompositionFamilyInstalledEnvironment = compositionFamilyEnvironments[0];
3009 }
3010
3011 // Add the composition environment to the compiler (or add it back in if it already existed).
3012
3013 if (compositionEnvironments.empty())
3014 {
3015 compositionEnvironments = vector<Environment *>(2, NULL);
3016 compositionEnvironments[0] = new Environment(this->target, false, false);
3017 compositionEnvironments[1] = new Environment(this->target, false, true);
3018 }
3019 environments.push_back(compositionEnvironments);
3020
3021 for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i) {
3022 (*i)[0]->addCompilerToNotify(this);
3023 }
3024
3025 delete dependencyGraph;
3026 delete compositionDependencyGraph;
3027 dependencyGraph = makeDependencyNetwork(environments, ^VuoDirectedAcyclicGraph * (Environment *env) { return env->getDependencyGraph(); });
3028 compositionDependencyGraph = makeDependencyNetwork(environments, ^VuoDirectedAcyclicGraph * (Environment *env) { return env->getCompositionDependencyGraph(); });
3029
3030 // If the compiler has a different local Modules directory than before, notify the compiler's delegate
3031 // of composition-family modules that are newly available/unavailable.
3032
3033 if (oldCompositionFamilyInstalledEnvironment != newCompositionFamilyInstalledEnvironment)
3034 {
3035 auto getModules = [] (Environment *env)
3036 {
3037 map<string, VuoCompilerModule *> modules;
3038 if (env)
3039 {
3040 for (auto i : env->getNodeClasses()) {
3041 modules.insert(i);
3042 }
3043 for (auto i : env->getTypes()) {
3044 modules.insert(i);
3045 }
3046 for (auto i : env->getLibraryModules()) {
3047 modules.insert(i);
3048 }
3049 }
3050 return modules;
3051 };
3052
3053 map<string, VuoCompilerModule *> modulesAdded = getModules(newCompositionFamilyInstalledEnvironment);
3054 map<string, VuoCompilerModule *> modulesRemoved = getModules(oldCompositionFamilyInstalledEnvironment);
3055
3056 map<string, pair<VuoCompilerModule *, VuoCompilerModule *> > modulesModified;
3057 for (map<string, VuoCompilerModule *>::iterator add = modulesAdded.begin(); add != modulesAdded.end(); )
3058 {
3059 map<string, VuoCompilerModule *>::iterator rem = modulesRemoved.find(add->first);
3060 if (rem != modulesRemoved.end())
3061 {
3062 modulesModified[add->first] = make_pair(rem->second, add->second);
3063 modulesAdded.erase(add++);
3064 modulesRemoved.erase(rem);
3065 }
3066 else
3067 {
3068 ++add;
3069 }
3070 }
3071
3072 if (! (modulesAdded.empty() && modulesModified.empty() && modulesRemoved.empty()) )
3073 {
3074 VuoCompilerIssues *issues = new VuoCompilerIssues();
3075 VuoCompilerDelegate::LoadedModulesData *delegateData = new VuoCompilerDelegate::LoadedModulesData(set< pair<VuoCompilerModule *, VuoCompilerModule *> >(), set<VuoCompilerModule *>(), issues);
3076 delegateData->retain();
3077
3078 Environment *scopeEnvironment = newCompositionFamilyInstalledEnvironment;
3079 if (! scopeEnvironment) {
3080 scopeEnvironment = compositionEnvironments.at(0);
3081 }
3082
3083 loadedModules(modulesAdded, modulesModified, modulesRemoved, issues, delegateData, scopeEnvironment);
3084 }
3085 }
3086 });
3087}
3088
3094VuoDirectedAcyclicNetwork * VuoCompiler::makeDependencyNetwork(const vector< vector<Environment *> > &environments,
3095 VuoDirectedAcyclicGraph * (^graphForEnvironment)(Environment *))
3096{
3097 if (!graphForEnvironment)
3098 return NULL;
3099
3101
3102 for (vector< vector<Environment *> >::const_iterator i = environments.begin(); i != environments.end(); ++i)
3103 {
3104 // Installed environment depends on generated environment in same scope.
3105
3106 network->addEdge(graphForEnvironment(i->at(0)), graphForEnvironment(i->at(1)));
3107
3108 // Installed and generated environments depend on installed environments in broader scopes.
3109
3110 for (vector< vector<Environment *> >::const_iterator ii = environments.begin(); ii != i; ++ii)
3111 {
3112 network->addEdge(graphForEnvironment(i->at(0)), graphForEnvironment(ii->at(0)));
3113 network->addEdge(graphForEnvironment(i->at(1)), graphForEnvironment(ii->at(0)));
3114 }
3115 }
3116
3117 return network;
3118}
3119
3140void VuoCompiler::loadModulesIfNeeded(const set<string> &moduleKeys)
3141{
3142 __block bool willLoadAllModules = false;
3143 if (moduleKeys.empty())
3144 {
3145 dispatch_sync(modulesToLoadQueue, ^{
3146 if (shouldLoadAllModules && ! hasLoadedAllModules) {
3147 willLoadAllModules = true;
3148 hasLoadedAllModules = true;
3149 }
3150 });
3151 }
3152
3153 if (! willLoadAllModules && moduleKeys.empty())
3154 return;
3155
3156 // Load modules and start sources compiling.
3157
3158 __block set<dispatch_group_t> sourcesLoading;
3159 dispatch_sync(environmentQueue, ^{
3160 sourcesLoading = loadModulesAndSources(moduleKeys, set<string>(), set<string>(),
3161 moduleKeys, set<string>(), set<string>(),
3162 willLoadAllModules, false, nullptr, nullptr, nullptr, "");
3163 });
3164
3165 // Wait for subcompositions and specialized node classes to finish compiling and their modules to be loaded
3166 // to ensure that the next call to getNodeClass() will return them.
3167
3168 for (set<dispatch_group_t>::iterator i = sourcesLoading.begin(); i != sourcesLoading.end(); ++i)
3169 {
3170 dispatch_group_wait(*i, DISPATCH_TIME_FOREVER);
3171 dispatch_release(*i);
3172 }
3173}
3174
3183set<dispatch_group_t> VuoCompiler::loadModulesAndSources(const set<string> &modulesAddedKeys, const set<string> &modulesModifiedKeys, const set<string> &modulesRemovedKeys,
3184 const set<string> &sourcesAddedKeys, const set<string> &sourcesModifiedKeys, const set<string> &sourcesRemovedKeys,
3185 bool willLoadAllModules, bool shouldRecompileSourcesIfUnchanged,
3186 Environment *currentEnvironment, VuoCompilerIssues *issuesForCurrentEnvironment,
3187 std::function<void(void)> moduleLoadedCallback, const string &moduleAddedOrModifiedSourceCode)
3188{
3189 //VLog("C=%p E=%p -- %lu %lu %lu %lu %lu %lu %i %i", this, currentEnvironment,
3190 //modulesAddedKeys.size(), modulesModifiedKeys.size(), modulesRemovedKeys.size(),
3191 //sourcesAddedKeys.size(), sourcesModifiedKeys.size(), sourcesRemovedKeys.size(),
3192 //willLoadAllModules, shouldRecompileSourcesIfUnchanged);
3193 //if (modulesAddedKeys.size() == 1) VLog(" %s", modulesAddedKeys.begin()->c_str());
3194 //if (modulesModifiedKeys.size() == 1) VLog(" %s", modulesModifiedKeys.begin()->c_str());
3195 //if (modulesRemovedKeys.size() == 1) VLog(" %s", modulesRemovedKeys.begin()->c_str());
3196
3197 // Organize the modules, source files, and issues by environment.
3198
3199 map<Environment *, set<string> > modulesAdded;
3200 map<Environment *, set<string> > modulesModified;
3201 map<Environment *, set<string> > modulesRemoved;
3202 map<Environment *, set<string> > sourcesAdded;
3203 map<Environment *, set<string> > sourcesModified;
3204 map<Environment *, set<string> > sourcesRemoved;
3205 map<Environment *, set<string> > potentialSpecializedModules;
3206
3207 if (currentEnvironment)
3208 {
3209 modulesAdded[currentEnvironment] = modulesAddedKeys;
3210 modulesModified[currentEnvironment] = modulesModifiedKeys;
3211 modulesRemoved[currentEnvironment] = modulesRemovedKeys;
3212 sourcesAdded[currentEnvironment] = sourcesAddedKeys;
3213 sourcesModified[currentEnvironment] = sourcesModifiedKeys;
3214 sourcesRemoved[currentEnvironment] = sourcesRemovedKeys;
3215 }
3216 else
3217 {
3218 Environment *genEnv = nullptr;
3219 if (! willLoadAllModules)
3220 {
3221 // If compiling a top-level composition, generated modules that were directly requested (in modulesAddedKeys) are
3222 // added at the composition scope so they won't be cached. This prevents link errors when running multiple
3223 // compositions in the current process (https://b33p.net/kosada/node/14317).
3224
3225 int scope = environments.size() - (lastCompositionIsSubcomposition ? 2 : 1);
3226 genEnv = environments.at(scope).at(1);
3227 potentialSpecializedModules[genEnv] = modulesAddedKeys;
3228 }
3229
3230 for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3231 {
3232 Environment *env = (*i).at(0);
3233
3234 ModuleInfoIterator modulesAddedIter = (willLoadAllModules ? env->listAllModules() : env->listModules(modulesAddedKeys));
3235 ModuleInfoIterator sourcesAddedIter = (willLoadAllModules ? env->listAllSourceFiles() : env->listSourceFiles(sourcesAddedKeys));
3236 ModuleInfoIterator sourcesModifiedIter = (willLoadAllModules ? env->listAllSourceFiles() : env->listSourceFiles(sourcesModifiedKeys));
3237
3238 ModuleInfo *moduleInfo;
3239 while ((moduleInfo = modulesAddedIter.next()))
3240 {
3241 string moduleKey = moduleInfo->getModuleKey();
3242
3243 modulesAdded[env].insert(moduleKey);
3244
3245 if (! willLoadAllModules)
3246 {
3247 auto foundIter = potentialSpecializedModules[genEnv].find(moduleKey);
3248 if (foundIter != potentialSpecializedModules[genEnv].end())
3249 potentialSpecializedModules[genEnv].erase(foundIter);
3250 }
3251 }
3252
3253 // If a source file and a compiled file for the same module are in the same folder,
3254 // the compiled file takes precedence.
3255 auto isCompiledModuleAtSameSearchPath = [&env] (ModuleInfo *sourceInfo)
3256 {
3257 ModuleInfo *compiledModuleInfo = env->listModule(sourceInfo->getModuleKey());
3258 return (compiledModuleInfo && compiledModuleInfo->getSearchPath() == sourceInfo->getSearchPath());
3259 };
3260
3261 while ((moduleInfo = sourcesAddedIter.next()))
3262 {
3263 if (isCompiledModuleAtSameSearchPath(moduleInfo))
3264 continue;
3265
3266 sourcesAdded[env].insert( moduleInfo->getModuleKey() );
3267 }
3268
3269 while ((moduleInfo = sourcesModifiedIter.next()))
3270 {
3271 if (isCompiledModuleAtSameSearchPath(moduleInfo))
3272 continue;
3273
3274 sourcesModified[env].insert( moduleInfo->getModuleKey() );
3275 }
3276 }
3277 }
3278
3279 map<Environment *, VuoCompilerIssues *> issues;
3280 for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3281 {
3282 Environment *env = (*i).at(0);
3283 issues[env] = (env == currentEnvironment && issuesForCurrentEnvironment ? issuesForCurrentEnvironment : new VuoCompilerIssues());
3284 }
3285
3286 // Check for circular dependencies in added/modified sources.
3287
3288 for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3289 {
3290 Environment *env = (*i).at(0);
3291
3292 // Check for circular dependencies involving sources being loaded within this environment.
3293 // For circular dependencies involving sources in different environments,
3294 // an error will be reported elsewhere due to one of them being outside of the other's scope.
3295 set<VuoDirectedAcyclicGraph::Vertex *> circularDependencies = env->getCompositionDependencyGraph()->getCycleVertices();
3296
3297 set<string> sourcesAddedModified;
3298 sourcesAddedModified.insert(sourcesAdded[env].begin(), sourcesAdded[env].end());
3299 sourcesAddedModified.insert(sourcesModified[env].begin(), sourcesModified[env].end());
3300
3301 for (set<string>::iterator j = sourcesAddedModified.begin(); j != sourcesAddedModified.end(); ++j)
3302 {
3303 string moduleKey = *j;
3304
3305 bool found = false;
3306 for (set<VuoDirectedAcyclicGraph::Vertex *>::iterator k = circularDependencies.begin(); k != circularDependencies.end(); ++k)
3307 {
3308 DependencyGraphVertex *vertex = static_cast<DependencyGraphVertex *>(*k);
3309 if (vertex->getDependency() == moduleKey)
3310 {
3311 found = true;
3312 break;
3313 }
3314 }
3315
3316 if (found)
3317 {
3318 sourcesAdded[env].erase(moduleKey);
3319 sourcesModified[env].erase(moduleKey);
3320
3321 VuoCompilerIssue issue(VuoCompilerIssue::Error, "compiling subcomposition", moduleKey,
3322 "Subcomposition contains itself",
3323 "%moduleKey contains an instance of itself, "
3324 "or contains another subcomposition that contains an instance of %moduleKey.");
3325 issue.setModuleKey(moduleKey);
3326
3327 ModuleInfo *sourceInfo = env->listSourceFile(moduleKey);
3328 if (sourceInfo)
3329 issue.setFilePath(sourceInfo->getFile()->path());
3330
3331 issues[env]->append(issue);
3332 }
3333 }
3334 }
3335
3336 // Update the longest downstream paths for added/modified sources.
3337
3338 for (const vector<Environment *> &envs : environments)
3339 {
3340 Environment *env = envs.at(0);
3341
3342 set<string> sourcesAddedModified;
3343 sourcesAddedModified.insert(sourcesAdded[env].begin(), sourcesAdded[env].end());
3344 sourcesAddedModified.insert(sourcesModified[env].begin(), sourcesModified[env].end());
3345
3346 for (const string &moduleKey : sourcesAddedModified)
3347 {
3348 VuoDirectedAcyclicGraph::Vertex *vertex = env->getCompositionDependencyGraph()->findVertex(moduleKey);
3349 int pathLength = env->getCompositionDependencyGraph()->getLongestDownstreamPath(vertex);
3350
3351 ModuleInfo *sourceInfo = env->listSourceFile(moduleKey);
3352 sourceInfo->setLongestDownstreamPath(pathLength);
3353 }
3354 }
3355
3356 // Find all modules and sources that depend on the modules and sources being modified or removed.
3357 // Those that belong to one of this compiler's environments are used in subsequent stages.
3358 // Those that belong to some other environment are scheduled to be handled by a separate call to this function.
3359
3360 map<Environment *, set<string> > modulesDepOnModulesModified;
3361 map<Environment *, set<string> > sourcesDepOnModulesModified;
3362 map<Environment *, set<string> > modulesDepOnModulesRemoved;
3363 map<Environment *, set<string> > sourcesDepOnModulesRemoved;
3364
3365 {
3366 __block map<Environment *, set<string> > modulesDepOnModulesModified_otherCompiler;
3367 __block map<Environment *, set<string> > sourcesDepOnModulesModified_otherCompiler;
3368 __block map<Environment *, set<string> > modulesDepOnModulesRemoved_otherCompiler;
3369 __block map<Environment *, set<string> > sourcesDepOnModulesRemoved_otherCompiler;
3370
3371 vector<VuoDirectedAcyclicNetwork *> searchDependencyGraphs;
3372 for (VuoCompiler *compiler : allCompilers)
3373 searchDependencyGraphs.push_back(compiler->dependencyGraph);
3374
3375 VuoDirectedAcyclicGraph *currentEnvironmentDependencyGraph = (currentEnvironment ? currentEnvironment->getDependencyGraph() : nullptr);
3376
3377 findDependentModulesAndSources(modulesModified, searchDependencyGraphs, currentEnvironmentDependencyGraph,
3378 modulesDepOnModulesModified, modulesDepOnModulesModified_otherCompiler,
3379 sourcesDepOnModulesModified, sourcesDepOnModulesModified_otherCompiler);
3380
3381 findDependentModulesAndSources(modulesRemoved, searchDependencyGraphs, currentEnvironmentDependencyGraph,
3382 modulesDepOnModulesRemoved, modulesDepOnModulesRemoved_otherCompiler,
3383 sourcesDepOnModulesRemoved, sourcesDepOnModulesRemoved_otherCompiler);
3384
3385 set<Environment *> otherEnvironments;
3386 for (map<Environment *, set<string> >::iterator i = modulesDepOnModulesModified_otherCompiler.begin(); i != modulesDepOnModulesModified_otherCompiler.end(); ++i)
3387 otherEnvironments.insert(i->first);
3388 for (map<Environment *, set<string> >::iterator i = sourcesDepOnModulesModified_otherCompiler.begin(); i != sourcesDepOnModulesModified_otherCompiler.end(); ++i)
3389 otherEnvironments.insert(i->first);
3390 for (map<Environment *, set<string> >::iterator i = modulesDepOnModulesRemoved_otherCompiler.begin(); i != modulesDepOnModulesRemoved_otherCompiler.end(); ++i)
3391 otherEnvironments.insert(i->first);
3392 for (map<Environment *, set<string> >::iterator i = sourcesDepOnModulesRemoved_otherCompiler.begin(); i != sourcesDepOnModulesRemoved_otherCompiler.end(); ++i)
3393 otherEnvironments.insert(i->first);
3394
3395 for (Environment *env : otherEnvironments)
3396 {
3397 VuoCompiler *otherCompiler = nullptr;
3398 for (VuoCompiler *c : allCompilers)
3399 for (const vector<Environment *> &ee : c->environments)
3400 for (Environment *e : ee)
3401 if (e == env)
3402 {
3403 otherCompiler = c;
3404 goto foundOtherCompiler;
3405 }
3406 foundOtherCompiler:
3407
3408 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
3409 dispatch_sync(environmentQueue, ^{
3410 otherCompiler->loadModulesAndSources(set<string>(), modulesDepOnModulesModified_otherCompiler[env], modulesDepOnModulesRemoved_otherCompiler[env],
3411 set<string>(), sourcesDepOnModulesModified_otherCompiler[env], sourcesDepOnModulesRemoved_otherCompiler[env],
3412 false, true, env, nullptr, nullptr, "");
3413 });
3414 });
3415 }
3416 }
3417
3418 // Unload:
3419 // - modules that have been removed or modified
3420 // - modules that depend on them
3421
3422 map<Environment *, set<VuoCompilerModule *> > actualModulesRemoved;
3423 for (const vector<Environment *> &envs : environments)
3424 {
3425 for (Environment *env : envs)
3426 {
3427 set<string> modulesToUnload;
3428 modulesToUnload.insert(modulesRemoved[env].begin(), modulesRemoved[env].end());
3429 modulesToUnload.insert(modulesModified[env].begin(), modulesModified[env].end());
3430 modulesToUnload.insert(modulesDepOnModulesRemoved[env].begin(), modulesDepOnModulesRemoved[env].end());
3431 modulesToUnload.insert(modulesDepOnModulesModified[env].begin(), modulesDepOnModulesModified[env].end());
3432
3433 actualModulesRemoved[env] = env->unloadCompiledModules(modulesToUnload);
3434
3435 if (!env->isBuiltInOriginal() && !actualModulesRemoved[env].empty())
3436 {
3437 set<string> actualModulesRemovedKeys;
3438 for (auto m : actualModulesRemoved[env])
3439 actualModulesRemovedKeys.insert(m->getPseudoBase()->getModuleKey());
3440 VUserLog("Removed from %s environment: %s", env->getName().c_str(), VuoStringUtilities::join(actualModulesRemovedKeys, ", ").c_str());
3441 }
3442 }
3443 }
3444
3445 // Load:
3446 // - modules that have been added or modified
3447 // - modules that they depend on
3448 // - modules that depend on them that were just unloaded
3449 // Delete:
3450 // - cached module files in `modulesToLoad` whose source files have been removed
3451
3452 map<Environment *, set<string> > modulesToLoad;
3453 map<Environment *, map<string, string> > modulesToLoadSourceCode;
3454 for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3455 {
3456 Environment *env = (*i).at(0);
3457
3458 if (! modulesAdded[env].empty())
3459 modulesToLoad[env].insert(modulesAdded[env].begin(), modulesAdded[env].end());
3460 if (! modulesModified[env].empty())
3461 modulesToLoad[env].insert(modulesModified[env].begin(), modulesModified[env].end());
3462 if (! modulesDepOnModulesModified[env].empty())
3463 modulesToLoad[env].insert(modulesDepOnModulesModified[env].begin(), modulesDepOnModulesModified[env].end());
3464
3465 if (env == currentEnvironment && moduleLoadedCallback)
3466 {
3467 if (modulesAdded[env].size() == 1)
3468 modulesToLoadSourceCode[env][*modulesAdded[env].begin()] = moduleAddedOrModifiedSourceCode;
3469 else if (modulesModified[env].size() == 1)
3470 modulesToLoadSourceCode[env][*modulesModified[env].begin()] = moduleAddedOrModifiedSourceCode;
3471 }
3472 }
3473
3474 map<Environment *, set<VuoCompilerModule *> > actualModulesAdded;
3475 while (! modulesToLoad.empty())
3476 {
3477 set<string> dependenciesToLoad;
3478 map<Environment *, set<string> > potentialSpecializedDependencies;
3479 for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3480 {
3481 Environment *env = (*i).at(0);
3482 Environment *genEnv = (*i).at(1);
3483
3484 set<VuoCompilerModule *> actualModulesLoaded = env->loadCompiledModules(modulesToLoad[env], modulesToLoadSourceCode[env]);
3485
3486 actualModulesAdded[env].insert(actualModulesLoaded.begin(), actualModulesLoaded.end());
3487 modulesToLoad.erase(env);
3488
3489 for (set<VuoCompilerModule *>::iterator j = actualModulesLoaded.begin(); j != actualModulesLoaded.end(); ++j)
3490 {
3491 set<string> dependencies = (*j)->getDependencies();
3492 dependenciesToLoad.insert(dependencies.begin(), dependencies.end());
3493 potentialSpecializedDependencies[genEnv].insert(dependencies.begin(), dependencies.end());
3494 }
3495
3496 if (!env->isBuiltInOriginal() && !actualModulesLoaded.empty())
3497 {
3498 map<string, string> actualFilesAndHashesLoaded;
3499 for (auto module : actualModulesLoaded)
3500 {
3501 string path, hash;
3502 if (module->getPseudoBase()->getNodeSet())
3503 path = module->getPseudoBase()->getNodeSet()->getArchivePath();
3504 else
3505 {
3506 auto cnc = dynamic_cast<VuoCompilerNodeClass *>(module);
3507 if (cnc && !cnc->getSourcePath().empty())
3508 {
3509 path = cnc->getSourcePath();
3510 if (!cnc->getSourceCode().empty())
3511 // Use the latest source code, if it's been modified without saving.
3512 hash = VuoStringUtilities::calculateSHA256(cnc->getSourceCode());
3513 }
3514 else
3515 path = module->getModulePath();
3516 }
3517
3518 if (hash.empty())
3519 try
3520 {
3522 }
3523 catch (VuoException &e) {}
3524
3525 actualFilesAndHashesLoaded[path] = hash;
3526 }
3527
3528 for (pair<string, string> item : actualFilesAndHashesLoaded)
3529 VUserLog("Loaded into %s environment: [%8.8s] %s", env->getName().c_str(), item.second.c_str(), item.first.c_str());
3530 }
3531 }
3532
3533 for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3534 {
3535 Environment *env = (*i).at(0);
3536
3537 ModuleInfoIterator dependenciesInEnv = env->listModules(dependenciesToLoad);
3538 ModuleInfo *moduleInfo;
3539 while ((moduleInfo = dependenciesInEnv.next()))
3540 {
3541 modulesToLoad[env].insert( moduleInfo->getModuleKey() );
3542
3543 for (map<Environment *, set<string> >::iterator j = potentialSpecializedDependencies.begin(); j != potentialSpecializedDependencies.end(); ++j)
3544 {
3545 auto foundIter = j->second.find( moduleInfo->getModuleKey() );
3546 if (foundIter != j->second.end())
3547 j->second.erase(foundIter);
3548 }
3549 }
3550 }
3551
3552 for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3553 {
3554 Environment *genEnv = (*i).at(1);
3555 potentialSpecializedModules[genEnv].insert(potentialSpecializedDependencies[genEnv].begin(), potentialSpecializedDependencies[genEnv].end());
3556 }
3557 }
3558
3559 // Load asynchronously:
3560 // - specializations of generic modules
3561
3562 set<dispatch_group_t> specializedModulesLoading;
3563 for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3564 {
3565 Environment *genEnv = (*i).at(1);
3566 set<dispatch_group_t> s = genEnv->loadSpecializedModules(potentialSpecializedModules[genEnv], this, llvmQueue);
3567 specializedModulesLoading.insert(s.begin(), s.end());
3568 }
3569
3570 // Notify those waiting on a source file to be compiled that its module has now been loaded.
3571
3572 if (moduleLoadedCallback)
3573 moduleLoadedCallback();
3574
3575 // Move modified modules from `actualModulesAdded` and `actualModulesRemoved` to `actualModulesModified`.
3576
3577 map<Environment *, set< pair<VuoCompilerModule *, VuoCompilerModule *> > > actualModulesModified;
3578 for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3579 {
3580 Environment *env = (*i).at(0);
3581
3582 for (set<VuoCompilerModule *>::iterator add = actualModulesAdded[env].begin(); add != actualModulesAdded[env].end(); )
3583 {
3584 set<VuoCompilerModule *>::iterator rem;
3585 for (rem = actualModulesRemoved[env].begin(); rem != actualModulesRemoved[env].end(); ++rem)
3586 if ((*rem)->getPseudoBase()->getModuleKey() == (*add)->getPseudoBase()->getModuleKey())
3587 break;
3588
3589 if (rem != actualModulesRemoved[env].end())
3590 {
3591 actualModulesModified[env].insert( make_pair(*rem, *add) );
3592 actualModulesRemoved[env].erase(rem);
3593 actualModulesAdded[env].erase(add++);
3594 }
3595 else
3596 ++add;
3597 }
3598 }
3599
3600 // Reify port types on node classes (if needed).
3601
3602 bool wereModulesAddedOrModified = false;
3603 for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3604 {
3605 Environment *env = (*i).at(0);
3606 if (! (actualModulesAdded[env].empty() && actualModulesModified[env].empty()) )
3607 {
3608 wereModulesAddedOrModified = true;
3609 break;
3610 }
3611 }
3612
3613 if (wereModulesAddedOrModified)
3614 {
3615 map<string, VuoCompilerType *> inheritedTypes;
3616 for (const vector<Environment *> &envs : environments)
3617 {
3618 for (Environment *env : envs)
3619 {
3620 env->reifyPortTypes(inheritedTypes);
3621 map<string, VuoCompilerType *> envTypes = env->getTypes();
3622 inheritedTypes.insert(envTypes.begin(), envTypes.end());
3623 }
3624 }
3625 }
3626
3627 // Delete cached compiled module files and call this function recursively for:
3628 // - modules whose source files have been removed
3629 // - modules whose source files depend on them
3630
3631 for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3632 {
3633 Environment *env = (*i).at(0);
3634
3635 set<string> sourcesToUnload;
3636 sourcesToUnload.insert(sourcesRemoved[env].begin(), sourcesRemoved[env].end());
3637 sourcesToUnload.insert(sourcesDepOnModulesRemoved[env].begin(), sourcesDepOnModulesRemoved[env].end());
3638 if (! sourcesToUnload.empty())
3639 {
3640 string moduleSearchPath = env->getModuleSearchPaths().front();
3641
3642 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
3643 VuoCompiler *otherCompiler = new VuoCompiler(moduleSearchPath + "/unused", target);
3644
3645 dispatch_sync(environmentQueue, ^{
3646 otherCompiler->loadModulesAndSources(set<string>(), set<string>(), sourcesToUnload,
3647 set<string>(), set<string>(), set<string>(),
3648 false, false, env, nullptr, nullptr, "");
3649 });
3650
3651 delete otherCompiler;
3652 });
3653 }
3654
3655 if (!env->isBuiltInOriginal() && !sourcesToUnload.empty())
3656 VUserLog("Deleting from %s environment: %s", env->getName().c_str(), VuoStringUtilities::join(sourcesToUnload, ", ").c_str());
3657
3658 env->deleteModulesCompiledFromSourceCode(sourcesToUnload);
3659 }
3660
3661 // Compile asynchronously:
3662 // - source files that have been added or modified
3663 // - source files that depend on them
3664 // - source files that depend on modules that have been modified
3665
3666 map<Environment *, set<string> > sourcesDepOnModulesAdded;
3667 {
3668 map<Environment *, set<string> > modulesDepOnModulesAdded; // unused
3669 __block map<Environment *, set<string> > modulesDepOnModulesAdded_otherCompiler; //
3670 __block map<Environment *, set<string> > sourcesDepOnModulesAdded_otherCompiler;
3671
3672 map<Environment *, set<string> > actualModuleKeysAdded;
3673 for (const vector<Environment *> &envs : environments)
3674 {
3675 Environment *env = envs.at(0);
3676 for (VuoCompilerModule *module : actualModulesAdded[env])
3677 actualModuleKeysAdded[env].insert( module->getPseudoBase()->getModuleKey() );
3678 }
3679
3680 vector<VuoDirectedAcyclicNetwork *> searchDependencyGraphs;
3681 searchDependencyGraphs.push_back(compositionDependencyGraph);
3682 for (map<string, vector<Environment *> >::iterator ii = environmentsForCompositionFamily[target].begin(); ii != environmentsForCompositionFamily[target].end(); ++ii)
3683 {
3684 vector< vector<Environment *> > otherEnvs = sharedEnvironments[target];
3685 otherEnvs.push_back(ii->second);
3686 VuoDirectedAcyclicNetwork *other = makeDependencyNetwork(otherEnvs, ^VuoDirectedAcyclicGraph * (Environment *env) { return env->getCompositionDependencyGraph(); });
3687 searchDependencyGraphs.push_back(other);
3688 }
3689
3690 VuoDirectedAcyclicGraph *currentEnvironmentDependencyGraph = (currentEnvironment ? currentEnvironment->getCompositionDependencyGraph() : nullptr);
3691
3692 findDependentModulesAndSources(actualModuleKeysAdded, searchDependencyGraphs, currentEnvironmentDependencyGraph,
3693 modulesDepOnModulesAdded, modulesDepOnModulesAdded_otherCompiler,
3694 sourcesDepOnModulesAdded, sourcesDepOnModulesAdded_otherCompiler);
3695
3696 set<Environment *> otherEnvironments;
3697 for (map<Environment *, set<string> >::iterator i = sourcesDepOnModulesAdded_otherCompiler.begin(); i != sourcesDepOnModulesAdded_otherCompiler.end(); ++i)
3698 otherEnvironments.insert(i->first);
3699
3700 for (Environment *env : otherEnvironments)
3701 {
3702 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
3703 string moduleSearchPath = env->getModuleSearchPaths().front();
3704 VuoCompiler *otherCompiler = new VuoCompiler(moduleSearchPath + "/unused", target);
3705
3706 dispatch_sync(environmentQueue, ^{
3707 otherCompiler->loadModulesAndSources(set<string>(), set<string>(), set<string>(),
3708 sourcesDepOnModulesAdded_otherCompiler[env], set<string>(), set<string>(),
3709 false, true, env, nullptr, nullptr, "");
3710 });
3711
3712 delete otherCompiler;
3713 });
3714 }
3715 }
3716
3717 set<dispatch_group_t> sourcesLoading;
3718 for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3719 {
3720 Environment *env = (*i).at(0);
3721
3722 set<string> sourcesToCompile;
3723 sourcesToCompile.insert(sourcesAdded[env].begin(), sourcesAdded[env].end());
3724 sourcesToCompile.insert(sourcesModified[env].begin(), sourcesModified[env].end());
3725
3726 if (sourcesToCompile.size() == 0)
3727 continue;
3728
3729 set<dispatch_group_t> s = env->compileModulesFromSourceCode(sourcesToCompile, moduleSourceCompilersExist, shouldRecompileSourcesIfUnchanged);
3730 sourcesLoading.insert(s.begin(), s.end());
3731 }
3732
3733 for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3734 {
3735 Environment *env = (*i).at(0);
3736
3737 set<string> sourcesToCompile;
3738 sourcesToCompile.insert(sourcesDepOnModulesAdded[env].begin(), sourcesDepOnModulesAdded[env].end());
3739 sourcesToCompile.insert(sourcesDepOnModulesModified[env].begin(), sourcesDepOnModulesModified[env].end());
3740
3741 if (sourcesToCompile.size() == 0)
3742 continue;
3743
3744 env->compileModulesFromSourceCode(sourcesToCompile, moduleSourceCompilersExist, true);
3745 }
3746
3747 // Notify compiler delegates.
3748
3749 for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3750 {
3751 Environment *env = (*i).at(0);
3752 env->notifyCompilers(actualModulesAdded[env], actualModulesModified[env], actualModulesRemoved[env], issues[env]);
3753 }
3754
3755 // Since the dispatch groups for specialized modules are temporary (caller is responsible for releasing them)
3756 // but the dispatch groups for module sources should stay alive as long as the ModuleInfo that contains them,
3757 // retain the dispatch groups for module sources so that all dispatch groups can be safely released by the caller.
3758
3759 for (const dispatch_group_t &group : sourcesLoading)
3760 dispatch_retain(group);
3761
3762 set<dispatch_group_t> loadingGroups;
3763 loadingGroups.insert(specializedModulesLoading.begin(), specializedModulesLoading.end());
3764 loadingGroups.insert(sourcesLoading.begin(), sourcesLoading.end());
3765 return loadingGroups;
3766}
3767
3779void VuoCompiler::findDependentModulesAndSources(map<Environment *, set<string> > &changedModules,
3780 const vector<VuoDirectedAcyclicNetwork *> &searchDependencyGraphs,
3781 VuoDirectedAcyclicGraph *currentEnvironmentDependencyGraph,
3782 map<Environment *, set<string> > &modulesDepOnChangedModules_this,
3783 map<Environment *, set<string> > &modulesDepOnChangedModules_other,
3784 map<Environment *, set<string> > &sourcesDepOnChangedModules_this,
3785 map<Environment *, set<string> > &sourcesDepOnChangedModules_other)
3786{
3787 for (const vector<Environment *> &envs : environments)
3788 {
3789 Environment *env = envs.at(0);
3790
3791 for (const string &module : changedModules[env])
3792 {
3793 set<VuoDirectedAcyclicGraph::Vertex *> dependents;
3794 for (VuoDirectedAcyclicNetwork *searchDependencyGraph : searchDependencyGraphs)
3795 {
3796 vector<VuoDirectedAcyclicGraph::Vertex *> moduleVertices;
3797 if (currentEnvironmentDependencyGraph)
3798 {
3799 // If a module with the same module key is installed in multiple locations,
3800 // only consider the one being modified or removed.
3801 VuoDirectedAcyclicGraph::Vertex *mv = currentEnvironmentDependencyGraph->findVertex(module);
3802 if (mv)
3803 moduleVertices.push_back(mv);
3804 }
3805 else
3806 moduleVertices = searchDependencyGraph->findVertex(module);
3807
3808 for (VuoDirectedAcyclicGraph::Vertex *moduleVertexRaw : moduleVertices)
3809 {
3810 DependencyGraphVertex *moduleVertex = static_cast<DependencyGraphVertex *>(moduleVertexRaw);
3811 if (moduleVertex->getEnvironment())
3812 {
3813 vector<VuoDirectedAcyclicGraph::Vertex *> upstream = searchDependencyGraph->getUpstreamVertices(moduleVertex);
3814 dependents.insert(upstream.begin(), upstream.end());
3815 }
3816 }
3817 }
3818
3819 set< pair<Environment *, string> > dependentsMap;
3820 for (VuoDirectedAcyclicGraph::Vertex *dependentVertexRaw : dependents)
3821 {
3822 DependencyGraphVertex *v = static_cast<DependencyGraphVertex *>(dependentVertexRaw);
3823 Environment *dependentEnv = v->getEnvironment();
3824 if (! dependentEnv)
3825 continue;
3826
3827 string dependent = v->getDependency();
3828
3829 dependentsMap.insert({dependentEnv, dependent});
3830 }
3831
3832 // In case `module` is a generic node class, check the generated environment at the same scope for any
3833 // specializations of the node class, and add them to the list of dependencies.
3834 // (They aren't in the dependency graph since the graph edge goes from installed to generated.)
3835 for (auto i : envs.at(1)->getNodeClasses())
3836 {
3837 VuoCompilerSpecializedNodeClass *specializedNodeClass = dynamic_cast<VuoCompilerSpecializedNodeClass *>(i.second);
3838 if (specializedNodeClass->getOriginalGenericNodeClassName() == module)
3839 dependentsMap.insert({envs.at(1), i.first});
3840 }
3841
3842 for (auto i : dependentsMap)
3843 {
3844 Environment *dependentEnv = i.first;
3845 string dependent = i.second;
3846
3847 // Skip if the dependent module is already being modified/removed in its own right
3848 // (e.g. if the module depends on another in the same node set and the node set is being removed).
3849 if (changedModules[dependentEnv].find(dependent) != changedModules[dependentEnv].end())
3850 continue;
3851
3852 ModuleInfo *foundSourceInfo = dependentEnv->listSourceFile(dependent);
3853 ModuleInfo *foundModuleInfo = dependentEnv->listModule(dependent);
3854
3855 bool belongsToCurrentCompiler = false;
3856 for (const vector<Environment *> &envs2 : environments)
3857 {
3858 if (find(envs2.begin(), envs2.end(), dependentEnv) != envs2.end())
3859 {
3860 belongsToCurrentCompiler = true;
3861 break;
3862 }
3863 }
3864
3865 map<Environment *, set<string> > *whicheverDependents = nullptr;
3866 ModuleInfo *moduleInfo = nullptr;
3867 if (foundSourceInfo)
3868 {
3869 moduleInfo = foundSourceInfo;
3870 whicheverDependents = (belongsToCurrentCompiler ? &sourcesDepOnChangedModules_this : &sourcesDepOnChangedModules_other);
3871 }
3872 else if (foundModuleInfo)
3873 {
3874 moduleInfo = foundModuleInfo;
3875 whicheverDependents = (belongsToCurrentCompiler ? &modulesDepOnChangedModules_this : &modulesDepOnChangedModules_other);
3876 }
3877 else // Module in generated environment
3878 {
3879 whicheverDependents = (belongsToCurrentCompiler ? &modulesDepOnChangedModules_this : &modulesDepOnChangedModules_other);
3880 }
3881
3882 (*whicheverDependents)[dependentEnv].insert(dependent);
3883 if (moduleInfo)
3884 moduleInfo->setAttempted(false);
3885 }
3886 }
3887 }
3888}
3889
3893void VuoCompiler::loadedModules(map<string, VuoCompilerModule *> modulesAdded,
3894 map<string, pair<VuoCompilerModule *, VuoCompilerModule *> > modulesModified,
3895 map<string, VuoCompilerModule *> modulesRemoved,
3896 VuoCompilerIssues *issues, void *delegateDataV, Environment *currentEnvironment)
3897{
3898 //VLog("C=%p %lu %lu %lu", this, modulesAdded.size(), modulesModified.size(), modulesRemoved.size());
3899
3900 // If a module is added, superseding a version of the same module installed at a broader scope,
3901 // notify this VuoCompiler that the module has been modified rather than added.
3902 //
3903 // If a module is added, but a version of the same module is already installed at a narrower scope,
3904 // don't notify this VuoCompiler, since it will continue to use the version at the narrower scope.
3905 //
3906 // Same idea when a module is modified or removed while another version is installed at a different scope.
3907
3908 auto findVersionsOfModule = [this, currentEnvironment] (const string &moduleKey)
3909 {
3910 vector< pair<Environment *, VuoCompilerModule *> > moduleVersions;
3911 for (const vector<Environment *> &envs : environments)
3912 {
3913 Environment *env = envs.at(0);
3914 VuoCompilerModule *module = env->findModule(moduleKey);
3915 if (module || env == currentEnvironment)
3916 moduleVersions.push_back( make_pair(env, module) );
3917 }
3918 return moduleVersions;
3919 };
3920
3921 for (map<string, VuoCompilerModule *>::iterator i = modulesAdded.begin(); i != modulesAdded.end(); )
3922 {
3923 string moduleKey = i->first;
3924 VuoCompilerModule *moduleAdded = i->second;
3925
3926 vector< pair<Environment *, VuoCompilerModule *> > moduleVersions = findVersionsOfModule(moduleKey);
3927
3928 if (moduleVersions.size() > 1)
3929 {
3930 modulesAdded.erase(i++);
3931
3932 if (moduleVersions.back().second == moduleAdded)
3933 {
3934 VuoCompilerModule *moduleSuperseded = moduleVersions.at(moduleVersions.size()-2).second;
3935 modulesModified[moduleKey] = make_pair(moduleSuperseded, moduleAdded);
3936 }
3937 }
3938 else
3939 ++i;
3940 }
3941
3942 for (map<string, pair<VuoCompilerModule *, VuoCompilerModule *> >::iterator i = modulesModified.begin(); i != modulesModified.end(); )
3943 {
3944 string moduleKey = i->first;
3945 VuoCompilerModule *moduleModified = i->second.second;
3946
3947 vector< pair<Environment *, VuoCompilerModule *> > moduleVersions = findVersionsOfModule(moduleKey);
3948
3949 if (moduleVersions.size() > 1 && moduleVersions.back().second != moduleModified)
3950 modulesModified.erase(i++);
3951 else
3952 ++i;
3953 }
3954
3955 for (map<string, VuoCompilerModule *>::iterator i = modulesRemoved.begin(); i != modulesRemoved.end(); )
3956 {
3957 string moduleKey = i->first;
3958 VuoCompilerModule *moduleRemoved = i->second;
3959
3960 vector< pair<Environment *, VuoCompilerModule *> > moduleVersions = findVersionsOfModule(moduleKey);
3961
3962 if (moduleVersions.size() > 1)
3963 {
3964 modulesRemoved.erase(i++);
3965
3966 if (moduleVersions.back().first == currentEnvironment)
3967 {
3968 VuoCompilerModule *moduleUnsuperseded = moduleVersions.at(moduleVersions.size()-2).second;
3969 modulesModified[moduleKey] = make_pair(moduleRemoved, moduleUnsuperseded);
3970 }
3971 }
3972 else
3973 ++i;
3974 }
3975
3976 dispatch_async(delegateQueue, ^{
3977 VuoCompilerDelegate::LoadedModulesData *delegateData = static_cast<VuoCompilerDelegate::LoadedModulesData *>(delegateDataV);
3978
3979 if (delegate && ! (modulesAdded.empty() && modulesModified.empty() && modulesRemoved.empty() && issues->isEmpty()))
3980 {
3981 delegate->enqueueData(delegateData);
3982 delegate->loadedModules(modulesAdded, modulesModified, modulesRemoved, issues);
3983 }
3984 else
3985 {
3986 delegateData->release();
3987 }
3988 });
3989}
3990
3996void VuoCompiler::loadNodeClassGeneratedAtRuntime(VuoCompilerNodeClass *nodeClass, Environment *env)
3997{
3998 Module *module = nodeClass->getModule();
3999 if (module)
4000 {
4001 dispatch_sync(llvmQueue, ^{
4002 setTargetForModule(nodeClass->getModule(), env->getTarget());
4003 });
4004 }
4005
4006 dispatch_sync(environmentQueue, ^{
4007 env->replaceNodeClass(nodeClass);
4008 });
4009
4010 __block map<string, VuoCompilerType *> inheritedTypes;
4011 void (^envReifyPortTypes)(Environment *) = ^void (Environment *env) {
4012 env->reifyPortTypes(inheritedTypes);
4013 map<string, VuoCompilerType *> currentTypes = env->getTypes();
4014 inheritedTypes.insert(currentTypes.begin(), currentTypes.end());
4015 };
4016 applyToAllEnvironments(envReifyPortTypes);
4017}
4018
4022void VuoCompiler::reifyGenericPortTypes(VuoCompilerComposition *composition)
4023{
4024 for (VuoCompilerNode *node : composition->getCachedGraph(this)->getNodes())
4025 reifyGenericPortTypes(node->getBase());
4026
4027 composition->invalidateCachedGraph();
4028}
4029
4033void VuoCompiler::reifyGenericPortTypes(VuoNode *node)
4034{
4036 if (! nodeClass)
4037 return;
4038
4039 // Reify any generic types on the node that don't already have a compiler detail.
4040
4041 vector<VuoPort *> inputPorts = node->getInputPorts();
4042 vector<VuoPort *> outputPorts = node->getOutputPorts();
4043 vector<VuoPort *> ports;
4044 ports.insert(ports.end(), inputPorts.begin(), inputPorts.end());
4045 ports.insert(ports.end(), outputPorts.begin(), outputPorts.end());
4046
4047 for (vector<VuoPort *>::iterator j = ports.begin(); j != ports.end(); ++j)
4048 {
4049 VuoCompilerPort *port = static_cast<VuoCompilerPort *>((*j)->getCompiler());
4050 VuoGenericType *genericType = dynamic_cast<VuoGenericType *>(port->getDataVuoType());
4051 if (! genericType)
4052 continue;
4053
4054 if (! genericType->hasCompiler())
4055 {
4056 VuoCompilerType * (^compilerGetType)(string) = ^VuoCompilerType * (string moduleKey) {
4057 return getType(moduleKey);
4058 };
4059
4060 VuoCompilerGenericType *reifiedType = VuoCompilerGenericType::newGenericType(genericType, compilerGetType);
4061 if (reifiedType)
4062 port->setDataVuoType(reifiedType->getBase());
4063 }
4064 }
4065
4066 // Update the node class's backing to match the node's backing.
4067
4068 nodeClass->updateBackingNodeClass(node, this);
4069}
4070
4077void VuoCompiler::compileModule(string inputPath, string outputPath)
4078{
4079 compileModule(inputPath, outputPath, vector<string>());
4080}
4081
4089void VuoCompiler::compileModule(string inputPath, string outputPath, const vector<string> &includePaths)
4090{
4091 if (isVerbose)
4092 print();
4093
4094 vector<string> allIncludePaths = includePaths;
4095 string preprocessedInputPath = inputPath;
4096
4097 string tmpPreprocessedInputDir;
4098 string dir, file, ext;
4099 VuoFileUtilities::splitPath(inputPath, dir, file, ext);
4101 {
4102 string inputContents = VuoFileUtilities::readFileToString(inputPath);
4103 string preprocessedInputContents = inputContents;
4105 if (inputContents != preprocessedInputContents)
4106 {
4107 // Unspecialized generic node class
4108 allIncludePaths.push_back(dir.empty() ? "." : dir);
4109 tmpPreprocessedInputDir = VuoFileUtilities::makeTmpDir(file);
4110 preprocessedInputPath = tmpPreprocessedInputDir + "/" + file + "." + ext;
4111 VuoFileUtilities::preserveOriginalFileName(preprocessedInputContents, file + "." + ext);
4112 VuoFileUtilities::writeStringToFile(preprocessedInputContents, preprocessedInputPath);
4113 }
4114 }
4115 else if (ext == "fs")
4116 {
4117 VuoFileUtilities::File vuf(dir, file + "." + ext);
4118 VuoModuleCompiler *moduleCompiler = VuoModuleCompiler::newModuleCompiler("isf", getModuleKeyForPath(inputPath), &vuf);
4119 if (moduleCompiler)
4120 {
4121 auto getType = [this] (const string &moduleKey) { return this->getType(moduleKey); };
4122 VuoCompilerIssues *issues = new VuoCompilerIssues();
4123 Module *module = moduleCompiler->compile(getType, llvmQueue, issues);
4124 if (module)
4125 dispatch_sync(llvmQueue, ^{
4126 setTargetForModule(module, target);
4127 writeModuleToBitcode(module, outputPath);
4128#if VUO_PRO
4129 string dependencyOutputPath = _dependencyOutput();
4130 if (!dependencyOutputPath.empty())
4131 {
4132 string outputObjectPath = dependencyOutputPath.substr(0, dependencyOutputPath.length() - 2);
4133 VuoFileUtilities::writeStringToFile(outputObjectPath + ": " + inputPath, dependencyOutputPath);
4134 }
4135#endif
4136 });
4137
4138 if (!issues->isEmpty())
4139 throw VuoCompilerException(issues, true);
4140 delete issues;
4141 }
4142 return;
4143 }
4144
4145 vector<string> extraArgs;
4146 for (vector<string>::iterator i = allIncludePaths.begin(); i != allIncludePaths.end(); ++i)
4147 {
4148 extraArgs.push_back("-I");
4149 extraArgs.push_back(*i);
4150 }
4151
4152
4153 // When compiling on a development workstation or Jenkins, use the Conan-packaged macOS SDK, since it includes headers.
4154 // When compiling on an end-user system, no SDK is needed.
4155 string buildTimeMacOSSDKFolder = MACOS_SDK_ROOT;
4156 if (VuoFileUtilities::fileExists(buildTimeMacOSSDKFolder))
4157 {
4158 extraArgs.push_back("-isysroot");
4159 extraArgs.push_back(buildTimeMacOSSDKFolder);
4160 }
4161
4162
4163 __block vector<string> headerSearchPaths;
4164 void (^envGetHeaderSearchPaths)(Environment *) = ^void (Environment *env) {
4165 vector<string> result = env->getHeaderSearchPaths();
4166 headerSearchPaths.insert(headerSearchPaths.end(), result.begin(), result.end());
4167 };
4168 applyToInstalledEnvironments(envGetHeaderSearchPaths);
4169
4170 auto issues = new VuoCompilerIssues;
4171 __block Module *module;
4172 dispatch_sync(llvmQueue, ^{
4173 module = readModuleFromC(preprocessedInputPath, headerSearchPaths, extraArgs, issues);
4174 });
4175 string moduleKey = getModuleKeyForPath(inputPath);
4176 if (! tmpPreprocessedInputDir.empty())
4177 {
4178 remove(tmpPreprocessedInputDir.c_str());
4179 issues->setFilePath(inputPath);
4180 }
4181 if (! module)
4182 throw VuoCompilerException(issues, true);
4183 delete issues;
4184
4185 dispatch_sync(llvmQueue, ^{
4187 if (! compilerModule)
4188 {
4189 VUserLog("Error: Didn't recognize '%s' as a node class, type, or library.", inputPath.c_str());
4190 return;
4191 }
4192
4193 setTargetForModule(module, target);
4194 writeModuleToBitcode(module, outputPath);
4195
4196 delete module;
4197 });
4198}
4199
4203Module * VuoCompiler::compileCompositionToModule(VuoCompilerComposition *composition, const string &moduleKey, bool isTopLevelComposition,
4204 VuoCompilerIssues *issues)
4205{
4206 composition->check(issues);
4207
4208 reifyGenericPortTypes(composition);
4209
4211 isTopLevelComposition,
4212 moduleKey, this);
4213
4214 __block Module *module = nullptr;
4215 dispatch_sync(llvmQueue, ^{
4216 try
4217 {
4218 module = generator->generateBitcode();
4219 setTargetForModule(module, target);
4220 }
4221 catch (VuoCompilerException &e)
4222 {
4223 if (issues)
4224 issues->append(e.getIssues());
4225 else
4226 VUserLog("%s", e.getIssues()->getLongDescription(false).c_str());
4227 }
4228 });
4229
4230 delete generator;
4231
4232 return module;
4233}
4234
4245void VuoCompiler::compileComposition(VuoCompilerComposition *composition, string outputPath, bool isTopLevelComposition,
4246 VuoCompilerIssues *issues)
4247{
4248 string moduleKey = getModuleKeyForPath(outputPath);
4249 Module *module = compileCompositionToModule(composition, moduleKey, isTopLevelComposition, issues);
4250 if (!module)
4251 throw VuoCompilerException(issues, false);
4252
4253 dispatch_sync(llvmQueue, ^{
4254 writeModuleToBitcode(module, outputPath);
4255 });
4256}
4257
4270void VuoCompiler::compileComposition(string inputPath, string outputPath, bool isTopLevelComposition,
4271 VuoCompilerIssues *issues)
4272{
4273 VUserLog("Compiling '%s' (%s)…", inputPath.c_str(), target.c_str());
4274 if (isVerbose)
4275 print();
4276
4277 if (getCompositionLocalPath().empty())
4278 setCompositionPath(inputPath);
4279
4281 {
4282 VuoCompilerIssue issue(VuoCompilerIssue::Error, "opening composition", inputPath,
4283 "", "The composition file couldn't be read or was empty.");
4284 throw VuoCompilerException(issue);
4285 }
4286
4287 try
4288 {
4289 string compositionString = VuoFileUtilities::readFileToString(inputPath);
4290 compileCompositionString(compositionString, outputPath, isTopLevelComposition, issues);
4291 }
4292 catch (VuoCompilerException &e)
4293 {
4294 if (e.getIssues())
4295 e.getIssues()->setFilePathIfEmpty(inputPath);
4296 if (!issues && e.getIssues())
4297 VUserLog("%s", e.getIssues()->getLongDescription(false).c_str());
4298 throw;
4299 }
4300
4301 VUserLog("Done.");
4302}
4303
4314void VuoCompiler::compileCompositionString(const string &compositionString, string outputPath, bool isTopLevelComposition,
4315 VuoCompilerIssues *issues)
4316{
4318 compileComposition(composition, outputPath, isTopLevelComposition, issues);
4319
4320 VuoComposition *baseComposition = composition->getBase();
4321 delete composition;
4322 delete baseComposition;
4323}
4324
4340void VuoCompiler::linkCompositionToCreateExecutable(string inputPath, string outputPath, Optimization optimization, string rPath, bool shouldAdHocCodeSign)
4341{
4342 vector<string> rPaths = ! rPath.empty() ? vector<string>(1, rPath) : getRunPathSearchPaths(environments.back().front());
4343 linkCompositionToCreateExecutableOrDynamicLibrary(inputPath, outputPath, optimization, false, rPaths, shouldAdHocCodeSign);
4344}
4345
4363void VuoCompiler::linkCompositionToCreateDynamicLibrary(string inputPath, string outputPath, Optimization optimization, bool shouldAdHocCodeSign)
4364{
4365 vector<string> rPaths = getRunPathSearchPaths(environments.back().front());
4366 linkCompositionToCreateExecutableOrDynamicLibrary(inputPath, outputPath, optimization, true, rPaths, shouldAdHocCodeSign);
4367}
4368
4383void VuoCompiler::linkCompositionToCreateExecutableOrDynamicLibrary(string compiledCompositionPath, string linkedCompositionPath,
4384 Optimization optimization, bool isDylib, const vector<string> &rPaths,
4385 bool shouldAdHocCodeSign)
4386{
4387 if (isVerbose)
4388 print();
4389
4390 if (optimization == Optimization_FastBuildExistingCache)
4391 shouldLoadAllModules = false;
4392
4393 set<string> dependencies = getDependenciesForComposition(compiledCompositionPath);
4394 dependencies.insert(getRuntimeDependency());
4395 if (! isDylib)
4396 dependencies.insert(getRuntimeMainDependency());
4397
4398 set<Module *> modules;
4399 set<string> libraries;
4400 set<string> frameworks;
4401 getLinkerInputs(dependencies, optimization, modules, libraries, frameworks);
4402
4403 libraries.insert(compiledCompositionPath);
4404
4405 link(linkedCompositionPath, modules, libraries, frameworks, isDylib, rPaths, shouldAdHocCodeSign);
4406}
4407
4422void VuoCompiler::linkCompositionToCreateDynamicLibraries(string compiledCompositionPath, string linkedCompositionPath,
4423 VuoRunningCompositionLibraries *runningCompositionLibraries)
4424{
4425 if (isVerbose)
4426 print();
4427
4428 bool shouldAdHocCodeSign = false;
4429#if __arm64__
4430 shouldAdHocCodeSign = true;
4431#endif
4432
4433 // Get the dependencies used by the new resources and not the previous resources.
4434
4435 set<string> carriedOverDependencies = runningCompositionLibraries->getDependenciesLoaded();
4436 set<string> allDependencies = getDependenciesForComposition(compiledCompositionPath);
4437 set<string> addedDependencies;
4438 std::set_difference(allDependencies.begin(), allDependencies.end(),
4439 carriedOverDependencies.begin(), carriedOverDependencies.end(),
4440 std::inserter(addedDependencies, addedDependencies.end()));
4441
4442 // Get the libraries and frameworks used by the previous resources.
4443
4444 vector<string> carriedOverNonUnloadableLibraries = runningCompositionLibraries->getNonUnloadableLibrariesLoaded();
4445 vector<string> carriedOverUnloadableLibraries = runningCompositionLibraries->getUnloadableLibrariesLoaded();
4446 set<string> carriedOverExternalLibraries = runningCompositionLibraries->getExternalLibraries();
4447 set<string> carriedOverFrameworks = runningCompositionLibraries->getExternalFrameworks();
4448
4449 // Link the new resource dylibs, if needed.
4450
4451 string nonUnloadableResourcePath;
4452 string unloadableResourcePath;
4453 set<string> nonUnloadableDependencies;
4454 set<string> unloadableDependencies;
4455 map<string, set<string> > builtInCacheDependencies;
4456 map<string, set<string> > userCacheDependencies;
4457 set<string> builtInLibraries;
4458 set<string> userLibraries;
4459 set<string> addedExternalLibraries;
4460 set<string> addedFrameworks;
4461 set<string> allFrameworks;
4462 if (! addedDependencies.empty())
4463 {
4464 // Get the modules, libraries, and frameworks that will provide the composition's dependencies.
4465
4466 set<string> builtInModuleAndLibraryDependencies;
4467 set<string> userModuleAndLibraryDependencies;
4468 set<Module *> builtInModules;
4469 set<Module *> userModules;
4470
4471 getLinkerInputs(addedDependencies, Optimization_FastBuild,
4472 builtInModuleAndLibraryDependencies, userModuleAndLibraryDependencies, builtInCacheDependencies, userCacheDependencies,
4473 builtInModules, userModules, builtInLibraries, userLibraries, addedExternalLibraries, addedFrameworks);
4474
4475 allFrameworks.insert(carriedOverFrameworks.begin(), carriedOverFrameworks.end());
4476 allFrameworks.insert(addedFrameworks.begin(), addedFrameworks.end());
4477
4478 string dir, linkedCompositionFile, ext;
4479 VuoFileUtilities::splitPath(linkedCompositionPath, dir, linkedCompositionFile, ext);
4480
4481 // For any module caches that were rebuilt, remove the previous revision from the lists of libraries to link to and load.
4482
4483 vector<string> carriedOverUserCacheLibraries = runningCompositionLibraries->getUnloadableCacheLibrariesLoaded();
4484
4485 __block vector<string> currentCacheLibraries;
4486 applyToAllEnvironments(^void (Environment *env) {
4487 currentCacheLibraries.push_back( env->getCurrentModuleCacheDylib() );
4488 });
4489
4490 for (string cachePath : carriedOverUserCacheLibraries)
4491 {
4492 for (string currentCachePath : currentCacheLibraries)
4493 {
4495 {
4496 set<string> dependenciesInCache = runningCompositionLibraries->enqueueCacheLibraryToUnload(cachePath);
4497
4498 userCacheDependencies[currentCachePath].insert(dependenciesInCache.begin(), dependenciesInCache.end());
4499
4500 auto cacheDependenciesIter = userCacheDependencies.find(cachePath);
4501 if (cacheDependenciesIter != userCacheDependencies.end())
4502 {
4503 userCacheDependencies[currentCachePath].insert(cacheDependenciesIter->second.begin(), cacheDependenciesIter->second.end());
4504 userCacheDependencies.erase(cacheDependenciesIter);
4505 }
4506
4507 auto carriedOverIter = find(carriedOverUnloadableLibraries.begin(), carriedOverUnloadableLibraries.end(), cachePath);
4508 if (carriedOverIter != carriedOverUnloadableLibraries.end())
4509 *carriedOverIter = currentCachePath;
4510 }
4511 }
4512 }
4513
4514 // If any module caches were rebuilt, prepare to replace the existing user resource dylibs with the new resource dylib created below.
4515
4516 bool wasModuleCacheRebuilt = runningCompositionLibraries->hasCacheLibraryEnqueuedToUnload();
4517 if (wasModuleCacheRebuilt)
4518 {
4519 vector<string> carriedOverResourceLibraries = runningCompositionLibraries->getUnloadableResourceLibrariesLoaded();
4520
4521 vector<string> carriedOverUnloadableMinusResourceLibraries;
4522 std::set_difference(carriedOverUnloadableLibraries.begin(), carriedOverUnloadableLibraries.end(),
4523 carriedOverResourceLibraries.begin(), carriedOverResourceLibraries.end(),
4524 std::back_inserter(carriedOverUnloadableMinusResourceLibraries));
4525
4526 carriedOverUnloadableLibraries = carriedOverUnloadableMinusResourceLibraries;
4527
4528 set<string> dependenciesInResourceLibraries = runningCompositionLibraries->enqueueAllUnloadableResourceLibrariesToUnload();
4529 userModuleAndLibraryDependencies.insert(dependenciesInResourceLibraries.begin(), dependenciesInResourceLibraries.end());
4530
4531 set<string> builtInModuleAndLibraryDependencies_tmp;
4532 set<string> userModuleAndLibraryDependencies_tmp;
4533 map<string, set<string> > builtInCacheDependencies_tmp;
4534 set<Module *> builtInModules_tmp;
4535 set<string> builtInLibraries_tmp;
4536 set<string> externalLibraries_tmp;
4537 set<string> externalFrameworks_tmp;
4538
4539 getLinkerInputs(userModuleAndLibraryDependencies, Optimization_FastBuild,
4540 builtInModuleAndLibraryDependencies_tmp, userModuleAndLibraryDependencies_tmp, builtInCacheDependencies_tmp, userCacheDependencies,
4541 builtInModules_tmp, userModules, builtInLibraries_tmp, userLibraries, externalLibraries_tmp, externalFrameworks_tmp);
4542 }
4543
4544 // If built-in dependencies were added, create an additional resource dylib.
4545
4546 if (! builtInModules.empty() || builtInLibraries.size() > builtInCacheDependencies.size())
4547 {
4548 nonUnloadableResourcePath = VuoFileUtilities::makeTmpFile(linkedCompositionFile + "-resource-nonunloadable", "dylib");
4549 nonUnloadableDependencies = builtInModuleAndLibraryDependencies;
4550
4551 set<string> librariesForNonUnloadableResource = builtInLibraries;
4552 librariesForNonUnloadableResource.insert(carriedOverNonUnloadableLibraries.begin(), carriedOverNonUnloadableLibraries.end());
4553 librariesForNonUnloadableResource.insert(carriedOverExternalLibraries.begin(), carriedOverExternalLibraries.end());
4554 librariesForNonUnloadableResource.insert(addedExternalLibraries.begin(), addedExternalLibraries.end());
4555
4556 vector<string> rPaths = getRunPathSearchPaths(environments.front().front());
4557
4558 link(nonUnloadableResourcePath, builtInModules, librariesForNonUnloadableResource, allFrameworks, true, rPaths, shouldAdHocCodeSign);
4559
4560 for (set<string>::iterator i = builtInLibraries.begin(); i != builtInLibraries.end(); )
4561 {
4562 if (! VuoStringUtilities::endsWith(*i, ".dylib"))
4563 builtInLibraries.erase(i++);
4564 else
4565 i++;
4566 }
4567
4568 for (set<string>::iterator i = addedExternalLibraries.begin(); i != addedExternalLibraries.end(); )
4569 {
4570 if (! VuoStringUtilities::endsWith(*i, ".dylib"))
4571 addedExternalLibraries.erase(i++);
4572 else
4573 i++;
4574 }
4575 }
4576
4577 // If user dependencies were added or module caches were rebuilt, create an additional resource dylib.
4578
4579 if (! userModules.empty() || userLibraries.size() > userCacheDependencies.size() || wasModuleCacheRebuilt)
4580 {
4581 unloadableResourcePath = VuoFileUtilities::makeTmpFile(linkedCompositionFile + "-resource-unloadable", "dylib");
4582 unloadableDependencies = userModuleAndLibraryDependencies;
4583
4584 set<string> librariesForUnloadableResource = userLibraries;
4585 librariesForUnloadableResource.insert(builtInLibraries.begin(), builtInLibraries.end());
4586 librariesForUnloadableResource.insert(carriedOverUnloadableLibraries.begin(), carriedOverUnloadableLibraries.end());
4587 librariesForUnloadableResource.insert(carriedOverNonUnloadableLibraries.begin(), carriedOverNonUnloadableLibraries.end());
4588 librariesForUnloadableResource.insert(carriedOverExternalLibraries.begin(), carriedOverExternalLibraries.end());
4589 librariesForUnloadableResource.insert(addedExternalLibraries.begin(), addedExternalLibraries.end());
4590 if (! nonUnloadableResourcePath.empty())
4591 librariesForUnloadableResource.insert(nonUnloadableResourcePath);
4592
4593 // This is usually correct, but may fail in the case where there are two identically-named dylibs at different
4594 // levels of scope. However, it's the best we can do as long as modules at the system, user, and composition
4595 // levels of scope are all combined into one resource dylib. (https://b33p.net/kosada/vuo/vuo/-/merge_requests/196#note_2148884)
4596 vector<string> rPaths = getRunPathSearchPaths(environments.back().front());
4597
4598 link(unloadableResourcePath, userModules, librariesForUnloadableResource, allFrameworks, true, rPaths, shouldAdHocCodeSign);
4599
4600 for (set<string>::iterator i = userLibraries.begin(); i != userLibraries.end(); )
4601 {
4602 if (! VuoStringUtilities::endsWith(*i, ".dylib"))
4603 userLibraries.erase(i++);
4604 else
4605 i++;
4606 }
4607 }
4608 }
4609
4610 // Get the Vuo runtime dependency.
4611
4612 set<string> vuoRuntimePaths;
4613 {
4614 set<Module *> modules;
4615 set<string> libraries;
4616 set<string> frameworks;
4617
4618 set<string> dependencies;
4619 dependencies.insert(getRuntimeDependency());
4620 getLinkerInputs(dependencies, Optimization_FastBuild, modules, libraries, frameworks);
4621 vuoRuntimePaths = libraries;
4622 }
4623
4624 // Link the composition.
4625
4626 {
4627 set<Module *> modules;
4628 set<string> libraries;
4629
4630 libraries.insert(compiledCompositionPath);
4631 libraries.insert(carriedOverExternalLibraries.begin(), carriedOverExternalLibraries.end());
4632 libraries.insert(addedExternalLibraries.begin(), addedExternalLibraries.end());
4633 libraries.insert(carriedOverNonUnloadableLibraries.begin(), carriedOverNonUnloadableLibraries.end());
4634 libraries.insert(carriedOverUnloadableLibraries.begin(), carriedOverUnloadableLibraries.end());
4635 libraries.insert(builtInLibraries.begin(), builtInLibraries.end());
4636 libraries.insert(userLibraries.begin(), userLibraries.end());
4637 if (! nonUnloadableResourcePath.empty())
4638 libraries.insert(nonUnloadableResourcePath);
4639 if (! unloadableResourcePath.empty())
4640 libraries.insert(unloadableResourcePath);
4641 libraries.insert(vuoRuntimePaths.begin(), vuoRuntimePaths.end());
4642 vector<string> rPaths = getRunPathSearchPaths(environments.front().front());
4643 link(linkedCompositionPath, modules, libraries, allFrameworks, true, rPaths, shouldAdHocCodeSign);
4644 }
4645
4646 // Now that we're past the point where an exception can be thrown, update the RunningCompositionLibraries.
4647
4648 if (! nonUnloadableResourcePath.empty())
4649 runningCompositionLibraries->enqueueResourceLibraryToLoad(nonUnloadableResourcePath, nonUnloadableDependencies, false);
4650
4651 if (! unloadableResourcePath.empty())
4652 runningCompositionLibraries->enqueueResourceLibraryToLoad(unloadableResourcePath, unloadableDependencies, true);
4653
4654 for (map<string, set<string> >::iterator i = builtInCacheDependencies.begin(); i != builtInCacheDependencies.end(); ++i)
4655 runningCompositionLibraries->enqueueCacheLibraryToLoad(i->first, i->second, false);
4656
4657 for (map<string, set<string> >::iterator i = userCacheDependencies.begin(); i != userCacheDependencies.end(); ++i)
4658 runningCompositionLibraries->enqueueCacheLibraryToLoad(i->first, i->second, true);
4659
4660 runningCompositionLibraries->addExternalFrameworks(addedFrameworks);
4661 runningCompositionLibraries->addExternalLibraries(addedExternalLibraries);
4662}
4663
4670set<string> VuoCompiler::getDependenciesForComposition(const string &compiledCompositionPath)
4671{
4672 double t0 = VuoLogGetTime();
4673
4674 // Add the node classes in the top-level composition and their dependencies.
4675 __block set<string> directDependencies;
4676 string moduleKey = getModuleKeyForPath(compiledCompositionPath);
4677 Module *module = readModuleFromBitcode(compiledCompositionPath, getTargetArch(target));
4678 dispatch_sync(llvmQueue, ^{
4680 directDependencies = compilerModule->getDependencies();
4681 delete compilerModule;
4682 delete module;
4683 });
4684
4685 try
4686 {
4687 auto deps = getDependenciesForComposition(directDependencies, true);
4688 VUserLog("Gathering dependencies for '%s' took %5.2fs", compiledCompositionPath.c_str(), VuoLogGetTime() - t0);
4689 return deps;
4690 }
4691 catch (VuoCompilerException &e)
4692 {
4693 e.getIssues()->setFilePathIfEmpty(compiledCompositionPath);
4694 throw;
4695 }
4696}
4697
4705{
4706 set<string> directDependencies;
4707
4708 set<VuoCompilerNode *> nodes = composition->getCachedGraph(this)->getNodes();
4709 for (VuoCompilerNode *node : nodes)
4710 if (node->getBase()->getNodeClass()->hasCompiler())
4711 directDependencies.insert( node->getBase()->getNodeClass()->getCompiler()->getDependencyName() );
4712
4713 vector<VuoPublishedPort *> publishedInputPorts = composition->getBase()->getPublishedInputPorts();
4714 vector<VuoPublishedPort *> publishedOutputPorts = composition->getBase()->getPublishedOutputPorts();
4715 vector<VuoPublishedPort *> publishedPorts;
4716 publishedPorts.insert(publishedPorts.end(), publishedInputPorts.begin(), publishedInputPorts.end());
4717 publishedPorts.insert(publishedPorts.end(), publishedOutputPorts.begin(), publishedOutputPorts.end());
4718 for (VuoPublishedPort *publishedPort : publishedPorts)
4719 {
4720 if (publishedPort->getClass()->hasCompiler())
4721 {
4722 VuoType *portType = static_cast<VuoCompilerPortClass *>( publishedPort->getClass()->getCompiler() )->getDataVuoType();
4723 if (portType)
4724 {
4725 string dependency;
4726 VuoGenericType *genericType = dynamic_cast<VuoGenericType *>(portType);
4727 if (genericType)
4728 {
4729 VuoGenericType::Compatibility compatibility;
4730 vector<string> compatibleTypeNames = genericType->getCompatibleSpecializedTypes(compatibility);
4731 dependency = VuoCompilerGenericType::chooseBackingTypeName(portType->getModuleKey(), compatibleTypeNames);
4732 }
4733 else
4734 dependency = portType->getModuleKey();
4735
4736 directDependencies.insert(dependency);
4737 }
4738 }
4739 }
4740
4741 return directDependencies;
4742}
4743
4750set<string> VuoCompiler::getDependenciesForComposition(VuoCompilerComposition *composition)
4751{
4752 set<string> directDependencies = getDirectDependenciesForComposition(composition);
4753 return getDependenciesForComposition(directDependencies, false);
4754}
4755
4762{
4763 __block vector<string> librarySearchPaths;
4764 applyToInstalledEnvironments(^void (Environment *env) {
4765 vector<string> result = env->getLibrarySearchPaths();
4766 librarySearchPaths.insert(librarySearchPaths.end(), result.begin(), result.end());
4767 });
4768
4769 set<string> dylibDeps;
4770 for (string dep : getDependenciesForComposition(composition))
4771 {
4772 string path = getLibraryPath(dep, librarySearchPaths);
4773 if (VuoStringUtilities::endsWith(path, ".dylib"))
4774 dylibDeps.insert(path);
4775 }
4776
4777 return dylibDeps;
4778}
4779
4793set<string> VuoCompiler::getDependenciesForComposition(const set<string> &directDependencies, bool checkCompatibility)
4794{
4795 // Make sure that any compiler-generated node classes have been generated and added to the dependency graph.
4796 for (set<string>::const_iterator i = directDependencies.begin(); i != directDependencies.end(); ++i)
4797 getNodeClass(*i);
4798
4799 set<string> dependencies;
4800 for (set<string>::iterator i = directDependencies.begin(); i != directDependencies.end(); ++i)
4801 {
4802 string moduleKey = *i;
4803
4804 dependencies.insert(moduleKey);
4805
4806 // First pass: Find all dependencies of the direct dependency that have been loaded so far.
4807 vector<VuoDirectedAcyclicGraph::Vertex *> firstPassVertices = dependencyGraph->findVertex(moduleKey);
4808 set<VuoDirectedAcyclicGraph::Vertex *> firstPassDependencies(firstPassVertices.begin(), firstPassVertices.end());
4809 for (vector<VuoDirectedAcyclicGraph::Vertex *>::iterator j = firstPassVertices.begin(); j != firstPassVertices.end(); ++j)
4810 {
4811 vector<VuoDirectedAcyclicGraph::Vertex *> downstream = dependencyGraph->getDownstreamVertices(*j);
4812 firstPassDependencies.insert(downstream.begin(), downstream.end());
4813 }
4814
4815 // Make sure that any compiler-generated node classes in subcompositions have been generated and added to the dependency graph.
4816 for (set<VuoDirectedAcyclicGraph::Vertex *>::iterator j = firstPassDependencies.begin(); j != firstPassDependencies.end(); ++j)
4817 {
4818 DependencyGraphVertex *v = static_cast<DependencyGraphVertex *>(*j);
4819 getNodeClass(v->getDependency());
4820 }
4821
4822 // Second pass: Find all dependencies of the direct dependency, this time including dependencies of the node classes just generated.
4823 vector<VuoDirectedAcyclicGraph::Vertex *> moduleVertices = dependencyGraph->findVertex(moduleKey);
4824 for (vector<VuoDirectedAcyclicGraph::Vertex *>::iterator j = moduleVertices.begin(); j != moduleVertices.end(); ++j)
4825 {
4826 vector<VuoDirectedAcyclicGraph::Vertex *> downstream = dependencyGraph->getDownstreamVertices(*j);
4827 for (VuoDirectedAcyclicGraph::Vertex *v : downstream)
4828 dependencies.insert( static_cast<DependencyGraphVertex *>(v)->getDependency() );
4829 }
4830 }
4831
4832 // Check that the dependencies are compatible with the compiler's target.
4833 if (checkCompatibility)
4834 {
4836 VuoCompilerCompatibility actualCompatibility = getCompatibilityOfDependencies(dependencies);
4837
4838 if (! actualCompatibility.isCompatibleWith(neededCompatibility))
4839 {
4841 set<VuoCompilerNodeClass *> nodeClassesReported;
4842
4843 for (string moduleKey : dependencies)
4844 {
4845 VuoCompilerModule *module = getModule(moduleKey);
4846 if (module && ! module->getCompatibleTargets().isCompatibleWith(neededCompatibility))
4847 {
4848 vector<VuoCompilerNodeClass *> incompatibleNodeClasses;
4849
4850 VuoCompilerNodeClass *moduleAsNodeClass = dynamic_cast<VuoCompilerNodeClass *>(module);
4851 if (moduleAsNodeClass)
4852 incompatibleNodeClasses.push_back(moduleAsNodeClass);
4853 else
4854 {
4855 vector<VuoDirectedAcyclicGraph::Vertex *> moduleVertices = dependencyGraph->findVertex(moduleKey);
4856 for (VuoDirectedAcyclicGraph::Vertex *v : moduleVertices)
4857 {
4858 vector<VuoDirectedAcyclicGraph::Vertex *> upstream = dependencyGraph->getUpstreamVertices(v);
4859
4860 vector<string> upstreamModuleKeys;
4861 std::transform(upstream.begin(), upstream.end(),
4862 std::back_inserter(upstreamModuleKeys),
4863 [](VuoDirectedAcyclicGraph::Vertex *u){ return static_cast<DependencyGraphVertex *>(u)->getDependency(); });
4864
4865 vector<string> potentialNodeClassNames;
4866 std::set_intersection(directDependencies.begin(), directDependencies.end(),
4867 upstreamModuleKeys.begin(), upstreamModuleKeys.end(),
4868 std::back_inserter(potentialNodeClassNames));
4869
4870 for (string nodeClassName : potentialNodeClassNames)
4871 {
4872 VuoCompilerNodeClass *upstreamNodeClass = getNodeClass(nodeClassName);
4873 if (upstreamNodeClass)
4874 incompatibleNodeClasses.push_back(upstreamNodeClass);
4875 }
4876 }
4877 }
4878
4879 if (incompatibleNodeClasses.empty())
4880 {
4881 VuoCompilerIssue issue(VuoCompilerIssue::Error, "linking composition", "",
4882 "Dependencies incompatible with system",
4883 "%module is only compatible with " + module->getCompatibleTargets().toString() +
4884 ", so this composition can't run on " + neededCompatibility.toString() + ".");
4885 issue.setModule(module->getPseudoBase());
4886 issues->append(issue);
4887 }
4888 else
4889 {
4890 for (VuoCompilerNodeClass *nodeClass : incompatibleNodeClasses)
4891 {
4892 if (nodeClassesReported.find(nodeClass) != nodeClassesReported.end())
4893 continue;
4894
4895 nodeClassesReported.insert(nodeClass);
4896
4897 VuoCompilerIssue issue(VuoCompilerIssue::Error, "linking composition", "",
4898 "Nodes incompatible with system",
4899 "%module is only compatible with " + module->getCompatibleTargets().toString() +
4900 ", so this composition can't run on " + neededCompatibility.toString() + ".");
4901 issue.setModule(nodeClass->getPseudoBase());
4902 issues->append(issue);
4903 }
4904 }
4905 }
4906 }
4907
4908 if (issues->isEmpty())
4909 {
4910 VuoCompilerIssue issue(VuoCompilerIssue::Error, "linking composition", "",
4911 "Dependencies incompatible with system",
4912 "Some dependencies of this composition are only compatible with " + actualCompatibility.toString() +
4913 ", so this composition can't run on " + neededCompatibility.toString() + ".");
4914 issues->append(issue);
4915 }
4916
4917 throw VuoCompilerException(issues, true);
4918 }
4919 }
4920
4921 // Add the libraries needed by every linked composition.
4922 vector<string> coreDependencies = getCoreVuoDependencies();
4923 dependencies.insert(coreDependencies.begin(), coreDependencies.end());
4924
4925 return dependencies;
4926}
4927
4932{
4933 VuoCompilerCompatibility compatibility(nullptr);
4934
4935 for (string dependency : dependencies)
4936 {
4937 VuoCompilerModule *module = getModule(dependency);
4938 if (module)
4939 compatibility = compatibility.intersection(module->getCompatibleTargets());
4940 }
4941
4942 return compatibility;
4943}
4944
4949void VuoCompiler::getLinkerInputs(const set<string> &dependencies, Optimization optimization,
4950 set<Module *> &modules, set<string> &libraries, set<string> &frameworks)
4951{
4952 set<string> builtInModuleAndLibraryDependencies;
4953 set<string> userModuleAndLibraryDependencies;
4954 map<string, set<string> > builtInCacheDependencies;
4955 map<string, set<string> > userCacheDependencies;
4956 set<Module *> builtInModules;
4957 set<Module *> userModules;
4958 set<string> builtInLibraries;
4959 set<string> userLibraries;
4960 set<string> externalLibraries;
4961
4962 getLinkerInputs(dependencies, optimization,
4963 builtInModuleAndLibraryDependencies, userModuleAndLibraryDependencies, builtInCacheDependencies, userCacheDependencies,
4964 builtInModules, userModules, builtInLibraries, userLibraries, externalLibraries, frameworks);
4965
4966 modules.insert(builtInModules.begin(), builtInModules.end());
4967 modules.insert(userModules.begin(), userModules.end());
4968 libraries.insert(builtInLibraries.begin(), builtInLibraries.end());
4969 libraries.insert(userLibraries.begin(), userLibraries.end());
4970 libraries.insert(externalLibraries.begin(), externalLibraries.end());
4971}
4972
4986void VuoCompiler::getLinkerInputs(const set<string> &dependencies, Optimization optimization,
4987 set<string> &builtInModuleAndLibraryDependencies, set<string> &userModuleAndLibraryDependencies,
4988 map<string, set<string> > &builtInCacheDependencies, map<string, set<string> > &userCacheDependencies,
4989 set<Module *> &builtInModules, set<Module *> &userModules,
4990 set<string> &builtInLibraries, set<string> &userLibraries,
4991 set<string> &externalLibraries, set<string> &externalFrameworks)
4992{
4993 bool shouldUseModuleCache = (optimization == Optimization_FastBuild || optimization == Optimization_FastBuildExistingCache);
4994 if (shouldUseModuleCache)
4995 useModuleCache(true, optimization == Optimization_FastBuildExistingCache);
4996
4997 __block vector<string> librarySearchPaths;
4998 void (^envGetLibrarySearchPaths)(Environment *) = ^void (Environment *env) {
4999 vector<string> result = env->getLibrarySearchPaths();
5000 librarySearchPaths.insert(librarySearchPaths.end(), result.begin(), result.end());
5001 };
5002 applyToInstalledEnvironments(envGetLibrarySearchPaths);
5003
5004 for (set<string>::iterator i = dependencies.begin(); i != dependencies.end(); ++i)
5005 {
5006 string dependency = *i;
5007
5008 bool foundInCache = false;
5009 string moduleCachePath;
5010 bool isInBuiltInModuleCache = false;
5011 if (shouldUseModuleCache)
5012 foundInCache = findInModuleCache(dependency, moduleCachePath, isInBuiltInModuleCache);
5013
5014 if (foundInCache)
5015 {
5016 if (isInBuiltInModuleCache)
5017 {
5018 builtInLibraries.insert(moduleCachePath);
5019 builtInCacheDependencies[moduleCachePath].insert(dependency);
5020 }
5021 else
5022 {
5023 userLibraries.insert(moduleCachePath);
5024 userCacheDependencies[moduleCachePath].insert(dependency);
5025 }
5026 }
5027 else
5028 {
5029 __block VuoCompilerModule *module = NULL;
5030 void (^envFindModule)(Environment *) = ^void (Environment *env) {
5031 VuoCompilerModule *result = env->findModule(dependency);
5032 if (result)
5033 module = result;
5034 };
5035 applyToAllEnvironments(envFindModule);
5036
5037 if (module)
5038 {
5039 VuoCompilerNodeClass *nodeClass = dynamic_cast<VuoCompilerNodeClass *>(module);
5040 if (! (nodeClass && VuoCompilerSpecializedNodeClass::hasGenericPortTypes(nodeClass)) ) // Skip not-fully-specialized generic modules
5041 {
5042 string modulePath = module->getModulePath();
5043 if (! modulePath.empty() && dynamic_cast<VuoCompilerType *>(module))
5044 {
5045 if (module->isBuiltIn())
5046 builtInLibraries.insert(modulePath);
5047 else
5048 userLibraries.insert(modulePath);
5049 }
5050 else
5051 {
5052 if (module->isBuiltIn())
5053 builtInModules.insert(module->getModule());
5054 else
5055 userModules.insert(module->getModule());
5056 }
5057
5058 if (module->isBuiltIn())
5059 builtInModuleAndLibraryDependencies.insert(dependency);
5060 else
5061 userModuleAndLibraryDependencies.insert(dependency);
5062 }
5063 }
5064 else
5065 {
5066 if (VuoStringUtilities::endsWith(dependency, ".framework"))
5067 externalFrameworks.insert(dependency);
5068 else
5069 {
5070 string dependencyPath = getLibraryPath(dependency, librarySearchPaths);
5071 if (! dependencyPath.empty())
5072 externalLibraries.insert(dependencyPath);
5073
5074 // On macOS 11, libc.dylib and libobjc.dylib are not present,
5075 // but we can still link since Vuo.framework includes the TBDs.
5076 else if (dependency != "c"
5077 && dependency != "objc")
5078 VUserLog("Warning: Could not locate dependency '%s'.", dependency.c_str());
5079 }
5080 }
5081 }
5082 }
5083}
5084
5090string VuoCompiler::getLibraryPath(const string &dependency, vector<string> librarySearchPaths)
5091{
5092 if (dependency[0] == '/' && VuoFileUtilities::fileExists(dependency))
5093 return dependency;
5094
5095 // Put the system library folder last in the list — prefer to use the libraries bundled in Vuo.framework.
5096 // Don't attempt to use OpenSSL from the system library folder, since macOS 10.15 prevents linking to it.
5097 if (dependency != "crypto"
5098 && dependency != "ssl")
5099 librarySearchPaths.push_back("/usr/lib");
5100
5101 for (auto &path : librarySearchPaths)
5102 {
5103 vector<string> variations;
5104 variations.push_back(path + "/" + dependency);
5105 variations.push_back(path + "/lib" + dependency);
5106 variations.push_back(path + "/lib" + dependency + ".dylib");
5107 variations.push_back(path + "/lib" + dependency + ".a");
5108 for (auto &variation : variations)
5109 if (VuoFileUtilities::fileExists(variation))
5110 return variation;
5111 }
5112
5113 return "";
5114}
5115
5123void VuoCompiler::useModuleCache(bool shouldUseExistingBuiltInCaches, bool shouldUseExistingOtherCaches)
5124{
5125 loadModulesIfNeeded();
5126 dispatch_group_wait(moduleSourceCompilersExist, DISPATCH_TIME_FOREVER); // Wait for any previous loadModulesIfNeeded() calls to complete.
5127
5128 // Iterate through the environments in the order that the caches need to be built.
5129
5130 dispatch_sync(environmentQueue, ^{
5131 set<string> dylibsForCachesOfInstalledModules;
5132 set<string> frameworksForCachesOfInstalledModules;
5133 unsigned long lastPrerequisiteModuleCacheRebuild = 0;
5134 for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
5135 {
5136 set<string> dylibsForCacheOfGeneratedModules;
5137 set<string> frameworksForCacheOfGeneratedModules;
5138
5139 for (int j = i->size() - 1; j >= 0; --j)
5140 {
5141 Environment *env = i->at(j);
5142 bool installed = (j == 0);
5143
5144 set<string> cacheableModulesAndDependencies;
5145 set<string> dylibsNeededToLinkToThisCache;
5146 set<string> frameworksNeededToLinkToThisCache;
5147 env->getCacheableModulesAndDependencies(cacheableModulesAndDependencies,
5148 dylibsNeededToLinkToThisCache, frameworksNeededToLinkToThisCache);
5149
5150 set<string> accumulatedDylibs;
5151 accumulatedDylibs.insert(dylibsNeededToLinkToThisCache.begin(), dylibsNeededToLinkToThisCache.end());
5152 accumulatedDylibs.insert(dylibsForCachesOfInstalledModules.begin(), dylibsForCachesOfInstalledModules.end());
5153 accumulatedDylibs.insert(dylibsForCacheOfGeneratedModules.begin(), dylibsForCacheOfGeneratedModules.end());
5154
5155 set<string> accumulatedFrameworks;
5156 accumulatedFrameworks.insert(frameworksNeededToLinkToThisCache.begin(), frameworksNeededToLinkToThisCache.end());
5157 accumulatedFrameworks.insert(frameworksForCachesOfInstalledModules.begin(), frameworksForCachesOfInstalledModules.end());
5158 accumulatedFrameworks.insert(frameworksForCacheOfGeneratedModules.begin(), frameworksForCacheOfGeneratedModules.end());
5159
5160 bool shouldUseExistingCache = (env->isBuiltIn() ? shouldUseExistingBuiltInCaches : shouldUseExistingOtherCaches);
5161 env->useModuleCache(shouldUseExistingCache, this, cacheableModulesAndDependencies,
5162 accumulatedDylibs, accumulatedFrameworks, lastPrerequisiteModuleCacheRebuild);
5163
5164 string cacheDylib = env->getCurrentModuleCacheDylib();
5165 accumulatedDylibs.insert(cacheDylib);
5166 dylibsForCachesOfInstalledModules.insert(cacheDylib);
5167
5168 lastPrerequisiteModuleCacheRebuild = max(lastPrerequisiteModuleCacheRebuild, env->getLastModuleCacheRebuild());
5169
5170 if (installed)
5171 {
5172 dylibsForCachesOfInstalledModules.insert(dylibsNeededToLinkToThisCache.begin(), dylibsNeededToLinkToThisCache.end());
5173 frameworksForCachesOfInstalledModules.insert(frameworksNeededToLinkToThisCache.begin(), frameworksNeededToLinkToThisCache.end());
5174 }
5175 else
5176 {
5177 dylibsForCacheOfGeneratedModules.insert(dylibsNeededToLinkToThisCache.begin(), dylibsNeededToLinkToThisCache.end());
5178 frameworksForCacheOfGeneratedModules.insert(frameworksNeededToLinkToThisCache.begin(), frameworksNeededToLinkToThisCache.end());
5179 }
5180 }
5181 }
5182 });
5183
5184 Environment::waitForModuleCachesToBuild();
5185}
5186
5195bool VuoCompiler::findInModuleCache(const string &moduleOrDependency, string &cachePath, bool &isBuiltinCache)
5196{
5197 __block bool found = false;
5198 __block string outPath;
5199 __block bool outBuiltin;
5200 dispatch_sync(environmentQueue, ^{
5201 for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
5202 {
5203 bool builtin = (i == environments.begin());
5204
5205 for (int j = i->size() - 1; j >= 0; --j)
5206 {
5207 Environment *env = i->at(j);
5208
5209 string resultPath;
5210 bool resultFound = env->findInModuleCache(moduleOrDependency, resultPath);
5211 if (resultFound)
5212 {
5213 found = true;
5214 outPath = resultPath;
5215 outBuiltin = builtin;
5216 }
5217 }
5218 }
5219 });
5220
5221 cachePath = outPath;
5222 isBuiltinCache = outBuiltin;
5223 return found;
5224}
5225
5234{
5235 dispatch_group_async(moduleCacheBuilding, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
5236 useModuleCache(true, false);
5237 });
5238}
5239
5245{
5246 return target;
5247}
5248
5254{
5255 return getTargetArch(target);
5256}
5257
5271void VuoCompiler::generateBuiltInModuleCaches(string vuoFrameworkPath, string target)
5272{
5273 vuoFrameworkInProgressPath = vuoFrameworkPath;
5274
5275 VuoCompiler compiler("", target);
5276 compiler.useModuleCache(false, true);
5277}
5278
5287{
5288 unsigned long maxSeconds = 30 * 24 * 60 * 60; // 30 days
5289
5290 set<VuoFileUtilities::File *> cacheDirs = VuoFileUtilities::findAllFilesInDirectory(VuoFileUtilities::getCachePath());
5291 for (set<VuoFileUtilities::File *>::iterator i = cacheDirs.begin(); i != cacheDirs.end(); ++i)
5292 {
5293 string path = (*i)->path();
5294
5295 string file = (*i)->basename();
5296 if (file != "Builtin" && file != "System" && file != "User")
5297 {
5298 unsigned long fileSeconds = VuoFileUtilities::getSecondsSinceFileLastAccessed(path);
5299 if (fileSeconds > maxSeconds)
5301 }
5302
5303 if (VuoStringUtilities::beginsWith(file, Environment::pidCacheDirPrefix))
5304 {
5305 string pidAsString = file.substr(Environment::pidCacheDirPrefix.length());
5306 int pid = atoi(pidAsString.c_str());
5307 if (kill(pid, 0) != 0) // no running process has this pid
5309 }
5310
5311 delete *i;
5312 }
5313}
5314
5319void VuoCompiler::setLoadAllModules(bool shouldLoadAllModules)
5320{
5321 dispatch_sync(modulesToLoadQueue, ^{
5322 this->shouldLoadAllModules = shouldLoadAllModules;
5323 });
5324}
5325
5338void 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)
5339{
5340 VUserLog("Linking '%s' (%s)…", outputPath.c_str(), getTargetArch(target).c_str());
5341 // https://stackoverflow.com/questions/11657529/how-to-generate-an-executable-from-an-llvmmodule
5342
5343 bool ownsIssues = false;
5344 if (! issues)
5345 {
5346 issues = new VuoCompilerIssues;
5347 ownsIssues = true;
5348 }
5349
5350 // Write all the modules with renamed symbols to a composite module file (since the linker can't operate on in-memory modules).
5351 string compositeModulePath = VuoFileUtilities::makeTmpFile("composite", "bc");
5352 dispatch_sync(llvmQueue, ^{
5353 double t0 = VuoLogGetTime();
5354
5355 unique_ptr<Module> compositeModule(new Module("composite", *globalLLVMContext));
5356 Linker linker(*compositeModule);
5357 setTargetForModule(compositeModule.get(), target);
5358
5359 for (auto i : modules)
5360 {
5361 unique_ptr<Module> upi = llvm::CloneModule(i);
5362 if (linker.linkInModule(std::move(upi)))
5363 {
5364 VuoCompilerIssue issue(VuoCompilerIssue::IssueType::Error, "linking composite module", "",
5365 "", "Failed to link in the module with ID '" + i->getModuleIdentifier() + "'");
5366 issues->append(issue);
5367 }
5368 }
5369
5370 writeModuleToBitcode(compositeModule.get(), compositeModulePath);
5371
5372 VUserLog("\tLinkModules took %5.2fs", VuoLogGetTime() - t0);
5373 });
5374
5375
5376 // llvm-3.1/llvm/tools/clang/tools/driver/driver.cpp
5377
5378 // Invoke clang as `clang++` so it includes the C++ standard libraries.
5379 string clangPath(getClangPath() + "++");
5380
5381 vector<const char *> args;
5382 vector<char *> argsToFree;
5383 args.push_back(clangPath.c_str());
5384
5385 {
5386 char *outputPathZ = strdup(("-o" + outputPath).c_str());
5387 args.push_back(outputPathZ);
5388 argsToFree.push_back(outputPathZ);
5389 }
5390
5391 args.push_back(compositeModulePath.c_str());
5392
5393 vector<string> coreDependencies = getCoreVuoDependencies();
5394 for (set<string>::const_iterator i = libraries.begin(); i != libraries.end(); ++i)
5395 {
5396 string library = *i;
5397
5398 for (vector<string>::iterator j = coreDependencies.begin(); j != coreDependencies.end(); ++j)
5399 {
5400 string coreDependency = *j;
5401 if (VuoStringUtilities::endsWith(library, "lib" + coreDependency + ".a"))
5402 args.push_back("-force_load"); // Load all symbols of static core dependencies, not just those used in the objects.
5403 }
5404
5405 // Use the pre-built native object file if it exists (faster than converting .bc to .o every build).
5406 if (VuoStringUtilities::endsWith(library, ".bc"))
5407 {
5408 string libraryObject = VuoStringUtilities::substrBefore(library, ".bc") + ".o";
5409 if (VuoFileUtilities::fileExists(libraryObject))
5410 library = libraryObject;
5411 }
5412
5413 char *libraryZ = strdup(library.c_str());
5414 args.push_back(libraryZ);
5415 argsToFree.push_back(libraryZ);
5416 }
5417
5418 // Add framework search paths
5419 vector<string> frameworkArguments;
5420
5421 __block vector<string> frameworkSearchPaths;
5422 void (^envGetFrameworkSearchPaths)(Environment *) = ^void (Environment *env) {
5423 vector<string> result = env->getFrameworkSearchPaths();
5424 frameworkSearchPaths.insert(frameworkSearchPaths.end(), result.begin(), result.end());
5425 };
5426 applyToInstalledEnvironments(envGetFrameworkSearchPaths);
5427
5428 for (vector<string>::const_iterator i = frameworkSearchPaths.begin(); i != frameworkSearchPaths.end(); ++i)
5429 {
5430 string a = "-F"+*i;
5431 // 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.
5432 frameworkArguments.push_back(a);
5433 char *frameworkArgument = strdup(a.c_str());
5434 args.push_back(frameworkArgument);
5435 argsToFree.push_back(frameworkArgument);
5436 }
5437
5438 for (set<string>::const_iterator i = frameworks.begin(); i != frameworks.end(); ++i)
5439 {
5440 args.push_back("-framework");
5441
5442 string frameworkName = *i;
5443 frameworkName = frameworkName.substr(0, frameworkName.length() - string(".framework").length());
5444 char *frameworkNameZ = strdup(frameworkName.c_str());
5445 args.push_back(frameworkNameZ);
5446 argsToFree.push_back(frameworkNameZ);
5447 }
5448
5449 // When linking on a development workstation or Jenkins or an end-user system,
5450 // use the partial macOS SDK bundled in Vuo.framework, since it includes all the TBDs we need.
5451 string vuoFrameworkPath = getVuoFrameworkPath();
5452 string frameworkMacOSSDKFolder = vuoFrameworkPath + "/SDKs/MacOSX.sdk";
5453 if (!VuoFileUtilities::fileExists(frameworkMacOSSDKFolder))
5454 throw VuoException("Couldn't find the macOS SDK.");
5455
5456 args.push_back("-Xlinker");
5457 args.push_back("-syslibroot");
5458 args.push_back("-Xlinker");
5459 char *frameworkMacOSSDKFolderZ = strdup(frameworkMacOSSDKFolder.c_str());
5460 args.push_back(frameworkMacOSSDKFolderZ);
5461 argsToFree.push_back(frameworkMacOSSDKFolderZ);
5462
5463 args.push_back("-Xlinker");
5464 args.push_back("-platform_version");
5465 args.push_back("-Xlinker");
5466 args.push_back("macos");
5467 args.push_back("-Xlinker");
5468 char *deploymentTargetZ = strdup(MACOS_DEPLOYMENT_TARGET);
5469 args.push_back(deploymentTargetZ);
5470 argsToFree.push_back(deploymentTargetZ);
5471 args.push_back("-Xlinker");
5472 char *sdkVersionZ = strdup(MACOS_SDK_VERSION);
5473 args.push_back(sdkVersionZ);
5474 argsToFree.push_back(sdkVersionZ);
5475
5476 // Linker option necessary for compatibility with our bundled version of ld64:
5477 args.push_back("-Xlinker");
5478 args.push_back("--no-demangle");
5479
5480 if (isVerbose)
5481 args.push_back("-v");
5482
5483 if (isDylib)
5484 args.push_back("-dynamiclib");
5485
5486 args.push_back("-Xlinker");
5487 args.push_back("-headerpad_max_install_names");
5488
5489 // Tell the built dylib/executable where to find dylibs that it depends on
5490 for (const string &rPath : rPaths)
5491 {
5492 args.push_back("-rpath");
5493 args.push_back(rPath.c_str());
5494 }
5495
5496#ifdef COVERAGE
5497 args.push_back("-rpath");
5498 args.push_back(LLVM_ROOT "/lib");
5499#endif
5500
5501 args.push_back("-target");
5502 args.push_back(target.c_str());
5503
5504 args.push_back("-std=c++14");
5505 args.push_back("-stdlib=libc++");
5506 args.push_back("-mmacosx-version-min=10.10");
5507
5508 // Allow clang to print meaningful error messages.
5509 auto diagnosticConsumer = new VuoCompilerDiagnosticConsumer(issues);
5510 clang::DiagnosticOptions *diagOptions = new clang::DiagnosticOptions();
5511 IntrusiveRefCntPtr<clang::DiagnosticIDs> DiagID(new clang::DiagnosticIDs());
5512 clang::DiagnosticsEngine Diags(DiagID, diagOptions, diagnosticConsumer);
5513
5514 if (isVerbose)
5515 {
5516 ostringstream s;
5517 for (vector<const char *>::iterator i = args.begin(); i != args.end(); ++i)
5518 s << *i << " ";
5519 VUserLog("\t%s", s.str().c_str());
5520 }
5521
5522 // Redirect linker output to a file, so we can feed it through VuoLog.
5523 string stdoutFile = VuoFileUtilities::makeTmpFile("vuo-linker-output", "txt");
5524 const StringRef stdoutPath(stdoutFile);
5525 const StringRef *redirects[] = {
5526 nullptr, // stdin
5527 &stdoutPath, // stdout
5528 &stdoutPath, // stderr
5529 };
5530
5531 // ExecuteAndWait's args needs to be null-terminated.
5532 const char **argsz = (const char **)malloc(sizeof(char *) * args.size() + 1);
5533 for (int i = 0; i < args.size(); ++i)
5534 argsz[i] = args[i];
5535 argsz[args.size()] = nullptr;
5536
5537 string errMsg;
5538 bool executionFailed;
5539 double t0 = VuoLogGetTime();
5540 int ret = llvm::sys::ExecuteAndWait(args[0], argsz, nullptr, redirects, 0, 0, &errMsg, &executionFailed);
5541
5542 for (auto i : argsToFree)
5543 free(i);
5544
5545 // Clean up composite module file.
5546 remove(compositeModulePath.c_str());
5547
5548 if (!isDylib)
5549 // Ensure the linked binary has the execute permission set
5550 // (ld64-242 doesn't reliably set it, whereas ld64-133.3 did).
5551 // https://b33p.net/kosada/node/14152
5552 chmod(outputPath.c_str(), 0755);
5553
5554 if (ret != 0)
5555 {
5556 string details;
5557 if (!errMsg.empty())
5558 {
5559 VUserLog("%s", errMsg.c_str());
5560 details += "\n" + errMsg + "\n";
5561 }
5562 string stdoutFileContents = VuoFileUtilities::readFileToString(stdoutFile);
5563 if (!stdoutFileContents.empty())
5564 {
5565 VUserLog("%s", stdoutFileContents.c_str());
5566 details += "\n" + stdoutFileContents + "\n";
5567 }
5568 VuoFileUtilities::deleteFile(stdoutFile);
5569
5570 VuoCompilerIssue issue(VuoCompilerIssue::Error, "linking composition", outputPath, "", details);
5571 issues->append(issue);
5572 throw VuoCompilerException(issues, ownsIssues);
5573 }
5574
5575 VuoFileUtilities::deleteFile(stdoutFile);
5576 VUserLog("\tLinking took %5.2fs", VuoLogGetTime() - t0);
5577
5578 if (shouldAdHocCodeSign)
5579 adHocCodeSign(outputPath);
5580}
5581
5585void VuoCompiler::adHocCodeSign(string path)
5586{
5588#if VUO_PRO
5589 "CODESIGN_ALLOCATE=" + getCodesignAllocatePath(),
5590#endif
5591 });
5592}
5593
5599Module *VuoCompiler::readModuleFromC(string inputPath, const vector<string> &headerSearchPaths, const vector<string> &extraArgs, VuoCompilerIssues *issues)
5600{
5601 // llvm-3.1/llvm/tools/clang/examples/clang-interpreter/main.cpp
5602
5603 vector<const char *> args;
5604 vector<char *> argsToFree;
5605
5606 args.push_back(inputPath.c_str());
5607 args.push_back("-DVUO_COMPILER");
5608 args.push_back("-fblocks");
5609
5610 // Provide full backtraces, for easier debugging using Instruments and `VuoLog_backtrace()`.
5611 // The Clang driver translates `-fno-omit-frame-pointer` to `clang -cc1`'s `-mdisable-fp-elim`.
5612 // In Clang 10, this was renamed to `-mframe-pointer=all`.
5613 // https://b33p.net/kosada/vuo/vuo/-/issues/19064
5614 args.push_back("-mdisable-fp-elim");
5615
5616 // Sync with /CMakeLists.txt's `commonFlags`.
5617 args.push_back("-Wall");
5618 args.push_back("-Wextra");
5619 args.push_back("-Wimplicit-fallthrough");
5620 args.push_back("-Wno-unused-parameter");
5621 args.push_back("-Wno-sign-compare");
5622 args.push_back("-Werror=implicit");
5623
5624 if (VuoStringUtilities::endsWith(inputPath, ".cc"))
5625 {
5626 args.push_back("-std=c++14");
5627 args.push_back("-stdlib=libc++");
5628 args.push_back("-fexceptions");
5629 args.push_back("-fcxx-exceptions");
5630 }
5631
5632 for (vector<string>::const_iterator i = headerSearchPaths.begin(); i != headerSearchPaths.end(); ++i)
5633 {
5634 args.push_back("-I");
5635 args.push_back(i->c_str());
5636 }
5637
5638#if VUO_PRO
5639 string dependencyOutputPath = _dependencyOutput();
5640 if (!dependencyOutputPath.empty())
5641 {
5642 char *outputObjectPath = strdup(dependencyOutputPath.substr(0, dependencyOutputPath.length() - 2).c_str());
5643 argsToFree.push_back(outputObjectPath);
5644
5645 // https://bugzilla.mozilla.org/show_bug.cgi?id=1340588#c4
5646 // https://lists.llvm.org/pipermail/cfe-users/2018-March/001268.html
5647 args.push_back("-MT");
5648 args.push_back(outputObjectPath);
5649 args.push_back("-dependency-file");
5650 args.push_back(dependencyOutputPath.c_str());
5651 }
5652#endif
5653
5654 if (isVerbose)
5655 args.push_back("-v");
5656
5657 for (vector<string>::const_iterator i = extraArgs.begin(); i != extraArgs.end(); ++i)
5658 args.push_back(i->c_str());
5659
5660 auto diagnosticConsumer = new VuoCompilerDiagnosticConsumer(issues);
5661 clang::DiagnosticOptions * diagOptions = new clang::DiagnosticOptions();
5662 IntrusiveRefCntPtr<clang::DiagnosticIDs> DiagID(new clang::DiagnosticIDs());
5663 clang::DiagnosticsEngine *diags = new clang::DiagnosticsEngine(DiagID, diagOptions, diagnosticConsumer);
5664
5665 // This invokes `clang -cc1`. See `clang -cc1 --help` for available options.
5666 shared_ptr<clang::CompilerInvocation> compilerInvocation(new clang::CompilerInvocation);
5667 clang::CompilerInvocation::CreateFromArgs(*compilerInvocation, &args[0], &args[0] + args.size(), *diags);
5668 compilerInvocation->TargetOpts->Triple = target;
5669
5670 clang::CompilerInstance Clang;
5671 Clang.setInvocation(compilerInvocation);
5672
5673 Clang.setDiagnostics(diags);
5674 if (!Clang.hasDiagnostics())
5675 return NULL;
5676
5677 // See CompilerInvocation::GetResourcesPath -- though we're not calling it because we don't have MainAddr.
5678 string builtinHeaderSearchPath;
5679 string vuoFrameworkPath = getVuoFrameworkPath();
5680 if (vuoFrameworkPath.empty())
5681 {
5682 builtinHeaderSearchPath = getClangPath();
5683 if (VuoStringUtilities::endsWith(builtinHeaderSearchPath, "Helpers/clang"))
5684 builtinHeaderSearchPath = VuoStringUtilities::substrBefore(builtinHeaderSearchPath, "Helpers/clang");
5685 else if (VuoStringUtilities::endsWith(builtinHeaderSearchPath, "bin/clang"))
5686 builtinHeaderSearchPath = VuoStringUtilities::substrBefore(builtinHeaderSearchPath, "bin/clang");
5687 builtinHeaderSearchPath += "lib/clang/" CLANG_VERSION_STRING;
5688 }
5689 else
5690 builtinHeaderSearchPath = vuoFrameworkPath + "/Frameworks/llvm.framework/Versions/A/lib/clang/" CLANG_VERSION_STRING;
5691 Clang.getHeaderSearchOpts().ResourceDir = builtinHeaderSearchPath;
5692
5693// OwningPtr<clang::CodeGenAction> Act(new clang::EmitLLVMOnlyAction()); // @@@ return value of takeModule() is destroyed at the end of this function
5694 clang::CodeGenAction *Act = new clang::EmitLLVMOnlyAction();
5695 if (!Clang.ExecuteAction(*Act))
5696 return NULL;
5697
5698 for (auto i : argsToFree)
5699 free(i);
5700
5701 unique_ptr<Module> module = Act->takeModule();
5702 if (!module)
5703 VUserLog("Error compiling %s: module is null.", inputPath.c_str());
5704 return module.release();
5705}
5706
5712Module *VuoCompiler::readModuleFromBitcode(string inputPath, string arch)
5713{
5714 string dir, file, ext;
5715 VuoFileUtilities::splitPath(inputPath, dir, file, ext);
5716 VuoFileUtilities::File inputFile(dir, file + "." + ext);
5717 return readModuleFromBitcode(&inputFile, arch);
5718}
5719
5727Module *VuoCompiler::readModuleFromBitcode(VuoFileUtilities::File *inputFile, string arch)
5728{
5729 size_t inputDataBytes;
5730 char *inputData = inputFile->getContentsAsRawData(inputDataBytes);
5731
5732 string error;
5733 set<string> availableArchs;
5734 VuoLog_status("Loading module \"%s\" (%s)", inputFile->getRelativePath().c_str(), arch.c_str());
5735 Module *module = readModuleFromBitcodeData(inputData, inputDataBytes, arch, availableArchs, error);
5736 VuoLog_status(NULL);
5737 if (! module)
5738 VUserLog("Error: Couldn't parse module '%s' (%s): %s.", inputFile->getRelativePath().c_str(), arch.c_str(), error.c_str());
5739
5740 free(inputData);
5741
5742 return module;
5743}
5744
5750Module *VuoCompiler::readModuleFromBitcodeData(char *inputData, size_t inputDataBytes, string arch,
5751 set<string> &availableArchs, string &error)
5752{
5753 if (inputDataBytes < sizeof(unsigned int))
5754 return nullptr;
5755
5756 __block Module *module = nullptr;
5757 dispatch_sync(llvmQueue, ^{
5758 StringRef inputDataAsStringRef(inputData, inputDataBytes);
5759 auto mb = MemoryBuffer::getMemBuffer(inputDataAsStringRef, "", false);
5760 if (!mb)
5761 {
5762 error = "Couldn't create MemoryBuffer";
5763 return;
5764 }
5765
5766 MemoryBufferRef bitcodeBuffer;
5767 string moduleArch;
5768 unsigned int fileID = *(unsigned int *)inputData;
5769 if (fileID == 0x0b17c0de)
5770 // This is a single-architecture LLVM bitcode `.bc` file, so read the entire file.
5771 bitcodeBuffer = mb.get()->getMemBufferRef();
5772
5773 else if (fileID == 0xdec04342)
5774 {
5775 // This is a single-architecture raw bitcode file, presumably generated by Vuo 2.2.1 or earlier.
5776 bitcodeBuffer = mb.get()->getMemBufferRef();
5777 moduleArch = "x86_64";
5778 availableArchs.insert(moduleArch);
5779 }
5780
5781 else if (fileID == 0xbebafeca)
5782 {
5783 if (arch.empty())
5784 {
5785 error = "It's a Mach-O universal binary, but this compiler instance's LLVM target isn't set";
5786 return;
5787 }
5788
5789 // This is a Mach-O wrapper around multiple LLVM bitcode files;
5790 // parse the Mach-O header to extract just a single architecture.
5791 auto binary = llvm::object::MachOUniversalBinary::create(mb.get()->getMemBufferRef());
5792 if (!binary)
5793 {
5794 error = "Couldn't read Mach-O universal binary:";
5795 handleAllErrors(binary.takeError(), [&error](const ErrorInfoBase &ei) {
5796 error += " " + ei.message();
5797 });
5798 return;
5799 }
5800
5801 for (auto &o : binary.get()->objects())
5802 {
5803 if (o.getArchFlagName() == arch)
5804 bitcodeBuffer = MemoryBufferRef(mb.get()->getMemBufferRef().getBuffer().slice(o.getOffset(), o.getOffset() + o.getSize()), "");
5805
5806 availableArchs.insert(o.getArchFlagName());
5807 }
5808
5809 if (!bitcodeBuffer.getBufferSize())
5810 {
5811 error = "The Mach-O universal binary doesn't have an \"" + arch + "\" slice";
5812 return;
5813 }
5814 }
5815
5816 auto wrappedModule = llvm::parseBitcodeFile(bitcodeBuffer, *globalLLVMContext);
5817 if (!wrappedModule)
5818 {
5819 error = "Couldn't parse bitcode file:";
5820 handleAllErrors(wrappedModule.takeError(), [&error](const ErrorInfoBase &ei) {
5821 error += " " + ei.message();
5822 });
5823 return;
5824 }
5825
5826 module = wrappedModule.get().release();
5827
5828 if (moduleArch.empty())
5829 moduleArch = getTargetArch(module->getTargetTriple());
5830
5831 if (availableArchs.empty())
5832 availableArchs.insert(moduleArch);
5833
5834 if (moduleArch != arch)
5835 {
5836 error = "The module's CPU architecture \"" + moduleArch + "\" doesn't match the compiler's CPU architecture \"" + arch + "\"";
5837 delete module;
5838 module = nullptr;
5839 return;
5840 }
5841 });
5842 return module;
5843}
5844
5852bool VuoCompiler::writeModuleToBitcode(Module *module, string outputPath)
5853{
5854 string str;
5855 raw_string_ostream verifyOut(str);
5856 if (llvm::verifyModule(*module, &verifyOut))
5857 {
5858 VUserLog("Error: Module verification failed:\n%s", verifyOut.str().c_str());
5859 return true;
5860 }
5861
5862 // Ensure the module gets output in the bitcode wrapper format instead of raw bitcode.
5863 if (module->getTargetTriple().empty())
5864 setTargetForModule(module, getProcessTarget());
5865
5866 std::error_code err;
5867 raw_fd_ostream out(outputPath.c_str(), err, sys::fs::F_None);
5868 if (err)
5869 {
5870 VUserLog("Error: Couldn't open file '%s' for writing: %s", outputPath.c_str(), err.message().c_str());
5871 return true;
5872 }
5873 llvm::WriteBitcodeToFile(module, out);
5874
5875 return false;
5876}
5877
5884void VuoCompiler::setTargetForModule(Module *module, string targetTriple)
5885{
5886 // Replace the OS version in targetTriple to avoid warnings about linking modules of different target triples.
5887 llvm::Triple triple(targetTriple);
5888 if (triple.isMacOSX())
5889 triple.setOSName("macosx10.10.0");
5890
5891 module->setTargetTriple(triple.str());
5892
5893 string error;
5894 auto target = TargetRegistry::lookupTarget(module->getTargetTriple(), error);
5895 if (!target)
5896 {
5897 VUserLog("Error: Couldn't look up target: %s", error.c_str());
5898 return;
5899 }
5900
5901 auto targetMachine = target->createTargetMachine(module->getTargetTriple(), "", "", TargetOptions(), Optional<Reloc::Model>());
5902 if (!targetMachine)
5903 {
5904 VUserLog("Error: Couldn't create targetMachine.");
5905 return;
5906 }
5907
5908 module->setDataLayout(targetMachine->createDataLayout());
5909
5910 delete targetMachine;
5911}
5912
5917string VuoCompiler::getTargetArch(string target)
5918{
5919 auto hyphen = target.find('-');
5920 if (hyphen == string::npos)
5921 return "";
5922
5923 return target.substr(0, hyphen);
5924}
5925
5929string VuoCompiler::getProcessTarget(void)
5930{
5931 // llvm::sys::getProcessTriple() returns `LLVM_HOST_TRIPLE`,
5932 // which is always `x86_64-*` since we built LLVM on x86_64.
5933 // Instead, use llvm::sys::getDefaultTargetTriple()
5934 // which _actually_ returns the current process's target.
5935 llvm::Triple triple(llvm::sys::getDefaultTargetTriple());
5936
5937 // Replace darwin with macosx, e.g. x86_64-apple-darwin18.7.0 -> x86_64-apple-macosx10.14.6
5938 if (triple.isMacOSX())
5939 {
5940 unsigned int major, minor, micro;
5941 if (triple.getMacOSXVersion(major, minor, micro))
5942 {
5943 ostringstream osVersion;
5944 osVersion << "macosx" << major << "." << minor << "." << micro;
5945 triple.setOSName(osVersion.str());
5946 }
5947 }
5948
5949 return triple.str();
5950}
5951
5959{
5960 Module *llvmModule = module->getModule();
5961
5962 // In C++ the return value of a cast may not be the same pointer as the cast arg.
5963 // Because of this, VuoModule::getPseudoBase() returns a different value than VuoNodeClass::getBase().
5964 // Calling delete on VuoModule::getPseudoBase() gives a malloc error: "pointer being freed was not allocated".
5965 // So call it on VuoNodeClass::getBase(). Same idea for VuoType.
5966
5967 VuoNodeClass *baseNodeClass = NULL;
5968 VuoType *baseType = NULL;
5969 VuoModule *baseModule = NULL;
5970 VuoCompilerNodeClass *nodeClass = dynamic_cast<VuoCompilerNodeClass *>(module);
5971 if (nodeClass)
5972 {
5973 baseNodeClass = dynamic_cast<VuoNodeClass *>(nodeClass->getBase());
5974
5976 if (dynamic_cast<VuoCompilerSpecializedNodeClass *>(module))
5977 module = NULL;
5978 }
5979 else
5980 {
5981 VuoCompilerType *type = dynamic_cast<VuoCompilerType *>(module);
5982 if (type)
5983 baseType = dynamic_cast<VuoType *>(type->getBase());
5984 else
5985 baseModule = module->getPseudoBase();
5986 }
5987
5988 delete module;
5989 delete baseNodeClass;
5990 delete baseType;
5991 delete baseModule;
5992 destroyLlvmModule(llvmModule);
5993}
5994
6002{
6003 dispatch_sync(llvmQueue, ^{
6004 delete module;
6005 });
6006}
6007
6019VuoNode * VuoCompiler::createNode(VuoCompilerNodeClass *nodeClass, string title, double x, double y)
6020{
6022 if (nodeClassForNode)
6023 return nodeClassForNode->newNode(title, x, y);
6024
6025 return nullptr;
6026}
6027
6034VuoNode * VuoCompiler::createNode(VuoCompilerNodeClass *nodeClass, VuoNode *nodeToCopyMetadataFrom)
6035{
6037 if (nodeClassForNode)
6038 return nodeClassForNode->newNode(nodeToCopyMetadataFrom);
6039
6040 return nullptr;
6041}
6042
6046VuoNode * VuoCompiler::createPublishedInputNode(vector<VuoPublishedPort *> publishedInputPorts)
6047{
6048 string nodeClassName = VuoCompilerPublishedInputNodeClass::buildNodeClassName(publishedInputPorts);
6049 return createPublishedNode(nodeClassName, publishedInputPorts);
6050}
6051
6055VuoNode * VuoCompiler::createPublishedOutputNode(vector<VuoPublishedPort *> publishedOutputPorts)
6056{
6057 string nodeClassName = VuoCompilerPublishedOutputNodeClass::buildNodeClassName(publishedOutputPorts);
6058 return createPublishedNode(nodeClassName, publishedOutputPorts);
6059}
6060
6064VuoNode * VuoCompiler::createPublishedNode(const string &nodeClassName, const vector<VuoPublishedPort *> &publishedPorts)
6065{
6066 VuoCompilerNodeClass *nodeClass = getNodeClass(nodeClassName);
6067 VuoNode *node = createNode(nodeClass);
6068
6069 // Set the generic port types on the node to match those on the published ports set by VuoCompilerComposition::updateGenericPortTypes().
6070 VuoCompilerPublishedInputNodeClass *inputNodeClass = dynamic_cast<VuoCompilerPublishedInputNodeClass *>(nodeClass);
6071 VuoCompilerPublishedOutputNodeClass *outputNodeClass = dynamic_cast<VuoCompilerPublishedOutputNodeClass *>(nodeClass);
6072 for (size_t i = 0; i < publishedPorts.size(); ++i)
6073 {
6074 VuoType *publishedPortType = static_cast<VuoCompilerPort *>(publishedPorts[i]->getCompiler())->getDataVuoType();
6075 if (! dynamic_cast<VuoGenericType *>(publishedPortType))
6076 continue;
6077
6078 set<VuoPort *> nodePorts;
6079 if (inputNodeClass)
6080 {
6081 VuoPort *inputPort = node->getInputPorts().at( inputNodeClass->getInputPortIndexForPublishedInputPort(i) );
6082 nodePorts.insert(inputPort);
6083
6084 VuoPort *outputPort = node->getOutputPorts().at( inputNodeClass->getOutputPortIndexForPublishedInputPort(i) );
6085 nodePorts.insert(outputPort);
6086 }
6087 else
6088 {
6089 VuoPort *inputPort = node->getInputPorts().at( outputNodeClass->getInputPortIndexForPublishedOutputPort(i) );
6090 nodePorts.insert(inputPort);
6091 }
6092
6093 for (VuoPort *port : nodePorts)
6094 {
6095 VuoCompilerPort *compilerPort = static_cast<VuoCompilerPort *>(port->getCompiler());
6096 compilerPort->setDataVuoType(publishedPortType);
6097 }
6098 }
6099
6100 reifyGenericPortTypes(node);
6101
6102 return node;
6103}
6104
6110{
6111 dispatch_sync(environmentQueue, ^{
6112 if (environments.size() >= 5)
6113 environments.at(3).at(0)->addExpatriateSourceFile(sourcePath);
6114 });
6115}
6116
6122{
6123 dispatch_sync(environmentQueue, ^{
6124 if (environments.size() >= 5)
6125 {
6126 environments.at(3).at(0)->removeExpatriateSourceFile(sourcePath);
6127
6128 set<string> sourcesRemoved;
6129 sourcesRemoved.insert(getModuleKeyForPath(sourcePath));
6130 loadModulesAndSources(set<string>(), set<string>(), set<string>(), set<string>(), set<string>(), sourcesRemoved,
6131 false, false, environments.at(3).at(0), nullptr, nullptr, "");
6132 }
6133 });
6134}
6135
6152void VuoCompiler::overrideInstalledNodeClass(const string &sourcePath, const string &sourceCode)
6153{
6154 string sourcePathCopy = sourcePath;
6155 string sourceCodeCopy = sourceCode;
6156
6157 dispatch_async(environmentQueue, ^{
6158 string nodeClassName = getModuleKeyForPath(sourcePathCopy);
6159 ModuleInfo *sourceInfo = NULL;
6160
6161 for (const vector<Environment *> &envs : environments)
6162 {
6163 for (Environment *env : envs)
6164 {
6165 ModuleInfo *potentialSourceInfo = env->listSourceFile(nodeClassName);
6166 if (potentialSourceInfo && VuoFileUtilities::arePathsEqual(potentialSourceInfo->getFile()->path(), sourcePathCopy))
6167 {
6168 sourceInfo = potentialSourceInfo;
6169 break;
6170 }
6171 }
6172 }
6173
6174 if (! sourceInfo)
6175 return;
6176
6177 bool shouldRecompileSourcesIfUnchanged;
6178 if (! sourceCodeCopy.empty())
6179 {
6180 sourceInfo->setSourceCode(sourceCodeCopy);
6181 sourceInfo->setSourceCodeOverridden(true);
6182
6183 shouldRecompileSourcesIfUnchanged = false;
6184 }
6185 else
6186 {
6187 sourceInfo->revertSourceCode();
6188 sourceInfo->setSourceCodeOverridden(false);
6189
6190 shouldRecompileSourcesIfUnchanged = true;
6191 }
6192 sourceInfo->setAttempted(false);
6193 sourceInfo->setLastModifiedToNow();
6194
6195 set<string> sourcesModified;
6196 sourcesModified.insert(nodeClassName);
6197
6198 loadModulesAndSources(set<string>(), set<string>(), set<string>(), set<string>(), sourcesModified, set<string>(),
6199 false, shouldRecompileSourcesIfUnchanged, nullptr, nullptr, nullptr, "");
6200 });
6201}
6202
6212void VuoCompiler::revertOverriddenNodeClass(const string &sourcePath)
6213{
6214 overrideInstalledNodeClass(sourcePath, "");
6215}
6216
6230{
6231 // For performance, don't bother searching if it's obviously not a node class.
6232
6233 if (VuoStringUtilities::endsWith(nodeClassName, ".framework"))
6234 return NULL;
6235
6236 // Attempt to load the node class (if it hasn't already been loaded).
6237
6238 set<string> nodeClassNameSet;
6239 nodeClassNameSet.insert(nodeClassName);
6240 loadModulesIfNeeded(nodeClassNameSet);
6241
6242 // If the node class has been loaded, return it.
6243
6244 __block VuoCompilerNodeClass *nodeClass = NULL;
6245 void (^envGetNodeClass)(Environment *) = ^void (Environment *env) {
6246 VuoCompilerNodeClass *result = env->getNodeClass(nodeClassName);
6247 if (result)
6248 nodeClass = result;
6249 };
6250 applyToAllEnvironments(envGetNodeClass);
6251 return nodeClass;
6252}
6253
6259map<string, VuoCompilerNodeClass *> VuoCompiler::getNodeClasses()
6260{
6261 loadModulesIfNeeded();
6262
6263 __block map<string, VuoCompilerNodeClass *> nodeClasses;
6264 void (^envGetNodeClasses)(Environment *) = ^void (Environment *env) {
6265 map<string, VuoCompilerNodeClass *> result = env->getNodeClasses();
6266 nodeClasses.insert(result.begin(), result.end());
6267 };
6268 applyToInstalledEnvironments(envGetNodeClasses);
6269 return nodeClasses;
6270}
6271
6277VuoCompilerType * VuoCompiler::getType(const string &typeName)
6278{
6279 set<string> typeNameSet;
6280 typeNameSet.insert(typeName);
6281 loadModulesIfNeeded(typeNameSet);
6282
6283 __block VuoCompilerType *type = NULL;
6284 void (^envGetType)(Environment *) = ^void (Environment *env) {
6285 VuoCompilerType *result = env->getType(typeName);
6286 if (result)
6287 type = result;
6288 };
6289 applyToInstalledEnvironments(envGetType);
6290
6291 if (! type && VuoGenericType::isGenericTypeName(typeName))
6292 {
6293 VuoCompilerType * (^compilerGetType)(string) = ^VuoCompilerType * (string moduleKey) {
6294 return getType(moduleKey);
6295 };
6296
6297 VuoGenericType *genericType = new VuoGenericType(typeName, vector<string>());
6298 type = VuoCompilerGenericType::newGenericType(genericType, compilerGetType);
6299 }
6300
6301 return type;
6302}
6303
6309map<string, VuoCompilerType *> VuoCompiler::getTypes()
6310{
6311 loadModulesIfNeeded();
6312
6313 __block map<string, VuoCompilerType *> types;
6314 void (^envGetTypes)(Environment *) = ^void (Environment *env) {
6315 map<string, VuoCompilerType *> result = env->getTypes();
6316 types.insert(result.begin(), result.end());
6317 };
6318 applyToInstalledEnvironments(envGetTypes);
6319 return types;
6320}
6321
6327VuoCompilerModule *VuoCompiler::getLibraryModule(const string &libraryModuleName)
6328{
6329 set<string> libraryNameSet;
6330 libraryNameSet.insert(libraryModuleName);
6331 loadModulesIfNeeded(libraryNameSet);
6332
6333 __block VuoCompilerModule *module = nullptr;
6334 void (^envGetLibraryModule)(Environment *) = ^void (Environment *env) {
6335 VuoCompilerModule *result = env->getLibraryModule(libraryModuleName);
6336 if (result)
6337 module = result;
6338 };
6339 applyToInstalledEnvironments(envGetLibraryModule);
6340
6341 return module;
6342}
6343
6349map<string, VuoCompilerModule *> VuoCompiler::getLibraryModules()
6350{
6351 loadModulesIfNeeded();
6352
6353 __block map<string, VuoCompilerModule *> libraryModules;
6354 void (^envGetLibraryModules)(Environment *) = ^void (Environment *env) {
6355 map<string, VuoCompilerModule *> result = env->getLibraryModules();
6356 libraryModules.insert(result.begin(), result.end());
6357 };
6358 applyToInstalledEnvironments(envGetLibraryModules);
6359 return libraryModules;
6360}
6361
6367map<string, VuoNodeSet *> VuoCompiler::getNodeSets()
6368{
6369 loadModulesIfNeeded();
6370
6371 __block map<string, VuoNodeSet *> nodeSets;
6372 void (^envGetNodeSets)(Environment *) = ^void (Environment *env) {
6373 map<string, VuoNodeSet *> result = env->getNodeSets();
6374 nodeSets.insert(result.begin(), result.end());
6375 };
6376 applyToInstalledEnvironments(envGetNodeSets);
6377 return nodeSets;
6378}
6379
6386{
6387 loadModulesIfNeeded();
6388
6389 __block VuoNodeSet *nodeSet = NULL;
6390 void (^envFindNodeSet)(Environment *) = ^void (Environment *env) {
6391 VuoNodeSet *result = env->findNodeSet(name);
6392 if (result)
6393 nodeSet = result;
6394 };
6395 applyToInstalledEnvironments(envFindNodeSet);
6396 return nodeSet;
6397}
6398
6402VuoCompilerModule * VuoCompiler::getModule(const string &moduleKey)
6403{
6404 __block VuoCompilerModule *module = NULL;
6405 void (^envFindModule)(Environment *) = ^void (Environment *env) {
6406 VuoCompilerModule *result = env->findModule(moduleKey);
6407 if (result)
6408 module = result;
6409 };
6410 applyToAllEnvironments(envFindModule);
6411 return module;
6412}
6413
6425void VuoCompiler::listNodeClasses(const string &format)
6426{
6427 map<string, VuoCompilerNodeClass *> nodeClasses = getNodeClasses();
6428 for (map<string, VuoCompilerNodeClass *>::const_iterator i = nodeClasses.begin(); i != nodeClasses.end(); ++i)
6429 {
6430 VuoCompilerNodeClass *nodeClass = i->second;
6431 if (format == "")
6432 {
6433 printf("%s\n", nodeClass->getBase()->getClassName().c_str());
6434 }
6435 else if (format == "path")
6436 {
6437 // TODO: If "path", prints the absolute path of each node class, one per line.
6438 }
6439 else if (format == "dot")
6440 {
6441 VuoCompilerNode *node = nodeClass->newNode()->getCompiler();
6442
6443 printf("%s\n", nodeClass->getDoxygenDocumentation().c_str());
6444 printf("%s\n\n", node->getGraphvizDeclaration().c_str());
6445
6446 delete node;
6447 }
6448 }
6449}
6450
6461vector<string> VuoCompiler::getRunPathSearchPaths(Environment *narrowestScope)
6462{
6463 vector<string> rPaths;
6464 for (size_t i = 0; i < environments.size(); ++i)
6465 {
6466 if (i == 0)
6467 {
6468 string vuoFrameworkPath = getVuoFrameworkPath();
6469 if (! vuoFrameworkPath.empty())
6470 rPaths.push_back(vuoFrameworkPath + "/..");
6471 }
6472 else if (i == 1)
6473 rPaths.push_back(VuoFileUtilities::getSystemModulesPath());
6474 else if (i == 2)
6475 rPaths.push_back(VuoFileUtilities::getUserModulesPath());
6476 else if (i == 3)
6477 {
6478 string localModulesPath = getCompositionLocalModulesPath();
6479 if (! localModulesPath.empty())
6480 rPaths.push_back(localModulesPath);
6481 }
6482
6483 if (std::find(environments[i].begin(), environments[i].end(), narrowestScope) != environments[i].end())
6484 break;
6485 }
6486
6487 std::reverse(rPaths.begin(), rPaths.end());
6488 return rPaths;
6489}
6490
6497vector<string> VuoCompiler::getCoreVuoDependencies(void)
6498{
6499 vector<string> dependencies;
6500
6501 dependencies.push_back("VuoHeap");
6502 dependencies.push_back("VuoApp");
6503
6504 dependencies.push_back("zmq");
6505 dependencies.push_back("json-c");
6506 dependencies.push_back("objc");
6507 dependencies.push_back("c");
6508 dependencies.push_back("AppKit.framework");
6509
6510#ifdef COVERAGE
6511 dependencies.push_back(LLVM_ROOT "/lib/libprofile_rt.dylib");
6512#endif
6513 return dependencies;
6514}
6515
6519string VuoCompiler::getRuntimeMainDependency(void)
6520{
6521 return "VuoRuntimeMain.o";
6522}
6523
6531string VuoCompiler::getRuntimeDependency(void)
6532{
6533 return "VuoRuntime.o";
6534}
6535
6543{
6544 if (! vuoFrameworkInProgressPath.empty())
6545 return vuoFrameworkInProgressPath;
6546
6548}
6549
6553string VuoCompiler::getClangPath(void)
6554{
6555 return clangPath;
6556}
6557
6562{
6563 // Start with the file name without extension.
6564 string dir, moduleKey, ext;
6565 VuoFileUtilities::splitPath(path, dir, moduleKey, ext);
6566
6568 {
6569 vector<string> nodeClassNameParts = VuoStringUtilities::split(moduleKey, '.');
6570
6571 // Remove repeated file extensions.
6572 while (nodeClassNameParts.size() > 1 && nodeClassNameParts.back() == ext)
6573 nodeClassNameParts.pop_back();
6574
6575 // Convert each part to lowerCamelCase.
6576 for (string &part : nodeClassNameParts)
6577 part = VuoStringUtilities::convertToCamelCase(part, false, true, false);
6578
6579 // If the node class name has only one part, add a prefix.
6580 if (nodeClassNameParts.size() == 1)
6581 nodeClassNameParts.insert(nodeClassNameParts.begin(), "isf");
6582
6583 moduleKey = VuoStringUtilities::join(nodeClassNameParts, '.');
6584 }
6585
6586 return moduleKey;
6587}
6588
6594{
6595 __block bool isLocal = false;
6596
6597 dispatch_sync(environmentQueue, ^{
6598 if (environments.size() >= 5)
6599 isLocal = environments.at(3).at(0)->findModule(moduleKey);
6600 });
6601
6602 return isLocal;
6603}
6604
6612{
6613 return lastCompositionBaseDir.empty() ? "" : lastCompositionBaseDir + "/Modules";
6614}
6615
6627{
6628 return lastCompositionBaseDir;
6629}
6630
6634void VuoCompiler::addModuleSearchPath(string path)
6635{
6636 dispatch_sync(environmentQueue, ^{
6637 environments.back().at(0)->addModuleSearchPath(path);
6638 environments.back().at(0)->addLibrarySearchPath(path);
6639 });
6640}
6641
6645void VuoCompiler::addHeaderSearchPath(const string &path)
6646{
6647 dispatch_sync(environmentQueue, ^{
6648 environments.back().at(0)->addHeaderSearchPath(path);
6649 });
6650}
6651
6656{
6657 dispatch_sync(environmentQueue, ^{
6658 environments.back().at(0)->addLibrarySearchPath(path);
6659 });
6660}
6661
6666{
6667 dispatch_sync(environmentQueue, ^{
6668 environments.back().at(0)->addFrameworkSearchPath(path);
6669 });
6670}
6671
6675void VuoCompiler::setVerbose(bool isVerbose)
6676{
6677 this->isVerbose = isVerbose;
6678}
6679
6685{
6686#if VUO_PRO
6687 if (VuoPro::getProAccess())
6688 {
6689 _shouldShowSplashWindow = false;
6690 return;
6691 }
6692#endif
6693
6694 _shouldShowSplashWindow = potentiallyShow;
6695}
6696
6702void VuoCompiler::setDependencyOutput(const string &path)
6703{
6704#if VUO_PRO
6705 _setDependencyOutput(path);
6706#endif
6707}
6708
6713{
6714 return _shouldShowSplashWindow;
6715}
6716
6720void VuoCompiler::setClangPath(const string &clangPath)
6721{
6722 this->clangPath = clangPath;
6723}
6724
6729{
6730 string vuoFrameworkPath = getVuoFrameworkPath();
6731 return (vuoFrameworkPath.empty() ?
6732 VUO_BUILD_DIR "/bin/VuoCompositionLoader.app/Contents/MacOS/VuoCompositionLoader" :
6733 vuoFrameworkPath + "/Helpers/VuoCompositionLoader.app/Contents/MacOS/VuoCompositionLoader");
6734}
6735
6739string VuoCompiler::getCompositionStubPath(void)
6740{
6741 string vuoFrameworkPath = getVuoFrameworkPath();
6742 return (vuoFrameworkPath.empty() ?
6743 VUO_BUILD_DIR "/lib/libVuoCompositionStub.dylib" :
6744 vuoFrameworkPath + "/Modules/libVuoCompositionStub.dylib");
6745}
6746
6750string VuoCompiler::getCachePathForComposition(const string compositionDir)
6751{
6752 string modifierLetterColon("꞉");
6753 string cachedModulesName = compositionDir;
6754 VuoStringUtilities::replaceAll(cachedModulesName, "/", modifierLetterColon);
6755 return VuoFileUtilities::getCachePath() + "/" + cachedModulesName;
6756}
6757
6762{
6763 __block vector<string> moduleSearchPaths;
6764 void (^envGetModuleSearchPaths)(Environment *) = ^void (Environment *env) {
6765 vector<string> result = env->getModuleSearchPaths();
6766 moduleSearchPaths.insert(moduleSearchPaths.end(), result.begin(), result.end());
6767 };
6768 applyToInstalledEnvironments(envGetModuleSearchPaths);
6769
6770 __block vector<string> headerSearchPaths;
6771 void (^envGetHeaderSearchPaths)(Environment *) = ^void (Environment *env) {
6772 vector<string> result = env->getHeaderSearchPaths();
6773 headerSearchPaths.insert(headerSearchPaths.end(), result.begin(), result.end());
6774 };
6775 applyToInstalledEnvironments(envGetHeaderSearchPaths);
6776
6777 __block vector<string> librarySearchPaths;
6778 void (^envGetLibrarySearchPaths)(Environment *) = ^void (Environment *env) {
6779 vector<string> result = env->getLibrarySearchPaths();
6780 librarySearchPaths.insert(librarySearchPaths.end(), result.begin(), result.end());
6781 };
6782 applyToInstalledEnvironments(envGetLibrarySearchPaths);
6783
6784 __block vector<string> frameworkSearchPaths;
6785 void (^envGetFrameworkSearchPaths)(Environment *) = ^void (Environment *env) {
6786 vector<string> result = env->getFrameworkSearchPaths();
6787 frameworkSearchPaths.insert(frameworkSearchPaths.end(), result.begin(), result.end());
6788 };
6789 applyToInstalledEnvironments(envGetFrameworkSearchPaths);
6790
6791 fprintf(stderr, "Module (node class, type, library) search paths:\n");
6792 for (vector<string>::iterator i = moduleSearchPaths.begin(); i != moduleSearchPaths.end(); ++i)
6793 fprintf(stderr, " %s\n", (*i).c_str());
6794 fprintf(stderr, "Header search paths:\n");
6795 for (vector<string>::iterator i = headerSearchPaths.begin(); i != headerSearchPaths.end(); ++i)
6796 fprintf(stderr, " %s\n", (*i).c_str());
6797 fprintf(stderr, "Other library search paths:\n");
6798 for (vector<string>::iterator i = librarySearchPaths.begin(); i != librarySearchPaths.end(); ++i)
6799 fprintf(stderr, " %s\n", (*i).c_str());
6800 fprintf(stderr, "Other framework search paths:\n");
6801 for (vector<string>::iterator i = frameworkSearchPaths.begin(); i != frameworkSearchPaths.end(); ++i)
6802 fprintf(stderr, " %s\n", (*i).c_str());
6803 fprintf(stderr, "Framework path:\n");
6804 if (! getVuoFrameworkPath().empty())
6805 fprintf(stderr, " %s\n", getVuoFrameworkPath().c_str());
6806 fprintf(stderr, "Clang path:\n");
6807 if (! getClangPath().empty())
6808 fprintf(stderr, " %s\n", getClangPath().c_str());
6809}
6810
6820{
6821 try
6822 {
6823 VuoCompiler compiler(compositionFilePath);
6824 string directory, file, extension;
6825 VuoFileUtilities::splitPath(compositionFilePath, directory, file, extension);
6826 string compiledCompositionPath = VuoFileUtilities::makeTmpFile(file, "bc");
6827 string linkedCompositionPath = VuoFileUtilities::makeTmpFile(file + "-linked", "");
6828 compiler.compileComposition(compositionFilePath, compiledCompositionPath, true, issues);
6829 compiler.linkCompositionToCreateExecutable(compiledCompositionPath, linkedCompositionPath, Optimization_FastBuild);
6830 remove(compiledCompositionPath.c_str());
6831 return VuoRunner::newSeparateProcessRunnerFromExecutable(linkedCompositionPath, directory, false, true);
6832 }
6833 catch (VuoCompilerException &e)
6834 {
6835 if (issues != e.getIssues())
6836 issues->append(e.getIssues());
6837 return NULL;
6838 }
6839}
6840
6855VuoRunner * VuoCompiler::newSeparateProcessRunnerFromCompositionString(string composition, string processName, string workingDirectory, VuoCompilerIssues *issues)
6856{
6857 try
6858 {
6859 string compositionPath = (workingDirectory.empty() ? "." : workingDirectory) + "/" + processName + ".vuo";
6860 VuoCompiler compiler(compositionPath);
6861 string compiledCompositionPath = VuoFileUtilities::makeTmpFile(processName, "bc");
6862 string linkedCompositionPath = VuoFileUtilities::makeTmpFile(processName, "");
6863 compiler.compileCompositionString(composition, compiledCompositionPath, true, issues);
6864 compiler.linkCompositionToCreateExecutable(compiledCompositionPath, linkedCompositionPath, Optimization_FastBuild);
6865 remove(compiledCompositionPath.c_str());
6866 return VuoRunner::newSeparateProcessRunnerFromExecutable(linkedCompositionPath, workingDirectory, false, true);
6867 }
6868 catch (VuoCompilerException &e)
6869 {
6870 if (issues != e.getIssues())
6871 issues->append(e.getIssues());
6872 return NULL;
6873 }
6874}
6875
6885{
6886 try
6887 {
6888 VuoCompiler compiler(compositionFilePath);
6889 string directory, file, extension;
6890 VuoFileUtilities::splitPath(compositionFilePath, directory, file, extension);
6891 string compiledCompositionPath = VuoFileUtilities::makeTmpFile(file, "bc");
6892 compiler.compileComposition(compositionFilePath, compiledCompositionPath, true, issues);
6893 string linkedCompositionPath = VuoFileUtilities::makeTmpFile(file, "dylib");
6894 compiler.linkCompositionToCreateDynamicLibrary(compiledCompositionPath, linkedCompositionPath, Optimization_FastBuild);
6895 remove(compiledCompositionPath.c_str());
6896 return VuoRunner::newCurrentProcessRunnerFromDynamicLibrary(linkedCompositionPath, directory, true);
6897 }
6898 catch (VuoCompilerException &e)
6899 {
6900 if (issues != e.getIssues())
6901 issues->append(e.getIssues());
6902 return NULL;
6903 }
6904}
6905
6921{
6922 try
6923 {
6924 string compositionPath = (workingDirectory.empty() ? "." : workingDirectory) + "/UntitledComposition.vuo";
6925 VuoCompiler compiler(compositionPath);
6926 string compiledCompositionPath = VuoFileUtilities::makeTmpFile("VuoRunnerComposition", "bc");
6927 compiler.compileCompositionString(composition, compiledCompositionPath, true, issues);
6928 string linkedCompositionPath = VuoFileUtilities::makeTmpFile("VuoRunnerComposition", "dylib");
6929 compiler.linkCompositionToCreateDynamicLibrary(compiledCompositionPath, linkedCompositionPath, Optimization_FastBuild);
6930 remove(compiledCompositionPath.c_str());
6931 return VuoRunner::newCurrentProcessRunnerFromDynamicLibrary(linkedCompositionPath, workingDirectory, true);
6932 }
6933 catch (VuoCompilerException &e)
6934 {
6935 if (issues != e.getIssues())
6936 issues->append(e.getIssues());
6937 return NULL;
6938 }
6939}
6940
6942{
6943 pendingDataQueue = dispatch_queue_create("org.vuo.compiler.delegate.pending", 0);
6944}
6945
6947{
6948 LoadedModulesData *data = dequeueData();
6949 data->release();
6950}
6951
6955void VuoCompilerDelegate::enqueueData(LoadedModulesData *data)
6956{
6957 dispatch_sync(pendingDataQueue, ^{
6958 pendingData.push_back(data);
6959 });
6960}
6961
6965VuoCompilerDelegate::LoadedModulesData * VuoCompilerDelegate::dequeueData(void)
6966{
6967 __block LoadedModulesData *ret;
6968 dispatch_sync(pendingDataQueue, ^{
6969 ret = pendingData.front();
6970 pendingData.pop_front();
6971 });
6972 return ret;
6973}
6974
6979VuoCompilerDelegate::LoadedModulesData::LoadedModulesData(const set< pair<VuoCompilerModule *, VuoCompilerModule *> > &modulesModified,
6980 const set<VuoCompilerModule *> &modulesRemoved, VuoCompilerIssues *issues)
6981{
6982 referenceCountQueue = dispatch_queue_create("org.vuo.compiler.delegate.reference", 0);
6983 referenceCount = 0;
6984
6985 this->modulesModified = modulesModified;
6986 this->modulesRemoved = modulesRemoved;
6987 this->issues = issues;
6988}
6989
6993VuoCompilerDelegate::LoadedModulesData::~LoadedModulesData(void)
6994{
6995 delete issues;
6996
6997 for (set< pair<VuoCompilerModule *, VuoCompilerModule *> >::iterator i = modulesModified.begin(); i != modulesModified.end(); ++i)
6998 VuoCompiler::destroyModule((*i).first);
6999
7000 for (set<VuoCompilerModule *>::iterator i = modulesRemoved.begin(); i != modulesRemoved.end(); ++i)
7002}
7003
7007void VuoCompilerDelegate::LoadedModulesData::retain(void)
7008{
7009 dispatch_sync(referenceCountQueue, ^{
7010 ++referenceCount;
7011 });
7012}
7013
7017void VuoCompilerDelegate::LoadedModulesData::release(void)
7018{
7019 dispatch_sync(referenceCountQueue, ^{
7020 --referenceCount;
7021 if (referenceCount == 0) {
7022 delete this;
7023 }
7024 });
7025}