conanfile.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483
  1. import os
  2. from io import StringIO
  3. from pathlib import Path
  4. from jinja2 import Template
  5. from conan import ConanFile
  6. from conan.tools.files import copy, rmdir, save, mkdir, rm, update_conandata
  7. from conan.tools.microsoft import unix_path
  8. from conan.tools.env import VirtualRunEnv, Environment, VirtualBuildEnv
  9. from conan.tools.scm import Version
  10. from conan.errors import ConanInvalidConfiguration, ConanException
  11. required_conan_version = ">=2.7.0"
  12. class CuraConan(ConanFile):
  13. name = "cura"
  14. license = "LGPL-3.0"
  15. author = "UltiMaker"
  16. url = "https://github.com/Ultimaker/cura"
  17. description = "3D printer / slicing GUI built on top of the Uranium framework"
  18. topics = ("conan", "python", "pyqt6", "qt", "qml", "3d-printing", "slicer")
  19. build_policy = "missing"
  20. exports = "LICENSE*", "*.jinja"
  21. settings = "os", "compiler", "build_type", "arch"
  22. generators = "VirtualPythonEnv"#, "VirtualRunEnv"
  23. # FIXME: Remove specific branch once merged to main
  24. python_requires = "translationextractor/[>=2.2.0]@ultimaker/cura_11622"
  25. options = {
  26. "enterprise": [True, False],
  27. "staging": [True, False],
  28. "pyinstaller": [True, False],
  29. "cloud_api_version": ["ANY"],
  30. "display_name": ["ANY"], # TODO: should this be an option??
  31. "cura_debug_mode": [True, False], # FIXME: Use profiles
  32. "internal": [True, False],
  33. "enable_i18n": [True, False],
  34. }
  35. default_options = {
  36. "enterprise": False,
  37. "staging": False,
  38. "pyinstaller": False,
  39. "cloud_api_version": "1",
  40. "display_name": "UltiMaker Cura",
  41. "cura_debug_mode": False, # Not yet implemented
  42. "internal": False,
  43. "enable_i18n": False,
  44. }
  45. def set_version(self):
  46. if not self.version:
  47. self.version = self.conan_data["version"]
  48. @property
  49. def _i18n_options(self):
  50. return self.conf.get("user.i18n:options", default = {"extract": True, "build": True}, check_type = dict)
  51. # FIXME: These env vars should be defined in the runenv.
  52. _cura_env = None
  53. # @property
  54. # def _cura_run_env(self):
  55. # if self._cura_env:
  56. # return self._cura_env
  57. #
  58. # self._cura_env = Environment()
  59. # self._cura_env.define("QML2_IMPORT_PATH", str(self._site_packages.joinpath("PyQt6", "Qt6", "qml")))
  60. # self._cura_env.define("QT_PLUGIN_PATH", str(self._site_packages.joinpath("PyQt6", "Qt6", "plugins")))
  61. # self._cura_env.define("CURA_DATA_ROOT", str(self._share_dir.joinpath("cura")))
  62. #
  63. # if self.settings.os == "Linux":
  64. # self._cura_env.define("QT_QPA_FONTDIR", "/usr/share/fonts")
  65. # self._cura_env.define("QT_QPA_PLATFORMTHEME", "xdgdesktopportal")
  66. # self._cura_env.define("QT_XKB_CONFIG_ROOT", "/usr/share/X11/xkb")
  67. # return self._cura_env
  68. @property
  69. def _app_name(self):
  70. if self.options.enterprise:
  71. return str(self.options.display_name) + " Enterprise"
  72. return str(self.options.display_name)
  73. @property
  74. def _urls(self):
  75. if self.options.staging:
  76. return "staging"
  77. return "default"
  78. @property
  79. def _root_dir(self):
  80. return Path(self.deploy_folder if hasattr(self, "deploy_folder") else self.source_folder)
  81. @property
  82. def _base_dir(self):
  83. return self._root_dir.joinpath("venv")
  84. @property
  85. def _share_dir(self):
  86. return self._base_dir.joinpath("share")
  87. @property
  88. def _script_dir(self):
  89. if self.settings.os == "Windows":
  90. return self._base_dir.joinpath("Scripts")
  91. return self._base_dir.joinpath("bin")
  92. @property
  93. def _site_packages(self):
  94. if self.settings.os == "Windows":
  95. return self._base_dir.joinpath("Lib", "site-packages")
  96. py_version = Version(self.dependencies["cpython"].ref.version)
  97. return self._base_dir.joinpath("lib", f"python{py_version.major}.{py_version.minor}", "site-packages")
  98. @property
  99. def _py_interp(self):
  100. py_interp = self._script_dir.joinpath(Path(self.deps_user_info["cpython"].python).name)
  101. if self.settings.os == "Windows":
  102. py_interp = Path(*[f'"{p}"' if " " in p else p for p in py_interp.parts])
  103. return py_interp
  104. @property
  105. def _pyinstaller_spec_arch(self):
  106. if self.settings.os == "Macos":
  107. if self.settings.arch == "armv8":
  108. return "'arm64'"
  109. return "'x86_64'"
  110. return "None"
  111. def _conan_installs(self):
  112. self.output.info("Collecting conan installs")
  113. conan_installs = {}
  114. # list of conan installs
  115. for dependency in self.dependencies.host.values():
  116. conan_installs[dependency.ref.name] = {
  117. "version": str(dependency.ref.version),
  118. "revision": dependency.ref.revision
  119. }
  120. return conan_installs
  121. def _python_installs(self):
  122. self.output.info("Collecting python installs")
  123. python_installs = {}
  124. outer = '"' if self.settings.os == "Windows" else "'"
  125. inner = "'" if self.settings.os == "Windows" else '"'
  126. buffer = StringIO()
  127. self.run(f"""python -c {outer}import importlib.metadata; print({inner};{inner}.join([(package.metadata[{inner}Name{inner}]+{inner},{inner}+ package.metadata[{inner}Version{inner}]) for package in importlib.metadata.distributions()])){outer}""",
  128. env = "virtual_python_env",
  129. stdout = buffer)
  130. packages = str(buffer.getvalue()).strip('\r\n').split(";")
  131. for package in packages:
  132. name, version = package.split(",")
  133. python_installs[name] = {"version": version}
  134. print(python_installs)
  135. return python_installs
  136. # def _generate_version_summary(self):
  137. # version = self.conf.get("user.cura:version", default = self.version, check_type = str)
  138. # cura_version = Version(version)
  139. #
  140. # version_data = {
  141. # "version_major": cura_version.major,
  142. # "version_minor": cura_version.minor,
  143. # "version_patch": cura_version.patch,
  144. # "version_build": cura_version.build if cura_version.build != "" else "0",
  145. # "version_full": self.version,
  146. # "cura_app_name": self._app_name,
  147. # }
  148. #
  149. # file_path = os.path.join(os.getcwd(), 'version_summary.yml')
  150. # self.output.info(f"Generating version summary to {file_path}")
  151. # save(self, file_path, activate_github_actions_version_env)
  152. def _generate_cura_version(self, location):
  153. with open(os.path.join(self.recipe_folder, "CuraVersion.py.jinja"), "r") as f:
  154. cura_version_py = Template(f.read())
  155. # If you want a specific Cura version to show up on the splash screen add the user configuration `user.cura:version=VERSION`
  156. # the global.conf, profile, package_info (of dependency) or via the cmd line `-c user.cura:version=VERSION`
  157. cura_version = Version(self.conf.get("user.cura:version", default = self.version, check_type = str))
  158. pre_tag = f"-{cura_version.pre}" if cura_version.pre else ""
  159. build_tag = f"+{cura_version.build}" if cura_version.build else ""
  160. internal_tag = f"+internal" if self.options.internal else ""
  161. cura_version = f"{cura_version.major}.{cura_version.minor}.{cura_version.patch}{pre_tag}{build_tag}{internal_tag}"
  162. self.output.info(f"Write CuraVersion.py to {self.recipe_folder}")
  163. with open(os.path.join(location, "CuraVersion.py"), "w") as f:
  164. f.write(cura_version_py.render(
  165. cura_app_name = self.name,
  166. cura_app_display_name = self._app_name,
  167. cura_version = cura_version,
  168. cura_version_full = self.version,
  169. cura_build_type = "Enterprise" if self.options.enterprise else "",
  170. cura_debug_mode = self.options.cura_debug_mode,
  171. cura_cloud_api_root = self.conan_data["urls"][self._urls]["cloud_api_root"],
  172. cura_cloud_api_version = self.options.cloud_api_version,
  173. cura_cloud_account_api_root = self.conan_data["urls"][self._urls]["cloud_account_api_root"],
  174. cura_marketplace_root = self.conan_data["urls"][self._urls]["marketplace_root"],
  175. cura_digital_factory_url = self.conan_data["urls"][self._urls]["digital_factory_url"],
  176. cura_latest_url=self.conan_data["urls"][self._urls]["cura_latest_url"],
  177. conan_installs=self._conan_installs(),
  178. python_installs=self._python_installs(),
  179. ))
  180. def _generate_pyinstaller_spec(self, location, entrypoint_location, icon_path, entitlements_file, cura_source_folder):
  181. pyinstaller_metadata = self.conan_data["pyinstaller"]
  182. datas = []
  183. for data in pyinstaller_metadata["datas"].values():
  184. if (not self.options.internal and data.get("internal", False)) or (not self.options.enterprise and data.get("enterprise_only", False)):
  185. continue
  186. if "oses" in data and self.settings.os not in data["oses"]:
  187. continue
  188. if "package" in data: # get the paths from conan package
  189. if data["package"] == self.name:
  190. src_path = str(Path(cura_source_folder, data["src"]))
  191. else:
  192. if data["package"] not in self.dependencies:
  193. raise ConanException(f"Required package {data["package"]} does not exist as a dependency")
  194. package_folder = self.dependencies[data["package"]].package_folder
  195. if package_folder is None:
  196. raise ConanException(f"Unable to find package_folder for {data["package"]}, check that it has not been skipped")
  197. src_path = os.path.join(self.dependencies[data["package"]].package_folder, data["src"])
  198. elif "root" in data: # get the paths relative from the install folder
  199. src_path = os.path.join(self.install_folder, data["root"], data["src"])
  200. else:
  201. raise ConanException("Misformatted conan data for pyinstaller datas, expected either package or root option")
  202. if not Path(src_path).exists():
  203. raise ConanException(f"Missing folder {src_path} for pyinstaller data {data}")
  204. datas.append((str(src_path), data["dst"]))
  205. binaries = []
  206. for binary in pyinstaller_metadata["binaries"].values():
  207. if "package" in binary: # get the paths from conan package
  208. src_path = os.path.join(self.dependencies[binary["package"]].package_folder, binary["src"])
  209. elif "root" in binary: # get the paths relative from the sourcefolder
  210. src_path = str(Path(self.source_folder, binary["root"], binary["src"]))
  211. if self.settings.os == "Windows":
  212. src_path = src_path.replace("\\", "\\\\")
  213. else:
  214. raise ConanException("Misformatted conan data for pyinstaller binaries, expected either package or root option")
  215. if not Path(src_path).exists():
  216. raise ConanException(f"Missing folder {src_path} for pyinstaller binary {binary}")
  217. for bin in Path(src_path).glob(binary["binary"] + "*[.exe|.dll|.so|.dylib|.so.]*"):
  218. binaries.append((str(bin), binary["dst"]))
  219. for bin in Path(src_path).glob(binary["binary"]):
  220. binaries.append((str(bin), binary["dst"]))
  221. # Make sure all Conan dependencies which are shared are added to the binary list for pyinstaller
  222. for _, dependency in self.dependencies.host.items():
  223. for bin_paths in dependency.cpp_info.bindirs:
  224. binaries.extend([(f"{p}", ".") for p in Path(bin_paths).glob("**/*.dll")])
  225. for lib_paths in dependency.cpp_info.libdirs:
  226. binaries.extend([(f"{p}", ".") for p in Path(lib_paths).glob("**/*.so*")])
  227. binaries.extend([(f"{p}", ".") for p in Path(lib_paths).glob("**/*.dylib*")])
  228. # Copy dynamic libs from lib path
  229. binaries.extend([(f"{p}", ".") for p in Path(self._base_dir.joinpath("lib")).glob("**/*.dylib*")])
  230. binaries.extend([(f"{p}", ".") for p in Path(self._base_dir.joinpath("lib")).glob("**/*.so*")])
  231. # Collect all dll's from PyQt6 and place them in the root
  232. binaries.extend([(f"{p}", ".") for p in Path(self._site_packages, "PyQt6", "Qt6").glob("**/*.dll")])
  233. with open(os.path.join(self.recipe_folder, "UltiMaker-Cura.spec.jinja"), "r") as f:
  234. pyinstaller = Template(f.read())
  235. version = self.conf.get("user.cura:version", default = self.version, check_type = str)
  236. cura_version = Version(version)
  237. with open(os.path.join(location, "UltiMaker-Cura.spec"), "w") as f:
  238. f.write(pyinstaller.render(
  239. name = str(self.options.display_name).replace(" ", "-"),
  240. display_name = self._app_name,
  241. entrypoint = entrypoint_location,
  242. datas = datas,
  243. binaries = binaries,
  244. venv_script_path = str(self._script_dir),
  245. hiddenimports = pyinstaller_metadata["hiddenimports"],
  246. collect_all = pyinstaller_metadata["collect_all"],
  247. icon = icon_path,
  248. entitlements_file = entitlements_file,
  249. osx_bundle_identifier = "'nl.ultimaker.cura'" if self.settings.os == "Macos" else "None",
  250. upx = str(self.settings.os == "Windows"),
  251. strip = False, # This should be possible on Linux and MacOS but, it can also cause issues on some distributions. Safest is to disable it for now
  252. target_arch = self._pyinstaller_spec_arch,
  253. macos = self.settings.os == "Macos",
  254. version = f"'{version}'",
  255. short_version = f"'{cura_version.major}.{cura_version.minor}.{cura_version.patch}'",
  256. ))
  257. def export(self):
  258. update_conandata(self, {"version": self.version})
  259. def export_sources(self):
  260. copy(self, "*", os.path.join(self.recipe_folder, "plugins"), os.path.join(self.export_sources_folder, "plugins"))
  261. copy(self, "*", os.path.join(self.recipe_folder, "resources"), os.path.join(self.export_sources_folder, "resources"), excludes = "*.mo")
  262. copy(self, "*", os.path.join(self.recipe_folder, "tests"), os.path.join(self.export_sources_folder, "tests"))
  263. copy(self, "*", os.path.join(self.recipe_folder, "cura"), os.path.join(self.export_sources_folder, "cura"), excludes="CuraVersion.py")
  264. copy(self, "*", os.path.join(self.recipe_folder, "packaging"), os.path.join(self.export_sources_folder, "packaging"))
  265. copy(self, "*", os.path.join(self.recipe_folder, ".run_templates"), os.path.join(self.export_sources_folder, ".run_templates"))
  266. copy(self, "cura_app.py", self.recipe_folder, self.export_sources_folder)
  267. def config_options(self):
  268. if self.settings.os == "Windows" and not self.conf.get("tools.microsoft.bash:path", check_type=str):
  269. del self.options.enable_i18n
  270. def validate(self):
  271. version = self.conf.get("user.cura:version", default = self.version, check_type = str)
  272. if version and Version(version) <= Version("4"):
  273. raise ConanInvalidConfiguration("Only versions 5+ are support")
  274. def requirements(self):
  275. for req in self.conan_data["requirements"]:
  276. if self.options.internal and "fdm_materials" in req:
  277. continue
  278. self.requires(req)
  279. if self.options.internal:
  280. for req in self.conan_data["requirements_internal"]:
  281. self.requires(req)
  282. if self.options.enterprise:
  283. for req in self.conan_data["requirements_enterprise"]:
  284. self.requires(req)
  285. self.requires("cpython/3.12.2")
  286. def build_requirements(self):
  287. if self.options.get_safe("enable_i18n", False):
  288. self.test_requires("gettext/0.21")
  289. def layout(self):
  290. self.folders.source = "."
  291. self.folders.build = "build"
  292. self.folders.generators = os.path.join(self.folders.build, "generators")
  293. self.cpp.package.libdirs = [os.path.join("site-packages", "cura")]
  294. self.cpp.package.bindirs = ["bin"]
  295. self.cpp.package.resdirs = ["resources", "plugins", "packaging"]
  296. def generate(self):
  297. copy(self, "cura_app.py", self.source_folder, str(self._script_dir))
  298. # cura_run_envvars = self._cura_run_env.vars(self, scope = "run")
  299. # ext = ".ps1" if self.settings.os == "Windows" else ".sh"
  300. # cura_run_envvars.save_script(os.path.join(self.folders.generators, f"cura_run_environment{ext}"))
  301. # vr = VirtualRunEnv(self)
  302. # vr.generate()
  303. self._generate_cura_version(str(Path(self.source_folder, "cura")))
  304. # Copy CuraEngine.exe to bindirs of Virtual Python Environment
  305. curaengine = self.dependencies["curaengine"].cpp_info
  306. copy(self, "CuraEngine.exe", curaengine.bindirs[0], self.source_folder, keep_path = False)
  307. copy(self, "CuraEngine", curaengine.bindirs[0], self.source_folder, keep_path = False)
  308. # Copy the external plugins that we want to bundle with Cura
  309. if self.options.enterprise:
  310. rmdir(self, str(Path(self.source_folder, "plugins", "NativeCADplugin")))
  311. native_cad_plugin = self.dependencies["native_cad_plugin"].cpp_info
  312. copy(self, "*", native_cad_plugin.resdirs[0], str(Path(self.source_folder, "plugins", "NativeCADplugin")), keep_path = True)
  313. copy(self, "bundled_*.json", native_cad_plugin.resdirs[1], str(Path(self.source_folder, "resources", "bundled_packages")), keep_path = False)
  314. # Copy resources of cura_binary_data
  315. cura_binary_data = self.dependencies["cura_binary_data"].cpp_info
  316. copy(self, "*", cura_binary_data.resdirs[0], str(self._share_dir.joinpath("cura")), keep_path = True)
  317. copy(self, "*", cura_binary_data.resdirs[1], str(self._share_dir.joinpath("uranium")), keep_path = True)
  318. if self.settings.os == "Windows":
  319. copy(self, "*", cura_binary_data.resdirs[2], str(self._share_dir.joinpath("windows")), keep_path = True)
  320. for dependency in self.dependencies.host.values():
  321. for bindir in dependency.cpp_info.bindirs:
  322. copy(self, "*.dll", bindir, str(self._site_packages), keep_path = False)
  323. for libdir in dependency.cpp_info.libdirs:
  324. copy(self, "*.pyd", libdir, str(self._site_packages), keep_path = False)
  325. copy(self, "*.pyi", libdir, str(self._site_packages), keep_path = False)
  326. copy(self, "*.dylib", libdir, str(self._base_dir.joinpath("lib")), keep_path = False)
  327. # Copy materials (flat)
  328. rmdir(self, str(Path(self.source_folder, "resources", "materials")))
  329. fdm_materials = self.dependencies["fdm_materials"].cpp_info
  330. copy(self, "*", fdm_materials.resdirs[0], self.source_folder)
  331. # Copy internal resources
  332. if self.options.internal:
  333. cura_private_data = self.dependencies["cura_private_data"].cpp_info
  334. copy(self, "*", cura_private_data.resdirs[0], str(self._share_dir.joinpath("cura")))
  335. if self.options.pyinstaller:
  336. entitlements_file = "'{}'".format(str(Path(self.source_folder, "packaging", "MacOS", "cura.entitlements")))
  337. self._generate_pyinstaller_spec(
  338. location=self.generators_folder,
  339. entrypoint_location="'{}'".format(
  340. os.path.join(self.source_folder, self.conan_data["pyinstaller"]["runinfo"]["entrypoint"])).replace(
  341. "\\", "\\\\"),
  342. icon_path="'{}'".format(os.path.join(self.source_folder, "packaging",
  343. self.conan_data["pyinstaller"]["icon"][
  344. str(self.settings.os)])).replace("\\", "\\\\"),
  345. entitlements_file=entitlements_file if self.settings.os == "Macos" else "None",
  346. cura_source_folder=self.source_folder
  347. )
  348. if self.options.get_safe("enable_i18n", False) and self._i18n_options["extract"]:
  349. vb = VirtualBuildEnv(self)
  350. vb.generate()
  351. # # FIXME: once m4, autoconf, automake are Conan V2 ready use self.win_bash and add gettext as base tool_requirement
  352. cpp_info = self.dependencies["gettext"].cpp_info
  353. pot = self.python_requires["translationextractor"].module.ExtractTranslations(self, cpp_info.bindirs[0])
  354. pot.generate()
  355. def build(self):
  356. if self.options.get_safe("enable_i18n", False) and self._i18n_options["build"]:
  357. for po_file in Path(self.source_folder, "resources", "i18n").glob("**/*.po"):
  358. mo_file = Path(self.build_folder, po_file.with_suffix('.mo').relative_to(self.source_folder))
  359. mo_file = mo_file.parent.joinpath("LC_MESSAGES", mo_file.name)
  360. mkdir(self, str(unix_path(self, Path(mo_file).parent)))
  361. cpp_info = self.dependencies["gettext"].cpp_info
  362. self.run(f"{cpp_info.bindirs[0]}/msgfmt {po_file} -o {mo_file} -f", env="conanbuild", ignore_errors=True)
  363. def deploy(self):
  364. copy(self, "*", os.path.join(self.package_folder, self.cpp.package.resdirs[2]), os.path.join(self.deploy_folder, "packaging"), keep_path = True)
  365. # Copy resources of Cura (keep folder structure) needed by pyinstaller to determine the module structure
  366. copy(self, "*", os.path.join(self.package_folder, self.cpp_info.bindirs[0]), str(self._base_dir), keep_path = False)
  367. copy(self, "*", os.path.join(self.package_folder, self.cpp_info.libdirs[0]), str(self._site_packages.joinpath("cura")), keep_path = True)
  368. copy(self, "*", os.path.join(self.package_folder, self.cpp_info.resdirs[0]), str(self._share_dir.joinpath("cura", "resources")), keep_path = True)
  369. copy(self, "*", os.path.join(self.package_folder, self.cpp_info.resdirs[1]), str(self._share_dir.joinpath("cura", "plugins")), keep_path = True)
  370. # Copy the cura_resources resources from the package
  371. rm(self, "conanfile.py", os.path.join(self.package_folder, self.cpp.package.resdirs[0]))
  372. cura_resources = self.dependencies["cura_resources"].cpp_info
  373. for res_dir in cura_resources.resdirs:
  374. copy(self, "*", res_dir, str(self._share_dir.joinpath("cura", "resources", Path(res_dir).name)), keep_path = True)
  375. # Copy resources of Uranium (keep folder structure)
  376. uranium = self.dependencies["uranium"].cpp_info
  377. copy(self, "*", uranium.resdirs[0], str(self._share_dir.joinpath("uranium", "resources")), keep_path = True)
  378. copy(self, "*", uranium.resdirs[1], str(self._share_dir.joinpath("uranium", "plugins")), keep_path = True)
  379. copy(self, "*", uranium.libdirs[0], str(self._site_packages.joinpath("UM")), keep_path = True)
  380. entitlements_file = "'{}'".format(Path(self.deploy_folder, "packaging", "MacOS", "cura.entitlements"))
  381. self._generate_pyinstaller_spec(location = self.deploy_folder,
  382. entrypoint_location = "'{}'".format(os.path.join(self.package_folder, self.cpp_info.bindirs[0], self.conan_data["pyinstaller"]["runinfo"]["entrypoint"])).replace("\\", "\\\\"),
  383. icon_path = "'{}'".format(os.path.join(self.package_folder, self.cpp_info.resdirs[2], self.conan_data["pyinstaller"]["icon"][str(self.settings.os)])).replace("\\", "\\\\"),
  384. entitlements_file = entitlements_file if self.settings.os == "Macos" else "None",
  385. cura_source_folder = self.package_folder)
  386. def package(self):
  387. copy(self, "cura_app.py", src = self.source_folder, dst = os.path.join(self.package_folder, self.cpp.package.bindirs[0]))
  388. copy(self, "*", src = os.path.join(self.source_folder, "cura"), dst = os.path.join(self.package_folder, self.cpp.package.libdirs[0]))
  389. copy(self, "*", src = os.path.join(self.source_folder, "resources"), dst = os.path.join(self.package_folder, self.cpp.package.resdirs[0]))
  390. copy(self, "*.mo", os.path.join(self.build_folder, "resources"), os.path.join(self.package_folder, "resources"))
  391. copy(self, "*", src = os.path.join(self.source_folder, "plugins"), dst = os.path.join(self.package_folder, self.cpp.package.resdirs[1]))
  392. copy(self, "*", src = os.path.join(self.source_folder, "packaging"), dst = os.path.join(self.package_folder, self.cpp.package.resdirs[2]))
  393. copy(self, "pip_requirements_*.txt", src = self.generators_folder, dst = os.path.join(self.package_folder, self.cpp.package.resdirs[-1]))
  394. # Remove the fdm_materials from the package
  395. rmdir(self, os.path.join(self.package_folder, self.cpp.package.resdirs[0], "materials"))
  396. # Remove the cura_resources resources from the package
  397. rm(self, "conanfile.py", os.path.join(self.package_folder, self.cpp.package.resdirs[0]))
  398. cura_resources = self.dependencies["cura_resources"].cpp_info
  399. for res_dir in cura_resources.resdirs:
  400. rmdir(self, os.path.join(self.package_folder, self.cpp.package.resdirs[0], Path(res_dir).name))
  401. def package_info(self):
  402. self.runenv_info.append_path("PYTHONPATH", os.path.join(self.package_folder, "site-packages"))
  403. self.runenv_info.append_path("PYTHONPATH", os.path.join(self.package_folder, "plugins"))
  404. def package_id(self):
  405. self.info.options.rm_safe("enable_i18n")