# Compiler & Toolchain Configuration This document explains how CppLab IDE manages MinGW toolchains and compiler configuration. ## Overview CppLab IDE bundles two complete MinGW toolchains: | Toolchain | Bits | Purpose | Compiler | Libraries | |-----------|------|---------|----------|-----------| | **mingw32** | 32-bit | Graphics (graphics.h) | GCC 8.1.0+ | WinBGIm, GDI32 | | **mingw64** | 64-bit | OpenMP, Modern C++ | GCC 8.1.0+ | libgomp, pthread | ## Directory Structure ``` compilers/ ├── mingw32/ # 32-bit toolchain │ ├── bin/ │ │ ├── gcc.exe # C compiler │ │ ├── g++.exe # C++ compiler │ │ ├── gdb.exe # Debugger (future) │ │ └── *.dll # Runtime libraries │ ├── include/ │ │ ├── graphics.h # WinBGIm header │ │ ├── winbgim.h │ │ └── ... # Standard headers │ ├── lib/ │ │ ├── libbgi.a # WinBGIm library │ │ ├── libgdi32.a # Windows GDI │ │ └── ... │ └── libexec/ │ └── gcc/ │ └── ... # GCC internals │ └── mingw64/ # 64-bit toolchain ├── bin/ │ ├── gcc.exe │ ├── g++.exe │ └── ... ├── include/ │ ├── omp.h # OpenMP header │ └── ... ├── lib/ │ ├── libgomp.a # OpenMP library │ ├── libpthread.a │ └── ... └── libexec/ └── ... ``` ## Toolchain Discovery ### Initialization **File**: `src/cpplab/core/toolchains.py` ```python def get_toolchains() -> dict: """Discover and return available MinGW toolchains.""" app_root = get_app_root() compilers_dir = app_root / "compilers" toolchains = {} # Check mingw32 mingw32_path = compilers_dir / "mingw32" if mingw32_path.exists(): gcc_path = mingw32_path / "bin" / "gcc.exe" gpp_path = mingw32_path / "bin" / "g++.exe" if gcc_path.exists() and gpp_path.exists(): toolchains["mingw32"] = Toolchain( name="mingw32", root=mingw32_path, gcc=gcc_path, gpp=gpp_path, bits=32 ) # Check mingw64 mingw64_path = compilers_dir / "mingw64" if mingw64_path.exists(): gcc_path = mingw64_path / "bin" / "gcc.exe" gpp_path = mingw64_path / "bin" / "g++.exe" if gcc_path.exists() and gpp_path.exists(): toolchains["mingw64"] = Toolchain( name="mingw64", root=mingw64_path, gcc=gcc_path, gpp=gpp_path, bits=64 ) return toolchains ``` ### Toolchain Data Structure ```python @dataclass class Toolchain: name: str # "mingw32" or "mingw64" root: Path # C:/path/to/CppLabIDE/compilers/mingw32 gcc: Path # Path to gcc.exe gpp: Path # Path to g++.exe bits: int # 32 or 64 def is_available(self) -> bool: return self.gcc.exists() and self.gpp.exists() ``` ## Toolchain Selection Logic ### Decision Tree ``` ┌─────────────────────────────────────┐ │ Project/File Configuration │ └─────────────┬───────────────────────┘ │ ↓ ┌─────────────────┐ │ graphics=true? │ └────┬────────────┘ │ YES │ NO │ ┌────↓────┐ ┌──────────────┐ │ mingw32 │ │ openmp=true? │ └─────────┘ └──────┬───────┘ │ YES │ NO │ ┌────↓────┐ ┌──────────┐ │ mingw64 │ │ mingw64 │ └─────────┘ └──────────┘ (default) ``` ### Implementation **File**: `src/cpplab/core/toolchains.py` ```python def select_toolchain(config: ProjectConfig, toolchains: dict) -> Toolchain: """Select appropriate toolchain based on project features.""" # User override takes precedence if config.toolchain_preference != "auto": return toolchains.get(config.toolchain_preference) # Graphics requires 32-bit if config.features.get("graphics", False): return toolchains["mingw32"] # OpenMP prefers 64-bit if config.features.get("openmp", False): return toolchains["mingw64"] # Default to 64-bit return toolchains["mingw64"] ``` ### Why This Logic? 1. **Graphics → 32-bit**: WinBGIm library is 32-bit only 2. **OpenMP → 64-bit**: Better performance, more memory 3. **Default → 64-bit**: Modern systems, better performance ## Compiler Configuration ### Language Standards #### C Standards ```python C_STANDARDS = { "c99": "-std=c99", "c11": "-std=c11", "c17": "-std=c17", "c18": "-std=c18", # Alias for c17 "c23": "-std=c23", # Experimental } ``` #### C++ Standards ```python CPP_STANDARDS = { "c++11": "-std=c++11", "c++14": "-std=c++14", "c++17": "-std=c++17", "c++20": "-std=c++20", "c++23": "-std=c++23", # Experimental } ``` ### Compiler Flags #### Base Flags (Always Included) ```bash -Wall # Enable all warnings -Wextra # Extra warnings -o # Output file ``` #### Optional Flags **Graphics.h Projects** (32-bit): ```bash -lbgi # WinBGIm library -lgdi32 # Windows GDI -lcomdlg32 # Windows dialogs -luuid # Windows UUID -lole32 # Windows OLE -loleaut32 # Windows OLE Automation ``` **OpenMP Projects** (64-bit): ```bash -fopenmp # Enable OpenMP ``` **Debug Mode** (future): ```bash -g # Include debug symbols -O0 # No optimization ``` **Release Mode** (future): ```bash -O2 # Optimize for speed -DNDEBUG # Disable assertions ``` ## Build Command Construction ### Example: Graphics Project **Input:** ```python ProjectConfig( name="CircleDemo", language="cpp", standard="c++17", features={"graphics": True, "openmp": False}, files=["src/main.cpp"], main_file="src/main.cpp" ) ``` **Output Command:** ```bash C:/CppLabIDE/compilers/mingw32/bin/g++.exe \ src/main.cpp \ -std=c++17 \ -Wall \ -Wextra \ -o build/CircleDemo.exe \ -lbgi \ -lgdi32 \ -lcomdlg32 \ -luuid \ -lole32 \ -loleaut32 ``` ### Example: OpenMP Project **Input:** ```python ProjectConfig( name="ParallelSum", language="cpp", standard="c++20", features={"graphics": False, "openmp": True}, files=["src/main.cpp"], main_file="src/main.cpp" ) ``` **Output Command:** ```bash C:/CppLabIDE/compilers/mingw64/bin/g++.exe \ src/main.cpp \ -std=c++20 \ -Wall \ -Wextra \ -fopenmp \ -o build/ParallelSum.exe ``` ### Example: Plain Console Project **Input:** ```python ProjectConfig( name="HelloWorld", language="c", standard="c17", features={"graphics": False, "openmp": False}, files=["src/main.c"], main_file="src/main.c" ) ``` **Output Command:** ```bash C:/CppLabIDE/compilers/mingw64/bin/gcc.exe \ src/main.c \ -std=c17 \ -Wall \ -Wextra \ -o build/HelloWorld.exe ``` ## Auto-Detection ### Feature Detection from Source Code **File**: `src/cpplab/core/builder.py` ```python def detect_features_from_source(source_path: Path) -> dict: """Detect graphics.h and OpenMP usage by scanning source code.""" features = {"graphics": False, "openmp": False} try: with open(source_path, 'r', encoding='utf-8', errors='ignore') as f: content = f.read() # Check for graphics.h if '#include' in content and 'graphics.h' in content: features["graphics"] = True # Check for OpenMP if '#pragma' in content and 'omp' in content: features["openmp"] = True except Exception: pass return features ``` ### Standalone File Workflow ``` 1. User opens: test.cpp 2. User presses F7 (Build) 3. detect_features_from_source(test.cpp) → Scans for #include → Scans for #pragma omp 4. Select toolchain based on features 5. Build with appropriate flags ``` ## Toolchain Verification ### Startup Check **Location**: `src/cpplab/app.py` → `_check_toolchains()` ```python def _check_toolchains(self): """Check if toolchains are available and show warning if not.""" self.toolchains = get_toolchains() mingw32 = self.toolchains.get("mingw32") mingw64 = self.toolchains.get("mingw64") missing = [] if not mingw32 or not mingw32.is_available(): missing.append("mingw32 (32-bit, required for graphics.h)") if not mingw64 or not mingw64.is_available(): missing.append("mingw64 (64-bit, required for OpenMP)") if missing: msg = ( "

