# Copyright (c) 2010, 2024, Oracle and/or its affiliates. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2.0, # as published by the Free Software Foundation. # # This program is designed to work with certain software (including # but not limited to OpenSSL) that is licensed under separate terms, as # designated in a particular file or component or in included license # documentation. The authors of MySQL hereby grant you an additional # permission to link the program and your derivative works with the # separately licensed software that they have either included with # the program or referenced in the documentation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See # the GNU General Public License, version 2.0, for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA cmake_minimum_required(VERSION 3.0) # # Build MSI package from files installed in CMAKE_INSTALL_PREFIX location. # # Usage: # cmake -D CMAKE_INSTALL_PREFIX= # -D INSTALL_MANIFEST= ... /packaging/WiX # cmake --build . --target MSI # cmake --build . --target ZIP # # This should be invoked (in a dedicated location) after building and # installing main connector project. CMAKE_INSTALL_PREFIX should point # at install location of the main project. INSTALL_MANIFEST should point # at install manifest file generated by the builds (this happens if the # same INSTALL_MANIFEST option is given together with WITH_PACKAGES during # the builds). The manifest file describes install components defined in # the project. # # Generating MSI package # ---------------------- # During configuration time, based on the available information, this # project generates connector-cpp.wxs file from the .wxs.in template. # This file is then used by WiX tools invoked from the MSI target to # build the pacakge. # # Some other options might need to be specified to correctly generate # the .wxs definition. For example STATIC_MSVCRT or # BUNDLE_RUNTIME_LIBRARIES. Otherwise most of the information is taken # from the install manifest and version.cmake, packaging/PackageSpecs.cmake # files in the main project sources. # # Generating ZIP package # ---------------------- # ZIP package simply includes all the files from the given install # location. # IF(NOT WIN32) message(FATAL_ERROR "This project is only for Windows platform") ENDIF() # # Check that we are invoked from the main source tree # get_filename_component(BASE_DIR "../.." REALPATH) message("BASE_DIR: ${BASE_DIR}") if(NOT EXISTS "${BASE_DIR}/packaging/WiX") message(FATAL_ERROR "This CMakeList.txt should be used from within packagin/WiX foler" " within Connector/C++ source tree. Looks that this is not the case." ) endif() # # Check the install location and the manifest file # file(TO_CMAKE_PATH ${CMAKE_INSTALL_PREFIX} INSTALL_DIR) if(NOT EXISTS ${INSTALL_MANIFEST}) message(FATAL_ERROR "Could not find specified install manifest file:" " ${INSTALL_MANIFEST}" ) endif() # Project definition ########################################################################## project(Build_packages NONE) macro(main) # Determine 64 vs. 33 bit by looking at library install location (lib/ vs. lib64/) # Note: should be done before including PackageSpecs.cmake file(GLOB IS64BIT "${INSTALL_DIR}/lib*") if(NOT IS64BIT) message(FATAL_ERROR "Could not determine if build is 64 bit (no lib*/ folder inside ZIP?)") endif() get_filename_component(IS64BIT ${IS64BIT} NAME) if(IS64BIT MATCHES "lib64") set(IS64BIT true) set(BITNESS "always64") else() set(IS64BIT false) set(BITNESS "always32") endif() # # Get version and package info from the main project. # include(${BASE_DIR}/version.cmake) include(${BASE_DIR}/packaging/PackageSpecs.cmake) ################################### # MSI package ################################### # # Set variables used in .wxs.in template. # SET(MANUFACTURER "Oracle Corporation") SET(PRODUCT_NAME "MySQL Connector/C++") SET(PRODUCT_DESCRIPTION "MySQL Connector/C++") set(MAJOR_VERSION ${CONCPP_VERSION_MAJOR}) set(MINOR_VERSION ${CONCPP_VERSION_MINOR}) set(PATCH_VERSION ${CONCPP_VERSION_MICRO}) set(WIX_INSTALL_BASE "MySQL Connector C++") set(WIX_INSTALL_DIR "${WIX_INSTALL_BASE} ${MAJOR_VERSION}.${MINOR_VERSION}") # **** IMPORTANT **** # # The code below needs to be replaced when moving from one version # series to another. I.e. when moving from 4.0 to 4.1, from 4.13 to # 5.0 and so on. # # You DON'T change this code for patchlevel version changes, i.e. # when only the third part of the version is changed. # # You can use any GUID generator that produces random GUID codes. You # can also or invent a code of your own if you follow the syntax rules. #set(CONCPP_MINORMAJOR_UPGRADE_CODE "a1195164-bc2d-45fb-a5e5-1ba834771ce8") SET(UPGRADE_CODE_32_BIT "C5071C05-F2D8-4A7D-A089-F3347B2C720F") SET(UPGRADE_CODE_64_BIT "9CE29674-49E6-4D9D-B424-E851CC983906") SET(UPGRADE_CODE_OLD "FE990D78-8BB1-4880-930A-0430E707F3CA") set(LICENSE_RTF "${INSTALL_DIR}/LICENSE.rtf") # Generate .rtf from .txt, if not already present generate_rtf(${LICENSE_RTF}) if(STATIC_MSVCRT) set(STATIC_MSVCRT "ON") else() set(STATIC_MSVCRT "OFF") endif() show(STATIC_MSVCRT) if(BUNDLE_RUNTIME_LIBRARIES) set(BUNDLE_RUNTIME_LIBRARIES "ON") else() set(BUNDLE_RUNTIME_LIBRARIES "OFF") endif() show(BUNDLE_RUNTIME_LIBRARIES) if(IS64BIT) set(IS64BIT "yes") set(PLATFORM x64) set(UPGRADE_CODE "${UPGRADE_CODE_64_BIT}") set(PROGRAM_FILES_FOLDER ProgramFiles64Folder) else() set(IS64BIT "no") set(PLATFORM x86) set(UPGRADE_CODE "${UPGRADE_CODE_32_BIT}") set(PROGRAM_FILES_FOLDER ProgramFilesFolder) endif() show(IS64BIT) if(NOT WIX_UI) set(WIX_UI "WixUI_Mondo_Custom") endif() list(APPEND WIX_INCLUDE "${CMAKE_CURRENT_SOURCE_DIR}/custom_ui.wxs") # Location of other files used to build MSI such as icons etc. set(SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) # # Generate WIX_DIRECTORIES, WIX_COMPONENTS and WIX_COMPONENT_GROUPS # fragments used in connector-cpp.wxs.in based on information from # install manifest. # include(${INSTALL_MANIFEST}) set_wix_components() # # Generate .wxs definition from the template # configure_file(connector-cpp.wxs.in ${CMAKE_CURRENT_BINARY_DIR}/connector-cpp.wxs) # Clean up cache set(WIX_DIRECTORIES "" CACHE STRING "output var" FORCE) set(WIX_COMPONENTS "" CACHE STRING "output var" FORCE) set(WIX_COMPONENT_GROUPS "" CACHE STRING "output var" FORCE) set(WIX_INCLUDES "" CACHE STRING "output var" FORCE) # # Target for building MSI package # include(wix_setup.cmake) set(EXTRA_WIX_ARGS $ENV{EXTRA_WIX_ARGS}) add_custom_target(MSI COMMAND ${WIX_EXECUTABLE} build -ext WixToolset.UI.wixext -ext WixToolset.Util.wixext -arch ${PLATFORM} connector-cpp.wxs -out ${CMAKE_BINARY_DIR}/${CPACK_PACKAGE_FILE_NAME}.msi ${EXTRA_WIX_ARGS} ) ################################### # ZIP package ################################### # # We use cpack to generate ZIP package. All the files from INSTALL_DIR # are "installed" again, and this tells cpack to include them in # the generated ZIP package. # # Note: All required cpack settings are defined in PackageSpecs.cmake # included above. # set(CPACK_GENERATOR ZIP) install(DIRECTORY ${INSTALL_DIR}/ DESTINATION . PATTERN "install_manifest.cmake" EXCLUDE PATTERN "LICENSE.rtf" EXCLUDE ) include(CPack) # # Target for building ZIP package # # We simply build the 'package' target that is defined by CPack # add_custom_target(ZIP COMMAND ${CMAKE_COMMAND} --build . --target package WORKING_DIRECTORY ${PROJECT_BINARY_DIR} ) endmacro(main) ######################################################################## # # Set WIX_DIRECTORIES, WIX_COMPONENTS and WIX_COMPONENT_GROUPS # variables to .wxs fragments that define WiX components and files # they contain. # function(set_wix_components) # Parse information from install manifest, setting the following # variables: # # - DIR_LIST -- list of install directory suffixes # - DIRS${D} -- list of sub-directories in a directory with suffix ${D} # - FILES${C}${D} -- list of files from install component ${C} inside # directory with suffix ${D} # - GROUP${C} -- list of suffixes of directories which have files from # comonent ${C} # # - PATH${D}_${F} -- path to file F in directory with suffix ${D} # parse_install_info() # Define direcrtories set(OUT_VAR WIX_DIRECTORIES) out("") define_dirs(" ") out("") # Define components # Note: Currently we do not put Debuginfo component into MSI, only ZIP set(COMPONENTS ${COMPONENTS}) list(REMOVE_ITEM COMPONENTS "Debuginfo") set(OUT_VAR WIX_COMPONENTS) foreach(d_suffix ${DIR_LIST}) get_dir_id(${d_suffix} d_id) foreach(C ${COMPONENTS}) if(NOT FILES${C}${d_suffix}) continue() endif() get_comp_id(${C}${d_suffix} c_id) generate_guid(GUID) out("") out(" ") foreach(F ${FILES${C}${d_suffix}}) get_file_id(${d_suffix}_${F} id path) out(" ") endforeach() out(" ") out("") endforeach() endforeach() # Define component groups set(OUT_VAR WIX_COMPONENT_GROUPS) foreach(C ${COMPONENTS}) if(NOT GROUP${C}) continue() endif() out("") foreach(d_suffix ${GROUP${C}}) get_comp_id(${C}${d_suffix} c_id) out(" ") endforeach() out("") endforeach() # Includes set(OUT_VAR WIX_INCLUDES) foreach(I ${WIX_INCLUDE}) out("") endforeach() endfunction(set_wix_components) # # Parse information from install manifest (COMPONENTS, FILES_${C}) and # extract it into variables expected by set_wix_components() # macro(parse_install_info) set(DIRS) foreach(C ${COMPONENTS}) if(C STREQUAL "Auxiliary" OR NOT FILES_${C}) continue() endif() #message("- component: ${C}") foreach(F ${FILES_${C}}) #message("-- file: ${F}") get_filename_component(F_name ${F} NAME) get_filename_component(F_path ${F} PATH) # transform path to a list of folder names, for example # if F_path is /foo/bar/baz then F_list will be [foo,bar,baz] set(F_list) get_path(F_list ${F_path}) #message("-- path: ${F_list} > ${F_name}") # Add all components of the path to the DIRS${suffix} # lists which list subdirectories of each directory. # In the example above, with F_list equal [foo,bar,baz] # this will happen: # # - "foo" is added to DIRS list, # - "bar" is added to DIRS_foo list, # - "baz" is added to DIRS_foo_bar list # # After this ${suffix} is _foo_bar_baz and corresponds # to the full path of the file. set(suffix "") foreach(D ${F_list}) list(APPEND DIRS${suffix} ${D}) list(REMOVE_DUPLICATES DIRS${suffix}) set(suffix "${suffix}_${D}") endforeach() # Add file name to the FILES${suffix} list of files # at path given by ${suffix}. Also store the actual path # to the file in PATH... list(APPEND FILES${C}${suffix} ${F_name}) set(PATH${suffix}_${F_name} ${F}) # Add file path suffix to the list of all directories # and to the list of directories for the component ${C} list(APPEND DIR_LIST ${suffix}) list(REMOVE_DUPLICATES DIR_LIST) list(APPEND GROUP${C} ${suffix}) list(REMOVE_DUPLICATES GROUP${C}) endforeach() endforeach() endmacro(parse_install_info) # # Output directory definitions (recursive) # function(define_dirs) set(indent ${ARGV0}) set(suffix ${ARGV1}) foreach(D ${DIRS${suffix}}) set(d_suffix "${suffix}_${D}") if(d_suffix STREQUAL "_.") continue() endif() get_dir_id(${d_suffix} id) out("${indent}") define_dirs("${indent} " ${d_suffix}) out("${indent}") endforeach() endfunction(define_dirs) # # Generate WiX identifiers for different objects # function(get_comp_id SUFFIX ID) make_wix_identifier("${SUFFIX}" id) set(${ID} "C.${id}" PARENT_SCOPE) endfunction() function(get_dir_id SUFFIX ID) if(SUFFIX STREQUAL "_.") set(${ID} "INSTALLDIR" PARENT_SCOPE) else() make_wix_identifier("${SUFFIX}" id) set(${ID} "D.${id}" PARENT_SCOPE) endif() endfunction() function(get_file_id SUFFIX ID PATH) make_wix_identifier("${SUFFIX}" id) set(${ID} "F.${id}" PARENT_SCOPE) set(${PATH} ${PATH${SUFFIX}} PARENT_SCOPE) endfunction() function(make_wix_identifier STR VAR) string(MD5 out ${STR}) string(MAKE_C_IDENTIFIER ${STR} STR) string(LENGTH ${STR} len) if(len GREATER 30) math(EXPR len ${len}-30) string(SUBSTRING ${STR} ${len} -1 STR) endif() set(${VAR} "${STR}.${out}" PARENT_SCOPE) endfunction() # # Generate UUIDs # function(generate_guid VAR) string(RANDOM x) string(UUID guid NAMESPACE "96122528-4F58-40F8-AB22-96F9853460F8" NAME "${x}" TYPE MD5 UPPER) # message(STATUS "generated guid: ${guid}") set(${VAR} ${guid} PARENT_SCOPE) endfunction() # # Convert path into a list # function(get_path OUT PATH) get_filename_component(top ${PATH} NAME) get_filename_component(leading ${PATH} PATH) if(leading AND NOT (leading STREQUAL ".")) get_path(${OUT} ${leading}) endif() list(APPEND ${OUT} ${top}) set(${OUT} ${${OUT}} PARENT_SCOPE) endfunction() # # If the given .rtf file does not exist, try to generate it from # a corresponding .txt file. # function(generate_rtf FILE) if(EXISTS ${FILE}) return() endif() get_filename_component(f_name ${FILE} NAME_WE) get_filename_component(f_path ${FILE} PATH) find_file(${f_name}_TXT "${f_name}.txt" PATHS ${f_path} PATH_SUFFIXES "." "../.." NO_DEFAULT_PATH ) if(NOT EXISTS ${${f_name}_TXT}) return() endif() FILE(READ ${${f_name}_TXT} CONTENTS) STRING(REGEX REPLACE "\n" "\\\\par\n" CONTENTS "${CONTENTS}") STRING(REGEX REPLACE "\t" "\\\\tab" CONTENTS "${CONTENTS}") FILE(WRITE ${FILE} "{\\rtf1\\ansi\\deff0{\\fonttbl{\\f0\\fnil\\fcharset0 Courier New;}}\\viewkind4\\uc1\\pard\\lang1031\\f0\\fs15") FILE(APPEND ${FILE} "${CONTENTS}") FILE(APPEND ${FILE} "\n}\n") endfunction() # # Append string to OUT_VAR # macro(out) if(NOT OUT_VAR) message(FATAL_ERROR "Output variable not defined") endif() #message(STATUS "${ARGV}") set(${OUT_VAR} "${${OUT_VAR}}${ARGV}\n" CACHE STRING "output variable" FORCE) endmacro() function(show VAR) message("- ${VAR}: ${${VAR}}") endfunction() main() return()