diff --git a/.gitignore b/.gitignore index c1fd5477d9..3e3eadbc1b 100644 --- a/.gitignore +++ b/.gitignore @@ -89,4 +89,12 @@ CuraEngine #Prevents import failures when plugin running tests plugins/__init__.py -/venv +venv/ +build/ +dist/ +conaninfo.txt +conan.lock +conan_imports_manifest.txt +conanbuildinfo.txt +graph_info.json +Ultimaker-Cura.spec diff --git a/Ultimaker-Cura.spec.jinja b/Ultimaker-Cura.spec.jinja new file mode 100644 index 0000000000..a6aa79e10d --- /dev/null +++ b/Ultimaker-Cura.spec.jinja @@ -0,0 +1,60 @@ +# -*- mode: python ; coding: utf-8 -*- +from PyInstaller.utils.hooks import collect_all + +datas = {{ datas }} +binaries = {{ binaries }} + +hiddenimports = {{ hiddenimports }} + +{% for value in collect_all %}tmp_ret = collect_all('{{ value }}') +datas += tmp_ret[0]; binaries += tmp_ret[1]; hiddenimports += tmp_ret[2] +{% endfor %} + +block_cipher = None + + +a = Analysis( + ['{{ entrypoint }}'], + pathex={{ pathex }}, + binaries=binaries, + datas=datas, + hiddenimports=hiddenimports, + hookspath=[], + hooksconfig={}, + runtime_hooks=[], + excludes=[], + win_no_prefer_redirects=False, + win_private_assemblies=False, + cipher=block_cipher, + noarchive=False, +) +pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) + +exe = EXE( + pyz, + a.scripts, + [], + exclude_binaries=True, + name='{{ name }}', + debug=False, + bootloader_ignore_signals=False, + strip=False, + upx=True, + console=False, + disable_windowed_traceback=False, + argv_emulation=False, + target_arch=None, + codesign_identity=None, + entitlements_file=None, + icon='{{ icon }}', +) +coll = COLLECT( + exe, + a.binaries, + a.zipfiles, + a.datas, + strip=False, + upx=True, + upx_exclude=[], + name='{{ name }}', +) diff --git a/conandata.yml b/conandata.yml index 416d7a1360..e09a67922f 100644 --- a/conandata.yml +++ b/conandata.yml @@ -1,24 +1,138 @@ "dev": - git: - fdm_materials: - url: "https://github.com/Ultimaker/fdm_materials.git" - branch: "master" - directory: "resources/materials/fdm_materials" - private: False conan: curaengine: "curaengine/latest@ultimaker/cura-9365" arcus: "arcus/latest@ultimaker/cura-9365" savitar: "savitar/latest@ultimaker/cura-9365" pynest2d: "pynest2d/latest@ultimaker/cura-9365" + sources: + fdm_materials: + root: "../fdm_materials" + src: "." + dst: "resources/materials/fdm_materials" + filter: [ "**/*.fdm_material", "**/*.sig" ] + pyinstaller: + datas: + cura_plugins: + root: "." + src: "plugins" + dst: "share/cura/plugins" + cura_resources: + root: "." + src: "resources" + dst: "share/cura/resources" + uranium_plugins: + root: "../uranium" + src: "plugins" + dst: "share/uranium/plugins" + uranium_resources: + root: "../uranium" + src: "resources" + dst: "share/uranium/resources" + uranium_um_qt_qml_um: + root: "../uranium" + src: "UM/Qt/qml/UM" + dst: "PyQt6/qt6/qml/UM" + cura_binary_data: + root: "../cura-binary-data" + src: "cura/resources" + dst: "share/cura/resources" + uranium_binary_data: + root: "../cura-binary-data" + src: "uranium/resources" + dst: "share/uranium/resources" + binaries: + curaengine: + package: "curaengine" + src: "bin" + dst: "." + binary: "CuraEngine" + hiddenimports: + - "pySavitar" + - "pyArcus" + - "pynest2d" + - "PyQt6.QtNetwork" + - "logging.handlers" + - "zeroconf" + - "fcntl" + collect_all: + - "cura" + - "UM" + - "serial" + - "Charon" + - "sqlite3" + - "trimesh" + - "win32ctypes" + pathex: + - "." + - "../uranium" + - "../libcharon" + icon: "./icons/Cura.ico" "5.1.0-alpha.1": - git: - fdm_materials: - url: "https://github.com/Ultimaker/fdm_materials.git" - branch: "5.1" - directory: "resources/materials/fdm_materials" - private: False conan: curaengine: "curaengine/latest@ultimaker/cura-9365" arcus: "arcus/latest@ultimaker/cura-9365" savitar: "savitar/latest@ultimaker/cura-9365" pynest2d: "pynest2d/latest@ultimaker/cura-9365" + sources: + fdm_materials: + root: "../fdm_materials" + src: "." + dst: "resources/materials/fdm_materials" + filter: [ "**/*.fdm_material", "**/*.sig" ] + pyinstaller: + datas: + cura_plugins: + root: "." + src: "plugins" + dst: "share/cura/plugins" + cura_resources: + root: "." + src: "resources" + dst: "share/cura/resources" + uranium_plugins: + root: "../uranium" + src: "plugins" + dst: "share/uranium/plugins" + uranium_resources: + root: "../uranium" + src: "resources" + dst: "share/uranium/resources" + uranium_um_qt_qml_um: + root: "../uranium" + src: "UM/Qt/qml/UM" + dst: "PyQt6/qt6/qml/UM" + cura_binary_data: + root: "../cura-binary-data" + src: "cura/resources" + dst: "share/cura/resources" + uranium_binary_data: + root: "../cura-binary-data" + src: "uranium/resources" + dst: "share/uranium/resources" + binaries: + curaengine: + package: "curaengine" + src: "bin" + dst: "." + binary: "CuraEngine" + hiddenimports: + - "pySavitar" + - "pyArcus" + - "pynest2d" + - "PyQt6.QtNetwork" + - "logging.handlers" + - "zeroconf" + - "fcntl" + collect_all: + - "cura" + - "UM" + - "serial" + - "Charon" + - "sqlite3" + - "trimesh" + - "win32ctypes" + pathex: + - "." + - "../uranium" + - "../libcharon" + icon: "./icons/Cura.ico" diff --git a/conanfile.py b/conanfile.py index 2ec3bc8faa..99dd386a4e 100644 --- a/conanfile.py +++ b/conanfile.py @@ -1,4 +1,5 @@ import os +import shutil from pathlib import Path from platform import python_version @@ -22,7 +23,6 @@ class CuraConan(ConanFile): build_policy = "missing" exports = "LICENSE*" settings = "os", "compiler", "build_type", "arch" - short_paths = True generators = "VirtualPythonEnv" options = { "python_version": "ANY", @@ -101,20 +101,24 @@ class CuraConan(ConanFile): self.requires(req) def source(self): - username = os.environ.get("GIT_USERNAME", None) - password = os.environ.get("GIT_PASSWORD", None) - for git_src in self.conan_data[self.version]["git"].values(): - folder = Path(self.source_folder, git_src["directory"]) - should_clone = folder.exists() - git = tools.Git(folder = folder, username = username, password = password) - if should_clone: - git.checkout(git_src["branch"]) + for source in self.conan_data[self.version]["sources"].values(): + src_path = Path(self.source_folder, source["root"], source["src"]) + if not src_path.exists(): + continue + dst_root_path = Path(self.source_folder, source["dst"]) + if dst_root_path.exists(): + shutil.rmtree(dst_root_path, ignore_errors = True) + dst_root_path.mkdir(parents = True) + if "filter" in source: + for pattern in source["filter"]: + for file in src_path.glob(pattern): + rel_file = file.relative_to(src_path) + dst_file = dst_root_path.joinpath(rel_file) + if not dst_file.parent.exists(): + dst_file.parent.mkdir(parents = True) + shutil.copy(file, dst_file) else: - if username and password: - url = git.get_url_with_credentials(git_src["url"]) - else: - url = git_src["url"] - git.clone(url = url, branch = git_src["branch"], shallow = True) + shutil.copytree(src_path, dst_root_path) def generate(self): with open(Path(self.source_folder, "cura", "CuraVersion.py.jinja"), "r") as f: @@ -133,14 +137,61 @@ class CuraConan(ConanFile): cura_marketplace_root = self._marketplace_root, cura_digital_factory_url = self._digital_factory_url)) + if self.options.devtools: + # Create the Ultimaker-Cura.spec based on the data in the conandata.yml + with open(Path(self.source_folder, "Ultimaker-Cura.spec.jinja"), "r") as f: + pyinstaller = Template(f.read()) + + pyinstaller_metadata = self.conan_data[self.version]["pyinstaller"] + datas = [] + for data in pyinstaller_metadata["datas"].values(): + if "package" in data: # get the paths from conan package + src_path = Path(self.deps_cpp_info[data["package"]].rootpath, data["src"]) + elif "root" in data: # get the paths relative from the sourcefolder + src_path = Path(self.source_folder, data["root"], data["src"]) + else: + continue + if src_path.exists(): + datas.append((str(src_path), data["dst"])) + + binaries = [] + for binary in pyinstaller_metadata["binaries"].values(): + if "package" in binary: # get the paths from conan package + src_path = Path(self.deps_cpp_info[binary["package"]].rootpath, binary["src"]) + elif "root" in binary: # get the paths relative from the sourcefolder + src_path = Path(self.source_folder, binary["root"], binary["src"]) + else: + continue + if not src_path.exists(): + continue + for bin in src_path.glob(binary["binary"] + ".*[exe|dll|so|dylib]"): + binaries.append((str(bin), binary["dst"])) + for bin in src_path.glob(binary["binary"]): + binaries.append((str(bin), binary["dst"])) + + pathex = [str(Path(self.source_folder, p)) for p in pyinstaller_metadata["pathex"]] + + with open(Path(self.source_folder, "Ultimaker-Cura.spec"), "w") as f: + f.write(pyinstaller.render( + name = str(self.options.display_name).replace(" ", "-"), + entrypoint = str(Path(self.source_folder, "cura_app.py")), + datas = datas, + binaries = binaries, + hiddenimports = pyinstaller_metadata["hiddenimports"], + collect_all = pyinstaller_metadata["collect_all"], + pathex = pathex, + icon = str(Path(self.source_folder, pyinstaller_metadata["icon"])) + )) + def layout(self): self.folders.source = "." self.folders.build = "venv" self.folders.generators = os.path.join(self.folders.build, "conan") # FIXME: Once libCharon en Uranium are also Packages - if "PYTHONPATH" in os.environ: - self.runenv_info.append_path("PYTHONPATH", os.environ["PYTHONPATH"]) + self.runenv_info.append_path("PYTHONPATH", self.source_folder) + self.runenv_info.append_path("PYTHONPATH", str(Path(self.source_folder).parent.joinpath("uranium"))) + self.runenv_info.append_path("PYTHONPATH", str(Path(self.source_folder).parent.joinpath("libcharon"))) def imports(self): self.copy("CuraEngine.exe", root_package = "curaengine", src = "@bindirs", dst = self.source_folder, keep_path = False) diff --git a/icons/cura.ico b/icons/Cura.ico similarity index 100% rename from icons/cura.ico rename to icons/Cura.ico