In the previous example we wrote a simple C program that only prints “hello world” and includes a standard header file. Real programs often use external code/libraries installed via system package manager (apt
or brew
) and link against it. It’s possible to use Bazel to do the same thing - rather than a “hermetic” build. This isn’t typically what you want, but might be useful during a migration, or for certain fixed development environments.
In this example we will write a simple program that reads a file and tries to determine what kind of file it is by using libmagic
. Before we do that, we need to install libmagic
on our computers.
- MacOS users can
brew install libmagic
followed bybrew link libmagic
- Debian users can
apt-get install libmagic-dev
- Windows users can follow along by using Windows Subsystem for Linux (WSL).
Here is an example c program that opens a file and tells the mime type using libmagic
. Let’s name this src/magic.c
.
#include <stdio.h>
#include <stdlib.h>
#include <magic.h>
int main(int argc, char *argv[]) {
// Check if filename is provided
if (argc != (int)2) {
fprintf(stderr, "Usage: %s <filename>\n", argv[0]);
return 1;
}
// Initialize magic handle
magic_t magic = magic_open(MAGIC_MIME_TYPE);
if (magic == NULL) {
fprintf(stderr, "Failed to initialize libmagic\n");
return 1;
}
// Load magic database
if (magic_load(magic, NULL) != 0) {
fprintf(stderr, "Cannot load magic database: %s\n", magic_error(magic));
magic_close(magic);
return 1;
}
// Get MIME type
const char *mime_type = magic_file(magic, argv[1]);
if (mime_type == NULL) {
fprintf(stderr, "Error determining MIME type: %s\n", magic_error(magic));
magic_close(magic);
return 1;
}
// Print result
printf("MIME type: %s\n", mime_type);
// Cleanup
magic_close(magic);
return 0;
}
Outside of Bazel, compiling this program can be accomplished by running gcc main.c -lmagic -o main
where the compiler is told to link against libmagic
via -lmagic
.
Here’s an equivalent Makefile, though note the portability issues with finding the lib
and include
folders:
main:
gcc main.c -lmagic -o main \
-L $(shell brew --prefix)/lib -I $(shell brew --prefix)/include
On macOS -L $(brew --prefix)/lib -I $(brew --prefix)/include
flags has to be added command line to allow the compiler to find brew managed libraries. On Linux this is not necessary as apt
installs everything under system /usr
When compiling on macOS some additional configuration is also needed to allow linking against homebrew managed libraries.
llvm.toolchain(
# Add /opt/homebrew to allowlist of linking against system libraries.
cxx_builtin_include_directories = {
"darwin-aarch64": ["/opt/homebrew"]
},
)
Finally we can create a cc_binary
target for our program and run it via bazel run src:magic
and should see the Usage:
line from earlier.
cc_binary(
name = "magic",
srcs = ["magic.cc"],
copts = [
# on macOS search for headers on homebrew managed /include directory.
"-I/opt/homebrew/include",
],
linkopts = [
# link the magic library.
"-lmagic",
# on macOS allow linking against homebrew installed dylibs.
"-L/opt/homebrew/lib",
]
)