feat: reimplement webots recipe through cmake

This commit is contained in:
insleker 2026-05-22 11:40:07 +08:00
parent bf33aad15e
commit ded13c428f
12 changed files with 455 additions and 334 deletions

View File

@ -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 "$<TARGET_PROPERTY:dirent::dirent,INTERFACE_INCLUDE_DIRECTORIES>"
"${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)

View File

@ -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"

View File

@ -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:
# <WEBOTS_HOME>\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")
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")

View File

@ -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 <windows.h> // GetCommandLine
#else

View File

@ -0,0 +1,2 @@
add_subdirectory(c)
add_subdirectory(cpp)

View File

@ -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 $<BUILD_INTERFACE:${WEBOTS_INCLUDE_ROOT}>
$<BUILD_INTERFACE:${WEBOTS_HEADERS_ROOT}> $<INSTALL_INTERFACE:include>
$<INSTALL_INTERFACE:include/controller/c>
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")

View File

@ -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 $<BUILD_INTERFACE:${WEBOTS_REPO_SOURCE_DIR}/include/controller/cpp>
$<INSTALL_INTERFACE:include/controller/cpp>)
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")

View File

@ -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

View File

@ -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
.)

View File

@ -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")

View File

@ -0,0 +1,7 @@
#include <webots/robot.h>
int main(void) {
double (*c_symbol)(void) = &wb_robot_get_time;
(void)c_symbol;
return 0;
}