summaryrefslogtreecommitdiff
path: root/cmake/libutils.cmake
blob: 002d5a4cabc22d925b3e65daf9b609602e477489 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
# Copyright (c) 2009, 2010, Oracle and/or its affiliates.
# 
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 2 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1335  USA 


# This file exports macros that emulate some functionality found  in GNU libtool
# on Unix systems. One such feature is convenience libraries. In this context,
# convenience library is a static library that can be linked to shared library
# On systems that force position-independent code, linking into shared library
# normally requires compilation with a special flag (often -fPIC). To enable 
# linking static libraries to shared, we compile source files that come into 
# static library with the PIC flag (${CMAKE_SHARED_LIBRARY_C_FLAGS} in CMake)
# Some systems, like Windows or OSX do not need special compilation (Windows 
# never uses PIC and OSX always uses it). 
#
# The intention behind convenience libraries is simplify the build and to reduce
# excessive recompiles.

# Except for convenience libraries, this file provides macros to merge static 
# libraries (we need it for mysqlclient) and to create shared library out of 
# convenience libraries(again, for mysqlclient)

# Following macros are exported
# - ADD_CONVENIENCE_LIBRARY(target source1...sourceN)
# This macro creates convenience library. The functionality is similar to 
# ADD_LIBRARY(target STATIC source1...sourceN), the difference is that resulting 
# library can always be linked to shared library
# 
# - MERGE_LIBRARIES(target [STATIC|SHARED|MODULE]  [linklib1 .... linklibN]
#  [EXPORTS exported_func1 .... exported_func_N]
#  [OUTPUT_NAME output_name]
# This macro merges several static libraries into a single one or creates a shared
# library from several convenience libraries

# Important global flags 
# - WITH_PIC : If set, it is assumed that everything is compiled as position
# independent code (that is CFLAGS/CMAKE_C_FLAGS contain -fPIC or equivalent)
# If defined, ADD_CONVENIENCE_LIBRARY does not add PIC flag to compile flags
#
# - DISABLE_SHARED: If set, it is assumed that shared libraries are not produced
# during the build. ADD_CONVENIENCE_LIBRARY does not add anything to compile flags


GET_FILENAME_COMPONENT(MYSQL_CMAKE_SCRIPT_DIR ${CMAKE_CURRENT_LIST_FILE} PATH)
IF(WIN32 OR CYGWIN OR APPLE OR WITH_PIC OR DISABLE_SHARED OR NOT CMAKE_SHARED_LIBRARY_C_FLAGS)
 SET(_SKIP_PIC 1)
ENDIF()

