[CMake] 기초 (3rdParty 빌드)

happy_quokka·2023년 10월 16일

C++

목록 보기
3/3

Build 3rdParty

cmake 설치

  • 난 이미 되어 있다
  • 터미널에서 설치
$ sudo apt-get install -y cmake

설치할 프로그램의 소스코드 가져오기

  • https://github.com/opencv/opencv
  • opencv가 cmake로 빌드 될 수 있다는 것을 어떻게 알 수 있나?
    • 파일 구조 확인
    • 가장 상위단에 CMakeLists.txt 파일이 있으면 cmake로 빌드를 지원하겠다는 의미
  • 깃헙에서 clone 하기
$ git clone https://github.com/opencv/opencv.git

  • 현재는 홈 디렉토리에 설치 했지만 좋은 위치가 아니다. 나중에 좋은 경로 알려준다. (일단은 내가 dev_opencv 폴더 만들어서 넣어놨다)

빌드

cmake 빌드에는 두단계가 있다

  1. generate 단계
  • txt파일을 읽어서 c++ 프로젝트의 구조를 파악하고 빌드를 위한 컴파일러가 있는지, 빌드 시스템이 존재하는지 빌드를 위한 소스코드가 제자리에 있는지 확인
  • 그 후, 빌드 시스템에 맞춰 빌드 build command 생성
  1. build 단계
  • 빌드 시스템에 c++ 프로그램과 라이브러리 빌드

사용하기

build, install 폴더 생성

  • build : 빌드 완성물
  • install : 프로그램 설치 작업, 완성된 빌드에서 유저가 직접 사용할 라이브러리와 프로그램들만 시스템 폴더로 옮겨주는 작업을 install이라고 함. 보통은 system이라는 폴더로 가서 install폴더를 만들 필요가 없다.고
  • 지금은 시스템 설치 방법, 원하는 곳에 설치하는 방법을 배우기 위해 install 폴더를 만들었다.
$ cd dev_opencv
$ mkdir build
$ mkdir install

generate 단계

cmake 명령어로 cmake 호출

$ cd build
$ cmake ../opencv/ --> 이렇게만해도 generate된다
  • 파라미터 : generate하고 싶은 프로젝트에 가장 상위단의 CMakeListas.txt가 존재하는 경로를 적어주면 된다.

디버그, 릴리즈 모드 선택

  • 디버깅 모드 : 모든 최적화 과정을 풀어서
$ cmake -DCMAKE_BUILD_TYPE=Debug ../opencv
  • -D는 파라미터를 의미

  • -DCMAKE_BUILD_TYPE=Debug 또는 Release 사용

  • 릴리즈 모드 : 코드 최적화, 디버깅하기 어렵다

$ cmake -DCMAKE_BUILD_TYPE=Release ../opencv

빌드 시스템(generator) 선택

  • 리눅스는 make
  • 닌자 : 빌드 시스템
  • -G 사용
$ cmake -DCMAKE_BUILD_TYPE=Release -GNinja ../opencv

install할때 저장 위치 설정

  • 아무런 설정을 해주지 않으면 usr/local/include 같은 곳에 저장이 된다
  • 장점 : 어떤 프로젝트에서도 쉽게 라이브러리를 로딩할 수 있다
  • 단점 : 동일한 라이브러리를 여러 버전으로 설치할 경우, 충돌이 날 수 있다. (cmake에서 버전을 설정할 수 있지만 꼬일 수 있다)
  • 그래서 system 디렉토리가 아닌 local 폴더에 저장하는 것을 선호 한다
  • 이 방법의 장점은 지울때 해당 폴더만 지우면 된다 / 꼬이는 것을 피할 수 있다.
  • -DCMAKE_INSTALL_PREFIX=원하는 경로
$ cmake -DCMAKE_BUILD_TYPE=Release -GNinja -DCMAKE_INSTALL_PREFIX=../install ../opencv

현재 최종 generate 실행

$ cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../install ../opencv
  • 이걸 하고 나면 build 폴더에 뭐가 많이 생긴다.
  • Makefile : make build 시스템이 사용할 수 있는 모든 command를 늘여놓은 것

빌드하기

$ make
또는
$ make -j (난 이걸로 하니 터져버렸다...)

makefile 기준으로 빌드가 된다.

  • 빌드 완료 확인 방법
    • build/lib 확인 : 라이브러리 파일들이 있으면 빌드가 잘 된거다

