Vuo  2.1.0
VuoCompiler.cc
Go to the documentation of this file.
1 
10 #include <string.h>
11 #include <sys/param.h>
12 #include <sys/stat.h>
13 #include <sys/time.h>
14 #include <sstream>
15 #include <CoreFoundation/CoreFoundation.h>
16 #include "VuoCompiler.hh"
21 #include "VuoCompilerException.hh"
23 #include "VuoCompilerGraph.hh"
25 #include "VuoCompilerIssue.hh"
26 #include "VuoCompilerNode.hh"
27 #include "VuoCompilerPort.hh"
28 #include "VuoCompilerPortClass.hh"
32 #include "VuoComposition.hh"
33 #include "VuoException.hh"
34 #include "VuoGenericType.hh"
35 #include "VuoModuleCompiler.hh"
37 #include "VuoNode.hh"
38 #include "VuoNodeClass.hh"
39 #include "VuoNodeSet.hh"
40 #include "VuoPublishedPort.hh"
41 #include "VuoRunner.hh"
43 #include "VuoStringUtilities.hh"
44 
45 #if VUO_PRO
46 #include "VuoPro.hh"
47 #endif
48 
49 map<string, VuoFileUtilities::File *> VuoCompiler::Environment::moduleCacheFileForLocking;
50 dispatch_queue_t VuoCompiler::Environment::moduleCacheBuildingQueue = dispatch_queue_create("org.vuo.compiler.cache", NULL);
51 const string VuoCompiler::Environment::pidCacheDirPrefix = "pid-";
52 set<VuoCompiler *> VuoCompiler::allCompilers;
53 dispatch_queue_t VuoCompiler::environmentQueue = dispatch_queue_create("org.vuo.compiler.environment", NULL);
54 vector< vector<VuoCompiler::Environment *> > VuoCompiler::sharedEnvironments;
55 map<string, vector<VuoCompiler::Environment *> > VuoCompiler::environmentsForCompositionFamily;
56 dispatch_group_t VuoCompiler::moduleSourceCompilersExist = dispatch_group_create();
57 string VuoCompiler::vuoFrameworkInProgressPath;
58 
59 dispatch_queue_t llvmQueue = NULL;
60 
64 static void __attribute__((constructor)) VuoCompiler_init(void)
65 {
66  // Increase the open-files limit (macOS defaults to 256).
67  struct rlimit rl{OPEN_MAX, OPEN_MAX};
68  getrlimit(RLIMIT_NOFILE, &rl);
69  rl.rlim_cur = MIN(OPEN_MAX, rl.rlim_max);
70  if (setrlimit(RLIMIT_NOFILE, &rl))
71  VUserLog("Warning: Couldn't set open-files limit: %s", strerror(errno));
72 
73 
74  llvmQueue = dispatch_queue_create("org.vuo.compiler.llvm", NULL);
75 
76  dispatch_sync(llvmQueue, ^{
77  llvm::InitializeNativeTarget();
78 
79  // If the Vuo compiler/linker...
80  // 1. Loads a node class that uses dispatch_object_t.
81  // 2. Generates code that uses dispatch_object_t.
82  // 3. Links the node class into a composition.
83  // 4. Generates more code that uses dispatch_object_t.
84  // ... then Step 4 ends up with the wrong llvm::Type for dispatch_object_t.
85  //
86  // A workaround is to generate some code that uses dispatch_object_t before doing Step 1.
87  //
88  // https://b33p.net/kosada/node/3845
89  // http://lists.cs.uiuc.edu/pipermail/llvmdev/2012-December/057075.html
90  Module module("", getGlobalContext());
92 
93  // Workaround for a possibly related error where the compiler ends up with the wrong
94  // llvm::Type for dispatch_semaphore_s. https://b33p.net/kosada/node/10545
96 
97  // Load the NodeContext and PortContext struct types preemptively to make sure that
98  // their fields get the right dispatch types. If these struct types were to be loaded
99  // first from a subcomposition module, they'd get the wrong dispatch types.
100  // https://b33p.net/kosada/node/11160
103  });
104 }
105 
109 VuoCompiler::ModuleInfo::ModuleInfo(Environment *environment, const string &searchPath, const string &relativePath,
110  bool isSourceFile, bool isSubcomposition)
111 {
112  VuoFileUtilities::File *file = new VuoFileUtilities::File(searchPath, relativePath);
113  initialize(environment, searchPath, file, isSourceFile, isSubcomposition);
114 }
115 
119 VuoCompiler::ModuleInfo::ModuleInfo(Environment *environment, const string &searchPath, VuoFileUtilities::File *file,
120  bool isSourceFile, bool isSubcomposition)
121 {
122  initialize(environment, searchPath, file, isSourceFile, isSubcomposition);
123 }
124 
128 void VuoCompiler::ModuleInfo::initialize(Environment *environment, const string &searchPath, VuoFileUtilities::File *file,
129  bool isSourceFile, bool isSubcomposition)
130 {
131  this->environment = environment;
132  this->searchPath = searchPath;
133  this->file = file;
134  moduleKey = getModuleKeyForPath(file->getRelativePath());
135  sourceCodeOverridden = false;
136  lastModified = VuoFileUtilities::getFileLastModifiedInSeconds(file->isInArchive() ? file->getArchivePath() : file->path());
137  longestDownstreamPath = 0;
138  attempted = false;
139  loading = nullptr;
140 
141 #if VUO_PRO
142  init_Pro();
143 #endif
144 
145  if (isSourceFile)
146  {
147  loading = dispatch_group_create();
148  sourceCode = file->getContentsAsString();
149 
150  if (isSubcomposition)
151  {
152  try
153  {
155  }
156  catch (...)
157  {
158  // Issues parsing the composition will be caught later when compiling it.
159  }
160  }
161  }
162 }
163 
164 VuoCompiler::ModuleInfo::~ModuleInfo(void)
165 {
166 #if VUO_PRO
167  fini_Pro();
168 #endif
169 
170  delete file;
171 
172  if (loading)
173  dispatch_release(loading);
174 }
175 
176 VuoCompiler::Environment * VuoCompiler::ModuleInfo::getEnvironment(void) const
177 {
178  return environment;
179 }
180 
181 string VuoCompiler::ModuleInfo::getSearchPath(void) const
182 {
183  return searchPath;
184 }
185 
186 string VuoCompiler::ModuleInfo::getModuleKey(void) const
187 {
188  return moduleKey;
189 }
190 
191 VuoFileUtilities::File * VuoCompiler::ModuleInfo::getFile(void) const
192 {
193  return file;
194 }
195 
196 string VuoCompiler::ModuleInfo::getSourceCode(void) const
197 {
198  return sourceCode;
199 }
200 
201 void VuoCompiler::ModuleInfo::setSourceCode(const string &sourceCode)
202 {
203  this->sourceCode = sourceCode;
204 }
205 
206 void VuoCompiler::ModuleInfo::revertSourceCode(void)
207 {
208  sourceCode = file->getContentsAsString();
209 }
210 
211 bool VuoCompiler::ModuleInfo::isSourceCodeOverridden(void) const
212 {
213  return sourceCodeOverridden;
214 }
215 
216 void VuoCompiler::ModuleInfo::setSourceCodeOverridden(bool overridden)
217 {
218  sourceCodeOverridden = overridden;
219 }
220 
221 bool VuoCompiler::ModuleInfo::isNewerThan(ModuleInfo *other) const
222 {
223  return lastModified > other->lastModified;
224 }
225 
226 bool VuoCompiler::ModuleInfo::isNewerThan(unsigned long ageInSeconds) const
227 {
228  return lastModified > ageInSeconds;
229 }
230 
231 bool VuoCompiler::ModuleInfo::isOlderThan(unsigned long ageInSeconds) const
232 {
233  return lastModified < ageInSeconds;
234 }
235 
236 void VuoCompiler::ModuleInfo::setLastModifiedToNow(void)
237 {
238  struct timeval t;
239  gettimeofday(&t, NULL);
240  lastModified = t.tv_sec;
241 }
242 
243 set<string> VuoCompiler::ModuleInfo::getContainedNodeClasses(void) const
244 {
245  return containedNodeClasses;
246 }
247 
248 int VuoCompiler::ModuleInfo::getLongestDownstreamPath(void) const
249 {
250  return longestDownstreamPath;
251 }
252 
253 void VuoCompiler::ModuleInfo::setLongestDownstreamPath(int pathLength)
254 {
255  longestDownstreamPath = pathLength;
256 }
257 
258 bool VuoCompiler::ModuleInfo::getAttempted(void) const
259 {
260  return attempted;
261 }
262 
263 void VuoCompiler::ModuleInfo::setAttempted(bool attempted)
264 {
265  this->attempted = attempted;
266 }
267 
268 dispatch_group_t VuoCompiler::ModuleInfo::getLoadingGroup(void) const
269 {
270  return loading;
271 }
272 
273 void VuoCompiler::ModuleInfo::dump() const
274 {
275  fprintf(stderr, "module %s:\n"
276  "\tloadingGroup=%p\n"
277  "\tsearchPath=%s\n"
278  "\tattempted=%d\n"
279  "\tenvironment=%s\n"
280  "\tfile=%s%s%s\n"
281  "\tsourceCodeOverridden=%d\n"
282  "\tsourceCode=%s\n"
283  "\tcontainedNodeClasses:\n",
284  moduleKey.c_str(),
285  loading,
286  searchPath.c_str(),
287  attempted,
288  environment->getCompiledModuleCachePath().c_str(),
289  file->isInArchive() ? file->getArchivePath().c_str() : "",
290  file->isInArchive() ? "::" : "",
291  file->isInArchive() ? file->getRelativePath().c_str() : file->path().c_str(),
292  sourceCodeOverridden,
293  sourceCode.c_str());
294  for (auto i: containedNodeClasses)
295  fprintf(stderr, "\t\t%s\n", i.c_str());
296 }
297 
298 VuoCompiler::DependencyGraphVertex * VuoCompiler::DependencyGraphVertex::vertexForDependency(const string &dependency, VuoDirectedAcyclicGraph *graph)
299 {
300  VuoDirectedAcyclicGraph::Vertex *v = graph->findVertex(dependency);
301  if (v)
302  return dynamic_cast<DependencyGraphVertex *>(v);
303 
304  DependencyGraphVertex *vv = new DependencyGraphVertex();
305  vv->dependency = dependency;
306  vv->environment = NULL;
307  vv->compatible = true;
308  return vv;
309 }
310 
311 string VuoCompiler::DependencyGraphVertex::getDependency(void)
312 {
313  return dependency;
314 }
315 
316 VuoCompiler::Environment * VuoCompiler::DependencyGraphVertex::getEnvironment(void)
317 {
318  return environment;
319 }
320 
321 void VuoCompiler::DependencyGraphVertex::setEnvironment(Environment *environment)
322 {
323  this->environment = environment;
324 }
325 
326 bool VuoCompiler::DependencyGraphVertex::isCompatible(void)
327 {
328  return compatible;
329 }
330 
331 void VuoCompiler::DependencyGraphVertex::setCompatible(bool compatible)
332 {
333  this->compatible = compatible;
334 }
335 
336 string VuoCompiler::DependencyGraphVertex::key(void)
337 {
338  return dependency;
339 }
340 
346 VuoCompiler::ModuleInfoIterator::ModuleInfoIterator(map<string, map<string, ModuleInfo *> > *allModuleInfos)
347 {
348  this->allModuleInfos = allModuleInfos;
349  hasSearchModuleKeys = false;
350  initialize();
351 }
352 
358 VuoCompiler::ModuleInfoIterator::ModuleInfoIterator(map<string, map<string, ModuleInfo *> > *allModuleInfos, const set<string> &searchModuleKeys)
359 {
360  this->allModuleInfos = allModuleInfos;
361  this->searchModuleKeys = searchModuleKeys;
362  hasSearchModuleKeys = true;
363  initialize();
364 }
365 
371 void VuoCompiler::ModuleInfoIterator::initialize(void)
372 {
373  currSearchPath = allModuleInfos->begin();
374  hasCurrModuleKey = false;
375 }
376 
382 VuoCompiler::ModuleInfo * VuoCompiler::ModuleInfoIterator::next(void)
383 {
384  for ( ; currSearchPath != allModuleInfos->end(); ++currSearchPath)
385  {
386  if (! hasCurrModuleKey)
387  {
388  currModuleKey = currSearchPath->second.begin();
389  hasCurrModuleKey = true;
390  }
391 
392  for ( ; currModuleKey != currSearchPath->second.end(); ++currModuleKey)
393  {
394  if (! hasSearchModuleKeys || searchModuleKeys.find(currModuleKey->first) != searchModuleKeys.end())
395  {
396  ModuleInfo *moduleInfo = currModuleKey->second;
397  ++currModuleKey;
398  return moduleInfo;
399  }
400  }
401 
402  hasCurrModuleKey = false;
403  }
404 
405  return NULL;
406 }
407 
411 VuoCompiler::Environment::Environment(void)
412 {
413  compilersToNotifyQueue = dispatch_queue_create("org.vuo.compiler.notify", 0);
414  moduleSearchPathContentsChangedQueue = dispatch_queue_create("org.vuo.compiler.watch", 0);
415  isModuleCacheableDataDirty = false;
416  isModuleCacheInitialized = false;
417  isModuleCacheAvailable = false;
418  dependencyGraph = new VuoDirectedAcyclicGraph();
419  compositionDependencyGraph = new VuoDirectedAcyclicGraph();
420  moduleCompilationQueue = new VuoModuleCompilationQueue();
421 }
422 
426 VuoCompiler::Environment::~Environment(void)
427 {
428  stopWatchingModuleSearchPaths();
429  dispatch_sync(moduleSearchPathContentsChangedQueue, ^{});
430 
431  dispatch_release(moduleSearchPathContentsChangedQueue);
432  dispatch_release(compilersToNotifyQueue);
433 
434  for (map<string, VuoCompilerNodeClass *>::iterator i = nodeClasses.begin(); i != nodeClasses.end(); ++i)
435  destroyModule(i->second);
436 
437  for (map<string, VuoCompilerType *>::iterator i = types.begin(); i != types.end(); ++i)
438  destroyModule(i->second);
439 
440  for (map<string, VuoCompilerModule *>::iterator i = libraryModules.begin(); i != libraryModules.end(); ++i)
441  destroyModule(i->second);
442 
443  for (set<VuoCompilerGenericType *>::iterator i = genericTypes.begin(); i != genericTypes.end(); ++i)
444  {
445  VuoType *base = (*i)->getBase();
446  delete *i;
447  delete base;
448  }
449 
450  for (map<string, VuoNodeSet *>::iterator i = nodeSetForName.begin(); i != nodeSetForName.end(); ++i)
451  delete i->second;
452 
453  for (map<string, map<string, ModuleInfo *> >::iterator i = moduleFilesAtSearchPath.begin(); i != moduleFilesAtSearchPath.end(); ++i)
454  for (map<string, ModuleInfo *>::iterator j = i->second.begin(); j != i->second.end(); ++j)
455  delete j->second;
456 
457  for (map<string, map<string, ModuleInfo *> >::iterator i = sourceFilesAtSearchPath.begin(); i != sourceFilesAtSearchPath.end(); ++i)
458  for (map<string, ModuleInfo *>::iterator j = i->second.begin(); j != i->second.end(); ++j)
459  delete j->second;
460 
461  delete dependencyGraph;
462  delete compositionDependencyGraph;
463 }
464 
468 void VuoCompiler::Environment::addCompilerToNotify(VuoCompiler *compiler)
469 {
470  dispatch_sync(compilersToNotifyQueue, ^{
471  compilersToNotify.insert(compiler);
472  });
473 }
474 
478 void VuoCompiler::Environment::removeCompilerToNotify(VuoCompiler *compiler)
479 {
480  dispatch_sync(compilersToNotifyQueue, ^{
481  compilersToNotify.erase(compiler);
482  });
483 }
484 
490 map<string, VuoCompilerNodeClass *> VuoCompiler::Environment::getNodeClasses(void)
491 {
492  return nodeClasses;
493 }
494 
500 VuoCompilerNodeClass * VuoCompiler::Environment::getNodeClass(const string &moduleKey)
501 {
502  map<string, VuoCompilerNodeClass *>::iterator nodeClassIter = nodeClasses.find(moduleKey);
503  if (nodeClassIter != nodeClasses.end())
504  return nodeClassIter->second;
505 
506  return NULL;
507 }
508 
513 VuoCompilerNodeClass * VuoCompiler::Environment::takeNodeClass(const string &moduleKey)
514 {
515  VuoCompilerNodeClass *nodeClass = NULL;
516 
517  map<string, VuoCompilerNodeClass *>::iterator nodeClassIter = nodeClasses.find(moduleKey);
518  if (nodeClassIter != nodeClasses.end())
519  {
520  nodeClass = nodeClassIter->second;
521 
522  nodeClasses.erase(nodeClassIter);
523  removeFromDependencyGraph(nodeClass);
524  modulesChanged();
525  }
526 
527  return nodeClass;
528 }
529 
535 map<string, VuoCompilerType *> VuoCompiler::Environment::getTypes(void)
536 {
537  return types;
538 }
539 
545 VuoCompilerType * VuoCompiler::Environment::getType(const string &moduleKey)
546 {
547  map<string, VuoCompilerType *>::iterator typeIter = types.find(moduleKey);
548  if (typeIter != types.end())
549  return typeIter->second;
550 
551  return NULL;
552 }
553 
559 map<string, VuoNodeSet *> VuoCompiler::Environment::getNodeSets(void)
560 {
561  return nodeSetForName;
562 }
563 
569 map<string, VuoCompilerModule *> VuoCompiler::Environment::getLibraryModules(void)
570 {
571  return libraryModules;
572 }
573 
580 VuoCompilerModule * VuoCompiler::Environment::findModule(const string &moduleKey)
581 {
582  map<string, VuoCompilerNodeClass *>::iterator nodeClassIter = nodeClasses.find(moduleKey);
583  if (nodeClassIter != nodeClasses.end())
584  return nodeClassIter->second;
585 
586  map<string, VuoCompilerType *>::iterator typeIter = types.find(moduleKey);
587  if (typeIter != types.end())
588  return typeIter->second;
589 
590  map<string, VuoCompilerModule *>::iterator libraryIter = libraryModules.find(moduleKey);
591  if (libraryIter != libraryModules.end())
592  return libraryIter->second;
593 
594  return NULL;
595 }
596 
602 VuoNodeSet * VuoCompiler::Environment::findNodeSet(const string &name)
603 {
604 
605  map<string, VuoNodeSet *>::iterator nodeSetIter = nodeSetForName.find(name);
606  if (nodeSetIter != nodeSetForName.end())
607  return nodeSetIter->second;
608 
609  return NULL;
610 }
611 
618 void VuoCompiler::Environment::addModuleSearchPath(const string &path, bool shouldWatch)
619 {
620  moduleSearchPaths.push_back(path);
621 
622  updateModulesAtSearchPath(path);
623  updateSourceFilesAtSearchPath(path);
624 
625  if (shouldWatch)
626  startWatchingModuleSearchPath(path);
627 }
628 
634 vector<string> VuoCompiler::Environment::getModuleSearchPaths(void)
635 {
636  return moduleSearchPaths;
637 }
638 
644 void VuoCompiler::Environment::addHeaderSearchPath(const string &path)
645 {
646  headerSearchPaths.push_back(path);
647 }
648 
654 vector<string> VuoCompiler::Environment::getHeaderSearchPaths(void)
655 {
656  return headerSearchPaths;
657 }
658 
664 void VuoCompiler::Environment::addLibrarySearchPath(const string &path)
665 {
666  librarySearchPaths.push_back(path);
667 }
668 
674 vector<string> VuoCompiler::Environment::getLibrarySearchPaths(void)
675 {
676  return librarySearchPaths;
677 }
678 
684 void VuoCompiler::Environment::addFrameworkSearchPath(const string &path)
685 {
686  frameworkSearchPaths.push_back(path);
687 }
688 
694 vector<string> VuoCompiler::Environment::getFrameworkSearchPaths(void)
695 {
696  return frameworkSearchPaths;
697 }
698 
699 VuoDirectedAcyclicGraph * VuoCompiler::Environment::getDependencyGraph(void)
700 {
701  return dependencyGraph;
702 }
703 
704 VuoDirectedAcyclicGraph * VuoCompiler::Environment::getCompositionDependencyGraph(void)
705 {
706  return compositionDependencyGraph;
707 }
708 
714 void VuoCompiler::Environment::setModuleCachePath(const string &path)
715 {
716  moduleCachePath = path;
717 }
718 
724 string VuoCompiler::Environment::getCompiledModuleCachePath(void)
725 {
726  return (moduleCachePath.empty() ? "" : moduleCachePath + "/Modules");
727 }
728 
734 string VuoCompiler::Environment::getOverriddenCompiledModuleCachePath(void)
735 {
736  if (moduleCachePath.empty())
737  return "";
738 
739  ostringstream pid;
740  pid << getpid();
741 
742  string dir, moduleCacheDirName, ext;
743  VuoFileUtilities::splitPath(moduleCachePath, dir, moduleCacheDirName, ext);
744 
745  return (VuoFileUtilities::getCachePath() + "/" + pidCacheDirPrefix + pid.str() + "/" + moduleCacheDirName + "/Modules");
746 }
747 
755 void VuoCompiler::Environment::addExpatriateSourceFile(const string &sourcePath)
756 {
757  expatriateSourceFiles.push_back(sourcePath);
758 
759  if (find(moduleSearchPaths.begin(), moduleSearchPaths.end(), "") == moduleSearchPaths.end())
760  moduleSearchPaths.push_back("");
761 
762  auto iter = sourceFilesAtSearchPath.find("");
763  if (iter != sourceFilesAtSearchPath.end())
764  sourceFilesAtSearchPath.erase(iter);
765 }
766 
772 void VuoCompiler::Environment::removeExpatriateSourceFile(const string &sourcePath)
773 {
774  for (auto i = expatriateSourceFiles.begin(); i != expatriateSourceFiles.end(); ++i)
775  {
776  if (VuoFileUtilities::arePathsEqual(*i, sourcePath))
777  {
778  expatriateSourceFiles.erase(i);
779 
780  auto iter = sourceFilesAtSearchPath.find("");
781  if (iter != sourceFilesAtSearchPath.end())
782  sourceFilesAtSearchPath.erase(iter);
783 
784  break;
785  }
786  }
787 }
788 
801 void VuoCompiler::Environment::updateModulesAtSearchPath(const string &path)
802 {
803  if (moduleFilesAtSearchPath.find(path) != moduleFilesAtSearchPath.end())
804  return;
805 
806  set<string> moduleExtensions;
807  moduleExtensions.insert("vuonode");
808  moduleExtensions.insert("vuonode+");
809  moduleExtensions.insert("bc");
810  moduleExtensions.insert("bc+");
811  set<string> archiveExtensions;
812  archiveExtensions.insert("vuonode");
813  set<VuoFileUtilities::File *> moduleFiles = VuoFileUtilities::findFilesInDirectory(path, moduleExtensions, archiveExtensions);
814 
815  map<string, ModuleInfo *> fileForModuleKey;
816  for (set<VuoFileUtilities::File *>::iterator i = moduleFiles.begin(); i != moduleFiles.end(); ++i)
817  {
818  VuoFileUtilities::File *moduleFile = *i;
819 
820  ModuleInfo *m = new ModuleInfo(this, path, moduleFile, false, false);
821  fileForModuleKey[m->getModuleKey()] = m;
822  }
823 
824  if (path == getCompiledModuleCachePath())
825  {
826  for (map<string, ModuleInfo *>::iterator i = fileForModuleKey.begin(); i != fileForModuleKey.end(); )
827  {
828  ModuleInfo *sourceInfo = listSourceFile(i->first);
829  if (! sourceInfo || sourceInfo->isNewerThan(i->second))
830  {
831  ModuleInfo *m = i->second;
832  VuoFileUtilities::deleteFile(m->getFile()->path());
833  delete m;
834  fileForModuleKey.erase(i++);
835  }
836  else
837  ++i;
838  }
839  }
840 
841  moduleFilesAtSearchPath[path] = fileForModuleKey;
842 }
843 
849 void VuoCompiler::Environment::updateModuleAtSearchPath(const string &moduleSearchPath, const string &moduleRelativePath)
850 {
851  string dir, file, ext;
852  VuoFileUtilities::splitPath(moduleRelativePath, dir, file, ext);
853 
854  set<string> moduleExtensions;
855  moduleExtensions.insert(ext);
856  set<string> archiveExtensions;
857  archiveExtensions.insert("vuonode");
858 
859  set<VuoFileUtilities::File *> moduleFiles = VuoFileUtilities::findFilesInDirectory(moduleSearchPath, moduleExtensions, archiveExtensions);
860 
861  for (set<VuoFileUtilities::File *>::iterator i = moduleFiles.begin(); i != moduleFiles.end(); ++i)
862  {
863  VuoFileUtilities::File *moduleFile = *i;
864 
865  ModuleInfo *m = new ModuleInfo(this, moduleSearchPath, moduleFile, false, false);
866  moduleFilesAtSearchPath[moduleSearchPath][m->getModuleKey()] = m;
867  }
868 }
869 
877 void VuoCompiler::Environment::updateSourceFilesAtSearchPath(const string &path)
878 {
879  map<string, map<string, ModuleInfo *> >::iterator sourceFilesIter = sourceFilesAtSearchPath.find(path);
880  if (sourceFilesIter != sourceFilesAtSearchPath.end())
881  return;
882 
883  set<VuoFileUtilities::File *> sourceFiles;
884  if (! path.empty())
885  {
886  set<string> sourceExtensions;
887  sourceExtensions.insert("vuo");
888  sourceExtensions.insert("fs");
890  sourceExtensions.insert(cext.begin(), cext.end());
891  sourceFiles = VuoFileUtilities::findFilesInDirectory(path, sourceExtensions, set<string>());
892  }
893  else
894  {
895  for (const string &sourcePath : expatriateSourceFiles)
896  {
897  string dir, file, ext;
898  VuoFileUtilities::splitPath(sourcePath, dir, file, ext);
899  VuoFileUtilities::File *sourceFile = new VuoFileUtilities::File(dir, file + "." + ext);
900  sourceFiles.insert(sourceFile);
901  }
902  }
903 
904  map<string, ModuleInfo *> fileForModuleKey;
905  for (set<VuoFileUtilities::File *>::iterator i = sourceFiles.begin(); i != sourceFiles.end(); ++i)
906  {
907  VuoFileUtilities::File *sourceFile = *i;
908 
909  string dir, moduleKey, ext;
910  VuoFileUtilities::splitPath(sourceFile->getRelativePath(), dir, moduleKey, ext);
911  bool isSubcomposition = (ext == "vuo");
912 
913  // Ignore missing expatriateSourceFiles — they might have been deleted in the meantime.
914  if (path.empty() && !sourceFile->exists())
915  continue;
916 
917  ModuleInfo *m = new ModuleInfo(this, path, sourceFile, true, isSubcomposition);
918  if (fileForModuleKey.find(m->getModuleKey()) != fileForModuleKey.end())
919  VUserLog("Warning: Conflicting source files for module %s are installed at %s", m->getModuleKey().c_str(), path.c_str());
920  fileForModuleKey[m->getModuleKey()] = m;
921 
922  if (isSubcomposition)
923  {
924  DependencyGraphVertex *compositionVertex = DependencyGraphVertex::vertexForDependency(moduleKey, compositionDependencyGraph);
925  compositionDependencyGraph->addVertex(compositionVertex);
926 
927  compositionVertex->setEnvironment(this);
928 
929  set<string> dependencies = m->getContainedNodeClasses();
930  for (set<string>::iterator j = dependencies.begin(); j != dependencies.end(); ++j)
931  {
932  DependencyGraphVertex *dependencyVertex = DependencyGraphVertex::vertexForDependency(*j, compositionDependencyGraph);
933  compositionDependencyGraph->addEdge(compositionVertex, dependencyVertex);
934  }
935  }
936  }
937 
938  sourceFilesAtSearchPath[path] = fileForModuleKey;
939 }
940 
946 VuoCompiler::ModuleInfo * VuoCompiler::Environment::listModule(const string &moduleKey)
947 {
948  for (const auto &moduleFiles : moduleFilesAtSearchPath)
949  {
950  map<string, ModuleInfo *>::const_iterator foundIter = moduleFiles.second.find(moduleKey);
951  if (foundIter != moduleFiles.second.end())
952  return foundIter->second;
953  }
954 
955  return NULL;
956 }
957 
963 VuoCompiler::ModuleInfoIterator VuoCompiler::Environment::listModules(const set<string> &moduleKeys)
964 {
965  for (const string &path : moduleSearchPaths)
966  updateModulesAtSearchPath(path);
967 
968  return ModuleInfoIterator(&moduleFilesAtSearchPath, moduleKeys);
969 }
970 
976 VuoCompiler::ModuleInfoIterator VuoCompiler::Environment::listAllModules(void)
977 {
978  for (const string &path : moduleSearchPaths)
979  updateModulesAtSearchPath(path);
980 
981  return ModuleInfoIterator(&moduleFilesAtSearchPath);
982 }
983 
989 VuoCompiler::ModuleInfo * VuoCompiler::Environment::listSourceFile(const string &moduleKey)
990 {
991  for (const auto &sourceFiles : sourceFilesAtSearchPath)
992  {
993  map<string, ModuleInfo *>::const_iterator foundIter = sourceFiles.second.find(moduleKey);
994  if (foundIter != sourceFiles.second.end())
995  return foundIter->second;
996  }
997 
998  return NULL;
999 }
1000 
1006 VuoCompiler::ModuleInfoIterator VuoCompiler::Environment::listSourceFiles(const set<string> &moduleKeys)
1007 {
1008  for (const string &path : moduleSearchPaths)
1009  updateSourceFilesAtSearchPath(path);
1010 
1011  return ModuleInfoIterator(&sourceFilesAtSearchPath, moduleKeys);
1012 }
1013 
1019 VuoCompiler::ModuleInfoIterator VuoCompiler::Environment::listAllSourceFiles(void)
1020 {
1021  for (const string &path : moduleSearchPaths)
1022  updateSourceFilesAtSearchPath(path);
1023 
1024  return ModuleInfoIterator(&sourceFilesAtSearchPath);
1025 }
1026 
1030 vector<string> VuoCompiler::Environment::getBuiltInModuleSearchPaths(void)
1031 {
1032  vector<string> builtInModuleSearchPaths;
1033 
1034  string vuoFrameworkPath = getVuoFrameworkPath();
1035  if (! vuoFrameworkPath.empty())
1036  {
1037  builtInModuleSearchPaths.push_back(vuoFrameworkPath + "/Modules");
1038  }
1039  else
1040  {
1041  builtInModuleSearchPaths.push_back(VUO_BUILD_DIR "/library");
1042  builtInModuleSearchPaths.push_back(VUO_BUILD_DIR "/node");
1043  builtInModuleSearchPaths.push_back(VUO_BUILD_DIR "/type");
1044  builtInModuleSearchPaths.push_back(VUO_BUILD_DIR "/type/list");
1045  }
1046 
1047  return builtInModuleSearchPaths;
1048 }
1049 
1053 vector<string> VuoCompiler::Environment::getBuiltInHeaderSearchPaths(void)
1054 {
1055  vector<string> builtInHeaderSearchPaths;
1056 
1057  string vuoFrameworkPath = getVuoFrameworkPath();
1058  if (! vuoFrameworkPath.empty())
1059  {
1060  builtInHeaderSearchPaths.push_back(vuoFrameworkPath + "/Headers");
1061  builtInHeaderSearchPaths.push_back(vuoFrameworkPath + "/Headers/macos"); // system headers installed by Xcode Command Line Tools
1062  builtInHeaderSearchPaths.push_back(vuoFrameworkPath + "/Frameworks/llvm.framework/Versions/A/Headers/lib/c++/v1");
1063  }
1064  else
1065  {
1066  builtInHeaderSearchPaths.push_back(VUO_SOURCE_DIR "/library");
1067  builtInHeaderSearchPaths.push_back(VUO_SOURCE_DIR "/node");
1068  builtInHeaderSearchPaths.push_back(VUO_SOURCE_DIR "/type");
1069  builtInHeaderSearchPaths.push_back(VUO_SOURCE_DIR "/type/list");
1070  builtInHeaderSearchPaths.push_back(VUO_SOURCE_DIR "/runtime");
1071  builtInHeaderSearchPaths.push_back(JSONC_ROOT "/include");
1072  builtInHeaderSearchPaths.push_back(LLVM_ROOT "/include/c++/v1");
1073  }
1074 
1075  return builtInHeaderSearchPaths;
1076 }
1077 
1081 vector<string> VuoCompiler::Environment::getBuiltInLibrarySearchPaths(void)
1082 {
1083  vector<string> builtInLibrarySearchPaths;
1084 
1085  string vuoFrameworkPath = getVuoFrameworkPath();
1086  if (! vuoFrameworkPath.empty())
1087  {
1088  builtInLibrarySearchPaths.push_back(vuoFrameworkPath + "/Modules");
1089 
1090  // Ensure we (statically) link to our OpenSSL build when generating Vuo.framework's built-in module cache.
1091  builtInLibrarySearchPaths.push_back(OPENSSL_ROOT "/lib");
1092  }
1093  else
1094  {
1095  builtInLibrarySearchPaths.push_back(VUO_BUILD_DIR "/library");
1096  builtInLibrarySearchPaths.push_back(VUO_BUILD_DIR "/node");
1097  builtInLibrarySearchPaths.push_back(VUO_BUILD_DIR "/type");
1098  builtInLibrarySearchPaths.push_back(VUO_BUILD_DIR "/type/list");
1099  builtInLibrarySearchPaths.push_back(VUO_BUILD_DIR "/runtime");
1100 
1101  builtInLibrarySearchPaths.push_back(LEAP_ROOT);
1102 
1103  vector<string> conanLibDirs = VuoStringUtilities::split(CONAN_LIBRARY_PATHS, ';');
1104  builtInLibrarySearchPaths.insert(builtInLibrarySearchPaths.end(), conanLibDirs.begin(), conanLibDirs.end());
1105  }
1106 
1107  return builtInLibrarySearchPaths;
1108 }
1109 
1113 vector<string> VuoCompiler::Environment::getBuiltInFrameworkSearchPaths(void)
1114 {
1115  vector<string> builtInFrameworkSearchPaths;
1116 
1117  string vuoFrameworkPath = getVuoFrameworkPath();
1118  if (! vuoFrameworkPath.empty())
1119  {
1120  builtInFrameworkSearchPaths.push_back(vuoFrameworkPath + "/Modules/");
1121  builtInFrameworkSearchPaths.push_back(vuoFrameworkPath + "/Frameworks/");
1122  }
1123  else
1124  {
1125  builtInFrameworkSearchPaths.push_back(SYPHON_ROOT);
1126  }
1127 
1128  return builtInFrameworkSearchPaths;
1129 }
1130 
1137 void VuoCompiler::Environment::startWatchingModuleSearchPath(const string &moduleSearchPath)
1138 {
1139  VuoFileWatcher *watcher = new VuoFileWatcher(this, moduleSearchPath);
1140  moduleSearchPathWatchers.insert(watcher);
1141 }
1142 
1148 void VuoCompiler::Environment::stopWatchingModuleSearchPaths(void)
1149 {
1150  for (set<VuoFileWatcher *>::iterator i = moduleSearchPathWatchers.begin(); i != moduleSearchPathWatchers.end(); ++i)
1151  delete *i;
1152 
1153  moduleSearchPathWatchers.clear();
1154 }
1155 
1160 void VuoCompiler::Environment::fileChanged(const string &moduleSearchPath)
1161 {
1162  dispatch_sync(moduleSearchPathContentsChangedQueue, ^{
1163  moduleSearchPathContentsChanged(moduleSearchPath);
1164  });
1165 }
1166 
1174 void VuoCompiler::Environment::moduleSearchPathContentsChanged(const string &moduleSearchPath)
1175 {
1176  //VLog(" E=%p -- %s", this, moduleSearchPath.c_str());
1177  VuoCompiler *compilerForLoading = new VuoCompiler(moduleSearchPath + "/unused");
1178 
1179  __block set<string> modulesAdded;
1180  __block set<string> modulesModifed;
1181  __block set<string> modulesRemoved;
1182  __block set<string> compositionsAdded;
1183  __block set<string> compositionsModifed;
1184  __block set<string> compositionsRemoved;
1185 
1186  dispatch_sync(environmentQueue, ^{
1187 
1188  // Remove the old file records from the environment.
1189 
1190  map<string, ModuleInfo *> oldModules;
1191  map<string, ModuleInfo *> oldCompositions;
1192 
1193  map<string, map<string, ModuleInfo *> >::iterator mf = moduleFilesAtSearchPath.find(moduleSearchPath);
1194  if (mf != moduleFilesAtSearchPath.end())
1195  {
1196  for (map<string, ModuleInfo *>::iterator j = mf->second.begin(); j != mf->second.end(); ++j)
1197  {
1198  oldModules[j->first] = j->second;
1199  }
1200 
1201  moduleFilesAtSearchPath.erase(mf);
1202  }
1203 
1204  map<string, map<string, ModuleInfo *> >::iterator cf = sourceFilesAtSearchPath.find(moduleSearchPath);
1205  if (cf != sourceFilesAtSearchPath.end())
1206  {
1207  for (map<string, ModuleInfo *>::iterator j = cf->second.begin(); j != cf->second.end(); ++j)
1208  {
1209  oldCompositions[j->first] = j->second;
1210 
1211  VuoDirectedAcyclicGraph::Vertex *vertex = compositionDependencyGraph->findVertex(j->first);
1212  compositionDependencyGraph->removeVertex(vertex);
1213  }
1214 
1215  sourceFilesAtSearchPath.erase(cf);
1216  }
1217 
1218  // Rebuild the file records based on the directory contents.
1219 
1220  updateModulesAtSearchPath(moduleSearchPath);
1221  updateSourceFilesAtSearchPath(moduleSearchPath);
1222 
1223  // Compare the old and new file records to see what has changed.
1224 
1225  mf = moduleFilesAtSearchPath.find(moduleSearchPath);
1226  if (mf != moduleFilesAtSearchPath.end())
1227  {
1228  for (map<string, ModuleInfo *>::iterator n = mf->second.begin(); n != mf->second.end(); ++n)
1229  {
1230  string moduleKey = n->first;
1231 
1232  map<string, ModuleInfo *>::iterator o = oldModules.find(moduleKey);
1233  if (o != oldModules.end())
1234  {
1235  if (n->second->isNewerThan(o->second))
1236  {
1237  modulesModifed.insert(moduleKey);
1238  }
1239  else
1240  {
1241  n->second->setAttempted(o->second->getAttempted());
1242  }
1243 
1244  delete o->second;
1245  oldModules.erase(o);
1246  }
1247  else
1248  {
1249  modulesAdded.insert(moduleKey);
1250  }
1251  }
1252  }
1253 
1254  cf = sourceFilesAtSearchPath.find(moduleSearchPath);
1255  if (cf != sourceFilesAtSearchPath.end())
1256  {
1257  for (map<string, ModuleInfo *>::iterator n = cf->second.begin(); n != cf->second.end(); ++n)
1258  {
1259  string moduleKey = n->first;
1260 
1261  map<string, ModuleInfo *>::iterator o = oldCompositions.find(moduleKey);
1262  if (o != oldCompositions.end())
1263  {
1264  if (n->second->isNewerThan(o->second))
1265  {
1266  compositionsModifed.insert(moduleKey);
1267  }
1268  else
1269  {
1270  n->second->setAttempted(o->second->getAttempted());
1271  n->second->setSourceCode(o->second->getSourceCode());
1272  }
1273 
1274  delete o->second;
1275  oldCompositions.erase(o);
1276  }
1277  else
1278  {
1279  compositionsAdded.insert(moduleKey);
1280  }
1281  }
1282  }
1283 
1284  for (map<string, ModuleInfo *>::iterator o = oldModules.begin(); o != oldModules.end(); ++o)
1285  {
1286  delete o->second;
1287  modulesRemoved.insert(o->first);
1288  }
1289 
1290  for (map<string, ModuleInfo *>::iterator o = oldCompositions.begin(); o != oldCompositions.end(); ++o)
1291  {
1292  delete o->second;
1293  compositionsRemoved.insert(o->first);
1294  }
1295 
1296  compilerForLoading->loadModulesAndSources(modulesAdded, modulesModifed, modulesRemoved,
1297  compositionsAdded, compositionsModifed, compositionsRemoved,
1298  false, false, this, nullptr, nullptr, "");
1299  });
1300 
1301  delete compilerForLoading;
1302 }
1303 
1310 void VuoCompiler::Environment::moduleFileChanged(const string &modulePath, const string &moduleSourceCode,
1311  std::function<void(void)> moduleLoadedCallback,
1312  VuoCompiler *compiler, VuoCompilerIssues *issues)
1313 {
1314  //VLog(" E=%p -- %s", this, modulePath.c_str());
1315  dispatch_sync(environmentQueue, ^{
1316 
1317  string moduleDir, moduleKey, ext;
1318  VuoFileUtilities::splitPath(modulePath, moduleDir, moduleKey, ext);
1320 
1321  // Remove the old file record from the environment.
1322 
1323  bool foundOldModule = false;
1324  auto moduleSearchPathIter = moduleFilesAtSearchPath.find(moduleDir);
1325  if (moduleSearchPathIter != moduleFilesAtSearchPath.end())
1326  {
1327  auto moduleIter = moduleSearchPathIter->second.find(moduleKey);
1328  if (moduleIter != moduleSearchPathIter->second.end())
1329  {
1330  delete moduleIter->second;
1331  moduleSearchPathIter->second.erase(moduleIter);
1332  foundOldModule = true;
1333  }
1334  }
1335 
1336  // Update the file record for the module by re-checking the file.
1337 
1338  updateModuleAtSearchPath(moduleDir, moduleKey + "." + ext);
1339 
1340  // Compare the old and new file records to see how the module has changed.
1341 
1342  bool foundNewModule = false;
1343  moduleSearchPathIter = moduleFilesAtSearchPath.find(moduleDir);
1344  if (moduleSearchPathIter != moduleFilesAtSearchPath.end())
1345  {
1346  auto moduleIter = moduleSearchPathIter->second.find(moduleKey);
1347  if (moduleIter != moduleSearchPathIter->second.end())
1348  {
1349  foundNewModule = true;
1350  }
1351  }
1352 
1353  set<string> modulesAdded;
1354  set<string> modulesModified;
1355  set<string> modulesRemoved;
1356 
1357  if ((foundOldModule || VuoFileUtilities::arePathsEqual(moduleDir, getOverriddenCompiledModuleCachePath())) && foundNewModule)
1358  {
1359  modulesModified.insert(moduleKey);
1360  }
1361  else if (! foundOldModule && foundNewModule)
1362  {
1363  modulesAdded.insert(moduleKey);
1364  }
1365  else if (foundOldModule && ! foundNewModule)
1366  {
1367  modulesRemoved.insert(moduleKey);
1368  }
1369 
1370  compiler->loadModulesAndSources(modulesAdded, modulesModified, modulesRemoved,
1371  set<string>(), set<string>(), set<string>(),
1372  false, false, this, issues, moduleLoadedCallback, moduleSourceCode);
1373  });
1374 
1375 }
1376 
1380 void VuoCompiler::Environment::notifyCompilers(const set<VuoCompilerModule *> &modulesAdded,
1381  const set< pair<VuoCompilerModule *, VuoCompilerModule *> > &modulesModified,
1382  const set<VuoCompilerModule *> &modulesRemoved, VuoCompilerIssues *issues,
1383  bool oldModulesInvalidated)
1384 {
1385  if (! (modulesAdded.empty() && modulesModified.empty() && modulesRemoved.empty() && issues->isEmpty()) )
1386  {
1387  set< pair<VuoCompilerModule *, VuoCompilerModule *> > invalidatedModulesModified;
1388  set<VuoCompilerModule *> invalidatedModulesRemoved;
1389  if (oldModulesInvalidated)
1390  {
1391  invalidatedModulesModified = modulesModified;
1392  invalidatedModulesRemoved = modulesRemoved;
1393  }
1394 
1395  VuoCompilerDelegate::LoadedModulesData *delegateData = new VuoCompilerDelegate::LoadedModulesData(invalidatedModulesModified, invalidatedModulesRemoved, issues);
1396 
1397  map<string, VuoCompilerModule *> modulesAddedMap;
1398  map<string, pair<VuoCompilerModule *, VuoCompilerModule *> > modulesModifiedMap;
1399  map<string, VuoCompilerModule *> modulesRemovedMap;
1400  for (VuoCompilerModule *m : modulesAdded)
1401  modulesAddedMap[m->getPseudoBase()->getModuleKey()] = m;
1402  for (pair<VuoCompilerModule *, VuoCompilerModule *> m : modulesModified)
1403  modulesModifiedMap[m.first->getPseudoBase()->getModuleKey()] = m;
1404  for (VuoCompilerModule *m : modulesRemoved)
1405  modulesRemovedMap[m->getPseudoBase()->getModuleKey()] = m;
1406 
1407  dispatch_sync(compilersToNotifyQueue, ^{
1408  for (set<VuoCompiler *>::iterator i = compilersToNotify.begin(); i != compilersToNotify.end(); ++i) {
1409  delegateData->retain();
1410  }
1411  for (set<VuoCompiler *>::iterator i = compilersToNotify.begin(); i != compilersToNotify.end(); ++i) {
1412  (*i)->loadedModules(modulesAddedMap, modulesModifiedMap, modulesRemovedMap, issues, delegateData, this);
1413  }
1414  });
1415  }
1416  else
1417  {
1418  delete issues;
1419  }
1420 }
1421 
1432 set<VuoCompilerModule *> VuoCompiler::Environment::loadCompiledModules(const set<string> &moduleKeys, const map<string, string> &sourceCodeForModule)
1433 {
1434  ModuleInfoIterator modulesToLoadIter = listModules(moduleKeys);
1435  set<VuoCompilerModule *> modulesLoaded;
1436 
1437  ModuleInfo *moduleInfo;
1438  while ((moduleInfo = modulesToLoadIter.next()))
1439  {
1440  string moduleKey = moduleInfo->getModuleKey();
1441 
1442  // Skip the module if its file is not in this environment, if the module has already been loaded,
1443  // or if the compiler previously tried to load the module and it failed.
1444  if (moduleInfo->getEnvironment() != this || findModule(moduleKey) || moduleInfo->getAttempted())
1445  continue;
1446 
1447  moduleInfo->setAttempted(true);
1448 
1449  // Actually load the module.
1450  VuoCompilerModule *module = loadModule(moduleInfo);
1451 
1452  if (module)
1453  {
1454  modulesLoaded.insert(module);
1455  addToDependencyGraph(module);
1456  modulesChanged();
1457 
1458  // For a compiled subcomposition or other source file, store its source code in the VuoCompilerNodeClass.
1459  string searchPath = moduleInfo->getSearchPath();
1460  if (VuoFileUtilities::arePathsEqual(searchPath, getCompiledModuleCachePath()) ||
1461  VuoFileUtilities::arePathsEqual(searchPath, getOverriddenCompiledModuleCachePath()))
1462  {
1463  VuoCompilerNodeClass *nodeClass = dynamic_cast<VuoCompilerNodeClass *>(module);
1464  if (nodeClass)
1465  {
1466  ModuleInfo *sourceInfo = listSourceFile(moduleKey);
1467  if (sourceInfo)
1468  nodeClass->setSourcePath( sourceInfo->getFile()->path() );
1469 
1470  auto sourceCodeIter = sourceCodeForModule.find(moduleKey);
1471  if (sourceCodeIter != sourceCodeForModule.end())
1472  nodeClass->setSourceCode( sourceCodeIter->second );
1473  }
1474  }
1475  }
1476  }
1477 
1478  return modulesLoaded;
1479 }
1480 
1492 set<dispatch_group_t> VuoCompiler::Environment::loadSpecializedModules(const set<string> &moduleKeys,
1493  VuoCompiler *compiler, dispatch_queue_t llvmQueue)
1494 {
1495  set<dispatch_group_t > loadingGroups;
1496 
1497  for (string moduleKey : moduleKeys)
1498  {
1499  // Skip if it's not a node class.
1500 
1501  if (moduleKey.find(".") == string::npos || VuoStringUtilities::endsWith(moduleKey, ".framework"))
1502  continue;
1503 
1504  // Skip the module if it's already been loaded.
1505 
1506  if (findModule(moduleKey))
1507  continue;
1508 
1509  // Skip the module if it's in the process of being loaded, but have the caller wait for it to finish loading.
1510 
1511  map<string, dispatch_group_t>::iterator iter = specializedModulesLoading.find(moduleKey);
1512  if (iter != specializedModulesLoading.end())
1513  {
1514  loadingGroups.insert(iter->second);
1515  dispatch_retain(iter->second);
1516  continue;
1517  }
1518 
1519  dispatch_group_t loadingGroup = dispatch_group_create();
1520  specializedModulesLoading[moduleKey] = loadingGroup;
1521  loadingGroups.insert(loadingGroup);
1522 
1523  // Generate the module.
1524  // This is done asynchronously since VuoCompilerSpecializedNodeClass::newNodeClass() needs to use environmentQueue
1525  // to compile the generated C code.
1526 
1527  dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
1528  dispatch_group_async(loadingGroup, queue, ^{
1529  VuoNodeClass *baseNodeClass = nullptr;
1530  try
1531  {
1532  baseNodeClass = VuoCompilerSpecializedNodeClass::newNodeClass(moduleKey, compiler, llvmQueue);
1533  }
1534  catch (VuoCompilerException &e)
1535  {
1536  VUserLog("Error: %s", e.what());
1537  }
1538 
1539  if (baseNodeClass)
1540  {
1541  VuoCompilerSpecializedNodeClass *specNodeClass = static_cast<VuoCompilerSpecializedNodeClass *>(baseNodeClass->getCompiler());
1542  compiler->loadNodeClassGeneratedAtRuntime(specNodeClass, this);
1543 
1544  set<string> dependencies = specNodeClass->getDependencies();
1545  compiler->loadModulesIfNeeded(dependencies);
1546  }
1547 
1548  dispatch_sync(environmentQueue, ^{
1549  specializedModulesLoading.erase(moduleKey);
1550  });
1551  });
1552  }
1553 
1554  return loadingGroups;
1555 }
1556 
1569 set<dispatch_group_t> VuoCompiler::Environment::compileModulesFromSourceCode(const set<string> &moduleKeys, bool shouldRecompileIfUnchanged)
1570 {
1571  ModuleInfoIterator modulesToLoadIter = listSourceFiles(moduleKeys);
1572 
1573  int environmentIndex = sharedEnvironments.size();
1574  for (int i = 0; i < sharedEnvironments.size(); ++i)
1575  if (this == sharedEnvironments.at(i).at(0))
1576  environmentIndex = i;
1577 
1578  set<dispatch_group_t> sourcesLoading;
1579  int sourcesEnqueued = 0;
1580  ModuleInfo *sourceInfo;
1581  while ((sourceInfo = modulesToLoadIter.next()))
1582  {
1583  string moduleKey = sourceInfo->getModuleKey();
1584 
1585  dispatch_group_t loadingGroup = sourceInfo->getLoadingGroup();
1586  sourcesLoading.insert(loadingGroup);
1587 
1588  // Skip compiling if the source file has already been scheduled for compilation.
1589  // Either its compilation is in progress or it failed to compile.
1590 
1591  if (sourceInfo->getAttempted())
1592  continue;
1593 
1594  // Skip compiling if the compiled module is up-to-date.
1595 
1596  string sourceCode = sourceInfo->getSourceCode();
1597 
1598  if (! shouldRecompileIfUnchanged)
1599  {
1600  VuoCompilerNodeClass *existingNodeClass = getNodeClass(moduleKey);
1601  if (existingNodeClass && existingNodeClass->getSourceCode() == sourceCode)
1602  continue;
1603  }
1604 
1605  // Enqueue the source file to be compiled.
1606 
1607  sourceInfo->setAttempted(true);
1608 
1609  dispatch_group_enter(loadingGroup);
1610  dispatch_group_enter(moduleSourceCompilersExist);
1611 
1613  queueItem->moduleKey = moduleKey;
1614  queueItem->sourcePath = sourceInfo->getFile()->path();
1615  queueItem->sourceCode = sourceCode;
1616  queueItem->sourceFile = sourceInfo->getFile();
1617  queueItem->cachedModulesPath = sourceInfo->isSourceCodeOverridden() ? getOverriddenCompiledModuleCachePath() : getCompiledModuleCachePath();
1618  queueItem->compiledModulePath = queueItem->cachedModulesPath + "/" + moduleKey + ".vuonode";
1619  queueItem->loadingGroup = loadingGroup;
1620  queueItem->priority = { environmentIndex, sourceInfo->getLongestDownstreamPath() };
1621  moduleCompilationQueue->enqueue(queueItem);
1622  ++sourcesEnqueued;
1623  }
1624 
1625  // Compile each enqueued source file. This is done asynchronously for several reasons:
1626  // - To avoid environmentQueue calling compiler code calling environmentQueue.
1627  // - To compile dependencies that were enqueued later while a compilation that was enqueued earlier waits.
1628  // - To be more efficient when compiling multiple source files.
1629 
1630  dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
1631  dispatch_async(queue, ^{
1632  for (int i = 0; i < sourcesEnqueued; ++i)
1633  {
1634  __block VuoModuleCompilationQueue::Item *queueItem = moduleCompilationQueue->next();
1635  VUserLog("Compiling %s", queueItem->moduleKey.c_str());
1636 
1637  dispatch_async(queue, ^{
1638  try
1639  {
1640  VuoFileUtilities::makeDir(queueItem->cachedModulesPath);
1641  }
1642  catch (VuoException &e)
1643  {
1644  VuoCompilerIssue issue(VuoCompilerIssue::Error, "compiling subcomposition", "", "Couldn't create cached modules folder", e.what());
1645  VuoCompilerIssues *issues = new VuoCompilerIssues(issue);
1646  notifyCompilers(set<VuoCompilerModule *>(), set< pair<VuoCompilerModule *, VuoCompilerModule *> >(), set<VuoCompilerModule *>(), issues, false);
1647  }
1648 
1649  VuoCompiler *compiler = new VuoCompiler(queueItem->sourcePath);
1650 
1651  VuoModuleCompilationQueue::Item *queueItemForCallback = queueItem;
1652  auto moduleLoadedCallback = [=](){
1653  dispatch_group_leave(queueItemForCallback->loadingGroup);
1654  moduleCompilationQueue->completed(queueItemForCallback);
1655  };
1656 
1657  string ext = queueItem->sourceFile->extension();
1659  {
1660  VuoModuleCompiler *moduleCompiler = NULL;
1661  try
1662  {
1663  moduleCompiler = VuoModuleCompiler::newModuleCompiler("isf", queueItem->moduleKey, queueItem->sourceFile);
1664  }
1665  catch (VuoException &e)
1666  {
1667  VuoCompilerIssues *issues = new VuoCompilerIssues(VuoCompilerIssue(VuoCompilerIssue::Error, "compiling ISF module", queueItem->sourcePath, "", e.what()));
1668  notifyCompilers(set<VuoCompilerModule *>(), set< pair<VuoCompilerModule *, VuoCompilerModule *> >(), set<VuoCompilerModule *>(), issues, false);
1669  }
1670 
1671  if (moduleCompiler)
1672  {
1673  moduleCompiler->overrideSourceCode(queueItem->sourceCode, queueItem->sourceFile);
1674 
1675  auto getType = [&compiler] (const string &moduleKey) { return compiler->getType(moduleKey); };
1676  VuoCompilerIssues *issues = new VuoCompilerIssues();
1677  Module *module = moduleCompiler->compile(getType, llvmQueue, issues);
1678 
1679  if (module)
1680  {
1681  dispatch_sync(llvmQueue, ^{
1682  //setTargetForModule(module, target);
1683  writeModuleToBitcode(module, queueItem->compiledModulePath);
1684  });
1685  }
1686  else
1687  {
1688  issues->setFilePathIfEmpty(queueItem->sourcePath);
1689  }
1690 
1691  moduleFileChanged(queueItem->compiledModulePath, queueItem->sourceCode, moduleLoadedCallback, compiler, issues);
1692  }
1693  else
1694  moduleLoadedCallback();
1695  }
1696  else if (VuoFileUtilities::isCSourceExtension(ext) && ! queueItem->cachedModulesPath.empty())
1697  {
1698  try
1699  {
1700  compiler->compileModule(queueItem->sourcePath, queueItem->compiledModulePath, vector<string>());
1701  moduleFileChanged(queueItem->compiledModulePath, queueItem->sourceCode, moduleLoadedCallback, compiler, nullptr);
1702  }
1703  catch (VuoCompilerException &e)
1704  {
1705  VuoCompilerIssues *issues = new VuoCompilerIssues;
1706  for (auto issue : e.getIssues()->getList())
1707  issues->append(issue);
1708  notifyCompilers(set<VuoCompilerModule *>(), set< pair<VuoCompilerModule *, VuoCompilerModule *> >(), set<VuoCompilerModule *>(), issues, false);
1709  moduleLoadedCallback();
1710  }
1711  }
1712  else
1713  {
1714  compiler->setLoadAllModules(false);
1715  compiler->compileSubcompositionString(queueItem->sourceCode, queueItem->compiledModulePath, moduleLoadedCallback, this, NULL, queueItem->sourcePath);
1716  }
1717 
1718  delete compiler;
1719  dispatch_group_leave(moduleSourceCompilersExist);
1720  });
1721  }
1722  });
1723 
1724  return sourcesLoading;
1725 }
1726 
1735 set<VuoCompilerModule *> VuoCompiler::Environment::unloadCompiledModules(const set<string> &moduleKeys)
1736 {
1737  set<VuoCompilerModule *> modulesUnloaded;
1738 
1739  for (set<string>::const_iterator i = moduleKeys.begin(); i != moduleKeys.end(); ++i)
1740  {
1741  string moduleKey = *i;
1742  VuoCompilerModule *module = NULL;
1743 
1744  map<string, VuoCompilerNodeClass *>::iterator nodeClassIter = nodeClasses.find(moduleKey);
1745  if (nodeClassIter != nodeClasses.end())
1746  {
1747  module = nodeClassIter->second;
1748  nodeClasses.erase(nodeClassIter);
1749  }
1750  else
1751  {
1752  map<string, VuoCompilerType *>::iterator typeIter = types.find(moduleKey);
1753  if (typeIter != types.end())
1754  {
1755  module = typeIter->second;
1756  types.erase(typeIter);
1757  }
1758  else
1759  {
1760  map<string, VuoCompilerModule *>::iterator libraryModuleIter = libraryModules.find(moduleKey);
1761  if (libraryModuleIter != libraryModules.end())
1762  {
1763  module = libraryModuleIter->second;
1764  libraryModules.erase(libraryModuleIter);
1765  }
1766  }
1767  }
1768 
1769  if (module)
1770  {
1771  modulesUnloaded.insert(module);
1772  removeFromDependencyGraph(module);
1773  modulesChanged();
1774  }
1775  }
1776 
1777  return modulesUnloaded;
1778 }
1779 
1785 void VuoCompiler::Environment::deleteModulesCompiledFromSourceCode(const set<string> &moduleKeys)
1786 {
1787  for (set<string>::const_iterator i = moduleKeys.begin(); i != moduleKeys.end(); ++i)
1788  {
1789  string moduleKey = *i;
1790 
1791  map<string, ModuleInfo *>::iterator iter = moduleFilesAtSearchPath[getCompiledModuleCachePath()].find(moduleKey);
1792  if (iter != moduleFilesAtSearchPath[getCompiledModuleCachePath()].end())
1793  VuoFileUtilities::deleteFile(iter->second->getFile()->path());
1794  }
1795 }
1796 
1804 VuoCompilerModule * VuoCompiler::Environment::loadModule(ModuleInfo *moduleInfo)
1805 {
1806  string moduleKey = moduleInfo->getModuleKey();
1807 
1808  // Skip certain LLVM modules that definitely aren't Vuo modules to avoid adding struct types defined in them to the LLVM
1809  // context, resulting in mismatched struct types in code generation (e.g. %struct.NodeContext and %struct.NodeContext.1).
1810  if (VuoStringUtilities::beginsWith(moduleKey, "libVuo"))
1811  return NULL;
1812 
1813  __block size_t inputDataBytes;
1814  __block char *rawInputData;
1815  dispatch_sync(llvmQueue, ^{
1816  try
1817  {
1818  rawInputData = moduleInfo->getFile()->getContentsAsRawData(inputDataBytes);
1819  }
1820  catch (VuoException &e)
1821  {
1822  rawInputData = NULL;
1823  VUserLog("Warning: Couldn't load module '%s'. Its file may have been deleted. (%s)", moduleKey.c_str(), e.what());
1824  }
1825  });
1826  if (! rawInputData)
1827  return NULL;
1828 
1829  char *processedInputData;
1830 #if VUO_PRO
1831  processedInputData = loadModule_Pro0(moduleInfo, moduleKey, inputDataBytes, rawInputData);
1832 #else
1833  processedInputData = rawInputData;
1834 #endif
1835 
1836  Module *module = NULL;
1837  bool moduleParseError = !processedInputData;
1838  if (!moduleParseError)
1839  {
1840  string moduleReadError;
1841  VuoLog_status("Loading module \"%s\"", moduleKey.c_str());
1842  module = readModuleFromBitcodeData(processedInputData, inputDataBytes, moduleReadError);
1843  VuoLog_status(NULL);
1844  free(processedInputData);
1845 
1846  if (!module)
1847  {
1848  moduleParseError = true;
1849 
1850  string dir, file, ext;
1851  VuoFileUtilities::splitPath(moduleInfo->getFile()->isInArchive() ? moduleInfo->getFile()->getRelativePath() : moduleInfo->getFile()->path(), dir, file, ext);
1853  if (dir == getCompiledModuleCachePath())
1854  VuoFileUtilities::deleteFile(moduleInfo->getFile()->path());
1855  else
1856  VUserLog("Error: Couldn't parse module '%s': %s.", moduleInfo->getFile()->getRelativePath().c_str(), moduleReadError.c_str());
1857  }
1858  }
1859 
1860  if (!moduleParseError)
1861  {
1862  string modulePath;
1863  if (! moduleInfo->getFile()->isInArchive())
1864  modulePath = moduleInfo->getFile()->path();
1865 
1866  __block VuoCompilerModule *compilerModule;
1867  dispatch_sync(llvmQueue, ^{
1868  compilerModule = VuoCompilerModule::newModule(moduleKey, module, modulePath);
1869  });
1870 
1871  if (compilerModule)
1872  {
1873  if (dynamic_cast<VuoCompilerNodeClass *>(compilerModule))
1874  nodeClasses[moduleKey] = static_cast<VuoCompilerNodeClass *>(compilerModule);
1875  else if (dynamic_cast<VuoCompilerType *>(compilerModule))
1876  types[moduleKey] = static_cast<VuoCompilerType *>(compilerModule);
1877  else
1878  libraryModules[moduleKey] = compilerModule;
1879 
1880  VuoNodeSet *nodeSet = VuoNodeSet::createNodeSetForModule(moduleInfo->getFile());
1881  if (nodeSet)
1882  {
1883  map<string, VuoNodeSet *>::iterator nodeSetIter = nodeSetForName.find(nodeSet->getName());
1884  if (nodeSetIter == nodeSetForName.end())
1885  {
1886  nodeSetForName[nodeSet->getName()] = nodeSet;
1887  }
1888  else
1889  {
1890  delete nodeSet;
1891  nodeSet = nodeSetIter->second;
1892  }
1893  compilerModule->getPseudoBase()->setNodeSet(nodeSet);
1894  }
1895 
1896 #if VUO_PRO
1897  loadModule_Pro1(rawInputData, processedInputData, compilerModule);
1898 #endif
1899 
1900  compilerModule->setBuiltIn( isBuiltIn() );
1901 
1902  return compilerModule;
1903  }
1904  else
1905  {
1906  destroyLlvmModule(module);
1907  }
1908  }
1909 
1910  return NULL;
1911 }
1912 
1918 void VuoCompiler::Environment::replaceNodeClass(VuoCompilerNodeClass *newNodeClass)
1919 {
1920  string moduleKey = newNodeClass->getBase()->getModuleKey();
1921 
1922  VuoCompilerNodeClass *oldNodeClass = nodeClasses[moduleKey];
1923  if (oldNodeClass)
1924  {
1925  removeFromDependencyGraph(oldNodeClass);
1926  destroyModule(oldNodeClass);
1927  }
1928 
1929  nodeClasses[moduleKey] = newNodeClass;
1930  addToDependencyGraph(newNodeClass);
1931  modulesChanged();
1932 }
1933 
1934 void VuoCompiler::Environment::addToDependencyGraph(VuoCompilerModule *module)
1935 {
1936  DependencyGraphVertex *vertex = DependencyGraphVertex::vertexForDependency(module->getPseudoBase()->getModuleKey(), dependencyGraph);
1937  dependencyGraph->addVertex(vertex);
1938 
1939  vertex->setEnvironment(this);
1940 
1941  VuoCompilerTargetSet compositionTargets;
1942  compositionTargets.restrictToCurrentOperatingSystemVersion();
1943  vertex->setCompatible( module->getCompatibleTargets().isCompatibleWithAllOf(compositionTargets) );
1944 
1945  set<string> dependencies = module->getDependencies();
1946  for (set<string>::iterator i = dependencies.begin(); i != dependencies.end(); ++i)
1947  {
1948  DependencyGraphVertex *depVertex = DependencyGraphVertex::vertexForDependency(*i, dependencyGraph);
1949  dependencyGraph->addEdge(vertex, depVertex);
1950  }
1951 }
1952 
1953 void VuoCompiler::Environment::removeFromDependencyGraph(VuoCompilerModule *module)
1954 {
1955  DependencyGraphVertex *vertex = DependencyGraphVertex::vertexForDependency(module->getPseudoBase()->getModuleKey(), dependencyGraph);
1956  dependencyGraph->removeVertex(vertex);
1957 }
1958 
1969 void VuoCompiler::Environment::reifyPortTypes(const map<string, VuoCompilerType *> &inheritedTypes)
1970 {
1971  map<string, VuoCompilerType *> availableTypes = inheritedTypes;
1972  availableTypes.insert(types.begin(), types.end());
1973 
1974  for (map<string, VuoCompilerNodeClass *>::const_iterator i = nodeClasses.begin(); i != nodeClasses.end(); ++i)
1975  {
1976  VuoNodeClass *nodeClass = i->second->getBase();
1977 
1978  vector<VuoPortClass *> inputPortClasses = nodeClass->getInputPortClasses();
1979  vector<VuoPortClass *> outputPortClasses = nodeClass->getOutputPortClasses();
1980  vector<VuoPortClass *> portClasses;
1981  portClasses.insert(portClasses.end(), inputPortClasses.begin(), inputPortClasses.end());
1982  portClasses.insert(portClasses.end(), outputPortClasses.begin(), outputPortClasses.end());
1983 
1984  for (vector<VuoPortClass *>::iterator j = portClasses.begin(); j != portClasses.end(); ++j)
1985  {
1986  VuoCompilerPortClass *portClass = static_cast<VuoCompilerPortClass *>((*j)->getCompiler());
1987  VuoType *baseType = portClass->getDataVuoType();
1988 
1989  if (baseType && ! baseType->hasCompiler())
1990  {
1991  string typeName = baseType->getModuleKey();
1992  VuoCompilerType *reifiedType = NULL;
1993 
1994  VuoGenericType *genericType = dynamic_cast<VuoGenericType *>(baseType);
1995  if (genericType)
1996  {
1997  reifiedType = VuoCompilerGenericType::newGenericType(genericType, availableTypes);
1998  if (reifiedType) {
1999  genericTypes.insert( static_cast<VuoCompilerGenericType *>(reifiedType) );
2000  }
2001  }
2002  else
2003  {
2004  map<string, VuoCompilerType *>::iterator reifiedTypeIter = availableTypes.find(typeName);
2005  if (reifiedTypeIter != availableTypes.end())
2006  {
2007  delete baseType;
2008  reifiedType = reifiedTypeIter->second;
2009  }
2010  }
2011 
2012  if (reifiedType) {
2013  portClass->setDataVuoType(reifiedType->getBase());
2014  }
2015  }
2016  }
2017 
2018  vector<VuoCompilerTriggerDescription *> triggers = nodeClass->getCompiler()->getTriggerDescriptions();
2019  for (vector<VuoCompilerTriggerDescription *>::iterator j = triggers.begin(); j != triggers.end(); ++j)
2020  {
2021  VuoCompilerTriggerDescription *trigger = *j;
2022  VuoType *baseType = trigger->getDataType();
2023 
2024  if (baseType && ! baseType->hasCompiler())
2025  {
2026  string typeName = baseType->getModuleKey();
2027  map<string, VuoCompilerType *>::iterator reifiedTypeIter = availableTypes.find(typeName);
2028  if (reifiedTypeIter != availableTypes.end())
2029  {
2030  delete baseType;
2031  VuoCompilerType *reifiedType = reifiedTypeIter->second;
2032  trigger->setDataType(reifiedType->getBase());
2033  }
2034  }
2035  }
2036  }
2037 }
2038 
2051 void VuoCompiler::Environment::getCacheableModulesAndDependencies(bool builtIn, bool installed, set<string> &cacheableModulesAndDependencies,
2052  set<string> &dylibsNeededToLinkToThisCache,
2053  set<string> &frameworksNeededToLinkToThisCache)
2054 {
2055  if (isModuleCacheInitialized && ! isModuleCacheableDataDirty)
2056  {
2057  cacheableModulesAndDependencies = moduleCacheContents;
2058  dylibsNeededToLinkToThisCache = moduleCacheDylibs;
2059  frameworksNeededToLinkToThisCache = moduleCacheFrameworks;
2060  return;
2061  }
2062 
2063  VuoCompilerTargetSet compositionTargets;
2064  compositionTargets.restrictToCurrentOperatingSystemVersion();
2065 
2066  // Include all modules…
2067  map<string, VuoCompilerModule *> allModules;
2068  allModules.insert(nodeClasses.begin(), nodeClasses.end());
2069  allModules.insert(types.begin(), types.end());
2070  allModules.insert(libraryModules.begin(), libraryModules.end());
2071 
2072  set<string> dependencies;
2073  for (map<string, VuoCompilerModule *>::iterator i = allModules.begin(); i != allModules.end(); ++i)
2074  {
2075  string moduleKey = i->first;
2076  VuoCompilerModule *module = i->second;
2077 
2078 #if VUO_PRO
2079  // … except Pro modules and modules that depend on them…
2080  if (module->requiresPro())
2081  continue;
2082 #endif
2083 
2084  // … and incompatible modules.
2085  if (! module->getCompatibleTargets().isCompatibleWithAllOf(compositionTargets))
2086  continue;
2087 
2088  cacheableModulesAndDependencies.insert(moduleKey);
2089 
2090  set<string> moduleDependencies = module->getDependencies();
2091  dependencies.insert(moduleDependencies.begin(), moduleDependencies.end());
2092  }
2093 
2094  // For the built-in environment, include Vuo's core dependencies.
2095  if (builtIn && installed)
2096  {
2097  vector<string> coreDependencies = getCoreVuoDependencies();
2098  dependencies.insert(coreDependencies.begin(), coreDependencies.end());
2099  }
2100 
2101  // Include all dependencies of the included module that are located in this environment.
2102  // (All modules are already included, so we only need to handle non-module dependencies.)
2103  for (set<string>::iterator i = dependencies.begin(); i != dependencies.end(); ++i)
2104  {
2105  string dependency = *i;
2106  if (allModules.find(dependency) == allModules.end())
2107  {
2108  if (VuoStringUtilities::endsWith(dependency, ".framework"))
2109  frameworksNeededToLinkToThisCache.insert(dependency);
2110  else
2111  {
2112  string dependencyPath = VuoCompiler::getLibraryPath(dependency, librarySearchPaths);
2113  if (! dependencyPath.empty())
2114  {
2115  if (VuoStringUtilities::endsWith(dependencyPath, ".dylib"))
2116  dylibsNeededToLinkToThisCache.insert(dependencyPath);
2117  else
2118  cacheableModulesAndDependencies.insert(dependency);
2119  }
2120  }
2121  }
2122  }
2123 
2124  moduleCacheSuffix = (installed ? "installed" : "generated");
2125  dylibsNeededToLinkToThisCache.insert(moduleCachePath + "/libVuoModuleCache-" + moduleCacheSuffix + ".dylib");
2126 
2127  moduleCacheDylibs = dylibsNeededToLinkToThisCache;
2128  moduleCacheFrameworks = frameworksNeededToLinkToThisCache;
2129 }
2130 
2150 void VuoCompiler::Environment::useModuleCache(bool shouldUseExistingCache, VuoCompiler *compiler, set<string> cacheableModulesAndDependencies,
2151  set<string> dylibsNeededToLinkToCaches, set<string> frameworksNeededToLinkToCaches)
2152 {
2153  // Ignore the cache if the `prelinkCache` preference is false.
2154 
2155  static dispatch_once_t checked = 0;
2156  static bool prelinkCache = true;
2157  dispatch_once(&checked, ^{
2158  Boolean valid;
2159  bool result = CFPreferencesGetAppBooleanValue(CFSTR("prelinkCache"), CFSTR("org.vuo.Editor"), &valid);
2160  if (valid)
2161  prelinkCache = result;
2162  });
2163  if (! prelinkCache)
2164  {
2165  VDebugLog("Ignoring the module cache since the 'prelinkCache' preference is false.");
2166  return;
2167  }
2168 
2169  // Don't do anything if this environment doesn't have a cache configured.
2170 
2171  if (moduleCachePath.empty())
2172  return;
2173 
2174  // Don't bother rechecking the cache if the modules in this environment haven't changed.
2175 
2176  string cacheDescription = string() + "the cache of " + moduleCacheSuffix + " modules at '" + moduleCachePath + "'";
2177  if (isModuleCacheInitialized && ! isModuleCacheableDataDirty)
2178  {
2179  VDebugLog("No need to recheck %s.", cacheDescription.c_str());
2180  return;
2181  }
2182 
2183  try
2184  {
2185  VDebugLog("Checking if %s is up-to-date…", cacheDescription.c_str());
2186 
2187  bool isCacheUpToDate = true;
2188 
2189  if (isModuleCacheInitialized && isModuleCacheableDataDirty)
2190  isCacheUpToDate = false;
2191 
2192  isModuleCacheInitialized = true;
2193  isModuleCacheableDataDirty = false;
2194 
2195  const string dylibFileName = "libVuoModuleCache-" + moduleCacheSuffix + ".dylib";
2196  const string indexFileName = "moduleCache-" + moduleCacheSuffix + ".txt";
2197  string dylibPath = moduleCachePath + "/" + dylibFileName;
2198  string indexPath = moduleCachePath + "/" + indexFileName;
2199 
2200  // Create the cache files if they don't already exist. (If they do exist, don't affect the last-modified times.)
2201 
2202  bool dylibFileExists = false;
2203  bool indexFileExists = false;
2204 
2205  bool dirExists = VuoFileUtilities::fileExists(moduleCachePath);
2206  if (dirExists)
2207  {
2208  dylibFileExists = VuoFileUtilities::fileExists(dylibPath);
2209  indexFileExists = VuoFileUtilities::fileExists(indexPath);
2210  }
2211 
2212  if (! (dirExists && dylibFileExists && indexFileExists))
2213  {
2214  if (shouldUseExistingCache)
2215  throw VuoException("Trying to use the existing cache, but the cache doesn't exist.", false);
2216  else
2217  {
2218  if (! dirExists)
2219  VuoFileUtilities::makeDir(moduleCachePath);
2220  if (! indexFileExists)
2221  VuoFileUtilities::createFile(indexPath);
2222  if (! dylibFileExists)
2223  VuoFileUtilities::createFile(dylibPath);
2224 
2225  isCacheUpToDate = false;
2226  }
2227  }
2228 
2229  // Lock the cache for reading.
2230 
2231  VuoFileUtilities::File *fileForLocking;
2232  {
2233  fileForLocking = moduleCacheFileForLocking[dylibPath];
2234  if (! fileForLocking)
2235  {
2236  fileForLocking = new VuoFileUtilities::File(moduleCachePath, dylibFileName);
2237  moduleCacheFileForLocking[dylibPath] = fileForLocking;
2238  }
2239 
2240  if (!fileForLocking->lockForReading())
2241  VDebugLog("\tWarning: Couldn't lock for reading.");
2242  }
2243 
2244  // Check if the dylib looks remotely valid.
2245 
2246  if (isCacheUpToDate)
2247  {
2248  bool dylibHasData = VuoFileUtilities::fileContainsReadableData(dylibPath);
2249  if (! dylibHasData)
2250  {
2251  if (shouldUseExistingCache)
2252  throw VuoException("Trying to use the existing cache, but the cache doesn't contain readable data.", false);
2253  else
2254  isCacheUpToDate = false;
2255  }
2256  }
2257 
2258  // List the items actually in the cache, according to its index.
2259 
2260  const char separator = '\n';
2261  if (isCacheUpToDate || shouldUseExistingCache)
2262  {
2263  VuoFileUtilities::File indexFile(moduleCachePath, indexFileName);
2264  string index = indexFile.getContentsAsString();
2265  vector<string> actualIndex = VuoStringUtilities::split(index, separator);
2266 
2267  moduleCacheContents.clear();
2268  moduleCacheContents.insert(actualIndex.begin(), actualIndex.end());
2269 
2270  if (shouldUseExistingCache)
2271  {
2272  isModuleCacheAvailable = true;
2273  return;
2274  }
2275  }
2276 
2277  // Check if the list of actual items matches the list of expected items.
2278 
2279  if (isCacheUpToDate)
2280  {
2281  if (moduleCacheContents.size() != cacheableModulesAndDependencies.size())
2282  isCacheUpToDate = false;
2283  else
2284  {
2285  set<string> contentsInBoth;
2286  std::set_intersection(moduleCacheContents.begin(), moduleCacheContents.end(),
2287  cacheableModulesAndDependencies.begin(), cacheableModulesAndDependencies.end(),
2288  std::inserter(contentsInBoth, contentsInBoth.begin()));
2289 
2290  if (contentsInBoth.size() != cacheableModulesAndDependencies.size())
2291  isCacheUpToDate = false;
2292  }
2293  }
2294 
2295  // Check if the cache is newer than all of the modules in it.
2296 
2297  if (isCacheUpToDate)
2298  {
2299  unsigned long cacheLastModified = VuoFileUtilities::getFileLastModifiedInSeconds(dylibPath);
2300 
2301  for (map<string, map<string, ModuleInfo *> >::iterator i = moduleFilesAtSearchPath.begin(); i != moduleFilesAtSearchPath.end(); ++i)
2302  {
2303  for (map<string, ModuleInfo *>::iterator j = i->second.begin(); j != i->second.end(); ++j)
2304  {
2305  if (! j->second->isOlderThan(cacheLastModified))
2306  {
2307  isCacheUpToDate = false;
2308  break;
2309  }
2310  }
2311  }
2312  }
2313 
2314  // If the cache is consistent with this environment, we're done.
2315 
2316  if (isCacheUpToDate)
2317  {
2318  VDebugLog("Up-to-date.");
2319 
2320  isModuleCacheAvailable = true;
2321  return;
2322  }
2323 
2324  // Otherwise, (re)build the cache.
2325 
2326  dispatch_async(moduleCacheBuildingQueue, ^{
2327  VDebugLog("Rebuilding %s…", cacheDescription.c_str());
2328 
2329  set<Module *> modulesToLink;
2330  set<string> librariesToLink;
2331  set<string> frameworksToLink;
2332  {
2333  compiler->getLinkerInputs(cacheableModulesAndDependencies, Optimization_SmallBinary, modulesToLink, librariesToLink, frameworksToLink);
2334 
2335  librariesToLink.insert(dylibsNeededToLinkToCaches.begin(), dylibsNeededToLinkToCaches.end());
2336  set<string>::iterator iter = librariesToLink.find(dylibPath); // getCacheableModulesAndDependencies includes dylibPath, but don't want to link against self
2337  if (iter != librariesToLink.end())
2338  librariesToLink.erase(iter);
2339 
2340  frameworksToLink.insert(frameworksNeededToLinkToCaches.begin(), frameworksNeededToLinkToCaches.end());
2341  }
2342 
2343  bool gotLockForWriting = false;
2344  try
2345  {
2346  // Try to upgrade the file lock for writing.
2347  gotLockForWriting = fileForLocking->lockForWriting(true);
2348  if (! gotLockForWriting)
2349  throw VuoException("The cache file is being used by another process. "
2350  "If any composition windows are open from previous Vuo sessions, quit them. "
2351  "If any processes whose names start with \"VuoComposition\" or one of your composition file names appear in Activity Monitor, force-quit them.",
2352  false);
2353 
2354  // Link the dependencies to create a temporary file.
2355  string dir, file, ext;
2356  VuoFileUtilities::splitPath(dylibFileName, dir, file, ext);
2357  string tmpPath = VuoFileUtilities::makeTmpFile(file, "dylib");
2358  compiler->link(tmpPath, modulesToLink, librariesToLink, frameworksToLink, true);
2359 
2360  // Copy the contents of the temporary file into the cached resources dylib.
2361  // This preserves the lock on the cached resources dylib file (https://b33p.net/kosada/node/12970).
2362  VuoFileUtilities::copyFile(tmpPath, dylibPath, true);
2364 
2366  getVuoFrameworkPath() + "/Helpers/install_name_tool",
2367  "-id",
2368  dylibPath,
2369  dylibPath,
2370  });
2371 
2372  // Write the list of dependencies to the index file.
2373  vector<string> expectedContents(cacheableModulesAndDependencies.begin(), cacheableModulesAndDependencies.end());
2374  string index = VuoStringUtilities::join(expectedContents, separator);
2375  VuoFileUtilities::writeStringToFile(index, indexPath);
2376 
2377  // Downgrade the file lock back to reading.
2378  if (!fileForLocking->lockForReading())
2379  VDebugLog("\tWarning: Couldn't downgrade the lock back to reading.");
2380 
2381  dispatch_sync(environmentQueue, ^{
2382  moduleCacheContents = cacheableModulesAndDependencies;
2383  isModuleCacheAvailable = true;
2384  });
2385  }
2386  catch (VuoException &e)
2387  {
2388  // Downgrade the file lock back to reading.
2389  if (gotLockForWriting)
2390  if (!fileForLocking->lockForReading())
2391  VDebugLog("\tWarning: Couldn't downgrade the lock back to reading.");
2392 
2393  VUserLog("Warning: Couldn't rebuild %s for the \"faster build\" optimization: %s", cacheDescription.c_str(), e.what());
2394  }
2395 
2396  VDebugLog("Done.");
2397  });
2398  }
2399  catch (VuoException &e)
2400  {
2401  VUserLog("Warning: Couldn't use %s for the \"faster build\" optimization: %s", cacheDescription.c_str(), e.what());
2402  }
2403 }
2404 
2408 void VuoCompiler::Environment::waitForModuleCachesToBuild(void)
2409 {
2410  dispatch_sync(moduleCacheBuildingQueue, ^{});
2411 }
2412 
2422 bool VuoCompiler::Environment::findInModuleCache(const string &moduleOrDependency, string &cachePath)
2423 {
2424  if (isModuleCacheAvailable && moduleCacheContents.find(moduleOrDependency) != moduleCacheContents.end())
2425  {
2426  cachePath = moduleCachePath + "/libVuoModuleCache-" + moduleCacheSuffix + ".dylib";
2427  return true;
2428  }
2429 
2430  return false;
2431 }
2432 
2439 void VuoCompiler::Environment::modulesChanged(void)
2440 {
2441  isModuleCacheableDataDirty = true;
2442  isModuleCacheAvailable = false;
2443 }
2444 
2448 bool VuoCompiler::Environment::isBuiltIn()
2449 {
2450  return this == sharedEnvironments[0][0];
2451 }
2452 
2456 string VuoCompiler::Environment::getName()
2457 {
2458  if (isBuiltIn())
2459  return "builtin";
2460  else if (this == sharedEnvironments[0][1])
2461  return "builtin (generated)";
2462  else if (this == sharedEnvironments[1][0])
2463  return "system";
2464  else if (this == sharedEnvironments[1][1])
2465  return "system (generated)";
2466  else if (this == sharedEnvironments[2][0])
2467  return "user";
2468  else if (this == sharedEnvironments[2][1])
2469  return "user (generated)";
2470  return "composition-local";
2471 }
2472 
2476 void VuoCompiler::applyToInstalledEnvironments(void (^doForEnvironment)(Environment *))
2477 {
2478  dispatch_sync(environmentQueue, ^{
2479  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i) {
2480  doForEnvironment((*i)[0]);
2481  }
2482  });
2483 }
2484 
2488 void VuoCompiler::applyToInstalledEnvironments(void (^doForEnvironment)(Environment *, bool *, string), bool *result, string arg)
2489 {
2490  dispatch_sync(environmentQueue, ^{
2491  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i) {
2492  doForEnvironment((*i)[0], result, arg);
2493  }
2494  });
2495 }
2496 
2500 void VuoCompiler::applyToAllEnvironments(void (^doForEnvironment)(Environment *environment))
2501 {
2502  dispatch_sync(environmentQueue, ^{
2503  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i) {
2504  for (vector<Environment *>::iterator j = i->begin(); j != i->end(); ++j) {
2505  doForEnvironment(*j);
2506  }
2507  }
2508  });
2509 }
2510 
2520 VuoCompiler::VuoCompiler(const string &compositionPath)
2521 {
2522 #if VUO_PRO
2523  init_Pro();
2524 #endif
2525 
2526  shouldLoadAllModules = true;
2527  hasLoadedAllModules = false;
2528  modulesToLoadQueue = dispatch_queue_create("org.vuo.compiler.modules", NULL);
2529  moduleCacheBuilding = dispatch_group_create();
2530  dependencyGraph = NULL;
2531  compositionDependencyGraph = NULL;
2532  isVerbose = false;
2533  _shouldShowSplashWindow = false;
2534  delegate = NULL;
2535  delegateQueue = dispatch_queue_create("org.vuo.compiler.delegate", NULL);
2536 
2537  string vuoFrameworkPath = getVuoFrameworkPath();
2538  if (! vuoFrameworkPath.empty())
2539  clangPath = vuoFrameworkPath + "/Helpers/clang";
2540  else
2541  clangPath = llvm::sys::Path(StringRef(LLVM_ROOT "/bin/clang"));
2542 
2543  dispatch_sync(environmentQueue, ^{
2544  allCompilers.insert(this);
2545 
2546  if (sharedEnvironments.empty())
2547  {
2548  sharedEnvironments = vector< vector<Environment *> >(3, vector<Environment *>(2, NULL));
2549  for (vector< vector<Environment *> >::iterator i = sharedEnvironments.begin(); i != sharedEnvironments.end(); ++i) {
2550  for (vector<Environment *>::iterator j = i->begin(); j != i->end(); ++j) {
2551  *j = new Environment();
2552  }
2553  }
2554 
2555  vector<string> builtInModuleSearchPaths = Environment::getBuiltInModuleSearchPaths();
2556  for (vector<string>::iterator i = builtInModuleSearchPaths.begin(); i != builtInModuleSearchPaths.end(); ++i) {
2557  sharedEnvironments[0][0]->addModuleSearchPath(*i, false);
2558  }
2559 
2560  vector<string> builtInHeaderSearchPaths = Environment::getBuiltInHeaderSearchPaths();
2561  for (vector<string>::iterator i = builtInHeaderSearchPaths.begin(); i != builtInHeaderSearchPaths.end(); ++i) {
2562  sharedEnvironments[0][0]->addHeaderSearchPath(*i);
2563  }
2564 
2565  vector<string> builtInLibrarySearchPaths = Environment::getBuiltInLibrarySearchPaths();
2566  for (vector<string>::iterator i = builtInLibrarySearchPaths.begin(); i != builtInLibrarySearchPaths.end(); ++i) {
2567  sharedEnvironments[0][0]->addLibrarySearchPath(*i);
2568  }
2569 
2570  vector<string> builtInFrameworkSearchPaths = Environment::getBuiltInFrameworkSearchPaths();
2571  for (vector<string>::iterator i = builtInFrameworkSearchPaths.begin(); i != builtInFrameworkSearchPaths.end(); ++i) {
2572  sharedEnvironments[0][0]->addFrameworkSearchPath(*i);
2573  }
2574 
2575  // Allow system administrator to override Vuo.framework modules
2576  sharedEnvironments[1][0]->addModuleSearchPath(VuoFileUtilities::getSystemModulesPath());
2577  sharedEnvironments[1][0]->addLibrarySearchPath(VuoFileUtilities::getSystemModulesPath());
2578 
2579  // Allow user to override Vuo.framework and system-wide modules
2580  sharedEnvironments[2][0]->addModuleSearchPath(VuoFileUtilities::getUserModulesPath());
2581  sharedEnvironments[2][0]->addLibrarySearchPath(VuoFileUtilities::getUserModulesPath());
2582 
2583  // Set up module cache paths.
2584  // Since the built-in module caches are part of Vuo.framework (put there by `generateBuiltInModuleCaches`),
2585  // only attempt to use module caches if Vuo.framework exists.
2586  string vuoFrameworkPath = getVuoFrameworkPath();
2587  if (! vuoFrameworkPath.empty())
2588  {
2589  vector<string> moduleCachePaths(3);
2590  moduleCachePaths[0] = vuoFrameworkPath + "/Modules/Builtin";
2591  moduleCachePaths[1] = VuoFileUtilities::getCachePath() + "/System";
2592  moduleCachePaths[2] = VuoFileUtilities::getCachePath() + "/User";
2593 
2594  for (size_t i = 0; i < moduleCachePaths.size(); ++i)
2595  {
2596  string moduleCachePath = moduleCachePaths[i];
2597 
2598  sharedEnvironments[i][0]->setModuleCachePath(moduleCachePath);
2599  sharedEnvironments[i][0]->addModuleSearchPath(moduleCachePath + "/Modules", false);
2600 
2601  sharedEnvironments[i][1]->setModuleCachePath(moduleCachePath);
2602  }
2603  }
2604  }
2605  });
2606 
2607  setCompositionPath(compositionPath);
2608 }
2609 
2614 {
2615 #if VUO_PRO
2616  fini_Pro();
2617 #endif
2618 
2619  dispatch_sync(environmentQueue, ^{
2620  allCompilers.erase(this);
2621  });
2622 
2623  dispatch_group_wait(moduleCacheBuilding, DISPATCH_TIME_FOREVER);
2624  dispatch_release(moduleCacheBuilding);
2625 
2626  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
2627  (*i)[0]->removeCompilerToNotify(this);
2628 
2629  dispatch_sync(delegateQueue, ^{});
2630 
2631  dispatch_release(modulesToLoadQueue);
2632  dispatch_release(delegateQueue);
2633 
2634  delete dependencyGraph;
2635  delete compositionDependencyGraph;
2636 }
2637 
2641 void VuoCompiler::reset(void)
2642 {
2643  dispatch_group_wait(moduleSourceCompilersExist, DISPATCH_TIME_FOREVER);
2644 
2645  dispatch_sync(environmentQueue, ^{
2646  for (vector< vector<Environment *> >::iterator i = sharedEnvironments.begin(); i != sharedEnvironments.end(); ++i)
2647  {
2648  (*i)[0]->stopWatchingModuleSearchPaths();
2649  dispatch_sync((*i)[0]->moduleSearchPathContentsChangedQueue, ^{});
2650  }
2651 
2652  for (map< string, vector<Environment *> >::iterator i = environmentsForCompositionFamily.begin(); i != environmentsForCompositionFamily.end(); ++i)
2653  {
2654  (i->second)[0]->stopWatchingModuleSearchPaths();
2655  dispatch_sync((i->second)[0]->moduleSearchPathContentsChangedQueue, ^{});
2656  }
2657 
2658  for (vector< vector<Environment *> >::iterator i = sharedEnvironments.begin(); i != sharedEnvironments.end(); ++i)
2659  for (vector<Environment *>::iterator j = i->begin(); j != i->end(); ++j)
2660  delete *j;
2661 
2662  for (map< string, vector<Environment *> >::iterator i = environmentsForCompositionFamily.begin(); i != environmentsForCompositionFamily.end(); ++i)
2663  for (vector<Environment *>::iterator j = i->second.begin(); j != i->second.end(); ++j)
2664  delete *j;
2665 
2666  allCompilers.clear();
2667  sharedEnvironments.clear();
2668  environmentsForCompositionFamily.clear();
2669  });
2670 }
2671 
2678 {
2679  dispatch_async(delegateQueue, ^{
2680  this->delegate = delegate;
2681  });
2682 }
2683 
2693 void VuoCompiler::setCompositionPath(const string &compositionPath)
2694 {
2695  string compositionModulesDir;
2696  string compositionBaseDir;
2697  lastCompositionIsSubcomposition = false;
2698  if (! compositionPath.empty())
2699  {
2700  compositionModulesDir = VuoFileUtilities::getCompositionLocalModulesPath(compositionPath);
2701 
2702  string file, ext;
2703  VuoFileUtilities::splitPath(compositionModulesDir, compositionBaseDir, file, ext);
2704  VuoFileUtilities::canonicalizePath(compositionBaseDir);
2705 
2706  string compositionDir;
2707  VuoFileUtilities::splitPath(compositionPath, compositionDir, file, ext);
2708  VuoFileUtilities::canonicalizePath(compositionDir);
2709  lastCompositionIsSubcomposition = (compositionDir == compositionModulesDir);
2710  }
2711 
2712  // Set up `environments` to contain all environments available to this compiler, in order from broadest to narrowest.
2713 
2714  dispatch_sync(environmentQueue, ^{
2715  if (! environments.empty() && compositionBaseDir == lastCompositionBaseDir) {
2716  return;
2717  }
2718  lastCompositionBaseDir = compositionBaseDir;
2719 
2720  // Clear out the existing environments for this compiler.
2721 
2722  Environment *oldCompositionFamilyInstalledEnvironment = nullptr;
2723  vector<Environment *> compositionEnvironments;
2724  if (! environments.empty())
2725  {
2726  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i) {
2727  (*i)[0]->removeCompilerToNotify(this);
2728  }
2729 
2730  if (environments.size() >= 5) {
2731  oldCompositionFamilyInstalledEnvironment = environments[3][0];
2732  }
2733 
2734  compositionEnvironments = environments.back();
2735 
2736  environments.clear();
2737  }
2738 
2739  // If the composition is located in one of the shared environments (built-in, system, user),
2740  // add that shared environment and any shared environments at broader scope to the compiler.
2741  // If the composition is not in a shared environment, add all of the shared environments to the compiler.
2742 
2743  bool isCompositionInSharedEnvironment = false;
2744  for (vector< vector<Environment *> >::iterator i = sharedEnvironments.begin(); i != sharedEnvironments.end(); ++i)
2745  {
2746  environments.push_back(*i);
2747 
2748  vector<string> moduleSearchPaths = (*i)[0]->getModuleSearchPaths();
2749  for (vector<string>::iterator j = moduleSearchPaths.begin(); j != moduleSearchPaths.end(); ++j)
2750  {
2751  string moduleSearchPath = *j;
2752  VuoFileUtilities::canonicalizePath(moduleSearchPath);
2753  if (moduleSearchPath == compositionModulesDir)
2754  {
2755  isCompositionInSharedEnvironment = true;
2756  break;
2757  }
2758  }
2759 
2760  if (isCompositionInSharedEnvironment) {
2761  break;
2762  }
2763  }
2764 
2765  // If the composition is not in a shared environment, add the composition-family environment to the compiler.
2766 
2767  Environment *newCompositionFamilyInstalledEnvironment = nullptr;
2768  if (! isCompositionInSharedEnvironment && ! compositionPath.empty())
2769  {
2770  vector<Environment *> compositionFamilyEnvironments = environmentsForCompositionFamily[compositionBaseDir];
2771  if (compositionFamilyEnvironments.empty())
2772  {
2773  compositionFamilyEnvironments = vector<Environment *>(2, NULL);
2774  compositionFamilyEnvironments[0] = new Environment();
2775  compositionFamilyEnvironments[1] = new Environment();
2776  environmentsForCompositionFamily[compositionBaseDir] = compositionFamilyEnvironments;
2777 
2778  // Allow the user to place modules/subcompositions in a Modules folder inside the composition folder.
2779 
2780  compositionFamilyEnvironments[0]->addModuleSearchPath(compositionModulesDir);
2781 
2782  // Locate this environment's cache in a folder whose name is the composition folder path with
2783  // slashes replaced with Unicode Modifier Letter Colon.
2784  string moduleCachePath = getCachePathForComposition(compositionBaseDir);
2785 
2786  compositionFamilyEnvironments[0]->setModuleCachePath(moduleCachePath);
2787  compositionFamilyEnvironments[0]->addModuleSearchPath(moduleCachePath + "/Modules", false);
2788 
2789  compositionFamilyEnvironments[1]->setModuleCachePath(moduleCachePath);
2790  }
2791  environments.push_back(compositionFamilyEnvironments);
2792 
2793  newCompositionFamilyInstalledEnvironment = compositionFamilyEnvironments[0];
2794  }
2795 
2796  // Add the composition environment to the compiler (or add it back in if it already existed).
2797 
2798  if (compositionEnvironments.empty())
2799  {
2800  compositionEnvironments = vector<Environment *>(2, NULL);
2801  compositionEnvironments[0] = new Environment();
2802  compositionEnvironments[1] = new Environment();
2803  }
2804  environments.push_back(compositionEnvironments);
2805 
2806  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i) {
2807  (*i)[0]->addCompilerToNotify(this);
2808  }
2809 
2810  delete dependencyGraph;
2811  delete compositionDependencyGraph;
2812  dependencyGraph = makeDependencyNetwork(environments, ^VuoDirectedAcyclicGraph * (Environment *env) { return env->getDependencyGraph(); });
2813  compositionDependencyGraph = makeDependencyNetwork(environments, ^VuoDirectedAcyclicGraph * (Environment *env) { return env->getCompositionDependencyGraph(); });
2814 
2815  // If the compiler has a different local Modules directory than before, notify the compiler's delegate
2816  // of composition-family modules that are newly available/unavailable.
2817 
2818  if (oldCompositionFamilyInstalledEnvironment != newCompositionFamilyInstalledEnvironment)
2819  {
2820  auto getModules = [] (Environment *env)
2821  {
2822  map<string, VuoCompilerModule *> modules;
2823  if (env)
2824  {
2825  for (auto i : env->getNodeClasses()) {
2826  modules.insert(i);
2827  }
2828  for (auto i : env->getTypes()) {
2829  modules.insert(i);
2830  }
2831  for (auto i : env->getLibraryModules()) {
2832  modules.insert(i);
2833  }
2834  }
2835  return modules;
2836  };
2837 
2838  map<string, VuoCompilerModule *> modulesAdded = getModules(newCompositionFamilyInstalledEnvironment);
2839  map<string, VuoCompilerModule *> modulesRemoved = getModules(oldCompositionFamilyInstalledEnvironment);
2840 
2841  map<string, pair<VuoCompilerModule *, VuoCompilerModule *> > modulesModified;
2842  for (map<string, VuoCompilerModule *>::iterator add = modulesAdded.begin(); add != modulesAdded.end(); )
2843  {
2844  map<string, VuoCompilerModule *>::iterator rem = modulesRemoved.find(add->first);
2845  if (rem != modulesRemoved.end())
2846  {
2847  modulesModified[add->first] = make_pair(rem->second, add->second);
2848  modulesAdded.erase(add++);
2849  modulesRemoved.erase(rem);
2850  }
2851  else
2852  {
2853  ++add;
2854  }
2855  }
2856 
2857  if (! (modulesAdded.empty() && modulesModified.empty() && modulesRemoved.empty()) )
2858  {
2859  VuoCompilerIssues *issues = new VuoCompilerIssues();
2860  VuoCompilerDelegate::LoadedModulesData *delegateData = new VuoCompilerDelegate::LoadedModulesData(set< pair<VuoCompilerModule *, VuoCompilerModule *> >(), set<VuoCompilerModule *>(), issues);
2861  delegateData->retain();
2862 
2863  Environment *scopeEnvironment = newCompositionFamilyInstalledEnvironment;
2864  if (! scopeEnvironment) {
2865  scopeEnvironment = compositionEnvironments.at(0);
2866  }
2867 
2868  loadedModules(modulesAdded, modulesModified, modulesRemoved, issues, delegateData, scopeEnvironment);
2869  }
2870  }
2871  });
2872 }
2873 
2879 VuoDirectedAcyclicNetwork * VuoCompiler::makeDependencyNetwork(const vector< vector<Environment *> > &environments,
2880  VuoDirectedAcyclicGraph * (^graphForEnvironment)(Environment *))
2881 {
2882  if (!graphForEnvironment)
2883  return NULL;
2884 
2886 
2887  for (vector< vector<Environment *> >::const_iterator i = environments.begin(); i != environments.end(); ++i)
2888  {
2889  // Installed environment depends on generated environment in same scope.
2890 
2891  network->addEdge(graphForEnvironment(i->at(0)), graphForEnvironment(i->at(1)));
2892 
2893  // Installed and generated environments depend on installed environments in broader scopes.
2894 
2895  for (vector< vector<Environment *> >::const_iterator ii = environments.begin(); ii != i; ++ii)
2896  {
2897  network->addEdge(graphForEnvironment(i->at(0)), graphForEnvironment(ii->at(0)));
2898  network->addEdge(graphForEnvironment(i->at(1)), graphForEnvironment(ii->at(0)));
2899  }
2900  }
2901 
2902  return network;
2903 }
2904 
2925 void VuoCompiler::loadModulesIfNeeded(const set<string> &moduleKeys)
2926 {
2927  __block bool willLoadAllModules = false;
2928  if (moduleKeys.empty())
2929  {
2930  dispatch_sync(modulesToLoadQueue, ^{
2931  if (shouldLoadAllModules && ! hasLoadedAllModules) {
2932  willLoadAllModules = true;
2933  hasLoadedAllModules = true;
2934  }
2935  });
2936  }
2937 
2938  if (! willLoadAllModules && moduleKeys.empty())
2939  return;
2940 
2941  // Load modules and start sources compiling.
2942 
2943  __block set<dispatch_group_t> sourcesLoading;
2944  dispatch_sync(environmentQueue, ^{
2945  sourcesLoading = loadModulesAndSources(moduleKeys, set<string>(), set<string>(),
2946  moduleKeys, set<string>(), set<string>(),
2947  willLoadAllModules, false, nullptr, nullptr, nullptr, "");
2948  });
2949 
2950  // Wait for sources to finish compiling and their modules to be loaded,
2951  // to ensure that `getNodeClass(subcomposition)` finds the subcomposition node class.
2952 
2953  for (set<dispatch_group_t>::iterator i = sourcesLoading.begin(); i != sourcesLoading.end(); ++i)
2954  {
2955  dispatch_group_wait(*i, DISPATCH_TIME_FOREVER);
2956  dispatch_release(*i);
2957  }
2958 }
2959 
2968 set<dispatch_group_t> VuoCompiler::loadModulesAndSources(const set<string> &modulesAddedKeys, const set<string> &modulesModifiedKeys, const set<string> &modulesRemovedKeys,
2969  const set<string> &sourcesAddedKeys, const set<string> &sourcesModifiedKeys, const set<string> &sourcesRemovedKeys,
2970  bool willLoadAllModules, bool shouldRecompileSourcesIfUnchanged,
2971  Environment *currentEnvironment, VuoCompilerIssues *issuesForCurrentEnvironment,
2972  std::function<void(void)> moduleLoadedCallback, const string &moduleAddedOrModifiedSourceCode)
2973 {
2974  //VLog("C=%p E=%p -- %lu %lu %lu %lu %lu %lu %i %i", this, currentEnvironment,
2975  //modulesAddedKeys.size(), modulesModifiedKeys.size(), modulesRemovedKeys.size(),
2976  //sourcesAddedKeys.size(), sourcesModifiedKeys.size(), sourcesRemovedKeys.size(),
2977  //willLoadAllModules, shouldRecompileSourcesIfUnchanged);
2978  //if (modulesAddedKeys.size() == 1) VLog(" %s", modulesAddedKeys.begin()->c_str());
2979  //if (modulesModifiedKeys.size() == 1) VLog(" %s", modulesModifiedKeys.begin()->c_str());
2980  //if (modulesRemovedKeys.size() == 1) VLog(" %s", modulesRemovedKeys.begin()->c_str());
2981 
2982  // Organize the modules, source files, and issues by environment.
2983 
2984  map<Environment *, set<string> > modulesAdded;
2985  map<Environment *, set<string> > modulesModified;
2986  map<Environment *, set<string> > modulesRemoved;
2987  map<Environment *, set<string> > sourcesAdded;
2988  map<Environment *, set<string> > sourcesModified;
2989  map<Environment *, set<string> > sourcesRemoved;
2990  map<Environment *, set<string> > potentialSpecializedModules;
2991 
2992  if (currentEnvironment)
2993  {
2994  modulesAdded[currentEnvironment] = modulesAddedKeys;
2995  modulesModified[currentEnvironment] = modulesModifiedKeys;
2996  modulesRemoved[currentEnvironment] = modulesRemovedKeys;
2997  sourcesAdded[currentEnvironment] = sourcesAddedKeys;
2998  sourcesModified[currentEnvironment] = sourcesModifiedKeys;
2999  sourcesRemoved[currentEnvironment] = sourcesRemovedKeys;
3000  }
3001  else
3002  {
3003  Environment *genEnv = nullptr;
3004  if (! willLoadAllModules)
3005  {
3006  // If compiling a top-level composition, generated modules that were directly requested (in modulesAddedKeys) are
3007  // added at the composition scope so they won't be cached. This prevents link errors when running multiple
3008  // compositions in the current process (https://b33p.net/kosada/node/14317).
3009 
3010  int scope = environments.size() - (lastCompositionIsSubcomposition ? 2 : 1);
3011  genEnv = environments.at(scope).at(1);
3012  potentialSpecializedModules[genEnv] = modulesAddedKeys;
3013  }
3014 
3015  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3016  {
3017  Environment *env = (*i).at(0);
3018 
3019  ModuleInfoIterator modulesAddedIter = (willLoadAllModules ? env->listAllModules() : env->listModules(modulesAddedKeys));
3020  ModuleInfoIterator sourcesAddedIter = (willLoadAllModules ? env->listAllSourceFiles() : env->listSourceFiles(sourcesAddedKeys));
3021  ModuleInfoIterator sourcesModifiedIter = (willLoadAllModules ? env->listAllSourceFiles() : env->listSourceFiles(sourcesModifiedKeys));
3022 
3023  ModuleInfo *moduleInfo;
3024  while ((moduleInfo = modulesAddedIter.next()))
3025  {
3026  string moduleKey = moduleInfo->getModuleKey();
3027 
3028  modulesAdded[env].insert(moduleKey);
3029 
3030  if (! willLoadAllModules)
3031  {
3032  auto foundIter = potentialSpecializedModules[genEnv].find(moduleKey);
3033  if (foundIter != potentialSpecializedModules[genEnv].end())
3034  potentialSpecializedModules[genEnv].erase(foundIter);
3035  }
3036  }
3037 
3038  // If a source file and a compiled file for the same module are in the same folder,
3039  // the compiled file takes precedence.
3040  auto isCompiledModuleAtSameSearchPath = [&env] (ModuleInfo *sourceInfo)
3041  {
3042  ModuleInfo *compiledModuleInfo = env->listModule(sourceInfo->getModuleKey());
3043  return (compiledModuleInfo && compiledModuleInfo->getSearchPath() == sourceInfo->getSearchPath());
3044  };
3045 
3046  while ((moduleInfo = sourcesAddedIter.next()))
3047  {
3048  if (isCompiledModuleAtSameSearchPath(moduleInfo))
3049  continue;
3050 
3051  sourcesAdded[env].insert( moduleInfo->getModuleKey() );
3052  }
3053 
3054  while ((moduleInfo = sourcesModifiedIter.next()))
3055  {
3056  if (isCompiledModuleAtSameSearchPath(moduleInfo))
3057  continue;
3058 
3059  sourcesModified[env].insert( moduleInfo->getModuleKey() );
3060  }
3061  }
3062  }
3063 
3064  map<Environment *, VuoCompilerIssues *> issues;
3065  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3066  {
3067  Environment *env = (*i).at(0);
3068  issues[env] = (env == currentEnvironment && issuesForCurrentEnvironment ? issuesForCurrentEnvironment : new VuoCompilerIssues());
3069  }
3070 
3071  // Check for circular dependencies in added/modified sources.
3072 
3073  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3074  {
3075  Environment *env = (*i).at(0);
3076 
3077  // Check for circular dependencies involving sources being loaded within this environment.
3078  // For circular dependencies involving sources in different environments,
3079  // an error will be reported elsewhere due to one of them being outside of the other's scope.
3080  set<VuoDirectedAcyclicGraph::Vertex *> circularDependencies = env->getCompositionDependencyGraph()->getCycleVertices();
3081 
3082  set<string> sourcesAddedModified;
3083  sourcesAddedModified.insert(sourcesAdded[env].begin(), sourcesAdded[env].end());
3084  sourcesAddedModified.insert(sourcesModified[env].begin(), sourcesModified[env].end());
3085 
3086  for (set<string>::iterator j = sourcesAddedModified.begin(); j != sourcesAddedModified.end(); ++j)
3087  {
3088  string moduleKey = *j;
3089 
3090  bool found = false;
3091  for (set<VuoDirectedAcyclicGraph::Vertex *>::iterator k = circularDependencies.begin(); k != circularDependencies.end(); ++k)
3092  {
3093  DependencyGraphVertex *vertex = static_cast<DependencyGraphVertex *>(*k);
3094  if (vertex->getDependency() == moduleKey)
3095  {
3096  found = true;
3097  break;
3098  }
3099  }
3100 
3101  if (found)
3102  {
3103  sourcesAdded[env].erase(moduleKey);
3104  sourcesModified[env].erase(moduleKey);
3105 
3106  VuoCompilerIssue issue(VuoCompilerIssue::Error, "compiling subcomposition", moduleKey,
3107  "Subcomposition contains itself",
3108  "%moduleKey contains an instance of itself, "
3109  "or contains another subcomposition that contains an instance of %moduleKey.");
3110  issue.setModuleKey(moduleKey);
3111 
3112  ModuleInfo *sourceInfo = env->listSourceFile(moduleKey);
3113  if (sourceInfo)
3114  issue.setFilePath(sourceInfo->getFile()->path());
3115 
3116  issues[env]->append(issue);
3117  }
3118  }
3119  }
3120 
3121  // Update the longest downstream paths for added/modified sources.
3122 
3123  for (const vector<Environment *> &envs : environments)
3124  {
3125  Environment *env = envs.at(0);
3126 
3127  set<string> sourcesAddedModified;
3128  sourcesAddedModified.insert(sourcesAdded[env].begin(), sourcesAdded[env].end());
3129  sourcesAddedModified.insert(sourcesModified[env].begin(), sourcesModified[env].end());
3130 
3131  for (const string &moduleKey : sourcesAddedModified)
3132  {
3133  VuoDirectedAcyclicGraph::Vertex *vertex = env->getCompositionDependencyGraph()->findVertex(moduleKey);
3134  int pathLength = env->getCompositionDependencyGraph()->getLongestDownstreamPath(vertex);
3135 
3136  ModuleInfo *sourceInfo = env->listSourceFile(moduleKey);
3137  sourceInfo->setLongestDownstreamPath(pathLength);
3138  }
3139  }
3140 
3141  // Find all modules and sources that depend on the modules and sources being modified or removed.
3142  // Those that belong to one of this compiler's environments are used in subsequent stages.
3143  // Those that belong to some other environment are scheduled to be handled by a separate call to this function.
3144 
3145  map<Environment *, set<string> > modulesDepOnModulesModified;
3146  map<Environment *, set<string> > sourcesDepOnModulesModified;
3147  map<Environment *, set<string> > modulesDepOnModulesRemoved;
3148  map<Environment *, set<string> > sourcesDepOnModulesRemoved;
3149 
3150  {
3151  __block map<Environment *, set<string> > modulesDepOnModulesModified_otherCompiler;
3152  __block map<Environment *, set<string> > sourcesDepOnModulesModified_otherCompiler;
3153  __block map<Environment *, set<string> > modulesDepOnModulesRemoved_otherCompiler;
3154  __block map<Environment *, set<string> > sourcesDepOnModulesRemoved_otherCompiler;
3155 
3156  vector<VuoDirectedAcyclicNetwork *> searchDependencyGraphs;
3157  for (VuoCompiler *compiler : allCompilers)
3158  searchDependencyGraphs.push_back(compiler->dependencyGraph);
3159 
3160  VuoDirectedAcyclicGraph *currentEnvironmentDependencyGraph = (currentEnvironment ? currentEnvironment->getDependencyGraph() : nullptr);
3161 
3162  findDependentModulesAndSources(modulesModified, searchDependencyGraphs, currentEnvironmentDependencyGraph,
3163  modulesDepOnModulesModified, modulesDepOnModulesModified_otherCompiler,
3164  sourcesDepOnModulesModified, sourcesDepOnModulesModified_otherCompiler);
3165 
3166  findDependentModulesAndSources(modulesRemoved, searchDependencyGraphs, currentEnvironmentDependencyGraph,
3167  modulesDepOnModulesRemoved, modulesDepOnModulesRemoved_otherCompiler,
3168  sourcesDepOnModulesRemoved, sourcesDepOnModulesRemoved_otherCompiler);
3169 
3170  set<Environment *> otherEnvironments;
3171  for (map<Environment *, set<string> >::iterator i = modulesDepOnModulesModified_otherCompiler.begin(); i != modulesDepOnModulesModified_otherCompiler.end(); ++i)
3172  otherEnvironments.insert(i->first);
3173  for (map<Environment *, set<string> >::iterator i = sourcesDepOnModulesModified_otherCompiler.begin(); i != sourcesDepOnModulesModified_otherCompiler.end(); ++i)
3174  otherEnvironments.insert(i->first);
3175  for (map<Environment *, set<string> >::iterator i = modulesDepOnModulesRemoved_otherCompiler.begin(); i != modulesDepOnModulesRemoved_otherCompiler.end(); ++i)
3176  otherEnvironments.insert(i->first);
3177  for (map<Environment *, set<string> >::iterator i = sourcesDepOnModulesRemoved_otherCompiler.begin(); i != sourcesDepOnModulesRemoved_otherCompiler.end(); ++i)
3178  otherEnvironments.insert(i->first);
3179 
3180  for (Environment *env : otherEnvironments)
3181  {
3182  VuoCompiler *otherCompiler = nullptr;
3183  for (VuoCompiler *c : allCompilers)
3184  for (const vector<Environment *> &ee : c->environments)
3185  for (Environment *e : ee)
3186  if (e == env)
3187  {
3188  otherCompiler = c;
3189  goto foundOtherCompiler;
3190  }
3191  foundOtherCompiler:
3192 
3193  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
3194  dispatch_sync(environmentQueue, ^{
3195  otherCompiler->loadModulesAndSources(set<string>(), modulesDepOnModulesModified_otherCompiler[env], modulesDepOnModulesRemoved_otherCompiler[env],
3196  set<string>(), sourcesDepOnModulesModified_otherCompiler[env], sourcesDepOnModulesRemoved_otherCompiler[env],
3197  false, true, env, nullptr, nullptr, "");
3198  });
3199  });
3200  }
3201  }
3202 
3203  // Unload:
3204  // - modules that have been removed or modified
3205  // - modules that depend on them
3206 
3207  map<Environment *, set<VuoCompilerModule *> > actualModulesRemoved;
3208  for (const vector<Environment *> &envs : environments)
3209  {
3210  for (Environment *env : envs)
3211  {
3212  set<string> modulesToUnload;
3213  modulesToUnload.insert(modulesRemoved[env].begin(), modulesRemoved[env].end());
3214  modulesToUnload.insert(modulesModified[env].begin(), modulesModified[env].end());
3215  modulesToUnload.insert(modulesDepOnModulesRemoved[env].begin(), modulesDepOnModulesRemoved[env].end());
3216  modulesToUnload.insert(modulesDepOnModulesModified[env].begin(), modulesDepOnModulesModified[env].end());
3217 
3218  actualModulesRemoved[env] = env->unloadCompiledModules(modulesToUnload);
3219 
3220  if (!env->isBuiltIn() && !actualModulesRemoved[env].empty())
3221  {
3222  set<string> actualModulesRemovedKeys;
3223  for (auto m : actualModulesRemoved[env])
3224  actualModulesRemovedKeys.insert(m->getPseudoBase()->getModuleKey());
3225  VUserLog("Removed from %s environment: %s", env->getName().c_str(), VuoStringUtilities::join(actualModulesRemovedKeys, ", ").c_str());
3226  }
3227  }
3228  }
3229 
3230  // Load:
3231  // - modules that have been added or modified
3232  // - modules that they depend on
3233  // - modules that depend on them that were just unloaded
3234  // Delete:
3235  // - cached module files in `modulesToLoad` whose source files have been removed
3236 
3237  map<Environment *, set<string> > modulesToLoad;
3238  map<Environment *, map<string, string> > modulesToLoadSourceCode;
3239  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3240  {
3241  Environment *env = (*i).at(0);
3242 
3243  if (! modulesAdded[env].empty())
3244  modulesToLoad[env].insert(modulesAdded[env].begin(), modulesAdded[env].end());
3245  if (! modulesModified[env].empty())
3246  modulesToLoad[env].insert(modulesModified[env].begin(), modulesModified[env].end());
3247  if (! modulesDepOnModulesModified[env].empty())
3248  modulesToLoad[env].insert(modulesDepOnModulesModified[env].begin(), modulesDepOnModulesModified[env].end());
3249 
3250  if (env == currentEnvironment && moduleLoadedCallback)
3251  {
3252  if (modulesAdded[env].size() == 1)
3253  modulesToLoadSourceCode[env][*modulesAdded[env].begin()] = moduleAddedOrModifiedSourceCode;
3254  else if (modulesModified[env].size() == 1)
3255  modulesToLoadSourceCode[env][*modulesModified[env].begin()] = moduleAddedOrModifiedSourceCode;
3256  }
3257  }
3258 
3259  map<Environment *, set<VuoCompilerModule *> > actualModulesAdded;
3260  while (! modulesToLoad.empty())
3261  {
3262  set<string> dependenciesToLoad;
3263  map<Environment *, set<string> > potentialSpecializedDependencies;
3264  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3265  {
3266  Environment *env = (*i).at(0);
3267  Environment *genEnv = (*i).at(1);
3268 
3269  set<VuoCompilerModule *> actualModulesLoaded = env->loadCompiledModules(modulesToLoad[env], modulesToLoadSourceCode[env]);
3270 
3271  actualModulesAdded[env].insert(actualModulesLoaded.begin(), actualModulesLoaded.end());
3272  modulesToLoad.erase(env);
3273 
3274  for (set<VuoCompilerModule *>::iterator j = actualModulesLoaded.begin(); j != actualModulesLoaded.end(); ++j)
3275  {
3276  set<string> dependencies = (*j)->getDependencies();
3277  dependenciesToLoad.insert(dependencies.begin(), dependencies.end());
3278  potentialSpecializedDependencies[genEnv].insert(dependencies.begin(), dependencies.end());
3279  }
3280 
3281  if (!env->isBuiltIn() && !actualModulesLoaded.empty())
3282  {
3283  map<string, string> actualFilesAndHashesLoaded;
3284  for (auto module : actualModulesLoaded)
3285  {
3286  string path, hash;
3287  if (module->getPseudoBase()->getNodeSet())
3288  path = module->getPseudoBase()->getNodeSet()->getArchivePath();
3289  else
3290  {
3291  auto cnc = dynamic_cast<VuoCompilerNodeClass *>(module);
3292  if (cnc && !cnc->getSourcePath().empty())
3293  {
3294  path = cnc->getSourcePath();
3295  if (!cnc->getSourceCode().empty())
3296  // Use the latest source code, if it's been modified without saving.
3297  hash = VuoStringUtilities::calculateSHA256(cnc->getSourceCode());
3298  }
3299  else
3300  path = module->getModulePath();
3301  }
3302 
3303  if (hash.empty())
3304  try
3305  {
3307  }
3308  catch (VuoException &e) {}
3309 
3310  actualFilesAndHashesLoaded[path] = hash;
3311  }
3312 
3313  for (pair<string, string> item : actualFilesAndHashesLoaded)
3314  VUserLog("Loaded into %s environment: [%8.8s] %s", env->getName().c_str(), item.second.c_str(), item.first.c_str());
3315  }
3316  }
3317 
3318  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3319  {
3320  Environment *env = (*i).at(0);
3321 
3322  ModuleInfoIterator dependenciesInEnv = env->listModules(dependenciesToLoad);
3323  ModuleInfo *moduleInfo;
3324  while ((moduleInfo = dependenciesInEnv.next()))
3325  {
3326  modulesToLoad[env].insert( moduleInfo->getModuleKey() );
3327 
3328  for (map<Environment *, set<string> >::iterator j = potentialSpecializedDependencies.begin(); j != potentialSpecializedDependencies.end(); ++j)
3329  {
3330  auto foundIter = j->second.find( moduleInfo->getModuleKey() );
3331  if (foundIter != j->second.end())
3332  j->second.erase(foundIter);
3333  }
3334  }
3335  }
3336 
3337  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3338  {
3339  Environment *genEnv = (*i).at(1);
3340  potentialSpecializedModules[genEnv].insert(potentialSpecializedDependencies[genEnv].begin(), potentialSpecializedDependencies[genEnv].end());
3341  }
3342  }
3343 
3344  // Load asynchronously:
3345  // - specializations of generic modules
3346 
3347  set<dispatch_group_t> specializedModulesLoading;
3348  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3349  {
3350  Environment *genEnv = (*i).at(1);
3351  set<dispatch_group_t> s = genEnv->loadSpecializedModules(potentialSpecializedModules[genEnv], this, llvmQueue);
3352  specializedModulesLoading.insert(s.begin(), s.end());
3353  }
3354 
3355  // Notify those waiting on a source file to be compiled that its module has now been loaded.
3356 
3357  if (moduleLoadedCallback)
3358  moduleLoadedCallback();
3359 
3360  // Move modified modules from `actualModulesAdded` and `actualModulesRemoved` to `actualModulesModified`.
3361 
3362  map<Environment *, set< pair<VuoCompilerModule *, VuoCompilerModule *> > > actualModulesModified;
3363  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3364  {
3365  Environment *env = (*i).at(0);
3366 
3367  for (set<VuoCompilerModule *>::iterator add = actualModulesAdded[env].begin(); add != actualModulesAdded[env].end(); )
3368  {
3369  set<VuoCompilerModule *>::iterator rem;
3370  for (rem = actualModulesRemoved[env].begin(); rem != actualModulesRemoved[env].end(); ++rem)
3371  if ((*rem)->getPseudoBase()->getModuleKey() == (*add)->getPseudoBase()->getModuleKey())
3372  break;
3373 
3374  if (rem != actualModulesRemoved[env].end())
3375  {
3376  actualModulesModified[env].insert( make_pair(*rem, *add) );
3377  actualModulesRemoved[env].erase(rem);
3378  actualModulesAdded[env].erase(add++);
3379  }
3380  else
3381  ++add;
3382  }
3383  }
3384 
3385  // Reify port types on node classes (if needed).
3386 
3387  bool wereModulesAddedOrModified = false;
3388  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3389  {
3390  Environment *env = (*i).at(0);
3391  if (! (actualModulesAdded[env].empty() && actualModulesModified[env].empty()) )
3392  {
3393  wereModulesAddedOrModified = true;
3394  break;
3395  }
3396  }
3397 
3398  if (wereModulesAddedOrModified)
3399  {
3400  map<string, VuoCompilerType *> inheritedTypes;
3401  for (const vector<Environment *> &envs : environments)
3402  {
3403  for (Environment *env : envs)
3404  {
3405  env->reifyPortTypes(inheritedTypes);
3406  map<string, VuoCompilerType *> envTypes = env->getTypes();
3407  inheritedTypes.insert(envTypes.begin(), envTypes.end());
3408  }
3409  }
3410  }
3411 
3412  // Delete cached compiled module files and call this function recursively for:
3413  // - modules whose source files have been removed
3414  // - modules whose source files depend on them
3415 
3416  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3417  {
3418  Environment *env = (*i).at(0);
3419 
3420  set<string> sourcesToUnload;
3421  sourcesToUnload.insert(sourcesRemoved[env].begin(), sourcesRemoved[env].end());
3422  sourcesToUnload.insert(sourcesDepOnModulesRemoved[env].begin(), sourcesDepOnModulesRemoved[env].end());
3423  if (! sourcesToUnload.empty())
3424  {
3425  string moduleSearchPath = env->getModuleSearchPaths().front();
3426 
3427  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
3428  VuoCompiler *otherCompiler = new VuoCompiler(moduleSearchPath + "/unused");
3429 
3430  dispatch_sync(environmentQueue, ^{
3431  otherCompiler->loadModulesAndSources(set<string>(), set<string>(), sourcesToUnload,
3432  set<string>(), set<string>(), set<string>(),
3433  false, false, env, nullptr, nullptr, "");
3434  });
3435 
3436  delete otherCompiler;
3437  });
3438  }
3439 
3440  if (!env->isBuiltIn() && !sourcesToUnload.empty())
3441  VUserLog("Deleting from %s environment: %s", env->getName().c_str(), VuoStringUtilities::join(sourcesToUnload, ", ").c_str());
3442 
3443  env->deleteModulesCompiledFromSourceCode(sourcesToUnload);
3444  }
3445 
3446  // Compile asynchronously:
3447  // - source files that have been added or modified
3448  // - source files that depend on them
3449  // - source files that depend on modules that have been modified
3450 
3451  map<Environment *, set<string> > sourcesDepOnModulesAdded;
3452  {
3453  map<Environment *, set<string> > modulesDepOnModulesAdded; // unused
3454  __block map<Environment *, set<string> > modulesDepOnModulesAdded_otherCompiler; //
3455  __block map<Environment *, set<string> > sourcesDepOnModulesAdded_otherCompiler;
3456 
3457  map<Environment *, set<string> > actualModuleKeysAdded;
3458  for (const vector<Environment *> &envs : environments)
3459  {
3460  Environment *env = envs.at(0);
3461  for (VuoCompilerModule *module : actualModulesAdded[env])
3462  actualModuleKeysAdded[env].insert( module->getPseudoBase()->getModuleKey() );
3463  }
3464 
3465  vector<VuoDirectedAcyclicNetwork *> searchDependencyGraphs;
3466  searchDependencyGraphs.push_back(compositionDependencyGraph);
3467  for (map<string, vector<Environment *> >::iterator ii = environmentsForCompositionFamily.begin(); ii != environmentsForCompositionFamily.end(); ++ii)
3468  {
3469  vector< vector<Environment *> > otherEnvs = sharedEnvironments;
3470  otherEnvs.push_back(ii->second);
3471  VuoDirectedAcyclicNetwork *other = makeDependencyNetwork(otherEnvs, ^VuoDirectedAcyclicGraph * (Environment *env) { return env->getCompositionDependencyGraph(); });
3472  searchDependencyGraphs.push_back(other);
3473  }
3474 
3475  VuoDirectedAcyclicGraph *currentEnvironmentDependencyGraph = (currentEnvironment ? currentEnvironment->getCompositionDependencyGraph() : nullptr);
3476 
3477  findDependentModulesAndSources(actualModuleKeysAdded, searchDependencyGraphs, currentEnvironmentDependencyGraph,
3478  modulesDepOnModulesAdded, modulesDepOnModulesAdded_otherCompiler,
3479  sourcesDepOnModulesAdded, sourcesDepOnModulesAdded_otherCompiler);
3480 
3481  set<Environment *> otherEnvironments;
3482  for (map<Environment *, set<string> >::iterator i = sourcesDepOnModulesAdded_otherCompiler.begin(); i != sourcesDepOnModulesAdded_otherCompiler.end(); ++i)
3483  otherEnvironments.insert(i->first);
3484 
3485  for (Environment *env : otherEnvironments)
3486  {
3487  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
3488  string moduleSearchPath = env->getModuleSearchPaths().front();
3489  VuoCompiler *otherCompiler = new VuoCompiler(moduleSearchPath + "/unused");
3490 
3491  dispatch_sync(environmentQueue, ^{
3492  otherCompiler->loadModulesAndSources(set<string>(), set<string>(), set<string>(),
3493  sourcesDepOnModulesAdded_otherCompiler[env], set<string>(), set<string>(),
3494  false, true, env, nullptr, nullptr, "");
3495  });
3496 
3497  delete otherCompiler;
3498  });
3499  }
3500  }
3501 
3502  set<dispatch_group_t> sourcesLoading;
3503  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3504  {
3505  Environment *env = (*i).at(0);
3506 
3507  set<string> sourcesToCompile;
3508  sourcesToCompile.insert(sourcesAdded[env].begin(), sourcesAdded[env].end());
3509  sourcesToCompile.insert(sourcesModified[env].begin(), sourcesModified[env].end());
3510 
3511  if (sourcesToCompile.size() == 0)
3512  continue;
3513 
3514  set<dispatch_group_t> s = env->compileModulesFromSourceCode(sourcesToCompile, shouldRecompileSourcesIfUnchanged);
3515  sourcesLoading.insert(s.begin(), s.end());
3516  }
3517 
3518  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3519  {
3520  Environment *env = (*i).at(0);
3521 
3522  set<string> sourcesToCompile;
3523  sourcesToCompile.insert(sourcesDepOnModulesAdded[env].begin(), sourcesDepOnModulesAdded[env].end());
3524  sourcesToCompile.insert(sourcesDepOnModulesModified[env].begin(), sourcesDepOnModulesModified[env].end());
3525 
3526  if (sourcesToCompile.size() == 0)
3527  continue;
3528 
3529  env->compileModulesFromSourceCode(sourcesToCompile, true);
3530  }
3531 
3532  // Notify compiler delegates.
3533 
3534  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
3535  {
3536  Environment *env = (*i).at(0);
3537  env->notifyCompilers(actualModulesAdded[env], actualModulesModified[env], actualModulesRemoved[env], issues[env]);
3538  }
3539 
3540  // Since the dispatch groups for specialized modules are temporary (caller is responsible for releasing them)
3541  // but the dispatch groups for module sources should stay alive as long as the ModuleInfo that contains them,
3542  // retain the dispatch groups for module sources so that all dispatch groups can be safely released by the caller.
3543 
3544  for (const dispatch_group_t &group : sourcesLoading)
3545  dispatch_retain(group);
3546 
3547  set<dispatch_group_t> loadingGroups;
3548  loadingGroups.insert(specializedModulesLoading.begin(), specializedModulesLoading.end());
3549  loadingGroups.insert(sourcesLoading.begin(), sourcesLoading.end());
3550  return loadingGroups;
3551 }
3552 
3564 void VuoCompiler::findDependentModulesAndSources(map<Environment *, set<string> > &changedModules,
3565  const vector<VuoDirectedAcyclicNetwork *> &searchDependencyGraphs,
3566  VuoDirectedAcyclicGraph *currentEnvironmentDependencyGraph,
3567  map<Environment *, set<string> > &modulesDepOnChangedModules_this,
3568  map<Environment *, set<string> > &modulesDepOnChangedModules_other,
3569  map<Environment *, set<string> > &sourcesDepOnChangedModules_this,
3570  map<Environment *, set<string> > &sourcesDepOnChangedModules_other)
3571 {
3572  for (const vector<Environment *> &envs : environments)
3573  {
3574  Environment *env = envs.at(0);
3575 
3576  for (const string &module : changedModules[env])
3577  {
3578  set<VuoDirectedAcyclicGraph::Vertex *> dependents;
3579  for (VuoDirectedAcyclicNetwork *searchDependencyGraph : searchDependencyGraphs)
3580  {
3581  vector<VuoDirectedAcyclicGraph::Vertex *> moduleVertices;
3582  if (currentEnvironmentDependencyGraph)
3583  {
3584  // If a module with the same module key is installed in multiple locations,
3585  // only consider the one being modified or removed.
3586  VuoDirectedAcyclicGraph::Vertex *mv = currentEnvironmentDependencyGraph->findVertex(module);
3587  if (mv)
3588  moduleVertices.push_back(mv);
3589  }
3590  else
3591  moduleVertices = searchDependencyGraph->findVertex(module);
3592 
3593  for (VuoDirectedAcyclicGraph::Vertex *moduleVertexRaw : moduleVertices)
3594  {
3595  DependencyGraphVertex *moduleVertex = static_cast<DependencyGraphVertex *>(moduleVertexRaw);
3596  if (moduleVertex->getEnvironment())
3597  {
3598  vector<VuoDirectedAcyclicGraph::Vertex *> upstream = searchDependencyGraph->getUpstreamVertices(moduleVertex);
3599  dependents.insert(upstream.begin(), upstream.end());
3600  }
3601  }
3602  }
3603 
3604  set< pair<Environment *, string> > dependentsMap;
3605  for (VuoDirectedAcyclicGraph::Vertex *dependentVertexRaw : dependents)
3606  {
3607  DependencyGraphVertex *v = static_cast<DependencyGraphVertex *>(dependentVertexRaw);
3608  Environment *dependentEnv = v->getEnvironment();
3609  if (! dependentEnv)
3610  continue;
3611 
3612  string dependent = v->getDependency();
3613 
3614  dependentsMap.insert({dependentEnv, dependent});
3615  }
3616 
3617  // In case `module` is a generic node class, check the generated environment at the same scope for any
3618  // specializations of the node class, and add them to the list of dependencies.
3619  // (They aren't in the dependency graph since the graph edge goes from installed to generated.)
3620  for (auto i : envs.at(1)->getNodeClasses())
3621  {
3622  VuoCompilerSpecializedNodeClass *specializedNodeClass = dynamic_cast<VuoCompilerSpecializedNodeClass *>(i.second);
3623  if (specializedNodeClass->getOriginalGenericNodeClassName() == module)
3624  dependentsMap.insert({envs.at(1), i.first});
3625  }
3626 
3627  for (auto i : dependentsMap)
3628  {
3629  Environment *dependentEnv = i.first;
3630  string dependent = i.second;
3631 
3632  // Skip if the dependent module is already being modified/removed in its own right
3633  // (e.g. if the module depends on another in the same node set and the node set is being removed).
3634  if (changedModules[dependentEnv].find(dependent) != changedModules[dependentEnv].end())
3635  continue;
3636 
3637  ModuleInfo *foundSourceInfo = dependentEnv->listSourceFile(dependent);
3638  ModuleInfo *foundModuleInfo = dependentEnv->listModule(dependent);
3639 
3640  bool belongsToCurrentCompiler = false;
3641  for (const vector<Environment *> &envs2 : environments)
3642  {
3643  if (find(envs2.begin(), envs2.end(), dependentEnv) != envs2.end())
3644  {
3645  belongsToCurrentCompiler = true;
3646  break;
3647  }
3648  }
3649 
3650  map<Environment *, set<string> > *whicheverDependents = nullptr;
3651  ModuleInfo *moduleInfo = nullptr;
3652  if (foundSourceInfo)
3653  {
3654  moduleInfo = foundSourceInfo;
3655  whicheverDependents = (belongsToCurrentCompiler ? &sourcesDepOnChangedModules_this : &sourcesDepOnChangedModules_other);
3656  }
3657  else if (foundModuleInfo)
3658  {
3659  moduleInfo = foundModuleInfo;
3660  whicheverDependents = (belongsToCurrentCompiler ? &modulesDepOnChangedModules_this : &modulesDepOnChangedModules_other);
3661  }
3662  else // Module in generated environment
3663  {
3664  whicheverDependents = (belongsToCurrentCompiler ? &modulesDepOnChangedModules_this : &modulesDepOnChangedModules_other);
3665  }
3666 
3667  (*whicheverDependents)[dependentEnv].insert(dependent);
3668  if (moduleInfo)
3669  moduleInfo->setAttempted(false);
3670  }
3671  }
3672  }
3673 }
3674 
3678 void VuoCompiler::loadedModules(map<string, VuoCompilerModule *> modulesAdded,
3679  map<string, pair<VuoCompilerModule *, VuoCompilerModule *> > modulesModified,
3680  map<string, VuoCompilerModule *> modulesRemoved,
3681  VuoCompilerIssues *issues, void *delegateDataV, Environment *currentEnvironment)
3682 {
3683  //VLog("C=%p %lu %lu %lu", this, modulesAdded.size(), modulesModified.size(), modulesRemoved.size());
3684 
3685  // If a module is added, superseding a version of the same module installed at a broader scope,
3686  // notify this VuoCompiler that the module has been modified rather than added.
3687  //
3688  // If a module is added, but a version of the same module is already installed at a narrower scope,
3689  // don't notify this VuoCompiler, since it will continue to use the version at the narrower scope.
3690  //
3691  // Same idea when a module is modified or removed while another version is installed at a different scope.
3692 
3693  auto findVersionsOfModule = [this, currentEnvironment] (const string &moduleKey)
3694  {
3695  vector< pair<Environment *, VuoCompilerModule *> > moduleVersions;
3696  for (const vector<Environment *> &envs : environments)
3697  {
3698  Environment *env = envs.at(0);
3699  VuoCompilerModule *module = env->findModule(moduleKey);
3700  if (module || env == currentEnvironment)
3701  moduleVersions.push_back( make_pair(env, module) );
3702  }
3703  return moduleVersions;
3704  };
3705 
3706  for (map<string, VuoCompilerModule *>::iterator i = modulesAdded.begin(); i != modulesAdded.end(); )
3707  {
3708  string moduleKey = i->first;
3709  VuoCompilerModule *moduleAdded = i->second;
3710 
3711  vector< pair<Environment *, VuoCompilerModule *> > moduleVersions = findVersionsOfModule(moduleKey);
3712 
3713  if (moduleVersions.size() > 1)
3714  {
3715  modulesAdded.erase(i++);
3716 
3717  if (moduleVersions.back().second == moduleAdded)
3718  {
3719  VuoCompilerModule *moduleSuperseded = moduleVersions.at(moduleVersions.size()-2).second;
3720  modulesModified[moduleKey] = make_pair(moduleSuperseded, moduleAdded);
3721  }
3722  }
3723  else
3724  ++i;
3725  }
3726 
3727  for (map<string, pair<VuoCompilerModule *, VuoCompilerModule *> >::iterator i = modulesModified.begin(); i != modulesModified.end(); )
3728  {
3729  string moduleKey = i->first;
3730  VuoCompilerModule *moduleModified = i->second.second;
3731 
3732  vector< pair<Environment *, VuoCompilerModule *> > moduleVersions = findVersionsOfModule(moduleKey);
3733 
3734  if (moduleVersions.size() > 1 && moduleVersions.back().second != moduleModified)
3735  modulesModified.erase(i++);
3736  else
3737  ++i;
3738  }
3739 
3740  for (map<string, VuoCompilerModule *>::iterator i = modulesRemoved.begin(); i != modulesRemoved.end(); )
3741  {
3742  string moduleKey = i->first;
3743  VuoCompilerModule *moduleRemoved = i->second;
3744 
3745  vector< pair<Environment *, VuoCompilerModule *> > moduleVersions = findVersionsOfModule(moduleKey);
3746 
3747  if (moduleVersions.size() > 1)
3748  {
3749  modulesRemoved.erase(i++);
3750 
3751  if (moduleVersions.back().first == currentEnvironment)
3752  {
3753  VuoCompilerModule *moduleUnsuperseded = moduleVersions.at(moduleVersions.size()-2).second;
3754  modulesModified[moduleKey] = make_pair(moduleRemoved, moduleUnsuperseded);
3755  }
3756  }
3757  else
3758  ++i;
3759  }
3760 
3761  dispatch_async(delegateQueue, ^{
3762  VuoCompilerDelegate::LoadedModulesData *delegateData = static_cast<VuoCompilerDelegate::LoadedModulesData *>(delegateDataV);
3763 
3764  if (delegate && ! (modulesAdded.empty() && modulesModified.empty() && modulesRemoved.empty() && issues->isEmpty()))
3765  {
3766  delegate->enqueueData(delegateData);
3767  delegate->loadedModules(modulesAdded, modulesModified, modulesRemoved, issues);
3768  }
3769  else
3770  {
3771  delegateData->release();
3772  }
3773  });
3774 }
3775 
3781 void VuoCompiler::loadNodeClassGeneratedAtRuntime(VuoCompilerNodeClass *nodeClass, Environment *env)
3782 {
3783  Module *module = nodeClass->getModule();
3784  if (module)
3785  {
3786  dispatch_sync(llvmQueue, ^{
3787  setTargetForModule(nodeClass->getModule());
3788  });
3789  }
3790 
3791  dispatch_sync(environmentQueue, ^{
3792  env->replaceNodeClass(nodeClass);
3793  });
3794 
3795  __block map<string, VuoCompilerType *> inheritedTypes;
3796  void (^envReifyPortTypes)(Environment *) = ^void (Environment *env) {
3797  env->reifyPortTypes(inheritedTypes);
3798  map<string, VuoCompilerType *> currentTypes = env->getTypes();
3799  inheritedTypes.insert(currentTypes.begin(), currentTypes.end());
3800  };
3801  applyToAllEnvironments(envReifyPortTypes);
3802 }
3803 
3807 void VuoCompiler::reifyGenericPortTypes(VuoCompilerComposition *composition)
3808 {
3809  for (VuoCompilerNode *node : composition->getCachedGraph(this)->getNodes())
3810  reifyGenericPortTypes(node->getBase());
3811 
3812  composition->invalidateCachedGraph();
3813 }
3814 
3818 void VuoCompiler::reifyGenericPortTypes(VuoNode *node)
3819 {
3821  if (! nodeClass)
3822  return;
3823 
3824  // Reify any generic types on the node that don't already have a compiler detail.
3825 
3826  vector<VuoPort *> inputPorts = node->getInputPorts();
3827  vector<VuoPort *> outputPorts = node->getOutputPorts();
3828  vector<VuoPort *> ports;
3829  ports.insert(ports.end(), inputPorts.begin(), inputPorts.end());
3830  ports.insert(ports.end(), outputPorts.begin(), outputPorts.end());
3831 
3832  for (vector<VuoPort *>::iterator j = ports.begin(); j != ports.end(); ++j)
3833  {
3834  VuoCompilerPort *port = static_cast<VuoCompilerPort *>((*j)->getCompiler());
3835  VuoGenericType *genericType = dynamic_cast<VuoGenericType *>(port->getDataVuoType());
3836  if (! genericType)
3837  continue;
3838 
3839  if (! genericType->hasCompiler())
3840  {
3841  VuoCompilerType * (^compilerGetType)(string) = ^VuoCompilerType * (string moduleKey) {
3842  return getType(moduleKey);
3843  };
3844 
3845  VuoCompilerGenericType *reifiedType = VuoCompilerGenericType::newGenericType(genericType, compilerGetType);
3846  if (reifiedType)
3847  port->setDataVuoType(reifiedType->getBase());
3848  }
3849  }
3850 
3851  // Update the node class's backing to match the node's backing.
3852 
3853  nodeClass->updateBackingNodeClass(node, this);
3854 }
3855 
3862 void VuoCompiler::compileModule(string inputPath, string outputPath)
3863 {
3864  compileModule(inputPath, outputPath, vector<string>());
3865 }
3866 
3874 void VuoCompiler::compileModule(string inputPath, string outputPath, const vector<string> &includePaths)
3875 {
3876  if (isVerbose)
3877  print();
3878 
3879  vector<string> allIncludePaths = includePaths;
3880  string preprocessedInputPath = inputPath;
3881 
3882  string tmpPreprocessedInputDir;
3883  string dir, file, ext;
3884  VuoFileUtilities::splitPath(inputPath, dir, file, ext);
3886  {
3887  string inputContents = VuoFileUtilities::readFileToString(inputPath);
3888  string preprocessedInputContents = inputContents;
3890  if (inputContents != preprocessedInputContents)
3891  {
3892  // Unspecialized generic node class
3893  allIncludePaths.push_back(dir.empty() ? "." : dir);
3894  tmpPreprocessedInputDir = VuoFileUtilities::makeTmpDir(file);
3895  preprocessedInputPath = tmpPreprocessedInputDir + "/" + file + "." + ext;
3896  VuoFileUtilities::preserveOriginalFileName(preprocessedInputContents, file + "." + ext);
3897  VuoFileUtilities::writeStringToFile(preprocessedInputContents, preprocessedInputPath);
3898  }
3899  }
3900  else if (ext == "fs")
3901  {
3902  VuoFileUtilities::File vuf(dir, file + "." + ext);
3903  VuoModuleCompiler *moduleCompiler = VuoModuleCompiler::newModuleCompiler("isf", getModuleKeyForPath(inputPath), &vuf);
3904  if (moduleCompiler)
3905  {
3906  auto getType = [this] (const string &moduleKey) { return this->getType(moduleKey); };
3907  VuoCompilerIssues *issues = new VuoCompilerIssues();
3908  Module *module = moduleCompiler->compile(getType, llvmQueue, issues);
3909  if (module)
3910  dispatch_sync(llvmQueue, ^{
3911  writeModuleToBitcode(module, outputPath);
3912  });
3913 
3914  if (!issues->isEmpty())
3915  throw VuoCompilerException(issues, true);
3916  delete issues;
3917  }
3918  return;
3919  }
3920 
3921  vector<string> extraArgs;
3922  for (vector<string>::iterator i = allIncludePaths.begin(); i != allIncludePaths.end(); ++i)
3923  {
3924  extraArgs.push_back("-I");
3925  extraArgs.push_back(*i);
3926  }
3927 
3928  string macosxSdkFolder = "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/";
3929  if (VuoFileUtilities::fileExists(macosxSdkFolder + "MacOSX10.10.sdk"))
3930  {
3931  extraArgs.push_back("-isysroot");
3932  extraArgs.push_back(macosxSdkFolder + "MacOSX10.10.sdk");
3933  }
3934 
3935  __block vector<string> headerSearchPaths;
3936  void (^envGetHeaderSearchPaths)(Environment *) = ^void (Environment *env) {
3937  vector<string> result = env->getHeaderSearchPaths();
3938  headerSearchPaths.insert(headerSearchPaths.end(), result.begin(), result.end());
3939  };
3940  applyToInstalledEnvironments(envGetHeaderSearchPaths);
3941 
3942  auto issues = new VuoCompilerIssues;
3943  __block Module *module;
3944  dispatch_sync(llvmQueue, ^{
3945  module = readModuleFromC(preprocessedInputPath, headerSearchPaths, extraArgs, issues);
3946  });
3947  string moduleKey = getModuleKeyForPath(inputPath);
3948  if (! tmpPreprocessedInputDir.empty())
3949  {
3950  remove(tmpPreprocessedInputDir.c_str());
3951  issues->setFilePath(inputPath);
3952  }
3953  if (! module)
3954  throw VuoCompilerException(issues, true);
3955  delete issues;
3956 
3957  dispatch_sync(llvmQueue, ^{
3958  VuoCompilerModule *compilerModule = VuoCompilerModule::newModule(moduleKey, module, "");
3959  if (! compilerModule)
3960  {
3961  VUserLog("Error: Didn't recognize '%s' as a node class, type, or library.", inputPath.c_str());
3962  return;
3963  }
3964 
3965  setTargetForModule(module, target);
3966  writeModuleToBitcode(module, outputPath);
3967 
3968  delete module;
3969  });
3970 }
3971 
3975 Module * VuoCompiler::compileCompositionToModule(VuoCompilerComposition *composition, const string &moduleKey, bool isTopLevelComposition,
3976  VuoCompilerIssues *issues)
3977 {
3978  composition->check(issues);
3979 
3980  reifyGenericPortTypes(composition);
3981 
3983  isTopLevelComposition,
3984  moduleKey, this);
3985  if (telemetry == "console")
3986  generator->setDebugMode(true);
3987 
3988  __block Module *module = nullptr;
3989  dispatch_sync(llvmQueue, ^{
3990  try
3991  {
3992  module = generator->generateBitcode();
3993  setTargetForModule(module, target);
3994  }
3995  catch (VuoCompilerException &e)
3996  {
3997  issues->append(e.getIssues());
3998  }
3999  });
4000 
4001  delete generator;
4002 
4003  return module;
4004 }
4005 
4016 void VuoCompiler::compileComposition(VuoCompilerComposition *composition, string outputPath, bool isTopLevelComposition,
4017  VuoCompilerIssues *issues)
4018 {
4019  string moduleKey = getModuleKeyForPath(outputPath);
4020  Module *module = compileCompositionToModule(composition, moduleKey, isTopLevelComposition, issues);
4021  if (!module)
4022  throw VuoCompilerException(issues, false);
4023 
4024  dispatch_sync(llvmQueue, ^{
4025  writeModuleToBitcode(module, outputPath);
4026  });
4027 }
4028 
4041 void VuoCompiler::compileComposition(string inputPath, string outputPath, bool isTopLevelComposition,
4042  VuoCompilerIssues *issues)
4043 {
4044  VDebugLog("Compiling '%s'…", inputPath.c_str());
4045  if (isVerbose)
4046  print();
4047 
4048  if (getCompositionLocalPath().empty())
4049  setCompositionPath(inputPath);
4050 
4052  {
4053  VuoCompilerIssue issue(VuoCompilerIssue::Error, "opening composition", inputPath,
4054  "", "The composition file couldn't be read or was empty.");
4055  throw VuoCompilerException(issue);
4056  }
4057 
4058  try
4059  {
4060  string compositionString = VuoFileUtilities::readFileToString(inputPath);
4061  compileCompositionString(compositionString, outputPath, isTopLevelComposition, issues);
4062  }
4063  catch (VuoCompilerException &e)
4064  {
4065  e.getIssues()->setFilePathIfEmpty(inputPath);
4066  if (!issues)
4067  VUserLog("%s", e.getIssues()->getLongDescription(false).c_str());
4068  throw;
4069  }
4070 
4071  VDebugLog("Done.");
4072 }
4073 
4084 void VuoCompiler::compileCompositionString(const string &compositionString, string outputPath, bool isTopLevelComposition,
4085  VuoCompilerIssues *issues)
4086 {
4088  compileComposition(composition, outputPath, isTopLevelComposition, issues);
4089 
4090  VuoComposition *baseComposition = composition->getBase();
4091  delete composition;
4092  delete baseComposition;
4093 }
4094 
4098 void VuoCompiler::compileSubcompositionString(const string &compositionString, const string &outputPath,
4099  std::function<void(void)> moduleLoadedCallback, Environment *environment,
4100  VuoCompilerIssues *issues, const string inputPathForIssues)
4101 {
4102  if (! issues)
4103  issues = new VuoCompilerIssues();
4104 
4105  bool compilationSucceeded = false;
4106  try
4107  {
4108  compileCompositionString(compositionString, outputPath, false, issues);
4109  compilationSucceeded = true;
4110  }
4111  catch (VuoCompilerException &e)
4112  {
4113  if (issues != e.getIssues())
4114  issues->append(e.getIssues());
4115  }
4116 
4117  if (! compilationSucceeded)
4118  {
4119  VuoFileUtilities::deleteFile(outputPath);
4120  issues->setFilePathIfEmpty(inputPathForIssues);
4121  }
4122 
4123  environment->moduleFileChanged(outputPath, compositionString, moduleLoadedCallback, this, issues);
4124 }
4125 
4139 void VuoCompiler::linkCompositionToCreateExecutable(string inputPath, string outputPath, Optimization optimization, string rPath)
4140 {
4141  linkCompositionToCreateExecutableOrDynamicLibrary(inputPath, outputPath, optimization, false, rPath);
4142 }
4143 
4160 void VuoCompiler::linkCompositionToCreateDynamicLibrary(string inputPath, string outputPath, Optimization optimization)
4161 {
4162  linkCompositionToCreateExecutableOrDynamicLibrary(inputPath, outputPath, optimization, true);
4163 }
4164 
4179 void VuoCompiler::linkCompositionToCreateExecutableOrDynamicLibrary(string compiledCompositionPath, string linkedCompositionPath,
4180  Optimization optimization, bool isDylib, string rPath)
4181 {
4182  if (isVerbose)
4183  print();
4184 
4185  if (optimization == Optimization_FastBuildExistingCache)
4186  shouldLoadAllModules = false;
4187 
4188  set<string> dependencies = getDependenciesForComposition(compiledCompositionPath);
4189  dependencies.insert(getRuntimeDependency());
4190  if (! isDylib)
4191  dependencies.insert(getRuntimeMainDependency());
4192 
4193  set<Module *> modules;
4194  set<string> libraries;
4195  set<string> frameworks;
4196  getLinkerInputs(dependencies, optimization, modules, libraries, frameworks);
4197 
4198  libraries.insert(compiledCompositionPath);
4199 
4200  link(linkedCompositionPath, modules, libraries, frameworks, isDylib, rPath);
4201 }
4202 
4217 void VuoCompiler::linkCompositionToCreateDynamicLibraries(string compiledCompositionPath, string linkedCompositionPath,
4218  VuoRunningCompositionLibraries *runningCompositionLibraries)
4219 {
4220  if (isVerbose)
4221  print();
4222 
4223  // Get the dependencies used by the new resources and not the previous resources.
4224 
4225  set<string> carriedOverDependencies = runningCompositionLibraries->getDependenciesLoaded();
4226  set<string> allDependencies = getDependenciesForComposition(compiledCompositionPath);
4227  set<string> addedDependencies;
4228  std::set_difference(allDependencies.begin(), allDependencies.end(),
4229  carriedOverDependencies.begin(), carriedOverDependencies.end(),
4230  std::inserter(addedDependencies, addedDependencies.end()));
4231 
4232  // Get the libraries and frameworks used by the previous resources.
4233 
4234  vector<string> carriedOverNonUnloadableLibraries = runningCompositionLibraries->getNonUnloadableLibrariesLoaded();
4235  vector<string> carriedOverUnloadableLibraries = runningCompositionLibraries->getUnloadableLibrariesLoaded();
4236  set<string> carriedOverExternalLibraries = runningCompositionLibraries->getExternalLibraries();
4237  set<string> carriedOverFrameworks = runningCompositionLibraries->getExternalFrameworks();
4238 
4239  // Link the new resource dylibs, if needed.
4240 
4241  string nonUnloadableResourcePath;
4242  string unloadableResourcePath;
4243  set<string> nonUnloadableDependencies;
4244  set<string> unloadableDependencies;
4245  map<string, set<string> > builtInCacheDependencies;
4246  map<string, set<string> > userCacheDependencies;
4247  set<string> builtInLibraries;
4248  set<string> userLibraries;
4249  set<string> addedExternalLibraries;
4250  set<string> addedFrameworks;
4251  set<string> allFrameworks;
4252  if (! addedDependencies.empty())
4253  {
4254  set<string> builtInModuleAndLibraryDependencies;
4255  set<string> userModuleAndLibraryDependencies;
4256  set<Module *> builtInModules;
4257  set<Module *> userModules;
4258 
4259  getLinkerInputs(addedDependencies, Optimization_FastBuild,
4260  builtInModuleAndLibraryDependencies, userModuleAndLibraryDependencies, builtInCacheDependencies, userCacheDependencies,
4261  builtInModules, userModules, builtInLibraries, userLibraries, addedExternalLibraries, addedFrameworks);
4262 
4263  allFrameworks.insert(carriedOverFrameworks.begin(), carriedOverFrameworks.end());
4264  allFrameworks.insert(addedFrameworks.begin(), addedFrameworks.end());
4265 
4266  string dir, linkedCompositionFile, ext;
4267  VuoFileUtilities::splitPath(linkedCompositionPath, dir, linkedCompositionFile, ext);
4268 
4269  if (! builtInModules.empty() || builtInLibraries.size() > builtInCacheDependencies.size())
4270  {
4271  nonUnloadableResourcePath = VuoFileUtilities::makeTmpFile(linkedCompositionFile + "-resource", "dylib");
4272  nonUnloadableDependencies = builtInModuleAndLibraryDependencies;
4273 
4274  set<string> librariesForNonUnloadableResource = builtInLibraries;
4275  librariesForNonUnloadableResource.insert(carriedOverNonUnloadableLibraries.begin(), carriedOverNonUnloadableLibraries.end());
4276  librariesForNonUnloadableResource.insert(carriedOverExternalLibraries.begin(), carriedOverExternalLibraries.end());
4277  librariesForNonUnloadableResource.insert(addedExternalLibraries.begin(), addedExternalLibraries.end());
4278 
4279  link(nonUnloadableResourcePath, builtInModules, librariesForNonUnloadableResource, allFrameworks, true);\
4280 
4281  for (set<string>::iterator i = builtInLibraries.begin(); i != builtInLibraries.end(); )
4282  {
4283  if (! VuoStringUtilities::endsWith(*i, ".dylib"))
4284  builtInLibraries.erase(i++);
4285  else
4286  i++;
4287  }
4288 
4289  for (set<string>::iterator i = addedExternalLibraries.begin(); i != addedExternalLibraries.end(); )
4290  {
4291  if (! VuoStringUtilities::endsWith(*i, ".dylib"))
4292  addedExternalLibraries.erase(i++);
4293  else
4294  i++;
4295  }
4296  }
4297 
4298  if (! userModules.empty() || userLibraries.size() > userCacheDependencies.size())
4299  {
4300  unloadableResourcePath = VuoFileUtilities::makeTmpFile(linkedCompositionFile + "-resource", "dylib");
4301  unloadableDependencies = userModuleAndLibraryDependencies;
4302 
4303  set<string> librariesForUnloadableResource = userLibraries;
4304  librariesForUnloadableResource.insert(builtInLibraries.begin(), builtInLibraries.end());
4305  librariesForUnloadableResource.insert(carriedOverUnloadableLibraries.begin(), carriedOverUnloadableLibraries.end());
4306  librariesForUnloadableResource.insert(carriedOverNonUnloadableLibraries.begin(), carriedOverNonUnloadableLibraries.end());
4307  librariesForUnloadableResource.insert(carriedOverExternalLibraries.begin(), carriedOverExternalLibraries.end());
4308  librariesForUnloadableResource.insert(addedExternalLibraries.begin(), addedExternalLibraries.end());
4309  if (! nonUnloadableResourcePath.empty())
4310  librariesForUnloadableResource.insert(nonUnloadableResourcePath);
4311 
4312  link(unloadableResourcePath, userModules, librariesForUnloadableResource, allFrameworks, true);
4313 
4314  for (set<string>::iterator i = userLibraries.begin(); i != userLibraries.end(); )
4315  {
4316  if (! VuoStringUtilities::endsWith(*i, ".dylib"))
4317  userLibraries.erase(i++);
4318  else
4319  i++;
4320  }
4321  }
4322  }
4323 
4324  // Get the Vuo runtime dependency.
4325 
4326  set<string> vuoRuntimePaths;
4327  {
4328  set<Module *> modules;
4329  set<string> libraries;
4330  set<string> frameworks;
4331 
4332  set<string> dependencies;
4333  dependencies.insert(getRuntimeDependency());
4334  getLinkerInputs(dependencies, Optimization_FastBuild, modules, libraries, frameworks);
4335  vuoRuntimePaths = libraries;
4336  }
4337 
4338  // Link the composition.
4339 
4340  {
4341  set<Module *> modules;
4342  set<string> libraries;
4343 
4344  libraries.insert(compiledCompositionPath);
4345  libraries.insert(carriedOverExternalLibraries.begin(), carriedOverExternalLibraries.end());
4346  libraries.insert(addedExternalLibraries.begin(), addedExternalLibraries.end());
4347  libraries.insert(carriedOverNonUnloadableLibraries.begin(), carriedOverNonUnloadableLibraries.end());
4348  libraries.insert(carriedOverUnloadableLibraries.begin(), carriedOverUnloadableLibraries.end());
4349  libraries.insert(builtInLibraries.begin(), builtInLibraries.end());
4350  libraries.insert(userLibraries.begin(), userLibraries.end());
4351  if (! nonUnloadableResourcePath.empty())
4352  libraries.insert(nonUnloadableResourcePath);
4353  if (! unloadableResourcePath.empty())
4354  libraries.insert(unloadableResourcePath);
4355  libraries.insert(vuoRuntimePaths.begin(), vuoRuntimePaths.end());
4356  link(linkedCompositionPath, modules, libraries, allFrameworks, true);
4357  }
4358 
4359  // Now that we're past the point where an exception can be thrown, update the RunningCompositionLibraries.
4360 
4361  if (! nonUnloadableResourcePath.empty())
4362  runningCompositionLibraries->enqueueResourceLibraryToLoad(nonUnloadableResourcePath, nonUnloadableDependencies, false);
4363 
4364  if (! unloadableResourcePath.empty())
4365  runningCompositionLibraries->enqueueResourceLibraryToLoad(unloadableResourcePath, unloadableDependencies, true);
4366 
4367  for (map<string, set<string> >::iterator i = builtInCacheDependencies.begin(); i != builtInCacheDependencies.end(); ++i)
4368  runningCompositionLibraries->enqueueCacheLibraryToLoad(i->first, i->second, false);
4369 
4370  for (map<string, set<string> >::iterator i = userCacheDependencies.begin(); i != userCacheDependencies.end(); ++i)
4371  runningCompositionLibraries->enqueueCacheLibraryToLoad(i->first, i->second, true);
4372 
4373  runningCompositionLibraries->addExternalFrameworks(addedFrameworks);
4374  runningCompositionLibraries->addExternalLibraries(addedExternalLibraries);
4375 }
4376 
4383 set<string> VuoCompiler::getDependenciesForComposition(const string &compiledCompositionPath)
4384 {
4385  VDebugLog("Gathering dependencies for '%s'…", compiledCompositionPath.c_str());
4386 
4387  // Add the node classes in the top-level composition and their dependencies.
4388  __block set<string> directDependencies;
4389  string moduleKey = getModuleKeyForPath(compiledCompositionPath);
4390  Module *module = readModuleFromBitcode(compiledCompositionPath);
4391  dispatch_sync(llvmQueue, ^{
4392  VuoCompilerModule *compilerModule = VuoCompilerModule::newModule(moduleKey, module, "");
4393  directDependencies = compilerModule->getDependencies();
4394  delete compilerModule;
4395  delete module;
4396  });
4397 
4398  try
4399  {
4400  auto deps = getDependenciesForComposition(directDependencies, true);
4401  VDebugLog("Done.");
4402  return deps;
4403  }
4404  catch (VuoCompilerException &e)
4405  {
4406  e.getIssues()->setFilePathIfEmpty(compiledCompositionPath);
4407  throw;
4408  }
4409 }
4410 
4418 {
4419  set<string> directDependencies;
4420 
4421  set<VuoCompilerNode *> nodes = composition->getCachedGraph(this)->getNodes();
4422  for (VuoCompilerNode *node : nodes)
4423  if (node->getBase()->getNodeClass()->hasCompiler())
4424  directDependencies.insert( node->getBase()->getNodeClass()->getCompiler()->getDependencyName() );
4425 
4426  vector<VuoPublishedPort *> publishedInputPorts = composition->getBase()->getPublishedInputPorts();
4427  vector<VuoPublishedPort *> publishedOutputPorts = composition->getBase()->getPublishedOutputPorts();
4428  vector<VuoPublishedPort *> publishedPorts;
4429  publishedPorts.insert(publishedPorts.end(), publishedInputPorts.begin(), publishedInputPorts.end());
4430  publishedPorts.insert(publishedPorts.end(), publishedOutputPorts.begin(), publishedOutputPorts.end());
4431  for (VuoPublishedPort *publishedPort : publishedPorts)
4432  {
4433  if (publishedPort->getClass()->hasCompiler())
4434  {
4435  VuoType *portType = static_cast<VuoCompilerPortClass *>( publishedPort->getClass()->getCompiler() )->getDataVuoType();
4436  if (portType)
4437  {
4438  string dependency;
4439  VuoGenericType *genericType = dynamic_cast<VuoGenericType *>(portType);
4440  if (genericType)
4441  {
4442  VuoGenericType::Compatibility compatibility;
4443  vector<string> compatibleTypeNames = genericType->getCompatibleSpecializedTypes(compatibility);
4444  dependency = VuoCompilerGenericType::chooseBackingTypeName(portType->getModuleKey(), compatibleTypeNames);
4445  }
4446  else
4447  dependency = portType->getModuleKey();
4448 
4449  directDependencies.insert(dependency);
4450  }
4451  }
4452  }
4453 
4454  return directDependencies;
4455 }
4456 
4463 set<string> VuoCompiler::getDependenciesForComposition(VuoCompilerComposition *composition)
4464 {
4465  set<string> directDependencies = getDirectDependenciesForComposition(composition);
4466  return getDependenciesForComposition(directDependencies, false);
4467 }
4468 
4475 {
4476  __block vector<string> librarySearchPaths;
4477  applyToInstalledEnvironments(^void (Environment *env) {
4478  vector<string> result = env->getLibrarySearchPaths();
4479  librarySearchPaths.insert(librarySearchPaths.end(), result.begin(), result.end());
4480  });
4481 
4482  set<string> dylibDeps;
4483  for (string dep : getDependenciesForComposition(composition))
4484  {
4485  string path = getLibraryPath(dep, librarySearchPaths);
4486  if (VuoStringUtilities::endsWith(path, ".dylib"))
4487  dylibDeps.insert(path);
4488  }
4489 
4490  return dylibDeps;
4491 }
4492 
4506 set<string> VuoCompiler::getDependenciesForComposition(const set<string> &directDependencies, bool checkCompatibility)
4507 {
4508  // Make sure that any compiler-generated node classes have been generated and added to the dependency graph.
4509  for (set<string>::const_iterator i = directDependencies.begin(); i != directDependencies.end(); ++i)
4510  getNodeClass(*i);
4511 
4512  set<string> dependencies;
4513  for (set<string>::iterator i = directDependencies.begin(); i != directDependencies.end(); ++i)
4514  {
4515  string moduleKey = *i;
4516 
4517  dependencies.insert(moduleKey);
4518 
4519  // First pass: Find all dependencies of the direct dependency that have been loaded so far.
4520  vector<VuoDirectedAcyclicGraph::Vertex *> firstPassVertices = dependencyGraph->findVertex(moduleKey);
4521  set<VuoDirectedAcyclicGraph::Vertex *> firstPassDependencies(firstPassVertices.begin(), firstPassVertices.end());
4522  for (vector<VuoDirectedAcyclicGraph::Vertex *>::iterator j = firstPassVertices.begin(); j != firstPassVertices.end(); ++j)
4523  {
4524  vector<VuoDirectedAcyclicGraph::Vertex *> downstream = dependencyGraph->getDownstreamVertices(*j);
4525  firstPassDependencies.insert(downstream.begin(), downstream.end());
4526  }
4527 
4528  // Make sure that any compiler-generated node classes in subcompositions have been generated and added to the dependency graph.
4529  for (set<VuoDirectedAcyclicGraph::Vertex *>::iterator j = firstPassDependencies.begin(); j != firstPassDependencies.end(); ++j)
4530  {
4531  DependencyGraphVertex *v = static_cast<DependencyGraphVertex *>(*j);
4532  getNodeClass(v->getDependency());
4533  }
4534 
4535  // Second pass: Find all dependencies of the direct dependency, this time including dependencies of the node classes just generated.
4536  vector<VuoDirectedAcyclicGraph::Vertex *> moduleVertices = dependencyGraph->findVertex(moduleKey);
4537  set<VuoDirectedAcyclicGraph::Vertex *> moduleDependencies(moduleVertices.begin(), moduleVertices.end());
4538  for (vector<VuoDirectedAcyclicGraph::Vertex *>::iterator j = moduleVertices.begin(); j != moduleVertices.end(); ++j)
4539  {
4540  vector<VuoDirectedAcyclicGraph::Vertex *> downstream = dependencyGraph->getDownstreamVertices(*j);
4541  moduleDependencies.insert(downstream.begin(), downstream.end());
4542  }
4543 
4544  // Sort the direct dependency and all of its dependencies into those that are and are not compatible.
4545  set<string> dependenciesToAdd;
4546  set<string> incompatibleDependencies;
4547  for (set<VuoDirectedAcyclicGraph::Vertex *>::iterator j = moduleDependencies.begin(); j != moduleDependencies.end(); ++j)
4548  {
4549  DependencyGraphVertex *v = static_cast<DependencyGraphVertex *>(*j);
4550  if (! checkCompatibility || ! v->getEnvironment() || v->isCompatible())
4551  dependenciesToAdd.insert(v->getDependency());
4552  else
4553  incompatibleDependencies.insert(v->getDependency());
4554  }
4555 
4556  if (! checkCompatibility || incompatibleDependencies.empty())
4557  {
4558  dependencies.insert(dependenciesToAdd.begin(), dependenciesToAdd.end());
4559  }
4560  else
4561  {
4562  VuoCompilerTargetSet compositionTargets;
4563  compositionTargets.restrictToCurrentOperatingSystemVersion();
4564 
4565  string dependencyTargetString;
4566  VuoCompilerModule *module = getModule(moduleKey);
4567  if (module)
4568  {
4569  VuoCompilerTargetSet dependencyTargets = module->getCompatibleTargets();
4570  for (set<string>::iterator i = incompatibleDependencies.begin(); i != incompatibleDependencies.end(); ++i)
4571  {
4572  VuoCompilerModule *subModule = getModule(*i);
4573  if (subModule)
4574  {
4575  VuoCompilerTargetSet subDependencyTargets = subModule->getCompatibleTargets();
4576  dependencyTargets.restrictToBeCompatibleWithAllOf(subDependencyTargets);
4577  }
4578  }
4579  dependencyTargetString = dependencyTargets.toString();
4580  }
4581  else
4582  dependencyTargetString = "(unknown operating systems)";
4583 
4584  string modulePlaceholder = (module ? "%module" : "%moduleKey");
4585  VuoCompilerIssue issue(VuoCompilerIssue::Error, "linking composition", "",
4586  "Node incompatible with operating system",
4587  modulePlaceholder + " is only compatible with " + dependencyTargetString +
4588  ", so this composition can't run on your macOS version (" + compositionTargets.toString() + ").");
4589  issue.setModule(module->getPseudoBase());
4590  issue.setModuleKey(moduleKey);
4591  throw VuoCompilerException(issue);
4592  }
4593  }
4594 
4595  // Add the libraries needed by every linked composition.
4596  vector<string> coreDependencies = getCoreVuoDependencies();
4597  dependencies.insert(coreDependencies.begin(), coreDependencies.end());
4598 
4599  return dependencies;
4600 }
4601 
4606 void VuoCompiler::getLinkerInputs(const set<string> &dependencies, Optimization optimization,
4607  set<Module *> &modules, set<string> &libraries, set<string> &frameworks)
4608 {
4609  set<string> builtInModuleAndLibraryDependencies;
4610  set<string> userModuleAndLibraryDependencies;
4611  map<string, set<string> > builtInCacheDependencies;
4612  map<string, set<string> > userCacheDependencies;
4613  set<Module *> builtInModules;
4614  set<Module *> userModules;
4615  set<string> builtInLibraries;
4616  set<string> userLibraries;
4617  set<string> externalLibraries;
4618 
4619  getLinkerInputs(dependencies, optimization,
4620  builtInModuleAndLibraryDependencies, userModuleAndLibraryDependencies, builtInCacheDependencies, userCacheDependencies,
4621  builtInModules, userModules, builtInLibraries, userLibraries, externalLibraries, frameworks);
4622 
4623  modules.insert(builtInModules.begin(), builtInModules.end());
4624  modules.insert(userModules.begin(), userModules.end());
4625  libraries.insert(builtInLibraries.begin(), builtInLibraries.end());
4626  libraries.insert(userLibraries.begin(), userLibraries.end());
4627  libraries.insert(externalLibraries.begin(), externalLibraries.end());
4628 }
4629 
4643 void VuoCompiler::getLinkerInputs(const set<string> &dependencies, Optimization optimization,
4644  set<string> &builtInModuleAndLibraryDependencies, set<string> &userModuleAndLibraryDependencies,
4645  map<string, set<string> > &builtInCacheDependencies, map<string, set<string> > &userCacheDependencies,
4646  set<Module *> &builtInModules, set<Module *> &userModules,
4647  set<string> &builtInLibraries, set<string> &userLibraries,
4648  set<string> &externalLibraries, set<string> &externalFrameworks)
4649 {
4650  bool shouldUseModuleCache = (optimization == Optimization_FastBuild || optimization == Optimization_FastBuildExistingCache);
4651  if (shouldUseModuleCache)
4652  useModuleCache(true, optimization == Optimization_FastBuildExistingCache);
4653 
4654  __block vector<string> librarySearchPaths;
4655  void (^envGetLibrarySearchPaths)(Environment *) = ^void (Environment *env) {
4656  vector<string> result = env->getLibrarySearchPaths();
4657  librarySearchPaths.insert(librarySearchPaths.end(), result.begin(), result.end());
4658  };
4659  applyToInstalledEnvironments(envGetLibrarySearchPaths);
4660 
4661  for (set<string>::iterator i = dependencies.begin(); i != dependencies.end(); ++i)
4662  {
4663  string dependency = *i;
4664 
4665  bool foundInCache = false;
4666  string moduleCachePath;
4667  bool isInBuiltInModuleCache = false;
4668  if (shouldUseModuleCache)
4669  foundInCache = findInModuleCache(dependency, moduleCachePath, isInBuiltInModuleCache);
4670 
4671  if (foundInCache)
4672  {
4673  if (isInBuiltInModuleCache)
4674  {
4675  builtInLibraries.insert(moduleCachePath);
4676  builtInCacheDependencies[moduleCachePath].insert(dependency);
4677  }
4678  else
4679  {
4680  userLibraries.insert(moduleCachePath);
4681  userCacheDependencies[moduleCachePath].insert(dependency);
4682  }
4683  }
4684  else
4685  {
4686  __block VuoCompilerModule *module = NULL;
4687  void (^envFindModule)(Environment *) = ^void (Environment *env) {
4688  VuoCompilerModule *result = env->findModule(dependency);
4689  if (result)
4690  module = result;
4691  };
4692  applyToAllEnvironments(envFindModule);
4693 
4694  if (module)
4695  {
4696  VuoCompilerNodeClass *nodeClass = dynamic_cast<VuoCompilerNodeClass *>(module);
4697  if (! (nodeClass && VuoCompilerSpecializedNodeClass::hasGenericPortTypes(nodeClass)) ) // Skip not-fully-specialized generic modules
4698  {
4699  string modulePath = module->getModulePath();
4700  if (! modulePath.empty() && dynamic_cast<VuoCompilerType *>(module))
4701  {
4702  if (module->isBuiltIn())
4703  builtInLibraries.insert(modulePath);
4704  else
4705  userLibraries.insert(modulePath);
4706  }
4707  else
4708  {
4709  if (module->isBuiltIn())
4710  builtInModules.insert(module->getModule());
4711  else
4712  userModules.insert(module->getModule());
4713  }
4714 
4715  if (module->isBuiltIn())
4716  builtInModuleAndLibraryDependencies.insert(dependency);
4717  else
4718  userModuleAndLibraryDependencies.insert(dependency);
4719  }
4720  }
4721  else
4722  {
4723  if (VuoStringUtilities::endsWith(dependency, ".framework"))
4724  externalFrameworks.insert(dependency);
4725  else
4726  {
4727  string dependencyPath = getLibraryPath(dependency, librarySearchPaths);
4728  if (! dependencyPath.empty())
4729  externalLibraries.insert(dependencyPath);
4730  else
4731  VUserLog("Warning: Could not locate dependency '%s'.", dependency.c_str());
4732  }
4733  }
4734  }
4735  }
4736 }
4737 
4743 string VuoCompiler::getLibraryPath(const string &dependency, vector<string> librarySearchPaths)
4744 {
4745  if (dependency[0] == '/' && VuoFileUtilities::fileExists(dependency))
4746  return dependency;
4747 
4748  // Put the system library folder last in the list — prefer to use the libraries bundled in Vuo.framework.
4749  // Don't attempt to use OpenSSL from the system library folder, since macOS 10.15 prevents linking to it.
4750  if (dependency != "crypto"
4751  && dependency != "ssl")
4752  librarySearchPaths.push_back("/usr/lib");
4753 
4754  for (auto &path : librarySearchPaths)
4755  {
4756  vector<string> variations;
4757  variations.push_back(path + "/" + dependency);
4758  variations.push_back(path + "/lib" + dependency);
4759  variations.push_back(path + "/lib" + dependency + ".dylib");
4760  variations.push_back(path + "/lib" + dependency + ".a");
4761  for (auto &variation : variations)
4762  if (VuoFileUtilities::fileExists(variation))
4763  return variation;
4764  }
4765 
4766  return "";
4767 }
4768 
4776 void VuoCompiler::useModuleCache(bool shouldUseExistingBuiltInCaches, bool shouldUseExistingOtherCaches)
4777 {
4778  loadModulesIfNeeded();
4779 
4780  // Iterate through the environments in the order that the caches need to be built.
4781 
4782  dispatch_sync(environmentQueue, ^{
4783  set<string> dylibsForCachesOfInstalledModules;
4784  set<string> frameworksForCachesOfInstalledModules;
4785  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
4786  {
4787  bool builtin = (i == environments.begin());
4788  set<string> dylibsForCacheOfGeneratedModules;
4789  set<string> frameworksForCacheOfGeneratedModules;
4790 
4791  for (int j = i->size() - 1; j >= 0; --j)
4792  {
4793  Environment *env = i->at(j);
4794  bool installed = (j == 0);
4795 
4796  set<string> cacheableModulesAndDependencies;
4797  set<string> dylibsNeededToLinkToThisCache;
4798  set<string> frameworksNeededToLinkToThisCache;
4799  env->getCacheableModulesAndDependencies(builtin, installed, cacheableModulesAndDependencies,
4800  dylibsNeededToLinkToThisCache, frameworksNeededToLinkToThisCache);
4801 
4802  set<string> accumulatedDylibs;
4803  accumulatedDylibs.insert(dylibsNeededToLinkToThisCache.begin(), dylibsNeededToLinkToThisCache.end());
4804  accumulatedDylibs.insert(dylibsForCachesOfInstalledModules.begin(), dylibsForCachesOfInstalledModules.end());
4805  accumulatedDylibs.insert(dylibsForCacheOfGeneratedModules.begin(), dylibsForCacheOfGeneratedModules.end());
4806 
4807  set<string> accumulatedFrameworks;
4808  accumulatedFrameworks.insert(frameworksNeededToLinkToThisCache.begin(), frameworksNeededToLinkToThisCache.end());
4809  accumulatedFrameworks.insert(frameworksForCachesOfInstalledModules.begin(), frameworksForCachesOfInstalledModules.end());
4810  accumulatedFrameworks.insert(frameworksForCacheOfGeneratedModules.begin(), frameworksForCacheOfGeneratedModules.end());
4811 
4812  bool shouldUseExistingCache = (builtin ? shouldUseExistingBuiltInCaches : shouldUseExistingOtherCaches);
4813  env->useModuleCache(shouldUseExistingCache, this, cacheableModulesAndDependencies,
4814  accumulatedDylibs, accumulatedFrameworks);
4815 
4816  if (installed)
4817  {
4818  dylibsForCachesOfInstalledModules.insert(dylibsNeededToLinkToThisCache.begin(), dylibsNeededToLinkToThisCache.end());
4819  frameworksForCachesOfInstalledModules.insert(frameworksNeededToLinkToThisCache.begin(), frameworksNeededToLinkToThisCache.end());
4820  }
4821  else
4822  {
4823  dylibsForCacheOfGeneratedModules.insert(dylibsNeededToLinkToThisCache.begin(), dylibsNeededToLinkToThisCache.end());
4824  frameworksForCacheOfGeneratedModules.insert(frameworksNeededToLinkToThisCache.begin(), frameworksNeededToLinkToThisCache.end());
4825  }
4826  }
4827  }
4828  });
4829 
4830  Environment::waitForModuleCachesToBuild();
4831 }
4832 
4841 bool VuoCompiler::findInModuleCache(const string &moduleOrDependency, string &cachePath, bool &isBuiltinCache)
4842 {
4843  __block bool found = false;
4844  __block string outPath;
4845  __block bool outBuiltin;
4846  dispatch_sync(environmentQueue, ^{
4847  for (vector< vector<Environment *> >::iterator i = environments.begin(); i != environments.end(); ++i)
4848  {
4849  bool builtin = (i == environments.begin());
4850 
4851  for (int j = i->size() - 1; j >= 0; --j)
4852  {
4853  Environment *env = i->at(j);
4854 
4855  string resultPath;
4856  bool resultFound = env->findInModuleCache(moduleOrDependency, resultPath);
4857  if (resultFound)
4858  {
4859  found = true;
4860  outPath = resultPath;
4861  outBuiltin = builtin;
4862  }
4863  }
4864  }
4865  });
4866 
4867  cachePath = outPath;
4868  isBuiltinCache = outBuiltin;
4869  return found;
4870 }
4871 
4880 {
4881  dispatch_group_async(moduleCacheBuilding, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
4882  useModuleCache(true, false);
4883  });
4884 }
4885 
4898 void VuoCompiler::generateBuiltInModuleCaches(const string &vuoFrameworkPath)
4899 {
4900  vuoFrameworkInProgressPath = vuoFrameworkPath;
4901 
4902  VuoCompiler compiler;
4903  compiler.useModuleCache(false, true);
4904 }
4905 
4914 {
4915  unsigned long maxSeconds = 30 * 24 * 60 * 60; // 30 days
4916 
4917  set<VuoFileUtilities::File *> cacheDirs = VuoFileUtilities::findAllFilesInDirectory(VuoFileUtilities::getCachePath());
4918  for (set<VuoFileUtilities::File *>::iterator i = cacheDirs.begin(); i != cacheDirs.end(); ++i)
4919  {
4920  string path = (*i)->path();
4921 
4922  string file = (*i)->basename();
4923  if (file != "Builtin" && file != "System" && file != "User")
4924  {
4925  unsigned long fileSeconds = VuoFileUtilities::getSecondsSinceFileLastAccessed(path);
4926  if (fileSeconds > maxSeconds)
4928  }
4929 
4930  if (VuoStringUtilities::beginsWith(file, Environment::pidCacheDirPrefix))
4931  {
4932  string pidAsString = file.substr(Environment::pidCacheDirPrefix.length());
4933  int pid = atoi(pidAsString.c_str());
4934  if (kill(pid, 0) != 0) // no running process has this pid
4936  }
4937 
4938  delete *i;
4939  }
4940 }
4941 
4946 void VuoCompiler::setLoadAllModules(bool shouldLoadAllModules)
4947 {
4948  dispatch_sync(modulesToLoadQueue, ^{
4949  this->shouldLoadAllModules = shouldLoadAllModules;
4950  });
4951 }
4952 
4964 void VuoCompiler::link(string outputPath, const set<Module *> &modules, const set<string> &libraries, const set<string> &frameworks, bool isDylib, string rPath, VuoCompilerIssues *issues)
4965 {
4966  VDebugLog("Linking '%s'…", outputPath.c_str());
4967  // https://stackoverflow.com/questions/11657529/how-to-generate-an-executable-from-an-llvmmodule
4968 
4969 
4970  // Write all the modules with renamed symbols to a composite module file (since the linker can't operate on in-memory modules).
4971  string compositeModulePath = VuoFileUtilities::makeTmpFile("composite", "bc");
4972  dispatch_sync(llvmQueue, ^{
4973  double t0 = VuoLogGetTime();
4974  Module *compositeModule = new Module("composite", getGlobalContext());
4975  setTargetForModule(compositeModule);
4976  for (set<Module *>::const_iterator i = modules.begin(); i != modules.end(); ++i)
4977  {
4978  string error;
4979  if (Linker::LinkModules(compositeModule, *i, Linker::PreserveSource, &error))
4980  VUserLog("Error: Failed to link compositeModule: %s", error.c_str());
4981  }
4982  writeModuleToBitcode(compositeModule, compositeModulePath);
4983  delete compositeModule;
4984  VDebugLog("\tLinkModules took %5.2fs", VuoLogGetTime() - t0);
4985  });
4986 
4987 
4988  // llvm-3.1/llvm/tools/clang/tools/driver/driver.cpp
4989 
4990  // Invoke clang as `clang++` so it includes the C++ standard libraries.
4991  llvm::sys::Path clangPath(getClangPath().str() + "++");
4992 
4993  vector<const char *> args;
4994  vector<char *> argsToFree;
4995  args.push_back(clangPath.c_str());
4996 
4997  {
4998  char *outputPathZ = strdup(("-o" + outputPath).c_str());
4999  args.push_back(outputPathZ);
5000  argsToFree.push_back(outputPathZ);
5001  }
5002 
5003  args.push_back(compositeModulePath.c_str());
5004 
5005  vector<string> coreDependencies = getCoreVuoDependencies();
5006  for (set<string>::const_iterator i = libraries.begin(); i != libraries.end(); ++i)
5007  {
5008  string library = *i;
5009 
5010  for (vector<string>::iterator j = coreDependencies.begin(); j != coreDependencies.end(); ++j)
5011  {
5012  string coreDependency = *j;
5013  if (VuoStringUtilities::endsWith(library, "lib" + coreDependency + ".a"))
5014  args.push_back("-force_load"); // Load all symbols of static core dependencies, not just those used in the objects.
5015  }
5016 
5017  // Use the pre-built native object file if it exists (faster than converting .bc to .o every build).
5018  if (VuoStringUtilities::endsWith(library, ".bc"))
5019  {
5020  string libraryObject = VuoStringUtilities::substrBefore(library, ".bc") + ".o";
5021  if (VuoFileUtilities::fileExists(libraryObject))
5022  library = libraryObject;
5023  }
5024 
5025  char *libraryZ = strdup(library.c_str());
5026  args.push_back(libraryZ);
5027  argsToFree.push_back(libraryZ);
5028  }
5029 
5030  // Add framework search paths
5031  vector<string> frameworkArguments;
5032 
5033  __block vector<string> frameworkSearchPaths;
5034  void (^envGetFrameworkSearchPaths)(Environment *) = ^void (Environment *env) {
5035  vector<string> result = env->getFrameworkSearchPaths();
5036  frameworkSearchPaths.insert(frameworkSearchPaths.end(), result.begin(), result.end());
5037  };
5038  applyToInstalledEnvironments(envGetFrameworkSearchPaths);
5039 
5040  for (vector<string>::const_iterator i = frameworkSearchPaths.begin(); i != frameworkSearchPaths.end(); ++i)
5041  {
5042  string a = "-F"+*i;
5043  // 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.
5044  frameworkArguments.push_back(a);
5045  char *frameworkArgument = strdup(a.c_str());
5046  args.push_back(frameworkArgument);
5047  argsToFree.push_back(frameworkArgument);
5048  }
5049 
5050  for (set<string>::const_iterator i = frameworks.begin(); i != frameworks.end(); ++i)
5051  {
5052  args.push_back("-framework");
5053 
5054  string frameworkName = *i;
5055  frameworkName = frameworkName.substr(0, frameworkName.length() - string(".framework").length());
5056  char *frameworkNameZ = strdup(frameworkName.c_str());
5057  args.push_back(frameworkNameZ);
5058  argsToFree.push_back(frameworkNameZ);
5059  }
5060 
5061  // Check for C Runtime path within Vuo.framework
5062  llvm::sys::Path cRuntimePath;
5063  llvm::sys::Path crt1Path;
5064  string vuoFrameworkPath = getVuoFrameworkPath();
5065  string vuoFrameworkContainingFolder = vuoFrameworkPath + "/..";
5066  if (! vuoFrameworkPath.empty())
5067  {
5068  cRuntimePath = vuoFrameworkPath + "/Modules/";
5069  crt1Path = cRuntimePath;
5070  crt1Path.appendComponent("crt1.o");
5071  }
5072 
5073  // If we have located a bundled version of crt1.o, link it in explicitly rather than relying on
5074  // clang's heuristic to locate a system version.
5075  if (!isDylib && crt1Path.canRead())
5076  {
5077  args.push_back("-nostartfiles");
5078  args.push_back(crt1Path.c_str());
5079  }
5080 
5081  // Linker option necessary for compatibility with our bundled version of ld64:
5082  args.push_back("-Xlinker");
5083  args.push_back("--no-demangle");
5084 
5085  if (isVerbose)
5086  args.push_back("-v");
5087 
5088  if (isDylib)
5089  args.push_back("-dynamiclib");
5090 
5091  args.push_back("-Xlinker");
5092  args.push_back("-headerpad_max_install_names");
5093 
5094  // Tell the built dylib/executable where to find Vuo.framework
5096  args.push_back("-rpath");
5097  string rPathArg = (rPath.empty() ? vuoFrameworkContainingFolder : rPath);
5098  args.push_back(rPathArg.c_str());
5099 
5100 #ifdef COVERAGE
5101  args.push_back("-rpath");
5102  args.push_back(LLVM_ROOT "/lib");
5103 #endif
5104 
5105  args.push_back("-std=c++11");
5106  args.push_back("-stdlib=libc++");
5107 
5108  // Allow clang to print meaningful error messages.
5109  auto diagnosticConsumer = new VuoCompilerDiagnosticConsumer(issues);
5110  clang::DiagnosticOptions *diagOptions = new clang::DiagnosticOptions();
5111  IntrusiveRefCntPtr<clang::DiagnosticIDs> DiagID(new clang::DiagnosticIDs());
5112  clang::DiagnosticsEngine Diags(DiagID, diagOptions, diagnosticConsumer);
5113 
5114  if (isVerbose)
5115  {
5116  ostringstream s;
5117  for (vector<const char *>::iterator i = args.begin(); i != args.end(); ++i)
5118  s << *i << " ";
5119  VUserLog("\t%s", s.str().c_str());
5120  }
5121 
5122  // Redirect linker output to a file, so we can feed it through VuoLog.
5123  string stdoutFile = VuoFileUtilities::makeTmpFile("vuo-linker-output", "txt");
5124  const llvm::sys::Path stdoutPath(stdoutFile);
5125  const llvm::sys::Path *redirects[] = {
5126  nullptr, // stdin
5127  &stdoutPath, // stdout
5128  &stdoutPath, // stderr
5129  };
5130 
5131  // ExecuteAndWait's args needs to be null-terminated.
5132  const char **argsz = (const char **)malloc(sizeof(char *) * args.size() + 1);
5133  for (int i = 0; i < args.size(); ++i)
5134  argsz[i] = args[i];
5135  argsz[args.size()] = nullptr;
5136 
5137  string errMsg;
5138  bool executionFailed;
5139  double t0 = VuoLogGetTime();
5140  int ret = llvm::sys::Program::ExecuteAndWait(llvm::sys::Path(args[0]), argsz, nullptr, redirects, 0, 0, &errMsg, &executionFailed);
5141 
5142  for (auto i : argsToFree)
5143  free(i);
5144 
5145  // Clean up composite module file.
5146  remove(compositeModulePath.c_str());
5147 
5148  if (!isDylib)
5149  // Ensure the linked binary has the execute permission set
5150  // (ld64-242 doesn't reliably set it, whereas ld64-133.3 did).
5151  // https://b33p.net/kosada/node/14152
5152  chmod(outputPath.c_str(), 0755);
5153 
5154  if (ret != 0)
5155  {
5156  string details;
5157  if (!errMsg.empty())
5158  {
5159  VUserLog("%s", errMsg.c_str());
5160  details += "\n" + errMsg + "\n";
5161  }
5162  string stdoutFileContents = VuoFileUtilities::readFileToString(stdoutFile);
5163  if (!stdoutFileContents.empty())
5164  {
5165  VUserLog("%s", stdoutFileContents.c_str());
5166  details += "\n" + stdoutFileContents + "\n";
5167  }
5168  VuoFileUtilities::deleteFile(stdoutFile);
5169 
5170  VuoCompilerIssue issue(VuoCompilerIssue::Error, "linking", outputPath,
5171  "Node broken or outdated", details);
5172  throw VuoCompilerException(issue);
5173  }
5174 
5175  VuoFileUtilities::deleteFile(stdoutFile);
5176  VDebugLog("\tLinking took %5.2fs", VuoLogGetTime() - t0);
5177 }
5178 
5184 Module *VuoCompiler::readModuleFromC(string inputPath, const vector<string> &headerSearchPaths, const vector<string> &extraArgs, VuoCompilerIssues *issues)
5185 {
5186  // llvm-3.1/llvm/tools/clang/examples/clang-interpreter/main.cpp
5187 
5188  vector<const char *> args;
5189  args.push_back(inputPath.c_str());
5190  args.push_back("-DVUO_COMPILER");
5191  args.push_back("-fblocks");
5192 
5193  // Sync with /CMakeLists.txt's `commonFlags`.
5194  args.push_back("-Wall");
5195  args.push_back("-Wextra");
5196  args.push_back("-Wimplicit-fallthrough");
5197  args.push_back("-Wno-unused-parameter");
5198  args.push_back("-Wno-c++11-extensions");
5199  args.push_back("-Wno-sign-compare");
5200  args.push_back("-Werror=implicit");
5201 
5202  if (VuoStringUtilities::endsWith(inputPath, ".cc"))
5203  {
5204  args.push_back("-std=c++11");
5205  args.push_back("-stdlib=libc++");
5206  }
5207 
5208  for (vector<string>::const_iterator i = headerSearchPaths.begin(); i != headerSearchPaths.end(); ++i)
5209  {
5210  args.push_back("-I");
5211  args.push_back(i->c_str());
5212  }
5213 
5214  if (isVerbose)
5215  args.push_back("-v");
5216 
5217  for (vector<string>::const_iterator i = extraArgs.begin(); i != extraArgs.end(); ++i)
5218  args.push_back(i->c_str());
5219 
5220  auto diagnosticConsumer = new VuoCompilerDiagnosticConsumer(issues);
5221  clang::DiagnosticOptions * diagOptions = new clang::DiagnosticOptions();
5222  IntrusiveRefCntPtr<clang::DiagnosticIDs> DiagID(new clang::DiagnosticIDs());
5223  clang::DiagnosticsEngine *diags = new clang::DiagnosticsEngine(DiagID, diagOptions, diagnosticConsumer);
5224 
5225  OwningPtr<clang::CompilerInvocation> CI(new clang::CompilerInvocation);
5226  clang::CompilerInvocation::CreateFromArgs(*CI, &args[0], &args[0] + args.size(), *diags);
5227 
5228  clang::CompilerInstance Clang;
5229  Clang.setInvocation(CI.take());
5230 
5231  Clang.setDiagnostics(diags);
5232  if (!Clang.hasDiagnostics())
5233  return NULL;
5234 
5235  // See CompilerInvocation::GetResourcesPath -- though we're not calling it because we don't have MainAddr.
5236  llvm::sys::Path builtinHeaderSearchPath;
5237  string vuoFrameworkPath = getVuoFrameworkPath();
5238  if (vuoFrameworkPath.empty())
5239  {
5240  llvm::sys::Path clangPath = getClangPath();
5241  builtinHeaderSearchPath = clangPath;
5242  builtinHeaderSearchPath.eraseComponent(); // Remove /clang from foo/bin/clang
5243  builtinHeaderSearchPath.eraseComponent(); // Remove /bin from foo/bin
5244  builtinHeaderSearchPath.appendComponent("lib");
5245  builtinHeaderSearchPath.appendComponent("clang");
5246  builtinHeaderSearchPath.appendComponent(CLANG_VERSION_STRING); // foo/lib/clang/<version>
5247  }
5248  else
5249  {
5250  builtinHeaderSearchPath = vuoFrameworkPath;
5251  builtinHeaderSearchPath.appendComponent("Frameworks");
5252  builtinHeaderSearchPath.appendComponent("llvm.framework");
5253  builtinHeaderSearchPath.appendComponent("Versions");
5254  builtinHeaderSearchPath.appendComponent("A");
5255  builtinHeaderSearchPath.appendComponent("lib");
5256  builtinHeaderSearchPath.appendComponent("clang");
5257  builtinHeaderSearchPath.appendComponent(CLANG_VERSION_STRING); // foo/lib/clang/<version>
5258  }
5259  Clang.getHeaderSearchOpts().ResourceDir = builtinHeaderSearchPath.str();
5260 
5261 // OwningPtr<clang::CodeGenAction> Act(new clang::EmitLLVMOnlyAction()); // @@@ return value of takeModule() is destroyed at the end of this function
5262  clang::CodeGenAction *Act = new clang::EmitLLVMOnlyAction();
5263  if (!Clang.ExecuteAction(*Act))
5264  return NULL;
5265 
5266  return Act->takeModule();
5267 }
5268 
5274 Module * VuoCompiler::readModuleFromBitcode(string inputPath)
5275 {
5276  string dir, file, ext;
5277  VuoFileUtilities::splitPath(inputPath, dir, file, ext);
5278  VuoFileUtilities::File inputFile(dir, file + "." + ext);
5279  return readModuleFromBitcode(&inputFile);
5280 }
5281 
5289 Module * VuoCompiler::readModuleFromBitcode(VuoFileUtilities::File *inputFile)
5290 {
5291  size_t inputDataBytes;
5292  char *inputData = inputFile->getContentsAsRawData(inputDataBytes);
5293 
5294  string error;
5295  VuoLog_status("Loading module \"%s\"", inputFile->getRelativePath().c_str());
5296  Module *module = readModuleFromBitcodeData(inputData, inputDataBytes, error);
5297  VuoLog_status(NULL);
5298  if (! module)
5299  VUserLog("Error: Couldn't parse module '%s': %s.", inputFile->getRelativePath().c_str(), error.c_str());
5300 
5301  free(inputData);
5302 
5303  return module;
5304 }
5305 
5311 Module * VuoCompiler::readModuleFromBitcodeData(char *inputData, size_t inputDataBytes, string &error)
5312 {
5313  __block Module *module;
5314  dispatch_sync(llvmQueue, ^{
5315  StringRef inputDataAsStringRef(inputData, inputDataBytes);
5316  MemoryBuffer *mb = MemoryBuffer::getMemBuffer(inputDataAsStringRef, "", false);
5317  module = ParseBitcodeFile(&(*mb), getGlobalContext(), &error);
5318  delete mb;
5319  });
5320  return module;
5321 }
5322 
5330 bool VuoCompiler::writeModuleToBitcode(Module *module, string outputPath)
5331 {
5332  if (verifyModule(*module, PrintMessageAction))
5333  {
5334  VUserLog("Error: Module verification failed.");
5335  return true;
5336  }
5337 
5338  string err;
5339  raw_fd_ostream out(outputPath.c_str(), err);
5340  if (! err.empty())
5341  {
5342  VUserLog("Error: Couldn't open file '%s' for writing: %s", outputPath.c_str(), err.c_str());
5343  return true;
5344  }
5345  WriteBitcodeToFile(module, out);
5346 
5347  return false;
5348 }
5349 
5355 void VuoCompiler::setTargetForModule(Module *module, string target)
5356 {
5357 /*
5358  string effectiveTarget = target;
5359  if (effectiveTarget.empty())
5360  {
5361  // llvm::sys::getDefaultTargetTriple() finds a target based on the host, but the "default" target is not necessarily the
5362  // same target that results from invoking command-line clang without a -target argument. That is the "effective" target.
5363  // For example, the "default" target could be x86_64-apple-darwin10.10.0 and the "effective" target could be x86_64-apple-macosx10.10.0.
5364 
5365  llvm::sys::Path clangPath = getClangPath();
5366 
5367  vector<const char *> args;
5368  args.push_back(clangPath.c_str());
5369  args.push_back("/bin/sh"); // Driver needs an input file (that exists) or it refuses to give you the correct effective target.
5370 
5371  clang::DiagnosticOptions * diagOptions = new clang::DiagnosticOptions();
5372  IntrusiveRefCntPtr<clang::DiagnosticIDs> DiagID(new clang::DiagnosticIDs());
5373  clang::DiagnosticsEngine Diags(DiagID, diagOptions);
5374 
5375  clang::driver::Driver TheDriver(args[0], llvm::sys::getDefaultTargetTriple(), "a.out", true, Diags);
5376  OwningPtr<clang::driver::Compilation> C(TheDriver.BuildCompilation(args));
5377  effectiveTarget = C->getDefaultToolChain().ComputeEffectiveClangTriple(C->getArgs());
5378  }
5379 
5380  module->setTargetTriple(effectiveTarget);
5381 */
5382  module->setTargetTriple("x86_64-apple-macosx10.10.0");
5383 }
5384 
5392 {
5393  Module *llvmModule = module->getModule();
5394 
5395  // In C++ the return value of a cast may not be the same pointer as the cast arg.
5396  // Because of this, VuoModule::getPseudoBase() returns a different value than VuoNodeClass::getBase().
5397  // Calling delete on VuoModule::getPseudoBase() gives a malloc error: "pointer being freed was not allocated".
5398  // So call it on VuoNodeClass::getBase(). Same idea for VuoType.
5399 
5400  VuoNodeClass *baseNodeClass = NULL;
5401  VuoType *baseType = NULL;
5402  VuoModule *baseModule = NULL;
5403  VuoCompilerNodeClass *nodeClass = dynamic_cast<VuoCompilerNodeClass *>(module);
5404  if (nodeClass)
5405  {
5406  baseNodeClass = dynamic_cast<VuoNodeClass *>(nodeClass->getBase());
5407 
5409  if (dynamic_cast<VuoCompilerSpecializedNodeClass *>(module))
5410  module = NULL;
5411  }
5412  else
5413  {
5414  VuoCompilerType *type = dynamic_cast<VuoCompilerType *>(module);
5415  if (type)
5416  baseType = dynamic_cast<VuoType *>(type->getBase());
5417  else
5418  baseModule = module->getPseudoBase();
5419  }
5420 
5421  delete module;
5422  delete baseNodeClass;
5423  delete baseType;
5424  delete baseModule;
5425  destroyLlvmModule(llvmModule);
5426 }
5427 
5435 {
5436  dispatch_sync(llvmQueue, ^{
5437  delete module;
5438  });
5439 }
5440 
5452 VuoNode * VuoCompiler::createNode(VuoCompilerNodeClass *nodeClass, string title, double x, double y)
5453 {
5455  if (nodeClassForNode)
5456  return nodeClassForNode->newNode(title, x, y);
5457 
5458  return nullptr;
5459 }
5460 
5467 VuoNode * VuoCompiler::createNode(VuoCompilerNodeClass *nodeClass, VuoNode *nodeToCopyMetadataFrom)
5468 {
5470  if (nodeClassForNode)
5471  return nodeClassForNode->newNode(nodeToCopyMetadataFrom);
5472 
5473  return nullptr;
5474 }
5475 
5479 VuoNode * VuoCompiler::createPublishedInputNode(vector<VuoPublishedPort *> publishedInputPorts)
5480 {
5481  string nodeClassName = VuoCompilerPublishedInputNodeClass::buildNodeClassName(publishedInputPorts);
5482  return createPublishedNode(nodeClassName, publishedInputPorts);
5483 }
5484 
5488 VuoNode * VuoCompiler::createPublishedOutputNode(vector<VuoPublishedPort *> publishedOutputPorts)
5489 {
5490  string nodeClassName = VuoCompilerPublishedOutputNodeClass::buildNodeClassName(publishedOutputPorts);
5491  return createPublishedNode(nodeClassName, publishedOutputPorts);
5492 }
5493 
5497 VuoNode * VuoCompiler::createPublishedNode(const string &nodeClassName, const vector<VuoPublishedPort *> &publishedPorts)
5498 {
5499  VuoCompilerNodeClass *nodeClass = getNodeClass(nodeClassName);
5500  VuoNode *node = createNode(nodeClass);
5501 
5502  // Set the generic port types on the node to match those on the published ports set by VuoCompilerComposition::updateGenericPortTypes().
5503  VuoCompilerPublishedInputNodeClass *inputNodeClass = dynamic_cast<VuoCompilerPublishedInputNodeClass *>(nodeClass);
5504  VuoCompilerPublishedOutputNodeClass *outputNodeClass = dynamic_cast<VuoCompilerPublishedOutputNodeClass *>(nodeClass);
5505  for (size_t i = 0; i < publishedPorts.size(); ++i)
5506  {
5507  VuoType *publishedPortType = static_cast<VuoCompilerPort *>(publishedPorts[i]->getCompiler())->getDataVuoType();
5508  if (! dynamic_cast<VuoGenericType *>(publishedPortType))
5509  continue;
5510 
5511  set<VuoPort *> nodePorts;
5512  if (inputNodeClass)
5513  {
5514  VuoPort *inputPort = node->getInputPorts().at( inputNodeClass->getInputPortIndexForPublishedInputPort(i) );
5515  nodePorts.insert(inputPort);
5516 
5517  VuoPort *outputPort = node->getOutputPorts().at( inputNodeClass->getOutputPortIndexForPublishedInputPort(i) );
5518  nodePorts.insert(outputPort);
5519  }
5520  else
5521  {
5522  VuoPort *inputPort = node->getInputPorts().at( outputNodeClass->getInputPortIndexForPublishedOutputPort(i) );
5523  nodePorts.insert(inputPort);
5524  }
5525 
5526  for (VuoPort *port : nodePorts)
5527  {
5528  VuoCompilerPort *compilerPort = static_cast<VuoCompilerPort *>(port->getCompiler());
5529  compilerPort->setDataVuoType(publishedPortType);
5530  }
5531  }
5532 
5533  reifyGenericPortTypes(node);
5534 
5535  return node;
5536 }
5537 
5543 {
5544  dispatch_sync(environmentQueue, ^{
5545  if (environments.size() >= 5)
5546  environments.at(3).at(0)->addExpatriateSourceFile(sourcePath);
5547  });
5548 }
5549 
5555 {
5556  dispatch_sync(environmentQueue, ^{
5557  if (environments.size() >= 5)
5558  {
5559  environments.at(3).at(0)->removeExpatriateSourceFile(sourcePath);
5560 
5561  set<string> sourcesRemoved;
5562  sourcesRemoved.insert(getModuleKeyForPath(sourcePath));
5563  loadModulesAndSources(set<string>(), set<string>(), set<string>(), set<string>(), set<string>(), sourcesRemoved,
5564  false, false, environments.at(3).at(0), nullptr, nullptr, "");
5565  }
5566  });
5567 }
5568 
5585 void VuoCompiler::overrideInstalledNodeClass(const string &sourcePath, const string &sourceCode)
5586 {
5587  string sourcePathCopy = sourcePath;
5588  string sourceCodeCopy = sourceCode;
5589 
5590  dispatch_async(environmentQueue, ^{
5591  string nodeClassName = getModuleKeyForPath(sourcePathCopy);
5592  ModuleInfo *sourceInfo = NULL;
5593 
5594  for (const vector<Environment *> &envs : environments)
5595  {
5596  for (Environment *env : envs)
5597  {
5598  ModuleInfo *potentialSourceInfo = env->listSourceFile(nodeClassName);
5599  if (potentialSourceInfo && VuoFileUtilities::arePathsEqual(potentialSourceInfo->getFile()->path(), sourcePathCopy))
5600  {
5601  sourceInfo = potentialSourceInfo;
5602  break;
5603  }
5604  }
5605  }
5606 
5607  if (! sourceInfo)
5608  return;
5609 
5610  bool shouldRecompileSourcesIfUnchanged;
5611  if (! sourceCodeCopy.empty())
5612  {
5613  sourceInfo->setSourceCode(sourceCodeCopy);
5614  sourceInfo->setSourceCodeOverridden(true);
5615 
5616  shouldRecompileSourcesIfUnchanged = false;
5617  }
5618  else
5619  {
5620  sourceInfo->revertSourceCode();
5621  sourceInfo->setSourceCodeOverridden(false);
5622 
5623  shouldRecompileSourcesIfUnchanged = true;
5624  }
5625  sourceInfo->setAttempted(false);
5626  sourceInfo->setLastModifiedToNow();
5627 
5628  set<string> sourcesModified;
5629  sourcesModified.insert(nodeClassName);
5630 
5631  loadModulesAndSources(set<string>(), set<string>(), set<string>(), set<string>(), sourcesModified, set<string>(),
5632  false, shouldRecompileSourcesIfUnchanged, nullptr, nullptr, nullptr, "");
5633  });
5634 }
5635 
5645 void VuoCompiler::revertOverriddenNodeClass(const string &sourcePath)
5646 {
5647  overrideInstalledNodeClass(sourcePath, "");
5648 }
5649 
5662 VuoCompilerNodeClass * VuoCompiler::getNodeClass(const string &nodeClassName)
5663 {
5664  // For performance, don't bother searching if it's obviously not a node class.
5665 
5666  if (VuoStringUtilities::endsWith(nodeClassName, ".framework"))
5667  return NULL;
5668 
5669  // Attempt to load the node class (if it hasn't already been loaded).
5670 
5671  set<string> nodeClassNameSet;
5672  nodeClassNameSet.insert(nodeClassName);
5673  loadModulesIfNeeded(nodeClassNameSet);
5674 
5675  // If the node class has been loaded, return it.
5676 
5677  __block VuoCompilerNodeClass *nodeClass = NULL;
5678  void (^envGetNodeClass)(Environment *) = ^void (Environment *env) {
5679  VuoCompilerNodeClass *result = env->getNodeClass(nodeClassName);
5680  if (result)
5681  nodeClass = result;
5682  };
5683  applyToAllEnvironments(envGetNodeClass);
5684  return nodeClass;
5685 }
5686 
5692 map<string, VuoCompilerNodeClass *> VuoCompiler::getNodeClasses()
5693 {
5694  loadModulesIfNeeded();
5695 
5696  __block map<string, VuoCompilerNodeClass *> nodeClasses;
5697  void (^envGetNodeClasses)(Environment *) = ^void (Environment *env) {
5698  map<string, VuoCompilerNodeClass *> result = env->getNodeClasses();
5699  nodeClasses.insert(result.begin(), result.end());
5700  };
5701  applyToInstalledEnvironments(envGetNodeClasses);
5702  return nodeClasses;
5703 }
5704 
5710 VuoCompilerType * VuoCompiler::getType(const string &typeName)
5711 {
5712  set<string> typeNameSet;
5713  typeNameSet.insert(typeName);
5714  loadModulesIfNeeded(typeNameSet);
5715 
5716  __block VuoCompilerType *type = NULL;
5717  void (^envGetType)(Environment *) = ^void (Environment *env) {
5718  VuoCompilerType *result = env->getType(typeName);
5719  if (result)
5720  type = result;
5721  };
5722  applyToInstalledEnvironments(envGetType);
5723 
5724  if (! type && VuoGenericType::isGenericTypeName(typeName))
5725  {
5726  VuoCompilerType * (^compilerGetType)(string) = ^VuoCompilerType * (string moduleKey) {
5727  return getType(moduleKey);
5728  };
5729 
5730  VuoGenericType *genericType = new VuoGenericType(typeName, vector<string>());
5731  type = VuoCompilerGenericType::newGenericType(genericType, compilerGetType);
5732  }
5733 
5734  return type;
5735 }
5736 
5742 map<string, VuoCompilerType *> VuoCompiler::getTypes()
5743 {
5744  loadModulesIfNeeded();
5745 
5746  __block map<string, VuoCompilerType *> types;
5747  void (^envGetTypes)(Environment *) = ^void (Environment *env) {
5748  map<string, VuoCompilerType *> result = env->getTypes();
5749  types.insert(result.begin(), result.end());
5750  };
5751  applyToInstalledEnvironments(envGetTypes);
5752  return types;
5753 }
5754 
5760 map<string, VuoCompilerModule *> VuoCompiler::getLibraryModules()
5761 {
5762  loadModulesIfNeeded();
5763 
5764  __block map<string, VuoCompilerModule *> libraryModules;
5765  void (^envGetLibraryModules)(Environment *) = ^void (Environment *env) {
5766  map<string, VuoCompilerModule *> result = env->getLibraryModules();
5767  libraryModules.insert(result.begin(), result.end());
5768  };
5769  applyToInstalledEnvironments(envGetLibraryModules);
5770  return libraryModules;
5771 }
5772 
5778 map<string, VuoNodeSet *> VuoCompiler::getNodeSets()
5779 {
5780  loadModulesIfNeeded();
5781 
5782  __block map<string, VuoNodeSet *> nodeSets;
5783  void (^envGetNodeSets)(Environment *) = ^void (Environment *env) {
5784  map<string, VuoNodeSet *> result = env->getNodeSets();
5785  nodeSets.insert(result.begin(), result.end());
5786  };
5787  applyToInstalledEnvironments(envGetNodeSets);
5788  return nodeSets;
5789 }
5790 
5797 {
5798  loadModulesIfNeeded();
5799 
5800  __block VuoNodeSet *nodeSet = NULL;
5801  void (^envFindNodeSet)(Environment *) = ^void (Environment *env) {
5802  VuoNodeSet *result = env->findNodeSet(name);
5803  if (result)
5804  nodeSet = result;
5805  };
5806  applyToInstalledEnvironments(envFindNodeSet);
5807  return nodeSet;
5808 }
5809 
5813 VuoCompilerModule * VuoCompiler::getModule(const string &moduleKey)
5814 {
5815  __block VuoCompilerModule *module = NULL;
5816  void (^envFindModule)(Environment *) = ^void (Environment *env) {
5817  VuoCompilerModule *result = env->findModule(moduleKey);
5818  if (result)
5819  module = result;
5820  };
5821  applyToAllEnvironments(envFindModule);
5822  return module;
5823 }
5824 
5836 void VuoCompiler::listNodeClasses(const string &format)
5837 {
5838  map<string, VuoCompilerNodeClass *> nodeClasses = getNodeClasses();
5839  for (map<string, VuoCompilerNodeClass *>::const_iterator i = nodeClasses.begin(); i != nodeClasses.end(); ++i)
5840  {
5841  VuoCompilerNodeClass *nodeClass = i->second;
5842  if (format == "")
5843  {
5844  printf("%s\n", nodeClass->getBase()->getClassName().c_str());
5845  }
5846  else if (format == "path")
5847  {
5848  // TODO: If "path", prints the absolute path of each node class, one per line.
5849  }
5850  else if (format == "dot")
5851  {
5852  VuoCompilerNode *node = nodeClass->newNode()->getCompiler();
5853 
5854  printf("%s\n", nodeClass->getDoxygenDocumentation().c_str());
5855  printf("%s\n\n", node->getGraphvizDeclaration().c_str());
5856 
5857  delete node;
5858  }
5859  }
5860 }
5861 
5868 vector<string> VuoCompiler::getCoreVuoDependencies(void)
5869 {
5870  vector<string> dependencies;
5871 
5872  dependencies.push_back("VuoHeap");
5873  dependencies.push_back("VuoApp");
5874 
5875  dependencies.push_back("zmq");
5876  dependencies.push_back("json-c");
5877  dependencies.push_back("objc");
5878  dependencies.push_back("c");
5879  dependencies.push_back("AppKit.framework");
5880 
5881 #ifdef COVERAGE
5882  dependencies.push_back(LLVM_ROOT "/lib/libprofile_rt.dylib");
5883 #endif
5884  return dependencies;
5885 }
5886 
5890 string VuoCompiler::getRuntimeMainDependency(void)
5891 {
5892  return "VuoRuntimeMain.o";
5893 }
5894 
5902 string VuoCompiler::getRuntimeDependency(void)
5903 {
5904  return "VuoRuntime.o";
5905 }
5906 
5913 string VuoCompiler::getVuoFrameworkPath(void)
5914 {
5915  if (! vuoFrameworkInProgressPath.empty())
5916  return vuoFrameworkInProgressPath;
5917 
5919 }
5920 
5924 llvm::sys::Path VuoCompiler::getClangPath(void)
5925 {
5926  return clangPath;
5927 }
5928 
5933 {
5934  // Start with the file name without extension.
5935  string dir, moduleKey, ext;
5936  VuoFileUtilities::splitPath(path, dir, moduleKey, ext);
5937 
5939  {
5940  vector<string> nodeClassNameParts = VuoStringUtilities::split(moduleKey, '.');
5941 
5942  // Remove repeated file extensions.
5943  while (nodeClassNameParts.size() > 1 && nodeClassNameParts.back() == ext)
5944  nodeClassNameParts.pop_back();
5945 
5946  // Convert each part to lowerCamelCase.
5947  for (string &part : nodeClassNameParts)
5948  part = VuoStringUtilities::convertToCamelCase(part, false, true, true);
5949 
5950  // If the node class name has only one part, add a prefix.
5951  if (nodeClassNameParts.size() == 1)
5952  nodeClassNameParts.insert(nodeClassNameParts.begin(), "isf");
5953 
5954  moduleKey = VuoStringUtilities::join(nodeClassNameParts, '.');
5955  }
5956 
5957  return moduleKey;
5958 }
5959 
5965 {
5966  __block bool isLocal = false;
5967 
5968  dispatch_sync(environmentQueue, ^{
5969  if (environments.size() >= 5)
5970  isLocal = environments.at(3).at(0)->findModule(moduleKey);
5971  });
5972 
5973  return isLocal;
5974 }
5975 
5983 {
5984  return lastCompositionBaseDir.empty() ? "" : lastCompositionBaseDir + "/Modules";
5985 }
5986 
5998 {
5999  return lastCompositionBaseDir;
6000 }
6001 
6005 void VuoCompiler::addModuleSearchPath(string path)
6006 {
6007  dispatch_sync(environmentQueue, ^{
6008  environments.back().at(0)->addModuleSearchPath(path);
6009  environments.back().at(0)->addLibrarySearchPath(path);
6010  });
6011 }
6012 
6016 void VuoCompiler::addHeaderSearchPath(const string &path)
6017 {
6018  dispatch_sync(environmentQueue, ^{
6019  environments.back().at(0)->addHeaderSearchPath(path);
6020  });
6021 }
6022 
6026 void VuoCompiler::addLibrarySearchPath(const string &path)
6027 {
6028  dispatch_sync(environmentQueue, ^{
6029  environments.back().at(0)->addLibrarySearchPath(path);
6030  });
6031 }
6032 
6036 void VuoCompiler::addFrameworkSearchPath(const string &path)
6037 {
6038  dispatch_sync(environmentQueue, ^{
6039  environments.back().at(0)->addFrameworkSearchPath(path);
6040  });
6041 }
6042 
6046 void VuoCompiler::setTelemetry(const string &telemetry)
6047 {
6048  this->telemetry = telemetry;
6049 }
6050 
6054 void VuoCompiler::setTarget(const string &target)
6055 {
6056  this->target = target;
6057 }
6058 
6062 void VuoCompiler::setVerbose(bool isVerbose)
6063 {
6064  this->isVerbose = isVerbose;
6065 }
6066 
6072 {
6073 #if VUO_PRO
6074  if (VuoPro::getProAccess())
6075  {
6076  _shouldShowSplashWindow = false;
6077  return;
6078  }
6079 #endif
6080 
6081  _shouldShowSplashWindow = potentiallyShow;
6082 }
6083 
6088 {
6089  return _shouldShowSplashWindow;
6090 }
6091 
6095 void VuoCompiler::setClangPath(const string &clangPath)
6096 {
6097  this->clangPath = llvm::sys::Path(StringRef(clangPath));
6098 }
6099 
6104 {
6105  string vuoFrameworkPath = getVuoFrameworkPath();
6106  return (vuoFrameworkPath.empty() ?
6107  VUO_BUILD_DIR "/bin/VuoCompositionLoader.app/Contents/MacOS/VuoCompositionLoader" :
6108  vuoFrameworkPath + "/Helpers/VuoCompositionLoader.app/Contents/MacOS/VuoCompositionLoader");
6109 }
6110 
6114 string VuoCompiler::getCompositionStubPath(void)
6115 {
6116  string vuoFrameworkPath = getVuoFrameworkPath();
6117  return (vuoFrameworkPath.empty() ?
6118  VUO_BUILD_DIR "/lib/libVuoCompositionStub.dylib" :
6119  vuoFrameworkPath + "/Modules/libVuoCompositionStub.dylib");
6120 }
6121 
6125 string VuoCompiler::getCachePathForComposition(const string compositionDir)
6126 {
6127  string modifierLetterColon("꞉");
6128  string cachedModulesName = compositionDir;
6129  VuoStringUtilities::replaceAll(cachedModulesName, "/", modifierLetterColon);
6130  return VuoFileUtilities::getCachePath() + "/" + cachedModulesName;
6131 }
6132 
6137 {
6138  __block vector<string> moduleSearchPaths;
6139  void (^envGetModuleSearchPaths)(Environment *) = ^void (Environment *env) {
6140  vector<string> result = env->getModuleSearchPaths();
6141  moduleSearchPaths.insert(moduleSearchPaths.end(), result.begin(), result.end());
6142  };
6143  applyToInstalledEnvironments(envGetModuleSearchPaths);
6144 
6145  __block vector<string> headerSearchPaths;
6146  void (^envGetHeaderSearchPaths)(Environment *) = ^void (Environment *env) {
6147  vector<string> result = env->getHeaderSearchPaths();
6148  headerSearchPaths.insert(headerSearchPaths.end(), result.begin(), result.end());
6149  };
6150  applyToInstalledEnvironments(envGetHeaderSearchPaths);
6151 
6152  __block vector<string> librarySearchPaths;
6153  void (^envGetLibrarySearchPaths)(Environment *) = ^void (Environment *env) {
6154  vector<string> result = env->getLibrarySearchPaths();
6155  librarySearchPaths.insert(librarySearchPaths.end(), result.begin(), result.end());
6156  };
6157  applyToInstalledEnvironments(envGetLibrarySearchPaths);
6158 
6159  __block vector<string> frameworkSearchPaths;
6160  void (^envGetFrameworkSearchPaths)(Environment *) = ^void (Environment *env) {
6161  vector<string> result = env->getFrameworkSearchPaths();
6162  frameworkSearchPaths.insert(frameworkSearchPaths.end(), result.begin(), result.end());
6163  };
6164  applyToInstalledEnvironments(envGetFrameworkSearchPaths);
6165 
6166  fprintf(stderr, "Module (node class, type, library) search paths:\n");
6167  for (vector<string>::iterator i = moduleSearchPaths.begin(); i != moduleSearchPaths.end(); ++i)
6168  fprintf(stderr, " %s\n", (*i).c_str());
6169  fprintf(stderr, "Header search paths:\n");
6170  for (vector<string>::iterator i = headerSearchPaths.begin(); i != headerSearchPaths.end(); ++i)
6171  fprintf(stderr, " %s\n", (*i).c_str());
6172  fprintf(stderr, "Other library search paths:\n");
6173  for (vector<string>::iterator i = librarySearchPaths.begin(); i != librarySearchPaths.end(); ++i)
6174  fprintf(stderr, " %s\n", (*i).c_str());
6175  fprintf(stderr, "Other framework search paths:\n");
6176  for (vector<string>::iterator i = frameworkSearchPaths.begin(); i != frameworkSearchPaths.end(); ++i)
6177  fprintf(stderr, " %s\n", (*i).c_str());
6178  fprintf(stderr, "Framework path:\n");
6179  if (! getVuoFrameworkPath().empty())
6180  fprintf(stderr, " %s\n", getVuoFrameworkPath().c_str());
6181  fprintf(stderr, "Clang path:\n");
6182  if (! getClangPath().str().empty())
6183  fprintf(stderr, " %s\n", getClangPath().c_str());
6184 }
6185 
6195 {
6196  try
6197  {
6198  VuoCompiler compiler(compositionFilePath);
6199  string directory, file, extension;
6200  VuoFileUtilities::splitPath(compositionFilePath, directory, file, extension);
6201  string compiledCompositionPath = VuoFileUtilities::makeTmpFile(file, "bc");
6202  string linkedCompositionPath = VuoFileUtilities::makeTmpFile(file + "-linked", "");
6203  compiler.compileComposition(compositionFilePath, compiledCompositionPath, true, issues);
6204  compiler.linkCompositionToCreateExecutable(compiledCompositionPath, linkedCompositionPath, Optimization_FastBuild);
6205  remove(compiledCompositionPath.c_str());
6206  return VuoRunner::newSeparateProcessRunnerFromExecutable(linkedCompositionPath, directory, false, true);
6207  }
6208  catch (VuoCompilerException &e)
6209  {
6210  if (issues != e.getIssues())
6211  issues->append(e.getIssues());
6212  return NULL;
6213  }
6214 }
6215 
6230 VuoRunner * VuoCompiler::newSeparateProcessRunnerFromCompositionString(string composition, string processName, string workingDirectory, VuoCompilerIssues *issues)
6231 {
6232  try
6233  {
6234  string compositionPath = (workingDirectory.empty() ? "." : workingDirectory) + "/" + processName + ".vuo";
6235  VuoCompiler compiler(compositionPath);
6236  string compiledCompositionPath = VuoFileUtilities::makeTmpFile(processName, "bc");
6237  string linkedCompositionPath = VuoFileUtilities::makeTmpFile(processName, "");
6238  compiler.compileCompositionString(composition, compiledCompositionPath, true, issues);
6239  compiler.linkCompositionToCreateExecutable(compiledCompositionPath, linkedCompositionPath, Optimization_FastBuild);
6240  remove(compiledCompositionPath.c_str());
6241  return VuoRunner::newSeparateProcessRunnerFromExecutable(linkedCompositionPath, workingDirectory, false, true);
6242  }
6243  catch (VuoCompilerException &e)
6244  {
6245  if (issues != e.getIssues())
6246  issues->append(e.getIssues());
6247  return NULL;
6248  }
6249 }
6250 
6260 {
6261  try
6262  {
6263  VuoCompiler compiler(compositionFilePath);
6264  string directory, file, extension;
6265  VuoFileUtilities::splitPath(compositionFilePath, directory, file, extension);
6266  string compiledCompositionPath = VuoFileUtilities::makeTmpFile(file, "bc");
6267  compiler.compileComposition(compositionFilePath, compiledCompositionPath, true, issues);
6268  string linkedCompositionPath = VuoFileUtilities::makeTmpFile(file, "dylib");
6269  compiler.linkCompositionToCreateDynamicLibrary(compiledCompositionPath, linkedCompositionPath, Optimization_FastBuild);
6270  remove(compiledCompositionPath.c_str());
6271  return VuoRunner::newCurrentProcessRunnerFromDynamicLibrary(linkedCompositionPath, directory, true);
6272  }
6273  catch (VuoCompilerException &e)
6274  {
6275  if (issues != e.getIssues())
6276  issues->append(e.getIssues());
6277  return NULL;
6278  }
6279 }
6280 
6295 VuoRunner * VuoCompiler::newCurrentProcessRunnerFromCompositionString(string composition, string workingDirectory, VuoCompilerIssues *issues)
6296 {
6297  try
6298  {
6299  string compositionPath = (workingDirectory.empty() ? "." : workingDirectory) + "/UntitledComposition.vuo";
6300  VuoCompiler compiler(compositionPath);
6301  string compiledCompositionPath = VuoFileUtilities::makeTmpFile("VuoRunnerComposition", "bc");
6302  compiler.compileCompositionString(composition, compiledCompositionPath, true, issues);
6303  string linkedCompositionPath = VuoFileUtilities::makeTmpFile("VuoRunnerComposition", "dylib");
6304  compiler.linkCompositionToCreateDynamicLibrary(compiledCompositionPath, linkedCompositionPath, Optimization_FastBuild);
6305  remove(compiledCompositionPath.c_str());
6306  return VuoRunner::newCurrentProcessRunnerFromDynamicLibrary(linkedCompositionPath, workingDirectory, true);
6307  }
6308  catch (VuoCompilerException &e)
6309  {
6310  if (issues != e.getIssues())
6311  issues->append(e.getIssues());
6312  return NULL;
6313  }
6314 }
6315 
6317 {
6318  pendingDataQueue = dispatch_queue_create("org.vuo.compiler.delegate.pending", 0);
6319 }
6320 
6322 {
6323  LoadedModulesData *data = dequeueData();
6324  data->release();
6325 }
6326 
6330 void VuoCompilerDelegate::enqueueData(LoadedModulesData *data)
6331 {
6332  dispatch_sync(pendingDataQueue, ^{
6333  pendingData.push_back(data);
6334  });
6335 }
6336 
6340 VuoCompilerDelegate::LoadedModulesData * VuoCompilerDelegate::dequeueData(void)
6341 {
6342  __block LoadedModulesData *ret;
6343  dispatch_sync(pendingDataQueue, ^{
6344  ret = pendingData.front();
6345  pendingData.pop_front();
6346  });
6347  return ret;
6348 }
6349 
6354 VuoCompilerDelegate::LoadedModulesData::LoadedModulesData(const set< pair<VuoCompilerModule *, VuoCompilerModule *> > &modulesModified,
6355  const set<VuoCompilerModule *> &modulesRemoved, VuoCompilerIssues *issues)
6356 {
6357  referenceCountQueue = dispatch_queue_create("org.vuo.compiler.delegate.reference", 0);
6358  referenceCount = 0;
6359 
6360  this->modulesModified = modulesModified;
6361  this->modulesRemoved = modulesRemoved;
6362  this->issues = issues;
6363 }
6364 
6368 VuoCompilerDelegate::LoadedModulesData::~LoadedModulesData(void)
6369 {
6370  delete issues;
6371 
6372  for (set< pair<VuoCompilerModule *, VuoCompilerModule *> >::iterator i = modulesModified.begin(); i != modulesModified.end(); ++i)
6373  VuoCompiler::destroyModule((*i).first);
6374 
6375  for (set<VuoCompilerModule *>::iterator i = modulesRemoved.begin(); i != modulesRemoved.end(); ++i)
6377 }
6378 
6382 void VuoCompilerDelegate::LoadedModulesData::retain(void)
6383 {
6384  dispatch_sync(referenceCountQueue, ^{
6385  ++referenceCount;
6386  });
6387 }
6388 
6392 void VuoCompilerDelegate::LoadedModulesData::release(void)
6393 {
6394  dispatch_sync(referenceCountQueue, ^{
6395  --referenceCount;
6396  if (referenceCount == 0) {
6397  delete this;
6398  }
6399  });
6400 }