I use CMake for my builds not the XCode GUI.
I want to be able to build a single .app that contains both the GUI version and the command line version. I have seen products that ship both as part of the same .app (e.g. CMake) so this is clearly entirely possible, the 64k question is HOW?
Right now I am building them as separate apps - lets call them DSS and DSSCL (*).
The code base for each is in its own directory - each with its own CMakelists.txt file.
My initial thoughts are to change the build for DSSCL so it doesn't create a bundle and then simply copy the DSSCL command and related .qm files into DSS.app/.../MacOS.
However that's likely enough totally wrong, so how should I handle this please.
As much detail as possible please, I am very new to macOS development -please don't assume knowledge of stuff that's second nature to you
Many thanks David
(*) Strictly they are DeepSkyStacker and DeepSkyStackerCL
I've placed the CL executable in the MacOS directory I expect that users will place a symlink to it in /usr/local/bin/
OK.
Unlike Etresoft, I’m kinda neutral on that approach. At a technical level it works just fine, and at a UI level I’ve certainly used products that work that we. We even have explicit support for that model in sandboxed apps [1].
Speaking of the sandbox, you didn’t answer my App Sandbox question. I’m gonna presume that your app is not sandboxed and thus you have no plans to distribute it via the Mac App Store. If that’s not right, lemme know because it changes the following answer.
If you want to distribute an app with a command-line tool that users can run from Terminal, this structure is fine:
MyApp.app/
Contents/
Info.plist
MacOS/
MyApp -- the app's main executable
MyTool
Resources/
…
Frameworks/
…
The only tricky thing about this approach is that, when running in the tool, +[NSBundle mainBundle]
won’t return the app’s bundle. Rather, it’ll point to the tool itself. That’s because your tool is not the main executable of the app’s bundled, as recorded in the Info.plist
.
There are two standard ways to deal with that:
-
Put all the interesting code and its associated resources in framework in
Contents/Frameworks
. There’s a standard mechanism for framework code to access framework resources. -
Have the tool use
+[NSBundle mainBundle]
to find its own path, then walk up the file system hierarchy and create a bundle for the app by callingbundleWithURL:
.
If you do use a framework, use an rpath-relative install name, as explained in Dynamic Library Standard Setup for Apps.
Finally, when it comes to code signing, keep in mind that the tool, any frameworks, and the app itself are all separate code items. Creating distribution-signed code for macOS discusses the signing process for things like this in detail. Part of that process is working out the order in which to sign items, whether an item is bundled code or not, and whether it’s a main executable or not. Here’s a quick summary of that for the structure I’ve outlined above:
# Item Bundled? Main Executable?
- ---- -------- ----------------
1 framework yes no
2 tool no yes
3 app yes yes
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
[1] The privileged file operation feature in NSWorkspace
.