Compare commits

...

15 Commits

Author SHA1 Message Date
existedinnettw fdeb035971
Merge pull request #5 from existedinnettw/fix/large_LKB_source 2026-05-23 00:45:18 +08:00
insleker 217b5628e0 chore: `linux-kbuild-tree` prevent upload binary 2026-05-23 00:40:07 +08:00
insleker 94811349b0 test: add linux kbuild module test package 2026-05-22 23:27:45 +08:00
existedinnettw fcc49db4a0
Merge pull request #4 from existedinnettw/feat/linux_kbuild_tree
feat/linux kbuild tree
2026-05-22 21:51:24 +08:00
insleker 8e50a5f134 fix: relax old linux objtool warning 2026-05-22 21:46:10 +08:00
insleker 18fcb473e8 ci: install libelf headers for linux kbuild 2026-05-22 21:39:24 +08:00
insleker 0245c15855 feat: linux-kbuild-tree download source from CDN rather than git now
fix: linux-kbuild-tree path move in `all` subfolder
2026-05-22 21:29:44 +08:00
insleker 8d6a663379 fix: remove all `user.kernel:*` config 2026-05-22 19:40:52 +08:00
insleker e231e9b967 feat: basic linux kbuild tree recipe implement 2026-05-22 18:58:53 +08:00
insleker 9409f4be3e feat: linux ktree build recipe prepare 2026-05-22 18:25:49 +08:00
existedinnettw 82b5994be3
Merge pull request #3 from existedinnettw/fix/webots
fix: update CMakeLists to use existing source paths for backward version support
2026-05-22 11:58:37 +08:00
insleker 81417b57d6 fix: update CMakeLists to use existing source paths for backward version support 2026-05-22 11:55:44 +08:00
insleker ded13c428f feat: reimplement webots recipe through cmake 2026-05-22 11:42:25 +08:00
insleker bf33aad15e feat: enhance `webots-controller` Windows build support with MinGW and MSYS2 requirements 2026-05-22 11:41:42 +08:00
existedinnettw 52af2e1f21
Merge pull request #2 from existedinnettw/feat/webots
feat: add source-built webots-controller Conan recipe
2026-05-18 23:31:53 +08:00
21 changed files with 917 additions and 57 deletions

View File

@ -64,7 +64,7 @@ jobs:
- name: Install host build tools
uses: awalsh128/cache-apt-pkgs-action@latest
with:
packages: build-essential gcc-14 g++-14 cmake ninja-build graphviz crudini
packages: build-essential gcc-14 g++-14 cmake ninja-build graphviz crudini libelf-dev
version: 1.0
execute_install_scripts: true

View File

@ -61,7 +61,7 @@ jobs:
- name: Install host build tools
uses: awalsh128/cache-apt-pkgs-action@latest
with:
packages: build-essential gcc-14 g++-14 cmake ninja-build graphviz crudini
packages: build-essential gcc-14 g++-14 cmake ninja-build graphviz crudini libelf-dev
version: 1.0
execute_install_scripts: true

1
.gitignore vendored
View File

@ -150,3 +150,4 @@ Session.vim
!/.vscode
/.env*
!/.env*.example
**/test_package/bin

View File

@ -0,0 +1,3 @@
/build/
/linux/
*.txt

View File

@ -0,0 +1,21 @@
sources:
"7.0.0":
"6.17.0":
"6.8.0":
"6.5.9":
url: "https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.5.9.tar.gz"
sha256: "3c269612220746ba87dc9063bec2afcb2df04b4fdaa9f7e1f67751284966cf37"
"5.19.9":
url: "https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.19.9.tar.gz"
sha256: "1f01b0ee846547737793103f23cc46a382e4694e7728e8cb8c48ea51a2bb7a8d"
"5.19.0":
"5.15.0":
"5.15.128":
url: "https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.128.tar.gz"
sha256: "4ce1331bb3877bc92581ec5faff76d63d19e7b1fa174acdda1f5cba4a8f2abf7"
"5.14.9":
url: "https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.14.9.tar.gz"
sha256: "b5cd37e6d193c8f6a98e41ce6f9d97260d178e0a3cfc43d7ff684f67c6c25f29"
"5.13.9":
url: "https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.13.9.tar.gz"
sha256: "cc82e4461d5f759c820b74079164450d1f1ad09def9f3356a58814abd0268787"

