Question

I use CMake to manage a project with the following layout:

ProjectA/
  include
  doc
  test
ProjectB
  include
  doc
  test

I would like to use CPack to package up a tar.gz source archive with the following layout:

include/
ProjectA/
  doc
  test
ProjectB/
  doc
  test

where the new top-level include contains all include files.

I tried achieving this by running a CMake script through CPACK_INSTALL_SCRIPT, but this script runs before the files are created. Where can I hook into CPack to get that effect?

It also seems that install has no influence on what make package_source does, but it has an effect on make package. Unfortunately make package will also build and package libraries, which is something I don't want to happen. I want a pure source distribution ala make dist.

Was it helpful?

Solution

You can call install(DIRECTORY include DESTINATION include) on headers from both ProjectA and ProjectB and install them into the same dir. This would cause CPack to place them together in the generated package.

Update

Okay, i've managed to do this with CPACK_INSTALL_SCRIPT variable.

I've created following script:

set(CMAKE_SOURCE_DIR @CMAKE_SOURCE_DIR@)

execute_process(COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/A/ ${CMAKE_SOURCE_DIR})
execute_process(COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/B/ ${CMAKE_SOURCE_DIR})
execute_process(COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_SOURCE_DIR}/A)
execute_process(COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_SOURCE_DIR}/B)

In the CMakeLists.txt i've populated it with actual value of CMAKE_SOURCE_DIR:

configure_file(CPackScript.cmake CPackScript.cmake @ONLY)

Finally, i've added set(CPACK_INSTALL_SCRIPT ${CMAKE_BINARY_DIR}/CPackScript.cmake) and voila! Generated packages now have include dir with headers from both A/ and B/, while A/ and B/ dirs don't exist.

OTHER TIPS

Another possible approach is to completely bypass CPack for the generation of the TGZ archive and simply use a CMake custom target which generates the archive:

project (MyProject)

set (DIST_TEMP_DIR "${CMAKE_BINARY_DIR}/dist")
make_directory(${DIST_TEMP_DIR})

add_custom_target(dist 
    COMMAND ${CMAKE_COMMAND} -E remove_directory "${DIST_TEMP_DIR}/${PROJECT_NAME}/"
    COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/ProjectA/include" "${DIST_TEMP_DIR}/${PROJECT_NAME}/include"
    COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/ProjectA/test" "${DIST_TEMP_DIR}/${PROJECT_NAME}/ProjectA/test"
    COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/ProjectA/doc" "${DIST_TEMP_DIR}/${PROJECT_NAME}/ProjectB/doc"
    COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/ProjectB/include" "${DIST_TEMP_DIR}/${PROJECT_NAME}/include"
    COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/ProjectB/doc" "${DIST_TEMP_DIR}/${PROJECT_NAME}/ProjectA/doc"
    COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/ProjectB/test" "${DIST_TEMP_DIR}/${PROJECT_NAME}/ProjectB/test"
    COMMAND ${CMAKE_COMMAND} -E tar cvz "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.tar.gz"  "${DIST_TEMP_DIR}/${PROJECT_NAME}/"
    COMMENT "Building ${PROJECT_NAME}.tar.gz"
    WORKING_DIRECTORY "${DIST_TEMP_DIR}"
)

The custom target dist first sets up the desired structure of the archive in a temporary directory inside the build folder by invoking CMake in command mode with multiple copy_directory commands. Then it generates the tar.gz archive by using the tar command mode sub-command.

I am not sure what excactly is not working for you.

This is what works for me for the ZIP generator on windows. I will try to find a linux machine to see if this works with TGZ as well (EDIT: it does):

Directory structure:

CMakeLists.txt
ProjectA/
    doc
        foo.txt
    include
        foo.h
    test
        foo.test
ProjectB/
    doc
        bar.txt
    include
        bar.h
    test
        bar.test

ProjectA/CMakeLists.txt

project( ProjectA )

INSTALL( DIRECTORY include DESTINATION . )
INSTALL( DIRECTORY doc DESTINATION ${PROJECT_NAME}/ )
INSTALL( DIRECTORY test DESTINATION ${PROJECT_NAME}/ )

ProjectB/CMakeLists.txt

project( ProjectB )

INSTALL( DIRECTORY include DESTINATION . )
INSTALL( DIRECTORY doc DESTINATION ${PROJECT_NAME}/ )
INSTALL( DIRECTORY test DESTINATION ${PROJECT_NAME}/ )

CMakeLists.txt:

project( MyProject )

ADD_SUBDIRECTORY(ProjectA)
ADD_SUBDIRECTORY(ProjectB)

INCLUDE(CPack)

If I create the package, I get

MyProject-0.1.1-win32.zip
    MyProject-0.1.1-win32
        include
            bar.h
            foo.h
        ProjectA
            doc
                foo.txt
            test
                foo.test
        ProjectB
            doc
                bar.txt
            test
                bar.test

Is that what you intended?

Source packages

For source package creation, CPack by default ignores the install commands and installs/copies all directories that are specified in CPACK_SOURCE_INSTALLED_DIRECTORIES. This variable contains pairs of source and destination directories. It defaults to "${CMAKE_SOURCE_DIR};/" if not manually set. To be precise, it globs these directories, ignoring all files in CPACK_SOURCE_IGNORE_FILES which defaults to all major VCS bookkeeping files (see your CPackSourceConfig.cmake for example).

You you could do the following:

CMakeLists.txt

project( MyProject )

SET( PROJECTS ProjectA ProjectB )

SET(CPACK_SOURCE_INSTALLED_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR};/") 
FOREACH( p ${PROJECTS} ) 
    ADD_SUBDIRECTORY( ${p} )
    LIST( APPEND CPACK_SOURCE_INSTALLED_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR/${p}/include include )
ENDFOREACH()


INCLUDE(CPack)

or add the manipulation to of CPACK_SOURCE_INSTALLED_DIRECTORIES to the respective project files.

However: This will install your include directories to the top-level include directory, but additionally still create another copy inside the project directories due to globbing. You could probably create additional directory-pairs for your doc and test directories and skip the initialization of the CPACK_SOURCE_INSTALLED_DIRECTORIES in the SET command. If you do this, you will need to find a way to install/copy your project-specific CMake files.

BIG CAVEAT: If ProjectAor ProjectB refer to the project-local include directory (eg. with INCLUDE_DIRECTORIES(...) you will break your CMake code, rendering the source installation (partially) useless. So you might want to rethink your idea of the top-level include directory.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top