7

I am working on simply creating a hello world executable on macOS using Objective-C and CMake. I understand that CMake does not natively support building Objective-C. You must first add the

set(CMAKE_C_FLAGS "-x objective-c")

or

set(CMAKE_CXX_FLAGS "-x objective-c++")

for the Makefile to even be created. The part where I'm getting stuck is how to appropriately link the existing frameworks. I've followed both the instructions in this post as well as the tutorial it's based off of: Can't link MacOS frameworks with CMake I've also tried linking the libraries with:

set(CMAKE_EXE_LINKER_FLAGS "-framework fm1-framework fm2…")

I can now get a successful CMake generated Xcode project, but when I open the project, none of the frameworks I specified in the CMakeLists.txt file are in the 'resources' folder. Using this Xcode project, I can get the project to compile, but when I run the executable I get the error:

"2014-01-06 18:02:35.859 HelloWorld[62641:303] No Info.plist file in application bundle or no NSPrincipalClass in the Info.plist file, exiting
Program ended with exit code: 1"

I also tried manually running the makefile made by CMake in the terminal and got many many warnings (during linking) all saying:

CMakeFiles/HelloWorld.dir/AppDelegate.m.o:3:2252: warning: null character
ignored [-Wnull-character]
...

CMakeFiles/HelloWorld.dir/AppDelegate.m.o:3:2263: warning: null character
ignored [-Wnull-character]
...

And 7 errors (during linking):

CMakeFiles/HelloWorld.dir/AppDelegate.m.o:1:1: error: expected unqualified-id <U+0007>

CMakeFiles/HelloWorld.dir/AppDelegate.m.o:3:90: error: expected unqualified-id ...H<89>uH<89>U]fffff.<U+000F><U+001F><84>

CMakeFiles/HelloWorld.dir/AppDelegate.m.o:3:121: error: extraneous closing brace ('}') ...}H<89>uH<8B>uH<8B>=

CMakeFiles/HelloWorld.dir/AppDelegate.m.o:3:122: error: expected unqualified-id ...H<89>uH<8B>uH<8B>=

CMakeFiles/HelloWorld.dir/AppDelegate.m.o:3:169: error: extraneous closing brace ('}') ...}H<89>uH<89>UH<8B>uH<8B>=

CMakeFiles/HelloWorld.dir/AppDelegate.m.o:3:170: error: expected unqualified-id ...H<89>uH<89>UH<8B>uH<8B>=

CMakeFiles/HelloWorld.dir/AppDelegate.m.o:3:2246: error: expected unqualified-id ...16@0:8

Any ideas on what I'm during wrong? I've spent the better portion of the day looking for answers and the post I referenced had the most useful information, but it still didn't fix my problem. Is it because the original program is also written in Xcode? And somehow that's messing up the CMake?

Here's the code for my HelloWorld app:

#import <Cocoa/Cocoa.h>

int main(int argc, const char * argv[])
{
    return NSApplicationMain(argc, argv);
}

And the code for the CMakeLists.txt file:

cmake_minimum_required(VERSION 2.8)
project(HelloWorld)

set(src_files
   AppDelegate.h
   AppDelegate.m
   main.m
)

set(CMAKE_C_FLAGS "-x objective-c")
set(CMAKE_CXX_FLAGS "-x objective-c++")
set(CMAKE_EXE_LINKER_FLAGS "-framework Cocoa -framework AppKit -framework CoreData -     framework Foundation")

add_executable(HelloWorld 
   ${src_files}
)
Cœur
  • 37,241
  • 25
  • 195
  • 267
bhawley
  • 395
  • 1
  • 3
  • 13
  • Working fine for me without modifying `CMAKE_CXX_FLAGS`. What if remove `-x objective-c++`? –  Jan 07 '14 at 10:58
  • I still get the same result if I remove the -x objective-c++ – bhawley Jan 08 '14 at 17:54
  • But I do think I've made some headway, it appears the issue lies with linking the Cocoa framework. If I open just a regular objective c Xcode project as a command line executable instead of a Cocoa project, I can get CMake to work just fine. Any ideas if this might be specific to version CMake 2.8-11 or Xcode 5.0.2? – bhawley Jan 08 '14 at 17:56
  • `If I open just a regular objective c Xcode project as a command line executable instead of a Cocoa project, I can get CMake to work just fine` I don't understand what you mean... –  Jan 08 '14 at 20:08
  • 1
    Take a look this project: https://github.com/forexample/cocoa-app –  Jan 08 '14 at 20:08
  • @ruslo - I substituted the CMakeLists.txt file you pointed me to and received the following error message when running CMake: CMake Error in CMakeLists.txt: Cannot find source file: MainMenu.xib Where should I tell CMake to look for it? Or should it be able to find it on its own? – bhawley Jan 08 '14 at 20:55
  • The code from main.m shown in the original post is from an out of the box project created in Xcode. If you select File > New > Project and then select OS X > Application and select a 'Cocoa' project, you will see the same code generated. This Xcode project (lets call it HelloWorld1) is what I want to use CMake on. And then use CMake to generate a new Xcode project (HelloWorld2) for other developers to create from an automatic build system. If you follow the same steps I outlined above, but create a command line executable instead of a Cocoa project, CMake generates HelloWorld2 correctly. – bhawley Jan 08 '14 at 21:25
  • `I substituted the CMakeLists.txt file you pointed me to and received the following error message` Have you read `Usage` section of `README.md`? My point is - project compiled fine from scratch, you can see it on `clean` environment on travis: https://travis-ci.org/forexample/cocoa-app/builds/16612121 –  Jan 08 '14 at 21:40
  • @ruslo Firstly, I want to say thanks for the help. Secondly, you're right. I can see the clean build. And I cloned the repo and tried it myself; I also got a working build from the CMake. So my question becomes, why does this work and my original CMake does not? In the repo it doesn't look like you're working from Xcode originally. Could that be part of the problem? – bhawley Jan 08 '14 at 22:33
  • `In the repo it doesn't look like you're working from Xcode originally` what do you mean? CMake generates Xcode project using CMakeLists.txt as a source. Then you can open **Xcode** project in Xcode IDE and work. –  Jan 09 '14 at 11:15
  • The original source code I'm using is from Xcode. I need to have CMake work on this code so my teammates can generate an Xcode project from the source on a mercurial repo. – bhawley Jan 09 '14 at 17:54

