zip files using CMake?

20,151

Solution 1

Since version 3.2 CMake has the functionality to generate a zip file built-in. The CMake command-line mode sub-command tar supports both the creation of zip and 7zip archives.

For example, if the current CMake source directory contains the file testfile.txt and the directory testdir, you can use the following CMake commands to create a zip file containing both items:

add_custom_target(create_zip COMMAND
    ${CMAKE_COMMAND} -E tar "cfv" "archive.zip" --format=zip
       "${CMAKE_CURRENT_SOURCE_DIR}/testfile.txt"
       "${CMAKE_CURRENT_SOURCE_DIR}/testdir")

As a work-around for earlier CMake versions, you can use the jar command that is part of a standard Java JRE installation.

find_package(Java)

execute_process(
    COMMAND
        "${Java_JAR_EXECUTABLE}" "cfM" "archive.zip" 
        "-C" "${CMAKE_CURRENT_SOURCE_DIR}" "testfile.txt" 
        "-C" "${CMAKE_CURRENT_SOURCE_DIR}" "testdir"
    RESULT_VARIABLE _result
)

The zip file will be generated in the current CMake binary dir (CMAKE_CURRENT_BINARY_DIR).

Solution 2

It's never late to show real answer:

function(create_zip output_file input_files working_dir)
    add_custom_command(
        COMMAND ${CMAKE_COMMAND} -E tar "cf" "${output_file}" --format=zip -- ${input_files}
        WORKING_DIRECTORY "${working_dir}"
        OUTPUT  "${output_file}"
        DEPENDS ${input_files}
        COMMENT "Zipping to ${output_file}."
    )
endfunction()

Use like

file(GLOB ZIP_FILES "${CMAKE_CURRENT_SOURCE_DIR}/zip/*")
create_zip("${CMAKE_CURRENT_BINARY_DIR}/native_data.zip" "${ZIP_FILES}" "${CMAKE_CURRENT_SOURCE_DIR}/zip")

This will pack all files from zip/ subdirectory into native_data.zip (in build directory). Then either include your archive (path will differ in different CMakeLists.txt!) as source file or add it as target:

add_custom_target("project-data" ALL DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/native_data.zip")

Install will not differ a lot from usual:

install(FILES "${CMAKE_CURRENT_BINARY_DIR}/native_data.zip" DESTINATION ${DATADIR} RENAME "data000.zip") # Install our zip (as data000.zip)

Solution 3

I assume you already have a zip-tool installed (WinZip or 7z, etc.). You could write a find_zip-tool script which will search for WinZip, or 7Z, etc...

Snippet for WinZip:

FIND_PROGRAM(ZIP_EXECUTABLE wzzip PATHS "$ENV{ProgramFiles}/WinZip")
IF(ZIP_EXECUTABLE)
  SET(ZIP_COMMAND "\"${ZIP_EXECUTABLE}\" -P \"<ARCHIVE>\" @<FILELIST>")
ENDIF(ZIP_EXECUTABLE)

Snippet for 7-zip:

  FIND_PROGRAM(ZIP_EXECUTABLE 7z PATHS "$ENV{ProgramFiles}/7-Zip") 
  IF(ZIP_EXECUTABLE)
    SET(ZIP_COMMAND "\"${ZIP_EXECUTABLE}\" a -tzip \"<ARCHIVE>\" @<FILELIST>")
  ENDIF(ZIP_EXECUTABLE)

Take a look at the file

<cmake-install-dir>\share\cmake-2.8\Modules\CPackZIP.cmake

it shows how CPack searches for a Zip_Executable and prepares some "useful" default flags.

After that, I would suggest to execute_process, similar to sakra's answer

Solution 4

As of version 3.18, CMake now directly supports creating zip or archive files using the file() command with ARCHIVE_CREATE:

file(ARCHIVE_CREATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/MyData.zip
    PATHS ${CMAKE_CURRENT_SOURCE_DIR}/data
    FORMAT zip
)

Be sure to specify a full path for the OUTPUT zipped filename, or the file may not be generated. Also, the PATHS option accepts files or directories to be placed in the zip file, but it does not accept wildcards at the time of writing.

This command supports several archive formats and compression flavors. So, you can use the same command to create tarballs as well:

file(ARCHIVE_CREATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/MyData.tar.gz
    PATHS ${CMAKE_CURRENT_SOURCE_DIR}/data
    FORMAT gnutar
    COMPRESSION GZip
)

Solution 5

Since this is the top search result for creating zip files with CMake, here is a CPack solution for completeness. The basic idea is that you make calls to install() and then tell it what to name the resulting zip file. It will be placed in the build directory, though there may be a way to change that. Then you can create the zip file with make package or cpack.

# Version 1: Subtractive
# Include everything in the project source directory.
# Put it at the top level of the zip via `DESTINATION .`
# Subtract things we don't want.
# The trailing slash after "${PROJECT_SOURCE_DIR}/" prevents
# an extra layer of directories.
install(DIRECTORY "${PROJECT_SOURCE_DIR}/"
    DESTINATION .
    PATTERN ".git*" EXCLUDE
    PATTERN ".DS_Store" EXCLUDE
    PATTERN "examples" EXCLUDE
    PATTERN "docs" EXCLUDE
    PATTERN "README.md" EXCLUDE
)

# Version 2: Additive
# Include only the list of things we specify.
# Put it at the top level of the zip via `DESTINATION .`
# install(FILES
#     ${SRCS}
#     "Notes.txt"
#     DESTINATION .
# )

# Tell CPack to create a zip file.
set(CPACK_GENERATOR "ZIP")
# Tell CPack what to name the zip file. It will append `.zip`.
set(CPACK_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}")
# Tell CPack not to put everything inside an enclosing directory.
set(CPACK_INCLUDE_TOPLEVEL_DIRECTORY OFF)
# Apparently this should be always on but isn't for backwards compatibility.
set(CPACK_VERBATIM_VARIABLES YES)
include(CPack)
Share:
20,151

Related videos on Youtube

blubberbernd
Author by

blubberbernd

Hi!

Updated on October 12, 2020

Comments

  • blubberbernd
    blubberbernd over 3 years

    tl;dr version:

    Is it possible with CMake (>= 2.8) to generate zip files from some files and put the packed zip file in a specific location?

    longer version:

    I have a CMakeLists.txt that builds my project into a .exe file, and this exe file will read data from a zip file. The content to be packed in the zip file is in my git repository so that it can be edited, too. But, the program needs this data in a zip file. So it would be good if the CMake script could take the data, put it in a zip file, and place it next to the exe. I already heard of CPack, but I did not find any easy examples and am not sure if this is even the right tool for my task.

    Is this possible? If yes, how?

  • Begui
    Begui over 6 years
    This method seems to create an empty zip file. However, seems like i'm able to create a populated archive by specifying the "files-from=files.txt" option. Seems to fail with directories and files within directories
  • Begui
    Begui over 6 years
    Ahh, just figured it out.. I'll explain below what I did to get this to work
  • Kevin
    Kevin over 3 years
    @yig I'm not sure what you mean. This command runs at the CMake configure stage, before any of your CMake targets are even built. If you want to zip CMake targets after they are built, you can try something like this response using the POST_BUILD option.
  • yig
    yig over 3 years
    I was asking if this new 3.18 file(ARCHIVE_CREATE) functionality can be used in a target. Your answer clarifies that it cannot. Thank you.