Skip to content

Commit

Permalink
Use Xpress_jll for binaries (#220)
Browse files Browse the repository at this point in the history
Co-authored-by: Joaquim Dias Garcia <joaquimdgarcia@gmail.com>
  • Loading branch information
odow and joaquimg authored Apr 2, 2024
1 parent 82fe624 commit 5eb8119
Show file tree
Hide file tree
Showing 8 changed files with 109 additions and 38 deletions.
27 changes: 25 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,21 @@ 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]
# If updating most recent version, change shell script below
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
Expand All @@ -26,6 +33,22 @@ 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 = "9.3.0")
import Xpress_jll
# 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
- uses: julia-actions/julia-processcoverage@v1
- uses: codecov/codecov-action@v1
Expand Down
6 changes: 3 additions & 3 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
56 changes: 53 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -46,12 +51,55 @@ 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 the directory of 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 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

Expand Down Expand Up @@ -102,6 +150,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

Expand Down
21 changes: 0 additions & 21 deletions deps/build.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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
Expand All @@ -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
4 changes: 3 additions & 1 deletion src/Lib/Lib.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
19 changes: 11 additions & 8 deletions src/Xpress.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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()
Expand Down
5 changes: 5 additions & 0 deletions src/license.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 9 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,19 @@
# 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 isfile(joinpath(dirname(@__DIR__), "xpauth.xpr"))
ENV["XPAUTH_PATH"] = dirname(@__DIR__)
end

using Test
using Xpress

function test_licensing()
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()
filename = joinpath(xpauth_path, "xpauth.xpr")
Expand Down

0 comments on commit 5eb8119

Please sign in to comment.