2

I am trying to build a debian package that contains a single bash script. The script is generated by:

go.sh:

#!/bin/bash

cat >main <<EOF
#!/bin/bash
echo $@
EOF

chmod +x main

and my CMakeLists.txt:

cmake_minimum_required(VERSION 3.5)

add_custom_command(OUTPUT main ${CMAKE_CURRENT_SOURCE_DIR}/go.sh ${PROJECT_NAME})

add_custom_target(main ALL)

install(
    TARGETS main
    DESTINATION /usr/local/lib/
)

set(CPACK_GENERATOR "DEB")
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Me")

include(CPack)

This just nets me a cryptic error message:

CMake Error at CMakeLists.txt:7 (install):
  install TARGETS given target "main" which is not an executable, library, or
  module.

Does anyone know what this error message means, and what I should do about it?

EDIT - I am so lost.

I've renamed the script that was generated by go.sh to program so that it doesn't conflict with the name of the target which is supposed to produce the file:

#!/bin/bash

MAIN=program

cat >$MAIN <<EOF
#!/bin/bash
echo $@
EOF

chmod 'x $MAIN

I've also changed my CMakeLists.txt file:

cmake_minimum_required(VERSION 3.5)

add_custom_target(
    main
    DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/program
)

add_custom_command(
    OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/program
    COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/go.sh ${PROJECT_NAME}
)

install(FILES ${CMAKE_CURRENT_BINARY_DIR}/program DESTINATION /usr/local/lib/)

set(CPACK_GENERATOR "DEB")
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Me")

include(CPack)

However, my build still doesn't work. In this case, my main target doesn't get built. If I run:

rm -rf ./* && cmake .. && make package

I get an error about file INSTALL cannot find program

I think I am fundamentally misunderstanding the difference between a file and a target. I cannot imagine that, using cmake, one is expected to name every generated file and then give every generated file a distinct target name to sidestep naming conflicts. That just doesn't make any sense to me.

spierepf
  • 2,774
  • 2
  • 30
  • 52
  • Having target name and output filename the same does not work for some generators: for every target CMake creates directory in the build tree with the same name, so it will conflict with the file produced. – Tsyvarev May 26 '17 at 21:13
  • I don'I understand. What is a target name if not the name of the file that is the result of the target? – spierepf May 26 '17 at 21:54
  • In CMake target name is just an abstract identificator for some purpose. It is not the same as file it produces. Moreover, single target may produce several files ... or none of them. BTW, your `add_custom_command` does not work: your forget to add *DEPENDS* option for the custom target, [as here](https://stackoverflow.com/a/2940153/3440745). – Tsyvarev May 26 '17 at 22:07
  • According to the CMake documentation, DEPENDS is used to "Specify files on which the command depends." In my case, the command doesn't depend on any files, so I am not sure what I would put in that clause. – spierepf May 27 '17 at 01:18
  • You need *DEPENDS* for **add_custom_target** corresponded to *OUTPUT* of `add_custom_command`. – Tsyvarev May 27 '17 at 08:11

3 Answers3

3

@tsyvarev's answer pointed you in the right direction and your edited question is almost correct. You left out the ALL option to add_custom_target(), which is why your main target didn't get built by default. Correcting your CMakeLists.txt file to account for this, the following should do what you want:

cmake_minimum_required(VERSION 3.5)

add_custom_target(
    main ALL
    DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/program
)

add_custom_command(
    OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/program
    COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/go.sh ${PROJECT_NAME}
)

install(FILES ${CMAKE_CURRENT_BINARY_DIR}/program DESTINATION /usr/local/lib/)

set(CPACK_GENERATOR "DEB")
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Me")

include(CPack)

This is a pretty standard pattern with CMake, having a custom command create a file and a custom target depend on it so that it can be triggered as needed.

Craig Scott
  • 9,238
  • 5
  • 56
  • 85
  • According to the CMake docs, `ALL` indicates that "this target should be added to the default build target so that it will be run every time". What then is the distinction of the additional `add_custom_command`. – spierepf May 28 '17 at 13:13
  • By making the custom _target_ part of ALL, it will always be evaluated. It depends on the custom _output_, so that output will be rebuilt _if it is out of date_. So if the custom output already exists, it won't be regenerated. – Craig Scott May 28 '17 at 20:51
  • Another question: What is the purpose of the `main` in the `add_custom_target` clause? I understand that it is the target name, but nothing refers to the target as `main`, only as `${CMAKE_CURRENT_BINARY_DIR}/program`. – spierepf May 29 '17 at 12:52
  • The ALL target implicitly refers to it. If you are using `Unix Makefiles` as your generator, you would be able to load the `Makefile` file and see the dependency relationship in there. Even without that, it's okay to create custom targets that have nothing referring to them. This can be useful, for example, to create on-demand targets that developers can ask to be built only when needed, such as running some kind of cleanup task or special tool. – Craig Scott May 29 '17 at 21:01
2

Command flow install(TARGETS) is only for targets created with add_executable() or add_library().

For files built with custom targets use install(FILES):

install(FILES ${CMAKE_CURRENT_BINARY_DIR}/main DESTINATION /usr/local/lib/)
Tsyvarev
  • 60,011
  • 17
  • 110
  • 153
0

Fixed it. I had to fold the add_custom_command into the add_custom_target:

cmake_minimum_required(VERSION 3.5)

add_custom_target(
    main ALL
    COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/go.sh ${PROJECT_NAME}
)

install(FILES ${CMAKE_CURRENT_BINARY_DIR}/main DESTINATION /usr/local/bin/)

set(CPACK_GENERATOR "DEB")
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Me")

include(CPack)
spierepf
  • 2,774
  • 2
  • 30
  • 52
  • This will cause the `main` script to be regenerated in every build. Probably not such a big issue for a tiny script like this, but not normally what you'd want. – Craig Scott May 27 '17 at 22:24