Building a MAC .app with more than one executable

I would like to create a macOS .app containing a gui application and its command line equivalent.

This is exactly the sort of thing that the CMake.app appears to do.

How can I do this

I initially thought to build the command line version specifying it shouldn’t create a bundle and save the output directory name, then when building the gui version I thought I’d just copy the binary for the CL version into the MacOS directory alongside the gui version:

if(APPLE)
    message("ls -l ${DSSCL_BINARY_DIR}")
    message("ls -l ${CMAKE_CURRENT_BINARY_DIR}/DeepSkyStacker.app/Contents/MacOS")
    message ("cp ${DSSCL_BINARY_DIR}/DeepSkyStackerCL ${CMAKE_CURRENT_BINARY_DIR}/DeepSkyStacker.app/Contents/MacOS/")
    message ("$ENV{HOME}/.unlock_keychain")
    add_custom_command(TARGET DeepSkyStacker
        POST_BUILD
        #
        # Now we've built the DSS .app bundle, copy the DSSCL binary into the bundle
        #
        COMMAND "ls -l ${DSSCL_BINARY_DIR}"
        COMMAND "ls -l ${CMAKE_CURRENT_BINARY_DIR}/DeepSkyStacker.app/Contents/MacOS"
        COMMAND "cp ${DSSCL_BINARY_DIR}/DeepSkyStackerCL ${CMAKE_CURRENT_BINARY_DIR}/DeepSkyStacker.app/Contents/MacOS/"
        COMMAND "$ENV{HOME}/.unlock_keychain"
   )

Configure output said:

1> [CMake] ls -l /Users/amonra/.vs/DSS/out/build/DeepSkyStackerCL
1> [CMake] ls -l /Users/amonra/.vs/DSS/out/build/DeepSkyStacker/DeepSkyStacker.app/Contents/MacOS
1> [CMake] cp /Users/amonra/.vs/DSS/out/build/DeepSkyStackerCL/DeepSkyStackerCL /Users/amonra/.vs/DSS/out/build/DeepSkyStacker/DeepSkyStacker.app/Contents/MacOS/
1> [CMake] /Users/amonra/.unlock_keychain

Build said:

/bin/sh: ls -l /Users/amonra/.vs/DSS/out/build/DeepSkyStackerCL: No such file or directory

But copy paste that to terminal on MAC says:

amonra@Saturn ~ % ls -l /Users/amonra/.vs/DSS/out/build/DeepSkyStackerCL
total 36144
-rw-r--r--@ 1 amonra  staff      1501 28 Apr 09:12 cmake_install.cmake
drwxr-xr-x@ 4 amonra  staff       128 28 Apr 09:12 CMakeFiles
-rwxr-xr-x@ 1 amonra  staff  18435112 28 Apr 09:13 DeepSkyStackerCL
 ...etc

Questions:

  1. Is this the correct approach?
  2. If yes, what is going wrong above?
  3. If not, what should I be doing please?

Thanks
David

Don’t put quotes around the whole command. CMake would treat the whole quoted string as the executable to run, which you can see in your output where the error message lists the whole command for “No such file…”.

You’re also hard-coding locations for the built binaries (command line and GUI app), and at least one of those will be wrong because Xcode is a multi-config generator, which you haven’t accounted for. Instead of hard-codeing locations of executables, use generator expressions to let CMake provide them for you. For example (omitting all the printing and just focusing on the copy command):

    add_custom_command(TARGET DeepSkyStacker
        POST_BUILD
        COMMAND cp $<TARGET_FILE:DeepSkyStackerCL> $<TARGET_BUNDLE_CONTENT_DIR:DeepSkyStacker>/MacOS/
   )

EDIT: Typo fixed in the above example.

Wow! Thank you lots, but there seems to be a problem:

1> [CMake]     COMMAND ${CMAKE_COMMAND} -E copy ${<TARGET_FILE:DeepSkyStackerCL> $<TARGET_BUNDLE_CONTENT_DIR:DeepSkyStacker>/MacOS/
1> [CMake] 
1> [CMake]   Invalid character ('<') in a variable name: ''

The { is a typo, remove it. The generator expression should be just $<TARGET_FILE:DeepSkyStackerCL>.

:slight_smile: Thanks

Is there any way to express the dependency here - add_dependencies complains about a non-existent target. Or does the use of $<TARGET_FILE:DeepSkyStackerCL> achieve that?

I want to tell the build the DeepSkyStackerCL must have been built …

Thanks
David

It adds the dependecy automatically. Quoting the relevant docs:

$<TARGET_FILE:tgt>

Full path to the tgt binary file.

Note that tgt is not added as a dependency of the target this expression is evaluated on, unless the expression is being used in add_custom_command() or add_custom_target().

Since you’re using it as part of an add_custom_command(), this tells us (after untangling the double negative) that a dependency on the target is added.

Excellent, thanks for the explanation - exactly what I’d hoped for!!!

Many thanks again
David