빌드하기

Oasis 응용 프로그램 개발과 빌드는 주로 호스트 PC (x86_64 Ubuntu 등)에서 진행됩니다.

Oasis는 컴포넌트 별로 구성되어 있어서 응용 프로그램이 사용하는 컴포넌트 라이브러리만 사용하여 빌드합니다.

Oasis 응용 프로그램이 실행되는 타겟 장치의 프로세서가 호스트 PC의 프로세서와 다르거나 바이너리상 호환이 안 되는 경우, 크로스 컴파일을 합니다.

여기서는 Makefile을 이용하여 빌드하는 방법CMake를 이용하여 빌드하는 방법을 설명합니다.

타겟 장치 프로세서가 aarch64 이며 호스트 PC에 GCC 크로스툴 체인이 /opt/vtcs_toolchain/leipzig 에 설치되어 있다고 가정합니다.

Oasis SDK는 호스트 PC의 아래 폴더에 있다고 가정합니다.

  • 라이브러리 /opt/oasis/sdk-4.3.29/lib
  • 헤더파일 /opt/oasis/sdk-4.3.29/include

hello.cpp

아래 코드(hello.cpp)는 빌드 과정 설명을 위한 응용 프로그램 코드입니다.

빌드를 마치면 hello 라는 Oasis 응용 프로그램이 생성됩니다.

#include "OasisAPI.h"
#include "OasisLog.h"
#include "OasisUI.h"
#include "OasisUtil.h"
#include "OasisDisplay.h"

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>

#include <thread>
#include <mutex>
#include <memory>
#include <condition_variable>


#undef DLOG_TAG
#define DLOG_TAG "Hello"


using namespace oasis;

bool got_interrupt = false;

#include <signal.h>

//SIGINT 핸들러입니다. 프로그램을 종료합니다.
void cancel_handler(int sig)
{
  got_interrupt = true;
}

//Capture Me! 버튼이 클릭되었을 때 호출되는 함수입니다. 스크린을 캡춰하여 /tmp에 저장합니다.
void captureMeClicked(ui::WidgetRef widget)
{
  ui::ScreenRef screen = ui::getDefaultScreen();

  std::vector<uint8_t> png_data;
  char image_path[PATH_MAX];
  time_t t;
  struct tm tm_t;
  time(&t);
  localtime_r(&t, &tm_t);    

  sprintf(image_path, "/tmp/snapshot_%4d-%02d-%02d-%02d-%02d-%02d.png", tm_t.tm_year + 1900, tm_t.tm_mon + 1, tm_t.tm_mday, tm_t.tm_hour, tm_t.tm_min, tm_t.tm_sec);
  if(ui::takeScreenShot(screen, png_data) > 0) {
    printf("saved to %s\n", image_path);
    writeFile(png_data.data(), png_data.size(), image_path);
  }
}

//Goodbye! 버튼이 클릭되었을 때 호출되는 함수입니다. 프로그램을 종료합니다.
void goodbyeClicked(ui::WidgetRef widget)
{
  got_interrupt = true;
}

