通过CPack将库打包为.sh可执行包

        软件开发完成后对外发布后,通常需要将生成的库文件和头文件进行打包发布,供其他人安装使用,使用CMake进行构建的项目中,通常使用CPack进行软件包的打包和发布,本文将介绍如何通过CPack将软件包打包为.sh包发布,并替换默认的脚本文件,定制安装操作。

一、CPack简介

        CPack是一款优秀的跨平台软件打包发布工具,支持丰富的打包发布格式,包括NSIS、RPM、DEB、TGZ和STGZ等。不同的操作系统下,有多种对应的CPack打包器,其中Windows平台一下一般选择通过NSIS打包,生成.exe可执行文件进行交互式安装。Linux下Ubuntu系统一般通过DEB打包,生成.deb文件(Ubuntn不支持rpm包,需要将rpm包转换为deb后再安装)。而RedHat系统下,可通过RPM打包器打包为.rpm包。对应不同操作系统,也可以通过CPack将软件打包为与平台无关的TGZ和ZIP等压缩包。

        Windows下打包为.exe可执行文件进行安装,是可以支持寻找软件包安装目录,并可通过添加安装后处理脚本,自动添加环境变量等操作的,比较友好。Linux的.rpm包也是可以修改默认安装路径的,而.deb包的默认安装路径是/usr/local或者/usr,对于开发环境下多用户的服务器,会有很多不友好的地方,因此通过STZG打包器生成.sh包,进行交互式安装是一个比较好的替代方式。

        CPack打包哪些文件是依赖于CMake中的install操作的,CPack只是打包工具,不指定打包文件,CMake install请参考其他博客。

二、STGZ包简介

        Linux下的STGZ包后缀通常是.sh和.run,其结构是一样的,都是一个sh脚本和压缩包文件拼接在一起后生成的,sh脚本中定义了软件包安装的一些操作,包括校验压缩包,解压压缩包,将文件拷贝到对应目录和设置环境变量等等,可以根据软件包安装需要进行定义。压缩包通常就是软件包对应的头文件和库文件了。

cat install.sh software.tag.gz > stgz_install.sh

 三、通过CPack生成.sh包

        在指定了install内容之后,可通过设置CPACK_GENERATOR为STGZ来指定STGZ生成器将软件打包为.sh文件。打包的其他设置也可以通过对应CPack变量来进行设置,具体可设置的变量和对应功能可查阅官方文档(CPack官方文档),特别需要注意的是,只有CPack对应的变量才能在STGZ打包器的sh脚本中读取到,其他CMake变量在sh脚本中无法获取,因此如果想要传递一些设置到sh脚本里,只能通过CPack变量传递。

set(CPACK_GENERATOR "STGZ")
set(CPACK_PACKAGING_INSTALL_PREFIX "/")
set(CPACK_PACKAGE_VENDOR "test")
set(CPACK_PACKAGE_NAME "${CMAKE_PROJECT_NAME}")
set(CPACK_PACKAGE_VERSION "${CMAKE_PROJECT_VERSION}")
set(CPACK_PACKAGE_DESCRIPTION "${CMAKE_PROJECT_NAME} library")
set(CPACK_PACKAGE_MAINTAINER "test")
set(CPACK_PACKAGE_HOMEPAGE_URL "http://xxxx.com")

