projects/aports·blob master · refs

287 lines · 12.7 KiB

1diff --git a/cmake/UraniumPluginInstall.cmake b/cmake/UraniumPluginInstall.cmake
2new file mode 100644
3index 000000000..8ba93c60b
4--- /dev/null
5+++ b/cmake/UraniumPluginInstall.cmake
6@@ -0,0 +1,86 @@
7+# Copyright (c) 2022 Ultimaker B.V.
8+# UraniumPluginInstall.cmake is released under the terms of the LGPLv3 or higher.
9+
10+#
11+# This module detects all plugins that need to be installed and adds them using the CMake install() command.
12+# It detects all plugin folder in the path "plugins/*" where there's a "plugin.json" in it.
13+#
14+# Plugins can be configured to NOT BE INSTALLED via the variable "UM_NO_INSTALL_PLUGINS" as a list of string in the
15+# form of "a;b;c" or "a,b,c". By default all plugins will be installed.
16+#
17+
18+# Options or configuration variables
19+set(UM_NO_INSTALL_PLUGINS "" CACHE STRING "A list of plugins that should not be installed, separated with ';' or ','.")
20+
21+file(GLOB_RECURSE _plugin_json_list ${CMAKE_SOURCE_DIR}/plugins/*/plugin.json)
22+list(LENGTH _plugin_json_list _plugin_json_list_len)
23+
24+# Sort the lists alphabetically so we can handle cases like this:
25+# - plugins/my_plugin/plugin.json
26+# - plugins/my_plugin/my_module/plugin.json
27+# In this case, only "plugins/my_plugin" should be added via install().
28+set(_no_install_plugin_list ${UM_NO_INSTALL_PLUGINS})
29+# Sanitize the string so the comparison will be case-insensitive.
30+string(STRIP "${_no_install_plugin_list}" _no_install_plugin_list)
31+string(TOLOWER "${_no_install_plugin_list}" _no_install_plugin_list)
32+
33+# WORKAROUND counterpart of what's in cura-build.
34+string(REPLACE "," ";" _no_install_plugin_list "${_no_install_plugin_list}")
35+
36+list(LENGTH _no_install_plugin_list _no_install_plugin_list_len)
37+
38+if(_no_install_plugin_list_len GREATER 0)
39+ list(SORT _no_install_plugin_list)
40+endif()
41+if(_plugin_json_list_len GREATER 0)
42+ list(SORT _plugin_json_list)
43+endif()
44+
45+# Check all plugin directories and add them via install() if needed.
46+set(_install_plugin_list "")
47+foreach(_plugin_json_path ${_plugin_json_list})
48+ get_filename_component(_plugin_dir ${_plugin_json_path} DIRECTORY)
49+ file(RELATIVE_PATH _rel_plugin_dir ${CMAKE_CURRENT_SOURCE_DIR} ${_plugin_dir})
50+ get_filename_component(_plugin_dir_name ${_plugin_dir} NAME)
51+
52+ # Make plugin name comparison case-insensitive
53+ string(TOLOWER "${_plugin_dir_name}" _plugin_dir_name_lowercase)
54+
55+ # Check if this plugin needs to be skipped for installation
56+ set(_add_plugin ON) # Indicates if this plugin should be added to the build or not.
57+ set(_is_no_install_plugin OFF) # If this plugin will not be added, this indicates if it's because the plugin is
58+ # specified in the NO_INSTALL_PLUGINS list.
59+ if(_no_install_plugin_list)
60+ if("${_plugin_dir_name_lowercase}" IN_LIST _no_install_plugin_list)
61+ set(_add_plugin OFF)
62+ set(_is_no_install_plugin ON)
63+ endif()
64+ endif()
65+
66+ # Make sure this is not a subdirectory in a plugin that's already in the install list
67+ if(_add_plugin)
68+ foreach(_known_install_plugin_dir ${_install_plugin_list})
69+ if(_plugin_dir MATCHES "${_known_install_plugin_dir}.+")
70+ set(_add_plugin OFF)
71+ break()
72+ endif()
73+ endforeach()
74+ endif()
75+
76+ if(_add_plugin)
77+ message(STATUS "[+] PLUGIN TO INSTALL: ${_rel_plugin_dir}")
78+ get_filename_component(_rel_plugin_parent_dir ${_rel_plugin_dir} DIRECTORY)
79+ install(DIRECTORY ${_rel_plugin_dir}
80+ DESTINATION lib${LIB_SUFFIX}/uranium/${_rel_plugin_parent_dir}
81+ PATTERN "__pycache__" EXCLUDE
82+ PATTERN "*.qmlc" EXCLUDE
83+ )
84+ list(APPEND _install_plugin_list ${_plugin_dir})
85+ elseif(_is_no_install_plugin)
86+ message(STATUS "[-] PLUGIN TO REMOVE : ${_rel_plugin_dir}")
87+ execute_process(COMMAND ${Python_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/mod_bundled_packages_json.py
88+ -d ${CMAKE_CURRENT_SOURCE_DIR}/resources/bundled_packages
89+ ${_plugin_dir_name}
90+ RESULT_VARIABLE _mod_json_result)
91+ endif()
92+endforeach()
93diff --git a/cmake/UraniumTranslationTools.cmake b/cmake/UraniumTranslationTools.cmake
94new file mode 100644
95index 000000000..91e65f5a7
96--- /dev/null
97+++ b/cmake/UraniumTranslationTools.cmake
98@@ -0,0 +1,114 @@
99+## 3rd party code:
100+
101+# Macro needed to list all sub-directory of a directory.
102+# There is no function in cmake as far as I know.
103+# Found at: http://stackoverflow.com/a/7788165
104+MACRO(SUBDIRLIST result curdir)
105+ FILE(GLOB children RELATIVE ${curdir} ${curdir}/*)
106+ SET(dirlist "")
107+ FOREACH(child ${children})
108+ IF(IS_DIRECTORY ${curdir}/${child})
109+ STRING(REPLACE "/" "" child ${child})
110+ LIST(APPEND dirlist ${child})
111+ ENDIF()
112+ ENDFOREACH()
113+ SET(${result} ${dirlist})
114+ENDMACRO()
115+
116+
117+## Translation tools:
118+
119+SET(CURA_BINARY_DATA_DIRECTORY CACHE PATH "Directory to the cura-binary-data repository")
120+
121+if(NOT CURA_BINARY_DATA_DIRECTORY AND NOT DEFINED $ENV{CURA_BINARY_DATA_DIRECTORY})
122+ message(STATUS "Using CURA_BINARY_DATA_DIRECTORY from set of environment variables...")
123+ SET(CURA_BINARY_DATA_DIRECTORY $ENV{CURA_BINARY_DATA_DIRECTORY})
124+endif()
125+
126+# Dynamically creates targets for each language to create a *.po-file
127+MACRO(TARGETS_FOR_PO_FILES language)
128+ if(DEFINED GETTEXT_MSGINIT_EXECUTABLE)
129+ message(STATUS "Creating target i18n-create-po-${language}")
130+ add_custom_target(i18n-create-po-${language})
131+ add_dependencies(i18n-create-po i18n-create-po-${language})
132+ endif()
133+ message(STATUS "Creating target i18n-update-po-${language}")
134+ add_custom_target(i18n-update-po-${language})
135+ add_dependencies(i18n-update-po i18n-update-po-${language})
136+ foreach(pot_file ${pot_files})
137+ string(REGEX REPLACE ".*/(.*).pot" "${CMAKE_SOURCE_DIR}/resources/i18n/${language}/\\1.po" po_file ${pot_file})
138+ if(DEFINED GETTEXT_MSGINIT_EXECUTABLE)
139+ add_custom_command(TARGET i18n-create-po-${language} POST_BUILD
140+ COMMAND ${GETTEXT_MSGINIT_EXECUTABLE} ARGS --no-wrap --no-translator -l ${language} -i ${pot_file} -o ${po_file})
141+ endif()
142+ add_custom_command(TARGET i18n-update-po-${language} POST_BUILD
143+ COMMAND ${GETTEXT_MSGMERGE_EXECUTABLE} ARGS --no-wrap --no-fuzzy-matching -o ${po_file} ${po_file} ${pot_file})
144+ endforeach()
145+ENDMACRO()
146+
147+# Dynamically creates targets for each language to create a *.mo-file
148+MACRO(TARGETS_FOR_MO_FILES language)
149+ message(STATUS "Creating target i18n-create-mo-${language}")
150+ add_custom_target(i18n-create-mo-${language})
151+ add_dependencies(i18n-create-mo i18n-create-mo-${language})
152+ if(TARGET i18n-copy-mo)
153+ message(STATUS "Creating target i18n-copy-mo-${language}")
154+ add_custom_target(i18n-copy-mo-${language})
155+ add_dependencies(i18n-copy-mo i18n-copy-mo-${language})
156+ endif()
157+ file(GLOB po_files ${CMAKE_SOURCE_DIR}/resources/i18n/${language}/*.po)
158+ foreach(po_file ${po_files})
159+ string(REGEX REPLACE ".*/(.*).po" "${CMAKE_BINARY_DIR}/resources/i18n/${language}/LC_MESSAGES/\\1.mo" mo_file ${po_file})
160+ add_custom_command(TARGET i18n-create-mo-${language} POST_BUILD
161+ COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/resources/i18n/${language}/LC_MESSAGES/
162+ COMMAND ${GETTEXT_MSGFMT_EXECUTABLE} ARGS ${po_file} -o ${mo_file} -f)
163+ if(TARGET i18n-copy-mo-${language})
164+ string(REGEX REPLACE ".*/(.*).po" "${CURA_BINARY_DATA_DIRECTORY}/${PROJECT_NAME}/resources/i18n/${language}/LC_MESSAGES/\\1.mo" mo_file_binary_copy ${po_file})
165+ add_custom_command(TARGET i18n-copy-mo-${language} POST_BUILD
166+ COMMAND ${CMAKE_COMMAND} -E make_directory ${CURA_BINARY_DATA_DIRECTORY}/resources/i18n/${language}/LC_MESSAGES/
167+ COMMAND ${CMAKE_COMMAND} -E copy ${mo_file} ${mo_file_binary_copy})
168+ add_dependencies(i18n-copy-mo-${language} i18n-create-mo-${language})
169+ endif()
170+ endforeach()
171+ENDMACRO()
172+
173+# Checks for availability of gettext and when found creates all targets
174+# TODO: Adding option to set the PROJECT_NAME externally!
175+MACRO(CREATE_TRANSLATION_TARGETS)
176+find_package(Gettext)
177+
178+if(GETTEXT_FOUND)
179+ # translations target will convert .po files into .mo and .qm as needed.
180+ # The files are checked for a _qt suffix and if it is found, converted to
181+ # qm, otherwise they are converted to .po.
182+ if(DEFINED GETTEXT_MSGINIT_EXECUTABLE)
183+ message(STATUS "Creating target i18n-create-po")
184+ add_custom_target(i18n-create-po)
185+ else()
186+ message(WARNING "GETTEXT_MSGINIT_EXECUTABLE is undefined!\nSkipping to create i18n-create-po* targets...")
187+ endif()
188+ if(CURA_BINARY_DATA_DIRECTORY)
189+ if(EXISTS ${CURA_BINARY_DATA_DIRECTORY})
190+ message(STATUS "CURA_BINARY_DATA_DIRECTORY: ${CURA_BINARY_DATA_DIRECTORY}")
191+ message(STATUS "Creating target i18n-copy-mo")
192+ add_custom_target(i18n-copy-mo)
193+ else()
194+ message(WARNING "CURA_BINARY_DATA_DIRECTORY does not exist! (${CURA_BINARY_DATA_DIRECTORY})")
195+ endif()
196+ else()
197+ message(WARNING "CURA_BINARY_DATA_DIRECTORY is not set!")
198+ endif()
199+ message(STATUS "Creating target i18n-update-po")
200+ add_custom_target(i18n-update-po)
201+ message(STATUS "Creating target i18n-create-mo")
202+ add_custom_target(i18n-create-mo ALL)
203+
204+ SUBDIRLIST(languages ${CMAKE_SOURCE_DIR}/resources/i18n/)
205+ file(GLOB pot_files ${CMAKE_SOURCE_DIR}/resources/i18n/*.pot)
206+ foreach(language ${languages})
207+ TARGETS_FOR_PO_FILES(${language})
208+ TARGETS_FOR_MO_FILES(${language})
209+ endforeach()
210+ install(DIRECTORY ${CMAKE_BINARY_DIR}/resources DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/)
211+endif()
212+ENDMACRO()
213diff --git a/cmake/mod_bundled_packages_json.py b/cmake/mod_bundled_packages_json.py
214new file mode 100644
215index 000000000..6423591f5
216--- /dev/null
217+++ b/cmake/mod_bundled_packages_json.py
218@@ -0,0 +1,69 @@
219+#!/usr/bin/env python3
220+#
221+# This script removes the given package entries in the bundled_packages JSON files. This is used by the PluginInstall
222+# CMake module.
223+#
224+
225+import argparse
226+import collections
227+import json
228+import os
229+import sys
230+
231+
232+## Finds all JSON files in the given directory recursively and returns a list of those files in absolute paths.
233+#
234+# \param work_dir The directory to look for JSON files recursively.
235+# \return A list of JSON files in absolute paths that are found in the given directory.
236+def find_json_files(work_dir: str) -> list:
237+ json_file_list = []
238+ for root, dir_names, file_names in os.walk(work_dir):
239+ for file_name in file_names:
240+ abs_path = os.path.abspath(os.path.join(root, file_name))
241+ json_file_list.append(abs_path)
242+ return json_file_list
243+
244+
245+## Removes the given entries from the given JSON file. The file will modified in-place.
246+#
247+# \param file_path The JSON file to modify.
248+# \param entries A list of strings as entries to remove.
249+# \return None
250+def remove_entries_from_json_file(file_path: str, entries: list) -> None:
251+ try:
252+ with open(file_path, "r", encoding = "utf-8") as f:
253+ package_dict = json.load(f, object_hook = collections.OrderedDict)
254+ except Exception as e:
255+ msg = "Failed to load '{file_path}' as a JSON file. This file will be ignored Exception: {e}"\
256+ .format(file_path = file_path, e = e)
257+ sys.stderr.write(msg + os.linesep)
258+ return
259+
260+ for entry in entries:
261+ if entry in package_dict:
262+ del package_dict[entry]
263+ print("[INFO] Remove entry [{entry}] from [{file_path}]".format(file_path = file_path, entry = entry))
264+
265+ try:
266+ with open(file_path, "w", encoding = "utf-8", newline = "\n") as f:
267+ json.dump(package_dict, f, indent = 4)
268+ except Exception as e:
269+ msg = "Failed to write '{file_path}' as a JSON file. Exception: {e}".format(file_path = file_path, e = e)
270+ raise IOError(msg)
271+
272+
273+def main() -> None:
274+ parser = argparse.ArgumentParser("mod_bundled_packages_json")
275+ parser.add_argument("-d", "--dir", dest = "work_dir",
276+ help = "The directory to look for bundled packages JSON files, recursively.")
277+ parser.add_argument("entries", metavar = "ENTRIES", type = str, nargs = "+")
278+
279+ args = parser.parse_args()
280+
281+ json_file_list = find_json_files(args.work_dir)
282+ for json_file_path in json_file_list:
283+ remove_entries_from_json_file(json_file_path, args.entries)
284+
285+
286+if __name__ == "__main__":
287+ main()