CMake/packaging/mac/MacPackagingTasks.cmake.in

CMake/packaging/mac/MacPackagingTasks.cmake.in

OS X packaging tasks

This file is processed by CONFIGURE_FILE in ../CMakeLists.txt which inserts values for @VARIABLE@ declarations. This is done to import values for some variables that would otherwise be undefined when CPack is running.

SET(PROJECT_NAME @PROJECT_NAME@)
SET(PROJECT_SOURCE_DIR @PROJECT_SOURCE_DIR@)
SET(PROJECT_BINARY_DIR @PROJECT_BINARY_DIR@)
SET(TeXworks_LIB_DIRS @TeXworks_LIB_DIRS@)
SET(CMAKE_SHARED_LIBRARY_SUFFIX @CMAKE_SHARED_LIBRARY_SUFFIX@)
SET(QT_PLUGINS @QT_PLUGINS@)
SET(QT_VERSION_MAJOR @QT_VERSION_MAJOR@)

Poppler data files: version, matching hash, and derived variables.

SET(POPPLER_DATA_VERSION "0.4.7")
SET(POPPLER_DATA_ARCHIVE_MD5 "636a8f2b9f6df9e7ced8ec0946961eaf")
SET(POPPLER_DATA_BASE "poppler-data-${POPPLER_DATA_VERSION}")
SET(POPPLER_DATA_ARCHIVE "${POPPLER_DATA_BASE}.tar.gz")
SET(POPPLER_DATA_ARCHIVE_URL "http://poppler.freedesktop.org/${POPPLER_DATA_ARCHIVE}")

TeXworks HTML manual: version, matching hash, and derived variables.

SET(TW_MANUAL_VERSION "20150506-64280a1")
SET(TW_MANUAL_ARCHIVE_MD5 "6f4cb6bb29b1ff3b482ff0700ff06d9d")
SET(TW_MANUAL_BASE "TeXworks-manual-html-${TW_MANUAL_VERSION}")
SET(TW_MANUAL_ARCHIVE "${TW_MANUAL_BASE}.zip")
SET(TW_MANUAL_ARCHIVE_URL "https://github.com/TeXworks/manual/releases/download/2015-05-06/${TW_MANUAL_ARCHIVE}")

This IF statement ensures that the following commands are executed only when CPack is running---i.e. when a user executes make package but not make install

IF ( ${CMAKE_INSTALL_PREFIX} MATCHES .*/_CPack_Packages/.* )

Download and install Poppler data

  IF ( NOT EXISTS "${PROJECT_SOURCE_DIR}/${POPPLER_DATA_ARCHIVE}" )
    MESSAGE(STATUS "Downloading Poppler data files")
    FILE(DOWNLOAD "${POPPLER_DATA_ARCHIVE_URL}"
      "${PROJECT_SOURCE_DIR}/${POPPLER_DATA_ARCHIVE}"
      EXPECTED_MD5 "${POPPLER_DATA_ARCHIVE_MD5}"
      SHOW_PROGRESS
    )
  ELSE ( )
    MESSAGE(STATUS "Using Poppler data files in '${PROJECT_SOURCE_DIR}/${POPPLER_DATA_ARCHIVE}'")
  ENDIF ()

  IF ( NOT EXISTS "${PROJECT_SOURCE_DIR}/${POPPLER_DATA_BASE}" )
    EXECUTE_PROCESS(
      COMMAND tar xzf "${PROJECT_SOURCE_DIR}/${POPPLER_DATA_ARCHIVE}"
      WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}"
    )
  ELSE ( )
    MESSAGE(STATUS "'${PROJECT_SOURCE_DIR}/${POPPLER_DATA_BASE}' already present")
  ENDIF ()

  MESSAGE(STATUS "Bundling poppler data files")
  FILE(INSTALL "${PROJECT_SOURCE_DIR}/${POPPLER_DATA_BASE}/"
    DESTINATION "${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}.app/Contents/poppler-data"
    PATTERN CMakeLists.txt EXCLUDE
    PATTERN Makefile EXCLUDE
  )

Download and install TeXworks manual

  IF ( NOT EXISTS "${PROJECT_SOURCE_DIR}/${TW_MANUAL_ARCHIVE}" )
    MESSAGE(STATUS "Downloading TeXworks HTML manual")
    FILE(DOWNLOAD "${TW_MANUAL_ARCHIVE_URL}"
      "${PROJECT_SOURCE_DIR}/${TW_MANUAL_ARCHIVE}"
      EXPECTED_MD5 "${TW_MANUAL_ARCHIVE_MD5}"
      SHOW_PROGRESS
    )
  ELSE ( )
    MESSAGE(STATUS "Using manual files in '${PROJECT_SOURCE_DIR}/${TW_MANUAL_ARCHIVE}'")
  ENDIF ()

  IF ( NOT EXISTS "${PROJECT_SOURCE_DIR}/${TW_MANUAL_BASE}" )
    FILE(MAKE_DIRECTORY "${PROJECT_SOURCE_DIR}/${TW_MANUAL_BASE}")
    EXECUTE_PROCESS(
      COMMAND unzip "${PROJECT_SOURCE_DIR}/${TW_MANUAL_ARCHIVE}"
      WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/${TW_MANUAL_BASE}"
    )
  ELSE ( )
    MESSAGE(STATUS "'${PROJECT_SOURCE_DIR}/${TW_MANUAL_BASE}' already present")
  ENDIF ()

  MESSAGE(STATUS "Bundling manual files")
  FILE(INSTALL "${PROJECT_SOURCE_DIR}/${TW_MANUAL_BASE}/TeXworks-manual"
    DESTINATION "${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}.app/Contents/texworks-help/"
  )