INCLUDE(CMakeParseArguments)
# CREATE_EXPORTS_FILE (VAR target api_functions)
# Internal macro, used to create source file for shared libraries that 
# otherwise consists entirely of "convenience" libraries. On Windows, 
# also exports API functions as dllexport. On unix, creates a dummy file 
# that references all exports and this prevents linker from creating an 
# empty library(there are unportable alternatives, --whole-archive)
MACRO(CREATE_EXPORTS_FILE VAR TARGET API_FUNCTIONS)
  IF(WIN32)
    SET(DUMMY ${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_dummy.c)
    SET(EXPORTS ${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_exports.def)
    CONFIGURE_FILE_CONTENT("" ${DUMMY})
    SET(CONTENT "EXPORTS\n")
    FOREACH(FUNC ${API_FUNCTIONS})
      SET(CONTENT "${CONTENT} ${FUNC}\n")
    ENDFOREACH()
    CONFIGURE_FILE_CONTENT(${CONTENT} ${EXPORTS})
    SET(${VAR} ${DUMMY} ${EXPORTS})
  ELSE()
    SET(EXPORTS ${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_exports_file.cc)
    SET(CONTENT)
    FOREACH(FUNC ${API_FUNCTIONS})
      SET(CONTENT "${CONTENT} extern void* ${FUNC}\;\n")
    ENDFOREACH()
    SET(CONTENT "${CONTENT} void *${TARGET}_api_funcs[] = {\n")
    FOREACH(FUNC ${API_FUNCTIONS})
     SET(CONTENT "${CONTENT} &${FUNC},\n")
    ENDFOREACH()
    SET(CONTENT "${CONTENT} (void *)0\n}\;")
    CONFIGURE_FILE_CONTENT(${CONTENT} ${EXPORTS})
    # Avoid "function redeclared as variable" error
    # when using gcc/clang option -flto(link time optimization)
    IF(" ${CMAKE_C_FLAGS} ${CMAKE_CXX_FLAGS} " MATCHES " -flto")
      SET_SOURCE_FILES_PROPERTIES(${EXPORTS} PROPERTIES COMPILE_FLAGS "-fno-lto")
    ENDIF()
    SET(${VAR} ${EXPORTS})
  ENDIF()
ENDMACRO()


# MYSQL_ADD_CONVENIENCE_LIBRARY(name source1...sourceN)
# Create static library that can be linked to shared library.
# On systems that force position-independent code, adds -fPIC or 
# equivalent flag to compile flags.
MACRO(ADD_CONVENIENCE_LIBRARY)
  SET(TARGET ${ARGV0})
  SET(SOURCES ${ARGN})
  LIST(REMOVE_AT SOURCES 0)
  ADD_LIBRARY(${TARGET} STATIC ${SOURCES})
  IF(NOT _SKIP_PIC)
    SET_TARGET_PROPERTIES(${TARGET} PROPERTIES  COMPILE_FLAGS
    "${CMAKE_SHARED_LIBRARY_C_FLAGS}")
  ENDIF()
ENDMACRO()


# Write content to file, using CONFIGURE_FILE
# The advantage compared to FILE(WRITE) is that timestamp
# does not change if file already has the same content
MACRO(CONFIGURE_FILE_CONTENT content file)
 SET(CMAKE_CONFIGURABLE_FILE_CONTENT 
  "${content}\n")
 CONFIGURE_FILE(
  ${MYSQL_CMAKE_SCRIPT_DIR}/configurable_file_content.in
  ${file}
  @ONLY)
ENDMACRO()

# Merge static libraries into a big static lib. The resulting library 
# should not not have dependencies on other static libraries.
# We use it in MySQL to merge mysys,dbug,vio etc into mysqlclient

MACRO(MERGE_STATIC_LIBS TARGET OUTPUT_NAME LIBS_TO_MERGE)
  # To produce a library we need at least one source file.
  # It is created by ADD_CUSTOM_COMMAND below and will helps 
  # also help to track dependencies.
  SET(SOURCE_FILE ${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_depends.c)
  ADD_LIBRARY(${TARGET} STATIC ${SOURCE_FILE})
  SET_TARGET_PROPERTIES(${TARGET} PROPERTIES OUTPUT_NAME ${OUTPUT_NAME})
  IF(NOT _SKIP_PIC)
    SET_TARGET_PROPERTIES(${TARGET} PROPERTIES  COMPILE_FLAGS
    "${CMAKE_SHARED_LIBRARY_C_FLAGS}")
  ENDIF()

  SET(OSLIBS)
  FOREACH(LIB ${LIBS_TO_MERGE})
    IF(NOT TARGET ${LIB})
       # 3rd party library like libz.so. Make sure that everything
       # that links to our library links to this one as well.
       LIST(APPEND OSLIBS ${LIB})
    ELSE()
      GET_TARGET_PROPERTY(LIB_TYPE ${LIB} TYPE)
      # This is a target in current project
      # (can be a static or shared lib)
      IF(LIB_TYPE STREQUAL "STATIC_LIBRARY")
        SET(STATIC_TGTS ${STATIC_TGTS} ${LIB})
        SET(STATIC_LIBS ${STATIC_LIBS} $<TARGET_FILE:${LIB}>)
        ADD_DEPENDENCIES(${TARGET} ${LIB})
        # Extract dependent OS libraries
        GET_DEPENDEND_OS_LIBS(${LIB} LIB_OSLIBS)
        LIST(APPEND OSLIBS ${LIB_OSLIBS})
      ELSE()
        # This is a shared library our static lib depends on.
        LIST(APPEND OSLIBS ${LIB})
      ENDIF()
    ENDIF()
  ENDFOREACH()
  IF(OSLIBS)
    LIST(REMOVE_DUPLICATES OSLIBS)
    TARGET_LINK_LIBRARIES(${TARGET} ${OSLIBS})
  ENDIF()

  # Make the generated dummy source file depended on all static input
  # libs. If input lib changes,the source file is touched
  # which causes the desired effect (relink).
  ADD_CUSTOM_COMMAND( 
    OUTPUT  ${SOURCE_FILE}
    COMMAND ${CMAKE_COMMAND}  -E touch ${SOURCE_FILE}
    DEPENDS ${STATIC_TGTS})

  IF(MSVC)
    # To merge libs, just pass them to lib.exe command line.
    SET(LINKER_EXTRA_FLAGS "")
    FOREACH(LIB ${STATIC_LIBS})
      SET(LINKER_EXTRA_FLAGS "${LINKER_EXTRA_FLAGS} ${LIB}")
    ENDFOREACH()
    SET_TARGET_PROPERTIES(${TARGET} PROPERTIES STATIC_LIBRARY_FLAGS 
      "${LINKER_EXTRA_FLAGS}")
  ELSE()
    IF(APPLE)
      # Use OSX's libtool to merge archives (ihandles universal 
      # binaries properly)
      ADD_CUSTOM_COMMAND(TARGET ${TARGET} POST_BUILD
        COMMAND rm $<TARGET_FILE:${TARGET}>
        COMMAND libtool -static -o $<TARGET_FILE:${TARGET}>
        ${STATIC_LIBS}
      )  
    ELSE()
      # Generic Unix, Cygwin or MinGW. In post-build step, call
      # script, that extracts objects from archives with "ar x" 
      # and repacks them with "ar r"
      ADD_CUSTOM_COMMAND(TARGET ${TARGET} POST_BUILD
        COMMAND ${CMAKE_COMMAND}
          -DTARGET_LOCATION="$<TARGET_FILE:${TARGET}>"
          -DTARGET="${TARGET}"
          -DSTATIC_LIBS="${STATIC_LIBS}"
          -DCMAKE_CURRENT_BINARY_DIR="${CMAKE_CURRENT_BINARY_DIR}"
          -DCMAKE_AR="${CMAKE_AR}"
          -DCMAKE_RANLIB="${CMAKE_RANLIB}"
          -P "${MYSQL_CMAKE_SCRIPT_DIR}/merge_archives_unix.cmake"
      )
    ENDIF()
  ENDIF()
ENDMACRO()

# Create libs from libs.
# Merges static libraries, creates shared libraries out of convenience libraries.
# MERGE_LIBRARIES(target [STATIC|SHARED|MODULE] 
#  [linklib1 .... linklibN]
#  [EXPORTS exported_func1 .... exportedFuncN]
#  [OUTPUT_NAME output_name]
#)
MACRO(MERGE_LIBRARIES)
  CMAKE_PARSE_ARGUMENTS(ARG
    "STATIC;SHARED;MODULE;NOINSTALL"
    "OUTPUT_NAME;COMPONENT;VERSION;SOVERSION"
    "EXPORTS"
    ${ARGN}
  )
  LIST(GET ARG_UNPARSED_ARGUMENTS 0 TARGET)
  SET(LIBS ${ARG_UNPARSED_ARGUMENTS})
  LIST(REMOVE_AT LIBS 0)
  IF(ARG_STATIC)
    IF (NOT ARG_OUTPUT_NAME)
      SET(ARG_OUTPUT_NAME ${TARGET})
    ENDIF()
    MERGE_STATIC_LIBS(${TARGET} ${ARG_OUTPUT_NAME} "${LIBS}") 
  ELSEIF(ARG_SHARED OR ARG_MODULE)
    IF(ARG_SHARED)
      SET(LIBTYPE SHARED)
    ELSE()
      SET(LIBTYPE MODULE)
    ENDIF()
    # check for non-PIC libraries
    IF(NOT _SKIP_PIC)
      FOREACH(LIB ${LIBS})
        GET_TARGET_PROPERTY(LTYPE ${LIB} TYPE)
        IF(LTYPE STREQUAL "STATIC_LIBRARY")
          GET_TARGET_PROPERTY(LIB_COMPILE_FLAGS ${LIB} COMPILE_FLAGS)
          STRING(REPLACE "${CMAKE_SHARED_LIBRARY_C_FLAGS}" 
            "<PIC_FLAG>" LIB_COMPILE_FLAGS "${LIB_COMPILE_FLAGS}")
          IF(NOT LIB_COMPILE_FLAGS MATCHES "<PIC_FLAG>")
            MESSAGE(FATAL_ERROR 
            "Attempted to link non-PIC static library ${LIB} to shared library ${TARGET}\n"
            "Please use ADD_CONVENIENCE_LIBRARY, instead of ADD_LIBRARY for ${LIB}"
            )
          ENDIF()
        ENDIF()
      ENDFOREACH()
    ENDIF()
    CREATE_EXPORTS_FILE(SRC ${TARGET} "${ARG_EXPORTS}")
    IF(NOT ARG_NOINSTALL)
      ADD_VERSION_INFO(${TARGET} SHARED SRC)
    ENDIF()
    IF(ARG_VERSION)
      SET(VERS VERSION ${ARG_VERSION})
    ENDIF()
    ADD_LIBRARY(${TARGET} ${LIBTYPE} ${SRC})
    IF (ARG_VERSION)
      SET_TARGET_PROPERTIES(${TARGET} PROPERTIES VERSION  ${ARG_VERSION})
    ENDIF()
    IF (ARG_SOVERSION)
      SET_TARGET_PROPERTIES(${TARGET} PROPERTIES SOVERSION  ${ARG_VERSION})
    ENDIF()
    TARGET_LINK_LIBRARIES(${TARGET} LINK_PRIVATE ${LIBS})
    IF(ARG_OUTPUT_NAME)
      SET_TARGET_PROPERTIES(${TARGET} PROPERTIES OUTPUT_NAME "${ARG_OUTPUT_NAME}")
    ENDIF()
  ELSE()
    MESSAGE(FATAL_ERROR "Unknown library type")
  ENDIF()
  IF(NOT ARG_NOINSTALL)
    IF(ARG_COMPONENT)
      SET(COMP COMPONENT ${ARG_COMPONENT}) 
    ENDIF()
    MYSQL_INSTALL_TARGETS(${TARGET} DESTINATION "${INSTALL_LIBDIR}" ${COMP})
  ENDIF()
  IF(ARG_SHARED AND LINK_FLAG_NO_UNDEFINED)
    # Do not allow undefined symbols in shared libraries
    GET_TARGET_PROPERTY(TARGET_LINK_FLAGS ${TARGET} LINK_FLAGS)
    IF(NOT TARGET_LINK_FLAGS)
      SET(TARGET_LINK_FLAGS)
    ENDIF()
    SET_TARGET_PROPERTIES(${TARGET} PROPERTIES LINK_FLAGS 
      "${TARGET_LINK_FLAGS} ${LINK_FLAG_NO_UNDEFINED}")
  ENDIF() 
ENDMACRO()

FUNCTION(GET_DEPENDEND_OS_LIBS target result)
  FOREACH(lib ${${target}_LIB_DEPENDS})
    IF(NOT TARGET ${lib})
      SET(ret ${ret} ${lib})
    ENDIF()
  ENDFOREACH()
  SET(${result} ${ret} PARENT_SCOPE)
ENDFUNCTION()

INCLUDE(CheckCCompilerFlag)

SET(VISIBILITY_HIDDEN_FLAG)

IF(CMAKE_C_COMPILER_ID MATCHES "SunPro")
  SET(VISIBILITY_HIDDEN_FLAG "-xldscope=hidden")
ELSEIF(UNIX)
  CHECK_C_COMPILER_FLAG("-fvisibility=hidden" HAVE_VISIBILITY_HIDDEN)
  IF(HAVE_VISIBILITY_HIDDEN)
    SET(VISIBILITY_HIDDEN_FLAG "-fvisibility=hidden")
  ENDIF()
ENDIF()

# We try to hide the symbols in yassl/zlib to avoid name clashes with
# other libraries like openssl.
FUNCTION(RESTRICT_SYMBOL_EXPORTS target)
  IF(VISIBILITY_HIDDEN_FLAG)
    GET_TARGET_PROPERTY(COMPILE_FLAGS ${target} COMPILE_FLAGS)
    IF(NOT COMPILE_FLAGS)
      # Avoid COMPILE_FLAGS-NOTFOUND
      SET(COMPILE_FLAGS)
    ENDIF()
    SET_TARGET_PROPERTIES(${target} PROPERTIES 
      COMPILE_FLAGS "${COMPILE_FLAGS} ${VISIBILITY_HIDDEN_FLAG}")
  ENDIF()
ENDFUNCTION()