-------- Forwarded Message --------

Date: Sat, 27 Jan 2024 00:30:37 -0500
Subject: GLPK CMake build configuration (Windows)
To: help-glpk@gnu.org
From: Derek Huang <djh...@stern.nyu.edu>
> Hi,
> My name is Derek Huang; I am currently a quant [developer] at TD
> Securities. I needed to use GLPK at work on Windows but found the
> NMake Makefiles not sufficient for my purpose as I wanted the
> following:
> 
> 1. Ability to link against C runtime statically or dynamically
> 2. Build more of the example programs (except netgen, got error
> saying glp_netgen disabled for licensing reasons)
> 3. Provide installation rule for Windows distinguishing debug/release
> config + C runtime linking
> 4. Build PE32 (32-bit) or PE32+ (64-bit) on Windows from the same
> Developer Command Prompt
> 
> CMake makes this relatively straightforward to do so I spent some time
> at home today in the evening to write a CMakeLists.txt (attached). I
> am not sure what is the best way to make contributions upstream, hence
> why I have attached it to the email. It contains explanations on a few
> ways one can build, run tests, and install using CMake, and I have
> verified all example programs run properly for 32-bit and 64-bit Debug
> + Release static and dynamic builds. The CMake generator (underlying
> build tool) used is Visual Studio 2022. Unfortunately, however, I have
> not yet added any formal documentation outside of the CMakeLists.txt.
> 
> Please let me know if this could be accepted as part of the GLPK
> source base as an alternative method to build on Windows and if so, if
> there are any specific steps I should take to introduce it to the
> source tree. Thank you.
> 
> Best,
> Derek
> 
cmake_minimum_required(VERSION 3.15)

##
# Usage
# =====
#
# This section provides some brief instructions on how to compile, test, and
# install GLPK using CMake. Although currently configured only for use on
# Windows, the config can be extended to support *nix systems as well.
#
# Note:
#
# This file can be split up into smaller .cmake files if desired. It is a few
# hundred lines long unfortunately in large part since the sources are all
# listed explicitly instead of using globs and because of the install logic.
#
# Windows
# -------
#
# The following items are values to be determined by the user. They are used in
# the build commands enclosed in brackets to distinguish them.
#
# Arch: Win32, x64
# Config: Debug, Release
# Prefix: (desired install root)
#
# Building
# ~~~~~~~~
#
# Build static linking against C runtime dynamically + also build examples:
#
#   cmake -S . -B build_windows_[Arch] -A [Arch]
#   cmake --build build_windows_[Arch] --config [Config] -j
#
# Build dynamic linking against C runtime statically + no examples:
#
#   cmake -S . -B build_windows_[Arch] -A [Arch] -DBUILD_SHARED_LIBS=ON ^
#       -DGLPK_USE_STATIC_CRT=ON -DGLPK_BUILD_EXAMPLES=OFF
#   cmake --build build_windows_[Arch] --config [Config] -j
#
# Build dynamic linking against C runtime dynamically + also build examples
# while installing with multi-configuration install tree:
#
#   cmake -S . -B build_windows_[Arch] -A [Arch] -DBUILD_SHARED_LIBS=ON ^
#       -DGLPK_INSTALL_MULTI_CONFIG=ON
#   cmake --build build_windows_[Arch] --config [Config] -j
#
# Testing
# ~~~~~~~
#
#   ctest --test-dir build_windows_[Arch] -C [Config] -j
#
# Install
# ~~~~~~~
#
#   cmake --install build_windows_[Arch] --prefix [Prefix] --config [Config]
#
# If GLPK_INSTALL_MULTI_CONFIG is specified, and assuming that we are building
# shared libraries, the install tree looks like this:
#
# install_root/
#   bin/
#     Debug/
#       glpk.dll
#       glpsol.exe
#     Release/
#       glpk.dll
#       glpsol.exe
#   include/
#     glpk.h
#   lib/
#     Debug/
#       glpk.lib
#       glpk.pdb
#     Release/
#       glpk.lib
#
# Otherwise, we have a flat install with "d" suffices, e.g.
#
# install_root/
#   bin/
#     glpk.dll
#     glpkd.dll
#     glpsol.exe
#     glpsold.exe
#   include/
#     glpk.h
#   lib/
#     glpk.lib
#     glpkd.lib
#     glpkd.pdb
#

