Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CMake and no-warn MSVC support #118

Open
wants to merge 4 commits into
base: release
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 99 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
cmake_minimum_required(VERSION 3.11)

project(greatest VERSION 1.5.0 LANGUAGES C CXX)

#===============#
# Compiler args #
#===============#

set(CMAKE_C_VISIBILITY_PRESET hidden)
set(CMAKE_VISIBILITY_INLINES_HIDDEN YES)
set(CMAKE_C_STANDARD 90 CACHE STRING "")

add_library("${PROJECT_NAME}_compiler_flags" INTERFACE)
target_compile_features("${PROJECT_NAME}_compiler_flags" INTERFACE "c_std_${CMAKE_C_STANDARD}")

# add compiler warning flags just when building this project via
# the BUILD_INTERFACE genex
set(gcc_like "$<COMPILE_LANG_AND_ID:C,CXX,ARMClang,AppleClang,Clang,GNU,LCC>")
set(msvc "$<COMPILE_LANG_AND_ID:C,CXX,MSVC>")
target_compile_options(
"${PROJECT_NAME}_compiler_flags"
INTERFACE
"$<${gcc_like}:$<BUILD_INTERFACE:-Wshadow;-Wformat=2;-Wall;-pedantic;-Wno-format-nonliteral;-Wno-unused-function;-Wno-long-long>>"
"$<${msvc}:$<BUILD_INTERFACE:-W3;-WX;-Zi;-permissive->>"
)

# Set the build directories
if (MSVC)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}")
else ()
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib")
endif ()

#===========#
# Configure #
#===========#

# configure a header file to pass the version number only
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/config.h.in"
"${PROJECT_NAME}Config.h"
)

#=============#
# Sub-targets #
#=============#

include(CTest)
if (BUILD_TESTING)
add_subdirectory("example")
endif (BUILD_TESTING)

#=========#
# Install #
#=========#

include(GNUInstallDirs)

include(InstallRequiredSystemLibraries)
set(CPACK_BUNDLE_NAME "${PROJECT_NAME}")
set(CPACK_PACKAGE_VENDOR "SamuelMarks")
set(CPACK_PACKAGE_DESCRIPTION " A C testing library in 1 file. No dependencies, no dynamic allocation. ISC licensed.")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "${CPACK_PACKAGE_DESCRIPTION_SUMMARY}")
if (APPLE)
set(CPACK_BUNDLE_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Info.plist")
set(CPACK_BUNDLE_ICON "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Info.plist")
set(CPACK_PACKAGE_ICON "${CMAKE_CURRENT_SOURCE_DIR}/cmake/CustomVolumeIcon.icns")
endif (APPLE)
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE")
set(CPACK_PACKAGE_VERSION_MAJOR "${${PROJECT_NAME}_VERSION_MAJOR}")
set(CPACK_PACKAGE_VERSION_MINOR "${${PROJECT_NAME}_VERSION_MINOR}")
set(CPACK_RESOURCE_FILE_README "${CMAKE_SOURCE_DIR}/cmake/README.txt")
set(CPACK_RESOURCE_FILE_WELCOME "${CMAKE_SOURCE_DIR}/cmake/Welcome.txt")
set(CPACK_PACKAGE_CONTACT "https://github.com/silentbicycle/${PROJECT_NAME}")

include(CPack)
include(CMakePackageConfigHelpers)

# generate the config file that is includes the exports
configure_package_config_file(
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/Config.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
INSTALL_DESTINATION "${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}"
NO_SET_AND_CHECK_MACRO
NO_CHECK_REQUIRED_COMPONENTS_MACRO
)

# generate the version file for the config file
write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
VERSION "${${PROJECT_NAME}_VERSION_MAJOR}.${${PROJECT_NAME}_VERSION_MINOR}"
COMPATIBILITY AnyNewerVersion
)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
DESTINATION "${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}")
Binary file added cmake/BundleIcon.icns
Binary file not shown.
7 changes: 7 additions & 0 deletions cmake/CTestConfig.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
set(CTEST_PROJECT_NAME "greatest")
set(CTEST_NIGHTLY_START_TIME "00:00:00 EST")

set(CTEST_DROP_METHOD "http")
set(CTEST_DROP_SITE "my.cdash.org")
set(CTEST_DROP_LOCATION "/submit.php?project=greatest")
set(CTEST_DROP_SITE_CDASH TRUE)
4 changes: 4 additions & 0 deletions cmake/Config.cmake.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

@PACKAGE_INIT@