int main(int argc, char *argv[])
{
  int32_t err;

  signal(SIGINT, cancel_handler);

  oasis::key_value_map_t parameters;

  //Oasis를 초기화합니다. Oasis 파일 시스템을 비활성화합니다.
  parameters["offs-disable"] = "1";
  if(oasis::initialize(parameters) < 0) {
    DLOG(DLOG_ERROR, "Oasis init failed\n");
    return -1;
  }


  //디스플레이 장치를 초기화합니다.
  parameters.clear();

  parameters["memory-type"] = "ion"; //cma
  parameters["screen-width"] = "480";
  parameters["screen-height"] = "320";

  display::setup(parameters);

  //GUI를 초기화합니다.
  parameters.clear();

  parameters["system-font-size"] = "11";
  parameters["system-font-path"] = "/mnt/sd/NanumGothicCoding.ttf";
  parameters["display-rotation"] = "0";
  parameters["enable-touch"] = "1";
  parameters["touch-cal-data-path"] = "/mnt/sd/cal.dat";

  err = ui::setup(parameters);
  if (err < 0) {
    DLOG(DLOG_ERROR, "Oasis ui::setup failed\n");
    oasis::finalize();
    return -1;
  }

  //디폴트 화면 객체를 얻습니다.
  ui::ScreenRef screen = ui::getDefaultScreen();

  //최상위 Window를 생성합니다. 생성시 보이지 않습니다.
  ui::WindowRef window = ui::createWindow("Hello");

  //최상위 Window에 세로 Box 레이아웃 객체를 생성합니다.
  ui::BoxRef vbox = ui::createBox(ui::kOrientationVertical, false, 6);
  //Box 레이아웃은 Window에 추가된 후 중앙에 배치되도록 합니다.
  ui::setVAlign(vbox, ui::kAlignCenter);

  //라벨1,2,3를 생성하고 각각 Box 레이아웃에 추가합니다.
  ui::LabelRef label = ui::createLabelWithText("Hello, World!");
  ui::setVAlign(label, ui::kAlignCenter);
  ui::setHAlign(label, ui::kAlignCenter);
  ui::setLabelHorzJustification(label, ui::kJustificationCenter);
  ui::setLabelVertJustification(label, ui::kJustificationCenter);
  ui::setLabelFontPointSize(label, 16);

  ui::boxPackStart(vbox, label, true, true, 0);

  ui::LabelRef label2 = ui::createLabelWithText("안녕하세요!");
  ui::setVAlign(label2, ui::kAlignCenter);
  ui::setHAlign(label2, ui::kAlignCenter);
  ui::setLabelHorzJustification(label2, ui::kJustificationCenter);
  ui::setLabelVertJustification(label2, ui::kJustificationCenter);
  ui::setLabelFontPointSize(label2, 13);

  ui::boxPackStart(vbox, label2, true, true, 0);

  ui::LabelRef label3 = ui::createLabelWithText("こんにちは!");
  ui::setVAlign(label3, ui::kAlignCenter);
  ui::setHAlign(label3, ui::kAlignCenter);
  ui::setLabelHorzJustification(label3, ui::kJustificationCenter);
  ui::setLabelVertJustification(label3, ui::kJustificationCenter);
  ui::setLabelFontPointSize(label3, 13);

  ui::boxPackStart(vbox, label3, true, true, 0);

  //Goodbye! 버튼을 생성하고 Box 레이아웃 끝쪽부터 추가합니다.
  ui::ButtonRef close_button = ui::createButtonWithLabel("Goodbye!", true);
  ui::setID(close_button, "close_button");
  ui::setLabelFontPointSize(ui::getButtonLabel(close_button), 13);
  ui::setLabelTextColor(ui::getButtonLabel(close_button), Color(255, COLOR_YELLOW));
  ui::setVAlign(close_button, ui::kAlignCenter);
  ui::setHAlign(close_button, ui::kAlignCenter);
  ui::boxPackEnd(vbox, close_button, false, true, 0);

  //Capture Me! 버튼을 생성하고 Box 레이아웃 끝쪽부터 추가합니다.
  ui::ButtonRef capture_button = ui::createButtonWithLabel("Capture Me!", true);
  ui::setID(capture_button, "capture_me_button");
  ui::setLabelFontPointSize(ui::getButtonLabel(capture_button), 13);
  ui::setLabelTextColor(ui::getButtonLabel(capture_button), Color(255, COLOR_YELLOW));
  ui::setVAlign(capture_button, ui::kAlignCenter);
  ui::setHAlign(capture_button, ui::kAlignCenter);
  ui::boxPackEnd(vbox, capture_button, false, true, 20);

  //Capture Me! 버튼 클릭 이벤트 호출 함수를 연결합니다.
  ui::connectSignal(capture_button, "clicked", clicked_slot_t(sig::ptr_fn(&captureMeClicked)), 0);

  //Goodbye! 버튼 클릭 이벤트 호출 함수를 연결합니다.
  ui::connectSignal(close_button, "clicked", clicked_slot_t(sig::ptr_fn(&goodbyeClicked)), 0);

  //최상위 Window에 Box 레이아웃을 추가합니다.
  ui::containerAdd(window, vbox);
  //화면에 Window를 추가합니다.
  ui::screenAdd(screen, window);

  //최상위 Window가 보이도록 합니다.
  ui::setVisible(window, true);

  //메인 쓰레드가 종료 신호를 받을 때까지 기다립니다.
  do {
    usleep(100000);
  } while(got_interrupt == false);

  //GUI를 해제합니다.
  err = ui::cleanup();

  //Oasis를 해제합니다.
  oasis::finalize();

  return 0;
}

