Provide the CMake install the necessarily information (and flags) to hand Windows DLL and add Windows installation tests (#1457)

* This gives the CMake install the necessarily information (and flags) to know
whether we have a Windows DLL and in such cases how to handle the linkage.
This commit is contained in:
Daniel Lemire 2021-02-26 16:17:05 -05:00 committed by GitHub
parent 72c40be553
commit 9577c54999
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 216 additions and 64 deletions

View File

@ -26,4 +26,4 @@ jobs:
cd build &&
cmake -DCMAKE_CXX_FLAGS="-Werror=old-style-cast -pedantic -Wpedantic" -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_BUILD_STATIC=ON -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
cmake --build . --target checkperf &&
ctest --output-on-failure -R checkperf
ctest --output-on-failure -R checkperf

View File

@ -27,8 +27,7 @@ jobs:
cmake -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_BUILD_STATIC=ON -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
cmake --build . &&
ctest -j --output-on-failure -LE explicitonly &&
make install &&
cmake --install . &&
echo -e '#include <simdjson.h>\nint main(int argc,char**argv) {simdjson::dom::parser parser;simdjson::dom::element tweets = parser.load(argv[1]); }' > tmp.cpp && c++ -Idestination/include -Ldestination/lib -std=c++17 -Wl,-rpath,destination/lib -o linkandrun tmp.cpp -lsimdjson && ./linkandrun jsonexamples/twitter.json &&
mkdir testfindpackage &&
cd testfindpackage &&
echo -e 'cmake_minimum_required(VERSION 3.1)\nproject(simdjsontester)\nset(CMAKE_CXX_STANDARD 17)\nfind_package(simdjson REQUIRED)'> CMakeLists.txt && mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX:PATH=../destination .. && cmake --build .
cd ../tests/installation_tests/find &&
mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX:PATH=../../../build/destination .. && cmake --build .

View File

@ -15,24 +15,28 @@ jobs:
! contains(toJSON(github.event.commits.*.message), '[skip github]')
name: windows-vs16
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
include:
- {gen: Visual Studio 16 2019, arch: x64, static: ON}
- {gen: Visual Studio 16 2019, arch: x64, static: OFF}
steps:
- uses: actions/checkout@v2
- uses: actions/cache@v2
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
- name: 'Run CMake with VS16'
uses: lukka/run-cmake@v2
with:
cmakeListsOrSettingsJson: CMakeListsTxtAdvanced
cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt'
buildDirectory: "${{ github.workspace }}/../../_temp/windows"
cmakeBuildType: Release
buildWithCMake: true
cmakeAppendedArgs: -DSIMDJSON_COMPETITION=OFF
buildWithCMakeArgs: --config Release
- name: 'Run CTest'
run: ctest -C Release -LE explicitonly --output-on-failure
working-directory: "${{ github.workspace }}/../../_temp/windows"
- name: checkout
uses: actions/checkout@v2
- name: Configure
run: |
cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -DSIMDJSON_COMPETITION=OFF -DSIMDJSON_BUILD_STATIC=${{matrix.static}} -B build
- name: Build
run: cmake --build build --config Release
- name: Run tests
run: |
cd build
ctest -C Release -LE explicitonly --output-on-failure
- name: Install
run: |
cmake --install build --config Release
- name: Test Installation
run: |
cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -B build_install_test tests/installation_tests/find
cmake --build build_install_test --config Release

View File

@ -21,17 +21,17 @@ jobs:
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
- name: 'Run CMake with VS16'
uses: lukka/run-cmake@v2
- name: 'Run CMake with VS16 Clang'
uses: lukka/run-cmake@v3
with:
cmakeListsOrSettingsJson: CMakeListsTxtAdvanced
cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt'
buildDirectory: "${{ github.workspace }}/../../_temp/windows"
cmakeBuildType: Release
cmakeBuildType: Release
buildWithCMake: true
cmakeAppendedArgs: -T ClangCL -DSIMDJSON_COMPETITION=OFF -DSIMDJSON_BUILD_STATIC=ON
buildWithCMakeArgs: --config Release
buildWithCMakeArgs: --config Release
- name: 'Run CTest'
run: ctest -C Release -LE explicitonly --output-on-failure
working-directory: "${{ github.workspace }}/../../_temp/windows"
run: ctest -C Release -LE explicitonly --output-on-failure
working-directory: "${{ github.workspace }}/../../_temp/windows"

View File

@ -27,12 +27,25 @@ jobs:
cmakeListsOrSettingsJson: CMakeListsTxtAdvanced
cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt'
buildDirectory: "${{ github.workspace }}/../../_temp/windows"
cmakeBuildType: Release
cmakeBuildType: Release
buildWithCMake: true
cmakeAppendedArgs: -G Ninja -DSIMDJSON_COMPETITION=OFF -DSIMDJSON_BUILD_STATIC=ON
buildWithCMakeArgs: --config Release
buildWithCMakeArgs: --config Release
- name: 'Run CTest'
run: ctest -C Release -LE explicitonly --output-on-failure
run: ctest -C Release -LE explicitonly --output-on-failure
working-directory: "${{ github.workspace }}/../../_temp/windows"
- name: 'Install with CMake'
uses: lukka/run-cmake@v3
with:
cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt'
buildWithCMakeArgs: '--target install'
- name: 'Test Installation with CMake'
uses: lukka/run-cmake@v3
with:
cmakeListsOrSettingsJson: CMakeListsTxtAdvanced
cmakeListsTxtPath: '${{ github.workspace }}/tests/installation_tests/find/CMakeLists.txt'
cmakeBuildType: Release
buildWithCMake: true
buildDirectory: '${{ github.workspace }}/tests/installation_tests/find/buildDirectory'
cmakeAppendedArgs: -G Ninja
buildWithCMakeArgs: '--config Release --verbose'

View File

@ -19,18 +19,18 @@ jobs:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
- name: 'Run CMake with VS16'
uses: lukka/run-cmake@v2
uses: lukka/run-cmake@v3
with:
cmakeListsOrSettingsJson: CMakeListsTxtAdvanced
cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt'
buildDirectory: "${{ github.workspace }}/../../_temp/windows"
cmakeBuildType: Release
cmakeBuildType: Release
buildWithCMake: true
cmakeGenerator: VS16Win64
cmakeGenerator: VS16Win64
cmakeAppendedArgs: -DSIMDJSON_COMPETITION=OFF -DSIMDJSON_EXCEPTIONS=OFF
buildWithCMakeArgs: --config Release
buildWithCMakeArgs: --config Release
- name: 'Run CTest'
run: ctest -C Release -LE explicitonly --output-on-failure
run: ctest -C Release -LE explicitonly --output-on-failure
working-directory: "${{ github.workspace }}/../../_temp/windows"

View File

@ -16,14 +16,16 @@ struct yyjson_base {
switch (yyjson_get_subtype(val)) {
case YYJSON_SUBTYPE_UINT:
return yyjson_get_uint(val);
return double(yyjson_get_uint(val));
case YYJSON_SUBTYPE_SINT:
return yyjson_get_sint(val);
return double(yyjson_get_sint(val));
case YYJSON_SUBTYPE_REAL:
return yyjson_get_real(val);
default:
SIMDJSON_UNREACHABLE();
}
SIMDJSON_UNREACHABLE();
return 0.0; // unreachable
}
bool run(yyjson_doc *doc, std::vector<point> &result) {

View File

@ -16,14 +16,16 @@ struct yyjson_base {
switch (yyjson_get_subtype(val)) {
case YYJSON_SUBTYPE_UINT:
return yyjson_get_uint(val);
return double(yyjson_get_uint(val));
case YYJSON_SUBTYPE_SINT:
return yyjson_get_sint(val);
return double(yyjson_get_sint(val));
case YYJSON_SUBTYPE_REAL:
return yyjson_get_real(val);
default:
SIMDJSON_UNREACHABLE();
}
SIMDJSON_UNREACHABLE();
return 0.0; // unreachable
}
bool run(yyjson_doc *doc, std::vector<point> &result) {

View File

@ -52,6 +52,11 @@ else()
option(SIMDJSON_USE_LIBCPP "Use the libc++ library" OFF)
endif()
if(MSVC AND NOT(SIMDJSON_BUILD_STATIC))
# This will require special handling.
set(SIMDJSON_WINDOWS_DLL TRUE)
endif()
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/tools/cmake")
# We compile tools, tests, etc. with C++ 17. Override yourself if you need on a target.

View File

@ -1,4 +1,4 @@
if(NOT SIMDJSON_LEGACY_VISUAL_STUDIO)
if(NOT SIMDJSON_LEGACY_VISUAL_STUDIO AND NOT SIMDJSON_WINDOWS_DLL)
option(ENABLE_FUZZING "enable building the fuzzers" ON)
else()
option(ENABLE_FUZZING "enable building the fuzzers" OFF)

View File

@ -149,15 +149,43 @@ constexpr size_t DEFAULT_MAX_DEPTH = 1024;
#if defined(SIMDJSON_VISUAL_STUDIO)
/**
* Windows users need to do some extra work when building
* or using a dynamic library (DLL). When building, we need
* to set SIMDJSON_DLLIMPORTEXPORT to __declspec(dllexport).
* When *using* the DLL, the user needs to set
* SIMDJSON_DLLIMPORTEXPORT __declspec(dllimport).
*
* Static libraries not need require such work.
*
* It does not matter here whether you are using
* the regular visual studio or clang under visual
* studio.
* studio, you still need to handle these issues.
*
* Non-Windows sytems do not have this complexity.
*/
#if SIMDJSON_USING_LIBRARY
#if SIMDJSON_BUILDING_WINDOWS_DYNAMIC_LIBRARY
// We set SIMDJSON_BUILDING_WINDOWS_DYNAMIC_LIBRARY when we build a DLL under Windows.
// It should never happen that both SIMDJSON_BUILDING_WINDOWS_DYNAMIC_LIBRARY and
// SIMDJSON_USING_WINDOWS_DYNAMIC_LIBRARY are set.
#define SIMDJSON_DLLIMPORTEXPORT __declspec(dllexport)
#elif SIMDJSON_USING_WINDOWS_DYNAMIC_LIBRARY
// Windows user who call a dynamic library should set SIMDJSON_USING_WINDOWS_DYNAMIC_LIBRARY to 1.
#define SIMDJSON_DLLIMPORTEXPORT __declspec(dllimport)
#else
#define SIMDJSON_DLLIMPORTEXPORT __declspec(dllexport)
// We assume by default static linkage
#define SIMDJSON_DLLIMPORTEXPORT
#endif
/**
* Workaround for the vcpkg package manager. Only vcpkg should
* ever touch the next line. The SIMDJSON_USING_LIBRARY macro is otherwise unused.
*/
#if SIMDJSON_USING_LIBRARY
#define SIMDJSON_DLLIMPORTEXPORT __declspec(dllimport)
#endif
/**
* End of workaround for the vcpkg package manager.
*/
#else
#define SIMDJSON_DLLIMPORTEXPORT
#endif

View File

@ -46,13 +46,13 @@ if (Python3_Interpreter_FOUND AND (NOT WIN32))
##
# This is used by "make amalgamate" to update the original source files.
# You can invoke it as cmake --build . --target amalgamate
# You can invoke it as cmake --build . --target amalgamate
# We obviously don't do
# this if source and generated files are in the same place--cmake gets mad!
if (NOT (${CMAKE_CURRENT_SOURCE_DIR} STREQUAL ${CMAKE_CURRENT_BINARY_DIR}))
add_custom_target(amalgamate)
add_custom_command(TARGET amalgamate
# We don't want CMake to know that it is writing to the source directory. No magic
# We don't want CMake to know that it is writing to the source directory. No magic
# file regeneration in the source directory without the user's knowledge.
# OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/simdjson.cpp ${CMAKE_CURRENT_SOURCE_DIR}/simdjson.h ${CMAKE_CURRENT_SOURCE_DIR}/amalgamate_demo.cpp ${CMAKE_CURRENT_SOURCE_DIR}/README.md
COMMAND ${CMAKE_COMMAND} -E copy ${SINGLEHEADER_FILES} ${CMAKE_CURRENT_SOURCE_DIR}
@ -117,20 +117,29 @@ target_link_libraries(simdjson-singleheader-source INTERFACE simdjson-singlehead
include(${PROJECT_SOURCE_DIR}/cmake/add_compile_only_test.cmake)
#
# Test the generated simdjson.cpp/simdjson.h using the generated amalgamate_demo.cpp
#
if(NOT SIMDJSON_LEGACY_VISUAL_STUDIO)
# Under Windows you should not mix static and dynamic. Pick one. The following test is static.
if(NOT SIMDJSON_LEGACY_VISUAL_STUDIO AND NOT SIMDJSON_WINDOWS_DLL)
add_executable(amalgamate_demo $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/amalgamate_demo.cpp>)
target_link_libraries(amalgamate_demo simdjson-singleheader-include-source simdjson-internal-flags)
add_test(amalgamate_demo amalgamate_demo ${EXAMPLE_JSON} ${EXAMPLE_NDJSON})
MESSAGE( STATUS "Including amalgamate_demo test. ${SIMDJSON_WINDOWS_DLL}" )
else()
MESSAGE( STATUS "You either have an old Visual Studio or you are building a DLL, amalgamate_demo test disabled." )
endif()
install(FILES simdjson.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
if(NOT SIMDJSON_LEGACY_VISUAL_STUDIO)
# Under Windows you should not mix static and dynamic. Pick one. The following test is static.
if(NOT SIMDJSON_LEGACY_VISUAL_STUDIO AND NOT SIMDJSON_WINDOWS_DLL)
add_library(simdjson-singleheader STATIC "")
target_link_libraries(simdjson-singleheader simdjson-singleheader-source simdjson-internal-flags)
add_compile_only_test(simdjson-singleheader)
MESSAGE( STATUS "Including simdjson-singleheader test." )
else()
MESSAGE( STATUS "You either have an old Visual Studio or you are building a DLL, simdjson-singleheader test disabled." )
endif()
#
@ -148,9 +157,15 @@ if (NOT (${CMAKE_CURRENT_SOURCE_DIR} STREQUAL ${CMAKE_CURRENT_BINARY_DIR}))
target_sources(simdjson-singleheader-source-direct-from-repository INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(simdjson-singleheader-source-direct-from-repository INTERFACE simdjson-singleheader-include-source-direct-from-repository)
if(NOT SIMDJSON_LEGACY_VISUAL_STUDIO)
# If you want to use simdjson as a DLL under Windows, it makes little sense to run the following static test. Furthermore
# when a DLL was built, we pass SIMDJSON_USING_WINDOWS_DYNAMIC_LIBRARY=1 within the simdjson-flags so that static compilation
# breaks. Simply put: under Windows you should not mix static and dynamic. Pick one.
if(NOT SIMDJSON_LEGACY_VISUAL_STUDIO AND NOT SIMDJSON_WINDOWS_DLL)
add_executable(amalgamate_demo_direct_from_repository ${CMAKE_CURRENT_SOURCE_DIR}/amalgamate_demo.cpp)
target_link_libraries(amalgamate_demo_direct_from_repository simdjson-singleheader-include-source-direct-from-repository simdjson-internal-flags)
add_test(amalgamate_demo_direct_from_repository amalgamate_demo_direct_from_repository ${EXAMPLE_JSON} ${EXAMPLE_NDJSON})
MESSAGE( STATUS "Including amalgamate_demo_direct_from_repository test." )
else()
MESSAGE( STATUS "You either have an old Visual Studio or you are building a DLL, amalgamate_demo test disabled." )
endif()
endif()
endif()

View File

@ -405,15 +405,43 @@ constexpr size_t DEFAULT_MAX_DEPTH = 1024;
#if defined(SIMDJSON_VISUAL_STUDIO)
/**
* Windows users need to do some extra work when building
* or using a dynamic library (DLL). When building, we need
* to set SIMDJSON_DLLIMPORTEXPORT to __declspec(dllexport).
* When *using* the DLL, the user needs to set
* SIMDJSON_DLLIMPORTEXPORT __declspec(dllimport).
*
* Static libraries not need require such work.
*
* It does not matter here whether you are using
* the regular visual studio or clang under visual
* studio.
* studio, you still need to handle these issues.
*
* Non-Windows sytems do not have this complexity.
*/
#if SIMDJSON_USING_LIBRARY
#if SIMDJSON_BUILDING_WINDOWS_DYNAMIC_LIBRARY
// We set SIMDJSON_BUILDING_WINDOWS_DYNAMIC_LIBRARY when we build a DLL under Windows.
// It should never happen that both SIMDJSON_BUILDING_WINDOWS_DYNAMIC_LIBRARY and
// SIMDJSON_USING_WINDOWS_DYNAMIC_LIBRARY are set.
#define SIMDJSON_DLLIMPORTEXPORT __declspec(dllexport)
#elif SIMDJSON_USING_WINDOWS_DYNAMIC_LIBRARY
// Windows user who call a dynamic library should set SIMDJSON_USING_WINDOWS_DYNAMIC_LIBRARY to 1.
#define SIMDJSON_DLLIMPORTEXPORT __declspec(dllimport)
#else
#define SIMDJSON_DLLIMPORTEXPORT __declspec(dllexport)
// We assume by default static linkage
#define SIMDJSON_DLLIMPORTEXPORT
#endif
/**
* Workaround for the vcpkg package manager. Only vcpkg should
* ever touch the next line. The SIMDJSON_USING_LIBRARY macro is otherwise unused.
*/
#if SIMDJSON_USING_LIBRARY
#define SIMDJSON_DLLIMPORTEXPORT __declspec(dllimport)
#endif
/**
* End of workaround for the vcpkg package manager.
*/
#else
#define SIMDJSON_DLLIMPORTEXPORT
#endif

View File

@ -55,10 +55,23 @@ if(SIMDJSON_BUILD_STATIC)
else()
MESSAGE( STATUS "Building a dynamic library." )
add_library(simdjson SHARED "")
target_compile_definitions(simdjson INTERFACE SIMDJSON_USING_LIBRARY=1)
if(MSVC)
MESSAGE( STATUS "Building a Windows DLL using Visual Studio, exporting all symbols automatically." )
set_target_properties(simdjson PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS 1)
# Setting the dllexport, users of the library should never need to set SIMDJSON_BUILDING_WINDOWS_DYNAMIC_LIBRARY
# once the DLL is built. Note the private scope on the next line.
target_compile_definitions(simdjson PRIVATE SIMDJSON_BUILDING_WINDOWS_DYNAMIC_LIBRARY=1)
#
# The simdjson-flags are exported as part of the CMake install. Therefore users of
# the library should pick up the SIMDJSON_USING_WINDOWS_DYNAMIC_LIBRARY=1 value.
# It should appear in simdjson-config.cmake (an installed file) as (for example)
# set_target_properties(simdjson::simdjson-flags PROPERTIES
# INTERFACE_COMPILE_DEFINITIONS "SIMDJSON_THREADS_ENABLED=1"
# INTERFACE_COMPILE_DEFINITIONS "SIMDJSON_USING_WINDOWS_DYNAMIC_LIBRARY=1"
# INTERFACE_LINK_LIBRARIES "Threads::Threads"
# )
#
target_compile_definitions(simdjson-flags INTERFACE SIMDJSON_USING_WINDOWS_DYNAMIC_LIBRARY=1)
endif()
###
# Somehow, for a dynamic library, the next line is entirely fine (but not for a static one).
@ -67,6 +80,7 @@ else()
endif()
target_link_libraries(simdjson PUBLIC simdjson-headers simdjson-flags) # Only expose the headers, not sources
##
## In systems like R, libraries must not use stderr or abort to be acceptable.
## Thus we make it a hard rule that one is not allowed to call abort or stderr.

View File

@ -10,7 +10,7 @@ add_subdirectory(ondemand)
#
# These tests explicitly do #include "simdjson.cpp" so they can override stuff
#
if(NOT SIMDJSON_LEGACY_VISUAL_STUDIO)
if(NOT SIMDJSON_LEGACY_VISUAL_STUDIO AND NOT SIMDJSON_WINDOWS_DLL)
add_cpp_test(numberparsingcheck LABELS acceptance per_implementation)
target_link_libraries(numberparsingcheck simdjson-include-source simdjson-windows-headers)
target_compile_definitions(numberparsingcheck PRIVATE NOMINMAX)
@ -21,7 +21,7 @@ endif()
# All remaining tests link with simdjson proper
link_libraries(simdjson)
if(NOT SIMDJSON_LEGACY_VISUAL_STUDIO)
if(NOT SIMDJSON_LEGACY_VISUAL_STUDIO AND NOT SIMDJSON_WINDOWS_DLL)
add_cpp_test(random_string_number_tests LABELS dom acceptance per_implementation)
endif()
add_cpp_test(basictests LABELS dom acceptance per_implementation)

View File

@ -0,0 +1,5 @@
# Installation tests
The tests in this directory are meant to run on top of an installed simdjson library.
As such they are not meant to run as part of the main tests of the library, executed
while building. They should run after the installation step has completed.

View File

@ -0,0 +1,37 @@
cmake_minimum_required(VERSION 3.15)
project(test_simdjson_install VERSION 0.1.0 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(simdjson REQUIRED)
file(WRITE main.cpp "
#include \"simdjson.h\"
#include <iostream>
int main(void) {
auto cars_json = R\"( [
{ \"make\": \"Toyota\", \"model\": \"Camry\", \"year\": 2018, \"tire_pressure\": [ 40.1, 39.9, 37.7, 40.4 ] },
{ \"make\": \"Kia\", \"model\": \"Soul\", \"year\": 2012, \"tire_pressure\": [ 30.1, 31.0, 28.6, 28.7 ] },
{ \"make\": \"Toyota\", \"model\": \"Tercel\", \"year\": 1999, \"tire_pressure\": [ 29.8, 30.0, 30.2, 30.5 ] }
] )\"_padded;
std::cout << \"parsing : \" << cars_json << std::endl;
simdjson::dom::parser parser;
simdjson::dom::array obj;
auto err = parser.parse(cars_json).get(obj);
if (err) {
std::cerr << \"Failed to parse json\" << std::endl;
std::cerr << err << std::endl;
return EXIT_FAILURE;
}
std::cout << \"Success\" << std::endl;
return EXIT_SUCCESS;
}")
add_executable(repro main.cpp)
target_link_libraries(repro PUBLIC simdjson::simdjson)