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

Update wrapper of xprs.h #257

Merged
merged 5 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
1 change: 0 additions & 1 deletion scripts/Project.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
[deps]
Clang = "40e3b903-d033-50b4-a0cc-940c62c95e31"
Xpress = "9e70acf3-d6c9-5be6-b5bd-4e2c73e3e054"
162 changes: 117 additions & 45 deletions scripts/generate.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,52 +3,124 @@
# 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.

# requires julia 1.6

using Clang

# LIBXPRESS_HEADERS are those headers to be wrapped.
const LIBXPRESS_INCLUDE =
joinpath(ENV["XPRESSDIR"], "include") |> expanduser |> normpath
const LIBXPRESS_HEADERS = [joinpath(LIBXPRESS_INCLUDE, header) for header in [
# "bindrv.h",
# "mmhttp.h",
# "mmquad.h",
# "mmsystem.h",
# "mmxprs.h",
# "mosel_dso.h",
# "mosel_mc.h",
# "mosel_ni.h",
# "mosel_nifct.h",
# "mosel_rt.h",
# "xprb.h",
# "xprb_cpp.h",
# "xprd.h",
# "xprm_mc.h",
# "xprm_ni.h",
# "xprm_rt.h",
# "xprnls.h",
"xprs.h",
# "xprs_mse_defaulthandler.h",
# "XPRSdefaultMipSolEnumHandler.java",
# "xslp.h",
]]

wc = init(;
headers = LIBXPRESS_HEADERS,
output_file = joinpath(@__DIR__, "../src/lib.jl"),
common_file = joinpath(@__DIR__, "../src/common.jl"),
clang_includes = vcat(LIBXPRESS_INCLUDE, CLANG_INCLUDE),
clang_args = ["-I", joinpath(LIBXPRESS_INCLUDE, "..")],
header_wrapped = (root, current) -> root == current,
header_library = x -> "libxprs",
clang_diagnostics = true,
using Clang.Generators

# Copy the appropriate xprs.h file to the scripts directory and edit these lines.

old_xprs_filename = joinpath(@__DIR__, "xprs33.01.12.h")
xprs_filename = joinpath(@__DIR__, "xprs42.01.05.h")

context = create_context(
[xprs_filename],
get_default_args(),
load_options(joinpath(@__DIR__, "generate.toml")),
)
build!(context)

function replace_line_cstring(line, signature)
changes = getindex.(
signature,
findall(r"char(\s*?\*| [a-z0-9]+?\[\])", signature),
)
last_index = 1
outputs = String[]
for (change, range) in zip(changes, findall("Ptr{UInt8}", line))
push!(outputs, line[last_index:(first(range)-1)])
if endswith(change, "*")
push!(outputs, "Cstring")
else
push!(outputs, line[range])
end
last_index = last(range) + 1
end
push!(outputs, line[last_index:end])
return join(outputs)
end

run(wc)
function postprocess(filename)
contents = read(filename, String);
# Remove the deprecated if-else blocks
regex = r"if XPRSdeprecated[a-z]+\n\s+(.+?)\n\s+else\n\s+\1\s+end"s
while (m = match(regex, contents)) !== nothing
contents = replace(contents, m.match => m.captures[1])
end
# Remove mutable structs
regex = r"mutable struct (.+?) end"
while (m = match(regex, contents)) !== nothing
contents = replace(contents, m.match => "")
contents = replace(contents, m.captures[1] => "Cvoid")
end
# Remove the cenum block
contents = replace(contents, r"\@cenum.+?end\s+"s => "")
# For backwards compatibility, older versions of Xpress.jl used UInt8
# instead of Cchar.
contents = replace(contents, "Cchar" => "UInt8")
# For backwards compatibility, older versions of Xpress.jl used Cint
# instead of Clong.
# TODO(odow): this is wrong. Fix me in a separate PR.
contents = replace(contents, "Clong" => "Cint")
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I found one super weird thing here. The xprs.jl uses Cint in places where it should use Clong. They are equivalent on windows, but not on Mac or Linux. I don't know if this causes any issues because we don't call the 64( API.

write(filename, contents)
# Add comments to any symbols which are new in the current version
lines = readlines(filename)
old_xprs_contents = read(old_xprs_filename, String);
# XPRSsetcheckedmode(int checkedmode)
xprs_signatures = Dict{String,String}()
for line in readlines(xprs_filename)
if (m = match(r"XPRS_CC (XPRS[a-z0-9\_]+?)\(.+?\)", line)) !== nothing
xprs_signatures[m.captures[1]] = m.match
end
end
open(filename, "w") do io
header = """
# Copyright (c) 2016: Joaquim Garcia, and contributors
#
# 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.
#
# Automatically generated using scripts/generate.jl
#
#! format: off

# # create context
# ctx = create_context(headers, args, options)
"""
write(io, header)
if isempty(lines[end])
pop!(lines)
end
last_line = ""
for line in lines
if startswith(line, "#")
continue # skip
elseif startswith(line, "const ")
m = match(r"const (.+) =", line)
@assert m !== nothing
if !occursin(m[1], old_xprs_contents)
println(io, "# Struct does not exist in v33")
end
elseif startswith(line, "function ")
m = match(r"function (.+)\(", line)
if m !== nothing && !occursin(m[1], old_xprs_contents)
println(io, "# Function does not exist in v33")
end
end
if occursin("Ptr{UInt8}", line)
# Replace Ptr{Cchar} with Cstring for backward compatibility
# with older versions of Xpress.jl
#
# We need to replace char* with Ptr{UInt8} and char[] with
# Cstring.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay. I've hacked around to get the right Cstring and Ptr{UInt8} in the right places. Focusing on minimal changes. We can reconsider in a different PR.

map = Dict("char*" => "Cstring", "char" => "Ptr{UInt8}")
m = match(r"ccall\(\(:(XPRS.+?)\,", line)
@assert m !== nothing
signature = xprs_signatures[m.captures[1]]
line = replace_line_cstring(line, signature)
end
if !(isempty(line) && isempty(last_line))
println(io, line)
end
last_line = line
end
end
return
end

# # run generator
# build!(ctx)
postprocess("src/Lib/xprs.jl")
5 changes: 5 additions & 0 deletions scripts/generate.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[general]
library_name = "libxprs"
output_file_path = "src/Lib/xprs.jl"
print_using_CEnum = false
extract_c_comment_style = "doxygen"
1 change: 0 additions & 1 deletion src/Lib/Lib.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ global libxprs = Xpress.libxprs

set_libxprs(libxprs_) = (global libxprs = libxprs_)

include("common.jl")
include("xprs.jl")

end # Lib
Loading
Loading