Browse Source

Merge topic 'plugin_dependencies'

  STYLE: DGraph: added the -sort argument to the help message
  ENH: PluginDependencies: Remove test plugins
  ENH: DependencyGraph: Added tests for cycle detection and subgraph ordering on disconnected (directed) graphs
  ENH: PluginDependencies: Added test dependencies
  FIX: PluginDependencies: Fix dynamic export header generation
  ENH: PluginDependencies: Use "DGraph -sort label" in ctkMacroValidateBuildOptions
  FIX: Fixed counting of vertices.
  ENH: DGraph: Restored old behavior for -paths argument. Added new
  FIX: DependencyGraph: Fix cycle detection by reseting the discovered and
  FIX: DGraph: Use topological sort on subgraphs to print dependencies for
  ENH: DependencyGraph: Enhanced the topologicalSort() method to be able to
  ENH: Dependency Graph: find a path from between two vertices in graphs
  ENH: DependencyGraph: When checking for a cycle, check all vertices, not
  STYLE: DependencyGraph: fix comment
  ENH: DependencyGraph: Set the parent even in the case when a vertex
  FIX: DependencyGraph: Cycle detection failed for example on 1->2->1.
  FIX: DependenyGraph: Fixed infinite loop if an empty or commented line is encountered.
  ENH: DependencyGraph: Added a method to retrieve all source vertices (with
  STYLE: DependencyGraph: Rename Degree to OutDegree and indegree to outdegree, because they
  ENH: first draft for a plugin dependency system
Jean-Christophe Fillion-Robin 14 years ago
parent
commit
7b68427630

+ 8 - 2
CMake/ctkFunctionGenerateDGraphInput.cmake

@@ -63,8 +63,6 @@ FUNCTION(ctkFunctionGenerateDGraphInput dir target_directories with_option)
       ENDIF()
       #MESSAGE(STATUS target_project_name:${target_project_name})
 
-      LIST(APPEND vertices ${target_project_name})
-
       # Make sure the variable is cleared
       SET(dependencies )
 
@@ -77,16 +75,24 @@ FUNCTION(ctkFunctionGenerateDGraphInput dir target_directories with_option)
       # filter dependencies starting with CTK
       ctkMacroGetAllCTKTargetLibraries("${dependencies}" ctk_dependencies)
 
+      IF(ctk_dependencies)
+        LIST(APPEND vertices ${target_project_name})
+      ENDIF()
+
       # Generate XML related to the dependencies
       FOREACH(dependency_name ${ctk_dependencies})
         LIST(APPEND edges ${dependency_name})
         SET(dgraph_list ${dgraph_list} "${target_project_name} ${dependency_name}\n")
+        LIST(APPEND vertices ${dependency_name})
       ENDFOREACH()
 
     ENDIF()
     
   ENDFOREACH()
 
+  IF(vertices)
+    LIST(REMOVE_DUPLICATES vertices)
+  ENDIF()
   LIST(LENGTH vertices numberOfVertices)
   LIST(LENGTH edges numberOfEdges)
 

+ 4 - 3
CMake/ctkMacroGeneratePluginManifest.cmake

@@ -3,7 +3,7 @@
 #  CTK/CMake/ctkMacroParseArguments.cmake
 #
 
-MACRO(ctkMacroGeneratePluginManifest QRC_SRCS)
+FUNCTION(ctkFunctionGeneratePluginManifest QRC_SRCS)
 
   CtkMacroParseArguments(MY
     "ACTIVATIONPOLICY;CATEGORY;CONTACT_ADDRESS;COPYRIGHT;DESCRIPTION;DOC_URL;ICON;LICENSE;NAME;REQUIRE_PLUGIN;SYMBOLIC_NAME;VENDOR;VERSION"
@@ -60,7 +60,8 @@ MACRO(ctkMacroGeneratePluginManifest QRC_SRCS)
   ENDIF()
 
   IF(DEFINED MY_REQUIRE_PLUGIN)
-    SET(_manifest_content "${_manifest_content}\nRequire-Plugin: ${MY_REQUIRE_PLUGIN}")
+    STRING(REPLACE ";" "," require_plugin "${MY_REQUIRE_PLUGIN}")
+    SET(_manifest_content "${_manifest_content}\nRequire-Plugin: ${require_plugin}")
   ENDIF()
 
   IF(DEFINED MY_VENDOR)
@@ -89,4 +90,4 @@ MACRO(ctkMacroGeneratePluginManifest QRC_SRCS)
 
   QT4_ADD_RESOURCES(${QRC_SRCS} ${_manifest_qrc_filepath})
 
-ENDMACRO()
+ENDFUNCTION()

+ 41 - 35
CMake/ctkMacroBuildPlugin.cmake

@@ -42,21 +42,17 @@
 # Plugin-License
 # Plugin-Name
 # Require-Plugin
-# Plugin-SymbolicName
 # Plugin-Vendor
 # Plugin-Version
 #
 MACRO(ctkMacroBuildPlugin)
   CtkMacroParseArguments(MY
-    "NAME;EXPORT_DIRECTIVE;SRCS;MOC_SRCS;UI_FORMS;INCLUDE_DIRECTORIES;TARGET_LIBRARIES;RESOURCES;CACHED_RESOURCEFILES;LIBRARY_TYPE"
+    "EXPORT_DIRECTIVE;SRCS;MOC_SRCS;UI_FORMS;INCLUDE_DIRECTORIES;TARGET_LIBRARIES;RESOURCES;CACHED_RESOURCEFILES;LIBRARY_TYPE"
     ""
     ${ARGN}
     )
 
   # Sanity checks
-  IF(NOT DEFINED MY_NAME)
-    MESSAGE(SEND_ERROR "NAME is mandatory")
-  ENDIF()
   IF(NOT DEFINED MY_EXPORT_DIRECTIVE)
     MESSAGE(SEND_ERROR "EXPORT_DIRECTIVE is mandatory")
   ENDIF()
@@ -65,7 +61,31 @@ MACRO(ctkMacroBuildPlugin)
   ENDIF()
 
   # Define library name
-  SET(lib_name ${MY_NAME})
+  SET(lib_name ${PROJECT_NAME})
+
+  # Clear the variables for the manifest headers
+  SET(Plugin-ActivationPolicy )
+  SET(Plugin-Category )
+  SET(Plugin-ContactAddress )
+  SET(Plugin-Copyright )
+  SET(Plugin-Description )
+  SET(Plugin-DocURL )
+  SET(Plugin-Icon )
+  SET(Plugin-License )
+  SET(Plugin-Name )
+  SET(Require-Plugin )
+  SET(Plugin-SymbolicName )
+  SET(Plugin-Vendor )
+  SET(Plugin-Version )
+
+  # If a file named manifest_headers.cmake exists, read it
+  SET(manifest_headers_dep )
+  IF(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/manifest_headers.cmake")
+    INCLUDE(${CMAKE_CURRENT_SOURCE_DIR}/manifest_headers.cmake)
+    SET(manifest_headers_dep "${CMAKE_CURRENT_SOURCE_DIR}/manifest_headers.cmake")
+  ENDIF()
+
+  STRING(REPLACE "_" "." Plugin-SymbolicName ${lib_name})
 
   # --------------------------------------------------------------------------
   # Include dirs
@@ -75,12 +95,25 @@ MACRO(ctkMacroBuildPlugin)
     ${CMAKE_CURRENT_BINARY_DIR}
     ${MY_INCLUDE_DIRECTORIES}
     )
+
+  # Add the include directories from the plugin dependencies
+  # The variable ${lib_name}_DEPENDENCIES is set in the
+  # macro ctkMacroValidateBuildOptions
+  FOREACH(dep ${${lib_name}_DEPENDENCIES})
+    LIST(APPEND my_includes
+         ${${dep}_SOURCE_DIR}
+         ${${dep}_BINARY_DIR}
+         )
+  ENDFOREACH()
+
+  LIST(REMOVE_DUPLICATES my_includes)
+
   INCLUDE_DIRECTORIES(
     ${my_includes}
     )
  
   SET(MY_LIBRARY_EXPORT_DIRECTIVE ${MY_EXPORT_DIRECTIVE})
-  SET(MY_EXPORT_HEADER_PREFIX ${MY_NAME})
+  SET(MY_EXPORT_HEADER_PREFIX "${lib_name}_")
   SET(MY_LIBNAME ${lib_name})
   
   CONFIGURE_FILE(
@@ -102,36 +135,9 @@ MACRO(ctkMacroBuildPlugin)
     QT4_ADD_RESOURCES(MY_QRC_SRCS ${MY_RESOURCES})
   ENDIF()
 
-  # Clear the variables for the manifest headers
-  SET(Plugin-ActivationPolicy )
-  SET(Plugin-Category )
-  SET(Plugin-ContactAddress )
-  SET(Plugin-Copyright )
-  SET(Plugin-Description )
-  SET(Plugin-DocURL )
-  SET(Plugin-Icon )
-  SET(Plugin-License )
-  SET(Plugin-Name )
-  SET(Require-Plugin )
-  SET(Plugin-SymbolicName )
-  SET(Plugin-Vendor )
-  SET(Plugin-Version )
-
-  # If a file named manifest_headers.cmake exists, read it
-  SET(manifest_headers_dep )
-  IF(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/manifest_headers.cmake")
-    INCLUDE(${CMAKE_CURRENT_SOURCE_DIR}/manifest_headers.cmake)
-    SET(manifest_headers_dep "${CMAKE_CURRENT_SOURCE_DIR}/manifest_headers.cmake")
-  ENDIF()
-
-  # Set the plugin_symbolicname to the library name if it is not set
-  IF(NOT Plugin-SymbolicName)
-    STRING(REPLACE "_" "." Plugin-SymbolicName ${lib_name})
-  ENDIF()
-
   # Add the generated manifest qrc file
   SET(manifest_qrc_src )
-  ctkMacroGeneratePluginManifest(manifest_qrc_src
+  ctkFunctionGeneratePluginManifest(manifest_qrc_src
     ACTIVATIONPOLICY ${Plugin-ActivationPolicy}
     CATEGORY ${Plugin-Category}
     CONTACT_ADDRESS ${Plugin-ContactAddress}

+ 29 - 0
CMake/ctkMacroGeneratePluginList.cmake

@@ -0,0 +1,29 @@
+#
+# Depends on:
+#  CTK/CMake/ctkMacroParseArguments.cmake
+#
+
+MACRO(ctkMacroGeneratePluginList )
+
+  CtkMacroParseArguments(MY
+    "FILE;DIRECTORIES"
+    ""
+    ${ARGN}
+    )
+
+  # Sanity checks
+  IF(NOT DEFINED MY_FILE)
+    MESSAGE(SEND_ERROR "FILE is mandatory")
+  ENDIF()
+
+  SET(_content )
+  FOREACH(plugin ${MY_DIRECTORIES})
+    SET(_content "${_content}\n
+
+  ENDFOREACH()
+
+
+  FILE(WRITE "${_manifest_filepath}" "${_manifest_content}")
+  FILE(WRITE "${_manifest_qrc_filepath}" "${_manifest_qrc_content}")
+
+ENDMACRO()

+ 90 - 40
CMake/ctkMacroTargetLibraries.cmake

@@ -24,34 +24,57 @@
 MACRO(ctkMacroGetTargetLibraries varname)
 
   SET(filepath ${CMAKE_CURRENT_SOURCE_DIR}/target_libraries.cmake)
+  SET(manifestpath ${CMAKE_CURRENT_SOURCE_DIR}/manifest_headers.cmake)
 
-  # Check if "target_libraries.cmake" file exists
-  IF(NOT EXISTS ${filepath})
-    MESSAGE(FATAL_ERROR "${filepath} doesn't exists !")
+  # Check if "target_libraries.cmake" or "manifest_headers.cmake" file exists
+  IF(NOT EXISTS ${filepath} AND NOT EXISTS ${manifestpath})
+    MESSAGE(FATAL_ERROR "${filepath} or ${manifestpath} doesn't exists !")
   ENDIF()
 
   # Make sure the variable is cleared
   SET(target_libraries )
+  SET(Require-Plugin )
+
+  IF(EXISTS ${filepath})
+    # Let's make sure target_libraries contains only strings
+    FILE(STRINGS "${filepath}" stringtocheck) # read content of 'filepath' into 'stringtocheck'
+    STRING(REGEX MATCHALL "[^\\#]\\$\\{.*\\}" incorrect_elements ${stringtocheck})
+    FOREACH(incorrect_element ${incorrect_elements})
+      STRING(REGEX REPLACE "\\$|\\{|\\}" "" correct_element ${incorrect_element})
+      MESSAGE(FATAL_ERROR "In ${filepath}, ${incorrect_element} should be replaced by ${correct_element}")
+    ENDFOREACH()
+
+    INCLUDE(${filepath})
+
+    # Loop over all target library, if it does *NOT* start with "CTK",
+    # let's resolve the variable to access its content
+    FOREACH(target_library ${target_libraries})
+      IF(${target_library} MATCHES "^CTK[a-zA-Z0-9]+$")
+        LIST(APPEND ${varname} ${target_library})
+      ELSE()
+        LIST(APPEND ${varname} "${${target_library}}")
+      ENDIF()
+    ENDFOREACH()
+  ENDIF()
 
-  # Let's make sure target_libraries contains only strings
-  FILE(STRINGS "${filepath}" stringtocheck) # read content of 'filepath' into 'stringtocheck'
-  STRING(REGEX MATCHALL "[^\\#]\\$\\{.*\\}" incorrect_elements ${stringtocheck})
-  FOREACH(incorrect_element ${incorrect_elements})
-    STRING(REGEX REPLACE "\\$|\\{|\\}" "" correct_element ${incorrect_element})
-    MESSAGE(FATAL_ERROR "In ${filepath}, ${incorrect_element} should be replaced by ${correct_element}")
-  ENDFOREACH()
-
-  INCLUDE(${filepath})
+  IF(EXISTS ${manifestpath})
+    # Let's make sure Require-Plugins contains only strings
+    FILE(STRINGS "${manifestpath}" stringtocheck) # read content of 'manifestpath' into 'stringtocheck'
+    STRING(REGEX MATCHALL "[^\\#]\\$\\{.*\\}" incorrect_elements ${stringtocheck})
+    FOREACH(incorrect_element ${incorrect_elements})
+      STRING(REGEX REPLACE "\\$|\\{|\\}" "" correct_element ${incorrect_element})
+      MESSAGE(FATAL_ERROR "In ${manifestpath}, ${incorrect_element} should be replaced by ${correct_element}")
+    ENDFOREACH()
+
+    INCLUDE(${manifestpath})
+
+    # Loop over all plugin dependencies,
+    FOREACH(plugin_symbolicname ${Require-Plugin})
+      STRING(REPLACE "." "_" plugin_library ${plugin_symbolicname})
+      LIST(APPEND ${varname} ${plugin_library})
+    ENDFOREACH()
+  ENDIF()
 
-  # Loop over all target library, if it does *NOT* start with "CTK",
-  # let's resolve the variable to access its content
-  FOREACH(target_library ${target_libraries})
-    IF(${target_library} MATCHES "^CTK[a-zA-Z0-9]+$")
-      LIST(APPEND ${varname} ${target_library})
-    ELSE()
-      LIST(APPEND ${varname} "${${target_library}}")
-    ENDIF()
-  ENDFOREACH()
 ENDMACRO()
 
 #
@@ -63,27 +86,49 @@ MACRO(ctkMacroCollectTargetLibraryNames target_dir varname)
   SET(lib_targets)
 
   SET(filepath ${target_dir}/target_libraries.cmake)
-  #MESSAGE(STATUS filepath:${filepath})
+  SET(manifestpath ${target_dir}/manifest_headers.cmake)
 
-  # Check if "target_libraries.cmake" file exists
-  IF(NOT EXISTS ${filepath})
-    MESSAGE(FATAL_ERROR "${filepath} doesn't exists !")
+  # Check if "target_libraries.cmake" or "manifest_headers.cmake" file exists
+  IF(NOT EXISTS ${filepath} AND NOT EXISTS ${manifestpath})
+    MESSAGE(FATAL_ERROR "${filepath} or ${manifestpath} doesn't exists !")
   ENDIF()
 
-  # Let's make sure target_libraries contains only strings
-  FILE(STRINGS "${filepath}" stringtocheck)
-  STRING(REGEX MATCHALL "[^#]\\$\\{.*\\}" incorrect_elements ${stringtocheck})
-  FOREACH(incorrect_element ${incorrect_elements})
-    STRING(REGEX REPLACE "\\$|\\{|\\}" "" correct_element ${incorrect_element})
-    MESSAGE(FATAL_ERROR "In ${filepath}, ${incorrect_element} should be replaced by ${correct_element}")
-  ENDFOREACH()
-
   # Make sure the variable is cleared
-  SET(target_libraries)
+  SET(target_libraries )
+  SET(Require-Plugin )
 
-  INCLUDE(${filepath})
+  IF(EXISTS ${filepath})
+    # Let's make sure target_libraries contains only strings
+    FILE(STRINGS "${filepath}" stringtocheck) # read content of 'filepath' into 'stringtocheck'
+    STRING(REGEX MATCHALL "[^\\#]\\$\\{.*\\}" incorrect_elements ${stringtocheck})
+    FOREACH(incorrect_element ${incorrect_elements})
+      STRING(REGEX REPLACE "\\$|\\{|\\}" "" correct_element ${incorrect_element})
+      MESSAGE(FATAL_ERROR "In ${filepath}, ${incorrect_element} should be replaced by ${correct_element}")
+    ENDFOREACH()
+
+    INCLUDE(${filepath})
+
+    LIST(APPEND ${varname} ${target_libraries})
+  ENDIF()
+
+  IF(EXISTS ${manifestpath})
+    # Let's make sure Require-Plugins contains only strings
+    FILE(STRINGS "${manifestpath}" stringtocheck) # read content of 'manifestpath' into 'stringtocheck'
+    STRING(REGEX MATCHALL "[^\\#]\\$\\{.*\\}" incorrect_elements ${stringtocheck})
+    FOREACH(incorrect_element ${incorrect_elements})
+      STRING(REGEX REPLACE "\\$|\\{|\\}" "" correct_element ${incorrect_element})
+      MESSAGE(FATAL_ERROR "In ${manifestpath}, ${incorrect_element} should be replaced by ${correct_element}")
+    ENDFOREACH()
+
+    INCLUDE(${manifestpath})
+
+    # Loop over all plugin dependencies
+    FOREACH(plugin_symbolicname ${Require-Plugin})
+      STRING(REPLACE "." "_" plugin_library ${plugin_symbolicname})
+      LIST(APPEND ${varname} ${plugin_library})
+    ENDFOREACH()
+  ENDIF()
 
-  LIST(APPEND ${varname} ${target_libraries})
   LIST(REMOVE_DUPLICATES ${varname})
 ENDMACRO()
 
@@ -121,25 +166,30 @@ MACRO(ctkMacroCollectAllTargetLibraries targets subdir varname)
       ctkMacroCollectTargetLibraryNames(${target_dir} target_libraries)
     ENDIF()
 
-    LIST(APPEND ${varname} ${target_libraries})
-    LIST(REMOVE_DUPLICATES ${varname})
+    IF(target_libraries)
+      LIST(APPEND ${varname} ${target_libraries})
+      LIST(REMOVE_DUPLICATES ${varname})
+    ENDIF()
   ENDFOREACH()
   
 ENDMACRO()
 
 #
-# Extract all library names starting with CTK uppercase
+# Extract all library names starting with CTK uppercase or org_commontk_
 #
 MACRO(ctkMacroGetAllCTKTargetLibraries all_target_libraries varname)
   SET(re_ctklib "^(c|C)(t|T)(k|K)[a-zA-Z0-9]+$")
+  SET(re_ctkplugin "^org_commontk_[a-zA-Z0-9]+$")
   SET(_tmp_list)
   LIST(APPEND _tmp_list ${all_target_libraries})
   ctkMacroListFilter(_tmp_list re_ctklib OUTPUT_VARIABLE ${varname})
+  ctkMacroListFilter(_tmp_list re_ctkplugin)
+  LIST(APPEND ${varname} ${_tmp_list})
   #MESSAGE(STATUS varname:${varname}:${${varname}})
 ENDMACRO()
 
 #
-# Extract all library names *NOT* starting with CTK uppercase
+# Extract all library names *NOT* starting with CTK uppercase or org_commontk_
 #
 MACRO(ctkMacroGetAllNonCTKTargetLibraries all_target_libraries varname)
   ctkMacroGetAllCTKTargetLibraries("${all_target_libraries}" all_ctk_libraries)

+ 14 - 2
CMakeLists.txt

@@ -151,7 +151,7 @@ INCLUDE(CMake/ctkMacroValidateBuildOptions.cmake)
 INCLUDE(CMake/ctkMacroAddCtkLibraryOptions.cmake)
 INCLUDE(CMake/ctkFunctionGenerateDGraphInput.cmake)
 INCLUDE(CMake/ctkFunctionGenerateProjectXml.cmake)
-INCLUDE(CMake/ctkMacroGeneratePluginManifest.cmake)
+INCLUDE(CMake/ctkFunctionGeneratePluginManifest.cmake)
 INCLUDE(CMake/ctkMacroGeneratePluginResourceFile.cmake)
 INCLUDE(CMake/ctkFunctionCheckCompilerFlags.cmake)
 
@@ -348,6 +348,18 @@ FOREACH(plugin ${CTK_PLUGINS})
   LIST(APPEND CTK_PLUGINS_SUBDIRS "${plugin_name}")
 ENDFOREACH()
 
+# Create a file with variables containing the paths to the source, binary, and library directory
+# of each enabled plugin. This is needed to setup include and library dependencies to other plugins
+#SET(CTK_PLUGIN_LIST "${CMAKE_CURRENT_BINARY_DIR}/ctkPluginList.cmake")
+#SET(CTK_PLUGINS_SUBDIRS_ENABLED )
+#FOREACH(plugin ${CTK_PLUGINS_SUBDIRS})
+#  IF (CTK_PLUGIN_${plugin})
+#    LIST(APPEND CTK_PLUGINS_SUBDIRS_ENABLED Plugins/${plugin})
+#  ENDIF()
+#ENDFOREACH()
+#ctkMacroGeneratePluginList(FILE ${CTK_PLUGIN_LIST}
+#                           DIRECTORIES ${CTK_PLUGINS_SUBDIRS_ENABLED})
+
 # Build options associated with CTK applications
 FOREACH(app ${CTK_APPLICATIONS})
   ctkFunctionExtractOptionNameAndValue(${app} app_name app_value)
@@ -501,7 +513,7 @@ ENDFOREACH()
 # Add CTK plugin subdirectories
 #
 FOREACH(plugin ${CTK_PLUGINS_SUBDIRS})
-  IF (CTK_PLUGIN_${plugin})
+  IF(CTK_PLUGIN_${plugin})
     ADD_SUBDIRECTORY(Plugins/${plugin})
   ENDIF()
 ENDFOREACH()

+ 2 - 0
Libs/Core/Testing/Cpp/CMakeLists.txt

@@ -10,6 +10,7 @@ CREATE_TEST_SOURCELIST(Tests ${KIT}CppTests.cpp
   ctkModelTesterTest1.cpp
   ctkUtilsTest1.cpp
   ctkDependencyGraphTest1.cpp
+  ctkDependencyGraphTest2.cpp
   ctkPimplTest1.cpp
   ctkSingletonTest1.cpp
   #EXTRA_INCLUDE TestingMacros.h
@@ -39,6 +40,7 @@ ENDMACRO( SIMPLE_TEST  )
 
 SIMPLE_TEST( ctkCommandLineParserTest1 )
 SIMPLE_TEST( ctkDependencyGraphTest1 )
+SIMPLE_TEST( ctkDependencyGraphTest2 )
 SIMPLE_TEST( ctkModelTesterTest1 )
 SIMPLE_TEST( ctkPimplTest1 )
 SIMPLE_TEST( ctkSingletonTest1 )

+ 326 - 0
Libs/Core/Testing/Cpp/ctkDependencyGraphTest2.cpp

@@ -0,0 +1,326 @@
+/*=========================================================================
+
+  Library:   CTK
+ 
+  Copyright (c) 2010 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.commontk.org/LICENSE
+
+  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.
+ 
+=========================================================================*/
+
+// CTK includes
+#include "ctkDependencyGraph.h"
+
+// STL includes
+#include <cstdlib>
+#include <iostream>
+
+// Qt includes
+#include <QDebug>
+
+
+int ctkDependencyGraphTest2(int argc, char * argv [] )
+{
+  Q_UNUSED(argc);
+  Q_UNUSED(argv);  
+
+  // check that cycle detection works
+  {
+  const int numberOfVertices = 2;
+
+  ctkDependencyGraph graph(numberOfVertices);
+
+  //
+  //  1  ->  2
+  //   \    /
+  //     <-
+  //
+  graph.insertEdge(1,2);
+  graph.insertEdge(2,1);
+
+  int expectedNumberOfEdge = 2;
+  
+  int nov = graph.numberOfVertices();
+
+  if( nov != numberOfVertices )
+    {
+    qCritical() << "Number of vertices does not match (expected" << numberOfVertices << "got" << nov << ")";
+    return EXIT_FAILURE;
+    }
+
+  int noe = graph.numberOfEdges();
+  if( noe != expectedNumberOfEdge )
+    {
+    qCritical() << "Number of edges does not match (expected" << expectedNumberOfEdge << "got" << noe << ")";
+    return EXIT_FAILURE;
+    }
+
+  bool cfc = graph.checkForCycle();
+  
+  if( cfc == false )
+    {
+    qCritical() << "Cycle detection failed";
+    return EXIT_FAILURE;
+    }
+
+  bool cdtd = graph.cycleDetected();
+
+  if( cdtd == false )
+    {
+    qCritical() << "Cycle detected flag wrong";
+    return EXIT_FAILURE;
+    }
+
+  int co = graph.cycleOrigin();
+  int ce = graph.cycleEnd();
+
+  if (co != 2)
+    {
+    qCritical() << "Wrong cycle origin (expected" << 2 << "got" << co << ")";
+    return EXIT_FAILURE;
+    }
+
+  if (ce != 1)
+    {
+    qCritical() << "Wrong cycle end (expected" << 1 << "got" << ce << ")";
+    return EXIT_FAILURE;
+    }
+
+  }
+
+  {
+  const int numberOfVertices = 4;
+
+  ctkDependencyGraph graph(numberOfVertices);
+
+  //         ->  3
+  //       /
+  // 1 -> 2  ->  4
+  //       \    /
+  //         <-
+  //
+  graph.insertEdge(1,2);
+  graph.insertEdge(2,3);
+  graph.insertEdge(2,4);
+  graph.insertEdge(4,2);
+
+  int expectedNumberOfEdge = 4;
+
+  int nov = graph.numberOfVertices();
+
+  if( nov != numberOfVertices )
+    {
+    qCritical() << "Number of vertices does not match (expected" << numberOfVertices << "got" << nov << ")";
+    return EXIT_FAILURE;
+    }
+
+  int noe = graph.numberOfEdges();
+  if( noe != expectedNumberOfEdge )
+    {
+    qCritical() << "Number of edges does not match (expected" << expectedNumberOfEdge << "got" << noe << ")";
+    return EXIT_FAILURE;
+    }
+
+  bool cfc = graph.checkForCycle();
+
+  if( cfc == false )
+    {
+    qCritical() << "Cycle detection failed";
+    return EXIT_FAILURE;
+    }
+
+  bool cdtd = graph.cycleDetected();
+
+  if( cdtd == false )
+    {
+    qCritical() << "Cycle detected flag wrong";
+    return EXIT_FAILURE;
+    }
+
+  int co = graph.cycleOrigin();
+  int ce = graph.cycleEnd();
+
+  if (co != 2)
+    {
+    qCritical() << "Wrong cycle origin (expected" << 2 << "got" << co << ")";
+    return EXIT_FAILURE;
+    }
+
+  if (ce != 4)
+    {
+    qCritical() << "Wrong cycle end (expected" << 4 << "got" << ce << ")";
+    return EXIT_FAILURE;
+    }
+
+  }
+
+  // check that cycle detection works on disconnected graphs
+  {
+  const int numberOfVertices = 8;
+
+  ctkDependencyGraph graph(numberOfVertices);
+
+  /* 1 -> 2  -> 3
+   *       \
+   *         -> 4
+   *
+   *         ->  7
+   *       /
+   * 5 -> 6  ->  8
+   *  \         /
+   *    ---<---
+   */
+  graph.insertEdge(1,2);
+  graph.insertEdge(2,3);
+  graph.insertEdge(2,4);
+  graph.insertEdge(5,6);
+  graph.insertEdge(6,7);
+  graph.insertEdge(6,8);
+  graph.insertEdge(8,5);
+
+  int expectedNumberOfEdge = 7;
+
+  int nov = graph.numberOfVertices();
+
+  if( nov != numberOfVertices )
+    {
+    qCritical() << "Number of vertices does not match (expected" << numberOfVertices << "got" << nov << ")";
+    return EXIT_FAILURE;
+    }
+
+  int noe = graph.numberOfEdges();
+  if( noe != expectedNumberOfEdge )
+    {
+    qCritical() << "Number of edges does not match (expected" << expectedNumberOfEdge << "got" << noe << ")";
+    return EXIT_FAILURE;
+    }
+
+  bool cfc = graph.checkForCycle();
+
+  if( cfc == false )
+    {
+    qCritical() << "Cycle detection failed";
+    return EXIT_FAILURE;
+    }
+
+  bool cdtd = graph.cycleDetected();
+
+  if( cdtd == false )
+    {
+    qCritical() << "Cycle detected flag wrong";
+    return EXIT_FAILURE;
+    }
+
+  }
+
+  // check that topological ordering and paths
+  // work on disconnected graphs
+  {
+  const int numberOfVertices = 11;
+
+  ctkDependencyGraph graph(numberOfVertices);
+
+  /* 1 -> 2  -> 3
+   *       \
+   *         -> 4
+   *
+   *         ->  7 ->
+   *       /          \
+   * 5 -> 6  ->  8 ->  9
+   *             ^
+   *             |
+   *            10 -> 11
+   */
+  graph.insertEdge(1,2);
+  graph.insertEdge(2,3);
+  graph.insertEdge(2,4);
+  graph.insertEdge(5,6);
+  graph.insertEdge(6,7);
+  graph.insertEdge(6,8);
+  graph.insertEdge(7,9);
+  graph.insertEdge(8,9);
+  graph.insertEdge(10,8);
+  graph.insertEdge(10,11);
+
+  int expectedNumberOfEdge = 10;
+
+  int nov = graph.numberOfVertices();
+
+  if( nov != numberOfVertices )
+    {
+    qCritical() << "Number of vertices does not match (expected" << numberOfVertices << "got" << nov << ")";
+    return EXIT_FAILURE;
+    }
+
+  int noe = graph.numberOfEdges();
+  if( noe != expectedNumberOfEdge )
+    {
+    qCritical() << "Number of edges does not match (expected" << expectedNumberOfEdge << "got" << noe << ")";
+    return EXIT_FAILURE;
+    }
+
+  bool cfc = graph.checkForCycle();
+
+  if( cfc == true )
+    {
+    qCritical() << "Cycle detection failed";
+    return EXIT_FAILURE;
+    }
+
+  bool cdtd = graph.cycleDetected();
+
+  if( cdtd == true )
+    {
+    qCritical() << "Cycle detected flag wrong";
+    return EXIT_FAILURE;
+    }
+
+  QList<int> sources;
+  graph.sourceVertices(sources);
+
+  QList<int> expectedSources;
+  expectedSources << 1 << 5 << 10;
+
+  if (sources != expectedSources)
+    {
+    qCritical() << "Source vertices do not match (expected" << expectedSources << "got" << sources << ")";
+    return EXIT_FAILURE;
+    }
+
+  QList<int> globalSort;
+  graph.topologicalSort(globalSort);
+
+  QList<int> expectedGlobalSort;
+  expectedGlobalSort << 1 << 5 << 10 << 2 << 6 << 11 << 3 << 4 << 7 << 8 << 9;
+  if (globalSort != expectedGlobalSort)
+  {
+    qCritical() << "Topological sort error (expected" << expectedGlobalSort << "got" << globalSort << ")";
+    return EXIT_FAILURE;
+  }
+
+  QList<int> subSort10;
+  graph.topologicalSort(subSort10, 10);
+
+  QList<int> expectedSubSort10;
+  expectedSubSort10 << 10 << 8 << 11 << 9;
+  if (subSort10 != expectedSubSort10)
+  {
+    qCritical() << "Topological subgraph sort error (expected" << expectedSubSort10 << "got" << subSort10 << ")";
+    return EXIT_FAILURE;
+  }
+
+  }
+
+  return EXIT_SUCCESS;
+}

+ 193 - 47
Libs/Core/ctkDependencyGraph.cpp

@@ -38,8 +38,8 @@ class ctkDependencyGraph::ctkInternal
 public:
   ctkInternal(ctkDependencyGraph* p);
   
-  /// Compute indegree
-  void computeIndegrees(QVarLengthArray<int, MAXV>& computedIndegrees);
+  /// Compute outdegree
+  void computeOutdegrees(QVarLengthArray<int, MAXV>& computedOutdegrees);
   
   /// Traverse tree using Depth-first_search
   void traverseUsingDFS(int v);
@@ -58,10 +58,23 @@ public:
   
   void setEdge(int vertice, int degree, int value);
   int edge(int vertice, int degree);
-  
+
+  void verticesWithIndegree(int indegree, QList<int>& list);
+
+  int subgraphSize(int rootId);
+  void subgraphSizeRec(int rootId, QSet<int>& uniqueVertices);
+
+  void subgraphInsert(ctkDependencyGraph& subgraph, int rootId,
+                      QHash<int,int>& subgraphIdToGlobal, QHash<int,int>& globalIdToSubgraph);
+
+  int getOrGenerateSubgraphId(QHash<int, int>& subgraphIdToGlobal,
+                      QHash<int, int>& globalIdToSubgraph,
+                      int globalId);
+
   /// See http://en.wikipedia.org/wiki/Adjacency_list
   QVarLengthArray<QVarLengthArray<int,MAXDEGREE>*, MAXV+1> Edges;
-  QVarLengthArray<int, MAXV+1> Degree;
+  QVarLengthArray<int, MAXV+1> OutDegree;
+  QVarLengthArray<int, MAXV+1> InDegree;
   int NVertices;
   int NEdges;
   
@@ -101,18 +114,18 @@ ctkDependencyGraph::ctkInternal::ctkInternal(ctkDependencyGraph* p)
 }
 
 //----------------------------------------------------------------------------
-void ctkDependencyGraph::ctkInternal::computeIndegrees(QVarLengthArray<int, MAXV>& computedIndegrees)
+void ctkDependencyGraph::ctkInternal::computeOutdegrees(QVarLengthArray<int, MAXV>& computedOutdegrees)
 {
 	for (int i=1; i <= this->NVertices; i++)
 	  {
-	  computedIndegrees[i] = 0;
+    computedOutdegrees[i] = 0;
 	  }
 
 	for (int i=1; i <= this->NVertices; i++) 
 	  {
-		for (int j=0; j < this->Degree[i]; j++) 
+    for (int j=0; j < this->OutDegree[i]; j++)
 		  {
-		  computedIndegrees[ this->edge(i,j) ] ++;
+      computedOutdegrees[ this->edge(i,j) ] ++;
 		  }
 		}
 }
@@ -130,14 +143,14 @@ void ctkDependencyGraph::ctkInternal::traverseUsingDFS(int v)
 	this->processVertex(v);
 
   int y; // successor vertex
-	for (int i=0; i<this->Degree[v]; i++)
+  for (int i=0; i<this->OutDegree[v]; i++)
 	  {
 		y = this->edge(v, i);
 		if (this->P->shouldExcludeEdge(this->edge(v, i)) == false)
 		  {
+      this->Parent[y] = v;
 			if (this->Discovered[y] == false)
 			  {
-				this->Parent[y] = v;
 				this->traverseUsingDFS(y);
 			  } 
 			else 
@@ -160,7 +173,7 @@ void ctkDependencyGraph::ctkInternal::traverseUsingDFS(int v)
 //----------------------------------------------------------------------------
 void ctkDependencyGraph::ctkInternal::processEdge(int from, int to)
 {
-  if (this->Parent[from] != to)
+  if (this->Discovered[to] == true)
     {
     this->CycleDetected = true;
     this->CycleOrigin = to; 
@@ -229,7 +242,7 @@ void ctkDependencyGraph::ctkInternal::findPathsRec(
   
   QList<int> branch(*path);
   int child = from;
-  for (int j=0; j < this->Degree[child]; j++)
+  for (int j=0; j < this->OutDegree[child]; j++)
     {
     if (j == 0)
       {
@@ -248,6 +261,77 @@ void ctkDependencyGraph::ctkInternal::findPathsRec(
       }
     }
 }
+
+void ctkDependencyGraph::ctkInternal::verticesWithIndegree(int indegree, QList<int>& list)
+{
+  Q_ASSERT(indegree >= 0);
+
+  for (int i=1; i <= this->NVertices; i++)
+    {
+    if (this->InDegree[i] == indegree)
+      {
+      list << i;
+      }
+    }
+}
+
+void ctkDependencyGraph::ctkInternal::subgraphSizeRec(int rootId, QSet<int>& uniqueVertices)
+{
+  Q_ASSERT(rootId > 0);
+
+  for (int i = 0; i < this->OutDegree[rootId]; ++i)
+    {
+    int child = this->edge(rootId, i);
+    uniqueVertices << child;
+    subgraphSizeRec(child, uniqueVertices);
+    }
+}
+
+int ctkDependencyGraph::ctkInternal::subgraphSize(int rootId)
+{
+  Q_ASSERT(rootId > 0);
+
+  QSet<int> vertices;
+  vertices << rootId;
+  this->subgraphSizeRec(rootId, vertices);
+  return vertices.size();
+}
+
+void ctkDependencyGraph::ctkInternal::subgraphInsert(
+    ctkDependencyGraph& subgraph, int rootId,
+    QHash<int,int>& subgraphIdToGlobal, QHash<int,int>& globalIdToSubgraph)
+{
+  int from = this->getOrGenerateSubgraphId(subgraphIdToGlobal, globalIdToSubgraph, rootId);
+  for (int i = 0; i < this->OutDegree[rootId]; ++i)
+  {
+    int childId = this->edge(rootId, i);
+    int to = this->getOrGenerateSubgraphId(subgraphIdToGlobal, globalIdToSubgraph,
+                                           childId);
+
+    subgraph.insertEdge(from, to);
+    this->subgraphInsert(subgraph, childId, subgraphIdToGlobal, globalIdToSubgraph);
+  }
+}
+
+int ctkDependencyGraph::ctkInternal::getOrGenerateSubgraphId(
+    QHash<int, int>& subgraphIdToGlobal,
+    QHash<int, int>& globalIdToSubgraph,
+    int globalId)
+{
+  // If needed, generate vertex id
+  int subgraphId = -1;
+  if (!globalIdToSubgraph.keys().contains(globalId))
+    {
+    subgraphId = globalIdToSubgraph.keys().size() + 1;
+    globalIdToSubgraph[globalId] = subgraphId;
+    subgraphIdToGlobal[subgraphId] = globalId;
+    }
+  else
+    {
+    subgraphId = globalIdToSubgraph[globalId];
+    }
+  return subgraphId;
+}
     
 //----------------------------------------------------------------------------
 // ctkDependencyGraph methods
@@ -264,11 +348,13 @@ ctkDependencyGraph::ctkDependencyGraph(int nvertices)
   this->Internal->Discovered.resize(nvertices + 1);
   this->Internal->Parent.resize(nvertices + 1);
   this->Internal->Edges.resize(nvertices + 1);
-  this->Internal->Degree.resize(nvertices + 1);
+  this->Internal->OutDegree.resize(nvertices + 1);
+  this->Internal->InDegree.resize(nvertices + 1);
 
   for (int i=1; i <= nvertices; i++)
     {
-    this->Internal->Degree[i] = 0;
+    this->Internal->OutDegree[i] = 0;
+    this->Internal->InDegree[i] = 0;
     }
     
   // initialize Edge adjacency list
@@ -332,7 +418,7 @@ void ctkDependencyGraph::printGraph()
   for(int i=1; i <= this->Internal->NVertices; i++)
     {
     std::cout << i << ":";
-    for (int j=0; j < this->Internal->Degree[i]; j++)
+    for (int j=0; j < this->Internal->OutDegree[i]; j++)
       {
       std::cout << " " << this->Internal->edge(i, j);
       }
@@ -372,12 +458,55 @@ bool ctkDependencyGraph::shouldExcludeEdge(int edge)
 
 //----------------------------------------------------------------------------
 bool ctkDependencyGraph::checkForCycle()
-{
+{ 
   if (this->Internal->NEdges > 0)
     {
-    // get a valid vertice Id
-    int verticeId = 1;
-    this->Internal->traverseUsingDFS(verticeId);
+    // Store unprocessed vertex ids
+    QList<int> uncheckedVertices;
+    for (int i = 1; i <= this->Internal->NVertices; ++i)
+      {
+        uncheckedVertices << i;
+      }
+
+    // Start the cycle detection on the source vertices
+    QList<int> sources;
+    this->sourceVertices(sources);
+    foreach(int sourceId, sources)
+      {
+      this->Internal->traverseUsingDFS(sourceId);
+      if (this->cycleDetected()) return true;
+
+      for (int i=0; i < this->Internal->Processed.size(); i++)
+        {
+          if (this->Internal->Processed[i] == true)
+            {
+            uncheckedVertices.removeOne(i);
+            }
+
+          this->Internal->Discovered[i] = false;
+          this->Internal->Processed[i] = false;
+        }
+      }
+
+    // If a component does not have a source vertex,
+    // i.e. it is a cycle a -> b -> a, check all non
+    // processed vertices.
+    while (!uncheckedVertices.empty())
+      {
+      this->Internal->traverseUsingDFS(uncheckedVertices.last());
+      if (this->cycleDetected()) return true;
+
+      for (int i=0; i < this->Internal->Processed.size(); i++)
+        {
+          if (this->Internal->Processed[i] == true)
+            {
+            uncheckedVertices.removeOne(i);
+            }
+
+          this->Internal->Discovered[i] = false;
+          this->Internal->Processed[i] = false;
+        }
+      }
     }
   return this->cycleDetected();
 }
@@ -408,13 +537,14 @@ void ctkDependencyGraph::insertEdge(int from, int to)
   
   // resize if needed
   int capacity = this->Internal->Edges[from]->capacity(); 
-  if (this->Internal->Degree[from] > capacity)
+  if (this->Internal->OutDegree[from] > capacity)
     {
     this->Internal->Edges[from]->resize(capacity + capacity * 0.3);
     }
 
-  this->Internal->setEdge(from, this->Internal->Degree[from], to);
-  this->Internal->Degree[from]++;
+  this->Internal->setEdge(from, this->Internal->OutDegree[from], to);
+  this->Internal->OutDegree[from]++;
+  this->Internal->InDegree[to]++;
 
   this->Internal->NEdges++;
 }
@@ -449,59 +579,70 @@ void ctkDependencyGraph::findPaths(int from, int to, QList<QList<int>* >& paths)
 //----------------------------------------------------------------------------
 void ctkDependencyGraph::findPath(int from, int to, QList<int>& path)
 {
-  int child = from;
-  int parent = this->Internal->edge(child, 0);
-  path << child; 
-  while (parent > 0)
+  QList<QList<int>* > paths;
+  this->findPaths(from, to, paths);
+  if (!paths.empty())
     {
-    path << parent;
-    if (parent == to)
-      {
-      break;
-      }
-    child = parent;
-    parent = this->Internal->edge(child, 0);
+    path << *(paths.first());
     }
+
+  qDeleteAll(paths);
 }
 
 //----------------------------------------------------------------------------
-bool ctkDependencyGraph::topologicalSort(QList<int>& sorted)
+bool ctkDependencyGraph::topologicalSort(QList<int>& sorted, int rootId)
 {
-	QVarLengthArray<int, MAXV> indegree; // indegree of each vertex
-	QQueue<int> zeroin;	  // vertices of indegree 0
+  if (rootId > 0)
+    {
+    ctkDependencyGraph subgraph(this->Internal->subgraphSize(rootId));
+    QHash<int,int> subgraphIdToGlobal;
+    QHash<int,int> globalIdToSubgraph;
+    this->Internal->subgraphInsert(subgraph, rootId, subgraphIdToGlobal, globalIdToSubgraph);
+
+    QList<int> subgraphSorted;
+    bool result = subgraph.topologicalSort(subgraphSorted);
+    foreach(int subgraphId, subgraphSorted)
+      {
+      sorted << subgraphIdToGlobal[subgraphId];
+      }
+    return result;
+    }
+
+  QVarLengthArray<int, MAXV> outdegree; // outdegree of each vertex
+  QQueue<int> zeroout;	  // vertices of outdegree 0
 	int x, y;			        // current and next vertex
   
-  indegree.resize(this->Internal->NVertices + 1);
+  outdegree.resize(this->Internal->NVertices + 1);
 	
 	// resize if needed
 	if (this->Internal->NVertices > MAXV)
 	  {
-	  indegree.resize(this->Internal->NVertices);
+    outdegree.resize(this->Internal->NVertices);
 	  }
 
-	this->Internal->computeIndegrees(indegree);
+  this->Internal->computeOutdegrees(outdegree);
 	
 	for (int i=1; i <= this->Internal->NVertices; i++)
 	  {
-		if (indegree[i] == 0) 
+    if (outdegree[i] == 0)
 		  {
-		  zeroin.enqueue(i);
+      zeroout.enqueue(i);
 		  }
 		}
 
 	int j=0;
-	while (zeroin.empty() == false) 
+  while (zeroout.empty() == false)
 	  {
 		j = j+1;
-		x = zeroin.dequeue();
+    x = zeroout.dequeue();
 		sorted << x;
-		for (int i=0; i < this->Internal->Degree[x]; i++)
+    for (int i=0; i < this->Internal->OutDegree[x]; i++)
 		  {
 			y = this->Internal->edge(x, i);
-			indegree[y] --;
-			if (indegree[y] == 0)
+      outdegree[y] --;
+      if (outdegree[y] == 0)
 			  {
-			  zeroin.enqueue(y);
+        zeroout.enqueue(y);
 			  }
 		  }
 	  }
@@ -513,3 +654,8 @@ bool ctkDependencyGraph::topologicalSort(QList<int>& sorted)
 		
   return true;
 }
+
+void ctkDependencyGraph::sourceVertices(QList<int>& sources)
+{
+  this->Internal->verticesWithIndegree(0, sources);
+}

+ 6 - 2
Libs/Core/ctkDependencyGraph.h

@@ -85,10 +85,14 @@ public:
   /// Called each time an edge is visited
   virtual void processEdge(int /*from*/, int /*to*/){}
   
-  /// Perform a topological search
+  /// Perform a topological sort
   /// Return false if the graph contains cycles
+  /// If a rootId is given, the subgraph starting at the root id is sorted
   /// See cycleDetected, cycleOrigin, cycleEnd
-  bool topologicalSort(QList<int>& sorted);
+  bool topologicalSort(QList<int>& sorted, int rootId = -1);
+
+  /// Retrieve all vertices with indegree 0
+  void sourceVertices(QList<int>& sources);
   
 private:
   class ctkInternal; 

+ 1 - 1
Libs/PluginFramework/CMakeLists.txt

@@ -101,7 +101,7 @@ SET(KIT_resources
 
 # Create a MANIFEST.MF resource for the PluginFramework library,
 # pretending that is is a plugin (the system plugin)
-ctkMacroGeneratePluginManifest(KIT_SRCS
+ctkFunctionGeneratePluginManifest(KIT_SRCS
   SYMBOLIC_NAME "system.plugin"
   VERSION "0.9.9"
   )

+ 0 - 1
Plugins/org.commontk.eventbus/CMakeLists.txt

@@ -20,7 +20,6 @@ SET(PLUGIN_resources
 ctkMacroGetTargetLibraries(PLUGIN_target_libraries)
 
 ctkMacroBuildPlugin(
-  NAME ${PROJECT_NAME}
   EXPORT_DIRECTIVE ${PLUGIN_export_directive}
   SRCS ${PLUGIN_SRCS}
   MOC_SRCS ${PLUGIN_MOC_SRCS}

+ 64 - 21
Utilities/DGraph/DGraph.cpp

@@ -35,7 +35,7 @@
 //----------------------------------------------------------------------------
 QString help(const QString& progName)
 {
-  QString msg = "Usage: %1 <graphfile> [-paths Label]";
+  QString msg = "Usage: %1 <graphfile> [-paths Label | -sort Label]";
   return msg.arg(progName);
 }
 
@@ -81,6 +81,7 @@ int main(int argc, char** argv)
     }
 
   bool outputPath = false;
+  bool outputSort = false;
   QString label;
   if (argc == 3)
     {
@@ -89,14 +90,23 @@ int main(int argc, char** argv)
     }
   if (argc == 4)
     {
-    if (QString(argv[2]).compare("-paths")!=0)
+    QString arg2 = QString::fromLatin1(argv[2]);
+    if (arg2.compare("-paths")!=0 && arg2.compare("-sort")!=0)
       {
-      displayError(argv[0], QString("Wrong argument: %1").arg(argv[2]));
+      displayError(argv[0], QString("Wrong argument: %1").arg(arg2));
       return EXIT_FAILURE;
       }
     label = QLatin1String(argv[3]);
     outputTopologicalOrder = false;
-    outputPath = true;
+    if (arg2.compare("-paths") == 0)
+      {
+      outputPath = true;
+      }
+    else
+      {
+      outputSort = true;
+      }
+
     if (verbose)
       {
       qDebug() << "label:" << label; 
@@ -166,6 +176,7 @@ int main(int argc, char** argv)
     // Skip empty line or commented line
     if (line.isEmpty() || line.startsWith("#"))
       {
+      line = in.readLine();
       continue;
       }
 
@@ -206,7 +217,7 @@ int main(int argc, char** argv)
   if (mygraph.cycleDetected())
     {
     std::cerr << "Cycle detected !" << std::endl;
-    QList<int> path; 
+    QList<int> path;
     mygraph.findPath(mygraph.cycleOrigin(), mygraph.cycleEnd(), path);
     
     for(int i = 0; i < path.size(); ++i)
@@ -255,6 +266,13 @@ int main(int argc, char** argv)
       std::cout << std::endl;
       }
     }
+
+  if (verbose)
+    {
+    QList<int> sources;
+    mygraph.sourceVertices(sources);
+    qDebug() << "Source vertices: " << sources;
+    }
     
   if (outputPath)
     {
@@ -262,31 +280,56 @@ int main(int argc, char** argv)
     QList<int> out;
     if (mygraph.topologicalSort(out))
       {
-      // Assume all targets depend on the first lib
-      int rootId = out.last();
-      int labelId = vertexLabelToId[label];
-      QList<QList<int>*> paths;
-      mygraph.findPaths(labelId, rootId, paths);
-      for(int i=0; i < paths.size(); i++)
+      for(int i=0; i < out.size(); i++)
         {
-        QList<int>* p = paths[i];
-        Q_ASSERT(p);
-        for(int j=0; j < p->size(); j++)
+        // Assume all targets depend on the first lib
+        // We could get all sinks and find all paths
+        // from the rootId to the sink vertices.
+        int rootId = out.last();
+        int labelId = vertexLabelToId[label];
+        QList<QList<int>*> paths;
+        mygraph.findPaths(labelId, rootId, paths);
+        for(int i=0; i < paths.size(); i++)
           {
-          int id = p->at(j);
-          std::cout << vertexIdToLabel[id].toStdString();
-          if (j != p->size() - 1)
+          QList<int>* p = paths[i];
+          Q_ASSERT(p);
+          for(int j=0; j < p->size(); j++)
             {
-            std::cout << " ";
+            int id = p->at(j);
+            std::cout << vertexIdToLabel[id].toStdString();
+            if (j != p->size() - 1)
+              {
+              std::cout << " ";
+              }
+            }
+          if (i != paths.size() - 1)
+            {
+            std::cout << ";";
             }
           }
-        if (i != paths.size() - 1)
+        }
+      }
+    }
+
+  if (outputSort)
+    {
+    // TODO Make sure label is valid
+    QList<int> out;
+    int labelId = vertexLabelToId[label];
+    if (mygraph.topologicalSort(out, labelId))
+      {
+      for(int i=0; i < out.size(); i++)
+        {
+        int id = out.at(i);
+        std::cout << vertexIdToLabel[id].toStdString();
+
+        if (i != out.size() - 1)
           {
-          std::cout << ";";
+          std::cout << " ";
           }
         }
       }
     }
     
   return EXIT_SUCCESS;
-}
+}