# GLPK version
set(GLPK_VERSION_MAJOR 5)
set(GLPK_VERSION_MINOR 0)
set(GLPK_VERSION ${GLPK_VERSION_MAJOR}.${GLPK_VERSION_MINOR})

project(
    glpk
    VERSION 5.0
    DESCRIPTION "GNU Linear Programming Kit"
    HOMEPAGE_URL "https://www.gnu.org/software/glpk/";
    LANGUAGES C
)

# select static/shared library compilation
option(BUILD_SHARED_LIBS "Build GLPK library as shared" OFF)
# use static C runtime library. defaulted to OFF to match the CMake default of
# using MultiThreaded$<$<CONFIG:Debug>:Debug>DLL to select dynamic C runtime.
# see https://cmake.org/cmake/help/latest/prop_tgt/MSVC_RUNTIME_LIBRARY.html,
# https://learn.microsoft.com/en-us/cpp/c-runtime-library/crt-library-features
option(GLPK_USE_STATIC_CRT "Link against C runtime statically" OFF)
# to separate Debug and Release libraries, which also link against debug and
# release C runtimes respectively (should never be mixed), add an extra layer
# of namespacing to lib and bin, e.g. lib/Debug, bin/Release, etc. for MSVC, if
# this is not specified, debug libraries + PDB is suffixed with "d"
option(GLPK_INSTALL_MULTI_CONFIG OFF)
# build example programs
option(GLPK_BUILD_EXAMPLES "Build the GLPK example programs" ON)

# only available for Windows currently
if(NOT WIN32)
    message(FATAL_ERROR "CMake config is currently only usable for Windows")
endif()

# enable testing via ctest
include(CTest)

