diff --git a/recipes/webots-controller/all/CMakeLists.txt b/recipes/webots-controller/all/CMakeLists.txt new file mode 100644 index 0000000..0e18009 --- /dev/null +++ b/recipes/webots-controller/all/CMakeLists.txt @@ -0,0 +1,46 @@ +cmake_minimum_required(VERSION 3.23) +project(webots_controller LANGUAGES C CXX) + +option(WEBOTS_CONTROLLER_NO_PLUGINS "Disable runtime plugin loading (dlopen/LoadLibrary paths)." ON) + +if(NOT DEFINED WEBOTS_REPO_SOURCE_DIR) + set(WEBOTS_REPO_SOURCE_DIR "") +endif() + +if(WEBOTS_REPO_SOURCE_DIR STREQUAL "") + message(FATAL_ERROR "WEBOTS_REPO_SOURCE_DIR must point to cloned Webots repository") +endif() + +if(MSVC) + find_package(dirent CONFIG REQUIRED) + + file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/cmake") + file( + DOWNLOAD + https://github.com/cpm-cmake/CPM.cmake/releases/download/v0.40.8/CPM.cmake + "${CMAKE_CURRENT_BINARY_DIR}/cmake/CPM.cmake" + EXPECTED_HASH + SHA256=78ba32abdf798bc616bab7c73aac32a17bbd7b06ad9e26a6add69de8f3ae4791) + include("${CMAKE_CURRENT_BINARY_DIR}/cmake/CPM.cmake") + + CPMAddPackage( + NAME libunistd + GITHUB_REPOSITORY robinrowe/libunistd + GIT_TAG v1.4 + DOWNLOAD_ONLY YES) + + add_library(webots_controller_msvc_unix_headers INTERFACE) + target_include_directories( + webots_controller_msvc_unix_headers + SYSTEM + INTERFACE "$" + "${libunistd_SOURCE_DIR}/unistd") + target_link_libraries(webots_controller_msvc_unix_headers + INTERFACE dirent::dirent) + target_compile_definitions(webots_controller_msvc_unix_headers + INTERFACE _CRT_DECLARE_NONSTDC_NAMES=0) + target_compile_options(webots_controller_msvc_unix_headers + INTERFACE /FIunistd.h) +endif() + +add_subdirectory(src/controller) \ No newline at end of file diff --git a/recipes/webots-controller/all/conandata.yml b/recipes/webots-controller/all/conandata.yml index d143a97..b271ec2 100644 --- a/recipes/webots-controller/all/conandata.yml +++ b/recipes/webots-controller/all/conandata.yml @@ -1,10 +1,10 @@ sources: - "2023a": + "r2023a": git_url: "https://github.com/cyberbotics/webots.git" ref: "R2023a" - "2023b": + "r2023b": git_url: "https://github.com/cyberbotics/webots.git" ref: "R2023b" - "2025a": + "r2025a": git_url: "https://github.com/cyberbotics/webots.git" ref: "R2025a" diff --git a/recipes/webots-controller/all/conanfile.py b/recipes/webots-controller/all/conanfile.py index c465950..b405254 100644 --- a/recipes/webots-controller/all/conanfile.py +++ b/recipes/webots-controller/all/conanfile.py @@ -1,116 +1,9 @@ from conan import ConanFile -from conan.errors import ConanException, ConanInvalidConfiguration -from conan.tools.env import Environment -from conan.tools.files import chdir, copy, get +from conan.tools.cmake import CMake, CMakeDeps, CMakeToolchain, cmake_layout +from conan.tools.files import copy from conan.tools.scm import Git -from pathlib import Path +from conan.tools.microsoft import is_msvc import os -import shutil -import subprocess -import platform - - -def find_webots_install_path() -> str | None: - """Locate an existing Webots installation directory. - - Windows strategy (in order): - 1. Respect WEBOTS_HOME environment variable if it points to an existing path. - 2. Query uninstall registry keys (both 64-bit and 32-bit views) for InstallLocation: - HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Webots - 3. Look in common default install directories (Program Files, Program Files (x86)). - 4. Fallback: derive from a discovered `webots-controller.exe` on PATH. - - macOS strategy: - * If a `webots` or `webots-controller` binary is found inside a Webots.app bundle, - return the bundle root (…/Webots.app). - - Linux / Other: - * Attempt to locate `webots-controller` or `webots` via PATH and return its parent directory. - - Returns None if nothing suitable is found. - """ - - system = platform.system() - - # 1. Explicit env var (all platforms) - env_home = os.getenv("WEBOTS_HOME") - if env_home and os.path.exists(env_home): - return env_home - - if system == "Windows": - # 2. Registry (both views) - try: # pragma: no cover (import guarded for non-Windows) - import winreg # type: ignore - except ImportError: # pragma: no cover - winreg = None # type: ignore - - if winreg: - reg_paths = [ - ( - winreg.HKEY_LOCAL_MACHINE, - r"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Webots", - ), - # 32-bit view on 64-bit systems - ( - winreg.HKEY_LOCAL_MACHINE, - r"SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Webots", - ), - ] - for hive, subkey in reg_paths: - try: - with winreg.OpenKey(hive, subkey) as key: # type: ignore[attr-defined] - install_path, _ = winreg.QueryValueEx(key, "InstallLocation") # type: ignore[attr-defined] - if install_path and os.path.exists(install_path): - return install_path - except FileNotFoundError: - pass - except OSError: - pass - - # 3. Common default directories - program_files = os.environ.get("ProgramFiles", r"C:\\Program Files") - program_files_x86 = os.environ.get("ProgramFiles(x86)", r"C:\\Program Files (x86)") - candidates = [ - os.path.join(program_files, "Webots"), - os.path.join(program_files_x86, "Webots"), - ] - for c in candidates: - if os.path.exists(c): - return c - - # 4. PATH discovery of controller exe - exe = shutil.which("webots-controller.exe") or shutil.which("webots.exe") - if exe: - parent = os.path.dirname( - os.path.dirname(os.path.dirname(os.path.dirname(exe))) - ) # heuristic - # Explanation: typical Windows structure: - # \msys64\mingw64\bin\webots-controller.exe - # So we ascend four levels to reach WEBOTS_HOME. - if os.path.exists(parent): - return parent - return None - - # Non-Windows - candidate = shutil.which("webots-controller") or shutil.which("webots") - if not candidate: - return None - - # Resolve symlinks to get actual installation root. This matters on Linux - # distributions where Webots may be installed under /opt/webots but only a - # symlink placed into /usr/local/bin or similar locations. - resolved = os.path.realpath(candidate) - - if system == "Darwin": - token = "Webots.app" - # If the resolved path sits inside an app bundle, return the bundle root. - if token in resolved: - prefix, _sep, _rest = resolved.partition(token) - return os.path.join(prefix, token) - # Fallback: just use the directory of the (possibly resolved) binary. - return os.path.dirname(resolved) - - return os.path.dirname(resolved) class WebotsControllerConan(ConanFile): @@ -120,229 +13,101 @@ class WebotsControllerConan(ConanFile): url = "https://github.com/cyberbotics/webots" homepage = "https://cyberbotics.com" topics = ("robotics", "simulation", "webots", "controller") - package_type = "shared-library" + package_type = "library" settings = "os", "arch", "compiler", "build_type" - options = {"fPIC": [True, False]} - default_options = {"fPIC": True} + options = { + "shared": [True, False], + "fPIC": [True, False], + "no_plugins": [True, False], + } + default_options = { + "shared": True, + "fPIC": True, + "no_plugins": True, + } exports_sources = ( + "CMakeLists.txt", + "src/*", + "patches/*.patch", "test_package/conanfile.py", "test_package/CMakeLists.txt", "test_package/test_package.cpp", + "test_package/test_package.c", ) @property def _source_subfolder(self): return "source_subfolder" - @property - def _is_system_package(self): - return str(self.version) == "system" - - @property - def _system_webots_home(self): - webots_home = find_webots_install_path() - if not webots_home: - raise ConanInvalidConfiguration( - f"{self.ref} could not locate an installed Webots root. " - "Set WEBOTS_HOME or install Webots in a standard location discoverable by find_webots_install_path()." - ) - return os.path.abspath(os.path.expanduser(webots_home)) - - def _validate_system_webots_home(self): - webots_home = self._system_webots_home - required_paths = [ - os.path.join(webots_home, "include", "controller", "c", "webots", "robot.h"), - os.path.join(webots_home, "include", "controller", "cpp", "webots", "Robot.hpp"), - os.path.join(webots_home, "lib", "controller"), - ] - missing = [path for path in required_paths if not os.path.exists(path)] - if missing: - formatted = ", ".join(f"'{path}'" for path in missing) - raise ConanInvalidConfiguration( - f"{self.ref} could not find the expected Webots controller files under WEBOTS_HOME='{webots_home}': {formatted}" - ) - def config_options(self): if self.settings.os == "Windows": self.options.rm_safe("fPIC") - def validate(self): - if self._is_system_package: - self._validate_system_webots_home() + def configure(self): + if self.options.get_safe("no_plugins", True): return - if str(self.settings.compiler) == "msvc": - raise ConanInvalidConfiguration("Webots controller libraries are built with GCC/Clang toolchains, not MSVC") + if self.settings.os == "Windows" and is_msvc(self): + raise ValueError("Official Windows Webots is built with MinGW GCC, not MSVC.") def source(self): - if self._is_system_package: - return source_data = self.conan_data["sources"][self.version] if "git_url" in source_data: git = Git(self) - clone_args = [ - "--depth", - "1", - ] + clone_args = ["--depth", "1"] if "ref" in source_data: clone_args.extend(["--branch", source_data["ref"]]) git.clone(url=source_data["git_url"], target=self._source_subfolder, args=clone_args) - with chdir(self, os.path.join(self.source_folder, self._source_subfolder)): - if "ref" in source_data: - git.checkout(source_data["ref"]) - git.run("submodule update --init --recursive --depth 1") + if "ref" in source_data: + git.run(f"-C {self._source_subfolder} checkout {source_data['ref']}") + git.run("-C {} submodule update --init --recursive --depth 1".format(self._source_subfolder)) return - get(self, **source_data, strip_root=True, destination=self._source_subfolder) + raise ValueError("conandata.yml sources entry must provide git_url/ref for this recipe") - def requirements(self): - # [Using a MinGW as tool_requires to build with gcc in Windows](https://docs.conan.io/2/examples/dev_flow/tool_requires/mingw.html#examples-dev-flow-tool-requires-mingw) - if not self._is_system_package and self.settings.os == "Windows": - self.tool_requires("msys2/cci.latest") + def layout(self): + cmake_layout(self) - def package_id(self): - if self._is_system_package: - self.info.clear() + def generate(self): + deps = CMakeDeps(self) + deps.generate() + + tc = CMakeToolchain(self) + tc.variables["WEBOTS_CONTROLLER_NO_PLUGINS"] = bool(self.options.no_plugins) + webots_repo_source_dir = os.path.join(self.source_folder, self._source_subfolder).replace("\\", "/") + tc.variables["WEBOTS_REPO_SOURCE_DIR"] = webots_repo_source_dir + + libcontroller_version = str(self.version) + if libcontroller_version.startswith("r"): + libcontroller_version = f"R{libcontroller_version[1:]}" + tc.variables["LIBCONTROLLER_VERSION"] = libcontroller_version + tc.generate() def build(self): - if self._is_system_package: - return - source_root = os.path.join(self.source_folder, self._source_subfolder) - if self.settings.os == "Windows": - # Webots makefiles invoke POSIX tools and shell fragments, so run MSYS make from bash. - bash_exe = None - make_exe = None - for dep in self.dependencies.build.values(): - if not dep.ref or not dep.package_folder: - continue - if dep.ref.name == "msys2": - bash_matches = list(Path(dep.package_folder).glob("**/bash.exe")) - make_matches = list(Path(dep.package_folder).glob("**/make.exe")) - if bash_matches and not bash_exe: - bash_exe = str(bash_matches[0]) - if make_matches and not make_exe: - make_exe = str(make_matches[0]) - if not bash_exe or not make_exe: - raise ConanInvalidConfiguration( - "Windows build requires bash.exe and make.exe from tool_requires 'msys2'." - ) - mingw_dep = next((dep for dep in self.dependencies.build.values() if dep.ref and dep.ref.name == "mingw-builds"), None) - if mingw_dep is None: - raise ConanInvalidConfiguration("Windows build requires tool_requires 'mingw-builds'.") - env = os.environ.copy() - env["WEBOTS_HOME"] = source_root - env["PATH"] = os.pathsep.join([ - os.path.join(mingw_dep.package_folder, "bin"), - str(Path(make_exe).parent), - env.get("PATH", ""), - ]) - drive, tail = os.path.splitdrive(make_exe) - msys_make = f"/{drive[0].lower()}{tail.replace('\\', '/')}" - for controller_dir in ("c", "cpp"): - cwd = os.path.join(source_root, "src", "controller", controller_dir) - subprocess.run([bash_exe, "--noprofile", "--norc", "-c", f'"{msys_make}" clean'], cwd=cwd, env=env, check=True) - subprocess.run([bash_exe, "--noprofile", "--norc", "-c", f'"{msys_make}" release'], cwd=cwd, env=env, check=True) - else: - build_env = Environment() - build_env.define("WEBOTS_HOME", source_root) - with build_env.vars(self).apply(): - with chdir(self, os.path.join(source_root, "src", "controller", "c")): - self.run("make clean") - self.run("make release") - with chdir(self, os.path.join(source_root, "src", "controller", "cpp")): - self.run("make clean") - self.run("make release") - - expected_artifacts = { - "Linux": ["libController.so", "libCppController.so"], - "Macos": ["libController.dylib", "libCppController.dylib"], - "Windows": ["Controller.dll", "libController.a", "CppController.dll", "libCppController.a"], - } - lib_dir = os.path.join(source_root, "lib", "controller") - missing = [name for name in expected_artifacts.get(str(self.settings.os), []) if not os.path.exists(os.path.join(lib_dir, name))] - if missing: - raise ConanException( - f"Expected Webots controller artifacts were not produced in '{lib_dir}': {', '.join(missing)}. " - "On Windows, the upstream Webots controller build may be incompatible with the current MinGW/MSYS toolchain setup." - ) + cmake = CMake(self) + cmake.configure() + cmake.build() def package(self): - if self._is_system_package: - return - source_root = os.path.join(self.source_folder, self._source_subfolder) - build_root = os.path.join(self.build_folder, self._source_subfolder) - copy(self, "LICENSE", src=source_root, dst=os.path.join(self.package_folder, "licenses")) - - copy(self, "*.h", src=os.path.join(source_root, "include", "controller", "c"), dst=os.path.join(self.package_folder, "include", "controller", "c"), keep_path=True) - copy(self, "*.hpp", src=os.path.join(source_root, "include", "controller", "cpp"), dst=os.path.join(self.package_folder, "include", "controller", "cpp"), keep_path=True) - - lib_dst = os.path.join(self.package_folder, "lib") - bin_dst = os.path.join(self.package_folder, "bin") - lib_src_candidates = [ - os.path.join(build_root, "lib", "controller"), - os.path.join(source_root, "lib", "controller"), - ] - for lib_src in lib_src_candidates: - copy(self, "*.so*", src=lib_src, dst=lib_dst, keep_path=False) - copy(self, "*.dylib*", src=lib_src, dst=lib_dst, keep_path=False) - copy(self, "*.a", src=lib_src, dst=lib_dst, keep_path=False) - copy(self, "*.dll", src=lib_src, dst=bin_dst, keep_path=False) - copy(self, "*.lib", src=lib_src, dst=lib_dst, keep_path=False) - - if self.settings.os == "Linux": - patchelf = shutil.which("patchelf") - if patchelf: - package_lib = os.path.join(self.package_folder, "lib") - for soname in ("libController.so", "libCppController.so"): - lib_path = os.path.join(package_lib, soname) - if os.path.exists(lib_path): - self.run(f'"{patchelf}" --set-soname "{soname}" "{lib_path}"') + cmake = CMake(self) + cmake.install() + copy(self, "LICENSE", src=os.path.join(self.source_folder, self._source_subfolder), dst=os.path.join(self.package_folder, "licenses")) def package_info(self): self.cpp_info.set_property("cmake_file_name", "webots-controller") - if self._is_system_package: - webots_home = self._system_webots_home - controller_lib_dir = os.path.join(webots_home, "lib", "controller") - controller_include_dir = os.path.join(webots_home, "include", "controller", "c") - cpp_controller_include_dir = os.path.join(webots_home, "include", "controller", "cpp") - extra_libdirs = [] - extra_bindirs = [] - self.cpp_info.bindirs = [] - self.cpp_info.includedirs = [] - self.cpp_info.libdirs = [] + controller = self.cpp_info.components["Controller"] + controller.libs = ["webots_controller"] + controller.includedirs = ["include", "include/controller/c"] + controller.set_property("cmake_target_name", "webots-controller::Controller") - self.buildenv_info.define_path("WEBOTS_HOME", webots_home) - self.runenv_info.define_path("WEBOTS_HOME", webots_home) - - if self.settings.os == "Windows": - mingw_bin_dir = os.path.join(webots_home, "msys64", "mingw64", "bin") - extra_libdirs.append(mingw_bin_dir) - extra_bindirs.append(mingw_bin_dir) - elif self.settings.os == "Linux": - extra_libdirs.append(os.path.join(webots_home, "lib")) - - self.cpp_info.components["controller"].includedirs = [controller_include_dir] - self.cpp_info.components["controller"].libdirs = [controller_lib_dir, *extra_libdirs] - if self.settings.os == "Windows": - self.cpp_info.components["controller"].bindirs = [controller_lib_dir, *extra_bindirs] - - self.cpp_info.components["cpp_controller"].includedirs = [ - cpp_controller_include_dir, - controller_include_dir, - ] - self.cpp_info.components["cpp_controller"].libdirs = [controller_lib_dir, *extra_libdirs] - if self.settings.os == "Windows": - self.cpp_info.components["cpp_controller"].bindirs = [controller_lib_dir, *extra_bindirs] - else: - self.cpp_info.components["controller"].includedirs = ["include/controller/c"] - self.cpp_info.components["cpp_controller"].includedirs = ["include/controller/cpp", "include/controller/c"] - - self.cpp_info.components["controller"].libs = ["Controller"] - self.cpp_info.components["controller"].set_property("cmake_target_name", "Webots::Controller") - self.cpp_info.components["cpp_controller"].libs = ["CppController"] - self.cpp_info.components["cpp_controller"].set_property("cmake_target_name", "Webots::CppController") - self.cpp_info.components["cpp_controller"].requires = ["controller"] + cpp_controller = self.cpp_info.components["CppController"] + cpp_controller.libs = ["webots_cpp_controller"] + cpp_controller.includedirs = ["include", "include/controller/cpp", "include/controller/c"] + cpp_controller.requires = ["Controller"] + cpp_controller.set_property("cmake_target_name", "webots-controller::CppController") if self.settings.os == "Linux": - self.cpp_info.components["controller"].system_libs = ["m", "pthread", "dl", "rt"] + controller.system_libs = ["m", "pthread", "rt"] + if not bool(self.options.no_plugins): + controller.system_libs.append("dl") diff --git a/recipes/webots-controller/all/patches/msvc-controller-c.patch b/recipes/webots-controller/all/patches/msvc-controller-c.patch new file mode 100644 index 0000000..9f73128 --- /dev/null +++ b/recipes/webots-controller/all/patches/msvc-controller-c.patch @@ -0,0 +1,28 @@ +diff --git a/motion.c b/motion.c +index 02c5e62..3de3b2f 100644 +--- a/motion.c ++++ b/motion.c +@@ -45,7 +45,7 @@ typedef struct WbMotionStructPrivate { + extern void wb_motor_set_position_no_mutex(WbDeviceTag, double); + + static const int UNDEFINED_TIME = -1; +-static const int MAX_LINE = 4096; ++#define MAX_LINE 4096 + static const double UNDEFINED_POSITION = -9999999.9; + static WbMotionRef head = NULL; + static const char *HEADER = "#WEBOTS_MOTION"; +diff --git a/robot.c b/robot.c +index 6bb2b81..4d4cb6b 100644 +--- a/robot.c ++++ b/robot.c +@@ -65,6 +65,10 @@ + #include "supervisor_private.h" + #include "tcp_client.h" + ++#ifdef _WIN32 ++#undef wb_robot_init ++#endif ++ + #ifdef _WIN32 + #include // GetCommandLine + #else \ No newline at end of file diff --git a/recipes/webots-controller/all/src/controller/CMakeLists.txt b/recipes/webots-controller/all/src/controller/CMakeLists.txt new file mode 100644 index 0000000..9599c97 --- /dev/null +++ b/recipes/webots-controller/all/src/controller/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(c) +add_subdirectory(cpp) \ No newline at end of file diff --git a/recipes/webots-controller/all/src/controller/c/CMakeLists.txt b/recipes/webots-controller/all/src/controller/c/CMakeLists.txt new file mode 100644 index 0000000..813f20a --- /dev/null +++ b/recipes/webots-controller/all/src/controller/c/CMakeLists.txt @@ -0,0 +1,167 @@ +set(WEBOTS_HEADERS_ROOT "${WEBOTS_REPO_SOURCE_DIR}/include/controller/c") +set(WEBOTS_INCLUDE_ROOT "${WEBOTS_REPO_SOURCE_DIR}/include") +set(WEBOTS_STB_DIR "${WEBOTS_REPO_SOURCE_DIR}/src/stb") +set(WEBOTS_CONTROLLER_C_SOURCE_DIR "${WEBOTS_REPO_SOURCE_DIR}/src/controller/c") + +file(GLOB_RECURSE WEBOTS_CONTROLLER_C_PUBLIC_HEADERS CONFIGURE_DEPENDS + "${WEBOTS_HEADERS_ROOT}/webots/*.h") +file(GLOB_RECURSE WEBOTS_CONTROLLER_PLUGIN_PUBLIC_HEADERS CONFIGURE_DEPENDS + "${WEBOTS_INCLUDE_ROOT}/plugins/*.h") + +set(WEBOTS_CONTROLLER_C_CORE_SOURCES + abstract_camera.c + accelerometer.c + altimeter.c + ansi_codes.c + base64.c + brake.c + camera.c + compass.c + connector.c + console.c + default_robot_window.c + device.c + display.c + distance_sensor.c + emitter.c + file.c + g_image.c + g_pipe.c + gps.c + gyro.c + image.c + inertial_unit.c + joystick.c + keyboard.c + led.c + lidar.c + light_sensor.c + microphone.c + motion.c + motor.c + mouse.c + node.c + pen.c + percent.c + position_sensor.c + radar.c + range_finder.c + receiver.c + request.c + robot.c + scheduler.c + sha1.c + skin.c + speaker.c + string.c + supervisor.c + system.c + tcp_client.c + touch_sensor.c + vacuum_gripper.c) + +set(WEBOTS_CONTROLLER_C_PLUGIN_SOURCES dynamic_library.c html_robot_window.c + radio.c remote_control.c robot_window.c) + +set(WEBOTS_CONTROLLER_C_SOURCES ${WEBOTS_CONTROLLER_C_CORE_SOURCES}) +list(APPEND WEBOTS_CONTROLLER_C_SOURCES ${WEBOTS_CONTROLLER_C_PLUGIN_SOURCES}) + +list(TRANSFORM WEBOTS_CONTROLLER_C_SOURCES PREPEND "${WEBOTS_CONTROLLER_C_SOURCE_DIR}/") + +if(MSVC) + find_package(Git REQUIRED) + set(WEBOTS_CONTROLLER_MSVC_SOURCE_DIR + "${CMAKE_CURRENT_BINARY_DIR}/msvc-patched-source") + file(REMOVE_RECURSE "${WEBOTS_CONTROLLER_MSVC_SOURCE_DIR}") + file(COPY "${WEBOTS_CONTROLLER_C_SOURCE_DIR}/" + DESTINATION "${WEBOTS_CONTROLLER_MSVC_SOURCE_DIR}") + file(RELATIVE_PATH WEBOTS_CONTROLLER_MSVC_SOURCE_RELATIVE_DIR + "${CMAKE_SOURCE_DIR}" "${WEBOTS_CONTROLLER_MSVC_SOURCE_DIR}") + execute_process( + COMMAND + "${GIT_EXECUTABLE}" apply + "--directory=${WEBOTS_CONTROLLER_MSVC_SOURCE_RELATIVE_DIR}" + "${CMAKE_SOURCE_DIR}/patches/msvc-controller-c.patch" + WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" + RESULT_VARIABLE WEBOTS_CONTROLLER_MSVC_PATCH_RESULT) + if(NOT WEBOTS_CONTROLLER_MSVC_PATCH_RESULT EQUAL 0) + message(FATAL_ERROR "Failed to apply MSVC controller C source patch") + endif() + + list(TRANSFORM WEBOTS_CONTROLLER_C_SOURCES + REPLACE "^${WEBOTS_CONTROLLER_C_SOURCE_DIR}" "${WEBOTS_CONTROLLER_MSVC_SOURCE_DIR}") +endif() + +add_library(webots_controller ${WEBOTS_CONTROLLER_C_SOURCES}) +add_library(webots-controller::webots_controller ALIAS webots_controller) +add_library(webots-controller::Controller ALIAS webots_controller) + +target_sources( + webots_controller + PUBLIC FILE_SET + webots_c_headers + TYPE + HEADERS + BASE_DIRS + "${WEBOTS_HEADERS_ROOT}" + FILES + ${WEBOTS_CONTROLLER_C_PUBLIC_HEADERS} + FILE_SET + webots_plugin_headers + TYPE + HEADERS + BASE_DIRS + "${WEBOTS_INCLUDE_ROOT}" + FILES + ${WEBOTS_CONTROLLER_PLUGIN_PUBLIC_HEADERS}) + +if(WIN32) + set_target_properties(webots_controller PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS + ON) + target_link_libraries(webots_controller PRIVATE ws2_32) +endif() + +if(MSVC) + target_sources(webots_controller + PRIVATE "${libunistd_SOURCE_DIR}/unistd/clock_gettime.cpp") + target_link_libraries(webots_controller + PRIVATE webots_controller_msvc_unix_headers) + target_compile_definitions(webots_controller PRIVATE _USE_MATH_DEFINES + O_TEXT=_O_TEXT) + target_compile_options(webots_controller PRIVATE /utf-8) +endif() + +target_include_directories( + webots_controller + PUBLIC $ + $ $ + $ + PRIVATE ${WEBOTS_STB_DIR} ${WEBOTS_CONTROLLER_C_SOURCE_DIR} + ${WEBOTS_CONTROLLER_MSVC_SOURCE_DIR}) + +target_compile_definitions( + webots_controller PRIVATE LIBCONTROLLER_VERSION="${LIBCONTROLLER_VERSION}") +if(WEBOTS_CONTROLLER_NO_PLUGINS) + target_compile_definitions(webots_controller + PUBLIC WEBOTS_CONTROLLER_NO_PLUGINS=1) +endif() + +if(UNIX) + target_link_libraries(webots_controller PRIVATE m) + if(NOT APPLE) + target_link_libraries(webots_controller PRIVATE pthread rt) + if(NOT WEBOTS_CONTROLLER_NO_PLUGINS) + target_link_libraries(webots_controller PRIVATE dl) + endif() + endif() +endif() + +install( + TARGETS webots_controller + RUNTIME DESTINATION "bin" + LIBRARY DESTINATION "lib" + ARCHIVE DESTINATION "lib" + FILE_SET webots_c_headers + DESTINATION "include/controller/c" + FILE_SET webots_plugin_headers + DESTINATION "include") diff --git a/recipes/webots-controller/all/src/controller/cpp/CMakeLists.txt b/recipes/webots-controller/all/src/controller/cpp/CMakeLists.txt new file mode 100644 index 0000000..75b2ee4 --- /dev/null +++ b/recipes/webots-controller/all/src/controller/cpp/CMakeLists.txt @@ -0,0 +1,80 @@ +set(WEBOTS_CONTROLLER_CPP_SOURCE_DIR "${WEBOTS_REPO_SOURCE_DIR}/src/controller/cpp") + +set(WEBOTS_CONTROLLER_CPP_SOURCES + Accelerometer.cpp + Altimeter.cpp + Brake.cpp + Camera.cpp + Compass.cpp + Connector.cpp + Device.cpp + Display.cpp + DistanceSensor.cpp + Emitter.cpp + Field.cpp + GPS.cpp + Gyro.cpp + InertialUnit.cpp + Joystick.cpp + Keyboard.cpp + LED.cpp + Lidar.cpp + LightSensor.cpp + Motion.cpp + Motor.cpp + Mouse.cpp + Node.cpp + Pen.cpp + PositionSensor.cpp + Proto.cpp + Radar.cpp + RangeFinder.cpp + Receiver.cpp + Robot.cpp + Skin.cpp + Speaker.cpp + Supervisor.cpp + TouchSensor.cpp + VacuumGripper.cpp) + +list(TRANSFORM WEBOTS_CONTROLLER_CPP_SOURCES PREPEND "${WEBOTS_CONTROLLER_CPP_SOURCE_DIR}/") + +file(GLOB_RECURSE WEBOTS_CONTROLLER_CPP_PUBLIC_HEADERS CONFIGURE_DEPENDS + "${WEBOTS_REPO_SOURCE_DIR}/include/controller/cpp/webots/*.hpp") + +add_library(webots_cpp_controller ${WEBOTS_CONTROLLER_CPP_SOURCES}) +add_library(webots-controller::webots_cpp_controller ALIAS + webots_cpp_controller) +add_library(webots-controller::CppController ALIAS webots_cpp_controller) +add_library(webots-controller::webots-controller ALIAS webots_cpp_controller) + +target_sources( + webots_cpp_controller + PUBLIC FILE_SET + webots_cpp_headers + TYPE + HEADERS + BASE_DIRS + "${WEBOTS_REPO_SOURCE_DIR}/include/controller/cpp" + FILES + ${WEBOTS_CONTROLLER_CPP_PUBLIC_HEADERS}) + +if(WIN32) + set_target_properties(webots_cpp_controller + PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON) +endif() + +target_include_directories( + webots_cpp_controller + PUBLIC $ + $) + +target_link_libraries(webots_cpp_controller PUBLIC webots_controller) + +install( + TARGETS webots_cpp_controller + RUNTIME DESTINATION "bin" + LIBRARY DESTINATION "lib" + ARCHIVE DESTINATION "lib" + FILE_SET webots_cpp_headers + DESTINATION "include/controller/cpp") \ No newline at end of file diff --git a/recipes/webots-controller/all/test_package/.gitignore b/recipes/webots-controller/all/test_package/.gitignore new file mode 100644 index 0000000..6142d2f --- /dev/null +++ b/recipes/webots-controller/all/test_package/.gitignore @@ -0,0 +1,11 @@ +# Ignore everything +* +# But do not ignore files with extensions (like .c, .py, .md) +!*.* +# Do not ignore directories +!*/ +*.dll +*.exe +build/ +*.pyc +*.json diff --git a/recipes/webots-controller/all/test_package/CMakeLists.txt b/recipes/webots-controller/all/test_package/CMakeLists.txt index ddbb225..fb08fa4 100644 --- a/recipes/webots-controller/all/test_package/CMakeLists.txt +++ b/recipes/webots-controller/all/test_package/CMakeLists.txt @@ -1,46 +1,58 @@ -cmake_minimum_required(VERSION 3.15) -project(test_package CXX) +cmake_minimum_required(VERSION 3.23) +project(test_package C CXX) find_package(webots-controller REQUIRED CONFIG) add_executable(test_package test_package.cpp) -target_link_libraries(test_package PRIVATE Webots::CppController) +target_link_libraries(test_package PRIVATE webots-controller::CppController) + +add_executable(test_package_c test_package.c) +target_link_libraries(test_package_c PRIVATE webots-controller::Controller) if(POLICY CMP0207) cmake_policy(SET CMP0207 NEW) endif() -# Webots controllers rely on seeing shared deps in the same directory as the executable. -install(TARGETS test_package - RUNTIME_DEPENDENCY_SET test_package_runtime_deps - RUNTIME DESTINATION bin - LIBRARY DESTINATION lib - ARCHIVE DESTINATION lib) +set(_extra_runtime_dirs) +if(CMAKE_PREFIX_PATH) + foreach(prefix IN LISTS CMAKE_PREFIX_PATH) + list(APPEND _extra_runtime_dirs "${prefix}/lib") + endforeach() +endif() +if(WIN32 AND DEFINED ENV{MINGW_HOME}) + list(APPEND _extra_runtime_dirs "$ENV{MINGW_HOME}/bin") +endif() + +# Webots controllers rely on seeing shared deps in the same directory as the +# executable. Destination therefore change to "." +install( + TARGETS test_package test_package_c RUNTIME_DEPENDENCY_SET + test_package_runtime_deps + RUNTIME DESTINATION . + LIBRARY DESTINATION . + ARCHIVE DESTINATION .) if(WIN32) - # for single-config generators. - set(_runtime_dependency_directories) - string(TOUPPER "${CMAKE_BUILD_TYPE}" _conan_build_type) - set(_webots_bin_dirs_var "webots-controller_BIN_DIRS_${_conan_build_type}") - if(DEFINED ${_webots_bin_dirs_var}) - list(APPEND _runtime_dependency_directories ${${_webots_bin_dirs_var}}) - endif() - if(DEFINED ENV{MINGW_HOME}) - list(APPEND _runtime_dependency_directories "$ENV{MINGW_HOME}/bin") - endif() - install( - RUNTIME_DEPENDENCY_SET test_package_runtime_deps - DIRECTORIES ${_runtime_dependency_directories} - PRE_EXCLUDE_REGEXES - "^api-ms-win-.*" - "^ext-ms-.*" - POST_EXCLUDE_REGEXES - ".*/system32/.*" - ".*/Windows/.*" - ".*/SysWOW64/.*" - DESTINATION bin) + set(_runtime_dep_pre_excludes "^api-ms-win-.*" "^ext-ms-.*") + set(_runtime_dep_post_excludes ".*/system32/.*" ".*/Windows/.*" + ".*/SysWOW64/.*") else() - install( - RUNTIME_DEPENDENCY_SET test_package_runtime_deps - DESTINATION bin) + set(_runtime_dep_pre_excludes + [[libc\.so\..*]] [[libgcc_s\.so\..*]] [[libm\.so\..*]] + [[libstdc\+\+\.so\..*]] [[ld-linux-x86-64\.so\..*]]) + set(_runtime_dep_post_excludes [[^/lib.*]] [[^/usr/lib.*]]) endif() + +install( + RUNTIME_DEPENDENCY_SET + test_package_runtime_deps + PRE_EXCLUDE_REGEXES + ${_runtime_dep_pre_excludes} + POST_EXCLUDE_REGEXES + ${_runtime_dep_post_excludes} + DIRECTORIES + ${CONAN_RUNTIME_LIB_DIRS} + ${CMAKE_SYSROOT}/lib + ${_extra_runtime_dirs} + DESTINATION + .) diff --git a/recipes/webots-controller/all/test_package/conanfile.py b/recipes/webots-controller/all/test_package/conanfile.py index dcae494..6bbc624 100644 --- a/recipes/webots-controller/all/test_package/conanfile.py +++ b/recipes/webots-controller/all/test_package/conanfile.py @@ -21,8 +21,11 @@ class TestPackageConan(ConanFile): cmake.build() def test(self): + # check if RUNTIME_DEPENDENCY_SET work cmake = CMake(self) cmake.install() if can_run(self): - exe = os.path.join(self.source_folder, "bin", "test_package") + exe = os.path.join(self.source_folder, "test_package") + self.run(exe, env="conanrun") + exe = os.path.join(self.source_folder, "test_package_c") self.run(exe, env="conanrun") diff --git a/recipes/webots-controller/all/test_package/test_package.c b/recipes/webots-controller/all/test_package/test_package.c new file mode 100644 index 0000000..29854f5 --- /dev/null +++ b/recipes/webots-controller/all/test_package/test_package.c @@ -0,0 +1,7 @@ +#include + +int main(void) { + double (*c_symbol)(void) = &wb_robot_get_time; + (void)c_symbol; + return 0; +} diff --git a/recipes/webots-controller/all/test_package/test_package.cpp b/recipes/webots-controller/all/test_package/test_package.cpp index d044c5a..b9cc1cb 100644 --- a/recipes/webots-controller/all/test_package/test_package.cpp +++ b/recipes/webots-controller/all/test_package/test_package.cpp @@ -9,4 +9,4 @@ int main() { (void)c_symbol; std::cout << "Success!" << std::endl; return 0; -} +} \ No newline at end of file