From f5bcbeec0b06ad260e3b01c2d18f64c39696aebe Mon Sep 17 00:00:00 2001 From: Bartek Kryza Date: Fri, 6 Jan 2023 23:39:56 +0000 Subject: [PATCH] Initial MSVC build working --- CMakeLists.txt | 68 ++++++++++++++----- src/class_diagram/model/class_parent.h | 2 - .../visitor/translation_unit_visitor.cc | 5 +- src/common/generators/plantuml/generator.h | 9 ++- src/common/model/diagram_filter.cc | 4 +- src/common/model/source_file.h | 2 +- src/common/visitor/translation_unit_visitor.h | 2 +- src/decorators/decorators.cc | 2 +- .../visitor/translation_unit_visitor.h | 2 +- src/main.cc | 2 +- .../visitor/translation_unit_visitor.cc | 2 + src/util/util.cc | 27 ++++++-- tests/CMakeLists.txt | 16 ++++- tests/t00018/t00018.h | 2 + tests/t30002/t30002.cc | 1 + thirdparty/glob/glob.hpp | 2 +- 16 files changed, 106 insertions(+), 42 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cd5d69a7..e2bb0f79 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,6 +49,11 @@ message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") message(STATUS "LLVM library dir: ${LLVM_LIBRARY_DIR}") +if(MSVC) + # LLVM_BUILD_LLVM_DYLIB is not available on Windows + set(LINK_LLVM_SHARED NO) +endif(MSVC) + if(LINK_LLVM_SHARED) set(LIBTOOLING_LIBS clang-cpp LLVM) else(LINK_LLVM_SHARED) @@ -59,6 +64,7 @@ else(LINK_LLVM_SHARED) clangDriver clangParse clangSema + clangSupport clangAnalysis clangAST clangBasic @@ -78,8 +84,19 @@ else(LINK_LLVM_SHARED) LLVMBitReader LLVMCore LLVMSupport) + if(MSVC) + list(APPEND LIBTOOLING_LIBS + LLVMWindowsDriver + LLVMWindowsManifest) + endif(MSVC) endif(LINK_LLVM_SHARED) +if("${LIBTOOLING_LIBS}" STREQUAL "") + message(FATAL_ERROR "Failed to find LibTooling libraries!") +else() + message(STATUS "Found LibTooling libraries: ${LIBTOOLING_LIBS}") +endif() + # # Setup threads library # @@ -90,22 +107,28 @@ find_package(Threads REQUIRED) # message(STATUS "Checking for yaml-cpp...") if(APPLE) -find_package(PkgConfig) -if(PKG_CONFIG_FOUND) - pkg_check_modules(YAML_CPP yaml-cpp) - find_path(YAML_CPP_INCLUDE_DIR - NAMES yaml.h - PATHS ${YAML_CPP_INCLUDE_DIR} /usr/local/include/yaml-cpp) - find_library(YAML_CPP_LIBRARY - NAMES yaml-cpp - PATHS ${YAML_CPP_LIBRARIES} /usr/local/lib) - set(YAML_CPP_LIBRARY_DIR /usr/local/lib) -endif() -else(APPLE) + find_package(PkgConfig) + if(PKG_CONFIG_FOUND) + pkg_check_modules(YAML_CPP yaml-cpp) + find_path(YAML_CPP_INCLUDE_DIR + NAMES yaml.h + PATHS ${YAML_CPP_INCLUDE_DIR} /usr/local/include/yaml-cpp) + find_library(YAML_CPP_LIBRARY + NAMES yaml-cpp + PATHS ${YAML_CPP_LIBRARIES} /usr/local/lib) + set(YAML_CPP_LIBRARY_DIR /usr/local/lib) + endif(PKG_CONFIG_FOUND) +elseif(MSVC) + set(YAML_CPP_LIBRARIES "yaml-cpp") +else() find_package(yaml-cpp REQUIRED) -endif(APPLE) +endif() -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wall -Wextra -std=c++17 -Wno-unused-parameter -Wno-unused-private-field") +if("${YAML_CPP_LIBRARIES}" STREQUAL "") + message(FATAL_ERROR "Failed to find yaml-cpp library!") +else() + message(STATUS "Found yaml-cpp libraries: ${YAML_CPP_LIBRARIES}") +endif() link_directories(${LLVM_LIBRARY_DIR} ${YAML_CPP_LIBRARY_DIR}) @@ -129,7 +152,13 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OUTPUT_VARIABLE GCC_STDDEF_INCLUDE) message(STATUS "FOUND GCC STDDEF INCLUDE ${GCC_STDDEF_INCLUDE}") include_directories(${GCC_STDDEF_INCLUDE}) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wall -Wextra -std=c++17 -Wno-unused-parameter -Wno-unused-private-field") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -I${GCC_STDDEF_INCLUDE}") +elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wall -Wextra -std=c++17 -Wno-unused-parameter -Wno-unused-private-field") +elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DLLVM_FORCE_USE_OLD_TOOLCHAIN /W1 /std:c++17 /bigobj /wd4291 /wd4624 /wd4244") + set(LINK_OPTIONS "${LINK_OPTIONS} /NODEFAULTLIB:MSVCRT /NODEFAULTLIB:MSVCRTD") endif() message(STATUS "Using CMAKE_CXX_FLAGS: ${CMAKE_CXX_FLAGS}") @@ -145,7 +174,6 @@ include_directories(${THIRDPARTY_HEADERS_DIR}) include_directories(${PROJECT_SOURCE_DIR}/src/) include_directories(${PROJECT_BINARY_DIR}/src/version) - # # Generate source list dynamically # @@ -153,19 +181,25 @@ file(GLOB_RECURSE SOURCES src/*.cc include/*.h) set(MAIN_SOURCE_FILE ${CMAKE_CURRENT_SOURCE_DIR}/src/main.cc) list(REMOVE_ITEM SOURCES ${MAIN_SOURCE_FILE}) - # # Define library target for linking with test cases and output executable # -add_library(clang-umllib OBJECT ${SOURCES}) +if(MSVC) + add_library(clang-umllib STATIC ${SOURCES}) + set(MSVC_LIBRARIES "version") +else(MSVC) + add_library(clang-umllib OBJECT ${SOURCES}) +endif(MSVC) # # Define the target executable clang-uml # add_executable(clang-uml ${MAIN_SOURCE_FILE}) + target_link_libraries(clang-uml ${YAML_CPP_LIBRARIES} ${LIBTOOLING_LIBS} + ${MSVC_LIBRARIES} clang-umllib Threads::Threads) diff --git a/src/class_diagram/model/class_parent.h b/src/class_diagram/model/class_parent.h index 1e059097..79c872c9 100644 --- a/src/class_diagram/model/class_parent.h +++ b/src/class_diagram/model/class_parent.h @@ -32,8 +32,6 @@ public: clanguml::common::id_t id() const noexcept { return id_; } void set_id(clanguml::common::id_t id) { id_ = id; } - void set_id(id_t id); - void is_virtual(bool is_virtual); bool is_virtual() const; diff --git a/src/class_diagram/visitor/translation_unit_visitor.cc b/src/class_diagram/visitor/translation_unit_visitor.cc index 4e9d8a6c..7206e50f 100644 --- a/src/class_diagram/visitor/translation_unit_visitor.cc +++ b/src/class_diagram/visitor/translation_unit_visitor.cc @@ -763,6 +763,7 @@ void translation_unit_visitor::process_class_children( // Static fields have to be processed by iterating over variable // declarations +#ifndef _MSC_VER for (const auto *decl : cls->decls()) { if (decl->getKind() == clang::Decl::Var) { const clang::VarDecl *variable_declaration{ @@ -788,7 +789,7 @@ void translation_unit_visitor::process_class_children( } } } - +#endif if (cls->isCompleteDefinition()) for (const auto *friend_declaration : cls->friends()) { if (friend_declaration != nullptr) @@ -1429,7 +1430,7 @@ std::unique_ptr translation_unit_visitor:: template_instantiation.set_name(template_decl->getNameAsString()); template_instantiation.set_namespace(ns); template_instantiation.set_id(template_decl->getID() + - static_cast( + static_cast( std::hash{}(full_template_specialization_name) >> 4U)); build_template_instantiation_process_template_arguments(parent, diff --git a/src/common/generators/plantuml/generator.h b/src/common/generators/plantuml/generator.h index f5b9ea76..8427fef3 100644 --- a/src/common/generators/plantuml/generator.h +++ b/src/common/generators/plantuml/generator.h @@ -191,14 +191,17 @@ inja::json generator::element_context(const E &e) const if (!e.file().empty()) { std::filesystem::path file{e.file()}; std::string relative_path = file.string(); - +#if _MSC_VER + if (file.is_absolute() && ctx.contains("git")) +#else if (file.is_absolute() && ctx.template contains("git")) +#endif relative_path = - std::filesystem::relative(file, ctx["git"]["toplevel"]); + std::filesystem::relative(file, ctx["git"]["toplevel"]).string(); ctx["element"]["source"]["path"] = relative_path; ctx["element"]["source"]["full_path"] = file.string(); - ctx["element"]["source"]["name"] = file.filename(); + ctx["element"]["source"]["name"] = file.filename().string(); ctx["element"]["source"]["line"] = e.line(); } diff --git a/src/common/model/diagram_filter.cc b/src/common/model/diagram_filter.cc index d11951f9..b65e12b4 100644 --- a/src/common/model/diagram_filter.cc +++ b/src/common/model/diagram_filter.cc @@ -184,12 +184,12 @@ tvl::value_t namespace_filter::match( auto namespace_starts_with_element_qualified_name = nsit.starts_with(e.get_namespace()); - auto result = element_full_name_starts_with_namespace | + auto result = element_full_name_starts_with_namespace || element_full_name_equals_pattern; if (is_inclusive) result = - result | namespace_starts_with_element_qualified_name; + result || namespace_starts_with_element_qualified_name; return result; }); diff --git a/src/common/model/source_file.h b/src/common/model/source_file.h index 7ce58e3b..dd171bfe 100644 --- a/src/common/model/source_file.h +++ b/src/common/model/source_file.h @@ -53,7 +53,7 @@ public: explicit source_file(const std::filesystem::path &p) { set_path({p.parent_path().string()}); - set_name(p.filename()); + set_name(p.filename().string()); is_absolute_ = p.is_absolute(); set_id(common::to_id(p)); } diff --git a/src/common/visitor/translation_unit_visitor.h b/src/common/visitor/translation_unit_visitor.h index 56f5a105..12671d31 100644 --- a/src/common/visitor/translation_unit_visitor.h +++ b/src/common/visitor/translation_unit_visitor.h @@ -103,7 +103,7 @@ protected: private: clang::SourceManager &source_manager_; - const clanguml::config::diagram &config_; + [[maybe_unused]] const clanguml::config::diagram &config_; std::unique_ptr comment_visitor_; }; diff --git a/src/decorators/decorators.cc b/src/decorators/decorators.cc index 8e062353..0b112e9d 100644 --- a/src/decorators/decorators.cc +++ b/src/decorators/decorators.cc @@ -62,7 +62,7 @@ decorator_toks decorator::tokenize(const std::string &label, std::string_view c) decorator_toks res; res.label = label; size_t pos{}; - const auto *it = c.begin(); + auto it = c.begin(); std::advance(it, label.size()); if (*it == ':') { diff --git a/src/include_diagram/visitor/translation_unit_visitor.h b/src/include_diagram/visitor/translation_unit_visitor.h index 7b4c21ea..b496636e 100644 --- a/src/include_diagram/visitor/translation_unit_visitor.h +++ b/src/include_diagram/visitor/translation_unit_visitor.h @@ -104,7 +104,7 @@ public: void finalize() { } private: - clang::SourceManager &source_manager_; + [[maybe_unused]] clang::SourceManager &source_manager_; // Reference to the output diagram model clanguml::include_diagram::model::diagram &diagram_; diff --git a/src/main.cc b/src/main.cc index da969ce9..2ee80fec 100644 --- a/src/main.cc +++ b/src/main.cc @@ -551,7 +551,7 @@ int add_config_diagram(clanguml::common::model::diagram_t type, return 1; } - YAML::Node doc = YAML::LoadFile(config_file); + YAML::Node doc = YAML::LoadFile(config_file.string()); for (YAML::const_iterator it = doc["diagrams"].begin(); it != doc["diagrams"].end(); ++it) { diff --git a/src/package_diagram/visitor/translation_unit_visitor.cc b/src/package_diagram/visitor/translation_unit_visitor.cc index 15e85d33..30349496 100644 --- a/src/package_diagram/visitor/translation_unit_visitor.cc +++ b/src/package_diagram/visitor/translation_unit_visitor.cc @@ -220,6 +220,7 @@ void translation_unit_visitor::process_class_children( // Static fields have to be processed by iterating over variable // declarations +#ifndef _MSC_VER for (const auto *decl : cls.decls()) { if (decl->getKind() == clang::Decl::Var) { const clang::VarDecl *variable_declaration{ @@ -230,6 +231,7 @@ void translation_unit_visitor::process_class_children( } } } +#endif if (cls.isCompleteDefinition()) for (const auto *friend_declaration : cls.friends()) { diff --git a/src/util/util.cc b/src/util/util.cc index 3849f544..10c5ea85 100644 --- a/src/util/util.cc +++ b/src/util/util.cc @@ -49,12 +49,16 @@ void setup_logging(int verbose) std::string get_process_output(const std::string &command) { constexpr size_t kBufferSize{1024}; - std::array buffer{}; std::string result; + +#if defined(__linux) || defined(__unix) std::unique_ptr pipe( popen(command.c_str(), "r"), pclose); - +#elif defined(WINDOWS) || defined(_WIN32) || defined(WIN32) + std::unique_ptr pipe( + _popen(command.c_str(), "r"), _pclose); +#endif if (!pipe) { throw std::runtime_error("popen() failed!"); } @@ -68,12 +72,25 @@ std::string get_process_output(const std::string &command) std::string get_env(const std::string &name) { +#if defined(__linux) || defined(__unix) const char *value = std::getenv(name.c_str()); // NOLINT if (value == nullptr) return {}; return std::string{value}; +#elif defined(WINDOWS) || defined(_WIN32) || defined(WIN32) + static constexpr auto kMaxEnvLength = 2096U; + static char value[kMaxEnvLength]; + const DWORD ret = + GetEnvironmentVariableA(name.c_str(), value, kMaxEnvLength); + if (ret == 0 || ret > kMaxEnvLength) + return {}; + else + return value; +#else + return {}; +#endif } bool is_git_repository() @@ -83,13 +100,9 @@ bool is_git_repository() if (!env.empty()) return true; -#if defined(_WIN32) || defined(_WIN64) - return false; -#else return contains( - trim(get_process_output("git rev-parse --git-dir 2> /dev/null")), + trim(get_process_output("git rev-parse --git-dir")), ".git"); -#endif } std::string get_git_branch() diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 8d6a31ac..19326ed0 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -4,9 +4,15 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_CXX_STANDARD 17) -set(TEST_DISABLE_WARNINGS_DEBUG "-Wno-unused-parameter -Wno-unused-private-field -Wno-unused-variable -Wno-attributes -Wno-nonnull") -set(TEST_DISABLE_WARNINGS_RELEASE "${TEST_DISABLE_WARNINGS} -Wno-aggressive-loop-optimizations") - +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set(TEST_DISABLE_WARNINGS_DEBUG "-Wno-unused-parameter -Wno-unused-private-field -Wno-unused-variable -Wno-attributes -Wno-nonnull") + set(TEST_DISABLE_WARNINGS_RELEASE "${TEST_DISABLE_WARNINGS} -Wno-aggressive-loop-optimizations") +elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + set(TEST_DISABLE_WARNINGS_DEBUG "-Wno-unused-parameter -Wno-unused-private-field -Wno-unused-variable -Wno-attributes -Wno-nonnull") + set(TEST_DISABLE_WARNINGS_RELEASE "${TEST_DISABLE_WARNINGS} -Wno-aggressive-loop-optimizations") +elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W1 /std:c++17 /bigobj /wd4624") +endif() set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS} ${LIBCLANG_CXXFLAGS} ${TEST_DISABLE_WARNINGS_RELEASE}") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS} ${LIBCLANG_CXXFLAGS} ${TEST_DISABLE_WARNINGS_DEBUG}") @@ -28,6 +34,10 @@ set(CLANG_UML_TEST_LIBRARIES ${LIBTOOLING_LIBS} Threads::Threads) +if(MSVC) + list(APPEND CLANG_UML_TEST_LIBRARIES "Version.lib") +endif(MSVC) + set(CLANG_UML_TEST_UTIL_SRC test_util.cc ${TEST_UTIL_SOURCES}) set(CLANG_UML_TEST_UTIL_HEADER catch.h) diff --git a/tests/t00018/t00018.h b/tests/t00018/t00018.h index ca993d08..870f6316 100644 --- a/tests/t00018/t00018.h +++ b/tests/t00018/t00018.h @@ -1,6 +1,8 @@ #pragma once +#ifndef _MSC_VER #include +#endif #include #include diff --git a/tests/t30002/t30002.cc b/tests/t30002/t30002.cc index c5e55b77..a731d51f 100644 --- a/tests/t30002/t30002.cc +++ b/tests/t30002/t30002.cc @@ -2,6 +2,7 @@ #include #include #include +#include namespace clanguml { namespace t30002 { diff --git a/thirdparty/glob/glob.hpp b/thirdparty/glob/glob.hpp index 37322701..0df3f96e 100644 --- a/thirdparty/glob/glob.hpp +++ b/thirdparty/glob/glob.hpp @@ -177,7 +177,7 @@ static inline fs::path expand_tilde(fs::path path) #ifdef _WIN32 char *home; size_t sz; - errno_t err = _dupenv_s(&home, &sz, "USERPROFILE"); + [[maybe_unused]] errno_t err = _dupenv_s(&home, &sz, "USERPROFILE"); #else const char *home = std::getenv("HOME"); #endif