diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index e9b87df1d..a0622f4b4 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -26,7 +26,7 @@ on: - development jobs: - win-server: + win-server-build: name: Windows language server runs-on: windows-2019 @@ -34,100 +34,160 @@ jobs: - uses: actions/checkout@v1 - name: Configure shell: cmd - run: mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Release ../ + run: mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_VSIX=Off ../ - name: Build shell: cmd run: cd build && cmake --build . --parallel --config Release - name: Server Test shell: cmd run: cd build\bin && library_test.exe && server_test.exe - - name: Extension Test - shell: cmd - run: npm --prefix .\clients\vscode-hlasmplugin run test - - name: Extension Test Insiders - shell: cmd - run: npm --prefix .\clients\vscode-hlasmplugin run test:insiders - name: Actions artifact uses: actions/upload-artifact@v1 with: - name: language_server_win + name: language_server_win32 path: build/bin/language_server.exe - linux-server: + linux-server-build: name: Linux language server - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v1 - name: Requirements install - run: sudo apt-get update && sudo apt-get install uuid-dev g++-8 ninja-build + run: sudo apt-get update && sudo apt-get install -y uuid-dev ninja-build - name: Configure - run: mkdir build && cd build && cmake -G Ninja -DCMAKE_C_COMPILER=gcc-8 -DCMAKE_CXX_COMPILER=g++-8 ../ + run: mkdir build && cd build && cmake -G Ninja -DCMAKE_C_COMPILER=gcc-10 -DCMAKE_CXX_COMPILER=g++-10 -DBUILD_VSIX=Off ../ - name: Build run: cd build && cmake --build . - name: Server Test run: cd build/bin && ./server_test && ./library_test - - name: Extension Test - uses: GabrielBB/xvfb-action@v1.0 - with: - run: npm --prefix clients/vscode-hlasmplugin run test - - name: Extension Test Insiders - uses: GabrielBB/xvfb-action@v1.0 - with: - run: npm --prefix clients/vscode-hlasmplugin run test:insiders - name: Actions artifact uses: actions/upload-artifact@v1 with: name: language_server_linux path: build/bin/language_server - macos-server: + wasm-server-build: + name: WASM language server + runs-on: ubuntu-20.04 + container: emscripten/emsdk:2.0.12 + + steps: + - uses: actions/checkout@v1 + - name: Requirements install + run: sudo apt-get update && sudo apt-get install -y ninja-build maven + - name: Configure + run: > + mkdir build && cd build && emcmake cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DDISCOVER_TESTS=Off + -DWITH_LIBCXX=Off -DWITH_STATIC_CRT=Off -DCMAKE_EXE_LINKER_FLAGS="-s NODERAWFS=1" + -DCMAKE_CXX_FLAGS="-s USE_PTHREADS=1 -s PTHREAD_POOL_SIZE=8 -s TOTAL_MEMORY=268435456 -s PROXY_TO_PTHREAD=1 -fexceptions -s NODERAWFS=1 -s EXIT_RUNTIME=1 --bind" + -DCMAKE_CROSSCOMPILING_EMULATOR="node;--experimental-wasm-threads;--experimental-wasm-bulk-memory" + -Dgtest_disable_pthreads=On -DBUILD_VSIX=Off ../ + - name: Build + run: cd build && cmake --build . + - name: Server Test + run: cd build/bin && node --experimental-wasm-threads --experimental-wasm-bulk-memory ./server_test.js && node --experimental-wasm-threads --experimental-wasm-bulk-memory ./library_test.js + - name: Actions artifact + uses: actions/upload-artifact@v2 + with: + name: language_server_wasm + path: build/bin/language_server.* + + macos-server-build: name: MacOS language server runs-on: macos-10.15 steps: - uses: actions/checkout@v1 - name: Requirements install - run: | - brew install ninja llvm@8 - export PATH=~/usr/local/opt/llvm\@8/bin:$PATH + run: brew install ninja - name: Configure - run: mkdir build && cd build && cmake -G Ninja -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DLLVM_PATH=/usr/local/opt/llvm\@8 ../ + run: > + mkdir build && cd build && cmake -G Ninja + -DCMAKE_C_COMPILER=$(brew --prefix llvm)/bin/clang -DCMAKE_CXX_COMPILER=$(brew --prefix llvm)/bin/clang++ + -DLLVM_PATH=$(brew --prefix llvm) -DBUILD_VSIX=Off ../ - name: Build run: cd build && cmake --build . -- -v - name: Server Test run: cd build/bin && ./server_test && ./library_test - - name: Extension Test - run: npm --prefix clients/vscode-hlasmplugin run test -# Remove the insiders test until it is clear where is the problem with freezing of the test -# - name: Extension Test Insiders -# run: npm --prefix clients/vscode-hlasmplugin run test:insiders - name: Actions artifact uses: actions/upload-artifact@v1 with: - name: language_server_macos + name: language_server_darwin path: build/bin/language_server + test-matrix: + name: Test + runs-on: ${{ matrix.os }} + needs: [win-server-build, linux-server-build, wasm-server-build, macos-server-build] + strategy: + matrix: + include: + - os: ubuntu-20.04 + native: linux + chmod: true + - os: windows-2019 + native: win32 + chmod: false + - os: macos-10.15 + native: darwin + chmod: true + + steps: + - uses: actions/checkout@v1 + - name: Download native language server + uses: actions/download-artifact@v1 + with: + name: language_server_${{ matrix.native }} + path: clients/vscode-hlasmplugin/bin/${{ matrix.native }}/ + - name: Run chmod + if: ${{ matrix.chmod }} + run: chmod +x clients/vscode-hlasmplugin/bin/${{ matrix.native }}/language_server + - name: Download wasm language server + uses: actions/download-artifact@v1 + with: + name: language_server_wasm + path: clients/vscode-hlasmplugin/bin/wasm/ + - name: NPM CI + run: npm --prefix clients/vscode-hlasmplugin ci + - name: Extension Test + uses: GabrielBB/xvfb-action@v1.0 + with: + run: npm --prefix clients/vscode-hlasmplugin run test + - name: Extension Test WASM + uses: GabrielBB/xvfb-action@v1.0 + with: + run: npm --prefix clients/vscode-hlasmplugin run test:wasm + - name: Extension Test Insiders + uses: GabrielBB/xvfb-action@v1.0 + with: + run: npm --prefix clients/vscode-hlasmplugin run test:insiders + VSIX: - runs-on: ubuntu-18.04 - needs: [win-server, linux-server, macos-server] + runs-on: ubuntu-20.04 + needs: [test-matrix] steps: - uses: actions/checkout@v1 - name: Download windows language server uses: actions/download-artifact@v1 with: - name: language_server_win + name: language_server_win32 path: clients/vscode-hlasmplugin/bin/win32/ - name: Download linux language server uses: actions/download-artifact@v1 with: name: language_server_linux path: clients/vscode-hlasmplugin/bin/linux/ + - name: Download wasm language server + uses: actions/download-artifact@v1 + with: + name: language_server_wasm + path: clients/vscode-hlasmplugin/bin/wasm/ - name: Download MacOS language server uses: actions/download-artifact@v1 with: - name: language_server_macos + name: language_server_darwin path: clients/vscode-hlasmplugin/bin/darwin/ - name: Set executable flag run: | @@ -154,7 +214,7 @@ jobs: alpine-VSIX: name: Alpine VSIX - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 container: image: alpine:3.10 @@ -191,7 +251,7 @@ jobs: release: name: Release VSIXs - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 if: (github.event_name == 'push' && github.ref == 'refs/heads/master') || (github.event_name == 'push' && startsWith(github.ref, 'refs/heads/release')) needs: [alpine-VSIX, VSIX] @@ -265,7 +325,7 @@ jobs: theia-test: name: Theia Integration Test - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 needs: alpine-VSIX strategy: matrix: diff --git a/.github/workflows/Check.yml b/.github/workflows/Check.yml index 49f14bd0c..797d272fd 100644 --- a/.github/workflows/Check.yml +++ b/.github/workflows/Check.yml @@ -27,14 +27,14 @@ on: jobs: clang-asan: name: Clang ASAN, UBSAN - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v1 - name: Requirements install - run: sudo apt-get update && sudo apt-get install uuid-dev ninja-build libc++-8-dev libc++abi-8-dev + run: sudo apt-get update && sudo apt-get install uuid-dev ninja-build libc++-10-dev libc++abi-10-dev - name: Configure - run: mkdir build && cd build && cmake -G Ninja -DBUILD_VSIX=Off -DCMAKE_C_COMPILER=clang-8 -DCMAKE_CXX_COMPILER=clang++-8 -DCMAKE_CXX_FLAGS="-fsanitize=address,undefined" ../ + run: mkdir build && cd build && cmake -G Ninja -DBUILD_VSIX=Off -DCMAKE_C_COMPILER=clang-10 -DCMAKE_CXX_COMPILER=clang++-10 -DCMAKE_CXX_FLAGS="-fsanitize=address,undefined" ../ - name: Build run: cd build && cmake --build . - name: Test @@ -42,14 +42,14 @@ jobs: clang-tsan: name: Clang TSAN - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v1 - name: Requirements install - run: sudo apt-get update && sudo apt-get install uuid-dev ninja-build libc++-8-dev libc++abi-8-dev + run: sudo apt-get update && sudo apt-get install uuid-dev ninja-build libc++-10-dev libc++abi-10-dev - name: Configure - run: mkdir build && cd build && cmake -G Ninja -DBUILD_VSIX=Off -DCMAKE_C_COMPILER=clang-8 -DCMAKE_CXX_COMPILER=clang++-8 -DCMAKE_CXX_FLAGS="-fsanitize=thread" ../ + run: mkdir build && cd build && cmake -G Ninja -DBUILD_VSIX=Off -DCMAKE_C_COMPILER=clang-10 -DCMAKE_CXX_COMPILER=clang++-10 -DCMAKE_CXX_FLAGS="-fsanitize=thread" ../ - name: Build run: cd build && cmake --build . - name: Test @@ -57,16 +57,12 @@ jobs: clang-format-check: name: Code format - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v1 - name: Requirements install - run: | - wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - - sudo add-apt-repository "deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-10 main" - sudo apt-get update - sudo apt-get install -y clang-format-10 + run: sudo apt-get install -y clang-format-10 - name: Run clang-format run: clang-format-10 -style=file -n $(find . -name \*.h -print -o -name \*.cpp -print) 2>&1 | tee clang-format-output.txt - name: Upload clang-format-output.txt @@ -83,7 +79,7 @@ jobs: license-headers: name: License headers check - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v1 - name: Check license headers @@ -93,7 +89,7 @@ jobs: pr-base-check: name: Master PR check - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 if: github.event_name == 'pull_request' steps: - name: Check, whether PR to master is from a release branch diff --git a/.github/workflows/Sonarcloud-build.yml b/.github/workflows/Sonarcloud-build.yml index cfa9cd468..9a4953199 100644 --- a/.github/workflows/Sonarcloud-build.yml +++ b/.github/workflows/Sonarcloud-build.yml @@ -27,20 +27,20 @@ on: jobs: sonarcloud-build: name: SonarCloud build - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v2 - name: Get version run: echo "VERSION=$(node -e "console.log(require('./clients/vscode-hlasmplugin/package.json').version)")" >> $GITHUB_ENV - name: Requirements install - run: sudo apt-get update && sudo apt-get install uuid-dev ninja-build libc++-8-dev libc++abi-8-dev + run: sudo apt-get update && sudo apt-get install uuid-dev ninja-build libc++-10-dev libc++abi-10-dev - name: Configure run: > mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Debug - -DCMAKE_C_COMPILER=clang-8 - -DCMAKE_CXX_COMPILER=clang++-8 + -DCMAKE_C_COMPILER=clang-10 + -DCMAKE_CXX_COMPILER=clang++-10 -DCMAKE_CXX_FLAGS="-fprofile-instr-generate -fcoverage-mapping" ../ - name: Sonar build wrapper download @@ -61,9 +61,9 @@ jobs: with: run: npm --prefix clients/vscode-hlasmplugin test - name: Merge raw profiles - run: cd build/bin && llvm-profdata-8 merge -o hlasm_profile library.rawprof server.rawprof + run: cd build/bin && llvm-profdata-10 merge -o hlasm_profile library.rawprof server.rawprof - name: Generate lcov coverage - run: cd build/bin && llvm-cov-8 show -instr-profile hlasm_profile library_test -object server_test > ../coverage.txt + run: cd build/bin && llvm-cov-10 show -instr-profile hlasm_profile library_test -object server_test > ../coverage.txt - name: Pull request event info if: github.event_name == 'pull_request' run: | diff --git a/CMakeLists.txt b/CMakeLists.txt index 0c9abc8ac..4555187d1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,8 @@ # Contributors: # Broadcom, Inc. - initial API and implementation -cmake_minimum_required (VERSION 3.10) +cmake_minimum_required(VERSION 3.13) +set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) PROJECT(HlasmServer) @@ -71,85 +72,15 @@ if(NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") set(WITH_LIBCXX Off) endif() - -if(CMAKE_SYSTEM_NAME MATCHES "Linux") - find_package(PkgConfig REQUIRED) - pkg_check_modules(UUID REQUIRED uuid) -endif() -if(APPLE) - find_library(COREFOUNDATION_LIBRARY CoreFoundation) -endif() - -#Check whether maven is installed and in path -find_program(MVN_RETURN mvn) -if(MVN_RETURN MATCHES "MVN_RETURN-NOTFOUND") - message(FATAL_ERROR "Cannot find mvn. Are you sure maven is installed and in the path?" ) -endif() #Check whether npm is installed and in path find_program(NPM_RETURN npm) if(NPM_RETURN MATCHES "NPM_RETURN-NOTFOUND") message(FATAL_ERROR "Cannot find npm. Are you sure npm is installed and in the path?" ) endif() -if (MSVC_VERSION) - string(REGEX REPLACE " /W[0-4]" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4 /wd4251 /wd4996") - set(MY_CXX_WARNING_FLAGS " /w35038") -else() - set(MY_CXX_WARNING_FLAGS " -Wall -Wextra -Wno-attributes -Wno-unknown-pragmas") -endif() - -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${MY_CXX_WARNING_FLAGS}") -string(REGEX REPLACE " /Ob[0-4]" "" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}") -string(REGEX REPLACE " /Ob[0-4]" "" CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL}") -string(REGEX REPLACE " /Ob[0-4]" "" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}") -string(REGEX REPLACE " /Ob[0-4]" "" CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}") - -if (MSVC_VERSION) - if(WITH_STATIC_CRT) - set(CompilerFlags - CMAKE_CXX_FLAGS - CMAKE_CXX_FLAGS_RELWITHDEBINFO - CMAKE_CXX_FLAGS_MINSIZEREL - CMAKE_CXX_FLAGS_RELEASE - ) - foreach(CompilerFlag ${CompilerFlags}) - string(REPLACE "/MD" "/MT" ${CompilerFlag} "${${CompilerFlag}}") - - string(REPLACE "/MDd" "/MTd" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}") - endforeach() - endif() - - string(REPLACE "/ZI" "/Zi" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}") - string(REPLACE "/ZI" "/Zi" CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}") - - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Od /MP ${MY_CXX_WARNING_FLAGS}") - set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} /O1 /Oi /Ob2 /Gy /MP /DNDEBUG ${MY_CXX_WARNING_FLAGS}") - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /O2 /Oi /Ob2 /Gy /MP /DNDEBUG ${MY_CXX_WARNING_FLAGS}") - set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} /O2 /Oi /Ob2 /Gy /MP ${MY_CXX_WARNING_FLAGS}") - set(CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO} ") -else() - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -g ${MY_CXX_WARNING_FLAGS}") - set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} -Os -DNDEBUG ${MY_CXX_WARNING_FLAGS}") - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 -DNDEBUG ${MY_CXX_WARNING_FLGAS}") - set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -O2 -g ${MY_CXX_WARNING_FLAGS}") -endif() - -if(WITH_LIBCXX) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") -endif() - -if(BUILD_FUZZER) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=fuzzer-no-link") -endif() +LIST( APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake ) -if(APPLE) - if(WITH_STATIC_CRT) - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lc++abi ${LLVM_PATH}/lib/libc++.a") - set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -lc++abi ${LLVM_PATH}/lib/libc++.a") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -nostdlib++") - endif() -endif() +include(compiler_flags) if(BUILD_SHARED_LIBS) SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) @@ -162,105 +93,36 @@ if(BUILD_SHARED_LIBS) endif() endif() -set(CMAKE_FLAGS_PASS -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS} -DCMAKE_EXE_LINKER_FLAGS=${CMAKE_EXE_LINKER_FLAGS} -DCMAKE_SHARED_LINKER_FLAGS=${CMAKE_SHARED_LINKER_FLAGS}) -#indicates whether we have to link filesystem library explicitly -if(UNIX) - try_run(TRY_LINK_RUN_RESULT TRY_LINK_COMPILE_RESULT ${CMAKE_BINARY_DIR}/try_filesystem_link ${CMAKE_SOURCE_DIR}/cmake/try_filesystem_link.cpp - CMAKE_FLAGS ${CMAKE_FLAGS_PASS} - ) - - #if the program ended with non-zero or a compile error occured - if(NOT (TRY_LINK_RUN_RESULT EQUAL "0") OR NOT ${TRY_LINK_COMPILE_RESULT}) - set(FILESYSTEM_LINK On) - if(APPLE) - set(FILESYSTEM_LIBRARY "${LLVM_PATH}/lib/libc++fs.a") - elseif(WITH_LIBCXX) - set(FILESYSTEM_LIBRARY c++fs) - else() - set(FILESYSTEM_LIBRARY stdc++fs) - endif() - endif() +set(CMAKE_THREAD_PREFER_PTHREAD TRUE) +set(THREADS_PREFER_PTHREAD_FLAG TRUE) +find_package(Threads REQUIRED) - #test filesystem setup - set(TEST_LINK_LIBS "") - if(FILESYSTEM_LINK) - set(TEST_LINK_LIBS "-DLINK_LIBRARIES=${FILESYSTEM_LIBRARY}") - endif() - - try_run(TEST_LINK_RUN_RESULT TEST_LINK_COMPILE_RESULT ${CMAKE_BINARY_DIR}/try_filesystem_link_test ${CMAKE_SOURCE_DIR}/cmake/try_filesystem_link.cpp - CMAKE_FLAGS ${CMAKE_FLAGS_PASS} ${TEST_LINK_LIBS} - COMPILE_OUTPUT_VARIABLE COMPILE_OUTPUT_RESULT - ) - - if(NOT (TEST_LINK_RUN_RESULT EQUAL "0") OR NOT ${TEST_LINK_COMPILE_RESULT}) - message(FATAL_ERROR "Filesystem link error: ${CMAKE_SOURCE_DIR}/cmake/try_filesystem_link.cpp could not be compiled. Message: \n ${COMPILE_OUTPUT_RESULT}") - endif() -endif() +find_package(Filesystem REQUIRED) include(CTest) -LIST( APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake ) - include( external_antlr4cpp ) - include_directories( ${ANTLR4CPP_INCLUDE_DIRS} ) - link_directories( ${ANTLR4CPP_LIBS} ) +include(external_antlr4cpp) -include( external_json ) -include_directories( ${JSON_INCLUDE_DIRS} ) +include(external_json) include(external_uri) -include_directories( ${URI_INCLUDE_DIRS} ) -link_directories( ${URI_LIBS} ) include(external_boost) -include_directories( ${BOOST_INCLUDE_DIRS} ) - #Testing setup if(BUILD_TESTING) - set(GTEST_SHARED_CRT NOT ${WITH_STATIC_CRT}) - # Download and unpack googletest at configure time - configure_file(cmake/external_gtest.cmake googletest-download/CMakeLists.txt) - execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . - RESULT_VARIABLE result - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download ) - if(result) - message(FATAL_ERROR "CMake step for googletest failed: ${result}") - endif() - execute_process(COMMAND ${CMAKE_COMMAND} --build . - RESULT_VARIABLE result - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download ) - if(result) - message(FATAL_ERROR "Build step for googletest failed: ${result}") - endif() - - # Prevent overriding the parent project's compiler/linker - # settings on Windows - set(gtest_force_shared_crt ${GTEST_SHARED_CRT} CACHE BOOL "" FORCE) - - #we want to link google test staticly under all circumstances - set(BUILD_SHARED_LIBS_TMP ${BUILD_SHARED_LIBS}) - set(BUILD_SHARED_LIBS Off) - # Add googletest directly to our build. This defines - # the gtest and gtest_main targets. - add_subdirectory(${CMAKE_BINARY_DIR}/googletest-src - ${CMAKE_BINARY_DIR}/googletest-build - EXCLUDE_FROM_ALL) - set(BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS_TMP}) + include(external_gtest) endif() -# Global link directories -link_directories(${GLOBAL_OUTPUT_PATH} ${ANTLR4CPP_LIBS}) - # Libraries (+ their includes) add_subdirectory(parser_library) -include_directories(parser_library/include) - # Applications add_subdirectory(language_server) -add_subdirectory(dummy) add_subdirectory(benchmark) +add_subdirectory(utils) + if(BUILD_VSIX) add_subdirectory(clients) endif() @@ -282,7 +144,7 @@ file(GLOB_RECURSE executables "${GLOBAL_OUTPUT_PATH}/*${EXECUTABLE}" ) install(FILES ${executables} DESTINATION "bin") -file(GLOB_RECURSE libraries "${GLOBAL_OUTPUT_PATH}/*${LIBRARY}" "${ANTLR4CPP_LIBS}/*${LIBRARY}") +file(GLOB_RECURSE libraries "${GLOBAL_OUTPUT_PATH}/*${LIBRARY}") install(FILES ${libraries} DESTINATION "bin") diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 71360ec56..e3bc6c401 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -43,10 +43,10 @@ We follow the [Conventional commits](https://www.conventionalcommits.org/en/v1.0 Each pull request is automatically verified in the following environments (in a GitHub Actions pipeline): - Windows build with the MSVC compiler -- Ubuntu 18.04 build with the GNU GCC 8 compiler using the libstdc++ standard library -- Ubuntu 18.04 build with the Clang 8 compiler using the libc++ standard library and address sanitizer +- Ubuntu 20.04 build with the GNU GCC 10 compiler using the libstdc++ standard library +- Ubuntu 20.04 build with the Clang 10 compiler using the libc++ standard library and address sanitizer - Alpine Linux 3.10 build with GNU GCC 8 compiler -- MacOS 10.15 build with LLVM CLang 8 (not Apple Clang) using the libc++ standard +- MacOS 10.15 build with CLang 11 using the libc++ standard library Further, all C++ sources must be formatted with clang-format. The required configuration is available in the [.clang-format](https://github.com/eclipse/che-che4z-lsp-for-hlasm/blob/development/.clang-format) file in the root of the repository. diff --git a/benchmark/CMakeLists.txt b/benchmark/CMakeLists.txt index 78232a9d3..6ba5dde1f 100644 --- a/benchmark/CMakeLists.txt +++ b/benchmark/CMakeLists.txt @@ -10,16 +10,12 @@ # Contributors: # Broadcom, Inc. - initial API and implementation -cmake_minimum_required (VERSION 3.10) - project(benchmark) add_executable(benchmark ${PROJECT_SOURCE_DIR}/benchmark.cpp) -add_dependencies(benchmark json) +target_link_libraries(benchmark nlohmann_json::nlohmann_json) target_link_libraries(benchmark parser_library) -if(UNIX) - target_link_libraries(benchmark pthread) -endif() +target_link_libraries(benchmark Threads::Threads) diff --git a/benchmark/benchmark.cpp b/benchmark/benchmark.cpp index a34fc3b87..2c26f313c 100644 --- a/benchmark/benchmark.cpp +++ b/benchmark/benchmark.cpp @@ -20,8 +20,7 @@ #include #include -#include "json.hpp" - +#include "nlohmann/json.hpp" #include "workspace_manager.h" #include "workspaces/file_impl.h" diff --git a/clients/CMakeLists.txt b/clients/CMakeLists.txt index 04932418b..a1671052a 100644 --- a/clients/CMakeLists.txt +++ b/clients/CMakeLists.txt @@ -10,8 +10,6 @@ # Contributors: # Broadcom, Inc. - initial API and implementation -cmake_minimum_required (VERSION 3.10) - PROJECT(HlasmClients) add_subdirectory(vscode-hlasmplugin) diff --git a/clients/vscode-hlasmplugin/CMakeLists.txt b/clients/vscode-hlasmplugin/CMakeLists.txt index 2f7245c31..1e0f4f4dc 100644 --- a/clients/vscode-hlasmplugin/CMakeLists.txt +++ b/clients/vscode-hlasmplugin/CMakeLists.txt @@ -10,8 +10,6 @@ # Contributors: # Broadcom, Inc. - initial API and implementation -cmake_minimum_required (VERSION 3.10) - PROJECT(vscode-hlasmplugin) set(CHECK "${GLOBAL_OUTPUT_PATH}/npm_install.stamp") @@ -28,8 +26,11 @@ else() set(ANTLR_VERSION_SUFFIX .${ANTLR4CPP_EXTERNAL_TAG}) endif() - -if(WIN32) +set(BIN_EXTRAS "") +if(EMSCRIPTEN) + set(BIN_FOLDER "${CMAKE_CURRENT_SOURCE_DIR}/bin/wasm/") + list(APPEND BIN_EXTRAS "$/language_server.worker.js" "$/language_server.wasm") +elseif(WIN32) set(BIN_FOLDER "${CMAKE_CURRENT_SOURCE_DIR}/bin/win32/") elseif(APPLE) set(BIN_FOLDER "${CMAKE_CURRENT_SOURCE_DIR}/bin/darwin/") @@ -49,7 +50,7 @@ if(BUILD_SHARED_LIBS) ${CMAKE_COMMAND} -E copy ${GLOBAL_OUTPUT_PATH}/${CMAKE_SHARED_LIBRARY_PREFIX}antlr4-runtime${ANTLR_VERSION_SUFFIX}${CMAKE_SHARED_LIBRARY_SUFFIX} ${BIN_FOLDER}) else() set(COPY_ANTLR_LIBRARY_COMMAND - ${CMAKE_COMMAND} -E copy ${GLOBAL_OUTPUT_PATH}/${CMAKE_SHARED_LIBRARY_PREFIX}antlr4-runtime${CMAKE_SHARED_LIBRARY_SUFFIX}${ANTLR_VERSION_SUFFIX} ${BIN_FOLDER}) + ${CMAKE_COMMAND} -E copy ${GLOBAL_OUTPUT_PATH}/${CMAKE_SHARED_LIBRARY_PREFIX}antlr4-runtime${CMAKE_SHARED_LIBRARY_SUFFIX}${ANTLR_VERSION_SUFFIX} ${BIN_FOLDER}) endif() endif() @@ -66,7 +67,7 @@ add_custom_command( COMMAND ${COPY_ANTLR_LIBRARY_COMMAND} COMMAND - ${CMAKE_COMMAND} -E copy $ ${BIN_FOLDER} + ${CMAKE_COMMAND} -E copy $ ${BIN_EXTRAS} ${BIN_FOLDER} COMMAND npm run-script package -- -o ${GLOBAL_OUTPUT_PATH}/vscode-hlasmplugin.vsix COMMAND diff --git a/clients/vscode-hlasmplugin/build/bin/prepareWorkspace.js b/clients/vscode-hlasmplugin/build/bin/prepareWorkspace.js index 5d2feb4e1..628ab6a4b 100644 --- a/clients/vscode-hlasmplugin/build/bin/prepareWorkspace.js +++ b/clients/vscode-hlasmplugin/build/bin/prepareWorkspace.js @@ -15,15 +15,31 @@ const fs = require('fs') const path = require('path') -recursiveRemoveSync(path.join(__dirname,'..','..','lib/test/workspace/')); -recursiveCopySync(path.join(__dirname,'..','..','src/test/workspace/'),path.join(__dirname,'..','..','lib/test/workspace/')); +const workspace_dir = path.join(__dirname, '..', '..', 'lib/test/workspace/'); +const workspace_vscode = path.join(workspace_dir, '.vscode'); +recursiveRemoveSync(workspace_dir); +recursiveCopySync(path.join(__dirname, '..', '..', 'src/test/workspace/'), workspace_dir); + +const source_settings_file = (function () { + if (process.argv.indexOf('wasm') !== -1) { + console.log('Preparing WASM'); + return 'settings.wasm.json'; + } + else { + console.log('Preparing native'); + return 'settings.native.json'; + } +})(); + +recursiveCopySync(path.join(workspace_vscode, source_settings_file), path.join(workspace_vscode, 'settings.json')); + console.log('Test workspace ready') function recursiveCopySync(origin, dest) { if (fs.existsSync(origin)) { if (fs.statSync(origin).isDirectory()) { fs.mkdirSync(dest); - fs.readdirSync(origin).forEach(file => + fs.readdirSync(origin).forEach(file => recursiveCopySync(path.join(origin, file), path.join(dest, file))); } else { @@ -35,13 +51,13 @@ function recursiveCopySync(origin, dest) { function recursiveRemoveSync(dest) { if (fs.existsSync(dest)) { fs.readdirSync(dest).forEach(file => { - const currPath = path.join(dest, file); - if (fs.statSync(currPath).isDirectory()) { - recursiveRemoveSync(currPath); - } - else { - fs.unlinkSync(currPath); - } + const currPath = path.join(dest, file); + if (fs.statSync(currPath).isDirectory()) { + recursiveRemoveSync(currPath); + } + else { + fs.unlinkSync(currPath); + } }); fs.rmdirSync(dest); } diff --git a/clients/vscode-hlasmplugin/package.json b/clients/vscode-hlasmplugin/package.json index 6db0ba13d..3d6ad8055 100644 --- a/clients/vscode-hlasmplugin/package.json +++ b/clients/vscode-hlasmplugin/package.json @@ -66,6 +66,7 @@ "clean": "node ./build/bin/clean.js", "package": "vsce package", "test": "npm run compile && node ./build/bin/prepareWorkspace.js && node ./lib/test/runTest.js", + "test:wasm": "npm run compile && node ./build/bin/prepareWorkspace.js wasm && node ./lib/test/runTest.js", "test:insiders": "npm run compile && node ./build/bin/prepareWorkspace.js && node ./lib/test/runTest.js insiders", "test:theia": "npm run compile && node ./build/bin/prepareWorkspace.js && node ./build/bin/theiaTest.js" }, @@ -313,7 +314,7 @@ ], "configuration": { "type": "object", - "title": "hlasmplugin configuration", + "title": "HLASM language support configuration", "properties": { "hlasm.arguments": { "type": "array", @@ -337,6 +338,16 @@ "type": "integer", "default": 10, "description": "This option limits number of diagnostics shown for an open code when there is no configuration in pgm_conf.json." + }, + "hlasm.communicationMethod": { + "type": "string", + "default": "native", + "description": "Select language server variant - native, wasm, tcp.", + "enum": [ + "native", + "tcp", + "wasm" + ] } } } diff --git a/clients/vscode-hlasmplugin/src/extension.ts b/clients/vscode-hlasmplugin/src/extension.ts index 59f94e56a..241da03af 100644 --- a/clients/vscode-hlasmplugin/src/extension.ts +++ b/clients/vscode-hlasmplugin/src/extension.ts @@ -21,10 +21,9 @@ import { HLASMConfigurationProvider, getCurrentProgramName, getProgramName } fro import { ContinuationHandler } from './continuationHandler'; import { CustomEditorCommands } from './customEditorCommands'; import { EventsHandler, getConfig } from './eventsHandler'; -import { ServerFactory } from './serverFactory'; +import { ServerFactory, ServerCommunicationMethod } from './serverFactory'; import { HLASMDebugAdapterFactory } from './hlasmDebugAdapterFactory'; -const useTcp = false; const offset = 71; const continueColumn = 15; //export var hlasmpluginClient : vscodelc.LanguageClient; @@ -52,7 +51,8 @@ export async function activate(context: vscode.ExtensionContext) { // create server options var factory = new ServerFactory(); - const serverOptions = await factory.create(useTcp); + const commMethod = getConfig('communicationMethod', 'native'); + const serverOptions = await factory.create(commMethod); //client init var hlasmpluginClient = new vscodelc.LanguageClient('Hlasmplugin Language Server', serverOptions, clientOptions); @@ -66,7 +66,7 @@ export async function activate(context: vscode.ExtensionContext) { //give the server some time to start listening when using TCP setTimeout(function () { hlasmpluginClient.start(); - }, (useTcp) ? 2000 : 0); + }, (commMethod === 'tcp') ? 2000 : 0); let api = { getExtension(): vscodelc.LanguageClient { diff --git a/clients/vscode-hlasmplugin/src/serverFactory.ts b/clients/vscode-hlasmplugin/src/serverFactory.ts index 949baff86..46640fe17 100644 --- a/clients/vscode-hlasmplugin/src/serverFactory.ts +++ b/clients/vscode-hlasmplugin/src/serverFactory.ts @@ -14,9 +14,12 @@ import * as vscodelc from 'vscode-languageclient'; import * as net from 'net'; -import * as fork from 'child_process' +import * as cp from 'child_process' import * as path from 'path' import { getConfig } from './eventsHandler' +import { SourceBreakpoint } from 'vscode'; + +export type ServerCommunicationMethod = "tcp" | "native" | "wasm"; /** * factory to create server options @@ -29,13 +32,13 @@ export class ServerFactory { this.usedPorts = new Set(); } - async create(useTcp: boolean): Promise { + async create(method: ServerCommunicationMethod): Promise { const langServerFolder = process.platform; - if (useTcp) { + if (method === 'tcp') { const lspPort = await this.getPort(); //spawn the server - fork.execFile( + cp.execFile( path.join(__dirname, '..', 'bin', langServerFolder, 'language_server'), [lspPort.toString()]); @@ -48,13 +51,28 @@ export class ServerFactory { return Promise.resolve(result); }; } - else { + else if (method === 'native') { const server: vscodelc.Executable = { command: path.join(__dirname, '..', 'bin', langServerFolder, 'language_server'), args: getConfig('arguments') }; return server; } + else if (method === 'wasm') { + const server: vscodelc.Executable = { + command: process.execPath, + args: [ + '--experimental-wasm-threads', + '--experimental-wasm-bulk-memory', + path.join(__dirname, '..', 'bin', 'wasm', 'language_server'), + ...getConfig('arguments') + ] + }; + return server; + } + else { + throw Error("Invalid method"); + } } private async getPort(): Promise { diff --git a/clients/vscode-hlasmplugin/src/test/runTest.ts b/clients/vscode-hlasmplugin/src/test/runTest.ts index 27134e65c..4d29127f9 100644 --- a/clients/vscode-hlasmplugin/src/test/runTest.ts +++ b/clients/vscode-hlasmplugin/src/test/runTest.ts @@ -22,7 +22,7 @@ async function main() { // prepare development and tests paths const extensionDevelopmentPath = path.join(__dirname, '../../'); const extensionTestsPath = path.join(__dirname, './suite/index'); - const launchArgs = [path.join(__dirname, './workspace/'),'--disable-extensions']; + const launchArgs = [path.join(__dirname, './workspace/'), '--disable-extensions']; var options: TestOptions; if (process.argv.length > 2 && process.argv[2] == 'insiders') { const vscodeExecutablePath = await downloadAndUnzipVSCode('insiders'); @@ -42,6 +42,7 @@ async function main() { // run tests await runTests(options); } catch (error) { + console.log(error); console.error('Tests Failed'); process.exit(1); } diff --git a/clients/vscode-hlasmplugin/src/test/suite/index.ts b/clients/vscode-hlasmplugin/src/test/suite/index.ts index 1f503ad3c..1dd29f109 100644 --- a/clients/vscode-hlasmplugin/src/test/suite/index.ts +++ b/clients/vscode-hlasmplugin/src/test/suite/index.ts @@ -20,14 +20,14 @@ import * as process from 'process'; export async function run(): Promise { const sourceRoot = path.join(__dirname, '..', '..'); - + // initialize nyc code coverage const NYC = require('nyc'); - const nyc = new NYC({ + const nyc = new NYC({ cwd: path.join(sourceRoot, '..'), reporter: ['lcov'], hookRequire: true, - exclude: ['**/test/**','.vscode-test/**'] + exclude: ['**/test/**', '.vscode-test/**'] }); const is_vscode = process.execPath.includes('Code'); @@ -49,27 +49,27 @@ export async function run(): Promise { const mocha = new Mocha({ ui: 'tdd', color: true }); const testsPath = path.join(__dirname, '..'); + const files = await new Promise((resolve, reject) => { + glob((is_vscode) ? '**/**.test.js' : '**/integration.test.js', { cwd: testsPath }, (err, files) => { + if (err) + reject(err); + else + resolve(files); + }); + }); + + // Add files to the test suite + files.forEach(file => + mocha.addFile(path.resolve(testsPath, file))); + await new Promise((resolve, reject) => { - glob((is_vscode) ? '**/**.test.js' : '**/integration.test.js', { cwd: testsPath }, (_, files) => { - // Add files to the test suite - files.forEach(file => - mocha.addFile(path.resolve(testsPath, file))); - - try { - vscode.workspace.getConfiguration('hlasm').update('continuationHandling',true).then(() => { - // Run the mocha test - mocha.run(failures => { - if (failures > 0) { - reject(new Error(`${failures} tests failed.`)); - } else { - resolve(); - } - }); - }); - } catch (error) { - console.error(error); - reject(error); - } + // Run the mocha test + mocha.run(failures => { + if (failures > 0) { + reject(new Error(`${failures} tests failed.`)); + } else { + resolve(undefined); + } }); }); diff --git a/clients/vscode-hlasmplugin/src/test/suite/integration.test.ts b/clients/vscode-hlasmplugin/src/test/suite/integration.test.ts index dc76377b4..1b941a818 100644 --- a/clients/vscode-hlasmplugin/src/test/suite/integration.test.ts +++ b/clients/vscode-hlasmplugin/src/test/suite/integration.test.ts @@ -17,189 +17,172 @@ import * as vscode from 'vscode'; import * as path from 'path'; suite('Integration Test Suite', () => { - var openFileEditor: vscode.TextEditor = null; const workspacePath = vscode.workspace.workspaceFolders[0].uri.fsPath; - - // open 'open' file, should be recognized as hlasm - test('HLASM file open test', (done) => { - + + const workspace_file = 'open'; + const get_editor = () => { + const editor = vscode.window.activeTextEditor; + assert.equal(editor.document.uri.fsPath, path.join(vscode.workspace.workspaceFolders[0].uri.fsPath, workspace_file)); + + return editor; + } + const sleep = (ms: number) => { + return new Promise((resolve) => { setTimeout(resolve, ms) }); + }; + + suiteSetup(async () => { // 'open' should be in workspace - vscode.workspace.findFiles('open').then(files => { - assert.ok(files && files[0]); - const file = files[0]; - // open the file - vscode.workspace.openTextDocument(file).then(document => { - vscode.window.showTextDocument(document).then(editor => { - openFileEditor = editor; - // setting a language takes a while but shouldn't take longer than a second - setTimeout(function () { - if (document.languageId != 'hlasm') - done('Wrong language'); - else - done(); - }, 1000); - - }); - }); - }); + const files = await vscode.workspace.findFiles(workspace_file); + + assert.ok(files && files[0]); + const file = files[0]; + + // open the file + const document = await vscode.workspace.openTextDocument(file); + + await vscode.window.showTextDocument(document); + }); + + // open 'open' file, should be recognized as hlasm + test('HLASM file open test', async () => { + const editor = get_editor(); + + // setting a language takes a while but shouldn't take longer than a second + await sleep(1000); + assert.ok(editor.document.languageId === 'hlasm'); }).timeout(10000).slow(4000); // change 'open' file to create diagnostic - test('Diagnostic test', (done) => { - assert.equal(vscode.window.activeTextEditor, openFileEditor); + test('Diagnostic test', async () => { + const editor = get_editor(); // register callback to check for the correctness of the diagnostic - const listener = vscode.languages.onDidChangeDiagnostics(_ => { - const allDiags = vscode.languages.getDiagnostics(); - listener.dispose(); - var openDiags = allDiags.find(pair => pair[0].path.endsWith("open"))[1] - - if (openDiags.length == 1 && openDiags[0].code == 'M003') - done(); - else - done('Wrong diagnostic'); - }) + var diagnostic_event = new Promise<[vscode.Uri, vscode.Diagnostic[]][]>((resolve, reject) => { + const listener = vscode.languages.onDidChangeDiagnostics((_) => { + listener.dispose(); + resolve(vscode.languages.getDiagnostics()); + }); + }); // remove second parameter from LR instruction - openFileEditor.edit(edit => { - edit.delete(new vscode.Range(new vscode.Position(2,6), new vscode.Position(2,7))); + await editor.edit(edit => { + edit.delete(new vscode.Range(new vscode.Position(2, 6), new vscode.Position(2, 7))); }); + + const allDiags = await diagnostic_event; + var openDiags = allDiags.find(pair => pair[0].path.endsWith("open"))[1] + + assert.ok(openDiags.length == 1 && openDiags[0].code == 'M003', 'Wrong diagnostic'); }).timeout(10000).slow(1000); // test completion for instructions - test('Completion Instructions test', (done) => { - assert.equal(vscode.window.activeTextEditor, openFileEditor); - openFileEditor.edit(edit => { - edit.insert(new vscode.Position(7,1),'L'); - }).then(_ => { - const movePosition = new vscode.Position(7,2); - openFileEditor.selection = new vscode.Selection(movePosition,movePosition); - vscode.commands.executeCommand('editor.action.triggerSuggest') - .then(() => { - setTimeout(() => { - vscode.commands.executeCommand('acceptSelectedSuggestion').then(result => { - setTimeout(() => { - const text = openFileEditor.document.getText(); - const acceptedLine = text.split('\n')[7]; - if (acceptedLine.includes('L R,D12U(X,B)')) - done(); - else - done('Wrong suggestion result' + acceptedLine) - }, 1000) - }) - },1000); - }); - }) + test('Completion Instructions test', async () => { + const editor = get_editor(); + await editor.edit(edit => { + edit.insert(new vscode.Position(7, 1), 'L'); + }); + const movePosition = new vscode.Position(7, 2); + editor.selection = new vscode.Selection(movePosition, movePosition); + + await vscode.commands.executeCommand('editor.action.triggerSuggest'); + await sleep(1000); + + await vscode.commands.executeCommand('acceptSelectedSuggestion'); + await sleep(1000); + + const text = editor.document.getText(); + const acceptedLine = text.split('\n')[7]; + + assert.ok(acceptedLine.includes('L R,D12U(X,B)'), 'Wrong suggestion result' + acceptedLine); }).timeout(10000).slow(4000); // test completion for variable symbols - test('Completion Variable symbol test', (done) => { - assert.equal(vscode.window.activeTextEditor, openFileEditor); + test('Completion Variable symbol test', async () => { + const editor = get_editor(); // add '&' to simulate start of a variable symbol - openFileEditor.edit(edit => { - edit.insert(new vscode.Position(8,0),'&'); - }).then(_ => { - const movePosition = new vscode.Position(8,1); - openFileEditor.selection = new vscode.Selection(movePosition,movePosition); - vscode.commands.executeCommand('editor.action.triggerSuggest') - .then(() => { - setTimeout(() => { - vscode.commands.executeCommand('acceptSelectedSuggestion').then(result => { - setTimeout(() => { - const text = openFileEditor.document.getText(); - const acceptedLine = text.split('\n')[8]; - if (acceptedLine.includes('&VAR')) - done(); - else - done('Wrong suggestion result' + acceptedLine) - }, 1000) - }) - },1000); - }); - }) + await editor.edit(edit => { + edit.insert(new vscode.Position(8, 0), '&'); + }); + const movePosition = new vscode.Position(8, 1); + editor.selection = new vscode.Selection(movePosition, movePosition); + + await vscode.commands.executeCommand('editor.action.triggerSuggest'); + await sleep(1000); + + await vscode.commands.executeCommand('acceptSelectedSuggestion') + await sleep(1000); + + const text = editor.document.getText(); + const acceptedLine = text.split('\n')[8]; + + assert.ok(acceptedLine.includes('&VAR'), 'Wrong suggestion result' + acceptedLine); }).timeout(10000).slow(4000); // go to definition for ordinary symbol - test('Definition Ordinary symbol test', (done) => { - assert.equal(vscode.window.activeTextEditor, openFileEditor); - vscode.commands.executeCommand('vscode.executeDefinitionProvider',openFileEditor.document.uri,new vscode.Position(1,7)) - .then((result: vscode.Location[]) => { - if (result.length == 1 - && result[0].uri.fsPath == openFileEditor.document.fileName - && result[0].range.start.line == 9 - && result[0].range.start.character == 0) - done(); - else - done('Wrong ordinary symbol definition location'); - }); + test('Definition Ordinary symbol test', async () => { + const editor = get_editor(); + const result: vscode.Location[] = await vscode.commands.executeCommand('vscode.executeDefinitionProvider', editor.document.uri, new vscode.Position(1, 7)); + + assert.ok(result.length == 1 + && result[0].uri.fsPath == editor.document.fileName + && result[0].range.start.line == 9 + && result[0].range.start.character == 0, 'Wrong ordinary symbol definition location'); }).timeout(10000).slow(1000); // hover for variable symbol - test('Hover Variable symbol test', (done) => { - assert.equal(vscode.window.activeTextEditor, openFileEditor); - vscode.commands.executeCommand('vscode.executeHoverProvider',openFileEditor.document.uri,new vscode.Position(6,8)) - .then((result: vscode.Hover[]) => { - if (result.length == 1 - && result[0].contents.length == 1 - && (result[0].contents[0] as vscode.MarkdownString).value == 'SETA variable') - done(); - else - done('Wrong variable symbol hover contents'); - }); + test('Hover Variable symbol test', async () => { + const editor = get_editor(); + const result: vscode.Hover[] = await vscode.commands.executeCommand('vscode.executeHoverProvider', editor.document.uri, new vscode.Position(6, 8)); + + assert.ok(result.length == 1 + && result[0].contents.length == 1 + && (result[0].contents[0] as vscode.MarkdownString).value == 'SETA variable', 'Wrong variable symbol hover contents'); }).timeout(10000).slow(1000); // go to definition for macros - test('Definition Macro test', (done) => { - assert.equal(vscode.window.activeTextEditor, openFileEditor); - vscode.commands.executeCommand('vscode.executeDefinitionProvider',openFileEditor.document.uri,new vscode.Position(6,2)) - .then((result: vscode.Location[]) => { - if (result.length == 1 - && result[0].uri.fsPath == path.join(workspacePath, 'libs','mac.asm') - && result[0].range.start.line == 1 - && result[0].range.start.character == 4) - done(); - else - done('Wrong macro definition location'); - }); - }).timeout(10000).slow(1000); + test('Definition Macro test', async () => { + const editor = get_editor(); + const result: vscode.Location[] = await vscode.commands.executeCommand('vscode.executeDefinitionProvider', editor.document.uri, new vscode.Position(6, 2)); + assert.ok(result.length == 1 + && result[0].uri.fsPath == path.join(workspacePath, 'libs', 'mac.asm') + && result[0].range.start.line == 1 + && result[0].range.start.character == 4, 'Wrong macro definition location'); + }).timeout(10000).slow(1000); // debug open code test - test('Debug test', (done) => { + test('Debug test', async () => { // simulates basic debugging procedure - assert.equal(vscode.window.activeTextEditor, openFileEditor); - - // when the debug session starts - const disposable = vscode.debug.onDidStartDebugSession(session => { - // step over once - // wait a second to let the debug session complete - setTimeout(() => { - vscode.commands.executeCommand('workbench.action.debug.stepOver') - // wait 1 more second to let step over take place - // then check for VAR2 variable - setTimeout(() => { - session.customRequest('scopes',{frameId:0}).then(scopesResult => { - const noBody = scopesResult.body == undefined; - var scopes; - if (noBody) - scopes = scopesResult.scopes; - else - scopes = scopesResult.body.scopes; - const reference = scopes.find((scope : {name:string}) => scope.name == 'Locals').variablesReference; - session.customRequest('variables',{variablesReference: reference}).then(variablesResult => { - disposable.dispose(); - vscode.commands.executeCommand('workbench.action.debug.stop'); - var variables; - if (noBody) - variables = variablesResult.variables; - else - variables = variablesResult.body.variables; - if (variables.length == 1 && variables[0].value == 'SOMETHING' && variables[0].name == '&VAR2') - done(); - else - done('Wrong debug variable &VAR2'); - }); - })}, 1000); - }, 1000) + const editor = get_editor(); + + const session_started_event = new Promise((resolve) => { + // when the debug session starts + const disposable = vscode.debug.onDidStartDebugSession((session) => { + disposable.dispose(); + resolve(session); + }); }); // start debugging - vscode.debug.startDebugging(vscode.workspace.workspaceFolders[0],'Macro tracer: current program'); + if (!await vscode.debug.startDebugging(vscode.workspace.workspaceFolders[0], 'Macro tracer: current program')) + throw new Error("Failed to start a debugging session"); + + const session = await session_started_event; + + // wait a second to let the debug session complete + await sleep(1000); + // step over once + await vscode.commands.executeCommand('workbench.action.debug.stepOver'); + // wait 1 more second to let step over take place + await sleep(1000); + // then check for VAR2 variable + const scopesResult = await session.customRequest('scopes', { frameId: 0 }); + + const scopes = scopesResult.body ? scopesResult.body.scopes : scopesResult.scopes; + + const reference = scopes.find((scope: { name: string }) => scope.name == 'Locals').variablesReference; + const variablesResult = await session.customRequest('variables', { variablesReference: reference }); + + await vscode.commands.executeCommand('workbench.action.debug.stop'); + + const variables = variablesResult.body ? variablesResult.body.variables : variablesResult.variables; + + assert.ok(variables.length == 1 && variables[0].value == 'SOMETHING' && variables[0].name == '&VAR2', 'Wrong debug variable &VAR2'); }).timeout(10000).slow(4000); }); diff --git a/clients/vscode-hlasmplugin/src/test/suite/serverFactory.test.ts b/clients/vscode-hlasmplugin/src/test/suite/serverFactory.test.ts index a6242bf15..0386b16fe 100644 --- a/clients/vscode-hlasmplugin/src/test/suite/serverFactory.test.ts +++ b/clients/vscode-hlasmplugin/src/test/suite/serverFactory.test.ts @@ -23,7 +23,7 @@ suite('ServerFactory Test Suite', () => { test('non TCP server options test', async () => { // create standard server options - const options = await factory.create(false); + const options = await factory.create('native'); // retrieve executable const execOptions = (options); // check command diff --git a/clients/vscode-hlasmplugin/src/test/workspace/.vscode/settings.json b/clients/vscode-hlasmplugin/src/test/workspace/.vscode/settings.json deleted file mode 100644 index c679343cb..000000000 --- a/clients/vscode-hlasmplugin/src/test/workspace/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "hlasm.continuationHandling" : true -} diff --git a/clients/vscode-hlasmplugin/src/test/workspace/.vscode/settings.native.json b/clients/vscode-hlasmplugin/src/test/workspace/.vscode/settings.native.json new file mode 100644 index 000000000..55caf9096 --- /dev/null +++ b/clients/vscode-hlasmplugin/src/test/workspace/.vscode/settings.native.json @@ -0,0 +1,3 @@ +{ + "hlasm.continuationHandling": true +} diff --git a/clients/vscode-hlasmplugin/src/test/workspace/.vscode/settings.wasm.json b/clients/vscode-hlasmplugin/src/test/workspace/.vscode/settings.wasm.json new file mode 100644 index 000000000..908300bd8 --- /dev/null +++ b/clients/vscode-hlasmplugin/src/test/workspace/.vscode/settings.wasm.json @@ -0,0 +1,4 @@ +{ + "hlasm.continuationHandling": true, + "hlasm.communicationMethod": "wasm" +} diff --git a/cmake/FindFilesystem.cmake b/cmake/FindFilesystem.cmake new file mode 100644 index 000000000..a152e5229 --- /dev/null +++ b/cmake/FindFilesystem.cmake @@ -0,0 +1,247 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +#[=======================================================================[.rst: + +FindFilesystem +############## + +This module supports the C++17 standard library's filesystem utilities. Use the +:imp-target:`std::filesystem` imported target to + +Options +******* + +The ``COMPONENTS`` argument to this module supports the following values: + +.. find-component:: Experimental + :name: fs.Experimental + + Allows the module to find the "experimental" Filesystem TS version of the + Filesystem library. This is the library that should be used with the + ``std::experimental::filesystem`` namespace. + +.. find-component:: Final + :name: fs.Final + + Finds the final C++17 standard version of the filesystem library. + +If no components are provided, behaves as if the +:find-component:`fs.Final` component was specified. + +If both :find-component:`fs.Experimental` and :find-component:`fs.Final` are +provided, first looks for ``Final``, and falls back to ``Experimental`` in case +of failure. If ``Final`` is found, :imp-target:`std::filesystem` and all +:ref:`variables ` will refer to the ``Final`` version. + + +Imported Targets +**************** + +.. imp-target:: std::filesystem + + The ``std::filesystem`` imported target is defined when any requested + version of the C++ filesystem library has been found, whether it is + *Experimental* or *Final*. + + If no version of the filesystem library is available, this target will not + be defined. + + .. note:: + This target has ``cxx_std_17`` as an ``INTERFACE`` + :ref:`compile language standard feature `. Linking + to this target will automatically enable C++17 if no later standard + version is already required on the linking target. + + +.. _fs.variables: + +Variables +********* + +.. variable:: CXX_FILESYSTEM_IS_EXPERIMENTAL + + Set to ``TRUE`` when the :find-component:`fs.Experimental` version of C++ + filesystem library was found, otherwise ``FALSE``. + +.. variable:: CXX_FILESYSTEM_HAVE_FS + + Set to ``TRUE`` when a filesystem header was found. + +.. variable:: CXX_FILESYSTEM_HEADER + + Set to either ``filesystem`` or ``experimental/filesystem`` depending on + whether :find-component:`fs.Final` or :find-component:`fs.Experimental` was + found. + +.. variable:: CXX_FILESYSTEM_NAMESPACE + + Set to either ``std::filesystem`` or ``std::experimental::filesystem`` + depending on whether :find-component:`fs.Final` or + :find-component:`fs.Experimental` was found. + + +Examples +******** + +Using `find_package(Filesystem)` with no component arguments: + +.. code-block:: cmake + + find_package(Filesystem REQUIRED) + + add_executable(my-program main.cpp) + target_link_libraries(my-program PRIVATE std::filesystem) + + +#]=======================================================================] + + +if(TARGET std::filesystem) + # This module has already been processed. Don't do it again. + return() +endif() + +cmake_minimum_required(VERSION 3.10) + +include(CMakePushCheckState) +include(CheckIncludeFileCXX) + +# If we're not cross-compiling, try to run test executables. +# Otherwise, assume that compile + link is a sufficient check. +if(CMAKE_CROSSCOMPILING) + include(CheckCXXSourceCompiles) + macro(_cmcm_check_cxx_source code var) + check_cxx_source_compiles("${code}" ${var}) + endmacro() +else() + include(CheckCXXSourceRuns) + macro(_cmcm_check_cxx_source code var) + check_cxx_source_runs("${code}" ${var}) + endmacro() +endif() + +cmake_push_check_state() + +set(CMAKE_REQUIRED_QUIET ${Filesystem_FIND_QUIETLY}) + +# All of our tests required C++17 or later +set(CMAKE_CXX_STANDARD 17) + +# Normalize and check the component list we were given +set(want_components ${Filesystem_FIND_COMPONENTS}) +if(Filesystem_FIND_COMPONENTS STREQUAL "") + set(want_components Final) +endif() + +# Warn on any unrecognized components +set(extra_components ${want_components}) +list(REMOVE_ITEM extra_components Final Experimental) +foreach(component IN LISTS extra_components) + message(WARNING "Extraneous find_package component for Filesystem: ${component}") +endforeach() + +# Detect which of Experimental and Final we should look for +set(find_experimental TRUE) +set(find_final TRUE) +if(NOT "Final" IN_LIST want_components) + set(find_final FALSE) +endif() +if(NOT "Experimental" IN_LIST want_components) + set(find_experimental FALSE) +endif() + +if(find_final) + check_include_file_cxx("filesystem" _CXX_FILESYSTEM_HAVE_HEADER) + mark_as_advanced(_CXX_FILESYSTEM_HAVE_HEADER) + if(_CXX_FILESYSTEM_HAVE_HEADER) + # We found the non-experimental header. Don't bother looking for the + # experimental one. + set(find_experimental FALSE) + endif() +else() + set(_CXX_FILESYSTEM_HAVE_HEADER FALSE) +endif() + +if(find_experimental) + check_include_file_cxx("experimental/filesystem" _CXX_FILESYSTEM_HAVE_EXPERIMENTAL_HEADER) + mark_as_advanced(_CXX_FILESYSTEM_HAVE_EXPERIMENTAL_HEADER) +else() + set(_CXX_FILESYSTEM_HAVE_EXPERIMENTAL_HEADER FALSE) +endif() + +if(_CXX_FILESYSTEM_HAVE_HEADER) + set(_have_fs TRUE) + set(_fs_header filesystem) + set(_fs_namespace std::filesystem) + set(_is_experimental FALSE) +elseif(_CXX_FILESYSTEM_HAVE_EXPERIMENTAL_HEADER) + set(_have_fs TRUE) + set(_fs_header experimental/filesystem) + set(_fs_namespace std::experimental::filesystem) + set(_is_experimental TRUE) +else() + set(_have_fs FALSE) +endif() + +set(CXX_FILESYSTEM_HAVE_FS ${_have_fs} CACHE BOOL "TRUE if we have the C++ filesystem headers") +set(CXX_FILESYSTEM_HEADER ${_fs_header} CACHE STRING "The header that should be included to obtain the filesystem APIs") +set(CXX_FILESYSTEM_NAMESPACE ${_fs_namespace} CACHE STRING "The C++ namespace that contains the filesystem APIs") +set(CXX_FILESYSTEM_IS_EXPERIMENTAL ${_is_experimental} CACHE BOOL "TRUE if the C++ filesystem library is the experimental version") + +set(_found FALSE) + +if(CXX_FILESYSTEM_HAVE_FS) + # We have some filesystem library available. Do link checks + string(CONFIGURE [[ + #include + #include <@CXX_FILESYSTEM_HEADER@> + + int main() { + auto cwd = @CXX_FILESYSTEM_NAMESPACE@::current_path(); + printf("%s", cwd.c_str()); + return EXIT_SUCCESS; + } + ]] code @ONLY) + + # Check a simple filesystem program without any linker flags + _cmcm_check_cxx_source("${code}" CXX_FILESYSTEM_NO_LINK_NEEDED) + + set(can_link ${CXX_FILESYSTEM_NO_LINK_NEEDED}) + + if(NOT CXX_FILESYSTEM_NO_LINK_NEEDED) + set(prev_libraries ${CMAKE_REQUIRED_LIBRARIES}) + # Add the libstdc++ flag + set(CMAKE_REQUIRED_LIBRARIES ${prev_libraries} -lstdc++fs) + _cmcm_check_cxx_source("${code}" CXX_FILESYSTEM_STDCPPFS_NEEDED) + set(can_link ${CXX_FILESYSTEM_STDCPPFS_NEEDED}) + if(NOT CXX_FILESYSTEM_STDCPPFS_NEEDED) + # Try the libc++ flag + set(CMAKE_REQUIRED_LIBRARIES ${prev_libraries} -lc++fs) + _cmcm_check_cxx_source("${code}" CXX_FILESYSTEM_CPPFS_NEEDED) + set(can_link ${CXX_FILESYSTEM_CPPFS_NEEDED}) + endif() + endif() + + if(can_link) + add_library(std::filesystem INTERFACE IMPORTED) + set_property(TARGET std::filesystem APPEND PROPERTY INTERFACE_COMPILE_FEATURES cxx_std_17) + set(_found TRUE) + + if(CXX_FILESYSTEM_NO_LINK_NEEDED) + # Nothing to add... + elseif(CXX_FILESYSTEM_STDCPPFS_NEEDED) + set_property(TARGET std::filesystem APPEND PROPERTY INTERFACE_LINK_LIBRARIES -lstdc++fs) + elseif(CXX_FILESYSTEM_CPPFS_NEEDED) + set_property(TARGET std::filesystem APPEND PROPERTY INTERFACE_LINK_LIBRARIES -lc++fs) + endif() + endif() +endif() + +cmake_pop_check_state() + +set(Filesystem_FOUND ${_found} CACHE BOOL "TRUE if we can run a program using std::filesystem" FORCE) + +if(Filesystem_FIND_REQUIRED AND NOT Filesystem_FOUND) + message(FATAL_ERROR "Cannot run simple program using std::filesystem") +endif() diff --git a/cmake/compiler_flags.cmake b/cmake/compiler_flags.cmake new file mode 100644 index 000000000..d198b48ee --- /dev/null +++ b/cmake/compiler_flags.cmake @@ -0,0 +1,61 @@ +if (MSVC_VERSION) + string(REGEX REPLACE " /W[0-4]" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4 /wd4251 /wd4996") + set(MY_CXX_WARNING_FLAGS " /w35038") +else() + set(MY_CXX_WARNING_FLAGS " -Wall -Wextra -Wno-attributes -Wno-unknown-pragmas") +endif() + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${MY_CXX_WARNING_FLAGS}") +string(REGEX REPLACE " /Ob[0-4]" "" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}") +string(REGEX REPLACE " /Ob[0-4]" "" CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL}") +string(REGEX REPLACE " /Ob[0-4]" "" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}") +string(REGEX REPLACE " /Ob[0-4]" "" CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}") + +if (MSVC_VERSION) + if(WITH_STATIC_CRT) + set(CompilerFlags + CMAKE_CXX_FLAGS + CMAKE_CXX_FLAGS_RELWITHDEBINFO + CMAKE_CXX_FLAGS_MINSIZEREL + CMAKE_CXX_FLAGS_RELEASE + ) + foreach(CompilerFlag ${CompilerFlags}) + string(REPLACE "/MD" "/MT" ${CompilerFlag} "${${CompilerFlag}}") + + string(REPLACE "/MDd" "/MTd" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}") + endforeach() + endif() + + string(REPLACE "/ZI" "/Zi" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}") + string(REPLACE "/ZI" "/Zi" CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}") + + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Od /MP ${MY_CXX_WARNING_FLAGS}") + set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} /O1 /Oi /Ob2 /Gy /MP /DNDEBUG ${MY_CXX_WARNING_FLAGS}") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /O2 /Oi /Ob2 /Gy /MP /DNDEBUG ${MY_CXX_WARNING_FLAGS}") + set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} /O2 /Oi /Ob2 /Gy /MP ${MY_CXX_WARNING_FLAGS}") + set(CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO} ") +else() + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -g ${MY_CXX_WARNING_FLAGS}") + set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} -Os -DNDEBUG ${MY_CXX_WARNING_FLAGS}") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 -DNDEBUG ${MY_CXX_WARNING_FLGAS}") + set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -O2 -g ${MY_CXX_WARNING_FLAGS}") +endif() + +if(WITH_LIBCXX) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") +endif() + +if(BUILD_FUZZER) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=fuzzer-no-link") +endif() + +if(APPLE) + if(WITH_STATIC_CRT) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lc++abi ${LLVM_PATH}/lib/libc++.a") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -lc++abi ${LLVM_PATH}/lib/libc++.a") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -nostdlib++") + endif() +endif() + +set(CMAKE_FLAGS_PASS -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS} -DCMAKE_EXE_LINKER_FLAGS=${CMAKE_EXE_LINKER_FLAGS} -DCMAKE_SHARED_LINKER_FLAGS=${CMAKE_SHARED_LINKER_FLAGS}) diff --git a/cmake/external_antlr4cpp.cmake b/cmake/external_antlr4cpp.cmake index caaf5c24f..1c260e451 100644 --- a/cmake/external_antlr4cpp.cmake +++ b/cmake/external_antlr4cpp.cmake @@ -10,93 +10,84 @@ # Contributors: # Broadcom, Inc. - initial API and implementation -CMAKE_MINIMUM_REQUIRED(VERSION 3.10) PROJECT(antlr4cpp_fetcher CXX) -INCLUDE(ExternalProject) + +INCLUDE(FetchContent) FIND_PACKAGE(Git REQUIRED) # only JRE required FIND_PACKAGE(Java COMPONENTS Runtime REQUIRED) -############ Download and Generate runtime ################# -set(ANTLR4CPP_EXTERNAL_ROOT ${CMAKE_BINARY_DIR}/externals/antlr4cpp) +if(APPLE) + find_library(COREFOUNDATION_LIBRARY CoreFoundation) +endif() + +#Check whether maven is installed and in path +find_program(MVN_RETURN mvn) +if(MVN_RETURN MATCHES "MVN_RETURN-NOTFOUND") + message(FATAL_ERROR "Cannot find mvn. Are you sure maven is installed and in the path?" ) +endif() + +if(CMAKE_SYSTEM_NAME MATCHES "Linux") + find_package(PkgConfig REQUIRED) + pkg_check_modules(UUID REQUIRED uuid) +endif() # external repository # GIT_REPOSITORY https://github.com/antlr/antlr4.git set(ANTLR4CPP_EXTERNAL_REPO "https://github.com/antlr/antlr4.git") set(ANTLR4CPP_EXTERNAL_TAG "4.7.2") +set(ANTLR_VERSION ${ANTLR4CPP_EXTERNAL_TAG}) -set(ANTLR_VERSION "4.7.2") +FetchContent_Declare( + antlr4cpp + GIT_REPOSITORY https://github.com/antlr/antlr4.git + GIT_TAG ${ANTLR4CPP_EXTERNAL_TAG} + GIT_SHALLOW ON + LOG_DOWNLOAD ON + GIT_PROGRESS 1 + PATCH_COMMAND ${CMAKE_COMMAND} -DGIT_EXECUTABLE=${GIT_EXECUTABLE} -DPROJECT_SOURCE_DIR=${PROJECT_SOURCE_DIR} -DCMAKE_BINARY_DIR=${CMAKE_BINARY_DIR} -P ${PROJECT_SOURCE_DIR}/cmake/apply_patch.cmake +) -if(BUILD_SHARED_LIBS) - if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") - set(ANTLR4_LIB_FILENAME "antlr4-runtime.dll") - elseif(APPLE) - set(ANTLR4_LIB_FILENAME "antlr4-runtime.${ANTLR_VERSION}.dylib") - else() - set(ANTLR4_LIB_FILENAME "antlr4-runtime.so.${ANTLR_VERSION}") - endif() -else() - if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") - set(ANTLR4_LIB_FILENAME "antlr4-runtime-static.lib") - else() - set(ANTLR4_LIB_FILENAME "libantlr4-runtime.a") - endif() -endif() +FetchContent_GetProperties(antlr4cpp) -# download runtime environment -ExternalProject_ADD( - antlr4cpp - PREFIX ${ANTLR4CPP_EXTERNAL_ROOT} - GIT_REPOSITORY ${ANTLR4CPP_EXTERNAL_REPO} - GIT_TAG ${ANTLR4CPP_EXTERNAL_TAG} - GIT_SHALLOW ON - # the fix for https://github.com/antlr/antlr4/issues/2550 - PATCH_COMMAND ${CMAKE_COMMAND} -DGIT_EXECUTABLE=${GIT_EXECUTABLE} -DPROJECT_SOURCE_DIR=${PROJECT_SOURCE_DIR} -DCMAKE_BINARY_DIR=${CMAKE_BINARY_DIR} -P ${PROJECT_SOURCE_DIR}/cmake/apply_patch.cmake - TIMEOUT 10 - LOG_DOWNLOAD ON - GIT_PROGRESS 1 - CMAKE_ARGS -G ${CMAKE_GENERATOR} -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS} -DCMAKE_EXE_LINKER_FLAGS=${CMAKE_EXE_LINKER_FLAGS} -DCMAKE_SHARED_LINKER_FLAGS=${CMAKE_SHARED_LINKER_FLAGS} -DWITH_LIBCXX=${WITH_LIBCXX} -DBUILD_SHARED_LIBS=ON -DWITH_STATIC_CRT=${WITH_STATIC_CRT} -DCMAKE_MACOSX_RPATH=ON -DBUILD_TESTS=OFF -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH= - SOURCE_SUBDIR runtime/Cpp - LOG_CONFIGURE ON - LOG_BUILD ON - BUILD_BYPRODUCTS ${CMAKE_BINARY_DIR}/externals/antlr4cpp/lib/${ANTLR4_LIB_FILENAME} -) +function(add_antlr4) + set(PROJECT_SOURCE_DIR ${antlr4cpp_SOURCE_DIR}/runtime/Cpp) + set(LIB_OUTPUT_DIR ${CMAKE_BINARY_DIR}/bin) + add_subdirectory(${antlr4cpp_SOURCE_DIR}/runtime/Cpp/runtime ${antlr4cpp_BINARY_DIR} EXCLUDE_FROM_ALL) -ExternalProject_Get_Property(antlr4cpp INSTALL_DIR) + target_include_directories(antlr4_shared INTERFACE ${antlr4cpp_SOURCE_DIR}/runtime/Cpp/runtime/src) + target_include_directories(antlr4_static INTERFACE ${antlr4cpp_SOURCE_DIR}/runtime/Cpp/runtime/src) + target_compile_definitions(antlr4_static INTERFACE ANTLR4CPP_STATIC=1) +endfunction() +if(NOT antlr4cpp_POPULATED) + FetchContent_Populate(antlr4cpp) + add_antlr4() # set ANTLR jar location -set(ANTLR_JAR_LOCATION - ${ANTLR4CPP_EXTERNAL_ROOT}/src/antlr4cpp/tool/target/antlr4-4.7.2-complete.jar) - -add_custom_command( - OUTPUT - ${ANTLR_JAR_LOCATION} - DEPENDS - antlr4cpp - COMMAND - mvn -DskipTests install - COMMENT - "Building ANTLR jar..." - WORKING_DIRECTORY - ${ANTLR4CPP_EXTERNAL_ROOT}/src/antlr4cpp/tool/ - VERBATIM - ) - -add_custom_target(antlr4jar - DEPENDS - ${ANTLR_JAR_LOCATION}) - -list(APPEND ANTLR4CPP_INCLUDE_DIRS ${INSTALL_DIR}/include/antlr4-runtime) -foreach(src_path misc atn dfa tree support) - list(APPEND ANTLR4CPP_INCLUDE_DIRS ${INSTALL_DIR}/include/antlr4-runtime/${src_path}) -endforeach(src_path) - -set(ANTLR4CPP_LIBS "${INSTALL_DIR}/lib") + set(ANTLR_JAR_LOCATION + ${antlr4cpp_SOURCE_DIR}/tool/target/antlr4-${ANTLR4CPP_EXTERNAL_TAG}-complete.jar) + + add_custom_command( + OUTPUT + ${ANTLR_JAR_LOCATION} + COMMAND + mvn -DskipTests install + COMMENT + "Building ANTLR jar..." + WORKING_DIRECTORY + ${antlr4cpp_SOURCE_DIR}/tool/ + VERBATIM + ) + + add_custom_target(antlr4jar + DEPENDS + ${ANTLR_JAR_LOCATION}) +endif() if(BUILD_SHARED_LIBS) - set(ANTLR4_RUNTIME ${ANTLR4_LIB_FILENAME}) + set(ANTLR4_RUNTIME antlr4_shared) else() - set(ANTLR4_RUNTIME "${ANTLR4CPP_LIBS}/${ANTLR4_LIB_FILENAME}") + set(ANTLR4_RUNTIME antlr4_static) endif() diff --git a/cmake/external_boost.cmake b/cmake/external_boost.cmake index 503bdb442..12ed88d52 100644 --- a/cmake/external_boost.cmake +++ b/cmake/external_boost.cmake @@ -10,24 +10,22 @@ # Contributors: # Broadcom, Inc. - initial API and implementation -cmake_minimum_required (VERSION 3.10) - project(boost-asio) -include(ExternalProject) +include(FetchContent) -FIND_PACKAGE(Git REQUIRED) -ExternalProject_Add(boost_ext - PREFIX ${CMAKE_BINARY_DIR}/externals/boost - GIT_REPOSITORY https://github.com/chriskohlhoff/asio.git - GIT_TAG asio-1-12-2 - GIT_SHALLOW ON - CONFIGURE_COMMAND "" - BUILD_COMMAND "" - INSTALL_COMMAND "" - TEST_COMMAND "" +FetchContent_Declare( + boost_ext + GIT_REPOSITORY https://github.com/chriskohlhoff/asio.git + GIT_TAG asio-1-12-2 + GIT_SHALLOW ON + LOG_DOWNLOAD ON + GIT_PROGRESS 1 ) +if(NOT boost_ext_POPULATED) + FetchContent_Populate(boost_ext) +endif() -ExternalProject_Get_Property(boost_ext INSTALL_DIR) -set(BOOST_INCLUDE_DIRS ${INSTALL_DIR}/src/boost_ext/asio/include/) \ No newline at end of file +add_library(boost-asio INTERFACE) +target_include_directories(boost-asio INTERFACE ${boost_ext_SOURCE_DIR}/asio/include) diff --git a/cmake/external_gtest.cmake b/cmake/external_gtest.cmake index 25970aab1..d554e15b4 100644 --- a/cmake/external_gtest.cmake +++ b/cmake/external_gtest.cmake @@ -10,21 +10,28 @@ # Contributors: # Broadcom, Inc. - initial API and implementation -cmake_minimum_required (VERSION 3.10) +project(googletest-download NONE) +include(FetchContent) -project(googletest-download NONE) +FetchContent_Declare(googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG release-1.10.0 + LOG_DOWNLOAD ON + GIT_PROGRESS 1 + ) + +FetchContent_GetProperties(googletest) -set(GTEST_EXTERNAL_ROOT ${CMAKE_BINARY_DIR}/externals/googletest) +function(add_googletest) + set(CMAKE_SUPPRESS_DEVELOPER_WARNINGS 1 CACHE BOOL "") + set(BUILD_SHARED_LIBS Off) + set(gtest_force_shared_crt ${WITH_STATIC_CRT} CACHE BOOL "" FORCE) + add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR} EXCLUDE_FROM_ALL) + unset(CMAKE_SUPPRESS_DEVELOPER_WARNINGS) +endfunction() -include(ExternalProject) -ExternalProject_Add(googletest - GIT_REPOSITORY https://github.com/google/googletest.git - GIT_TAG release-1.10.0 - SOURCE_DIR ${CMAKE_BINARY_DIR}/googletest-src - BINARY_DIR ${CMAKE_BINARY_DIR}/googletest-build - CONFIGURE_COMMAND "" - BUILD_COMMAND "" - INSTALL_COMMAND "" - TEST_COMMAND "" -) +if(NOT googletest_POPULATED) + FetchContent_Populate(googletest) + add_googletest() +endif() diff --git a/cmake/external_json.cmake b/cmake/external_json.cmake index 5c255c696..e37fcb06f 100644 --- a/cmake/external_json.cmake +++ b/cmake/external_json.cmake @@ -10,38 +10,25 @@ # Contributors: # Broadcom, Inc. - initial API and implementation -CMAKE_MINIMUM_REQUIRED(VERSION 3.5) PROJECT(JSON_fetcher) -INCLUDE(ExternalProject) -FIND_PACKAGE(Git REQUIRED) -############ Download and Generate runtime ################# -set(JSON_EXTERNAL_ROOT ${CMAKE_BINARY_DIR}/externals/json) +INCLUDE(FetchContent) -# external repository -set(JSON_EXTERNAL_REPO "https://github.com/nlohmann/json.git") -set(JSON_EXTERNAL_TAG "v3.3.0") - - -# download runtime environment -ExternalProject_ADD( +FetchContent_Declare( json - PREFIX ${JSON_EXTERNAL_ROOT} - GIT_REPOSITORY ${JSON_EXTERNAL_REPO} - GIT_TAG ${JSON_EXTERNAL_TAG} - GIT_SHALLOW ON - TIMEOUT 10 - LOG_DOWNLOAD ON - GIT_PROGRESS 1 - CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH= - CONFIGURE_COMMAND "" - BUILD_COMMAND "" - INSTALL_COMMAND "" - TEST_COMMAND "" + GIT_REPOSITORY https://github.com/nlohmann/json.git + GIT_TAG v3.3.0 + GIT_SHALLOW ON + LOG_DOWNLOAD ON + GIT_PROGRESS 1 ) -ExternalProject_Get_Property(json INSTALL_DIR) +set(JSON_BuildTests Off) -set(JSON_INCLUDE_DIRS ${INSTALL_DIR}/src/json/single_include/nlohmann) +FetchContent_GetProperties(json) +if(NOT json_POPULATED) + FetchContent_Populate(json) + add_subdirectory(${json_SOURCE_DIR} ${json_BINARY_DIR} EXCLUDE_FROM_ALL) +endif() diff --git a/cmake/external_uri.cmake b/cmake/external_uri.cmake index 2071716c2..b04c288b4 100644 --- a/cmake/external_uri.cmake +++ b/cmake/external_uri.cmake @@ -10,36 +10,41 @@ # Contributors: # Broadcom, Inc. - initial API and implementation -CMAKE_MINIMUM_REQUIRED(VERSION 3.5) PROJECT(uri_fetcher CXX) INCLUDE(ExternalProject) FIND_PACKAGE(Git REQUIRED) -if(WITH_LIBCXX) - set(DISABLE_LIBCXX Off) -else() - set(DISABLE_LIBCXX On) -endif() +include(FetchContent) # download runtime environment -ExternalProject_ADD( +FetchContent_Declare( uri_ext - PREFIX ${CMAKE_BINARY_DIR}/externals/uri - GIT_REPOSITORY "https://github.com/cpp-netlib/uri.git" - GIT_TAG v1.1.0 - GIT_SHALLOW ON - TIMEOUT 10 - LOG_DOWNLOAD ON - GIT_PROGRESS 1 - CMAKE_ARGS -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS} -DUri_DISABLE_LIBCXX=${DISABLE_LIBCXX} -DUri_BUILD_DOCS=OFF -DUri_BUILD_TESTS=OFF -DUri_USE_STATIC_CRT=${WITH_STATIC_CRT} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH= -DUri_WARNINGS_AS_ERRORS=OFF + GIT_REPOSITORY https://github.com/cpp-netlib/uri.git + GIT_TAG 9f477677a1fefa235027a64c47db5fca53e7f61d + GIT_SHALLOW ON + LOG_DOWNLOAD ON + GIT_PROGRESS 1 ) -ExternalProject_Get_Property(uri_ext INSTALL_DIR) - +FetchContent_GetProperties(uri_ext) +function(add_uri_ext) + if(WITH_LIBCXX) + set(Uri_DISABLE_LIBCXX Off) + else() + set(Uri_DISABLE_LIBCXX On) + endif() + set(Uri_BUILD_TESTS Off) + set(Uri_BUILD_DOCS Off) + set(Uri_WARNINGS_AS_ERRORS Off) -set(URI_INCLUDE_DIRS ${INSTALL_DIR}/include/) - -set(URI_LIBS "${INSTALL_DIR}/lib") + add_subdirectory(${uri_ext_SOURCE_DIR}/src ${uri_ext_BINARY_DIR} EXCLUDE_FROM_ALL) + target_include_directories(network-uri PRIVATE ${uri_ext_SOURCE_DIR}/src) + target_include_directories(network-uri PUBLIC ${uri_ext_SOURCE_DIR}/include) +endfunction() +if(NOT uri_ext_POPULATED) + FetchContent_Populate(uri_ext) + add_uri_ext() +endif() diff --git a/cmake/no_viable_shared.diff b/cmake/no_viable_shared.diff index 81973150d..dcaa46cd7 100644 --- a/cmake/no_viable_shared.diff +++ b/cmake/no_viable_shared.diff @@ -1,5 +1,29 @@ +diff --git a/runtime/Cpp/runtime/CMakeLists.txt b/runtime/Cpp/runtime/CMakeLists.txt +index 2c5e737..6b968d8 100644 +--- a/runtime/Cpp/runtime/CMakeLists.txt ++++ b/runtime/Cpp/runtime/CMakeLists.txt +@@ -25,8 +25,8 @@ file(GLOB libantlrcpp_SRC + add_library(antlr4_shared SHARED ${libantlrcpp_SRC}) + add_library(antlr4_static STATIC ${libantlrcpp_SRC}) + +-set(LIB_OUTPUT_DIR "${CMAKE_HOME_DIRECTORY}/dist") # put generated libraries here. +-message(STATUS "Output libraries to ${LIB_OUTPUT_DIR}") ++#set(LIB_OUTPUT_DIR "${CMAKE_HOME_DIRECTORY}/dist") # put generated libraries here. ++#message(STATUS "Output libraries to ${LIB_OUTPUT_DIR}") + + # make sure 'make' works fine even if ${LIB_OUTPUT_DIR} is deleted. + add_custom_target(make_lib_output_dir ALL +@@ -74,7 +74,7 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") + endif() + + set(static_lib_suffix "") +-if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") ++if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC" OR EMSCRIPTEN) + set(static_lib_suffix "-static") + endif() + diff --git a/runtime/Cpp/runtime/src/NoViableAltException.cpp b/runtime/Cpp/runtime/src/NoViableAltException.cpp -index ced7f827f..83fb78b3b 100755 +index ced7f82..83fb78b 100755 --- a/runtime/Cpp/runtime/src/NoViableAltException.cpp +++ b/runtime/Cpp/runtime/src/NoViableAltException.cpp @@ -16,13 +16,16 @@ NoViableAltException::NoViableAltException(Parser *recognizer) @@ -31,7 +55,7 @@ index ced7f827f..83fb78b3b 100755 + return _deadEndConfigs.get(); } diff --git a/runtime/Cpp/runtime/src/NoViableAltException.h b/runtime/Cpp/runtime/src/NoViableAltException.h -index 94d43c54c..415a0d9db 100755 +index 94d43c5..415a0d9 100755 --- a/runtime/Cpp/runtime/src/NoViableAltException.h +++ b/runtime/Cpp/runtime/src/NoViableAltException.h @@ -27,10 +27,7 @@ namespace antlr4 { @@ -47,7 +71,7 @@ index 94d43c54c..415a0d9db 100755 /// The token object at the start index; the input stream might /// not be buffering tokens so get a reference to it. (At the diff --git a/runtime/Cpp/runtime/src/support/StringUtils.h b/runtime/Cpp/runtime/src/support/StringUtils.h -index d0a0472a0..49715287e 100644 +index d0a0472..4971528 100644 --- a/runtime/Cpp/runtime/src/support/StringUtils.h +++ b/runtime/Cpp/runtime/src/support/StringUtils.h @@ -22,7 +22,7 @@ namespace antlrcpp { diff --git a/docs/Build-instructions.md b/docs/Build-instructions.md index 3397bf10b..0297fd823 100644 --- a/docs/Build-instructions.md +++ b/docs/Build-instructions.md @@ -38,16 +38,16 @@ In addition to the prerequisites listed in \[prereq\], the Linux build has two m - UUID library -We build the project for Ubuntu 18.04 and for the Alpine Linux. +We build the project for Ubuntu 20.04 and for the Alpine Linux. ### Ubuntu -On Ubuntu 18.04 the following commands install all prerequisites and then build the project into the `build` folder: +On Ubuntu 20.04 the following commands install all prerequisites and then build the project into the `build` folder: - apt update && sudo apt install cmake g++-8 uuid-dev npm default-jdk + apt update && sudo apt install cmake g++-10 uuid-dev npm default-jdk pkg-config maven mkdir build && cd build - cmake -DCMAKE_C_COMPILER=gcc-8 -DCMAKE_CXX_COMPILER=g++-8 ../ + cmake -DCMAKE_C_COMPILER=gcc-10 -DCMAKE_CXX_COMPILER=g++-10 ../ cmake --build . ### Alpine Linux @@ -60,10 +60,24 @@ The build works on Alpine linux version 3.10. The following commands install all cmake ../ cmake --build . +WASM +---- + +The project can be built for the WASM target using the Emscripten SDK. Currently the only verified docker image is emscripten/emsdk:2.0.12, also utilized in the CI pipeline. + + apt update && apt-get install -y ninja-build maven + mkdir build && cd build + emcmake cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DDISCOVER_TESTS=Off -DWITH_LIBCXX=Off -DWITH_STATIC_CRT=Off -DCMAKE_EXE_LINKER_FLAGS="-s NODERAWFS=1" -DCMAKE_CXX_FLAGS="-s USE_PTHREADS=1 -s PTHREAD_POOL_SIZE=8 -s TOTAL_MEMORY=268435456 -s PROXY_TO_PTHREAD=1 -fexceptions -s NODERAWFS=1 -s EXIT_RUNTIME=1 --bind" -DCMAKE_CROSSCOMPILING_EMULATOR="node;--experimental-wasm-threads;--experimental-wasm-bulk-memory" -Dgtest_disable_pthreads=On ../ + cmake --build . + +The project tests or the language server itself then needs to be run in the Node with several experimental features enabled. + + node --experimental-wasm-threads --experimental-wasm-bulk-memory language_server.js + Mac OS ------ -We have only built the project on MacOS 10.14. In order to successfully build, first install LLVM 8 using homebrew. +We have only built the project on MacOS 10.14. In order to successfully build, first install LLVM 8 or 10+ using homebrew. The project can be built with a snippet like this: @@ -72,7 +86,7 @@ The project can be built with a snippet like this: -DLLVM_PATH= ../ cmake --build . -For instance, a possible path to LLVM is `/usr/local/opt/llvm8` +For instance, a possible path to LLVM is `/usr/local/opt/llvm8`. You may use `$(brew --prefix llvm)` as the path to the LLVM installation directory. Running Tests ------------- diff --git a/docs/Language-server-pages/IO-handling.md b/docs/Language-server-pages/IO-handling.md index 4cfc7efc8..90dfc6f8c 100644 --- a/docs/Language-server-pages/IO-handling.md +++ b/docs/Language-server-pages/IO-handling.md @@ -1,5 +1,9 @@ -The purpose of the `dispatcher` is to abstract from the complexity of working with raw strings and streams. It executes an infinite loop in which it reads messages from `std::iostream` and adds them to the [`request_manager`](https://github.com/eclipse/che-che4z-lsp-for-hlasm/wiki/Request-manager) as parsed JSON objects. At the same time, it is able to write responses in the correct format. +The purpose of the `dispatcher` is to execute an infinite loop in which it read JSON messages from `json_source` and passes them to the [`request_manager`](https://github.com/eclipse/che-che4z-lsp-for-hlasm/wiki/Request-manager). At the same time, it is able to write responses in the correct format. -The language server communicates with the LSP client on a standard input and output, so we use the `dispatcher` with the standard `std::cin` and `std::cout` objects to communicate with the LSP client. +The language server communicates with the LSP client either via standard input and output or a TCP/IP connection, in the case of running under Node.js only standard I/O channels are supported. +In the case of TCP/IP, the VS Code extension identified a free TCP port and passes it to the language server as an argument. Thanks to the ASIO library (see [[third party libraries]]) implementation of the `std::iostream` interface, the implemenation is is able to completely abstract from the fact that it is communicating through TCP and not through the standard IO. -The DAP communicates using TCP/IP, which is less straightforward. Before the VS Code extension starts the language server, it finds a free TCP port and passes it as an argument to the language server executable. The `TCP handler` then starts listening on that port. Once the user wants to start the macro tracer, the DAP client connects to the port on localhost. The `tcp_handler` accepts the TCP client and creates a `dispatcher` and a `dap_server`. Once the DAP communication ends, both the `dispatcher` and the `dap_server` are destroyed and the `tcp_handler` starts listening again for the next DAP session. Thanks to the ASIO library (see [[third party libraries]]) implementation of the `std::iostream` interface, the `dispatcher` is able to completely abstract from the fact that it is communicating through TCP and not through the standard IO. +The DAP communication is tunneled via notification messages through the existing LSP channel. Incomming messages are routed to the correct components via `message_router` class. In case of DAP messages the incomming message envelop is stripped away by `dap::unmessage_wrapper`, similarly outgoing messages are wrapped by `dap::message_wrapper`. Both of these classes implement `json_source` or `json_sink` respectively to shield the server implementation from this implementation detail. +Once the macro tracer is started, a registration message is processed by the server and a new `dap_session` is started by the `dap_session_manager` to handle the debugging session. + +Both the LSP server and (possibly multiple) DAP servers run in their own thread. All of them use simple `blocking_queue` for passing parsed JSON objects from the main thread. \ No newline at end of file diff --git a/docs/Language-server-pages/LSP-and-DAP-server.md b/docs/Language-server-pages/LSP-and-DAP-server.md index 02a6fc69f..7aa6b33b1 100644 --- a/docs/Language-server-pages/LSP-and-DAP-server.md +++ b/docs/Language-server-pages/LSP-and-DAP-server.md @@ -21,12 +21,11 @@ The following table shows the list of all implemented LSP methods and the classe |Language feature| textDocument/definition
textDocument/references
textDocument/hover
textDocument/completion| | -The DAP server uses only one feature — the launch feature, which handles stepping through the code and retrieving information about both variables and stack trace. The following table shows the list of all implemented DAP methods: +The DAP server uses only one feature — the dap feature, which handles stepping through the code and retrieving information about both variables and stack trace. The following table shows the list of all implemented DAP methods: | **Class** | **DAP Method Name** | |:----------|:---------------------------------| -|`dap_server`| `initialize`
`disconnect`
`launch`| -|`feature_launch`| `setBreakpoints`
`configurationDone`
`threads`
`stackTrace`
`scopes`
`next`
`stepIn`
`variables`
`continue`
`stopped`
`exited`
`terminated`| +|`dap_feature`| `initialize`
`disconnect`
`launch`
`setBreakpoints`
`configurationDone`
`threads`
`stackTrace`
`scopes`
`next`
`stepIn`
`variables`
`continue`
`stopped`
`exited`
`terminated`| | Response With Result diff --git a/docs/Language-server-pages/Language-server-overview.md b/docs/Language-server-pages/Language-server-overview.md index 8d45f13ec..683092e37 100644 --- a/docs/Language-server-pages/Language-server-overview.md +++ b/docs/Language-server-pages/Language-server-overview.md @@ -1,8 +1,8 @@ Architecture of language server. -The architecture of the language server component is illustrated in the picture above. It communicates on the standard input/output via LSP with the LSP client and listens on a TCP port to provide DAP support for the macro tracer. The TCP communication is wrapped by the class `tcp_handler`, which abstracts from the complexity of communicating through TCP/IP. +The architecture of the language server component is illustrated in the picture above. It communicates via the standard input/output or TCP/IP connection using LSP with the LSP client. The low level details of receiving and sending JSON messages are abstracted away by `json_source` and `json_sink` interfaces. Specific implementations are provided during the server start-up depending on parameters and the environemnt by `server_streams` implementation. -The main purpose of the class [`dispatcher`](https://github.com/eclipse/che-che4z-lsp-for-hlasm/wiki/IO-handling) is to provide abstraction for the lowest level communication, which is shared by LSP and DAP. It reads iostream to parse messages using the JSON for Modern C++ library (see [[third party libraries]]) and stores them in the [`request_manager`](https://github.com/eclipse/che-che4z-lsp-for-hlasm/wiki/Request-manager) as `requests`. +The main purpose of the class [`dispatcher`](https://github.com/eclipse/che-che4z-lsp-for-hlasm/wiki/IO-handling) is to implement a message loop shared by LSP and DAP. It reads `json_source` to get messages parsed using the JSON for Modern C++ library (see [[third party libraries]]) and stores them in the [`request_manager`](https://github.com/eclipse/che-che4z-lsp-for-hlasm/wiki/Request-manager) as `requests`. A `request` encapsulates one message that came from the client and is represented only by raw (but parsed) JSON. @@ -19,10 +19,10 @@ Example: Hover Request Handling The image above shows handling of the hover request in the language server. The hover request is sent from the LSP client to the `lsp_server` when the user hovers over the text of a file. The hover request contains the location of the mouse cursor in text, i.e. the name of the file and the number of the line and column where the cursor is. The LSP client then expects a response containing a string (possibly written in markdown language) to be shown in a tooltip box. -The whole process begins with reading from the standard input by the LSP instance of the `dispatcher`. It first reads the header of the message, which contains information about the length of the following JSON. Then it reads the JSON itself and deserializes it using the JSON for Modern C++ library (see [[third party libraries]]). All other components of the language server work only with the parsed representation of the message. The `dispatcher` adds the message to the `request_manager` and returns to reading the next message from the standard input. +The whole process begins with reading from the standard input by the instance of the `base_protocol_channel` (which implements the `json_channel`). It first reads the header of the message, which contains information about the length of the following JSON. Then it reads the JSON itself and deserializes it using the JSON for Modern C++ library (see [[third party libraries]]). All other components of the language server work only with the parsed representation of the message. The parsed JSON is pushed onto a queue associated with the LSP server thread. The `dispatcher` pops the message off the queue and adds the message to the `request_manager` and returns to reading the next message from the `json_source`. The request in the `request_manager` either waits in a queue to be processed, or, if the queue was empty, the worker thread is woken up from sleep using conditional variable. The worker then passes the JSON to the `lsp_server`, which looks at the name of the method written in the message and calls the method “hover” from the language feature. The hover method unpacks the actual arguments from the JSON and converts any URIs to paths using the cpp-netlib URI library. Then, it calls the hover method from the parser library, which returns a string to be shown in the tooltip next to the hovering mouse. The language feature then wraps the return value back in JSON and calls the `respond` method of its `response_provider` implemented by the `lsp_server`. -The `lsp_server` wraps JSON arguments into a LSP response and uses the `send message provider` implemented by `dispatcher` to send it to the LSP client. The `dispatcher` serializes the JSON, adds the header with the length of the JSON and writes the message to a standard output. Finally, all methods return and the worker thread in `request_manager` looks for another request. If there is none, it goes to sleep. +The `lsp_server` wraps JSON arguments into a LSP response and uses the `send message provider` implemented by `dispatcher` to send it to the LSP client. The `dispatcher` currently just passes the JSON message to `base_protocol_channel` for serialization (via the `json_sink` interface), which adds the header with the length of the JSON and writes the message to a standard output. Finally, all methods return and the worker thread in `request_manager` looks for another request. If there is none, it goes to sleep. diff --git a/docs/img/all_arch.svg b/docs/img/all_arch.svg index f6c9a2773..7dc03aec5 100644 --- a/docs/img/all_arch.svg +++ b/docs/img/all_arch.svg @@ -1,3 +1,3 @@ -
 Language server
 Language server
 Workspace
 manager
Workspace...
Analyzer
Analyzer
Macro tracer 
Macro tracer 
Machine expressions
Machine expressions
use
use
CA Expressions
CA Expressions
Parser and lexer
Parser and lexer
uses
uses
uses
uses
Processing manager
Processing manager
reads/writes
reads/writes
uses
uses
Dispatcher
Dispatcher
uses
uses
Server
Server
runs
runs
uses
uses
Request manager
Request mana...
LSP communication
(standard IO)
LSP communicati...
DAP communication
(TCP loopback)
DAP communicati...
implements
implements
uses
uses
LSP server
LSP server
Request
Request
implements
implements
uses
uses
DAP server
DAP server
Feature
Feature
implements
implements
LSP feature
LSP feature
LSP feature
LSP feature
use
use
LSP features
LSP features
implements
implements
use
use
DAP features
DAP features
uses
uses
reads/writes
reads/writes
TCP handler
TCP handler
HLASM context
HLASM context
Macro storage
Macro storage
Copy storage
Copy storage
Source stack
Source stack
ID storage
ID storage
uses
uses
Global variable symbol storage
Global variable...
Scope stack
Scope stack
LSP context
LSP context
Processing stack
Processing stack
uses
uses
LSP data
collector
LSP data...
implements
implements
Ordinary assembly context
Ordinary assembly context
has
has
Symbol storage
Symbol stora...
has
has
Section storage
Section stor...
Symbol dependency tables
Symbol depen...
Symbol
Symbol
Value
Value
Attributes
Attributes
Variable symbol
Variable sym...
extends
extends
Macro parameter base
Macro parame...
implements
implements
Positional parameter
Positional p...
implements
implements
Keyword parameter
Keyword para...
implements
implements
System variable
System varia...
extends
extends
SET symbol base
SET symbol b...
SETC symbol
SETC symbol
SETB symbol
SETB symbol
implements
implements
SETA symbol
SETA symbol
Operand
Operand
implements
implements
Empty Operand
Empty Operand
implements
implements
uses
uses
Model Operand
Model Operand
uses
uses
Machine Operand
Machine Oper...
implements
implements
Expression Machine operand
Expression M...
implements
implements
Address Machine operand
Address Mach...
Assembler Operand
Assembler Op...
implements
implements
Expression Assembler operand
Expression A...
uses
uses
USING Assembler operand
USING Assemb...
Complex Assembler operand
Complex Asse...
String Assembler operand
String Assem...
implements
implements
uses
uses
Data Definition Assembler operand
Data Definitio...
extends
extends
uses
uses
Conditional Assembly Operand
Conditional...
implements
implements
Variable symbol CA operand
Variable sym...
Sequnece symbol CA operand
Sequnece sym...
Branch CA operand
Branch CA op...
implements
implements
Expression CA operand
Expression C...
implements
implements
Macro Operand
Macro Operand
Evaluable Operand
Evaluable Op...
uses
uses
implements
implements
Debugger
Debugger
Processor tracer
Processor tracer
Variable
Variable
implements
implements
uses
uses
Ordinary symbol variable
Ordinary symbol...
implements
implements
Set symbol variable
Set symbol vari...
implements
implements
uses
uses
Macro parameter  variable
Macro parameter...
implements
implements
Attribute variable
Attribute varia...
uses
uses
implements
implements
implements
implements
generates
generates
uses
uses
uses
uses
Parser
Parser
uses
uses
Token stream
Token stream
uses
uses
uses
uses
uses
uses
Lexer
Lexer
Input Source
Input Source
Instruction format validation
Instruction format validation
uses
uses
uses
uses
Processing Manager
Processing M...
Statement Processors
Statement Pr...
Statement Provider
Statement Pr...
implements
implements
Opencode statement Provider
Opencode sta...
implements
implements
Copy statement Provider
Copy stateme...
implements
implements
Macro statement Provider
Macro statem...
implements
implements
uses
uses
Lookahead Processor
Lookahead Pr...
implements
implements
Macro definition Processor
Macro defini...
implements
implements
notifies
notifies
Opencode Processor
Opencode Pro...
implements
implements
Copy definition Processor
Copy definit...
Instruction Processors
Instruction...
implements
implements
uses
uses
Conditional Assembly IP
Conditional...
uses
uses
Machine instruction checker
Machine inst...
implements
implements
uses
uses
Assembler
IP
Assembler...
Assembler instruction checker
Assembler in...
implements
implements
Macro IP
Macro IP
implements
implements
uses
uses
uses
uses
Machine IP
Machine IP
holds
holds
uses
uses
uses
uses
Workspace manager
Workspace manager
holds
holds
holds
holds
File manager
File manager
File
File
extends
extends
Processor file
Processor file
uses
uses
uses
uses
implements
implements
Workspace
Workspace
uses
uses
uses
uses
uses
uses
Analyzer
Analyzer
uses
uses
Library
Library
uses
uses
implements
implements
Local library
Local library
Parse library provider
Parse library p...
uses
uses
Processor group
Processor group
Token
Token
Statement fields parser
Statement fi...
uses
uses
creates
creates
Token factory
Token factory
Statement
Statement
uses
uses
Collector
Collector
uses
uses
Machine expression
Machine expression
uses
uses
uses
uses
evaluates
evaluates
Expression evaluator
Expression eva...
CA expression
CA expression
implements
implements
CAE symbol
CAE symbol
CAE symbol attribute
CAE symbol at...
CAE function
CAE function
CAE variable symbol
CAE variable...
ME constant
ME constant
implements
implements
ME data attribute
ME data attribu...
implements
implements
ME symbol
ME symbol
implements
implements
ME
 self defining term
ME...
ME operator
ME operator
uses
uses
uses
uses
Data definition
Data definition
Nominal value
Nominal value
Data definition types
Data definit...
Concatenation
Concatenation
Section
Section
Location counter storage
Location cou...
Dependency solver
Dependency s...
reads/writes
reads/writes
reads/writes
reads/writes
uses
uses
Visual Studio Code
Visual Studio Code
Extension
Extension
Diagnosable
Diagnosable
uses
uses
CAE constant
CAE constant
implements
implements
uses
uses
CAE expression list
CAE expressio...
CAE string
CAE string
CAE policy
CAE policy
Viewer does not support full SVG 1.1
\ No newline at end of file +
 Language server
 Language server
 Workspace
 manager
Workspace...
Analyzer
Analyzer
Macro tracer 
Macro tracer 
Machine expressions
Machine expressions
use
use
CA Expressions
CA Expressions
Parser and lexer
Parser and lexer
uses
uses
uses
uses
Processing manager
Processing manager
uses
uses
Dispatcher
Dispatcher
uses
uses
Server
Server
runs
runs
uses
uses
Request manager
Request mana...
standard IO
standard IO
TCP loopback
TCP loopback
implements
implements
uses
uses
LSP server
LSP server
Request
Request
implements
implements
uses
uses
DAP server
DAP server
Feature
Feature
implements
implements
LSP feature
LSP feature
LSP feature
LSP feature
use
use
LSP features
LSP features
implements
implements
use
use
DAP features
DAP features
HLASM context
HLASM context
Macro storage
Macro storage
Copy storage
Copy storage
Source stack
Source stack
ID storage
ID storage
uses
uses
Global variable symbol storage
Global variable...
Scope stack
Scope stack
LSP context
LSP context
Processing stack
Processing stack
uses
uses
LSP data
collector
LSP data...
implements
implements
Ordinary assembly context
Ordinary assembly context
has
has
Symbol storage
Symbol stora...
has
has
Section storage
Section stor...
Symbol dependency tables
Symbol depen...
Symbol
Symbol
Value
Value
Attributes
Attributes
Variable symbol
Variable sym...
extends
extends
Macro parameter base
Macro parame...
implements
implements
Positional parameter
Positional p...
implements
implements
Keyword parameter
Keyword para...
implements
implements
System variable
System varia...
extends
extends
SET symbol base
SET symbol b...
SETC symbol
SETC symbol
SETB symbol
SETB symbol
implements
implements
SETA symbol
SETA symbol
Operand
Operand
implements
implements
Empty Operand
Empty Operand
implements
implements
uses
uses
Model Operand
Model Operand
uses
uses
Machine Operand
Machine Oper...
implements
implements
Expression Machine operand
Expression M...
implements
implements
Address Machine operand
Address Mach...
Assembler Operand
Assembler Op...
implements
implements
Expression Assembler operand
Expression A...
uses
uses
USING Assembler operand
USING Assemb...
Complex Assembler operand
Complex Asse...
String Assembler operand
String Assem...
implements
implements
uses
uses
Data Definition Assembler operand
Data Definitio...
extends
extends
uses
uses
Conditional Assembly Operand
Conditional...
implements
implements
Variable symbol CA operand
Variable sym...
Sequnece symbol CA operand
Sequnece sym...
Branch CA operand
Branch CA op...
implements
implements
Expression CA operand
Expression C...
implements
implements
Macro Operand
Macro Operand
Evaluable Operand
Evaluable Op...
uses
uses
implements
implements
Debugger
Debugger
Processor tracer
Processor tracer
Variable
Variable
implements
implements
uses
uses
Ordinary symbol variable
Ordinary symbol...
implements
implements
Set symbol variable
Set symbol vari...
implements
implements
uses
uses
Macro parameter  variable
Macro parameter...
implements
implements
Attribute variable
Attribute varia...
uses
uses
implements
implements
implements
implements
generates
generates
uses
uses
uses
uses
Parser
Parser
uses
uses
Token stream
Token stream
uses
uses
uses
uses
uses
uses
Lexer
Lexer
Input Source
Input Source
Instruction format validation
Instruction format validation
uses
uses
uses
uses
Processing Manager
Processing M...
Statement Processors
Statement Pr...
Statement Provider
Statement Pr...
implements
implements
Opencode statement Provider
Opencode sta...
implements
implements
Copy statement Provider
Copy stateme...
implements
implements
Macro statement Provider
Macro statem...
implements
implements
uses
uses
Lookahead Processor
Lookahead Pr...
implements
implements
Macro definition Processor
Macro defini...
implements
implements
notifies
notifies
Opencode Processor
Opencode Pro...
implements
implements
Copy definition Processor
Copy definit...
Instruction Processors
Instruction...
implements
implements
uses
uses
Conditional Assembly IP
Conditional...
uses
uses
Machine instruction checker
Machine inst...
implements
implements
uses
uses
Assembler
IP
Assembler...
Assembler instruction checker
Assembler in...
implements
implements
Macro IP
Macro IP
implements
implements
uses
uses
uses
uses
Machine IP
Machine IP
holds
holds
uses
uses
uses
uses
Workspace manager
Workspace manager
holds
holds
holds
holds
File manager
File manager
File
File
extends
extends
Processor file
Processor file
uses
uses
uses
uses
implements
implements
Workspace
Workspace
uses
uses
uses
uses
uses
uses
Analyzer
Analyzer
uses
uses
Library
Library
uses
uses
implements
implements
Local library
Local library
Parse library provider
Parse library p...
uses
uses
Processor group
Processor group
Token
Token
Statement fields parser
Statement fi...
uses
uses
creates
creates
Token factory
Token factory
Statement
Statement
uses
uses
Collector
Collector
uses
uses
Machine expression
Machine expression
uses
uses
uses
uses
evaluates
evaluates
Expression evaluator
Expression eva...
CA expression
CA expression
implements
implements
CAE symbol
CAE symbol
CAE symbol attribute
CAE symbol at...
CAE function
CAE function
CAE variable symbol
CAE variable...
ME constant
ME constant
implements
implements
ME data attribute
ME data attribu...
implements
implements
ME symbol
ME symbol
implements
implements
ME
 self defining term
ME...
ME operator
ME operator
uses
uses
uses
uses
Data definition
Data definition
Nominal value
Nominal value
Data definition types
Data definit...
Concatenation
Concatenation
Section
Section
Location counter storage
Location cou...
Dependency solver
Dependency s...
reads/writes
reads/writes
reads/writes
reads/writes
uses
uses
Visual Studio Code
Visual Studio Code
Extension
Extension
Diagnosable
Diagnosable
uses
uses
CAE constant
CAE constant
implements
implements
uses
uses
CAE expression list
CAE expressio...
CAE string
CAE string
CAE policy
CAE policy
implements
implements
Stdio setup
Stdio setup
TCP setup
TCP setup
uses
uses
Server stream
Server stream
implements
imple...
Emscripten setup
Emscripten setup
JSON channel
JSON channel
implements
imple...
provides
provi...
reads/writes
reads/writes
reads/writes
reads/writes
reads/writes
reads/writes
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/docs/img/hover_sequence.svg b/docs/img/hover_sequence.svg index 1085f08eb..5abe7293d 100644 --- a/docs/img/hover_sequence.svg +++ b/docs/img/hover_sequence.svg @@ -1,3 +1,3 @@ -
Request manager
Request manager
cond_var.notify_one()
cond_var.notify_one()
LSP server
LSP server
Feature: Language features
Feature: Languag...
Workspace manager
Workspace manager
Dispatcher
Dispatcher
hover(file_name, position)
hover(file_name, position)
respond(message_id,args)
respond(message_id,args)
return
return
hover(message_id,
 json_args)
hover(message_id,...
add_request(server, json)
add_request(server, json)
read input
read input
Content-Length: 47

{"jsonrpc":"2.0",
"id":1,
"method":"textDocum
ent/hover",
"params:{...}}
Content-Length: 47...
return
return
message_received(json)
message_received(json)
return
return
{"jsonrpc":"2.0",
"id":1,
"method":"textDocum
ent/hover",
"params":{...}}
{"jsonrpc":"2.0",...
{"jsonrpc":"2.0",
"id":1,
"method":"textDocum
ent/hover",
"params":{...}}
{"jsonrpc":"2.0",...
message_id = 1,
args = {"textDocument":{"uri":"file://c:/file.asm"},
"position":{"line":24,"character":31}
message_id = 1,...
file_name = "C:\file.asm"
position = {24, 31}
file_name = "C:\file.a...
write_message(json)
write_message(json)
message_id = 1,
args = {"contents":
"hover message"}
message_id = 1,...
write output
write output
return
return
{"jsonrpc":"2.0",
"id":1,
"result":{"contents":
"hover message"}}
{"jsonrpc":"2.0",...
Content-Length: 63

{"jsonrpc":"2.0",
"id":1,
"result":{"contents":
"hover message"}}
Content-Length: 63...
return
return
Viewer does not support full SVG 1.1
\ No newline at end of file +
Request manager
Request manager
cond_var.notify_one()
cond_var.notify_one()
LSP server
LSP server
Feature: Language features
Feature: Languag...
Workspace manager
Workspace manager
Dispatcher
Dispatcher
hover(file_name, position)
hover(file_name, position)
respond(message_id,args)
respond(message_id,args)
return
return
hover(message_id,
 json_args)
hover(message_id,...
add_request(server, json)
add_request(server, json)
read input
read input
Content-Length: 47

{"jsonrpc":"2.0",
"id":1,
"method":"textDocum
ent/hover",
"params:{...}}
Content-Length: 47...
return
return
message_received(json)
message_received(json)
return
return
{"jsonrpc":"2.0",
"id":1,
"method":"textDocum
ent/hover",
"params":{...}}
{"jsonrpc":"2.0",...
{"jsonrpc":"2.0",
"id":1,
"method":"textDocum
ent/hover",
"params":{...}}
{"jsonrpc":"2.0",...
message_id = 1,
args = {"textDocument":{"uri":"file://c:/file.asm"},
"position":{"line":24,"character":31}
message_id = 1,...
file_name = "C:\file.asm"
position = {24, 31}
file_name = "C:\file.a...
reply(json)
reply(json)
message_id = 1,
args = {"contents":
"hover message"}
message_id = 1,...
write output
write output
return
return
{"jsonrpc":"2.0",
"id":1,
"result":{"contents":
"hover message"}}
{"jsonrpc":"2.0",...
Content-Length: 63

{"jsonrpc":"2.0",
"id":1,
"result":{"contents":
"hover message"}}
Content-Length: 63...
return
return
LSP channel
LSP channel
write(json)
write(json)
return
return
JSON queue channel
JSON queue chann...
{"jsonrpc":"2.0",
"id":1,
"method":"textDocum
ent/hover",
"params":{...}}
{"jsonrpc":"2.0",...
{"jsonrpc":"2.0",
"id":1,
"method":"textDocum
ent/hover",
"params":{...}}
{"jsonrpc":"2.0",...
return
return
return
return
write(json)
write(json)
read()
read()
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/docs/img/lang_server.svg b/docs/img/lang_server.svg index 769ebface..bcc08782f 100644 --- a/docs/img/lang_server.svg +++ b/docs/img/lang_server.svg @@ -1,3 +1,3 @@ -
reads/writes
reads/writes
uses
uses
Dispatcher
Dispatcher
Parser library
Parser library
uses
uses
Server
Server
runs
runs
uses
uses
Request manager
Request mana...
LSP communication
(standard IO)
LSP communicati...
DAP communication
(TCP loopback)
DAP communicati...
implements
implements
uses
uses
LSP server
LSP server
Request
Request
implements
implements
uses
uses
DAP server
DAP server
Feature
Feature
implements
implements
LSP feature
LSP feature
LSP feature
LSP feature
use
use
LSP features
LSP features
implements
implements
use
use
DAP features
DAP features
uses
uses
TCP handler
TCP handler
wraps
wraps
Viewer does not support full SVG 1.1
\ No newline at end of file +
implements
implements
uses
uses
Dispatcher
Dispatcher
Parser library
Parser library
uses
uses
Server
Server
runs
runs
uses
uses
Request manager
Request mana...
LSP communication
(standard IO)
LSP communicati...
TCP loopback
TCP loopback
implements
implements
uses
uses
LSP server
LSP server
Request
Request
implements
implements
uses
uses
DAP server
DAP server
Feature
Feature
implements
implements
LSP feature
LSP feature
LSP feature
LSP feature
use
use
LSP features
LSP features
implements
implements
use
use
DAP features
DAP features
uses
uses
Server stream
Server stream
implements
imple...
Node.js
event based IO
Node.js...
JSON channel
JSON channel
implements
imple...
provides
provi...
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/dummy/src/main.cpp b/dummy/src/main.cpp deleted file mode 100644 index 06907f5b3..000000000 --- a/dummy/src/main.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2019 Broadcom. - * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. - * - * This program and the accompanying materials are made - * available under the terms of the Eclipse Public License 2.0 - * which is available at https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - * - * Contributors: - * Broadcom, Inc. - initial API and implementation - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -int main(int argc, char* argv[]) -{ -#ifdef CONSOLE - - std::string input; - - while (std::getline(std::cin, input)) - { - auto p = new hlasm_plugin::parser_library::parser_library(); - p->parse(std::move(input)); - } - -#else - - // std::string tcase = "simple"; - // std::string tcase = "operand"; - // std::string tcase = "continuation"; - // std::string tcase = "model_statement"; - // std::string tcase = "correctness"; - // std::string tcase = "aread"; - // std::string tcase = "comment"; - std::string tcase = "test"; - std::string inp = "test/library/input/" + tcase + ".in"; - if (argc > 1) - inp = std::string(argv[1]); - std::ifstream ifs(inp); - std::string content(std::istreambuf_iterator(ifs), (std::istreambuf_iterator())); - - auto p = new hlasm_plugin::parser_library::parser_library(); - p->parse(std::move(content)); - -#endif - - return 0; -} \ No newline at end of file diff --git a/language_server/CMakeLists.txt b/language_server/CMakeLists.txt index a79d01f2b..521b986c1 100644 --- a/language_server/CMakeLists.txt +++ b/language_server/CMakeLists.txt @@ -10,75 +10,31 @@ # Contributors: # Broadcom, Inc. - initial API and implementation -cmake_minimum_required (VERSION 3.10) - PROJECT(language_server) include(GoogleTest) -set(SOURCES ${PROJECT_SOURCE_DIR}/src/logger.cpp - ${PROJECT_SOURCE_DIR}/src/feature.cpp - ${PROJECT_SOURCE_DIR}/src/server.cpp - ${PROJECT_SOURCE_DIR}/src/dispatcher.cpp - ${PROJECT_SOURCE_DIR}/src/request_manager.cpp - ${PROJECT_SOURCE_DIR}/src/message_router.cpp - ${PROJECT_SOURCE_DIR}/src/json_queue_channel.cpp - ${PROJECT_SOURCE_DIR}/src/base_protocol_channel.cpp - ${PROJECT_SOURCE_DIR}/src/lsp/lsp_server.cpp - ${PROJECT_SOURCE_DIR}/src/lsp/feature_workspace_folders.cpp - ${PROJECT_SOURCE_DIR}/src/lsp/feature_text_synchronization.cpp - ${PROJECT_SOURCE_DIR}/src/lsp/feature_language_features.cpp - ${PROJECT_SOURCE_DIR}/src/dap/dap_server.cpp - ${PROJECT_SOURCE_DIR}/src/dap/dap_feature.cpp - ${PROJECT_SOURCE_DIR}/src/dap/dap_message_wrappers.cpp - ${PROJECT_SOURCE_DIR}/src/dap/dap_session.cpp - ${PROJECT_SOURCE_DIR}/src/dap/dap_session_manager.cpp - ) - # compile sources to the executable -add_executable(language_server - ${PROJECT_SOURCE_DIR}/src/main.cpp - ${SOURCES} - ) - -if(NOT BUILD_SHARED_LIBS) - set_target_properties(language_server PROPERTIES COMPILE_FLAGS "-DPARSER_LIBRARY_STATIC_DEFINE") -endif() - -# link executable with libraries +add_library(language_server_base OBJECT) -add_dependencies(language_server json) -add_dependencies(language_server uri_ext) -add_dependencies(language_server boost_ext) +target_include_directories(language_server_base PUBLIC src) -target_link_libraries(language_server network-uri) -target_link_libraries(language_server parser_library) -if(UNIX) - target_link_libraries(language_server pthread) -endif() -if(BUILD_TESTING) - file(GLOB_RECURSE SERVER_TEST_SRC - "${PROJECT_SOURCE_DIR}/test/*.cpp" - ) +add_executable(language_server) - add_executable(server_test - ${SERVER_TEST_SRC} - ${SOURCES} - ) - add_dependencies(server_test json) - add_dependencies(server_test uri_ext) - add_dependencies(server_test boost_ext) +add_subdirectory(src) - target_link_libraries(server_test gmock_main) - target_link_libraries(server_test network-uri) - target_link_libraries(server_test parser_library) +# link executable with libraries +target_link_libraries(language_server_base nlohmann_json::nlohmann_json) +target_link_libraries(language_server_base network-uri) +target_link_libraries(language_server_base parser_library) +target_link_libraries(language_server_base boost-asio) - target_include_directories(server_test - PUBLIC - ${PROJECT_SOURCE_DIR}/src - ) +target_link_libraries(language_server language_server_base) +target_link_libraries(language_server nlohmann_json::nlohmann_json) +target_link_libraries(language_server boost-asio) +target_link_libraries(language_server Threads::Threads) +target_link_libraries(language_server hlasm_utils) - if(DISCOVER_TESTS) - gtest_discover_tests(server_test WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/bin DISCOVERY_TIMEOUT 30) - endif() +if(BUILD_TESTING) + add_subdirectory(test) endif() diff --git a/language_server/src/CMakeLists.txt b/language_server/src/CMakeLists.txt new file mode 100644 index 000000000..4483f8921 --- /dev/null +++ b/language_server/src/CMakeLists.txt @@ -0,0 +1,49 @@ +# Copyright (c) 2021 Broadcom. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Broadcom, Inc. - initial API and implementation + +target_sources(language_server_base PRIVATE + base_protocol_channel.cpp + base_protocol_channel.h + blocking_queue.h + common_types.h + dispatcher.cpp + dispatcher.h + feature.cpp + feature.h + json_channel.h + json_queue_channel.cpp + json_queue_channel.h + logger.cpp + logger.h + message_router.cpp + message_router.h + request_manager.cpp + request_manager.h + scope_exit.h + server.cpp + server.h + server_streams.h + stream_helper.h +) + +if(EMSCRIPTEN) + target_sources(language_server_base PRIVATE emscripten_server_streams.cpp) +else() + target_sources(language_server_base PRIVATE native_server_streams.cpp) +endif() + +target_sources(language_server PRIVATE + main.cpp +) + +add_subdirectory(dap) +add_subdirectory(lsp) diff --git a/language_server/src/base_protocol_channel.cpp b/language_server/src/base_protocol_channel.cpp index c7ded01ad..45f0c2ba6 100644 --- a/language_server/src/base_protocol_channel.cpp +++ b/language_server/src/base_protocol_channel.cpp @@ -15,14 +15,11 @@ #include "base_protocol_channel.h" #include -#include #include #include #include #include -#include "json.hpp" - #include "logger.h" namespace hlasm_plugin::language_server { diff --git a/language_server/src/base_protocol_channel.h b/language_server/src/base_protocol_channel.h index da288e351..9a82cf53d 100644 --- a/language_server/src/base_protocol_channel.h +++ b/language_server/src/base_protocol_channel.h @@ -15,8 +15,9 @@ #ifndef HLASMPLUGIN_HLASMLANGUAGESERVER_BASE_PROTOCOL_CHANNEL_H #define HLASMPLUGIN_HLASMLANGUAGESERVER_BASE_PROTOCOL_CHANNEL_H -#include +#include #include +#include #include "json_channel.h" diff --git a/language_server/src/blocking_queue.h b/language_server/src/blocking_queue.h index 18fd38f12..1a5e0a961 100644 --- a/language_server/src/blocking_queue.h +++ b/language_server/src/blocking_queue.h @@ -22,13 +22,21 @@ #include namespace hlasm_plugin::language_server { -template + +enum class blocking_queue_termination_policy : bool +{ + drop_elements, + process_elements, +}; + +template class blocking_queue { std::mutex mutex; std::condition_variable cond_var; std::deque queue; - std::atomic terminated = false; + bool terminated = false; public: void push(T&& t) @@ -62,10 +70,15 @@ class blocking_queue std::optional pop() { + constexpr const auto drop = blocking_queue_termination_policy::drop_elements; + constexpr const auto process = blocking_queue_termination_policy::process_elements; + std::unique_lock g(mutex); cond_var.wait(g, [this] { return queue.size() || terminated; }); - if (terminated) + + if ((termination_policy == drop && terminated) || (termination_policy == process && queue.size() == 0)) return std::nullopt; + std::optional result = std::move(queue.front()); queue.pop_front(); @@ -74,7 +87,10 @@ class blocking_queue void terminate() { + std::unique_lock g(mutex); terminated = true; + g.unlock(); + cond_var.notify_one(); } }; diff --git a/language_server/src/common_types.h b/language_server/src/common_types.h index 015f09f0c..c461b3d86 100644 --- a/language_server/src/common_types.h +++ b/language_server/src/common_types.h @@ -12,7 +12,7 @@ * Broadcom, Inc. - initial API and implementation */ -#include "json.hpp" +#include "nlohmann/json.hpp" // Types that are used throughout the language server component namespace hlasm_plugin::language_server { diff --git a/language_server/src/dap/CMakeLists.txt b/language_server/src/dap/CMakeLists.txt new file mode 100644 index 000000000..1c5c9a962 --- /dev/null +++ b/language_server/src/dap/CMakeLists.txt @@ -0,0 +1,24 @@ +# Copyright (c) 2021 Broadcom. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Broadcom, Inc. - initial API and implementation + +target_sources(language_server_base PRIVATE + dap_feature.cpp + dap_feature.h + dap_message_wrappers.cpp + dap_message_wrappers.h + dap_server.cpp + dap_server.h + dap_session.cpp + dap_session.h + dap_session_manager.cpp + dap_session_manager.h +) \ No newline at end of file diff --git a/language_server/src/dap/dap_feature.cpp b/language_server/src/dap/dap_feature.cpp index b79af8ab9..e8216e4d3 100644 --- a/language_server/src/dap/dap_feature.cpp +++ b/language_server/src/dap/dap_feature.cpp @@ -14,7 +14,8 @@ #include "dap_feature.h" -#include +#include "utils/path.h" +#include "utils/platform.h" namespace { using namespace hlasm_plugin::language_server::dap; @@ -24,19 +25,19 @@ std::string convert_path(const std::string& path, path_format path_format) if (path_format == path_format::URI) return hlasm_plugin::language_server::feature::uri_to_path(path); - std::filesystem::path p(path); // Theia sends us relative path (while not accepting it back) change, to absolute - if (p.is_relative()) - p = std::filesystem::absolute(p); - std::string result = p.lexically_normal().string(); + std::filesystem::path p = hlasm_plugin::utils::path::absolute(path); + + std::string result = hlasm_plugin::utils::path::lexically_normal(p).string(); // on windows, VS code sends us path with capital drive letter through DAP and // lowercase drive letter through LSP. // Remove, once we implement case-insensitive comparison of paths in parser_library for windows -#ifdef _WIN32 - if (result[1] == ':') - result[0] = (char)tolower(result[0]); -#endif // _WIN32 + if (hlasm_plugin::utils::platform::is_windows()) + { + if (result[1] == ':') + result[0] = (char)tolower(result[0]); + } return result; } diff --git a/language_server/src/dap/dap_server.h b/language_server/src/dap/dap_server.h index fa5736650..8146ace9b 100644 --- a/language_server/src/dap/dap_server.h +++ b/language_server/src/dap/dap_server.h @@ -19,11 +19,10 @@ #include #include -#include "json.hpp" - #include "../common_types.h" #include "../server.h" #include "dap_feature.h" +#include "nlohmann/json.hpp" namespace hlasm_plugin::language_server::dap { diff --git a/language_server/src/dap/dap_session.cpp b/language_server/src/dap/dap_session.cpp index 431597b2c..73a988520 100644 --- a/language_server/src/dap/dap_session.cpp +++ b/language_server/src/dap/dap_session.cpp @@ -14,23 +14,33 @@ #include "dap_session.h" -#include "json.hpp" - #include "../dispatcher.h" #include "../request_manager.h" #include "../scope_exit.h" #include "dap_server.h" +#include "logger.h" namespace hlasm_plugin::language_server::dap { void session::thread_routine() { - std::atomic cancel = false; - scope_exit indicate_end([this]() { running = false; }); - request_manager req_mgr(&cancel); - scope_exit end_request_manager([&req_mgr]() { req_mgr.end_worker(); }); - dap::server server(*ws_mngr); - dispatcher dispatcher(json_channel_adapter(msg_unwrapper, msg_wrapper), server, req_mgr); - dispatcher.run_server_loop(); + try + { + std::atomic cancel = false; + scope_exit indicate_end([this]() { running = false; }); + request_manager req_mgr(&cancel); + scope_exit end_request_manager([&req_mgr]() { req_mgr.end_worker(); }); + dap::server server(*ws_mngr); + dispatcher dispatcher(json_channel_adapter(msg_unwrapper, msg_wrapper), server, req_mgr); + dispatcher.run_server_loop(); + } + catch (const std::exception& ex) + { + LOG_ERROR(std::string("DAP Thread exception encountered: ") + ex.what()); + } + catch (...) + { + LOG_ERROR("DAP Thread encountered an unknown exception."); + } } session::session(size_t s_id, hlasm_plugin::parser_library::workspace_manager& ws, json_sink& out) : session_id(message_wrapper::generate_method_name(s_id)) diff --git a/language_server/src/dispatcher.cpp b/language_server/src/dispatcher.cpp index ca77dc298..81a066563 100644 --- a/language_server/src/dispatcher.cpp +++ b/language_server/src/dispatcher.cpp @@ -14,9 +14,8 @@ #include "dispatcher.h" -#include "json.hpp" - #include "logger.h" +#include "nlohmann/json.hpp" namespace hlasm_plugin::language_server { diff --git a/language_server/src/emscripten_server_streams.cpp b/language_server/src/emscripten_server_streams.cpp new file mode 100644 index 000000000..64627bf7c --- /dev/null +++ b/language_server/src/emscripten_server_streams.cpp @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2021 Broadcom. + * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Broadcom, Inc. - initial API and implementation + */ +#include +#include +#include + +#include + +#include "blocking_queue.h" +#include "logger.h" +#include "server_streams.h" + +namespace hlasm_plugin::language_server { +namespace { + +class emscripten_std_setup : public server_streams, public json_source, public json_sink +{ + blocking_queue queue; + + std::string stdin_buffer; + + intptr_t get_ptr_token() const { return reinterpret_cast(this); } + +public: + static emscripten::val get_stdin_buffer(intptr_t ptr_val, ssize_t len) + { + auto* ptr = reinterpret_cast(ptr_val); + ptr->stdin_buffer.resize(len); + return emscripten::val(emscripten::typed_memory_view(len, (unsigned char*)ptr->stdin_buffer.data())); + } + + static void commit_stdin_buffer(intptr_t ptr_val) + { + auto* ptr = reinterpret_cast(ptr_val); + + ptr->queue.push(std::move(ptr->stdin_buffer)); + } + + static void terminate_input(intptr_t ptr_val) + { + auto* ptr = reinterpret_cast(ptr_val); + ptr->queue.terminate(); + } + + json_sink& get_response_stream() & override { return *this; } + + json_source& get_request_stream() & override { return *this; } + + void write(const nlohmann::json& msg) override + { + auto msg_string = msg.dump(); + auto msg_string_size = msg_string.size(); + auto msg_to_send = "Content-Length: " + std::to_string(msg_string_size) + "\r\n\r\n" + std::move(msg_string); + + MAIN_THREAD_EM_ASM( + { process.stdout.write(HEAPU8.slice($0, $0 + $1)); }, msg_to_send.data(), msg_to_send.size()); + } + void write(nlohmann::json&& msg) override { write(msg); } + + std::optional read() override + { + while (true) + { + auto msg = queue.pop(); + if (!msg.has_value()) + return std::nullopt; + + try + { + return nlohmann::json::parse(msg.value()); + } + catch (const nlohmann::json::exception&) + { + LOG_WARNING("Could not parse received JSON: " + msg.value()); + } + } + } + + emscripten_std_setup() + { + MAIN_THREAD_EM_ASM( + { + const content_length = 'Content-Length: '; + var buffer = Buffer.from([]); + + const ptr = $0; + Module["emscripten_std_setup_term"] = Module["emscripten_std_setup_term"] || new Map(); + + function end_event_handler() { Module.terminate_input(ptr); }; + function data_event_handler(data) + { + buffer = Buffer.concat([ buffer, data ]); + while (true) + { + if (buffer.indexOf(content_length) != 0) + return; + const end_of_line = buffer.indexOf('\\x0D\\x0A'); + if (end_of_line < 0) + return; + const length = +buffer.slice(content_length.length, end_of_line); + const end_of_headers = buffer.indexOf('\\x0D\\x0A\\x0D\\x0A'); + if (end_of_headers < 0) + return; + const data_start = end_of_headers + 4; + const data_end = data_start + length; + if (data_end > buffer.length) + return; + + const data_to_pass = buffer.slice(data_start, data_end); + buffer = buffer.slice(data_end); + + const store_buffer = Module.get_stdin_buffer(ptr, data_to_pass.length); + data_to_pass.copy(store_buffer); + Module.commit_stdin_buffer(ptr); + } + }; + process.stdin.on('data', data_event_handler); + process.stdin.on('end', end_event_handler); + + Module["emscripten_std_setup_term"][ptr] = function() + { + process.stdin.removeListener('data', data_event_handler); + process.stdin.removeListener('end', end_event_handler); + }; + }, + get_ptr_token()); + } + + ~emscripten_std_setup() + { + MAIN_THREAD_EM_ASM( + { + const ptr = $0; + Module["emscripten_std_setup_term"][ptr](); + delete Module["emscripten_std_setup_term"][ptr]; + }, + get_ptr_token()); + } +}; + +EMSCRIPTEN_BINDINGS(main_thread) +{ + emscripten::function("get_stdin_buffer", &emscripten_std_setup::get_stdin_buffer); + emscripten::function("commit_stdin_buffer", &emscripten_std_setup::commit_stdin_buffer); + emscripten::function("terminate_input", &emscripten_std_setup::terminate_input); +} + +} // namespace + +std::unique_ptr server_streams::create(int argc, char** argv) +{ + (void)argv; + if (argc != 1) + { + std::cerr << "No arguments allowed"; + return {}; + } + + return std::make_unique(); +} + +} // namespace hlasm_plugin::language_server diff --git a/language_server/src/feature.cpp b/language_server/src/feature.cpp index 296a1579a..ca4f2f313 100644 --- a/language_server/src/feature.cpp +++ b/language_server/src/feature.cpp @@ -19,6 +19,8 @@ #include "network/uri/uri.hpp" #include "logger.h" +#include "utils/path.h" +#include "utils/platform.h" namespace hlasm_plugin::language_server { @@ -45,24 +47,28 @@ std::string feature::uri_to_path(const std::string& uri) if (u.has_authority() && u.authority().to_string() != "") { auth_path = u.authority().to_string() + u.path().to_string(); -#ifdef _WIN32 // handle remote locations correctly, like \\server\path - auth_path = "//" + auth_path; -#endif + if (utils::platform::is_windows()) + { + // handle remote locations correctly, like \\server\path + auth_path = "//" + auth_path; + } } else { -#ifdef _WIN32 // we get path always beginning with / on windows, e.g. /c:/Users/path - path.remove_prefix(1); -#endif + if (utils::platform::is_windows()) + { + // we get path always beginning with / on windows, e.g. /c:/Users/path + path.remove_prefix(1); + } auth_path = path.to_string(); -#ifdef _WIN32 - auth_path[0] = (char)tolower(auth_path[0]); -#endif + if (utils::platform::is_windows()) + { + auth_path[0] = (char)tolower(auth_path[0]); + } } - std::filesystem::path p(network::detail::decode(auth_path)); - return p.lexically_normal().string(); + return utils::path::lexically_normal(network::detail::decode(auth_path)).string(); } std::string feature::path_to_uri(std::string_view path) @@ -81,15 +87,18 @@ std::string feature::path_to_uri(std::string_view path) network::detail::encode_char(c, out, "/.%;="); } -#ifdef _WIN32 - // in case of remote address such as \\server\path\to\file - if (uri.size() >= 2 && uri[0] == '/' && uri[1] == '/') - uri.insert(0, "file:"); + if (utils::platform::is_windows()) + { + // in case of remote address such as \\server\path\to\file + if (uri.size() >= 2 && uri[0] == '/' && uri[1] == '/') + uri.insert(0, "file:"); + else + uri.insert(0, "file:///"); + } else - uri.insert(0, "file:///"); -#else - uri.insert(0, "file://"); -#endif // _WIN32 + { + uri.insert(0, "file://"); + } return uri; } diff --git a/language_server/src/feature.h b/language_server/src/feature.h index f0c9d0e56..d987df675 100644 --- a/language_server/src/feature.h +++ b/language_server/src/feature.h @@ -18,9 +18,8 @@ #include #include -#include "json.hpp" - #include "common_types.h" +#include "nlohmann/json.hpp" #include "workspace_manager.h" namespace hlasm_plugin::language_server { diff --git a/language_server/src/json_channel.h b/language_server/src/json_channel.h index fc8ab81b1..d79c3d396 100644 --- a/language_server/src/json_channel.h +++ b/language_server/src/json_channel.h @@ -17,7 +17,7 @@ #include -#include "json.hpp" +#include "nlohmann/json.hpp" namespace hlasm_plugin::language_server { diff --git a/language_server/src/json_queue_channel.h b/language_server/src/json_queue_channel.h index 7c5c92911..4d42cdd4e 100644 --- a/language_server/src/json_queue_channel.h +++ b/language_server/src/json_queue_channel.h @@ -15,7 +15,6 @@ #ifndef HLASMPLUGIN_HLASMLANGUAGESERVER_JSON_QUEUE_CHANNEL_H #define HLASMPLUGIN_HLASMLANGUAGESERVER_JSON_QUEUE_CHANNEL_H -#include "json.hpp" #include "json_channel.h" #include "blocking_queue.h" diff --git a/language_server/src/logger.cpp b/language_server/src/logger.cpp index 980abcee9..4c491350c 100644 --- a/language_server/src/logger.cpp +++ b/language_server/src/logger.cpp @@ -14,7 +14,6 @@ #include -#include #define __STDC_WANT_LIB_EXT1__ 1 #include #include @@ -29,17 +28,31 @@ constexpr const char* log_filename = "hlasmplugin.log"; logger::logger() { - auto log_folder = std::filesystem::temp_directory_path(); - auto log_path = log_folder / log_filename; - file_.open(log_path, ios::out); + std::error_code err {}; + auto log_folder = std::filesystem::temp_directory_path(err); + if (!err) + { + auto log_path = log_folder / log_filename; + file_.open(log_path, ios::out); + } } logger::~logger() { file_.close(); } -void logger::log(const std::string& data) { file_ << current_time() << " " << data << endl; } +void logger::log(const std::string& data) +{ + std::lock_guard g(mutex_); + if (file_.is_open()) + file_ << current_time() << " " << data << endl; +} -void logger::log(const char* data) { file_ << current_time() << " " << data << endl; } +void logger::log(const char* data) +{ + std::lock_guard g(mutex_); + if (file_.is_open()) + file_ << current_time() << " " << data << endl; +} string logger::current_time() { diff --git a/language_server/src/logger.h b/language_server/src/logger.h index 7e21b193d..4376b5c6d 100644 --- a/language_server/src/logger.h +++ b/language_server/src/logger.h @@ -16,6 +16,7 @@ #define HLASMPLUGIN_HLASMLANGUAGESERVER_LOGGER_H #include +#include #include namespace hlasm_plugin::language_server { @@ -62,6 +63,7 @@ class logger // File to write the log into. std::ofstream file_; + std::mutex mutex_; }; } // namespace hlasm_plugin::language_server diff --git a/language_server/src/lsp/CMakeLists.txt b/language_server/src/lsp/CMakeLists.txt new file mode 100644 index 000000000..f898419aa --- /dev/null +++ b/language_server/src/lsp/CMakeLists.txt @@ -0,0 +1,22 @@ +# Copyright (c) 2021 Broadcom. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Broadcom, Inc. - initial API and implementation + +target_sources(language_server_base PRIVATE + feature_language_features.cpp + feature_language_features.h + feature_text_synchronization.cpp + feature_text_synchronization.h + feature_workspace_folders.cpp + feature_workspace_folders.h + lsp_server.cpp + lsp_server.h +) \ No newline at end of file diff --git a/language_server/src/lsp/feature_language_features.cpp b/language_server/src/lsp/feature_language_features.cpp index 4035628e7..756b92186 100644 --- a/language_server/src/lsp/feature_language_features.cpp +++ b/language_server/src/lsp/feature_language_features.cpp @@ -15,8 +15,6 @@ #include "feature_language_features.h" -#include - #include "../feature.h" namespace hlasm_plugin::language_server::lsp { diff --git a/language_server/src/lsp/feature_text_synchronization.cpp b/language_server/src/lsp/feature_text_synchronization.cpp index bfaa06a7b..2ac129aa4 100644 --- a/language_server/src/lsp/feature_text_synchronization.cpp +++ b/language_server/src/lsp/feature_text_synchronization.cpp @@ -12,7 +12,6 @@ * Broadcom, Inc. - initial API and implementation */ - #include "feature_text_synchronization.h" #include "../logger.h" diff --git a/language_server/src/lsp/feature_workspace_folders.cpp b/language_server/src/lsp/feature_workspace_folders.cpp index e79f338ac..e3067e3b9 100644 --- a/language_server/src/lsp/feature_workspace_folders.cpp +++ b/language_server/src/lsp/feature_workspace_folders.cpp @@ -20,7 +20,8 @@ #include "../logger.h" #include "lib_config.h" - +#include "utils/path.h" +#include "utils/platform.h" namespace hlasm_plugin::language_server::lsp { @@ -83,8 +84,8 @@ void feature_workspace_folders::initialize_feature(const json& initialize_params auto root_path = initialize_params.find("rootPath"); if (root_path != initialize_params.end() && !root_path->is_null()) { - std::filesystem::path path(root_path->get()); - add_workspace(path.lexically_normal().string(), path.lexically_normal().string()); + auto path = utils::path::lexically_normal(root_path->get()).string(); + add_workspace(path, path); } } diff --git a/language_server/src/lsp/lsp_server.h b/language_server/src/lsp/lsp_server.h index 5f0747481..9fb0c2d4f 100644 --- a/language_server/src/lsp/lsp_server.h +++ b/language_server/src/lsp/lsp_server.h @@ -19,11 +19,10 @@ #include #include -#include "json.hpp" - #include "../common_types.h" #include "../feature.h" #include "../server.h" +#include "nlohmann/json.hpp" #include "workspace_manager.h" namespace hlasm_plugin::language_server::lsp { diff --git a/language_server/src/main.cpp b/language_server/src/main.cpp index ed51226df..ae98ac02c 100644 --- a/language_server/src/main.cpp +++ b/language_server/src/main.cpp @@ -14,15 +14,7 @@ #include #include -#include -#define ASIO_STANDALONE -#include "asio.hpp" -#include "asio/stream_socket_service.hpp" -#include "asio/system_error.hpp" -#include "json_queue_channel.h" - -#include "base_protocol_channel.h" #include "dap/dap_message_wrappers.h" #include "dap/dap_server.h" #include "dap/dap_session.h" @@ -32,121 +24,101 @@ #include "lsp/lsp_server.h" #include "message_router.h" #include "scope_exit.h" -#include "stream_helper.h" +#include "server_streams.h" #include "workspace_manager.h" -#ifdef _WIN32 // set binary mode for input on windows -# include -# include -# define SET_BINARY_MODE(handle) _setmode(_fileno(handle), O_BINARY) -#else -# define SET_BINARY_MODE(handle) -#endif -// no need for binary on linux, because it does not change \n into \r\n - using namespace hlasm_plugin::language_server; namespace { -struct tcp_setup + +class main_program : public json_sink { - asio::io_service io_service; - asio::ip::tcp::acceptor acceptor; - asio::ip::tcp::socket socket; - asio::ip::tcp::iostream stream; - explicit tcp_setup(uint16_t port) - : acceptor(io_service, asio::ip::tcp::endpoint(asio::ip::address::from_string("127.0.0.1"), port)) - , socket(io_service) - { - acceptor.accept(stream.socket()); + std::atomic cancel = false; + hlasm_plugin::parser_library::workspace_manager ws_mngr; - newline_is_space::imbue_stream(stream); - } - std::pair get_streams() + json_queue_channel lsp_queue; + + message_router router; + + std::thread lsp_thread; + + dap::session_manager dap_sessions; + +public: + main_program(json_sink& json_output, int& ret) + : ws_mngr(&cancel) + , router(&lsp_queue) + , dap_sessions(ws_mngr, json_output) { - return std::pair(stream, stream); + router.register_route(dap_sessions.get_filtering_predicate(), dap_sessions); + + lsp_thread = std::thread([&ret, this, io = json_channel_adapter(lsp_queue, json_output)]() { + try + { + request_manager req_mgr(&cancel); + scope_exit end_request_manager([&req_mgr]() { req_mgr.end_worker(); }); + lsp::server server(ws_mngr); + + dispatcher lsp_dispatcher(io, server, req_mgr); + ret = lsp_dispatcher.run_server_loop(); + } + catch (const std::exception& e) + { + LOG_ERROR(std::string("LSP thread exception: ") + e.what()); + } + catch (...) + { + LOG_ERROR("LSP thread unknown exception."); + } + }); } - ~tcp_setup() { stream.close(); } -}; -struct std_setup -{ - std::pair get_streams() + ~main_program() { - return std::pair(std::cin, std::cout); + cancel = true; + lsp_queue.terminate(); + if (lsp_thread.joinable()) + lsp_thread.join(); } + main_program(const main_program&) = delete; + main_program(main_program&&) = delete; + + // Inherited via json_sink + void write(const nlohmann::json& msg) override { router.write(msg); } + void write(nlohmann::json&& msg) override { router.write(std::move(msg)); } }; + } // namespace int main(int argc, char** argv) { - using namespace std; using namespace hlasm_plugin::language_server; - if (argc > 2) - { - std::cout << "Invalid arguments. Use language_server []"; + auto io_setup = server_streams::create(argc, argv); + if (!io_setup) return 1; - } - const bool use_tcp = argc == 2; - int lsp_port = 0; - if (use_tcp) - { - lsp_port = atoi(argv[1]); - if (lsp_port <= 0 || lsp_port > 65535) - { - std::cout << "Wrong port entered."; - return 1; - } - } - - std::atomic cancel = false; try { - SET_BINARY_MODE(stdin); - SET_BINARY_MODE(stdout); - newline_is_space::imbue_stream(cin); + int ret = 0; - hlasm_plugin::parser_library::workspace_manager ws_mngr(&cancel); + main_program pgm(io_setup->get_response_stream(), ret); - json_queue_channel lsp_queue; - - message_router router(&lsp_queue); - - std::variant io_setup; - if (use_tcp) - io_setup.emplace((uint16_t)lsp_port); - - auto [in_stream, out_stream] = std::visit([](auto& p) { return p.get_streams(); }, io_setup); - base_protocol_channel channel(in_stream, out_stream); - - std::thread lsp_thread; - scope_exit clean_up_threads([&]() { - lsp_queue.terminate(); - if (lsp_thread.joinable()) - lsp_thread.join(); - }); - - dap::session_manager dap_sessions(ws_mngr, channel); - router.register_route(dap_sessions.get_filtering_predicate(), dap_sessions); - - int ret; - lsp_thread = std::thread([&ret, &ws_mngr, &cancel, io = json_channel_adapter(lsp_queue, channel)]() { - request_manager req_mgr(&cancel); - scope_exit end_request_manager([&req_mgr]() { req_mgr.end_worker(); }); - lsp::server server(ws_mngr); - - dispatcher lsp_dispatcher(io, server, req_mgr); - ret = lsp_dispatcher.run_server_loop(); - }); - - for (;;) + for (auto& source = io_setup->get_request_stream();;) { - auto msg = channel.read(); + auto msg = source.read(); + if (!msg.has_value()) break; - router.write(std::move(msg).value()); - } + try + { + pgm.write(msg.value()); + } + catch (const nlohmann::json::exception&) + { + LOG_WARNING("Could not parse received JSON: " + msg.value()); + } + } return ret; } diff --git a/language_server/src/message_router.h b/language_server/src/message_router.h index e60985c05..aa453ec4f 100644 --- a/language_server/src/message_router.h +++ b/language_server/src/message_router.h @@ -19,7 +19,6 @@ #include #include -#include "json.hpp" #include "json_channel.h" namespace hlasm_plugin::language_server { diff --git a/language_server/src/native_server_streams.cpp b/language_server/src/native_server_streams.cpp new file mode 100644 index 000000000..863a19197 --- /dev/null +++ b/language_server/src/native_server_streams.cpp @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2021 Broadcom. + * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Broadcom, Inc. - initial API and implementation + */ +#include + +#include "base_protocol_channel.h" +#include "server_streams.h" +#include "stream_helper.h" + +#define ASIO_STANDALONE +#include "asio.hpp" +#include "asio/stream_socket_service.hpp" +#include "asio/system_error.hpp" + +#ifdef _WIN32 // set binary mode for input on windows +# include +# include +# define SET_BINARY_MODE(handle) _setmode(_fileno(handle), O_BINARY) +#else +# define SET_BINARY_MODE(handle) +#endif +// no need for binary on linux, because it does not change \n into \r\n + +namespace hlasm_plugin::language_server { +namespace { + +class stdio_setup final : public server_streams +{ + base_protocol_channel channel; + +public: + stdio_setup() + : channel(std::cin, std::cout) + { + SET_BINARY_MODE(stdin); + SET_BINARY_MODE(stdout); + newline_is_space::imbue_stream(std::cin); + } + + json_sink& get_response_stream() & override { return channel; } + + json_source& get_request_stream() & override { return channel; } +}; + +class tcp_setup final : public server_streams +{ + asio::io_service io_service; + asio::ip::tcp::acceptor acceptor; + asio::ip::tcp::socket socket; + asio::ip::tcp::iostream stream; + + base_protocol_channel channel; + +public: + explicit tcp_setup(uint16_t port) + : acceptor(io_service, asio::ip::tcp::endpoint(asio::ip::address::from_string("127.0.0.1"), port)) + , socket(io_service) + , channel(stream, stream) + { + acceptor.accept(stream.socket()); + + newline_is_space::imbue_stream(stream); + } + + ~tcp_setup() { stream.close(); } + + json_sink& get_response_stream() & override { return channel; } + + json_source& get_request_stream() & override { return channel; } +}; + +} // namespace + +std::unique_ptr server_streams::create(int argc, char** argv) +{ + if (argc > 2) + { + std::cerr << "Invalid arguments. Use language_server []"; + return {}; + } + const bool use_tcp = argc == 2; + int lsp_port = 0; + if (use_tcp) + { + lsp_port = atoi(argv[1]); + if (lsp_port <= 0 || lsp_port > 65535) + { + std::cerr << "Wrong port entered."; + return {}; + } + return std::make_unique((uint16_t)lsp_port); + } + else + return std::make_unique(); +} + +} // namespace hlasm_plugin::language_server diff --git a/language_server/src/server_streams.h b/language_server/src/server_streams.h new file mode 100644 index 000000000..9d2b06ef5 --- /dev/null +++ b/language_server/src/server_streams.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2021 Broadcom. + * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Broadcom, Inc. - initial API and implementation + */ + +#ifndef HLASMPLUGIN_HLASMLANGUAGESERVER_SERVER_STREAMS_H +#define HLASMPLUGIN_HLASMLANGUAGESERVER_SERVER_STREAMS_H 1 + +#include + +#include "json_channel.h" + +namespace hlasm_plugin::language_server { +class server_streams +{ +public: + virtual ~server_streams() = default; + virtual json_sink& get_response_stream() & = 0; + virtual json_source& get_request_stream() & = 0; + + static std::unique_ptr create(int argc, char** argv); +}; +} // namespace hlasm_plugin::language_server + +#endif // !HLASMPLUGIN_HLASMLANGUAGESERVER_SERVER_STREAMS_H diff --git a/language_server/test/CMakeLists.txt b/language_server/test/CMakeLists.txt new file mode 100644 index 000000000..b8f47f9aa --- /dev/null +++ b/language_server/test/CMakeLists.txt @@ -0,0 +1,42 @@ +# Copyright (c) 2021 Broadcom. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Broadcom, Inc. - initial API and implementation + +add_executable(server_test) + +target_sources(server_test PRIVATE + blocking_queue_test.cpp + channel_test.cpp + dispatcher_test.cpp + message_router_test.cpp + regress_test.cpp + request_manager_test.cpp + response_provider_mock.h + send_message_provider_mock.h + stream_helper_test.cpp + ws_mngr_mock.h +) + +add_subdirectory(dap) +add_subdirectory(lsp) + +target_link_libraries(server_test nlohmann_json::nlohmann_json) +target_link_libraries(server_test network-uri) +target_link_libraries(server_test boost-asio) +target_link_libraries(server_test language_server_base) + +target_link_libraries(server_test gmock_main) +target_link_libraries(server_test network-uri) +target_link_libraries(server_test parser_library) + +if(DISCOVER_TESTS) + gtest_discover_tests(server_test WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/bin DISCOVERY_TIMEOUT 120) +endif() diff --git a/language_server/test/channel_test.cpp b/language_server/test/channel_test.cpp index caf275125..d421ac43c 100644 --- a/language_server/test/channel_test.cpp +++ b/language_server/test/channel_test.cpp @@ -16,7 +16,6 @@ #include #include "gmock/gmock.h" -#include "json.hpp" #include "json_queue_channel.h" #include "base_protocol_channel.h" diff --git a/parser_library/tag_generated_files.cmake b/language_server/test/dap/CMakeLists.txt similarity index 61% rename from parser_library/tag_generated_files.cmake rename to language_server/test/dap/CMakeLists.txt index 8be7657ad..e53cf020b 100644 --- a/parser_library/tag_generated_files.cmake +++ b/language_server/test/dap/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2019 Broadcom. +# Copyright (c) 2021 Broadcom. # The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. # # This program and the accompanying materials are made @@ -10,10 +10,8 @@ # Contributors: # Broadcom, Inc. - initial API and implementation -foreach( src_file ${GENERATED_SRC} ) - set_source_files_properties( - ${src_file} - PROPERTIES - GENERATED TRUE - ) -endforeach( src_file ${GENERATED_SRC} ) +target_sources(server_test PRIVATE + dap_feature_test.cpp + dap_server_test.cpp + dap_sessions_test.cpp +) \ No newline at end of file diff --git a/language_server/test/dap/dap_feature_test.cpp b/language_server/test/dap/dap_feature_test.cpp index dd3c977ba..e0e5a7355 100644 --- a/language_server/test/dap/dap_feature_test.cpp +++ b/language_server/test/dap/dap_feature_test.cpp @@ -19,13 +19,12 @@ #include #include "gmock/gmock.h" -#include "json.hpp" #include "dap/dap_server.h" #include "feature.h" +#include "utils/path.h" #include "workspace_manager.h" - using namespace hlasm_plugin; using namespace hlasm_plugin::language_server; using namespace hlasm_plugin::language_server::dap; @@ -123,7 +122,7 @@ struct feature_launch_test : public testing::Test "0"_json, R"({"linesStartAt1":false, "columnsStartAt1":false, "pathFormat":"path"})"_json); resp_provider.reset(); - file_name = std::filesystem::absolute("to_trace").string(); + file_name = hlasm_plugin::utils::path::absolute("to_trace").string(); file_name[0] = (char)std::tolower((char)file_name[0]); } diff --git a/language_server/test/dap/dap_server_test.cpp b/language_server/test/dap/dap_server_test.cpp index 03a26c78c..b707ea833 100644 --- a/language_server/test/dap/dap_server_test.cpp +++ b/language_server/test/dap/dap_server_test.cpp @@ -21,6 +21,8 @@ #include "gmock/gmock.h" #include "dap/dap_server.h" +#include "utils/path.h" +#include "utils/platform.h" #include "workspace_manager.h" using namespace hlasm_plugin; @@ -36,7 +38,7 @@ struct send_message_provider_mock : public send_message_provider TEST(dap_server, dap_server) { - std::string file_name = std::filesystem::absolute("to_trace").string(); + std::string file_name = utils::path::absolute("to_trace").string(); file_name[0] = (char)std::tolower((char)file_name[0]); std::string file_text = " LR 1,1"; diff --git a/language_server/test/lsp/CMakeLists.txt b/language_server/test/lsp/CMakeLists.txt new file mode 100644 index 000000000..750ac3fe4 --- /dev/null +++ b/language_server/test/lsp/CMakeLists.txt @@ -0,0 +1,18 @@ +# Copyright (c) 2021 Broadcom. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Broadcom, Inc. - initial API and implementation + +target_sources(server_test PRIVATE + feature_language_features_test.cpp + feature_text_synchronization_test.cpp + lsp_server_test.cpp + workspace_folders_test.cpp +) \ No newline at end of file diff --git a/language_server/test/lsp/feature_language_features_test.cpp b/language_server/test/lsp/feature_language_features_test.cpp index 877084b7d..d74984be3 100644 --- a/language_server/test/lsp/feature_language_features_test.cpp +++ b/language_server/test/lsp/feature_language_features_test.cpp @@ -20,12 +20,11 @@ #include "../response_provider_mock.h" #include "../ws_mngr_mock.h" #include "lsp/feature_language_features.h" +#include "utils/platform.h" -#ifdef _WIN32 -constexpr const char* path = "c:\\test"; -#else -constexpr const char* path = "/home/test"; -#endif +using hlasm_plugin::utils::platform::is_windows; + +const char* path = is_windows() ? "c:\\test" : "/home/test"; using namespace hlasm_plugin; using namespace hlasm_plugin::language_server; @@ -38,13 +37,10 @@ TEST(language_features, completion) lsp::feature_language_features f(ws_mngr, response_mock); std::map notifs; f.register_methods(notifs); -#ifdef _WIN32 - json params1 = - R"({"textDocument":{"uri":"file:///c%3A/test"},"position":{"line":0,"character":1},"context":{"triggerKind":1}})"_json; -#else - json params1 = - R"({"textDocument":{"uri":"file:///home/test"},"position":{"line":0,"character":1},"context":{"triggerKind":1}})"_json; -#endif + + json params1 = is_windows() + ? R"({"textDocument":{"uri":"file:///c%3A/test"},"position":{"line":0,"character":1},"context":{"triggerKind":1}})"_json + : R"({"textDocument":{"uri":"file:///home/test"},"position":{"line":0,"character":1},"context":{"triggerKind":1}})"_json; EXPECT_CALL(ws_mngr, completion( @@ -60,11 +56,11 @@ TEST(language_features, hover) lsp::feature_language_features f(ws_mngr, response_mock); std::map notifs; f.register_methods(notifs); -#ifdef _WIN32 - json params1 = R"({"textDocument":{"uri":"file:///c%3A/test"},"position":{"line":0,"character":1}})"_json; -#else - json params1 = R"({"textDocument":{"uri":"file:///home/test"},"position":{"line":0,"character":1}})"_json; -#endif + + json params1 = is_windows() + ? R"({"textDocument":{"uri":"file:///c%3A/test"},"position":{"line":0,"character":1}})"_json + : R"({"textDocument":{"uri":"file:///home/test"},"position":{"line":0,"character":1}})"_json; + std::string s("test"); std::string_view ret(s); EXPECT_CALL(ws_mngr, hover(StrEq(path), parser_library::position(0, 1))).WillOnce(Return(ret)); @@ -82,11 +78,10 @@ TEST(language_features, definition) lsp::feature_language_features f(ws_mngr, response_mock); std::map notifs; f.register_methods(notifs); -#ifdef _WIN32 - json params1 = R"({"textDocument":{"uri":"file:///c%3A/test"},"position":{"line":0,"character":1}})"_json; -#else - json params1 = R"({"textDocument":{"uri":"file:///home/test"},"position":{"line":0,"character":1}})"_json; -#endif + + json params1 = is_windows() + ? R"({"textDocument":{"uri":"file:///c%3A/test"},"position":{"line":0,"character":1}})"_json + : R"({"textDocument":{"uri":"file:///home/test"},"position":{"line":0,"character":1}})"_json; EXPECT_CALL(response_mock, respond(json(""), "", ::testing::_)); notifs["textDocument/definition"]("", params1); @@ -100,11 +95,10 @@ TEST(language_features, references) lsp::feature_language_features f(ws_mngr, response_mock); std::map notifs; f.register_methods(notifs); -#ifdef _WIN32 - json params1 = R"({"textDocument":{"uri":"file:///c%3A/test"},"position":{"line":0,"character":1}})"_json; -#else - json params1 = R"({"textDocument":{"uri":"file:///home/test"},"position":{"line":0,"character":1}})"_json; -#endif + + json params1 = is_windows() + ? R"({"textDocument":{"uri":"file:///c%3A/test"},"position":{"line":0,"character":1}})"_json + : R"({"textDocument":{"uri":"file:///home/test"},"position":{"line":0,"character":1}})"_json; EXPECT_CALL(ws_mngr, references(StrEq(path), parser_library::position(0, 1))); notifs["textDocument/references"]("", params1); diff --git a/language_server/test/lsp/feature_text_synchronization_test.cpp b/language_server/test/lsp/feature_text_synchronization_test.cpp index 41cd9ac6c..e7d04adce 100644 --- a/language_server/test/lsp/feature_text_synchronization_test.cpp +++ b/language_server/test/lsp/feature_text_synchronization_test.cpp @@ -20,14 +20,12 @@ #include "../response_provider_mock.h" #include "../ws_mngr_mock.h" #include "lsp/feature_text_synchronization.h" +#include "utils/platform.h" -#ifdef _WIN32 -const std::string txt_file_uri = R"(file:///c%3A/test/one/blah.txt)"; -const std::string txt_file_path = R"(c:\test\one\blah.txt)"; -#else -const std::string txt_file_uri = R"(file:///home/user/somefile)"; -const std::string txt_file_path = R"(/home/user/somefile)"; -#endif +const std::string txt_file_uri = + hlasm_plugin::utils::platform::is_windows() ? R"(file:///c%3A/test/one/blah.txt)" : R"(file:///home/user/somefile)"; +const std::string txt_file_path = + hlasm_plugin::utils::platform::is_windows() ? R"(c:\test\one\blah.txt)" : R"(/home/user/somefile)"; using namespace hlasm_plugin; using namespace hlasm_plugin::language_server; @@ -116,37 +114,36 @@ TEST(text_synchronization, did_close_file) notifs["textDocument/didClose"]("", params1); } -#ifdef _WIN32 - TEST(feature, uri_to_path) { using namespace hlasm_plugin::language_server; - EXPECT_EQ(feature::uri_to_path("file://czprfs50/Public"), "\\\\czprfs50\\Public"); - EXPECT_EQ(feature::uri_to_path("file:///C%3A/Public"), "c:\\Public"); -} - -TEST(feature, path_to_uri) -{ - using namespace hlasm_plugin::language_server; - EXPECT_EQ(feature::path_to_uri("\\\\czprfs50\\Public"), "file://czprfs50/Public"); - EXPECT_EQ(feature::path_to_uri("c:\\Public"), "file:///c%3A/Public"); -} -#else -TEST(feature, uri_to_path) -{ - using namespace hlasm_plugin::language_server; - EXPECT_EQ(feature::uri_to_path("file:///home/user/somefile"), "/home/user/somefile"); - EXPECT_EQ(feature::uri_to_path("file:///C%3A/Public"), "/C:/Public"); + if (hlasm_plugin::utils::platform::is_windows()) + { + EXPECT_EQ(feature::uri_to_path("file://czprfs50/Public"), "\\\\czprfs50\\Public"); + EXPECT_EQ(feature::uri_to_path("file:///C%3A/Public"), "c:\\Public"); + } + else + { + EXPECT_EQ(feature::uri_to_path("file:///home/user/somefile"), "/home/user/somefile"); + EXPECT_EQ(feature::uri_to_path("file:///C%3A/Public"), "/C:/Public"); + } } TEST(feature, path_to_uri) { using namespace hlasm_plugin::language_server; - EXPECT_EQ(feature::path_to_uri("/home/user/somefile"), "file:///home/user/somefile"); - EXPECT_EQ(feature::path_to_uri("/C:/Public"), "file:///C%3A/Public"); -} -#endif // _WIN32 + if (hlasm_plugin::utils::platform::is_windows()) + { + EXPECT_EQ(feature::path_to_uri("\\\\czprfs50\\Public"), "file://czprfs50/Public"); + EXPECT_EQ(feature::path_to_uri("c:\\Public"), "file:///c%3A/Public"); + } + else + { + EXPECT_EQ(feature::path_to_uri("/home/user/somefile"), "file:///home/user/somefile"); + EXPECT_EQ(feature::path_to_uri("/C:/Public"), "file:///C%3A/Public"); + } +} #endif // !HLASMPLUGIN_LANGUAGESERVER_TEST_FEATURE_TEXT_SYNCHRONIZATION_TEST_H \ No newline at end of file diff --git a/language_server/test/lsp/lsp_server_test.cpp b/language_server/test/lsp/lsp_server_test.cpp index 426cd3b8e..79b981b77 100644 --- a/language_server/test/lsp/lsp_server_test.cpp +++ b/language_server/test/lsp/lsp_server_test.cpp @@ -15,7 +15,6 @@ #include #include "gmock/gmock.h" -#include "json.hpp" #include "../send_message_provider_mock.h" #include "../ws_mngr_mock.h" @@ -23,6 +22,7 @@ #include "lsp/feature_text_synchronization.h" #include "lsp/feature_workspace_folders.h" #include "lsp/lsp_server.h" +#include "nlohmann/json.hpp" #include "workspace_manager.h" namespace nlohmann { diff --git a/language_server/test/lsp/workspace_folders_test.cpp b/language_server/test/lsp/workspace_folders_test.cpp index 4674c9e80..647d74f45 100644 --- a/language_server/test/lsp/workspace_folders_test.cpp +++ b/language_server/test/lsp/workspace_folders_test.cpp @@ -19,35 +19,23 @@ #include "../ws_mngr_mock.h" #include "lib_config.h" #include "lsp/feature_workspace_folders.h" +#include "utils/platform.h" using namespace hlasm_plugin; using namespace hlasm_plugin::language_server; +using hlasm_plugin::utils::platform::is_windows; -#ifdef _WIN32 -const std::string ws1_uri = "file:///c%3A/path/to/W%20S/OneDrive"; -const std::string ws2_uri = "file:///c%3A/path/to/W%20S/TwoDrive"; -const std::string ws3_uri = "file:///c%3A/path/to/W%20S/ThreeDrive"; -const std::string ws4_uri = "file:///c%3A/path/to/W%20S/FourDrive"; - -const std::string ws1_path = R"(c:\path\to\W S\OneDrive)"; -const std::string ws2_path = R"(c:\path\to\W S\TwoDrive)"; -const std::string ws3_path = R"(c:\path\to\W S\ThreeDrive)"; -const std::string ws4_path = R"(c:\path\to\W S\FourDrive)"; - -const std::string ws1_path_json_string = R"(c:\\path\\to\\W S\\OneDrive)"; -#else -const std::string ws1_uri = "file:///path/to/W%20S/OneDrive"; -const std::string ws2_uri = "file:///path/to/W%20S/TwoDrive"; -const std::string ws3_uri = "file:///path/to/W%20S/ThreeDrive"; -const std::string ws4_uri = "file:///path/to/W%20S/FourDrive"; - -const std::string ws1_path = R"(/path/to/W S/OneDrive)"; -const std::string ws2_path = R"(/path/to/W S/TwoDrive)"; -const std::string ws3_path = R"(/path/to/W S/ThreeDrive)"; -const std::string ws4_path = R"(/path/to/W S/FourDrive)"; - -const std::string ws1_path_json_string = R"(/path/to/W S/OneDrive)"; -#endif +const std::string ws1_uri = is_windows() ? "file:///c%3A/path/to/W%20S/OneDrive" : "file:///path/to/W%20S/OneDrive"; +const std::string ws2_uri = is_windows() ? "file:///c%3A/path/to/W%20S/TwoDrive" : "file:///path/to/W%20S/TwoDrive"; +const std::string ws3_uri = is_windows() ? "file:///c%3A/path/to/W%20S/ThreeDrive" : "file:///path/to/W%20S/ThreeDrive"; +const std::string ws4_uri = is_windows() ? "file:///c%3A/path/to/W%20S/FourDrive" : "file:///path/to/W%20S/FourDrive"; + +const std::string ws1_path = is_windows() ? R"(c:\path\to\W S\OneDrive)" : R"(/path/to/W S/OneDrive)"; +const std::string ws2_path = is_windows() ? R"(c:\path\to\W S\TwoDrive)" : R"(/path/to/W S/TwoDrive)"; +const std::string ws3_path = is_windows() ? R"(c:\path\to\W S\ThreeDrive)" : R"(/path/to/W S/ThreeDrive)"; +const std::string ws4_path = is_windows() ? R"(c:\path\to\W S\FourDrive)" : R"(/path/to/W S/FourDrive)"; + +const std::string ws1_path_json_string = is_windows() ? R"(c:\\path\\to\\W S\\OneDrive)" : R"(/path/to/W S/OneDrive)"; TEST(workspace_folders, did_change_workspace_folders) { diff --git a/language_server/test/message_router_test.cpp b/language_server/test/message_router_test.cpp index 0ab298c16..5bab44b5f 100644 --- a/language_server/test/message_router_test.cpp +++ b/language_server/test/message_router_test.cpp @@ -17,7 +17,6 @@ #include #include "gmock/gmock.h" -#include "json.hpp" #include "message_router.h" diff --git a/parser_library/CMakeLists.txt b/parser_library/CMakeLists.txt index 7c7b28c57..0392b6456 100644 --- a/parser_library/CMakeLists.txt +++ b/parser_library/CMakeLists.txt @@ -10,134 +10,43 @@ # Contributors: # Broadcom, Inc. - initial API and implementation -cmake_minimum_required (VERSION 3.10) include(GenerateExportHeader) include(GoogleTest) project(parser_library) -SET (CMAKE_CXX_STANDARD 17) - set(GENERATED_FOLDER ${CMAKE_BINARY_DIR}/generated_parser/) -#generated grammar source files - -set(GENERATED_SRC_CPP - ${GENERATED_FOLDER}/hlasmparser.cpp -) - -set(GENERATED_SRC - ${GENERATED_SRC_CPP} - ${GENERATED_FOLDER}/hlasmparser.h - ) - -if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") - set_source_files_properties(${GENERATED_FOLDER}/hlasmparser.cpp PROPERTIES COMPILE_FLAGS "-Wno-unused-parameter") -endif() -file(GLOB GRAMMAR_SRC - "${PROJECT_SOURCE_DIR}/src/parsing/grammar/*.g4" -) - -include(tag_generated_files.cmake) - -add_custom_target(GenerateParser DEPENDS ${GENERATED_SRC}) -add_custom_command(OUTPUT ${GENERATED_SRC} - COMMAND - ${CMAKE_COMMAND} -E make_directory ${GENERATED_FOLDER} - COMMAND - "${Java_JAVA_EXECUTABLE}" -jar ${ANTLR_JAR_LOCATION} -Werror -Dlanguage=Cpp -lib ${PROJECT_SOURCE_DIR}/src/parsing/grammar/ -o ${GENERATED_FOLDER}/ -package hlasm_plugin::parser_library::parsing hlasmparser.g4 - WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/src/parsing/grammar/" - DEPENDS antlr4jar ${GRAMMAR_SRC} ${PROJECT_SOURCE_DIR}/src/parsing/grammar/lex.tokens - ) +add_library(parser_library) -file(GLOB_RECURSE LIB_SRC - "${PROJECT_SOURCE_DIR}/src/*.cpp" -) +add_subdirectory(src) +add_subdirectory(include) -#Generate the shared library from the library sources -add_library(parser_library - ${LIB_SRC} - ${GENERATED_SRC_CPP} -) if(NOT BUILD_SHARED_LIBS) - set_target_properties(parser_library PROPERTIES COMPILE_FLAGS "-DANTLR4CPP_STATIC") + target_compile_definitions(parser_library PUBLIC PARSER_LIBRARY_STATIC_DEFINE=1) endif() generate_export_header(parser_library EXPORT_FILE_NAME ${GENERATED_FOLDER}/export/parser_library_export.h) -target_link_libraries(parser_library ${ANTLR4_RUNTIME}) -if(FILESYSTEM_LINK) - target_link_libraries(parser_library ${FILESYSTEM_LIBRARY}) -endif() +target_sources(parser_library PUBLIC ${GENERATED_FOLDER}/export/parser_library_export.h) -target_include_directories(parser_library - PUBLIC - ${PROJECT_SOURCE_DIR}/include - ${PROJECT_SOURCE_DIR}/src - ${GENERATED_FOLDER} - ${GENERATED_FOLDER}/export +target_include_directories(parser_library PUBLIC + ${PROJECT_SOURCE_DIR}/include + ${PROJECT_SOURCE_DIR}/src + ${GENERATED_FOLDER} + ${GENERATED_FOLDER}/export ) -ADD_CUSTOM_COMMAND (TARGET parser_library POST_BUILD # Adds a post-build event to MyTest - COMMAND ${CMAKE_COMMAND} -E copy_directory # which executes "cmake - E copy_if_different..." - "${ANTLR4CPP_LIBS}" # <--this is in-file - "${PROJECT_BINARY_DIR}/../bin/" ) - -add_dependencies(parser_library antlr4jar GenerateParser) -add_dependencies(parser_library json) +target_link_libraries(parser_library parser_library_generated) +target_link_libraries(parser_library nlohmann_json::nlohmann_json) +target_link_libraries(parser_library ${ANTLR4_RUNTIME}) +target_link_libraries(parser_library std::filesystem) +target_link_libraries(parser_library hlasm_utils) if(BUILD_TESTING) - file(GLOB_RECURSE TEST_SRC - "${PROJECT_SOURCE_DIR}/test/*.cpp" - ) - - if(BUILD_SHARED_LIBS) #when building shared libary, we need to compile from source, - #because not all classes are exported - add_executable(library_test - ${TEST_SRC} - ${LIB_SRC} - ${GENERATED_SRC_CPP} - ) - else() #when building statically linked library, we can link already built parser_library. - add_executable(library_test ${TEST_SRC}) - target_link_libraries(library_test parser_library) - set_target_properties(library_test PROPERTIES COMPILE_FLAGS "-DANTLR4CPP_STATIC") - endif() - - if(MSVC) - target_compile_options(library_test PRIVATE /bigobj) - endif() - - target_include_directories(library_test - PUBLIC - ${PROJECT_SOURCE_DIR}/include - ${PROJECT_SOURCE_DIR}/src - ${GENERATED_FOLDER} - ${GENERATED_FOLDER}/export - ) - - ADD_CUSTOM_COMMAND (TARGET library_test POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_directory - "${ANTLR4CPP_LIBS}" - "${PROJECT_BINARY_DIR}/../bin/" ) - - add_custom_target(library_tests_copy - COMMAND ${CMAKE_COMMAND} -E copy_directory - ${PROJECT_SOURCE_DIR}/test/res ${CMAKE_BINARY_DIR}/bin/test/library) - - target_link_libraries(library_test gmock_main) - target_link_libraries(library_test ${ANTLR4_RUNTIME}) - if(FILESYSTEM_LINK) - target_link_libraries(library_test ${FILESYSTEM_LIBRARY}) - endif() - - add_dependencies(library_test library_tests_copy) - add_dependencies(library_test antlr4jar) - add_dependencies(library_test json) - - if(DISCOVER_TESTS) - gtest_discover_tests(library_test WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/bin DISCOVERY_TIMEOUT 30) - endif() + add_subdirectory(test) endif() -add_subdirectory(fuzzer) +if(BUILD_FUZZER) + add_subdirectory(fuzzer) +endif() diff --git a/parser_library/fuzzer/CMakeLists.txt b/parser_library/fuzzer/CMakeLists.txt index 6ea52fa98..5e477d62e 100644 --- a/parser_library/fuzzer/CMakeLists.txt +++ b/parser_library/fuzzer/CMakeLists.txt @@ -13,27 +13,22 @@ Project(fuzzer) -if(BUILD_FUZZER) - add_executable(library_fuzzer fuzzer.cpp - ) - target_link_libraries(library_fuzzer parser_library) +add_executable(library_fuzzer) +target_sources(library_fuzzer PRIVATE + fuzzer.cpp +) - set_target_properties(library_fuzzer PROPERTIES LINK_FLAGS "-fsanitize=fuzzer") - set_target_properties(library_fuzzer PROPERTIES COMPILE_FLAGS "-DANTLR4CPP_STATIC") +set_target_properties(library_fuzzer PROPERTIES LINK_FLAGS "-fsanitize=fuzzer") - configure_file( - ${PROJECT_SOURCE_DIR}/fuzzer.dict - ${CMAKE_BINARY_DIR}/bin/fuzzer.dict - COPYONLY) +configure_file( + fuzzer.dict + ${CMAKE_BINARY_DIR}/bin/fuzzer.dict + COPYONLY) + +target_link_libraries(library_fuzzer parser_library) +target_link_libraries(library_fuzzer Threads::Threads) +target_link_libraries(library_fuzzer ${ANTLR4_RUNTIME}) +target_link_libraries(library_fuzzer std::filesystem) +target_link_libraries(library_fuzzer nlohmann_json::nlohmann_json) - if(UNIX) - target_link_libraries(library_fuzzer pthread) - endif() - target_link_libraries(library_fuzzer ${ANTLR4_RUNTIME}) - if(FILESYSTEM_LINK) - target_link_libraries(library_fuzzer ${FILESYSTEM_LIBRARY}) - endif() - - add_dependencies(library_fuzzer antlr4jar json) -endif() \ No newline at end of file diff --git a/parser_library/include/CMakeLists.txt b/parser_library/include/CMakeLists.txt new file mode 100644 index 000000000..7bebe186d --- /dev/null +++ b/parser_library/include/CMakeLists.txt @@ -0,0 +1,23 @@ +# Copyright (c) 2021 Broadcom. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Broadcom, Inc. - initial API and implementation +# + +target_sources(parser_library PUBLIC + debugger.h + lib_config.h + message_consumer.h + parser_library.h + protocol.h + range.h + sequence.h + workspace_manager.h +) diff --git a/parser_library/include/lib_config.h b/parser_library/include/lib_config.h index 5dc17a64a..33d1b9ae6 100644 --- a/parser_library/include/lib_config.h +++ b/parser_library/include/lib_config.h @@ -18,9 +18,9 @@ #include #include -#include "json.hpp" - +#include "nlohmann/json.hpp" #include "parser_library_export.h" + namespace hlasm_plugin::parser_library { // Encapsulates user defined settings of library and individual workspaces diff --git a/parser_library/src/CMakeLists.txt b/parser_library/src/CMakeLists.txt new file mode 100644 index 000000000..2e8cfe343 --- /dev/null +++ b/parser_library/src/CMakeLists.txt @@ -0,0 +1,45 @@ +# Copyright (c) 2021 Broadcom. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Broadcom, Inc. - initial API and implementation + +target_sources(parser_library PRIVATE + analyzer.cpp + analyzer.h + analyzing_context.h + compiler_options.h + diagnosable.h + diagnosable_ctx.h + diagnosable_impl.h + diagnostic.cpp + diagnostic.h + diagnostic_adder.cpp + diagnostic_adder.h + ebcdic_encoding.cpp + ebcdic_encoding.h + error_messages.h + lib_config.cpp + location.h + parser_library.cpp + protocol.cpp + workspace_manager.cpp + workspace_manager_impl.h +) + +add_subdirectory(checking) +add_subdirectory(context) +add_subdirectory(debugging) +add_subdirectory(expressions) +add_subdirectory(lexing) +add_subdirectory(lsp) +add_subdirectory(parsing) +add_subdirectory(processing) +add_subdirectory(semantics) +add_subdirectory(workspaces) diff --git a/parser_library/src/checking/CMakeLists.txt b/parser_library/src/checking/CMakeLists.txt new file mode 100644 index 000000000..96b6b0a7c --- /dev/null +++ b/parser_library/src/checking/CMakeLists.txt @@ -0,0 +1,28 @@ +# Copyright (c) 2021 Broadcom. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Broadcom, Inc. - initial API and implementation + +target_sources(parser_library PRIVATE + asm_instr_check.cpp + asm_instr_check.h + asm_instr_class.cpp + asm_instr_class.h + checker_helper.h + diagnostic_collector.cpp + diagnostic_collector.h + instr_operand.cpp + instr_operand.h + instruction_checker.cpp + instruction_checker.h + operand.h +) + +add_subdirectory(data_definition) diff --git a/parser_library/src/checking/data_definition/CMakeLists.txt b/parser_library/src/checking/data_definition/CMakeLists.txt new file mode 100644 index 000000000..fe6c181d5 --- /dev/null +++ b/parser_library/src/checking/data_definition/CMakeLists.txt @@ -0,0 +1,26 @@ +# Copyright (c) 2021 Broadcom. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Broadcom, Inc. - initial API and implementation + +target_sources(parser_library PRIVATE + data_def_fields.h + data_def_type_base.cpp + data_def_type_base.h + data_def_types.h + data_def_types_address.cpp + data_def_types_fixed_point.cpp + data_def_types_floating_point.cpp + data_def_types_string.cpp + data_definition_operand.cpp + data_definition_operand.h + data_instruction.cpp +) + diff --git a/parser_library/src/context/CMakeLists.txt b/parser_library/src/context/CMakeLists.txt new file mode 100644 index 000000000..dab2b4ad8 --- /dev/null +++ b/parser_library/src/context/CMakeLists.txt @@ -0,0 +1,42 @@ +# Copyright (c) 2021 Broadcom. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Broadcom, Inc. - initial API and implementation + +target_sources(parser_library PRIVATE + code_scope.h + common_types.cpp + common_types.h + copy_member.h + hlasm_context.cpp + hlasm_context.h + hlasm_statement.cpp + hlasm_statement.h + id_storage.cpp + id_storage.h + instruction.cpp + instruction.h + instruction_type.h + macro.cpp + macro.h + macro_param_data.cpp + macro_param_data.h + operation_code.h + processing_context.cpp + processing_context.h + sequence_symbol.cpp + sequence_symbol.h + source_snapshot.h + statement_cache.cpp + statement_cache.h +) + +add_subdirectory(ordinary_assembly) +add_subdirectory(variables) diff --git a/parser_library/src/context/ordinary_assembly/CMakeLists.txt b/parser_library/src/context/ordinary_assembly/CMakeLists.txt new file mode 100644 index 000000000..0d652d8df --- /dev/null +++ b/parser_library/src/context/ordinary_assembly/CMakeLists.txt @@ -0,0 +1,42 @@ +# Copyright (c) 2021 Broadcom. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Broadcom, Inc. - initial API and implementation + +target_sources(parser_library PRIVATE + address.cpp + address.h + address_resolver.cpp + address_resolver.h + alignment.h + dependable.h + dependant.cpp + dependant.h + dependency_collector.cpp + dependency_collector.h + location_counter.cpp + location_counter.h + location_counter_data.cpp + location_counter_data.h + loctr_dependency_resolver.h + ordinary_assembly_context.cpp + ordinary_assembly_context.h + postponed_statement.cpp + postponed_statement.h + section.cpp + section.h + symbol.cpp + symbol.h + symbol_attributes.cpp + symbol_attributes.h + symbol_dependency_tables.cpp + symbol_dependency_tables.h +) + diff --git a/parser_library/src/context/variables/CMakeLists.txt b/parser_library/src/context/variables/CMakeLists.txt new file mode 100644 index 000000000..ecc4d5c73 --- /dev/null +++ b/parser_library/src/context/variables/CMakeLists.txt @@ -0,0 +1,23 @@ +# Copyright (c) 2021 Broadcom. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Broadcom, Inc. - initial API and implementation + +target_sources(parser_library PRIVATE + macro_param.cpp + macro_param.h + set_symbol.cpp + set_symbol.h + system_variable.cpp + system_variable.h + variable.cpp + variable.h +) + diff --git a/parser_library/src/debugging/CMakeLists.txt b/parser_library/src/debugging/CMakeLists.txt new file mode 100644 index 000000000..01ef6786c --- /dev/null +++ b/parser_library/src/debugging/CMakeLists.txt @@ -0,0 +1,28 @@ +# Copyright (c) 2021 Broadcom. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Broadcom, Inc. - initial API and implementation + +target_sources(parser_library PRIVATE + attribute_variable.cpp + attribute_variable.h + debug_lib_provider.h + debug_types.h + debugger.cpp + macro_param_variable.cpp + macro_param_variable.h + ordinary_symbol_variable.cpp + ordinary_symbol_variable.h + set_symbol_variable.cpp + set_symbol_variable.h + variable.cpp + variable.h +) + diff --git a/parser_library/src/expressions/CMakeLists.txt b/parser_library/src/expressions/CMakeLists.txt new file mode 100644 index 000000000..947c4e801 --- /dev/null +++ b/parser_library/src/expressions/CMakeLists.txt @@ -0,0 +1,27 @@ +# Copyright (c) 2021 Broadcom. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Broadcom, Inc. - initial API and implementation + +target_sources(parser_library PRIVATE + data_definition.cpp + data_definition.h + evaluation_context.h + mach_expr_term.cpp + mach_expr_term.h + mach_expr_visitor.h + mach_expression.cpp + mach_expression.h + mach_operator.h + nominal_value.cpp + nominal_value.h +) + +add_subdirectory(conditional_assembly) diff --git a/parser_library/src/expressions/conditional_assembly/CMakeLists.txt b/parser_library/src/expressions/conditional_assembly/CMakeLists.txt new file mode 100644 index 000000000..45f15c50c --- /dev/null +++ b/parser_library/src/expressions/conditional_assembly/CMakeLists.txt @@ -0,0 +1,25 @@ +# Copyright (c) 2021 Broadcom. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Broadcom, Inc. - initial API and implementation + +target_sources(parser_library PRIVATE + ca_expr_policy.cpp + ca_expr_policy.h + ca_expr_visitor.h + ca_expression.cpp + ca_expression.h + ca_operator_binary.cpp + ca_operator_binary.h + ca_operator_unary.cpp + ca_operator_unary.h +) + +add_subdirectory(terms) diff --git a/parser_library/src/expressions/conditional_assembly/terms/CMakeLists.txt b/parser_library/src/expressions/conditional_assembly/terms/CMakeLists.txt new file mode 100644 index 000000000..86c0d7a3c --- /dev/null +++ b/parser_library/src/expressions/conditional_assembly/terms/CMakeLists.txt @@ -0,0 +1,29 @@ +# Copyright (c) 2021 Broadcom. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Broadcom, Inc. - initial API and implementation + +target_sources(parser_library PRIVATE + ca_constant.cpp + ca_constant.h + ca_expr_list.cpp + ca_expr_list.h + ca_function.cpp + ca_function.h + ca_string.cpp + ca_string.h + ca_symbol.cpp + ca_symbol.h + ca_symbol_attribute.cpp + ca_symbol_attribute.h + ca_var_sym.cpp + ca_var_sym.h +) + diff --git a/parser_library/src/lexing/CMakeLists.txt b/parser_library/src/lexing/CMakeLists.txt new file mode 100644 index 000000000..8a2cc85e1 --- /dev/null +++ b/parser_library/src/lexing/CMakeLists.txt @@ -0,0 +1,25 @@ +# Copyright (c) 2021 Broadcom. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Broadcom, Inc. - initial API and implementation + +target_sources(parser_library PRIVATE + input_source.cpp + input_source.h + lexer.cpp + lexer.h + token.cpp + token.h + token_factory.cpp + token_factory.h + token_stream.cpp + token_stream.h +) + diff --git a/parser_library/src/lexing/token.cpp b/parser_library/src/lexing/token.cpp index a5d3e9874..a29b14dd7 100644 --- a/parser_library/src/lexing/token.cpp +++ b/parser_library/src/lexing/token.cpp @@ -15,7 +15,8 @@ #include "token.h" #include -#include + +#include using namespace hlasm_plugin::parser_library::lexing; diff --git a/parser_library/src/lsp/CMakeLists.txt b/parser_library/src/lsp/CMakeLists.txt new file mode 100644 index 000000000..7ce28f7fe --- /dev/null +++ b/parser_library/src/lsp/CMakeLists.txt @@ -0,0 +1,26 @@ +# Copyright (c) 2021 Broadcom. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Broadcom, Inc. - initial API and implementation + +target_sources(parser_library PRIVATE + completion_item.cpp + completion_item.h + feature_provider.h + file_info.cpp + file_info.h + lsp_context.cpp + lsp_context.h + macro_info.h + opencode_info.h + symbol_occurence.h + text_data_ref_t.cpp + text_data_ref_t.h +) diff --git a/parser_library/src/parser_library.cpp b/parser_library/src/parser_library.cpp index 66f6b436a..9787f4a1c 100644 --- a/parser_library/src/parser_library.cpp +++ b/parser_library/src/parser_library.cpp @@ -14,8 +14,6 @@ #include "parser_library.h" -#include - #include "analyzer.h" #include "context/hlasm_context.h" #include "hlasmparser.h" diff --git a/parser_library/src/parsing/CMakeLists.txt b/parser_library/src/parsing/CMakeLists.txt new file mode 100644 index 000000000..b2e0904b1 --- /dev/null +++ b/parser_library/src/parsing/CMakeLists.txt @@ -0,0 +1,25 @@ +# Copyright (c) 2021 Broadcom. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Broadcom, Inc. - initial API and implementation + +target_sources(parser_library PRIVATE + error_strategy.h + parser_error_listener.cpp + parser_error_listener.h + parser_error_listener_ctx.cpp + parser_error_listener_ctx.h + parser_impl.cpp + parser_impl.h + parser_tools.cpp + parser_tools.h +) + +add_subdirectory(grammar) diff --git a/parser_library/src/parsing/grammar/CMakeLists.txt b/parser_library/src/parsing/grammar/CMakeLists.txt new file mode 100644 index 000000000..eb8b73601 --- /dev/null +++ b/parser_library/src/parsing/grammar/CMakeLists.txt @@ -0,0 +1,48 @@ +# Copyright (c) 2021 Broadcom. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Broadcom, Inc. - initial API and implementation +#generated grammar source files + +set(GENERATED_SRC_CPP + ${GENERATED_FOLDER}/hlasmparser.cpp +) + +set(GENERATED_SRC + ${GENERATED_SRC_CPP} + ${GENERATED_FOLDER}/hlasmparser.h + ) + +file(GLOB GRAMMAR_SRC + "*.g4" +) + +add_custom_command(OUTPUT ${GENERATED_SRC} + COMMAND + ${CMAKE_COMMAND} -E make_directory ${GENERATED_FOLDER} + COMMAND + "${Java_JAVA_EXECUTABLE}" -jar ${ANTLR_JAR_LOCATION} -Werror -Dlanguage=Cpp -lib ${PROJECT_SOURCE_DIR}/src/parsing/grammar/ -o ${GENERATED_FOLDER}/ -package hlasm_plugin::parser_library::parsing hlasmparser.g4 + WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/src/parsing/grammar/" + DEPENDS antlr4jar ${GRAMMAR_SRC} lex.tokens +) + +if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + set_source_files_properties(${GENERATED_FOLDER}/hlasmparser.cpp PROPERTIES COMPILE_FLAGS "-Wno-unused-parameter") +endif() + +add_library(parser_library_generated OBJECT "${GENERATED_SRC}") + +target_include_directories(parser_library_generated PRIVATE + ${PROJECT_SOURCE_DIR}/include + ${PROJECT_SOURCE_DIR}/src + ${GENERATED_FOLDER} + ${GENERATED_FOLDER}/export +) +target_link_libraries(parser_library_generated ${ANTLR4_RUNTIME}) diff --git a/parser_library/src/processing/CMakeLists.txt b/parser_library/src/processing/CMakeLists.txt new file mode 100644 index 000000000..7a063d62b --- /dev/null +++ b/parser_library/src/processing/CMakeLists.txt @@ -0,0 +1,30 @@ +# Copyright (c) 2021 Broadcom. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Broadcom, Inc. - initial API and implementation + +target_sources(parser_library PRIVATE + branching_provider.h + context_manager.cpp + context_manager.h + op_code.h + opencode_provider.h + processing_format.h + processing_manager.cpp + processing_manager.h + processing_state_listener.h + statement.h + statement_fields_parser.h +) + +add_subdirectory(instruction_sets) +add_subdirectory(statement_analyzers) +add_subdirectory(statement_processors) +add_subdirectory(statement_providers) diff --git a/parser_library/src/processing/instruction_sets/CMakeLists.txt b/parser_library/src/processing/instruction_sets/CMakeLists.txt new file mode 100644 index 000000000..198d4e860 --- /dev/null +++ b/parser_library/src/processing/instruction_sets/CMakeLists.txt @@ -0,0 +1,28 @@ +# Copyright (c) 2021 Broadcom. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Broadcom, Inc. - initial API and implementation + +target_sources(parser_library PRIVATE + asm_processor.cpp + asm_processor.h + ca_processor.cpp + ca_processor.h + data_def_postponed_statement.h + instruction_processor.h + low_language_processor.cpp + low_language_processor.h + mach_processor.cpp + mach_processor.h + macro_processor.cpp + macro_processor.h + postponed_statement_impl.h +) + diff --git a/parser_library/src/processing/statement_analyzers/CMakeLists.txt b/parser_library/src/processing/statement_analyzers/CMakeLists.txt new file mode 100644 index 000000000..133309b21 --- /dev/null +++ b/parser_library/src/processing/statement_analyzers/CMakeLists.txt @@ -0,0 +1,19 @@ +# Copyright (c) 2021 Broadcom. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Broadcom, Inc. - initial API and implementation + +target_sources(parser_library PRIVATE + lsp_analyzer.cpp + lsp_analyzer.h + occurence_collector.cpp + occurence_collector.h + statement_analyzer.h +) diff --git a/parser_library/src/processing/statement_processors/CMakeLists.txt b/parser_library/src/processing/statement_processors/CMakeLists.txt new file mode 100644 index 000000000..de20445ea --- /dev/null +++ b/parser_library/src/processing/statement_processors/CMakeLists.txt @@ -0,0 +1,29 @@ +# Copyright (c) 2021 Broadcom. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Broadcom, Inc. - initial API and implementation + +target_sources(parser_library PRIVATE + copy_processing_info.h + copy_processor.cpp + copy_processor.h + empty_processor.cpp + empty_processor.h + lookahead_processing_info.h + lookahead_processor.cpp + lookahead_processor.h + macrodef_processing_info.h + macrodef_processor.cpp + macrodef_processor.h + ordinary_processor.cpp + ordinary_processor.h + statement_processor.h +) + diff --git a/parser_library/src/processing/statement_providers/CMakeLists.txt b/parser_library/src/processing/statement_providers/CMakeLists.txt new file mode 100644 index 000000000..42060294b --- /dev/null +++ b/parser_library/src/processing/statement_providers/CMakeLists.txt @@ -0,0 +1,24 @@ +# Copyright (c) 2021 Broadcom. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Broadcom, Inc. - initial API and implementation + +target_sources(parser_library PRIVATE + copy_statement_provider.cpp + copy_statement_provider.h + macro_statement_provider.cpp + macro_statement_provider.h + members_statement_provider.cpp + members_statement_provider.h + statement_provider.cpp + statement_provider.h + statement_provider_kind.h +) + diff --git a/parser_library/src/semantics/CMakeLists.txt b/parser_library/src/semantics/CMakeLists.txt new file mode 100644 index 000000000..348b1f50a --- /dev/null +++ b/parser_library/src/semantics/CMakeLists.txt @@ -0,0 +1,33 @@ +# Copyright (c) 2021 Broadcom. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Broadcom, Inc. - initial API and implementation + +target_sources(parser_library PRIVATE + collector.cpp + collector.h + concatenation.cpp + concatenation.h + concatenation_term.cpp + concatenation_term.h + highlighting_info.h + operand.h + operand_impls.cpp + operand_impls.h + operand_visitor.h + range_provider.cpp + range_provider.h + source_info_processor.cpp + source_info_processor.h + statement.h + statement_fields.h + variable_symbol.cpp + variable_symbol.h +) diff --git a/parser_library/src/workspaces/CMakeLists.txt b/parser_library/src/workspaces/CMakeLists.txt new file mode 100644 index 000000000..cdbdb090b --- /dev/null +++ b/parser_library/src/workspaces/CMakeLists.txt @@ -0,0 +1,33 @@ +# Copyright (c) 2021 Broadcom. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Broadcom, Inc. - initial API and implementation + +target_sources(parser_library PRIVATE + file.h + file_impl.cpp + file_impl.h + file_manager.h + file_manager_impl.cpp + file_manager_impl.h + library.cpp + library.h + parse_lib_provider.cpp + parse_lib_provider.h + processor.h + processor_file_impl.cpp + processor_file_impl.h + processor_group.h + wildcard.cpp + wildcard.h + workspace.cpp + workspace.h +) + diff --git a/parser_library/src/workspaces/file_manager_impl.cpp b/parser_library/src/workspaces/file_manager_impl.cpp index 969bc03c9..a1b8b3595 100644 --- a/parser_library/src/workspaces/file_manager_impl.cpp +++ b/parser_library/src/workspaces/file_manager_impl.cpp @@ -17,6 +17,8 @@ #include #include "processor_file_impl.h" +#include "utils/path.h" +#include "utils/platform.h" namespace hlasm_plugin::parser_library::workspaces { @@ -123,32 +125,30 @@ std::unordered_map file_manager_impl::list_directory_f { std::filesystem::path lib_p(path); std::unordered_map found_files; - try - { - std::filesystem::directory_entry dir(lib_p); - if (!dir.exists() && optional) - return found_files; - if (!dir.is_directory()) - { + auto ec = utils::path::list_directory_regular_files(lib_p, [&found_files](const std::filesystem::path& f) { + found_files[utils::path::filename(f).string()] = utils::path::absolute(f).string(); + }); + switch (ec) + { + case hlasm_plugin::utils::path::list_directory_rc::done: + break; + case hlasm_plugin::utils::path::list_directory_rc::not_exists: + if (!optional) + add_diagnostic(diagnostic_s { "", + {}, + "L0001", + "Unable to load library: " + path + ". Error: The path does not point to directory." }); + break; + case hlasm_plugin::utils::path::list_directory_rc::not_a_directory: add_diagnostic(diagnostic_s { "", {}, "L0001", "Unable to load library: " + path + ". Error: The path does not point to directory." }); - return found_files; - } - - std::filesystem::directory_iterator it(lib_p); - - for (auto& p : it) - { - if (p.is_regular_file()) - found_files[p.path().filename().string()] = p.path().string(); - } - } - catch (const std::filesystem::filesystem_error& e) - { - add_diagnostic(diagnostic_s { path, {}, "L0001", "Unable to load library: " + path + ". Error: " + e.what() }); + break; + case hlasm_plugin::utils::path::list_directory_rc::other_failure: + add_diagnostic(diagnostic_s { path, {}, "L0001", "Unable to load library: " + path + "." }); + break; } return found_files; } @@ -221,9 +221,7 @@ bool file_manager_impl::file_exists(const std::string& file_name) bool file_manager_impl::lib_file_exists(const std::string& lib_path, const std::string& file_name) { - std::filesystem::path lib_path_p(lib_path); - std::filesystem::path file_path(lib_path_p / file_name); - return std::filesystem::exists(file_path); + return std::filesystem::exists(utils::path::join(lib_path, file_name)); } } // namespace hlasm_plugin::parser_library::workspaces diff --git a/parser_library/src/workspaces/library.cpp b/parser_library/src/workspaces/library.cpp index 54b4b6c46..e8cc88a5a 100644 --- a/parser_library/src/workspaces/library.cpp +++ b/parser_library/src/workspaces/library.cpp @@ -15,11 +15,13 @@ #include "library.h" #include +#include #include #include -#include "json.hpp" - +#include "nlohmann/json.hpp" +#include "utils/path.h" +#include "utils/platform.h" #include "wildcard.h" namespace hlasm_plugin::parser_library::workspaces { @@ -64,14 +66,12 @@ std::shared_ptr library_local::find_file(const std::string& file_name auto found = files_.find(file_name); if (found != files_.end()) { - std::filesystem::path lib_path(lib_path_); - return file_manager_.add_processor_file((lib_path / found->second).string()); + return file_manager_.add_processor_file(utils::path::join(lib_path_, found->second).string()); } else return nullptr; } - void library_local::load_files() { auto files_list = file_manager_.list_directory_files(lib_path_, optional_); @@ -88,6 +88,7 @@ void library_local::load_files() { files_[context::to_upper_copy(file.first.substr(0, file.first.size() - extension.first.size()))] = file.second; + // TODO: the stored value is a full path, yet we try to interpret it as a relative one later on added = true; break; } diff --git a/parser_library/src/workspaces/wildcard.cpp b/parser_library/src/workspaces/wildcard.cpp index 4f50a6c30..1d9c2329e 100644 --- a/parser_library/src/workspaces/wildcard.cpp +++ b/parser_library/src/workspaces/wildcard.cpp @@ -14,17 +14,25 @@ #include "wildcard.h" -#include +#include "utils/platform.h" namespace hlasm_plugin::parser_library::workspaces { +namespace { +// used for wildcard to regex conversions +const std::regex escape("(\\(|\\[|\\{|\\\\|\\^|\\-|\\=|\\$|\\!|\\||\\]|\\}|\\)|\\.)"); +const std::regex question("\\?"); +const std::regex nongreedy("(\\*|\\+)"); +const std::regex slash("\\/"); +} // namespace std::regex wildcard2regex(const std::string& wildcard) { auto regex_str = wildcard; -#ifdef _WIN32 - // change of forward slash to double backslash on windows - regex_str = std::regex_replace(regex_str, slash, "\\"); -#endif + if (utils::platform::is_windows()) + { + // change of forward slash to double backslash on windows + regex_str = std::regex_replace(regex_str, slash, "\\"); + } regex_str = std::regex_replace(regex_str, escape, "\\$1"); regex_str = std::regex_replace(regex_str, question, "."); regex_str = std::regex_replace(regex_str, nongreedy, ".$1?"); diff --git a/parser_library/src/workspaces/wildcard.h b/parser_library/src/workspaces/wildcard.h index 4be97152f..8a27f5a80 100644 --- a/parser_library/src/workspaces/wildcard.h +++ b/parser_library/src/workspaces/wildcard.h @@ -18,13 +18,6 @@ #include namespace hlasm_plugin::parser_library::workspaces { - -// used for wildcard to regex conversions - -static const std::regex escape("(\\(|\\[|\\{|\\\\|\\^|\\-|\\=|\\$|\\!|\\||\\]|\\}|\\)|\\.)"); -static const std::regex question("\\?"); -static const std::regex nongreedy("(\\*|\\+)"); -static const std::regex slash("\\/"); // Returns a regex that can be used for wildcard matching. std::regex wildcard2regex(const std::string& wildcard); diff --git a/parser_library/src/workspaces/workspace.cpp b/parser_library/src/workspaces/workspace.cpp index 5574cfe8b..32a8f5796 100644 --- a/parser_library/src/workspaces/workspace.cpp +++ b/parser_library/src/workspaces/workspace.cpp @@ -15,11 +15,14 @@ #include "workspace.h" #include +#include #include #include #include "lib_config.h" #include "processor.h" +#include "utils/path.h" +#include "utils/platform.h" #include "wildcard.h" using json = nlohmann::json; @@ -39,8 +42,9 @@ workspace::workspace(const ws_uri& uri, , ws_path_(uri) , global_config_(global_config) { - proc_grps_path_ = ws_path_ / HLASM_PLUGIN_FOLDER / FILENAME_PROC_GRPS; - pgm_conf_path_ = ws_path_ / HLASM_PLUGIN_FOLDER / FILENAME_PGM_CONF; + auto hlasm_folder = utils::path::join(ws_path_, HLASM_PLUGIN_FOLDER); + proc_grps_path_ = utils::path::join(hlasm_folder, FILENAME_PROC_GRPS); + pgm_conf_path_ = utils::path::join(hlasm_folder, FILENAME_PGM_CONF); } workspace::workspace( @@ -123,8 +127,7 @@ const processor_group& workspace::get_proc_grp_by_program(const std::string& fil { assert(opened_); - std::filesystem::path fname_path(filename); - std::string file = fname_path.lexically_relative(uri_).lexically_normal().string(); + std::string file = utils::path::lexically_normal(utils::path::lexically_relative(filename, uri_)).string(); // direct match auto program = exact_pgm_conf_.find(file); @@ -145,7 +148,7 @@ void workspace::parse_file(const std::string& file_uri) { std::filesystem::path file_path(file_uri); // add support for hlasm to vscode (auto detection??) and do the decision based on languageid - if (file_path == proc_grps_path_ || file_path == pgm_conf_path_) + if (utils::path::equal(file_path, proc_grps_path_) || utils::path::equal(file_path, pgm_conf_path_)) { if (load_and_process_config()) { @@ -342,7 +345,7 @@ bool workspace::load_and_process_config() // extension wildcard if (std::regex_match(wildcard_str, extension_regex)) extensions.insert({ std::regex_replace(wildcard_str, extension_regex, "$2"), - wildcard2regex((ws_path / wildcard_str).string()) }); + wildcard2regex(utils::path::join(ws_path, wildcard_str).string()) }); } /*auto suppress_diags_limit_json = pgm_conf_json.find("diagnosticsSuppressLimit"); @@ -408,15 +411,20 @@ bool workspace::load_and_process_config() if (valid) { - if (!path.empty()) - path += '/'; - std::filesystem::path lib_path(std::move(path)); - if (lib_path.is_absolute()) - prc_grp.add_library(std::make_unique( - file_manager_, lib_path.lexically_normal().string(), extensions_ptr, optional)); - else if (lib_path.is_relative()) + std::filesystem::path lib_path = [&path]() { + if (!path.empty()) + return utils::path::join(std::move(path), ""); + return std::filesystem::path {}; + }(); + + if (utils::path::is_absolute(lib_path)) prc_grp.add_library(std::make_unique( - file_manager_, (ws_path / lib_path).lexically_normal().string(), extensions_ptr, optional)); + file_manager_, utils::path::lexically_normal(lib_path).string(), extensions_ptr, optional)); + else + prc_grp.add_library(std::make_unique(file_manager_, + utils::path::lexically_normal(utils::path::join(ws_path, lib_path)).string(), + extensions_ptr, + optional)); // else ignore, publish warning } } @@ -437,10 +445,12 @@ bool workspace::load_and_process_config() if (proc_grps_.find(pgroup) != proc_grps_.end()) { -#ifdef _WIN32 - // change of forward slash to double backslash on windows - pgm_name = std::regex_replace(pgm_name, slash, "\\"); -#endif + if (utils::platform::is_windows()) + { + // change of forward slash to double backslash on windows + static const std::regex slash("\\/"); + pgm_name = std::regex_replace(pgm_name, slash, "\\"); + } if (!is_wildcard(pgm_name)) exact_pgm_conf_.emplace(pgm_name, program { pgm_name, pgroup }); else @@ -456,16 +466,14 @@ bool workspace::load_and_process_config() } bool workspace::load_config(nlohmann::json& proc_grps_json, nlohmann::json& pgm_conf_json, file_ptr& pgm_conf_file) { - std::filesystem::path ws_path(uri_); - + std::filesystem::path hlasm_base = utils::path::join(uri_, HLASM_PLUGIN_FOLDER); // proc_grps.json parse - file_ptr proc_grps_file = file_manager_.add_file((ws_path / HLASM_PLUGIN_FOLDER / FILENAME_PROC_GRPS).string()); + file_ptr proc_grps_file = file_manager_.add_file(utils::path::join(hlasm_base, FILENAME_PROC_GRPS).string()); if (proc_grps_file->update_and_get_bad()) return false; - try { proc_grps_json = nlohmann::json::parse(proc_grps_file->get_text()); @@ -479,7 +487,7 @@ bool workspace::load_config(nlohmann::json& proc_grps_json, nlohmann::json& pgm_ } // pgm_conf.json parse - pgm_conf_file = file_manager_.add_file((ws_path / HLASM_PLUGIN_FOLDER / FILENAME_PGM_CONF).string()); + pgm_conf_file = file_manager_.add_file(utils::path::join(hlasm_base, FILENAME_PGM_CONF).string()); if (pgm_conf_file->update_and_get_bad()) return false; diff --git a/parser_library/src/workspaces/workspace.h b/parser_library/src/workspaces/workspace.h index 826c6527d..b73a5a5c9 100644 --- a/parser_library/src/workspaces/workspace.h +++ b/parser_library/src/workspaces/workspace.h @@ -22,13 +22,12 @@ #include #include -#include "json.hpp" - #include "diagnosable_impl.h" #include "file_manager.h" #include "lib_config.h" #include "library.h" #include "message_consumer.h" +#include "nlohmann/json.hpp" #include "processor.h" #include "processor_group.h" diff --git a/parser_library/test/CMakeLists.txt b/parser_library/test/CMakeLists.txt new file mode 100644 index 000000000..3b25ea102 --- /dev/null +++ b/parser_library/test/CMakeLists.txt @@ -0,0 +1,60 @@ +# Copyright (c) 2021 Broadcom. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Broadcom, Inc. - initial API and implementation + +add_executable(library_test) + +target_sources(library_test PRIVATE + common_testing.h + copy_mock.h + diagnosable_ctx_test.cpp + diagnostics_check_test.cpp + gtest_stringers.h + message_consumer_mock.h + metrics_test.cpp + mock_parse_lib_provider.h + stability_test.cpp + utf_conv_test.cpp + workspace_manager_test.cpp +) + +add_subdirectory(checking) +add_subdirectory(context) +add_subdirectory(debugging) +add_subdirectory(export) +add_subdirectory(expressions) +add_subdirectory(lexing) +add_subdirectory(lsp) +add_subdirectory(parsing) +add_subdirectory(processing) +add_subdirectory(semantics) +add_subdirectory(workspace) + + +if(MSVC) + target_compile_options(library_test PRIVATE /bigobj) +endif() + +add_custom_target(library_tests_copy + COMMAND ${CMAKE_COMMAND} -E copy_directory + ${PROJECT_SOURCE_DIR}/test/res ${CMAKE_BINARY_DIR}/bin/test/library) + +target_link_libraries(library_test parser_library) +target_link_libraries(library_test gmock_main) +target_link_libraries(library_test ${ANTLR4_RUNTIME}) +target_link_libraries(library_test std::filesystem) +target_link_libraries(library_test nlohmann_json::nlohmann_json) + +add_dependencies(library_test library_tests_copy) + +if(DISCOVER_TESTS) + gtest_discover_tests(library_test WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/bin DISCOVERY_TIMEOUT 120) +endif() diff --git a/parser_library/test/checking/CMakeLists.txt b/parser_library/test/checking/CMakeLists.txt new file mode 100644 index 000000000..7c0ef31d7 --- /dev/null +++ b/parser_library/test/checking/CMakeLists.txt @@ -0,0 +1,20 @@ +# Copyright (c) 2021 Broadcom. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Broadcom, Inc. - initial API and implementation + +target_sources(library_test PRIVATE + asm_instr_check_test.cpp + asm_instr_diag_test.cpp + mach_instr_check_test.cpp + mach_instr_diag_test.cpp +) + +add_subdirectory(data_definition) diff --git a/parser_library/test/checking/data_definition/CMakeLists.txt b/parser_library/test/checking/data_definition/CMakeLists.txt new file mode 100644 index 000000000..e0803740f --- /dev/null +++ b/parser_library/test/checking/data_definition/CMakeLists.txt @@ -0,0 +1,21 @@ +# Copyright (c) 2021 Broadcom. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Broadcom, Inc. - initial API and implementation + +target_sources(library_test PRIVATE + data_definition_check_test.cpp + data_definition_common.h + data_definition_integer_test.cpp + data_definition_length_test.cpp + data_definition_scale_test.cpp + data_definition_test.cpp +) + diff --git a/parser_library/test/context/CMakeLists.txt b/parser_library/test/context/CMakeLists.txt new file mode 100644 index 000000000..56b28291d --- /dev/null +++ b/parser_library/test/context/CMakeLists.txt @@ -0,0 +1,21 @@ +# Copyright (c) 2021 Broadcom. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Broadcom, Inc. - initial API and implementation + +target_sources(library_test PRIVATE + address_test.cpp + context_test.cpp + data_attribute_test.cpp + dependency_collector_test.cpp + macro_test.cpp + ord_sym_test.cpp +) + diff --git a/parser_library/test/debugging/CMakeLists.txt b/parser_library/test/debugging/CMakeLists.txt new file mode 100644 index 000000000..cb42d7adf --- /dev/null +++ b/parser_library/test/debugging/CMakeLists.txt @@ -0,0 +1,17 @@ +# Copyright (c) 2021 Broadcom. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Broadcom, Inc. - initial API and implementation + +target_sources(library_test PRIVATE + debug_event_consumer_s_mock.h + debugger_test.cpp +) + diff --git a/parser_library/test/export/CMakeLists.txt b/parser_library/test/export/CMakeLists.txt new file mode 100644 index 000000000..34dee608d --- /dev/null +++ b/parser_library/test/export/CMakeLists.txt @@ -0,0 +1,16 @@ +# Copyright (c) 2021 Broadcom. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Broadcom, Inc. - initial API and implementation + +target_sources(library_test PRIVATE + parser_library_export.h +) + diff --git a/parser_library/test/expressions/CMakeLists.txt b/parser_library/test/expressions/CMakeLists.txt new file mode 100644 index 000000000..ee96c73f9 --- /dev/null +++ b/parser_library/test/expressions/CMakeLists.txt @@ -0,0 +1,27 @@ +# Copyright (c) 2021 Broadcom. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Broadcom, Inc. - initial API and implementation + +target_sources(library_test PRIVATE + arithmetic_expression_test.cpp + ca_constant_test.cpp + ca_expr_list_test.cpp + ca_function_test.cpp + ca_operator_test.cpp + ca_string_test.cpp + ca_symbol_attribute_test.cpp + ca_symbol_test.cpp + ca_var_sym_test.cpp + character_expression_test.cpp + expr_mocks.h + logical_expression_test.cpp +) + diff --git a/parser_library/test/lexing/CMakeLists.txt b/parser_library/test/lexing/CMakeLists.txt new file mode 100644 index 000000000..cf6cf41e1 --- /dev/null +++ b/parser_library/test/lexing/CMakeLists.txt @@ -0,0 +1,16 @@ +# Copyright (c) 2021 Broadcom. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Broadcom, Inc. - initial API and implementation + +target_sources(library_test PRIVATE + lexer_test.cpp +) + diff --git a/parser_library/test/lexing/lexer_test.cpp b/parser_library/test/lexing/lexer_test.cpp index 44ebcf25b..1cd73ef30 100644 --- a/parser_library/test/lexing/lexer_test.cpp +++ b/parser_library/test/lexing/lexer_test.cpp @@ -12,7 +12,6 @@ * Broadcom, Inc. - initial API and implementation */ -#include #include #include diff --git a/parser_library/test/lsp/CMakeLists.txt b/parser_library/test/lsp/CMakeLists.txt new file mode 100644 index 000000000..98ebaa4bd --- /dev/null +++ b/parser_library/test/lsp/CMakeLists.txt @@ -0,0 +1,23 @@ +# Copyright (c) 2021 Broadcom. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Broadcom, Inc. - initial API and implementation + +target_sources(library_test PRIVATE + analyzer_fixture.h + lsp_context_copy_in_macro_test.cpp + lsp_context_macro_documentation_test.cpp + lsp_context_macro_in_opencode_test.cpp + lsp_context_nested_macro_test.cpp + lsp_context_ord_sym_test.cpp + lsp_context_seq_sym_test.cpp + lsp_context_var_sym_test.cpp + lsp_features_test.cpp +) diff --git a/parser_library/test/parsing/CMakeLists.txt b/parser_library/test/parsing/CMakeLists.txt new file mode 100644 index 000000000..7d8ee07d7 --- /dev/null +++ b/parser_library/test/parsing/CMakeLists.txt @@ -0,0 +1,19 @@ +# Copyright (c) 2021 Broadcom. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Broadcom, Inc. - initial API and implementation + +target_sources(library_test PRIVATE + parser_model_test.cpp + parser_range_test.cpp + parser_test.cpp + string_test.cpp +) + diff --git a/parser_library/test/processing/CMakeLists.txt b/parser_library/test/processing/CMakeLists.txt new file mode 100644 index 000000000..6f9eb7c36 --- /dev/null +++ b/parser_library/test/processing/CMakeLists.txt @@ -0,0 +1,24 @@ +# Copyright (c) 2021 Broadcom. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Broadcom, Inc. - initial API and implementation + +target_sources(library_test PRIVATE + ca_instr_test.cpp + copy_test.cpp + dc_test.cpp + equ_test.cpp + loctr_test.cpp + lookahead_test.cpp + occurence_collector_test.cpp + opsyn_test.cpp + org_test.cpp +) + diff --git a/parser_library/test/semantics/CMakeLists.txt b/parser_library/test/semantics/CMakeLists.txt new file mode 100644 index 000000000..83f58cca4 --- /dev/null +++ b/parser_library/test/semantics/CMakeLists.txt @@ -0,0 +1,17 @@ +# Copyright (c) 2021 Broadcom. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Broadcom, Inc. - initial API and implementation + +target_sources(library_test PRIVATE + concatenation_test.cpp + highlighting_test.cpp + operand_test.cpp +) diff --git a/parser_library/test/workspace/CMakeLists.txt b/parser_library/test/workspace/CMakeLists.txt new file mode 100644 index 000000000..ced5a74ac --- /dev/null +++ b/parser_library/test/workspace/CMakeLists.txt @@ -0,0 +1,21 @@ +# Copyright (c) 2021 Broadcom. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Broadcom, Inc. - initial API and implementation + +target_sources(library_test PRIVATE + diags_suppress_test.cpp + empty_configs.h + extension_handling_test.cpp + load_config_test.cpp + text_synchronization_test.cpp + workspace_test.cpp +) + diff --git a/parser_library/test/workspace/empty_configs.h b/parser_library/test/workspace/empty_configs.h index f506d476f..3bdb70f6f 100644 --- a/parser_library/test/workspace/empty_configs.h +++ b/parser_library/test/workspace/empty_configs.h @@ -17,8 +17,10 @@ #include -inline std::string pgm_conf_name = (std::filesystem::path(".hlasmplugin") / "pgm_conf.json").string(); -inline std::string proc_grps_name = (std::filesystem::path(".hlasmplugin") / "proc_grps.json").string(); +#include "utils/path.h" + +inline std::string pgm_conf_name = hlasm_plugin::utils::path::join(".hlasmplugin", "pgm_conf.json").string(); +inline std::string proc_grps_name = hlasm_plugin::utils::path::join(".hlasmplugin", "proc_grps.json").string(); inline std::string empty_pgm_conf = R"({ "pgms": []})"; inline std::string empty_proc_grps = R"({ "pgroups": []})"; diff --git a/parser_library/test/workspace/extension_handling_test.cpp b/parser_library/test/workspace/extension_handling_test.cpp index c85513c36..f24d1e98a 100644 --- a/parser_library/test/workspace/extension_handling_test.cpp +++ b/parser_library/test/workspace/extension_handling_test.cpp @@ -14,19 +14,15 @@ #include "gtest/gtest.h" +#include "utils/platform.h" #include "workspaces/file_manager_impl.h" #include "workspaces/wildcard.h" #include "workspaces/workspace.h" using namespace hlasm_plugin::parser_library::workspaces; -#ifdef _WIN32 -const std::string lib_path = "lib\\"; -const std::string lib_path2 = "lib2\\"; -#else -const std::string lib_path = "lib/"; -const std::string lib_path2 = "lib2/"; -#endif +const std::string lib_path = hlasm_plugin::utils::platform::is_windows() ? "lib\\" : "lib/"; +const std::string lib_path2 = hlasm_plugin::utils::platform::is_windows() ? "lib2\\" : "lib2/"; class file_manager_extension_mock : public file_manager_impl { diff --git a/parser_library/test/workspace/load_config_test.cpp b/parser_library/test/workspace/load_config_test.cpp index ad5719b29..f7b0323e5 100644 --- a/parser_library/test/workspace/load_config_test.cpp +++ b/parser_library/test/workspace/load_config_test.cpp @@ -19,12 +19,14 @@ #include "gtest/gtest.h" #include "empty_configs.h" +#include "utils/platform.h" #include "workspaces/file_impl.h" #include "workspaces/file_manager_impl.h" #include "workspaces/workspace.h" using namespace hlasm_plugin::parser_library; using namespace hlasm_plugin::parser_library::workspaces; +using hlasm_plugin::utils::platform::is_windows; class file_proc_grps : public file_impl { @@ -41,8 +43,8 @@ class file_proc_grps : public file_impl virtual bool update_and_get_bad() override { return false; } -#ifdef _WIN32 - std::string file = R"({ + std::string file = is_windows() ? + R"({ "pgroups": [ { "name": "P1", @@ -77,9 +79,8 @@ class file_proc_grps : public file_impl ] } ] -})"; -#else - std::string file = R"({ +})" + : R"({ "pgroups": [ { "name": "P1", @@ -115,7 +116,6 @@ class file_proc_grps : public file_impl } ] })"; -#endif //_WIN32 }; class file_pgm_conf : public file_impl @@ -133,8 +133,7 @@ class file_pgm_conf : public file_impl virtual bool update_and_get_bad() override { return false; } -#if _WIN32 - std::string file = R"({ + std::string file = is_windows() ? R"({ "pgms": [ { "program": "pgm1", @@ -145,9 +144,8 @@ class file_pgm_conf : public file_impl "pgroup": "P2" } ] -})"; -#else - std::string file = R"({ +})" + : R"({ "pgms": [ { "program": "pgm1", @@ -159,7 +157,6 @@ class file_pgm_conf : public file_impl } ] })"; -#endif }; class file_manager_proc_grps_test : public file_manager_impl @@ -193,16 +190,18 @@ TEST(workspace, load_config_synthetic) auto& pg = ws.get_proc_grp("P1"); EXPECT_EQ("P1", pg.name()); -#ifdef _WIN32 - std::string expected[4] { "C:\\Users\\Desktop\\ASLib\\", - "test_proc_grps_uri\\lib\\", - "test_proc_grps_uri\\libs\\lib2\\", - "test_proc_grps_uri\\" }; -#else - std::string expected[4] { - "/home/user/ASLib/", "test_proc_grps_uri/lib/", "test_proc_grps_uri/libs/lib2/", "test_proc_grps_uri/" - }; -#endif // _WIN32 + auto expected = []() -> std::array { + if (is_windows()) + return { "C:\\Users\\Desktop\\ASLib\\", + "test_proc_grps_uri\\lib\\", + "test_proc_grps_uri\\libs\\lib2\\", + "test_proc_grps_uri\\" }; + else + return { + "/home/user/ASLib/", "test_proc_grps_uri/lib/", "test_proc_grps_uri/libs/lib2/", "test_proc_grps_uri/" + }; + }(); + EXPECT_EQ(std::size(expected), pg.libraries().size()); for (size_t i = 0; i < std::min(std::size(expected), pg.libraries().size()); ++i) { @@ -213,13 +212,16 @@ TEST(workspace, load_config_synthetic) auto& pg2 = ws.get_proc_grp("P2"); EXPECT_EQ("P2", pg2.name()); -#ifdef _WIN32 - std::string expected2[3] { - "C:\\Users\\Desktop\\ASLib\\", "test_proc_grps_uri\\P2lib\\", "test_proc_grps_uri\\P2libs\\libb\\" - }; -#else - std::string expected2[3] { "/home/user/ASLib/", "test_proc_grps_uri/P2lib/", "test_proc_grps_uri/P2libs/libb/" }; -#endif // _WIN32 + + auto expected2 = []() -> std::array { + if (is_windows()) + return { + "C:\\Users\\Desktop\\ASLib\\", "test_proc_grps_uri\\P2lib\\", "test_proc_grps_uri\\P2libs\\libb\\" + }; + else + return { "/home/user/ASLib/", "test_proc_grps_uri/P2lib/", "test_proc_grps_uri/P2libs/libb/" }; + }(); + EXPECT_EQ(std::size(expected2), pg2.libraries().size()); for (size_t i = 0; i < std::min(std::size(expected2), pg2.libraries().size()); ++i) { @@ -230,11 +232,9 @@ TEST(workspace, load_config_synthetic) // test of pgm_conf and workspace::get_proc_grp_by_program -#ifdef _WIN32 - auto& pg3 = ws.get_proc_grp_by_program("test_proc_grps_uri\\pgm1"); -#else - auto& pg3 = ws.get_proc_grp_by_program("test_proc_grps_uri/pgm1"); -#endif + auto& pg3 = is_windows() ? ws.get_proc_grp_by_program("test_proc_grps_uri\\pgm1") + : ws.get_proc_grp_by_program("test_proc_grps_uri/pgm1"); + EXPECT_EQ(pg3.libraries().size(), std::size(expected)); for (size_t i = 0; i < std::min(std::size(expected), pg3.libraries().size()); ++i) { @@ -244,11 +244,9 @@ TEST(workspace, load_config_synthetic) } -#ifdef _WIN32 - auto& pg4 = ws.get_proc_grp_by_program("test_proc_grps_uri\\pgms\\anything"); -#else - auto& pg4 = ws.get_proc_grp_by_program("test_proc_grps_uri/pgms/anything"); -#endif + auto& pg4 = is_windows() ? ws.get_proc_grp_by_program("test_proc_grps_uri\\pgms\\anything") + : ws.get_proc_grp_by_program("test_proc_grps_uri/pgms/anything"); + EXPECT_EQ(pg4.libraries().size(), std::size(expected2)); for (size_t i = 0; i < std::min(std::size(expected2), pg4.libraries().size()); ++i) { diff --git a/parser_library/test/workspace/workspace_test.cpp b/parser_library/test/workspace/workspace_test.cpp index c2c1f66c7..b1943850f 100644 --- a/parser_library/test/workspace/workspace_test.cpp +++ b/parser_library/test/workspace/workspace_test.cpp @@ -19,12 +19,15 @@ #include "gtest/gtest.h" +#include "utils/path.h" +#include "utils/platform.h" #include "workspaces/file_impl.h" #include "workspaces/file_manager_impl.h" #include "workspaces/workspace.h" using namespace hlasm_plugin::parser_library; using namespace hlasm_plugin::parser_library::workspaces; +using hlasm_plugin::utils::platform::is_windows; class workspace_test : public diagnosable_impl, public testing::Test { @@ -59,11 +62,12 @@ class workspace_test : public diagnosable_impl, public testing::Test TEST_F(workspace_test, parse_lib_provider) { + using namespace hlasm_plugin::utils; + lib_config config; file_manager_impl file_mngr; - std::string test_wks_path = (std::filesystem::path("test") / "library" / "test_wks").string(); - + std::string test_wks_path = path::join(path::join("test", "library"), "test_wks").string(); workspace ws(test_wks_path, file_mngr, config); @@ -75,17 +79,21 @@ TEST_F(workspace_test, parse_lib_provider) file_mngr.add_processor_file("test\\library\\test_wks\\correct"); - context::hlasm_ctx_ptr ctx_1, ctx_2; lsp::lsp_ctx_ptr lsp_ptr = std::make_shared(); -#if _WIN32 - ws.did_open_file("test\\library\\test_wks\\correct"); - ctx_1 = std::make_shared("test\\library\\test_wks\\correct"); - ctx_2 = std::make_shared("test\\library\\test_wks\\correct"); -#else - ws.did_open_file("test/library/test_wks/correct"); - ctx_1 = std::make_shared("test/library/test_wks/correct"); - ctx_2 = std::make_shared("test/library/test_wks/correct"); -#endif + auto [ctx_1, ctx_2] = [&ws]() { + if (platform::is_windows()) + { + ws.did_open_file("test\\library\\test_wks\\correct"); + return std::make_pair(std::make_shared("test\\library\\test_wks\\correct"), + std::make_shared("test\\library\\test_wks\\correct")); + } + else + { + ws.did_open_file("test/library/test_wks/correct"); + return std::make_pair(std::make_shared("test/library/test_wks/correct"), + std::make_shared("test/library/test_wks/correct")); + } + }(); collect_diags_from_child(file_mngr); EXPECT_EQ(diags().size(), (size_t)0); @@ -219,15 +227,9 @@ class file_with_text : public processor_file_impl virtual bool update_and_get_bad() override { return false; } }; -#ifdef _WIN32 -constexpr const char* faulty_macro_path = "lib\\ERROR"; -constexpr const char* correct_macro_path = "lib\\CORRECT"; -std::string hlasmplugin_folder = ".hlasmplugin\\"; -#else -constexpr const char* faulty_macro_path = "lib/ERROR"; -constexpr const char* correct_macro_path = "lib/CORRECT"; -std::string hlasmplugin_folder = ".hlasmplugin/"; -#endif // _WIN32 +const char* faulty_macro_path = is_windows() ? "lib\\ERROR" : "lib/ERROR"; +const char* correct_macro_path = is_windows() ? "lib\\CORRECT" : "lib/CORRECT"; +std::string hlasmplugin_folder = is_windows() ? ".hlasmplugin\\" : ".hlasmplugin/"; class file_manager_extended : public file_manager_impl { diff --git a/parser_library/test/workspace_manager_test.cpp b/parser_library/test/workspace_manager_test.cpp index 8175d7ed0..13300225d 100644 --- a/parser_library/test/workspace_manager_test.cpp +++ b/parser_library/test/workspace_manager_test.cpp @@ -18,6 +18,8 @@ #include "lib_config.h" #include "message_consumer_mock.h" +#include "utils/path.h" +#include "utils/platform.h" #include "workspace_manager.h" using namespace hlasm_plugin::parser_library; @@ -118,7 +120,7 @@ TEST(workspace_manager, set_message_consumer) ASSERT_EQ(msg_consumer.messages.size(), 1U); msg_consumer.messages.clear(); - mngr.did_open_file((std::filesystem::path("ws1") / "no_workspace_file").string().c_str(), + mngr.did_open_file(hlasm_plugin::utils::path::join("ws1", "no_workspace_file").string().c_str(), 0, error_file_text.c_str(), error_file_text.size()); diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt new file mode 100644 index 000000000..8b196a6ca --- /dev/null +++ b/utils/CMakeLists.txt @@ -0,0 +1,18 @@ +# Copyright (c) 2021 Broadcom. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Broadcom, Inc. - initial API and implementation + +add_library(hlasm_utils STATIC EXCLUDE_FROM_ALL) + +target_include_directories(hlasm_utils PUBLIC include) + +add_subdirectory(include) +add_subdirectory(src) diff --git a/utils/include/CMakeLists.txt b/utils/include/CMakeLists.txt new file mode 100644 index 000000000..2f36e9b05 --- /dev/null +++ b/utils/include/CMakeLists.txt @@ -0,0 +1,13 @@ +# Copyright (c) 2021 Broadcom. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Broadcom, Inc. - initial API and implementation + +add_subdirectory(utils) diff --git a/utils/include/utils/CMakeLists.txt b/utils/include/utils/CMakeLists.txt new file mode 100644 index 000000000..cf8c7b10a --- /dev/null +++ b/utils/include/utils/CMakeLists.txt @@ -0,0 +1,16 @@ +# Copyright (c) 2021 Broadcom. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Broadcom, Inc. - initial API and implementation + +target_sources(hlasm_utils PUBLIC + path.h + platform.h +) diff --git a/utils/include/utils/path.h b/utils/include/utils/path.h new file mode 100644 index 000000000..b06ec2f43 --- /dev/null +++ b/utils/include/utils/path.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2021 Broadcom. + * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Broadcom, Inc. - initial API and implementation + */ + +#ifndef HLASMPLUGIN_UTILS_PATH_H +#define HLASMPLUGIN_UTILS_PATH_H + +#include +#include +#include + +namespace hlasm_plugin::utils::path { + +bool is_relative(const std::filesystem::path&); +bool is_absolute(const std::filesystem::path&); + +std::filesystem::path absolute(const std::filesystem::path& p); +std::filesystem::path join(const std::filesystem::path& left, const std::filesystem::path& right); +std::filesystem::path lexically_normal(const std::filesystem::path& p); +std::filesystem::path lexically_relative(const std::filesystem::path& p, std::string q); +std::filesystem::path filename(const std::filesystem::path& p); +bool equal(const std::filesystem::path& left, const std::filesystem::path& right); + +enum class list_directory_rc +{ + done, + not_exists, + not_a_directory, + other_failure, +}; + +list_directory_rc list_directory_regular_files( + const std::filesystem::path& d, std::function h); +} // namespace hlasm_plugin::utils::path + +#endif diff --git a/cmake/try_filesystem_link.cpp b/utils/include/utils/platform.h similarity index 63% rename from cmake/try_filesystem_link.cpp rename to utils/include/utils/platform.h index 6e0c1afea..64db5bbb1 100644 --- a/cmake/try_filesystem_link.cpp +++ b/utils/include/utils/platform.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 Broadcom. + * Copyright (c) 2021 Broadcom. * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. * * This program and the accompanying materials are made @@ -12,12 +12,11 @@ * Broadcom, Inc. - initial API and implementation */ -#include +#ifndef HLASMPLUGIN_UTILS_PLATFORM_H +#define HLASMPLUGIN_UTILS_PLATFORM_H +namespace hlasm_plugin::utils::platform { +bool is_windows(); +} // namespace hlasm_plugin::utils::platform -int main() -{ - std::filesystem::path p("/home/"); - p.stem(); - return 0; -} +#endif diff --git a/dummy/CMakeLists.txt b/utils/src/CMakeLists.txt similarity index 53% rename from dummy/CMakeLists.txt rename to utils/src/CMakeLists.txt index 4c1a16f71..5850b325d 100644 --- a/dummy/CMakeLists.txt +++ b/utils/src/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2019 Broadcom. +# Copyright (c) 2021 Broadcom. # The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. # # This program and the accompanying materials are made @@ -10,18 +10,12 @@ # Contributors: # Broadcom, Inc. - initial API and implementation -cmake_minimum_required (VERSION 3.10) +target_sources(hlasm_utils PRIVATE + platform.cpp +) -PROJECT(dummy) - -# compile sources to the executable -add_executable(dummy - ${PROJECT_SOURCE_DIR}/src/main.cpp - ) - -# link executable with libraries -target_link_libraries(dummy parser_library) - -if(BUILD_TESTING) - add_dependencies(dummy library_tests_copy) +if(EMSCRIPTEN) + target_sources(hlasm_utils PRIVATE emscripten_path.cpp) +else() + target_sources(hlasm_utils PRIVATE native_path.cpp) endif() diff --git a/utils/src/emscripten_path.cpp b/utils/src/emscripten_path.cpp new file mode 100644 index 000000000..747101a99 --- /dev/null +++ b/utils/src/emscripten_path.cpp @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2021 Broadcom. + * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Broadcom, Inc. - initial API and implementation + */ +#include +#include + +#include + +#include "utils/path.h" +#include "utils/platform.h" + +namespace hlasm_plugin::utils::path { +using utils::platform::is_windows; + +namespace { +std::string make_windows_preferred(std::string s) +{ + std::replace(s.begin(), s.end(), '/', '\\'); + return s; +} +std::string make_linux_preferred(std::string s) +{ + std::replace(s.begin(), s.end(), '\\', '/'); + return s; +} +} // namespace + +bool is_relative(const std::filesystem::path& p) { return !is_absolute(p); } +bool is_absolute(const std::filesystem::path& p) +{ + // emscripten implementation seems to be broken on windows + if (is_windows()) + { + const auto path = p.string(); + return (path.size() >= 2 && std::isalpha(path[0]) && path[1] == ':') // C:... + || (path.size() >= 4 && path[0] == '\\' && path[1] == '\\'); // \\...\....; + } + else + return p.is_absolute(); +} + +std::filesystem::path absolute(const std::filesystem::path& p) +{ + if (p.empty() || is_absolute(p)) + return p; + + return join(std::filesystem::current_path(), p); +} + +std::filesystem::path join(const std::filesystem::path& left, const std::filesystem::path& right) +{ + // emscripten implementation seems to be broken on windows + if (is_windows()) + { + if (is_absolute(right)) + return right; + return std::filesystem::path(make_windows_preferred((std::filesystem::path(make_linux_preferred(left.string())) + / std::filesystem::path(make_linux_preferred(right.string()))) + .string())); + } + else + return left / right; +} + +std::filesystem::path lexically_normal(const std::filesystem::path& p) +{ + // emscripten implementation seems to be broken on windows + if (is_windows()) + { + auto p_ = make_linux_preferred(p.string()); + const bool unc = p_.size() >= 2 && p_[0] == '/' && p_[1] == '/'; + return std::filesystem::path( + (unc ? "\\" : "") + make_windows_preferred(std::filesystem::path(p_).lexically_normal().string())); + } + else + return p.lexically_normal(); +} + + +std::filesystem::path lexically_relative(const std::filesystem::path& p, std::string q) +{ + // emscripten implementation seems to be broken on windows + if (is_windows()) + return std::filesystem::path(make_windows_preferred(std::filesystem::path(make_linux_preferred(p.string())) + .lexically_relative(make_linux_preferred(std::move(q))) + .string())); + else + return p.lexically_relative(q); +} + +std::filesystem::path filename(const std::filesystem::path& p) +{ + // emscripten implementation seems to be broken on windows + if (is_windows()) + return std::filesystem::path(make_linux_preferred(p.string())).filename(); + else + return p.filename(); +} + +bool equal(const std::filesystem::path& left, const std::filesystem::path& right) +{ + // emscripten implementation seems to be broken on windows + if (is_windows()) + return std::filesystem::path(make_linux_preferred(left.string())) + == std::filesystem::path(make_linux_preferred(right.string())); + else + return left == right; +} + +class directory_listing +{ + std::string buffer; + std::function handler; + + static intptr_t get_buffer(intptr_t this_, int size) + { + auto ptr = reinterpret_cast(this_); + ptr->buffer.resize(size); + return reinterpret_cast(ptr->buffer.data()); + } + + static void commit_buffer(intptr_t this_) + { + auto ptr = reinterpret_cast(this_); + ptr->handler(ptr->buffer); + } + +public: + directory_listing(std::function h) + : handler(h) + { + static thread_local bool registered = false; + if (!registered) + { + registered = true; + emscripten::function("directory_listing_get_buffer", &directory_listing::get_buffer); + emscripten::function("directory_listing_commit_buffer", &directory_listing::commit_buffer); + } + } + + list_directory_rc run(const std::filesystem::path& d) + { + auto path = path::absolute(d).string(); + + int result = EM_ASM_INT( + { + let rc = 0; + try + { + const dir_name = UTF8ToString($0); + const fs = require('fs'); + const path = require('path'); + + fs.accessSync(dir_name); + + rc = 1; + + const dir = fs.opendirSync(dir_name); + + rc = 2; + + let de = null; + while (de = dir.readSync()) + { + if (de.isFile()) + { + const file_name = path.join(dir_name, de.name); + const buf_len = lengthBytesUTF8(file_name); + const ptr = Module.directory_listing_get_buffer($1, buf_len); + stringToUTF8(file_name, ptr, buf_len + 1); + Module.directory_listing_commit_buffer($1); + } + } + rc = 3; + } + catch (e) + {} + return rc; + }, + (intptr_t)path.c_str(), + (intptr_t)this); + + switch (result) + { + case 0: + return list_directory_rc::not_exists; + case 1: + return list_directory_rc::not_a_directory; + case 2: + return list_directory_rc::other_failure; + case 3: + return list_directory_rc::done; + default: + throw std::logic_error("unreachable"); + } + } +}; + +list_directory_rc list_directory_regular_files( + const std::filesystem::path& d, std::function h) +{ + // directory listing seems broken everywhere + directory_listing l(h); + return l.run(d); +} + +} // namespace hlasm_plugin::utils::path diff --git a/utils/src/native_path.cpp b/utils/src/native_path.cpp new file mode 100644 index 000000000..e0111f39f --- /dev/null +++ b/utils/src/native_path.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2021 Broadcom. + * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Broadcom, Inc. - initial API and implementation + */ +#include "utils/path.h" +#include "utils/platform.h" + +namespace hlasm_plugin::utils::path { + +bool is_relative(const std::filesystem::path& p) { return !is_absolute(p); } +bool is_absolute(const std::filesystem::path& p) { return p.is_absolute(); } + +std::filesystem::path absolute(const std::filesystem::path& p) { return std::filesystem::absolute(p); } + +std::filesystem::path join(const std::filesystem::path& left, const std::filesystem::path& right) +{ + return left / right; +} + +std::filesystem::path lexically_normal(const std::filesystem::path& p) { return p.lexically_normal(); } + +std::filesystem::path lexically_relative(const std::filesystem::path& p, std::string q) +{ + return p.lexically_relative(q); +} + +std::filesystem::path filename(const std::filesystem::path& p) { return p.filename(); } + +bool equal(const std::filesystem::path& left, const std::filesystem::path& right) { return left == right; } + +list_directory_rc list_directory_regular_files( + const std::filesystem::path& d, std::function h) +{ + std::filesystem::directory_entry dir(d); + + if (!dir.exists()) + return list_directory_rc::not_exists; + + if (!dir.is_directory()) + return list_directory_rc::not_a_directory; + + try + { + std::filesystem::directory_iterator it(dir); + + for (auto& p : it) + { + if (p.is_regular_file()) + { + h(p.path()); + } + } + } + catch (const std::filesystem::filesystem_error&) + { + return list_directory_rc::other_failure; + } + + return list_directory_rc::done; +} + +} // namespace hlasm_plugin::utils::path diff --git a/utils/src/platform.cpp b/utils/src/platform.cpp new file mode 100644 index 000000000..f7ec2c96c --- /dev/null +++ b/utils/src/platform.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2021 Broadcom. + * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Broadcom, Inc. - initial API and implementation + */ + +#include "utils/platform.h" + +#ifdef __EMSCRIPTEN__ +# include + +# include +#endif + +namespace hlasm_plugin::utils::platform { + +bool is_windows() +{ +#ifdef _WIN32 + return true; +#elif __EMSCRIPTEN__ + // clang-format off + static const bool windows_flag = []() { return EM_ASM_INT({ return process.platform === "win32" ? 1 : 0; }); }(); + // clang-format on + return windows_flag; +#else + return false; +#endif +} + +} // namespace hlasm_plugin::utils::platform