Vuo  2.0.0
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"
20 #include "VuoCompilerException.hh"
22 #include "VuoCompilerGraph.hh"
24 #include "VuoCompilerIssue.hh"
25 #include "VuoCompilerNode.hh"
26 #include "VuoCompilerPort.hh"
27 #include "VuoCompilerPortClass.hh"
31 #include "VuoComposition.hh"
32 #include "VuoException.hh"
33 #include "VuoGenericType.hh"
34 #include "VuoModuleCompiler.hh"
36 #include "VuoNode.hh"
37 #include "VuoNodeClass.hh"
38 #include "VuoNodeSet.hh"
39 #include "VuoPublishedPort.hh"
40 #include "VuoRunner.hh"
42 #include "VuoStringUtilities.hh"
43 
44 #if VUO_PRO
45 #include "VuoPro.hh"
46 #endif
47 
48 map<string, VuoFileUtilities::File *> VuoCompiler::Environment::moduleCacheFileForLocking;
49 dispatch_queue_t VuoCompiler::Environment::moduleCacheBuildingQueue = dispatch_queue_create("org.vuo.compiler.cache", NULL);
50 const string VuoCompiler::Environment::pidCacheDirPrefix = "pid-";
51 dispatch_queue_t VuoCompiler::environmentQueue = dispatch_queue_create("org.vuo.compiler.environment", NULL);
52 vector< vector<VuoCompiler::Environment *> > VuoCompiler::sharedEnvironments;
53 map<string, vector<VuoCompiler::Environment *> > VuoCompiler::environmentsForCompositionFamily;
54 dispatch_group_t VuoCompiler::moduleSourceCompilersExist = dispatch_group_create();
55 string VuoCompiler::vuoFrameworkInProgressPath;
56 
57 dispatch_queue_t llvmQueue = NULL;
58 
62 static void __attribute__((constructor)) VuoCompiler_init(void)
63 {
64  // Increase the open-files limit (macOS defaults to 256).
65  struct rlimit rl{OPEN_MAX, OPEN_MAX};
66  getrlimit(RLIMIT_NOFILE, &rl);
67  rl.rlim_cur = MIN(OPEN_MAX, rl.rlim_max);
68  if (setrlimit(RLIMIT_NOFILE, &rl))
69  VUserLog("Warning: Couldn't set open-files limit: %s", strerror(errno));
70 
71 
72  llvmQueue = dispatch_queue_create("org.vuo.compiler.llvm", NULL);
73 
74  dispatch_sync(llvmQueue, ^{
75  llvm::InitializeNativeTarget();
76 
77  // If the Vuo compiler/linker...
78  // 1. Loads a node class that uses dispatch_object_t.
79  // 2. Generates code that uses dispatch_object_t.
80  // 3. Links the node class into a composition.
81  // 4. Generates more code that uses dispatch_object_t.
82  // ... then Step 4 ends up with the wrong llvm::Type for dispatch_object_t.
83  //
84  // A workaround is to generate some code that uses dispatch_object_t before doing Step 1.
85  //
86  // https://b33p.net/kosada/node/3845
87  // http://lists.cs.uiuc.edu/pipermail/llvmdev/2012-December/057075.html
88  Module module("", getGlobalContext());
90 
91  // Workaround for a possibly related error where the compiler ends up with the wrong
92  // llvm::Type for dispatch_semaphore_s. https://b33p.net/kosada/node/10545
94 
95  // Load the NodeContext and PortContext struct types preemptively to make sure that
96  // their fields get the right dispatch types. If these struct types were to be loaded
97  // first from a subcomposition module, they'd get the wrong dispatch types.
98  // https://b33p.net/kosada/node/11160
101  });
102 }
103 
107 VuoCompiler::ModuleInfo::ModuleInfo(Environment *environment, const string &searchPath, const string &relativePath,
108  bool isSourceFile, bool isSubcomposition)
109 {
110  VuoFileUtilities::File *file = new VuoFileUtilities::File(searchPath, relativePath);
111  initialize(environment, searchPath, file, isSourceFile, isSubcomposition);
112 }
113 
117 VuoCompiler::ModuleInfo::ModuleInfo(Environment *environment, const string &searchPath, VuoFileUtilities::File *file,
118  bool isSourceFile, bool isSubcomposition)
119 {
120  initialize(environment, searchPath, file, isSourceFile, isSubcomposition);
121 }
122 
126 void VuoCompiler::ModuleInfo::initialize(Environment *environment, const string &searchPath, VuoFileUtilities::File *file,
127  bool isSourceFile, bool isSubcomposition)
128 {
129  this->environment = environment;
130  this->searchPath = searchPath;
131  this->file = file;
132  moduleKey = getModuleKeyForPath(file->getRelativePath());
133  sourceCodeOverridden = false;
134  lastModified = VuoFileUtilities::getFileLastModifiedInSeconds(file->isInArchive() ? file->getArchivePath() : file->path());
135  attempted = false;
136  loading = nullptr;
137 
138 #if VUO_PRO
139  initialize_Pro();
140 #endif
141 
142  if (isSourceFile)
143  {
144  loading = dispatch_group_create();
145  sourceCode = file->getContentsAsString();
146 
147  if (isSubcomposition)
148  {
149  try
150  {
152  }
153  catch (...)
154  {
155  // Issues parsing the composition will be caught later when compiling it.
156  }
157  }
158  }
159 }
160 
161 VuoCompiler::ModuleInfo::~ModuleInfo(void)
162 {
163  delete file;
164 
165  if (loading)
166  dispatch_release(loading);
167 }
168 
169 VuoCompiler::Environment * VuoCompiler::ModuleInfo::getEnvironment(void) const
170 {
171  return environment;
172 }
173 
174 string VuoCompiler::ModuleInfo::getSearchPath(void) const
175 {
176  return searchPath;
177 }
178 
179 string VuoCompiler::ModuleInfo::getModuleKey(void) const
180 {
181  return moduleKey;
182 }
183 
184 VuoFileUtilities::File * VuoCompiler::ModuleInfo::getFile(void) const
185 {
186  return file;
187 }
188 
189 string VuoCompiler::ModuleInfo::getSourceCode(void) const
190 {
191  return sourceCode;
192 }
193 
194 void VuoCompiler::ModuleInfo::setSourceCode(const string &sourceCode)
195 {
196  this->sourceCode = sourceCode;
197 }
198 
199 void VuoCompiler::ModuleInfo::revertSourceCode(void)
200 {
201  sourceCode = file->getContentsAsString();
202 }
203 
204 bool VuoCompiler::ModuleInfo::isSourceCodeOverridden(void) const
205 {
206  return sourceCodeOverridden;
207 }
208 
209 void VuoCompiler::ModuleInfo::setSourceCodeOverridden(bool overridden)
210 {
211  sourceCodeOverridden = overridden;
212 }
213 
214 bool VuoCompiler::ModuleInfo::isNewerThan(ModuleInfo *other) const
215 {
216  return lastModified > other->lastModified;
217 }
218 
219 bool VuoCompiler::ModuleInfo::isNewerThan(unsigned long ageInSeconds) const
220 {
221  return lastModified > ageInSeconds;
222 }
223 
224 bool VuoCompiler::ModuleInfo::isOlderThan(unsigned long ageInSeconds) const
225 {
226  return lastModified < ageInSeconds;
227 }
228 
229 void VuoCompiler::ModuleInfo::setLastModifiedToNow(void)
230 {
231  struct timeval t;
232  gettimeofday(&t, NULL);
233  lastModified = t.tv_sec;
234 }
235 
236 set<string> VuoCompiler::ModuleInfo::getContainedNodeClasses(void) const
237 {
238  return containedNodeClasses;
239 }
240 
241 bool VuoCompiler::ModuleInfo::getAttempted(void) const
242 {
243  return attempted;
244 }
245 
246 void VuoCompiler::ModuleInfo::setAttempted(bool attempted)
247 {
248  this->attempted = attempted;
249 }
250 
251 dispatch_group_t VuoCompiler::ModuleInfo::getLoadingGroup(void) const
252 {
253  return loading;
254 }
255 
256 void VuoCompiler::ModuleInfo::dump() const
257 {
258  fprintf(stderr, "module %s:\n"
259  "\tloadingGroup=%p\n"
260  "\tsearchPath=%s\n"
261  "\tattempted=%d\n"
262  "\tenvironment=%s\n"
263  "\tfile=%s%s%s\n"
264  "\tsourceCodeOverridden=%d\n"
265  "\tsourceCode=%s\n"
266  "\tcontainedNodeClasses:\n",
267  moduleKey.c_str(),
268  loading,
269  searchPath.c_str(),
270  attempted,
271  environment->getCompiledModuleCachePath().c_str(),
272  file->isInArchive() ? file->getArchivePath().c_str() : "",
273  file->isInArchive() ? "::" : "",
274  file->isInArchive() ? file->getRelativePath().c_str() : file->path().c_str(),
275  sourceCodeOverridden,
276  sourceCode.c_str());
277  for (auto i: containedNodeClasses)
278  fprintf(stderr, "\t\t%s\n", i.c_str());
279 }
280 
281 VuoCompiler::DependencyGraphVertex * VuoCompiler::DependencyGraphVertex::vertexForDependency(const string &dependency, VuoDirectedAcyclicGraph *graph)
282 {
283  VuoDirectedAcyclicGraph::Vertex *v = graph->findVertex(dependency);
284  if (v)
285  return dynamic_cast<DependencyGraphVertex *>(v);
286 
287  DependencyGraphVertex *vv = new DependencyGraphVertex();
288  vv->dependency = dependency;
289  vv->environment = NULL;
290  vv->compatible = true;
291  return vv;
292 }
293 
294 string VuoCompiler::DependencyGraphVertex::getDependency(void)
295 {
296  return dependency;
297 }
298 
299 VuoCompiler::Environment * VuoCompiler::DependencyGraphVertex::getEnvironment(void)
300 {
301  return environment;
302 }
303 
304 void VuoCompiler::DependencyGraphVertex::setEnvironment(Environment *environment)
305 {
306  this->environment = environment;
307 }
308 
309 bool VuoCompiler::DependencyGraphVertex::isCompatible(void)
310 {
311  return compatible;
312 }
313 
314 void VuoCompiler::DependencyGraphVertex::setCompatible(bool compatible)
315 {
316  this->compatible = compatible;
317 }
318 
319 string VuoCompiler::DependencyGraphVertex::key(void)
320 {
321  return dependency;
322 }
323 
329 VuoCompiler::ModuleInfoIterator::ModuleInfoIterator(map<string, map<string, ModuleInfo *> > *allModuleInfos)
330 {
331  this->allModuleInfos = allModuleInfos;
332  hasSearchModuleKeys = false;
333  initialize();
334 }
335 
341 VuoCompiler::ModuleInfoIterator::ModuleInfoIterator(map<string, map<string, ModuleInfo *> > *allModuleInfos, const set<string> &searchModuleKeys)
342 {
343  this->allModuleInfos = allModuleInfos;
344  this->searchModuleKeys = searchModuleKeys;
345  hasSearchModuleKeys = true;
346  initialize();
347 }
348 
354 void VuoCompiler::ModuleInfoIterator::initialize(void)
355 {
356  currSearchPath = allModuleInfos->begin();
357  hasCurrModuleKey = false;
358 }
359 
365 VuoCompiler::ModuleInfo * VuoCompiler::ModuleInfoIterator::next(void)
366 {
367  for ( ; currSearchPath != allModuleInfos->end(); ++currSearchPath)
368  {
369  if (! hasCurrModuleKey)
370  {
371  currModuleKey = currSearchPath->second.begin();
372  hasCurrModuleKey = true;
373  }
374 
375  for ( ; currModuleKey != currSearchPath->second.end(); ++currModuleKey)
376  {
377  if (! hasSearchModuleKeys || searchModuleKeys.find(currModuleKey->first) != searchModuleKeys.end())
378  {
379  ModuleInfo *moduleInfo = currModuleKey->second;
380  ++currModuleKey;
381  return moduleInfo;
382  }
383  }
384 
385  hasCurrModuleKey = false;
386  }
387 
388  return NULL;
389 }
390 
394 VuoCompiler::Environment::Environment(void)
395 {
396  compilersToNotifyQueue = dispatch_queue_create("org.vuo.compiler.notify", 0);
397  moduleSearchPathContentsChangedQueue = dispatch_queue_create("org.vuo.compiler.watch", 0);
398  isModuleCacheableDataDirty = false;
399  isModuleCacheInitialized = false;
400  isModuleCacheAvailable = false;
401  dependencyGraph = new VuoDirectedAcyclicGraph();
402  compositionDependencyGraph = new VuoDirectedAcyclicGraph();
403  moduleCompilationQueue = new VuoModuleCompilationQueue();
404 }
405 
409 VuoCompiler::Environment::~Environment(void)
410 {
411  stopWatchingModuleSearchPaths();
412  dispatch_sync(moduleSearchPathContentsChangedQueue, ^{});
413 
414  dispatch_release(moduleSearchPathContentsChangedQueue);
415  dispatch_release(compilersToNotifyQueue);
416 
417  for (map<string, VuoCompilerNodeClass *>::iterator i = nodeClasses.begin(); i != nodeClasses.end(); ++i)
418  destroyModule(i->second);
419 
420  for (map<string, VuoCompilerType *>::iterator i = types.begin(); i != types.end(); ++i)
421  destroyModule(i->second);
422 
423  for (map<string, VuoCompilerModule *>::iterator i = libraryModules.begin(); i != libraryModules.end(); ++i)
424  destroyModule(i->second);
425 
426  for (set<VuoCompilerGenericType *>::iterator i = genericTypes.begin(); i != genericTypes.end(); ++i)
427  {
428  VuoType *base = (*i)->getBase();
429  delete *i;
430  delete base;
431  }
432 
433  for (map<string, VuoNodeSet *>::iterator i = nodeSetForName.begin(); i != nodeSetForName.end(); ++i)
434  delete i->second;
435 
436  for (map<string, map<string, ModuleInfo *> >::iterator i = moduleFilesAtSearchPath.begin(); i != moduleFilesAtSearchPath.end(); ++i)
437  for (map<string, ModuleInfo *>::iterator j = i->second.begin(); j != i->second.end(); ++j)
438  delete j->second;
439 
440  for (map<string, map<string, ModuleInfo *> >::iterator i = sourceFilesAtSearchPath.begin(); i != sourceFilesAtSearchPath.end(); ++i)
441  for (map<string, ModuleInfo *>::iterator j = i->second.begin(); j != i->second.end(); ++j)
442  delete j->second;
443 
444  delete dependencyGraph;
445  delete compositionDependencyGraph;
446 }
447 
451 void VuoCompiler::Environment::addCompilerToNotify(VuoCompiler *compiler)
452 {
453  dispatch_sync(compilersToNotifyQueue, ^{
454  compilersToNotify.insert(compiler);
455  });
456 }
457 
461 void VuoCompiler::Environment::removeCompilerToNotify(VuoCompiler *compiler)
462 {
463  dispatch_sync(compilersToNotifyQueue, ^{
464  compilersToNotify.erase(compiler);
465  });
466 }
467 
473 map<string, VuoCompilerNodeClass *> VuoCompiler::Environment::getNodeClasses(void)
474 {
475  return nodeClasses;
476 }
477 
483 VuoCompilerNodeClass * VuoCompiler::Environment::getNodeClass(const string &moduleKey)
484 {
485  map<string, VuoCompilerNodeClass *>::iterator nodeClassIter = nodeClasses.find(moduleKey);
486  if (nodeClassIter != nodeClasses.end())
487  return nodeClassIter->second;
488 
489  return NULL;
490 }
491 
496 VuoCompilerNodeClass * VuoCompiler::Environment::takeNodeClass(const string &moduleKey)
497 {
498  VuoCompilerNodeClass *nodeClass = NULL;
499 
500  map<string, VuoCompilerNodeClass *>::iterator nodeClassIter = nodeClasses.find(moduleKey);
501  if (nodeClassIter != nodeClasses.end())
502  {
503  nodeClass = nodeClassIter->second;
504 
505  nodeClasses.erase(nodeClassIter);
506  removeFromDependencyGraph(nodeClass);
507  modulesChanged();
508  }
509 
510  return nodeClass;
511 }
512 
518 map<string, VuoCompilerType *> VuoCompiler::Environment::getTypes(void)
519 {
520  return types;
521 }
522 
528 VuoCompilerType * VuoCompiler::Environment::getType(const string &moduleKey)
529 {
530  map<string, VuoCompilerType *>::iterator typeIter = types.find(moduleKey);
531  if (typeIter != types.end())
532  return typeIter->second;
533 
534  return NULL;
535 }
536 
542 map<string, VuoNodeSet *> VuoCompiler::Environment::getNodeSets(void)
543 {
544  return nodeSetForName;
545 }
546 
552 map<string, VuoCompilerModule *> VuoCompiler::Environment::getLibraryModules(void)
553 {
554  return libraryModules;
555 }
556 
563 VuoCompilerModule * VuoCompiler::Environment::findModule(const string &moduleKey)
564 {
565  map<string, VuoCompilerNodeClass *>::iterator nodeClassIter = nodeClasses.find(moduleKey);
566  if (nodeClassIter != nodeClasses.end())
567  return nodeClassIter->second;
568 
569  map<string, VuoCompilerType *>::iterator typeIter = types.find(moduleKey);
570  if (typeIter != types.end())
571  return typeIter->second;
572 
573  map<string, VuoCompilerModule *>::iterator libraryIter = libraryModules.find(moduleKey);
574  if (libraryIter != libraryModules.end())
575  return libraryIter->second;
576 
577  return NULL;
578 }
579 
585 VuoNodeSet * VuoCompiler::Environment::findNodeSet(const string &name)
586 {
587 
588  map<string, VuoNodeSet *>::iterator nodeSetIter = nodeSetForName.find(name);
589  if (nodeSetIter != nodeSetForName.end())
590  return nodeSetIter->second;
591 
592  return NULL;
593 }
594 
601 void VuoCompiler::Environment::addModuleSearchPath(const string &path, bool shouldWatch)
602 {
603  moduleSearchPaths.push_back(path);
604 
605  updateModulesAtSearchPath(path);
606  updateSourceFilesAtSearchPath(path);
607 
608  if (shouldWatch)
609  startWatchingModuleSearchPath(path);
610 }
611 
617 vector<string> VuoCompiler::Environment::getModuleSearchPaths(void)
618 {
619  return moduleSearchPaths;
620 }
621 
627 void VuoCompiler::Environment::addHeaderSearchPath(const string &path)
628 {
629  headerSearchPaths.push_back(path);
630 }
631 
637 vector<string> VuoCompiler::Environment::getHeaderSearchPaths(void)
638 {
639  return headerSearchPaths;
640 }
641 
647 void VuoCompiler::Environment::addLibrarySearchPath(const string &path)
648 {
649  librarySearchPaths.push_back(path);
650 }
651 
657 vector<string> VuoCompiler::Environment::getLibrarySearchPaths(void)
658 {
659  return librarySearchPaths;
660 }
661 
667 void VuoCompiler::Environment::addFrameworkSearchPath(const string &path)
668 {
669  frameworkSearchPaths.push_back(path);
670 }
671 
677 vector<string> VuoCompiler::Environment::getFrameworkSearchPaths(void)
678 {
679  return frameworkSearchPaths;
680 }
681 
682 VuoDirectedAcyclicGraph * VuoCompiler::Environment::getDependencyGraph(void)
683 {
684  return dependencyGraph;
685 }
686 
687 VuoDirectedAcyclicGraph * VuoCompiler::Environment::getCompositionDependencyGraph(void)
688 {
689  return compositionDependencyGraph;
690 }
691 
697 void VuoCompiler::Environment::setModuleCachePath(const string &path)
698 {
699  moduleCachePath = path;
700 }
701 
707 string VuoCompiler::Environment::getCompiledModuleCachePath(void)
708 {
709  return (moduleCachePath.empty() ? "" : moduleCachePath + "/Modules");
710 }
711 
717 string VuoCompiler::Environment::getOverriddenCompiledModuleCachePath(void)
718 {
719  if (moduleCachePath.empty())
720  return "";
721 
722  ostringstream pid;
723  pid << getpid();
724 
725  string dir, moduleCacheDirName, ext;
726  VuoFileUtilities::splitPath(moduleCachePath, dir, moduleCacheDirName, ext);
727 
728  return (VuoFileUtilities::getCachePath() + "/" + pidCacheDirPrefix + pid.str() + "/" + moduleCacheDirName + "/Modules");
729 }
730 
738 void VuoCompiler::Environment::addExpatriateSourceFile(const string &sourcePath)
739 {
740  expatriateSourceFiles.push_back(sourcePath);
741 
742  if (find(moduleSearchPaths.begin(), moduleSearchPaths.end(), "") == moduleSearchPaths.end())
743  moduleSearchPaths.push_back("");
744 
745  auto iter = sourceFilesAtSearchPath.find("");
746  if (iter != sourceFilesAtSearchPath.end())
747  sourceFilesAtSearchPath.erase(iter);
748 }
749 
755 void VuoCompiler::Environment::removeExpatriateSourceFile(const string &sourcePath)
756 {
757  for (auto i = expatriateSourceFiles.begin(); i != expatriateSourceFiles.end(); ++i)
758  {
759  if (VuoFileUtilities::arePathsEqual(*i, sourcePath))
760  {
761  expatriateSourceFiles.erase(i);
762 
763  auto iter = sourceFilesAtSearchPath.find("");
764  if (iter != sourceFilesAtSearchPath.end())
765  sourceFilesAtSearchPath.erase(iter);
766 
767  break;
768  }
769  }
770 }
771 
784 void VuoCompiler::Environment::updateModulesAtSearchPath(const string &path, bool shouldCleanModuleCache)
785 {
786  if (moduleFilesAtSearchPath.find(path) != moduleFilesAtSearchPath.end())
787  return;
788 
789  set<string> moduleExtensions;
790  moduleExtensions.insert("vuonode");
791  moduleExtensions.insert("vuonode+");
792  moduleExtensions.insert("bc");
793  moduleExtensions.insert("bc+");
794  set<string> archiveExtensions;
795  archiveExtensions.insert("vuonode");
796  set<VuoFileUtilities::File *> moduleFiles = VuoFileUtilities::findFilesInDirectory(path, moduleExtensions, archiveExtensions);
797 
798  map<string, ModuleInfo *> fileForModuleKey;
799  for (set<VuoFileUtilities::File *>::iterator i = moduleFiles.begin(); i != moduleFiles.end(); ++i)
800  {
801  VuoFileUtilities::File *moduleFile = *i;
802 
803  ModuleInfo *m = new ModuleInfo(this, path, moduleFile, false, false);
804  fileForModuleKey[m->getModuleKey()] = m;
805  }
806 
807  if (shouldCleanModuleCache && path == getCompiledModuleCachePath())
808  {
809  for (map<string, ModuleInfo *>::iterator i = fileForModuleKey.begin(); i != fileForModuleKey.end(); )
810  {
811  ModuleInfo *sourceInfo = listSourceFile(i->first);
812  if (! sourceInfo || sourceInfo->isNewerThan(i->second))
813  {
814  ModuleInfo *m = i->second;
815  VuoFileUtilities::deleteFile(m->getFile()->path());
816  delete m;
817  fileForModuleKey.erase(i++);
818  }
819  else
820  ++i;
821  }
822  }
823 
824  moduleFilesAtSearchPath[path] = fileForModuleKey;
825 }
826 
834 void VuoCompiler::Environment::updateSourceFilesAtSearchPath(const string &path)
835 {
836  map<string, map<string, ModuleInfo *> >::iterator sourceFilesIter = sourceFilesAtSearchPath.find(path);
837  if (sourceFilesIter != sourceFilesAtSearchPath.end())
838  return;
839 
840  set<VuoFileUtilities::File *> sourceFiles;
841  if (! path.empty())
842  {
843  set<string> sourceExtensions;
844  sourceExtensions.insert("vuo");
845  sourceExtensions.insert("fs");
847  sourceExtensions.insert(cext.begin(), cext.end());
848  sourceFiles = VuoFileUtilities::findFilesInDirectory(path, sourceExtensions, set<string>());
849  }
850  else
851  {
852  for (const string &sourcePath : expatriateSourceFiles)
853  {
854  string dir, file, ext;
855  VuoFileUtilities::splitPath(sourcePath, dir, file, ext);
856  VuoFileUtilities::File *sourceFile = new VuoFileUtilities::File(dir, file + "." + ext);
857  sourceFiles.insert(sourceFile);
858  }
859  }
860 
861  map<string, ModuleInfo *> fileForModuleKey;
862  for (set<VuoFileUtilities::File *>::iterator i = sourceFiles.begin(); i != sourceFiles.end(); ++i)
863  {
864  VuoFileUtilities::File *sourceFile = *i;
865 
866  string dir, moduleKey, ext;
867  VuoFileUtilities::splitPath(sourceFile->getRelativePath(), dir, moduleKey, ext);
868  bool isSubcomposition = (ext == "vuo");
869 
870  // Ignore missing expatriateSourceFiles — they might have been deleted in the meantime.
871  if (path.empty() && !sourceFile->exists())
872  continue;
873 
874  ModuleInfo *m = new ModuleInfo(this, path, sourceFile, true, isSubcomposition);
875  if (fileForModuleKey.find(m->getModuleKey()) != fileForModuleKey.end())
876  VUserLog("Warning: Conflicting source files for module %s are installed at %s", m->getModuleKey().c_str(), path.c_str());
877  fileForModuleKey[m->getModuleKey()] = m;
878 
879  if (isSubcomposition)
880  {
881  DependencyGraphVertex *compositionVertex = DependencyGraphVertex::vertexForDependency(moduleKey, compositionDependencyGraph);
882  compositionDependencyGraph->addVertex(compositionVertex);
883 
884  compositionVertex->setEnvironment(this);
885 
886  set<string> dependencies = m->getContainedNodeClasses();
887  for (set<string>::iterator j = dependencies.begin(); j != dependencies.end(); ++j)
888  {
889  DependencyGraphVertex *dependencyVertex = DependencyGraphVertex::vertexForDependency(*j, compositionDependencyGraph);
890  compositionDependencyGraph->addEdge(compositionVertex, dependencyVertex);
891  }
892  }
893  }
894 
895  sourceFilesAtSearchPath[path] = fileForModuleKey;
896 }
897 
903 VuoCompiler::ModuleInfo * VuoCompiler::Environment::listModule(const string &moduleKey)
904 {
905  for (const auto &moduleFiles : moduleFilesAtSearchPath)
906  {
907  map<string, ModuleInfo *>::const_iterator foundIter = moduleFiles.second.find(moduleKey);
908  if (foundIter != moduleFiles.second.end())
909  return foundIter->second;
910  }
911 
912  return NULL;
913 }
914 
920 VuoCompiler::ModuleInfoIterator VuoCompiler::Environment::listModules(const set<string> &moduleKeys)
921 {
922  for (const string &path : moduleSearchPaths)
923  updateModulesAtSearchPath(path);
924 
925  return ModuleInfoIterator(&moduleFilesAtSearchPath, moduleKeys);
926 }
927 
933 VuoCompiler::ModuleInfoIterator VuoCompiler::Environment::listAllModules(void)
934 {
935  for (const string &path : moduleSearchPaths)
936  updateModulesAtSearchPath(path);
937 
938  return ModuleInfoIterator(&moduleFilesAtSearchPath);
939 }
940 
946 VuoCompiler::ModuleInfo * VuoCompiler::Environment::listSourceFile(const string &moduleKey)
947 {
948  for (const auto &sourceFiles : sourceFilesAtSearchPath)
949  {
950  map<string, ModuleInfo *>::const_iterator foundIter = sourceFiles.second.find(moduleKey);
951  if (foundIter != sourceFiles.second.end())
952  return foundIter->second;
953  }
954 
955  return NULL;
956 }
957 
963 VuoCompiler::ModuleInfoIterator VuoCompiler::Environment::listSourceFiles(const set<string> &moduleKeys)
964 {
965  for (const string &path : moduleSearchPaths)
966  updateSourceFilesAtSearchPath(path);
967 
968  return ModuleInfoIterator(&sourceFilesAtSearchPath, moduleKeys);
969 }
970 
976 VuoCompiler::ModuleInfoIterator VuoCompiler::Environment::listAllSourceFiles(void)
977 {
978  for (const string &path : moduleSearchPaths)
979  updateSourceFilesAtSearchPath(path);
980 
981  return ModuleInfoIterator(&sourceFilesAtSearchPath);
982 }
983 
987 vector<string> VuoCompiler::Environment::getBuiltInModuleSearchPaths(void)
988 {
989  vector<string> builtInModuleSearchPaths;
990 
991  string vuoFrameworkPath = getVuoFrameworkPath();
992  if (! vuoFrameworkPath.empty())
993  {
994  builtInModuleSearchPaths.push_back(vuoFrameworkPath + "/Modules");
995  }
996  else
997  {
998  builtInModuleSearchPaths.push_back(VUO_BUILD_DIR "/library");
999  builtInModuleSearchPaths.push_back(VUO_BUILD_DIR "/node");
1000  builtInModuleSearchPaths.push_back(VUO_BUILD_DIR "/type");
1001  builtInModuleSearchPaths.push_back(VUO_BUILD_DIR "/type/list");
1002  }
1003 
1004  return builtInModuleSearchPaths;
1005 }
1006 
1010 vector<string> VuoCompiler::Environment::getBuiltInHeaderSearchPaths(void)
1011 {
1012  vector<string> builtInHeaderSearchPaths;
1013 
1014  string vuoFrameworkPath = getVuoFrameworkPath();
1015  if (! vuoFrameworkPath.empty())
1016  {
1017  builtInHeaderSearchPaths.push_back(vuoFrameworkPath + "/Headers");
1018  builtInHeaderSearchPaths.push_back(vuoFrameworkPath + "/Headers/macos"); // system headers installed by Xcode Command Line Tools
1019  builtInHeaderSearchPaths.push_back(vuoFrameworkPath + "/Frameworks/llvm.framework/Versions/A/Headers/lib/c++/v1");
1020  }
1021  else
1022  {
1023  builtInHeaderSearchPaths.push_back(VUO_SOURCE_DIR "/library");
1024  builtInHeaderSearchPaths.push_back(VUO_SOURCE_DIR "/node");
1025  builtInHeaderSearchPaths.push_back(VUO_SOURCE_DIR "/type");
1026  builtInHeaderSearchPaths.push_back(VUO_SOURCE_DIR "/type/list");
1027  builtInHeaderSearchPaths.push_back(VUO_SOURCE_DIR "/runtime");
1028  builtInHeaderSearchPaths.push_back(JSONC_ROOT "/include");
1029  builtInHeaderSearchPaths.push_back(LLVM_ROOT "/include/c++/v1");
1030  }
1031 
1032  return builtInHeaderSearchPaths;
1033 }
1034 
1038 vector<string> VuoCompiler::Environment::getBuiltInLibrarySearchPaths(void)
1039 {
1040  vector<string> builtInLibrarySearchPaths;
1041 
1042  string vuoFrameworkPath = getVuoFrameworkPath();
1043  if (! vuoFrameworkPath.empty())
1044  {
1045  builtInLibrarySearchPaths.push_back(vuoFrameworkPath + "/Modules");
1046 
1047  // Ensure we (statically) link to our OpenSSL build when generating Vuo.framework's built-in module cache.
1048  builtInLibrarySearchPaths.push_back(OPENSSL_ROOT "/lib");
1049  }
1050  else
1051  {
1052  builtInLibrarySearchPaths.push_back(VUO_BUILD_DIR "/library");
1053  builtInLibrarySearchPaths.push_back(VUO_BUILD_DIR "/node");
1054  builtInLibrarySearchPaths.push_back(VUO_BUILD_DIR "/type");
1055  builtInLibrarySearchPaths.push_back(VUO_BUILD_DIR "/type/list");
1056  builtInLibrarySearchPaths.push_back(VUO_BUILD_DIR "/runtime");
1057 
1058  builtInLibrarySearchPaths.push_back(LEAP_ROOT);
1059 
1060  vector<string> conanLibDirs = VuoStringUtilities::split(CONAN_LIBRARY_PATHS, ';');
1061  builtInLibrarySearchPaths.insert(builtInLibrarySearchPaths.end(), conanLibDirs.begin(), conanLibDirs.end());
1062  }
1063 
1064  return builtInLibrarySearchPaths;
1065 }
1066 
1070 vector<string> VuoCompiler::Environment::getBuiltInFrameworkSearchPaths(void)
1071 {
1072  vector<string> builtInFrameworkSearchPaths;
1073 
1074  string vuoFrameworkPath = getVuoFrameworkPath();
1075  if (! vuoFrameworkPath.empty())
1076  {
1077  builtInFrameworkSearchPaths.push_back(vuoFrameworkPath + "/Modules/");
1078  builtInFrameworkSearchPaths.push_back(vuoFrameworkPath + "/Frameworks/");
1079  }
1080  else
1081  {
1082  builtInFrameworkSearchPaths.push_back(SYPHON_ROOT);
1083  }
1084 
1085  return builtInFrameworkSearchPaths;
1086 }
1087 
1094 void VuoCompiler::Environment::startWatchingModuleSearchPath(const string &moduleSearchPath)
1095 {
1096  VuoFileWatcher *watcher = new VuoFileWatcher(this, moduleSearchPath);
1097  moduleSearchPathWatchers.insert(watcher);
1098 }
1099 
1105 void VuoCompiler::Environment::stopWatchingModuleSearchPaths(void)
1106 {
1107  for (set<VuoFileWatcher *>::iterator i = moduleSearchPathWatchers.begin(); i != moduleSearchPathWatchers.end(); ++i)
1108  delete *i;
1109 
1110  moduleSearchPathWatchers.clear();
1111 }
1112 
1117 void VuoCompiler::Environment::fileChanged(const string &moduleSearchPath)
1118 {
1119  dispatch_sync(moduleSearchPathContentsChangedQueue, ^{
1120  moduleSearchPathContentsChanged(moduleSearchPath);
1121  });
1122 }
1123 
1131 void VuoCompiler::Environment::moduleSearchPathContentsChanged(const string &moduleSearchPath, const string &moduleAddedOrModifiedPath,
1132  const string &moduleAddedOrModifiedSourceCode,
1133  std::function<void(void)> moduleLoadedCallback,
1134  VuoCompiler *compiler, VuoCompilerIssues *issues)
1135 {
1136  //VLog(" E=%p -- %s", this, moduleSearchPath.c_str());
1137  VuoCompiler *compilerForLoading = (compiler ? compiler : new VuoCompiler(moduleSearchPath + "/unused"));
1138 
1139  __block set<string> modulesAdded;
1140  __block set<string> modulesModifed;
1141  __block set<string> modulesRemoved;
1142  __block set<string> compositionsAdded;
1143  __block set<string> compositionsModifed;
1144  __block set<string> compositionsRemoved;
1145 
1146  dispatch_sync(environmentQueue, ^{
1147 
1148  // Remove the old file records from the environment.
1149 
1150  map<string, ModuleInfo *> oldModules;
1151  map<string, ModuleInfo *> oldCompositions;
1152 
1153  map<string, map<string, ModuleInfo *> >::iterator mf = moduleFilesAtSearchPath.find(moduleSearchPath);
1154  if (mf != moduleFilesAtSearchPath.end())
1155  {
1156  for (map<string, ModuleInfo *>::iterator j = mf->second.begin(); j != mf->second.end(); ++j)
1157  {
1158  oldModules[j->first] = j->second;
1159  }
1160 
1161  moduleFilesAtSearchPath.erase(mf);
1162  }
1163 
1164  map<string, map<string, ModuleInfo *> >::iterator cf = sourceFilesAtSearchPath.find(moduleSearchPath);
1165  if (cf != sourceFilesAtSearchPath.end())
1166  {
1167  for (map<string, ModuleInfo *>::iterator j = cf->second.begin(); j != cf->second.end(); ++j)
1168  {
1169  oldCompositions[j->first] = j->second;
1170 
1171  VuoDirectedAcyclicGraph::Vertex *vertex = compositionDependencyGraph->findVertex(j->first);
1172  compositionDependencyGraph->removeVertex(vertex);
1173  }
1174 
1175  sourceFilesAtSearchPath.erase(cf);
1176  }
1177 
1178  // Compare the old and new file records to see what has changed.
1179 
1180  updateModulesAtSearchPath(moduleSearchPath, moduleAddedOrModifiedPath.empty());
1181  updateSourceFilesAtSearchPath(moduleSearchPath);
1182 
1183  mf = moduleFilesAtSearchPath.find(moduleSearchPath);
1184  if (mf != moduleFilesAtSearchPath.end())
1185  {
1186  for (map<string, ModuleInfo *>::iterator n = mf->second.begin(); n != mf->second.end(); ++n)
1187  {
1188  string moduleKey = n->first;
1189 
1190  map<string, ModuleInfo *>::iterator o = oldModules.find(moduleKey);
1191  if (o != oldModules.end())
1192  {
1193  if (n->second->isNewerThan(o->second) ||
1194  (n->second->getFile() && (n->second->getFile()->isInArchive() || VuoFileUtilities::arePathsEqual(n->second->getFile()->path(), moduleAddedOrModifiedPath))))
1195  {
1196  modulesModifed.insert(moduleKey);
1197  }
1198  else
1199  {
1200  n->second->setAttempted(o->second->getAttempted());
1201  }
1202 
1203  delete o->second;
1204  oldModules.erase(o);
1205  }
1206  else if (VuoFileUtilities::arePathsEqual(moduleSearchPath, getOverriddenCompiledModuleCachePath()))
1207  {
1208  modulesModifed.insert(moduleKey);
1209  }
1210  else
1211  {
1212  modulesAdded.insert(moduleKey);
1213  }
1214  }
1215  }
1216 
1217  cf = sourceFilesAtSearchPath.find(moduleSearchPath);
1218  if (cf != sourceFilesAtSearchPath.end())
1219  {
1220  for (map<string, ModuleInfo *>::iterator n = cf->second.begin(); n != cf->second.end(); ++n)
1221  {
1222  string moduleKey = n->first;
1223 
1224  map<string, ModuleInfo *>::iterator o = oldCompositions.find(moduleKey);
1225  if (o != oldCompositions.end())
1226  {
1227  if (n->second->isNewerThan(o->second))
1228  {
1229  compositionsModifed.insert(moduleKey);
1230  }
1231  else
1232  {
1233  n->second->setAttempted(o->second->getAttempted());
1234  n->second->setSourceCode(o->second->getSourceCode());
1235  }
1236 
1237  delete o->second;
1238  oldCompositions.erase(o);
1239  }
1240  else
1241  {
1242  compositionsAdded.insert(moduleKey);
1243  }
1244  }
1245  }
1246 
1247  for (map<string, ModuleInfo *>::iterator o = oldModules.begin(); o != oldModules.end(); ++o)
1248  {
1249  delete o->second;
1250  modulesRemoved.insert(o->first);
1251  }
1252 
1253  for (map<string, ModuleInfo *>::iterator o = oldCompositions.begin(); o != oldCompositions.end(); ++o)
1254  {
1255  delete o->second;
1256  compositionsRemoved.insert(o->first);
1257  }
1258 
1259  compilerForLoading->loadModulesAndSources(modulesAdded, modulesModifed, modulesRemoved,
1260  compositionsAdded, compositionsModifed, compositionsRemoved,
1261  false, false, this, issues, moduleLoadedCallback, moduleAddedOrModifiedSourceCode);
1262  });
1263 
1264  if (! compiler)
1265  delete compilerForLoading;
1266 }
1267 
1271 void VuoCompiler::Environment::notifyCompilers(const set<VuoCompilerModule *> &modulesAdded,
1272  const set< pair<VuoCompilerModule *, VuoCompilerModule *> > &modulesModified,
1273  const set<VuoCompilerModule *> &modulesRemoved, VuoCompilerIssues *issues,
1274  bool oldModulesInvalidated)
1275 {
1276  if (! (modulesAdded.empty() && modulesModified.empty() && modulesRemoved.empty() && issues->isEmpty()) )
1277  {
1278  set< pair<VuoCompilerModule *, VuoCompilerModule *> > invalidatedModulesModified;
1279  set<VuoCompilerModule *> invalidatedModulesRemoved;
1280  if (oldModulesInvalidated)
1281  {
1282  invalidatedModulesModified = modulesModified;
1283  invalidatedModulesRemoved = modulesRemoved;
1284  }
1285 
1286  VuoCompilerDelegate::LoadedModulesData *delegateData = new VuoCompilerDelegate::LoadedModulesData(invalidatedModulesModified, invalidatedModulesRemoved, issues);
1287 
1288  map<string, VuoCompilerModule *> modulesAddedMap;
1289  map<string, pair<VuoCompilerModule *, VuoCompilerModule *> > modulesModifiedMap;
1290  map<string, VuoCompilerModule *> modulesRemovedMap;
1291  for (VuoCompilerModule *m : modulesAdded)
1292  modulesAddedMap[m->getPseudoBase()->getModuleKey()] = m;
1293  for (pair<VuoCompilerModule *, VuoCompilerModule *> m : modulesModified)
1294  modulesModifiedMap[m.first->getPseudoBase()->getModuleKey()] = m;
1295  for (VuoCompilerModule *m : modulesRemoved)
1296  modulesRemovedMap[m->getPseudoBase()->getModuleKey()] = m;
1297 
1298  dispatch_sync(compilersToNotifyQueue, ^{
1299  for (set<VuoCompiler *>::iterator i = compilersToNotify.begin(); i != compilersToNotify.end(); ++i) {
1300  delegateData->retain();
1301  }
1302  for (set<VuoCompiler *>::iterator i = compilersToNotify.begin(); i != compilersToNotify.end(); ++i) {
1303  (*i)->loadedModules(modulesAddedMap, modulesModifiedMap, modulesRemovedMap, issues, delegateData, this);
1304  }
1305  });
1306  }
1307  else
1308  {
1309  delete issues;
1310  }
1311 }
1312 
1323 set<VuoCompilerModule *> VuoCompiler::Environment::loadCompiledModules(const set<string> &moduleKeys, const map<string, string> &sourceCodeForModule)
1324 {
1325  ModuleInfoIterator modulesToLoadIter = listModules(moduleKeys);
1326  set<VuoCompilerModule *> modulesLoaded;
1327 
1328  ModuleInfo *moduleInfo;
1329  while ((moduleInfo = modulesToLoadIter.next()))
1330  {
1331  string moduleKey = moduleInfo->getModuleKey();
1332 
1333  // Skip the module if its file is not in this environment, if the module has already been loaded,
1334  // or if the compiler previously tried to load the module and it failed.
1335  if (moduleInfo->getEnvironment() != this || findModule(moduleKey) || moduleInfo->getAttempted())
1336  continue;
1337 
1338  moduleInfo->setAttempted(true);
1339 
1340  // Actually load the module.
1341  VuoCompilerModule *module = loadModule(moduleInfo);
1342 
1343  if (module)
1344  {
1345  modulesLoaded.insert(module);
1346  addToDependencyGraph(module);
1347  modulesChanged();
1348 
1349  // For a compiled subcomposition or other source file, store its source code in the VuoCompilerNodeClass.
1350  string searchPath = moduleInfo->getSearchPath();
1351  if (VuoFileUtilities::arePathsEqual(searchPath, getCompiledModuleCachePath()) ||
1352  VuoFileUtilities::arePathsEqual(searchPath, getOverriddenCompiledModuleCachePath()))
1353  {
1354  VuoCompilerNodeClass *nodeClass = dynamic_cast<VuoCompilerNodeClass *>(module);
1355  if (nodeClass)
1356  {
1357  ModuleInfo *sourceInfo = listSourceFile(moduleKey);
1358  if (sourceInfo)
1359  nodeClass->setSourcePath( sourceInfo->getFile()->path() );
1360 
1361  auto sourceCodeIter = sourceCodeForModule.find(moduleKey);
1362  if (sourceCodeIter != sourceCodeForModule.end())
1363  nodeClass->setSourceCode( sourceCodeIter->second );
1364  }
1365  }
1366  }
1367  }
1368 
1369  return modulesLoaded;
1370 }
1371 
1383 set<dispatch_group_t> VuoCompiler::Environment::loadSpecializedModules(const set<string> &moduleKeys,
1384  VuoCompiler *compiler, dispatch_queue_t llvmQueue)
1385 {
1386  set<dispatch_group_t > loadingGroups;
1387 
1388  for (string moduleKey : moduleKeys)
1389  {
1390  // Skip if it's not a node class.
1391 
1392  if (moduleKey.find(".") == string::npos || VuoStringUtilities::endsWith(moduleKey, ".framework"))
1393  continue;
1394 
1395  // Skip the module if it's already been loaded.
1396 
1397  if (findModule(moduleKey))
1398  continue;
1399 
1400  // Skip the module if it's in the process of being loaded, but have the caller wait for it to finish loading.
1401 
1402  map<string, dispatch_group_t>::iterator iter = specializedModulesLoading.find(moduleKey);
1403  if (iter != specializedModulesLoading.end())
1404  {
1405  loadingGroups.insert(iter->second);
1406  dispatch_retain(iter->second);
1407  continue;
1408  }
1409 
1410  dispatch_group_t loadingGroup = dispatch_group_create();
1411  specializedModulesLoading[moduleKey] = loadingGroup;
1412  loadingGroups.insert(loadingGroup);
1413 
1414  // Generate the module.
1415  // This is done asynchronously since VuoCompilerSpecializedNodeClass::newNodeClass() needs to use environmentQueue
1416  // to compile the generated C code.
1417 
1418  dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
1419  dispatch_group_async(loadingGroup, queue, ^{
1420  VuoNodeClass *baseNodeClass = nullptr;
1421  try
1422  {
1423  baseNodeClass = VuoCompilerSpecializedNodeClass::newNodeClass(moduleKey, compiler, llvmQueue);
1424  }
1425  catch (VuoCompilerException &e)
1426  {
1427  VUserLog("Error: %s", e.what());
1428  }
1429 
1430  if (baseNodeClass)
1431  {
1432  VuoCompilerSpecializedNodeClass *specNodeClass = static_cast<VuoCompilerSpecializedNodeClass *>(baseNodeClass->getCompiler());
1433  compiler->loadNodeClassGeneratedAtRuntime(specNodeClass, this);
1434 
1435  set<string> dependencies = specNodeClass->getDependencies();
1436  compiler->loadModulesIfNeeded(dependencies);
1437  }
1438 
1439  dispatch_sync(environmentQueue, ^{
1440  specializedModulesLoading.erase(moduleKey);
1441  });
1442  });
1443  }
1444 
1445  return loadingGroups;
1446 }
1447 
1460 set<dispatch_group_t> VuoCompiler::Environment::compileModulesFromSourceCode(const set<string> &moduleKeys, bool shouldRecompileIfUnchanged)
1461 {
1462  ModuleInfoIterator modulesToLoadIter = listSourceFiles(moduleKeys);
1463 
1464  set<dispatch_group_t> sourcesLoading;
1465  int sourcesEnqueued = 0;
1466  ModuleInfo *sourceInfo;
1467  while ((sourceInfo = modulesToLoadIter.next()))
1468  {
1469  string moduleKey = sourceInfo->getModuleKey();
1470 
1471  dispatch_group_t loadingGroup = sourceInfo->getLoadingGroup();
1472  sourcesLoading.insert(loadingGroup);
1473 
1474  // Skip compiling if the source file has already been scheduled for compilation.
1475  // Either its compilation is in progress or it failed to compile.
1476 
1477  if (sourceInfo->getAttempted())
1478  continue;
1479 
1480  // Skip compiling if the compiled module is up-to-date.
1481 
1482  string sourceCode = sourceInfo->getSourceCode();
1483 
1484  if (! shouldRecompileIfUnchanged)
1485  {
1486  VuoCompilerNodeClass *existingNodeClass = getNodeClass(moduleKey);
1487  if (existingNodeClass && existingNodeClass->getSourceCode() == sourceCode)
1488  continue;
1489  }
1490 
1491  // Enqueue the source file to be compiled.
1492 
1493  sourceInfo->setAttempted(true);
1494 
1495  dispatch_group_enter(loadingGroup);
1496  dispatch_group_enter(moduleSourceCompilersExist);
1497 
1499  queueItem->moduleKey = moduleKey;
1500  queueItem->sourcePath = sourceInfo->getFile()->path();
1501  queueItem->sourceCode = sourceCode;
1502  queueItem->sourceFile = sourceInfo->getFile();
1503  queueItem->cachedModulesPath = sourceInfo->isSourceCodeOverridden() ? getOverriddenCompiledModuleCachePath() : getCompiledModuleCachePath();
1504  queueItem->compiledModulePath = queueItem->cachedModulesPath + "/" + moduleKey + ".vuonode";
1505  queueItem->loadingGroup = loadingGroup;
1506  moduleCompilationQueue->enqueue(queueItem);
1507  ++sourcesEnqueued;
1508  }
1509 
1510  // Compile each enqueued source file. This is done asynchronously for several reasons:
1511  // - To avoid environmentQueue calling compiler code calling environmentQueue.
1512  // - To compile dependencies that were enqueued later while a compilation that was enqueued earlier waits.
1513  // - To be more efficient when compiling multiple source files.
1514 
1515  for (int i = 0; i < sourcesEnqueued; ++i)
1516  {
1517  dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
1518  dispatch_async(queue, ^{
1519  VuoModuleCompilationQueue::Item *queueItem = moduleCompilationQueue->next();
1520  VUserLog("Compiling %s", queueItem->moduleKey.c_str());
1521 
1522  try
1523  {
1525  }
1526  catch (VuoException &e)
1527  {
1528  VuoCompilerIssue issue(VuoCompilerIssue::Error, "compiling subcomposition", "", "Couldn't create cached modules folder", e.what());
1529  VuoCompilerIssues *issues = new VuoCompilerIssues(issue);
1530  notifyCompilers(set<VuoCompilerModule *>(), set< pair<VuoCompilerModule *, VuoCompilerModule *> >(), set<VuoCompilerModule *>(), issues, false);
1531  }
1532 
1533  VuoCompiler *compiler = new VuoCompiler(queueItem->sourcePath);
1534 
1535  auto moduleLoadedCallback = [=](){
1536  dispatch_group_leave(queueItem->loadingGroup);
1537  moduleCompilationQueue->completed(queueItem);
1538  };
1539 
1540  string ext = queueItem->sourceFile->extension();
1542  {
1543  VuoModuleCompiler *moduleCompiler = NULL;
1544  try
1545  {
1546  moduleCompiler = VuoModuleCompiler::newModuleCompiler("isf", queueItem->moduleKey, queueItem->sourceFile);
1547  }
1548  catch (VuoException &e)
1549  {
1550  VuoCompilerIssues *issues = new VuoCompilerIssues(VuoCompilerIssue(VuoCompilerIssue::Error, "compiling ISF module", queueItem->sourcePath, "", e.what()));
1551  notifyCompilers(set<VuoCompilerModule *>(), set< pair<VuoCompilerModule *, VuoCompilerModule *> >(), set<VuoCompilerModule *>(), issues, false);
1552  }
1553 
1554  if (moduleCompiler)
1555  {
1556  moduleCompiler->overrideSourceCode(queueItem->sourceCode, queueItem->sourceFile);
1557 
1558  auto getType = [&compiler] (const string &moduleKey) { return compiler->getType(moduleKey); };
1559  VuoCompilerIssues *issues = new VuoCompilerIssues();
1560  Module *module = moduleCompiler->compile(getType, llvmQueue, issues);
1561 
1562  if (module)
1563  {
1564  dispatch_sync(llvmQueue, ^{
1565  //setTargetForModule(module, target);
1566  writeModuleToBitcode(module, queueItem->compiledModulePath);
1567  });
1568  }
1569  else
1570  {
1571  issues->setFilePathIfEmpty(queueItem->sourcePath);
1572  }
1573 
1574  moduleSearchPathContentsChanged(queueItem->cachedModulesPath, queueItem->compiledModulePath, queueItem->sourceCode, moduleLoadedCallback, compiler, issues);
1575  }
1576  else
1577  moduleLoadedCallback();
1578  }
1579  else if (VuoFileUtilities::isCSourceExtension(ext) && ! queueItem->cachedModulesPath.empty())
1580  {
1581  try
1582  {
1583  compiler->compileModule(queueItem->sourcePath, queueItem->compiledModulePath, vector<string>());
1584  moduleSearchPathContentsChanged(queueItem->cachedModulesPath, queueItem->compiledModulePath, queueItem->sourceCode, moduleLoadedCallback, compiler);
1585  }
1586  catch (VuoCompilerException &e)
1587  {
1588  VuoCompilerIssues *issues = new VuoCompilerIssues;
1589  for (auto issue : e.getIssues()->getList())
1590  issues->append(issue);
1591  notifyCompilers(set<VuoCompilerModule *>(), set< pair<VuoCompilerModule *, VuoCompilerModule *> >(), set<VuoCompilerModule *>(), issues, false);
1592  moduleLoadedCallback();
1593  }
1594  }
1595  else
1596  {
1597  compiler->setLoadAllModules(false);
1598  compiler->compileSubcompositionString(queueItem->sourceCode, queueItem->compiledModulePath, moduleLoadedCallback, this, NULL, queueItem->sourcePath);
1599  }
1600 
1601  delete compiler;
1602  dispatch_group_leave(moduleSourceCompilersExist);
1603  });
1604  }
1605 
1606  return sourcesLoading;
1607 }
1608 
1617 set<VuoCompilerModule *> VuoCompiler::Environment::unloadCompiledModules(const set<string> &moduleKeys)
1618 {
1619  set<VuoCompilerModule *> modulesUnloaded;
1620 
1621  for (set<string>::const_iterator i = moduleKeys.begin(); i != moduleKeys.end(); ++i)
1622  {
1623  string moduleKey = *i;
1624  VuoCompilerModule *module = NULL;
1625 
1626  map<string, VuoCompilerNodeClass *>::iterator nodeClassIter = nodeClasses.find(moduleKey);
1627  if (nodeClassIter != nodeClasses.end())
1628  {
1629  module = nodeClassIter->second;
1630  nodeClasses.erase(nodeClassIter);
1631  }
1632  else
1633  {
1634  map<string, VuoCompilerType *>::iterator typeIter = types.find(moduleKey);
1635  if (typeIter != types.end())
1636  {
1637  module = typeIter->second;
1638  types.erase(typeIter);
1639  }
1640  else
1641  {
1642  map<string, VuoCompilerModule *>::iterator libraryModuleIter = libraryModules.find(moduleKey);
1643  if (libraryModuleIter != libraryModules.end())
1644  {
1645  module = libraryModuleIter->second;
1646  libraryModules.erase(libraryModuleIter);
1647  }
1648  }
1649  }
1650 
1651  if (module)
1652  {
1653  modulesUnloaded.insert(module);
1654  removeFromDependencyGraph(module);
1655  modulesChanged();
1656  }
1657  }
1658 
1659  return modulesUnloaded;
1660 }
1661 
1667 void VuoCompiler::Environment::deleteModulesCompiledFromSourceCode(const set<string> &moduleKeys)
1668 {
1669  for (set<string>::const_iterator i = moduleKeys.begin(); i != moduleKeys.end(); ++i)
1670  {
1671  string moduleKey = *i;
1672 
1673  map<string, ModuleInfo *>::iterator iter = moduleFilesAtSearchPath[getCompiledModuleCachePath()].find(moduleKey);
1674  if (iter != moduleFilesAtSearchPath[getCompiledModuleCachePath()].end())
1675  VuoFileUtilities::deleteFile(iter->second->getFile()->path());
1676  }
1677 }
1678 
1686 VuoCompilerModule * VuoCompiler::Environment::loadModule(ModuleInfo *moduleInfo)
1687 {
1688  string moduleKey = moduleInfo->getModuleKey();
1689 
1690  // Skip certain LLVM modules that definitely aren't Vuo modules to avoid adding struct types defined in them to the LLVM
1691  // context, resulting in mismatched struct types in code generation (e.g. %struct.NodeContext and %struct.NodeContext.1).
1692  if (VuoStringUtilities::beginsWith(moduleKey, "libVuo"))
1693  return NULL;
1694 
1695  __block size_t inputDataBytes;
1696  __block char *rawInputData;
1697  dispatch_sync(llvmQueue, ^{
1698  try
1699  {
1700  rawInputData = moduleInfo->getFile()->getContentsAsRawData(inputDataBytes);
1701  }
1702  catch (VuoException &e)
1703  {
1704  rawInputData = NULL;
1705  VUserLog("Warning: Couldn't load module '%s'. Its file may have been deleted. (%s)", moduleKey.c_str(), e.what());
1706  }
1707  });
1708  if (! rawInputData)
1709  return NULL;
1710 
1711  char *processedInputData;
1712 #if VUO_PRO
1713  processedInputData = loadModule_Pro0(moduleInfo, moduleKey, inputDataBytes, rawInputData);
1714 #else
1715  processedInputData = rawInputData;
1716 #endif
1717 
1718  Module *module = NULL;
1719  bool moduleParseError = !processedInputData;
1720  if (!moduleParseError)
1721  {
1722  string moduleReadError;
1723  VuoLog_status("Loading module \"%s\"", moduleKey.c_str());
1724  module = readModuleFromBitcodeData(processedInputData, inputDataBytes, moduleReadError);
1725  VuoLog_status(NULL);
1726  free(processedInputData);
1727 
1728  if (!module)
1729  {
1730  moduleParseError = true;
1731 
1732  string dir, file, ext;
1733  VuoFileUtilities::splitPath(moduleInfo->getFile()->isInArchive() ? moduleInfo->getFile()->getRelativePath() : moduleInfo->getFile()->path(), dir, file, ext);
1735  if (dir == getCompiledModuleCachePath())
1736  VuoFileUtilities::deleteFile(moduleInfo->getFile()->path());
1737  else
1738  VUserLog("Error: Couldn't parse module '%s': %s.", moduleInfo->getFile()->getRelativePath().c_str(), moduleReadError.c_str());
1739  }
1740  }
1741 
1742  if (!moduleParseError)
1743  {
1744  string modulePath;
1745  if (! moduleInfo->getFile()->isInArchive())
1746  modulePath = moduleInfo->getFile()->path();
1747 
1748  __block VuoCompilerModule *compilerModule;
1749  dispatch_sync(llvmQueue, ^{
1750  compilerModule = VuoCompilerModule::newModule(moduleKey, module, modulePath);
1751  });
1752 
1753  if (compilerModule)
1754  {
1755  if (dynamic_cast<VuoCompilerNodeClass *>(compilerModule))
1756  nodeClasses[moduleKey] = static_cast<VuoCompilerNodeClass *>(compilerModule);
1757  else if (dynamic_cast<VuoCompilerType *>(compilerModule))
1758  types[moduleKey] = static_cast<VuoCompilerType *>(compilerModule);
1759  else
1760  libraryModules[moduleKey] = compilerModule;
1761 
1762  VuoNodeSet *nodeSet = VuoNodeSet::createNodeSetForModule(moduleInfo->getFile());
1763  if (nodeSet)
1764  {
1765  map<string, VuoNodeSet *>::iterator nodeSetIter = nodeSetForName.find(nodeSet->getName());
1766  if (nodeSetIter == nodeSetForName.end())
1767  {
1768  nodeSetForName[nodeSet->getName()] = nodeSet;
1769  }
1770  else
1771  {
1772  delete nodeSet;
1773  nodeSet = nodeSetIter->second;
1774  }
1775  compilerModule->getPseudoBase()->setNodeSet(nodeSet);
1776  }
1777 
1778 #if VUO_PRO
1779  loadModule_Pro1(rawInputData, processedInputData, compilerModule);
1780 #endif
1781 
1782  vector<string> builtInModuleSearchPaths = getBuiltInModuleSearchPaths();
1783  bool isBuiltIn = (find(builtInModuleSearchPaths.begin(), builtInModuleSearchPaths.end(), moduleInfo->getSearchPath()) != builtInModuleSearchPaths.end());
1784  compilerModule->setBuiltIn(isBuiltIn);
1785 
1786  return compilerModule;
1787  }
1788  else
1789  {
1790  destroyLlvmModule(module);
1791  }
1792  }
1793 
1794  return NULL;
1795 }
1796 
1802 void VuoCompiler::Environment::replaceNodeClass(VuoCompilerNodeClass *newNodeClass)
1803 {
1804  string moduleKey = newNodeClass->getBase()->getModuleKey();
1805 
1806  VuoCompilerNodeClass *oldNodeClass = nodeClasses[moduleKey];
1807  if (oldNodeClass)
1808  {
1809  removeFromDependencyGraph(oldNodeClass);
1810  destroyModule(oldNodeClass);
1811  }
1812 
1813  nodeClasses[moduleKey] = newNodeClass;
1814  addToDependencyGraph(newNodeClass);
1815  modulesChanged();
1816 }
1817 
1818 void VuoCompiler::Environment::addToDependencyGraph(VuoCompilerModule *module)
1819 {
1820  DependencyGraphVertex *vertex = DependencyGraphVertex::vertexForDependency(module->getPseudoBase()->getModuleKey(), dependencyGraph);
1821  dependencyGraph->addVertex(vertex);
1822 
1823  vertex->setEnvironment(this);
1824 
1825  VuoCompilerTargetSet compositionTargets;
1826  compositionTargets.restrictToCurrentOperatingSystemVersion();
1827  vertex->setCompatible( module->getCompatibleTargets().isCompatibleWithAllOf(compositionTargets) );
1828 
1829  set<string> dependencies = module->getDependencies();
1830  for (set<string>::iterator i = dependencies.begin(); i != dependencies.end(); ++i)
1831  {
1832  DependencyGraphVertex *depVertex = DependencyGraphVertex::vertexForDependency(*i, dependencyGraph);
1833  dependencyGraph->addEdge(vertex, depVertex);
1834  }
1835 }
1836 
1837 void VuoCompiler::Environment::removeFromDependencyGraph(VuoCompilerModule *module)
1838 {
1839  DependencyGraphVertex *vertex = DependencyGraphVertex::vertexForDependency(module->getPseudoBase()->getModuleKey(), dependencyGraph);
1840  dependencyGraph->removeVertex(vertex);
1841 }
1842 
1853 void VuoCompiler::Environment::reifyPortTypes(const map<string, VuoCompilerType *> &inheritedTypes)
1854 {
1855  map<string, VuoCompilerType *> availableTypes = inheritedTypes;
1856  availableTypes.insert(types.begin(), types.end());
1857 
1858  for (map<string, VuoCompilerNodeClass *>::const_iterator i = nodeClasses.begin(); i != nodeClasses.end(); ++i)
1859  {
1860  VuoNodeClass *nodeClass = i->second->getBase();
1861 
1862  vector<VuoPortClass *> inputPortClasses = nodeClass->getInputPortClasses();
1863  vector<VuoPortClass *> outputPortClasses = nodeClass->getOutputPortClasses();
1864  vector<VuoPortClass *> portClasses;
1865  portClasses.insert(portClasses.end(), inputPortClasses.begin(), inputPortClasses.end());
1866  portClasses.insert(portClasses.end(), outputPortClasses.begin(), outputPortClasses.end());
1867 
1868  for (vector<VuoPortClass *>::iterator j = portClasses.begin(); j != portClasses.end(); ++j)
1869  {
1870  VuoCompilerPortClass *portClass = static_cast<VuoCompilerPortClass *>((*j)->getCompiler());
1871  VuoType *baseType = portClass->getDataVuoType();
1872 
1873  if (baseType && ! baseType->hasCompiler())
1874  {
1875  string typeName = baseType->getModuleKey();
1876  VuoCompilerType *reifiedType = NULL;
1877 
1878  VuoGenericType *genericType = dynamic_cast<VuoGenericType *>(baseType);
1879  if (genericType)
1880  {
1881  reifiedType = VuoCompilerGenericType::newGenericType(genericType, availableTypes);
1882  if (reifiedType) {
1883  genericTypes.insert( static_cast<VuoCompilerGenericType *>(reifiedType) );
1884  }
1885  }
1886  else
1887  {
1888  map<string, VuoCompilerType *>::iterator reifiedTypeIter = availableTypes.find(typeName);
1889  if (reifiedTypeIter != availableTypes.end())
1890  {
1891  delete baseType;
1892  reifiedType = reifiedTypeIter->second;
1893  }
1894  }
1895 
1896  if (reifiedType) {
1897  portClass->setDataVuoType(reifiedType->getBase());
1898  }
1899  }
1900  }
1901 
1902  vector<VuoCompilerTriggerDescription *> triggers = nodeClass->getCompiler()->getTriggerDescriptions();
1903  for (vector<VuoCompilerTriggerDescription *>::iterator j = triggers.begin(); j != triggers.end(); ++j)
1904  {
1905  VuoCompilerTriggerDescription *trigger = *j;
1906  VuoType *baseType = trigger->getDataType();
1907 
1908  if (baseType && ! baseType->hasCompiler())
1909  {
1910  string typeName = baseType->getModuleKey();
1911  map<string, VuoCompilerType *>::iterator reifiedTypeIter = availableTypes.find(typeName);
1912  if (reifiedTypeIter != availableTypes.end())
1913  {
1914  delete baseType;
1915  VuoCompilerType *reifiedType = reifiedTypeIter->second;
1916  trigger->setDataType(reifiedType->getBase());
1917  }
1918  }
1919  }
1920  }
1921 }
1922 
1935 void VuoCompiler::Environment::getCacheableModulesAndDependencies(bool builtIn, bool installed, set<string> &cacheableModulesAndDependencies,
1936  set<string> &dylibsNeededToLinkToThisCache,
1937  set<string> &frameworksNeededToLinkToThisCache)
1938 {
1939  if (isModuleCacheInitialized && ! isModuleCacheableDataDirty)
1940  {
1941  cacheableModulesAndDependencies = moduleCacheContents;
1942  dylibsNeededToLinkToThisCache = moduleCacheDylibs;
1943  frameworksNeededToLinkToThisCache = moduleCacheFrameworks;
1944  return;
1945  }
1946 
1947  VuoCompilerTargetSet compositionTargets;
1948  compositionTargets.restrictToCurrentOperatingSystemVersion();
1949 
1950  // Include all modules…
1951  map<string, VuoCompilerModule *> allModules;
1952  allModules.insert(nodeClasses.begin(), nodeClasses.end());
1953  allModules.insert(types.begin(), types.end());
1954  allModules.insert(libraryModules.begin(), libraryModules.end());
1955 
1956  set<string> dependencies;
1957  for (map<string, VuoCompilerModule *>::iterator i = allModules.begin(); i != allModules.end(); ++i)
1958  {
1959  string moduleKey = i->first;
1960  VuoCompilerModule *module = i->second;
1961 
1962 #if VUO_PRO
1963  // … except Pro modules and modules that depend on them…
1964  if (module->requiresPro())
1965  continue;
1966 #endif
1967 
1968  // … and incompatible modules.
1969  if (! module->getCompatibleTargets().isCompatibleWithAllOf(compositionTargets))
1970  continue;
1971 
1972  cacheableModulesAndDependencies.insert(moduleKey);
1973 
1974  set<string> moduleDependencies = module->getDependencies();
1975  dependencies.insert(moduleDependencies.begin(), moduleDependencies.end());
1976  }
1977 
1978  // For the built-in environment, include Vuo's core dependencies.
1979  if (builtIn && installed)
1980  {
1981  vector<string> coreDependencies = getCoreVuoDependencies();
1982  dependencies.insert(coreDependencies.begin(), coreDependencies.end());
1983  }
1984 
1985  // Include all dependencies of the included module that are located in this environment.
1986  // (All modules are already included, so we only need to handle non-module dependencies.)
1987  for (set<string>::iterator i = dependencies.begin(); i != dependencies.end(); ++i)
1988  {
1989  string dependency = *i;
1990  if (allModules.find(dependency) == allModules.end())
1991  {
1992  if (VuoStringUtilities::endsWith(dependency, ".framework"))
1993  frameworksNeededToLinkToThisCache.insert(dependency);
1994  else
1995  {
1996  string dependencyPath = VuoCompiler::getLibraryPath(dependency, librarySearchPaths);
1997  if (! dependencyPath.empty())
1998  {
1999  if (VuoStringUtilities::endsWith(dependencyPath, ".dylib"))
2000  dylibsNeededToLinkToThisCache.insert(dependencyPath);
2001  else
2002  cacheableModulesAndDependencies.insert(dependency);
2003  }
2004  }
2005  }
2006  }
2007 
2008  moduleCacheSuffix = (installed ? "installed" : "generated");
2009  dylibsNeededToLinkToThisCache.insert(moduleCachePath + "/libVuoModuleCache-" + moduleCacheSuffix + ".dylib");
2010 
2011  moduleCacheDylibs = dylibsNeededToLinkToThisCache;
2012  moduleCacheFrameworks = frameworksNeededToLinkToThisCache;
2013 }
2014 
2034 void VuoCompiler::Environment::useModuleCache(bool shouldUseExistingCache, VuoCompiler *compiler, set<string> cacheableModulesAndDependencies,
2035  set<string> dylibsNeededToLinkToCaches, set<string> frameworksNeededToLinkToCaches)
2036 {
2037  // Ignore the cache if the `prelinkCache` preference is false.
2038 
2039  static dispatch_once_t checked = 0;
2040  static bool prelinkCache = true;
2041  dispatch_once(&checked, ^{
2042  Boolean valid;
2043  bool result = CFPreferencesGetAppBooleanValue(CFSTR("prelinkCache"), CFSTR("org.vuo.Editor"), &valid);
2044  if (valid)
2045  prelinkCache = result;
2046  });
2047  if (! prelinkCache)
2048  {
2049  VDebugLog("Ignoring the module cache since the 'prelinkCache' preference is false.");
2050  return;
2051  }
2052 
2053  // Don't do anything if this environment doesn't have a cache configured.
2054 
2055  if (moduleCachePath.empty())
2056  return;
2057 
2058  // Don't bother rechecking the cache if the modules in this environment haven't changed.
2059 
2060  string cacheDescription = string() + "the cache of " + moduleCacheSuffix + " modules at '" + moduleCachePath + "'";
2061  if (isModuleCacheInitialized && ! isModuleCacheableDataDirty)
2062  {
2063  VDebugLog("No need to recheck %s.", cacheDescription.c_str());
2064  return;
2065  }
2066 
2067  try
2068  {
2069  VDebugLog("Checking if %s is up-to-date…", cacheDescription.c_str());
2070 
2071  bool isCacheUpToDate = true;
2072 
2073  if (isModuleCacheInitialized && isModuleCacheableDataDirty)
2074  isCacheUpToDate = false;
2075 
2076  isModuleCacheInitialized = true;
2077  isModuleCacheableDataDirty = false;
2078 
2079  const string dylibFileName = "libVuoModuleCache-" + moduleCacheSuffix + ".dylib";
2080  const string indexFileName = "moduleCache-" + moduleCacheSuffix + ".txt";
2081  string dylibPath = moduleCachePath + "/" + dylibFileName;
2082  string indexPath = moduleCachePath + "/" + indexFileName;
2083 
2084  // Create the cache files if they don't already exist. (If they do exist, don't affect the last-modified times.)
2085 
2086  bool dylibFileExists = false;
2087  bool indexFileExists = false;
2088 
2089  bool dirExists = VuoFileUtilities::fileExists(moduleCachePath);
2090  if (dirExists)
2091  {
2092  dylibFileExists = VuoFileUtilities::fileExists(dylibPath);
2093  indexFileExists = VuoFileUtilities::fileExists(indexPath);
2094  }
2095 
2096  if (! (dirExists && dylibFileExists && indexFileExists))
2097  {
2098  if (shouldUseExistingCache)
2099  throw VuoException("Trying to use the existing cache, but the cache doesn't exist.");
2100  else
2101  {
2102  if (! dirExists)
2103  VuoFileUtilities::makeDir(moduleCachePath);
2104  if (! indexFileExists)
2105  VuoFileUtilities::createFile(indexPath);
2106  if (! dylibFileExists)
2107  VuoFileUtilities::createFile(dylibPath);
2108 
2109  isCacheUpToDate = false;
2110  }
2111  }
2112 
2113  // Lock the cache for reading.
2114 
2115  VuoFileUtilities::File *fileForLocking;
2116  {
2117  fileForLocking = moduleCacheFileForLocking[dylibPath];
2118  if (! fileForLocking)
2119  {
2120  fileForLocking = new VuoFileUtilities::File(moduleCachePath, dylibFileName);
2121  moduleCacheFileForLocking[dylibPath] = fileForLocking;
2122  }
2123 
2124  if (!fileForLocking->lockForReading())
2125  VDebugLog("\tWarning: Couldn't lock for reading.");
2126  }
2127 
2128  // Check if the dylib looks remotely valid.
2129 
2130  if (isCacheUpToDate)
2131  {
2132  bool dylibHasData = VuoFileUtilities::fileContainsReadableData(dylibPath);
2133  if (! dylibHasData)
2134  {
2135  if (shouldUseExistingCache)
2136  throw VuoException("Trying to use the existing cache, but the cache doesn't contain readable data.");
2137  else
2138  isCacheUpToDate = false;
2139  }
2140  }
2141 
2142  // List the items actually in the cache, according to its index.
2143 
2144  const char separator = '\n';
2145  if (isCacheUpToDate || shouldUseExistingCache)
2146  {
2147  VuoFileUtilities::File indexFile(moduleCachePath, indexFileName);
2148  string index = indexFile.getContentsAsString();
2149  vector<string> actualIndex = VuoStringUtilities::split(index, separator);
2150 
2151  moduleCacheContents.clear();
2152  moduleCacheContents.insert(actualIndex.begin(), actualIndex.end());
2153 
2154  if (shouldUseExistingCache)
2155  {
2156  isModuleCacheAvailable = true;
2157  return;
2158  }
2159  }
2160 
2161  // Check if the list of actual items matches the list of expected items.
2162 
2163  if (isCacheUpToDate)
2164  {
2165  if (moduleCacheContents.size() != cacheableModulesAndDependencies.size())
2166  isCacheUpToDate = false;
2167  else
2168  {
2169  set<string> contentsInBoth;
2170  std::set_intersection(moduleCacheContents.begin(), moduleCacheContents.end(),
2171  cacheableModulesAndDependencies.begin(), cacheableModulesAndDependencies.end(),
2172  std::inserter(contentsInBoth, contentsInBoth.begin()));
2173 
2174  if (contentsInBoth.size() != cacheableModulesAndDependencies.size())
2175  isCacheUpToDate = false;
2176  }
2177  }
2178 
2179  // Check if the cache is newer than all of the modules in it.
2180 
2181  if (isCacheUpToDate)
2182  {
2183  unsigned long cacheLastModified = VuoFileUtilities::getFileLastModifiedInSeconds(dylibPath);
2184 
2185  for (map<string, map<string, ModuleInfo *> >::iterator i = moduleFilesAtSearchPath.begin(); i != moduleFilesAtSearchPath.end(); ++i)
2186  {
2187  for (map<string, ModuleInfo *>::iterator j = i->second.begin(); j != i->second.end(); ++j)
2188  {
2189  if (! j->second->isOlderThan(cacheLastModified))
2190  {
2191  isCacheUpToDate = false;
2192  break;
2193  }
2194  }
2195  }
2196  }
2197 
2198  // If the cache is consistent with this environment, we're done.
2199 
2200  if (isCacheUpToDate)
2201  {
2202  VDebugLog("Up-to-date.");
2203 
2204  isModuleCacheAvailable = true;
2205  return;
2206  }
2207 
2208  // Otherwise, (re)build the cache.
2209 
2210  dispatch_async(moduleCacheBuildingQueue, ^{
2211  VDebugLog("Rebuilding %s…", cacheDescription.c_str());
2212 
2213  set<Module *> modulesToLink;
2214  set<string> librariesToLink;
2215  set<string> frameworksToLink;
2216  {
2217  compiler->getLinkerInputs(cacheableModulesAndDependencies, Optimization_SmallBinary, modulesToLink, librariesToLink, frameworksToLink);
2218 
2219  librariesToLink.insert(dylibsNeededToLinkToCaches.begin(), dylibsNeededToLinkToCaches.end());
2220  set<string>::iterator iter = librariesToLink.find(dylibPath); // getCacheableModulesAndDependencies includes dylibPath, but don't want to link against self
2221  if (iter != librariesToLink.end())
2222  librariesToLink.erase(iter);
2223 
2224  frameworksToLink.insert(frameworksNeededToLinkToCaches.begin(), frameworksNeededToLinkToCaches.end());
2225  }
2226 
2227  bool gotLockForWriting = false;
2228  try
2229  {
2230  // Try to upgrade the file lock for writing.
2231  gotLockForWriting = fileForLocking->lockForWriting(true);
2232  if (! gotLockForWriting)
2233  throw VuoException("The cache file is being used by another process. "
2234  "If any composition windows are open from previous Vuo sessions, quit them. "
2235  "If any processes whose names start with \"VuoComposition\" or one of your composition file names appear in Activity Monitor, force-quit them.");
2236 
2237  // Link the dependencies to create a temporary file.
2238  string dir, file, ext;
2239  VuoFileUtilities::splitPath(dylibFileName, dir, file, ext);
2240  string tmpPath = VuoFileUtilities::makeTmpFile(file, "dylib");
2241  compiler->link(tmpPath, modulesToLink, librariesToLink, frameworksToLink, true);
2242 
2243  // Copy the contents of the temporary file into the cached resources dylib.
2244  // This preserves the lock on the cached resources dylib file (https://b33p.net/kosada/node/12970).
2245  VuoFileUtilities::copyFile(tmpPath, dylibPath, true);
2247 
2249  getVuoFrameworkPath() + "/Helpers/install_name_tool",
2250  "-id",
2251  dylibPath,
2252  dylibPath,
2253  });
2254 
2255  // Write the list of dependencies to the index file.
2256  vector<string> expectedContents(cacheableModulesAndDependencies.begin(), cacheableModulesAndDependencies.end());
2257  string index = VuoStringUtilities::join(expectedContents, separator);
2258  VuoFileUtilities::writeStringToFile(index, indexPath);
2259 
2260  // Downgrade the file lock back to reading.
2261  if (!fileForLocking->lockForReading())
2262  VDebugLog("\tWarning: Couldn't downgrade the lock back to reading.");
2263 
2264  dispatch_sync(environmentQueue, ^{
2265  moduleCacheContents = cacheableModulesAndDependencies;
2266  isModuleCacheAvailable = true;
2267  });
2268  }
2269  catch (VuoException &e)
2270  {
2271  // Downgrade the file lock back to reading.
2272  if (gotLockForWriting)
2273  if (!fileForLocking->lockForReading())
2274  VDebugLog("\tWarning: Couldn't downgrade the lock back to reading.");
2275 
2276  VUserLog("Warning: Couldn't rebuild %s for the \"faster build\" optimization: %s", cacheDescription.c_str(), e.what());
2277  }
2278 
2279  VDebugLog("Done.");
2280  });
2281  }
2282  catch (VuoException &e)
2283  {
2284  VUserLog("Warning: Couldn't use %s for the \"faster build\" optimization: %s", cacheDescription.c_str(), e.what());
2285  }
2286 }
2287 
2291 void VuoCompiler::Environment::waitForModuleCachesToBuild(void)
2292 {
2293  dispatch_sync(moduleCacheBuildingQueue, ^{});
2294 }
2295 
2305 bool VuoCompiler::Environment::findInModuleCache(const string &moduleOrDependency, string &cachePath)
2306 {
2307  if (isModuleCacheAvailable && moduleCacheContents.find(moduleOrDependency) != moduleCacheContents.end())
2308  {
2309  cachePath = moduleCachePath + "/libVuoModuleCache-" + moduleCacheSuffix + ".dylib";
2310  return true;
2311  }
2312 
2313  return false;
2314 }
2315 
2322 void VuoCompiler::Environment::modulesChanged(void)
2323 {
2324  isModuleCacheableDataDirty = true;
2325  isModuleCacheAvailable = false;
2326 }
2327 
2331 bool VuoCompiler::Environment::isBuiltIn()
2332 {
2333  return this == sharedEnvironments[0][0];
2334 }
2335 
2339 string VuoCompiler::Environment::getName()
2340 {
2341  if (isBuiltIn())
2342  return "builtin";
2343  else if (this == sharedEnvironments[1][0])
2344  return "system";
2345  else if (this == sharedEnvironments[2][0])
2346  return "user";
2347  return "composition-local";
2348 }
2349 
2353 void VuoCompiler::applyToInstalledEnvironments(void (^doForEnvironment)(Environment *))
2354 {
2355  dispatch_sync(environmentQueue, ^{
2356  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i) {
2357  doForEnvironment((*i)[0]);
2358  }
2359  });
2360 }
2361 
2365 void VuoCompiler::applyToInstalledEnvironments(void (^doForEnvironment)(Environment *, bool *, string), bool *result, string arg)
2366 {
2367  dispatch_sync(environmentQueue, ^{
2368  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i) {
2369  doForEnvironment((*i)[0], result, arg);
2370  }
2371  });
2372 }
2373 
2377 void VuoCompiler::applyToAllEnvironments(void (^doForEnvironment)(Environment *environment))
2378 {
2379  dispatch_sync(environmentQueue, ^{
2380  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i) {
2381  for (vector<Environment *>::iterator j = i->begin(); j != i->end(); ++j) {
2382  doForEnvironment(*j);
2383  }
2384  }
2385  });
2386 }
2387 
2396 VuoCompiler::VuoCompiler(const string &compositionPath)
2397 {
2398 #if VUO_PRO
2399  VuoCompiler_Pro();
2400 #endif
2401 
2402  shouldLoadAllModules = true;
2403  hasLoadedAllModules = false;
2404  modulesToLoadQueue = dispatch_queue_create("org.vuo.compiler.modules", NULL);
2405  moduleCacheBuilding = dispatch_group_create();
2406  dependencyGraph = NULL;
2407  compositionDependencyGraph = NULL;
2408  isVerbose = false;
2409  _shouldShowSplashWindow = false;
2410  delegate = NULL;
2411  delegateQueue = dispatch_queue_create("org.vuo.compiler.delegate", NULL);
2412 
2413  string vuoFrameworkPath = getVuoFrameworkPath();
2414  if (! vuoFrameworkPath.empty())
2415  clangPath = vuoFrameworkPath + "/Helpers/clang";
2416  else
2417  clangPath = llvm::sys::Path(StringRef(LLVM_ROOT "/bin/clang"));
2418 
2419  dispatch_sync(environmentQueue, ^{
2420  if (sharedEnvironments.empty())
2421  {
2422  sharedEnvironments = vector< vector<Environment *> >(3, vector<Environment *>(2, NULL));
2423  for (vector< vector<Environment *> >::iterator i = sharedEnvironments.begin(); i != sharedEnvironments.end(); ++i) {
2424  for (vector<Environment *>::iterator j = i->begin(); j != i->end(); ++j) {
2425  *j = new Environment();
2426  }
2427  }
2428 
2429  vector<string> builtInModuleSearchPaths = Environment::getBuiltInModuleSearchPaths();
2430  for (vector<string>::iterator i = builtInModuleSearchPaths.begin(); i != builtInModuleSearchPaths.end(); ++i) {
2431  sharedEnvironments[0][0]->addModuleSearchPath(*i, false);
2432  }
2433 
2434  vector<string> builtInHeaderSearchPaths = Environment::getBuiltInHeaderSearchPaths();
2435  for (vector<string>::iterator i = builtInHeaderSearchPaths.begin(); i != builtInHeaderSearchPaths.end(); ++i) {
2436  sharedEnvironments[0][0]->addHeaderSearchPath(*i);
2437  }
2438 
2439  vector<string> builtInLibrarySearchPaths = Environment::getBuiltInLibrarySearchPaths();
2440  for (vector<string>::iterator i = builtInLibrarySearchPaths.begin(); i != builtInLibrarySearchPaths.end(); ++i) {
2441  sharedEnvironments[0][0]->addLibrarySearchPath(*i);
2442  }
2443 
2444  vector<string> builtInFrameworkSearchPaths = Environment::getBuiltInFrameworkSearchPaths();
2445  for (vector<string>::iterator i = builtInFrameworkSearchPaths.begin(); i != builtInFrameworkSearchPaths.end(); ++i) {
2446  sharedEnvironments[0][0]->addFrameworkSearchPath(*i);
2447  }
2448 
2449  // Allow system administrator to override Vuo.framework modules
2450  sharedEnvironments[1][0]->addModuleSearchPath(VuoFileUtilities::getSystemModulesPath());
2451  sharedEnvironments[1][0]->addLibrarySearchPath(VuoFileUtilities::getSystemModulesPath());
2452 
2453  // Allow user to override Vuo.framework and system-wide modules
2454  sharedEnvironments[2][0]->addModuleSearchPath(VuoFileUtilities::getUserModulesPath());
2455  sharedEnvironments[2][0]->addLibrarySearchPath(VuoFileUtilities::getUserModulesPath());
2456 
2457  // Set up module cache paths.
2458  // Since the built-in module caches are part of Vuo.framework (put there by `generateBuiltInModuleCaches`),
2459  // only attempt to use module caches if Vuo.framework exists.
2460  string vuoFrameworkPath = getVuoFrameworkPath();
2461  if (! vuoFrameworkPath.empty())
2462  {
2463  vector<string> moduleCachePaths(3);
2464  moduleCachePaths[0] = vuoFrameworkPath + "/Modules/Builtin";
2465  moduleCachePaths[1] = VuoFileUtilities::getCachePath() + "/System";
2466  moduleCachePaths[2] = VuoFileUtilities::getCachePath() + "/User";
2467 
2468  for (size_t i = 0; i < moduleCachePaths.size(); ++i)
2469  {
2470  string moduleCachePath = moduleCachePaths[i];
2471 
2472  sharedEnvironments[i][0]->setModuleCachePath(moduleCachePath);
2473  sharedEnvironments[i][0]->addModuleSearchPath(moduleCachePath + "/Modules", false);
2474 
2475  sharedEnvironments[i][1]->setModuleCachePath(moduleCachePath);
2476  }
2477  }
2478  }
2479  });
2480 
2481  setCompositionPath(compositionPath);
2482 }
2483 
2488 {
2489  dispatch_group_wait(moduleCacheBuilding, DISPATCH_TIME_FOREVER);
2490  dispatch_release(moduleCacheBuilding);
2491 
2492  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
2493  (*i)[0]->removeCompilerToNotify(this);
2494 
2495  dispatch_sync(delegateQueue, ^{});
2496 
2497  dispatch_release(modulesToLoadQueue);
2498  dispatch_release(delegateQueue);
2499 
2500  delete dependencyGraph;
2501  delete compositionDependencyGraph;
2502 }
2503 
2507 void VuoCompiler::reset(void)
2508 {
2509  dispatch_group_wait(moduleSourceCompilersExist, DISPATCH_TIME_FOREVER);
2510 
2511  for (vector< vector<Environment *> >::iterator i = sharedEnvironments.begin(); i != sharedEnvironments.end(); ++i)
2512  {
2513  (*i)[0]->stopWatchingModuleSearchPaths();
2514  dispatch_sync((*i)[0]->moduleSearchPathContentsChangedQueue, ^{});
2515  }
2516 
2517  for (map< string, vector<Environment *> >::iterator i = environmentsForCompositionFamily.begin(); i != environmentsForCompositionFamily.end(); ++i)
2518  {
2519  (i->second)[0]->stopWatchingModuleSearchPaths();
2520  dispatch_sync((i->second)[0]->moduleSearchPathContentsChangedQueue, ^{});
2521  }
2522 
2523  for (vector< vector<Environment *> >::iterator i = sharedEnvironments.begin(); i != sharedEnvironments.end(); ++i)
2524  for (vector<Environment *>::iterator j = i->begin(); j != i->end(); ++j)
2525  delete *j;
2526 
2527  for (map< string, vector<Environment *> >::iterator i = environmentsForCompositionFamily.begin(); i != environmentsForCompositionFamily.end(); ++i)
2528  for (vector<Environment *>::iterator j = i->second.begin(); j != i->second.end(); ++j)
2529  delete *j;
2530 
2531  sharedEnvironments.clear();
2532  environmentsForCompositionFamily.clear();
2533 }
2534 
2541 {
2542  dispatch_async(delegateQueue, ^{
2543  this->delegate = delegate;
2544  });
2545 }
2546 
2556 void VuoCompiler::setCompositionPath(const string &compositionPath)
2557 {
2558  string compositionModulesDir;
2559  string compositionBaseDir;
2560  lastCompositionIsSubcomposition = false;
2561  if (! compositionPath.empty())
2562  {
2563  compositionModulesDir = VuoFileUtilities::getCompositionLocalModulesPath(compositionPath);
2564 
2565  string file, ext;
2566  VuoFileUtilities::splitPath(compositionModulesDir, compositionBaseDir, file, ext);
2567  VuoFileUtilities::canonicalizePath(compositionBaseDir);
2568 
2569  string compositionDir;
2570  VuoFileUtilities::splitPath(compositionPath, compositionDir, file, ext);
2571  VuoFileUtilities::canonicalizePath(compositionDir);
2572  lastCompositionIsSubcomposition = (compositionDir == compositionModulesDir);
2573  }
2574 
2575  // Set up `environments` to contain all environments available to this compiler, in order from broadest to narrowest.
2576 
2577  dispatch_sync(environmentQueue, ^{
2578  if (! environments.empty() && compositionBaseDir == lastCompositionBaseDir) {
2579  return;
2580  }
2581  lastCompositionBaseDir = compositionBaseDir;
2582 
2583  // Clear out the existing environments for this compiler.
2584 
2585  Environment *oldCompositionFamilyInstalledEnvironment = nullptr;
2586  vector<Environment *> compositionEnvironments;
2587  if (! environments.empty())
2588  {
2589  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i) {
2590  (*i)[0]->removeCompilerToNotify(this);
2591  }
2592 
2593  if (environments.size() >= 5) {
2594  oldCompositionFamilyInstalledEnvironment = environments[3][0];
2595  }
2596 
2597  compositionEnvironments = environments.back();
2598 
2599  environments.clear();
2600  }
2601 
2602  // If the composition is located in one of the shared environments (built-in, system, user),
2603  // add that shared environment and any shared environments at broader scope to the compiler.
2604  // If the composition is not in a shared environment, add all of the shared environments to the compiler.
2605 
2606  bool isCompositionInSharedEnvironment = false;
2607  for (vector< vector<Environment *> >::iterator i = sharedEnvironments.begin(); i != sharedEnvironments.end(); ++i)
2608  {
2609  environments.push_back(*i);
2610 
2611  vector<string> moduleSearchPaths = (*i)[0]->getModuleSearchPaths();
2612  for (vector<string>::iterator j = moduleSearchPaths.begin(); j != moduleSearchPaths.end(); ++j)
2613  {
2614  string moduleSearchPath = *j;
2615  VuoFileUtilities::canonicalizePath(moduleSearchPath);
2616  if (moduleSearchPath == compositionModulesDir)
2617  {
2618  isCompositionInSharedEnvironment = true;
2619  break;
2620  }
2621  }
2622 
2623  if (isCompositionInSharedEnvironment) {
2624  break;
2625  }
2626  }
2627 
2628  // If the composition is not in a shared environment, add the composition-family environment to the compiler.
2629 
2630  Environment *newCompositionFamilyInstalledEnvironment = nullptr;
2631  if (! isCompositionInSharedEnvironment && ! compositionPath.empty())
2632  {
2633  vector<Environment *> compositionFamilyEnvironments = environmentsForCompositionFamily[compositionBaseDir];
2634  if (compositionFamilyEnvironments.empty())
2635  {
2636  compositionFamilyEnvironments = vector<Environment *>(2, NULL);
2637  compositionFamilyEnvironments[0] = new Environment();
2638  compositionFamilyEnvironments[1] = new Environment();
2639  environmentsForCompositionFamily[compositionBaseDir] = compositionFamilyEnvironments;
2640 
2641  // Allow the user to place modules/subcompositions in a Modules folder inside the composition folder.
2642 
2643  compositionFamilyEnvironments[0]->addModuleSearchPath(compositionModulesDir);
2644 
2645  // Locate this environment's cache in a folder whose name is the composition folder path with
2646  // slashes replaced with Unicode Modifier Letter Colon.
2647  string moduleCachePath = getCachePathForComposition(compositionBaseDir);
2648 
2649  compositionFamilyEnvironments[0]->setModuleCachePath(moduleCachePath);
2650  compositionFamilyEnvironments[0]->addModuleSearchPath(moduleCachePath + "/Modules", false);
2651 
2652  compositionFamilyEnvironments[1]->setModuleCachePath(moduleCachePath);
2653  }
2654  environments.push_back(compositionFamilyEnvironments);
2655 
2656  newCompositionFamilyInstalledEnvironment = compositionFamilyEnvironments[0];
2657  }
2658 
2659  // Add the composition environment to the compiler (or add it back in if it already existed).
2660 
2661  if (compositionEnvironments.empty())
2662  {
2663  compositionEnvironments = vector<Environment *>(2, NULL);
2664  compositionEnvironments[0] = new Environment();
2665  compositionEnvironments[1] = new Environment();
2666  }
2667  environments.push_back(compositionEnvironments);
2668 
2669  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i) {
2670  (*i)[0]->addCompilerToNotify(this);
2671  }
2672 
2673  delete dependencyGraph;
2674  delete compositionDependencyGraph;
2675  dependencyGraph = makeDependencyNetwork(environments, ^VuoDirectedAcyclicGraph * (Environment *env) { return env->getDependencyGraph(); });
2676  compositionDependencyGraph = makeDependencyNetwork(environments, ^VuoDirectedAcyclicGraph * (Environment *env) { return env->getCompositionDependencyGraph(); });
2677 
2678  // If the compiler has a different local Modules directory than before, notify the compiler's delegate
2679  // of composition-family modules that are newly available/unavailable.
2680 
2681  if (oldCompositionFamilyInstalledEnvironment != newCompositionFamilyInstalledEnvironment)
2682  {
2683  auto getModules = [] (Environment *env)
2684  {
2685  map<string, VuoCompilerModule *> modules;
2686  if (env)
2687  {
2688  for (auto i : env->getNodeClasses()) {
2689  modules.insert(i);
2690  }
2691  for (auto i : env->getTypes()) {
2692  modules.insert(i);
2693  }
2694  for (auto i : env->getLibraryModules()) {
2695  modules.insert(i);
2696  }
2697  }
2698  return modules;
2699  };
2700 
2701  map<string, VuoCompilerModule *> modulesAdded = getModules(newCompositionFamilyInstalledEnvironment);
2702  map<string, VuoCompilerModule *> modulesRemoved = getModules(oldCompositionFamilyInstalledEnvironment);
2703 
2704  map<string, pair<VuoCompilerModule *, VuoCompilerModule *> > modulesModified;
2705  for (map<string, VuoCompilerModule *>::iterator add = modulesAdded.begin(); add != modulesAdded.end(); )
2706  {
2707  map<string, VuoCompilerModule *>::iterator rem = modulesRemoved.find(add->first);
2708  if (rem != modulesRemoved.end())
2709  {
2710  modulesModified[add->first] = make_pair(rem->second, add->second);
2711  modulesAdded.erase(add++);
2712  modulesRemoved.erase(rem);
2713  }
2714  else
2715  {
2716  ++add;
2717  }
2718  }
2719 
2720  if (! (modulesAdded.empty() && modulesModified.empty() && modulesRemoved.empty()) )
2721  {
2722  VuoCompilerIssues *issues = new VuoCompilerIssues();
2723  VuoCompilerDelegate::LoadedModulesData *delegateData = new VuoCompilerDelegate::LoadedModulesData(set< pair<VuoCompilerModule *, VuoCompilerModule *> >(), set<VuoCompilerModule *>(), issues);
2724  delegateData->retain();
2725 
2726  Environment *scopeEnvironment = newCompositionFamilyInstalledEnvironment;
2727  if (! scopeEnvironment) {
2728  scopeEnvironment = compositionEnvironments.at(0);
2729  }
2730 
2731  loadedModules(modulesAdded, modulesModified, modulesRemoved, issues, delegateData, scopeEnvironment);
2732  }
2733  }
2734  });
2735 }
2736 
2737 // environmentQueue
2738 VuoDirectedAcyclicNetwork * VuoCompiler::makeDependencyNetwork(const vector< vector<Environment *> > &environments,
2739  VuoDirectedAcyclicGraph * (^graphForEnvironment)(Environment *))
2740 {
2741  if (!graphForEnvironment)
2742  return NULL;
2743 
2745 
2746  for (vector< vector<Environment *> >::const_iterator i = environments.begin(); i != environments.end(); ++i)
2747  {
2748  // Installed environment depends on generated environment in same scope.
2749 
2750  network->addEdge(graphForEnvironment(i->at(0)), graphForEnvironment(i->at(1)));
2751 
2752  // Installed and generated environments depend on installed environments in broader scopes.
2753 
2754  for (vector< vector<Environment *> >::const_iterator ii = environments.begin(); ii != i; ++ii)
2755  {
2756  network->addEdge(graphForEnvironment(i->at(0)), graphForEnvironment(ii->at(0)));
2757  network->addEdge(graphForEnvironment(i->at(1)), graphForEnvironment(ii->at(0)));
2758  }
2759  }
2760 
2761  return network;
2762 }
2763 
2784 void VuoCompiler::loadModulesIfNeeded(const set<string> &moduleKeys)
2785 {
2786  __block bool willLoadAllModules = false;
2787  if (moduleKeys.empty())
2788  {
2789  dispatch_sync(modulesToLoadQueue, ^{
2790  if (shouldLoadAllModules && ! hasLoadedAllModules) {
2791  willLoadAllModules = true;
2792  hasLoadedAllModules = true;
2793  }
2794  });
2795  }
2796 
2797  if (! willLoadAllModules && moduleKeys.empty())
2798  return;
2799 
2800  // Load modules and start sources compiling.
2801 
2802  __block set<dispatch_group_t> sourcesLoading;
2803  dispatch_sync(environmentQueue, ^{
2804  sourcesLoading = loadModulesAndSources(moduleKeys, set<string>(), set<string>(),
2805  moduleKeys, set<string>(), set<string>(),
2806  willLoadAllModules, false, nullptr, nullptr, nullptr, "");
2807  });
2808 
2809  // Wait for sources to finish compiling and their modules to be loaded,
2810  // to ensure that `getNodeClass(subcomposition)` finds the subcomposition node class.
2811 
2812  for (set<dispatch_group_t>::iterator i = sourcesLoading.begin(); i != sourcesLoading.end(); ++i)
2813  {
2814  dispatch_group_wait(*i, DISPATCH_TIME_FOREVER);
2815  dispatch_release(*i);
2816  }
2817 }
2818 
2827 set<dispatch_group_t> VuoCompiler::loadModulesAndSources(const set<string> &modulesAddedKeys, const set<string> &modulesModifiedKeys, const set<string> &modulesRemovedKeys,
2828  const set<string> &sourcesAddedKeys, const set<string> &sourcesModifiedKeys, const set<string> &sourcesRemovedKeys,
2829  bool willLoadAllModules, bool shouldRecompileSourcesIfUnchanged,
2830  Environment *currentEnvironment, VuoCompilerIssues *issuesForCurrentEnvironment,
2831  std::function<void(void)> moduleLoadedCallback, const string &moduleAddedOrModifiedSourceCode)
2832 {
2833  //VLog("C=%p E=%p -- %lu %lu %lu %lu %lu %lu %i %p %p", this, currentEnvironment,
2834  //modulesAddedKeys.size(), modulesModifiedKeys.size(), modulesRemovedKeys.size(),
2835  //sourcesAddedKeys.size(), sourcesModifiedKeys.size(), sourcesRemovedKeys.size(),
2836  //willLoadAllModules, issuesForCurrentEnvironment, moduleLoadedCallback);
2837  //if (modulesAddedKeys.size() == 1) VLog(" %s", modulesAddedKeys.begin()->c_str());
2838  //if (modulesModifiedKeys.size() == 1) VLog(" %s", modulesModifiedKeys.begin()->c_str());
2839  //if (modulesRemovedKeys.size() == 1) VLog(" %s", modulesRemovedKeys.begin()->c_str());
2840 
2841  // Organize the modules, source files, and issues by environment.
2842 
2843  map<Environment *, set<string> > modulesAdded;
2844  map<Environment *, set<string> > modulesModified;
2845  map<Environment *, set<string> > modulesRemoved;
2846  map<Environment *, set<string> > sourcesAdded;
2847  map<Environment *, set<string> > sourcesModified;
2848  map<Environment *, set<string> > sourcesRemoved;
2849  map<Environment *, set<string> > potentialSpecializedModules;
2850 
2851  if (currentEnvironment)
2852  {
2853  modulesAdded[currentEnvironment] = modulesAddedKeys;
2854  modulesModified[currentEnvironment] = modulesModifiedKeys;
2855  modulesRemoved[currentEnvironment] = modulesRemovedKeys;
2856  sourcesAdded[currentEnvironment] = sourcesAddedKeys;
2857  sourcesModified[currentEnvironment] = sourcesModifiedKeys;
2858  sourcesRemoved[currentEnvironment] = sourcesRemovedKeys;
2859  }
2860  else
2861  {
2862  Environment *genEnv = nullptr;
2863  if (! willLoadAllModules)
2864  {
2865  // If compiling a top-level composition, generated modules that were directly requested (in modulesAddedKeys) are
2866  // added at the composition scope so they won't be cached. This prevents link errors when running multiple
2867  // compositions in the current process (https://b33p.net/kosada/node/14317).
2868 
2869  int scope = environments.size() - (lastCompositionIsSubcomposition ? 2 : 1);
2870  genEnv = environments.at(scope).at(1);
2871  potentialSpecializedModules[genEnv] = modulesAddedKeys;
2872  }
2873 
2874  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
2875  {
2876  Environment *env = (*i).at(0);
2877 
2878  ModuleInfoIterator modulesAddedIter = (willLoadAllModules ? env->listAllModules() : env->listModules(modulesAddedKeys));
2879  ModuleInfoIterator sourcesAddedIter = (willLoadAllModules ? env->listAllSourceFiles() : env->listSourceFiles(sourcesAddedKeys));
2880  ModuleInfoIterator sourcesModifiedIter = (willLoadAllModules ? env->listAllSourceFiles() : env->listSourceFiles(sourcesModifiedKeys));
2881 
2882  ModuleInfo *moduleInfo;
2883  while ((moduleInfo = modulesAddedIter.next()))
2884  {
2885  string moduleKey = moduleInfo->getModuleKey();
2886 
2887  modulesAdded[env].insert(moduleKey);
2888 
2889  if (! willLoadAllModules)
2890  {
2891  auto foundIter = potentialSpecializedModules[genEnv].find(moduleKey);
2892  if (foundIter != potentialSpecializedModules[genEnv].end())
2893  potentialSpecializedModules[genEnv].erase(foundIter);
2894  }
2895  }
2896 
2897  // If a source file and a compiled file for the same module are in the same folder,
2898  // the compiled file takes precedence.
2899  auto isCompiledModuleAtSameSearchPath = [&env] (ModuleInfo *sourceInfo)
2900  {
2901  ModuleInfo *compiledModuleInfo = env->listModule(sourceInfo->getModuleKey());
2902  return (compiledModuleInfo && compiledModuleInfo->getSearchPath() == sourceInfo->getSearchPath());
2903  };
2904 
2905  while ((moduleInfo = sourcesAddedIter.next()))
2906  {
2907  if (isCompiledModuleAtSameSearchPath(moduleInfo))
2908  continue;
2909 
2910  sourcesAdded[env].insert( moduleInfo->getModuleKey() );
2911  }
2912 
2913  while ((moduleInfo = sourcesModifiedIter.next()))
2914  {
2915  if (isCompiledModuleAtSameSearchPath(moduleInfo))
2916  continue;
2917 
2918  sourcesModified[env].insert( moduleInfo->getModuleKey() );
2919  }
2920  }
2921  }
2922 
2923  map<Environment *, VuoCompilerIssues *> issues;
2924  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
2925  {
2926  Environment *env = (*i).at(0);
2927  issues[env] = (env == currentEnvironment && issuesForCurrentEnvironment ? issuesForCurrentEnvironment : new VuoCompilerIssues());
2928  }
2929 
2930  // Check for circular dependencies in sources.
2931 
2932  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
2933  {
2934  Environment *env = (*i).at(0);
2935 
2936  // Check for circular dependencies involving sources being loaded within this environment.
2937  // For circular dependencies involving sources in different environments,
2938  // an error will be reported elsewhere due to one of them being outside of the other's scope.
2939  set<VuoDirectedAcyclicGraph::Vertex *> circularDependencies = env->getCompositionDependencyGraph()->getCycleVertices();
2940 
2941  set<string> sourcesAddedModified;
2942  sourcesAddedModified.insert(sourcesAdded[env].begin(), sourcesAdded[env].end());
2943  sourcesAddedModified.insert(sourcesModified[env].begin(), sourcesModified[env].end());
2944 
2945  for (set<string>::iterator j = sourcesAddedModified.begin(); j != sourcesAddedModified.end(); ++j)
2946  {
2947  string moduleKey = *j;
2948 
2949  bool found = false;
2950  for (set<VuoDirectedAcyclicGraph::Vertex *>::iterator k = circularDependencies.begin(); k != circularDependencies.end(); ++k)
2951  {
2952  DependencyGraphVertex *vertex = static_cast<DependencyGraphVertex *>(*k);
2953  if (vertex->getDependency() == moduleKey)
2954  {
2955  found = true;
2956  break;
2957  }
2958  }
2959 
2960  if (found)
2961  {
2962  sourcesAdded[env].erase(moduleKey);
2963  sourcesModified[env].erase(moduleKey);
2964 
2965  VuoCompilerIssue issue(VuoCompilerIssue::Error, "compiling subcomposition", moduleKey,
2966  "Subcomposition contains itself",
2967  "%moduleKey contains an instance of itself, "
2968  "or contains another subcomposition that contains an instance of %moduleKey.");
2969  issue.setModuleKey(moduleKey);
2970 
2971  ModuleInfo *sourceInfo = env->listSourceFile(moduleKey);
2972  if (sourceInfo)
2973  issue.setFilePath(sourceInfo->getFile()->path());
2974 
2975  issues[env]->append(issue);
2976  }
2977  }
2978  }
2979 
2980  // Find all modules and sources that depend on the modules and sources being modified or removed.
2981  // Those that belong to one of this compiler's environments are used in subsequent stages.
2982  // Those that belong to some other environment are scheduled to be handled by a separate call to this function.
2983 
2984  map<Environment *, set<string> > modulesDepOnModulesModified;
2985  map<Environment *, set<string> > sourcesDepOnModulesModified;
2986  map<Environment *, set<string> > modulesDepOnModulesRemoved;
2987  map<Environment *, set<string> > sourcesDepOnModulesRemoved;
2988 
2989  {
2990  __block map<Environment *, set<string> > modulesDepOnModulesModified_otherCompiler;
2991  __block map<Environment *, set<string> > sourcesDepOnModulesModified_otherCompiler;
2992  __block map<Environment *, set<string> > modulesDepOnModulesRemoved_otherCompiler;
2993  __block map<Environment *, set<string> > sourcesDepOnModulesRemoved_otherCompiler;
2994 
2995  vector<VuoDirectedAcyclicNetwork *> searchDependencyGraphs;
2996  searchDependencyGraphs.push_back(dependencyGraph);
2997  for (map<string, vector<Environment *> >::iterator ii = environmentsForCompositionFamily.begin(); ii != environmentsForCompositionFamily.end(); ++ii)
2998  {
2999  vector< vector<Environment *> > otherEnvs = sharedEnvironments;
3000  otherEnvs.push_back(ii->second);
3001  VuoDirectedAcyclicNetwork *other = makeDependencyNetwork(otherEnvs, ^VuoDirectedAcyclicGraph * (Environment *env) { return env->getDependencyGraph(); });
3002  searchDependencyGraphs.push_back(other);
3003  }
3004 
3005  VuoDirectedAcyclicGraph *currentEnvironmentDependencyGraph = (currentEnvironment ? currentEnvironment->getDependencyGraph() : nullptr);
3006 
3007  findDependentModulesAndSources(modulesModified, searchDependencyGraphs, currentEnvironmentDependencyGraph,
3008  modulesDepOnModulesModified, modulesDepOnModulesModified_otherCompiler,
3009  sourcesDepOnModulesModified, sourcesDepOnModulesModified_otherCompiler);
3010 
3011  findDependentModulesAndSources(modulesRemoved, searchDependencyGraphs, currentEnvironmentDependencyGraph,
3012  modulesDepOnModulesRemoved, modulesDepOnModulesRemoved_otherCompiler,
3013  sourcesDepOnModulesRemoved, sourcesDepOnModulesRemoved_otherCompiler);
3014 
3015  set<Environment *> otherEnvironments;
3016  for (map<Environment *, set<string> >::iterator i = modulesDepOnModulesModified_otherCompiler.begin(); i != modulesDepOnModulesModified_otherCompiler.end(); ++i)
3017  otherEnvironments.insert(i->first);
3018  for (map<Environment *, set<string> >::iterator i = sourcesDepOnModulesModified_otherCompiler.begin(); i != sourcesDepOnModulesModified_otherCompiler.end(); ++i)
3019  otherEnvironments.insert(i->first);
3020  for (map<Environment *, set<string> >::iterator i = modulesDepOnModulesRemoved_otherCompiler.begin(); i != modulesDepOnModulesRemoved_otherCompiler.end(); ++i)
3021  otherEnvironments.insert(i->first);
3022  for (map<Environment *, set<string> >::iterator i = sourcesDepOnModulesRemoved_otherCompiler.begin(); i != sourcesDepOnModulesRemoved_otherCompiler.end(); ++i)
3023  otherEnvironments.insert(i->first);
3024 
3025  for (Environment *env : otherEnvironments)
3026  {
3027  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
3028  string moduleSearchPath = env->getModuleSearchPaths().front();
3029  VuoCompiler *otherCompiler = new VuoCompiler(moduleSearchPath + "/unused");
3030 
3031  dispatch_sync(environmentQueue, ^{
3032  otherCompiler->loadModulesAndSources(set<string>(), modulesDepOnModulesModified_otherCompiler[env], modulesDepOnModulesRemoved_otherCompiler[env],
3033  set<string>(), sourcesDepOnModulesModified_otherCompiler[env], sourcesDepOnModulesRemoved_otherCompiler[env],
3034  false, true, env, nullptr, nullptr, "");
3035  });
3036 
3037  delete otherCompiler;
3038  });
3039  }
3040  }
3041 
3042  // Unload:
3043  // - modules that have been removed or modified
3044  // - modules that depend on them
3045 
3046  map<Environment *, set<VuoCompilerModule *> > actualModulesRemoved;
3047  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3048  {
3049  Environment *env = (*i).at(0);
3050 
3051  set<string> modulesToUnload;
3052  modulesToUnload.insert(modulesRemoved[env].begin(), modulesRemoved[env].end());
3053  modulesToUnload.insert(modulesModified[env].begin(), modulesModified[env].end());
3054  modulesToUnload.insert(modulesDepOnModulesRemoved[env].begin(), modulesDepOnModulesRemoved[env].end());
3055  modulesToUnload.insert(modulesDepOnModulesModified[env].begin(), modulesDepOnModulesModified[env].end());
3056 
3057  actualModulesRemoved[env] = env->unloadCompiledModules(modulesToUnload);
3058 
3059  if (!env->isBuiltIn() && !actualModulesRemoved[env].empty())
3060  {
3061  set<string> actualModulesRemovedKeys;
3062  for (auto m : actualModulesRemoved[env])
3063  actualModulesRemovedKeys.insert(m->getPseudoBase()->getModuleKey());
3064  VUserLog("Removed from %s environment: %s", env->getName().c_str(), VuoStringUtilities::join(actualModulesRemovedKeys, ", ").c_str());
3065  }
3066  }
3067 
3068  // Load:
3069  // - modules that have been added or modified
3070  // - modules that they depend on
3071  // - modules that depend on them that were just unloaded
3072  // Delete:
3073  // - cached module files in `modulesToLoad` whose source files have been removed
3074 
3075  map<Environment *, set<string> > modulesToLoad;
3076  map<Environment *, map<string, string> > modulesToLoadSourceCode;
3077  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3078  {
3079  Environment *env = (*i).at(0);
3080 
3081  if (! modulesAdded[env].empty())
3082  modulesToLoad[env].insert(modulesAdded[env].begin(), modulesAdded[env].end());
3083  if (! modulesModified[env].empty())
3084  modulesToLoad[env].insert(modulesModified[env].begin(), modulesModified[env].end());
3085  if (! modulesDepOnModulesModified[env].empty())
3086  modulesToLoad[env].insert(modulesDepOnModulesModified[env].begin(), modulesDepOnModulesModified[env].end());
3087 
3088  if (env == currentEnvironment && moduleLoadedCallback)
3089  {
3090  if (modulesAdded[env].size() == 1)
3091  modulesToLoadSourceCode[env][*modulesAdded[env].begin()] = moduleAddedOrModifiedSourceCode;
3092  else if (modulesModified[env].size() == 1)
3093  modulesToLoadSourceCode[env][*modulesModified[env].begin()] = moduleAddedOrModifiedSourceCode;
3094  }
3095  }
3096 
3097  map<Environment *, set<VuoCompilerModule *> > actualModulesAdded;
3098  while (! modulesToLoad.empty())
3099  {
3100  set<string> dependenciesToLoad;
3101  map<Environment *, set<string> > potentialSpecializedDependencies;
3102  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3103  {
3104  Environment *env = (*i).at(0);
3105  Environment *genEnv = (*i).at(1);
3106 
3107  set<VuoCompilerModule *> actualModulesLoaded = env->loadCompiledModules(modulesToLoad[env], modulesToLoadSourceCode[env]);
3108 
3109  actualModulesAdded[env].insert(actualModulesLoaded.begin(), actualModulesLoaded.end());
3110  modulesToLoad.erase(env);
3111 
3112  set<string> actualModulesLoadedKeys;
3113  for (set<VuoCompilerModule *>::iterator j = actualModulesLoaded.begin(); j != actualModulesLoaded.end(); ++j)
3114  {
3115  set<string> dependencies = (*j)->getDependencies();
3116  dependenciesToLoad.insert(dependencies.begin(), dependencies.end());
3117  potentialSpecializedDependencies[genEnv].insert(dependencies.begin(), dependencies.end());
3118  actualModulesLoadedKeys.insert((*j)->getPseudoBase()->getModuleKey());
3119  }
3120 
3121  if (!env->isBuiltIn() && !actualModulesLoadedKeys.empty())
3122  VUserLog("Loaded into %s environment: %s", env->getName().c_str(), VuoStringUtilities::join(actualModulesLoadedKeys, ", ").c_str());
3123  }
3124 
3125  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3126  {
3127  Environment *env = (*i).at(0);
3128 
3129  ModuleInfoIterator dependenciesInEnv = env->listModules(dependenciesToLoad);
3130  ModuleInfo *moduleInfo;
3131  while ((moduleInfo = dependenciesInEnv.next()))
3132  {
3133  modulesToLoad[env].insert( moduleInfo->getModuleKey() );
3134 
3135  for (map<Environment *, set<string> >::iterator j = potentialSpecializedDependencies.begin(); j != potentialSpecializedDependencies.end(); ++j)
3136  {
3137  auto foundIter = j->second.find( moduleInfo->getModuleKey() );
3138  if (foundIter != j->second.end())
3139  j->second.erase(foundIter);
3140  }
3141  }
3142  }
3143 
3144  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3145  {
3146  Environment *genEnv = (*i).at(1);
3147  potentialSpecializedModules[genEnv].insert(potentialSpecializedDependencies[genEnv].begin(), potentialSpecializedDependencies[genEnv].end());
3148  }
3149  }
3150 
3151  // Load asynchronously:
3152  // - specializations of generic modules
3153 
3154  set<dispatch_group_t> specializedModulesLoading;
3155  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3156  {
3157  Environment *genEnv = (*i).at(1);
3158  set<dispatch_group_t> s = genEnv->loadSpecializedModules(potentialSpecializedModules[genEnv], this, llvmQueue);
3159  specializedModulesLoading.insert(s.begin(), s.end());
3160  }
3161 
3162  // Notify those waiting on a source file to be compiled that its module has now been loaded.
3163 
3164  if (moduleLoadedCallback)
3165  moduleLoadedCallback();
3166 
3167  // Move modified modules from `actualModulesAdded` and `actualModulesRemoved` to `actualModulesModified`.
3168 
3169  map<Environment *, set< pair<VuoCompilerModule *, VuoCompilerModule *> > > actualModulesModified;
3170  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3171  {
3172  Environment *env = (*i).at(0);
3173 
3174  for (set<VuoCompilerModule *>::iterator add = actualModulesAdded[env].begin(); add != actualModulesAdded[env].end(); )
3175  {
3176  set<VuoCompilerModule *>::iterator rem;
3177  for (rem = actualModulesRemoved[env].begin(); rem != actualModulesRemoved[env].end(); ++rem)
3178  if ((*rem)->getPseudoBase()->getModuleKey() == (*add)->getPseudoBase()->getModuleKey())
3179  break;
3180 
3181  if (rem != actualModulesRemoved[env].end())
3182  {
3183  actualModulesModified[env].insert( make_pair(*rem, *add) );
3184  actualModulesRemoved[env].erase(rem);
3185  actualModulesAdded[env].erase(add++);
3186  }
3187  else
3188  ++add;
3189  }
3190  }
3191 
3192  // Reify port types on node classes (if needed).
3193 
3194  bool wereModulesAddedOrModified = false;
3195  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3196  {
3197  Environment *env = (*i).at(0);
3198  if (! (actualModulesAdded[env].empty() && actualModulesModified[env].empty()) )
3199  {
3200  wereModulesAddedOrModified = true;
3201  break;
3202  }
3203  }
3204 
3205  if (wereModulesAddedOrModified)
3206  {
3207  map<string, VuoCompilerType *> inheritedTypes;
3208  for (const vector<Environment *> &envs : environments)
3209  {
3210  for (Environment *env : envs)
3211  {
3212  env->reifyPortTypes(inheritedTypes);
3213  map<string, VuoCompilerType *> envTypes = env->getTypes();
3214  inheritedTypes.insert(envTypes.begin(), envTypes.end());
3215  }
3216  }
3217  }
3218 
3219  // Delete cached compiled module files and call this function recursively for:
3220  // - modules whose source files have been removed
3221  // - modules whose source files depend on them
3222 
3223  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3224  {
3225  Environment *env = (*i).at(0);
3226 
3227  set<string> sourcesToUnload;
3228  sourcesToUnload.insert(sourcesRemoved[env].begin(), sourcesRemoved[env].end());
3229  sourcesToUnload.insert(sourcesDepOnModulesRemoved[env].begin(), sourcesDepOnModulesRemoved[env].end());
3230  if (! sourcesToUnload.empty())
3231  {
3232  string moduleSearchPath = env->getModuleSearchPaths().front();
3233 
3234  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
3235  VuoCompiler *otherCompiler = new VuoCompiler(moduleSearchPath + "/unused");
3236 
3237  dispatch_sync(environmentQueue, ^{
3238  otherCompiler->loadModulesAndSources(set<string>(), set<string>(), sourcesToUnload,
3239  set<string>(), set<string>(), set<string>(),
3240  false, false, env, nullptr, nullptr, "");
3241  });
3242 
3243  delete otherCompiler;
3244  });
3245  }
3246 
3247  if (!env->isBuiltIn() && !sourcesToUnload.empty())
3248  VUserLog("Deleting from %s environment: %s", env->getName().c_str(), VuoStringUtilities::join(sourcesToUnload, ", ").c_str());
3249 
3250  env->deleteModulesCompiledFromSourceCode(sourcesToUnload);
3251  }
3252 
3253  // Compile asynchronously:
3254  // - source files that have been added or modified
3255  // - source files that depend on them
3256  // - source files that depend on modules that have been modified
3257 
3258  map<Environment *, set<string> > sourcesDepOnModulesAdded;
3259  {
3260  map<Environment *, set<string> > modulesDepOnModulesAdded; // unused
3261  __block map<Environment *, set<string> > modulesDepOnModulesAdded_otherCompiler; //
3262  __block map<Environment *, set<string> > sourcesDepOnModulesAdded_otherCompiler;
3263 
3264  map<Environment *, set<string> > actualModuleKeysAdded;
3265  for (const vector<Environment *> &envs : environments)
3266  {
3267  Environment *env = envs.at(0);
3268  for (VuoCompilerModule *module : actualModulesAdded[env])
3269  actualModuleKeysAdded[env].insert( module->getPseudoBase()->getModuleKey() );
3270  }
3271 
3272  vector<VuoDirectedAcyclicNetwork *> searchDependencyGraphs;
3273  searchDependencyGraphs.push_back(compositionDependencyGraph);
3274  for (map<string, vector<Environment *> >::iterator ii = environmentsForCompositionFamily.begin(); ii != environmentsForCompositionFamily.end(); ++ii)
3275  {
3276  vector< vector<Environment *> > otherEnvs = sharedEnvironments;
3277  otherEnvs.push_back(ii->second);
3278  VuoDirectedAcyclicNetwork *other = makeDependencyNetwork(otherEnvs, ^VuoDirectedAcyclicGraph * (Environment *env) { return env->getCompositionDependencyGraph(); });
3279  searchDependencyGraphs.push_back(other);
3280  }
3281 
3282  VuoDirectedAcyclicGraph *currentEnvironmentDependencyGraph = (currentEnvironment ? currentEnvironment->getCompositionDependencyGraph() : nullptr);
3283 
3284  findDependentModulesAndSources(actualModuleKeysAdded, searchDependencyGraphs, currentEnvironmentDependencyGraph,
3285  modulesDepOnModulesAdded, modulesDepOnModulesAdded_otherCompiler,
3286  sourcesDepOnModulesAdded, sourcesDepOnModulesAdded_otherCompiler);
3287 
3288  set<Environment *> otherEnvironments;
3289  for (map<Environment *, set<string> >::iterator i = sourcesDepOnModulesAdded_otherCompiler.begin(); i != sourcesDepOnModulesAdded_otherCompiler.end(); ++i)
3290  otherEnvironments.insert(i->first);
3291 
3292  for (Environment *env : otherEnvironments)
3293  {
3294  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
3295  string moduleSearchPath = env->getModuleSearchPaths().front();
3296  VuoCompiler *otherCompiler = new VuoCompiler(moduleSearchPath + "/unused");
3297 
3298  dispatch_sync(environmentQueue, ^{
3299  otherCompiler->loadModulesAndSources(set<string>(), set<string>(), set<string>(),
3300  sourcesDepOnModulesAdded_otherCompiler[env], set<string>(), set<string>(),
3301  false, true, env, nullptr, nullptr, "");
3302  });
3303 
3304  delete otherCompiler;
3305  });
3306  }
3307  }
3308 
3309  set<dispatch_group_t> sourcesLoading;
3310  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3311  {
3312  Environment *env = (*i).at(0);
3313 
3314  set<string> sourcesToCompile;
3315  sourcesToCompile.insert(sourcesAdded[env].begin(), sourcesAdded[env].end());
3316  sourcesToCompile.insert(sourcesModified[env].begin(), sourcesModified[env].end());
3317 
3318  if (sourcesToCompile.size() == 0)
3319  continue;
3320 
3321  set<dispatch_group_t> s = env->compileModulesFromSourceCode(sourcesToCompile, shouldRecompileSourcesIfUnchanged);
3322  sourcesLoading.insert(s.begin(), s.end());
3323  }
3324 
3325  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3326  {
3327  Environment *env = (*i).at(0);
3328 
3329  set<string> sourcesToCompile;
3330  sourcesToCompile.insert(sourcesDepOnModulesAdded[env].begin(), sourcesDepOnModulesAdded[env].end());
3331  sourcesToCompile.insert(sourcesDepOnModulesModified[env].begin(), sourcesDepOnModulesModified[env].end());
3332 
3333  if (sourcesToCompile.size() == 0)
3334  continue;
3335 
3336  env->compileModulesFromSourceCode(sourcesToCompile, true);
3337  }
3338 
3339  // Notify compiler delegates.
3340 
3341  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3342  {
3343  Environment *env = (*i).at(0);
3344  env->notifyCompilers(actualModulesAdded[env], actualModulesModified[env], actualModulesRemoved[env], issues[env]);
3345  }
3346 
3347  // Since the dispatch groups for specialized modules are temporary (caller is responsible for releasing them)
3348  // but the dispatch groups for module sources should stay alive as long as the ModuleInfo that contains them,
3349  // retain the dispatch groups for module sources so that all dispatch groups can be safely released by the caller.
3350 
3351  for (const dispatch_group_t &group : sourcesLoading)
3352  dispatch_retain(group);
3353 
3354  set<dispatch_group_t> loadingGroups;
3355  loadingGroups.insert(specializedModulesLoading.begin(), specializedModulesLoading.end());
3356  loadingGroups.insert(sourcesLoading.begin(), sourcesLoading.end());
3357  return loadingGroups;
3358 }
3359 
3371 void VuoCompiler::findDependentModulesAndSources(map<Environment *, set<string> > &changedModules,
3372  const vector<VuoDirectedAcyclicNetwork *> &searchDependencyGraphs,
3373  VuoDirectedAcyclicGraph *currentEnvironmentDependencyGraph,
3374  map<Environment *, set<string> > &modulesDepOnChangedModules_this,
3375  map<Environment *, set<string> > &modulesDepOnChangedModules_other,
3376  map<Environment *, set<string> > &sourcesDepOnChangedModules_this,
3377  map<Environment *, set<string> > &sourcesDepOnChangedModules_other)
3378 {
3379  for (const vector<Environment *> &envs : environments)
3380  {
3381  Environment *env = envs.at(0);
3382 
3383  for (const string &module : changedModules[env])
3384  {
3385  set<VuoDirectedAcyclicGraph::Vertex *> dependents;
3386  for (VuoDirectedAcyclicNetwork *searchDependencyGraph : searchDependencyGraphs)
3387  {
3388  vector<VuoDirectedAcyclicGraph::Vertex *> moduleVertices;
3389  if (currentEnvironmentDependencyGraph)
3390  {
3391  // If a module with the same module key is installed in multiple locations,
3392  // only consider the one being modified or removed.
3393  VuoDirectedAcyclicGraph::Vertex *mv = currentEnvironmentDependencyGraph->findVertex(module);
3394  if (mv)
3395  moduleVertices.push_back(mv);
3396  }
3397  else
3398  moduleVertices = searchDependencyGraph->findVertex(module);
3399 
3400  for (VuoDirectedAcyclicGraph::Vertex *moduleVertexRaw : moduleVertices)
3401  {
3402  DependencyGraphVertex *moduleVertex = static_cast<DependencyGraphVertex *>(moduleVertexRaw);
3403  if (moduleVertex->getEnvironment())
3404  {
3405  vector<VuoDirectedAcyclicGraph::Vertex *> upstream = searchDependencyGraph->getUpstreamVertices(moduleVertex);
3406  dependents.insert(upstream.begin(), upstream.end());
3407  }
3408  }
3409  }
3410 
3411  for (VuoDirectedAcyclicGraph::Vertex *dependentVertexRaw : dependents)
3412  {
3413  DependencyGraphVertex *v = static_cast<DependencyGraphVertex *>(dependentVertexRaw);
3414  Environment *dependentEnv = v->getEnvironment();
3415  if (! dependentEnv)
3416  continue;
3417 
3418  string dependent = v->getDependency();
3419 
3420  // Skip if the dependent module is already being modified/removed in its own right
3421  // (e.g. if the module depends on another in the same node set and the node set is being removed).
3422  if (changedModules[dependentEnv].find(dependent) != changedModules[dependentEnv].end())
3423  continue;
3424 
3425  ModuleInfo *foundSourceInfo = dependentEnv->listSourceFile(dependent);
3426  ModuleInfo *foundModuleInfo = dependentEnv->listModule(dependent);
3427 
3428  bool belongsToCurrentCompiler = false;
3429  for (const vector<Environment *> &envs2 : environments)
3430  {
3431  if (find(envs2.begin(), envs2.end(), dependentEnv) != envs2.end())
3432  {
3433  belongsToCurrentCompiler = true;
3434  break;
3435  }
3436  }
3437 
3438  map<Environment *, set<string> > *whicheverDependents = nullptr;
3439  ModuleInfo *moduleInfo = nullptr;
3440  if (foundSourceInfo)
3441  {
3442  moduleInfo = foundSourceInfo;
3443  whicheverDependents = (belongsToCurrentCompiler ? &sourcesDepOnChangedModules_this : &sourcesDepOnChangedModules_other);
3444  }
3445  else if (foundModuleInfo)
3446  {
3447  moduleInfo = foundModuleInfo;
3448  whicheverDependents = (belongsToCurrentCompiler ? &modulesDepOnChangedModules_this : &modulesDepOnChangedModules_other);
3449  }
3450 
3451  (*whicheverDependents)[dependentEnv].insert(dependent);
3452  moduleInfo->setAttempted(false);
3453  }
3454  }
3455  }
3456 }
3457 
3461 void VuoCompiler::loadedModules(map<string, VuoCompilerModule *> modulesAdded,
3462  map<string, pair<VuoCompilerModule *, VuoCompilerModule *> > modulesModified,
3463  map<string, VuoCompilerModule *> modulesRemoved,
3464  VuoCompilerIssues *issues, void *delegateDataV, Environment *currentEnvironment)
3465 {
3466  //VLog("C=%p %lu %lu %lu", this, modulesAdded.size(), modulesModified.size(), modulesRemoved.size());
3467 
3468  // If a module is added, superseding a version of the same module installed at a broader scope,
3469  // notify this VuoCompiler that the module has been modified rather than added.
3470  //
3471  // If a module is added, but a version of the same module is already installed at a narrower scope,
3472  // don't notify this VuoCompiler, since it will continue to use the version at the narrower scope.
3473  //
3474  // Same idea when a module is modified or removed while another version is installed at a different scope.
3475 
3476  auto findVersionsOfModule = [this, currentEnvironment] (const string &moduleKey)
3477  {
3478  vector< pair<Environment *, VuoCompilerModule *> > moduleVersions;
3479  for (const vector<Environment *> &envs : environments)
3480  {
3481  Environment *env = envs.at(0);
3482  VuoCompilerModule *module = env->findModule(moduleKey);
3483  if (module || env == currentEnvironment)
3484  moduleVersions.push_back( make_pair(env, module) );
3485  }
3486  return moduleVersions;
3487  };
3488 
3489  for (map<string, VuoCompilerModule *>::iterator i = modulesAdded.begin(); i != modulesAdded.end(); )
3490  {
3491  string moduleKey = i->first;
3492  VuoCompilerModule *moduleAdded = i->second;
3493 
3494  vector< pair<Environment *, VuoCompilerModule *> > moduleVersions = findVersionsOfModule(moduleKey);
3495 
3496  if (moduleVersions.size() > 1)
3497  {
3498  modulesAdded.erase(i++);
3499 
3500  if (moduleVersions.back().second == moduleAdded)
3501  {
3502  VuoCompilerModule *moduleSuperseded = moduleVersions.at(moduleVersions.size()-2).second;
3503  modulesModified[moduleKey] = make_pair(moduleSuperseded, moduleAdded);
3504  }
3505  }
3506  else
3507  ++i;
3508  }
3509 
3510  for (map<string, pair<VuoCompilerModule *, VuoCompilerModule *> >::iterator i = modulesModified.begin(); i != modulesModified.end(); )
3511  {
3512  string moduleKey = i->first;
3513  VuoCompilerModule *moduleModified = i->second.second;
3514 
3515  vector< pair<Environment *, VuoCompilerModule *> > moduleVersions = findVersionsOfModule(moduleKey);
3516 
3517  if (moduleVersions.size() > 1 && moduleVersions.back().second != moduleModified)
3518  modulesModified.erase(i++);
3519  else
3520  ++i;
3521  }
3522 
3523  for (map<string, VuoCompilerModule *>::iterator i = modulesRemoved.begin(); i != modulesRemoved.end(); )
3524  {
3525  string moduleKey = i->first;
3526  VuoCompilerModule *moduleRemoved = i->second;
3527 
3528  vector< pair<Environment *, VuoCompilerModule *> > moduleVersions = findVersionsOfModule(moduleKey);
3529 
3530  if (moduleVersions.size() > 1)
3531  {
3532  modulesRemoved.erase(i++);
3533 
3534  if (moduleVersions.back().first == currentEnvironment)
3535  {
3536  VuoCompilerModule *moduleUnsuperseded = moduleVersions.at(moduleVersions.size()-2).second;
3537  modulesModified[moduleKey] = make_pair(moduleRemoved, moduleUnsuperseded);
3538  }
3539  }
3540  else
3541  ++i;
3542  }
3543 
3544  dispatch_async(delegateQueue, ^{
3545  VuoCompilerDelegate::LoadedModulesData *delegateData = static_cast<VuoCompilerDelegate::LoadedModulesData *>(delegateDataV);
3546 
3547  if (delegate && ! (modulesAdded.empty() && modulesModified.empty() && modulesRemoved.empty() && issues->isEmpty()))
3548  {
3549  delegate->enqueueData(delegateData);
3550  delegate->loadedModules(modulesAdded, modulesModified, modulesRemoved, issues);
3551  }
3552  else
3553  {
3554  delegateData->release();
3555  }
3556  });
3557 }
3558 
3564 void VuoCompiler::loadNodeClassGeneratedAtRuntime(VuoCompilerNodeClass *nodeClass, Environment *env)
3565 {
3566  Module *module = nodeClass->getModule();
3567  if (module)
3568  {
3569  dispatch_sync(llvmQueue, ^{
3570  setTargetForModule(nodeClass->getModule());
3571  });
3572  }
3573 
3574  dispatch_sync(environmentQueue, ^{
3575  env->replaceNodeClass(nodeClass);
3576  });
3577 
3578  __block map<string, VuoCompilerType *> inheritedTypes;
3579  void (^envReifyPortTypes)(Environment *) = ^void (Environment *env) {
3580  env->reifyPortTypes(inheritedTypes);
3581  map<string, VuoCompilerType *> currentTypes = env->getTypes();
3582  inheritedTypes.insert(currentTypes.begin(), currentTypes.end());
3583  };
3584  applyToAllEnvironments(envReifyPortTypes);
3585 }
3586 
3590 void VuoCompiler::reifyGenericPortTypes(VuoCompilerComposition *composition)
3591 {
3592  for (VuoCompilerNode *node : composition->getCachedGraph(this)->getNodes())
3593  reifyGenericPortTypes(node->getBase());
3594 
3595  composition->invalidateCachedGraph();
3596 }
3597 
3601 void VuoCompiler::reifyGenericPortTypes(VuoNode *node)
3602 {
3603  VuoCompilerSpecializedNodeClass *nodeClass = dynamic_cast<VuoCompilerSpecializedNodeClass *>(node->getNodeClass()->getCompiler());
3604  if (! nodeClass)
3605  return;
3606 
3607  // Reify any generic types on the node that don't already have a compiler detail.
3608 
3609  vector<VuoPort *> inputPorts = node->getInputPorts();
3610  vector<VuoPort *> outputPorts = node->getOutputPorts();
3611  vector<VuoPort *> ports;
3612  ports.insert(ports.end(), inputPorts.begin(), inputPorts.end());
3613  ports.insert(ports.end(), outputPorts.begin(), outputPorts.end());
3614 
3615  for (vector<VuoPort *>::iterator j = ports.begin(); j != ports.end(); ++j)
3616  {
3617  VuoCompilerPort *port = static_cast<VuoCompilerPort *>((*j)->getCompiler());
3618  VuoGenericType *genericType = dynamic_cast<VuoGenericType *>(port->getDataVuoType());
3619  if (! genericType)
3620  continue;
3621 
3622  if (! genericType->hasCompiler())
3623  {
3624  VuoCompilerType * (^compilerGetType)(string) = ^VuoCompilerType * (string moduleKey) {
3625  return getType(moduleKey);
3626  };
3627 
3628  VuoCompilerGenericType *reifiedType = VuoCompilerGenericType::newGenericType(genericType, compilerGetType);
3629  if (reifiedType)
3630  port->setDataVuoType(reifiedType->getBase());
3631  }
3632  }
3633 
3634  // Update the node class's backing to match the node's backing.
3635 
3636  nodeClass->updateBackingNodeClass(node, this);
3637 }
3638 
3645 void VuoCompiler::compileModule(string inputPath, string outputPath)
3646 {
3647  compileModule(inputPath, outputPath, vector<string>());
3648 }
3649 
3657 void VuoCompiler::compileModule(string inputPath, string outputPath, const vector<string> &includePaths)
3658 {
3659  if (isVerbose)
3660  print();
3661 
3662  vector<string> allIncludePaths = includePaths;
3663  string preprocessedInputPath = inputPath;
3664 
3665  string tmpPreprocessedInputDir;
3666  string dir, file, ext;
3667  VuoFileUtilities::splitPath(inputPath, dir, file, ext);
3669  {
3670  string inputContents = VuoFileUtilities::readFileToString(inputPath);
3671  string preprocessedInputContents = inputContents;
3673  if (inputContents != preprocessedInputContents)
3674  {
3675  // Unspecialized generic node class
3676  allIncludePaths.push_back(dir.empty() ? "." : dir);
3677  tmpPreprocessedInputDir = VuoFileUtilities::makeTmpDir(file);
3678  preprocessedInputPath = tmpPreprocessedInputDir + "/" + file + "." + ext;
3679  VuoFileUtilities::preserveOriginalFileName(preprocessedInputContents, file + "." + ext);
3680  VuoFileUtilities::writeStringToFile(preprocessedInputContents, preprocessedInputPath);
3681  }
3682  }
3683  else if (ext == "fs")
3684  {
3685  VuoFileUtilities::File vuf(dir, file + "." + ext);
3686  VuoModuleCompiler *moduleCompiler = VuoModuleCompiler::newModuleCompiler("isf", getModuleKeyForPath(inputPath), &vuf);
3687  if (moduleCompiler)
3688  {
3689  auto getType = [this] (const string &moduleKey) { return this->getType(moduleKey); };
3690  VuoCompilerIssues *issues = new VuoCompilerIssues();
3691  Module *module = moduleCompiler->compile(getType, llvmQueue, issues);
3692  if (module)
3693  dispatch_sync(llvmQueue, ^{
3694  writeModuleToBitcode(module, outputPath);
3695  });
3696 
3697  if (!issues->isEmpty())
3698  throw VuoCompilerException(issues, true);
3699  delete issues;
3700  }
3701  return;
3702  }
3703 
3704  vector<string> extraArgs;
3705  for (vector<string>::iterator i = allIncludePaths.begin(); i != allIncludePaths.end(); ++i)
3706  {
3707  extraArgs.push_back("-I");
3708  extraArgs.push_back(*i);
3709  }
3710 
3711  string macosxSdkFolder = "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/";
3712  if (VuoFileUtilities::fileExists(macosxSdkFolder + "MacOSX10.10.sdk"))
3713  {
3714  extraArgs.push_back("-isysroot");
3715  extraArgs.push_back(macosxSdkFolder + "MacOSX10.10.sdk");
3716  }
3717 
3718  __block vector<string> headerSearchPaths;
3719  void (^envGetHeaderSearchPaths)(Environment *) = ^void (Environment *env) {
3720  vector<string> result = env->getHeaderSearchPaths();
3721  headerSearchPaths.insert(headerSearchPaths.end(), result.begin(), result.end());
3722  };
3723  applyToInstalledEnvironments(envGetHeaderSearchPaths);
3724 
3725  __block Module *module;
3726  dispatch_sync(llvmQueue, ^{
3727  module = readModuleFromC(preprocessedInputPath, headerSearchPaths, extraArgs);
3728  });
3729  string moduleKey = getModuleKeyForPath(inputPath);
3730  if (! tmpPreprocessedInputDir.empty())
3731  remove(tmpPreprocessedInputDir.c_str());
3732  if (! module)
3733  {
3734  VuoCompilerIssue issue(VuoCompilerIssue::Error, "compiling module", inputPath,
3735  "", "%moduleKey couldn't be compiled as a node class, type, or library. Check the macOS Console for details.");
3736  issue.setModuleKey(moduleKey);
3737  throw VuoCompilerException(issue);
3738  }
3739 
3740  dispatch_sync(llvmQueue, ^{
3741  VuoCompilerModule *compilerModule = VuoCompilerModule::newModule(moduleKey, module, "");
3742  if (! compilerModule)
3743  {
3744  VUserLog("Error: Didn't recognize '%s' as a node class, type, or library.", inputPath.c_str());
3745  return;
3746  }
3747 
3748  setTargetForModule(module, target);
3749  writeModuleToBitcode(module, outputPath);
3750 
3751  delete module;
3752  });
3753 }
3754 
3758 Module * VuoCompiler::compileCompositionToModule(VuoCompilerComposition *composition, const string &moduleKey, bool isTopLevelComposition,
3759  VuoCompilerIssues *issues)
3760 {
3761  composition->check(issues);
3762 
3763  reifyGenericPortTypes(composition);
3764 
3766  isTopLevelComposition,
3767  moduleKey, this);
3768  if (telemetry == "console")
3769  generator->setDebugMode(true);
3770 
3771  __block Module *module;
3772  dispatch_sync(llvmQueue, ^{
3773  module = generator->generateBitcode();
3774  setTargetForModule(module, target);
3775  });
3776 
3777  delete generator;
3778 
3779  return module;
3780 }
3781 
3792 void VuoCompiler::compileComposition(VuoCompilerComposition *composition, string outputPath, bool isTopLevelComposition,
3793  VuoCompilerIssues *issues)
3794 {
3795  string moduleKey = getModuleKeyForPath(outputPath);
3796  Module *module = compileCompositionToModule(composition, moduleKey, isTopLevelComposition, issues);
3797 
3798  dispatch_sync(llvmQueue, ^{
3799  writeModuleToBitcode(module, outputPath);
3800  });
3801 }
3802 
3813 void VuoCompiler::compileComposition(string inputPath, string outputPath, bool isTopLevelComposition,
3814  VuoCompilerIssues *issues)
3815 {
3816  VDebugLog("Compiling '%s'…", inputPath.c_str());
3817  if (isVerbose)
3818  print();
3819 
3821  {
3822  VuoCompilerIssue issue(VuoCompilerIssue::Error, "opening composition", inputPath,
3823  "", "The composition file couldn't be read or was empty.");
3824  throw VuoCompilerException(issue);
3825  }
3826 
3827  try
3828  {
3829  string compositionString = VuoFileUtilities::readFileToString(inputPath);
3830  compileCompositionString(compositionString, outputPath, isTopLevelComposition, issues);
3831  }
3832  catch (VuoCompilerException &e)
3833  {
3834  e.getIssues()->setFilePathIfEmpty(inputPath);
3835  throw;
3836  }
3837 
3838  VDebugLog("Done.");
3839 }
3840 
3851 void VuoCompiler::compileCompositionString(const string &compositionString, string outputPath, bool isTopLevelComposition,
3852  VuoCompilerIssues *issues)
3853 {
3855  compileComposition(composition, outputPath, isTopLevelComposition, issues);
3856 
3857  VuoComposition *baseComposition = composition->getBase();
3858  delete composition;
3859  delete baseComposition;
3860 }
3861 
3865 void VuoCompiler::compileSubcompositionString(const string &compositionString, const string &outputPath,
3866  std::function<void(void)> moduleLoadedCallback, Environment *environment,
3867  VuoCompilerIssues *issues, const string inputPathForIssues)
3868 {
3869  if (! issues)
3870  issues = new VuoCompilerIssues();
3871 
3872  bool compilationSucceeded = false;
3873  try
3874  {
3875  compileCompositionString(compositionString, outputPath, false, issues);
3876  compilationSucceeded = true;
3877  }
3878  catch (VuoCompilerException &e)
3879  {
3880  if (issues != e.getIssues())
3881  issues->append(e.getIssues());
3882  }
3883 
3884  if (! compilationSucceeded)
3885  {
3886  VuoFileUtilities::deleteFile(outputPath);
3887  issues->setFilePathIfEmpty(inputPathForIssues);
3888  }
3889 
3890  string outputDir, file, ext;
3891  VuoFileUtilities::splitPath(outputPath, outputDir, file, ext);
3893 
3894  environment->moduleSearchPathContentsChanged(outputDir, outputPath, compositionString, moduleLoadedCallback, this, issues);
3895 }
3896 
3910 void VuoCompiler::linkCompositionToCreateExecutable(string inputPath, string outputPath, Optimization optimization, string rPath)
3911 {
3912  linkCompositionToCreateExecutableOrDynamicLibrary(inputPath, outputPath, optimization, false, rPath);
3913 }
3914 
3931 void VuoCompiler::linkCompositionToCreateDynamicLibrary(string inputPath, string outputPath, Optimization optimization)
3932 {
3933  linkCompositionToCreateExecutableOrDynamicLibrary(inputPath, outputPath, optimization, true);
3934 }
3935 
3950 void VuoCompiler::linkCompositionToCreateExecutableOrDynamicLibrary(string compiledCompositionPath, string linkedCompositionPath,
3951  Optimization optimization, bool isDylib, string rPath)
3952 {
3953  if (isVerbose)
3954  print();
3955 
3956  if (optimization == Optimization_FastBuildExistingCache)
3957  shouldLoadAllModules = false;
3958 
3959  set<string> dependencies = getDependenciesForComposition(compiledCompositionPath);
3960  dependencies.insert(getRuntimeDependency());
3961  if (! isDylib)
3962  dependencies.insert(getRuntimeMainDependency());
3963 
3964  set<Module *> modules;
3965  set<string> libraries;
3966  set<string> frameworks;
3967  getLinkerInputs(dependencies, optimization, modules, libraries, frameworks);
3968 
3969  libraries.insert(compiledCompositionPath);
3970 
3971  link(linkedCompositionPath, modules, libraries, frameworks, isDylib, rPath);
3972 }
3973 
3988 void VuoCompiler::linkCompositionToCreateDynamicLibraries(string compiledCompositionPath, string linkedCompositionPath,
3989  VuoRunningCompositionLibraries *runningCompositionLibraries)
3990 {
3991  if (isVerbose)
3992  print();
3993 
3994  // Get the dependencies used by the new resources and not the previous resources.
3995 
3996  set<string> carriedOverDependencies = runningCompositionLibraries->getDependenciesLoaded();
3997  set<string> allDependencies = getDependenciesForComposition(compiledCompositionPath);
3998  set<string> addedDependencies;
3999  std::set_difference(allDependencies.begin(), allDependencies.end(),
4000  carriedOverDependencies.begin(), carriedOverDependencies.end(),
4001  std::inserter(addedDependencies, addedDependencies.end()));
4002 
4003  // Get the libraries and frameworks used by the previous resources.
4004 
4005  vector<string> carriedOverNonUnloadableLibraries = runningCompositionLibraries->getNonUnloadableLibrariesLoaded();
4006  vector<string> carriedOverUnloadableLibraries = runningCompositionLibraries->getUnloadableLibrariesLoaded();
4007  set<string> carriedOverExternalLibraries = runningCompositionLibraries->getExternalLibraries();
4008  set<string> carriedOverFrameworks = runningCompositionLibraries->getExternalFrameworks();
4009 
4010  // Link the new resource dylibs, if needed.
4011 
4012  string nonUnloadableResourcePath;
4013  string unloadableResourcePath;
4014  set<string> nonUnloadableDependencies;
4015  set<string> unloadableDependencies;
4016  map<string, set<string> > builtInCacheDependencies;
4017  map<string, set<string> > userCacheDependencies;
4018  set<string> builtInLibraries;
4019  set<string> userLibraries;
4020  set<string> addedExternalLibraries;
4021  set<string> addedFrameworks;
4022  set<string> allFrameworks;
4023  if (! addedDependencies.empty())
4024  {
4025  set<string> builtInModuleAndLibraryDependencies;
4026  set<string> userModuleAndLibraryDependencies;
4027  set<Module *> builtInModules;
4028  set<Module *> userModules;
4029 
4030  getLinkerInputs(addedDependencies, Optimization_FastBuild,
4031  builtInModuleAndLibraryDependencies, userModuleAndLibraryDependencies, builtInCacheDependencies, userCacheDependencies,
4032  builtInModules, userModules, builtInLibraries, userLibraries, addedExternalLibraries, addedFrameworks);
4033 
4034  allFrameworks.insert(carriedOverFrameworks.begin(), carriedOverFrameworks.end());
4035  allFrameworks.insert(addedFrameworks.begin(), addedFrameworks.end());
4036 
4037  string dir, linkedCompositionFile, ext;
4038  VuoFileUtilities::splitPath(linkedCompositionPath, dir, linkedCompositionFile, ext);
4039 
4040  if (! builtInModules.empty() || builtInLibraries.size() > builtInCacheDependencies.size())
4041  {
4042  nonUnloadableResourcePath = VuoFileUtilities::makeTmpFile(linkedCompositionFile + "-resource", "dylib");
4043  nonUnloadableDependencies = builtInModuleAndLibraryDependencies;
4044 
4045  set<string> librariesForNonUnloadableResource = builtInLibraries;
4046  librariesForNonUnloadableResource.insert(carriedOverNonUnloadableLibraries.begin(), carriedOverNonUnloadableLibraries.end());
4047  librariesForNonUnloadableResource.insert(carriedOverExternalLibraries.begin(), carriedOverExternalLibraries.end());
4048  librariesForNonUnloadableResource.insert(addedExternalLibraries.begin(), addedExternalLibraries.end());
4049 
4050  link(nonUnloadableResourcePath, builtInModules, librariesForNonUnloadableResource, allFrameworks, true);\
4051 
4052  for (set<string>::iterator i = builtInLibraries.begin(); i != builtInLibraries.end(); )
4053  {
4054  if (! VuoStringUtilities::endsWith(*i, ".dylib"))
4055  builtInLibraries.erase(i++);
4056  else
4057  i++;
4058  }
4059 
4060  for (set<string>::iterator i = addedExternalLibraries.begin(); i != addedExternalLibraries.end(); )
4061  {
4062  if (! VuoStringUtilities::endsWith(*i, ".dylib"))
4063  addedExternalLibraries.erase(i++);
4064  else
4065  i++;
4066  }
4067  }
4068 
4069  if (! userModules.empty() || userLibraries.size() > userCacheDependencies.size())
4070  {
4071  unloadableResourcePath = VuoFileUtilities::makeTmpFile(linkedCompositionFile + "-resource", "dylib");
4072  unloadableDependencies = userModuleAndLibraryDependencies;
4073 
4074  set<string> librariesForUnloadableResource = userLibraries;
4075  librariesForUnloadableResource.insert(builtInLibraries.begin(), builtInLibraries.end());
4076  librariesForUnloadableResource.insert(carriedOverUnloadableLibraries.begin(), carriedOverUnloadableLibraries.end());
4077  librariesForUnloadableResource.insert(carriedOverNonUnloadableLibraries.begin(), carriedOverNonUnloadableLibraries.end());
4078  librariesForUnloadableResource.insert(carriedOverExternalLibraries.begin(), carriedOverExternalLibraries.end());
4079  librariesForUnloadableResource.insert(addedExternalLibraries.begin(), addedExternalLibraries.end());
4080  if (! nonUnloadableResourcePath.empty())
4081  librariesForUnloadableResource.insert(nonUnloadableResourcePath);
4082 
4083  link(unloadableResourcePath, userModules, librariesForUnloadableResource, allFrameworks, true);
4084 
4085  for (set<string>::iterator i = userLibraries.begin(); i != userLibraries.end(); )
4086  {
4087  if (! VuoStringUtilities::endsWith(*i, ".dylib"))
4088  userLibraries.erase(i++);
4089  else
4090  i++;
4091  }
4092  }
4093  }
4094 
4095  // Get the Vuo runtime dependency.
4096 
4097  set<string> vuoRuntimePaths;
4098  {
4099  set<Module *> modules;
4100  set<string> libraries;
4101  set<string> frameworks;
4102 
4103  set<string> dependencies;
4104  dependencies.insert(getRuntimeDependency());
4105  getLinkerInputs(dependencies, Optimization_FastBuild, modules, libraries, frameworks);
4106  vuoRuntimePaths = libraries;
4107  }
4108 
4109  // Link the composition.
4110 
4111  {
4112  set<Module *> modules;
4113  set<string> libraries;
4114 
4115  libraries.insert(compiledCompositionPath);
4116  libraries.insert(carriedOverExternalLibraries.begin(), carriedOverExternalLibraries.end());
4117  libraries.insert(addedExternalLibraries.begin(), addedExternalLibraries.end());
4118  libraries.insert(carriedOverNonUnloadableLibraries.begin(), carriedOverNonUnloadableLibraries.end());
4119  libraries.insert(carriedOverUnloadableLibraries.begin(), carriedOverUnloadableLibraries.end());
4120  libraries.insert(builtInLibraries.begin(), builtInLibraries.end());
4121  libraries.insert(userLibraries.begin(), userLibraries.end());
4122  if (! nonUnloadableResourcePath.empty())
4123  libraries.insert(nonUnloadableResourcePath);
4124  if (! unloadableResourcePath.empty())
4125  libraries.insert(unloadableResourcePath);
4126  libraries.insert(vuoRuntimePaths.begin(), vuoRuntimePaths.end());
4127  link(linkedCompositionPath, modules, libraries, allFrameworks, true);
4128  }
4129 
4130  // Now that we're past the point where an exception can be thrown, update the RunningCompositionLibraries.
4131 
4132  if (! nonUnloadableResourcePath.empty())
4133  runningCompositionLibraries->enqueueResourceLibraryToLoad(nonUnloadableResourcePath, nonUnloadableDependencies, false);
4134 
4135  if (! unloadableResourcePath.empty())
4136  runningCompositionLibraries->enqueueResourceLibraryToLoad(unloadableResourcePath, unloadableDependencies, true);
4137 
4138  for (map<string, set<string> >::iterator i = builtInCacheDependencies.begin(); i != builtInCacheDependencies.end(); ++i)
4139  runningCompositionLibraries->enqueueCacheLibraryToLoad(i->first, i->second, false);
4140 
4141  for (map<string, set<string> >::iterator i = userCacheDependencies.begin(); i != userCacheDependencies.end(); ++i)
4142  runningCompositionLibraries->enqueueCacheLibraryToLoad(i->first, i->second, true);
4143 
4144  runningCompositionLibraries->addExternalFrameworks(addedFrameworks);
4145  runningCompositionLibraries->addExternalLibraries(addedExternalLibraries);
4146 }
4147 
4154 set<string> VuoCompiler::getDependenciesForComposition(const string &compiledCompositionPath)
4155 {
4156  VDebugLog("Gathering dependencies for '%s'…", compiledCompositionPath.c_str());
4157 
4158  // Add the node classes in the top-level composition and their dependencies.
4159  __block set<string> directDependencies;
4160  string moduleKey = getModuleKeyForPath(compiledCompositionPath);
4161  Module *module = readModuleFromBitcode(compiledCompositionPath);
4162  dispatch_sync(llvmQueue, ^{
4163  VuoCompilerModule *compilerModule = VuoCompilerModule::newModule(moduleKey, module, "");
4164  directDependencies = compilerModule->getDependencies();
4165  delete compilerModule;
4166  delete module;
4167  });
4168 
4169  try
4170  {
4171  auto deps = getDependenciesForComposition(directDependencies, true);
4172  VDebugLog("Done.");
4173  return deps;
4174  }
4175  catch (VuoCompilerException &e)
4176  {
4177  e.getIssues()->setFilePathIfEmpty(compiledCompositionPath);
4178  throw;
4179  }
4180 }
4181 
4189 {
4190  set<string> directDependencies;
4191 
4192  set<VuoCompilerNode *> nodes = composition->getCachedGraph(this)->getNodes();
4193  for (VuoCompilerNode *node : nodes)
4194  if (node->getBase()->getNodeClass()->hasCompiler())
4195  directDependencies.insert( node->getBase()->getNodeClass()->getCompiler()->getDependencyName() );
4196 
4197  vector<VuoPublishedPort *> publishedInputPorts = composition->getBase()->getPublishedInputPorts();
4198  vector<VuoPublishedPort *> publishedOutputPorts = composition->getBase()->getPublishedOutputPorts();
4199  vector<VuoPublishedPort *> publishedPorts;
4200  publishedPorts.insert(publishedPorts.end(), publishedInputPorts.begin(), publishedInputPorts.end());
4201  publishedPorts.insert(publishedPorts.end(), publishedOutputPorts.begin(), publishedOutputPorts.end());
4202  for (VuoPublishedPort *publishedPort : publishedPorts)
4203  {
4204  if (publishedPort->getClass()->hasCompiler())
4205  {
4206  VuoType *portType = static_cast<VuoCompilerPortClass *>( publishedPort->getClass()->getCompiler() )->getDataVuoType();
4207  if (portType)
4208  {
4209  string dependency;
4210  VuoGenericType *genericType = dynamic_cast<VuoGenericType *>(portType);
4211  if (genericType)
4212  {
4213  VuoGenericType::Compatibility compatibility;
4214  vector<string> compatibleTypeNames = genericType->getCompatibleSpecializedTypes(compatibility);
4215  dependency = VuoCompilerGenericType::chooseBackingTypeName(portType->getModuleKey(), compatibleTypeNames);
4216  }
4217  else
4218  dependency = portType->getModuleKey();
4219 
4220  directDependencies.insert(dependency);
4221  }
4222  }
4223  }
4224 
4225  return directDependencies;
4226 }
4227 
4234 set<string> VuoCompiler::getDependenciesForComposition(VuoCompilerComposition *composition)
4235 {
4236  set<string> directDependencies = getDirectDependenciesForComposition(composition);
4237  return getDependenciesForComposition(directDependencies, false);
4238 }
4239 
4246 {
4247  __block vector<string> librarySearchPaths;
4248  applyToInstalledEnvironments(^void (Environment *env) {
4249  vector<string> result = env->getLibrarySearchPaths();
4250  librarySearchPaths.insert(librarySearchPaths.end(), result.begin(), result.end());
4251  });
4252 
4253  set<string> dylibDeps;
4254  for (string dep : getDependenciesForComposition(composition))
4255  {
4256  string path = getLibraryPath(dep, librarySearchPaths);
4257  if (VuoStringUtilities::endsWith(path, ".dylib"))
4258  dylibDeps.insert(path);
4259  }
4260 
4261  return dylibDeps;
4262 }
4263 
4277 set<string> VuoCompiler::getDependenciesForComposition(const set<string> &directDependencies, bool checkCompatibility)
4278 {
4279  // Make sure that any compiler-generated node classes have been generated and added to the dependency graph.
4280  for (set<string>::const_iterator i = directDependencies.begin(); i != directDependencies.end(); ++i)
4281  getNodeClass(*i);
4282 
4283  set<string> dependencies;
4284  for (set<string>::iterator i = directDependencies.begin(); i != directDependencies.end(); ++i)
4285  {
4286  string moduleKey = *i;
4287 
4288  // First pass: Find all dependencies of the direct dependency that have been loaded so far.
4289  vector<VuoDirectedAcyclicGraph::Vertex *> firstPassVertices = dependencyGraph->findVertex(moduleKey);
4290  set<VuoDirectedAcyclicGraph::Vertex *> firstPassDependencies(firstPassVertices.begin(), firstPassVertices.end());
4291  for (vector<VuoDirectedAcyclicGraph::Vertex *>::iterator j = firstPassVertices.begin(); j != firstPassVertices.end(); ++j)
4292  {
4293  vector<VuoDirectedAcyclicGraph::Vertex *> downstream = dependencyGraph->getDownstreamVertices(*j);
4294  firstPassDependencies.insert(downstream.begin(), downstream.end());
4295  }
4296 
4297  // Make sure that any compiler-generated node classes in subcompositions have been generated and added to the dependency graph.
4298  for (set<VuoDirectedAcyclicGraph::Vertex *>::iterator j = firstPassDependencies.begin(); j != firstPassDependencies.end(); ++j)
4299  {
4300  DependencyGraphVertex *v = static_cast<DependencyGraphVertex *>(*j);
4301  getNodeClass(v->getDependency());
4302  }
4303 
4304  // Second pass: Find all dependencies of the direct dependency, this time including dependencies of the node classes just generated.
4305  vector<VuoDirectedAcyclicGraph::Vertex *> moduleVertices = dependencyGraph->findVertex(moduleKey);
4306  set<VuoDirectedAcyclicGraph::Vertex *> moduleDependencies(moduleVertices.begin(), moduleVertices.end());
4307  for (vector<VuoDirectedAcyclicGraph::Vertex *>::iterator j = moduleVertices.begin(); j != moduleVertices.end(); ++j)
4308  {
4309  vector<VuoDirectedAcyclicGraph::Vertex *> downstream = dependencyGraph->getDownstreamVertices(*j);
4310  moduleDependencies.insert(downstream.begin(), downstream.end());
4311  }
4312 
4313  // Sort the direct dependency and all of its dependencies into those that are and are not compatible.
4314  set<string> dependenciesToAdd;
4315  set<string> incompatibleDependencies;
4316  for (set<VuoDirectedAcyclicGraph::Vertex *>::iterator j = moduleDependencies.begin(); j != moduleDependencies.end(); ++j)
4317  {
4318  DependencyGraphVertex *v = static_cast<DependencyGraphVertex *>(*j);
4319  if (! checkCompatibility || ! v->getEnvironment() || v->isCompatible())
4320  dependenciesToAdd.insert(v->getDependency());
4321  else
4322  incompatibleDependencies.insert(v->getDependency());
4323  }
4324 
4325  if (! checkCompatibility || incompatibleDependencies.empty())
4326  {
4327  dependencies.insert(dependenciesToAdd.begin(), dependenciesToAdd.end());
4328  }
4329  else
4330  {
4331  VuoCompilerTargetSet compositionTargets;
4332  compositionTargets.restrictToCurrentOperatingSystemVersion();
4333 
4334  string dependencyTargetString;
4335  VuoCompilerModule *module = getModule(moduleKey);
4336  if (module)
4337  {
4338  VuoCompilerTargetSet dependencyTargets = module->getCompatibleTargets();
4339  for (set<string>::iterator i = incompatibleDependencies.begin(); i != incompatibleDependencies.end(); ++i)
4340  {
4341  VuoCompilerModule *subModule = getModule(*i);
4342  if (subModule)
4343  {
4344  VuoCompilerTargetSet subDependencyTargets = subModule->getCompatibleTargets();
4345  dependencyTargets.restrictToBeCompatibleWithAllOf(subDependencyTargets);
4346  }
4347  }
4348  dependencyTargetString = dependencyTargets.toString();
4349  }
4350  else
4351  dependencyTargetString = "(unknown operating systems)";
4352 
4353  string modulePlaceholder = (module ? "%module" : "%moduleKey");
4354  VuoCompilerIssue issue(VuoCompilerIssue::Error, "linking composition", "",
4355  "Node incompatible with operating system",
4356  modulePlaceholder + " is only compatible with " + dependencyTargetString +
4357  ", so this composition can't run on your macOS version (" + compositionTargets.toString() + ").");
4358  issue.setModule(module->getPseudoBase());
4359  issue.setModuleKey(moduleKey);
4360  throw VuoCompilerException(issue);
4361  }
4362  }
4363 
4364  // Add the libraries needed by every linked composition.
4365  vector<string> coreDependencies = getCoreVuoDependencies();
4366  dependencies.insert(coreDependencies.begin(), coreDependencies.end());
4367 
4368  return dependencies;
4369 }
4370 
4375 void VuoCompiler::getLinkerInputs(const set<string> &dependencies, Optimization optimization,
4376  set<Module *> &modules, set<string> &libraries, set<string> &frameworks)
4377 {
4378  set<string> builtInModuleAndLibraryDependencies;
4379  set<string> userModuleAndLibraryDependencies;
4380  map<string, set<string> > builtInCacheDependencies;
4381  map<string, set<string> > userCacheDependencies;
4382  set<Module *> builtInModules;
4383  set<Module *> userModules;
4384  set<string> builtInLibraries;
4385  set<string> userLibraries;
4386  set<string> externalLibraries;
4387 
4388  getLinkerInputs(dependencies, optimization,
4389  builtInModuleAndLibraryDependencies, userModuleAndLibraryDependencies, builtInCacheDependencies, userCacheDependencies,
4390  builtInModules, userModules, builtInLibraries, userLibraries, externalLibraries, frameworks);
4391 
4392  modules.insert(builtInModules.begin(), builtInModules.end());
4393  modules.insert(userModules.begin(), userModules.end());
4394  libraries.insert(builtInLibraries.begin(), builtInLibraries.end());
4395  libraries.insert(userLibraries.begin(), userLibraries.end());
4396  libraries.insert(externalLibraries.begin(), externalLibraries.end());
4397 }
4398 
4412 void VuoCompiler::getLinkerInputs(const set<string> &dependencies, Optimization optimization,
4413  set<string> &builtInModuleAndLibraryDependencies, set<string> &userModuleAndLibraryDependencies,
4414  map<string, set<string> > &builtInCacheDependencies, map<string, set<string> > &userCacheDependencies,
4415  set<Module *> &builtInModules, set<Module *> &userModules,
4416  set<string> &builtInLibraries, set<string> &userLibraries,
4417  set<string> &externalLibraries, set<string> &externalFrameworks)
4418 {
4419  bool shouldUseModuleCache = (optimization == Optimization_FastBuild || optimization == Optimization_FastBuildExistingCache);
4420  if (shouldUseModuleCache)
4421  useModuleCache(true, optimization == Optimization_FastBuildExistingCache);
4422 
4423  __block vector<string> librarySearchPaths;
4424  void (^envGetLibrarySearchPaths)(Environment *) = ^void (Environment *env) {
4425  vector<string> result = env->getLibrarySearchPaths();
4426  librarySearchPaths.insert(librarySearchPaths.end(), result.begin(), result.end());
4427  };
4428  applyToInstalledEnvironments(envGetLibrarySearchPaths);
4429 
4430  for (set<string>::iterator i = dependencies.begin(); i != dependencies.end(); ++i)
4431  {
4432  string dependency = *i;
4433 
4434  bool foundInCache = false;
4435  string moduleCachePath;
4436  bool isInBuiltInModuleCache = false;
4437  if (shouldUseModuleCache)
4438  foundInCache = findInModuleCache(dependency, moduleCachePath, isInBuiltInModuleCache);
4439 
4440  if (foundInCache)
4441  {
4442  if (isInBuiltInModuleCache)
4443  {
4444  builtInLibraries.insert(moduleCachePath);
4445  builtInCacheDependencies[moduleCachePath].insert(dependency);
4446  }
4447  else
4448  {
4449  userLibraries.insert(moduleCachePath);
4450  userCacheDependencies[moduleCachePath].insert(dependency);
4451  }
4452  }
4453  else
4454  {
4455  __block VuoCompilerModule *module = NULL;
4456  void (^envFindModule)(Environment *) = ^void (Environment *env) {
4457  VuoCompilerModule *result = env->findModule(dependency);
4458  if (result)
4459  module = result;
4460  };
4461  applyToAllEnvironments(envFindModule);
4462 
4463  if (module)
4464  {
4465  if (module->getModule()) // Skip not-fully-specialized generic modules when building the module cache.
4466  {
4467  string modulePath = module->getModulePath();
4468  if (! modulePath.empty() && dynamic_cast<VuoCompilerType *>(module))
4469  {
4470  if (module->isBuiltIn())
4471  builtInLibraries.insert(modulePath);
4472  else
4473  userLibraries.insert(modulePath);
4474  }
4475  else
4476  {
4477  if (module->isBuiltIn())
4478  builtInModules.insert(module->getModule());
4479  else
4480  userModules.insert(module->getModule());
4481  }
4482 
4483  if (module->isBuiltIn())
4484  builtInModuleAndLibraryDependencies.insert(dependency);
4485  else
4486  userModuleAndLibraryDependencies.insert(dependency);
4487  }
4488  }
4489  else
4490  {
4491  if (VuoStringUtilities::endsWith(dependency, ".framework"))
4492  externalFrameworks.insert(dependency);
4493  else
4494  {
4495  string dependencyPath = getLibraryPath(dependency, librarySearchPaths);
4496  if (! dependencyPath.empty())
4497  externalLibraries.insert(dependencyPath);
4498  else
4499  VUserLog("Warning: Could not locate dependency '%s'.", dependency.c_str());
4500  }
4501  }
4502  }
4503  }
4504 }
4505 
4511 string VuoCompiler::getLibraryPath(const string &dependency, vector<string> librarySearchPaths)
4512 {
4513  if (dependency[0] == '/' && VuoFileUtilities::fileExists(dependency))
4514  return dependency;
4515 
4516  // Put the system library folder last in the list — prefer to use the libraries bundled in Vuo.framework.
4517  // Don't attempt to use OpenSSL from the system library folder, since macOS 10.15 prevents linking to it.
4518  if (dependency != "crypto"
4519  && dependency != "ssl")
4520  librarySearchPaths.push_back("/usr/lib");
4521 
4522  for (auto &path : librarySearchPaths)
4523  {
4524  vector<string> variations;
4525  variations.push_back(path + "/" + dependency);
4526  variations.push_back(path + "/lib" + dependency);
4527  variations.push_back(path + "/lib" + dependency + ".dylib");
4528  variations.push_back(path + "/lib" + dependency + ".a");
4529  for (auto &variation : variations)
4530  if (VuoFileUtilities::fileExists(variation))
4531  return variation;
4532  }
4533 
4534  return "";
4535 }
4536 
4544 void VuoCompiler::useModuleCache(bool shouldUseExistingBuiltInCaches, bool shouldUseExistingOtherCaches)
4545 {
4546  loadModulesIfNeeded();
4547 
4548  // Iterate through the environments in the order that the caches need to be built.
4549 
4550  dispatch_sync(environmentQueue, ^{
4551  set<string> dylibsForCachesOfInstalledModules;
4552  set<string> frameworksForCachesOfInstalledModules;
4553  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
4554  {
4555  bool builtin = (i == environments.begin());
4556  set<string> dylibsForCacheOfGeneratedModules;
4557  set<string> frameworksForCacheOfGeneratedModules;
4558 
4559  for (int j = i->size() - 1; j >= 0; --j)
4560  {
4561  Environment *env = i->at(j);
4562  bool installed = (j == 0);
4563 
4564  set<string> cacheableModulesAndDependencies;
4565  set<string> dylibsNeededToLinkToThisCache;
4566  set<string> frameworksNeededToLinkToThisCache;
4567  env->getCacheableModulesAndDependencies(builtin, installed, cacheableModulesAndDependencies,
4568  dylibsNeededToLinkToThisCache, frameworksNeededToLinkToThisCache);
4569 
4570  set<string> accumulatedDylibs;
4571  accumulatedDylibs.insert(dylibsNeededToLinkToThisCache.begin(), dylibsNeededToLinkToThisCache.end());
4572  accumulatedDylibs.insert(dylibsForCachesOfInstalledModules.begin(), dylibsForCachesOfInstalledModules.end());
4573  accumulatedDylibs.insert(dylibsForCacheOfGeneratedModules.begin(), dylibsForCacheOfGeneratedModules.end());
4574 
4575  set<string> accumulatedFrameworks;
4576  accumulatedFrameworks.insert(frameworksNeededToLinkToThisCache.begin(), frameworksNeededToLinkToThisCache.end());
4577  accumulatedFrameworks.insert(frameworksForCachesOfInstalledModules.begin(), frameworksForCachesOfInstalledModules.end());
4578  accumulatedFrameworks.insert(frameworksForCacheOfGeneratedModules.begin(), frameworksForCacheOfGeneratedModules.end());
4579 
4580  bool shouldUseExistingCache = (builtin ? shouldUseExistingBuiltInCaches : shouldUseExistingOtherCaches);
4581  env->useModuleCache(shouldUseExistingCache, this, cacheableModulesAndDependencies,
4582  accumulatedDylibs, accumulatedFrameworks);
4583 
4584  if (installed)
4585  {
4586  dylibsForCachesOfInstalledModules.insert(dylibsNeededToLinkToThisCache.begin(), dylibsNeededToLinkToThisCache.end());
4587  frameworksForCachesOfInstalledModules.insert(frameworksNeededToLinkToThisCache.begin(), frameworksNeededToLinkToThisCache.end());
4588  }
4589  else
4590  {
4591  dylibsForCacheOfGeneratedModules.insert(dylibsNeededToLinkToThisCache.begin(), dylibsNeededToLinkToThisCache.end());
4592  frameworksForCacheOfGeneratedModules.insert(frameworksNeededToLinkToThisCache.begin(), frameworksNeededToLinkToThisCache.end());
4593  }
4594  }
4595  }
4596  });
4597 
4598  Environment::waitForModuleCachesToBuild();
4599 }
4600 
4609 bool VuoCompiler::findInModuleCache(const string &moduleOrDependency, string &cachePath, bool &isBuiltinCache)
4610 {
4611  __block bool found = false;
4612  __block string outPath;
4613  __block bool outBuiltin;
4614  dispatch_sync(environmentQueue, ^{
4615  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
4616  {
4617  bool builtin = (i == environments.begin());
4618 
4619  for (int j = i->size() - 1; j >= 0; --j)
4620  {
4621  Environment *env = i->at(j);
4622 
4623  string resultPath;
4624  bool resultFound = env->findInModuleCache(moduleOrDependency, resultPath);
4625  if (resultFound)
4626  {
4627  found = true;
4628  outPath = resultPath;
4629  outBuiltin = builtin;
4630  }
4631  }
4632  }
4633  });
4634 
4635  cachePath = outPath;
4636  isBuiltinCache = outBuiltin;
4637  return found;
4638 }
4639 
4648 {
4649  dispatch_group_async(moduleCacheBuilding, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
4650  useModuleCache(true, false);
4651  });
4652 }
4653 
4666 void VuoCompiler::generateBuiltInModuleCaches(const string &vuoFrameworkPath)
4667 {
4668  vuoFrameworkInProgressPath = vuoFrameworkPath;
4669 
4670  VuoCompiler compiler;
4671  compiler.useModuleCache(false, true);
4672 }
4673 
4682 {
4683  unsigned long maxSeconds = 30 * 24 * 60 * 60; // 30 days
4684 
4685  set<VuoFileUtilities::File *> cacheDirs = VuoFileUtilities::findAllFilesInDirectory(VuoFileUtilities::getCachePath());
4686  for (set<VuoFileUtilities::File *>::iterator i = cacheDirs.begin(); i != cacheDirs.end(); ++i)
4687  {
4688  string path = (*i)->path();
4689 
4690  string file = (*i)->basename();
4691  if (file != "Builtin" && file != "System" && file != "User")
4692  {
4693  unsigned long fileSeconds = VuoFileUtilities::getSecondsSinceFileLastAccessed(path);
4694  if (fileSeconds > maxSeconds)
4696  }
4697 
4698  if (VuoStringUtilities::beginsWith(file, Environment::pidCacheDirPrefix))
4699  {
4700  string pidAsString = file.substr(Environment::pidCacheDirPrefix.length());
4701  int pid = atoi(pidAsString.c_str());
4702  if (kill(pid, 0) != 0) // no running process has this pid
4704  }
4705 
4706  delete *i;
4707  }
4708 }
4709 
4714 void VuoCompiler::setLoadAllModules(bool shouldLoadAllModules)
4715 {
4716  dispatch_sync(modulesToLoadQueue, ^{
4717  this->shouldLoadAllModules = shouldLoadAllModules;
4718  });
4719 }
4720 
4732 void VuoCompiler::link(string outputPath, const set<Module *> &modules, const set<string> &libraries, const set<string> &frameworks, bool isDylib, string rPath)
4733 {
4734  VDebugLog("Linking '%s'…", outputPath.c_str());
4735  // https://stackoverflow.com/questions/11657529/how-to-generate-an-executable-from-an-llvmmodule
4736 
4737 
4738  // Write all the modules with renamed symbols to a composite module file (since the linker can't operate on in-memory modules).
4739  string compositeModulePath = VuoFileUtilities::makeTmpFile("composite", "bc");
4740  dispatch_sync(llvmQueue, ^{
4741  double t0 = VuoLogGetTime();
4742  Module *compositeModule = new Module("composite", getGlobalContext());
4743  setTargetForModule(compositeModule);
4744  for (set<Module *>::const_iterator i = modules.begin(); i != modules.end(); ++i)
4745  {
4746  string error;
4747  if (Linker::LinkModules(compositeModule, *i, Linker::PreserveSource, &error))
4748  VUserLog("Error: Failed to link compositeModule: %s", error.c_str());
4749  }
4750  writeModuleToBitcode(compositeModule, compositeModulePath);
4751  delete compositeModule;
4752  VDebugLog("\tLinkModules took %5.2fs", VuoLogGetTime() - t0);
4753  });
4754 
4755 
4756  // llvm-3.1/llvm/tools/clang/tools/driver/driver.cpp
4757 
4758  llvm::sys::Path clangPath = getClangPath();
4759 
4760  vector<const char *> args;
4761  vector<char *> argsToFree;
4762  args.push_back(clangPath.c_str());
4763 
4764  args.push_back(compositeModulePath.c_str());
4765 
4766  vector<string> coreDependencies = getCoreVuoDependencies();
4767  for (set<string>::const_iterator i = libraries.begin(); i != libraries.end(); ++i)
4768  {
4769  string library = *i;
4770 
4771  for (vector<string>::iterator j = coreDependencies.begin(); j != coreDependencies.end(); ++j)
4772  {
4773  string coreDependency = *j;
4774  if (VuoStringUtilities::endsWith(library, "lib" + coreDependency + ".a"))
4775  args.push_back("-force_load"); // Load all symbols of static core dependencies, not just those used in the objects.
4776  }
4777 
4778  // Use the pre-built native object file if it exists (faster than converting .bc to .o every build).
4779  if (VuoStringUtilities::endsWith(library, ".bc"))
4780  {
4781  string libraryObject = VuoStringUtilities::substrBefore(library, ".bc") + ".o";
4782  if (VuoFileUtilities::fileExists(libraryObject))
4783  library = libraryObject;
4784  }
4785 
4786  char *libraryZ = strdup(library.c_str());
4787  args.push_back(libraryZ);
4788  argsToFree.push_back(libraryZ);
4789  }
4790 
4791  // Add framework search paths
4792  vector<string> frameworkArguments;
4793 
4794  __block vector<string> frameworkSearchPaths;
4795  void (^envGetFrameworkSearchPaths)(Environment *) = ^void (Environment *env) {
4796  vector<string> result = env->getFrameworkSearchPaths();
4797  frameworkSearchPaths.insert(frameworkSearchPaths.end(), result.begin(), result.end());
4798  };
4799  applyToInstalledEnvironments(envGetFrameworkSearchPaths);
4800 
4801  for (vector<string>::const_iterator i = frameworkSearchPaths.begin(); i != frameworkSearchPaths.end(); ++i)
4802  {
4803  string a = "-F"+*i;
4804  // 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.
4805  frameworkArguments.push_back(a);
4806  char *frameworkArgument = strdup(a.c_str());
4807  args.push_back(frameworkArgument);
4808  argsToFree.push_back(frameworkArgument);
4809  }
4810 
4811  for (set<string>::const_iterator i = frameworks.begin(); i != frameworks.end(); ++i)
4812  {
4813  args.push_back("-framework");
4814 
4815  string frameworkName = *i;
4816  frameworkName = frameworkName.substr(0, frameworkName.length() - string(".framework").length());
4817  char *frameworkNameZ = strdup(frameworkName.c_str());
4818  args.push_back(frameworkNameZ);
4819  argsToFree.push_back(frameworkNameZ);
4820  }
4821 
4822  // Check for C Runtime path within Vuo.framework
4823  llvm::sys::Path cRuntimePath;
4824  llvm::sys::Path crt1Path;
4825  string vuoFrameworkPath = getVuoFrameworkPath();
4826  string vuoFrameworkContainingFolder = vuoFrameworkPath + "/..";
4827  if (! vuoFrameworkPath.empty())
4828  {
4829  cRuntimePath = vuoFrameworkPath + "/Modules/";
4830  crt1Path = cRuntimePath;
4831  crt1Path.appendComponent("crt1.o");
4832  }
4833 
4834  // If we have located a bundled version of crt1.o, link it in explicitly rather than relying on
4835  // clang's heuristic to locate a system version.
4836  if (!isDylib && crt1Path.canRead())
4837  {
4838  args.push_back("-nostartfiles");
4839  args.push_back(crt1Path.c_str());
4840  }
4841 
4842  // Linker option necessary for compatibility with our bundled version of ld64:
4843  args.push_back("-Xlinker");
4844  args.push_back("--no-demangle");
4845 
4846  if (isVerbose)
4847  args.push_back("-v");
4848 
4849  if (isDylib)
4850  args.push_back("-dynamiclib");
4851 
4852  args.push_back("-Xlinker");
4853  args.push_back("-headerpad_max_install_names");
4854 
4855  // Tell the built dylib/executable where to find Vuo.framework
4857  args.push_back("-rpath");
4858  string rPathArg = (rPath.empty() ? vuoFrameworkContainingFolder : rPath);
4859  args.push_back(rPathArg.c_str());
4860 
4861 #ifdef COVERAGE
4862  args.push_back("-rpath");
4863  args.push_back(LLVM_ROOT "/lib");
4864 #endif
4865 
4866  args.push_back("-std=c++11");
4867  args.push_back("-stdlib=libc++");
4868 
4869  // Allow clang to print meaningful error messages.
4870  clang::DiagnosticOptions *diagOptions = new clang::DiagnosticOptions();
4871  clang::TextDiagnosticPrinter *diagClient = new clang::TextDiagnosticPrinter(llvm::errs(), diagOptions);
4872  diagClient->setPrefix(clangPath.str());
4873  IntrusiveRefCntPtr<clang::DiagnosticIDs> DiagID(new clang::DiagnosticIDs());
4874  clang::DiagnosticsEngine Diags(DiagID, diagOptions, diagClient);
4875 
4876  clang::driver::Driver TheDriver(args[0], "x86_64-apple-macosx10.10.0", outputPath, Diags);
4877 
4878  TheDriver.CCCIsCXX = true; // clang++ instead of clang
4879 
4880  if (isVerbose)
4881  {
4882  ostringstream s;
4883  for (vector<const char *>::iterator i = args.begin(); i != args.end(); ++i)
4884  s << *i << " ";
4885  VUserLog("\t%s", s.str().c_str());
4886  }
4887 
4888  OwningPtr<clang::driver::Compilation> C(TheDriver.BuildCompilation(args));
4889 
4890  int Res = 0;
4891  if (C)
4892  {
4893  SmallVector<std::pair<int, const clang::driver::Command *>, 4> FailingCommands;
4894  double t0 = VuoLogGetTime();
4895  Res = TheDriver.ExecuteCompilation(*C, FailingCommands);
4896  VDebugLog("\tLinking took %5.2fs", VuoLogGetTime() - t0);
4897  }
4898  for (auto i : argsToFree)
4899  free(i);
4900 
4901  // Clean up composite module file.
4902  remove(compositeModulePath.c_str());
4903 
4904  if (!isDylib)
4905  // Ensure the linked binary has the execute permission set
4906  // (ld64-242 doesn't reliably set it, whereas ld64-133.3 did).
4907  // https://b33p.net/kosada/node/14152
4908  chmod(outputPath.c_str(), 0755);
4909 
4910  if (Res != 0)
4911  {
4912  __block vector<string> thirdPartyNodeClasses;
4913  dispatch_sync(environmentQueue, ^{
4914  for (size_t i = 1; i < environments.size(); ++i)
4915  {
4916  map<string, VuoCompilerNodeClass *> envNodeClasses = environments[i].at(0)->getNodeClasses();
4917  for (map<string, VuoCompilerNodeClass *>::iterator j = envNodeClasses.begin(); j != envNodeClasses.end(); ++j)
4918  thirdPartyNodeClasses.push_back(j->first);
4919  }
4920  });
4921 
4922  string details = "One or more nodes in this composition can't be used by this version of Vuo. ";
4923  if (! thirdPartyNodeClasses.empty())
4924  {
4925  details += "Make sure you're using the latest version of all the extra Vuo nodes you've installed:\n";
4926  sort(thirdPartyNodeClasses.begin(), thirdPartyNodeClasses.end());
4927  for (vector<string>::iterator i = thirdPartyNodeClasses.begin(); i != thirdPartyNodeClasses.end(); ++i)
4928  details += " • " + *i + "\n";
4929  }
4930  details += "Check the macOS Console for more information about the problem.";
4931 
4932  VuoCompilerIssue issue(VuoCompilerIssue::Error, "linking", outputPath,
4933  "Node broken or outdated", details);
4934  throw VuoCompilerException(issue);
4935  }
4936  VDebugLog("Done.");
4937 }
4938 
4944 Module * VuoCompiler::readModuleFromC(string inputPath, const vector<string> &headerSearchPaths, const vector<string> &extraArgs)
4945 {
4946  // llvm-3.1/llvm/tools/clang/examples/clang-interpreter/main.cpp
4947 
4948  vector<const char *> args;
4949  args.push_back(inputPath.c_str());
4950  args.push_back("-DVUO_COMPILER");
4951  args.push_back("-fblocks");
4952 
4953  // Sync with vuo.pri's WARNING_ADD.
4954  args.push_back("-Wall");
4955  args.push_back("-Wextra");
4956  args.push_back("-Wimplicit-fallthrough");
4957  args.push_back("-Wno-unused-parameter");
4958  args.push_back("-Wno-c++11-extensions");
4959  args.push_back("-Wno-sign-compare");
4960 
4961  if (VuoStringUtilities::endsWith(inputPath, ".cc"))
4962  {
4963  args.push_back("-std=c++11");
4964  args.push_back("-stdlib=libc++");
4965  }
4966 
4967  for (vector<string>::const_iterator i = headerSearchPaths.begin(); i != headerSearchPaths.end(); ++i)
4968  {
4969  args.push_back("-I");
4970  args.push_back(i->c_str());
4971  }
4972 
4973  if (isVerbose)
4974  args.push_back("-v");
4975 
4976  for (vector<string>::const_iterator i = extraArgs.begin(); i != extraArgs.end(); ++i)
4977  args.push_back(i->c_str());
4978 
4979  clang::DiagnosticOptions * diagOptions = new clang::DiagnosticOptions();
4980  IntrusiveRefCntPtr<clang::DiagnosticIDs> DiagID(new clang::DiagnosticIDs());
4981  clang::DiagnosticsEngine Diags(DiagID, diagOptions);
4982 
4983  OwningPtr<clang::CompilerInvocation> CI(new clang::CompilerInvocation);
4984  clang::CompilerInvocation::CreateFromArgs(*CI, &args[0], &args[0] + args.size(), Diags);
4985 
4986  clang::CompilerInstance Clang;
4987  Clang.setInvocation(CI.take());
4988 
4989  Clang.createDiagnostics();
4990  if (!Clang.hasDiagnostics())
4991  return NULL;
4992 
4993  // See CompilerInvocation::GetResourcesPath -- though we're not calling it because we don't have MainAddr.
4994  llvm::sys::Path builtinHeaderSearchPath;
4995  string vuoFrameworkPath = getVuoFrameworkPath();
4996  if (vuoFrameworkPath.empty())
4997  {
4998  llvm::sys::Path clangPath = getClangPath();
4999  builtinHeaderSearchPath = clangPath;
5000  builtinHeaderSearchPath.eraseComponent(); // Remove /clang from foo/bin/clang
5001  builtinHeaderSearchPath.eraseComponent(); // Remove /bin from foo/bin
5002  builtinHeaderSearchPath.appendComponent("lib");
5003  builtinHeaderSearchPath.appendComponent("clang");
5004  builtinHeaderSearchPath.appendComponent(CLANG_VERSION_STRING); // foo/lib/clang/<version>
5005  }
5006  else
5007  {
5008  builtinHeaderSearchPath = vuoFrameworkPath;
5009  builtinHeaderSearchPath.appendComponent("Frameworks");
5010  builtinHeaderSearchPath.appendComponent("llvm.framework");
5011  builtinHeaderSearchPath.appendComponent("Versions");
5012  builtinHeaderSearchPath.appendComponent("A");
5013  builtinHeaderSearchPath.appendComponent("lib");
5014  builtinHeaderSearchPath.appendComponent("clang");
5015  builtinHeaderSearchPath.appendComponent(CLANG_VERSION_STRING); // foo/lib/clang/<version>
5016  }
5017  Clang.getHeaderSearchOpts().ResourceDir = builtinHeaderSearchPath.str();
5018 
5019 // OwningPtr<clang::CodeGenAction> Act(new clang::EmitLLVMOnlyAction()); // @@@ return value of takeModule() is destroyed at the end of this function
5020  clang::CodeGenAction *Act = new clang::EmitLLVMOnlyAction();
5021  if (!Clang.ExecuteAction(*Act))
5022  return NULL;
5023 
5024  return Act->takeModule();
5025 }
5026 
5032 Module * VuoCompiler::readModuleFromBitcode(string inputPath)
5033 {
5034  string dir, file, ext;
5035  VuoFileUtilities::splitPath(inputPath, dir, file, ext);
5036  VuoFileUtilities::File inputFile(dir, file + "." + ext);
5037  return readModuleFromBitcode(&inputFile);
5038 }
5039 
5047 Module * VuoCompiler::readModuleFromBitcode(VuoFileUtilities::File *inputFile)
5048 {
5049  size_t inputDataBytes;
5050  char *inputData = inputFile->getContentsAsRawData(inputDataBytes);
5051 
5052  string error;
5053  VuoLog_status("Loading module \"%s\"", inputFile->getRelativePath().c_str());
5054  Module *module = readModuleFromBitcodeData(inputData, inputDataBytes, error);
5055  VuoLog_status(NULL);
5056  if (! module)
5057  VUserLog("Error: Couldn't parse module '%s': %s.", inputFile->getRelativePath().c_str(), error.c_str());
5058 
5059  free(inputData);
5060 
5061  return module;
5062 }
5063 
5069 Module * VuoCompiler::readModuleFromBitcodeData(char *inputData, size_t inputDataBytes, string &error)
5070 {
5071  __block Module *module;
5072  dispatch_sync(llvmQueue, ^{
5073  StringRef inputDataAsStringRef(inputData, inputDataBytes);
5074  MemoryBuffer *mb = MemoryBuffer::getMemBuffer(inputDataAsStringRef, "", false);
5075  module = ParseBitcodeFile(&(*mb), getGlobalContext(), &error);
5076  delete mb;
5077  });
5078  return module;
5079 }
5080 
5088 bool VuoCompiler::writeModuleToBitcode(Module *module, string outputPath)
5089 {
5090  if (verifyModule(*module, PrintMessageAction))
5091  {
5092  VUserLog("Error: Module verification failed.");
5093  return true;
5094  }
5095 
5096  string err;
5097  raw_fd_ostream out(outputPath.c_str(), err);
5098  if (! err.empty())
5099  {
5100  VUserLog("Error: Couldn't open file '%s' for writing: %s", outputPath.c_str(), err.c_str());
5101  return true;
5102  }
5103  WriteBitcodeToFile(module, out);
5104 
5105  return false;
5106 }
5107 
5113 void VuoCompiler::setTargetForModule(Module *module, string target)
5114 {
5115 /*
5116  string effectiveTarget = target;
5117  if (effectiveTarget.empty())
5118  {
5119  // llvm::sys::getDefaultTargetTriple() finds a target based on the host, but the "default" target is not necessarily the
5120  // same target that results from invoking command-line clang without a -target argument. That is the "effective" target.
5121  // For example, the "default" target could be x86_64-apple-darwin10.10.0 and the "effective" target could be x86_64-apple-macosx10.10.0.
5122 
5123  llvm::sys::Path clangPath = getClangPath();
5124 
5125  vector<const char *> args;
5126  args.push_back(clangPath.c_str());
5127  args.push_back("/bin/sh"); // Driver needs an input file (that exists) or it refuses to give you the correct effective target.
5128 
5129  clang::DiagnosticOptions * diagOptions = new clang::DiagnosticOptions();
5130  IntrusiveRefCntPtr<clang::DiagnosticIDs> DiagID(new clang::DiagnosticIDs());
5131  clang::DiagnosticsEngine Diags(DiagID, diagOptions);
5132 
5133  clang::driver::Driver TheDriver(args[0], llvm::sys::getDefaultTargetTriple(), "a.out", true, Diags);
5134  OwningPtr<clang::driver::Compilation> C(TheDriver.BuildCompilation(args));
5135  effectiveTarget = C->getDefaultToolChain().ComputeEffectiveClangTriple(C->getArgs());
5136  }
5137 
5138  module->setTargetTriple(effectiveTarget);
5139 */
5140  module->setTargetTriple("x86_64-apple-macosx10.10.0");
5141 }
5142 
5150 {
5151  Module *llvmModule = module->getModule();
5152 
5153  // In C++ the return value of a cast may not be the same pointer as the cast arg.
5154  // Because of this, VuoModule::getPseudoBase() returns a different value than VuoNodeClass::getBase().
5155  // Calling delete on VuoModule::getPseudoBase() gives a malloc error: "pointer being freed was not allocated".
5156  // So call it on VuoNodeClass::getBase(). Same idea for VuoType.
5157 
5158  VuoNodeClass *baseNodeClass = NULL;
5159  VuoType *baseType = NULL;
5160  VuoModule *baseModule = NULL;
5161  VuoCompilerNodeClass *nodeClass = dynamic_cast<VuoCompilerNodeClass *>(module);
5162  if (nodeClass)
5163  {
5164  baseNodeClass = dynamic_cast<VuoNodeClass *>(nodeClass->getBase());
5165 
5167  if (dynamic_cast<VuoCompilerSpecializedNodeClass *>(module))
5168  module = NULL;
5169  }
5170  else
5171  {
5172  VuoCompilerType *type = dynamic_cast<VuoCompilerType *>(module);
5173  if (type)
5174  baseType = dynamic_cast<VuoType *>(type->getBase());
5175  else
5176  baseModule = module->getPseudoBase();
5177  }
5178 
5179  delete module;
5180  delete baseNodeClass;
5181  delete baseType;
5182  delete baseModule;
5183  destroyLlvmModule(llvmModule);
5184 }
5185 
5193 {
5194  dispatch_sync(llvmQueue, ^{
5195  delete module;
5196  });
5197 }
5198 
5210 VuoNode * VuoCompiler::createNode(VuoCompilerNodeClass *nodeClass, string title, double x, double y)
5211 {
5213  return nodeClassForNode->newNode(title, x, y);
5214 }
5215 
5219 VuoNode * VuoCompiler::createPublishedInputNode(vector<VuoPublishedPort *> publishedInputPorts)
5220 {
5221  string nodeClassName = VuoCompilerPublishedInputNodeClass::buildNodeClassName(publishedInputPorts);
5222  return createPublishedNode(nodeClassName, publishedInputPorts);
5223 }
5224 
5228 VuoNode * VuoCompiler::createPublishedOutputNode(vector<VuoPublishedPort *> publishedOutputPorts)
5229 {
5230  string nodeClassName = VuoCompilerPublishedOutputNodeClass::buildNodeClassName(publishedOutputPorts);
5231  return createPublishedNode(nodeClassName, publishedOutputPorts);
5232 }
5233 
5237 VuoNode * VuoCompiler::createPublishedNode(const string &nodeClassName, const vector<VuoPublishedPort *> &publishedPorts)
5238 {
5239  VuoCompilerNodeClass *nodeClass = getNodeClass(nodeClassName);
5240  VuoNode *node = createNode(nodeClass);
5241 
5242  // Set the generic port types on the node to match those on the published ports set by VuoCompilerComposition::updateGenericPortTypes().
5243  VuoCompilerPublishedInputNodeClass *inputNodeClass = dynamic_cast<VuoCompilerPublishedInputNodeClass *>(nodeClass);
5244  VuoCompilerPublishedOutputNodeClass *outputNodeClass = dynamic_cast<VuoCompilerPublishedOutputNodeClass *>(nodeClass);
5245  for (size_t i = 0; i < publishedPorts.size(); ++i)
5246  {
5247  VuoType *publishedPortType = static_cast<VuoCompilerPort *>(publishedPorts[i]->getCompiler())->getDataVuoType();
5248  if (! dynamic_cast<VuoGenericType *>(publishedPortType))
5249  continue;
5250 
5251  set<VuoPort *> nodePorts;
5252  if (inputNodeClass)
5253  {
5254  VuoPort *inputPort = node->getInputPorts().at( inputNodeClass->getInputPortIndexForPublishedInputPort(i) );
5255  nodePorts.insert(inputPort);
5256 
5257  VuoPort *outputPort = node->getOutputPorts().at( inputNodeClass->getOutputPortIndexForPublishedInputPort(i) );
5258  nodePorts.insert(outputPort);
5259  }
5260  else
5261  {
5262  VuoPort *inputPort = node->getInputPorts().at( outputNodeClass->getInputPortIndexForPublishedOutputPort(i) );
5263  nodePorts.insert(inputPort);
5264  }
5265 
5266  for (VuoPort *port : nodePorts)
5267  {
5268  VuoCompilerPort *compilerPort = static_cast<VuoCompilerPort *>(port->getCompiler());
5269  compilerPort->setDataVuoType(publishedPortType);
5270  }
5271  }
5272 
5273  reifyGenericPortTypes(node);
5274 
5275  return node;
5276 }
5277 
5283 {
5284  dispatch_sync(environmentQueue, ^{
5285  if (environments.size() >= 5)
5286  environments.at(3).at(0)->addExpatriateSourceFile(sourcePath);
5287  });
5288 }
5289 
5295 {
5296  dispatch_sync(environmentQueue, ^{
5297  if (environments.size() >= 5)
5298  {
5299  environments.at(3).at(0)->removeExpatriateSourceFile(sourcePath);
5300 
5301  set<string> sourcesRemoved;
5302  sourcesRemoved.insert(getModuleKeyForPath(sourcePath));
5303  loadModulesAndSources(set<string>(), set<string>(), set<string>(), set<string>(), set<string>(), sourcesRemoved,
5304  false, false, environments.at(3).at(0), nullptr, nullptr, "");
5305  }
5306  });
5307 }
5308 
5325 void VuoCompiler::overrideInstalledNodeClass(const string &sourcePath, const string &sourceCode)
5326 {
5327  string sourcePathCopy = sourcePath;
5328  string sourceCodeCopy = sourceCode;
5329 
5330  dispatch_async(environmentQueue, ^{
5331  string nodeClassName = getModuleKeyForPath(sourcePathCopy);
5332  ModuleInfo *sourceInfo = NULL;
5333 
5334  for (const vector<Environment *> &envs : environments)
5335  {
5336  for (Environment *env : envs)
5337  {
5338  ModuleInfo *potentialSourceInfo = env->listSourceFile(nodeClassName);
5339  if (potentialSourceInfo && VuoFileUtilities::arePathsEqual(potentialSourceInfo->getFile()->path(), sourcePathCopy))
5340  {
5341  sourceInfo = potentialSourceInfo;
5342  break;
5343  }
5344  }
5345  }
5346 
5347  if (! sourceInfo)
5348  return;
5349 
5350  bool shouldRecompileSourcesIfUnchanged;
5351  if (! sourceCodeCopy.empty())
5352  {
5353  sourceInfo->setSourceCode(sourceCodeCopy);
5354  sourceInfo->setSourceCodeOverridden(true);
5355 
5356  shouldRecompileSourcesIfUnchanged = false;
5357  }
5358  else
5359  {
5360  sourceInfo->revertSourceCode();
5361  sourceInfo->setSourceCodeOverridden(false);
5362 
5363  shouldRecompileSourcesIfUnchanged = true;
5364  }
5365  sourceInfo->setAttempted(false);
5366  sourceInfo->setLastModifiedToNow();
5367 
5368  set<string> sourcesModified;
5369  sourcesModified.insert(nodeClassName);
5370 
5371  loadModulesAndSources(set<string>(), set<string>(), set<string>(), set<string>(), sourcesModified, set<string>(),
5372  false, shouldRecompileSourcesIfUnchanged, nullptr, nullptr, nullptr, "");
5373  });
5374 }
5375 
5385 void VuoCompiler::revertOverriddenNodeClass(const string &sourcePath)
5386 {
5387  overrideInstalledNodeClass(sourcePath, "");
5388 }
5389 
5402 VuoCompilerNodeClass * VuoCompiler::getNodeClass(const string &nodeClassName)
5403 {
5404  // For performance, don't bother searching if it's obviously not a node class.
5405 
5406  if (VuoStringUtilities::endsWith(nodeClassName, ".framework"))
5407  return NULL;
5408 
5409  // Attempt to load the node class (if it hasn't already been loaded).
5410 
5411  set<string> nodeClassNameSet;
5412  nodeClassNameSet.insert(nodeClassName);
5413  loadModulesIfNeeded(nodeClassNameSet);
5414 
5415  // If the node class has been loaded, return it.
5416 
5417  __block VuoCompilerNodeClass *nodeClass = NULL;
5418  void (^envGetNodeClass)(Environment *) = ^void (Environment *env) {
5419  VuoCompilerNodeClass *result = env->getNodeClass(nodeClassName);
5420  if (result)
5421  nodeClass = result;
5422  };
5423  applyToAllEnvironments(envGetNodeClass);
5424  return nodeClass;
5425 }
5426 
5432 map<string, VuoCompilerNodeClass *> VuoCompiler::getNodeClasses()
5433 {
5434  loadModulesIfNeeded();
5435 
5436  __block map<string, VuoCompilerNodeClass *> nodeClasses;
5437  void (^envGetNodeClasses)(Environment *) = ^void (Environment *env) {
5438  map<string, VuoCompilerNodeClass *> result = env->getNodeClasses();
5439  nodeClasses.insert(result.begin(), result.end());
5440  };
5441  applyToInstalledEnvironments(envGetNodeClasses);
5442  return nodeClasses;
5443 }
5444 
5450 VuoCompilerType * VuoCompiler::getType(const string &typeName)
5451 {
5452  set<string> typeNameSet;
5453  typeNameSet.insert(typeName);
5454  loadModulesIfNeeded(typeNameSet);
5455 
5456  __block VuoCompilerType *type = NULL;
5457  void (^envGetType)(Environment *) = ^void (Environment *env) {
5458  VuoCompilerType *result = env->getType(typeName);
5459  if (result)
5460  type = result;
5461  };
5462  applyToInstalledEnvironments(envGetType);
5463 
5464  if (! type && VuoGenericType::isGenericTypeName(typeName))
5465  {
5466  VuoCompilerType * (^compilerGetType)(string) = ^VuoCompilerType * (string moduleKey) {
5467  return getType(moduleKey);
5468  };
5469 
5470  VuoGenericType *genericType = new VuoGenericType(typeName, vector<string>());
5471  type = VuoCompilerGenericType::newGenericType(genericType, compilerGetType);
5472  }
5473 
5474  return type;
5475 }
5476 
5482 map<string, VuoCompilerType *> VuoCompiler::getTypes()
5483 {
5484  loadModulesIfNeeded();
5485 
5486  __block map<string, VuoCompilerType *> types;
5487  void (^envGetTypes)(Environment *) = ^void (Environment *env) {
5488  map<string, VuoCompilerType *> result = env->getTypes();
5489  types.insert(result.begin(), result.end());
5490  };
5491  applyToInstalledEnvironments(envGetTypes);
5492  return types;
5493 }
5494 
5500 map<string, VuoCompilerModule *> VuoCompiler::getLibraryModules()
5501 {
5502  loadModulesIfNeeded();
5503 
5504  __block map<string, VuoCompilerModule *> libraryModules;
5505  void (^envGetLibraryModules)(Environment *) = ^void (Environment *env) {
5506  map<string, VuoCompilerModule *> result = env->getLibraryModules();
5507  libraryModules.insert(result.begin(), result.end());
5508  };
5509  applyToInstalledEnvironments(envGetLibraryModules);
5510  return libraryModules;
5511 }
5512 
5518 map<string, VuoNodeSet *> VuoCompiler::getNodeSets()
5519 {
5520  loadModulesIfNeeded();
5521 
5522  __block map<string, VuoNodeSet *> nodeSets;
5523  void (^envGetNodeSets)(Environment *) = ^void (Environment *env) {
5524  map<string, VuoNodeSet *> result = env->getNodeSets();
5525  nodeSets.insert(result.begin(), result.end());
5526  };
5527  applyToInstalledEnvironments(envGetNodeSets);
5528  return nodeSets;
5529 }
5530 
5537 {
5538  loadModulesIfNeeded();
5539 
5540  __block VuoNodeSet *nodeSet = NULL;
5541  void (^envFindNodeSet)(Environment *) = ^void (Environment *env) {
5542  VuoNodeSet *result = env->findNodeSet(name);
5543  if (result)
5544  nodeSet = result;
5545  };
5546  applyToInstalledEnvironments(envFindNodeSet);
5547  return nodeSet;
5548 }
5549 
5553 VuoCompilerModule * VuoCompiler::getModule(const string &moduleKey)
5554 {
5555  __block VuoCompilerModule *module = NULL;
5556  void (^envFindModule)(Environment *) = ^void (Environment *env) {
5557  VuoCompilerModule *result = env->findModule(moduleKey);
5558  if (result)
5559  module = result;
5560  };
5561  applyToAllEnvironments(envFindModule);
5562  return module;
5563 }
5564 
5576 void VuoCompiler::listNodeClasses(const string &format)
5577 {
5578  map<string, VuoCompilerNodeClass *> nodeClasses = getNodeClasses();
5579  for (map<string, VuoCompilerNodeClass *>::const_iterator i = nodeClasses.begin(); i != nodeClasses.end(); ++i)
5580  {
5581  VuoCompilerNodeClass *nodeClass = i->second;
5582  if (format == "")
5583  {
5584  printf("%s\n", nodeClass->getBase()->getClassName().c_str());
5585  }
5586  else if (format == "path")
5587  {
5588  // TODO: If "path", prints the absolute path of each node class, one per line.
5589  }
5590  else if (format == "dot")
5591  {
5592  VuoCompilerNode *node = nodeClass->newNode()->getCompiler();
5593 
5594  printf("%s\n", nodeClass->getDoxygenDocumentation().c_str());
5595  printf("%s\n\n", node->getGraphvizDeclaration().c_str());
5596 
5597  delete node;
5598  }
5599  }
5600 }
5601 
5608 vector<string> VuoCompiler::getCoreVuoDependencies(void)
5609 {
5610  vector<string> dependencies;
5611  dependencies.push_back("VuoCompositionDiff.o");
5612  dependencies.push_back("VuoHeap");
5613  dependencies.push_back("VuoException.o");
5614  dependencies.push_back("VuoNodeRegistry.o");
5615  dependencies.push_back("VuoRuntimeCommunicator.o");
5616  dependencies.push_back("VuoRuntimeContext.o");
5617  dependencies.push_back("VuoRuntimePersistentState.o");
5618  dependencies.push_back("VuoRuntimeState.o");
5619  dependencies.push_back("VuoTelemetry.o");
5620  dependencies.push_back("VuoThreadManager.o");
5621  dependencies.push_back("VuoApp");
5622  dependencies.push_back("zmq");
5623  dependencies.push_back("json-c");
5624  dependencies.push_back("objc");
5625  dependencies.push_back("c");
5626  dependencies.push_back("AppKit.framework");
5627 
5628 #ifdef COVERAGE
5629  dependencies.push_back(LLVM_ROOT "/lib/libprofile_rt.dylib");
5630 #endif
5631  return dependencies;
5632 }
5633 
5637 string VuoCompiler::getRuntimeMainDependency(void)
5638 {
5639  return "VuoRuntimeMain.o";
5640 }
5641 
5649 string VuoCompiler::getRuntimeDependency(void)
5650 {
5651  return "VuoRuntime.o";
5652 }
5653 
5660 string VuoCompiler::getVuoFrameworkPath(void)
5661 {
5662  if (! vuoFrameworkInProgressPath.empty())
5663  return vuoFrameworkInProgressPath;
5664 
5666 }
5667 
5671 llvm::sys::Path VuoCompiler::getClangPath(void)
5672 {
5673  return clangPath;
5674 }
5675 
5680 {
5681  // Start with the file name without extension.
5682  string dir, moduleKey, ext;
5683  VuoFileUtilities::splitPath(path, dir, moduleKey, ext);
5684 
5686  {
5687  vector<string> nodeClassNameParts = VuoStringUtilities::split(moduleKey, '.');
5688 
5689  // Remove repeated file extensions.
5690  while (nodeClassNameParts.size() > 1 && nodeClassNameParts.back() == ext)
5691  nodeClassNameParts.pop_back();
5692 
5693  // Convert each part to lowerCamelCase.
5694  for (string &part : nodeClassNameParts)
5695  part = VuoStringUtilities::convertToCamelCase(part, false, true, true);
5696 
5697  // If the node class name has only one part, add a prefix.
5698  if (nodeClassNameParts.size() == 1)
5699  nodeClassNameParts.insert(nodeClassNameParts.begin(), "isf");
5700 
5701  moduleKey = VuoStringUtilities::join(nodeClassNameParts, '.');
5702  }
5703 
5704  return moduleKey;
5705 }
5706 
5712 {
5713  __block bool isLocal = false;
5714 
5715  dispatch_sync(environmentQueue, ^{
5716  if (environments.size() >= 5)
5717  isLocal = environments.at(3).at(0)->findModule(moduleKey);
5718  });
5719 
5720  return isLocal;
5721 }
5722 
5730 {
5731  return lastCompositionBaseDir.empty() ? "" : lastCompositionBaseDir + "/Modules";
5732 }
5733 
5745 {
5746  return lastCompositionBaseDir;
5747 }
5748 
5752 void VuoCompiler::addModuleSearchPath(string path)
5753 {
5754  dispatch_sync(environmentQueue, ^{
5755  environments.back().at(0)->addModuleSearchPath(path);
5756  environments.back().at(0)->addLibrarySearchPath(path);
5757  });
5758 }
5759 
5763 void VuoCompiler::addHeaderSearchPath(const string &path)
5764 {
5765  dispatch_sync(environmentQueue, ^{
5766  environments.back().at(0)->addHeaderSearchPath(path);
5767  });
5768 }
5769 
5773 void VuoCompiler::addLibrarySearchPath(const string &path)
5774 {
5775  dispatch_sync(environmentQueue, ^{
5776  environments.back().at(0)->addLibrarySearchPath(path);
5777  });
5778 }
5779 
5783 void VuoCompiler::addFrameworkSearchPath(const string &path)
5784 {
5785  dispatch_sync(environmentQueue, ^{
5786  environments.back().at(0)->addFrameworkSearchPath(path);
5787  });
5788 }
5789 
5793 void VuoCompiler::setTelemetry(const string &telemetry)
5794 {
5795  this->telemetry = telemetry;
5796 }
5797 
5801 void VuoCompiler::setTarget(const string &target)
5802 {
5803  this->target = target;
5804 }
5805 
5809 void VuoCompiler::setVerbose(bool isVerbose)
5810 {
5811  this->isVerbose = isVerbose;
5812 }
5813 
5819 {
5820 #if VUO_PRO
5821  if (VuoPro::getProAccess())
5822  {
5823  _shouldShowSplashWindow = false;
5824  return;
5825  }
5826 #endif
5827 
5828  _shouldShowSplashWindow = potentiallyShow;
5829 }
5830 
5835 {
5836  return _shouldShowSplashWindow;
5837 }
5838 
5842 void VuoCompiler::setClangPath(const string &clangPath)
5843 {
5844  this->clangPath = llvm::sys::Path(StringRef(clangPath));
5845 }
5846 
5851 {
5852  string vuoFrameworkPath = getVuoFrameworkPath();
5853  return (vuoFrameworkPath.empty() ?
5854  VUO_BUILD_DIR "/bin/VuoCompositionLoader.app/Contents/MacOS/VuoCompositionLoader" :
5855  vuoFrameworkPath + "/Helpers/VuoCompositionLoader.app/Contents/MacOS/VuoCompositionLoader");
5856 }
5857 
5861 string VuoCompiler::getCompositionStubPath(void)
5862 {
5863  string vuoFrameworkPath = getVuoFrameworkPath();
5864  return (vuoFrameworkPath.empty() ?
5865  VUO_BUILD_DIR "/lib/libVuoCompositionStub.dylib" :
5866  vuoFrameworkPath + "/Modules/libVuoCompositionStub.dylib");
5867 }
5868 
5872 string VuoCompiler::getCachePathForComposition(const string compositionDir)
5873 {
5874  string modifierLetterColon("꞉");
5875  string cachedModulesName = compositionDir;
5876  VuoStringUtilities::replaceAll(cachedModulesName, "/", modifierLetterColon);
5877  return VuoFileUtilities::getCachePath() + "/" + cachedModulesName;
5878 }
5879 
5884 {
5885  __block vector<string> moduleSearchPaths;
5886  void (^envGetModuleSearchPaths)(Environment *) = ^void (Environment *env) {
5887  vector<string> result = env->getModuleSearchPaths();
5888  moduleSearchPaths.insert(moduleSearchPaths.end(), result.begin(), result.end());
5889  };
5890  applyToInstalledEnvironments(envGetModuleSearchPaths);
5891 
5892  __block vector<string> headerSearchPaths;
5893  void (^envGetHeaderSearchPaths)(Environment *) = ^void (Environment *env) {
5894  vector<string> result = env->getHeaderSearchPaths();
5895  headerSearchPaths.insert(headerSearchPaths.end(), result.begin(), result.end());
5896  };
5897  applyToInstalledEnvironments(envGetHeaderSearchPaths);
5898 
5899  __block vector<string> librarySearchPaths;
5900  void (^envGetLibrarySearchPaths)(Environment *) = ^void (Environment *env) {
5901  vector<string> result = env->getLibrarySearchPaths();
5902  librarySearchPaths.insert(librarySearchPaths.end(), result.begin(), result.end());
5903  };
5904  applyToInstalledEnvironments(envGetLibrarySearchPaths);
5905 
5906  __block vector<string> frameworkSearchPaths;
5907  void (^envGetFrameworkSearchPaths)(Environment *) = ^void (Environment *env) {
5908  vector<string> result = env->getFrameworkSearchPaths();
5909  frameworkSearchPaths.insert(frameworkSearchPaths.end(), result.begin(), result.end());
5910  };
5911  applyToInstalledEnvironments(envGetFrameworkSearchPaths);
5912 
5913  fprintf(stderr, "Module (node class, type, library) search paths:\n");
5914  for (vector<string>::iterator i = moduleSearchPaths.begin(); i != moduleSearchPaths.end(); ++i)
5915  fprintf(stderr, " %s\n", (*i).c_str());
5916  fprintf(stderr, "Header search paths:\n");
5917  for (vector<string>::iterator i = headerSearchPaths.begin(); i != headerSearchPaths.end(); ++i)
5918  fprintf(stderr, " %s\n", (*i).c_str());
5919  fprintf(stderr, "Other library search paths:\n");
5920  for (vector<string>::iterator i = librarySearchPaths.begin(); i != librarySearchPaths.end(); ++i)
5921  fprintf(stderr, " %s\n", (*i).c_str());
5922  fprintf(stderr, "Other framework search paths:\n");
5923  for (vector<string>::iterator i = frameworkSearchPaths.begin(); i != frameworkSearchPaths.end(); ++i)
5924  fprintf(stderr, " %s\n", (*i).c_str());
5925  fprintf(stderr, "Framework path:\n");
5926  if (! getVuoFrameworkPath().empty())
5927  fprintf(stderr, " %s\n", getVuoFrameworkPath().c_str());
5928  fprintf(stderr, "Clang path:\n");
5929  if (! getClangPath().str().empty())
5930  fprintf(stderr, " %s\n", getClangPath().c_str());
5931 }
5932 
5942 {
5943  try
5944  {
5945  VuoCompiler compiler(compositionFilePath);
5946  string directory, file, extension;
5947  VuoFileUtilities::splitPath(compositionFilePath, directory, file, extension);
5948  string compiledCompositionPath = VuoFileUtilities::makeTmpFile(file, "bc");
5949  string linkedCompositionPath = VuoFileUtilities::makeTmpFile(file + "-linked", "");
5950  compiler.compileComposition(compositionFilePath, compiledCompositionPath, true, issues);
5951  compiler.linkCompositionToCreateExecutable(compiledCompositionPath, linkedCompositionPath, Optimization_FastBuild);
5952  remove(compiledCompositionPath.c_str());
5953  return VuoRunner::newSeparateProcessRunnerFromExecutable(linkedCompositionPath, directory, false, true);
5954  }
5955  catch (VuoCompilerException &e)
5956  {
5957  if (issues != e.getIssues())
5958  issues->append(e.getIssues());
5959  return NULL;
5960  }
5961 }
5962 
5977 VuoRunner * VuoCompiler::newSeparateProcessRunnerFromCompositionString(string composition, string processName, string workingDirectory, VuoCompilerIssues *issues)
5978 {
5979  try
5980  {
5981  string compositionPath = (workingDirectory.empty() ? "." : workingDirectory) + "/" + processName + ".vuo";
5982  VuoCompiler compiler(compositionPath);
5983  string compiledCompositionPath = VuoFileUtilities::makeTmpFile(processName, "bc");
5984  string linkedCompositionPath = VuoFileUtilities::makeTmpFile(processName, "");
5985  compiler.compileCompositionString(composition, compiledCompositionPath, true, issues);
5986  compiler.linkCompositionToCreateExecutable(compiledCompositionPath, linkedCompositionPath, Optimization_FastBuild);
5987  remove(compiledCompositionPath.c_str());
5988  return VuoRunner::newSeparateProcessRunnerFromExecutable(linkedCompositionPath, workingDirectory, false, true);
5989  }
5990  catch (VuoCompilerException &e)
5991  {
5992  if (issues != e.getIssues())
5993  issues->append(e.getIssues());
5994  return NULL;
5995  }
5996 }
5997 
6007 {
6008  try
6009  {
6010  VuoCompiler compiler(compositionFilePath);
6011  string directory, file, extension;
6012  VuoFileUtilities::splitPath(compositionFilePath, directory, file, extension);
6013  string compiledCompositionPath = VuoFileUtilities::makeTmpFile(file, "bc");
6014  compiler.compileComposition(compositionFilePath, compiledCompositionPath, true, issues);
6015  string linkedCompositionPath = VuoFileUtilities::makeTmpFile(file, "dylib");
6016  compiler.linkCompositionToCreateDynamicLibrary(compiledCompositionPath, linkedCompositionPath, Optimization_FastBuild);
6017  remove(compiledCompositionPath.c_str());
6018  return VuoRunner::newCurrentProcessRunnerFromDynamicLibrary(linkedCompositionPath, directory, true);
6019  }
6020  catch (VuoCompilerException &e)
6021  {
6022  if (issues != e.getIssues())
6023  issues->append(e.getIssues());
6024  return NULL;
6025  }
6026 }
6027 
6042 VuoRunner * VuoCompiler::newCurrentProcessRunnerFromCompositionString(string composition, string workingDirectory, VuoCompilerIssues *issues)
6043 {
6044  try
6045  {
6046  string compositionPath = (workingDirectory.empty() ? "." : workingDirectory) + "/UntitledComposition.vuo";
6047  VuoCompiler compiler(compositionPath);
6048  string compiledCompositionPath = VuoFileUtilities::makeTmpFile("VuoRunnerComposition", "bc");
6049  compiler.compileCompositionString(composition, compiledCompositionPath, true, issues);
6050  string linkedCompositionPath = VuoFileUtilities::makeTmpFile("VuoRunnerComposition", "dylib");
6051  compiler.linkCompositionToCreateDynamicLibrary(compiledCompositionPath, linkedCompositionPath, Optimization_FastBuild);
6052  remove(compiledCompositionPath.c_str());
6053  return VuoRunner::newCurrentProcessRunnerFromDynamicLibrary(linkedCompositionPath, workingDirectory, true);
6054  }
6055  catch (VuoCompilerException &e)
6056  {
6057  if (issues != e.getIssues())
6058  issues->append(e.getIssues());
6059  return NULL;
6060  }
6061 }
6062 
6064 {
6065  pendingDataQueue = dispatch_queue_create("org.vuo.compiler.delegate.pending", 0);
6066 }
6067 
6069 {
6070  LoadedModulesData *data = dequeueData();
6071  data->release();
6072 }
6073 
6077 void VuoCompilerDelegate::enqueueData(LoadedModulesData *data)
6078 {
6079  dispatch_sync(pendingDataQueue, ^{
6080  pendingData.push_back(data);
6081  });
6082 }
6083 
6087 VuoCompilerDelegate::LoadedModulesData * VuoCompilerDelegate::dequeueData(void)
6088 {
6089  __block LoadedModulesData *ret;
6090  dispatch_sync(pendingDataQueue, ^{
6091  ret = pendingData.front();
6092  pendingData.pop_front();
6093  });
6094  return ret;
6095 }
6096 
6101 VuoCompilerDelegate::LoadedModulesData::LoadedModulesData(const set< pair<VuoCompilerModule *, VuoCompilerModule *> > &modulesModified,
6102  const set<VuoCompilerModule *> &modulesRemoved, VuoCompilerIssues *issues)
6103 {
6104  referenceCountQueue = dispatch_queue_create("org.vuo.compiler.delegate.reference", 0);
6105  referenceCount = 0;
6106 
6107  this->modulesModified = modulesModified;
6108  this->modulesRemoved = modulesRemoved;
6109  this->issues = issues;
6110 }
6111 
6115 VuoCompilerDelegate::LoadedModulesData::~LoadedModulesData(void)
6116 {
6117  delete issues;
6118 
6119  for (set< pair<VuoCompilerModule *, VuoCompilerModule *> >::iterator i = modulesModified.begin(); i != modulesModified.end(); ++i)
6120  VuoCompiler::destroyModule((*i).first);
6121 
6122  for (set<VuoCompilerModule *>::iterator i = modulesRemoved.begin(); i != modulesRemoved.end(); ++i)
6124 }
6125 
6129 void VuoCompilerDelegate::LoadedModulesData::retain(void)
6130 {
6131  dispatch_sync(referenceCountQueue, ^{
6132  ++referenceCount;
6133  });
6134 }
6135 
6139 void VuoCompilerDelegate::LoadedModulesData::release(void)
6140 {
6141  dispatch_sync(referenceCountQueue, ^{
6142  --referenceCount;
6143  if (referenceCount == 0) {
6144  delete this;
6145  }
6146  });
6147 }