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  init_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 #if VUO_PRO
164  fini_Pro();
165 #endif
166 
167  delete file;
168 
169  if (loading)
170  dispatch_release(loading);
171 }
172 
173 VuoCompiler::Environment * VuoCompiler::ModuleInfo::getEnvironment(void) const
174 {
175  return environment;
176 }
177 
178 string VuoCompiler::ModuleInfo::getSearchPath(void) const
179 {
180  return searchPath;
181 }
182 
183 string VuoCompiler::ModuleInfo::getModuleKey(void) const
184 {
185  return moduleKey;
186 }
187 
188 VuoFileUtilities::File * VuoCompiler::ModuleInfo::getFile(void) const
189 {
190  return file;
191 }
192 
193 string VuoCompiler::ModuleInfo::getSourceCode(void) const
194 {
195  return sourceCode;
196 }
197 
198 void VuoCompiler::ModuleInfo::setSourceCode(const string &sourceCode)
199 {
200  this->sourceCode = sourceCode;
201 }
202 
203 void VuoCompiler::ModuleInfo::revertSourceCode(void)
204 {
205  sourceCode = file->getContentsAsString();
206 }
207 
208 bool VuoCompiler::ModuleInfo::isSourceCodeOverridden(void) const
209 {
210  return sourceCodeOverridden;
211 }
212 
213 void VuoCompiler::ModuleInfo::setSourceCodeOverridden(bool overridden)
214 {
215  sourceCodeOverridden = overridden;
216 }
217 
218 bool VuoCompiler::ModuleInfo::isNewerThan(ModuleInfo *other) const
219 {
220  return lastModified > other->lastModified;
221 }
222 
223 bool VuoCompiler::ModuleInfo::isNewerThan(unsigned long ageInSeconds) const
224 {
225  return lastModified > ageInSeconds;
226 }
227 
228 bool VuoCompiler::ModuleInfo::isOlderThan(unsigned long ageInSeconds) const
229 {
230  return lastModified < ageInSeconds;
231 }
232 
233 void VuoCompiler::ModuleInfo::setLastModifiedToNow(void)
234 {
235  struct timeval t;
236  gettimeofday(&t, NULL);
237  lastModified = t.tv_sec;
238 }
239 
240 set<string> VuoCompiler::ModuleInfo::getContainedNodeClasses(void) const
241 {
242  return containedNodeClasses;
243 }
244 
245 bool VuoCompiler::ModuleInfo::getAttempted(void) const
246 {
247  return attempted;
248 }
249 
250 void VuoCompiler::ModuleInfo::setAttempted(bool attempted)
251 {
252  this->attempted = attempted;
253 }
254 
255 dispatch_group_t VuoCompiler::ModuleInfo::getLoadingGroup(void) const
256 {
257  return loading;
258 }
259 
260 void VuoCompiler::ModuleInfo::dump() const
261 {
262  fprintf(stderr, "module %s:\n"
263  "\tloadingGroup=%p\n"
264  "\tsearchPath=%s\n"
265  "\tattempted=%d\n"
266  "\tenvironment=%s\n"
267  "\tfile=%s%s%s\n"
268  "\tsourceCodeOverridden=%d\n"
269  "\tsourceCode=%s\n"
270  "\tcontainedNodeClasses:\n",
271  moduleKey.c_str(),
272  loading,
273  searchPath.c_str(),
274  attempted,
275  environment->getCompiledModuleCachePath().c_str(),
276  file->isInArchive() ? file->getArchivePath().c_str() : "",
277  file->isInArchive() ? "::" : "",
278  file->isInArchive() ? file->getRelativePath().c_str() : file->path().c_str(),
279  sourceCodeOverridden,
280  sourceCode.c_str());
281  for (auto i: containedNodeClasses)
282  fprintf(stderr, "\t\t%s\n", i.c_str());
283 }
284 
285 VuoCompiler::DependencyGraphVertex * VuoCompiler::DependencyGraphVertex::vertexForDependency(const string &dependency, VuoDirectedAcyclicGraph *graph)
286 {
287  VuoDirectedAcyclicGraph::Vertex *v = graph->findVertex(dependency);
288  if (v)
289  return dynamic_cast<DependencyGraphVertex *>(v);
290 
291  DependencyGraphVertex *vv = new DependencyGraphVertex();
292  vv->dependency = dependency;
293  vv->environment = NULL;
294  vv->compatible = true;
295  return vv;
296 }
297 
298 string VuoCompiler::DependencyGraphVertex::getDependency(void)
299 {
300  return dependency;
301 }
302 
303 VuoCompiler::Environment * VuoCompiler::DependencyGraphVertex::getEnvironment(void)
304 {
305  return environment;
306 }
307 
308 void VuoCompiler::DependencyGraphVertex::setEnvironment(Environment *environment)
309 {
310  this->environment = environment;
311 }
312 
313 bool VuoCompiler::DependencyGraphVertex::isCompatible(void)
314 {
315  return compatible;
316 }
317 
318 void VuoCompiler::DependencyGraphVertex::setCompatible(bool compatible)
319 {
320  this->compatible = compatible;
321 }
322 
323 string VuoCompiler::DependencyGraphVertex::key(void)
324 {
325  return dependency;
326 }
327 
333 VuoCompiler::ModuleInfoIterator::ModuleInfoIterator(map<string, map<string, ModuleInfo *> > *allModuleInfos)
334 {
335  this->allModuleInfos = allModuleInfos;
336  hasSearchModuleKeys = false;
337  initialize();
338 }
339 
345 VuoCompiler::ModuleInfoIterator::ModuleInfoIterator(map<string, map<string, ModuleInfo *> > *allModuleInfos, const set<string> &searchModuleKeys)
346 {
347  this->allModuleInfos = allModuleInfos;
348  this->searchModuleKeys = searchModuleKeys;
349  hasSearchModuleKeys = true;
350  initialize();
351 }
352 
358 void VuoCompiler::ModuleInfoIterator::initialize(void)
359 {
360  currSearchPath = allModuleInfos->begin();
361  hasCurrModuleKey = false;
362 }
363 
369 VuoCompiler::ModuleInfo * VuoCompiler::ModuleInfoIterator::next(void)
370 {
371  for ( ; currSearchPath != allModuleInfos->end(); ++currSearchPath)
372  {
373  if (! hasCurrModuleKey)
374  {
375  currModuleKey = currSearchPath->second.begin();
376  hasCurrModuleKey = true;
377  }
378 
379  for ( ; currModuleKey != currSearchPath->second.end(); ++currModuleKey)
380  {
381  if (! hasSearchModuleKeys || searchModuleKeys.find(currModuleKey->first) != searchModuleKeys.end())
382  {
383  ModuleInfo *moduleInfo = currModuleKey->second;
384  ++currModuleKey;
385  return moduleInfo;
386  }
387  }
388 
389  hasCurrModuleKey = false;
390  }
391 
392  return NULL;
393 }
394 
398 VuoCompiler::Environment::Environment(void)
399 {
400  compilersToNotifyQueue = dispatch_queue_create("org.vuo.compiler.notify", 0);
401  moduleSearchPathContentsChangedQueue = dispatch_queue_create("org.vuo.compiler.watch", 0);
402  isModuleCacheableDataDirty = false;
403  isModuleCacheInitialized = false;
404  isModuleCacheAvailable = false;
405  dependencyGraph = new VuoDirectedAcyclicGraph();
406  compositionDependencyGraph = new VuoDirectedAcyclicGraph();
407  moduleCompilationQueue = new VuoModuleCompilationQueue();
408 }
409 
413 VuoCompiler::Environment::~Environment(void)
414 {
415  stopWatchingModuleSearchPaths();
416  dispatch_sync(moduleSearchPathContentsChangedQueue, ^{});
417 
418  dispatch_release(moduleSearchPathContentsChangedQueue);
419  dispatch_release(compilersToNotifyQueue);
420 
421  for (map<string, VuoCompilerNodeClass *>::iterator i = nodeClasses.begin(); i != nodeClasses.end(); ++i)
422  destroyModule(i->second);
423 
424  for (map<string, VuoCompilerType *>::iterator i = types.begin(); i != types.end(); ++i)
425  destroyModule(i->second);
426 
427  for (map<string, VuoCompilerModule *>::iterator i = libraryModules.begin(); i != libraryModules.end(); ++i)
428  destroyModule(i->second);
429 
430  for (set<VuoCompilerGenericType *>::iterator i = genericTypes.begin(); i != genericTypes.end(); ++i)
431  {
432  VuoType *base = (*i)->getBase();
433  delete *i;
434  delete base;
435  }
436 
437  for (map<string, VuoNodeSet *>::iterator i = nodeSetForName.begin(); i != nodeSetForName.end(); ++i)
438  delete i->second;
439 
440  for (map<string, map<string, ModuleInfo *> >::iterator i = moduleFilesAtSearchPath.begin(); i != moduleFilesAtSearchPath.end(); ++i)
441  for (map<string, ModuleInfo *>::iterator j = i->second.begin(); j != i->second.end(); ++j)
442  delete j->second;
443 
444  for (map<string, map<string, ModuleInfo *> >::iterator i = sourceFilesAtSearchPath.begin(); i != sourceFilesAtSearchPath.end(); ++i)
445  for (map<string, ModuleInfo *>::iterator j = i->second.begin(); j != i->second.end(); ++j)
446  delete j->second;
447 
448  delete dependencyGraph;
449  delete compositionDependencyGraph;
450 }
451 
455 void VuoCompiler::Environment::addCompilerToNotify(VuoCompiler *compiler)
456 {
457  dispatch_sync(compilersToNotifyQueue, ^{
458  compilersToNotify.insert(compiler);
459  });
460 }
461 
465 void VuoCompiler::Environment::removeCompilerToNotify(VuoCompiler *compiler)
466 {
467  dispatch_sync(compilersToNotifyQueue, ^{
468  compilersToNotify.erase(compiler);
469  });
470 }
471 
477 map<string, VuoCompilerNodeClass *> VuoCompiler::Environment::getNodeClasses(void)
478 {
479  return nodeClasses;
480 }
481 
487 VuoCompilerNodeClass * VuoCompiler::Environment::getNodeClass(const string &moduleKey)
488 {
489  map<string, VuoCompilerNodeClass *>::iterator nodeClassIter = nodeClasses.find(moduleKey);
490  if (nodeClassIter != nodeClasses.end())
491  return nodeClassIter->second;
492 
493  return NULL;
494 }
495 
500 VuoCompilerNodeClass * VuoCompiler::Environment::takeNodeClass(const string &moduleKey)
501 {
502  VuoCompilerNodeClass *nodeClass = NULL;
503 
504  map<string, VuoCompilerNodeClass *>::iterator nodeClassIter = nodeClasses.find(moduleKey);
505  if (nodeClassIter != nodeClasses.end())
506  {
507  nodeClass = nodeClassIter->second;
508 
509  nodeClasses.erase(nodeClassIter);
510  removeFromDependencyGraph(nodeClass);
511  modulesChanged();
512  }
513 
514  return nodeClass;
515 }
516 
522 map<string, VuoCompilerType *> VuoCompiler::Environment::getTypes(void)
523 {
524  return types;
525 }
526 
532 VuoCompilerType * VuoCompiler::Environment::getType(const string &moduleKey)
533 {
534  map<string, VuoCompilerType *>::iterator typeIter = types.find(moduleKey);
535  if (typeIter != types.end())
536  return typeIter->second;
537 
538  return NULL;
539 }
540 
546 map<string, VuoNodeSet *> VuoCompiler::Environment::getNodeSets(void)
547 {
548  return nodeSetForName;
549 }
550 
556 map<string, VuoCompilerModule *> VuoCompiler::Environment::getLibraryModules(void)
557 {
558  return libraryModules;
559 }
560 
567 VuoCompilerModule * VuoCompiler::Environment::findModule(const string &moduleKey)
568 {
569  map<string, VuoCompilerNodeClass *>::iterator nodeClassIter = nodeClasses.find(moduleKey);
570  if (nodeClassIter != nodeClasses.end())
571  return nodeClassIter->second;
572 
573  map<string, VuoCompilerType *>::iterator typeIter = types.find(moduleKey);
574  if (typeIter != types.end())
575  return typeIter->second;
576 
577  map<string, VuoCompilerModule *>::iterator libraryIter = libraryModules.find(moduleKey);
578  if (libraryIter != libraryModules.end())
579  return libraryIter->second;
580 
581  return NULL;
582 }
583 
589 VuoNodeSet * VuoCompiler::Environment::findNodeSet(const string &name)
590 {
591 
592  map<string, VuoNodeSet *>::iterator nodeSetIter = nodeSetForName.find(name);
593  if (nodeSetIter != nodeSetForName.end())
594  return nodeSetIter->second;
595 
596  return NULL;
597 }
598 
605 void VuoCompiler::Environment::addModuleSearchPath(const string &path, bool shouldWatch)
606 {
607  moduleSearchPaths.push_back(path);
608 
609  updateModulesAtSearchPath(path);
610  updateSourceFilesAtSearchPath(path);
611 
612  if (shouldWatch)
613  startWatchingModuleSearchPath(path);
614 }
615 
621 vector<string> VuoCompiler::Environment::getModuleSearchPaths(void)
622 {
623  return moduleSearchPaths;
624 }
625 
631 void VuoCompiler::Environment::addHeaderSearchPath(const string &path)
632 {
633  headerSearchPaths.push_back(path);
634 }
635 
641 vector<string> VuoCompiler::Environment::getHeaderSearchPaths(void)
642 {
643  return headerSearchPaths;
644 }
645 
651 void VuoCompiler::Environment::addLibrarySearchPath(const string &path)
652 {
653  librarySearchPaths.push_back(path);
654 }
655 
661 vector<string> VuoCompiler::Environment::getLibrarySearchPaths(void)
662 {
663  return librarySearchPaths;
664 }
665 
671 void VuoCompiler::Environment::addFrameworkSearchPath(const string &path)
672 {
673  frameworkSearchPaths.push_back(path);
674 }
675 
681 vector<string> VuoCompiler::Environment::getFrameworkSearchPaths(void)
682 {
683  return frameworkSearchPaths;
684 }
685 
686 VuoDirectedAcyclicGraph * VuoCompiler::Environment::getDependencyGraph(void)
687 {
688  return dependencyGraph;
689 }
690 
691 VuoDirectedAcyclicGraph * VuoCompiler::Environment::getCompositionDependencyGraph(void)
692 {
693  return compositionDependencyGraph;
694 }
695 
701 void VuoCompiler::Environment::setModuleCachePath(const string &path)
702 {
703  moduleCachePath = path;
704 }
705 
711 string VuoCompiler::Environment::getCompiledModuleCachePath(void)
712 {
713  return (moduleCachePath.empty() ? "" : moduleCachePath + "/Modules");
714 }
715 
721 string VuoCompiler::Environment::getOverriddenCompiledModuleCachePath(void)
722 {
723  if (moduleCachePath.empty())
724  return "";
725 
726  ostringstream pid;
727  pid << getpid();
728 
729  string dir, moduleCacheDirName, ext;
730  VuoFileUtilities::splitPath(moduleCachePath, dir, moduleCacheDirName, ext);
731 
732  return (VuoFileUtilities::getCachePath() + "/" + pidCacheDirPrefix + pid.str() + "/" + moduleCacheDirName + "/Modules");
733 }
734 
742 void VuoCompiler::Environment::addExpatriateSourceFile(const string &sourcePath)
743 {
744  expatriateSourceFiles.push_back(sourcePath);
745 
746  if (find(moduleSearchPaths.begin(), moduleSearchPaths.end(), "") == moduleSearchPaths.end())
747  moduleSearchPaths.push_back("");
748 
749  auto iter = sourceFilesAtSearchPath.find("");
750  if (iter != sourceFilesAtSearchPath.end())
751  sourceFilesAtSearchPath.erase(iter);
752 }
753 
759 void VuoCompiler::Environment::removeExpatriateSourceFile(const string &sourcePath)
760 {
761  for (auto i = expatriateSourceFiles.begin(); i != expatriateSourceFiles.end(); ++i)
762  {
763  if (VuoFileUtilities::arePathsEqual(*i, sourcePath))
764  {
765  expatriateSourceFiles.erase(i);
766 
767  auto iter = sourceFilesAtSearchPath.find("");
768  if (iter != sourceFilesAtSearchPath.end())
769  sourceFilesAtSearchPath.erase(iter);
770 
771  break;
772  }
773  }
774 }
775 
788 void VuoCompiler::Environment::updateModulesAtSearchPath(const string &path, bool shouldCleanModuleCache)
789 {
790  if (moduleFilesAtSearchPath.find(path) != moduleFilesAtSearchPath.end())
791  return;
792 
793  set<string> moduleExtensions;
794  moduleExtensions.insert("vuonode");
795  moduleExtensions.insert("vuonode+");
796  moduleExtensions.insert("bc");
797  moduleExtensions.insert("bc+");
798  set<string> archiveExtensions;
799  archiveExtensions.insert("vuonode");
800  set<VuoFileUtilities::File *> moduleFiles = VuoFileUtilities::findFilesInDirectory(path, moduleExtensions, archiveExtensions);
801 
802  map<string, ModuleInfo *> fileForModuleKey;
803  for (set<VuoFileUtilities::File *>::iterator i = moduleFiles.begin(); i != moduleFiles.end(); ++i)
804  {
805  VuoFileUtilities::File *moduleFile = *i;
806 
807  ModuleInfo *m = new ModuleInfo(this, path, moduleFile, false, false);
808  fileForModuleKey[m->getModuleKey()] = m;
809  }
810 
811  if (shouldCleanModuleCache && path == getCompiledModuleCachePath())
812  {
813  for (map<string, ModuleInfo *>::iterator i = fileForModuleKey.begin(); i != fileForModuleKey.end(); )
814  {
815  ModuleInfo *sourceInfo = listSourceFile(i->first);
816  if (! sourceInfo || sourceInfo->isNewerThan(i->second))
817  {
818  ModuleInfo *m = i->second;
819  VuoFileUtilities::deleteFile(m->getFile()->path());
820  delete m;
821  fileForModuleKey.erase(i++);
822  }
823  else
824  ++i;
825  }
826  }
827 
828  moduleFilesAtSearchPath[path] = fileForModuleKey;
829 }
830 
838 void VuoCompiler::Environment::updateSourceFilesAtSearchPath(const string &path)
839 {
840  map<string, map<string, ModuleInfo *> >::iterator sourceFilesIter = sourceFilesAtSearchPath.find(path);
841  if (sourceFilesIter != sourceFilesAtSearchPath.end())
842  return;
843 
844  set<VuoFileUtilities::File *> sourceFiles;
845  if (! path.empty())
846  {
847  set<string> sourceExtensions;
848  sourceExtensions.insert("vuo");
849  sourceExtensions.insert("fs");
851  sourceExtensions.insert(cext.begin(), cext.end());
852  sourceFiles = VuoFileUtilities::findFilesInDirectory(path, sourceExtensions, set<string>());
853  }
854  else
855  {
856  for (const string &sourcePath : expatriateSourceFiles)
857  {
858  string dir, file, ext;
859  VuoFileUtilities::splitPath(sourcePath, dir, file, ext);
860  VuoFileUtilities::File *sourceFile = new VuoFileUtilities::File(dir, file + "." + ext);
861  sourceFiles.insert(sourceFile);
862  }
863  }
864 
865  map<string, ModuleInfo *> fileForModuleKey;
866  for (set<VuoFileUtilities::File *>::iterator i = sourceFiles.begin(); i != sourceFiles.end(); ++i)
867  {
868  VuoFileUtilities::File *sourceFile = *i;
869 
870  string dir, moduleKey, ext;
871  VuoFileUtilities::splitPath(sourceFile->getRelativePath(), dir, moduleKey, ext);
872  bool isSubcomposition = (ext == "vuo");
873 
874  // Ignore missing expatriateSourceFiles — they might have been deleted in the meantime.
875  if (path.empty() && !sourceFile->exists())
876  continue;
877 
878  ModuleInfo *m = new ModuleInfo(this, path, sourceFile, true, isSubcomposition);
879  if (fileForModuleKey.find(m->getModuleKey()) != fileForModuleKey.end())
880  VUserLog("Warning: Conflicting source files for module %s are installed at %s", m->getModuleKey().c_str(), path.c_str());
881  fileForModuleKey[m->getModuleKey()] = m;
882 
883  if (isSubcomposition)
884  {
885  DependencyGraphVertex *compositionVertex = DependencyGraphVertex::vertexForDependency(moduleKey, compositionDependencyGraph);
886  compositionDependencyGraph->addVertex(compositionVertex);
887 
888  compositionVertex->setEnvironment(this);
889 
890  set<string> dependencies = m->getContainedNodeClasses();
891  for (set<string>::iterator j = dependencies.begin(); j != dependencies.end(); ++j)
892  {
893  DependencyGraphVertex *dependencyVertex = DependencyGraphVertex::vertexForDependency(*j, compositionDependencyGraph);
894  compositionDependencyGraph->addEdge(compositionVertex, dependencyVertex);
895  }
896  }
897  }
898 
899  sourceFilesAtSearchPath[path] = fileForModuleKey;
900 }
901 
907 VuoCompiler::ModuleInfo * VuoCompiler::Environment::listModule(const string &moduleKey)
908 {
909  for (const auto &moduleFiles : moduleFilesAtSearchPath)
910  {
911  map<string, ModuleInfo *>::const_iterator foundIter = moduleFiles.second.find(moduleKey);
912  if (foundIter != moduleFiles.second.end())
913  return foundIter->second;
914  }
915 
916  return NULL;
917 }
918 
924 VuoCompiler::ModuleInfoIterator VuoCompiler::Environment::listModules(const set<string> &moduleKeys)
925 {
926  for (const string &path : moduleSearchPaths)
927  updateModulesAtSearchPath(path);
928 
929  return ModuleInfoIterator(&moduleFilesAtSearchPath, moduleKeys);
930 }
931 
937 VuoCompiler::ModuleInfoIterator VuoCompiler::Environment::listAllModules(void)
938 {
939  for (const string &path : moduleSearchPaths)
940  updateModulesAtSearchPath(path);
941 
942  return ModuleInfoIterator(&moduleFilesAtSearchPath);
943 }
944 
950 VuoCompiler::ModuleInfo * VuoCompiler::Environment::listSourceFile(const string &moduleKey)
951 {
952  for (const auto &sourceFiles : sourceFilesAtSearchPath)
953  {
954  map<string, ModuleInfo *>::const_iterator foundIter = sourceFiles.second.find(moduleKey);
955  if (foundIter != sourceFiles.second.end())
956  return foundIter->second;
957  }
958 
959  return NULL;
960 }
961 
967 VuoCompiler::ModuleInfoIterator VuoCompiler::Environment::listSourceFiles(const set<string> &moduleKeys)
968 {
969  for (const string &path : moduleSearchPaths)
970  updateSourceFilesAtSearchPath(path);
971 
972  return ModuleInfoIterator(&sourceFilesAtSearchPath, moduleKeys);
973 }
974 
980 VuoCompiler::ModuleInfoIterator VuoCompiler::Environment::listAllSourceFiles(void)
981 {
982  for (const string &path : moduleSearchPaths)
983  updateSourceFilesAtSearchPath(path);
984 
985  return ModuleInfoIterator(&sourceFilesAtSearchPath);
986 }
987 
991 vector<string> VuoCompiler::Environment::getBuiltInModuleSearchPaths(void)
992 {
993  vector<string> builtInModuleSearchPaths;
994 
995  string vuoFrameworkPath = getVuoFrameworkPath();
996  if (! vuoFrameworkPath.empty())
997  {
998  builtInModuleSearchPaths.push_back(vuoFrameworkPath + "/Modules");
999  }
1000  else
1001  {
1002  builtInModuleSearchPaths.push_back(VUO_BUILD_DIR "/library");
1003  builtInModuleSearchPaths.push_back(VUO_BUILD_DIR "/node");
1004  builtInModuleSearchPaths.push_back(VUO_BUILD_DIR "/type");
1005  builtInModuleSearchPaths.push_back(VUO_BUILD_DIR "/type/list");
1006  }
1007 
1008  return builtInModuleSearchPaths;
1009 }
1010 
1014 vector<string> VuoCompiler::Environment::getBuiltInHeaderSearchPaths(void)
1015 {
1016  vector<string> builtInHeaderSearchPaths;
1017 
1018  string vuoFrameworkPath = getVuoFrameworkPath();
1019  if (! vuoFrameworkPath.empty())
1020  {
1021  builtInHeaderSearchPaths.push_back(vuoFrameworkPath + "/Headers");
1022  builtInHeaderSearchPaths.push_back(vuoFrameworkPath + "/Headers/macos"); // system headers installed by Xcode Command Line Tools
1023  builtInHeaderSearchPaths.push_back(vuoFrameworkPath + "/Frameworks/llvm.framework/Versions/A/Headers/lib/c++/v1");
1024  }
1025  else
1026  {
1027  builtInHeaderSearchPaths.push_back(VUO_SOURCE_DIR "/library");
1028  builtInHeaderSearchPaths.push_back(VUO_SOURCE_DIR "/node");
1029  builtInHeaderSearchPaths.push_back(VUO_SOURCE_DIR "/type");
1030  builtInHeaderSearchPaths.push_back(VUO_SOURCE_DIR "/type/list");
1031  builtInHeaderSearchPaths.push_back(VUO_SOURCE_DIR "/runtime");
1032  builtInHeaderSearchPaths.push_back(JSONC_ROOT "/include");
1033  builtInHeaderSearchPaths.push_back(LLVM_ROOT "/include/c++/v1");
1034  }
1035 
1036  return builtInHeaderSearchPaths;
1037 }
1038 
1042 vector<string> VuoCompiler::Environment::getBuiltInLibrarySearchPaths(void)
1043 {
1044  vector<string> builtInLibrarySearchPaths;
1045 
1046  string vuoFrameworkPath = getVuoFrameworkPath();
1047  if (! vuoFrameworkPath.empty())
1048  {
1049  builtInLibrarySearchPaths.push_back(vuoFrameworkPath + "/Modules");
1050 
1051  // Ensure we (statically) link to our OpenSSL build when generating Vuo.framework's built-in module cache.
1052  builtInLibrarySearchPaths.push_back(OPENSSL_ROOT "/lib");
1053  }
1054  else
1055  {
1056  builtInLibrarySearchPaths.push_back(VUO_BUILD_DIR "/library");
1057  builtInLibrarySearchPaths.push_back(VUO_BUILD_DIR "/node");
1058  builtInLibrarySearchPaths.push_back(VUO_BUILD_DIR "/type");
1059  builtInLibrarySearchPaths.push_back(VUO_BUILD_DIR "/type/list");
1060  builtInLibrarySearchPaths.push_back(VUO_BUILD_DIR "/runtime");
1061 
1062  builtInLibrarySearchPaths.push_back(LEAP_ROOT);
1063 
1064  vector<string> conanLibDirs = VuoStringUtilities::split(CONAN_LIBRARY_PATHS, ';');
1065  builtInLibrarySearchPaths.insert(builtInLibrarySearchPaths.end(), conanLibDirs.begin(), conanLibDirs.end());
1066  }
1067 
1068  return builtInLibrarySearchPaths;
1069 }
1070 
1074 vector<string> VuoCompiler::Environment::getBuiltInFrameworkSearchPaths(void)
1075 {
1076  vector<string> builtInFrameworkSearchPaths;
1077 
1078  string vuoFrameworkPath = getVuoFrameworkPath();
1079  if (! vuoFrameworkPath.empty())
1080  {
1081  builtInFrameworkSearchPaths.push_back(vuoFrameworkPath + "/Modules/");
1082  builtInFrameworkSearchPaths.push_back(vuoFrameworkPath + "/Frameworks/");
1083  }
1084  else
1085  {
1086  builtInFrameworkSearchPaths.push_back(SYPHON_ROOT);
1087  }
1088 
1089  return builtInFrameworkSearchPaths;
1090 }
1091 
1098 void VuoCompiler::Environment::startWatchingModuleSearchPath(const string &moduleSearchPath)
1099 {
1100  VuoFileWatcher *watcher = new VuoFileWatcher(this, moduleSearchPath);
1101  moduleSearchPathWatchers.insert(watcher);
1102 }
1103 
1109 void VuoCompiler::Environment::stopWatchingModuleSearchPaths(void)
1110 {
1111  for (set<VuoFileWatcher *>::iterator i = moduleSearchPathWatchers.begin(); i != moduleSearchPathWatchers.end(); ++i)
1112  delete *i;
1113 
1114  moduleSearchPathWatchers.clear();
1115 }
1116 
1121 void VuoCompiler::Environment::fileChanged(const string &moduleSearchPath)
1122 {
1123  dispatch_sync(moduleSearchPathContentsChangedQueue, ^{
1124  moduleSearchPathContentsChanged(moduleSearchPath);
1125  });
1126 }
1127 
1135 void VuoCompiler::Environment::moduleSearchPathContentsChanged(const string &moduleSearchPath, const string &moduleAddedOrModifiedPath,
1136  const string &moduleAddedOrModifiedSourceCode,
1137  std::function<void(void)> moduleLoadedCallback,
1138  VuoCompiler *compiler, VuoCompilerIssues *issues)
1139 {
1140  //VLog(" E=%p -- %s", this, moduleSearchPath.c_str());
1141  VuoCompiler *compilerForLoading = (compiler ? compiler : new VuoCompiler(moduleSearchPath + "/unused"));
1142 
1143  __block set<string> modulesAdded;
1144  __block set<string> modulesModifed;
1145  __block set<string> modulesRemoved;
1146  __block set<string> compositionsAdded;
1147  __block set<string> compositionsModifed;
1148  __block set<string> compositionsRemoved;
1149 
1150  dispatch_sync(environmentQueue, ^{
1151 
1152  // Remove the old file records from the environment.
1153 
1154  map<string, ModuleInfo *> oldModules;
1155  map<string, ModuleInfo *> oldCompositions;
1156 
1157  map<string, map<string, ModuleInfo *> >::iterator mf = moduleFilesAtSearchPath.find(moduleSearchPath);
1158  if (mf != moduleFilesAtSearchPath.end())
1159  {
1160  for (map<string, ModuleInfo *>::iterator j = mf->second.begin(); j != mf->second.end(); ++j)
1161  {
1162  oldModules[j->first] = j->second;
1163  }
1164 
1165  moduleFilesAtSearchPath.erase(mf);
1166  }
1167 
1168  map<string, map<string, ModuleInfo *> >::iterator cf = sourceFilesAtSearchPath.find(moduleSearchPath);
1169  if (cf != sourceFilesAtSearchPath.end())
1170  {
1171  for (map<string, ModuleInfo *>::iterator j = cf->second.begin(); j != cf->second.end(); ++j)
1172  {
1173  oldCompositions[j->first] = j->second;
1174 
1175  VuoDirectedAcyclicGraph::Vertex *vertex = compositionDependencyGraph->findVertex(j->first);
1176  compositionDependencyGraph->removeVertex(vertex);
1177  }
1178 
1179  sourceFilesAtSearchPath.erase(cf);
1180  }
1181 
1182  // Compare the old and new file records to see what has changed.
1183 
1184  updateModulesAtSearchPath(moduleSearchPath, moduleAddedOrModifiedPath.empty());
1185  updateSourceFilesAtSearchPath(moduleSearchPath);
1186 
1187  mf = moduleFilesAtSearchPath.find(moduleSearchPath);
1188  if (mf != moduleFilesAtSearchPath.end())
1189  {
1190  for (map<string, ModuleInfo *>::iterator n = mf->second.begin(); n != mf->second.end(); ++n)
1191  {
1192  string moduleKey = n->first;
1193 
1194  map<string, ModuleInfo *>::iterator o = oldModules.find(moduleKey);
1195  if (o != oldModules.end())
1196  {
1197  if (n->second->isNewerThan(o->second) ||
1198  (n->second->getFile() && (n->second->getFile()->isInArchive() || VuoFileUtilities::arePathsEqual(n->second->getFile()->path(), moduleAddedOrModifiedPath))))
1199  {
1200  modulesModifed.insert(moduleKey);
1201  }
1202  else
1203  {
1204  n->second->setAttempted(o->second->getAttempted());
1205  }
1206 
1207  delete o->second;
1208  oldModules.erase(o);
1209  }
1210  else if (VuoFileUtilities::arePathsEqual(moduleSearchPath, getOverriddenCompiledModuleCachePath()))
1211  {
1212  modulesModifed.insert(moduleKey);
1213  }
1214  else
1215  {
1216  modulesAdded.insert(moduleKey);
1217  }
1218  }
1219  }
1220 
1221  cf = sourceFilesAtSearchPath.find(moduleSearchPath);
1222  if (cf != sourceFilesAtSearchPath.end())
1223  {
1224  for (map<string, ModuleInfo *>::iterator n = cf->second.begin(); n != cf->second.end(); ++n)
1225  {
1226  string moduleKey = n->first;
1227 
1228  map<string, ModuleInfo *>::iterator o = oldCompositions.find(moduleKey);
1229  if (o != oldCompositions.end())
1230  {
1231  if (n->second->isNewerThan(o->second))
1232  {
1233  compositionsModifed.insert(moduleKey);
1234  }
1235  else
1236  {
1237  n->second->setAttempted(o->second->getAttempted());
1238  n->second->setSourceCode(o->second->getSourceCode());
1239  }
1240 
1241  delete o->second;
1242  oldCompositions.erase(o);
1243  }
1244  else
1245  {
1246  compositionsAdded.insert(moduleKey);
1247  }
1248  }
1249  }
1250 
1251  for (map<string, ModuleInfo *>::iterator o = oldModules.begin(); o != oldModules.end(); ++o)
1252  {
1253  delete o->second;
1254  modulesRemoved.insert(o->first);
1255  }
1256 
1257  for (map<string, ModuleInfo *>::iterator o = oldCompositions.begin(); o != oldCompositions.end(); ++o)
1258  {
1259  delete o->second;
1260  compositionsRemoved.insert(o->first);
1261  }
1262 
1263  compilerForLoading->loadModulesAndSources(modulesAdded, modulesModifed, modulesRemoved,
1264  compositionsAdded, compositionsModifed, compositionsRemoved,
1265  false, false, this, issues, moduleLoadedCallback, moduleAddedOrModifiedSourceCode);
1266  });
1267 
1268  if (! compiler)
1269  delete compilerForLoading;
1270 }
1271 
1275 void VuoCompiler::Environment::notifyCompilers(const set<VuoCompilerModule *> &modulesAdded,
1276  const set< pair<VuoCompilerModule *, VuoCompilerModule *> > &modulesModified,
1277  const set<VuoCompilerModule *> &modulesRemoved, VuoCompilerIssues *issues,
1278  bool oldModulesInvalidated)
1279 {
1280  if (! (modulesAdded.empty() && modulesModified.empty() && modulesRemoved.empty() && issues->isEmpty()) )
1281  {
1282  set< pair<VuoCompilerModule *, VuoCompilerModule *> > invalidatedModulesModified;
1283  set<VuoCompilerModule *> invalidatedModulesRemoved;
1284  if (oldModulesInvalidated)
1285  {
1286  invalidatedModulesModified = modulesModified;
1287  invalidatedModulesRemoved = modulesRemoved;
1288  }
1289 
1290  VuoCompilerDelegate::LoadedModulesData *delegateData = new VuoCompilerDelegate::LoadedModulesData(invalidatedModulesModified, invalidatedModulesRemoved, issues);
1291 
1292  map<string, VuoCompilerModule *> modulesAddedMap;
1293  map<string, pair<VuoCompilerModule *, VuoCompilerModule *> > modulesModifiedMap;
1294  map<string, VuoCompilerModule *> modulesRemovedMap;
1295  for (VuoCompilerModule *m : modulesAdded)
1296  modulesAddedMap[m->getPseudoBase()->getModuleKey()] = m;
1297  for (pair<VuoCompilerModule *, VuoCompilerModule *> m : modulesModified)
1298  modulesModifiedMap[m.first->getPseudoBase()->getModuleKey()] = m;
1299  for (VuoCompilerModule *m : modulesRemoved)
1300  modulesRemovedMap[m->getPseudoBase()->getModuleKey()] = m;
1301 
1302  dispatch_sync(compilersToNotifyQueue, ^{
1303  for (set<VuoCompiler *>::iterator i = compilersToNotify.begin(); i != compilersToNotify.end(); ++i) {
1304  delegateData->retain();
1305  }
1306  for (set<VuoCompiler *>::iterator i = compilersToNotify.begin(); i != compilersToNotify.end(); ++i) {
1307  (*i)->loadedModules(modulesAddedMap, modulesModifiedMap, modulesRemovedMap, issues, delegateData, this);
1308  }
1309  });
1310  }
1311  else
1312  {
1313  delete issues;
1314  }
1315 }
1316 
1327 set<VuoCompilerModule *> VuoCompiler::Environment::loadCompiledModules(const set<string> &moduleKeys, const map<string, string> &sourceCodeForModule)
1328 {
1329  ModuleInfoIterator modulesToLoadIter = listModules(moduleKeys);
1330  set<VuoCompilerModule *> modulesLoaded;
1331 
1332  ModuleInfo *moduleInfo;
1333  while ((moduleInfo = modulesToLoadIter.next()))
1334  {
1335  string moduleKey = moduleInfo->getModuleKey();
1336 
1337  // Skip the module if its file is not in this environment, if the module has already been loaded,
1338  // or if the compiler previously tried to load the module and it failed.
1339  if (moduleInfo->getEnvironment() != this || findModule(moduleKey) || moduleInfo->getAttempted())
1340  continue;
1341 
1342  moduleInfo->setAttempted(true);
1343 
1344  // Actually load the module.
1345  VuoCompilerModule *module = loadModule(moduleInfo);
1346 
1347  if (module)
1348  {
1349  modulesLoaded.insert(module);
1350  addToDependencyGraph(module);
1351  modulesChanged();
1352 
1353  // For a compiled subcomposition or other source file, store its source code in the VuoCompilerNodeClass.
1354  string searchPath = moduleInfo->getSearchPath();
1355  if (VuoFileUtilities::arePathsEqual(searchPath, getCompiledModuleCachePath()) ||
1356  VuoFileUtilities::arePathsEqual(searchPath, getOverriddenCompiledModuleCachePath()))
1357  {
1358  VuoCompilerNodeClass *nodeClass = dynamic_cast<VuoCompilerNodeClass *>(module);
1359  if (nodeClass)
1360  {
1361  ModuleInfo *sourceInfo = listSourceFile(moduleKey);
1362  if (sourceInfo)
1363  nodeClass->setSourcePath( sourceInfo->getFile()->path() );
1364 
1365  auto sourceCodeIter = sourceCodeForModule.find(moduleKey);
1366  if (sourceCodeIter != sourceCodeForModule.end())
1367  nodeClass->setSourceCode( sourceCodeIter->second );
1368  }
1369  }
1370  }
1371  }
1372 
1373  return modulesLoaded;
1374 }
1375 
1387 set<dispatch_group_t> VuoCompiler::Environment::loadSpecializedModules(const set<string> &moduleKeys,
1388  VuoCompiler *compiler, dispatch_queue_t llvmQueue)
1389 {
1390  set<dispatch_group_t > loadingGroups;
1391 
1392  for (string moduleKey : moduleKeys)
1393  {
1394  // Skip if it's not a node class.
1395 
1396  if (moduleKey.find(".") == string::npos || VuoStringUtilities::endsWith(moduleKey, ".framework"))
1397  continue;
1398 
1399  // Skip the module if it's already been loaded.
1400 
1401  if (findModule(moduleKey))
1402  continue;
1403 
1404  // Skip the module if it's in the process of being loaded, but have the caller wait for it to finish loading.
1405 
1406  map<string, dispatch_group_t>::iterator iter = specializedModulesLoading.find(moduleKey);
1407  if (iter != specializedModulesLoading.end())
1408  {
1409  loadingGroups.insert(iter->second);
1410  dispatch_retain(iter->second);
1411  continue;
1412  }
1413 
1414  dispatch_group_t loadingGroup = dispatch_group_create();
1415  specializedModulesLoading[moduleKey] = loadingGroup;
1416  loadingGroups.insert(loadingGroup);
1417 
1418  // Generate the module.
1419  // This is done asynchronously since VuoCompilerSpecializedNodeClass::newNodeClass() needs to use environmentQueue
1420  // to compile the generated C code.
1421 
1422  dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
1423  dispatch_group_async(loadingGroup, queue, ^{
1424  VuoNodeClass *baseNodeClass = nullptr;
1425  try
1426  {
1427  baseNodeClass = VuoCompilerSpecializedNodeClass::newNodeClass(moduleKey, compiler, llvmQueue);
1428  }
1429  catch (VuoCompilerException &e)
1430  {
1431  VUserLog("Error: %s", e.what());
1432  }
1433 
1434  if (baseNodeClass)
1435  {
1436  VuoCompilerSpecializedNodeClass *specNodeClass = static_cast<VuoCompilerSpecializedNodeClass *>(baseNodeClass->getCompiler());
1437  compiler->loadNodeClassGeneratedAtRuntime(specNodeClass, this);
1438 
1439  set<string> dependencies = specNodeClass->getDependencies();
1440  compiler->loadModulesIfNeeded(dependencies);
1441  }
1442 
1443  dispatch_sync(environmentQueue, ^{
1444  specializedModulesLoading.erase(moduleKey);
1445  });
1446  });
1447  }
1448 
1449  return loadingGroups;
1450 }
1451 
1464 set<dispatch_group_t> VuoCompiler::Environment::compileModulesFromSourceCode(const set<string> &moduleKeys, bool shouldRecompileIfUnchanged)
1465 {
1466  ModuleInfoIterator modulesToLoadIter = listSourceFiles(moduleKeys);
1467 
1468  set<dispatch_group_t> sourcesLoading;
1469  int sourcesEnqueued = 0;
1470  ModuleInfo *sourceInfo;
1471  while ((sourceInfo = modulesToLoadIter.next()))
1472  {
1473  string moduleKey = sourceInfo->getModuleKey();
1474 
1475  dispatch_group_t loadingGroup = sourceInfo->getLoadingGroup();
1476  sourcesLoading.insert(loadingGroup);
1477 
1478  // Skip compiling if the source file has already been scheduled for compilation.
1479  // Either its compilation is in progress or it failed to compile.
1480 
1481  if (sourceInfo->getAttempted())
1482  continue;
1483 
1484  // Skip compiling if the compiled module is up-to-date.
1485 
1486  string sourceCode = sourceInfo->getSourceCode();
1487 
1488  if (! shouldRecompileIfUnchanged)
1489  {
1490  VuoCompilerNodeClass *existingNodeClass = getNodeClass(moduleKey);
1491  if (existingNodeClass && existingNodeClass->getSourceCode() == sourceCode)
1492  continue;
1493  }
1494 
1495  // Enqueue the source file to be compiled.
1496 
1497  sourceInfo->setAttempted(true);
1498 
1499  dispatch_group_enter(loadingGroup);
1500  dispatch_group_enter(moduleSourceCompilersExist);
1501 
1503  queueItem->moduleKey = moduleKey;
1504  queueItem->sourcePath = sourceInfo->getFile()->path();
1505  queueItem->sourceCode = sourceCode;
1506  queueItem->sourceFile = sourceInfo->getFile();
1507  queueItem->cachedModulesPath = sourceInfo->isSourceCodeOverridden() ? getOverriddenCompiledModuleCachePath() : getCompiledModuleCachePath();
1508  queueItem->compiledModulePath = queueItem->cachedModulesPath + "/" + moduleKey + ".vuonode";
1509  queueItem->loadingGroup = loadingGroup;
1510  moduleCompilationQueue->enqueue(queueItem);
1511  ++sourcesEnqueued;
1512  }
1513 
1514  // Compile each enqueued source file. This is done asynchronously for several reasons:
1515  // - To avoid environmentQueue calling compiler code calling environmentQueue.
1516  // - To compile dependencies that were enqueued later while a compilation that was enqueued earlier waits.
1517  // - To be more efficient when compiling multiple source files.
1518 
1519  for (int i = 0; i < sourcesEnqueued; ++i)
1520  {
1521  dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
1522  dispatch_async(queue, ^{
1523  VuoModuleCompilationQueue::Item *queueItem = moduleCompilationQueue->next();
1524  VUserLog("Compiling %s", queueItem->moduleKey.c_str());
1525 
1526  try
1527  {
1529  }
1530  catch (VuoException &e)
1531  {
1532  VuoCompilerIssue issue(VuoCompilerIssue::Error, "compiling subcomposition", "", "Couldn't create cached modules folder", e.what());
1533  VuoCompilerIssues *issues = new VuoCompilerIssues(issue);
1534  notifyCompilers(set<VuoCompilerModule *>(), set< pair<VuoCompilerModule *, VuoCompilerModule *> >(), set<VuoCompilerModule *>(), issues, false);
1535  }
1536 
1537  VuoCompiler *compiler = new VuoCompiler(queueItem->sourcePath);
1538 
1539  auto moduleLoadedCallback = [=](){
1540  dispatch_group_leave(queueItem->loadingGroup);
1541  moduleCompilationQueue->completed(queueItem);
1542  };
1543 
1544  string ext = queueItem->sourceFile->extension();
1546  {
1547  VuoModuleCompiler *moduleCompiler = NULL;
1548  try
1549  {
1550  moduleCompiler = VuoModuleCompiler::newModuleCompiler("isf", queueItem->moduleKey, queueItem->sourceFile);
1551  }
1552  catch (VuoException &e)
1553  {
1554  VuoCompilerIssues *issues = new VuoCompilerIssues(VuoCompilerIssue(VuoCompilerIssue::Error, "compiling ISF module", queueItem->sourcePath, "", e.what()));
1555  notifyCompilers(set<VuoCompilerModule *>(), set< pair<VuoCompilerModule *, VuoCompilerModule *> >(), set<VuoCompilerModule *>(), issues, false);
1556  }
1557 
1558  if (moduleCompiler)
1559  {
1560  moduleCompiler->overrideSourceCode(queueItem->sourceCode, queueItem->sourceFile);
1561 
1562  auto getType = [&compiler] (const string &moduleKey) { return compiler->getType(moduleKey); };
1563  VuoCompilerIssues *issues = new VuoCompilerIssues();
1564  Module *module = moduleCompiler->compile(getType, llvmQueue, issues);
1565 
1566  if (module)
1567  {
1568  dispatch_sync(llvmQueue, ^{
1569  //setTargetForModule(module, target);
1570  writeModuleToBitcode(module, queueItem->compiledModulePath);
1571  });
1572  }
1573  else
1574  {
1575  issues->setFilePathIfEmpty(queueItem->sourcePath);
1576  }
1577 
1578  moduleSearchPathContentsChanged(queueItem->cachedModulesPath, queueItem->compiledModulePath, queueItem->sourceCode, moduleLoadedCallback, compiler, issues);
1579  }
1580  else
1581  moduleLoadedCallback();
1582  }
1583  else if (VuoFileUtilities::isCSourceExtension(ext) && ! queueItem->cachedModulesPath.empty())
1584  {
1585  try
1586  {
1587  compiler->compileModule(queueItem->sourcePath, queueItem->compiledModulePath, vector<string>());
1588  moduleSearchPathContentsChanged(queueItem->cachedModulesPath, queueItem->compiledModulePath, queueItem->sourceCode, moduleLoadedCallback, compiler);
1589  }
1590  catch (VuoCompilerException &e)
1591  {
1592  VuoCompilerIssues *issues = new VuoCompilerIssues;
1593  for (auto issue : e.getIssues()->getList())
1594  issues->append(issue);
1595  notifyCompilers(set<VuoCompilerModule *>(), set< pair<VuoCompilerModule *, VuoCompilerModule *> >(), set<VuoCompilerModule *>(), issues, false);
1596  moduleLoadedCallback();
1597  }
1598  }
1599  else
1600  {
1601  compiler->setLoadAllModules(false);
1602  compiler->compileSubcompositionString(queueItem->sourceCode, queueItem->compiledModulePath, moduleLoadedCallback, this, NULL, queueItem->sourcePath);
1603  }
1604 
1605  delete compiler;
1606  dispatch_group_leave(moduleSourceCompilersExist);
1607  });
1608  }
1609 
1610  return sourcesLoading;
1611 }
1612 
1621 set<VuoCompilerModule *> VuoCompiler::Environment::unloadCompiledModules(const set<string> &moduleKeys)
1622 {
1623  set<VuoCompilerModule *> modulesUnloaded;
1624 
1625  for (set<string>::const_iterator i = moduleKeys.begin(); i != moduleKeys.end(); ++i)
1626  {
1627  string moduleKey = *i;
1628  VuoCompilerModule *module = NULL;
1629 
1630  map<string, VuoCompilerNodeClass *>::iterator nodeClassIter = nodeClasses.find(moduleKey);
1631  if (nodeClassIter != nodeClasses.end())
1632  {
1633  module = nodeClassIter->second;
1634  nodeClasses.erase(nodeClassIter);
1635  }
1636  else
1637  {
1638  map<string, VuoCompilerType *>::iterator typeIter = types.find(moduleKey);
1639  if (typeIter != types.end())
1640  {
1641  module = typeIter->second;
1642  types.erase(typeIter);
1643  }
1644  else
1645  {
1646  map<string, VuoCompilerModule *>::iterator libraryModuleIter = libraryModules.find(moduleKey);
1647  if (libraryModuleIter != libraryModules.end())
1648  {
1649  module = libraryModuleIter->second;
1650  libraryModules.erase(libraryModuleIter);
1651  }
1652  }
1653  }
1654 
1655  if (module)
1656  {
1657  modulesUnloaded.insert(module);
1658  removeFromDependencyGraph(module);
1659  modulesChanged();
1660  }
1661  }
1662 
1663  return modulesUnloaded;
1664 }
1665 
1671 void VuoCompiler::Environment::deleteModulesCompiledFromSourceCode(const set<string> &moduleKeys)
1672 {
1673  for (set<string>::const_iterator i = moduleKeys.begin(); i != moduleKeys.end(); ++i)
1674  {
1675  string moduleKey = *i;
1676 
1677  map<string, ModuleInfo *>::iterator iter = moduleFilesAtSearchPath[getCompiledModuleCachePath()].find(moduleKey);
1678  if (iter != moduleFilesAtSearchPath[getCompiledModuleCachePath()].end())
1679  VuoFileUtilities::deleteFile(iter->second->getFile()->path());
1680  }
1681 }
1682 
1690 VuoCompilerModule * VuoCompiler::Environment::loadModule(ModuleInfo *moduleInfo)
1691 {
1692  string moduleKey = moduleInfo->getModuleKey();
1693 
1694  // Skip certain LLVM modules that definitely aren't Vuo modules to avoid adding struct types defined in them to the LLVM
1695  // context, resulting in mismatched struct types in code generation (e.g. %struct.NodeContext and %struct.NodeContext.1).
1696  if (VuoStringUtilities::beginsWith(moduleKey, "libVuo"))
1697  return NULL;
1698 
1699  __block size_t inputDataBytes;
1700  __block char *rawInputData;
1701  dispatch_sync(llvmQueue, ^{
1702  try
1703  {
1704  rawInputData = moduleInfo->getFile()->getContentsAsRawData(inputDataBytes);
1705  }
1706  catch (VuoException &e)
1707  {
1708  rawInputData = NULL;
1709  VUserLog("Warning: Couldn't load module '%s'. Its file may have been deleted. (%s)", moduleKey.c_str(), e.what());
1710  }
1711  });
1712  if (! rawInputData)
1713  return NULL;
1714 
1715  char *processedInputData;
1716 #if VUO_PRO
1717  processedInputData = loadModule_Pro0(moduleInfo, moduleKey, inputDataBytes, rawInputData);
1718 #else
1719  processedInputData = rawInputData;
1720 #endif
1721 
1722  Module *module = NULL;
1723  bool moduleParseError = !processedInputData;
1724  if (!moduleParseError)
1725  {
1726  string moduleReadError;
1727  VuoLog_status("Loading module \"%s\"", moduleKey.c_str());
1728  module = readModuleFromBitcodeData(processedInputData, inputDataBytes, moduleReadError);
1729  VuoLog_status(NULL);
1730  free(processedInputData);
1731 
1732  if (!module)
1733  {
1734  moduleParseError = true;
1735 
1736  string dir, file, ext;
1737  VuoFileUtilities::splitPath(moduleInfo->getFile()->isInArchive() ? moduleInfo->getFile()->getRelativePath() : moduleInfo->getFile()->path(), dir, file, ext);
1739  if (dir == getCompiledModuleCachePath())
1740  VuoFileUtilities::deleteFile(moduleInfo->getFile()->path());
1741  else
1742  VUserLog("Error: Couldn't parse module '%s': %s.", moduleInfo->getFile()->getRelativePath().c_str(), moduleReadError.c_str());
1743  }
1744  }
1745 
1746  if (!moduleParseError)
1747  {
1748  string modulePath;
1749  if (! moduleInfo->getFile()->isInArchive())
1750  modulePath = moduleInfo->getFile()->path();
1751 
1752  __block VuoCompilerModule *compilerModule;
1753  dispatch_sync(llvmQueue, ^{
1754  compilerModule = VuoCompilerModule::newModule(moduleKey, module, modulePath);
1755  });
1756 
1757  if (compilerModule)
1758  {
1759  if (dynamic_cast<VuoCompilerNodeClass *>(compilerModule))
1760  nodeClasses[moduleKey] = static_cast<VuoCompilerNodeClass *>(compilerModule);
1761  else if (dynamic_cast<VuoCompilerType *>(compilerModule))
1762  types[moduleKey] = static_cast<VuoCompilerType *>(compilerModule);
1763  else
1764  libraryModules[moduleKey] = compilerModule;
1765 
1766  VuoNodeSet *nodeSet = VuoNodeSet::createNodeSetForModule(moduleInfo->getFile());
1767  if (nodeSet)
1768  {
1769  map<string, VuoNodeSet *>::iterator nodeSetIter = nodeSetForName.find(nodeSet->getName());
1770  if (nodeSetIter == nodeSetForName.end())
1771  {
1772  nodeSetForName[nodeSet->getName()] = nodeSet;
1773  }
1774  else
1775  {
1776  delete nodeSet;
1777  nodeSet = nodeSetIter->second;
1778  }
1779  compilerModule->getPseudoBase()->setNodeSet(nodeSet);
1780  }
1781 
1782 #if VUO_PRO
1783  loadModule_Pro1(rawInputData, processedInputData, compilerModule);
1784 #endif
1785 
1786  compilerModule->setBuiltIn( isBuiltIn() );
1787 
1788  return compilerModule;
1789  }
1790  else
1791  {
1792  destroyLlvmModule(module);
1793  }
1794  }
1795 
1796  return NULL;
1797 }
1798 
1804 void VuoCompiler::Environment::replaceNodeClass(VuoCompilerNodeClass *newNodeClass)
1805 {
1806  string moduleKey = newNodeClass->getBase()->getModuleKey();
1807 
1808  VuoCompilerNodeClass *oldNodeClass = nodeClasses[moduleKey];
1809  if (oldNodeClass)
1810  {
1811  removeFromDependencyGraph(oldNodeClass);
1812  destroyModule(oldNodeClass);
1813  }
1814 
1815  nodeClasses[moduleKey] = newNodeClass;
1816  addToDependencyGraph(newNodeClass);
1817  modulesChanged();
1818 }
1819 
1820 void VuoCompiler::Environment::addToDependencyGraph(VuoCompilerModule *module)
1821 {
1822  DependencyGraphVertex *vertex = DependencyGraphVertex::vertexForDependency(module->getPseudoBase()->getModuleKey(), dependencyGraph);
1823  dependencyGraph->addVertex(vertex);
1824 
1825  vertex->setEnvironment(this);
1826 
1827  VuoCompilerTargetSet compositionTargets;
1828  compositionTargets.restrictToCurrentOperatingSystemVersion();
1829  vertex->setCompatible( module->getCompatibleTargets().isCompatibleWithAllOf(compositionTargets) );
1830 
1831  set<string> dependencies = module->getDependencies();
1832  for (set<string>::iterator i = dependencies.begin(); i != dependencies.end(); ++i)
1833  {
1834  DependencyGraphVertex *depVertex = DependencyGraphVertex::vertexForDependency(*i, dependencyGraph);
1835  dependencyGraph->addEdge(vertex, depVertex);
1836  }
1837 }
1838 
1839 void VuoCompiler::Environment::removeFromDependencyGraph(VuoCompilerModule *module)
1840 {
1841  DependencyGraphVertex *vertex = DependencyGraphVertex::vertexForDependency(module->getPseudoBase()->getModuleKey(), dependencyGraph);
1842  dependencyGraph->removeVertex(vertex);
1843 }
1844 
1855 void VuoCompiler::Environment::reifyPortTypes(const map<string, VuoCompilerType *> &inheritedTypes)
1856 {
1857  map<string, VuoCompilerType *> availableTypes = inheritedTypes;
1858  availableTypes.insert(types.begin(), types.end());
1859 
1860  for (map<string, VuoCompilerNodeClass *>::const_iterator i = nodeClasses.begin(); i != nodeClasses.end(); ++i)
1861  {
1862  VuoNodeClass *nodeClass = i->second->getBase();
1863 
1864  vector<VuoPortClass *> inputPortClasses = nodeClass->getInputPortClasses();
1865  vector<VuoPortClass *> outputPortClasses = nodeClass->getOutputPortClasses();
1866  vector<VuoPortClass *> portClasses;
1867  portClasses.insert(portClasses.end(), inputPortClasses.begin(), inputPortClasses.end());
1868  portClasses.insert(portClasses.end(), outputPortClasses.begin(), outputPortClasses.end());
1869 
1870  for (vector<VuoPortClass *>::iterator j = portClasses.begin(); j != portClasses.end(); ++j)
1871  {
1872  VuoCompilerPortClass *portClass = static_cast<VuoCompilerPortClass *>((*j)->getCompiler());
1873  VuoType *baseType = portClass->getDataVuoType();
1874 
1875  if (baseType && ! baseType->hasCompiler())
1876  {
1877  string typeName = baseType->getModuleKey();
1878  VuoCompilerType *reifiedType = NULL;
1879 
1880  VuoGenericType *genericType = dynamic_cast<VuoGenericType *>(baseType);
1881  if (genericType)
1882  {
1883  reifiedType = VuoCompilerGenericType::newGenericType(genericType, availableTypes);
1884  if (reifiedType) {
1885  genericTypes.insert( static_cast<VuoCompilerGenericType *>(reifiedType) );
1886  }
1887  }
1888  else
1889  {
1890  map<string, VuoCompilerType *>::iterator reifiedTypeIter = availableTypes.find(typeName);
1891  if (reifiedTypeIter != availableTypes.end())
1892  {
1893  delete baseType;
1894  reifiedType = reifiedTypeIter->second;
1895  }
1896  }
1897 
1898  if (reifiedType) {
1899  portClass->setDataVuoType(reifiedType->getBase());
1900  }
1901  }
1902  }
1903 
1904  vector<VuoCompilerTriggerDescription *> triggers = nodeClass->getCompiler()->getTriggerDescriptions();
1905  for (vector<VuoCompilerTriggerDescription *>::iterator j = triggers.begin(); j != triggers.end(); ++j)
1906  {
1907  VuoCompilerTriggerDescription *trigger = *j;
1908  VuoType *baseType = trigger->getDataType();
1909 
1910  if (baseType && ! baseType->hasCompiler())
1911  {
1912  string typeName = baseType->getModuleKey();
1913  map<string, VuoCompilerType *>::iterator reifiedTypeIter = availableTypes.find(typeName);
1914  if (reifiedTypeIter != availableTypes.end())
1915  {
1916  delete baseType;
1917  VuoCompilerType *reifiedType = reifiedTypeIter->second;
1918  trigger->setDataType(reifiedType->getBase());
1919  }
1920  }
1921  }
1922  }
1923 }
1924 
1937 void VuoCompiler::Environment::getCacheableModulesAndDependencies(bool builtIn, bool installed, set<string> &cacheableModulesAndDependencies,
1938  set<string> &dylibsNeededToLinkToThisCache,
1939  set<string> &frameworksNeededToLinkToThisCache)
1940 {
1941  if (isModuleCacheInitialized && ! isModuleCacheableDataDirty)
1942  {
1943  cacheableModulesAndDependencies = moduleCacheContents;
1944  dylibsNeededToLinkToThisCache = moduleCacheDylibs;
1945  frameworksNeededToLinkToThisCache = moduleCacheFrameworks;
1946  return;
1947  }
1948 
1949  VuoCompilerTargetSet compositionTargets;
1950  compositionTargets.restrictToCurrentOperatingSystemVersion();
1951 
1952  // Include all modules…
1953  map<string, VuoCompilerModule *> allModules;
1954  allModules.insert(nodeClasses.begin(), nodeClasses.end());
1955  allModules.insert(types.begin(), types.end());
1956  allModules.insert(libraryModules.begin(), libraryModules.end());
1957 
1958  set<string> dependencies;
1959  for (map<string, VuoCompilerModule *>::iterator i = allModules.begin(); i != allModules.end(); ++i)
1960  {
1961  string moduleKey = i->first;
1962  VuoCompilerModule *module = i->second;
1963 
1964 #if VUO_PRO
1965  // … except Pro modules and modules that depend on them…
1966  if (module->requiresPro())
1967  continue;
1968 #endif
1969 
1970  // … and incompatible modules.
1971  if (! module->getCompatibleTargets().isCompatibleWithAllOf(compositionTargets))
1972  continue;
1973 
1974  cacheableModulesAndDependencies.insert(moduleKey);
1975 
1976  set<string> moduleDependencies = module->getDependencies();
1977  dependencies.insert(moduleDependencies.begin(), moduleDependencies.end());
1978  }
1979 
1980  // For the built-in environment, include Vuo's core dependencies.
1981  if (builtIn && installed)
1982  {
1983  vector<string> coreDependencies = getCoreVuoDependencies();
1984  dependencies.insert(coreDependencies.begin(), coreDependencies.end());
1985  }
1986 
1987  // Include all dependencies of the included module that are located in this environment.
1988  // (All modules are already included, so we only need to handle non-module dependencies.)
1989  for (set<string>::iterator i = dependencies.begin(); i != dependencies.end(); ++i)
1990  {
1991  string dependency = *i;
1992  if (allModules.find(dependency) == allModules.end())
1993  {
1994  if (VuoStringUtilities::endsWith(dependency, ".framework"))
1995  frameworksNeededToLinkToThisCache.insert(dependency);
1996  else
1997  {
1998  string dependencyPath = VuoCompiler::getLibraryPath(dependency, librarySearchPaths);
1999  if (! dependencyPath.empty())
2000  {
2001  if (VuoStringUtilities::endsWith(dependencyPath, ".dylib"))
2002  dylibsNeededToLinkToThisCache.insert(dependencyPath);
2003  else
2004  cacheableModulesAndDependencies.insert(dependency);
2005  }
2006  }
2007  }
2008  }
2009 
2010  moduleCacheSuffix = (installed ? "installed" : "generated");
2011  dylibsNeededToLinkToThisCache.insert(moduleCachePath + "/libVuoModuleCache-" + moduleCacheSuffix + ".dylib");
2012 
2013  moduleCacheDylibs = dylibsNeededToLinkToThisCache;
2014  moduleCacheFrameworks = frameworksNeededToLinkToThisCache;
2015 }
2016 
2036 void VuoCompiler::Environment::useModuleCache(bool shouldUseExistingCache, VuoCompiler *compiler, set<string> cacheableModulesAndDependencies,
2037  set<string> dylibsNeededToLinkToCaches, set<string> frameworksNeededToLinkToCaches)
2038 {
2039  // Ignore the cache if the `prelinkCache` preference is false.
2040 
2041  static dispatch_once_t checked = 0;
2042  static bool prelinkCache = true;
2043  dispatch_once(&checked, ^{
2044  Boolean valid;
2045  bool result = CFPreferencesGetAppBooleanValue(CFSTR("prelinkCache"), CFSTR("org.vuo.Editor"), &valid);
2046  if (valid)
2047  prelinkCache = result;
2048  });
2049  if (! prelinkCache)
2050  {
2051  VDebugLog("Ignoring the module cache since the 'prelinkCache' preference is false.");
2052  return;
2053  }
2054 
2055  // Don't do anything if this environment doesn't have a cache configured.
2056 
2057  if (moduleCachePath.empty())
2058  return;
2059 
2060  // Don't bother rechecking the cache if the modules in this environment haven't changed.
2061 
2062  string cacheDescription = string() + "the cache of " + moduleCacheSuffix + " modules at '" + moduleCachePath + "'";
2063  if (isModuleCacheInitialized && ! isModuleCacheableDataDirty)
2064  {
2065  VDebugLog("No need to recheck %s.", cacheDescription.c_str());
2066  return;
2067  }
2068 
2069  try
2070  {
2071  VDebugLog("Checking if %s is up-to-date…", cacheDescription.c_str());
2072 
2073  bool isCacheUpToDate = true;
2074 
2075  if (isModuleCacheInitialized && isModuleCacheableDataDirty)
2076  isCacheUpToDate = false;
2077 
2078  isModuleCacheInitialized = true;
2079  isModuleCacheableDataDirty = false;
2080 
2081  const string dylibFileName = "libVuoModuleCache-" + moduleCacheSuffix + ".dylib";
2082  const string indexFileName = "moduleCache-" + moduleCacheSuffix + ".txt";
2083  string dylibPath = moduleCachePath + "/" + dylibFileName;
2084  string indexPath = moduleCachePath + "/" + indexFileName;
2085 
2086  // Create the cache files if they don't already exist. (If they do exist, don't affect the last-modified times.)
2087 
2088  bool dylibFileExists = false;
2089  bool indexFileExists = false;
2090 
2091  bool dirExists = VuoFileUtilities::fileExists(moduleCachePath);
2092  if (dirExists)
2093  {
2094  dylibFileExists = VuoFileUtilities::fileExists(dylibPath);
2095  indexFileExists = VuoFileUtilities::fileExists(indexPath);
2096  }
2097 
2098  if (! (dirExists && dylibFileExists && indexFileExists))
2099  {
2100  if (shouldUseExistingCache)
2101  throw VuoException("Trying to use the existing cache, but the cache doesn't exist.", false);
2102  else
2103  {
2104  if (! dirExists)
2105  VuoFileUtilities::makeDir(moduleCachePath);
2106  if (! indexFileExists)
2107  VuoFileUtilities::createFile(indexPath);
2108  if (! dylibFileExists)
2109  VuoFileUtilities::createFile(dylibPath);
2110 
2111  isCacheUpToDate = false;
2112  }
2113  }
2114 
2115  // Lock the cache for reading.
2116 
2117  VuoFileUtilities::File *fileForLocking;
2118  {
2119  fileForLocking = moduleCacheFileForLocking[dylibPath];
2120  if (! fileForLocking)
2121  {
2122  fileForLocking = new VuoFileUtilities::File(moduleCachePath, dylibFileName);
2123  moduleCacheFileForLocking[dylibPath] = fileForLocking;
2124  }
2125 
2126  if (!fileForLocking->lockForReading())
2127  VDebugLog("\tWarning: Couldn't lock for reading.");
2128  }
2129 
2130  // Check if the dylib looks remotely valid.
2131 
2132  if (isCacheUpToDate)
2133  {
2134  bool dylibHasData = VuoFileUtilities::fileContainsReadableData(dylibPath);
2135  if (! dylibHasData)
2136  {
2137  if (shouldUseExistingCache)
2138  throw VuoException("Trying to use the existing cache, but the cache doesn't contain readable data.", false);
2139  else
2140  isCacheUpToDate = false;
2141  }
2142  }
2143 
2144  // List the items actually in the cache, according to its index.
2145 
2146  const char separator = '\n';
2147  if (isCacheUpToDate || shouldUseExistingCache)
2148  {
2149  VuoFileUtilities::File indexFile(moduleCachePath, indexFileName);
2150  string index = indexFile.getContentsAsString();
2151  vector<string> actualIndex = VuoStringUtilities::split(index, separator);
2152 
2153  moduleCacheContents.clear();
2154  moduleCacheContents.insert(actualIndex.begin(), actualIndex.end());
2155 
2156  if (shouldUseExistingCache)
2157  {
2158  isModuleCacheAvailable = true;
2159  return;
2160  }
2161  }
2162 
2163  // Check if the list of actual items matches the list of expected items.
2164 
2165  if (isCacheUpToDate)
2166  {
2167  if (moduleCacheContents.size() != cacheableModulesAndDependencies.size())
2168  isCacheUpToDate = false;
2169  else
2170  {
2171  set<string> contentsInBoth;
2172  std::set_intersection(moduleCacheContents.begin(), moduleCacheContents.end(),
2173  cacheableModulesAndDependencies.begin(), cacheableModulesAndDependencies.end(),
2174  std::inserter(contentsInBoth, contentsInBoth.begin()));
2175 
2176  if (contentsInBoth.size() != cacheableModulesAndDependencies.size())
2177  isCacheUpToDate = false;
2178  }
2179  }
2180 
2181  // Check if the cache is newer than all of the modules in it.
2182 
2183  if (isCacheUpToDate)
2184  {
2185  unsigned long cacheLastModified = VuoFileUtilities::getFileLastModifiedInSeconds(dylibPath);
2186 
2187  for (map<string, map<string, ModuleInfo *> >::iterator i = moduleFilesAtSearchPath.begin(); i != moduleFilesAtSearchPath.end(); ++i)
2188  {
2189  for (map<string, ModuleInfo *>::iterator j = i->second.begin(); j != i->second.end(); ++j)
2190  {
2191  if (! j->second->isOlderThan(cacheLastModified))
2192  {
2193  isCacheUpToDate = false;
2194  break;
2195  }
2196  }
2197  }
2198  }
2199 
2200  // If the cache is consistent with this environment, we're done.
2201 
2202  if (isCacheUpToDate)
2203  {
2204  VDebugLog("Up-to-date.");
2205 
2206  isModuleCacheAvailable = true;
2207  return;
2208  }
2209 
2210  // Otherwise, (re)build the cache.
2211 
2212  dispatch_async(moduleCacheBuildingQueue, ^{
2213  VDebugLog("Rebuilding %s…", cacheDescription.c_str());
2214 
2215  set<Module *> modulesToLink;
2216  set<string> librariesToLink;
2217  set<string> frameworksToLink;
2218  {
2219  compiler->getLinkerInputs(cacheableModulesAndDependencies, Optimization_SmallBinary, modulesToLink, librariesToLink, frameworksToLink);
2220 
2221  librariesToLink.insert(dylibsNeededToLinkToCaches.begin(), dylibsNeededToLinkToCaches.end());
2222  set<string>::iterator iter = librariesToLink.find(dylibPath); // getCacheableModulesAndDependencies includes dylibPath, but don't want to link against self
2223  if (iter != librariesToLink.end())
2224  librariesToLink.erase(iter);
2225 
2226  frameworksToLink.insert(frameworksNeededToLinkToCaches.begin(), frameworksNeededToLinkToCaches.end());
2227  }
2228 
2229  bool gotLockForWriting = false;
2230  try
2231  {
2232  // Try to upgrade the file lock for writing.
2233  gotLockForWriting = fileForLocking->lockForWriting(true);
2234  if (! gotLockForWriting)
2235  throw VuoException("The cache file is being used by another process. "
2236  "If any composition windows are open from previous Vuo sessions, quit them. "
2237  "If any processes whose names start with \"VuoComposition\" or one of your composition file names appear in Activity Monitor, force-quit them.",
2238  false);
2239 
2240  // Link the dependencies to create a temporary file.
2241  string dir, file, ext;
2242  VuoFileUtilities::splitPath(dylibFileName, dir, file, ext);
2243  string tmpPath = VuoFileUtilities::makeTmpFile(file, "dylib");
2244  compiler->link(tmpPath, modulesToLink, librariesToLink, frameworksToLink, true);
2245 
2246  // Copy the contents of the temporary file into the cached resources dylib.
2247  // This preserves the lock on the cached resources dylib file (https://b33p.net/kosada/node/12970).
2248  VuoFileUtilities::copyFile(tmpPath, dylibPath, true);
2250 
2252  getVuoFrameworkPath() + "/Helpers/install_name_tool",
2253  "-id",
2254  dylibPath,
2255  dylibPath,
2256  });
2257 
2258  // Write the list of dependencies to the index file.
2259  vector<string> expectedContents(cacheableModulesAndDependencies.begin(), cacheableModulesAndDependencies.end());
2260  string index = VuoStringUtilities::join(expectedContents, separator);
2261  VuoFileUtilities::writeStringToFile(index, indexPath);
2262 
2263  // Downgrade the file lock back to reading.
2264  if (!fileForLocking->lockForReading())
2265  VDebugLog("\tWarning: Couldn't downgrade the lock back to reading.");
2266 
2267  dispatch_sync(environmentQueue, ^{
2268  moduleCacheContents = cacheableModulesAndDependencies;
2269  isModuleCacheAvailable = true;
2270  });
2271  }
2272  catch (VuoException &e)
2273  {
2274  // Downgrade the file lock back to reading.
2275  if (gotLockForWriting)
2276  if (!fileForLocking->lockForReading())
2277  VDebugLog("\tWarning: Couldn't downgrade the lock back to reading.");
2278 
2279  VUserLog("Warning: Couldn't rebuild %s for the \"faster build\" optimization: %s", cacheDescription.c_str(), e.what());
2280  }
2281 
2282  VDebugLog("Done.");
2283  });
2284  }
2285  catch (VuoException &e)
2286  {
2287  VUserLog("Warning: Couldn't use %s for the \"faster build\" optimization: %s", cacheDescription.c_str(), e.what());
2288  }
2289 }
2290 
2294 void VuoCompiler::Environment::waitForModuleCachesToBuild(void)
2295 {
2296  dispatch_sync(moduleCacheBuildingQueue, ^{});
2297 }
2298 
2308 bool VuoCompiler::Environment::findInModuleCache(const string &moduleOrDependency, string &cachePath)
2309 {
2310  if (isModuleCacheAvailable && moduleCacheContents.find(moduleOrDependency) != moduleCacheContents.end())
2311  {
2312  cachePath = moduleCachePath + "/libVuoModuleCache-" + moduleCacheSuffix + ".dylib";
2313  return true;
2314  }
2315 
2316  return false;
2317 }
2318 
2325 void VuoCompiler::Environment::modulesChanged(void)
2326 {
2327  isModuleCacheableDataDirty = true;
2328  isModuleCacheAvailable = false;
2329 }
2330 
2334 bool VuoCompiler::Environment::isBuiltIn()
2335 {
2336  return this == sharedEnvironments[0][0];
2337 }
2338 
2342 string VuoCompiler::Environment::getName()
2343 {
2344  if (isBuiltIn())
2345  return "builtin";
2346  else if (this == sharedEnvironments[0][1])
2347  return "builtin (generated)";
2348  else if (this == sharedEnvironments[1][0])
2349  return "system";
2350  else if (this == sharedEnvironments[1][1])
2351  return "system (generated)";
2352  else if (this == sharedEnvironments[2][0])
2353  return "user";
2354  else if (this == sharedEnvironments[2][1])
2355  return "user (generated)";
2356  return "composition-local";
2357 }
2358 
2362 void VuoCompiler::applyToInstalledEnvironments(void (^doForEnvironment)(Environment *))
2363 {
2364  dispatch_sync(environmentQueue, ^{
2365  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i) {
2366  doForEnvironment((*i)[0]);
2367  }
2368  });
2369 }
2370 
2374 void VuoCompiler::applyToInstalledEnvironments(void (^doForEnvironment)(Environment *, bool *, string), bool *result, string arg)
2375 {
2376  dispatch_sync(environmentQueue, ^{
2377  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i) {
2378  doForEnvironment((*i)[0], result, arg);
2379  }
2380  });
2381 }
2382 
2386 void VuoCompiler::applyToAllEnvironments(void (^doForEnvironment)(Environment *environment))
2387 {
2388  dispatch_sync(environmentQueue, ^{
2389  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i) {
2390  for (vector<Environment *>::iterator j = i->begin(); j != i->end(); ++j) {
2391  doForEnvironment(*j);
2392  }
2393  }
2394  });
2395 }
2396 
2405 VuoCompiler::VuoCompiler(const string &compositionPath)
2406 {
2407 #if VUO_PRO
2408  init_Pro();
2409 #endif
2410 
2411  shouldLoadAllModules = true;
2412  hasLoadedAllModules = false;
2413  modulesToLoadQueue = dispatch_queue_create("org.vuo.compiler.modules", NULL);
2414  moduleCacheBuilding = dispatch_group_create();
2415  dependencyGraph = NULL;
2416  compositionDependencyGraph = NULL;
2417  isVerbose = false;
2418  _shouldShowSplashWindow = false;
2419  delegate = NULL;
2420  delegateQueue = dispatch_queue_create("org.vuo.compiler.delegate", NULL);
2421 
2422  string vuoFrameworkPath = getVuoFrameworkPath();
2423  if (! vuoFrameworkPath.empty())
2424  clangPath = vuoFrameworkPath + "/Helpers/clang";
2425  else
2426  clangPath = llvm::sys::Path(StringRef(LLVM_ROOT "/bin/clang"));
2427 
2428  dispatch_sync(environmentQueue, ^{
2429  if (sharedEnvironments.empty())
2430  {
2431  sharedEnvironments = vector< vector<Environment *> >(3, vector<Environment *>(2, NULL));
2432  for (vector< vector<Environment *> >::iterator i = sharedEnvironments.begin(); i != sharedEnvironments.end(); ++i) {
2433  for (vector<Environment *>::iterator j = i->begin(); j != i->end(); ++j) {
2434  *j = new Environment();
2435  }
2436  }
2437 
2438  vector<string> builtInModuleSearchPaths = Environment::getBuiltInModuleSearchPaths();
2439  for (vector<string>::iterator i = builtInModuleSearchPaths.begin(); i != builtInModuleSearchPaths.end(); ++i) {
2440  sharedEnvironments[0][0]->addModuleSearchPath(*i, false);
2441  }
2442 
2443  vector<string> builtInHeaderSearchPaths = Environment::getBuiltInHeaderSearchPaths();
2444  for (vector<string>::iterator i = builtInHeaderSearchPaths.begin(); i != builtInHeaderSearchPaths.end(); ++i) {
2445  sharedEnvironments[0][0]->addHeaderSearchPath(*i);
2446  }
2447 
2448  vector<string> builtInLibrarySearchPaths = Environment::getBuiltInLibrarySearchPaths();
2449  for (vector<string>::iterator i = builtInLibrarySearchPaths.begin(); i != builtInLibrarySearchPaths.end(); ++i) {
2450  sharedEnvironments[0][0]->addLibrarySearchPath(*i);
2451  }
2452 
2453  vector<string> builtInFrameworkSearchPaths = Environment::getBuiltInFrameworkSearchPaths();
2454  for (vector<string>::iterator i = builtInFrameworkSearchPaths.begin(); i != builtInFrameworkSearchPaths.end(); ++i) {
2455  sharedEnvironments[0][0]->addFrameworkSearchPath(*i);
2456  }
2457 
2458  // Allow system administrator to override Vuo.framework modules
2459  sharedEnvironments[1][0]->addModuleSearchPath(VuoFileUtilities::getSystemModulesPath());
2460  sharedEnvironments[1][0]->addLibrarySearchPath(VuoFileUtilities::getSystemModulesPath());
2461 
2462  // Allow user to override Vuo.framework and system-wide modules
2463  sharedEnvironments[2][0]->addModuleSearchPath(VuoFileUtilities::getUserModulesPath());
2464  sharedEnvironments[2][0]->addLibrarySearchPath(VuoFileUtilities::getUserModulesPath());
2465 
2466  // Set up module cache paths.
2467  // Since the built-in module caches are part of Vuo.framework (put there by `generateBuiltInModuleCaches`),
2468  // only attempt to use module caches if Vuo.framework exists.
2469  string vuoFrameworkPath = getVuoFrameworkPath();
2470  if (! vuoFrameworkPath.empty())
2471  {
2472  vector<string> moduleCachePaths(3);
2473  moduleCachePaths[0] = vuoFrameworkPath + "/Modules/Builtin";
2474  moduleCachePaths[1] = VuoFileUtilities::getCachePath() + "/System";
2475  moduleCachePaths[2] = VuoFileUtilities::getCachePath() + "/User";
2476 
2477  for (size_t i = 0; i < moduleCachePaths.size(); ++i)
2478  {
2479  string moduleCachePath = moduleCachePaths[i];
2480 
2481  sharedEnvironments[i][0]->setModuleCachePath(moduleCachePath);
2482  sharedEnvironments[i][0]->addModuleSearchPath(moduleCachePath + "/Modules", false);
2483 
2484  sharedEnvironments[i][1]->setModuleCachePath(moduleCachePath);
2485  }
2486  }
2487  }
2488  });
2489 
2490  setCompositionPath(compositionPath);
2491 }
2492 
2497 {
2498 #if VUO_PRO
2499  fini_Pro();
2500 #endif
2501 
2502  dispatch_group_wait(moduleCacheBuilding, DISPATCH_TIME_FOREVER);
2503  dispatch_release(moduleCacheBuilding);
2504 
2505  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
2506  (*i)[0]->removeCompilerToNotify(this);
2507 
2508  dispatch_sync(delegateQueue, ^{});
2509 
2510  dispatch_release(modulesToLoadQueue);
2511  dispatch_release(delegateQueue);
2512 
2513  delete dependencyGraph;
2514  delete compositionDependencyGraph;
2515 }
2516 
2520 void VuoCompiler::reset(void)
2521 {
2522  dispatch_group_wait(moduleSourceCompilersExist, DISPATCH_TIME_FOREVER);
2523 
2524  for (vector< vector<Environment *> >::iterator i = sharedEnvironments.begin(); i != sharedEnvironments.end(); ++i)
2525  {
2526  (*i)[0]->stopWatchingModuleSearchPaths();
2527  dispatch_sync((*i)[0]->moduleSearchPathContentsChangedQueue, ^{});
2528  }
2529 
2530  for (map< string, vector<Environment *> >::iterator i = environmentsForCompositionFamily.begin(); i != environmentsForCompositionFamily.end(); ++i)
2531  {
2532  (i->second)[0]->stopWatchingModuleSearchPaths();
2533  dispatch_sync((i->second)[0]->moduleSearchPathContentsChangedQueue, ^{});
2534  }
2535 
2536  for (vector< vector<Environment *> >::iterator i = sharedEnvironments.begin(); i != sharedEnvironments.end(); ++i)
2537  for (vector<Environment *>::iterator j = i->begin(); j != i->end(); ++j)
2538  delete *j;
2539 
2540  for (map< string, vector<Environment *> >::iterator i = environmentsForCompositionFamily.begin(); i != environmentsForCompositionFamily.end(); ++i)
2541  for (vector<Environment *>::iterator j = i->second.begin(); j != i->second.end(); ++j)
2542  delete *j;
2543 
2544  sharedEnvironments.clear();
2545  environmentsForCompositionFamily.clear();
2546 }
2547 
2554 {
2555  dispatch_async(delegateQueue, ^{
2556  this->delegate = delegate;
2557  });
2558 }
2559 
2569 void VuoCompiler::setCompositionPath(const string &compositionPath)
2570 {
2571  string compositionModulesDir;
2572  string compositionBaseDir;
2573  lastCompositionIsSubcomposition = false;
2574  if (! compositionPath.empty())
2575  {
2576  compositionModulesDir = VuoFileUtilities::getCompositionLocalModulesPath(compositionPath);
2577 
2578  string file, ext;
2579  VuoFileUtilities::splitPath(compositionModulesDir, compositionBaseDir, file, ext);
2580  VuoFileUtilities::canonicalizePath(compositionBaseDir);
2581 
2582  string compositionDir;
2583  VuoFileUtilities::splitPath(compositionPath, compositionDir, file, ext);
2584  VuoFileUtilities::canonicalizePath(compositionDir);
2585  lastCompositionIsSubcomposition = (compositionDir == compositionModulesDir);
2586  }
2587 
2588  // Set up `environments` to contain all environments available to this compiler, in order from broadest to narrowest.
2589 
2590  dispatch_sync(environmentQueue, ^{
2591  if (! environments.empty() && compositionBaseDir == lastCompositionBaseDir) {
2592  return;
2593  }
2594  lastCompositionBaseDir = compositionBaseDir;
2595 
2596  // Clear out the existing environments for this compiler.
2597 
2598  Environment *oldCompositionFamilyInstalledEnvironment = nullptr;
2599  vector<Environment *> compositionEnvironments;
2600  if (! environments.empty())
2601  {
2602  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i) {
2603  (*i)[0]->removeCompilerToNotify(this);
2604  }
2605 
2606  if (environments.size() >= 5) {
2607  oldCompositionFamilyInstalledEnvironment = environments[3][0];
2608  }
2609 
2610  compositionEnvironments = environments.back();
2611 
2612  environments.clear();
2613  }
2614 
2615  // If the composition is located in one of the shared environments (built-in, system, user),
2616  // add that shared environment and any shared environments at broader scope to the compiler.
2617  // If the composition is not in a shared environment, add all of the shared environments to the compiler.
2618 
2619  bool isCompositionInSharedEnvironment = false;
2620  for (vector< vector<Environment *> >::iterator i = sharedEnvironments.begin(); i != sharedEnvironments.end(); ++i)
2621  {
2622  environments.push_back(*i);
2623 
2624  vector<string> moduleSearchPaths = (*i)[0]->getModuleSearchPaths();
2625  for (vector<string>::iterator j = moduleSearchPaths.begin(); j != moduleSearchPaths.end(); ++j)
2626  {
2627  string moduleSearchPath = *j;
2628  VuoFileUtilities::canonicalizePath(moduleSearchPath);
2629  if (moduleSearchPath == compositionModulesDir)
2630  {
2631  isCompositionInSharedEnvironment = true;
2632  break;
2633  }
2634  }
2635 
2636  if (isCompositionInSharedEnvironment) {
2637  break;
2638  }
2639  }
2640 
2641  // If the composition is not in a shared environment, add the composition-family environment to the compiler.
2642 
2643  Environment *newCompositionFamilyInstalledEnvironment = nullptr;
2644  if (! isCompositionInSharedEnvironment && ! compositionPath.empty())
2645  {
2646  vector<Environment *> compositionFamilyEnvironments = environmentsForCompositionFamily[compositionBaseDir];
2647  if (compositionFamilyEnvironments.empty())
2648  {
2649  compositionFamilyEnvironments = vector<Environment *>(2, NULL);
2650  compositionFamilyEnvironments[0] = new Environment();
2651  compositionFamilyEnvironments[1] = new Environment();
2652  environmentsForCompositionFamily[compositionBaseDir] = compositionFamilyEnvironments;
2653 
2654  // Allow the user to place modules/subcompositions in a Modules folder inside the composition folder.
2655 
2656  compositionFamilyEnvironments[0]->addModuleSearchPath(compositionModulesDir);
2657 
2658  // Locate this environment's cache in a folder whose name is the composition folder path with
2659  // slashes replaced with Unicode Modifier Letter Colon.
2660  string moduleCachePath = getCachePathForComposition(compositionBaseDir);
2661 
2662  compositionFamilyEnvironments[0]->setModuleCachePath(moduleCachePath);
2663  compositionFamilyEnvironments[0]->addModuleSearchPath(moduleCachePath + "/Modules", false);
2664 
2665  compositionFamilyEnvironments[1]->setModuleCachePath(moduleCachePath);
2666  }
2667  environments.push_back(compositionFamilyEnvironments);
2668 
2669  newCompositionFamilyInstalledEnvironment = compositionFamilyEnvironments[0];
2670  }
2671 
2672  // Add the composition environment to the compiler (or add it back in if it already existed).
2673 
2674  if (compositionEnvironments.empty())
2675  {
2676  compositionEnvironments = vector<Environment *>(2, NULL);
2677  compositionEnvironments[0] = new Environment();
2678  compositionEnvironments[1] = new Environment();
2679  }
2680  environments.push_back(compositionEnvironments);
2681 
2682  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i) {
2683  (*i)[0]->addCompilerToNotify(this);
2684  }
2685 
2686  delete dependencyGraph;
2687  delete compositionDependencyGraph;
2688  dependencyGraph = makeDependencyNetwork(environments, ^VuoDirectedAcyclicGraph * (Environment *env) { return env->getDependencyGraph(); });
2689  compositionDependencyGraph = makeDependencyNetwork(environments, ^VuoDirectedAcyclicGraph * (Environment *env) { return env->getCompositionDependencyGraph(); });
2690 
2691  // If the compiler has a different local Modules directory than before, notify the compiler's delegate
2692  // of composition-family modules that are newly available/unavailable.
2693 
2694  if (oldCompositionFamilyInstalledEnvironment != newCompositionFamilyInstalledEnvironment)
2695  {
2696  auto getModules = [] (Environment *env)
2697  {
2698  map<string, VuoCompilerModule *> modules;
2699  if (env)
2700  {
2701  for (auto i : env->getNodeClasses()) {
2702  modules.insert(i);
2703  }
2704  for (auto i : env->getTypes()) {
2705  modules.insert(i);
2706  }
2707  for (auto i : env->getLibraryModules()) {
2708  modules.insert(i);
2709  }
2710  }
2711  return modules;
2712  };
2713 
2714  map<string, VuoCompilerModule *> modulesAdded = getModules(newCompositionFamilyInstalledEnvironment);
2715  map<string, VuoCompilerModule *> modulesRemoved = getModules(oldCompositionFamilyInstalledEnvironment);
2716 
2717  map<string, pair<VuoCompilerModule *, VuoCompilerModule *> > modulesModified;
2718  for (map<string, VuoCompilerModule *>::iterator add = modulesAdded.begin(); add != modulesAdded.end(); )
2719  {
2720  map<string, VuoCompilerModule *>::iterator rem = modulesRemoved.find(add->first);
2721  if (rem != modulesRemoved.end())
2722  {
2723  modulesModified[add->first] = make_pair(rem->second, add->second);
2724  modulesAdded.erase(add++);
2725  modulesRemoved.erase(rem);
2726  }
2727  else
2728  {
2729  ++add;
2730  }
2731  }
2732 
2733  if (! (modulesAdded.empty() && modulesModified.empty() && modulesRemoved.empty()) )
2734  {
2735  VuoCompilerIssues *issues = new VuoCompilerIssues();
2736  VuoCompilerDelegate::LoadedModulesData *delegateData = new VuoCompilerDelegate::LoadedModulesData(set< pair<VuoCompilerModule *, VuoCompilerModule *> >(), set<VuoCompilerModule *>(), issues);
2737  delegateData->retain();
2738 
2739  Environment *scopeEnvironment = newCompositionFamilyInstalledEnvironment;
2740  if (! scopeEnvironment) {
2741  scopeEnvironment = compositionEnvironments.at(0);
2742  }
2743 
2744  loadedModules(modulesAdded, modulesModified, modulesRemoved, issues, delegateData, scopeEnvironment);
2745  }
2746  }
2747  });
2748 }
2749 
2750 // environmentQueue
2751 VuoDirectedAcyclicNetwork * VuoCompiler::makeDependencyNetwork(const vector< vector<Environment *> > &environments,
2752  VuoDirectedAcyclicGraph * (^graphForEnvironment)(Environment *))
2753 {
2754  if (!graphForEnvironment)
2755  return NULL;
2756 
2758 
2759  for (vector< vector<Environment *> >::const_iterator i = environments.begin(); i != environments.end(); ++i)
2760  {
2761  // Installed environment depends on generated environment in same scope.
2762 
2763  network->addEdge(graphForEnvironment(i->at(0)), graphForEnvironment(i->at(1)));
2764 
2765  // Installed and generated environments depend on installed environments in broader scopes.
2766 
2767  for (vector< vector<Environment *> >::const_iterator ii = environments.begin(); ii != i; ++ii)
2768  {
2769  network->addEdge(graphForEnvironment(i->at(0)), graphForEnvironment(ii->at(0)));
2770  network->addEdge(graphForEnvironment(i->at(1)), graphForEnvironment(ii->at(0)));
2771  }
2772  }
2773 
2774  return network;
2775 }
2776 
2797 void VuoCompiler::loadModulesIfNeeded(const set<string> &moduleKeys)
2798 {
2799  __block bool willLoadAllModules = false;
2800  if (moduleKeys.empty())
2801  {
2802  dispatch_sync(modulesToLoadQueue, ^{
2803  if (shouldLoadAllModules && ! hasLoadedAllModules) {
2804  willLoadAllModules = true;
2805  hasLoadedAllModules = true;
2806  }
2807  });
2808  }
2809 
2810  if (! willLoadAllModules && moduleKeys.empty())
2811  return;
2812 
2813  // Load modules and start sources compiling.
2814 
2815  __block set<dispatch_group_t> sourcesLoading;
2816  dispatch_sync(environmentQueue, ^{
2817  sourcesLoading = loadModulesAndSources(moduleKeys, set<string>(), set<string>(),
2818  moduleKeys, set<string>(), set<string>(),
2819  willLoadAllModules, false, nullptr, nullptr, nullptr, "");
2820  });
2821 
2822  // Wait for sources to finish compiling and their modules to be loaded,
2823  // to ensure that `getNodeClass(subcomposition)` finds the subcomposition node class.
2824 
2825  for (set<dispatch_group_t>::iterator i = sourcesLoading.begin(); i != sourcesLoading.end(); ++i)
2826  {
2827  dispatch_group_wait(*i, DISPATCH_TIME_FOREVER);
2828  dispatch_release(*i);
2829  }
2830 }
2831 
2840 set<dispatch_group_t> VuoCompiler::loadModulesAndSources(const set<string> &modulesAddedKeys, const set<string> &modulesModifiedKeys, const set<string> &modulesRemovedKeys,
2841  const set<string> &sourcesAddedKeys, const set<string> &sourcesModifiedKeys, const set<string> &sourcesRemovedKeys,
2842  bool willLoadAllModules, bool shouldRecompileSourcesIfUnchanged,
2843  Environment *currentEnvironment, VuoCompilerIssues *issuesForCurrentEnvironment,
2844  std::function<void(void)> moduleLoadedCallback, const string &moduleAddedOrModifiedSourceCode)
2845 {
2846  //VLog("C=%p E=%p -- %lu %lu %lu %lu %lu %lu %i %p %p", this, currentEnvironment,
2847  //modulesAddedKeys.size(), modulesModifiedKeys.size(), modulesRemovedKeys.size(),
2848  //sourcesAddedKeys.size(), sourcesModifiedKeys.size(), sourcesRemovedKeys.size(),
2849  //willLoadAllModules, issuesForCurrentEnvironment, moduleLoadedCallback);
2850  //if (modulesAddedKeys.size() == 1) VLog(" %s", modulesAddedKeys.begin()->c_str());
2851  //if (modulesModifiedKeys.size() == 1) VLog(" %s", modulesModifiedKeys.begin()->c_str());
2852  //if (modulesRemovedKeys.size() == 1) VLog(" %s", modulesRemovedKeys.begin()->c_str());
2853 
2854  // Organize the modules, source files, and issues by environment.
2855 
2856  map<Environment *, set<string> > modulesAdded;
2857  map<Environment *, set<string> > modulesModified;
2858  map<Environment *, set<string> > modulesRemoved;
2859  map<Environment *, set<string> > sourcesAdded;
2860  map<Environment *, set<string> > sourcesModified;
2861  map<Environment *, set<string> > sourcesRemoved;
2862  map<Environment *, set<string> > potentialSpecializedModules;
2863 
2864  if (currentEnvironment)
2865  {
2866  modulesAdded[currentEnvironment] = modulesAddedKeys;
2867  modulesModified[currentEnvironment] = modulesModifiedKeys;
2868  modulesRemoved[currentEnvironment] = modulesRemovedKeys;
2869  sourcesAdded[currentEnvironment] = sourcesAddedKeys;
2870  sourcesModified[currentEnvironment] = sourcesModifiedKeys;
2871  sourcesRemoved[currentEnvironment] = sourcesRemovedKeys;
2872  }
2873  else
2874  {
2875  Environment *genEnv = nullptr;
2876  if (! willLoadAllModules)
2877  {
2878  // If compiling a top-level composition, generated modules that were directly requested (in modulesAddedKeys) are
2879  // added at the composition scope so they won't be cached. This prevents link errors when running multiple
2880  // compositions in the current process (https://b33p.net/kosada/node/14317).
2881 
2882  int scope = environments.size() - (lastCompositionIsSubcomposition ? 2 : 1);
2883  genEnv = environments.at(scope).at(1);
2884  potentialSpecializedModules[genEnv] = modulesAddedKeys;
2885  }
2886 
2887  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
2888  {
2889  Environment *env = (*i).at(0);
2890 
2891  ModuleInfoIterator modulesAddedIter = (willLoadAllModules ? env->listAllModules() : env->listModules(modulesAddedKeys));
2892  ModuleInfoIterator sourcesAddedIter = (willLoadAllModules ? env->listAllSourceFiles() : env->listSourceFiles(sourcesAddedKeys));
2893  ModuleInfoIterator sourcesModifiedIter = (willLoadAllModules ? env->listAllSourceFiles() : env->listSourceFiles(sourcesModifiedKeys));
2894 
2895  ModuleInfo *moduleInfo;
2896  while ((moduleInfo = modulesAddedIter.next()))
2897  {
2898  string moduleKey = moduleInfo->getModuleKey();
2899 
2900  modulesAdded[env].insert(moduleKey);
2901 
2902  if (! willLoadAllModules)
2903  {
2904  auto foundIter = potentialSpecializedModules[genEnv].find(moduleKey);
2905  if (foundIter != potentialSpecializedModules[genEnv].end())
2906  potentialSpecializedModules[genEnv].erase(foundIter);
2907  }
2908  }
2909 
2910  // If a source file and a compiled file for the same module are in the same folder,
2911  // the compiled file takes precedence.
2912  auto isCompiledModuleAtSameSearchPath = [&env] (ModuleInfo *sourceInfo)
2913  {
2914  ModuleInfo *compiledModuleInfo = env->listModule(sourceInfo->getModuleKey());
2915  return (compiledModuleInfo && compiledModuleInfo->getSearchPath() == sourceInfo->getSearchPath());
2916  };
2917 
2918  while ((moduleInfo = sourcesAddedIter.next()))
2919  {
2920  if (isCompiledModuleAtSameSearchPath(moduleInfo))
2921  continue;
2922 
2923  sourcesAdded[env].insert( moduleInfo->getModuleKey() );
2924  }
2925 
2926  while ((moduleInfo = sourcesModifiedIter.next()))
2927  {
2928  if (isCompiledModuleAtSameSearchPath(moduleInfo))
2929  continue;
2930 
2931  sourcesModified[env].insert( moduleInfo->getModuleKey() );
2932  }
2933  }
2934  }
2935 
2936  map<Environment *, VuoCompilerIssues *> issues;
2937  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
2938  {
2939  Environment *env = (*i).at(0);
2940  issues[env] = (env == currentEnvironment && issuesForCurrentEnvironment ? issuesForCurrentEnvironment : new VuoCompilerIssues());
2941  }
2942 
2943  // Check for circular dependencies in sources.
2944 
2945  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
2946  {
2947  Environment *env = (*i).at(0);
2948 
2949  // Check for circular dependencies involving sources being loaded within this environment.
2950  // For circular dependencies involving sources in different environments,
2951  // an error will be reported elsewhere due to one of them being outside of the other's scope.
2952  set<VuoDirectedAcyclicGraph::Vertex *> circularDependencies = env->getCompositionDependencyGraph()->getCycleVertices();
2953 
2954  set<string> sourcesAddedModified;
2955  sourcesAddedModified.insert(sourcesAdded[env].begin(), sourcesAdded[env].end());
2956  sourcesAddedModified.insert(sourcesModified[env].begin(), sourcesModified[env].end());
2957 
2958  for (set<string>::iterator j = sourcesAddedModified.begin(); j != sourcesAddedModified.end(); ++j)
2959  {
2960  string moduleKey = *j;
2961 
2962  bool found = false;
2963  for (set<VuoDirectedAcyclicGraph::Vertex *>::iterator k = circularDependencies.begin(); k != circularDependencies.end(); ++k)
2964  {
2965  DependencyGraphVertex *vertex = static_cast<DependencyGraphVertex *>(*k);
2966  if (vertex->getDependency() == moduleKey)
2967  {
2968  found = true;
2969  break;
2970  }
2971  }
2972 
2973  if (found)
2974  {
2975  sourcesAdded[env].erase(moduleKey);
2976  sourcesModified[env].erase(moduleKey);
2977 
2978  VuoCompilerIssue issue(VuoCompilerIssue::Error, "compiling subcomposition", moduleKey,
2979  "Subcomposition contains itself",
2980  "%moduleKey contains an instance of itself, "
2981  "or contains another subcomposition that contains an instance of %moduleKey.");
2982  issue.setModuleKey(moduleKey);
2983 
2984  ModuleInfo *sourceInfo = env->listSourceFile(moduleKey);
2985  if (sourceInfo)
2986  issue.setFilePath(sourceInfo->getFile()->path());
2987 
2988  issues[env]->append(issue);
2989  }
2990  }
2991  }
2992 
2993  // Find all modules and sources that depend on the modules and sources being modified or removed.
2994  // Those that belong to one of this compiler's environments are used in subsequent stages.
2995  // Those that belong to some other environment are scheduled to be handled by a separate call to this function.
2996 
2997  map<Environment *, set<string> > modulesDepOnModulesModified;
2998  map<Environment *, set<string> > sourcesDepOnModulesModified;
2999  map<Environment *, set<string> > modulesDepOnModulesRemoved;
3000  map<Environment *, set<string> > sourcesDepOnModulesRemoved;
3001 
3002  {
3003  __block map<Environment *, set<string> > modulesDepOnModulesModified_otherCompiler;
3004  __block map<Environment *, set<string> > sourcesDepOnModulesModified_otherCompiler;
3005  __block map<Environment *, set<string> > modulesDepOnModulesRemoved_otherCompiler;
3006  __block map<Environment *, set<string> > sourcesDepOnModulesRemoved_otherCompiler;
3007 
3008  vector<VuoDirectedAcyclicNetwork *> searchDependencyGraphs;
3009  searchDependencyGraphs.push_back(dependencyGraph);
3010  for (map<string, vector<Environment *> >::iterator ii = environmentsForCompositionFamily.begin(); ii != environmentsForCompositionFamily.end(); ++ii)
3011  {
3012  vector< vector<Environment *> > otherEnvs = sharedEnvironments;
3013  otherEnvs.push_back(ii->second);
3014  VuoDirectedAcyclicNetwork *other = makeDependencyNetwork(otherEnvs, ^VuoDirectedAcyclicGraph * (Environment *env) { return env->getDependencyGraph(); });
3015  searchDependencyGraphs.push_back(other);
3016  }
3017 
3018  VuoDirectedAcyclicGraph *currentEnvironmentDependencyGraph = (currentEnvironment ? currentEnvironment->getDependencyGraph() : nullptr);
3019 
3020  findDependentModulesAndSources(modulesModified, searchDependencyGraphs, currentEnvironmentDependencyGraph,
3021  modulesDepOnModulesModified, modulesDepOnModulesModified_otherCompiler,
3022  sourcesDepOnModulesModified, sourcesDepOnModulesModified_otherCompiler);
3023 
3024  findDependentModulesAndSources(modulesRemoved, searchDependencyGraphs, currentEnvironmentDependencyGraph,
3025  modulesDepOnModulesRemoved, modulesDepOnModulesRemoved_otherCompiler,
3026  sourcesDepOnModulesRemoved, sourcesDepOnModulesRemoved_otherCompiler);
3027 
3028  set<Environment *> otherEnvironments;
3029  for (map<Environment *, set<string> >::iterator i = modulesDepOnModulesModified_otherCompiler.begin(); i != modulesDepOnModulesModified_otherCompiler.end(); ++i)
3030  otherEnvironments.insert(i->first);
3031  for (map<Environment *, set<string> >::iterator i = sourcesDepOnModulesModified_otherCompiler.begin(); i != sourcesDepOnModulesModified_otherCompiler.end(); ++i)
3032  otherEnvironments.insert(i->first);
3033  for (map<Environment *, set<string> >::iterator i = modulesDepOnModulesRemoved_otherCompiler.begin(); i != modulesDepOnModulesRemoved_otherCompiler.end(); ++i)
3034  otherEnvironments.insert(i->first);
3035  for (map<Environment *, set<string> >::iterator i = sourcesDepOnModulesRemoved_otherCompiler.begin(); i != sourcesDepOnModulesRemoved_otherCompiler.end(); ++i)
3036  otherEnvironments.insert(i->first);
3037 
3038  for (Environment *env : otherEnvironments)
3039  {
3040  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
3041  string moduleSearchPath = env->getModuleSearchPaths().front();
3042  VuoCompiler *otherCompiler = new VuoCompiler(moduleSearchPath + "/unused");
3043 
3044  dispatch_sync(environmentQueue, ^{
3045  otherCompiler->loadModulesAndSources(set<string>(), modulesDepOnModulesModified_otherCompiler[env], modulesDepOnModulesRemoved_otherCompiler[env],
3046  set<string>(), sourcesDepOnModulesModified_otherCompiler[env], sourcesDepOnModulesRemoved_otherCompiler[env],
3047  false, true, env, nullptr, nullptr, "");
3048  });
3049 
3050  delete otherCompiler;
3051  });
3052  }
3053  }
3054 
3055  // Unload:
3056  // - modules that have been removed or modified
3057  // - modules that depend on them
3058 
3059  map<Environment *, set<VuoCompilerModule *> > actualModulesRemoved;
3060  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3061  {
3062  Environment *env = (*i).at(0);
3063 
3064  set<string> modulesToUnload;
3065  modulesToUnload.insert(modulesRemoved[env].begin(), modulesRemoved[env].end());
3066  modulesToUnload.insert(modulesModified[env].begin(), modulesModified[env].end());
3067  modulesToUnload.insert(modulesDepOnModulesRemoved[env].begin(), modulesDepOnModulesRemoved[env].end());
3068  modulesToUnload.insert(modulesDepOnModulesModified[env].begin(), modulesDepOnModulesModified[env].end());
3069 
3070  actualModulesRemoved[env] = env->unloadCompiledModules(modulesToUnload);
3071 
3072  if (!env->isBuiltIn() && !actualModulesRemoved[env].empty())
3073  {
3074  set<string> actualModulesRemovedKeys;
3075  for (auto m : actualModulesRemoved[env])
3076  actualModulesRemovedKeys.insert(m->getPseudoBase()->getModuleKey());
3077  VUserLog("Removed from %s environment: %s", env->getName().c_str(), VuoStringUtilities::join(actualModulesRemovedKeys, ", ").c_str());
3078  }
3079  }
3080 
3081  // Load:
3082  // - modules that have been added or modified
3083  // - modules that they depend on
3084  // - modules that depend on them that were just unloaded
3085  // Delete:
3086  // - cached module files in `modulesToLoad` whose source files have been removed
3087 
3088  map<Environment *, set<string> > modulesToLoad;
3089  map<Environment *, map<string, string> > modulesToLoadSourceCode;
3090  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3091  {
3092  Environment *env = (*i).at(0);
3093 
3094  if (! modulesAdded[env].empty())
3095  modulesToLoad[env].insert(modulesAdded[env].begin(), modulesAdded[env].end());
3096  if (! modulesModified[env].empty())
3097  modulesToLoad[env].insert(modulesModified[env].begin(), modulesModified[env].end());
3098  if (! modulesDepOnModulesModified[env].empty())
3099  modulesToLoad[env].insert(modulesDepOnModulesModified[env].begin(), modulesDepOnModulesModified[env].end());
3100 
3101  if (env == currentEnvironment && moduleLoadedCallback)
3102  {
3103  if (modulesAdded[env].size() == 1)
3104  modulesToLoadSourceCode[env][*modulesAdded[env].begin()] = moduleAddedOrModifiedSourceCode;
3105  else if (modulesModified[env].size() == 1)
3106  modulesToLoadSourceCode[env][*modulesModified[env].begin()] = moduleAddedOrModifiedSourceCode;
3107  }
3108  }
3109 
3110  map<Environment *, set<VuoCompilerModule *> > actualModulesAdded;
3111  while (! modulesToLoad.empty())
3112  {
3113  set<string> dependenciesToLoad;
3114  map<Environment *, set<string> > potentialSpecializedDependencies;
3115  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3116  {
3117  Environment *env = (*i).at(0);
3118  Environment *genEnv = (*i).at(1);
3119 
3120  set<VuoCompilerModule *> actualModulesLoaded = env->loadCompiledModules(modulesToLoad[env], modulesToLoadSourceCode[env]);
3121 
3122  actualModulesAdded[env].insert(actualModulesLoaded.begin(), actualModulesLoaded.end());
3123  modulesToLoad.erase(env);
3124 
3125  set<string> actualModulesLoadedKeys;
3126  for (set<VuoCompilerModule *>::iterator j = actualModulesLoaded.begin(); j != actualModulesLoaded.end(); ++j)
3127  {
3128  set<string> dependencies = (*j)->getDependencies();
3129  dependenciesToLoad.insert(dependencies.begin(), dependencies.end());
3130  potentialSpecializedDependencies[genEnv].insert(dependencies.begin(), dependencies.end());
3131  actualModulesLoadedKeys.insert((*j)->getPseudoBase()->getModuleKey());
3132  }
3133 
3134  if (!env->isBuiltIn() && !actualModulesLoadedKeys.empty())
3135  VUserLog("Loaded into %s environment: %s", env->getName().c_str(), VuoStringUtilities::join(actualModulesLoadedKeys, ", ").c_str());
3136  }
3137 
3138  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3139  {
3140  Environment *env = (*i).at(0);
3141 
3142  ModuleInfoIterator dependenciesInEnv = env->listModules(dependenciesToLoad);
3143  ModuleInfo *moduleInfo;
3144  while ((moduleInfo = dependenciesInEnv.next()))
3145  {
3146  modulesToLoad[env].insert( moduleInfo->getModuleKey() );
3147 
3148  for (map<Environment *, set<string> >::iterator j = potentialSpecializedDependencies.begin(); j != potentialSpecializedDependencies.end(); ++j)
3149  {
3150  auto foundIter = j->second.find( moduleInfo->getModuleKey() );
3151  if (foundIter != j->second.end())
3152  j->second.erase(foundIter);
3153  }
3154  }
3155  }
3156 
3157  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3158  {
3159  Environment *genEnv = (*i).at(1);
3160  potentialSpecializedModules[genEnv].insert(potentialSpecializedDependencies[genEnv].begin(), potentialSpecializedDependencies[genEnv].end());
3161  }
3162  }
3163 
3164  // Load asynchronously:
3165  // - specializations of generic modules
3166 
3167  set<dispatch_group_t> specializedModulesLoading;
3168  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3169  {
3170  Environment *genEnv = (*i).at(1);
3171  set<dispatch_group_t> s = genEnv->loadSpecializedModules(potentialSpecializedModules[genEnv], this, llvmQueue);
3172  specializedModulesLoading.insert(s.begin(), s.end());
3173  }
3174 
3175  // Notify those waiting on a source file to be compiled that its module has now been loaded.
3176 
3177  if (moduleLoadedCallback)
3178  moduleLoadedCallback();
3179 
3180  // Move modified modules from `actualModulesAdded` and `actualModulesRemoved` to `actualModulesModified`.
3181 
3182  map<Environment *, set< pair<VuoCompilerModule *, VuoCompilerModule *> > > actualModulesModified;
3183  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3184  {
3185  Environment *env = (*i).at(0);
3186 
3187  for (set<VuoCompilerModule *>::iterator add = actualModulesAdded[env].begin(); add != actualModulesAdded[env].end(); )
3188  {
3189  set<VuoCompilerModule *>::iterator rem;
3190  for (rem = actualModulesRemoved[env].begin(); rem != actualModulesRemoved[env].end(); ++rem)
3191  if ((*rem)->getPseudoBase()->getModuleKey() == (*add)->getPseudoBase()->getModuleKey())
3192  break;
3193 
3194  if (rem != actualModulesRemoved[env].end())
3195  {
3196  actualModulesModified[env].insert( make_pair(*rem, *add) );
3197  actualModulesRemoved[env].erase(rem);
3198  actualModulesAdded[env].erase(add++);
3199  }
3200  else
3201  ++add;
3202  }
3203  }
3204 
3205  // Reify port types on node classes (if needed).
3206 
3207  bool wereModulesAddedOrModified = false;
3208  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3209  {
3210  Environment *env = (*i).at(0);
3211  if (! (actualModulesAdded[env].empty() && actualModulesModified[env].empty()) )
3212  {
3213  wereModulesAddedOrModified = true;
3214  break;
3215  }
3216  }
3217 
3218  if (wereModulesAddedOrModified)
3219  {
3220  map<string, VuoCompilerType *> inheritedTypes;
3221  for (const vector<Environment *> &envs : environments)
3222  {
3223  for (Environment *env : envs)
3224  {
3225  env->reifyPortTypes(inheritedTypes);
3226  map<string, VuoCompilerType *> envTypes = env->getTypes();
3227  inheritedTypes.insert(envTypes.begin(), envTypes.end());
3228  }
3229  }
3230  }
3231 
3232  // Delete cached compiled module files and call this function recursively for:
3233  // - modules whose source files have been removed
3234  // - modules whose source files depend on them
3235 
3236  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3237  {
3238  Environment *env = (*i).at(0);
3239 
3240  set<string> sourcesToUnload;
3241  sourcesToUnload.insert(sourcesRemoved[env].begin(), sourcesRemoved[env].end());
3242  sourcesToUnload.insert(sourcesDepOnModulesRemoved[env].begin(), sourcesDepOnModulesRemoved[env].end());
3243  if (! sourcesToUnload.empty())
3244  {
3245  string moduleSearchPath = env->getModuleSearchPaths().front();
3246 
3247  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
3248  VuoCompiler *otherCompiler = new VuoCompiler(moduleSearchPath + "/unused");
3249 
3250  dispatch_sync(environmentQueue, ^{
3251  otherCompiler->loadModulesAndSources(set<string>(), set<string>(), sourcesToUnload,
3252  set<string>(), set<string>(), set<string>(),
3253  false, false, env, nullptr, nullptr, "");
3254  });
3255 
3256  delete otherCompiler;
3257  });
3258  }
3259 
3260  if (!env->isBuiltIn() && !sourcesToUnload.empty())
3261  VUserLog("Deleting from %s environment: %s", env->getName().c_str(), VuoStringUtilities::join(sourcesToUnload, ", ").c_str());
3262 
3263  env->deleteModulesCompiledFromSourceCode(sourcesToUnload);
3264  }
3265 
3266  // Compile asynchronously:
3267  // - source files that have been added or modified
3268  // - source files that depend on them
3269  // - source files that depend on modules that have been modified
3270 
3271  map<Environment *, set<string> > sourcesDepOnModulesAdded;
3272  {
3273  map<Environment *, set<string> > modulesDepOnModulesAdded; // unused
3274  __block map<Environment *, set<string> > modulesDepOnModulesAdded_otherCompiler; //
3275  __block map<Environment *, set<string> > sourcesDepOnModulesAdded_otherCompiler;
3276 
3277  map<Environment *, set<string> > actualModuleKeysAdded;
3278  for (const vector<Environment *> &envs : environments)
3279  {
3280  Environment *env = envs.at(0);
3281  for (VuoCompilerModule *module : actualModulesAdded[env])
3282  actualModuleKeysAdded[env].insert( module->getPseudoBase()->getModuleKey() );
3283  }
3284 
3285  vector<VuoDirectedAcyclicNetwork *> searchDependencyGraphs;
3286  searchDependencyGraphs.push_back(compositionDependencyGraph);
3287  for (map<string, vector<Environment *> >::iterator ii = environmentsForCompositionFamily.begin(); ii != environmentsForCompositionFamily.end(); ++ii)
3288  {
3289  vector< vector<Environment *> > otherEnvs = sharedEnvironments;
3290  otherEnvs.push_back(ii->second);
3291  VuoDirectedAcyclicNetwork *other = makeDependencyNetwork(otherEnvs, ^VuoDirectedAcyclicGraph * (Environment *env) { return env->getCompositionDependencyGraph(); });
3292  searchDependencyGraphs.push_back(other);
3293  }
3294 
3295  VuoDirectedAcyclicGraph *currentEnvironmentDependencyGraph = (currentEnvironment ? currentEnvironment->getCompositionDependencyGraph() : nullptr);
3296 
3297  findDependentModulesAndSources(actualModuleKeysAdded, searchDependencyGraphs, currentEnvironmentDependencyGraph,
3298  modulesDepOnModulesAdded, modulesDepOnModulesAdded_otherCompiler,
3299  sourcesDepOnModulesAdded, sourcesDepOnModulesAdded_otherCompiler);
3300 
3301  set<Environment *> otherEnvironments;
3302  for (map<Environment *, set<string> >::iterator i = sourcesDepOnModulesAdded_otherCompiler.begin(); i != sourcesDepOnModulesAdded_otherCompiler.end(); ++i)
3303  otherEnvironments.insert(i->first);
3304 
3305  for (Environment *env : otherEnvironments)
3306  {
3307  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
3308  string moduleSearchPath = env->getModuleSearchPaths().front();
3309  VuoCompiler *otherCompiler = new VuoCompiler(moduleSearchPath + "/unused");
3310 
3311  dispatch_sync(environmentQueue, ^{
3312  otherCompiler->loadModulesAndSources(set<string>(), set<string>(), set<string>(),
3313  sourcesDepOnModulesAdded_otherCompiler[env], set<string>(), set<string>(),
3314  false, true, env, nullptr, nullptr, "");
3315  });
3316 
3317  delete otherCompiler;
3318  });
3319  }
3320  }
3321 
3322  set<dispatch_group_t> sourcesLoading;
3323  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3324  {
3325  Environment *env = (*i).at(0);
3326 
3327  set<string> sourcesToCompile;
3328  sourcesToCompile.insert(sourcesAdded[env].begin(), sourcesAdded[env].end());
3329  sourcesToCompile.insert(sourcesModified[env].begin(), sourcesModified[env].end());
3330 
3331  if (sourcesToCompile.size() == 0)
3332  continue;
3333 
3334  set<dispatch_group_t> s = env->compileModulesFromSourceCode(sourcesToCompile, shouldRecompileSourcesIfUnchanged);
3335  sourcesLoading.insert(s.begin(), s.end());
3336  }
3337 
3338  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3339  {
3340  Environment *env = (*i).at(0);
3341 
3342  set<string> sourcesToCompile;
3343  sourcesToCompile.insert(sourcesDepOnModulesAdded[env].begin(), sourcesDepOnModulesAdded[env].end());
3344  sourcesToCompile.insert(sourcesDepOnModulesModified[env].begin(), sourcesDepOnModulesModified[env].end());
3345 
3346  if (sourcesToCompile.size() == 0)
3347  continue;
3348 
3349  env->compileModulesFromSourceCode(sourcesToCompile, true);
3350  }
3351 
3352  // Notify compiler delegates.
3353 
3354  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3355  {
3356  Environment *env = (*i).at(0);
3357  env->notifyCompilers(actualModulesAdded[env], actualModulesModified[env], actualModulesRemoved[env], issues[env]);
3358  }
3359 
3360  // Since the dispatch groups for specialized modules are temporary (caller is responsible for releasing them)
3361  // but the dispatch groups for module sources should stay alive as long as the ModuleInfo that contains them,
3362  // retain the dispatch groups for module sources so that all dispatch groups can be safely released by the caller.
3363 
3364  for (const dispatch_group_t &group : sourcesLoading)
3365  dispatch_retain(group);
3366 
3367  set<dispatch_group_t> loadingGroups;
3368  loadingGroups.insert(specializedModulesLoading.begin(), specializedModulesLoading.end());
3369  loadingGroups.insert(sourcesLoading.begin(), sourcesLoading.end());
3370  return loadingGroups;
3371 }
3372 
3384 void VuoCompiler::findDependentModulesAndSources(map<Environment *, set<string> > &changedModules,
3385  const vector<VuoDirectedAcyclicNetwork *> &searchDependencyGraphs,
3386  VuoDirectedAcyclicGraph *currentEnvironmentDependencyGraph,
3387  map<Environment *, set<string> > &modulesDepOnChangedModules_this,
3388  map<Environment *, set<string> > &modulesDepOnChangedModules_other,
3389  map<Environment *, set<string> > &sourcesDepOnChangedModules_this,
3390  map<Environment *, set<string> > &sourcesDepOnChangedModules_other)
3391 {
3392  for (const vector<Environment *> &envs : environments)
3393  {
3394  Environment *env = envs.at(0);
3395 
3396  for (const string &module : changedModules[env])
3397  {
3398  set<VuoDirectedAcyclicGraph::Vertex *> dependents;
3399  for (VuoDirectedAcyclicNetwork *searchDependencyGraph : searchDependencyGraphs)
3400  {
3401  vector<VuoDirectedAcyclicGraph::Vertex *> moduleVertices;
3402  if (currentEnvironmentDependencyGraph)
3403  {
3404  // If a module with the same module key is installed in multiple locations,
3405  // only consider the one being modified or removed.
3406  VuoDirectedAcyclicGraph::Vertex *mv = currentEnvironmentDependencyGraph->findVertex(module);
3407  if (mv)
3408  moduleVertices.push_back(mv);
3409  }
3410  else
3411  moduleVertices = searchDependencyGraph->findVertex(module);
3412 
3413  for (VuoDirectedAcyclicGraph::Vertex *moduleVertexRaw : moduleVertices)
3414  {
3415  DependencyGraphVertex *moduleVertex = static_cast<DependencyGraphVertex *>(moduleVertexRaw);
3416  if (moduleVertex->getEnvironment())
3417  {
3418  vector<VuoDirectedAcyclicGraph::Vertex *> upstream = searchDependencyGraph->getUpstreamVertices(moduleVertex);
3419  dependents.insert(upstream.begin(), upstream.end());
3420  }
3421  }
3422  }
3423 
3424  for (VuoDirectedAcyclicGraph::Vertex *dependentVertexRaw : dependents)
3425  {
3426  DependencyGraphVertex *v = static_cast<DependencyGraphVertex *>(dependentVertexRaw);
3427  Environment *dependentEnv = v->getEnvironment();
3428  if (! dependentEnv)
3429  continue;
3430 
3431  string dependent = v->getDependency();
3432 
3433  // Skip if the dependent module is already being modified/removed in its own right
3434  // (e.g. if the module depends on another in the same node set and the node set is being removed).
3435  if (changedModules[dependentEnv].find(dependent) != changedModules[dependentEnv].end())
3436  continue;
3437 
3438  ModuleInfo *foundSourceInfo = dependentEnv->listSourceFile(dependent);
3439  ModuleInfo *foundModuleInfo = dependentEnv->listModule(dependent);
3440 
3441  bool belongsToCurrentCompiler = false;
3442  for (const vector<Environment *> &envs2 : environments)
3443  {
3444  if (find(envs2.begin(), envs2.end(), dependentEnv) != envs2.end())
3445  {
3446  belongsToCurrentCompiler = true;
3447  break;
3448  }
3449  }
3450 
3451  map<Environment *, set<string> > *whicheverDependents = nullptr;
3452  ModuleInfo *moduleInfo = nullptr;
3453  if (foundSourceInfo)
3454  {
3455  moduleInfo = foundSourceInfo;
3456  whicheverDependents = (belongsToCurrentCompiler ? &sourcesDepOnChangedModules_this : &sourcesDepOnChangedModules_other);
3457  }
3458  else if (foundModuleInfo)
3459  {
3460  moduleInfo = foundModuleInfo;
3461  whicheverDependents = (belongsToCurrentCompiler ? &modulesDepOnChangedModules_this : &modulesDepOnChangedModules_other);
3462  }
3463 
3464  (*whicheverDependents)[dependentEnv].insert(dependent);
3465  moduleInfo->setAttempted(false);
3466  }
3467  }
3468  }
3469 }
3470 
3474 void VuoCompiler::loadedModules(map<string, VuoCompilerModule *> modulesAdded,
3475  map<string, pair<VuoCompilerModule *, VuoCompilerModule *> > modulesModified,
3476  map<string, VuoCompilerModule *> modulesRemoved,
3477  VuoCompilerIssues *issues, void *delegateDataV, Environment *currentEnvironment)
3478 {
3479  //VLog("C=%p %lu %lu %lu", this, modulesAdded.size(), modulesModified.size(), modulesRemoved.size());
3480 
3481  // If a module is added, superseding a version of the same module installed at a broader scope,
3482  // notify this VuoCompiler that the module has been modified rather than added.
3483  //
3484  // If a module is added, but a version of the same module is already installed at a narrower scope,
3485  // don't notify this VuoCompiler, since it will continue to use the version at the narrower scope.
3486  //
3487  // Same idea when a module is modified or removed while another version is installed at a different scope.
3488 
3489  auto findVersionsOfModule = [this, currentEnvironment] (const string &moduleKey)
3490  {
3491  vector< pair<Environment *, VuoCompilerModule *> > moduleVersions;
3492  for (const vector<Environment *> &envs : environments)
3493  {
3494  Environment *env = envs.at(0);
3495  VuoCompilerModule *module = env->findModule(moduleKey);
3496  if (module || env == currentEnvironment)
3497  moduleVersions.push_back( make_pair(env, module) );
3498  }
3499  return moduleVersions;
3500  };
3501 
3502  for (map<string, VuoCompilerModule *>::iterator i = modulesAdded.begin(); i != modulesAdded.end(); )
3503  {
3504  string moduleKey = i->first;
3505  VuoCompilerModule *moduleAdded = i->second;
3506 
3507  vector< pair<Environment *, VuoCompilerModule *> > moduleVersions = findVersionsOfModule(moduleKey);
3508 
3509  if (moduleVersions.size() > 1)
3510  {
3511  modulesAdded.erase(i++);
3512 
3513  if (moduleVersions.back().second == moduleAdded)
3514  {
3515  VuoCompilerModule *moduleSuperseded = moduleVersions.at(moduleVersions.size()-2).second;
3516  modulesModified[moduleKey] = make_pair(moduleSuperseded, moduleAdded);
3517  }
3518  }
3519  else
3520  ++i;
3521  }
3522 
3523  for (map<string, pair<VuoCompilerModule *, VuoCompilerModule *> >::iterator i = modulesModified.begin(); i != modulesModified.end(); )
3524  {
3525  string moduleKey = i->first;
3526  VuoCompilerModule *moduleModified = i->second.second;
3527 
3528  vector< pair<Environment *, VuoCompilerModule *> > moduleVersions = findVersionsOfModule(moduleKey);
3529 
3530  if (moduleVersions.size() > 1 && moduleVersions.back().second != moduleModified)
3531  modulesModified.erase(i++);
3532  else
3533  ++i;
3534  }
3535 
3536  for (map<string, VuoCompilerModule *>::iterator i = modulesRemoved.begin(); i != modulesRemoved.end(); )
3537  {
3538  string moduleKey = i->first;
3539  VuoCompilerModule *moduleRemoved = i->second;
3540 
3541  vector< pair<Environment *, VuoCompilerModule *> > moduleVersions = findVersionsOfModule(moduleKey);
3542 
3543  if (moduleVersions.size() > 1)
3544  {
3545  modulesRemoved.erase(i++);
3546 
3547  if (moduleVersions.back().first == currentEnvironment)
3548  {
3549  VuoCompilerModule *moduleUnsuperseded = moduleVersions.at(moduleVersions.size()-2).second;
3550  modulesModified[moduleKey] = make_pair(moduleRemoved, moduleUnsuperseded);
3551  }
3552  }
3553  else
3554  ++i;
3555  }
3556 
3557  dispatch_async(delegateQueue, ^{
3558  VuoCompilerDelegate::LoadedModulesData *delegateData = static_cast<VuoCompilerDelegate::LoadedModulesData *>(delegateDataV);
3559 
3560  if (delegate && ! (modulesAdded.empty() && modulesModified.empty() && modulesRemoved.empty() && issues->isEmpty()))
3561  {
3562  delegate->enqueueData(delegateData);
3563  delegate->loadedModules(modulesAdded, modulesModified, modulesRemoved, issues);
3564  }
3565  else
3566  {
3567  delegateData->release();
3568  }
3569  });
3570 }
3571 
3577 void VuoCompiler::loadNodeClassGeneratedAtRuntime(VuoCompilerNodeClass *nodeClass, Environment *env)
3578 {
3579  Module *module = nodeClass->getModule();
3580  if (module)
3581  {
3582  dispatch_sync(llvmQueue, ^{
3583  setTargetForModule(nodeClass->getModule());
3584  });
3585  }
3586 
3587  dispatch_sync(environmentQueue, ^{
3588  env->replaceNodeClass(nodeClass);
3589  });
3590 
3591  __block map<string, VuoCompilerType *> inheritedTypes;
3592  void (^envReifyPortTypes)(Environment *) = ^void (Environment *env) {
3593  env->reifyPortTypes(inheritedTypes);
3594  map<string, VuoCompilerType *> currentTypes = env->getTypes();
3595  inheritedTypes.insert(currentTypes.begin(), currentTypes.end());
3596  };
3597  applyToAllEnvironments(envReifyPortTypes);
3598 }
3599 
3603 void VuoCompiler::reifyGenericPortTypes(VuoCompilerComposition *composition)
3604 {
3605  for (VuoCompilerNode *node : composition->getCachedGraph(this)->getNodes())
3606  reifyGenericPortTypes(node->getBase());
3607 
3608  composition->invalidateCachedGraph();
3609 }
3610 
3614 void VuoCompiler::reifyGenericPortTypes(VuoNode *node)
3615 {
3616  VuoCompilerSpecializedNodeClass *nodeClass = dynamic_cast<VuoCompilerSpecializedNodeClass *>(node->getNodeClass()->getCompiler());
3617  if (! nodeClass)
3618  return;
3619 
3620  // Reify any generic types on the node that don't already have a compiler detail.
3621 
3622  vector<VuoPort *> inputPorts = node->getInputPorts();
3623  vector<VuoPort *> outputPorts = node->getOutputPorts();
3624  vector<VuoPort *> ports;
3625  ports.insert(ports.end(), inputPorts.begin(), inputPorts.end());
3626  ports.insert(ports.end(), outputPorts.begin(), outputPorts.end());
3627 
3628  for (vector<VuoPort *>::iterator j = ports.begin(); j != ports.end(); ++j)
3629  {
3630  VuoCompilerPort *port = static_cast<VuoCompilerPort *>((*j)->getCompiler());
3631  VuoGenericType *genericType = dynamic_cast<VuoGenericType *>(port->getDataVuoType());
3632  if (! genericType)
3633  continue;
3634 
3635  if (! genericType->hasCompiler())
3636  {
3637  VuoCompilerType * (^compilerGetType)(string) = ^VuoCompilerType * (string moduleKey) {
3638  return getType(moduleKey);
3639  };
3640 
3641  VuoCompilerGenericType *reifiedType = VuoCompilerGenericType::newGenericType(genericType, compilerGetType);
3642  if (reifiedType)
3643  port->setDataVuoType(reifiedType->getBase());
3644  }
3645  }
3646 
3647  // Update the node class's backing to match the node's backing.
3648 
3649  nodeClass->updateBackingNodeClass(node, this);
3650 }
3651 
3658 void VuoCompiler::compileModule(string inputPath, string outputPath)
3659 {
3660  compileModule(inputPath, outputPath, vector<string>());
3661 }
3662 
3670 void VuoCompiler::compileModule(string inputPath, string outputPath, const vector<string> &includePaths)
3671 {
3672  if (isVerbose)
3673  print();
3674 
3675  vector<string> allIncludePaths = includePaths;
3676  string preprocessedInputPath = inputPath;
3677 
3678  string tmpPreprocessedInputDir;
3679  string dir, file, ext;
3680  VuoFileUtilities::splitPath(inputPath, dir, file, ext);
3682  {
3683  string inputContents = VuoFileUtilities::readFileToString(inputPath);
3684  string preprocessedInputContents = inputContents;
3686  if (inputContents != preprocessedInputContents)
3687  {
3688  // Unspecialized generic node class
3689  allIncludePaths.push_back(dir.empty() ? "." : dir);
3690  tmpPreprocessedInputDir = VuoFileUtilities::makeTmpDir(file);
3691  preprocessedInputPath = tmpPreprocessedInputDir + "/" + file + "." + ext;
3692  VuoFileUtilities::preserveOriginalFileName(preprocessedInputContents, file + "." + ext);
3693  VuoFileUtilities::writeStringToFile(preprocessedInputContents, preprocessedInputPath);
3694  }
3695  }
3696  else if (ext == "fs")
3697  {
3698  VuoFileUtilities::File vuf(dir, file + "." + ext);
3699  VuoModuleCompiler *moduleCompiler = VuoModuleCompiler::newModuleCompiler("isf", getModuleKeyForPath(inputPath), &vuf);
3700  if (moduleCompiler)
3701  {
3702  auto getType = [this] (const string &moduleKey) { return this->getType(moduleKey); };
3703  VuoCompilerIssues *issues = new VuoCompilerIssues();
3704  Module *module = moduleCompiler->compile(getType, llvmQueue, issues);
3705  if (module)
3706  dispatch_sync(llvmQueue, ^{
3707  writeModuleToBitcode(module, outputPath);
3708  });
3709 
3710  if (!issues->isEmpty())
3711  throw VuoCompilerException(issues, true);
3712  delete issues;
3713  }
3714  return;
3715  }
3716 
3717  vector<string> extraArgs;
3718  for (vector<string>::iterator i = allIncludePaths.begin(); i != allIncludePaths.end(); ++i)
3719  {
3720  extraArgs.push_back("-I");
3721  extraArgs.push_back(*i);
3722  }
3723 
3724  string macosxSdkFolder = "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/";
3725  if (VuoFileUtilities::fileExists(macosxSdkFolder + "MacOSX10.10.sdk"))
3726  {
3727  extraArgs.push_back("-isysroot");
3728  extraArgs.push_back(macosxSdkFolder + "MacOSX10.10.sdk");
3729  }
3730 
3731  __block vector<string> headerSearchPaths;
3732  void (^envGetHeaderSearchPaths)(Environment *) = ^void (Environment *env) {
3733  vector<string> result = env->getHeaderSearchPaths();
3734  headerSearchPaths.insert(headerSearchPaths.end(), result.begin(), result.end());
3735  };
3736  applyToInstalledEnvironments(envGetHeaderSearchPaths);
3737 
3738  __block Module *module;
3739  dispatch_sync(llvmQueue, ^{
3740  module = readModuleFromC(preprocessedInputPath, headerSearchPaths, extraArgs);
3741  });
3742  string moduleKey = getModuleKeyForPath(inputPath);
3743  if (! tmpPreprocessedInputDir.empty())
3744  remove(tmpPreprocessedInputDir.c_str());
3745  if (! module)
3746  {
3747  VuoCompilerIssue issue(VuoCompilerIssue::Error, "compiling module", inputPath,
3748  "", "%moduleKey couldn't be compiled as a node class, type, or library. Check the macOS Console for details.");
3749  issue.setModuleKey(moduleKey);
3750  throw VuoCompilerException(issue);
3751  }
3752 
3753  dispatch_sync(llvmQueue, ^{
3754  VuoCompilerModule *compilerModule = VuoCompilerModule::newModule(moduleKey, module, "");
3755  if (! compilerModule)
3756  {
3757  VUserLog("Error: Didn't recognize '%s' as a node class, type, or library.", inputPath.c_str());
3758  return;
3759  }
3760 
3761  setTargetForModule(module, target);
3762  writeModuleToBitcode(module, outputPath);
3763 
3764  delete module;
3765  });
3766 }
3767 
3771 Module * VuoCompiler::compileCompositionToModule(VuoCompilerComposition *composition, const string &moduleKey, bool isTopLevelComposition,
3772  VuoCompilerIssues *issues)
3773 {
3774  composition->check(issues);
3775 
3776  reifyGenericPortTypes(composition);
3777 
3779  isTopLevelComposition,
3780  moduleKey, this);
3781  if (telemetry == "console")
3782  generator->setDebugMode(true);
3783 
3784  __block Module *module;
3785  dispatch_sync(llvmQueue, ^{
3786  module = generator->generateBitcode();
3787  setTargetForModule(module, target);
3788  });
3789 
3790  delete generator;
3791 
3792  return module;
3793 }
3794 
3805 void VuoCompiler::compileComposition(VuoCompilerComposition *composition, string outputPath, bool isTopLevelComposition,
3806  VuoCompilerIssues *issues)
3807 {
3808  string moduleKey = getModuleKeyForPath(outputPath);
3809  Module *module = compileCompositionToModule(composition, moduleKey, isTopLevelComposition, issues);
3810 
3811  dispatch_sync(llvmQueue, ^{
3812  writeModuleToBitcode(module, outputPath);
3813  });
3814 }
3815 
3826 void VuoCompiler::compileComposition(string inputPath, string outputPath, bool isTopLevelComposition,
3827  VuoCompilerIssues *issues)
3828 {
3829  VDebugLog("Compiling '%s'…", inputPath.c_str());
3830  if (isVerbose)
3831  print();
3832 
3834  {
3835  VuoCompilerIssue issue(VuoCompilerIssue::Error, "opening composition", inputPath,
3836  "", "The composition file couldn't be read or was empty.");
3837  throw VuoCompilerException(issue);
3838  }
3839 
3840  try
3841  {
3842  string compositionString = VuoFileUtilities::readFileToString(inputPath);
3843  compileCompositionString(compositionString, outputPath, isTopLevelComposition, issues);
3844  }
3845  catch (VuoCompilerException &e)
3846  {
3847  e.getIssues()->setFilePathIfEmpty(inputPath);
3848  throw;
3849  }
3850 
3851  VDebugLog("Done.");
3852 }
3853 
3864 void VuoCompiler::compileCompositionString(const string &compositionString, string outputPath, bool isTopLevelComposition,
3865  VuoCompilerIssues *issues)
3866 {
3868  compileComposition(composition, outputPath, isTopLevelComposition, issues);
3869 
3870  VuoComposition *baseComposition = composition->getBase();
3871  delete composition;
3872  delete baseComposition;
3873 }
3874 
3878 void VuoCompiler::compileSubcompositionString(const string &compositionString, const string &outputPath,
3879  std::function<void(void)> moduleLoadedCallback, Environment *environment,
3880  VuoCompilerIssues *issues, const string inputPathForIssues)
3881 {
3882  if (! issues)
3883  issues = new VuoCompilerIssues();
3884 
3885  bool compilationSucceeded = false;
3886  try
3887  {
3888  compileCompositionString(compositionString, outputPath, false, issues);
3889  compilationSucceeded = true;
3890  }
3891  catch (VuoCompilerException &e)
3892  {
3893  if (issues != e.getIssues())
3894  issues->append(e.getIssues());
3895  }
3896 
3897  if (! compilationSucceeded)
3898  {
3899  VuoFileUtilities::deleteFile(outputPath);
3900  issues->setFilePathIfEmpty(inputPathForIssues);
3901  }
3902 
3903  string outputDir, file, ext;
3904  VuoFileUtilities::splitPath(outputPath, outputDir, file, ext);
3906 
3907  environment->moduleSearchPathContentsChanged(outputDir, outputPath, compositionString, moduleLoadedCallback, this, issues);
3908 }
3909 
3923 void VuoCompiler::linkCompositionToCreateExecutable(string inputPath, string outputPath, Optimization optimization, string rPath)
3924 {
3925  linkCompositionToCreateExecutableOrDynamicLibrary(inputPath, outputPath, optimization, false, rPath);
3926 }
3927 
3944 void VuoCompiler::linkCompositionToCreateDynamicLibrary(string inputPath, string outputPath, Optimization optimization)
3945 {
3946  linkCompositionToCreateExecutableOrDynamicLibrary(inputPath, outputPath, optimization, true);
3947 }
3948 
3963 void VuoCompiler::linkCompositionToCreateExecutableOrDynamicLibrary(string compiledCompositionPath, string linkedCompositionPath,
3964  Optimization optimization, bool isDylib, string rPath)
3965 {
3966  if (isVerbose)
3967  print();
3968 
3969  if (optimization == Optimization_FastBuildExistingCache)
3970  shouldLoadAllModules = false;
3971 
3972  set<string> dependencies = getDependenciesForComposition(compiledCompositionPath);
3973  dependencies.insert(getRuntimeDependency());
3974  if (! isDylib)
3975  dependencies.insert(getRuntimeMainDependency());
3976 
3977  set<Module *> modules;
3978  set<string> libraries;
3979  set<string> frameworks;
3980  getLinkerInputs(dependencies, optimization, modules, libraries, frameworks);
3981 
3982  libraries.insert(compiledCompositionPath);
3983 
3984  link(linkedCompositionPath, modules, libraries, frameworks, isDylib, rPath);
3985 }
3986 
4001 void VuoCompiler::linkCompositionToCreateDynamicLibraries(string compiledCompositionPath, string linkedCompositionPath,
4002  VuoRunningCompositionLibraries *runningCompositionLibraries)
4003 {
4004  if (isVerbose)
4005  print();
4006 
4007  // Get the dependencies used by the new resources and not the previous resources.
4008 
4009  set<string> carriedOverDependencies = runningCompositionLibraries->getDependenciesLoaded();
4010  set<string> allDependencies = getDependenciesForComposition(compiledCompositionPath);
4011  set<string> addedDependencies;
4012  std::set_difference(allDependencies.begin(), allDependencies.end(),
4013  carriedOverDependencies.begin(), carriedOverDependencies.end(),
4014  std::inserter(addedDependencies, addedDependencies.end()));
4015 
4016  // Get the libraries and frameworks used by the previous resources.
4017 
4018  vector<string> carriedOverNonUnloadableLibraries = runningCompositionLibraries->getNonUnloadableLibrariesLoaded();
4019  vector<string> carriedOverUnloadableLibraries = runningCompositionLibraries->getUnloadableLibrariesLoaded();
4020  set<string> carriedOverExternalLibraries = runningCompositionLibraries->getExternalLibraries();
4021  set<string> carriedOverFrameworks = runningCompositionLibraries->getExternalFrameworks();
4022 
4023  // Link the new resource dylibs, if needed.
4024 
4025  string nonUnloadableResourcePath;
4026  string unloadableResourcePath;
4027  set<string> nonUnloadableDependencies;
4028  set<string> unloadableDependencies;
4029  map<string, set<string> > builtInCacheDependencies;
4030  map<string, set<string> > userCacheDependencies;
4031  set<string> builtInLibraries;
4032  set<string> userLibraries;
4033  set<string> addedExternalLibraries;
4034  set<string> addedFrameworks;
4035  set<string> allFrameworks;
4036  if (! addedDependencies.empty())
4037  {
4038  set<string> builtInModuleAndLibraryDependencies;
4039  set<string> userModuleAndLibraryDependencies;
4040  set<Module *> builtInModules;
4041  set<Module *> userModules;
4042 
4043  getLinkerInputs(addedDependencies, Optimization_FastBuild,
4044  builtInModuleAndLibraryDependencies, userModuleAndLibraryDependencies, builtInCacheDependencies, userCacheDependencies,
4045  builtInModules, userModules, builtInLibraries, userLibraries, addedExternalLibraries, addedFrameworks);
4046 
4047  allFrameworks.insert(carriedOverFrameworks.begin(), carriedOverFrameworks.end());
4048  allFrameworks.insert(addedFrameworks.begin(), addedFrameworks.end());
4049 
4050  string dir, linkedCompositionFile, ext;
4051  VuoFileUtilities::splitPath(linkedCompositionPath, dir, linkedCompositionFile, ext);
4052 
4053  if (! builtInModules.empty() || builtInLibraries.size() > builtInCacheDependencies.size())
4054  {
4055  nonUnloadableResourcePath = VuoFileUtilities::makeTmpFile(linkedCompositionFile + "-resource", "dylib");
4056  nonUnloadableDependencies = builtInModuleAndLibraryDependencies;
4057 
4058  set<string> librariesForNonUnloadableResource = builtInLibraries;
4059  librariesForNonUnloadableResource.insert(carriedOverNonUnloadableLibraries.begin(), carriedOverNonUnloadableLibraries.end());
4060  librariesForNonUnloadableResource.insert(carriedOverExternalLibraries.begin(), carriedOverExternalLibraries.end());
4061  librariesForNonUnloadableResource.insert(addedExternalLibraries.begin(), addedExternalLibraries.end());
4062 
4063  link(nonUnloadableResourcePath, builtInModules, librariesForNonUnloadableResource, allFrameworks, true);\
4064 
4065  for (set<string>::iterator i = builtInLibraries.begin(); i != builtInLibraries.end(); )
4066  {
4067  if (! VuoStringUtilities::endsWith(*i, ".dylib"))
4068  builtInLibraries.erase(i++);
4069  else
4070  i++;
4071  }
4072 
4073  for (set<string>::iterator i = addedExternalLibraries.begin(); i != addedExternalLibraries.end(); )
4074  {
4075  if (! VuoStringUtilities::endsWith(*i, ".dylib"))
4076  addedExternalLibraries.erase(i++);
4077  else
4078  i++;
4079  }
4080  }
4081 
4082  if (! userModules.empty() || userLibraries.size() > userCacheDependencies.size())
4083  {
4084  unloadableResourcePath = VuoFileUtilities::makeTmpFile(linkedCompositionFile + "-resource", "dylib");
4085  unloadableDependencies = userModuleAndLibraryDependencies;
4086 
4087  set<string> librariesForUnloadableResource = userLibraries;
4088  librariesForUnloadableResource.insert(builtInLibraries.begin(), builtInLibraries.end());
4089  librariesForUnloadableResource.insert(carriedOverUnloadableLibraries.begin(), carriedOverUnloadableLibraries.end());
4090  librariesForUnloadableResource.insert(carriedOverNonUnloadableLibraries.begin(), carriedOverNonUnloadableLibraries.end());
4091  librariesForUnloadableResource.insert(carriedOverExternalLibraries.begin(), carriedOverExternalLibraries.end());
4092  librariesForUnloadableResource.insert(addedExternalLibraries.begin(), addedExternalLibraries.end());
4093  if (! nonUnloadableResourcePath.empty())
4094  librariesForUnloadableResource.insert(nonUnloadableResourcePath);
4095 
4096  link(unloadableResourcePath, userModules, librariesForUnloadableResource, allFrameworks, true);
4097 
4098  for (set<string>::iterator i = userLibraries.begin(); i != userLibraries.end(); )
4099  {
4100  if (! VuoStringUtilities::endsWith(*i, ".dylib"))
4101  userLibraries.erase(i++);
4102  else
4103  i++;
4104  }
4105  }
4106  }
4107 
4108  // Get the Vuo runtime dependency.
4109 
4110  set<string> vuoRuntimePaths;
4111  {
4112  set<Module *> modules;
4113  set<string> libraries;
4114  set<string> frameworks;
4115 
4116  set<string> dependencies;
4117  dependencies.insert(getRuntimeDependency());
4118  getLinkerInputs(dependencies, Optimization_FastBuild, modules, libraries, frameworks);
4119  vuoRuntimePaths = libraries;
4120  }
4121 
4122  // Link the composition.
4123 
4124  {
4125  set<Module *> modules;
4126  set<string> libraries;
4127 
4128  libraries.insert(compiledCompositionPath);
4129  libraries.insert(carriedOverExternalLibraries.begin(), carriedOverExternalLibraries.end());
4130  libraries.insert(addedExternalLibraries.begin(), addedExternalLibraries.end());
4131  libraries.insert(carriedOverNonUnloadableLibraries.begin(), carriedOverNonUnloadableLibraries.end());
4132  libraries.insert(carriedOverUnloadableLibraries.begin(), carriedOverUnloadableLibraries.end());
4133  libraries.insert(builtInLibraries.begin(), builtInLibraries.end());
4134  libraries.insert(userLibraries.begin(), userLibraries.end());
4135  if (! nonUnloadableResourcePath.empty())
4136  libraries.insert(nonUnloadableResourcePath);
4137  if (! unloadableResourcePath.empty())
4138  libraries.insert(unloadableResourcePath);
4139  libraries.insert(vuoRuntimePaths.begin(), vuoRuntimePaths.end());
4140  link(linkedCompositionPath, modules, libraries, allFrameworks, true);
4141  }
4142 
4143  // Now that we're past the point where an exception can be thrown, update the RunningCompositionLibraries.
4144 
4145  if (! nonUnloadableResourcePath.empty())
4146  runningCompositionLibraries->enqueueResourceLibraryToLoad(nonUnloadableResourcePath, nonUnloadableDependencies, false);
4147 
4148  if (! unloadableResourcePath.empty())
4149  runningCompositionLibraries->enqueueResourceLibraryToLoad(unloadableResourcePath, unloadableDependencies, true);
4150 
4151  for (map<string, set<string> >::iterator i = builtInCacheDependencies.begin(); i != builtInCacheDependencies.end(); ++i)
4152  runningCompositionLibraries->enqueueCacheLibraryToLoad(i->first, i->second, false);
4153 
4154  for (map<string, set<string> >::iterator i = userCacheDependencies.begin(); i != userCacheDependencies.end(); ++i)
4155  runningCompositionLibraries->enqueueCacheLibraryToLoad(i->first, i->second, true);
4156 
4157  runningCompositionLibraries->addExternalFrameworks(addedFrameworks);
4158  runningCompositionLibraries->addExternalLibraries(addedExternalLibraries);
4159 }
4160 
4167 set<string> VuoCompiler::getDependenciesForComposition(const string &compiledCompositionPath)
4168 {
4169  VDebugLog("Gathering dependencies for '%s'…", compiledCompositionPath.c_str());
4170 
4171  // Add the node classes in the top-level composition and their dependencies.
4172  __block set<string> directDependencies;
4173  string moduleKey = getModuleKeyForPath(compiledCompositionPath);
4174  Module *module = readModuleFromBitcode(compiledCompositionPath);
4175  dispatch_sync(llvmQueue, ^{
4176  VuoCompilerModule *compilerModule = VuoCompilerModule::newModule(moduleKey, module, "");
4177  directDependencies = compilerModule->getDependencies();
4178  delete compilerModule;
4179  delete module;
4180  });
4181 
4182  try
4183  {
4184  auto deps = getDependenciesForComposition(directDependencies, true);
4185  VDebugLog("Done.");
4186  return deps;
4187  }
4188  catch (VuoCompilerException &e)
4189  {
4190  e.getIssues()->setFilePathIfEmpty(compiledCompositionPath);
4191  throw;
4192  }
4193 }
4194 
4202 {
4203  set<string> directDependencies;
4204 
4205  set<VuoCompilerNode *> nodes = composition->getCachedGraph(this)->getNodes();
4206  for (VuoCompilerNode *node : nodes)
4207  if (node->getBase()->getNodeClass()->hasCompiler())
4208  directDependencies.insert( node->getBase()->getNodeClass()->getCompiler()->getDependencyName() );
4209 
4210  vector<VuoPublishedPort *> publishedInputPorts = composition->getBase()->getPublishedInputPorts();
4211  vector<VuoPublishedPort *> publishedOutputPorts = composition->getBase()->getPublishedOutputPorts();
4212  vector<VuoPublishedPort *> publishedPorts;
4213  publishedPorts.insert(publishedPorts.end(), publishedInputPorts.begin(), publishedInputPorts.end());
4214  publishedPorts.insert(publishedPorts.end(), publishedOutputPorts.begin(), publishedOutputPorts.end());
4215  for (VuoPublishedPort *publishedPort : publishedPorts)
4216  {
4217  if (publishedPort->getClass()->hasCompiler())
4218  {
4219  VuoType *portType = static_cast<VuoCompilerPortClass *>( publishedPort->getClass()->getCompiler() )->getDataVuoType();
4220  if (portType)
4221  {
4222  string dependency;
4223  VuoGenericType *genericType = dynamic_cast<VuoGenericType *>(portType);
4224  if (genericType)
4225  {
4226  VuoGenericType::Compatibility compatibility;
4227  vector<string> compatibleTypeNames = genericType->getCompatibleSpecializedTypes(compatibility);
4228  dependency = VuoCompilerGenericType::chooseBackingTypeName(portType->getModuleKey(), compatibleTypeNames);
4229  }
4230  else
4231  dependency = portType->getModuleKey();
4232 
4233  directDependencies.insert(dependency);
4234  }
4235  }
4236  }
4237 
4238  return directDependencies;
4239 }
4240 
4247 set<string> VuoCompiler::getDependenciesForComposition(VuoCompilerComposition *composition)
4248 {
4249  set<string> directDependencies = getDirectDependenciesForComposition(composition);
4250  return getDependenciesForComposition(directDependencies, false);
4251 }
4252 
4259 {
4260  __block vector<string> librarySearchPaths;
4261  applyToInstalledEnvironments(^void (Environment *env) {
4262  vector<string> result = env->getLibrarySearchPaths();
4263  librarySearchPaths.insert(librarySearchPaths.end(), result.begin(), result.end());
4264  });
4265 
4266  set<string> dylibDeps;
4267  for (string dep : getDependenciesForComposition(composition))
4268  {
4269  string path = getLibraryPath(dep, librarySearchPaths);
4270  if (VuoStringUtilities::endsWith(path, ".dylib"))
4271  dylibDeps.insert(path);
4272  }
4273 
4274  return dylibDeps;
4275 }
4276 
4290 set<string> VuoCompiler::getDependenciesForComposition(const set<string> &directDependencies, bool checkCompatibility)
4291 {
4292  // Make sure that any compiler-generated node classes have been generated and added to the dependency graph.
4293  for (set<string>::const_iterator i = directDependencies.begin(); i != directDependencies.end(); ++i)
4294  getNodeClass(*i);
4295 
4296  set<string> dependencies;
4297  for (set<string>::iterator i = directDependencies.begin(); i != directDependencies.end(); ++i)
4298  {
4299  string moduleKey = *i;
4300 
4301  // First pass: Find all dependencies of the direct dependency that have been loaded so far.
4302  vector<VuoDirectedAcyclicGraph::Vertex *> firstPassVertices = dependencyGraph->findVertex(moduleKey);
4303  set<VuoDirectedAcyclicGraph::Vertex *> firstPassDependencies(firstPassVertices.begin(), firstPassVertices.end());
4304  for (vector<VuoDirectedAcyclicGraph::Vertex *>::iterator j = firstPassVertices.begin(); j != firstPassVertices.end(); ++j)
4305  {
4306  vector<VuoDirectedAcyclicGraph::Vertex *> downstream = dependencyGraph->getDownstreamVertices(*j);
4307  firstPassDependencies.insert(downstream.begin(), downstream.end());
4308  }
4309 
4310  // Make sure that any compiler-generated node classes in subcompositions have been generated and added to the dependency graph.
4311  for (set<VuoDirectedAcyclicGraph::Vertex *>::iterator j = firstPassDependencies.begin(); j != firstPassDependencies.end(); ++j)
4312  {
4313  DependencyGraphVertex *v = static_cast<DependencyGraphVertex *>(*j);
4314  getNodeClass(v->getDependency());
4315  }
4316 
4317  // Second pass: Find all dependencies of the direct dependency, this time including dependencies of the node classes just generated.
4318  vector<VuoDirectedAcyclicGraph::Vertex *> moduleVertices = dependencyGraph->findVertex(moduleKey);
4319  set<VuoDirectedAcyclicGraph::Vertex *> moduleDependencies(moduleVertices.begin(), moduleVertices.end());
4320  for (vector<VuoDirectedAcyclicGraph::Vertex *>::iterator j = moduleVertices.begin(); j != moduleVertices.end(); ++j)
4321  {
4322  vector<VuoDirectedAcyclicGraph::Vertex *> downstream = dependencyGraph->getDownstreamVertices(*j);
4323  moduleDependencies.insert(downstream.begin(), downstream.end());
4324  }
4325 
4326  // Sort the direct dependency and all of its dependencies into those that are and are not compatible.
4327  set<string> dependenciesToAdd;
4328  set<string> incompatibleDependencies;
4329  for (set<VuoDirectedAcyclicGraph::Vertex *>::iterator j = moduleDependencies.begin(); j != moduleDependencies.end(); ++j)
4330  {
4331  DependencyGraphVertex *v = static_cast<DependencyGraphVertex *>(*j);
4332  if (! checkCompatibility || ! v->getEnvironment() || v->isCompatible())
4333  dependenciesToAdd.insert(v->getDependency());
4334  else
4335  incompatibleDependencies.insert(v->getDependency());
4336  }
4337 
4338  if (! checkCompatibility || incompatibleDependencies.empty())
4339  {
4340  dependencies.insert(dependenciesToAdd.begin(), dependenciesToAdd.end());
4341  }
4342  else
4343  {
4344  VuoCompilerTargetSet compositionTargets;
4345  compositionTargets.restrictToCurrentOperatingSystemVersion();
4346 
4347  string dependencyTargetString;
4348  VuoCompilerModule *module = getModule(moduleKey);
4349  if (module)
4350  {
4351  VuoCompilerTargetSet dependencyTargets = module->getCompatibleTargets();
4352  for (set<string>::iterator i = incompatibleDependencies.begin(); i != incompatibleDependencies.end(); ++i)
4353  {
4354  VuoCompilerModule *subModule = getModule(*i);
4355  if (subModule)
4356  {
4357  VuoCompilerTargetSet subDependencyTargets = subModule->getCompatibleTargets();
4358  dependencyTargets.restrictToBeCompatibleWithAllOf(subDependencyTargets);
4359  }
4360  }
4361  dependencyTargetString = dependencyTargets.toString();
4362  }
4363  else
4364  dependencyTargetString = "(unknown operating systems)";
4365 
4366  string modulePlaceholder = (module ? "%module" : "%moduleKey");
4367  VuoCompilerIssue issue(VuoCompilerIssue::Error, "linking composition", "",
4368  "Node incompatible with operating system",
4369  modulePlaceholder + " is only compatible with " + dependencyTargetString +
4370  ", so this composition can't run on your macOS version (" + compositionTargets.toString() + ").");
4371  issue.setModule(module->getPseudoBase());
4372  issue.setModuleKey(moduleKey);
4373  throw VuoCompilerException(issue);
4374  }
4375  }
4376 
4377  // Add the libraries needed by every linked composition.
4378  vector<string> coreDependencies = getCoreVuoDependencies();
4379  dependencies.insert(coreDependencies.begin(), coreDependencies.end());
4380 
4381  return dependencies;
4382 }
4383 
4388 void VuoCompiler::getLinkerInputs(const set<string> &dependencies, Optimization optimization,
4389  set<Module *> &modules, set<string> &libraries, set<string> &frameworks)
4390 {
4391  set<string> builtInModuleAndLibraryDependencies;
4392  set<string> userModuleAndLibraryDependencies;
4393  map<string, set<string> > builtInCacheDependencies;
4394  map<string, set<string> > userCacheDependencies;
4395  set<Module *> builtInModules;
4396  set<Module *> userModules;
4397  set<string> builtInLibraries;
4398  set<string> userLibraries;
4399  set<string> externalLibraries;
4400 
4401  getLinkerInputs(dependencies, optimization,
4402  builtInModuleAndLibraryDependencies, userModuleAndLibraryDependencies, builtInCacheDependencies, userCacheDependencies,
4403  builtInModules, userModules, builtInLibraries, userLibraries, externalLibraries, frameworks);
4404 
4405  modules.insert(builtInModules.begin(), builtInModules.end());
4406  modules.insert(userModules.begin(), userModules.end());
4407  libraries.insert(builtInLibraries.begin(), builtInLibraries.end());
4408  libraries.insert(userLibraries.begin(), userLibraries.end());
4409  libraries.insert(externalLibraries.begin(), externalLibraries.end());
4410 }
4411 
4425 void VuoCompiler::getLinkerInputs(const set<string> &dependencies, Optimization optimization,
4426  set<string> &builtInModuleAndLibraryDependencies, set<string> &userModuleAndLibraryDependencies,
4427  map<string, set<string> > &builtInCacheDependencies, map<string, set<string> > &userCacheDependencies,
4428  set<Module *> &builtInModules, set<Module *> &userModules,
4429  set<string> &builtInLibraries, set<string> &userLibraries,
4430  set<string> &externalLibraries, set<string> &externalFrameworks)
4431 {
4432  bool shouldUseModuleCache = (optimization == Optimization_FastBuild || optimization == Optimization_FastBuildExistingCache);
4433  if (shouldUseModuleCache)
4434  useModuleCache(true, optimization == Optimization_FastBuildExistingCache);
4435 
4436  __block vector<string> librarySearchPaths;
4437  void (^envGetLibrarySearchPaths)(Environment *) = ^void (Environment *env) {
4438  vector<string> result = env->getLibrarySearchPaths();
4439  librarySearchPaths.insert(librarySearchPaths.end(), result.begin(), result.end());
4440  };
4441  applyToInstalledEnvironments(envGetLibrarySearchPaths);
4442 
4443  for (set<string>::iterator i = dependencies.begin(); i != dependencies.end(); ++i)
4444  {
4445  string dependency = *i;
4446 
4447  bool foundInCache = false;
4448  string moduleCachePath;
4449  bool isInBuiltInModuleCache = false;
4450  if (shouldUseModuleCache)
4451  foundInCache = findInModuleCache(dependency, moduleCachePath, isInBuiltInModuleCache);
4452 
4453  if (foundInCache)
4454  {
4455  if (isInBuiltInModuleCache)
4456  {
4457  builtInLibraries.insert(moduleCachePath);
4458  builtInCacheDependencies[moduleCachePath].insert(dependency);
4459  }
4460  else
4461  {
4462  userLibraries.insert(moduleCachePath);
4463  userCacheDependencies[moduleCachePath].insert(dependency);
4464  }
4465  }
4466  else
4467  {
4468  __block VuoCompilerModule *module = NULL;
4469  void (^envFindModule)(Environment *) = ^void (Environment *env) {
4470  VuoCompilerModule *result = env->findModule(dependency);
4471  if (result)
4472  module = result;
4473  };
4474  applyToAllEnvironments(envFindModule);
4475 
4476  if (module)
4477  {
4478  if (module->getModule()) // Skip not-fully-specialized generic modules when building the module cache.
4479  {
4480  string modulePath = module->getModulePath();
4481  if (! modulePath.empty() && dynamic_cast<VuoCompilerType *>(module))
4482  {
4483  if (module->isBuiltIn())
4484  builtInLibraries.insert(modulePath);
4485  else
4486  userLibraries.insert(modulePath);
4487  }
4488  else
4489  {
4490  if (module->isBuiltIn())
4491  builtInModules.insert(module->getModule());
4492  else
4493  userModules.insert(module->getModule());
4494  }
4495 
4496  if (module->isBuiltIn())
4497  builtInModuleAndLibraryDependencies.insert(dependency);
4498  else
4499  userModuleAndLibraryDependencies.insert(dependency);
4500  }
4501  }
4502  else
4503  {
4504  if (VuoStringUtilities::endsWith(dependency, ".framework"))
4505  externalFrameworks.insert(dependency);
4506  else
4507  {
4508  string dependencyPath = getLibraryPath(dependency, librarySearchPaths);
4509  if (! dependencyPath.empty())
4510  externalLibraries.insert(dependencyPath);
4511  else
4512  VUserLog("Warning: Could not locate dependency '%s'.", dependency.c_str());
4513  }
4514  }
4515  }
4516  }
4517 }
4518 
4524 string VuoCompiler::getLibraryPath(const string &dependency, vector<string> librarySearchPaths)
4525 {
4526  if (dependency[0] == '/' && VuoFileUtilities::fileExists(dependency))
4527  return dependency;
4528 
4529  // Put the system library folder last in the list — prefer to use the libraries bundled in Vuo.framework.
4530  // Don't attempt to use OpenSSL from the system library folder, since macOS 10.15 prevents linking to it.
4531  if (dependency != "crypto"
4532  && dependency != "ssl")
4533  librarySearchPaths.push_back("/usr/lib");
4534 
4535  for (auto &path : librarySearchPaths)
4536  {
4537  vector<string> variations;
4538  variations.push_back(path + "/" + dependency);
4539  variations.push_back(path + "/lib" + dependency);
4540  variations.push_back(path + "/lib" + dependency + ".dylib");
4541  variations.push_back(path + "/lib" + dependency + ".a");
4542  for (auto &variation : variations)
4543  if (VuoFileUtilities::fileExists(variation))
4544  return variation;
4545  }
4546 
4547  return "";
4548 }
4549 
4557 void VuoCompiler::useModuleCache(bool shouldUseExistingBuiltInCaches, bool shouldUseExistingOtherCaches)
4558 {
4559  loadModulesIfNeeded();
4560 
4561  // Iterate through the environments in the order that the caches need to be built.
4562 
4563  dispatch_sync(environmentQueue, ^{
4564  set<string> dylibsForCachesOfInstalledModules;
4565  set<string> frameworksForCachesOfInstalledModules;
4566  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
4567  {
4568  bool builtin = (i == environments.begin());
4569  set<string> dylibsForCacheOfGeneratedModules;
4570  set<string> frameworksForCacheOfGeneratedModules;
4571 
4572  for (int j = i->size() - 1; j >= 0; --j)
4573  {
4574  Environment *env = i->at(j);
4575  bool installed = (j == 0);
4576 
4577  set<string> cacheableModulesAndDependencies;
4578  set<string> dylibsNeededToLinkToThisCache;
4579  set<string> frameworksNeededToLinkToThisCache;
4580  env->getCacheableModulesAndDependencies(builtin, installed, cacheableModulesAndDependencies,
4581  dylibsNeededToLinkToThisCache, frameworksNeededToLinkToThisCache);
4582 
4583  set<string> accumulatedDylibs;
4584  accumulatedDylibs.insert(dylibsNeededToLinkToThisCache.begin(), dylibsNeededToLinkToThisCache.end());
4585  accumulatedDylibs.insert(dylibsForCachesOfInstalledModules.begin(), dylibsForCachesOfInstalledModules.end());
4586  accumulatedDylibs.insert(dylibsForCacheOfGeneratedModules.begin(), dylibsForCacheOfGeneratedModules.end());
4587 
4588  set<string> accumulatedFrameworks;
4589  accumulatedFrameworks.insert(frameworksNeededToLinkToThisCache.begin(), frameworksNeededToLinkToThisCache.end());
4590  accumulatedFrameworks.insert(frameworksForCachesOfInstalledModules.begin(), frameworksForCachesOfInstalledModules.end());
4591  accumulatedFrameworks.insert(frameworksForCacheOfGeneratedModules.begin(), frameworksForCacheOfGeneratedModules.end());
4592 
4593  bool shouldUseExistingCache = (builtin ? shouldUseExistingBuiltInCaches : shouldUseExistingOtherCaches);
4594  env->useModuleCache(shouldUseExistingCache, this, cacheableModulesAndDependencies,
4595  accumulatedDylibs, accumulatedFrameworks);
4596 
4597  if (installed)
4598  {
4599  dylibsForCachesOfInstalledModules.insert(dylibsNeededToLinkToThisCache.begin(), dylibsNeededToLinkToThisCache.end());
4600  frameworksForCachesOfInstalledModules.insert(frameworksNeededToLinkToThisCache.begin(), frameworksNeededToLinkToThisCache.end());
4601  }
4602  else
4603  {
4604  dylibsForCacheOfGeneratedModules.insert(dylibsNeededToLinkToThisCache.begin(), dylibsNeededToLinkToThisCache.end());
4605  frameworksForCacheOfGeneratedModules.insert(frameworksNeededToLinkToThisCache.begin(), frameworksNeededToLinkToThisCache.end());
4606  }
4607  }
4608  }
4609  });
4610 
4611  Environment::waitForModuleCachesToBuild();
4612 }
4613 
4622 bool VuoCompiler::findInModuleCache(const string &moduleOrDependency, string &cachePath, bool &isBuiltinCache)
4623 {
4624  __block bool found = false;
4625  __block string outPath;
4626  __block bool outBuiltin;
4627  dispatch_sync(environmentQueue, ^{
4628  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
4629  {
4630  bool builtin = (i == environments.begin());
4631 
4632  for (int j = i->size() - 1; j >= 0; --j)
4633  {
4634  Environment *env = i->at(j);
4635 
4636  string resultPath;
4637  bool resultFound = env->findInModuleCache(moduleOrDependency, resultPath);
4638  if (resultFound)
4639  {
4640  found = true;
4641  outPath = resultPath;
4642  outBuiltin = builtin;
4643  }
4644  }
4645  }
4646  });
4647 
4648  cachePath = outPath;
4649  isBuiltinCache = outBuiltin;
4650  return found;
4651 }
4652 
4661 {
4662  dispatch_group_async(moduleCacheBuilding, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
4663  useModuleCache(true, false);
4664  });
4665 }
4666 
4679 void VuoCompiler::generateBuiltInModuleCaches(const string &vuoFrameworkPath)
4680 {
4681  vuoFrameworkInProgressPath = vuoFrameworkPath;
4682 
4683  VuoCompiler compiler;
4684  compiler.useModuleCache(false, true);
4685 }
4686 
4695 {
4696  unsigned long maxSeconds = 30 * 24 * 60 * 60; // 30 days
4697 
4698  set<VuoFileUtilities::File *> cacheDirs = VuoFileUtilities::findAllFilesInDirectory(VuoFileUtilities::getCachePath());
4699  for (set<VuoFileUtilities::File *>::iterator i = cacheDirs.begin(); i != cacheDirs.end(); ++i)
4700  {
4701  string path = (*i)->path();
4702 
4703  string file = (*i)->basename();
4704  if (file != "Builtin" && file != "System" && file != "User")
4705  {
4706  unsigned long fileSeconds = VuoFileUtilities::getSecondsSinceFileLastAccessed(path);
4707  if (fileSeconds > maxSeconds)
4709  }
4710 
4711  if (VuoStringUtilities::beginsWith(file, Environment::pidCacheDirPrefix))
4712  {
4713  string pidAsString = file.substr(Environment::pidCacheDirPrefix.length());
4714  int pid = atoi(pidAsString.c_str());
4715  if (kill(pid, 0) != 0) // no running process has this pid
4717  }
4718 
4719  delete *i;
4720  }
4721 }
4722 
4727 void VuoCompiler::setLoadAllModules(bool shouldLoadAllModules)
4728 {
4729  dispatch_sync(modulesToLoadQueue, ^{
4730  this->shouldLoadAllModules = shouldLoadAllModules;
4731  });
4732 }
4733 
4745 void VuoCompiler::link(string outputPath, const set<Module *> &modules, const set<string> &libraries, const set<string> &frameworks, bool isDylib, string rPath)
4746 {
4747  VDebugLog("Linking '%s'…", outputPath.c_str());
4748  // https://stackoverflow.com/questions/11657529/how-to-generate-an-executable-from-an-llvmmodule
4749 
4750 
4751  // Write all the modules with renamed symbols to a composite module file (since the linker can't operate on in-memory modules).
4752  string compositeModulePath = VuoFileUtilities::makeTmpFile("composite", "bc");
4753  dispatch_sync(llvmQueue, ^{
4754  double t0 = VuoLogGetTime();
4755  Module *compositeModule = new Module("composite", getGlobalContext());
4756  setTargetForModule(compositeModule);
4757  for (set<Module *>::const_iterator i = modules.begin(); i != modules.end(); ++i)
4758  {
4759  string error;
4760  if (Linker::LinkModules(compositeModule, *i, Linker::PreserveSource, &error))
4761  VUserLog("Error: Failed to link compositeModule: %s", error.c_str());
4762  }
4763  writeModuleToBitcode(compositeModule, compositeModulePath);
4764  delete compositeModule;
4765  VDebugLog("\tLinkModules took %5.2fs", VuoLogGetTime() - t0);
4766  });
4767 
4768 
4769  // llvm-3.1/llvm/tools/clang/tools/driver/driver.cpp
4770 
4771  llvm::sys::Path clangPath = getClangPath();
4772 
4773  vector<const char *> args;
4774  vector<char *> argsToFree;
4775  args.push_back(clangPath.c_str());
4776 
4777  args.push_back(compositeModulePath.c_str());
4778 
4779  vector<string> coreDependencies = getCoreVuoDependencies();
4780  for (set<string>::const_iterator i = libraries.begin(); i != libraries.end(); ++i)
4781  {
4782  string library = *i;
4783 
4784  for (vector<string>::iterator j = coreDependencies.begin(); j != coreDependencies.end(); ++j)
4785  {
4786  string coreDependency = *j;
4787  if (VuoStringUtilities::endsWith(library, "lib" + coreDependency + ".a"))
4788  args.push_back("-force_load"); // Load all symbols of static core dependencies, not just those used in the objects.
4789  }
4790 
4791  // Use the pre-built native object file if it exists (faster than converting .bc to .o every build).
4792  if (VuoStringUtilities::endsWith(library, ".bc"))
4793  {
4794  string libraryObject = VuoStringUtilities::substrBefore(library, ".bc") + ".o";
4795  if (VuoFileUtilities::fileExists(libraryObject))
4796  library = libraryObject;
4797  }
4798 
4799  char *libraryZ = strdup(library.c_str());
4800  args.push_back(libraryZ);
4801  argsToFree.push_back(libraryZ);
4802  }
4803 
4804  // Add framework search paths
4805  vector<string> frameworkArguments;
4806 
4807  __block vector<string> frameworkSearchPaths;
4808  void (^envGetFrameworkSearchPaths)(Environment *) = ^void (Environment *env) {
4809  vector<string> result = env->getFrameworkSearchPaths();
4810  frameworkSearchPaths.insert(frameworkSearchPaths.end(), result.begin(), result.end());
4811  };
4812  applyToInstalledEnvironments(envGetFrameworkSearchPaths);
4813 
4814  for (vector<string>::const_iterator i = frameworkSearchPaths.begin(); i != frameworkSearchPaths.end(); ++i)
4815  {
4816  string a = "-F"+*i;
4817  // 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.
4818  frameworkArguments.push_back(a);
4819  char *frameworkArgument = strdup(a.c_str());
4820  args.push_back(frameworkArgument);
4821  argsToFree.push_back(frameworkArgument);
4822  }
4823 
4824  for (set<string>::const_iterator i = frameworks.begin(); i != frameworks.end(); ++i)
4825  {
4826  args.push_back("-framework");
4827 
4828  string frameworkName = *i;
4829  frameworkName = frameworkName.substr(0, frameworkName.length() - string(".framework").length());
4830  char *frameworkNameZ = strdup(frameworkName.c_str());
4831  args.push_back(frameworkNameZ);
4832  argsToFree.push_back(frameworkNameZ);
4833  }
4834 
4835  // Check for C Runtime path within Vuo.framework
4836  llvm::sys::Path cRuntimePath;
4837  llvm::sys::Path crt1Path;
4838  string vuoFrameworkPath = getVuoFrameworkPath();
4839  string vuoFrameworkContainingFolder = vuoFrameworkPath + "/..";
4840  if (! vuoFrameworkPath.empty())
4841  {
4842  cRuntimePath = vuoFrameworkPath + "/Modules/";
4843  crt1Path = cRuntimePath;
4844  crt1Path.appendComponent("crt1.o");
4845  }
4846 
4847  // If we have located a bundled version of crt1.o, link it in explicitly rather than relying on
4848  // clang's heuristic to locate a system version.
4849  if (!isDylib && crt1Path.canRead())
4850  {
4851  args.push_back("-nostartfiles");
4852  args.push_back(crt1Path.c_str());
4853  }
4854 
4855  // Linker option necessary for compatibility with our bundled version of ld64:
4856  args.push_back("-Xlinker");
4857  args.push_back("--no-demangle");
4858 
4859  if (isVerbose)
4860  args.push_back("-v");
4861 
4862  if (isDylib)
4863  args.push_back("-dynamiclib");
4864 
4865  args.push_back("-Xlinker");
4866  args.push_back("-headerpad_max_install_names");
4867 
4868  // Tell the built dylib/executable where to find Vuo.framework
4870  args.push_back("-rpath");
4871  string rPathArg = (rPath.empty() ? vuoFrameworkContainingFolder : rPath);
4872  args.push_back(rPathArg.c_str());
4873 
4874 #ifdef COVERAGE
4875  args.push_back("-rpath");
4876  args.push_back(LLVM_ROOT "/lib");
4877 #endif
4878 
4879  args.push_back("-std=c++11");
4880  args.push_back("-stdlib=libc++");
4881 
4882  // Allow clang to print meaningful error messages.
4883  clang::DiagnosticOptions *diagOptions = new clang::DiagnosticOptions();
4884  clang::TextDiagnosticPrinter *diagClient = new clang::TextDiagnosticPrinter(llvm::errs(), diagOptions);
4885  diagClient->setPrefix(clangPath.str());
4886  IntrusiveRefCntPtr<clang::DiagnosticIDs> DiagID(new clang::DiagnosticIDs());
4887  clang::DiagnosticsEngine Diags(DiagID, diagOptions, diagClient);
4888 
4889  clang::driver::Driver TheDriver(args[0], "x86_64-apple-macosx10.10.0", outputPath, Diags);
4890 
4891  TheDriver.CCCIsCXX = true; // clang++ instead of clang
4892 
4893  if (isVerbose)
4894  {
4895  ostringstream s;
4896  for (vector<const char *>::iterator i = args.begin(); i != args.end(); ++i)
4897  s << *i << " ";
4898  VUserLog("\t%s", s.str().c_str());
4899  }
4900 
4901  OwningPtr<clang::driver::Compilation> C(TheDriver.BuildCompilation(args));
4902 
4903  int Res = 0;
4904  if (C)
4905  {
4906  SmallVector<std::pair<int, const clang::driver::Command *>, 4> FailingCommands;
4907  double t0 = VuoLogGetTime();
4908  Res = TheDriver.ExecuteCompilation(*C, FailingCommands);
4909  VDebugLog("\tLinking took %5.2fs", VuoLogGetTime() - t0);
4910  }
4911  for (auto i : argsToFree)
4912  free(i);
4913 
4914  // Clean up composite module file.
4915  remove(compositeModulePath.c_str());
4916 
4917  if (!isDylib)
4918  // Ensure the linked binary has the execute permission set
4919  // (ld64-242 doesn't reliably set it, whereas ld64-133.3 did).
4920  // https://b33p.net/kosada/node/14152
4921  chmod(outputPath.c_str(), 0755);
4922 
4923  if (Res != 0)
4924  {
4925  __block vector<string> thirdPartyNodeClasses;
4926  dispatch_sync(environmentQueue, ^{
4927  for (size_t i = 1; i < environments.size(); ++i)
4928  {
4929  map<string, VuoCompilerNodeClass *> envNodeClasses = environments[i].at(0)->getNodeClasses();
4930  for (map<string, VuoCompilerNodeClass *>::iterator j = envNodeClasses.begin(); j != envNodeClasses.end(); ++j)
4931  thirdPartyNodeClasses.push_back(j->first);
4932  }
4933  });
4934 
4935  string details = "One or more nodes in this composition can't be used by this version of Vuo. ";
4936  if (! thirdPartyNodeClasses.empty())
4937  {
4938  details += "Make sure you're using the latest version of all the extra Vuo nodes you've installed:\n";
4939  sort(thirdPartyNodeClasses.begin(), thirdPartyNodeClasses.end());
4940  for (vector<string>::iterator i = thirdPartyNodeClasses.begin(); i != thirdPartyNodeClasses.end(); ++i)
4941  details += " • " + *i + "\n";
4942  }
4943  details += "Check the macOS Console for more information about the problem.";
4944 
4945  VuoCompilerIssue issue(VuoCompilerIssue::Error, "linking", outputPath,
4946  "Node broken or outdated", details);
4947  throw VuoCompilerException(issue);
4948  }
4949  VDebugLog("Done.");
4950 }
4951 
4957 Module * VuoCompiler::readModuleFromC(string inputPath, const vector<string> &headerSearchPaths, const vector<string> &extraArgs)
4958 {
4959  // llvm-3.1/llvm/tools/clang/examples/clang-interpreter/main.cpp
4960 
4961  vector<const char *> args;
4962  args.push_back(inputPath.c_str());
4963  args.push_back("-DVUO_COMPILER");
4964  args.push_back("-fblocks");
4965 
4966  // Sync with vuo.pri's WARNING_ADD.
4967  args.push_back("-Wall");
4968  args.push_back("-Wextra");
4969  args.push_back("-Wimplicit-fallthrough");
4970  args.push_back("-Wno-unused-parameter");
4971  args.push_back("-Wno-c++11-extensions");
4972  args.push_back("-Wno-sign-compare");
4973  args.push_back("-Werror=implicit");
4974 
4975  if (VuoStringUtilities::endsWith(inputPath, ".cc"))
4976  {
4977  args.push_back("-std=c++11");
4978  args.push_back("-stdlib=libc++");
4979  }
4980 
4981  for (vector<string>::const_iterator i = headerSearchPaths.begin(); i != headerSearchPaths.end(); ++i)
4982  {
4983  args.push_back("-I");
4984  args.push_back(i->c_str());
4985  }
4986 
4987  if (isVerbose)
4988  args.push_back("-v");
4989 
4990  for (vector<string>::const_iterator i = extraArgs.begin(); i != extraArgs.end(); ++i)
4991  args.push_back(i->c_str());
4992 
4993  clang::DiagnosticOptions * diagOptions = new clang::DiagnosticOptions();
4994  IntrusiveRefCntPtr<clang::DiagnosticIDs> DiagID(new clang::DiagnosticIDs());
4995  clang::DiagnosticsEngine Diags(DiagID, diagOptions);
4996 
4997  OwningPtr<clang::CompilerInvocation> CI(new clang::CompilerInvocation);
4998  clang::CompilerInvocation::CreateFromArgs(*CI, &args[0], &args[0] + args.size(), Diags);
4999 
5000  clang::CompilerInstance Clang;
5001  Clang.setInvocation(CI.take());
5002 
5003  Clang.createDiagnostics();
5004  if (!Clang.hasDiagnostics())
5005  return NULL;
5006 
5007  // See CompilerInvocation::GetResourcesPath -- though we're not calling it because we don't have MainAddr.
5008  llvm::sys::Path builtinHeaderSearchPath;
5009  string vuoFrameworkPath = getVuoFrameworkPath();
5010  if (vuoFrameworkPath.empty())
5011  {
5012  llvm::sys::Path clangPath = getClangPath();
5013  builtinHeaderSearchPath = clangPath;
5014  builtinHeaderSearchPath.eraseComponent(); // Remove /clang from foo/bin/clang
5015  builtinHeaderSearchPath.eraseComponent(); // Remove /bin from foo/bin
5016  builtinHeaderSearchPath.appendComponent("lib");
5017  builtinHeaderSearchPath.appendComponent("clang");
5018  builtinHeaderSearchPath.appendComponent(CLANG_VERSION_STRING); // foo/lib/clang/<version>
5019  }
5020  else
5021  {
5022  builtinHeaderSearchPath = vuoFrameworkPath;
5023  builtinHeaderSearchPath.appendComponent("Frameworks");
5024  builtinHeaderSearchPath.appendComponent("llvm.framework");
5025  builtinHeaderSearchPath.appendComponent("Versions");
5026  builtinHeaderSearchPath.appendComponent("A");
5027  builtinHeaderSearchPath.appendComponent("lib");
5028  builtinHeaderSearchPath.appendComponent("clang");
5029  builtinHeaderSearchPath.appendComponent(CLANG_VERSION_STRING); // foo/lib/clang/<version>
5030  }
5031  Clang.getHeaderSearchOpts().ResourceDir = builtinHeaderSearchPath.str();
5032 
5033 // OwningPtr<clang::CodeGenAction> Act(new clang::EmitLLVMOnlyAction()); // @@@ return value of takeModule() is destroyed at the end of this function
5034  clang::CodeGenAction *Act = new clang::EmitLLVMOnlyAction();
5035  if (!Clang.ExecuteAction(*Act))
5036  return NULL;
5037 
5038  return Act->takeModule();
5039 }
5040 
5046 Module * VuoCompiler::readModuleFromBitcode(string inputPath)
5047 {
5048  string dir, file, ext;
5049  VuoFileUtilities::splitPath(inputPath, dir, file, ext);
5050  VuoFileUtilities::File inputFile(dir, file + "." + ext);
5051  return readModuleFromBitcode(&inputFile);
5052 }
5053 
5061 Module * VuoCompiler::readModuleFromBitcode(VuoFileUtilities::File *inputFile)
5062 {
5063  size_t inputDataBytes;
5064  char *inputData = inputFile->getContentsAsRawData(inputDataBytes);
5065 
5066  string error;
5067  VuoLog_status("Loading module \"%s\"", inputFile->getRelativePath().c_str());
5068  Module *module = readModuleFromBitcodeData(inputData, inputDataBytes, error);
5069  VuoLog_status(NULL);
5070  if (! module)
5071  VUserLog("Error: Couldn't parse module '%s': %s.", inputFile->getRelativePath().c_str(), error.c_str());
5072 
5073  free(inputData);
5074 
5075  return module;
5076 }
5077 
5083 Module * VuoCompiler::readModuleFromBitcodeData(char *inputData, size_t inputDataBytes, string &error)
5084 {
5085  __block Module *module;
5086  dispatch_sync(llvmQueue, ^{
5087  StringRef inputDataAsStringRef(inputData, inputDataBytes);
5088  MemoryBuffer *mb = MemoryBuffer::getMemBuffer(inputDataAsStringRef, "", false);
5089  module = ParseBitcodeFile(&(*mb), getGlobalContext(), &error);
5090  delete mb;
5091  });
5092  return module;
5093 }
5094 
5102 bool VuoCompiler::writeModuleToBitcode(Module *module, string outputPath)
5103 {
5104  if (verifyModule(*module, PrintMessageAction))
5105  {
5106  VUserLog("Error: Module verification failed.");
5107  return true;
5108  }
5109 
5110  string err;
5111  raw_fd_ostream out(outputPath.c_str(), err);
5112  if (! err.empty())
5113  {
5114  VUserLog("Error: Couldn't open file '%s' for writing: %s", outputPath.c_str(), err.c_str());
5115  return true;
5116  }
5117  WriteBitcodeToFile(module, out);
5118 
5119  return false;
5120 }
5121 
5127 void VuoCompiler::setTargetForModule(Module *module, string target)
5128 {
5129 /*
5130  string effectiveTarget = target;
5131  if (effectiveTarget.empty())
5132  {
5133  // llvm::sys::getDefaultTargetTriple() finds a target based on the host, but the "default" target is not necessarily the
5134  // same target that results from invoking command-line clang without a -target argument. That is the "effective" target.
5135  // 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.
5136 
5137  llvm::sys::Path clangPath = getClangPath();
5138 
5139  vector<const char *> args;
5140  args.push_back(clangPath.c_str());
5141  args.push_back("/bin/sh"); // Driver needs an input file (that exists) or it refuses to give you the correct effective target.
5142 
5143  clang::DiagnosticOptions * diagOptions = new clang::DiagnosticOptions();
5144  IntrusiveRefCntPtr<clang::DiagnosticIDs> DiagID(new clang::DiagnosticIDs());
5145  clang::DiagnosticsEngine Diags(DiagID, diagOptions);
5146 
5147  clang::driver::Driver TheDriver(args[0], llvm::sys::getDefaultTargetTriple(), "a.out", true, Diags);
5148  OwningPtr<clang::driver::Compilation> C(TheDriver.BuildCompilation(args));
5149  effectiveTarget = C->getDefaultToolChain().ComputeEffectiveClangTriple(C->getArgs());
5150  }
5151 
5152  module->setTargetTriple(effectiveTarget);
5153 */
5154  module->setTargetTriple("x86_64-apple-macosx10.10.0");
5155 }
5156 
5164 {
5165  Module *llvmModule = module->getModule();
5166 
5167  // In C++ the return value of a cast may not be the same pointer as the cast arg.
5168  // Because of this, VuoModule::getPseudoBase() returns a different value than VuoNodeClass::getBase().
5169  // Calling delete on VuoModule::getPseudoBase() gives a malloc error: "pointer being freed was not allocated".
5170  // So call it on VuoNodeClass::getBase(). Same idea for VuoType.
5171 
5172  VuoNodeClass *baseNodeClass = NULL;
5173  VuoType *baseType = NULL;
5174  VuoModule *baseModule = NULL;
5175  VuoCompilerNodeClass *nodeClass = dynamic_cast<VuoCompilerNodeClass *>(module);
5176  if (nodeClass)
5177  {
5178  baseNodeClass = dynamic_cast<VuoNodeClass *>(nodeClass->getBase());
5179 
5181  if (dynamic_cast<VuoCompilerSpecializedNodeClass *>(module))
5182  module = NULL;
5183  }
5184  else
5185  {
5186  VuoCompilerType *type = dynamic_cast<VuoCompilerType *>(module);
5187  if (type)
5188  baseType = dynamic_cast<VuoType *>(type->getBase());
5189  else
5190  baseModule = module->getPseudoBase();
5191  }
5192 
5193  delete module;
5194  delete baseNodeClass;
5195  delete baseType;
5196  delete baseModule;
5197  destroyLlvmModule(llvmModule);
5198 }
5199 
5207 {
5208  dispatch_sync(llvmQueue, ^{
5209  delete module;
5210  });
5211 }
5212 
5224 VuoNode * VuoCompiler::createNode(VuoCompilerNodeClass *nodeClass, string title, double x, double y)
5225 {
5227  return nodeClassForNode->newNode(title, x, y);
5228 }
5229 
5233 VuoNode * VuoCompiler::createPublishedInputNode(vector<VuoPublishedPort *> publishedInputPorts)
5234 {
5235  string nodeClassName = VuoCompilerPublishedInputNodeClass::buildNodeClassName(publishedInputPorts);
5236  return createPublishedNode(nodeClassName, publishedInputPorts);
5237 }
5238 
5242 VuoNode * VuoCompiler::createPublishedOutputNode(vector<VuoPublishedPort *> publishedOutputPorts)
5243 {
5244  string nodeClassName = VuoCompilerPublishedOutputNodeClass::buildNodeClassName(publishedOutputPorts);
5245  return createPublishedNode(nodeClassName, publishedOutputPorts);
5246 }
5247 
5251 VuoNode * VuoCompiler::createPublishedNode(const string &nodeClassName, const vector<VuoPublishedPort *> &publishedPorts)
5252 {
5253  VuoCompilerNodeClass *nodeClass = getNodeClass(nodeClassName);
5254  VuoNode *node = createNode(nodeClass);
5255 
5256  // Set the generic port types on the node to match those on the published ports set by VuoCompilerComposition::updateGenericPortTypes().
5257  VuoCompilerPublishedInputNodeClass *inputNodeClass = dynamic_cast<VuoCompilerPublishedInputNodeClass *>(nodeClass);
5258  VuoCompilerPublishedOutputNodeClass *outputNodeClass = dynamic_cast<VuoCompilerPublishedOutputNodeClass *>(nodeClass);
5259  for (size_t i = 0; i < publishedPorts.size(); ++i)
5260  {
5261  VuoType *publishedPortType = static_cast<VuoCompilerPort *>(publishedPorts[i]->getCompiler())->getDataVuoType();
5262  if (! dynamic_cast<VuoGenericType *>(publishedPortType))
5263  continue;
5264 
5265  set<VuoPort *> nodePorts;
5266  if (inputNodeClass)
5267  {
5268  VuoPort *inputPort = node->getInputPorts().at( inputNodeClass->getInputPortIndexForPublishedInputPort(i) );
5269  nodePorts.insert(inputPort);
5270 
5271  VuoPort *outputPort = node->getOutputPorts().at( inputNodeClass->getOutputPortIndexForPublishedInputPort(i) );
5272  nodePorts.insert(outputPort);
5273  }
5274  else
5275  {
5276  VuoPort *inputPort = node->getInputPorts().at( outputNodeClass->getInputPortIndexForPublishedOutputPort(i) );
5277  nodePorts.insert(inputPort);
5278  }
5279 
5280  for (VuoPort *port : nodePorts)
5281  {
5282  VuoCompilerPort *compilerPort = static_cast<VuoCompilerPort *>(port->getCompiler());
5283  compilerPort->setDataVuoType(publishedPortType);
5284  }
5285  }
5286 
5287  reifyGenericPortTypes(node);
5288 
5289  return node;
5290 }
5291 
5297 {
5298  dispatch_sync(environmentQueue, ^{
5299  if (environments.size() >= 5)
5300  environments.at(3).at(0)->addExpatriateSourceFile(sourcePath);
5301  });
5302 }
5303 
5309 {
5310  dispatch_sync(environmentQueue, ^{
5311  if (environments.size() >= 5)
5312  {
5313  environments.at(3).at(0)->removeExpatriateSourceFile(sourcePath);
5314 
5315  set<string> sourcesRemoved;
5316  sourcesRemoved.insert(getModuleKeyForPath(sourcePath));
5317  loadModulesAndSources(set<string>(), set<string>(), set<string>(), set<string>(), set<string>(), sourcesRemoved,
5318  false, false, environments.at(3).at(0), nullptr, nullptr, "");
5319  }
5320  });
5321 }
5322 
5339 void VuoCompiler::overrideInstalledNodeClass(const string &sourcePath, const string &sourceCode)
5340 {
5341  string sourcePathCopy = sourcePath;
5342  string sourceCodeCopy = sourceCode;
5343 
5344  dispatch_async(environmentQueue, ^{
5345  string nodeClassName = getModuleKeyForPath(sourcePathCopy);
5346  ModuleInfo *sourceInfo = NULL;
5347 
5348  for (const vector<Environment *> &envs : environments)
5349  {
5350  for (Environment *env : envs)
5351  {
5352  ModuleInfo *potentialSourceInfo = env->listSourceFile(nodeClassName);
5353  if (potentialSourceInfo && VuoFileUtilities::arePathsEqual(potentialSourceInfo->getFile()->path(), sourcePathCopy))
5354  {
5355  sourceInfo = potentialSourceInfo;
5356  break;
5357  }
5358  }
5359  }
5360 
5361  if (! sourceInfo)
5362  return;
5363 
5364  bool shouldRecompileSourcesIfUnchanged;
5365  if (! sourceCodeCopy.empty())
5366  {
5367  sourceInfo->setSourceCode(sourceCodeCopy);
5368  sourceInfo->setSourceCodeOverridden(true);
5369 
5370  shouldRecompileSourcesIfUnchanged = false;
5371  }
5372  else
5373  {
5374  sourceInfo->revertSourceCode();
5375  sourceInfo->setSourceCodeOverridden(false);
5376 
5377  shouldRecompileSourcesIfUnchanged = true;
5378  }
5379  sourceInfo->setAttempted(false);
5380  sourceInfo->setLastModifiedToNow();
5381 
5382  set<string> sourcesModified;
5383  sourcesModified.insert(nodeClassName);
5384 
5385  loadModulesAndSources(set<string>(), set<string>(), set<string>(), set<string>(), sourcesModified, set<string>(),
5386  false, shouldRecompileSourcesIfUnchanged, nullptr, nullptr, nullptr, "");
5387  });
5388 }
5389 
5399 void VuoCompiler::revertOverriddenNodeClass(const string &sourcePath)
5400 {
5401  overrideInstalledNodeClass(sourcePath, "");
5402 }
5403 
5416 VuoCompilerNodeClass * VuoCompiler::getNodeClass(const string &nodeClassName)
5417 {
5418  // For performance, don't bother searching if it's obviously not a node class.
5419 
5420  if (VuoStringUtilities::endsWith(nodeClassName, ".framework"))
5421  return NULL;
5422 
5423  // Attempt to load the node class (if it hasn't already been loaded).
5424 
5425  set<string> nodeClassNameSet;
5426  nodeClassNameSet.insert(nodeClassName);
5427  loadModulesIfNeeded(nodeClassNameSet);
5428 
5429  // If the node class has been loaded, return it.
5430 
5431  __block VuoCompilerNodeClass *nodeClass = NULL;
5432  void (^envGetNodeClass)(Environment *) = ^void (Environment *env) {
5433  VuoCompilerNodeClass *result = env->getNodeClass(nodeClassName);
5434  if (result)
5435  nodeClass = result;
5436  };
5437  applyToAllEnvironments(envGetNodeClass);
5438  return nodeClass;
5439 }
5440 
5446 map<string, VuoCompilerNodeClass *> VuoCompiler::getNodeClasses()
5447 {
5448  loadModulesIfNeeded();
5449 
5450  __block map<string, VuoCompilerNodeClass *> nodeClasses;
5451  void (^envGetNodeClasses)(Environment *) = ^void (Environment *env) {
5452  map<string, VuoCompilerNodeClass *> result = env->getNodeClasses();
5453  nodeClasses.insert(result.begin(), result.end());
5454  };
5455  applyToInstalledEnvironments(envGetNodeClasses);
5456  return nodeClasses;
5457 }
5458 
5464 VuoCompilerType * VuoCompiler::getType(const string &typeName)
5465 {
5466  set<string> typeNameSet;
5467  typeNameSet.insert(typeName);
5468  loadModulesIfNeeded(typeNameSet);
5469 
5470  __block VuoCompilerType *type = NULL;
5471  void (^envGetType)(Environment *) = ^void (Environment *env) {
5472  VuoCompilerType *result = env->getType(typeName);
5473  if (result)
5474  type = result;
5475  };
5476  applyToInstalledEnvironments(envGetType);
5477 
5478  if (! type && VuoGenericType::isGenericTypeName(typeName))
5479  {
5480  VuoCompilerType * (^compilerGetType)(string) = ^VuoCompilerType * (string moduleKey) {
5481  return getType(moduleKey);
5482  };
5483 
5484  VuoGenericType *genericType = new VuoGenericType(typeName, vector<string>());
5485  type = VuoCompilerGenericType::newGenericType(genericType, compilerGetType);
5486  }
5487 
5488  return type;
5489 }
5490 
5496 map<string, VuoCompilerType *> VuoCompiler::getTypes()
5497 {
5498  loadModulesIfNeeded();
5499 
5500  __block map<string, VuoCompilerType *> types;
5501  void (^envGetTypes)(Environment *) = ^void (Environment *env) {
5502  map<string, VuoCompilerType *> result = env->getTypes();
5503  types.insert(result.begin(), result.end());
5504  };
5505  applyToInstalledEnvironments(envGetTypes);
5506  return types;
5507 }
5508 
5514 map<string, VuoCompilerModule *> VuoCompiler::getLibraryModules()
5515 {
5516  loadModulesIfNeeded();
5517 
5518  __block map<string, VuoCompilerModule *> libraryModules;
5519  void (^envGetLibraryModules)(Environment *) = ^void (Environment *env) {
5520  map<string, VuoCompilerModule *> result = env->getLibraryModules();
5521  libraryModules.insert(result.begin(), result.end());
5522  };
5523  applyToInstalledEnvironments(envGetLibraryModules);
5524  return libraryModules;
5525 }
5526 
5532 map<string, VuoNodeSet *> VuoCompiler::getNodeSets()
5533 {
5534  loadModulesIfNeeded();
5535 
5536  __block map<string, VuoNodeSet *> nodeSets;
5537  void (^envGetNodeSets)(Environment *) = ^void (Environment *env) {
5538  map<string, VuoNodeSet *> result = env->getNodeSets();
5539  nodeSets.insert(result.begin(), result.end());
5540  };
5541  applyToInstalledEnvironments(envGetNodeSets);
5542  return nodeSets;
5543 }
5544 
5551 {
5552  loadModulesIfNeeded();
5553 
5554  __block VuoNodeSet *nodeSet = NULL;
5555  void (^envFindNodeSet)(Environment *) = ^void (Environment *env) {
5556  VuoNodeSet *result = env->findNodeSet(name);
5557  if (result)
5558  nodeSet = result;
5559  };
5560  applyToInstalledEnvironments(envFindNodeSet);
5561  return nodeSet;
5562 }
5563 
5567 VuoCompilerModule * VuoCompiler::getModule(const string &moduleKey)
5568 {
5569  __block VuoCompilerModule *module = NULL;
5570  void (^envFindModule)(Environment *) = ^void (Environment *env) {
5571  VuoCompilerModule *result = env->findModule(moduleKey);
5572  if (result)
5573  module = result;
5574  };
5575  applyToAllEnvironments(envFindModule);
5576  return module;
5577 }
5578 
5590 void VuoCompiler::listNodeClasses(const string &format)
5591 {
5592  map<string, VuoCompilerNodeClass *> nodeClasses = getNodeClasses();
5593  for (map<string, VuoCompilerNodeClass *>::const_iterator i = nodeClasses.begin(); i != nodeClasses.end(); ++i)
5594  {
5595  VuoCompilerNodeClass *nodeClass = i->second;
5596  if (format == "")
5597  {
5598  printf("%s\n", nodeClass->getBase()->getClassName().c_str());
5599  }
5600  else if (format == "path")
5601  {
5602  // TODO: If "path", prints the absolute path of each node class, one per line.
5603  }
5604  else if (format == "dot")
5605  {
5606  VuoCompilerNode *node = nodeClass->newNode()->getCompiler();
5607 
5608  printf("%s\n", nodeClass->getDoxygenDocumentation().c_str());
5609  printf("%s\n\n", node->getGraphvizDeclaration().c_str());
5610 
5611  delete node;
5612  }
5613  }
5614 }
5615 
5622 vector<string> VuoCompiler::getCoreVuoDependencies(void)
5623 {
5624  vector<string> dependencies;
5625  dependencies.push_back("VuoCompositionDiff.o");
5626  dependencies.push_back("VuoHeap");
5627  dependencies.push_back("VuoException.o");
5628  dependencies.push_back("VuoNodeRegistry.o");
5629  dependencies.push_back("VuoRuntimeCommunicator.o");
5630  dependencies.push_back("VuoRuntimeContext.o");
5631  dependencies.push_back("VuoRuntimePersistentState.o");
5632  dependencies.push_back("VuoRuntimeState.o");
5633  dependencies.push_back("VuoTelemetry.o");
5634  dependencies.push_back("VuoThreadManager.o");
5635  dependencies.push_back("VuoApp");
5636  dependencies.push_back("zmq");
5637  dependencies.push_back("json-c");
5638  dependencies.push_back("objc");
5639  dependencies.push_back("c");
5640  dependencies.push_back("AppKit.framework");
5641 
5642 #ifdef COVERAGE
5643  dependencies.push_back(LLVM_ROOT "/lib/libprofile_rt.dylib");
5644 #endif
5645  return dependencies;
5646 }
5647 
5651 string VuoCompiler::getRuntimeMainDependency(void)
5652 {
5653  return "VuoRuntimeMain.o";
5654 }
5655 
5663 string VuoCompiler::getRuntimeDependency(void)
5664 {
5665  return "VuoRuntime.o";
5666 }
5667 
5674 string VuoCompiler::getVuoFrameworkPath(void)
5675 {
5676  if (! vuoFrameworkInProgressPath.empty())
5677  return vuoFrameworkInProgressPath;
5678 
5680 }
5681 
5685 llvm::sys::Path VuoCompiler::getClangPath(void)
5686 {
5687  return clangPath;
5688 }
5689 
5694 {
5695  // Start with the file name without extension.
5696  string dir, moduleKey, ext;
5697  VuoFileUtilities::splitPath(path, dir, moduleKey, ext);
5698 
5700  {
5701  vector<string> nodeClassNameParts = VuoStringUtilities::split(moduleKey, '.');
5702 
5703  // Remove repeated file extensions.
5704  while (nodeClassNameParts.size() > 1 && nodeClassNameParts.back() == ext)
5705  nodeClassNameParts.pop_back();
5706 
5707  // Convert each part to lowerCamelCase.
5708  for (string &part : nodeClassNameParts)
5709  part = VuoStringUtilities::convertToCamelCase(part, false, true, true);
5710 
5711  // If the node class name has only one part, add a prefix.
5712  if (nodeClassNameParts.size() == 1)
5713  nodeClassNameParts.insert(nodeClassNameParts.begin(), "isf");
5714 
5715  moduleKey = VuoStringUtilities::join(nodeClassNameParts, '.');
5716  }
5717 
5718  return moduleKey;
5719 }
5720 
5726 {
5727  __block bool isLocal = false;
5728 
5729  dispatch_sync(environmentQueue, ^{
5730  if (environments.size() >= 5)
5731  isLocal = environments.at(3).at(0)->findModule(moduleKey);
5732  });
5733 
5734  return isLocal;
5735 }
5736 
5744 {
5745  return lastCompositionBaseDir.empty() ? "" : lastCompositionBaseDir + "/Modules";
5746 }
5747 
5759 {
5760  return lastCompositionBaseDir;
5761 }
5762 
5766 void VuoCompiler::addModuleSearchPath(string path)
5767 {
5768  dispatch_sync(environmentQueue, ^{
5769  environments.back().at(0)->addModuleSearchPath(path);
5770  environments.back().at(0)->addLibrarySearchPath(path);
5771  });
5772 }
5773 
5777 void VuoCompiler::addHeaderSearchPath(const string &path)
5778 {
5779  dispatch_sync(environmentQueue, ^{
5780  environments.back().at(0)->addHeaderSearchPath(path);
5781  });
5782 }
5783 
5787 void VuoCompiler::addLibrarySearchPath(const string &path)
5788 {
5789  dispatch_sync(environmentQueue, ^{
5790  environments.back().at(0)->addLibrarySearchPath(path);
5791  });
5792 }
5793 
5797 void VuoCompiler::addFrameworkSearchPath(const string &path)
5798 {
5799  dispatch_sync(environmentQueue, ^{
5800  environments.back().at(0)->addFrameworkSearchPath(path);
5801  });
5802 }
5803 
5807 void VuoCompiler::setTelemetry(const string &telemetry)
5808 {
5809  this->telemetry = telemetry;
5810 }
5811 
5815 void VuoCompiler::setTarget(const string &target)
5816 {
5817  this->target = target;
5818 }
5819 
5823 void VuoCompiler::setVerbose(bool isVerbose)
5824 {
5825  this->isVerbose = isVerbose;
5826 }
5827 
5833 {
5834 #if VUO_PRO
5835  if (VuoPro::getProAccess())
5836  {
5837  _shouldShowSplashWindow = false;
5838  return;
5839  }
5840 #endif
5841 
5842  _shouldShowSplashWindow = potentiallyShow;
5843 }
5844 
5849 {
5850  return _shouldShowSplashWindow;
5851 }
5852 
5856 void VuoCompiler::setClangPath(const string &clangPath)
5857 {
5858  this->clangPath = llvm::sys::Path(StringRef(clangPath));
5859 }
5860 
5865 {
5866  string vuoFrameworkPath = getVuoFrameworkPath();
5867  return (vuoFrameworkPath.empty() ?
5868  VUO_BUILD_DIR "/bin/VuoCompositionLoader.app/Contents/MacOS/VuoCompositionLoader" :
5869  vuoFrameworkPath + "/Helpers/VuoCompositionLoader.app/Contents/MacOS/VuoCompositionLoader");
5870 }
5871 
5875 string VuoCompiler::getCompositionStubPath(void)
5876 {
5877  string vuoFrameworkPath = getVuoFrameworkPath();
5878  return (vuoFrameworkPath.empty() ?
5879  VUO_BUILD_DIR "/lib/libVuoCompositionStub.dylib" :
5880  vuoFrameworkPath + "/Modules/libVuoCompositionStub.dylib");
5881 }
5882 
5886 string VuoCompiler::getCachePathForComposition(const string compositionDir)
5887 {
5888  string modifierLetterColon("꞉");
5889  string cachedModulesName = compositionDir;
5890  VuoStringUtilities::replaceAll(cachedModulesName, "/", modifierLetterColon);
5891  return VuoFileUtilities::getCachePath() + "/" + cachedModulesName;
5892 }
5893 
5898 {
5899  __block vector<string> moduleSearchPaths;
5900  void (^envGetModuleSearchPaths)(Environment *) = ^void (Environment *env) {
5901  vector<string> result = env->getModuleSearchPaths();
5902  moduleSearchPaths.insert(moduleSearchPaths.end(), result.begin(), result.end());
5903  };
5904  applyToInstalledEnvironments(envGetModuleSearchPaths);
5905 
5906  __block vector<string> headerSearchPaths;
5907  void (^envGetHeaderSearchPaths)(Environment *) = ^void (Environment *env) {
5908  vector<string> result = env->getHeaderSearchPaths();
5909  headerSearchPaths.insert(headerSearchPaths.end(), result.begin(), result.end());
5910  };
5911  applyToInstalledEnvironments(envGetHeaderSearchPaths);
5912 
5913  __block vector<string> librarySearchPaths;
5914  void (^envGetLibrarySearchPaths)(Environment *) = ^void (Environment *env) {
5915  vector<string> result = env->getLibrarySearchPaths();
5916  librarySearchPaths.insert(librarySearchPaths.end(), result.begin(), result.end());
5917  };
5918  applyToInstalledEnvironments(envGetLibrarySearchPaths);
5919 
5920  __block vector<string> frameworkSearchPaths;
5921  void (^envGetFrameworkSearchPaths)(Environment *) = ^void (Environment *env) {
5922  vector<string> result = env->getFrameworkSearchPaths();
5923  frameworkSearchPaths.insert(frameworkSearchPaths.end(), result.begin(), result.end());
5924  };
5925  applyToInstalledEnvironments(envGetFrameworkSearchPaths);
5926 
5927  fprintf(stderr, "Module (node class, type, library) search paths:\n");
5928  for (vector<string>::iterator i = moduleSearchPaths.begin(); i != moduleSearchPaths.end(); ++i)
5929  fprintf(stderr, " %s\n", (*i).c_str());
5930  fprintf(stderr, "Header search paths:\n");
5931  for (vector<string>::iterator i = headerSearchPaths.begin(); i != headerSearchPaths.end(); ++i)
5932  fprintf(stderr, " %s\n", (*i).c_str());
5933  fprintf(stderr, "Other library search paths:\n");
5934  for (vector<string>::iterator i = librarySearchPaths.begin(); i != librarySearchPaths.end(); ++i)
5935  fprintf(stderr, " %s\n", (*i).c_str());
5936  fprintf(stderr, "Other framework search paths:\n");
5937  for (vector<string>::iterator i = frameworkSearchPaths.begin(); i != frameworkSearchPaths.end(); ++i)
5938  fprintf(stderr, " %s\n", (*i).c_str());
5939  fprintf(stderr, "Framework path:\n");
5940  if (! getVuoFrameworkPath().empty())
5941  fprintf(stderr, " %s\n", getVuoFrameworkPath().c_str());
5942  fprintf(stderr, "Clang path:\n");
5943  if (! getClangPath().str().empty())
5944  fprintf(stderr, " %s\n", getClangPath().c_str());
5945 }
5946 
5956 {
5957  try
5958  {
5959  VuoCompiler compiler(compositionFilePath);
5960  string directory, file, extension;
5961  VuoFileUtilities::splitPath(compositionFilePath, directory, file, extension);
5962  string compiledCompositionPath = VuoFileUtilities::makeTmpFile(file, "bc");
5963  string linkedCompositionPath = VuoFileUtilities::makeTmpFile(file + "-linked", "");
5964  compiler.compileComposition(compositionFilePath, compiledCompositionPath, true, issues);
5965  compiler.linkCompositionToCreateExecutable(compiledCompositionPath, linkedCompositionPath, Optimization_FastBuild);
5966  remove(compiledCompositionPath.c_str());
5967  return VuoRunner::newSeparateProcessRunnerFromExecutable(linkedCompositionPath, directory, false, true);
5968  }
5969  catch (VuoCompilerException &e)
5970  {
5971  if (issues != e.getIssues())
5972  issues->append(e.getIssues());
5973  return NULL;
5974  }
5975 }
5976 
5991 VuoRunner * VuoCompiler::newSeparateProcessRunnerFromCompositionString(string composition, string processName, string workingDirectory, VuoCompilerIssues *issues)
5992 {
5993  try
5994  {
5995  string compositionPath = (workingDirectory.empty() ? "." : workingDirectory) + "/" + processName + ".vuo";
5996  VuoCompiler compiler(compositionPath);
5997  string compiledCompositionPath = VuoFileUtilities::makeTmpFile(processName, "bc");
5998  string linkedCompositionPath = VuoFileUtilities::makeTmpFile(processName, "");
5999  compiler.compileCompositionString(composition, compiledCompositionPath, true, issues);
6000  compiler.linkCompositionToCreateExecutable(compiledCompositionPath, linkedCompositionPath, Optimization_FastBuild);
6001  remove(compiledCompositionPath.c_str());
6002  return VuoRunner::newSeparateProcessRunnerFromExecutable(linkedCompositionPath, workingDirectory, false, true);
6003  }
6004  catch (VuoCompilerException &e)
6005  {
6006  if (issues != e.getIssues())
6007  issues->append(e.getIssues());
6008  return NULL;
6009  }
6010 }
6011 
6021 {
6022  try
6023  {
6024  VuoCompiler compiler(compositionFilePath);
6025  string directory, file, extension;
6026  VuoFileUtilities::splitPath(compositionFilePath, directory, file, extension);
6027  string compiledCompositionPath = VuoFileUtilities::makeTmpFile(file, "bc");
6028  compiler.compileComposition(compositionFilePath, compiledCompositionPath, true, issues);
6029  string linkedCompositionPath = VuoFileUtilities::makeTmpFile(file, "dylib");
6030  compiler.linkCompositionToCreateDynamicLibrary(compiledCompositionPath, linkedCompositionPath, Optimization_FastBuild);
6031  remove(compiledCompositionPath.c_str());
6032  return VuoRunner::newCurrentProcessRunnerFromDynamicLibrary(linkedCompositionPath, directory, true);
6033  }
6034  catch (VuoCompilerException &e)
6035  {
6036  if (issues != e.getIssues())
6037  issues->append(e.getIssues());
6038  return NULL;
6039  }
6040 }
6041 
6056 VuoRunner * VuoCompiler::newCurrentProcessRunnerFromCompositionString(string composition, string workingDirectory, VuoCompilerIssues *issues)
6057 {
6058  try
6059  {
6060  string compositionPath = (workingDirectory.empty() ? "." : workingDirectory) + "/UntitledComposition.vuo";
6061  VuoCompiler compiler(compositionPath);
6062  string compiledCompositionPath = VuoFileUtilities::makeTmpFile("VuoRunnerComposition", "bc");
6063  compiler.compileCompositionString(composition, compiledCompositionPath, true, issues);
6064  string linkedCompositionPath = VuoFileUtilities::makeTmpFile("VuoRunnerComposition", "dylib");
6065  compiler.linkCompositionToCreateDynamicLibrary(compiledCompositionPath, linkedCompositionPath, Optimization_FastBuild);
6066  remove(compiledCompositionPath.c_str());
6067  return VuoRunner::newCurrentProcessRunnerFromDynamicLibrary(linkedCompositionPath, workingDirectory, true);
6068  }
6069  catch (VuoCompilerException &e)
6070  {
6071  if (issues != e.getIssues())
6072  issues->append(e.getIssues());
6073  return NULL;
6074  }
6075 }
6076 
6078 {
6079  pendingDataQueue = dispatch_queue_create("org.vuo.compiler.delegate.pending", 0);
6080 }
6081 
6083 {
6084  LoadedModulesData *data = dequeueData();
6085  data->release();
6086 }
6087 
6091 void VuoCompilerDelegate::enqueueData(LoadedModulesData *data)
6092 {
6093  dispatch_sync(pendingDataQueue, ^{
6094  pendingData.push_back(data);
6095  });
6096 }
6097 
6101 VuoCompilerDelegate::LoadedModulesData * VuoCompilerDelegate::dequeueData(void)
6102 {
6103  __block LoadedModulesData *ret;
6104  dispatch_sync(pendingDataQueue, ^{
6105  ret = pendingData.front();
6106  pendingData.pop_front();
6107  });
6108  return ret;
6109 }
6110 
6115 VuoCompilerDelegate::LoadedModulesData::LoadedModulesData(const set< pair<VuoCompilerModule *, VuoCompilerModule *> > &modulesModified,
6116  const set<VuoCompilerModule *> &modulesRemoved, VuoCompilerIssues *issues)
6117 {
6118  referenceCountQueue = dispatch_queue_create("org.vuo.compiler.delegate.reference", 0);
6119  referenceCount = 0;
6120 
6121  this->modulesModified = modulesModified;
6122  this->modulesRemoved = modulesRemoved;
6123  this->issues = issues;
6124 }
6125 
6129 VuoCompilerDelegate::LoadedModulesData::~LoadedModulesData(void)
6130 {
6131  delete issues;
6132 
6133  for (set< pair<VuoCompilerModule *, VuoCompilerModule *> >::iterator i = modulesModified.begin(); i != modulesModified.end(); ++i)
6134  VuoCompiler::destroyModule((*i).first);
6135 
6136  for (set<VuoCompilerModule *>::iterator i = modulesRemoved.begin(); i != modulesRemoved.end(); ++i)
6138 }
6139 
6143 void VuoCompilerDelegate::LoadedModulesData::retain(void)
6144 {
6145  dispatch_sync(referenceCountQueue, ^{
6146  ++referenceCount;
6147  });
6148 }
6149 
6153 void VuoCompilerDelegate::LoadedModulesData::release(void)
6154 {
6155  dispatch_sync(referenceCountQueue, ^{
6156  --referenceCount;
6157  if (referenceCount == 0) {
6158  delete this;
6159  }
6160  });
6161 }