From c53f60affa48b30d23d6de6c5dfea7161bfa85a7 Mon Sep 17 00:00:00 2001 From: odow Date: Fri, 1 Mar 2024 13:43:45 +1300 Subject: [PATCH 1/9] Use Xpress_jll for binaries --- .github/workflows/ci.yml | 20 +++++++++++++++-- Project.toml | 6 ++--- README.md | 48 +++++++++++++++++++++++++++++++++++++--- deps/build.jl | 21 ------------------ src/Lib/Lib.jl | 4 +++- src/Xpress.jl | 19 +++++++++------- src/license.jl | 5 +++++ test/runtests.jl | 10 ++++++++- 8 files changed, 94 insertions(+), 39 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 897bc794..7e553365 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,14 +10,20 @@ permissions: contents: read jobs: test: - name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }} + name: Julia ${{ matrix.version }}-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.XPRESS_JLL_VERSION }} runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: version: ['1.6', '1'] # Test against LTS and current minor release - os: [ubuntu-latest, macOS-latest] + os: [ubuntu-latest, macOS-latest, windows-latest] arch: [x64] + XPRESS_JLL_VERSION: ['8.13.4', '9.3.0'] + include: + - version: '1' + os: macos-14 + arch: aarch64 + XPRESS_JLL_VERSION: '9.3.0' steps: - uses: actions/checkout@v4 - uses: julia-actions/setup-julia@v1 @@ -26,7 +32,17 @@ jobs: arch: ${{ matrix.arch }} - uses: julia-actions/cache@v1 - uses: julia-actions/julia-buildpkg@v1 + env: + XPRESS_JL_SKIP_LIB_CHECK: "true" + - shell: julia --project=. --color=yes {0} + run: | + import Pkg + Pkg.add(; name = "Xpress_jll", version = ENV["XPRESS_JLL_VERSION"]) + env: + XPRESS_JLL_VERSION: ${{ matrix.XPRESS_JLL_VERSION }} - uses: julia-actions/julia-runtest@v1 + env: + XPRESS_JL_COMMUNITY_XPAUTH_XPR: ${{ secrets.XPRESS_JL_COMMUNITY_XPAUTH_XPR }} - uses: julia-actions/julia-processcoverage@v1 - uses: codecov/codecov-action@v1 with: diff --git a/Project.toml b/Project.toml index 6e22d46f..ca6b7b9a 100644 --- a/Project.toml +++ b/Project.toml @@ -5,19 +5,19 @@ url = "https://github.com/jump-dev/Xpress.jl" version = "0.16.2" [deps] -Downloads = "f43a241f-c20a-4ad4-852c-f6b1247861c6" Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee" [compat] -Downloads = "<0.0.1, 1" Libdl = "<0.0.1, 1.6" MathOptInterface = "1" Test = "<0.0.1, 1.6" +Xpress_jll = "=8.13.4, =9.3.0" julia = "1.6" [extras] Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" +Xpress_jll = "308bddfa-7f95-4fa6-a557-f2c7addc1869" [targets] -test = ["Test"] +test = ["Test", "Xpress_jll"] diff --git a/README.md b/README.md index be5e14ce..0776441a 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,11 @@ The underlying solver is a closed-source commercial product for which you must First, obtain a license of Xpress and install Xpress solver, following the [instructions on the FICO website](https://www.fico.com/products/fico-xpress-solver). +Ensure that the `XPRESSDIR` license variable is set to the install location by +checking the output of: +```julia +julia> ENV["XPRESSDIR"] +``` Then, install this package using: ```julia @@ -46,12 +51,47 @@ import Pkg Pkg.add("Xpress") ``` +### Skipping installation + By default, building Xpress.jl will fail if the Xpress library is not found. + This may not be desirable in certain cases, for example when part of a package's test suite uses Xpress as an optional test dependency, but Xpress cannot be -installed on a CI server running the test suite. To support this use case, the -`XPRESS_JL_SKIP_LIB_CHECK` environment variable may be set (to any value) to -make Xpress.jl installable (but not usable). +installed on a CI server running the test suite. + +To skip the error, set the `XPRESS_JL_SKIP_LIB_CHECK` environment variable to +`true` to make Xpress.jl installable (but not usable). + +```julia +ENV["XPRESS_JL_SKIP_LIB_CHECK"] = true +import Pkg +Pkg.add("Xpress") +``` + +## Use with Xpress_jll + +Instead of manually installing Xpress, you can use the binaries provided by the +[Xpress_jll.jl](https://github.com/jump-dev/Xpress_jll.jl) package. + +By using Xpress_jll, you agree to certain license conditions. See the +[Xpress_jll.jl README](https://github.com/jump-dev/Xpress_jll.jl/tree/master?tab=readme-ov-file#license) +for more details. + +By default, `Xpress_jll` includes a limited size community license. If you have +purchased a license for FICO Xpress, you should additionally set the +`XPAUTH_PATH` environment variable to point to your license file. + +```julia +import Xpress_jll +# This environment variable must be set _before_ loading Xpress.jl +ENV["XPRESS_JL_LIBRARY"] = Xpress_jll.libxprs +# Optional: point to the directory of your xpauth.xpr license file +ENV["XPAUTH_PATH"] = "/path/to/directory" +using Xpress +``` + +If you plan to use Xpress_jll, `Pkg.add("Xpress")` will fail because it cannot +find a local installation of Xpress. Therefore, you should ## Use with JuMP @@ -102,6 +142,8 @@ current implementation should be considered experimental. - `XPRESS_JL_NO_DEPS_ERROR`: Disable error when do deps.jl file is found. - `XPRESS_JL_NO_AUTO_INIT`: Disable automatic run of `Xpress.initialize()`. Specially useful for explicitly loading the dynamic library. + - `XPRESS_JL_LIBRARY`: Provide a custom path to `libxprs` + - `XPAUTH_PATH`: Provide a custom path to the license file ## C API diff --git a/deps/build.jl b/deps/build.jl index 20128e87..44455184 100644 --- a/deps/build.jl +++ b/deps/build.jl @@ -3,7 +3,6 @@ # Use of this source code is governed by an MIT-style license that can be found # in the LICENSE.md file or at https://opensource.org/licenses/MIT. -import Downloads import Libdl const DEPS_FILE = joinpath(dirname(@__FILE__), "deps.jl") @@ -41,24 +40,6 @@ function local_installation() """) end -function ci_installation() - url = if Sys.islinux() - "https://anaconda.org/fico-xpress/xpress/9.3.0/download/linux-64/xpress-9.3.0-py310ha14b774_0.tar.bz2" - else - @assert Sys.isapple() - "https://anaconda.org/fico-xpress/xpress/9.3.0/download/osx-64/xpress-9.3.0-py310h9b76c6a_0.tar.bz2" - end - Downloads.download(url, "xpress.tar.bz2") - run(`tar -xjf xpress.tar.bz2`) - root = "lib/python3.10/site-packages/xpress" - run(`cp $root/license/community-xpauth.xpr $root/lib/xpauth.xpr`) - if Sys.islinux() - run(`cp $root/lib/libxprs.so.42 $root/lib/libxprs.so`) - end - write_deps_file(joinpath(@__DIR__, root, "lib")) - return -end - if isfile(DEPS_FILE) rm(DEPS_FILE) end @@ -67,8 +48,6 @@ if haskey(ENV, "XPRESS_JL_SKIP_LIB_CHECK") # Skip! elseif get(ENV, "JULIA_REGISTRYCI_AUTOMERGE", "false") == "true" write_deps_file("julia_registryci_automerge") -elseif get(ENV, "CI", "") == "true" - ci_installation() else local_installation() end diff --git a/src/Lib/Lib.jl b/src/Lib/Lib.jl index 046468e5..447f4f06 100644 --- a/src/Lib/Lib.jl +++ b/src/Lib/Lib.jl @@ -6,7 +6,9 @@ module Lib import ..Xpress -const libxprs = Xpress.libxprs +global libxprs = Xpress.libxprs + +set_libxprs(libxprs_) = (global libxprs = libxprs_) include("common.jl") include("xprs.jl") diff --git a/src/Xpress.jl b/src/Xpress.jl index bd962bba..cacafea8 100644 --- a/src/Xpress.jl +++ b/src/Xpress.jl @@ -13,17 +13,14 @@ const depsjl_path = joinpath(@__DIR__, "..", "deps", "deps.jl") if isfile(depsjl_path) include(depsjl_path) -elseif !haskey(ENV, "XPRESS_JL_NO_DEPS_ERROR") - error("XPRESS cannot be loaded. Please run Pkg.build(\"Xpress\").") + global libxprs = joinpath( + xpressdlpath, + string(Sys.iswindows() ? "" : "lib", "xprs", ".", Libdl.dlext), + ) else - const xpressdlpath = "" + global libxprs = "" end -const libxprs = joinpath( - xpressdlpath, - string(Sys.iswindows() ? "" : "lib", "xprs", ".", Libdl.dlext), -) - include("Lib/Lib.jl") include("helper.jl") include("api.jl") @@ -69,6 +66,12 @@ include("MOI/MOI_wrapper.jl") include("MOI/MOI_callbacks.jl") function __init__() + if haskey(ENV, "XPRESS_JL_LIBRARY") + global libxprs = ENV["XPRESS_JL_LIBRARY"] + Lib.set_libxprs(libxprs) + elseif isempty(libxprs) && !haskey(ENV, "XPRESS_JL_NO_DEPS_ERROR") + error("XPRESS cannot be loaded. Please run Pkg.build(\"Xpress\").") + end if !haskey(ENV, "XPRESS_JL_NO_AUTO_INIT") && get(ENV, "JULIA_REGISTRYCI_AUTOMERGE", "false") != "true" initialize() diff --git a/src/license.jl b/src/license.jl index 0d73827d..429b7a05 100644 --- a/src/license.jl +++ b/src/license.jl @@ -21,6 +21,11 @@ function _get_xpauthpath(xpauth_path = "", verbose::Bool = true) # Search in `xpress/lib/../bin/xpauth.xpr`. This is a common location on # Windows. push!(candidates, joinpath(dirname(libdir), "bin", XPAUTH)) + # This location is used by Xpress_jll + push!( + candidates, + joinpath(dirname(dirname(libxprs)), "license", "community-xpauth.xpr"), + ) for candidate in candidates # We assume a relative root directory of the shared library. If # `candidate` is an absolute path, thhen joinpath will ignore libdir and diff --git a/test/runtests.jl b/test/runtests.jl index e03e5f20..50c7ba6d 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -3,6 +3,14 @@ # Use of this source code is governed by an MIT-style license that can be found # in the LICENSE.md file or at https://opensource.org/licenses/MIT. +import Xpress_jll +ENV["XPRESS_JL_LIBRARY"] = Xpress_jll.libxprs +if haskey(ENV, "XPRESS_JL_COMMUNITY_XPAUTH_XPR") + ENV["XPAUTH_PATH"] = dirname(@__DIR__) + contents = ENV["XPRESS_JL_COMMUNITY_XPAUTH_XPR"] + write(joinpath(dirname(@__DIR__), "xpauth.xpr"), contents) +end + using Test using Xpress @@ -29,7 +37,7 @@ end @testset "test_licensing" begin # It is important that we test this first, so that there no XpressProblem # objects with finalizers that may get run during the function call. - test_licensing() + # test_licensing() end println(Xpress.get_banner()) From bf05c94cefe22c502d6b12b585c4bd96fd7b412b Mon Sep 17 00:00:00 2001 From: odow Date: Sat, 30 Mar 2024 11:15:12 +1300 Subject: [PATCH 2/9] Update --- .github/workflows/ci.yml | 10 ++++++++-- test/runtests.jl | 4 +--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7e553365..0dbfc14c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,13 +36,19 @@ jobs: XPRESS_JL_SKIP_LIB_CHECK: "true" - shell: julia --project=. --color=yes {0} run: | + import Xpress_jll + # Assume Pkg has installed the latest version with a valid community + # license. Copy it to the current directory. + write( + "xpauth.xpr", + joinpath(dirname(dirname(Xpress_jll.libxprs)), "license", "community-xpauth.xpr"), + ) + # Now install the specific version of Xpress import Pkg Pkg.add(; name = "Xpress_jll", version = ENV["XPRESS_JLL_VERSION"]) env: XPRESS_JLL_VERSION: ${{ matrix.XPRESS_JLL_VERSION }} - uses: julia-actions/julia-runtest@v1 - env: - XPRESS_JL_COMMUNITY_XPAUTH_XPR: ${{ secrets.XPRESS_JL_COMMUNITY_XPAUTH_XPR }} - uses: julia-actions/julia-processcoverage@v1 - uses: codecov/codecov-action@v1 with: diff --git a/test/runtests.jl b/test/runtests.jl index 50c7ba6d..67cb102b 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -5,10 +5,8 @@ import Xpress_jll ENV["XPRESS_JL_LIBRARY"] = Xpress_jll.libxprs -if haskey(ENV, "XPRESS_JL_COMMUNITY_XPAUTH_XPR") +if isfile(joinpath(dirname(@__DIR__), "xpauth.xpr")) ENV["XPAUTH_PATH"] = dirname(@__DIR__) - contents = ENV["XPRESS_JL_COMMUNITY_XPAUTH_XPR"] - write(joinpath(dirname(@__DIR__), "xpauth.xpr"), contents) end using Test From 16bc354b798d2cd607230dda56c60692305de9dd Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Sat, 30 Mar 2024 13:38:40 +1300 Subject: [PATCH 3/9] Update ci.yml --- .github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0dbfc14c..32612cf5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,6 +36,8 @@ jobs: XPRESS_JL_SKIP_LIB_CHECK: "true" - shell: julia --project=. --color=yes {0} run: | + import Pkg + Pkg.add("Xpress_jll") import Xpress_jll # Assume Pkg has installed the latest version with a valid community # license. Copy it to the current directory. @@ -44,7 +46,6 @@ jobs: joinpath(dirname(dirname(Xpress_jll.libxprs)), "license", "community-xpauth.xpr"), ) # Now install the specific version of Xpress - import Pkg Pkg.add(; name = "Xpress_jll", version = ENV["XPRESS_JLL_VERSION"]) env: XPRESS_JLL_VERSION: ${{ matrix.XPRESS_JLL_VERSION }} From c155cb0c9b3df7c515423d86f1254e4c44f45fd9 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Sat, 30 Mar 2024 14:00:03 +1300 Subject: [PATCH 4/9] Update ci.yml --- .github/workflows/ci.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 32612cf5..55b54ac1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,10 +41,8 @@ jobs: import Xpress_jll # Assume Pkg has installed the latest version with a valid community # license. Copy it to the current directory. - write( - "xpauth.xpr", - joinpath(dirname(dirname(Xpress_jll.libxprs)), "license", "community-xpauth.xpr"), - ) + license_dir = joinpath(dirname(dirname(Xpress_jll.libxprs)), "license") + cp(joinpath(license_dir, "community-xpauth.xpr"), "xpauth.xpr") # Now install the specific version of Xpress Pkg.add(; name = "Xpress_jll", version = ENV["XPRESS_JLL_VERSION"]) env: From e654677b9b254da3478ea58c91e4fcf85f289a29 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Sat, 30 Mar 2024 15:36:39 +1300 Subject: [PATCH 5/9] Update ci.yml --- .github/workflows/ci.yml | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 55b54ac1..069c122c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,6 +18,7 @@ jobs: version: ['1.6', '1'] # Test against LTS and current minor release os: [ubuntu-latest, macOS-latest, windows-latest] arch: [x64] + # If updating most recent version, change shell script below XPRESS_JLL_VERSION: ['8.13.4', '9.3.0'] include: - version: '1' @@ -37,14 +38,15 @@ jobs: - shell: julia --project=. --color=yes {0} run: | import Pkg - Pkg.add("Xpress_jll") + Pkg.add(; name = "Xpress_jll", version = "9.3.0") import Xpress_jll - # Assume Pkg has installed the latest version with a valid community - # license. Copy it to the current directory. - license_dir = joinpath(dirname(dirname(Xpress_jll.libxprs)), "license") - cp(joinpath(license_dir, "community-xpauth.xpr"), "xpauth.xpr") - # Now install the specific version of Xpress - Pkg.add(; name = "Xpress_jll", version = ENV["XPRESS_JLL_VERSION"]) + # Older licenses may have expired. Copy 9.3.0 license to cwd and then + # install specific version. + if ENV["XPRESS_JLL_VERSION"] != "9.3.0" + license_dir = joinpath(dirname(dirname(Xpress_jll.libxprs)), "license") + cp(joinpath(license_dir, "community-xpauth.xpr"), "xpauth.xpr") + Pkg.add(; name = "Xpress_jll", version = ENV["XPRESS_JLL_VERSION"]) + end env: XPRESS_JLL_VERSION: ${{ matrix.XPRESS_JLL_VERSION }} - uses: julia-actions/julia-runtest@v1 From 8e9e9d1c94e25a21c6f6713311421cfde085104b Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Sat, 30 Mar 2024 15:37:39 +1300 Subject: [PATCH 6/9] Update runtests.jl --- test/runtests.jl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/runtests.jl b/test/runtests.jl index 67cb102b..44755aa3 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -13,6 +13,9 @@ using Test using Xpress function test_licensing() + if haskey(ENV, "XPAUTH_PATH") || haskey(ENV, "XPRESSDIR") + return # Skip for non-standand licenses + end # Create a bogus license file xpauth_path = mktempdir() filename = joinpath(xpauth_path, "xpauth.xpr") @@ -35,7 +38,7 @@ end @testset "test_licensing" begin # It is important that we test this first, so that there no XpressProblem # objects with finalizers that may get run during the function call. - # test_licensing() + test_licensing() end println(Xpress.get_banner()) From c15f1e6fed1f4259a0808c668b05de985ed82483 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Sun, 31 Mar 2024 11:02:55 +1300 Subject: [PATCH 7/9] Update runtests.jl --- test/runtests.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/runtests.jl b/test/runtests.jl index 44755aa3..56d9afbd 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -13,8 +13,8 @@ using Test using Xpress function test_licensing() - if haskey(ENV, "XPAUTH_PATH") || haskey(ENV, "XPRESSDIR") - return # Skip for non-standand licenses + if any(k -> haskey(ENV, k), ("XPAUTH_PATH", "XPRESSDIR", "XPRESS_JL_LIBRARY")) + return # Skip for non-standard licenses end # Create a bogus license file xpauth_path = mktempdir() From a43181c95f55106ec29c0f7a53f3e1c75a2a199b Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Mon, 1 Apr 2024 10:12:25 +1300 Subject: [PATCH 8/9] Update README.md --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0776441a..8ea9dd6b 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,8 @@ for more details. By default, `Xpress_jll` includes a limited size community license. If you have purchased a license for FICO Xpress, you should additionally set the -`XPAUTH_PATH` environment variable to point to your license file. +`XPAUTH_PATH` environment variable to point to the directory of your license +file. ```julia import Xpress_jll @@ -91,7 +92,8 @@ using Xpress ``` If you plan to use Xpress_jll, `Pkg.add("Xpress")` will fail because it cannot -find a local installation of Xpress. Therefore, you should +find a local installation of Xpress. Therefore, you should set +`XPRESS_JL_SKIP_LIB_CHECK` before installing. ## Use with JuMP From 0c7664ba13d028a15b3e37999a8af6e0beb231b7 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Tue, 2 Apr 2024 10:38:12 +1300 Subject: [PATCH 9/9] Update README.md Co-authored-by: Joaquim Dias Garcia --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 8ea9dd6b..fcf45ab1 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,12 @@ If you plan to use Xpress_jll, `Pkg.add("Xpress")` will fail because it cannot find a local installation of Xpress. Therefore, you should set `XPRESS_JL_SKIP_LIB_CHECK` before installing. +The community license that is shipped with `Xpress_jll`, has an expiration date. +If usage fails because of the community license expiration date, please update +to more recent version of `Xpress_jll`. If an older version of `Xpress_jll` is +required for reproducibility issues, you should obtain and manually specify a +license via the `XPAUTH_PATH` environment variable. + ## Use with JuMP To use Xpress with JuMP, use: