Browse Source

Merge branch 'option-to-reduce-compile-python-script-target-number'

* option-to-reduce-compile-python-script-target-number:
  Report a CMake error if script compilation fails
  Regenerate python compile script at build time
  Compile python scripts explicitly instead of compiling an entire folder
  Add option allowing to reduce number of python copy/compilation targets
Jean-Christophe Fillion-Robin 12 years ago
parent
commit
d7fb818714
1 changed files with 206 additions and 77 deletions
  1. 206 77
      CMake/ctkMacroCompilePythonScript.cmake

+ 206 - 77
CMake/ctkMacroCompilePythonScript.cmake

@@ -5,21 +5,29 @@
 #          ParaView/VTK/Wrapping/Python/CMakeLists.txt
 #
 
+#
+# By globally defining the variable CTK_COMPILE_PYTHON_SCRIPTS_GLOBAL_TARGET_NAME to a 
+# non-empty string or by specifying the macro option 'GLOBAL_TARGET', 
+# the following targets will be defined for the whole build system:
+#  - Copy<GLOBAL_TARGET_NAME>PythonResourceFiles
+#  - Copy<GLOBAL_TARGET_NAME>PythonScriptFiles
+#  - Compile<GLOBAL_TARGET_NAME>PythonFiles
+#
+# For complex projects, this can help reducing the number of targets and
+# simplify the manual rebuild of copy and compile targets.
+#
+
 include(${CTK_CMAKE_DIR}/ctkMacroParseArguments.cmake)
 
+set(CTK_PYTHON_COMPILE_FILE_SCRIPT_DIR "${CMAKE_BINARY_DIR}/CMakeFiles")
+
 #! \ingroup CMakeAPI
 macro(ctkMacroCompilePythonScript)
   ctkMacroParseArguments(MY
     "TARGET_NAME;SCRIPTS;RESOURCES;SOURCE_DIR;DESTINATION_DIR;INSTALL_DIR"
-    "NO_INSTALL_SUBDIR"
+    "NO_INSTALL_SUBDIR;GLOBAL_TARGET"
     ${ARGN}
     )
-
-  find_package(PythonInterp REQUIRED)
-  find_package(PythonLibs REQUIRED)
-
-  # Extract python lib path
-  get_filename_component(PYTHON_LIBRARY_PATH ${PYTHON_LIBRARY} PATH)
   
   # Sanity checks
   foreach(varname TARGET_NAME SCRIPTS DESTINATION_DIR INSTALL_DIR)
