How to embed Info.plist in a simple Mac binary file

Greetings! For some time I have been happily using CMake to build Unix and Windows executables for my Carabiner project. This has worked great until macOS Catalina. In order to let people run this command-line tool without complaint in that operating system, I need to be able to code-sign and notarize it, using the hardened runtime.

The problem is, I can’t figure out how to get it into a state where code-signing will work with CMake. I need to be able to compile a binary representation of an Info.plist file into the single-file executable. I see CMake examples of how to build a macOS application package, and a macOS framework, both of which are folder structures with places to put the Info.plist as a standalone file. And I have seen examples of how to compile the Info.plist into a binary segment in the executable file using Xcode. But I can’t for the life of me figure out how to do that with just CMake.

The closest I have come was finding that if I add the following lines before my _add_executable, the compiler does seem to pass the right linker flags when building my executable originally, but it then seems to go on to do other linker stages which blow away the section.

if (APPLE)
    add_link_options("-Wl–sectcreate,__TEXT,__info_plist,${Carabiner_SOURCE_DIR}/CMake/Info.plist.in")
endif()

(Or maybe it never got created in the first place, I was just assuming it did because I could see the linker flags in the compile command when I ran the build with -v, and they did not result in compiler errors.)

Can anyone point me at the right way of doing this?

We’re successfully using CMake to embed an Info.plist into a CLI using the following syntax:

target_link_options(${target} PRIVATE LINKER:-sectcreate,__TEXT,__info_plist,${CMAKE_CURRENT_SOURCE_DIR}/Info.plist)

(If I understand correctly, it’s preferred to use target_link_options to specify linker options per-target rather than globally with add_link_options. And the LINKER: keyword is a compiler-independent way to pass options to the linker.)

but it then seems to go on to do other linker stages which blow away the section

If the above doesn’t work, could you either push a branch to https://github.com/Deep-Symmetry/carabiner that demonstrates the problem, or share the verbose build output? (I’m not clear on what “other linker stages” you’re referring to.)

1 Like

Thanks so much, Steve! This got me unstuck. For some reason when I was trying to use add_link_options I could not use the LINKER: keyword, it was translating that into -Xlinker arguments which were being rejected by c++. Looking now at the working version, I see that I was also missing a “,” after -Wl.

I apologize for my imprecise language, I haven’t worked in any real way with C++ since the early 1990s, and precious little with CMake, just enough to get this build working a year or so ago, and I am back at it now. So I am unclear of all the steps that are happening after building that target; looking more closely, it was actually running some makefiles for the dependencies. So I greatly appreciate your help.

1 Like

All right, I think I have taken this as far as I can go. Thanks again for your help!

The binary is now being successfully code signed and notarized by my GitHub Actions workflow. And you can run it in Catalina after downloading it from GitHub with no complaint, as long as you do so from a terminal session you started yourself.

Sadly, if you try to run it by double-clicking it in the Finder, which would work fine if it had not been quarantined (this saves you the trouble of opening Terminal.app and typing the path to the program), both Catalina and Mojave incorrectly report that the developer cannot be identified. However, if you look deeper at why that is happening, the problem is different than what the Finder reports:

> spctl -a -t exec -vv /Volumes/Carabiner/Carabiner
Volumes/Carabiner/Carabiner: rejected (the code is valid but does not seem to be an app)
origin=Developer ID Application: Deep Symmetry, LLC (9M6LKU948Y)

But compare:

> spctl -a -t install -vv /Volumes/Carabiner/Carabiner
/Volumes/Carabiner/Carabiner: accepted
source=Notarized Developer ID
origin=Developer ID Application: Deep Symmetry, LLC (9M6LKU948Y)

This really looks like a bug in the OS, forgetting that individual files can be executables, not just application bundles. Has anyone filed a Radar with Apple over this, or found a workaround for it? I suspect I am just going to have to educate my users to launch Terminal manually, and run the second spctl command if they want to reassure themselves the download is ok.