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}
)
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!
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
Related
So I'm trying to convert a make-based project to cmake, and having trouble wrapping my head around how it works. I've figured out how to get custom commands to generate a header file, and how to compile source file for the target executable, but I can't seem to link them together -- I can't figure out how to trigger the custom command to generate the header file. Here's a trivial example of what I'm trying to do:
CMakeLists.txt:
add_executable(test test.c)
add_custom_command(OUTPUT foo.h
COMMAND echo "/* test */" > foo.h
)
test.c:
#include "foo.h"
int main() { return 0; }
However, when I run cmake and make, it gives me:
/home/cdodd/test/test.c:1:17: fatal error: foo.h: No such file or directory
compilation terminated.
It can't seem to figure out that it needs to create foo.h first before compiling test.c. With make I'd just add a dependency; how do I do that with cmake?
Add the generated file foo.h to the executable target as a dependency:
add_executable(testexe test.c foo.h)
This will make CMake add a file level dependency of the target testexe to the file foo.h in the generated build system.
Suppose you have a very simple CMakeLists.txt
add_executable(silent T.cpp A.asm)
CMake will happily generate a C++ target for building silent, with T.cpp in it, but will silently drop any and all reference to A.asm, because it doesn't know what to do with the suffix.
Is there any way to get CMake to loudly complain about this source file it doesn't understand (to aid in porting a Makefile to CMake).
Ignoring unknown file extensions is - unfortunately for your case - by design.
If I look at the code of cmGeneratorTarget::ComputeKindedSources() anything unknown ends up to be classified as SourceKindExtra (to be added as such to generated IDE files).
So I tested a little and came up with the following script that evaluates your executable target source files for valid file extensions by overwriting add_executable() itself:
cmake_minimum_required(VERSION 3.3)
project(silent CXX)
file(WRITE T.cpp "int main() { return 0; }")
file(WRITE T.h "")
file(WRITE A.asm "")
function(add_executable _target)
_add_executable(${_target} ${ARGN})
get_property(_langs GLOBAL PROPERTY ENABLED_LANGUAGES)
foreach(_lang IN LISTS _langs)
list(APPEND _ignore "${CMAKE_${_lang}_IGNORE_EXTENSIONS}")
endforeach()
get_target_property(_srcs ${_target} SOURCES)
foreach(_src IN LISTS _srcs)
get_source_file_property(_lang "${_src}" LANGUAGE)
get_filename_component(_ext "${_src}" EXT)
string(SUBSTRING "${_ext}" 1 -1 _ext) # remove leading dot
if (NOT _lang AND NOT _ext IN_LIST _ignore)
message(FATAL_ERROR "Target ${_target}: Unknown source file type '${_src}'")
endif()
endforeach()
endfunction()
add_executable(silent T.cpp T.h A.asm)
Since you wanted a rather loudly complain by CMake I declared it an FATAL_ERROR in this example implementation.
CMake doesn't just drop unknown files in add_executable().
If alongside with
add_executable(silent T.cpp A.asm)
you have
add_custom_command(OUTPUT A.asm COMMAND <...>
DEPENDS <dependees>)
then whenever <dependees> changed CMake will rerun command for create A.asm before compiling the executable.
Note, that automatical headers scanning doesn't provide such functionality: if your executable includes foo.h then executable will be rebuilt only when foo.h itself is changed. Any custom command creating this header will be ignored.
However, you may change behavior of add_executable by redefining it. See #Florian's answer for example of such redefinition.
I'm waiting for the debug version of the library from an external source, they have delivered the release version already.
We use a Find... module to locate the library. This now results in something like:
optimized;libfoo.a;debug;foo-NOTFOUND
The CMakeLists.txt file:
...
add_executable(main main.c)
target_link_libraries(main ${foo})
Initiating the build with:
cmake source/dir -DCMAKE_BUILD_TYPE=Release
But cmake still complains about the debug library missing.
CMake Error: The following variables are used in this project, but they are set to NOTFOUND.
Please set them or make sure they are set and tested correctly in the CMake files:
foo
linked by target "main" in directory source/dir
Is this the expected behaviour? Can I avoid this problem without changing our Find module or force setting the foo variable before each use?
I've given it a try and you can't suppress this error. Looking at the responsible source code cmGlobalGenerator::CheckTargetProperties() this check is only skipped with INTERFACE link libraries (which you obviously don't want since it would not link anything to main).
But you can declare a placeholder IMPORTED library of the name causing the error like:
add_library(foo-NOTFOUND STATIC IMPORTED)
To reproduce your problem and test the fix I've setup the following CMakeLists.txt:
cmake_minimum_required(VERSION 3.3)
project(FooNotFound)
cmake_policy(SET CMP0057 NEW)
set(foo "optimized;libfoo.a;debug;foo-NOTFOUND")
file(WRITE main.c "int main(void) { return 0; }")
if ("foo-NOTFOUND" IN_LIST foo)
add_library(foo-NOTFOUND STATIC IMPORTED)
endif()
add_executable(main main.c)
target_link_libraries(main INTERFACE ${foo})
I'm currently playing around with ncurses. Ncurses is a library I installed, NOT my own file. I already did some stuff but using an IDE is much easier so I decided to use CLion (I'm on Linux so can't use Visual Studio). I got the following CMakeLists.txt:
cmake_minimum_required(VERSION 3.6)
project(ncurses)
set(CMAKE_C_STANDARD "${CMAKE_C_FLAGS} -Wall -Werror -lpdcurses")
set(SOURCE_FILES main.cpp ncurses.h)
add_executable(ncurses ${SOURCE_FILES})
My project is called ncurses I don't know if that'd matter.
I got the following main.cpp
#include <ncurses.h>
int main() {
initscr();
printw("Hello");
refresh();
getch();
endwin();
return 0;
}
However, I get the following errors:
/opt/clion/bin/cmake/bin/cmake --build /home/josh/ClionProjects/ncurses /cmake-build-debug --target all -- -j 4
make[2]: *** No rule to make target 'CMakeFiles/ncurses.dir/build'. Stop.
make[1]: *** [CMakeFiles/Makefile2:68: CMakeFiles/ncurses.dir/all] Error 2
make: *** [Makefile:84: all] Error 2
I don't get what the problem is. I tried -lncurses except of lpdcurses but that doesn't work either. It only gives an error when building but not in the IDEA itself.
in your CMakeLists.txt
just add :
set(CMAKE_CXX_FLAGS "-lncurses")
To tell the compiler to link a library using CMake, you should use the target_link_libraries() function.
Add this in your CMakeLists.txt:
target_link_libraries(${PROJECT_NAME} ncurses)
However, the error code that you have doesn't seem to be caused by linking. Try adding this line: set(CMAKE_CXX_STANDARD 17) before your add_executable(). Replace 17 by whatever C++ version you want. I'm pretty sure that it wont change anything but heh, worth a try. Also, don't forget to reload cmake project and reset the cache.
For me solutions above did not work. However appending the last 4 lines to the code block below worked for me.
(Linux mint 20, Clion 2020.3)
CMakeLists.txt
cmake_minimum_required(VERSION 3.17)
project(<YOUR_PROJECT>)
set(CMAKE_CXX_STANDARD 14)
find_package(Curses REQUIRED)
include_directories(${CURSES_INCLUDE_DIR})
add_executable(<YOUR_PROJECT> main.cpp)
target_link_libraries(<YOUR_PROJECT> ${CURSES_LIBRARIES})
Change <YOUR_PROJECT> to your actual project name.
I am attempting to follow the tutorial here for developing a "Hello, World" LLVM pass - I am using the guidelines linked by that tutorial here for doing so out of the LLVM source directory. However, when I attempt to follow this tutorial, CMake reports a number of errors internal to LLVM itself.
I have the following directory structure:
HelloWorld/
CMakeLists.txt
HelloWorld/
CMakeLists.txt
HelloWorld.cpp
My HelloWorld.cpp, and the two CMakeLists.txt are copy and pasted directly from the tutorials linked to above.
I run CMake HelloWorld and it successfully generates a CMake configuration. However, when I run make. I get numerous errors reported from within the LLVM codebase itself.
[ 50%] Building CXX object CMakeFiles/LLVMPassName.dir/Vectorize.cpp.o
In file included from /Volumes/andromeda/HelloWorld/HelloWorld.cpp:1:
In file included from /usr/local/Cellar/llvm/3.6.2/include/llvm/Pass.h:377:
In file included from /usr/local/Cellar/llvm/3.6.2/include/llvm/PassSupport.h:27:
In file included from /usr/local/Cellar/llvm/3.6.2/include/llvm/PassRegistry.h:20:
In file included from /usr/local/Cellar/llvm/3.6.2/include/llvm-c/Core.h:18:
In file included from /usr/local/Cellar/llvm/3.6.2/include/llvm-c/Support.h:17:
/usr/local/Cellar/llvm/3.6.2/include/llvm/Support/DataTypes.h:57:3: error: "Must #define
__STDC_LIMIT_MACROS before #including Support/DataTypes.h"
# error "Must #define __STDC_LIMIT_MACROS before #including Support/DataTypes.h"
^
/usr/local/Cellar/llvm/3.6.2/include/llvm/Support/DataTypes.h:61:3: error: "Must #define
__STDC_CONSTANT_MACROS before " "#including Support/DataTypes.h"
# error "Must #define __STDC_CONSTANT_MACROS before " \
The list goes on and on and all of them refer to errors in LLVM header files. This is a clean install of LLVM using Homebrew. To get linking to work, I had to set CPLUS_INCLUDE_PATH to the Homebrew include directory for LLVM.
My first thought was that CMake was attempting to use a different compiler (Clang vs. GCC or vice versa), but setting CMAKE_CXX_COMPILER to point to either my clang or g++ installation did not help.
Does anyone have any ideas for what might be the problem here?
After following the link provided by #oak in the comments, I was able to get rid of the first two Support/DataType errors. However, many of the errors still remain.
In file included from /usr/local/Cellar/llvm/3.6.2/include/llvm/Pass.h:377:
In file included from /usr/local/Cellar/llvm/3.6.2/include/llvm/PassSupport.h:27:
In file included from /usr/local/Cellar/llvm/3.6.2/include/llvm/PassRegistry.h:21:
/usr/local/Cellar/llvm/3.6.2/include/llvm/ADT/DenseMap.h:543:63: error: a space is required
between consecutive right angle brackets (use '> >')
typename BucketT = detail::DenseMapPair<KeyT, ValueT>>
^
/usr/local/Cellar/llvm/3.6.2/include/llvm/ADT/DenseMap.h:694:63: error: a space is required
between consecutive right angle brackets (use '> >')
typename BucketT = detail::DenseMapPair<KeyT, ValueT>>
So, after much research, it turns out that there is an inconsistency with how LLVM and CMake support out-of-source builds. The LLVM binaries are built with -fno-rtti, so CMake will complain about missing symbols unless it is also uses -fno-rtti when compiling the LLVM pass.
I fixed all of my troubles (including those solved by the temporary fix proposed by Oak) by adding SET(CMAKE_CXX_FLAGS "-Wall -fno-rtti") to my CMakeLists.txt file in the innermost directory.
This was inspired by this question also on StackOverflow.