diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 6ff6f49b..d7406a73 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -10,7 +10,7 @@ jobs: # TODO: add coverage build (requires lcov) # TODO: add clang + libc++ builds for ubuntu job: - name: ${{ matrix.os }}.${{ matrix.build_type }}.${{ matrix.compiler }} + name: ${{ matrix.os }}.${{ matrix.build_type }}.${{ matrix.lib }}.${{ matrix.compiler }} runs-on: ${{ matrix.os }} strategy: fail-fast: false @@ -18,28 +18,39 @@ jobs: os: [ubuntu-latest, ubuntu-20.04, macos-latest] build_type: ['Release', 'Debug'] compiler: [g++, clang++] - include: - - displayTargetName: windows-latest-release - os: windows-latest - build_type: 'Release' - - displayTargetName: windows-latest-debug - os: windows-latest - build_type: 'Debug' + lib: ['shared', 'static'] + steps: - uses: actions/checkout@v2 - name: create build environment run: cmake -E make_directory ${{ runner.workspace }}/_build + - name: setup cmake initial cache + run: touch compiler-cache.cmake + + - name: setup lto + # Workaround for enabling -flto on old GCC versions + if: matrix.build_type == 'Release' && startsWith(matrix.compiler, 'g++') && matrix.os != 'macos-latest' + run: > + echo 'set (CMAKE_CXX_FLAGS -flto CACHE STRING "")' >> compiler-cache.cmake; + echo 'set (CMAKE_RANLIB /usr/bin/gcc-ranlib CACHE FILEPATH "")' >> compiler-cache.cmake; + echo 'set (CMAKE_AR /usr/bin/gcc-ar CACHE FILEPATH "")' >> compiler-cache.cmake; + echo 'set (CMAKE_NM /usr/bin/gcc-nm CACHE FILEPATH "")' >> compiler-cache.cmake; + - name: configure cmake env: CXX: ${{ matrix.compiler }} shell: bash working-directory: ${{ runner.workspace }}/_build run: > - cmake $GITHUB_WORKSPACE + cmake -C ${{ github.workspace }}/compiler-cache.cmake + $GITHUB_WORKSPACE -DBENCHMARK_DOWNLOAD_DEPENDENCIES=ON + -DBUILD_SHARED_LIBS=${{ matrix.lib == 'shared' }} -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} + -DCMAKE_CXX_VISIBILITY_PRESET=hidden + -DCMAKE_VISIBILITY_INLINES_HIDDEN=ON - name: build shell: bash @@ -51,6 +62,57 @@ jobs: working-directory: ${{ runner.workspace }}/_build run: ctest -C ${{ matrix.build_type }} -VV + msvc: + name: ${{ matrix.os }}.${{ matrix.build_type }}.${{ matrix.lib }}.${{ matrix.msvc }} + runs-on: ${{ matrix.os }} + defaults: + run: + shell: powershell + strategy: + fail-fast: false + matrix: + msvc: + - VS-16-2019 + - VS-17-2022 + arch: + - x64 + build_type: + - Debug + - Release + lib: + - shared + - static + include: + - msvc: VS-16-2019 + os: windows-2019 + generator: 'Visual Studio 16 2019' + - msvc: VS-17-2022 + os: windows-2022 + generator: 'Visual Studio 17 2022' + + steps: + - uses: actions/checkout@v2 + + - name: configure cmake + run: > + cmake -S . -B _build/ + -A ${{ matrix.arch }} + -G "${{ matrix.generator }}" + -DBENCHMARK_DOWNLOAD_DEPENDENCIES=ON + -DBUILD_SHARED_LIBS=${{ matrix.lib == 'shared' }} + + - name: build + run: cmake --build _build/ --config ${{ matrix.build_type }} + + - name: setup test environment + # Make sure gmock and benchmark DLLs can be found + run: > + echo "$((Get-Item .).FullName)/_build/bin/${{ matrix.build_type }}" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append; + echo "$((Get-Item .).FullName)/_build/src/${{ matrix.build_type }}" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append; + + - name: test + run: ctest --test-dir _build/ -C ${{ matrix.build_type }} -VV + ubuntu-16_04: name: ubuntu-16.04.${{ matrix.build_type }}.${{ matrix.compiler }} runs-on: [ubuntu-latest] @@ -71,15 +133,34 @@ jobs: - name: create build environment run: cmake -E make_directory $GITHUB_WORKSPACE/_build + - name: setup cmake initial cache + run: touch compiler-cache.cmake + + - name: setup lto + # Workaround for enabling -flto on old GCC versions + # -Wl,--no-as-needed is needed to avoid the following linker error: + # + # /usr/lib/gcc/x86_64-linux-gnu/5/libstdc++.so: undefined reference to `pthread_create' + # + if: matrix.build_type == 'Release' && startsWith(matrix.compiler, 'g++') + run: > + echo 'set (CMAKE_CXX_FLAGS "-Wl,--no-as-needed -flto" CACHE STRING "")' >> compiler-cache.cmake; + echo 'set (CMAKE_RANLIB "/usr/bin/gcc-ranlib" CACHE FILEPATH "")' >> compiler-cache.cmake; + echo 'set (CMAKE_AR "/usr/bin/gcc-ar" CACHE FILEPATH "")' >> compiler-cache.cmake; + echo 'set (CMAKE_NM "/usr/bin/gcc-nm" CACHE FILEPATH "")' >> compiler-cache.cmake; + - name: configure cmake env: CXX: ${{ matrix.compiler }} shell: bash working-directory: ${{ github.workspace }}/_build run: > - cmake $GITHUB_WORKSPACE + cmake -C ../compiler-cache.cmake .. -DBENCHMARK_DOWNLOAD_DEPENDENCIES=ON + -DBUILD_SHARED_LIBS=${{ matrix.lib == 'shared' }} -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} + -DCMAKE_CXX_VISIBILITY_PRESET=hidden + -DCMAKE_VISIBILITY_INLINES_HIDDEN=ON - name: build shell: bash @@ -126,16 +207,38 @@ jobs: - name: create build environment run: cmake -E make_directory $GITHUB_WORKSPACE/_build + - name: setup cmake initial cache + run: touch compiler-cache.cmake + + - name: setup lto + # Workaround for enabling -flto on old GCC versions + # -Wl,--no-as-needed is needed to avoid the following linker error: + # + # /usr/lib/gcc/x86_64-linux-gnu/6/libstdc++.so: undefined reference to `pthread_create' + # + if: matrix.build_type == 'Release' && startsWith(matrix.compiler, 'g++') + run: > + COMPILER=${{ matrix.compiler }}; + VERSION=${COMPILER#g++-}; + PREFIX=/usr/bin/gcc; + echo "set (CMAKE_CXX_FLAGS \"-Wl,--no-as-needed -flto\" CACHE STRING \"\")" >> compiler-cache.cmake; + echo "set (CMAKE_RANLIB \"$PREFIX-ranlib-$VERSION\" CACHE FILEPATH \"\")" >> compiler-cache.cmake; + echo "set (CMAKE_AR \"$PREFIX-ar-$VERSION\" CACHE FILEPATH \"\")" >> compiler-cache.cmake; + echo "set (CMAKE_NM \"$PREFIX-nm-$VERSION\" CACHE FILEPAT \"\")" >> compiler-cache.cmake; + - name: configure cmake env: CXX: ${{ matrix.compiler }} shell: bash working-directory: ${{ github.workspace }}/_build run: > - cmake $GITHUB_WORKSPACE - -DBENCHMARK_ENABLE_TESTING=${{ matrix.run_tests }} - -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} + cmake -C ../compiler-cache.cmake .. -DBENCHMARK_DOWNLOAD_DEPENDENCIES=${{ matrix.run_tests }} + -DBENCHMARK_ENABLE_TESTING=${{ matrix.run_tests }} + -DBUILD_SHARED_LIBS=${{ matrix.lib == 'shared' }} + -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} + -DCMAKE_CXX_VISIBILITY_PRESET=hidden + -DCMAKE_VISIBILITY_INLINES_HIDDEN=ON - name: build shell: bash diff --git a/BUILD.bazel b/BUILD.bazel index 904c691d..31a7660e 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -1,5 +1,22 @@ licenses(["notice"]) +load( + "@com_github_google_benchmark//tools:workspace/generate_export_header.bzl", + "generate_export_header", +) + +posix_copts = [ + "-fvisibility=hidden", + "-fvisibility-inlines-hidden", +] + +# Generate header to provide ABI export symbols +generate_export_header( + out = "include/benchmark/export.h", + lib = "benchmark", + static_define = "BENCHMARK_STATIC_DEFINE", +) + config_setting( name = "qnx", constraint_values = ["@platforms//os:qnx"], @@ -27,13 +44,20 @@ cc_library( ], exclude = ["src/benchmark_main.cc"], ), - hdrs = ["include/benchmark/benchmark.h"], + hdrs = [ + "include/benchmark/benchmark.h", + "include/benchmark/export.h", # From generate_export_header + ], linkopts = select({ ":windows": ["-DEFAULTLIB:shlwapi.lib"], "//conditions:default": ["-pthread"], }), strip_include_prefix = "include", visibility = ["//visibility:public"], + copts = select({ + ":windows": [], + "//conditions:default": posix_copts, + }), ) cc_library( @@ -43,6 +67,10 @@ cc_library( strip_include_prefix = "include", visibility = ["//visibility:public"], deps = [":benchmark"], + copts = select({ + ":windows": [], + "//conditions:default": posix_copts, + }), ) cc_library( diff --git a/CMakeLists.txt b/CMakeLists.txt index b8852e4c..cc80702c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,7 +50,10 @@ option(BENCHMARK_USE_BUNDLED_GTEST "Use bundled GoogleTest. If disabled, the fin option(BENCHMARK_ENABLE_LIBPFM "Enable performance counters provided by libpfm" OFF) -set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) +# Export only public symbols +set(CMAKE_CXX_VISIBILITY_PRESET hidden) +set(CMAKE_VISIBILITY_INLINES_HIDDEN ON) + if(MSVC) # As of CMake 3.18, CMAKE_SYSTEM_PROCESSOR is not set properly for MSVC and # cross-compilation (e.g. Host=x86_64, target=aarch64) requires using the @@ -123,10 +126,11 @@ set(GENERIC_LIB_VERSION ${VERSION}) string(SUBSTRING ${VERSION} 0 1 GENERIC_LIB_SOVERSION) # Import our CMake modules -include(CheckCXXCompilerFlag) include(AddCXXCompilerFlag) -include(CXXFeatureCheck) +include(CheckCXXCompilerFlag) include(CheckLibraryExists) +include(CXXFeatureCheck) +include(GenerateExportHeader) check_library_exists(rt shm_open "" HAVE_LIB_RT) diff --git a/bindings/python/google_benchmark/benchmark.cc b/bindings/python/google_benchmark/benchmark.cc index 02b6ed7e..89e44fff 100644 --- a/bindings/python/google_benchmark/benchmark.cc +++ b/bindings/python/google_benchmark/benchmark.cc @@ -1,5 +1,7 @@ // Benchmark for Python. +#include "benchmark/benchmark.h" + #include #include #include @@ -9,8 +11,6 @@ #include "pybind11/stl.h" #include "pybind11/stl_bind.h" -#include "benchmark/benchmark.h" - PYBIND11_MAKE_OPAQUE(benchmark::UserCounters); namespace { diff --git a/include/benchmark/benchmark.h b/include/benchmark/benchmark.h index 504fae1f..2478daea 100644 --- a/include/benchmark/benchmark.h +++ b/include/benchmark/benchmark.h @@ -187,6 +187,8 @@ BENCHMARK(BM_test)->Unit(benchmark::kMillisecond); #include #include +#include "benchmark/export.h" + #if defined(BENCHMARK_HAS_CXX11) #include #include @@ -280,23 +282,29 @@ BENCHMARK(BM_test)->Unit(benchmark::kMillisecond); #define BENCHMARK_OVERRIDE #endif +#if defined(_MSC_VER) +#pragma warning(push) +// C4251: needs to have dll-interface to be used by clients of class +#pragma warning(disable : 4251) +#endif + namespace benchmark { class BenchmarkReporter; -void Initialize(int* argc, char** argv); -void Shutdown(); +BENCHMARK_EXPORT void Initialize(int* argc, char** argv); +BENCHMARK_EXPORT void Shutdown(); // Report to stdout all arguments in 'argv' as unrecognized except the first. // Returns true there is at least on unrecognized argument (i.e. 'argc' > 1). -bool ReportUnrecognizedArguments(int argc, char** argv); +BENCHMARK_EXPORT bool ReportUnrecognizedArguments(int argc, char** argv); // Returns the current value of --benchmark_filter. -std::string GetBenchmarkFilter(); +BENCHMARK_EXPORT std::string GetBenchmarkFilter(); // Creates a default display reporter. Used by the library when no display // reporter is provided, but also made available for external use in case a // custom reporter should respect the `--benchmark_format` flag as a fallback -BenchmarkReporter* CreateDefaultDisplayReporter(); +BENCHMARK_EXPORT BenchmarkReporter* CreateDefaultDisplayReporter(); // Generate a list of benchmarks matching the specified --benchmark_filter flag // and if --benchmark_list_tests is specified return after printing the name @@ -314,18 +322,19 @@ BenchmarkReporter* CreateDefaultDisplayReporter(); // 'file_reporter' is ignored. // // RETURNS: The number of matching benchmarks. -size_t RunSpecifiedBenchmarks(); -size_t RunSpecifiedBenchmarks(std::string spec); +BENCHMARK_EXPORT size_t RunSpecifiedBenchmarks(); +BENCHMARK_EXPORT size_t RunSpecifiedBenchmarks(std::string spec); -size_t RunSpecifiedBenchmarks(BenchmarkReporter* display_reporter); -size_t RunSpecifiedBenchmarks(BenchmarkReporter* display_reporter, - std::string spec); +BENCHMARK_EXPORT size_t +RunSpecifiedBenchmarks(BenchmarkReporter* display_reporter); +BENCHMARK_EXPORT size_t +RunSpecifiedBenchmarks(BenchmarkReporter* display_reporter, std::string spec); -size_t RunSpecifiedBenchmarks(BenchmarkReporter* display_reporter, - BenchmarkReporter* file_reporter); -size_t RunSpecifiedBenchmarks(BenchmarkReporter* display_reporter, - BenchmarkReporter* file_reporter, - std::string spec); +BENCHMARK_EXPORT size_t RunSpecifiedBenchmarks( + BenchmarkReporter* display_reporter, BenchmarkReporter* file_reporter); +BENCHMARK_EXPORT size_t +RunSpecifiedBenchmarks(BenchmarkReporter* display_reporter, + BenchmarkReporter* file_reporter, std::string spec); // If a MemoryManager is registered (via RegisterMemoryManager()), // it can be used to collect and report allocation metrics for a run of the @@ -374,9 +383,11 @@ class MemoryManager { // Register a MemoryManager instance that will be used to collect and report // allocation measurements for benchmark runs. +BENCHMARK_EXPORT void RegisterMemoryManager(MemoryManager* memory_manager); // Add a key-value pair to output as part of the context stanza in the report. +BENCHMARK_EXPORT void AddCustomContext(const std::string& key, const std::string& value); namespace internal { @@ -384,14 +395,15 @@ class Benchmark; class BenchmarkImp; class BenchmarkFamilies; +BENCHMARK_EXPORT void UseCharPointer(char const volatile*); // Take ownership of the pointer and register the benchmark. Return the // registered benchmark. -Benchmark* RegisterBenchmarkInternal(Benchmark*); +BENCHMARK_EXPORT Benchmark* RegisterBenchmarkInternal(Benchmark*); // Ensure that the standard streams are properly initialized in every TU. -int InitializeStreams(); +BENCHMARK_EXPORT int InitializeStreams(); BENCHMARK_UNUSED static int stream_init_anchor = InitializeStreams(); } // namespace internal @@ -573,7 +585,7 @@ enum AggregationReportMode // State is passed to a running Benchmark and contains state for the // benchmark to use. -class State { +class BENCHMARK_EXPORT State { public: struct StateIterator; friend struct StateIterator; @@ -904,7 +916,7 @@ typedef void(Function)(State&); // be called on this object to change the properties of the benchmark. // Each method returns "this" so that multiple method calls can // chained into one expression. -class Benchmark { +class BENCHMARK_EXPORT Benchmark { public: virtual ~Benchmark(); @@ -1097,7 +1109,6 @@ class Benchmark { protected: explicit Benchmark(const char* name); - Benchmark(Benchmark const&); void SetName(const char* name); int ArgsCnt() const; @@ -1127,7 +1138,17 @@ class Benchmark { callback_function setup_; callback_function teardown_; - Benchmark& operator=(Benchmark const&); + Benchmark(Benchmark const&) +#if defined(BENCHMARK_HAS_CXX11) + = delete +#endif + ; + + Benchmark& operator=(Benchmark const&) +#if defined(BENCHMARK_HAS_CXX11) + = delete +#endif + ; }; } // namespace internal @@ -1146,12 +1167,12 @@ internal::Benchmark* RegisterBenchmark(const char* name, Lambda&& fn); // Remove all registered benchmarks. All pointers to previously registered // benchmarks are invalidated. -void ClearRegisteredBenchmarks(); +BENCHMARK_EXPORT void ClearRegisteredBenchmarks(); namespace internal { // The class used to hold all Benchmarks created from static function. // (ie those created using the BENCHMARK(...) macros. -class FunctionBenchmark : public Benchmark { +class BENCHMARK_EXPORT FunctionBenchmark : public Benchmark { public: FunctionBenchmark(const char* name, Function* func) : Benchmark(name), func_(func) {} @@ -1444,22 +1465,14 @@ class Fixture : public internal::Benchmark { #endif // Helper macro to create a main routine in a test that runs the benchmarks -#define BENCHMARK_MAIN() \ - int main(int argc, char** argv) { \ - ::benchmark::Initialize(&argc, argv); \ - if (::benchmark::ReportUnrecognizedArguments(argc, argv)) return 1; \ - ::benchmark::RunSpecifiedBenchmarks(); \ - ::benchmark::Shutdown(); \ - return 0; \ - } \ - int main(int, char**) +#define BENCHMARK_MAIN() BENCHMARK_EXPORT int main(int argc, char** argv) // ------------------------------------------------------ // Benchmark Reporters namespace benchmark { -struct CPUInfo { +struct BENCHMARK_EXPORT CPUInfo { struct CacheInfo { std::string type; int level; @@ -1483,7 +1496,7 @@ struct CPUInfo { }; // Adding Struct for System Information -struct SystemInfo { +struct BENCHMARK_EXPORT SystemInfo { std::string name; static const SystemInfo& Get(); @@ -1495,7 +1508,7 @@ struct SystemInfo { // BenchmarkName contains the components of the Benchmark's name // which allows individual fields to be modified or cleared before // building the final name using 'str()'. -struct BenchmarkName { +struct BENCHMARK_EXPORT BenchmarkName { std::string function_name; std::string args; std::string min_time; @@ -1514,7 +1527,7 @@ struct BenchmarkName { // can control the destination of the reports by calling // RunSpecifiedBenchmarks and passing it a custom reporter object. // The reporter object must implement the following interface. -class BenchmarkReporter { +class BENCHMARK_EXPORT BenchmarkReporter { public: struct Context { CPUInfo const& cpu_info; @@ -1525,7 +1538,7 @@ class BenchmarkReporter { Context(); }; - struct Run { + struct BENCHMARK_EXPORT Run { static const int64_t no_repetition_index = -1; enum RunType { RT_Iteration, RT_Aggregate }; @@ -1670,7 +1683,7 @@ class BenchmarkReporter { // Simple reporter that outputs benchmark data to the console. This is the // default reporter used by RunSpecifiedBenchmarks(). -class ConsoleReporter : public BenchmarkReporter { +class BENCHMARK_EXPORT ConsoleReporter : public BenchmarkReporter { public: enum OutputOptions { OO_None = 0, @@ -1695,7 +1708,7 @@ class ConsoleReporter : public BenchmarkReporter { bool printed_header_; }; -class JSONReporter : public BenchmarkReporter { +class BENCHMARK_EXPORT JSONReporter : public BenchmarkReporter { public: JSONReporter() : first_report_(true) {} virtual bool ReportContext(const Context& context) BENCHMARK_OVERRIDE; @@ -1708,7 +1721,7 @@ class JSONReporter : public BenchmarkReporter { bool first_report_; }; -class BENCHMARK_DEPRECATED_MSG( +class BENCHMARK_EXPORT BENCHMARK_DEPRECATED_MSG( "The CSV Reporter will be removed in a future release") CSVReporter : public BenchmarkReporter { public: @@ -1760,11 +1773,17 @@ inline double GetTimeUnitMultiplier(TimeUnit unit) { // CreateRange(0, 100, /*multi=*/4), // CreateDenseRange(0, 4, /*step=*/1), // }); +BENCHMARK_EXPORT std::vector CreateRange(int64_t lo, int64_t hi, int multi); // Creates a list of integer values for the given range and step. +BENCHMARK_EXPORT std::vector CreateDenseRange(int64_t start, int64_t limit, int step); } // namespace benchmark +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + #endif // BENCHMARK_BENCHMARK_H_ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e814a4e0..70813129 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -22,10 +22,15 @@ add_library(benchmark::benchmark ALIAS benchmark) set_target_properties(benchmark PROPERTIES OUTPUT_NAME "benchmark" VERSION ${GENERIC_LIB_VERSION} - SOVERSION ${GENERIC_LIB_SOVERSION} + SOVERSION 2 ) target_include_directories(benchmark PUBLIC - $) + $ + $ +) + +generate_export_header(benchmark + EXPORT_FILE_NAME ${PROJECT_BINARY_DIR}/include/benchmark/export.h) # libpfm, if available if (HAVE_LIBPFM) @@ -59,7 +64,8 @@ add_library(benchmark::benchmark_main ALIAS benchmark_main) set_target_properties(benchmark_main PROPERTIES OUTPUT_NAME "benchmark_main" VERSION ${GENERIC_LIB_VERSION} - SOVERSION ${GENERIC_LIB_SOVERSION} + SOVERSION 2 + DEFINE_SYMBOL benchmark_EXPORTS ) target_link_libraries(benchmark_main PUBLIC benchmark::benchmark) @@ -107,6 +113,7 @@ if (BENCHMARK_ENABLE_INSTALL) install( DIRECTORY "${PROJECT_SOURCE_DIR}/include/benchmark" + "${PROJECT_BINARY_DIR}/include/benchmark" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} FILES_MATCHING PATTERN "*.*h") diff --git a/src/benchmark.cc b/src/benchmark.cc index f88d9054..3c06c85e 100644 --- a/src/benchmark.cc +++ b/src/benchmark.cc @@ -126,7 +126,7 @@ BM_DEFINE_int32(v, 0); namespace internal { -std::map* global_context = nullptr; +BENCHMARK_EXPORT std::map* global_context = nullptr; // FIXME: wouldn't LTO mess this up? void UseCharPointer(char const volatile*) {} diff --git a/src/benchmark_api_internal.h b/src/benchmark_api_internal.h index 94c2b297..8060fe39 100644 --- a/src/benchmark_api_internal.h +++ b/src/benchmark_api_internal.h @@ -76,6 +76,7 @@ bool FindBenchmarksInternal(const std::string& re, bool IsZero(double n); +BENCHMARK_EXPORT ConsoleReporter::OutputOptions GetOutputOptions(bool force_no_color = false); } // end namespace internal diff --git a/src/benchmark_main.cc b/src/benchmark_main.cc index b3b24783..9b2c47a3 100644 --- a/src/benchmark_main.cc +++ b/src/benchmark_main.cc @@ -15,3 +15,13 @@ #include "benchmark/benchmark.h" BENCHMARK_MAIN(); + +// MSVC does not allow the definition of dllimport. Thus, define it here instead +// inline in a macro. +int main(int argc, char** argv) { + ::benchmark::Initialize(&argc, argv); + if (::benchmark::ReportUnrecognizedArguments(argc, argv)) return 1; + ::benchmark::RunSpecifiedBenchmarks(); + ::benchmark::Shutdown(); + return 0; +} diff --git a/src/check.cc b/src/check.cc new file mode 100644 index 00000000..422b9483 --- /dev/null +++ b/src/check.cc @@ -0,0 +1,11 @@ +#include "check.h" + +namespace benchmark { +namespace internal { + +static AbortHandlerT* handler = &std::abort; + +AbortHandlerT*& GetAbortHandler() { return handler; } + +} // namespace internal +} // namespace benchmark diff --git a/src/check.h b/src/check.h index 90c7bbfa..1129e814 100644 --- a/src/check.h +++ b/src/check.h @@ -5,6 +5,7 @@ #include #include +#include "benchmark/export.h" #include "internal_macros.h" #include "log.h" @@ -13,10 +14,8 @@ namespace internal { typedef void(AbortHandlerT)(); -inline AbortHandlerT*& GetAbortHandler() { - static AbortHandlerT* handler = &std::abort; - return handler; -} +BENCHMARK_EXPORT +AbortHandlerT*& GetAbortHandler(); BENCHMARK_NORETURN inline void CallAbortHandler() { GetAbortHandler()(); diff --git a/src/commandlineflags.h b/src/commandlineflags.h index 5baaf117..78826289 100644 --- a/src/commandlineflags.h +++ b/src/commandlineflags.h @@ -5,28 +5,33 @@ #include #include +#include "benchmark/export.h" + // Macro for referencing flags. #define FLAG(name) FLAGS_##name // Macros for declaring flags. -#define BM_DECLARE_bool(name) extern bool FLAG(name) -#define BM_DECLARE_int32(name) extern int32_t FLAG(name) -#define BM_DECLARE_double(name) extern double FLAG(name) -#define BM_DECLARE_string(name) extern std::string FLAG(name) +#define BM_DECLARE_bool(name) BENCHMARK_EXPORT extern bool FLAG(name) +#define BM_DECLARE_int32(name) BENCHMARK_EXPORT extern int32_t FLAG(name) +#define BM_DECLARE_double(name) BENCHMARK_EXPORT extern double FLAG(name) +#define BM_DECLARE_string(name) BENCHMARK_EXPORT extern std::string FLAG(name) #define BM_DECLARE_kvpairs(name) \ - extern std::map FLAG(name) + BENCHMARK_EXPORT extern std::map FLAG(name) // Macros for defining flags. #define BM_DEFINE_bool(name, default_val) \ - bool FLAG(name) = benchmark::BoolFromEnv(#name, default_val) + BENCHMARK_EXPORT bool FLAG(name) = benchmark::BoolFromEnv(#name, default_val) #define BM_DEFINE_int32(name, default_val) \ - int32_t FLAG(name) = benchmark::Int32FromEnv(#name, default_val) + BENCHMARK_EXPORT int32_t FLAG(name) = \ + benchmark::Int32FromEnv(#name, default_val) #define BM_DEFINE_double(name, default_val) \ - double FLAG(name) = benchmark::DoubleFromEnv(#name, default_val) + BENCHMARK_EXPORT double FLAG(name) = \ + benchmark::DoubleFromEnv(#name, default_val) #define BM_DEFINE_string(name, default_val) \ - std::string FLAG(name) = benchmark::StringFromEnv(#name, default_val) -#define BM_DEFINE_kvpairs(name, default_val) \ - std::map FLAG(name) = \ + BENCHMARK_EXPORT std::string FLAG(name) = \ + benchmark::StringFromEnv(#name, default_val) +#define BM_DEFINE_kvpairs(name, default_val) \ + BENCHMARK_EXPORT std::map FLAG(name) = \ benchmark::KvPairsFromEnv(#name, default_val) namespace benchmark { @@ -35,6 +40,7 @@ namespace benchmark { // // If the variable exists, returns IsTruthyFlagValue() value; if not, // returns the given default value. +BENCHMARK_EXPORT bool BoolFromEnv(const char* flag, bool default_val); // Parses an Int32 from the environment variable corresponding to the given @@ -42,6 +48,7 @@ bool BoolFromEnv(const char* flag, bool default_val); // // If the variable exists, returns ParseInt32() value; if not, returns // the given default value. +BENCHMARK_EXPORT int32_t Int32FromEnv(const char* flag, int32_t default_val); // Parses an Double from the environment variable corresponding to the given @@ -49,6 +56,7 @@ int32_t Int32FromEnv(const char* flag, int32_t default_val); // // If the variable exists, returns ParseDouble(); if not, returns // the given default value. +BENCHMARK_EXPORT double DoubleFromEnv(const char* flag, double default_val); // Parses a string from the environment variable corresponding to the given @@ -56,6 +64,7 @@ double DoubleFromEnv(const char* flag, double default_val); // // If variable exists, returns its value; if not, returns // the given default value. +BENCHMARK_EXPORT const char* StringFromEnv(const char* flag, const char* default_val); // Parses a set of kvpairs from the environment variable corresponding to the @@ -63,6 +72,7 @@ const char* StringFromEnv(const char* flag, const char* default_val); // // If variable exists, returns its value; if not, returns // the given default value. +BENCHMARK_EXPORT std::map KvPairsFromEnv( const char* flag, std::map default_val); @@ -75,40 +85,47 @@ std::map KvPairsFromEnv( // // On success, stores the value of the flag in *value, and returns // true. On failure, returns false without changing *value. +BENCHMARK_EXPORT bool ParseBoolFlag(const char* str, const char* flag, bool* value); // Parses a string for an Int32 flag, in the form of "--flag=value". // // On success, stores the value of the flag in *value, and returns // true. On failure, returns false without changing *value. +BENCHMARK_EXPORT bool ParseInt32Flag(const char* str, const char* flag, int32_t* value); // Parses a string for a Double flag, in the form of "--flag=value". // // On success, stores the value of the flag in *value, and returns // true. On failure, returns false without changing *value. +BENCHMARK_EXPORT bool ParseDoubleFlag(const char* str, const char* flag, double* value); // Parses a string for a string flag, in the form of "--flag=value". // // On success, stores the value of the flag in *value, and returns // true. On failure, returns false without changing *value. +BENCHMARK_EXPORT bool ParseStringFlag(const char* str, const char* flag, std::string* value); // Parses a string for a kvpairs flag in the form "--flag=key=value,key=value" // // On success, stores the value of the flag in *value and returns true. On // failure returns false, though *value may have been mutated. +BENCHMARK_EXPORT bool ParseKeyValueFlag(const char* str, const char* flag, std::map* value); // Returns true if the string matches the flag. +BENCHMARK_EXPORT bool IsFlag(const char* str, const char* flag); // Returns true unless value starts with one of: '0', 'f', 'F', 'n' or 'N', or // some non-alphanumeric character. Also returns false if the value matches // one of 'no', 'false', 'off' (case-insensitive). As a special case, also // returns true if value is the empty string. +BENCHMARK_EXPORT bool IsTruthyFlagValue(const std::string& value); } // end namespace benchmark diff --git a/src/perf_counters.h b/src/perf_counters.h index 427f08e6..680555d4 100644 --- a/src/perf_counters.h +++ b/src/perf_counters.h @@ -29,6 +29,12 @@ #include #endif +#if defined(_MSC_VER) +#pragma warning(push) +// C4251: needs to have dll-interface to be used by clients of class +#pragma warning(disable : 4251) +#endif + namespace benchmark { namespace internal { @@ -68,7 +74,7 @@ class PerfCounterValues { // Collect PMU counters. The object, once constructed, is ready to be used by // calling read(). PMU counter collection is enabled from the time create() is // called, to obtain the object, until the object's destructor is called. -class PerfCounters final { +class BENCHMARK_EXPORT PerfCounters final { public: // True iff this platform supports performance counters. static const bool kSupported; @@ -125,7 +131,7 @@ class PerfCounters final { }; // Typical usage of the above primitives. -class PerfCountersMeasurement final { +class BENCHMARK_EXPORT PerfCountersMeasurement final { public: PerfCountersMeasurement(const std::vector& counter_names); ~PerfCountersMeasurement(); @@ -184,4 +190,8 @@ BENCHMARK_UNUSED static bool perf_init_anchor = PerfCounters::Initialize(); } // namespace internal } // namespace benchmark +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + #endif // BENCHMARK_PERF_COUNTERS_H diff --git a/src/statistics.h b/src/statistics.h index a9545a58..b0d2c05e 100644 --- a/src/statistics.h +++ b/src/statistics.h @@ -25,12 +25,17 @@ namespace benchmark { // Return a vector containing the mean, median and standard devation information // (and any user-specified info) for the specified list of reports. If 'reports' // contains less than two non-errored runs an empty vector is returned +BENCHMARK_EXPORT std::vector ComputeStats( const std::vector& reports); +BENCHMARK_EXPORT double StatisticsMean(const std::vector& v); +BENCHMARK_EXPORT double StatisticsMedian(const std::vector& v); +BENCHMARK_EXPORT double StatisticsStdDev(const std::vector& v); +BENCHMARK_EXPORT double StatisticsCV(const std::vector& v); } // end namespace benchmark diff --git a/src/string_util.h b/src/string_util.h index ff3b7da4..41458618 100644 --- a/src/string_util.h +++ b/src/string_util.h @@ -5,6 +5,7 @@ #include #include +#include "benchmark/export.h" #include "internal_macros.h" namespace benchmark { @@ -13,6 +14,7 @@ void AppendHumanReadable(int n, std::string* str); std::string HumanReadableNumber(double n, double one_k = 1024.0); +BENCHMARK_EXPORT #if defined(__MINGW32__) __attribute__((format(__MINGW_PRINTF_FORMAT, 1, 2))) #elif defined(__GNUC__) @@ -38,6 +40,7 @@ inline std::string StrCat(Args&&... args) { return ss.str(); } +BENCHMARK_EXPORT std::vector StrSplit(const std::string& str, char delim); // Disable lint checking for this block since it re-implements C functions. diff --git a/test/AssemblyTests.cmake b/test/AssemblyTests.cmake index 3d078586..48318bdd 100644 --- a/test/AssemblyTests.cmake +++ b/test/AssemblyTests.cmake @@ -23,6 +23,7 @@ string(TOUPPER "${CMAKE_CXX_COMPILER_ID}" ASM_TEST_COMPILER) macro(add_filecheck_test name) cmake_parse_arguments(ARG "" "" "CHECK_PREFIXES" ${ARGV}) add_library(${name} OBJECT ${name}.cc) + target_link_libraries(${name} PRIVATE benchmark::benchmark) set_target_properties(${name} PROPERTIES COMPILE_FLAGS "-S ${ASM_TEST_FLAGS}") set(ASM_OUTPUT_FILE "${CMAKE_CURRENT_BINARY_DIR}/${name}.s") add_custom_target(copy_${name} ALL diff --git a/test/BUILD b/test/BUILD index df700a7a..70753db4 100644 --- a/test/BUILD +++ b/test/BUILD @@ -50,9 +50,8 @@ cc_library( "//:benchmark", "//:benchmark_internal_headers", "@com_google_googletest//:gtest", - ] + ( - ["@com_google_googletest//:gtest_main"] if (test_src[-len("gtest.cc"):] == "gtest.cc") else [] - ), + "@com_google_googletest//:gtest_main", + ] # FIXME: Add support for assembly tests to bazel. # See Issue #556 # https://github.com/google/benchmark/issues/556 diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 162af53f..205be911 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,5 +1,7 @@ # Enable the tests +set(THREADS_PREFER_PTHREAD_FLAG ON) + find_package(Threads REQUIRED) include(CheckCXXCompilerFlag) @@ -35,10 +37,11 @@ if (DEFINED BENCHMARK_CXX_LINKER_FLAGS) endif() add_library(output_test_helper STATIC output_test_helper.cc output_test.h) +target_link_libraries(output_test_helper PRIVATE benchmark::benchmark) macro(compile_benchmark_test name) add_executable(${name} "${name}.cc") - target_link_libraries(${name} benchmark::benchmark ${CMAKE_THREAD_LIBS_INIT}) + target_link_libraries(${name} benchmark::benchmark_main ${CMAKE_THREAD_LIBS_INIT}) endmacro(compile_benchmark_test) macro(compile_benchmark_test_with_main name) @@ -48,7 +51,7 @@ endmacro(compile_benchmark_test_with_main) macro(compile_output_test name) add_executable(${name} "${name}.cc" output_test.h) - target_link_libraries(${name} output_test_helper benchmark::benchmark + target_link_libraries(${name} output_test_helper benchmark::benchmark_main ${BENCHMARK_CXX_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) endmacro(compile_output_test) @@ -158,8 +161,8 @@ add_test(NAME user_counters_thousands_test COMMAND user_counters_thousands_test compile_output_test(memory_manager_test) add_test(NAME memory_manager_test COMMAND memory_manager_test --benchmark_min_time=0.01) -check_cxx_compiler_flag(-std=c++03 BENCHMARK_HAS_CXX03_FLAG) -if (BENCHMARK_HAS_CXX03_FLAG) +# MSVC does not allow to set the language standard to C++98/03. +if(NOT CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") compile_benchmark_test(cxx03_test) set_target_properties(cxx03_test PROPERTIES @@ -170,11 +173,17 @@ if (BENCHMARK_HAS_CXX03_FLAG) # causing the test to fail to compile. To prevent this we explicitly disable # the warning. check_cxx_compiler_flag(-Wno-odr BENCHMARK_HAS_WNO_ODR) - if (BENCHMARK_ENABLE_LTO AND BENCHMARK_HAS_WNO_ODR) - set_target_properties(cxx03_test - PROPERTIES - LINK_FLAGS "-Wno-odr") + check_cxx_compiler_flag(-Wno-lto-type-mismatch BENCHMARK_HAS_WNO_LTO_TYPE_MISMATCH) + # Cannot set_target_properties multiple times here because the warnings will + # be overwritten on each call + set (DISABLE_LTO_WARNINGS "") + if (BENCHMARK_HAS_WNO_ODR) + set(DISABLE_LTO_WARNINGS "${DISABLE_LTO_WARNINGS} -Wno-odr") endif() + if (BENCHMARK_HAS_WNO_LTO_TYPE_MISMATCH) + set(DISABLE_LTO_WARNINGS "${DISABLE_LTO_WARNINGS} -Wno-lto-type-mismatch") + endif() + set_target_properties(cxx03_test PROPERTIES LINK_FLAGS "${DISABLE_LTO_WARNINGS}") add_test(NAME cxx03 COMMAND cxx03_test --benchmark_min_time=0.01) endif() diff --git a/test/benchmark_gtest.cc b/test/benchmark_gtest.cc index 14a885ba..cfc0a0f7 100644 --- a/test/benchmark_gtest.cc +++ b/test/benchmark_gtest.cc @@ -8,7 +8,7 @@ namespace benchmark { namespace internal { -extern std::map* global_context; +BENCHMARK_EXPORT extern std::map* global_context; namespace { diff --git a/tools/workspace/generate_export_header.bzl b/tools/workspace/generate_export_header.bzl new file mode 100644 index 00000000..0d0b671d --- /dev/null +++ b/tools/workspace/generate_export_header.bzl @@ -0,0 +1,148 @@ +# +# Originl file is located at: +# https://github.com/RobotLocomotion/drake/blob/bad032aeb09b13c7f8c87ed64b624c8d1e9adb30/tools/workspace/generate_export_header.bzl +# +# All components of Drake are licensed under the BSD 3-Clause License +# shown below. Where noted in the source code, some portions may +# be subject to other permissive, non-viral licenses. +# +# Copyright 2012-2016 Robot Locomotion Group @ CSAIL +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. Redistributions +# in binary form must reproduce the above copyright notice, this list of +# conditions and the following disclaimer in the documentation and/or +# other materials provided with the distribution. Neither the name of +# the Massachusetts Institute of Technology nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# -*- python -*- + +def _make_identifier(s): + result = "" + for i in range(len(s)): + result += s[i] if s[i].isalnum() else "_" + + return result + +# Defines the implementation actions to generate_export_header. +def _generate_export_header_impl(ctx): + output = ctx.outputs.out + + guard = _make_identifier(output.basename.upper()) + + content = [ + "#ifndef %s" % guard, + "#define %s" % guard, + "", + "#ifdef %s" % ctx.attr.static_define, + "# define %s" % ctx.attr.export_macro_name, + "# define %s" % ctx.attr.no_export_macro_name, + "#else", + "# define %s __attribute__((visibility(\"default\")))" % ctx.attr.export_macro_name, # noqa + "# define %s __attribute__((visibility(\"hidden\")))" % ctx.attr.no_export_macro_name, # noqa + "#endif", + "", + "#ifndef %s" % ctx.attr.deprecated_macro_name, + "# define %s __attribute__ ((__deprecated__))" % ctx.attr.deprecated_macro_name, # noqa + "#endif", + "", + "#ifndef %s" % ctx.attr.export_deprecated_macro_name, + "# define %s %s %s" % (ctx.attr.export_deprecated_macro_name, ctx.attr.export_macro_name, ctx.attr.deprecated_macro_name), # noqa + "#endif", + "", + "#ifndef %s" % ctx.attr.no_export_deprecated_macro_name, + "# define %s %s %s" % (ctx.attr.no_export_deprecated_macro_name, ctx.attr.no_export_macro_name, ctx.attr.deprecated_macro_name), # noqa + "#endif", + "", + "#endif", + ] + + ctx.actions.write(output = output, content = "\n".join(content) + "\n") + +# Defines the rule to generate_export_header. +_generate_export_header_gen = rule( + attrs = { + "out": attr.output(mandatory = True), + "export_macro_name": attr.string(), + "deprecated_macro_name": attr.string(), + "export_deprecated_macro_name": attr.string(), + "no_export_macro_name": attr.string(), + "no_export_deprecated_macro_name": attr.string(), + "static_define": attr.string(), + }, + output_to_genfiles = True, + implementation = _generate_export_header_impl, +) + +def generate_export_header( + lib = None, + name = None, + out = None, + export_macro_name = None, + deprecated_macro_name = None, + export_deprecated_macro_name = None, + no_export_macro_name = None, + no_export_deprecated_macro_name = None, + static_define = None, + **kwargs): + """Creates a rule to generate an export header for a named library. This + is an incomplete implementation of CMake's generate_export_header. (In + particular, it assumes a platform that uses + __attribute__((visibility("default"))) to decorate exports.) + + By default, the rule will have a mangled name related to the library name, + and will produce "_export.h". + + The CMake documentation of the generate_export_header macro is: + https://cmake.org/cmake/help/latest/module/GenerateExportHeader.html + + """ + + if name == None: + name = "__%s_export_h" % lib + if out == None: + out = "%s_export.h" % lib + if export_macro_name == None: + export_macro_name = "%s_EXPORT" % lib.upper() + if deprecated_macro_name == None: + deprecated_macro_name = "%s_DEPRECATED" % lib.upper() + if export_deprecated_macro_name == None: + export_deprecated_macro_name = "%s_DEPRECATED_EXPORT" % lib.upper() + if no_export_macro_name == None: + no_export_macro_name = "%s_NO_EXPORT" % lib.upper() + if no_export_deprecated_macro_name == None: + no_export_deprecated_macro_name = \ + "%s_DEPRECATED_NO_EXPORT" % lib.upper() + if static_define == None: + static_define = "%s_STATIC_DEFINE" % lib.upper() + + _generate_export_header_gen( + name = name, + out = out, + export_macro_name = export_macro_name, + deprecated_macro_name = deprecated_macro_name, + export_deprecated_macro_name = export_deprecated_macro_name, + no_export_macro_name = no_export_macro_name, + no_export_deprecated_macro_name = no_export_deprecated_macro_name, + static_define = static_define, + **kwargs + )