How to specify different folder structure for CPack TGZ generator? - cmake

I have a CMake project that installs things to a system according to the install command as follows:
install (
TARGETS myTarget
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
)
make install works perfectly. And then I want to have a binary archive:
set(CPACK_GENERATOR "TGZ")
make package produces an tar.gz file with the same folder structure as the install command specified. However, I want to have a flat structure, that is, put everything (both executables and libraries) in "prefix", without the "bin" and "lib" directory.
Is that possible? May be with some clever use of the component system, the build type system, or CPACK_PROJECT_CONFIG_FILE?

At the end I added a custom install script, which detects whether it is run by CPack by looking at CMAKE_INSTALL_PREFIX, and restructure the install tree if necessary.
Here is my solution:
In CMakeLists.txt, after all the install() commands, add
install(SCRIPT "${CMAKE_SOURCE_DIR}/cmake/flatten.cmake")
Add a file, "cmake/flatten.cmake", with content as follows
# Detect if the install is run by CPack.
if (${CMAKE_INSTALL_PREFIX} MATCHES "/_CPack_Packages/.*/(TGZ|ZIP)/")
# Flatten the directory structure such that everything except the header files is placed in root.
file(GLOB bin_files LIST_DIRECTORIES FALSE ${CMAKE_INSTALL_PREFIX}/bin/*)
file(GLOB lib_files LIST_DIRECTORIES FALSE ${CMAKE_INSTALL_PREFIX}/lib/*)
foreach(file ${bin_files} ${lib_files})
get_filename_component(file_name ${file} NAME)
execute_process(
COMMAND ${CMAKE_COMMAND} -E rename
${file}
${CMAKE_INSTALL_PREFIX}/${file_name}
)
endforeach()
execute_process( COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_INSTALL_PREFIX}/bin)
execute_process( COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_INSTALL_PREFIX}/lib)
endif()

Related

How to copy a file if modified and avoid rebuilding executables and libraries when running make all?

I want to copy a file from the source directory to the binary directory when running make or make all if the file has been modified. So if only this file has been modified, then no libraries or executables should be rebuilt when running make. Only the file should be copied. I tried several approaches, for example:
cmake_minimum_required(VERSION 3.18)
project(copy-file VERSION 1.0 DESCRIPTION "testing copy file if modified on make all")
set(FILE_PATH "some_dir/file.txt")
add_executable(hello hello.c)
#add_custom_command(
# OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${FILE_PATH}
# COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/${FILE_PATH} ${CMAKE_CURRENT_BINARY_DIR}/${FILE_PATH}
# MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/${FILE_PATH})
add_custom_command(
TARGET hello
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
${CMAKE_CURRENT_SOURCE_DIR}/${FILE_PATH} ${CMAKE_CURRENT_BINARY_DIR}/${FILE_PATH})
#configure_file(${FILE_PATH} ${FILE_PATH} COPYONLY)
Unfortunately, this only copies file.txt (when running make) if hello.c has been modified. If hello.c has not been modified, but file.txt has been modified, nothing happens when I run make (whereas I expected the file to be copied from the source directory to the binary directory)
Here is a link to the source files I used for this minimal example.
Any ideas what I am missing?
Do not use POST_BUILD on a custom target. Specify input and output and let CMake take care of the dependency.
add_custom_command(
OUTPUT
${CMAKE_CURRENT_BINARY_DIR}/${FILE_PATH}
DEPENDS
${CMAKE_CURRENT_SOURCE_DIR}/${FILE_PATH}
COMMAND
${CMAKE_COMMAND} -E copy_if_different
${CMAKE_CURRENT_SOURCE_DIR}/${FILE_PATH}
${CMAKE_CURRENT_BINARY_DIR}/${FILE_PATH}
)
add_custom_target(copy DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${FILE_PATH})
Following the suggestion of #vre and adding copy_if_different instead of copy and then adding add_custom_target() that can be used with the custom command like this seems to work now:
cmake_minimum_required(VERSION 3.18)
project(copy-file VERSION 1.0 DESCRIPTION "testing copy file if modified on make all")
set(FILE_PATH "some_dir/file.txt")
add_executable(hello hello.c)
add_custom_target(copy ALL )
add_custom_command(
TARGET copy
PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${CMAKE_CURRENT_SOURCE_DIR}/${FILE_PATH} ${CMAKE_CURRENT_BINARY_DIR}/${FILE_PATH})

Copy File to Executable Directory Recopy on Modified

I'm trying to copy a file to the same directory where the executable ends up. This means including the configuration type "RelWithDebInfo" directory with Visual Studio. Any CMake variable such as "CMAKE_BINARY_DIR" and similar don't include this folder in the path.
add_executable(${PROJECT_NAME} main.cpp)
add_custom_command(
TARGET ${PROJECT_NAME}
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/copy_this.txt copy_this.txt
COMMENT "Copying..."
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/copy_this.txt
)
The above code works but it doesn't cause the file to be recopied with it is modified. The only other solution I found where it does copy when it is to use add_custom_command(OUTPUT copy_this.txt ...) but it doesn't copy the file into the correct folder.
add_custom_command(
OUTPUT copy_this.txt # this expects the file to be in ${CMAKE_CURRENT_BINARY_DIR}
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/copy_this.txt copy_this.txt
COMMENT "Copying..."
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/copy_this.txt
)
add_custom_target(target_copy_this.txt DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/copy_this.txt)
add_dependencies(${PROJECT_NAME} target_copy_this.txt)
The add_custom_command needs to be updated to specify where the file needs to be copied. In both examples the file is copied into the current working directory because the path is not specified. Here is an example of how to copy to the home directory.
set(newloc ~)
add_custom_command(
OUTPUT ${newloc}/copy_this.txt
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/copy_this.txt ${newloc}/copy_this.txt
COMMENT "Copying to ${newloc}"
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/copy_this.txt
)
add_custom_target(target_copy_this.txt ALL DEPENDS copy_this.txt)
It seems that you want the file to be copied to where one of the targets winds up. In visual studio this could be ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR} or ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${CMAKE_CFG_INTDIR} depending upon what else is going on in the CMakeLists.txt.
Using this example and setting newloc to ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR} you will get the following output:
cmake --build . --config RelWithDebInfo
CustomBuild:
Copying to C:/Users/ZZZ/Projects/test/bld/RelWithDebInfo

Copy file from source directory to binary directory using CMake

I'm trying to create a simple project on CLion. It uses CMake to generate Makefiles to build project (or some sort of it)
All I need to is transfer some non-project file (some sort of resource file) to binary directory each time when I run the my code.
That file contains test data and application open it to read them. I tried several ways to do so:
Via file(COPY ...
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/input.txt
DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/input.txt
Looking good but it work just once and not recopy file after next run.
Via add_custom_command
OUTPUT version
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/input.txt
COMMAND ${CMAKE_COMMAND} -E copy
${CMAKE_CURRENT_SOURCE_DIR}/input.txt
${CMAKE_CURRENT_BINARY_DIR}/input.txt)
TARGET version
add_custom_target(foo)
add_custom_command(
TARGET foo
COMMAND ${CMAKE_COMMAND} copy
${CMAKE_CURRENT_BINARY_DIR}/test/input.txt
${CMAKE_SOURCE_DIR})
But no one of it work.
What am I doing wrong?
You may consider using configure_file with the COPYONLY option:
configure_file(<input> <output> COPYONLY)
Unlike file(COPY ...) it creates a file-level dependency between input and output, that is:
If the input file is modified the build system will re-run CMake to re-configure the file and generate the build system again.
Both option are valid and targeting two different steps of your build:
file(COPY ... copies the file during the configuration step and only in this step. When you rebuild your project without having changed your cmake configuration, this command won't be executed.
add_custom_command is the preferred choice when you want to copy the file around on each build step.
The right version for your task would be:
add_custom_command(
TARGET foo POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
${CMAKE_SOURCE_DIR}/test/input.txt
${CMAKE_CURRENT_BINARY_DIR}/input.txt)
you can choose between PRE_BUILD, PRE_LINK, POST_BUILD
best is you read the documentation of add_custom_command
An example on how to use the first version can be found here: Use CMake add_custom_command to generate source for another target
The first of option you tried doesn't work for two reasons.
First, you forgot to close the parenthesis.
Second, the DESTINATION should be a directory, not a file name. Assuming that you closed the parenthesis, the file would end up in a folder called input.txt.
To make it work, just change it to
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/input.txt
DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
I would suggest TARGET_FILE_DIR if you want the file to be copied to the same folder as your .exe file.
$
Directory of main file (.exe, .so.1.2, .a).
add_custom_command(
TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
${CMAKE_CURRENT_SOURCE_DIR}/input.txt
$<TARGET_FILE_DIR:${PROJECT_NAME}>)
In VS, this cmake script will copy input.txt to the same file as your final exe, no matter it's debug or release.
The suggested configure_file is probably the easiest solution. However, it will not rerun the copy command to if you manually deleted the file from the build directory. To also handle this case, the following works for me:
add_custom_target(copy-test-makefile ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/input.txt)
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/input.txt
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/input.txt
${CMAKE_CURRENT_BINARY_DIR}/input.txt
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/input.txt)
if you want to copy folder from currant directory to binary (build folder) folder
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/yourFolder/ DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/yourFolder/)
then the syntexe is :
file(COPY pathSource DESTINATION pathDistination)
This is what I used to copy some resource files:
the copy-files is an empty target to ignore errors
add_custom_target(copy-files ALL
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_BINARY_DIR}/SOURCEDIRECTORY
${CMAKE_BINARY_DIR}/DESTINATIONDIRECTORY
)
If you want to put the content of example into install folder after build:
code/
src/
example/
CMakeLists.txt
try add the following to your CMakeLists.txt:
install(DIRECTORY example/ DESTINATION example)

CMake Custom Command copy multiple files

I am attempting to copy multiple files using the ${CMAKE_COMMAND} -E copy <from> <to> format, but I was wondering if there was a way to provide a number of files to copy to a specific directory. It seems the cmake copy only allows for one file to be copied at a time. I really don't want to use the copy command repeatedly when I would rather provide a list of files to copy as the first argument.
I'm thinking the easiest solution is to use the platform dependent "cp" command. While this definitely is not good for portability, our system is guaranteed to be built on Linux. A simple, platform independent solution would be better.
Copying multiple files is available from CMake 3.5
cmake -E copy <file>... <destination>
"cmake -E copy" support for multiple files
Command-Line Tool Mode
I did it with a loop
# create a list of files to copy
set( THIRD_PARTY_DLLS
C:/DLLFOLDER/my_dll_1.dll
C:/DLLFOLDER/my_dll_2.dll
)
# do the copying
foreach( file_i ${THIRD_PARTY_DLLS})
add_custom_command(
TARGET ${VIEWER_NAME}
POST_BUILD
COMMAND ${CMAKE_COMMAND}
ARGS -E copy ${file_i} "C:/TargetDirectory"
)
endforeach( file_i )
A relatively simple workaround would be to use ${CMAKE_COMMAND} -E tar to bundle the sources, move the tarball and extract it in the destination directory.
This could be more trouble than it's worth if your sources are scattered across many different directories, since extracting would retain the original directory structure (unlike using cp). If all the files are in one directory however, you could achieve the copy in just 2 add_custom_command calls.
Say your sources to be moved are all in ${CMAKE_SOURCE_DIR}/source_dir, the destination is ${CMAKE_SOURCE_DIR}/destination_dir and your list of filenames (not full paths) are in ${FileList}. You could do:
add_custom_command(
TARGET MyExe POST_BUILD
COMMAND ${CMAKE_COMMAND} -E tar cfj ${CMAKE_BINARY_DIR}/temp.tar ${FileList}
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/source_dir)
add_custom_command(
TARGET MyExe POST_BUILD
COMMAND ${CMAKE_COMMAND} -E rename ${CMAKE_BINARY_DIR}/temp.tar temp.tar
COMMAND ${CMAKE_COMMAND} -E tar xfj temp.tar ${FileList}
COMMAND ${CMAKE_COMMAND} -E remove temp.tar
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/destination_dir)
Since I had more or less exactly the same issue and didn't like the solutions above I eventually came up with this. It does more than just copy files, but I thought I would post the whole thing as it shows the flexibility of the the technique in conjunction with generator expressions that allow different files and directories depending on the build variant. I believe the COMMAND_EXPAND_LISTS is critical to the functionality here.
This function not only copies some files to a new directory but then runs a command on each of them. In this case it uses the microsoft signtool program to add digital signatures to each file.
cmake_minimum_required (VERSION 3.12)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
SET(ALL_3RD_PARTY_DLLS_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/file1.dll" "${CMAKE_CURRENT_SOURCE_DIR}/file2.dll")
SET(ALL_3RD_PARTY_DLLS_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/file3.dll" "${CMAKE_CURRENT_SOURCE_DIR}/file4.dll")
STRING(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}" ";${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Debug" ALL_OUTPUT_3RD_PARTY_DLLS_DEBUG ${ALL_3RD_PARTY_DLLS_DEBUG})
LIST(REMOVE_AT ALL_OUTPUT_3RD_PARTY_DLLS_DEBUG 0)
STRING(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}" ";${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Release" ALL_OUTPUT_3RD_PARTY_DLLS_RELEASE ${ALL_3RD_PARTY_DLLS_RELEASE})
LIST(REMOVE_AT ALL_OUTPUT_3RD_PARTY_DLLS_RELEASE 0)
FILE(TO_NATIVE_PATH "C:\\Program\ Files\ (x86)\\Windows\ Kits\\10\\bin\\10.0.17763.0\\x86\\signtool.exe" SIGNTOOL_COMMAND)
add_custom_target(Copy3rdPartyDLLs ALL
COMMENT "Copying and signing 3rd Party DLLs"
VERBATIM
COMMAND_EXPAND_LISTS
COMMAND ${CMAKE_COMMAND} -E
make_directory "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$<$<CONFIG:Release>:Release>$<$<CONFIG:Debug>:Debug>/"
COMMAND ${CMAKE_COMMAND} -E
copy_if_different
"$<$<CONFIG:Release>:${ALL_3RD_PARTY_DLLS_RELEASE}>"
"$<$<CONFIG:Debug>:${ALL_3RD_PARTY_DLLS_DEBUG}>"
"${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$<$<CONFIG:Release>:Release>$<$<CONFIG:Debug>:Debug>/"
COMMAND ${SIGNTOOL_COMMAND} sign
"$<$<CONFIG:Release>:${ALL_OUTPUT_3RD_PARTY_DLLS_RELEASE}>"
"$<$<CONFIG:Debug>:${ALL_OUTPUT_3RD_PARTY_DLLS_DEBUG}>"
)
I hope this saves someone the day or so it took me to figure this out.

Changing the layout when creating a TGZ archive

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.
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.
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.