@@ -31,6 +39,13 @@ macro(ctkMacroCompilePythonScript)
   if(NOT DEFINED MY_SOURCE_DIR)
     set(MY_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
   endif()
+  
+  if("${CTK_COMPILE_PYTHON_SCRIPTS_GLOBAL_TARGET_NAME}" STREQUAL "")
+    set(target ${MY_TARGET_NAME})
+  else()
+    set(MY_GLOBAL_TARGET TRUE)
+    set(target ${CTK_COMPILE_PYTHON_SCRIPTS_GLOBAL_TARGET_NAME})
+  endif()
 
   # Since 'add_custom_command' doesn't play nicely with path having multiple
   # consecutive slashes. Let's make sure there are no trailing slashes.
@@ -38,7 +53,6 @@ macro(ctkMacroCompilePythonScript)
   get_filename_component(MY_DESTINATION_DIR ${MY_DESTINATION_DIR} REALPATH)
 
   set(input_python_files)
-  set(copied_python_files)
   foreach(file ${MY_SCRIPTS})
     # Append "py" extension if needed
     get_filename_component(file_ext ${file} EXT)
@@ -47,50 +61,186 @@ macro(ctkMacroCompilePythonScript)
     endif()
 
     set(src "${MY_SOURCE_DIR}/${file}")
-    set(tgt "${MY_DESTINATION_DIR}/${file}")
+    set(tgt_file ${file})
     if(IS_ABSOLUTE ${file})
       set(src ${file})
       file(RELATIVE_PATH tgt_file ${CMAKE_CURRENT_BINARY_DIR} ${file})
-      set(tgt "${MY_DESTINATION_DIR}/${tgt_file}")
     endif()
-
-    list(APPEND input_python_files ${src})
-    add_custom_command(DEPENDS ${src}
-                        COMMAND ${CMAKE_COMMAND} -E copy ${src} ${tgt}
-                        OUTPUT ${tgt}
-                        COMMENT "Copying python script: ${file}")
-    list(APPEND copied_python_files ${tgt})
+    set_property(GLOBAL APPEND PROPERTY
+      _CTK_${target}_PYTHON_SCRIPTS "${src}|${tgt_file}|${MY_DESTINATION_DIR}")
   endforeach()
+      
+  if(DEFINED MY_RESOURCES)
+    set(resource_input_files)
+    foreach(file ${MY_RESOURCES})
+      set(src "${CMAKE_CURRENT_SOURCE_DIR}/${file}")
+      set_property(GLOBAL APPEND PROPERTY
+      _CTK_${target}_PYTHON_RESOURCES "${src}|${file}|${MY_DESTINATION_DIR}")
+    endforeach()
+  endif()
+
+  set(MY_DIRECTORY_TO_INSTALL ${MY_DESTINATION_DIR})
+  if(MY_NO_INSTALL_SUBDIR)
+    set(MY_DIRECTORY_TO_INSTALL ${MY_DESTINATION_DIR}/)
+  endif()
+
+  # Install python module / resources directory
+  install(DIRECTORY "${MY_DIRECTORY_TO_INSTALL}"
+    DESTINATION "${MY_INSTALL_DIR}" COMPONENT RuntimeLibraries
+    USE_SOURCE_PERMISSIONS)
   
-  add_custom_target(Copy${MY_TARGET_NAME}PythonFiles DEPENDS ${input_python_files} ${copied_python_files})
-  
-  # Byte compile the Python files.
-  set(compile_all_script "${CMAKE_CURRENT_BINARY_DIR}/compile_${MY_TARGET_NAME}_python_scripts.py")
-  
-  # Generate compile_${MY_TARGET_NAME}_python_scripts.py
-  file(WRITE ${compile_all_script} "
+  if(NOT MY_GLOBAL_TARGET)
+    ctkFunctionAddCompilePythonScriptTargets(${target})
+  endif()
+endmacro()
+
+
+function(_ctk_add_copy_python_files_target target type)
+  # 'type' is expected to be either "Resource" or "Script"
+  set(target_name Copy${target}Python${type}Files)
+  if(NOT TARGET ${target_name})
+    string(TOUPPER ${type} type_upper)
+    get_property(entries GLOBAL PROPERTY _CTK_${target}_PYTHON_${type_upper}S)
+    set(input_files)
+    set(copied_files)
+    foreach(entry IN LISTS entries)
+      string(REPLACE "|" ";" tuple "${entry}")
+      list(GET tuple 0 src)
+      list(GET tuple 1 tgt_file)
+      list(GET tuple 2 dest_dir)
+      set(tgt ${dest_dir}/${tgt_file})
+      add_custom_command(DEPENDS ${src}
+                         COMMAND ${CMAKE_COMMAND} -E copy ${src} ${tgt}
+                         OUTPUT ${tgt}
+                         COMMENT "Copying python ${type}: ${tgt_file}")
+      list(APPEND input_files ${src})
+      list(APPEND copied_files ${tgt})
+    endforeach()
+    if(entries)
+      add_custom_target(${target_name} ALL
+        DEPENDS
+          ${input_files}
+          ${copied_files}
+          )
+    endif()
+  endif()
+endfunction()
+
+
+function(_ctk_add_compile_python_directories_target target)
+  set(target_name Compile${target}PythonFiles)
+  if(NOT TARGET ${target_name}) 
+    # Byte compile the Python files.
+    set(compile_all_script "${CMAKE_CURRENT_BINARY_DIR}/compile_${target}_python_scripts.py")
+
+    set(_compileall_code )
+    get_property(entries GLOBAL PROPERTY _CTK_${target}_PYTHON_SCRIPTS)
+    list(REMOVE_DUPLICATES entries)
+    foreach(entry IN LISTS entries)
+      string(REPLACE "|" ";" tuple "${entry}")
+      list(GET tuple 1 tgt_file)
+      list(GET tuple 2 dest_dir)
+      set(tgt ${dest_dir}/${tgt_file})
+      set(_compileall_code "${_compileall_code}\nctk_compile_file('${tgt}', force=1)")
+    endforeach()
+
+    find_package(PythonInterp REQUIRED)
+    find_package(PythonLibs REQUIRED)
+
+    # Extract python lib path
+    get_filename_component(PYTHON_LIBRARY_PATH ${PYTHON_LIBRARY} PATH)
+
+    # Configure cmake script associated with the custom command
+    # required to properly update the library path with PYTHON_LIBRARY_PATH
+    set(compile_all_cmake_script "${CMAKE_CURRENT_BINARY_DIR}/compile_${target}_python_scripts.cmake")
+    file(WRITE ${compile_all_cmake_script} "
 #
 # Generated by ctkMacroCompilePythonScript CMAKE macro
 #
 
+# Generate compile_${target}_python_scripts.py
+file(WRITE \"@compile_all_script@\" \"
+#
+# Generated by ctkMacroCompilePythonScript CMake macro
+#
+
+#
+# Copied function 'compileall.compile_file' introduced in python 2.7 so that code compiled
+# using python 2.6 works.
+#
+# This version of the function has been copied from:
+#   https://github.com/jonashaag/cpython/blob/ce5e5df0c9d8098da05dee26e12ffe2aa331889e/Lib/compileall.py#L61-111
+#
+
+import os
+import sys
+import py_compile
+import struct
+import imp
+
+def ctk_compile_file(fullname, ddir=None, force=0, rx=None, quiet=0):
+    \\\"\\\"\\\"Byte-compile one file.
+
+    Arguments (only fullname is required):
+
+    fullname:  the file to byte-compile
+    ddir:      if given, the directory name compiled in to the
+               byte-code file.
+    force:     if 1, force compilation, even if timestamps are up-to-date
+    quiet:     if 1, be quiet during compilation
+    \\\"\\\"\\\"
+    success = 1
+    name = os.path.basename(fullname)
+    if ddir is not None:
+        dfile = os.path.join(ddir, name)
+    else:
+        dfile = None
+    if rx is not None:
+        mo = rx.search(fullname)
+        if mo:
+            return success
+    if os.path.isfile(fullname):
+        head, tail = name[:-3], name[-3:]
+        if tail == '.py':
+            if not force:
+                try:
+                    mtime = int(os.stat(fullname).st_mtime)
+                    expect = struct.pack('<4sl', imp.get_magic(), mtime)
+                    cfile = fullname + (__debug__ and 'c' or 'o')
+                    with open(cfile, 'rb') as chandle:
+                        actual = chandle.read(8)
+                    if expect == actual:
+                        return success
+                except IOError:
+                    pass
+            if not quiet:
+                print 'Compiling', fullname, '...'
+            try:
+                ok = py_compile.compile(fullname, None, dfile, True)
+            except py_compile.PyCompileError,err:
+                if quiet:
+                    print 'Compiling', fullname, '...'
+                print err.msg
+                success = 0
+            except IOError, e:
+                print "Sorry", e
+                success = 0
+            else:
+                if ok == 0:
+                    success = 0
+    return success
+
 # Based on paraview/VTK/Wrapping/Python/compile_all_vtk.py.in
 
-import compileall
-compileall.compile_dir('@MY_DESTINATION_DIR@')
-file = open('@CMAKE_CURRENT_BINARY_DIR@/python_compile_@MY_TARGET_NAME@_complete', 'w')
+@_compileall_code@
+file = open('@CMAKE_CURRENT_BINARY_DIR@/python_compile_@target@_complete', 'w')
 file.write('Done')
-")
+\")
 
-  # Configure cmake script associated with the custom command
-  # required to properly update the library path with PYTHON_LIBRARY_PATH
-  set(compile_all_cmake_script "${CMAKE_CURRENT_BINARY_DIR}/compile_${MY_TARGET_NAME}_python_scripts.cmake")
-  file(WRITE ${compile_all_cmake_script} "
-#
-# Generated by ctkMacroCompilePythonScript CMAKE macro
-#
+set(ENV{PYTHONPATH} \"@CTK_PYTHON_COMPILE_FILE_SCRIPT_DIR@\")
 
 if(WIN32)
-    set(ENV{PATH} \"@PYTHON_LIBRARY_PATH@;\$ENV{PATH}\")
+  set(ENV{PATH} \"@PYTHON_LIBRARY_PATH@;\$ENV{PATH}\")
 elseif(APPLE)
   set(ENV{DYLD_LIBRARY_PATH} \"@PYTHON_LIBRARY_PATH@:\$ENV{DYLD_LIBRARY_PATH}\")
 else()
@@ -99,52 +249,31 @@ endif()
 
 execute_process(
   COMMAND \"@PYTHON_EXECUTABLE@\" \"@compile_all_script@\"
+  RESULT_VARIABLE result_var
   )
+if(NOT result_var STREQUAL 0)
+  message(FATAL_ERROR \"Failed to compile python scripts: ${target} \")
+endif()
 ")
 
-  add_custom_command(
-    COMMAND ${CMAKE_COMMAND} -P ${compile_all_cmake_script}
-    DEPENDS ${copied_python_files}  ${compile_all_script}
-    OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/python_compile_${MY_TARGET_NAME}_complete"
-    COMMENT "Compiling python scripts: ${MY_TARGET_NAME}"
-    )
-
-  add_custom_target(Compile${MY_TARGET_NAME}PythonFiles ALL
-    DEPENDS 
-      ${CMAKE_CURRENT_BINARY_DIR}/python_compile_${MY_TARGET_NAME}_complete
-      ${compile_all_script}
+    add_custom_command(
+      COMMAND ${CMAKE_COMMAND} -P ${compile_all_cmake_script}
+      DEPENDS Copy${target}PythonScriptFiles
+      OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/python_compile_${target}_complete"
+      COMMENT "Compiling python scripts: ${target}"
       )
-      
-  if(DEFINED MY_RESOURCES)
-    set(resource_input_files)
-    set(copied_resource_files)
-    foreach(file ${MY_RESOURCES})
-      set(src "${CMAKE_CURRENT_SOURCE_DIR}/${file}")
-      set(tgt "${MY_DESTINATION_DIR}/${file}")
-      
-      list(APPEND resource_input_files ${src})
-      add_custom_command(DEPENDS ${src}
-                          COMMAND ${CMAKE_COMMAND} -E copy ${src} ${tgt}
-                          OUTPUT ${tgt}
-                          COMMENT "Copying python resource: ${file}")
-      list(APPEND copied_resource_files ${tgt})
-    endforeach()
-    add_custom_target(Copy${MY_TARGET_NAME}PythonResourceFiles ALL
-      DEPENDS
-        ${resource_input_files} 
-        ${copied_resource_files}
-        )
-  endif()
 
-  set(MY_DIRECTORY_TO_INSTALL ${MY_DESTINATION_DIR})
-  if(MY_NO_INSTALL_SUBDIR)
-    set(MY_DIRECTORY_TO_INSTALL ${MY_DESTINATION_DIR}/)
+    add_custom_target(${target_name} ALL
+      DEPENDS 
+        ${CMAKE_CURRENT_BINARY_DIR}/python_compile_${target}_complete
+        ${compile_all_cmake_script}
+        )
   endif()
+endfunction()
 
-  # Install python module / resources directory
-  install(DIRECTORY "${MY_DIRECTORY_TO_INSTALL}"
-    DESTINATION "${MY_INSTALL_DIR}" COMPONENT RuntimeLibraries
-    USE_SOURCE_PERMISSIONS)
-       
-endmacro()
+function(ctkFunctionAddCompilePythonScriptTargets target)
+  _ctk_add_copy_python_files_target(${target} Script)
+  _ctk_add_copy_python_files_target(${target} Resource)
+  _ctk_add_compile_python_directories_target(${target})
+endfunction()