OverviewFilesCommitsIssuesDocsReleasesPeers

pearbrowser-desktop / appling / CMakeLists.txt

349 lines · txt ref:
cmake_minimum_required(VERSION 4.0)

find_package(cmake-pear REQUIRED PATHS node_modules/cmake-pear)

project(pear_appling C CXX ASM)

set(PEARBROWSER_APPLING_DIR "${CMAKE_CURRENT_LIST_DIR}")
set(PEARBROWSER_BARE_HEADERS_VERSION "1.28.7" CACHE STRING "Bare headers version compatible with cmake-pear's pinned Bare runtime")
set(PEARBROWSER_MACOS_ICNS "${PEARBROWSER_APPLING_DIR}/assets/darwin/icon.icns")
set(PEARBROWSER_LINUX_METAINFO "${PEARBROWSER_APPLING_DIR}/assets/linux/io.github.bigdestiny2.pearbrowser.metainfo.xml")

if(NOT DEFINED PEARBROWSER_MACOS_SIGNING_IDENTITY)
  if(DEFINED ENV{PEARBROWSER_MACOS_SIGNING_IDENTITY} AND NOT "$ENV{PEARBROWSER_MACOS_SIGNING_IDENTITY}" STREQUAL "")
    set(PEARBROWSER_MACOS_SIGNING_IDENTITY "$ENV{PEARBROWSER_MACOS_SIGNING_IDENTITY}" CACHE STRING "macOS codesign identity; '-' means ad-hoc signing")
  else()
    set(PEARBROWSER_MACOS_SIGNING_IDENTITY "-" CACHE STRING "macOS codesign identity; '-' means ad-hoc signing")
  endif()
endif()

if(NOT DEFINED PEARBROWSER_MACOS_SIGNING_KEYCHAIN)
  if(DEFINED ENV{PEARBROWSER_MACOS_SIGNING_KEYCHAIN})
    set(PEARBROWSER_MACOS_SIGNING_KEYCHAIN "$ENV{PEARBROWSER_MACOS_SIGNING_KEYCHAIN}" CACHE STRING "Optional macOS keychain for release signing")
  else()
    set(PEARBROWSER_MACOS_SIGNING_KEYCHAIN "" CACHE STRING "Optional macOS keychain for release signing")
  endif()
endif()

if(NOT DEFINED PEARBROWSER_WINDOWS_SIGNING_SUBJECT)
  if(DEFINED ENV{PEARBROWSER_WINDOWS_SIGNING_SUBJECT} AND NOT "$ENV{PEARBROWSER_WINDOWS_SIGNING_SUBJECT}" STREQUAL "")
    set(PEARBROWSER_WINDOWS_SIGNING_SUBJECT "$ENV{PEARBROWSER_WINDOWS_SIGNING_SUBJECT}" CACHE STRING "Windows MSIX publisher subject")
  else()
    set(PEARBROWSER_WINDOWS_SIGNING_SUBJECT "CN=PearBrowser Desktop" CACHE STRING "Windows MSIX publisher subject")
  endif()
endif()

if(NOT DEFINED PEARBROWSER_WINDOWS_SIGNING_THUMBPRINT)
  if(DEFINED ENV{PEARBROWSER_WINDOWS_SIGNING_THUMBPRINT})
    set(PEARBROWSER_WINDOWS_SIGNING_THUMBPRINT "$ENV{PEARBROWSER_WINDOWS_SIGNING_THUMBPRINT}" CACHE STRING "Optional Windows signing certificate thumbprint")
  else()
    set(PEARBROWSER_WINDOWS_SIGNING_THUMBPRINT "" CACHE STRING "Optional Windows signing certificate thumbprint")
  endif()
endif()