Toolchains Not Found

" "

The following MinGW toolchains are missing:

" "" "

Expected location: compilers/ directory

" "

Building will not work until toolchains are installed.

" ) QMessageBox.warning(self, "Toolchains Missing", msg) ``` ### User Feedback If toolchains are missing: ``` ┌─────────────────────────────────────────┐ │ ⚠ Toolchains Not Found │ ├─────────────────────────────────────────┤ │ The following MinGW toolchains are │ │ missing: │ │ │ │ • mingw32 (32-bit, required for │ │ graphics.h) │ │ • mingw64 (64-bit, required for │ │ OpenMP) │ │ │ │ Expected location: compilers/ directory │ │ │ │ Building will not work until toolchains │ │ are installed. │ └─────────────────────────────────────────┘ ``` ## Frozen Mode Support ### Challenge - **Development**: `compilers/` relative to source code - **Frozen**: `compilers/` relative to .exe ### Solution ```python def get_app_root() -> Path: """Get application root directory (works in dev and frozen modes).""" if getattr(sys, 'frozen', False): # Running as PyInstaller bundle return Path(sys._MEIPASS).parent else: # Running from source return Path(__file__).parent.parent.parent ``` **Usage:** ```python app_root = get_app_root() # Dev: C:/Users/Dev/CppLabEngine # Frozen: C:/Program Files/CppLabIDE compilers_dir = app_root / "compilers" # Dev: C:/Users/Dev/CppLabEngine/compilers # Frozen: C:/Program Files/CppLabIDE/compilers ``` ## Toolchain UI ### Toolbar Combo Box **Location**: `src/cpplab/app.py` → `_setup_combo_boxes()` ```python self.toolchainComboBox = QComboBox() self.toolchainComboBox.addItem("Auto", "auto") self.toolchainComboBox.addItem("64-bit (mingw64)", "mingw64") self.toolchainComboBox.addItem("32-bit (mingw32)", "mingw32") ``` **Behavior:** - **Auto**: Selects based on project features (default) - **64-bit**: Forces mingw64 (even for graphics - may fail) - **32-bit**: Forces mingw32 (even for OpenMP - slower) ### User Override Users can override toolchain selection: ```python def on_toolchain_changed(self, index: int): """Handle toolchain combo box selection change.""" toolchain_map = {0: "auto", 1: "mingw64", 2: "mingw32"} value = toolchain_map.get(index, "auto") if self.current_project: self.current_project.toolchain_preference = value self.current_project.save() # Persist to JSON else: self.standalone_toolchain_preference = value ``` ## Troubleshooting ### Toolchains Not Detected **Symptom**: Warning on startup "Toolchains Not Found" **Causes:** 1. Missing `compilers/` directory 2. Missing `mingw32/` or `mingw64/` subdirectories 3. Missing `bin/gcc.exe` or `bin/g++.exe` **Solution:** 1. Check directory structure matches expected layout 2. Verify compiler executables exist and are not corrupted 3. Re-extract toolchains from release package ### Build Fails with "No such file or directory" **Symptom**: Build error: `gcc.exe: error: CreateProcess: No such file or directory` **Cause**: Toolchain path contains spaces or special characters **Solution**: Install CppLabIDE to path without spaces (e.g., `C:\CppLabIDE`) ### Graphics Build Fails on 64-bit **Symptom**: Linker error: `cannot find -lbgi` **Cause**: Graphics project trying to use mingw64 (64-bit) **Solution:** 1. Check project configuration: `"features": {"graphics": true}` 2. Set toolchain to "Auto" or "32-bit" explicitly 3. Rebuild project ## Performance Considerations ### Toolchain Caching Toolchains discovered once at startup: ```python # MainWindow.__init__ self.toolchains = get_toolchains() # Cached for session ``` **Benefit**: No repeated filesystem checks during builds ### Path Resolution Absolute paths used throughout: ```python # Bad: Relative path (requires working directory) cmd = ["gcc", "main.c"] # Good: Absolute path (works from any directory) cmd = [str(toolchain.gcc), "main.c"] ``` **Benefit**: Builds work regardless of current directory --- **Next**: [Language Standards Support](Language-Standards-Support.md) **Previous**: [Asynchronous Build System](Asynchronous-Build-System.md)