Vuo  2.3.2
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"
31 #include "VuoCompilerGroup.hh"
33 #include "VuoComposition.hh"
34 #include "VuoException.hh"
35 #include "VuoGenericType.hh"
36 #include "VuoModuleCompiler.hh"
38 #include "VuoNode.hh"
39 #include "VuoNodeClass.hh"
40 #include "VuoNodeSet.hh"
41 #include "VuoPublishedPort.hh"
42 #include "VuoRunner.hh"
44 #include "VuoStringUtilities.hh"
45 
46 #if VUO_PRO
47 #include "VuoPro.hh"
48 #endif
49 
50 map<string, VuoFileUtilities::File *> VuoCompiler::Environment::moduleCacheFileForLocking;
51 dispatch_queue_t VuoCompiler::Environment::moduleCacheBuildingQueue = dispatch_queue_create("org.vuo.compiler.cache", NULL);
52 const string VuoCompiler::Environment::pidCacheDirPrefix = "pid-";
53 set<VuoCompiler *> VuoCompiler::allCompilers;
54 dispatch_queue_t VuoCompiler::environmentQueue = dispatch_queue_create("org.vuo.compiler.environment", NULL);
55 map<string, vector< vector<VuoCompiler::Environment *> > > VuoCompiler::sharedEnvironments;
56 map<string, map<string, vector<VuoCompiler::Environment *> > > VuoCompiler::environmentsForCompositionFamily;
57 dispatch_group_t VuoCompiler::moduleSourceCompilersExistGlobally = dispatch_group_create();
58 string VuoCompiler::vuoFrameworkInProgressPath;
59 
60 dispatch_queue_t llvmQueue = NULL;
61 
62 llvm::LLVMContext *VuoCompiler::globalLLVMContext = nullptr;
63 
67 static void __attribute__((constructor)) VuoCompiler_init(void)
68 {
69  // Increase the open-files limit (macOS defaults to 256).
70  struct rlimit rl{OPEN_MAX, OPEN_MAX};
71  getrlimit(RLIMIT_NOFILE, &rl);
72  rl.rlim_cur = MIN(OPEN_MAX, rl.rlim_max);
73  if (setrlimit(RLIMIT_NOFILE, &rl))
74  VUserLog("Warning: Couldn't set open-files limit: %s", strerror(errno));
75 
76 
77  llvmQueue = dispatch_queue_create("org.vuo.compiler.llvm", NULL);
78 
79  dispatch_sync(llvmQueue, ^{
80  // llvm::InitializeNativeTarget();
81  llvm::InitializeAllTargetMCs();
82  llvm::InitializeAllTargets();
83  // TargetRegistry::printRegisteredTargetsForVersion();
84 
85  VuoCompiler::globalLLVMContext = new llvm::LLVMContext;
86 
87  // Set up a diagnostic handler so that llvm::LLVMContext::diagnose doesn't exit
88  // when a call to llvm::Linker::linkInModule generates an error.
89  VuoCompiler::globalLLVMContext->setDiagnosticHandler([](const DiagnosticInfo &DI, void *context) {
90  string message;
91  raw_string_ostream stream(message);
92  DiagnosticPrinterRawOStream printer(stream);
93  DI.print(printer);
94  stream.flush();
95  if (! message.empty())
96  {
97  const char *severityName;
98  if (DI.getSeverity() == DS_Error)
99  severityName = "error";
100  else if (DI.getSeverity() == DS_Warning)
101  severityName = "warning";
102  else
103  severityName = "note";
104 
105  VuoLog(__FILE__, __LINE__, "llvmDiagnosticHandler", "%s: %s", severityName, message.c_str());
106  }
107  });
108 
109  // If the Vuo compiler/linker...
110  // 1. Loads a node class that uses dispatch_object_t.
111  // 2. Generates code that uses dispatch_object_t.
112  // 3. Links the node class into a composition.
113  // 4. Generates more code that uses dispatch_object_t.
114  // ... then Step 4 ends up with the wrong llvm::Type for dispatch_object_t.
115  //
116  // A workaround is to generate some code that uses dispatch_object_t before doing Step 1.
117  //
118  // https://b33p.net/kosada/node/3845
119  // http://lists.cs.uiuc.edu/pipermail/llvmdev/2012-December/057075.html
120  Module module("", *VuoCompiler::globalLLVMContext);
122 
123  // Workaround for a possibly related error where the compiler ends up with the wrong
124  // llvm::Type for dispatch_semaphore_s. https://b33p.net/kosada/node/10545
126 
127  // Load the NodeContext and PortContext struct types preemptively to make sure that
128  // their fields get the right dispatch types. If these struct types were to be loaded
129  // first from a subcomposition module, they'd get the wrong dispatch types.
130  // https://b33p.net/kosada/node/11160
133  });
134 }
135 
139 VuoCompiler::ModuleInfo::ModuleInfo(Environment *environment, const string &searchPath, const string &relativePath,
140  bool isSourceFile, bool isSubcomposition)
141 {
142  VuoFileUtilities::File *file = new VuoFileUtilities::File(searchPath, relativePath);
143  initialize(environment, searchPath, file, isSourceFile, isSubcomposition);
144 }
145 
149 VuoCompiler::ModuleInfo::ModuleInfo(Environment *environment, const string &searchPath, VuoFileUtilities::File *file,
150  bool isSourceFile, bool isSubcomposition)
151 {
152  initialize(environment, searchPath, file, isSourceFile, isSubcomposition);
153 }
154 
158 void VuoCompiler::ModuleInfo::initialize(Environment *environment, const string &searchPath, VuoFileUtilities::File *file,
159  bool isSourceFile, bool isSubcomposition)
160 {
161  this->environment = environment;
162  this->searchPath = searchPath;
163  this->file = file;
164  moduleKey = getModuleKeyForPath(file->getRelativePath());
165  sourceCodeOverridden = false;
166  lastModified = VuoFileUtilities::getFileLastModifiedInSeconds(file->isInArchive() ? file->getArchivePath() : file->path());
167  longestDownstreamPath = 0;
168  attempted = false;
169  loading = nullptr;
170 
171 #if VUO_PRO
172  init_Pro();
173 #endif
174 
175  if (isSourceFile)
176  {
177  loading = dispatch_group_create();
178  sourceCode = file->getContentsAsString();
179 
180  if (isSubcomposition)
181  {
182  try
183  {
185  }
186  catch (...)
187  {
188  // Issues parsing the composition will be caught later when compiling it.
189  }
190  }
191  }
192 }
193 
194 VuoCompiler::ModuleInfo::~ModuleInfo(void)
195 {
196 #if VUO_PRO
197  fini_Pro();
198 #endif
199 
200  delete file;
201 
202  if (loading)
203  dispatch_release(loading);
204 }
205 
206 VuoCompiler::Environment * VuoCompiler::ModuleInfo::getEnvironment(void) const
207 {
208  return environment;
209 }
210 
211 string VuoCompiler::ModuleInfo::getSearchPath(void) const
212 {
213  return searchPath;
214 }
215 
216 string VuoCompiler::ModuleInfo::getModuleKey(void) const
217 {
218  return moduleKey;
219 }
220 
221 VuoFileUtilities::File * VuoCompiler::ModuleInfo::getFile(void) const
222 {
223  return file;
224 }
225 
226 string VuoCompiler::ModuleInfo::getSourceCode(void) const
227 {
228  return sourceCode;
229 }
230 
231 void VuoCompiler::ModuleInfo::setSourceCode(const string &sourceCode)
232 {
233  this->sourceCode = sourceCode;
234 }
235 
236 void VuoCompiler::ModuleInfo::revertSourceCode(void)
237 {
238  sourceCode = file->getContentsAsString();
239 }
240 
241 bool VuoCompiler::ModuleInfo::isSourceCodeOverridden(void) const
242 {
243  return sourceCodeOverridden;
244 }
245 
246 void VuoCompiler::ModuleInfo::setSourceCodeOverridden(bool overridden)
247 {
248  sourceCodeOverridden = overridden;
249 }
250 
251 bool VuoCompiler::ModuleInfo::isNewerThan(ModuleInfo *other) const
252 {
253  return lastModified > other->lastModified;
254 }
255 
256 bool VuoCompiler::ModuleInfo::isNewerThan(unsigned long ageInSeconds) const
257 {
258  return lastModified > ageInSeconds;
259 }
260 
261 bool VuoCompiler::ModuleInfo::isOlderThan(unsigned long ageInSeconds) const
262 {
263  return lastModified < ageInSeconds;
264 }
265 
266 void VuoCompiler::ModuleInfo::setLastModifiedToNow(void)
267 {
268  struct timeval t;
269  gettimeofday(&t, NULL);
270  lastModified = t.tv_sec;
271 }
272 
273 set<string> VuoCompiler::ModuleInfo::getContainedNodeClasses(void) const
274 {
275  return containedNodeClasses;
276 }
277 
278 int VuoCompiler::ModuleInfo::getLongestDownstreamPath(void) const
279 {
280  return longestDownstreamPath;
281 }
282 
283 void VuoCompiler::ModuleInfo::setLongestDownstreamPath(int pathLength)
284 {
285  longestDownstreamPath = pathLength;
286 }
287 
288 bool VuoCompiler::ModuleInfo::getAttempted(void) const
289 {
290  return attempted;
291 }
292 
293 void VuoCompiler::ModuleInfo::setAttempted(bool attempted)
294 {
295  this->attempted = attempted;
296 }
297 
298 dispatch_group_t VuoCompiler::ModuleInfo::getLoadingGroup(void) const
299 {
300  return loading;
301 }
302 
303 void VuoCompiler::ModuleInfo::dump() const
304 {
305  fprintf(stderr, "module %s:\n"
306  "\tloadingGroup=%p\n"
307  "\tsearchPath=%s\n"
308  "\tattempted=%d\n"
309  "\tenvironment=%s\n"
310  "\tfile=%s%s%s\n"
311  "\tsourceCodeOverridden=%d\n"
312  "\tsourceCode=%s\n"
313  "\tcontainedNodeClasses:\n",
314  moduleKey.c_str(),
315  loading,
316  searchPath.c_str(),
317  attempted,
318  environment->getCompiledModuleCachePath().c_str(),
319  file->isInArchive() ? file->getArchivePath().c_str() : "",
320  file->isInArchive() ? "::" : "",
321  file->isInArchive() ? file->getRelativePath().c_str() : file->path().c_str(),
322  sourceCodeOverridden,
323  sourceCode.c_str());
324  for (auto i: containedNodeClasses)
325  fprintf(stderr, "\t\t%s\n", i.c_str());
326 }
327 
328 VuoCompiler::DependencyGraphVertex * VuoCompiler::DependencyGraphVertex::vertexForDependency(const string &dependency, VuoDirectedAcyclicGraph *graph)
329 {
330  VuoDirectedAcyclicGraph::Vertex *v = graph->findVertex(dependency);
331  if (v)
332  return dynamic_cast<DependencyGraphVertex *>(v);
333 
334  DependencyGraphVertex *vv = new DependencyGraphVertex();
335  vv->dependency = dependency;
336  vv->environment = NULL;
337  vv->compatible = true;
338  return vv;
339 }
340 
341 string VuoCompiler::DependencyGraphVertex::getDependency(void)
342 {
343  return dependency;
344 }
345 
346 VuoCompiler::Environment * VuoCompiler::DependencyGraphVertex::getEnvironment(void)
347 {
348  return environment;
349 }
350 
351 void VuoCompiler::DependencyGraphVertex::setEnvironment(Environment *environment)
352 {
353  this->environment = environment;
354 }
355 
356 string VuoCompiler::DependencyGraphVertex::key(void)
357 {
358  return dependency;
359 }
360 
368 VuoCompiler::ModuleInfoIterator::ModuleInfoIterator(map<string, map<string, ModuleInfo *> > *allModuleInfos,
369  const string &overriddenCompiledModuleCachePath)
370 {
371  this->allModuleInfos = allModuleInfos;
372  this->overriddenCompiledModuleCachePath = overriddenCompiledModuleCachePath;
373  hasSearchModuleKeys = false;
374  initialize();
375 }
376 
384 VuoCompiler::ModuleInfoIterator::ModuleInfoIterator(map<string, map<string, ModuleInfo *> > *allModuleInfos,
385  const string &overriddenCompiledModuleCachePath,
386  const set<string> &searchModuleKeys)
387 {
388  this->allModuleInfos = allModuleInfos;
389  this->overriddenCompiledModuleCachePath = overriddenCompiledModuleCachePath;
390  this->searchModuleKeys = searchModuleKeys;
391  hasSearchModuleKeys = true;
392  initialize();
393 }
394 
400 void VuoCompiler::ModuleInfoIterator::initialize(void)
401 {
402  currSearchPath = allModuleInfos->begin();
403  hasCurrModuleKey = false;
404 
405  if (! overriddenCompiledModuleCachePath.empty())
406  {
407  auto i = allModuleInfos->find(overriddenCompiledModuleCachePath);
408  if (i != allModuleInfos->end())
409  {
410  std::transform(i->second.begin(), i->second.end(),
411  std::inserter(overriddenModuleKeys, overriddenModuleKeys.begin()),
412  [](const pair<string, ModuleInfo *> &p) { return p.first; });
413  }
414  }
415 }
416 
422 VuoCompiler::ModuleInfo * VuoCompiler::ModuleInfoIterator::next(void)
423 {
424  for ( ; currSearchPath != allModuleInfos->end(); ++currSearchPath)
425  {
426  if (! hasCurrModuleKey)
427  {
428  currModuleKey = currSearchPath->second.begin();
429  hasCurrModuleKey = true;
430  }
431 
432  bool isOverriddenCompiledModuleCachePath = ! overriddenCompiledModuleCachePath.empty() &&
433  VuoFileUtilities::arePathsEqual(currSearchPath->first, overriddenCompiledModuleCachePath);
434 
435  for ( ; currModuleKey != currSearchPath->second.end(); ++currModuleKey)
436  {
437  if ((! hasSearchModuleKeys || searchModuleKeys.find(currModuleKey->first) != searchModuleKeys.end()) &&
438  (isOverriddenCompiledModuleCachePath || overriddenModuleKeys.find(currModuleKey->first) == overriddenModuleKeys.end()))
439  {
440  ModuleInfo *moduleInfo = currModuleKey->second;
441  ++currModuleKey;
442  return moduleInfo;
443  }
444  }
445 
446  hasCurrModuleKey = false;
447  }
448 
449  return NULL;
450 }
451 
455 VuoCompiler::Environment::Environment(string target, bool builtIn, bool generated)
456  : target(target), builtIn(builtIn), generated(generated)
457 {
458  compilersToNotifyQueue = dispatch_queue_create("org.vuo.compiler.notify", 0);
459  moduleSearchPathContentsChangedQueue = dispatch_queue_create("org.vuo.compiler.watch", 0);
460  lastModuleCacheRebuild = 0;
461  isModuleCacheableDataDirty = false;
462  isModuleCacheInitialized = false;
463  isModuleCacheAvailable = false;
464  dependencyGraph = new VuoDirectedAcyclicGraph();
465  compositionDependencyGraph = new VuoDirectedAcyclicGraph();
466  moduleCompilationQueue = new VuoModuleCompilationQueue();
467 }
468 
472 VuoCompiler::Environment::~Environment(void)
473 {
474  stopWatchingModuleSearchPaths();
475  dispatch_sync(moduleSearchPathContentsChangedQueue, ^{});
476 
477  dispatch_release(moduleSearchPathContentsChangedQueue);
478  dispatch_release(compilersToNotifyQueue);
479 
480  for (map<string, VuoCompilerNodeClass *>::iterator i = nodeClasses.begin(); i != nodeClasses.end(); ++i)
481  destroyModule(i->second);
482 
483  for (map<string, VuoCompilerType *>::iterator i = types.begin(); i != types.end(); ++i)
484  destroyModule(i->second);
485 
486  for (map<string, VuoCompilerModule *>::iterator i = libraryModules.begin(); i != libraryModules.end(); ++i)
487  destroyModule(i->second);
488 
489  for (set<VuoCompilerGenericType *>::iterator i = genericTypes.begin(); i != genericTypes.end(); ++i)
490  {
491  VuoType *base = (*i)->getBase();
492  delete *i;
493  delete base;
494  }
495 
496  for (map<string, VuoNodeSet *>::iterator i = nodeSetForName.begin(); i != nodeSetForName.end(); ++i)
497  delete i->second;
498 
499  for (map<string, map<string, ModuleInfo *> >::iterator i = moduleFilesAtSearchPath.begin(); i != moduleFilesAtSearchPath.end(); ++i)
500  for (map<string, ModuleInfo *>::iterator j = i->second.begin(); j != i->second.end(); ++j)
501  delete j->second;
502 
503  for (map<string, map<string, ModuleInfo *> >::iterator i = sourceFilesAtSearchPath.begin(); i != sourceFilesAtSearchPath.end(); ++i)
504  for (map<string, ModuleInfo *>::iterator j = i->second.begin(); j != i->second.end(); ++j)
505  delete j->second;
506 
507  delete dependencyGraph;
508  delete compositionDependencyGraph;
509 }
510 
514 string VuoCompiler::Environment::getTarget()
515 {
516  return target;
517 }
518 
522 void VuoCompiler::Environment::addCompilerToNotify(VuoCompiler *compiler)
523 {
524  dispatch_sync(compilersToNotifyQueue, ^{
525  compilersToNotify.insert(compiler);
526  });
527 }
528 
532 void VuoCompiler::Environment::removeCompilerToNotify(VuoCompiler *compiler)
533 {
534  dispatch_sync(compilersToNotifyQueue, ^{
535  compilersToNotify.erase(compiler);
536  });
537 }
538 
544 map<string, VuoCompilerNodeClass *> VuoCompiler::Environment::getNodeClasses(void)
545 {
546  return nodeClasses;
547 }
548 
554 VuoCompilerNodeClass * VuoCompiler::Environment::getNodeClass(const string &moduleKey)
555 {
556  map<string, VuoCompilerNodeClass *>::iterator nodeClassIter = nodeClasses.find(moduleKey);
557  if (nodeClassIter != nodeClasses.end())
558  return nodeClassIter->second;
559 
560  return NULL;
561 }
562 
567 VuoCompilerNodeClass * VuoCompiler::Environment::takeNodeClass(const string &moduleKey)
568 {
569  VuoCompilerNodeClass *nodeClass = NULL;
570 
571  map<string, VuoCompilerNodeClass *>::iterator nodeClassIter = nodeClasses.find(moduleKey);
572  if (nodeClassIter != nodeClasses.end())
573  {
574  nodeClass = nodeClassIter->second;
575 
576  nodeClasses.erase(nodeClassIter);
577  removeFromDependencyGraph(nodeClass);
578  modulesChanged();
579  }
580 
581  return nodeClass;
582 }
583 
589 map<string, VuoCompilerType *> VuoCompiler::Environment::getTypes(void)
590 {
591  return types;
592 }
593 
599 VuoCompilerType * VuoCompiler::Environment::getType(const string &moduleKey)
600 {
601  map<string, VuoCompilerType *>::iterator typeIter = types.find(moduleKey);
602  if (typeIter != types.end())
603  return typeIter->second;
604 
605  return NULL;
606 }
607 
613 map<string, VuoNodeSet *> VuoCompiler::Environment::getNodeSets(void)
614 {
615  return nodeSetForName;
616 }
617 
623 VuoCompilerModule *VuoCompiler::Environment::getLibraryModule(const string &libraryModuleName)
624 {
625  map<string, VuoCompilerModule *>::iterator libraryIter = libraryModules.find(libraryModuleName);
626  if (libraryIter != libraryModules.end())
627  return libraryIter->second;
628 
629  return nullptr;
630 }
631 
637 map<string, VuoCompilerModule *> VuoCompiler::Environment::getLibraryModules(void)
638 {
639  return libraryModules;
640 }
641 
648 VuoCompilerModule * VuoCompiler::Environment::findModule(const string &moduleKey)
649 {
650  map<string, VuoCompilerNodeClass *>::iterator nodeClassIter = nodeClasses.find(moduleKey);
651  if (nodeClassIter != nodeClasses.end())
652  return nodeClassIter->second;
653 
654  map<string, VuoCompilerType *>::iterator typeIter = types.find(moduleKey);
655  if (typeIter != types.end())
656  return typeIter->second;
657 
658  map<string, VuoCompilerModule *>::iterator libraryIter = libraryModules.find(moduleKey);
659  if (libraryIter != libraryModules.end())
660  return libraryIter->second;
661 
662  return NULL;
663 }
664 
670 VuoNodeSet * VuoCompiler::Environment::findNodeSet(const string &name)
671 {
672 
673  map<string, VuoNodeSet *>::iterator nodeSetIter = nodeSetForName.find(name);
674  if (nodeSetIter != nodeSetForName.end())
675  return nodeSetIter->second;
676 
677  return NULL;
678 }
679 
686 void VuoCompiler::Environment::addModuleSearchPath(const string &path, bool shouldWatch)
687 {
688  moduleSearchPaths.push_back(path);
689 
690  updateModulesAtSearchPath(path);
691  updateSourceFilesAtSearchPath(path);
692 
693  if (shouldWatch)
694  startWatchingModuleSearchPath(path);
695 }
696 
702 vector<string> VuoCompiler::Environment::getModuleSearchPaths(void)
703 {
704  return moduleSearchPaths;
705 }
706 
712 void VuoCompiler::Environment::addHeaderSearchPath(const string &path)
713 {
714  headerSearchPaths.push_back(path);
715 }
716 
722 vector<string> VuoCompiler::Environment::getHeaderSearchPaths(void)
723 {
724  return headerSearchPaths;
725 }
726 
732 void VuoCompiler::Environment::addLibrarySearchPath(const string &path)
733 {
734  librarySearchPaths.push_back(path);
735 }
736 
742 vector<string> VuoCompiler::Environment::getLibrarySearchPaths(void)
743 {
744  return librarySearchPaths;
745 }
746 
752 void VuoCompiler::Environment::addFrameworkSearchPath(const string &path)
753 {
754  frameworkSearchPaths.push_back(path);
755 }
756 
762 vector<string> VuoCompiler::Environment::getFrameworkSearchPaths(void)
763 {
764  return frameworkSearchPaths;
765 }
766 
767 VuoDirectedAcyclicGraph * VuoCompiler::Environment::getDependencyGraph(void)
768 {
769  return dependencyGraph;
770 }
771 
772 VuoDirectedAcyclicGraph * VuoCompiler::Environment::getCompositionDependencyGraph(void)
773 {
774  return compositionDependencyGraph;
775 }
776 
782 void VuoCompiler::Environment::setModuleCachePath(const string &path, bool shouldAddModuleSearchPath)
783 {
784  moduleCachePath = path;
785 
786  if (shouldAddModuleSearchPath)
787  {
788  addModuleSearchPath(getCompiledModuleCachePath(), false);
789  addModuleSearchPath(getOverriddenCompiledModuleCachePath(), false);
790  }
791 }
792 
798 string VuoCompiler::Environment::getCompiledModuleCachePath(void)
799 {
800  return (moduleCachePath.empty() ? "" : moduleCachePath + "/Modules/" + VuoCompiler::getTargetArch(target));
801 }
802 
808 string VuoCompiler::Environment::getOverriddenCompiledModuleCachePath(void)
809 {
810  if (moduleCachePath.empty())
811  return "";
812 
813  ostringstream pid;
814  pid << getpid();
815 
816  string dir, moduleCacheDirName, ext;
817  VuoFileUtilities::splitPath(moduleCachePath, dir, moduleCacheDirName, ext);
818 
819  return (VuoFileUtilities::getCachePath() + "/" + pidCacheDirPrefix + pid.str() + "/" + moduleCacheDirName + "/Modules/" + VuoCompiler::getTargetArch(target));
820 }
821 
829 void VuoCompiler::Environment::addExpatriateSourceFile(const string &sourcePath)
830 {
831  expatriateSourceFiles.push_back(sourcePath);
832 
833  if (find(moduleSearchPaths.begin(), moduleSearchPaths.end(), "") == moduleSearchPaths.end())
834  moduleSearchPaths.push_back("");
835 
836  auto iter = sourceFilesAtSearchPath.find("");
837  if (iter != sourceFilesAtSearchPath.end())
838  sourceFilesAtSearchPath.erase(iter);
839 }
840 
846 void VuoCompiler::Environment::removeExpatriateSourceFile(const string &sourcePath)
847 {
848  for (auto i = expatriateSourceFiles.begin(); i != expatriateSourceFiles.end(); ++i)
849  {
850  if (VuoFileUtilities::arePathsEqual(*i, sourcePath))
851  {
852  expatriateSourceFiles.erase(i);
853 
854  auto iter = sourceFilesAtSearchPath.find("");
855  if (iter != sourceFilesAtSearchPath.end())
856  sourceFilesAtSearchPath.erase(iter);
857 
858  break;
859  }
860  }
861 }
862 
875 void VuoCompiler::Environment::updateModulesAtSearchPath(const string &path)
876 {
877  if (moduleFilesAtSearchPath.find(path) != moduleFilesAtSearchPath.end())
878  return;
879 
880  set<string> moduleExtensions;
881  moduleExtensions.insert("vuonode");
882  moduleExtensions.insert("vuonode+");
883  moduleExtensions.insert("bc");
884  moduleExtensions.insert("bc+");
885  set<string> archiveExtensions;
886  archiveExtensions.insert("vuonode");
887  set<VuoFileUtilities::File *> moduleFiles = VuoFileUtilities::findFilesInDirectory(path, moduleExtensions, archiveExtensions);
888 
889  map<string, ModuleInfo *> fileForModuleKey;
890  for (set<VuoFileUtilities::File *>::iterator i = moduleFiles.begin(); i != moduleFiles.end(); ++i)
891  {
892  VuoFileUtilities::File *moduleFile = *i;
893 
894  // Ignore macOS extended attribute storage (a.k.a. xattr, resource fork).
895  if (VuoStringUtilities::beginsWith(moduleFile->basename(), "._"))
896  continue;
897 
898  ModuleInfo *m = new ModuleInfo(this, path, moduleFile, false, false);
899  fileForModuleKey[m->getModuleKey()] = m;
900  }
901 
902  if (path == getCompiledModuleCachePath())
903  {
904  for (map<string, ModuleInfo *>::iterator i = fileForModuleKey.begin(); i != fileForModuleKey.end(); )
905  {
906  ModuleInfo *sourceInfo = listSourceFile(i->first);
907  if (! sourceInfo || sourceInfo->isNewerThan(i->second))
908  {
909  ModuleInfo *m = i->second;
910  VuoFileUtilities::deleteFile(m->getFile()->path());
911  delete m;
912  fileForModuleKey.erase(i++);
913  }
914  else
915  ++i;
916  }
917  }
918 
919  moduleFilesAtSearchPath[path] = fileForModuleKey;
920 }
921 
927 void VuoCompiler::Environment::updateModuleAtSearchPath(const string &moduleSearchPath, const string &moduleRelativePath)
928 {
929  string dir, file, ext;
930  VuoFileUtilities::splitPath(moduleRelativePath, dir, file, ext);
931 
932  set<string> moduleExtensions;
933  moduleExtensions.insert(ext);
934  set<string> archiveExtensions;
935  archiveExtensions.insert("vuonode");
936 
937  set<VuoFileUtilities::File *> moduleFiles = VuoFileUtilities::findFilesInDirectory(moduleSearchPath, moduleExtensions, archiveExtensions);
938 
939  for (set<VuoFileUtilities::File *>::iterator i = moduleFiles.begin(); i != moduleFiles.end(); ++i)
940  {
941  VuoFileUtilities::File *moduleFile = *i;
942 
943  ModuleInfo *m = new ModuleInfo(this, moduleSearchPath, moduleFile, false, false);
944  moduleFilesAtSearchPath[moduleSearchPath][m->getModuleKey()] = m;
945  }
946 }
947 
955 void VuoCompiler::Environment::updateSourceFilesAtSearchPath(const string &path)
956 {
957  map<string, map<string, ModuleInfo *> >::iterator sourceFilesIter = sourceFilesAtSearchPath.find(path);
958  if (sourceFilesIter != sourceFilesAtSearchPath.end())
959  return;
960 
961  set<VuoFileUtilities::File *> sourceFiles;
962  if (! path.empty())
963  {
964  set<string> sourceExtensions;
965  sourceExtensions.insert("vuo");
966  sourceExtensions.insert("fs");
968  sourceExtensions.insert(cext.begin(), cext.end());
969  sourceFiles = VuoFileUtilities::findFilesInDirectory(path, sourceExtensions, set<string>());
970  }
971  else
972  {
973  for (const string &sourcePath : expatriateSourceFiles)
974  {
975  string dir, file, ext;
976  VuoFileUtilities::splitPath(sourcePath, dir, file, ext);
977  VuoFileUtilities::File *sourceFile = new VuoFileUtilities::File(dir, file + "." + ext);
978  sourceFiles.insert(sourceFile);
979  }
980  }
981 
982  map<string, ModuleInfo *> fileForModuleKey;
983  for (set<VuoFileUtilities::File *>::iterator i = sourceFiles.begin(); i != sourceFiles.end(); ++i)
984  {
985  VuoFileUtilities::File *sourceFile = *i;
986 
987  string dir, moduleKey, ext;
988  VuoFileUtilities::splitPath(sourceFile->getRelativePath(), dir, moduleKey, ext);
989  bool isSubcomposition = (ext == "vuo");
990 
991  // Ignore missing expatriateSourceFiles — they might have been deleted in the meantime.
992  if (path.empty() && !sourceFile->exists())
993  continue;
994 
995  ModuleInfo *m = new ModuleInfo(this, path, sourceFile, true, isSubcomposition);
996  if (fileForModuleKey.find(m->getModuleKey()) != fileForModuleKey.end())
997  VUserLog("Warning: Conflicting source files for module %s are installed at %s", m->getModuleKey().c_str(), path.c_str());
998  fileForModuleKey[m->getModuleKey()] = m;
999 
1000  if (isSubcomposition)
1001  {
1002  DependencyGraphVertex *compositionVertex = DependencyGraphVertex::vertexForDependency(moduleKey, compositionDependencyGraph);
1003  compositionDependencyGraph->addVertex(compositionVertex);
1004 
1005  compositionVertex->setEnvironment(this);
1006 
1007  set<string> dependencies = m->getContainedNodeClasses();
1008  for (set<string>::iterator j = dependencies.begin(); j != dependencies.end(); ++j)
1009  {
1010  DependencyGraphVertex *dependencyVertex = DependencyGraphVertex::vertexForDependency(*j, compositionDependencyGraph);
1011  compositionDependencyGraph->addEdge(compositionVertex, dependencyVertex);
1012  }
1013  }
1014  }
1015 
1016  sourceFilesAtSearchPath[path] = fileForModuleKey;
1017 }
1018 
1024 VuoCompiler::ModuleInfo * VuoCompiler::Environment::listModule(const string &moduleKey)
1025 {
1026  for (const auto &moduleFiles : moduleFilesAtSearchPath)
1027  {
1028  map<string, ModuleInfo *>::const_iterator foundIter = moduleFiles.second.find(moduleKey);
1029  if (foundIter != moduleFiles.second.end())
1030  return foundIter->second;
1031  }
1032 
1033  return NULL;
1034 }
1035 
1041 VuoCompiler::ModuleInfoIterator VuoCompiler::Environment::listModules(const set<string> &moduleKeys)
1042 {
1043  for (const string &path : moduleSearchPaths)
1044  updateModulesAtSearchPath(path);
1045 
1046  return ModuleInfoIterator(&moduleFilesAtSearchPath, getOverriddenCompiledModuleCachePath(), moduleKeys);
1047 }
1048 
1054 VuoCompiler::ModuleInfoIterator VuoCompiler::Environment::listAllModules(void)
1055 {
1056  for (const string &path : moduleSearchPaths)
1057  updateModulesAtSearchPath(path);
1058 
1059  return ModuleInfoIterator(&moduleFilesAtSearchPath, getOverriddenCompiledModuleCachePath());
1060 }
1061 
1067 VuoCompiler::ModuleInfo * VuoCompiler::Environment::listSourceFile(const string &moduleKey)
1068 {
1069  for (const auto &sourceFiles : sourceFilesAtSearchPath)
1070  {
1071  map<string, ModuleInfo *>::const_iterator foundIter = sourceFiles.second.find(moduleKey);
1072  if (foundIter != sourceFiles.second.end())
1073  return foundIter->second;
1074  }
1075 
1076  return NULL;
1077 }
1078 
1084 VuoCompiler::ModuleInfoIterator VuoCompiler::Environment::listSourceFiles(const set<string> &moduleKeys)
1085 {
1086  for (const string &path : moduleSearchPaths)
1087  updateSourceFilesAtSearchPath(path);
1088 
1089  return ModuleInfoIterator(&sourceFilesAtSearchPath, "", moduleKeys);
1090 }
1091 
1097 VuoCompiler::ModuleInfoIterator VuoCompiler::Environment::listAllSourceFiles(void)
1098 {
1099  for (const string &path : moduleSearchPaths)
1100  updateSourceFilesAtSearchPath(path);
1101 
1102  return ModuleInfoIterator(&sourceFilesAtSearchPath, "");
1103 }
1104 
1108 vector<string> VuoCompiler::Environment::getBuiltInModuleSearchPaths(void)
1109 {
1110  vector<string> builtInModuleSearchPaths;
1111 
1112  string vuoFrameworkPath = getVuoFrameworkPath();
1113  if (! vuoFrameworkPath.empty())
1114  {
1115  builtInModuleSearchPaths.push_back(vuoFrameworkPath + "/Modules");
1116  }
1117  else
1118  {
1119  builtInModuleSearchPaths.push_back(VUO_BUILD_DIR "/library");
1120  builtInModuleSearchPaths.push_back(VUO_BUILD_DIR "/node");
1121  builtInModuleSearchPaths.push_back(VUO_BUILD_DIR "/type");
1122  builtInModuleSearchPaths.push_back(VUO_BUILD_DIR "/type/list");
1123  }
1124 
1125  return builtInModuleSearchPaths;
1126 }
1127 
1131 vector<string> VuoCompiler::Environment::getBuiltInHeaderSearchPaths(void)
1132 {
1133  vector<string> builtInHeaderSearchPaths;
1134 
1135  string vuoFrameworkPath = getVuoFrameworkPath();
1136  if (! vuoFrameworkPath.empty())
1137  {
1138  builtInHeaderSearchPaths.push_back(vuoFrameworkPath + "/Headers");
1139  builtInHeaderSearchPaths.push_back(vuoFrameworkPath + "/Headers/macos"); // system headers installed by Xcode Command Line Tools
1140  builtInHeaderSearchPaths.push_back(vuoFrameworkPath + "/Headers/macos/pthread"); //
1141  builtInHeaderSearchPaths.push_back(vuoFrameworkPath + "/Frameworks/llvm.framework/Versions/A/Headers/lib/c++/v1");
1142  }
1143  else
1144  {
1145  builtInHeaderSearchPaths.push_back(VUO_SOURCE_DIR "/library");
1146  builtInHeaderSearchPaths.push_back(VUO_SOURCE_DIR "/node");
1147  builtInHeaderSearchPaths.push_back(VUO_SOURCE_DIR "/type");
1148  builtInHeaderSearchPaths.push_back(VUO_SOURCE_DIR "/type/list");
1149  builtInHeaderSearchPaths.push_back(VUO_SOURCE_DIR "/runtime");
1150  builtInHeaderSearchPaths.push_back(JSONC_ROOT "/include");
1151  builtInHeaderSearchPaths.push_back(LLVM_ROOT "/include/c++/v1");
1152  }
1153 
1154  return builtInHeaderSearchPaths;
1155 }
1156 
1160 vector<string> VuoCompiler::Environment::getBuiltInLibrarySearchPaths(void)
1161 {
1162  vector<string> builtInLibrarySearchPaths;
1163 
1164  string vuoFrameworkPath = getVuoFrameworkPath();
1165  if (! vuoFrameworkPath.empty())
1166  {
1167  builtInLibrarySearchPaths.push_back(vuoFrameworkPath + "/Modules");
1168 
1169  // Ensure we (statically) link to our OpenSSL build when generating Vuo.framework's built-in module cache.
1170  builtInLibrarySearchPaths.push_back(OPENSSL_ROOT "/lib");
1171  }
1172  else
1173  {
1174  builtInLibrarySearchPaths.push_back(VUO_BUILD_DIR "/library");
1175  builtInLibrarySearchPaths.push_back(VUO_BUILD_DIR "/node");
1176  builtInLibrarySearchPaths.push_back(VUO_BUILD_DIR "/type");
1177  builtInLibrarySearchPaths.push_back(VUO_BUILD_DIR "/type/list");
1178  builtInLibrarySearchPaths.push_back(VUO_BUILD_DIR "/runtime");
1179 
1180  vector<string> conanLibDirs = VuoStringUtilities::split(CONAN_LIBRARY_PATHS, ';');
1181  builtInLibrarySearchPaths.insert(builtInLibrarySearchPaths.end(), conanLibDirs.begin(), conanLibDirs.end());
1182  }
1183 
1184  return builtInLibrarySearchPaths;
1185 }
1186 
1190 vector<string> VuoCompiler::Environment::getBuiltInFrameworkSearchPaths(void)
1191 {
1192  vector<string> builtInFrameworkSearchPaths;
1193 
1194  string vuoFrameworkPath = getVuoFrameworkPath();
1195  if (! vuoFrameworkPath.empty())
1196  {
1197  builtInFrameworkSearchPaths.push_back(vuoFrameworkPath + "/Modules/");
1198  builtInFrameworkSearchPaths.push_back(vuoFrameworkPath + "/Frameworks/");
1199  }
1200 
1201  return builtInFrameworkSearchPaths;
1202 }
1203 
1210 void VuoCompiler::Environment::startWatchingModuleSearchPath(const string &moduleSearchPath)
1211 {
1212  VuoFileWatcher *watcher = new VuoFileWatcher(this, moduleSearchPath);
1213  moduleSearchPathWatchers.insert(watcher);
1214 }
1215 
1221 void VuoCompiler::Environment::stopWatchingModuleSearchPaths(void)
1222 {
1223  for (set<VuoFileWatcher *>::iterator i = moduleSearchPathWatchers.begin(); i != moduleSearchPathWatchers.end(); ++i)
1224  delete *i;
1225 
1226  moduleSearchPathWatchers.clear();
1227 }
1228 
1233 void VuoCompiler::Environment::fileChanged(const string &moduleSearchPath)
1234 {
1235  dispatch_sync(moduleSearchPathContentsChangedQueue, ^{
1236  moduleSearchPathContentsChanged(moduleSearchPath);
1237  });
1238 }
1239 
1247 void VuoCompiler::Environment::moduleSearchPathContentsChanged(const string &moduleSearchPath)
1248 {
1249  //VLog(" E=%p -- %s", this, moduleSearchPath.c_str());
1250  VuoCompiler *compilerForLoading = new VuoCompiler(moduleSearchPath + "/unused");
1251 
1252  __block set<string> modulesAdded;
1253  __block set<string> modulesModifed;
1254  __block set<string> modulesRemoved;
1255  __block set<string> compositionsAdded;
1256  __block set<string> compositionsModifed;
1257  __block set<string> compositionsRemoved;
1258 
1259  dispatch_sync(environmentQueue, ^{
1260 
1261  // Remove the old file records from the environment.
1262 
1263  map<string, ModuleInfo *> oldModules;
1264  map<string, ModuleInfo *> oldCompositions;
1265 
1266  map<string, map<string, ModuleInfo *> >::iterator mf = moduleFilesAtSearchPath.find(moduleSearchPath);
1267  if (mf != moduleFilesAtSearchPath.end())
1268  {
1269  for (map<string, ModuleInfo *>::iterator j = mf->second.begin(); j != mf->second.end(); ++j)
1270  {
1271  oldModules[j->first] = j->second;
1272  }
1273 
1274  moduleFilesAtSearchPath.erase(mf);
1275  }
1276 
1277  map<string, map<string, ModuleInfo *> >::iterator cf = sourceFilesAtSearchPath.find(moduleSearchPath);
1278  if (cf != sourceFilesAtSearchPath.end())
1279  {
1280  for (map<string, ModuleInfo *>::iterator j = cf->second.begin(); j != cf->second.end(); ++j)
1281  {
1282  oldCompositions[j->first] = j->second;
1283 
1284  VuoDirectedAcyclicGraph::Vertex *vertex = compositionDependencyGraph->findVertex(j->first);
1285  compositionDependencyGraph->removeVertex(vertex);
1286  }
1287 
1288  sourceFilesAtSearchPath.erase(cf);
1289  }
1290 
1291  // Rebuild the file records based on the directory contents.
1292 
1293  updateModulesAtSearchPath(moduleSearchPath);
1294  updateSourceFilesAtSearchPath(moduleSearchPath);
1295 
1296  // Remove any overrides for source files that have been modified or removed.
1297 
1298  auto omf = moduleFilesAtSearchPath.find(getOverriddenCompiledModuleCachePath());
1299  if (omf != moduleFilesAtSearchPath.end())
1300  {
1301  for (auto oldComposition : oldCompositions)
1302  {
1303  for (auto i = omf->second.begin(); i != omf->second.end(); ++i)
1304  {
1305  if (i->first == oldComposition.first)
1306  {
1307  VuoFileUtilities::deleteFile(i->second->getFile()->path());
1308  omf->second.erase(i);
1309  break;
1310  }
1311  }
1312  }
1313  }
1314 
1315  // Compare the old and new file records to see what has changed.
1316 
1317  mf = moduleFilesAtSearchPath.find(moduleSearchPath);
1318  if (mf != moduleFilesAtSearchPath.end())
1319  {
1320  for (map<string, ModuleInfo *>::iterator n = mf->second.begin(); n != mf->second.end(); ++n)
1321  {
1322  string moduleKey = n->first;
1323 
1324  map<string, ModuleInfo *>::iterator o = oldModules.find(moduleKey);
1325  if (o != oldModules.end())
1326  {
1327  if (n->second->isNewerThan(o->second))
1328  {
1329  modulesModifed.insert(moduleKey);
1330  }
1331  else
1332  {
1333  n->second->setAttempted(o->second->getAttempted());
1334  }
1335 
1336  delete o->second;
1337  oldModules.erase(o);
1338  }
1339  else
1340  {
1341  modulesAdded.insert(moduleKey);
1342  }
1343  }
1344  }
1345 
1346  cf = sourceFilesAtSearchPath.find(moduleSearchPath);
1347  if (cf != sourceFilesAtSearchPath.end())
1348  {
1349  for (map<string, ModuleInfo *>::iterator n = cf->second.begin(); n != cf->second.end(); ++n)
1350  {
1351  string moduleKey = n->first;
1352 
1353  map<string, ModuleInfo *>::iterator o = oldCompositions.find(moduleKey);
1354  if (o != oldCompositions.end())
1355  {
1356  if (n->second->isNewerThan(o->second))
1357  {
1358  compositionsModifed.insert(moduleKey);
1359  }
1360  else
1361  {
1362  n->second->setAttempted(o->second->getAttempted());
1363  n->second->setSourceCode(o->second->getSourceCode());
1364  }
1365 
1366  delete o->second;
1367  oldCompositions.erase(o);
1368  }
1369  else
1370  {
1371  compositionsAdded.insert(moduleKey);
1372  }
1373  }
1374  }
1375 
1376  for (map<string, ModuleInfo *>::iterator o = oldModules.begin(); o != oldModules.end(); ++o)
1377  {
1378  delete o->second;
1379  modulesRemoved.insert(o->first);
1380  }
1381 
1382  for (map<string, ModuleInfo *>::iterator o = oldCompositions.begin(); o != oldCompositions.end(); ++o)
1383  {
1384  delete o->second;
1385  compositionsRemoved.insert(o->first);
1386  }
1387 
1388  compilerForLoading->loadModulesAndSources(modulesAdded, modulesModifed, modulesRemoved,
1389  compositionsAdded, compositionsModifed, compositionsRemoved,
1390  false, false, this, nullptr, nullptr, "");
1391  });
1392 
1393  delete compilerForLoading;
1394 }
1395 
1402 void VuoCompiler::Environment::moduleFileChanged(const string &modulePath, const string &moduleSourceCode,
1403  std::function<void(void)> moduleLoadedCallback,
1404  VuoCompiler *compiler, VuoCompilerIssues *issues)
1405 {
1406  //VLog(" E=%p -- %s", this, modulePath.c_str());
1407  dispatch_sync(environmentQueue, ^{
1408 
1409  string moduleDir, moduleKey, ext;
1410  VuoFileUtilities::splitPath(modulePath, moduleDir, moduleKey, ext);
1412 
1413  // Remove the old file record from the environment.
1414 
1415  bool foundOldModule = false;
1416  auto moduleSearchPathIter = moduleFilesAtSearchPath.find(moduleDir);
1417  if (moduleSearchPathIter != moduleFilesAtSearchPath.end())
1418  {
1419  auto moduleIter = moduleSearchPathIter->second.find(moduleKey);
1420  if (moduleIter != moduleSearchPathIter->second.end())
1421  {
1422  delete moduleIter->second;
1423  moduleSearchPathIter->second.erase(moduleIter);
1424  foundOldModule = true;
1425  }
1426  }
1427 
1428  // Update the file record for the module by re-checking the file.
1429 
1430  updateModuleAtSearchPath(moduleDir, moduleKey + "." + ext);
1431 
1432  // Compare the old and new file records to see how the module has changed.
1433 
1434  bool foundNewModule = false;
1435  moduleSearchPathIter = moduleFilesAtSearchPath.find(moduleDir);
1436  if (moduleSearchPathIter != moduleFilesAtSearchPath.end())
1437  {
1438  auto moduleIter = moduleSearchPathIter->second.find(moduleKey);
1439  if (moduleIter != moduleSearchPathIter->second.end())
1440  {
1441  foundNewModule = true;
1442  }
1443  }
1444 
1445  set<string> modulesAdded;
1446  set<string> modulesModified;
1447  set<string> modulesRemoved;
1448 
1449  if ((foundOldModule || VuoFileUtilities::arePathsEqual(moduleDir, getOverriddenCompiledModuleCachePath())) && foundNewModule)
1450  {
1451  modulesModified.insert(moduleKey);
1452  }
1453  else if (! foundOldModule && foundNewModule)
1454  {
1455  modulesAdded.insert(moduleKey);
1456  }
1457  else if (foundOldModule && ! foundNewModule)
1458  {
1459  modulesRemoved.insert(moduleKey);
1460  }
1461 
1462  compiler->loadModulesAndSources(modulesAdded, modulesModified, modulesRemoved,
1463  set<string>(), set<string>(), set<string>(),
1464  false, false, this, issues, moduleLoadedCallback, moduleSourceCode);
1465  });
1466 
1467 }
1468 
1472 void VuoCompiler::Environment::notifyCompilers(const set<VuoCompilerModule *> &modulesAdded,
1473  const set< pair<VuoCompilerModule *, VuoCompilerModule *> > &modulesModified,
1474  const set<VuoCompilerModule *> &modulesRemoved, VuoCompilerIssues *issues,
1475  bool oldModulesInvalidated)
1476 {
1477  if (! (modulesAdded.empty() && modulesModified.empty() && modulesRemoved.empty() && issues->isEmpty()) )
1478  {
1479  set< pair<VuoCompilerModule *, VuoCompilerModule *> > invalidatedModulesModified;
1480  set<VuoCompilerModule *> invalidatedModulesRemoved;
1481  if (oldModulesInvalidated)
1482  {
1483  invalidatedModulesModified = modulesModified;
1484  invalidatedModulesRemoved = modulesRemoved;
1485  }
1486 
1487  VuoCompilerDelegate::LoadedModulesData *delegateData = new VuoCompilerDelegate::LoadedModulesData(invalidatedModulesModified, invalidatedModulesRemoved, issues);
1488 
1489  map<string, VuoCompilerModule *> modulesAddedMap;
1490  map<string, pair<VuoCompilerModule *, VuoCompilerModule *> > modulesModifiedMap;
1491  map<string, VuoCompilerModule *> modulesRemovedMap;
1492  for (VuoCompilerModule *m : modulesAdded)
1493  modulesAddedMap[m->getPseudoBase()->getModuleKey()] = m;
1494  for (pair<VuoCompilerModule *, VuoCompilerModule *> m : modulesModified)
1495  modulesModifiedMap[m.first->getPseudoBase()->getModuleKey()] = m;
1496  for (VuoCompilerModule *m : modulesRemoved)
1497  modulesRemovedMap[m->getPseudoBase()->getModuleKey()] = m;
1498 
1499  dispatch_sync(compilersToNotifyQueue, ^{
1500  for (set<VuoCompiler *>::iterator i = compilersToNotify.begin(); i != compilersToNotify.end(); ++i) {
1501  delegateData->retain();
1502  }
1503  for (set<VuoCompiler *>::iterator i = compilersToNotify.begin(); i != compilersToNotify.end(); ++i) {
1504  (*i)->loadedModules(modulesAddedMap, modulesModifiedMap, modulesRemovedMap, issues, delegateData, this);
1505  }
1506  });
1507  }
1508  else
1509  {
1510  delete issues;
1511  }
1512 }
1513 
1524 set<VuoCompilerModule *> VuoCompiler::Environment::loadCompiledModules(const set<string> &moduleKeys, const map<string, string> &sourceCodeForModule)
1525 {
1526  ModuleInfoIterator modulesToLoadIter = listModules(moduleKeys);
1527  set<VuoCompilerModule *> modulesLoaded;
1528 
1529  ModuleInfo *moduleInfo;
1530  while ((moduleInfo = modulesToLoadIter.next()))
1531  {
1532  string moduleKey = moduleInfo->getModuleKey();
1533 
1534  // Skip the module if its file is not in this environment, if the module has already been loaded,
1535  // or if the compiler previously tried to load the module and it failed.
1536  if (moduleInfo->getEnvironment() != this || findModule(moduleKey) || moduleInfo->getAttempted())
1537  continue;
1538 
1539  moduleInfo->setAttempted(true);
1540 
1541  // Actually load the module.
1542  VuoCompilerModule *module = loadModule(moduleInfo);
1543 
1544  if (module)
1545  {
1546  modulesLoaded.insert(module);
1547  addToDependencyGraph(module);
1548  modulesChanged();
1549 
1550  // For a compiled subcomposition or other source file, store its source code in the VuoCompilerNodeClass.
1551  string searchPath = moduleInfo->getSearchPath();
1552  if (VuoFileUtilities::arePathsEqual(searchPath, getCompiledModuleCachePath()) ||
1553  VuoFileUtilities::arePathsEqual(searchPath, getOverriddenCompiledModuleCachePath()))
1554  {
1555  VuoCompilerNodeClass *nodeClass = dynamic_cast<VuoCompilerNodeClass *>(module);
1556  if (nodeClass)
1557  {
1558  ModuleInfo *sourceInfo = listSourceFile(moduleKey);
1559  if (sourceInfo)
1560  nodeClass->setSourcePath( sourceInfo->getFile()->path() );
1561 
1562  auto sourceCodeIter = sourceCodeForModule.find(moduleKey);
1563  if (sourceCodeIter != sourceCodeForModule.end())
1564  nodeClass->setSourceCode( sourceCodeIter->second );
1565  }
1566  }
1567  }
1568  }
1569 
1570  return modulesLoaded;
1571 }
1572 
1584 set<dispatch_group_t> VuoCompiler::Environment::loadSpecializedModules(const set<string> &moduleKeys,
1585  VuoCompiler *compiler, dispatch_queue_t llvmQueue)
1586 {
1587  set<dispatch_group_t > loadingGroups;
1588 
1589  for (string moduleKey : moduleKeys)
1590  {
1591  // Skip if it's not a node class.
1592 
1593  if (moduleKey.find(".") == string::npos || VuoStringUtilities::endsWith(moduleKey, ".framework"))
1594  continue;
1595 
1596  // Skip the module if it's already been loaded.
1597 
1598  if (findModule(moduleKey))
1599  continue;
1600 
1601  // Skip the module if it's in the process of being loaded, but have the caller wait for it to finish loading.
1602 
1603  map<string, dispatch_group_t>::iterator iter = specializedModulesLoading.find(moduleKey);
1604  if (iter != specializedModulesLoading.end())
1605  {
1606  loadingGroups.insert(iter->second);
1607  dispatch_retain(iter->second);
1608  continue;
1609  }
1610 
1611  dispatch_group_t loadingGroup = dispatch_group_create();
1612  specializedModulesLoading[moduleKey] = loadingGroup;
1613  loadingGroups.insert(loadingGroup);
1614 
1615  // Generate the module.
1616  // This is done asynchronously since VuoCompilerSpecializedNodeClass::newNodeClass() needs to use environmentQueue
1617  // to compile the generated C code.
1618 
1619  dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
1620  dispatch_group_async(loadingGroup, queue, ^{
1621  VuoNodeClass *baseNodeClass = nullptr;
1622  try
1623  {
1624  baseNodeClass = VuoCompilerSpecializedNodeClass::newNodeClass(moduleKey, compiler, llvmQueue);
1625  }
1626  catch (VuoCompilerException &e)
1627  {
1628  VUserLog("Error: %s", e.what());
1629  }
1630 
1631  if (baseNodeClass)
1632  {
1633  VuoCompilerSpecializedNodeClass *specNodeClass = static_cast<VuoCompilerSpecializedNodeClass *>(baseNodeClass->getCompiler());
1634  compiler->loadNodeClassGeneratedAtRuntime(specNodeClass, this);
1635 
1636  set<string> dependencies = specNodeClass->getDependencies();
1637  if (!dependencies.empty())
1638  compiler->loadModulesIfNeeded(dependencies);
1639  }
1640 
1641  dispatch_sync(environmentQueue, ^{
1642  specializedModulesLoading.erase(moduleKey);
1643  });
1644  });
1645  }
1646 
1647  return loadingGroups;
1648 }
1649 
1662 set<dispatch_group_t> VuoCompiler::Environment::compileModulesFromSourceCode(const set<string> &moduleKeys, bool shouldRecompileIfUnchanged)
1663 {
1664  ModuleInfoIterator modulesToLoadIter = listSourceFiles(moduleKeys);
1665 
1666  int environmentIndex = sharedEnvironments[target].size();
1667  for (int i = 0; i < sharedEnvironments[target].size(); ++i)
1668  if (this == sharedEnvironments[target].at(i).at(0))
1669  environmentIndex = i;
1670 
1671  set<dispatch_group_t> sourcesLoading;
1672  int sourcesEnqueued = 0;
1673  ModuleInfo *sourceInfo;
1674  while ((sourceInfo = modulesToLoadIter.next()))
1675  {
1676  string moduleKey = sourceInfo->getModuleKey();
1677 
1678  dispatch_group_t loadingGroup = sourceInfo->getLoadingGroup();
1679  sourcesLoading.insert(loadingGroup);
1680 
1681  // Skip compiling if the source file has already been scheduled for compilation.
1682  // Either its compilation is in progress or it failed to compile.
1683 
1684  if (sourceInfo->getAttempted())
1685  continue;
1686 
1687  // Skip compiling if the compiled module is up-to-date.
1688 
1689  string sourceCode = sourceInfo->getSourceCode();
1690 
1691  if (! shouldRecompileIfUnchanged)
1692  {
1693  VuoCompilerNodeClass *existingNodeClass = getNodeClass(moduleKey);
1694  if (existingNodeClass && existingNodeClass->getSourceCode() == sourceCode)
1695  continue;
1696  }
1697 
1698  // Enqueue the source file to be compiled.
1699 
1700  sourceInfo->setAttempted(true);
1701 
1702  dispatch_group_enter(loadingGroup);
1703  dispatch_group_enter(moduleSourceCompilersExistGlobally);
1704 
1706  queueItem->moduleKey = moduleKey;
1707  queueItem->sourcePath = sourceInfo->getFile()->path();
1708  queueItem->sourceCode = sourceCode;
1709  queueItem->sourceFile = sourceInfo->getFile();
1710  queueItem->cachedModulesPath = sourceInfo->isSourceCodeOverridden() ? getOverriddenCompiledModuleCachePath() : getCompiledModuleCachePath();
1711  queueItem->compiledModulePath = queueItem->cachedModulesPath + "/" + moduleKey + ".vuonode";
1712  queueItem->loadingGroup = loadingGroup;
1713  queueItem->priority = { environmentIndex, sourceInfo->getLongestDownstreamPath() };
1714  moduleCompilationQueue->enqueue(queueItem);
1715  ++sourcesEnqueued;
1716  }
1717 
1718  // Compile each enqueued source file. This is done asynchronously for several reasons:
1719  // - To avoid environmentQueue calling compiler code calling environmentQueue.
1720  // - To compile dependencies that were enqueued later while a compilation that was enqueued earlier waits.
1721  // - To be more efficient when compiling multiple source files.
1722 
1723  dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
1724  dispatch_async(queue, ^{
1725  for (int i = 0; i < sourcesEnqueued; ++i)
1726  {
1727  __block VuoModuleCompilationQueue::Item *queueItem = moduleCompilationQueue->next();
1728  VUserLog("Compiling %s", queueItem->moduleKey.c_str());
1729 
1730  dispatch_async(queue, ^{
1731  try
1732  {
1733  VuoFileUtilities::makeDir(queueItem->cachedModulesPath);
1734  }
1735  catch (VuoException &e)
1736  {
1737  VuoCompilerIssue issue(VuoCompilerIssue::Error, "compiling subcomposition", "", "Couldn't create cached modules folder", e.what());
1738  VuoCompilerIssues *issues = new VuoCompilerIssues(issue);
1739  notifyCompilers(set<VuoCompilerModule *>(), set< pair<VuoCompilerModule *, VuoCompilerModule *> >(), set<VuoCompilerModule *>(), issues, false);
1740  }
1741 
1742  VuoCompiler *compiler = new VuoCompiler(queueItem->sourcePath, target);
1743  compiler->setLoadAllModules(false);
1744 
1745  VuoModuleCompilationQueue::Item *queueItemForCallback = queueItem;
1746  auto moduleLoadedCallback = [=](){
1747  dispatch_group_leave(queueItemForCallback->loadingGroup);
1748  moduleCompilationQueue->completed(queueItemForCallback);
1749  };
1750 
1751  string ext = queueItem->sourceFile->extension();
1753  {
1754  VuoModuleCompiler *moduleCompiler = NULL;
1755  try
1756  {
1757  moduleCompiler = VuoModuleCompiler::newModuleCompiler("isf", queueItem->moduleKey, queueItem->sourceFile);
1758  }
1759  catch (VuoException &e)
1760  {
1761  VuoCompilerIssues *issues = new VuoCompilerIssues(VuoCompilerIssue(VuoCompilerIssue::Error, "compiling ISF module", queueItem->sourcePath, "", e.what()));
1762  notifyCompilers(set<VuoCompilerModule *>(), set< pair<VuoCompilerModule *, VuoCompilerModule *> >(), set<VuoCompilerModule *>(), issues, false);
1763  }
1764 
1765  if (moduleCompiler)
1766  {
1767  moduleCompiler->overrideSourceCode(queueItem->sourceCode, queueItem->sourceFile);
1768 
1769  auto getType = [&compiler] (const string &moduleKey) { return compiler->getType(moduleKey); };
1770  VuoCompilerIssues *issues = new VuoCompilerIssues;
1771  Module *module = moduleCompiler->compile(getType, llvmQueue, issues);
1772 
1773  if (module)
1774  {
1775  dispatch_sync(llvmQueue, ^{
1776  setTargetForModule(module, target);
1777  writeModuleToBitcode(module, queueItem->compiledModulePath);
1778  });
1779  }
1780  else
1781  {
1782  issues->setFilePathIfEmpty(queueItem->sourcePath);
1783  }
1784 
1785  moduleFileChanged(queueItem->compiledModulePath, queueItem->sourceCode, moduleLoadedCallback, compiler, issues);
1786  }
1787  else
1788  moduleLoadedCallback();
1789  }
1790  else if (VuoFileUtilities::isCSourceExtension(ext) && ! queueItem->cachedModulesPath.empty())
1791  {
1792  VuoCompilerIssues *issues = new VuoCompilerIssues;
1793 
1794  try
1795  {
1796  compiler->compileModule(queueItem->sourcePath, queueItem->compiledModulePath, vector<string>());
1797  }
1798  catch (VuoCompilerException &e)
1799  {
1800  if (issues != e.getIssues())
1801  issues->append(e.getIssues());
1802 
1803  issues->setFilePathIfEmpty(queueItem->sourcePath);
1804  }
1805 
1806  moduleFileChanged(queueItem->compiledModulePath, queueItem->sourceCode, moduleLoadedCallback, compiler, issues);
1807  }
1808  else
1809  {
1810  VuoCompilerIssues *issues = new VuoCompilerIssues;
1811 
1812  try
1813  {
1814  VuoCompilerComposition *composition = VuoCompilerComposition::newCompositionFromGraphvizDeclaration(queueItem->sourceCode, compiler);
1815 
1816  Module *module = compiler->compileCompositionToModule(composition, queueItem->moduleKey, false, issues);
1817  if (! module)
1818  throw VuoCompilerException(issues, false);
1819 
1820  dispatch_sync(llvmQueue, ^{
1821  setTargetForModule(module, target);
1822  writeModuleToBitcode(module, queueItem->compiledModulePath);
1823  });
1824 
1825  VuoComposition *baseComposition = composition->getBase();
1826  delete composition;
1827  delete baseComposition;
1828  }
1829  catch (VuoCompilerException &e)
1830  {
1831  if (issues != e.getIssues())
1832  issues->append(e.getIssues());
1833 
1834  issues->setFilePathIfEmpty(queueItem->sourcePath);
1835  }
1836 
1837  moduleFileChanged(queueItem->compiledModulePath, queueItem->sourceCode, moduleLoadedCallback, compiler, issues);
1838  }
1839 
1840  delete compiler;
1841  dispatch_group_leave(moduleSourceCompilersExistGlobally);
1842  });
1843  }
1844  });
1845 
1846  return sourcesLoading;
1847 }
1848 
1857 set<VuoCompilerModule *> VuoCompiler::Environment::unloadCompiledModules(const set<string> &moduleKeys)
1858 {
1859  set<VuoCompilerModule *> modulesUnloaded;
1860 
1861  for (set<string>::const_iterator i = moduleKeys.begin(); i != moduleKeys.end(); ++i)
1862  {
1863  string moduleKey = *i;
1864  VuoCompilerModule *module = NULL;
1865 
1866  map<string, VuoCompilerNodeClass *>::iterator nodeClassIter = nodeClasses.find(moduleKey);
1867  if (nodeClassIter != nodeClasses.end())
1868  {
1869  module = nodeClassIter->second;
1870  nodeClasses.erase(nodeClassIter);
1871  }
1872  else
1873  {
1874  map<string, VuoCompilerType *>::iterator typeIter = types.find(moduleKey);
1875  if (typeIter != types.end())
1876  {
1877  module = typeIter->second;
1878  types.erase(typeIter);
1879  }
1880  else
1881  {
1882  map<string, VuoCompilerModule *>::iterator libraryModuleIter = libraryModules.find(moduleKey);
1883  if (libraryModuleIter != libraryModules.end())
1884  {
1885  module = libraryModuleIter->second;
1886  libraryModules.erase(libraryModuleIter);
1887  }
1888  }
1889  }
1890 
1891  if (module)
1892  {
1893  modulesUnloaded.insert(module);
1894  removeFromDependencyGraph(module);
1895  modulesChanged();
1896  }
1897  }
1898 
1899  return modulesUnloaded;
1900 }
1901 
1907 void VuoCompiler::Environment::deleteModulesCompiledFromSourceCode(const set<string> &moduleKeys)
1908 {
1909  for (set<string>::const_iterator i = moduleKeys.begin(); i != moduleKeys.end(); ++i)
1910  {
1911  string moduleKey = *i;
1912 
1913  map<string, ModuleInfo *>::iterator iter = moduleFilesAtSearchPath[getCompiledModuleCachePath()].find(moduleKey);
1914  if (iter != moduleFilesAtSearchPath[getCompiledModuleCachePath()].end())
1915  VuoFileUtilities::deleteFile(iter->second->getFile()->path());
1916  }
1917 }
1918 
1926 VuoCompilerModule * VuoCompiler::Environment::loadModule(ModuleInfo *moduleInfo)
1927 {
1928  string moduleKey = moduleInfo->getModuleKey();
1929 
1930  // Skip certain LLVM modules that definitely aren't Vuo modules to avoid adding struct types defined in them to the LLVM
1931  // context, resulting in mismatched struct types in code generation (e.g. %struct.NodeContext and %struct.NodeContext.1).
1932  if (VuoStringUtilities::beginsWith(moduleKey, "libVuo"))
1933  return NULL;
1934 
1935  // Don't try to load single-target parts
1936  // (such as those found in `build/test/TestControlAndTelemetry`),
1937  // since they just fail and pollute the logs.
1938  if (VuoStringUtilities::endsWith(moduleKey, "-x86_64")
1939  || VuoStringUtilities::endsWith(moduleKey, "-arm64"))
1940  return nullptr;
1941 
1942  __block size_t inputDataBytes;
1943  __block char *rawInputData;
1944  dispatch_sync(llvmQueue, ^{
1945  try
1946  {
1947  rawInputData = moduleInfo->getFile()->getContentsAsRawData(inputDataBytes);
1948  }
1949  catch (VuoException &e)
1950  {
1951  rawInputData = NULL;
1952  VUserLog("Warning: Couldn't load module '%s'. Its file may have been deleted. (%s)", moduleKey.c_str(), e.what());
1953  }
1954  });
1955  if (! rawInputData)
1956  return NULL;
1957 
1958  char *processedInputData;
1959 #if VUO_PRO
1960  processedInputData = loadModule_Pro0(moduleInfo, moduleKey, inputDataBytes, rawInputData);
1961 #else
1962  processedInputData = rawInputData;
1963 #endif
1964 
1965  Module *module = NULL;
1966  set<string> moduleArchs;
1967  bool moduleParseError = !processedInputData;
1968  if (!moduleParseError)
1969  {
1970  string moduleReadError;
1971  string arch = VuoCompiler::getTargetArch(target);
1972  VuoLog_status("Loading module \"%s\" (%s)", moduleKey.c_str(), arch.c_str());
1973  module = readModuleFromBitcodeData(processedInputData, inputDataBytes, arch, moduleArchs, moduleReadError);
1974  VuoLog_status(NULL);
1975  free(processedInputData);
1976 
1977  if (!module)
1978  {
1979  moduleParseError = true;
1980 
1981  if (VuoFileUtilities::arePathsEqual(moduleInfo->getSearchPath(), getCompiledModuleCachePath()))
1982  VuoFileUtilities::deleteFile(moduleInfo->getFile()->path());
1983  else
1984  VUserLog("Error: Couldn't load module '%s' into %s environment: %s.", moduleKey.c_str(), arch.c_str(), moduleReadError.c_str());
1985  }
1986  }
1987 
1988  if (!moduleParseError)
1989  {
1990  string modulePath;
1991  if (! moduleInfo->getFile()->isInArchive())
1992  modulePath = moduleInfo->getFile()->path();
1993 
1994  VuoCompilerCompatibility moduleCompatibility = (VuoFileUtilities::arePathsEqual(moduleInfo->getSearchPath(), getCompiledModuleCachePath()) ?
1996  VuoCompilerCompatibility::compatibilityWithArchitectures(moduleArchs));
1997 
1998  __block VuoCompilerModule *compilerModule;
1999  dispatch_sync(llvmQueue, ^{
2000  compilerModule = VuoCompilerModule::newModule(moduleKey, module, modulePath, moduleCompatibility);
2001  });
2002 
2003  if (compilerModule)
2004  {
2005  if (dynamic_cast<VuoCompilerNodeClass *>(compilerModule))
2006  nodeClasses[moduleKey] = static_cast<VuoCompilerNodeClass *>(compilerModule);
2007  else if (dynamic_cast<VuoCompilerType *>(compilerModule))
2008  types[moduleKey] = static_cast<VuoCompilerType *>(compilerModule);
2009  else
2010  libraryModules[moduleKey] = compilerModule;
2011 
2012  VuoNodeSet *nodeSet = VuoNodeSet::createNodeSetForModule(moduleInfo->getFile());
2013  if (nodeSet)
2014  {
2015  map<string, VuoNodeSet *>::iterator nodeSetIter = nodeSetForName.find(nodeSet->getName());
2016  if (nodeSetIter == nodeSetForName.end())
2017  {
2018  nodeSetForName[nodeSet->getName()] = nodeSet;
2019  }
2020  else
2021  {
2022  delete nodeSet;
2023  nodeSet = nodeSetIter->second;
2024  }
2025  compilerModule->getPseudoBase()->setNodeSet(nodeSet);
2026  }
2027 
2028 #if VUO_PRO
2029  loadModule_Pro1(rawInputData, processedInputData, compilerModule);
2030 #endif
2031 
2032  compilerModule->setBuiltIn( isBuiltInOriginal() );
2033 
2034  return compilerModule;
2035  }
2036  else
2037  {
2038  destroyLlvmModule(module);
2039  }
2040  }
2041 
2042  return NULL;
2043 }
2044 
2050 void VuoCompiler::Environment::replaceNodeClass(VuoCompilerNodeClass *newNodeClass)
2051 {
2052  string moduleKey = newNodeClass->getBase()->getModuleKey();
2053 
2054  VuoCompilerNodeClass *oldNodeClass = nodeClasses[moduleKey];
2055  if (oldNodeClass)
2056  {
2057  removeFromDependencyGraph(oldNodeClass);
2058  destroyModule(oldNodeClass);
2059  }
2060 
2061  nodeClasses[moduleKey] = newNodeClass;
2062  addToDependencyGraph(newNodeClass);
2063  modulesChanged();
2064 }
2065 
2066 void VuoCompiler::Environment::addToDependencyGraph(VuoCompilerModule *module)
2067 {
2068  DependencyGraphVertex *vertex = DependencyGraphVertex::vertexForDependency(module->getPseudoBase()->getModuleKey(), dependencyGraph);
2069  dependencyGraph->addVertex(vertex);
2070 
2071  vertex->setEnvironment(this);
2072 
2073  set<string> dependencies = module->getDependencies();
2074  for (set<string>::iterator i = dependencies.begin(); i != dependencies.end(); ++i)
2075  {
2076  DependencyGraphVertex *depVertex = DependencyGraphVertex::vertexForDependency(*i, dependencyGraph);
2077  dependencyGraph->addEdge(vertex, depVertex);
2078  }
2079 }
2080 
2081 void VuoCompiler::Environment::removeFromDependencyGraph(VuoCompilerModule *module)
2082 {
2083  DependencyGraphVertex *vertex = DependencyGraphVertex::vertexForDependency(module->getPseudoBase()->getModuleKey(), dependencyGraph);
2084  dependencyGraph->removeVertex(vertex);
2085 }
2086 
2097 void VuoCompiler::Environment::reifyPortTypes(const map<string, VuoCompilerType *> &inheritedTypes)
2098 {
2099  map<string, VuoCompilerType *> availableTypes = inheritedTypes;
2100  availableTypes.insert(types.begin(), types.end());
2101 
2102  for (map<string, VuoCompilerNodeClass *>::const_iterator i = nodeClasses.begin(); i != nodeClasses.end(); ++i)
2103  {
2104  VuoNodeClass *nodeClass = i->second->getBase();
2105 
2106  vector<VuoPortClass *> inputPortClasses = nodeClass->getInputPortClasses();
2107  vector<VuoPortClass *> outputPortClasses = nodeClass->getOutputPortClasses();
2108  vector<VuoPortClass *> portClasses;
2109  portClasses.insert(portClasses.end(), inputPortClasses.begin(), inputPortClasses.end());
2110  portClasses.insert(portClasses.end(), outputPortClasses.begin(), outputPortClasses.end());
2111 
2112  for (vector<VuoPortClass *>::iterator j = portClasses.begin(); j != portClasses.end(); ++j)
2113  {
2114  VuoCompilerPortClass *portClass = static_cast<VuoCompilerPortClass *>((*j)->getCompiler());
2115  VuoType *baseType = portClass->getDataVuoType();
2116 
2117  if (baseType && ! baseType->hasCompiler())
2118  {
2119  string typeName = baseType->getModuleKey();
2120  VuoCompilerType *reifiedType = NULL;
2121 
2122  VuoGenericType *genericType = dynamic_cast<VuoGenericType *>(baseType);
2123  if (genericType)
2124  {
2125  reifiedType = VuoCompilerGenericType::newGenericType(genericType, availableTypes);
2126  if (reifiedType) {
2127  genericTypes.insert( static_cast<VuoCompilerGenericType *>(reifiedType) );
2128  }
2129  }
2130  else
2131  {
2132  map<string, VuoCompilerType *>::iterator reifiedTypeIter = availableTypes.find(typeName);
2133  if (reifiedTypeIter != availableTypes.end())
2134  {
2135  delete baseType;
2136  reifiedType = reifiedTypeIter->second;
2137  }
2138  }
2139 
2140  if (reifiedType) {
2141  portClass->setDataVuoType(reifiedType->getBase());
2142  }
2143  }
2144  }
2145 
2146  vector<VuoCompilerTriggerDescription *> triggers = nodeClass->getCompiler()->getTriggerDescriptions();
2147  for (vector<VuoCompilerTriggerDescription *>::iterator j = triggers.begin(); j != triggers.end(); ++j)
2148  {
2149  VuoCompilerTriggerDescription *trigger = *j;
2150  VuoType *baseType = trigger->getDataType();
2151 
2152  if (baseType && ! baseType->hasCompiler())
2153  {
2154  string typeName = baseType->getModuleKey();
2155  map<string, VuoCompilerType *>::iterator reifiedTypeIter = availableTypes.find(typeName);
2156  if (reifiedTypeIter != availableTypes.end())
2157  {
2158  delete baseType;
2159  VuoCompilerType *reifiedType = reifiedTypeIter->second;
2160  trigger->setDataType(reifiedType->getBase());
2161  }
2162  }
2163  }
2164  }
2165 }
2166 
2177 void VuoCompiler::Environment::getCacheableModulesAndDependencies(set<string> &cacheableModulesAndDependencies,
2178  set<string> &dylibsNeededToLinkToThisCache,
2179  set<string> &frameworksNeededToLinkToThisCache)
2180 {
2181  if (isModuleCacheInitialized && ! isModuleCacheableDataDirty)
2182  {
2183  cacheableModulesAndDependencies = moduleCacheContents;
2184  dylibsNeededToLinkToThisCache = moduleCacheDylibs;
2185  frameworksNeededToLinkToThisCache = moduleCacheFrameworks;
2186  return;
2187  }
2188 
2190 
2191  // Include all modules…
2192  map<string, VuoCompilerModule *> allModules;
2193  allModules.insert(nodeClasses.begin(), nodeClasses.end());
2194  allModules.insert(types.begin(), types.end());
2195  allModules.insert(libraryModules.begin(), libraryModules.end());
2196 
2197  set<string> dependencies;
2198  for (map<string, VuoCompilerModule *>::iterator i = allModules.begin(); i != allModules.end(); ++i)
2199  {
2200  string moduleKey = i->first;
2201  VuoCompilerModule *module = i->second;
2202 
2203 #if VUO_PRO
2204  // … except Pro modules and modules that depend on them…
2205  if (module->requiresPro())
2206  continue;
2207 #endif
2208 
2209  // … and incompatible modules.
2210  if (! module->getCompatibleTargets().isCompatibleWith(compositionTargets))
2211  continue;
2212 
2213  cacheableModulesAndDependencies.insert(moduleKey);
2214 
2215  set<string> moduleDependencies = module->getDependencies();
2216  dependencies.insert(moduleDependencies.begin(), moduleDependencies.end());
2217  }
2218 
2219  // For the built-in environment, include Vuo's core dependencies.
2220  if (builtIn && ! generated)
2221  {
2222  vector<string> coreDependencies = getCoreVuoDependencies();
2223  dependencies.insert(coreDependencies.begin(), coreDependencies.end());
2224  }
2225 
2226  // Include all dependencies of the included module that are located in this environment.
2227  // (All modules are already included, so we only need to handle non-module dependencies.)
2228  for (set<string>::iterator i = dependencies.begin(); i != dependencies.end(); ++i)
2229  {
2230  string dependency = *i;
2231  if (allModules.find(dependency) == allModules.end())
2232  {
2233  if (VuoStringUtilities::endsWith(dependency, ".framework"))
2234  frameworksNeededToLinkToThisCache.insert(dependency);
2235  else
2236  {
2237  string dependencyPath = VuoCompiler::getLibraryPath(dependency, librarySearchPaths);
2238  if (! dependencyPath.empty())
2239  {
2240  if (VuoStringUtilities::endsWith(dependencyPath, ".dylib"))
2241  dylibsNeededToLinkToThisCache.insert(dependencyPath);
2242  else
2243  cacheableModulesAndDependencies.insert(dependency);
2244  }
2245  }
2246  }
2247  }
2248 
2249  moduleCacheDylibs = dylibsNeededToLinkToThisCache;
2250  moduleCacheFrameworks = frameworksNeededToLinkToThisCache;
2251 
2252  if (builtIn)
2253  {
2254  currentModuleCacheDylib = VuoFileUtilities::buildModuleCacheDylibPath(moduleCachePath, builtIn, generated);
2255 
2256  // Give each built-in cache arch a unique name, so they can be built in parallel.
2257  if (!vuoFrameworkInProgressPath.empty())
2258  currentModuleCacheDylib += "-" + VuoCompiler::getTargetArch(target);
2259  }
2260 }
2261 
2282 void VuoCompiler::Environment::useModuleCache(bool shouldUseExistingCache, VuoCompiler *compiler,
2283  set<string> cacheableModulesAndDependencies,
2284  set<string> dylibsNeededToLinkToCaches, set<string> frameworksNeededToLinkToCaches,
2285  unsigned long lastPrerequisiteModuleCacheRebuild)
2286 {
2287  // Ignore the cache if the `prelinkCache` preference is false.
2288 
2289  static dispatch_once_t checked = 0;
2290  static bool prelinkCache = true;
2291  dispatch_once(&checked, ^{
2292  Boolean valid;
2293  bool result = CFPreferencesGetAppBooleanValue(CFSTR("prelinkCache"), CFSTR("org.vuo.Editor"), &valid);
2294  if (valid)
2295  prelinkCache = result;
2296  });
2297  if (! prelinkCache)
2298  {
2299  VDebugLog("Ignoring the module cache since the 'prelinkCache' preference is false.");
2300  return;
2301  }
2302 
2303  // Don't do anything if this environment doesn't have a cache configured.
2304 
2305  if (moduleCachePath.empty())
2306  return;
2307 
2308  // Don't bother rechecking the cache if neither the modules in this environment nor the caches that it depends on have changed.
2309 
2310  string cacheDescription = VuoFileUtilities::buildModuleCacheDescription(moduleCachePath, generated);
2311  if (isModuleCacheInitialized && ! isModuleCacheableDataDirty &&
2312  (builtIn || lastModuleCacheRebuild >= lastPrerequisiteModuleCacheRebuild))
2313  {
2314  VDebugLog("No need to recheck %s.", cacheDescription.c_str());
2315  return;
2316  }
2317 
2318  try
2319  {
2320  VDebugLog("Checking if %s is up-to-date…", cacheDescription.c_str());
2321 
2322  bool isCacheUpToDate = true;
2323 
2324  if (isModuleCacheInitialized && isModuleCacheableDataDirty)
2325  isCacheUpToDate = false;
2326 
2327  isModuleCacheInitialized = true;
2328  isModuleCacheableDataDirty = false;
2329 
2330  string indexPath = VuoFileUtilities::buildModuleCacheIndexPath(moduleCachePath, builtIn, generated);
2331  if (!vuoFrameworkInProgressPath.empty())
2332  indexPath += "-" + VuoCompiler::getTargetArch(target);
2333  string dir, file, ext;
2334  VuoFileUtilities::splitPath(indexPath, dir, file, ext);
2335  string indexFileName = file + "." + ext;
2336 
2337  // Create the cache directory and index if they don't already exist. (If they do exist, don't affect the last-modified times.)
2338 
2339  bool dirExists = VuoFileUtilities::fileExists(moduleCachePath);
2340  if (! dirExists)
2341  {
2342  if (shouldUseExistingCache)
2343  throw VuoException("Trying to use the existing cache, but the cache directory doesn't exist.", false);
2344 
2345  VuoFileUtilities::makeDir(moduleCachePath);
2346  isCacheUpToDate = false;
2347  }
2348 
2349  bool indexFileExists = VuoFileUtilities::fileExists(indexPath);
2350  if (! indexFileExists)
2351  {
2352  if (shouldUseExistingCache)
2353  throw VuoException("Trying to use the existing cache, but the cache index doesn't exist.", false);
2354 
2355  VuoFileUtilities::createFile(indexPath);
2356  isCacheUpToDate = false;
2357  }
2358 
2359  // Lock the cache for reading.
2360 
2361  VuoFileUtilities::File *fileForLocking;
2362  {
2363  fileForLocking = moduleCacheFileForLocking[indexPath];
2364  if (! fileForLocking)
2365  {
2366  fileForLocking = new VuoFileUtilities::File(moduleCachePath, indexFileName);
2367  moduleCacheFileForLocking[indexPath] = fileForLocking;
2368  }
2369 
2370  if (!fileForLocking->lockForReading())
2371  VDebugLog("\tWarning: Couldn't lock for reading.");
2372  }
2373 
2374  // If this is the first time this Environment is using its cache, see if there's a dylib on disk.
2375 
2376  if (currentModuleCacheDylib.empty())
2377  currentModuleCacheDylib = VuoFileUtilities::findLatestRevisionOfModuleCacheDylib(moduleCachePath, builtIn, generated, lastModuleCacheRebuild);
2378 
2379  if (shouldUseExistingCache && currentModuleCacheDylib.empty())
2380  throw VuoException("Trying to use the existing cache, but the cache dylib doesn't exist.", false);
2381 
2382  // Check if the dylib is newer than the other caches that it depends on.
2383 
2384  if (isCacheUpToDate)
2385  isCacheUpToDate = lastModuleCacheRebuild >= lastPrerequisiteModuleCacheRebuild;
2386 
2387  // Check if the dylib looks remotely valid.
2388 
2389  if (isCacheUpToDate)
2390  {
2391  bool dylibHasData = VuoFileUtilities::fileContainsReadableData(currentModuleCacheDylib);
2392  if (! dylibHasData)
2393  {
2394  if (shouldUseExistingCache)
2395  throw VuoException("Trying to use the existing cache, but the cache doesn't contain readable data.", false);
2396  else
2397  isCacheUpToDate = false;
2398  }
2399  }
2400 
2401  // List the items actually in the cache, according to its index.
2402 
2403  const char separator = '\n';
2404  if (isCacheUpToDate || shouldUseExistingCache)
2405  {
2406  VuoFileUtilities::File indexFile(moduleCachePath, indexFileName);
2407  string index = indexFile.getContentsAsString();
2408  vector<string> actualIndex = VuoStringUtilities::split(index, separator);
2409 
2410  moduleCacheContents.clear();
2411  moduleCacheContents.insert(actualIndex.begin(), actualIndex.end());
2412 
2413  if (shouldUseExistingCache)
2414  {
2415  isModuleCacheAvailable = true;
2416  return;
2417  }
2418  }
2419 
2420  // Check if the list of actual items matches the list of expected items.
2421 
2422  if (isCacheUpToDate)
2423  {
2424  if (moduleCacheContents.size() != cacheableModulesAndDependencies.size())
2425  isCacheUpToDate = false;
2426  else
2427  {
2428  set<string> contentsInBoth;
2429  std::set_intersection(moduleCacheContents.begin(), moduleCacheContents.end(),
2430  cacheableModulesAndDependencies.begin(), cacheableModulesAndDependencies.end(),
2431  std::inserter(contentsInBoth, contentsInBoth.begin()));
2432 
2433  if (contentsInBoth.size() != cacheableModulesAndDependencies.size())
2434  isCacheUpToDate = false;
2435  }
2436  }
2437 
2438  // Check if the cache is newer than all of the modules in it.
2439 
2440  if (isCacheUpToDate)
2441  {
2442  unsigned long cacheLastModified = VuoFileUtilities::getFileLastModifiedInSeconds(currentModuleCacheDylib);
2443 
2444  for (map<string, map<string, ModuleInfo *> >::iterator i = moduleFilesAtSearchPath.begin(); i != moduleFilesAtSearchPath.end(); ++i)
2445  {
2446  for (map<string, ModuleInfo *>::iterator j = i->second.begin(); j != i->second.end(); ++j)
2447  {
2448  if (! j->second->isOlderThan(cacheLastModified))
2449  {
2450  isCacheUpToDate = false;
2451  break;
2452  }
2453  }
2454  }
2455  }
2456 
2457  // If the cache is consistent with this environment, we're done.
2458 
2459  if (isCacheUpToDate)
2460  {
2461  VDebugLog("Up-to-date.");
2462 
2463  isModuleCacheAvailable = true;
2464  return;
2465  }
2466 
2467  // Otherwise, (re)build the cache.
2468 
2469  if (! builtIn)
2470  {
2471  currentModuleCacheDylib = VuoFileUtilities::buildModuleCacheDylibPath(moduleCachePath, builtIn, generated);
2472 
2473  struct timeval t;
2474  gettimeofday(&t, NULL);
2475  lastModuleCacheRebuild = t.tv_sec;
2476  }
2477 
2478  dispatch_async(moduleCacheBuildingQueue, ^{
2479  VDebugLog("Rebuilding %s…", cacheDescription.c_str());
2480 
2481  set<Module *> modulesToLink;
2482  set<string> librariesToLink;
2483  set<string> frameworksToLink;
2484  {
2485  compiler->getLinkerInputs(cacheableModulesAndDependencies, Optimization_SmallBinary, modulesToLink, librariesToLink, frameworksToLink);
2486 
2487  librariesToLink.insert(dylibsNeededToLinkToCaches.begin(), dylibsNeededToLinkToCaches.end());
2488  frameworksToLink.insert(frameworksNeededToLinkToCaches.begin(), frameworksNeededToLinkToCaches.end());
2489  }
2490 
2491  bool gotLockForWriting = false;
2492  try
2493  {
2494  // Try to upgrade the file lock for writing.
2495  gotLockForWriting = fileForLocking->lockForWriting(true);
2496  if (! gotLockForWriting)
2497  throw VuoException("The cache file is being used by another process. "
2498  "If any composition windows are open from previous Vuo sessions, quit them. "
2499  "If any processes whose names start with \"VuoComposition\" or one of your composition file names appear in Activity Monitor, force-quit them.",
2500  false);
2501 
2502  // Link the dependencies to create the cached resources dylib in a temporary file.
2503  string dir, file, ext;
2504  VuoFileUtilities::splitPath(currentModuleCacheDylib, dir, file, ext);
2505  string tmpPath = VuoFileUtilities::makeTmpFile(file, "dylib");
2506  VuoCompilerIssues *issues = new VuoCompilerIssues;
2507  compiler->link(tmpPath, modulesToLink, librariesToLink, frameworksToLink, true, "", false, issues);
2508 
2509  if (! issues->isEmpty())
2510  VUserLog("Warning: May not have fully rebuilt %s for the \"faster build\" optimization:\n%s", cacheDescription.c_str(), issues->getLongDescription(false).c_str());
2511 
2512  // Move the temporary file into the cache.
2513  VuoFileUtilities::moveFile(tmpPath, currentModuleCacheDylib);
2514 
2515  // Change the dylib's ID from the temporary path to the path within the cache.
2517  getVuoFrameworkPath() + "/Helpers/install_name_tool",
2518  "-id",
2519  currentModuleCacheDylib,
2520  currentModuleCacheDylib,
2521  });
2522 
2523  // Ad-hoc code-sign the runtime-generated System and User caches,
2524  // but don't ad-hoc code-sign the buildtime-generated Builtin module cache
2525  // since `framework/CMakeLists.txt` later changes its ID/rpath/loads.
2526  if (vuoFrameworkInProgressPath.empty())
2527  adHocCodeSign(currentModuleCacheDylib);
2528 
2529  // Write the list of dependencies to the index file.
2530  vector<string> expectedContents(cacheableModulesAndDependencies.begin(), cacheableModulesAndDependencies.end());
2531  string index = VuoStringUtilities::join(expectedContents, separator);
2532  VuoFileUtilities::writeStringToFile(index, indexPath);
2533 
2534  // Delete any older revisions of the dylib.
2536 
2537  // Downgrade the file lock back to reading.
2538  if (!fileForLocking->lockForReading())
2539  VDebugLog("\tWarning: Couldn't downgrade the lock back to reading.");
2540 
2541  dispatch_sync(environmentQueue, ^{
2542  moduleCacheContents = cacheableModulesAndDependencies;
2543  isModuleCacheAvailable = true;
2544  });
2545  }
2546  catch (VuoException &e)
2547  {
2548  // Downgrade the file lock back to reading.
2549  if (gotLockForWriting)
2550  if (!fileForLocking->lockForReading())
2551  VDebugLog("\tWarning: Couldn't downgrade the lock back to reading.");
2552 
2553  VUserLog("Warning: Couldn't rebuild %s for the \"faster build\" optimization: %s", cacheDescription.c_str(), e.what());
2554  }
2555 
2556  VDebugLog("Done.");
2557  });
2558  }
2559  catch (VuoException &e)
2560  {
2561  if (vuoFrameworkInProgressPath.empty())
2562  VUserLog("Warning: Couldn't use %s for the \"faster build\" optimization: %s", cacheDescription.c_str(), e.what());
2563  }
2564 }
2565 
2569 void VuoCompiler::Environment::waitForModuleCachesToBuild(void)
2570 {
2571  dispatch_sync(moduleCacheBuildingQueue, ^{});
2572 }
2573 
2583 bool VuoCompiler::Environment::findInModuleCache(const string &moduleOrDependency, string &cachePath)
2584 {
2585  if (isModuleCacheAvailable && moduleCacheContents.find(moduleOrDependency) != moduleCacheContents.end())
2586  {
2587  cachePath = currentModuleCacheDylib;
2588  return true;
2589  }
2590 
2591  return false;
2592 }
2593 
2597 string VuoCompiler::Environment::getCurrentModuleCacheDylib(void)
2598 {
2599  return currentModuleCacheDylib;
2600 }
2601 
2606 unsigned long VuoCompiler::Environment::getLastModuleCacheRebuild(void)
2607 {
2608  return lastModuleCacheRebuild;
2609 }
2610 
2617 void VuoCompiler::Environment::modulesChanged(void)
2618 {
2619  isModuleCacheableDataDirty = true;
2620  isModuleCacheAvailable = false;
2621 }
2622 
2626 bool VuoCompiler::Environment::isBuiltInOriginal()
2627 {
2628  return builtIn && ! generated;
2629 }
2630 
2634 bool VuoCompiler::Environment::isBuiltIn()
2635 {
2636  return builtIn;
2637 }
2638 
2642 bool VuoCompiler::Environment::isGenerated()
2643 {
2644  return generated;
2645 }
2646 
2650 string VuoCompiler::Environment::getName()
2651 {
2652  if (isBuiltInOriginal())
2653  return "builtin";
2654  else if (this == sharedEnvironments[target][0][1])
2655  return "builtin (generated)";
2656  else if (this == sharedEnvironments[target][1][0])
2657  return "system";
2658  else if (this == sharedEnvironments[target][1][1])
2659  return "system (generated)";
2660  else if (this == sharedEnvironments[target][2][0])
2661  return "user";
2662  else if (this == sharedEnvironments[target][2][1])
2663  return "user (generated)";
2664  return "composition-local";
2665 }
2666 
2670 void VuoCompiler::applyToInstalledEnvironments(void (^doForEnvironment)(Environment *))
2671 {
2672  dispatch_sync(environmentQueue, ^{
2673  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i) {
2674  doForEnvironment((*i)[0]);
2675  }
2676  });
2677 }
2678 
2682 void VuoCompiler::applyToInstalledEnvironments(void (^doForEnvironment)(Environment *, bool *, string), bool *result, string arg)
2683 {
2684  dispatch_sync(environmentQueue, ^{
2685  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i) {
2686  doForEnvironment((*i)[0], result, arg);
2687  }
2688  });
2689 }
2690 
2694 void VuoCompiler::applyToAllEnvironments(void (^doForEnvironment)(Environment *environment))
2695 {
2696  dispatch_sync(environmentQueue, ^{
2697  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i) {
2698  for (vector<Environment *>::iterator j = i->begin(); j != i->end(); ++j) {
2699  doForEnvironment(*j);
2700  }
2701  }
2702  });
2703 }
2704 
2718 VuoCompiler::VuoCompiler(const string &compositionPath, string target)
2719 {
2720  if (target.empty())
2721  {
2722  this->target = target = getProcessTarget();
2723  VDebugLog("%p target=%s (from current process)", this, this->target.c_str());
2724  }
2725  else
2726  {
2727  this->target = target;
2728  VDebugLog("%p target=%s", this, this->target.c_str());
2729  }
2730 
2731 #if VUO_PRO
2732  init_Pro();
2733 #endif
2734 
2735  shouldLoadAllModules = true;
2736  hasLoadedAllModules = false;
2737  modulesToLoadQueue = dispatch_queue_create("org.vuo.compiler.modules", NULL);
2738  moduleSourceCompilersExist = dispatch_group_create();
2739  moduleCacheBuilding = dispatch_group_create();
2740  dependencyGraph = NULL;
2741  compositionDependencyGraph = NULL;
2742  isVerbose = false;
2743  _shouldShowSplashWindow = false;
2744  delegate = NULL;
2745  delegateQueue = dispatch_queue_create("org.vuo.compiler.delegate", NULL);
2746 
2747  string vuoFrameworkPath = getVuoFrameworkPath();
2748  if (! vuoFrameworkPath.empty())
2749  clangPath = vuoFrameworkPath + "/Helpers/clang";
2750  else
2751  clangPath = LLVM_ROOT "/bin/clang";
2752 
2753  dispatch_sync(environmentQueue, ^{
2754  allCompilers.insert(this);
2755 
2756  if (sharedEnvironments[target].empty())
2757  {
2758  sharedEnvironments[target] = vector< vector<Environment *> >(3, vector<Environment *>(2, NULL));
2759  for (vector< vector<Environment *> >::iterator i = sharedEnvironments[target].begin(); i != sharedEnvironments[target].end(); ++i)
2760  for (vector<Environment *>::iterator j = i->begin(); j != i->end(); ++j)
2761  *j = new Environment(this->target, i == sharedEnvironments[target].begin(), j != i->begin());
2762 
2763  vector<string> builtInModuleSearchPaths = Environment::getBuiltInModuleSearchPaths();
2764  for (vector<string>::iterator i = builtInModuleSearchPaths.begin(); i != builtInModuleSearchPaths.end(); ++i)
2765  sharedEnvironments[target][0][0]->addModuleSearchPath(*i, false);
2766 
2767  vector<string> builtInHeaderSearchPaths = Environment::getBuiltInHeaderSearchPaths();
2768  for (vector<string>::iterator i = builtInHeaderSearchPaths.begin(); i != builtInHeaderSearchPaths.end(); ++i)
2769  sharedEnvironments[target][0][0]->addHeaderSearchPath(*i);
2770 
2771  vector<string> builtInLibrarySearchPaths = Environment::getBuiltInLibrarySearchPaths();
2772  for (vector<string>::iterator i = builtInLibrarySearchPaths.begin(); i != builtInLibrarySearchPaths.end(); ++i)
2773  sharedEnvironments[target][0][0]->addLibrarySearchPath(*i);
2774 
2775  vector<string> builtInFrameworkSearchPaths = Environment::getBuiltInFrameworkSearchPaths();
2776  for (vector<string>::iterator i = builtInFrameworkSearchPaths.begin(); i != builtInFrameworkSearchPaths.end(); ++i)
2777  sharedEnvironments[target][0][0]->addFrameworkSearchPath(*i);
2778 
2779  // Allow system administrator to override Vuo.framework modules
2780  sharedEnvironments[target][1][0]->addModuleSearchPath(VuoFileUtilities::getSystemModulesPath());
2781  sharedEnvironments[target][1][0]->addLibrarySearchPath(VuoFileUtilities::getSystemModulesPath());
2782 
2783  // Allow user to override Vuo.framework and system-wide modules
2784  sharedEnvironments[target][2][0]->addModuleSearchPath(VuoFileUtilities::getUserModulesPath());
2785  sharedEnvironments[target][2][0]->addLibrarySearchPath(VuoFileUtilities::getUserModulesPath());
2786 
2787  // Set up module cache paths.
2788  // Since the built-in module caches are part of Vuo.framework (put there by `generateBuiltInModuleCaches`),
2789  // only attempt to use module caches if Vuo.framework exists.
2790  string vuoFrameworkPath = getVuoFrameworkPath();
2791  if (! vuoFrameworkPath.empty())
2792  {
2793  vector<string> moduleCachePaths(3);
2794  moduleCachePaths[0] = vuoFrameworkPath + "/Modules/Builtin";
2795  moduleCachePaths[1] = VuoFileUtilities::getCachePath() + "/System";
2796  moduleCachePaths[2] = VuoFileUtilities::getCachePath() + "/User";
2797 
2798  for (size_t i = 0; i < moduleCachePaths.size(); ++i)
2799  {
2800  string moduleCachePath = moduleCachePaths[i];
2801 
2802  sharedEnvironments[target][i][0]->setModuleCachePath(moduleCachePath, true);
2803  sharedEnvironments[target][i][1]->setModuleCachePath(moduleCachePath, false);
2804  }
2805  }
2806  }
2807  });
2808 
2809  setCompositionPath(compositionPath);
2810 }
2811 
2816 {
2817 #if VUO_PRO
2818  fini_Pro();
2819 #endif
2820 
2821  dispatch_sync(environmentQueue, ^{
2822  allCompilers.erase(this);
2823  });
2824 
2825  dispatch_group_wait(moduleCacheBuilding, DISPATCH_TIME_FOREVER);
2826  dispatch_release(moduleCacheBuilding);
2827 
2828  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
2829  (*i)[0]->removeCompilerToNotify(this);
2830 
2831  dispatch_sync(delegateQueue, ^{});
2832 
2833  dispatch_release(modulesToLoadQueue);
2834  dispatch_release(delegateQueue);
2835 
2836  delete dependencyGraph;
2837  delete compositionDependencyGraph;
2838 }
2839 
2843 void VuoCompiler::reset(void)
2844 {
2845  dispatch_group_wait(moduleSourceCompilersExistGlobally, DISPATCH_TIME_FOREVER);
2846 
2847  dispatch_sync(environmentQueue, ^{
2848 
2849  for (auto e : sharedEnvironments)
2850  for (vector< vector<Environment *> >::iterator i = e.second.begin(); i != e.second.end(); ++i)
2851  {
2852  (*i)[0]->stopWatchingModuleSearchPaths();
2853  dispatch_sync((*i)[0]->moduleSearchPathContentsChangedQueue, ^{});
2854  }
2855 
2856  for (auto e : environmentsForCompositionFamily)
2857  for (map< string, vector<Environment *> >::iterator i = e.second.begin(); i != e.second.end(); ++i)
2858  {
2859  (i->second)[0]->stopWatchingModuleSearchPaths();
2860  dispatch_sync((i->second)[0]->moduleSearchPathContentsChangedQueue, ^{});
2861  }
2862 
2863  for (auto e : sharedEnvironments)
2864  for (vector< vector<Environment *> >::iterator i = e.second.begin(); i != e.second.end(); ++i)
2865  for (vector<Environment *>::iterator j = i->begin(); j != i->end(); ++j)
2866  delete *j;
2867 
2868  for (auto e : environmentsForCompositionFamily)
2869  for (map< string, vector<Environment *> >::iterator i = e.second.begin(); i != e.second.end(); ++i)
2870  for (vector<Environment *>::iterator j = i->second.begin(); j != i->second.end(); ++j)
2871  delete *j;
2872 
2873  allCompilers.clear();
2874  sharedEnvironments.clear();
2875  environmentsForCompositionFamily.clear();
2876 
2877  });
2878 }
2879 
2886 {
2887  dispatch_async(delegateQueue, ^{
2888  this->delegate = delegate;
2889  });
2890 }
2891 
2901 void VuoCompiler::setCompositionPath(const string &compositionPath)
2902 {
2903  string compositionModulesDir;
2904  string compositionBaseDir;
2905  lastCompositionIsSubcomposition = false;
2906  if (! compositionPath.empty())
2907  {
2908  compositionModulesDir = VuoFileUtilities::getCompositionLocalModulesPath(compositionPath);
2909 
2910  string file, ext;
2911  VuoFileUtilities::splitPath(compositionModulesDir, compositionBaseDir, file, ext);
2912  VuoFileUtilities::canonicalizePath(compositionBaseDir);
2913 
2914  string compositionDir;
2915  VuoFileUtilities::splitPath(compositionPath, compositionDir, file, ext);
2916  VuoFileUtilities::canonicalizePath(compositionDir);
2917  lastCompositionIsSubcomposition = (compositionDir == compositionModulesDir);
2918  }
2919 
2920  // Set up `environments` to contain all environments available to this compiler, in order from broadest to narrowest.
2921 
2922  dispatch_sync(environmentQueue, ^{
2923  if (! environments.empty() && compositionBaseDir == lastCompositionBaseDir) {
2924  return;
2925  }
2926  lastCompositionBaseDir = compositionBaseDir;
2927 
2928  // Clear out the existing environments for this compiler.
2929 
2930  Environment *oldCompositionFamilyInstalledEnvironment = nullptr;
2931  vector<Environment *> compositionEnvironments;
2932  if (! environments.empty())
2933  {
2934  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i) {
2935  (*i)[0]->removeCompilerToNotify(this);
2936  }
2937 
2938  if (environments.size() >= 5) {
2939  oldCompositionFamilyInstalledEnvironment = environments[3][0];
2940  }
2941 
2942  compositionEnvironments = environments.back();
2943 
2944  environments.clear();
2945  }
2946 
2947  // If the composition is located in one of the shared environments (built-in, system, user),
2948  // add that shared environment and any shared environments at broader scope to the compiler.
2949  // If the composition is not in a shared environment, add all of the shared environments to the compiler.
2950 
2951  bool isCompositionInSharedEnvironment = false;
2952  for (vector< vector<Environment *> >::iterator i = sharedEnvironments[target].begin(); i != sharedEnvironments[target].end(); ++i)
2953  {
2954  environments.push_back(*i);
2955 
2956  vector<string> moduleSearchPaths = (*i)[0]->getModuleSearchPaths();
2957  for (vector<string>::iterator j = moduleSearchPaths.begin(); j != moduleSearchPaths.end(); ++j)
2958  {
2959  string moduleSearchPath = *j;
2960  VuoFileUtilities::canonicalizePath(moduleSearchPath);
2961  if (moduleSearchPath == compositionModulesDir)
2962  {
2963  isCompositionInSharedEnvironment = true;
2964  break;
2965  }
2966  }
2967 
2968  if (isCompositionInSharedEnvironment) {
2969  break;
2970  }
2971  }
2972 
2973  // If the composition is not in a shared environment, add the composition-family environment to the compiler.
2974 
2975  Environment *newCompositionFamilyInstalledEnvironment = nullptr;
2976  if (! isCompositionInSharedEnvironment && ! compositionPath.empty())
2977  {
2978  vector<Environment *> compositionFamilyEnvironments = environmentsForCompositionFamily[target][compositionBaseDir];
2979  if (compositionFamilyEnvironments.empty())
2980  {
2981  compositionFamilyEnvironments = vector<Environment *>(2, NULL);
2982  compositionFamilyEnvironments[0] = new Environment(this->target, false, false);
2983  compositionFamilyEnvironments[1] = new Environment(this->target, false, true);
2984  environmentsForCompositionFamily[target][compositionBaseDir] = compositionFamilyEnvironments;
2985 
2986  // Allow the user to place modules/subcompositions in a Modules folder inside the composition folder.
2987 
2988  compositionFamilyEnvironments[0]->addModuleSearchPath(compositionModulesDir);
2989 
2990  // Locate this environment's cache in a folder whose name is the composition folder path with
2991  // slashes replaced with Unicode Modifier Letter Colon.
2992  string moduleCachePath = getCachePathForComposition(compositionBaseDir);
2993 
2994  compositionFamilyEnvironments[0]->setModuleCachePath(moduleCachePath, true);
2995  compositionFamilyEnvironments[1]->setModuleCachePath(moduleCachePath, false);
2996  }
2997  environments.push_back(compositionFamilyEnvironments);
2998 
2999  newCompositionFamilyInstalledEnvironment = compositionFamilyEnvironments[0];
3000  }
3001 
3002  // Add the composition environment to the compiler (or add it back in if it already existed).
3003 
3004  if (compositionEnvironments.empty())
3005  {
3006  compositionEnvironments = vector<Environment *>(2, NULL);
3007  compositionEnvironments[0] = new Environment(this->target, false, false);
3008  compositionEnvironments[1] = new Environment(this->target, false, true);
3009  }
3010  environments.push_back(compositionEnvironments);
3011 
3012  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i) {
3013  (*i)[0]->addCompilerToNotify(this);
3014  }
3015 
3016  delete dependencyGraph;
3017  delete compositionDependencyGraph;
3018  dependencyGraph = makeDependencyNetwork(environments, ^VuoDirectedAcyclicGraph * (Environment *env) { return env->getDependencyGraph(); });
3019  compositionDependencyGraph = makeDependencyNetwork(environments, ^VuoDirectedAcyclicGraph * (Environment *env) { return env->getCompositionDependencyGraph(); });
3020 
3021  // If the compiler has a different local Modules directory than before, notify the compiler's delegate
3022  // of composition-family modules that are newly available/unavailable.
3023 
3024  if (oldCompositionFamilyInstalledEnvironment != newCompositionFamilyInstalledEnvironment)
3025  {
3026  auto getModules = [] (Environment *env)
3027  {
3028  map<string, VuoCompilerModule *> modules;
3029  if (env)
3030  {
3031  for (auto i : env->getNodeClasses()) {
3032  modules.insert(i);
3033  }
3034  for (auto i : env->getTypes()) {
3035  modules.insert(i);
3036  }
3037  for (auto i : env->getLibraryModules()) {
3038  modules.insert(i);
3039  }
3040  }
3041  return modules;
3042  };
3043 
3044  map<string, VuoCompilerModule *> modulesAdded = getModules(newCompositionFamilyInstalledEnvironment);
3045  map<string, VuoCompilerModule *> modulesRemoved = getModules(oldCompositionFamilyInstalledEnvironment);
3046 
3047  map<string, pair<VuoCompilerModule *, VuoCompilerModule *> > modulesModified;
3048  for (map<string, VuoCompilerModule *>::iterator add = modulesAdded.begin(); add != modulesAdded.end(); )
3049  {
3050  map<string, VuoCompilerModule *>::iterator rem = modulesRemoved.find(add->first);
3051  if (rem != modulesRemoved.end())
3052  {
3053  modulesModified[add->first] = make_pair(rem->second, add->second);
3054  modulesAdded.erase(add++);
3055  modulesRemoved.erase(rem);
3056  }
3057  else
3058  {
3059  ++add;
3060  }
3061  }
3062 
3063  if (! (modulesAdded.empty() && modulesModified.empty() && modulesRemoved.empty()) )
3064  {
3065  VuoCompilerIssues *issues = new VuoCompilerIssues();
3066  VuoCompilerDelegate::LoadedModulesData *delegateData = new VuoCompilerDelegate::LoadedModulesData(set< pair<VuoCompilerModule *, VuoCompilerModule *> >(), set<VuoCompilerModule *>(), issues);
3067  delegateData->retain();
3068 
3069  Environment *scopeEnvironment = newCompositionFamilyInstalledEnvironment;
3070  if (! scopeEnvironment) {
3071  scopeEnvironment = compositionEnvironments.at(0);
3072  }
3073 
3074  loadedModules(modulesAdded, modulesModified, modulesRemoved, issues, delegateData, scopeEnvironment);
3075  }
3076  }
3077  });
3078 }
3079 
3085 VuoDirectedAcyclicNetwork * VuoCompiler::makeDependencyNetwork(const vector< vector<Environment *> > &environments,
3086  VuoDirectedAcyclicGraph * (^graphForEnvironment)(Environment *))
3087 {
3088  if (!graphForEnvironment)
3089  return NULL;
3090 
3092 
3093  for (vector< vector<Environment *> >::const_iterator i = environments.begin(); i != environments.end(); ++i)
3094  {
3095  // Installed environment depends on generated environment in same scope.
3096 
3097  network->addEdge(graphForEnvironment(i->at(0)), graphForEnvironment(i->at(1)));
3098 
3099  // Installed and generated environments depend on installed environments in broader scopes.
3100 
3101  for (vector< vector<Environment *> >::const_iterator ii = environments.begin(); ii != i; ++ii)
3102  {
3103  network->addEdge(graphForEnvironment(i->at(0)), graphForEnvironment(ii->at(0)));
3104  network->addEdge(graphForEnvironment(i->at(1)), graphForEnvironment(ii->at(0)));
3105  }
3106  }
3107 
3108  return network;
3109 }
3110 
3131 void VuoCompiler::loadModulesIfNeeded(const set<string> &moduleKeys)
3132 {
3133  __block bool willLoadAllModules = false;
3134  if (moduleKeys.empty())
3135  {
3136  dispatch_sync(modulesToLoadQueue, ^{
3137  if (shouldLoadAllModules && ! hasLoadedAllModules) {
3138  willLoadAllModules = true;
3139  hasLoadedAllModules = true;
3140  }
3141  });
3142  }
3143 
3144  if (! willLoadAllModules && moduleKeys.empty())
3145  return;
3146 
3147  // Load modules and start sources compiling.
3148 
3149  dispatch_group_enter(moduleSourceCompilersExist);
3150  __block set<dispatch_group_t> sourcesLoading;
3151  dispatch_sync(environmentQueue, ^{
3152  sourcesLoading = loadModulesAndSources(moduleKeys, set<string>(), set<string>(),
3153  moduleKeys, set<string>(), set<string>(),
3154  willLoadAllModules, false, nullptr, nullptr, nullptr, "");
3155  });
3156 
3157  // Wait for subcompositions and specialized node classes to finish compiling and their modules to be loaded
3158  // to ensure that the next call to getNodeClass() will return them.
3159 
3160  for (set<dispatch_group_t>::iterator i = sourcesLoading.begin(); i != sourcesLoading.end(); ++i)
3161  {
3162  dispatch_group_wait(*i, DISPATCH_TIME_FOREVER);
3163  dispatch_release(*i);
3164  }
3165  dispatch_group_leave(moduleSourceCompilersExist);
3166 }
3167 
3176 set<dispatch_group_t> VuoCompiler::loadModulesAndSources(const set<string> &modulesAddedKeys, const set<string> &modulesModifiedKeys, const set<string> &modulesRemovedKeys,
3177  const set<string> &sourcesAddedKeys, const set<string> &sourcesModifiedKeys, const set<string> &sourcesRemovedKeys,
3178  bool willLoadAllModules, bool shouldRecompileSourcesIfUnchanged,
3179  Environment *currentEnvironment, VuoCompilerIssues *issuesForCurrentEnvironment,
3180  std::function<void(void)> moduleLoadedCallback, const string &moduleAddedOrModifiedSourceCode)
3181 {
3182  //VLog("C=%p E=%p -- %lu %lu %lu %lu %lu %lu %i %i", this, currentEnvironment,
3183  //modulesAddedKeys.size(), modulesModifiedKeys.size(), modulesRemovedKeys.size(),
3184  //sourcesAddedKeys.size(), sourcesModifiedKeys.size(), sourcesRemovedKeys.size(),
3185  //willLoadAllModules, shouldRecompileSourcesIfUnchanged);
3186  //if (modulesAddedKeys.size() == 1) VLog(" %s", modulesAddedKeys.begin()->c_str());
3187  //if (modulesModifiedKeys.size() == 1) VLog(" %s", modulesModifiedKeys.begin()->c_str());
3188  //if (modulesRemovedKeys.size() == 1) VLog(" %s", modulesRemovedKeys.begin()->c_str());
3189 
3190  // Organize the modules, source files, and issues by environment.
3191 
3192  map<Environment *, set<string> > modulesAdded;
3193  map<Environment *, set<string> > modulesModified;
3194  map<Environment *, set<string> > modulesRemoved;
3195  map<Environment *, set<string> > sourcesAdded;
3196  map<Environment *, set<string> > sourcesModified;
3197  map<Environment *, set<string> > sourcesRemoved;
3198  map<Environment *, set<string> > potentialSpecializedModules;
3199 
3200  if (currentEnvironment)
3201  {
3202  modulesAdded[currentEnvironment] = modulesAddedKeys;
3203  modulesModified[currentEnvironment] = modulesModifiedKeys;
3204  modulesRemoved[currentEnvironment] = modulesRemovedKeys;
3205  sourcesAdded[currentEnvironment] = sourcesAddedKeys;
3206  sourcesModified[currentEnvironment] = sourcesModifiedKeys;
3207  sourcesRemoved[currentEnvironment] = sourcesRemovedKeys;
3208  }
3209  else
3210  {
3211  Environment *genEnv = nullptr;
3212  if (! willLoadAllModules)
3213  {
3214  // If compiling a top-level composition, generated modules that were directly requested (in modulesAddedKeys) are
3215  // added at the composition scope so they won't be cached. This prevents link errors when running multiple
3216  // compositions in the current process (https://b33p.net/kosada/node/14317).
3217 
3218  int scope = environments.size() - (lastCompositionIsSubcomposition ? 2 : 1);
3219  genEnv = environments.at(scope).at(1);
3220  potentialSpecializedModules[genEnv] = modulesAddedKeys;
3221  }
3222 
3223  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3224  {
3225  Environment *env = (*i).at(0);
3226 
3227  ModuleInfoIterator modulesAddedIter = (willLoadAllModules ? env->listAllModules() : env->listModules(modulesAddedKeys));
3228  ModuleInfoIterator sourcesAddedIter = (willLoadAllModules ? env->listAllSourceFiles() : env->listSourceFiles(sourcesAddedKeys));
3229  ModuleInfoIterator sourcesModifiedIter = (willLoadAllModules ? env->listAllSourceFiles() : env->listSourceFiles(sourcesModifiedKeys));
3230 
3231  ModuleInfo *moduleInfo;
3232  while ((moduleInfo = modulesAddedIter.next()))
3233  {
3234  string moduleKey = moduleInfo->getModuleKey();
3235 
3236  modulesAdded[env].insert(moduleKey);
3237 
3238  if (! willLoadAllModules)
3239  {
3240  auto foundIter = potentialSpecializedModules[genEnv].find(moduleKey);
3241  if (foundIter != potentialSpecializedModules[genEnv].end())
3242  potentialSpecializedModules[genEnv].erase(foundIter);
3243  }
3244  }
3245 
3246  // If a source file and a compiled file for the same module are in the same folder,
3247  // the compiled file takes precedence.
3248  auto isCompiledModuleAtSameSearchPath = [&env] (ModuleInfo *sourceInfo)
3249  {
3250  ModuleInfo *compiledModuleInfo = env->listModule(sourceInfo->getModuleKey());
3251  return (compiledModuleInfo && compiledModuleInfo->getSearchPath() == sourceInfo->getSearchPath());
3252  };
3253 
3254  while ((moduleInfo = sourcesAddedIter.next()))
3255  {
3256  if (isCompiledModuleAtSameSearchPath(moduleInfo))
3257  continue;
3258 
3259  sourcesAdded[env].insert( moduleInfo->getModuleKey() );
3260  }
3261 
3262  while ((moduleInfo = sourcesModifiedIter.next()))
3263  {
3264  if (isCompiledModuleAtSameSearchPath(moduleInfo))
3265  continue;
3266 
3267  sourcesModified[env].insert( moduleInfo->getModuleKey() );
3268  }
3269  }
3270  }
3271 
3272  map<Environment *, VuoCompilerIssues *> issues;
3273  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3274  {
3275  Environment *env = (*i).at(0);
3276  issues[env] = (env == currentEnvironment && issuesForCurrentEnvironment ? issuesForCurrentEnvironment : new VuoCompilerIssues());
3277  }
3278 
3279  // Check for circular dependencies in added/modified sources.
3280 
3281  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3282  {
3283  Environment *env = (*i).at(0);
3284 
3285  // Check for circular dependencies involving sources being loaded within this environment.
3286  // For circular dependencies involving sources in different environments,
3287  // an error will be reported elsewhere due to one of them being outside of the other's scope.
3288  set<VuoDirectedAcyclicGraph::Vertex *> circularDependencies = env->getCompositionDependencyGraph()->getCycleVertices();
3289 
3290  set<string> sourcesAddedModified;
3291  sourcesAddedModified.insert(sourcesAdded[env].begin(), sourcesAdded[env].end());
3292  sourcesAddedModified.insert(sourcesModified[env].begin(), sourcesModified[env].end());
3293 
3294  for (set<string>::iterator j = sourcesAddedModified.begin(); j != sourcesAddedModified.end(); ++j)
3295  {
3296  string moduleKey = *j;
3297 
3298  bool found = false;
3299  for (set<VuoDirectedAcyclicGraph::Vertex *>::iterator k = circularDependencies.begin(); k != circularDependencies.end(); ++k)
3300  {
3301  DependencyGraphVertex *vertex = static_cast<DependencyGraphVertex *>(*k);
3302  if (vertex->getDependency() == moduleKey)
3303  {
3304  found = true;
3305  break;
3306  }
3307  }
3308 
3309  if (found)
3310  {
3311  sourcesAdded[env].erase(moduleKey);
3312  sourcesModified[env].erase(moduleKey);
3313 
3314  VuoCompilerIssue issue(VuoCompilerIssue::Error, "compiling subcomposition", moduleKey,
3315  "Subcomposition contains itself",
3316  "%moduleKey contains an instance of itself, "
3317  "or contains another subcomposition that contains an instance of %moduleKey.");
3318  issue.setModuleKey(moduleKey);
3319 
3320  ModuleInfo *sourceInfo = env->listSourceFile(moduleKey);
3321  if (sourceInfo)
3322  issue.setFilePath(sourceInfo->getFile()->path());
3323 
3324  issues[env]->append(issue);
3325  }
3326  }
3327  }
3328 
3329  // Update the longest downstream paths for added/modified sources.
3330 
3331  for (const vector<Environment *> &envs : environments)
3332  {
3333  Environment *env = envs.at(0);
3334 
3335  set<string> sourcesAddedModified;
3336  sourcesAddedModified.insert(sourcesAdded[env].begin(), sourcesAdded[env].end());
3337  sourcesAddedModified.insert(sourcesModified[env].begin(), sourcesModified[env].end());
3338 
3339  for (const string &moduleKey : sourcesAddedModified)
3340  {
3341  VuoDirectedAcyclicGraph::Vertex *vertex = env->getCompositionDependencyGraph()->findVertex(moduleKey);
3342  int pathLength = env->getCompositionDependencyGraph()->getLongestDownstreamPath(vertex);
3343 
3344  ModuleInfo *sourceInfo = env->listSourceFile(moduleKey);
3345  sourceInfo->setLongestDownstreamPath(pathLength);
3346  }
3347  }
3348 
3349  // Find all modules and sources that depend on the modules and sources being modified or removed.
3350  // Those that belong to one of this compiler's environments are used in subsequent stages.
3351  // Those that belong to some other environment are scheduled to be handled by a separate call to this function.
3352 
3353  map<Environment *, set<string> > modulesDepOnModulesModified;
3354  map<Environment *, set<string> > sourcesDepOnModulesModified;
3355  map<Environment *, set<string> > modulesDepOnModulesRemoved;
3356  map<Environment *, set<string> > sourcesDepOnModulesRemoved;
3357 
3358  {
3359  __block map<Environment *, set<string> > modulesDepOnModulesModified_otherCompiler;
3360  __block map<Environment *, set<string> > sourcesDepOnModulesModified_otherCompiler;
3361  __block map<Environment *, set<string> > modulesDepOnModulesRemoved_otherCompiler;
3362  __block map<Environment *, set<string> > sourcesDepOnModulesRemoved_otherCompiler;
3363 
3364  vector<VuoDirectedAcyclicNetwork *> searchDependencyGraphs;
3365  for (VuoCompiler *compiler : allCompilers)
3366  searchDependencyGraphs.push_back(compiler->dependencyGraph);
3367 
3368  VuoDirectedAcyclicGraph *currentEnvironmentDependencyGraph = (currentEnvironment ? currentEnvironment->getDependencyGraph() : nullptr);
3369 
3370  findDependentModulesAndSources(modulesModified, searchDependencyGraphs, currentEnvironmentDependencyGraph,
3371  modulesDepOnModulesModified, modulesDepOnModulesModified_otherCompiler,
3372  sourcesDepOnModulesModified, sourcesDepOnModulesModified_otherCompiler);
3373 
3374  findDependentModulesAndSources(modulesRemoved, searchDependencyGraphs, currentEnvironmentDependencyGraph,
3375  modulesDepOnModulesRemoved, modulesDepOnModulesRemoved_otherCompiler,
3376  sourcesDepOnModulesRemoved, sourcesDepOnModulesRemoved_otherCompiler);
3377 
3378  set<Environment *> otherEnvironments;
3379  for (map<Environment *, set<string> >::iterator i = modulesDepOnModulesModified_otherCompiler.begin(); i != modulesDepOnModulesModified_otherCompiler.end(); ++i)
3380  otherEnvironments.insert(i->first);
3381  for (map<Environment *, set<string> >::iterator i = sourcesDepOnModulesModified_otherCompiler.begin(); i != sourcesDepOnModulesModified_otherCompiler.end(); ++i)
3382  otherEnvironments.insert(i->first);
3383  for (map<Environment *, set<string> >::iterator i = modulesDepOnModulesRemoved_otherCompiler.begin(); i != modulesDepOnModulesRemoved_otherCompiler.end(); ++i)
3384  otherEnvironments.insert(i->first);
3385  for (map<Environment *, set<string> >::iterator i = sourcesDepOnModulesRemoved_otherCompiler.begin(); i != sourcesDepOnModulesRemoved_otherCompiler.end(); ++i)
3386  otherEnvironments.insert(i->first);
3387 
3388  for (Environment *env : otherEnvironments)
3389  {
3390  VuoCompiler *otherCompiler = nullptr;
3391  for (VuoCompiler *c : allCompilers)
3392  for (const vector<Environment *> &ee : c->environments)
3393  for (Environment *e : ee)
3394  if (e == env)
3395  {
3396  otherCompiler = c;
3397  goto foundOtherCompiler;
3398  }
3399  foundOtherCompiler:
3400 
3401  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
3402  dispatch_sync(environmentQueue, ^{
3403  otherCompiler->loadModulesAndSources(set<string>(), modulesDepOnModulesModified_otherCompiler[env], modulesDepOnModulesRemoved_otherCompiler[env],
3404  set<string>(), sourcesDepOnModulesModified_otherCompiler[env], sourcesDepOnModulesRemoved_otherCompiler[env],
3405  false, true, env, nullptr, nullptr, "");
3406  });
3407  });
3408  }
3409  }
3410 
3411  // Unload:
3412  // - modules that have been removed or modified
3413  // - modules that depend on them
3414 
3415  map<Environment *, set<VuoCompilerModule *> > actualModulesRemoved;
3416  for (const vector<Environment *> &envs : environments)
3417  {
3418  for (Environment *env : envs)
3419  {
3420  set<string> modulesToUnload;
3421  modulesToUnload.insert(modulesRemoved[env].begin(), modulesRemoved[env].end());
3422  modulesToUnload.insert(modulesModified[env].begin(), modulesModified[env].end());
3423  modulesToUnload.insert(modulesDepOnModulesRemoved[env].begin(), modulesDepOnModulesRemoved[env].end());
3424  modulesToUnload.insert(modulesDepOnModulesModified[env].begin(), modulesDepOnModulesModified[env].end());
3425 
3426  actualModulesRemoved[env] = env->unloadCompiledModules(modulesToUnload);
3427 
3428  if (!env->isBuiltInOriginal() && !actualModulesRemoved[env].empty())
3429  {
3430  set<string> actualModulesRemovedKeys;
3431  for (auto m : actualModulesRemoved[env])
3432  actualModulesRemovedKeys.insert(m->getPseudoBase()->getModuleKey());
3433  VUserLog("Removed from %s environment: %s", env->getName().c_str(), VuoStringUtilities::join(actualModulesRemovedKeys, ", ").c_str());
3434  }
3435  }
3436  }
3437 
3438  // Load:
3439  // - modules that have been added or modified
3440  // - modules that they depend on
3441  // - modules that depend on them that were just unloaded
3442  // Delete:
3443  // - cached module files in `modulesToLoad` whose source files have been removed
3444 
3445  map<Environment *, set<string> > modulesToLoad;
3446  map<Environment *, map<string, string> > modulesToLoadSourceCode;
3447  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3448  {
3449  Environment *env = (*i).at(0);
3450 
3451  if (! modulesAdded[env].empty())
3452  modulesToLoad[env].insert(modulesAdded[env].begin(), modulesAdded[env].end());
3453  if (! modulesModified[env].empty())
3454  modulesToLoad[env].insert(modulesModified[env].begin(), modulesModified[env].end());
3455  if (! modulesDepOnModulesModified[env].empty())
3456  modulesToLoad[env].insert(modulesDepOnModulesModified[env].begin(), modulesDepOnModulesModified[env].end());
3457 
3458  if (env == currentEnvironment && moduleLoadedCallback)
3459  {
3460  if (modulesAdded[env].size() == 1)
3461  modulesToLoadSourceCode[env][*modulesAdded[env].begin()] = moduleAddedOrModifiedSourceCode;
3462  else if (modulesModified[env].size() == 1)
3463  modulesToLoadSourceCode[env][*modulesModified[env].begin()] = moduleAddedOrModifiedSourceCode;
3464  }
3465  }
3466 
3467  map<Environment *, set<VuoCompilerModule *> > actualModulesAdded;
3468  while (! modulesToLoad.empty())
3469  {
3470  set<string> dependenciesToLoad;
3471  map<Environment *, set<string> > potentialSpecializedDependencies;
3472  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3473  {
3474  Environment *env = (*i).at(0);
3475  Environment *genEnv = (*i).at(1);
3476 
3477  set<VuoCompilerModule *> actualModulesLoaded = env->loadCompiledModules(modulesToLoad[env], modulesToLoadSourceCode[env]);
3478 
3479  actualModulesAdded[env].insert(actualModulesLoaded.begin(), actualModulesLoaded.end());
3480  modulesToLoad.erase(env);
3481 
3482  for (set<VuoCompilerModule *>::iterator j = actualModulesLoaded.begin(); j != actualModulesLoaded.end(); ++j)
3483  {
3484  set<string> dependencies = (*j)->getDependencies();
3485  dependenciesToLoad.insert(dependencies.begin(), dependencies.end());
3486  potentialSpecializedDependencies[genEnv].insert(dependencies.begin(), dependencies.end());
3487  }
3488 
3489  if (!env->isBuiltInOriginal() && !actualModulesLoaded.empty())
3490  {
3491  map<string, string> actualFilesAndHashesLoaded;
3492  for (auto module : actualModulesLoaded)
3493  {
3494  string path, hash;
3495  if (module->getPseudoBase()->getNodeSet())
3496  path = module->getPseudoBase()->getNodeSet()->getArchivePath();
3497  else
3498  {
3499  auto cnc = dynamic_cast<VuoCompilerNodeClass *>(module);
3500  if (cnc && !cnc->getSourcePath().empty())
3501  {
3502  path = cnc->getSourcePath();
3503  if (!cnc->getSourceCode().empty())
3504  // Use the latest source code, if it's been modified without saving.
3505  hash = VuoStringUtilities::calculateSHA256(cnc->getSourceCode());
3506  }
3507  else
3508  path = module->getModulePath();
3509  }
3510 
3511  if (hash.empty())
3512  try
3513  {
3515  }
3516  catch (VuoException &e) {}
3517 
3518  actualFilesAndHashesLoaded[path] = hash;
3519  }
3520 
3521  for (pair<string, string> item : actualFilesAndHashesLoaded)
3522  VUserLog("Loaded into %s environment: [%8.8s] %s", env->getName().c_str(), item.second.c_str(), item.first.c_str());
3523  }
3524  }
3525 
3526  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3527  {
3528  Environment *env = (*i).at(0);
3529 
3530  ModuleInfoIterator dependenciesInEnv = env->listModules(dependenciesToLoad);
3531  ModuleInfo *moduleInfo;
3532  while ((moduleInfo = dependenciesInEnv.next()))
3533  {
3534  modulesToLoad[env].insert( moduleInfo->getModuleKey() );
3535 
3536  for (map<Environment *, set<string> >::iterator j = potentialSpecializedDependencies.begin(); j != potentialSpecializedDependencies.end(); ++j)
3537  {
3538  auto foundIter = j->second.find( moduleInfo->getModuleKey() );
3539  if (foundIter != j->second.end())
3540  j->second.erase(foundIter);
3541  }
3542  }
3543  }
3544 
3545  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3546  {
3547  Environment *genEnv = (*i).at(1);
3548  potentialSpecializedModules[genEnv].insert(potentialSpecializedDependencies[genEnv].begin(), potentialSpecializedDependencies[genEnv].end());
3549  }
3550  }
3551 
3552  // Load asynchronously:
3553  // - specializations of generic modules
3554 
3555  set<dispatch_group_t> specializedModulesLoading;
3556  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3557  {
3558  Environment *genEnv = (*i).at(1);
3559  set<dispatch_group_t> s = genEnv->loadSpecializedModules(potentialSpecializedModules[genEnv], this, llvmQueue);
3560  specializedModulesLoading.insert(s.begin(), s.end());
3561  }
3562 
3563  // Notify those waiting on a source file to be compiled that its module has now been loaded.
3564 
3565  if (moduleLoadedCallback)
3566  moduleLoadedCallback();
3567 
3568  // Move modified modules from `actualModulesAdded` and `actualModulesRemoved` to `actualModulesModified`.
3569 
3570  map<Environment *, set< pair<VuoCompilerModule *, VuoCompilerModule *> > > actualModulesModified;
3571  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3572  {
3573  Environment *env = (*i).at(0);
3574 
3575  for (set<VuoCompilerModule *>::iterator add = actualModulesAdded[env].begin(); add != actualModulesAdded[env].end(); )
3576  {
3577  set<VuoCompilerModule *>::iterator rem;
3578  for (rem = actualModulesRemoved[env].begin(); rem != actualModulesRemoved[env].end(); ++rem)
3579  if ((*rem)->getPseudoBase()->getModuleKey() == (*add)->getPseudoBase()->getModuleKey())
3580  break;
3581 
3582  if (rem != actualModulesRemoved[env].end())
3583  {
3584  actualModulesModified[env].insert( make_pair(*rem, *add) );
3585  actualModulesRemoved[env].erase(rem);
3586  actualModulesAdded[env].erase(add++);
3587  }
3588  else
3589  ++add;
3590  }
3591  }
3592 
3593  // Reify port types on node classes (if needed).
3594 
3595  bool wereModulesAddedOrModified = false;
3596  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3597  {
3598  Environment *env = (*i).at(0);
3599  if (! (actualModulesAdded[env].empty() && actualModulesModified[env].empty()) )
3600  {
3601  wereModulesAddedOrModified = true;
3602  break;
3603  }
3604  }
3605 
3606  if (wereModulesAddedOrModified)
3607  {
3608  map<string, VuoCompilerType *> inheritedTypes;
3609  for (const vector<Environment *> &envs : environments)
3610  {
3611  for (Environment *env : envs)
3612  {
3613  env->reifyPortTypes(inheritedTypes);
3614  map<string, VuoCompilerType *> envTypes = env->getTypes();
3615  inheritedTypes.insert(envTypes.begin(), envTypes.end());
3616  }
3617  }
3618  }
3619 
3620  // Delete cached compiled module files and call this function recursively for:
3621  // - modules whose source files have been removed
3622  // - modules whose source files depend on them
3623 
3624  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3625  {
3626  Environment *env = (*i).at(0);
3627 
3628  set<string> sourcesToUnload;
3629  sourcesToUnload.insert(sourcesRemoved[env].begin(), sourcesRemoved[env].end());
3630  sourcesToUnload.insert(sourcesDepOnModulesRemoved[env].begin(), sourcesDepOnModulesRemoved[env].end());
3631  if (! sourcesToUnload.empty())
3632  {
3633  string moduleSearchPath = env->getModuleSearchPaths().front();
3634 
3635  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
3636  VuoCompiler *otherCompiler = new VuoCompiler(moduleSearchPath + "/unused", target);
3637 
3638  dispatch_sync(environmentQueue, ^{
3639  otherCompiler->loadModulesAndSources(set<string>(), set<string>(), sourcesToUnload,
3640  set<string>(), set<string>(), set<string>(),
3641  false, false, env, nullptr, nullptr, "");
3642  });
3643 
3644  delete otherCompiler;
3645  });
3646  }
3647 
3648  if (!env->isBuiltInOriginal() && !sourcesToUnload.empty())
3649  VUserLog("Deleting from %s environment: %s", env->getName().c_str(), VuoStringUtilities::join(sourcesToUnload, ", ").c_str());
3650 
3651  env->deleteModulesCompiledFromSourceCode(sourcesToUnload);
3652  }
3653 
3654  // Compile asynchronously:
3655  // - source files that have been added or modified
3656  // - source files that depend on them
3657  // - source files that depend on modules that have been modified
3658 
3659  map<Environment *, set<string> > sourcesDepOnModulesAdded;
3660  {
3661  map<Environment *, set<string> > modulesDepOnModulesAdded; // unused
3662  __block map<Environment *, set<string> > modulesDepOnModulesAdded_otherCompiler; //
3663  __block map<Environment *, set<string> > sourcesDepOnModulesAdded_otherCompiler;
3664 
3665  map<Environment *, set<string> > actualModuleKeysAdded;
3666  for (const vector<Environment *> &envs : environments)
3667  {
3668  Environment *env = envs.at(0);
3669  for (VuoCompilerModule *module : actualModulesAdded[env])
3670  actualModuleKeysAdded[env].insert( module->getPseudoBase()->getModuleKey() );
3671  }
3672 
3673  vector<VuoDirectedAcyclicNetwork *> searchDependencyGraphs;
3674  searchDependencyGraphs.push_back(compositionDependencyGraph);
3675  for (map<string, vector<Environment *> >::iterator ii = environmentsForCompositionFamily[target].begin(); ii != environmentsForCompositionFamily[target].end(); ++ii)
3676  {
3677  vector< vector<Environment *> > otherEnvs = sharedEnvironments[target];
3678  otherEnvs.push_back(ii->second);
3679  VuoDirectedAcyclicNetwork *other = makeDependencyNetwork(otherEnvs, ^VuoDirectedAcyclicGraph * (Environment *env) { return env->getCompositionDependencyGraph(); });
3680  searchDependencyGraphs.push_back(other);
3681  }
3682 
3683  VuoDirectedAcyclicGraph *currentEnvironmentDependencyGraph = (currentEnvironment ? currentEnvironment->getCompositionDependencyGraph() : nullptr);
3684 
3685  findDependentModulesAndSources(actualModuleKeysAdded, searchDependencyGraphs, currentEnvironmentDependencyGraph,
3686  modulesDepOnModulesAdded, modulesDepOnModulesAdded_otherCompiler,
3687  sourcesDepOnModulesAdded, sourcesDepOnModulesAdded_otherCompiler);
3688 
3689  set<Environment *> otherEnvironments;
3690  for (map<Environment *, set<string> >::iterator i = sourcesDepOnModulesAdded_otherCompiler.begin(); i != sourcesDepOnModulesAdded_otherCompiler.end(); ++i)
3691  otherEnvironments.insert(i->first);
3692 
3693  for (Environment *env : otherEnvironments)
3694  {
3695  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
3696  string moduleSearchPath = env->getModuleSearchPaths().front();
3697  VuoCompiler *otherCompiler = new VuoCompiler(moduleSearchPath + "/unused", target);
3698 
3699  dispatch_sync(environmentQueue, ^{
3700  otherCompiler->loadModulesAndSources(set<string>(), set<string>(), set<string>(),
3701  sourcesDepOnModulesAdded_otherCompiler[env], set<string>(), set<string>(),
3702  false, true, env, nullptr, nullptr, "");
3703  });
3704 
3705  delete otherCompiler;
3706  });
3707  }
3708  }
3709 
3710  set<dispatch_group_t> sourcesLoading;
3711  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3712  {
3713  Environment *env = (*i).at(0);
3714 
3715  set<string> sourcesToCompile;
3716  sourcesToCompile.insert(sourcesAdded[env].begin(), sourcesAdded[env].end());
3717  sourcesToCompile.insert(sourcesModified[env].begin(), sourcesModified[env].end());
3718 
3719  if (sourcesToCompile.size() == 0)
3720  continue;
3721 
3722  set<dispatch_group_t> s = env->compileModulesFromSourceCode(sourcesToCompile, shouldRecompileSourcesIfUnchanged);
3723  sourcesLoading.insert(s.begin(), s.end());
3724  }
3725 
3726  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3727  {
3728  Environment *env = (*i).at(0);
3729 
3730  set<string> sourcesToCompile;
3731  sourcesToCompile.insert(sourcesDepOnModulesAdded[env].begin(), sourcesDepOnModulesAdded[env].end());
3732  sourcesToCompile.insert(sourcesDepOnModulesModified[env].begin(), sourcesDepOnModulesModified[env].end());
3733 
3734  if (sourcesToCompile.size() == 0)
3735  continue;
3736 
3737  env->compileModulesFromSourceCode(sourcesToCompile, true);
3738  }
3739 
3740  // Notify compiler delegates.
3741 
3742  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3743  {
3744  Environment *env = (*i).at(0);
3745  env->notifyCompilers(actualModulesAdded[env], actualModulesModified[env], actualModulesRemoved[env], issues[env]);
3746  }
3747 
3748  // Since the dispatch groups for specialized modules are temporary (caller is responsible for releasing them)
3749  // but the dispatch groups for module sources should stay alive as long as the ModuleInfo that contains them,
3750  // retain the dispatch groups for module sources so that all dispatch groups can be safely released by the caller.
3751 
3752  for (const dispatch_group_t &group : sourcesLoading)
3753  dispatch_retain(group);
3754 
3755  set<dispatch_group_t> loadingGroups;
3756  loadingGroups.insert(specializedModulesLoading.begin(), specializedModulesLoading.end());
3757  loadingGroups.insert(sourcesLoading.begin(), sourcesLoading.end());
3758  return loadingGroups;
3759 }
3760 
3772 void VuoCompiler::findDependentModulesAndSources(map<Environment *, set<string> > &changedModules,
3773  const vector<VuoDirectedAcyclicNetwork *> &searchDependencyGraphs,
3774  VuoDirectedAcyclicGraph *currentEnvironmentDependencyGraph,
3775  map<Environment *, set<string> > &modulesDepOnChangedModules_this,
3776  map<Environment *, set<string> > &modulesDepOnChangedModules_other,
3777  map<Environment *, set<string> > &sourcesDepOnChangedModules_this,
3778  map<Environment *, set<string> > &sourcesDepOnChangedModules_other)
3779 {
3780  for (const vector<Environment *> &envs : environments)
3781  {
3782  Environment *env = envs.at(0);
3783 
3784  for (const string &module : changedModules[env])
3785  {
3786  set<VuoDirectedAcyclicGraph::Vertex *> dependents;
3787  for (VuoDirectedAcyclicNetwork *searchDependencyGraph : searchDependencyGraphs)
3788  {
3789  vector<VuoDirectedAcyclicGraph::Vertex *> moduleVertices;
3790  if (currentEnvironmentDependencyGraph)
3791  {
3792  // If a module with the same module key is installed in multiple locations,
3793  // only consider the one being modified or removed.
3794  VuoDirectedAcyclicGraph::Vertex *mv = currentEnvironmentDependencyGraph->findVertex(module);
3795  if (mv)
3796  moduleVertices.push_back(mv);
3797  }
3798  else
3799  moduleVertices = searchDependencyGraph->findVertex(module);
3800 
3801  for (VuoDirectedAcyclicGraph::Vertex *moduleVertexRaw : moduleVertices)
3802  {
3803  DependencyGraphVertex *moduleVertex = static_cast<DependencyGraphVertex *>(moduleVertexRaw);
3804  if (moduleVertex->getEnvironment())
3805  {
3806  vector<VuoDirectedAcyclicGraph::Vertex *> upstream = searchDependencyGraph->getUpstreamVertices(moduleVertex);
3807  dependents.insert(upstream.begin(), upstream.end());
3808  }
3809  }
3810  }
3811 
3812  set< pair<Environment *, string> > dependentsMap;
3813  for (VuoDirectedAcyclicGraph::Vertex *dependentVertexRaw : dependents)
3814  {
3815  DependencyGraphVertex *v = static_cast<DependencyGraphVertex *>(dependentVertexRaw);
3816  Environment *dependentEnv = v->getEnvironment();
3817  if (! dependentEnv)
3818  continue;
3819 
3820  string dependent = v->getDependency();
3821 
3822  dependentsMap.insert({dependentEnv, dependent});
3823  }
3824 
3825  // In case `module` is a generic node class, check the generated environment at the same scope for any
3826  // specializations of the node class, and add them to the list of dependencies.
3827  // (They aren't in the dependency graph since the graph edge goes from installed to generated.)
3828  for (auto i : envs.at(1)->getNodeClasses())
3829  {
3830  VuoCompilerSpecializedNodeClass *specializedNodeClass = dynamic_cast<VuoCompilerSpecializedNodeClass *>(i.second);
3831  if (specializedNodeClass->getOriginalGenericNodeClassName() == module)
3832  dependentsMap.insert({envs.at(1), i.first});
3833  }
3834 
3835  for (auto i : dependentsMap)
3836  {
3837  Environment *dependentEnv = i.first;
3838  string dependent = i.second;
3839 
3840  // Skip if the dependent module is already being modified/removed in its own right
3841  // (e.g. if the module depends on another in the same node set and the node set is being removed).
3842  if (changedModules[dependentEnv].find(dependent) != changedModules[dependentEnv].end())
3843  continue;
3844 
3845  ModuleInfo *foundSourceInfo = dependentEnv->listSourceFile(dependent);
3846  ModuleInfo *foundModuleInfo = dependentEnv->listModule(dependent);
3847 
3848  bool belongsToCurrentCompiler = false;
3849  for (const vector<Environment *> &envs2 : environments)
3850  {
3851  if (find(envs2.begin(), envs2.end(), dependentEnv) != envs2.end())
3852  {
3853  belongsToCurrentCompiler = true;
3854  break;
3855  }
3856  }
3857 
3858  map<Environment *, set<string> > *whicheverDependents = nullptr;
3859  ModuleInfo *moduleInfo = nullptr;
3860  if (foundSourceInfo)
3861  {
3862  moduleInfo = foundSourceInfo;
3863  whicheverDependents = (belongsToCurrentCompiler ? &sourcesDepOnChangedModules_this : &sourcesDepOnChangedModules_other);
3864  }
3865  else if (foundModuleInfo)
3866  {
3867  moduleInfo = foundModuleInfo;
3868  whicheverDependents = (belongsToCurrentCompiler ? &modulesDepOnChangedModules_this : &modulesDepOnChangedModules_other);
3869  }
3870  else // Module in generated environment
3871  {
3872  whicheverDependents = (belongsToCurrentCompiler ? &modulesDepOnChangedModules_this : &modulesDepOnChangedModules_other);
3873  }
3874 
3875  (*whicheverDependents)[dependentEnv].insert(dependent);
3876  if (moduleInfo)
3877  moduleInfo->setAttempted(false);
3878  }
3879  }
3880  }
3881 }
3882 
3886 void VuoCompiler::loadedModules(map<string, VuoCompilerModule *> modulesAdded,
3887  map<string, pair<VuoCompilerModule *, VuoCompilerModule *> > modulesModified,
3888  map<string, VuoCompilerModule *> modulesRemoved,
3889  VuoCompilerIssues *issues, void *delegateDataV, Environment *currentEnvironment)
3890 {
3891  //VLog("C=%p %lu %lu %lu", this, modulesAdded.size(), modulesModified.size(), modulesRemoved.size());
3892 
3893  // If a module is added, superseding a version of the same module installed at a broader scope,
3894  // notify this VuoCompiler that the module has been modified rather than added.
3895  //
3896  // If a module is added, but a version of the same module is already installed at a narrower scope,
3897  // don't notify this VuoCompiler, since it will continue to use the version at the narrower scope.
3898  //
3899  // Same idea when a module is modified or removed while another version is installed at a different scope.
3900 
3901  auto findVersionsOfModule = [this, currentEnvironment] (const string &moduleKey)
3902  {
3903  vector< pair<Environment *, VuoCompilerModule *> > moduleVersions;
3904  for (const vector<Environment *> &envs : environments)
3905  {
3906  Environment *env = envs.at(0);
3907  VuoCompilerModule *module = env->findModule(moduleKey);
3908  if (module || env == currentEnvironment)
3909  moduleVersions.push_back( make_pair(env, module) );
3910  }
3911  return moduleVersions;
3912  };
3913 
3914  for (map<string, VuoCompilerModule *>::iterator i = modulesAdded.begin(); i != modulesAdded.end(); )
3915  {
3916  string moduleKey = i->first;
3917  VuoCompilerModule *moduleAdded = i->second;
3918 
3919  vector< pair<Environment *, VuoCompilerModule *> > moduleVersions = findVersionsOfModule(moduleKey);
3920 
3921  if (moduleVersions.size() > 1)
3922  {
3923  modulesAdded.erase(i++);
3924 
3925  if (moduleVersions.back().second == moduleAdded)
3926  {
3927  VuoCompilerModule *moduleSuperseded = moduleVersions.at(moduleVersions.size()-2).second;
3928  modulesModified[moduleKey] = make_pair(moduleSuperseded, moduleAdded);
3929  }
3930  }
3931  else
3932  ++i;
3933  }
3934 
3935  for (map<string, pair<VuoCompilerModule *, VuoCompilerModule *> >::iterator i = modulesModified.begin(); i != modulesModified.end(); )
3936  {
3937  string moduleKey = i->first;
3938  VuoCompilerModule *moduleModified = i->second.second;
3939 
3940  vector< pair<Environment *, VuoCompilerModule *> > moduleVersions = findVersionsOfModule(moduleKey);
3941 
3942  if (moduleVersions.size() > 1 && moduleVersions.back().second != moduleModified)
3943  modulesModified.erase(i++);
3944  else
3945  ++i;
3946  }
3947 
3948  for (map<string, VuoCompilerModule *>::iterator i = modulesRemoved.begin(); i != modulesRemoved.end(); )
3949  {
3950  string moduleKey = i->first;
3951  VuoCompilerModule *moduleRemoved = i->second;
3952 
3953  vector< pair<Environment *, VuoCompilerModule *> > moduleVersions = findVersionsOfModule(moduleKey);
3954 
3955  if (moduleVersions.size() > 1)
3956  {
3957  modulesRemoved.erase(i++);
3958 
3959  if (moduleVersions.back().first == currentEnvironment)
3960  {
3961  VuoCompilerModule *moduleUnsuperseded = moduleVersions.at(moduleVersions.size()-2).second;
3962  modulesModified[moduleKey] = make_pair(moduleRemoved, moduleUnsuperseded);
3963  }
3964  }
3965  else
3966  ++i;
3967  }
3968 
3969  dispatch_async(delegateQueue, ^{
3970  VuoCompilerDelegate::LoadedModulesData *delegateData = static_cast<VuoCompilerDelegate::LoadedModulesData *>(delegateDataV);
3971 
3972  if (delegate && ! (modulesAdded.empty() && modulesModified.empty() && modulesRemoved.empty() && issues->isEmpty()))
3973  {
3974  delegate->enqueueData(delegateData);
3975  delegate->loadedModules(modulesAdded, modulesModified, modulesRemoved, issues);
3976  }
3977  else
3978  {
3979  delegateData->release();
3980  }
3981  });
3982 }
3983 
3989 void VuoCompiler::loadNodeClassGeneratedAtRuntime(VuoCompilerNodeClass *nodeClass, Environment *env)
3990 {
3991  Module *module = nodeClass->getModule();
3992  if (module)
3993  {
3994  dispatch_sync(llvmQueue, ^{
3995  setTargetForModule(nodeClass->getModule(), env->getTarget());
3996  });
3997  }
3998 
3999  dispatch_sync(environmentQueue, ^{
4000  env->replaceNodeClass(nodeClass);
4001  });
4002 
4003  __block map<string, VuoCompilerType *> inheritedTypes;
4004  void (^envReifyPortTypes)(Environment *) = ^void (Environment *env) {
4005  env->reifyPortTypes(inheritedTypes);
4006  map<string, VuoCompilerType *> currentTypes = env->getTypes();
4007  inheritedTypes.insert(currentTypes.begin(), currentTypes.end());
4008  };
4009  applyToAllEnvironments(envReifyPortTypes);
4010 }
4011 
4015 void VuoCompiler::reifyGenericPortTypes(VuoCompilerComposition *composition)
4016 {
4017  for (VuoCompilerNode *node : composition->getCachedGraph(this)->getNodes())
4018  reifyGenericPortTypes(node->getBase());
4019 
4020  composition->invalidateCachedGraph();
4021 }
4022 
4026 void VuoCompiler::reifyGenericPortTypes(VuoNode *node)
4027 {
4029  if (! nodeClass)
4030  return;
4031 
4032  // Reify any generic types on the node that don't already have a compiler detail.
4033 
4034  vector<VuoPort *> inputPorts = node->getInputPorts();
4035  vector<VuoPort *> outputPorts = node->getOutputPorts();
4036  vector<VuoPort *> ports;
4037  ports.insert(ports.end(), inputPorts.begin(), inputPorts.end());
4038  ports.insert(ports.end(), outputPorts.begin(), outputPorts.end());
4039 
4040  for (vector<VuoPort *>::iterator j = ports.begin(); j != ports.end(); ++j)
4041  {
4042  VuoCompilerPort *port = static_cast<VuoCompilerPort *>((*j)->getCompiler());
4043  VuoGenericType *genericType = dynamic_cast<VuoGenericType *>(port->getDataVuoType());
4044  if (! genericType)
4045  continue;
4046 
4047  if (! genericType->hasCompiler())
4048  {
4049  VuoCompilerType * (^compilerGetType)(string) = ^VuoCompilerType * (string moduleKey) {
4050  return getType(moduleKey);
4051  };
4052 
4053  VuoCompilerGenericType *reifiedType = VuoCompilerGenericType::newGenericType(genericType, compilerGetType);
4054  if (reifiedType)
4055  port->setDataVuoType(reifiedType->getBase());
4056  }
4057  }
4058 
4059  // Update the node class's backing to match the node's backing.
4060 
4061  nodeClass->updateBackingNodeClass(node, this);
4062 }
4063 
4070 void VuoCompiler::compileModule(string inputPath, string outputPath)
4071 {
4072  compileModule(inputPath, outputPath, vector<string>());
4073 }
4074 
4082 void VuoCompiler::compileModule(string inputPath, string outputPath, const vector<string> &includePaths)
4083 {
4084  if (isVerbose)
4085  print();
4086 
4087  vector<string> allIncludePaths = includePaths;
4088  string preprocessedInputPath = inputPath;
4089 
4090  string tmpPreprocessedInputDir;
4091  string dir, file, ext;
4092  VuoFileUtilities::splitPath(inputPath, dir, file, ext);
4094  {
4095  string inputContents = VuoFileUtilities::readFileToString(inputPath);
4096  string preprocessedInputContents = inputContents;
4098  if (inputContents != preprocessedInputContents)
4099  {
4100  // Unspecialized generic node class
4101  allIncludePaths.push_back(dir.empty() ? "." : dir);
4102  tmpPreprocessedInputDir = VuoFileUtilities::makeTmpDir(file);
4103  preprocessedInputPath = tmpPreprocessedInputDir + "/" + file + "." + ext;
4104  VuoFileUtilities::preserveOriginalFileName(preprocessedInputContents, file + "." + ext);
4105  VuoFileUtilities::writeStringToFile(preprocessedInputContents, preprocessedInputPath);
4106  }
4107  }
4108  else if (ext == "fs")
4109  {
4110  VuoFileUtilities::File vuf(dir, file + "." + ext);
4111  VuoModuleCompiler *moduleCompiler = VuoModuleCompiler::newModuleCompiler("isf", getModuleKeyForPath(inputPath), &vuf);
4112  if (moduleCompiler)
4113  {
4114  auto getType = [this] (const string &moduleKey) { return this->getType(moduleKey); };
4115  VuoCompilerIssues *issues = new VuoCompilerIssues();
4116  Module *module = moduleCompiler->compile(getType, llvmQueue, issues);
4117  if (module)
4118  dispatch_sync(llvmQueue, ^{
4119  setTargetForModule(module, target);
4120  writeModuleToBitcode(module, outputPath);
4121  });
4122 
4123  if (!issues->isEmpty())
4124  throw VuoCompilerException(issues, true);
4125  delete issues;
4126  }
4127  return;
4128  }
4129 
4130  vector<string> extraArgs;
4131  for (vector<string>::iterator i = allIncludePaths.begin(); i != allIncludePaths.end(); ++i)
4132  {
4133  extraArgs.push_back("-I");
4134  extraArgs.push_back(*i);
4135  }
4136 
4137 
4138  // When compiling on a development workstation or Jenkins, use the Conan-packaged macOS SDK, since it includes headers.
4139  // When compiling on an end-user system, no SDK is needed.
4140  string buildTimeMacOSSDKFolder = MACOS_SDK_ROOT;
4141  if (VuoFileUtilities::fileExists(buildTimeMacOSSDKFolder))
4142  {
4143  extraArgs.push_back("-isysroot");
4144  extraArgs.push_back(buildTimeMacOSSDKFolder);
4145  }
4146 
4147 
4148  __block vector<string> headerSearchPaths;
4149  void (^envGetHeaderSearchPaths)(Environment *) = ^void (Environment *env) {
4150  vector<string> result = env->getHeaderSearchPaths();
4151  headerSearchPaths.insert(headerSearchPaths.end(), result.begin(), result.end());
4152  };
4153  applyToInstalledEnvironments(envGetHeaderSearchPaths);
4154 
4155  auto issues = new VuoCompilerIssues;
4156  __block Module *module;
4157  dispatch_sync(llvmQueue, ^{
4158  module = readModuleFromC(preprocessedInputPath, headerSearchPaths, extraArgs, issues);
4159  });
4160  string moduleKey = getModuleKeyForPath(inputPath);
4161  if (! tmpPreprocessedInputDir.empty())
4162  {
4163  remove(tmpPreprocessedInputDir.c_str());
4164  issues->setFilePath(inputPath);
4165  }
4166  if (! module)
4167  throw VuoCompilerException(issues, true);
4168  delete issues;
4169 
4170  dispatch_sync(llvmQueue, ^{
4172  if (! compilerModule)
4173  {
4174  VUserLog("Error: Didn't recognize '%s' as a node class, type, or library.", inputPath.c_str());
4175  return;
4176  }
4177 
4178  setTargetForModule(module, target);
4179  writeModuleToBitcode(module, outputPath);
4180 
4181  delete module;
4182  });
4183 }
4184 
4188 Module * VuoCompiler::compileCompositionToModule(VuoCompilerComposition *composition, const string &moduleKey, bool isTopLevelComposition,
4189  VuoCompilerIssues *issues)
4190 {
4191  composition->check(issues);
4192 
4193  reifyGenericPortTypes(composition);
4194 
4196  isTopLevelComposition,
4197  moduleKey, this);
4198  if (telemetry == "console")
4199  generator->setDebugMode(true);
4200 
4201  __block Module *module = nullptr;
4202  dispatch_sync(llvmQueue, ^{
4203  try
4204  {
4205  module = generator->generateBitcode();
4206  setTargetForModule(module, target);
4207  }
4208  catch (VuoCompilerException &e)
4209  {
4210  if (issues)
4211  issues->append(e.getIssues());
4212  else
4213  VUserLog("%s", e.getIssues()->getLongDescription(false).c_str());
4214  }
4215  });
4216 
4217  delete generator;
4218 
4219  return module;
4220 }
4221 
4232 void VuoCompiler::compileComposition(VuoCompilerComposition *composition, string outputPath, bool isTopLevelComposition,
4233  VuoCompilerIssues *issues)
4234 {
4235  string moduleKey = getModuleKeyForPath(outputPath);
4236  Module *module = compileCompositionToModule(composition, moduleKey, isTopLevelComposition, issues);
4237  if (!module)
4238  throw VuoCompilerException(issues, false);
4239 
4240  dispatch_sync(llvmQueue, ^{
4241  writeModuleToBitcode(module, outputPath);
4242  });
4243 }
4244 
4257 void VuoCompiler::compileComposition(string inputPath, string outputPath, bool isTopLevelComposition,
4258  VuoCompilerIssues *issues)
4259 {
4260  VDebugLog("Compiling '%s' (%s)…", inputPath.c_str(), target.c_str());
4261  if (isVerbose)
4262  print();
4263 
4264  if (getCompositionLocalPath().empty())
4265  setCompositionPath(inputPath);
4266 
4268  {
4269  VuoCompilerIssue issue(VuoCompilerIssue::Error, "opening composition", inputPath,
4270  "", "The composition file couldn't be read or was empty.");
4271  throw VuoCompilerException(issue);
4272  }
4273 
4274  try
4275  {
4276  string compositionString = VuoFileUtilities::readFileToString(inputPath);
4277  compileCompositionString(compositionString, outputPath, isTopLevelComposition, issues);
4278  }
4279  catch (VuoCompilerException &e)
4280  {
4281  if (e.getIssues())
4282  e.getIssues()->setFilePathIfEmpty(inputPath);
4283  if (!issues && e.getIssues())
4284  VUserLog("%s", e.getIssues()->getLongDescription(false).c_str());
4285  throw;
4286  }
4287 
4288  VDebugLog("Done.");
4289 }
4290 
4301 void VuoCompiler::compileCompositionString(const string &compositionString, string outputPath, bool isTopLevelComposition,
4302  VuoCompilerIssues *issues)
4303 {
4305  compileComposition(composition, outputPath, isTopLevelComposition, issues);
4306 
4307  VuoComposition *baseComposition = composition->getBase();
4308  delete composition;
4309  delete baseComposition;
4310 }
4311 
4326 void VuoCompiler::linkCompositionToCreateExecutable(string inputPath, string outputPath, Optimization optimization, string rPath, bool shouldAdHocCodeSign)
4327 {
4328  linkCompositionToCreateExecutableOrDynamicLibrary(inputPath, outputPath, optimization, false, rPath, shouldAdHocCodeSign);
4329 }
4330 
4348 void VuoCompiler::linkCompositionToCreateDynamicLibrary(string inputPath, string outputPath, Optimization optimization, bool shouldAdHocCodeSign)
4349 {
4350  linkCompositionToCreateExecutableOrDynamicLibrary(inputPath, outputPath, optimization, true, "", shouldAdHocCodeSign);
4351 }
4352 
4367 void VuoCompiler::linkCompositionToCreateExecutableOrDynamicLibrary(string compiledCompositionPath, string linkedCompositionPath,
4368  Optimization optimization, bool isDylib, string rPath, bool shouldAdHocCodeSign)
4369 {
4370  if (isVerbose)
4371  print();
4372 
4373  if (optimization == Optimization_FastBuildExistingCache)
4374  shouldLoadAllModules = false;
4375 
4376  set<string> dependencies = getDependenciesForComposition(compiledCompositionPath);
4377  dependencies.insert(getRuntimeDependency());
4378  if (! isDylib)
4379  dependencies.insert(getRuntimeMainDependency());
4380 
4381  set<Module *> modules;
4382  set<string> libraries;
4383  set<string> frameworks;
4384  getLinkerInputs(dependencies, optimization, modules, libraries, frameworks);
4385 
4386  libraries.insert(compiledCompositionPath);
4387 
4388  link(linkedCompositionPath, modules, libraries, frameworks, isDylib, rPath, shouldAdHocCodeSign);
4389 }
4390 
4405 void VuoCompiler::linkCompositionToCreateDynamicLibraries(string compiledCompositionPath, string linkedCompositionPath,
4406  VuoRunningCompositionLibraries *runningCompositionLibraries)
4407 {
4408  if (isVerbose)
4409  print();
4410 
4411  bool shouldAdHocCodeSign = false;
4412 #if __arm64__
4413  shouldAdHocCodeSign = true;
4414 #endif
4415 
4416  // Get the dependencies used by the new resources and not the previous resources.
4417 
4418  set<string> carriedOverDependencies = runningCompositionLibraries->getDependenciesLoaded();
4419  set<string> allDependencies = getDependenciesForComposition(compiledCompositionPath);
4420  set<string> addedDependencies;
4421  std::set_difference(allDependencies.begin(), allDependencies.end(),
4422  carriedOverDependencies.begin(), carriedOverDependencies.end(),
4423  std::inserter(addedDependencies, addedDependencies.end()));
4424 
4425  // Get the libraries and frameworks used by the previous resources.
4426 
4427  vector<string> carriedOverNonUnloadableLibraries = runningCompositionLibraries->getNonUnloadableLibrariesLoaded();
4428  vector<string> carriedOverUnloadableLibraries = runningCompositionLibraries->getUnloadableLibrariesLoaded();
4429  set<string> carriedOverExternalLibraries = runningCompositionLibraries->getExternalLibraries();
4430  set<string> carriedOverFrameworks = runningCompositionLibraries->getExternalFrameworks();
4431 
4432  // Link the new resource dylibs, if needed.
4433 
4434  string nonUnloadableResourcePath;
4435  string unloadableResourcePath;
4436  set<string> nonUnloadableDependencies;
4437  set<string> unloadableDependencies;
4438  map<string, set<string> > builtInCacheDependencies;
4439  map<string, set<string> > userCacheDependencies;
4440  set<string> builtInLibraries;
4441  set<string> userLibraries;
4442  set<string> addedExternalLibraries;
4443  set<string> addedFrameworks;
4444  set<string> allFrameworks;
4445  if (! addedDependencies.empty())
4446  {
4447  // Get the modules, libraries, and frameworks that will provide the composition's dependencies.
4448 
4449  set<string> builtInModuleAndLibraryDependencies;
4450  set<string> userModuleAndLibraryDependencies;
4451  set<Module *> builtInModules;
4452  set<Module *> userModules;
4453 
4454  getLinkerInputs(addedDependencies, Optimization_FastBuild,
4455  builtInModuleAndLibraryDependencies, userModuleAndLibraryDependencies, builtInCacheDependencies, userCacheDependencies,
4456  builtInModules, userModules, builtInLibraries, userLibraries, addedExternalLibraries, addedFrameworks);
4457 
4458  allFrameworks.insert(carriedOverFrameworks.begin(), carriedOverFrameworks.end());
4459  allFrameworks.insert(addedFrameworks.begin(), addedFrameworks.end());
4460 
4461  string dir, linkedCompositionFile, ext;
4462  VuoFileUtilities::splitPath(linkedCompositionPath, dir, linkedCompositionFile, ext);
4463 
4464  // For any module caches that were rebuilt, remove the previous revision from the lists of libraries to link to and load.
4465 
4466  vector<string> carriedOverUserCacheLibraries = runningCompositionLibraries->getUnloadableCacheLibrariesLoaded();
4467 
4468  __block vector<string> currentCacheLibraries;
4469  applyToAllEnvironments(^void (Environment *env) {
4470  currentCacheLibraries.push_back( env->getCurrentModuleCacheDylib() );
4471  });
4472 
4473  for (string cachePath : carriedOverUserCacheLibraries)
4474  {
4475  for (string currentCachePath : currentCacheLibraries)
4476  {
4477  if (VuoFileUtilities::areDifferentRevisionsOfSameModuleCacheDylib(cachePath, currentCachePath))
4478  {
4479  set<string> dependenciesInCache = runningCompositionLibraries->enqueueCacheLibraryToUnload(cachePath);
4480 
4481  userCacheDependencies[currentCachePath].insert(dependenciesInCache.begin(), dependenciesInCache.end());
4482 
4483  auto cacheDependenciesIter = userCacheDependencies.find(cachePath);
4484  if (cacheDependenciesIter != userCacheDependencies.end())
4485  {
4486  userCacheDependencies[currentCachePath].insert(cacheDependenciesIter->second.begin(), cacheDependenciesIter->second.end());
4487  userCacheDependencies.erase(cacheDependenciesIter);
4488  }
4489 
4490  auto carriedOverIter = find(carriedOverUnloadableLibraries.begin(), carriedOverUnloadableLibraries.end(), cachePath);
4491  if (carriedOverIter != carriedOverUnloadableLibraries.end())
4492  *carriedOverIter = currentCachePath;
4493  }
4494  }
4495  }
4496 
4497  // If any module caches were rebuilt, prepare to replace the existing user resource dylibs with the new resource dylib created below.
4498 
4499  bool wasModuleCacheRebuilt = runningCompositionLibraries->hasCacheLibraryEnqueuedToUnload();
4500  if (wasModuleCacheRebuilt)
4501  {
4502  vector<string> carriedOverResourceLibraries = runningCompositionLibraries->getUnloadableResourceLibrariesLoaded();
4503 
4504  vector<string> carriedOverUnloadableMinusResourceLibraries;
4505  std::set_difference(carriedOverUnloadableLibraries.begin(), carriedOverUnloadableLibraries.end(),
4506  carriedOverResourceLibraries.begin(), carriedOverResourceLibraries.end(),
4507  std::back_inserter(carriedOverUnloadableMinusResourceLibraries));
4508 
4509  carriedOverUnloadableLibraries = carriedOverUnloadableMinusResourceLibraries;
4510 
4511  set<string> dependenciesInResourceLibraries = runningCompositionLibraries->enqueueAllUnloadableResourceLibrariesToUnload();
4512  userModuleAndLibraryDependencies.insert(dependenciesInResourceLibraries.begin(), dependenciesInResourceLibraries.end());
4513 
4514  set<string> builtInModuleAndLibraryDependencies_tmp;
4515  set<string> userModuleAndLibraryDependencies_tmp;
4516  map<string, set<string> > builtInCacheDependencies_tmp;
4517  set<Module *> builtInModules_tmp;
4518  set<string> builtInLibraries_tmp;
4519  set<string> externalLibraries_tmp;
4520  set<string> externalFrameworks_tmp;
4521 
4522  getLinkerInputs(userModuleAndLibraryDependencies, Optimization_FastBuild,
4523  builtInModuleAndLibraryDependencies_tmp, userModuleAndLibraryDependencies_tmp, builtInCacheDependencies_tmp, userCacheDependencies,
4524  builtInModules_tmp, userModules, builtInLibraries_tmp, userLibraries, externalLibraries_tmp, externalFrameworks_tmp);
4525  }
4526 
4527  // If built-in dependencies were added, create an additional resource dylib.
4528 
4529  if (! builtInModules.empty() || builtInLibraries.size() > builtInCacheDependencies.size())
4530  {
4531  nonUnloadableResourcePath = VuoFileUtilities::makeTmpFile(linkedCompositionFile + "-resource-nonunloadable", "dylib");
4532  nonUnloadableDependencies = builtInModuleAndLibraryDependencies;
4533 
4534  set<string> librariesForNonUnloadableResource = builtInLibraries;
4535  librariesForNonUnloadableResource.insert(carriedOverNonUnloadableLibraries.begin(), carriedOverNonUnloadableLibraries.end());
4536  librariesForNonUnloadableResource.insert(carriedOverExternalLibraries.begin(), carriedOverExternalLibraries.end());
4537  librariesForNonUnloadableResource.insert(addedExternalLibraries.begin(), addedExternalLibraries.end());
4538 
4539  link(nonUnloadableResourcePath, builtInModules, librariesForNonUnloadableResource, allFrameworks, true, "", shouldAdHocCodeSign);
4540 
4541  for (set<string>::iterator i = builtInLibraries.begin(); i != builtInLibraries.end(); )
4542  {
4543  if (! VuoStringUtilities::endsWith(*i, ".dylib"))
4544  builtInLibraries.erase(i++);
4545  else
4546  i++;
4547  }
4548 
4549  for (set<string>::iterator i = addedExternalLibraries.begin(); i != addedExternalLibraries.end(); )
4550  {
4551  if (! VuoStringUtilities::endsWith(*i, ".dylib"))
4552  addedExternalLibraries.erase(i++);
4553  else
4554  i++;
4555  }
4556  }
4557 
4558  // If user dependencies were added or module caches were rebuilt, create an additional resource dylib.
4559 
4560  if (! userModules.empty() || userLibraries.size() > userCacheDependencies.size() || wasModuleCacheRebuilt)
4561  {
4562  unloadableResourcePath = VuoFileUtilities::makeTmpFile(linkedCompositionFile + "-resource-unloadable", "dylib");
4563  unloadableDependencies = userModuleAndLibraryDependencies;
4564 
4565  set<string> librariesForUnloadableResource = userLibraries;
4566  librariesForUnloadableResource.insert(builtInLibraries.begin(), builtInLibraries.end());
4567  librariesForUnloadableResource.insert(carriedOverUnloadableLibraries.begin(), carriedOverUnloadableLibraries.end());
4568  librariesForUnloadableResource.insert(carriedOverNonUnloadableLibraries.begin(), carriedOverNonUnloadableLibraries.end());
4569  librariesForUnloadableResource.insert(carriedOverExternalLibraries.begin(), carriedOverExternalLibraries.end());
4570  librariesForUnloadableResource.insert(addedExternalLibraries.begin(), addedExternalLibraries.end());
4571  if (! nonUnloadableResourcePath.empty())
4572  librariesForUnloadableResource.insert(nonUnloadableResourcePath);
4573 
4574  link(unloadableResourcePath, userModules, librariesForUnloadableResource, allFrameworks, true, "", shouldAdHocCodeSign);
4575 
4576  for (set<string>::iterator i = userLibraries.begin(); i != userLibraries.end(); )
4577  {
4578  if (! VuoStringUtilities::endsWith(*i, ".dylib"))
4579  userLibraries.erase(i++);
4580  else
4581  i++;
4582  }
4583  }
4584  }
4585 
4586  // Get the Vuo runtime dependency.
4587 
4588  set<string> vuoRuntimePaths;
4589  {
4590  set<Module *> modules;
4591  set<string> libraries;
4592  set<string> frameworks;
4593 
4594  set<string> dependencies;
4595  dependencies.insert(getRuntimeDependency());
4596  getLinkerInputs(dependencies, Optimization_FastBuild, modules, libraries, frameworks);
4597  vuoRuntimePaths = libraries;
4598  }
4599 
4600  // Link the composition.
4601 
4602  {
4603  set<Module *> modules;
4604  set<string> libraries;
4605 
4606  libraries.insert(compiledCompositionPath);
4607  libraries.insert(carriedOverExternalLibraries.begin(), carriedOverExternalLibraries.end());
4608  libraries.insert(addedExternalLibraries.begin(), addedExternalLibraries.end());
4609  libraries.insert(carriedOverNonUnloadableLibraries.begin(), carriedOverNonUnloadableLibraries.end());
4610  libraries.insert(carriedOverUnloadableLibraries.begin(), carriedOverUnloadableLibraries.end());
4611  libraries.insert(builtInLibraries.begin(), builtInLibraries.end());
4612  libraries.insert(userLibraries.begin(), userLibraries.end());
4613  if (! nonUnloadableResourcePath.empty())
4614  libraries.insert(nonUnloadableResourcePath);
4615  if (! unloadableResourcePath.empty())
4616  libraries.insert(unloadableResourcePath);
4617  libraries.insert(vuoRuntimePaths.begin(), vuoRuntimePaths.end());
4618  link(linkedCompositionPath, modules, libraries, allFrameworks, true, "", shouldAdHocCodeSign);
4619  }
4620 
4621  // Now that we're past the point where an exception can be thrown, update the RunningCompositionLibraries.
4622 
4623  if (! nonUnloadableResourcePath.empty())
4624  runningCompositionLibraries->enqueueResourceLibraryToLoad(nonUnloadableResourcePath, nonUnloadableDependencies, false);
4625 
4626  if (! unloadableResourcePath.empty())
4627  runningCompositionLibraries->enqueueResourceLibraryToLoad(unloadableResourcePath, unloadableDependencies, true);
4628 
4629  for (map<string, set<string> >::iterator i = builtInCacheDependencies.begin(); i != builtInCacheDependencies.end(); ++i)
4630  runningCompositionLibraries->enqueueCacheLibraryToLoad(i->first, i->second, false);
4631 
4632  for (map<string, set<string> >::iterator i = userCacheDependencies.begin(); i != userCacheDependencies.end(); ++i)
4633  runningCompositionLibraries->enqueueCacheLibraryToLoad(i->first, i->second, true);
4634 
4635  runningCompositionLibraries->addExternalFrameworks(addedFrameworks);
4636  runningCompositionLibraries->addExternalLibraries(addedExternalLibraries);
4637 }
4638 
4645 set<string> VuoCompiler::getDependenciesForComposition(const string &compiledCompositionPath)
4646 {
4647  VDebugLog("Gathering dependencies for '%s'…", compiledCompositionPath.c_str());
4648 
4649  // Add the node classes in the top-level composition and their dependencies.
4650  __block set<string> directDependencies;
4651  string moduleKey = getModuleKeyForPath(compiledCompositionPath);
4652  Module *module = readModuleFromBitcode(compiledCompositionPath, getTargetArch(target));
4653  dispatch_sync(llvmQueue, ^{
4655  directDependencies = compilerModule->getDependencies();
4656  delete compilerModule;
4657  delete module;
4658  });
4659 
4660  try
4661  {
4662  auto deps = getDependenciesForComposition(directDependencies, true);
4663  VDebugLog("Done.");
4664  return deps;
4665  }
4666  catch (VuoCompilerException &e)
4667  {
4668  e.getIssues()->setFilePathIfEmpty(compiledCompositionPath);
4669  throw;
4670  }
4671 }
4672 
4680 {
4681  set<string> directDependencies;
4682 
4683  set<VuoCompilerNode *> nodes = composition->getCachedGraph(this)->getNodes();
4684  for (VuoCompilerNode *node : nodes)
4685  if (node->getBase()->getNodeClass()->hasCompiler())
4686  directDependencies.insert( node->getBase()->getNodeClass()->getCompiler()->getDependencyName() );
4687 
4688  vector<VuoPublishedPort *> publishedInputPorts = composition->getBase()->getPublishedInputPorts();
4689  vector<VuoPublishedPort *> publishedOutputPorts = composition->getBase()->getPublishedOutputPorts();
4690  vector<VuoPublishedPort *> publishedPorts;
4691  publishedPorts.insert(publishedPorts.end(), publishedInputPorts.begin(), publishedInputPorts.end());
4692  publishedPorts.insert(publishedPorts.end(), publishedOutputPorts.begin(), publishedOutputPorts.end());
4693  for (VuoPublishedPort *publishedPort : publishedPorts)
4694  {
4695  if (publishedPort->getClass()->hasCompiler())
4696  {
4697  VuoType *portType = static_cast<VuoCompilerPortClass *>( publishedPort->getClass()->getCompiler() )->getDataVuoType();
4698  if (portType)
4699  {
4700  string dependency;
4701  VuoGenericType *genericType = dynamic_cast<VuoGenericType *>(portType);
4702  if (genericType)
4703  {
4704  VuoGenericType::Compatibility compatibility;
4705  vector<string> compatibleTypeNames = genericType->getCompatibleSpecializedTypes(compatibility);
4706  dependency = VuoCompilerGenericType::chooseBackingTypeName(portType->getModuleKey(), compatibleTypeNames);
4707  }
4708  else
4709  dependency = portType->getModuleKey();
4710 
4711  directDependencies.insert(dependency);
4712  }
4713  }
4714  }
4715 
4716  return directDependencies;
4717 }
4718 
4725 set<string> VuoCompiler::getDependenciesForComposition(VuoCompilerComposition *composition)
4726 {
4727  set<string> directDependencies = getDirectDependenciesForComposition(composition);
4728  return getDependenciesForComposition(directDependencies, false);
4729 }
4730 
4737 {
4738  __block vector<string> librarySearchPaths;
4739  applyToInstalledEnvironments(^void (Environment *env) {
4740  vector<string> result = env->getLibrarySearchPaths();
4741  librarySearchPaths.insert(librarySearchPaths.end(), result.begin(), result.end());
4742  });
4743 
4744  set<string> dylibDeps;
4745  for (string dep : getDependenciesForComposition(composition))
4746  {
4747  string path = getLibraryPath(dep, librarySearchPaths);
4748  if (VuoStringUtilities::endsWith(path, ".dylib"))
4749  dylibDeps.insert(path);
4750  }
4751 
4752  return dylibDeps;
4753 }
4754 
4768 set<string> VuoCompiler::getDependenciesForComposition(const set<string> &directDependencies, bool checkCompatibility)
4769 {
4770  // Make sure that any compiler-generated node classes have been generated and added to the dependency graph.
4771  for (set<string>::const_iterator i = directDependencies.begin(); i != directDependencies.end(); ++i)
4772  getNodeClass(*i);
4773 
4774  set<string> dependencies;
4775  for (set<string>::iterator i = directDependencies.begin(); i != directDependencies.end(); ++i)
4776  {
4777  string moduleKey = *i;
4778 
4779  dependencies.insert(moduleKey);
4780 
4781  // First pass: Find all dependencies of the direct dependency that have been loaded so far.
4782  vector<VuoDirectedAcyclicGraph::Vertex *> firstPassVertices = dependencyGraph->findVertex(moduleKey);
4783  set<VuoDirectedAcyclicGraph::Vertex *> firstPassDependencies(firstPassVertices.begin(), firstPassVertices.end());
4784  for (vector<VuoDirectedAcyclicGraph::Vertex *>::iterator j = firstPassVertices.begin(); j != firstPassVertices.end(); ++j)
4785  {
4786  vector<VuoDirectedAcyclicGraph::Vertex *> downstream = dependencyGraph->getDownstreamVertices(*j);
4787  firstPassDependencies.insert(downstream.begin(), downstream.end());
4788  }
4789 
4790  // Make sure that any compiler-generated node classes in subcompositions have been generated and added to the dependency graph.
4791  for (set<VuoDirectedAcyclicGraph::Vertex *>::iterator j = firstPassDependencies.begin(); j != firstPassDependencies.end(); ++j)
4792  {
4793  DependencyGraphVertex *v = static_cast<DependencyGraphVertex *>(*j);
4794  getNodeClass(v->getDependency());
4795  }
4796 
4797  // Second pass: Find all dependencies of the direct dependency, this time including dependencies of the node classes just generated.
4798  vector<VuoDirectedAcyclicGraph::Vertex *> moduleVertices = dependencyGraph->findVertex(moduleKey);
4799  for (vector<VuoDirectedAcyclicGraph::Vertex *>::iterator j = moduleVertices.begin(); j != moduleVertices.end(); ++j)
4800  {
4801  vector<VuoDirectedAcyclicGraph::Vertex *> downstream = dependencyGraph->getDownstreamVertices(*j);
4802  for (VuoDirectedAcyclicGraph::Vertex *v : downstream)
4803  dependencies.insert( static_cast<DependencyGraphVertex *>(v)->getDependency() );
4804  }
4805  }
4806 
4807  // Check that the dependencies are compatible with the compiler's target.
4808  if (checkCompatibility)
4809  {
4811  VuoCompilerCompatibility actualCompatibility = getCompatibilityOfDependencies(dependencies);
4812 
4813  if (! actualCompatibility.isCompatibleWith(neededCompatibility))
4814  {
4815  VuoCompilerIssues *issues = new VuoCompilerIssues;
4816  set<VuoCompilerNodeClass *> nodeClassesReported;
4817 
4818  for (string moduleKey : dependencies)
4819  {
4820  VuoCompilerModule *module = getModule(moduleKey);
4821  if (module && ! module->getCompatibleTargets().isCompatibleWith(neededCompatibility))
4822  {
4823  vector<VuoCompilerNodeClass *> incompatibleNodeClasses;
4824 
4825  VuoCompilerNodeClass *moduleAsNodeClass = dynamic_cast<VuoCompilerNodeClass *>(module);
4826  if (moduleAsNodeClass)
4827  incompatibleNodeClasses.push_back(moduleAsNodeClass);
4828  else
4829  {
4830  vector<VuoDirectedAcyclicGraph::Vertex *> moduleVertices = dependencyGraph->findVertex(moduleKey);
4831  for (VuoDirectedAcyclicGraph::Vertex *v : moduleVertices)
4832  {
4833  vector<VuoDirectedAcyclicGraph::Vertex *> upstream = dependencyGraph->getUpstreamVertices(v);
4834 
4835  vector<string> upstreamModuleKeys;
4836  std::transform(upstream.begin(), upstream.end(),
4837  std::back_inserter(upstreamModuleKeys),
4838  [](VuoDirectedAcyclicGraph::Vertex *u){ return static_cast<DependencyGraphVertex *>(u)->getDependency(); });
4839 
4840  vector<string> potentialNodeClassNames;
4841  std::set_intersection(directDependencies.begin(), directDependencies.end(),
4842  upstreamModuleKeys.begin(), upstreamModuleKeys.end(),
4843  std::back_inserter(potentialNodeClassNames));
4844 
4845  for (string nodeClassName : potentialNodeClassNames)
4846  {
4847  VuoCompilerNodeClass *upstreamNodeClass = getNodeClass(nodeClassName);
4848  if (upstreamNodeClass)
4849  incompatibleNodeClasses.push_back(upstreamNodeClass);
4850  }
4851  }
4852  }
4853 
4854  if (incompatibleNodeClasses.empty())
4855  {
4856  VuoCompilerIssue issue(VuoCompilerIssue::Error, "linking composition", "",
4857  "Dependencies incompatible with system",
4858  "%module is only compatible with " + module->getCompatibleTargets().toString() +
4859  ", so this composition can't run on " + neededCompatibility.toString() + ".");
4860  issue.setModule(module->getPseudoBase());
4861  issues->append(issue);
4862  }
4863  else
4864  {
4865  for (VuoCompilerNodeClass *nodeClass : incompatibleNodeClasses)
4866  {
4867  if (nodeClassesReported.find(nodeClass) != nodeClassesReported.end())
4868  continue;
4869 
4870  nodeClassesReported.insert(nodeClass);
4871 
4872  VuoCompilerIssue issue(VuoCompilerIssue::Error, "linking composition", "",
4873  "Nodes incompatible with system",
4874  "%module is only compatible with " + module->getCompatibleTargets().toString() +
4875  ", so this composition can't run on " + neededCompatibility.toString() + ".");
4876  issue.setModule(nodeClass->getPseudoBase());
4877  issues->append(issue);
4878  }
4879  }
4880  }
4881  }
4882 
4883  if (issues->isEmpty())
4884  {
4885  VuoCompilerIssue issue(VuoCompilerIssue::Error, "linking composition", "",
4886  "Dependencies incompatible with system",
4887  "Some dependencies of this composition are only compatible with " + actualCompatibility.toString() +
4888  ", so this composition can't run on " + neededCompatibility.toString() + ".");
4889  issues->append(issue);
4890  }
4891 
4892  throw VuoCompilerException(issues, true);
4893  }
4894  }
4895 
4896  // Add the libraries needed by every linked composition.
4897  vector<string> coreDependencies = getCoreVuoDependencies();
4898  dependencies.insert(coreDependencies.begin(), coreDependencies.end());
4899 
4900  return dependencies;
4901 }
4902 
4907 {
4908  VuoCompilerCompatibility compatibility(nullptr);
4909 
4910  for (string dependency : dependencies)
4911  {
4912  VuoCompilerModule *module = getModule(dependency);
4913  if (module)
4914  compatibility = compatibility.intersection(module->getCompatibleTargets());
4915  }
4916 
4917  return compatibility;
4918 }
4919 
4924 void VuoCompiler::getLinkerInputs(const set<string> &dependencies, Optimization optimization,
4925  set<Module *> &modules, set<string> &libraries, set<string> &frameworks)
4926 {
4927  set<string> builtInModuleAndLibraryDependencies;
4928  set<string> userModuleAndLibraryDependencies;
4929  map<string, set<string> > builtInCacheDependencies;
4930  map<string, set<string> > userCacheDependencies;
4931  set<Module *> builtInModules;
4932  set<Module *> userModules;
4933  set<string> builtInLibraries;
4934  set<string> userLibraries;
4935  set<string> externalLibraries;
4936 
4937  getLinkerInputs(dependencies, optimization,
4938  builtInModuleAndLibraryDependencies, userModuleAndLibraryDependencies, builtInCacheDependencies, userCacheDependencies,
4939  builtInModules, userModules, builtInLibraries, userLibraries, externalLibraries, frameworks);
4940 
4941  modules.insert(builtInModules.begin(), builtInModules.end());
4942  modules.insert(userModules.begin(), userModules.end());
4943  libraries.insert(builtInLibraries.begin(), builtInLibraries.end());
4944  libraries.insert(userLibraries.begin(), userLibraries.end());
4945  libraries.insert(externalLibraries.begin(), externalLibraries.end());
4946 }
4947 
4961 void VuoCompiler::getLinkerInputs(const set<string> &dependencies, Optimization optimization,
4962  set<string> &builtInModuleAndLibraryDependencies, set<string> &userModuleAndLibraryDependencies,
4963  map<string, set<string> > &builtInCacheDependencies, map<string, set<string> > &userCacheDependencies,
4964  set<Module *> &builtInModules, set<Module *> &userModules,
4965  set<string> &builtInLibraries, set<string> &userLibraries,
4966  set<string> &externalLibraries, set<string> &externalFrameworks)
4967 {
4968  bool shouldUseModuleCache = (optimization == Optimization_FastBuild || optimization == Optimization_FastBuildExistingCache);
4969  if (shouldUseModuleCache)
4970  useModuleCache(true, optimization == Optimization_FastBuildExistingCache);
4971 
4972  __block vector<string> librarySearchPaths;
4973  void (^envGetLibrarySearchPaths)(Environment *) = ^void (Environment *env) {
4974  vector<string> result = env->getLibrarySearchPaths();
4975  librarySearchPaths.insert(librarySearchPaths.end(), result.begin(), result.end());
4976  };
4977  applyToInstalledEnvironments(envGetLibrarySearchPaths);
4978 
4979  for (set<string>::iterator i = dependencies.begin(); i != dependencies.end(); ++i)
4980  {
4981  string dependency = *i;
4982 
4983  bool foundInCache = false;
4984  string moduleCachePath;
4985  bool isInBuiltInModuleCache = false;
4986  if (shouldUseModuleCache)
4987  foundInCache = findInModuleCache(dependency, moduleCachePath, isInBuiltInModuleCache);
4988 
4989  if (foundInCache)
4990  {
4991  if (isInBuiltInModuleCache)
4992  {
4993  builtInLibraries.insert(moduleCachePath);
4994  builtInCacheDependencies[moduleCachePath].insert(dependency);
4995  }
4996  else
4997  {
4998  userLibraries.insert(moduleCachePath);
4999  userCacheDependencies[moduleCachePath].insert(dependency);
5000  }
5001  }
5002  else
5003  {
5004  __block VuoCompilerModule *module = NULL;
5005  void (^envFindModule)(Environment *) = ^void (Environment *env) {
5006  VuoCompilerModule *result = env->findModule(dependency);
5007  if (result)
5008  module = result;
5009  };
5010  applyToAllEnvironments(envFindModule);
5011 
5012  if (module)
5013  {
5014  VuoCompilerNodeClass *nodeClass = dynamic_cast<VuoCompilerNodeClass *>(module);
5015  if (! (nodeClass && VuoCompilerSpecializedNodeClass::hasGenericPortTypes(nodeClass)) ) // Skip not-fully-specialized generic modules
5016  {
5017  string modulePath = module->getModulePath();
5018  if (! modulePath.empty() && dynamic_cast<VuoCompilerType *>(module))
5019  {
5020  if (module->isBuiltIn())
5021  builtInLibraries.insert(modulePath);
5022  else
5023  userLibraries.insert(modulePath);
5024  }
5025  else
5026  {
5027  if (module->isBuiltIn())
5028  builtInModules.insert(module->getModule());
5029  else
5030  userModules.insert(module->getModule());
5031  }
5032 
5033  if (module->isBuiltIn())
5034  builtInModuleAndLibraryDependencies.insert(dependency);
5035  else
5036  userModuleAndLibraryDependencies.insert(dependency);
5037  }
5038  }
5039  else
5040  {
5041  if (VuoStringUtilities::endsWith(dependency, ".framework"))
5042  externalFrameworks.insert(dependency);
5043  else
5044  {
5045  string dependencyPath = getLibraryPath(dependency, librarySearchPaths);
5046  if (! dependencyPath.empty())
5047  externalLibraries.insert(dependencyPath);
5048 
5049  // On macOS 11, libc.dylib and libobjc.dylib are not present,
5050  // but we can still link since Vuo.framework includes the TBDs.
5051  else if (dependency != "c"
5052  && dependency != "objc")
5053  VUserLog("Warning: Could not locate dependency '%s'.", dependency.c_str());
5054  }
5055  }
5056  }
5057  }
5058 }
5059 
5065 string VuoCompiler::getLibraryPath(const string &dependency, vector<string> librarySearchPaths)
5066 {
5067  if (dependency[0] == '/' && VuoFileUtilities::fileExists(dependency))
5068  return dependency;
5069 
5070  // Put the system library folder last in the list — prefer to use the libraries bundled in Vuo.framework.
5071  // Don't attempt to use OpenSSL from the system library folder, since macOS 10.15 prevents linking to it.
5072  if (dependency != "crypto"
5073  && dependency != "ssl")
5074  librarySearchPaths.push_back("/usr/lib");
5075 
5076  for (auto &path : librarySearchPaths)
5077  {
5078  vector<string> variations;
5079  variations.push_back(path + "/" + dependency);
5080  variations.push_back(path + "/lib" + dependency);
5081  variations.push_back(path + "/lib" + dependency + ".dylib");
5082  variations.push_back(path + "/lib" + dependency + ".a");
5083  for (auto &variation : variations)
5084  if (VuoFileUtilities::fileExists(variation))
5085  return variation;
5086  }
5087 
5088  return "";
5089 }
5090 
5098 void VuoCompiler::useModuleCache(bool shouldUseExistingBuiltInCaches, bool shouldUseExistingOtherCaches)
5099 {
5100  loadModulesIfNeeded();
5101  dispatch_group_wait(moduleSourceCompilersExist, DISPATCH_TIME_FOREVER); // Wait for any previous loadModulesIfNeeded() calls to complete.
5102 
5103  // Iterate through the environments in the order that the caches need to be built.
5104 
5105  dispatch_sync(environmentQueue, ^{
5106  set<string> dylibsForCachesOfInstalledModules;
5107  set<string> frameworksForCachesOfInstalledModules;
5108  unsigned long lastPrerequisiteModuleCacheRebuild = 0;
5109  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
5110  {
5111  set<string> dylibsForCacheOfGeneratedModules;
5112  set<string> frameworksForCacheOfGeneratedModules;
5113 
5114  for (int j = i->size() - 1; j >= 0; --j)
5115  {
5116  Environment *env = i->at(j);
5117  bool installed = (j == 0);
5118 
5119  set<string> cacheableModulesAndDependencies;
5120  set<string> dylibsNeededToLinkToThisCache;
5121  set<string> frameworksNeededToLinkToThisCache;
5122  env->getCacheableModulesAndDependencies(cacheableModulesAndDependencies,
5123  dylibsNeededToLinkToThisCache, frameworksNeededToLinkToThisCache);
5124 
5125  set<string> accumulatedDylibs;
5126  accumulatedDylibs.insert(dylibsNeededToLinkToThisCache.begin(), dylibsNeededToLinkToThisCache.end());
5127  accumulatedDylibs.insert(dylibsForCachesOfInstalledModules.begin(), dylibsForCachesOfInstalledModules.end());
5128  accumulatedDylibs.insert(dylibsForCacheOfGeneratedModules.begin(), dylibsForCacheOfGeneratedModules.end());
5129 
5130  set<string> accumulatedFrameworks;
5131  accumulatedFrameworks.insert(frameworksNeededToLinkToThisCache.begin(), frameworksNeededToLinkToThisCache.end());
5132  accumulatedFrameworks.insert(frameworksForCachesOfInstalledModules.begin(), frameworksForCachesOfInstalledModules.end());
5133  accumulatedFrameworks.insert(frameworksForCacheOfGeneratedModules.begin(), frameworksForCacheOfGeneratedModules.end());
5134 
5135  bool shouldUseExistingCache = (env->isBuiltIn() ? shouldUseExistingBuiltInCaches : shouldUseExistingOtherCaches);
5136  env->useModuleCache(shouldUseExistingCache, this, cacheableModulesAndDependencies,
5137  accumulatedDylibs, accumulatedFrameworks, lastPrerequisiteModuleCacheRebuild);
5138 
5139  string cacheDylib = env->getCurrentModuleCacheDylib();
5140  accumulatedDylibs.insert(cacheDylib);
5141  dylibsForCachesOfInstalledModules.insert(cacheDylib);
5142 
5143  lastPrerequisiteModuleCacheRebuild = max(lastPrerequisiteModuleCacheRebuild, env->getLastModuleCacheRebuild());
5144 
5145  if (installed)
5146  {
5147  dylibsForCachesOfInstalledModules.insert(dylibsNeededToLinkToThisCache.begin(), dylibsNeededToLinkToThisCache.end());
5148  frameworksForCachesOfInstalledModules.insert(frameworksNeededToLinkToThisCache.begin(), frameworksNeededToLinkToThisCache.end());
5149  }
5150  else
5151  {
5152  dylibsForCacheOfGeneratedModules.insert(dylibsNeededToLinkToThisCache.begin(), dylibsNeededToLinkToThisCache.end());
5153  frameworksForCacheOfGeneratedModules.insert(frameworksNeededToLinkToThisCache.begin(), frameworksNeededToLinkToThisCache.end());
5154  }
5155  }
5156  }
5157  });
5158 
5159  Environment::waitForModuleCachesToBuild();
5160 }
5161 
5170 bool VuoCompiler::findInModuleCache(const string &moduleOrDependency, string &cachePath, bool &isBuiltinCache)
5171 {
5172  __block bool found = false;
5173  __block string outPath;
5174  __block bool outBuiltin;
5175  dispatch_sync(environmentQueue, ^{
5176  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
5177  {
5178  bool builtin = (i == environments.begin());
5179 
5180  for (int j = i->size() - 1; j >= 0; --j)
5181  {
5182  Environment *env = i->at(j);
5183 
5184  string resultPath;
5185  bool resultFound = env->findInModuleCache(moduleOrDependency, resultPath);
5186  if (resultFound)
5187  {
5188  found = true;
5189  outPath = resultPath;
5190  outBuiltin = builtin;
5191  }
5192  }
5193  }
5194  });
5195 
5196  cachePath = outPath;
5197  isBuiltinCache = outBuiltin;
5198  return found;
5199 }
5200 
5209 {
5210  dispatch_group_async(moduleCacheBuilding, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
5211  useModuleCache(true, false);
5212  });
5213 }
5214 
5220 {
5221  return target;
5222 }
5223 
5229 {
5230  return getTargetArch(target);
5231 }
5232 
5246 void VuoCompiler::generateBuiltInModuleCaches(string vuoFrameworkPath, string target)
5247 {
5248  vuoFrameworkInProgressPath = vuoFrameworkPath;
5249 
5250  VuoCompiler compiler("", target);
5251  compiler.useModuleCache(false, true);
5252 }
5253 
5262 {
5263  unsigned long maxSeconds = 30 * 24 * 60 * 60; // 30 days
5264 
5265  set<VuoFileUtilities::File *> cacheDirs = VuoFileUtilities::findAllFilesInDirectory(VuoFileUtilities::getCachePath());
5266  for (set<VuoFileUtilities::File *>::iterator i = cacheDirs.begin(); i != cacheDirs.end(); ++i)
5267  {
5268  string path = (*i)->path();
5269 
5270  string file = (*i)->basename();
5271  if (file != "Builtin" && file != "System" && file != "User")
5272  {
5273  unsigned long fileSeconds = VuoFileUtilities::getSecondsSinceFileLastAccessed(path);
5274  if (fileSeconds > maxSeconds)
5276  }
5277 
5278  if (VuoStringUtilities::beginsWith(file, Environment::pidCacheDirPrefix))
5279  {
5280  string pidAsString = file.substr(Environment::pidCacheDirPrefix.length());
5281  int pid = atoi(pidAsString.c_str());
5282  if (kill(pid, 0) != 0) // no running process has this pid
5284  }
5285 
5286  delete *i;
5287  }
5288 }
5289 
5294 void VuoCompiler::setLoadAllModules(bool shouldLoadAllModules)
5295 {
5296  dispatch_sync(modulesToLoadQueue, ^{
5297  this->shouldLoadAllModules = shouldLoadAllModules;
5298  });
5299 }
5300 
5313 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)
5314 {
5315  VDebugLog("Linking '%s' (%s)…", outputPath.c_str(), getTargetArch(target).c_str());
5316  // https://stackoverflow.com/questions/11657529/how-to-generate-an-executable-from-an-llvmmodule
5317 
5318  bool ownsIssues = false;
5319  if (! issues)
5320  {
5321  issues = new VuoCompilerIssues;
5322  ownsIssues = true;
5323  }
5324 
5325  // Write all the modules with renamed symbols to a composite module file (since the linker can't operate on in-memory modules).
5326  string compositeModulePath = VuoFileUtilities::makeTmpFile("composite", "bc");
5327  dispatch_sync(llvmQueue, ^{
5328  double t0 = VuoLogGetTime();
5329 
5330  unique_ptr<Module> compositeModule(new Module("composite", *globalLLVMContext));
5331  Linker linker(*compositeModule);
5332  setTargetForModule(compositeModule.get(), target);
5333 
5334  for (auto i : modules)
5335  {
5336  unique_ptr<Module> upi = llvm::CloneModule(i);
5337  if (linker.linkInModule(std::move(upi)))
5338  {
5339  VuoCompilerIssue issue(VuoCompilerIssue::IssueType::Error, "linking composite module", "",
5340  "", "Failed to link in the module with ID '" + i->getModuleIdentifier() + "'");
5341  issues->append(issue);
5342  }
5343  }
5344 
5345  writeModuleToBitcode(compositeModule.get(), compositeModulePath);
5346 
5347  VDebugLog("\tLinkModules took %5.2fs", VuoLogGetTime() - t0);
5348  });
5349 
5350 
5351  // llvm-3.1/llvm/tools/clang/tools/driver/driver.cpp
5352 
5353  // Invoke clang as `clang++` so it includes the C++ standard libraries.
5354  string clangPath(getClangPath() + "++");
5355 
5356  vector<const char *> args;
5357  vector<char *> argsToFree;
5358  args.push_back(clangPath.c_str());
5359 
5360  {
5361  char *outputPathZ = strdup(("-o" + outputPath).c_str());
5362  args.push_back(outputPathZ);
5363  argsToFree.push_back(outputPathZ);
5364  }
5365 
5366  args.push_back(compositeModulePath.c_str());
5367 
5368  vector<string> coreDependencies = getCoreVuoDependencies();
5369  for (set<string>::const_iterator i = libraries.begin(); i != libraries.end(); ++i)
5370  {
5371  string library = *i;
5372 
5373  for (vector<string>::iterator j = coreDependencies.begin(); j != coreDependencies.end(); ++j)
5374  {
5375  string coreDependency = *j;
5376  if (VuoStringUtilities::endsWith(library, "lib" + coreDependency + ".a"))
5377  args.push_back("-force_load"); // Load all symbols of static core dependencies, not just those used in the objects.
5378  }
5379 
5380  // Use the pre-built native object file if it exists (faster than converting .bc to .o every build).
5381  if (VuoStringUtilities::endsWith(library, ".bc"))
5382  {
5383  string libraryObject = VuoStringUtilities::substrBefore(library, ".bc") + ".o";
5384  if (VuoFileUtilities::fileExists(libraryObject))
5385  library = libraryObject;
5386  }
5387 
5388  char *libraryZ = strdup(library.c_str());
5389  args.push_back(libraryZ);
5390  argsToFree.push_back(libraryZ);
5391  }
5392 
5393  // Add framework search paths
5394  vector<string> frameworkArguments;
5395 
5396  __block vector<string> frameworkSearchPaths;
5397  void (^envGetFrameworkSearchPaths)(Environment *) = ^void (Environment *env) {
5398  vector<string> result = env->getFrameworkSearchPaths();
5399  frameworkSearchPaths.insert(frameworkSearchPaths.end(), result.begin(), result.end());
5400  };
5401  applyToInstalledEnvironments(envGetFrameworkSearchPaths);
5402 
5403  for (vector<string>::const_iterator i = frameworkSearchPaths.begin(); i != frameworkSearchPaths.end(); ++i)
5404  {
5405  string a = "-F"+*i;
5406  // 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.
5407  frameworkArguments.push_back(a);
5408  char *frameworkArgument = strdup(a.c_str());
5409  args.push_back(frameworkArgument);
5410  argsToFree.push_back(frameworkArgument);
5411  }
5412 
5413  for (set<string>::const_iterator i = frameworks.begin(); i != frameworks.end(); ++i)
5414  {
5415  args.push_back("-framework");
5416 
5417  string frameworkName = *i;
5418  frameworkName = frameworkName.substr(0, frameworkName.length() - string(".framework").length());
5419  char *frameworkNameZ = strdup(frameworkName.c_str());
5420  args.push_back(frameworkNameZ);
5421  argsToFree.push_back(frameworkNameZ);
5422  }
5423 
5424  // When linking on a development workstation or Jenkins or an end-user system,
5425  // use the partial macOS SDK bundled in Vuo.framework, since it includes all the TBDs we need.
5426  string vuoFrameworkPath = getVuoFrameworkPath();
5427  string vuoFrameworkContainingFolder = vuoFrameworkPath + "/..";
5428  string frameworkMacOSSDKFolder = vuoFrameworkPath + "/SDKs/MacOSX10.11.sdk";
5429  if (!VuoFileUtilities::fileExists(frameworkMacOSSDKFolder))
5430  throw VuoException("Couldn't find the macOS SDK.");
5431 
5432  args.push_back("-Xlinker");
5433  args.push_back("-syslibroot");
5434  args.push_back("-Xlinker");
5435  char *frameworkMacOSSDKFolderZ = strdup(frameworkMacOSSDKFolder.c_str());
5436  args.push_back(frameworkMacOSSDKFolderZ);
5437  argsToFree.push_back(frameworkMacOSSDKFolderZ);
5438 
5439 
5440  // Linker option necessary for compatibility with our bundled version of ld64:
5441  args.push_back("-Xlinker");
5442  args.push_back("--no-demangle");
5443 
5444  if (isVerbose)
5445  args.push_back("-v");
5446 
5447  if (isDylib)
5448  args.push_back("-dynamiclib");
5449 
5450  args.push_back("-Xlinker");
5451  args.push_back("-headerpad_max_install_names");
5452 
5453  // Tell the built dylib/executable where to find Vuo.framework
5454  args.push_back("-rpath");
5455  string rPathArg = (rPath.empty() ? vuoFrameworkContainingFolder : rPath);
5456  args.push_back(rPathArg.c_str());
5457 
5458 #ifdef COVERAGE
5459  args.push_back("-rpath");
5460  args.push_back(LLVM_ROOT "/lib");
5461 #endif
5462 
5463  args.push_back("-target");
5464  args.push_back(target.c_str());
5465 
5466  args.push_back("-std=c++11");
5467  args.push_back("-stdlib=libc++");
5468  args.push_back("-mmacosx-version-min=10.10");
5469 
5470  // Allow clang to print meaningful error messages.
5471  auto diagnosticConsumer = new VuoCompilerDiagnosticConsumer(issues);
5472  clang::DiagnosticOptions *diagOptions = new clang::DiagnosticOptions();
5473  IntrusiveRefCntPtr<clang::DiagnosticIDs> DiagID(new clang::DiagnosticIDs());
5474  clang::DiagnosticsEngine Diags(DiagID, diagOptions, diagnosticConsumer);
5475 
5476  if (isVerbose)
5477  {
5478  ostringstream s;
5479  for (vector<const char *>::iterator i = args.begin(); i != args.end(); ++i)
5480  s << *i << " ";
5481  VUserLog("\t%s", s.str().c_str());
5482  }
5483 
5484  // Redirect linker output to a file, so we can feed it through VuoLog.
5485  string stdoutFile = VuoFileUtilities::makeTmpFile("vuo-linker-output", "txt");
5486  const StringRef stdoutPath(stdoutFile);
5487  const StringRef *redirects[] = {
5488  nullptr, // stdin
5489  &stdoutPath, // stdout
5490  &stdoutPath, // stderr
5491  };
5492 
5493  // ExecuteAndWait's args needs to be null-terminated.
5494  const char **argsz = (const char **)malloc(sizeof(char *) * args.size() + 1);
5495  for (int i = 0; i < args.size(); ++i)
5496  argsz[i] = args[i];
5497  argsz[args.size()] = nullptr;
5498 
5499  string errMsg;
5500  bool executionFailed;
5501  double t0 = VuoLogGetTime();
5502  int ret = llvm::sys::ExecuteAndWait(args[0], argsz, nullptr, redirects, 0, 0, &errMsg, &executionFailed);
5503 
5504  for (auto i : argsToFree)
5505  free(i);
5506 
5507  // Clean up composite module file.
5508  remove(compositeModulePath.c_str());
5509 
5510  if (!isDylib)
5511  // Ensure the linked binary has the execute permission set
5512  // (ld64-242 doesn't reliably set it, whereas ld64-133.3 did).
5513  // https://b33p.net/kosada/node/14152
5514  chmod(outputPath.c_str(), 0755);
5515 
5516  if (ret != 0)
5517  {
5518  string details;
5519  if (!errMsg.empty())
5520  {
5521  VUserLog("%s", errMsg.c_str());
5522  details += "\n" + errMsg + "\n";
5523  }
5524  string stdoutFileContents = VuoFileUtilities::readFileToString(stdoutFile);
5525  if (!stdoutFileContents.empty())
5526  {
5527  VUserLog("%s", stdoutFileContents.c_str());
5528  details += "\n" + stdoutFileContents + "\n";
5529  }
5530  VuoFileUtilities::deleteFile(stdoutFile);
5531 
5532  VuoCompilerIssue issue(VuoCompilerIssue::Error, "linking composition", outputPath, "", details);
5533  issues->append(issue);
5534  throw VuoCompilerException(issues, ownsIssues);
5535  }
5536 
5537  VuoFileUtilities::deleteFile(stdoutFile);
5538  VDebugLog("\tLinking took %5.2fs", VuoLogGetTime() - t0);
5539 
5540  if (shouldAdHocCodeSign)
5541  adHocCodeSign(outputPath);
5542 }
5543 
5547 void VuoCompiler::adHocCodeSign(string path)
5548 {
5549  double t0 = VuoLogGetTime();
5551  "/usr/bin/codesign",
5552  "--sign",
5553  "-", // "-" = ad-hoc
5554  path,
5555  }, {
5556 #if VUO_PRO
5557  "CODESIGN_ALLOCATE=" + getCodesignAllocatePath(),
5558 #endif
5559  });
5560 
5561  VDebugLog("\tAd-hoc code-signing took %5.2fs", VuoLogGetTime() - t0);
5562 }
5563 
5569 Module *VuoCompiler::readModuleFromC(string inputPath, const vector<string> &headerSearchPaths, const vector<string> &extraArgs, VuoCompilerIssues *issues)
5570 {
5571  // llvm-3.1/llvm/tools/clang/examples/clang-interpreter/main.cpp
5572 
5573  vector<const char *> args;
5574  args.push_back(inputPath.c_str());
5575  args.push_back("-DVUO_COMPILER");
5576  args.push_back("-fblocks");
5577 
5578  // Sync with /CMakeLists.txt's `commonFlags`.
5579  args.push_back("-Wall");
5580  args.push_back("-Wextra");
5581  args.push_back("-Wimplicit-fallthrough");
5582  args.push_back("-Wno-unused-parameter");
5583  args.push_back("-Wno-c++11-extensions");
5584  args.push_back("-Wno-sign-compare");
5585  args.push_back("-Werror=implicit");
5586 
5587  if (VuoStringUtilities::endsWith(inputPath, ".cc"))
5588  {
5589  args.push_back("-std=c++11");
5590  args.push_back("-stdlib=libc++");
5591  }
5592 
5593  for (vector<string>::const_iterator i = headerSearchPaths.begin(); i != headerSearchPaths.end(); ++i)
5594  {
5595  args.push_back("-I");
5596  args.push_back(i->c_str());
5597  }
5598 
5599  if (isVerbose)
5600  args.push_back("-v");
5601 
5602  for (vector<string>::const_iterator i = extraArgs.begin(); i != extraArgs.end(); ++i)
5603  args.push_back(i->c_str());
5604 
5605  auto diagnosticConsumer = new VuoCompilerDiagnosticConsumer(issues);
5606  clang::DiagnosticOptions * diagOptions = new clang::DiagnosticOptions();
5607  IntrusiveRefCntPtr<clang::DiagnosticIDs> DiagID(new clang::DiagnosticIDs());
5608  clang::DiagnosticsEngine *diags = new clang::DiagnosticsEngine(DiagID, diagOptions, diagnosticConsumer);
5609 
5610  shared_ptr<clang::CompilerInvocation> compilerInvocation(new clang::CompilerInvocation);
5611  clang::CompilerInvocation::CreateFromArgs(*compilerInvocation, &args[0], &args[0] + args.size(), *diags);
5612  compilerInvocation->TargetOpts->Triple = target;
5613 
5614  clang::CompilerInstance Clang;
5615  Clang.setInvocation(compilerInvocation);
5616 
5617  Clang.setDiagnostics(diags);
5618  if (!Clang.hasDiagnostics())
5619  return NULL;
5620 
5621  // See CompilerInvocation::GetResourcesPath -- though we're not calling it because we don't have MainAddr.
5622  string builtinHeaderSearchPath;
5623  string vuoFrameworkPath = getVuoFrameworkPath();
5624  if (vuoFrameworkPath.empty())
5625  {
5626  builtinHeaderSearchPath = getClangPath();
5627  if (VuoStringUtilities::endsWith(builtinHeaderSearchPath, "Helpers/clang"))
5628  builtinHeaderSearchPath = VuoStringUtilities::substrBefore(builtinHeaderSearchPath, "Helpers/clang");
5629  else if (VuoStringUtilities::endsWith(builtinHeaderSearchPath, "bin/clang"))
5630  builtinHeaderSearchPath = VuoStringUtilities::substrBefore(builtinHeaderSearchPath, "bin/clang");
5631  builtinHeaderSearchPath += "lib/clang/" CLANG_VERSION_STRING;
5632  }
5633  else
5634  builtinHeaderSearchPath = vuoFrameworkPath + "/Frameworks/llvm.framework/Versions/A/lib/clang/" CLANG_VERSION_STRING;
5635  Clang.getHeaderSearchOpts().ResourceDir = builtinHeaderSearchPath;
5636 
5637 // OwningPtr<clang::CodeGenAction> Act(new clang::EmitLLVMOnlyAction()); // @@@ return value of takeModule() is destroyed at the end of this function
5638  clang::CodeGenAction *Act = new clang::EmitLLVMOnlyAction();
5639  if (!Clang.ExecuteAction(*Act))
5640  return NULL;
5641 
5642  unique_ptr<Module> module = Act->takeModule();
5643  if (!module)
5644  VUserLog("Error compiling %s: module is null.", inputPath.c_str());
5645  return module.release();
5646 }
5647 
5653 Module *VuoCompiler::readModuleFromBitcode(string inputPath, string arch)
5654 {
5655  string dir, file, ext;
5656  VuoFileUtilities::splitPath(inputPath, dir, file, ext);
5657  VuoFileUtilities::File inputFile(dir, file + "." + ext);
5658  return readModuleFromBitcode(&inputFile, arch);
5659 }
5660 
5668 Module *VuoCompiler::readModuleFromBitcode(VuoFileUtilities::File *inputFile, string arch)
5669 {
5670  size_t inputDataBytes;
5671  char *inputData = inputFile->getContentsAsRawData(inputDataBytes);
5672 
5673  string error;
5674  set<string> availableArchs;
5675  VuoLog_status("Loading module \"%s\" (%s)", inputFile->getRelativePath().c_str(), arch.c_str());
5676  Module *module = readModuleFromBitcodeData(inputData, inputDataBytes, arch, availableArchs, error);
5677  VuoLog_status(NULL);
5678  if (! module)
5679  VUserLog("Error: Couldn't parse module '%s' (%s): %s.", inputFile->getRelativePath().c_str(), arch.c_str(), error.c_str());
5680 
5681  free(inputData);
5682 
5683  return module;
5684 }
5685 
5691 Module *VuoCompiler::readModuleFromBitcodeData(char *inputData, size_t inputDataBytes, string arch,
5692  set<string> &availableArchs, string &error)
5693 {
5694  if (inputDataBytes < sizeof(unsigned int))
5695  return nullptr;
5696 
5697  __block Module *module = nullptr;
5698  dispatch_sync(llvmQueue, ^{
5699  StringRef inputDataAsStringRef(inputData, inputDataBytes);
5700  auto mb = MemoryBuffer::getMemBuffer(inputDataAsStringRef, "", false);
5701  if (!mb)
5702  {
5703  error = "Couldn't create MemoryBuffer";
5704  return;
5705  }
5706 
5707  MemoryBufferRef bitcodeBuffer;
5708  string moduleArch;
5709  unsigned int fileID = *(unsigned int *)inputData;
5710  if (fileID == 0x0b17c0de)
5711  // This is a single-architecture LLVM bitcode `.bc` file, so read the entire file.
5712  bitcodeBuffer = mb.get()->getMemBufferRef();
5713 
5714  else if (fileID == 0xdec04342)
5715  {
5716  // This is a single-architecture raw bitcode file, presumably generated by Vuo 2.2.1 or earlier.
5717  bitcodeBuffer = mb.get()->getMemBufferRef();
5718  moduleArch = "x86_64";
5719  availableArchs.insert(moduleArch);
5720  }
5721 
5722  else if (fileID == 0xbebafeca)
5723  {
5724  if (arch.empty())
5725  {
5726  error = "It's a Mach-O universal binary, but this compiler instance's LLVM target isn't set";
5727  return;
5728  }
5729 
5730  // This is a Mach-O wrapper around multiple LLVM bitcode files;
5731  // parse the Mach-O header to extract just a single architecture.
5732  auto binary = llvm::object::MachOUniversalBinary::create(mb.get()->getMemBufferRef());
5733  if (!binary)
5734  {
5735  error = "Couldn't read Mach-O universal binary:";
5736  handleAllErrors(binary.takeError(), [&error](const ErrorInfoBase &ei) {
5737  error += " " + ei.message();
5738  });
5739  return;
5740  }
5741 
5742  for (auto &o : binary.get()->objects())
5743  {
5744  if (o.getArchFlagName() == arch)
5745  bitcodeBuffer = MemoryBufferRef(mb.get()->getMemBufferRef().getBuffer().slice(o.getOffset(), o.getOffset() + o.getSize()), "");
5746 
5747  availableArchs.insert(o.getArchFlagName());
5748  }
5749 
5750  if (!bitcodeBuffer.getBufferSize())
5751  {
5752  error = "The Mach-O universal binary doesn't have an \"" + arch + "\" slice";
5753  return;
5754  }
5755  }
5756 
5757  auto wrappedModule = llvm::parseBitcodeFile(bitcodeBuffer, *globalLLVMContext);
5758  if (!wrappedModule)
5759  {
5760  error = "Couldn't parse bitcode file:";
5761  handleAllErrors(wrappedModule.takeError(), [&error](const ErrorInfoBase &ei) {
5762  error += " " + ei.message();
5763  });
5764  return;
5765  }
5766 
5767  module = wrappedModule.get().release();
5768 
5769  if (moduleArch.empty())
5770  moduleArch = getTargetArch(module->getTargetTriple());
5771 
5772  if (availableArchs.empty())
5773  availableArchs.insert(moduleArch);
5774 
5775  if (moduleArch != arch)
5776  {
5777  error = "The module's CPU architecture \"" + moduleArch + "\" doesn't match the compiler's CPU architecture \"" + arch + "\"";
5778  delete module;
5779  module = nullptr;
5780  return;
5781  }
5782  });
5783  return module;
5784 }
5785 
5793 bool VuoCompiler::writeModuleToBitcode(Module *module, string outputPath)
5794 {
5795  string str;
5796  raw_string_ostream verifyOut(str);
5797  if (llvm::verifyModule(*module, &verifyOut))
5798  {
5799  VUserLog("Error: Module verification failed:\n%s", verifyOut.str().c_str());
5800  return true;
5801  }
5802 
5803  // Ensure the module gets output in the bitcode wrapper format instead of raw bitcode.
5804  if (module->getTargetTriple().empty())
5805  setTargetForModule(module, getProcessTarget());
5806 
5807  std::error_code err;
5808  raw_fd_ostream out(outputPath.c_str(), err, sys::fs::F_None);
5809  if (err)
5810  {
5811  VUserLog("Error: Couldn't open file '%s' for writing: %s", outputPath.c_str(), err.message().c_str());
5812  return true;
5813  }
5814  llvm::WriteBitcodeToFile(module, out);
5815 
5816  return false;
5817 }
5818 
5825 void VuoCompiler::setTargetForModule(Module *module, string targetTriple)
5826 {
5827  // Replace the OS version in targetTriple to avoid warnings about linking modules of different target triples.
5828  llvm::Triple triple(targetTriple);
5829  if (triple.isMacOSX())
5830  triple.setOSName("macosx10.10.0");
5831 
5832  module->setTargetTriple(triple.str());
5833 
5834  string error;
5835  auto target = TargetRegistry::lookupTarget(module->getTargetTriple(), error);
5836  if (!target)
5837  {
5838  VUserLog("Error: Couldn't look up target: %s", error.c_str());
5839  return;
5840  }
5841 
5842  auto targetMachine = target->createTargetMachine(module->getTargetTriple(), "", "", TargetOptions(), Optional<Reloc::Model>());
5843  if (!targetMachine)
5844  {
5845  VUserLog("Error: Couldn't create targetMachine.");
5846  return;
5847  }
5848 
5849  module->setDataLayout(targetMachine->createDataLayout());
5850 
5851  delete targetMachine;
5852 }
5853 
5858 string VuoCompiler::getTargetArch(string target)
5859 {
5860  auto hyphen = target.find('-');
5861  if (hyphen == string::npos)
5862  return "";
5863 
5864  return target.substr(0, hyphen);
5865 }
5866 
5870 string VuoCompiler::getProcessTarget(void)
5871 {
5872  // llvm::sys::getProcessTriple() returns `LLVM_HOST_TRIPLE`,
5873  // which is always `x86_64-*` since we built LLVM on x86_64.
5874  // Instead, use llvm::sys::getDefaultTargetTriple()
5875  // which _actually_ returns the current process's target.
5876  llvm::Triple triple(llvm::sys::getDefaultTargetTriple());
5877 
5878  // Replace darwin with macosx, e.g. x86_64-apple-darwin18.7.0 -> x86_64-apple-macosx10.14.6
5879  if (triple.isMacOSX())
5880  {
5881  unsigned int major, minor, micro;
5882  if (triple.getMacOSXVersion(major, minor, micro))
5883  {
5884  ostringstream osVersion;
5885  osVersion << "macosx" << major << "." << minor << "." << micro;
5886  triple.setOSName(osVersion.str());
5887  }
5888  }
5889 
5890  return triple.str();
5891 }
5892 
5900 {
5901  Module *llvmModule = module->getModule();
5902 
5903  // In C++ the return value of a cast may not be the same pointer as the cast arg.
5904  // Because of this, VuoModule::getPseudoBase() returns a different value than VuoNodeClass::getBase().
5905  // Calling delete on VuoModule::getPseudoBase() gives a malloc error: "pointer being freed was not allocated".
5906  // So call it on VuoNodeClass::getBase(). Same idea for VuoType.
5907 
5908  VuoNodeClass *baseNodeClass = NULL;
5909  VuoType *baseType = NULL;
5910  VuoModule *baseModule = NULL;
5911  VuoCompilerNodeClass *nodeClass = dynamic_cast<VuoCompilerNodeClass *>(module);
5912  if (nodeClass)
5913  {
5914  baseNodeClass = dynamic_cast<VuoNodeClass *>(nodeClass->getBase());
5915 
5917  if (dynamic_cast<VuoCompilerSpecializedNodeClass *>(module))
5918  module = NULL;
5919  }
5920  else
5921  {
5922  VuoCompilerType *type = dynamic_cast<VuoCompilerType *>(module);
5923  if (type)
5924  baseType = dynamic_cast<VuoType *>(type->getBase());
5925  else
5926  baseModule = module->getPseudoBase();
5927  }
5928 
5929  delete module;
5930  delete baseNodeClass;
5931  delete baseType;
5932  delete baseModule;
5933  destroyLlvmModule(llvmModule);
5934 }
5935 
5943 {
5944  dispatch_sync(llvmQueue, ^{
5945  delete module;
5946  });
5947 }
5948 
5960 VuoNode * VuoCompiler::createNode(VuoCompilerNodeClass *nodeClass, string title, double x, double y)
5961 {
5963  if (nodeClassForNode)
5964  return nodeClassForNode->newNode(title, x, y);
5965 
5966  return nullptr;
5967 }
5968 
5975 VuoNode * VuoCompiler::createNode(VuoCompilerNodeClass *nodeClass, VuoNode *nodeToCopyMetadataFrom)
5976 {
5978  if (nodeClassForNode)
5979  return nodeClassForNode->newNode(nodeToCopyMetadataFrom);
5980 
5981  return nullptr;
5982 }
5983 
5987 VuoNode * VuoCompiler::createPublishedInputNode(vector<VuoPublishedPort *> publishedInputPorts)
5988 {
5989  string nodeClassName = VuoCompilerPublishedInputNodeClass::buildNodeClassName(publishedInputPorts);
5990  return createPublishedNode(nodeClassName, publishedInputPorts);
5991 }
5992 
5996 VuoNode * VuoCompiler::createPublishedOutputNode(vector<VuoPublishedPort *> publishedOutputPorts)
5997 {
5998  string nodeClassName = VuoCompilerPublishedOutputNodeClass::buildNodeClassName(publishedOutputPorts);
5999  return createPublishedNode(nodeClassName, publishedOutputPorts);
6000 }
6001 
6005 VuoNode * VuoCompiler::createPublishedNode(const string &nodeClassName, const vector<VuoPublishedPort *> &publishedPorts)
6006 {
6007  VuoCompilerNodeClass *nodeClass = getNodeClass(nodeClassName);
6008  VuoNode *node = createNode(nodeClass);
6009 
6010  // Set the generic port types on the node to match those on the published ports set by VuoCompilerComposition::updateGenericPortTypes().
6011  VuoCompilerPublishedInputNodeClass *inputNodeClass = dynamic_cast<VuoCompilerPublishedInputNodeClass *>(nodeClass);
6012  VuoCompilerPublishedOutputNodeClass *outputNodeClass = dynamic_cast<VuoCompilerPublishedOutputNodeClass *>(nodeClass);
6013  for (size_t i = 0; i < publishedPorts.size(); ++i)
6014  {
6015  VuoType *publishedPortType = static_cast<VuoCompilerPort *>(publishedPorts[i]->getCompiler())->getDataVuoType();
6016  if (! dynamic_cast<VuoGenericType *>(publishedPortType))
6017  continue;
6018 
6019  set<VuoPort *> nodePorts;
6020  if (inputNodeClass)
6021  {
6022  VuoPort *inputPort = node->getInputPorts().at( inputNodeClass->getInputPortIndexForPublishedInputPort(i) );
6023  nodePorts.insert(inputPort);
6024 
6025  VuoPort *outputPort = node->getOutputPorts().at( inputNodeClass->getOutputPortIndexForPublishedInputPort(i) );
6026  nodePorts.insert(outputPort);
6027  }
6028  else
6029  {
6030  VuoPort *inputPort = node->getInputPorts().at( outputNodeClass->getInputPortIndexForPublishedOutputPort(i) );
6031  nodePorts.insert(inputPort);
6032  }
6033 
6034  for (VuoPort *port : nodePorts)
6035  {
6036  VuoCompilerPort *compilerPort = static_cast<VuoCompilerPort *>(port->getCompiler());
6037  compilerPort->setDataVuoType(publishedPortType);
6038  }
6039  }
6040 
6041  reifyGenericPortTypes(node);
6042 
6043  return node;
6044 }
6045 
6051 {
6052  dispatch_sync(environmentQueue, ^{
6053  if (environments.size() >= 5)
6054  environments.at(3).at(0)->addExpatriateSourceFile(sourcePath);
6055  });
6056 }
6057 
6063 {
6064  dispatch_sync(environmentQueue, ^{
6065  if (environments.size() >= 5)
6066  {
6067  environments.at(3).at(0)->removeExpatriateSourceFile(sourcePath);
6068 
6069  set<string> sourcesRemoved;
6070  sourcesRemoved.insert(getModuleKeyForPath(sourcePath));
6071  loadModulesAndSources(set<string>(), set<string>(), set<string>(), set<string>(), set<string>(), sourcesRemoved,
6072  false, false, environments.at(3).at(0), nullptr, nullptr, "");
6073  }
6074  });
6075 }
6076 
6093 void VuoCompiler::overrideInstalledNodeClass(const string &sourcePath, const string &sourceCode)
6094 {
6095  string sourcePathCopy = sourcePath;
6096  string sourceCodeCopy = sourceCode;
6097 
6098  dispatch_async(environmentQueue, ^{
6099  string nodeClassName = getModuleKeyForPath(sourcePathCopy);
6100  ModuleInfo *sourceInfo = NULL;
6101 
6102  for (const vector<Environment *> &envs : environments)
6103  {
6104  for (Environment *env : envs)
6105  {
6106  ModuleInfo *potentialSourceInfo = env->listSourceFile(nodeClassName);
6107  if (potentialSourceInfo && VuoFileUtilities::arePathsEqual(potentialSourceInfo->getFile()->path(), sourcePathCopy))
6108  {
6109  sourceInfo = potentialSourceInfo;
6110  break;
6111  }
6112  }
6113  }
6114 
6115  if (! sourceInfo)
6116  return;
6117 
6118  bool shouldRecompileSourcesIfUnchanged;
6119  if (! sourceCodeCopy.empty())
6120  {
6121  sourceInfo->setSourceCode(sourceCodeCopy);
6122  sourceInfo->setSourceCodeOverridden(true);
6123 
6124  shouldRecompileSourcesIfUnchanged = false;
6125  }
6126  else
6127  {
6128  sourceInfo->revertSourceCode();
6129  sourceInfo->setSourceCodeOverridden(false);
6130 
6131  shouldRecompileSourcesIfUnchanged = true;
6132  }
6133  sourceInfo->setAttempted(false);
6134  sourceInfo->setLastModifiedToNow();
6135 
6136  set<string> sourcesModified;
6137  sourcesModified.insert(nodeClassName);
6138 
6139  loadModulesAndSources(set<string>(), set<string>(), set<string>(), set<string>(), sourcesModified, set<string>(),
6140  false, shouldRecompileSourcesIfUnchanged, nullptr, nullptr, nullptr, "");
6141  });
6142 }
6143 
6153 void VuoCompiler::revertOverriddenNodeClass(const string &sourcePath)
6154 {
6155  overrideInstalledNodeClass(sourcePath, "");
6156 }
6157 
6170 VuoCompilerNodeClass * VuoCompiler::getNodeClass(const string &nodeClassName)
6171 {
6172  // For performance, don't bother searching if it's obviously not a node class.
6173 
6174  if (VuoStringUtilities::endsWith(nodeClassName, ".framework"))
6175  return NULL;
6176 
6177  // Attempt to load the node class (if it hasn't already been loaded).
6178 
6179  set<string> nodeClassNameSet;
6180  nodeClassNameSet.insert(nodeClassName);
6181  loadModulesIfNeeded(nodeClassNameSet);
6182 
6183  // If the node class has been loaded, return it.
6184 
6185  __block VuoCompilerNodeClass *nodeClass = NULL;
6186  void (^envGetNodeClass)(Environment *) = ^void (Environment *env) {
6187  VuoCompilerNodeClass *result = env->getNodeClass(nodeClassName);
6188  if (result)
6189  nodeClass = result;
6190  };
6191  applyToAllEnvironments(envGetNodeClass);
6192  return nodeClass;
6193 }
6194 
6200 map<string, VuoCompilerNodeClass *> VuoCompiler::getNodeClasses()
6201 {
6202  loadModulesIfNeeded();
6203 
6204  __block map<string, VuoCompilerNodeClass *> nodeClasses;
6205  void (^envGetNodeClasses)(Environment *) = ^void (Environment *env) {
6206  map<string, VuoCompilerNodeClass *> result = env->getNodeClasses();
6207  nodeClasses.insert(result.begin(), result.end());
6208  };
6209  applyToInstalledEnvironments(envGetNodeClasses);
6210  return nodeClasses;
6211 }
6212 
6218 VuoCompilerType * VuoCompiler::getType(const string &typeName)
6219 {
6220  set<string> typeNameSet;
6221  typeNameSet.insert(typeName);
6222  loadModulesIfNeeded(typeNameSet);
6223 
6224  __block VuoCompilerType *type = NULL;
6225  void (^envGetType)(Environment *) = ^void (Environment *env) {
6226  VuoCompilerType *result = env->getType(typeName);
6227  if (result)
6228  type = result;
6229  };
6230  applyToInstalledEnvironments(envGetType);
6231 
6232  if (! type && VuoGenericType::isGenericTypeName(typeName))
6233  {
6234  VuoCompilerType * (^compilerGetType)(string) = ^VuoCompilerType * (string moduleKey) {
6235  return getType(moduleKey);
6236  };
6237 
6238  VuoGenericType *genericType = new VuoGenericType(typeName, vector<string>());
6239  type = VuoCompilerGenericType::newGenericType(genericType, compilerGetType);
6240  }
6241 
6242  return type;
6243 }
6244 
6250 map<string, VuoCompilerType *> VuoCompiler::getTypes()
6251 {
6252  loadModulesIfNeeded();
6253 
6254  __block map<string, VuoCompilerType *> types;
6255  void (^envGetTypes)(Environment *) = ^void (Environment *env) {
6256  map<string, VuoCompilerType *> result = env->getTypes();
6257  types.insert(result.begin(), result.end());
6258  };
6259  applyToInstalledEnvironments(envGetTypes);
6260  return types;
6261 }
6262 
6268 VuoCompilerModule *VuoCompiler::getLibraryModule(const string &libraryModuleName)
6269 {
6270  set<string> libraryNameSet;
6271  libraryNameSet.insert(libraryModuleName);
6272  loadModulesIfNeeded(libraryNameSet);
6273 
6274  __block VuoCompilerModule *module = nullptr;
6275  void (^envGetLibraryModule)(Environment *) = ^void (Environment *env) {
6276  VuoCompilerModule *result = env->getLibraryModule(libraryModuleName);
6277  if (result)
6278  module = result;
6279  };
6280  applyToInstalledEnvironments(envGetLibraryModule);
6281 
6282  return module;
6283 }
6284 
6290 map<string, VuoCompilerModule *> VuoCompiler::getLibraryModules()
6291 {
6292  loadModulesIfNeeded();
6293 
6294  __block map<string, VuoCompilerModule *> libraryModules;
6295  void (^envGetLibraryModules)(Environment *) = ^void (Environment *env) {
6296  map<string, VuoCompilerModule *> result = env->getLibraryModules();
6297  libraryModules.insert(result.begin(), result.end());
6298  };
6299  applyToInstalledEnvironments(envGetLibraryModules);
6300  return libraryModules;
6301 }
6302 
6308 map<string, VuoNodeSet *> VuoCompiler::getNodeSets()
6309 {
6310  loadModulesIfNeeded();
6311 
6312  __block map<string, VuoNodeSet *> nodeSets;
6313  void (^envGetNodeSets)(Environment *) = ^void (Environment *env) {
6314  map<string, VuoNodeSet *> result = env->getNodeSets();
6315  nodeSets.insert(result.begin(), result.end());
6316  };
6317  applyToInstalledEnvironments(envGetNodeSets);
6318  return nodeSets;
6319 }
6320 
6327 {
6328  loadModulesIfNeeded();
6329 
6330  __block VuoNodeSet *nodeSet = NULL;
6331  void (^envFindNodeSet)(Environment *) = ^void (Environment *env) {
6332  VuoNodeSet *result = env->findNodeSet(name);
6333  if (result)
6334  nodeSet = result;
6335  };
6336  applyToInstalledEnvironments(envFindNodeSet);
6337  return nodeSet;
6338 }
6339 
6343 VuoCompilerModule * VuoCompiler::getModule(const string &moduleKey)
6344 {
6345  __block VuoCompilerModule *module = NULL;
6346  void (^envFindModule)(Environment *) = ^void (Environment *env) {
6347  VuoCompilerModule *result = env->findModule(moduleKey);
6348  if (result)
6349  module = result;
6350  };
6351  applyToAllEnvironments(envFindModule);
6352  return module;
6353 }
6354 
6366 void VuoCompiler::listNodeClasses(const string &format)
6367 {
6368  map<string, VuoCompilerNodeClass *> nodeClasses = getNodeClasses();
6369  for (map<string, VuoCompilerNodeClass *>::const_iterator i = nodeClasses.begin(); i != nodeClasses.end(); ++i)
6370  {
6371  VuoCompilerNodeClass *nodeClass = i->second;
6372  if (format == "")
6373  {
6374  printf("%s\n", nodeClass->getBase()->getClassName().c_str());
6375  }
6376  else if (format == "path")
6377  {
6378  // TODO: If "path", prints the absolute path of each node class, one per line.
6379  }
6380  else if (format == "dot")
6381  {
6382  VuoCompilerNode *node = nodeClass->newNode()->getCompiler();
6383 
6384  printf("%s\n", nodeClass->getDoxygenDocumentation().c_str());
6385  printf("%s\n\n", node->getGraphvizDeclaration().c_str());
6386 
6387  delete node;
6388  }
6389  }
6390 }
6391 
6398 vector<string> VuoCompiler::getCoreVuoDependencies(void)
6399 {
6400  vector<string> dependencies;
6401 
6402  dependencies.push_back("VuoHeap");
6403  dependencies.push_back("VuoApp");
6404 
6405  dependencies.push_back("zmq");
6406  dependencies.push_back("json-c");
6407  dependencies.push_back("objc");
6408  dependencies.push_back("c");
6409  dependencies.push_back("AppKit.framework");
6410 
6411 #ifdef COVERAGE
6412  dependencies.push_back(LLVM_ROOT "/lib/libprofile_rt.dylib");
6413 #endif
6414  return dependencies;
6415 }
6416 
6420 string VuoCompiler::getRuntimeMainDependency(void)
6421 {
6422  return "VuoRuntimeMain.o";
6423 }
6424 
6432 string VuoCompiler::getRuntimeDependency(void)
6433 {
6434  return "VuoRuntime.o";
6435 }
6436 
6444 {
6445  if (! vuoFrameworkInProgressPath.empty())
6446  return vuoFrameworkInProgressPath;
6447 
6449 }
6450 
6454 string VuoCompiler::getClangPath(void)
6455 {
6456  return clangPath;
6457 }
6458 
6463 {
6464  // Start with the file name without extension.
6465  string dir, moduleKey, ext;
6466  VuoFileUtilities::splitPath(path, dir, moduleKey, ext);
6467 
6469  {
6470  vector<string> nodeClassNameParts = VuoStringUtilities::split(moduleKey, '.');
6471 
6472  // Remove repeated file extensions.
6473  while (nodeClassNameParts.size() > 1 && nodeClassNameParts.back() == ext)
6474  nodeClassNameParts.pop_back();
6475 
6476  // Convert each part to lowerCamelCase.
6477  for (string &part : nodeClassNameParts)
6478  part = VuoStringUtilities::convertToCamelCase(part, false, true, false);
6479 
6480  // If the node class name has only one part, add a prefix.
6481  if (nodeClassNameParts.size() == 1)
6482  nodeClassNameParts.insert(nodeClassNameParts.begin(), "isf");
6483 
6484  moduleKey = VuoStringUtilities::join(nodeClassNameParts, '.');
6485  }
6486 
6487  return moduleKey;
6488 }
6489 
6495 {
6496  __block bool isLocal = false;
6497 
6498  dispatch_sync(environmentQueue, ^{
6499  if (environments.size() >= 5)
6500  isLocal = environments.at(3).at(0)->findModule(moduleKey);
6501  });
6502 
6503  return isLocal;
6504 }
6505 
6513 {
6514  return lastCompositionBaseDir.empty() ? "" : lastCompositionBaseDir + "/Modules";
6515 }
6516 
6528 {
6529  return lastCompositionBaseDir;
6530 }
6531 
6535 void VuoCompiler::addModuleSearchPath(string path)
6536 {
6537  dispatch_sync(environmentQueue, ^{
6538  environments.back().at(0)->addModuleSearchPath(path);
6539  environments.back().at(0)->addLibrarySearchPath(path);
6540  });
6541 }
6542 
6546 void VuoCompiler::addHeaderSearchPath(const string &path)
6547 {
6548  dispatch_sync(environmentQueue, ^{
6549  environments.back().at(0)->addHeaderSearchPath(path);
6550  });
6551 }
6552 
6556 void VuoCompiler::addLibrarySearchPath(const string &path)
6557 {
6558  dispatch_sync(environmentQueue, ^{
6559  environments.back().at(0)->addLibrarySearchPath(path);
6560  });
6561 }
6562 
6566 void VuoCompiler::addFrameworkSearchPath(const string &path)
6567 {
6568  dispatch_sync(environmentQueue, ^{
6569  environments.back().at(0)->addFrameworkSearchPath(path);
6570  });
6571 }
6572 
6576 void VuoCompiler::setTelemetry(const string &telemetry)
6577 {
6578  this->telemetry = telemetry;
6579 }
6580 
6584 void VuoCompiler::setVerbose(bool isVerbose)
6585 {
6586  this->isVerbose = isVerbose;
6587 }
6588 
6594 {
6595 #if VUO_PRO
6596  if (VuoPro::getProAccess())
6597  {
6598  _shouldShowSplashWindow = false;
6599  return;
6600  }
6601 #endif
6602 
6603  _shouldShowSplashWindow = potentiallyShow;
6604 }
6605 
6610 {
6611  return _shouldShowSplashWindow;
6612 }
6613 
6617 void VuoCompiler::setClangPath(const string &clangPath)
6618 {
6619  this->clangPath = clangPath;
6620 }
6621 
6626 {
6627  string vuoFrameworkPath = getVuoFrameworkPath();
6628  return (vuoFrameworkPath.empty() ?
6629  VUO_BUILD_DIR "/bin/VuoCompositionLoader.app/Contents/MacOS/VuoCompositionLoader" :
6630  vuoFrameworkPath + "/Helpers/VuoCompositionLoader.app/Contents/MacOS/VuoCompositionLoader");
6631 }
6632 
6636 string VuoCompiler::getCompositionStubPath(void)
6637 {
6638  string vuoFrameworkPath = getVuoFrameworkPath();
6639  return (vuoFrameworkPath.empty() ?
6640  VUO_BUILD_DIR "/lib/libVuoCompositionStub.dylib" :
6641  vuoFrameworkPath + "/Modules/libVuoCompositionStub.dylib");
6642 }
6643 
6647 string VuoCompiler::getCachePathForComposition(const string compositionDir)
6648 {
6649  string modifierLetterColon("꞉");
6650  string cachedModulesName = compositionDir;
6651  VuoStringUtilities::replaceAll(cachedModulesName, "/", modifierLetterColon);
6652  return VuoFileUtilities::getCachePath() + "/" + cachedModulesName;
6653 }
6654 
6659 {
6660  __block vector<string> moduleSearchPaths;
6661  void (^envGetModuleSearchPaths)(Environment *) = ^void (Environment *env) {
6662  vector<string> result = env->getModuleSearchPaths();
6663  moduleSearchPaths.insert(moduleSearchPaths.end(), result.begin(), result.end());
6664  };
6665  applyToInstalledEnvironments(envGetModuleSearchPaths);
6666 
6667  __block vector<string> headerSearchPaths;
6668  void (^envGetHeaderSearchPaths)(Environment *) = ^void (Environment *env) {
6669  vector<string> result = env->getHeaderSearchPaths();
6670  headerSearchPaths.insert(headerSearchPaths.end(), result.begin(), result.end());
6671  };
6672  applyToInstalledEnvironments(envGetHeaderSearchPaths);
6673 
6674  __block vector<string> librarySearchPaths;
6675  void (^envGetLibrarySearchPaths)(Environment *) = ^void (Environment *env) {
6676  vector<string> result = env->getLibrarySearchPaths();
6677  librarySearchPaths.insert(librarySearchPaths.end(), result.begin(), result.end());
6678  };
6679  applyToInstalledEnvironments(envGetLibrarySearchPaths);
6680 
6681  __block vector<string> frameworkSearchPaths;
6682  void (^envGetFrameworkSearchPaths)(Environment *) = ^void (Environment *env) {
6683  vector<string> result = env->getFrameworkSearchPaths();
6684  frameworkSearchPaths.insert(frameworkSearchPaths.end(), result.begin(), result.end());
6685  };
6686  applyToInstalledEnvironments(envGetFrameworkSearchPaths);
6687 
6688  fprintf(stderr, "Module (node class, type, library) search paths:\n");
6689  for (vector<string>::iterator i = moduleSearchPaths.begin(); i != moduleSearchPaths.end(); ++i)
6690  fprintf(stderr, " %s\n", (*i).c_str());
6691  fprintf(stderr, "Header search paths:\n");
6692  for (vector<string>::iterator i = headerSearchPaths.begin(); i != headerSearchPaths.end(); ++i)
6693  fprintf(stderr, " %s\n", (*i).c_str());
6694  fprintf(stderr, "Other library search paths:\n");
6695  for (vector<string>::iterator i = librarySearchPaths.begin(); i != librarySearchPaths.end(); ++i)
6696  fprintf(stderr, " %s\n", (*i).c_str());
6697  fprintf(stderr, "Other framework search paths:\n");
6698  for (vector<string>::iterator i = frameworkSearchPaths.begin(); i != frameworkSearchPaths.end(); ++i)
6699  fprintf(stderr, " %s\n", (*i).c_str());
6700  fprintf(stderr, "Framework path:\n");
6701  if (! getVuoFrameworkPath().empty())
6702  fprintf(stderr, " %s\n", getVuoFrameworkPath().c_str());
6703  fprintf(stderr, "Clang path:\n");
6704  if (! getClangPath().empty())
6705  fprintf(stderr, " %s\n", getClangPath().c_str());
6706 }
6707 
6717 {
6718  try
6719  {
6720  VuoCompiler compiler(compositionFilePath);
6721  string directory, file, extension;
6722  VuoFileUtilities::splitPath(compositionFilePath, directory, file, extension);
6723  string compiledCompositionPath = VuoFileUtilities::makeTmpFile(file, "bc");
6724  string linkedCompositionPath = VuoFileUtilities::makeTmpFile(file + "-linked", "");
6725  compiler.compileComposition(compositionFilePath, compiledCompositionPath, true, issues);
6726  compiler.linkCompositionToCreateExecutable(compiledCompositionPath, linkedCompositionPath, Optimization_FastBuild);
6727  remove(compiledCompositionPath.c_str());
6728  return VuoRunner::newSeparateProcessRunnerFromExecutable(linkedCompositionPath, directory, false, true);
6729  }
6730  catch (VuoCompilerException &e)
6731  {
6732  if (issues != e.getIssues())
6733  issues->append(e.getIssues());
6734  return NULL;
6735  }
6736 }
6737 
6752 VuoRunner * VuoCompiler::newSeparateProcessRunnerFromCompositionString(string composition, string processName, string workingDirectory, VuoCompilerIssues *issues)
6753 {
6754  try
6755  {
6756  string compositionPath = (workingDirectory.empty() ? "." : workingDirectory) + "/" + processName + ".vuo";
6757  VuoCompiler compiler(compositionPath);
6758  string compiledCompositionPath = VuoFileUtilities::makeTmpFile(processName, "bc");
6759  string linkedCompositionPath = VuoFileUtilities::makeTmpFile(processName, "");
6760  compiler.compileCompositionString(composition, compiledCompositionPath, true, issues);
6761  compiler.linkCompositionToCreateExecutable(compiledCompositionPath, linkedCompositionPath, Optimization_FastBuild);
6762  remove(compiledCompositionPath.c_str());
6763  return VuoRunner::newSeparateProcessRunnerFromExecutable(linkedCompositionPath, workingDirectory, false, true);
6764  }
6765  catch (VuoCompilerException &e)
6766  {
6767  if (issues != e.getIssues())
6768  issues->append(e.getIssues());
6769  return NULL;
6770  }
6771 }
6772 
6782 {
6783  try
6784  {
6785  VuoCompiler compiler(compositionFilePath);
6786  string directory, file, extension;
6787  VuoFileUtilities::splitPath(compositionFilePath, directory, file, extension);
6788  string compiledCompositionPath = VuoFileUtilities::makeTmpFile(file, "bc");
6789  compiler.compileComposition(compositionFilePath, compiledCompositionPath, true, issues);
6790  string linkedCompositionPath = VuoFileUtilities::makeTmpFile(file, "dylib");
6791  compiler.linkCompositionToCreateDynamicLibrary(compiledCompositionPath, linkedCompositionPath, Optimization_FastBuild);
6792  remove(compiledCompositionPath.c_str());
6793  return VuoRunner::newCurrentProcessRunnerFromDynamicLibrary(linkedCompositionPath, directory, true);
6794  }
6795  catch (VuoCompilerException &e)
6796  {
6797  if (issues != e.getIssues())
6798  issues->append(e.getIssues());
6799  return NULL;
6800  }
6801 }
6802 
6817 VuoRunner * VuoCompiler::newCurrentProcessRunnerFromCompositionString(string composition, string workingDirectory, VuoCompilerIssues *issues)
6818 {
6819  try
6820  {
6821  string compositionPath = (workingDirectory.empty() ? "." : workingDirectory) + "/UntitledComposition.vuo";
6822  VuoCompiler compiler(compositionPath);
6823  string compiledCompositionPath = VuoFileUtilities::makeTmpFile("VuoRunnerComposition", "bc");
6824  compiler.compileCompositionString(composition, compiledCompositionPath, true, issues);
6825  string linkedCompositionPath = VuoFileUtilities::makeTmpFile("VuoRunnerComposition", "dylib");
6826  compiler.linkCompositionToCreateDynamicLibrary(compiledCompositionPath, linkedCompositionPath, Optimization_FastBuild);
6827  remove(compiledCompositionPath.c_str());
6828  return VuoRunner::newCurrentProcessRunnerFromDynamicLibrary(linkedCompositionPath, workingDirectory, true);
6829  }
6830  catch (VuoCompilerException &e)
6831  {
6832  if (issues != e.getIssues())
6833  issues->append(e.getIssues());
6834  return NULL;
6835  }
6836 }
6837 
6839 {
6840  pendingDataQueue = dispatch_queue_create("org.vuo.compiler.delegate.pending", 0);
6841 }
6842 
6844 {
6845  LoadedModulesData *data = dequeueData();
6846  data->release();
6847 }
6848 
6852 void VuoCompilerDelegate::enqueueData(LoadedModulesData *data)
6853 {
6854  dispatch_sync(pendingDataQueue, ^{
6855  pendingData.push_back(data);
6856  });
6857 }
6858 
6862 VuoCompilerDelegate::LoadedModulesData * VuoCompilerDelegate::dequeueData(void)
6863 {
6864  __block LoadedModulesData *ret;
6865  dispatch_sync(pendingDataQueue, ^{
6866  ret = pendingData.front();
6867  pendingData.pop_front();
6868  });
6869  return ret;
6870 }
6871 
6876 VuoCompilerDelegate::LoadedModulesData::LoadedModulesData(const set< pair<VuoCompilerModule *, VuoCompilerModule *> > &modulesModified,
6877  const set<VuoCompilerModule *> &modulesRemoved, VuoCompilerIssues *issues)
6878 {
6879  referenceCountQueue = dispatch_queue_create("org.vuo.compiler.delegate.reference", 0);
6880  referenceCount = 0;
6881 
6882  this->modulesModified = modulesModified;
6883  this->modulesRemoved = modulesRemoved;
6884  this->issues = issues;
6885 }
6886 
6890 VuoCompilerDelegate::LoadedModulesData::~LoadedModulesData(void)
6891 {
6892  delete issues;
6893 
6894  for (set< pair<VuoCompilerModule *, VuoCompilerModule *> >::iterator i = modulesModified.begin(); i != modulesModified.end(); ++i)
6895  VuoCompiler::destroyModule((*i).first);
6896 
6897  for (set<VuoCompilerModule *>::iterator i = modulesRemoved.begin(); i != modulesRemoved.end(); ++i)
6899 }
6900 
6904 void VuoCompilerDelegate::LoadedModulesData::retain(void)
6905 {
6906  dispatch_sync(referenceCountQueue, ^{
6907  ++referenceCount;
6908  });
6909 }
6910 
6914 void VuoCompilerDelegate::LoadedModulesData::release(void)
6915 {
6916  dispatch_sync(referenceCountQueue, ^{
6917  --referenceCount;
6918  if (referenceCount == 0) {
6919  delete this;
6920  }
6921  });
6922 }