목표
- Windows와 Linux 환경에서 모두 빌드와 실행이 가능한 C++ 공유 라이브러리 프로젝트를 생성하고자 한다.
- 빌드시 Windows에서는 .dll이, Linux에서는 .so가 생성되게 할 것이다.
- 또한 생성된 공유 라이브러리를 테스트 할 수 있는 환경 역시 구축할 것이다.
프로젝트 구성
- 평소 같으면 Visual Studio에서 솔루션을 생성하겠지만, cross-platform이 목적인 이상 그럴 수는 없다.
- Visual Studio는 Windows에서만 사용 가능하기 때문이다.
- 모든 플랫폼에서 작동 가능한 프로젝트를 위해서는 특정 IDE에 종속되는 프로젝트를 구성해서는 안된다.
- 때문에 여러 IDE들이 지원하며, 심지어 터미널에서도 쉽게 사용 가능한 CMake를 사용하여 프로젝트를 구성하기로 결정했다.
본 글은 CMake 자체에 대한 정보는 다루지 않는다. CMake에 대한 초보적인 이해가 있다는 가정 하에 글을 작성할 것이다.
CMake
- CMake는 makefile을 자동으로 생성해주는 빌드 시스템이다.
- makefile은 터미널에서 make 명령어를 사용할 때 참조하는 텍스트 파일이다.
- 프로젝트를 구성하는 파일들의 의존성, 컴파일러에 입력되는 변수 등을 정의할 수 있다.
- CMake 역시 makefile에 정의되는 여러 정보들을 정의할 수 있다.
- Visual Studio에 CMake 기반 프로젝트를 생성하는 기능이 존재하기 때문에 아래의 방법으로 프로젝트를 생성하였다.
- 위의 방법으로 프로젝트를 생성하면 아래와 같은 구조가 생성된다.
- 프로젝트에 필요한 몇 개의 파일들을 추가로 생성하였다.
공유 라이브러리 빌드 설정
- 상위 디랙터리의 CMakeLists.txt는 당장 수정할 것이 없다.
- 프로젝트가 커져 추가적인 하위 디렉터리를 생성할 일이 발생했을 때 수정하면 된다.
- 하위 디랙터리의 CMakeLists.txt의 초기 모습은 다음과 같다.
# CMakeList.txt : CMake project for Math, include source and define
# project specific logic here.
#
# Add source to this project's executable.
add_executable(Math "Math.cpp")
if (CMAKE_VERSION VERSION_GREATER 3.12)
set_property(TARGET Math PROPERTY CXX_STANDARD 20)
endif()
# TODO: Add tests and install targets if needed.
- 먼저 해야할 것은
add_executable
부분을 수정하는 것이다.- 우리는 해당 소스코드를 실행 파일이 아닌 shared library로 빌드하고자 한다.
- 따라서
add_library(Math SHARED "Math.cpp")
로 수정해야한다. - 수정을 마친 CMakeLists.txt는 아래와 같다.
# CMakeList.txt : CMake project for Math, include source and define
# project specific logic here.
#
# Add source to this project's executable.
add_library(Math SHARED "Math.cpp")
if (CMAKE_VERSION VERSION_GREATER 3.12)
set_property(TARGET Math PROPERTY CXX_STANDARD 20)
endif()
# TODO: Add tests and install targets if needed.
- 그럼 이제 shared library 생성을 위한 소스코드들을 작성해보자.
/* Math.h */
#pragma once
// Shared library import/export macro
#ifdef _WIN32
#ifdef MATH_EXPORTS
#define MATH_API __declspec(dllexport)
#else
#define MATH_API __declspec(dllimport)
#endif
#elif __linux__
#define MATH_API
#endif
extern "C" MATH_API double Add(double a, double b);
extern "C" MATH_API double Subtract(double a, double b);
/* Math.cpp */
#include "Math.h"
double Add(double a, double b) {
return a + b;
}
double Subtract(double a, double b) {
return a - b;
}
Math.cpp
파일은 별 내용 없다.- 핵심은
Math.h
이다.- Windows의 경우 dll로 export/import 하기 위해서
__declspec(dllexport)
혹은__declspec(dllimport)
를 함수 시그니쳐 앞에 추가해야 한다.__declspec(dllexport)
: dll을 빌드 할 경우 추가해야 한다.__declspec(dllimport)
: dll을 사용할 경우 추가해야 한다.
- Linux의 경우 그러한 작업이 필요하지 않다. 하지만 위의 키워드들은 msvc에서만 사용 가능하기 때문에 위의 키워드가 포함될 경우 컴파일 에러가 발생할 것이다.
- 때문에 전처리문을 통해서 MATH_API 매크로를 Windows에서 빌드할 경우 해당 키워드로 선언하고, Linux 환경에서는 공란으로 선언할 것이다.
- _WIN32는 Windows 32bit/64bit 환경에서, __linux__는 linux 환경에서 빌드할 경우 선언되는 매크로이다.
<대문자_파일명>_EXPORTS
는 해당 파일이 dll 등으로 export될 때 선언되는 매크로이다.- 즉 헤더 파일이 export될 경우에는
__declspec(dllexport)
가, 그 외의 헤더 파일을 다른 프로젝트에 include 하는 등의 경우에는__declspec(dllimport)
가 선언된다.
- 즉 헤더 파일이 export될 경우에는
- Windows의 경우 dll로 export/import 하기 위해서
- 헤더 파일의
extern "C"
키워드는 해당 함수를 링킹 과정에서 C 함수의 링킹 규칙을 따라 처리하라는 의미이다.- 즉, Name Mangling을 수행하지 말라는 뜻이다. 자세한 내용은 생략한다.
- 해당 키워드를 추가하면 빌드된 라이브러리를 C에서도 사용할 수 있다.
- C++에서만 사용될 라이브러리라면 해당 키워드를 붙이지 않아도 된다.
Google Test
Google Test의 공식 문서를 참고하면 CMake 프로젝트에서 Google Test를 사용하기 위해서는 다음과 같이 CMakeLists.txt 파일을 수정하면 된다. TODO:
아래가 추가된 코드이다.
# CMakeList.txt : CMake project for Math, include source and define
# project specific logic here.
#
# Add source to this project's executable.
add_library(Math SHARED "Math.cpp")
if (CMAKE_VERSION VERSION_GREATER 3.12)
set_property(TARGET Math PROPERTY CXX_STANDARD 20)
endif()
# TODO: Add tests and install targets if needed.
include(FetchContent)
FetchContent_Declare(
googletest
URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip
)
# For Windows: Prevent overriding the parent project's compiler/linker settings
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)
enable_testing()
add_executable(MathTest "MathTest.cpp")
target_link_libraries(MathTest GTest::gtest_main Math)
include(GoogleTest)
gtest_discover_tests(MathTest)
- 위의 코드를 추가하면 CMake가 작동하는 그 시점에 Google Test 라이브러리를 다운로드 받아
MathTest.cpp
에 링크하게 된다. 이제 MathTest.cpp를 작성해보자.
#include <gtest/gtest.h>
#include "Math.h"
TEST(MathLibraryTest, AddTest) {
EXPECT_EQ(Add(1, 2), 3);
EXPECT_EQ(Add(-1, 2), 1);
}
TEST(MathLibraryTest, SubtractTest) {
EXPECT_EQ(Subtract(2, 1), 1);
EXPECT_EQ(Subtract(2, -1), 3);
}
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
- 위와 같이 작성하고 Visual Studio에서 Startup item을 테스트 실행 파일로 설정한다.
- google test의 사용법은 공식 문서 참고.
- 이후 실행 버튼을 누르면 dll과 테스트 프로그램을 포함한 모든 소스코드가 잘 빌드되고 google test 실행 결과를 확인할 수 있다.
- 같은 프로젝트를 Ubuntu에 설치된 Eclipse IDE로 빌드 후 실행한 결과 역시 .so 라이브러리가 잘 빌드되며 google test 결과를 확인할 수 있다.