From d95cf1a4dcea05fb085a3f5115ed42a79431c979 Mon Sep 17 00:00:00 2001 From: "Artem H." Date: Sun, 25 Oct 2026 12:51:23 +0100 Subject: [PATCH] feat: build and run HAL natively on Windows (MSYS2/UCRT64) --- CMakeLists.txt | 17 +++- app/CMakeLists.txt | 4 + app/hal.manifest | 13 +++ app/hal.rc | 2 + cmake/hal_plugin.cmake | 9 ++- deps/abc/CMakeLists.txt | 11 +++ deps/abc/src/aig/gia/giaKf.c | 2 +- deps/abc/src/base/cmd/cmdAuto.c | 2 +- deps/abc/src/base/cmd/cmdStarter.c | 2 +- deps/abc/src/base/wlc/wlcPth.c | 2 +- deps/abc/src/map/if/ifDsd.c | 2 +- deps/abc/src/map/if/ifTest.c | 2 +- deps/abc/src/proof/abs/absPth.c | 2 +- deps/abc/src/proof/cec/cecSplit.c | 2 +- deps/abc/src/sat/bmc/bmcBmcS.c | 2 +- deps/igraph-1.0.x/CMakeLists.txt | 6 +- deps/subprocess/subprocess/process.h | 2 +- include/hal_core/arch/arch_win.h | 2 +- include/hal_core/defines.h | 4 + .../netlist/boolean_function/solver.h | 4 +- .../hal_core/plugin_system/runtime_library.h | 9 ++- plugins/dot_viewer/src/dot_viewer_factory.cpp | 2 +- plugins/gate_libraries/CMakeLists.txt | 7 +- plugins/gexf_writer/CMakeLists.txt | 5 +- plugins/gexf_writer/src/gexf_writer.cpp | 2 +- .../include/graph_algorithm/netlist_graph.h | 6 ++ plugins/gui/CMakeLists.txt | 5 +- .../gui/content_manager/content_manager.h | 2 + .../src/content_manager/content_manager.cpp | 5 ++ .../gui/src/export/export_project_dialog.cpp | 2 +- plugins/gui/src/file_manager/file_manager.cpp | 6 +- .../src/file_manager/new_project_dialog.cpp | 2 +- .../gatelibrary_selection.cpp | 2 +- plugins/gui/src/main_window/main_window.cpp | 2 +- plugins/gui/src/plugin_gui.cpp | 2 +- .../src/plugin_relay/gui_plugin_manager.cpp | 5 +- plugins/gui/src/python/python_code_editor.cpp | 4 +- plugins/gui/src/python/python_context.cpp | 4 +- .../python_shell/src/plugin_python_shell.cpp | 2 +- .../boolean_function/simplification_abc.cpp | 16 ++++ src/netlist/boolean_function/solver.cpp | 6 +- .../gate_library/gate_library_manager.cpp | 2 +- src/netlist/netlist_factory.cpp | 9 +-- src/netlist/project_manager.cpp | 6 +- src/plugin_system/runtime_library.cpp | 26 +++++- src/plugin_system/user_feedback.cpp | 20 +++++ src/python_bindings/CMakeLists.txt | 5 +- src/utilities/log.cpp | 6 -- src/utilities/project_directory.cpp | 4 +- src/utilities/utils.cpp | 79 +++++++++++++------ 50 files changed, 259 insertions(+), 86 deletions(-) create mode 100644 app/hal.manifest create mode 100644 app/hal.rc diff --git a/CMakeLists.txt b/CMakeLists.txt index 57b60c4f7791..ab6dbdb76df5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -172,7 +172,8 @@ include(detect_dependencies) # ############################### # Set UNIX (macOS or Linux) compiler flags (clang or gcc). Set optimization level for debug or release. -if(UNIX) +# MinGW is gcc as well, so it takes the same flags. +if(UNIX OR MINGW) message(STATUS "Checking supported compiler flags...") enable_c_compile_option_if_supported("-Wall" "" "PRIVATE") @@ -229,8 +230,20 @@ if(UNIX) message(VERBOSE "COMPILE_OPTIONS_PRIVATE: ${COMPILE_OPTIONS_PRIVATE}") message(VERBOSE "COMPILE_OPTIONS_PUBLIC: ${COMPILE_OPTIONS_PUBLIC}") message(VERBOSE "COMPILE_OPTIONS_INTERFACE: ${COMPILE_OPTIONS_INTERFACE}") + if(MINGW) + # the python bindings and the gui exceed the default COFF section limit + enable_cxx_compile_option_if_supported("-Wa,-mbig-obj" "" "PUBLIC") + + # HAL marks its api with CORE_API/NETLIST_API/PLUGIN_API, which expand to nothing on MinGW, + # so let the linker export everything instead of annotating every symbol by hand. + # ld only exports everything by default while nothing is marked dllexport, and pybind11 + # marks its module entry points, so the flag has to be passed explicitly. + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--export-all-symbols") + set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--export-all-symbols") + endif(MINGW) + message(VERBOSE " ") -endif(UNIX) +endif(UNIX OR MINGW) # Enable Debug Symbols for Libcpp on macOS if(${CMAKE_BUILD_TYPE} EQUAL "Debug" AND APPLE AND CMAKE_HOST_APPLE) diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index e7e932433718..9c99c30a68bc 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -1,6 +1,10 @@ set(SRC main.cpp) add_executable(hal ${SRC}) +if(MINGW) + # UTF-8 active code page, see hal.manifest + target_sources(hal PRIVATE hal.rc) +endif() set_target_properties(hal PROPERTIES OUTPUT_NAME "hal") set_target_properties(hal PROPERTIES ENABLE_EXPORTS 1) set_target_properties(hal PROPERTIES INSTALL_RPATH "${CMAKE_INSTALL_RPATH}") diff --git a/app/hal.manifest b/app/hal.manifest new file mode 100644 index 000000000000..e6193d09ae89 --- /dev/null +++ b/app/hal.manifest @@ -0,0 +1,13 @@ + + + + + + UTF-8 + + + diff --git a/app/hal.rc b/app/hal.rc new file mode 100644 index 000000000000..7b6eb8c59921 --- /dev/null +++ b/app/hal.rc @@ -0,0 +1,2 @@ +// embeds hal.manifest as the application manifest (resource type 24 = RT_MANIFEST) +1 24 "hal.manifest" diff --git a/cmake/hal_plugin.cmake b/cmake/hal_plugin.cmake index f6bbb790c33a..4e581c49cf53 100644 --- a/cmake/hal_plugin.cmake +++ b/cmake/hal_plugin.cmake @@ -13,6 +13,10 @@ function(hal_add_plugin target_name) message(FATAL_ERROR "Can't be both MODULE and SHARED") elseif(ARG_SHARED) set(lib_type SHARED) + elseif(WIN32) + # plugins link against each other, which needs an import library, and Windows does not + # produce one for a MODULE library + set(lib_type SHARED) else() set(lib_type MODULE) endif() @@ -54,7 +58,10 @@ function(hal_add_plugin target_name) if(APPLE AND CMAKE_HOST_APPLE) set_target_properties(${target_name} PROPERTIES SUFFIX ".so") set_target_properties(${target_name} PROPERTIES INSTALL_NAME_DIR ${PLUGIN_LIBRARY_INSTALL_DIRECTORY}) - endif(APPLE AND CMAKE_HOST_APPLE) + elseif(WIN32) + # Python only imports extension modules with the .pyd suffix on Windows + set_target_properties(${target_name} PROPERTIES SUFFIX ".pyd") + endif() target_link_libraries(${target_name} PUBLIC hal::core hal::netlist ${Python3_LIBRARIES} pybind11::pybind11 diff --git a/deps/abc/CMakeLists.txt b/deps/abc/CMakeLists.txt index f2d08ac38b64..d028c6c91de4 100644 --- a/deps/abc/CMakeLists.txt +++ b/deps/abc/CMakeLists.txt @@ -67,6 +67,11 @@ extract_var(SEPARATOR_LIBS ABC_LIBS ${MAKE_OUTPUT}) extract_var(SEPARATOR_CFLAGS ABC_CFLAGS ${MAKE_OUTPUT}) extract_var(SEPARATOR_CXXFLAGS ABC_CXXFLAGS ${MAKE_OUTPUT}) +if(WIN32) + # dlopen and the realtime clock live in the CRT on Windows, there is no -ldl or -lrt + list(REMOVE_ITEM ABC_LIBS "-ldl" "-lrt") +endif() + if(ABC_USE_NAMESPACE) set_source_files_properties(${ABC_SRC} PROPERTIES LANGUAGE CXX) endif() @@ -90,5 +95,11 @@ list(REMOVE_ITEM ABC_SRC ${ABC_MAIN_SRC}) add_library(libabc-pic EXCLUDE_FROM_ALL ${ABC_SRC}) abc_properties(libabc-pic PUBLIC) + +if(WIN32) + # without this abc declares its whole api __declspec(dllimport), including inside the library + # itself; MinGW exports and imports the symbols on its own + target_compile_definitions(libabc-pic PUBLIC WIN32_NO_DLL) +endif() set_property(TARGET libabc-pic PROPERTY POSITION_INDEPENDENT_CODE ON) set_property(TARGET libabc-pic PROPERTY OUTPUT_NAME abc-pic) diff --git a/deps/abc/src/aig/gia/giaKf.c b/deps/abc/src/aig/gia/giaKf.c index a823f7262186..0cbb125addcb 100644 --- a/deps/abc/src/aig/gia/giaKf.c +++ b/deps/abc/src/aig/gia/giaKf.c @@ -23,7 +23,7 @@ #ifdef ABC_USE_PTHREADS -#ifdef _WIN32 +#if defined(_WIN32) && !defined(__MINGW32__) #include "../lib/pthread.h" #else #include diff --git a/deps/abc/src/base/cmd/cmdAuto.c b/deps/abc/src/base/cmd/cmdAuto.c index 8151c2e53458..98cb6a5a2a60 100644 --- a/deps/abc/src/base/cmd/cmdAuto.c +++ b/deps/abc/src/base/cmd/cmdAuto.c @@ -29,7 +29,7 @@ #ifdef ABC_USE_PTHREADS -#ifdef _WIN32 +#if defined(_WIN32) && !defined(__MINGW32__) #include "../lib/pthread.h" #else #include diff --git a/deps/abc/src/base/cmd/cmdStarter.c b/deps/abc/src/base/cmd/cmdStarter.c index ffb2b752206f..4cea6e660d75 100644 --- a/deps/abc/src/base/cmd/cmdStarter.c +++ b/deps/abc/src/base/cmd/cmdStarter.c @@ -27,7 +27,7 @@ #ifdef ABC_USE_PTHREADS -#ifdef _WIN32 +#if defined(_WIN32) && !defined(__MINGW32__) #include "../lib/pthread.h" #else #include diff --git a/deps/abc/src/base/wlc/wlcPth.c b/deps/abc/src/base/wlc/wlcPth.c index ddafab23ca57..5a699e888ac4 100644 --- a/deps/abc/src/base/wlc/wlcPth.c +++ b/deps/abc/src/base/wlc/wlcPth.c @@ -23,7 +23,7 @@ #ifdef ABC_USE_PTHREADS -#ifdef _WIN32 +#if defined(_WIN32) && !defined(__MINGW32__) #include "../lib/pthread.h" #else #include diff --git a/deps/abc/src/map/if/ifDsd.c b/deps/abc/src/map/if/ifDsd.c index ddfc10365a6c..69cb50b7a8fe 100644 --- a/deps/abc/src/map/if/ifDsd.c +++ b/deps/abc/src/map/if/ifDsd.c @@ -32,7 +32,7 @@ #ifdef ABC_USE_PTHREADS -#ifdef _WIN32 +#if defined(_WIN32) && !defined(__MINGW32__) #include "../lib/pthread.h" #else #include diff --git a/deps/abc/src/map/if/ifTest.c b/deps/abc/src/map/if/ifTest.c index fa9a644437db..4b1ede06f5cf 100644 --- a/deps/abc/src/map/if/ifTest.c +++ b/deps/abc/src/map/if/ifTest.c @@ -23,7 +23,7 @@ #ifdef ABC_USE_PTHREADS -#ifdef _WIN32 +#if defined(_WIN32) && !defined(__MINGW32__) #include "../lib/pthread.h" #else #include diff --git a/deps/abc/src/proof/abs/absPth.c b/deps/abc/src/proof/abs/absPth.c index f04a20d1c677..d8cb343598e7 100644 --- a/deps/abc/src/proof/abs/absPth.c +++ b/deps/abc/src/proof/abs/absPth.c @@ -25,7 +25,7 @@ #ifdef ABC_USE_PTHREADS -#ifdef _WIN32 +#if defined(_WIN32) && !defined(__MINGW32__) #include "../lib/pthread.h" #else #include diff --git a/deps/abc/src/proof/cec/cecSplit.c b/deps/abc/src/proof/cec/cecSplit.c index e2fa8ff1da90..f89a00aa9b48 100644 --- a/deps/abc/src/proof/cec/cecSplit.c +++ b/deps/abc/src/proof/cec/cecSplit.c @@ -28,7 +28,7 @@ #ifdef ABC_USE_PTHREADS -#ifdef _WIN32 +#if defined(_WIN32) && !defined(__MINGW32__) #include "../lib/pthread.h" #else #include diff --git a/deps/abc/src/sat/bmc/bmcBmcS.c b/deps/abc/src/sat/bmc/bmcBmcS.c index 8bfda56ba755..f43722c9e878 100644 --- a/deps/abc/src/sat/bmc/bmcBmcS.c +++ b/deps/abc/src/sat/bmc/bmcBmcS.c @@ -48,7 +48,7 @@ #ifdef ABC_USE_PTHREADS -#ifdef _WIN32 +#if defined(_WIN32) && !defined(__MINGW32__) #include "../lib/pthread.h" #else #include diff --git a/deps/igraph-1.0.x/CMakeLists.txt b/deps/igraph-1.0.x/CMakeLists.txt index 63b9191ace0c..8b50ca1eff76 100644 --- a/deps/igraph-1.0.x/CMakeLists.txt +++ b/deps/igraph-1.0.x/CMakeLists.txt @@ -5,7 +5,11 @@ set (IGRAPH_BASE "${CMAKE_BINARY_DIR}/igraph") set (IGRAPH_DOWNLOAD "${IGRAPH_BASE}/download") set (IGRAPH_BUILD "${IGRAPH_BASE}/build") set (IGRAPH_INCLUDES "${IGRAPH_INSTALL}/include") -set (IGRAPH_LIB "${IGRAPH_INSTALL}/lib/libigraph${CMAKE_SHARED_LIBRARY_SUFFIX}") +if(WIN32) + set (IGRAPH_LIB "${IGRAPH_INSTALL}/lib/${CMAKE_IMPORT_LIBRARY_PREFIX}igraph${CMAKE_IMPORT_LIBRARY_SUFFIX}") +else() + set (IGRAPH_LIB "${IGRAPH_INSTALL}/lib/libigraph${CMAKE_SHARED_LIBRARY_SUFFIX}") +endif() set (HAVE_IGRAPH TRUE) message("-- IGRAPH version 1.0.1 : download and build libigraph${CMAKE_SHARED_LIBRARY_SUFFIX} in ${IGRAPH_BASE}") diff --git a/deps/subprocess/subprocess/process.h b/deps/subprocess/subprocess/process.h index 4187b0ecfcb8..71a317de37dc 100644 --- a/deps/subprocess/subprocess/process.h +++ b/deps/subprocess/subprocess/process.h @@ -149,7 +149,7 @@ namespace subprocess //-------------------------------------------------------------------- //Environment Variable types -#ifndef _MSC_VER +#ifndef __USING_WINDOWS__ using env_string_t = std::string; using env_char_t = char; #else diff --git a/include/hal_core/arch/arch_win.h b/include/hal_core/arch/arch_win.h index a0c074bf0a40..b66881a724cc 100644 --- a/include/hal_core/arch/arch_win.h +++ b/include/hal_core/arch/arch_win.h @@ -33,7 +33,7 @@ #define LIBRARY_FILE_EXTENSION "dll" -#define ALTERNATE_LIBRARY_FILE_EXTENSION "" +#define ALTERNATE_LIBRARY_FILE_EXTENSION "pyd" #ifdef BUILDING_CORE #undef CORE_API diff --git a/include/hal_core/defines.h b/include/hal_core/defines.h index 71cc23bb2c7f..986c02abae0c 100644 --- a/include/hal_core/defines.h +++ b/include/hal_core/defines.h @@ -61,6 +61,10 @@ namespace hal /* Microsoft */ #define EXPORT __declspec(dllexport) #define IMPORT __declspec(dllimport) +#elif defined(_WIN32) +/* MinGW: symbols are exported via CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS, not per-symbol attributes */ +#define EXPORT +#define IMPORT #else /* other */ #define EXPORTED __attribute__((visibility("default"))) diff --git a/include/hal_core/netlist/boolean_function/solver.h b/include/hal_core/netlist/boolean_function/solver.h index b0085c8956a1..e56f80defd41 100644 --- a/include/hal_core/netlist/boolean_function/solver.h +++ b/include/hal_core/netlist/boolean_function/solver.h @@ -165,11 +165,11 @@ namespace hal * https://smtlib.cs.uiowa.edu/language.shtml * * @param[in] was_killed - Indicates whether solver process was killed. - * @param[in] stdout - Stdout of solver process. + * @param[in] solver_output - Stdout of solver process. * @param[in] config - SMT solver configuration. * @returns Ok() and SMT solver result on success, Err() otherwise. */ - static Result translate_from_smt2(bool was_killed, std::string stdout, const QueryConfig& config); + static Result translate_from_smt2(bool was_killed, std::string solver_output, const QueryConfig& config); }; } // namespace SMT } // namespace hal \ No newline at end of file diff --git a/include/hal_core/plugin_system/runtime_library.h b/include/hal_core/plugin_system/runtime_library.h index f7f116d08a43..72dd919823f8 100644 --- a/include/hal_core/plugin_system/runtime_library.h +++ b/include/hal_core/plugin_system/runtime_library.h @@ -29,15 +29,20 @@ #include +// operating system headers have to be included at global scope, not inside namespace hal +#ifdef _WIN32 +#include +#else +#include +#endif + namespace hal { // operating systems independent type definitions for function and handle access #ifdef _WIN32 -#include using lib_fn_ptr_t = FARPROC; using handle_ptr_t = HINSTANCE; #else -#include using lib_fn_ptr_t = void (*)(); using handle_ptr_t = void*; #endif diff --git a/plugins/dot_viewer/src/dot_viewer_factory.cpp b/plugins/dot_viewer/src/dot_viewer_factory.cpp index 540c99126f31..bc2e3c306f7a 100644 --- a/plugins/dot_viewer/src/dot_viewer_factory.cpp +++ b/plugins/dot_viewer/src/dot_viewer_factory.cpp @@ -46,7 +46,7 @@ namespace hal { QJsonObject dvObj; std::filesystem::path relDotPath = ProjectManager::instance()->get_project_directory().get_relative_file_path(dv->filename().toStdString()); - dvObj["filename"] = QString::fromStdString(relDotPath); + dvObj["filename"] = QString::fromStdString(relDotPath.string()); dvObj["creator"] = dv->creatorPlugin(); dvFile.write(QJsonDocument(dvObj).toJson(QJsonDocument::Compact)); diff --git a/plugins/gate_libraries/CMakeLists.txt b/plugins/gate_libraries/CMakeLists.txt index 7a5331ea2242..ead10fda447e 100644 --- a/plugins/gate_libraries/CMakeLists.txt +++ b/plugins/gate_libraries/CMakeLists.txt @@ -7,6 +7,9 @@ if(PL_GATE_LIBRARIES OR BUILD_ALL_PLUGINS) set_property(GLOBAL PROPERTY GATE_LIBS_DEFINITIONS "${CMAKE_CURRENT_SOURCE_DIR}/definitions") message(STATUS "GATE_LIBS: ${GATE_LIBS}") foreach(file ${GATE_LIBS}) + if(IS_DIRECTORY ${file}) + continue() + endif() get_filename_component(f ${file} NAME) list(APPEND SOURCE_GATE_LIBS ${CMAKE_CURRENT_SOURCE_DIR}/definitions/${f}) list(APPEND DESTINATION_GATE_LIBS ${CMAKE_BINARY_DIR}/share/hal/gate_libraries/${f}) @@ -24,8 +27,8 @@ if(PL_GATE_LIBRARIES OR BUILD_ALL_PLUGINS) add_custom_target(update_internal_gate_library_definitions ALL BYPRODUCTS ${DESTINATION_GATE_LIBS} ${DESTINATION_HELPER_GATE_LIBS} - COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/definitions/* ${CMAKE_BINARY_DIR}/share/hal/gate_libraries/ - COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/definitions/helper_libs/* ${CMAKE_BINARY_DIR}/share/hal/gate_libraries/ + COMMAND ${CMAKE_COMMAND} -E copy ${SOURCE_GATE_LIBS} ${CMAKE_BINARY_DIR}/share/hal/gate_libraries/ + COMMAND ${CMAKE_COMMAND} -E copy ${SOURCE_HELPER_GATE_LIBS} ${CMAKE_BINARY_DIR}/share/hal/gate_libraries/ DEPENDS ${SOURCE_GATE_LIBS} ${SOURCE_HELPER_GATE_LIBS} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMENT "Update gate library definitions") diff --git a/plugins/gexf_writer/CMakeLists.txt b/plugins/gexf_writer/CMakeLists.txt index 37c9060b1ea0..0a7412b1e4c3 100644 --- a/plugins/gexf_writer/CMakeLists.txt +++ b/plugins/gexf_writer/CMakeLists.txt @@ -41,7 +41,10 @@ if(PL_GEXF_WRITER OR BUILD_ALL_PLUGINS) if(APPLE AND CMAKE_HOST_APPLE) set_target_properties(gexf_writer PROPERTIES SUFFIX ".so") set_target_properties(gexf_writer PROPERTIES INSTALL_NAME_DIR ${PLUGIN_LIBRARY_INSTALL_DIRECTORY}) - endif(APPLE AND CMAKE_HOST_APPLE) + elseif(WIN32) + # Python only imports extension modules with the .pyd suffix on Windows + set_target_properties(gexf_writer PROPERTIES SUFFIX ".pyd") + endif() target_link_libraries(gexf_writer PUBLIC gui diff --git a/plugins/gexf_writer/src/gexf_writer.cpp b/plugins/gexf_writer/src/gexf_writer.cpp index 9a61b38c6780..7ffdbe5e9468 100644 --- a/plugins/gexf_writer/src/gexf_writer.cpp +++ b/plugins/gexf_writer/src/gexf_writer.cpp @@ -37,7 +37,7 @@ namespace hal { return ERR("could not write netlist to GEXF file '" + file_path.string() + "': file path is empty"); } - QString filename = QString::fromStdString(file_path); + QString filename = QString::fromStdString(file_path.string()); if (!filename.endsWith(".gexf")) { diff --git a/plugins/graph_algorithm/include/graph_algorithm/netlist_graph.h b/plugins/graph_algorithm/include/graph_algorithm/netlist_graph.h index ee8fe70631d1..6be0d5300fe9 100644 --- a/plugins/graph_algorithm/include/graph_algorithm/netlist_graph.h +++ b/plugins/graph_algorithm/include/graph_algorithm/netlist_graph.h @@ -63,6 +63,12 @@ namespace hal * @enum Direction * @brief The direction of exploration within the graph. */ +// windows.h defines IN and OUT as empty SAL annotation macros +#ifdef _WIN32 +#undef IN +#undef OUT +#endif + enum class Direction { /** diff --git a/plugins/gui/CMakeLists.txt b/plugins/gui/CMakeLists.txt index e80d143a2582..b77a7ffc5763 100644 --- a/plugins/gui/CMakeLists.txt +++ b/plugins/gui/CMakeLists.txt @@ -107,7 +107,10 @@ if(PL_GUI OR BUILD_ALL_PLUGINS) set_target_properties(gui PROPERTIES PREFIX "") if(APPLE AND CMAKE_HOST_APPLE) set_target_properties(gui PROPERTIES SUFFIX ".so") - endif(APPLE AND CMAKE_HOST_APPLE) + elseif(WIN32) + # Python only imports extension modules with the .pyd suffix on Windows + set_target_properties(gui PROPERTIES SUFFIX ".pyd") + endif() target_include_directories(gui PUBLIC $ diff --git a/plugins/gui/include/gui/content_manager/content_manager.h b/plugins/gui/include/gui/content_manager/content_manager.h index 135d88019ecd..f21a17e9428c 100644 --- a/plugins/gui/include/gui/content_manager/content_manager.h +++ b/plugins/gui/include/gui/content_manager/content_manager.h @@ -65,6 +65,8 @@ namespace hal QString mPluginName; public: ContentFactory(const QString& nam=QString()) : mPluginName(nam) {;} + // factories are deleted through this base, and may have further bases such as ProjectSerializer + virtual ~ContentFactory() {;} QString name() const {return mPluginName; } virtual ExternalContentWidget* contentFactory() const = 0; }; diff --git a/plugins/gui/src/content_manager/content_manager.cpp b/plugins/gui/src/content_manager/content_manager.cpp index de2cf8c34d5e..f7849ae41349 100644 --- a/plugins/gui/src/content_manager/content_manager.cpp +++ b/plugins/gui/src/content_manager/content_manager.cpp @@ -54,7 +54,12 @@ namespace hal auto it = begin(); while (it != end()) if ((*it)->name() == pluginName) + { + // the factory lives as long as its plugin, deleting it here (while the plugin is still loaded) + // also unregisters it from everything it registered with, e.g. as a project serializer + delete *it; it = erase(it); + } else ++it; diff --git a/plugins/gui/src/export/export_project_dialog.cpp b/plugins/gui/src/export/export_project_dialog.cpp index 8dc8d54e85ab..f0b02c5897fa 100644 --- a/plugins/gui/src/export/export_project_dialog.cpp +++ b/plugins/gui/src/export/export_project_dialog.cpp @@ -19,7 +19,7 @@ namespace hal ExportProjectDialog::ExportProjectDialog(QWidget *parent) : QFileDialog(parent), mStatus(NoExport) { - QFileInfo pinfo(QString::fromStdString(ProjectManager::instance()->get_project_directory().get_default_filename())); + QFileInfo pinfo(QString::fromStdString(ProjectManager::instance()->get_project_directory().get_default_filename().string())); mProjectName = pinfo.baseName().simplified(); mProjectName.replace(" ",""); setDefaultSuffix(".zip"); diff --git a/plugins/gui/src/file_manager/file_manager.cpp b/plugins/gui/src/file_manager/file_manager.cpp index f9e3282c8016..365dfeeb015d 100644 --- a/plugins/gui/src/file_manager/file_manager.cpp +++ b/plugins/gui/src/file_manager/file_manager.cpp @@ -259,7 +259,7 @@ namespace hal { ProjectManager* pm = ProjectManager::instance(); if (pm->get_project_status() == ProjectManager::ProjectStatus::NONE) return; - QDir shDir(QString::fromStdString(pm->get_project_directory().get_shadow_dir())); + QDir shDir(QString::fromStdString(pm->get_project_directory().get_shadow_dir().string())); if (shDir.exists()) { shDir.removeRecursively(); @@ -282,6 +282,8 @@ namespace hal QMessageBox::warning(qApp->activeWindow(),"Aborted", "Cannot create project <" + projdir + ">, no gate library selected"); return; } + QString glSuffix = QFileInfo(gatelib).suffix(); + gPluginRelay->mGuiPluginTable->loadFeature(FacExtensionInterface::FacGatelibParser,"."+glSuffix); GateLibrary* glib = gate_library_manager::load(gatelib.toStdString()); if (!glib) { @@ -453,7 +455,7 @@ namespace hal ProjectManager* pm = ProjectManager::instance(); pm->set_project_directory(projPath.toStdString()); - QDir shDir(QString::fromStdString(pm->get_project_directory().get_shadow_dir())); + QDir shDir(QString::fromStdString(pm->get_project_directory().get_shadow_dir().string())); QFileInfo shInfo(shDir.absoluteFilePath(QString::fromStdString(ProjectManager::s_project_file))); if (shInfo.exists() && shInfo.isFile()) { diff --git a/plugins/gui/src/file_manager/new_project_dialog.cpp b/plugins/gui/src/file_manager/new_project_dialog.cpp index 4ea28bc4f816..02e2798111a1 100644 --- a/plugins/gui/src/file_manager/new_project_dialog.cpp +++ b/plugins/gui/src/file_manager/new_project_dialog.cpp @@ -65,7 +65,7 @@ namespace hal for (const std::filesystem::path& path : gate_library_manager::get_all_path()) { int n = mGateLibraryPath.size(); - QString qName = QString::fromStdString(path.filename()); + QString qName = QString::fromStdString(path.filename().string()); mGateLibraryMap.insert(qName, n); QString qPath = QString::fromStdString(path.string()); mGateLibraryPath.append(qPath); diff --git a/plugins/gui/src/gatelibrary_management/gatelibrary_selection.cpp b/plugins/gui/src/gatelibrary_management/gatelibrary_selection.cpp index 2090c9e05738..ea602ed3a247 100644 --- a/plugins/gui/src/gatelibrary_management/gatelibrary_selection.cpp +++ b/plugins/gui/src/gatelibrary_management/gatelibrary_selection.cpp @@ -133,7 +133,7 @@ namespace hal { QMap nameMap; for (const std::filesystem::path& path : gate_library_manager::get_all_path()) { - QString name = QString::fromStdString(path.filename()); + QString name = QString::fromStdString(path.filename().string()); mEntries.append(GateLibrarySelectionEntry(name, QString::fromStdString(path.string()), nameMap[name]++)); } } diff --git a/plugins/gui/src/main_window/main_window.cpp b/plugins/gui/src/main_window/main_window.cpp index c35c6607236f..bc314c6d8513 100644 --- a/plugins/gui/src/main_window/main_window.cpp +++ b/plugins/gui/src/main_window/main_window.cpp @@ -854,7 +854,7 @@ namespace hal } } - QString qNetlistPath = QString::fromStdString(pm->get_project_directory().get_default_filename(".hal")); + QString qNetlistPath = QString::fromStdString(pm->get_project_directory().get_default_filename(".hal").string()); if (!pm->serialize_project(gNetlist)) { diff --git a/plugins/gui/src/plugin_gui.cpp b/plugins/gui/src/plugin_gui.cpp index e4068f376386..29605f390052 100644 --- a/plugins/gui/src/plugin_gui.cpp +++ b/plugins/gui/src/plugin_gui.cpp @@ -117,7 +117,7 @@ namespace hal FileManager::get_instance()->openProject(QString::fromStdString(projDir.string())); break; case DefaultImport: - projDir = ProjectDirectory(fileName); + projDir = ProjectDirectory(fileName.string()); [[fallthrough]]; // continue with Import case ImportToProject: diff --git a/plugins/gui/src/plugin_relay/gui_plugin_manager.cpp b/plugins/gui/src/plugin_relay/gui_plugin_manager.cpp index 455f58d7e14e..5f338625d7f2 100644 --- a/plugins/gui/src/plugin_relay/gui_plugin_manager.cpp +++ b/plugins/gui/src/plugin_relay/gui_plugin_manager.cpp @@ -373,7 +373,7 @@ namespace hal { void GuiPluginTable::populateTable(bool refresh) { - QDir userConfigDir(QString::fromStdString(utils::get_user_config_directory())); + QDir userConfigDir(QString::fromStdString(utils::get_user_config_directory().string())); QMap pluginEntries; mSettings = new QSettings(userConfigDir.absoluteFilePath("plugins.ini"),QSettings::IniFormat); @@ -489,6 +489,9 @@ namespace hal { { // unload dependencies unless they are required from other side if (pluginEntries.contains(dep) && pluginEntries.value(dep)->mLoadState > GuiPluginEntry::NotLoaded) continue; + // a dependency that was loaded before the dummy load is not ours to unload, this + // covers hal_gui itself, which a plugin like dot_viewer depends on + if (pluginsBeforeLoad.find(dep.toStdString()) != pluginsBeforeLoad.end()) continue; plugin_manager::unload(dep.toStdString()); } } diff --git a/plugins/gui/src/python/python_code_editor.cpp b/plugins/gui/src/python/python_code_editor.cpp index dc547040d75d..538b69e37a07 100644 --- a/plugins/gui/src/python/python_code_editor.cpp +++ b/plugins/gui/src/python/python_code_editor.cpp @@ -314,14 +314,14 @@ namespace hal { if (mRelFilename.isEmpty() || mRelFilename.startsWith('/')) return mRelFilename; - QDir projectPydir(QString::fromStdString(ProjectManager::instance()->get_project_directory().get_filename("py"))); + QDir projectPydir(QString::fromStdString(ProjectManager::instance()->get_project_directory().get_filename("py").string())); return projectPydir.absoluteFilePath(mRelFilename); } void PythonCodeEditor::setFilename(const QString &name) { mRelFilename = name; - QString projectLoc = QString::fromStdString(ProjectManager::instance()->get_project_directory().get_filename("py")) + '/'; + QString projectLoc = QString::fromStdString(ProjectManager::instance()->get_project_directory().get_filename("py").string()) + '/'; if (name.startsWith(projectLoc)) mRelFilename.remove(0,projectLoc.size()); } diff --git a/plugins/gui/src/python/python_context.cpp b/plugins/gui/src/python/python_context.cpp index e3820e299f1d..20e1bf91c0a9 100644 --- a/plugins/gui/src/python/python_context.cpp +++ b/plugins/gui/src/python/python_context.cpp @@ -104,9 +104,9 @@ namespace hal "import io, sys, threading, builtins\n"; for (auto path : utils::get_plugin_directories()) { - command += "sys.path.append('" + path.string() + "')\n"; + command += "sys.path.append('" + path.generic_string() + "')\n"; } - command += "sys.path.append('" + utils::get_library_directory().string() + command += "sys.path.append('" + utils::get_library_directory().generic_string() + "')\n" "from hal_gui.console import reset\n" "from hal_gui.console import clear\n" diff --git a/plugins/python_shell/src/plugin_python_shell.cpp b/plugins/python_shell/src/plugin_python_shell.cpp index 4f4c8cc75534..3fe3423ccd0e 100644 --- a/plugins/python_shell/src/plugin_python_shell.cpp +++ b/plugins/python_shell/src/plugin_python_shell.cpp @@ -79,7 +79,7 @@ namespace hal PySys_SetArgv(argc, argv); PyRun_SimpleString("import sys"); - PyRun_SimpleString(std::string("sys.path.append(\"" + utils::get_library_directory().string() + "\")").c_str()); + PyRun_SimpleString(std::string("sys.path.append(\"" + utils::get_library_directory().generic_string() + "\")").c_str()); PyRun_SimpleString("from hal_py import *"); PyRun_SimpleString("import hal_py"); diff --git a/src/netlist/boolean_function/simplification_abc.cpp b/src/netlist/boolean_function/simplification_abc.cpp index a1e8a8899942..d96551cfb3f3 100644 --- a/src/netlist/boolean_function/simplification_abc.cpp +++ b/src/netlist/boolean_function/simplification_abc.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -797,12 +798,27 @@ namespace hal // (2.2) compute the right-hand side Boolean function expression via // in-memory FILE* to mimic the original interface without the // use of an actual file read/write operation +#ifdef _WIN32 + // Windows has no open_memstream, so the data is round-tripped through a temporary file + auto stream = std::tmpfile(); + Hop_ObjPrintVerilog(stream, (Hop_Obj_t*)node->pData, vLevels, 0, 0); + std::fflush(stream); + std::rewind(stream); + + std::string rhs; + char chunk[4096]; + while (const auto read = std::fread(chunk, 1, sizeof(chunk), stream)) + { + rhs.append(chunk, read); + } +#else char* rhs{}; size_t len{}; auto stream = open_memstream(&rhs, &len); Hop_ObjPrintVerilog(stream, (Hop_Obj_t*)node->pData, vLevels, 0, 0); fflush(stream); +#endif // (2.3) assemble "assign $(LHS) = $(RHS);" ss << "assign " << lhs << " = " << rhs << ";" << std::endl; diff --git a/src/netlist/boolean_function/solver.cpp b/src/netlist/boolean_function/solver.cpp index 0ae33dc15867..d6afa12a0b34 100644 --- a/src/netlist/boolean_function/solver.cpp +++ b/src/netlist/boolean_function/solver.cpp @@ -539,15 +539,15 @@ namespace hal return ERR_APPEND(constraints_str.get_error(), "could not translate constraint to SMT-LIB v2: unable to generate translation constraints"); } - Result Solver::translate_from_smt2(bool was_killed, std::string stdout, const QueryConfig& config) + Result Solver::translate_from_smt2(bool was_killed, std::string solver_output, const QueryConfig& config) { if (was_killed) { return ERR("could not parse SMT result from string: the SMT solver was killed"); } - auto position = stdout.find_first_of('\n'); - auto [result, model_str] = std::make_tuple(std::string(stdout, 0, position), std::string(stdout, position + 1)); + auto position = solver_output.find_first_of('\n'); + auto [result, model_str] = std::make_tuple(std::string(solver_output, 0, position), std::string(solver_output, position + 1)); auto to_lowercase = [](const auto& s) -> std::string { auto lowercase = s; diff --git a/src/netlist/gate_library/gate_library_manager.cpp b/src/netlist/gate_library/gate_library_manager.cpp index e41651485feb..1ebdc4216fcc 100644 --- a/src/netlist/gate_library/gate_library_manager.cpp +++ b/src/netlist/gate_library/gate_library_manager.cpp @@ -205,7 +205,7 @@ namespace hal auto stripped_name = std::filesystem::path(file_path).filename(); log_info("gate_library_manager", "file '{}' does not exist, searching for '{}' in the default gate library directories...", file_path, stripped_name.string()); - auto lib_path = utils::get_file(stripped_name, utils::get_gate_library_directories()); + auto lib_path = utils::get_file(stripped_name.string(), utils::get_gate_library_directories()); if (lib_path.empty()) { log_info("gate_library_manager", "could not find gate library file '{}'.", stripped_name.string()); diff --git a/src/netlist/netlist_factory.cpp b/src/netlist/netlist_factory.cpp index 605ab831a7e0..877d7cf3cdfb 100644 --- a/src/netlist/netlist_factory.cpp +++ b/src/netlist/netlist_factory.cpp @@ -10,7 +10,6 @@ #include "hal_core/utilities/project_directory.h" #include -#include namespace hal { @@ -46,7 +45,7 @@ namespace hal std::unique_ptr load_netlist(const std::filesystem::path& netlist_file, const std::filesystem::path& gate_library_file) { - if (access(netlist_file.c_str(), F_OK | R_OK) == -1) + if (!std::ifstream(netlist_file).good()) { log_critical("netlist", "could not access file '{}'.", netlist_file.string()); return nullptr; @@ -81,7 +80,7 @@ namespace hal std::unique_ptr load_netlist(const std::filesystem::path& netlist_file, GateLibrary* gate_library) { - if (access(netlist_file.c_str(), F_OK | R_OK) == -1) + if (!std::ifstream(netlist_file).good()) { log_critical("netlist", "could not access file '{}'.", netlist_file.string()); return nullptr; @@ -126,7 +125,7 @@ namespace hal { std::filesystem::path netlist_file = args.is_option_set("--import-netlist") ? std::filesystem::path(args.get_parameter("--import-netlist")) : pdir.get_default_filename(); - if (access(netlist_file.c_str(), F_OK | R_OK) == -1) + if (!std::ifstream(netlist_file).good()) { log_critical("netlist", "cannot access file '{}'.", netlist_file.string()); return nullptr; @@ -144,7 +143,7 @@ namespace hal std::vector> load_netlists(const std::filesystem::path& netlist_file) { - if (access(netlist_file.c_str(), F_OK | R_OK) == -1) + if (!std::ifstream(netlist_file).good()) { log_critical("netlist", "could not access file '{}'.", netlist_file.string()); return {}; diff --git a/src/netlist/project_manager.cpp b/src/netlist/project_manager.cpp index b04c1a012a49..0bb4f0f299fe 100644 --- a/src/netlist/project_manager.cpp +++ b/src/netlist/project_manager.cpp @@ -107,7 +107,7 @@ namespace hal } if (!success) return false; - m_netlist_file = m_proj_dir.get_default_filename(".hal"); + m_netlist_file = m_proj_dir.get_default_filename(".hal").string(); std::filesystem::create_directory(m_proj_dir.get_filename("py")); std::filesystem::create_directory(m_proj_dir.get_filename(ProjectDirectory::s_shadow_dir)); @@ -146,9 +146,9 @@ namespace hal if (gl) m_gatelib_path = gl->get_path().string(); if (shadow) - m_netlist_file = m_proj_dir.get_shadow_filename(".hal"); + m_netlist_file = m_proj_dir.get_shadow_filename(".hal").string(); else - m_netlist_file = m_proj_dir.get_default_filename(".hal"); + m_netlist_file = m_proj_dir.get_default_filename(".hal").string(); if (!netlist_serializer::serialize_to_file(m_netlist_save, m_netlist_file)) return false; diff --git a/src/plugin_system/runtime_library.cpp b/src/plugin_system/runtime_library.cpp index 94ed59efef3f..9b230363d892 100644 --- a/src/plugin_system/runtime_library.cpp +++ b/src/plugin_system/runtime_library.cpp @@ -1,5 +1,7 @@ #include "hal_core/plugin_system/runtime_library.h" +#include + #include "hal_core/utilities/log.h" namespace hal @@ -26,7 +28,13 @@ namespace hal bool RuntimeLibrary::load_library(const std::string& file_name) { #ifdef _WIN32 - m_handle = LoadLibrary(file_name.c_str()); + // plugins link against each other, so the directory of the plugin being loaded has to be + // searched for its dependencies as well. SetDllDirectory adds it to the standard search + // order, which keeps both the directory of hal itself and PATH in the search. + const auto native_path = std::filesystem::absolute(std::filesystem::path(file_name)).make_preferred(); + SetDllDirectoryA(native_path.parent_path().string().c_str()); + m_handle = LoadLibraryA(native_path.string().c_str()); + SetDllDirectoryA(NULL); #else m_handle = dlopen((file_name).c_str(), RTLD_GLOBAL | RTLD_LAZY); #endif @@ -40,7 +48,18 @@ namespace hal auto error_message = std::string(""); #ifdef _WIN32 - error_message = "to be implemented for windows based operating systems."; + char* buffer = NULL; + const auto length = + FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), 0, (char*)&buffer, 0, NULL); + if (length != 0) + { + error_message = std::string(buffer, length); + while (!error_message.empty() && (error_message.back() == '\n' || error_message.back() == '\r')) + { + error_message.pop_back(); + } + } + LocalFree(buffer); #else error_message = dlerror(); #endif @@ -56,7 +75,8 @@ namespace hal return true; } #ifdef _WIN32 - if (FreeLibrary(m_handle) == 0) + // FreeLibrary reports success with a non-zero value, unlike dlclose + if (FreeLibrary(m_handle) != 0) { log_debug("core", "unloaded library '{}'", m_file_name); m_handle = NULL; diff --git a/src/plugin_system/user_feedback.cpp b/src/plugin_system/user_feedback.cpp index acc26c99e8e0..10fb6c1b3722 100644 --- a/src/plugin_system/user_feedback.cpp +++ b/src/plugin_system/user_feedback.cpp @@ -7,8 +7,14 @@ #include #include #include + +#ifdef _WIN32 +#include +#include +#else #include #include +#endif namespace hal { @@ -173,6 +179,19 @@ namespace hal int ProgressPrinter::get_terminal_width() { +#ifdef _WIN32 + CONSOLE_SCREEN_BUFFER_INFO info; + if (!GetConsoleScreenBufferInfo(GetStdHandle(STD_ERROR_HANDLE), &info)) + { + return -1; + } + const int columns = info.srWindow.Right - info.srWindow.Left + 1; + if (columns <= 0 || columns > MAX_PLAUSIBLE_TERMINAL_WIDTH) + { + return -1; + } + return columns; +#else struct winsize size; if (ioctl(STDERR_FILENO, TIOCGWINSZ, &size) < 0) { @@ -183,6 +202,7 @@ namespace hal return -1; } return size.ws_col; +#endif } } // namespace user_feedback } // namespace hal diff --git a/src/python_bindings/CMakeLists.txt b/src/python_bindings/CMakeLists.txt index bcd1c0d619a1..617de6b84b9f 100644 --- a/src/python_bindings/CMakeLists.txt +++ b/src/python_bindings/CMakeLists.txt @@ -20,7 +20,10 @@ target_compile_options(py set_target_properties(py PROPERTIES PREFIX "") if(APPLE AND CMAKE_HOST_APPLE) set_target_properties(py PROPERTIES SUFFIX ".so") -endif(APPLE AND CMAKE_HOST_APPLE) +elseif(WIN32) + # Python only imports extension modules with the .pyd suffix on Windows + set_target_properties(py PROPERTIES SUFFIX ".pyd") +endif() target_link_libraries(py PUBLIC PRIVATE ${Python3_LIBRARIES} pybind11::module hal::netlist hal::utilities hal::plugin_system) install(TARGETS py EXPORT hal diff --git a/src/utilities/log.cpp b/src/utilities/log.cpp index e3ef1cead5df..97a1bab30581 100644 --- a/src/utilities/log.cpp +++ b/src/utilities/log.cpp @@ -225,11 +225,6 @@ namespace hal } else { -#ifdef _WIN32 - // todo: to be implemented for windows - sink->spdlog_sink = nullptr; - log_error("stdout", "create_stdout_sink() has to be implemented for Windows."); -#else auto stdout_sink = std::make_shared(); stdout_sink->set_color(spdlog::level::trace, stdout_sink->green); stdout_sink->set_color(spdlog::level::debug, stdout_sink->blue); @@ -238,7 +233,6 @@ namespace hal stdout_sink->set_color(spdlog::level::err, stdout_sink->red); stdout_sink->set_color(spdlog::level::critical, stdout_sink->red_bold); sink->spdlog_sink = stdout_sink; -#endif } return sink; diff --git a/src/utilities/project_directory.cpp b/src/utilities/project_directory.cpp index 0b77742308ce..7fa76bb43b87 100644 --- a/src/utilities/project_directory.cpp +++ b/src/utilities/project_directory.cpp @@ -37,7 +37,7 @@ namespace hal { std::filesystem::path ProjectDirectory::get_default_filename(const std::string& extension) const { - std::string name(filename()); + std::string name(filename().string()); name += extension.empty() ? std::string(".hal") : extension; std::filesystem::path retval(get_canonical_path()); retval.append(name); @@ -57,7 +57,7 @@ namespace hal { std::filesystem::path ProjectDirectory::get_shadow_filename(const std::string& extension) const { - std::string name(filename()); + std::string name(filename().string()); name += extension.empty() ? std::string(".hal") : extension; std::filesystem::path retval = get_shadow_dir(); retval.append(name); diff --git a/src/utilities/utils.cpp b/src/utilities/utils.cpp index d63aa95ce035..21c4c443a40f 100644 --- a/src/utilities/utils.cpp +++ b/src/utilities/utils.cpp @@ -28,6 +28,23 @@ namespace hal { namespace utils { + namespace + { + /** Windows does not set HOME, the per-user root is USERPROFILE there. */ + std::filesystem::path get_home_directory() + { + for (const char* variable : {"HOME", "USERPROFILE"}) + { + const char* value = std::getenv(variable); + if (value != nullptr && *value != '\0') + { + return std::filesystem::path(value); + } + } + return std::filesystem::temp_directory_path(); + } + } // namespace + bool file_exists(const std::string& filename) { std::ifstream ifile(filename.c_str()); @@ -37,21 +54,8 @@ namespace hal bool folder_exists_and_is_accessible(const std::filesystem::path& folder) { #ifdef _WIN32 - DWORD ftyp = GetFileAttributesA(folder.c_str()); - if (ftyp == INVALID_FILE_ATTRIBUTES) - { - return false; //something is wrong with your path! - } - - if (ftyp & FILE_ATTRIBUTE_DIRECTORY) - { - if (0 == access(folder.c_str(), R_OK)) - { - return true; - } - } - - return false; // this is not a directory! + hal::error_code ec; + return std::filesystem::is_directory(folder, ec); #else struct stat sb; @@ -71,12 +75,18 @@ namespace hal { char buf[1024] = {0}; #ifdef _WIN32 - DWORD ret = GetModuleFileNameA(NULL, buf, sizeof(buf)); - if (ret == 0 || ret == sizeof(buf)) + // the wide API hands the path over without any code page conversion, which would throw + // for non-ASCII characters (this runs during static initialization of the plugin manager) + wchar_t wbuf[MAX_PATH] = {0}; + DWORD ret = GetModuleFileNameW(NULL, wbuf, MAX_PATH); + if (ret == 0 || ret == MAX_PATH) { return std::string(); } - return std::filesystem::path(buf); + UNUSED(buf); + hal::error_code ec; + auto p = std::filesystem::canonical(std::filesystem::path(wbuf), ec); + return p.make_preferred().parent_path(); #elif __APPLE__ && __MACH__ uint32_t size = sizeof(buf); int ret = _NSGetExecutablePath(buf, &size); @@ -105,10 +115,15 @@ namespace hal return std::filesystem::path(environ_path).make_preferred(); } hal::error_code ec; +#ifdef _WIN32 + const std::filesystem::path hal_binary = "hal.exe"; +#else + const std::filesystem::path hal_binary = "hal"; +#endif auto bin_dir = get_binary_directory(); while (std::filesystem::exists(bin_dir)) { - if (std::filesystem::exists(bin_dir / "hal", ec)) + if (std::filesystem::exists(bin_dir / hal_binary, ec)) { return bin_dir.parent_path(); } @@ -118,7 +133,7 @@ namespace hal } bin_dir = bin_dir.parent_path(); } - std::filesystem::path which_result = which("hal"); + std::filesystem::path which_result = which(hal_binary.string()); if (!which_result.empty()) { return which_result.parent_path().parent_path(); @@ -129,6 +144,9 @@ namespace hal std::filesystem::path get_library_directory() { std::vector path_hints = { +#ifdef _WIN32 + get_base_directory() / "bin/", +#endif get_base_directory() / "lib/x86_64-linux-gnu", get_base_directory() / "lib64/", get_base_directory() / "lib/", @@ -156,7 +174,7 @@ namespace hal std::filesystem::path get_user_share_directory() { - std::filesystem::path dir = std::filesystem::path(getenv("HOME")) / ".local/share/hal"; + std::filesystem::path dir = get_home_directory() / ".local/share/hal"; std::filesystem::create_directories(dir); return dir; } @@ -171,7 +189,7 @@ namespace hal std::filesystem::path get_user_config_directory() { - std::filesystem::path dir = std::filesystem::path(getenv("HOME")) / ".config/hal"; + std::filesystem::path dir = get_home_directory() / ".config/hal"; std::filesystem::create_directories(dir); return dir; } @@ -199,7 +217,7 @@ namespace hal get_library_directory() / "hal_plugins/", get_library_directory() / "plugins/", get_user_share_directory() / "plugins/", - std::filesystem::path(getenv("HOME")) / "plugins/", + get_home_directory() / "plugins/", }; return path_hints; } @@ -253,7 +271,6 @@ namespace hal } #ifdef _WIN32 const char which_delimiter = ';'; - return std::filesystem::path(); // Curerently no support for windows. Sorry ... #else const char which_delimiter = ':'; #endif @@ -263,12 +280,26 @@ namespace hal hal::error_code ec; UNUSED(ec); std::filesystem::path p = std::filesystem::path(folder) / name; +#ifdef _WIN32 + // Windows has no executable bit, and callers pass the name without the suffix + if (std::filesystem::is_regular_file(p, ec)) + { + return p; + } + std::filesystem::path with_suffix = p; + with_suffix += ".exe"; + if (std::filesystem::is_regular_file(with_suffix, ec)) + { + return with_suffix; + } +#else struct stat sb; if (stat(p.c_str(), &sb) == 0 && sb.st_mode & S_IXUSR) { return p; } +#endif } return std::filesystem::path(); }