To start, I'll state that I am quite new to CMake and complex C++ projects. I tried to compile some classes into a static library, and one of these classes (render::ViewRenderer
, declared in view.hpp
, defined in view.cpp
) doesn't have any symbols in the resulting librender.a
file. I'm using clang-15 to compile it, but switching to different compilers didn't do anything.
The class render::WindowRenderer
(window.hpp
, window.cpp
) actually compiles without issue. To see if it was an issue with the library compilation, i separated render::ViewRenderer
to a separate library -- again render::WindowRenderer
was fine, but there were no symbols for render::ViewRenderer
. I even tried to compile the sources directly in the executable instead of the library, but still no effect.
One thing that did have effect though (obviously) was including the definition in the view.hpp
file, so that nothing needs to be compiled, but that's not desired.
So I gather the problem must lie somewhere in compiling view.cpp
.
Here's the code:
view.hpp
#ifndef VIEW_HPP
#define VIEW_HPP
#include <string>
namespace render {
enum View {
main_menu,
game
};
/**
* A class handling the rendering of a window's contents.
*/
template <class View>
class ViewRenderer {
const View view;
public:
void render();
ViewRenderer(View view);
~ViewRenderer();
};
}
#endif
view.cpp
#include "render/view.hpp"
template <class View>
void render::ViewRenderer<View>::render() {
}
template <class View>
render::ViewRenderer<View>::ViewRenderer(View view) : view{ view } {
}
template <class View>
render::ViewRenderer<View>::~ViewRenderer() {
}
window.hpp
#ifndef WINDOW_HPP
#define WINDOW_HPP
#include <GLFW/glfw3.h>
#include <string>
namespace render {
/**
* A class to manage the application's windows. A window opens on init and blocks the thread with its execute method.
* @param name the title of the window to open
* @param game_renderer the initial GameRenderer to handle the window's contents or nullptr for default behaviour
*/
enum WindowMode { windowed, fullscreen, windowed_fullscreen };
class WindowRenderer {
const char* window_name;
GLFWwindow* window;
render::WindowMode window_mode;
void execute();
public:
void (*set_fullscreen)(WindowMode);
void (*end)();
WindowRenderer(std::string);
~WindowRenderer();
};
}
#endif
window.cpp
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <string>
#include <stdexcept>
#include "render/window.hpp"
render::WindowRenderer::WindowRenderer(std::string _window_name) : window_name{ _window_name.c_str() }, window_mode{ render::fullscreen } {
bool glewExperimental = true;
if (!glfwInit()) {
throw std::runtime_error("Failed to initialize GLFW\n"); return;
}
GLFWmonitor* monitor = glfwGetPrimaryMonitor();
this->window = glfwCreateWindow(640, 480, this->window_name, NULL, NULL);
glfwMakeContextCurrent(window);
if (glewInit() != GLEW_OK) {
throw std::runtime_error("Failed to initialize GLEW\n"); return;
}
this->execute();
}
void render::WindowRenderer::execute() {
do {
glClear(GL_COLOR_BUFFER_BIT);
glfwSwapBuffers(window);
glfwPollEvents();
} while (glfwGetKey(window, GLFW_KEY_ESCAPE) != GLFW_PRESS && glfwWindowShouldClose(window) == 0);
}
render::WindowRenderer::~WindowRenderer() {
}
CMakeLists.txt
set(CMAKE_C_COMPILER /opt/local/bin/clang-mp-15)
set(CMAKE_CXX_COMPILER /opt/local/bin/clang++-mp-15)
cmake_policy(SET CMP0048 NEW)
cmake_minimum_required(VERSION 3.20)
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
set(OPENGL_LIBRARY
${OPENGL_LIBRARY}
-lGL -lGLU -lXrandr -lXext -lX11 -lrt
${CMAKE_DL_LIBS}
${GLFW_LIBRARIES}
)
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
set(OPENGL_LIBRARY
${OPENGL_LIBRARY}
${CMAKE_DL_LIBS}
${GLFW_LIBRARIES}
)
endif(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
add_definitions(
-DTW_STATIC
-DTW_NO_LIB_PRAGMA
-DTW_NO_DIRECT3D
-DGLEW_STATIC
-D_CRT_SECURE_NO_WARNINGS
)
project(
game
VERSION 0.0.1
LANGUAGES CXX C OBJC OBJCXX
)
include(CheckCXXCompilerFlag)
CHECK_CXX_COMPILER_FLAG("-std=c++20" COMPILER_SUPPORTS_CXX20)
if(COMPILER_SUPPORTS_CXX20)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++20")
else()
message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++20 support. Please use a different C++ compiler.")
endif()
# ---- Libraries ----
set(SOURCE
${CMAKE_SOURCE_DIR}/src/app/render/window.cpp
${CMAKE_SOURCE_DIR}/src/app/include/render/window.hpp
${CMAKE_SOURCE_DIR}/src/app/render/view.cpp
${CMAKE_SOURCE_DIR}/src/app/include/render/view.hpp
)
# ---- External libraries ----
set(GLEW_VERSION 2.1.0)
set (GLFW_VERSION 3.3.8)
set(GLM_VERSION 0.9.9.8)
# >>>>>>>>>>>>>>>>>>>>>>> GLEW
add_subdirectory(${CMAKE_SOURCE_DIR}/lib/glew-${GLEW_VERSION}/build/cmake) #this produces glew target
set(GLEW_INCLUDE ${CMAKE_SOURCE_DIR}/lib/glew-${GLEW_VERSION}/include)
# >>>>>>>>>>>>>>>>>>>>>>> GLFW
add_subdirectory(lib/glfw-${GLFW_VERSION})
SET(GLFW_INCLUDE lib/glfw-${GLFW_VERSION}/include)
# >>>>>>>>>>>>>>>>>>>>>>>> GLM
SET(GLM_INCLUDE lib/glm-${GLM_VERSION})
# -----------------------------
include_directories(
${CMAKE_SOURCE_DIR}/src/app/include
${GLEW_INCLUDE}
${GLFW_INCLUDE}
${GLM_INCLUDE}
)
add_executable(
game_exe
${CMAKE_SOURCE_DIR}/src/app/main.cpp
)
set(TARGET game_exe PROPERTY CMAKE_CXX_STANDARD 20)
add_library(
render
${CMAKE_SOURCE_DIR}/src/app/render/window.cpp
${CMAKE_SOURCE_DIR}/src/app/include/render/window.hpp
${CMAKE_SOURCE_DIR}/src/app/render/view.cpp
${CMAKE_SOURCE_DIR}/src/app/include/render/view.hpp
)
target_link_libraries(
game_exe
render
glfw
glew
)
Project structure:
app
...
src
include
render
view.hpp
window.hpp
render
view.cpp
window.cpp
main.cpp
...
CMakeLists.txt
Build error I am dealing with:
[main] Building folder: game
[build] Starting build
[proc] Executing command: /opt/homebrew/bin/cmake --build /Users/matt/projects/game/build --config Debug --target all --
[build] [3/5 20% :: 0.445] Building CXX object CMakeFiles/render.dir/src/app/render/view.cpp.o
[build] [3/5 40% :: 0.447] Building CXX object CMakeFiles/render.dir/src/app/render/window.cpp.o
[build] [4/5 60% :: 0.448] Building CXX object CMakeFiles/game_exe.dir/src/app/main.cpp.o
[build] [4/5 80% :: 0.474] Linking CXX static library librender.a
[build] [5/5 100% :: 0.619] Linking CXX executable game_exe
[build] FAILED: game_exe
[build] : && /opt/local/bin/clang++-mp-15 -std=c++20 -arch arm64 -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX13.0.sdk -Wl,-search_paths_first -Wl,-headerpad_max_install_names CMakeFiles/game_exe.dir/src/app/main.cpp.o -o game_exe -Wl,-rpath,/Users/matt/projects/game/build/lib librender.a lib/glfw-3.3.8/src/libglfw3.a lib/libGLEW.2.1.0.dylib -framework Cocoa -framework IOKit -framework CoreFoundation -Xlinker -framework -Xlinker OpenGL && :
[build] Undefined symbols for architecture arm64:
[build] "render::ViewRenderer<render::View>::ViewRenderer(render::View)", referenced from:
[build] _main in main.cpp.o
[build] "render::ViewRenderer<render::View>::~ViewRenderer()", referenced from:
[build] _main in main.cpp.o
[build] ld: symbol(s) not found for architecture arm64
[build] clang: error: linker command failed with exit code 1 (use -v to see invocation)
[build] ninja: build stopped: subcommand failed.
[proc] The command: /opt/homebrew/bin/cmake --build /Users/matt/projects/game/build --config Debug --target all -- exited with code: 1 and signal: null
[build] Build finished with exit code 1
[cpptools] The build configurations generated do not contain the active build configuration. Using "" for CMAKE_BUILD_TYPE instead of "Debug" to ensure that IntelliSense configurations can be found