# check if multi-config or not
get_property(GLPK_BUILD_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if(GLPK_BUILD_MULTI_CONFIG)
    message(STATUS "Build generator: Multi-config")
else()
    message(STATUS "Build generator: Single-config")
endif()

# static/shared library build
if(BUILD_SHARED_LIBS)
    message(STATUS "GLPK build: Shared")
else()
    message(STATUS "GLPK build: Static")
endif()
# link statically against C runtime library if requested
if(WIN32)
    if(GLPK_USE_STATIC_CRT)
        set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
        message(STATUS "UCRT linkage: Static")
    # link dynamically. this is the default but we set this explicitly
    else()
        set(CMAKE_MSVC_RUNTIME_LIBRARY 
"MultiThreaded$<$<CONFIG:Debug>:Debug>DLL")
        message(STATUS "UCRT linkage: Shared")
    endif()
endif()
# multi-config installation (only for MSVC)
if(MSVC AND GLPK_INSTALL_MULTI_CONFIG)
    message(STATUS "Installation: Multi")
else()
    message(STATUS "Installation: Flat")
endif()
# example programs
if(GLPK_BUILD_EXAMPLES)
    message(STATUS "Build examples: No")
else()
    message(STATUS "Build examples: Yes")
endif()

# examples directory absolute path
set(GLPK_EXAMPLES_DIR ${CMAKE_CURRENT_SOURCE_DIR}/examples)

# check if generating 32- or 64-bit code
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
    set(GLPK_TARGET_64 ON)
    set(GLPK_TARGET_32 OFF)
    message(STATUS "Architecture: 64-bit")
elseif(CMAKE_SIZEOF_VOID_P EQUAL 4)
    set(GLPK_TARGET_64 OFF)
    set(GLPK_TARGET_32 ON)
    message(STATUS "Architecture: 32-bit")
else()
    message(FATAL_ERROR "Incompatible pointer size, must be 8 or 4 bytes")
endif()

# compile flags
if(MSVC)
    # TODO: increase warning level later? e.g. /W4, /Wall
    add_compile_definitions(HAVE_CONFIG_H _CRT_SECURE_NO_WARNINGS)
    add_compile_options(/W3)
else()
    # TODO: fill in for *nix
endif()

# include directories
include_directories(
    src
    src/amd
    src/api
    src/bflib
    src/colamd
    src/draft
    src/env
    src/intopt
    src/minisat
    src/misc
    src/mpl
    src/npp
    src/proxy
    src/simplex
    src/zlib
)
# add w32 or w64 as include for config.h appropriately
if(GLPK_TARGET_64)
    include_directories(w64)
    set(GLPK_CONFIG_H_DIR ${CMAKE_CURRENT_SOURCE_DIR}/w64)
    message(STATUS "config.h from: w64")
else()
    include_directories(w32)
    set(GLPK_CONFIG_H_DIR ${CMAKE_CURRENT_SOURCE_DIR}/w32)
    message(STATUS "config.h from: w32")
endif()

# build GLPK library. maybe use wildcards?
add_library(
    glpk
    src/amd/amd_1.c
    src/amd/amd_2.c
    src/amd/amd_aat.c
    src/amd/amd_control.c
    src/amd/amd_defaults.c
    src/amd/amd_dump.c
    src/amd/amd_info.c
    src/amd/amd_order.c
    src/amd/amd_post_tree.c
    src/amd/amd_postorder.c
    src/amd/amd_preprocess.c
    src/amd/amd_valid.c
    src/api/advbas.c
    src/api/asnhall.c
    src/api/asnlp.c
    src/api/asnokalg.c
    src/api/ckasn.c
    src/api/ckcnf.c
    src/api/cplex.c
    src/api/cpp.c
    src/api/cpxbas.c
    src/api/graph.c
    src/api/gridgen.c
    src/api/intfeas1.c
    src/api/maxffalg.c
    src/api/maxflp.c
    src/api/mcflp.c
    src/api/mcfokalg.c
    src/api/mcfrelax.c
    src/api/minisat1.c
    src/api/mpl.c
    src/api/mps.c
    src/api/netgen.c
    src/api/npp.c
    src/api/pript.c
    src/api/prmip.c
    src/api/prob1.c
    src/api/prob2.c
    src/api/prob3.c
    src/api/prob4.c
    src/api/prob5.c
    src/api/prrngs.c
    src/api/prsol.c
    src/api/rdasn.c
    src/api/rdcc.c
    src/api/rdcnf.c
    src/api/rdipt.c
    src/api/rdmaxf.c
    src/api/rdmcf.c
    src/api/rdmip.c
    src/api/rdprob.c
    src/api/rdsol.c
    src/api/rmfgen.c
    src/api/strong.c
    src/api/topsort.c
    src/api/weak.c
    src/api/wcliqex.c
    src/api/wrasn.c
    src/api/wrcc.c
    src/api/wrcnf.c
    src/api/wript.c
    src/api/wrmaxf.c
    src/api/wrmcf.c
    src/api/wrmip.c
    src/api/wrprob.c
    src/api/wrsol.c
    src/bflib/btf.c
    src/bflib/btfint.c
    src/bflib/fhv.c
    src/bflib/fhvint.c
    src/bflib/ifu.c
    src/bflib/luf.c
    src/bflib/lufint.c
    src/bflib/scf.c
    src/bflib/scfint.c
    src/bflib/sgf.c
    src/bflib/sva.c
    src/colamd/colamd.c
    src/draft/bfd.c
    src/draft/bfx.c
    src/draft/glpapi06.c
    src/draft/glpapi07.c
    src/draft/glpapi08.c
    src/draft/glpapi09.c
    src/draft/glpapi10.c
    src/draft/glpapi12.c
    src/draft/glpapi13.c
    src/draft/glpios01.c
    src/draft/glpios02.c
    src/draft/glpios03.c
    src/draft/glpios07.c
    src/draft/glpios09.c
    src/draft/glpios11.c
    src/draft/glpios12.c
    src/draft/glpipm.c
    src/draft/glpmat.c
    src/draft/glpscl.c
    src/draft/glpssx01.c
    src/draft/glpssx02.c
    src/draft/lux.c
    src/env/alloc.c
    src/env/dlsup.c
    src/env/env.c
    src/env/error.c
    src/env/stdc.c
    src/env/stdout.c
    src/env/stream.c
    src/env/time.c
    src/env/tls.c
    src/intopt/cfg.c
    src/intopt/cfg1.c
    src/intopt/cfg2.c
    src/intopt/clqcut.c
    src/intopt/covgen.c
    src/intopt/fpump.c
    src/intopt/gmicut.c
    src/intopt/gmigen.c
    src/intopt/mirgen.c
    src/intopt/spv.c
    src/minisat/minisat.c
    src/misc/avl.c
    src/misc/bignum.c
    src/misc/dimacs.c
    src/misc/dmp.c
    src/misc/ffalg.c
    src/misc/fp2rat.c
    src/misc/fvs.c
    src/misc/gcd.c
    src/misc/hbm.c
    src/misc/jd.c
    src/misc/keller.c
    src/misc/ks.c
    src/misc/mc13d.c
    src/misc/mc21a.c
    src/misc/mt1.c
    src/misc/mygmp.c
    src/misc/okalg.c
    src/misc/qmd.c
    src/misc/relax4.c
    src/misc/rgr.c
    src/misc/rng.c
    src/misc/rng1.c
    src/misc/round2n.c
    src/misc/spm.c
    src/misc/str2int.c
    src/misc/str2num.c
    src/misc/strspx.c
    src/misc/strtrim.c
    src/misc/triang.c
    src/misc/wclique.c
    src/misc/wclique1.c
    src/mpl/mpl1.c
    src/mpl/mpl2.c
    src/mpl/mpl3.c
    src/mpl/mpl4.c
    src/mpl/mpl5.c
    src/mpl/mpl6.c
    src/mpl/mplsql.c
    src/npp/npp1.c
    src/npp/npp2.c
    src/npp/npp3.c
    src/npp/npp4.c
    src/npp/npp5.c
    src/npp/npp6.c
    src/proxy/proxy.c
    src/proxy/proxy1.c
    src/simplex/spxat.c
    src/simplex/spxchuzc.c
    src/simplex/spxchuzr.c
    src/simplex/spxlp.c
    src/simplex/spxnt.c
    src/simplex/spxprim.c
    src/simplex/spxprob.c
    src/simplex/spychuzc.c
    src/simplex/spychuzr.c
    src/simplex/spydual.c
    src/zlib/adler32.c
    src/zlib/compress.c
    src/zlib/crc32.c
    src/zlib/deflate.c
    src/zlib/gzclose.c
    src/zlib/gzlib.c
    src/zlib/gzread.c
    src/zlib/gzwrite.c
    src/zlib/inffast.c
    src/zlib/inflate.c
    src/zlib/inftrees.c
    src/zlib/trees.c
    src/zlib/uncompr.c
    src/zlib/zio.c
    src/zlib/zutil.c
)
# config.h copying from config_VC. w32 and w64 are same but we choose
add_custom_command(
    TARGET glpk PRE_BUILD
    COMMAND
        ${CMAKE_COMMAND} -E copy_if_different ${GLPK_CONFIG_H_DIR}/config_VC
            ${GLPK_CONFIG_H_DIR}/config.h
    COMMENT "Copying config_VC to config.h"
    VERBATIM
)
# glpk.h is the public header
set_target_properties(glpk PROPERTIES PUBLIC_HEADER src/glpk.h)
# if not using multi-config install, suffix "d" to glpk
if(MSVC AND NOT GLPK_INSTALL_MULTI_CONFIG)
    set_target_properties(glpk PROPERTIES OUTPUT_NAME glpk$<$<CONFIG:Debug>:d>)
endif()

# build glpsol
add_executable(glpsol examples/glpsol.c)
target_link_libraries(glpsol PRIVATE glpk)
# if not using multi-config install, suffix "d" to glpsol
if(MSVC AND NOT GLPK_INSTALL_MULTI_CONFIG)
    set_target_properties(glpsol PROPERTIES OUTPUT_NAME 
glpsol$<$<CONFIG:Debug>:d>)
endif()

# build examples
if(GLPK_BUILD_EXAMPLES)
    add_executable(iptsamp examples/iptsamp.c)
    target_link_libraries(iptsamp PRIVATE glpk)
    add_executable(mplsamp1 examples/mplsamp1.c)
    target_link_libraries(mplsamp1 PRIVATE glpk)
    add_executable(mplsamp2 examples/mplsamp2.c)
    target_link_libraries(mplsamp2 PRIVATE glpk)
    # aborts since glp_netgen disabled due to licensing problems
    # add_executable(netgen examples/netgen.c)
    # target_link_libraries(netgen PRIVATE glpk)
    add_executable(nppsamp examples/nppsamp.c)
    target_link_libraries(nppsamp PRIVATE glpk)
    add_executable(sample examples/sample.c)
    target_link_libraries(sample PRIVATE glpk)
    add_executable(spxsamp1 examples/spxsamp1.c)
    target_link_libraries(spxsamp1 PRIVATE glpk)
    add_executable(spxsamp2 examples/spxsamp2.c)
    target_link_libraries(spxsamp2 PRIVATE glpk)
endif()

# add glpsol as ctest test
add_test(NAME "glpsol_version" COMMAND glpsol --version)
add_test(
    NAME "glpsol_murtagh"
    COMMAND glpsol --mps ${CMAKE_CURRENT_SOURCE_DIR}/examples/murtagh.mps --max
)
# add examples as additional tests
if(GLPK_BUILD_EXAMPLES)
    add_test(NAME iptsamp COMMAND iptsamp WORKING_DIRECTORY 
${GLPK_EXAMPLES_DIR})
    add_test(NAME mplsamp1 COMMAND mplsamp1 WORKING_DIRECTORY 
${GLPK_EXAMPLES_DIR})
    add_test(NAME mplsamp2 COMMAND mplsamp2 WORKING_DIRECTORY 
${GLPK_EXAMPLES_DIR})
    # add_test(NAME netgen COMMAND netgen WORKING_DIRECTORY 
${GLPK_EXAMPLES_DIR})
    add_test(NAME nppsamp COMMAND nppsamp WORKING_DIRECTORY 
${GLPK_EXAMPLES_DIR})
    add_test(NAME sample COMMAND sample WORKING_DIRECTORY ${GLPK_EXAMPLES_DIR})
    add_test(NAME spxsamp1 COMMAND spxsamp1 WORKING_DIRECTORY 
${GLPK_EXAMPLES_DIR})
    add_test(NAME spxsamp2 COMMAND spxsamp2 WORKING_DIRECTORY 
${GLPK_EXAMPLES_DIR})
endif()