set (TAR_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${CMAKE_HOST_SYSTEM_NAME}-any")
if (DEFINED CUDA_VERSION)
	set (TAR_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${CMAKE_HOST_SYSTEM_NAME}-CUDA${CUDA_VERSION}")
elseif (DEFINED CUDAToolkit_VERSION)
	set (TAR_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${CMAKE_HOST_SYSTEM_NAME}-CUDA${CUDAToolkit_VERSION}")
endif ()
set(CPACK_PACKAGE_FILE_NAME "${TAR_FILE_NAME}")

set(CPACK_PACKAGE_DIRECTORY package)
include(CPack)

         CPack有默认的sh脚本,不过sh脚本功能比较简单,只支持展示license以及是否添加次文件夹操作,难以满足实际需要,如下图glog的.sh包安装所示。

 四、替换CPack默认的sh脚本,定制安装操作

       CPack默认的sh安装脚本,功能过于简单,如果想要添加额外的安装操作,就需要替换掉CPack默认的sh安装脚本,并定制安装操作。只需要自己写一个shell脚本,并命名为CPack.STGZ_Header.sh.in,.in文件是CMake中的模板文件,模板文件中的通过@包裹的@xxx@模板变量会在CMake阶段替换实际的内容,前面也说到,sh脚本中只能获取到CPack的对应变量,而无法获取到CMake中的其他变量,这是需要特别注意的。通过CMake的CMAKE_MODULE_PATH将包含CPack.STGZ_Header.sh.in的路径设置为CMake的模块路径,引入脚本文件就可以替换掉默认的sh脚本文件,CPack就会使用你的sh脚本来生成sh包。

set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/Modules")

        这里提供了一个安装脚本模板,可在此基础上进行修改,支持交互式选择安装目录,并自动添加环境变量,也可以通过命令行参数设置对应的参数。

#!/bin/bash
#
# NAME:  @CPACK_PACKAGE_NAME@
# VER:   @CPACK_PACKAGE_VERSION@
# PLAT:  linux-64
# LINES: 328

# check shell avoid running in the parent process
if ! echo "$0" | grep '\.sh$' > /dev/null; then
    printf 'Please run using "bash" or "sh", but not "." or "source"\\n' >&2
    return 1
fi

# Determine RUNNING_SHELL; if SHELL is non-zero use that.
if [ -n "$SHELL" ]; then
    RUNNING_SHELL="$SHELL"
else
    if [ "$(uname)" = "Darwin" ]; then
        RUNNING_SHELL=/bin/bash
    else
        if [ -d /proc ] && [ -r /proc ] && [ -d /proc/$$ ] && [ -r /proc/$$ ] && [ -L /proc/$$/exe ] && [ -r /proc/$$/exe ]; then
            RUNNING_SHELL=$(readlink /proc/$$/exe)
        fi
        if [ -z "$RUNNING_SHELL" ] || [ ! -f "$RUNNING_SHELL" ]; then
            RUNNING_SHELL=$(ps -p $$ -o args= | sed 's|^-||')
            case "$RUNNING_SHELL" in
                */*)
                    ;;
                default)
                    RUNNING_SHELL=$(which "$RUNNING_SHELL")
                    ;;
            esac
        fi
    fi
fi

# Some final fallback locations
if [ -z "$RUNNING_SHELL" ] || [ ! -f "$RUNNING_SHELL" ]; then
    if [ -f /bin/bash ]; then
        RUNNING_SHELL=/bin/bash
    else
        if [ -f /bin/sh ]; then
            RUNNING_SHELL=/bin/sh
        fi
    fi
fi

if [ -z "$RUNNING_SHELL" ] || [ ! -f "$RUNNING_SHELL" ]; then
    printf 'Unable to determine your shell. Please set the SHELL env. var and re-run\\n' >&2
    exit 1
fi

THIS_DIR=$(DIRNAME=$(dirname "$0"); cd "$DIRNAME"; pwd)
THIS_FILE=$(basename "$0")
THIS_PATH="$THIS_DIR/$THIS_FILE"
PREFIX=$HOME/software/@CPACK_PACKAGE_NAME@
UPREFIX=0
SETENV=0
DSETENV=0
FORCE=0
USAGE="
usage: $0 [options]

Installs @CPACK_PACKAGE_NAME@ @CPACK_PACKAGE_VERSION@

-h|--help              print this help message and exit    
-p|--prefix=<path>     install prefix, defaults to $PREFIX, must not contain spaces
-e|--env               automatic setup of environment variables
-d|--disable-env       not automatic setup of environment variables
-f|--force             clear folder if install prefix folder already exists  
"

OPTS=$(getopt -o hp:edf -l help,prefix:,env,disable-env,force -- "$@" 2>/dev/null)
if [ ! $? ]; then
    printf "%s\\n" "$USAGE"
    exit 2
fi
eval set -- "$OPTS"

while true; do
    case "$1" in
        -h|--help)
            printf "%s\\n" "$USAGE"
            exit 2
            ;;
        -f|--force)
            FORCE=1
            shift
            ;;
        -p|--prefix)
            PREFIX="$2"
            UPREFIX=1
            shift
            shift
            ;;
        -e|--env)
            SETENV=1
            shift
            ;;
        -d|--disable-env)
            DSETENV=1
            shift
            ;;
        --)
            shift
            break
            ;;
        *)
            printf "ERROR: did not recognize option '%s', please try -h\\n" "$1"
            exit 1
            ;;
    esac
done

# check tar
if ! tar --help >/dev/null 2>&1; then
    printf "WARNING: tar does not appear to be installed this may cause problems below\\n" >&2
fi

# check system architecture type
if [ "$(uname -m)" != "x86_64" ]; then
    printf "WARNING:\\n"
    printf "    Your operating system appears not to be 64-bit, but you are trying to\\n"
    printf "    install a 64-bit version of @CPACK_PACKAGE_NAME@.\\n"
    printf "    Are sure you want to continue the installation? [yes|no]\\n"
    printf "[no] >>> "
    read -r ans
    if [ "$ans" != "yes" ] && [ "$ans" != "Yes" ] && [ "$ans" != "YES" ] && \
        [ "$ans" != "y" ]   && [ "$ans" != "Y" ]
    then
        printf "Aborting installation\\n"
        exit 2
    fi
fi

#check operating system type
if [ "$(uname)" != "Linux" ]; then
    printf "WARNING:\\n"
    printf "    Your operating system does not appear to be Linux, \\n"
    printf "    but you are trying to install a Linux version of @CPACK_PACKAGE_NAME@.\\n"
    printf "    Are sure you want to continue the installation? [yes|no]\\n"
    printf "[no] >>> "
    read -r ans
    if [ "$ans" != "yes" ] && [ "$ans" != "Yes" ] && [ "$ans" != "YES" ] && \
        [ "$ans" != "y" ]   && [ "$ans" != "Y" ]
    then
        printf "Aborting installation\\n"
        exit 2
    fi
fi

printf "\\n"
printf "Welcome to @CPACK_PACKAGE_NAME@ @CPACK_PACKAGE_VERSION@\\n"
printf "\\n"
printf "@CPACK_PACKAGE_NAME@ will now be installed into this location:\\n"
printf "%s\\n" "$PREFIX"
printf "\\n"

if [ "$UPREFIX" = "0" ]; then
    printf "  - Press ENTER to confirm the location\\n"
    printf "  - Press CTRL-C to abort the installation\\n"
    printf "  - Or specify a different location below\\n"
    printf "\\n"
    printf "[%s] >>> " "$PREFIX"
    read -r user_prefix
    if [ "$user_prefix" != "" ]; then
        case "$user_prefix" in
            *\ * )
                printf "ERROR: Cannot install into directories with spaces\\n" >&2
                exit 1
                ;;
            *)
                eval PREFIX="$user_prefix"
                ;;
        esac
    fi
fi

case "$PREFIX" in
    *\ * )
        printf "ERROR: Cannot install into directories with spaces\\n" >&2
        exit 1
        ;;
esac

# confirm whether to overwrite
if [ "$FORCE" = "0" ] && [ -e "$PREFIX" ]; then
    if [ "$(ls -A $PREFIX)" ]; then
        printf "ERROR: File or directory is not empty: '%s'\\n" "$PREFIX" >&2
        printf "Or use -f/--force to force the installation which will clear the original directory.\n" >&2
        exit 1
    fi
fi

if [ "$FORCE" = "1" ] && [ -e "$PREFIX" ]; then
    if [ "$(ls -A $PREFIX)" ]; then
        force_ans=no
        printf "! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !\\n"
        printf "Do you wish to clear the installation directory: '%s' ? [yes|no]\\n" "$PREFIX"
        printf "\\n"
        printf "[default is %s] >>> " "$force_ans"
        read -r uans
        if [ "$uans" != "" ]; then
            force_ans=$uans
        fi

        if [ "$force_ans" = "yes" ] || [ "$force_ans" = "Yes" ] || [ "$force_ans" = "YES" ] || \
            [ "$force_ans" = "y" ] || [ "$force_ans" = "Y" ]; then
            rm -fr $PREFIX/*
            printf "\\n"
            printf "Finished to clear the installation directory: '%s'!\\n" "$PREFIX"
            printf "\\n"
        fi
    fi
fi

# creat install directory
if ! mkdir -p "$PREFIX"; then
    printf "ERROR: Could not create directory: '%s'\\n" "$PREFIX" >&2
    exit 1
fi

PREFIX=$(cd "$PREFIX"; pwd)
export PREFIX
printf "install prefix = %s\\n" "$PREFIX"

# extract the tarball appended to this header, this creates the *.tar.gz files
# for all the packages which get installed below
cd "$PREFIX"
printf "Extracting, please wait...\\n"

if ! tail -n +328 "$THIS_PATH" | tar xzvf -; then
    printf "ERROR: could not extract tar starting at line 328\\n" >&2
    exit 1
fi

printf "installation finished.\\n"

BASH_RC="$HOME"/.bashrc
eans=no
ask=yes

if [ "$SETENV" = "1" ]; then
    if [ "$DSETENV" = "1" ]; then
        eans=no
        ask=yes
    else
        eans=yes
        ask=no
    fi
else
    if [ "$DSETENV" = "1" ]; then
        eans=no
        ask=no
    else
        eans=no
        ask=yes
    fi
fi

if [ "$ask" = "yes" ]; then
    printf "Do you wish the installer to initialize @CPACK_PACKAGE_NAME@\\n"
    printf "in your %s ? [yes|no]\\n" "$BASH_RC"
    printf "[default is %s] >>> " "$eans"

    read -r ans
    if [ "$ans" != "" ]; then
        eans=$ans
    fi
fi

typeset -u PROJECT_UNAME
PROJECT_UNAME=@CPACK_PACKAGE_NAME@

if [ "$eans" != "yes" ] && [ "$eans" != "Yes" ] && [ "$eans" != "YES" ] && \
    [ "$eans" != "y" ]   && [ "$eans" != "Y" ]; then
    printf "\\n"
    printf "You may need to set "$PROJECT_UNAME"_DIR manually in your $BASH_RC or /etc/profile\\n"
    printf "\\n"
    printf "export "$PROJECT_UNAME"_DIR=\"$PREFIX/lib/cmake/@CPACK_PACKAGE_NAME@\"\\n"
    printf "\\n"
    printf "If it is a dynamic library, you also need to set LD_LIBRARY_PATH in your $BASH_RC or /etc/profile\\n"
    printf "\\n"
    printf "export LD_LIBRARY_PATH=\"$PREFIX/lib:\$LD_LIBRARY_PATH\"\\n"
    printf "\\n"
    printf "Please source your $BASH_RC before include @CPACK_PACKAGE_NAME@ \\n"
    printf "\\n"
else
    printf "\\n"
    printf "Initializing @CPACK_PACKAGE_NAME@ in %s\\n" "$BASH_RC"
    printf "\\n"

    tips=no

    addDir="export "$PROJECT_UNAME"_DIR=\"$PREFIX/lib/cmake/@CPACK_PACKAGE_NAME@\""
    if ! grep "$addDir" $BASH_RC > /dev/null 2>&1; then
        echo "# @CPACK_PACKAGE_NAME@_DIR" >> $BASH_RC
        echo ${addDir} >> $BASH_RC
        printf "DIR of @CPACK_PACKAGE_NAME@ has been added in $BASH_RC\\n"
        tips=yes
    else
        printf "DIR of @CPACK_PACKAGE_NAME@ already exists in $BASH_RC\\n"
    fi

    if [ ! "$(find $PREFIX/lib -name "*.a")" ]; then
        addLDPath="export LD_LIBRARY_PATH=\"$PREFIX/lib:\$LD_LIBRARY_PATH\""
        if ! grep "$addLDPath" $BASH_RC > /dev/null 2>&1; then
            echo "# @CPACK_PACKAGE_NAME@ LD_LIBRARY_PATH" >> $BASH_RC
            echo ${addLDPath} >> $BASH_RC
            printf "LD_LIBRARY_PATH of @CPACK_PACKAGE_NAME@ has been added in $BASH_RC\\n"
            tips=yes
        else
            printf "LD_LIBRARY_PATH of @CPACK_PACKAGE_NAME@ already exists in $BASH_RC\\n"
        fi
    fi

    printf "\\n"
    if [ "$tips" = "yes" ]; then
        printf "Please source your $BASH_RC before include @CPACK_PACKAGE_NAME@ \\n"
        printf "\\n"
    fi
fi

printf "Thank you for installing @CPACK_PACKAGE_NAME@!\\n"
exit 0

### Start of TAR.GZ file ###;

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要使用CPack打包外部库,需要按照以下步骤操作: 1.在CMakeLists.txt文件中定义要打包的目标,括库文件和头文件。例如: ``` add_library(mylib SHARED mylib.cpp) target_include_directories(mylib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) ``` 2.在CMakeLists.txt文件中添加CPack相关设置。例如: ``` set(CPACK_GENERATOR "ZIP") set(CPACK_PACKAGE_NAME "mylib") set(CPACK_PACKAGE_VERSION "1.0.0") set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "My library") set(CPACK_PACKAGE_VENDOR "My Company") set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}") set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE") set(CPACK_SOURCE_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-src") set(CPACK_SOURCE_GENERATOR "ZIP") set(CPACK_SOURCE_IGNORE_FILES "/build/" "/.git/" "/.idea/" "/tmp/" "/dist/" ) include(CPack) ``` 其中,`CPACK_GENERATOR`设置打包格式,`CPACK_PACKAGE_NAME`设置名,`CPACK_PACKAGE_VERSION`设置版本号,`CPACK_PACKAGE_DESCRIPTION_SUMMARY`设置简介,`CPACK_PACKAGE_VENDOR`设置厂商,`CPACK_PACKAGE_FILE_NAME`设置生成的文件名,`CPACK_SOURCE_PACKAGE_FILE_NAME`设置源代码的文件名,`CPACK_RESOURCE_FILE_LICENSE`设置许可证文件路径,`CPACK_SOURCE_IGNORE_FILES`设置忽略的文件。 3.使用`cpack`命令生成打包文件。例如: ``` $ mkdir build $ cd build $ cmake .. $ make $ cpack --config CPackConfig.cmake ``` 生成的打包文件会在build目录下生成。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值