옵션들

  • -j : 동시에 빌드를 할 수 있는 작업의 수, 보통 core하나당 작업이 하나씩 할당되어 병렬 빌드 -> 더 빠르게 빌드가 된다
    • 빌드 중에 빌드가 터지는 경우, 엄청 느려지는 경우, 에러가 (cc1+) 난 경우 -> 이 옵션 때문에 생겼다.
    • 빌드할 때 메모리를 많이 잡아먹는데, 모든 core를 사용하면 다른 작업을 할 수 없기 때문에 느려진다.
    • 메모리를 다 차지하게 되면 더 이상 빌드를 할 메모리가 존재하지 않으면 난리가 나고 빌드가 종료된다.
    • 본인의 컴퓨터에서 ram 용량의 2~3배 정도를 swap 메모리에 할당하면 좋다
      • swap 메모리 : 실제 메모리 Ram이 가득 찼지만 더 많은 메모리가 필요할때 디스크 공간을 이용하여 부족한 메모리를 대체할 수 있는 공간
    • 또는 -j2 이런식으로 동시 작업의 수를 정해준다. 자신의 전체 core 수보다 2개 정도 작게 잡으면 좋다

install

  • 유저 입장에서 필요한 것들만 모아주는 것 -> install
빌드 파일에서
$ sudo make install
  • sudo를 해주는 이유 : 원래는 system 폴더에 저장이 되기 때문에 이에 접근하기 위해 sudo를 사용한다 .
  • install 파일 확인

Hello CMake

  • 수업에서는 CLine 을 사용했지만 나는 vscode를 사용할거다
  • 먼저 vscode에 cmake와 cmake tools라는 확장툴이 설치되어 있어야한다.

참고 : https://geeks-mimic.tistory.com/62

  1. 새 프로젝트를 만들어 준다
  • 홈 디렉토리에 hello_cmake 폴더를 만든다

  • cmake : 빠른 시작 -> gcc 선택 -> c++ 선택

  • 결과로 이렇게 만들어 진다.

  1. CMAkeLists.txt 파일 살펴보기
