Setting up a Compiler Project with LLVM on MacOS
Over spring break I had tried to start a compiler project that emits LLVM IR. I had a terribly hard time finding information about how to setup the Makefile and the project structure specifically for MacOS. I got it working by a lot of research and some help from Claude.
Setup
This tutorial assumes you have homebrew installed
You are going to need the Developer Tools for MacOS. You can install it by running the following command in your terminal.
xcode-select --install
There are going to be a couple times every year where you will have to update these tools through the settings app.
You will have to also install the LLVM tools through brew.
brew install llvm
You may be good to go once installed, but for further use of the tools that LLVM provides you may have to add it to your PATH variable.
The way that I did it was by modifying my ~/.zshrc
file. I added this to the end of my file.
export PATH="/opt/homebrew/opt/llvm/bin:$PATH"
You should be good to go now!
If you end up with problems following this article, please don’t hesitate to email me with your problems so I can update this blog to adjust the setup process.
Directory Structure
This is the directory structure you should setup. Try to setup your project as a git project because version control never hurt anyone.
The src
directory is where all your *.c
files will go and the include
directory will contain all your header files (*.h
).
├── Makefile
├── include
│ └── lexer.h
└── src
├── lexer.c
└── main.c
The Makefile
This is the most important part of this setup and took me forever to figure out.
# Compiler settings
CC = clang
CFLAGS = -Wall -Wextra $(shell llvm-config --cflags)
LDFLAGS = $(shell llvm-config --ldflags --libs core)
# Directory structure
SRC_DIR = src
OBJ_DIR = obj
BIN_DIR = bin
# Find source files and generate object file paths
SRC_FILES = $(wildcard $(SRC_DIR)/*.c)
OBJ_FILES = $(patsubst $(SRC_DIR)/%.c,$(OBJ_DIR)/%.o,$(SRC_FILES))
# Output executable
TARGET = $(BIN_DIR)/mycompiler
.PHONY: all clean
all: $(TARGET)
$(TARGET): $(OBJ_FILES) | $(BIN_DIR)
$(CC) $^ -o $@ $(LDFLAGS)
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(OBJ_DIR)
$(CC) $(CFLAGS) -I./include -c $< -o $@
$(BIN_DIR) $(OBJ_DIR):
mkdir -p $@
clean:
rm -rf $(OBJ_DIR) $(BIN_DIR)
The above code should allow you to code in an enviornment that imports LLVM header files.
One other thing I had to add for my VSCode to stop yelling at me is to modify my includePath in vscode
This is what my .vscode/c_cpp_properties.json
file looks like:
{
"configurations": [
{
"name": "Mac",
"includePath": [
"${workspaceFolder}/**",
"/opt/homebrew/opt/llvm/include/*"
],
"defines": [],
"macFrameworkPath": [
"/Library/Developer/CommandLineTools/SDKs/MacOSX15.sdk/System/Library/Frameworks"
],
"compilerPath": "/opt/homebrew/opt/llvm/bin/clang",
"cStandard": "c17",
"intelliSenseMode": "macos-clang-arm64"
}
],
"version": 4
}
Hope this helps! I used this setup for my spunc language.