Makefile 빌드

  1. hello.cpp가 있는 소스 폴더에 Makefile 파일을 아래 내용으로 생성합니다.

    # 크로스 툴체인 환경을 설정합니다.
    CROSS_COMPILER_PREFIX = aarch64-linux-
    EXTERNAL_TOOLCHAIN_ROOT = /opt/vtcs_toolchain/leipzig/usr/
    EXTERNAL_TOOLCHAIN_PATH = $(EXTERNAL_TOOLCHAIN_ROOT)bin/
    SYSROOT = $(EXTERNAL_TOOLCHAIN_ROOT)/aarch64-buildroot-linux-gnu/sysroot
    
    CC = $(EXTERNAL_TOOLCHAIN_PATH)$(CROSS_COMPILER_PREFIX)gcc
    CXX = $(EXTERNAL_TOOLCHAIN_PATH)$(CROSS_COMPILER_PREFIX)g++
    LD = $(EXTERNAL_TOOLCHAIN_PATH)$(CROSS_COMPILER_PREFIX)ld
    AR = $(EXTERNAL_TOOLCHAIN_PATH)$(CROSS_COMPILER_PREFIX)ar
    
    # Oasis SDK 환경을 설정합니다.
    OASIS_SDK_ROOT = /opt/oasis/sdk-4.3.29/
    OASIS_INC_PATH = $(OASIS_SDK_ROOT)/include
    OASIS_LIB_PATH = $(OASIS_SDK_ROOT)/lib
    OASIS_LIBS = -loasis_codec -loasis_fs -loasis_pipe -loasis_media -loasis_ui -loasis_util -loasis_certs -loasis_disp -loasis
    OASIS_DEFS = -DOASIS_NO_NVWA
    
    # 타겟을 설정합니다.
    TARGET = hello
    SRCS = hello.cpp
    OBJS = $(SRCS:.cpp=.o)
    
    CXXFLAGS += -I$(OASIS_INC_PATH) --sysroot=$(SYSROOT)
    LDFLAGS += -s -L$(OASIS_LIB_PATH) $(OASIS_LIBS)
    
    CXXFLAGS += -std=c++17 -O2 -g $(OASIS_DEFS)
    
    # 기타 의존적인 라이브러리를 설정합니다.
    LIBS = -lexif -lpng16 -ljpeg -lfreetype -lz -ljsoncpp -lrt -lpthread -lcva -lvmf_nnm -lvmf -lvdec -lutil -liniparser
    
    .PHONY: all clean
    
    all: $(TARGET)
    
    $(TARGET): $(OBJS)
      $(CXX) $(LDFLAGS) $^ $(LIBS) -o $@
    
    %.o: %.cpp
      $(CXX) $(CXXFLAGS) -c $< -o $@
    
    clean:
      rm -f $(OBJS) $(TARGET)
    
  2. 소스 폴더에서 make 명령으로 응용 프로그램을 빌드합니다.

    $ make
    $ ls -l
    total 1144
    -rwxrwxr-x 1 cobenhan cobenhan  133176  3월 14 00:59 hello
    -rw-rw-r-- 1 cobenhan cobenhan    5121  3월 14 00:43 hello.cpp
    -rw-rw-r-- 1 cobenhan cobenhan 1023288  3월 14 00:59 hello.o
    -rw-rw-r-- 1 cobenhan cobenhan    1446  3월 14 00:59 Makefile
    

