mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-13 00:04:19 +00:00
Projucer: Refactored module scanning code and added AvailableModuleList class for asynchronous scanning
This commit is contained in:
parent
8632d853e9
commit
f77c995b4d
19 changed files with 502 additions and 432 deletions
|
|
@ -25,10 +25,9 @@
|
|||
*/
|
||||
|
||||
#include "../Application/jucer_Headers.h"
|
||||
#include "jucer_Module.h"
|
||||
#include "../ProjectSaving/jucer_ProjectSaver.h"
|
||||
#include "../ProjectSaving/jucer_ProjectExport_Xcode.h"
|
||||
#include "../Application/jucer_ProjucerAnalytics.h"
|
||||
#include "../Application/jucer_Application.h"
|
||||
|
||||
//==============================================================================
|
||||
static var parseModuleDesc (const StringArray& lines)
|
||||
|
|
@ -113,195 +112,154 @@ StringArray ModuleDescription::getDependencies() const
|
|||
}
|
||||
|
||||
//==============================================================================
|
||||
ModuleList::ModuleList()
|
||||
{
|
||||
}
|
||||
|
||||
ModuleList::ModuleList (const ModuleList& other)
|
||||
{
|
||||
operator= (other);
|
||||
}
|
||||
|
||||
ModuleList& ModuleList::operator= (const ModuleList& other)
|
||||
{
|
||||
modules.clear();
|
||||
modules.addCopiesOf (other.modules);
|
||||
return *this;
|
||||
}
|
||||
|
||||
const ModuleDescription* ModuleList::getModuleWithID (const String& moduleID) const
|
||||
{
|
||||
for (auto* m : modules)
|
||||
if (m->getID() == moduleID)
|
||||
return m;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ModuleList::sort()
|
||||
{
|
||||
std::sort (modules.begin(), modules.end(), [] (const ModuleDescription* m1, const ModuleDescription* m2)
|
||||
{
|
||||
return m1->getID().compareIgnoreCase (m2->getID()) < 0;
|
||||
});
|
||||
}
|
||||
|
||||
StringArray ModuleList::getIDs() const
|
||||
{
|
||||
StringArray results;
|
||||
|
||||
for (auto* m : modules)
|
||||
results.add (m->getID());
|
||||
|
||||
results.sort (true);
|
||||
return results;
|
||||
}
|
||||
|
||||
bool ModuleList::tryToAddModuleFromFolder (const File& path)
|
||||
static bool tryToAddModuleFromFolder (const File& path, ModuleIDAndFolderList& list)
|
||||
{
|
||||
ModuleDescription m (path);
|
||||
|
||||
if (m.isValid())
|
||||
{
|
||||
getPreviousModuleDirectories().addIfNotAlreadyThere (path.getParentDirectory());
|
||||
modules.add (new ModuleDescription (m));
|
||||
list.push_back ({ m.getID(), path });
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ModuleList::addAllModulesInFolder (const File& path)
|
||||
static bool addAllModulesInSubfoldersRecursively (const File& path, int depth, ModuleIDAndFolderList& list)
|
||||
{
|
||||
if (! tryToAddModuleFromFolder (path))
|
||||
if (depth > 0)
|
||||
{
|
||||
for (DirectoryIterator iter (path, false, "*", File::findDirectories); iter.next();)
|
||||
tryToAddModuleFromFolder (iter.getFile().getLinkedTarget());
|
||||
{
|
||||
if (auto* job = ThreadPoolJob::getCurrentThreadPoolJob())
|
||||
if (job->shouldExit())
|
||||
return false;
|
||||
|
||||
auto childPath = iter.getFile().getLinkedTarget();
|
||||
|
||||
if (! tryToAddModuleFromFolder (childPath, list))
|
||||
if (! addAllModulesInSubfoldersRecursively (childPath, depth - 1, list))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Array<File>& ModuleList::getPreviousModuleDirectories()
|
||||
static bool addAllModulesInFolder (const File& path, ModuleIDAndFolderList& list)
|
||||
{
|
||||
static Array<File> previous;
|
||||
return previous;
|
||||
}
|
||||
bool exitedBeforeCompletion = false;
|
||||
|
||||
File ModuleList::tryToFindModulePathFromPrevious (const String& id)
|
||||
{
|
||||
for (auto& f : getPreviousModuleDirectories())
|
||||
if (! tryToAddModuleFromFolder (path, list))
|
||||
{
|
||||
auto modulePath = f.getChildFile (id);
|
||||
if (ModuleDescription (modulePath).isValid())
|
||||
return modulePath;
|
||||
int subfolders = 3;
|
||||
exitedBeforeCompletion = addAllModulesInSubfoldersRecursively (path, subfolders, list);
|
||||
}
|
||||
|
||||
return {};
|
||||
return exitedBeforeCompletion;
|
||||
}
|
||||
|
||||
static void sort (ModuleIDAndFolderList& listToSort)
|
||||
{
|
||||
std::sort (listToSort.begin(), listToSort.end(), [] (const ModuleIDAndFolder& m1, const ModuleIDAndFolder& m2)
|
||||
{
|
||||
return m1.first.compareIgnoreCase (m2.first) < 0;
|
||||
});
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static File getModuleFolderFromPathIfItExists (const String& path, const String& moduleID)
|
||||
struct ModuleScannerJob : public ThreadPoolJob
|
||||
{
|
||||
if (path.isNotEmpty())
|
||||
ModuleScannerJob (const Array<File>& paths, std::function<void (const ModuleIDAndFolderList&)>&& callback)
|
||||
: ThreadPoolJob ("ModuleScannerJob"),
|
||||
pathsToScan (paths),
|
||||
completionCallback (std::move (callback))
|
||||
{
|
||||
ModuleList list;
|
||||
list.addAllModulesInFolder (path);
|
||||
|
||||
if (auto* desc = list.getModuleWithID (moduleID))
|
||||
return desc->getFolder();
|
||||
}
|
||||
|
||||
JobStatus runJob() override
|
||||
{
|
||||
ModuleIDAndFolderList list;
|
||||
|
||||
for (auto& p : pathsToScan)
|
||||
if (! addAllModulesInFolder (p, list))
|
||||
return jobNeedsRunningAgain;
|
||||
|
||||
sort (list);
|
||||
completionCallback (list);
|
||||
|
||||
return jobHasFinished;
|
||||
}
|
||||
|
||||
Array<File> pathsToScan;
|
||||
std::function<void (const ModuleIDAndFolderList&)> completionCallback;
|
||||
};
|
||||
|
||||
AvailableModuleList::AvailableModuleList()
|
||||
{
|
||||
}
|
||||
|
||||
ThreadPoolJob* AvailableModuleList::createScannerJob (const Array<File>& paths)
|
||||
{
|
||||
return new ModuleScannerJob (paths, [this] (ModuleIDAndFolderList scannedModuleList)
|
||||
{
|
||||
{
|
||||
const ScopedLock swapLock (lock);
|
||||
moduleList.swap (scannedModuleList);
|
||||
}
|
||||
|
||||
MessageManager::callAsync ([this] { listeners.call ([] (Listener& l) { l.availableModulesChanged(); }); });
|
||||
});
|
||||
}
|
||||
|
||||
void AvailableModuleList::removePendingAndAddJob (ThreadPoolJob* jobToAdd)
|
||||
{
|
||||
scanPool.removeAllJobs (false, 100);
|
||||
scanPool.addJob (jobToAdd, true);
|
||||
}
|
||||
|
||||
void AvailableModuleList::scanPaths (const Array<File>& paths)
|
||||
{
|
||||
auto* job = createScannerJob (paths);
|
||||
|
||||
removePendingAndAddJob (job);
|
||||
scanPool.waitForJobToFinish (job, -1);
|
||||
}
|
||||
|
||||
void AvailableModuleList::scanPathsAsync (const Array<File>& paths)
|
||||
{
|
||||
removePendingAndAddJob (createScannerJob (paths));
|
||||
}
|
||||
|
||||
ModuleIDAndFolderList AvailableModuleList::getAllModules() const
|
||||
{
|
||||
const ScopedLock readLock (lock);
|
||||
|
||||
return moduleList;
|
||||
}
|
||||
|
||||
ModuleIDAndFolder AvailableModuleList::getModuleWithID (const String& id) const
|
||||
{
|
||||
const ScopedLock readLock (lock);
|
||||
|
||||
for (auto& mod : moduleList)
|
||||
if (mod.first == id)
|
||||
return mod;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
static Array<File> getAllPossibleModulePathsFromExporters (Project& project, bool onlyThisOS)
|
||||
void AvailableModuleList::removeDuplicates (const ModuleIDAndFolderList& other)
|
||||
{
|
||||
StringArray paths;
|
||||
const ScopedLock readLock (lock);
|
||||
|
||||
for (Project::ExporterIterator exporter (project); exporter.next();)
|
||||
for (auto& m : other)
|
||||
{
|
||||
if (onlyThisOS && ! exporter->mayCompileOnCurrentOS())
|
||||
continue;
|
||||
auto pos = std::find (moduleList.begin(), moduleList.end(), m);
|
||||
|
||||
auto& modules = project.getModules();
|
||||
auto n = modules.getNumModules();
|
||||
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
auto id = modules.getModuleID (i);
|
||||
|
||||
if (modules.shouldUseGlobalPath (id))
|
||||
continue;
|
||||
|
||||
auto path = exporter->getPathForModuleString (id);
|
||||
|
||||
if (path.isNotEmpty())
|
||||
paths.addIfNotAlreadyThere (path);
|
||||
}
|
||||
|
||||
auto oldPath = exporter->getLegacyModulePath();
|
||||
|
||||
if (oldPath.isNotEmpty())
|
||||
paths.addIfNotAlreadyThere (oldPath);
|
||||
if (pos != moduleList.end())
|
||||
moduleList.erase (pos);
|
||||
}
|
||||
|
||||
Array<File> files;
|
||||
|
||||
for (auto& path : paths)
|
||||
{
|
||||
auto f = project.resolveFilename (path);
|
||||
|
||||
if (f.isDirectory())
|
||||
{
|
||||
files.addIfNotAlreadyThere (f);
|
||||
|
||||
if (f.getChildFile ("modules").isDirectory())
|
||||
files.addIfNotAlreadyThere (f.getChildFile ("modules"));
|
||||
}
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
void ModuleList::scanProjectExporterModulePaths (Project& project)
|
||||
{
|
||||
modules.clear();
|
||||
|
||||
for (auto& m : getAllPossibleModulePathsFromExporters (project, false))
|
||||
addAllModulesInFolder (m);
|
||||
|
||||
sort();
|
||||
}
|
||||
|
||||
void ModuleList::scanGlobalJuceModulePath()
|
||||
{
|
||||
modules.clear();
|
||||
|
||||
auto& settings = getAppSettings();
|
||||
|
||||
auto path = settings.getStoredPath (Ids::defaultJuceModulePath).toString();
|
||||
|
||||
if (path.isNotEmpty())
|
||||
addAllModulesInFolder ({ path });
|
||||
|
||||
sort();
|
||||
}
|
||||
|
||||
void ModuleList::scanGlobalUserModulePath()
|
||||
{
|
||||
modules.clear();
|
||||
|
||||
auto paths = StringArray::fromTokens (getAppSettings().getStoredPath (Ids::defaultUserModulePath).toString(), ";", {});
|
||||
|
||||
for (auto p : paths)
|
||||
{
|
||||
p = p.replace ("~", File::getSpecialLocation (File::userHomeDirectory).getFullPathName());
|
||||
|
||||
auto f = File::createFileWithoutCheckingPath (p.trim());
|
||||
if (f.exists())
|
||||
addAllModulesInFolder (f);
|
||||
}
|
||||
|
||||
sort();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
|
@ -314,7 +272,7 @@ LibraryModule::LibraryModule (const ModuleDescription& d)
|
|||
void LibraryModule::writeIncludes (ProjectSaver& projectSaver, OutputStream& out)
|
||||
{
|
||||
auto& project = projectSaver.project;
|
||||
auto& modules = project.getModules();
|
||||
auto& modules = project.getEnabledModules();
|
||||
|
||||
auto id = getID();
|
||||
|
||||
|
|
@ -382,7 +340,7 @@ void LibraryModule::addSettingsForModuleToExporter (ProjectExporter& exporter, P
|
|||
|
||||
{
|
||||
Array<File> compiled;
|
||||
auto& modules = project.getModules();
|
||||
auto& modules = project.getEnabledModules();
|
||||
auto id = getID();
|
||||
|
||||
auto localModuleFolder = modules.shouldCopyModuleFilesLocally (id).getValue() ? project.getLocalModuleFolder (id)
|
||||
|
|
@ -649,7 +607,7 @@ EnabledModuleList::EnabledModuleList (Project& p, const ValueTree& s)
|
|||
|
||||
ModuleDescription EnabledModuleList::getModuleInfo (const String& moduleID)
|
||||
{
|
||||
return ModuleDescription (findFolderForModule (moduleID));
|
||||
return ModuleDescription (project.getModuleWithID (moduleID).second);
|
||||
}
|
||||
|
||||
bool EnabledModuleList::isModuleEnabled (const String& moduleID) const
|
||||
|
|
@ -681,48 +639,6 @@ Value EnabledModuleList::shouldShowAllModuleFilesInProject (const String& module
|
|||
.getPropertyAsValue (Ids::showAllCode, getUndoManager());
|
||||
}
|
||||
|
||||
File EnabledModuleList::findFolderForModule (const String& moduleID)
|
||||
{
|
||||
if (shouldUseGlobalPath (moduleID))
|
||||
{
|
||||
if (isJUCEModule (moduleID))
|
||||
return File (getAppSettings().getStoredPath (Ids::defaultJuceModulePath).toString()).getChildFile (moduleID);
|
||||
|
||||
{
|
||||
auto previous = ModuleList::tryToFindModulePathFromPrevious (moduleID);
|
||||
|
||||
if (previous != File())
|
||||
return previous;
|
||||
}
|
||||
|
||||
ModuleList list;
|
||||
list.scanGlobalUserModulePath();
|
||||
|
||||
if (auto* desc = list.getModuleWithID (moduleID))
|
||||
return desc->getFolder();
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto p : getAllPossibleModulePathsFromExporters (project, true))
|
||||
{
|
||||
auto f = getModuleFolderFromPathIfItExists (p.getFullPathName(), moduleID);
|
||||
|
||||
if (f != File())
|
||||
return f;
|
||||
}
|
||||
|
||||
for (auto p : getAllPossibleModulePathsFromExporters (project, false))
|
||||
{
|
||||
auto f = getModuleFolderFromPathIfItExists (p.getFullPathName(), moduleID);
|
||||
|
||||
if (f != File())
|
||||
return f;
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
struct ModuleTreeSorter
|
||||
{
|
||||
static int compareElements (const ValueTree& m1, const ValueTree& m2)
|
||||
|
|
@ -754,7 +670,7 @@ void EnabledModuleList::addModule (const File& moduleFolder, bool copyLocally, b
|
|||
if (! isModuleEnabled (moduleID))
|
||||
{
|
||||
ValueTree module (Ids::MODULE);
|
||||
module.setProperty (Ids::ID, moduleID, nullptr);
|
||||
module.setProperty (Ids::ID, moduleID, getUndoManager());
|
||||
|
||||
state.appendChild (module, getUndoManager());
|
||||
sortAlphabetically();
|
||||
|
|
@ -769,6 +685,9 @@ void EnabledModuleList::addModule (const File& moduleFolder, bool copyLocally, b
|
|||
for (Project::ExporterIterator exporter (project); exporter.next();)
|
||||
exporter->getPathForModuleValue (moduleID) = path.toUnixStyle();
|
||||
|
||||
if (! useGlobalPath)
|
||||
project.rescanExporterPathModules (false);
|
||||
|
||||
if (sendAnalyticsEvent)
|
||||
{
|
||||
StringPairArray data;
|
||||
|
|
@ -808,7 +727,7 @@ StringArray EnabledModuleList::getAllModules() const
|
|||
|
||||
static void getDependencies (Project& project, const String& moduleID, StringArray& dependencies)
|
||||
{
|
||||
auto info = project.getModules().getModuleInfo (moduleID);
|
||||
auto info = project.getEnabledModules().getModuleInfo (moduleID);
|
||||
|
||||
for (auto uid : info.getDependencies())
|
||||
{
|
||||
|
|
@ -877,7 +796,7 @@ bool EnabledModuleList::areMostModulesCopiedLocally() const
|
|||
void EnabledModuleList::setLocalCopyModeForAllModules (bool copyLocally)
|
||||
{
|
||||
for (auto i = getNumModules(); --i >= 0;)
|
||||
shouldCopyModuleFilesLocally (project.getModules().getModuleID (i)) = copyLocally;
|
||||
shouldCopyModuleFilesLocally (project.getEnabledModules().getModuleID (i)) = copyLocally;
|
||||
}
|
||||
|
||||
File EnabledModuleList::findDefaultModulesFolder (Project& project)
|
||||
|
|
@ -887,12 +806,9 @@ File EnabledModuleList::findDefaultModulesFolder (Project& project)
|
|||
if (globalPath != File())
|
||||
return globalPath;
|
||||
|
||||
ModuleList available;
|
||||
available.scanProjectExporterModulePaths (project);
|
||||
|
||||
for (auto i = available.modules.size(); --i >= 0;)
|
||||
for (auto& exporterPathModule : project.getExporterPathsModuleList().getAllModules())
|
||||
{
|
||||
auto f = available.modules.getUnchecked(i)->getFolder();
|
||||
auto f = exporterPathModule.second;
|
||||
|
||||
if (f.isDirectory())
|
||||
return f.getParentDirectory();
|
||||
|
|
@ -916,27 +832,15 @@ void EnabledModuleList::addModuleFromUserSelectedFile()
|
|||
|
||||
void EnabledModuleList::addModuleInteractive (const String& moduleID)
|
||||
{
|
||||
ModuleList list;
|
||||
auto f = project.getModuleWithID (moduleID).second;
|
||||
|
||||
list.scanGlobalJuceModulePath();
|
||||
if (auto* info = list.getModuleWithID (moduleID))
|
||||
if (f != File())
|
||||
{
|
||||
addModule (info->moduleFolder, areMostModulesCopiedLocally(), areMostModulesUsingGlobalPath(), true);
|
||||
addModule (f, areMostModulesCopiedLocally(), areMostModulesUsingGlobalPath(), true);
|
||||
return;
|
||||
}
|
||||
|
||||
list.scanGlobalUserModulePath();
|
||||
if (auto* info = list.getModuleWithID (moduleID))
|
||||
{
|
||||
addModule (info->moduleFolder, areMostModulesCopiedLocally(), areMostModulesUsingGlobalPath(), true);
|
||||
return;
|
||||
}
|
||||
|
||||
list.scanProjectExporterModulePaths (project);
|
||||
if (auto* info = list.getModuleWithID (moduleID))
|
||||
addModule (info->moduleFolder, areMostModulesCopiedLocally(), false, true);
|
||||
else
|
||||
addModuleFromUserSelectedFile();
|
||||
addModuleFromUserSelectedFile();
|
||||
}
|
||||
|
||||
void EnabledModuleList::addModuleOfferingToCopy (const File& f, bool isFromUserSpecifiedFolder)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue