Created
November 1, 2025 05:45
-
-
Save hbina/e56dff3be980004187b275d85f15f34f to your computer and use it in GitHub Desktop.
Python script to manage docker builds
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python3 | |
| """ | |
| Docker management script for GCC 15.2.0 builder. | |
| Replaces docker-compose with simple Python commands. | |
| """ | |
| import subprocess | |
| import sys | |
| import argparse | |
| IMAGE_NAME = "gcc15-builder:latest" | |
| VOLUMES = ["gcc-build", "gcc-install", "yaml-cpp-build"] | |
| MEMORY_LIMIT = "8g" | |
| SHM_SIZE = "2g" | |
| PROJECT_DIR = "/yaml-cpp" | |
| def run_command(cmd, check=True, stream_output=False): | |
| """Run a shell command and handle errors.""" | |
| print(f"Running: {' '.join(cmd)}") | |
| try: | |
| if stream_output: | |
| # Stream output in real-time for long-running commands | |
| result = subprocess.run(cmd, check=check, text=True) | |
| return result.returncode == 0 | |
| else: | |
| result = subprocess.run(cmd, check=check, text=True, capture_output=True) | |
| if result.stdout: | |
| print(result.stdout) | |
| return result.returncode == 0 | |
| except subprocess.CalledProcessError as e: | |
| if hasattr(e, 'stderr') and e.stderr: | |
| print(f"Error: {e.stderr}", file=sys.stderr) | |
| if check: | |
| sys.exit(1) | |
| return False | |
| def build(): | |
| """Build the Docker image.""" | |
| print("Building Docker image...") | |
| cmd = ["docker", "build", "-t", IMAGE_NAME, "."] | |
| run_command(cmd) | |
| print(f"✓ Image '{IMAGE_NAME}' built successfully") | |
| def create_volumes(): | |
| """Create Docker volumes if they don't exist.""" | |
| for volume in VOLUMES: | |
| # Check if volume exists | |
| check_cmd = ["docker", "volume", "inspect", volume] | |
| exists = run_command(check_cmd, check=False) | |
| if not exists: | |
| print(f"Creating volume '{volume}'...") | |
| create_cmd = ["docker", "volume", "create", volume] | |
| run_command(create_cmd) | |
| else: | |
| print(f"Volume '{volume}' already exists") | |
| def run(): | |
| """Run the GCC build non-interactively inside the container.""" | |
| # Create volumes first | |
| create_volumes() | |
| print("Starting GCC compilation in container...") | |
| print("This will take 30-60 minutes depending on your CPU.") | |
| print("Output will be streamed in real-time.\n") | |
| cmd = [ | |
| "docker", "run", | |
| "--rm", # Remove container when it exits | |
| "-v", f"{VOLUMES[0]}:/gcc-build", | |
| "-v", f"{VOLUMES[1]}:/gcc-install", | |
| "-w", "/gcc-build", | |
| "--memory", MEMORY_LIMIT, | |
| "--shm-size", SHM_SIZE, | |
| IMAGE_NAME, | |
| "/home/builder/build-gcc.sh" # Run the build script automatically | |
| ] | |
| # Stream output in real-time | |
| run_command(cmd, stream_output=True) | |
| print("\n✓ Build complete! GCC installed to gcc-install volume") | |
| print("Run './docker-manager.py test' to verify the installation") | |
| def clean(): | |
| """Remove Docker volumes.""" | |
| print("Removing Docker volumes...") | |
| for volume in VOLUMES: | |
| # Check if volume exists before trying to remove | |
| check_cmd = ["docker", "volume", "inspect", volume] | |
| exists = run_command(check_cmd, check=False) | |
| if exists: | |
| print(f"Removing volume '{volume}'...") | |
| remove_cmd = ["docker", "volume", "rm", volume] | |
| run_command(remove_cmd) | |
| else: | |
| print(f"Volume '{volume}' does not exist, skipping") | |
| print("✓ Cleanup complete") | |
| def test(): | |
| """Test the built GCC by running gcc --version.""" | |
| print("Testing GCC installation...") | |
| cmd = [ | |
| "docker", "run", | |
| "--rm", | |
| "-v", f"{VOLUMES[1]}:/gcc-install", | |
| IMAGE_NAME, | |
| "/gcc-install/bin/gcc", "--version" | |
| ] | |
| run_command(cmd, stream_output=True) | |
| def build_yaml_cpp(): | |
| """Build yaml-cpp project using the built GCC 15.""" | |
| # Create volumes first | |
| create_volumes() | |
| print("Building yaml-cpp with GCC 15...") | |
| print("This will compile the yaml-cpp library and tools.\\n") | |
| # Get the current directory (yaml-cpp source) | |
| import os | |
| yaml_cpp_source = os.path.abspath(os.path.dirname(__file__)) | |
| cmd = [ | |
| "docker", "run", | |
| "--rm", | |
| "--user", "root", # Run as root to fix permissions | |
| "-v", f"{VOLUMES[1]}:/gcc-install", # GCC installation | |
| "-v", f"{VOLUMES[2]}:/yaml-cpp-build", # yaml-cpp build output | |
| "-v", f"{yaml_cpp_source}:{PROJECT_DIR}:ro", # yaml-cpp source (read-only) | |
| "-w", "/yaml-cpp-build", | |
| "--memory", MEMORY_LIMIT, | |
| "--shm-size", SHM_SIZE, | |
| IMAGE_NAME, | |
| "/bin/bash", "-c", | |
| """ | |
| # Fix ownership of build directory | |
| chown -R builder:builder /yaml-cpp-build | |
| # Switch to builder user and run the build | |
| su - builder -c ' | |
| export PATH=/gcc-install/bin:$PATH | |
| export LD_LIBRARY_PATH=/gcc-install/lib64:/gcc-install/lib:$LD_LIBRARY_PATH | |
| cd /yaml-cpp-build | |
| echo "=== GCC Version ===" | |
| gcc --version | |
| echo "" | |
| echo "=== Configuring yaml-cpp with CMake ===" | |
| cmake {project_dir} \\ | |
| -DCMAKE_C_COMPILER=/gcc-install/bin/gcc \\ | |
| -DCMAKE_CXX_COMPILER=/gcc-install/bin/g++ \\ | |
| -DCMAKE_BUILD_TYPE=Release \\ | |
| -DYAML_CPP_BUILD_TESTS=ON \\ | |
| -DYAML_CPP_BUILD_TOOLS=ON \\ | |
| -DYAML_BUILD_SHARED_LIBS=OFF | |
| echo "" | |
| echo "=== Building yaml-cpp ===" | |
| make -j$(nproc) | |
| echo "" | |
| echo "=== Build Summary ===" | |
| ls -lh libyaml-cpp.a 2>/dev/null || echo "Static library not found" | |
| ls -lh util/ 2>/dev/null || echo "Tools not found" | |
| ' | |
| """.format(project_dir=PROJECT_DIR) | |
| ] | |
| run_command(cmd, stream_output=True) | |
| print("\\n✓ yaml-cpp build complete! Output saved to yaml-cpp-build volume") | |
| print("Run './builder.py test-yaml-cpp' to verify the build") | |
| def test_yaml_cpp(): | |
| """Test the built yaml-cpp by checking the library and running parse tool.""" | |
| print("Testing yaml-cpp build...") | |
| cmd = [ | |
| "docker", "run", | |
| "--rm", | |
| "-v", f"{VOLUMES[1]}:/gcc-install", | |
| "-v", f"{VOLUMES[2]}:/yaml-cpp-build", | |
| IMAGE_NAME, | |
| "/bin/bash", "-c", | |
| """ | |
| export LD_LIBRARY_PATH=/gcc-install/lib64:/gcc-install/lib:$LD_LIBRARY_PATH | |
| echo "=== Library Info ===" | |
| file /yaml-cpp-build/libyaml-cpp.a | |
| ls -lh /yaml-cpp-build/libyaml-cpp.a | |
| echo "" | |
| echo "=== Parse Tool ===" | |
| if [ -f /yaml-cpp-build/util/parse ]; then | |
| /yaml-cpp-build/util/parse --help 2>&1 || echo "Parse tool exists" | |
| else | |
| echo "Parse tool not found" | |
| fi | |
| """ | |
| ] | |
| run_command(cmd, stream_output=True) | |
| def build_all(): | |
| """Build GCC 15 first, then build yaml-cpp with it.""" | |
| print("=== Stage 1: Building GCC 15 from source ===\\n") | |
| run() | |
| print("\\n=== Stage 2: Building yaml-cpp with GCC 15 ===\\n") | |
| build_yaml_cpp() | |
| print("\\n=== All builds complete! ===") | |
| print("GCC 15 is in gcc-install volume") | |
| print("yaml-cpp build is in yaml-cpp-build volume") | |
| def shell(): | |
| """Start an interactive shell in the container.""" | |
| create_volumes() | |
| print("Starting interactive shell in container...") | |
| print("Type 'exit' to leave the container.\n") | |
| cmd = [ | |
| "docker", "run", "-it", | |
| "--rm", | |
| "-v", f"{VOLUMES[0]}:/gcc-build", | |
| "-v", f"{VOLUMES[1]}:/gcc-install", | |
| "-w", "/gcc-build", | |
| "--memory", MEMORY_LIMIT, | |
| "--shm-size", SHM_SIZE, | |
| IMAGE_NAME, | |
| "/bin/bash" | |
| ] | |
| subprocess.call(cmd) | |
| def status(): | |
| """Show status of volumes and images.""" | |
| print("=== Docker Image ===") | |
| cmd = ["docker", "images", IMAGE_NAME] | |
| run_command(cmd, check=False) | |
| print("\n=== Docker Volumes ===") | |
| for volume in VOLUMES: | |
| cmd = ["docker", "volume", "inspect", volume] | |
| if run_command(cmd, check=False): | |
| # Get volume size | |
| size_cmd = ["docker", "system", "df", "-v", "--format", "{{.Name}}\t{{.Size}}"] | |
| run_command(size_cmd, check=False) | |
| def main(): | |
| parser = argparse.ArgumentParser( | |
| description="Manage GCC 15.2.0 builder and yaml-cpp compilation", | |
| formatter_class=argparse.RawDescriptionHelpFormatter, | |
| epilog=""" | |
| Examples: | |
| %(prog)s build Build the Docker image | |
| %(prog)s run Compile GCC inside container (non-interactive) | |
| %(prog)s test Test the built GCC version | |
| %(prog)s build-yaml-cpp Build yaml-cpp with GCC 15 | |
| %(prog)s test-yaml-cpp Test the yaml-cpp build | |
| %(prog)s build-all Build GCC 15 and then yaml-cpp | |
| %(prog)s shell Start interactive bash shell in container | |
| %(prog)s clean Remove all volumes (clears build data) | |
| %(prog)s status Show current status | |
| """ | |
| ) | |
| parser.add_argument( | |
| "command", | |
| choices=["build", "run", "test", "build-yaml-cpp", "test-yaml-cpp", | |
| "build-all", "shell", "clean", "status"], | |
| help="Command to execute" | |
| ) | |
| if len(sys.argv) == 1: | |
| parser.print_help() | |
| sys.exit(1) | |
| args = parser.parse_args() | |
| # Execute the requested command | |
| commands = { | |
| "build": build, | |
| "run": run, | |
| "test": test, | |
| "build-yaml-cpp": build_yaml_cpp, | |
| "test-yaml-cpp": test_yaml_cpp, | |
| "build-all": build_all, | |
| "shell": shell, | |
| "clean": clean, | |
| "status": status, | |
| } | |
| commands[args.command]() | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment