Просмотр исходного кода

Add CMake options to exclude plugins for installation

CURA-6557
Lipu Fei 5 лет назад
Родитель
Сommit
9e6207794b
3 измененных файлов с 172 добавлено и 3 удалено
  1. 3 3
      CMakeLists.txt
  2. 98 0
      cmake/CuraPluginInstall.cmake
  3. 71 0
      cmake/mod_bundled_packages_json.py

+ 3 - 3
CMakeLists.txt

@@ -49,7 +49,7 @@ endif()
 
 
 if(NOT ${URANIUM_DIR} STREQUAL "")
-    set(CMAKE_MODULE_PATH "${URANIUM_DIR}/cmake")
+    set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH};${URANIUM_DIR}/cmake")
 endif()
 if(NOT ${URANIUM_SCRIPTS_DIR} STREQUAL "")
     list(APPEND CMAKE_MODULE_PATH ${URANIUM_DIR}/cmake)
@@ -63,8 +63,8 @@ endif()
 
 install(DIRECTORY resources
         DESTINATION ${CMAKE_INSTALL_DATADIR}/cura)
-install(DIRECTORY plugins
-        DESTINATION lib${LIB_SUFFIX}/cura)
+
+include(CuraPluginInstall)
 
 if(NOT APPLE AND NOT WIN32)
     install(FILES cura_app.py

+ 98 - 0
cmake/CuraPluginInstall.cmake

@@ -0,0 +1,98 @@
+# Copyright (c) 2019 Ultimaker B.V.
+# CuraPluginInstall.cmake is released under the terms of the LGPLv3 or higher.
+
+#
+# This module detects all plugins that need to be installed and adds them using the CMake install() command.
+# It detects all plugin folder in the path "plugins/*" where there's a "plugin.json" in it.
+#
+# Plugins can be configured to NOT BE INSTALLED via the variable "CURA_NO_INSTALL_PLUGINS" as a list of string in the
+# form of "a;b;c" or "a,b,c". By default all plugins will be installed.
+#
+
+# FIXME: Remove the code for CMake <3.12 once we have switched over completely.
+# FindPython3 is a new module since CMake 3.12. It deprecates FindPythonInterp and FindPythonLibs. The FindPython3
+# module is copied from the CMake repository here so in CMake <3.12 we can still use it.
+if(${CMAKE_VERSION} VERSION_LESS 3.12)
+    # Use FindPythonInterp and FindPythonLibs for CMake <3.12
+    find_package(PythonInterp 3 REQUIRED)
+
+    set(Python3_EXECUTABLE ${PYTHON_EXECUTABLE})
+else()
+    # Use FindPython3 for CMake >=3.12
+    find_package(Python3 REQUIRED COMPONENTS Interpreter)
+endif()
+
+# Options or configuration variables
+set(CURA_NO_INSTALL_PLUGINS "" CACHE STRING "A list of plugins that should not be installed, separated with ';' or ','.")
+
+file(GLOB_RECURSE _plugin_json_list ${CMAKE_SOURCE_DIR}/plugins/*/plugin.json)
+list(LENGTH _plugin_json_list _plugin_json_list_len)
+
+# Sort the lists alphabetically so we can handle cases like this:
+#   - plugins/my_plugin/plugin.json
+#   - plugins/my_plugin/my_module/plugin.json
+# In this case, only "plugins/my_plugin" should be added via install().
+set(_no_install_plugin_list ${CURA_NO_INSTALL_PLUGINS})
+# Sanitize the string so the comparison will be case-insensitive.
+string(STRIP   "${_no_install_plugin_list}" _no_install_plugin_list)
+string(TOLOWER "${_no_install_plugin_list}" _no_install_plugin_list)
+
+# WORKAROUND counterpart of what's in cura-build.
+string(REPLACE "," ";" _no_install_plugin_list "${_no_install_plugin_list}")
+
+list(LENGTH _no_install_plugin_list _no_install_plugin_list_len)
+
+if(_no_install_plugin_list_len GREATER 0)
+    list(SORT _no_install_plugin_list)
+endif()
+if(_plugin_json_list_len GREATER 0)
+    list(SORT _plugin_json_list)
+endif()
+
+# Check all plugin directories and add them via install() if needed.
+set(_install_plugin_list "")
+foreach(_plugin_json_path ${_plugin_json_list})
+    get_filename_component(_plugin_dir ${_plugin_json_path} DIRECTORY)
+    file(RELATIVE_PATH _rel_plugin_dir ${CMAKE_CURRENT_SOURCE_DIR} ${_plugin_dir})
+    get_filename_component(_plugin_dir_name ${_plugin_dir} NAME)
+
+    # Make plugin name comparison case-insensitive
+    string(TOLOWER "${_plugin_dir_name}" _plugin_dir_name_lowercase)
+
+    # Check if this plugin needs to be skipped for installation
+    set(_add_plugin ON)
+    set(_is_no_install_plugin OFF)
+    if(_no_install_plugin_list)
+        if("${_plugin_dir_name_lowercase}" IN_LIST _no_install_plugin_list)
+            set(_add_plugin OFF)
+            set(_is_no_install_plugin ON)
+        endif()
+    endif()
+
+    # Make sure this is not a subdirectory in a plugin that's already in the install list
+    if(_add_plugin)
+        foreach(_known_install_plugin_dir ${_install_plugin_list})
+            if(_plugin_dir MATCHES "${_known_install_plugin_dir}.+")
+                set(_add_plugin OFF)
+                break()
+            endif()
+        endforeach()
+    endif()
+
+    if(_add_plugin)
+        message(STATUS "[+] PLUGIN TO INSTALL: ${_rel_plugin_dir}")
+        get_filename_component(_rel_plugin_parent_dir ${_rel_plugin_dir} DIRECTORY)
+        install(DIRECTORY ${_rel_plugin_dir}
+                DESTINATION lib${LIB_SUFFIX}/cura/${_rel_plugin_parent_dir}
+                PATTERN "__pycache__" EXCLUDE
+                PATTERN "*.qmlc" EXCLUDE
+                )
+        list(APPEND _install_plugin_list ${_plugin_dir})
+    elseif(_is_no_install_plugin)
+        message(STATUS "[-] PLUGIN TO REMOVE : ${_rel_plugin_dir}")
+        execute_process(COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/mod_bundled_packages_json.py
+                        -d ${CMAKE_CURRENT_SOURCE_DIR}/resources/bundled_packages
+                        ${_plugin_dir_name}
+                        RESULT_VARIABLE _mod_json_result)
+    endif()
+endforeach()

+ 71 - 0
cmake/mod_bundled_packages_json.py

@@ -0,0 +1,71 @@
+#!/usr/bin/env python3
+#
+# This script removes the given package entries in the bundled_packages JSON files. This is used by the PluginInstall
+# CMake module.
+#
+
+import argparse
+import collections
+import json
+import os
+import sys
+
+
+def find_json_files(work_dir: str) -> list:
+    """
+    Finds all JSON files in the given directory recursively and returns a list of those files in absolute paths.
+    :param work_dir: The directory to look for JSON files recursively.
+    :return: A list of JSON files in absolute paths that are found in the given directory.
+    """
+    json_file_list = []
+    for root, dir_names, file_names in os.walk(work_dir):
+        for file_name in file_names:
+            abs_path = os.path.abspath(os.path.join(root, file_name))
+            json_file_list.append(abs_path)
+    return json_file_list
+
+
+def remove_entries_from_json_file(file_path: str, entries: list) -> None:
+    """
+    Removes the given entries from the given JSON file. The file will modified in-place.
+    :param file_path: The JSON file to modify.
+    :param entries: A list of strings as entries to remove.
+    :return: None
+    """
+    try:
+        with open(file_path, "r", encoding = "utf-8") as f:
+            package_dict = json.load(f, object_hook = collections.OrderedDict)
+    except Exception as e:
+        msg = "Failed to load '{file_path}' as a JSON file. This file will be ignored Exception: {e}"\
+            .format(file_path = file_path, e = e)
+        sys.stderr.write(msg + os.linesep)
+        return
+
+    for entry in entries:
+        if entry in package_dict:
+            del package_dict[entry]
+            print("[INFO] Remove entry [{entry}] from [{file_path}]".format(file_path = file_path, entry = entry))
+
+    try:
+        with open(file_path, "w", encoding = "utf-8", newline = "\n") as f:
+            json.dump(package_dict, f, indent = 4)
+    except Exception as e:
+        msg = "Failed to write '{file_path}' as a JSON file. Exception: {e}".format(file_path = file_path, e = e)
+        raise IOError(msg)
+
+
+def main() -> None:
+    parser = argparse.ArgumentParser("mod_bundled_packages_json")
+    parser.add_argument("-d", "--dir", dest = "work_dir",
+                        help = "The directory to look for bundled packages JSON files, recursively.")
+    parser.add_argument("entries", metavar = "ENTRIES", type = str, nargs = "+")
+
+    args = parser.parse_args()
+
+    json_file_list = find_json_files(args.work_dir)
+    for json_file_path in json_file_list:
+        remove_entries_from_json_file(json_file_path, args.entries)
+
+
+if __name__ == "__main__":
+    main()