From 9c9a269dee001036805c481f7c1c1aa1588382f2 Mon Sep 17 00:00:00 2001 From: "nengli.ln" Date: Mon, 27 Jul 2026 14:41:09 +0800 Subject: [PATCH 01/19] temp commit --- CMakeLists.txt | 226 +++++++++++++----- cmake/BuildProtobufThirdParty.cmake | 21 +- cmake/neug_symbol_visibility.cmake | 5 +- include/neug/common/columns/vertex_columns.h | 27 ++- include/neug/common/types.h | 4 +- .../compiler/binder/copy/bound_copy_from.h | 2 + .../neug/compiler/common/enums/delete_type.h | 2 +- .../neug/compiler/common/enums/explain_type.h | 5 + .../arithmetic/arithmetic_functions.h | 5 + .../neug/compiler/function/read_function.h | 2 + .../neug/compiler/gopt/g_physical_analyzer.h | 2 +- .../neug/compiler/gopt/g_physical_convertor.h | 2 +- include/neug/compiler/gopt/g_result_schema.h | 2 +- .../planner/operator/logical_operator.h | 2 +- .../operator/persistent/logical_delete.h | 2 +- .../operator/sip/side_way_info_passing.h | 5 + include/neug/compiler/planner/planner.h | 8 + .../persistent/reader/copy_from_error.h | 3 +- .../processor/operator/physical_operator.h | 4 +- .../storage/predicate/column_predicate.h | 2 +- include/neug/compiler/storage/store/table.h | 8 +- .../common/operators/retrieve/edge_expand.h | 1 + include/neug/main/connection.h | 2 +- include/neug/main/neug_db.h | 2 +- include/neug/main/query_result.h | 2 +- include/neug/storages/checkpoint_manager.h | 3 +- include/neug/storages/csr/csr_view.h | 29 +-- include/neug/storages/graph/edge_table.h | 5 +- include/neug/storages/graph/property_graph.h | 10 +- include/neug/storages/graph/schema.h | 5 +- include/neug/storages/graph/vertex_table.h | 17 +- .../neug/storages/graph/vertex_timestamp.h | 3 +- include/neug/storages/graph_snapshot_store.h | 2 +- include/neug/storages/module/module.h | 3 +- include/neug/utils/bitset.h | 88 +++++-- include/neug/utils/bolt_utils.h | 3 +- include/neug/utils/id_indexer.h | 12 +- include/neug/utils/io/file/file_utils.h | 86 +++++++ include/neug/utils/platform.h | 46 ++++ include/neug/utils/property/column.h | 7 +- include/neug/utils/property/table.h | 9 +- include/neug/utils/property/types.h | 4 +- include/neug/utils/result.h | 13 +- include/neug/utils/service_utils.h | 20 +- src/CMakeLists.txt | 6 +- .../binder/bind/copy/bind_copy_from.cpp | 2 + src/compiler/gopt/g_alias_manager.cpp | 4 +- src/compiler/gopt/g_query_converter.cpp | 13 +- src/compiler/graph/on_disk_graph.cpp | 2 +- .../optimizer/logical_operator_visitor.cpp | 4 +- .../transform/transform_updating_clause.cpp | 2 +- .../planner/operator/logical_operator.cpp | 4 +- src/compiler/planner/plan/plan_join_order.cpp | 2 +- .../execute/ops/batch/batch_update_utils.cc | 10 + .../expression/exprs/extract_expr.cc | 9 + src/execution/extension/extension.cc | 29 +++ src/main/file_lock.cc | 57 +++++ src/main/neug_db.cc | 2 + src/storages/container/anon_mmap_container.cc | 19 +- src/storages/container/file_mmap_container.cc | 40 +++- src/storages/container/mmap_container.cc | 2 + src/storages/csr/mutable_csr.cc | 2 + src/storages/graph/checkpoint_manifest.cc | 10 + src/storages/graph/edge_table.cc | 16 +- src/storages/graph/vertex_table.cc | 2 +- src/storages/loader/loader_factory.cc | 12 +- src/storages/loader/loader_utils.cc | 10 + src/storages/loader/loading_config.cc | 7 +- src/transaction/wal/local_wal_parser.cc | 17 ++ src/transaction/wal/local_wal_writer.cc | 34 ++- src/utils/CMakeLists.txt | 9 +- src/utils/io/file/file_utils.cc | 90 +++++++ src/utils/service_utils.cc | 30 +++ src/utils/yaml_utils.cc | 12 +- tools/python_bind/CMakeLists.txt | 21 +- tools/python_bind/neug/__init__.py | 16 +- tools/python_bind/neug/database.py | 7 +- 77 files changed, 1011 insertions(+), 202 deletions(-) create mode 100644 include/neug/utils/platform.h diff --git a/CMakeLists.txt b/CMakeLists.txt index fd063f49b..1e744809e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -105,6 +105,46 @@ endforeach() add_definitions(-DNEUG_CMAKE_VERSION="${CMAKE_PROJECT_VERSION}") +# Windows port: disable features that are not yet supported or unnecessary for the core library. +if(WIN32) + message(STATUS "Windows detected: disabling non-core features for core library build") + # BUILD_PYTHON is no longer forced off; pass -DBUILD_PYTHON=ON to enable. + set(BUILD_NODEJS OFF CACHE BOOL "Node.js binding is not supported on Windows yet" FORCE) + set(BUILD_HTTP_SERVER OFF CACHE BOOL "HTTP server (brpc) is not supported on Windows yet" FORCE) + set(BUILD_EXTENSIONS "" CACHE STRING "Extensions are not supported on Windows yet" FORCE) + set(BUILD_TEST OFF CACHE BOOL "Tests are disabled on Windows core build" FORCE) + set(BUILD_EXECUTABLES OFF CACHE BOOL "Executables are disabled on Windows core build" FORCE) + set(BUILD_EXAMPLES OFF CACHE BOOL "Examples are disabled on Windows core build" FORCE) + set(NEUG_NATIVE_ARCH OFF CACHE BOOL "Native architecture tuning is not supported on Windows" FORCE) + set(ENABLE_BACKTRACES OFF CACHE BOOL "Backtraces are not supported on Windows yet" FORCE) + # Use dynamic C runtime (/MD) consistently across all targets and third-party + # dependencies to match vcpkg's x64-windows-static-md triplet. + set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreadedDLL" CACHE STRING "" FORCE) + # Windows headers define an ERROR macro which conflicts with glog's ERROR + # severity level. Disable abbreviated severity macros on Windows. + add_compile_definitions(GLOG_NO_ABBREVIATED_SEVERITIES) + # Windows headers also define min/max macros that conflict with std::min/max. + add_compile_definitions(NOMINMAX) + # Windows headers define GetObject as GetObjectA/GetObjectW (in wingdi.h), + # which conflicts with rapidjson::Value::GetObject(). Exclude GDI headers. + add_compile_definitions(NOGDI) + # yaml-cpp is built as a static third-party library; tell its headers not to + # use __declspec(dllimport) on MSVC. + add_compile_definitions(YAML_CPP_STATIC_DEFINE) + # NEUG_EXPORTS must be visible to all object libraries that feed into the + # neug SHARED target, not just the final link step. Without this, NEUG_API + # resolves to __declspec(dllimport) in object files, causing C2491 when + # defining static data members. + add_compile_definitions(NEUG_EXPORTS) + # Force-include platform.h so that GCC extensions such as __attribute__ are + # neutralized for MSVC without touching every source file. Restrict to CXX + # so the resource compiler (RC) does not see the option. + add_compile_options($<$:/FI${CMAKE_SOURCE_DIR}/include/neug/utils/platform.h>) + # Force-include api.h so NEUG_API is visible everywhere (needed because + # protobuf-generated headers use dllexport_decl=NEUG_API). + add_compile_options($<$:/FI${CMAKE_SOURCE_DIR}/include/neug/utils/api.h>) +endif() + include(CheckCXXCompilerFlag) include(cmake/neug_symbol_visibility.cmake) include(cmake/NeugNativeArch.cmake) @@ -121,10 +161,7 @@ endif () add_compile_definitions(NEUG_VERSION="${NEUG_VERSION}") -# reference: https://gitlab.kitware.com/cmake/community/-/wikis/doc/cmake/RPATH-handling#always-full-rpath -set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) -set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib") -set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) +# C++ standard is required on all platforms. set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED TRUE) # set(CMAKE_CXX_VISIBILITY_PRESET hidden) @@ -133,8 +170,16 @@ set(CMAKE_CXX_STANDARD_REQUIRED TRUE) # set(CMAKE_FIND_PACKAGE_RESOLVE_SYMLINKS TRUE) # set(CMAKE_POSITION_INDEPENDENT_CODE ON) # set(CMAKE_VISIBILITY_INLINES_HIDDEN ON) -# On Linux, symbols in executables are not accessible by loaded shared libraries (e.g. via dlopen(3)). However, we need to export public symbols in executables so that extensions can access public symbols. This enables that behaviour. -set(CMAKE_ENABLE_EXPORTS TRUE) + +# RPATH is a Linux/macOS concept; keep related settings off Windows. +if (NOT WIN32) + # reference: https://gitlab.kitware.com/cmake/community/-/wikis/doc/cmake/RPATH-handling#always-full-rpath + set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) + set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib") + set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) + # On Linux, symbols in executables are not accessible by loaded shared libraries (e.g. via dlopen(3)). However, we need to export public symbols in executables so that extensions can access public symbols. This enables that behaviour. + set(CMAKE_ENABLE_EXPORTS TRUE) +endif() if(ENABLE_WERROR) if (CMAKE_VERSION VERSION_GREATER "3.24.0" OR CMAKE_VERSION VERSION_EQUAL "3.24.0") @@ -148,33 +193,49 @@ endif() if (APPLE) set(CMAKE_MACOSX_RPATH ON) -else () +elseif(NOT WIN32) if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lopen-pal") else () set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wl,-rpath,$ORIGIN") endif () endif () -# Only add -fopenmp if not using clang -if (NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang") +# Only add OpenMP if not using clang (MSVC supports /openmp) +if (MSVC) + message(STATUS "Using MSVC compiler, adding /openmp") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /openmp") +elseif (NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang") message(STATUS "Using non-clang compiler, adding -fopenmp") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fopenmp") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fopenmp") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fopenmp") endif () -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -fPIC -Wno-psabi") -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC") -check_cxx_compiler_flag("-std=c++20" COMPILER_SUPPORTS_CXX20) -if (NOT COMPILER_SUPPORTS_CXX20) - check_cxx_compiler_flag("-std=c++2a" COMPILER_SUPPORTS_CXX2A) - if (COMPILER_SUPPORTS_CXX2A) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++2a") +if (MSVC) + # MSVC warning levels and common suppressions for third-party headers + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W3 /wd4996 /wd4267 /wd4244 /wd4305 /wd4800 /wd4251") + # Treat source files as UTF-8 to avoid code page issues with non-ASCII characters. + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /utf-8") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Od /Zi") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /O2") +else() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -fPIC -Wno-psabi") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC") + + check_cxx_compiler_flag("-std=c++20" COMPILER_SUPPORTS_CXX20) + if (NOT COMPILER_SUPPORTS_CXX20) + check_cxx_compiler_flag("-std=c++2a" COMPILER_SUPPORTS_CXX2A) + if (COMPILER_SUPPORTS_CXX2A) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++2a") + else() + message(WARNING "Compiler does not support C++20 or C++2a") + endif() else() - message(WARNING "Compiler does not support C++20 or C++2a") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++20") endif() -else() - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++20") + + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -g") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3") endif() include(CheckCXXSourceCompiles) @@ -190,8 +251,6 @@ if (HAVE_CHRONO_YMD) add_definitions(-DENABLE_CHRONO_YMD) endif() -set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -g") -set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3") #if compiler is gcc if (CMAKE_CXX_COMPILER_ID MATCHES "GNU") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-class-memaccess -Wno-maybe-uninitialized -Wno-ignored-attributes") @@ -239,35 +298,38 @@ if(${ENABLE_LTO}) set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) endif() -# if os is macos, -if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") +# Apple Clang ABI compatibility flag (not needed on Windows or non-Apple Clang) +if (APPLE AND CMAKE_CXX_COMPILER_ID MATCHES "Clang") message("macos detected") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-class-conversion -fclang-abi-compat=14") # need fclang-abi-compat for clang compiler, due to absl problem: https://github.com/protocolbuffers/protobuf/issues/12693 endif() -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated") -check_cxx_compiler_flag("-Wno-sign-compare" COMPILER_SUPPORTS_SIGN_COMPARE_FLAG) -check_cxx_compiler_flag("-Wno-deprecated-declarations" COMPILER_SUPPORTS_DEPRECATED_DECLARATIONS_FLAG) -check_cxx_compiler_flag("-Wno-attributes" COMPILER_SUPPORTS_ATTRIBUTES_FLAG) -check_cxx_compiler_flag("-Wno-error=stringop-overflow" COMPILER_SUPPORTS_STRINGOP_OVERFLOW_FLAG) -check_cxx_compiler_flag("-Wno-array-bounds" COMPILER_SUPPORTS_ARRAY_BOUNDS_FLAG) -check_cxx_compiler_flag("-Wno-deprecated-builtins" COMPILER_SUPPORTS_DEPRECATED_BUILTINS_FLAG) -if (COMPILER_SUPPORTS_SIGN_COMPARE_FLAG) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-sign-compare") -endif() -if (COMPILER_SUPPORTS_DEPRECATED_DECLARATIONS_FLAG) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated-declarations") -endif() -if (COMPILER_SUPPORTS_ATTRIBUTES_FLAG) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-attributes") -endif() -if (COMPILER_SUPPORTS_STRINGOP_OVERFLOW_FLAG) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-error=stringop-overflow") -endif() -if (COMPILER_SUPPORTS_ARRAY_BOUNDS_FLAG) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-array-bounds") -endif() -if (COMPILER_SUPPORTS_DEPRECATED_BUILTINS_FLAG) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated-builtins") + +if (NOT MSVC) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated") + check_cxx_compiler_flag("-Wno-sign-compare" COMPILER_SUPPORTS_SIGN_COMPARE_FLAG) + check_cxx_compiler_flag("-Wno-deprecated-declarations" COMPILER_SUPPORTS_DEPRECATED_DECLARATIONS_FLAG) + check_cxx_compiler_flag("-Wno-attributes" COMPILER_SUPPORTS_ATTRIBUTES_FLAG) + check_cxx_compiler_flag("-Wno-error=stringop-overflow" COMPILER_SUPPORTS_STRINGOP_OVERFLOW_FLAG) + check_cxx_compiler_flag("-Wno-array-bounds" COMPILER_SUPPORTS_ARRAY_BOUNDS_FLAG) + check_cxx_compiler_flag("-Wno-deprecated-builtins" COMPILER_SUPPORTS_DEPRECATED_BUILTINS_FLAG) + if (COMPILER_SUPPORTS_SIGN_COMPARE_FLAG) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-sign-compare") + endif() + if (COMPILER_SUPPORTS_DEPRECATED_DECLARATIONS_FLAG) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated-declarations") + endif() + if (COMPILER_SUPPORTS_ATTRIBUTES_FLAG) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-attributes") + endif() + if (COMPILER_SUPPORTS_STRINGOP_OVERFLOW_FLAG) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-error=stringop-overflow") + endif() + if (COMPILER_SUPPORTS_ARRAY_BOUNDS_FLAG) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-array-bounds") + endif() + if (COMPILER_SUPPORTS_DEPRECATED_BUILTINS_FLAG) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated-builtins") + endif() endif() macro(install_neug_target target) @@ -337,13 +399,26 @@ endif() if (WITH_MIMALLOC) set(MI_BUILD_TESTS OFF CACHE BOOL "Build mimalloc tests") - set(MI_BUILD_SHARED ON CACHE BOOL "Build mimalloc as shared library" FORCE) - set(MI_BUILD_STATIC OFF CACHE BOOL "Build mimalloc as static library") + if(WIN32) + # On Windows, build mimalloc as a static library to avoid DLL + # initialization issues (DllMain fails when loaded into Python). + # MI_OVERRIDE=OFF ensures static mimalloc does not override malloc. + set(MI_BUILD_SHARED OFF CACHE BOOL "Build mimalloc as shared library" FORCE) + set(MI_BUILD_STATIC ON CACHE BOOL "Build mimalloc as static library" FORCE) + else() + set(MI_BUILD_SHARED ON CACHE BOOL "Build mimalloc as shared library" FORCE) + set(MI_BUILD_STATIC OFF CACHE BOOL "Build mimalloc as static library") + endif() set(MI_OVERRIDE OFF CACHE BOOL "Override malloc with mimalloc" FORCE) set(MI_USE_LIBATOMIC OFF CACHE BOOL "Use libatomic") # we should not use static mimalloc, the reason is that static mimalloc will override the default malloc. add_subdirectory(third_party/mimalloc) - set_target_properties(mimalloc PROPERTIES DEBUG_POSTFIX "") + if(TARGET mimalloc) + set_target_properties(mimalloc PROPERTIES DEBUG_POSTFIX "") + endif() + if(TARGET mimalloc-static) + set_target_properties(mimalloc-static PROPERTIES DEBUG_POSTFIX "") + endif() include_directories(SYSTEM third_party/mimalloc/include) add_compile_definitions(NEUG_WITH_MIMALLOC) unset(PACKAGE_VERSION) # Unset PACKAGE_VERSION to avoid generate two PACKAGE_VERSION in config.h for libdwarf-lite. @@ -460,7 +535,9 @@ function(add_neug_test TEST_NAME) endfunction() -set(TEST_PROGRAM " +# copy_file_range is a Linux-specific syscall; skip the probe on Windows. +if(NOT WIN32) + set(TEST_PROGRAM " #define _GNU_SOURCE #include #include @@ -473,22 +550,29 @@ int main() { } ") -check_cxx_source_compiles("${TEST_PROGRAM}" HAVE_COPY_FILE_RANGE_COMPILES) + check_cxx_source_compiles("${TEST_PROGRAM}" HAVE_COPY_FILE_RANGE_COMPILES) -if(NOT HAVE_COPY_FILE_RANGE_COMPILES) - message(WARNING "copy_file_range is not available or failed to compile!") -else() - message(STATUS "copy_file_range compilation succeeded.") -endif() + if(NOT HAVE_COPY_FILE_RANGE_COMPILES) + message(WARNING "copy_file_range is not available or failed to compile!") + else() + message(STATUS "copy_file_range compilation succeeded.") + endif() -if(HAVE_COPY_FILE_RANGE_COMPILES) - add_definitions(-DUSE_COPY_FILE_RANGE) + if(HAVE_COPY_FILE_RANGE_COMPILES) + add_definitions(-DUSE_COPY_FILE_RANGE) + endif() +else() + message(STATUS "copy_file_range is not available on Windows; skipping probe.") endif() find_package(Threads REQUIRED) find_package(OpenSSL) if (NOT OpenSSL_FOUND) - message(FATAL_ERROR "Fail to find OpenSSL, try to install openssl development package. On macos: brew install openssl@3; on ubuntu: apt-get install libssl-dev") + if(WIN32) + message(FATAL_ERROR "Fail to find OpenSSL. On Windows, install OpenSSL via vcpkg (openssl:x64-windows-static-md) or set OPENSSL_ROOT_DIR.") + else() + message(FATAL_ERROR "Fail to find OpenSSL, try to install openssl development package. On macos: brew install openssl@3; on ubuntu: apt-get install libssl-dev") + endif() endif() include_directories(SYSTEM ${OPENSSL_INCLUDE_DIR}) @@ -530,7 +614,9 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}/third_party) # find Threads------------------------------------------------------------------ -set(CMAKE_THREAD_PREFER_PTHREAD ON) +if(NOT WIN32) + set(CMAKE_THREAD_PREFER_PTHREAD ON) +endif() find_package(Threads REQUIRED) # find gflags------------------------------------------------------------------- @@ -894,6 +980,10 @@ endif() if (ENABLE_BACKTRACES) set(COMPILER_LIBRARIES ${COMPILER_LIBRARIES} cpptrace::cpptrace) endif() +# Windows requires additional system libraries for sockets, security, etc. +if(WIN32) + set(COMPILER_LIBRARIES ${COMPILER_LIBRARIES} ws2_32 mswsock advapi32 shell32 bcrypt) +endif() message(STATUS "head: Compiler libraries: ${COMPILER_LIBRARIES}") set(MACOS_DYLIBS @@ -929,6 +1019,8 @@ endif() include_directories(SYSTEM third_party/expected) add_subdirectory(src) neug_apply_native_arch_to_directory("${CMAKE_CURRENT_SOURCE_DIR}/src") +# tools contains python_bind (gated by BUILD_PYTHON) and utils; +# allow on Windows so Python bindings can be built when requested. add_subdirectory(tools) if (BUILD_EXECUTABLES) add_subdirectory(bin) @@ -1045,7 +1137,11 @@ set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "neug is a embedded graph database") set(CPACK_PACKAGE_VENDOR "GraphScope") set(CPACK_PACKAGE_VERSION ${NEUG_VERSION_SEMVER}) set(CPACK_PACKAGE_RELEASE 1) -set(CPACK_GENERATOR "DEB") +if(WIN32) + set(CPACK_GENERATOR "ZIP") +else() + set(CPACK_GENERATOR "DEB") +endif() set(CPACK_PACKAGE_CONTACT "graphscope@alibaba-inc.com") set(CPACK_DEBIAN_PACKAGE_MAINTAINER "graphscope") set(CPACK_DEBIAN_FILE_NAME DEB-DEFAULT) @@ -1138,7 +1234,11 @@ if (WITH_MIMALLOC) ${PROJECT_SOURCE_DIR}/third_party/mimalloc/include/mimalloc-new-delete.h DESTINATION include ) - install_neug_target(mimalloc) + if(WIN32) + install_neug_target(mimalloc-static) + else() + install_neug_target(mimalloc) + endif() endif() install_neug_target(${GLOG_LIBRARIES}) diff --git a/cmake/BuildProtobufThirdParty.cmake b/cmake/BuildProtobufThirdParty.cmake index c659a5ac7..e2ec346f8 100644 --- a/cmake/BuildProtobufThirdParty.cmake +++ b/cmake/BuildProtobufThirdParty.cmake @@ -27,6 +27,7 @@ function (build_protobuf_as_third_party) set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build shared libraries" FORCE) set(protobuf_BUILD_SHARED_LIBS OFF CACHE BOOL "Build protobuf shared libraries" FORCE) + set(protobuf_MSVC_STATIC_RUNTIME OFF CACHE BOOL "Use dynamic C runtime for protobuf" FORCE) set(protobuf_BUILD_TESTS OFF CACHE BOOL "Build protobuf tests" FORCE) set(protobuf_BUILD_CONFORMANCE OFF CACHE BOOL "Build protobuf conformance tests") set(protobuf_BUILD_EXAMPLES OFF CACHE BOOL "Build protobuf examples") @@ -67,10 +68,12 @@ function (build_protobuf_as_third_party) # trigger this warning on GCC 13+. foreach(_proto_tgt libprotobuf libprotobuf-lite libprotoc ${protobuf_ABSL_USED_TARGETS}) if(TARGET ${_proto_tgt}) - target_compile_options(${_proto_tgt} PRIVATE - -Wno-sign-compare -Wno-deprecated-declarations -Wno-attributes -Wno-ignored-attributes - $<$:-Wno-stringop-overflow -Wno-stringop-overread -Wno-missing-requires> - ) + if(NOT MSVC) + target_compile_options(${_proto_tgt} PRIVATE + -Wno-sign-compare -Wno-deprecated-declarations -Wno-attributes -Wno-ignored-attributes + $<$:-Wno-stringop-overflow -Wno-stringop-overread -Wno-missing-requires> + ) + endif() endif() endforeach() # libprotobuf and friends default to hidden visibility when built as @@ -94,10 +97,12 @@ function (build_protobuf_as_third_party) message(STATUS "protobuf uses abseil-cpp targets: ${_absl_targets}") foreach(_absl_tgt IN LISTS _absl_targets) if(TARGET ${_absl_tgt}) - target_compile_options(${_absl_tgt} PRIVATE - -Wno-sign-compare -Wno-deprecated-declarations -Wno-attributes -Wno-ignored-attributes - $<$:-Wno-stringop-overflow -Wno-stringop-overread -Wno-missing-requires> - ) + if(NOT MSVC) + target_compile_options(${_absl_tgt} PRIVATE + -Wno-sign-compare -Wno-deprecated-declarations -Wno-attributes -Wno-ignored-attributes + $<$:-Wno-stringop-overflow -Wno-stringop-overread -Wno-missing-requires> + ) + endif() else() message(STATUS "Requested abseil target ${_absl_tgt} not built in this configuration, skipping warning suppression") endif() diff --git a/cmake/neug_symbol_visibility.cmake b/cmake/neug_symbol_visibility.cmake index fe907005e..6f49d63d4 100644 --- a/cmake/neug_symbol_visibility.cmake +++ b/cmake/neug_symbol_visibility.cmake @@ -18,7 +18,10 @@ # Apply to every non-test binary that ships alongside libneug.so/dylib. macro(neug_apply_symbol_visibility target) if(WIN32) - message(FATAL_ERROR "neug_apply_symbol_visibility: symbol visibility control is not supported on Windows.") + # Windows: rely on the NEUG_API macro (defined in include/neug/utils/api.h) + # to export only the public API. Exporting all symbols hits the linker + # limit of 65535 exports (LNK1189) for a library of this size. + set_target_properties(${target} PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS OFF) elseif(APPLE) target_link_options(${target} PRIVATE "LINKER:-unexported_symbols_list,${CMAKE_SOURCE_DIR}/cmake/neug_unexported.sym") diff --git a/include/neug/common/columns/vertex_columns.h b/include/neug/common/columns/vertex_columns.h index 07e95aba0..18bd906d3 100644 --- a/include/neug/common/columns/vertex_columns.h +++ b/include/neug/common/columns/vertex_columns.h @@ -15,6 +15,7 @@ #pragma once #include "neug/common/types/i_context_column.h" +#include "neug/utils/platform.h" namespace neug { @@ -43,7 +44,7 @@ class IVertexColumn : public IContextColumn { return Value::VERTEX(this->get_vertex(idx)); } - __attribute__((always_inline)) const DataType& elem_type() const override { + NEUG_ALWAYS_INLINE const DataType& elem_type() const override { return type_; } @@ -76,7 +77,7 @@ class SLVertexColumn : public IVertexColumn { explicit SLVertexColumn(label_t label) : label_(label) {} ~SLVertexColumn() = default; - __attribute__((always_inline)) inline size_t size() const override { + NEUG_ALWAYS_INLINE inline size_t size() const override { return vertices_.size(); } @@ -96,7 +97,7 @@ class SLVertexColumn : public IVertexColumn { std::shared_ptr optional_shuffle( const sel_vec_t& offset) const override; - __attribute__((always_inline)) VertexRecord get_vertex( + NEUG_ALWAYS_INLINE VertexRecord get_vertex( size_t idx) const override { return {label_, vertices_[idx]}; } @@ -131,9 +132,9 @@ class SLVertexColumn : public IVertexColumn { return ret; } - __attribute__((always_inline)) label_t label() const { return label_; } + NEUG_ALWAYS_INLINE label_t label() const { return label_; } - __attribute__((always_inline)) const vector_t& vertices() const { + NEUG_ALWAYS_INLINE const vector_t& vertices() const { return vertices_; } @@ -149,7 +150,7 @@ class MSVertexColumn : public IVertexColumn { MSVertexColumn() = default; ~MSVertexColumn() = default; - __attribute__((always_inline)) size_t size() const override { + NEUG_ALWAYS_INLINE size_t size() const override { size_t ret = 0; for (auto& pair : vertices_) { ret += pair.second.size(); @@ -181,7 +182,7 @@ class MSVertexColumn : public IVertexColumn { std::shared_ptr optional_shuffle( const sel_vec_t& offsets) const override; - __attribute__((always_inline)) VertexRecord get_vertex( + NEUG_ALWAYS_INLINE VertexRecord get_vertex( size_t idx) const override { for (auto& pair : vertices_) { if (idx < pair.second.size()) { @@ -194,11 +195,11 @@ class MSVertexColumn : public IVertexColumn { std::numeric_limits::max()}; } - __attribute__((always_inline)) bool is_optional() const override { + NEUG_ALWAYS_INLINE bool is_optional() const override { return is_optional_; } - __attribute__((always_inline)) bool has_value(size_t idx) const override { + NEUG_ALWAYS_INLINE bool has_value(size_t idx) const override { auto v = get_vertex(idx); return v.vid_ != std::numeric_limits::max(); } @@ -216,15 +217,15 @@ class MSVertexColumn : public IVertexColumn { std::set get_labels_set() const override { return labels_; } - __attribute__((always_inline)) size_t seg_num() const { + NEUG_ALWAYS_INLINE size_t seg_num() const { return vertices_.size(); } - __attribute__((always_inline)) label_t seg_label(size_t seg_id) const { + NEUG_ALWAYS_INLINE label_t seg_label(size_t seg_id) const { return vertices_[seg_id].first; } - __attribute__((always_inline)) const vector_t& seg_vertices( + NEUG_ALWAYS_INLINE const vector_t& seg_vertices( size_t seg_id) const { return vertices_[seg_id].second; } @@ -266,7 +267,7 @@ class MSVertexColumnBuilder : public IVertexColumnBuilder { push_back_opt(v.vid_); } - __attribute__((always_inline)) void start_label(label_t label) { + NEUG_ALWAYS_INLINE void start_label(label_t label) { if (!cur_list_.empty() && cur_label_ != label && cur_label_ != std::numeric_limits::max()) { vertices_.emplace_back(cur_label_, std::move(cur_list_)); diff --git a/include/neug/common/types.h b/include/neug/common/types.h index 284048df2..bdf93b9b7 100644 --- a/include/neug/common/types.h +++ b/include/neug/common/types.h @@ -23,6 +23,8 @@ #include #include + +#include "neug/utils/api.h" #include #include #include @@ -109,7 +111,7 @@ enum class DataTypeId : uint8_t { struct ExtraTypeInfo; -struct DataType { +struct NEUG_API DataType { DataType(); DataType(DataTypeId id); DataType(DataTypeId id, std::shared_ptr type_info); diff --git a/include/neug/compiler/binder/copy/bound_copy_from.h b/include/neug/compiler/binder/copy/bound_copy_from.h index 6200fe0aa..cbdd9776f 100644 --- a/include/neug/compiler/binder/copy/bound_copy_from.h +++ b/include/neug/compiler/binder/copy/bound_copy_from.h @@ -29,6 +29,7 @@ #include "neug/compiler/binder/expression/expression.h" #include "neug/compiler/binder/expression_binder.h" #include "neug/compiler/catalog/catalog_entry/table_catalog_entry.h" +#include "neug/compiler/catalog/catalog_entry/node_table_catalog_entry.h" #include "neug/compiler/common/enums/column_evaluate_type.h" #include "neug/compiler/common/types/types.h" #include "neug/compiler/gopt/g_rel_table_entry.h" @@ -62,6 +63,7 @@ struct NEUG_API DDLVertexInfo : public DDLTableInfo { const std::string& primaryKeyName, const expression_vector& columns, ExpressionBinder& binder, bool temporary = false); + ~DDLVertexInfo(); // return vertex label name std::string getVertexLabelName(); diff --git a/include/neug/compiler/common/enums/delete_type.h b/include/neug/compiler/common/enums/delete_type.h index 8c2907aee..c2f6f4a51 100644 --- a/include/neug/compiler/common/enums/delete_type.h +++ b/include/neug/compiler/common/enums/delete_type.h @@ -28,7 +28,7 @@ namespace neug { namespace common { enum class DeleteNodeType : uint8_t { - DELETE = 0, + DELETE_NODE = 0, DETACH_DELETE = 1, }; diff --git a/include/neug/compiler/common/enums/explain_type.h b/include/neug/compiler/common/enums/explain_type.h index ff1c75ff8..a15f9a327 100644 --- a/include/neug/compiler/common/enums/explain_type.h +++ b/include/neug/compiler/common/enums/explain_type.h @@ -24,6 +24,11 @@ #include +// Windows headers may define NONE as a macro; undefine it. +#ifdef NONE +#undef NONE +#endif + namespace neug { namespace common { diff --git a/include/neug/compiler/function/arithmetic/arithmetic_functions.h b/include/neug/compiler/function/arithmetic/arithmetic_functions.h index ae72562e2..f9f606b00 100644 --- a/include/neug/compiler/function/arithmetic/arithmetic_functions.h +++ b/include/neug/compiler/function/arithmetic/arithmetic_functions.h @@ -24,6 +24,11 @@ #include +// MSVC does not define M_PI by default; define it if missing. +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + #include "neug/compiler/common/types/int128_t.h" namespace neug { diff --git a/include/neug/compiler/function/read_function.h b/include/neug/compiler/function/read_function.h index cfc8c74e1..61bcbb380 100644 --- a/include/neug/compiler/function/read_function.h +++ b/include/neug/compiler/function/read_function.h @@ -16,7 +16,9 @@ #pragma once +#ifndef _WIN32 #include +#endif #include #include #include diff --git a/include/neug/compiler/gopt/g_physical_analyzer.h b/include/neug/compiler/gopt/g_physical_analyzer.h index 3db560159..adb9b663c 100644 --- a/include/neug/compiler/gopt/g_physical_analyzer.h +++ b/include/neug/compiler/gopt/g_physical_analyzer.h @@ -210,7 +210,7 @@ class GPhysicalAnalyzer { break; } case planner::LogicalOperatorType::SET_PROPERTY: - case planner::LogicalOperatorType::DELETE: + case planner::LogicalOperatorType::DELETE_OP: case planner::LogicalOperatorType::MERGE: { flag.update = true; break; diff --git a/include/neug/compiler/gopt/g_physical_convertor.h b/include/neug/compiler/gopt/g_physical_convertor.h index ce6c012d8..3933ae23c 100644 --- a/include/neug/compiler/gopt/g_physical_convertor.h +++ b/include/neug/compiler/gopt/g_physical_convertor.h @@ -77,7 +77,7 @@ class GPhysicalConvertor { op->getOperatorType() == planner::LogicalOperatorType::MERGE || op->getOperatorType() == planner::LogicalOperatorType::SET_PROPERTY || - op->getOperatorType() == planner::LogicalOperatorType::DELETE; + op->getOperatorType() == planner::LogicalOperatorType::DELETE_OP; } bool ddlClause(std::shared_ptr op) { diff --git a/include/neug/compiler/gopt/g_result_schema.h b/include/neug/compiler/gopt/g_result_schema.h index 58aa21475..99bd42323 100644 --- a/include/neug/compiler/gopt/g_result_schema.h +++ b/include/neug/compiler/gopt/g_result_schema.h @@ -97,7 +97,7 @@ class GResultSchema { opType == planner::LogicalOperatorType::INSERT || opType == planner::LogicalOperatorType::MERGE || opType == planner::LogicalOperatorType::SET_PROPERTY || - opType == planner::LogicalOperatorType::DELETE || + opType == planner::LogicalOperatorType::DELETE_OP || opType == planner::LogicalOperatorType::COPY_TO || opType == planner::LogicalOperatorType::TRANSACTION || opType == planner::LogicalOperatorType::EXTENSION || diff --git a/include/neug/compiler/planner/operator/logical_operator.h b/include/neug/compiler/planner/operator/logical_operator.h index 87e51558a..7bac822cc 100644 --- a/include/neug/compiler/planner/operator/logical_operator.h +++ b/include/neug/compiler/planner/operator/logical_operator.h @@ -23,7 +23,7 @@ enum class LogicalOperatorType : uint8_t { CREATE_TABLE, CREATE_TYPE, CROSS_PRODUCT, - DELETE, + DELETE_OP, DETACH_DATABASE, DISTINCT, DROP, diff --git a/include/neug/compiler/planner/operator/persistent/logical_delete.h b/include/neug/compiler/planner/operator/persistent/logical_delete.h index b36c759be..e3c61a42e 100644 --- a/include/neug/compiler/planner/operator/persistent/logical_delete.h +++ b/include/neug/compiler/planner/operator/persistent/logical_delete.h @@ -22,7 +22,7 @@ struct LogicalDeletePrintInfo final : OPPrintInfo { }; class LogicalDelete final : public LogicalOperator { - static constexpr LogicalOperatorType type_ = LogicalOperatorType::DELETE; + static constexpr LogicalOperatorType type_ = LogicalOperatorType::DELETE_OP; public: LogicalDelete(std::vector infos, diff --git a/include/neug/compiler/planner/operator/sip/side_way_info_passing.h b/include/neug/compiler/planner/operator/sip/side_way_info_passing.h index 5a43fd78c..87f2a7b86 100644 --- a/include/neug/compiler/planner/operator/sip/side_way_info_passing.h +++ b/include/neug/compiler/planner/operator/sip/side_way_info_passing.h @@ -2,6 +2,11 @@ #include +// Windows headers may define NONE as a macro; undefine it. +#ifdef NONE +#undef NONE +#endif + namespace neug { namespace planner { diff --git a/include/neug/compiler/planner/planner.h b/include/neug/compiler/planner/planner.h index c910606d3..bf3456b10 100644 --- a/include/neug/compiler/planner/planner.h +++ b/include/neug/compiler/planner/planner.h @@ -30,6 +30,14 @@ namespace planner { struct LogicalInsertInfo; +// Windows headers may define NONE and OPTIONAL as macros; undefine them. +#ifdef NONE +#undef NONE +#endif +#ifdef OPTIONAL +#undef OPTIONAL +#endif + enum class SubqueryPlanningType : uint8_t { NONE = 0, UNNEST_CORRELATED = 1, diff --git a/include/neug/compiler/processor/operator/persistent/reader/copy_from_error.h b/include/neug/compiler/processor/operator/persistent/reader/copy_from_error.h index f6f09ab94..5964dc60f 100644 --- a/include/neug/compiler/processor/operator/persistent/reader/copy_from_error.h +++ b/include/neug/compiler/processor/operator/persistent/reader/copy_from_error.h @@ -56,7 +56,8 @@ struct NEUG_API WarningSourceData { static constexpr size_t NUM_BLOCK_VALUES = 2; WarningSourceData() : WarningSourceData(0) {} - explicit WarningSourceData(uint64_t numSourceSpecificValues); + explicit WarningSourceData(uint64_t numSourceSpecificValues) + : numValues(numSourceSpecificValues) {} template void dumpTo(uint64_t& blockIdx, uint32_t& offsetInBlock, diff --git a/include/neug/compiler/processor/operator/physical_operator.h b/include/neug/compiler/processor/operator/physical_operator.h index 9c47fc2fa..64ce313bd 100644 --- a/include/neug/compiler/processor/operator/physical_operator.h +++ b/include/neug/compiler/processor/operator/physical_operator.h @@ -162,7 +162,7 @@ class NEUG_API PhysicalOperator { bool getNextTuple(ExecutionContext* context); - virtual void finalize(ExecutionContext* context); + virtual void finalize(ExecutionContext* context) {} std::unordered_map getProfilerKeyValAttributes( common::Profiler& profiler) const; @@ -173,7 +173,7 @@ class NEUG_API PhysicalOperator { virtual std::unique_ptr copy() = 0; - virtual double getProgress(ExecutionContext* context) const; + virtual double getProgress(ExecutionContext* context) const { return 0.0; } template TARGET* ptrCast() { diff --git a/include/neug/compiler/storage/predicate/column_predicate.h b/include/neug/compiler/storage/predicate/column_predicate.h index ccbe7a960..31c4ef16c 100644 --- a/include/neug/compiler/storage/predicate/column_predicate.h +++ b/include/neug/compiler/storage/predicate/column_predicate.h @@ -67,7 +67,7 @@ class NEUG_API ColumnPredicate { virtual common::ZoneMapCheckResult checkZoneMap( const MergedColumnChunkStats& stats) const = 0; - virtual std::string toString(); + virtual std::string toString() { return columnName; } virtual std::unique_ptr copy() const = 0; diff --git a/include/neug/compiler/storage/store/table.h b/include/neug/compiler/storage/store/table.h index e8fd9a3c3..cdf946f86 100644 --- a/include/neug/compiler/storage/store/table.h +++ b/include/neug/compiler/storage/store/table.h @@ -44,7 +44,13 @@ class NEUG_API Table { tableName{const_cast(tableEntry)->get_label()} {} Table(const SchemaEntry* tableEntry, const GraphStats* storageManager, - MemoryManager* memoryManager); + MemoryManager* memoryManager) + : tableType{tableEntry->get_entry_type()}, + tableID{tableEntry->get_entry_id()}, + tableName{const_cast(tableEntry)->get_label()}, + enableCompression{false}, + memoryManager{memoryManager}, + hasChanges{false} {} virtual ~Table() = default; SchemaEntryType getTableType() const { return tableType; } diff --git a/include/neug/execution/common/operators/retrieve/edge_expand.h b/include/neug/execution/common/operators/retrieve/edge_expand.h index 5ce519d7f..e886a44cc 100644 --- a/include/neug/execution/common/operators/retrieve/edge_expand.h +++ b/include/neug/execution/common/operators/retrieve/edge_expand.h @@ -16,6 +16,7 @@ #include "neug/common/types/graph_types.h" #include "neug/execution/common/context_chunk.h" +#include "neug/utils/platform.h" #include "neug/execution/common/operators/retrieve/edge_expand_impl.h" #include "neug/execution/common/params_map.h" #include "neug/execution/expression/special_predicates.h" diff --git a/include/neug/main/connection.h b/include/neug/main/connection.h index 8aac09662..9fcea01c7 100644 --- a/include/neug/main/connection.h +++ b/include/neug/main/connection.h @@ -81,7 +81,7 @@ class NeugDB; * * @since v0.1.0 */ -class Connection { +class NEUG_API Connection { public: Connection(GraphSnapshotStore& snapshot_store, std::shared_ptr query_processor) diff --git a/include/neug/main/neug_db.h b/include/neug/main/neug_db.h index 9f5af0573..6dfc7f2f7 100644 --- a/include/neug/main/neug_db.h +++ b/include/neug/main/neug_db.h @@ -111,7 +111,7 @@ class Schema; * * @since v0.1.0 */ -class NeugDB { +class NEUG_API NeugDB { public: NeugDB(); ~NeugDB(); diff --git a/include/neug/main/query_result.h b/include/neug/main/query_result.h index 0fd78253c..c8811b1c9 100644 --- a/include/neug/main/query_result.h +++ b/include/neug/main/query_result.h @@ -38,7 +38,7 @@ namespace neug { * - typed cell access via `GetInt32()`, `GetString()`, etc. */ -class QueryResult { +class NEUG_API QueryResult { public: static QueryResult From(std::string&& serialized_table); static QueryResult From(const std::string& serialized_table); diff --git a/include/neug/storages/checkpoint_manager.h b/include/neug/storages/checkpoint_manager.h index 5db6ff6e0..286bc3352 100644 --- a/include/neug/storages/checkpoint_manager.h +++ b/include/neug/storages/checkpoint_manager.h @@ -19,6 +19,7 @@ #include #include +#include "neug/utils/api.h" #include "neug/storages/checkpoint.h" namespace neug { @@ -47,7 +48,7 @@ namespace neug { * an internal mutex). Callers that race CreateStagingCheckpoint() / Close() * must coordinate externally. */ -class CheckpointManager { +class NEUG_API CheckpointManager { public: class StagingCheckpoint { public: diff --git a/include/neug/storages/csr/csr_view.h b/include/neug/storages/csr/csr_view.h index 659bfdb52..5f18a016e 100644 --- a/include/neug/storages/csr/csr_view.h +++ b/include/neug/storages/csr/csr_view.h @@ -17,6 +17,7 @@ #include #include "neug/common/types/value.h" +#include "neug/utils/platform.h" #include "neug/storages/csr/nbr.h" #include "neug/storages/csr/prefetch_utils.h" #include "neug/utils/property/column.h" @@ -115,7 +116,7 @@ struct NbrIterator { inline vid_t operator*() const { return get_vertex(); } /** @brief Advance to next visible neighbor. */ - __attribute__((always_inline)) NbrIterator& operator++() { + NEUG_ALWAYS_INLINE NbrIterator& operator++() { cur = static_cast(cur) + cfg.stride; while (cur != end && get_timestamp() > timestamp) { cur = static_cast(cur) + cfg.stride; @@ -124,18 +125,18 @@ struct NbrIterator { } /** @brief Advance by n positions. */ - __attribute__((always_inline)) NbrIterator& operator+=(size_t n) { + NEUG_ALWAYS_INLINE NbrIterator& operator+=(size_t n) { for (size_t i = 0; i < n; ++i) { ++(*this); } return *this; } - __attribute__((always_inline)) bool operator==(const NbrIterator& rhs) const { + NEUG_ALWAYS_INLINE bool operator==(const NbrIterator& rhs) const { return (cur == rhs.cur); } - __attribute__((always_inline)) bool operator!=(const NbrIterator& rhs) const { + NEUG_ALWAYS_INLINE bool operator!=(const NbrIterator& rhs) const { return (cur != rhs.cur); } @@ -210,14 +211,14 @@ struct NbrList { ~NbrList() = default; /** @brief Get iterator to first visible neighbor. */ - __attribute__((always_inline)) NbrIterator begin() const { + NEUG_ALWAYS_INLINE NbrIterator begin() const { NbrIterator it; it.init(start_ptr, end_ptr, cfg, timestamp); return it; } /** @brief Get iterator to past-the-end position. */ - __attribute__((always_inline)) NbrIterator end() const { + NEUG_ALWAYS_INLINE NbrIterator end() const { NbrIterator it; it.init(end_ptr, end_ptr, cfg, timestamp); return it; @@ -615,7 +616,7 @@ struct CsrView { * @note This is the primary method for graph traversal. * @note Empty NbrList is returned if vertex has no edges. */ - __attribute__((always_inline)) NbrList get_edges(vid_t v) const { + NEUG_ALWAYS_INLINE NbrList get_edges(vid_t v) const { NbrList ret; if (degrees_ == nullptr) { const char* start_ptr = adjlists_ + v * cfg_.stride; @@ -663,15 +664,15 @@ struct CsrView { } } - __attribute__((always_inline)) size_t prefetch_metadata_dist() const { + NEUG_ALWAYS_INLINE size_t prefetch_metadata_dist() const { return prefetch_policy_.metadata_distance; } - __attribute__((always_inline)) size_t prefetch_head_dist() const { + NEUG_ALWAYS_INLINE size_t prefetch_head_dist() const { return prefetch_policy_.head_distance; } - __attribute__((always_inline)) void prefetch_metadata(vid_t v) const { + NEUG_ALWAYS_INLINE void prefetch_metadata(vid_t v) const { if (degrees_ == nullptr) { const char* start_ptr = adjlists_ + v * cfg_.stride; prefetch_read(start_ptr, prefetch_policy_.metadata_locality); @@ -682,7 +683,7 @@ struct CsrView { } } - __attribute__((always_inline)) void prefetch_head(vid_t v) const { + NEUG_ALWAYS_INLINE void prefetch_head(vid_t v) const { if (degrees_ == nullptr) { const char* start_ptr = adjlists_ + v * cfg_.stride; prefetch_read(start_ptr, prefetch_policy_.head_locality); @@ -696,7 +697,7 @@ struct CsrView { } private: - __attribute__((always_inline)) static void prefetch_read(const void* ptr, + NEUG_ALWAYS_INLINE static void prefetch_read(const void* ptr, uint8_t locality) { switch (locality) { case 0: @@ -722,7 +723,7 @@ struct CsrView { }; template -__attribute__((always_inline)) static inline void prefetch_next_vertex( +NEUG_ALWAYS_INLINE static inline void prefetch_next_vertex( const CsrView& view, const VECTOR_T& vertices, size_t idx) { size_t metadata_dist = view.prefetch_metadata_dist(); if (metadata_dist != 0 && idx + metadata_dist < vertices.size()) { @@ -735,7 +736,7 @@ __attribute__((always_inline)) static inline void prefetch_next_vertex( } template -__attribute__((always_inline)) static inline void prefetch_next_vertex_column( +NEUG_ALWAYS_INLINE static inline void prefetch_next_vertex_column( const CsrView& view, const VERTEX_COLUMN_T& vertices, size_t idx) { size_t metadata_dist = view.prefetch_metadata_dist(); if (metadata_dist != 0 && idx + metadata_dist < vertices.size()) { diff --git a/include/neug/storages/graph/edge_table.h b/include/neug/storages/graph/edge_table.h index 56ac97a6a..d3250ca1e 100644 --- a/include/neug/storages/graph/edge_table.h +++ b/include/neug/storages/graph/edge_table.h @@ -28,6 +28,7 @@ #include "neug/storages/checkpoint_manager.h" #include "neug/storages/csr/csr_base.h" #include "neug/storages/csr/csr_view.h" +#include "neug/utils/api.h" #include "neug/storages/graph/schema.h" #include "neug/storages/module/module.h" #include "neug/utils/indexers.h" @@ -42,12 +43,14 @@ class PropertyGraph; class IDataChunkSupplier; -class EdgeTable { +class NEUG_API EdgeTable { public: EdgeTable(std::shared_ptr meta) : meta_(meta) {} EdgeTable(EdgeTable&& edge_table); EdgeTable(const EdgeTable&) = delete; + EdgeTable& operator=(const EdgeTable&) = delete; + EdgeTable& operator=(EdgeTable&& other) noexcept; ~EdgeTable() = default; void Swap(EdgeTable& other); diff --git a/include/neug/storages/graph/property_graph.h b/include/neug/storages/graph/property_graph.h index 4b12e6403..632dd9462 100644 --- a/include/neug/storages/graph/property_graph.h +++ b/include/neug/storages/graph/property_graph.h @@ -29,6 +29,7 @@ #include "neug/common/types/value.h" #include "neug/storages/allocators.h" +#include "neug/utils/api.h" #include "neug/storages/checkpoint.h" #include "neug/storages/checkpoint_manager.h" #include "neug/storages/csr/csr_view.h" @@ -96,7 +97,7 @@ namespace neug { * * @since v0.1.0 */ -class PropertyGraph { +class NEUG_API PropertyGraph { public: /** * @brief Construct PropertyGraph with default settings. @@ -115,6 +116,13 @@ class PropertyGraph { */ ~PropertyGraph(); + // Move-only: copy is not meaningful because the underlying storage + // (VertexTable, EdgeTable) is move-only. Use Clone() for explicit copies. + PropertyGraph(const PropertyGraph&) = delete; + PropertyGraph& operator=(const PropertyGraph&) = delete; + PropertyGraph(PropertyGraph&&) = default; + PropertyGraph& operator=(PropertyGraph&&) = default; + /** * @brief Open the graph from the given Checkpoint using the Module interface. * diff --git a/include/neug/storages/graph/schema.h b/include/neug/storages/graph/schema.h index d5e1cf092..41d7ad038 100644 --- a/include/neug/storages/graph/schema.h +++ b/include/neug/storages/graph/schema.h @@ -26,6 +26,7 @@ #include #include #include +#include "neug/utils/api.h" #include "neug/common/types/value.h" #include "neug/utils/bitset.h" #include "neug/utils/property/default_value.h" @@ -90,7 +91,7 @@ class SchemaEntry { virtual std::string get_label() const = 0; }; -class LabelIndexer { +class NEUG_API LabelIndexer { public: bool add(const std::string& name, label_t& lid); bool get_index(const std::string& name, label_t& lid) const; @@ -480,7 +481,7 @@ struct EdgeSchema : public SchemaEntry { * * @since v0.1.0 */ -class Schema { +class NEUG_API Schema { public: /// @name Plugin ID Constants /// @{ diff --git a/include/neug/storages/graph/vertex_table.h b/include/neug/storages/graph/vertex_table.h index 8ca36284a..2605d32a2 100644 --- a/include/neug/storages/graph/vertex_table.h +++ b/include/neug/storages/graph/vertex_table.h @@ -22,6 +22,7 @@ #include "neug/storages/module/module.h" #include "neug/utils/indexers.h" #include "neug/utils/property/table.h" +#include "neug/utils/api.h" namespace neug { @@ -88,7 +89,7 @@ class VertexSet { }; class PropertyGraph; -class VertexTable { +class NEUG_API VertexTable { public: VertexTable() : ckp_(nullptr), @@ -121,6 +122,20 @@ class VertexTable { memory_level_(other.memory_level_) {} VertexTable(const VertexTable&) = delete; + VertexTable& operator=(const VertexTable&) = delete; + + VertexTable& operator=(VertexTable&& other) noexcept { + if (this != &other) { + ckp_ = std::move(other.ckp_); + indexer_ = std::move(other.indexer_); + table_ = std::move(other.table_); + pk_type_ = other.pk_type_; + vertex_schema_ = other.vertex_schema_; + v_ts_ = std::move(other.v_ts_); + memory_level_ = other.memory_level_; + } + return *this; + } void Swap(VertexTable& other) { std::swap(ckp_, other.ckp_); diff --git a/include/neug/storages/graph/vertex_timestamp.h b/include/neug/storages/graph/vertex_timestamp.h index 6287cd8d3..5dad1bef0 100644 --- a/include/neug/storages/graph/vertex_timestamp.h +++ b/include/neug/storages/graph/vertex_timestamp.h @@ -23,10 +23,11 @@ #include "neug/storages/module/module.h" #include "neug/utils/likely.h" #include "neug/utils/property/types.h" +#include "neug/utils/api.h" namespace neug { -class VertexTimestamp : public Module { +class NEUG_API VertexTimestamp : public Module { public: static constexpr timestamp_t DELETED_TIMESTAMP = std::numeric_limits::max(); diff --git a/include/neug/storages/graph_snapshot_store.h b/include/neug/storages/graph_snapshot_store.h index 8a1c0b8fa..3d7c4b49c 100644 --- a/include/neug/storages/graph_snapshot_store.h +++ b/include/neug/storages/graph_snapshot_store.h @@ -48,7 +48,7 @@ namespace neug { * - PublishSnapshot publishes the new slot BEFORE VersionManager advances * read_ts_, so readers never see "new ts + old slot". */ -class GraphSnapshotStore { +class NEUG_API GraphSnapshotStore { public: /// A slot holding a PropertyGraph, its GraphView, and a pin count. class SnapshotSlot { diff --git a/include/neug/storages/module/module.h b/include/neug/storages/module/module.h index 4dad4b61e..2d8e75bd5 100644 --- a/include/neug/storages/module/module.h +++ b/include/neug/storages/module/module.h @@ -20,6 +20,7 @@ #include "neug/config.h" #include "neug/storages/checkpoint_manifest.h" #include "neug/storages/module_descriptor.h" +#include "neug/utils/api.h" namespace neug { @@ -32,7 +33,7 @@ class Checkpoint; * Clone (zero-copy COW clone), and Detach (detach * shared storage before mutation). */ -class Module { +class NEUG_API Module { public: virtual ~Module() = default; diff --git a/include/neug/utils/bitset.h b/include/neug/utils/bitset.h index d9d4a677d..79b0049d6 100644 --- a/include/neug/utils/bitset.h +++ b/include/neug/utils/bitset.h @@ -25,6 +25,11 @@ #include +#ifdef _WIN32 +#include +#include +#endif + #define WORD_SIZE(n) (((n) + 63ul) >> 6) #define NEUG_BYTE_SIZE(n) (WORD_SIZE(n) * sizeof(uint64_t)) @@ -39,6 +44,49 @@ (((i) + (alignment) -1) & (~((alignment) -1))) namespace neug { +namespace detail { + +inline void* AlignedAlloc(size_t alignment, size_t size) { +#ifdef _WIN32 + return _aligned_malloc(size, alignment); +#else + return aligned_alloc(alignment, size); +#endif +} + +inline void AlignedFree(void* ptr) { +#ifdef _WIN32 + _aligned_free(ptr); +#else + free(ptr); +#endif +} + +inline uint64_t AtomicFetchOr(uint64_t* ptr, uint64_t value) { +#ifdef _WIN32 + return static_cast(_InterlockedOr64(reinterpret_cast(ptr), static_cast<__int64>(value))); +#else + return __sync_fetch_and_or(ptr, value); +#endif +} + +inline uint64_t AtomicFetchAnd(uint64_t* ptr, uint64_t value) { +#ifdef _WIN32 + return static_cast(_InterlockedAnd64(reinterpret_cast(ptr), static_cast<__int64>(value))); +#else + return __sync_fetch_and_and(ptr, value); +#endif +} + +inline int PopCountLL(uint64_t value) { +#ifdef _WIN32 + return static_cast(__popcnt64(value)); +#else + return __builtin_popcountll(value); +#endif +} + +} // namespace detail class Bitset { static constexpr size_t kAlignment = 64; // 64-byte alignment @@ -62,7 +110,7 @@ class Bitset { assert(kAlignment <= capacity_in_words_ * sizeof(uint64_t)); auto alloc_size = ROUND_UP_TO_ALIGNMENT( capacity_in_words_ * sizeof(uint64_t), kAlignment); - data_ = static_cast(aligned_alloc(kAlignment, alloc_size)); + data_ = static_cast(detail::AlignedAlloc(kAlignment, alloc_size)); memcpy(data_, other.data_, capacity_in_words_ * sizeof(uint64_t)); } } @@ -82,7 +130,7 @@ class Bitset { ~Bitset() { if (data_ != nullptr) { - free(data_); + detail::AlignedFree(data_); } } @@ -92,7 +140,7 @@ class Bitset { } if (data_ != nullptr) { - free(data_); + detail::AlignedFree(data_); } size_ = other.size_; @@ -106,7 +154,7 @@ class Bitset { assert(kAlignment <= capacity_in_words_ * sizeof(uint64_t)); auto alloc_size = ROUND_UP_TO_ALIGNMENT( capacity_in_words_ * sizeof(uint64_t), kAlignment); - data_ = static_cast(aligned_alloc(kAlignment, alloc_size)); + data_ = static_cast(detail::AlignedAlloc(kAlignment, alloc_size)); memcpy(data_, other.data_, capacity_in_words_ * sizeof(uint64_t)); } return *this; @@ -118,7 +166,7 @@ class Bitset { } if (data_ != nullptr) { - free(data_); + detail::AlignedFree(data_); } data_ = other.data_; @@ -148,10 +196,10 @@ class Bitset { size_t alloc_size = ROUND_UP_TO_ALIGNMENT(new_cap_in_words * sizeof(uint64_t), kAlignment); uint64_t* new_data = - static_cast(aligned_alloc(kAlignment, alloc_size)); + static_cast(detail::AlignedAlloc(kAlignment, alloc_size)); if (data_ != nullptr) { memcpy(new_data, data_, size_in_words_ * sizeof(uint64_t)); - free(data_); + detail::AlignedFree(data_); } data_ = new_data; capacity_ = cap; @@ -178,7 +226,7 @@ class Bitset { (new_size_in_words - size_in_words_) * sizeof(uint64_t)); if (size_in_words_ && BIT_OFFSET(size_) != 0) { - uint64_t mask = ((1ul << BIT_OFFSET(size_)) - 1); + uint64_t mask = ((1ull << BIT_OFFSET(size_)) - 1); data_[size_in_words_ - 1] &= mask; } @@ -186,40 +234,40 @@ class Bitset { size_in_words_ = new_size_in_words; } - void set(size_t i) { data_[WORD_INDEX(i)] |= (1ul << BIT_OFFSET(i)); } + void set(size_t i) { data_[WORD_INDEX(i)] |= (1ull << BIT_OFFSET(i)); } - void reset(size_t i) { data_[WORD_INDEX(i)] &= (~(1ul << BIT_OFFSET(i))); } + void reset(size_t i) { data_[WORD_INDEX(i)] &= (~(1ull << BIT_OFFSET(i))); } void atomic_set(size_t i) { - uint64_t mask = 1ul << BIT_OFFSET(i); - __sync_fetch_and_or(data_ + WORD_INDEX(i), mask); + uint64_t mask = 1ull << BIT_OFFSET(i); + detail::AtomicFetchOr(data_ + WORD_INDEX(i), mask); } void atomic_reset(size_t i) { - uint64_t mask = 1ul << BIT_OFFSET(i); - __sync_fetch_and_and(data_ + WORD_INDEX(i), ~mask); + uint64_t mask = 1ull << BIT_OFFSET(i); + detail::AtomicFetchAnd(data_ + WORD_INDEX(i), ~mask); } bool atomic_set_with_ret(size_t i) { - uint64_t mask = 1ul << BIT_OFFSET(i); - uint64_t ret = __sync_fetch_and_or(data_ + WORD_INDEX(i), mask); + uint64_t mask = 1ull << BIT_OFFSET(i); + uint64_t ret = detail::AtomicFetchOr(data_ + WORD_INDEX(i), mask); return (ret & mask); } bool atomic_reset_with_ret(size_t i) { - uint64_t mask = 1ul << BIT_OFFSET(i); - uint64_t ret = __sync_fetch_and_and(data_ + WORD_INDEX(i), ~mask); + uint64_t mask = 1ull << BIT_OFFSET(i); + uint64_t ret = detail::AtomicFetchAnd(data_ + WORD_INDEX(i), ~mask); return (ret & mask); } bool get(size_t i) const { - return data_[WORD_INDEX(i)] & (1ul << BIT_OFFSET(i)); + return data_[WORD_INDEX(i)] & (1ull << BIT_OFFSET(i)); } size_t count() const { size_t ret = 0; for (size_t i = 0; i < size_in_words_; ++i) { - ret += __builtin_popcountll(data_[i]); + ret += detail::PopCountLL(data_[i]); } return ret; } diff --git a/include/neug/utils/bolt_utils.h b/include/neug/utils/bolt_utils.h index 977edc627..a419f2af4 100644 --- a/include/neug/utils/bolt_utils.h +++ b/include/neug/utils/bolt_utils.h @@ -17,10 +17,11 @@ #include "glog/logging.h" #include "neug/generated/proto/response/response.pb.h" +#include "neug/utils/api.h" namespace neug { -std::string results_to_bolt_response( +NEUG_API std::string results_to_bolt_response( const neug::QueryResponse& table, const std::vector& column_names); diff --git a/include/neug/utils/id_indexer.h b/include/neug/utils/id_indexer.h index 375ad26c9..aaafcf6cb 100644 --- a/include/neug/utils/id_indexer.h +++ b/include/neug/utils/id_indexer.h @@ -281,7 +281,7 @@ class LFIndexer { void reserve(size_t size) { rehash(std::max(size, num_elements_.load())); } void rehash(size_t size) { - size = std::max(size, 4ul); + size = std::max(size, static_cast(4)); keys_->resize(size); size = static_cast(std::ceil(size / id_indexer_impl::max_load_factor)); @@ -453,9 +453,19 @@ class LFIndexer { auto* indices_ptr = indices_->mutable_data(); size_t index = hash_policy_.index_for_hash(hash, num_slots_minus_one_); while (true) { +#ifdef _WIN32 + INDEX_T expected = sentinel; + if (std::atomic_compare_exchange_strong_explicit( + reinterpret_cast*>(&indices_ptr[index]), + &expected, ind, std::memory_order_seq_cst, + std::memory_order_seq_cst)) { + break; + } +#else if (__sync_bool_compare_and_swap(&indices_ptr[index], sentinel, ind)) { break; } +#endif index = (index + 1) % (num_slots_minus_one_ + 1); } } diff --git a/include/neug/utils/io/file/file_utils.h b/include/neug/utils/io/file/file_utils.h index 8a43b230b..a9f78cb83 100644 --- a/include/neug/utils/io/file/file_utils.h +++ b/include/neug/utils/io/file/file_utils.h @@ -19,6 +19,92 @@ #include #include +#ifdef _WIN32 +#include +#include +#include +#include + +inline int truncate(const char* path, int64_t length) { + int fd = _open(path, _O_WRONLY, 0); + if (fd < 0) { + return -1; + } + int ret = _chsize_s(fd, length); + _close(fd); + return ret; +} + +// Minimal POSIX mmap/munmap/msync shim for Windows. +#define PROT_READ 1 +#define PROT_WRITE 2 +#define PROT_EXEC 4 + +#define MAP_SHARED 1 +#define MAP_PRIVATE 2 +#define MAP_ANONYMOUS 4 +#define MAP_FIXED 0x10 +#define MAP_HUGETLB 0x40000 +#define MAP_FAILED ((void*)-1) + +#define MS_ASYNC 1 +#define MS_SYNC 2 +#define MS_INVALIDATE 4 + +inline void* mmap(void* addr, size_t len, int prot, int flags, int fd, + off_t offset) { + (void)addr; + DWORD pageProtect = PAGE_NOACCESS; + if (prot & PROT_WRITE) { + pageProtect = (flags & MAP_PRIVATE) ? PAGE_WRITECOPY : PAGE_READWRITE; + } else if (prot & PROT_READ) { + pageProtect = PAGE_READONLY; + } + if (flags & MAP_ANONYMOUS) { + // VirtualAlloc only accepts PAGE_READWRITE (not PAGE_WRITECOPY) + // for anonymous mappings. + void* ptr = VirtualAlloc(nullptr, len, MEM_COMMIT | MEM_RESERVE, + PAGE_READWRITE); + return ptr ? ptr : MAP_FAILED; + } + HANDLE hFile = + (fd == -1) ? INVALID_HANDLE_VALUE : (HANDLE)_get_osfhandle(fd); + DWORD sizeHigh = static_cast((static_cast(len) >> 32) & + 0xFFFFFFFFULL); + DWORD sizeLow = static_cast(len & 0xFFFFFFFFULL); + HANDLE hMap = CreateFileMapping(hFile, nullptr, pageProtect, sizeHigh, + sizeLow, nullptr); + if (!hMap) { + return MAP_FAILED; + } + DWORD access = FILE_MAP_READ; + if (prot & PROT_WRITE) { + access = (flags & MAP_PRIVATE) ? FILE_MAP_COPY : FILE_MAP_WRITE; + } + DWORD offsetHigh = static_cast( + (static_cast(offset) >> 32) & 0xFFFFFFFFULL); + DWORD offsetLow = static_cast(offset & 0xFFFFFFFFULL); + void* ptr = MapViewOfFile(hMap, access, offsetHigh, offsetLow, len); + CloseHandle(hMap); + return ptr ? ptr : MAP_FAILED; +} + +inline int munmap(void* addr, size_t len) { + (void)len; + // Try VirtualFree first (for anonymous mmap via VirtualAlloc); + // fall back to UnmapViewOfFile (for file-backed mmap). + if (VirtualFree(addr, 0, MEM_RELEASE)) { + return 0; + } + return UnmapViewOfFile(addr) ? 0 : -1; +} + +inline int msync(void* addr, size_t len, int flags) { + (void)flags; + return FlushViewOfFile(addr, len) ? 0 : -1; +} +#endif + namespace neug { namespace file_utils { diff --git a/include/neug/utils/platform.h b/include/neug/utils/platform.h new file mode 100644 index 000000000..f39d0c192 --- /dev/null +++ b/include/neug/utils/platform.h @@ -0,0 +1,46 @@ +/** Copyright 2020 Alibaba Group Holding Limited. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +/// Cross-platform hint to force a function to be inlined. +#ifdef _WIN32 +#define NEUG_ALWAYS_INLINE __forceinline +// MSVC does not support GCC's __attribute__ extension; define it away. +#define __attribute__(x) +// MSVC does not have __builtin_prefetch; use _mm_prefetch instead. +#include +#define __builtin_prefetch(ptr, rw, loc) _mm_prefetch((const char*)(ptr), (loc)) +// Windows headers define GetObject as a macro (GetObjectA/GetObjectW), +// which conflicts with rapidjson::Value::GetObject(). Undefine it. +#ifdef GetObject +#undef GetObject +#endif +// Windows headers (winnt.h) define DELETE, OPTIONAL, NONE as macros, +// which conflict with enum values in the codebase. +#ifdef DELETE +#undef DELETE +#endif +#ifdef OPTIONAL +#undef OPTIONAL +#endif +#ifdef NONE +#undef NONE +#endif +#else +#define NEUG_ALWAYS_INLINE __attribute__((always_inline)) +#endif diff --git a/include/neug/utils/property/column.h b/include/neug/utils/property/column.h index f6c7f802c..3775365c4 100644 --- a/include/neug/utils/property/column.h +++ b/include/neug/utils/property/column.h @@ -32,6 +32,7 @@ #include #include "neug/common/types/value.h" +#include "neug/utils/api.h" #include "neug/config.h" #include "neug/storages/checkpoint.h" #include "neug/storages/container/container_utils.h" @@ -51,7 +52,7 @@ namespace neug { class Table; -std::string_view truncate_utf8(std::string_view str, size_t length); +NEUG_API std::string_view truncate_utf8(std::string_view str, size_t length); class ColumnBase : public Module { public: @@ -379,7 +380,7 @@ class TypedColumn : public ColumnBase { item_out.close(); size_t avg_size = count_no_empty > 0 ? offset / count_no_empty : width_; - size_t count = std::max(size_ + (size_ + 3) / 4, 4096UL); + size_t count = std::max(size_ + (size_ + 3) / 4, static_cast(4096)); size_t truncated_size = avg_size * count + sizeof(FileHeader); int rt = truncate(data_file.c_str(), truncated_size); if (rt != 0) { @@ -670,6 +671,6 @@ class TypedRefColumn : public RefColumnBase { // Create a reference column from a ColumnBase that contains a const reference // to the actual column storage, offering a column-based store interface for // vertex properties. -std::shared_ptr CreateRefColumn(const ColumnBase& column); +NEUG_API std::shared_ptr CreateRefColumn(const ColumnBase& column); } // namespace neug diff --git a/include/neug/utils/property/table.h b/include/neug/utils/property/table.h index fa8bcd086..54b581371 100644 --- a/include/neug/utils/property/table.h +++ b/include/neug/utils/property/table.h @@ -27,13 +27,14 @@ #include "neug/storages/checkpoint_manager.h" #include "neug/storages/module/module.h" #include "neug/utils/property/column.h" +#include "neug/utils/api.h" #include "neug/utils/property/types.h" namespace neug { class TableView; -class Table { +class NEUG_API Table { public: Table(); @@ -42,6 +43,12 @@ class Table { ~Table(); + // Table holds unique_ptr; it is move-only. + Table(const Table&) = delete; + Table& operator=(const Table&) = delete; + Table(Table&&) = default; + Table& operator=(Table&&) = default; + void Init(Checkpoint& ckp, MemoryLevel level); void SetColumn(int idx, std::unique_ptr col); diff --git a/include/neug/utils/property/types.h b/include/neug/utils/property/types.h index 5cc6dd6f3..7640700fa 100644 --- a/include/neug/utils/property/types.h +++ b/include/neug/utils/property/types.h @@ -33,6 +33,8 @@ limitations under the License. #include #include +#include "neug/utils/api.h" + #include "neug/common/extra_type_info.h" #include "neug/common/types.h" #include "neug/config.h" @@ -251,7 +253,7 @@ struct Interval { inline bool operator>=(const Interval& rhs) const { return !(*this < rhs); } }; -struct Date { +struct NEUG_API Date { inline static const int32_t NORMAL_DAYS[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; inline static const int32_t CUMULATIVE_DAYS[13] = { diff --git a/include/neug/utils/result.h b/include/neug/utils/result.h index f71f36aa9..6520ea4ed 100644 --- a/include/neug/utils/result.h +++ b/include/neug/utils/result.h @@ -20,6 +20,7 @@ #include #include +#include "neug/utils/api.h" #include "neug/generated/proto/plan/error.pb.h" #include "tl/expected.hpp" @@ -27,7 +28,7 @@ namespace neug { using StatusCode = neug::interactive::Code; -class Status { +class NEUG_API Status { public: Status() noexcept; explicit Status(StatusCode error_code) noexcept; @@ -112,6 +113,15 @@ inline std::string to_string(const neug::interactive::Code& status) { std::move(_r); \ }).value() +#ifdef _WIN32 +#define GS_AUTO(var, expr) \ + auto _gs_r_##var = (expr); \ + if (!_gs_r_##var) { \ + LOG(ERROR) << "Error: " << _gs_r_##var.error().ToString(); \ + return tl::unexpected(_gs_r_##var.error()); \ + } \ + auto var = std::move(_gs_r_##var).value(); +#else #define GS_AUTO(var, expr) \ auto var = ({ \ auto&& _r = (expr); \ @@ -121,6 +131,7 @@ inline std::string to_string(const neug::interactive::Code& status) { } \ std::move(_r); \ }).value(); +#endif #define GS_ASSIGN(var, expr) \ do { \ auto&& _r = (expr); \ diff --git a/include/neug/utils/service_utils.h b/include/neug/utils/service_utils.h index 3b5c3adf4..b44339caf 100644 --- a/include/neug/utils/service_utils.h +++ b/include/neug/utils/service_utils.h @@ -19,15 +19,18 @@ // Disable class-memaccess warning to facilitate compilation with gcc>7 // https://github.com/Tencent/rapidjson/issues/1700 -#pragma GCC diagnostic push #if defined(__GNUC__) && __GNUC__ >= 8 +#pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wclass-memaccess" -#endif #include - #pragma GCC diagnostic pop +#else +#include +#endif +#ifndef _WIN32 #include +#endif #include #include @@ -42,13 +45,14 @@ // Disable class-memaccess warning to facilitate compilation with gcc>7 // https://github.com/Tencent/rapidjson/issues/1700 -#pragma GCC diagnostic push #if defined(__GNUC__) && __GNUC__ >= 8 +#pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wclass-memaccess" -#endif #include - #pragma GCC diagnostic pop +#else +#include +#endif #include #include #include @@ -61,12 +65,16 @@ namespace neug { /// Util functions. inline void blockSignal(int sig) { +#ifndef _WIN32 sigset_t set; sigemptyset(&set); sigaddset(&set, sig); if (pthread_sigmask(SIG_BLOCK, &set, NULL) != 0) { perror("pthread_sigmask"); } +#else + (void)sig; +#endif } inline int64_t GetCurrentTimeStamp() { diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8a825b860..c058ede17 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -50,7 +50,11 @@ if (APPLE) endif() if (WITH_MIMALLOC) # It must be PUBLIC, otherwise there will be performance differences. - target_link_libraries(neug PUBLIC mimalloc) + if(WIN32) + target_link_libraries(neug PUBLIC mimalloc-static) + else() + target_link_libraries(neug PUBLIC mimalloc) + endif() endif() target_include_directories(neug PUBLIC $ diff --git a/src/compiler/binder/bind/copy/bind_copy_from.cpp b/src/compiler/binder/bind/copy/bind_copy_from.cpp index 6f5916ab5..1e47f9e3c 100644 --- a/src/compiler/binder/bind/copy/bind_copy_from.cpp +++ b/src/compiler/binder/bind/copy/bind_copy_from.cpp @@ -102,6 +102,8 @@ DDLVertexInfo::DDLVertexInfo(const std::string& vertexLabelName, false /* isInternal */, false /* hasParent */, temporary); } +DDLVertexInfo::~DDLVertexInfo() = default; + std::string DDLVertexInfo::getVertexLabelName() { return nodeTableEntry->getName(); } diff --git a/src/compiler/gopt/g_alias_manager.cpp b/src/compiler/gopt/g_alias_manager.cpp index 2e164c0eb..09ae456dc 100644 --- a/src/compiler/gopt/g_alias_manager.cpp +++ b/src/compiler/gopt/g_alias_manager.cpp @@ -110,7 +110,7 @@ std::vector GAliasManager::extractSingleOpGAliasNames( case planner::LogicalOperatorType::ORDER_BY: case planner::LogicalOperatorType::LIMIT: case planner::LogicalOperatorType::SET_PROPERTY: - case planner::LogicalOperatorType::DELETE: + case planner::LogicalOperatorType::DELETE_OP: case planner::LogicalOperatorType::COPY_FROM: case planner::LogicalOperatorType::COPY_TO: case planner::LogicalOperatorType::ALTER: @@ -150,7 +150,7 @@ void GAliasManager::extractGAliasNames( case planner::LogicalOperatorType::ORDER_BY: case planner::LogicalOperatorType::LIMIT: case planner::LogicalOperatorType::SET_PROPERTY: - case planner::LogicalOperatorType::DELETE: + case planner::LogicalOperatorType::DELETE_OP: case planner::LogicalOperatorType::INSERT: { for (auto& child : op.getChildren()) { extractGAliasNames(*child, aliasNames); diff --git a/src/compiler/gopt/g_query_converter.cpp b/src/compiler/gopt/g_query_converter.cpp index f67c55b25..ea98b3785 100644 --- a/src/compiler/gopt/g_query_converter.cpp +++ b/src/compiler/gopt/g_query_converter.cpp @@ -15,6 +15,17 @@ #include "neug/compiler/gopt/g_query_converter.h" +// Windows headers define IN and OUT as empty macros (SAL annotations), +// which conflict with ::physical::EdgeExpand::IN and ::physical::EdgeExpand::OUT. +#ifdef _WIN32 +#ifdef IN +#undef IN +#endif +#ifdef OUT +#undef OUT +#endif +#endif + #include #include #include @@ -213,7 +224,7 @@ void GQueryConvertor::convertOperator(const planner::LogicalOperator& op, convertSetProperty(*set, plan); break; } - case planner::LogicalOperatorType::DELETE: { + case planner::LogicalOperatorType::DELETE_OP: { auto deleteOp = op.constPtrCast(); convertDelete(*deleteOp, plan); break; diff --git a/src/compiler/graph/on_disk_graph.cpp b/src/compiler/graph/on_disk_graph.cpp index 798269ba6..12d60b362 100644 --- a/src/compiler/graph/on_disk_graph.cpp +++ b/src/compiler/graph/on_disk_graph.cpp @@ -199,7 +199,7 @@ OnDiskGraphVertexScanState::OnDiskGraphVertexScanState( void OnDiskGraphVertexScanState::startScan(offset_t beginOffset, offset_t endOffsetExclusive) {} -bool OnDiskGraphVertexScanState::next() {} +bool OnDiskGraphVertexScanState::next() { return false; } } // namespace graph } // namespace neug diff --git a/src/compiler/optimizer/logical_operator_visitor.cpp b/src/compiler/optimizer/logical_operator_visitor.cpp index 581e6b96a..89a9db570 100644 --- a/src/compiler/optimizer/logical_operator_visitor.cpp +++ b/src/compiler/optimizer/logical_operator_visitor.cpp @@ -41,7 +41,7 @@ void LogicalOperatorVisitor::visitOperatorSwitch(LogicalOperator* op) { case LogicalOperatorType::COPY_TO: { visitCopyTo(op); } break; - case LogicalOperatorType::DELETE: { + case LogicalOperatorType::DELETE_OP: { visitDelete(op); } break; case LogicalOperatorType::DISTINCT: { @@ -134,7 +134,7 @@ LogicalOperatorVisitor::visitOperatorReplaceSwitch( case LogicalOperatorType::COPY_TO: { return visitCopyToReplace(op); } - case LogicalOperatorType::DELETE: { + case LogicalOperatorType::DELETE_OP: { return visitDeleteReplace(op); } case LogicalOperatorType::DISTINCT: { diff --git a/src/compiler/parser/transform/transform_updating_clause.cpp b/src/compiler/parser/transform/transform_updating_clause.cpp index 970b88f54..0e0d33ed5 100644 --- a/src/compiler/parser/transform/transform_updating_clause.cpp +++ b/src/compiler/parser/transform/transform_updating_clause.cpp @@ -85,7 +85,7 @@ parsed_expr_pair Transformer::transformSetItem( std::unique_ptr Transformer::transformDelete( CypherParser::OC_DeleteContext& ctx) { auto deleteClauseType = ctx.DETACH() ? common::DeleteNodeType::DETACH_DELETE - : common::DeleteNodeType::DELETE; + : common::DeleteNodeType::DELETE_NODE; auto deleteClause = std::make_unique(deleteClauseType); for (auto& expression : ctx.oC_Expression()) { deleteClause->addExpression(transformExpression(*expression)); diff --git a/src/compiler/planner/operator/logical_operator.cpp b/src/compiler/planner/operator/logical_operator.cpp index eb08d34b0..441b0c453 100644 --- a/src/compiler/planner/operator/logical_operator.cpp +++ b/src/compiler/planner/operator/logical_operator.cpp @@ -31,7 +31,7 @@ std::string LogicalOperatorUtils::logicalOperatorTypeToString( return "CREATE_TABLE"; case LogicalOperatorType::CROSS_PRODUCT: return "CROSS_PRODUCT"; - case LogicalOperatorType::DELETE: + case LogicalOperatorType::DELETE_OP: return "DELETE_NODE"; case LogicalOperatorType::DETACH_DATABASE: return "DETACH_DATABASE"; @@ -120,7 +120,7 @@ std::string LogicalOperatorUtils::logicalOperatorTypeToString( bool LogicalOperatorUtils::isUpdate(LogicalOperatorType type) { switch (type) { case LogicalOperatorType::INSERT: - case LogicalOperatorType::DELETE: + case LogicalOperatorType::DELETE_OP: case LogicalOperatorType::SET_PROPERTY: case LogicalOperatorType::MERGE: return true; diff --git a/src/compiler/planner/plan/plan_join_order.cpp b/src/compiler/planner/plan/plan_join_order.cpp index 4b1b0bca9..2600b6d9f 100644 --- a/src/compiler/planner/plan/plan_join_order.cpp +++ b/src/compiler/planner/plan/plan_join_order.cpp @@ -867,7 +867,7 @@ planner::GetVOpt getGetVOpt(common::ExtendDirection direction) { return planner::GetVOpt::OTHER; default: THROW_RUNTIME_ERROR("Unsupported extend direction for GetV: " + - static_cast(direction)); + std::to_string(static_cast(direction))); } } diff --git a/src/execution/execute/ops/batch/batch_update_utils.cc b/src/execution/execute/ops/batch/batch_update_utils.cc index 4339f2947..b95d8f15b 100644 --- a/src/execution/execute/ops/batch/batch_update_utils.cc +++ b/src/execution/execute/ops/batch/batch_update_utils.cc @@ -15,7 +15,11 @@ #include "neug/execution/execute/ops/batch/batch_update_utils.h" +#ifdef _WIN32 +#include "glob/glob/glob.hpp" +#else #include +#endif #include #include #include @@ -343,6 +347,11 @@ std::vector match_files_with_pattern( std::vector result; if (file_path.find('*') != std::string::npos || file_path.find('?') != std::string::npos) { +#ifdef _WIN32 + for (const auto& p : glob::glob(file_path)) { + result.push_back(p.string()); + } +#else glob_t glob_result; int flags = GLOB_TILDE | GLOB_MARK; int ret = glob(file_path.c_str(), flags, nullptr, &glob_result); @@ -352,6 +361,7 @@ std::vector match_files_with_pattern( } } globfree(&glob_result); +#endif } else { std::filesystem::path p = std::filesystem::absolute(file_path); if (!std::filesystem::exists(p)) { diff --git a/src/execution/expression/exprs/extract_expr.cc b/src/execution/expression/exprs/extract_expr.cc index 67ece94ee..2603785ab 100644 --- a/src/execution/expression/exprs/extract_expr.cc +++ b/src/execution/expression/exprs/extract_expr.cc @@ -15,9 +15,18 @@ #include "neug/execution/expression/exprs/extract_expr.h" +#include #include "neug/generated/proto/plan/expr.pb.h" #include "neug/utils/exception/exception.h" +#ifdef _WIN32 +// MSVC provides gmtime_s instead of gmtime_r. +// gmtime_r(timer, result) -> gmtime_s(result, timer) +static inline struct tm* gmtime_r(const time_t* timer, struct tm* result) { + return gmtime_s(result, timer) == 0 ? result : nullptr; +} +#endif + namespace neug { namespace execution { diff --git a/src/execution/extension/extension.cc b/src/execution/extension/extension.cc index b11eae693..133cbeeba 100644 --- a/src/execution/extension/extension.cc +++ b/src/execution/extension/extension.cc @@ -13,7 +13,31 @@ * limitations under the License. */ +#ifndef _WIN32 #include +#else +// Include winsock2.h before windows.h to avoid conflicting winsock.h/winsock2.h +// definitions; httplib.h also includes winsock2.h later. +#include +#include +// Windows shims for POSIX dlopen/dlsym/dlclose/dlerror. +#define RTLD_NOW 0 +#define RTLD_LOCAL 0 +static inline void* dlopen(const char* path, int) { + return LoadLibraryA(path); +} +static inline void* dlsym(void* handle, const char* name) { + return reinterpret_cast(GetProcAddress(static_cast(handle), name)); +} +static inline int dlclose(void* handle) { + return FreeLibrary(static_cast(handle)) ? 0 : 1; +} +static inline char* dlerror() { + static char buf[256]; + snprintf(buf, sizeof(buf), "Windows error code: %lu", GetLastError()); + return buf; +} +#endif #include #include @@ -450,6 +474,7 @@ Status verifyExtensionChecksum(const ExtensionRepoInfo& libRepoInfo, // libneug.so in DT_NEEDED. Re-opening with RTLD_NOLOAD | RTLD_GLOBAL // promotes the already-loaded instance without reloading it. static void ensureNeugSymbolsGlobal() { +#ifndef _WIN32 static bool promoted = false; if (promoted) return; @@ -459,6 +484,10 @@ static void ensureNeugSymbolsGlobal() { dlopen(info.dli_fname, RTLD_NOW | RTLD_NOLOAD | RTLD_GLOBAL); } promoted = true; +#else + // On Windows, symbols are exported via WINDOWS_EXPORT_ALL_SYMBOLS, + // so no promotion is needed. +#endif } Status load_extension(const std::string& extension_name) { diff --git a/src/main/file_lock.cc b/src/main/file_lock.cc index 44cc38524..08d2bba38 100644 --- a/src/main/file_lock.cc +++ b/src/main/file_lock.cc @@ -19,8 +19,14 @@ #include #include #include +#ifndef _WIN32 #include #include +#else +#include +#include +#include +#endif #include #include #include @@ -28,6 +34,13 @@ #include "neug/utils/exception/exception.h" +#ifdef _WIN32 +// Windows file locking constants (match POSIX values used in the codebase). +#define F_RDLCK 0 +#define F_WRLCK 1 +#define F_UNLCK 2 +#endif + namespace neug { // A helper class to track the databases currently locked by the current @@ -76,7 +89,11 @@ FileLock::FileLock(const std::string& data_dir) : lock_file_path_(data_dir + "/" + LOCK_FILE_NAME), fd_(-1), locked_(false) { +#ifdef _WIN32 + fd_ = _open(lock_file_path_.c_str(), _O_RDWR | _O_CREAT, _S_IREAD | _S_IWRITE); +#else fd_ = ::open(lock_file_path_.c_str(), O_RDWR | O_CREAT, 0600); +#endif if (fd_ == -1) { if (errno == EACCES) { THROW_PERMISSION_DENIED( @@ -91,7 +108,11 @@ FileLock::FileLock(const std::string& data_dir) FileLock::~FileLock() { if (fd_ != -1) { unlock(); +#ifdef _WIN32 + _close(fd_); +#else ::close(fd_); +#endif } } @@ -141,6 +162,41 @@ void FileLock::unlock() { } bool FileLock::lock(short type, bool wait, std::string& error_msg) { +#ifdef _WIN32 + // Windows file locking via LockFileEx/UnlockFileEx. + HANDLE hFile = reinterpret_cast(_get_osfhandle(fd_)); + if (hFile == INVALID_HANDLE_VALUE) { + error_msg = "Failed to get file handle for locking."; + return false; + } + DWORD flags = 0; + if (type == F_WRLCK) flags |= LOCKFILE_EXCLUSIVE_LOCK; + if (wait) flags |= LOCKFILE_FAIL_IMMEDIATELY; + if (type == F_UNLCK) { + OVERLAPPED ov = {}; + if (!UnlockFileEx(hFile, 0, 1, 0, &ov)) { + error_msg = "Failed to unlock file: " + std::to_string(GetLastError()); + return false; + } + return true; + } + OVERLAPPED ov = {}; + while (true) { + if (LockFileEx(hFile, flags, 0, 1, 0, &ov)) { + return true; + } + DWORD err = GetLastError(); + if (err == ERROR_LOCK_VIOLATION) { + error_msg = + "Lock file is already locked by another process: " + lock_file_path_ + + ", please check if another instance of the database is running."; + return false; + } else { + error_msg = "Failed to acquire lock: " + std::to_string(err); + return false; + } + } +#else struct flock fl; std::memset(&fl, 0, sizeof(fl)); fl.l_type = type; @@ -167,6 +223,7 @@ bool FileLock::lock(short type, bool wait, std::string& error_msg) { return false; } } +#endif } } // namespace neug diff --git a/src/main/neug_db.cc b/src/main/neug_db.cc index 2744d50e8..89e7b7553 100644 --- a/src/main/neug_db.cc +++ b/src/main/neug_db.cc @@ -16,7 +16,9 @@ #include "neug/main/neug_db.h" #include +#ifndef _WIN32 #include +#endif #include #include #include diff --git a/src/storages/container/anon_mmap_container.cc b/src/storages/container/anon_mmap_container.cc index 877f00169..4d0ed690c 100644 --- a/src/storages/container/anon_mmap_container.cc +++ b/src/storages/container/anon_mmap_container.cc @@ -14,23 +14,30 @@ */ #include +#ifndef _WIN32 #include #include -#include -#include -#include -#include #ifdef __linux__ #include #include #endif #include -#include #include #include -#include +#else +#include +// POSIX-to-MSVC shims +#define open _open +#define close _close +#ifndef O_RDONLY +#define O_RDONLY _O_RDONLY +#endif +#endif +#include +#include #include #include +#include #ifdef __ia64__ #define ADDR (void*) (0x8000000000000000UL) diff --git a/src/storages/container/file_mmap_container.cc b/src/storages/container/file_mmap_container.cc index 9d56b1e8a..213d8ffd8 100644 --- a/src/storages/container/file_mmap_container.cc +++ b/src/storages/container/file_mmap_container.cc @@ -14,8 +14,36 @@ */ #include +#ifndef _WIN32 #include #include +#else +#include +#include +#include +// POSIX-to-MSVC shims +#define open _open +#define close _close +#define unlink _unlink +#ifndef O_RDWR +#define O_RDWR _O_RDWR +#endif +#ifndef O_RDONLY +#define O_RDONLY _O_RDONLY +#endif +#ifndef O_WRONLY +#define O_WRONLY _O_WRONLY +#endif +#ifndef O_CREAT +#define O_CREAT _O_CREAT +#endif +#ifndef O_TRUNC +#define O_TRUNC _O_TRUNC +#endif +#ifndef EXDEV +#define EXDEV 18 +#endif +#endif #include #include #include @@ -46,13 +74,13 @@ void FilePrivateMMap::OpenAnonymous(size_t size) { } void* FilePrivateMMap::mmapImpl(const std::string& path, size_t mmap_size) { - int fd = open(path.c_str(), O_RDONLY); + int fd = _open(path.c_str(), O_RDONLY, 0); if (fd == -1) { THROW_RUNTIME_ERROR("Failed to open file: " + path); } void* mmap_data = mmap(nullptr, mmap_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); - close(fd); + _close(fd); return mmap_data; } @@ -75,15 +103,15 @@ void FileSharedMMap::Resize(size_t size) { data_ = nullptr; size_ = 0; - int fd = open(path_.c_str(), O_RDWR); + int fd = _open(path_.c_str(), O_RDWR, 0); if (fd == -1) { THROW_RUNTIME_ERROR("Failed to open file for resizing: " + path_); } - if (ftruncate(fd, real_size) == -1) { - close(fd); + if (_chsize_s(fd, real_size) != 0) { + _close(fd); THROW_RUNTIME_ERROR("Failed to resize file: " + path_); } - close(fd); + _close(fd); mmap_size_ = std::filesystem::file_size(path_); assert(mmap_size_ == real_size); // Create a new mapping with the updated file size diff --git a/src/storages/container/mmap_container.cc b/src/storages/container/mmap_container.cc index 4cbb437cb..ed2d38645 100644 --- a/src/storages/container/mmap_container.cc +++ b/src/storages/container/mmap_container.cc @@ -14,8 +14,10 @@ */ #include +#ifndef _WIN32 #include #include +#endif #include #include #include diff --git a/src/storages/csr/mutable_csr.cc b/src/storages/csr/mutable_csr.cc index 160a8047a..b1302a56a 100644 --- a/src/storages/csr/mutable_csr.cc +++ b/src/storages/csr/mutable_csr.cc @@ -20,7 +20,9 @@ #include #include +#ifndef _WIN32 #include +#endif #include #include #include diff --git a/src/storages/graph/checkpoint_manifest.cc b/src/storages/graph/checkpoint_manifest.cc index 693bfc93a..78e8ef3b2 100644 --- a/src/storages/graph/checkpoint_manifest.cc +++ b/src/storages/graph/checkpoint_manifest.cc @@ -22,6 +22,16 @@ #include #include + +#ifdef _WIN32 +// Windows headers may define GetObject as GetObjectA/GetObjectW, which +// conflicts with rapidjson::Value::GetObject(). Undefine it before including +// rapidjson headers. +#ifdef GetObject +#undef GetObject +#endif +#endif + #include #include #include diff --git a/src/storages/graph/edge_table.cc b/src/storages/graph/edge_table.cc index 9b7437f6b..5d9f99130 100644 --- a/src/storages/graph/edge_table.cc +++ b/src/storages/graph/edge_table.cc @@ -460,6 +460,20 @@ EdgeTable::EdgeTable(EdgeTable&& edge_table) capacity_ = edge_table.capacity_.load(); } +EdgeTable& EdgeTable::operator=(EdgeTable&& other) noexcept { + if (this != &other) { + ckp_ = std::move(other.ckp_); + meta_ = other.meta_; + memory_level_ = other.memory_level_; + out_csr_ = std::move(other.out_csr_); + in_csr_ = std::move(other.in_csr_); + table_ = std::move(other.table_); + table_idx_ = other.table_idx_.load(); + capacity_ = other.capacity_.load(); + } + return *this; +} + void EdgeTable::Swap(EdgeTable& edge_table) { std::swap(ckp_, edge_table.ckp_); std::swap(meta_, edge_table.meta_); @@ -626,7 +640,7 @@ void EdgeTable::EnsureCapacity(size_t capacity) { if (capacity <= capacity_.load()) { return; } - capacity = std::max(capacity, 4096UL); + capacity = std::max(capacity, static_cast(4096)); table_->resize(capacity, meta_->get_default_property_values()); capacity_.store(capacity); } diff --git a/src/storages/graph/vertex_table.cc b/src/storages/graph/vertex_table.cc index e1150fc22..742c9b1ef 100644 --- a/src/storages/graph/vertex_table.cc +++ b/src/storages/graph/vertex_table.cc @@ -219,7 +219,7 @@ size_t VertexTable::EnsureCapacity(size_t capacity) { if (capacity <= indexer_->capacity()) { return indexer_->capacity(); } - capacity = std::max(capacity, 4096UL); + capacity = std::max(capacity, static_cast(4096)); if (capacity > indexer_->capacity()) { indexer_->reserve(capacity); } diff --git a/src/storages/loader/loader_factory.cc b/src/storages/loader/loader_factory.cc index d7e72f5ad..1ed6a4c2f 100644 --- a/src/storages/loader/loader_factory.cc +++ b/src/storages/loader/loader_factory.cc @@ -15,7 +15,11 @@ #include "neug/storages/loader/loader_factory.h" -#include // for dlerror +#ifdef _WIN32 +#include +#else +#include // for dlerror +#endif #include // for LOG, Log... #include // for getenv #include // for allocator @@ -45,9 +49,15 @@ void LoaderFactory::Init() { split_string_into_vec(other_loaders, ":"); for (auto const& adaptor : adaptors) { if (!adaptor.empty()) { +#ifdef _WIN32 + if (LoadLibraryA(adaptor.c_str()) == NULL) { + LOG(WARNING) << "Failed to load io adaptors " << adaptor + << ", reason = " << GetLastError(); +#else if (dlopen(adaptor.c_str(), RTLD_GLOBAL | RTLD_NOW) == nullptr) { LOG(WARNING) << "Failed to load io adaptors " << adaptor << ", reason = " << dlerror(); +#endif } else { LOG(INFO) << "Loaded io adaptors " << adaptor; } diff --git a/src/storages/loader/loader_utils.cc b/src/storages/loader/loader_utils.cc index 4c2d0e8ba..9c0284549 100644 --- a/src/storages/loader/loader_utils.cc +++ b/src/storages/loader/loader_utils.cc @@ -18,7 +18,9 @@ #include #include #include +#ifndef _WIN32 #include +#endif #include #include @@ -776,11 +778,19 @@ static void put_null_values(const LoadingConfig& loading_config, } void printDiskRemaining(const std::string& path) { +#ifdef _WIN32 + ULARGE_INTEGER free_bytes_available; + if (GetDiskFreeSpaceExA(path.c_str(), &free_bytes_available, NULL, NULL)) { + LOG(INFO) << "Disk remaining: " + << free_bytes_available.QuadPart / 1024 / 1024 << "MB"; + } +#else struct statvfs buf; if (statvfs(path.c_str(), &buf) == 0) { LOG(INFO) << "Disk remaining: " << buf.f_bsize * buf.f_bavail / 1024 / 1024 << "MB"; } +#endif } void put_delimiter_option(const std::string& delimiter_str, diff --git a/src/storages/loader/loading_config.cc b/src/storages/loader/loading_config.cc index 777ac342e..26d53724e 100644 --- a/src/storages/loader/loading_config.cc +++ b/src/storages/loader/loading_config.cc @@ -286,7 +286,7 @@ static Status parse_vertex_files( "vertex file - [" + file_path + "] file not found..."); } std::filesystem::path path(file_path); - files[label_id].emplace_back(std::filesystem::canonical(path)); + files[label_id].emplace_back(std::filesystem::canonical(path).string()); } else { // append file_path to data_location if (!data_location.empty()) { @@ -484,9 +484,10 @@ static Status parse_edge_files( } std::filesystem::path path(file_path); VLOG(10) << "src " << src_label << " dst " << dst_label << " edge " - << edge_label << " path " << std::filesystem::canonical(path); + << edge_label << " path " + << std::filesystem::canonical(path).string(); files[std::tuple{src_label_id, dst_label_id, edge_label_id}] - .emplace_back(std::filesystem::canonical(path)); + .emplace_back(std::filesystem::canonical(path).string()); } else { // append file_path to data_location if (!data_location.empty()) { diff --git a/src/transaction/wal/local_wal_parser.cc b/src/transaction/wal/local_wal_parser.cc index c48f9fb37..96fc42647 100644 --- a/src/transaction/wal/local_wal_parser.cc +++ b/src/transaction/wal/local_wal_parser.cc @@ -16,8 +16,12 @@ #include "neug/transaction/wal/local_wal_parser.h" #include +#ifndef _WIN32 #include #include +#else +#include +#endif #include #include #include @@ -26,6 +30,7 @@ #include "neug/transaction/wal/wal.h" #include "neug/utils/exception/exception.h" +#include "neug/utils/io/file/file_utils.h" namespace neug { @@ -49,7 +54,11 @@ void LocalWalParser::open(const std::string& wal_uri) { if (file_size == 0) { continue; } +#ifdef _WIN32 + int fd = _open(path.c_str(), O_RDONLY, 0); +#else int fd = ::open(path.c_str(), O_RDONLY); +#endif if (fd == -1) { close(); THROW_IO_EXCEPTION("Failed to open wal file: " + path + ": " + @@ -58,7 +67,11 @@ void LocalWalParser::open(const std::string& wal_uri) { void* mmapped_buffer = ::mmap(NULL, file_size, PROT_READ, MAP_PRIVATE, fd, 0); if (mmapped_buffer == MAP_FAILED) { +#ifdef _WIN32 + _close(fd); +#else ::close(fd); +#endif close(); THROW_IO_EXCEPTION("Failed to mmap wal file: " + path + ": " + strerror(errno)); @@ -113,7 +126,11 @@ void LocalWalParser::close() { munmap(mmapped_ptrs_[i], mmapped_size_[i]); } for (auto fd : fds_) { +#ifdef _WIN32 + _close(fd); +#else ::close(fd); +#endif } fds_.clear(); mmapped_ptrs_.clear(); diff --git a/src/transaction/wal/local_wal_writer.cc b/src/transaction/wal/local_wal_writer.cc index 989ee66d1..1fbea0675 100644 --- a/src/transaction/wal/local_wal_writer.cc +++ b/src/transaction/wal/local_wal_writer.cc @@ -21,7 +21,11 @@ #include #include #include +#ifdef _WIN32 +#include +#else #include +#endif #include #include @@ -47,14 +51,23 @@ void LocalWalWriter::open() { if (std::filesystem::exists(path)) { continue; } +#ifdef _WIN32 + fd_ = _open(path.c_str(), O_RDWR | O_CREAT | O_TRUNC, + _S_IREAD | _S_IWRITE); +#else fd_ = ::open(path.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0644); +#endif break; } if (fd_ == -1) { THROW_IO_EXCEPTION("Failed to open wal file " + std::string(strerror(errno))); } +#ifdef _WIN32 + if (_chsize_s(fd_, TRUNC_SIZE) != 0) { +#else if (ftruncate(fd_, TRUNC_SIZE) != 0) { +#endif THROW_IO_EXCEPTION("Failed to truncate wal file " + std::string(strerror(errno))); } @@ -64,7 +77,11 @@ void LocalWalWriter::open() { void LocalWalWriter::close() { if (fd_ != -1) { +#ifdef _WIN32 + if (_close(fd_) != 0) { +#else if (::close(fd_) != 0) { +#endif THROW_IO_EXCEPTION("Failed to close file" + std::string(strerror(errno))); } fd_ = -1; @@ -80,7 +97,11 @@ bool LocalWalWriter::append(const char* data, size_t length) { size_t expected_size = file_used_ + length; if (expected_size > file_size_) { size_t new_file_size = (expected_size / TRUNC_SIZE + 1) * TRUNC_SIZE; +#ifdef _WIN32 + if (_chsize_s(fd_, new_file_size) != 0) { +#else if (ftruncate(fd_, new_file_size) != 0) { +#endif THROW_IO_EXCEPTION("Failed to truncate wal file " + std::string(strerror(errno))); } @@ -89,20 +110,29 @@ bool LocalWalWriter::append(const char* data, size_t length) { file_used_ += length; +#ifdef _WIN32 + if (static_cast(_write(fd_, data, length)) != length) { +#else if (static_cast(write(fd_, data, length)) != length) { +#endif THROW_IO_EXCEPTION("Failed to write wal file " + std::string(strerror(errno))); } #if 1 -#ifdef F_FULLFSYNC +#ifdef _WIN32 + if (_commit(fd_) != 0) { + THROW_IO_EXCEPTION("Failed to fsync wal file " + + std::string(strerror(errno))); + } +#elif defined(F_FULLFSYNC) if (fcntl(fd_, F_FULLFSYNC) != 0) { #ifdef __APPLE__ THROW_IO_EXCEPTION("Failed to fcntl sync wal file " + std::string(strerror(errno))); #else THROW_IO_EXCEPTION("Failed to fcntl sync wal file " + - std::string(strerrno(errno))); + std::string(strErrno(errno))); #endif } #else diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt index a2a926184..1aa347ece 100644 --- a/src/utils/CMakeLists.txt +++ b/src/utils/CMakeLists.txt @@ -44,12 +44,17 @@ function(compile_proto OUT_HDRS OUT_SRCS DESTDIR HDR_OUTPUT_DIR PROTO_DIR PROTO_ get_filename_component(SRC_DIR ${SRC} DIRECTORY) string(REPLACE ";" " " PROTOC_INCLUDE_ARGS_STR "${PROTOC_INCLUDE_ARGS}") - message(STATUS "Generating protobuf C++ files with command: ${PROTOC_EXE} ${PROTOC_FLAGS} ${PROTOC_INCLUDE_ARGS_STR} --cpp_out=${DESTDIR} ${PROTO_DIR}/${REL_PROTO}") + if(WIN32) + set(PROTOC_DLLEXPORT "dllexport_decl=NEUG_API:") + else() + set(PROTOC_DLLEXPORT "") + endif() + message(STATUS "Generating protobuf C++ files with command: ${PROTOC_EXE} ${PROTOC_FLAGS} ${PROTOC_INCLUDE_ARGS_STR} --cpp_out=${PROTOC_DLLEXPORT}${DESTDIR} ${PROTO_DIR}/${REL_PROTO}") add_custom_command( OUTPUT ${HDR} ${SRC} COMMAND ${CMAKE_COMMAND} -E make_directory ${HDR_DIR} ${SRC_DIR} - COMMAND ${PROTOC_EXE} ${PROTOC_FLAGS} ${PROTOC_INCLUDE_ARGS} --cpp_out=${DESTDIR} ${PROTO_DIR}/${REL_PROTO} + COMMAND ${PROTOC_EXE} ${PROTOC_FLAGS} ${PROTOC_INCLUDE_ARGS} --cpp_out=${PROTOC_DLLEXPORT}${DESTDIR} ${PROTO_DIR}/${REL_PROTO} # When HDR_OUTPUT_DIR differs from DESTDIR, optionally copy headers COMMAND ${CMAKE_COMMAND} -E copy ${HDR} ${HDR_OUTPUT_DIR}/${HDR_RELATIVE} DEPENDS ${PROTO_DIR}/${REL_PROTO} diff --git a/src/utils/io/file/file_utils.cc b/src/utils/io/file/file_utils.cc index 0215d79d4..fab9d3363 100644 --- a/src/utils/io/file/file_utils.cc +++ b/src/utils/io/file/file_utils.cc @@ -29,10 +29,71 @@ #ifdef __APPLE__ #include #endif +#ifndef _WIN32 #include #include #include #include +#else +#include +#include +#include +#include + +// POSIX-to-MSVC shims for file_utils.cc +#define open _open +#define close _close +#define read _read +#define write _write +#define lseek _lseek +#define fsync _commit +#define ftruncate _chsize_s +#define unlink _unlink +#define chmod _chmod +#define mkdir _mkdir +#define rmdir _rmdir +#define stat _stat64 +#define fstat _fstat64 +#define O_RDONLY _O_RDONLY +#define O_WRONLY _O_WRONLY +#define O_CREAT _O_CREAT +#define O_TRUNC _O_TRUNC +#define O_DIRECTORY 0 +#define S_IRUSR _S_IREAD +#define S_IWUSR _S_IWRITE +#define S_IRGRP 0 +#define S_IWGRP 0 +#define S_IROTH 0 +#define S_IWOTH 0 +#define POSIX_FADV_SEQUENTIAL 0 +#define fdopen _fdopen + +typedef long long ssize_t; + +static inline int posix_fadvise(int, off_t, off_t, int) { return 0; } + +static ssize_t pread(int fd, void* buf, size_t count, off_t offset) { + off_t old = _lseek(fd, 0, SEEK_CUR); + if (old == -1) return -1; + if (_lseek(fd, offset, SEEK_SET) == -1) return -1; + ssize_t r = _read(fd, buf, count); + int err = errno; + _lseek(fd, old, SEEK_SET); + errno = err; + return r; +} + +static ssize_t pwrite(int fd, const void* buf, size_t count, off_t offset) { + off_t old = _lseek(fd, 0, SEEK_CUR); + if (old == -1) return -1; + if (_lseek(fd, offset, SEEK_SET) == -1) return -1; + ssize_t w = _write(fd, buf, count); + int err = errno; + _lseek(fd, old, SEEK_SET); + errno = err; + return w; +} +#endif #include #include #include @@ -52,6 +113,7 @@ static void copy_metadata(const struct stat& src_stat, // Copy permissions ::chmod(dst_path.c_str(), src_stat.st_mode); +#ifndef _WIN32 // Copy access and modification times struct timespec times[2]; #ifdef __linux__ @@ -64,6 +126,10 @@ static void copy_metadata(const struct stat& src_stat, times[1].tv_nsec = 0; #endif ::utimensat(AT_FDCWD, dst_path.c_str(), times, 0); +#else + (void)src_stat; + (void)dst_path; +#endif } /** @@ -129,7 +195,12 @@ static bool try_reflink(const std::string& src_path, * `st_blocks` is always in 512-byte units regardless of FS block size. */ static bool is_sparse(const struct stat& st) { +#ifdef _WIN32 + (void)st; + return false; +#else return static_cast(st.st_blocks) * 512 < st.st_size; +#endif } /** @@ -591,6 +662,24 @@ void create_file(const std::string& path, size_t size) { } bool fsync_directory(const std::string& dir_path) { +#ifdef _WIN32 + // On Windows, open the directory with FILE_FLAG_BACKUP_SEMANTICS (required + // to obtain a handle to a directory) and call FlushFileBuffers. + HANDLE hDir = CreateFileW( + std::filesystem::path(dir_path).wstring().c_str(), + GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + nullptr, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, + nullptr); + if (hDir == INVALID_HANDLE_VALUE) { + return false; + } + bool ok = FlushFileBuffers(hDir) != 0; + CloseHandle(hDir); + return ok; +#else #ifdef O_DIRECTORY int dir_fd = ::open(dir_path.c_str(), O_RDONLY | O_DIRECTORY); #else @@ -602,6 +691,7 @@ bool fsync_directory(const std::string& dir_path) { bool ok = (::fsync(dir_fd) == 0); ::close(dir_fd); return ok; +#endif } } // namespace file_utils diff --git a/src/utils/service_utils.cc b/src/utils/service_utils.cc index 956d0153f..49c9f37cf 100644 --- a/src/utils/service_utils.cc +++ b/src/utils/service_utils.cc @@ -15,6 +15,9 @@ #include "neug/utils/service_utils.h" +#ifdef _WIN32 +#include +#else #include #if defined(__APPLE__) #include @@ -23,6 +26,7 @@ #endif #include #include +#endif #include #include @@ -30,6 +34,20 @@ namespace neug { // get current executable's directory std::string get_current_dir() { +#ifdef _WIN32 + char buf[MAX_PATH]; + DWORD len = GetModuleFileNameA(NULL, buf, MAX_PATH); + if (len == 0 || len >= MAX_PATH) { + LOG(ERROR) << "Failed to get current executable path"; + return ""; + } + std::string exe_path(buf); + size_t pos = exe_path.rfind('\\'); + if (pos == std::string::npos) { + return ""; + } + return exe_path.substr(0, pos); +#else char buf[1024]; int dirfd = open("/proc/self/", O_RDONLY | O_DIRECTORY); if (dirfd == -1) { @@ -44,6 +62,7 @@ std::string get_current_dir() { close(dirfd); std::string exe_path(buf); return exe_path.substr(0, exe_path.rfind('/')); +#endif } std::pair get_total_physical_memory_usage() { @@ -67,6 +86,17 @@ std::pair get_total_physical_memory_usage() { // Get the used physical memory int64_t used_mem = total_mem - free_mem; return std::make_pair(used_mem, total_mem); +#elif defined(_WIN32) + MEMORYSTATUSEX status; + status.dwLength = sizeof(status); + if (!GlobalMemoryStatusEx(&status)) { + LOG(ERROR) << "Failed to get memory status"; + return std::make_pair(0, 0); + } + uint64_t total_mem = status.ullTotalPhys; + uint64_t free_mem = status.ullAvailPhys; + uint64_t used_mem = total_mem - free_mem; + return std::make_pair(used_mem, total_mem); #else struct sysinfo memInfo; diff --git a/src/utils/yaml_utils.cc b/src/utils/yaml_utils.cc index 6d1a70b2e..bd3aa2d54 100644 --- a/src/utils/yaml_utils.cc +++ b/src/utils/yaml_utils.cc @@ -18,16 +18,20 @@ // Disable class-memaccess warning to facilitate compilation with gcc>7 // https://github.com/Tencent/rapidjson/issues/1700 -#pragma GCC diagnostic push #if defined(__GNUC__) && __GNUC__ >= 8 +#pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wclass-memaccess" -#endif #include #include #include #include - #pragma GCC diagnostic pop +#else +#include +#include +#include +#include +#endif #include #include #include @@ -101,7 +105,7 @@ std::vector get_yaml_files(const std::string& plugin_dir) { for (auto& entry : std::filesystem::directory_iterator(dir_path)) { if (entry.is_regular_file() && ((entry.path().extension() == ".yaml") || (entry.path().extension() == ".yml"))) { - res_yaml_files.emplace_back(entry.path()); + res_yaml_files.emplace_back(entry.path().string()); } } return res_yaml_files; diff --git a/tools/python_bind/CMakeLists.txt b/tools/python_bind/CMakeLists.txt index cffea74dd..4af51bd05 100644 --- a/tools/python_bind/CMakeLists.txt +++ b/tools/python_bind/CMakeLists.txt @@ -35,7 +35,15 @@ set_target_properties(neug_py_bind PROPERTIES # protobuf is linked privately because pybind sources use protobuf-generated # headers whose inline/template code references absl/protobuf symbols; # libneug.dylib hides those symbols to avoid conflicts with pyarrow. -target_link_libraries(neug_py_bind PRIVATE neug ${GLOG_LIBRARIES} ${Protobuf_LIBRARIES}) +# On Windows, also link neug_proto OBJECT files directly because protobuf's +# _default_instance_ variables are at namespace scope and not exported +# from neug.dll (dllexport_decl only marks class declarations, not these +# file-scope static variables). +if(WIN32) + target_link_libraries(neug_py_bind PRIVATE neug ${GLOG_LIBRARIES} ${Protobuf_LIBRARIES} $) +else() + target_link_libraries(neug_py_bind PRIVATE neug ${GLOG_LIBRARIES} ${Protobuf_LIBRARIES}) +endif() # Suppress VLA warnings from brpc third-party headers (bvar/detail/percentile.h # uses DEFINE_SMALL_ARRAY which is a GCC VLA extension unsupported by Clang). @@ -45,4 +53,13 @@ endif() # Hide protobuf/abseil/arrow symbols from neug_py_bind.so as well, so they # don't leak into the Python process and conflict with pyarrow's copies. -neug_apply_symbol_visibility(neug_py_bind) \ No newline at end of file +neug_apply_symbol_visibility(neug_py_bind) + +# On Windows, copy neug.dll next to neug_py_bind.pyd so the loader finds it. +# RPATH ($ORIGIN/@loader_path) is a POSIX-only mechanism; Windows relies on +# DLL search order (same-dir-first). +if(WIN32) + add_custom_command(TARGET neug_py_bind POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + $ $) +endif() \ No newline at end of file diff --git a/tools/python_bind/neug/__init__.py b/tools/python_bind/neug/__init__.py index 461185486..2334a6c52 100644 --- a/tools/python_bind/neug/__init__.py +++ b/tools/python_bind/neug/__init__.py @@ -165,13 +165,15 @@ def get_build_lib_dir() -> str: build_dir = None else: # Try multiple build directory patterns in order of preference + # Use platform.machine() instead of os.uname().machine for Windows compat + _machine = platform.machine() build_dir_patterns = [ # Modern pattern with cpython tag (Python 3.8+) - f"lib.{os_name}-{os.uname().machine}-cpython-{sys.version_info.major}{sys.version_info.minor}", + f"lib.{os_name}-{_machine}-cpython-{sys.version_info.major}{sys.version_info.minor}", # Legacy pattern with version - f"lib.{os_name}-{os.uname().machine}-{sys.version_info.major}.{sys.version_info.minor}", + f"lib.{os_name}-{_machine}-{sys.version_info.major}.{sys.version_info.minor}", # Very old pattern without version suffix - f"lib.{os_name}-{os.uname().machine}", + f"lib.{os_name}-{_machine}", ] build_dir = None @@ -196,14 +198,18 @@ def _find_neug_py_bind_dir(): """ cur_dir = os.path.dirname(os.path.abspath(__file__)) - if glob.glob(os.path.join(cur_dir, "neug_py_bind*.so")): + if glob.glob(os.path.join(cur_dir, "neug_py_bind*.so")) or glob.glob( + os.path.join(cur_dir, "neug_py_bind*.pyd") + ): return cur_dir # Assume layout: /tools/python_bind/neug/__init__.py → repo is 3 up. repo_root = os.path.abspath(os.path.join(cur_dir, "..", "..", "..")) root_build = os.environ.get("NEUG_BUILD_DIR", os.path.join(repo_root, "build")) candidate = os.path.join(root_build, "tools", "python_bind") - if glob.glob(os.path.join(candidate, "neug_py_bind*.so")): + if glob.glob(os.path.join(candidate, "neug_py_bind*.so")) or glob.glob( + os.path.join(candidate, "neug_py_bind*.pyd") + ): return candidate return get_build_lib_dir() diff --git a/tools/python_bind/neug/database.py b/tools/python_bind/neug/database.py index 099a8e043..fb2b33b09 100644 --- a/tools/python_bind/neug/database.py +++ b/tools/python_bind/neug/database.py @@ -124,7 +124,12 @@ def __init__( self._db_path = None self._connections = [] self._async_connections = [] - self._illegal_chars = ["?", "*", '"', "<", ">", "|", ":", "\\"] + import sys as _sys + # On Windows, ':' (drive letter) and '\\' (path separator) are valid. + if _sys.platform == "win32": + self._illegal_chars = ["?", '*', '"', '<', '>', '|'] + else: + self._illegal_chars = ["?", '*', '"', '<', '>', '|', ':', '\\'] self._pure_memory_path = [":memory", ":memory:"] if isinstance(db_path, str): if ( From ec572843ba264a50c5dc3825e9d7d9d012c9b9af Mon Sep 17 00:00:00 2001 From: "nengli.ln" Date: Mon, 27 Jul 2026 15:12:03 +0800 Subject: [PATCH 02/19] update cypher parser --- .../antlr4_cypher/include/cypher_lexer.h | 21 +++++++++++++++++++ .../antlr4_cypher/include/cypher_parser.h | 21 +++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/third_party/antlr4_cypher/include/cypher_lexer.h b/third_party/antlr4_cypher/include/cypher_lexer.h index ea08a6ee1..373797498 100644 --- a/third_party/antlr4_cypher/include/cypher_lexer.h +++ b/third_party/antlr4_cypher/include/cypher_lexer.h @@ -6,6 +6,27 @@ #include "antlr4-runtime.h" +// Windows headers define DELETE, NONE, OPTIONAL, IN, OUT as macros which conflict +// with enum values below. Undefine them before the enum. +#ifdef DELETE +#undef DELETE +#endif +#ifdef NONE +#undef NONE +#endif +#ifdef OPTIONAL +#undef OPTIONAL +#endif +#ifdef ERROR +#undef ERROR +#endif +#ifdef IN +#undef IN +#endif +#ifdef OUT +#undef OUT +#endif + diff --git a/third_party/antlr4_cypher/include/cypher_parser.h b/third_party/antlr4_cypher/include/cypher_parser.h index 33ae52936..d826c8e4a 100644 --- a/third_party/antlr4_cypher/include/cypher_parser.h +++ b/third_party/antlr4_cypher/include/cypher_parser.h @@ -6,6 +6,27 @@ #include "antlr4-runtime.h" +// Windows headers define DELETE, NONE, OPTIONAL, IN, OUT as macros which conflict +// with enum values below. Undefine them before the enum. +#ifdef DELETE +#undef DELETE +#endif +#ifdef NONE +#undef NONE +#endif +#ifdef OPTIONAL +#undef OPTIONAL +#endif +#ifdef ERROR +#undef ERROR +#endif +#ifdef IN +#undef IN +#endif +#ifdef OUT +#undef OUT +#endif + From 11cc710dddf43484e75c3a967425a955632a38a3 Mon Sep 17 00:00:00 2001 From: "nengli.ln" Date: Mon, 27 Jul 2026 15:21:41 +0800 Subject: [PATCH 03/19] add build script --- scripts/build_windows.bat | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 scripts/build_windows.bat diff --git a/scripts/build_windows.bat b/scripts/build_windows.bat new file mode 100644 index 000000000..b8b5628f4 --- /dev/null +++ b/scripts/build_windows.bat @@ -0,0 +1,9 @@ +@echo off +call "C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Auxiliary\Build\vcvarsall.bat" x64 + +REM cd to project root (parent of scripts/) +cd /d "%~dp0.." + +set PYTHON_EXE=C:\Users\neng\AppData\Local\Programs\Python\Python311\python.exe +cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=C:\Users\neng\vcpkg\scripts\buildsystems\vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows-static-md -DOPENSSL_ROOT_DIR=C:\Users\neng\vcpkg\installed\x64-windows-static-md -DBUILD_PYTHON=ON -DPython_EXECUTABLE=%PYTHON_EXE% -DPYTHON_EXECUTABLE=%PYTHON_EXE% . +cmake --build build -j 8 --target neug neug_py_bind From 73f79370ca18a5b1e3de0c39113f0bb826a7d44d Mon Sep 17 00:00:00 2001 From: "nengli.ln" Date: Tue, 28 Jul 2026 10:24:29 +0800 Subject: [PATCH 04/19] fix module --- include/neug/storages/module/module_factory.h | 17 +++++++++++++++++ src/main/neug_db.cc | 18 ++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/include/neug/storages/module/module_factory.h b/include/neug/storages/module/module_factory.h index 403823cdb..41ec9dc07 100644 --- a/include/neug/storages/module/module_factory.h +++ b/include/neug/storages/module/module_factory.h @@ -69,11 +69,22 @@ class ModuleFactory { * * Usage: Place REGISTER_MODULE(MyModule) in the source file after class * definition. + * + * On GCC/Clang we use __attribute__((constructor)) which runs the function + * before main(). On MSVC this attribute is not supported, so we use a static + * variable whose dynamic initializer performs the registration. The comma- + * expression returns 0 so the variable can be a plain int. */ +#ifdef _WIN32 +#define NEUG_REGISTER_MODULE(Class) \ + static int _register_##Class = \ + (ModuleFactory::instance().Register(), 0); +#else #define NEUG_REGISTER_MODULE(Class) \ __attribute__((constructor)) static void _register_##Class() { \ ModuleFactory::instance().Register(); \ } +#endif /** * @brief Macro for registering template module instantiations. @@ -82,11 +93,17 @@ class ModuleFactory { * Works with types containing '::' (e.g. std::string_view) by using * __COUNTER__ for a stable, type-name-independent function identifier. */ +#ifdef _WIN32 +#define NEUG_REGISTER_TEMPLATE_MODULE_IMPL(TemplateClass, T, Counter) \ + static int _register_##TemplateClass##_##Counter = \ + (ModuleFactory::instance().Register>(), 0); +#else #define NEUG_REGISTER_TEMPLATE_MODULE_IMPL(TemplateClass, T, Counter) \ __attribute__( \ (constructor)) static void _register_##TemplateClass##_##Counter() { \ ModuleFactory::instance().Register>(); \ } +#endif // Indirection layer: N is not adjacent to ##, so __COUNTER__ is fully expanded // here before being passed to IMPL (where it would otherwise be pasted raw). diff --git a/src/main/neug_db.cc b/src/main/neug_db.cc index 89e7b7553..f2046a4b5 100644 --- a/src/main/neug_db.cc +++ b/src/main/neug_db.cc @@ -18,6 +18,8 @@ #include #ifndef _WIN32 #include +#else +#include #endif #include #include @@ -234,7 +236,23 @@ void NeugDB::preprocessConfig() { if (prefix_env) { db_dir_prefix = std::string(prefix_env); } else { +#ifdef _WIN32 + // On Windows, use the system temp directory (e.g. C:\Users\\AppData\Local\Temp) + char tmp_path[MAX_PATH]; + DWORD len = GetTempPathA(MAX_PATH, tmp_path); + if (len > 0) { + // Remove trailing backslash + if (tmp_path[len - 1] == '\\' || tmp_path[len - 1] == '/') { + tmp_path[len - 1] = '\0'; + len--; + } + db_dir_prefix = std::string(tmp_path); + } else { + db_dir_prefix = "."; + } +#else db_dir_prefix = "/tmp"; +#endif } std::stringstream ss; auto now = std::chrono::system_clock::now(); From d2cf71816c88bed7b52aeeef58732443f623333c Mon Sep 17 00:00:00 2001 From: "nengli.ln" Date: Tue, 28 Jul 2026 10:56:55 +0800 Subject: [PATCH 05/19] format --- include/neug/common/columns/vertex_columns.h | 17 +++------ include/neug/common/types.h | 2 +- .../compiler/binder/copy/bound_copy_from.h | 2 +- .../common/operators/retrieve/edge_expand.h | 2 +- include/neug/storages/checkpoint_manager.h | 2 +- include/neug/storages/csr/csr_view.h | 4 +- include/neug/storages/graph/edge_table.h | 2 +- include/neug/storages/graph/property_graph.h | 2 +- include/neug/storages/graph/schema.h | 2 +- include/neug/storages/graph/vertex_table.h | 2 +- .../neug/storages/graph/vertex_timestamp.h | 2 +- include/neug/storages/module/module_factory.h | 8 ++-- include/neug/utils/bitset.h | 12 ++++-- include/neug/utils/io/file/file_utils.h | 38 +++++++++---------- include/neug/utils/platform.h | 3 +- include/neug/utils/property/column.h | 5 ++- include/neug/utils/property/table.h | 2 +- include/neug/utils/result.h | 14 +++---- include/neug/utils/service_utils.h | 2 +- tools/python_bind/neug/database.py | 5 ++- 20 files changed, 64 insertions(+), 64 deletions(-) diff --git a/include/neug/common/columns/vertex_columns.h b/include/neug/common/columns/vertex_columns.h index 18bd906d3..db1070a7f 100644 --- a/include/neug/common/columns/vertex_columns.h +++ b/include/neug/common/columns/vertex_columns.h @@ -97,8 +97,7 @@ class SLVertexColumn : public IVertexColumn { std::shared_ptr optional_shuffle( const sel_vec_t& offset) const override; - NEUG_ALWAYS_INLINE VertexRecord get_vertex( - size_t idx) const override { + NEUG_ALWAYS_INLINE VertexRecord get_vertex(size_t idx) const override { return {label_, vertices_[idx]}; } @@ -182,8 +181,7 @@ class MSVertexColumn : public IVertexColumn { std::shared_ptr optional_shuffle( const sel_vec_t& offsets) const override; - NEUG_ALWAYS_INLINE VertexRecord get_vertex( - size_t idx) const override { + NEUG_ALWAYS_INLINE VertexRecord get_vertex(size_t idx) const override { for (auto& pair : vertices_) { if (idx < pair.second.size()) { return {pair.first, pair.second[idx]}; @@ -195,9 +193,7 @@ class MSVertexColumn : public IVertexColumn { std::numeric_limits::max()}; } - NEUG_ALWAYS_INLINE bool is_optional() const override { - return is_optional_; - } + NEUG_ALWAYS_INLINE bool is_optional() const override { return is_optional_; } NEUG_ALWAYS_INLINE bool has_value(size_t idx) const override { auto v = get_vertex(idx); @@ -217,16 +213,13 @@ class MSVertexColumn : public IVertexColumn { std::set get_labels_set() const override { return labels_; } - NEUG_ALWAYS_INLINE size_t seg_num() const { - return vertices_.size(); - } + NEUG_ALWAYS_INLINE size_t seg_num() const { return vertices_.size(); } NEUG_ALWAYS_INLINE label_t seg_label(size_t seg_id) const { return vertices_[seg_id].first; } - NEUG_ALWAYS_INLINE const vector_t& seg_vertices( - size_t seg_id) const { + NEUG_ALWAYS_INLINE const vector_t& seg_vertices(size_t seg_id) const { return vertices_[seg_id].second; } diff --git a/include/neug/common/types.h b/include/neug/common/types.h index bdf93b9b7..9065f426b 100644 --- a/include/neug/common/types.h +++ b/include/neug/common/types.h @@ -24,11 +24,11 @@ #include #include -#include "neug/utils/api.h" #include #include #include #include +#include "neug/utils/api.h" namespace common { class DataType; diff --git a/include/neug/compiler/binder/copy/bound_copy_from.h b/include/neug/compiler/binder/copy/bound_copy_from.h index cbdd9776f..3d6e734be 100644 --- a/include/neug/compiler/binder/copy/bound_copy_from.h +++ b/include/neug/compiler/binder/copy/bound_copy_from.h @@ -28,8 +28,8 @@ #include "neug/compiler/binder/ddl/bound_create_table_info.h" #include "neug/compiler/binder/expression/expression.h" #include "neug/compiler/binder/expression_binder.h" -#include "neug/compiler/catalog/catalog_entry/table_catalog_entry.h" #include "neug/compiler/catalog/catalog_entry/node_table_catalog_entry.h" +#include "neug/compiler/catalog/catalog_entry/table_catalog_entry.h" #include "neug/compiler/common/enums/column_evaluate_type.h" #include "neug/compiler/common/types/types.h" #include "neug/compiler/gopt/g_rel_table_entry.h" diff --git a/include/neug/execution/common/operators/retrieve/edge_expand.h b/include/neug/execution/common/operators/retrieve/edge_expand.h index e886a44cc..f69a3bed8 100644 --- a/include/neug/execution/common/operators/retrieve/edge_expand.h +++ b/include/neug/execution/common/operators/retrieve/edge_expand.h @@ -16,11 +16,11 @@ #include "neug/common/types/graph_types.h" #include "neug/execution/common/context_chunk.h" -#include "neug/utils/platform.h" #include "neug/execution/common/operators/retrieve/edge_expand_impl.h" #include "neug/execution/common/params_map.h" #include "neug/execution/expression/special_predicates.h" #include "neug/execution/utils/params.h" +#include "neug/utils/platform.h" #include "neug/utils/result.h" namespace neug { diff --git a/include/neug/storages/checkpoint_manager.h b/include/neug/storages/checkpoint_manager.h index 286bc3352..05a94af34 100644 --- a/include/neug/storages/checkpoint_manager.h +++ b/include/neug/storages/checkpoint_manager.h @@ -19,8 +19,8 @@ #include #include -#include "neug/utils/api.h" #include "neug/storages/checkpoint.h" +#include "neug/utils/api.h" namespace neug { diff --git a/include/neug/storages/csr/csr_view.h b/include/neug/storages/csr/csr_view.h index 5f18a016e..e5f386685 100644 --- a/include/neug/storages/csr/csr_view.h +++ b/include/neug/storages/csr/csr_view.h @@ -17,9 +17,9 @@ #include #include "neug/common/types/value.h" -#include "neug/utils/platform.h" #include "neug/storages/csr/nbr.h" #include "neug/storages/csr/prefetch_utils.h" +#include "neug/utils/platform.h" #include "neug/utils/property/column.h" #include "neug/utils/property/types.h" @@ -698,7 +698,7 @@ struct CsrView { private: NEUG_ALWAYS_INLINE static void prefetch_read(const void* ptr, - uint8_t locality) { + uint8_t locality) { switch (locality) { case 0: __builtin_prefetch(ptr, 0, 0); diff --git a/include/neug/storages/graph/edge_table.h b/include/neug/storages/graph/edge_table.h index d3250ca1e..1819b792f 100644 --- a/include/neug/storages/graph/edge_table.h +++ b/include/neug/storages/graph/edge_table.h @@ -28,9 +28,9 @@ #include "neug/storages/checkpoint_manager.h" #include "neug/storages/csr/csr_base.h" #include "neug/storages/csr/csr_view.h" -#include "neug/utils/api.h" #include "neug/storages/graph/schema.h" #include "neug/storages/module/module.h" +#include "neug/utils/api.h" #include "neug/utils/indexers.h" #include "neug/utils/property/table.h" #include "neug/utils/property/types.h" diff --git a/include/neug/storages/graph/property_graph.h b/include/neug/storages/graph/property_graph.h index 632dd9462..5dbed6614 100644 --- a/include/neug/storages/graph/property_graph.h +++ b/include/neug/storages/graph/property_graph.h @@ -29,7 +29,6 @@ #include "neug/common/types/value.h" #include "neug/storages/allocators.h" -#include "neug/utils/api.h" #include "neug/storages/checkpoint.h" #include "neug/storages/checkpoint_manager.h" #include "neug/storages/csr/csr_view.h" @@ -37,6 +36,7 @@ #include "neug/storages/graph/operation_params.h" #include "neug/storages/graph/schema.h" #include "neug/storages/graph/vertex_table.h" +#include "neug/utils/api.h" #include "neug/utils/exception/exception.h" #include "neug/utils/property/types.h" #include "neug/utils/result.h" diff --git a/include/neug/storages/graph/schema.h b/include/neug/storages/graph/schema.h index 41d7ad038..a92afac4c 100644 --- a/include/neug/storages/graph/schema.h +++ b/include/neug/storages/graph/schema.h @@ -26,8 +26,8 @@ #include #include #include -#include "neug/utils/api.h" #include "neug/common/types/value.h" +#include "neug/utils/api.h" #include "neug/utils/bitset.h" #include "neug/utils/property/default_value.h" #include "neug/utils/property/property_definition.h" diff --git a/include/neug/storages/graph/vertex_table.h b/include/neug/storages/graph/vertex_table.h index 2605d32a2..f4e4ac59a 100644 --- a/include/neug/storages/graph/vertex_table.h +++ b/include/neug/storages/graph/vertex_table.h @@ -20,9 +20,9 @@ #include "neug/storages/graph/vertex_timestamp.h" #include "neug/storages/loader/loader_utils.h" #include "neug/storages/module/module.h" +#include "neug/utils/api.h" #include "neug/utils/indexers.h" #include "neug/utils/property/table.h" -#include "neug/utils/api.h" namespace neug { diff --git a/include/neug/storages/graph/vertex_timestamp.h b/include/neug/storages/graph/vertex_timestamp.h index 5dad1bef0..aff40d7fb 100644 --- a/include/neug/storages/graph/vertex_timestamp.h +++ b/include/neug/storages/graph/vertex_timestamp.h @@ -21,9 +21,9 @@ #include #include "neug/storages/module/module.h" +#include "neug/utils/api.h" #include "neug/utils/likely.h" #include "neug/utils/property/types.h" -#include "neug/utils/api.h" namespace neug { diff --git a/include/neug/storages/module/module_factory.h b/include/neug/storages/module/module_factory.h index 41ec9dc07..d70d8bc7b 100644 --- a/include/neug/storages/module/module_factory.h +++ b/include/neug/storages/module/module_factory.h @@ -76,8 +76,8 @@ class ModuleFactory { * expression returns 0 so the variable can be a plain int. */ #ifdef _WIN32 -#define NEUG_REGISTER_MODULE(Class) \ - static int _register_##Class = \ +#define NEUG_REGISTER_MODULE(Class) \ + static int _register_##Class = \ (ModuleFactory::instance().Register(), 0); #else #define NEUG_REGISTER_MODULE(Class) \ @@ -94,8 +94,8 @@ class ModuleFactory { * __COUNTER__ for a stable, type-name-independent function identifier. */ #ifdef _WIN32 -#define NEUG_REGISTER_TEMPLATE_MODULE_IMPL(TemplateClass, T, Counter) \ - static int _register_##TemplateClass##_##Counter = \ +#define NEUG_REGISTER_TEMPLATE_MODULE_IMPL(TemplateClass, T, Counter) \ + static int _register_##TemplateClass##_##Counter = \ (ModuleFactory::instance().Register>(), 0); #else #define NEUG_REGISTER_TEMPLATE_MODULE_IMPL(TemplateClass, T, Counter) \ diff --git a/include/neug/utils/bitset.h b/include/neug/utils/bitset.h index 79b0049d6..48da33099 100644 --- a/include/neug/utils/bitset.h +++ b/include/neug/utils/bitset.h @@ -64,7 +64,8 @@ inline void AlignedFree(void* ptr) { inline uint64_t AtomicFetchOr(uint64_t* ptr, uint64_t value) { #ifdef _WIN32 - return static_cast(_InterlockedOr64(reinterpret_cast(ptr), static_cast<__int64>(value))); + return static_cast(_InterlockedOr64( + reinterpret_cast(ptr), static_cast<__int64>(value))); #else return __sync_fetch_and_or(ptr, value); #endif @@ -72,7 +73,8 @@ inline uint64_t AtomicFetchOr(uint64_t* ptr, uint64_t value) { inline uint64_t AtomicFetchAnd(uint64_t* ptr, uint64_t value) { #ifdef _WIN32 - return static_cast(_InterlockedAnd64(reinterpret_cast(ptr), static_cast<__int64>(value))); + return static_cast(_InterlockedAnd64( + reinterpret_cast(ptr), static_cast<__int64>(value))); #else return __sync_fetch_and_and(ptr, value); #endif @@ -110,7 +112,8 @@ class Bitset { assert(kAlignment <= capacity_in_words_ * sizeof(uint64_t)); auto alloc_size = ROUND_UP_TO_ALIGNMENT( capacity_in_words_ * sizeof(uint64_t), kAlignment); - data_ = static_cast(detail::AlignedAlloc(kAlignment, alloc_size)); + data_ = + static_cast(detail::AlignedAlloc(kAlignment, alloc_size)); memcpy(data_, other.data_, capacity_in_words_ * sizeof(uint64_t)); } } @@ -154,7 +157,8 @@ class Bitset { assert(kAlignment <= capacity_in_words_ * sizeof(uint64_t)); auto alloc_size = ROUND_UP_TO_ALIGNMENT( capacity_in_words_ * sizeof(uint64_t), kAlignment); - data_ = static_cast(detail::AlignedAlloc(kAlignment, alloc_size)); + data_ = + static_cast(detail::AlignedAlloc(kAlignment, alloc_size)); memcpy(data_, other.data_, capacity_in_words_ * sizeof(uint64_t)); } return *this; diff --git a/include/neug/utils/io/file/file_utils.h b/include/neug/utils/io/file/file_utils.h index a9f78cb83..de5e40716 100644 --- a/include/neug/utils/io/file/file_utils.h +++ b/include/neug/utils/io/file/file_utils.h @@ -20,19 +20,19 @@ #include #ifdef _WIN32 -#include #include #include #include +#include inline int truncate(const char* path, int64_t length) { - int fd = _open(path, _O_WRONLY, 0); - if (fd < 0) { - return -1; - } - int ret = _chsize_s(fd, length); - _close(fd); - return ret; + int fd = _open(path, _O_WRONLY, 0); + if (fd < 0) { + return -1; + } + int ret = _chsize_s(fd, length); + _close(fd); + return ret; } // Minimal POSIX mmap/munmap/msync shim for Windows. @@ -45,7 +45,7 @@ inline int truncate(const char* path, int64_t length) { #define MAP_ANONYMOUS 4 #define MAP_FIXED 0x10 #define MAP_HUGETLB 0x40000 -#define MAP_FAILED ((void*)-1) +#define MAP_FAILED ((void*) -1) #define MS_ASYNC 1 #define MS_SYNC 2 @@ -53,7 +53,7 @@ inline int truncate(const char* path, int64_t length) { inline void* mmap(void* addr, size_t len, int prot, int flags, int fd, off_t offset) { - (void)addr; + (void) addr; DWORD pageProtect = PAGE_NOACCESS; if (prot & PROT_WRITE) { pageProtect = (flags & MAP_PRIVATE) ? PAGE_WRITECOPY : PAGE_READWRITE; @@ -63,14 +63,14 @@ inline void* mmap(void* addr, size_t len, int prot, int flags, int fd, if (flags & MAP_ANONYMOUS) { // VirtualAlloc only accepts PAGE_READWRITE (not PAGE_WRITECOPY) // for anonymous mappings. - void* ptr = VirtualAlloc(nullptr, len, MEM_COMMIT | MEM_RESERVE, - PAGE_READWRITE); + void* ptr = + VirtualAlloc(nullptr, len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); return ptr ? ptr : MAP_FAILED; } HANDLE hFile = - (fd == -1) ? INVALID_HANDLE_VALUE : (HANDLE)_get_osfhandle(fd); - DWORD sizeHigh = static_cast((static_cast(len) >> 32) & - 0xFFFFFFFFULL); + (fd == -1) ? INVALID_HANDLE_VALUE : (HANDLE) _get_osfhandle(fd); + DWORD sizeHigh = + static_cast((static_cast(len) >> 32) & 0xFFFFFFFFULL); DWORD sizeLow = static_cast(len & 0xFFFFFFFFULL); HANDLE hMap = CreateFileMapping(hFile, nullptr, pageProtect, sizeHigh, sizeLow, nullptr); @@ -81,8 +81,8 @@ inline void* mmap(void* addr, size_t len, int prot, int flags, int fd, if (prot & PROT_WRITE) { access = (flags & MAP_PRIVATE) ? FILE_MAP_COPY : FILE_MAP_WRITE; } - DWORD offsetHigh = static_cast( - (static_cast(offset) >> 32) & 0xFFFFFFFFULL); + DWORD offsetHigh = + static_cast((static_cast(offset) >> 32) & 0xFFFFFFFFULL); DWORD offsetLow = static_cast(offset & 0xFFFFFFFFULL); void* ptr = MapViewOfFile(hMap, access, offsetHigh, offsetLow, len); CloseHandle(hMap); @@ -90,7 +90,7 @@ inline void* mmap(void* addr, size_t len, int prot, int flags, int fd, } inline int munmap(void* addr, size_t len) { - (void)len; + (void) len; // Try VirtualFree first (for anonymous mmap via VirtualAlloc); // fall back to UnmapViewOfFile (for file-backed mmap). if (VirtualFree(addr, 0, MEM_RELEASE)) { @@ -100,7 +100,7 @@ inline int munmap(void* addr, size_t len) { } inline int msync(void* addr, size_t len, int flags) { - (void)flags; + (void) flags; return FlushViewOfFile(addr, len) ? 0 : -1; } #endif diff --git a/include/neug/utils/platform.h b/include/neug/utils/platform.h index f39d0c192..4db8378e9 100644 --- a/include/neug/utils/platform.h +++ b/include/neug/utils/platform.h @@ -24,7 +24,8 @@ #define __attribute__(x) // MSVC does not have __builtin_prefetch; use _mm_prefetch instead. #include -#define __builtin_prefetch(ptr, rw, loc) _mm_prefetch((const char*)(ptr), (loc)) +#define __builtin_prefetch(ptr, rw, loc) \ + _mm_prefetch((const char*) (ptr), (loc)) // Windows headers define GetObject as a macro (GetObjectA/GetObjectW), // which conflicts with rapidjson::Value::GetObject(). Undefine it. #ifdef GetObject diff --git a/include/neug/utils/property/column.h b/include/neug/utils/property/column.h index 3775365c4..fb089dcb7 100644 --- a/include/neug/utils/property/column.h +++ b/include/neug/utils/property/column.h @@ -32,7 +32,6 @@ #include #include "neug/common/types/value.h" -#include "neug/utils/api.h" #include "neug/config.h" #include "neug/storages/checkpoint.h" #include "neug/storages/container/container_utils.h" @@ -41,6 +40,7 @@ #include "neug/storages/container/mmap_container.h" #include "neug/storages/module/module.h" #include "neug/storages/module/type_name.h" +#include "neug/utils/api.h" #include "neug/utils/exception/exception.h" #include "neug/utils/io/file/file_utils.h" #include "neug/utils/likely.h" @@ -671,6 +671,7 @@ class TypedRefColumn : public RefColumnBase { // Create a reference column from a ColumnBase that contains a const reference // to the actual column storage, offering a column-based store interface for // vertex properties. -NEUG_API std::shared_ptr CreateRefColumn(const ColumnBase& column); +NEUG_API std::shared_ptr CreateRefColumn( + const ColumnBase& column); } // namespace neug diff --git a/include/neug/utils/property/table.h b/include/neug/utils/property/table.h index 54b581371..cad37906b 100644 --- a/include/neug/utils/property/table.h +++ b/include/neug/utils/property/table.h @@ -26,8 +26,8 @@ #include "neug/storages/checkpoint.h" #include "neug/storages/checkpoint_manager.h" #include "neug/storages/module/module.h" -#include "neug/utils/property/column.h" #include "neug/utils/api.h" +#include "neug/utils/property/column.h" #include "neug/utils/property/types.h" namespace neug { diff --git a/include/neug/utils/result.h b/include/neug/utils/result.h index 6520ea4ed..cfc64ec7d 100644 --- a/include/neug/utils/result.h +++ b/include/neug/utils/result.h @@ -20,8 +20,8 @@ #include #include -#include "neug/utils/api.h" #include "neug/generated/proto/plan/error.pb.h" +#include "neug/utils/api.h" #include "tl/expected.hpp" namespace neug { @@ -114,12 +114,12 @@ inline std::string to_string(const neug::interactive::Code& status) { }).value() #ifdef _WIN32 -#define GS_AUTO(var, expr) \ - auto _gs_r_##var = (expr); \ - if (!_gs_r_##var) { \ - LOG(ERROR) << "Error: " << _gs_r_##var.error().ToString(); \ - return tl::unexpected(_gs_r_##var.error()); \ - } \ +#define GS_AUTO(var, expr) \ + auto _gs_r_##var = (expr); \ + if (!_gs_r_##var) { \ + LOG(ERROR) << "Error: " << _gs_r_##var.error().ToString(); \ + return tl::unexpected(_gs_r_##var.error()); \ + } \ auto var = std::move(_gs_r_##var).value(); #else #define GS_AUTO(var, expr) \ diff --git a/include/neug/utils/service_utils.h b/include/neug/utils/service_utils.h index b44339caf..4757305e0 100644 --- a/include/neug/utils/service_utils.h +++ b/include/neug/utils/service_utils.h @@ -73,7 +73,7 @@ inline void blockSignal(int sig) { perror("pthread_sigmask"); } #else - (void)sig; + (void) sig; #endif } diff --git a/tools/python_bind/neug/database.py b/tools/python_bind/neug/database.py index fb2b33b09..65962216c 100644 --- a/tools/python_bind/neug/database.py +++ b/tools/python_bind/neug/database.py @@ -125,11 +125,12 @@ def __init__( self._connections = [] self._async_connections = [] import sys as _sys + # On Windows, ':' (drive letter) and '\\' (path separator) are valid. if _sys.platform == "win32": - self._illegal_chars = ["?", '*', '"', '<', '>', '|'] + self._illegal_chars = ["?", "*", '"', "<", ">", "|"] else: - self._illegal_chars = ["?", '*', '"', '<', '>', '|', ':', '\\'] + self._illegal_chars = ["?", "*", '"', "<", ">", "|", ":", "\\"] self._pure_memory_path = [":memory", ":memory:"] if isinstance(db_path, str): if ( From 632baf1b6b22d30ca6721c87106b6791bfbb5873 Mon Sep 17 00:00:00 2001 From: "nengli.ln" Date: Tue, 28 Jul 2026 14:59:36 +0800 Subject: [PATCH 06/19] update --- src/storages/container/file_mmap_container.cc | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/storages/container/file_mmap_container.cc b/src/storages/container/file_mmap_container.cc index 213d8ffd8..3ab5a87b3 100644 --- a/src/storages/container/file_mmap_container.cc +++ b/src/storages/container/file_mmap_container.cc @@ -25,6 +25,9 @@ #define open _open #define close _close #define unlink _unlink +// _chsize_s returns 0 on success and an errno value on failure, so +// error checks must use `!= 0` (POSIX ftruncate returns 0 or -1). +#define ftruncate _chsize_s #ifndef O_RDWR #define O_RDWR _O_RDWR #endif @@ -74,13 +77,13 @@ void FilePrivateMMap::OpenAnonymous(size_t size) { } void* FilePrivateMMap::mmapImpl(const std::string& path, size_t mmap_size) { - int fd = _open(path.c_str(), O_RDONLY, 0); + int fd = open(path.c_str(), O_RDONLY); if (fd == -1) { THROW_RUNTIME_ERROR("Failed to open file: " + path); } void* mmap_data = mmap(nullptr, mmap_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); - _close(fd); + close(fd); return mmap_data; } @@ -103,15 +106,15 @@ void FileSharedMMap::Resize(size_t size) { data_ = nullptr; size_ = 0; - int fd = _open(path_.c_str(), O_RDWR, 0); + int fd = open(path_.c_str(), O_RDWR); if (fd == -1) { THROW_RUNTIME_ERROR("Failed to open file for resizing: " + path_); } - if (_chsize_s(fd, real_size) != 0) { - _close(fd); + if (ftruncate(fd, real_size) != 0) { + close(fd); THROW_RUNTIME_ERROR("Failed to resize file: " + path_); } - _close(fd); + close(fd); mmap_size_ = std::filesystem::file_size(path_); assert(mmap_size_ == real_size); // Create a new mapping with the updated file size From 9dad19449b1e8dc8c72e251c95b627a38155f70d Mon Sep 17 00:00:00 2001 From: "nengli.ln" Date: Tue, 28 Jul 2026 15:56:27 +0800 Subject: [PATCH 07/19] fix format --- include/neug/main/connection.h | 1 + include/neug/main/neug_db.h | 1 + include/neug/main/query_result.h | 1 + src/compiler/gopt/g_query_converter.cpp | 3 +- src/execution/extension/extension.cc | 9 ++-- src/main/file_lock.cc | 11 ++-- src/main/neug_db.cc | 3 +- src/storages/container/anon_mmap_container.cc | 2 +- src/transaction/wal/local_wal_writer.cc | 3 +- src/utils/io/file/file_utils.cc | 54 +++++++++---------- 10 files changed, 47 insertions(+), 41 deletions(-) diff --git a/include/neug/main/connection.h b/include/neug/main/connection.h index 9fcea01c7..d60a4b9db 100644 --- a/include/neug/main/connection.h +++ b/include/neug/main/connection.h @@ -27,6 +27,7 @@ #include "neug/main/query_processor.h" #include "neug/main/query_result.h" #include "neug/storages/graph_snapshot_store.h" +#include "neug/utils/api.h" #include "neug/utils/result.h" namespace neug { diff --git a/include/neug/main/neug_db.h b/include/neug/main/neug_db.h index 6dfc7f2f7..e1719ab76 100644 --- a/include/neug/main/neug_db.h +++ b/include/neug/main/neug_db.h @@ -36,6 +36,7 @@ #include "neug/transaction/insert_transaction.h" #include "neug/transaction/read_transaction.h" #include "neug/transaction/update_transaction.h" +#include "neug/utils/api.h" #include "neug/utils/property/types.h" #include "neug/version.h" diff --git a/include/neug/main/query_result.h b/include/neug/main/query_result.h index c8811b1c9..1350a8e45 100644 --- a/include/neug/main/query_result.h +++ b/include/neug/main/query_result.h @@ -23,6 +23,7 @@ #include "glog/logging.h" #include "neug/generated/proto/response/response.pb.h" +#include "neug/utils/api.h" namespace neug { /** diff --git a/src/compiler/gopt/g_query_converter.cpp b/src/compiler/gopt/g_query_converter.cpp index ea98b3785..042153c76 100644 --- a/src/compiler/gopt/g_query_converter.cpp +++ b/src/compiler/gopt/g_query_converter.cpp @@ -16,7 +16,8 @@ #include "neug/compiler/gopt/g_query_converter.h" // Windows headers define IN and OUT as empty macros (SAL annotations), -// which conflict with ::physical::EdgeExpand::IN and ::physical::EdgeExpand::OUT. +// which conflict with ::physical::EdgeExpand::IN and +// ::physical::EdgeExpand::OUT. #ifdef _WIN32 #ifdef IN #undef IN diff --git a/src/execution/extension/extension.cc b/src/execution/extension/extension.cc index 133cbeeba..66c9ebe0e 100644 --- a/src/execution/extension/extension.cc +++ b/src/execution/extension/extension.cc @@ -18,16 +18,15 @@ #else // Include winsock2.h before windows.h to avoid conflicting winsock.h/winsock2.h // definitions; httplib.h also includes winsock2.h later. -#include #include +#include // Windows shims for POSIX dlopen/dlsym/dlclose/dlerror. #define RTLD_NOW 0 #define RTLD_LOCAL 0 -static inline void* dlopen(const char* path, int) { - return LoadLibraryA(path); -} +static inline void* dlopen(const char* path, int) { return LoadLibraryA(path); } static inline void* dlsym(void* handle, const char* name) { - return reinterpret_cast(GetProcAddress(static_cast(handle), name)); + return reinterpret_cast( + GetProcAddress(static_cast(handle), name)); } static inline int dlclose(void* handle) { return FreeLibrary(static_cast(handle)) ? 0 : 1; diff --git a/src/main/file_lock.cc b/src/main/file_lock.cc index 08d2bba38..bb63b07d5 100644 --- a/src/main/file_lock.cc +++ b/src/main/file_lock.cc @@ -23,8 +23,8 @@ #include #include #else -#include #include +#include #include #endif #include @@ -90,7 +90,8 @@ FileLock::FileLock(const std::string& data_dir) fd_(-1), locked_(false) { #ifdef _WIN32 - fd_ = _open(lock_file_path_.c_str(), _O_RDWR | _O_CREAT, _S_IREAD | _S_IWRITE); + fd_ = + _open(lock_file_path_.c_str(), _O_RDWR | _O_CREAT, _S_IREAD | _S_IWRITE); #else fd_ = ::open(lock_file_path_.c_str(), O_RDWR | O_CREAT, 0600); #endif @@ -170,8 +171,10 @@ bool FileLock::lock(short type, bool wait, std::string& error_msg) { return false; } DWORD flags = 0; - if (type == F_WRLCK) flags |= LOCKFILE_EXCLUSIVE_LOCK; - if (wait) flags |= LOCKFILE_FAIL_IMMEDIATELY; + if (type == F_WRLCK) + flags |= LOCKFILE_EXCLUSIVE_LOCK; + if (wait) + flags |= LOCKFILE_FAIL_IMMEDIATELY; if (type == F_UNLCK) { OVERLAPPED ov = {}; if (!UnlockFileEx(hFile, 0, 1, 0, &ov)) { diff --git a/src/main/neug_db.cc b/src/main/neug_db.cc index f2046a4b5..e04c53ae5 100644 --- a/src/main/neug_db.cc +++ b/src/main/neug_db.cc @@ -237,7 +237,8 @@ void NeugDB::preprocessConfig() { db_dir_prefix = std::string(prefix_env); } else { #ifdef _WIN32 - // On Windows, use the system temp directory (e.g. C:\Users\\AppData\Local\Temp) + // On Windows, use the system temp directory (e.g. + // C:\Users\\AppData\Local\Temp) char tmp_path[MAX_PATH]; DWORD len = GetTempPathA(MAX_PATH, tmp_path); if (len > 0) { diff --git a/src/storages/container/anon_mmap_container.cc b/src/storages/container/anon_mmap_container.cc index 4d0ed690c..41f8ef05b 100644 --- a/src/storages/container/anon_mmap_container.cc +++ b/src/storages/container/anon_mmap_container.cc @@ -34,10 +34,10 @@ #endif #endif #include +#include #include #include #include -#include #ifdef __ia64__ #define ADDR (void*) (0x8000000000000000UL) diff --git a/src/transaction/wal/local_wal_writer.cc b/src/transaction/wal/local_wal_writer.cc index 1fbea0675..9c6a51704 100644 --- a/src/transaction/wal/local_wal_writer.cc +++ b/src/transaction/wal/local_wal_writer.cc @@ -52,8 +52,7 @@ void LocalWalWriter::open() { continue; } #ifdef _WIN32 - fd_ = _open(path.c_str(), O_RDWR | O_CREAT | O_TRUNC, - _S_IREAD | _S_IWRITE); + fd_ = _open(path.c_str(), O_RDWR | O_CREAT | O_TRUNC, _S_IREAD | _S_IWRITE); #else fd_ = ::open(path.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0644); #endif diff --git a/src/utils/io/file/file_utils.cc b/src/utils/io/file/file_utils.cc index fab9d3363..0225ce8d3 100644 --- a/src/utils/io/file/file_utils.cc +++ b/src/utils/io/file/file_utils.cc @@ -73,25 +73,29 @@ typedef long long ssize_t; static inline int posix_fadvise(int, off_t, off_t, int) { return 0; } static ssize_t pread(int fd, void* buf, size_t count, off_t offset) { - off_t old = _lseek(fd, 0, SEEK_CUR); - if (old == -1) return -1; - if (_lseek(fd, offset, SEEK_SET) == -1) return -1; - ssize_t r = _read(fd, buf, count); - int err = errno; - _lseek(fd, old, SEEK_SET); - errno = err; - return r; + off_t old = _lseek(fd, 0, SEEK_CUR); + if (old == -1) + return -1; + if (_lseek(fd, offset, SEEK_SET) == -1) + return -1; + ssize_t r = _read(fd, buf, count); + int err = errno; + _lseek(fd, old, SEEK_SET); + errno = err; + return r; } static ssize_t pwrite(int fd, const void* buf, size_t count, off_t offset) { - off_t old = _lseek(fd, 0, SEEK_CUR); - if (old == -1) return -1; - if (_lseek(fd, offset, SEEK_SET) == -1) return -1; - ssize_t w = _write(fd, buf, count); - int err = errno; - _lseek(fd, old, SEEK_SET); - errno = err; - return w; + off_t old = _lseek(fd, 0, SEEK_CUR); + if (old == -1) + return -1; + if (_lseek(fd, offset, SEEK_SET) == -1) + return -1; + ssize_t w = _write(fd, buf, count); + int err = errno; + _lseek(fd, old, SEEK_SET); + errno = err; + return w; } #endif #include @@ -127,8 +131,8 @@ static void copy_metadata(const struct stat& src_stat, #endif ::utimensat(AT_FDCWD, dst_path.c_str(), times, 0); #else - (void)src_stat; - (void)dst_path; + (void) src_stat; + (void) dst_path; #endif } @@ -196,7 +200,7 @@ static bool try_reflink(const std::string& src_path, */ static bool is_sparse(const struct stat& st) { #ifdef _WIN32 - (void)st; + (void) st; return false; #else return static_cast(st.st_blocks) * 512 < st.st_size; @@ -665,14 +669,10 @@ bool fsync_directory(const std::string& dir_path) { #ifdef _WIN32 // On Windows, open the directory with FILE_FLAG_BACKUP_SEMANTICS (required // to obtain a handle to a directory) and call FlushFileBuffers. - HANDLE hDir = CreateFileW( - std::filesystem::path(dir_path).wstring().c_str(), - GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, - nullptr, - OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS, - nullptr); + HANDLE hDir = + CreateFileW(std::filesystem::path(dir_path).wstring().c_str(), + GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); if (hDir == INVALID_HANDLE_VALUE) { return false; } From ffd0806f2649adebbde3473a8f32a0cd46c6f3a6 Mon Sep 17 00:00:00 2001 From: "nengli.ln" Date: Tue, 28 Jul 2026 16:51:29 +0800 Subject: [PATCH 08/19] update --- include/neug/compiler/binder/binder.h | 1 + include/neug/compiler/binder/expression_evaluator_utils.h | 1 + include/neug/compiler/common/types/timestamp_t.h | 1 + include/neug/storages/graph_snapshot_store.h | 1 + src/compiler/common/vector/value_vector.cpp | 1 + 5 files changed, 5 insertions(+) diff --git a/include/neug/compiler/binder/binder.h b/include/neug/compiler/binder/binder.h index f38016846..0d4b001c1 100644 --- a/include/neug/compiler/binder/binder.h +++ b/include/neug/compiler/binder/binder.h @@ -35,6 +35,7 @@ #include "neug/compiler/function/neug_call_function.h" #include "neug/compiler/parser/ddl/parsed_property_definition.h" #include "neug/compiler/parser/query/graph_pattern/pattern_element.h" +#include "neug/utils/api.h" namespace neug { struct VertexSchema; diff --git a/include/neug/compiler/binder/expression_evaluator_utils.h b/include/neug/compiler/binder/expression_evaluator_utils.h index fedc09754..3e7c6b276 100644 --- a/include/neug/compiler/binder/expression_evaluator_utils.h +++ b/include/neug/compiler/binder/expression_evaluator_utils.h @@ -25,6 +25,7 @@ #include "neug/compiler/binder/expression/expression.h" #include "neug/compiler/common/types/value/value.h" #include "neug/compiler/main/client_context.h" +#include "neug/utils/api.h" namespace neug { namespace evaluator { diff --git a/include/neug/compiler/common/types/timestamp_t.h b/include/neug/compiler/common/types/timestamp_t.h index d60fb7c0d..a3243f03a 100644 --- a/include/neug/compiler/common/types/timestamp_t.h +++ b/include/neug/compiler/common/types/timestamp_t.h @@ -24,6 +24,7 @@ #include "date_t.h" #include "dtime_t.h" +#include "neug/utils/api.h" namespace neug { namespace compiler_impl { diff --git a/include/neug/storages/graph_snapshot_store.h b/include/neug/storages/graph_snapshot_store.h index 3d7c4b49c..9f340d23c 100644 --- a/include/neug/storages/graph_snapshot_store.h +++ b/include/neug/storages/graph_snapshot_store.h @@ -23,6 +23,7 @@ #include "neug/storages/graph/graph_view.h" #include "neug/storages/graph/property_graph.h" +#include "neug/utils/api.h" #include "neug/utils/result.h" namespace neug { diff --git a/src/compiler/common/vector/value_vector.cpp b/src/compiler/common/vector/value_vector.cpp index 090d3f0aa..32fc55906 100644 --- a/src/compiler/common/vector/value_vector.cpp +++ b/src/compiler/common/vector/value_vector.cpp @@ -31,6 +31,7 @@ #include "neug/compiler/common/types/value/nested.h" #include "neug/compiler/common/types/value/value.h" #include "neug/compiler/common/vector/auxiliary_buffer.h" +#include "neug/utils/api.h" #include "neug/utils/exception/exception.h" namespace neug { From 8f0e1268fd4a11d1784d47663548d083d303bd24 Mon Sep 17 00:00:00 2001 From: "nengli.ln" Date: Wed, 29 Jul 2026 16:10:15 +0800 Subject: [PATCH 09/19] fix compile --- include/neug/storages/graph/property_graph.h | 7 +++- src/execution/extension/extension.cc | 9 ++-- src/main/neug_db.cc | 44 ++++++++++++++++++++ src/storages/graph/property_graph.cc | 5 +++ 4 files changed, 60 insertions(+), 5 deletions(-) diff --git a/include/neug/storages/graph/property_graph.h b/include/neug/storages/graph/property_graph.h index 0ebbfaabd..01403e24d 100644 --- a/include/neug/storages/graph/property_graph.h +++ b/include/neug/storages/graph/property_graph.h @@ -120,10 +120,13 @@ class NEUG_API PropertyGraph { // Move-only: copy is not meaningful because the underlying storage // (VertexTable, EdgeTable) is move-only. Use Clone() for explicit copies. + // Move operations are defaulted in the .cc where StorageIndexManager is + // complete; defaulting them here would require the full type to destroy + // index_manager_ in every including TU (MSVC C2027). PropertyGraph(const PropertyGraph&) = delete; PropertyGraph& operator=(const PropertyGraph&) = delete; - PropertyGraph(PropertyGraph&&) = default; - PropertyGraph& operator=(PropertyGraph&&) = default; + PropertyGraph(PropertyGraph&&) noexcept; + PropertyGraph& operator=(PropertyGraph&&) noexcept; /** * @brief Open the graph from the given Checkpoint using the Module interface. diff --git a/src/execution/extension/extension.cc b/src/execution/extension/extension.cc index 66c9ebe0e..325ff9cb4 100644 --- a/src/execution/extension/extension.cc +++ b/src/execution/extension/extension.cc @@ -16,10 +16,13 @@ #ifndef _WIN32 #include #else -// Include winsock2.h before windows.h to avoid conflicting winsock.h/winsock2.h -// definitions; httplib.h also includes winsock2.h later. -#include +// winsock2.h must be included before windows.h, otherwise windows.h pulls +// in the legacy winsock.h which conflicts with the winsock2.h that +// httplib.h includes later. The blank line keeps clang-format from +// re-sorting the two includes into the broken order. #include + +#include // Windows shims for POSIX dlopen/dlsym/dlclose/dlerror. #define RTLD_NOW 0 #define RTLD_LOCAL 0 diff --git a/src/main/neug_db.cc b/src/main/neug_db.cc index 6fe9a98aa..6666417cf 100644 --- a/src/main/neug_db.cc +++ b/src/main/neug_db.cc @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -54,6 +55,45 @@ namespace neug { +#ifdef _WIN32 +// MSVC's CRT has no mkdtemp(); emulate it by replacing the trailing +// "XXXXXX" with random characters and retrying until a fresh directory is +// created. Sets errno and returns false on failure, mirroring mkdtemp. +static bool mkdtemp_win(std::string& path_template) { + constexpr char kChars[] = + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + constexpr size_t kSuffixLen = 6; + if (path_template.size() < kSuffixLen || + path_template.compare(path_template.size() - kSuffixLen, kSuffixLen, + "XXXXXX") != 0) { + errno = EINVAL; + return false; + } + std::random_device rd; + std::mt19937_64 gen(rd()); + std::uniform_int_distribution dist(0, sizeof(kChars) - 2); + for (int attempt = 0; attempt < 100; ++attempt) { + for (size_t i = path_template.size() - kSuffixLen; i < path_template.size(); + ++i) { + path_template[i] = kChars[dist(gen)]; + } + std::error_code ec; + if (std::filesystem::create_directory(path_template, ec)) { + return true; + } + if (ec && ec != std::errc::file_exists) { + // Map the system error to its POSIX equivalent so the errno-based + // reporting at the call site stays meaningful. + errno = ec.default_error_condition().value(); + return false; + } + // Name collision: retry with a new random suffix. + } + errno = EEXIST; + return false; +} +#endif + inline std::string allocator_prefix(const std::string& allocator_dir, int thread_id) { return (std::filesystem::path(allocator_dir) / @@ -365,7 +405,11 @@ void NeugDB::preprocessConfig() { db_dir_prefix = std::filesystem::absolute(db_dir_prefix); std::filesystem::create_directories(db_dir_prefix); auto path_template = (db_dir_prefix / "neug_db_XXXXXX").string(); +#ifdef _WIN32 + if (!mkdtemp_win(path_template)) { +#else if (::mkdtemp(path_template.data()) == nullptr) { +#endif const auto error = std::error_code(errno, std::generic_category()); THROW_IO_EXCEPTION("Failed to create temporary NeugDB under " + db_dir_prefix.string() + ": " + error.message()); diff --git a/src/storages/graph/property_graph.cc b/src/storages/graph/property_graph.cc index b66ca1e4e..0540ad43b 100644 --- a/src/storages/graph/property_graph.cc +++ b/src/storages/graph/property_graph.cc @@ -50,6 +50,11 @@ PropertyGraph::PropertyGraph() PropertyGraph::~PropertyGraph() { Clear(); } +// Defaulted here (not in the header) so that StorageIndexManager is a +// complete type when the compiler generates the unique_ptr member moves. +PropertyGraph::PropertyGraph(PropertyGraph&&) noexcept = default; +PropertyGraph& PropertyGraph::operator=(PropertyGraph&&) noexcept = default; + void PropertyGraph::loadSchema(const std::string& schema_path) { std::ifstream in(schema_path); schema_.Deserialize(in); From 60536472cc004fb4c130f655e97b4626afc10c4f Mon Sep 17 00:00:00 2001 From: "nengli.ln" Date: Tue, 4 Aug 2026 14:07:57 +0800 Subject: [PATCH 10/19] add test workflow for windows --- .github/workflows/Windows.yml | 128 +++++++++++ CMakeLists.txt | 14 +- include/neug/storages/csr/immutable_csr.h | 5 +- scripts/install_deps.ps1 | 254 ++++++++++++++++++++++ src/CMakeLists.txt | 33 +++ tests/compiler/extension_test.cpp | 4 +- tests/compiler/gopt_test.h | 7 +- tests/main/test_execution_slot.cc | 5 + tests/storage/test_open_graph.cc | 5 + tests/unittest/test_mmap_container.cc | 6 +- tests/utils/CMakeLists.txt | 4 +- tests/utils/test_table.cc | 5 + 12 files changed, 456 insertions(+), 14 deletions(-) create mode 100644 .github/workflows/Windows.yml create mode 100644 scripts/install_deps.ps1 diff --git a/.github/workflows/Windows.yml b/.github/workflows/Windows.yml new file mode 100644 index 000000000..3b31db2bd --- /dev/null +++ b/.github/workflows/Windows.yml @@ -0,0 +1,128 @@ +name: NeuG Test (Windows) + +on: + workflow_dispatch: + +concurrency: + group: ${{ github.repository }}-${{ github.event.number || github.head_ref || github.sha }}-${{ github.workflow }} + cancel-in-progress: true + +jobs: + format_check: + uses: ./.github/workflows/format-check.yml + with: + is_pull_request: false + + # ============================================================ + # Windows x64 Job: Build NeuG + run C++ and Python tests + # Uses GitHub-hosted windows-2022 runner with pre-installed + # MSVC 2022, vcpkg, Python, CMake, and Ninja. + # ============================================================ + build_and_test_windows: + needs: format_check + if: ${{ needs.format_check.result == 'success' || needs.format_check.result == 'skipped' }} + runs-on: windows-2022 + timeout-minutes: 120 + defaults: + run: + shell: pwsh + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + fetch-depth: 0 + + - name: Setup MSVC Developer Command Prompt + uses: ilammy/msvc-dev-cmd@v1 + with: + arch: x64 + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - name: Install Python dependencies + run: | + python -m pip install --upgrade pip + python -m pip install -r tools/python_bind/requirements.txt + python -m pip install pytest pytest-cov coverage + + # vcpkg is pre-installed on GitHub-hosted Windows runners at C:\vcpkg. + # Setting VCPKG_ROOT lets CMake's vcpkg toolchain auto-install OpenSSL + # and other dependencies declared via find_package() in CMakeLists.txt. + - name: Setup vcpkg + run: | + echo "VCPKG_ROOT=$env:VCPKG_INSTALLATION_ROOT" >> $env:GITHUB_ENV + echo "Using vcpkg at: $env:VCPKG_INSTALLATION_ROOT" + + - name: Cache vcpkg packages + uses: actions/cache@v4 + with: + path: ${{ env.VCPKG_ROOT }}/installed + key: vcpkg-x64-windows-static-md-${{ hashFiles('CMakeLists.txt') }} + restore-keys: | + vcpkg-x64-windows-static-md- + + - name: Configure CMake + run: | + $pythonExe = (Get-Command python).Path + $vcpkgToolchain = Join-Path $env:VCPKG_ROOT "scripts\buildsystems\vcpkg.cmake" + cmake -B build -G Ninja ` + -DCMAKE_BUILD_TYPE=Release ` + -DCMAKE_TOOLCHAIN_FILE="$vcpkgToolchain" ` + -DVCPKG_TARGET_TRIPLET=x64-windows-static-md ` + -DBUILD_PYTHON=ON ` + -DBUILD_TEST=ON ` + -DPython_EXECUTABLE="$pythonExe" ` + -DPYTHON_EXECUTABLE="$pythonExe" ` + . + + - name: Build + run: | + $cpuCount = [Environment]::ProcessorCount + $jobs = [Math]::Min($cpuCount, 8) + Write-Host "Building with $jobs parallel jobs" + cmake --build build -j $jobs + + # ======================================== + # Phase 1: C++ Tests (ctest) + # ======================================== + - name: Run C++ tests + continue-on-error: true + run: | + cd build + ctest --output-on-failure --timeout 60 -j $env:NUMBER_OF_PROCESSORS + + # ======================================== + # Phase 2: Python Tests + # ======================================== + - name: Run Python smoke test (custom graph) + run: | + python tools/python_bind/example/simple_load_query.py + + - name: Run Python DB init tests + continue-on-error: true + run: | + cd tools/python_bind + python -m pytest -sv tests/test_db_init.py --exitfirst + + - name: Run Python schema tests + continue-on-error: true + run: | + cd tools/python_bind + python -m pytest -sv tests/test_schema.py --exitfirst + + - name: Run Python DML tests + continue-on-error: true + run: | + cd tools/python_bind + python -m pytest -sv tests/test_dml.py --exitfirst + + - name: Run Python query tests + continue-on-error: true + run: | + cd tools/python_bind + python -m pytest -sv tests/test_query.py --exitfirst diff --git a/CMakeLists.txt b/CMakeLists.txt index dfb57fed4..fdd3a36fe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -121,7 +121,6 @@ if(WIN32) set(BUILD_NODEJS OFF CACHE BOOL "Node.js binding is not supported on Windows yet" FORCE) set(BUILD_HTTP_SERVER OFF CACHE BOOL "HTTP server (brpc) is not supported on Windows yet" FORCE) set(BUILD_EXTENSIONS "" CACHE STRING "Extensions are not supported on Windows yet" FORCE) - set(BUILD_TEST OFF CACHE BOOL "Tests are disabled on Windows core build" FORCE) set(BUILD_EXECUTABLES OFF CACHE BOOL "Executables are disabled on Windows core build" FORCE) set(BUILD_EXAMPLES OFF CACHE BOOL "Examples are disabled on Windows core build" FORCE) set(NEUG_NATIVE_ARCH OFF CACHE BOOL "Native architecture tuning is not supported on Windows" FORCE) @@ -524,9 +523,16 @@ function(add_neug_test TEST_NAME) find_package(OpenSSL REQUIRED) set(SRCS ${ARGN}) add_executable(${TEST_NAME} ${SRCS}) - target_link_libraries(${TEST_NAME} PUBLIC neug::GTest neug - ${Protobuf_LIBRARIES} ${GLOG_LIBRARIES} ${ARROW_LIB} ${OPENSSL_LIBRARIES} - ${CRYPTO_LIB} ${YAML_CPP_LIBRARIES}) + if(WIN32 AND BUILD_TEST) + # neug_static contains the same object files as neug.dll but exposes all + # symbols, which is required by tests that exercise internal APIs. + target_link_libraries(${TEST_NAME} PUBLIC neug::GTest neug_static + ${ARROW_LIB} ${OPENSSL_LIBRARIES} ${CRYPTO_LIB}) + else() + target_link_libraries(${TEST_NAME} PUBLIC neug::GTest neug + ${Protobuf_LIBRARIES} ${GLOG_LIBRARIES} ${ARROW_LIB} ${OPENSSL_LIBRARIES} + ${CRYPTO_LIB} ${YAML_CPP_LIBRARIES}) + endif() # The bundled googletest (v1.13.0, fetched into _deps) must beat any # system/Homebrew gtest that leaks in via an -isystem include path; otherwise # tests compile against newer gtest headers but link the bundled library, diff --git a/include/neug/storages/csr/immutable_csr.h b/include/neug/storages/csr/immutable_csr.h index bf9e51670..530f2ee53 100644 --- a/include/neug/storages/csr/immutable_csr.h +++ b/include/neug/storages/csr/immutable_csr.h @@ -26,12 +26,13 @@ #include "neug/storages/csr/csr_view.h" #include "neug/storages/csr/nbr.h" #include "neug/storages/module/type_name.h" +#include "neug/utils/api.h" #include "neug/utils/property/types.h" namespace neug { template -class ImmutableCsr : public TypedCsrBase { +class NEUG_API ImmutableCsr : public TypedCsrBase { public: using data_t = EDATA_T; using nbr_t = ImmutableNbr; @@ -142,7 +143,7 @@ class ImmutableCsr : public TypedCsrBase { }; template -class SingleImmutableCsr : public TypedCsrBase { +class NEUG_API SingleImmutableCsr : public TypedCsrBase { public: using data_t = EDATA_T; using nbr_t = ImmutableNbr; diff --git a/scripts/install_deps.ps1 b/scripts/install_deps.ps1 new file mode 100644 index 000000000..4dd4921ae --- /dev/null +++ b/scripts/install_deps.ps1 @@ -0,0 +1,254 @@ +# Copyright 2020 Alibaba Group Holding Limited. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +<# +.SYNOPSIS + Install NeuG build dependencies on Windows. + +.DESCRIPTION + Windows counterpart of scripts/install_deps.sh. Instead of apt/yum/brew it + relies on vcpkg (classic mode) for third-party libraries that are not + bundled under third_party/, and on Visual Studio Build Tools for the MSVC + toolchain (cl/link/cmake/ninja). + + On success it writes $HOME\.neug_env.ps1 (counterpart of ~/.neug_env); + dot-source it before building: + + . $HOME\.neug_env.ps1 + + All third-party libraries (OpenSSL, etc.) are installed under + $InstallDir\vcpkg\installed\$Triplet\. The vcpkg checkout itself + lives at $InstallDir\vcpkg\. + +.PARAMETER InstallDir + Root directory for all NeuG third-party dependencies. vcpkg is cloned + into $InstallDir\vcpkg and packages are installed under + $InstallDir\vcpkg\installed\$Triplet\. Defaults to C:\neug-deps. + +.PARAMETER VcpkgRoot + Explicit vcpkg checkout directory. Overrides auto-detection from + $InstallDir\vcpkg, $env:VCPKG_ROOT, and common locations. + +.PARAMETER Triplet + vcpkg target triplet. Must stay in sync with build_windows.bat and the + CRT configuration in CMakeLists.txt (/MD == *-static-md). + +.PARAMETER CN + Use mirrors inside mainland China where possible (counterpart of --cn). + +.PARAMETER DebugMode + Print extra diagnostic output (counterpart of --debug). +#> +[CmdletBinding()] +param( + [string]$InstallDir = "C:\neug-deps", + [string]$VcpkgRoot = "", + [string]$Triplet = "x64-windows-static-md", + [switch]$CN, + [switch]$DebugMode +) + +$ErrorActionPreference = "Stop" + +# Libraries taken from vcpkg. Everything else (protobuf, gflags, glog, +# yaml-cpp, ...) is vendored under third_party/ and built by CMake. +$VcpkgPackages = @("openssl") + +$OutputEnvFile = Join-Path $HOME ".neug_env.ps1" + +# --------------------------------------------------------------------------- +# logging helpers (mirror info/err/warning/debug of install_deps.sh) +# --------------------------------------------------------------------------- +function Info($msg) { Write-Host $msg -ForegroundColor Blue } +function Err($msg) { Write-Host $msg -ForegroundColor Red } +function Warning($msg) { Write-Host $msg -ForegroundColor Yellow } +function DebugLog($msg) { + if ($DebugMode) { Write-Host "[DEBUG] $msg" -ForegroundColor Red } +} + +# --------------------------------------------------------------------------- +# platform gate (counterpart of get_os_version) +# --------------------------------------------------------------------------- +if (-not ($env:OS -eq "Windows_NT")) { + Err "This script only supports Windows. Use scripts/install_deps.sh on Linux/macOS." + exit 1 +} +DebugLog "OS: $([System.Environment]::OSVersion.VersionString), Triplet: $Triplet" + +# --------------------------------------------------------------------------- +# Visual Studio Build Tools (MSVC + CMake + Ninja) +# --------------------------------------------------------------------------- +function Find-VcVarsAll { + # Preferred: query via vswhere (installed together with any VS >= 15.2). + $vswhere = Join-Path ${env:ProgramFiles(x86)} "Microsoft Visual Studio\Installer\vswhere.exe" + if (Test-Path $vswhere) { + $installPath = & $vswhere -latest -products * ` + -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 ` + -property installationPath 2>$null | Select-Object -First 1 + if ($installPath) { + $bat = Join-Path $installPath "VC\Auxiliary\Build\vcvarsall.bat" + if (Test-Path $bat) { return $bat } + } + } + # Fallback: well-known install locations. + $candidates = @( + (Join-Path ${env:ProgramFiles(x86)} "Microsoft Visual Studio\2022\BuildTools\VC\Auxiliary\Build\vcvarsall.bat"), + (Join-Path $env:ProgramFiles "Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat"), + (Join-Path $env:ProgramFiles "Microsoft Visual Studio\2022\Professional\VC\Auxiliary\Build\vcvarsall.bat"), + (Join-Path $env:ProgramFiles "Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat") + ) + foreach ($c in $candidates) { + if (Test-Path $c) { return $c } + } + return $null +} + +$VcVarsAll = Find-VcVarsAll +if (-not $VcVarsAll) { + Err ("Visual Studio Build Tools (C++ workload) not found.`n" + + "Install them first, e.g.:`n" + + " winget install Microsoft.VisualStudio.2022.BuildTools --override ` +`"--add Microsoft.VisualStudio.Workload.VCTools --includeRecommended`"") + exit 1 +} +Info "Found MSVC environment script: $VcVarsAll" + +# --------------------------------------------------------------------------- +# Python 3 (required by BUILD_PYTHON=ON and pre-commit tooling) +# --------------------------------------------------------------------------- +$PythonExe = $null +$pythonCmd = Get-Command python -ErrorAction SilentlyContinue +if ($pythonCmd) { + # The WindowsApps store alias is a stub that opens the Microsoft Store; + # filter it out. + if ($pythonCmd.Source -notmatch "WindowsApps") { + $PythonExe = $pythonCmd.Source + } +} +if (-not $PythonExe) { + Err ("Python 3 not found on PATH.`n" + + "Install it first, e.g.: winget install Python.Python.3.11") + exit 1 +} +$pythonVersion = & $PythonExe --version 2>&1 +Info "Found Python: $PythonExe ($pythonVersion)" + +# --------------------------------------------------------------------------- +# vcpkg: locate, bootstrap when missing (counterpart of install_openssl/ +# install_curl source builds on Linux) +# --------------------------------------------------------------------------- +function Find-VcpkgRoot { + # 1. Explicit -VcpkgRoot parameter + if ($VcpkgRoot -and (Test-Path (Join-Path $VcpkgRoot ".vcpkg-root"))) { + return (Resolve-Path $VcpkgRoot).Path + } + # 2. $env:VCPKG_ROOT (e.g. pre-set on GitHub runners) + if ($env:VCPKG_ROOT -and (Test-Path (Join-Path $env:VCPKG_ROOT ".vcpkg-root"))) { + return $env:VCPKG_ROOT + } + # 3. Candidate locations: $InstallDir\vcpkg, $HOME\vcpkg, C:\vcpkg + $candidates = @( + (Join-Path $InstallDir "vcpkg"), + (Join-Path $HOME "vcpkg"), + "C:\vcpkg" + ) + foreach ($c in $candidates) { + if (Test-Path (Join-Path $c ".vcpkg-root")) { return $c } + } + return $null +} + +$ResolvedVcpkgRoot = Find-VcpkgRoot +if (-not $ResolvedVcpkgRoot) { + $ResolvedVcpkgRoot = Join-Path $InstallDir "vcpkg" + if (-not (Test-Path $InstallDir)) { + New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null + } + Warning "vcpkg not found; cloning into $ResolvedVcpkgRoot" + $gitCmd = Get-Command git -ErrorAction SilentlyContinue + if (-not $gitCmd) { + Err "git is required to bootstrap vcpkg. Install it first: winget install Git.Git" + exit 1 + } + $vcpkgRepo = "https://github.com/microsoft/vcpkg.git" + if ($CN) { + # GitHub is reachable from mainland China but often slow; keep the + # official repo and let the user override with a mirror if needed. + Warning "-CN: cloning vcpkg from github.com; set up a git mirror manually if this is too slow." + } + git clone --depth 1 $vcpkgRepo $ResolvedVcpkgRoot + if ($LASTEXITCODE -ne 0) { + Err "Failed to clone vcpkg." + exit 1 + } +} + +$VcpkgExe = Join-Path $ResolvedVcpkgRoot "vcpkg.exe" +if (-not (Test-Path $VcpkgExe)) { + Info "Bootstrapping vcpkg..." + & (Join-Path $ResolvedVcpkgRoot "bootstrap-vcpkg.bat") -disableMetrics + if (-not (Test-Path $VcpkgExe)) { + Err "vcpkg bootstrap failed." + exit 1 + } +} +Info "Using vcpkg: $VcpkgExe" + +$InstallPrefix = Join-Path $ResolvedVcpkgRoot "installed\$Triplet" + +# --------------------------------------------------------------------------- +# install packages (counterpart of install_neug_dependencies) +# --------------------------------------------------------------------------- +foreach ($pkg in $VcpkgPackages) { + Info "Installing ${pkg}:${Triplet} via vcpkg" + & $VcpkgExe install "${pkg}:${Triplet}" + if ($LASTEXITCODE -ne 0) { + Err "vcpkg failed to install ${pkg}:${Triplet}" + exit 1 + } +} + +# --------------------------------------------------------------------------- +# write env config (counterpart of write_env_config) +# --------------------------------------------------------------------------- +function Write-EnvConfig { + $lines = @( + "# NeuG build environment - generated by scripts/install_deps.ps1", + "# Windows counterpart of ~/.neug_env; load with: . `$HOME\.neug_env.ps1", + "#", + "# All third-party libraries are under $InstallDir\vcpkg\installed\$Triplet\", + "`$env:NEUG_DEPS_DIR = '$InstallDir'", + "`$env:NEUG_HOME = '$InstallPrefix'", + "`$env:VCPKG_ROOT = '$ResolvedVcpkgRoot'", + "`$env:VCPKG_TARGET_TRIPLET = '$Triplet'", + "`$env:CMAKE_TOOLCHAIN_FILE = '$ResolvedVcpkgRoot\scripts\buildsystems\vcpkg.cmake'", + "`$env:CMAKE_PREFIX_PATH = '$InstallPrefix'", + "`$env:OPENSSL_ROOT_DIR = '$InstallPrefix'", + "`$env:PYTHON_EXE = '$PythonExe'", + "`$env:NEUG_VCVARSALL = '$VcVarsAll'", + "# Windows resolves DLLs via PATH (counterpart of LD_LIBRARY_PATH).", + "`$env:Path = '$InstallPrefix\bin;' + `$env:Path" + ) + Set-Content -Path $OutputEnvFile -Value ($lines -join "`r`n") -Encoding UTF8 +} + +Write-EnvConfig + +Info ("Dependencies installed under $InstallDir`n" + + " vcpkg checkout: $ResolvedVcpkgRoot`n" + + " installed packages: $InstallPrefix`n`n" + + "The environment config has been written to $OutputEnvFile`n" + + "Don't forget to load it before building:`n`n" + + " . `$HOME\.neug_env.ps1`n`n" + + "and run vcvarsall / build_windows.bat for the MSVC toolchain.") diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c058ede17..4361aa7b9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -67,3 +67,36 @@ if (ENABLE_BACKTRACES) target_link_libraries(neug PUBLIC cpptrace::cpptrace) endif() +# On Windows, tests need access to many internal symbols that are not exported +# from the shared neug.dll (NEUG_API only covers public API). Build a static +# counterpart for tests to link against so they can see all symbols. +if(WIN32 AND BUILD_TEST) + add_library(neug_static STATIC ${ALL_OBJECT_FILES}) + target_compile_definitions(neug_static PRIVATE NEUG_EXPORTS) + if (ENABLE_GCOV) + target_link_options(neug_static PRIVATE --coverage) + endif() + # Dependencies are PUBLIC so test executables inherit them automatically. + target_link_libraries(neug_static PUBLIC OpenSSL::SSL OpenSSL::Crypto) + target_link_libraries(neug_static PUBLIC ${GFLAGS_LIBRARIES} ${YAML_CPP_LIBRARIES} ${COMPILER_LIBRARIES}) + target_link_libraries(neug_static PUBLIC ${GLOG_LIBRARIES}) + target_link_libraries(neug_static PUBLIC ${Protobuf_LIBRARIES}) + if (BUILD_HTTP_SERVER) + target_link_libraries(neug_static PUBLIC ${BRPC_LIB} ${LEVELDB_LIB}) + target_link_libraries(neug_static PUBLIC ${CRYPTO_LIB}) + endif() + if (WITH_MIMALLOC) + if(WIN32) + target_link_libraries(neug_static PUBLIC mimalloc-static) + else() + target_link_libraries(neug_static PUBLIC mimalloc) + endif() + endif() + if (ENABLE_BACKTRACES) + target_link_libraries(neug_static PUBLIC cpptrace::cpptrace) + endif() + target_include_directories(neug_static PUBLIC + $ + $) +endif() + diff --git a/tests/compiler/extension_test.cpp b/tests/compiler/extension_test.cpp index 4d071f709..70058d01c 100644 --- a/tests/compiler/extension_test.cpp +++ b/tests/compiler/extension_test.cpp @@ -14,10 +14,12 @@ * limitations under the License. */ -#include #include #include +#ifndef _WIN32 +#include #include +#endif #include #include diff --git a/tests/compiler/gopt_test.h b/tests/compiler/gopt_test.h index aafbb4753..69d567845 100644 --- a/tests/compiler/gopt_test.h +++ b/tests/compiler/gopt_test.h @@ -16,7 +16,9 @@ #pragma once +#ifndef _WIN32 #include +#endif #include #include #include @@ -68,11 +70,10 @@ class Utils { return val ? std::string(val) : defaultVal; } - static std::filesystem::path getTestResourcePath( - const std::string& relativePath) { + static std::string getTestResourcePath(const std::string& relativePath) { auto parentPath = getEnvVarOrDefault("TEST_RESOURCE", "/workspaces/neug/tests/compiler"); - return std::filesystem::path(parentPath) / relativePath; + return (std::filesystem::path(parentPath) / relativePath).string(); } template diff --git a/tests/main/test_execution_slot.cc b/tests/main/test_execution_slot.cc index a89f9b489..486048b51 100644 --- a/tests/main/test_execution_slot.cc +++ b/tests/main/test_execution_slot.cc @@ -13,7 +13,12 @@ * limitations under the License. */ +#ifndef _WIN32 #include +#else +#include +#define getpid _getpid +#endif #include #include diff --git a/tests/storage/test_open_graph.cc b/tests/storage/test_open_graph.cc index ea8b653cc..516b7d13a 100644 --- a/tests/storage/test_open_graph.cc +++ b/tests/storage/test_open_graph.cc @@ -14,7 +14,12 @@ */ #include +#ifndef _WIN32 #include +#else +#include +#define getpid _getpid +#endif #include #include #include diff --git a/tests/unittest/test_mmap_container.cc b/tests/unittest/test_mmap_container.cc index 52972d922..f202b055d 100644 --- a/tests/unittest/test_mmap_container.cc +++ b/tests/unittest/test_mmap_container.cc @@ -56,9 +56,9 @@ TEST_F(MMapContainerTest, IsDirty_AnonymousMMap) { EXPECT_TRUE(container.IsDirty()); // Small anonymous mmap (< FileHeader size) should also be dirty - neug::FilePrivateMMap small; - small.OpenAnonymous(8); - EXPECT_TRUE(small.IsDirty()); + neug::FilePrivateMMap small_container; + small_container.OpenAnonymous(8); + EXPECT_TRUE(small_container.IsDirty()); } TEST_F(MMapContainerTest, IsDirty_Resize) { diff --git a/tests/utils/CMakeLists.txt b/tests/utils/CMakeLists.txt index 976a54c64..1d33a08a6 100644 --- a/tests/utils/CMakeLists.txt +++ b/tests/utils/CMakeLists.txt @@ -6,5 +6,7 @@ add_neug_test( test_utils.cc test_exception.cc test_sniffer.cc - test_file_utils.cc json_test.cc) +if(NOT WIN32) + target_sources(utils_test PRIVATE test_file_utils.cc) +endif() diff --git a/tests/utils/test_table.cc b/tests/utils/test_table.cc index efb3e6ea0..00191a798 100644 --- a/tests/utils/test_table.cc +++ b/tests/utils/test_table.cc @@ -13,7 +13,12 @@ * limitations under the License. */ #include +#ifndef _WIN32 #include +#else +#include +#define getpid _getpid +#endif #include #include "neug/common/types/value.h" From 4b5e91469fb7eeda6bb376ebb53204e879886d91 Mon Sep 17 00:00:00 2001 From: "nengli.ln" Date: Tue, 4 Aug 2026 14:08:25 +0800 Subject: [PATCH 11/19] update --- tools/utils/CMakeLists.txt | 32 ++++++++++++++++++-------------- tools/utils/bulk_loader.cc | 4 ++++ 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/tools/utils/CMakeLists.txt b/tools/utils/CMakeLists.txt index 851a0317a..3f1cd244e 100644 --- a/tools/utils/CMakeLists.txt +++ b/tools/utils/CMakeLists.txt @@ -1,17 +1,21 @@ -add_executable(bulk_loader bulk_loader.cc) -target_link_libraries(bulk_loader PRIVATE neug ${GLOG_LIBRARIES}) +# bulk_loader is a POSIX-heavy utility executable; skip it on Windows when +# executables are disabled for the core build. +if(NOT WIN32 OR BUILD_EXECUTABLES) + add_executable(bulk_loader bulk_loader.cc) + target_link_libraries(bulk_loader PRIVATE neug ${GLOG_LIBRARIES}) -find_package(OpenSSL REQUIRED) -target_link_libraries(bulk_loader - PRIVATE - ${OPENSSL_LIBRARIES} - ${CRYPTO_LIB} - ${GFLAGS_LIBRARIES} -) + find_package(OpenSSL REQUIRED) + target_link_libraries(bulk_loader + PRIVATE + ${OPENSSL_LIBRARIES} + ${CRYPTO_LIB} + ${GFLAGS_LIBRARIES} + ) -if (ENABLE_GCOV) - target_compile_options(bulk_loader PRIVATE --coverage) - target_link_options(bulk_loader PRIVATE --coverage) -endif() + if (ENABLE_GCOV) + target_compile_options(bulk_loader PRIVATE --coverage) + target_link_options(bulk_loader PRIVATE --coverage) + endif() -install_without_export_neug_target(bulk_loader) \ No newline at end of file + install_without_export_neug_target(bulk_loader) +endif() \ No newline at end of file diff --git a/tools/utils/bulk_loader.cc b/tools/utils/bulk_loader.cc index 0b2163226..c016df70e 100644 --- a/tools/utils/bulk_loader.cc +++ b/tools/utils/bulk_loader.cc @@ -123,7 +123,11 @@ int main(int argc, char** argv) { return -1; } +#ifdef _WIN32 + _putenv("TZ=Asia/Shanghai"); +#else setenv("TZ", "Asia/Shanghai", 1); +#endif tzset(); auto start = std::chrono::high_resolution_clock::now(); From dfa1c992387c7119b55c580572f0eb543a8c91bd Mon Sep 17 00:00:00 2001 From: "nengli.ln" Date: Tue, 4 Aug 2026 17:13:02 +0800 Subject: [PATCH 12/19] fix test --- .github/workflows/Windows.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/Windows.yml b/.github/workflows/Windows.yml index 3b31db2bd..95ce18f30 100644 --- a/.github/workflows/Windows.yml +++ b/.github/workflows/Windows.yml @@ -99,10 +99,6 @@ jobs: # ======================================== # Phase 2: Python Tests # ======================================== - - name: Run Python smoke test (custom graph) - run: | - python tools/python_bind/example/simple_load_query.py - - name: Run Python DB init tests continue-on-error: true run: | From de75cd097211f95739335c2e3dc4df483c106c35 Mon Sep 17 00:00:00 2001 From: "nengli.ln" Date: Wed, 5 Aug 2026 10:10:54 +0800 Subject: [PATCH 13/19] update trigger condition of Window.yml --- .github/workflows/Windows.yml | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/.github/workflows/Windows.yml b/.github/workflows/Windows.yml index 95ce18f30..f9e03a062 100644 --- a/.github/workflows/Windows.yml +++ b/.github/workflows/Windows.yml @@ -2,6 +2,26 @@ name: NeuG Test (Windows) on: workflow_dispatch: + release: + types: + - created + push: + paths: + - 'cmake/**' + - 'src/**' + - 'bin/**' + - 'proto/**' + - 'third_party/**' + - 'tools/python_bind/**' + - 'tools/utils/**' + - 'tests/**' + - '.github/workflows/Windows.yml' + - '.github/workflows/build-and-test-common.yml' + - 'scripts/install_deps.ps1' + - 'scripts/build_windows.bat' + - 'include/**' + - 'CMakeLists.txt' + - 'doc/source/tutorials/**' concurrency: group: ${{ github.repository }}-${{ github.event.number || github.head_ref || github.sha }}-${{ github.workflow }} @@ -9,9 +29,13 @@ concurrency: jobs: format_check: + if: | + github.event_name == 'workflow_dispatch' || + github.event_name == 'release' || + (github.event_name == 'push' && (github.repository != 'alibaba/neug' || github.ref == 'refs/heads/main')) uses: ./.github/workflows/format-check.yml with: - is_pull_request: false + is_pull_request: ${{ github.event_name == 'pull_request' }} # ============================================================ # Windows x64 Job: Build NeuG + run C++ and Python tests @@ -20,7 +44,7 @@ jobs: # ============================================================ build_and_test_windows: needs: format_check - if: ${{ needs.format_check.result == 'success' || needs.format_check.result == 'skipped' }} + if: ${{ (needs.format_check.result == 'success' || needs.format_check.result == 'skipped') && (github.event_name == 'workflow_dispatch' || github.event_name == 'release' || (github.event_name == 'push' && (github.repository != 'alibaba/neug' || github.ref == 'refs/heads/main'))) }} runs-on: windows-2022 timeout-minutes: 120 defaults: From a6dfb1767daeb38f37a038dbfc3dc7b44f386268 Mon Sep 17 00:00:00 2001 From: "nengli.ln" Date: Wed, 5 Aug 2026 15:34:09 +0800 Subject: [PATCH 14/19] update Windows test --- .github/workflows/Windows.yml | 137 +++++++++++++++++++++++++++++++++- 1 file changed, 136 insertions(+), 1 deletion(-) diff --git a/.github/workflows/Windows.yml b/.github/workflows/Windows.yml index f9e03a062..d051478f8 100644 --- a/.github/workflows/Windows.yml +++ b/.github/workflows/Windows.yml @@ -113,22 +113,61 @@ jobs: # ======================================== # Phase 1: C++ Tests (ctest) + # Mirrors build-and-test-common.yml Phase 1. + # test_db_svc is skipped (BRPC not available on Windows). # ======================================== - name: Run C++ tests continue-on-error: true + env: + MODERN_GRAPH_DATA_DIR: ${{ github.workspace }}/example_dataset/modern_graph + COMPREHENSIVE_GRAPH_DATA_DIR: ${{ github.workspace }}/example_dataset/comprehensive_graph + FLEX_DATA_DIR: ${{ github.workspace }}/example_dataset/modern_graph + TEST_PATH: ${{ github.workspace }}/tests + TEST_RESOURCE: ${{ github.workspace }}/tests/compiler run: | cd build - ctest --output-on-failure --timeout 60 -j $env:NUMBER_OF_PROCESSORS + ctest -R storage_test -V + ctest -R transaction_test -V + ctest -R csr_test -V + ctest -R edge_table_test -V + ctest -R graph_view_test -V + ctest -R graph_snapshot_store_test -V + ctest -R utils_test -V + ctest -R execution_test -V + ctest -R gopt_test -V + ctest -R test_vertex_table -V + ctest -R schema_test -V + ctest -R test_mmap_container -V + ctest -R logical_delete_test -V + ctest -R test_connection -V + ctest -R test_request -V + ctest -R sink_test -V + ctest -R test_indexer -V + ctest -R temporary_graph_test -V + ctest -R copy_temp_test -V # ======================================== # Phase 2: Python Tests + # Mirrors build-and-test-common.yml Phase 3. + # Tests depending on bulk-loaded /tmp data are skipped + # (bulk_loader executable is not built on Windows). # ======================================== + - name: Install Python dev requirements + run: | + python -m pip install -r tools/python_bind/requirements_dev.txt + - name: Run Python DB init tests continue-on-error: true run: | cd tools/python_bind python -m pytest -sv tests/test_db_init.py --exitfirst + - name: Run Python DB connection test + continue-on-error: true + run: | + cd tools/python_bind + python -m pytest -sv tests/test_db_connection.py --exitfirst + - name: Run Python schema tests continue-on-error: true run: | @@ -146,3 +185,99 @@ jobs: run: | cd tools/python_bind python -m pytest -sv tests/test_query.py --exitfirst + + - name: Run Python path tests + continue-on-error: true + run: | + cd tools/python_bind + python -m pytest -sv tests/test_path.py --exitfirst + + - name: Run Python persistence tests + continue-on-error: true + run: | + cd tools/python_bind + python -m pytest -sv tests/test_persistence.py --exitfirst + + - name: Run Python call string literal tests + continue-on-error: true + run: | + cd tools/python_bind + python -m pytest -sv tests/test_call_string_literal.py --exitfirst + + - name: Run Python dataset tests + continue-on-error: true + run: | + cd tools/python_bind + python -m pytest -sv tests/test_dataset.py --exitfirst + + - name: Run Python merge tests + continue-on-error: true + run: | + cd tools/python_bind + python -m pytest -sv tests/test_merge.py --exitfirst + + - name: Run Python transaction tests + continue-on-error: true + run: | + cd tools/python_bind + python -m pytest -sv tests/test_db_transaction.py --exitfirst + + - name: Run Python import/export tests + continue-on-error: true + run: | + cd tools/python_bind + python -m pytest -sv tests/test_db_import_export.py --exitfirst + + - name: Run Python Data I/O documentation tests + continue-on-error: true + env: + FLEX_DATA_DIR: ${{ github.workspace }}/example_dataset/modern_graph + run: | + cd tools/python_bind + python -m pytest -sv tests/test_data_io_docs.py --exitfirst + + - name: Run Python LOAD FROM sniffer tests (CSV and JSON) + continue-on-error: true + run: | + cd tools/python_bind + python -m pytest -sv tests/test_sniffer.py -k "csv or json" --exitfirst + + - name: Run Python complex query tests + continue-on-error: true + run: | + cd tools/python_bind + python -m pytest -sv tests/test_db_cases.py --exitfirst + + - name: Run Python list/array tests + continue-on-error: true + run: | + cd tools/python_bind + python -m pytest -sv tests/test_db_list.py --exitfirst + python -m pytest -sv tests/test_db_array.py --exitfirst + + - name: Run Python concurrency tests + continue-on-error: true + run: | + cd tools/python_bind + python -m pytest -sv tests/test_db_concurrent.py --exitfirst + + - name: Run Python transaction concurrent tests + continue-on-error: true + run: | + cd tools/python_bind + python -m pytest -sv tests/transaction/elle_tester_insert.py --exitfirst + + - name: Run Python PyArrow related tests + continue-on-error: true + run: | + cd tools/python_bind + python -m pip install pyarrow==18.0.0 + python -m pytest -sv tests/test_to_arrow.py --exitfirst + python -m pip uninstall -y pyarrow + + - name: Run Python CLI tool tests + continue-on-error: true + run: | + cd tools/python_bind + python -m pytest -sv tests/test_ngcli_commands.py --exitfirst + python -m pytest -sv tests/test_ngcli_basics.py --exitfirst From d18dd02f15547178e9b703b80d2b01683391510e Mon Sep 17 00:00:00 2001 From: "nengli.ln" Date: Wed, 5 Aug 2026 17:05:26 +0800 Subject: [PATCH 15/19] fix python bind --- .github/workflows/Windows.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.github/workflows/Windows.yml b/.github/workflows/Windows.yml index d051478f8..830a26b24 100644 --- a/.github/workflows/Windows.yml +++ b/.github/workflows/Windows.yml @@ -111,6 +111,20 @@ jobs: Write-Host "Building with $jobs parallel jobs" cmake --build build -j $jobs + # Windows has no RPATH ($ORIGIN/@loader_path); DLLs are resolved via + # the DLL search order (PATH, same-dir-first). Add the directories + # that contain libneug.dll and vcpkg-installed DLLs so that + # neug_py_bind.pyd can find all its dependencies at import time. + - name: Add DLL directories to PATH + run: | + $vcpkgBin = Join-Path $env:VCPKG_ROOT "installed\x64-windows-static-md\bin" + $buildSrc = Join-Path $env:GITHUB_WORKSPACE "build\src" + $buildPyBind = Join-Path $env:GITHUB_WORKSPACE "build\tools\python_bind" + Write-Host "Adding to PATH: $vcpkgBin, $buildSrc, $buildPyBind" + echo "$vcpkgBin" >> $env:GITHUB_PATH + echo "$buildSrc" >> $env:GITHUB_PATH + echo "$buildPyBind" >> $env:GITHUB_PATH + # ======================================== # Phase 1: C++ Tests (ctest) # Mirrors build-and-test-common.yml Phase 1. From 74d87e1b7a71b752c186be737232097ab1c6e86e Mon Sep 17 00:00:00 2001 From: "nengli.ln" Date: Wed, 5 Aug 2026 19:31:13 +0800 Subject: [PATCH 16/19] temp commit --- .github/workflows/Windows.yml | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/.github/workflows/Windows.yml b/.github/workflows/Windows.yml index 830a26b24..a4685ce39 100644 --- a/.github/workflows/Windows.yml +++ b/.github/workflows/Windows.yml @@ -125,6 +125,37 @@ jobs: echo "$buildSrc" >> $env:GITHUB_PATH echo "$buildPyBind" >> $env:GITHUB_PATH + - name: Debug DLL dependencies + shell: pwsh + run: | + Write-Host "=== Listing .pyd and .dll files in build/tools/python_bind ===" + Get-ChildItem -Path "build\tools\python_bind" -Filter "*.pyd" | Format-Table FullName + Get-ChildItem -Path "build\tools\python_bind" -Filter "*.dll" | Format-Table FullName + + Write-Host "=== Listing .dll files in build/src ===" + Get-ChildItem -Path "build\src" -Filter "*.dll" -ErrorAction SilentlyContinue | Format-Table FullName + + $pyd = Get-ChildItem -Path "build\tools\python_bind" -Filter "neug_py_bind*.pyd" | Select-Object -First 1 + if ($pyd) { + Write-Host "=== dumpbin /dependents for $($pyd.FullName) ===" + & dumpbin /dependents $pyd.FullName + } else { + Write-Host "WARNING: neug_py_bind*.pyd not found in build\tools\python_bind" + Write-Host "=== Searching entire build tree for neug_py_bind ===" + Get-ChildItem -Path "build" -Recurse -Filter "neug_py_bind*" -ErrorAction SilentlyContinue | Format-Table FullName + } + + $neugDll = Get-ChildItem -Path "build" -Recurse -Filter "*neug*.dll" -ErrorAction SilentlyContinue | Select-Object -First 1 + if ($neugDll) { + Write-Host "=== Found neug DLL: $($neugDll.FullName) ===" + Write-Host "=== dumpbin /dependents for neug DLL ===" + & dumpbin /dependents $neugDll.FullName + } else { + Write-Host "WARNING: No *neug*.dll found in build tree" + Write-Host "=== Listing all .dll in build tree ===" + Get-ChildItem -Path "build" -Recurse -Filter "*.dll" -ErrorAction SilentlyContinue | Format-Table FullName + } + # ======================================== # Phase 1: C++ Tests (ctest) # Mirrors build-and-test-common.yml Phase 1. From 86b3417be3d62f970cf497834160a02f55c1c6a7 Mon Sep 17 00:00:00 2001 From: "nengli.ln" Date: Thu, 6 Aug 2026 10:16:44 +0800 Subject: [PATCH 17/19] fix dll --- .github/workflows/Windows.yml | 44 ++++++++++++++++------------------- 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/.github/workflows/Windows.yml b/.github/workflows/Windows.yml index a4685ce39..08e95bf32 100644 --- a/.github/workflows/Windows.yml +++ b/.github/workflows/Windows.yml @@ -125,36 +125,32 @@ jobs: echo "$buildSrc" >> $env:GITHUB_PATH echo "$buildPyBind" >> $env:GITHUB_PATH - - name: Debug DLL dependencies + # neug.dll links against OpenSSL dynamically (libssl-3-x64.dll / + # libcrypto-3-x64.dll). Copy them next to neug_py_bind.pyd so the + # Windows loader finds them via the same-directory rule. + - name: Copy OpenSSL DLLs next to Python bindings shell: pwsh run: | - Write-Host "=== Listing .pyd and .dll files in build/tools/python_bind ===" - Get-ChildItem -Path "build\tools\python_bind" -Filter "*.pyd" | Format-Table FullName - Get-ChildItem -Path "build\tools\python_bind" -Filter "*.dll" | Format-Table FullName - - Write-Host "=== Listing .dll files in build/src ===" - Get-ChildItem -Path "build\src" -Filter "*.dll" -ErrorAction SilentlyContinue | Format-Table FullName - - $pyd = Get-ChildItem -Path "build\tools\python_bind" -Filter "neug_py_bind*.pyd" | Select-Object -First 1 - if ($pyd) { - Write-Host "=== dumpbin /dependents for $($pyd.FullName) ===" - & dumpbin /dependents $pyd.FullName + $dest = Join-Path $env:GITHUB_WORKSPACE "build\tools\python_bind" + $vcpkgBin = Join-Path $env:VCPKG_ROOT "installed\x64-windows-static-md\bin" + $ssl = Join-Path $vcpkgBin "libssl-3-x64.dll" + $crypto = Join-Path $vcpkgBin "libcrypto-3-x64.dll" + if (Test-Path $ssl) { + Copy-Item $ssl $dest -Force + Write-Host "Copied $ssl to $dest" } else { - Write-Host "WARNING: neug_py_bind*.pyd not found in build\tools\python_bind" - Write-Host "=== Searching entire build tree for neug_py_bind ===" - Get-ChildItem -Path "build" -Recurse -Filter "neug_py_bind*" -ErrorAction SilentlyContinue | Format-Table FullName + Write-Host "WARNING: $ssl not found" + Get-ChildItem $vcpkgBin -Filter "libssl*.dll" -ErrorAction SilentlyContinue | Format-Table FullName } - - $neugDll = Get-ChildItem -Path "build" -Recurse -Filter "*neug*.dll" -ErrorAction SilentlyContinue | Select-Object -First 1 - if ($neugDll) { - Write-Host "=== Found neug DLL: $($neugDll.FullName) ===" - Write-Host "=== dumpbin /dependents for neug DLL ===" - & dumpbin /dependents $neugDll.FullName + if (Test-Path $crypto) { + Copy-Item $crypto $dest -Force + Write-Host "Copied $crypto to $dest" } else { - Write-Host "WARNING: No *neug*.dll found in build tree" - Write-Host "=== Listing all .dll in build tree ===" - Get-ChildItem -Path "build" -Recurse -Filter "*.dll" -ErrorAction SilentlyContinue | Format-Table FullName + Write-Host "WARNING: $crypto not found" + Get-ChildItem $vcpkgBin -Filter "libcrypto*.dll" -ErrorAction SilentlyContinue | Format-Table FullName } + Write-Host "=== Files in $dest ===" + Get-ChildItem $dest -Filter "*.dll" | Format-Table FullName # ======================================== # Phase 1: C++ Tests (ctest) From 3744ac8f84871a6525bd683a6b77570bf833f009 Mon Sep 17 00:00:00 2001 From: "nengli.ln" Date: Thu, 6 Aug 2026 14:47:39 +0800 Subject: [PATCH 18/19] fix ci --- .github/workflows/Windows.yml | 45 ++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/.github/workflows/Windows.yml b/.github/workflows/Windows.yml index 08e95bf32..fa60f5b54 100644 --- a/.github/workflows/Windows.yml +++ b/.github/workflows/Windows.yml @@ -132,24 +132,37 @@ jobs: shell: pwsh run: | $dest = Join-Path $env:GITHUB_WORKSPACE "build\tools\python_bind" - $vcpkgBin = Join-Path $env:VCPKG_ROOT "installed\x64-windows-static-md\bin" - $ssl = Join-Path $vcpkgBin "libssl-3-x64.dll" - $crypto = Join-Path $vcpkgBin "libcrypto-3-x64.dll" - if (Test-Path $ssl) { - Copy-Item $ssl $dest -Force - Write-Host "Copied $ssl to $dest" - } else { - Write-Host "WARNING: $ssl not found" - Get-ChildItem $vcpkgBin -Filter "libssl*.dll" -ErrorAction SilentlyContinue | Format-Table FullName + $srcBuild = Join-Path $env:GITHUB_WORKSPACE "build\src" + $vcpkgInstalled = Join-Path $env:VCPKG_ROOT "installed" + + # Search for OpenSSL DLLs across the entire vcpkg installation + # and the build tree (static triplet may not place them in bin/). + $searchDirs = @( + (Join-Path $vcpkgInstalled "x64-windows-static-md\bin"), + (Join-Path $vcpkgInstalled "x64-windows-static-md\tools"), + (Join-Path $vcpkgInstalled "x64-windows\bin"), + $srcBuild + ) + foreach ($d in $searchDirs) { + if (Test-Path $d) { + Write-Host "Searching: $d" + Get-ChildItem -Path $d -Recurse -Filter "libssl*.dll" -ErrorAction SilentlyContinue | ForEach-Object { Write-Host " Found: $($_.FullName)" } + Get-ChildItem -Path $d -Recurse -Filter "libcrypto*.dll" -ErrorAction SilentlyContinue | ForEach-Object { Write-Host " Found: $($_.FullName)" } + } } - if (Test-Path $crypto) { - Copy-Item $crypto $dest -Force - Write-Host "Copied $crypto to $dest" - } else { - Write-Host "WARNING: $crypto not found" - Get-ChildItem $vcpkgBin -Filter "libcrypto*.dll" -ErrorAction SilentlyContinue | Format-Table FullName + + # Copy all found OpenSSL DLLs to the destination + foreach ($d in $searchDirs) { + if (Test-Path $d) { + $ssl = Get-ChildItem -Path $d -Recurse -Filter "libssl-3-x64.dll" -ErrorAction SilentlyContinue | Select-Object -First 1 + $crypto = Get-ChildItem -Path $d -Recurse -Filter "libcrypto-3-x64.dll" -ErrorAction SilentlyContinue | Select-Object -First 1 + if ($ssl) { Copy-Item $ssl.FullName $dest -Force; Write-Host "Copied $($ssl.FullName) to $dest" } + if ($crypto) { Copy-Item $crypto.FullName $dest -Force; Write-Host "Copied $($crypto.FullName) to $dest" } + if ($ssl -and $crypto) { break } + } } - Write-Host "=== Files in $dest ===" + + Write-Host "=== All .dll files in $dest ===" Get-ChildItem $dest -Filter "*.dll" | Format-Table FullName # ======================================== From d9d35249d9be5909f31ecc27d47d23ced7b3e757 Mon Sep 17 00:00:00 2001 From: "nengli.ln" Date: Fri, 7 Aug 2026 14:17:01 +0800 Subject: [PATCH 19/19] fix link open ssl --- .github/workflows/Windows.yml | 84 +++++++++++------------------------ 1 file changed, 25 insertions(+), 59 deletions(-) diff --git a/.github/workflows/Windows.yml b/.github/workflows/Windows.yml index fa60f5b54..8af7677c4 100644 --- a/.github/workflows/Windows.yml +++ b/.github/workflows/Windows.yml @@ -74,9 +74,12 @@ jobs: python -m pip install -r tools/python_bind/requirements.txt python -m pip install pytest pytest-cov coverage - # vcpkg is pre-installed on GitHub-hosted Windows runners at C:\vcpkg. - # Setting VCPKG_ROOT lets CMake's vcpkg toolchain auto-install OpenSSL - # and other dependencies declared via find_package() in CMakeLists.txt. + # vcpkg is pre-installed on GitHub-hosted Windows runners at C:\vcpkg + # but ships with NO pre-built packages. In classic mode (no vcpkg.json) + # the toolchain does not auto-install missing packages, so we must + # install OpenSSL explicitly before CMake configure. Without this step + # CMake falls back to the system-level dynamic OpenSSL (DLL), which + # Python 3.8+ cannot load (PATH is not searched for DLLs). - name: Setup vcpkg run: | echo "VCPKG_ROOT=$env:VCPKG_INSTALLATION_ROOT" >> $env:GITHUB_ENV @@ -86,9 +89,26 @@ jobs: uses: actions/cache@v4 with: path: ${{ env.VCPKG_ROOT }}/installed - key: vcpkg-x64-windows-static-md-${{ hashFiles('CMakeLists.txt') }} + key: vcpkg-x64-windows-static-md-openssl-${{ hashFiles('CMakeLists.txt') }} restore-keys: | - vcpkg-x64-windows-static-md- + vcpkg-x64-windows-static-md-openssl- + + - name: Install OpenSSL via vcpkg + run: | + $vcpkgExe = Join-Path $env:VCPKG_ROOT "vcpkg.exe" + $triplet = "x64-windows-static-md" + $installDir = Join-Path $env:VCPKG_ROOT "installed\$triplet" + # Skip if already installed (cache hit) + if (Test-Path (Join-Path $installDir "lib\libssl.lib")) { + Write-Host "OpenSSL already installed in $installDir (cache hit)" + } else { + Write-Host "Installing openssl:$triplet via vcpkg" + & $vcpkgExe install "openssl:$triplet" + if ($LASTEXITCODE -ne 0) { + Write-Error "vcpkg failed to install openssl:$triplet" + exit 1 + } + } - name: Configure CMake run: | @@ -111,60 +131,6 @@ jobs: Write-Host "Building with $jobs parallel jobs" cmake --build build -j $jobs - # Windows has no RPATH ($ORIGIN/@loader_path); DLLs are resolved via - # the DLL search order (PATH, same-dir-first). Add the directories - # that contain libneug.dll and vcpkg-installed DLLs so that - # neug_py_bind.pyd can find all its dependencies at import time. - - name: Add DLL directories to PATH - run: | - $vcpkgBin = Join-Path $env:VCPKG_ROOT "installed\x64-windows-static-md\bin" - $buildSrc = Join-Path $env:GITHUB_WORKSPACE "build\src" - $buildPyBind = Join-Path $env:GITHUB_WORKSPACE "build\tools\python_bind" - Write-Host "Adding to PATH: $vcpkgBin, $buildSrc, $buildPyBind" - echo "$vcpkgBin" >> $env:GITHUB_PATH - echo "$buildSrc" >> $env:GITHUB_PATH - echo "$buildPyBind" >> $env:GITHUB_PATH - - # neug.dll links against OpenSSL dynamically (libssl-3-x64.dll / - # libcrypto-3-x64.dll). Copy them next to neug_py_bind.pyd so the - # Windows loader finds them via the same-directory rule. - - name: Copy OpenSSL DLLs next to Python bindings - shell: pwsh - run: | - $dest = Join-Path $env:GITHUB_WORKSPACE "build\tools\python_bind" - $srcBuild = Join-Path $env:GITHUB_WORKSPACE "build\src" - $vcpkgInstalled = Join-Path $env:VCPKG_ROOT "installed" - - # Search for OpenSSL DLLs across the entire vcpkg installation - # and the build tree (static triplet may not place them in bin/). - $searchDirs = @( - (Join-Path $vcpkgInstalled "x64-windows-static-md\bin"), - (Join-Path $vcpkgInstalled "x64-windows-static-md\tools"), - (Join-Path $vcpkgInstalled "x64-windows\bin"), - $srcBuild - ) - foreach ($d in $searchDirs) { - if (Test-Path $d) { - Write-Host "Searching: $d" - Get-ChildItem -Path $d -Recurse -Filter "libssl*.dll" -ErrorAction SilentlyContinue | ForEach-Object { Write-Host " Found: $($_.FullName)" } - Get-ChildItem -Path $d -Recurse -Filter "libcrypto*.dll" -ErrorAction SilentlyContinue | ForEach-Object { Write-Host " Found: $($_.FullName)" } - } - } - - # Copy all found OpenSSL DLLs to the destination - foreach ($d in $searchDirs) { - if (Test-Path $d) { - $ssl = Get-ChildItem -Path $d -Recurse -Filter "libssl-3-x64.dll" -ErrorAction SilentlyContinue | Select-Object -First 1 - $crypto = Get-ChildItem -Path $d -Recurse -Filter "libcrypto-3-x64.dll" -ErrorAction SilentlyContinue | Select-Object -First 1 - if ($ssl) { Copy-Item $ssl.FullName $dest -Force; Write-Host "Copied $($ssl.FullName) to $dest" } - if ($crypto) { Copy-Item $crypto.FullName $dest -Force; Write-Host "Copied $($crypto.FullName) to $dest" } - if ($ssl -and $crypto) { break } - } - } - - Write-Host "=== All .dll files in $dest ===" - Get-ChildItem $dest -Filter "*.dll" | Format-Table FullName - # ======================================== # Phase 1: C++ Tests (ctest) # Mirrors build-and-test-common.yml Phase 1.