uNetworking / uSockets

Miniscule cross-platform eventing, networking & crypto for async applications
Apache License 2.0
1.29k stars 268 forks source link

bazel build in case someone needs it [with both openssl and boringssl] #116

Closed yesudeep closed 4 years ago

yesudeep commented 4 years ago

Hi,

Thank you for this amazing library.

I use Bazel to build uSockets and I understand the project wants to maintain only a Makefile. However, I'm making my BUILD.bazel file and configuration available so that anybody who needs it can use it. The users of the library don't need to know how to build the library and its dependencies—the rules are part of the remote BUILD file. Here's example usage from the perspective of the library user (simpler one):

load("@rules_cc//cc:defs.bzl", "cc_binary")

cc_binary(
    name = "executable",
    srcs = ["src/main.cc"],
    deps = [
        "@usockets",       # <-- super simple.
    ],
)

A more complex example:

load("@rules_cc//cc:defs.bzl", "cc_binary")

cc_binary(
    name = "executable",
    srcs = ["src/main.cc"],
    args = [
        "--key=$(location :key_target)",
        "--cert=$(location :cert_target)",
    ],
    copts = [
        "-flto",  # optional. change flags based on OS/compiler. see examples below.
    ],
    deps = [
        "@usockets",
   ],
    data = [
        # Add cert and key targets here. can use `genrule`s to generate them.
        ":key_target",
        ":cert_target",
    ],
)

To run the example:

❯ bazel run //package/path:executable

I include uSockets in my workspace using:

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")

http_archive(
    name = "usockets",
    type = "zip",
    url = "https://github.com/uNetworking/uSockets/archive/<commit>.zip",
    sha256 = "<sha256-digest>",
    strip_prefix = "uSockets-<commit>",
    # Don't need the following line if project includes a Bazel build (renamed to BUILD.bazel) file shown below along 
    # with a WORKSPACE file.
    build_file = "//third_party/c/remote:usockets.BUILD",
)

http_archive(
    name = "libuv",
    url = "https://github.com/libuv/libuv/archive/<commit>.tar,gz",
    sha256 = "<sha256-digest>",
    strip_prefix = "libuv-<commit>",
    build_file = "//third_party/c/remote:libuv.BUILD",
)

usockets.BUILD file:

load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library")

licenses(["notice"])  # MIT

exports_files(["LICENSE"])

config_setting(
    name = "platform_linux",
    constraint_values = [
        "@bazel_tools//platforms:linux",
    ],
)

config_setting(
    name = "platform_osx",
    constraint_values = [
        "@bazel_tools//platforms:osx",
    ],
)

DEFAULT_DEPS = [
    # Prefer BoringSSL to OpenSSL.
    "@boringssl//:crypto",
    "@boringssl//:ssl",
    # "@openssl",
]

cc_library(
    name = "usockets",
    srcs = glob([
        "src/**/*.c",
    ]),
    hdrs = glob([
        "src/**/*.h",
    ]),
    copts = select({
        ":platform_linux": [
            "-std=c11",
            "-flto",
        ],
        ":platform_osx": [
            "-std=c11",
            "-flto",
        ],
        "//conditions:default": [],
    }),
    defines = select({
        ":platform_linux": [
            "LIBUS_USE_OPENSSL=1",
            "LIBUS_USE_EPOLL=1",
        ],
        ":platform_osx": [
            "LIBUS_USE_OPENSSL=1",
            "LIBUS_USE_KQUEUE=1",
        ],
        "//conditions:default": [
            "LIBUS_USE_OPENSSL=1",
            "LIBUS_USE_LIBUV=1",
        ],
    }),
    includes = [
        "src",
    ],
    linkopts = select({
        ":platform_linux": [
            "-pthread",
            "-ldl",
        ],
        ":platform_osx": [
            "-pthread",
        ],
        "//conditions:default": [],
    }),
    linkstatic = True,
    strip_include_prefix = "src",
    visibility = ["//visibility:public"],
    deps = select({
        ":platform_linux": DEFAULT_DEPS,
        ":platform_osx": DEFAULT_DEPS,
        "//conditions:default": DEFAULT_DEPS + [
            "@libuv",
        ],
    }),
)

cc_binary(
    name = "example_hammer_test",
    srcs = [
        "examples/hammer_test.c",
    ],
    visibility = ["//visibility:public"],
    deps = [
        ":usockets",
    ],
    data = [
        # add key and cert targets here.
    ],
)

