include(CheckSymbolExists)

file(GLOB SRCS "*.cpp")

if(NOT OCI)
  file(GLOB OCI_SRCS "buildboxcommon_httpclient.cpp" "buildboxcommon_ociclient.cpp" "buildboxcommon_ocimanifest.cpp")
  list(REMOVE_ITEM SRCS ${OCI_SRCS})
endif()

set_source_files_properties(buildboxcommon_version.cpp PROPERTIES COMPILE_DEFINITIONS BUILDBOX_VERSION="${BUILDBOX_VERSION}")

set_source_files_properties(${PROTO_GENERATED_SRCS} PROPERTIES COMPILE_FLAGS "-Wno-error -Wno-conversion")
set_source_files_properties(${PROTO_GENERATED_SRCS} PROPERTIES GENERATED 1)

add_library(common STATIC
    ${SRCS}
    ${PROTO_GENERATED_SRCS})

target_precompile_headers(common PRIVATE buildboxcommon_deps.h)

target_link_libraries(common
    Threads::Threads
    ${PROTOBUF_TARGET}
    ${GRPC_TARGET}
    ${OPENSSL_TARGET}
    ${GLOG_TARGET}
    ${CURL_TARGET}
    commonmetrics
)

if(NOT APPLE)
  # macOS includes UUID generation functionality in libc, but on other platforms
  # it's a separate library.
  find_package(PkgConfig REQUIRED)
  pkg_check_modules(uuid REQUIRED IMPORTED_TARGET uuid)
  target_link_libraries(common PkgConfig::uuid)
endif()

target_include_directories(common PRIVATE .)

target_compile_features(common PUBLIC cxx_std_20)

set(PUBLIC_FLAGS -Wall -Wformat-security)

# Compiler flags that apply only to the buildbox-common library.
# TODO: Move these additional warning flags to public flags once
# the other BuildBox components can be built with them.
set(PRIVATE_FLAGS -Wextra -pedantic-errors -Wno-vla -Wconversion -Wno-sign-conversion)

check_cxx_compiler_flag(-ftrivial-auto-var-init=pattern COMPILER_SUPPORTS_TRIVIAL_AUTO_VAR_INIT_PATTERN)
check_cxx_compiler_flag(-ftrivial-auto-var-init=zero COMPILER_SUPPORTS_TRIVIAL_AUTO_VAR_INIT_ZERO)

if(CMAKE_BUILD_TYPE STREQUAL "DEBUG")
  # Compiler flags for debug builds of all BuildBox components.
  set(DEBUG_FLAGS -Werror)

  if(COMPILER_SUPPORTS_TRIVIAL_AUTO_VAR_INIT_PATTERN)
    set(DEBUG_FLAGS ${DEBUG_FLAGS} -ftrivial-auto-var-init=pattern)
  endif()
else()
  # Compiler flags for release builds of all BuildBox components
  if(COMPILER_SUPPORTS_TRIVIAL_AUTO_VAR_INIT_ZERO)
    set(PUBLIC_FLAGS ${PUBLIC_FLAGS} -ftrivial-auto-var-init=zero)
  endif()
endif()

if(CMAKE_CXX_COMPILER_ID STREQUAL Clang OR CMAKE_CXX_COMPILER_ID STREQUAL AppleClang)
  set(PUBLIC_FLAGS ${PUBLIC_FLAGS} -Wno-vla-cxx-extension -Wno-nullability-completeness -Wno-nullability-extension)
  set(PRIVATE_FLAGS ${PRIVATE_FLAGS} -Xclang -fno-pch-timestamp -Winconsistent-missing-override)
elseif(CMAKE_CXX_COMPILER_ID STREQUAL GNU)
  set(PRIVATE_FLAGS ${PRIVATE_FLAGS} -Wsuggest-override)
endif()

target_compile_options(common PUBLIC ${PUBLIC_FLAGS} ${DEBUG_FLAGS} PRIVATE ${PRIVATE_FLAGS})

target_include_directories(common SYSTEM PRIVATE ${PROTO_GEN_DIR})

if(POLICY CMP0075)
  cmake_policy(SET CMP0075 NEW)
endif()

check_symbol_exists(pipe2 "unistd.h" HAVE_PIPE2)
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux" AND NOT HAVE_PIPE2)
  # Sanity check to always build with pipe2() on Linux as that's available
  # already since Linux 2.6.27 and glibc 2.9.
  message(FATAL_ERROR "pipe2() not found on Linux")
endif()
if(HAVE_PIPE2)
  target_compile_definitions(common PUBLIC HAVE_PIPE2)
endif()

add_dependencies(common generate_protobufs)

# Hash function used to calculate Digests.
# Integer value as defined in `enum DigestFunction.Value` in the REAPI proto
# (`protos/build/bazel/remote/execution/v2/remote_execution.proto`).
if(NOT BUILDBOXCOMMON_DIGEST_FUNCTION_VALUE)
  set(BUILDBOXCOMMON_DIGEST_FUNCTION_VALUE 1) # By default, use SHA-256
  message("`BUILDBOXCOMMON_DIGEST_FUNCTION_VALUE` not set, "
            "setting `DigestFunction.Value` to default value: 1 (SHA-256)")
else()
  message("Setting `DigestFunction.Value` to "
            "${BUILDBOXCOMMON_DIGEST_FUNCTION_VALUE}")
endif()

target_compile_definitions(common PUBLIC
    BUILDBOXCOMMON_DIGEST_FUNCTION_VALUE=${BUILDBOXCOMMON_DIGEST_FUNCTION_VALUE}
)

if(OCI)
  # Default OCI registry for OciClient
  if(NOT BUILDBOXCOMMON_DEFAULT_OCI_REGISTRY)
    set(BUILDBOXCOMMON_DEFAULT_OCI_REGISTRY "registry-1.docker.io") # By default, use docker.io
  else()
    message("Setting default OCI registry to "
              "${BUILDBOXCOMMON_DEFAULT_OCI_REGISTRY}")
  endif()

  target_compile_definitions(common PUBLIC
      BUILDBOXCOMMON_DEFAULT_OCI_REGISTRY="${BUILDBOXCOMMON_DEFAULT_OCI_REGISTRY}"
  )
endif()

include(CTest)
if(BUILD_TESTING)
  add_subdirectory(test)
endif()
