From b7dc3dd38d5580bbfb81159273d25d2363d09e3d Mon Sep 17 00:00:00 2001 From: whaffman Date: Wed, 24 Sep 2025 09:57:48 +0200 Subject: [PATCH] feat(iwyu): add scripts for Include What You Use analysis and results review --- .devcontainer/Dockerfile | 1 + .gitignore | 1 + .iwyu.imp | 17 ++++ check_iwyu.sh | 179 +++++++++++++++++++++++++++++++++++++++ fix_iwyu.sh | 78 +++++++++++++++++ 5 files changed, 276 insertions(+) create mode 100644 .iwyu.imp create mode 100755 check_iwyu.sh create mode 100755 fix_iwyu.sh diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 5f54dcb..7d69b20 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -7,6 +7,7 @@ ENV DEBIAN_FRONTEND=noninteractive RUN apt-get update && apt-get install -y \ # Build essentials build-essential \ + # Standard clang tools (this will install the default version) clang \ clang-format \ clang-tidy \ diff --git a/.gitignore b/.gitignore index 2e8fe45..d09646d 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ build-* .cache webserv.log compile_commands.json +iwyu_results/ \ No newline at end of file diff --git a/.iwyu.imp b/.iwyu.imp new file mode 100644 index 0000000..ed824ed --- /dev/null +++ b/.iwyu.imp @@ -0,0 +1,17 @@ +[ + # Standard C++ library mappings + { include: ["", "private", "", "public"] }, + { include: ["", "private", "", "public"] }, + { include: ["", "private", "", "public"] }, + + # System headers + { include: ["", "public", "", "public"] }, + { include: ["", "public", "", "public"] }, + { include: ["", "public", "", "public"] }, + + # Project mappings - adjust these to your actual header structure + { include: ["\"webserv/server/Server.hpp\"", "public", "\"webserv/server/Server.hpp\"", "public"] }, + { include: ["\"webserv/log/Log.hpp\"", "public", "\"webserv/log/Log.hpp\"", "public"] }, + { include: ["\"webserv/socket/Socket.hpp\"", "public", "\"webserv/socket/Socket.hpp\"", "public"] }, + { include: ["\"webserv/client/Client.hpp\"", "public", "\"webserv/client/Client.hpp\"", "public"] } +] \ No newline at end of file diff --git a/check_iwyu.sh b/check_iwyu.sh new file mode 100755 index 0000000..08efe55 --- /dev/null +++ b/check_iwyu.sh @@ -0,0 +1,179 @@ +#!/bin/bash + +# Don't exit on first error - we want to continue checking all files +# set -e + +# Detect project root - try container path first, then current directory +if [ -d "/workspace" ]; then + PROJECT_ROOT="/workspace" +else + PROJECT_ROOT="$(pwd)" +fi + +# Find the build directory - check multiple possible locations +BUILD_DIR="" +echo -e "${BLUE}🔍 Looking for build directory with compile_commands.json...${NC}" +for build_candidate in "$PROJECT_ROOT/build-container" "$PROJECT_ROOT/build-local" "$PROJECT_ROOT/build"; do + echo -e " Checking: $build_candidate" + if [ -d "$build_candidate" ] && [ -f "$build_candidate/compile_commands.json" ]; then + BUILD_DIR="$build_candidate" + echo -e "${GREEN} ✅ Found!${NC}" + break + else + echo -e "${YELLOW} ❌ Not found or no compile_commands.json${NC}" + fi +done + +IWYU_MAPPING="$PROJECT_ROOT/.iwyu.imp" + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +echo -e "${BLUE}🔍 Running Include What You Use Analysis...${NC}" +echo -e "${BLUE}📁 Project root: $PROJECT_ROOT${NC}" + +# Check if IWYU is available (try both common names) +IWYU_CMD="" +if command -v include-what-you-use >/dev/null 2>&1; then + IWYU_CMD="include-what-you-use" + echo -e "${GREEN}✅ Found IWYU as: include-what-you-use${NC}" +elif command -v iwyu >/dev/null 2>&1; then + IWYU_CMD="iwyu" + echo -e "${GREEN}✅ Found IWYU as: iwyu${NC}" +else + echo -e "${RED}❌ IWYU not found. Please install it first.${NC}" + echo -e "${YELLOW}💡 Try: sudo apt install iwyu or yay -S include-what-you-use${NC}" + exit 1 +fi + +echo -e "${BLUE}🛠️ Using IWYU command: $IWYU_CMD${NC}" + +# Check if we found a build directory +if [ -z "$BUILD_DIR" ]; then + echo -e "${YELLOW}⚠️ No build directory with compile_commands.json found.${NC}" + echo -e "${YELLOW}📂 Checked: build-container/, build-local/, build/${NC}" + echo -e "${YELLOW}🔨 Running cmake to create build directory...${NC}" + + cd "$PROJECT_ROOT" + # Try to create build directory (prefer build-container in container, build-local otherwise) + if [ -d "/workspace" ]; then + BUILD_DIR="$PROJECT_ROOT/build-container" + else + BUILD_DIR="$PROJECT_ROOT/build-local" + fi + + mkdir -p "$BUILD_DIR" + cd "$BUILD_DIR" + cmake .. -DCMAKE_EXPORT_COMPILE_COMMANDS=ON +else + echo -e "${GREEN}✅ Found build directory: $BUILD_DIR${NC}" +fi + +# Final check that compile_commands.json exists +if [ ! -f "$BUILD_DIR/compile_commands.json" ]; then + echo -e "${RED}❌ Failed to create compile_commands.json in $BUILD_DIR${NC}" + cd "$PROJECT_ROOT" + cmake -B "$BUILD_DIR" -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=Debug + + # Check again after cmake + if [ ! -f "$BUILD_DIR/compile_commands.json" ]; then + echo -e "${RED}❌ Still no compile_commands.json after cmake. Exiting.${NC}" + exit 1 + fi +fi + +# Create results directory +RESULTS_DIR="$PROJECT_ROOT/iwyu_results" +mkdir -p "$RESULTS_DIR" + +# Function to run IWYU on a single file +run_iwyu_on_file() { + local file="$1" + local relative_path="${file#$PROJECT_ROOT/}" + local output_file="$RESULTS_DIR/$(basename "$file" .cpp).iwyu" + + echo -e "${BLUE}Analyzing: ${relative_path}${NC}" + + # Run IWYU with compile commands + if "$IWYU_CMD" \ + -I"$PROJECT_ROOT" \ + -std=c++20 \ + -Xiwyu --verbose=3 \ + -Xiwyu --quoted_includes_first \ + -Xiwyu --cxx17ns \ + "$file" \ + 2>&1 | tee "$output_file"; then + + # Check if IWYU found issues + if grep -q "should add these lines:" "$output_file" || grep -q "should remove these lines:" "$output_file"; then + echo -e "${YELLOW}⚠️ Issues found in $relative_path${NC}" + return 2 # Issues found (not a script failure) + else + echo -e "${GREEN}✅ $relative_path looks good${NC}" + return 0 # All good + fi + else + local iwyu_exit_code=$? + echo -e "${RED}❌ IWYU failed for $relative_path (exit code: $iwyu_exit_code)${NC}" + return 1 # IWYU execution failed + return 1 + fi +} + +# Find all C++ source files +echo -e "\n${BLUE}Finding C++ source files...${NC}" +cpp_files=() +while IFS= read -r -d '' file; do + cpp_files+=("$file") +done < <(find "$PROJECT_ROOT/webserv" -name "*.cpp" -print0 2>/dev/null) + +if [ ${#cpp_files[@]} -eq 0 ]; then + echo -e "${RED}❌ No .cpp files found in webserv directory${NC}" + exit 1 +fi + +echo -e "${BLUE}Found ${#cpp_files[@]} C++ source files${NC}\n" + +# Run IWYU on all files +issues_found=0 +total_files=${#cpp_files[@]} +current_file=0 + +for file in "${cpp_files[@]}"; do + ((current_file++)) + echo -e "${BLUE}[$current_file/$total_files]${NC}" + + run_iwyu_on_file "$file" + exit_code=$? + + if [ $exit_code -eq 2 ]; then + # Issues found (normal) + ((issues_found++)) + elif [ $exit_code -eq 1 ]; then + # IWYU execution failed (error) + echo -e "${RED}⚠️ IWYU execution error for $(basename "$file")${NC}" + ((issues_found++)) + fi + # exit_code 0 means no issues found + + echo "" +done + +# Summary +echo -e "${BLUE}📊 IWYU Analysis Summary${NC}" +echo -e "Total files analyzed: $total_files" +echo -e "Files with issues: $issues_found" +echo -e "Results saved in: $RESULTS_DIR" + +if [ $issues_found -eq 0 ]; then + echo -e "${GREEN}🎉 All files have proper includes!${NC}" + exit 0 +else + echo -e "${YELLOW}⚠️ $issues_found files need attention${NC}" + echo -e "${BLUE}💡 Run './fix_iwyu.sh' to review suggested fixes${NC}" + exit 0 # Don't fail the script for include suggestions +fi \ No newline at end of file diff --git a/fix_iwyu.sh b/fix_iwyu.sh new file mode 100755 index 0000000..eb9ff5e --- /dev/null +++ b/fix_iwyu.sh @@ -0,0 +1,78 @@ +#!/bin/bash + +set -e + +PROJECT_ROOT="/workspace" +RESULTS_DIR="$PROJECT_ROOT/iwyu_results" + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +echo -e "${BLUE}🔧 Reviewing IWYU fixes...${NC}" + +if [ ! -d "$RESULTS_DIR" ]; then + echo -e "${RED}❌ No IWYU results found. Run './check_iwyu.sh' first.${NC}" + exit 1 +fi + +# Check if there are any result files +result_files=("$RESULTS_DIR"/*.iwyu) +if [ ! -f "${result_files[0]}" ]; then + echo -e "${YELLOW}⚠️ No IWYU result files found.${NC}" + exit 1 +fi + +echo -e "${BLUE}💡 IWYU Analysis Results - Manual Review Required${NC}" +echo -e "${YELLOW}Note: Automatic fixing requires careful review before applying changes.${NC}\n" + +files_with_issues=0 + +for result_file in "$RESULTS_DIR"/*.iwyu; do + if [ -f "$result_file" ]; then + filename=$(basename "$result_file" .iwyu) + + # Check if this file has suggestions + if grep -q "should add these lines:\|should remove these lines:" "$result_file"; then + ((files_with_issues++)) + + echo -e "${BLUE}=== $filename.cpp ===${NC}" + + # Show additions + if grep -q "should add these lines:" "$result_file"; then + echo -e "${GREEN}📥 Suggested additions:${NC}" + sed -n '/should add these lines:/,/^$/p' "$result_file" | grep -v "should add these lines:" | head -20 + echo "" + fi + + # Show removals + if grep -q "should remove these lines:" "$result_file"; then + echo -e "${RED}📤 Suggested removals:${NC}" + sed -n '/should remove these lines:/,/^$/p' "$result_file" | grep -v "should remove these lines:" | head -20 + echo "" + fi + + # Show full analysis (first 30 lines for context) + echo -e "${BLUE}📋 Full analysis:${NC}" + head -30 "$result_file" + echo -e "${YELLOW}... (see $result_file for complete output)${NC}" + echo -e "${BLUE}${'='*60}${NC}\n" + fi + fi +done + +if [ $files_with_issues -eq 0 ]; then + echo -e "${GREEN}🎉 No issues found in any analyzed files!${NC}" +else + echo -e "${YELLOW}📊 Summary: $files_with_issues files have suggested changes${NC}" + echo -e "${BLUE}💡 Tips for applying fixes:${NC}" + echo -e " • Review each suggestion carefully" + echo -e " • Test compilation after each change" + echo -e " • Some suggestions might be false positives" + echo -e " • Consider project-specific header policies" + echo "" + echo -e "${BLUE}🗂️ Detailed results available in: $RESULTS_DIR${NC}" +fi \ No newline at end of file