# ... other examples.

libuv.BUILD:

load("@rules_foreign_cc//tools/build_defs:configure.bzl", "configure_make")

config_setting(
    name = "platform_linux",
    constraint_values = [
        "@bazel_tools//platforms:linux",
    ],
)

config_setting(
    name = "platform_osx",
    constraint_values = [
        "@bazel_tools//platforms:osx",
    ],
)

filegroup(
    name = "all",
    srcs = glob(["**"]),
    visibility = ["//visibility:public"],
)

configure_make(
    name = "libuv",
    autogen = True,
    # see @rules_foreign_cc#315
    configure_env_vars = select({
        ":platform_osx": {"AR": ""},
        "//conditions:default": {},
    }),
    configure_in_place = True,
    lib_source = "@libuv//:all",
    linkopts = select({
        ":platform_linux": [
            "-pthread",
            "-ldl",
        ],
        "//conditions:default": [
            "-pthread",
        ],
    }),
    out_lib_dir = "lib",
    static_libraries = ["libuv.a"],
    visibility = ["//visibility:public"],
)

To build and run the example with AddressSanitizer, for example, I just type:

❯ bazel run --config=asan "@usockets//:example_hammer_test"
INFO: Analyzed target @usockets//:example_hammer_test (0 packages loaded, 2 targets configured).
INFO: Found 1 target...
Target @usockets//:example_hammer_test up-to-date:
  bazel-bin/external/usockets/example_hammer_test
INFO: Elapsed time: 0.531s, Critical Path: 0.40s
INFO: 2 processes: 2 darwin-sandbox.
INFO: Build completed successfully, 4 total actions
INFO: Build completed successfully, 4 total actions
AddressSanitizer:DEADLYSIGNAL
=================================================================
==85977==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000028 (pc 0x000100ae3f37 bp 0x7ffeef5634c0 sp 0x7ffeef5634a0 T0)
==85977==The signal is caused by a WRITE memory access.
==85977==Hint: address points to the zero page.
    #0 0x100ae3f36 in us_socket_context_on_open (example_hammer_test:x86_64+0x100447f36)
    #1 0x100ae64d1 in us_internal_ssl_socket_context_on_open (example_hammer_test:x86_64+0x10044a4d1)
    #2 0x100ae3f05 in us_socket_context_on_open (example_hammer_test:x86_64+0x100447f05)
    #3 0x10069fa99 in main (example_hammer_test:x86_64+0x100003a99)
    #4 0x7fff73232cc8 in start (libdyld.dylib:x86_64+0x1acc8)

==85977==Register values:
rax = 0x0000000100ae45d0  rbx = 0x00007ffeef5635a0  rcx = 0x0000100000000005  rdx = 0x0000100000000000
rdi = 0x0000000000000028  rsi = 0x0000000000000000  rbp = 0x00007ffeef5634c0  rsp = 0x00007ffeef5634a0
 r8 = 0x0000000000000048   r9 = 0x00000fffffffffff  r10 = 0x0000000000000000  r11 = 0xffffffffffffffff
r12 = 0x0000000000000000  r13 = 0x0000000000000000  r14 = 0x0000000000000000  r15 = 0x0000000000000000
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV (example_hammer_test:x86_64+0x100447f36) in us_socket_context_on_open
==85977==ABORTING

Bazel fetches the correct commit from github for each dependency without needing to use submodules and builds everything into your workspace only if you use it—you don't pay for what you don't use. Build files for openssl and boringssl can be found here. --config=asan is defined here and goes into a workspace level .bazelrc file in more recent versions of Bazel.

Please feel free to close the bug. Just reporting this here so it helps anybody who needs it.

Cheers.

ghost commented 4 years ago

I'm closing this, like you said people can always check closed issues but I have no interest in Bazel or any specific build system.

yesudeep commented 4 years ago

Update. Build fails when using BoringSSL with uSockets on Windows with MSVC++. The fix is to define WIN32_LEAN_AND_MEAN=1 and link with the appropriate libraries based on this discussion.

Updated build file:

load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library")

licenses(["notice"])  # MIT

exports_files(["LICENSE"])

config_setting(
    name = "platform_linux",
    constraint_values = [
        "@platforms//os:linux",
    ],
)

config_setting(
    name = "platform_macos",
    constraint_values = [
        "@platforms//os:macos",
    ],
)