CMake 빌드

  1. hello.cpp가 있는 소스 폴더에 toolchain.cmake 파일을 아래 내용으로 생성합니다.

    # this one is important
    SET(CMAKE_SYSTEM_NAME Linux)
    #this one not so much
    SET(CMAKE_SYSTEM_VERSION 1)
    SET(CMAKE_SYSTEM_PROCESSOR aarch64)
    
    SET(EXTERNAL_TOOLCHAIN_ROOT /opt/vtcs_toolchain/leipzig/usr/)
    SET(EXTERNAL_TOOLCHAIN_PATH ${EXTERNAL_TOOLCHAIN_ROOT}/bin)
    SET(CROSS_COMPILER_PREFIX aarch64-linux-)
    
    # specify the cross compiler
    SET(CMAKE_C_COMPILER   /opt/vtcs_toolchain/leipzig/usr/bin/aarch64-linux-gcc)
    SET(CMAKE_CXX_COMPILER ${EXTERNAL_TOOLCHAIN_PATH}/aarch64-linux-g++)
    SET(CMAKE_AR_COMPILER ${EXTERNAL_TOOLCHAIN_PATH}/aarch64-linux-ar)
    SET(CMAKE_STRIP_COMPILER ${EXTERNAL_TOOLCHAIN_PATH}/aarch64-linux-strip)
    SET(CMAKE_SYSROOT ${EXTERNAL_TOOLCHAIN_ROOT}/aarch64-buildroot-linux-gnu/sysroot)
    
    # where is the target environment 
    SET(CMAKE_FIND_ROOT_PATH ${CMAKE_FIND_ROOT_PATH} ${EXTERNAL_TOOLCHAIN_ROOT})
    SET(CMAKE_FIND_ROOT_PATH ${CMAKE_FIND_ROOT_PATH} ${EXTERNAL_TOOLCHAIN_ROOT}/aarch64-buildroot-linux-gnu)
    SET(CMAKE_FIND_ROOT_PATH ${CMAKE_FIND_ROOT_PATH} ${EXTERNAL_TOOLCHAIN_ROOT}/aarch64-buildroot-linux-gnu/sysroot/lib)
    
    # search for programs in the build host directories
    SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
    # for libraries and headers in the target directories
    SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
    SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
    
    # SET(TOOLSDIR /opt/vtcs_toolchain/leipzig)
    
    SET(CMAKE_ASM_COMPILER   ${EXTERNAL_TOOLCHAIN_PATH}/${CROSS_COMPILER_PREFIX}gcc)
    # Override ar and ranlib tools that CMake should use for linking lib
    SET(CMAKE_AR             ${EXTERNAL_TOOLCHAIN_PATH}/${CROSS_COMPILER_PREFIX}ar      CACHE STRING "")
    SET(CMAKE_RANLIB        ${EXTERNAL_TOOLCHAIN_PATH}/${CROSS_COMPILER_PREFIX}ranlib  CACHE STRING "")
    
  2. hello.cpp가 있는 소스 폴더에 CMakeLists.txt 파일을 아래 내용으로 생성합니다.

    cmake_minimum_required (VERSION 3.5.1)
    
    project (Hello)
    
    # Oasis SDK 환경을 설정합니다.
    include_directories(SYSTEM "/opt/oasis/sdk-4.3.29/include")
    #-Wl,-rpath,path1:path2:path3...
    link_directories(/opt/oasis/sdk-4.3.29/lib)
    add_definitions(-DOASIS_NO_NVWA -DOFFS_NO_NVWA)
    
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -O2 -g")
    # strip
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s")
    
    # 타겟을 설정합니다.
    add_executable(hello hello.cpp)
    
    target_link_libraries(hello oasis_codec oasis_fs oasis_pipe oasis_media oasis_ui oasis_util oasis_certs oasis_disp oasis)
    
    target_link_libraries(hello cva vmf_nnm vmf vdec util iniparser)
    target_link_libraries(hello exif png16 jpeg freetype z jsoncpp)
    target_link_libraries(hello rt pthread)
    
  3. 아래와 같이 빌드를 진행합니다.

    $ mkdir build
    $ ls -l
    total 20
    drwxrwxr-x 3 cobenhan cobenhan 4096  3월 14 01:46 build
    -rw-rw-r-- 1 cobenhan cobenhan  751  3월 14 01:44 CMakeLists.txt
    -rw-rw-r-- 1 cobenhan cobenhan 5121  3월 14 00:43 hello.cpp
    -rwxrwxr-x 1 cobenhan cobenhan 1679 12월 11 16:27 toolchain.cmake
    $ cd build
    build$ cmake -DCMAKE_TOOLCHAIN_FILE=../toolchain.cmake ..
    build$ make
    build$ ls -l
    total 164
    -rw-rw-r-- 1 cobenhan cobenhan  14549  3월 14 01:46 CMakeCache.txt
    drwxrwxr-x 6 cobenhan cobenhan   4096  3월 14 01:46 CMakeFiles
    -rw-rw-r-- 1 cobenhan cobenhan   1716  3월 14 01:46 cmake_install.cmake
    -rwxrwxr-x 1 cobenhan cobenhan 133176  3월 14 01:46 hello
    -rw-rw-r-- 1 cobenhan cobenhan   5246  3월 14 01:46 Makefile
    

실행 결과

빌드 후 타겟 장치에 응용 프로그램을 설치하기 가이드에 따라 설치합니다.

아래는 타겟 장치에서 실행 후 Capture Me! 버튼을 터치하여 생성된 캡춰 이미지입니다.

Hello

예제 다운로드