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  compilerModule->setBuiltIn( isBuiltIn() );
1783 
1784  return compilerModule;
1785  }
1786  else
1787  {
1788  destroyLlvmModule(module);
1789  }
1790  }
1791 
1792  return NULL;
1793 }
1794 
1800 void VuoCompiler::Environment::replaceNodeClass(VuoCompilerNodeClass *newNodeClass)
1801 {
1802  string moduleKey = newNodeClass->getBase()->getModuleKey();
1803 
1804  VuoCompilerNodeClass *oldNodeClass = nodeClasses[moduleKey];
1805  if (oldNodeClass)
1806  {
1807  removeFromDependencyGraph(oldNodeClass);
1808  destroyModule(oldNodeClass);
1809  }
1810 
1811  nodeClasses[moduleKey] = newNodeClass;
1812  addToDependencyGraph(newNodeClass);
1813  modulesChanged();
1814 }
1815 
1816 void VuoCompiler::Environment::addToDependencyGraph(VuoCompilerModule *module)
1817 {
1818  DependencyGraphVertex *vertex = DependencyGraphVertex::vertexForDependency(module->getPseudoBase()->getModuleKey(), dependencyGraph);
1819  dependencyGraph->addVertex(vertex);
1820 
1821  vertex->setEnvironment(this);
1822 
1823  VuoCompilerTargetSet compositionTargets;
1824  compositionTargets.restrictToCurrentOperatingSystemVersion();
1825  vertex->setCompatible( module->getCompatibleTargets().isCompatibleWithAllOf(compositionTargets) );
1826 
1827  set<string> dependencies = module->getDependencies();
1828  for (set<string>::iterator i = dependencies.begin(); i != dependencies.end(); ++i)
1829  {
1830  DependencyGraphVertex *depVertex = DependencyGraphVertex::vertexForDependency(*i, dependencyGraph);
1831  dependencyGraph->addEdge(vertex, depVertex);
1832  }
1833 }
1834 
1835 void VuoCompiler::Environment::removeFromDependencyGraph(VuoCompilerModule *module)
1836 {
1837  DependencyGraphVertex *vertex = DependencyGraphVertex::vertexForDependency(module->getPseudoBase()->getModuleKey(), dependencyGraph);
1838  dependencyGraph->removeVertex(vertex);
1839 }
1840 
1851 void VuoCompiler::Environment::reifyPortTypes(const map<string, VuoCompilerType *> &inheritedTypes)
1852 {
1853  map<string, VuoCompilerType *> availableTypes = inheritedTypes;
1854  availableTypes.insert(types.begin(), types.end());
1855 
1856  for (map<string, VuoCompilerNodeClass *>::const_iterator i = nodeClasses.begin(); i != nodeClasses.end(); ++i)
1857  {
1858  VuoNodeClass *nodeClass = i->second->getBase();
1859 
1860  vector<VuoPortClass *> inputPortClasses = nodeClass->getInputPortClasses();
1861  vector<VuoPortClass *> outputPortClasses = nodeClass->getOutputPortClasses();
1862  vector<VuoPortClass *> portClasses;
1863  portClasses.insert(portClasses.end(), inputPortClasses.begin(), inputPortClasses.end());
1864  portClasses.insert(portClasses.end(), outputPortClasses.begin(), outputPortClasses.end());
1865 
1866  for (vector<VuoPortClass *>::iterator j = portClasses.begin(); j != portClasses.end(); ++j)
1867  {
1868  VuoCompilerPortClass *portClass = static_cast<VuoCompilerPortClass *>((*j)->getCompiler());
1869  VuoType *baseType = portClass->getDataVuoType();
1870 
1871  if (baseType && ! baseType->hasCompiler())
1872  {
1873  string typeName = baseType->getModuleKey();
1874  VuoCompilerType *reifiedType = NULL;
1875 
1876  VuoGenericType *genericType = dynamic_cast<VuoGenericType *>(baseType);
1877  if (genericType)
1878  {
1879  reifiedType = VuoCompilerGenericType::newGenericType(genericType, availableTypes);
1880  if (reifiedType) {
1881  genericTypes.insert( static_cast<VuoCompilerGenericType *>(reifiedType) );
1882  }
1883  }
1884  else
1885  {
1886  map<string, VuoCompilerType *>::iterator reifiedTypeIter = availableTypes.find(typeName);
1887  if (reifiedTypeIter != availableTypes.end())
1888  {
1889  delete baseType;
1890  reifiedType = reifiedTypeIter->second;
1891  }
1892  }
1893 
1894  if (reifiedType) {
1895  portClass->setDataVuoType(reifiedType->getBase());
1896  }
1897  }
1898  }
1899 
1900  vector<VuoCompilerTriggerDescription *> triggers = nodeClass->getCompiler()->getTriggerDescriptions();
1901  for (vector<VuoCompilerTriggerDescription *>::iterator j = triggers.begin(); j != triggers.end(); ++j)
1902  {
1903  VuoCompilerTriggerDescription *trigger = *j;
1904  VuoType *baseType = trigger->getDataType();
1905 
1906  if (baseType && ! baseType->hasCompiler())
1907  {
1908  string typeName = baseType->getModuleKey();
1909  map<string, VuoCompilerType *>::iterator reifiedTypeIter = availableTypes.find(typeName);
1910  if (reifiedTypeIter != availableTypes.end())
1911  {
1912  delete baseType;
1913  VuoCompilerType *reifiedType = reifiedTypeIter->second;
1914  trigger->setDataType(reifiedType->getBase());
1915  }
1916  }
1917  }
1918  }
1919 }
1920 
1933 void VuoCompiler::Environment::getCacheableModulesAndDependencies(bool builtIn, bool installed, set<string> &cacheableModulesAndDependencies,
1934  set<string> &dylibsNeededToLinkToThisCache,
1935  set<string> &frameworksNeededToLinkToThisCache)
1936 {
1937  if (isModuleCacheInitialized && ! isModuleCacheableDataDirty)
1938  {
1939  cacheableModulesAndDependencies = moduleCacheContents;
1940  dylibsNeededToLinkToThisCache = moduleCacheDylibs;
1941  frameworksNeededToLinkToThisCache = moduleCacheFrameworks;
1942  return;
1943  }
1944 
1945  VuoCompilerTargetSet compositionTargets;
1946  compositionTargets.restrictToCurrentOperatingSystemVersion();
1947 
1948  // Include all modules…
1949  map<string, VuoCompilerModule *> allModules;
1950  allModules.insert(nodeClasses.begin(), nodeClasses.end());
1951  allModules.insert(types.begin(), types.end());
1952  allModules.insert(libraryModules.begin(), libraryModules.end());
1953 
1954  set<string> dependencies;
1955  for (map<string, VuoCompilerModule *>::iterator i = allModules.begin(); i != allModules.end(); ++i)
1956  {
1957  string moduleKey = i->first;
1958  VuoCompilerModule *module = i->second;
1959 
1960 #if VUO_PRO
1961  // … except Pro modules and modules that depend on them…
1962  if (module->requiresPro())
1963  continue;
1964 #endif
1965 
1966  // … and incompatible modules.
1967  if (! module->getCompatibleTargets().isCompatibleWithAllOf(compositionTargets))
1968  continue;
1969 
1970  cacheableModulesAndDependencies.insert(moduleKey);
1971 
1972  set<string> moduleDependencies = module->getDependencies();
1973  dependencies.insert(moduleDependencies.begin(), moduleDependencies.end());
1974  }
1975 
1976  // For the built-in environment, include Vuo's core dependencies.
1977  if (builtIn && installed)
1978  {
1979  vector<string> coreDependencies = getCoreVuoDependencies();
1980  dependencies.insert(coreDependencies.begin(), coreDependencies.end());
1981  }
1982 
1983  // Include all dependencies of the included module that are located in this environment.
1984  // (All modules are already included, so we only need to handle non-module dependencies.)
1985  for (set<string>::iterator i = dependencies.begin(); i != dependencies.end(); ++i)
1986  {
1987  string dependency = *i;
1988  if (allModules.find(dependency) == allModules.end())
1989  {
1990  if (VuoStringUtilities::endsWith(dependency, ".framework"))
1991  frameworksNeededToLinkToThisCache.insert(dependency);
1992  else
1993  {
1994  string dependencyPath = VuoCompiler::getLibraryPath(dependency, librarySearchPaths);
1995  if (! dependencyPath.empty())
1996  {
1997  if (VuoStringUtilities::endsWith(dependencyPath, ".dylib"))
1998  dylibsNeededToLinkToThisCache.insert(dependencyPath);
1999  else
2000  cacheableModulesAndDependencies.insert(dependency);
2001  }
2002  }
2003  }
2004  }
2005 
2006  moduleCacheSuffix = (installed ? "installed" : "generated");
2007  dylibsNeededToLinkToThisCache.insert(moduleCachePath + "/libVuoModuleCache-" + moduleCacheSuffix + ".dylib");
2008 
2009  moduleCacheDylibs = dylibsNeededToLinkToThisCache;
2010  moduleCacheFrameworks = frameworksNeededToLinkToThisCache;
2011 }
2012 
2032 void VuoCompiler::Environment::useModuleCache(bool shouldUseExistingCache, VuoCompiler *compiler, set<string> cacheableModulesAndDependencies,
2033  set<string> dylibsNeededToLinkToCaches, set<string> frameworksNeededToLinkToCaches)
2034 {
2035  // Ignore the cache if the `prelinkCache` preference is false.
2036 
2037  static dispatch_once_t checked = 0;
2038  static bool prelinkCache = true;
2039  dispatch_once(&checked, ^{
2040  Boolean valid;
2041  bool result = CFPreferencesGetAppBooleanValue(CFSTR("prelinkCache"), CFSTR("org.vuo.Editor"), &valid);
2042  if (valid)
2043  prelinkCache = result;
2044  });
2045  if (! prelinkCache)
2046  {
2047  VDebugLog("Ignoring the module cache since the 'prelinkCache' preference is false.");
2048  return;
2049  }
2050 
2051  // Don't do anything if this environment doesn't have a cache configured.
2052 
2053  if (moduleCachePath.empty())
2054  return;
2055 
2056  // Don't bother rechecking the cache if the modules in this environment haven't changed.
2057 
2058  string cacheDescription = string() + "the cache of " + moduleCacheSuffix + " modules at '" + moduleCachePath + "'";
2059  if (isModuleCacheInitialized && ! isModuleCacheableDataDirty)
2060  {
2061  VDebugLog("No need to recheck %s.", cacheDescription.c_str());
2062  return;
2063  }
2064 
2065  try
2066  {
2067  VDebugLog("Checking if %s is up-to-date…", cacheDescription.c_str());
2068 
2069  bool isCacheUpToDate = true;
2070 
2071  if (isModuleCacheInitialized && isModuleCacheableDataDirty)
2072  isCacheUpToDate = false;
2073 
2074  isModuleCacheInitialized = true;
2075  isModuleCacheableDataDirty = false;
2076 
2077  const string dylibFileName = "libVuoModuleCache-" + moduleCacheSuffix + ".dylib";
2078  const string indexFileName = "moduleCache-" + moduleCacheSuffix + ".txt";
2079  string dylibPath = moduleCachePath + "/" + dylibFileName;
2080  string indexPath = moduleCachePath + "/" + indexFileName;
2081 
2082  // Create the cache files if they don't already exist. (If they do exist, don't affect the last-modified times.)
2083 
2084  bool dylibFileExists = false;
2085  bool indexFileExists = false;
2086 
2087  bool dirExists = VuoFileUtilities::fileExists(moduleCachePath);
2088  if (dirExists)
2089  {
2090  dylibFileExists = VuoFileUtilities::fileExists(dylibPath);
2091  indexFileExists = VuoFileUtilities::fileExists(indexPath);
2092  }
2093 
2094  if (! (dirExists && dylibFileExists && indexFileExists))
2095  {
2096  if (shouldUseExistingCache)
2097  throw VuoException("Trying to use the existing cache, but the cache doesn't exist.", false);
2098  else
2099  {
2100  if (! dirExists)
2101  VuoFileUtilities::makeDir(moduleCachePath);
2102  if (! indexFileExists)
2103  VuoFileUtilities::createFile(indexPath);
2104  if (! dylibFileExists)
2105  VuoFileUtilities::createFile(dylibPath);
2106 
2107  isCacheUpToDate = false;
2108  }
2109  }
2110 
2111  // Lock the cache for reading.
2112 
2113  VuoFileUtilities::File *fileForLocking;
2114  {
2115  fileForLocking = moduleCacheFileForLocking[dylibPath];
2116  if (! fileForLocking)
2117  {
2118  fileForLocking = new VuoFileUtilities::File(moduleCachePath, dylibFileName);
2119  moduleCacheFileForLocking[dylibPath] = fileForLocking;
2120  }
2121 
2122  if (!fileForLocking->lockForReading())
2123  VDebugLog("\tWarning: Couldn't lock for reading.");
2124  }
2125 
2126  // Check if the dylib looks remotely valid.
2127 
2128  if (isCacheUpToDate)
2129  {
2130  bool dylibHasData = VuoFileUtilities::fileContainsReadableData(dylibPath);
2131  if (! dylibHasData)
2132  {
2133  if (shouldUseExistingCache)
2134  throw VuoException("Trying to use the existing cache, but the cache doesn't contain readable data.", false);
2135  else
2136  isCacheUpToDate = false;
2137  }
2138  }
2139 
2140  // List the items actually in the cache, according to its index.
2141 
2142  const char separator = '\n';
2143  if (isCacheUpToDate || shouldUseExistingCache)
2144  {
2145  VuoFileUtilities::File indexFile(moduleCachePath, indexFileName);
2146  string index = indexFile.getContentsAsString();
2147  vector<string> actualIndex = VuoStringUtilities::split(index, separator);
2148 
2149  moduleCacheContents.clear();
2150  moduleCacheContents.insert(actualIndex.begin(), actualIndex.end());
2151 
2152  if (shouldUseExistingCache)
2153  {
2154  isModuleCacheAvailable = true;
2155  return;
2156  }
2157  }
2158 
2159  // Check if the list of actual items matches the list of expected items.
2160 
2161  if (isCacheUpToDate)
2162  {
2163  if (moduleCacheContents.size() != cacheableModulesAndDependencies.size())
2164  isCacheUpToDate = false;
2165  else
2166  {
2167  set<string> contentsInBoth;
2168  std::set_intersection(moduleCacheContents.begin(), moduleCacheContents.end(),
2169  cacheableModulesAndDependencies.begin(), cacheableModulesAndDependencies.end(),
2170  std::inserter(contentsInBoth, contentsInBoth.begin()));
2171 
2172  if (contentsInBoth.size() != cacheableModulesAndDependencies.size())
2173  isCacheUpToDate = false;
2174  }
2175  }
2176 
2177  // Check if the cache is newer than all of the modules in it.
2178 
2179  if (isCacheUpToDate)
2180  {
2181  unsigned long cacheLastModified = VuoFileUtilities::getFileLastModifiedInSeconds(dylibPath);
2182 
2183  for (map<string, map<string, ModuleInfo *> >::iterator i = moduleFilesAtSearchPath.begin(); i != moduleFilesAtSearchPath.end(); ++i)
2184  {
2185  for (map<string, ModuleInfo *>::iterator j = i->second.begin(); j != i->second.end(); ++j)
2186  {
2187  if (! j->second->isOlderThan(cacheLastModified))
2188  {
2189  isCacheUpToDate = false;
2190  break;
2191  }
2192  }
2193  }
2194  }
2195 
2196  // If the cache is consistent with this environment, we're done.
2197 
2198  if (isCacheUpToDate)
2199  {
2200  VDebugLog("Up-to-date.");
2201 
2202  isModuleCacheAvailable = true;
2203  return;
2204  }
2205 
2206  // Otherwise, (re)build the cache.
2207 
2208  dispatch_async(moduleCacheBuildingQueue, ^{
2209  VDebugLog("Rebuilding %s…", cacheDescription.c_str());
2210 
2211  set<Module *> modulesToLink;
2212  set<string> librariesToLink;
2213  set<string> frameworksToLink;
2214  {
2215  compiler->getLinkerInputs(cacheableModulesAndDependencies, Optimization_SmallBinary, modulesToLink, librariesToLink, frameworksToLink);
2216 
2217  librariesToLink.insert(dylibsNeededToLinkToCaches.begin(), dylibsNeededToLinkToCaches.end());
2218  set<string>::iterator iter = librariesToLink.find(dylibPath); // getCacheableModulesAndDependencies includes dylibPath, but don't want to link against self
2219  if (iter != librariesToLink.end())
2220  librariesToLink.erase(iter);
2221 
2222  frameworksToLink.insert(frameworksNeededToLinkToCaches.begin(), frameworksNeededToLinkToCaches.end());
2223  }
2224 
2225  bool gotLockForWriting = false;
2226  try
2227  {
2228  // Try to upgrade the file lock for writing.
2229  gotLockForWriting = fileForLocking->lockForWriting(true);
2230  if (! gotLockForWriting)
2231  throw VuoException("The cache file is being used by another process. "
2232  "If any composition windows are open from previous Vuo sessions, quit them. "
2233  "If any processes whose names start with \"VuoComposition\" or one of your composition file names appear in Activity Monitor, force-quit them.",
2234  false);
2235 
2236  // Link the dependencies to create a temporary file.
2237  string dir, file, ext;
2238  VuoFileUtilities::splitPath(dylibFileName, dir, file, ext);
2239  string tmpPath = VuoFileUtilities::makeTmpFile(file, "dylib");
2240  compiler->link(tmpPath, modulesToLink, librariesToLink, frameworksToLink, true);
2241 
2242  // Copy the contents of the temporary file into the cached resources dylib.
2243  // This preserves the lock on the cached resources dylib file (https://b33p.net/kosada/node/12970).
2244  VuoFileUtilities::copyFile(tmpPath, dylibPath, true);
2246 
2248  getVuoFrameworkPath() + "/Helpers/install_name_tool",
2249  "-id",
2250  dylibPath,
2251  dylibPath,
2252  });
2253 
2254  // Write the list of dependencies to the index file.
2255  vector<string> expectedContents(cacheableModulesAndDependencies.begin(), cacheableModulesAndDependencies.end());
2256  string index = VuoStringUtilities::join(expectedContents, separator);
2257  VuoFileUtilities::writeStringToFile(index, indexPath);
2258 
2259  // Downgrade the file lock back to reading.
2260  if (!fileForLocking->lockForReading())
2261  VDebugLog("\tWarning: Couldn't downgrade the lock back to reading.");
2262 
2263  dispatch_sync(environmentQueue, ^{
2264  moduleCacheContents = cacheableModulesAndDependencies;
2265  isModuleCacheAvailable = true;
2266  });
2267  }
2268  catch (VuoException &e)
2269  {
2270  // Downgrade the file lock back to reading.
2271  if (gotLockForWriting)
2272  if (!fileForLocking->lockForReading())
2273  VDebugLog("\tWarning: Couldn't downgrade the lock back to reading.");
2274 
2275  VUserLog("Warning: Couldn't rebuild %s for the \"faster build\" optimization: %s", cacheDescription.c_str(), e.what());
2276  }
2277 
2278  VDebugLog("Done.");
2279  });
2280  }
2281  catch (VuoException &e)
2282  {
2283  VUserLog("Warning: Couldn't use %s for the \"faster build\" optimization: %s", cacheDescription.c_str(), e.what());
2284  }
2285 }
2286 
2290 void VuoCompiler::Environment::waitForModuleCachesToBuild(void)
2291 {
2292  dispatch_sync(moduleCacheBuildingQueue, ^{});
2293 }
2294 
2304 bool VuoCompiler::Environment::findInModuleCache(const string &moduleOrDependency, string &cachePath)
2305 {
2306  if (isModuleCacheAvailable && moduleCacheContents.find(moduleOrDependency) != moduleCacheContents.end())
2307  {
2308  cachePath = moduleCachePath + "/libVuoModuleCache-" + moduleCacheSuffix + ".dylib";
2309  return true;
2310  }
2311 
2312  return false;
2313 }
2314 
2321 void VuoCompiler::Environment::modulesChanged(void)
2322 {
2323  isModuleCacheableDataDirty = true;
2324  isModuleCacheAvailable = false;
2325 }
2326 
2330 bool VuoCompiler::Environment::isBuiltIn()
2331 {
2332  return this == sharedEnvironments[0][0];
2333 }
2334 
2338 string VuoCompiler::Environment::getName()
2339 {
2340  if (isBuiltIn())
2341  return "builtin";
2342  else if (this == sharedEnvironments[0][1])
2343  return "builtin (generated)";
2344  else if (this == sharedEnvironments[1][0])
2345  return "system";
2346  else if (this == sharedEnvironments[1][1])
2347  return "system (generated)";
2348  else if (this == sharedEnvironments[2][0])
2349  return "user";
2350  else if (this == sharedEnvironments[2][1])
2351  return "user (generated)";
2352  return "composition-local";
2353 }
2354 
2358 void VuoCompiler::applyToInstalledEnvironments(void (^doForEnvironment)(Environment *))
2359 {
2360  dispatch_sync(environmentQueue, ^{
2361  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i) {
2362  doForEnvironment((*i)[0]);
2363  }
2364  });
2365 }
2366 
2370 void VuoCompiler::applyToInstalledEnvironments(void (^doForEnvironment)(Environment *, bool *, string), bool *result, string arg)
2371 {
2372  dispatch_sync(environmentQueue, ^{
2373  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i) {
2374  doForEnvironment((*i)[0], result, arg);
2375  }
2376  });
2377 }
2378 
2382 void VuoCompiler::applyToAllEnvironments(void (^doForEnvironment)(Environment *environment))
2383 {
2384  dispatch_sync(environmentQueue, ^{
2385  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i) {
2386  for (vector<Environment *>::iterator j = i->begin(); j != i->end(); ++j) {
2387  doForEnvironment(*j);
2388  }
2389  }
2390  });
2391 }
2392 
2401 VuoCompiler::VuoCompiler(const string &compositionPath)
2402 {
2403 #if VUO_PRO
2404  VuoCompiler_Pro();
2405 #endif
2406 
2407  shouldLoadAllModules = true;
2408  hasLoadedAllModules = false;
2409  modulesToLoadQueue = dispatch_queue_create("org.vuo.compiler.modules", NULL);
2410  moduleCacheBuilding = dispatch_group_create();
2411  dependencyGraph = NULL;
2412  compositionDependencyGraph = NULL;
2413  isVerbose = false;
2414  _shouldShowSplashWindow = false;
2415  delegate = NULL;
2416  delegateQueue = dispatch_queue_create("org.vuo.compiler.delegate", NULL);
2417 
2418  string vuoFrameworkPath = getVuoFrameworkPath();
2419  if (! vuoFrameworkPath.empty())
2420  clangPath = vuoFrameworkPath + "/Helpers/clang";
2421  else
2422  clangPath = llvm::sys::Path(StringRef(LLVM_ROOT "/bin/clang"));
2423 
2424  dispatch_sync(environmentQueue, ^{
2425  if (sharedEnvironments.empty())
2426  {
2427  sharedEnvironments = vector< vector<Environment *> >(3, vector<Environment *>(2, NULL));
2428  for (vector< vector<Environment *> >::iterator i = sharedEnvironments.begin(); i != sharedEnvironments.end(); ++i) {
2429  for (vector<Environment *>::iterator j = i->begin(); j != i->end(); ++j) {
2430  *j = new Environment();
2431  }
2432  }
2433 
2434  vector<string> builtInModuleSearchPaths = Environment::getBuiltInModuleSearchPaths();
2435  for (vector<string>::iterator i = builtInModuleSearchPaths.begin(); i != builtInModuleSearchPaths.end(); ++i) {
2436  sharedEnvironments[0][0]->addModuleSearchPath(*i, false);
2437  }
2438 
2439  vector<string> builtInHeaderSearchPaths = Environment::getBuiltInHeaderSearchPaths();
2440  for (vector<string>::iterator i = builtInHeaderSearchPaths.begin(); i != builtInHeaderSearchPaths.end(); ++i) {
2441  sharedEnvironments[0][0]->addHeaderSearchPath(*i);
2442  }
2443 
2444  vector<string> builtInLibrarySearchPaths = Environment::getBuiltInLibrarySearchPaths();
2445  for (vector<string>::iterator i = builtInLibrarySearchPaths.begin(); i != builtInLibrarySearchPaths.end(); ++i) {
2446  sharedEnvironments[0][0]->addLibrarySearchPath(*i);
2447  }
2448 
2449  vector<string> builtInFrameworkSearchPaths = Environment::getBuiltInFrameworkSearchPaths();
2450  for (vector<string>::iterator i = builtInFrameworkSearchPaths.begin(); i != builtInFrameworkSearchPaths.end(); ++i) {
2451  sharedEnvironments[0][0]->addFrameworkSearchPath(*i);
2452  }
2453 
2454  // Allow system administrator to override Vuo.framework modules
2455  sharedEnvironments[1][0]->addModuleSearchPath(VuoFileUtilities::getSystemModulesPath());
2456  sharedEnvironments[1][0]->addLibrarySearchPath(VuoFileUtilities::getSystemModulesPath());
2457 
2458  // Allow user to override Vuo.framework and system-wide modules
2459  sharedEnvironments[2][0]->addModuleSearchPath(VuoFileUtilities::getUserModulesPath());
2460  sharedEnvironments[2][0]->addLibrarySearchPath(VuoFileUtilities::getUserModulesPath());
2461 
2462  // Set up module cache paths.
2463  // Since the built-in module caches are part of Vuo.framework (put there by `generateBuiltInModuleCaches`),
2464  // only attempt to use module caches if Vuo.framework exists.
2465  string vuoFrameworkPath = getVuoFrameworkPath();
2466  if (! vuoFrameworkPath.empty())
2467  {
2468  vector<string> moduleCachePaths(3);
2469  moduleCachePaths[0] = vuoFrameworkPath + "/Modules/Builtin";
2470  moduleCachePaths[1] = VuoFileUtilities::getCachePath() + "/System";
2471  moduleCachePaths[2] = VuoFileUtilities::getCachePath() + "/User";
2472 
2473  for (size_t i = 0; i < moduleCachePaths.size(); ++i)
2474  {
2475  string moduleCachePath = moduleCachePaths[i];
2476 
2477  sharedEnvironments[i][0]->setModuleCachePath(moduleCachePath);
2478  sharedEnvironments[i][0]->addModuleSearchPath(moduleCachePath + "/Modules", false);
2479 
2480  sharedEnvironments[i][1]->setModuleCachePath(moduleCachePath);
2481  }
2482  }
2483  }
2484  });
2485 
2486  setCompositionPath(compositionPath);
2487 }
2488 
2493 {
2494  dispatch_group_wait(moduleCacheBuilding, DISPATCH_TIME_FOREVER);
2495  dispatch_release(moduleCacheBuilding);
2496 
2497  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
2498  (*i)[0]->removeCompilerToNotify(this);
2499 
2500  dispatch_sync(delegateQueue, ^{});
2501 
2502  dispatch_release(modulesToLoadQueue);
2503  dispatch_release(delegateQueue);
2504 
2505  delete dependencyGraph;
2506  delete compositionDependencyGraph;
2507 }
2508 
2512 void VuoCompiler::reset(void)
2513 {
2514  dispatch_group_wait(moduleSourceCompilersExist, DISPATCH_TIME_FOREVER);
2515 
2516  for (vector< vector<Environment *> >::iterator i = sharedEnvironments.begin(); i != sharedEnvironments.end(); ++i)
2517  {
2518  (*i)[0]->stopWatchingModuleSearchPaths();
2519  dispatch_sync((*i)[0]->moduleSearchPathContentsChangedQueue, ^{});
2520  }
2521 
2522  for (map< string, vector<Environment *> >::iterator i = environmentsForCompositionFamily.begin(); i != environmentsForCompositionFamily.end(); ++i)
2523  {
2524  (i->second)[0]->stopWatchingModuleSearchPaths();
2525  dispatch_sync((i->second)[0]->moduleSearchPathContentsChangedQueue, ^{});
2526  }
2527 
2528  for (vector< vector<Environment *> >::iterator i = sharedEnvironments.begin(); i != sharedEnvironments.end(); ++i)
2529  for (vector<Environment *>::iterator j = i->begin(); j != i->end(); ++j)
2530  delete *j;
2531 
2532  for (map< string, vector<Environment *> >::iterator i = environmentsForCompositionFamily.begin(); i != environmentsForCompositionFamily.end(); ++i)
2533  for (vector<Environment *>::iterator j = i->second.begin(); j != i->second.end(); ++j)
2534  delete *j;
2535 
2536  sharedEnvironments.clear();
2537  environmentsForCompositionFamily.clear();
2538 }
2539 
2546 {
2547  dispatch_async(delegateQueue, ^{
2548  this->delegate = delegate;
2549  });
2550 }
2551 
2561 void VuoCompiler::setCompositionPath(const string &compositionPath)
2562 {
2563  string compositionModulesDir;
2564  string compositionBaseDir;
2565  lastCompositionIsSubcomposition = false;
2566  if (! compositionPath.empty())
2567  {
2568  compositionModulesDir = VuoFileUtilities::getCompositionLocalModulesPath(compositionPath);
2569 
2570  string file, ext;
2571  VuoFileUtilities::splitPath(compositionModulesDir, compositionBaseDir, file, ext);
2572  VuoFileUtilities::canonicalizePath(compositionBaseDir);
2573 
2574  string compositionDir;
2575  VuoFileUtilities::splitPath(compositionPath, compositionDir, file, ext);
2576  VuoFileUtilities::canonicalizePath(compositionDir);
2577  lastCompositionIsSubcomposition = (compositionDir == compositionModulesDir);
2578  }
2579 
2580  // Set up `environments` to contain all environments available to this compiler, in order from broadest to narrowest.
2581 
2582  dispatch_sync(environmentQueue, ^{
2583  if (! environments.empty() && compositionBaseDir == lastCompositionBaseDir) {
2584  return;
2585  }
2586  lastCompositionBaseDir = compositionBaseDir;
2587 
2588  // Clear out the existing environments for this compiler.
2589 
2590  Environment *oldCompositionFamilyInstalledEnvironment = nullptr;
2591  vector<Environment *> compositionEnvironments;
2592  if (! environments.empty())
2593  {
2594  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i) {
2595  (*i)[0]->removeCompilerToNotify(this);
2596  }
2597 
2598  if (environments.size() >= 5) {
2599  oldCompositionFamilyInstalledEnvironment = environments[3][0];
2600  }
2601 
2602  compositionEnvironments = environments.back();
2603 
2604  environments.clear();
2605  }
2606 
2607  // If the composition is located in one of the shared environments (built-in, system, user),
2608  // add that shared environment and any shared environments at broader scope to the compiler.
2609  // If the composition is not in a shared environment, add all of the shared environments to the compiler.
2610 
2611  bool isCompositionInSharedEnvironment = false;
2612  for (vector< vector<Environment *> >::iterator i = sharedEnvironments.begin(); i != sharedEnvironments.end(); ++i)
2613  {
2614  environments.push_back(*i);
2615 
2616  vector<string> moduleSearchPaths = (*i)[0]->getModuleSearchPaths();
2617  for (vector<string>::iterator j = moduleSearchPaths.begin(); j != moduleSearchPaths.end(); ++j)
2618  {
2619  string moduleSearchPath = *j;
2620  VuoFileUtilities::canonicalizePath(moduleSearchPath);
2621  if (moduleSearchPath == compositionModulesDir)
2622  {
2623  isCompositionInSharedEnvironment = true;
2624  break;
2625  }
2626  }
2627 
2628  if (isCompositionInSharedEnvironment) {
2629  break;
2630  }
2631  }
2632 
2633  // If the composition is not in a shared environment, add the composition-family environment to the compiler.
2634 
2635  Environment *newCompositionFamilyInstalledEnvironment = nullptr;
2636  if (! isCompositionInSharedEnvironment && ! compositionPath.empty())
2637  {
2638  vector<Environment *> compositionFamilyEnvironments = environmentsForCompositionFamily[compositionBaseDir];
2639  if (compositionFamilyEnvironments.empty())
2640  {
2641  compositionFamilyEnvironments = vector<Environment *>(2, NULL);
2642  compositionFamilyEnvironments[0] = new Environment();
2643  compositionFamilyEnvironments[1] = new Environment();
2644  environmentsForCompositionFamily[compositionBaseDir] = compositionFamilyEnvironments;
2645 
2646  // Allow the user to place modules/subcompositions in a Modules folder inside the composition folder.
2647 
2648  compositionFamilyEnvironments[0]->addModuleSearchPath(compositionModulesDir);
2649 
2650  // Locate this environment's cache in a folder whose name is the composition folder path with
2651  // slashes replaced with Unicode Modifier Letter Colon.
2652  string moduleCachePath = getCachePathForComposition(compositionBaseDir);
2653 
2654  compositionFamilyEnvironments[0]->setModuleCachePath(moduleCachePath);
2655  compositionFamilyEnvironments[0]->addModuleSearchPath(moduleCachePath + "/Modules", false);
2656 
2657  compositionFamilyEnvironments[1]->setModuleCachePath(moduleCachePath);
2658  }
2659  environments.push_back(compositionFamilyEnvironments);
2660 
2661  newCompositionFamilyInstalledEnvironment = compositionFamilyEnvironments[0];
2662  }
2663 
2664  // Add the composition environment to the compiler (or add it back in if it already existed).
2665 
2666  if (compositionEnvironments.empty())
2667  {
2668  compositionEnvironments = vector<Environment *>(2, NULL);
2669  compositionEnvironments[0] = new Environment();
2670  compositionEnvironments[1] = new Environment();
2671  }
2672  environments.push_back(compositionEnvironments);
2673 
2674  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i) {
2675  (*i)[0]->addCompilerToNotify(this);
2676  }
2677 
2678  delete dependencyGraph;
2679  delete compositionDependencyGraph;
2680  dependencyGraph = makeDependencyNetwork(environments, ^VuoDirectedAcyclicGraph * (Environment *env) { return env->getDependencyGraph(); });
2681  compositionDependencyGraph = makeDependencyNetwork(environments, ^VuoDirectedAcyclicGraph * (Environment *env) { return env->getCompositionDependencyGraph(); });
2682 
2683  // If the compiler has a different local Modules directory than before, notify the compiler's delegate
2684  // of composition-family modules that are newly available/unavailable.
2685 
2686  if (oldCompositionFamilyInstalledEnvironment != newCompositionFamilyInstalledEnvironment)
2687  {
2688  auto getModules = [] (Environment *env)
2689  {
2690  map<string, VuoCompilerModule *> modules;
2691  if (env)
2692  {
2693  for (auto i : env->getNodeClasses()) {
2694  modules.insert(i);
2695  }
2696  for (auto i : env->getTypes()) {
2697  modules.insert(i);
2698  }
2699  for (auto i : env->getLibraryModules()) {
2700  modules.insert(i);
2701  }
2702  }
2703  return modules;
2704  };
2705 
2706  map<string, VuoCompilerModule *> modulesAdded = getModules(newCompositionFamilyInstalledEnvironment);
2707  map<string, VuoCompilerModule *> modulesRemoved = getModules(oldCompositionFamilyInstalledEnvironment);
2708 
2709  map<string, pair<VuoCompilerModule *, VuoCompilerModule *> > modulesModified;
2710  for (map<string, VuoCompilerModule *>::iterator add = modulesAdded.begin(); add != modulesAdded.end(); )
2711  {
2712  map<string, VuoCompilerModule *>::iterator rem = modulesRemoved.find(add->first);
2713  if (rem != modulesRemoved.end())
2714  {
2715  modulesModified[add->first] = make_pair(rem->second, add->second);
2716  modulesAdded.erase(add++);
2717  modulesRemoved.erase(rem);
2718  }
2719  else
2720  {
2721  ++add;
2722  }
2723  }
2724 
2725  if (! (modulesAdded.empty() && modulesModified.empty() && modulesRemoved.empty()) )
2726  {
2727  VuoCompilerIssues *issues = new VuoCompilerIssues();
2728  VuoCompilerDelegate::LoadedModulesData *delegateData = new VuoCompilerDelegate::LoadedModulesData(set< pair<VuoCompilerModule *, VuoCompilerModule *> >(), set<VuoCompilerModule *>(), issues);
2729  delegateData->retain();
2730 
2731  Environment *scopeEnvironment = newCompositionFamilyInstalledEnvironment;
2732  if (! scopeEnvironment) {
2733  scopeEnvironment = compositionEnvironments.at(0);
2734  }
2735 
2736  loadedModules(modulesAdded, modulesModified, modulesRemoved, issues, delegateData, scopeEnvironment);
2737  }
2738  }
2739  });
2740 }
2741 
2742 // environmentQueue
2743 VuoDirectedAcyclicNetwork * VuoCompiler::makeDependencyNetwork(const vector< vector<Environment *> > &environments,
2744  VuoDirectedAcyclicGraph * (^graphForEnvironment)(Environment *))
2745 {
2746  if (!graphForEnvironment)
2747  return NULL;
2748 
2750 
2751  for (vector< vector<Environment *> >::const_iterator i = environments.begin(); i != environments.end(); ++i)
2752  {
2753  // Installed environment depends on generated environment in same scope.
2754 
2755  network->addEdge(graphForEnvironment(i->at(0)), graphForEnvironment(i->at(1)));
2756 
2757  // Installed and generated environments depend on installed environments in broader scopes.
2758 
2759  for (vector< vector<Environment *> >::const_iterator ii = environments.begin(); ii != i; ++ii)
2760  {
2761  network->addEdge(graphForEnvironment(i->at(0)), graphForEnvironment(ii->at(0)));
2762  network->addEdge(graphForEnvironment(i->at(1)), graphForEnvironment(ii->at(0)));
2763  }
2764  }
2765 
2766  return network;
2767 }
2768 
2789 void VuoCompiler::loadModulesIfNeeded(const set<string> &moduleKeys)
2790 {
2791  __block bool willLoadAllModules = false;
2792  if (moduleKeys.empty())
2793  {
2794  dispatch_sync(modulesToLoadQueue, ^{
2795  if (shouldLoadAllModules && ! hasLoadedAllModules) {
2796  willLoadAllModules = true;
2797  hasLoadedAllModules = true;
2798  }
2799  });
2800  }
2801 
2802  if (! willLoadAllModules && moduleKeys.empty())
2803  return;
2804 
2805  // Load modules and start sources compiling.
2806 
2807  __block set<dispatch_group_t> sourcesLoading;
2808  dispatch_sync(environmentQueue, ^{
2809  sourcesLoading = loadModulesAndSources(moduleKeys, set<string>(), set<string>(),
2810  moduleKeys, set<string>(), set<string>(),
2811  willLoadAllModules, false, nullptr, nullptr, nullptr, "");
2812  });
2813 
2814  // Wait for sources to finish compiling and their modules to be loaded,
2815  // to ensure that `getNodeClass(subcomposition)` finds the subcomposition node class.
2816 
2817  for (set<dispatch_group_t>::iterator i = sourcesLoading.begin(); i != sourcesLoading.end(); ++i)
2818  {
2819  dispatch_group_wait(*i, DISPATCH_TIME_FOREVER);
2820  dispatch_release(*i);
2821  }
2822 }
2823 
2832 set<dispatch_group_t> VuoCompiler::loadModulesAndSources(const set<string> &modulesAddedKeys, const set<string> &modulesModifiedKeys, const set<string> &modulesRemovedKeys,
2833  const set<string> &sourcesAddedKeys, const set<string> &sourcesModifiedKeys, const set<string> &sourcesRemovedKeys,
2834  bool willLoadAllModules, bool shouldRecompileSourcesIfUnchanged,
2835  Environment *currentEnvironment, VuoCompilerIssues *issuesForCurrentEnvironment,
2836  std::function<void(void)> moduleLoadedCallback, const string &moduleAddedOrModifiedSourceCode)
2837 {
2838  //VLog("C=%p E=%p -- %lu %lu %lu %lu %lu %lu %i %p %p", this, currentEnvironment,
2839  //modulesAddedKeys.size(), modulesModifiedKeys.size(), modulesRemovedKeys.size(),
2840  //sourcesAddedKeys.size(), sourcesModifiedKeys.size(), sourcesRemovedKeys.size(),
2841  //willLoadAllModules, issuesForCurrentEnvironment, moduleLoadedCallback);
2842  //if (modulesAddedKeys.size() == 1) VLog(" %s", modulesAddedKeys.begin()->c_str());
2843  //if (modulesModifiedKeys.size() == 1) VLog(" %s", modulesModifiedKeys.begin()->c_str());
2844  //if (modulesRemovedKeys.size() == 1) VLog(" %s", modulesRemovedKeys.begin()->c_str());
2845 
2846  // Organize the modules, source files, and issues by environment.
2847 
2848  map<Environment *, set<string> > modulesAdded;
2849  map<Environment *, set<string> > modulesModified;
2850  map<Environment *, set<string> > modulesRemoved;
2851  map<Environment *, set<string> > sourcesAdded;
2852  map<Environment *, set<string> > sourcesModified;
2853  map<Environment *, set<string> > sourcesRemoved;
2854  map<Environment *, set<string> > potentialSpecializedModules;
2855 
2856  if (currentEnvironment)
2857  {
2858  modulesAdded[currentEnvironment] = modulesAddedKeys;
2859  modulesModified[currentEnvironment] = modulesModifiedKeys;
2860  modulesRemoved[currentEnvironment] = modulesRemovedKeys;
2861  sourcesAdded[currentEnvironment] = sourcesAddedKeys;
2862  sourcesModified[currentEnvironment] = sourcesModifiedKeys;
2863  sourcesRemoved[currentEnvironment] = sourcesRemovedKeys;
2864  }
2865  else
2866  {
2867  Environment *genEnv = nullptr;
2868  if (! willLoadAllModules)
2869  {
2870  // If compiling a top-level composition, generated modules that were directly requested (in modulesAddedKeys) are
2871  // added at the composition scope so they won't be cached. This prevents link errors when running multiple
2872  // compositions in the current process (https://b33p.net/kosada/node/14317).
2873 
2874  int scope = environments.size() - (lastCompositionIsSubcomposition ? 2 : 1);
2875  genEnv = environments.at(scope).at(1);
2876  potentialSpecializedModules[genEnv] = modulesAddedKeys;
2877  }
2878 
2879  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
2880  {
2881  Environment *env = (*i).at(0);
2882 
2883  ModuleInfoIterator modulesAddedIter = (willLoadAllModules ? env->listAllModules() : env->listModules(modulesAddedKeys));
2884  ModuleInfoIterator sourcesAddedIter = (willLoadAllModules ? env->listAllSourceFiles() : env->listSourceFiles(sourcesAddedKeys));
2885  ModuleInfoIterator sourcesModifiedIter = (willLoadAllModules ? env->listAllSourceFiles() : env->listSourceFiles(sourcesModifiedKeys));
2886 
2887  ModuleInfo *moduleInfo;
2888  while ((moduleInfo = modulesAddedIter.next()))
2889  {
2890  string moduleKey = moduleInfo->getModuleKey();
2891 
2892  modulesAdded[env].insert(moduleKey);
2893 
2894  if (! willLoadAllModules)
2895  {
2896  auto foundIter = potentialSpecializedModules[genEnv].find(moduleKey);
2897  if (foundIter != potentialSpecializedModules[genEnv].end())
2898  potentialSpecializedModules[genEnv].erase(foundIter);
2899  }
2900  }
2901 
2902  // If a source file and a compiled file for the same module are in the same folder,
2903  // the compiled file takes precedence.
2904  auto isCompiledModuleAtSameSearchPath = [&env] (ModuleInfo *sourceInfo)
2905  {
2906  ModuleInfo *compiledModuleInfo = env->listModule(sourceInfo->getModuleKey());
2907  return (compiledModuleInfo && compiledModuleInfo->getSearchPath() == sourceInfo->getSearchPath());
2908  };
2909 
2910  while ((moduleInfo = sourcesAddedIter.next()))
2911  {
2912  if (isCompiledModuleAtSameSearchPath(moduleInfo))
2913  continue;
2914 
2915  sourcesAdded[env].insert( moduleInfo->getModuleKey() );
2916  }
2917 
2918  while ((moduleInfo = sourcesModifiedIter.next()))
2919  {
2920  if (isCompiledModuleAtSameSearchPath(moduleInfo))
2921  continue;
2922 
2923  sourcesModified[env].insert( moduleInfo->getModuleKey() );
2924  }
2925  }
2926  }
2927 
2928  map<Environment *, VuoCompilerIssues *> issues;
2929  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
2930  {
2931  Environment *env = (*i).at(0);
2932  issues[env] = (env == currentEnvironment && issuesForCurrentEnvironment ? issuesForCurrentEnvironment : new VuoCompilerIssues());
2933  }
2934 
2935  // Check for circular dependencies in sources.
2936 
2937  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
2938  {
2939  Environment *env = (*i).at(0);
2940 
2941  // Check for circular dependencies involving sources being loaded within this environment.
2942  // For circular dependencies involving sources in different environments,
2943  // an error will be reported elsewhere due to one of them being outside of the other's scope.
2944  set<VuoDirectedAcyclicGraph::Vertex *> circularDependencies = env->getCompositionDependencyGraph()->getCycleVertices();
2945 
2946  set<string> sourcesAddedModified;
2947  sourcesAddedModified.insert(sourcesAdded[env].begin(), sourcesAdded[env].end());
2948  sourcesAddedModified.insert(sourcesModified[env].begin(), sourcesModified[env].end());
2949 
2950  for (set<string>::iterator j = sourcesAddedModified.begin(); j != sourcesAddedModified.end(); ++j)
2951  {
2952  string moduleKey = *j;
2953 
2954  bool found = false;
2955  for (set<VuoDirectedAcyclicGraph::Vertex *>::iterator k = circularDependencies.begin(); k != circularDependencies.end(); ++k)
2956  {
2957  DependencyGraphVertex *vertex = static_cast<DependencyGraphVertex *>(*k);
2958  if (vertex->getDependency() == moduleKey)
2959  {
2960  found = true;
2961  break;
2962  }
2963  }
2964 
2965  if (found)
2966  {
2967  sourcesAdded[env].erase(moduleKey);
2968  sourcesModified[env].erase(moduleKey);
2969 
2970  VuoCompilerIssue issue(VuoCompilerIssue::Error, "compiling subcomposition", moduleKey,
2971  "Subcomposition contains itself",
2972  "%moduleKey contains an instance of itself, "
2973  "or contains another subcomposition that contains an instance of %moduleKey.");
2974  issue.setModuleKey(moduleKey);
2975 
2976  ModuleInfo *sourceInfo = env->listSourceFile(moduleKey);
2977  if (sourceInfo)
2978  issue.setFilePath(sourceInfo->getFile()->path());
2979 
2980  issues[env]->append(issue);
2981  }
2982  }
2983  }
2984 
2985  // Find all modules and sources that depend on the modules and sources being modified or removed.
2986  // Those that belong to one of this compiler's environments are used in subsequent stages.
2987  // Those that belong to some other environment are scheduled to be handled by a separate call to this function.
2988 
2989  map<Environment *, set<string> > modulesDepOnModulesModified;
2990  map<Environment *, set<string> > sourcesDepOnModulesModified;
2991  map<Environment *, set<string> > modulesDepOnModulesRemoved;
2992  map<Environment *, set<string> > sourcesDepOnModulesRemoved;
2993 
2994  {
2995  __block map<Environment *, set<string> > modulesDepOnModulesModified_otherCompiler;
2996  __block map<Environment *, set<string> > sourcesDepOnModulesModified_otherCompiler;
2997  __block map<Environment *, set<string> > modulesDepOnModulesRemoved_otherCompiler;
2998  __block map<Environment *, set<string> > sourcesDepOnModulesRemoved_otherCompiler;
2999 
3000  vector<VuoDirectedAcyclicNetwork *> searchDependencyGraphs;
3001  searchDependencyGraphs.push_back(dependencyGraph);
3002  for (map<string, vector<Environment *> >::iterator ii = environmentsForCompositionFamily.begin(); ii != environmentsForCompositionFamily.end(); ++ii)
3003  {
3004  vector< vector<Environment *> > otherEnvs = sharedEnvironments;
3005  otherEnvs.push_back(ii->second);
3006  VuoDirectedAcyclicNetwork *other = makeDependencyNetwork(otherEnvs, ^VuoDirectedAcyclicGraph * (Environment *env) { return env->getDependencyGraph(); });
3007  searchDependencyGraphs.push_back(other);
3008  }
3009 
3010  VuoDirectedAcyclicGraph *currentEnvironmentDependencyGraph = (currentEnvironment ? currentEnvironment->getDependencyGraph() : nullptr);
3011 
3012  findDependentModulesAndSources(modulesModified, searchDependencyGraphs, currentEnvironmentDependencyGraph,
3013  modulesDepOnModulesModified, modulesDepOnModulesModified_otherCompiler,
3014  sourcesDepOnModulesModified, sourcesDepOnModulesModified_otherCompiler);
3015 
3016  findDependentModulesAndSources(modulesRemoved, searchDependencyGraphs, currentEnvironmentDependencyGraph,
3017  modulesDepOnModulesRemoved, modulesDepOnModulesRemoved_otherCompiler,
3018  sourcesDepOnModulesRemoved, sourcesDepOnModulesRemoved_otherCompiler);
3019 
3020  set<Environment *> otherEnvironments;
3021  for (map<Environment *, set<string> >::iterator i = modulesDepOnModulesModified_otherCompiler.begin(); i != modulesDepOnModulesModified_otherCompiler.end(); ++i)
3022  otherEnvironments.insert(i->first);
3023  for (map<Environment *, set<string> >::iterator i = sourcesDepOnModulesModified_otherCompiler.begin(); i != sourcesDepOnModulesModified_otherCompiler.end(); ++i)
3024  otherEnvironments.insert(i->first);
3025  for (map<Environment *, set<string> >::iterator i = modulesDepOnModulesRemoved_otherCompiler.begin(); i != modulesDepOnModulesRemoved_otherCompiler.end(); ++i)
3026  otherEnvironments.insert(i->first);
3027  for (map<Environment *, set<string> >::iterator i = sourcesDepOnModulesRemoved_otherCompiler.begin(); i != sourcesDepOnModulesRemoved_otherCompiler.end(); ++i)
3028  otherEnvironments.insert(i->first);
3029 
3030  for (Environment *env : otherEnvironments)
3031  {
3032  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
3033  string moduleSearchPath = env->getModuleSearchPaths().front();
3034  VuoCompiler *otherCompiler = new VuoCompiler(moduleSearchPath + "/unused");
3035 
3036  dispatch_sync(environmentQueue, ^{
3037  otherCompiler->loadModulesAndSources(set<string>(), modulesDepOnModulesModified_otherCompiler[env], modulesDepOnModulesRemoved_otherCompiler[env],
3038  set<string>(), sourcesDepOnModulesModified_otherCompiler[env], sourcesDepOnModulesRemoved_otherCompiler[env],
3039  false, true, env, nullptr, nullptr, "");
3040  });
3041 
3042  delete otherCompiler;
3043  });
3044  }
3045  }
3046 
3047  // Unload:
3048  // - modules that have been removed or modified
3049  // - modules that depend on them
3050 
3051  map<Environment *, set<VuoCompilerModule *> > actualModulesRemoved;
3052  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3053  {
3054  Environment *env = (*i).at(0);
3055 
3056  set<string> modulesToUnload;
3057  modulesToUnload.insert(modulesRemoved[env].begin(), modulesRemoved[env].end());
3058  modulesToUnload.insert(modulesModified[env].begin(), modulesModified[env].end());
3059  modulesToUnload.insert(modulesDepOnModulesRemoved[env].begin(), modulesDepOnModulesRemoved[env].end());
3060  modulesToUnload.insert(modulesDepOnModulesModified[env].begin(), modulesDepOnModulesModified[env].end());
3061 
3062  actualModulesRemoved[env] = env->unloadCompiledModules(modulesToUnload);
3063 
3064  if (!env->isBuiltIn() && !actualModulesRemoved[env].empty())
3065  {
3066  set<string> actualModulesRemovedKeys;
3067  for (auto m : actualModulesRemoved[env])
3068  actualModulesRemovedKeys.insert(m->getPseudoBase()->getModuleKey());
3069  VUserLog("Removed from %s environment: %s", env->getName().c_str(), VuoStringUtilities::join(actualModulesRemovedKeys, ", ").c_str());
3070  }
3071  }
3072 
3073  // Load:
3074  // - modules that have been added or modified
3075  // - modules that they depend on
3076  // - modules that depend on them that were just unloaded
3077  // Delete:
3078  // - cached module files in `modulesToLoad` whose source files have been removed
3079 
3080  map<Environment *, set<string> > modulesToLoad;
3081  map<Environment *, map<string, string> > modulesToLoadSourceCode;
3082  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3083  {
3084  Environment *env = (*i).at(0);
3085 
3086  if (! modulesAdded[env].empty())
3087  modulesToLoad[env].insert(modulesAdded[env].begin(), modulesAdded[env].end());
3088  if (! modulesModified[env].empty())
3089  modulesToLoad[env].insert(modulesModified[env].begin(), modulesModified[env].end());
3090  if (! modulesDepOnModulesModified[env].empty())
3091  modulesToLoad[env].insert(modulesDepOnModulesModified[env].begin(), modulesDepOnModulesModified[env].end());
3092 
3093  if (env == currentEnvironment && moduleLoadedCallback)
3094  {
3095  if (modulesAdded[env].size() == 1)
3096  modulesToLoadSourceCode[env][*modulesAdded[env].begin()] = moduleAddedOrModifiedSourceCode;
3097  else if (modulesModified[env].size() == 1)
3098  modulesToLoadSourceCode[env][*modulesModified[env].begin()] = moduleAddedOrModifiedSourceCode;
3099  }
3100  }
3101 
3102  map<Environment *, set<VuoCompilerModule *> > actualModulesAdded;
3103  while (! modulesToLoad.empty())
3104  {
3105  set<string> dependenciesToLoad;
3106  map<Environment *, set<string> > potentialSpecializedDependencies;
3107  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3108  {
3109  Environment *env = (*i).at(0);
3110  Environment *genEnv = (*i).at(1);
3111 
3112  set<VuoCompilerModule *> actualModulesLoaded = env->loadCompiledModules(modulesToLoad[env], modulesToLoadSourceCode[env]);
3113 
3114  actualModulesAdded[env].insert(actualModulesLoaded.begin(), actualModulesLoaded.end());
3115  modulesToLoad.erase(env);
3116 
3117  set<string> actualModulesLoadedKeys;
3118  for (set<VuoCompilerModule *>::iterator j = actualModulesLoaded.begin(); j != actualModulesLoaded.end(); ++j)
3119  {
3120  set<string> dependencies = (*j)->getDependencies();
3121  dependenciesToLoad.insert(dependencies.begin(), dependencies.end());
3122  potentialSpecializedDependencies[genEnv].insert(dependencies.begin(), dependencies.end());
3123  actualModulesLoadedKeys.insert((*j)->getPseudoBase()->getModuleKey());
3124  }
3125 
3126  if (!env->isBuiltIn() && !actualModulesLoadedKeys.empty())
3127  VUserLog("Loaded into %s environment: %s", env->getName().c_str(), VuoStringUtilities::join(actualModulesLoadedKeys, ", ").c_str());
3128  }
3129 
3130  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3131  {
3132  Environment *env = (*i).at(0);
3133 
3134  ModuleInfoIterator dependenciesInEnv = env->listModules(dependenciesToLoad);
3135  ModuleInfo *moduleInfo;
3136  while ((moduleInfo = dependenciesInEnv.next()))
3137  {
3138  modulesToLoad[env].insert( moduleInfo->getModuleKey() );
3139 
3140  for (map<Environment *, set<string> >::iterator j = potentialSpecializedDependencies.begin(); j != potentialSpecializedDependencies.end(); ++j)
3141  {
3142  auto foundIter = j->second.find( moduleInfo->getModuleKey() );
3143  if (foundIter != j->second.end())
3144  j->second.erase(foundIter);
3145  }
3146  }
3147  }
3148 
3149  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3150  {
3151  Environment *genEnv = (*i).at(1);
3152  potentialSpecializedModules[genEnv].insert(potentialSpecializedDependencies[genEnv].begin(), potentialSpecializedDependencies[genEnv].end());
3153  }
3154  }
3155 
3156  // Load asynchronously:
3157  // - specializations of generic modules
3158 
3159  set<dispatch_group_t> specializedModulesLoading;
3160  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3161  {
3162  Environment *genEnv = (*i).at(1);
3163  set<dispatch_group_t> s = genEnv->loadSpecializedModules(potentialSpecializedModules[genEnv], this, llvmQueue);
3164  specializedModulesLoading.insert(s.begin(), s.end());
3165  }
3166 
3167  // Notify those waiting on a source file to be compiled that its module has now been loaded.
3168 
3169  if (moduleLoadedCallback)
3170  moduleLoadedCallback();
3171 
3172  // Move modified modules from `actualModulesAdded` and `actualModulesRemoved` to `actualModulesModified`.
3173 
3174  map<Environment *, set< pair<VuoCompilerModule *, VuoCompilerModule *> > > actualModulesModified;
3175  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3176  {
3177  Environment *env = (*i).at(0);
3178 
3179  for (set<VuoCompilerModule *>::iterator add = actualModulesAdded[env].begin(); add != actualModulesAdded[env].end(); )
3180  {
3181  set<VuoCompilerModule *>::iterator rem;
3182  for (rem = actualModulesRemoved[env].begin(); rem != actualModulesRemoved[env].end(); ++rem)
3183  if ((*rem)->getPseudoBase()->getModuleKey() == (*add)->getPseudoBase()->getModuleKey())
3184  break;
3185 
3186  if (rem != actualModulesRemoved[env].end())
3187  {
3188  actualModulesModified[env].insert( make_pair(*rem, *add) );
3189  actualModulesRemoved[env].erase(rem);
3190  actualModulesAdded[env].erase(add++);
3191  }
3192  else
3193  ++add;
3194  }
3195  }
3196 
3197  // Reify port types on node classes (if needed).
3198 
3199  bool wereModulesAddedOrModified = false;
3200  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3201  {
3202  Environment *env = (*i).at(0);
3203  if (! (actualModulesAdded[env].empty() && actualModulesModified[env].empty()) )
3204  {
3205  wereModulesAddedOrModified = true;
3206  break;
3207  }
3208  }
3209 
3210  if (wereModulesAddedOrModified)
3211  {
3212  map<string, VuoCompilerType *> inheritedTypes;
3213  for (const vector<Environment *> &envs : environments)
3214  {
3215  for (Environment *env : envs)
3216  {
3217  env->reifyPortTypes(inheritedTypes);
3218  map<string, VuoCompilerType *> envTypes = env->getTypes();
3219  inheritedTypes.insert(envTypes.begin(), envTypes.end());
3220  }
3221  }
3222  }
3223 
3224  // Delete cached compiled module files and call this function recursively for:
3225  // - modules whose source files have been removed
3226  // - modules whose source files depend on them
3227 
3228  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3229  {
3230  Environment *env = (*i).at(0);
3231 
3232  set<string> sourcesToUnload;
3233  sourcesToUnload.insert(sourcesRemoved[env].begin(), sourcesRemoved[env].end());
3234  sourcesToUnload.insert(sourcesDepOnModulesRemoved[env].begin(), sourcesDepOnModulesRemoved[env].end());
3235  if (! sourcesToUnload.empty())
3236  {
3237  string moduleSearchPath = env->getModuleSearchPaths().front();
3238 
3239  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
3240  VuoCompiler *otherCompiler = new VuoCompiler(moduleSearchPath + "/unused");
3241 
3242  dispatch_sync(environmentQueue, ^{
3243  otherCompiler->loadModulesAndSources(set<string>(), set<string>(), sourcesToUnload,
3244  set<string>(), set<string>(), set<string>(),
3245  false, false, env, nullptr, nullptr, "");
3246  });
3247 
3248  delete otherCompiler;
3249  });
3250  }
3251 
3252  if (!env->isBuiltIn() && !sourcesToUnload.empty())
3253  VUserLog("Deleting from %s environment: %s", env->getName().c_str(), VuoStringUtilities::join(sourcesToUnload, ", ").c_str());
3254 
3255  env->deleteModulesCompiledFromSourceCode(sourcesToUnload);
3256  }
3257 
3258  // Compile asynchronously:
3259  // - source files that have been added or modified
3260  // - source files that depend on them
3261  // - source files that depend on modules that have been modified
3262 
3263  map<Environment *, set<string> > sourcesDepOnModulesAdded;
3264  {
3265  map<Environment *, set<string> > modulesDepOnModulesAdded; // unused
3266  __block map<Environment *, set<string> > modulesDepOnModulesAdded_otherCompiler; //
3267  __block map<Environment *, set<string> > sourcesDepOnModulesAdded_otherCompiler;
3268 
3269  map<Environment *, set<string> > actualModuleKeysAdded;
3270  for (const vector<Environment *> &envs : environments)
3271  {
3272  Environment *env = envs.at(0);
3273  for (VuoCompilerModule *module : actualModulesAdded[env])
3274  actualModuleKeysAdded[env].insert( module->getPseudoBase()->getModuleKey() );
3275  }
3276 
3277  vector<VuoDirectedAcyclicNetwork *> searchDependencyGraphs;
3278  searchDependencyGraphs.push_back(compositionDependencyGraph);
3279  for (map<string, vector<Environment *> >::iterator ii = environmentsForCompositionFamily.begin(); ii != environmentsForCompositionFamily.end(); ++ii)
3280  {
3281  vector< vector<Environment *> > otherEnvs = sharedEnvironments;
3282  otherEnvs.push_back(ii->second);
3283  VuoDirectedAcyclicNetwork *other = makeDependencyNetwork(otherEnvs, ^VuoDirectedAcyclicGraph * (Environment *env) { return env->getCompositionDependencyGraph(); });
3284  searchDependencyGraphs.push_back(other);
3285  }
3286 
3287  VuoDirectedAcyclicGraph *currentEnvironmentDependencyGraph = (currentEnvironment ? currentEnvironment->getCompositionDependencyGraph() : nullptr);
3288 
3289  findDependentModulesAndSources(actualModuleKeysAdded, searchDependencyGraphs, currentEnvironmentDependencyGraph,
3290  modulesDepOnModulesAdded, modulesDepOnModulesAdded_otherCompiler,
3291  sourcesDepOnModulesAdded, sourcesDepOnModulesAdded_otherCompiler);
3292 
3293  set<Environment *> otherEnvironments;
3294  for (map<Environment *, set<string> >::iterator i = sourcesDepOnModulesAdded_otherCompiler.begin(); i != sourcesDepOnModulesAdded_otherCompiler.end(); ++i)
3295  otherEnvironments.insert(i->first);
3296 
3297  for (Environment *env : otherEnvironments)
3298  {
3299  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
3300  string moduleSearchPath = env->getModuleSearchPaths().front();
3301  VuoCompiler *otherCompiler = new VuoCompiler(moduleSearchPath + "/unused");
3302 
3303  dispatch_sync(environmentQueue, ^{
3304  otherCompiler->loadModulesAndSources(set<string>(), set<string>(), set<string>(),
3305  sourcesDepOnModulesAdded_otherCompiler[env], set<string>(), set<string>(),
3306  false, true, env, nullptr, nullptr, "");
3307  });
3308 
3309  delete otherCompiler;
3310  });
3311  }
3312  }
3313 
3314  set<dispatch_group_t> sourcesLoading;
3315  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3316  {
3317  Environment *env = (*i).at(0);
3318 
3319  set<string> sourcesToCompile;
3320  sourcesToCompile.insert(sourcesAdded[env].begin(), sourcesAdded[env].end());
3321  sourcesToCompile.insert(sourcesModified[env].begin(), sourcesModified[env].end());
3322 
3323  if (sourcesToCompile.size() == 0)
3324  continue;
3325 
3326  set<dispatch_group_t> s = env->compileModulesFromSourceCode(sourcesToCompile, shouldRecompileSourcesIfUnchanged);
3327  sourcesLoading.insert(s.begin(), s.end());
3328  }
3329 
3330  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3331  {
3332  Environment *env = (*i).at(0);
3333 
3334  set<string> sourcesToCompile;
3335  sourcesToCompile.insert(sourcesDepOnModulesAdded[env].begin(), sourcesDepOnModulesAdded[env].end());
3336  sourcesToCompile.insert(sourcesDepOnModulesModified[env].begin(), sourcesDepOnModulesModified[env].end());
3337 
3338  if (sourcesToCompile.size() == 0)
3339  continue;
3340 
3341  env->compileModulesFromSourceCode(sourcesToCompile, true);
3342  }
3343 
3344  // Notify compiler delegates.
3345 
3346  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3347  {
3348  Environment *env = (*i).at(0);
3349  env->notifyCompilers(actualModulesAdded[env], actualModulesModified[env], actualModulesRemoved[env], issues[env]);
3350  }
3351 
3352  // Since the dispatch groups for specialized modules are temporary (caller is responsible for releasing them)
3353  // but the dispatch groups for module sources should stay alive as long as the ModuleInfo that contains them,
3354  // retain the dispatch groups for module sources so that all dispatch groups can be safely released by the caller.
3355 
3356  for (const dispatch_group_t &group : sourcesLoading)
3357  dispatch_retain(group);
3358 
3359  set<dispatch_group_t> loadingGroups;
3360  loadingGroups.insert(specializedModulesLoading.begin(), specializedModulesLoading.end());
3361  loadingGroups.insert(sourcesLoading.begin(), sourcesLoading.end());
3362  return loadingGroups;
3363 }
3364 
3376 void VuoCompiler::findDependentModulesAndSources(map<Environment *, set<string> > &changedModules,
3377  const vector<VuoDirectedAcyclicNetwork *> &searchDependencyGraphs,
3378  VuoDirectedAcyclicGraph *currentEnvironmentDependencyGraph,
3379  map<Environment *, set<string> > &modulesDepOnChangedModules_this,
3380  map<Environment *, set<string> > &modulesDepOnChangedModules_other,
3381  map<Environment *, set<string> > &sourcesDepOnChangedModules_this,
3382  map<Environment *, set<string> > &sourcesDepOnChangedModules_other)
3383 {
3384  for (const vector<Environment *> &envs : environments)
3385  {
3386  Environment *env = envs.at(0);
3387 
3388  for (const string &module : changedModules[env])
3389  {
3390  set<VuoDirectedAcyclicGraph::Vertex *> dependents;
3391  for (VuoDirectedAcyclicNetwork *searchDependencyGraph : searchDependencyGraphs)
3392  {
3393  vector<VuoDirectedAcyclicGraph::Vertex *> moduleVertices;
3394  if (currentEnvironmentDependencyGraph)
3395  {
3396  // If a module with the same module key is installed in multiple locations,
3397  // only consider the one being modified or removed.
3398  VuoDirectedAcyclicGraph::Vertex *mv = currentEnvironmentDependencyGraph->findVertex(module);
3399  if (mv)
3400  moduleVertices.push_back(mv);
3401  }
3402  else
3403  moduleVertices = searchDependencyGraph->findVertex(module);
3404 
3405  for (VuoDirectedAcyclicGraph::Vertex *moduleVertexRaw : moduleVertices)
3406  {
3407  DependencyGraphVertex *moduleVertex = static_cast<DependencyGraphVertex *>(moduleVertexRaw);
3408  if (moduleVertex->getEnvironment())
3409  {
3410  vector<VuoDirectedAcyclicGraph::Vertex *> upstream = searchDependencyGraph->getUpstreamVertices(moduleVertex);
3411  dependents.insert(upstream.begin(), upstream.end());
3412  }
3413  }
3414  }
3415 
3416  for (VuoDirectedAcyclicGraph::Vertex *dependentVertexRaw : dependents)
3417  {
3418  DependencyGraphVertex *v = static_cast<DependencyGraphVertex *>(dependentVertexRaw);
3419  Environment *dependentEnv = v->getEnvironment();
3420  if (! dependentEnv)
3421  continue;
3422 
3423  string dependent = v->getDependency();
3424 
3425  // Skip if the dependent module is already being modified/removed in its own right
3426  // (e.g. if the module depends on another in the same node set and the node set is being removed).
3427  if (changedModules[dependentEnv].find(dependent) != changedModules[dependentEnv].end())
3428  continue;
3429 
3430  ModuleInfo *foundSourceInfo = dependentEnv->listSourceFile(dependent);
3431  ModuleInfo *foundModuleInfo = dependentEnv->listModule(dependent);
3432 
3433  bool belongsToCurrentCompiler = false;
3434  for (const vector<Environment *> &envs2 : environments)
3435  {
3436  if (find(envs2.begin(), envs2.end(), dependentEnv) != envs2.end())
3437  {
3438  belongsToCurrentCompiler = true;
3439  break;
3440  }
3441  }
3442 
3443  map<Environment *, set<string> > *whicheverDependents = nullptr;
3444  ModuleInfo *moduleInfo = nullptr;
3445  if (foundSourceInfo)
3446  {
3447  moduleInfo = foundSourceInfo;
3448  whicheverDependents = (belongsToCurrentCompiler ? &sourcesDepOnChangedModules_this : &sourcesDepOnChangedModules_other);
3449  }
3450  else if (foundModuleInfo)
3451  {
3452  moduleInfo = foundModuleInfo;
3453  whicheverDependents = (belongsToCurrentCompiler ? &modulesDepOnChangedModules_this : &modulesDepOnChangedModules_other);
3454  }
3455 
3456  (*whicheverDependents)[dependentEnv].insert(dependent);
3457  moduleInfo->setAttempted(false);
3458  }
3459  }
3460  }
3461 }
3462 
3466 void VuoCompiler::loadedModules(map<string, VuoCompilerModule *> modulesAdded,
3467  map<string, pair<VuoCompilerModule *, VuoCompilerModule *> > modulesModified,
3468  map<string, VuoCompilerModule *> modulesRemoved,
3469  VuoCompilerIssues *issues, void *delegateDataV, Environment *currentEnvironment)
3470 {
3471  //VLog("C=%p %lu %lu %lu", this, modulesAdded.size(), modulesModified.size(), modulesRemoved.size());
3472 
3473  // If a module is added, superseding a version of the same module installed at a broader scope,
3474  // notify this VuoCompiler that the module has been modified rather than added.
3475  //
3476  // If a module is added, but a version of the same module is already installed at a narrower scope,
3477  // don't notify this VuoCompiler, since it will continue to use the version at the narrower scope.
3478  //
3479  // Same idea when a module is modified or removed while another version is installed at a different scope.
3480 
3481  auto findVersionsOfModule = [this, currentEnvironment] (const string &moduleKey)
3482  {
3483  vector< pair<Environment *, VuoCompilerModule *> > moduleVersions;
3484  for (const vector<Environment *> &envs : environments)
3485  {
3486  Environment *env = envs.at(0);
3487  VuoCompilerModule *module = env->findModule(moduleKey);
3488  if (module || env == currentEnvironment)
3489  moduleVersions.push_back( make_pair(env, module) );
3490  }
3491  return moduleVersions;
3492  };
3493 
3494  for (map<string, VuoCompilerModule *>::iterator i = modulesAdded.begin(); i != modulesAdded.end(); )
3495  {
3496  string moduleKey = i->first;
3497  VuoCompilerModule *moduleAdded = i->second;
3498 
3499  vector< pair<Environment *, VuoCompilerModule *> > moduleVersions = findVersionsOfModule(moduleKey);
3500 
3501  if (moduleVersions.size() > 1)
3502  {
3503  modulesAdded.erase(i++);
3504 
3505  if (moduleVersions.back().second == moduleAdded)
3506  {
3507  VuoCompilerModule *moduleSuperseded = moduleVersions.at(moduleVersions.size()-2).second;
3508  modulesModified[moduleKey] = make_pair(moduleSuperseded, moduleAdded);
3509  }
3510  }
3511  else
3512  ++i;
3513  }
3514 
3515  for (map<string, pair<VuoCompilerModule *, VuoCompilerModule *> >::iterator i = modulesModified.begin(); i != modulesModified.end(); )
3516  {
3517  string moduleKey = i->first;
3518  VuoCompilerModule *moduleModified = i->second.second;
3519 
3520  vector< pair<Environment *, VuoCompilerModule *> > moduleVersions = findVersionsOfModule(moduleKey);
3521 
3522  if (moduleVersions.size() > 1 && moduleVersions.back().second != moduleModified)
3523  modulesModified.erase(i++);
3524  else
3525  ++i;
3526  }
3527 
3528  for (map<string, VuoCompilerModule *>::iterator i = modulesRemoved.begin(); i != modulesRemoved.end(); )
3529  {
3530  string moduleKey = i->first;
3531  VuoCompilerModule *moduleRemoved = i->second;
3532 
3533  vector< pair<Environment *, VuoCompilerModule *> > moduleVersions = findVersionsOfModule(moduleKey);
3534 
3535  if (moduleVersions.size() > 1)
3536  {
3537  modulesRemoved.erase(i++);
3538 
3539  if (moduleVersions.back().first == currentEnvironment)
3540  {
3541  VuoCompilerModule *moduleUnsuperseded = moduleVersions.at(moduleVersions.size()-2).second;
3542  modulesModified[moduleKey] = make_pair(moduleRemoved, moduleUnsuperseded);
3543  }
3544  }
3545  else
3546  ++i;
3547  }
3548 
3549  dispatch_async(delegateQueue, ^{
3550  VuoCompilerDelegate::LoadedModulesData *delegateData = static_cast<VuoCompilerDelegate::LoadedModulesData *>(delegateDataV);
3551 
3552  if (delegate && ! (modulesAdded.empty() && modulesModified.empty() && modulesRemoved.empty() && issues->isEmpty()))
3553  {
3554  delegate->enqueueData(delegateData);
3555  delegate->loadedModules(modulesAdded, modulesModified, modulesRemoved, issues);
3556  }
3557  else
3558  {
3559  delegateData->release();
3560  }
3561  });
3562 }
3563 
3569 void VuoCompiler::loadNodeClassGeneratedAtRuntime(VuoCompilerNodeClass *nodeClass, Environment *env)
3570 {
3571  Module *module = nodeClass->getModule();
3572  if (module)
3573  {
3574  dispatch_sync(llvmQueue, ^{
3575  setTargetForModule(nodeClass->getModule());
3576  });
3577  }
3578 
3579  dispatch_sync(environmentQueue, ^{
3580  env->replaceNodeClass(nodeClass);
3581  });
3582 
3583  __block map<string, VuoCompilerType *> inheritedTypes;
3584  void (^envReifyPortTypes)(Environment *) = ^void (Environment *env) {
3585  env->reifyPortTypes(inheritedTypes);
3586  map<string, VuoCompilerType *> currentTypes = env->getTypes();
3587  inheritedTypes.insert(currentTypes.begin(), currentTypes.end());
3588  };
3589  applyToAllEnvironments(envReifyPortTypes);
3590 }
3591 
3595 void VuoCompiler::reifyGenericPortTypes(VuoCompilerComposition *composition)
3596 {
3597  for (VuoCompilerNode *node : composition->getCachedGraph(this)->getNodes())
3598  reifyGenericPortTypes(node->getBase());
3599 
3600  composition->invalidateCachedGraph();
3601 }
3602 
3606 void VuoCompiler::reifyGenericPortTypes(VuoNode *node)
3607 {
3608  VuoCompilerSpecializedNodeClass *nodeClass = dynamic_cast<VuoCompilerSpecializedNodeClass *>(node->getNodeClass()->getCompiler());
3609  if (! nodeClass)
3610  return;
3611 
3612  // Reify any generic types on the node that don't already have a compiler detail.
3613 
3614  vector<VuoPort *> inputPorts = node->getInputPorts();
3615  vector<VuoPort *> outputPorts = node->getOutputPorts();
3616  vector<VuoPort *> ports;
3617  ports.insert(ports.end(), inputPorts.begin(), inputPorts.end());
3618  ports.insert(ports.end(), outputPorts.begin(), outputPorts.end());
3619 
3620  for (vector<VuoPort *>::iterator j = ports.begin(); j != ports.end(); ++j)
3621  {
3622  VuoCompilerPort *port = static_cast<VuoCompilerPort *>((*j)->getCompiler());
3623  VuoGenericType *genericType = dynamic_cast<VuoGenericType *>(port->getDataVuoType());
3624  if (! genericType)
3625  continue;
3626 
3627  if (! genericType->hasCompiler())
3628  {
3629  VuoCompilerType * (^compilerGetType)(string) = ^VuoCompilerType * (string moduleKey) {
3630  return getType(moduleKey);
3631  };
3632 
3633  VuoCompilerGenericType *reifiedType = VuoCompilerGenericType::newGenericType(genericType, compilerGetType);
3634  if (reifiedType)
3635  port->setDataVuoType(reifiedType->getBase());
3636  }
3637  }
3638 
3639  // Update the node class's backing to match the node's backing.
3640 
3641  nodeClass->updateBackingNodeClass(node, this);
3642 }
3643 
3650 void VuoCompiler::compileModule(string inputPath, string outputPath)
3651 {
3652  compileModule(inputPath, outputPath, vector<string>());
3653 }
3654 
3662 void VuoCompiler::compileModule(string inputPath, string outputPath, const vector<string> &includePaths)
3663 {
3664  if (isVerbose)
3665  print();
3666 
3667  vector<string> allIncludePaths = includePaths;
3668  string preprocessedInputPath = inputPath;
3669 
3670  string tmpPreprocessedInputDir;
3671  string dir, file, ext;
3672  VuoFileUtilities::splitPath(inputPath, dir, file, ext);
3674  {
3675  string inputContents = VuoFileUtilities::readFileToString(inputPath);
3676  string preprocessedInputContents = inputContents;
3678  if (inputContents != preprocessedInputContents)
3679  {
3680  // Unspecialized generic node class
3681  allIncludePaths.push_back(dir.empty() ? "." : dir);
3682  tmpPreprocessedInputDir = VuoFileUtilities::makeTmpDir(file);
3683  preprocessedInputPath = tmpPreprocessedInputDir + "/" + file + "." + ext;
3684  VuoFileUtilities::preserveOriginalFileName(preprocessedInputContents, file + "." + ext);
3685  VuoFileUtilities::writeStringToFile(preprocessedInputContents, preprocessedInputPath);
3686  }
3687  }
3688  else if (ext == "fs")
3689  {
3690  VuoFileUtilities::File vuf(dir, file + "." + ext);
3691  VuoModuleCompiler *moduleCompiler = VuoModuleCompiler::newModuleCompiler("isf", getModuleKeyForPath(inputPath), &vuf);
3692  if (moduleCompiler)
3693  {
3694  auto getType = [this] (const string &moduleKey) { return this->getType(moduleKey); };
3695  VuoCompilerIssues *issues = new VuoCompilerIssues();
3696  Module *module = moduleCompiler->compile(getType, llvmQueue, issues);
3697  if (module)
3698  dispatch_sync(llvmQueue, ^{
3699  writeModuleToBitcode(module, outputPath);
3700  });
3701 
3702  if (!issues->isEmpty())
3703  throw VuoCompilerException(issues, true);
3704  delete issues;
3705  }
3706  return;
3707  }
3708 
3709  vector<string> extraArgs;
3710  for (vector<string>::iterator i = allIncludePaths.begin(); i != allIncludePaths.end(); ++i)
3711  {
3712  extraArgs.push_back("-I");
3713  extraArgs.push_back(*i);
3714  }
3715 
3716  string macosxSdkFolder = "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/";
3717  if (VuoFileUtilities::fileExists(macosxSdkFolder + "MacOSX10.10.sdk"))
3718  {
3719  extraArgs.push_back("-isysroot");
3720  extraArgs.push_back(macosxSdkFolder + "MacOSX10.10.sdk");
3721  }
3722 
3723  __block vector<string> headerSearchPaths;
3724  void (^envGetHeaderSearchPaths)(Environment *) = ^void (Environment *env) {
3725  vector<string> result = env->getHeaderSearchPaths();
3726  headerSearchPaths.insert(headerSearchPaths.end(), result.begin(), result.end());
3727  };
3728  applyToInstalledEnvironments(envGetHeaderSearchPaths);
3729 
3730  __block Module *module;
3731  dispatch_sync(llvmQueue, ^{
3732  module = readModuleFromC(preprocessedInputPath, headerSearchPaths, extraArgs);
3733  });
3734  string moduleKey = getModuleKeyForPath(inputPath);
3735  if (! tmpPreprocessedInputDir.empty())
3736  remove(tmpPreprocessedInputDir.c_str());
3737  if (! module)
3738  {
3739  VuoCompilerIssue issue(VuoCompilerIssue::Error, "compiling module", inputPath,
3740  "", "%moduleKey couldn't be compiled as a node class, type, or library. Check the macOS Console for details.");
3741  issue.setModuleKey(moduleKey);
3742  throw VuoCompilerException(issue);
3743  }
3744 
3745  dispatch_sync(llvmQueue, ^{
3746  VuoCompilerModule *compilerModule = VuoCompilerModule::newModule(moduleKey, module, "");
3747  if (! compilerModule)
3748  {
3749  VUserLog("Error: Didn't recognize '%s' as a node class, type, or library.", inputPath.c_str());
3750  return;
3751  }
3752 
3753  setTargetForModule(module, target);
3754  writeModuleToBitcode(module, outputPath);
3755 
3756  delete module;
3757  });
3758 }
3759 
3763 Module * VuoCompiler::compileCompositionToModule(VuoCompilerComposition *composition, const string &moduleKey, bool isTopLevelComposition,
3764  VuoCompilerIssues *issues)
3765 {
3766  composition->check(issues);
3767 
3768  reifyGenericPortTypes(composition);
3769 
3771  isTopLevelComposition,
3772  moduleKey, this);
3773  if (telemetry == "console")
3774  generator->setDebugMode(true);
3775 
3776  __block Module *module;
3777  dispatch_sync(llvmQueue, ^{
3778  module = generator->generateBitcode();
3779  setTargetForModule(module, target);
3780  });
3781 
3782  delete generator;
3783 
3784  return module;
3785 }
3786 
3797 void VuoCompiler::compileComposition(VuoCompilerComposition *composition, string outputPath, bool isTopLevelComposition,
3798  VuoCompilerIssues *issues)
3799 {
3800  string moduleKey = getModuleKeyForPath(outputPath);
3801  Module *module = compileCompositionToModule(composition, moduleKey, isTopLevelComposition, issues);
3802 
3803  dispatch_sync(llvmQueue, ^{
3804  writeModuleToBitcode(module, outputPath);
3805  });
3806 }
3807 
3818 void VuoCompiler::compileComposition(string inputPath, string outputPath, bool isTopLevelComposition,
3819  VuoCompilerIssues *issues)
3820 {
3821  VDebugLog("Compiling '%s'…", inputPath.c_str());
3822  if (isVerbose)
3823  print();
3824 
3826  {
3827  VuoCompilerIssue issue(VuoCompilerIssue::Error, "opening composition", inputPath,
3828  "", "The composition file couldn't be read or was empty.");
3829  throw VuoCompilerException(issue);
3830  }
3831 
3832  try
3833  {
3834  string compositionString = VuoFileUtilities::readFileToString(inputPath);
3835  compileCompositionString(compositionString, outputPath, isTopLevelComposition, issues);
3836  }
3837  catch (VuoCompilerException &e)
3838  {
3839  e.getIssues()->setFilePathIfEmpty(inputPath);
3840  throw;
3841  }
3842 
3843  VDebugLog("Done.");
3844 }
3845 
3856 void VuoCompiler::compileCompositionString(const string &compositionString, string outputPath, bool isTopLevelComposition,
3857  VuoCompilerIssues *issues)
3858 {
3860  compileComposition(composition, outputPath, isTopLevelComposition, issues);
3861 
3862  VuoComposition *baseComposition = composition->getBase();
3863  delete composition;
3864  delete baseComposition;
3865 }
3866 
3870 void VuoCompiler::compileSubcompositionString(const string &compositionString, const string &outputPath,
3871  std::function<void(void)> moduleLoadedCallback, Environment *environment,
3872  VuoCompilerIssues *issues, const string inputPathForIssues)
3873 {
3874  if (! issues)
3875  issues = new VuoCompilerIssues();
3876 
3877  bool compilationSucceeded = false;
3878  try
3879  {
3880  compileCompositionString(compositionString, outputPath, false, issues);
3881  compilationSucceeded = true;
3882  }
3883  catch (VuoCompilerException &e)
3884  {
3885  if (issues != e.getIssues())
3886  issues->append(e.getIssues());
3887  }
3888 
3889  if (! compilationSucceeded)
3890  {
3891  VuoFileUtilities::deleteFile(outputPath);
3892  issues->setFilePathIfEmpty(inputPathForIssues);
3893  }
3894 
3895  string outputDir, file, ext;
3896  VuoFileUtilities::splitPath(outputPath, outputDir, file, ext);
3898 
3899  environment->moduleSearchPathContentsChanged(outputDir, outputPath, compositionString, moduleLoadedCallback, this, issues);
3900 }
3901 
3915 void VuoCompiler::linkCompositionToCreateExecutable(string inputPath, string outputPath, Optimization optimization, string rPath)
3916 {
3917  linkCompositionToCreateExecutableOrDynamicLibrary(inputPath, outputPath, optimization, false, rPath);
3918 }
3919 
3936 void VuoCompiler::linkCompositionToCreateDynamicLibrary(string inputPath, string outputPath, Optimization optimization)
3937 {
3938  linkCompositionToCreateExecutableOrDynamicLibrary(inputPath, outputPath, optimization, true);
3939 }
3940 
3955 void VuoCompiler::linkCompositionToCreateExecutableOrDynamicLibrary(string compiledCompositionPath, string linkedCompositionPath,
3956  Optimization optimization, bool isDylib, string rPath)
3957 {
3958  if (isVerbose)
3959  print();
3960 
3961  if (optimization == Optimization_FastBuildExistingCache)
3962  shouldLoadAllModules = false;
3963 
3964  set<string> dependencies = getDependenciesForComposition(compiledCompositionPath);
3965  dependencies.insert(getRuntimeDependency());
3966  if (! isDylib)
3967  dependencies.insert(getRuntimeMainDependency());
3968 
3969  set<Module *> modules;
3970  set<string> libraries;
3971  set<string> frameworks;
3972  getLinkerInputs(dependencies, optimization, modules, libraries, frameworks);
3973 
3974  libraries.insert(compiledCompositionPath);
3975 
3976  link(linkedCompositionPath, modules, libraries, frameworks, isDylib, rPath);
3977 }
3978 
3993 void VuoCompiler::linkCompositionToCreateDynamicLibraries(string compiledCompositionPath, string linkedCompositionPath,
3994  VuoRunningCompositionLibraries *runningCompositionLibraries)
3995 {
3996  if (isVerbose)
3997  print();
3998 
3999  // Get the dependencies used by the new resources and not the previous resources.
4000 
4001  set<string> carriedOverDependencies = runningCompositionLibraries->getDependenciesLoaded();
4002  set<string> allDependencies = getDependenciesForComposition(compiledCompositionPath);
4003  set<string> addedDependencies;
4004  std::set_difference(allDependencies.begin(), allDependencies.end(),
4005  carriedOverDependencies.begin(), carriedOverDependencies.end(),
4006  std::inserter(addedDependencies, addedDependencies.end()));
4007 
4008  // Get the libraries and frameworks used by the previous resources.
4009 
4010  vector<string> carriedOverNonUnloadableLibraries = runningCompositionLibraries->getNonUnloadableLibrariesLoaded();
4011  vector<string> carriedOverUnloadableLibraries = runningCompositionLibraries->getUnloadableLibrariesLoaded();
4012  set<string> carriedOverExternalLibraries = runningCompositionLibraries->getExternalLibraries();
4013  set<string> carriedOverFrameworks = runningCompositionLibraries->getExternalFrameworks();
4014 
4015  // Link the new resource dylibs, if needed.
4016 
4017  string nonUnloadableResourcePath;
4018  string unloadableResourcePath;
4019  set<string> nonUnloadableDependencies;
4020  set<string> unloadableDependencies;
4021  map<string, set<string> > builtInCacheDependencies;
4022  map<string, set<string> > userCacheDependencies;
4023  set<string> builtInLibraries;
4024  set<string> userLibraries;
4025  set<string> addedExternalLibraries;
4026  set<string> addedFrameworks;
4027  set<string> allFrameworks;
4028  if (! addedDependencies.empty())
4029  {
4030  set<string> builtInModuleAndLibraryDependencies;
4031  set<string> userModuleAndLibraryDependencies;
4032  set<Module *> builtInModules;
4033  set<Module *> userModules;
4034 
4035  getLinkerInputs(addedDependencies, Optimization_FastBuild,
4036  builtInModuleAndLibraryDependencies, userModuleAndLibraryDependencies, builtInCacheDependencies, userCacheDependencies,
4037  builtInModules, userModules, builtInLibraries, userLibraries, addedExternalLibraries, addedFrameworks);
4038 
4039  allFrameworks.insert(carriedOverFrameworks.begin(), carriedOverFrameworks.end());
4040  allFrameworks.insert(addedFrameworks.begin(), addedFrameworks.end());
4041 
4042  string dir, linkedCompositionFile, ext;
4043  VuoFileUtilities::splitPath(linkedCompositionPath, dir, linkedCompositionFile, ext);
4044 
4045  if (! builtInModules.empty() || builtInLibraries.size() > builtInCacheDependencies.size())
4046  {
4047  nonUnloadableResourcePath = VuoFileUtilities::makeTmpFile(linkedCompositionFile + "-resource", "dylib");
4048  nonUnloadableDependencies = builtInModuleAndLibraryDependencies;
4049 
4050  set<string> librariesForNonUnloadableResource = builtInLibraries;
4051  librariesForNonUnloadableResource.insert(carriedOverNonUnloadableLibraries.begin(), carriedOverNonUnloadableLibraries.end());
4052  librariesForNonUnloadableResource.insert(carriedOverExternalLibraries.begin(), carriedOverExternalLibraries.end());
4053  librariesForNonUnloadableResource.insert(addedExternalLibraries.begin(), addedExternalLibraries.end());
4054 
4055  link(nonUnloadableResourcePath, builtInModules, librariesForNonUnloadableResource, allFrameworks, true);\
4056 
4057  for (set<string>::iterator i = builtInLibraries.begin(); i != builtInLibraries.end(); )
4058  {
4059  if (! VuoStringUtilities::endsWith(*i, ".dylib"))
4060  builtInLibraries.erase(i++);
4061  else
4062  i++;
4063  }
4064 
4065  for (set<string>::iterator i = addedExternalLibraries.begin(); i != addedExternalLibraries.end(); )
4066  {
4067  if (! VuoStringUtilities::endsWith(*i, ".dylib"))
4068  addedExternalLibraries.erase(i++);
4069  else
4070  i++;
4071  }
4072  }
4073 
4074  if (! userModules.empty() || userLibraries.size() > userCacheDependencies.size())
4075  {
4076  unloadableResourcePath = VuoFileUtilities::makeTmpFile(linkedCompositionFile + "-resource", "dylib");
4077  unloadableDependencies = userModuleAndLibraryDependencies;
4078 
4079  set<string> librariesForUnloadableResource = userLibraries;
4080  librariesForUnloadableResource.insert(builtInLibraries.begin(), builtInLibraries.end());
4081  librariesForUnloadableResource.insert(carriedOverUnloadableLibraries.begin(), carriedOverUnloadableLibraries.end());
4082  librariesForUnloadableResource.insert(carriedOverNonUnloadableLibraries.begin(), carriedOverNonUnloadableLibraries.end());
4083  librariesForUnloadableResource.insert(carriedOverExternalLibraries.begin(), carriedOverExternalLibraries.end());
4084  librariesForUnloadableResource.insert(addedExternalLibraries.begin(), addedExternalLibraries.end());
4085  if (! nonUnloadableResourcePath.empty())
4086  librariesForUnloadableResource.insert(nonUnloadableResourcePath);
4087 
4088  link(unloadableResourcePath, userModules, librariesForUnloadableResource, allFrameworks, true);
4089 
4090  for (set<string>::iterator i = userLibraries.begin(); i != userLibraries.end(); )
4091  {
4092  if (! VuoStringUtilities::endsWith(*i, ".dylib"))
4093  userLibraries.erase(i++);
4094  else
4095  i++;
4096  }
4097  }
4098  }
4099 
4100  // Get the Vuo runtime dependency.
4101 
4102  set<string> vuoRuntimePaths;
4103  {
4104  set<Module *> modules;
4105  set<string> libraries;
4106  set<string> frameworks;
4107 
4108  set<string> dependencies;
4109  dependencies.insert(getRuntimeDependency());
4110  getLinkerInputs(dependencies, Optimization_FastBuild, modules, libraries, frameworks);
4111  vuoRuntimePaths = libraries;
4112  }
4113 
4114  // Link the composition.
4115 
4116  {
4117  set<Module *> modules;
4118  set<string> libraries;
4119 
4120  libraries.insert(compiledCompositionPath);
4121  libraries.insert(carriedOverExternalLibraries.begin(), carriedOverExternalLibraries.end());
4122  libraries.insert(addedExternalLibraries.begin(), addedExternalLibraries.end());
4123  libraries.insert(carriedOverNonUnloadableLibraries.begin(), carriedOverNonUnloadableLibraries.end());
4124  libraries.insert(carriedOverUnloadableLibraries.begin(), carriedOverUnloadableLibraries.end());
4125  libraries.insert(builtInLibraries.begin(), builtInLibraries.end());
4126  libraries.insert(userLibraries.begin(), userLibraries.end());
4127  if (! nonUnloadableResourcePath.empty())
4128  libraries.insert(nonUnloadableResourcePath);
4129  if (! unloadableResourcePath.empty())
4130  libraries.insert(unloadableResourcePath);
4131  libraries.insert(vuoRuntimePaths.begin(), vuoRuntimePaths.end());
4132  link(linkedCompositionPath, modules, libraries, allFrameworks, true);
4133  }
4134 
4135  // Now that we're past the point where an exception can be thrown, update the RunningCompositionLibraries.
4136 
4137  if (! nonUnloadableResourcePath.empty())
4138  runningCompositionLibraries->enqueueResourceLibraryToLoad(nonUnloadableResourcePath, nonUnloadableDependencies, false);
4139 
4140  if (! unloadableResourcePath.empty())
4141  runningCompositionLibraries->enqueueResourceLibraryToLoad(unloadableResourcePath, unloadableDependencies, true);
4142 
4143  for (map<string, set<string> >::iterator i = builtInCacheDependencies.begin(); i != builtInCacheDependencies.end(); ++i)
4144  runningCompositionLibraries->enqueueCacheLibraryToLoad(i->first, i->second, false);
4145 
4146  for (map<string, set<string> >::iterator i = userCacheDependencies.begin(); i != userCacheDependencies.end(); ++i)
4147  runningCompositionLibraries->enqueueCacheLibraryToLoad(i->first, i->second, true);
4148 
4149  runningCompositionLibraries->addExternalFrameworks(addedFrameworks);
4150  runningCompositionLibraries->addExternalLibraries(addedExternalLibraries);
4151 }
4152 
4159 set<string> VuoCompiler::getDependenciesForComposition(const string &compiledCompositionPath)
4160 {
4161  VDebugLog("Gathering dependencies for '%s'…", compiledCompositionPath.c_str());
4162 
4163  // Add the node classes in the top-level composition and their dependencies.
4164  __block set<string> directDependencies;
4165  string moduleKey = getModuleKeyForPath(compiledCompositionPath);
4166  Module *module = readModuleFromBitcode(compiledCompositionPath);
4167  dispatch_sync(llvmQueue, ^{
4168  VuoCompilerModule *compilerModule = VuoCompilerModule::newModule(moduleKey, module, "");
4169  directDependencies = compilerModule->getDependencies();
4170  delete compilerModule;
4171  delete module;
4172  });
4173 
4174  try
4175  {
4176  auto deps = getDependenciesForComposition(directDependencies, true);
4177  VDebugLog("Done.");
4178  return deps;
4179  }
4180  catch (VuoCompilerException &e)
4181  {
4182  e.getIssues()->setFilePathIfEmpty(compiledCompositionPath);
4183  throw;
4184  }
4185 }
4186 
4194 {
4195  set<string> directDependencies;
4196 
4197  set<VuoCompilerNode *> nodes = composition->getCachedGraph(this)->getNodes();
4198  for (VuoCompilerNode *node : nodes)
4199  if (node->getBase()->getNodeClass()->hasCompiler())
4200  directDependencies.insert( node->getBase()->getNodeClass()->getCompiler()->getDependencyName() );
4201 
4202  vector<VuoPublishedPort *> publishedInputPorts = composition->getBase()->getPublishedInputPorts();
4203  vector<VuoPublishedPort *> publishedOutputPorts = composition->getBase()->getPublishedOutputPorts();
4204  vector<VuoPublishedPort *> publishedPorts;
4205  publishedPorts.insert(publishedPorts.end(), publishedInputPorts.begin(), publishedInputPorts.end());
4206  publishedPorts.insert(publishedPorts.end(), publishedOutputPorts.begin(), publishedOutputPorts.end());
4207  for (VuoPublishedPort *publishedPort : publishedPorts)
4208  {
4209  if (publishedPort->getClass()->hasCompiler())
4210  {
4211  VuoType *portType = static_cast<VuoCompilerPortClass *>( publishedPort->getClass()->getCompiler() )->getDataVuoType();
4212  if (portType)
4213  {
4214  string dependency;
4215  VuoGenericType *genericType = dynamic_cast<VuoGenericType *>(portType);
4216  if (genericType)
4217  {
4218  VuoGenericType::Compatibility compatibility;
4219  vector<string> compatibleTypeNames = genericType->getCompatibleSpecializedTypes(compatibility);
4220  dependency = VuoCompilerGenericType::chooseBackingTypeName(portType->getModuleKey(), compatibleTypeNames);
4221  }
4222  else
4223  dependency = portType->getModuleKey();
4224 
4225  directDependencies.insert(dependency);
4226  }
4227  }
4228  }
4229 
4230  return directDependencies;
4231 }
4232 
4239 set<string> VuoCompiler::getDependenciesForComposition(VuoCompilerComposition *composition)
4240 {
4241  set<string> directDependencies = getDirectDependenciesForComposition(composition);
4242  return getDependenciesForComposition(directDependencies, false);
4243 }
4244 
4251 {
4252  __block vector<string> librarySearchPaths;
4253  applyToInstalledEnvironments(^void (Environment *env) {
4254  vector<string> result = env->getLibrarySearchPaths();
4255  librarySearchPaths.insert(librarySearchPaths.end(), result.begin(), result.end());
4256  });
4257 
4258  set<string> dylibDeps;
4259  for (string dep : getDependenciesForComposition(composition))
4260  {
4261  string path = getLibraryPath(dep, librarySearchPaths);
4262  if (VuoStringUtilities::endsWith(path, ".dylib"))
4263  dylibDeps.insert(path);
4264  }
4265 
4266  return dylibDeps;
4267 }
4268 
4282 set<string> VuoCompiler::getDependenciesForComposition(const set<string> &directDependencies, bool checkCompatibility)
4283 {
4284  // Make sure that any compiler-generated node classes have been generated and added to the dependency graph.
4285  for (set<string>::const_iterator i = directDependencies.begin(); i != directDependencies.end(); ++i)
4286  getNodeClass(*i);
4287 
4288  set<string> dependencies;
4289  for (set<string>::iterator i = directDependencies.begin(); i != directDependencies.end(); ++i)
4290  {
4291  string moduleKey = *i;
4292 
4293  // First pass: Find all dependencies of the direct dependency that have been loaded so far.
4294  vector<VuoDirectedAcyclicGraph::Vertex *> firstPassVertices = dependencyGraph->findVertex(moduleKey);
4295  set<VuoDirectedAcyclicGraph::Vertex *> firstPassDependencies(firstPassVertices.begin(), firstPassVertices.end());
4296  for (vector<VuoDirectedAcyclicGraph::Vertex *>::iterator j = firstPassVertices.begin(); j != firstPassVertices.end(); ++j)
4297  {
4298  vector<VuoDirectedAcyclicGraph::Vertex *> downstream = dependencyGraph->getDownstreamVertices(*j);
4299  firstPassDependencies.insert(downstream.begin(), downstream.end());
4300  }
4301 
4302  // Make sure that any compiler-generated node classes in subcompositions have been generated and added to the dependency graph.
4303  for (set<VuoDirectedAcyclicGraph::Vertex *>::iterator j = firstPassDependencies.begin(); j != firstPassDependencies.end(); ++j)
4304  {
4305  DependencyGraphVertex *v = static_cast<DependencyGraphVertex *>(*j);
4306  getNodeClass(v->getDependency());
4307  }
4308 
4309  // Second pass: Find all dependencies of the direct dependency, this time including dependencies of the node classes just generated.
4310  vector<VuoDirectedAcyclicGraph::Vertex *> moduleVertices = dependencyGraph->findVertex(moduleKey);
4311  set<VuoDirectedAcyclicGraph::Vertex *> moduleDependencies(moduleVertices.begin(), moduleVertices.end());
4312  for (vector<VuoDirectedAcyclicGraph::Vertex *>::iterator j = moduleVertices.begin(); j != moduleVertices.end(); ++j)
4313  {
4314  vector<VuoDirectedAcyclicGraph::Vertex *> downstream = dependencyGraph->getDownstreamVertices(*j);
4315  moduleDependencies.insert(downstream.begin(), downstream.end());
4316  }
4317 
4318  // Sort the direct dependency and all of its dependencies into those that are and are not compatible.
4319  set<string> dependenciesToAdd;
4320  set<string> incompatibleDependencies;
4321  for (set<VuoDirectedAcyclicGraph::Vertex *>::iterator j = moduleDependencies.begin(); j != moduleDependencies.end(); ++j)
4322  {
4323  DependencyGraphVertex *v = static_cast<DependencyGraphVertex *>(*j);
4324  if (! checkCompatibility || ! v->getEnvironment() || v->isCompatible())
4325  dependenciesToAdd.insert(v->getDependency());
4326  else
4327  incompatibleDependencies.insert(v->getDependency());
4328  }
4329 
4330  if (! checkCompatibility || incompatibleDependencies.empty())
4331  {
4332  dependencies.insert(dependenciesToAdd.begin(), dependenciesToAdd.end());
4333  }
4334  else
4335  {
4336  VuoCompilerTargetSet compositionTargets;
4337  compositionTargets.restrictToCurrentOperatingSystemVersion();
4338 
4339  string dependencyTargetString;
4340  VuoCompilerModule *module = getModule(moduleKey);
4341  if (module)
4342  {
4343  VuoCompilerTargetSet dependencyTargets = module->getCompatibleTargets();
4344  for (set<string>::iterator i = incompatibleDependencies.begin(); i != incompatibleDependencies.end(); ++i)
4345  {
4346  VuoCompilerModule *subModule = getModule(*i);
4347  if (subModule)
4348  {
4349  VuoCompilerTargetSet subDependencyTargets = subModule->getCompatibleTargets();
4350  dependencyTargets.restrictToBeCompatibleWithAllOf(subDependencyTargets);
4351  }
4352  }
4353  dependencyTargetString = dependencyTargets.toString();
4354  }
4355  else
4356  dependencyTargetString = "(unknown operating systems)";
4357 
4358  string modulePlaceholder = (module ? "%module" : "%moduleKey");
4359  VuoCompilerIssue issue(VuoCompilerIssue::Error, "linking composition", "",
4360  "Node incompatible with operating system",
4361  modulePlaceholder + " is only compatible with " + dependencyTargetString +
4362  ", so this composition can't run on your macOS version (" + compositionTargets.toString() + ").");
4363  issue.setModule(module->getPseudoBase());
4364  issue.setModuleKey(moduleKey);
4365  throw VuoCompilerException(issue);
4366  }
4367  }
4368 
4369  // Add the libraries needed by every linked composition.
4370  vector<string> coreDependencies = getCoreVuoDependencies();
4371  dependencies.insert(coreDependencies.begin(), coreDependencies.end());
4372 
4373  return dependencies;
4374 }
4375 
4380 void VuoCompiler::getLinkerInputs(const set<string> &dependencies, Optimization optimization,
4381  set<Module *> &modules, set<string> &libraries, set<string> &frameworks)
4382 {
4383  set<string> builtInModuleAndLibraryDependencies;
4384  set<string> userModuleAndLibraryDependencies;
4385  map<string, set<string> > builtInCacheDependencies;
4386  map<string, set<string> > userCacheDependencies;
4387  set<Module *> builtInModules;
4388  set<Module *> userModules;
4389  set<string> builtInLibraries;
4390  set<string> userLibraries;
4391  set<string> externalLibraries;
4392 
4393  getLinkerInputs(dependencies, optimization,
4394  builtInModuleAndLibraryDependencies, userModuleAndLibraryDependencies, builtInCacheDependencies, userCacheDependencies,
4395  builtInModules, userModules, builtInLibraries, userLibraries, externalLibraries, frameworks);
4396 
4397  modules.insert(builtInModules.begin(), builtInModules.end());
4398  modules.insert(userModules.begin(), userModules.end());
4399  libraries.insert(builtInLibraries.begin(), builtInLibraries.end());
4400  libraries.insert(userLibraries.begin(), userLibraries.end());
4401  libraries.insert(externalLibraries.begin(), externalLibraries.end());
4402 }
4403 
4417 void VuoCompiler::getLinkerInputs(const set<string> &dependencies, Optimization optimization,
4418  set<string> &builtInModuleAndLibraryDependencies, set<string> &userModuleAndLibraryDependencies,
4419  map<string, set<string> > &builtInCacheDependencies, map<string, set<string> > &userCacheDependencies,
4420  set<Module *> &builtInModules, set<Module *> &userModules,
4421  set<string> &builtInLibraries, set<string> &userLibraries,
4422  set<string> &externalLibraries, set<string> &externalFrameworks)
4423 {
4424  bool shouldUseModuleCache = (optimization == Optimization_FastBuild || optimization == Optimization_FastBuildExistingCache);
4425  if (shouldUseModuleCache)
4426  useModuleCache(true, optimization == Optimization_FastBuildExistingCache);
4427 
4428  __block vector<string> librarySearchPaths;
4429  void (^envGetLibrarySearchPaths)(Environment *) = ^void (Environment *env) {
4430  vector<string> result = env->getLibrarySearchPaths();
4431  librarySearchPaths.insert(librarySearchPaths.end(), result.begin(), result.end());
4432  };
4433  applyToInstalledEnvironments(envGetLibrarySearchPaths);
4434 
4435  for (set<string>::iterator i = dependencies.begin(); i != dependencies.end(); ++i)
4436  {
4437  string dependency = *i;
4438 
4439  bool foundInCache = false;
4440  string moduleCachePath;
4441  bool isInBuiltInModuleCache = false;
4442  if (shouldUseModuleCache)
4443  foundInCache = findInModuleCache(dependency, moduleCachePath, isInBuiltInModuleCache);
4444 
4445  if (foundInCache)
4446  {
4447  if (isInBuiltInModuleCache)
4448  {
4449  builtInLibraries.insert(moduleCachePath);
4450  builtInCacheDependencies[moduleCachePath].insert(dependency);
4451  }
4452  else
4453  {
4454  userLibraries.insert(moduleCachePath);
4455  userCacheDependencies[moduleCachePath].insert(dependency);
4456  }
4457  }
4458  else
4459  {
4460  __block VuoCompilerModule *module = NULL;
4461  void (^envFindModule)(Environment *) = ^void (Environment *env) {
4462  VuoCompilerModule *result = env->findModule(dependency);
4463  if (result)
4464  module = result;
4465  };
4466  applyToAllEnvironments(envFindModule);
4467 
4468  if (module)
4469  {
4470  if (module->getModule()) // Skip not-fully-specialized generic modules when building the module cache.
4471  {
4472  string modulePath = module->getModulePath();
4473  if (! modulePath.empty() && dynamic_cast<VuoCompilerType *>(module))
4474  {
4475  if (module->isBuiltIn())
4476  builtInLibraries.insert(modulePath);
4477  else
4478  userLibraries.insert(modulePath);
4479  }
4480  else
4481  {
4482  if (module->isBuiltIn())
4483  builtInModules.insert(module->getModule());
4484  else
4485  userModules.insert(module->getModule());
4486  }
4487 
4488  if (module->isBuiltIn())
4489  builtInModuleAndLibraryDependencies.insert(dependency);
4490  else
4491  userModuleAndLibraryDependencies.insert(dependency);
4492  }
4493  }
4494  else
4495  {
4496  if (VuoStringUtilities::endsWith(dependency, ".framework"))
4497  externalFrameworks.insert(dependency);
4498  else
4499  {
4500  string dependencyPath = getLibraryPath(dependency, librarySearchPaths);
4501  if (! dependencyPath.empty())
4502  externalLibraries.insert(dependencyPath);
4503  else
4504  VUserLog("Warning: Could not locate dependency '%s'.", dependency.c_str());
4505  }
4506  }
4507  }
4508  }
4509 }
4510 
4516 string VuoCompiler::getLibraryPath(const string &dependency, vector<string> librarySearchPaths)
4517 {
4518  if (dependency[0] == '/' && VuoFileUtilities::fileExists(dependency))
4519  return dependency;
4520 
4521  // Put the system library folder last in the list — prefer to use the libraries bundled in Vuo.framework.
4522  // Don't attempt to use OpenSSL from the system library folder, since macOS 10.15 prevents linking to it.
4523  if (dependency != "crypto"
4524  && dependency != "ssl")
4525  librarySearchPaths.push_back("/usr/lib");
4526 
4527  for (auto &path : librarySearchPaths)
4528  {
4529  vector<string> variations;
4530  variations.push_back(path + "/" + dependency);
4531  variations.push_back(path + "/lib" + dependency);
4532  variations.push_back(path + "/lib" + dependency + ".dylib");
4533  variations.push_back(path + "/lib" + dependency + ".a");
4534  for (auto &variation : variations)
4535  if (VuoFileUtilities::fileExists(variation))
4536  return variation;
4537  }
4538 
4539  return "";
4540 }
4541 
4549 void VuoCompiler::useModuleCache(bool shouldUseExistingBuiltInCaches, bool shouldUseExistingOtherCaches)
4550 {
4551  loadModulesIfNeeded();
4552 
4553  // Iterate through the environments in the order that the caches need to be built.
4554 
4555  dispatch_sync(environmentQueue, ^{
4556  set<string> dylibsForCachesOfInstalledModules;
4557  set<string> frameworksForCachesOfInstalledModules;
4558  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
4559  {
4560  bool builtin = (i == environments.begin());
4561  set<string> dylibsForCacheOfGeneratedModules;
4562  set<string> frameworksForCacheOfGeneratedModules;
4563 
4564  for (int j = i->size() - 1; j >= 0; --j)
4565  {
4566  Environment *env = i->at(j);
4567  bool installed = (j == 0);
4568 
4569  set<string> cacheableModulesAndDependencies;
4570  set<string> dylibsNeededToLinkToThisCache;
4571  set<string> frameworksNeededToLinkToThisCache;
4572  env->getCacheableModulesAndDependencies(builtin, installed, cacheableModulesAndDependencies,
4573  dylibsNeededToLinkToThisCache, frameworksNeededToLinkToThisCache);
4574 
4575  set<string> accumulatedDylibs;
4576  accumulatedDylibs.insert(dylibsNeededToLinkToThisCache.begin(), dylibsNeededToLinkToThisCache.end());
4577  accumulatedDylibs.insert(dylibsForCachesOfInstalledModules.begin(), dylibsForCachesOfInstalledModules.end());
4578  accumulatedDylibs.insert(dylibsForCacheOfGeneratedModules.begin(), dylibsForCacheOfGeneratedModules.end());
4579 
4580  set<string> accumulatedFrameworks;
4581  accumulatedFrameworks.insert(frameworksNeededToLinkToThisCache.begin(), frameworksNeededToLinkToThisCache.end());
4582  accumulatedFrameworks.insert(frameworksForCachesOfInstalledModules.begin(), frameworksForCachesOfInstalledModules.end());
4583  accumulatedFrameworks.insert(frameworksForCacheOfGeneratedModules.begin(), frameworksForCacheOfGeneratedModules.end());
4584 
4585  bool shouldUseExistingCache = (builtin ? shouldUseExistingBuiltInCaches : shouldUseExistingOtherCaches);
4586  env->useModuleCache(shouldUseExistingCache, this, cacheableModulesAndDependencies,
4587  accumulatedDylibs, accumulatedFrameworks);
4588 
4589  if (installed)
4590  {
4591  dylibsForCachesOfInstalledModules.insert(dylibsNeededToLinkToThisCache.begin(), dylibsNeededToLinkToThisCache.end());
4592  frameworksForCachesOfInstalledModules.insert(frameworksNeededToLinkToThisCache.begin(), frameworksNeededToLinkToThisCache.end());
4593  }
4594  else
4595  {
4596  dylibsForCacheOfGeneratedModules.insert(dylibsNeededToLinkToThisCache.begin(), dylibsNeededToLinkToThisCache.end());
4597  frameworksForCacheOfGeneratedModules.insert(frameworksNeededToLinkToThisCache.begin(), frameworksNeededToLinkToThisCache.end());
4598  }
4599  }
4600  }
4601  });
4602 
4603  Environment::waitForModuleCachesToBuild();
4604 }
4605 
4614 bool VuoCompiler::findInModuleCache(const string &moduleOrDependency, string &cachePath, bool &isBuiltinCache)
4615 {
4616  __block bool found = false;
4617  __block string outPath;
4618  __block bool outBuiltin;
4619  dispatch_sync(environmentQueue, ^{
4620  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
4621  {
4622  bool builtin = (i == environments.begin());
4623 
4624  for (int j = i->size() - 1; j >= 0; --j)
4625  {
4626  Environment *env = i->at(j);
4627 
4628  string resultPath;
4629  bool resultFound = env->findInModuleCache(moduleOrDependency, resultPath);
4630  if (resultFound)
4631  {
4632  found = true;
4633  outPath = resultPath;
4634  outBuiltin = builtin;
4635  }
4636  }
4637  }
4638  });
4639 
4640  cachePath = outPath;
4641  isBuiltinCache = outBuiltin;
4642  return found;
4643 }
4644 
4653 {
4654  dispatch_group_async(moduleCacheBuilding, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
4655  useModuleCache(true, false);
4656  });
4657 }
4658 
4671 void VuoCompiler::generateBuiltInModuleCaches(const string &vuoFrameworkPath)
4672 {
4673  vuoFrameworkInProgressPath = vuoFrameworkPath;
4674 
4675  VuoCompiler compiler;
4676  compiler.useModuleCache(false, true);
4677 }
4678 
4687 {
4688  unsigned long maxSeconds = 30 * 24 * 60 * 60; // 30 days
4689 
4690  set<VuoFileUtilities::File *> cacheDirs = VuoFileUtilities::findAllFilesInDirectory(VuoFileUtilities::getCachePath());
4691  for (set<VuoFileUtilities::File *>::iterator i = cacheDirs.begin(); i != cacheDirs.end(); ++i)
4692  {
4693  string path = (*i)->path();
4694 
4695  string file = (*i)->basename();
4696  if (file != "Builtin" && file != "System" && file != "User")
4697  {
4698  unsigned long fileSeconds = VuoFileUtilities::getSecondsSinceFileLastAccessed(path);
4699  if (fileSeconds > maxSeconds)
4701  }
4702 
4703  if (VuoStringUtilities::beginsWith(file, Environment::pidCacheDirPrefix))
4704  {
4705  string pidAsString = file.substr(Environment::pidCacheDirPrefix.length());
4706  int pid = atoi(pidAsString.c_str());
4707  if (kill(pid, 0) != 0) // no running process has this pid
4709  }
4710 
4711  delete *i;
4712  }
4713 }
4714 
4719 void VuoCompiler::setLoadAllModules(bool shouldLoadAllModules)
4720 {
4721  dispatch_sync(modulesToLoadQueue, ^{
4722  this->shouldLoadAllModules = shouldLoadAllModules;
4723  });
4724 }
4725 
4737 void VuoCompiler::link(string outputPath, const set<Module *> &modules, const set<string> &libraries, const set<string> &frameworks, bool isDylib, string rPath)
4738 {
4739  VDebugLog("Linking '%s'…", outputPath.c_str());
4740  // https://stackoverflow.com/questions/11657529/how-to-generate-an-executable-from-an-llvmmodule
4741 
4742 
4743  // Write all the modules with renamed symbols to a composite module file (since the linker can't operate on in-memory modules).
4744  string compositeModulePath = VuoFileUtilities::makeTmpFile("composite", "bc");
4745  dispatch_sync(llvmQueue, ^{
4746  double t0 = VuoLogGetTime();
4747  Module *compositeModule = new Module("composite", getGlobalContext());
4748  setTargetForModule(compositeModule);
4749  for (set<Module *>::const_iterator i = modules.begin(); i != modules.end(); ++i)
4750  {
4751  string error;
4752  if (Linker::LinkModules(compositeModule, *i, Linker::PreserveSource, &error))
4753  VUserLog("Error: Failed to link compositeModule: %s", error.c_str());
4754  }
4755  writeModuleToBitcode(compositeModule, compositeModulePath);
4756  delete compositeModule;
4757  VDebugLog("\tLinkModules took %5.2fs", VuoLogGetTime() - t0);
4758  });
4759 
4760 
4761  // llvm-3.1/llvm/tools/clang/tools/driver/driver.cpp
4762 
4763  llvm::sys::Path clangPath = getClangPath();
4764 
4765  vector<const char *> args;
4766  vector<char *> argsToFree;
4767  args.push_back(clangPath.c_str());
4768 
4769  args.push_back(compositeModulePath.c_str());
4770 
4771  vector<string> coreDependencies = getCoreVuoDependencies();
4772  for (set<string>::const_iterator i = libraries.begin(); i != libraries.end(); ++i)
4773  {
4774  string library = *i;
4775 
4776  for (vector<string>::iterator j = coreDependencies.begin(); j != coreDependencies.end(); ++j)
4777  {
4778  string coreDependency = *j;
4779  if (VuoStringUtilities::endsWith(library, "lib" + coreDependency + ".a"))
4780  args.push_back("-force_load"); // Load all symbols of static core dependencies, not just those used in the objects.
4781  }
4782 
4783  // Use the pre-built native object file if it exists (faster than converting .bc to .o every build).
4784  if (VuoStringUtilities::endsWith(library, ".bc"))
4785  {
4786  string libraryObject = VuoStringUtilities::substrBefore(library, ".bc") + ".o";
4787  if (VuoFileUtilities::fileExists(libraryObject))
4788  library = libraryObject;
4789  }
4790 
4791  char *libraryZ = strdup(library.c_str());
4792  args.push_back(libraryZ);
4793  argsToFree.push_back(libraryZ);
4794  }
4795 
4796  // Add framework search paths
4797  vector<string> frameworkArguments;
4798 
4799  __block vector<string> frameworkSearchPaths;
4800  void (^envGetFrameworkSearchPaths)(Environment *) = ^void (Environment *env) {
4801  vector<string> result = env->getFrameworkSearchPaths();
4802  frameworkSearchPaths.insert(frameworkSearchPaths.end(), result.begin(), result.end());
4803  };
4804  applyToInstalledEnvironments(envGetFrameworkSearchPaths);
4805 
4806  for (vector<string>::const_iterator i = frameworkSearchPaths.begin(); i != frameworkSearchPaths.end(); ++i)
4807  {
4808  string a = "-F"+*i;
4809  // 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.
4810  frameworkArguments.push_back(a);
4811  char *frameworkArgument = strdup(a.c_str());
4812  args.push_back(frameworkArgument);
4813  argsToFree.push_back(frameworkArgument);
4814  }
4815 
4816  for (set<string>::const_iterator i = frameworks.begin(); i != frameworks.end(); ++i)
4817  {
4818  args.push_back("-framework");
4819 
4820  string frameworkName = *i;
4821  frameworkName = frameworkName.substr(0, frameworkName.length() - string(".framework").length());
4822  char *frameworkNameZ = strdup(frameworkName.c_str());
4823  args.push_back(frameworkNameZ);
4824  argsToFree.push_back(frameworkNameZ);
4825  }
4826 
4827  // Check for C Runtime path within Vuo.framework
4828  llvm::sys::Path cRuntimePath;
4829  llvm::sys::Path crt1Path;
4830  string vuoFrameworkPath = getVuoFrameworkPath();
4831  string vuoFrameworkContainingFolder = vuoFrameworkPath + "/..";
4832  if (! vuoFrameworkPath.empty())
4833  {
4834  cRuntimePath = vuoFrameworkPath + "/Modules/";
4835  crt1Path = cRuntimePath;
4836  crt1Path.appendComponent("crt1.o");
4837  }
4838 
4839  // If we have located a bundled version of crt1.o, link it in explicitly rather than relying on
4840  // clang's heuristic to locate a system version.
4841  if (!isDylib && crt1Path.canRead())
4842  {
4843  args.push_back("-nostartfiles");
4844  args.push_back(crt1Path.c_str());
4845  }
4846 
4847  // Linker option necessary for compatibility with our bundled version of ld64:
4848  args.push_back("-Xlinker");
4849  args.push_back("--no-demangle");
4850 
4851  if (isVerbose)
4852  args.push_back("-v");
4853 
4854  if (isDylib)
4855  args.push_back("-dynamiclib");
4856 
4857  args.push_back("-Xlinker");
4858  args.push_back("-headerpad_max_install_names");
4859 
4860  // Tell the built dylib/executable where to find Vuo.framework
4862  args.push_back("-rpath");
4863  string rPathArg = (rPath.empty() ? vuoFrameworkContainingFolder : rPath);
4864  args.push_back(rPathArg.c_str());
4865 
4866 #ifdef COVERAGE
4867  args.push_back("-rpath");
4868  args.push_back(LLVM_ROOT "/lib");
4869 #endif
4870 
4871  args.push_back("-std=c++11");
4872  args.push_back("-stdlib=libc++");
4873 
4874  // Allow clang to print meaningful error messages.
4875  clang::DiagnosticOptions *diagOptions = new clang::DiagnosticOptions();
4876  clang::TextDiagnosticPrinter *diagClient = new clang::TextDiagnosticPrinter(llvm::errs(), diagOptions);
4877  diagClient->setPrefix(clangPath.str());
4878  IntrusiveRefCntPtr<clang::DiagnosticIDs> DiagID(new clang::DiagnosticIDs());
4879  clang::DiagnosticsEngine Diags(DiagID, diagOptions, diagClient);
4880 
4881  clang::driver::Driver TheDriver(args[0], "x86_64-apple-macosx10.10.0", outputPath, Diags);
4882 
4883  TheDriver.CCCIsCXX = true; // clang++ instead of clang
4884 
4885  if (isVerbose)
4886  {
4887  ostringstream s;
4888  for (vector<const char *>::iterator i = args.begin(); i != args.end(); ++i)
4889  s << *i << " ";
4890  VUserLog("\t%s", s.str().c_str());
4891  }
4892 
4893  OwningPtr<clang::driver::Compilation> C(TheDriver.BuildCompilation(args));
4894 
4895  int Res = 0;
4896  if (C)
4897  {
4898  SmallVector<std::pair<int, const clang::driver::Command *>, 4> FailingCommands;
4899  double t0 = VuoLogGetTime();
4900  Res = TheDriver.ExecuteCompilation(*C, FailingCommands);
4901  VDebugLog("\tLinking took %5.2fs", VuoLogGetTime() - t0);
4902  }
4903  for (auto i : argsToFree)
4904  free(i);
4905 
4906  // Clean up composite module file.
4907  remove(compositeModulePath.c_str());
4908 
4909  if (!isDylib)
4910  // Ensure the linked binary has the execute permission set
4911  // (ld64-242 doesn't reliably set it, whereas ld64-133.3 did).
4912  // https://b33p.net/kosada/node/14152
4913  chmod(outputPath.c_str(), 0755);
4914 
4915  if (Res != 0)
4916  {
4917  __block vector<string> thirdPartyNodeClasses;
4918  dispatch_sync(environmentQueue, ^{
4919  for (size_t i = 1; i < environments.size(); ++i)
4920  {
4921  map<string, VuoCompilerNodeClass *> envNodeClasses = environments[i].at(0)->getNodeClasses();
4922  for (map<string, VuoCompilerNodeClass *>::iterator j = envNodeClasses.begin(); j != envNodeClasses.end(); ++j)
4923  thirdPartyNodeClasses.push_back(j->first);
4924  }
4925  });
4926 
4927  string details = "One or more nodes in this composition can't be used by this version of Vuo. ";
4928  if (! thirdPartyNodeClasses.empty())
4929  {
4930  details += "Make sure you're using the latest version of all the extra Vuo nodes you've installed:\n";
4931  sort(thirdPartyNodeClasses.begin(), thirdPartyNodeClasses.end());
4932  for (vector<string>::iterator i = thirdPartyNodeClasses.begin(); i != thirdPartyNodeClasses.end(); ++i)
4933  details += " • " + *i + "\n";
4934  }
4935  details += "Check the macOS Console for more information about the problem.";
4936 
4937  VuoCompilerIssue issue(VuoCompilerIssue::Error, "linking", outputPath,
4938  "Node broken or outdated", details);
4939  throw VuoCompilerException(issue);
4940  }
4941  VDebugLog("Done.");
4942 }
4943 
4949 Module * VuoCompiler::readModuleFromC(string inputPath, const vector<string> &headerSearchPaths, const vector<string> &extraArgs)
4950 {
4951  // llvm-3.1/llvm/tools/clang/examples/clang-interpreter/main.cpp
4952 
4953  vector<const char *> args;
4954  args.push_back(inputPath.c_str());
4955  args.push_back("-DVUO_COMPILER");
4956  args.push_back("-fblocks");
4957 
4958  // Sync with vuo.pri's WARNING_ADD.
4959  args.push_back("-Wall");
4960  args.push_back("-Wextra");
4961  args.push_back("-Wimplicit-fallthrough");
4962  args.push_back("-Wno-unused-parameter");
4963  args.push_back("-Wno-c++11-extensions");
4964  args.push_back("-Wno-sign-compare");
4965  args.push_back("-Werror=implicit");
4966 
4967  if (VuoStringUtilities::endsWith(inputPath, ".cc"))
4968  {
4969  args.push_back("-std=c++11");
4970  args.push_back("-stdlib=libc++");
4971  }
4972 
4973  for (vector<string>::const_iterator i = headerSearchPaths.begin(); i != headerSearchPaths.end(); ++i)
4974  {
4975  args.push_back("-I");
4976  args.push_back(i->c_str());
4977  }
4978 
4979  if (isVerbose)
4980  args.push_back("-v");
4981 
4982  for (vector<string>::const_iterator i = extraArgs.begin(); i != extraArgs.end(); ++i)
4983  args.push_back(i->c_str());
4984 
4985  clang::DiagnosticOptions * diagOptions = new clang::DiagnosticOptions();
4986  IntrusiveRefCntPtr<clang::DiagnosticIDs> DiagID(new clang::DiagnosticIDs());
4987  clang::DiagnosticsEngine Diags(DiagID, diagOptions);
4988 
4989  OwningPtr<clang::CompilerInvocation> CI(new clang::CompilerInvocation);
4990  clang::CompilerInvocation::CreateFromArgs(*CI, &args[0], &args[0] + args.size(), Diags);
4991 
4992  clang::CompilerInstance Clang;
4993  Clang.setInvocation(CI.take());
4994 
4995  Clang.createDiagnostics();
4996  if (!Clang.hasDiagnostics())
4997  return NULL;
4998 
4999  // See CompilerInvocation::GetResourcesPath -- though we're not calling it because we don't have MainAddr.
5000  llvm::sys::Path builtinHeaderSearchPath;
5001  string vuoFrameworkPath = getVuoFrameworkPath();
5002  if (vuoFrameworkPath.empty())
5003  {
5004  llvm::sys::Path clangPath = getClangPath();
5005  builtinHeaderSearchPath = clangPath;
5006  builtinHeaderSearchPath.eraseComponent(); // Remove /clang from foo/bin/clang
5007  builtinHeaderSearchPath.eraseComponent(); // Remove /bin from foo/bin
5008  builtinHeaderSearchPath.appendComponent("lib");
5009  builtinHeaderSearchPath.appendComponent("clang");
5010  builtinHeaderSearchPath.appendComponent(CLANG_VERSION_STRING); // foo/lib/clang/<version>
5011  }
5012  else
5013  {
5014  builtinHeaderSearchPath = vuoFrameworkPath;
5015  builtinHeaderSearchPath.appendComponent("Frameworks");
5016  builtinHeaderSearchPath.appendComponent("llvm.framework");
5017  builtinHeaderSearchPath.appendComponent("Versions");
5018  builtinHeaderSearchPath.appendComponent("A");
5019  builtinHeaderSearchPath.appendComponent("lib");
5020  builtinHeaderSearchPath.appendComponent("clang");
5021  builtinHeaderSearchPath.appendComponent(CLANG_VERSION_STRING); // foo/lib/clang/<version>
5022  }
5023  Clang.getHeaderSearchOpts().ResourceDir = builtinHeaderSearchPath.str();
5024 
5025 // OwningPtr<clang::CodeGenAction> Act(new clang::EmitLLVMOnlyAction()); // @@@ return value of takeModule() is destroyed at the end of this function
5026  clang::CodeGenAction *Act = new clang::EmitLLVMOnlyAction();
5027  if (!Clang.ExecuteAction(*Act))
5028  return NULL;
5029 
5030  return Act->takeModule();
5031 }
5032 
5038 Module * VuoCompiler::readModuleFromBitcode(string inputPath)
5039 {
5040  string dir, file, ext;
5041  VuoFileUtilities::splitPath(inputPath, dir, file, ext);
5042  VuoFileUtilities::File inputFile(dir, file + "." + ext);
5043  return readModuleFromBitcode(&inputFile);
5044 }
5045 
5053 Module * VuoCompiler::readModuleFromBitcode(VuoFileUtilities::File *inputFile)
5054 {
5055  size_t inputDataBytes;
5056  char *inputData = inputFile->getContentsAsRawData(inputDataBytes);
5057 
5058  string error;
5059  VuoLog_status("Loading module \"%s\"", inputFile->getRelativePath().c_str());
5060  Module *module = readModuleFromBitcodeData(inputData, inputDataBytes, error);
5061  VuoLog_status(NULL);
5062  if (! module)
5063  VUserLog("Error: Couldn't parse module '%s': %s.", inputFile->getRelativePath().c_str(), error.c_str());
5064 
5065  free(inputData);
5066 
5067  return module;
5068 }
5069 
5075 Module * VuoCompiler::readModuleFromBitcodeData(char *inputData, size_t inputDataBytes, string &error)
5076 {
5077  __block Module *module;
5078  dispatch_sync(llvmQueue, ^{
5079  StringRef inputDataAsStringRef(inputData, inputDataBytes);
5080  MemoryBuffer *mb = MemoryBuffer::getMemBuffer(inputDataAsStringRef, "", false);
5081  module = ParseBitcodeFile(&(*mb), getGlobalContext(), &error);
5082  delete mb;
5083  });
5084  return module;
5085 }
5086 
5094 bool VuoCompiler::writeModuleToBitcode(Module *module, string outputPath)
5095 {
5096  if (verifyModule(*module, PrintMessageAction))
5097  {
5098  VUserLog("Error: Module verification failed.");
5099  return true;
5100  }
5101 
5102  string err;
5103  raw_fd_ostream out(outputPath.c_str(), err);
5104  if (! err.empty())
5105  {
5106  VUserLog("Error: Couldn't open file '%s' for writing: %s", outputPath.c_str(), err.c_str());
5107  return true;
5108  }
5109  WriteBitcodeToFile(module, out);
5110 
5111  return false;
5112 }
5113 
5119 void VuoCompiler::setTargetForModule(Module *module, string target)
5120 {
5121 /*
5122  string effectiveTarget = target;
5123  if (effectiveTarget.empty())
5124  {
5125  // llvm::sys::getDefaultTargetTriple() finds a target based on the host, but the "default" target is not necessarily the
5126  // same target that results from invoking command-line clang without a -target argument. That is the "effective" target.
5127  // 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.
5128 
5129  llvm::sys::Path clangPath = getClangPath();
5130 
5131  vector<const char *> args;
5132  args.push_back(clangPath.c_str());
5133  args.push_back("/bin/sh"); // Driver needs an input file (that exists) or it refuses to give you the correct effective target.
5134 
5135  clang::DiagnosticOptions * diagOptions = new clang::DiagnosticOptions();
5136  IntrusiveRefCntPtr<clang::DiagnosticIDs> DiagID(new clang::DiagnosticIDs());
5137  clang::DiagnosticsEngine Diags(DiagID, diagOptions);
5138 
5139  clang::driver::Driver TheDriver(args[0], llvm::sys::getDefaultTargetTriple(), "a.out", true, Diags);
5140  OwningPtr<clang::driver::Compilation> C(TheDriver.BuildCompilation(args));
5141  effectiveTarget = C->getDefaultToolChain().ComputeEffectiveClangTriple(C->getArgs());
5142  }
5143 
5144  module->setTargetTriple(effectiveTarget);
5145 */
5146  module->setTargetTriple("x86_64-apple-macosx10.10.0");
5147 }
5148 
5156 {
5157  Module *llvmModule = module->getModule();
5158 
5159  // In C++ the return value of a cast may not be the same pointer as the cast arg.
5160  // Because of this, VuoModule::getPseudoBase() returns a different value than VuoNodeClass::getBase().
5161  // Calling delete on VuoModule::getPseudoBase() gives a malloc error: "pointer being freed was not allocated".
5162  // So call it on VuoNodeClass::getBase(). Same idea for VuoType.
5163 
5164  VuoNodeClass *baseNodeClass = NULL;
5165  VuoType *baseType = NULL;
5166  VuoModule *baseModule = NULL;
5167  VuoCompilerNodeClass *nodeClass = dynamic_cast<VuoCompilerNodeClass *>(module);
5168  if (nodeClass)
5169  {
5170  baseNodeClass = dynamic_cast<VuoNodeClass *>(nodeClass->getBase());
5171 
5173  if (dynamic_cast<VuoCompilerSpecializedNodeClass *>(module))
5174  module = NULL;
5175  }
5176  else
5177  {
5178  VuoCompilerType *type = dynamic_cast<VuoCompilerType *>(module);
5179  if (type)
5180  baseType = dynamic_cast<VuoType *>(type->getBase());
5181  else
5182  baseModule = module->getPseudoBase();
5183  }
5184 
5185  delete module;
5186  delete baseNodeClass;
5187  delete baseType;
5188  delete baseModule;
5189  destroyLlvmModule(llvmModule);
5190 }
5191 
5199 {
5200  dispatch_sync(llvmQueue, ^{
5201  delete module;
5202  });
5203 }
5204 
5216 VuoNode * VuoCompiler::createNode(VuoCompilerNodeClass *nodeClass, string title, double x, double y)
5217 {
5219  return nodeClassForNode->newNode(title, x, y);
5220 }
5221 
5225 VuoNode * VuoCompiler::createPublishedInputNode(vector<VuoPublishedPort *> publishedInputPorts)
5226 {
5227  string nodeClassName = VuoCompilerPublishedInputNodeClass::buildNodeClassName(publishedInputPorts);
5228  return createPublishedNode(nodeClassName, publishedInputPorts);
5229 }
5230 
5234 VuoNode * VuoCompiler::createPublishedOutputNode(vector<VuoPublishedPort *> publishedOutputPorts)
5235 {
5236  string nodeClassName = VuoCompilerPublishedOutputNodeClass::buildNodeClassName(publishedOutputPorts);
5237  return createPublishedNode(nodeClassName, publishedOutputPorts);
5238 }
5239 
5243 VuoNode * VuoCompiler::createPublishedNode(const string &nodeClassName, const vector<VuoPublishedPort *> &publishedPorts)
5244 {
5245  VuoCompilerNodeClass *nodeClass = getNodeClass(nodeClassName);
5246  VuoNode *node = createNode(nodeClass);
5247 
5248  // Set the generic port types on the node to match those on the published ports set by VuoCompilerComposition::updateGenericPortTypes().
5249  VuoCompilerPublishedInputNodeClass *inputNodeClass = dynamic_cast<VuoCompilerPublishedInputNodeClass *>(nodeClass);
5250  VuoCompilerPublishedOutputNodeClass *outputNodeClass = dynamic_cast<VuoCompilerPublishedOutputNodeClass *>(nodeClass);
5251  for (size_t i = 0; i < publishedPorts.size(); ++i)
5252  {
5253  VuoType *publishedPortType = static_cast<VuoCompilerPort *>(publishedPorts[i]->getCompiler())->getDataVuoType();
5254  if (! dynamic_cast<VuoGenericType *>(publishedPortType))
5255  continue;
5256 
5257  set<VuoPort *> nodePorts;
5258  if (inputNodeClass)
5259  {
5260  VuoPort *inputPort = node->getInputPorts().at( inputNodeClass->getInputPortIndexForPublishedInputPort(i) );
5261  nodePorts.insert(inputPort);
5262 
5263  VuoPort *outputPort = node->getOutputPorts().at( inputNodeClass->getOutputPortIndexForPublishedInputPort(i) );
5264  nodePorts.insert(outputPort);
5265  }
5266  else
5267  {
5268  VuoPort *inputPort = node->getInputPorts().at( outputNodeClass->getInputPortIndexForPublishedOutputPort(i) );
5269  nodePorts.insert(inputPort);
5270  }
5271 
5272  for (VuoPort *port : nodePorts)
5273  {
5274  VuoCompilerPort *compilerPort = static_cast<VuoCompilerPort *>(port->getCompiler());
5275  compilerPort->setDataVuoType(publishedPortType);
5276  }
5277  }
5278 
5279  reifyGenericPortTypes(node);
5280 
5281  return node;
5282 }
5283 
5289 {
5290  dispatch_sync(environmentQueue, ^{
5291  if (environments.size() >= 5)
5292  environments.at(3).at(0)->addExpatriateSourceFile(sourcePath);
5293  });
5294 }
5295 
5301 {
5302  dispatch_sync(environmentQueue, ^{
5303  if (environments.size() >= 5)
5304  {
5305  environments.at(3).at(0)->removeExpatriateSourceFile(sourcePath);
5306 
5307  set<string> sourcesRemoved;
5308  sourcesRemoved.insert(getModuleKeyForPath(sourcePath));
5309  loadModulesAndSources(set<string>(), set<string>(), set<string>(), set<string>(), set<string>(), sourcesRemoved,
5310  false, false, environments.at(3).at(0), nullptr, nullptr, "");
5311  }
5312  });
5313 }
5314 
5331 void VuoCompiler::overrideInstalledNodeClass(const string &sourcePath, const string &sourceCode)
5332 {
5333  string sourcePathCopy = sourcePath;
5334  string sourceCodeCopy = sourceCode;
5335 
5336  dispatch_async(environmentQueue, ^{
5337  string nodeClassName = getModuleKeyForPath(sourcePathCopy);
5338  ModuleInfo *sourceInfo = NULL;
5339 
5340  for (const vector<Environment *> &envs : environments)
5341  {
5342  for (Environment *env : envs)
5343  {
5344  ModuleInfo *potentialSourceInfo = env->listSourceFile(nodeClassName);
5345  if (potentialSourceInfo && VuoFileUtilities::arePathsEqual(potentialSourceInfo->getFile()->path(), sourcePathCopy))
5346  {
5347  sourceInfo = potentialSourceInfo;
5348  break;
5349  }
5350  }
5351  }
5352 
5353  if (! sourceInfo)
5354  return;
5355 
5356  bool shouldRecompileSourcesIfUnchanged;
5357  if (! sourceCodeCopy.empty())
5358  {
5359  sourceInfo->setSourceCode(sourceCodeCopy);
5360  sourceInfo->setSourceCodeOverridden(true);
5361 
5362  shouldRecompileSourcesIfUnchanged = false;
5363  }
5364  else
5365  {
5366  sourceInfo->revertSourceCode();
5367  sourceInfo->setSourceCodeOverridden(false);
5368 
5369  shouldRecompileSourcesIfUnchanged = true;
5370  }
5371  sourceInfo->setAttempted(false);
5372  sourceInfo->setLastModifiedToNow();
5373 
5374  set<string> sourcesModified;
5375  sourcesModified.insert(nodeClassName);
5376 
5377  loadModulesAndSources(set<string>(), set<string>(), set<string>(), set<string>(), sourcesModified, set<string>(),
5378  false, shouldRecompileSourcesIfUnchanged, nullptr, nullptr, nullptr, "");
5379  });
5380 }
5381 
5391 void VuoCompiler::revertOverriddenNodeClass(const string &sourcePath)
5392 {
5393  overrideInstalledNodeClass(sourcePath, "");
5394 }
5395 
5408 VuoCompilerNodeClass * VuoCompiler::getNodeClass(const string &nodeClassName)
5409 {
5410  // For performance, don't bother searching if it's obviously not a node class.
5411 
5412  if (VuoStringUtilities::endsWith(nodeClassName, ".framework"))
5413  return NULL;
5414 
5415  // Attempt to load the node class (if it hasn't already been loaded).
5416 
5417  set<string> nodeClassNameSet;
5418  nodeClassNameSet.insert(nodeClassName);
5419  loadModulesIfNeeded(nodeClassNameSet);
5420 
5421  // If the node class has been loaded, return it.
5422 
5423  __block VuoCompilerNodeClass *nodeClass = NULL;
5424  void (^envGetNodeClass)(Environment *) = ^void (Environment *env) {
5425  VuoCompilerNodeClass *result = env->getNodeClass(nodeClassName);
5426  if (result)
5427  nodeClass = result;
5428  };
5429  applyToAllEnvironments(envGetNodeClass);
5430  return nodeClass;
5431 }
5432 
5438 map<string, VuoCompilerNodeClass *> VuoCompiler::getNodeClasses()
5439 {
5440  loadModulesIfNeeded();
5441 
5442  __block map<string, VuoCompilerNodeClass *> nodeClasses;
5443  void (^envGetNodeClasses)(Environment *) = ^void (Environment *env) {
5444  map<string, VuoCompilerNodeClass *> result = env->getNodeClasses();
5445  nodeClasses.insert(result.begin(), result.end());
5446  };
5447  applyToInstalledEnvironments(envGetNodeClasses);
5448  return nodeClasses;
5449 }
5450 
5456 VuoCompilerType * VuoCompiler::getType(const string &typeName)
5457 {
5458  set<string> typeNameSet;
5459  typeNameSet.insert(typeName);
5460  loadModulesIfNeeded(typeNameSet);
5461 
5462  __block VuoCompilerType *type = NULL;
5463  void (^envGetType)(Environment *) = ^void (Environment *env) {
5464  VuoCompilerType *result = env->getType(typeName);
5465  if (result)
5466  type = result;
5467  };
5468  applyToInstalledEnvironments(envGetType);
5469 
5470  if (! type && VuoGenericType::isGenericTypeName(typeName))
5471  {
5472  VuoCompilerType * (^compilerGetType)(string) = ^VuoCompilerType * (string moduleKey) {
5473  return getType(moduleKey);
5474  };
5475 
5476  VuoGenericType *genericType = new VuoGenericType(typeName, vector<string>());
5477  type = VuoCompilerGenericType::newGenericType(genericType, compilerGetType);
5478  }
5479 
5480  return type;
5481 }
5482 
5488 map<string, VuoCompilerType *> VuoCompiler::getTypes()
5489 {
5490  loadModulesIfNeeded();
5491 
5492  __block map<string, VuoCompilerType *> types;
5493  void (^envGetTypes)(Environment *) = ^void (Environment *env) {
5494  map<string, VuoCompilerType *> result = env->getTypes();
5495  types.insert(result.begin(), result.end());
5496  };
5497  applyToInstalledEnvironments(envGetTypes);
5498  return types;
5499 }
5500 
5506 map<string, VuoCompilerModule *> VuoCompiler::getLibraryModules()
5507 {
5508  loadModulesIfNeeded();
5509 
5510  __block map<string, VuoCompilerModule *> libraryModules;
5511  void (^envGetLibraryModules)(Environment *) = ^void (Environment *env) {
5512  map<string, VuoCompilerModule *> result = env->getLibraryModules();
5513  libraryModules.insert(result.begin(), result.end());
5514  };
5515  applyToInstalledEnvironments(envGetLibraryModules);
5516  return libraryModules;
5517 }
5518 
5524 map<string, VuoNodeSet *> VuoCompiler::getNodeSets()
5525 {
5526  loadModulesIfNeeded();
5527 
5528  __block map<string, VuoNodeSet *> nodeSets;
5529  void (^envGetNodeSets)(Environment *) = ^void (Environment *env) {
5530  map<string, VuoNodeSet *> result = env->getNodeSets();
5531  nodeSets.insert(result.begin(), result.end());
5532  };
5533  applyToInstalledEnvironments(envGetNodeSets);
5534  return nodeSets;
5535 }
5536 
5543 {
5544  loadModulesIfNeeded();
5545 
5546  __block VuoNodeSet *nodeSet = NULL;
5547  void (^envFindNodeSet)(Environment *) = ^void (Environment *env) {
5548  VuoNodeSet *result = env->findNodeSet(name);
5549  if (result)
5550  nodeSet = result;
5551  };
5552  applyToInstalledEnvironments(envFindNodeSet);
5553  return nodeSet;
5554 }
5555 
5559 VuoCompilerModule * VuoCompiler::getModule(const string &moduleKey)
5560 {
5561  __block VuoCompilerModule *module = NULL;
5562  void (^envFindModule)(Environment *) = ^void (Environment *env) {
5563  VuoCompilerModule *result = env->findModule(moduleKey);
5564  if (result)
5565  module = result;
5566  };
5567  applyToAllEnvironments(envFindModule);
5568  return module;
5569 }
5570 
5582 void VuoCompiler::listNodeClasses(const string &format)
5583 {
5584  map<string, VuoCompilerNodeClass *> nodeClasses = getNodeClasses();
5585  for (map<string, VuoCompilerNodeClass *>::const_iterator i = nodeClasses.begin(); i != nodeClasses.end(); ++i)
5586  {
5587  VuoCompilerNodeClass *nodeClass = i->second;
5588  if (format == "")
5589  {
5590  printf("%s\n", nodeClass->getBase()->getClassName().c_str());
5591  }
5592  else if (format == "path")
5593  {
5594  // TODO: If "path", prints the absolute path of each node class, one per line.
5595  }
5596  else if (format == "dot")
5597  {
5598  VuoCompilerNode *node = nodeClass->newNode()->getCompiler();
5599 
5600  printf("%s\n", nodeClass->getDoxygenDocumentation().c_str());
5601  printf("%s\n\n", node->getGraphvizDeclaration().c_str());
5602 
5603  delete node;
5604  }
5605  }
5606 }
5607 
5614 vector<string> VuoCompiler::getCoreVuoDependencies(void)
5615 {
5616  vector<string> dependencies;
5617  dependencies.push_back("VuoCompositionDiff.o");
5618  dependencies.push_back("VuoHeap");
5619  dependencies.push_back("VuoException.o");
5620  dependencies.push_back("VuoNodeRegistry.o");
5621  dependencies.push_back("VuoRuntimeCommunicator.o");
5622  dependencies.push_back("VuoRuntimeContext.o");
5623  dependencies.push_back("VuoRuntimePersistentState.o");
5624  dependencies.push_back("VuoRuntimeState.o");
5625  dependencies.push_back("VuoTelemetry.o");
5626  dependencies.push_back("VuoThreadManager.o");
5627  dependencies.push_back("VuoApp");
5628  dependencies.push_back("zmq");
5629  dependencies.push_back("json-c");
5630  dependencies.push_back("objc");
5631  dependencies.push_back("c");
5632  dependencies.push_back("AppKit.framework");
5633 
5634 #ifdef COVERAGE
5635  dependencies.push_back(LLVM_ROOT "/lib/libprofile_rt.dylib");
5636 #endif
5637  return dependencies;
5638 }
5639 
5643 string VuoCompiler::getRuntimeMainDependency(void)
5644 {
5645  return "VuoRuntimeMain.o";
5646 }
5647 
5655 string VuoCompiler::getRuntimeDependency(void)
5656 {
5657  return "VuoRuntime.o";
5658 }
5659 
5666 string VuoCompiler::getVuoFrameworkPath(void)
5667 {
5668  if (! vuoFrameworkInProgressPath.empty())
5669  return vuoFrameworkInProgressPath;
5670 
5672 }
5673 
5677 llvm::sys::Path VuoCompiler::getClangPath(void)
5678 {
5679  return clangPath;
5680 }
5681 
5686 {
5687  // Start with the file name without extension.
5688  string dir, moduleKey, ext;
5689  VuoFileUtilities::splitPath(path, dir, moduleKey, ext);
5690 
5692  {
5693  vector<string> nodeClassNameParts = VuoStringUtilities::split(moduleKey, '.');
5694 
5695  // Remove repeated file extensions.
5696  while (nodeClassNameParts.size() > 1 && nodeClassNameParts.back() == ext)
5697  nodeClassNameParts.pop_back();
5698 
5699  // Convert each part to lowerCamelCase.
5700  for (string &part : nodeClassNameParts)
5701  part = VuoStringUtilities::convertToCamelCase(part, false, true, true);
5702 
5703  // If the node class name has only one part, add a prefix.
5704  if (nodeClassNameParts.size() == 1)
5705  nodeClassNameParts.insert(nodeClassNameParts.begin(), "isf");
5706 
5707  moduleKey = VuoStringUtilities::join(nodeClassNameParts, '.');
5708  }
5709 
5710  return moduleKey;
5711 }
5712 
5718 {
5719  __block bool isLocal = false;
5720 
5721  dispatch_sync(environmentQueue, ^{
5722  if (environments.size() >= 5)
5723  isLocal = environments.at(3).at(0)->findModule(moduleKey);
5724  });
5725 
5726  return isLocal;
5727 }
5728 
5736 {
5737  return lastCompositionBaseDir.empty() ? "" : lastCompositionBaseDir + "/Modules";
5738 }
5739 
5751 {
5752  return lastCompositionBaseDir;
5753 }
5754 
5758 void VuoCompiler::addModuleSearchPath(string path)
5759 {
5760  dispatch_sync(environmentQueue, ^{
5761  environments.back().at(0)->addModuleSearchPath(path);
5762  environments.back().at(0)->addLibrarySearchPath(path);
5763  });
5764 }
5765 
5769 void VuoCompiler::addHeaderSearchPath(const string &path)
5770 {
5771  dispatch_sync(environmentQueue, ^{
5772  environments.back().at(0)->addHeaderSearchPath(path);
5773  });
5774 }
5775 
5779 void VuoCompiler::addLibrarySearchPath(const string &path)
5780 {
5781  dispatch_sync(environmentQueue, ^{
5782  environments.back().at(0)->addLibrarySearchPath(path);
5783  });
5784 }
5785 
5789 void VuoCompiler::addFrameworkSearchPath(const string &path)
5790 {
5791  dispatch_sync(environmentQueue, ^{
5792  environments.back().at(0)->addFrameworkSearchPath(path);
5793  });
5794 }
5795 
5799 void VuoCompiler::setTelemetry(const string &telemetry)
5800 {
5801  this->telemetry = telemetry;
5802 }
5803 
5807 void VuoCompiler::setTarget(const string &target)
5808 {
5809  this->target = target;
5810 }
5811 
5815 void VuoCompiler::setVerbose(bool isVerbose)
5816 {
5817  this->isVerbose = isVerbose;
5818 }
5819 
5825 {
5826 #if VUO_PRO
5827  if (VuoPro::getProAccess())
5828  {
5829  _shouldShowSplashWindow = false;
5830  return;
5831  }
5832 #endif
5833 
5834  _shouldShowSplashWindow = potentiallyShow;
5835 }
5836 
5841 {
5842  return _shouldShowSplashWindow;
5843 }
5844 
5848 void VuoCompiler::setClangPath(const string &clangPath)
5849 {
5850  this->clangPath = llvm::sys::Path(StringRef(clangPath));
5851 }
5852 
5857 {
5858  string vuoFrameworkPath = getVuoFrameworkPath();
5859  return (vuoFrameworkPath.empty() ?
5860  VUO_BUILD_DIR "/bin/VuoCompositionLoader.app/Contents/MacOS/VuoCompositionLoader" :
5861  vuoFrameworkPath + "/Helpers/VuoCompositionLoader.app/Contents/MacOS/VuoCompositionLoader");
5862 }
5863 
5867 string VuoCompiler::getCompositionStubPath(void)
5868 {
5869  string vuoFrameworkPath = getVuoFrameworkPath();
5870  return (vuoFrameworkPath.empty() ?
5871  VUO_BUILD_DIR "/lib/libVuoCompositionStub.dylib" :
5872  vuoFrameworkPath + "/Modules/libVuoCompositionStub.dylib");
5873 }
5874 
5878 string VuoCompiler::getCachePathForComposition(const string compositionDir)
5879 {
5880  string modifierLetterColon("꞉");
5881  string cachedModulesName = compositionDir;
5882  VuoStringUtilities::replaceAll(cachedModulesName, "/", modifierLetterColon);
5883  return VuoFileUtilities::getCachePath() + "/" + cachedModulesName;
5884 }
5885 
5890 {
5891  __block vector<string> moduleSearchPaths;
5892  void (^envGetModuleSearchPaths)(Environment *) = ^void (Environment *env) {
5893  vector<string> result = env->getModuleSearchPaths();
5894  moduleSearchPaths.insert(moduleSearchPaths.end(), result.begin(), result.end());
5895  };
5896  applyToInstalledEnvironments(envGetModuleSearchPaths);
5897 
5898  __block vector<string> headerSearchPaths;
5899  void (^envGetHeaderSearchPaths)(Environment *) = ^void (Environment *env) {
5900  vector<string> result = env->getHeaderSearchPaths();
5901  headerSearchPaths.insert(headerSearchPaths.end(), result.begin(), result.end());
5902  };
5903  applyToInstalledEnvironments(envGetHeaderSearchPaths);
5904 
5905  __block vector<string> librarySearchPaths;
5906  void (^envGetLibrarySearchPaths)(Environment *) = ^void (Environment *env) {
5907  vector<string> result = env->getLibrarySearchPaths();
5908  librarySearchPaths.insert(librarySearchPaths.end(), result.begin(), result.end());
5909  };
5910  applyToInstalledEnvironments(envGetLibrarySearchPaths);
5911 
5912  __block vector<string> frameworkSearchPaths;
5913  void (^envGetFrameworkSearchPaths)(Environment *) = ^void (Environment *env) {
5914  vector<string> result = env->getFrameworkSearchPaths();
5915  frameworkSearchPaths.insert(frameworkSearchPaths.end(), result.begin(), result.end());
5916  };
5917  applyToInstalledEnvironments(envGetFrameworkSearchPaths);
5918 
5919  fprintf(stderr, "Module (node class, type, library) search paths:\n");
5920  for (vector<string>::iterator i = moduleSearchPaths.begin(); i != moduleSearchPaths.end(); ++i)
5921  fprintf(stderr, " %s\n", (*i).c_str());
5922  fprintf(stderr, "Header search paths:\n");
5923  for (vector<string>::iterator i = headerSearchPaths.begin(); i != headerSearchPaths.end(); ++i)
5924  fprintf(stderr, " %s\n", (*i).c_str());
5925  fprintf(stderr, "Other library search paths:\n");
5926  for (vector<string>::iterator i = librarySearchPaths.begin(); i != librarySearchPaths.end(); ++i)
5927  fprintf(stderr, " %s\n", (*i).c_str());
5928  fprintf(stderr, "Other framework search paths:\n");
5929  for (vector<string>::iterator i = frameworkSearchPaths.begin(); i != frameworkSearchPaths.end(); ++i)
5930  fprintf(stderr, " %s\n", (*i).c_str());
5931  fprintf(stderr, "Framework path:\n");
5932  if (! getVuoFrameworkPath().empty())
5933  fprintf(stderr, " %s\n", getVuoFrameworkPath().c_str());
5934  fprintf(stderr, "Clang path:\n");
5935  if (! getClangPath().str().empty())
5936  fprintf(stderr, " %s\n", getClangPath().c_str());
5937 }
5938 
5948 {
5949  try
5950  {
5951  VuoCompiler compiler(compositionFilePath);
5952  string directory, file, extension;
5953  VuoFileUtilities::splitPath(compositionFilePath, directory, file, extension);
5954  string compiledCompositionPath = VuoFileUtilities::makeTmpFile(file, "bc");
5955  string linkedCompositionPath = VuoFileUtilities::makeTmpFile(file + "-linked", "");
5956  compiler.compileComposition(compositionFilePath, compiledCompositionPath, true, issues);
5957  compiler.linkCompositionToCreateExecutable(compiledCompositionPath, linkedCompositionPath, Optimization_FastBuild);
5958  remove(compiledCompositionPath.c_str());
5959  return VuoRunner::newSeparateProcessRunnerFromExecutable(linkedCompositionPath, directory, false, true);
5960  }
5961  catch (VuoCompilerException &e)
5962  {
5963  if (issues != e.getIssues())
5964  issues->append(e.getIssues());
5965  return NULL;
5966  }
5967 }
5968 
5983 VuoRunner * VuoCompiler::newSeparateProcessRunnerFromCompositionString(string composition, string processName, string workingDirectory, VuoCompilerIssues *issues)
5984 {
5985  try
5986  {
5987  string compositionPath = (workingDirectory.empty() ? "." : workingDirectory) + "/" + processName + ".vuo";
5988  VuoCompiler compiler(compositionPath);
5989  string compiledCompositionPath = VuoFileUtilities::makeTmpFile(processName, "bc");
5990  string linkedCompositionPath = VuoFileUtilities::makeTmpFile(processName, "");
5991  compiler.compileCompositionString(composition, compiledCompositionPath, true, issues);
5992  compiler.linkCompositionToCreateExecutable(compiledCompositionPath, linkedCompositionPath, Optimization_FastBuild);
5993  remove(compiledCompositionPath.c_str());
5994  return VuoRunner::newSeparateProcessRunnerFromExecutable(linkedCompositionPath, workingDirectory, false, true);
5995  }
5996  catch (VuoCompilerException &e)
5997  {
5998  if (issues != e.getIssues())
5999  issues->append(e.getIssues());
6000  return NULL;
6001  }
6002 }
6003 
6013 {
6014  try
6015  {
6016  VuoCompiler compiler(compositionFilePath);
6017  string directory, file, extension;
6018  VuoFileUtilities::splitPath(compositionFilePath, directory, file, extension);
6019  string compiledCompositionPath = VuoFileUtilities::makeTmpFile(file, "bc");
6020  compiler.compileComposition(compositionFilePath, compiledCompositionPath, true, issues);
6021  string linkedCompositionPath = VuoFileUtilities::makeTmpFile(file, "dylib");
6022  compiler.linkCompositionToCreateDynamicLibrary(compiledCompositionPath, linkedCompositionPath, Optimization_FastBuild);
6023  remove(compiledCompositionPath.c_str());
6024  return VuoRunner::newCurrentProcessRunnerFromDynamicLibrary(linkedCompositionPath, directory, true);
6025  }
6026  catch (VuoCompilerException &e)
6027  {
6028  if (issues != e.getIssues())
6029  issues->append(e.getIssues());
6030  return NULL;
6031  }
6032 }
6033 
6048 VuoRunner * VuoCompiler::newCurrentProcessRunnerFromCompositionString(string composition, string workingDirectory, VuoCompilerIssues *issues)
6049 {
6050  try
6051  {
6052  string compositionPath = (workingDirectory.empty() ? "." : workingDirectory) + "/UntitledComposition.vuo";
6053  VuoCompiler compiler(compositionPath);
6054  string compiledCompositionPath = VuoFileUtilities::makeTmpFile("VuoRunnerComposition", "bc");
6055  compiler.compileCompositionString(composition, compiledCompositionPath, true, issues);
6056  string linkedCompositionPath = VuoFileUtilities::makeTmpFile("VuoRunnerComposition", "dylib");
6057  compiler.linkCompositionToCreateDynamicLibrary(compiledCompositionPath, linkedCompositionPath, Optimization_FastBuild);
6058  remove(compiledCompositionPath.c_str());
6059  return VuoRunner::newCurrentProcessRunnerFromDynamicLibrary(linkedCompositionPath, workingDirectory, true);
6060  }
6061  catch (VuoCompilerException &e)
6062  {
6063  if (issues != e.getIssues())
6064  issues->append(e.getIssues());
6065  return NULL;
6066  }
6067 }
6068 
6070 {
6071  pendingDataQueue = dispatch_queue_create("org.vuo.compiler.delegate.pending", 0);
6072 }
6073 
6075 {
6076  LoadedModulesData *data = dequeueData();
6077  data->release();
6078 }
6079 
6083 void VuoCompilerDelegate::enqueueData(LoadedModulesData *data)
6084 {
6085  dispatch_sync(pendingDataQueue, ^{
6086  pendingData.push_back(data);
6087  });
6088 }
6089 
6093 VuoCompilerDelegate::LoadedModulesData * VuoCompilerDelegate::dequeueData(void)
6094 {
6095  __block LoadedModulesData *ret;
6096  dispatch_sync(pendingDataQueue, ^{
6097  ret = pendingData.front();
6098  pendingData.pop_front();
6099  });
6100  return ret;
6101 }
6102 
6107 VuoCompilerDelegate::LoadedModulesData::LoadedModulesData(const set< pair<VuoCompilerModule *, VuoCompilerModule *> > &modulesModified,
6108  const set<VuoCompilerModule *> &modulesRemoved, VuoCompilerIssues *issues)
6109 {
6110  referenceCountQueue = dispatch_queue_create("org.vuo.compiler.delegate.reference", 0);
6111  referenceCount = 0;
6112 
6113  this->modulesModified = modulesModified;
6114  this->modulesRemoved = modulesRemoved;
6115  this->issues = issues;
6116 }
6117 
6121 VuoCompilerDelegate::LoadedModulesData::~LoadedModulesData(void)
6122 {
6123  delete issues;
6124 
6125  for (set< pair<VuoCompilerModule *, VuoCompilerModule *> >::iterator i = modulesModified.begin(); i != modulesModified.end(); ++i)
6126  VuoCompiler::destroyModule((*i).first);
6127 
6128  for (set<VuoCompilerModule *>::iterator i = modulesRemoved.begin(); i != modulesRemoved.end(); ++i)
6130 }
6131 
6135 void VuoCompilerDelegate::LoadedModulesData::retain(void)
6136 {
6137  dispatch_sync(referenceCountQueue, ^{
6138  ++referenceCount;
6139  });
6140 }
6141 
6145 void VuoCompilerDelegate::LoadedModulesData::release(void)
6146 {
6147  dispatch_sync(referenceCountQueue, ^{
6148  --referenceCount;
6149  if (referenceCount == 0) {
6150  delete this;
6151  }
6152  });
6153 }