From ef239f835fbd63cdd96172319308215c12bf7a94 Mon Sep 17 00:00:00 2001 From: Megvii Engine Team Date: Thu, 6 Aug 2020 15:10:26 +0800 Subject: [PATCH] feat(windows/python_whl): make windows HAPPY for build megbrain python package GitOrigin-RevId: 92b2c07bf939efcac6432bf4c93cd5c46910f129 --- CMakeLists.txt | 17 ++- python_module/CMakeLists.txt | 10 +- python_module/megengine/_internal/plugin.py | 9 +- .../megengine/utils/max_recursion_limit.py | 13 +- python_module/setup.py | 6 +- python_module/src/cpp/function_replace.cpp | 38 ++++- python_module/src/cpp/megbrain_config.cpp | 28 +++- python_module/src/cpp/megbrain_config.h | 4 +- python_module/src/cpp/megbrain_wrap.cpp | 7 + python_module/src/cpp/plugin.cpp | 60 ++++++-- python_module/src/cpp/python_helper.cpp | 2 +- python_module/src/swig/mgb.i | 8 +- .../test/integration/test_distributed.py | 3 + python_module/test/run.sh | 4 +- .../test/unit/distributed/test_functional.py | 33 +++++ .../test/unit/distributed/test_util.py | 12 ++ .../test/unit/module/test_batchnorm.py | 15 ++ python_module/test/unit/module/test_module.py | 10 +- scripts/cmake-build/host_build.sh | 15 +- scripts/whl/BUILD_PYTHON_WHL_README.md | 63 +++++++- scripts/whl/macos/macos_build_whl.sh | 9 +- .../whl/windows/fix-ptr-define-issue.patch | 28 ++++ scripts/whl/windows/windows_build_whl.sh | 138 ++++++++++++++++++ sdk/load-and-run/src/mgblar.cpp | 8 +- src/core/impl/utils/debug.cpp | 18 ++- src/core/include/megbrain/common.h | 5 + src/serialization/impl/opr_registry.cpp | 5 +- .../megbrain/serialization/opr_registry.h | 3 +- test/src/include/megbrain/test/helper.h | 7 - 29 files changed, 506 insertions(+), 72 deletions(-) create mode 100644 scripts/whl/windows/fix-ptr-define-issue.patch create mode 100755 scripts/whl/windows/windows_build_whl.sh diff --git a/CMakeLists.txt b/CMakeLists.txt index 6241b6a8c..6657e578b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -439,6 +439,7 @@ endif() set(MGB_JIT ${MGE_WITH_JIT}) set(MGB_JIT_HALIDE ${MGE_WITH_HALIDE}) +# Thread IF(APPLE) set(CMAKE_THREAD_LIBS_INIT "-lpthread") set(CMAKE_HAVE_THREADS_LIBRARY 1) @@ -447,8 +448,14 @@ IF(APPLE) set(THREADS_PREFER_PTHREAD_FLAG ON) ENDIF() -# Thread -if(CMAKE_THREAD_LIBS_INIT) +if(MSVC OR WIN32) + set(CMAKE_HAVE_THREADS_LIBRARY 1) + set(CMAKE_USE_WIN32_THREADS_INIT 1) + set(CMAKE_USE_PTHREADS_INIT 1) + set(THREADS_PREFER_PTHREAD_FLAG ON) +endif() + +if(CMAKE_THREAD_LIBS_INIT OR CMAKE_USE_WIN32_THREADS_INIT) set(MGB_HAVE_THREAD 1) endif() @@ -471,12 +478,6 @@ else() set(MGB_ENABLE_DEBUG_UTIL 0) endif() -# FIXME: remove this after imp DEBUG UTIL for windows -if(MSVC OR WIN32) - set(MGB_ENABLE_DEBUG_UTIL 0) - message(" -- disable MGB_ENABLE_DEBUG_UTIL in windows build") -endif() - # TensorRT set(MGB_ENABLE_TENSOR_RT ${MGE_WITH_TRT}) diff --git a/python_module/CMakeLists.txt b/python_module/CMakeLists.txt index e98421397..c64b520d2 100644 --- a/python_module/CMakeLists.txt +++ b/python_module/CMakeLists.txt @@ -11,7 +11,13 @@ find_package(NumPy REQUIRED) find_package(SWIG REQUIRED) set(SWIG_SRC src/swig/mgb.i) -set(CMAKE_SWIG_FLAGS -Wall -threads -py3 -modern -DSWIGWORDSIZE64) +if(MSVC OR WIN32) + set(CMAKE_SWIG_FLAGS -Wall -threads -py3 -DSWIGWORDSIZE64) + message("WARN: swig have some define issue at windows(64) env") + message("Please refs scripts/whl/BUILD_PYTHON_WHL_README.md to init windows build env") +else() + set(CMAKE_SWIG_FLAGS -Wall -threads -py3 -modern -DSWIGWORDSIZE64) +endif() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-parameter") file(GLOB_RECURSE OPR_DECL_SRCS "${PROJECT_SOURCE_DIR}/src/**/*.oprdecl") @@ -69,6 +75,8 @@ set_target_properties(mgb PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BI if (APPLE) target_link_libraries(mgb megbrain megdnn) set_target_properties(mgb PROPERTIES LINK_FLAGS "-undefined dynamic_lookup") +elseif (MSVC OR WIN32) + target_link_libraries(mgb megbrain megdnn) else() target_link_libraries(mgb megbrain megdnn -Wl,--version-script=${VERSION_SCRIPT}) endif() diff --git a/python_module/megengine/_internal/plugin.py b/python_module/megengine/_internal/plugin.py index ceae5620b..4290bc1b0 100644 --- a/python_module/megengine/_internal/plugin.py +++ b/python_module/megengine/_internal/plugin.py @@ -13,6 +13,7 @@ import atexit import collections import json import os +import platform import signal import struct @@ -163,7 +164,11 @@ class GlobalInfkernFinder: further investigation """ - _signal = signal.SIGUSR1 + _signal = None + if platform.system() != "Windows": + _signal = signal.SIGUSR1 + else: + _signal = signal.CTRL_C_EVENT _registry = [] _shell_maker = None @@ -197,7 +202,7 @@ class GlobalInfkernFinder: from IPython.terminal.embed import InteractiveShellEmbed cls._shell_maker = InteractiveShellEmbed - fast_signal_hander(signal.SIGUSR1, cls._on_signal) + fast_signal_hander(cls._signal, cls._on_signal) cls._registry.append(finder) diff --git a/python_module/megengine/utils/max_recursion_limit.py b/python_module/megengine/utils/max_recursion_limit.py index cda37dfed..d7bce6e8b 100644 --- a/python_module/megengine/utils/max_recursion_limit.py +++ b/python_module/megengine/utils/max_recursion_limit.py @@ -7,10 +7,13 @@ # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. import platform -import resource import sys import threading +# Windows do not imp resource package +if platform.system() != "Windows": + import resource + class AlternativeRecursionLimit: r"""A reentrant context manager for setting global recursion limits. @@ -29,6 +32,7 @@ class AlternativeRecursionLimit: with self.lock: if self.count == 0: self.orig_py_limit = sys.getrecursionlimit() + if platform.system() != "Windows": ( self.orig_rlim_stack_soft, self.orig_rlim_stack_hard, @@ -43,8 +47,9 @@ class AlternativeRecursionLimit: except ValueError as exc: if platform.system() != "Darwin": raise exc - # increase recursion limit - sys.setrecursionlimit(self.new_py_limit) + + # increase recursion limit + sys.setrecursionlimit(self.new_py_limit) self.count += 1 def __exit__(self, type, value, traceback): @@ -52,6 +57,8 @@ class AlternativeRecursionLimit: self.count -= 1 if self.count == 0: sys.setrecursionlimit(self.orig_py_limit) + + if platform.system() != "Windows": try: resource.setrlimit( resource.RLIMIT_STACK, diff --git a/python_module/setup.py b/python_module/setup.py index fb48c4906..414f4fcfc 100644 --- a/python_module/setup.py +++ b/python_module/setup.py @@ -5,6 +5,7 @@ import os import re import pathlib +import platform from distutils.file_util import copy_file from setuptools import setup, find_packages, Extension from setuptools.command.build_ext import build_ext as _build_ext @@ -25,7 +26,10 @@ class build_ext(_build_ext): extdir.parent.mkdir(parents=True, exist_ok=True) modpath = self.get_ext_fullname(ext.name).split('.') - modpath[-1] += '.so' + if platform.system() == "Windows": + modpath[-1] += '.pyd' + else: + modpath[-1] += '.so' modpath = str(pathlib.Path(*modpath).resolve()) copy_file(modpath, fullpath, verbose=self.verbose, dry_run=self.dry_run) diff --git a/python_module/src/cpp/function_replace.cpp b/python_module/src/cpp/function_replace.cpp index cbd59790a..6fc0fb632 100644 --- a/python_module/src/cpp/function_replace.cpp +++ b/python_module/src/cpp/function_replace.cpp @@ -21,7 +21,13 @@ #include #include + +#ifdef WIN32 +#include +#include +#else #include +#endif namespace { @@ -50,8 +56,35 @@ class Init { }; Init Init::inst; -int fork_exec_impl(const std::string &arg0, const std::string &arg1, - const std::string &arg2) { +int fork_exec_impl(const std::string& arg0, const std::string& arg1, + const std::string& arg2) { +#ifdef WIN32 + STARTUPINFO si; + PROCESS_INFORMATION pi; + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + ZeroMemory(&pi, sizeof(pi)); + auto args_str = " " + arg1 + " " + arg2; + + // Start the child process. + if (!CreateProcess(arg0.c_str(), // exe name + const_cast(args_str.c_str()), // Command line + NULL, // Process handle not inheritable + NULL, // Thread handle not inheritable + FALSE, // Set handle inheritance to FALSE + 0, // No creation flags + NULL, // Use parent's environment block + NULL, // Use parent's starting directory + &si, // Pointer to STARTUPINFO structure + &pi) // Pointer to PROCESS_INFORMATION structure + ) { + mgb_log_warn("CreateProcess failed (%lu).\n", GetLastError()); + fprintf(stderr, "[megbrain] failed to execl %s [%s, %s]\n", + arg0.c_str(), arg1.c_str(), arg2.c_str()); + __builtin_trap(); + } + return pi.dwProcessId; +#else auto pid = fork(); if (!pid) { execl(arg0.c_str(), arg0.c_str(), arg1.c_str(), arg2.c_str(), nullptr); @@ -62,6 +95,7 @@ int fork_exec_impl(const std::string &arg0, const std::string &arg1, } mgb_assert(pid > 0, "failed to fork: %s", std::strerror(errno)); return pid; +#endif } } // anonymous namespace diff --git a/python_module/src/cpp/megbrain_config.cpp b/python_module/src/cpp/megbrain_config.cpp index 7fec8a68f..ce521c431 100644 --- a/python_module/src/cpp/megbrain_config.cpp +++ b/python_module/src/cpp/megbrain_config.cpp @@ -17,7 +17,32 @@ #include +#if defined(WIN32) +#include +#include +#define F_OK 0 +#define RTLD_LAZY 0 +#define RTLD_GLOBAL 0 +#define RTLD_NOLOAD 0 +#define access(a, b) false + +static void* dlopen(const char* file, int) { + return static_cast(LoadLibrary(file)); +} + +static void* dlerror() { + const char* errmsg = "dlerror not aviable in windows"; + return const_cast(errmsg); +} + +static void* dlsym(void* handle, const char* name) { + FARPROC symbol = GetProcAddress((HMODULE)handle, name); + return reinterpret_cast(symbol); +} + +#else #include +#endif #if MGB_ENABLE_OPR_MM #include "megbrain/opr/mm_handler.h" @@ -274,8 +299,7 @@ void _config::load_opr_library(const char* self_path, const char* lib_path) { } } -std::vector> -_config::dump_registered_oprs() { +std::vector> _config::dump_registered_oprs() { #if MGB_ENABLE_DEBUG_UTIL return serialization::OprRegistry::dump_registries(); #else diff --git a/python_module/src/cpp/megbrain_config.h b/python_module/src/cpp/megbrain_config.h index a24ccc089..5415aa2dc 100644 --- a/python_module/src/cpp/megbrain_config.h +++ b/python_module/src/cpp/megbrain_config.h @@ -62,8 +62,8 @@ class _config { static void load_opr_library( const char* self_path, const char* lib_path); - static std::vector> - dump_registered_oprs(); + static std::vector> + dump_registered_oprs(); static int create_mm_server(const std::string& server_addr, int port); diff --git a/python_module/src/cpp/megbrain_wrap.cpp b/python_module/src/cpp/megbrain_wrap.cpp index 466a96046..a71ada0d0 100644 --- a/python_module/src/cpp/megbrain_wrap.cpp +++ b/python_module/src/cpp/megbrain_wrap.cpp @@ -404,11 +404,18 @@ void CompGraphCallbackValueProxy::do_copy() { m_copy_event->record(); } +#if defined(WIN32) +#include +#include +#undef CONST +#define usleep Sleep +#endif void CompGraphCallbackValueProxy::sync() const { mgb_assert(!m_use_raw_hv); RealTimer t0; double next_warn_time = 2, warn_time_delta = 1; while (!m_copy_event->finished()) { + //! sleep 1ms or sleep 1us no difference for performance on win32 usleep(1); if (t0.get_secs() >= next_warn_time) { mgb_log_warn("wait d2h copy for more than %.3f secs", diff --git a/python_module/src/cpp/plugin.cpp b/python_module/src/cpp/plugin.cpp index c997d75f7..5ca4df44e 100644 --- a/python_module/src/cpp/plugin.cpp +++ b/python_module/src/cpp/plugin.cpp @@ -16,10 +16,15 @@ #include #include +#include +#ifdef WIN32 +#include +#else #include -#include #include +#endif +#include /* ================= _InfkernFinderImpl ================= */ size_t _InfkernFinderImpl::sm_id = 0; @@ -72,23 +77,28 @@ class _FastSignal::Impl { bool m_worker_started = false; std::mutex m_mtx; std::thread m_worker_hdl; +#ifdef WIN32 + SECURITY_ATTRIBUTES win_sa = {sizeof(SECURITY_ATTRIBUTES), NULL, TRUE}; + HANDLE pipe_r, pipe_w; + DWORD bytes_r_w; +#else int m_pfd[2]; //! pipe fds; write signal handlers, -1 for exit +#endif std::unordered_map m_handler_callbacks; void worker() { -#ifdef __APPLE__ - uint64_t tid; - pthread_threadid_np(NULL, &tid); - mgb_log("fast signal worker started in thread 0x%zx", - static_cast(tid)); -#else - mgb_log("fast signal worker started in thread 0x%zx", - static_cast(pthread_self())); -#endif + std::ostringstream oss; + oss << std::this_thread::get_id() << std::endl; + mgb_log("fast signal worker started in thread %s", oss.str().c_str()); mgb::sys::set_thread_name("fastsgl"); int signum; - for (; ; ) { + for (;;) { +#ifdef WIN32 + if (ReadFile(pipe_r, &signum, sizeof(int), &bytes_r_w, NULL) == + NULL) { +#else if (read(m_pfd[0], &signum, sizeof(int)) != sizeof(int)) { +#endif if (errno == EINTR) continue; mgb_log_error("fast signal worker: " @@ -114,10 +124,17 @@ class _FastSignal::Impl { if (m_worker_started) return; +#ifdef WIN32 + if (!CreatePipe(&pipe_r, &pipe_w, &win_sa, 0)) { + throw mgb::MegBrainError(mgb::ssprintf("failed to create pipe: %s", + strerror(errno))); + } +#else if (pipe(m_pfd)) { - throw mgb::MegBrainError(mgb::ssprintf( - "failed to create pipe: %s", strerror(errno))); + throw mgb::MegBrainError(mgb::ssprintf("failed to create pipe: %s", + strerror(errno))); } +#endif std::thread t(std::bind(&Impl::worker, this)); m_worker_hdl.swap(t); m_worker_started = true; @@ -125,7 +142,11 @@ class _FastSignal::Impl { void write_pipe(int v) { mgb_assert(m_worker_started); +#ifdef WIN32 + if (WriteFile(pipe_w, &v, sizeof(int), &bytes_r_w, NULL) == NULL) { +#else if (write(m_pfd[1], &v, sizeof(int)) != sizeof(int)) { +#endif mgb_log_error("fast signal: failed to write to self pipe: %s", strerror(errno)); } @@ -169,8 +190,13 @@ class _FastSignal::Impl { return; write_pipe(-1); m_worker_hdl.join(); +#ifdef WIN32 + CloseHandle(pipe_r); + CloseHandle(pipe_w); +#else close(m_pfd[0]); close(m_pfd[1]); +#endif m_handler_callbacks.clear(); m_worker_started = false; } @@ -192,11 +218,19 @@ void _FastSignal::signal_hander(int signum) { } void _FastSignal::register_handler(int signum, PyObject *func) { +#ifdef WIN32 + //! up to now we can only use CTRL_C_EVENT to unix signal.SIGUSR1/2 + //FIXME: how to coherence signal number at python side + // https://docs.microsoft.com/en-gb/cpp/c-runtime-library/reference/signal?view=vs-2017 + mgb_assert(signum == CTRL_C_EVENT, "only allow register CTRL_C_EVENT as unix signal.SIGUSR1/2 now"); + signal(signum, signal_hander); +#else struct sigaction action; memset(&action, 0, sizeof(action)); action.sa_handler = &signal_hander; int ret = sigaction(signum, &action, nullptr); mgb_assert(!ret, "sigaction failed: %s", strerror(errno)); +#endif sm_impl.register_handler(signum, func); } diff --git a/python_module/src/cpp/python_helper.cpp b/python_module/src/cpp/python_helper.cpp index 7aa485743..1cc97d7ae 100644 --- a/python_module/src/cpp/python_helper.cpp +++ b/python_module/src/cpp/python_helper.cpp @@ -45,7 +45,7 @@ std::string demangle_typeid(const char* name) { namespace { // does nothing if not g++ -std::string mgb::demangle_typeid(const char* name) { +std::string demangle_typeid(const char* name) { return name; } } diff --git a/python_module/src/swig/mgb.i b/python_module/src/swig/mgb.i index e668f6d63..3a2c871de 100644 --- a/python_module/src/swig/mgb.i +++ b/python_module/src/swig/mgb.i @@ -31,13 +31,7 @@ void _init_bfloat16_types(PyObject *m); // implemented in bfloat16.cpp %template(_VectorString) std::vector; %template(_PairStringSizeT) std::pair; %template(_PairSizeTSizeT) std::pair; -/* - * swig use uint64_t have compat build issue with - * clang at osx env, so we use unsigned long to - * replace uint64_t,more detail refs stdint.i - * - */ -%template(_VectorPairUint64String) std::vector>; +%template(_VectorPairSizeTString) std::vector>; %pythoncode %{ import numpy as np diff --git a/python_module/test/integration/test_distributed.py b/python_module/test/integration/test_distributed.py index bd629b2fe..ba2e30808 100644 --- a/python_module/test/integration/test_distributed.py +++ b/python_module/test/integration/test_distributed.py @@ -89,6 +89,9 @@ def start_workers(worker, world_size, trace=False): @pytest.mark.skipif( platform.system() == "Darwin", reason="do not imp GPU mode at macos now" ) +@pytest.mark.skipif( + platform.system() == "Windows", reason="do not imp GPU mode at Windows now" +) def test_distributed(): start_workers(worker, 2, trace=True) start_workers(worker, 2, trace=False) diff --git a/python_module/test/run.sh b/python_module/test/run.sh index 59a66e6f2..7f2d9038b 100755 --- a/python_module/test/run.sh +++ b/python_module/test/run.sh @@ -10,10 +10,10 @@ ignore_list="--ignore test/unit/module/test_pytorch.py \ test_dirs="megengine test" pushd $(dirname "${BASH_SOURCE[0]}")/.. >/dev/null - pytest -xv -m 'isolated_distributed' \ + python3 -m pytest -xv -m 'isolated_distributed' \ --json-report --json-report-file=time_python_test.json \ $ignore_list $test_dirs - pytest -xv -m 'not internet and not isolated_distributed' \ + python3 -m pytest -xv -m 'not internet and not isolated_distributed' \ --json-report --json-report-file=time_python_test.json \ $ignore_list $test_dirs popd >/dev/null diff --git a/python_module/test/unit/distributed/test_functional.py b/python_module/test/unit/distributed/test_functional.py index d417ddcb9..f4adcbd98 100644 --- a/python_module/test/unit/distributed/test_functional.py +++ b/python_module/test/unit/distributed/test_functional.py @@ -29,6 +29,9 @@ def _init_process_group_wrapper(world_size, rank, dev, backend, q): @pytest.mark.skipif( platform.system() == "Darwin", reason="do not imp GPU mode at macos now" ) +@pytest.mark.skipif( + platform.system() == "Windows", reason="do not imp GPU mode at Windows now" +) @pytest.mark.isolated_distributed def test_reduce_sum(): world_size = 2 @@ -68,6 +71,9 @@ def test_reduce_sum(): @pytest.mark.skipif( platform.system() == "Darwin", reason="do not imp GPU mode at macos now" ) +@pytest.mark.skipif( + platform.system() == "Windows", reason="do not imp GPU mode at Windows now" +) @pytest.mark.isolated_distributed def test_gather(): world_size = 2 @@ -107,6 +113,9 @@ def test_gather(): @pytest.mark.skipif( platform.system() == "Darwin", reason="do not imp GPU mode at macos now" ) +@pytest.mark.skipif( + platform.system() == "Windows", reason="do not imp GPU mode at Windows now" +) @pytest.mark.isolated_distributed def test_broadcast(): world_size = 2 @@ -142,6 +151,9 @@ def test_broadcast(): @pytest.mark.skipif( platform.system() == "Darwin", reason="do not imp GPU mode at macos now" ) +@pytest.mark.skipif( + platform.system() == "Windows", reason="do not imp GPU mode at Windows now" +) @pytest.mark.isolated_distributed def test_scatter(): world_size = 2 @@ -181,6 +193,9 @@ def test_scatter(): @pytest.mark.skipif( platform.system() == "Darwin", reason="do not imp GPU mode at macos now" ) +@pytest.mark.skipif( + platform.system() == "Windows", reason="do not imp GPU mode at Windows now" +) @pytest.mark.isolated_distributed def test_all_to_all(): world_size = 2 @@ -218,6 +233,9 @@ def test_all_to_all(): @pytest.mark.skipif( platform.system() == "Darwin", reason="do not imp GPU mode at macos now" ) +@pytest.mark.skipif( + platform.system() == "Windows", reason="do not imp GPU mode at Windows now" +) @pytest.mark.isolated_distributed def test_all_gather(): world_size = 2 @@ -254,6 +272,9 @@ def test_all_gather(): @pytest.mark.skipif( platform.system() == "Darwin", reason="do not imp GPU mode at macos now" ) +@pytest.mark.skipif( + platform.system() == "Windows", reason="do not imp GPU mode at Windows now" +) @pytest.mark.isolated_distributed def test_reduce_scatter_sum(): world_size = 2 @@ -294,6 +315,9 @@ def test_reduce_scatter_sum(): @pytest.mark.skipif( platform.system() == "Darwin", reason="do not imp GPU mode at macos now" ) +@pytest.mark.skipif( + platform.system() == "Windows", reason="do not imp GPU mode at Windows now" +) @pytest.mark.isolated_distributed def test_all_reduce_sum(): world_size = 2 @@ -330,6 +354,9 @@ def test_all_reduce_sum(): @pytest.mark.skipif( platform.system() == "Darwin", reason="do not imp GPU mode at macos now" ) +@pytest.mark.skipif( + platform.system() == "Windows", reason="do not imp GPU mode at Windows now" +) @pytest.mark.isolated_distributed def test_all_reduce_max(): world_size = 2 @@ -366,6 +393,9 @@ def test_all_reduce_max(): @pytest.mark.skipif( platform.system() == "Darwin", reason="do not imp GPU mode at macos now" ) +@pytest.mark.skipif( + platform.system() == "Windows", reason="do not imp GPU mode at Windows now" +) @pytest.mark.isolated_distributed def test_all_reduce_min(): world_size = 2 @@ -402,6 +432,9 @@ def test_all_reduce_min(): @pytest.mark.skipif( platform.system() == "Darwin", reason="do not imp GPU mode at macos now" ) +@pytest.mark.skipif( + platform.system() == "Windows", reason="do not imp GPU mode at Windows now" +) @pytest.mark.isolated_distributed def test_bcast_param(): world_size = 2 diff --git a/python_module/test/unit/distributed/test_util.py b/python_module/test/unit/distributed/test_util.py index 932145d97..30926b791 100644 --- a/python_module/test/unit/distributed/test_util.py +++ b/python_module/test/unit/distributed/test_util.py @@ -45,6 +45,9 @@ def _init_process_group_wrapper(world_size, rank, dev, backend, q): @pytest.mark.skipif( platform.system() == "Darwin", reason="do not imp GPU mode at macos now" ) +@pytest.mark.skipif( + platform.system() == "Windows", reason="do not imp GPU mode at Windows now" +) @pytest.mark.isolated_distributed def test_create_mm_server(): def worker(): @@ -67,6 +70,9 @@ def test_create_mm_server(): @pytest.mark.skipif( platform.system() == "Darwin", reason="do not imp GPU mode at macos now" ) +@pytest.mark.skipif( + platform.system() == "Windows", reason="do not imp GPU mode at Windows now" +) @pytest.mark.isolated_distributed def test_init_process_group(): world_size = 2 @@ -102,6 +108,9 @@ def test_init_process_group(): @pytest.mark.skipif( platform.system() == "Darwin", reason="do not imp GPU mode at macos now" ) +@pytest.mark.skipif( + platform.system() == "Windows", reason="do not imp GPU mode at Windows now" +) @pytest.mark.isolated_distributed def test_group_barrier(): world_size = 2 @@ -137,6 +146,9 @@ def test_group_barrier(): @pytest.mark.skipif( platform.system() == "Darwin", reason="do not imp GPU mode at macos now" ) +@pytest.mark.skipif( + platform.system() == "Windows", reason="do not imp GPU mode at Windows now" +) @pytest.mark.isolated_distributed def test_synchronized(): world_size = 2 diff --git a/python_module/test/unit/module/test_batchnorm.py b/python_module/test/unit/module/test_batchnorm.py index f1c94b97a..cd6514445 100644 --- a/python_module/test/unit/module/test_batchnorm.py +++ b/python_module/test/unit/module/test_batchnorm.py @@ -22,6 +22,9 @@ from megengine.test import assertTensorClose @pytest.mark.skipif( platform.system() == "Darwin", reason="do not imp GPU mode at macos now" ) +@pytest.mark.skipif( + platform.system() == "Windows", reason="do not imp GPU mode at Windows now" +) @pytest.mark.isolated_distributed def test_syncbn(): nr_chan = 8 @@ -143,6 +146,9 @@ def test_batchnorm(): @pytest.mark.skipif( platform.system() == "Darwin", reason="do not imp GPU mode at macos now" ) +@pytest.mark.skipif( + platform.system() == "Windows", reason="do not imp GPU mode at Windows now" +) def test_syncbn1d(): nr_chan = 8 data_shape = (3, nr_chan, 4) @@ -241,6 +247,9 @@ def test_batchnorm2d(): @pytest.mark.skipif( platform.system() == "Darwin", reason="do not imp GPU mode at macos now" ) +@pytest.mark.skipif( + platform.system() == "Windows", reason="do not imp GPU mode at Windows now" +) def test_syncbn2d(): nr_chan = 8 data_shape = (3, nr_chan, 16, 16) @@ -315,6 +324,9 @@ def test_batchnorm_no_stats(): @pytest.mark.skipif( platform.system() == "Darwin", reason="do not imp GPU mode at macos now" ) +@pytest.mark.skipif( + platform.system() == "Windows", reason="do not imp GPU mode at Windows now" +) def test_syncbn_no_stats(): nr_chan = 8 data_shape = (3, nr_chan, 4) @@ -367,6 +379,9 @@ def test_batchnorm2d_no_stats(): @pytest.mark.skipif( platform.system() == "Darwin", reason="do not imp GPU mode at macos now" ) +@pytest.mark.skipif( + platform.system() == "Windows", reason="do not imp GPU mode at Windows now" +) def test_syncbn2d_no_stats(): nr_chan = 8 data_shape = (3, nr_chan, 16, 16) diff --git a/python_module/test/unit/module/test_module.py b/python_module/test/unit/module/test_module.py index 1b0a194eb..2181407ac 100644 --- a/python_module/test/unit/module/test_module.py +++ b/python_module/test/unit/module/test_module.py @@ -6,6 +6,7 @@ # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +import os import tempfile from collections import OrderedDict from io import BytesIO @@ -367,8 +368,13 @@ def test_dump_model(): data.set_value(np.random.random(data_shape)) mlp = MLP() pred = mlp(data) - with tempfile.NamedTemporaryFile() as f: - mge.dump(pred, f.name) + f = tempfile.NamedTemporaryFile(delete=False) + f_name = f.name + try: + mge.dump(pred, f_name) + finally: + f.close() + os.unlink(f_name) def test_load_quantized(): diff --git a/scripts/cmake-build/host_build.sh b/scripts/cmake-build/host_build.sh index 508f4eee0..01aaecc24 100755 --- a/scripts/cmake-build/host_build.sh +++ b/scripts/cmake-build/host_build.sh @@ -166,6 +166,18 @@ function prepare_env_for_windows_build() { echo "put vcvarsall.bat path to PATH env.." } +WINDOWS_BUILD_TARGET="Ninja all > build.log" +if [[ -z ${MAKE_DEVELOP} ]] +then + MAKE_DEVELOP="false" +fi +function config_windows_build_target() { + if [ ${MAKE_DEVELOP} = "true" ]; then + echo "build all and develop for pytest test" + WINDOWS_BUILD_TARGET="Ninja all > build.log && Ninja develop" + fi +} + function cmake_build_windows() { # windows do not support long path, so we cache the BUILD_DIR ASAP prepare_env_for_windows_build @@ -201,11 +213,12 @@ function cmake_build_windows() { ${EXTRA_CMAKE_ARGS} \ ../../.. && \ echo \"start Ninja build log to build.log, may take serval min...\" && \ - Ninja load_and_run > build.log" + ${WINDOWS_BUILD_TARGET}" } if [[ $OS =~ "NT" ]]; then + config_windows_build_target cmake_build_windows $MGE_WITH_CUDA $MGE_INFERENCE_ONLY $BUILD_TYPE else cmake_build $MGE_WITH_CUDA $MGE_INFERENCE_ONLY $BUILD_TYPE diff --git a/scripts/whl/BUILD_PYTHON_WHL_README.md b/scripts/whl/BUILD_PYTHON_WHL_README.md index f470ca9d8..324be1947 100644 --- a/scripts/whl/BUILD_PYTHON_WHL_README.md +++ b/scripts/whl/BUILD_PYTHON_WHL_README.md @@ -1,7 +1,7 @@ # python whl package build support status -* windows build (not ok) -* linux build (ok, cpu or gpu) -* macos build (ok,cpu only) +* windows build (ok,cpu only) +* linux build (ok, cpu or gpu) +* macos build (ok,cpu only) # build env prepare ## linux @@ -17,6 +17,48 @@ ``` ./scripts/whl/macos/macos_whl_env_prepare.sh ``` + +## windows + ``` + 1: refs scripts/cmake-build/BUILD_README.md windows section build for base windows build + 2: install several python or install u care python version, default install dir: /c/Users/${USER}/mge_whl_python_env + a: mkdir /c/Users/${USER}/mge_whl_python_env + b: download python 64-bit install exe + https://www.python.org/ftp/python/3.5.4/python-3.5.4-amd64.exe + https://www.python.org/ftp/python/3.6.8/python-3.6.8-amd64.exe + https://www.python.org/ftp/python/3.7.7/python-3.7.7-amd64.exe + https://www.python.org/ftp/python/3.8.3/python-3.8.3-amd64.exe + c: install python-3.5.4-amd64.exe to /c/Users/${USER}/mge_whl_python_env/3.5.4 from install gui + d: install python-3.6.8-amd64.exe to /c/Users/${USER}/mge_whl_python_env/3.6.8 from install gui + e: install python-3.7.7-amd64.exe to /c/Users/${USER}/mge_whl_python_env/3.7.7 from install gui + f: install python-3.8.3-amd64.exe to /c/Users/${USER}/mge_whl_python_env/3.8.3 from install gui + 3: rename python.exe to python3.exe + a: mv /c/Users/${USER}/mge_whl_python_env/3.5.4/python.exe /c/Users/${USER}/mge_whl_python_env/3.5.4/python3.exe + b: mv /c/Users/${USER}/mge_whl_python_env/3.6.8/python.exe /c/Users/${USER}/mge_whl_python_env/3.6.8/python3.exe + c: mv /c/Users/${USER}/mge_whl_python_env/3.7.7/python.exe /c/Users/${USER}/mge_whl_python_env/3.7.7/python3.exe + d: mv /c/Users/${USER}/mge_whl_python_env/3.8.3/python.exe /c/Users/${USER}/mge_whl_python_env/3.8.3/python3.exe + 4: install needed package for build python whl package + a0: /c/Users/${USER}/mge_whl_python_env/3.5.4/python3.exe -m pip install --upgrade pip + a1: /c/Users/${USER}/mge_whl_python_env/3.5.4/python3.exe -m pip install -r python_module/requires-test.txt + a2: /c/Users/${USER}/mge_whl_python_env/3.5.4/python3.exe -m pip install numpy wheel requests tqdm tabulate + + b0: /c/Users/${USER}/mge_whl_python_env/3.6.8/python3.exe -m pip install --upgrade pip + b1: /c/Users/${USER}/mge_whl_python_env/3.6.8/python3.exe -m pip install -r python_module/requires-test.txt + b2: /c/Users/${USER}/mge_whl_python_env/3.6.8/python3.exe -m pip install numpy wheel requests tqdm tabulate + + c0: /c/Users/${USER}/mge_whl_python_env/3.7.7/python3.exe -m pip install --upgrade pip + c1: /c/Users/${USER}/mge_whl_python_env/3.7.7/python3.exe -m pip install -r python_module/requires-test.txt + c2: /c/Users/${USER}/mge_whl_python_env/3.7.7/python3.exe -m pip install numpy wheel requests tqdm tabulate + + d0: /c/Users/${USER}/mge_whl_python_env/3.8.3/python3.exe -m pip install --upgrade pip + d1: /c/Users/${USER}/mge_whl_python_env/3.8.3/python3.exe -m pip install -r python_module/requires-test.txt + d2: /c/Users/${USER}/mge_whl_python_env/3.8.3/python3.exe -m pip install numpy wheel requests tqdm tabulate + 5: install swig from install gui + a: download swig: https://nchc.dl.sourceforge.net/project/swig/swigwin/swigwin-4.0.2/swigwin-4.0.2.zip + b: install swig to /c/Users/${USER}/swigwin-4.0.2 + c: apply scripts/whl/windows/fix-ptr-define-issue.patch to c/Users/${USER}/swigwin-4.0.2 + ``` + # how to build ## build for linux MegBrain delivers `wheel` package with `manylinux2010` tag defined in [PEP-571](https://www.python.org/dev/peps/pep-0571/). @@ -36,9 +78,22 @@ ``` ALL_PYTHON=35m ./build_wheel.sh cpu - ``` ## build for macos ``` ./scripts/whl/macos/macos_build_whl.sh ``` + If you just want to build for a specific Python verison, you can use `ALL_PYTHON` environment variable. eg: + + ``` + ALL_PYTHON=3.5.9 ./scripts/whl/macos/macos_build_whl.sh + ``` +## build for windows + ``` + ./scripts/whl/windows/windows_build_whl.sh + ``` + If you just want to build for a specific Python verison, you can use `ALL_PYTHON` environment variable. eg: + + ``` + ALL_PYTHON=3.5.4 ./scripts/whl/windows/windows_build_whl.sh + ``` diff --git a/scripts/whl/macos/macos_build_whl.sh b/scripts/whl/macos/macos_build_whl.sh index c430e29f7..43bca5faf 100755 --- a/scripts/whl/macos/macos_build_whl.sh +++ b/scripts/whl/macos/macos_build_whl.sh @@ -149,13 +149,16 @@ function do_build() { } function third_party_prepare() { - if [[ -z ${ALREADY_INSTALL_THIRD_PARTY} ]] + echo "init third_party..." + ${SRC_DIR}/third_party/prepare.sh + + + if [[ -z ${ALREADY_INSTALL_MKL} ]] then echo "init third_party..." - ${SRC_DIR}/third_party/prepare.sh ${SRC_DIR}/third_party/install-mkl.sh else - echo "skip init third_party..." + echo "skip init mkl internal" fi } diff --git a/scripts/whl/windows/fix-ptr-define-issue.patch b/scripts/whl/windows/fix-ptr-define-issue.patch new file mode 100644 index 000000000..e93be925b --- /dev/null +++ b/scripts/whl/windows/fix-ptr-define-issue.patch @@ -0,0 +1,28 @@ +diff --git a/Lib/stdint.i b/Lib/stdint.i +index 14fe619..45337c0 100644 +--- a/Lib/stdint.i ++++ b/Lib/stdint.i +@@ -86,8 +86,8 @@ typedef unsigned long long int uint_fast64_t; + + /* Types for `void *' pointers. */ + #if defined(SWIGWORDSIZE64) +-typedef long int intptr_t; +-typedef unsigned long int uintptr_t; ++typedef long long intptr_t; ++typedef unsigned long long uintptr_t; + #else + typedef int intptr_t; + typedef unsigned int uintptr_t; +diff --git a/Lib/swigarch.i b/Lib/swigarch.i +index bf4ee8e..bf6b5c3 100644 +--- a/Lib/swigarch.i ++++ b/Lib/swigarch.i +@@ -53,7 +53,7 @@ + #ifndef LONG_MAX + #include + #endif +-#if (__WORDSIZE == 32) || (LONG_MAX == INT_MAX) ++#if (__WORDSIZE == 32) + # error "SWIG wrapped code invalid in 32 bit architecture, regenerate code using -DSWIGWORDSIZE32" + #endif + %} diff --git a/scripts/whl/windows/windows_build_whl.sh b/scripts/whl/windows/windows_build_whl.sh new file mode 100755 index 000000000..445a40fca --- /dev/null +++ b/scripts/whl/windows/windows_build_whl.sh @@ -0,0 +1,138 @@ +#!/bin/bash -e + +NT=$(echo `uname` | grep "NT") +echo $NT +if [ -z "$NT" ];then + echo "only run at windows bash env" + echo "pls consider install bash-like tools, eg MSYS or git-cmd, etc" + exit -1 +fi + +function err_env() { + echo "check_env failed: pls refs ${SRC_DIR}/scripts/whl/BUILD_PYTHON_WHL_README.md to init env" + exit -1 +} + +function append_path_env_and_check() { + echo "export swig pwd to PATH" + export PATH=/c/Users/${USER}/swigwin-4.0.2::$PATH + echo "export vs2019 install path" + export VS_PATH=/c/Program\ Files\ \(x86\)/Microsoft\ Visual\ Studio/2019/Enterprise + # for llvm-strip + export PATH=$VS_PATH/VC/Tools/Llvm/bin/:$PATH +} + +append_path_env_and_check + +SRC_DIR=$(READLINK -f "`dirname $0`/../../../") +ALL_PYTHON=${ALL_PYTHON} +FULL_PYTHON_VER="3.5.4 3.6.8 3.7.7 3.8.3" +if [[ -z ${ALL_PYTHON} ]] +then + ALL_PYTHON=${FULL_PYTHON_VER} +fi + +PYTHON_DIR= +PYTHON_LIBRARY= +PYTHON_INCLUDE_DIR= +WINDOWS_WHL_HOME=${SRC_DIR}/scripts/whl/windows/windows_whl_home +if [ -e "${WINDOWS_WHL_HOME}" ]; then + echo "remove old windows whl file" + rm -rf ${WINDOWS_WHL_HOME} +fi +mkdir -p ${WINDOWS_WHL_HOME} + +function config_python_env() { + PYTHON_DIR=/c/Users/${USER}/mge_whl_python_env/$1 + PYTHON_BIN=${PYTHON_DIR} + if [ ! -f "$PYTHON_BIN/python3.exe" ]; then + echo "ERR: can not find $PYTHON_BIN , Invalid python package" + echo "now support list: ${FULL_PYTHON_VER}" + err_env + else + echo "put python3 to env..." + export PATH=${PYTHON_BIN}:$PATH + which python3 + fi + echo ${ver} + + PYTHON_LIBRARY=${PYTHON_DIR}/libs/python3.lib + PYTHON_INCLUDE_DIR=${PYTHON_DIR}/include +} + +function do_build() { + for ver in ${ALL_PYTHON} + do + #config + config_python_env ${ver} + + #check env + if [ ! -f "$PYTHON_LIBRARY" ]; then + echo "ERR: can not find $PYTHON_LIBRARY , Invalid python package" + err_env + fi + if [ ! -d "$PYTHON_INCLUDE_DIR" ]; then + echo "ERR: can not find $PYTHON_INCLUDE_DIR , Invalid python package" + err_env + fi + echo "PYTHON_LIBRARY: ${PYTHON_LIBRARY}" + echo "PYTHON_INCLUDE_DIR: ${PYTHON_INCLUDE_DIR}" + #append cmake args for config python + #FIXME: ninja handle err with cmake 3.17 when assgin PYTHON_LIBRARY + #But after put python3.exe to HEAD of PATH by config_python_env, cmake can also handle the + #right PYTHON_LIBRARY and PYTHON_INCLUDE_DIR, at the same time, clang-cl need swig target + #force LINK a real PYTHON_LIBRARY file, after test we do not find the symbols conflict with python + #export EXTRA_CMAKE_ARGS="-DPYTHON_LIBRARY=${PYTHON_LIBRARY} -DPYTHON_INCLUDE_DIR=${PYTHON_INCLUDE_DIR} " + #config build type to RelWithDebInfo to enable MGB_ENABLE_DEBUG_UTIL etc + export EXTRA_CMAKE_ARGS=${EXTRA_CMAKE_ARGS}" -DCMAKE_BUILD_TYPE=RelWithDebInfo " + + #call build and install + #FIXME: cmake do not triger update python config, after + #change PYTHON_LIBRARY and PYTHON_INCLUDE_DIR, so add + #-r to remove build cache after a new ver build, which + #will be more slow build than without -r + ${SRC_DIR}/scripts/cmake-build/host_build.sh -t -r + + #call setup.py + BUILD_DIR=${SRC_DIR}/build_dir/host/build/ + cd ${BUILD_DIR} + + if [ -d "staging" ]; then + echo "remove old build cache file" + rm -rf staging + fi + mkdir -p staging + + + cp -a python_module/{megengine,setup.py,requires.txt,requires-style.txt,requires-test.txt} staging/ + cd ${BUILD_DIR}/staging/megengine/_internal + llvm-strip -s _mgb.pyd + cd ${BUILD_DIR}/staging + ${PYTHON_DIR}/python3 setup.py bdist_wheel + cp ${BUILD_DIR}/staging/dist/Meg*.whl ${WINDOWS_WHL_HOME}/ + + echo "" + echo "##############################################################################################" + echo "windows whl package location: ${WINDOWS_WHL_HOME}" + ls ${WINDOWS_WHL_HOME} + echo "##############################################################################################" + done +} + +function third_party_prepare() { + echo "init third_party..." + ${SRC_DIR}/third_party/prepare.sh + + + if [[ -z ${ALREADY_INSTALL_MKL} ]] + then + echo "init third_party..." + ${SRC_DIR}/third_party/install-mkl.sh + else + echo "skip init mkl internal" + fi +} + +###################### +third_party_prepare +do_build diff --git a/sdk/load-and-run/src/mgblar.cpp b/sdk/load-and-run/src/mgblar.cpp index 15ee3456e..300f6551f 100644 --- a/sdk/load-and-run/src/mgblar.cpp +++ b/sdk/load-and-run/src/mgblar.cpp @@ -598,11 +598,9 @@ void run_test_st(Args &env) { auto format = serialization::GraphLoader::identify_graph_dump_format(*inp_file); - if (!format.valid()) { - printf("invalid model: unknown model format, please make sure input " - "file is generated by GraphDumper\n"); - return; - } + mgb_assert(format.valid(), + "invalid model: unknown model format, please make sure input " + "file is generated by GraphDumper"); auto loader = serialization::GraphLoader::make(std::move(inp_file), format.val()); RealTimer timer; diff --git a/src/core/impl/utils/debug.cpp b/src/core/impl/utils/debug.cpp index 9752597cc..940c4968d 100644 --- a/src/core/impl/utils/debug.cpp +++ b/src/core/impl/utils/debug.cpp @@ -35,16 +35,21 @@ using namespace debug; #include #endif +#ifndef WIN32 #include +#include +#endif + #include #include -#include #ifdef __ANDROID__ #include #else +#ifndef WIN32 #include #endif +#endif #ifdef __ANDROID__ namespace { @@ -121,6 +126,8 @@ void get_mem_map( fclose(fin); } +#ifndef WIN32 +//FIXME: imp SigHandlerInit backtrace for windows class SigHandlerInit { static void death_handler(int signum) { char msg0[] = @@ -157,6 +164,7 @@ public: std::set_terminate([]() { death_handler(-1); }); } }; +#endif #if MGB_CUDA class CudaCheckOnFork { @@ -201,7 +209,9 @@ class InitCaller { static InitCaller inst; InitCaller() { +#ifndef WIN32 SigHandlerInit::init_for_segv(); +#endif #if MGB_CUDA CudaCheckOnFork::init(); #endif @@ -216,6 +226,7 @@ void (*ForkAfterCudaError::throw_)() = throw_fork_cuda_exc; std::atomic_size_t ScopedForkWarningSupress::sm_depth{0}; BacktraceResult mgb::debug::backtrace(int nr_exclude) { +#ifndef WIN32 static bool thread_local recursive_call = false; if (recursive_call) { fprintf(stderr, "recursive call to backtrace()!\n"); @@ -265,6 +276,11 @@ BacktraceResult mgb::debug::backtrace(int nr_exclude) { recursive_call = false; return result; +#else + //FIXME: imp Backtrace for windows + BacktraceResult result; + return result; +#endif } void BacktraceResult::fmt_to_str(std::string& dst) { diff --git a/src/core/include/megbrain/common.h b/src/core/include/megbrain/common.h index 49f5d9304..172a83a53 100644 --- a/src/core/include/megbrain/common.h +++ b/src/core/include/megbrain/common.h @@ -197,6 +197,11 @@ void __log__(LogLevel level, const char *file, const char *func, int line, #define MGB_GETENV(_name) static_cast(nullptr) #endif +#ifdef WIN32 +#define unsetenv(_name) _putenv_s(_name, ""); +#define setenv(name,value,overwrite) _putenv_s(name,value) +#endif + // use some macro tricks to get lock guard with unique variable name #define MGB_TOKENPASTE(x, y) x ## y #define MGB_TOKENPASTE2(x, y) MGB_TOKENPASTE(x, y) diff --git a/src/serialization/impl/opr_registry.cpp b/src/serialization/impl/opr_registry.cpp index 2f3b5673f..4c1df5561 100644 --- a/src/serialization/impl/opr_registry.cpp +++ b/src/serialization/impl/opr_registry.cpp @@ -159,10 +159,9 @@ void OprRegistry::add_using_dynamic_loader( } #if MGB_ENABLE_DEBUG_UTIL -std::vector> -OprRegistry::dump_registries() { +std::vector> OprRegistry::dump_registries() { auto&& id2reg = static_data().id2reg; - std::vector> result; + std::vector> result; for (auto iter = id2reg.begin(); iter != id2reg.end(); ++iter) { if (iter->second.name.size() == 0) result.push_back({iter->first, ""}); diff --git a/src/serialization/include/megbrain/serialization/opr_registry.h b/src/serialization/include/megbrain/serialization/opr_registry.h index 6856bc4fa..f69600760 100644 --- a/src/serialization/include/megbrain/serialization/opr_registry.h +++ b/src/serialization/include/megbrain/serialization/opr_registry.h @@ -76,8 +76,7 @@ namespace serialization { #if MGB_ENABLE_DEBUG_UTIL //! dump registered oprs - static std::vector> - dump_registries(); + static std::vector> dump_registries(); #endif }; diff --git a/test/src/include/megbrain/test/helper.h b/test/src/include/megbrain/test/helper.h index 01e3940e4..4a2d36b46 100644 --- a/test/src/include/megbrain/test/helper.h +++ b/test/src/include/megbrain/test/helper.h @@ -17,13 +17,6 @@ #include -#if defined(WIN32) -static inline void unsetenv(std::string name) { - name += "="; - _putenv(name.c_str()); -} -#define setenv(name,value,overwrite) _putenv_s(name,value) -#endif #if !MGB_ENABLE_EXCEPTION #pragma GCC diagnostic ignored "-Wunused-variable" #endif -- GitLab