251 lines
8.3 KiB
Makefile
251 lines
8.3 KiB
Makefile
SHELL = /bin/bash
|
|
|
|
CXX = g++
|
|
# CXX = clang++
|
|
|
|
SRC_DIR = src
|
|
GEN_DIR = generator
|
|
VAL_DIR = validator
|
|
INC_DIR = include
|
|
CHK_DIR = checker
|
|
|
|
TEST_DIR = src/test
|
|
|
|
BUILD_DIR = build
|
|
BUILD_TARGET_DIR = $(BUILD_DIR)/target
|
|
BUILD_GEN_DIR = $(BUILD_DIR)/gen
|
|
BUILD_VAL_DIR = $(BUILD_DIR)/val
|
|
BUILD_CHK_DIR = $(BUILD_DIR)/chk
|
|
BUILD_TEST_DIR = $(BUILD_DIR)/test
|
|
LOG_DIR = $(BUILD_DIR)/logs
|
|
|
|
BUILD_IN_DIR = $(BUILD_DIR)/input
|
|
BUILD_OUT_A_DIR = $(BUILD_DIR)/$(SOL_A)_out
|
|
BUILD_OUT_B_DIR = $(BUILD_DIR)/$(SOL_B)_out
|
|
|
|
|
|
DIRS = $(BUILD_DIR) $(BUILD_TARGET_DIR) $(BUILD_GEN_DIR) $(BUILD_VAL_DIR) $(BUILD_CHK_DIR) $(LOG_DIR) $(BUILD_IN_DIR) $(BUILD_OUT_A_DIR) $(BUILD_OUT_B_DIR) $(BUILD_TEST_DIR)
|
|
|
|
CXXFLAGS = -Wall -O2 -std=c++2a
|
|
GENFLAGS = -N $(N) -M $(M) -pm $(pm)
|
|
|
|
SRC_MAIN_FILES = $(wildcard $(SRC_DIR)/*/main.cpp)
|
|
SOLS_EXE = $(patsubst $(SRC_DIR)/%/main.cpp, $(BUILD_TARGET_DIR)/%, $(SRC_MAIN_FILES))
|
|
|
|
SOL_A ?= bsb
|
|
SOL_B ?= dijk
|
|
SOL_A_EXE = $(BUILD_TARGET_DIR)/$(SOL_A)
|
|
SOL_B_EXE = $(BUILD_TARGET_DIR)/$(SOL_B)
|
|
|
|
GEN_SRCS = $(wildcard $(GEN_DIR)/*.cpp)
|
|
GENS_EXE = $(patsubst $(GEN_DIR)/%.cpp, $(BUILD_GEN_DIR)/%, $(GEN_SRCS))
|
|
|
|
gen ?= gen1
|
|
GEN_TO_RUN = $(BUILD_GEN_DIR)/$(gen)
|
|
|
|
VAL_EXE = $(BUILD_VAL_DIR)/validator
|
|
CHK_EXE = $(BUILD_CHK_DIR)/checker
|
|
|
|
PADDED_ID = $(shell printf "%05d" $(id))
|
|
IN_FILE = $(BUILD_IN_DIR)/input$(PADDED_ID)
|
|
OUT_A_FILE = $(BUILD_OUT_A_DIR)/$(SOL_A)$(PADDED_ID).out
|
|
OUT_B_FILE = $(BUILD_OUT_B_DIR)/$(SOL_B)$(PADDED_ID).out
|
|
|
|
TEST_COUNT ?= 100
|
|
N ?= 1000
|
|
M ?= 5000
|
|
pm ?= 0.1
|
|
|
|
.PHONY: all
|
|
all: $(SOLS_EXE) $(GENS_EXE) $(VAL_EXE) $(CHK_EXE) $(DIRS)
|
|
@echo "All executables compiled in $(BUILD_DIR)/"
|
|
|
|
$(BUILD_DIR) $(BUILD_TARGET_DIR) $(BUILD_GEN_DIR) $(BUILD_VAL_DIR) $(BUILD_CHK_DIR) $(LOG_DIR) $(BUILD_IN_DIR) $(BUILD_OUT_A_DIR) $(BUILD_OUT_B_DIR) $(BUILD_TEST_DIR):
|
|
@echo "Creating directory: $@"
|
|
@mkdir -p $@
|
|
|
|
$(BUILD_TARGET_DIR)/%: $(SRC_DIR)/%/main.cpp | $(BUILD_TARGET_DIR)
|
|
@echo "Compiling Solution $< -> $@"
|
|
$(CXX) $(CXXFLAGS) -fsanitize=undefined,address -o $@ $<
|
|
|
|
$(BUILD_GEN_DIR)/%: $(GEN_DIR)/%.cpp | $(BUILD_GEN_DIR)
|
|
@echo "Compiling Generator $< -> $@"
|
|
$(CXX) $(CXXFLAGS) -I$(INC_DIR) -o $@ $<
|
|
|
|
$(BUILD_VAL_DIR)/%: $(VAL_DIR)/%.cpp | $(BUILD_VAL_DIR)
|
|
@echo "Compiling Validator $< -> $@"
|
|
$(CXX) $(CXXFLAGS) -I$(INC_DIR) -o $@ $<
|
|
|
|
$(BUILD_CHK_DIR)/%: $(CHK_DIR)/%.cpp | $(BUILD_CHK_DIR)
|
|
@echo "Compiling Checker $< -> $@"
|
|
$(CXX) $(CXXFLAGS) -I$(INC_DIR) -o $@ $<
|
|
|
|
.PRECIOUS: $(BUILD_IN_DIR)/input%
|
|
$(BUILD_IN_DIR)/input%: $(GEN_TO_RUN) | $(BUILD_IN_DIR)
|
|
@echo "--- Generating Input: $@ (Using [$(gen)]) ---"
|
|
@SEED=$$(export LC_ALL=C; cat /dev/urandom | tr -cd 'a-zA-Z0-9' | head -c 32); \
|
|
echo "Using Seed: $$SEED" >&2; \
|
|
./$(GEN_TO_RUN) $(GENFLAGS) "$$SEED" > $@
|
|
|
|
$(BUILD_TEST_DIR)/%: $(TEST_DIR)/%.cpp | $(BUILD_TEST_DIR)
|
|
@echo "Compiling Unit Test $< -> $@"
|
|
$(CXX) $(CXXFLAGS) -I$(INC_DIR) -I$(SRC_DIR)/bsb -o $@ $< -lgtest -lgtest_main -pthread
|
|
|
|
.PHONY: force
|
|
force:
|
|
|
|
$(BUILD_OUT_A_DIR)/$(SOL_A)%.out: $(BUILD_IN_DIR)/input% $(SOL_A_EXE) force | $(BUILD_OUT_A_DIR)
|
|
@echo "--- Running $(SOL_A) (ID: $*) ---"
|
|
@time ./$(SOL_A_EXE) < $< > $@
|
|
|
|
$(BUILD_OUT_B_DIR)/$(SOL_B)%.out: $(BUILD_IN_DIR)/input% $(SOL_B_EXE) force | $(BUILD_OUT_B_DIR)
|
|
@echo "--- Running $(SOL_B) (ID: $*) ---"
|
|
@time ./$(SOL_B_EXE) < $< > $@
|
|
|
|
.PHONY: gen
|
|
gen: all
|
|
ifeq ($(id),)
|
|
@echo "Error: 'make gen' requires an 'id' variable."
|
|
@echo "Usage: make gen id=<test_id> [gen=gen_name]"
|
|
@exit 1
|
|
else
|
|
@$(MAKE) --no-print-directory $(IN_FILE) id=$(id) gen=$(gen) N=$(N) M=$(M)
|
|
@echo "--- Generated $(IN_FILE) ---"
|
|
endif
|
|
|
|
# # 레거시 gen1 타겟
|
|
# .PHONY: gen1
|
|
# gen1: $(BUILD_GEN_DIR)/gen1
|
|
# @echo "--- gen1 : Generating 1 test case (DEPRECATED: use 'make gen id=1') ---"
|
|
# @SEED=$$(export LC_ALL=C; cat /dev/urandom | tr -cd 'a-zA-Z0-9' | head -c 32); \
|
|
# echo "./$(BUILD_GEN_DIR)/gen1 -N $(N) -M $(M) $$SEED | tee $(BUILD_IN_DIR)/input_legacy"; \
|
|
# ./$(BUILD_GEN_DIR)/gen1 -N $(N) -M $(M) $$SEED | tee $(BUILD_IN_DIR)/input_legacy
|
|
|
|
# # 레거시 gen1-silence 타겟
|
|
# .PHONY: gen1-silence
|
|
# gen1-silence: $(BUILD_GEN_DIR)/gen1
|
|
# @echo "--- gen1 : Generating 1 test case (DEPRECATED: use 'make gen id=1') ---"
|
|
# @SEED=$$(export LC_ALL=C; cat /dev/urandom | tr -cd 'a-zA-Z0-9' | head -c 32); \
|
|
# ./$(BUILD_GEN_DIR)/gen1 -N $(N) -M $(M) "$$SEED" > $(BUILD_IN_DIR)/input_legacy
|
|
|
|
.PHONY: val
|
|
val: all $(VAL_EXE)
|
|
ifeq ($(id),)
|
|
@echo "Error: 'make val' requires an 'id' variable."
|
|
@echo "Usage: make val id=<test_id>"
|
|
@exit 1
|
|
else
|
|
@$(MAKE) --no-print-directory $(IN_FILE) id=$(id) gen=$(gen) N=$(N) M=$(M)
|
|
@echo "--- Validating $(IN_FILE) (ID=$(id)) ---"
|
|
@./$(VAL_EXE) < $(IN_FILE)
|
|
@echo "Validator OK."
|
|
endif
|
|
|
|
.PHONY: check
|
|
check: all $(CHK_EXE)
|
|
ifeq ($(id),)
|
|
@echo "Error: 'make check' requires an 'id' variable."
|
|
@echo "Usage: make check id=<test_id>"
|
|
@exit 1
|
|
else
|
|
@echo "--- Checking ID=$(id) ---"
|
|
@$(MAKE) --no-print-directory $(OUT_A_FILE) id=$(id) gen=$(gen) SOL_A=$(SOL_A) GENARGS=$(GENARGS)
|
|
@$(MAKE) --no-print-directory $(OUT_B_FILE) id=$(id) gen=$(gen) SOL_B=$(SOL_B) GENARGS=$(GENARGS)
|
|
@echo "--- Comparing Outputs (ID=$(id)) ---"
|
|
@./$(CHK_EXE) $(IN_FILE) $(OUT_A_FILE) $(OUT_B_FILE) || ( \
|
|
echo "WA (Wrong Answer): Checker found an issue!" && \
|
|
echo "Input: $(IN_FILE)" && \
|
|
exit 1 \
|
|
)
|
|
@echo "OK: ID=$(id) outputs match!"
|
|
endif
|
|
|
|
UNIT_TEST_SRCS = $(wildcard $(TEST_DIR)/*.cpp)
|
|
UNIT_TEST_NAMES = $(patsubst $(TEST_DIR)/%.cpp, %, $(UNIT_TEST_SRCS))
|
|
|
|
.PHONY: test
|
|
test: all $(LOG_DIR) $(BUILD_OUT_A_DIR) $(BUILD_OUT_B_DIR)
|
|
ifdef target
|
|
@# Case 1: Unit Test 실행 (예: make test target=L33)
|
|
@echo "--- Running Unit Test Target: [$(target)] ---"
|
|
@$(MAKE) --no-print-directory $(BUILD_TEST_DIR)/$(target)
|
|
@echo "-------------------------------------------"
|
|
@./$(BUILD_TEST_DIR)/$(target)
|
|
else ifeq ($(id),)
|
|
@echo "--- Running $(TEST_COUNT) tests (Comparing $(SOL_A) vs $(SOL_B)) ---"
|
|
@echo "--- Using Generator [$(gen)] (N=$(N), M=$(M)) ---"
|
|
@LOG_FILE=$(LOG_DIR)/test_run_$$(date +%Y%m%d_%H%M%S).log; \
|
|
echo "--- Logging to $${LOG_FILE} ---"; \
|
|
script -q -c ' \
|
|
set -o pipefail; \
|
|
for i in {1..$(TEST_COUNT)}; do \
|
|
IN_FILE_LOOP=$(BUILD_IN_DIR)/input$$(printf "%05d" $$i); \
|
|
OUT_A_FILE_LOOP=$(BUILD_OUT_A_DIR)/$(SOL_A)$$(printf "%05d" $$i).out; \
|
|
OUT_B_FILE_LOOP=$(BUILD_OUT_B_DIR)/$(SOL_B)$$(printf "%05d" $$i).out; \
|
|
\
|
|
if [ ! -f $$IN_FILE_LOOP ]; then \
|
|
SEED=$$(export LC_ALL=C; cat /dev/urandom | tr -cd "a-zA-Z0-9" | head -c 32); \
|
|
printf "Test %4d (Seed: %s): " "$$i" "$$SEED"; \
|
|
./$(GEN_TO_RUN) -N $(N) -M $(M) "$$SEED" > $$IN_FILE_LOOP; \
|
|
printf "(GEN) "; \
|
|
else \
|
|
printf "Test %4d (Seed: EXISTING): " "$$i"; \
|
|
fi; \
|
|
\
|
|
printf "$(SOL_A): "; \
|
|
( TIMEFORMAT=%R; time ./$(SOL_A_EXE) < $$IN_FILE_LOOP > $$OUT_A_FILE_LOOP ) 2>&1 | tr "\n" " " ; \
|
|
printf "$(SOL_B): "; \
|
|
( TIMEFORMAT=%R; time ./$(SOL_B_EXE) < $$IN_FILE_LOOP > $$OUT_B_FILE_LOOP ) 2>&1 | tr "\n" " " ; \
|
|
printf "\t CHK: "; \
|
|
( ./$(CHK_EXE) $$IN_FILE_LOOP $$OUT_A_FILE_LOOP $$OUT_B_FILE_LOOP ) 2>&1 || { printf "\nWA! (Input: $$IN_FILE_LOOP)\n"; echo "Input saved to $$IN_FILE_LOOP"; exit 1; }; \
|
|
done; \
|
|
echo "--- All $(TEST_COUNT) tests passed! ---"; \
|
|
' /dev/null | tee $${LOG_FILE}; \
|
|
if [ $${PIPESTATUS[0]} -ne 0 ]; then \
|
|
printf "\n--- TESTS FAILED! See %s ---\n" "$${LOG_FILE}"; \
|
|
exit 1; \
|
|
fi
|
|
else
|
|
@echo "--- Running single test for ID=$(id) ---"
|
|
@$(MAKE) --no-print-directory check id=$(id) gen=$(gen) SOL_A=$(SOL_A) SOL_B=$(SOL_B) N=$(N) M=$(M)
|
|
endif
|
|
|
|
.PHONY: $(UNIT_TEST_NAMES)
|
|
$(UNIT_TEST_NAMES):
|
|
@$(MAKE) --no-print-directory test target=$@
|
|
|
|
.PHONY: bench
|
|
bench: all
|
|
ifeq ($(id),)
|
|
@echo "Error: 'make bench' requires an 'id' variable."
|
|
@exit 1
|
|
else
|
|
@$(MAKE) --no-print-directory $(IN_FILE) id=$(id) gen=$(gen) N=$(N) M=$(M)
|
|
@echo "--- Benchmarking $(SOL_A_EXE) with $(IN_FILE) (ID=$(id)) ---"
|
|
@time ./$(SOL_A_EXE) < $(IN_FILE) > $(OUT_A_FILE)
|
|
endif
|
|
|
|
.PHONY: format
|
|
format:
|
|
@echo "--- Formatting all C/C++ files ---"
|
|
@find $(SRC_DIR) $(GEN_DIR) $(VAL_DIR) $(CHK_DIR) $(INC_DIR) -type f \( -name "*.cpp" -o -name "*.c" -o -name "*.hpp" -o -name "*.h" \) -print0 | xargs -0 clang-format -i
|
|
@echo "Formatting complete."
|
|
|
|
.PHONY: clean-outputs
|
|
clean-outputs:
|
|
@echo "Cleaning up $(BUILD_OUT_A_DIR)/ and $(BUILD_OUT_B_DIR)/..."
|
|
@rm -rf $(BUILD_OUT_A_DIR) $(BUILD_OUT_B_DIR)
|
|
|
|
.PHONY: clean-inputs
|
|
clean-inputs:
|
|
@echo "Cleaning up $(BUILD_IN_DIR)/..."
|
|
@rm -rf $(BUILD_IN_DIR)
|
|
|
|
.PHONY: clean-tests
|
|
clean-tests: clean-inputs clean-outputs
|
|
|
|
.PHONY: clean
|
|
clean:
|
|
@echo "Cleaning up $(BUILD_DIR)/..."
|
|
@rm -rf $(BUILD_DIR)
|