cmake 速记模板

添加第三方库


include(FetchContent)
function(get_third_dep dep_name dep_url dep_tag)
  FetchContent_Declare(${dep_name}
    QUIET
    SOURCE_DIR ${CMAKE_BINARY_DIR}/_deps/src/${dep_name}
    BINARY_DIR ${CMAKE_BINARY_DIR}/_deps/build/${dep_name}
    SUBBUILD_DIR ${CMAKE_BINARY_DIR}/_deps/sub/${dep_name}
    GIT_REPOSITORY ${dep_url}
    GIT_TAG ${dep_tag}
    GIT_PROGRESS TRUE
    GIT_SHALLOW TRUE
  )
  FetchContent_MakeAvailable(${dep_name})
endfunction(get_third_dep)

get_third_dep(spdlog https://ghproxy.com/https://github.com/gabime/spdlog.git v1.11.0)
get_third_dep(googletest https://ghproxy.com/https://github.com/google/googletest.git release-1.12.1)
get_third_dep(asio https://ghproxy.com/https://github.com/chriskohlhoff/asio.git asio-1-28-0)

# 添加 head only 库
add_library(asio INTERFACE)
target_include_directories(asio
  INTERFACE ${CMAKE_BINARY_DIR}/_deps/src/asio/asio/include
)
target_compile_definitions(asio
  INTERFACE
    ASIO_STANDALONE
    ASIO_NO_DEPRECATED
)
target_link_libraries(asio
    INTERFACE pthread
)

添加 yacc lex 编译

find_package(FLEX REQUIRED)
find_package(BISON 3.5.0 REQUIRED)

bison_target(parser
    ${PROJECT_SOURCE_DIR}/src/parser/parser.yy
    ${PROJECT_SOURCE_DIR}/src/parser/parser.cpp
    # DEFINES_FILE ${PROJECT_SOURCE_DIR}/src/include/parser/parser.hpp
    COMPILE_FLAGS ${bison_flags})

flex_target(scanner
  ${PROJECT_SOURCE_DIR}/src/parser/scanner.lex
  ${PROJECT_SOURCE_DIR}/src/parser/scanner.cpp)

ADD_FLEX_BISON_DEPENDENCY(scanner parser)

add_library(ASKY_lib
  SHARED
    ${ASKY_SRC}
    ${BISON_parser_OUTPUTS}
    ${FLEX_scanner_OUTPUTS}
)

添加单元测试

function(add_cosmos_test test_name test_file)
  add_executable(${test_name} ${test_file})
  target_link_libraries(${test_name} PRIVATE gtest gtest_main ASKY_lib)
  add_test(NAME ${test_name} COMMAND ${test_name})
endfunction(add_cosmos_test)

foreach(TEST_SOURCE_FILE ${ASKY_TEST_SRC})
  file(RELATIVE_PATH ASKY_TEST_CPP_REL "${PROJECT_SOURCE_DIR}/test" ${TEST_SOURCE_FILE})
  get_filename_component(NOISEPAGE_TEST_DIR ${ASKY_TEST_CPP_REL} DIRECTORY)
  get_filename_component(NOISEPAGE_TEST ${TEST_SOURCE_FILE} NAME_WE)

  add_cosmos_test(${NOISEPAGE_TEST} ${TEST_SOURCE_FILE})
endforeach()

添加 自定义 command

# clang-format
add_custom_target(format
  COMMAND git diff --name-only --diff-filter=d --cached '*.cpp' '*.h' | xargs clang-format -i
  WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
)

cmake option

  • option 会缓存在cache 中,直接修改cmake文件不会立刻生效,可以手动 cmake 使用 -D 指定 cmake .. -DENABLE_ASAN=OFF
option(ENABLE_ASAN          "enable asna check"         ON)
option(ENABLE_TEST          "enable regress"            OFF)
option(ENABLE_COVERAGE      "enable coverage"           OFF)
option(ENABLE_PARSER_TRACE  "enable sql parser trace"   ON)

if (${ENABLE_ASAN})
    list(APPEND COMPILE_OPTIONS "-fsanitize=address" "-fno-omit-frame-pointer" "-fno-optimize-sibling-calls")
    list(APPEND LINK_OPTIONS "-fsanitize=address")
endif ()
message(STATUS "ASAN: ${ENABLE_ASAN}")

自定义 package 配置


# Copyright 2020 Mats Kindahl
#
# 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.
#
# .rst: FindPostgreSQL
# --------------------
#
# Find the PostgreSQL installation.
#
# This module defines the following variables
#
# ::
#
# PostgreSQL_LIBRARIES - the PostgreSQL libraries needed for linking
#
# PostgreSQL_INCLUDE_DIRS - include directories
#
# PostgreSQL_SERVER_INCLUDE_DIRS - include directories for server programming
#
# PostgreSQL_LIBRARY_DIRS  - link directories for PostgreSQL libraries
#
# PostgreSQL_EXTENSION_DIR  - the directory for extensions
#
# PostgreSQL_SHARED_LINK_OPTIONS  - options for shared libraries
#
# PostgreSQL_LINK_OPTIONS  - options for static libraries and executables
#
# PostgreSQL_VERSION_STRING - the version of PostgreSQL found (since CMake
# 2.8.8)
#
# ----------------------------------------------------------------------------
# History: This module is derived from the existing FindPostgreSQL.cmake and try
# to use most of the existing output variables of that module, but uses
# `pg_config` to extract the necessary information instead and add a macro for
# creating extensions. The use of `pg_config` is aligned with how the PGXS code
# distributed with PostgreSQL itself works.

# Define additional search paths for root directories.
set(PostgreSQL_ROOT_DIRECTORIES ENV PGROOT ENV PGPATH ${PostgreSQL_ROOT})

find_program(
  PG_CONFIG pg_config
  PATHS ${PostgreSQL_ROOT_DIRECTORIES}
  PATH_SUFFIXES bin)

if(NOT PG_CONFIG)
  message(FATAL_ERROR "Could not find pg_config")
else()
  set(PostgreSQL_FOUND TRUE)
endif()

message(STATUS "Found pg_config as ${PG_CONFIG}")

if(PostgreSQL_FOUND)
  macro(PG_CONFIG VAR OPT)
    execute_process(
      COMMAND ${PG_CONFIG} ${OPT}
      OUTPUT_VARIABLE ${VAR}
      OUTPUT_STRIP_TRAILING_WHITESPACE)
  endmacro()

  pg_config(_pg_bindir --bindir)
  pg_config(_pg_includedir --includedir)
  pg_config(_pg_pkgincludedir --pkgincludedir)
  pg_config(_pg_sharedir --sharedir)
  pg_config(_pg_includedir_server --includedir-server)
  pg_config(_pg_libs --libs)
  pg_config(_pg_ldflags --ldflags)
  pg_config(_pg_ldflags_sl --ldflags_sl)
  pg_config(_pg_ldflags_ex --ldflags_ex)
  pg_config(_pg_pkglibdir --pkglibdir)
  pg_config(_pg_libdir --libdir)
  pg_config(_pg_version --version)

  separate_arguments(_pg_ldflags)
  separate_arguments(_pg_ldflags_sl)
  separate_arguments(_pg_ldflags_ex)

  set(_server_lib_dirs ${_pg_libdir} ${_pg_pkglibdir})
  set(_server_inc_dirs ${_pg_pkgincludedir} ${_pg_includedir_server})
  string(REPLACE ";" " " _shared_link_options
                 "${_pg_ldflags};${_pg_ldflags_sl}")
  set(_link_options ${_pg_ldflags})
  if(_pg_ldflags_ex)
    list(APPEND _link_options ${_pg_ldflags_ex})
  endif()

  set(PostgreSQL_INCLUDE_DIRS
      "${_pg_includedir}"
      CACHE PATH
            "Top-level directory containing the PostgreSQL include directories."
  )
  set(PostgreSQL_EXTENSION_DIR
      "${_pg_sharedir}/extension"
      CACHE PATH "Directory containing extension SQL and control files")
  set(PostgreSQL_SERVER_INCLUDE_DIRS
      "${_server_inc_dirs}"
      CACHE PATH "PostgreSQL include directories for server include files.")
  set(PostgreSQL_LIBRARY_DIRS
      "${_pg_libdir}"
      CACHE PATH "library directory for PostgreSQL")
  set(PostgreSQL_LIBRARIES
      "${_pg_libs}"
      CACHE PATH "Libraries for PostgreSQL")
  set(PostgreSQL_SHARED_LINK_OPTIONS
      "${_shared_link_options}"
      CACHE STRING "PostgreSQL linker options for shared libraries.")
  set(PostgreSQL_LINK_OPTIONS
      "${_pg_ldflags},${_pg_ldflags_ex}"
      CACHE STRING "PostgreSQL linker options for executables.")
  set(PostgreSQL_SERVER_LIBRARY_DIRS
      "${_server_lib_dirs}"
      CACHE PATH "PostgreSQL server library directories.")
  set(PostgreSQL_VERSION_STRING
      "${_pg_version}"
      CACHE STRING "PostgreSQL version string")
  set(PostgreSQL_PACKAGE_LIBRARY_DIR
    "${_pg_pkglibdir}"
    CACHE STRING "PostgreSQL package library directory")

  find_program(
    PG_BINARY postgres
    PATHS ${PostgreSQL_ROOT_DIRECTORIES}
    HINTS ${_pg_bindir}
    PATH_SUFFIXES bin)

  if(NOT PG_BINARY)
    message(FATAL_ERROR "Could not find postgres binary")
  endif()

  message(STATUS "Found postgres binary at ${PG_BINARY}")

  find_program(PG_REGRESS pg_regress HINT
    ${PostgreSQL_PACKAGE_LIBRARY_DIR}/pgxs/src/test/regress)
  if(NOT PG_REGRESS)
    message(STATUS "Could not find pg_regress, tests not executed")
  endif()

  message(STATUS "PostgreSQL version ${PostgreSQL_VERSION_STRING} found")
  message(
    STATUS
    "PostgreSQL package library directory: ${PostgreSQL_PACKAGE_LIBRARY_DIR}")
  message(STATUS "PostgreSQL libraries: ${PostgreSQL_LIBRARIES}")
  message(STATUS "PostgreSQL extension directory: ${PostgreSQL_EXTENSION_DIR}")
  message(STATUS "PostgreSQL linker options: ${PostgreSQL_LINK_OPTIONS}")
  message(
    STATUS "PostgreSQL shared linker options: ${PostgreSQL_SHARED_LINK_OPTIONS}")
endif()

# add_postgresql_extension(NAME ...)
#
# VERSION Version of the extension. Is used when generating the control file.
# Required.
#
# ENCODING Encoding for the control file. Optional.
#
# COMMENT Comment for the control file. Optional.
#
# SOURCES List of source files to compile for the extension.
#
# REQUIRES List of extensions that are required by this extension.
#
# SCRIPTS Script files.
#
# SCRIPT_TEMPLATES Template script files.
#
# REGRESS Regress tests.
function(add_postgresql_extension NAME)
  set(_optional)
  set(_single VERSION ENCODING)
  set(_multi SOURCES SCRIPTS SCRIPT_TEMPLATES DEPENDS_LIB REQUIRES REGRESS)
  cmake_parse_arguments(_ext "${_optional}" "${_single}" "${_multi}" ${ARGN})

  if(NOT _ext_VERSION)
    message(FATAL_ERROR "Extension version not set")
  endif()

  # Here we are assuming that there is at least one source file, which is
  # strictly speaking not necessary for an extension. If we do not have source
  # files, we need to create a custom target and attach properties to that. We
  # expect the user to be able to add target properties after creating the
  # extension.
  add_library(${NAME} MODULE ${_ext_SOURCES})

  set(_link_flags "${PostgreSQL_SHARED_LINK_OPTIONS}")
  foreach(_dir ${PostgreSQL_SERVER_LIBRARY_DIRS})
    set(_link_flags "${_link_flags} -L${_dir}")
  endforeach()

  # Collect and build script files to install
  set(_script_files ${_ext_SCRIPTS})
  foreach(_template ${_ext_SCRIPT_TEMPLATES})
    string(REGEX REPLACE "\.in$" "" _script ${_template})
    configure_file(${_template} ${_script} @ONLY)
    list(APPEND _script_files ${CMAKE_CURRENT_BINARY_DIR}/${_script})
    message(
      STATUS "Building script file ${_script} from template file ${_template}")
  endforeach()

  if(APPLE)
    set(_link_flags "${_link_flags} -bundle_loader ${PG_BINARY}")
  endif()

  if (_ext_DEPENDS_LIB)
    target_link_libraries(${NAME} PRIVATE ${_ext_DEPENDS_LIB})
  endif()

  set_target_properties(
    ${NAME}
    PROPERTIES PREFIX ""
               LINK_FLAGS "${_link_flags}"
               POSITION_INDEPENDENT_CODE ON
               CXX_STANDARD 23
               CXX_EXTENSIONS OFF
               XX_STANDARD_REQUIRED ON)

  target_include_directories(
    ${NAME}
    PRIVATE ${PostgreSQL_SERVER_INCLUDE_DIRS}
    PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})

  target_compile_options(${NAME}
    PRIVATE
      # "-Werror"
      "-Wall"
      "-fvisibility=hidden"
      "-fvisibility-inlines-hidden"
      "-Wpointer-arith"
      "-Wendif-labels"
      "-Wmissing-format-attribute"
      "-Wimplicit-fallthrough=3"
      "-Wcast-function-type"
      "-Wshadow=compatible-local"
      "-Wformat-security"
      "-fno-strict-aliasing"
  )

  # Generate control file at build time (which is when GENERATE evaluate the
  # contents). We do not know the target file name until then.
  set(_control_file "${CMAKE_CURRENT_BINARY_DIR}/${NAME}.control")
  file(
    GENERATE
    OUTPUT ${_control_file}
    CONTENT
      "# This file is generated content from add_postgresql_extension.
# No point in modifying it, it will be overwritten anyway.

# Default version, always set
default_version = '${_ext_VERSION}'

# Module pathname generated from target shared library name. Use
# MODULE_PATHNAME in script file.
module_pathname = '$libdir/$<TARGET_FILE_NAME:${NAME}>'

# Comment for extension. Set using COMMENT option. Can be set in
# script file as well.
$<$<NOT:$<BOOL:${_ext_COMMENT}>>:#>comment = '${_ext_COMMENT}'

# Encoding for script file. Set using ENCODING option.
$<$<NOT:$<BOOL:${_ext_ENCODING}>>:#>encoding = '${_ext_ENCODING}'

# Required extensions. Set using REQUIRES option (multi-valued).
$<$<NOT:$<BOOL:${_ext_REQUIRES}>>:#>requires = '$<JOIN:${_ext_REQUIRES},$<COMMA>>'
")

  install(TARGETS ${NAME} LIBRARY DESTINATION ${PostgreSQL_PACKAGE_LIBRARY_DIR})
  install(FILES ${_control_file} ${_script_files}
          DESTINATION ${PostgreSQL_EXTENSION_DIR})

  if(_ext_REGRESS)
    foreach(_test ${_ext_REGRESS})
      set(_sql_file "${CMAKE_CURRENT_SOURCE_DIR}/sql/${_test}.sql")
      set(_out_file "${CMAKE_CURRENT_SOURCE_DIR}/expected/${_test}.out")
      if(NOT EXISTS "${_sql_file}")
        message(FATAL_ERROR "Test file '${_sql_file}' does not exist!")
      endif()
      if(NOT EXISTS "${_out_file}")
        file(WRITE "${_out_file}" )
        message(STATUS "Created empty file ${_out_file}")
      endif()
    endforeach()

    if(PG_REGRESS)
      add_test(
        NAME ${NAME}
        COMMAND
          ${PG_REGRESS} --temp-instance=${CMAKE_BINARY_DIR}/tmp_check
          --inputdir=${CMAKE_CURRENT_SOURCE_DIR}
          --outputdir=${CMAKE_CURRENT_BINARY_DIR} --load-extension=${NAME}
          ${_ext_REGRESS})
    endif()

    add_custom_target(
      ${NAME}_update_results
      COMMAND
        ${CMAKE_COMMAND} -E copy_if_different
        ${CMAKE_CURRENT_BINARY_DIR}/results/*.out
        ${CMAKE_CURRENT_SOURCE_DIR}/expected)
  endif()
endfunction()

if(PG_REGRESS)
  # We add a custom target to get output when there is a failure.
  add_custom_target(
    test_verbose COMMAND ${CMAKE_CTEST_COMMAND} --force-new-ctest-process
                         --verbose --output-on-failure)
endif()

添加资源到文件

  • 类似 xxd -i 选项,把一些资源序列化到文件中,然后在程序中读取

  • 直接 xxd -i 末尾没有 0, 这里自己实现了,加个 0


function(target_embedding_files TARGET_DEPENDS TARGET_FILES TARGET_PATH OUTOUT_PATH)
  file(READ ${TARGET_FILES} content HEX)
  string(REGEX MATCHALL "([A-Fa-f0-9][A-Fa-f0-9])" SEPARATED_HEX ${content})
  file(RELATIVE_PATH FILE_ORINGINAL ${TARGET_PATH} ${TARGET_FILES})
  get_filename_component(FILE ${TARGET_FILES} NAME_WE)

  set(counter 0)
  set(output_hex "")

  foreach (hex IN LISTS SEPARATED_HEX)
    string(APPEND output_hex "0x${hex},")
    MATH(EXPR counter "${counter}+1")
    if(counter GREATER 16)
      string(APPEND output_hex "\n")
      set(counter 0)
    endif()
  endforeach ()

  string(APPEND output_hex "0x00,")

  set(output_hex "
uint8_t __${FILE}[] = {
${output_hex}
}\;
unsigned __${FILE}_size = sizeof(__${FILE})\;
  ")
  
  if (NOT EXISTS ${OUTOUT_PATH})
    file(MAKE_DIRECTORY ${OUTOUT_PATH})
  endif ()

  file(WRITE ${OUTOUT_PATH}/${FILE}.h  ${output_hex})

  add_custom_target(${FILE}Target DEPENDS ${OUTOUT_PATH}/${FILE}.h)
  add_dependencies(${TARGET_DEPENDS} ${FILE}Target)
endfunction(target_embedding_files)


file(
  GLOB_RECURSE
  TPCDS_QUERIES
  CONFIGURE_DEPENDS
    ${CMAKE_CURRENT_SOURCE_DIR}/queries/*.sql 
)

foreach(QUERY_CELL ${TPCDS_QUERIES})
  target_embedding_files(tpcds
    ${QUERY_CELL}
    ${CMAKE_CURRENT_SOURCE_DIR}/queries
    ${CMAKE_CURRENT_SOURCE_DIR}/include/queries)
endforeach()