# cmake-bare defaults native addon headers to the npm "latest" tag. Keep
# PearBrowser builds reproducible by resolving the lockfile-owned copy instead.
function(download_bare_headers result)
  set(one_value_keywords
    DESTINATION
    VERSION
  )

  cmake_parse_arguments(
    PARSE_ARGV 1 ARGV "" "${one_value_keywords}" ""
  )

  if(ARGV_VERSION)
    set(expected_version "${ARGV_VERSION}")
  else()
    set(expected_version "${PEARBROWSER_BARE_HEADERS_VERSION}")
  endif()

  resolve_node_module(
    bare-headers
    output
    WORKING_DIRECTORY "${PEARBROWSER_APPLING_DIR}"
  )

  if("${output}" MATCHES "NOTFOUND")
    message(FATAL_ERROR "bare-headers is missing. Run npm ci --prefix appling.")
  endif()

  file(READ "${output}/package.json" bare_headers_package)
  string(JSON resolved_version GET "${bare_headers_package}" "version")

  if(NOT resolved_version STREQUAL expected_version)
    message(FATAL_ERROR "bare-headers ${resolved_version} does not match expected ${expected_version}. Run npm ci --prefix appling.")
  endif()

  cmake_path(APPEND output include OUTPUT_VARIABLE include_dir)

  set(${result} "${include_dir}")

  return(PROPAGATE ${result})
endfunction()

