294294 '' ;
295295 } ;
296296
297+ # COPT (Cardinal Optimizer) 完整安装
298+ # 包含求解器二进制文件、共享库和 Python 接口
299+ coptVersion = "8.0.2" ;
300+ copt = pkgs . stdenv . mkDerivation {
301+ name = "copt-${ coptVersion } " ;
302+ src = pkgs . fetchurl {
303+ url = "https://pub.shanshu.ai/download/copt/${ coptVersion } /linux64/CardinalOptimizer-${ coptVersion } -lnx64.tar.gz" ;
304+ sha256 = "1cns2z8cic4rvisxy5bmf60241a6c7a1g1mpxvb13dzwdn94r65v" ;
305+ } ;
306+
307+ nativeBuildInputs = [ pkgs . patchelf pkgs . unzip ] ;
308+ buildInputs = [ pkgs . glibc pkgs . zlib pkgs . stdenv . cc . cc . lib pkgs . libffi ] ;
309+
310+ installPhase = ''
311+ mkdir -p $out/opt/copt
312+ tar -xzf $src -C $out/opt/copt --strip-components=1
313+
314+ # 移除不需要的例子和文档以减小镜像体积
315+ rm -rf $out/opt/copt/examples $out/opt/copt/docs
316+
317+ # 确保权限正确
318+ find $out/opt/copt -type d -exec chmod 755 {} +
319+ find $out/opt/copt/bin -type f -exec chmod 755 {} +
320+ find $out/opt/copt/lib -type f -exec chmod 644 {} +
321+ '' ;
322+
323+ postFixup = ''
324+ echo "Patching COPT binaries and libs for Nix..."
325+ RPATH="${ pkgs . glibc } /lib:${ pkgs . stdenv . cc . cc . lib } /lib:${ pkgs . zlib } /lib:${ pkgs . libffi } /lib"
326+ INTERP="${ pkgs . glibc } /lib/ld-linux-x86-64.so.2"
327+
328+ # Patch 所有的二进制执行文件
329+ find $out/opt/copt/bin -type f | while read -r f; do
330+ if patchelf --print-interpreter "$f" >/dev/null 2>&1; then
331+ patchelf --set-interpreter "$INTERP" "$f" || true
332+ patchelf --set-rpath "$RPATH" "$f" || true
333+ fi
334+ done
335+
336+ # Patch 所有的共享库
337+ find $out/opt/copt/lib -name "*.so*" -type f | while read -r lib; do
338+ patchelf --set-rpath "$RPATH" "$lib" || true
339+ done
340+ '' ;
341+ } ;
342+
343+ # COPT Python 接口(优先使用 uv pip 安装以获得最佳兼容性)
344+ coptPythonPackages = pkgs . runCommand "copt-python-packages" {
345+ nativeBuildInputs = [ pythonWithPackages pkgs . uv pkgs . cacert ] ;
346+ __impureHostDeps = [ "/etc/resolv.conf" "/etc/hosts" ] ;
347+ } ''
348+ mkdir -p $out/site-packages
349+ export SSL_CERT_FILE=${ pkgs . cacert } /etc/ssl/certs/ca-bundle.crt
350+ export HOME="$TMPDIR/home"
351+ mkdir -p "$HOME"
352+ export UV_CACHE_DIR="$TMPDIR/.uv_cache"
353+ mkdir -p "$UV_CACHE_DIR"
354+ export UV_PYTHON_PREFERENCE="system"
355+ export UV_PYTHON="${ pythonWithPackages } /bin/python3.12"
356+
357+ echo "Installing coptpy via uv pip..."
358+ # 尝试从 PyPI 安装,如果失败则从解压后的 copt 目录提取
359+ if ${ pkgs . uv } /bin/uv pip install --python "$UV_PYTHON" --target $out/site-packages coptpy==${ coptVersion } 2>&1; then
360+ echo "✅ coptpy installed from PyPI"
361+ else
362+ echo "⚠️ PyPI install failed, extracting from copt package..."
363+ # 查找解压后的 copt 目录中的 python 接口
364+ CP_DIR=$(find ${ copt } /opt/copt -name "coptpy" -type d | head -n 1)
365+ if [ -n "$CP_DIR" ]; then
366+ cp -r "$CP_DIR" $out/site-packages/
367+ else
368+ echo "❌ Error: Could not find coptpy in package"
369+ exit 1
370+ fi
371+ fi
372+ '' ;
373+
297374 # Extract CPLEX Python API
298375 # First try to find it in the CPLEX installation, otherwise install via uv pip
299376 cplexPythonPackages = pkgs . runCommand "cplex-python-packages" {
449526
450527 # Set restricted environment
451528 # Use /.local instead of /tmp/.local because /tmp is mounted with noexec flag
452- export PYTHONPATH="/app:/opt/ortools/lib/python3.12/site-packages:/opt/cplex/lib/python3.12/site-packages:/.local/lib/python3.12/site-packages"
529+ export PYTHONPATH="/app:/opt/ortools/lib/python3.12/site-packages:/opt/cplex/lib/python3.12/site-packages:/opt/copt/lib/python3.12/site-packages:/ .local/lib/python3.12/site-packages"
453530 export PYTHONUNBUFFERED=1
454531 export PYTHONDONTWRITEBYTECODE=1
455532
460537 chmod 755 /.local/lib/python3.12/site-packages
461538
462539 # Restrict system tool access - ensure util-linux tools are accessible
463- # Also include CPLEX paths so docplex can find solvers
464- export PATH="${ pkgs . coreutils } /bin:${ pkgs . util-linux } /bin:/usr/local/bin:/usr/bin:/opt/ibm/ILOG/CPLEX_Studio221/cplex/bin/x86-64_linux:/opt/ibm/ILOG/CPLEX_Studio221/cpoptimizer/bin/x86-64_linux"
540+ # Also include CPLEX and COPT paths so docplex/coptpy can find solvers
541+ export PATH="${ pkgs . coreutils } /bin:${ pkgs . util-linux } /bin:/usr/local/bin:/usr/bin:/opt/ibm/ILOG/CPLEX_Studio221/cplex/bin/x86-64_linux:/opt/ibm/ILOG/CPLEX_Studio221/cpoptimizer/bin/x86-64_linux:/opt/copt/bin "
465542
466543 # Restrict environment variables
467544 unset HOME
@@ -619,7 +696,7 @@ finally:
619696
620697 # Set restricted environment
621698 # Use /.local instead of /tmp/.local because /tmp is mounted with noexec flag
622- export PYTHONPATH="/app:/opt/ortools/lib/python3.12/site-packages:/opt/cplex/lib/python3.12/site-packages:/.local/lib/python3.12/site-packages"
699+ export PYTHONPATH="/app:/opt/ortools/lib/python3.12/site-packages:/opt/cplex/lib/python3.12/site-packages:/opt/copt/lib/python3.12/site-packages:/ .local/lib/python3.12/site-packages"
623700 export PYTHONUNBUFFERED=1
624701 export PYTHONDONTWRITEBYTECODE=1
625702 # Some packages (sdists) require a valid HOME during build (e.g. setuptools expanduser()).
@@ -681,6 +758,7 @@ finally:
681758 secureUvScript
682759 pkgs . gurobi
683760 cplex
761+ copt
684762 pkgs . glibc
685763 pkgs . zlib
686764 pkgs . ncurses
@@ -727,6 +805,11 @@ finally:
727805 # Copy CPLEX Python API to /opt/cplex
728806 cp -r ${ cplexPythonPackages } /site-packages/* $out/opt/cplex/lib/python3.12/site-packages/
729807
808+ # Create copt directory for Python API
809+ mkdir -p $out/opt/copt/lib/python3.12/site-packages
810+ # Copy COPT Python API from uv installation
811+ cp -r ${ coptPythonPackages } /site-packages/* $out/opt/copt/lib/python3.12/site-packages/
812+
730813 # Create uv configuration
731814 cat > $out/etc/uv/uv.toml << 'EOFUV'
732815# Global uv configuration
@@ -880,6 +963,51 @@ EOFPYTHON
880963 EOFSCRIPT
881964 chmod +x $out/verify-cplex.sh
882965
966+ # Create COPT verification script
967+ cat > $out/verify-copt.sh << 'EOFSCRIPT'
968+ #!/bin/bash
969+ # Verify COPT is pre-installed and working
970+ echo "Verifying COPT installation..."
971+ echo ""
972+
973+ # Set COPT environment
974+ export COPT_HOME=/opt/copt
975+ export LD_LIBRARY_PATH=$COPT_HOME/lib:$LD_LIBRARY_PATH
976+
977+ python << 'EOFPYTHON'
978+ import sys
979+ import os
980+
981+ try:
982+ import coptpy
983+ print("✓ COPT Python API is pre-installed!")
984+ print(" COPT version:", coptpy.Envr().getVersion())
985+
986+ # Try to create a small model to verify binaries
987+ env = coptpy.Envr()
988+ model = env.createModel("verify")
989+ print("✓ COPT Binary libraries are accessible!")
990+
991+ print("")
992+ print("COPT is ready to use! 🚀")
993+
994+ except ImportError as e:
995+ print("✗ COPT Python API not found:", str(e))
996+ print(" PYTHONPATH:", os.environ.get('PYTHONPATH'))
997+ sys.exit(1)
998+ except Exception as e:
999+ print("✗ Error verifying COPT:", str(e))
1000+ # It might fail due to no license, but the import should work
1001+ if "license" in str(e).lower() or "113" in str(e):
1002+ print("✓ COPT Python API and binaries are loaded (License required for model creation)")
1003+ else:
1004+ import traceback
1005+ traceback.print_exc()
1006+ sys.exit(1)
1007+ EOFPYTHON
1008+ EOFSCRIPT
1009+ chmod +x $out/verify-copt.sh
1010+
8831011 # Note: python3 binary is provided by pythonWithPackages in runtimeEnv
8841012 # No need to create a symlink here to avoid collision
8851013
9591087 config = {
9601088 WorkingDir = "/tmp" ;
9611089 Env = [
962- "PYTHONPATH=/app:/opt/ortools/lib/python3.12/site-packages:/opt/cplex/lib/python3.12/site-packages:/.local/lib/python3.12/site-packages"
1090+ "PYTHONPATH=/app:/opt/ortools/lib/python3.12/site-packages:/opt/cplex/lib/python3.12/site-packages:/opt/copt/lib/python3.12/site-packages:/ .local/lib/python3.12/site-packages"
9631091 "PYTHONUNBUFFERED=1"
9641092 "PYTHONDONTWRITEBYTECODE=1"
9651093 # Force uv to use system Python
@@ -969,14 +1097,16 @@ in
9691097 "UV_CACHE_DIR=/tmp/.uv_cache"
9701098 "UV_PYTHON=/bin/python"
9711099 # Set PATH - put runtimeEnv/bin first to ensure our python symlink is found
972- "PATH=/bin:${ securePythonScript } /bin:${ runtimeEnv } /bin:${ systemPython } /bin:${ pkgs . coreutils } /bin:${ pkgs . util-linux } /bin:/usr/local/bin:/usr/bin:/opt/ibm/ILOG/CPLEX_Studio221/cplex/bin/x86-64_linux:/opt/ibm/ILOG/CPLEX_Studio221/cpoptimizer/bin/x86-64_linux"
1100+ "PATH=/bin:${ securePythonScript } /bin:${ runtimeEnv } /bin:${ systemPython } /bin:${ pkgs . coreutils } /bin:${ pkgs . util-linux } /bin:/usr/local/bin:/usr/bin:/opt/ibm/ILOG/CPLEX_Studio221/cplex/bin/x86-64_linux:/opt/ibm/ILOG/CPLEX_Studio221/cpoptimizer/bin/x86-64_linux:/opt/copt/bin "
9731101 # Set library search path
974- "LD_LIBRARY_PATH=${ runtimeEnv } /lib:${ runtimeEnv } /lib64:/opt/ibm/ILOG/CPLEX_Studio221/cplex/bin/x86-64_linux:/opt/ibm/ILOG/CPLEX_Studio221/cpoptimizer/bin/x86-64_linux"
1102+ "LD_LIBRARY_PATH=${ runtimeEnv } /lib:${ runtimeEnv } /lib64:/opt/ibm/ILOG/CPLEX_Studio221/cplex/bin/x86-64_linux:/opt/ibm/ILOG/CPLEX_Studio221/cpoptimizer/bin/x86-64_linux:/opt/copt/lib "
9751103 # Gurobi environment variables (using gurobi package from nixpkgs)
9761104 "GUROBI_HOME=${ pkgs . gurobi } "
9771105 "GRB_LICENSE_FILE=/app/gurobi.lic"
9781106 # CPLEX environment variables
9791107 "CPLEX_HOME=/opt/ibm/ILOG/CPLEX_Studio221/cplex"
1108+ # COPT environment variables
1109+ "COPT_HOME=/opt/copt"
9801110 # SSL certificate configuration for Gurobi WLS
9811111 "SSL_CERT_FILE=${ pkgs . cacert } /etc/ssl/certs/ca-bundle.crt"
9821112 "CURL_CA_BUNDLE=${ pkgs . cacert } /etc/ssl/certs/ca-bundle.crt"
0 commit comments