Salome HOME
CMake: finished doc
authorbruneton <bruneton>
Thu, 11 Jul 2013 11:16:55 +0000 (11:16 +0000)
committerbruneton <bruneton>
Thu, 11 Jul 2013 11:16:55 +0000 (11:16 +0000)
dev/cmake/source/config.rst
dev/cmake/source/index.rst
dev/cmake/source/intro.rst
dev/cmake/source/pkg.rst
dev/cmake/source/skeleton.rst
dev/cmake/source/various.rst

index c14b3222ef366f1c5695a15d0ed36f727fc8bad3..0b2ebcfb0f27784dde7697c22082f0388fa7b41b 100644 (file)
+.. _config:
+
 Exposing a module's configuration (advanced)
 ============================================
 
-(to be completed)
+.. _cfg_principle:
+
+Principle
+---------
+
+When invoking the FIND_PACKAGE() command in CMake, two modes are possible:
+
+* MODULE mode - this is the standard mode. CMake then looks for a file called FindXXX.cmake, pre-sets some variables (indicating if the package is required, etc ... see :ref:`pkg_impl`) and invokes the code in the macro. 
+* CONFIG (or NO_MODULE) mode - if the package was itself compiled with CMake, it might offer a file called xyz-config.cmake or XyzConfig.cmake (where <Xyz> is the case-sensitive name of the package) exposing the configuration elements for an usage by a dependent project. This mechanism allows to remain in a pure CMake logic, where the package detection triggers the inclusion of the targets it references. Those targets can then be used directly. For example finding HDF5 in CONFIG mode allows to directly reference the "hdf5" target (and to link against it). It also provides the ability to pass more configuration elements than just libraries and binaries (for example user options for compilation, etc ...)
+
+Take a look at the official CMake documentation for more details.
+
+With this last setup (and if done properly!), a project can theoritically be either:
+
+* configured, built and installed in a standalone fashion
+* or have its sources directly be included as a sub-folder in the soure code of another bigger project. An ADD_SUBDIRECTORY in the root CMakeLists.txt of the encapsulating project should then trigger the proper configuration/build of the sub-project. 
+
+The last point was however never tested in the SALOME context, and probably needs some further debug.
+
+In the SALOME context this logic offers the advantage to have a minimal specification on the prerequisites. Technically one just needs to set the _ROOT_DIR variables for the packages that were never found before, and to get "for free" what was already found in a dependent module (for example Qt4 in GUI, but not OmniORB as it was already used by KERNEL). This works thanks to the exposition of all the ROOT_DIR used by one module into its configuration file. This can then be automatically reused by a dependent module.
+
+Inspired from the CMake tutorial (http://www.cmake.org/Wiki/CMake/Tutorials/How_to_create_a_ProjectConfig.cmake_file) some 
+important module use this scheme and install the \*Config.cmake files facilitating the configuration of other dependent modules. Most important SALOME modules (KERNEL, GUI, MED) expose their configuration in such a way. 
+
 
-Most important SALOME modules (KERNEL, GUI, MED) expose their configuration in a dedicated CMake file.
+Guidelines
+----------
 
-Following the CMake tutorial (http://www.cmake.org/Wiki/CMake/Tutorials/How_to_create_a_ProjectConfig.cmake_file) some 
-important module install \*Config.cmake files facilitating the configuration of other dependent modules. When writing such 
-CMake files, care must be taken to use PROJECT_BINARY_DIR and PROJECT_SOURCE_DIR variables instead of CMAKE_BINARY_DIR and CMAKE_SOURCE_DIR. This ensures that the project will also work properly when included as a sub-folder of a bigger project.
+* When writing such CMake files, care must be taken to use PROJECT_BINARY_DIR and PROJECT_SOURCE_DIR variables instead of CMAKE_BINARY_DIR and CMAKE_SOURCE_DIR. This ensures that the project will also work properly when included as a sub-folder of a bigger project.
+* The use of the local (or global) CMake Registry (via the EXPORT(PACKAGE ...) command) is not recommended. This overrides some of the FIND_PACKAGE mechanisms and quickly becomes a mess when compiling several versions of the same package on the same machine (typically the case for SALOME developers).
+* Only level one dependency configurations are exported in the XXXConfig.cmake files (direct dependencies). See below :ref:`config_file` how the level 2 dependencies are automatically reloaded.
+* Care must be taken to explicitly request a target to be part of the export set in the code sub-folders, when installing it  (EXPORT option)::
 
-The use of the local (or global) CMake Registry (via the EXPORT(PACKAGE ...) command) is not recommended. This overrides 
-some of the FIND_PACKAGE mechanisms and quickly becomes a mess when compiling several versions of the same package on the 
-same machine (typically the case for SALOME developers).
+    INSTALL(TARGETS kerncompo kern_main
+       # IMPORTANT: Add the library kerncompo to the "export-set" so it will be available 
+       # to dependent projects
+       EXPORT ${PROJECT_NAME}Targets
+       LIBRARY DESTINATION "${INSTALL_LIB_DIR}"
+       RUNTIME DESTINATION "${INSTALL_BIN_DIR}")
 
-Only level one dependencies configuration are exported in the XXXConfig.cmake files (direct dependencies).
 
 The rules are a bit lengthy but very generic. The idea is to define a set of targets to be exported, and to then explicitly
-call the export command to generate the appropriate files.
+call the export command to generate the appropriate files (some fragments have been cut for readability).
+The standard CMake macro CONFIGURE_PACKAGE_CONFIG_FILE() and WRITE_BASIC_PACKAGE_VERSION_FILE() (both located
+in CMakePackageConfigHelpers) help generate a suitable configuration file (see next section)::
 
-With this setup, the project can be either:
+  # Configuration export
+  # ====================
+  INCLUDE(CMakePackageConfigHelpers)
+
+  # List of targets in this project we want to make visible to the rest of the world.
+  # They all have to be INSTALL'd with the option "EXPORT ${PROJECT_NAME}TargetGroup"
+  SET(_${PROJECT_NAME}_exposed_targets
+    CalciumC SalomeCalcium DF Launcher  
+  ...)
+    
+  # Add all targets to the build-tree export set
+  EXPORT(TARGETS ${_${PROJECT_NAME}_exposed_targets}
+    FILE ${PROJECT_BINARY_DIR}/${PROJECT_NAME}Targets.cmake)
+
+  # Create the configuration files:
+  #   - in the build tree:
+
+  #      Ensure the variables are always defined for the configure:
+  SET(CPPUNIT_ROOT_DIR "${CPPUNIT_ROOT_DIR}")
+  ...
+   
+  SET(CONF_INCLUDE_DIRS "${PROJECT_SOURCE_DIR}/include" "${PROJECT_BINARY_DIR}/include")
+  CONFIGURE_PACKAGE_CONFIG_FILE(salome_adm/cmake_files/${PROJECT_NAME}Config.cmake.in 
+      ${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake
+      INSTALL_DESTINATION "${SALOME_INSTALL_CMAKE}"
+      PATH_VARS CONF_INCLUDE_DIRS SALOME_INSTALL_CMAKE CMAKE_INSTALL_PREFIX
+          ...)
+
+  #   - in the install tree:
+  #       Get the relative path of the include directory so 
+  #       we can register it in the generated configuration files:
+  SET(CONF_INCLUDE_DIRS "${CMAKE_INSTALL_PREFIX}/${INSTALL_INCLUDE_DIR}")
+  CONFIGURE_PACKAGE_CONFIG_FILE(salome_adm/cmake_files/${PROJECT_NAME}Config.cmake.in 
+      ${PROJECT_BINARY_DIR}/to_install/${PROJECT_NAME}Config.cmake
+      INSTALL_DESTINATION "${SALOME_INSTALL_CMAKE}"
+      PATH_VARS CONF_INCLUDE_DIRS SALOME_INSTALL_CMAKE CMAKE_INSTALL_PREFIX 
+        ...)
+
+  WRITE_BASIC_PACKAGE_VERSION_FILE(${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake
+      VERSION ${${PROJECT_NAME_UC}_VERSION}
+      COMPATIBILITY AnyNewerVersion)
+    
+  # Install the CMake configuration files:
+  INSTALL(FILES
+    "${PROJECT_BINARY_DIR}/to_install/${PROJECT_NAME}Config.cmake"
+    "${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
+    DESTINATION "${SALOME_INSTALL_CMAKE}")
+
+  # Install the export set for use with the install-tree
+  INSTALL(EXPORT ${PROJECT_NAME}TargetGroup DESTINATION "${SALOME_INSTALL_CMAKE}" 
+          FILE ${PROJECT_NAME}Targets.cmake)
+
+
+.. _config_file:
+
+Configuration file
+------------------
+
+The configuration file exposed in the build tree (e.g. SalomeGUIConfig.cmake) and in the install tree is itself configured by CMake by substituing variables and other stuff in a template file (e.g. SalomeGUIConfig.cmake.in).
+
+We present here the config file of GUI as it loads itself the KERNEL. 
+
+The first part does some initialization (the tag @PACKAGE_INIT@ is expanded by the macro CONFIGURE_PACKAGE_CONFIG_FILE() and serves mainly to locate the root directory of the installation)::
+
+  ### Initialisation performed by CONFIGURE_PACKAGE_CONFIG_FILE:
+  @PACKAGE_INIT@
+
+
+The target file is loaded only if a representative target of the project (here in GUI the target ''Event'') is not already defined. This distinguishes between an inclusion of the project directly in the source directory or after a full standalone installation (see :ref:`cfg_principle` above)::
+
+  # Load the dependencies for the libraries of @PROJECT_NAME@ 
+  # (contains definitions for IMPORTED targets). This is only 
+  # imported if we are not built as a subproject (in this case targets are already there)
+  IF(NOT TARGET Event AND NOT @PROJECT_NAME@_BINARY_DIR)
+    INCLUDE("@PACKAGE_SALOME_INSTALL_CMAKE@/@PROJECT_NAME@Targets.cmake")
+  ENDIF()   
+
+Note how the variable SALOME_INSTALL_CMAKE is prefixed with PACKAGE_SALOME_INSTALL_CMAKE. With this setup the helper macro CONFIGURE_PACKAGE_CONFIG_FILE() is able to adjust correctly the path (contained in SALOME_INSTALL_CMAKE) to always point to the right place (regardless of wether the SalomeGUIConfig.cmake file is in the build tree or in the install tree).
+This is why the variable SALOME_INSTALL_CMAKE is passed as an argument when calling CONFIGURE_PACKAGE_CONFIG_FILE(). 
+
+The user options, and the directories of the level 1 prerequisites (i.e. direct dependencies) are exposed in a variable called ''XYZ_ROOT_DIR_EXP'' (note the trailing ''EXP'' like EXPosed). This will be used by the package detection logic to check for potential conflicts::
+
+  # Package root dir:
+  SET_AND_CHECK(GUI_ROOT_DIR_EXP "@PACKAGE_CMAKE_INSTALL_PREFIX@")
+
+  # Include directories
+  SET_AND_CHECK(GUI_INCLUDE_DIRS "${GUI_ROOT_DIR_EXP}/@SALOME_INSTALL_HEADERS@")
+
+  # Options exported by the package:
+  SET(SALOME_USE_MPI     @SALOME_USE_MPI@)
+  ...
+
+  # Advanced options
+  SET(SALOME_USE_OCCVIEWER    @SALOME_USE_OCCVIEWER@)
+  ...
+
+  # Level 1 prerequisites:
+  SET_AND_CHECK(KERNEL_ROOT_DIR_EXP "@PACKAGE_KERNEL_ROOT_DIR@")
+  SET_AND_CHECK(SIP_ROOT_DIR_EXP "@PACKAGE_SIP_ROOT_DIR@")
+  ...
+
+  # Optional level 1 prerequisites:
+  IF(SALOME_USE_OCCVIEWER)
+    SET_AND_CHECK(CAS_ROOT_DIR_EXP "@PACKAGE_CAS_ROOT_DIR@")    
+  ENDIF()
+  ...
+
+Then a specific logic is included to ensure the following: if a prerequisite of GUI was detected and used (in GUI) in CONFIG mode, then its targets should be included again when a third project uses GUI. For example, if GUI uses VTK, and VTK was found if CONFIG mode, it means that some GUI libraries were linked directly using a reference to a VTK target instead of a full path (simply ''vtkCommon'' instead of ''/usr/lib/bla/bli/libvtkCommon.so''). A project which needs to link on GUI must then also be able to link (indirectly) against VTK (which is just referred as ''-lvtkCommon'' in the GUI library description exposed by CMake).
+
+This avoids having to set the LD_LIBRARY_PATH (or PATH under Windows).
+
+The following loop detects such a situation (there is always a variable Xyz_DIR when the package was found in CONFIG mode) and reloads if necessary the corresponding target by calling FIND_PACKAGE() in CONFIG mode on the exact same directory. This is done for all level 1 prerequisites of GUI, and would in our case expose the target ''vtkCommon'' to a project linking against GUI::
+
+  # For all prerequisites, load the corresponding targets if the package was used 
+  # in CONFIG mode. This ensures dependent projects link correctly
+  # without having to set LD_LIBRARY_PATH:
+  SET(_PREREQ CAS OpenGL PyQt4 Qt4 Qwt SIP VTK)
+  SET(_PREREQ_CONFIG_DIR "@CAS_DIR@" "@OpenGL_DIR@" "@PyQt4_DIR@" "@Qt4_DIR@" "@Qwt_DIR@" "@SIP_DIR@" "@VTK_DIR@")
+  LIST(LENGTH _PREREQ_CONFIG_DIR _list_len)
+  # Another CMake stupidity - FOREACH(... RANGE r) generates r+1 numbers ...
+  MATH(EXPR _range "${_list_len}-1")
+  FOREACH(_p RANGE ${_range})  
+    LIST(GET _PREREQ            ${_p} _pkg    )
+    LIST(GET _PREREQ_CONFIG_DIR ${_p} _pkg_dir)
+    IF(_pkg_dir)
+       MESSAGE(STATUS "===> Reloading targets from ${_pkg} ...")
+       FIND_PACKAGE(${_pkg} REQUIRED NO_MODULE 
+            PATHS "${_pkg_dir}" 
+            NO_DEFAULT_PATH)
+    ENDIF()
+  ENDFOREACH()
+
+We also make sure, in the case of GUI, that the same is done for KERNEL's targets::
+
+  # Include KERNEL targets if they were not already loaded:
+  IF(NOT (TARGET SALOMEBasics)) 
+    INCLUDE("${KERNEL_ROOT_DIR_EXP}/${SALOME_INSTALL_CMAKE}/SalomeKERNELTargets.cmake")
+  ENDIF()
+
+We finally expose the installation directories and define the target variables that will be used to link against targets given in the export list::
+
+  # Installation directories
+  SET(SALOME_INSTALL_BINS "@SALOME_INSTALL_BINS@")
+  SET(SALOME_INSTALL_LIBS "@SALOME_INSTALL_LIBS@")
+  ...
+
+  # Exposed GUI targets:
+  SET(GUI_caf caf)
+  SET(GUI_CAM CAM)
+  ...
 
-* configured, built and installed in a standalone fashion
-* or directly be included as a sub-folder in the code of another bigger project. A simple ADD_SUBDIRECTORY in the root CMakeLists.txt of the encapsulating project will trigger the proper configuration/build of the sub-project.
 
-Care must be taken to explicitly request a target to be part of the export set in the code sub-folders, when installing it
-(EXPORT option)::
 
-  INSTALL(TARGETS kerncompo kern_main
-     # IMPORTANT: Add the library kerncompo to the "export-set" so it will be available 
-     # to dependent projects
-     EXPORT ${PROJECT_NAME}Targets
-     LIBRARY DESTINATION "${INSTALL_LIB_DIR}"
-     RUNTIME DESTINATION "${INSTALL_BIN_DIR}")
 
 
 
-possibility to have a minimal specification on the prerequisites (technically one just need to set the _ROOT_DIR variables for the packages that were never found before), and to get "for free" what was already found in a dependent module
index ef6f6854f512f8590c128967b19e7bb549649cce..64c2b01a22bfe1ef4c061b8c4edd54e6622e2839 100644 (file)
@@ -3,8 +3,12 @@
    You can adapt this file completely to your liking, but it should at least
    contain the root `toctree` directive.
 
-Welcome to CMake build procedure's documentation!
-=================================================
+SALOME - CMake build procedure
+==============================
+
+The section marked as ADVANCED need not be read by someone only trying to compile SALOME. Those
+sections are intented to the core SALOME developers, to help them understand/fix/improve
+the process.
 
 Contents:
 
index 63bc0bff23494df6796f5024c5060c036d045d5d..66b822a1ea2ee4e020898202776ac1d0409d81c2 100644 (file)
@@ -5,11 +5,11 @@ of a software project.
 This documentation describes the goals and the good practices to be applied when writing
 the CMake files for the configuration of a SALOME module.
 
-This documentation is best browsed with the SALOME KERNEL sources at hand, in order to be able to 
+This documentation is best browsed with the SALOME KERNEL's sources at hand, in order to be able to 
 take a look at code snipets. Most references in this document points to KERNEL sources.
 
 The section marked as ADVANCED need not be read by someone only trying to compile SALOME. Those
-sections are intented to the core SALOME developpers, to help them understand/fix/improve
+sections are intented to the core SALOME developers, to help them understand/fix/improve
 the process.
 
 Motivations and overview
@@ -29,34 +29,94 @@ Basic usage
 Once the sources have been retrieved (via a clone of the repository or an extraction of the tarball)
 one typically:
 
-* create a dedicated build directory, e.g. KERNEL_BUILD.
-* switch to it, and invoke the ccmake (or cmake-gui) command::
+* creates a dedicated build directory, e.g. KERNEL_BUILD.
+* switches to it, and invoke the ccmake (or cmake-gui) command::
 
-    ccmake 
-    ccmake
+    cd KERNEL_BUILD
+    ccmake ../KERNEL_SRC
 
 * sets all the xyz_ROOT_DIR to point to the root paths of the package <xyz>.
 * sets the installation directory in the variable CMAKE_INSTALL_PREFIX.
 * generates the build files (hiting 'g' under ccmake).
-* invoke the make command in the usual way::
+* invokes the make command in the usual way::
 
     make
     make install
 
+* if you see an error complaining that the package was not found, double check that the XYZ_ROOT_DIR variable was correctly set, and delete everything in your build directory before retrying::
+
+    cd KERNEL_BUILD
+    rm -rf *
+    ccmake ../KERNEL_SRC
+
 If you want to use a specific Python installation to configure and build SALOME, you should ensure that:
+
 * the interpreter is in your path.
-* the variables LD_LIBRARY_PATH (PATH under Windows) and PYTHONPATH are properly pointing to your desired Python installation.
+* the variables LD_LIBRARY_PATH (PATH under Windows) and PYTHONPATH are properly pointing to the desired Python installation.
+
+Variable reference
+------------------
+
+The following list indicates the expected variables for each module. They can be specified on the CMake command line (with the -D flag) or in the environment.
+
+KERNEL module:
+
+* LIBBATCH_ROOT_DIR: LibBatch package - already uses Python, Swig and PThread
+* PYTHON_ROOT_DIR: Python package
+* PTHREAD_ROOT_DIR: PThread package - typically not need on Unix systems
+* SWIG_ROOT_DIR: SWIG package
+* LIBXML2_ROOT_DIR: LibXml2 package
+* HDF5_ROOT_DIR: HDF5 package - if HDF5 was compiled with MPI support, the corresponding MPI root directory will be exposed automatically (no need to set MPI_ROOT_DIR)
+* BOOST_ROOT_DIR: Boost package
+* OMNIORB_ROOT_DIR: OmniORB package
+* OMNIORBPY_ROOT_DIR: OmniORB Python backend - if not given, OMNIORB_ROOT_DIR will be tried
+* MPI_ROOT_DIR: MPI package (see HDF5_ROOT_DIR above)
+* CPPUNIT_ROOT_DIR: CppUnit package
+* DOXYGEN_ROOT_DIR: Doxygen package
+* GRAPHVIZ_ROOT_DIR: Graphviz package
+* SPHINX_ROOT_DIR: Sphinx package - requires setuptools and docutils to work properly.
+* SETUPTOOLS_ROOT_DIR: Setuptools package. This package is not detected explicitly in the KERNEL, but the path is used to complete the PYTHON path given to Sphinx.
+* DOCUTILS_ROOT_DIR: Docutils package. This package is not detected explicitly in the KERNEL, but the path is used to complete the PYTHON path given to Sphinx.
+
+GUI module - on top of some of the KERNEL prerequisites, the following variables are used:
+
+* VTK_ROOT_DIR: VTK package. If not given, PARAVIEW_ROOT_DIR is used to look for a VTK installation inside the ParaView installation
+* (optional) PARAVIEW_ROOT_DIR: see above
+* CAS_ROOT_DIR: Cascade package
+* SIP_ROOT_DIR: SIP package
+* QT4_ROOT_DIR: Qt4 package (only some components are loaded)
+* PYQT4_ROOT_DIR: PyQt4 package
+* QWT_ROOT_DIR: Qwt package
+* OPENGL_ROOT_DIR: OpenGL package
+
+MED module - on top of some of the KERNEL and GUI prerequisites, the following variables are used:
+
+* MEDFILE_ROOT_DIR: MEDFile package
+* METIS_ROOT_DIR (optional): Metis package
+* PARMETIS_ROOT_DIR (optional): ParMetis package
+* SCOTCH_ROOT_DIR (optional): Scotch package
+
+PARAVIS module - on top of some of the KERNEL, GUI and MED prerequisites, the following variables are used:
+
+* PARAVIEW_ROOT_DIR: ParaView package
+* at present for a proper build of PARAVIS, the env variables LD_LIBRARY_PATH and PYTHONPATH should be set to contain the HDF5 and ParaView libraries. This is required since the configuration process itself uses a Python script in which the environment is not overriden::
+
+    # Paravis compilation needs ParaView (and hence HDF5) in the Python path:
+    export PYTHONPATH=$PARAVIEW_ROOT_DIR/lib/paraview-3.98/site-packages:$PARAVIEW_ROOT_DIR/lib/paraview-3.98:$PYTHONPATH
+    export LD_LIBRARY_PATH=$PARAVIEW_ROOT_DIR/lib/paraview-3.98:$HDF5_ROOT_DIR/lib:$LD_LIBRARY_PATH
+
+
 
 Overview of the logic (advanced)
 --------------------------------
 
 Here are the general principles guiding the implementation:
 
-* Only take into account first order prerequisites.
+* Only taking into account the first order prerequisites of a module should be required.
   For instance, CASCADE uses Tbb : 
 
-  * CASCADE is a prerequisite of first order of GUI,
-  * Tbb is a prerequisite of second order of GUI,
+  * CASCADE is a prerequisite of first order (level 1) of GUI,
+  * Tbb is a prerequisite of second order (level 2) of GUI,
   * GUI CMake files must reference explicitly CASCADE, but never Tbb. The detection logic of CASCADE should make sure Tbb gets included.
 
 * Being able to use different versions/installations of the same product, in the system, or generated by the user. 
@@ -65,6 +125,10 @@ Here are the general principles guiding the implementation:
   For example MPI is detected only if option SALOME_USE_MPI is ON.
 *      Detection of first order prerequisites is based on a <Product>_ROOT_DIR variable or on what has been detected in another   dependency. For example if both HDF5 and MPI are needed by the current module, we try to detect with which MPI installation HDF5 was compiled, and to offer this one as a default choice for the package itself. Other variables (PATH, LD_LIBRARY_PATH, PYTHONPATH) should never be needed at compile time.
 * The only exception to the previous point is Python, which is so central to the process that we assume that LD_LIBRARY_PATH and PYTHONPATH are already correctly pointing to the correct Python installation.
+* Trying as much as possible to detect potential conflict, and warn the user:
+
+  * if the package was detected at a place different from what was specified in XYZ_ROOT_DIR
+  * if there is a conflict between what was explicitly set in XYZ_ROOT_DIR, and what was previously used in a dependency
 
 
 
index 5f9924078dd534ec4a663683612bd0711bce6132..f575d066c90477e457002750b8e2b27bde179de1 100644 (file)
@@ -1,3 +1,5 @@
+.. _package:
+
 Package detection mechanism
 ===========================
 
@@ -24,6 +26,8 @@ The variable guiding the detection is always builts as::
 where <XYZ> is (*exactly*) the upper case name of the standard CMake module. For example, the
 detection of Qt4 is guided by setting QT4_ROOT_DIR. The variables \*_ROOT_DIR are only there to guide the process, not to force it. Typically under Linux, one would never set PTHREAD_ROOT_DIR, thus leaving the logic find the system installation. 
 
+Beware that package names in the CMakeLists.txt are case-sensitive, but the corresponding variables are always upper-case (because on some platforms environment variables are not case-sensitive).
+
 The order of priority for the detection of a package is (from high to low priority):
 
 1. CMake variables explicitly set by the user (typically on the command line with -DXYZ_ROOT_DIR=...)
@@ -31,8 +35,15 @@ The order of priority for the detection of a package is (from high to low priori
 3. Default value based on a previous dependency using the tool already
 4. Detection direclty in the host system by the standard CMake logic
 
+CMake has two possible modes of detection, CONFIG mode and MODULE mode. The order of priority is explicitly set in SALOME to:
+
+1. CONFIG (also called NO_MODULE) mode: this tries to load a xyz-config.cmake file from the package installation itself
+2. MODULE mode: this relies on the logic written in a FindXyz.cmake macro, looking directly for representative libraries, binaries or headers of the package.
+
+The first mode is preferred as it allows to directly include the CMake targets of the prerequisite.
+
 The package detection is only made in the root CMakeLists.txt, potentially conditionned on some
-user options. Package names are case-sensitive, but the corresponding variables are always upper-case.
+user options. 
 
 Writing the detection macro of a new SALOME prerequisite
 --------------------------------------------------------
@@ -48,23 +59,29 @@ or::
 All prerequisite detection in SALOME should be implemented by:
 
 * writing a file FindSalome<Xyz>.cmake (note the extra ''Salome''), where <Xyz> matches *exactly* the name of the standard CMake module (see below if there is no standard module for <Xyz>)
-* typically this file looks like this::
+* invoking FIND_PACKAGE() command in the root CMakeLists.txt::
+  
+    FIND_PACKAGE(SalomeLibXml2 REQUIRED)
+
+Typically the FindSalome<Xyz>.cmake file looks like this::
 
     SALOME_FIND_PACKAGE_AND_DETECT_CONFLICTS(CppUnit CPPUNIT_INCLUDE_DIRS 1)
     MARK_AS_ADVANCED(CPPUNIT_INCLUDE_DIRS CPPUNIT_LIBRARIES CPPUNIT_CONFIG_BIN CPPUNIT_SUBLIB_cppunit CPPUNIT_SUBLIB_dl)
 
-* It invokes the SALOME macro SALOME_FIND_PACKAGE_AND_DETECT_CONFLICTS() which takes:
+It invokes the SALOME macro SALOME_FIND_PACKAGE_AND_DETECT_CONFLICTS() which takes:
 
-  * as first argument the name of the package (here CppUnit), 
-  * as second argument, the name of a (path) variable set when the package is found properly, 
-  * as third argument, the number of levels this variable should be browsed up to reach the root directory of the package installation.
-  * as optional fourth (and plus) argument: a list of components (if needed - e.g. for Qt4, we only load a subset of components)
+* as first argument the name of the package (here CppUnit), 
+* as second argument, the name of a (path) variable set when the package is found properly, 
+* as third argument, the number of levels this variable should be browsed up to reach the root directory of the package installation.
+    
 
-* in the example above, we look for the package CppUnit (note that this is case-sensitive). There is already a standard CMake module to detect CppUnit, which sets the CMake variable CPPUNIT_INCLUDE_DIRS to the (list of) directories to include when compiling with CppUnit. Going one level up from the include directory (typically /usr/include) gives the root directory of the installation (/usr) 
-* the reference variable may be a list, only its first element is then considered.
+In the example above,
+
+* we look for the package CppUnit (note that this is case-sensitive). There is already a standard CMake module to detect CppUnit, which sets the CMake variable CPPUNIT_INCLUDE_DIRS to the (list of) directories to include when compiling with CppUnit. Going one level up from the include directory (typically /usr/include) gives the root directory of the installation (/usr).
 * all the variables exposed in the cache by the standard detection logic (CPPUNIT_INCLUDE_DIRS, CPPUNIT_LIBRARIES, etc ...) are marked as "advanced" so that they do not automatically appear in ccmake or cmake-gui.
 
+Note that the reference variable may be a list, only its first element is then considered.
+
 Writing a new generic detection macro (advanced)
 ------------------------------------------------
 
@@ -86,7 +103,8 @@ The following guidelines apply:
 
 * This macro takes care (among other things) of setting the XYZ_FOUND variable (upper case), and of displaying a message if not in QUIET mode (TBC).
 * the macro should be saved in the same directory as above
-* respect the naming conventions for the variables you set (start with the package name, upper case)
+* respect the naming conventions for the variables you set (start with the package name, upper case - see :ref:`naming_conventions`)
+* do not do any ADD_DEFINITIONS() or INCLUDE_DIRECTORIES() in such a macro. This should be done by the caller or in a UseXYZ.cmake file. The purpose of a FindXXX.cmake macro is to detect, not to make usable. This rule does not apply to FindSalomeXXX.cmake macros where we know we are always in the SALOME context.
 * here is a simple example of the detection of Sphinx::
 
     # - Sphinx detection
@@ -109,6 +127,8 @@ The following guidelines apply:
     FIND_PACKAGE_HANDLE_STANDARD_ARGS(Sphinx REQUIRED_VARS SPHINX_EXECUTABLE)
 
 
+.. _pkg_impl:
+
 Implementation details (advanced)
 ---------------------------------
 
@@ -117,19 +137,18 @@ SALOME_FIND_PACKAGE_AND_DETECT_CONFLICTS() implemented in KERNEL/salome_adm/cmak
 
 All the logic is thus concentrated in one (hopefully well documented) macro. This means: one place to fix if there is a bug, and better, one place to amend if we ever want to define a new behaviour (for example if we want to change the order of priorities between CONFIG and MODULE mode). The end user (someone developing in SALOME) just needs to call it. It is the responsability of the core SALOME developpers to understand and maintain this macro.
 
-The reader is invited to read the have the code at hand when reading the following.
+The reader is invited to have the code at hand when reading the following.
 
 The macro signature is
 ::
 
-  SALOME_FIND_PACKAGE_DETECT_CONFLICTS(pkg referenceVariable upCount <component1> <component2> ...)
+  SALOME_FIND_PACKAGE_DETECT_CONFLICTS(pkg referenceVariable upCount)
 
 where:
 
 * *pkg*              : name of the system package to be detected
 * *referenceVariable*: variable containing a path that can be browsed up to retrieve the package root directory (xxx_ROOT_DIR)
 * *upCount*          : number of times we have to go up from the path <referenceVariable> to obtain the package root directory.
-* *<component_n>*    : an optional list of components to be found.  
 
 For example::  
 
@@ -137,23 +156,34 @@ For example::
 
 The macro has a significant size but is very linear:
 
-1. Load a potential env variable XYZ_ROOT_DIR as a default choice for the cache entry XYZ_ROOT_DIR
+1. Load a potential env variable XYZ_ROOT_DIR as a default choice for the cache entry XYZ_ROOT_DIR.
    If empty, load a potential XYZ_ROOT_DIR_EXP as default value (path exposed by another package depending
    directly on XYZ)
 2. Invoke FIND_PACKAGE() in this order:
 
-    * in CONFIG mode first (if possible): priority is given to a potential "XYZ-config.cmake" file.
-    * then switch to the standard MODULE mode, appending on CMAKE_PREFIX_PATH the above XYZ_ROOT_DIR variable.
+  * in CONFIG mode first (if possible): priority is given to a potential "XYZ-config.cmake" file.
+  * then switch to the standard MODULE mode, appending on CMAKE_PREFIX_PATH the above XYZ_ROOT_DIR variable.
 
 3. Extract the path actually found into a temp variable _XYZ_TMP_DIR
 4. Warn if XYZ_ROOT_DIR is set and doesn't match what was found (e.g. when CMake found the system installation
-   instead of what is pointed to by XYZ_ROOT_DIR - happens when a typo in the content of XYZ_ROOT_DIR).
+   instead of what is pointed to by XYZ_ROOT_DIR - happens when there is a typo in the content of XYZ_ROOT_DIR).
 5. Conflict detection: check the temporary variable against a potentially existing XYZ_ROOT_DIR_EXP
-6. Finally expose what was _actually_ found in XYZ_ROOT_DIR.  
+6. Finally expose what was *actually* found in XYZ_ROOT_DIR.  This might be different from the initial XYZ_ROOT_DIR, but there has been a warning in such a case.
 
 
 The specific stuff (for example exposing a prerequisite of XYZ to the rest of the world for future conflict detection) is added after the call to the macro by the callee. See for example the FindSalomeHDF5.cmake macro which exposes the MPI_ROOT_DIR if HDF5 was compiled with parallel support.
 
+If the invokation of FIND_PACKAGE() was done with some options:
+
+* QUIET, REQUIRED
+* COMPONENTS
+* VERSION [EXACT]
+
+those options are completly handled through the analysis of the standard CMake variables (which are automatically set when those options are given):
+
+* Xyz_FIND_QUIETLY and Xyz_FIND_REQUIRED
+* Xyz_FIND_COMPONENTS
+* Xyz_FIND_VERSION and Xyz_FIND_VERSION_EXACT
 
 
 
index c0e155f6e3e10f20c624e010f712e0267a72e473..3505b7f119fcfedaf1d282f8c65bf7339098ed23 100644 (file)
@@ -1,4 +1,107 @@
-Anatomy of a root CMakeLists.txt
+.. _skeleton:
+
+Anatomy of a CMakeLists.txt file
 ================================
 
-TODO
+Root CMakeLists.txt
+-------------------
+
+The root CMakeLists.txt should contain the following elements:
+
+* Versioning: definition of the major, minor and patch version number. This is the sole place where those numbers should be defined.
+* Platform setup: specific flags, detection of the architecture, ... This it typically done by including the SalomeSetupPlatform macro.
+* User option definitions: in SALOME the following flags should be found in all modules:
+
+  * SALOME_USE_MPI: wether Salome should be built using MPI containers
+  * SALOME_BUILD_TESTS: wether the unit tests should be built
+  * SALOME_BUILD_DOC: wether the documentation for the current module should be generated and installed
+
+  Other flags specific to the module might be added, and should then start with *SALOME_XYZ_* where <XYZ> is the module's name (MED for example).
+
+* Detection of the required prerequisites for the module. All prerequisites in SALOME are detected through a call to FIND_PACKAGE(SalomeXYZ ...). See section :ref:`package`::
+
+    FIND_PACKAGE(SalomePython REQUIRED)
+    FIND_PACKAGE(SalomePThread REQUIRED)
+    FIND_PACKAGE(SalomeSWIG REQUIRED)
+
+
+* Detection of the optional prerequisites (potentially conditioned on some user options)::
+
+    IF(NOT SALOME_LIGHT_ONLY)
+      FIND_PACKAGE(SalomeOmniORB REQUIRED)
+      FIND_PACKAGE(SalomeOmniORBPy REQUIRED)
+    ENDIF() 
+
+
+* Installation directories. Those directories should be used consistently across all SALOME modules::
+
+    SET(SALOME_INSTALL_BINS bin/salome CACHE PATH "Install path: SALOME binaries")
+    SET(SALOME_INSTALL_LIBS lib/salome CACHE PATH "Install path: SALOME libs")
+    SET(SALOME_INSTALL_IDLS idl/salome CACHE PATH "Install path: SALOME IDL files")
+    SET(SALOME_INSTALL_HEADERS include/salome CACHE PATH "Install path: SALOME headers")
+    SET(SALOME_INSTALL_SCRIPT_SCRIPTS ${SALOME_INSTALL_BINS} CACHE PATH "Install path: SALOME scripts")
+    ...
+
+
+* Inclusion of the source code directories to be compiled::
+
+    IF(NOT SALOME_LIGHT_ONLY)
+      ADD_SUBDIRECTORY(idl)
+    ENDIF()
+    ADD_SUBDIRECTORY(src)
+
+
+* Header configuration: creation of the version header files, among other
+* Configuration export (see dedicated section :ref:`config`)
+
+
+CMakeLists.txt dedicated to the build of a target
+-------------------------------------------------
+
+First, include directories::
+
+  INCLUDE_DIRECTORIES(
+    ${OMNIORB_INCLUDE_DIR}
+    ${PTHREAD_INCLUDE_DIRS}
+    ${CMAKE_BINARY_DIR}/salome_adm
+    ${CMAKE_CURRENT_SOURCE_DIR}/../Basics
+    ${CMAKE_CURRENT_SOURCE_DIR}/../SALOMELocalTrace
+    ${CMAKE_CURRENT_SOURCE_DIR}/../Utils
+    ${CMAKE_BINARY_DIR}/idl
+    )
+
+Then we define the sources list <target>_SOURCES::
+
+  SET(SalomeNS_SOURCES
+    SALOME_NamingService.cxx
+    ServiceUnreachable.cxx
+    NamingService_WaitForServerReadiness.cxx
+  )
+
+Set the common compilation flags of all targets of the directory::
+
+  ADD_DEFINITIONS(${OMNIORB_DEFINITIONS})
+
+Ensure dependencies are correctly set, if needed (please refer to :ref:`dependencies`)::
+
+  ADD_DEPENDENCIES(SalomeNS SalomeIDLKernel)
+
+Then the standard way to compile, link and install a library or executable is::
+
+  ADD_LIBRARY(SalomeNS ${SalomeNS_SOURCES})
+  TARGET_LINK_LIBRARIES(SalomeNS OpUtil)
+  INSTALL(TARGETS SalomeNS DESTINATION ${SALOME_INSTALL_LIBS})
+
+Note that there is no SHARED reference, no SET_TARGET_PROPERTIES( .. COMPILE_FLAGS ..). If you need to link against a KERNEL or other SALOME target, use the variable name of the target, not the target directly::
+
+  TARGET_LINK_LIBRARIES(xyz ${KERNEL_SalomeNS})   # OK
+  TARGET_LINK_LIBRARIES(xyz SalomeNS)             # Bad!!
+
+Finally write the specific installation rule for scripts or headers::
+
+  SALOME_INSTALL_SCRIPTS(SALOME_NamingServicePy.py ${SALOME_INSTALL_SCRIPT_SCRIPTS})
+  FILE(GLOB COMMON_HEADERS_HXX "${CMAKE_CURRENT_SOURCE_DIR}/*.hxx")
+  INSTALL(FILES ${COMMON_HEADERS_HXX} DESTINATION ${SALOME_INSTALL_HEADERS})
+
+
+
index 736c049917dbaeed9604474b279a73aaeb258014..dee42b7f70d6499d5d807791e8ece6f78f02b5f4 100644 (file)
@@ -1,6 +1,8 @@
 Various guidelines and conventions (advanced)
 =============================================
 
+.. _debug:
+
 Debugging CMake
 ---------------
 
@@ -15,6 +17,8 @@ Each of the following solutions displays the full command line for each build ac
 To have this by default for every build, one can specify this setting at the configuration 
 step by toggling the CMAKE_VERBOSE_MAKEFILE to ON.
 
+.. _conventions:
+
 General conventions
 -------------------
 * Specify the languages used in the project::
@@ -27,8 +31,8 @@ General conventions
 * No use of GLOBS for sources (\*.cxx), but GLOBS can be used for includes and \*.i on installation
 * no \*.hxx in <SMTH>_SOURCES variable
 * FIND_PACKAGE() is called only in root <Module>/CMakeLists.txt
-* INCLUDE(), if needed, must be first
-* ADD_SUBDIRECTORY, if needed, must be done just after INCLUDE and FIND_PACKAGE, then the specific part of subdirectory can be done
+* INCLUDE() directives, if needed, must come early in the file
+* ADD_SUBDIRECTORY() directives, if needed, must be done just after INCLUDE and FIND_PACKAGE, then the specific part of subdirectory can be done
 * INSTALL should be called at the end of the CMakelists.txt
 * Create MACRO in \*.cmake to factorize some repetitive lines. (Try to) comment them. 
 * All <Module>/CMakeLists.txt (see KERNEL_SRC/CMakeLists.txt) contains the definition of the variables which specify the location of installation directories. Only these variables should be used in subdirectories, and the model given in KERNEL should be followed.
@@ -41,29 +45,70 @@ General conventions
 * Use FILE(TO_CMAKE_PATH) instead of REPLACE(“\\” “/”) and FILE(TO_NATIVE_PATH) to convert a path to the CMake internal format
 * Use strictly python to execute portably a script
 * Use PROJECT_BINARY_DIR and PROJECT_SOURCE_DIR instead of CMAKE_BINARY_DIR and CMAKE_SOURCE_DIR. This helps having a proper behavior when the module is included as a sub-folder in the code of a bigger project.
+* It is not necessary to rewrite the full text when closing a conditional statement or a control flow statement. This is up to you::
+
+    IF(A_VERY_LONG_CONDITION)
+      ..
+    ENDIF() # no need to repeat A_VERY_LONG_CONDITION 
+
+* When appending to CMake lists, the following syntax should be used::
+
+    LIST(APPEND a_variable ${element1} ...)
+
+* Names of variables used internally should start with an underscore
+* For every path read from outside CMake (typically from an environment variable), the following conversion should be used to ensure the path format is compatible with the internal CMake syntax::
+
+    FILE(TO_CMAKE_PATH ...)
+
+* all install paths should be relative
+* beware that variables inside macros are persistent (static) - do not assume they are reset everytime you re-enter the macro (think of a macro as a simple text subsitution at the place it was called)
+* always use variables to reference targets, do not reference target directly (this will save some effort the day we want to rename a target ...)
+
+
+.. _naming_conventions:
 
 Naming conventions
 ------------------
-They are few of them but its better to be consistent:
+They are few of them but let's try to be consistent:
 
 * use upper case for CMake commands. For vars, the case is free. Why? As targets are generally in lower case it allows discriminating more easily CMake commands from local vars and targets.
 * for sources the convention <Target>_SOURCES is used to store sources needed by target <Target>.
 * for headers the convention <Target>_HEADERS is used to store headers to be installed in pair with <Target>.
-* for all the variables set by a given package, the naming convention is based on a CMake prefix::
+* for all the variables set by a given package, the naming convention is based on a CMake prefix (upper case if possible)::
 
-    <Product>_myVariable
+    <PRODUCE>_myVariable
 
 * temporary variables (not used outside the macro or outside the module) should start with an underscore
 
-Parallel compilation
---------------------
-Contrary to Autotools, CMake is working by targets and not by directories. If parallel compilation fails, it means that some dependencies are missing. The linked libraries of the target are automatically considered as dependency by CMake. But, for instance, when a library needs only generated includes from Kernel idl (no link needed with the generated code for a CORBA client), the dependency should be explicitly added by the ADD_DEPENDENCIES(<target> SalomeIDLKernel) command in the CMakeLists.txt.
+
+.. _dependencies:
+
+Parallel compilation - Correct dependencies
+-------------------------------------------
+Contrary to Autotools, CMake is working by targets and not by directories. If parallel compilation fails, it means that some dependencies are missing or not properly set. This can happen mainly with generated sources (such as the ones produced by the IDL compiler, or by SWIG).
+
+The linked libraries of the target are automatically considered as dependency by CMake. But, for instance, when a library needs only generated includes from Kernel IDL (no link needed with the generated code for a CORBA client), the dependency should be explicitly added by the::
+
+  ADD_DEPENDENCIES(<target> SalomeIDLKernel) 
+
+command in the CMakeLists.txt.
+
 To check parallel compilation (i.e. dependencies) of a target, the developer must start from an empty, generated by CMake build directory and use the “make (-jX) <target>”.
 
+.. _cmd_conventions:
+
 Command specific conventions
 ----------------------------
 
 * INCLUDE(): only specify the name of the macro, e.g. INCLUDE(SalomeMacros). The directory is almost always already in the CMAKE_MODULE_PATH.
-* 
+* Strings: variables representing strings or paths should always be used quoted. For example "${my_var}". This ensures a proper behavior if the string contains a semi-colonn
+* appending an element to a list variable should be done with::
+
+    LIST(APPEND xyz ...)
+
+  and not with::
+    
+    SET(xyz ${xyz} ...)    
+