2

I'm new to CMake and having trouble getting my simple MacOS app (just the learn OpenGL tutorial at this point) to link against the MacOS frameworks required by GLFW. All my code is in main.cpp and I'm using the generated glad source files. My source tree looks like this:

.
├── CMakeLists.txt
├── glad
│  └── glad.h
├── glad.c
├── KHR
│  └── khrplatform.h
└── main.cpp

Here's my CMakeLists.txt:

cmake_minimum_required(VERSION 3.5)

project(simple_repro)

set(CMAKE_CXX_STANDARD 17)

find_package(PkgConfig REQUIRED)
pkg_search_module(GLFW REQUIRED glfw3)

add_executable(app1 main.cpp glad.c)
target_include_directories(app1 PRIVATE
  ${CMAKE_SOURCE_DIR}
  ${GLFW_INCLUDE_DIRS}
)

target_link_libraries(app1 ${GLFW_STATIC_LDFLAGS})

When I build, I get this error:

/Library/Developer/CommandLineTools/usr/bin/c++  -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX10.15.sdk -Wl,-search_paths_first -Wl,-headerpad_max_install_names CMakeFiles/app1.dir/main.cpp.o CMakeFiles/app1.dir/glad.c.o -o app1  -L/usr/local/lib -lglfw3 -framework -lCocoa -framework -lIOKit -framework -lCoreFoundation -lCocoa -lIOKit -lCoreFoundation
ld: framework not found -lCocoa
clang: error: linker command failed with exit code 1 (use -v to see invocation)

At the surface, the reason is pretty obvious: in the linker command, the frameworks parts of ${GLFW_STATIC_LDFLAGS} are being mangled. The names of the framworks are pre-pended with -l and they're repeated. For reference, after the pkg_search_module call, GLFW_STATIC_LDFLAGS is:

-L/usr/local/lib;-lglfw3;-framework;Cocoa;-framework;IOKit;-framework;CoreFoundation

Please note I have no control over the format of this; it gets set by pkg_search_module.

I think if the frameworks part got expanded as:

-framework Cocoa -framework IOKit -framework CoreFoundation

I'd be in business. But instead, it's coming out as

-framework -lCocoa -framework -lIOKit -framework -lCoreFoundation -lCocoa -lIOKit -lCoreFoundation

So the heart of my question is: what do I need to do to get the right linker command to be generated from the output of pkg_search_module? I've googled everything I could think of and tried a ton of things, to no avail. Some highlights of my exploration:

  • Based on this answer, I changed my pkg_search_module call to specify IMPORTED_TARGET and then link to that target:
pkg_search_module(GLFW REQUIRED IMPORTED_TARGET glfw3)
...
target_link_libraries(app1 PUBLIC PkgConfig::GLFW)