config_setting(
    name = "platform_windows",
    constraint_values = [
        "@platforms//os:windows",
    ],
)

DEFAULT_DEPS = [
    # Prefer BoringSSL to OpenSSL.
    "@boringssl//:crypto",
    "@boringssl//:ssl",
    # "@openssl",
]

cc_library(
    name = "usockets",
    srcs = glob([
        "src/**/*.c",
    ]),
    hdrs = glob([
        "src/**/*.h",
    ]),
    copts = select({
        ":platform_linux": [
            "-std=c11",
            "-flto",
        ],
        ":platform_macos": [
            "-std=c11",
            "-flto",
        ],
        "//conditions:default": [],
    }),
    defines = select({
        ":platform_linux": [
            "LIBUS_USE_OPENSSL=1",
            "LIBUS_USE_EPOLL=1",
        ],
        ":platform_macos": [
            "LIBUS_USE_OPENSSL=1",
            "LIBUS_USE_KQUEUE=1",
        ],
        ":platform_windows": [
            "LIBUS_USE_OPENSSL=1",
            "LIBUS_USE_LIBUV=1",
            "WIN32_LEAN_AND_MEAN=1",
        ],
        "//conditions:default": [
            "LIBUS_USE_OPENSSL=1",
            "LIBUS_USE_LIBUV=1",
        ],
    }),
    includes = [
        "src",
    ],
    linkopts = select({
        ":platform_linux": [
            "-pthread",
            "-ldl",
        ],
        ":platform_macos": [
            "-pthread",
        ],
        ":platform_windows": [
            "-DEFAULTLIB:Ws2_32.lib",
            "-DEFAULTLIB:Iphlpapi.lib",
            "-DEFAULTLIB:Psapi.lib",
            "-DEFAULTLIB:User32.lib",
            "-DEFAULTLIB:Userenv.lib",
        ],
        "//conditions:default": [],
    }),
    linkstatic = True,
    strip_include_prefix = "src",
    visibility = ["//visibility:public"],
    deps = select({
        ":platform_linux": DEFAULT_DEPS,
        ":platform_macos": DEFAULT_DEPS,
        "//conditions:default": DEFAULT_DEPS + [
            "@libuv",
        ],
    }),
)

cc_binary(
    name = "example_echo_server",
    srcs = [
        "examples/echo_server.c",
    ],
    visibility = ["//visibility:public"],
    deps = [
        ":usockets",
    ],
)

cc_binary(
    name = "example_http_server",
    srcs = [
        "examples/http_server.c",
    ],
    visibility = ["//visibility:public"],
    deps = [
        ":usockets",
    ],
)

cc_binary(
    name = "example_http_load_test",
    srcs = [
        "examples/http_load_test.c",
    ],
    visibility = ["//visibility:public"],
    deps = [
        ":usockets",
    ],
)

cc_binary(
    name = "example_hammer_test",
    srcs = [
        "examples/hammer_test.c",
    ],
    visibility = ["//visibility:public"],
    deps = [
        ":usockets",
    ],
)

cc_binary(
    name = "example_peer_verify_test",
    srcs = [
        "examples/peer_verify_test.c",
    ],
    visibility = ["//visibility:public"],
    deps = [
        ":usockets",
    ],
)
yesudeep commented 4 years ago

Since autotools don't work as well as I'd hoped on Windows (PowerShell), I've ported the libuv.BUILD to explicitly build using only Bazel on Windows. A fallback rule that uses autotools is also available.

libuv.BUILD:

load("@rules_cc//cc:defs.bzl", "cc_library")
load("@rules_foreign_cc//tools/build_defs:configure.bzl", "configure_make")

config_setting(
    name = "platform_linux",
    constraint_values = [
        "@platforms//os:linux",
    ],
)

config_setting(
    name = "platform_macos",
    constraint_values = [
        "@platforms//os:macos",
    ],
)

config_setting(
    name = "platform_windows",
    constraint_values = [
        "@platforms//os:windows",
    ],
)

config_setting(
    name = "platform_ios",
    constraint_values = [
        "@platforms//os:ios",
    ],
)

config_setting(
    name = "platform_android",
    constraint_values = [
        "@platforms//os:android",
    ],
)

filegroup(
    name = "all",
    srcs = glob(["**"]),
    visibility = ["//visibility:public"],
)