View File

@ -0,0 +1,318 @@
import os
import re
import shlex
import subprocess
from conan import ConanFile
from conan.errors import ConanInvalidConfiguration
from conan.tools.build import cross_building
from conan.tools.files import copy, get, save
class LinuxKbuildTreeConan(ConanFile):
name = "linux-kbuild-tree"
package_type = "unknown"
upload_policy = "skip"
build_policy = "missing"
settings = "os", "arch", "compiler", "build_type"
options = {
"url": ["ANY"],
"defconfig": ["ANY"],
"make_modules": [True, False],
}
default_options = {
"url": "",
"defconfig": "defconfig",
"make_modules": False,
}
exports_sources = "configs/*"
def layout(self):
self.folders.build = "build"
self.folders.generators = os.path.join(self.folders.build, "generators")
def package_id(self):
c_compiler = self._compiler_executable("c")
if c_compiler is not None:
self.info.conf.define("tools.build:compiler_executables", {"c": str(c_compiler)})
def validate(self):
if self.settings.os != "Linux":
raise ConanInvalidConfiguration("linux-kbuild-tree requires a Linux target")
if not self.version:
raise ConanInvalidConfiguration(
"linux-kbuild-tree requires a package version, e.g. "
"conan create recipes/linux-kbuild-tree --version=6.5.9"
)
self._kernel_arch
if cross_building(self) and self._cross_compile is None:
raise ConanInvalidConfiguration(
"Cross-building a kernel tree requires "
"tools.build:compiler_executables with a GCC-like C compiler"
)
def source(self):
source_url, sha256 = self._linux_source()
get(
self,
source_url,
sha256=sha256,
destination=os.path.join(self.source_folder, "linux"),
strip_root=True,
)
def build(self):
linux = os.path.join(self.source_folder, "linux")
arch = self._kernel_arch
cross_compile = self._cross_compile or ""
jobs = self.conf.get("tools.build:jobs", default=None)
make_jobs = f" -j{jobs}" if jobs else ""
make_base = (
f"make --no-print-directory -C {shlex.quote(linux)}{make_jobs} "
f"ARCH={shlex.quote(str(arch))} "
f"CROSS_COMPILE={shlex.quote(str(cross_compile))} "
f"{self._host_warning_flags_args}"
)
self._install_config(linux, make_base)
self.run(f"{make_base} olddefconfig prepare modules_prepare")
if self.options.make_modules:
# Required when CONFIG_MODVERSIONS=y and the target kernel exports
# symbols that are not available from a vendor-provided Module.symvers.
self.run(f"{make_base} modules")
kernelrelease = self._capture(f"{make_base} kernelrelease")
compiler = self._capture(f"{self._cross_gcc} --version").splitlines()[0]
source_url, sha256 = self._linux_source()
save(
self,
os.path.join(self.build_folder, "kbuild-tree-info.txt"),
"\n".join(
[
f"url={source_url}",
f"ref={self._linux_ref_from_version()}",
f"sha256={sha256 or ''}",
f"kernelrelease={kernelrelease}",
f"arch={arch}",
f"cross_compile={cross_compile}",
f"compiler={compiler}",
f"make_modules={self.options.make_modules}",
"",
]
),
)
def package(self):
linux = os.path.join(self.source_folder, "linux")
# External module builds need source files, generated headers, scripts,
# .config, and often Module.symvers. Packaging the whole prepared tree is
# intentionally conservative and can be optimized after the flow is stable.
copy(self, "*", src=linux, dst=self.package_folder, excludes=(".git", ".git/*"))
copy(self, ".config", src=linux, dst=self.package_folder, keep_path=False)
copy(
self,
"kbuild-tree-info.txt",
src=self.build_folder,
dst=self.package_folder,
keep_path=False,
)
def package_info(self):
self.cpp_info.includedirs = []
self.cpp_info.libdirs = []
self.cpp_info.bindirs = []
def _linux_ref_from_version(self):
# Upstream Linux tags base releases as v6.8, while the kernel release
# and Conan package version are commonly represented as 6.8.0.
version_parts = str(self.version).split(".")
if len(version_parts) == 3 and version_parts[2] == "0":
return f"v{version_parts[0]}.{version_parts[1]}"
return f"v{self.version}"
def _linux_archive_version(self):
version_parts = str(self.version).split(".")
if len(version_parts) == 3 and version_parts[2] == "0":
return f"{version_parts[0]}.{version_parts[1]}"
return str(self.version)
def _linux_source(self):
source = self.conan_data.get("sources", {}).get(str(self.version)) or {}
source_url = source.get("url") or self._kernel_cdn_url()
return source_url, source.get("sha256")
def _kernel_cdn_url(self):
archive_version = self._linux_archive_version()
major = archive_version.split(".", 1)[0]
return (
f"https://cdn.kernel.org/pub/linux/kernel/v{major}.x/"
f"linux-{archive_version}.tar.gz"
)
@property
def _host_warning_flags_args(self):
hostcflags = []
if self._version_lt("5.16.0") and self._host_compiler_accepts(
"-Wno-error=use-after-free"
):
# Older objtool builds use -Werror and trip GCC 14's realloc(ptr)
# use-after-free diagnostic in tools/lib/subcmd/subcmd-util.h.
hostcflags.append("-Wno-error=use-after-free")
if not hostcflags:
return ""
flags = shlex.quote(" ".join(hostcflags))
return f"HOSTCFLAGS={flags} EXTRA_CFLAGS={flags}"
def _host_compiler_accepts(self, flag):
try:
subprocess.run(
[
"gcc",
flag,
"-x",
"c",
"-c",
"-o",
os.devnull,
"-",
],
input="int main(void) { return 0; }\n",
text=True,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
check=True,
)
except (OSError, subprocess.CalledProcessError):
return False
return True
def _version_lt(self, other):
return self._version_tuple(str(self.version)) < self._version_tuple(other)
def _version_tuple(self, version):
return tuple(int(part) for part in version.split("."))
@property
def _kernel_arch(self):
arch = str(self.settings.arch)
arch_map = {
"x86": "x86",
"x86_64": "x86",
"armv5el": "arm",
"armv5hf": "arm",
"armv6": "arm",
"armv7": "arm",
"armv7hf": "arm",
"armv8": "arm64",
"armv8_32": "arm",
"armv8.3": "arm64",
"armv8.5": "arm64",
"armv8.6": "arm64",
"armv8.7": "arm64",
"armv8.8": "arm64",
"armv8.9": "arm64",
"ppc32": "powerpc",
"ppc64": "powerpc",
"ppc64le": "powerpc",
"riscv32": "riscv",
"riscv64": "riscv",
"s390": "s390",
"s390x": "s390",
"sparc": "sparc",
"sparcv9": "sparc",
"mips": "mips",
"mips64": "mips",
}
if arch not in arch_map:
raise ConanInvalidConfiguration(
f"Cannot derive Linux ARCH from settings.arch={arch!r}; "
"add this Conan architecture to the recipe arch map"
)
return arch_map[arch]
@property
def _cross_compile(self):
if not cross_building(self):
return ""
c_compiler = self._compiler_executable("c")
if not c_compiler:
return None
return self._cross_compile_from_executable(c_compiler)
def _cross_compile_from_compiler_executables(self):
c_compiler = self._compiler_executable("c")
if not c_compiler:
return None
return self._cross_compile_from_executable(c_compiler)
def _cross_compile_from_executable(self, c_compiler):
executable = os.path.basename(c_compiler)
match = re.match(r"^(.+?)(?:gcc|cc)(?:-\d+(?:\.\d+)*)?$", executable)
if not match:
return None
return match.group(1)
@property
def _cross_gcc(self):
cross_compile = self._cross_compile or ""
return shlex.quote(f"{cross_compile}gcc")
def _compiler_executable(self, language):
executables = self.conf.get("tools.build:compiler_executables", default={})
if not executables:
return None
if hasattr(executables, "get"):
return executables.get(language)
return None
def _install_config(self, linux, make_base):
defconfig = str(self.options.defconfig)
if not defconfig:
if not os.path.exists(os.path.join(linux, ".config")):
raise ConanInvalidConfiguration(
"Missing kernel config: set linux-kbuild-tree/*:defconfig "
"or provide a source tree with .config"
)
return
exported_config = os.path.join(self.source_folder, "configs", defconfig)
if os.path.isfile(exported_config):
copy(
self,
defconfig,
src=os.path.dirname(exported_config),
dst=linux,
keep_path=False,
)
os.replace(os.path.join(linux, defconfig), os.path.join(linux, ".config"))
return
self.run(f"{make_base} {shlex.quote(defconfig)}")
def _capture(self, command):
output = subprocess.check_output(
command,
shell=True,
stderr=subprocess.STDOUT,
text=True,
).strip()
if not output:
raise ConanInvalidConfiguration(f"Command produced no output: {command}")
return output

