- 相关教程
- 相关文献
- CMake工程构建(能访问GitHub方法)
- CMake工程构建(不能访问GitHub方法)
- CLion debug源码
- CMake打包Cbc.so:
Cbc(Coin或branch and cut)是一个开源的混合整数线性规划求解器,用C++编写。它可以用作可调用库或使用独立的可执行文件。它可以通过各种建模系统、包等以多种方式使用。Clp(Coin或线性规划)是一个开源线性规划求解器。它主要是用来作为一个可调用的库,但也有一个基本的、独立的可执行版本。本文将介绍如何用CLion(CMake)构建工程并能debug源码。
相关教程- 【从零开始】在Windows中使用Linux——在WSL使用CLion、IDEA、PyCharm(安装到建立工程)——更新于2021.12
- WSL2 运行 CLion/IDEA/PyCharm等GUI应用——更新于2022.01
- 【从零开始】Google OR-Tools源码构建debug(CLion/CMake)
- COIN-OR Branch-and-Cut solver(GitHub)
博主的环境是Ubuntu 20.04 LTS。
首先打开CLion创建新工程:
修改CMakeLists.txt:
cmake_minimum_required(VERSION 3.21)
project(CbcProject)
set(PROJECT_ROOT_PATH "${PROJECT_SOURCE_DIR}")
# 可以把cbc打包后的.so文件放进lib目录下
set(LIBRARY_OUTPUT_PATH "${PROJECT_ROOT_PATH}/${OUTDIRS}/lib/")
# 可以把输出文件放进bin目录下
set(EXECUTABLE_OUTPUT_PATH "${PROJECT_ROOT_PATH}/${OUTDIRS}/bin/")
# Build Cbc dependencies.
set(BUILD_DEPS ON)
if (BUILD_DEPS)
set(BUILD_CoinUtils ON)
set(BUILD_Osi ON)
set(BUILD_Clp ON)
set(BUILD_Cgl ON)
set(BUILD_Cbc ON)
endif ()
include(FetchContent)
# 填充期间的日志输出可能非常冗长,使得配置阶段非常嘈杂。此缓存选项(ON默认情况下)隐藏所有填充输出,除非遇到错误。如果遇到挂起下载的问题,
# 暂时关闭此选项可能有助于诊断导致问题的内容群体。
set(FETCHCONTENT_QUIET OFF)
# 避免每次下载编译及其所有依赖
get_filename_component(_deps "../_deps" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}")
# 在大多数情况下,保存的详细信息没有指定与用于内部子构建、最终源和构建区域的目录相关的任何选项。通常最好将这些决定留给FetchContent 模块来代表项目处理。
# 缓存变量控制收集所有内容填充目录的FETCHCONTENT_BASE_DIR 点,但在大多数情况下,开发人员不需要更改它。
# 默认位置是${CMAKE_BINARY_DIR}/_deps,但如果开发人员更改此值,他们应该致力于保持路径短且刚好低于构建树的顶层,以避免在 Windows 上遇到路径长度问题。
set(FETCHCONTENT_BASE_DIR ${_deps})
# ##############################################################################
# Coinutils
# ##############################################################################
# Coin-OR does not support C++17/C++20 (use of 'register' storage class specifier)
set(CMAKE_CXX_STANDARD 11)
if (BUILD_CoinUtils)
message(CHECK_START "Fetching CoinUtils")
list(APPEND CMAKE_MESSAGE_INDENT " ")
FetchContent_Declare(
CoinUtils
GIT_REPOSITORY "https://github.com/Mizux/CoinUtils.git"
GIT_TAG "cmake/2.11.6"
)
FetchContent_MakeAvailable(CoinUtils)
list(POP_BACK CMAKE_MESSAGE_INDENT)
message(CHECK_PASS "fetched")
endif ()
# ##############################################################################
# Osi
# ##############################################################################
if (BUILD_Osi)
message(CHECK_START "Fetching Osi")
list(APPEND CMAKE_MESSAGE_INDENT " ")
FetchContent_Declare(
Osi
GIT_REPOSITORY "https://github.com/Mizux/Osi.git"
GIT_TAG "cmake/0.108.7"
)
FetchContent_MakeAvailable(Osi)
list(POP_BACK CMAKE_MESSAGE_INDENT)
message(CHECK_PASS "fetched")
endif ()
# ##############################################################################
# Clp
# ##############################################################################
if (BUILD_Clp)
message(CHECK_START "Fetching Clp")
list(APPEND CMAKE_MESSAGE_INDENT " ")
FetchContent_Declare(
Clp
GIT_REPOSITORY "https://github.com/Mizux/Clp.git"
GIT_TAG "cmake/1.17.7"
)
FetchContent_MakeAvailable(Clp)
list(POP_BACK CMAKE_MESSAGE_INDENT)
message(CHECK_PASS "fetched")
endif ()
# ##############################################################################
# Cgl
# ##############################################################################
if (BUILD_Cgl)
message(CHECK_START "Fetching Cgl")
list(APPEND CMAKE_MESSAGE_INDENT " ")
FetchContent_Declare(
Cgl
GIT_REPOSITORY "https://github.com/Mizux/Cgl.git"
GIT_TAG "cmake/0.60.5"
)
FetchContent_MakeAvailable(Cgl)
list(POP_BACK CMAKE_MESSAGE_INDENT)
message(CHECK_PASS "fetched")
endif ()
# ##############################################################################
# Cbc
# ##############################################################################
if (BUILD_Cbc)
message(CHECK_START "Fetching Cbc")
list(APPEND CMAKE_MESSAGE_INDENT " ")
FetchContent_Declare(
Cbc
GIT_REPOSITORY "https://github.com/Mizux/Cbc.git"
GIT_TAG "cmake/2.10.7"
)
FetchContent_MakeAvailable(Cbc)
list(POP_BACK CMAKE_MESSAGE_INDENT)
message(CHECK_PASS "fetched")
endif ()
add_executable(${PROJECT_NAME} main.cpp)
if (BUILD_DEPS)
set(COINOR_DEPS Coin::CbcSolver Coin::OsiCbc Coin::ClpSolver Coin::OsiClp)
target_link_libraries(${PROJECT_NAME} PUBLIC ${COINOR_DEPS})
endif ()
然后你会看到:
博主的环境是Ubuntu 20.04 LTS。
首先打开CLion创建新工程:
下载:coin-or/CoinUtils-cmake-2.11.6 Osi-cmake-0.108.7 Clp-cmake-1.17.7 Cgl-cmake-0.60.5 Cbc-cmake-2.10.7源码
解压到:
修改CMakeLists.txt:
cmake_minimum_required(VERSION 3.21)
project(CbcProject)
set(PROJECT_ROOT_PATH "${PROJECT_SOURCE_DIR}")
# 可以把cbc打包后的.so文件放进lib目录下
set(LIBRARY_OUTPUT_PATH "${PROJECT_ROOT_PATH}/${OUTDIRS}/lib/")
# 可以把输出文件放进bin目录下
set(EXECUTABLE_OUTPUT_PATH "${PROJECT_ROOT_PATH}/${OUTDIRS}/bin/")
# Build Cbc dependencies.
set(BUILD_DEPS ON)
if (BUILD_DEPS)
set(BUILD_CoinUtils ON)
set(BUILD_Osi ON)
set(BUILD_Clp ON)
set(BUILD_Cgl ON)
set(BUILD_Cbc ON)
endif ()
include(FetchContent)
# 填充期间的日志输出可能非常冗长,使得配置阶段非常嘈杂。此缓存选项(ON默认情况下)隐藏所有填充输出,除非遇到错误。如果遇到挂起下载的问题,
# 暂时关闭此选项可能有助于诊断导致问题的内容群体。
set(FETCHCONTENT_QUIET OFF)
# 避免每次下载编译及其所有依赖
get_filename_component(_deps "../_deps" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}")
# 在大多数情况下,保存的详细信息没有指定与用于内部子构建、最终源和构建区域的目录相关的任何选项。通常最好将这些决定留给FetchContent 模块来代表项目处理。
# 缓存变量控制收集所有内容填充目录的FETCHCONTENT_BASE_DIR 点,但在大多数情况下,开发人员不需要更改它。
# 默认位置是${CMAKE_BINARY_DIR}/_deps,但如果开发人员更改此值,他们应该致力于保持路径短且刚好低于构建树的顶层,以避免在 Windows 上遇到路径长度问题。
set(FETCHCONTENT_BASE_DIR ${_deps})
# ##############################################################################
# Coinutils
# ##############################################################################
# Coin-OR does not support C++17/C++20 (use of 'register' storage class specifier)
set(CMAKE_CXX_STANDARD 11)
if (BUILD_CoinUtils)
message(CHECK_START "Fetching CoinUtils")
list(APPEND CMAKE_MESSAGE_INDENT " ")
FetchContent_Declare(
CoinUtils
URL "${PROJECT_SOURCE_DIR}/deps/CoinUtils-cmake-2.11.6.zip"
)
FetchContent_MakeAvailable(CoinUtils)
list(POP_BACK CMAKE_MESSAGE_INDENT)
message(CHECK_PASS "fetched")
endif ()
# ##############################################################################
# Osi
# ##############################################################################
if (BUILD_Osi)
message(CHECK_START "Fetching Osi")
list(APPEND CMAKE_MESSAGE_INDENT " ")
FetchContent_Declare(
Osi
URL "${PROJECT_SOURCE_DIR}/deps/Osi-cmake-0.108.7.zip"
)
FetchContent_MakeAvailable(Osi)
list(POP_BACK CMAKE_MESSAGE_INDENT)
message(CHECK_PASS "fetched")
endif ()
# ##############################################################################
# Clp
# ##############################################################################
if (BUILD_Clp)
message(CHECK_START "Fetching Clp")
list(APPEND CMAKE_MESSAGE_INDENT " ")
FetchContent_Declare(
Clp
URL "${PROJECT_SOURCE_DIR}/deps/Clp-cmake-1.17.7.zip"
)
FetchContent_MakeAvailable(Clp)
list(POP_BACK CMAKE_MESSAGE_INDENT)
message(CHECK_PASS "fetched")
endif ()
# ##############################################################################
# Cgl
# ##############################################################################
if (BUILD_Cgl)
message(CHECK_START "Fetching Cgl")
list(APPEND CMAKE_MESSAGE_INDENT " ")
FetchContent_Declare(
Cgl
URL "${PROJECT_SOURCE_DIR}/deps/Cgl-cmake-0.60.5.zip"
)
FetchContent_MakeAvailable(Cgl)
list(POP_BACK CMAKE_MESSAGE_INDENT)
message(CHECK_PASS "fetched")
endif ()
# ##############################################################################
# Cbc
# ##############################################################################
if (BUILD_Cbc)
message(CHECK_START "Fetching Cbc")
list(APPEND CMAKE_MESSAGE_INDENT " ")
FetchContent_Declare(
Cbc
URL "${PROJECT_SOURCE_DIR}/deps/Cbc-cmake-2.10.7.zip"
)
FetchContent_MakeAvailable(Cbc)
list(POP_BACK CMAKE_MESSAGE_INDENT)
message(CHECK_PASS "fetched")
endif ()
add_executable(${PROJECT_NAME} main.cpp)
if (BUILD_DEPS)
set(COINOR_DEPS Coin::CbcSolver Coin::OsiCbc Coin::ClpSolver Coin::OsiClp)
target_link_libraries(${PROJECT_NAME} PUBLIC ${COINOR_DEPS})
endif ()
CLion debug源码
main.cpp:
#include#include "ClpSimplex.hpp" #include "CoinBuild.hpp" #include "CoinHelperFunctions.hpp" #include "CoinModel.hpp" #include "CoinTime.hpp" #include int main() { // Empty model ClpSimplex model; // Objective - just nonzeros int objIndex[] = {0, 2}; double objValue[] = {1.0, 4.0}; // Upper bounds - as dense vector double upper[] = {2.0, COIN_DBL_MAX, 4.0}; // Create space for 3 columns model.resize(0, 3); // Fill in int i; // Virtuous way // First objective for (i = 0; i < 2; i++) model.setObjectiveCoefficient(objIndex[i], objValue[i]); // Now bounds (lower will be zero by default but do again) for (i = 0; i < 3; i++) { model.setColumnLower(i, 0.0); model.setColumnUpper(i, upper[i]); } // Faster to add rows all at once - but this is easier to show // Now add row 1 as >= 2.0 int row1Index[] = {0, 2}; double row1Value[] = {1.0, 1.0}; model.addRow(2, row1Index, row1Value, 2.0, COIN_DBL_MAX); // Now add row 2 as == 1.0 int row2Index[] = {0, 1, 2}; double row2Value[] = {1.0, -5.0, 1.0}; model.addRow(3, row2Index, row2Value, 1.0, 1.0); // solve model.dual(); model.allSlackBasis(); ClpSimplex modelSave = model; double time1 = CoinCpuTime(); int k; for (k = 0; k < 10000; k++) { int row2Index[] = {0, 1, 2}; double row2Value[] = {1.0, -5.0, 1.0}; model.addRow(3, row2Index, row2Value, 1.0, 1.0); } printf("Time for 10000 addRow is %gn", CoinCpuTime() - time1); model.dual(); model = modelSave; // Now use build CoinBuild buildObject; time1 = CoinCpuTime(); for (k = 0; k < 10000; k++) { int row2Index[] = {0, 1, 2}; double row2Value[] = {1.0, -5.0, 1.0}; buildObject.addRow(3, row2Index, row2Value, 1.0, 1.0); } model.addRows(buildObject); printf("Time for 10000 addRow using CoinBuild is %gn", CoinCpuTime() - time1); model.dual(); model = modelSave; int del[] = {0, 1, 2}; model.deleteRows(2, del); // Now use build +-1 CoinBuild buildObject2; time1 = CoinCpuTime(); for (k = 0; k < 10000; k++) { int row2Index[] = {0, 1, 2}; double row2Value[] = {1.0, -1.0, 1.0}; buildObject2.addRow(3, row2Index, row2Value, 1.0, 1.0); } model.addRows(buildObject2, true); printf("Time for 10000 addRow using CoinBuild+-1 is %gn", CoinCpuTime() - time1); model.dual(); model = modelSave; model.deleteRows(2, del); // Now use build +-1 CoinModel modelObject2; time1 = CoinCpuTime(); for (k = 0; k < 10000; k++) { int row2Index[] = {0, 1, 2}; double row2Value[] = {1.0, -1.0, 1.0}; modelObject2.addRow(3, row2Index, row2Value, 1.0, 1.0); } model.addRows(modelObject2, true); printf("Time for 10000 addRow using CoinModel+-1 is %gn", CoinCpuTime() - time1); model.dual(); model = ClpSimplex(); // Now use build +-1 CoinModel modelObject3; time1 = CoinCpuTime(); for (k = 0; k < 10000; k++) { int row2Index[] = {0, 1, 2}; double row2Value[] = {1.0, -1.0, 1.0}; modelObject3.addRow(3, row2Index, row2Value, 1.0, 1.0); } model.loadProblem(modelObject3, true); printf("Time for 10000 addRow using CoinModel load +-1 is %gn", CoinCpuTime() - time1); model.writeMps("xx.mps"); model.dual(); model = modelSave; // Now use model CoinModel modelObject; time1 = CoinCpuTime(); for (k = 0; k < 10000; k++) { int row2Index[] = {0, 1, 2}; double row2Value[] = {1.0, -5.0, 1.0}; modelObject.addRow(3, row2Index, row2Value, 1.0, 1.0); } model.addRows(modelObject); printf("Time for 10000 addRow using CoinModel is %gn", CoinCpuTime() - time1); model.dual(); model.writeMps("b.mps"); // Method using least memory - but most complicated time1 = CoinCpuTime(); // Assumes we know exact size of model and matrix // Empty model ClpSimplex model2; { // Create space for 3 columns and 10000 rows int numberRows = 10000; int numberColumns = 3; // This is fully dense - but would not normally be so int numberElements = numberRows * numberColumns; // Arrays will be set to default values model2.resize(numberRows, numberColumns); double *elements = new double[numberElements]; CoinBigIndex *starts = new CoinBigIndex[numberColumns + 1]; int *rows = new int[numberElements];; int *lengths = new int[numberColumns]; // Now fill in - totally unsafe but .... // no need as defaults to 0.0 double * columnLower = model2.columnLower(); double *columnUpper = model2.columnUpper(); double *objective = model2.objective(); double *rowLower = model2.rowLower(); double *rowUpper = model2.rowUpper(); // Columns - objective was packed for (k = 0; k < 2; k++) { int iColumn = objIndex[k]; objective[iColumn] = objValue[k]; } for (k = 0; k < numberColumns; k++) columnUpper[k] = upper[k]; // Rows for (k = 0; k < numberRows; k++) { rowLower[k] = 1.0; rowUpper[k] = 1.0; } // Now elements double row2Value[] = {1.0, -5.0, 1.0}; CoinBigIndex put = 0; for (k = 0; k < numberColumns; k++) { starts[k] = put; lengths[k] = numberRows; double value = row2Value[k]; for (int i = 0; i < numberRows; i++) { rows[put] = i; elements[put] = value; put++; } } starts[numberColumns] = put; // assign to matrix CoinPackedMatrix *matrix = new CoinPackedMatrix(true, 0.0, 0.0); matrix->assignMatrix(true, numberRows, numberColumns, numberElements, elements, rows, starts, lengths); ClpPackedMatrix *clpMatrix = new ClpPackedMatrix(matrix); model2.replaceMatrix(clpMatrix, true); printf("Time for 10000 addRow using hand written code is %gn", CoinCpuTime() - time1); // If matrix is really big could switch off creation of row copy // model2.setSpecialOptions(256); } model2.dual(); model2.writeMps("a.mps"); // Print column solution int numberColumns = model.numberColumns(); // Alternatively getColSolution() double *columnPrimal = model.primalColumnSolution(); // Alternatively getReducedCost() double *columnDual = model.dualColumnSolution(); // Alternatively getColLower() double *columnLower = model.columnLower(); // Alternatively getColUpper() double *columnUpper = model.columnUpper(); // Alternatively getObjCoefficients() double *columnObjective = model.objective(); int iColumn; std::cout << " Primal Dual Lower Upper Cost" << std::endl; for (iColumn = 0; iColumn < numberColumns; iColumn++) { double value; std::cout << std::setw(6) << iColumn << " "; value = columnPrimal[iColumn]; if (fabs(value) < 1.0e5) std::cout << std::setiosflags(std::ios::fixed | std::ios::showpoint) << std::setw(14) << value; else std::cout << std::setiosflags(std::ios::scientific) << std::setw(14) << value; value = columnDual[iColumn]; if (fabs(value) < 1.0e5) std::cout << std::setiosflags(std::ios::fixed | std::ios::showpoint) << std::setw(14) << value; else std::cout << std::setiosflags(std::ios::scientific) << std::setw(14) << value; value = columnLower[iColumn]; if (fabs(value) < 1.0e5) std::cout << std::setiosflags(std::ios::fixed | std::ios::showpoint) << std::setw(14) << value; else std::cout << std::setiosflags(std::ios::scientific) << std::setw(14) << value; value = columnUpper[iColumn]; if (fabs(value) < 1.0e5) std::cout << std::setiosflags(std::ios::fixed | std::ios::showpoint) << std::setw(14) << value; else std::cout << std::setiosflags(std::ios::scientific) << std::setw(14) << value; value = columnObjective[iColumn]; if (fabs(value) < 1.0e5) std::cout << std::setiosflags(std::ios::fixed | std::ios::showpoint) << std::setw(14) << value; else std::cout << std::setiosflags(std::ios::scientific) << std::setw(14) << value; std::cout << std::endl; } std::cout << "--------------------------------------" << std::endl; return 0; }
最后尽情地开始Debug吧!!!(上一张成果图):
其实通过上述步骤编译后你就会发现多了一个lib目录:



