Skip to content

Commit

Permalink
feat: Use macros to determine compiler version. (googleapis/google-cl…
Browse files Browse the repository at this point in the history
…oud-cpp-spanner#298)

* feat: Use macros to determine compiler version.

Fixes googleapis/google-cloud-cpp-spanner#294 and closes googleapis/google-cloud-cpp-spanner#281

The logic for determining the compiler ID and version are lifted from the CMake
v3.5 sources (the minimum we support). Only GCC, Clang and MSVC are supported
but it is trivial to support more. This breaks the logic out into a separate
file, although it is possible I misunderstood the requirements and I'm happy to
correct it.

cc: @devjgm

* Address review comments.

* Address review comments.

* Fix typo.

* Fix another typo.

* Address review comment.
  • Loading branch information
remyabel authored and coryan committed Aug 4, 2019
1 parent cfbe1fd commit df2fd85
Show file tree
Hide file tree
Showing 10 changed files with 237 additions and 126 deletions.
10 changes: 1 addition & 9 deletions google/cloud/spanner/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,7 @@ genrule(
outs = ["internal/build_info.cc"],
cmd = """
V="unknown";
CC_VERSION=$$($(location //bazel:compiler_version.sh) $(CC));
CC_ID=$$($(location //bazel:compiler_id.sh) $(CC));
sed -e "s;@CMAKE_CXX_COMPILER_ID@;$${CC_ID};" \
-e "s;@CMAKE_CXX_COMPILER_VERSION@;$${CC_VERSION};" \
-e "s;@CMAKE_CXX_FLAGS@;$(CC_FLAGS);" \
sed -e "s;@CMAKE_CXX_FLAGS@;$(CC_FLAGS);" \
-e "s;\\$${CMAKE_CXX_FLAGS_.*};-c $(COMPILATION_MODE);" \
-e "s;@GOOGLE_CLOUD_CPP_SPANNER_IS_RELEASE@;%s;" \
-e "s;@GOOGLE_CLOUD_CPP_SPANNER_BUILD_METADATA@;$${V};" < $< > $@
Expand All @@ -37,10 +33,6 @@ sed -e "s;@CMAKE_CXX_COMPILER_ID@;$${CC_ID};" \
"@bazel_tools//tools/cpp:current_cc_toolchain",
"@bazel_tools//tools/cpp:cc_flags",
],
tools = [
"//bazel:compiler_id.sh",
"//bazel:compiler_version.sh",
],
)

load(":spanner_client.bzl", "spanner_client_hdrs", "spanner_client_srcs")
Expand Down
3 changes: 3 additions & 0 deletions google/cloud/spanner/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ add_library(spanner_client
internal/base64.cc
internal/base64.h
internal/build_info.h
internal/compiler_info.cc
internal/compiler_info.h
internal/database_admin_retry.cc
internal/database_admin_retry.h
internal/database_admin_stub.cc
Expand Down Expand Up @@ -197,6 +199,7 @@ function (spanner_client_define_tests)
date_test.cc
internal/base64_test.cc
internal/build_info_test.cc
internal/compiler_info_test.cc
internal/date_test.cc
internal/merge_chunk_test.cc
internal/polling_loop_test.cc
Expand Down
36 changes: 0 additions & 36 deletions google/cloud/spanner/internal/build_info.cc.in
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,6 @@ namespace spanner {
inline namespace SPANNER_CLIENT_NS {
namespace internal {

std::string CompilerId() { return R"""(@CMAKE_CXX_COMPILER_ID@)"""; }

std::string CompilerVersion() { return R"""(@CMAKE_CXX_COMPILER_VERSION@)"""; }

std::string BuildFlags() {
static auto const* const kFlags = [] {
auto* flags = new std::string(R"""(@CMAKE_CXX_FLAGS@)""");
Expand All @@ -39,38 +35,6 @@ std::string BuildFlags() {
return *kFlags;
}

std::string CompilerFeatures() {
#if GOOGLE_CLOUD_CPP_HAVE_EXCEPTIONS
return "ex";
#else
return "noex";
#endif // GOOGLE_CLOUD_CPP_HAVE_EXCEPTIONS
}

std::string LanguageVersion() {
static auto const* const kLanguageVersion = [] {
auto* s = new std::string;
switch (__cplusplus) {
case 199711L:
*s += "98";
break;
case 201103L:
*s += "2011";
break;
case 201402L:
*s += "2014";
break;
case 201703L:
*s += "2017";
break;
default:
*s += "unknown";
}
return s;
}();
return *kLanguageVersion;
}

bool IsRelease() {
static bool const kIsRelease = []() -> bool {
// NOLINTNEXTLINE(readability-redundant-string-init)
Expand Down
28 changes: 0 additions & 28 deletions google/cloud/spanner/internal/build_info.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,41 +23,13 @@ namespace spanner {
inline namespace SPANNER_CLIENT_NS {
namespace internal {

/**
* Returns the compiler ID.
*
* The Compiler ID is a string like "GNU" or "Clang", as described by
* https://cmake.org/cmake/help/v3.5/variable/CMAKE_LANG_COMPILER_ID.html
*/
std::string CompilerId();

/**
* Returns the compiler version.
*
* This string will be something like "9.1.1".
*/
std::string CompilerVersion();

/**
* Returns the build flags.
*
* Examples include "-c fastbuild" or "-O2 -DNDEBUG".
*/
std::string BuildFlags();

/**
* Returns certain interesting compiler features.
*
* Currently this returns one of "ex" or "noex" to indicate whether or not
* C++ exceptions are enabled.
*/
std::string CompilerFeatures();

/**
* Returns the 4-digit year of the C++ language standard.
*/
std::string LanguageVersion();

/**
* Returns true if this is a release branch.
*/
Expand Down
53 changes: 0 additions & 53 deletions google/cloud/spanner/internal/build_info_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -22,44 +22,11 @@ inline namespace SPANNER_CLIENT_NS {
namespace internal {
namespace {

TEST(BuildInfo, CompilerId) {
auto cn = CompilerId();
EXPECT_FALSE(cn.empty());
EXPECT_THAT(cn, ::testing::Not(::testing::HasSubstr("@")));
}

TEST(BuildInfo, CompilerVersion) {
auto cv = CompilerVersion();
EXPECT_FALSE(cv.empty());
EXPECT_THAT(cv, ::testing::Not(::testing::HasSubstr("@")));
#ifndef _WIN32 // gMock's regex brackets don't work on Windows.
// Look for something that looks vaguely like an X.Y version number.
EXPECT_THAT(cv, ::testing::ContainsRegex(R"([0-9].[0-9])"));
#endif
}

TEST(BuildInfo, BuildFlags) {
auto bf = BuildFlags();
EXPECT_THAT(bf, ::testing::Not(::testing::HasSubstr("@")));
}

TEST(BuildInfo, CompilerFeatures) {
using ::testing::Eq;
auto cf = CompilerFeatures();
EXPECT_FALSE(cf.empty());
EXPECT_THAT(cf, ::testing::AnyOf(Eq("noex"), Eq("ex")));
}

TEST(BuildInfo, LanguageVersion) {
using ::testing::HasSubstr;
auto lv = LanguageVersion();
EXPECT_FALSE(lv.empty());
EXPECT_THAT(lv, ::testing::Not(::testing::HasSubstr("@")));
#ifndef _WIN32 // gMock's regex brackets don't work on Windows.
EXPECT_THAT(lv, ::testing::MatchesRegex(R"([0-9A-Za-z_.-]+)"));
#endif
}

TEST(BuildInfo, IsRelease) {
bool const b = IsRelease();
// We want to test this, but either value is fine.
Expand All @@ -72,26 +39,6 @@ TEST(BuildInfo, BuildMetadata) {
EXPECT_THAT(md, ::testing::Not(::testing::HasSubstr("@")));
}

TEST(BuildInfo, ApiClientHeader) {
// The build info is used to generate the "API Client Header", which is a
// gRPC metadata attribute with the name 'x-goog-api-client'. This test
// generates that whole string as a sanity check that it will contain the
// desired format.

std::string const api_client_header = "gl-cpp/" + //
CompilerId() + '-' + //
CompilerVersion() + '-' + //
CompilerFeatures() + '-' + //
LanguageVersion();
EXPECT_THAT(api_client_header, ::testing::Not(::testing::HasSubstr("@")));

#ifndef _WIN32 // gMock's regex brackets don't work on Windows.
EXPECT_THAT(api_client_header,
::testing::MatchesRegex(
R"(gl-cpp/[A-Za-z0-9]+-[0-9.+-]+-(no)?ex-20[1-9][0-9])"));
#endif
}

} // namespace
} // namespace internal
} // namespace SPANNER_CLIENT_NS
Expand Down
107 changes: 107 additions & 0 deletions google/cloud/spanner/internal/compiler_info.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "google/cloud/spanner/internal/build_info.h"
#include "google/cloud/internal/port_platform.h"
#include <algorithm>
#include <cctype>
#include <iterator>
#include <sstream>

namespace google {
namespace cloud {
namespace spanner {
inline namespace SPANNER_CLIENT_NS {
namespace internal {

/**
* The macros for determining the compiler ID are taken from:
* https://gitlab.kitware.com/cmake/cmake/tree/v3.5.0/Modules/Compiler/\*-DetermineCompiler.cmake
* We do not care to detect every single compiler possible and only target the
* most popular ones.
*
* Order is significant as some compilers can define the same macros.
*/
std::string CompilerId() {
#if defined(__apple_build_version__) && defined(__clang__)
return "AppleClang";
#elif defined(__clang__)
return "Clang";
#elif defined(__GNUC__)
return "GNU";
#elif defined(_MSC_VER)
return "MSVC";
#endif

return "Unknown";
}

std::string CompilerVersion() {
std::ostringstream os;

#if defined(__apple_build_version__) && defined(__clang__)
os << __clang_major__ << "." << __clang_minor__ << "." << __clang_patchlevel__
<< "." << __apple_build_version__;
return std::move(os).str();
#elif defined(__clang__)
os << __clang_major__ << "." << __clang_minor__ << "."
<< __clang_patchlevel__;
return std::move(os).str();
#elif defined(__GNUC__)
os << __GNUC__ << "." << __GNUC_MINOR__ << "." << __GNUC_PATCHLEVEL__;
return std::move(os).str();
#elif defined(_MSC_VER)
os << _MSC_VER / 100 << ".";
os << _MSC_VER % 100;
#if defined(_MSC_FULL_VER)
#if _MSC_VER >= 1400
os << "." << _MSC_FULL_VER % 100000;
#else
os << "." << _MSC_FULL_VER % 10000;
#endif
#endif
return std::move(os).str();
#endif

return "Unknown";
}

std::string CompilerFeatures() {
#if GOOGLE_CLOUD_CPP_HAVE_EXCEPTIONS
return "ex";
#else
return "noex";
#endif // GOOGLE_CLOUD_CPP_HAVE_EXCEPTIONS
}

std::string LanguageVersion() {
switch (__cplusplus) {
case 199711L:
return "98";
case 201103L:
return "2011";
case 201402L:
return "2014";
case 201703L:
return "2017";
default:
return "unknown";
}
}

} // namespace internal
} // namespace SPANNER_CLIENT_NS
} // namespace spanner
} // namespace cloud
} // namespace google
60 changes: 60 additions & 0 deletions google/cloud/spanner/internal/compiler_info.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef GOOGLE_CLOUD_CPP_SPANNER_GOOGLE_CLOUD_SPANNER_INTERNAL_COMPILER_INFO_H_
#define GOOGLE_CLOUD_CPP_SPANNER_GOOGLE_CLOUD_SPANNER_INTERNAL_COMPILER_INFO_H_

#include "google/cloud/spanner/version.h"

namespace google {
namespace cloud {
namespace spanner {
inline namespace SPANNER_CLIENT_NS {
namespace internal {

/**
* Returns the compiler ID.
*
* The Compiler ID is a string like "GNU" or "Clang", as described by
* https://cmake.org/cmake/help/v3.5/variable/CMAKE_LANG_COMPILER_ID.html
*/
std::string CompilerId();

/**
* Returns the compiler version.
*
* This string will be something like "9.1.1".
*/
std::string CompilerVersion();

/**
* Returns certain interesting compiler features.
*
* Currently this returns one of "ex" or "noex" to indicate whether or not
* C++ exceptions are enabled.
*/
std::string CompilerFeatures();

/**
* Returns the 4-digit year of the C++ language standard.
*/
std::string LanguageVersion();

} // namespace internal
} // namespace SPANNER_CLIENT_NS
} // namespace spanner
} // namespace cloud
} // namespace google

#endif // GOOGLE_CLOUD_CPP_SPANNER_GOOGLE_CLOUD_SPANNER_INTERNAL_COMPILER_INFO_H_
Loading

0 comments on commit df2fd85

Please sign in to comment.