Fix the linking of various tests against GMock
[folly.git] / folly / VersionCheck.h
1 /*
2  * Copyright 2017 Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #pragma once
18
19 #include <cstdio>
20 #include <cstdlib>
21 #include <cstring>
22
23 #include <folly/Portability.h>
24 #include <folly/Preprocessor.h>
25
26 /**
27  * Check if the currently loaded version of a library is what you expect.
28  *
29  * It is possible for multiple versions of the same shared library to end up
30  * being loaded simultaneously in the same address space, usually with
31  * disastrous results.
32  *
33  * For example, let's say you have a shared library (foo) that doesn't keep
34  * binary compatbility between releases, and so each version is distributed as
35  * a SO with different SONAME. Let's say you build another shared library, bar
36  * that depends on version 1 of foo: libbar.so depends on libfoo1.so.
37  * Your main executable now (baz) depends on version 2 of foo, and also
38  * depends on bar: baz depends on libfoo2.so and libbar.so.
39  *
40  * At load time, baz loads libfoo2.so first, then libbar.so; libbar.so will
41  * load libfoo1.so, but, as this is normal dynamic loading (and not explicit
42  * dlopen calls with RTLD_DEEPBIND), any symbols from libfoo1.so that are
43  * also present in libfoo2.so will be satisfied from the (already loaded)
44  * libfoo2.so.
45  *
46  * But foo does not preserve binary compatibility between versions, so all
47  * hell breaks loose (the symbols from libfoo2.so are not necessarily direct
48  * replacements of the identically-named symbols in libfoo1.so).
49  *
50  * It is better to crash with a helpful error message instead, which is what
51  * this macro provides. FOLLY_VERSION_CHECK verifies at load time that
52  * the compiled-in version is the same as the currently loaded version.
53  *
54  * Usage: use this macro at namespace scope in a .cpp file (IMPORTANT: NOT
55  * in the unnamed namespace):
56  *
57  * FOLLY_VERSION_CHECK(mylib, "1")
58  *
59  * The first argument identifies your library; the second argument is a
60  * string literal containing the desired version string.
61  *
62  * In order to avoid changing the file for each version, the version string
63  * could be provided on the compiler command line with -D:
64  *
65  * FOLLY_VERSION_CHECK(mylib, MYLIB_VERSION)
66  *
67  * ... and then commpile your file with -DMYLIB_VERSION=\"1\"
68  */
69
70 #if defined(_MSC_VER)
71 // MSVC doesn't support constructor priorities. Just pray it works, I guess.
72 // We could implement a link-time mechanism for MSVC,
73 // via #pragma detect_mismatch but that would only handle
74 // static library linking.
75 # define FOLLY_VERSION_CHECK_PRIORITY(Ret, name) \
76     __pragma(section(".CRT$XCU",read)) \
77     static Ret __cdecl name(void); \
78     __declspec(allocate(".CRT$XCU")) \
79     Ret (__cdecl*name##_)(void) = name; \
80     Ret __cdecl name()
81
82 #elif defined(__APPLE__)
83 // OS X doesn't support constructor priorities. Just pray it works, I guess.
84 # define FOLLY_VERSION_CHECK_PRIORITY(Ret, name) \
85   __attribute__((__constructor__)) Ret name()
86
87 #else
88 # define FOLLY_VERSION_CHECK_PRIORITY(Ret, name) \
89   __attribute__((__constructor__(101))) Ret name()
90 #endif
91
92 // Note that this is carefully crafted: PRODUCT##Version must have external
93 // linkage (so it collides among versions), versionCheck must have internal
94 // linkage (so it does NOT collide between versions); if we're trying to have
95 // multiple versions loaded at the same time, they must each run their copy
96 // of versionCheck, but share the PRODUCT##Version variable.
97 #define FOLLY_VERSION_CHECK(PRODUCT, VERSION) \
98   const char* PRODUCT##Version = VERSION; \
99   namespace { \
100   FOLLY_VERSION_CHECK_PRIORITY(void, versionCheck) { \
101     if (strcmp(PRODUCT##Version, VERSION)) { \
102       fprintf(stderr, \
103               "Invalid %s version: desired [%s], currently loaded [%s]\n", \
104               FB_STRINGIZE(PRODUCT), PRODUCT##Version, VERSION); \
105       abort(); \
106     } \
107   } \
108   }