include ( "${CMAKE_CURRENT_LIST_DIR}/greatestTargets.cmake" )
Binary file added cmake/CustomVolumeIcon.icns
Binary file not shown.
14 changes: 14 additions & 0 deletions cmake/Info.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleExecutable</key>
<string>BundleGeneratorTest</string>
<key>CFBundleIconFile</key>
<string>BundleGeneratorTest.icns</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
</dict>
</plist>
6 changes: 6 additions & 0 deletions cmake/MultiCPackConfig.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
include("release/CPackConfig.cmake")

set(CPACK_INSTALL_CMAKE_PROJECTS
"debug;greatest;ALL;/"
"release;greatest;ALL;/"
)
7 changes: 7 additions & 0 deletions cmake/README.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
greatest

A C testing library in 1 file. No dependencies, no dynamic allocation. ISC licensed.

------------------------------------------------------------------------

Licensed under ISC
1 change: 1 addition & 0 deletions cmake/Welcome.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This installs greatest.
9 changes: 9 additions & 0 deletions cmake/config.h.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#ifndef GREATEST_CONFIG_H
#define GREATEST_CONFIG_H

#define GREATEST_VERSION_MAJOR @greatest_VERSION_MAJOR@
#define GREATEST_VERSION_MINOR @greatest_VERSION_MINOR@
#define GREATEST_VERSION_PATCH @greatest_VERSION_PATCH@
#define GREATEST_VERSION "@greatest_VERSION@"

#endif /* !GREATEST_CONFIG_H */
2 changes: 1 addition & 1 deletion example.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

#include "greatest.h"

/* Define a suite, compiled seperately. */
/* Define a suite, compiled separately. */
SUITE_EXTERN(other_suite);

/* Declare a local suite. */
Expand Down
33 changes: 33 additions & 0 deletions example/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
foreach (example
"example"
"example_cpp"
"example_no_runner"
"example_no_suite"
"example_shuffle"
"example_suite"
"example_trunc")
set(EXEC_NAME "${PROJECT_NAME}_${example}")
if (example STREQUAL "example_cpp" OR example STREQUAL "example_suite")
continue() #set(Source_Files "../${example}.cpp")
elseif (example STREQUAL "example")
set(Source_Files "../${example}.c" "../${example}_suite.c")
else ()
set(Source_Files "../${example}.c")
endif ()
source_group("Source Files" FILES "${Source_Files}")

add_executable("${EXEC_NAME}" "${Header_Files}" "${Source_Files}")

target_link_libraries("${EXEC_NAME}" PUBLIC "${PROJECT_NAME}_compiler_flags")

include(GNUInstallDirs)
target_include_directories(
"${EXEC_NAME}"
PUBLIC
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>"
"$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
)
add_test(NAME "${EXEC_NAME}"
COMMAND "${CMAKE_BINARY_DIR}/${EXEC_NAME}")
endforeach ()
39 changes: 39 additions & 0 deletions example_shuffle.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,15 @@
#include <assert.h>
#include <string.h>

#if defined(_MSC_VER) || defined(__MINGW32__)
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winsock2.h>
typedef __int64 uint64_t;
#else
#include <sys/time.h>
#include <err.h>
#endif

#include "greatest.h"

Expand Down Expand Up @@ -56,6 +63,34 @@ TEST just_fail(void) {
FAIL();
}

#if defined(_MSC_VER) || defined(__MINGW32__)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than adding a substitute implementation of gettimeofday here, it could just call GetSystemTime and SystemTimeToFileTime then use the milliseconds field. Actually replicating the overall behavior of gettimeofday isn't important -- it's just grabbing a timestamp to use as a seed for example code.

int gettimeofday(struct timeval * tp, struct timezone * tzp)
{
/* Note: some broken versions only have 8 trailing zero's, the correct epoch has 9 trailing zero's
* This magic number is the number of 100 nanosecond intervals since January 1, 1601 (UTC)
* until 00:00:00 January 1, 1970 */
static const uint64_t EPOCH = ((uint64_t) 116444736000000000ULL);

SYSTEMTIME system_time;
FILETIME file_time;
uint64_t time;

GetSystemTime( &system_time );
SystemTimeToFileTime( &system_time, &file_time );
time = ((uint64_t)file_time.dwLowDateTime ) ;
time += ((uint64_t)file_time.dwHighDateTime) << 32;

tp->tv_sec = (long) ((time - EPOCH) / 10000000L);
tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
return 0;
}

void err(int exit_code, const char *msg) {
fputs(msg, stderr);
exit(exit_code);
}
#endif /* defined(_MSC_VER) || defined(__MINGW32__) */

