diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1253db0..d094ad3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,39 +1,37 @@ -name: C++ Build +name: C++ CI on: push: + branches: [main] pull_request: + branches: [main] jobs: - build: + build-and-run: runs-on: ubuntu-latest - - strategy: - matrix: - compiler: [g++] - + env: + ASAN_OPTIONS: detect_leaks=1 # automatically detect memory leaks steps: - - name: Checkout repo + # 1. Checkout repo + - name: Checkout code uses: actions/checkout@v4 + # 2. Install build tools - name: Install build tools run: | sudo apt-get update - sudo apt-get install -y build-essential - - - name: Build all examples - run: | - make + sudo apt-get install -y build-essential g++ clang - - name: Run examples + # 3. Build and run all examples dynamically + - name: Build and Run run: | - for dir in */ ; do - if [ -f "$dir/Makefile" ]; then - echo "Running $dir" - (cd "$dir" && make run || true) - fi + for san in address thread undefined; do + echo "=== Building all examples with sanitizer $san ===" + make SANITIZE=$san + echo "=== Running all examples with sanitizer $san ===" + make run done - - name: Clean + - name: Clean up build artifacts run: | make clean diff --git a/.gitignore b/.gitignore index b8d81fd..5a0b7ba 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,17 @@ -.o +# Object files +*.o + +# Executable +thread_safe_queue +unique_ptr_basics +*.out + +# Backup files +*~ +*.swp + +# CMake / IDE files +CMakeFiles/ +CMakeCache.txt +*.idea/ +*.vscode/ diff --git a/Makefile b/Makefile index abd5875..9ef44ba 100644 --- a/Makefile +++ b/Makefile @@ -1,13 +1,37 @@ +# ====================================== +# Top-level Makefile for cpp-coding-exercise +# ====================================== + +# Auto-detect all subdirectories that contain a Makefile SUBDIRS := $(dir $(wildcard */Makefile)) -.PHONY: all clean $(SUBDIRS) +# Default sanitizer (can be overridden) +SANITIZE ?= address + +.PHONY: all clean run $(SUBDIRS) +# -------------------------- +# Build all examples +# -------------------------- all: $(SUBDIRS) $(SUBDIRS): - $(MAKE) -C $@ + @echo "=== Building $@ with SANITIZE=$(SANITIZE) ===" + $(MAKE) -C $@ SANITIZE=$(SANITIZE) + +# -------------------------- +# Run all examples (optional) +# -------------------------- +run: + @for dir in $(SUBDIRS); do \ + echo "=== Running $$dir ==="; \ + (cd $$dir && $(MAKE) SANITIZE=$(SANITIZE) run) || exit 1; \ + done +# -------------------------- +# Clean all examples +# -------------------------- clean: - for dir in $(SUBDIRS); do \ + @for dir in $(SUBDIRS); do \ $(MAKE) -C $$dir clean; \ done diff --git a/README.md b/README.md index bfa8645..644c8c0 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ Runnable examples expose a `run` target: make run ``` -This keeps CI safe (build-only by default) while allowing local execution. +`make run` on top level will run all built targets. --- @@ -97,11 +97,11 @@ make clean ## Shared build configuration -Common compiler settings live in `common.mk`: +Common compiler settings live in [common.mk](./common.mk): ```make CXX := g++ -CXXFLAGS := -std=c++20 -O2 -Wall -Wextra +CXXFLAGS := -std=c++20 -Wall -Wextra ``` Individual examples may extend this, e.g.: @@ -112,6 +112,25 @@ CXXFLAGS += -pthread --- +## Address Sanitizer Check +The Address Sanitizer is enabled by default to ensure there is no memory leaks or other memory problems. + +```make +# Builds with AddressSanitizer automatically +make + +# ThreadSanitizer +make SANITIZE=thread + +# UndefinedBehaviorSanitizer +make SANITIZE=undefined + +# No sanitizers +make SANITIZE= +``` + +--- + ## Continuous Integration GitHub Actions automatically builds all examples on: diff --git a/common.mk b/common.mk index 79f1337..e1b2520 100644 --- a/common.mk +++ b/common.mk @@ -1,2 +1,48 @@ +# ====================================== +# common.mk - shared compiler flags +# ====================================== + CXX := g++ -CXXFLAGS := -std=c++20 -O2 -Wall -Wextra +OPTFLAGS ?= -O1 -g # Safe for ASAN/TSAN (default) + +# -------------------------- +# Sanitizer selection +# -------------------------- +# Default: AddressSanitizer +SANITIZE ?= address + +ifeq ($(SANITIZE),address) + SAN_FLAGS := -fsanitize=address +else ifeq ($(SANITIZE),thread) + SAN_FLAGS := -fsanitize=thread +else ifeq ($(SANITIZE),undefined) + SAN_FLAGS := -fsanitize=undefined +else ifeq ($(SANITIZE),) + SAN_FLAGS := +else + $(error Unknown SANITIZE=$(SANITIZE)) +endif + +# -------------------------- +# Compiler / Linker flags +# -------------------------- +CXXFLAGS := -std=c++20 -Wall -Wextra $(OPTFLAGS) $(SAN_FLAGS) +LDFLAGS := $(SAN_FLAGS) + +# -------------------------- +# Usage notes +# -------------------------- +# Local build (default): ASAN enabled +# $ make +# +# Override for ThreadSanitizer: +# $ make SANITIZE=thread +# +# Override for UBSAN: +# $ make SANITIZE=undefined +# +# Disable sanitizers (rare): +# $ make SANITIZE= +# +# LDFLAGS are automatically applied in example Makefiles: +# $(CXX) $(CXXFLAGS) -o $(TARGET) $(OBJS) $(LDFLAGS)