View File

@ -0,0 +1 @@
obj-m += test_kbuild_module.o

View File

@ -0,0 +1,61 @@
import os
import shlex
from conan import ConanFile
from conan.errors import ConanException
from conan.tools.files import copy
class TestPackageConan(ConanFile):
test_type = "explicit"
settings = "os", "compiler", "build_type", "arch"
exports_sources = "Makefile", "test_kbuild_module.c"
def requirements(self):
self.requires(self.tested_reference_str)
def layout(self):
self.folders.build = "build"
self.folders.generators = os.path.join(self.folders.build, "generators")
def build(self):
kernel_tree = self.dependencies["linux-kbuild-tree"].package_folder
module_dir = os.path.join(self.build_folder, "module")
copy(self, "*", src=self.source_folder, dst=module_dir)
tree_info = self._read_tree_info(kernel_tree)
arch = tree_info.get("arch", "")
cross_compile = tree_info.get("cross_compile", "")
jobs = self.conf.get("tools.build:jobs", default=None)
make_jobs = f" -j{jobs}" if jobs else ""
command = (
f"make --no-print-directory -C {shlex.quote(kernel_tree)}{make_jobs} "
f"M={shlex.quote(module_dir)} "
f"ARCH={shlex.quote(arch)} "
f"CROSS_COMPILE={shlex.quote(cross_compile)} "
"KBUILD_MODPOST_WARN=1 "
"modules"
)
self.run(command)
def test(self):
module_path = os.path.join(self.build_folder, "module", "test_kbuild_module.ko")
if not os.path.isfile(module_path):
raise ConanException(f"Kernel module was not built: {module_path}")
def _read_tree_info(self, kernel_tree):
path = os.path.join(kernel_tree, "kbuild-tree-info.txt")
if not os.path.isfile(path):
raise ConanException(f"Missing linux-kbuild-tree metadata: {path}")
result = {}
with open(path, encoding="utf-8") as tree_info:
for line in tree_info:
line = line.strip()
if not line or "=" not in line:
continue
key, value = line.split("=", 1)
result[key] = value
return result

