Skip to content

Commit 37ccc87

Browse files
authored
Use Gurobi_jll for binaries (#545)
1 parent 46b138c commit 37ccc87

File tree

6 files changed

+112
-78
lines changed

6 files changed

+112
-78
lines changed

.github/workflows/ci.yml

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,24 +10,38 @@ permissions:
1010
contents: read
1111
jobs:
1212
test:
13-
name: 'Gurobi'
14-
runs-on: 'ubuntu-latest'
13+
name: Julia ${{ matrix.version }}-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.jll_version }}
14+
runs-on: ${{ matrix.os }}
1515
strategy:
1616
fail-fast: false
17+
matrix:
18+
version: ['1.6', '1']
19+
os: [ubuntu-latest, macOS-latest, windows-latest]
20+
arch: [x64]
21+
# v9.5.2 does not support the web license service
22+
jll_version: ['10.0.3', '11.0.1']
23+
include:
24+
- version: '1'
25+
os: macos-14
26+
arch: aarch64
27+
jll_version: '11.0.1'
1728
steps:
1829
- uses: actions/checkout@v4
1930
- uses: julia-actions/setup-julia@v2
2031
with:
21-
version: '1'
22-
arch: 'x64'
32+
version: ${{ matrix.version }}
33+
arch: ${{ matrix.arch }}
2334
- uses: julia-actions/cache@v1
24-
- shell: bash
25-
env:
26-
WLSLICENSE: ${{ secrets.WLSLICENSE }}
35+
- shell: julia --project=. --color=yes {0}
2736
run: |
28-
mkdir -p /opt/gurobi
29-
echo "$WLSLICENSE" > /opt/gurobi/gurobi.lic
37+
import Pkg
38+
Pkg.add(; name = "Gurobi_jll", version = ENV["GUROBI_JLL_VERSION"])
39+
env:
40+
GUROBI_JL_SKIP_LIB_CHECK: "true"
41+
GUROBI_JLL_VERSION: ${{ matrix.jll_version }}
3042
- uses: julia-actions/julia-buildpkg@v1
43+
env:
44+
WLSLICENSE: ${{ secrets.WLSLICENSE }}
3145
- uses: julia-actions/julia-runtest@v1
3246
- uses: julia-actions/julia-processcoverage@v1
3347
- uses: codecov/codecov-action@v4

Artifacts.toml

Lines changed: 0 additions & 8 deletions
This file was deleted.

Project.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ repo = "https://github.com/jump-dev/Gurobi.jl"
44
version = "1.2.3"
55

66
[deps]
7-
LazyArtifacts = "4af54fe1-eca0-43a8-85a7-787d91b784e3"
7+
Gurobi_jll = "c018c7e6-a5b0-4aea-8f80-9c1ef9991411"
88
Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb"
99
MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee"
1010

1111
[compat]
12-
LazyArtifacts = "<0.0.1, 1.6"
12+
Gurobi_jll = "~9.5, ~10.0, ~11.0"
1313
Libdl = "<0.0.1, 1.6"
1414
MathOptInterface = "1.12"
1515
Random = "<0.0.1, 1.6"