static unsigned int seed_of_time(void) {
static unsigned int counter = 1;
struct timeval tv;
Expand All @@ -72,7 +107,11 @@ static void set_suffix(unsigned int i) {
* to test conditionally including a "_" separator. */
if (i > 0) {
/* Using `sprintf` here for building with `-std=c89`. */
#if _MSC_VER
(void)sprintf_s(suffix_buf, sizeof(unsigned), "%d", i);
#else
(void)sprintf(suffix_buf, "%d", i);
#endif
greatest_set_test_suffix(suffix_buf);
}
}
Expand Down
98 changes: 96 additions & 2 deletions greatest.h
Original file line number Diff line number Diff line change
Expand Up @@ -700,6 +700,99 @@ typedef enum greatest_test_res {
prng->count_run = prng->random_order = prng->initialized = 0; \
} while(0)

#if defined(__STDC_LIB_EXT1__) || defined(_MSC_VER) || defined(__MINGW32__)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this really necessary? strnlen and strncat are in the C standard library.

Note the bit in the README about the LOC limit -- this would exceed that, considerably.

Also, it adds a substantial block of code with a different license. I'd really prefer to avoid doing that.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes but strnlen_s and strncat_s are missing.

Again happy to remove the code and just use a different macro-branch for MSVC, the goal is to get it to compile in MSVC without throwing any warnings related to https://learn.microsoft.com/en-us/cpp/c-runtime-library/security-features-in-the-crt

#else
/* Taken then modified from Apache-2.0 licensed Vector Packet Processing @ 4141ded */
#include <err.h>

#define PREDICT_FALSE(x) __builtin_expect((x),0)
typedef unsigned int uword __attribute__ ((mode (SI)));
typedef unsigned char u8;


size_t
strnlen_s (const char *s, size_t maxsize)
{
u8 bad;

bad = (s == 0) + (maxsize == 0);
if (PREDICT_FALSE (bad != 0))
{
if (s == 0)
err(1, "[C11 violation] s NULL");
if (maxsize == 0)
err(1, "[C11 violation] maxsize 0");
return 0;
}
return strnlen (s, maxsize);
}

errno_t
strncat_s (char *__restrict__ dest, rsize_t dmax,
const char *__restrict__ src, rsize_t n)
{
u8 bad;
uword low, hi;
size_t m, dest_size, allowed_size;
errno_t status = EOK;

bad = (dest == 0) + (src == 0) + (dmax == 0) + (n == 0);
if (PREDICT_FALSE (bad != 0))
{
/* Not actually trying to concatenate anything is OK */
if (n == 0)
return EOK;
if (dest == 0)
err(1, "[C11 violation] dest NULL");
if (src == 0)
err(1, "[C11 violation] src NULL");
if (dmax == 0)
err(1, "[C11 violation] dmax 0");
return EINVAL;
}

/* Check for src/dst overlap, which is not allowed */
low = (uword) (src < dest ? src : dest);
hi = (uword) (src < dest ? dest : src);

if (PREDICT_FALSE (low + (n - 1) >= hi))
{
err(1, "[C11 violation] src/dest overlap");
return EINVAL;
}

dest_size = clib_strnlen (dest, dmax);
allowed_size = dmax - dest_size;

if (PREDICT_FALSE (allowed_size == 0))
{
err(1, "[C11 violation] no space left in dest");
return (EINVAL);
}

if (PREDICT_FALSE (n >= allowed_size))
{
/*
* unlike strcat_s, strncat_s will do the concatenation anyway when
* there is not enough space in dest. But it will do the truncation and
* null terminate dest
*/
m = strnlen (src, allowed_size);
if (m >= allowed_size)
{
m = allowed_size - 1;
status = EOVERFLOW;
}
}
else
m = strnlen (src, n);

memcpy (dest + dest_size, src, m);
dest[dest_size + m] = '\0';
return status;
}
#endif /* defined(__STDC_LIB_EXT1__) || defined(_MSC_VER) */

/* Include several function definitions in the main test file. */
#define GREATEST_MAIN_DEFS() \
\
Expand Down Expand Up @@ -728,10 +821,11 @@ static void greatest_buffer_test_name(const char *name) { \
struct greatest_run_info *g = &greatest_info; \
size_t len = strlen(name), size = sizeof(g->name_buf); \
memset(g->name_buf, 0x00, size); \
(void)strncat(g->name_buf, name, size - 1); \
(void)strncat_s(g->name_buf, size, name, size - 1); \
if (g->name_suffix && (len + 1 < size)) { \
g->name_buf[len] = '_'; \
strncat(&g->name_buf[len+1], g->name_suffix, size-(len+2)); \
strncat_s(&g->name_buf[len+1], size, g->name_suffix, \
size-(len+2)); \
} \
} \
\
Expand Down
Loading