Setting up a Compiler Project with LLVM on MacOS

Posted: 3-21-2025

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.

👋 Thanks for reading!

If you enjoyed this post, then consider sharing it and/or following me on Github.

cameron © 2023