Integrating SDL2 with bazel on macOS


Bazel

Came across bazel build system recently at my new job and found it to be quite nice compared to my earlier mainstays CMake and Make. It’s fast and correct, just as their website says. But I also liked it because it’s explicit with very little magic. And it has a powerful query/tools system that allows you to really analyze your builds and dependencies in depth. Though examples in the wild are a bit less since it is a fairly recent entrant compared to its competitors. For a personal side project, I needed to use SDL2 and it was the first time I had to use an external/pre-built library with bazel. This post documents the process I used for my own future reference and may be help some other lost soul like me. Although, the post talks about SDL2 here specifically, the same process can be used for most other external pre-compiled modules.

Download / Install libsdl

  • Download the latest MacOS dmg file from LibSDL
  • Double click the downloaded dmg file
  • Copy the SDL2.framework folder over to /Library/Frameworks path

Make bazel aware of existence of SDL2

Update your WORKSPACE file (resides at the root of your project) with below content. This is important because bazel does all the build steps in a sandbox and one cannot depend on anything outside this sandbox. This step allows bazel to make the content present at this path available to the sandbox as a bazel repository.

new_local_repository(
    name = "sdl2",
    path = "/Library/Frameworks/SDL2.framework",
    build_file = "external/sdl.build",
)

Set up your SDL library to be usable by other modules in your project

Note the build_file parameter. This tells bazel how to use this repository. This can be anything according to what you want, the path being relative to the project root. Considering the above example, create a new directory external in the root of your project and add a new file sdl.BUILD to it with below content.

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

cc_library(
    name = "sdl2",
    hdrs = glob(["Headers/*.h"]),
    includes = ["Header"], # Optional. 
    visibility = ["//visibility:public"],
)

This ensures that now your other modules can depend on SDL2 using @sdl2. The visibility option can be tweaked as per individual needs.

Use SDL2

Update the BUILD file of the module where you want to use SDL2 with below contents.

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

cc_library(
    name = "some_module",
    srcs = [
        "some_source.cc",
    ],
    # Optional: copts can be avoided if includes was set in sdl.BUILD
    copts = [
        "-Iexternal/sdl2/Headers",
    ],
    linkopts = [
        "-F/Library/Frameworks",
        "-framework SDL2",
    ],
    deps = [
        "@sdl2",
    ],
)

The important portions here are:

  • copts: To let bazel know where to search for SDL2 header files. Note that the path here is relative to project root.
  • linkopts: Need the -F option to let compiler know where to search for the framework files and -framework option as usual to link to ZSDL2. The path with -F can be absolute one. Not sure why this is not restricted by the sandbox. Investigating this currently.
  • deps: Finally, create the explicit dependency on your sdl2 module by referring to it as @sdl2

By now, the project is ready to be compiled and linked.