README.md

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -37,31 +37,51 @@ Free Gurobi licenses are available for [academics and students](https://www.guro
3737

3838
## Installation
3939

40-
First, obtain a license of Gurobi and install Gurobi solver.
40+
To use Gurobi, you need a license. To install the license, first obtain a key
41+
from [gurobi.com](https://www.gurobi.com), then run:
42+
```julia
43+
import Pkg
44+
Pkg.add("Gurobi_jll")
45+
import Gurobi_jll
46+
# Replace the contents xxxxx with your actual key
47+
key = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
48+
run(`$(Gurobi_jll.grbgetkey()) $key`)
49+
```
4150

42-
Then, set the `GUROBI_HOME` environment variable as appropriate and run
43-
`Pkg.add("Gurobi")`:
51+
### Default installation
52+
53+
Install Gurobi as follows:
54+
```julia
55+
import Pkg
56+
Pkg.add("Gurobi")
57+
```
58+
59+
In addition to installing the Gurobi.jl package, this will also download and
60+
install the Gurobi binaries from [Gurobi_jll.jl](https://github.com/jump-dev/Gurobi_jll.jl).
61+
You do not need to install Gurobi separately.
62+
63+
### Manual installation
64+
65+
To opt-out of using the Gurobi_jll binaries, set the `GUROBI_HOME` environment
66+
variable to point to your local installation and set the
67+
`GUROBI_JL_USE_GUROBI_JLL` environment variable to `"false"`, then run
68+
`Pkg.add` and `Pkg.build`:
4469

4570
```julia
4671
# On Windows, this might be
4772
ENV["GUROBI_HOME"] = "C:\\Program Files\\gurobi1100\\win64"
4873
# ... or perhaps ...
4974
ENV["GUROBI_HOME"] = "C:\\gurobi1100\\win64"
5075
# On Mac, this might be
51-
ENV["GUROBI_HOME"] = "/Library/gurobi1100/mac64"
76+
ENV["GUROBI_HOME"] = "/Library/gurobi1100/macos_universal2"
77+
78+
# Opt-out of using Gurobi_jll
79+
ENV["GUROBI_JL_USE_GUROBI_JLL"] = "false"
5280

5381
import Pkg
5482
Pkg.add("Gurobi")
83+
Pkg.build("Gurobi")
5584
```
56-
**Note: your path may differ. Check which folder you installed Gurobi in, and
57-
update the path accordingly.**
58-
59-
By default, building Gurobi.jl will fail if the Gurobi library is not found.
60-
This may not be desirable in certain cases, for example when part of a package's
61-
test suite uses Gurobi as an optional test dependency, but Gurobi cannot be
62-
installed on a CI server running the test suite. To support this use case, the
63-
`GUROBI_JL_SKIP_LIB_CHECK` environment variable may be set (to any value) to
64-
make Gurobi.jl installable (but not usable).
6585

6686
## Use with JuMP
6787

deps/build.jl

Lines changed: 47 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,17 @@ if isfile(DEPS_FILE)
1313
end
1414

1515
if Int === Int32
16-
error("Gurobi.jl does not support 32-bit Julia. Please install a 64-bit Julia.")
16+
error(
17+
"Gurobi.jl does not support 32-bit Julia. Please install a 64-bit Julia.",
18+
)
1719
end
1820

1921
function write_depsfile(path)
2022
open(DEPS_FILE, "w") do io
2123
println(io, "const libgurobi = \"$(escape_string(path))\"")
24+
return
2225
end
26+
return
2327
end
2428

2529
const ALIASES = [
@@ -30,35 +34,33 @@ const ALIASES = [
3034
"gurobi90"
3135
]
3236

33-
paths_to_try = copy(ALIASES)
34-
35-
for a in ALIASES
36-
if haskey(ENV, "GUROBI_HOME")
37-
if Sys.isunix()
38-
push!(paths_to_try, joinpath(ENV["GUROBI_HOME"], "lib", string("lib", a, ".so")))
39-
end
40-
if Sys.iswindows()
41-
push!(paths_to_try, joinpath(ENV["GUROBI_HOME"], "bin", string(a, ".", Libdl.dlext)))
37+
function _try_local_install()
38+
paths_to_try = copy(ALIASES)
39+
for a in ALIASES
40+
root = get(ENV, "GUROBI_HOME", nothing)
41+
if root !== nothing
42+
if Sys.isunix()
43+
push!(paths_to_try, joinpath(root, "lib", "lib$a.so"),)
44+
end
45+
if Sys.iswindows()
46+
push!(paths_to_try, joinpath(root, "bin", "$a.$(Libdl.dlext)"))
47+
end
48+
if Sys.isapple()
49+
push!(paths_to_try, joinpath(root, "lib", "lib$a.dylib"))
50+
end
4251
end
43-
if Sys.isapple()
44-
push!(paths_to_try, joinpath(ENV["GUROBI_HOME"], "lib", string("lib", a, ".dylib")))
52+
if Sys.isapple() # gurobi uses .so on OS X for some reason
53+
push!(paths_to_try, string("lib$a.so"))
54+
push!(paths_to_try, string("lib$a.dylib"))
4555
end
4656
end
47-
# gurobi uses .so on OS X for some reason
48-
if Sys.isapple()
49-
push!(paths_to_try, string("lib$a.so"))
50-
push!(paths_to_try, string("lib$a.dylib"))
51-
end
52-
end
53-
54-
found = false
55-
for l in paths_to_try
56-
d = Libdl.dlopen_e(l)
57-
if d != C_NULL
58-
global found = true
59-
write_depsfile(l)
60-
break
57+
for l in paths_to_try
58+
if Libdl.dlopen_e(l) != C_NULL
59+
write_depsfile(l)
60+
return true
61+
end
6162
end
63+
return false
6264
end
6365

6466
function _print_GUROBI_HOME_help()
@@ -164,6 +166,13 @@ function diagnose_gurobi_install()
164166
_print_GUROBI_HOME_help()
165167
end
166168
end
169+
return error(
170+
"""
171+
Unable to locate Gurobi installation. If the advice above did not help,
172+
open an issue at https://github.com/jump-dev/Gurobi.jl and post the full
173+
print-out of this diagnostic attempt.
174+
""",
175+
)
167176
end
168177

169178
if haskey(ENV, "GUROBI_JL_SKIP_LIB_CHECK")
@@ -172,15 +181,19 @@ if haskey(ENV, "GUROBI_JL_SKIP_LIB_CHECK")
172181
elseif get(ENV, "JULIA_REGISTRYCI_AUTOMERGE", "false") == "true"
173182
# We write a fake depsfile so Gurobi.jl is loadable but not usable.
174183
write_depsfile("__skipped_installation__")
175-
elseif !found && Sys.islinux()
184+
elseif get(ENV, "GUROBI_JL_USE_GUROBI_JLL", "true") == "false"
185+
# The user has asked to avoid Gurobi_jll
186+
found = _try_local_install()
187+
if !found
188+
diagnose_gurobi_install()
189+
end
190+
else
191+
# We're using the artifact
192+
if haskey(ENV, "WLSLICENSE") # This is used by CI
193+
home = Sys.iswindows() ? ENV["USERPROFILE"] : ENV["HOME"]
194+
write(joinpath(home, "gurobi.lic"), ENV["WLSLICENSE"])
195+
end
176196
open(DEPS_FILE, "w") do io
177197
println(io, "# No libgurobi constant; we're using the Artifact.")
178198
end
179-
elseif !found
180-
diagnose_gurobi_install()
181-
error("""
182-
Unable to locate Gurobi installation. If the advice above did not help,
183-
open an issue at https://github.com/jump-dev/Gurobi.jl and post the full
184-
print-out of this diagnostic attempt.
185-
""")
186199
end

src/Gurobi.jl

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,21 @@
66

77
module Gurobi
88

9-
import LazyArtifacts
10-
119
# deps.jl file is always built via `Pkg.build`, even if we didn't find a local
1210
# install and we want to use the artifact instead. This is so Gurobi.jl will be
1311
# recompiled if we update the file. See issue #438 for more details.
1412
include(joinpath(dirname(@__FILE__), "..", "deps", "deps.jl"))
1513

1614
if isdefined(@__MODULE__, :libgurobi)
1715
# deps.jl must define a local installation.
18-
elseif Sys.islinux()
19-
# Let's use the artifact instead.
20-
const libgurobi = joinpath(
21-
LazyArtifacts.artifact"gurobilinux64",
22-
"gurobi1100/linux64/lib/libgurobi110.so",
23-
)
16+
elseif Sys.islinux() || Sys.isapple() || Sys.iswindows()
17+
import Gurobi_jll
18+
const libgurobi = Gurobi_jll.libgurobi
2419
else
25-
error("""
26-
Gurobi not properly installed. Please run Pkg.build(\"Gurobi\"). For
27-
more information go to https://github.com/jump-dev/Gurobi.jl
28-
""")
20+
error(
21+
"Unsupported platform: Use a manual installation by setting " *
22+
"`GUROBI_JL_USE_GUROBI_JLL` to false. See the README for details.",
23+
)
2924
end
3025

3126
const _GUROBI_VERSION = if libgurobi == "__skipped_installation__"

0 commit comments

Comments
 (0)