Copy all runtime dependencies and rewrite loader paths

Bring in DeployQt4 a CMake module taken from the Charm application:

https://github.com/KDAB/Charm

This module offers the FIXUP_QT4_BUNDLE function which wraps FIXUP_BUNDLE from CMake's BundleUtilities module and extends it with additional Qt4-specific goodies---such as installing Qt4 plugins.

FIXUP_BUNDLE is a wonderful function that examines an executable, finds all non-system libraries it depends on, copies them into the .app bundle and then re-writes the necessary loader paths.

  SET(CMAKE_MODULE_PATH @CMAKE_MODULE_PATH@)
  if (QT_VERSION_MAJOR EQUAL 4)
    INCLUDE(DeployQt4)
  elseif (QT_VERSION_MAJOR EQUAL 5)
    INCLUDE(DeployQt5)
  endif ()

Gather all TeXworks Plugin libraries.

  FILE(GLOB TeXworks_PLUGINS
    ${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}.app/Contents/PlugIns/*${CMAKE_SHARED_MODULE_SUFFIX})

If BU_CHMOD_BUNDLE_ITEMS is not set, install_name_tool will fail to re-write some loader paths due to insufficiant permissions.

  SET(BU_CHMOD_BUNDLE_ITEMS ON)

  if (QT_VERSION_MAJOR EQUAL 4)
    FIXUP_QT4_BUNDLE(${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}.app "${QT_PLUGINS}" "${TeXworks_PLUGINS}" "${TeXworks_LIB_DIRS}")
  elseif(QT_VERSION_MAJOR EQUAL 5)
    FIXUP_QT5_EXECUTABLE(${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}.app "${QT_PLUGINS}" "${TeXworks_PLUGINS}" "${TeXworks_LIB_DIRS}")
  endif()

Remove unecessary architectures from universal binaries

Some libraries copied from the OS X system, such as X11 libraries, may contain up to 4 different architectures. Here we will iterate over these libraries and use lipo to strip out un-needed architectures.

Another useful function from BundleUtilities.

  GET_BUNDLE_MAIN_EXECUTABLE(${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}.app APP_MAIN)

We look at the TeXworks binary that was built rather than consulting the value of the CMAKE_OSX_ARCHITECTURES because if the user did not set CMAKE_OSX_ARCHITECTURES, then the variable will be an empty string and the format of the resulting binary will depend on the versions of OS X and XCode.

  MESSAGE(STATUS "Reducing the size of bundled libraries.")
  MESSAGE(STATUS "Scanning architectures of: ${APP_MAIN}")
  EXECUTE_PROCESS(

lipo -info returns a list of the form:

<is universal binary?>: <program name>: <list of architectures>

Piping this output to cut -d : -f 3- allows us to extract just the list of architectures.

    COMMAND lipo -info ${APP_MAIN}
    COMMAND cut -d : -f 3-
    OUTPUT_VARIABLE APP_ARCHS
  )

Strip leading and trailing whitespace.

  STRING(STRIP ${APP_ARCHS} APP_ARCHS)

Convert spaces to semicolons so CMake will interpret the string as a list.

  STRING(REPLACE " " ";" APP_ARCHS ${APP_ARCHS})

  MESSAGE(STATUS "Will reduce bundled libraries to: ${APP_ARCHS}")

  FOREACH(ARCH IN LISTS APP_ARCHS)
    SET(ARCHS_TO_EXTRACT "${ARCHS_TO_EXTRACT} -extract ${ARCH}")
  ENDFOREACH ()

NOTE: This will not process any dylibs from Frameworks copied by FIXUP_BUNDLE, hence it may not touch any of the Qt libraries. Something to fix in the future.

  FILE(GLOB BUNDLED_DYLIBS
    ${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}.app/Contents/MacOS/*${CMAKE_SHARED_LIBRARY_SUFFIX})

  FOREACH(DYLIB IN LISTS BUNDLED_DYLIBS)

lipo prints error messages when attempting to extract from a file that is not a universal (fat) binary. Avoid this by checking first.

    EXECUTE_PROCESS(
      COMMAND lipo -info ${DYLIB}
      COMMAND cut -d : -f 1
      COMMAND grep -q "Non-fat file"
      RESULT_VARIABLE DYLIB_IS_FAT
    )
    IF(NOT ${DYLIB_IS_FAT} EQUAL 0)
      MESSAGE(STATUS "Processing fat library: ${DYLIB}")

lipo is very very anal about how arguments get passed to it. So we execute through bash to side-step the issue.

      EXECUTE_PROCESS(
        COMMAND bash -c "lipo ${ARCHS_TO_EXTRACT} ${DYLIB} -output ${DYLIB}"
      )
    ELSE()
      MESSAGE(STATUS "Skipping non-fat library: ${DYLIB}")
    ENDIF()
  ENDFOREACH ()

  MESSAGE(STATUS "Finished stripping architectures from bundled libraries.")

ENDIF ()