# Use this if nothing else works--it results in slower builds.
configure_make(
    name = "libuv_autotools",
    autogen = True,
    # see @rules_foreign_cc#315
    configure_env_vars = select({
        ":platform_macos": {"AR": ""},
        "//conditions:default": {},
    }),
    configure_in_place = True,
    lib_source = "@libuv//:all",
    linkopts = select({
        ":platform_linux": [
            "-pthread",
            "-ldl",
        ],
        "//conditions:default": [
            "-pthread",
        ],
    }),
    out_lib_dir = "lib",
    static_libraries = ["libuv.a"],
    visibility = ["//visibility:public"],
)

# Based on https://github.com/grpc/grpc/blob/master/third_party/libuv.BUILD
COMMON_LIBUV_HEADERS = [
    "include/uv.h",
    "include/uv/errno.h",
    "include/uv/threadpool.h",
    "include/uv/version.h",
    "include/uv/tree.h",
]

UNIX_LIBUV_HEADERS = [
    "include/uv/unix.h",
    "src/unix/atomic-ops.h",
    "src/unix/internal.h",
    "src/unix/spinlock.h",
]

LINUX_LIBUV_HEADERS = [
    "include/uv/linux.h",
    "src/unix/linux-syscalls.h",
]

ANDROID_LIBUV_HEADERS = [
    "include/uv/android-ifaddrs.h",
]

DARWIN_LIBUV_HEADERS = [
    "include/uv/darwin.h",
    "src/unix/darwin-stub.h",
]

WINDOWS_LIBUV_HEADERS = [
    "include/uv/win.h",
    "src/win/atomicops-inl.h",
    "src/win/handle-inl.h",
    "src/win/internal.h",
    "src/win/req-inl.h",
    "src/win/stream-inl.h",
    "src/win/winapi.h",
    "src/win/winsock.h",
]

COMMON_LIBUV_SOURCES = [
    "src/fs-poll.c",
    "src/heap-inl.h",
    "src/idna.c",
    "src/idna.h",
    "src/inet.c",
    "src/queue.h",
    "src/strscpy.c",
    "src/strscpy.h",
    "src/threadpool.c",
    "src/timer.c",
    "src/uv-data-getter-setters.c",
    "src/uv-common.c",
    "src/uv-common.h",
    "src/version.c",
]

UNIX_LIBUV_SOURCES = [
    "src/unix/async.c",
    "src/unix/atomic-ops.h",
    "src/unix/core.c",
    "src/unix/dl.c",
    "src/unix/fs.c",
    "src/unix/getaddrinfo.c",
    "src/unix/getnameinfo.c",
    "src/unix/internal.h",
    "src/unix/loop.c",
    "src/unix/loop-watcher.c",
    "src/unix/pipe.c",
    "src/unix/poll.c",
    "src/unix/process.c",
    "src/unix/signal.c",
    "src/unix/spinlock.h",
    "src/unix/stream.c",
    "src/unix/tcp.c",
    "src/unix/thread.c",
    "src/unix/tty.c",
    "src/unix/udp.c",
]

LINUX_LIBUV_SOURCES = [
    "src/unix/linux-core.c",
    "src/unix/linux-inotify.c",
    "src/unix/linux-syscalls.c",
    "src/unix/linux-syscalls.h",
    "src/unix/procfs-exepath.c",
    "src/unix/proctitle.c",
    "src/unix/sysinfo-loadavg.c",
    "src/unix/sysinfo-memory.c",
]

ANDROID_LIBUV_SOURCES = [
    "src/unix/android-ifaddrs.c",
    "src/unix/pthread-fixes.c",
]

DARWIN_LIBUV_SOURCES = [
    "src/unix/bsd-ifaddrs.c",
    "src/unix/darwin.c",
    "src/unix/fsevents.c",
    "src/unix/kqueue.c",
    "src/unix/darwin-proctitle.c",
    "src/unix/proctitle.c",
]

