1# Copyright 2019 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# https://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# Embeds arbitrary binary data into a static library. 16# 17# NAME specifies the name for this target. 18# NAMESPACE is the C++ namespace the generated code is placed in. Can be empty. 19# SOURCES is a list of files that should be embedded. If a source names a 20# target the target binary is embedded instead. 21macro(sapi_cc_embed_data) 22 cmake_parse_arguments(_sapi_embed "" "OUTPUT_NAME;NAME;NAMESPACE" "SOURCES" 23 ${ARGN}) 24 foreach(src IN LISTS _sapi_embed_SOURCES) 25 if(TARGET "${src}") 26 get_target_property(_sapi_embed_src_OUTPUT_NAME ${src} OUTPUT_NAME) 27 if(NOT _sapi_embed_src_OUTPUT_NAME) 28 set(_sapi_embed_src_OUTPUT_NAME "${src}") 29 endif() 30 list(APPEND _sapi_embed_in 31 "${CMAKE_CURRENT_BINARY_DIR}/${_sapi_embed_src_OUTPUT_NAME}") 32 else() 33 list(APPEND _sapi_embed_in "${src}") 34 endif() 35 endforeach() 36 file(RELATIVE_PATH _sapi_embed_pkg 37 "${PROJECT_BINARY_DIR}" 38 "${CMAKE_CURRENT_BINARY_DIR}") 39 if(NOT _sapi_embed_OUTPUT_NAME) 40 set(_sapi_embed_OUTPUT_NAME "${_sapi_embed_NAME}") 41 endif() 42 add_custom_command( 43 OUTPUT "${_sapi_embed_OUTPUT_NAME}.h" 44 "${_sapi_embed_OUTPUT_NAME}.cc" 45 WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" 46 COMMAND filewrapper "${_sapi_embed_pkg}" 47 "${_sapi_embed_OUTPUT_NAME}" 48 "${_sapi_embed_NAMESPACE}" 49 "${CMAKE_CURRENT_BINARY_DIR}/${_sapi_embed_OUTPUT_NAME}.h" 50 "${CMAKE_CURRENT_BINARY_DIR}/${_sapi_embed_OUTPUT_NAME}.cc" 51 ${_sapi_embed_in} 52 DEPENDS ${_sapi_embed_SOURCES} 53 VERBATIM 54 ) 55 add_library("${_sapi_embed_NAME}" STATIC 56 "${_sapi_embed_OUTPUT_NAME}.h" 57 "${_sapi_embed_OUTPUT_NAME}.cc" 58 ) 59 target_link_libraries("${_sapi_embed_NAME}" PRIVATE 60 sapi::base 61 absl::core_headers 62 ) 63endmacro() 64 65# Adds a library target implementing a sandboxed API for another library. 66# The first argument is the target name, similar to the native add_library(). 67# This function implements the same functionality as the Bazel version in 68# sandboxed_api/bazel/sapi.bzl. 69# 70# SOURCES Any additional sources to include with the Sandboxed API library. 71# Typically not necessary, unless the sandbox definition should be in a .cc 72# file instead of the customary "sandbox.h" header. Bazel also has a "hdrs" 73# attribute, but CMake does not distinguish headers from sources. 74# FUNCTIONS A list of functions that to use in from host code. Leaving this 75# list empty will export and wrap all functions found in the library. 76# NOEMBED Whether the SAPI library should be embedded inside host code, so the 77# SAPI Sandbox can be initialized with the 78# ::sapi::Sandbox::Sandbox(FileToc*) constructor. 79# LIBRARY The library target to sandbox and expose to the host code (required). 80# LIBRARY_NAME The name of the class which will proxy the library functions 81# from the functions list (required). You will call functions from the 82# sandboxed library via instances of this class. 83# INPUTS List of source files which the SAPI interface generator should scan 84# for function declarations. Library header files are always scanned, so 85# this can usually be empty/omitted. 86# NAMESPACE C++ namespace identifier to place API class defined by 87# LIBRARY_NAME into. 88# HEADER If set, does not generate an interface header, but uses the one 89# specified. 90# API_VERSION Which version of the Sandboxed API to generate. Currently, only 91# version "1" is defined. 92function(add_sapi_library) 93 set(_sapi_opts NOEMBED) 94 set(_sapi_one_value HEADER LIBRARY LIBRARY_NAME NAMESPACE API_VERSION) 95 set(_sapi_multi_value SOURCES FUNCTIONS INPUTS) 96 cmake_parse_arguments(PARSE_ARGV 0 _sapi "${_sapi_opts}" 97 "${_sapi_one_value}" "${_sapi_multi_value}") 98 set(_sapi_NAME "${ARGV0}") 99 100 if(_sapi_API_VERSION AND NOT _sapi_API_VERSION VERSION_EQUAL "1") 101 message(FATAL_ERROR "API_VERSION \"1\" is the only one defined right now") 102 endif() 103 104 set(_sapi_gen_header "${_sapi_NAME}.sapi.h") 105 foreach(func IN LISTS _sapi_FUNCTIONS) 106 list(APPEND _sapi_exported_funcs "LINKER:--export-dynamic-symbol,${func}") 107 endforeach() 108 if(NOT _sapi_exported_funcs) 109 set(_sapi_exported_funcs LINKER:--allow-multiple-definition) 110 endif() 111 112 # The sandboxed binary 113 set(_sapi_bin "${_sapi_NAME}.bin") 114 add_executable("${_sapi_bin}" 115 "${SAPI_BINARY_DIR}/sapi_force_cxx_linkage.cc" 116 ) 117 target_link_libraries("${_sapi_bin}" PRIVATE 118 -fuse-ld=gold 119 -Wl,--whole-archive "${_sapi_LIBRARY}" -Wl,--no-whole-archive 120 # Needs to be whole-archive due to how it Abseil registers flags 121 -Wl,--whole-archive absl::log_flags -Wl,--no-whole-archive 122 sapi::client 123 ${CMAKE_DL_LIBS} 124 ) 125 target_link_options("${_sapi_bin}" PRIVATE 126 LINKER:-E 127 ${_sapi_exported_funcs} 128 ) 129 130 if(NOT _sapi_NOEMBED) 131 set(_sapi_embed "${_sapi_NAME}_embed") 132 sapi_cc_embed_data(NAME "${_sapi_embed}" 133 NAMESPACE "${_sapi_NAMESPACE}" 134 SOURCES "${_sapi_bin}" 135 ) 136 endif() 137 138 # Interface 139 list(JOIN _sapi_FUNCTIONS "," _sapi_funcs) 140 foreach(src IN LISTS _sapi_INPUTS) 141 get_filename_component(src "${src}" ABSOLUTE) 142 list(APPEND _sapi_full_inputs "${src}") 143 endforeach() 144 if(NOT _sapi_NOEMBED) 145 set(_sapi_embed_dir "${CMAKE_CURRENT_BINARY_DIR}") 146 set(_sapi_embed_name "${_sapi_NAME}") 147 endif() 148 149 list(APPEND _sapi_generator_args 150 "--sapi_name=${_sapi_LIBRARY_NAME}" 151 "--sapi_out=${_sapi_gen_header}" 152 "--sapi_embed_dir=${_sapi_embed_dir}" 153 "--sapi_embed_name=${_sapi_embed_name}" 154 "--sapi_functions=${_sapi_funcs}" 155 "--sapi_ns=${_sapi_NAMESPACE}" 156 ) 157 if(SAPI_ENABLE_CLANG_TOOL) 158 set(_sapi_isystem_args ${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES}) 159 list(TRANSFORM _sapi_isystem_args PREPEND --extra-arg-before=-isystem) 160 if(SAPI_CLANG_TOOL_EXECUTABLE) 161 list(APPEND _sapi_generator_command "${SAPI_CLANG_TOOL_EXECUTABLE}") 162 else() 163 list(APPEND _sapi_generator_command sapi_generator_tool) 164 endif() 165 list(APPEND _sapi_generator_command 166 -p "${CMAKE_CURRENT_BINARY_DIR}" 167 ${_sapi_generator_args} 168 ${_sapi_isystem_args} 169 ${_sapi_full_inputs} 170 ) 171 add_custom_command( 172 OUTPUT "${_sapi_gen_header}" 173 COMMAND ${_sapi_generator_command} 174 COMMENT "Generating interface" 175 DEPENDS ${_sapi_INPUTS} 176 VERBATIM 177 ) 178 else() 179 set(_sapi_isystem "${_sapi_NAME}.isystem") 180 list(JOIN _sapi_full_inputs "," _sapi_full_inputs) 181 list(APPEND _sapi_generator_command 182 "${SAPI_PYTHON3_EXECUTABLE}" -B 183 "${SAPI_SOURCE_DIR}/sandboxed_api/tools/generator2/sapi_generator.py" 184 ${_sapi_generator_args} 185 "--sapi_isystem=${_sapi_isystem}" 186 "--sapi_in=${_sapi_full_inputs}" 187 ) 188 add_custom_command( 189 OUTPUT "${_sapi_gen_header}" "${_sapi_isystem}" 190 COMMAND sh -c 191 "printf '${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES}' | \ 192 sed \"s/;/\\n/g\" > \"${_sapi_isystem}\"" 193 COMMAND ${_sapi_generator_command} 194 COMMENT "Generating interface" 195 DEPENDS ${_sapi_INPUTS} 196 VERBATIM 197 ) 198 endif() 199 200 # Library with the interface 201 if(NOT _sapi_SOURCES) 202 list(APPEND _sapi_SOURCES 203 "${SAPI_BINARY_DIR}/sapi_force_cxx_linkage.cc" 204 ) 205 endif() 206 add_library("${_sapi_NAME}" STATIC 207 "${_sapi_gen_header}" 208 ${_sapi_SOURCES} 209 ) 210 target_link_libraries("${_sapi_NAME}" PUBLIC 211 absl::status 212 absl::statusor 213 sapi::sapi 214 sapi::status 215 sapi::vars 216 ) 217 if(NOT _sapi_NOEMBED) 218 target_link_libraries("${_sapi_NAME}" PUBLIC 219 "${_sapi_embed}" 220 ) 221 endif() 222endfunction() 223 224# Wrapper for gtest_discover_tests to exclude tests discover when cross compiling. 225macro(gtest_discover_tests_xcompile) 226 if (NOT CMAKE_CROSSCOMPILING) 227 gtest_discover_tests(${ARGV}) 228 endif() 229endmacro() 230