View File

@ -0,0 +1,17 @@
#include <linux/init.h>
#include <linux/module.h>
static int __init test_kbuild_module_init(void)
{
return 0;
}
static void __exit test_kbuild_module_exit(void)
{
}
module_init(test_kbuild_module_init);
module_exit(test_kbuild_module_exit);
MODULE_DESCRIPTION("linux-kbuild-tree Conan test module");
MODULE_LICENSE("GPL");

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,8 +1,8 @@
from conan import ConanFile
from conan.errors import 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 conan.tools.microsoft import is_msvc
import os
@ -13,16 +13,28 @@ 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
@ -33,62 +45,69 @@ class WebotsControllerConan(ConanFile):
if self.settings.os == "Windows":
self.options.rm_safe("fPIC")
def validate(self):
if str(self.settings.compiler) == "msvc":
raise ConanInvalidConfiguration("Webots controller libraries are built with GCC/Clang toolchains, not MSVC")
def configure(self):
if self.options.get_safe("no_plugins", True):
return
if self.settings.os == "Windows" and is_msvc(self):
raise ValueError("Official Windows Webots is built with MinGW GCC, not MSVC.")
def source(self):
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 layout(self):
cmake_layout(self)
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):
build_env = Environment()
source_root = os.path.join(self.source_folder, self._source_subfolder)
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 release")
with chdir(self, os.path.join(source_root, "src", "controller", "cpp")):
self.run("make release")
cmake = CMake(self)
cmake.configure()
cmake.build()
def package(self):
source_root = os.path.join(self.source_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_src = os.path.join(source_root, "lib", "controller")
copy(self, "*.so*", src=lib_src, dst=os.path.join(self.package_folder, "lib"), keep_path=False)
copy(self, "*.dylib*", src=lib_src, dst=os.path.join(self.package_folder, "lib"), keep_path=False)
copy(self, "*.dll", src=lib_src, dst=os.path.join(self.package_folder, "bin"), keep_path=False)
copy(self, "*.lib", src=lib_src, dst=os.path.join(self.package_folder, "lib"), keep_path=False)
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")
self.cpp_info.components["controller"].libs = ["Controller"]
self.cpp_info.components["controller"].set_property("cmake_target_name", "Webots::Controller")
self.cpp_info.components["controller"].includedirs = ["include/controller/c"]
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.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"]
self.cpp_info.components["cpp_controller"].includedirs = ["include/controller/cpp", "include/controller/c"]
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,178 @@
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})
set(WEBOTS_CONTROLLER_C_SOURCE_PATHS)
foreach(_src IN LISTS WEBOTS_CONTROLLER_C_SOURCES)
set(_candidate "${WEBOTS_CONTROLLER_C_SOURCE_DIR}/${_src}")
if(EXISTS "${_candidate}")
list(APPEND WEBOTS_CONTROLLER_C_SOURCE_PATHS "${_candidate}")
endif()
endforeach()
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()
set(_msvc_source_paths)
foreach(_src IN LISTS WEBOTS_CONTROLLER_C_SOURCES)
set(_candidate "${WEBOTS_CONTROLLER_MSVC_SOURCE_DIR}/${_src}")
if(EXISTS "${_candidate}")
list(APPEND _msvc_source_paths "${_candidate}")
endif()
endforeach()
set(WEBOTS_CONTROLLER_C_SOURCE_PATHS ${_msvc_source_paths})
endif()
add_library(webots_controller ${WEBOTS_CONTROLLER_C_SOURCE_PATHS})
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,86 @@
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)
set(WEBOTS_CONTROLLER_CPP_SOURCE_PATHS)
foreach(_src IN LISTS WEBOTS_CONTROLLER_CPP_SOURCES)
set(_candidate "${WEBOTS_CONTROLLER_CPP_SOURCE_DIR}/${_src}")
if(EXISTS "${_candidate}")
list(APPEND WEBOTS_CONTROLLER_CPP_SOURCE_PATHS "${_candidate}")
endif()
endforeach()
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_SOURCE_PATHS})
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,7 +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()
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)
set(_runtime_dep_pre_excludes "^api-ms-win-.*" "^ext-ms-.*")
set(_runtime_dep_post_excludes ".*/system32/.*" ".*/Windows/.*"
".*/SysWOW64/.*")
else()
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,6 +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.cpp.build.bindirs[0], "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;
}

View File

@ -1,7 +1,12 @@
#include <webots/Robot.hpp>
#include <webots/robot.h>
#include <iostream>
int main() {
webots::Robot *robot = nullptr;
(void)robot;
auto cpp_symbol = &webots::Robot::getBasicTimeStep;
auto c_symbol = &wb_robot_get_time;
(void)cpp_symbol;
(void)c_symbol;
std::cout << "Success!" << std::endl;
return 0;
}
}