Skip to content

Commit f06d85d

Browse files
committed
Fix MQLib_jll shared library recipe (#7)
1 parent cf778aa commit f06d85d

3 files changed

Lines changed: 134 additions & 8 deletions

File tree

c_api/README.md

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,26 @@ containing MQLib's random-forest model files, usually the upstream `hhdata`
3939
directory. If no model files are found, `mqlib_solve_qubo` returns
4040
`MQLIB_STATUS_HYPERHEURISTIC_DATA_NOT_FOUND`.
4141

42-
## Build Sketch
42+
## BinaryBuilder Recipe
4343

44-
The exact build recipe belongs in `MQLib_jll`, but a local syntax check against
45-
an upstream MQLib checkout looks like:
44+
The `MQLib_jll` build recipe in `jll/build_tarballs.jl` builds the existing
45+
`MQLib` executable product and a shared library product named
46+
`libmqlib_c_api`. The library compiles `c_api/src/mqlib_c_api.cpp` with the
47+
upstream MQLib implementation sources, excluding the upstream executable entry
48+
point (`src/main.cpp`), and installs this header as `mqlib_c_api.h`.
49+
50+
Downstream Julia code should call the library product exported by `MQLib_jll`,
51+
for example:
52+
53+
```julia
54+
ccall((:mqlib_c_abi_version, libmqlib_c_api), Cint, ())
55+
```
56+
57+
The public ABI is versioned by `MQLIB_C_ABI_VERSION`. Any incompatible change
58+
to the structs, status codes, or exported functions must bump that value and be
59+
released through a new `MQLib_jll` build.
60+
61+
A local syntax check against an upstream MQLib checkout looks like:
4662

4763
```sh
4864
c++ -std=c++11 \
@@ -52,11 +68,6 @@ c++ -std=c++11 \
5268
c_api/src/mqlib_c_api.cpp
5369
```
5470

55-
When building the shared library, compile `c_api/src/mqlib_c_api.cpp` together
56-
with the upstream MQLib implementation sources needed by the heuristics. The
57-
upstream executable entry point (`src/main.cpp`) should not be part of the
58-
shared-library target.
59-
6071
The small native example in `c_api/examples/solve_qubo.c` demonstrates both an
6172
explicit QUBO heuristic and the hyperheuristic call shape:
6273

jll/build_tarballs.jl

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# Note that this script can accept some limited command-line arguments. Run
2+
# `julia build_tarballs.jl --help` to see a usage message.
3+
using BinaryBuilder, Pkg
4+
5+
name = "MQLib"
6+
version = v"0.1.1"
7+
8+
# Collection of sources required to complete build.
9+
sources = [
10+
GitSource(
11+
"https://github.com/MQLib/MQLib.git",
12+
"585496274af5abb0849d0d47e135496b4688680b",
13+
),
14+
# Provides the C ABI wrapper that is packaged as libmqlib_c_api.
15+
GitSource(
16+
"https://github.com/JuliaQUBO/MQLib.jl.git",
17+
"8b491876ef44b663e8fd04a48d262d72e73faff7",
18+
),
19+
]
20+
21+
# Bash recipe for building across all platforms.
22+
script = raw"""
23+
cd "${WORKSPACE}/srcdir"
24+
25+
MQLIB_SRC="${WORKSPACE}/srcdir/MQLib"
26+
MQLIB_C_HEADER="$(find "${WORKSPACE}/srcdir" -path '*/c_api/include/mqlib_c_api.h' -print -quit)"
27+
MQLIB_JL_SRC="${MQLIB_C_HEADER%/c_api/include/mqlib_c_api.h}"
28+
29+
if [[ ! -f "${MQLIB_JL_SRC}/c_api/src/mqlib_c_api.cpp" ]]; then
30+
echo "Could not find MQLib.jl C ABI sources" >&2
31+
exit 1
32+
fi
33+
34+
cd "${MQLIB_SRC}"
35+
make -j${nproc}
36+
install -Dvm 0755 bin/MQLib "${bindir}/MQLib${exeext}"
37+
install -Dvm 0644 \
38+
"${MQLIB_JL_SRC}/c_api/include/mqlib_c_api.h" \
39+
"${includedir}/mqlib_c_api.h"
40+
41+
mapfile -t MQLIB_LIBRARY_SOURCES < <(find src -name '*.cpp' ! -name main.cpp | sort)
42+
43+
if [[ "${target}" == *-mingw* ]]; then
44+
MQLIB_C_API_LIBRARY="${bindir}/libmqlib_c_api.${dlext}"
45+
MQLIB_C_API_SHARED_FLAGS=(
46+
-shared
47+
-Wl,--out-implib,"${libdir}/libmqlib_c_api.dll.a"
48+
)
49+
elif [[ "${target}" == *-apple-darwin* ]]; then
50+
MQLIB_C_API_LIBRARY="${libdir}/libmqlib_c_api.${dlext}"
51+
MQLIB_C_API_SHARED_FLAGS=(
52+
-dynamiclib
53+
-Wl,-install_name,@rpath/libmqlib_c_api.${dlext}
54+
)
55+
else
56+
MQLIB_C_API_LIBRARY="${libdir}/libmqlib_c_api.${dlext}"
57+
MQLIB_C_API_SHARED_FLAGS=(-shared)
58+
fi
59+
60+
"${CXX}" \
61+
-std=c++11 \
62+
-O2 \
63+
-fPIC \
64+
-DMQLIB_C_BUILD_SHARED \
65+
-Iinclude \
66+
-I"${MQLIB_JL_SRC}/c_api/include" \
67+
"${MQLIB_LIBRARY_SOURCES[@]}" \
68+
"${MQLIB_JL_SRC}/c_api/src/mqlib_c_api.cpp" \
69+
"${MQLIB_C_API_SHARED_FLAGS[@]}" \
70+
-o "${MQLIB_C_API_LIBRARY}" \
71+
-lm
72+
"""
73+
74+
# These are the platforms we will build for by default, unless further
75+
# platforms are passed in on the command line.
76+
platforms = expand_cxxstring_abis(supported_platforms())
77+
78+
# The products that we will ensure are always built.
79+
products = [
80+
ExecutableProduct("MQLib", :MQLib),
81+
LibraryProduct("libmqlib_c_api", :libmqlib_c_api),
82+
]
83+
84+
# Dependencies that must be installed before this package can be built.
85+
dependencies = [
86+
Dependency("CompilerSupportLibraries_jll"; platforms = filter(!Sys.isapple, platforms)),
87+
]
88+
89+
# Build the tarballs, and possibly a `build.jl` as well.
90+
build_tarballs(
91+
ARGS,
92+
name,
93+
version,
94+
sources,
95+
script,
96+
platforms,
97+
products,
98+
dependencies;
99+
julia_compat = "1.6",
100+
)

test/runtests.jl

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ Test.@testset "C ABI contract" begin
4444
Test.@test isfile(header)
4545
Test.@test isfile(source)
4646

47+
recipe = joinpath(root, "jll", "build_tarballs.jl")
48+
Test.@test isfile(recipe)
49+
4750
header_text = read(header, String)
4851
for symbol in (
4952
"MQLIB_C_ABI_VERSION",
@@ -80,6 +83,18 @@ Test.@testset "C ABI contract" begin
8083
end
8184
end
8285

86+
recipe_text = read(recipe, String)
87+
for snippet in (
88+
"ExecutableProduct(\"MQLib\", :MQLib)",
89+
"LibraryProduct(\"libmqlib_c_api\", :libmqlib_c_api)",
90+
"c_api/include/mqlib_c_api.h",
91+
"c_api/src/mqlib_c_api.cpp",
92+
"! -name main.cpp",
93+
"-DMQLIB_C_BUILD_SHARED",
94+
)
95+
Test.@test occursin(snippet, recipe_text)
96+
end
97+
8398
upstream_dir = get(ENV, "MQLIB_UPSTREAM_DIR", "")
8499
cxx = first_available_tool(["c++", "g++", "clang++"])
85100
if isempty(upstream_dir) || !isdir(joinpath(upstream_dir, "include"))

0 commit comments

Comments
 (0)