# targets to install. glpk includes the glpk.h header
set(GLPK_INSTALL_TARGETS glpk glpsol)
# install library and glpsol when cmake --install is invoked
if(MSVC)
    # Debug config should also install PDB files. they go to lib if libraries
    # are static, bin if libraries are DLL for debugging convenience
    if(BUILD_SHARED_LIBS)
        set(GLPK_PDB_INSTALL_SUBDIR bin)
    else()
        set(GLPK_PDB_INSTALL_SUBDIR lib)
    endif()
    # multi-config installation
    if(GLPK_INSTALL_MULTI_CONFIG)
        install(
            TARGETS ${GLPK_INSTALL_TARGETS}
            RUNTIME DESTINATION bin/$<CONFIG>
            LIBRARY DESTINATION lib/$<CONFIG>
            ARCHIVE DESTINATION lib/$<CONFIG>
            PUBLIC_HEADER DESTINATION include
            COMPONENT glpk
        )
        # Debug config should also install PDB files. they go to lib if
        # libraries are static, bin if libraries are DLL
        if(BUILD_SHARED_LIBS)
            set(GLPK_PDB_INSTALL_SUBDIR bin)
        else()
            set(GLPK_PDB_INSTALL_SUBDIR lib)
        endif()
        # install PDB file for Debug config only
        install(
            FILES ${CMAKE_CURRENT_BINARY_DIR}/Debug/glpk.pdb
            CONFIGURATIONS Debug
            DESTINATION ${GLPK_PDB_INSTALL_SUBDIR}/Debug
            COMPONENT glpk
        )
    # flat installation where artifacts are suffixed with "d"
    else()
        install(
            TARGETS ${GLPK_INSTALL_TARGETS}
            RUNTIME DESTINATION bin
            LIBRARY DESTINATION lib
            ARCHIVE DESTINATION lib
            PUBLIC_HEADER DESTINATION include
            COMPONENT glpk
        )
        install(
            FILES ${CMAKE_CURRENT_BINARY_DIR}/Debug/glpkd.pdb
            CONFIGURATIONS Debug
            DESTINATION ${GLPK_PDB_INSTALL_SUBDIR}
            COMPONENT glpk
        )
    endif()
# typical flat *nix installation
# TODO: for *nix, need pkg-config stuff too, etc.
else()
    install(
        TARGETS ${GLPK_INSTALL_TARGETS}
        RUNTIME DESTINATION bin
        LIBRARY DESTINATION lib
        ARCHIVE DESTINATION lib
        PUBLIC_HEADER DESTINATION include
        COMPONENT glpk
    )
endif()

Reply via email to