WINDOWS_LIBUV_SOURCES = [
    "src/win/async.c",
    "src/win/atomicops-inl.h",
    "src/win/core.c",
    "src/win/detect-wakeup.c",
    "src/win/dl.c",
    "src/win/error.c",
    "src/win/fs-event.c",
    "src/win/fs.c",
    "src/win/getaddrinfo.c",
    "src/win/getnameinfo.c",
    "src/win/handle.c",
    "src/win/handle-inl.h",
    "src/win/internal.h",
    "src/win/loop-watcher.c",
    "src/win/pipe.c",
    "src/win/poll.c",
    "src/win/process-stdio.c",
    "src/win/process.c",
    "src/win/req-inl.h",
    "src/win/signal.c",
    "src/win/stream.c",
    "src/win/stream-inl.h",
    "src/win/tcp.c",
    "src/win/thread.c",
    "src/win/tty.c",
    "src/win/udp.c",
    "src/win/util.c",
    "src/win/winapi.c",
    "src/win/winapi.h",
    "src/win/winsock.c",
    "src/win/winsock.h",
]

COPTS_COMMON = [
    "-D_LARGEFILE_SOURCE",
    "-D_FILE_OFFSET_BITS=64",
    "-D_GNU_SOURCE",
]

COPTS_CC = [
    "--std=gnu89",
    "-pedantic",
    "-O2",
    "-Wno-error",
    "-Wno-strict-aliasing",
    "-Wstrict-aliasing",
    "-Wno-implicit-function-declaration",
    "-Wno-unused-function",
    "-Wno-unused-variable",
    "-pthread",
]

COPTS_LINUX = COPTS_CC + [
    "-Wno-tree-vrp",
    "-Wno-omit-frame-pointer",
]

COPTS_MACOS = COPTS_CC + [
    "-D_DARWIN_USE_64_BIT_INODE=1",
    "-D_DARWIN_UNLIMITED_SELECT=1",
]

# TODO: We haven't build something for iOS yet.
COPTS_IOS = COPTS_CC

cc_library(
    name = "libuv",
    srcs = select({
        ":platform_android": COMMON_LIBUV_SOURCES + UNIX_LIBUV_SOURCES + LINUX_LIBUV_SOURCES + ANDROID_LIBUV_SOURCES,
        ":platform_ios": COMMON_LIBUV_SOURCES + UNIX_LIBUV_SOURCES + DARWIN_LIBUV_SOURCES,
        ":platform_macos": COMMON_LIBUV_SOURCES + UNIX_LIBUV_SOURCES + DARWIN_LIBUV_SOURCES,
        ":platform_windows": COMMON_LIBUV_SOURCES + WINDOWS_LIBUV_SOURCES,
        "//conditions:default": COMMON_LIBUV_SOURCES + UNIX_LIBUV_SOURCES + LINUX_LIBUV_SOURCES,
    }),
    hdrs = select({
        ":platform_android": COMMON_LIBUV_HEADERS + UNIX_LIBUV_HEADERS + LINUX_LIBUV_HEADERS + ANDROID_LIBUV_HEADERS,
        ":platform_macos": COMMON_LIBUV_HEADERS + UNIX_LIBUV_HEADERS + DARWIN_LIBUV_HEADERS,
        ":platform_ios": COMMON_LIBUV_HEADERS + UNIX_LIBUV_HEADERS + DARWIN_LIBUV_HEADERS,
        ":platform_windows": COMMON_LIBUV_HEADERS + WINDOWS_LIBUV_HEADERS,
        "//conditions:default": COMMON_LIBUV_HEADERS + UNIX_LIBUV_HEADERS + LINUX_LIBUV_HEADERS,
    }),
    copts = COPTS_COMMON + select({
        ":platform_macos": COPTS_MACOS,
        ":platform_ios": COPTS_IOS,
        ":platform_linux": COPTS_LINUX,
        ":platform_windows": [
            "-O2",
            "-DWIN32_LEAN_AND_MEAN",
            "-D_WIN32_WINNT=0x0600",
        ],
        "//conditions:default": COPTS_CC,
    }),
    includes = [
        "include",
        "src",
        "src/unix",
    ],
    linkopts = select({
        ":platform_windows": [
            "-DEFAULTLIB:Ws2_32.lib",
            "-DEFAULTLIB:Iphlpapi.lib",
            "-DEFAULTLIB:Psapi.lib",
            "-DEFAULTLIB:User32.lib",
            "-DEFAULTLIB:Userenv.lib",
        ],
        "//conditions:default": [
            "-lpthread",
            "-ldl",
        ],
    }),
    linkstatic = True,
    visibility = [
        "//visibility:public",
    ],
    deps = select({
        ":platform_windows": [
            "@pthread_windows//:pthread",
        ],
        "//conditions:default": [],
    }),
)
yesudeep commented 4 years ago

pthreads-w32 ported to Bazel is available here: pthread_windows.