Vuo  2.3.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"
21 #include "VuoCompilerException.hh"
23 #include "VuoCompilerGraph.hh"
25 #include "VuoCompilerIssue.hh"
26 #include "VuoCompilerNode.hh"
27 #include "VuoCompilerPort.hh"
28 #include "VuoCompilerPortClass.hh"
32 #include "VuoComposition.hh"
33 #include "VuoException.hh"
34 #include "VuoGenericType.hh"
35 #include "VuoModuleCompiler.hh"
37 #include "VuoNode.hh"
38 #include "VuoNodeClass.hh"
39 #include "VuoNodeSet.hh"
40 #include "VuoPublishedPort.hh"
41 #include "VuoRunner.hh"
43 #include "VuoStringUtilities.hh"
44 
45 #if VUO_PRO
46 #include "VuoPro.hh"
47 #endif
48 
49 map<string, VuoFileUtilities::File *> VuoCompiler::Environment::moduleCacheFileForLocking;
50 dispatch_queue_t VuoCompiler::Environment::moduleCacheBuildingQueue = dispatch_queue_create("org.vuo.compiler.cache", NULL);
51 const string VuoCompiler::Environment::pidCacheDirPrefix = "pid-";
52 set<VuoCompiler *> VuoCompiler::allCompilers;
53 dispatch_queue_t VuoCompiler::environmentQueue = dispatch_queue_create("org.vuo.compiler.environment", NULL);
54 map<string, vector< vector<VuoCompiler::Environment *> > > VuoCompiler::sharedEnvironments;
55 map<string, vector<VuoCompiler::Environment *> > VuoCompiler::environmentsForCompositionFamily;
56 dispatch_group_t VuoCompiler::moduleSourceCompilersExistGlobally = dispatch_group_create();
57 string VuoCompiler::vuoFrameworkInProgressPath;
58 
59 dispatch_queue_t llvmQueue = NULL;
60 
61 llvm::LLVMContext *VuoCompiler::globalLLVMContext = nullptr;
62 
66 static void __attribute__((constructor)) VuoCompiler_init(void)
67 {
68  // Increase the open-files limit (macOS defaults to 256).
69  struct rlimit rl{OPEN_MAX, OPEN_MAX};
70  getrlimit(RLIMIT_NOFILE, &rl);
71  rl.rlim_cur = MIN(OPEN_MAX, rl.rlim_max);
72  if (setrlimit(RLIMIT_NOFILE, &rl))
73  VUserLog("Warning: Couldn't set open-files limit: %s", strerror(errno));
74 
75 
76  llvmQueue = dispatch_queue_create("org.vuo.compiler.llvm", NULL);
77 
78  dispatch_sync(llvmQueue, ^{
79  // llvm::InitializeNativeTarget();
80  llvm::InitializeAllTargetMCs();
81  llvm::InitializeAllTargets();
82  // TargetRegistry::printRegisteredTargetsForVersion();
83 
84  VuoCompiler::globalLLVMContext = new llvm::LLVMContext;
85 
86  // If the Vuo compiler/linker...
87  // 1. Loads a node class that uses dispatch_object_t.
88  // 2. Generates code that uses dispatch_object_t.
89  // 3. Links the node class into a composition.
90  // 4. Generates more code that uses dispatch_object_t.
91  // ... then Step 4 ends up with the wrong llvm::Type for dispatch_object_t.
92  //
93  // A workaround is to generate some code that uses dispatch_object_t before doing Step 1.
94  //
95  // https://b33p.net/kosada/node/3845
96  // http://lists.cs.uiuc.edu/pipermail/llvmdev/2012-December/057075.html
97  Module module("", *VuoCompiler::globalLLVMContext);
99 
100  // Workaround for a possibly related error where the compiler ends up with the wrong
101  // llvm::Type for dispatch_semaphore_s. https://b33p.net/kosada/node/10545
103 
104  // Load the NodeContext and PortContext struct types preemptively to make sure that
105  // their fields get the right dispatch types. If these struct types were to be loaded
106  // first from a subcomposition module, they'd get the wrong dispatch types.
107  // https://b33p.net/kosada/node/11160
110  });
111 }
112 
116 VuoCompiler::ModuleInfo::ModuleInfo(Environment *environment, const string &searchPath, const string &relativePath,
117  bool isSourceFile, bool isSubcomposition)
118 {
119  VuoFileUtilities::File *file = new VuoFileUtilities::File(searchPath, relativePath);
120  initialize(environment, searchPath, file, isSourceFile, isSubcomposition);
121 }
122 
126 VuoCompiler::ModuleInfo::ModuleInfo(Environment *environment, const string &searchPath, VuoFileUtilities::File *file,
127  bool isSourceFile, bool isSubcomposition)
128 {
129  initialize(environment, searchPath, file, isSourceFile, isSubcomposition);
130 }
131 
135 void VuoCompiler::ModuleInfo::initialize(Environment *environment, const string &searchPath, VuoFileUtilities::File *file,
136  bool isSourceFile, bool isSubcomposition)
137 {
138  this->environment = environment;
139  this->searchPath = searchPath;
140  this->file = file;
141  moduleKey = getModuleKeyForPath(file->getRelativePath());
142  sourceCodeOverridden = false;
143  lastModified = VuoFileUtilities::getFileLastModifiedInSeconds(file->isInArchive() ? file->getArchivePath() : file->path());
144  longestDownstreamPath = 0;
145  attempted = false;
146  loading = nullptr;
147 
148 #if VUO_PRO
149  init_Pro();
150 #endif
151 
152  if (isSourceFile)
153  {
154  loading = dispatch_group_create();
155  sourceCode = file->getContentsAsString();
156 
157  if (isSubcomposition)
158  {
159  try
160  {
162  }
163  catch (...)
164  {
165  // Issues parsing the composition will be caught later when compiling it.
166  }
167  }
168  }
169 }
170 
171 VuoCompiler::ModuleInfo::~ModuleInfo(void)
172 {
173 #if VUO_PRO
174  fini_Pro();
175 #endif
176 
177  delete file;
178 
179  if (loading)
180  dispatch_release(loading);
181 }
182 
183 VuoCompiler::Environment * VuoCompiler::ModuleInfo::getEnvironment(void) const
184 {
185  return environment;
186 }
187 
188 string VuoCompiler::ModuleInfo::getSearchPath(void) const
189 {
190  return searchPath;
191 }
192 
193 string VuoCompiler::ModuleInfo::getModuleKey(void) const
194 {
195  return moduleKey;
196 }
197 
198 VuoFileUtilities::File * VuoCompiler::ModuleInfo::getFile(void) const
199 {
200  return file;
201 }
202 
203 string VuoCompiler::ModuleInfo::getSourceCode(void) const
204 {
205  return sourceCode;
206 }
207 
208 void VuoCompiler::ModuleInfo::setSourceCode(const string &sourceCode)
209 {
210  this->sourceCode = sourceCode;
211 }
212 
213 void VuoCompiler::ModuleInfo::revertSourceCode(void)
214 {
215  sourceCode = file->getContentsAsString();
216 }
217 
218 bool VuoCompiler::ModuleInfo::isSourceCodeOverridden(void) const
219 {
220  return sourceCodeOverridden;
221 }
222 
223 void VuoCompiler::ModuleInfo::setSourceCodeOverridden(bool overridden)
224 {
225  sourceCodeOverridden = overridden;
226 }
227 
228 bool VuoCompiler::ModuleInfo::isNewerThan(ModuleInfo *other) const
229 {
230  return lastModified > other->lastModified;
231 }
232 
233 bool VuoCompiler::ModuleInfo::isNewerThan(unsigned long ageInSeconds) const
234 {
235  return lastModified > ageInSeconds;
236 }
237 
238 bool VuoCompiler::ModuleInfo::isOlderThan(unsigned long ageInSeconds) const
239 {
240  return lastModified < ageInSeconds;
241 }
242 
243 void VuoCompiler::ModuleInfo::setLastModifiedToNow(void)
244 {
245  struct timeval t;
246  gettimeofday(&t, NULL);
247  lastModified = t.tv_sec;
248 }
249 
250 set<string> VuoCompiler::ModuleInfo::getContainedNodeClasses(void) const
251 {
252  return containedNodeClasses;
253 }
254 
255 int VuoCompiler::ModuleInfo::getLongestDownstreamPath(void) const
256 {
257  return longestDownstreamPath;
258 }
259 
260 void VuoCompiler::ModuleInfo::setLongestDownstreamPath(int pathLength)
261 {
262  longestDownstreamPath = pathLength;
263 }
264 
265 bool VuoCompiler::ModuleInfo::getAttempted(void) const
266 {
267  return attempted;
268 }
269 
270 void VuoCompiler::ModuleInfo::setAttempted(bool attempted)
271 {
272  this->attempted = attempted;
273 }
274 
275 dispatch_group_t VuoCompiler::ModuleInfo::getLoadingGroup(void) const
276 {
277  return loading;
278 }
279 
280 void VuoCompiler::ModuleInfo::dump() const
281 {
282  fprintf(stderr, "module %s:\n"
283  "\tloadingGroup=%p\n"
284  "\tsearchPath=%s\n"
285  "\tattempted=%d\n"
286  "\tenvironment=%s\n"
287  "\tfile=%s%s%s\n"
288  "\tsourceCodeOverridden=%d\n"
289  "\tsourceCode=%s\n"
290  "\tcontainedNodeClasses:\n",
291  moduleKey.c_str(),
292  loading,
293  searchPath.c_str(),
294  attempted,
295  environment->getCompiledModuleCachePath().c_str(),
296  file->isInArchive() ? file->getArchivePath().c_str() : "",
297  file->isInArchive() ? "::" : "",
298  file->isInArchive() ? file->getRelativePath().c_str() : file->path().c_str(),
299  sourceCodeOverridden,
300  sourceCode.c_str());
301  for (auto i: containedNodeClasses)
302  fprintf(stderr, "\t\t%s\n", i.c_str());
303 }
304 
305 VuoCompiler::DependencyGraphVertex * VuoCompiler::DependencyGraphVertex::vertexForDependency(const string &dependency, VuoDirectedAcyclicGraph *graph)
306 {
307  VuoDirectedAcyclicGraph::Vertex *v = graph->findVertex(dependency);
308  if (v)
309  return dynamic_cast<DependencyGraphVertex *>(v);
310 
311  DependencyGraphVertex *vv = new DependencyGraphVertex();
312  vv->dependency = dependency;
313  vv->environment = NULL;
314  vv->compatible = true;
315  return vv;
316 }
317 
318 string VuoCompiler::DependencyGraphVertex::getDependency(void)
319 {
320  return dependency;
321 }
322 
323 VuoCompiler::Environment * VuoCompiler::DependencyGraphVertex::getEnvironment(void)
324 {
325  return environment;
326 }
327 
328 void VuoCompiler::DependencyGraphVertex::setEnvironment(Environment *environment)
329 {
330  this->environment = environment;
331 }
332 
333 bool VuoCompiler::DependencyGraphVertex::isCompatible(void)
334 {
335  return compatible;
336 }
337 
338 void VuoCompiler::DependencyGraphVertex::setCompatible(bool compatible)
339 {
340  this->compatible = compatible;
341 }
342 
343 string VuoCompiler::DependencyGraphVertex::key(void)
344 {
345  return dependency;
346 }
347 
353 VuoCompiler::ModuleInfoIterator::ModuleInfoIterator(map<string, map<string, ModuleInfo *> > *allModuleInfos)
354 {
355  this->allModuleInfos = allModuleInfos;
356  hasSearchModuleKeys = false;
357  initialize();
358 }
359 
365 VuoCompiler::ModuleInfoIterator::ModuleInfoIterator(map<string, map<string, ModuleInfo *> > *allModuleInfos, const set<string> &searchModuleKeys)
366 {
367  this->allModuleInfos = allModuleInfos;
368  this->searchModuleKeys = searchModuleKeys;
369  hasSearchModuleKeys = true;
370  initialize();
371 }
372 
378 void VuoCompiler::ModuleInfoIterator::initialize(void)
379 {
380  currSearchPath = allModuleInfos->begin();
381  hasCurrModuleKey = false;
382 }
383 
389 VuoCompiler::ModuleInfo * VuoCompiler::ModuleInfoIterator::next(void)
390 {
391  for ( ; currSearchPath != allModuleInfos->end(); ++currSearchPath)
392  {
393  if (! hasCurrModuleKey)
394  {
395  currModuleKey = currSearchPath->second.begin();
396  hasCurrModuleKey = true;
397  }
398 
399  for ( ; currModuleKey != currSearchPath->second.end(); ++currModuleKey)
400  {
401  if (! hasSearchModuleKeys || searchModuleKeys.find(currModuleKey->first) != searchModuleKeys.end())
402  {
403  ModuleInfo *moduleInfo = currModuleKey->second;
404  ++currModuleKey;
405  return moduleInfo;
406  }
407  }
408 
409  hasCurrModuleKey = false;
410  }
411 
412  return NULL;
413 }
414 
418 VuoCompiler::Environment::Environment(string target, bool builtIn, bool generated)
419  : target(target), builtIn(builtIn), generated(generated)
420 {
421  compilersToNotifyQueue = dispatch_queue_create("org.vuo.compiler.notify", 0);
422  moduleSearchPathContentsChangedQueue = dispatch_queue_create("org.vuo.compiler.watch", 0);
423  lastModuleCacheRebuild = 0;
424  isModuleCacheableDataDirty = false;
425  isModuleCacheInitialized = false;
426  isModuleCacheAvailable = false;
427  dependencyGraph = new VuoDirectedAcyclicGraph();
428  compositionDependencyGraph = new VuoDirectedAcyclicGraph();
429  moduleCompilationQueue = new VuoModuleCompilationQueue();
430 }
431 
435 VuoCompiler::Environment::~Environment(void)
436 {
437  stopWatchingModuleSearchPaths();
438  dispatch_sync(moduleSearchPathContentsChangedQueue, ^{});
439 
440  dispatch_release(moduleSearchPathContentsChangedQueue);
441  dispatch_release(compilersToNotifyQueue);
442 
443  for (map<string, VuoCompilerNodeClass *>::iterator i = nodeClasses.begin(); i != nodeClasses.end(); ++i)
444  destroyModule(i->second);
445 
446  for (map<string, VuoCompilerType *>::iterator i = types.begin(); i != types.end(); ++i)
447  destroyModule(i->second);
448 
449  for (map<string, VuoCompilerModule *>::iterator i = libraryModules.begin(); i != libraryModules.end(); ++i)
450  destroyModule(i->second);
451 
452  for (set<VuoCompilerGenericType *>::iterator i = genericTypes.begin(); i != genericTypes.end(); ++i)
453  {
454  VuoType *base = (*i)->getBase();
455  delete *i;
456  delete base;
457  }
458 
459  for (map<string, VuoNodeSet *>::iterator i = nodeSetForName.begin(); i != nodeSetForName.end(); ++i)
460  delete i->second;
461 
462  for (map<string, map<string, ModuleInfo *> >::iterator i = moduleFilesAtSearchPath.begin(); i != moduleFilesAtSearchPath.end(); ++i)
463  for (map<string, ModuleInfo *>::iterator j = i->second.begin(); j != i->second.end(); ++j)
464  delete j->second;
465 
466  for (map<string, map<string, ModuleInfo *> >::iterator i = sourceFilesAtSearchPath.begin(); i != sourceFilesAtSearchPath.end(); ++i)
467  for (map<string, ModuleInfo *>::iterator j = i->second.begin(); j != i->second.end(); ++j)
468  delete j->second;
469 
470  delete dependencyGraph;
471  delete compositionDependencyGraph;
472 }
473 
477 string VuoCompiler::Environment::getTarget()
478 {
479  return target;
480 }
481 
485 void VuoCompiler::Environment::addCompilerToNotify(VuoCompiler *compiler)
486 {
487  dispatch_sync(compilersToNotifyQueue, ^{
488  compilersToNotify.insert(compiler);
489  });
490 }
491 
495 void VuoCompiler::Environment::removeCompilerToNotify(VuoCompiler *compiler)
496 {
497  dispatch_sync(compilersToNotifyQueue, ^{
498  compilersToNotify.erase(compiler);
499  });
500 }
501 
507 map<string, VuoCompilerNodeClass *> VuoCompiler::Environment::getNodeClasses(void)
508 {
509  return nodeClasses;
510 }
511 
517 VuoCompilerNodeClass * VuoCompiler::Environment::getNodeClass(const string &moduleKey)
518 {
519  map<string, VuoCompilerNodeClass *>::iterator nodeClassIter = nodeClasses.find(moduleKey);
520  if (nodeClassIter != nodeClasses.end())
521  return nodeClassIter->second;
522 
523  return NULL;
524 }
525 
530 VuoCompilerNodeClass * VuoCompiler::Environment::takeNodeClass(const string &moduleKey)
531 {
532  VuoCompilerNodeClass *nodeClass = NULL;
533 
534  map<string, VuoCompilerNodeClass *>::iterator nodeClassIter = nodeClasses.find(moduleKey);
535  if (nodeClassIter != nodeClasses.end())
536  {
537  nodeClass = nodeClassIter->second;
538 
539  nodeClasses.erase(nodeClassIter);
540  removeFromDependencyGraph(nodeClass);
541  modulesChanged();
542  }
543 
544  return nodeClass;
545 }
546 
552 map<string, VuoCompilerType *> VuoCompiler::Environment::getTypes(void)
553 {
554  return types;
555 }
556 
562 VuoCompilerType * VuoCompiler::Environment::getType(const string &moduleKey)
563 {
564  map<string, VuoCompilerType *>::iterator typeIter = types.find(moduleKey);
565  if (typeIter != types.end())
566  return typeIter->second;
567 
568  return NULL;
569 }
570 
576 map<string, VuoNodeSet *> VuoCompiler::Environment::getNodeSets(void)
577 {
578  return nodeSetForName;
579 }
580 
586 VuoCompilerModule *VuoCompiler::Environment::getLibraryModule(const string &libraryModuleName)
587 {
588  map<string, VuoCompilerModule *>::iterator libraryIter = libraryModules.find(libraryModuleName);
589  if (libraryIter != libraryModules.end())
590  return libraryIter->second;
591 
592  return nullptr;
593 }
594 
600 map<string, VuoCompilerModule *> VuoCompiler::Environment::getLibraryModules(void)
601 {
602  return libraryModules;
603 }
604 
611 VuoCompilerModule * VuoCompiler::Environment::findModule(const string &moduleKey)
612 {
613  map<string, VuoCompilerNodeClass *>::iterator nodeClassIter = nodeClasses.find(moduleKey);
614  if (nodeClassIter != nodeClasses.end())
615  return nodeClassIter->second;
616 
617  map<string, VuoCompilerType *>::iterator typeIter = types.find(moduleKey);
618  if (typeIter != types.end())
619  return typeIter->second;
620 
621  map<string, VuoCompilerModule *>::iterator libraryIter = libraryModules.find(moduleKey);
622  if (libraryIter != libraryModules.end())
623  return libraryIter->second;
624 
625  return NULL;
626 }
627 
633 VuoNodeSet * VuoCompiler::Environment::findNodeSet(const string &name)
634 {
635 
636  map<string, VuoNodeSet *>::iterator nodeSetIter = nodeSetForName.find(name);
637  if (nodeSetIter != nodeSetForName.end())
638  return nodeSetIter->second;
639 
640  return NULL;
641 }
642 
649 void VuoCompiler::Environment::addModuleSearchPath(const string &path, bool shouldWatch)
650 {
651  moduleSearchPaths.push_back(path);
652 
653  updateModulesAtSearchPath(path);
654  updateSourceFilesAtSearchPath(path);
655 
656  if (shouldWatch)
657  startWatchingModuleSearchPath(path);
658 }
659 
665 vector<string> VuoCompiler::Environment::getModuleSearchPaths(void)
666 {
667  return moduleSearchPaths;
668 }
669 
675 void VuoCompiler::Environment::addHeaderSearchPath(const string &path)
676 {
677  headerSearchPaths.push_back(path);
678 }
679 
685 vector<string> VuoCompiler::Environment::getHeaderSearchPaths(void)
686 {
687  return headerSearchPaths;
688 }
689 
695 void VuoCompiler::Environment::addLibrarySearchPath(const string &path)
696 {
697  librarySearchPaths.push_back(path);
698 }
699 
705 vector<string> VuoCompiler::Environment::getLibrarySearchPaths(void)
706 {
707  return librarySearchPaths;
708 }
709 
715 void VuoCompiler::Environment::addFrameworkSearchPath(const string &path)
716 {
717  frameworkSearchPaths.push_back(path);
718 }
719 
725 vector<string> VuoCompiler::Environment::getFrameworkSearchPaths(void)
726 {
727  return frameworkSearchPaths;
728 }
729 
730 VuoDirectedAcyclicGraph * VuoCompiler::Environment::getDependencyGraph(void)
731 {
732  return dependencyGraph;
733 }
734 
735 VuoDirectedAcyclicGraph * VuoCompiler::Environment::getCompositionDependencyGraph(void)
736 {
737  return compositionDependencyGraph;
738 }
739 
745 void VuoCompiler::Environment::setModuleCachePath(const string &path)
746 {
747  moduleCachePath = path;
748 }
749 
755 string VuoCompiler::Environment::getCompiledModuleCachePath(void)
756 {
757  return (moduleCachePath.empty() ? "" : moduleCachePath + "/Modules");
758 }
759 
765 string VuoCompiler::Environment::getOverriddenCompiledModuleCachePath(void)
766 {
767  if (moduleCachePath.empty())
768  return "";
769 
770  ostringstream pid;
771  pid << getpid();
772 
773  string dir, moduleCacheDirName, ext;
774  VuoFileUtilities::splitPath(moduleCachePath, dir, moduleCacheDirName, ext);
775 
776  return (VuoFileUtilities::getCachePath() + "/" + pidCacheDirPrefix + pid.str() + "/" + moduleCacheDirName + "/Modules");
777 }
778 
786 void VuoCompiler::Environment::addExpatriateSourceFile(const string &sourcePath)
787 {
788  expatriateSourceFiles.push_back(sourcePath);
789 
790  if (find(moduleSearchPaths.begin(), moduleSearchPaths.end(), "") == moduleSearchPaths.end())
791  moduleSearchPaths.push_back("");
792 
793  auto iter = sourceFilesAtSearchPath.find("");
794  if (iter != sourceFilesAtSearchPath.end())
795  sourceFilesAtSearchPath.erase(iter);
796 }
797 
803 void VuoCompiler::Environment::removeExpatriateSourceFile(const string &sourcePath)
804 {
805  for (auto i = expatriateSourceFiles.begin(); i != expatriateSourceFiles.end(); ++i)
806  {
807  if (VuoFileUtilities::arePathsEqual(*i, sourcePath))
808  {
809  expatriateSourceFiles.erase(i);
810 
811  auto iter = sourceFilesAtSearchPath.find("");
812  if (iter != sourceFilesAtSearchPath.end())
813  sourceFilesAtSearchPath.erase(iter);
814 
815  break;
816  }
817  }
818 }
819 
832 void VuoCompiler::Environment::updateModulesAtSearchPath(const string &path)
833 {
834  if (moduleFilesAtSearchPath.find(path) != moduleFilesAtSearchPath.end())
835  return;
836 
837  set<string> moduleExtensions;
838  moduleExtensions.insert("vuonode");
839  moduleExtensions.insert("vuonode+");
840  moduleExtensions.insert("bc");
841  moduleExtensions.insert("bc+");
842  set<string> archiveExtensions;
843  archiveExtensions.insert("vuonode");
844  set<VuoFileUtilities::File *> moduleFiles = VuoFileUtilities::findFilesInDirectory(path, moduleExtensions, archiveExtensions);
845 
846  map<string, ModuleInfo *> fileForModuleKey;
847  for (set<VuoFileUtilities::File *>::iterator i = moduleFiles.begin(); i != moduleFiles.end(); ++i)
848  {
849  VuoFileUtilities::File *moduleFile = *i;
850 
851  // Ignore macOS extended attribute storage (a.k.a. xattr, resource fork).
852  if (VuoStringUtilities::beginsWith(moduleFile->basename(), "._"))
853  continue;
854 
855  ModuleInfo *m = new ModuleInfo(this, path, moduleFile, false, false);
856  fileForModuleKey[m->getModuleKey()] = m;
857  }
858 
859  if (path == getCompiledModuleCachePath())
860  {
861  for (map<string, ModuleInfo *>::iterator i = fileForModuleKey.begin(); i != fileForModuleKey.end(); )
862  {
863  ModuleInfo *sourceInfo = listSourceFile(i->first);
864  if (! sourceInfo || sourceInfo->isNewerThan(i->second))
865  {
866  ModuleInfo *m = i->second;
867  VuoFileUtilities::deleteFile(m->getFile()->path());
868  delete m;
869  fileForModuleKey.erase(i++);
870  }
871  else
872  ++i;
873  }
874  }
875 
876  moduleFilesAtSearchPath[path] = fileForModuleKey;
877 }
878 
884 void VuoCompiler::Environment::updateModuleAtSearchPath(const string &moduleSearchPath, const string &moduleRelativePath)
885 {
886  string dir, file, ext;
887  VuoFileUtilities::splitPath(moduleRelativePath, dir, file, ext);
888 
889  set<string> moduleExtensions;
890  moduleExtensions.insert(ext);
891  set<string> archiveExtensions;
892  archiveExtensions.insert("vuonode");
893 
894  set<VuoFileUtilities::File *> moduleFiles = VuoFileUtilities::findFilesInDirectory(moduleSearchPath, moduleExtensions, archiveExtensions);
895 
896  for (set<VuoFileUtilities::File *>::iterator i = moduleFiles.begin(); i != moduleFiles.end(); ++i)
897  {
898  VuoFileUtilities::File *moduleFile = *i;
899 
900  ModuleInfo *m = new ModuleInfo(this, moduleSearchPath, moduleFile, false, false);
901  moduleFilesAtSearchPath[moduleSearchPath][m->getModuleKey()] = m;
902  }
903 }
904 
912 void VuoCompiler::Environment::updateSourceFilesAtSearchPath(const string &path)
913 {
914  map<string, map<string, ModuleInfo *> >::iterator sourceFilesIter = sourceFilesAtSearchPath.find(path);
915  if (sourceFilesIter != sourceFilesAtSearchPath.end())
916  return;
917 
918  set<VuoFileUtilities::File *> sourceFiles;
919  if (! path.empty())
920  {
921  set<string> sourceExtensions;
922  sourceExtensions.insert("vuo");
923  sourceExtensions.insert("fs");
925  sourceExtensions.insert(cext.begin(), cext.end());
926  sourceFiles = VuoFileUtilities::findFilesInDirectory(path, sourceExtensions, set<string>());
927  }
928  else
929  {
930  for (const string &sourcePath : expatriateSourceFiles)
931  {
932  string dir, file, ext;
933  VuoFileUtilities::splitPath(sourcePath, dir, file, ext);
934  VuoFileUtilities::File *sourceFile = new VuoFileUtilities::File(dir, file + "." + ext);
935  sourceFiles.insert(sourceFile);
936  }
937  }
938 
939  map<string, ModuleInfo *> fileForModuleKey;
940  for (set<VuoFileUtilities::File *>::iterator i = sourceFiles.begin(); i != sourceFiles.end(); ++i)
941  {
942  VuoFileUtilities::File *sourceFile = *i;
943 
944  string dir, moduleKey, ext;
945  VuoFileUtilities::splitPath(sourceFile->getRelativePath(), dir, moduleKey, ext);
946  bool isSubcomposition = (ext == "vuo");
947 
948  // Ignore missing expatriateSourceFiles — they might have been deleted in the meantime.
949  if (path.empty() && !sourceFile->exists())
950  continue;
951 
952  ModuleInfo *m = new ModuleInfo(this, path, sourceFile, true, isSubcomposition);
953  if (fileForModuleKey.find(m->getModuleKey()) != fileForModuleKey.end())
954  VUserLog("Warning: Conflicting source files for module %s are installed at %s", m->getModuleKey().c_str(), path.c_str());
955  fileForModuleKey[m->getModuleKey()] = m;
956 
957  if (isSubcomposition)
958  {
959  DependencyGraphVertex *compositionVertex = DependencyGraphVertex::vertexForDependency(moduleKey, compositionDependencyGraph);
960  compositionDependencyGraph->addVertex(compositionVertex);
961 
962  compositionVertex->setEnvironment(this);
963 
964  set<string> dependencies = m->getContainedNodeClasses();
965  for (set<string>::iterator j = dependencies.begin(); j != dependencies.end(); ++j)
966  {
967  DependencyGraphVertex *dependencyVertex = DependencyGraphVertex::vertexForDependency(*j, compositionDependencyGraph);
968  compositionDependencyGraph->addEdge(compositionVertex, dependencyVertex);
969  }
970  }
971  }
972 
973  sourceFilesAtSearchPath[path] = fileForModuleKey;
974 }
975 
981 VuoCompiler::ModuleInfo * VuoCompiler::Environment::listModule(const string &moduleKey)
982 {
983  for (const auto &moduleFiles : moduleFilesAtSearchPath)
984  {
985  map<string, ModuleInfo *>::const_iterator foundIter = moduleFiles.second.find(moduleKey);
986  if (foundIter != moduleFiles.second.end())
987  return foundIter->second;
988  }
989 
990  return NULL;
991 }
992 
998 VuoCompiler::ModuleInfoIterator VuoCompiler::Environment::listModules(const set<string> &moduleKeys)
999 {
1000  for (const string &path : moduleSearchPaths)
1001  updateModulesAtSearchPath(path);
1002 
1003  return ModuleInfoIterator(&moduleFilesAtSearchPath, moduleKeys);
1004 }
1005 
1011 VuoCompiler::ModuleInfoIterator VuoCompiler::Environment::listAllModules(void)
1012 {
1013  for (const string &path : moduleSearchPaths)
1014  updateModulesAtSearchPath(path);
1015 
1016  return ModuleInfoIterator(&moduleFilesAtSearchPath);
1017 }
1018 
1024 VuoCompiler::ModuleInfo * VuoCompiler::Environment::listSourceFile(const string &moduleKey)
1025 {
1026  for (const auto &sourceFiles : sourceFilesAtSearchPath)
1027  {
1028  map<string, ModuleInfo *>::const_iterator foundIter = sourceFiles.second.find(moduleKey);
1029  if (foundIter != sourceFiles.second.end())
1030  return foundIter->second;
1031  }
1032 
1033  return NULL;
1034 }
1035 
1041 VuoCompiler::ModuleInfoIterator VuoCompiler::Environment::listSourceFiles(const set<string> &moduleKeys)
1042 {
1043  for (const string &path : moduleSearchPaths)
1044  updateSourceFilesAtSearchPath(path);
1045 
1046  return ModuleInfoIterator(&sourceFilesAtSearchPath, moduleKeys);
1047 }
1048 
1054 VuoCompiler::ModuleInfoIterator VuoCompiler::Environment::listAllSourceFiles(void)
1055 {
1056  for (const string &path : moduleSearchPaths)
1057  updateSourceFilesAtSearchPath(path);
1058 
1059  return ModuleInfoIterator(&sourceFilesAtSearchPath);
1060 }
1061 
1065 vector<string> VuoCompiler::Environment::getBuiltInModuleSearchPaths(void)
1066 {
1067  vector<string> builtInModuleSearchPaths;
1068 
1069  string vuoFrameworkPath = getVuoFrameworkPath();
1070  if (! vuoFrameworkPath.empty())
1071  {
1072  builtInModuleSearchPaths.push_back(vuoFrameworkPath + "/Modules");
1073  }
1074  else
1075  {
1076  builtInModuleSearchPaths.push_back(VUO_BUILD_DIR "/library");
1077  builtInModuleSearchPaths.push_back(VUO_BUILD_DIR "/node");
1078  builtInModuleSearchPaths.push_back(VUO_BUILD_DIR "/type");
1079  builtInModuleSearchPaths.push_back(VUO_BUILD_DIR "/type/list");
1080  }
1081 
1082  return builtInModuleSearchPaths;
1083 }
1084 
1088 vector<string> VuoCompiler::Environment::getBuiltInHeaderSearchPaths(void)
1089 {
1090  vector<string> builtInHeaderSearchPaths;
1091 
1092  string vuoFrameworkPath = getVuoFrameworkPath();
1093  if (! vuoFrameworkPath.empty())
1094  {
1095  builtInHeaderSearchPaths.push_back(vuoFrameworkPath + "/Headers");
1096  builtInHeaderSearchPaths.push_back(vuoFrameworkPath + "/Headers/macos"); // system headers installed by Xcode Command Line Tools
1097  builtInHeaderSearchPaths.push_back(vuoFrameworkPath + "/Headers/macos/pthread"); //
1098  builtInHeaderSearchPaths.push_back(vuoFrameworkPath + "/Frameworks/llvm.framework/Versions/A/Headers/lib/c++/v1");
1099  }
1100  else
1101  {
1102  builtInHeaderSearchPaths.push_back(VUO_SOURCE_DIR "/library");
1103  builtInHeaderSearchPaths.push_back(VUO_SOURCE_DIR "/node");
1104  builtInHeaderSearchPaths.push_back(VUO_SOURCE_DIR "/type");
1105  builtInHeaderSearchPaths.push_back(VUO_SOURCE_DIR "/type/list");
1106  builtInHeaderSearchPaths.push_back(VUO_SOURCE_DIR "/runtime");
1107  builtInHeaderSearchPaths.push_back(JSONC_ROOT "/include");
1108  builtInHeaderSearchPaths.push_back(LLVM_ROOT "/include/c++/v1");
1109  }
1110 
1111  return builtInHeaderSearchPaths;
1112 }
1113 
1117 vector<string> VuoCompiler::Environment::getBuiltInLibrarySearchPaths(void)
1118 {
1119  vector<string> builtInLibrarySearchPaths;
1120 
1121  string vuoFrameworkPath = getVuoFrameworkPath();
1122  if (! vuoFrameworkPath.empty())
1123  {
1124  builtInLibrarySearchPaths.push_back(vuoFrameworkPath + "/Modules");
1125 
1126  // Ensure we (statically) link to our OpenSSL build when generating Vuo.framework's built-in module cache.
1127  builtInLibrarySearchPaths.push_back(OPENSSL_ROOT "/lib");
1128  }
1129  else
1130  {
1131  builtInLibrarySearchPaths.push_back(VUO_BUILD_DIR "/library");
1132  builtInLibrarySearchPaths.push_back(VUO_BUILD_DIR "/node");
1133  builtInLibrarySearchPaths.push_back(VUO_BUILD_DIR "/type");
1134  builtInLibrarySearchPaths.push_back(VUO_BUILD_DIR "/type/list");
1135  builtInLibrarySearchPaths.push_back(VUO_BUILD_DIR "/runtime");
1136 
1137  vector<string> conanLibDirs = VuoStringUtilities::split(CONAN_LIBRARY_PATHS, ';');
1138  builtInLibrarySearchPaths.insert(builtInLibrarySearchPaths.end(), conanLibDirs.begin(), conanLibDirs.end());
1139  }
1140 
1141  return builtInLibrarySearchPaths;
1142 }
1143 
1147 vector<string> VuoCompiler::Environment::getBuiltInFrameworkSearchPaths(void)
1148 {
1149  vector<string> builtInFrameworkSearchPaths;
1150 
1151  string vuoFrameworkPath = getVuoFrameworkPath();
1152  if (! vuoFrameworkPath.empty())
1153  {
1154  builtInFrameworkSearchPaths.push_back(vuoFrameworkPath + "/Modules/");
1155  builtInFrameworkSearchPaths.push_back(vuoFrameworkPath + "/Frameworks/");
1156  }
1157 
1158  return builtInFrameworkSearchPaths;
1159 }
1160 
1167 void VuoCompiler::Environment::startWatchingModuleSearchPath(const string &moduleSearchPath)
1168 {
1169  VuoFileWatcher *watcher = new VuoFileWatcher(this, moduleSearchPath);
1170  moduleSearchPathWatchers.insert(watcher);
1171 }
1172 
1178 void VuoCompiler::Environment::stopWatchingModuleSearchPaths(void)
1179 {
1180  for (set<VuoFileWatcher *>::iterator i = moduleSearchPathWatchers.begin(); i != moduleSearchPathWatchers.end(); ++i)
1181  delete *i;
1182 
1183  moduleSearchPathWatchers.clear();
1184 }
1185 
1190 void VuoCompiler::Environment::fileChanged(const string &moduleSearchPath)
1191 {
1192  dispatch_sync(moduleSearchPathContentsChangedQueue, ^{
1193  moduleSearchPathContentsChanged(moduleSearchPath);
1194  });
1195 }
1196 
1204 void VuoCompiler::Environment::moduleSearchPathContentsChanged(const string &moduleSearchPath)
1205 {
1206  //VLog(" E=%p -- %s", this, moduleSearchPath.c_str());
1207  VuoCompiler *compilerForLoading = new VuoCompiler(moduleSearchPath + "/unused");
1208 
1209  __block set<string> modulesAdded;
1210  __block set<string> modulesModifed;
1211  __block set<string> modulesRemoved;
1212  __block set<string> compositionsAdded;
1213  __block set<string> compositionsModifed;
1214  __block set<string> compositionsRemoved;
1215 
1216  dispatch_sync(environmentQueue, ^{
1217 
1218  // Remove the old file records from the environment.
1219 
1220  map<string, ModuleInfo *> oldModules;
1221  map<string, ModuleInfo *> oldCompositions;
1222 
1223  map<string, map<string, ModuleInfo *> >::iterator mf = moduleFilesAtSearchPath.find(moduleSearchPath);
1224  if (mf != moduleFilesAtSearchPath.end())
1225  {
1226  for (map<string, ModuleInfo *>::iterator j = mf->second.begin(); j != mf->second.end(); ++j)
1227  {
1228  oldModules[j->first] = j->second;
1229  }
1230 
1231  moduleFilesAtSearchPath.erase(mf);
1232  }
1233 
1234  map<string, map<string, ModuleInfo *> >::iterator cf = sourceFilesAtSearchPath.find(moduleSearchPath);
1235  if (cf != sourceFilesAtSearchPath.end())
1236  {
1237  for (map<string, ModuleInfo *>::iterator j = cf->second.begin(); j != cf->second.end(); ++j)
1238  {
1239  oldCompositions[j->first] = j->second;
1240 
1241  VuoDirectedAcyclicGraph::Vertex *vertex = compositionDependencyGraph->findVertex(j->first);
1242  compositionDependencyGraph->removeVertex(vertex);
1243  }
1244 
1245  sourceFilesAtSearchPath.erase(cf);
1246  }
1247 
1248  // Rebuild the file records based on the directory contents.
1249 
1250  updateModulesAtSearchPath(moduleSearchPath);
1251  updateSourceFilesAtSearchPath(moduleSearchPath);
1252 
1253  // Compare the old and new file records to see what has changed.
1254 
1255  mf = moduleFilesAtSearchPath.find(moduleSearchPath);
1256  if (mf != moduleFilesAtSearchPath.end())
1257  {
1258  for (map<string, ModuleInfo *>::iterator n = mf->second.begin(); n != mf->second.end(); ++n)
1259  {
1260  string moduleKey = n->first;
1261 
1262  map<string, ModuleInfo *>::iterator o = oldModules.find(moduleKey);
1263  if (o != oldModules.end())
1264  {
1265  if (n->second->isNewerThan(o->second))
1266  {
1267  modulesModifed.insert(moduleKey);
1268  }
1269  else
1270  {
1271  n->second->setAttempted(o->second->getAttempted());
1272  }
1273 
1274  delete o->second;
1275  oldModules.erase(o);
1276  }
1277  else
1278  {
1279  modulesAdded.insert(moduleKey);
1280  }
1281  }
1282  }
1283 
1284  cf = sourceFilesAtSearchPath.find(moduleSearchPath);
1285  if (cf != sourceFilesAtSearchPath.end())
1286  {
1287  for (map<string, ModuleInfo *>::iterator n = cf->second.begin(); n != cf->second.end(); ++n)
1288  {
1289  string moduleKey = n->first;
1290 
1291  map<string, ModuleInfo *>::iterator o = oldCompositions.find(moduleKey);
1292  if (o != oldCompositions.end())
1293  {
1294  if (n->second->isNewerThan(o->second))
1295  {
1296  compositionsModifed.insert(moduleKey);
1297  }
1298  else
1299  {
1300  n->second->setAttempted(o->second->getAttempted());
1301  n->second->setSourceCode(o->second->getSourceCode());
1302  }
1303 
1304  delete o->second;
1305  oldCompositions.erase(o);
1306  }
1307  else
1308  {
1309  compositionsAdded.insert(moduleKey);
1310  }
1311  }
1312  }
1313 
1314  for (map<string, ModuleInfo *>::iterator o = oldModules.begin(); o != oldModules.end(); ++o)
1315  {
1316  delete o->second;
1317  modulesRemoved.insert(o->first);
1318  }
1319 
1320  for (map<string, ModuleInfo *>::iterator o = oldCompositions.begin(); o != oldCompositions.end(); ++o)
1321  {
1322  delete o->second;
1323  compositionsRemoved.insert(o->first);
1324  }
1325 
1326  compilerForLoading->loadModulesAndSources(modulesAdded, modulesModifed, modulesRemoved,
1327  compositionsAdded, compositionsModifed, compositionsRemoved,
1328  false, false, this, nullptr, nullptr, "");
1329  });
1330 
1331  delete compilerForLoading;
1332 }
1333 
1340 void VuoCompiler::Environment::moduleFileChanged(const string &modulePath, const string &moduleSourceCode,
1341  std::function<void(void)> moduleLoadedCallback,
1342  VuoCompiler *compiler, VuoCompilerIssues *issues)
1343 {
1344  //VLog(" E=%p -- %s", this, modulePath.c_str());
1345  dispatch_sync(environmentQueue, ^{
1346 
1347  string moduleDir, moduleKey, ext;
1348  VuoFileUtilities::splitPath(modulePath, moduleDir, moduleKey, ext);
1350 
1351  // Remove the old file record from the environment.
1352 
1353  bool foundOldModule = false;
1354  auto moduleSearchPathIter = moduleFilesAtSearchPath.find(moduleDir);
1355  if (moduleSearchPathIter != moduleFilesAtSearchPath.end())
1356  {
1357  auto moduleIter = moduleSearchPathIter->second.find(moduleKey);
1358  if (moduleIter != moduleSearchPathIter->second.end())
1359  {
1360  delete moduleIter->second;
1361  moduleSearchPathIter->second.erase(moduleIter);
1362  foundOldModule = true;
1363  }
1364  }
1365 
1366  // Update the file record for the module by re-checking the file.
1367 
1368  updateModuleAtSearchPath(moduleDir, moduleKey + "." + ext);
1369 
1370  // Compare the old and new file records to see how the module has changed.
1371 
1372  bool foundNewModule = false;
1373  moduleSearchPathIter = moduleFilesAtSearchPath.find(moduleDir);
1374  if (moduleSearchPathIter != moduleFilesAtSearchPath.end())
1375  {
1376  auto moduleIter = moduleSearchPathIter->second.find(moduleKey);
1377  if (moduleIter != moduleSearchPathIter->second.end())
1378  {
1379  foundNewModule = true;
1380  }
1381  }
1382 
1383  set<string> modulesAdded;
1384  set<string> modulesModified;
1385  set<string> modulesRemoved;
1386 
1387  if ((foundOldModule || VuoFileUtilities::arePathsEqual(moduleDir, getOverriddenCompiledModuleCachePath())) && foundNewModule)
1388  {
1389  modulesModified.insert(moduleKey);
1390  }
1391  else if (! foundOldModule && foundNewModule)
1392  {
1393  modulesAdded.insert(moduleKey);
1394  }
1395  else if (foundOldModule && ! foundNewModule)
1396  {
1397  modulesRemoved.insert(moduleKey);
1398  }
1399 
1400  compiler->loadModulesAndSources(modulesAdded, modulesModified, modulesRemoved,
1401  set<string>(), set<string>(), set<string>(),
1402  false, false, this, issues, moduleLoadedCallback, moduleSourceCode);
1403  });
1404 
1405 }
1406 
1410 void VuoCompiler::Environment::notifyCompilers(const set<VuoCompilerModule *> &modulesAdded,
1411  const set< pair<VuoCompilerModule *, VuoCompilerModule *> > &modulesModified,
1412  const set<VuoCompilerModule *> &modulesRemoved, VuoCompilerIssues *issues,
1413  bool oldModulesInvalidated)
1414 {
1415  if (! (modulesAdded.empty() && modulesModified.empty() && modulesRemoved.empty() && issues->isEmpty()) )
1416  {
1417  set< pair<VuoCompilerModule *, VuoCompilerModule *> > invalidatedModulesModified;
1418  set<VuoCompilerModule *> invalidatedModulesRemoved;
1419  if (oldModulesInvalidated)
1420  {
1421  invalidatedModulesModified = modulesModified;
1422  invalidatedModulesRemoved = modulesRemoved;
1423  }
1424 
1425  VuoCompilerDelegate::LoadedModulesData *delegateData = new VuoCompilerDelegate::LoadedModulesData(invalidatedModulesModified, invalidatedModulesRemoved, issues);
1426 
1427  map<string, VuoCompilerModule *> modulesAddedMap;
1428  map<string, pair<VuoCompilerModule *, VuoCompilerModule *> > modulesModifiedMap;
1429  map<string, VuoCompilerModule *> modulesRemovedMap;
1430  for (VuoCompilerModule *m : modulesAdded)
1431  modulesAddedMap[m->getPseudoBase()->getModuleKey()] = m;
1432  for (pair<VuoCompilerModule *, VuoCompilerModule *> m : modulesModified)
1433  modulesModifiedMap[m.first->getPseudoBase()->getModuleKey()] = m;
1434  for (VuoCompilerModule *m : modulesRemoved)
1435  modulesRemovedMap[m->getPseudoBase()->getModuleKey()] = m;
1436 
1437  dispatch_sync(compilersToNotifyQueue, ^{
1438  for (set<VuoCompiler *>::iterator i = compilersToNotify.begin(); i != compilersToNotify.end(); ++i) {
1439  delegateData->retain();
1440  }
1441  for (set<VuoCompiler *>::iterator i = compilersToNotify.begin(); i != compilersToNotify.end(); ++i) {
1442  (*i)->loadedModules(modulesAddedMap, modulesModifiedMap, modulesRemovedMap, issues, delegateData, this);
1443  }
1444  });
1445  }
1446  else
1447  {
1448  delete issues;
1449  }
1450 }
1451 
1462 set<VuoCompilerModule *> VuoCompiler::Environment::loadCompiledModules(const set<string> &moduleKeys, const map<string, string> &sourceCodeForModule)
1463 {
1464  ModuleInfoIterator modulesToLoadIter = listModules(moduleKeys);
1465  set<VuoCompilerModule *> modulesLoaded;
1466 
1467  ModuleInfo *moduleInfo;
1468  while ((moduleInfo = modulesToLoadIter.next()))
1469  {
1470  string moduleKey = moduleInfo->getModuleKey();
1471 
1472  // Skip the module if its file is not in this environment, if the module has already been loaded,
1473  // or if the compiler previously tried to load the module and it failed.
1474  if (moduleInfo->getEnvironment() != this || findModule(moduleKey) || moduleInfo->getAttempted())
1475  continue;
1476 
1477  moduleInfo->setAttempted(true);
1478 
1479  // Actually load the module.
1480  VuoCompilerModule *module = loadModule(moduleInfo);
1481 
1482  if (module)
1483  {
1484  modulesLoaded.insert(module);
1485  addToDependencyGraph(module);
1486  modulesChanged();
1487 
1488  // For a compiled subcomposition or other source file, store its source code in the VuoCompilerNodeClass.
1489  string searchPath = moduleInfo->getSearchPath();
1490  if (VuoFileUtilities::arePathsEqual(searchPath, getCompiledModuleCachePath()) ||
1491  VuoFileUtilities::arePathsEqual(searchPath, getOverriddenCompiledModuleCachePath()))
1492  {
1493  VuoCompilerNodeClass *nodeClass = dynamic_cast<VuoCompilerNodeClass *>(module);
1494  if (nodeClass)
1495  {
1496  ModuleInfo *sourceInfo = listSourceFile(moduleKey);
1497  if (sourceInfo)
1498  nodeClass->setSourcePath( sourceInfo->getFile()->path() );
1499 
1500  auto sourceCodeIter = sourceCodeForModule.find(moduleKey);
1501  if (sourceCodeIter != sourceCodeForModule.end())
1502  nodeClass->setSourceCode( sourceCodeIter->second );
1503  }
1504  }
1505  }
1506  }
1507 
1508  return modulesLoaded;
1509 }
1510 
1522 set<dispatch_group_t> VuoCompiler::Environment::loadSpecializedModules(const set<string> &moduleKeys,
1523  VuoCompiler *compiler, dispatch_queue_t llvmQueue)
1524 {
1525  set<dispatch_group_t > loadingGroups;
1526 
1527  for (string moduleKey : moduleKeys)
1528  {
1529  // Skip if it's not a node class.
1530 
1531  if (moduleKey.find(".") == string::npos || VuoStringUtilities::endsWith(moduleKey, ".framework"))
1532  continue;
1533 
1534  // Skip the module if it's already been loaded.
1535 
1536  if (findModule(moduleKey))
1537  continue;
1538 
1539  // Skip the module if it's in the process of being loaded, but have the caller wait for it to finish loading.
1540 
1541  map<string, dispatch_group_t>::iterator iter = specializedModulesLoading.find(moduleKey);
1542  if (iter != specializedModulesLoading.end())
1543  {
1544  loadingGroups.insert(iter->second);
1545  dispatch_retain(iter->second);
1546  continue;
1547  }
1548 
1549  dispatch_group_t loadingGroup = dispatch_group_create();
1550  specializedModulesLoading[moduleKey] = loadingGroup;
1551  loadingGroups.insert(loadingGroup);
1552 
1553  // Generate the module.
1554  // This is done asynchronously since VuoCompilerSpecializedNodeClass::newNodeClass() needs to use environmentQueue
1555  // to compile the generated C code.
1556 
1557  dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
1558  dispatch_group_async(loadingGroup, queue, ^{
1559  VuoNodeClass *baseNodeClass = nullptr;
1560  try
1561  {
1562  baseNodeClass = VuoCompilerSpecializedNodeClass::newNodeClass(moduleKey, compiler, llvmQueue);
1563  }
1564  catch (VuoCompilerException &e)
1565  {
1566  VUserLog("Error: %s", e.what());
1567  }
1568 
1569  if (baseNodeClass)
1570  {
1571  VuoCompilerSpecializedNodeClass *specNodeClass = static_cast<VuoCompilerSpecializedNodeClass *>(baseNodeClass->getCompiler());
1572  compiler->loadNodeClassGeneratedAtRuntime(specNodeClass, this);
1573 
1574  set<string> dependencies = specNodeClass->getDependencies();
1575  if (!dependencies.empty())
1576  compiler->loadModulesIfNeeded(dependencies);
1577  }
1578 
1579  dispatch_sync(environmentQueue, ^{
1580  specializedModulesLoading.erase(moduleKey);
1581  });
1582  });
1583  }
1584 
1585  return loadingGroups;
1586 }
1587 
1600 set<dispatch_group_t> VuoCompiler::Environment::compileModulesFromSourceCode(const set<string> &moduleKeys, bool shouldRecompileIfUnchanged)
1601 {
1602  ModuleInfoIterator modulesToLoadIter = listSourceFiles(moduleKeys);
1603 
1604  int environmentIndex = sharedEnvironments[target].size();
1605  for (int i = 0; i < sharedEnvironments[target].size(); ++i)
1606  if (this == sharedEnvironments[target].at(i).at(0))
1607  environmentIndex = i;
1608 
1609  set<dispatch_group_t> sourcesLoading;
1610  int sourcesEnqueued = 0;
1611  ModuleInfo *sourceInfo;
1612  while ((sourceInfo = modulesToLoadIter.next()))
1613  {
1614  string moduleKey = sourceInfo->getModuleKey();
1615 
1616  dispatch_group_t loadingGroup = sourceInfo->getLoadingGroup();
1617  sourcesLoading.insert(loadingGroup);
1618 
1619  // Skip compiling if the source file has already been scheduled for compilation.
1620  // Either its compilation is in progress or it failed to compile.
1621 
1622  if (sourceInfo->getAttempted())
1623  continue;
1624 
1625  // Skip compiling if the compiled module is up-to-date.
1626 
1627  string sourceCode = sourceInfo->getSourceCode();
1628 
1629  if (! shouldRecompileIfUnchanged)
1630  {
1631  VuoCompilerNodeClass *existingNodeClass = getNodeClass(moduleKey);
1632  if (existingNodeClass && existingNodeClass->getSourceCode() == sourceCode)
1633  continue;
1634  }
1635 
1636  // Enqueue the source file to be compiled.
1637 
1638  sourceInfo->setAttempted(true);
1639 
1640  dispatch_group_enter(loadingGroup);
1641  dispatch_group_enter(moduleSourceCompilersExistGlobally);
1642 
1644  queueItem->moduleKey = moduleKey;
1645  queueItem->sourcePath = sourceInfo->getFile()->path();
1646  queueItem->sourceCode = sourceCode;
1647  queueItem->sourceFile = sourceInfo->getFile();
1648  queueItem->cachedModulesPath = sourceInfo->isSourceCodeOverridden() ? getOverriddenCompiledModuleCachePath() : getCompiledModuleCachePath();
1649  queueItem->compiledModulePath = queueItem->cachedModulesPath + "/" + moduleKey + ".vuonode";
1650  queueItem->loadingGroup = loadingGroup;
1651  queueItem->priority = { environmentIndex, sourceInfo->getLongestDownstreamPath() };
1652  moduleCompilationQueue->enqueue(queueItem);
1653  ++sourcesEnqueued;
1654  }
1655 
1656  // Compile each enqueued source file. This is done asynchronously for several reasons:
1657  // - To avoid environmentQueue calling compiler code calling environmentQueue.
1658  // - To compile dependencies that were enqueued later while a compilation that was enqueued earlier waits.
1659  // - To be more efficient when compiling multiple source files.
1660 
1661  dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
1662  dispatch_async(queue, ^{
1663  for (int i = 0; i < sourcesEnqueued; ++i)
1664  {
1665  __block VuoModuleCompilationQueue::Item *queueItem = moduleCompilationQueue->next();
1666  VUserLog("Compiling %s", queueItem->moduleKey.c_str());
1667 
1668  dispatch_async(queue, ^{
1669  try
1670  {
1671  VuoFileUtilities::makeDir(queueItem->cachedModulesPath);
1672  }
1673  catch (VuoException &e)
1674  {
1675  VuoCompilerIssue issue(VuoCompilerIssue::Error, "compiling subcomposition", "", "Couldn't create cached modules folder", e.what());
1676  VuoCompilerIssues *issues = new VuoCompilerIssues(issue);
1677  notifyCompilers(set<VuoCompilerModule *>(), set< pair<VuoCompilerModule *, VuoCompilerModule *> >(), set<VuoCompilerModule *>(), issues, false);
1678  }
1679 
1680  VuoCompiler *compiler = new VuoCompiler(queueItem->sourcePath);
1681 
1682  VuoModuleCompilationQueue::Item *queueItemForCallback = queueItem;
1683  auto moduleLoadedCallback = [=](){
1684  dispatch_group_leave(queueItemForCallback->loadingGroup);
1685  moduleCompilationQueue->completed(queueItemForCallback);
1686  };
1687 
1688  string ext = queueItem->sourceFile->extension();
1690  {
1691  VuoModuleCompiler *moduleCompiler = NULL;
1692  try
1693  {
1694  moduleCompiler = VuoModuleCompiler::newModuleCompiler("isf", queueItem->moduleKey, queueItem->sourceFile);
1695  }
1696  catch (VuoException &e)
1697  {
1698  VuoCompilerIssues *issues = new VuoCompilerIssues(VuoCompilerIssue(VuoCompilerIssue::Error, "compiling ISF module", queueItem->sourcePath, "", e.what()));
1699  notifyCompilers(set<VuoCompilerModule *>(), set< pair<VuoCompilerModule *, VuoCompilerModule *> >(), set<VuoCompilerModule *>(), issues, false);
1700  }
1701 
1702  if (moduleCompiler)
1703  {
1704  moduleCompiler->overrideSourceCode(queueItem->sourceCode, queueItem->sourceFile);
1705 
1706  auto getType = [&compiler] (const string &moduleKey) { return compiler->getType(moduleKey); };
1707  VuoCompilerIssues *issues = new VuoCompilerIssues();
1708  Module *module = moduleCompiler->compile(getType, llvmQueue, issues);
1709 
1710  if (module)
1711  {
1712  dispatch_sync(llvmQueue, ^{
1713  //setTargetForModule(module, target);
1714  writeModuleToBitcode(module, queueItem->compiledModulePath);
1715  });
1716  }
1717  else
1718  {
1719  issues->setFilePathIfEmpty(queueItem->sourcePath);
1720  }
1721 
1722  moduleFileChanged(queueItem->compiledModulePath, queueItem->sourceCode, moduleLoadedCallback, compiler, issues);
1723  }
1724  else
1725  moduleLoadedCallback();
1726  }
1727  else if (VuoFileUtilities::isCSourceExtension(ext) && ! queueItem->cachedModulesPath.empty())
1728  {
1729  try
1730  {
1731  compiler->compileModule(queueItem->sourcePath, queueItem->compiledModulePath, vector<string>());
1732  moduleFileChanged(queueItem->compiledModulePath, queueItem->sourceCode, moduleLoadedCallback, compiler, nullptr);
1733  }
1734  catch (VuoCompilerException &e)
1735  {
1736  VuoCompilerIssues *issues = new VuoCompilerIssues;
1737  for (auto issue : e.getIssues()->getList())
1738  issues->append(issue);
1739  notifyCompilers(set<VuoCompilerModule *>(), set< pair<VuoCompilerModule *, VuoCompilerModule *> >(), set<VuoCompilerModule *>(), issues, false);
1740  moduleLoadedCallback();
1741  }
1742  }
1743  else
1744  {
1745  compiler->setLoadAllModules(false);
1746  compiler->compileSubcompositionString(queueItem->sourceCode, queueItem->compiledModulePath, moduleLoadedCallback, this, NULL, queueItem->sourcePath);
1747  }
1748 
1749  delete compiler;
1750  dispatch_group_leave(moduleSourceCompilersExistGlobally);
1751  });
1752  }
1753  });
1754 
1755  return sourcesLoading;
1756 }
1757 
1766 set<VuoCompilerModule *> VuoCompiler::Environment::unloadCompiledModules(const set<string> &moduleKeys)
1767 {
1768  set<VuoCompilerModule *> modulesUnloaded;
1769 
1770  for (set<string>::const_iterator i = moduleKeys.begin(); i != moduleKeys.end(); ++i)
1771  {
1772  string moduleKey = *i;
1773  VuoCompilerModule *module = NULL;
1774 
1775  map<string, VuoCompilerNodeClass *>::iterator nodeClassIter = nodeClasses.find(moduleKey);
1776  if (nodeClassIter != nodeClasses.end())
1777  {
1778  module = nodeClassIter->second;
1779  nodeClasses.erase(nodeClassIter);
1780  }
1781  else
1782  {
1783  map<string, VuoCompilerType *>::iterator typeIter = types.find(moduleKey);
1784  if (typeIter != types.end())
1785  {
1786  module = typeIter->second;
1787  types.erase(typeIter);
1788  }
1789  else
1790  {
1791  map<string, VuoCompilerModule *>::iterator libraryModuleIter = libraryModules.find(moduleKey);
1792  if (libraryModuleIter != libraryModules.end())
1793  {
1794  module = libraryModuleIter->second;
1795  libraryModules.erase(libraryModuleIter);
1796  }
1797  }
1798  }
1799 
1800  if (module)
1801  {
1802  modulesUnloaded.insert(module);
1803  removeFromDependencyGraph(module);
1804  modulesChanged();
1805  }
1806  }
1807 
1808  return modulesUnloaded;
1809 }
1810 
1816 void VuoCompiler::Environment::deleteModulesCompiledFromSourceCode(const set<string> &moduleKeys)
1817 {
1818  for (set<string>::const_iterator i = moduleKeys.begin(); i != moduleKeys.end(); ++i)
1819  {
1820  string moduleKey = *i;
1821 
1822  map<string, ModuleInfo *>::iterator iter = moduleFilesAtSearchPath[getCompiledModuleCachePath()].find(moduleKey);
1823  if (iter != moduleFilesAtSearchPath[getCompiledModuleCachePath()].end())
1824  VuoFileUtilities::deleteFile(iter->second->getFile()->path());
1825  }
1826 }
1827 
1835 VuoCompilerModule * VuoCompiler::Environment::loadModule(ModuleInfo *moduleInfo)
1836 {
1837  string moduleKey = moduleInfo->getModuleKey();
1838 
1839  // Skip certain LLVM modules that definitely aren't Vuo modules to avoid adding struct types defined in them to the LLVM
1840  // context, resulting in mismatched struct types in code generation (e.g. %struct.NodeContext and %struct.NodeContext.1).
1841  if (VuoStringUtilities::beginsWith(moduleKey, "libVuo"))
1842  return NULL;
1843 
1844  // Don't try to load single-target parts
1845  // (such as those found in `build/test/TestControlAndTelemetry`),
1846  // since they just fail and pollute the logs.
1847  if (VuoStringUtilities::endsWith(moduleKey, "-x86_64")
1848  || VuoStringUtilities::endsWith(moduleKey, "-arm64"))
1849  return nullptr;
1850 
1851  __block size_t inputDataBytes;
1852  __block char *rawInputData;
1853  dispatch_sync(llvmQueue, ^{
1854  try
1855  {
1856  rawInputData = moduleInfo->getFile()->getContentsAsRawData(inputDataBytes);
1857  }
1858  catch (VuoException &e)
1859  {
1860  rawInputData = NULL;
1861  VUserLog("Warning: Couldn't load module '%s'. Its file may have been deleted. (%s)", moduleKey.c_str(), e.what());
1862  }
1863  });
1864  if (! rawInputData)
1865  return NULL;
1866 
1867  char *processedInputData;
1868 #if VUO_PRO
1869  processedInputData = loadModule_Pro0(moduleInfo, moduleKey, inputDataBytes, rawInputData);
1870 #else
1871  processedInputData = rawInputData;
1872 #endif
1873 
1874  Module *module = NULL;
1875  bool moduleParseError = !processedInputData;
1876  if (!moduleParseError)
1877  {
1878  string moduleReadError;
1879  string arch = VuoCompiler::getTargetArch(target);
1880  VuoLog_status("Loading module \"%s\" (%s)", moduleKey.c_str(), arch.c_str());
1881  module = readModuleFromBitcodeData(processedInputData, inputDataBytes, arch, moduleReadError);
1882  VuoLog_status(NULL);
1883  free(processedInputData);
1884 
1885  if (!module)
1886  {
1887  moduleParseError = true;
1888 
1889  string dir, file, ext;
1890  VuoFileUtilities::splitPath(moduleInfo->getFile()->isInArchive() ? moduleInfo->getFile()->getRelativePath() : moduleInfo->getFile()->path(), dir, file, ext);
1892  if (dir == getCompiledModuleCachePath())
1893  VuoFileUtilities::deleteFile(moduleInfo->getFile()->path());
1894  else
1895  VUserLog("Error: Couldn't load module '%s' into %s environment: %s.", moduleInfo->getFile()->getRelativePath().c_str(), arch.c_str(), moduleReadError.c_str());
1896  }
1897  }
1898 
1899  if (!moduleParseError)
1900  {
1901  string modulePath;
1902  if (! moduleInfo->getFile()->isInArchive())
1903  modulePath = moduleInfo->getFile()->path();
1904 
1905  __block VuoCompilerModule *compilerModule;
1906  dispatch_sync(llvmQueue, ^{
1907  compilerModule = VuoCompilerModule::newModule(moduleKey, module, modulePath);
1908  });
1909 
1910  if (compilerModule)
1911  {
1912  if (dynamic_cast<VuoCompilerNodeClass *>(compilerModule))
1913  nodeClasses[moduleKey] = static_cast<VuoCompilerNodeClass *>(compilerModule);
1914  else if (dynamic_cast<VuoCompilerType *>(compilerModule))
1915  types[moduleKey] = static_cast<VuoCompilerType *>(compilerModule);
1916  else
1917  libraryModules[moduleKey] = compilerModule;
1918 
1919  VuoNodeSet *nodeSet = VuoNodeSet::createNodeSetForModule(moduleInfo->getFile());
1920  if (nodeSet)
1921  {
1922  map<string, VuoNodeSet *>::iterator nodeSetIter = nodeSetForName.find(nodeSet->getName());
1923  if (nodeSetIter == nodeSetForName.end())
1924  {
1925  nodeSetForName[nodeSet->getName()] = nodeSet;
1926  }
1927  else
1928  {
1929  delete nodeSet;
1930  nodeSet = nodeSetIter->second;
1931  }
1932  compilerModule->getPseudoBase()->setNodeSet(nodeSet);
1933  }
1934 
1935 #if VUO_PRO
1936  loadModule_Pro1(rawInputData, processedInputData, compilerModule);
1937 #endif
1938 
1939  compilerModule->setBuiltIn( isBuiltInOriginal() );
1940 
1941  return compilerModule;
1942  }
1943  else
1944  {
1945  destroyLlvmModule(module);
1946  }
1947  }
1948 
1949  return NULL;
1950 }
1951 
1957 void VuoCompiler::Environment::replaceNodeClass(VuoCompilerNodeClass *newNodeClass)
1958 {
1959  string moduleKey = newNodeClass->getBase()->getModuleKey();
1960 
1961  VuoCompilerNodeClass *oldNodeClass = nodeClasses[moduleKey];
1962  if (oldNodeClass)
1963  {
1964  removeFromDependencyGraph(oldNodeClass);
1965  destroyModule(oldNodeClass);
1966  }
1967 
1968  nodeClasses[moduleKey] = newNodeClass;
1969  addToDependencyGraph(newNodeClass);
1970  modulesChanged();
1971 }
1972 
1973 void VuoCompiler::Environment::addToDependencyGraph(VuoCompilerModule *module)
1974 {
1975  DependencyGraphVertex *vertex = DependencyGraphVertex::vertexForDependency(module->getPseudoBase()->getModuleKey(), dependencyGraph);
1976  dependencyGraph->addVertex(vertex);
1977 
1978  vertex->setEnvironment(this);
1979 
1981  vertex->setCompatible( module->getCompatibleTargets().isCompatibleWith(compositionTargets) );
1982 
1983  set<string> dependencies = module->getDependencies();
1984  for (set<string>::iterator i = dependencies.begin(); i != dependencies.end(); ++i)
1985  {
1986  DependencyGraphVertex *depVertex = DependencyGraphVertex::vertexForDependency(*i, dependencyGraph);
1987  dependencyGraph->addEdge(vertex, depVertex);
1988  }
1989 }
1990 
1991 void VuoCompiler::Environment::removeFromDependencyGraph(VuoCompilerModule *module)
1992 {
1993  DependencyGraphVertex *vertex = DependencyGraphVertex::vertexForDependency(module->getPseudoBase()->getModuleKey(), dependencyGraph);
1994  dependencyGraph->removeVertex(vertex);
1995 }
1996 
2007 void VuoCompiler::Environment::reifyPortTypes(const map<string, VuoCompilerType *> &inheritedTypes)
2008 {
2009  map<string, VuoCompilerType *> availableTypes = inheritedTypes;
2010  availableTypes.insert(types.begin(), types.end());
2011 
2012  for (map<string, VuoCompilerNodeClass *>::const_iterator i = nodeClasses.begin(); i != nodeClasses.end(); ++i)
2013  {
2014  VuoNodeClass *nodeClass = i->second->getBase();
2015 
2016  vector<VuoPortClass *> inputPortClasses = nodeClass->getInputPortClasses();
2017  vector<VuoPortClass *> outputPortClasses = nodeClass->getOutputPortClasses();
2018  vector<VuoPortClass *> portClasses;
2019  portClasses.insert(portClasses.end(), inputPortClasses.begin(), inputPortClasses.end());
2020  portClasses.insert(portClasses.end(), outputPortClasses.begin(), outputPortClasses.end());
2021 
2022  for (vector<VuoPortClass *>::iterator j = portClasses.begin(); j != portClasses.end(); ++j)
2023  {
2024  VuoCompilerPortClass *portClass = static_cast<VuoCompilerPortClass *>((*j)->getCompiler());
2025  VuoType *baseType = portClass->getDataVuoType();
2026 
2027  if (baseType && ! baseType->hasCompiler())
2028  {
2029  string typeName = baseType->getModuleKey();
2030  VuoCompilerType *reifiedType = NULL;
2031 
2032  VuoGenericType *genericType = dynamic_cast<VuoGenericType *>(baseType);
2033  if (genericType)
2034  {
2035  reifiedType = VuoCompilerGenericType::newGenericType(genericType, availableTypes);
2036  if (reifiedType) {
2037  genericTypes.insert( static_cast<VuoCompilerGenericType *>(reifiedType) );
2038  }
2039  }
2040  else
2041  {
2042  map<string, VuoCompilerType *>::iterator reifiedTypeIter = availableTypes.find(typeName);
2043  if (reifiedTypeIter != availableTypes.end())
2044  {
2045  delete baseType;
2046  reifiedType = reifiedTypeIter->second;
2047  }
2048  }
2049 
2050  if (reifiedType) {
2051  portClass->setDataVuoType(reifiedType->getBase());
2052  }
2053  }
2054  }
2055 
2056  vector<VuoCompilerTriggerDescription *> triggers = nodeClass->getCompiler()->getTriggerDescriptions();
2057  for (vector<VuoCompilerTriggerDescription *>::iterator j = triggers.begin(); j != triggers.end(); ++j)
2058  {
2059  VuoCompilerTriggerDescription *trigger = *j;
2060  VuoType *baseType = trigger->getDataType();
2061 
2062  if (baseType && ! baseType->hasCompiler())
2063  {
2064  string typeName = baseType->getModuleKey();
2065  map<string, VuoCompilerType *>::iterator reifiedTypeIter = availableTypes.find(typeName);
2066  if (reifiedTypeIter != availableTypes.end())
2067  {
2068  delete baseType;
2069  VuoCompilerType *reifiedType = reifiedTypeIter->second;
2070  trigger->setDataType(reifiedType->getBase());
2071  }
2072  }
2073  }
2074  }
2075 }
2076 
2087 void VuoCompiler::Environment::getCacheableModulesAndDependencies(set<string> &cacheableModulesAndDependencies,
2088  set<string> &dylibsNeededToLinkToThisCache,
2089  set<string> &frameworksNeededToLinkToThisCache)
2090 {
2091  if (isModuleCacheInitialized && ! isModuleCacheableDataDirty)
2092  {
2093  cacheableModulesAndDependencies = moduleCacheContents;
2094  dylibsNeededToLinkToThisCache = moduleCacheDylibs;
2095  frameworksNeededToLinkToThisCache = moduleCacheFrameworks;
2096  return;
2097  }
2098 
2100 
2101  // Include all modules…
2102  map<string, VuoCompilerModule *> allModules;
2103  allModules.insert(nodeClasses.begin(), nodeClasses.end());
2104  allModules.insert(types.begin(), types.end());
2105  allModules.insert(libraryModules.begin(), libraryModules.end());
2106 
2107  set<string> dependencies;
2108  for (map<string, VuoCompilerModule *>::iterator i = allModules.begin(); i != allModules.end(); ++i)
2109  {
2110  string moduleKey = i->first;
2111  VuoCompilerModule *module = i->second;
2112 
2113 #if VUO_PRO
2114  // … except Pro modules and modules that depend on them…
2115  if (module->requiresPro())
2116  continue;
2117 #endif
2118 
2119  // … and incompatible modules.
2120  if (! module->getCompatibleTargets().isCompatibleWith(compositionTargets))
2121  continue;
2122 
2123  cacheableModulesAndDependencies.insert(moduleKey);
2124 
2125  set<string> moduleDependencies = module->getDependencies();
2126  dependencies.insert(moduleDependencies.begin(), moduleDependencies.end());
2127  }
2128 
2129  // For the built-in environment, include Vuo's core dependencies.
2130  if (builtIn && ! generated)
2131  {
2132  vector<string> coreDependencies = getCoreVuoDependencies();
2133  dependencies.insert(coreDependencies.begin(), coreDependencies.end());
2134  }
2135 
2136  // Include all dependencies of the included module that are located in this environment.
2137  // (All modules are already included, so we only need to handle non-module dependencies.)
2138  for (set<string>::iterator i = dependencies.begin(); i != dependencies.end(); ++i)
2139  {
2140  string dependency = *i;
2141  if (allModules.find(dependency) == allModules.end())
2142  {
2143  if (VuoStringUtilities::endsWith(dependency, ".framework"))
2144  frameworksNeededToLinkToThisCache.insert(dependency);
2145  else
2146  {
2147  string dependencyPath = VuoCompiler::getLibraryPath(dependency, librarySearchPaths);
2148  if (! dependencyPath.empty())
2149  {
2150  if (VuoStringUtilities::endsWith(dependencyPath, ".dylib"))
2151  dylibsNeededToLinkToThisCache.insert(dependencyPath);
2152  else
2153  cacheableModulesAndDependencies.insert(dependency);
2154  }
2155  }
2156  }
2157  }
2158 
2159  moduleCacheDylibs = dylibsNeededToLinkToThisCache;
2160  moduleCacheFrameworks = frameworksNeededToLinkToThisCache;
2161 
2162  if (builtIn)
2163  currentModuleCacheDylib = VuoFileUtilities::buildModuleCacheDylibPath(moduleCachePath, builtIn, generated);
2164 }
2165 
2186 void VuoCompiler::Environment::useModuleCache(bool shouldUseExistingCache, VuoCompiler *compiler,
2187  set<string> cacheableModulesAndDependencies,
2188  set<string> dylibsNeededToLinkToCaches, set<string> frameworksNeededToLinkToCaches,
2189  unsigned long lastPrerequisiteModuleCacheRebuild)
2190 {
2191  // Ignore the cache if the `prelinkCache` preference is false.
2192 
2193  static dispatch_once_t checked = 0;
2194  static bool prelinkCache = true;
2195  dispatch_once(&checked, ^{
2196  Boolean valid;
2197  bool result = CFPreferencesGetAppBooleanValue(CFSTR("prelinkCache"), CFSTR("org.vuo.Editor"), &valid);
2198  if (valid)
2199  prelinkCache = result;
2200  });
2201  if (! prelinkCache)
2202  {
2203  VDebugLog("Ignoring the module cache since the 'prelinkCache' preference is false.");
2204  return;
2205  }
2206 
2207  // Don't do anything if this environment doesn't have a cache configured.
2208 
2209  if (moduleCachePath.empty())
2210  return;
2211 
2212  // Don't bother rechecking the cache if neither the modules in this environment nor the caches that it depends on have changed.
2213 
2214  string cacheDescription = VuoFileUtilities::buildModuleCacheDescription(moduleCachePath, generated);
2215  if (isModuleCacheInitialized && ! isModuleCacheableDataDirty &&
2216  (builtIn || lastModuleCacheRebuild >= lastPrerequisiteModuleCacheRebuild))
2217  {
2218  VDebugLog("No need to recheck %s.", cacheDescription.c_str());
2219  return;
2220  }
2221 
2222  try
2223  {
2224  VDebugLog("Checking if %s is up-to-date…", cacheDescription.c_str());
2225 
2226  bool isCacheUpToDate = true;
2227 
2228  if (isModuleCacheInitialized && isModuleCacheableDataDirty)
2229  isCacheUpToDate = false;
2230 
2231  isModuleCacheInitialized = true;
2232  isModuleCacheableDataDirty = false;
2233 
2234  string indexPath = VuoFileUtilities::buildModuleCacheIndexPath(moduleCachePath, builtIn, generated);
2235  string dir, file, ext;
2236  VuoFileUtilities::splitPath(indexPath, dir, file, ext);
2237  string indexFileName = file + "." + ext;
2238 
2239  // Create the cache directory and index if they don't already exist. (If they do exist, don't affect the last-modified times.)
2240 
2241  bool dirExists = VuoFileUtilities::fileExists(moduleCachePath);
2242  if (! dirExists)
2243  {
2244  if (shouldUseExistingCache)
2245  throw VuoException("Trying to use the existing cache, but the cache directory doesn't exist.", false);
2246 
2247  VuoFileUtilities::makeDir(moduleCachePath);
2248  isCacheUpToDate = false;
2249  }
2250 
2251  bool indexFileExists = VuoFileUtilities::fileExists(indexPath);
2252  if (! indexFileExists)
2253  {
2254  if (shouldUseExistingCache)
2255  throw VuoException("Trying to use the existing cache, but the cache index doesn't exist.", false);
2256 
2257  VuoFileUtilities::createFile(indexPath);
2258  isCacheUpToDate = false;
2259  }
2260 
2261  // Lock the cache for reading.
2262 
2263  VuoFileUtilities::File *fileForLocking;
2264  {
2265  fileForLocking = moduleCacheFileForLocking[indexPath];
2266  if (! fileForLocking)
2267  {
2268  fileForLocking = new VuoFileUtilities::File(moduleCachePath, indexFileName);
2269  moduleCacheFileForLocking[indexPath] = fileForLocking;
2270  }
2271 
2272  if (!fileForLocking->lockForReading())
2273  VDebugLog("\tWarning: Couldn't lock for reading.");
2274  }
2275 
2276  // If this is the first time this Environment is using its cache, see if there's a dylib on disk.
2277 
2278  if (currentModuleCacheDylib.empty())
2279  currentModuleCacheDylib = VuoFileUtilities::findLatestRevisionOfModuleCacheDylib(moduleCachePath, builtIn, generated, lastModuleCacheRebuild);
2280 
2281  if (shouldUseExistingCache && currentModuleCacheDylib.empty())
2282  throw VuoException("Trying to use the existing cache, but the cache dylib doesn't exist.", false);
2283 
2284  // Check if the dylib is newer than the other caches that it depends on.
2285 
2286  if (isCacheUpToDate)
2287  isCacheUpToDate = lastModuleCacheRebuild >= lastPrerequisiteModuleCacheRebuild;
2288 
2289  // Check if the dylib looks remotely valid.
2290 
2291  if (isCacheUpToDate)
2292  {
2293  bool dylibHasData = VuoFileUtilities::fileContainsReadableData(currentModuleCacheDylib);
2294  if (! dylibHasData)
2295  {
2296  if (shouldUseExistingCache)
2297  throw VuoException("Trying to use the existing cache, but the cache doesn't contain readable data.", false);
2298  else
2299  isCacheUpToDate = false;
2300  }
2301  }
2302 
2303  // List the items actually in the cache, according to its index.
2304 
2305  const char separator = '\n';
2306  if (isCacheUpToDate || shouldUseExistingCache)
2307  {
2308  VuoFileUtilities::File indexFile(moduleCachePath, indexFileName);
2309  string index = indexFile.getContentsAsString();
2310  vector<string> actualIndex = VuoStringUtilities::split(index, separator);
2311 
2312  moduleCacheContents.clear();
2313  moduleCacheContents.insert(actualIndex.begin(), actualIndex.end());
2314 
2315  if (shouldUseExistingCache)
2316  {
2317  isModuleCacheAvailable = true;
2318  return;
2319  }
2320  }
2321 
2322  // Check if the list of actual items matches the list of expected items.
2323 
2324  if (isCacheUpToDate)
2325  {
2326  if (moduleCacheContents.size() != cacheableModulesAndDependencies.size())
2327  isCacheUpToDate = false;
2328  else
2329  {
2330  set<string> contentsInBoth;
2331  std::set_intersection(moduleCacheContents.begin(), moduleCacheContents.end(),
2332  cacheableModulesAndDependencies.begin(), cacheableModulesAndDependencies.end(),
2333  std::inserter(contentsInBoth, contentsInBoth.begin()));
2334 
2335  if (contentsInBoth.size() != cacheableModulesAndDependencies.size())
2336  isCacheUpToDate = false;
2337  }
2338  }
2339 
2340  // Check if the cache is newer than all of the modules in it.
2341 
2342  if (isCacheUpToDate)
2343  {
2344  unsigned long cacheLastModified = VuoFileUtilities::getFileLastModifiedInSeconds(currentModuleCacheDylib);
2345 
2346  for (map<string, map<string, ModuleInfo *> >::iterator i = moduleFilesAtSearchPath.begin(); i != moduleFilesAtSearchPath.end(); ++i)
2347  {
2348  for (map<string, ModuleInfo *>::iterator j = i->second.begin(); j != i->second.end(); ++j)
2349  {
2350  if (! j->second->isOlderThan(cacheLastModified))
2351  {
2352  isCacheUpToDate = false;
2353  break;
2354  }
2355  }
2356  }
2357  }
2358 
2359  // If the cache is consistent with this environment, we're done.
2360 
2361  if (isCacheUpToDate)
2362  {
2363  VDebugLog("Up-to-date.");
2364 
2365  isModuleCacheAvailable = true;
2366  return;
2367  }
2368 
2369  // Otherwise, (re)build the cache.
2370 
2371  if (! builtIn)
2372  {
2373  currentModuleCacheDylib = VuoFileUtilities::buildModuleCacheDylibPath(moduleCachePath, builtIn, generated);
2374 
2375  struct timeval t;
2376  gettimeofday(&t, NULL);
2377  lastModuleCacheRebuild = t.tv_sec;
2378  }
2379 
2380  dispatch_async(moduleCacheBuildingQueue, ^{
2381  VDebugLog("Rebuilding %s…", cacheDescription.c_str());
2382 
2383  set<Module *> modulesToLink;
2384  set<string> librariesToLink;
2385  set<string> frameworksToLink;
2386  {
2387  compiler->getLinkerInputs(cacheableModulesAndDependencies, Optimization_SmallBinary, modulesToLink, librariesToLink, frameworksToLink);
2388 
2389  librariesToLink.insert(dylibsNeededToLinkToCaches.begin(), dylibsNeededToLinkToCaches.end());
2390  frameworksToLink.insert(frameworksNeededToLinkToCaches.begin(), frameworksNeededToLinkToCaches.end());
2391  }
2392 
2393  bool gotLockForWriting = false;
2394  try
2395  {
2396  // Try to upgrade the file lock for writing.
2397  gotLockForWriting = fileForLocking->lockForWriting(true);
2398  if (! gotLockForWriting)
2399  throw VuoException("The cache file is being used by another process. "
2400  "If any composition windows are open from previous Vuo sessions, quit them. "
2401  "If any processes whose names start with \"VuoComposition\" or one of your composition file names appear in Activity Monitor, force-quit them.",
2402  false);
2403 
2404  // Link the dependencies to create the cached resources dylib in a temporary file.
2405  string dir, file, ext;
2406  VuoFileUtilities::splitPath(currentModuleCacheDylib, dir, file, ext);
2407  string tmpPath = VuoFileUtilities::makeTmpFile(file, "dylib");
2408  compiler->link(tmpPath, modulesToLink, librariesToLink, frameworksToLink, true, "", false);
2409 
2410  // Move the temporary file into the cache.
2411  VuoFileUtilities::moveFile(tmpPath, currentModuleCacheDylib);
2412 
2413  // Change the dylib's ID from the temporary path to the path within the cache.
2415  getVuoFrameworkPath() + "/Helpers/install_name_tool",
2416  "-id",
2417  currentModuleCacheDylib,
2418  currentModuleCacheDylib,
2419  });
2420 
2421  // Ad-hoc code-sign the runtime-generated System and User caches,
2422  // but don't ad-hoc code-sign the buildtime-generated Builtin module cache
2423  // since `framework/CMakeLists.txt` later changes its ID/rpath/loads.
2424  if (vuoFrameworkInProgressPath.empty())
2425  adHocCodeSign(currentModuleCacheDylib);
2426 
2427  // Write the list of dependencies to the index file.
2428  vector<string> expectedContents(cacheableModulesAndDependencies.begin(), cacheableModulesAndDependencies.end());
2429  string index = VuoStringUtilities::join(expectedContents, separator);
2430  VuoFileUtilities::writeStringToFile(index, indexPath);
2431 
2432  // Delete any older revisions of the dylib.
2434 
2435  // Downgrade the file lock back to reading.
2436  if (!fileForLocking->lockForReading())
2437  VDebugLog("\tWarning: Couldn't downgrade the lock back to reading.");
2438 
2439  dispatch_sync(environmentQueue, ^{
2440  moduleCacheContents = cacheableModulesAndDependencies;
2441  isModuleCacheAvailable = true;
2442  });
2443  }
2444  catch (VuoException &e)
2445  {
2446  // Downgrade the file lock back to reading.
2447  if (gotLockForWriting)
2448  if (!fileForLocking->lockForReading())
2449  VDebugLog("\tWarning: Couldn't downgrade the lock back to reading.");
2450 
2451  VUserLog("Warning: Couldn't rebuild %s for the \"faster build\" optimization: %s", cacheDescription.c_str(), e.what());
2452  }
2453 
2454  VDebugLog("Done.");
2455  });
2456  }
2457  catch (VuoException &e)
2458  {
2459  VUserLog("Warning: Couldn't use %s for the \"faster build\" optimization: %s", cacheDescription.c_str(), e.what());
2460  }
2461 }
2462 
2466 void VuoCompiler::Environment::waitForModuleCachesToBuild(void)
2467 {
2468  dispatch_sync(moduleCacheBuildingQueue, ^{});
2469 }
2470 
2480 bool VuoCompiler::Environment::findInModuleCache(const string &moduleOrDependency, string &cachePath)
2481 {
2482  if (isModuleCacheAvailable && moduleCacheContents.find(moduleOrDependency) != moduleCacheContents.end())
2483  {
2484  cachePath = currentModuleCacheDylib;
2485  return true;
2486  }
2487 
2488  return false;
2489 }
2490 
2494 string VuoCompiler::Environment::getCurrentModuleCacheDylib(void)
2495 {
2496  return currentModuleCacheDylib;
2497 }
2498 
2503 unsigned long VuoCompiler::Environment::getLastModuleCacheRebuild(void)
2504 {
2505  return lastModuleCacheRebuild;
2506 }
2507 
2514 void VuoCompiler::Environment::modulesChanged(void)
2515 {
2516  isModuleCacheableDataDirty = true;
2517  isModuleCacheAvailable = false;
2518 }
2519 
2523 bool VuoCompiler::Environment::isBuiltInOriginal()
2524 {
2525  return builtIn && ! generated;
2526 }
2527 
2531 bool VuoCompiler::Environment::isBuiltIn()
2532 {
2533  return builtIn;
2534 }
2535 
2539 bool VuoCompiler::Environment::isGenerated()
2540 {
2541  return generated;
2542 }
2543 
2547 string VuoCompiler::Environment::getName()
2548 {
2549  if (isBuiltInOriginal())
2550  return "builtin";
2551  else if (this == sharedEnvironments[target][0][1])
2552  return "builtin (generated)";
2553  else if (this == sharedEnvironments[target][1][0])
2554  return "system";
2555  else if (this == sharedEnvironments[target][1][1])
2556  return "system (generated)";
2557  else if (this == sharedEnvironments[target][2][0])
2558  return "user";
2559  else if (this == sharedEnvironments[target][2][1])
2560  return "user (generated)";
2561  return "composition-local";
2562 }
2563 
2567 void VuoCompiler::applyToInstalledEnvironments(void (^doForEnvironment)(Environment *))
2568 {
2569  dispatch_sync(environmentQueue, ^{
2570  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i) {
2571  doForEnvironment((*i)[0]);
2572  }
2573  });
2574 }
2575 
2579 void VuoCompiler::applyToInstalledEnvironments(void (^doForEnvironment)(Environment *, bool *, string), bool *result, string arg)
2580 {
2581  dispatch_sync(environmentQueue, ^{
2582  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i) {
2583  doForEnvironment((*i)[0], result, arg);
2584  }
2585  });
2586 }
2587 
2591 void VuoCompiler::applyToAllEnvironments(void (^doForEnvironment)(Environment *environment))
2592 {
2593  dispatch_sync(environmentQueue, ^{
2594  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i) {
2595  for (vector<Environment *>::iterator j = i->begin(); j != i->end(); ++j) {
2596  doForEnvironment(*j);
2597  }
2598  }
2599  });
2600 }
2601 
2615 VuoCompiler::VuoCompiler(const string &compositionPath, string target)
2616 {
2617  if (target.empty())
2618  {
2619  this->target = target = getProcessTarget();
2620  VDebugLog("%p target=%s (from current process)", this, this->target.c_str());
2621  }
2622  else
2623  {
2624  this->target = target;
2625  VDebugLog("%p target=%s", this, this->target.c_str());
2626  }
2627 
2628 #if VUO_PRO
2629  init_Pro();
2630 #endif
2631 
2632  shouldLoadAllModules = true;
2633  hasLoadedAllModules = false;
2634  modulesToLoadQueue = dispatch_queue_create("org.vuo.compiler.modules", NULL);
2635  moduleSourceCompilersExist = dispatch_group_create();
2636  moduleCacheBuilding = dispatch_group_create();
2637  dependencyGraph = NULL;
2638  compositionDependencyGraph = NULL;
2639  isVerbose = false;
2640  _shouldShowSplashWindow = false;
2641  delegate = NULL;
2642  delegateQueue = dispatch_queue_create("org.vuo.compiler.delegate", NULL);
2643 
2644  string vuoFrameworkPath = getVuoFrameworkPath();
2645  if (! vuoFrameworkPath.empty())
2646  clangPath = vuoFrameworkPath + "/Helpers/clang";
2647  else
2648  clangPath = LLVM_ROOT "/bin/clang";
2649 
2650  dispatch_sync(environmentQueue, ^{
2651  allCompilers.insert(this);
2652 
2653  if (sharedEnvironments[target].empty())
2654  {
2655  sharedEnvironments[target] = vector< vector<Environment *> >(3, vector<Environment *>(2, NULL));
2656  for (vector< vector<Environment *> >::iterator i = sharedEnvironments[target].begin(); i != sharedEnvironments[target].end(); ++i)
2657  for (vector<Environment *>::iterator j = i->begin(); j != i->end(); ++j)
2658  *j = new Environment(this->target, i == sharedEnvironments[target].begin(), j != i->begin());
2659 
2660  vector<string> builtInModuleSearchPaths = Environment::getBuiltInModuleSearchPaths();
2661  for (vector<string>::iterator i = builtInModuleSearchPaths.begin(); i != builtInModuleSearchPaths.end(); ++i)
2662  sharedEnvironments[target][0][0]->addModuleSearchPath(*i, false);
2663 
2664  vector<string> builtInHeaderSearchPaths = Environment::getBuiltInHeaderSearchPaths();
2665  for (vector<string>::iterator i = builtInHeaderSearchPaths.begin(); i != builtInHeaderSearchPaths.end(); ++i)
2666  sharedEnvironments[target][0][0]->addHeaderSearchPath(*i);
2667 
2668  vector<string> builtInLibrarySearchPaths = Environment::getBuiltInLibrarySearchPaths();
2669  for (vector<string>::iterator i = builtInLibrarySearchPaths.begin(); i != builtInLibrarySearchPaths.end(); ++i)
2670  sharedEnvironments[target][0][0]->addLibrarySearchPath(*i);
2671 
2672  vector<string> builtInFrameworkSearchPaths = Environment::getBuiltInFrameworkSearchPaths();
2673  for (vector<string>::iterator i = builtInFrameworkSearchPaths.begin(); i != builtInFrameworkSearchPaths.end(); ++i)
2674  sharedEnvironments[target][0][0]->addFrameworkSearchPath(*i);
2675 
2676  // Allow system administrator to override Vuo.framework modules
2677  sharedEnvironments[target][1][0]->addModuleSearchPath(VuoFileUtilities::getSystemModulesPath());
2678  sharedEnvironments[target][1][0]->addLibrarySearchPath(VuoFileUtilities::getSystemModulesPath());
2679 
2680  // Allow user to override Vuo.framework and system-wide modules
2681  sharedEnvironments[target][2][0]->addModuleSearchPath(VuoFileUtilities::getUserModulesPath());
2682  sharedEnvironments[target][2][0]->addLibrarySearchPath(VuoFileUtilities::getUserModulesPath());
2683 
2684  // Set up module cache paths.
2685  // Since the built-in module caches are part of Vuo.framework (put there by `generateBuiltInModuleCaches`),
2686  // only attempt to use module caches if Vuo.framework exists.
2687  string vuoFrameworkPath = getVuoFrameworkPath();
2688  if (! vuoFrameworkPath.empty())
2689  {
2690  vector<string> moduleCachePaths(3);
2691  moduleCachePaths[0] = vuoFrameworkPath + "/Modules/Builtin";
2692  moduleCachePaths[1] = VuoFileUtilities::getCachePath() + "/System";
2693  moduleCachePaths[2] = VuoFileUtilities::getCachePath() + "/User";
2694 
2695  for (size_t i = 0; i < moduleCachePaths.size(); ++i)
2696  {
2697  string moduleCachePath = moduleCachePaths[i];
2698 
2699  sharedEnvironments[target][i][0]->setModuleCachePath(moduleCachePath);
2700  sharedEnvironments[target][i][0]->addModuleSearchPath(moduleCachePath + "/Modules", false);
2701 
2702  sharedEnvironments[target][i][1]->setModuleCachePath(moduleCachePath);
2703  }
2704  }
2705  }
2706  });
2707 
2708  setCompositionPath(compositionPath);
2709 }
2710 
2715 {
2716 #if VUO_PRO
2717  fini_Pro();
2718 #endif
2719 
2720  dispatch_sync(environmentQueue, ^{
2721  allCompilers.erase(this);
2722  });
2723 
2724  dispatch_group_wait(moduleCacheBuilding, DISPATCH_TIME_FOREVER);
2725  dispatch_release(moduleCacheBuilding);
2726 
2727  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
2728  (*i)[0]->removeCompilerToNotify(this);
2729 
2730  dispatch_sync(delegateQueue, ^{});
2731 
2732  dispatch_release(modulesToLoadQueue);
2733  dispatch_release(delegateQueue);
2734 
2735  delete dependencyGraph;
2736  delete compositionDependencyGraph;
2737 }
2738 
2742 void VuoCompiler::reset(void)
2743 {
2744  dispatch_group_wait(moduleSourceCompilersExistGlobally, DISPATCH_TIME_FOREVER);
2745 
2746  dispatch_sync(environmentQueue, ^{
2747  for (auto e : sharedEnvironments)
2748  for (vector< vector<Environment *> >::iterator i = e.second.begin(); i != e.second.end(); ++i)
2749  {
2750  (*i)[0]->stopWatchingModuleSearchPaths();
2751  dispatch_sync((*i)[0]->moduleSearchPathContentsChangedQueue, ^{});
2752  }
2753 
2754  for (map< string, vector<Environment *> >::iterator i = environmentsForCompositionFamily.begin(); i != environmentsForCompositionFamily.end(); ++i)
2755  {
2756  (i->second)[0]->stopWatchingModuleSearchPaths();
2757  dispatch_sync((i->second)[0]->moduleSearchPathContentsChangedQueue, ^{});
2758  }
2759 
2760  for (auto e : sharedEnvironments)
2761  for (vector< vector<Environment *> >::iterator i = e.second.begin(); i != e.second.end(); ++i)
2762  for (vector<Environment *>::iterator j = i->begin(); j != i->end(); ++j)
2763  delete *j;
2764 
2765  for (map< string, vector<Environment *> >::iterator i = environmentsForCompositionFamily.begin(); i != environmentsForCompositionFamily.end(); ++i)
2766  for (vector<Environment *>::iterator j = i->second.begin(); j != i->second.end(); ++j)
2767  delete *j;
2768 
2769  allCompilers.clear();
2770  sharedEnvironments.clear();
2771  environmentsForCompositionFamily.clear();
2772  });
2773 }
2774 
2781 {
2782  dispatch_async(delegateQueue, ^{
2783  this->delegate = delegate;
2784  });
2785 }
2786 
2796 void VuoCompiler::setCompositionPath(const string &compositionPath)
2797 {
2798  string compositionModulesDir;
2799  string compositionBaseDir;
2800  lastCompositionIsSubcomposition = false;
2801  if (! compositionPath.empty())
2802  {
2803  compositionModulesDir = VuoFileUtilities::getCompositionLocalModulesPath(compositionPath);
2804 
2805  string file, ext;
2806  VuoFileUtilities::splitPath(compositionModulesDir, compositionBaseDir, file, ext);
2807  VuoFileUtilities::canonicalizePath(compositionBaseDir);
2808 
2809  string compositionDir;
2810  VuoFileUtilities::splitPath(compositionPath, compositionDir, file, ext);
2811  VuoFileUtilities::canonicalizePath(compositionDir);
2812  lastCompositionIsSubcomposition = (compositionDir == compositionModulesDir);
2813  }
2814 
2815  // Set up `environments` to contain all environments available to this compiler, in order from broadest to narrowest.
2816 
2817  dispatch_sync(environmentQueue, ^{
2818  if (! environments.empty() && compositionBaseDir == lastCompositionBaseDir) {
2819  return;
2820  }
2821  lastCompositionBaseDir = compositionBaseDir;
2822 
2823  // Clear out the existing environments for this compiler.
2824 
2825  Environment *oldCompositionFamilyInstalledEnvironment = nullptr;
2826  vector<Environment *> compositionEnvironments;
2827  if (! environments.empty())
2828  {
2829  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i) {
2830  (*i)[0]->removeCompilerToNotify(this);
2831  }
2832 
2833  if (environments.size() >= 5) {
2834  oldCompositionFamilyInstalledEnvironment = environments[3][0];
2835  }
2836 
2837  compositionEnvironments = environments.back();
2838 
2839  environments.clear();
2840  }
2841 
2842  // If the composition is located in one of the shared environments (built-in, system, user),
2843  // add that shared environment and any shared environments at broader scope to the compiler.
2844  // If the composition is not in a shared environment, add all of the shared environments to the compiler.
2845 
2846  bool isCompositionInSharedEnvironment = false;
2847  for (vector< vector<Environment *> >::iterator i = sharedEnvironments[target].begin(); i != sharedEnvironments[target].end(); ++i)
2848  {
2849  environments.push_back(*i);
2850 
2851  vector<string> moduleSearchPaths = (*i)[0]->getModuleSearchPaths();
2852  for (vector<string>::iterator j = moduleSearchPaths.begin(); j != moduleSearchPaths.end(); ++j)
2853  {
2854  string moduleSearchPath = *j;
2855  VuoFileUtilities::canonicalizePath(moduleSearchPath);
2856  if (moduleSearchPath == compositionModulesDir)
2857  {
2858  isCompositionInSharedEnvironment = true;
2859  break;
2860  }
2861  }
2862 
2863  if (isCompositionInSharedEnvironment) {
2864  break;
2865  }
2866  }
2867 
2868  // If the composition is not in a shared environment, add the composition-family environment to the compiler.
2869 
2870  Environment *newCompositionFamilyInstalledEnvironment = nullptr;
2871  if (! isCompositionInSharedEnvironment && ! compositionPath.empty())
2872  {
2873  vector<Environment *> compositionFamilyEnvironments = environmentsForCompositionFamily[compositionBaseDir];
2874  if (compositionFamilyEnvironments.empty())
2875  {
2876  compositionFamilyEnvironments = vector<Environment *>(2, NULL);
2877  compositionFamilyEnvironments[0] = new Environment(this->target, false, false);
2878  compositionFamilyEnvironments[1] = new Environment(this->target, false, true);
2879  environmentsForCompositionFamily[compositionBaseDir] = compositionFamilyEnvironments;
2880 
2881  // Allow the user to place modules/subcompositions in a Modules folder inside the composition folder.
2882 
2883  compositionFamilyEnvironments[0]->addModuleSearchPath(compositionModulesDir);
2884 
2885  // Locate this environment's cache in a folder whose name is the composition folder path with
2886  // slashes replaced with Unicode Modifier Letter Colon.
2887  string moduleCachePath = getCachePathForComposition(compositionBaseDir);
2888 
2889  compositionFamilyEnvironments[0]->setModuleCachePath(moduleCachePath);
2890  compositionFamilyEnvironments[0]->addModuleSearchPath(moduleCachePath + "/Modules", false);
2891 
2892  compositionFamilyEnvironments[1]->setModuleCachePath(moduleCachePath);
2893  }
2894  environments.push_back(compositionFamilyEnvironments);
2895 
2896  newCompositionFamilyInstalledEnvironment = compositionFamilyEnvironments[0];
2897  }
2898 
2899  // Add the composition environment to the compiler (or add it back in if it already existed).
2900 
2901  if (compositionEnvironments.empty())
2902  {
2903  compositionEnvironments = vector<Environment *>(2, NULL);
2904  compositionEnvironments[0] = new Environment(this->target, false, false);
2905  compositionEnvironments[1] = new Environment(this->target, false, true);
2906  }
2907  environments.push_back(compositionEnvironments);
2908 
2909  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i) {
2910  (*i)[0]->addCompilerToNotify(this);
2911  }
2912 
2913  delete dependencyGraph;
2914  delete compositionDependencyGraph;
2915  dependencyGraph = makeDependencyNetwork(environments, ^VuoDirectedAcyclicGraph * (Environment *env) { return env->getDependencyGraph(); });
2916  compositionDependencyGraph = makeDependencyNetwork(environments, ^VuoDirectedAcyclicGraph * (Environment *env) { return env->getCompositionDependencyGraph(); });
2917 
2918  // If the compiler has a different local Modules directory than before, notify the compiler's delegate
2919  // of composition-family modules that are newly available/unavailable.
2920 
2921  if (oldCompositionFamilyInstalledEnvironment != newCompositionFamilyInstalledEnvironment)
2922  {
2923  auto getModules = [] (Environment *env)
2924  {
2925  map<string, VuoCompilerModule *> modules;
2926  if (env)
2927  {
2928  for (auto i : env->getNodeClasses()) {
2929  modules.insert(i);
2930  }
2931  for (auto i : env->getTypes()) {
2932  modules.insert(i);
2933  }
2934  for (auto i : env->getLibraryModules()) {
2935  modules.insert(i);
2936  }
2937  }
2938  return modules;
2939  };
2940 
2941  map<string, VuoCompilerModule *> modulesAdded = getModules(newCompositionFamilyInstalledEnvironment);
2942  map<string, VuoCompilerModule *> modulesRemoved = getModules(oldCompositionFamilyInstalledEnvironment);
2943 
2944  map<string, pair<VuoCompilerModule *, VuoCompilerModule *> > modulesModified;
2945  for (map<string, VuoCompilerModule *>::iterator add = modulesAdded.begin(); add != modulesAdded.end(); )
2946  {
2947  map<string, VuoCompilerModule *>::iterator rem = modulesRemoved.find(add->first);
2948  if (rem != modulesRemoved.end())
2949  {
2950  modulesModified[add->first] = make_pair(rem->second, add->second);
2951  modulesAdded.erase(add++);
2952  modulesRemoved.erase(rem);
2953  }
2954  else
2955  {
2956  ++add;
2957  }
2958  }
2959 
2960  if (! (modulesAdded.empty() && modulesModified.empty() && modulesRemoved.empty()) )
2961  {
2962  VuoCompilerIssues *issues = new VuoCompilerIssues();
2963  VuoCompilerDelegate::LoadedModulesData *delegateData = new VuoCompilerDelegate::LoadedModulesData(set< pair<VuoCompilerModule *, VuoCompilerModule *> >(), set<VuoCompilerModule *>(), issues);
2964  delegateData->retain();
2965 
2966  Environment *scopeEnvironment = newCompositionFamilyInstalledEnvironment;
2967  if (! scopeEnvironment) {
2968  scopeEnvironment = compositionEnvironments.at(0);
2969  }
2970 
2971  loadedModules(modulesAdded, modulesModified, modulesRemoved, issues, delegateData, scopeEnvironment);
2972  }
2973  }
2974  });
2975 }
2976 
2982 VuoDirectedAcyclicNetwork * VuoCompiler::makeDependencyNetwork(const vector< vector<Environment *> > &environments,
2983  VuoDirectedAcyclicGraph * (^graphForEnvironment)(Environment *))
2984 {
2985  if (!graphForEnvironment)
2986  return NULL;
2987 
2989 
2990  for (vector< vector<Environment *> >::const_iterator i = environments.begin(); i != environments.end(); ++i)
2991  {
2992  // Installed environment depends on generated environment in same scope.
2993 
2994  network->addEdge(graphForEnvironment(i->at(0)), graphForEnvironment(i->at(1)));
2995 
2996  // Installed and generated environments depend on installed environments in broader scopes.
2997 
2998  for (vector< vector<Environment *> >::const_iterator ii = environments.begin(); ii != i; ++ii)
2999  {
3000  network->addEdge(graphForEnvironment(i->at(0)), graphForEnvironment(ii->at(0)));
3001  network->addEdge(graphForEnvironment(i->at(1)), graphForEnvironment(ii->at(0)));
3002  }
3003  }
3004 
3005  return network;
3006 }
3007 
3028 void VuoCompiler::loadModulesIfNeeded(const set<string> &moduleKeys)
3029 {
3030  __block bool willLoadAllModules = false;
3031  if (moduleKeys.empty())
3032  {
3033  dispatch_sync(modulesToLoadQueue, ^{
3034  if (shouldLoadAllModules && ! hasLoadedAllModules) {
3035  willLoadAllModules = true;
3036  hasLoadedAllModules = true;
3037  }
3038  });
3039  }
3040 
3041  if (! willLoadAllModules && moduleKeys.empty())
3042  return;
3043 
3044  // Load modules and start sources compiling.
3045 
3046  dispatch_group_enter(moduleSourceCompilersExist);
3047  __block set<dispatch_group_t> sourcesLoading;
3048  dispatch_sync(environmentQueue, ^{
3049  sourcesLoading = loadModulesAndSources(moduleKeys, set<string>(), set<string>(),
3050  moduleKeys, set<string>(), set<string>(),
3051  willLoadAllModules, false, nullptr, nullptr, nullptr, "");
3052  });
3053 
3054  // Wait for subcompositions and specialized node classes to finish compiling and their modules to be loaded
3055  // to ensure that the next call to getNodeClass() will return them.
3056 
3057  for (set<dispatch_group_t>::iterator i = sourcesLoading.begin(); i != sourcesLoading.end(); ++i)
3058  {
3059  dispatch_group_wait(*i, DISPATCH_TIME_FOREVER);
3060  dispatch_release(*i);
3061  }
3062  dispatch_group_leave(moduleSourceCompilersExist);
3063 }
3064 
3073 set<dispatch_group_t> VuoCompiler::loadModulesAndSources(const set<string> &modulesAddedKeys, const set<string> &modulesModifiedKeys, const set<string> &modulesRemovedKeys,
3074  const set<string> &sourcesAddedKeys, const set<string> &sourcesModifiedKeys, const set<string> &sourcesRemovedKeys,
3075  bool willLoadAllModules, bool shouldRecompileSourcesIfUnchanged,
3076  Environment *currentEnvironment, VuoCompilerIssues *issuesForCurrentEnvironment,
3077  std::function<void(void)> moduleLoadedCallback, const string &moduleAddedOrModifiedSourceCode)
3078 {
3079  //VLog("C=%p E=%p -- %lu %lu %lu %lu %lu %lu %i %i", this, currentEnvironment,
3080  //modulesAddedKeys.size(), modulesModifiedKeys.size(), modulesRemovedKeys.size(),
3081  //sourcesAddedKeys.size(), sourcesModifiedKeys.size(), sourcesRemovedKeys.size(),
3082  //willLoadAllModules, shouldRecompileSourcesIfUnchanged);
3083  //if (modulesAddedKeys.size() == 1) VLog(" %s", modulesAddedKeys.begin()->c_str());
3084  //if (modulesModifiedKeys.size() == 1) VLog(" %s", modulesModifiedKeys.begin()->c_str());
3085  //if (modulesRemovedKeys.size() == 1) VLog(" %s", modulesRemovedKeys.begin()->c_str());
3086 
3087  // Organize the modules, source files, and issues by environment.
3088 
3089  map<Environment *, set<string> > modulesAdded;
3090  map<Environment *, set<string> > modulesModified;
3091  map<Environment *, set<string> > modulesRemoved;
3092  map<Environment *, set<string> > sourcesAdded;
3093  map<Environment *, set<string> > sourcesModified;
3094  map<Environment *, set<string> > sourcesRemoved;
3095  map<Environment *, set<string> > potentialSpecializedModules;
3096 
3097  if (currentEnvironment)
3098  {
3099  modulesAdded[currentEnvironment] = modulesAddedKeys;
3100  modulesModified[currentEnvironment] = modulesModifiedKeys;
3101  modulesRemoved[currentEnvironment] = modulesRemovedKeys;
3102  sourcesAdded[currentEnvironment] = sourcesAddedKeys;
3103  sourcesModified[currentEnvironment] = sourcesModifiedKeys;
3104  sourcesRemoved[currentEnvironment] = sourcesRemovedKeys;
3105  }
3106  else
3107  {
3108  Environment *genEnv = nullptr;
3109  if (! willLoadAllModules)
3110  {
3111  // If compiling a top-level composition, generated modules that were directly requested (in modulesAddedKeys) are
3112  // added at the composition scope so they won't be cached. This prevents link errors when running multiple
3113  // compositions in the current process (https://b33p.net/kosada/node/14317).
3114 
3115  int scope = environments.size() - (lastCompositionIsSubcomposition ? 2 : 1);
3116  genEnv = environments.at(scope).at(1);
3117  potentialSpecializedModules[genEnv] = modulesAddedKeys;
3118  }
3119 
3120  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3121  {
3122  Environment *env = (*i).at(0);
3123 
3124  ModuleInfoIterator modulesAddedIter = (willLoadAllModules ? env->listAllModules() : env->listModules(modulesAddedKeys));
3125  ModuleInfoIterator sourcesAddedIter = (willLoadAllModules ? env->listAllSourceFiles() : env->listSourceFiles(sourcesAddedKeys));
3126  ModuleInfoIterator sourcesModifiedIter = (willLoadAllModules ? env->listAllSourceFiles() : env->listSourceFiles(sourcesModifiedKeys));
3127 
3128  ModuleInfo *moduleInfo;
3129  while ((moduleInfo = modulesAddedIter.next()))
3130  {
3131  string moduleKey = moduleInfo->getModuleKey();
3132 
3133  modulesAdded[env].insert(moduleKey);
3134 
3135  if (! willLoadAllModules)
3136  {
3137  auto foundIter = potentialSpecializedModules[genEnv].find(moduleKey);
3138  if (foundIter != potentialSpecializedModules[genEnv].end())
3139  potentialSpecializedModules[genEnv].erase(foundIter);
3140  }
3141  }
3142 
3143  // If a source file and a compiled file for the same module are in the same folder,
3144  // the compiled file takes precedence.
3145  auto isCompiledModuleAtSameSearchPath = [&env] (ModuleInfo *sourceInfo)
3146  {
3147  ModuleInfo *compiledModuleInfo = env->listModule(sourceInfo->getModuleKey());
3148  return (compiledModuleInfo && compiledModuleInfo->getSearchPath() == sourceInfo->getSearchPath());
3149  };
3150 
3151  while ((moduleInfo = sourcesAddedIter.next()))
3152  {
3153  if (isCompiledModuleAtSameSearchPath(moduleInfo))
3154  continue;
3155 
3156  sourcesAdded[env].insert( moduleInfo->getModuleKey() );
3157  }
3158 
3159  while ((moduleInfo = sourcesModifiedIter.next()))
3160  {
3161  if (isCompiledModuleAtSameSearchPath(moduleInfo))
3162  continue;
3163 
3164  sourcesModified[env].insert( moduleInfo->getModuleKey() );
3165  }
3166  }
3167  }
3168 
3169  map<Environment *, VuoCompilerIssues *> issues;
3170  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3171  {
3172  Environment *env = (*i).at(0);
3173  issues[env] = (env == currentEnvironment && issuesForCurrentEnvironment ? issuesForCurrentEnvironment : new VuoCompilerIssues());
3174  }
3175 
3176  // Check for circular dependencies in added/modified sources.
3177 
3178  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3179  {
3180  Environment *env = (*i).at(0);
3181 
3182  // Check for circular dependencies involving sources being loaded within this environment.
3183  // For circular dependencies involving sources in different environments,
3184  // an error will be reported elsewhere due to one of them being outside of the other's scope.
3185  set<VuoDirectedAcyclicGraph::Vertex *> circularDependencies = env->getCompositionDependencyGraph()->getCycleVertices();
3186 
3187  set<string> sourcesAddedModified;
3188  sourcesAddedModified.insert(sourcesAdded[env].begin(), sourcesAdded[env].end());
3189  sourcesAddedModified.insert(sourcesModified[env].begin(), sourcesModified[env].end());
3190 
3191  for (set<string>::iterator j = sourcesAddedModified.begin(); j != sourcesAddedModified.end(); ++j)
3192  {
3193  string moduleKey = *j;
3194 
3195  bool found = false;
3196  for (set<VuoDirectedAcyclicGraph::Vertex *>::iterator k = circularDependencies.begin(); k != circularDependencies.end(); ++k)
3197  {
3198  DependencyGraphVertex *vertex = static_cast<DependencyGraphVertex *>(*k);
3199  if (vertex->getDependency() == moduleKey)
3200  {
3201  found = true;
3202  break;
3203  }
3204  }
3205 
3206  if (found)
3207  {
3208  sourcesAdded[env].erase(moduleKey);
3209  sourcesModified[env].erase(moduleKey);
3210 
3211  VuoCompilerIssue issue(VuoCompilerIssue::Error, "compiling subcomposition", moduleKey,
3212  "Subcomposition contains itself",
3213  "%moduleKey contains an instance of itself, "
3214  "or contains another subcomposition that contains an instance of %moduleKey.");
3215  issue.setModuleKey(moduleKey);
3216 
3217  ModuleInfo *sourceInfo = env->listSourceFile(moduleKey);
3218  if (sourceInfo)
3219  issue.setFilePath(sourceInfo->getFile()->path());
3220 
3221  issues[env]->append(issue);
3222  }
3223  }
3224  }
3225 
3226  // Update the longest downstream paths for added/modified sources.
3227 
3228  for (const vector<Environment *> &envs : environments)
3229  {
3230  Environment *env = envs.at(0);
3231 
3232  set<string> sourcesAddedModified;
3233  sourcesAddedModified.insert(sourcesAdded[env].begin(), sourcesAdded[env].end());
3234  sourcesAddedModified.insert(sourcesModified[env].begin(), sourcesModified[env].end());
3235 
3236  for (const string &moduleKey : sourcesAddedModified)
3237  {
3238  VuoDirectedAcyclicGraph::Vertex *vertex = env->getCompositionDependencyGraph()->findVertex(moduleKey);
3239  int pathLength = env->getCompositionDependencyGraph()->getLongestDownstreamPath(vertex);
3240 
3241  ModuleInfo *sourceInfo = env->listSourceFile(moduleKey);
3242  sourceInfo->setLongestDownstreamPath(pathLength);
3243  }
3244  }
3245 
3246  // Find all modules and sources that depend on the modules and sources being modified or removed.
3247  // Those that belong to one of this compiler's environments are used in subsequent stages.
3248  // Those that belong to some other environment are scheduled to be handled by a separate call to this function.
3249 
3250  map<Environment *, set<string> > modulesDepOnModulesModified;
3251  map<Environment *, set<string> > sourcesDepOnModulesModified;
3252  map<Environment *, set<string> > modulesDepOnModulesRemoved;
3253  map<Environment *, set<string> > sourcesDepOnModulesRemoved;
3254 
3255  {
3256  __block map<Environment *, set<string> > modulesDepOnModulesModified_otherCompiler;
3257  __block map<Environment *, set<string> > sourcesDepOnModulesModified_otherCompiler;
3258  __block map<Environment *, set<string> > modulesDepOnModulesRemoved_otherCompiler;
3259  __block map<Environment *, set<string> > sourcesDepOnModulesRemoved_otherCompiler;
3260 
3261  vector<VuoDirectedAcyclicNetwork *> searchDependencyGraphs;
3262  for (VuoCompiler *compiler : allCompilers)
3263  searchDependencyGraphs.push_back(compiler->dependencyGraph);
3264 
3265  VuoDirectedAcyclicGraph *currentEnvironmentDependencyGraph = (currentEnvironment ? currentEnvironment->getDependencyGraph() : nullptr);
3266 
3267  findDependentModulesAndSources(modulesModified, searchDependencyGraphs, currentEnvironmentDependencyGraph,
3268  modulesDepOnModulesModified, modulesDepOnModulesModified_otherCompiler,
3269  sourcesDepOnModulesModified, sourcesDepOnModulesModified_otherCompiler);
3270 
3271  findDependentModulesAndSources(modulesRemoved, searchDependencyGraphs, currentEnvironmentDependencyGraph,
3272  modulesDepOnModulesRemoved, modulesDepOnModulesRemoved_otherCompiler,
3273  sourcesDepOnModulesRemoved, sourcesDepOnModulesRemoved_otherCompiler);
3274 
3275  set<Environment *> otherEnvironments;
3276  for (map<Environment *, set<string> >::iterator i = modulesDepOnModulesModified_otherCompiler.begin(); i != modulesDepOnModulesModified_otherCompiler.end(); ++i)
3277  otherEnvironments.insert(i->first);
3278  for (map<Environment *, set<string> >::iterator i = sourcesDepOnModulesModified_otherCompiler.begin(); i != sourcesDepOnModulesModified_otherCompiler.end(); ++i)
3279  otherEnvironments.insert(i->first);
3280  for (map<Environment *, set<string> >::iterator i = modulesDepOnModulesRemoved_otherCompiler.begin(); i != modulesDepOnModulesRemoved_otherCompiler.end(); ++i)
3281  otherEnvironments.insert(i->first);
3282  for (map<Environment *, set<string> >::iterator i = sourcesDepOnModulesRemoved_otherCompiler.begin(); i != sourcesDepOnModulesRemoved_otherCompiler.end(); ++i)
3283  otherEnvironments.insert(i->first);
3284 
3285  for (Environment *env : otherEnvironments)
3286  {
3287  VuoCompiler *otherCompiler = nullptr;
3288  for (VuoCompiler *c : allCompilers)
3289  for (const vector<Environment *> &ee : c->environments)
3290  for (Environment *e : ee)
3291  if (e == env)
3292  {
3293  otherCompiler = c;
3294  goto foundOtherCompiler;
3295  }
3296  foundOtherCompiler:
3297 
3298  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
3299  dispatch_sync(environmentQueue, ^{
3300  otherCompiler->loadModulesAndSources(set<string>(), modulesDepOnModulesModified_otherCompiler[env], modulesDepOnModulesRemoved_otherCompiler[env],
3301  set<string>(), sourcesDepOnModulesModified_otherCompiler[env], sourcesDepOnModulesRemoved_otherCompiler[env],
3302  false, true, env, nullptr, nullptr, "");
3303  });
3304  });
3305  }
3306  }
3307 
3308  // Unload:
3309  // - modules that have been removed or modified
3310  // - modules that depend on them
3311 
3312  map<Environment *, set<VuoCompilerModule *> > actualModulesRemoved;
3313  for (const vector<Environment *> &envs : environments)
3314  {
3315  for (Environment *env : envs)
3316  {
3317  set<string> modulesToUnload;
3318  modulesToUnload.insert(modulesRemoved[env].begin(), modulesRemoved[env].end());
3319  modulesToUnload.insert(modulesModified[env].begin(), modulesModified[env].end());
3320  modulesToUnload.insert(modulesDepOnModulesRemoved[env].begin(), modulesDepOnModulesRemoved[env].end());
3321  modulesToUnload.insert(modulesDepOnModulesModified[env].begin(), modulesDepOnModulesModified[env].end());
3322 
3323  actualModulesRemoved[env] = env->unloadCompiledModules(modulesToUnload);
3324 
3325  if (!env->isBuiltInOriginal() && !actualModulesRemoved[env].empty())
3326  {
3327  set<string> actualModulesRemovedKeys;
3328  for (auto m : actualModulesRemoved[env])
3329  actualModulesRemovedKeys.insert(m->getPseudoBase()->getModuleKey());
3330  VUserLog("Removed from %s environment: %s", env->getName().c_str(), VuoStringUtilities::join(actualModulesRemovedKeys, ", ").c_str());
3331  }
3332  }
3333  }
3334 
3335  // Load:
3336  // - modules that have been added or modified
3337  // - modules that they depend on
3338  // - modules that depend on them that were just unloaded
3339  // Delete:
3340  // - cached module files in `modulesToLoad` whose source files have been removed
3341 
3342  map<Environment *, set<string> > modulesToLoad;
3343  map<Environment *, map<string, string> > modulesToLoadSourceCode;
3344  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3345  {
3346  Environment *env = (*i).at(0);
3347 
3348  if (! modulesAdded[env].empty())
3349  modulesToLoad[env].insert(modulesAdded[env].begin(), modulesAdded[env].end());
3350  if (! modulesModified[env].empty())
3351  modulesToLoad[env].insert(modulesModified[env].begin(), modulesModified[env].end());
3352  if (! modulesDepOnModulesModified[env].empty())
3353  modulesToLoad[env].insert(modulesDepOnModulesModified[env].begin(), modulesDepOnModulesModified[env].end());
3354 
3355  if (env == currentEnvironment && moduleLoadedCallback)
3356  {
3357  if (modulesAdded[env].size() == 1)
3358  modulesToLoadSourceCode[env][*modulesAdded[env].begin()] = moduleAddedOrModifiedSourceCode;
3359  else if (modulesModified[env].size() == 1)
3360  modulesToLoadSourceCode[env][*modulesModified[env].begin()] = moduleAddedOrModifiedSourceCode;
3361  }
3362  }
3363 
3364  map<Environment *, set<VuoCompilerModule *> > actualModulesAdded;
3365  while (! modulesToLoad.empty())
3366  {
3367  set<string> dependenciesToLoad;
3368  map<Environment *, set<string> > potentialSpecializedDependencies;
3369  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3370  {
3371  Environment *env = (*i).at(0);
3372  Environment *genEnv = (*i).at(1);
3373 
3374  set<VuoCompilerModule *> actualModulesLoaded = env->loadCompiledModules(modulesToLoad[env], modulesToLoadSourceCode[env]);
3375 
3376  actualModulesAdded[env].insert(actualModulesLoaded.begin(), actualModulesLoaded.end());
3377  modulesToLoad.erase(env);
3378 
3379  for (set<VuoCompilerModule *>::iterator j = actualModulesLoaded.begin(); j != actualModulesLoaded.end(); ++j)
3380  {
3381  set<string> dependencies = (*j)->getDependencies();
3382  dependenciesToLoad.insert(dependencies.begin(), dependencies.end());
3383  potentialSpecializedDependencies[genEnv].insert(dependencies.begin(), dependencies.end());
3384  }
3385 
3386  if (!env->isBuiltInOriginal() && !actualModulesLoaded.empty())
3387  {
3388  map<string, string> actualFilesAndHashesLoaded;
3389  for (auto module : actualModulesLoaded)
3390  {
3391  string path, hash;
3392  if (module->getPseudoBase()->getNodeSet())
3393  path = module->getPseudoBase()->getNodeSet()->getArchivePath();
3394  else
3395  {
3396  auto cnc = dynamic_cast<VuoCompilerNodeClass *>(module);
3397  if (cnc && !cnc->getSourcePath().empty())
3398  {
3399  path = cnc->getSourcePath();
3400  if (!cnc->getSourceCode().empty())
3401  // Use the latest source code, if it's been modified without saving.
3402  hash = VuoStringUtilities::calculateSHA256(cnc->getSourceCode());
3403  }
3404  else
3405  path = module->getModulePath();
3406  }
3407 
3408  if (hash.empty())
3409  try
3410  {
3412  }
3413  catch (VuoException &e) {}
3414 
3415  actualFilesAndHashesLoaded[path] = hash;
3416  }
3417 
3418  for (pair<string, string> item : actualFilesAndHashesLoaded)
3419  VUserLog("Loaded into %s environment: [%8.8s] %s", env->getName().c_str(), item.second.c_str(), item.first.c_str());
3420  }
3421  }
3422 
3423  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3424  {
3425  Environment *env = (*i).at(0);
3426 
3427  ModuleInfoIterator dependenciesInEnv = env->listModules(dependenciesToLoad);
3428  ModuleInfo *moduleInfo;
3429  while ((moduleInfo = dependenciesInEnv.next()))
3430  {
3431  modulesToLoad[env].insert( moduleInfo->getModuleKey() );
3432 
3433  for (map<Environment *, set<string> >::iterator j = potentialSpecializedDependencies.begin(); j != potentialSpecializedDependencies.end(); ++j)
3434  {
3435  auto foundIter = j->second.find( moduleInfo->getModuleKey() );
3436  if (foundIter != j->second.end())
3437  j->second.erase(foundIter);
3438  }
3439  }
3440  }
3441 
3442  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3443  {
3444  Environment *genEnv = (*i).at(1);
3445  potentialSpecializedModules[genEnv].insert(potentialSpecializedDependencies[genEnv].begin(), potentialSpecializedDependencies[genEnv].end());
3446  }
3447  }
3448 
3449  // Load asynchronously:
3450  // - specializations of generic modules
3451 
3452  set<dispatch_group_t> specializedModulesLoading;
3453  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3454  {
3455  Environment *genEnv = (*i).at(1);
3456  set<dispatch_group_t> s = genEnv->loadSpecializedModules(potentialSpecializedModules[genEnv], this, llvmQueue);
3457  specializedModulesLoading.insert(s.begin(), s.end());
3458  }
3459 
3460  // Notify those waiting on a source file to be compiled that its module has now been loaded.
3461 
3462  if (moduleLoadedCallback)
3463  moduleLoadedCallback();
3464 
3465  // Move modified modules from `actualModulesAdded` and `actualModulesRemoved` to `actualModulesModified`.
3466 
3467  map<Environment *, set< pair<VuoCompilerModule *, VuoCompilerModule *> > > actualModulesModified;
3468  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3469  {
3470  Environment *env = (*i).at(0);
3471 
3472  for (set<VuoCompilerModule *>::iterator add = actualModulesAdded[env].begin(); add != actualModulesAdded[env].end(); )
3473  {
3474  set<VuoCompilerModule *>::iterator rem;
3475  for (rem = actualModulesRemoved[env].begin(); rem != actualModulesRemoved[env].end(); ++rem)
3476  if ((*rem)->getPseudoBase()->getModuleKey() == (*add)->getPseudoBase()->getModuleKey())
3477  break;
3478 
3479  if (rem != actualModulesRemoved[env].end())
3480  {
3481  actualModulesModified[env].insert( make_pair(*rem, *add) );
3482  actualModulesRemoved[env].erase(rem);
3483  actualModulesAdded[env].erase(add++);
3484  }
3485  else
3486  ++add;
3487  }
3488  }
3489 
3490  // Reify port types on node classes (if needed).
3491 
3492  bool wereModulesAddedOrModified = false;
3493  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3494  {
3495  Environment *env = (*i).at(0);
3496  if (! (actualModulesAdded[env].empty() && actualModulesModified[env].empty()) )
3497  {
3498  wereModulesAddedOrModified = true;
3499  break;
3500  }
3501  }
3502 
3503  if (wereModulesAddedOrModified)
3504  {
3505  map<string, VuoCompilerType *> inheritedTypes;
3506  for (const vector<Environment *> &envs : environments)
3507  {
3508  for (Environment *env : envs)
3509  {
3510  env->reifyPortTypes(inheritedTypes);
3511  map<string, VuoCompilerType *> envTypes = env->getTypes();
3512  inheritedTypes.insert(envTypes.begin(), envTypes.end());
3513  }
3514  }
3515  }
3516 
3517  // Delete cached compiled module files and call this function recursively for:
3518  // - modules whose source files have been removed
3519  // - modules whose source files depend on them
3520 
3521  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3522  {
3523  Environment *env = (*i).at(0);
3524 
3525  set<string> sourcesToUnload;
3526  sourcesToUnload.insert(sourcesRemoved[env].begin(), sourcesRemoved[env].end());
3527  sourcesToUnload.insert(sourcesDepOnModulesRemoved[env].begin(), sourcesDepOnModulesRemoved[env].end());
3528  if (! sourcesToUnload.empty())
3529  {
3530  string moduleSearchPath = env->getModuleSearchPaths().front();
3531 
3532  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
3533  VuoCompiler *otherCompiler = new VuoCompiler(moduleSearchPath + "/unused");
3534 
3535  dispatch_sync(environmentQueue, ^{
3536  otherCompiler->loadModulesAndSources(set<string>(), set<string>(), sourcesToUnload,
3537  set<string>(), set<string>(), set<string>(),
3538  false, false, env, nullptr, nullptr, "");
3539  });
3540 
3541  delete otherCompiler;
3542  });
3543  }
3544 
3545  if (!env->isBuiltInOriginal() && !sourcesToUnload.empty())
3546  VUserLog("Deleting from %s environment: %s", env->getName().c_str(), VuoStringUtilities::join(sourcesToUnload, ", ").c_str());
3547 
3548  env->deleteModulesCompiledFromSourceCode(sourcesToUnload);
3549  }
3550 
3551  // Compile asynchronously:
3552  // - source files that have been added or modified
3553  // - source files that depend on them
3554  // - source files that depend on modules that have been modified
3555 
3556  map<Environment *, set<string> > sourcesDepOnModulesAdded;
3557  {
3558  map<Environment *, set<string> > modulesDepOnModulesAdded; // unused
3559  __block map<Environment *, set<string> > modulesDepOnModulesAdded_otherCompiler; //
3560  __block map<Environment *, set<string> > sourcesDepOnModulesAdded_otherCompiler;
3561 
3562  map<Environment *, set<string> > actualModuleKeysAdded;
3563  for (const vector<Environment *> &envs : environments)
3564  {
3565  Environment *env = envs.at(0);
3566  for (VuoCompilerModule *module : actualModulesAdded[env])
3567  actualModuleKeysAdded[env].insert( module->getPseudoBase()->getModuleKey() );
3568  }
3569 
3570  vector<VuoDirectedAcyclicNetwork *> searchDependencyGraphs;
3571  searchDependencyGraphs.push_back(compositionDependencyGraph);
3572  for (map<string, vector<Environment *> >::iterator ii = environmentsForCompositionFamily.begin(); ii != environmentsForCompositionFamily.end(); ++ii)
3573  {
3574  vector< vector<Environment *> > otherEnvs = sharedEnvironments[target];
3575  otherEnvs.push_back(ii->second);
3576  VuoDirectedAcyclicNetwork *other = makeDependencyNetwork(otherEnvs, ^VuoDirectedAcyclicGraph * (Environment *env) { return env->getCompositionDependencyGraph(); });
3577  searchDependencyGraphs.push_back(other);
3578  }
3579 
3580  VuoDirectedAcyclicGraph *currentEnvironmentDependencyGraph = (currentEnvironment ? currentEnvironment->getCompositionDependencyGraph() : nullptr);
3581 
3582  findDependentModulesAndSources(actualModuleKeysAdded, searchDependencyGraphs, currentEnvironmentDependencyGraph,
3583  modulesDepOnModulesAdded, modulesDepOnModulesAdded_otherCompiler,
3584  sourcesDepOnModulesAdded, sourcesDepOnModulesAdded_otherCompiler);
3585 
3586  set<Environment *> otherEnvironments;
3587  for (map<Environment *, set<string> >::iterator i = sourcesDepOnModulesAdded_otherCompiler.begin(); i != sourcesDepOnModulesAdded_otherCompiler.end(); ++i)
3588  otherEnvironments.insert(i->first);
3589 
3590  for (Environment *env : otherEnvironments)
3591  {
3592  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
3593  string moduleSearchPath = env->getModuleSearchPaths().front();
3594  VuoCompiler *otherCompiler = new VuoCompiler(moduleSearchPath + "/unused");
3595 
3596  dispatch_sync(environmentQueue, ^{
3597  otherCompiler->loadModulesAndSources(set<string>(), set<string>(), set<string>(),
3598  sourcesDepOnModulesAdded_otherCompiler[env], set<string>(), set<string>(),
3599  false, true, env, nullptr, nullptr, "");
3600  });
3601 
3602  delete otherCompiler;
3603  });
3604  }
3605  }
3606 
3607  set<dispatch_group_t> sourcesLoading;
3608  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3609  {
3610  Environment *env = (*i).at(0);
3611 
3612  set<string> sourcesToCompile;
3613  sourcesToCompile.insert(sourcesAdded[env].begin(), sourcesAdded[env].end());
3614  sourcesToCompile.insert(sourcesModified[env].begin(), sourcesModified[env].end());
3615 
3616  if (sourcesToCompile.size() == 0)
3617  continue;
3618 
3619  set<dispatch_group_t> s = env->compileModulesFromSourceCode(sourcesToCompile, shouldRecompileSourcesIfUnchanged);
3620  sourcesLoading.insert(s.begin(), s.end());
3621  }
3622 
3623  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3624  {
3625  Environment *env = (*i).at(0);
3626 
3627  set<string> sourcesToCompile;
3628  sourcesToCompile.insert(sourcesDepOnModulesAdded[env].begin(), sourcesDepOnModulesAdded[env].end());
3629  sourcesToCompile.insert(sourcesDepOnModulesModified[env].begin(), sourcesDepOnModulesModified[env].end());
3630 
3631  if (sourcesToCompile.size() == 0)
3632  continue;
3633 
3634  env->compileModulesFromSourceCode(sourcesToCompile, true);
3635  }
3636 
3637  // Notify compiler delegates.
3638 
3639  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3640  {
3641  Environment *env = (*i).at(0);
3642  env->notifyCompilers(actualModulesAdded[env], actualModulesModified[env], actualModulesRemoved[env], issues[env]);
3643  }
3644 
3645  // Since the dispatch groups for specialized modules are temporary (caller is responsible for releasing them)
3646  // but the dispatch groups for module sources should stay alive as long as the ModuleInfo that contains them,
3647  // retain the dispatch groups for module sources so that all dispatch groups can be safely released by the caller.
3648 
3649  for (const dispatch_group_t &group : sourcesLoading)
3650  dispatch_retain(group);
3651 
3652  set<dispatch_group_t> loadingGroups;
3653  loadingGroups.insert(specializedModulesLoading.begin(), specializedModulesLoading.end());
3654  loadingGroups.insert(sourcesLoading.begin(), sourcesLoading.end());
3655  return loadingGroups;
3656 }
3657 
3669 void VuoCompiler::findDependentModulesAndSources(map<Environment *, set<string> > &changedModules,
3670  const vector<VuoDirectedAcyclicNetwork *> &searchDependencyGraphs,
3671  VuoDirectedAcyclicGraph *currentEnvironmentDependencyGraph,
3672  map<Environment *, set<string> > &modulesDepOnChangedModules_this,
3673  map<Environment *, set<string> > &modulesDepOnChangedModules_other,
3674  map<Environment *, set<string> > &sourcesDepOnChangedModules_this,
3675  map<Environment *, set<string> > &sourcesDepOnChangedModules_other)
3676 {
3677  for (const vector<Environment *> &envs : environments)
3678  {
3679  Environment *env = envs.at(0);
3680 
3681  for (const string &module : changedModules[env])
3682  {
3683  set<VuoDirectedAcyclicGraph::Vertex *> dependents;
3684  for (VuoDirectedAcyclicNetwork *searchDependencyGraph : searchDependencyGraphs)
3685  {
3686  vector<VuoDirectedAcyclicGraph::Vertex *> moduleVertices;
3687  if (currentEnvironmentDependencyGraph)
3688  {
3689  // If a module with the same module key is installed in multiple locations,
3690  // only consider the one being modified or removed.
3691  VuoDirectedAcyclicGraph::Vertex *mv = currentEnvironmentDependencyGraph->findVertex(module);
3692  if (mv)
3693  moduleVertices.push_back(mv);
3694  }
3695  else
3696  moduleVertices = searchDependencyGraph->findVertex(module);
3697 
3698  for (VuoDirectedAcyclicGraph::Vertex *moduleVertexRaw : moduleVertices)
3699  {
3700  DependencyGraphVertex *moduleVertex = static_cast<DependencyGraphVertex *>(moduleVertexRaw);
3701  if (moduleVertex->getEnvironment())
3702  {
3703  vector<VuoDirectedAcyclicGraph::Vertex *> upstream = searchDependencyGraph->getUpstreamVertices(moduleVertex);
3704  dependents.insert(upstream.begin(), upstream.end());
3705  }
3706  }
3707  }
3708 
3709  set< pair<Environment *, string> > dependentsMap;
3710  for (VuoDirectedAcyclicGraph::Vertex *dependentVertexRaw : dependents)
3711  {
3712  DependencyGraphVertex *v = static_cast<DependencyGraphVertex *>(dependentVertexRaw);
3713  Environment *dependentEnv = v->getEnvironment();
3714  if (! dependentEnv)
3715  continue;
3716 
3717  string dependent = v->getDependency();
3718 
3719  dependentsMap.insert({dependentEnv, dependent});
3720  }
3721 
3722  // In case `module` is a generic node class, check the generated environment at the same scope for any
3723  // specializations of the node class, and add them to the list of dependencies.
3724  // (They aren't in the dependency graph since the graph edge goes from installed to generated.)
3725  for (auto i : envs.at(1)->getNodeClasses())
3726  {
3727  VuoCompilerSpecializedNodeClass *specializedNodeClass = dynamic_cast<VuoCompilerSpecializedNodeClass *>(i.second);
3728  if (specializedNodeClass->getOriginalGenericNodeClassName() == module)
3729  dependentsMap.insert({envs.at(1), i.first});
3730  }
3731 
3732  for (auto i : dependentsMap)
3733  {
3734  Environment *dependentEnv = i.first;
3735  string dependent = i.second;
3736 
3737  // Skip if the dependent module is already being modified/removed in its own right
3738  // (e.g. if the module depends on another in the same node set and the node set is being removed).
3739  if (changedModules[dependentEnv].find(dependent) != changedModules[dependentEnv].end())
3740  continue;
3741 
3742  ModuleInfo *foundSourceInfo = dependentEnv->listSourceFile(dependent);
3743  ModuleInfo *foundModuleInfo = dependentEnv->listModule(dependent);
3744 
3745  bool belongsToCurrentCompiler = false;
3746  for (const vector<Environment *> &envs2 : environments)
3747  {
3748  if (find(envs2.begin(), envs2.end(), dependentEnv) != envs2.end())
3749  {
3750  belongsToCurrentCompiler = true;
3751  break;
3752  }
3753  }
3754 
3755  map<Environment *, set<string> > *whicheverDependents = nullptr;
3756  ModuleInfo *moduleInfo = nullptr;
3757  if (foundSourceInfo)
3758  {
3759  moduleInfo = foundSourceInfo;
3760  whicheverDependents = (belongsToCurrentCompiler ? &sourcesDepOnChangedModules_this : &sourcesDepOnChangedModules_other);
3761  }
3762  else if (foundModuleInfo)
3763  {
3764  moduleInfo = foundModuleInfo;
3765  whicheverDependents = (belongsToCurrentCompiler ? &modulesDepOnChangedModules_this : &modulesDepOnChangedModules_other);
3766  }
3767  else // Module in generated environment
3768  {
3769  whicheverDependents = (belongsToCurrentCompiler ? &modulesDepOnChangedModules_this : &modulesDepOnChangedModules_other);
3770  }
3771 
3772  (*whicheverDependents)[dependentEnv].insert(dependent);
3773  if (moduleInfo)
3774  moduleInfo->setAttempted(false);
3775  }
3776  }
3777  }
3778 }
3779 
3783 void VuoCompiler::loadedModules(map<string, VuoCompilerModule *> modulesAdded,
3784  map<string, pair<VuoCompilerModule *, VuoCompilerModule *> > modulesModified,
3785  map<string, VuoCompilerModule *> modulesRemoved,
3786  VuoCompilerIssues *issues, void *delegateDataV, Environment *currentEnvironment)
3787 {
3788  //VLog("C=%p %lu %lu %lu", this, modulesAdded.size(), modulesModified.size(), modulesRemoved.size());
3789 
3790  // If a module is added, superseding a version of the same module installed at a broader scope,
3791  // notify this VuoCompiler that the module has been modified rather than added.
3792  //
3793  // If a module is added, but a version of the same module is already installed at a narrower scope,
3794  // don't notify this VuoCompiler, since it will continue to use the version at the narrower scope.
3795  //
3796  // Same idea when a module is modified or removed while another version is installed at a different scope.
3797 
3798  auto findVersionsOfModule = [this, currentEnvironment] (const string &moduleKey)
3799  {
3800  vector< pair<Environment *, VuoCompilerModule *> > moduleVersions;
3801  for (const vector<Environment *> &envs : environments)
3802  {
3803  Environment *env = envs.at(0);
3804  VuoCompilerModule *module = env->findModule(moduleKey);
3805  if (module || env == currentEnvironment)
3806  moduleVersions.push_back( make_pair(env, module) );
3807  }
3808  return moduleVersions;
3809  };
3810 
3811  for (map<string, VuoCompilerModule *>::iterator i = modulesAdded.begin(); i != modulesAdded.end(); )
3812  {
3813  string moduleKey = i->first;
3814  VuoCompilerModule *moduleAdded = i->second;
3815 
3816  vector< pair<Environment *, VuoCompilerModule *> > moduleVersions = findVersionsOfModule(moduleKey);
3817 
3818  if (moduleVersions.size() > 1)
3819  {
3820  modulesAdded.erase(i++);
3821 
3822  if (moduleVersions.back().second == moduleAdded)
3823  {
3824  VuoCompilerModule *moduleSuperseded = moduleVersions.at(moduleVersions.size()-2).second;
3825  modulesModified[moduleKey] = make_pair(moduleSuperseded, moduleAdded);
3826  }
3827  }
3828  else
3829  ++i;
3830  }
3831 
3832  for (map<string, pair<VuoCompilerModule *, VuoCompilerModule *> >::iterator i = modulesModified.begin(); i != modulesModified.end(); )
3833  {
3834  string moduleKey = i->first;
3835  VuoCompilerModule *moduleModified = i->second.second;
3836 
3837  vector< pair<Environment *, VuoCompilerModule *> > moduleVersions = findVersionsOfModule(moduleKey);
3838 
3839  if (moduleVersions.size() > 1 && moduleVersions.back().second != moduleModified)
3840  modulesModified.erase(i++);
3841  else
3842  ++i;
3843  }
3844 
3845  for (map<string, VuoCompilerModule *>::iterator i = modulesRemoved.begin(); i != modulesRemoved.end(); )
3846  {
3847  string moduleKey = i->first;
3848  VuoCompilerModule *moduleRemoved = i->second;
3849 
3850  vector< pair<Environment *, VuoCompilerModule *> > moduleVersions = findVersionsOfModule(moduleKey);
3851 
3852  if (moduleVersions.size() > 1)
3853  {
3854  modulesRemoved.erase(i++);
3855 
3856  if (moduleVersions.back().first == currentEnvironment)
3857  {
3858  VuoCompilerModule *moduleUnsuperseded = moduleVersions.at(moduleVersions.size()-2).second;
3859  modulesModified[moduleKey] = make_pair(moduleRemoved, moduleUnsuperseded);
3860  }
3861  }
3862  else
3863  ++i;
3864  }
3865 
3866  dispatch_async(delegateQueue, ^{
3867  VuoCompilerDelegate::LoadedModulesData *delegateData = static_cast<VuoCompilerDelegate::LoadedModulesData *>(delegateDataV);
3868 
3869  if (delegate && ! (modulesAdded.empty() && modulesModified.empty() && modulesRemoved.empty() && issues->isEmpty()))
3870  {
3871  delegate->enqueueData(delegateData);
3872  delegate->loadedModules(modulesAdded, modulesModified, modulesRemoved, issues);
3873  }
3874  else
3875  {
3876  delegateData->release();
3877  }
3878  });
3879 }
3880 
3886 void VuoCompiler::loadNodeClassGeneratedAtRuntime(VuoCompilerNodeClass *nodeClass, Environment *env)
3887 {
3888  Module *module = nodeClass->getModule();
3889  if (module)
3890  {
3891  dispatch_sync(llvmQueue, ^{
3892  setTargetForModule(nodeClass->getModule(), env->getTarget());
3893  });
3894  }
3895 
3896  dispatch_sync(environmentQueue, ^{
3897  env->replaceNodeClass(nodeClass);
3898  });
3899 
3900  __block map<string, VuoCompilerType *> inheritedTypes;
3901  void (^envReifyPortTypes)(Environment *) = ^void (Environment *env) {
3902  env->reifyPortTypes(inheritedTypes);
3903  map<string, VuoCompilerType *> currentTypes = env->getTypes();
3904  inheritedTypes.insert(currentTypes.begin(), currentTypes.end());
3905  };
3906  applyToAllEnvironments(envReifyPortTypes);
3907 }
3908 
3912 void VuoCompiler::reifyGenericPortTypes(VuoCompilerComposition *composition)
3913 {
3914  for (VuoCompilerNode *node : composition->getCachedGraph(this)->getNodes())
3915  reifyGenericPortTypes(node->getBase());
3916 
3917  composition->invalidateCachedGraph();
3918 }
3919 
3923 void VuoCompiler::reifyGenericPortTypes(VuoNode *node)
3924 {
3926  if (! nodeClass)
3927  return;
3928 
3929  // Reify any generic types on the node that don't already have a compiler detail.
3930 
3931  vector<VuoPort *> inputPorts = node->getInputPorts();
3932  vector<VuoPort *> outputPorts = node->getOutputPorts();
3933  vector<VuoPort *> ports;
3934  ports.insert(ports.end(), inputPorts.begin(), inputPorts.end());
3935  ports.insert(ports.end(), outputPorts.begin(), outputPorts.end());
3936 
3937  for (vector<VuoPort *>::iterator j = ports.begin(); j != ports.end(); ++j)
3938  {
3939  VuoCompilerPort *port = static_cast<VuoCompilerPort *>((*j)->getCompiler());
3940  VuoGenericType *genericType = dynamic_cast<VuoGenericType *>(port->getDataVuoType());
3941  if (! genericType)
3942  continue;
3943 
3944  if (! genericType->hasCompiler())
3945  {
3946  VuoCompilerType * (^compilerGetType)(string) = ^VuoCompilerType * (string moduleKey) {
3947  return getType(moduleKey);
3948  };
3949 
3950  VuoCompilerGenericType *reifiedType = VuoCompilerGenericType::newGenericType(genericType, compilerGetType);
3951  if (reifiedType)
3952  port->setDataVuoType(reifiedType->getBase());
3953  }
3954  }
3955 
3956  // Update the node class's backing to match the node's backing.
3957 
3958  nodeClass->updateBackingNodeClass(node, this);
3959 }
3960 
3967 void VuoCompiler::compileModule(string inputPath, string outputPath)
3968 {
3969  compileModule(inputPath, outputPath, vector<string>());
3970 }
3971 
3979 void VuoCompiler::compileModule(string inputPath, string outputPath, const vector<string> &includePaths)
3980 {
3981  if (isVerbose)
3982  print();
3983 
3984  vector<string> allIncludePaths = includePaths;
3985  string preprocessedInputPath = inputPath;
3986 
3987  string tmpPreprocessedInputDir;
3988  string dir, file, ext;
3989  VuoFileUtilities::splitPath(inputPath, dir, file, ext);
3991  {
3992  string inputContents = VuoFileUtilities::readFileToString(inputPath);
3993  string preprocessedInputContents = inputContents;
3995  if (inputContents != preprocessedInputContents)
3996  {
3997  // Unspecialized generic node class
3998  allIncludePaths.push_back(dir.empty() ? "." : dir);
3999  tmpPreprocessedInputDir = VuoFileUtilities::makeTmpDir(file);
4000  preprocessedInputPath = tmpPreprocessedInputDir + "/" + file + "." + ext;
4001  VuoFileUtilities::preserveOriginalFileName(preprocessedInputContents, file + "." + ext);
4002  VuoFileUtilities::writeStringToFile(preprocessedInputContents, preprocessedInputPath);
4003  }
4004  }
4005  else if (ext == "fs")
4006  {
4007  VuoFileUtilities::File vuf(dir, file + "." + ext);
4008  VuoModuleCompiler *moduleCompiler = VuoModuleCompiler::newModuleCompiler("isf", getModuleKeyForPath(inputPath), &vuf);
4009  if (moduleCompiler)
4010  {
4011  auto getType = [this] (const string &moduleKey) { return this->getType(moduleKey); };
4012  VuoCompilerIssues *issues = new VuoCompilerIssues();
4013  Module *module = moduleCompiler->compile(getType, llvmQueue, issues);
4014  if (module)
4015  dispatch_sync(llvmQueue, ^{
4016  setTargetForModule(module, target);
4017  writeModuleToBitcode(module, outputPath);
4018  });
4019 
4020  if (!issues->isEmpty())
4021  throw VuoCompilerException(issues, true);
4022  delete issues;
4023  }
4024  return;
4025  }
4026 
4027  vector<string> extraArgs;
4028  for (vector<string>::iterator i = allIncludePaths.begin(); i != allIncludePaths.end(); ++i)
4029  {
4030  extraArgs.push_back("-I");
4031  extraArgs.push_back(*i);
4032  }
4033 
4034 
4035  // When compiling on a development workstation or Jenkins, use the Conan-packaged macOS SDK, since it includes headers.
4036  // When compiling on an end-user system, no SDK is needed.
4037  string buildTimeMacOSSDKFolder = MACOS_SDK_ROOT;
4038  if (VuoFileUtilities::fileExists(buildTimeMacOSSDKFolder))
4039  {
4040  extraArgs.push_back("-isysroot");
4041  extraArgs.push_back(buildTimeMacOSSDKFolder);
4042  }
4043 
4044 
4045  __block vector<string> headerSearchPaths;
4046  void (^envGetHeaderSearchPaths)(Environment *) = ^void (Environment *env) {
4047  vector<string> result = env->getHeaderSearchPaths();
4048  headerSearchPaths.insert(headerSearchPaths.end(), result.begin(), result.end());
4049  };
4050  applyToInstalledEnvironments(envGetHeaderSearchPaths);
4051 
4052  auto issues = new VuoCompilerIssues;
4053  __block Module *module;
4054  dispatch_sync(llvmQueue, ^{
4055  module = readModuleFromC(preprocessedInputPath, headerSearchPaths, extraArgs, issues);
4056  });
4057  string moduleKey = getModuleKeyForPath(inputPath);
4058  if (! tmpPreprocessedInputDir.empty())
4059  {
4060  remove(tmpPreprocessedInputDir.c_str());
4061  issues->setFilePath(inputPath);
4062  }
4063  if (! module)
4064  throw VuoCompilerException(issues, true);
4065  delete issues;
4066 
4067  dispatch_sync(llvmQueue, ^{
4068  VuoCompilerModule *compilerModule = VuoCompilerModule::newModule(moduleKey, module, "");
4069  if (! compilerModule)
4070  {
4071  VUserLog("Error: Didn't recognize '%s' as a node class, type, or library.", inputPath.c_str());
4072  return;
4073  }
4074 
4075  setTargetForModule(module, target);
4076  writeModuleToBitcode(module, outputPath);
4077 
4078  delete module;
4079  });
4080 }
4081 
4085 Module * VuoCompiler::compileCompositionToModule(VuoCompilerComposition *composition, const string &moduleKey, bool isTopLevelComposition,
4086  VuoCompilerIssues *issues)
4087 {
4088  composition->check(issues);
4089 
4090  reifyGenericPortTypes(composition);
4091 
4093  isTopLevelComposition,
4094  moduleKey, this);
4095  if (telemetry == "console")
4096  generator->setDebugMode(true);
4097 
4098  __block Module *module = nullptr;
4099  dispatch_sync(llvmQueue, ^{
4100  try
4101  {
4102  module = generator->generateBitcode();
4103  setTargetForModule(module, target);
4104  }
4105  catch (VuoCompilerException &e)
4106  {
4107  if (issues)
4108  issues->append(e.getIssues());
4109  else
4110  VUserLog("%s", e.getIssues()->getLongDescription(false).c_str());
4111  }
4112  });
4113 
4114  delete generator;
4115 
4116  return module;
4117 }
4118 
4129 void VuoCompiler::compileComposition(VuoCompilerComposition *composition, string outputPath, bool isTopLevelComposition,
4130  VuoCompilerIssues *issues)
4131 {
4132  string moduleKey = getModuleKeyForPath(outputPath);
4133  Module *module = compileCompositionToModule(composition, moduleKey, isTopLevelComposition, issues);
4134  if (!module)
4135  throw VuoCompilerException(issues, false);
4136 
4137  dispatch_sync(llvmQueue, ^{
4138  writeModuleToBitcode(module, outputPath);
4139  });
4140 }
4141 
4154 void VuoCompiler::compileComposition(string inputPath, string outputPath, bool isTopLevelComposition,
4155  VuoCompilerIssues *issues)
4156 {
4157  VDebugLog("Compiling '%s' (%s)…", inputPath.c_str(), target.c_str());
4158  if (isVerbose)
4159  print();
4160 
4161  if (getCompositionLocalPath().empty())
4162  setCompositionPath(inputPath);
4163 
4165  {
4166  VuoCompilerIssue issue(VuoCompilerIssue::Error, "opening composition", inputPath,
4167  "", "The composition file couldn't be read or was empty.");
4168  throw VuoCompilerException(issue);
4169  }
4170 
4171  try
4172  {
4173  string compositionString = VuoFileUtilities::readFileToString(inputPath);
4174  compileCompositionString(compositionString, outputPath, isTopLevelComposition, issues);
4175  }
4176  catch (VuoCompilerException &e)
4177  {
4178  if (e.getIssues())
4179  e.getIssues()->setFilePathIfEmpty(inputPath);
4180  if (!issues && e.getIssues())
4181  VUserLog("%s", e.getIssues()->getLongDescription(false).c_str());
4182  throw;
4183  }
4184 
4185  VDebugLog("Done.");
4186 }
4187 
4198 void VuoCompiler::compileCompositionString(const string &compositionString, string outputPath, bool isTopLevelComposition,
4199  VuoCompilerIssues *issues)
4200 {
4202  compileComposition(composition, outputPath, isTopLevelComposition, issues);
4203 
4204  VuoComposition *baseComposition = composition->getBase();
4205  delete composition;
4206  delete baseComposition;
4207 }
4208 
4212 void VuoCompiler::compileSubcompositionString(const string &compositionString, const string &outputPath,
4213  std::function<void(void)> moduleLoadedCallback, Environment *environment,
4214  VuoCompilerIssues *issues, const string inputPathForIssues)
4215 {
4216  if (! issues)
4217  issues = new VuoCompilerIssues();
4218 
4219  bool compilationSucceeded = false;
4220  try
4221  {
4222  compileCompositionString(compositionString, outputPath, false, issues);
4223  compilationSucceeded = true;
4224  }
4225  catch (VuoCompilerException &e)
4226  {
4227  if (issues != e.getIssues())
4228  issues->append(e.getIssues());
4229  }
4230 
4231  if (! compilationSucceeded)
4232  {
4233  VuoFileUtilities::deleteFile(outputPath);
4234  issues->setFilePathIfEmpty(inputPathForIssues);
4235  }
4236 
4237  environment->moduleFileChanged(outputPath, compositionString, moduleLoadedCallback, this, issues);
4238 }
4239 
4254 void VuoCompiler::linkCompositionToCreateExecutable(string inputPath, string outputPath, Optimization optimization, string rPath, bool shouldAdHocCodeSign)
4255 {
4256  linkCompositionToCreateExecutableOrDynamicLibrary(inputPath, outputPath, optimization, false, rPath, shouldAdHocCodeSign);
4257 }
4258 
4276 void VuoCompiler::linkCompositionToCreateDynamicLibrary(string inputPath, string outputPath, Optimization optimization, bool shouldAdHocCodeSign)
4277 {
4278  linkCompositionToCreateExecutableOrDynamicLibrary(inputPath, outputPath, optimization, true, "", shouldAdHocCodeSign);
4279 }
4280 
4295 void VuoCompiler::linkCompositionToCreateExecutableOrDynamicLibrary(string compiledCompositionPath, string linkedCompositionPath,
4296  Optimization optimization, bool isDylib, string rPath, bool shouldAdHocCodeSign)
4297 {
4298  if (isVerbose)
4299  print();
4300 
4301  if (optimization == Optimization_FastBuildExistingCache)
4302  shouldLoadAllModules = false;
4303 
4304  set<string> dependencies = getDependenciesForComposition(compiledCompositionPath);
4305  dependencies.insert(getRuntimeDependency());
4306  if (! isDylib)
4307  dependencies.insert(getRuntimeMainDependency());
4308 
4309  set<Module *> modules;
4310  set<string> libraries;
4311  set<string> frameworks;
4312  getLinkerInputs(dependencies, optimization, modules, libraries, frameworks);
4313 
4314  libraries.insert(compiledCompositionPath);
4315 
4316  link(linkedCompositionPath, modules, libraries, frameworks, isDylib, rPath, shouldAdHocCodeSign);
4317 }
4318 
4333 void VuoCompiler::linkCompositionToCreateDynamicLibraries(string compiledCompositionPath, string linkedCompositionPath,
4334  VuoRunningCompositionLibraries *runningCompositionLibraries)
4335 {
4336  if (isVerbose)
4337  print();
4338 
4339  bool shouldAdHocCodeSign = false;
4340 #if __arm64__
4341  shouldAdHocCodeSign = true;
4342 #endif
4343 
4344  // Get the dependencies used by the new resources and not the previous resources.
4345 
4346  set<string> carriedOverDependencies = runningCompositionLibraries->getDependenciesLoaded();
4347  set<string> allDependencies = getDependenciesForComposition(compiledCompositionPath);
4348  set<string> addedDependencies;
4349  std::set_difference(allDependencies.begin(), allDependencies.end(),
4350  carriedOverDependencies.begin(), carriedOverDependencies.end(),
4351  std::inserter(addedDependencies, addedDependencies.end()));
4352 
4353  // Get the libraries and frameworks used by the previous resources.
4354 
4355  vector<string> carriedOverNonUnloadableLibraries = runningCompositionLibraries->getNonUnloadableLibrariesLoaded();
4356  vector<string> carriedOverUnloadableLibraries = runningCompositionLibraries->getUnloadableLibrariesLoaded();
4357  set<string> carriedOverExternalLibraries = runningCompositionLibraries->getExternalLibraries();
4358  set<string> carriedOverFrameworks = runningCompositionLibraries->getExternalFrameworks();
4359 
4360  // Link the new resource dylibs, if needed.
4361 
4362  string nonUnloadableResourcePath;
4363  string unloadableResourcePath;
4364  set<string> nonUnloadableDependencies;
4365  set<string> unloadableDependencies;
4366  map<string, set<string> > builtInCacheDependencies;
4367  map<string, set<string> > userCacheDependencies;
4368  set<string> builtInLibraries;
4369  set<string> userLibraries;
4370  set<string> addedExternalLibraries;
4371  set<string> addedFrameworks;
4372  set<string> allFrameworks;
4373  if (! addedDependencies.empty())
4374  {
4375  // Get the modules, libraries, and frameworks that will provide the composition's dependencies.
4376 
4377  set<string> builtInModuleAndLibraryDependencies;
4378  set<string> userModuleAndLibraryDependencies;
4379  set<Module *> builtInModules;
4380  set<Module *> userModules;
4381 
4382  getLinkerInputs(addedDependencies, Optimization_FastBuild,
4383  builtInModuleAndLibraryDependencies, userModuleAndLibraryDependencies, builtInCacheDependencies, userCacheDependencies,
4384  builtInModules, userModules, builtInLibraries, userLibraries, addedExternalLibraries, addedFrameworks);
4385 
4386  allFrameworks.insert(carriedOverFrameworks.begin(), carriedOverFrameworks.end());
4387  allFrameworks.insert(addedFrameworks.begin(), addedFrameworks.end());
4388 
4389  string dir, linkedCompositionFile, ext;
4390  VuoFileUtilities::splitPath(linkedCompositionPath, dir, linkedCompositionFile, ext);
4391 
4392  // For any module caches that were rebuilt, remove the previous revision from the lists of libraries to link to and load.
4393 
4394  vector<string> carriedOverUserCacheLibraries = runningCompositionLibraries->getUnloadableCacheLibrariesLoaded();
4395 
4396  __block vector<string> currentCacheLibraries;
4397  applyToAllEnvironments(^void (Environment *env) {
4398  currentCacheLibraries.push_back( env->getCurrentModuleCacheDylib() );
4399  });
4400 
4401  for (string cachePath : carriedOverUserCacheLibraries)
4402  {
4403  for (string currentCachePath : currentCacheLibraries)
4404  {
4405  if (VuoFileUtilities::areDifferentRevisionsOfSameModuleCacheDylib(cachePath, currentCachePath))
4406  {
4407  set<string> dependenciesInCache = runningCompositionLibraries->enqueueCacheLibraryToUnload(cachePath);
4408 
4409  userCacheDependencies[currentCachePath].insert(dependenciesInCache.begin(), dependenciesInCache.end());
4410 
4411  auto cacheDependenciesIter = userCacheDependencies.find(cachePath);
4412  if (cacheDependenciesIter != userCacheDependencies.end())
4413  {
4414  userCacheDependencies[currentCachePath].insert(cacheDependenciesIter->second.begin(), cacheDependenciesIter->second.end());
4415  userCacheDependencies.erase(cacheDependenciesIter);
4416  }
4417 
4418  auto carriedOverIter = find(carriedOverUnloadableLibraries.begin(), carriedOverUnloadableLibraries.end(), cachePath);
4419  if (carriedOverIter != carriedOverUnloadableLibraries.end())
4420  *carriedOverIter = currentCachePath;
4421  }
4422  }
4423  }
4424 
4425  // If any module caches were rebuilt, prepare to replace the existing user resource dylibs with the new resource dylib created below.
4426 
4427  bool wasModuleCacheRebuilt = runningCompositionLibraries->hasCacheLibraryEnqueuedToUnload();
4428  if (wasModuleCacheRebuilt)
4429  {
4430  vector<string> carriedOverResourceLibraries = runningCompositionLibraries->getUnloadableResourceLibrariesLoaded();
4431 
4432  vector<string> carriedOverUnloadableMinusResourceLibraries;
4433  std::set_difference(carriedOverUnloadableLibraries.begin(), carriedOverUnloadableLibraries.end(),
4434  carriedOverResourceLibraries.begin(), carriedOverResourceLibraries.end(),
4435  std::back_inserter(carriedOverUnloadableMinusResourceLibraries));
4436 
4437  carriedOverUnloadableLibraries = carriedOverUnloadableMinusResourceLibraries;
4438 
4439  set<string> dependenciesInResourceLibraries = runningCompositionLibraries->enqueueAllUnloadableResourceLibrariesToUnload();
4440  userModuleAndLibraryDependencies.insert(dependenciesInResourceLibraries.begin(), dependenciesInResourceLibraries.end());
4441 
4442  set<string> builtInModuleAndLibraryDependencies_tmp;
4443  set<string> userModuleAndLibraryDependencies_tmp;
4444  map<string, set<string> > builtInCacheDependencies_tmp;
4445  set<Module *> builtInModules_tmp;
4446  set<string> builtInLibraries_tmp;
4447  set<string> externalLibraries_tmp;
4448  set<string> externalFrameworks_tmp;
4449 
4450  getLinkerInputs(userModuleAndLibraryDependencies, Optimization_FastBuild,
4451  builtInModuleAndLibraryDependencies_tmp, userModuleAndLibraryDependencies_tmp, builtInCacheDependencies_tmp, userCacheDependencies,
4452  builtInModules_tmp, userModules, builtInLibraries_tmp, userLibraries, externalLibraries_tmp, externalFrameworks_tmp);
4453  }
4454 
4455  // If built-in dependencies were added, create an additional resource dylib.
4456 
4457  if (! builtInModules.empty() || builtInLibraries.size() > builtInCacheDependencies.size())
4458  {
4459  nonUnloadableResourcePath = VuoFileUtilities::makeTmpFile(linkedCompositionFile + "-resource-nonunloadable", "dylib");
4460  nonUnloadableDependencies = builtInModuleAndLibraryDependencies;
4461 
4462  set<string> librariesForNonUnloadableResource = builtInLibraries;
4463  librariesForNonUnloadableResource.insert(carriedOverNonUnloadableLibraries.begin(), carriedOverNonUnloadableLibraries.end());
4464  librariesForNonUnloadableResource.insert(carriedOverExternalLibraries.begin(), carriedOverExternalLibraries.end());
4465  librariesForNonUnloadableResource.insert(addedExternalLibraries.begin(), addedExternalLibraries.end());
4466 
4467  link(nonUnloadableResourcePath, builtInModules, librariesForNonUnloadableResource, allFrameworks, true, "", shouldAdHocCodeSign);
4468 
4469  for (set<string>::iterator i = builtInLibraries.begin(); i != builtInLibraries.end(); )
4470  {
4471  if (! VuoStringUtilities::endsWith(*i, ".dylib"))
4472  builtInLibraries.erase(i++);
4473  else
4474  i++;
4475  }
4476 
4477  for (set<string>::iterator i = addedExternalLibraries.begin(); i != addedExternalLibraries.end(); )
4478  {
4479  if (! VuoStringUtilities::endsWith(*i, ".dylib"))
4480  addedExternalLibraries.erase(i++);
4481  else
4482  i++;
4483  }
4484  }
4485 
4486  // If user dependencies were added or module caches were rebuilt, create an additional resource dylib.
4487 
4488  if (! userModules.empty() || userLibraries.size() > userCacheDependencies.size() || wasModuleCacheRebuilt)
4489  {
4490  unloadableResourcePath = VuoFileUtilities::makeTmpFile(linkedCompositionFile + "-resource-unloadable", "dylib");
4491  unloadableDependencies = userModuleAndLibraryDependencies;
4492 
4493  set<string> librariesForUnloadableResource = userLibraries;
4494  librariesForUnloadableResource.insert(builtInLibraries.begin(), builtInLibraries.end());
4495  librariesForUnloadableResource.insert(carriedOverUnloadableLibraries.begin(), carriedOverUnloadableLibraries.end());
4496  librariesForUnloadableResource.insert(carriedOverNonUnloadableLibraries.begin(), carriedOverNonUnloadableLibraries.end());
4497  librariesForUnloadableResource.insert(carriedOverExternalLibraries.begin(), carriedOverExternalLibraries.end());
4498  librariesForUnloadableResource.insert(addedExternalLibraries.begin(), addedExternalLibraries.end());
4499  if (! nonUnloadableResourcePath.empty())
4500  librariesForUnloadableResource.insert(nonUnloadableResourcePath);
4501 
4502  link(unloadableResourcePath, userModules, librariesForUnloadableResource, allFrameworks, true, "", shouldAdHocCodeSign);
4503 
4504  for (set<string>::iterator i = userLibraries.begin(); i != userLibraries.end(); )
4505  {
4506  if (! VuoStringUtilities::endsWith(*i, ".dylib"))
4507  userLibraries.erase(i++);
4508  else
4509  i++;
4510  }
4511  }
4512  }
4513 
4514  // Get the Vuo runtime dependency.
4515 
4516  set<string> vuoRuntimePaths;
4517  {
4518  set<Module *> modules;
4519  set<string> libraries;
4520  set<string> frameworks;
4521 
4522  set<string> dependencies;
4523  dependencies.insert(getRuntimeDependency());
4524  getLinkerInputs(dependencies, Optimization_FastBuild, modules, libraries, frameworks);
4525  vuoRuntimePaths = libraries;
4526  }
4527 
4528  // Link the composition.
4529 
4530  {
4531  set<Module *> modules;
4532  set<string> libraries;
4533 
4534  libraries.insert(compiledCompositionPath);
4535  libraries.insert(carriedOverExternalLibraries.begin(), carriedOverExternalLibraries.end());
4536  libraries.insert(addedExternalLibraries.begin(), addedExternalLibraries.end());
4537  libraries.insert(carriedOverNonUnloadableLibraries.begin(), carriedOverNonUnloadableLibraries.end());
4538  libraries.insert(carriedOverUnloadableLibraries.begin(), carriedOverUnloadableLibraries.end());
4539  libraries.insert(builtInLibraries.begin(), builtInLibraries.end());
4540  libraries.insert(userLibraries.begin(), userLibraries.end());
4541  if (! nonUnloadableResourcePath.empty())
4542  libraries.insert(nonUnloadableResourcePath);
4543  if (! unloadableResourcePath.empty())
4544  libraries.insert(unloadableResourcePath);
4545  libraries.insert(vuoRuntimePaths.begin(), vuoRuntimePaths.end());
4546  link(linkedCompositionPath, modules, libraries, allFrameworks, true, "", shouldAdHocCodeSign);
4547  }
4548 
4549  // Now that we're past the point where an exception can be thrown, update the RunningCompositionLibraries.
4550 
4551  if (! nonUnloadableResourcePath.empty())
4552  runningCompositionLibraries->enqueueResourceLibraryToLoad(nonUnloadableResourcePath, nonUnloadableDependencies, false);
4553 
4554  if (! unloadableResourcePath.empty())
4555  runningCompositionLibraries->enqueueResourceLibraryToLoad(unloadableResourcePath, unloadableDependencies, true);
4556 
4557  for (map<string, set<string> >::iterator i = builtInCacheDependencies.begin(); i != builtInCacheDependencies.end(); ++i)
4558  runningCompositionLibraries->enqueueCacheLibraryToLoad(i->first, i->second, false);
4559 
4560  for (map<string, set<string> >::iterator i = userCacheDependencies.begin(); i != userCacheDependencies.end(); ++i)
4561  runningCompositionLibraries->enqueueCacheLibraryToLoad(i->first, i->second, true);
4562 
4563  runningCompositionLibraries->addExternalFrameworks(addedFrameworks);
4564  runningCompositionLibraries->addExternalLibraries(addedExternalLibraries);
4565 }
4566 
4573 set<string> VuoCompiler::getDependenciesForComposition(const string &compiledCompositionPath)
4574 {
4575  VDebugLog("Gathering dependencies for '%s'…", compiledCompositionPath.c_str());
4576 
4577  // Add the node classes in the top-level composition and their dependencies.
4578  __block set<string> directDependencies;
4579  string moduleKey = getModuleKeyForPath(compiledCompositionPath);
4580  Module *module = readModuleFromBitcode(compiledCompositionPath, getTargetArch(target));
4581  dispatch_sync(llvmQueue, ^{
4582  VuoCompilerModule *compilerModule = VuoCompilerModule::newModule(moduleKey, module, "");
4583  directDependencies = compilerModule->getDependencies();
4584  delete compilerModule;
4585  delete module;
4586  });
4587 
4588  try
4589  {
4590  auto deps = getDependenciesForComposition(directDependencies, true);
4591  VDebugLog("Done.");
4592  return deps;
4593  }
4594  catch (VuoCompilerException &e)
4595  {
4596  e.getIssues()->setFilePathIfEmpty(compiledCompositionPath);
4597  throw;
4598  }
4599 }
4600 
4608 {
4609  set<string> directDependencies;
4610 
4611  set<VuoCompilerNode *> nodes = composition->getCachedGraph(this)->getNodes();
4612  for (VuoCompilerNode *node : nodes)
4613  if (node->getBase()->getNodeClass()->hasCompiler())
4614  directDependencies.insert( node->getBase()->getNodeClass()->getCompiler()->getDependencyName() );
4615 
4616  vector<VuoPublishedPort *> publishedInputPorts = composition->getBase()->getPublishedInputPorts();
4617  vector<VuoPublishedPort *> publishedOutputPorts = composition->getBase()->getPublishedOutputPorts();
4618  vector<VuoPublishedPort *> publishedPorts;
4619  publishedPorts.insert(publishedPorts.end(), publishedInputPorts.begin(), publishedInputPorts.end());
4620  publishedPorts.insert(publishedPorts.end(), publishedOutputPorts.begin(), publishedOutputPorts.end());
4621  for (VuoPublishedPort *publishedPort : publishedPorts)
4622  {
4623  if (publishedPort->getClass()->hasCompiler())
4624  {
4625  VuoType *portType = static_cast<VuoCompilerPortClass *>( publishedPort->getClass()->getCompiler() )->getDataVuoType();
4626  if (portType)
4627  {
4628  string dependency;
4629  VuoGenericType *genericType = dynamic_cast<VuoGenericType *>(portType);
4630  if (genericType)
4631  {
4632  VuoGenericType::Compatibility compatibility;
4633  vector<string> compatibleTypeNames = genericType->getCompatibleSpecializedTypes(compatibility);
4634  dependency = VuoCompilerGenericType::chooseBackingTypeName(portType->getModuleKey(), compatibleTypeNames);
4635  }
4636  else
4637  dependency = portType->getModuleKey();
4638 
4639  directDependencies.insert(dependency);
4640  }
4641  }
4642  }
4643 
4644  return directDependencies;
4645 }
4646 
4653 set<string> VuoCompiler::getDependenciesForComposition(VuoCompilerComposition *composition)
4654 {
4655  set<string> directDependencies = getDirectDependenciesForComposition(composition);
4656  return getDependenciesForComposition(directDependencies, false);
4657 }
4658 
4665 {
4666  __block vector<string> librarySearchPaths;
4667  applyToInstalledEnvironments(^void (Environment *env) {
4668  vector<string> result = env->getLibrarySearchPaths();
4669  librarySearchPaths.insert(librarySearchPaths.end(), result.begin(), result.end());
4670  });
4671 
4672  set<string> dylibDeps;
4673  for (string dep : getDependenciesForComposition(composition))
4674  {
4675  string path = getLibraryPath(dep, librarySearchPaths);
4676  if (VuoStringUtilities::endsWith(path, ".dylib"))
4677  dylibDeps.insert(path);
4678  }
4679 
4680  return dylibDeps;
4681 }
4682 
4696 set<string> VuoCompiler::getDependenciesForComposition(const set<string> &directDependencies, bool checkCompatibility)
4697 {
4698  // Make sure that any compiler-generated node classes have been generated and added to the dependency graph.
4699  for (set<string>::const_iterator i = directDependencies.begin(); i != directDependencies.end(); ++i)
4700  getNodeClass(*i);
4701 
4702  set<string> dependencies;
4703  for (set<string>::iterator i = directDependencies.begin(); i != directDependencies.end(); ++i)
4704  {
4705  string moduleKey = *i;
4706 
4707  dependencies.insert(moduleKey);
4708 
4709  // First pass: Find all dependencies of the direct dependency that have been loaded so far.
4710  vector<VuoDirectedAcyclicGraph::Vertex *> firstPassVertices = dependencyGraph->findVertex(moduleKey);
4711  set<VuoDirectedAcyclicGraph::Vertex *> firstPassDependencies(firstPassVertices.begin(), firstPassVertices.end());
4712  for (vector<VuoDirectedAcyclicGraph::Vertex *>::iterator j = firstPassVertices.begin(); j != firstPassVertices.end(); ++j)
4713  {
4714  vector<VuoDirectedAcyclicGraph::Vertex *> downstream = dependencyGraph->getDownstreamVertices(*j);
4715  firstPassDependencies.insert(downstream.begin(), downstream.end());
4716  }
4717 
4718  // Make sure that any compiler-generated node classes in subcompositions have been generated and added to the dependency graph.
4719  for (set<VuoDirectedAcyclicGraph::Vertex *>::iterator j = firstPassDependencies.begin(); j != firstPassDependencies.end(); ++j)
4720  {
4721  DependencyGraphVertex *v = static_cast<DependencyGraphVertex *>(*j);
4722  getNodeClass(v->getDependency());
4723  }
4724 
4725  // Second pass: Find all dependencies of the direct dependency, this time including dependencies of the node classes just generated.
4726  vector<VuoDirectedAcyclicGraph::Vertex *> moduleVertices = dependencyGraph->findVertex(moduleKey);
4727  set<VuoDirectedAcyclicGraph::Vertex *> moduleDependencies(moduleVertices.begin(), moduleVertices.end());
4728  for (vector<VuoDirectedAcyclicGraph::Vertex *>::iterator j = moduleVertices.begin(); j != moduleVertices.end(); ++j)
4729  {
4730  vector<VuoDirectedAcyclicGraph::Vertex *> downstream = dependencyGraph->getDownstreamVertices(*j);
4731  moduleDependencies.insert(downstream.begin(), downstream.end());
4732  }
4733 
4734  // Sort the direct dependency and all of its dependencies into those that are and are not compatible.
4735  set<string> dependenciesToAdd;
4736  set<string> incompatibleDependencies;
4737  for (set<VuoDirectedAcyclicGraph::Vertex *>::iterator j = moduleDependencies.begin(); j != moduleDependencies.end(); ++j)
4738  {
4739  DependencyGraphVertex *v = static_cast<DependencyGraphVertex *>(*j);
4740  if (! checkCompatibility || ! v->getEnvironment() || v->isCompatible())
4741  dependenciesToAdd.insert(v->getDependency());
4742  else
4743  incompatibleDependencies.insert(v->getDependency());
4744  }
4745 
4746  if (! checkCompatibility || incompatibleDependencies.empty())
4747  {
4748  dependencies.insert(dependenciesToAdd.begin(), dependenciesToAdd.end());
4749  }
4750  else
4751  {
4753 
4754  string dependencyTargetString;
4755  VuoCompilerModule *module = getModule(moduleKey);
4756  if (module)
4757  {
4758  VuoCompilerCompatibility dependencyTargets = module->getCompatibleTargets();
4759  for (set<string>::iterator i = incompatibleDependencies.begin(); i != incompatibleDependencies.end(); ++i)
4760  {
4761  VuoCompilerModule *subModule = getModule(*i);
4762  if (subModule)
4763  {
4764  VuoCompilerCompatibility subDependencyTargets = subModule->getCompatibleTargets();
4765  dependencyTargets = dependencyTargets.intersection(subDependencyTargets);
4766  }
4767  }
4768  dependencyTargetString = dependencyTargets.toString();
4769  }
4770  else
4771  dependencyTargetString = "(unknown systems)";
4772 
4773  string modulePlaceholder = (module ? "%module" : "%moduleKey");
4774  VuoCompilerIssue issue(VuoCompilerIssue::Error, "linking composition", "",
4775  "Node incompatible with system",
4776  modulePlaceholder + " is only compatible with " + dependencyTargetString +
4777  ", so this composition can't run on your system (" + compositionTargets.toString() + ").");
4778  issue.setModule(module->getPseudoBase());
4779  issue.setModuleKey(moduleKey);
4780  throw VuoCompilerException(issue);
4781  }
4782  }
4783 
4784  // Add the libraries needed by every linked composition.
4785  vector<string> coreDependencies = getCoreVuoDependencies();
4786  dependencies.insert(coreDependencies.begin(), coreDependencies.end());
4787 
4788  return dependencies;
4789 }
4790 
4795 void VuoCompiler::getLinkerInputs(const set<string> &dependencies, Optimization optimization,
4796  set<Module *> &modules, set<string> &libraries, set<string> &frameworks)
4797 {
4798  set<string> builtInModuleAndLibraryDependencies;
4799  set<string> userModuleAndLibraryDependencies;
4800  map<string, set<string> > builtInCacheDependencies;
4801  map<string, set<string> > userCacheDependencies;
4802  set<Module *> builtInModules;
4803  set<Module *> userModules;
4804  set<string> builtInLibraries;
4805  set<string> userLibraries;
4806  set<string> externalLibraries;
4807 
4808  getLinkerInputs(dependencies, optimization,
4809  builtInModuleAndLibraryDependencies, userModuleAndLibraryDependencies, builtInCacheDependencies, userCacheDependencies,
4810  builtInModules, userModules, builtInLibraries, userLibraries, externalLibraries, frameworks);
4811 
4812  modules.insert(builtInModules.begin(), builtInModules.end());
4813  modules.insert(userModules.begin(), userModules.end());
4814  libraries.insert(builtInLibraries.begin(), builtInLibraries.end());
4815  libraries.insert(userLibraries.begin(), userLibraries.end());
4816  libraries.insert(externalLibraries.begin(), externalLibraries.end());
4817 }
4818 
4832 void VuoCompiler::getLinkerInputs(const set<string> &dependencies, Optimization optimization,
4833  set<string> &builtInModuleAndLibraryDependencies, set<string> &userModuleAndLibraryDependencies,
4834  map<string, set<string> > &builtInCacheDependencies, map<string, set<string> > &userCacheDependencies,
4835  set<Module *> &builtInModules, set<Module *> &userModules,
4836  set<string> &builtInLibraries, set<string> &userLibraries,
4837  set<string> &externalLibraries, set<string> &externalFrameworks)
4838 {
4839  bool shouldUseModuleCache = (optimization == Optimization_FastBuild || optimization == Optimization_FastBuildExistingCache);
4840  if (shouldUseModuleCache)
4841  useModuleCache(true, optimization == Optimization_FastBuildExistingCache);
4842 
4843  __block vector<string> librarySearchPaths;
4844  void (^envGetLibrarySearchPaths)(Environment *) = ^void (Environment *env) {
4845  vector<string> result = env->getLibrarySearchPaths();
4846  librarySearchPaths.insert(librarySearchPaths.end(), result.begin(), result.end());
4847  };
4848  applyToInstalledEnvironments(envGetLibrarySearchPaths);
4849 
4850  for (set<string>::iterator i = dependencies.begin(); i != dependencies.end(); ++i)
4851  {
4852  string dependency = *i;
4853 
4854  bool foundInCache = false;
4855  string moduleCachePath;
4856  bool isInBuiltInModuleCache = false;
4857  if (shouldUseModuleCache)
4858  foundInCache = findInModuleCache(dependency, moduleCachePath, isInBuiltInModuleCache);
4859 
4860  if (foundInCache)
4861  {
4862  if (isInBuiltInModuleCache)
4863  {
4864  builtInLibraries.insert(moduleCachePath);
4865  builtInCacheDependencies[moduleCachePath].insert(dependency);
4866  }
4867  else
4868  {
4869  userLibraries.insert(moduleCachePath);
4870  userCacheDependencies[moduleCachePath].insert(dependency);
4871  }
4872  }
4873  else
4874  {
4875  __block VuoCompilerModule *module = NULL;
4876  void (^envFindModule)(Environment *) = ^void (Environment *env) {
4877  VuoCompilerModule *result = env->findModule(dependency);
4878  if (result)
4879  module = result;
4880  };
4881  applyToAllEnvironments(envFindModule);
4882 
4883  if (module)
4884  {
4885  VuoCompilerNodeClass *nodeClass = dynamic_cast<VuoCompilerNodeClass *>(module);
4886  if (! (nodeClass && VuoCompilerSpecializedNodeClass::hasGenericPortTypes(nodeClass)) ) // Skip not-fully-specialized generic modules
4887  {
4888  string modulePath = module->getModulePath();
4889  if (! modulePath.empty() && dynamic_cast<VuoCompilerType *>(module))
4890  {
4891  if (module->isBuiltIn())
4892  builtInLibraries.insert(modulePath);
4893  else
4894  userLibraries.insert(modulePath);
4895  }
4896  else
4897  {
4898  if (module->isBuiltIn())
4899  builtInModules.insert(module->getModule());
4900  else
4901  userModules.insert(module->getModule());
4902  }
4903 
4904  if (module->isBuiltIn())
4905  builtInModuleAndLibraryDependencies.insert(dependency);
4906  else
4907  userModuleAndLibraryDependencies.insert(dependency);
4908  }
4909  }
4910  else
4911  {
4912  if (VuoStringUtilities::endsWith(dependency, ".framework"))
4913  externalFrameworks.insert(dependency);
4914  else
4915  {
4916  string dependencyPath = getLibraryPath(dependency, librarySearchPaths);
4917  if (! dependencyPath.empty())
4918  externalLibraries.insert(dependencyPath);
4919 
4920  // On macOS 11, libc.dylib and libobjc.dylib are not present,
4921  // but we can still link since Vuo.framework includes the TBDs.
4922  else if (dependency != "c"
4923  && dependency != "objc")
4924  VUserLog("Warning: Could not locate dependency '%s'.", dependency.c_str());
4925  }
4926  }
4927  }
4928  }
4929 }
4930 
4936 string VuoCompiler::getLibraryPath(const string &dependency, vector<string> librarySearchPaths)
4937 {
4938  if (dependency[0] == '/' && VuoFileUtilities::fileExists(dependency))
4939  return dependency;
4940 
4941  // Put the system library folder last in the list — prefer to use the libraries bundled in Vuo.framework.
4942  // Don't attempt to use OpenSSL from the system library folder, since macOS 10.15 prevents linking to it.
4943  if (dependency != "crypto"
4944  && dependency != "ssl")
4945  librarySearchPaths.push_back("/usr/lib");
4946 
4947  for (auto &path : librarySearchPaths)
4948  {
4949  vector<string> variations;
4950  variations.push_back(path + "/" + dependency);
4951  variations.push_back(path + "/lib" + dependency);
4952  variations.push_back(path + "/lib" + dependency + ".dylib");
4953  variations.push_back(path + "/lib" + dependency + ".a");
4954  for (auto &variation : variations)
4955  if (VuoFileUtilities::fileExists(variation))
4956  return variation;
4957  }
4958 
4959  return "";
4960 }
4961 
4969 void VuoCompiler::useModuleCache(bool shouldUseExistingBuiltInCaches, bool shouldUseExistingOtherCaches)
4970 {
4971  loadModulesIfNeeded();
4972  dispatch_group_wait(moduleSourceCompilersExist, DISPATCH_TIME_FOREVER); // Wait for any previous loadModulesIfNeeded() calls to complete.
4973 
4974  // Iterate through the environments in the order that the caches need to be built.
4975 
4976  dispatch_sync(environmentQueue, ^{
4977  set<string> dylibsForCachesOfInstalledModules;
4978  set<string> frameworksForCachesOfInstalledModules;
4979  unsigned long lastPrerequisiteModuleCacheRebuild = 0;
4980  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
4981  {
4982  set<string> dylibsForCacheOfGeneratedModules;
4983  set<string> frameworksForCacheOfGeneratedModules;
4984 
4985  for (int j = i->size() - 1; j >= 0; --j)
4986  {
4987  Environment *env = i->at(j);
4988  bool installed = (j == 0);
4989 
4990  set<string> cacheableModulesAndDependencies;
4991  set<string> dylibsNeededToLinkToThisCache;
4992  set<string> frameworksNeededToLinkToThisCache;
4993  env->getCacheableModulesAndDependencies(cacheableModulesAndDependencies,
4994  dylibsNeededToLinkToThisCache, frameworksNeededToLinkToThisCache);
4995 
4996  set<string> accumulatedDylibs;
4997  accumulatedDylibs.insert(dylibsNeededToLinkToThisCache.begin(), dylibsNeededToLinkToThisCache.end());
4998  accumulatedDylibs.insert(dylibsForCachesOfInstalledModules.begin(), dylibsForCachesOfInstalledModules.end());
4999  accumulatedDylibs.insert(dylibsForCacheOfGeneratedModules.begin(), dylibsForCacheOfGeneratedModules.end());
5000 
5001  set<string> accumulatedFrameworks;
5002  accumulatedFrameworks.insert(frameworksNeededToLinkToThisCache.begin(), frameworksNeededToLinkToThisCache.end());
5003  accumulatedFrameworks.insert(frameworksForCachesOfInstalledModules.begin(), frameworksForCachesOfInstalledModules.end());
5004  accumulatedFrameworks.insert(frameworksForCacheOfGeneratedModules.begin(), frameworksForCacheOfGeneratedModules.end());
5005 
5006  bool shouldUseExistingCache = (env->isBuiltIn() ? shouldUseExistingBuiltInCaches : shouldUseExistingOtherCaches);
5007  env->useModuleCache(shouldUseExistingCache, this, cacheableModulesAndDependencies,
5008  accumulatedDylibs, accumulatedFrameworks, lastPrerequisiteModuleCacheRebuild);
5009 
5010  string cacheDylib = env->getCurrentModuleCacheDylib();
5011  accumulatedDylibs.insert(cacheDylib);
5012  dylibsForCachesOfInstalledModules.insert(cacheDylib);
5013 
5014  lastPrerequisiteModuleCacheRebuild = max(lastPrerequisiteModuleCacheRebuild, env->getLastModuleCacheRebuild());
5015 
5016  if (installed)
5017  {
5018  dylibsForCachesOfInstalledModules.insert(dylibsNeededToLinkToThisCache.begin(), dylibsNeededToLinkToThisCache.end());
5019  frameworksForCachesOfInstalledModules.insert(frameworksNeededToLinkToThisCache.begin(), frameworksNeededToLinkToThisCache.end());
5020  }
5021  else
5022  {
5023  dylibsForCacheOfGeneratedModules.insert(dylibsNeededToLinkToThisCache.begin(), dylibsNeededToLinkToThisCache.end());
5024  frameworksForCacheOfGeneratedModules.insert(frameworksNeededToLinkToThisCache.begin(), frameworksNeededToLinkToThisCache.end());
5025  }
5026  }
5027  }
5028  });
5029 
5030  Environment::waitForModuleCachesToBuild();
5031 }
5032 
5041 bool VuoCompiler::findInModuleCache(const string &moduleOrDependency, string &cachePath, bool &isBuiltinCache)
5042 {
5043  __block bool found = false;
5044  __block string outPath;
5045  __block bool outBuiltin;
5046  dispatch_sync(environmentQueue, ^{
5047  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
5048  {
5049  bool builtin = (i == environments.begin());
5050 
5051  for (int j = i->size() - 1; j >= 0; --j)
5052  {
5053  Environment *env = i->at(j);
5054 
5055  string resultPath;
5056  bool resultFound = env->findInModuleCache(moduleOrDependency, resultPath);
5057  if (resultFound)
5058  {
5059  found = true;
5060  outPath = resultPath;
5061  outBuiltin = builtin;
5062  }
5063  }
5064  }
5065  });
5066 
5067  cachePath = outPath;
5068  isBuiltinCache = outBuiltin;
5069  return found;
5070 }
5071 
5080 {
5081  dispatch_group_async(moduleCacheBuilding, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
5082  useModuleCache(true, false);
5083  });
5084 }
5085 
5091 {
5092  return getTargetArch(target);
5093 }
5094 
5108 void VuoCompiler::generateBuiltInModuleCaches(string vuoFrameworkPath, string target)
5109 {
5110  vuoFrameworkInProgressPath = vuoFrameworkPath;
5111 
5112  VuoCompiler compiler("", target);
5113  compiler.useModuleCache(false, true);
5114 }
5115 
5124 {
5125  unsigned long maxSeconds = 30 * 24 * 60 * 60; // 30 days
5126 
5127  set<VuoFileUtilities::File *> cacheDirs = VuoFileUtilities::findAllFilesInDirectory(VuoFileUtilities::getCachePath());
5128  for (set<VuoFileUtilities::File *>::iterator i = cacheDirs.begin(); i != cacheDirs.end(); ++i)
5129  {
5130  string path = (*i)->path();
5131 
5132  string file = (*i)->basename();
5133  if (file != "Builtin" && file != "System" && file != "User")
5134  {
5135  unsigned long fileSeconds = VuoFileUtilities::getSecondsSinceFileLastAccessed(path);
5136  if (fileSeconds > maxSeconds)
5138  }
5139 
5140  if (VuoStringUtilities::beginsWith(file, Environment::pidCacheDirPrefix))
5141  {
5142  string pidAsString = file.substr(Environment::pidCacheDirPrefix.length());
5143  int pid = atoi(pidAsString.c_str());
5144  if (kill(pid, 0) != 0) // no running process has this pid
5146  }
5147 
5148  delete *i;
5149  }
5150 }
5151 
5156 void VuoCompiler::setLoadAllModules(bool shouldLoadAllModules)
5157 {
5158  dispatch_sync(modulesToLoadQueue, ^{
5159  this->shouldLoadAllModules = shouldLoadAllModules;
5160  });
5161 }
5162 
5175 void VuoCompiler::link(string outputPath, const set<Module *> &modules, const set<string> &libraries, const set<string> &frameworks, bool isDylib, string rPath, bool shouldAdHocCodeSign, VuoCompilerIssues *issues)
5176 {
5177  VDebugLog("Linking '%s' (%s)…", outputPath.c_str(), getTargetArch(target).c_str());
5178  // https://stackoverflow.com/questions/11657529/how-to-generate-an-executable-from-an-llvmmodule
5179 
5180 
5181  // Write all the modules with renamed symbols to a composite module file (since the linker can't operate on in-memory modules).
5182  string compositeModulePath = VuoFileUtilities::makeTmpFile("composite", "bc");
5183  dispatch_sync(llvmQueue, ^{
5184  double t0 = VuoLogGetTime();
5185  unique_ptr<Module> compositeModule(new Module("composite", *globalLLVMContext));
5186  Linker linker(*compositeModule);
5187  setTargetForModule(compositeModule.get(), target);
5188  for (auto i : modules)
5189  {
5190  unique_ptr<Module> upi = llvm::CloneModule(i);
5191  if (linker.linkInModule(std::move(upi)))
5192  VUserLog("Error: Failed to link compositeModule.");
5193  }
5194  writeModuleToBitcode(compositeModule.get(), compositeModulePath);
5195  VDebugLog("\tLinkModules took %5.2fs", VuoLogGetTime() - t0);
5196  });
5197 
5198 
5199  // llvm-3.1/llvm/tools/clang/tools/driver/driver.cpp
5200 
5201  // Invoke clang as `clang++` so it includes the C++ standard libraries.
5202  string clangPath(getClangPath() + "++");
5203 
5204  vector<const char *> args;
5205  vector<char *> argsToFree;
5206  args.push_back(clangPath.c_str());
5207 
5208  {
5209  char *outputPathZ = strdup(("-o" + outputPath).c_str());
5210  args.push_back(outputPathZ);
5211  argsToFree.push_back(outputPathZ);
5212  }
5213 
5214  args.push_back(compositeModulePath.c_str());
5215 
5216  vector<string> coreDependencies = getCoreVuoDependencies();
5217  for (set<string>::const_iterator i = libraries.begin(); i != libraries.end(); ++i)
5218  {
5219  string library = *i;
5220 
5221  for (vector<string>::iterator j = coreDependencies.begin(); j != coreDependencies.end(); ++j)
5222  {
5223  string coreDependency = *j;
5224  if (VuoStringUtilities::endsWith(library, "lib" + coreDependency + ".a"))
5225  args.push_back("-force_load"); // Load all symbols of static core dependencies, not just those used in the objects.
5226  }
5227 
5228  // Use the pre-built native object file if it exists (faster than converting .bc to .o every build).
5229  if (VuoStringUtilities::endsWith(library, ".bc"))
5230  {
5231  string libraryObject = VuoStringUtilities::substrBefore(library, ".bc") + ".o";
5232  if (VuoFileUtilities::fileExists(libraryObject))
5233  library = libraryObject;
5234  }
5235 
5236  char *libraryZ = strdup(library.c_str());
5237  args.push_back(libraryZ);
5238  argsToFree.push_back(libraryZ);
5239  }
5240 
5241  // Add framework search paths
5242  vector<string> frameworkArguments;
5243 
5244  __block vector<string> frameworkSearchPaths;
5245  void (^envGetFrameworkSearchPaths)(Environment *) = ^void (Environment *env) {
5246  vector<string> result = env->getFrameworkSearchPaths();
5247  frameworkSearchPaths.insert(frameworkSearchPaths.end(), result.begin(), result.end());
5248  };
5249  applyToInstalledEnvironments(envGetFrameworkSearchPaths);
5250 
5251  for (vector<string>::const_iterator i = frameworkSearchPaths.begin(); i != frameworkSearchPaths.end(); ++i)
5252  {
5253  string a = "-F"+*i;
5254  // 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.
5255  frameworkArguments.push_back(a);
5256  char *frameworkArgument = strdup(a.c_str());
5257  args.push_back(frameworkArgument);
5258  argsToFree.push_back(frameworkArgument);
5259  }
5260 
5261  for (set<string>::const_iterator i = frameworks.begin(); i != frameworks.end(); ++i)
5262  {
5263  args.push_back("-framework");
5264 
5265  string frameworkName = *i;
5266  frameworkName = frameworkName.substr(0, frameworkName.length() - string(".framework").length());
5267  char *frameworkNameZ = strdup(frameworkName.c_str());
5268  args.push_back(frameworkNameZ);
5269  argsToFree.push_back(frameworkNameZ);
5270  }
5271 
5272  // When linking on a development workstation or Jenkins or an end-user system,
5273  // use the partial macOS SDK bundled in Vuo.framework, since it includes all the TBDs we need.
5274  string vuoFrameworkPath = getVuoFrameworkPath();
5275  string vuoFrameworkContainingFolder = vuoFrameworkPath + "/..";
5276  string frameworkMacOSSDKFolder = vuoFrameworkPath + "/SDKs/MacOSX10.11.sdk";
5277  if (!VuoFileUtilities::fileExists(frameworkMacOSSDKFolder))
5278  throw VuoException("Couldn't find the macOS SDK.");
5279 
5280  args.push_back("-Xlinker");
5281  args.push_back("-syslibroot");
5282  args.push_back("-Xlinker");
5283  char *frameworkMacOSSDKFolderZ = strdup(frameworkMacOSSDKFolder.c_str());
5284  args.push_back(frameworkMacOSSDKFolderZ);
5285  argsToFree.push_back(frameworkMacOSSDKFolderZ);
5286 
5287 
5288  // Linker option necessary for compatibility with our bundled version of ld64:
5289  args.push_back("-Xlinker");
5290  args.push_back("--no-demangle");
5291 
5292  if (isVerbose)
5293  args.push_back("-v");
5294 
5295  if (isDylib)
5296  args.push_back("-dynamiclib");
5297 
5298  args.push_back("-Xlinker");
5299  args.push_back("-headerpad_max_install_names");
5300 
5301  // Tell the built dylib/executable where to find Vuo.framework
5302  args.push_back("-rpath");
5303  string rPathArg = (rPath.empty() ? vuoFrameworkContainingFolder : rPath);
5304  args.push_back(rPathArg.c_str());
5305 
5306 #ifdef COVERAGE
5307  args.push_back("-rpath");
5308  args.push_back(LLVM_ROOT "/lib");
5309 #endif
5310 
5311  args.push_back("-target");
5312  args.push_back(target.c_str());
5313 
5314  args.push_back("-std=c++11");
5315  args.push_back("-stdlib=libc++");
5316  args.push_back("-mmacosx-version-min=10.10");
5317 
5318  // Allow clang to print meaningful error messages.
5319  auto diagnosticConsumer = new VuoCompilerDiagnosticConsumer(issues);
5320  clang::DiagnosticOptions *diagOptions = new clang::DiagnosticOptions();
5321  IntrusiveRefCntPtr<clang::DiagnosticIDs> DiagID(new clang::DiagnosticIDs());
5322  clang::DiagnosticsEngine Diags(DiagID, diagOptions, diagnosticConsumer);
5323 
5324  if (isVerbose)
5325  {
5326  ostringstream s;
5327  for (vector<const char *>::iterator i = args.begin(); i != args.end(); ++i)
5328  s << *i << " ";
5329  VUserLog("\t%s", s.str().c_str());
5330  }
5331 
5332  // Redirect linker output to a file, so we can feed it through VuoLog.
5333  string stdoutFile = VuoFileUtilities::makeTmpFile("vuo-linker-output", "txt");
5334  const StringRef stdoutPath(stdoutFile);
5335  const StringRef *redirects[] = {
5336  nullptr, // stdin
5337  &stdoutPath, // stdout
5338  &stdoutPath, // stderr
5339  };
5340 
5341  // ExecuteAndWait's args needs to be null-terminated.
5342  const char **argsz = (const char **)malloc(sizeof(char *) * args.size() + 1);
5343  for (int i = 0; i < args.size(); ++i)
5344  argsz[i] = args[i];
5345  argsz[args.size()] = nullptr;
5346 
5347  string errMsg;
5348  bool executionFailed;
5349  double t0 = VuoLogGetTime();
5350  int ret = llvm::sys::ExecuteAndWait(args[0], argsz, nullptr, redirects, 0, 0, &errMsg, &executionFailed);
5351 
5352  for (auto i : argsToFree)
5353  free(i);
5354 
5355  // Clean up composite module file.
5356  remove(compositeModulePath.c_str());
5357 
5358  if (!isDylib)
5359  // Ensure the linked binary has the execute permission set
5360  // (ld64-242 doesn't reliably set it, whereas ld64-133.3 did).
5361  // https://b33p.net/kosada/node/14152
5362  chmod(outputPath.c_str(), 0755);
5363 
5364  if (ret != 0)
5365  {
5366  string details;
5367  if (!errMsg.empty())
5368  {
5369  VUserLog("%s", errMsg.c_str());
5370  details += "\n" + errMsg + "\n";
5371  }
5372  string stdoutFileContents = VuoFileUtilities::readFileToString(stdoutFile);
5373  if (!stdoutFileContents.empty())
5374  {
5375  VUserLog("%s", stdoutFileContents.c_str());
5376  details += "\n" + stdoutFileContents + "\n";
5377  }
5378  VuoFileUtilities::deleteFile(stdoutFile);
5379 
5380  VuoCompilerIssue issue(VuoCompilerIssue::Error, "linking", outputPath,
5381  "Node broken or outdated", details);
5382  throw VuoCompilerException(issue);
5383  }
5384 
5385  VuoFileUtilities::deleteFile(stdoutFile);
5386  VDebugLog("\tLinking took %5.2fs", VuoLogGetTime() - t0);
5387 
5388  if (shouldAdHocCodeSign)
5389  adHocCodeSign(outputPath);
5390 }
5391 
5395 void VuoCompiler::adHocCodeSign(string path)
5396 {
5397  double t0 = VuoLogGetTime();
5399  "/usr/bin/codesign",
5400  "--sign",
5401  "-", // "-" = ad-hoc
5402  path,
5403  }, {
5404 #if VUO_PRO
5405  "CODESIGN_ALLOCATE=" + getCodesignAllocatePath(),
5406 #endif
5407  });
5408 
5409  VDebugLog("\tAd-hoc code-signing took %5.2fs", VuoLogGetTime() - t0);
5410 }
5411 
5417 Module *VuoCompiler::readModuleFromC(string inputPath, const vector<string> &headerSearchPaths, const vector<string> &extraArgs, VuoCompilerIssues *issues)
5418 {
5419  // llvm-3.1/llvm/tools/clang/examples/clang-interpreter/main.cpp
5420 
5421  vector<const char *> args;
5422  args.push_back(inputPath.c_str());
5423  args.push_back("-DVUO_COMPILER");
5424  args.push_back("-fblocks");
5425 
5426  // Sync with /CMakeLists.txt's `commonFlags`.
5427  args.push_back("-Wall");
5428  args.push_back("-Wextra");
5429  args.push_back("-Wimplicit-fallthrough");
5430  args.push_back("-Wno-unused-parameter");
5431  args.push_back("-Wno-c++11-extensions");
5432  args.push_back("-Wno-sign-compare");
5433  args.push_back("-Werror=implicit");
5434 
5435  if (VuoStringUtilities::endsWith(inputPath, ".cc"))
5436  {
5437  args.push_back("-std=c++11");
5438  args.push_back("-stdlib=libc++");
5439  }
5440 
5441  for (vector<string>::const_iterator i = headerSearchPaths.begin(); i != headerSearchPaths.end(); ++i)
5442  {
5443  args.push_back("-I");
5444  args.push_back(i->c_str());
5445  }
5446 
5447  if (isVerbose)
5448  args.push_back("-v");
5449 
5450  for (vector<string>::const_iterator i = extraArgs.begin(); i != extraArgs.end(); ++i)
5451  args.push_back(i->c_str());
5452 
5453  auto diagnosticConsumer = new VuoCompilerDiagnosticConsumer(issues);
5454  clang::DiagnosticOptions * diagOptions = new clang::DiagnosticOptions();
5455  IntrusiveRefCntPtr<clang::DiagnosticIDs> DiagID(new clang::DiagnosticIDs());
5456  clang::DiagnosticsEngine *diags = new clang::DiagnosticsEngine(DiagID, diagOptions, diagnosticConsumer);
5457 
5458  shared_ptr<clang::CompilerInvocation> compilerInvocation(new clang::CompilerInvocation);
5459  clang::CompilerInvocation::CreateFromArgs(*compilerInvocation, &args[0], &args[0] + args.size(), *diags);
5460  compilerInvocation->TargetOpts->Triple = target;
5461 
5462  clang::CompilerInstance Clang;
5463  Clang.setInvocation(compilerInvocation);
5464 
5465  Clang.setDiagnostics(diags);
5466  if (!Clang.hasDiagnostics())
5467  return NULL;
5468 
5469  // See CompilerInvocation::GetResourcesPath -- though we're not calling it because we don't have MainAddr.
5470  string builtinHeaderSearchPath;
5471  string vuoFrameworkPath = getVuoFrameworkPath();
5472  if (vuoFrameworkPath.empty())
5473  {
5474  builtinHeaderSearchPath = getClangPath();
5475  if (VuoStringUtilities::endsWith(builtinHeaderSearchPath, "Helpers/clang"))
5476  builtinHeaderSearchPath = VuoStringUtilities::substrBefore(builtinHeaderSearchPath, "Helpers/clang");
5477  else if (VuoStringUtilities::endsWith(builtinHeaderSearchPath, "bin/clang"))
5478  builtinHeaderSearchPath = VuoStringUtilities::substrBefore(builtinHeaderSearchPath, "bin/clang");
5479  builtinHeaderSearchPath += "lib/clang/" CLANG_VERSION_STRING;
5480  }
5481  else
5482  builtinHeaderSearchPath = vuoFrameworkPath + "/Frameworks/llvm.framework/Versions/A/lib/clang/" CLANG_VERSION_STRING;
5483  Clang.getHeaderSearchOpts().ResourceDir = builtinHeaderSearchPath;
5484 
5485 // OwningPtr<clang::CodeGenAction> Act(new clang::EmitLLVMOnlyAction()); // @@@ return value of takeModule() is destroyed at the end of this function
5486  clang::CodeGenAction *Act = new clang::EmitLLVMOnlyAction();
5487  if (!Clang.ExecuteAction(*Act))
5488  return NULL;
5489 
5490  unique_ptr<Module> module = Act->takeModule();
5491  if (!module)
5492  VUserLog("Error compiling %s: module is null.", inputPath.c_str());
5493  return module.release();
5494 }
5495 
5501 Module *VuoCompiler::readModuleFromBitcode(string inputPath, string arch)
5502 {
5503  string dir, file, ext;
5504  VuoFileUtilities::splitPath(inputPath, dir, file, ext);
5505  VuoFileUtilities::File inputFile(dir, file + "." + ext);
5506  return readModuleFromBitcode(&inputFile, arch);
5507 }
5508 
5516 Module *VuoCompiler::readModuleFromBitcode(VuoFileUtilities::File *inputFile, string arch)
5517 {
5518  size_t inputDataBytes;
5519  char *inputData = inputFile->getContentsAsRawData(inputDataBytes);
5520 
5521  string error;
5522  VuoLog_status("Loading module \"%s\" (%s)", inputFile->getRelativePath().c_str(), arch.c_str());
5523  Module *module = readModuleFromBitcodeData(inputData, inputDataBytes, arch, error);
5524  VuoLog_status(NULL);
5525  if (! module)
5526  VUserLog("Error: Couldn't parse module '%s' (%s): %s.", inputFile->getRelativePath().c_str(), arch.c_str(), error.c_str());
5527 
5528  free(inputData);
5529 
5530  return module;
5531 }
5532 
5538 Module *VuoCompiler::readModuleFromBitcodeData(char *inputData, size_t inputDataBytes, string arch, string &error)
5539 {
5540  if (inputDataBytes < sizeof(unsigned int))
5541  return nullptr;
5542 
5543  __block Module *module = nullptr;
5544  dispatch_sync(llvmQueue, ^{
5545  StringRef inputDataAsStringRef(inputData, inputDataBytes);
5546  auto mb = MemoryBuffer::getMemBuffer(inputDataAsStringRef, "", false);
5547  if (!mb)
5548  {
5549  error = "Couldn't create MemoryBuffer";
5550  return;
5551  }
5552 
5553  MemoryBufferRef bitcodeBuffer;
5554  string moduleArch;
5555  unsigned int fileID = *(unsigned int *)inputData;
5556  if (fileID == 0x0b17c0de)
5557  // This is a single-architecture LLVM bitcode `.bc` file, so read the entire file.
5558  bitcodeBuffer = mb.get()->getMemBufferRef();
5559 
5560  else if (fileID == 0xdec04342)
5561  {
5562  // This is a single-architecture raw bitcode file, presumably generated by Vuo 2.2.1 or earlier.
5563  bitcodeBuffer = mb.get()->getMemBufferRef();
5564  moduleArch = "x86_64";
5565  }
5566 
5567  else if (fileID == 0xbebafeca)
5568  {
5569  if (arch.empty())
5570  {
5571  error = "It's a Mach-O universal binary, but this compiler instance's LLVM target isn't set";
5572  return;
5573  }
5574 
5575  // This is a Mach-O wrapper around multiple LLVM bitcode files;
5576  // parse the Mach-O header to extract just a single architecture.
5577  auto binary = llvm::object::MachOUniversalBinary::create(mb.get()->getMemBufferRef());
5578  if (!binary)
5579  {
5580  error = "Couldn't read Mach-O universal binary:";
5581  handleAllErrors(binary.takeError(), [&error](const ErrorInfoBase &ei) {
5582  error += " " + ei.message();
5583  });
5584  return;
5585  }
5586 
5587  for (auto &o : binary.get()->objects())
5588  if (o.getArchFlagName() == arch)
5589  bitcodeBuffer = MemoryBufferRef(mb.get()->getMemBufferRef().getBuffer().slice(o.getOffset(), o.getOffset() + o.getSize()), "");
5590  if (!bitcodeBuffer.getBufferSize())
5591  {
5592  error = "The Mach-O universal binary doesn't have an \"" + arch + "\" slice";
5593  return;
5594  }
5595  }
5596 
5597  auto wrappedModule = llvm::parseBitcodeFile(bitcodeBuffer, *globalLLVMContext);
5598  if (!wrappedModule)
5599  {
5600  error = "Couldn't parse bitcode file:";
5601  handleAllErrors(wrappedModule.takeError(), [&error](const ErrorInfoBase &ei) {
5602  error += " " + ei.message();
5603  });
5604  return;
5605  }
5606 
5607  module = wrappedModule.get().release();
5608 
5609  if (moduleArch.empty())
5610  moduleArch = getTargetArch(module->getTargetTriple());
5611  if (moduleArch != arch)
5612  {
5613  error = "The module's CPU architecture \"" + moduleArch + "\" doesn't match the compiler's CPU architecture \"" + arch + "\"";
5614  delete module;
5615  module = nullptr;
5616  return;
5617  }
5618  });
5619  return module;
5620 }
5621 
5629 bool VuoCompiler::writeModuleToBitcode(Module *module, string outputPath)
5630 {
5631  string str;
5632  raw_string_ostream verifyOut(str);
5633  if (llvm::verifyModule(*module, &verifyOut))
5634  {
5635  VUserLog("Error: Module verification failed:\n%s", verifyOut.str().c_str());
5636  return true;
5637  }
5638 
5639  // Ensure the module gets output in the bitcode wrapper format instead of raw bitcode.
5640  if (module->getTargetTriple().empty())
5641  setTargetForModule(module, getProcessTarget());
5642 
5643  std::error_code err;
5644  raw_fd_ostream out(outputPath.c_str(), err, sys::fs::F_None);
5645  if (err)
5646  {
5647  VUserLog("Error: Couldn't open file '%s' for writing: %s", outputPath.c_str(), err.message().c_str());
5648  return true;
5649  }
5650  llvm::WriteBitcodeToFile(module, out);
5651 
5652  return false;
5653 }
5654 
5660 void VuoCompiler::setTargetForModule(Module *module, string targetTriple)
5661 {
5662  module->setTargetTriple(targetTriple);
5663 
5664  string error;
5665  auto target = TargetRegistry::lookupTarget(module->getTargetTriple(), error);
5666  if (!target)
5667  {
5668  VUserLog("Error: Couldn't look up target: %s", error.c_str());
5669  return;
5670  }
5671 
5672  auto targetMachine = target->createTargetMachine(module->getTargetTriple(), "", "", TargetOptions(), Optional<Reloc::Model>());
5673  if (!targetMachine)
5674  {
5675  VUserLog("Error: Couldn't create targetMachine.");
5676  return;
5677  }
5678 
5679  module->setDataLayout(targetMachine->createDataLayout());
5680 
5681  delete targetMachine;
5682 }
5683 
5688 string VuoCompiler::getTargetArch(string target)
5689 {
5690  auto hyphen = target.find('-');
5691  if (hyphen == string::npos)
5692  return "";
5693 
5694  return target.substr(0, hyphen);
5695 }
5696 
5700 string VuoCompiler::getProcessTarget(void)
5701 {
5702  // llvm::sys::getProcessTriple() returns `LLVM_HOST_TRIPLE`,
5703  // which is always `x86_64-*` since we built LLVM on x86_64.
5704  // Instead, use llvm::sys::getDefaultTargetTriple()
5705  // which _actually_ returns the current process's target.
5706  return getTargetArch(llvm::sys::getDefaultTargetTriple())
5707  // Always target OS `macos10.10.0` (ignoring the current process's OS)
5708  // to ensure compatibility with 3rd-party modules.
5709  + "-apple-macosx10.10.0";
5710 }
5711 
5719 {
5720  Module *llvmModule = module->getModule();
5721 
5722  // In C++ the return value of a cast may not be the same pointer as the cast arg.
5723  // Because of this, VuoModule::getPseudoBase() returns a different value than VuoNodeClass::getBase().
5724  // Calling delete on VuoModule::getPseudoBase() gives a malloc error: "pointer being freed was not allocated".
5725  // So call it on VuoNodeClass::getBase(). Same idea for VuoType.
5726 
5727  VuoNodeClass *baseNodeClass = NULL;
5728  VuoType *baseType = NULL;
5729  VuoModule *baseModule = NULL;
5730  VuoCompilerNodeClass *nodeClass = dynamic_cast<VuoCompilerNodeClass *>(module);
5731  if (nodeClass)
5732  {
5733  baseNodeClass = dynamic_cast<VuoNodeClass *>(nodeClass->getBase());
5734 
5736  if (dynamic_cast<VuoCompilerSpecializedNodeClass *>(module))
5737  module = NULL;
5738  }
5739  else
5740  {
5741  VuoCompilerType *type = dynamic_cast<VuoCompilerType *>(module);
5742  if (type)
5743  baseType = dynamic_cast<VuoType *>(type->getBase());
5744  else
5745  baseModule = module->getPseudoBase();
5746  }
5747 
5748  delete module;
5749  delete baseNodeClass;
5750  delete baseType;
5751  delete baseModule;
5752  destroyLlvmModule(llvmModule);
5753 }
5754 
5762 {
5763  dispatch_sync(llvmQueue, ^{
5764  delete module;
5765  });
5766 }
5767 
5779 VuoNode * VuoCompiler::createNode(VuoCompilerNodeClass *nodeClass, string title, double x, double y)
5780 {
5782  if (nodeClassForNode)
5783  return nodeClassForNode->newNode(title, x, y);
5784 
5785  return nullptr;
5786 }
5787 
5794 VuoNode * VuoCompiler::createNode(VuoCompilerNodeClass *nodeClass, VuoNode *nodeToCopyMetadataFrom)
5795 {
5797  if (nodeClassForNode)
5798  return nodeClassForNode->newNode(nodeToCopyMetadataFrom);
5799 
5800  return nullptr;
5801 }
5802 
5806 VuoNode * VuoCompiler::createPublishedInputNode(vector<VuoPublishedPort *> publishedInputPorts)
5807 {
5808  string nodeClassName = VuoCompilerPublishedInputNodeClass::buildNodeClassName(publishedInputPorts);
5809  return createPublishedNode(nodeClassName, publishedInputPorts);
5810 }
5811 
5815 VuoNode * VuoCompiler::createPublishedOutputNode(vector<VuoPublishedPort *> publishedOutputPorts)
5816 {
5817  string nodeClassName = VuoCompilerPublishedOutputNodeClass::buildNodeClassName(publishedOutputPorts);
5818  return createPublishedNode(nodeClassName, publishedOutputPorts);
5819 }
5820 
5824 VuoNode * VuoCompiler::createPublishedNode(const string &nodeClassName, const vector<VuoPublishedPort *> &publishedPorts)
5825 {
5826  VuoCompilerNodeClass *nodeClass = getNodeClass(nodeClassName);
5827  VuoNode *node = createNode(nodeClass);
5828 
5829  // Set the generic port types on the node to match those on the published ports set by VuoCompilerComposition::updateGenericPortTypes().
5830  VuoCompilerPublishedInputNodeClass *inputNodeClass = dynamic_cast<VuoCompilerPublishedInputNodeClass *>(nodeClass);
5831  VuoCompilerPublishedOutputNodeClass *outputNodeClass = dynamic_cast<VuoCompilerPublishedOutputNodeClass *>(nodeClass);
5832  for (size_t i = 0; i < publishedPorts.size(); ++i)
5833  {
5834  VuoType *publishedPortType = static_cast<VuoCompilerPort *>(publishedPorts[i]->getCompiler())->getDataVuoType();
5835  if (! dynamic_cast<VuoGenericType *>(publishedPortType))
5836  continue;
5837 
5838  set<VuoPort *> nodePorts;
5839  if (inputNodeClass)
5840  {
5841  VuoPort *inputPort = node->getInputPorts().at( inputNodeClass->getInputPortIndexForPublishedInputPort(i) );
5842  nodePorts.insert(inputPort);
5843 
5844  VuoPort *outputPort = node->getOutputPorts().at( inputNodeClass->getOutputPortIndexForPublishedInputPort(i) );
5845  nodePorts.insert(outputPort);
5846  }
5847  else
5848  {
5849  VuoPort *inputPort = node->getInputPorts().at( outputNodeClass->getInputPortIndexForPublishedOutputPort(i) );
5850  nodePorts.insert(inputPort);
5851  }
5852 
5853  for (VuoPort *port : nodePorts)
5854  {
5855  VuoCompilerPort *compilerPort = static_cast<VuoCompilerPort *>(port->getCompiler());
5856  compilerPort->setDataVuoType(publishedPortType);
5857  }
5858  }
5859 
5860  reifyGenericPortTypes(node);
5861 
5862  return node;
5863 }
5864 
5870 {
5871  dispatch_sync(environmentQueue, ^{
5872  if (environments.size() >= 5)
5873  environments.at(3).at(0)->addExpatriateSourceFile(sourcePath);
5874  });
5875 }
5876 
5882 {
5883  dispatch_sync(environmentQueue, ^{
5884  if (environments.size() >= 5)
5885  {
5886  environments.at(3).at(0)->removeExpatriateSourceFile(sourcePath);
5887 
5888  set<string> sourcesRemoved;
5889  sourcesRemoved.insert(getModuleKeyForPath(sourcePath));
5890  loadModulesAndSources(set<string>(), set<string>(), set<string>(), set<string>(), set<string>(), sourcesRemoved,
5891  false, false, environments.at(3).at(0), nullptr, nullptr, "");
5892  }
5893  });
5894 }
5895 
5912 void VuoCompiler::overrideInstalledNodeClass(const string &sourcePath, const string &sourceCode)
5913 {
5914  string sourcePathCopy = sourcePath;
5915  string sourceCodeCopy = sourceCode;
5916 
5917  dispatch_async(environmentQueue, ^{
5918  string nodeClassName = getModuleKeyForPath(sourcePathCopy);
5919  ModuleInfo *sourceInfo = NULL;
5920 
5921  for (const vector<Environment *> &envs : environments)
5922  {
5923  for (Environment *env : envs)
5924  {
5925  ModuleInfo *potentialSourceInfo = env->listSourceFile(nodeClassName);
5926  if (potentialSourceInfo && VuoFileUtilities::arePathsEqual(potentialSourceInfo->getFile()->path(), sourcePathCopy))
5927  {
5928  sourceInfo = potentialSourceInfo;
5929  break;
5930  }
5931  }
5932  }
5933 
5934  if (! sourceInfo)
5935  return;
5936 
5937  bool shouldRecompileSourcesIfUnchanged;
5938  if (! sourceCodeCopy.empty())
5939  {
5940  sourceInfo->setSourceCode(sourceCodeCopy);
5941  sourceInfo->setSourceCodeOverridden(true);
5942 
5943  shouldRecompileSourcesIfUnchanged = false;
5944  }
5945  else
5946  {
5947  sourceInfo->revertSourceCode();
5948  sourceInfo->setSourceCodeOverridden(false);
5949 
5950  shouldRecompileSourcesIfUnchanged = true;
5951  }
5952  sourceInfo->setAttempted(false);
5953  sourceInfo->setLastModifiedToNow();
5954 
5955  set<string> sourcesModified;
5956  sourcesModified.insert(nodeClassName);
5957 
5958  loadModulesAndSources(set<string>(), set<string>(), set<string>(), set<string>(), sourcesModified, set<string>(),
5959  false, shouldRecompileSourcesIfUnchanged, nullptr, nullptr, nullptr, "");
5960  });
5961 }
5962 
5972 void VuoCompiler::revertOverriddenNodeClass(const string &sourcePath)
5973 {
5974  overrideInstalledNodeClass(sourcePath, "");
5975 }
5976 
5989 VuoCompilerNodeClass * VuoCompiler::getNodeClass(const string &nodeClassName)
5990 {
5991  // For performance, don't bother searching if it's obviously not a node class.
5992 
5993  if (VuoStringUtilities::endsWith(nodeClassName, ".framework"))
5994  return NULL;
5995 
5996  // Attempt to load the node class (if it hasn't already been loaded).
5997 
5998  set<string> nodeClassNameSet;
5999  nodeClassNameSet.insert(nodeClassName);
6000  loadModulesIfNeeded(nodeClassNameSet);
6001 
6002  // If the node class has been loaded, return it.
6003 
6004  __block VuoCompilerNodeClass *nodeClass = NULL;
6005  void (^envGetNodeClass)(Environment *) = ^void (Environment *env) {
6006  VuoCompilerNodeClass *result = env->getNodeClass(nodeClassName);
6007  if (result)
6008  nodeClass = result;
6009  };
6010  applyToAllEnvironments(envGetNodeClass);
6011  return nodeClass;
6012 }
6013 
6019 map<string, VuoCompilerNodeClass *> VuoCompiler::getNodeClasses()
6020 {
6021  loadModulesIfNeeded();
6022 
6023  __block map<string, VuoCompilerNodeClass *> nodeClasses;
6024  void (^envGetNodeClasses)(Environment *) = ^void (Environment *env) {
6025  map<string, VuoCompilerNodeClass *> result = env->getNodeClasses();
6026  nodeClasses.insert(result.begin(), result.end());
6027  };
6028  applyToInstalledEnvironments(envGetNodeClasses);
6029  return nodeClasses;
6030 }
6031 
6037 VuoCompilerType * VuoCompiler::getType(const string &typeName)
6038 {
6039  set<string> typeNameSet;
6040  typeNameSet.insert(typeName);
6041  loadModulesIfNeeded(typeNameSet);
6042 
6043  __block VuoCompilerType *type = NULL;
6044  void (^envGetType)(Environment *) = ^void (Environment *env) {
6045  VuoCompilerType *result = env->getType(typeName);
6046  if (result)
6047  type = result;
6048  };
6049  applyToInstalledEnvironments(envGetType);
6050 
6051  if (! type && VuoGenericType::isGenericTypeName(typeName))
6052  {
6053  VuoCompilerType * (^compilerGetType)(string) = ^VuoCompilerType * (string moduleKey) {
6054  return getType(moduleKey);
6055  };
6056 
6057  VuoGenericType *genericType = new VuoGenericType(typeName, vector<string>());
6058  type = VuoCompilerGenericType::newGenericType(genericType, compilerGetType);
6059  }
6060 
6061  return type;
6062 }
6063 
6069 map<string, VuoCompilerType *> VuoCompiler::getTypes()
6070 {
6071  loadModulesIfNeeded();
6072 
6073  __block map<string, VuoCompilerType *> types;
6074  void (^envGetTypes)(Environment *) = ^void (Environment *env) {
6075  map<string, VuoCompilerType *> result = env->getTypes();
6076  types.insert(result.begin(), result.end());
6077  };
6078  applyToInstalledEnvironments(envGetTypes);
6079  return types;
6080 }
6081 
6087 VuoCompilerModule *VuoCompiler::getLibraryModule(const string &libraryModuleName)
6088 {
6089  set<string> libraryNameSet;
6090  libraryNameSet.insert(libraryModuleName);
6091  loadModulesIfNeeded(libraryNameSet);
6092 
6093  __block VuoCompilerModule *module = nullptr;
6094  void (^envGetLibraryModule)(Environment *) = ^void (Environment *env) {
6095  VuoCompilerModule *result = env->getLibraryModule(libraryModuleName);
6096  if (result)
6097  module = result;
6098  };
6099  applyToInstalledEnvironments(envGetLibraryModule);
6100 
6101  return module;
6102 }
6103 
6109 map<string, VuoCompilerModule *> VuoCompiler::getLibraryModules()
6110 {
6111  loadModulesIfNeeded();
6112 
6113  __block map<string, VuoCompilerModule *> libraryModules;
6114  void (^envGetLibraryModules)(Environment *) = ^void (Environment *env) {
6115  map<string, VuoCompilerModule *> result = env->getLibraryModules();
6116  libraryModules.insert(result.begin(), result.end());
6117  };
6118  applyToInstalledEnvironments(envGetLibraryModules);
6119  return libraryModules;
6120 }
6121 
6127 map<string, VuoNodeSet *> VuoCompiler::getNodeSets()
6128 {
6129  loadModulesIfNeeded();
6130 
6131  __block map<string, VuoNodeSet *> nodeSets;
6132  void (^envGetNodeSets)(Environment *) = ^void (Environment *env) {
6133  map<string, VuoNodeSet *> result = env->getNodeSets();
6134  nodeSets.insert(result.begin(), result.end());
6135  };
6136  applyToInstalledEnvironments(envGetNodeSets);
6137  return nodeSets;
6138 }
6139 
6146 {
6147  loadModulesIfNeeded();
6148 
6149  __block VuoNodeSet *nodeSet = NULL;
6150  void (^envFindNodeSet)(Environment *) = ^void (Environment *env) {
6151  VuoNodeSet *result = env->findNodeSet(name);
6152  if (result)
6153  nodeSet = result;
6154  };
6155  applyToInstalledEnvironments(envFindNodeSet);
6156  return nodeSet;
6157 }
6158 
6162 VuoCompilerModule * VuoCompiler::getModule(const string &moduleKey)
6163 {
6164  __block VuoCompilerModule *module = NULL;
6165  void (^envFindModule)(Environment *) = ^void (Environment *env) {
6166  VuoCompilerModule *result = env->findModule(moduleKey);
6167  if (result)
6168  module = result;
6169  };
6170  applyToAllEnvironments(envFindModule);
6171  return module;
6172 }
6173 
6185 void VuoCompiler::listNodeClasses(const string &format)
6186 {
6187  map<string, VuoCompilerNodeClass *> nodeClasses = getNodeClasses();
6188  for (map<string, VuoCompilerNodeClass *>::const_iterator i = nodeClasses.begin(); i != nodeClasses.end(); ++i)
6189  {
6190  VuoCompilerNodeClass *nodeClass = i->second;
6191  if (format == "")
6192  {
6193  printf("%s\n", nodeClass->getBase()->getClassName().c_str());
6194  }
6195  else if (format == "path")
6196  {
6197  // TODO: If "path", prints the absolute path of each node class, one per line.
6198  }
6199  else if (format == "dot")
6200  {
6201  VuoCompilerNode *node = nodeClass->newNode()->getCompiler();
6202 
6203  printf("%s\n", nodeClass->getDoxygenDocumentation().c_str());
6204  printf("%s\n\n", node->getGraphvizDeclaration().c_str());
6205 
6206  delete node;
6207  }
6208  }
6209 }
6210 
6217 vector<string> VuoCompiler::getCoreVuoDependencies(void)
6218 {
6219  vector<string> dependencies;
6220 
6221  dependencies.push_back("VuoHeap");
6222  dependencies.push_back("VuoApp");
6223 
6224  dependencies.push_back("zmq");
6225  dependencies.push_back("json-c");
6226  dependencies.push_back("objc");
6227  dependencies.push_back("c");
6228  dependencies.push_back("AppKit.framework");
6229 
6230 #ifdef COVERAGE
6231  dependencies.push_back(LLVM_ROOT "/lib/libprofile_rt.dylib");
6232 #endif
6233  return dependencies;
6234 }
6235 
6239 string VuoCompiler::getRuntimeMainDependency(void)
6240 {
6241  return "VuoRuntimeMain.o";
6242 }
6243 
6251 string VuoCompiler::getRuntimeDependency(void)
6252 {
6253  return "VuoRuntime.o";
6254 }
6255 
6262 string VuoCompiler::getVuoFrameworkPath(void)
6263 {
6264  if (! vuoFrameworkInProgressPath.empty())
6265  return vuoFrameworkInProgressPath;
6266 
6268 }
6269 
6273 string VuoCompiler::getClangPath(void)
6274 {
6275  return clangPath;
6276 }
6277 
6282 {
6283  // Start with the file name without extension.
6284  string dir, moduleKey, ext;
6285  VuoFileUtilities::splitPath(path, dir, moduleKey, ext);
6286 
6288  {
6289  vector<string> nodeClassNameParts = VuoStringUtilities::split(moduleKey, '.');
6290 
6291  // Remove repeated file extensions.
6292  while (nodeClassNameParts.size() > 1 && nodeClassNameParts.back() == ext)
6293  nodeClassNameParts.pop_back();
6294 
6295  // Convert each part to lowerCamelCase.
6296  for (string &part : nodeClassNameParts)
6297  part = VuoStringUtilities::convertToCamelCase(part, false, true, false);
6298 
6299  // If the node class name has only one part, add a prefix.
6300  if (nodeClassNameParts.size() == 1)
6301  nodeClassNameParts.insert(nodeClassNameParts.begin(), "isf");
6302 
6303  moduleKey = VuoStringUtilities::join(nodeClassNameParts, '.');
6304  }
6305 
6306  return moduleKey;
6307 }
6308 
6314 {
6315  __block bool isLocal = false;
6316 
6317  dispatch_sync(environmentQueue, ^{
6318  if (environments.size() >= 5)
6319  isLocal = environments.at(3).at(0)->findModule(moduleKey);
6320  });
6321 
6322  return isLocal;
6323 }
6324 
6332 {
6333  return lastCompositionBaseDir.empty() ? "" : lastCompositionBaseDir + "/Modules";
6334 }
6335 
6347 {
6348  return lastCompositionBaseDir;
6349 }
6350 
6354 void VuoCompiler::addModuleSearchPath(string path)
6355 {
6356  dispatch_sync(environmentQueue, ^{
6357  environments.back().at(0)->addModuleSearchPath(path);
6358  environments.back().at(0)->addLibrarySearchPath(path);
6359  });
6360 }
6361 
6365 void VuoCompiler::addHeaderSearchPath(const string &path)
6366 {
6367  dispatch_sync(environmentQueue, ^{
6368  environments.back().at(0)->addHeaderSearchPath(path);
6369  });
6370 }
6371 
6375 void VuoCompiler::addLibrarySearchPath(const string &path)
6376 {
6377  dispatch_sync(environmentQueue, ^{
6378  environments.back().at(0)->addLibrarySearchPath(path);
6379  });
6380 }
6381 
6385 void VuoCompiler::addFrameworkSearchPath(const string &path)
6386 {
6387  dispatch_sync(environmentQueue, ^{
6388  environments.back().at(0)->addFrameworkSearchPath(path);
6389  });
6390 }
6391 
6395 void VuoCompiler::setTelemetry(const string &telemetry)
6396 {
6397  this->telemetry = telemetry;
6398 }
6399 
6403 void VuoCompiler::setVerbose(bool isVerbose)
6404 {
6405  this->isVerbose = isVerbose;
6406 }
6407 
6413 {
6414 #if VUO_PRO
6415  if (VuoPro::getProAccess())
6416  {
6417  _shouldShowSplashWindow = false;
6418  return;
6419  }
6420 #endif
6421 
6422  _shouldShowSplashWindow = potentiallyShow;
6423 }
6424 
6429 {
6430  return _shouldShowSplashWindow;
6431 }
6432 
6436 void VuoCompiler::setClangPath(const string &clangPath)
6437 {
6438  this->clangPath = clangPath;
6439 }
6440 
6445 {
6446  string vuoFrameworkPath = getVuoFrameworkPath();
6447  return (vuoFrameworkPath.empty() ?
6448  VUO_BUILD_DIR "/bin/VuoCompositionLoader.app/Contents/MacOS/VuoCompositionLoader" :
6449  vuoFrameworkPath + "/Helpers/VuoCompositionLoader.app/Contents/MacOS/VuoCompositionLoader");
6450 }
6451 
6455 string VuoCompiler::getCompositionStubPath(void)
6456 {
6457  string vuoFrameworkPath = getVuoFrameworkPath();
6458  return (vuoFrameworkPath.empty() ?
6459  VUO_BUILD_DIR "/lib/libVuoCompositionStub.dylib" :
6460  vuoFrameworkPath + "/Modules/libVuoCompositionStub.dylib");
6461 }
6462 
6466 string VuoCompiler::getCachePathForComposition(const string compositionDir)
6467 {
6468  string modifierLetterColon("꞉");
6469  string cachedModulesName = compositionDir;
6470  VuoStringUtilities::replaceAll(cachedModulesName, "/", modifierLetterColon);
6471  return VuoFileUtilities::getCachePath() + "/" + cachedModulesName;
6472 }
6473 
6478 {
6479  __block vector<string> moduleSearchPaths;
6480  void (^envGetModuleSearchPaths)(Environment *) = ^void (Environment *env) {
6481  vector<string> result = env->getModuleSearchPaths();
6482  moduleSearchPaths.insert(moduleSearchPaths.end(), result.begin(), result.end());
6483  };
6484  applyToInstalledEnvironments(envGetModuleSearchPaths);
6485 
6486  __block vector<string> headerSearchPaths;
6487  void (^envGetHeaderSearchPaths)(Environment *) = ^void (Environment *env) {
6488  vector<string> result = env->getHeaderSearchPaths();
6489  headerSearchPaths.insert(headerSearchPaths.end(), result.begin(), result.end());
6490  };
6491  applyToInstalledEnvironments(envGetHeaderSearchPaths);
6492 
6493  __block vector<string> librarySearchPaths;
6494  void (^envGetLibrarySearchPaths)(Environment *) = ^void (Environment *env) {
6495  vector<string> result = env->getLibrarySearchPaths();
6496  librarySearchPaths.insert(librarySearchPaths.end(), result.begin(), result.end());
6497  };
6498  applyToInstalledEnvironments(envGetLibrarySearchPaths);
6499 
6500  __block vector<string> frameworkSearchPaths;
6501  void (^envGetFrameworkSearchPaths)(Environment *) = ^void (Environment *env) {
6502  vector<string> result = env->getFrameworkSearchPaths();
6503  frameworkSearchPaths.insert(frameworkSearchPaths.end(), result.begin(), result.end());
6504  };
6505  applyToInstalledEnvironments(envGetFrameworkSearchPaths);
6506 
6507  fprintf(stderr, "Module (node class, type, library) search paths:\n");
6508  for (vector<string>::iterator i = moduleSearchPaths.begin(); i != moduleSearchPaths.end(); ++i)
6509  fprintf(stderr, " %s\n", (*i).c_str());
6510  fprintf(stderr, "Header search paths:\n");
6511  for (vector<string>::iterator i = headerSearchPaths.begin(); i != headerSearchPaths.end(); ++i)
6512  fprintf(stderr, " %s\n", (*i).c_str());
6513  fprintf(stderr, "Other library search paths:\n");
6514  for (vector<string>::iterator i = librarySearchPaths.begin(); i != librarySearchPaths.end(); ++i)
6515  fprintf(stderr, " %s\n", (*i).c_str());
6516  fprintf(stderr, "Other framework search paths:\n");
6517  for (vector<string>::iterator i = frameworkSearchPaths.begin(); i != frameworkSearchPaths.end(); ++i)
6518  fprintf(stderr, " %s\n", (*i).c_str());
6519  fprintf(stderr, "Framework path:\n");
6520  if (! getVuoFrameworkPath().empty())
6521  fprintf(stderr, " %s\n", getVuoFrameworkPath().c_str());
6522  fprintf(stderr, "Clang path:\n");
6523  if (! getClangPath().empty())
6524  fprintf(stderr, " %s\n", getClangPath().c_str());
6525 }
6526 
6536 {
6537  try
6538  {
6539  VuoCompiler compiler(compositionFilePath);
6540  string directory, file, extension;
6541  VuoFileUtilities::splitPath(compositionFilePath, directory, file, extension);
6542  string compiledCompositionPath = VuoFileUtilities::makeTmpFile(file, "bc");
6543  string linkedCompositionPath = VuoFileUtilities::makeTmpFile(file + "-linked", "");
6544  compiler.compileComposition(compositionFilePath, compiledCompositionPath, true, issues);
6545  compiler.linkCompositionToCreateExecutable(compiledCompositionPath, linkedCompositionPath, Optimization_FastBuild);
6546  remove(compiledCompositionPath.c_str());
6547  return VuoRunner::newSeparateProcessRunnerFromExecutable(linkedCompositionPath, directory, false, true);
6548  }
6549  catch (VuoCompilerException &e)
6550  {
6551  if (issues != e.getIssues())
6552  issues->append(e.getIssues());
6553  return NULL;
6554  }
6555 }
6556 
6571 VuoRunner * VuoCompiler::newSeparateProcessRunnerFromCompositionString(string composition, string processName, string workingDirectory, VuoCompilerIssues *issues)
6572 {
6573  try
6574  {
6575  string compositionPath = (workingDirectory.empty() ? "." : workingDirectory) + "/" + processName + ".vuo";
6576  VuoCompiler compiler(compositionPath);
6577  string compiledCompositionPath = VuoFileUtilities::makeTmpFile(processName, "bc");
6578  string linkedCompositionPath = VuoFileUtilities::makeTmpFile(processName, "");
6579  compiler.compileCompositionString(composition, compiledCompositionPath, true, issues);
6580  compiler.linkCompositionToCreateExecutable(compiledCompositionPath, linkedCompositionPath, Optimization_FastBuild);
6581  remove(compiledCompositionPath.c_str());
6582  return VuoRunner::newSeparateProcessRunnerFromExecutable(linkedCompositionPath, workingDirectory, false, true);
6583  }
6584  catch (VuoCompilerException &e)
6585  {
6586  if (issues != e.getIssues())
6587  issues->append(e.getIssues());
6588  return NULL;
6589  }
6590 }
6591 
6601 {
6602  try
6603  {
6604  VuoCompiler compiler(compositionFilePath);
6605  string directory, file, extension;
6606  VuoFileUtilities::splitPath(compositionFilePath, directory, file, extension);
6607  string compiledCompositionPath = VuoFileUtilities::makeTmpFile(file, "bc");
6608  compiler.compileComposition(compositionFilePath, compiledCompositionPath, true, issues);
6609  string linkedCompositionPath = VuoFileUtilities::makeTmpFile(file, "dylib");
6610  compiler.linkCompositionToCreateDynamicLibrary(compiledCompositionPath, linkedCompositionPath, Optimization_FastBuild);
6611  remove(compiledCompositionPath.c_str());
6612  return VuoRunner::newCurrentProcessRunnerFromDynamicLibrary(linkedCompositionPath, directory, true);
6613  }
6614  catch (VuoCompilerException &e)
6615  {
6616  if (issues != e.getIssues())
6617  issues->append(e.getIssues());
6618  return NULL;
6619  }
6620 }
6621 
6636 VuoRunner * VuoCompiler::newCurrentProcessRunnerFromCompositionString(string composition, string workingDirectory, VuoCompilerIssues *issues)
6637 {
6638  try
6639  {
6640  string compositionPath = (workingDirectory.empty() ? "." : workingDirectory) + "/UntitledComposition.vuo";
6641  VuoCompiler compiler(compositionPath);
6642  string compiledCompositionPath = VuoFileUtilities::makeTmpFile("VuoRunnerComposition", "bc");
6643  compiler.compileCompositionString(composition, compiledCompositionPath, true, issues);
6644  string linkedCompositionPath = VuoFileUtilities::makeTmpFile("VuoRunnerComposition", "dylib");
6645  compiler.linkCompositionToCreateDynamicLibrary(compiledCompositionPath, linkedCompositionPath, Optimization_FastBuild);
6646  remove(compiledCompositionPath.c_str());
6647  return VuoRunner::newCurrentProcessRunnerFromDynamicLibrary(linkedCompositionPath, workingDirectory, true);
6648  }
6649  catch (VuoCompilerException &e)
6650  {
6651  if (issues != e.getIssues())
6652  issues->append(e.getIssues());
6653  return NULL;
6654  }
6655 }
6656 
6658 {
6659  pendingDataQueue = dispatch_queue_create("org.vuo.compiler.delegate.pending", 0);
6660 }
6661 
6663 {
6664  LoadedModulesData *data = dequeueData();
6665  data->release();
6666 }
6667 
6671 void VuoCompilerDelegate::enqueueData(LoadedModulesData *data)
6672 {
6673  dispatch_sync(pendingDataQueue, ^{
6674  pendingData.push_back(data);
6675  });
6676 }
6677 
6681 VuoCompilerDelegate::LoadedModulesData * VuoCompilerDelegate::dequeueData(void)
6682 {
6683  __block LoadedModulesData *ret;
6684  dispatch_sync(pendingDataQueue, ^{
6685  ret = pendingData.front();
6686  pendingData.pop_front();
6687  });
6688  return ret;
6689 }
6690 
6695 VuoCompilerDelegate::LoadedModulesData::LoadedModulesData(const set< pair<VuoCompilerModule *, VuoCompilerModule *> > &modulesModified,
6696  const set<VuoCompilerModule *> &modulesRemoved, VuoCompilerIssues *issues)
6697 {
6698  referenceCountQueue = dispatch_queue_create("org.vuo.compiler.delegate.reference", 0);
6699  referenceCount = 0;
6700 
6701  this->modulesModified = modulesModified;
6702  this->modulesRemoved = modulesRemoved;
6703  this->issues = issues;
6704 }
6705 
6709 VuoCompilerDelegate::LoadedModulesData::~LoadedModulesData(void)
6710 {
6711  delete issues;
6712 
6713  for (set< pair<VuoCompilerModule *, VuoCompilerModule *> >::iterator i = modulesModified.begin(); i != modulesModified.end(); ++i)
6714  VuoCompiler::destroyModule((*i).first);
6715 
6716  for (set<VuoCompilerModule *>::iterator i = modulesRemoved.begin(); i != modulesRemoved.end(); ++i)
6718 }
6719 
6723 void VuoCompilerDelegate::LoadedModulesData::retain(void)
6724 {
6725  dispatch_sync(referenceCountQueue, ^{
6726  ++referenceCount;
6727  });
6728 }
6729 
6733 void VuoCompilerDelegate::LoadedModulesData::release(void)
6734 {
6735  dispatch_sync(referenceCountQueue, ^{
6736  --referenceCount;
6737  if (referenceCount == 0) {
6738  delete this;
6739  }
6740  });
6741 }