diff --git a/.github/julia/build_tarballs.jl b/.github/julia/build_tarballs.jl new file mode 100644 index 0000000..2d4b1f9 --- /dev/null +++ b/.github/julia/build_tarballs.jl @@ -0,0 +1,65 @@ +# Note that this script can accept some limited command-line arguments, run +# `julia build_tarballs.jl --help` to see a usage message. +using BinaryBuilder, Pkg + +name = "MiniZinc" + +version = v"2.8.5" + +sources = [ + GitSource( + "https://github.com/MiniZinc/libminizinc.git", + "2fdef7b40921981f3f9ea82017e9d84937ddab77", + ), + DirectorySource(joinpath(@__DIR__, "bundled")), +] + +script = raw""" +cd $WORKSPACE/srcdir/libminizinc + +atomic_patch -p1 ${WORKSPACE}/srcdir/patches/fixes.patch + +# Patch for MinGW toolchain +find .. -type f -exec sed -i 's/Windows.h/windows.h/g' {} + + +# FAST_BUILD is needed when linking HiGHS, because that's what +# we used when compiling HiGHS_jll. +cmake -B build \ + -DCMAKE_INSTALL_PREFIX=${prefix} \ + -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TARGET_TOOLCHAIN} \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_CXX_FLAGS="-std=c++17 -I${includedir}/highs" \ + -DFAST_BUILD=ON +cmake --build build --parallel ${nproc} +cmake --install build +""" + +products = [ + ExecutableProduct("minizinc", :minizinc), +] + +# These are the platforms we will build for by default, unless further +# platforms are passed in on the command line +platforms = expand_cxxstring_abis( + supported_platforms(; exclude = p -> arch(p) == "i686" && Sys.iswindows(p)), +) + +dependencies = [ + Dependency("CompilerSupportLibraries_jll"), + # Use an exact version for HiGHS. @odow has observed segfaults with + # HiGHS_jll v1.5.3 when libminizinc compiled with v1.5.1. + Dependency("HiGHS_jll"; compat="=1.7.1"), +] + +build_tarballs( + ARGS, + name, + version, + sources, + script, + platforms, + products, + dependencies; + preferred_gcc_version = v"12", + julia_compat = "1.6", +) diff --git a/.github/julia/bundled/patches/fixes.patch b/.github/julia/bundled/patches/fixes.patch new file mode 100644 index 0000000..71cbb75 --- /dev/null +++ b/.github/julia/bundled/patches/fixes.patch @@ -0,0 +1,82 @@ +--- a/lib/file_utils.cpp ++++ b/lib/file_utils.cpp +@@ -130,7 +130,7 @@ std::string file_path(const std::string& filename, const std::string& basePath) + auto f = !basePath.empty() && !is_absolute(filename) ? basePath + "/" + filename : filename; + + // Get real path of absolute path or resolve relative to current directory +-#ifdef _MSC_VER ++#ifdef _WIN32 + LPWSTR lpFilePart; + DWORD nBufferLength = GetFullPathNameW(utf8_to_wide(f).c_str(), 0, nullptr, &lpFilePart); + auto* lpBuffer = static_cast(LocalAlloc(LMEM_FIXED, sizeof(WCHAR) * nBufferLength)); +--- a/minizinc.cpp ++++ b/minizinc.cpp +@@ -89,7 +89,7 @@ int run(const std::string& exe, const std::vector& args, bool jsonS + + } // namespace + +-#ifdef _WIN32 ++#if defined(_WIN32) && !defined(__MINGW32__) + #include + + int wmain(int argc, wchar_t* argv[], wchar_t* envp[]) { +@@ -121,7 +121,9 @@ int wmain(int argc, wchar_t* argv[], wchar_t* envp[]) { + } + #else + int main(int argc, const char** argv) { ++ #if !defined(__MINGW32__) + OverflowHandler::install(argv); ++ #endif + std::vector args(argc - 1); + bool jsonStream = false; + for (int i = 1; i < argc; i++) { +--- a/include/minizinc/file_utils.hh ++++ b/include/minizinc/file_utils.hh +@@ -16,7 +16,7 @@ + + // Macro so that we can use overloaded wide versions of fstream::open for Windows + #ifdef _WIN32 +-#define FILE_PATH(path) MiniZinc::FileUtils::utf8_to_wide(path) ++#define FILE_PATH(path) (path) + #else + #define FILE_PATH(path) (path) + #endif +--- a/include/minizinc/process.hh ++++ b/include/minizinc/process.hh +@@ -225,11 +225,11 @@ public: + std::condition_variable cv; + + std::deque outputQueue; +- thread thrStdout(&ReadPipePrint, g_hChildStd_OUT_Rd, &doneStdout, nullptr, &outputQueue, ++ std::thread thrStdout(&ReadPipePrint, g_hChildStd_OUT_Rd, &doneStdout, nullptr, &outputQueue, + &pipeMutex, &cv_mutex, &cv); +- thread thrStderr(&ReadPipePrint, g_hChildStd_ERR_Rd, &doneStderr, &_pS2Out->getLog(), ++ std::thread thrStderr(&ReadPipePrint, g_hChildStd_ERR_Rd, &doneStderr, &_pS2Out->getLog(), + nullptr, &pipeMutex, nullptr, nullptr); +- thread thrTimeout([&] { ++ std::thread thrTimeout([&] { + auto shouldStop = [&] { return hadInterrupt || (doneStderr && doneStdout); }; + std::unique_lock lck(_interruptMutex); + if (_timelimit != 0) { +--- a/solvers/MIP/MIP_cplex_wrap.cpp ++++ b/solvers/MIP/MIP_cplex_wrap.cpp +@@ -61,7 +61,7 @@ void* dll_open(const std::string& file) { + } + void* dll_sym(void* dll, const char* sym) { + #ifdef _WIN32 +- void* ret = GetProcAddress((HMODULE)dll, sym); ++ void* ret = (void*)GetProcAddress((HMODULE)dll, sym); + #else + void* ret = dlsym(dll, sym); + #endif +--- a/solvers/MIP/MIP_gurobi_wrap.cpp ++++ b/solvers/MIP/MIP_gurobi_wrap.cpp +@@ -263,7 +263,7 @@ void* dll_open(const char* file) { + } + void* dll_sym(void* dll, const char* sym) { + #ifdef _WIN32 +- void* ret = GetProcAddress((HMODULE)dll, sym); ++ void* ret = (void*)GetProcAddress((HMODULE)dll, sym); + #else + void* ret = dlsym(dll, sym); + #endif diff --git a/.github/julia/bundled/patches/fixes.patch.bak b/.github/julia/bundled/patches/fixes.patch.bak new file mode 100644 index 0000000..aeb0ad7 --- /dev/null +++ b/.github/julia/bundled/patches/fixes.patch.bak @@ -0,0 +1,80 @@ +--- a/include/minizinc/file_utils.hh ++++ b/include/minizinc/file_utils.hh +@@ -15,12 +15,16 @@ + #include + + // Macro so that we can use overloaded wide versions of fstream::open for Windows +-#ifdef _WIN32 ++#if defined(_WIN32) && !defined(__MINGW32__) + #define FILE_PATH(path) MiniZinc::FileUtils::utf8_to_wide(path) + #else + #define FILE_PATH(path) (path) + #endif + ++#ifdef __MINGW32__ ++ #define realpath(N,R) _fullpath((R),(N),PATH_MAX) ++#endif ++ + namespace MiniZinc { + namespace FileUtils { + +--- a/minizinc.cpp ++++ b/minizinc.cpp +@@ -89,7 +89,7 @@ int run(const std::string& exe, const std::vector& args, bool jsonS + + } // namespace + +-#ifdef _WIN32 ++#if defined(_WIN32) && !defined(__MINGW32__) + #include + + int wmain(int argc, wchar_t* argv[], wchar_t* envp[]) { +@@ -121,7 +121,9 @@ int wmain(int argc, wchar_t* argv[], wchar_t* envp[]) { + } + #else + int main(int argc, const char** argv) { ++ #if !defined(__MINGW32__) + OverflowHandler::install(argv); ++ #endif + std::vector args(argc - 1); + bool jsonStream = false; + for (int i = 1; i < argc; i++) { +--- a/include/minizinc/process.hh ++++ b/include/minizinc/process.hh +@@ -225,11 +225,11 @@ public: + std::condition_variable cv; + + std::deque outputQueue; +- thread thrStdout(&ReadPipePrint, g_hChildStd_OUT_Rd, &doneStdout, nullptr, &outputQueue, ++ std::thread thrStdout(&ReadPipePrint, g_hChildStd_OUT_Rd, &doneStdout, nullptr, &outputQueue, + &pipeMutex, &cv_mutex, &cv); +- thread thrStderr(&ReadPipePrint, g_hChildStd_ERR_Rd, &doneStderr, &_pS2Out->getLog(), ++ std::thread thrStderr(&ReadPipePrint, g_hChildStd_ERR_Rd, &doneStderr, &_pS2Out->getLog(), + nullptr, &pipeMutex, nullptr, nullptr); +- thread thrTimeout([&] { ++ std::thread thrTimeout([&] { + auto shouldStop = [&] { return hadInterrupt || (doneStderr && doneStdout); }; + std::unique_lock lck(_interruptMutex); + if (_timelimit != 0) { +--- a/solvers/MIP/MIP_cplex_wrap.cpp ++++ b/solvers/MIP/MIP_cplex_wrap.cpp +@@ -61,7 +61,7 @@ void* dll_open(const std::string& file) { + } + void* dll_sym(void* dll, const char* sym) { + #ifdef _WIN32 +- void* ret = GetProcAddress((HMODULE)dll, sym); ++ void* ret = (void*)GetProcAddress((HMODULE)dll, sym); + #else + void* ret = dlsym(dll, sym); + #endif +--- a/solvers/MIP/MIP_gurobi_wrap.cpp ++++ b/solvers/MIP/MIP_gurobi_wrap.cpp +@@ -263,7 +263,7 @@ void* dll_open(const char* file) { + } + void* dll_sym(void* dll, const char* sym) { + #ifdef _WIN32 +- void* ret = GetProcAddress((HMODULE)dll, sym); ++ void* ret = (void*)GetProcAddress((HMODULE)dll, sym); + #else + void* ret = dlsym(dll, sym); + #endif diff --git a/.github/workflows/TagBot.yml b/.github/workflows.bak/TagBot.yml similarity index 100% rename from .github/workflows/TagBot.yml rename to .github/workflows.bak/TagBot.yml diff --git a/.github/workflows/ci.yml b/.github/workflows.bak/ci.yml similarity index 100% rename from .github/workflows/ci.yml rename to .github/workflows.bak/ci.yml diff --git a/.github/workflows/format_check.yml b/.github/workflows.bak/format_check.yml similarity index 100% rename from .github/workflows/format_check.yml rename to .github/workflows.bak/format_check.yml diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml new file mode 100644 index 0000000..c61c78d --- /dev/null +++ b/.github/workflows/test-windows.yml @@ -0,0 +1,55 @@ +name: Build on Linux, Run on Windows +on: + push: + branches: [master] + pull_request: + types: [opened, synchronize, reopened] +permissions: + actions: write + contents: read +jobs: + build-linux: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: julia-actions/setup-julia@v2 + with: + version: "1.7" + arch: x64 + - uses: julia-actions/cache@v2 + - run: | + PACKAGE=MiniZinc_jll + PLATFORM=x86_64-w64-mingw32-cxx11 + julia --color=yes -e 'using Pkg; Pkg.add("BinaryBuilder")' + julia --color=yes .github/julia/build_tarballs.jl ${PLATFORM} --verbose --deploy=local + file=/home/runner/.julia/dev/${PACKAGE}/Artifacts.toml + sha1=$(grep '^git-tree-sha1' "$file" | cut -d '"' -f2) + echo "ARTIFACT_SHA=${sha1}" >> $GITHUB_ENV + - uses: actions/upload-artifact@v4 + with: + name: artifacts + path: '/home/runner/.julia/artifacts/${{ env.ARTIFACT_SHA }}' + run-windows: + runs-on: windows-latest + needs: build-linux + steps: + - uses: actions/checkout@v4 + - uses: julia-actions/setup-julia@v2 + with: + version: "1" + arch: x64 + - uses: julia-actions/cache@v2 + - uses: julia-actions/julia-buildpkg@v1 + - uses: actions/download-artifact@v4 + with: + name: artifacts + path: override + - shell: julia --color=yes --project=. {0} + run: | + import MiniZinc_jll + artifact_dir = MiniZinc_jll.artifact_dir + sha = last(splitpath(artifact_dir)) + dir = escape_string(joinpath(ENV["GITHUB_WORKSPACE"], "override")) + content = "$sha = \"$(dir)\"\n" + write(replace(artifact_dir, sha => "Overrides.toml"), content) + - uses: julia-actions/julia-runtest@v1 diff --git a/src/optimize.jl b/src/optimize.jl index e8da0b5..71669de 100644 --- a/src/optimize.jl +++ b/src/optimize.jl @@ -61,7 +61,7 @@ function _minizinc_exe(f::F) where {F} else return f(joinpath(user_dir, "minizinc")) end - elseif Sys.islinux() || Sys.isapple() + else MiniZinc_jll.is_available() return f(MiniZinc_jll.minizinc()) end return error( @@ -103,6 +103,7 @@ function _run_minizinc(dest::Optimizer) if isfile(_stderr) status *= read(_stderr, String) end + @show status return status end if isfile(output)