cmake_minimum_required(VERSION 3.0.0)
project(hello_cmake VERSION 0.1.0 LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

add_executable(hello_cmake main.cpp)
  • cmake_minimum_required : cmake의 최소 버전 명시
  • project : 프로젝트의 이름
    • LANGUAGES CXX : c++ 언어를 사용하겠다
  • set : 어떤 변수의 값을 넣어주는 것
    • CMAKE_ : cmake에 종속되어 있다는 의미
    • set(CMAKE_CXX_STANDARD 14) : c++14으로 빌드하겠다
    • set(CMAKE_CXX_STANDARD_REQUIRED ON) : c++14을 넘어가는 기능들이 사용되는 경우 빌드가 되지 않도록 제한하는 것(이게 없으면 warning만 나타나고 빌드가 계속 진행됨)
      • 자율주행의 경우 14을 요구하는 경우가 많다
  • add_executable : 실행파일을 추가하겠다
    • add_executable(hello_cmake main.cpp) : (실행파일의 이름, 프로그램을 만들기 위해 사용될 소스코드)
  1. 빌드
    vscode 아래의 빌드 버튼을 누르면 빌드가 진행된다.

  2. 실행
    실행 버튼을 누르면 실행된다.

목표 1 : C++ 프로젝트의 폴더 구조 확립

소스파일을 넣어둘 폴더 (2가지 방법론이 있다)

(1) 헤더 파일과 소스 파일 분리하는 방법

  • include 폴더 : 헤더파일
  • source 폴더 : 소스코드 파일

(2) modules 폴더 안에 여러 module 폴더를 만들고 각각의 폴더에 헤더파일과 소스파일을 둘다 넣어둔다

-> 둘다 섞어서 쓰시는 걸 좋아한다

  1. modules 폴더 안에 module1, module2, module3 폴더를 만든다. 여기 안에 각각 include와 src를 하나씩 만든다.
  • 모듈들은 라이브러리만 생성한다
  1. examples 폴더 : main 문들만 있다. 모든 코드들의 entry point

  2. thirdparty : thirdparty 라이브러리

최종

목표 2 : openCV를 사용하는 라이브러리 빌드

opencv 설치

  1. thirdparty 안에 opencv 빌드한다
$ cd thirdparty
$ mkdir OpenCV
$ cd OpenCV
$ git clone https://github.com/opencv/opencv.git

  1. build와 install 폴더를 만든다.
$ mkdir build
$ mkdir install

  1. build 폴더에 디버그 모드로 빌드한다.
$ cd build
$ cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=../install ../opencv
$ make
  1. install 한다
$ sudo make install
  • 파일 생성 확인

module1 만들기

  1. include안에 module1 폴더 생성
  2. module1안에 헤더파일 만들기
  • ClassMat.hpp

#ifndef HELLO_CMAKE_CLASSMAT_HPP
#define HELLO_CMAKE_CLASSMAT_HPP

class ClassMat
{
public:
	ClassMat() = default;
private:
	cv::Mat cv_mat_;
};

#endif
  1. src 안에 ClassMat.cpp
#include "module1/ClassMat.hpp"
  1. 이렇게 하면 문제가 2개 있다
  • ClassMat.hpp에서는 opencv를 찾지 못해 cv에서 오류 발생
  • ClaaMat.cpp에서 module1을 찾지 못해 에러 발생
  • 왜인가? cmake로 프로젝트를 엮어주지 않았기 때문에 발생!
  1. 가장 최상위단의 CMakeLists.txt파일 수정
cmake_minimum_required(VERSION 3.0.0)
project(hello_cmake VERSION 0.1.0 LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

add_subdirectory(modules)
add_executable(hello_cmake main.cpp)
  • subdirectory : 지정한 디렉토리에 있는 CMakeList.txt 파일을 실행하겠다. 근데 아직 modules 폴더에 txt파일이 없다. -> 생성해야한다.
  1. modules 폴더 안에 CMakeLists.txt 파일 생성
add_subdirectory(module1)
  • module1에도 txt 파일이 없기 때문에 생성해야함
  1. module1 폴더 안에 CMakeLists.txt 파일 생성
  • module1 자체도 하나의 라이브러리가 될 예정이니까 이 역시 하나의 c++ 프로젝트라고 볼 수 있다. -> 최상위단과 비슷하게 만들어야한다.
cmake_minimum_required(VERSION 3.0.0)
project(module1 LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
  1. module1 작성 (소스파일 정리)
    CMake txt에
//소스파일이 어떤 것들이 잇는지, 다른 cpp 파일이 생기면 여기에 추가로 적으면 된다
//변수 MODULE_SOURCE_FILES 생성
set(MODULE1_SOURCE_FILES
		src/ClassMat.cpp
    )

//소스파일 기반 라이브러리 빌드를 위해
//라이브러리의 이름은 module1
//모든 소스파일들을 여기에 추가 `${}`이렇게 변수를 사용(안에 변수명을 적으면 된다)
add_library(module1
	${MODULE_SOURCE_FILES}
    )
    
//소스파일들을 사용한 헤더파일 연결
//특정 target 정해줌, 여기서는 module1, 즉 module1에 들어가는 헤더파일들에 대해서만
//어떻게 include 할지 정해줄 수 있다 : PRIVATE(외부에 노출 x), PUBLIC(이 라이브러리를 사용하는 상위 라이브러리들도 하위 라이브러리에 쉽게 접근할 수 있다) INTERFACE
//헤더 파일 include 해야함 -> 단순히 경로를 지정해주면 된다
//즉, target_include_directories (특정타겟 include방법지정 경로)
target_include_directories(module1 PRIVATE
	include)

//include_directoriese : global, 모든 하위 cmakelist 에서도 적용가능
  1. generate 해준다
  • 그 결과 라이브러리를 빌드하는 옵션이 생긴다.
  1. 빌드
  • 오류가 난다. cv 때문에
  • 소스파일과 헤더파일이 연결이 되어 헤더파일까지 검사한다.
    -> opencv를 연결해야한다.
  1. opencv 연결 (cmake로 가능)
  • thirdparty에 설치해둔 상태이다
  • module1 폴더의 CMakeLists.txt에
//thirdparty에서 opencv를 찾아야한다
//REQUIRED : 찾지 못했을 경우 빌드를 하지 않는다.
//fins_package는 시스템에 저장된 것들 먼저 찾는다 -> 우리는 local에 설치 했기 때문에 hint 를 사용해서 경로를 지정해야한다. 
//CMAKE_SOURCE_DIR : 가장 최상위 ,entry directory를 의미한다(여기에서는 hello_cmake폴더)
//find_package랑 호환되는 모든 라이브러리들은 '라이브러리이름config.cmake' -> 이걸 찾아서 연결해줘야한다 -> 그 파일이 있는 경로를 적어주면 된다.

find_package(OpenCV REQUIRED HINTS ${CMAKE_SOURCE_DIR}/thirdparty/OpenCV/install/lib/cmake/opencv4)
  1. 빌드

    아직 이런 에러가 있다

  2. opencv가 찾아졌는지 확인해본다

  • module1 폴더의 CMakeLists.txt에
//config.cmake에서 라이브러리를 찾았으면 `라이브러리_FOUND`가 true가 된다
//message(STATUS ) : 빌드를 할 때 상태 출력
if (OpenCV_FOUND)
	message(STATUS "opencv found!! - ${OpenCV_DIR}")
endif()

  • 제대로 찾았다
  1. include directory에 opencv를 include해준다
  • module1 폴더의 CMakeLists.txt에
target_include_directories(module1 PRIVATE
    include
    ${OpenCV_INCLUDE_DIRS}
    )
  1. 라이브러리를 link 해줘야한다
    target_include_directories는 헤더파일을 연결
    target_link_libraries() : 라이브러리를 linking 해주는 것

목표 3 : opencv를 사용하는 프로그램 만들기

  1. examples 폴더에 exec_module1.cpp 파일 생성
#include "module1/ClassMat.hpp"
#include <iostream>

int main()
{
    const auto instance = ClassMat();
    std::cout << "success" << std::endl;
    return 0;
}
  1. 최상단의 CMakeLists.txt에 추가
add_executable(exec_module1 examples/exec_module1.cpp)
  1. 라이브러리 링크를 해줘야한다. 최상단의 CMakeLists.txt에 추가
target_link_libraries(exec_module1 PRIVATE
    module1)
  1. 빌드
  • include가 안된 상황
    • include를 직접 해줘도 좋고
    • module1의 CMakeLists.txt파일의 target_include_directories 를 PUBLIC으로 (이 방법 선택했다)

--> 아니 다 똑같이 했는데 헤더파일이 없다고 뜬다... 이것저것 해보다가 안되서 vscode를 껐다가 키니까 됐다... 이게 뭔가....ㅠㅠ

목표 4 : opencv와 eigen3를 사용하는 프로그램 만들기

eigen3 소스파일 clone 후 빌드

https://gitlab.com/libeigen/eigen

$ cd
$ cd hello_cmake
$ cd thirdparty
$ mkdir Eigen3
$ cd Eigen3
$ git clone https://gitlab.com/libeigen/eigen.git

$ mkdir build
$ mkdir install
$ cd build
$ cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=../install ../eigen
$ make
$ sudo make install

module2에 eigen3를 매핑하는 라이브러리 만들기

  1. include 폴더 안에 module2 폴더 만들기
  2. module2 폴더 안에 ClassEigenMat.hpp 파일 만들기
#ifndef HELLO_CMAKE_HPP
#define HELLO_CMAKE_HPP

#include "Eigen/Dense"

class ClassEigenMat
{
public:
    ClassEigenMat() = default;
private:
    Eigen::Matrix3d eigen_mat_;
};

#endif
  1. src 폴더 안에 ClassEigenMat.cpp 파일 만들기
#include "module2/ClassEigenMat.hpp"
  1. module2 폴더 안에 CMakeLists.txt 파일 만들기
cmake_minimum_required(VERSION 3.0.0)
project(module2 LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

set(MODULE2_SOURCE_FILES
        src/ClassEigenMat.cpp
    )

add_library(module2
    ${MODULE2_SOURCE_FILES}
)

find_package(Eigen3 REQUIRED HINTS ${CMAKE_SOURCE_DIR}/thirdparty/Eigen3/install/share/eigen3/cmake)

if (Eigen3_FOUND)
	message(STATUS "Eigen3 found!! - ${Eigen3_DIR}")

    set(Eigen3_LIBS Eigen3::Eigen)
endif()

target_include_directories(module2 PUBLIC
    include
    ${Eigen3_INCLUDE_DIRS}
    )

target_link_libraries(module2 PUBLIC
    ${Eigen3_LIBS}
    )
  • Eigen3는 header only 라이브러리여서 라이브러리 안에서 include를 타고 가는 형식이여서 target_link_libraries할때 PUBLIC으로 해야 상위단에서 eigen에 include로 들어갈 수 있다고 한다.
  • eigen3는 opencv처럼 바로 OpenCV_LIBS를 안주기 때문에 이렇게 해줘야한다. set(Eigen3_LIBS Eigen3::Eigen)
  1. modules 폴더의 CMakeLists.txt에 추가
add_subdirectory(module2)

module1과 module1를 사용하는 프로그램 만들기

  1. examples에 exec_module1_module2.cpp 파일 만들기
#include "module1/ClassMat.hpp"
#include "module2/ClassEigenMat.hpp"

#include <iostream>

int main()
{
    const auto mat_module1 = ClassMat();
    const auto mat_module2 = ClassEigenMat();

    std::cout << "success!!" << std::endl;
}
  1. 최상단 CMakeLists.txt에 추가
  • examples/exec_module1_module2.cpp에 module1과 module2를 link 하겠다
add_executable(exec_module1_module2 examples/exec_module1_module2.cpp)
target_link_libraries(exec_module1_module2 PRIVATE
    module1
    module2
    )

0개의 댓글