This results in basically no references to the MacOS frameworks in the linker command and (as I'd expect) a bunch of core platform undefined symbols:

/Library/Developer/CommandLineTools/usr/bin/c++  -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX10.15.sdk -Wl,-search_paths_first -Wl,-headerpad_max_install_names CMakeFiles/app1.dir/main.cpp.o CMakeFiles/app1.dir/glad.c.o -o app1  /usr/local/lib/libglfw3.a
Undefined symbols for architecture x86_64:
  "_CFArrayAppendValue", referenced from:
      __glfwInitJoysticksNS in libglfw3.a(cocoa_joystick.m.o)
      _matchCallback in libglfw3.a(cocoa_joystick.m.o)
  "_CFArrayCreateMutable", referenced from:
... and many pages more

  • I tried explicitly setting the link flags via set_property(TARGET app1 PROPERTY LINK_FLAGS ${GLFW_STATIC_LDFLAGS}) This just interprets the whole semi-colon separated list in GLFW_STATIC_LDFLAGS as a single string and giving a linker warning:
/Library/Developer/CommandLineTools/usr/bin/c++  -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX10.15.sdk -Wl,-search_paths_first -Wl,-headerpad_max_install_names -L/usr/local/lib;-lglfw3;-framework;Cocoa;-framework;IOKit;-framework;CoreFoundation CMakeFiles/app1.dir/main.cpp.o CMakeFiles/app1.dir/glad.c.o -o app1  /usr/local/lib/libglfw3.a
ld: warning: directory not found for option '-L/usr/local/lib;-lglfw3;-framework;Cocoa;-framework;IOKit;-framework;CoreFoundation'
  • I know I can make this work by doing explicity find_library() calls on Cocoa, IOKit, and CoreFoundation and then linking against the found library, but I do not want to hardcode into my CMakeLists.txt the knowledge that GLFW requires these frameworks, because that's platform specific.

Many thanks in advance for any help in figuring out what incantation will generate the right linker command from the pkg_search_module output.

EDIT: in response to Tsyvarev's comment below, I changed the CMakeLists.txt to read:

cmake_minimum_required(VERSION 3.5)

project(simple_repro)

set(CMAKE_CXX_STANDARD 17)

find_package(PkgConfig REQUIRED)
pkg_check_modules(GLFW REQUIRED glfw3)

add_executable(app1 main.cpp glad.c)
target_include_directories(app1 PRIVATE
  ${CMAKE_SOURCE_DIR}
  ${GLFW_INCLUDE_DIRS}
)

target_link_libraries(app1 ${GLFW_LIBRARIES})

message(STATUS "GLFW_LIBRARIES is ${GLFW_LIBRARIES}")

target_compile_options(app1 PUBLIC ${GLFW_CFLAGS_OTHER})

This outputs -- GLFW_LIBRARIES is glfw3 and generates this linker command, which doesn't include the full path to glfw3 nor does it include the system frameworks needed:

/usr/local/Cellar/cmake/3.21.2/bin/cmake -E cmake_link_script CMakeFiles/app1.dir/link.txt --verbose=1
/Library/Developer/CommandLineTools/usr/bin/c++  -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX10.15.sdk -Wl,-search_paths_first -Wl,-headerpad_max_install_names CMakeFiles/app1.dir/main.cpp.o CMakeFiles/app1.dir/glad.c.o -o app1  -lglfw3

This is the full list of GLFW_* variables, after the pkg_search_module call, in case it helps:

-- GLFW_CFLAGS=-I/usr/local/include
-- GLFW_CFLAGS_I=
-- GLFW_CFLAGS_OTHER=
-- GLFW_FOUND=1
-- GLFW_INCLUDEDIR=/usr/local/include
-- GLFW_INCLUDE_DIRS=/usr/local/include
-- GLFW_LDFLAGS=-L/usr/local/lib;-lglfw3
-- GLFW_LDFLAGS_OTHER=
-- GLFW_LIBDIR=/usr/local/lib
-- GLFW_LIBRARIES=glfw3
-- GLFW_LIBRARY_DIRS=/usr/local/lib
-- GLFW_LIBS=
-- GLFW_LIBS_L=
-- GLFW_LIBS_OTHER=
-- GLFW_LIBS_PATHS=
-- GLFW_LINK_LIBRARIES=/usr/local/lib/libglfw3.a
-- GLFW_MODULE_NAME=glfw3
-- GLFW_PREFIX=/usr/local
-- GLFW_STATIC_CFLAGS=-I/usr/local/include
-- GLFW_STATIC_CFLAGS_I=
-- GLFW_STATIC_CFLAGS_OTHER=
-- GLFW_STATIC_INCLUDE_DIRS=/usr/local/include
-- GLFW_STATIC_LDFLAGS=-L/usr/local/lib;-lglfw3;-framework;Cocoa;-framework;IOKit;-framework;CoreFoundation
-- GLFW_STATIC_LDFLAGS_OTHER=-framework;Cocoa;-framework;IOKit;-framework;CoreFoundation
-- GLFW_STATIC_LIBDIR=
-- GLFW_STATIC_LIBRARIES=glfw3
-- GLFW_STATIC_LIBRARY_DIRS=/usr/local/lib
-- GLFW_STATIC_LIBS=
-- GLFW_STATIC_LIBS_L=
-- GLFW_STATIC_LIBS_OTHER=
-- GLFW_STATIC_LIBS_PATHS=
-- GLFW_VERSION=3.3.4
-- GLFW_glfw3_INCLUDEDIR=
-- GLFW_glfw3_LIBDIR=
-- GLFW_glfw3_PREFIX=
-- GLFW_glfw3_VERSION=
-- __pkg_config_arguments_GLFW=REQUIRED;glfw3
-- __pkg_config_checked_GLFW=1
-- pkgcfg_lib_GLFW_glfw3=/usr/local/lib/libglfw3.a
triangle_man
  • 1,072
  • 6
  • 10
  • "what do I need to do to get the right linker command to be generated from the output of `pkg_search_module`? - You shouldn't use `*_LDFLAGS` variable for link with. Instead, use `*_LIBRARIES` variable, which accumulates library-like linker options, and correctly processes `-framework` options. All other linker options are accumulated in `*_LDFLAGS_OTHER` variable. You could use that variable too, but in most cases it is empty. See e.g. [that answer](https://stackoverflow.com/a/29316084/3440745) which describes usage of `pkg_check_modules` results. – Tsyvarev Sep 03 '21 at 07:32
  • Thanks for the reply, @Tsyvarev. Sadly, for me, $GLFW_LIBRARIES is literally just "-lglfw3". So by using that, it can't even find that library because it's not a full path. – triangle_man Sep 03 '21 at 15:48
  • I edited the question with more specifics of what I tried in response to Tsyvarev's suggestion. – triangle_man Sep 03 '21 at 15:55

2 Answers2

0

Frameworks exist now in the dynamic cache, detected by dlopen() on the library path. You made it work, so I would suggest a conditional if(APPLE) section to handle Apple frameworks.

New in macOS Big Sur 11.0.1, the system ships with a built-in dynamic linker cache of all system-provided libraries. As part of this change, copies of dynamic libraries are no longer present on the filesystem. Code that attempts to check for dynamic library presence by looking for a file at a path or enumerating a directory will fail. Instead, check for library presence by attempting to dlopen() the path, which will correctly check for the library in the cache. (62986286)

https://developer.apple.com/documentation/macos-release-notes/macos-big-sur-11_0_1-release-notes

Richard Barber
  • 5,257
  • 2
  • 15
  • 26
0

In the end, I gave up on pkg-config and ended up getting it to work with find_package. This is the CMakeLists.txt I ended up with, based on the GLFW guide.

cmake_minimum_required(VERSION 3.5)

project(simple_repro)

set(CMAKE_CXX_STANDARD 17)

find_package(glfw3 3.3 REQUIRED)

add_executable(app1 main.cpp glad.c)
target_include_directories(app1 PRIVATE
  ${CMAKE_SOURCE_DIR}
)

target_link_libraries(app1 glfw)

find_package(OpenGL REQUIRED)
target_link_libraries(app1 OpenGL::GL)
triangle_man
  • 1,072
  • 6
  • 10