import os import subprocess from conan import ConanFile from conan.tools.cmake import CMakeToolchain, CMakeDeps, cmake_layout from conan.tools.files import copy, rmdir from conan.errors import ConanException class VideoToRosbag(ConanFile): name = "video-to-rosbag" description = ( "ROS2 (Jazzy) package to convert MP4 videos to image rosbags (MCAP format)" ) license = "MIT" url = "https://github.com/yourusername/video_to_rosbag" author = "your.email@example.com" topics = ("ros2", "jazzy", "video", "rosbag", "mp4", "mcap", "conversion") settings = "os", "compiler", "build_type", "arch" def export_sources(self): copy(self, "CMakeLists.txt", self.recipe_folder, self.export_sources_folder) copy(self, "package.xml", self.recipe_folder, self.export_sources_folder) copy(self, "src/*", self.recipe_folder, self.export_sources_folder) copy(self, "launch/*", self.recipe_folder, self.export_sources_folder) copy(self, "LICENSE", self.recipe_folder, self.export_sources_folder) copy(self, "README.md", self.recipe_folder, self.export_sources_folder) def layout(self): cmake_layout(self) def build_requirements(self): self.tool_requires("make/4.4.1") self.tool_requires("cmake/3.31.9") self.tool_requires( "ros2-jazzy-toolchain/latest", options={"variant": "ros_base"} ) def requirements(self): # Only ros_base - no cv_bridge, no perception self.requires("ros2-jazzy-python/latest") self.requires("ros2-jazzy-toolchain/latest", options={"variant": "ros_base"}) self.requires("openssl/1.1.1w") self.requires("console_bridge/1.0.2") self.requires("tinyxml2/7.1.0") self.requires("yaml-cpp/0.8.0", options={"shared": True}) # OpenCV for video reading - with FFmpeg for MP4 support self.requires( "opencv/4.12.0", options={ "with_ffmpeg": True, # "shared": True, }, ) def generate(self): deps = CMakeDeps(self) deps.generate() tc = CMakeToolchain(self) # Get Python from Conan python_dep = self.dependencies["ros2-jazzy-python"] python_exe = python_dep.conf_info.get( "user.ros2:python_interpreter", check_type=str ) if python_exe: tc.variables["Python3_EXECUTABLE"] = python_exe tc.variables["Python_EXECUTABLE"] = python_exe # OpenCV configuration opencv_dep = self.dependencies.get("opencv") if opencv_dep: tc.variables["OpenCV_ROOT"] = opencv_dep.package_folder.replace("\\", "/") tc.generate() def build(self): build_dep = self.dependencies.get("ros2-jazzy-toolchain") setup_script = None if build_dep: env_vars = build_dep.buildenv_info.vars(self) raw_cmd = env_vars.get("CONAN_ROS2_SOURCE_CMD", "") if "source " in raw_cmd: setup_script = raw_cmd.split("source ")[1].strip().split(" ")[0] elif raw_cmd.strip().endswith((".sh", ".bash")): setup_script = raw_cmd.strip() tc_file = os.path.join(self.generators_folder, "conan_toolchain.cmake") abs_build_base = os.path.join(self.build_folder, "colcon_build") abs_install_base = os.path.join(self.build_folder, "install") colcon_cmd = ( f"colcon build --merge-install " f"--packages-select video_to_rosbag " f"--build-base '{abs_build_base}' " f"--install-base '{abs_install_base}' " f"--event-handlers console_direct+ " f"--cmake-args -DCMAKE_TOOLCHAIN_FILE='{tc_file}' " f"-DCMAKE_BUILD_TYPE={self.settings.build_type}" ) if setup_script: full_cmd = f'/bin/bash -c "source {setup_script} && {colcon_cmd}"' else: full_cmd = colcon_cmd self.output.info(f"Building with: {full_cmd}") self.run(full_cmd, shell=True, cwd=self.source_folder) def package(self): install_dir = os.path.join(self.build_folder, "install") if not os.path.exists(install_dir): raise ConanException( f"Build failed to produce install directory: {install_dir}" ) ros_package_dir = os.path.join(self.package_folder, "video_to_rosbag") copy(self, "*", src=install_dir, dst=ros_package_dir) copy(self, "LICENSE", src=self.source_folder, dst=self.package_folder) copy(self, "README.md", src=self.source_folder, dst=self.package_folder) rmdir(self, os.path.join(ros_package_dir, "share", "doc")) rmdir(self, os.path.join(ros_package_dir, "colcon_build")) def package_info(self): ros_root = os.path.join(self.package_folder, "video_to_rosbag") self.cpp_info.libs = [] self.cpp_info.libdirs = [os.path.join("video_to_rosbag", "lib")] self.cpp_info.includedirs = [os.path.join("video_to_rosbag", "include")] self.cpp_info.bindirs = [os.path.join("video_to_rosbag", "bin")] self.runenv_info.prepend_path("AMENT_PREFIX_PATH", ros_root) self.buildenv_info.prepend_path("AMENT_PREFIX_PATH", ros_root) self.runenv_info.prepend_path("CMAKE_PREFIX_PATH", ros_root) self.buildenv_info.prepend_path("CMAKE_PREFIX_PATH", ros_root) self.runenv_info.prepend_path("PATH", os.path.join(ros_root, "bin")) self.runenv_info.prepend_path("LD_LIBRARY_PATH", os.path.join(ros_root, "lib")) lib_dir = os.path.join(ros_root, "lib") if os.path.exists(lib_dir): for item in os.listdir(lib_dir): if item.startswith("python"): site_packages = os.path.join(lib_dir, item, "site-packages") if os.path.exists(site_packages): self.runenv_info.prepend_path("PYTHONPATH", site_packages) self.buildenv_info.prepend_path("PYTHONPATH", site_packages) break