Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use Xpress_jll for binaries #220

Merged
merged 9 commits into from
Apr 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.
odow marked this conversation as resolved.
Show resolved Hide resolved

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 @@

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_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\").")

Check warning on line 73 in src/Xpress.jl

View check run for this annotation

Codecov / codecov/patch

src/Xpress.jl#L72-L73

Added lines #L72 - L73 were not covered by tests
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
Loading