Initial commit
Initial commit for rules_libusb that provides a release version module
extension that simplifies an external dependency on libusb.
Change-Id: I4f61890b74f975e792a6b159fb8a6c8e953c14a5
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..d6631ca
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+bazel-*
+
+# Ignore until https://github.com/bazelbuild/bazel/issues/20369 is fixed.
+MODULE.bazel.lock
diff --git a/BUILD.bazel b/BUILD.bazel
new file mode 100644
index 0000000..a21c90e
--- /dev/null
+++ b/BUILD.bazel
@@ -0,0 +1,42 @@
+# Copyright 2024 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+package(default_visibility = ["//visibility:public"])
+
+cc_library(
+ name = "libusb_linux_config",
+ hdrs = ["linux/config.h"],
+)
+
+cc_library(
+ name = "default_libusb_config",
+ hdrs = ["include/config.h"],
+ includes = ["include"],
+ deps = select({
+ "@platforms//os:windows": ["@libusb_sources//:libusb_msvc_config"],
+ "@platforms//os:macos": ["@libusb_sources//:libusb_macos_config"],
+ "@platforms//os:linux": ["//:libusb_linux_config"],
+ "//conditions:default": [],
+ }),
+)
+
+alias(
+ name = "libusb",
+ actual = "@libusb_sources//:libusb",
+)
+
+alias(
+ name = "libusb_dynamic",
+ actual = "@libusb_sources//:libusb_dynamic",
+)
diff --git a/MODULE.bazel b/MODULE.bazel
new file mode 100644
index 0000000..7b3ca35
--- /dev/null
+++ b/MODULE.bazel
@@ -0,0 +1,14 @@
+module(
+ name = "rules_libusb",
+ version = "0.1.0-rc1",
+)
+
+bazel_dep(name = "platforms", version = "0.0.8")
+
+# If your project has versioning requirements for libusb, you'll need to specify
+# this in your project's MODULE.bazel.
+libusb = use_extension("@rules_libusb//:extensions.bzl", "libusb")
+libusb.source_release(min_version = "1.0.20-rc1")
+
+# rules_libusb actually needs to be able to reference @libusb_sources.
+use_repo(libusb, "libusb_sources")
diff --git a/README.md b/README.md
index c5ae3d3..7b54ce0 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,43 @@
-Pigweed Open Source Template Repository
-=======================================
+# rules_libusb
-This repository is a template that we will use when creating new open source
-repositories for Pigweed.
+A small wrapper Bazel module that builds libusb from sources. Supports Windows,
+macOS, and Linux.
+
+## Getting Started
+
+At this time, rules_libusb only supports bzlmod projects. Legacy `WORKSPACE`
+projects are not explicitly supported.
+
+### blzmod
+
+Add rules_libusb to your `MODULE.bazel` file:
+```
+bazel_dep(name = "rules_libusb", version="0.1.0-rc1")
+```
+
+Then add `@libusb//:libusb` in `dynamic_deps` to the tool that uses it:
+```
+cc_binary(
+ name = "my_tool",
+ srcs = ["main.cpp"],
+ deps = ["@rules_libusb//:libusb"],
+ dynamic_deps = ["@rules_libusb//:libusb_dynamic"],
+)
+```
+
+If you have explicit libusb versioning requirements, you may add them to your
+`MODULE.bazel`:
+```
+libusb = use_extension("@rules_libusb//:extensions.bzl", "libusb")
+libusb.source_release(min_version = "1.0.27")
+```
+
+Note: `source_release` constraints follow bzlmod behavior of minimal version
+selection.
+
+## Building this repo
+
+To build this repo, run:
+```
+bazel build //:libusb
+```
diff --git a/extensions.bzl b/extensions.bzl
new file mode 100644
index 0000000..d782758
--- /dev/null
+++ b/extensions.bzl
@@ -0,0 +1,98 @@
+load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
+
+# This just silences warnings about canonical reproducible forms.
+_KNOWN_RELEASE_HASHES = {
+ "1.0.20-rc1": "914c4c66f4d6582fb19afc92aa8274f11e0745a93a02593e3bcc2ee7c6048e23",
+ "1.0.20": "cb057190ba0a961768224e4dc6883104c6f945b2bf2ef90d7da39e7c1834f7ff",
+ "1.0.21": "7dce9cce9a81194b7065ee912bcd55eeffebab694ea403ffb91b67db66b1824b",
+ "1.0.22": "75aeb9d59a4fdb800d329a545c2e6799f732362193b465ea198f2aa275518157",
+ "1.0.23": "db11c06e958a82dac52cf3c65cb4dd2c3f339c8a988665110e0d24d19312ad8d",
+ "1.0.24": "7efd2685f7b327326dcfb85cee426d9b871fd70e22caa15bb68d595ce2a2b12a",
+ "1.0.25": "8a28ef197a797ebac2702f095e81975e2b02b2eeff2774fa909c78a74ef50849",
+ "1.0.26": "12ce7a61fc9854d1d2a1ffe095f7b5fac19ddba095c259e6067a46500381b5a5",
+ "1.0.27": "ffaa41d741a8a3bee244ac8e54a72ea05bf2879663c098c82fc5757853441575",
+}
+
+def _version_parts(ver_string):
+ """Converts version parts into a list of integers.
+
+ Every version integer list will be four parts to ensure that release
+ candidates are properly handled. Final releases are akin to a
+ release candidate 9999999999.
+ """
+ parts = ver_string.split(".")
+ if "-" in parts[-1]:
+ rc = parts[-1].split("-")
+ if len(rc) > 2:
+ fail("Unknown version string format:", ver_string)
+ parts[-1] = rc[0]
+ parts.append(rc[1].lower().lstrip("rc"))
+ else:
+ parts.append(9999999999)
+ return [int(p) for p in parts]
+
+def _version_less_than(a, b):
+ a_parts = _version_parts(a)
+ b_parts = _version_parts(b)
+ for i in range(len(a_parts)):
+ if a_parts[i] == b_parts[i]:
+ continue
+ return a_parts[i] < b_parts[i]
+
+ # Must be equal.
+ return False
+
+def _libusb_impl(module_ctx):
+ """ Fetches libusb sources as requested by Bazel modules.
+
+ Intended behavior:
+ The minimum supported version is selected, per usual bzlmod behavior.
+ """
+ root_module = True
+ min_versions = {}
+ max_versions = {}
+ for module in module_ctx.modules:
+ for release in module.tags.source_release:
+ if release.min_version:
+ if release.min_version not in min_versions:
+ min_versions[release.min_version] = []
+ min_versions[release.min_version].append(module.name)
+ if release.max_version:
+ if release.max_version not in max_versions:
+ max_versions[release.max_version] = []
+ max_versions[release.max_version].append(module.name)
+
+ version_to_use = None
+ for v, m in min_versions.items():
+ if version_to_use == None or _version_less_than(version_to_use, v):
+ version_to_use = v
+
+ for v, m in max_versions.items():
+ if _version_less_than(v, version_to_use):
+ fail("Minimum supported libusb version for", min_versions[version_to_use], "is", version_to_use, "but", max_versions[v], "require at most", v)
+
+ http_archive(
+ name = "libusb_sources",
+ urls = ["https://github.com/libusb/libusb/releases/download/v{}/libusb-{}.tar.bz2".format(version_to_use, version_to_use)],
+ build_file = "@rules_libusb//:libusb.BUILD",
+ strip_prefix = "libusb-" + version_to_use,
+ sha256 = _KNOWN_RELEASE_HASHES.get(version_to_use, None),
+ )
+
+libusb = module_extension(
+ doc = "Bzlmod extension for pulling in libusb sources.",
+ implementation = _libusb_impl,
+ tag_classes = {
+ "source_release": tag_class(
+ doc = "Controls the source code version to use when building libusb.",
+ attrs = {
+ "min_version": attr.string(
+ doc = "Minimum supported version of libusb required",
+ ),
+ "max_version": attr.string(
+ doc = "Maximum supported version of libusb required",
+ ),
+ },
+ ),
+ },
+)
diff --git a/include/config.h b/include/config.h
new file mode 100644
index 0000000..38b5f11
--- /dev/null
+++ b/include/config.h
@@ -0,0 +1,41 @@
+// Copyright 2024 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#if defined(_WIN32) | defined(_WIN64)
+
+#if defined(_MSC_VER)
+#include "msvc/config.h"
+#endif // defined(_MSC_VER )
+
+// TODO: MinGW-W64 and clang support.
+
+#define _RULES_LIBUSB_OS_SUPPORTED 1
+
+#elif defined(__linux__)
+
+#include "linux/config.h"
+
+#define _RULES_LIBUSB_OS_SUPPORTED 1
+
+#elif defined(__APPLE__)
+
+#include "Xcode/config.h"
+
+#define _RULES_LIBUSB_OS_SUPPORTED 1
+
+#endif // defined(_WIN32) | defined(_WIN64)
+
+#if !defined(_RULES_LIBUSB_OS_SUPPORTED) || !_RULES_LIBUSB_OS_SUPPORTED
+#error Unsupported configuration, either contribute to rules_libusb, or build your own `config.h`.
+#endif // !defined(_RULES_LIBUSB_OS_SUPPORTED)
diff --git a/libusb.BUILD b/libusb.BUILD
new file mode 100644
index 0000000..959275c
--- /dev/null
+++ b/libusb.BUILD
@@ -0,0 +1,154 @@
+# Copyright 2024 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+cc_library(
+ name = "libusb",
+ srcs = glob(["libusb/*.c"]),
+ visibility = ["//visibility:public"],
+ deps = select({
+ "@platforms//os:linux": [":libusb_linux"],
+ "@platforms//os:macos": [":libusb_macos"],
+ "@platforms//os:windows": [":libusb_windows"],
+ "//conditions:default": ["@platforms//:incompatible"],
+ }) + [
+ ":libusb_config",
+ ":libusb_headers",
+ ],
+)
+
+cc_shared_library(
+ name = "libusb_dynamic",
+ # Always use exactly this name.
+ shared_lib_name = select({
+ "@platforms//os:macos": "libusb-1.0.dylib",
+ "@platforms//os:windows": "libusb-1.0.dll",
+ "//conditions:default": "libusb-1.0.so",
+ }),
+ visibility = ["//visibility:public"],
+ win_def_file = select({
+ "@platforms//os:windows": "libusb/libusb-1.0.def",
+ "//conditions:default": None,
+ }),
+ deps = [":libusb"],
+)
+
+label_flag(
+ name = "libusb_config",
+ build_setting_default = "@rules_libusb//:default_libusb_config",
+)
+
+# To use this `config.h`, include it as `msvc/config.h` from your actual
+# `config.h`.
+cc_library(
+ name = "libusb_msvc_config",
+ hdrs = ["msvc/config.h"],
+ visibility = ["//visibility:public"],
+)
+
+# To use this `config.h`, include it as `Xcode/config.h` from your actual
+# `config.h`.
+cc_library(
+ name = "libusb_macos_config",
+ hdrs = ["Xcode/config.h"],
+ visibility = ["//visibility:public"],
+)
+
+cc_library(
+ name = "libusb_headers",
+ hdrs = glob(["libusb/*.h"]),
+ includes = ["libusb"],
+ visibility = ["//visibility:private"],
+ deps = [":libusb_config"],
+)
+
+cc_library(
+ name = "libusb_windows",
+ srcs = glob(["libusb/os/*_windows.c"]),
+ hdrs = glob(["libusb/os/*_windows.h"]),
+ visibility = ["//visibility:private"],
+ deps = [
+ ":libusb_config",
+ ":libusb_headers",
+ ],
+)
+
+cc_library(
+ name = "libusb_posix",
+ srcs = glob(["libusb/os/*_posix.c"]),
+ hdrs = glob(["libusb/os/*_posix.h"]),
+ visibility = ["//visibility:private"],
+ deps = [
+ ":libusb_config",
+ ":libusb_headers",
+ ],
+)
+
+# TODO: Consider upstreaming a build-system-independent method for detecting
+# udev vs netlink.
+_GENERATED_UDEV_OR_NETLINK = """
+#include "config.h"
+
+#if HAVE_LIBUDEV
+#include "libusb/os/linux_udev.c"
+#else
+#include "libusb/os/linux_netlink.c"
+#endif // HAVE_LIBUDEV
+"""
+
+genrule(
+ name = "autodetect_udev",
+ outs = ["udev_or_netlink.c"],
+ cmd = "echo '{}' > $@".format(_GENERATED_UDEV_OR_NETLINK),
+)
+
+cc_library(
+ name = "libusb_linux",
+ srcs = glob(
+ ["libusb/os/linux_*.c"],
+ exclude = [
+ "libusb/os/linux_netlink.c",
+ "libusb/os/linux_udev.c",
+ ],
+ ) + [
+ "udev_or_netlink.c",
+ ],
+ hdrs = glob(["libusb/os/linux_*.h"]) + [
+ # These are listed as `hdrs` because they're selected by
+ # an `#include` in the generated `udev_or_netlink.c`.
+ "libusb/os/linux_netlink.c",
+ "libusb/os/linux_udev.c",
+ ],
+ visibility = ["//visibility:private"],
+ deps = [
+ ":libusb_config",
+ ":libusb_headers",
+ ":libusb_posix",
+ ],
+)
+
+cc_library(
+ name = "libusb_macos",
+ srcs = glob(["libusb/os/darwin_*.c"]),
+ hdrs = glob(["libusb/os/darwin_*.h"]),
+ linkopts = [
+ "-framework IOKit",
+ "-framework Security",
+ ],
+ visibility = ["//visibility:private"],
+ deps = [
+ ":libusb_config",
+ ":libusb_headers",
+ ":libusb_posix",
+ ],
+)
diff --git a/linux/config.h b/linux/config.h
new file mode 100644
index 0000000..3c87c02
--- /dev/null
+++ b/linux/config.h
@@ -0,0 +1,53 @@
+// Copyright 2024 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#define DEFAULT_VISIBILITY __attribute__ ((visibility ("default")))
+#define ENABLE_LOGGING 1
+#define HAVE_ASM_TYPES_H 1
+#define HAVE_CLOCK_GETTIME 1
+#define HAVE_DECL_EFD_CLOEXEC 1
+#define HAVE_DECL_EFD_NONBLOCK 1
+#define HAVE_DECL_TFD_CLOEXEC 1
+#define HAVE_DECL_TFD_NONBLOCK 1
+#define HAVE_DLFCN_H 1
+#define HAVE_EVENTFD 1
+#define HAVE_INTTYPES_H 1
+#define HAVE_NFDS_T 1
+#define HAVE_PIPE2 1
+#define HAVE_PTHREAD_CONDATTR_SETCLOCK 1
+#define HAVE_PTHREAD_SETNAME_NP 1
+#define HAVE_STDINT_H 1
+#define HAVE_STDIO_H 1
+#define HAVE_STDLIB_H 1
+#define HAVE_STRINGS_H 1
+#define HAVE_STRING_H 1
+#define HAVE_SYS_STAT_H 1
+#define HAVE_SYS_TIME_H 1
+#define HAVE_SYS_TYPES_H 1
+#define HAVE_TIMERFD 1
+#define HAVE_UNISTD_H 1
+
+#define PLATFORM_POSIX 1
+
+#define PRINTF_FORMAT(a, b) __attribute__ ((__format__ (__printf__, a, b)))
+#define STDC_HEADERS 1
+
+#define _GNU_SOURCE 1
+
+// Explicitly define HAVE_LIBUDEV to prevent auto-detection.
+#ifndef HAVE_LIBUDEV
+#if __has_include(<libudev.h>)
+#define HAVE_LIBUDEV 1
+#endif // __has_include(<libudev.h>)
+#endif // HAVE_LIBUDEV