diff --git a/NEWS b/NEWS index ea497efee..a4438da69 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,12 @@ +Choreonoid 2.1.1 released on March 21, 2024 +=========================================== + +Changes since 2.1.0: + +* Improve the plugin path management + * Add the command line option "--add-plugin-dir-as-prefix" + * Improve SimpleControllerItem to support additional controller directories in the plugin path + Choreonoid 2.1.0 released on March 17, 2024 =========================================== diff --git a/src/Base/App.cpp b/src/Base/App.cpp index f61937909..885d88cee 100644 --- a/src/Base/App.cpp +++ b/src/Base/App.cpp @@ -104,6 +104,7 @@ bool isNoWindowMode = false; bool ctrl_c_pressed = false; bool exitRequested = false; vector additionalPathVariables; +vector pluginDirsAsPrefix; void onCtrl_C_Input(int) { @@ -222,7 +223,11 @@ App::Impl::Impl(App* self, int& argc, char** argv, const std::string& appName, c mout->setPendingMode(true); AppConfig::initialize(appName, organization); + pluginManager = PluginManager::instance(); + if(auto pluginPathList = getenv("CNOID_PLUGIN_PATH")){ + pluginManager->addPluginPathList(toUTF8(pluginPathList)); + } ext = nullptr; mainWindow = nullptr; @@ -317,7 +322,7 @@ void App::setIcon(const std::string& filename) void App::addPluginPath(const std::string& path) { if(!path.empty()){ - impl->pluginManager->addPluginPath(path); + impl->pluginManager->addPluginPathList(path); } } @@ -375,6 +380,10 @@ void App::Impl::initialize() optionManager->add_option( "--path-variable", additionalPathVariables, "Set a path variable in the format \"name=value\""); + + optionManager->add_option( + "--add-plugin-dir-as-prefix", pluginDirsAsPrefix, + "Add a plugin directory as an install path prefix"); mainWindow = MainWindow::initialize(appName, ext); @@ -545,6 +554,10 @@ int App::Impl::exec() doQuit = true; } } + + for(auto& prefix : pluginDirsAsPrefix){ + pluginManager->addPluginDirectoryAsPrefix(prefix); + } optionManager->processOptionsPhase1(); diff --git a/src/Base/App.h b/src/Base/App.h index 4bea695b1..a65b01156 100644 --- a/src/Base/App.h +++ b/src/Base/App.h @@ -27,7 +27,9 @@ class CNOID_EXPORT App ~App(); + [[deprecated("Use PluginManager::addPluginPathList")]] void addPluginPath(const std::string& path); + bool requirePluginToCustomizeApplication(const std::string& pluginName); // Optional setting diff --git a/src/Base/PluginManager.cpp b/src/Base/PluginManager.cpp index adf982846..22d4d64a6 100644 --- a/src/Base/PluginManager.cpp +++ b/src/Base/PluginManager.cpp @@ -100,7 +100,7 @@ class PluginManager::Impl MainMenu* mainMenu; string pluginDirectory; - vector pluginPaths; + vector pluginDirectories; QRegExp pluginNamePattern; vector allPluginInfos; @@ -121,7 +121,7 @@ class PluginManager::Impl LazyCaller unloadPluginsLater; LazyCaller reloadPluginsLater; - void addPluginPath(const std::string& path); + void addPluginDirectory(const std::string& directory, bool doMakeAbsolute); void loadPlugins(bool doActivation); void scanPluginFiles(const std::string& pathString, bool isUTF8, bool isRecursive); void loadScannedPluginFiles(bool doActivation); @@ -176,7 +176,7 @@ PluginManager::Impl::Impl() mout = MessageOut::master(); mainMenu = nullptr; - addPluginPath(cnoid::pluginDir()); + addPluginDirectory(cnoid::pluginDir(), false); pluginNamePattern.setPattern( QString(DLL_PREFIX) + "Cnoid(.+)Plugin" + DEBUG_SUFFIX + "\\." + DLL_EXTENSION); @@ -208,27 +208,73 @@ PluginManager::Impl::~Impl() /** - @param path semicolon or colon separeted path list. + @param pathList semicolon or colon separeted absolute path list. */ -void PluginManager::addPluginPath(const std::string& pathString) +void PluginManager::addPluginPathList(const std::string& pathList) { - for(auto& path : Tokenizer>(pathString, CharSeparator(PATH_DELIMITER))){ - impl->addPluginPath(path); + for(auto& dir : Tokenizer>(pathList, CharSeparator(PATH_DELIMITER))){ + impl->addPluginDirectory(dir, false); } } - -void PluginManager::Impl::addPluginPath(const std::string& path) + +void PluginManager::addPluginPath(const std::string& pathList) { - pluginPaths.push_back(path); + addPluginPathList(pathList); +} + + +void PluginManager::addPluginDirectory(const std::string& directory) +{ + impl->addPluginDirectory(directory, true); +} + + +void PluginManager::Impl::addPluginDirectory(const std::string& directory, bool doMakeAbsolute) +{ + string directoryFromUTF8; + if(doMakeAbsolute){ + directoryFromUTF8 = fromUTF8(directory); + filesystem::path path(directoryFromUTF8); + if(!path.is_absolute()){ + string absDirectory = filesystem::absolute(path).string(); + pluginDirectories.insert(pluginDirectories.begin(), toUTF8(absDirectory)); +#ifdef Q_OS_WIN32 + // Add the plugin directory to PATH + qputenv("PATH", format("{0};{1}", absDirectory, qgetenv("PATH")).c_str()); +#endif + return; + } + } + + pluginDirectories.insert(pluginDirectories.begin(), directory); #ifdef Q_OS_WIN32 // Add the plugin directory to PATH - qputenv("PATH", format("{0};{1}", fromUTF8(path), qgetenv("PATH")).c_str()); + if(directoryFromUTF8.empty()){ + directoryFromUTF8 = fromUTF8(directory); + } + qputenv("PATH", format("{0};{1}", directoryFromUTF8, qgetenv("PATH")).c_str()); #endif } +/** + @param directory the install prefix of the corresponding plugin module. +*/ +void PluginManager::addPluginDirectoryAsPrefix(const std::string& prefix) +{ + addPluginDirectory( + toUTF8((filesystem::path(fromUTF8(prefix)) / CNOID_PLUGIN_SUBDIR).string())); +} + + +const std::vector PluginManager::pluginDirectories() const +{ + return impl->pluginDirectories; +} + + bool PluginManager::isStartupLoadingDisabled() const { return impl->isStartupLoadingDisabled; @@ -252,8 +298,8 @@ void PluginManager::loadPlugins(bool doActivation) void PluginManager::Impl::loadPlugins(bool doActivation) { if(allPluginInfos.empty()){ // Not scanned yet - for(auto& path : pluginPaths){ - scanPluginFiles(path, true, false); + for(auto& dir : pluginDirectories){ + scanPluginFiles(dir, true, false); } } loadScannedPluginFiles(doActivation); @@ -776,7 +822,7 @@ int PluginManager::numPlugins() const } -const std::string& PluginManager::pluginPath(int index) const +const std::string& PluginManager::pluginFile(int index) const { return impl->allPluginInfos[index]->pathString; } diff --git a/src/Base/PluginManager.h b/src/Base/PluginManager.h index 1df247694..b8f47e798 100644 --- a/src/Base/PluginManager.h +++ b/src/Base/PluginManager.h @@ -1,11 +1,8 @@ -/** - @author Shin'ichiro Nakaoka -*/ - #ifndef CNOID_BASE_PLUGIN_MANAGER_H #define CNOID_BASE_PLUGIN_MANAGER_H #include +#include #include "exportdecl.h" namespace cnoid { @@ -19,7 +16,13 @@ class CNOID_EXPORT PluginManager ~PluginManager(); - void addPluginPath(const std::string& path); + void addPluginPathList(const std::string& pathList); + [[deprecated("Use addPluginPathList")]] + void addPluginPath(const std::string& pathList); + + void addPluginDirectory(const std::string& directory); + void addPluginDirectoryAsPrefix(const std::string& prefix); + const std::vector pluginDirectories() const; bool isStartupLoadingDisabled() const; void doStartupLoading(); void loadPlugins(bool doActivation); @@ -32,7 +35,7 @@ class CNOID_EXPORT PluginManager void flushMessagesTo(std::ostream& os); int numPlugins() const; - const std::string& pluginPath(int index) const; + const std::string& pluginFile(int index) const; const std::string& pluginName(int index) const; enum PluginStatus { NOT_LOADED, LOADED, ACTIVE, FINALIZED, UNLOADED, INVALID, CONFLICT }; diff --git a/src/BodyPlugin/SimpleControllerItem.cpp b/src/BodyPlugin/SimpleControllerItem.cpp index f6a13b7ca..1a5a9e6c8 100644 --- a/src/BodyPlugin/SimpleControllerItem.cpp +++ b/src/BodyPlugin/SimpleControllerItem.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -90,7 +91,6 @@ class SimpleControllerItem::Impl : public SimulationSimpleControllerIO std::string controllerModuleName; std::string controllerModuleFilename; - filesystem::path controllerDirPath; QLibrary controllerModule; bool doReloading; bool isSymbolExportEnabled; @@ -109,6 +109,9 @@ class SimpleControllerItem::Impl : public SimulationSimpleControllerIO Impl(SimpleControllerItem* self, const Impl& org); ~Impl(); void doCommonInitializationInConstructor(); + bool checkIfControllerModuleInControllerDirectory( + const filesystem::path& modulePath, filesystem::path& out_controllerDirPath); + bool checkIfControllerModulePathExists(const filesystem::path& modulePath); void setController(const std::string& name); bool loadController(); bool configureController(BodyItem* bodyItem); @@ -164,8 +167,6 @@ class SimpleControllerItem::Impl : public SimulationSimpleControllerIO } -namespace { - MySimpleControllerConfig::MySimpleControllerConfig(SimpleControllerItem::Impl* impl) : SimpleControllerConfig(impl), impl(impl) @@ -179,8 +180,6 @@ Referenced* MySimpleControllerConfig::bodyItem() return impl->self->targetBodyItem(); } -} - void SimpleControllerItem::initializeClass(ExtensionManager* ext) { @@ -208,8 +207,6 @@ SimpleControllerItem::Impl::Impl(SimpleControllerItem* self) doReloading = false; isSymbolExportEnabled = false; - controllerDirPath = pluginDirPath() / "simplecontroller"; - baseDirectoryType.setSymbol(NO_BASE_DIRECTORY, N_("None")); baseDirectoryType.setSymbol(CONTROLLER_DIRECTORY, N_("Controller directory")); baseDirectoryType.setSymbol(PROJECT_DIRECTORY, N_("Project directory")); @@ -228,7 +225,6 @@ SimpleControllerItem::Impl::Impl(SimpleControllerItem* self, const Impl& org) : self(self), config(this), controllerModuleName(org.controllerModuleName), - controllerDirPath(org.controllerDirPath), baseDirectoryType(org.baseDirectoryType) { doCommonInitializationInConstructor(); @@ -296,6 +292,42 @@ SimpleController* SimpleControllerItem::controller() } +bool SimpleControllerItem::Impl::checkIfControllerModuleInControllerDirectory +(const filesystem::path& modulePath, filesystem::path& out_controllerDirPath) +{ + bool isInControllerDirectories = false; + + for(auto& pluginDir : PluginManager::instance()->pluginDirectories()){ + out_controllerDirPath = filesystem::path(fromUTF8(pluginDir)) / "simplecontroller"; + if(modulePath.is_absolute()){ + if(modulePath.parent_path() == out_controllerDirPath){ + isInControllerDirectories = true; + break; + } + } else { + if(checkIfControllerModulePathExists(out_controllerDirPath / modulePath)){ + isInControllerDirectories = true; + break; + } + } + } + + return isInControllerDirectories; +} + + +bool SimpleControllerItem::Impl::checkIfControllerModulePathExists(const filesystem::path& modulePath) +{ + if(filesystem::exists(modulePath)){ + return true; + } + filesystem::path pathWithExtension = modulePath; + pathWithExtension += "."; + pathWithExtension += DLL_EXTENSION; + return filesystem::exists(pathWithExtension); +} + + void SimpleControllerItem::setController(const std::string& name) { impl->setController(name); @@ -312,7 +344,8 @@ void SimpleControllerItem::Impl::setController(const std::string& name) filesystem::path modulePath(fromUTF8(name)); if(modulePath.is_absolute()){ baseDirectoryType.select(NO_BASE_DIRECTORY); - if(modulePath.parent_path() == controllerDirPath){ + filesystem::path controllerDirPath; + if(checkIfControllerModuleInControllerDirectory(modulePath, controllerDirPath)){ baseDirectoryType.select(CONTROLLER_DIRECTORY); modulePath = modulePath.filename(); } else { @@ -347,7 +380,14 @@ bool SimpleControllerItem::Impl::loadController() filesystem::path modulePath(fromUTF8(controllerModuleName)); if(!modulePath.is_absolute()){ if(baseDirectoryType.is(CONTROLLER_DIRECTORY)){ - modulePath = controllerDirPath / modulePath; + for(auto& pluginDir : PluginManager::instance()->pluginDirectories()){ + filesystem::path controllerDirPath = filesystem::path(fromUTF8(pluginDir)) / "simplecontroller"; + filesystem::path moduleFullPath = controllerDirPath / modulePath; + if(checkIfControllerModulePathExists(moduleFullPath)){ + modulePath = moduleFullPath; + break; + } + } } else if(baseDirectoryType.is(PROJECT_DIRECTORY)){ string projectDir = ProjectManager::instance()->currentProjectDirectory(); if(!projectDir.empty()){ @@ -1080,7 +1120,13 @@ void SimpleControllerItem::Impl::doPutProperties(PutPropertyFunction& putPropert { FilePathProperty moduleProperty(controllerModuleName); if(baseDirectoryType.is(CONTROLLER_DIRECTORY)){ - moduleProperty.setBaseDirectory(toUTF8(controllerDirPath.string())); + filesystem::path modulePath(fromUTF8(controllerModuleName)); + filesystem::path dirPath; + if(checkIfControllerModuleInControllerDirectory(modulePath, dirPath)){ + moduleProperty.setBaseDirectory(toUTF8(dirPath.string())); + } else { + moduleProperty.setBaseDirectory(toUTF8((pluginDirPath() / "simplecontroller").string())); + } } else if(baseDirectoryType.is(PROJECT_DIRECTORY)){ moduleProperty.setBaseDirectory(ProjectManager::instance()->currentProjectDirectory()); } diff --git a/src/Choreonoid/choreonoid.cpp b/src/Choreonoid/choreonoid.cpp index 9dc7d13ad..cecf3d492 100644 --- a/src/Choreonoid/choreonoid.cpp +++ b/src/Choreonoid/choreonoid.cpp @@ -6,8 +6,6 @@ */ #include -#include -#include using namespace cnoid; @@ -31,9 +29,6 @@ int main(int argc, char *argv[]) int execute(cnoid::App& app) { - if(auto pluginPath = getenv("CNOID_PLUGIN_PATH")){ - app.addPluginPath(toUTF8(pluginPath)); - } app.setBuiltinProject(":/Base/project/layout.cnoid"); return app.exec(); }