3 Answers3

4

Indirectly thanks to ruslo and the source code located here: https://github.com/forexample/cocoa-app I figured out the problem. It appears for CMake to work with a plist generated by Xcode, one MUST use the command line instructions specified in the README.md in the above mentioned repo. The GUI implementation of CMake will NOT work because the GUI has trouble interpreting the plist file. For clarity, I'll list them here:

cd to the directory containing the CMakeLists.txt file.

cmake -H. -B_OutputDirectory -GXcode

cmake --build _OutputDirectory/

open _OutputDirectory/HelloWorld.xcodeproj/

The other issue I encountered was that my plist and MainWindow.xib weren't included in my original CMake file. After adding the MainWindow.xib and the HelloWorld-info.plist necessary code, I was able to generate a usable Xcode project. Here's the code for reference:

cmake_minimum_required(VERSION 2.8)
project(HelloWorld)
set(NAME HelloWorld) 
set(CMAKE_C_FLAGS "-x objective-c")

set(HEADER_FILES 
    ./uhdplayerengine/HelloWorld/HelloWorld/AppDelegate.h
)
set(SOURCE_FILES 
    ./uhdplayerengine/HelloWorld/HelloWorld/AppDelegate.m
    ./uhdplayerengine/HelloWorld/HelloWorld/main.m
)
set(XIB_FILE
    ./uhdplayerengine/HelloWorld/HelloWorld/Base.lproj/MainMenu.xib
)

add_executable(
    ${NAME}
    MACOSX_BUNDLE
    ${HEADER_FILES}
    ${SOURCE_FILES}
    ${XIB_FILE}
)

set_source_files_properties(
    ${XIB_FILE}
    PROPERTIES
    MACOSX_PACKAGE_LOCATION
    Resources
)

set_target_properties(
    ${NAME}
    PROPERTIES
    MACOSX_BUNDLE_INFO_PLIST
    ./uhdplayerengine/HelloWorld/HelloWorld/HelloWorld-Info.plist
)

target_link_libraries(${NAME}
    "-framework Cocoa"
    "-framework AppKit"
    "-framework CoreData"
    "-framework Foundation"
)

I hope this saves someone else a headache! Thanks ruslo!

bhawley
  • 395
  • 1
  • 3
  • 13
  • 1
    `NOT work because the GUI has trouble interpreting the plist file` what trouble? I've tried to create project by `cmake-gui` and have some location error messages. I'll fix it by using absolute path. Pull changes to your repo and test it. –  Jan 12 '14 at 11:46
  • 2
    As I mentioned earlier it is not necessary to add this flag `set(CMAKE_C_FLAGS "-x objective-c")`. BTW this variable may not be empty, so if you want **add** some flags you need to use command: `set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -x objective-c")` –  Jan 12 '14 at 11:54
  • Thanks for the clarification on the set(CMAKE_C_FLAGS). And yes, the location errors were the ones I was talking about. It does look like the changes you made to the path fixed it! Thanks again for the help. – bhawley Jan 13 '14 at 17:52
4

Actually cmake support both objc and objc++ starting from 3.16 (check release notes)

You can use CMAKE_OBJCXX_FLAGS and CMAKE_OBJC_FLAGS to specify specific flags.

Also don't forget to call enable_language(OBJC) or enable_language(OBJCXX)

P.S. Here is related PR https://gitlab.kitware.com/cmake/cmake/-/merge_requests/3811

CAMOBAP
  • 5,523
  • 8
  • 58
  • 93
1

Minimal CMake project for Objective-C

Since CMake 3.16+, here is a minimal CMakeLists.txt for an Objective-C project:

cmake_minimum_required(VERSION 3.16)
project(HelloWorld)

enable_language(OBJCXX)

add_executable(HelloWorld main.m)

target_link_libraries(HelloWorld
    "-framework Foundation"
)

main.m:

#import <Foundation/Foundation.h>

int main() {
    @autoreleasepool {
        NSLog(@"Hello, World!");
    }
    return 0;
}

Compile, build and run with:

mkdir build && cd build
cmake ..
cmake --build .
./HelloWorld

Output:

2023-03-28 19:37:46.970 HelloWorld[10606:200462] Hello, World!

Mixing C++ and Objective-C++

CMakeLists.txt:

cmake_minimum_required(VERSION 3.16)
project(HelloWorld)

set(CMAKE_CXX_STANDARD 17)
enable_language(OBJCXX)

add_executable(HelloWorld hello.mm main.cpp)

target_link_libraries(HelloWorld
    "-framework Foundation"
)

main.cpp:

#include "hello.h"

int main() {
    hello();
    return 0;
}

hello.h:

#ifndef HELLO_H
#define HELLO_H

void hello();

#endif

hello.mm:

#import "hello.h"
#import <Foundation/Foundation.h>

void hello() {
    @autoreleasepool {
        NSLog(@"Hello, World!");
    }
}
Boris Dalstein
  • 7,015
  • 4
  • 30
  • 59