Parcourir la source

Merge branch 'plugin-support-for-third-party-projects'

* plugin-support-for-third-party-projects:
  Improved documentation and extended API.
  Added PLUGIN_VENDOR in PluginConstants.
  Write a separate "Plugin-Use-File" containing include and lib dirs for plugins.
  Better support for and diagnostics for third-party projects.
  Prevent multiple inclusions of CTKExports.cmake
  Use the build option prefix for option names.
  Changed function to macro for variable visiblity.
Sascha Zelzer il y a 14 ans
Parent
commit
e31f17d588

+ 43 - 0
CMake/ctkFunctionGeneratePluginUseFile.cmake

@@ -0,0 +1,43 @@
+###########################################################################
+#
+#  Library: CTK
+#
+#  Copyright (c) German Cancer Research Center,
+#    Division of Medical and Biological Informatics
+#
+#  Licensed under the Apache License, Version 2.0 (the "License");
+#  you may not use this file except in compliance with the License.
+#  You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+#
+###########################################################################
+
+# Write a set of variables containing CTK plugin specific include and library directories
+FUNCTION(ctkFunctionGeneratePluginUseFile filename)
+  IF(COMMAND GetMyTargetLibraries)
+    GetMyTargetLibraries("${CTK_PLUGIN_LIBRARIES}" my_ctk_plugin_libraries)
+  ELSE()
+    SET(my_ctk_plugin_libraries ${CTK_PLUGIN_LIBRARIES})
+  ENDIF()
+
+  SET(CTK_PLUGIN_INCLUDE_DIRS_CONFIG)
+  FOREACH(plugin ${my_ctk_plugin_libraries})
+    SET(${plugin}_INCLUDE_DIRS ${${plugin}_SOURCE_DIR} ${${plugin}_BINARY_DIR})
+    ctkFunctionGetIncludeDirs(${plugin}_INCLUDE_DIRS ${plugin})
+    SET(CTK_PLUGIN_INCLUDE_DIRS_CONFIG "${CTK_PLUGIN_INCLUDE_DIRS_CONFIG}
+SET(${plugin}_INCLUDE_DIRS \"${${plugin}_INCLUDE_DIRS}\")")
+
+    ctkFunctionGetLibraryDirs(${plugin}_LIBRARY_DIRS ${plugin})
+    SET(CTK_PLUGIN_LIBRARY_DIRS_CONFIG "${CTK_PLUGIN_LIBRARY_DIRS_CONFIG}
+SET(${plugin}_LIBRARY_DIRS \"${${plugin}_LIBRARY_DIRS}\")")
+  ENDFOREACH()
+
+  CONFIGURE_FILE("${CTK_CMAKE_DIR}/../CTKPluginUseFile.cmake.in" "${filename}" @ONLY)
+ENDFUNCTION()

+ 3 - 3
CMake/ctkFunctionSetupExternalPlugins.cmake

@@ -19,7 +19,7 @@
 #
 ###########################################################################
 
-FUNCTION(ctkFunctionSetupExternalPlugins )
+MACRO(ctkMacroSetupExternalPlugins )
 
   ctkMacroParseArguments(MY "BUILD_OPTION_PREFIX" "" ${ARGN})
 
@@ -47,9 +47,9 @@ FUNCTION(ctkFunctionSetupExternalPlugins )
   ctkMacroValidateBuildOptions("${CMAKE_CURRENT_BINARY_DIR}" "${CTK_DGRAPH_EXECUTABLE}" "${plugin_dirswithoption}")
 
   FOREACH(plugin ${plugin_subdirs})
-    IF(BUILD_${plugin})
+    IF(${MY_BUILD_OPTION_PREFIX}${plugin})
       ADD_SUBDIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/${plugin})
     ENDIF()
   ENDFOREACH()
 
-ENDFUNCTION()
+ENDMACRO()

+ 17 - 0
CMake/ctkMacroValidateBuildOptions.cmake

@@ -169,6 +169,15 @@ MACRO(ctkMacroValidateBuildOptions dir executable target_directories)
 
       IF(ext_dep_path_list)
         LIST(APPEND EXTERNAL_TARGETS ${ext_dep_path_list})
+        # If this macro is called from inside CTK itself, we add the external
+        # targets to the list of known targets (for external projects calling
+        # this macro, targets external to the calling project should be listed
+        # in CTK_LIBRARIES or CTK_PLUGIN_LIBRARIES
+        IF(CTK_SOURCE_DIR)
+          IF(${CMAKE_SOURCE_DIR} STREQUAL ${CTK_SOURCE_DIR})
+            LIST(APPEND known_targets ${ext_dep_path_list})
+          ENDIF()
+        ENDIF()
       ENDIF()
 
       FOREACH(dep ${int_dep_path_list})
@@ -184,6 +193,14 @@ MACRO(ctkMacroValidateBuildOptions dir executable target_directories)
           SET(${dep_option} ON CACHE BOOL "Enable ${target_project_name} library" FORCE)
         ENDIF()
       ENDFOREACH()
+
+      # Check if all external targets included in the dependency path are available
+      FOREACH(dep ${ext_dep_path_list})
+        LIST(FIND known_targets ${dep} dep_found)
+        IF(dep_found LESS 0)
+          MESSAGE(FATAL_ERROR "${target_project_name} depends on unknown external targets: ${dep}")
+        ENDIF()
+      ENDFOREACH()
     ENDIF()
     
   ENDFOREACH()

+ 9 - 11
CTKConfig.cmake.in

@@ -41,7 +41,10 @@
 
 
 # Import CTK targets
-INCLUDE("@CTK_SUPERBUILD_BINARY_DIR@/CTKExports.cmake")
+IF(NOT CTK_EXPORTS_INCLUDED)
+  INCLUDE("@CTK_SUPERBUILD_BINARY_DIR@/CTKExports.cmake")
+  SET(CTK_EXPORTS_INCLUDED 1)
+ENDIF()
 
 # Include CTK macros
 INCLUDE("@CTK_CMAKE_DIR_CONFIG@/ctkMacroParseArguments.cmake")
@@ -53,10 +56,11 @@ INCLUDE("@CTK_CMAKE_DIR_CONFIG@/ctkFunctionExtractOptionNameAndValue.cmake")
 INCLUDE("@CTK_CMAKE_DIR_CONFIG@/ctkMacroValidateBuildOptions.cmake")
 INCLUDE("@CTK_CMAKE_DIR_CONFIG@/ctkFunctionGenerateDGraphInput.cmake")
 INCLUDE("@CTK_CMAKE_DIR_CONFIG@/ctkFunctionGeneratePluginManifest.cmake")
+INCLUDE("@CTK_CMAKE_DIR_CONFIG@/ctkFunctionGeneratePluginUseFile.cmake")
 INCLUDE("@CTK_CMAKE_DIR_CONFIG@/ctkMacroGeneratePluginResourceFile.cmake")
 INCLUDE("@CTK_CMAKE_DIR_CONFIG@/ctkFunctionGetIncludeDirs.cmake")
 INCLUDE("@CTK_CMAKE_DIR_CONFIG@/ctkFunctionGetLibraryDirs.cmake")
-INCLUDE("@CTK_CMAKE_DIR_CONFIG@/ctkFunctionSetupExternalPlugins.cmake")
+INCLUDE("@CTK_CMAKE_DIR_CONFIG@/ctkMacroSetupExternalPlugins.cmake")
 
 SET(CTK_EXPORT_HEADER_TEMPLATE "@CTK_EXPORT_HEADER_TEMPLATE@")
 
@@ -66,12 +70,12 @@ SET(CTK_DGRAPH_EXECUTABLE "@DGraph_EXECUTABLE@")
 # List all libraries
 SET(CTK_LIBRARIES @CTK_LIBRARIES@)
 
-# List all plugins
-SET(CTK_PLUGIN_LIBRARIES @CTK_PLUGIN_LIBRARIES@)
-
 # List all CTK libraries wrapped with PythonQt
 SET(CTK_WRAPPED_LIBRARIES_PYTHONQT @CTK_WRAPPED_LIBRARIES_PYTHONQT@)
 
+# Include CTK Plugin specific variables
+INCLUDE("@CTK_PLUGIN_USE_FILE@")
+
 @CTK_CONFIG_INSTALL_ONLY@
 
 # Library specific include directories
@@ -80,12 +84,6 @@ SET(CTK_WRAPPED_LIBRARIES_PYTHONQT @CTK_WRAPPED_LIBRARIES_PYTHONQT@)
 # Library specific library directories
 @CTK_LIBRARY_LIBRARY_DIRS_CONFIG@
 
-# Plugin specific include directories
-@CTK_PLUGIN_INCLUDE_DIRS_CONFIG@
-
-# Plugin specific library directories
-@CTK_PLUGIN_LIBRARY_DIRS_CONFIG@
-
 # The CTK include file directories.
 SET(CTK_INCLUDE_DIRS "@CTK_BUILD_DIR@;@CTK_INCLUDE_DIRS_CONFIG@")
  

+ 8 - 0
CTKPluginUseFile.cmake.in

@@ -0,0 +1,8 @@
+# List all plugins
+SET(CTK_PLUGIN_LIBRARIES @CTK_PLUGIN_LIBRARIES@)
+
+# Plugin specific include directories
+@CTK_PLUGIN_INCLUDE_DIRS_CONFIG@
+
+# Plugin specific library directories
+@CTK_PLUGIN_LIBRARY_DIRS_CONFIG@

+ 1 - 0
Libs/PluginFramework/ctkPluginConstants.cpp

@@ -34,6 +34,7 @@ const QString ctkPluginConstants::PLUGIN_SYMBOLICNAME = "Plugin-SymbolicName";
 const QString ctkPluginConstants::PLUGIN_COPYRIGHT = "Plugin-Copyright";
 const QString ctkPluginConstants::PLUGIN_DESCRIPTION = "Plugin-Description";
 const QString ctkPluginConstants::PLUGIN_NAME = "Plugin-Name";
+const QString ctkPluginConstants::PLUGIN_VENDOR = "Plugin-Vendor";
 const QString ctkPluginConstants::PLUGIN_LOCALIZATION = "Plugin-Localization";
 const QString ctkPluginConstants::PLUGIN_LOCALIZATION_DEFAULT_BASENAME = "CTK-INF/l10n/plugin";
 const QString ctkPluginConstants::REQUIRE_PLUGIN = "Require-Plugin";

+ 8 - 0
Libs/PluginFramework/ctkPluginConstants.h

@@ -129,6 +129,14 @@ struct CTK_PLUGINFW_EXPORT ctkPluginConstants {
    */
   static const QString PLUGIN_NAME; // = "Plugin-Name"
 
+  /**
+   * Manifest header identifying the plugin's vendor.
+   *
+   * <p>
+   * The header value may be retrieved from the <code>QHash</code> object
+   * returned by the <code>ctkPlugin::getHeaders</code> method.
+   */
+  static const QString PLUGIN_VENDOR; // = "Plugin-Vendor"
 
   /**
    * Manifest header identifying the base name of the plugin's Qt .qm

+ 45 - 20
Libs/PluginFramework/ctkPluginFrameworkLauncher.cpp

@@ -73,46 +73,47 @@ void ctkPluginFrameworkLauncher::setFrameworkProperties(const ctkProperties& pro
 }
 
 //----------------------------------------------------------------------------
-bool ctkPluginFrameworkLauncher::install(const QString& symbolicName)
+long ctkPluginFrameworkLauncher::install(const QString& symbolicName, ctkPluginContext* context)
 {
   QString pluginPath = getPluginPath(symbolicName);
-  if (pluginPath.isEmpty()) return false;
+  if (pluginPath.isEmpty()) return -1;
 
-  if (d->fwFactory == 0) {
+  ctkPluginContext* pc = context;
+
+  if (pc == 0 && d->fwFactory == 0) {
     d->fwFactory = new ctkPluginFrameworkFactory(d->fwProps);
     try
     {
       d->fwFactory->getFramework()->init();
+      pc = getPluginContext();
     }
     catch (const ctkPluginException& exc)
     {
       qCritical() << "Failed to initialize the plug-in framework:" << exc;
       delete d->fwFactory;
       d->fwFactory = 0;
-      return false;
+      return -1;
     }
   }
 
   try
   {
-    getPluginContext()->installPlugin(QUrl::fromLocalFile(pluginPath));
+    return pc->installPlugin(QUrl::fromLocalFile(pluginPath))->getPluginId();
   }
   catch (const ctkPluginException& exc)
   {
     qWarning() << "Failed to install plugin:" << exc;
-    return false;
+    return -1;
   }
 
-  return true;
 }
 
 //----------------------------------------------------------------------------
-bool ctkPluginFrameworkLauncher::start(const QString& symbolicName, ctkPlugin::StartOptions options)
+bool ctkPluginFrameworkLauncher::start(const QString& symbolicName, ctkPlugin::StartOptions options,
+                                       ctkPluginContext* context)
 {
-  QString pluginPath = getPluginPath(symbolicName);
-  if (pluginPath.isEmpty()) return false;
-
-  if (d->fwFactory == 0) {
+  // instantiate and start the framework
+  if (context == 0 && d->fwFactory == 0) {
     d->fwFactory = new ctkPluginFrameworkFactory(d->fwProps);
     try
     {
@@ -126,7 +127,7 @@ bool ctkPluginFrameworkLauncher::start(const QString& symbolicName, ctkPlugin::S
       return false;
     }
   }
-  else if (d->fwFactory->getFramework()->getState() != ctkPlugin::ACTIVE)
+  else if (context == 0 && d->fwFactory->getFramework()->getState() != ctkPlugin::ACTIVE)
   {
     try
     {
@@ -141,14 +142,21 @@ bool ctkPluginFrameworkLauncher::start(const QString& symbolicName, ctkPlugin::S
     }
   }
 
-  try
+  if(!symbolicName.isEmpty())
   {
-    getPluginContext()->installPlugin(QUrl::fromLocalFile(pluginPath))->start(options);
-  }
-  catch (const ctkPluginException& exc)
-  {
-    qWarning() << "Failed to install plugin:" << exc;
-    return false;
+    QString pluginPath = getPluginPath(symbolicName);
+    if (pluginPath.isEmpty()) return false;
+
+    ctkPluginContext* pc = context ? context : getPluginContext();
+    try
+    {
+      pc->installPlugin(QUrl::fromLocalFile(pluginPath))->start(options);
+    }
+    catch (const ctkPluginException& exc)
+    {
+      qWarning() << "Failed to install plugin:" << exc;
+      return false;
+    }
   }
 
   return true;
@@ -250,3 +258,20 @@ QString ctkPluginFrameworkLauncher::getPluginPath(const QString& symbolicName)
   return QString();
 }
 
+//----------------------------------------------------------------------------
+QStringList ctkPluginFrameworkLauncher::getPluginSymbolicNames(const QString& searchPath)
+{
+  QStringList result;
+  QDirIterator dirIter(searchPath, d->pluginLibFilter, QDir::Files);
+  while(dirIter.hasNext())
+  {
+    dirIter.next();
+    QFileInfo fileInfo = dirIter.fileInfo();
+    QString fileBaseName = fileInfo.baseName();
+    if (fileBaseName.startsWith("lib")) fileBaseName = fileBaseName.mid(3);
+    result << fileBaseName.replace("_", ".");
+  }
+
+  return result;
+}
+

+ 36 - 19
Libs/PluginFramework/ctkPluginFrameworkLauncher.h

@@ -59,13 +59,13 @@ public:
   static void setFrameworkProperties(const ctkProperties& props);
 
   /**
-   * Install the plugin with the given symbolic name in the Plugin
-   * Framework.
+   * Install the plugin with the given symbolic name using the provided
+   * <code>ctkPluginContext</code>.
    *
    * <p>
    * This method instantiates and initializes an instance of a
-   * ctkPluginFramework object, if there is no framework already
-   * initialized.
+   * ctkPluginFramework object, if no plugin context is provided and
+   * if there is no framework already initialized.
    *
    * <p>
    * The plugin is searched in the paths previously given by calls
@@ -73,33 +73,38 @@ public:
    * CTK plugin installation path.
    *
    * \param symbolicName The symbolic name of the plugin to install.
-   * \return <code>true</code> if the plugin was found and successfully
-   *         installed, <code>false</code> otherwise.
+   * \param context The plugin context used for installing the plugin.
+   * \return The plugin id if the plugin was found and successfully
+   *         installed, <code>-1</code> otherwise.
    */
-  static bool install(const QString& symbolicName);
+  static long install(const QString& symbolicName, ctkPluginContext* context = 0);
 
   /**
-   * Start the CTK Plugin Framework and/or the plugin with the given
-   * symbolic name.
-   *
-   * <p>
-   * This method creates and initializes the CTK Plugin Framework if it
-   * has not been previously initialized and starts it. It then optionally
-   * starts the plugin with the given symbolic name and start options.
+   * This method instantiates, initializes, and starts an instance of a
+   * ctkPluginFramework object, if no plugin context is provided and
+   * if there is no framework already running. It then installs and
+   * starts the plugin with the given symbolic name (if not empty).
    *
    * <p>
    * If a symbolic name is given, the plugin is searched in the paths previously given by calls
    * to #addSearchPath(const QString&, bool) and in the default
-   * CTK plugin installation path.
+   * CTK plugin installation path and is started using the given <code>options</code>.
+   *
+   * <p>
+   * If a plugin context is provided, this context is used to install the plugin,
+   * otherwise the Plugin Framework context is used.
    *
    * \param symbolicName The symbolic name of the plugin to install.
    * \param options The options used to start the plugin.
+   * \param context The plugin context to use for installing the plugin.
    * \return <code>true</code> if the plugin was found and successfully
    *         installed, <code>false</code> otherwise.
    *
    * \see ctkPlugin::StartOption
    */
-  static bool start(const QString& symbolicName = QString(), ctkPlugin::StartOptions options = ctkPlugin::START_ACTIVATION_POLICY);
+  static bool start(const QString& symbolicName = QString(),
+                    ctkPlugin::StartOptions options = ctkPlugin::START_ACTIVATION_POLICY,
+                    ctkPluginContext* context = 0);
 
   /**
    * Get the plugin context for the Plugin Framework.
@@ -118,11 +123,11 @@ public:
   static QSharedPointer<ctkPluginFramework> getPluginFramework();
 
   /**
-   * Append a path to the PATH environment variable.
+   * This is a utility method to append a path to the PATH environment variable
+   * on Windows platforms.
    *
    * <p>
-   * This method appends the given path to the PATH environment variable.
-   * It does nothing on non-Windows platforms.
+   * This method does nothing on non-Windows platforms.
    *
    * \param path The path to be appended to PATH
    */
@@ -156,6 +161,18 @@ public:
    */
   static QString getPluginPath(const QString& symbolicName);
 
+  /**
+   * Get a list of symbolic names for the plugins in <code>searchPath</code>.
+   *
+   * <p>
+   * The symbolic names are deduced from the shared libraries found in
+   * <code>searchPath</code>, which may not represent loadable CTK plugins.
+   *
+   * \param searchPath The path to look for plugins.
+   * \return A list of potential plugin symbolic names.
+   */
+  static QStringList getPluginSymbolicNames(const QString& searchPath);
+
 private:
 
   static const QScopedPointer<ctkPluginFrameworkLauncherPrivate> d;

+ 5 - 12
Utilities/LastConfigureStep/CTKGenerateCTKConfig.cmake

@@ -38,6 +38,8 @@
 # one for installation.  The file tells external projects how to use CTK.
 #
 
+INCLUDE(ctkFunctionGeneratePluginUseFile)
+
 # Construct version numbers for CTKConfigVersion.cmake.
 SET(_CTK_VERSION_MAJOR ${CTK_MAJOR_VERSION})
 SET(_CTK_VERSION_MINOR ${CTK_MINOR_VERSION})
@@ -76,18 +78,9 @@ ENDFOREACH()
 # as an external library
 EXPORT(TARGETS ${CTK_TARGETS_TO_EXPORT} FILE ${CTK_SUPERBUILD_BINARY_DIR}/CTKExports.cmake)
 
-# Write a set of variables containing plugin specific include and library directories
-SET(CTK_PLUGIN_INCLUDE_DIRS_CONFIG)
-FOREACH(plugin ${CTK_PLUGIN_LIBRARIES})
-  SET(${plugin}_INCLUDE_DIRS ${${plugin}_SOURCE_DIR} ${${plugin}_BINARY_DIR})
-  ctkFunctionGetIncludeDirs(${plugin}_INCLUDE_DIRS ${plugin})
-  SET(CTK_PLUGIN_INCLUDE_DIRS_CONFIG "${CTK_PLUGIN_INCLUDE_DIRS_CONFIG}
-SET(${plugin}_INCLUDE_DIRS \"${${plugin}_INCLUDE_DIRS}\")")
-  
-  ctkFunctionGetLibraryDirs(${plugin}_LIBRARY_DIRS ${plugin})
-  SET(CTK_PLUGIN_LIBRARY_DIRS_CONFIG "${CTK_PLUGIN_LIBRARY_DIRS_CONFIG}
-SET(${plugin}_LIBRARY_DIRS \"${${plugin}_LIBRARY_DIRS}\")")
-ENDFOREACH()
+# Generate a file containing plugin specific variables
+SET(CTK_PLUGIN_USE_FILE "${CTK_SUPERBUILD_BINARY_DIR}/CTKPluginUseFile.cmake")
+ctkFunctionGeneratePluginUseFile(${CTK_PLUGIN_USE_FILE})
 
 # Write a set of variables containing library specific include and library directories
 SET(CTK_LIBRARY_INCLUDE_DIRS_CONFIG)