# cmake-pear passes a single 1024px macOS icon and then invokes iconutil. Prefer
# the committed ICNS; keep generated iconset support as a local fallback.
function(add_macos_iconset target)
  set(one_value_keywords
    DESTINATION
  )

  set(multi_value_keywords
    ICONS
    DEPENDS
  )

  cmake_parse_arguments(
    PARSE_ARGV 1 ARGV "" "${one_value_keywords}" "${multi_value_keywords}"
  )

  if(NOT ARGV_DESTINATION)
    set(ARGV_DESTINATION icon.icns)
  endif()

  cmake_path(ABSOLUTE_PATH ARGV_DESTINATION BASE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" NORMALIZE)

  if(EXISTS "${PEARBROWSER_MACOS_ICNS}")
    add_custom_target(
      ${target}
      ALL
      COMMAND ${CMAKE_COMMAND} -E copy_if_different "${PEARBROWSER_MACOS_ICNS}" "${ARGV_DESTINATION}"
      DEPENDS "${PEARBROWSER_MACOS_ICNS}" ${ARGV_DEPENDS}
    )

    return()
  endif()

  list(LENGTH ARGV_ICONS icons_len)

  if(icons_len LESS 1)
    message(FATAL_ERROR "add_macos_iconset requires at least one source icon")
  endif()

  list(GET ARGV_ICONS 0 source_icon)

  cmake_path(ABSOLUTE_PATH source_icon NORMALIZE)
  cmake_path(GET ARGV_DESTINATION STEM stem)

  find_program(sips NAMES sips REQUIRED)
  find_iconutil(iconutil)

  set(iconset "${CMAKE_CURRENT_BINARY_DIR}/${stem}.iconset")

  add_custom_target(
    ${target}
    ALL
    COMMAND ${CMAKE_COMMAND} -E make_directory "${iconset}"
    COMMAND ${sips} -z 16 16 "${source_icon}" --out "${iconset}/icon_16x16.png"
    COMMAND ${sips} -z 32 32 "${source_icon}" --out "${iconset}/icon_16x16@2x.png"
    COMMAND ${sips} -z 32 32 "${source_icon}" --out "${iconset}/icon_32x32.png"
    COMMAND ${sips} -z 64 64 "${source_icon}" --out "${iconset}/icon_32x32@2x.png"
    COMMAND ${sips} -z 128 128 "${source_icon}" --out "${iconset}/icon_128x128.png"
    COMMAND ${sips} -z 256 256 "${source_icon}" --out "${iconset}/icon_128x128@2x.png"
    COMMAND ${sips} -z 256 256 "${source_icon}" --out "${iconset}/icon_256x256.png"
    COMMAND ${sips} -z 512 512 "${source_icon}" --out "${iconset}/icon_256x256@2x.png"
    COMMAND ${sips} -z 512 512 "${source_icon}" --out "${iconset}/icon_512x512.png"
    COMMAND ${sips} -z 1024 1024 "${source_icon}" --out "${iconset}/icon_512x512@2x.png"
    COMMAND ${iconutil} --convert icns --output "${ARGV_DESTINATION}" "${iconset}"
    DEPENDS ${source_icon} ${ARGV_DEPENDS}
  )
endfunction()

# Default local and CI builds to ad-hoc signing. Release builds can pass a real
# Developer ID identity through PEARBROWSER_MACOS_SIGNING_IDENTITY.
function(code_sign_macos target)
  set(one_value_keywords
    PATH
    TARGET
    ENTITLEMENTS
    IDENTITY
    KEYCHAIN
  )

  set(multi_value_keywords
    DEPENDS
  )

  cmake_parse_arguments(
    PARSE_ARGV 1 ARGV "" "${one_value_keywords}" "${multi_value_keywords}"
  )

  if(ARGV_TARGET)
    set(ARGV_PATH $<TARGET_FILE:${ARGV_TARGET}>)
    set(base $<TARGET_FILE_DIR:${ARGV_TARGET}>)
  else()
    cmake_path(ABSOLUTE_PATH ARGV_PATH NORMALIZE)
    cmake_path(GET ARGV_PATH PARENT_PATH base)
  endif()

  if(NOT ARGV_IDENTITY)
    set(ARGV_IDENTITY "${PEARBROWSER_MACOS_SIGNING_IDENTITY}")
  endif()

  if(NOT ARGV_KEYCHAIN)
    set(ARGV_KEYCHAIN "${PEARBROWSER_MACOS_SIGNING_KEYCHAIN}")
  endif()

  if(ARGV_ENTITLEMENTS)
    cmake_path(ABSOLUTE_PATH ARGV_ENTITLEMENTS NORMALIZE)
  else()
    cmake_path(APPEND base "Entitlements.plist" OUTPUT_VARIABLE ARGV_ENTITLEMENTS)
  endif()

  set(args
    --force
    --options runtime
    --entitlements "${ARGV_ENTITLEMENTS}"
    --sign "${ARGV_IDENTITY}"
  )

  if(NOT ARGV_IDENTITY STREQUAL "-")
    list(PREPEND args --timestamp)
  endif()

  if(ARGV_KEYCHAIN)
    list(APPEND args --keychain "${ARGV_KEYCHAIN}")
  endif()

  find_codesign(codesign)

  add_custom_target(
    ${target}
    ALL
    COMMAND ${codesign} ${args} "${ARGV_PATH}"
    DEPENDS ${ARGV_DEPENDS}
  )
endfunction()

# Keep Windows CI useful before release certificates exist. An unsigned MSIX is
# enough to prove packaging; a real thumbprint turns SignTool signing back on.
function(code_sign_windows target)
  set(one_value_keywords
    PATH
    TARGET
    SUBJECT_NAME
    THUMBPRINT
    TIMESTAMP
  )

  set(multi_value_keywords
    DEPENDS
  )

  cmake_parse_arguments(
    PARSE_ARGV 1 ARGV "" "${one_value_keywords}" "${multi_value_keywords}"
  )

  if(ARGV_TARGET)
    set(ARGV_PATH $<TARGET_FILE:${ARGV_TARGET}>)
    list(APPEND ARGV_DEPENDS ${ARGV_TARGET})
  else()
    cmake_path(ABSOLUTE_PATH ARGV_PATH NORMALIZE)
  endif()

  if(NOT ARGV_SUBJECT_NAME)
    set(ARGV_SUBJECT_NAME "${PEARBROWSER_WINDOWS_SIGNING_SUBJECT}")
  endif()

  if(NOT ARGV_THUMBPRINT)
    set(ARGV_THUMBPRINT "${PEARBROWSER_WINDOWS_SIGNING_THUMBPRINT}")
  endif()

  if(NOT ARGV_THUMBPRINT)
    add_custom_target(
      ${target}
      ALL
      COMMAND ${CMAKE_COMMAND} -E echo "Skipping Windows code signing for ${ARGV_PATH}; set PEARBROWSER_WINDOWS_SIGNING_THUMBPRINT for signed MSIX releases."
      DEPENDS ${ARGV_DEPENDS}
    )

    return()
  endif()

  if(NOT ARGV_TIMESTAMP)
    set(ARGV_TIMESTAMP "http://timestamp.digicert.com")
  endif()

  set(args
    /sha1 "${ARGV_THUMBPRINT}"
    /fd SHA256
    /t "${ARGV_TIMESTAMP}"
  )

  find_sign_tool(sign_tool)

  add_custom_target(
    ${target}
    ALL
    COMMAND ${sign_tool} sign ${args} "${ARGV_PATH}"
    DEPENDS ${ARGV_DEPENDS}
  )
endfunction()

# cmake-pear delegates Linux packaging to cmake-app-image, which generates the
# desktop entry from CMake arguments. Add PearBrowser's AppStream metainfo to the
# generated AppDir so Linux release artifacts can be metadata-checked before
# upload.
function(configure_pear_appling_linux target)
  set(one_value_keywords
    NAME
    DESCRIPTION
    ICON
    CATEGORY
    SPLASH
  )

  cmake_parse_arguments(
    PARSE_ARGV 1 ARGV "" "${one_value_keywords}" ""
  )

  if(NOT ARGV_ICON)
    set(ARGV_ICON "assets/linux/icon.png")
  endif()

  string(TOLOWER "${ARGV_NAME}" ARGV_OUTPUT_NAME)

  set_target_properties(
    ${target}
    PROPERTIES
    OUTPUT_NAME "${ARGV_OUTPUT_NAME}"
  )

  if(NOT EXISTS "${PEARBROWSER_LINUX_METAINFO}")
    message(FATAL_ERROR "PearBrowser Linux AppStream metadata is missing: ${PEARBROWSER_LINUX_METAINFO}")
  endif()

  add_app_image(
    ${target}_app_image
    NAME "${ARGV_NAME}"
    DESCRIPTION "${ARGV_DESCRIPTION}"
    ICON "${ARGV_ICON}"
    CATEGORY "${ARGV_CATEGORY}"
    TARGET ${target}
    RESOURCES
      FILE "${ARGV_SPLASH}" "splash.png"
      FILE "${PEARBROWSER_LINUX_METAINFO}" "usr/share/metainfo/io.github.bigdestiny2.pearbrowser.metainfo.xml"
  )
endfunction()

add_pear_appling(
  pearbrowser_desktop
  ID "tco5k7h38uoxatedp1wongdbhjxow1x7jiwm3t1i9cujbebhsbty"
  NAME "PearBrowser"
  VERSION 0.8.0
  AUTHOR "pearbrowser-desktop contributors"
  DESCRIPTION "P2P browser, app store, and publishing platform — no servers required."

  MACOS_IDENTIFIER com.pearbrowser.desktop
  MACOS_CATEGORY public.app-category.productivity
  MACOS_SIGNING_IDENTITY "${PEARBROWSER_MACOS_SIGNING_IDENTITY}"
  MACOS_SIGNING_KEYCHAIN "${PEARBROWSER_MACOS_SIGNING_KEYCHAIN}"

  WINDOWS_SIGNING_SUBJECT "${PEARBROWSER_WINDOWS_SIGNING_SUBJECT}"
  WINDOWS_SIGNING_THUMBPRINT "${PEARBROWSER_WINDOWS_SIGNING_THUMBPRINT}"

  LINUX_CATEGORY Network
)