Skip to content

Commit 721c280

Browse files
var-consta-maurice
authored andcommitted
Add logic to detect the compiler version and the C++ language standard used.
This is largely adapted from the Cloud C++ APIs with the following changes: * added C++20 to the list of possible standards; * added "latest" as a possible C++ standard for MSVC; * added the C++ standard library vendor to the compiler info string, primarily to track STLPort. Here are some sample values I'm getting: * on my Mac laptop: `AppleClang-11.0.3.11030032-ex-2011-libcpp`; * google3 Android build: `Clang-9.0.8-ex-2017-libcpp`; * google3 Windows build: `MSVC-19.22.27905-ex-2017-msvc`; * google3 Linux build: `Clang-9999.0.0-noex-2017-libcpp` (unfortunately, Clang trunk in google3 simply sets the version to 9999: https://source.corp.google.com/piper///depot/google3/third_party/llvm/llvm-project/clang/BUILD;l=374-383;rcl=330443088) Note that as long as the C++ SDKs are distributed in precompiled form, the compiler version reported will be from the compiler we used to compile the binaries, not the compiler used by our users. Unfortunately, there is no clear way to avoid this that wouldn't cause an ODR violation (if e.g. `CompilerId` were placed in a header as an inline function, it would end up having different definitions depending on whether it's included internally or by the user (indirectly)). However, this shouldn't be a big issue as the metrics will still allow tracking total C++ usage and, to an extent, usage by platform. PiperOrigin-RevId: 331654266
1 parent f8240f7 commit 721c280

File tree

3 files changed

+258
-0
lines changed

3 files changed

+258
-0
lines changed
Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
// Copyright 2020 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "firestore/src/common/compiler_info.h"
16+
17+
#include <ostream>
18+
#include <sstream>
19+
20+
#include "app/meta/move.h"
21+
22+
namespace firebase {
23+
namespace firestore {
24+
25+
namespace {
26+
27+
// The code in this file is adapted from
28+
// https://github.com/googleapis/google-cloud-cpp/blob/7f418183fc4f07cd76995bd12f7c1971b8e057ac/google/cloud/internal/compiler_info.cc
29+
// and
30+
// https://github.com/googleapis/google-cloud-cpp/blob/7f418183fc4f07cd76995bd12f7c1971b8e057ac/google/cloud/internal/port_platform.h.
31+
32+
/**
33+
* Returns the compiler ID.
34+
*
35+
* The Compiler ID is a string like "GNU" or "Clang", as described by
36+
* https://cmake.org/cmake/help/v3.5/variable/CMAKE_LANG_COMPILER_ID.html
37+
*/
38+
struct CompilerId {};
39+
40+
std::ostream& operator<<(std::ostream& os, CompilerId) {
41+
// The macros for determining the compiler ID are taken from:
42+
// https://gitlab.kitware.com/cmake/cmake/tree/v3.5.0/Modules/Compiler/\*-DetermineCompiler.cmake
43+
// We do not care to detect every single compiler possible and only target
44+
// the most popular ones.
45+
//
46+
// Order is significant as some compilers can define the same macros.
47+
48+
#if defined(__apple_build_version__) && defined(__clang__)
49+
os << "AppleClang";
50+
#elif defined(__clang__)
51+
os << "Clang";
52+
#elif defined(__GNUC__)
53+
os << "GNU";
54+
#elif defined(_MSC_VER)
55+
os << "MSVC";
56+
#else
57+
os << "Unknown";
58+
#endif
59+
return os;
60+
}
61+
62+
/** Returns the compiler version. This string will be something like "9.1.1". */
63+
struct CompilerVersion {};
64+
65+
std::ostream& operator<<(std::ostream& os, CompilerVersion) {
66+
#if defined(__apple_build_version__) && defined(__clang__)
67+
os << __clang_major__ << "." << __clang_minor__ << "." << __clang_patchlevel__
68+
<< "." << __apple_build_version__;
69+
70+
#elif defined(__clang__)
71+
os << __clang_major__ << "." << __clang_minor__ << "."
72+
<< __clang_patchlevel__;
73+
74+
#elif defined(__GNUC__)
75+
os << __GNUC__ << "." << __GNUC_MINOR__ << "." << __GNUC_PATCHLEVEL__;
76+
77+
#elif defined(_MSC_VER)
78+
os << _MSC_VER / 100 << ".";
79+
os << _MSC_VER % 100;
80+
#if defined(_MSC_FULL_VER)
81+
#if _MSC_VER >= 1400
82+
os << "." << _MSC_FULL_VER % 100000;
83+
#else
84+
os << "." << _MSC_FULL_VER % 10000;
85+
#endif // _MSC_VER >= 1400
86+
#endif // defined(_MSC_VER)
87+
88+
#else
89+
os << "Unknown";
90+
91+
#endif // defined(__apple_build_version__) && defined(__clang__)
92+
93+
return os;
94+
}
95+
96+
// Discover if exceptions are enabled and define them as needed.
97+
#if defined(__clang__)
98+
#if defined(__EXCEPTIONS) && __has_feature(cxx_exceptions)
99+
#define FIRESTORE_HAVE_EXCEPTIONS 1
100+
#endif // defined(__EXCEPTIONS) && __has_feature(cxx_exceptions)
101+
102+
#elif defined(_MSC_VER)
103+
#if defined(_CPPUNWIND)
104+
#define FIRESTORE_HAVE_EXCEPTIONS 1
105+
#endif // defined(_CPPUNWIND)
106+
107+
#elif defined(__GNUC__)
108+
#if (__GNUC__ < 5) && defined(__EXCEPTIONS)
109+
#define FIRESTORE_HAVE_EXCEPTIONS 1
110+
#elif (__GNUC__ >= 5) && defined(__cpp_exceptions)
111+
#define FIRESTORE_HAVE_EXCEPTIONS 1
112+
#endif // (__GNUC__ >= 5) && defined(__cpp_exceptions)
113+
114+
#elif defined(__cpp_exceptions)
115+
// This should work in increasingly more and more compilers.
116+
// https://isocpp.org/std/standing-documents/sd-6-sg10-feature-test-recommendations
117+
#define FIRESTORE_HAVE_EXCEPTIONS 1
118+
#endif // FIRESTORE_HAVE_EXCEPTIONS
119+
120+
/**
121+
* Returns certain interesting compiler features.
122+
*
123+
* Currently this returns one of "ex" or "noex" to indicate whether or not
124+
* C++ exceptions are enabled.
125+
*/
126+
struct CompilerFeatures {};
127+
128+
std::ostream& operator<<(std::ostream& os, CompilerFeatures) {
129+
#if FIRESTORE_HAVE_EXCEPTIONS
130+
os << "ex";
131+
#else
132+
os << "noex";
133+
#endif // FIRESTORE_HAVE_EXCEPTIONS
134+
return os;
135+
}
136+
137+
// Microsoft Visual Studio does not define `__cplusplus` correctly by default:
138+
// https://devblogs.microsoft.com/cppblog/msvc-now-correctly-reports-__cplusplus
139+
// Instead, `_MSVC_LANG` macro can be used which uses the same version numbers
140+
// as the standard `__cplusplus` macro (except when the `/std:c++latest` option
141+
// is used, in which case it will be higher).
142+
#ifdef _MSC_VER
143+
#define FIRESTORE__CPLUSPLUS _MSVC_LANG
144+
#else
145+
#define FIRESTORE__CPLUSPLUS __cplusplus
146+
#endif // _MSC_VER
147+
148+
/** Returns the 4-digit year of the C++ language standard. */
149+
struct LanguageVersion {};
150+
151+
std::ostream& operator<<(std::ostream& os, LanguageVersion) {
152+
switch (FIRESTORE__CPLUSPLUS) {
153+
case 199711L:
154+
os << "1998";
155+
break;
156+
case 201103L:
157+
os << "2011";
158+
break;
159+
case 201402L:
160+
os << "2014";
161+
break;
162+
case 201703L:
163+
os << "2017";
164+
break;
165+
case 202002L:
166+
os << "2020";
167+
break;
168+
default:
169+
#ifdef _MSC_VER
170+
// According to
171+
// https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros,
172+
// _MSVC_LANG is "set to a higher, unspecified value when the
173+
// `/std:c++latest` option is specified".
174+
if (FIRESTORE__CPLUSPLUS > 202002L) {
175+
os << "latest";
176+
break;
177+
}
178+
#endif // _MSC_VER
179+
os << "unknown";
180+
break;
181+
}
182+
183+
return os;
184+
}
185+
186+
struct StandardLibraryVendor {};
187+
188+
std::ostream& operator<<(std::ostream& os, StandardLibraryVendor) {
189+
#if defined(_STLPORT_VERSION)
190+
os << "stlport";
191+
#elif defined(__GLIBCXX__) || defined(__GLIBCPP__)
192+
os << "gnustl";
193+
#elif defined(_LIBCPP_STD_VER)
194+
os << "libcpp";
195+
#elif defined(_MSC_VER)
196+
os << "msvc";
197+
#else
198+
os << "unknown";
199+
#endif
200+
return os;
201+
}
202+
203+
} // namespace
204+
205+
std::string GetFullCompilerInfo() {
206+
std::ostringstream os;
207+
os << CompilerId() << "-" << CompilerVersion() << "-" << CompilerFeatures()
208+
<< "-" << LanguageVersion() << "-" << StandardLibraryVendor();
209+
return Move(os).str();
210+
}
211+
212+
} // namespace firestore
213+
} // namespace firebase
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Copyright 2020 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#ifndef FIREBASE_FIRESTORE_CLIENT_CPP_SRC_COMMON_COMPILER_INFO_H_
16+
#define FIREBASE_FIRESTORE_CLIENT_CPP_SRC_COMMON_COMPILER_INFO_H_
17+
18+
#include <string>
19+
20+
namespace firebase {
21+
namespace firestore {
22+
23+
// Returns a string describing the compiler version and settings in the
24+
// following format:
25+
//
26+
// <CompilerId>-<CompilerVersion>-<CompilerFeatures>-<LanguageVersion>-<StandardLibraryVersion>
27+
//
28+
// e.g. "AppleClang-11.0.3.11030032-ex-2011-libcpp".
29+
//
30+
// The format is based on what is used by Cloud C++ libraries:
31+
// https://github.com/googleapis/google-cloud-cpp/blob/211006b86c841f2226fedf2f7ae6ced482aa2cc0/google/cloud/internal/api_client_header.cc#L23-L29
32+
// with the addition of <StandardLibraryVersion> (e.g. "libcpp").
33+
std::string GetFullCompilerInfo();
34+
35+
} // namespace firestore
36+
} // namespace firebase
37+
38+
#endif // FIREBASE_FIRESTORE_CLIENT_CPP_SRC_COMMON_COMPILER_INFO_H_

firestore/src/common/firestore.cc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "app/src/include/firebase/version.h"
1010
#include "app/src/log.h"
1111
#include "app/src/util.h"
12+
#include "firestore/src/common/compiler_info.h"
1213
#include "firestore/src/common/futures.h"
1314

1415
#if defined(__ANDROID__)
@@ -123,6 +124,12 @@ Firestore::Firestore(FirestoreInternal* internal)
123124
: internal_(internal) {
124125
internal_->set_firestore_public(this);
125126

127+
// Note: because Firestore libraries are currently distributed in
128+
// a precompiled form, `GetFullCompilerInfo` will reflect the compiler used to
129+
// produce the binaries. Unfortunately, there is no clear way to avoid that
130+
// without breaking ODR.
131+
SetClientLanguage(std::string("gl-cpp/") + GetFullCompilerInfo());
132+
126133
if (internal_->initialized()) {
127134
CleanupNotifier* app_notifier = CleanupNotifier::FindByOwner(app());
128135
assert(app_notifier);

0 commit comments

Comments
 (0)