[NNStreamer] 첫 번째 미팅 정리

7 minute read

제일 먼저 기본적인 Building에 대해서 설명해주셨습니다.

How to Build

basic terminology

build란? compile이란? link란?

가장 기본적인 의미는 프로그래밍 언어기계어로 바꾸어주는 과정입니다. 먼저 C나 java 같은 native code에서 (native/managed 코드의 자세한 구분은 후술) *.o파일을 만드는 것을 컴파일이라고 합니다. (이는 개발자가 작성한 소스코드를 binary code로 변환하는 과정을 의미합니다.) 그리고 *.o파일에서 .exe파일을 만드는 것을 링크라고 합니다. 기능별로 분할된 여러 개의 native code들이 있을 때 각각을 *.o파일로 컴파일 하고, 다수의 *.o파일을 하나의 *.exe파일을 만드는 것을 링크라고 하는 것입니다. 링크라고 부르는 것은 하나의 파일에서 다른 파일의 코드를 사용해야하는데 이를 가능하도록 연결해주기 때문이라고 기억하면 됩니다. 컴파일링크를 합쳐서 실행가능한 파일을 만드는 과정을빌드라고 부릅니다.

  • native code란? : OS에 의해서 직접적으로 컴파일 되는 코드를 의미합니다. 예를 들어서 C/C++ 입니다.
  • managed code란? : 구동을 시키기 위해서 interpreter라고 불리는 다른 프로그램이 반드시 요구되는 코드를 의미합니다. 예를 들어서 C#, java입니다.
  • 네이비브와 메니지드를 굳이 명확해서 구분해서 사용하는 것 같지는 않습니다.

make란?

유닉스 계열의 운영체제에서 주로 사용되는 프로그램 build 도구입니다. 여러 파일들끼리의 의존성과 각 파일에 필요한 명령을 정의함으로써 프로그램을 compile 할 수 있으며 프로그램을 만들 수 있는 과정을 서술할 수 있는 표준적인 문법을 가지고 있습니다. 만약 build 도구 없이 IDE를 통해서 build하고 deploy할 때 발생하는 가장 큰 문제는 해당 프로그램의 운영환경이 배포자의 PC에 의해서 결정된다는 것입니다. make를 실행하기 위해서는 어떻게 make를 할 것인지 명시해 놓은 Makefile이 필요합니다. ROS에서 사용하는 makeandroid에서 사용하는 gradlebuild tool의 한 종류입니다.

Makefile이란?

make을 실행하기 전에 프로젝트의 목록 및 compilelink 규칙을 만들어야 합니다. 이러한 규칙들을 명시해놓은 파일을 만들고 이를 Makefile이라고 부릅니다. 예를 들면 아래와 같은 형태입니다.

helloworld:
	gcc main.c -o helloworld

install: helloworld
	install -m 0755 helloworld /usr/local/bin

Configure란?

configure 스크립트는 개발 중인 프로그램을 각기 다른 수많은 컴퓨터들에서 실행할 수 있도록 도와주도록 설계된 실행 스크립트입니다. 소스 코드로부터 컴파일하기 직전에 사용자 컴퓨터의 라이브러리의 존재 여부를 확인하고 연결시켜줍니다. 즉, 각 개발자들의 개발 환경이 달라서 빌드를 잘 해야하는데 자세하게는 configure 스크립트를 잘 작성해야한다는 뜻이 됩니다. configure를 사용하는 방법을 구체적으로 알 필요는 당장 없어보입니다. Cmake가 이를 알아서 해주기 때문입니다.

Cmake란?

Cmake도 빌드 도구중 하나입니다. 정확히는 build system에서 필요로하는 파일을 생성하는데 그 목적이 있습니다. 프로그램을 빌드하는데 있는 것이 아닙니다. Cmake를 사용해 프로젝트를 관리하고자 한다면, 필요/의도에 맞게 CmakeLists.txt파일을 배치해야 합니다. 일반적으로는 Root Cmake파일을 두는 것이 편리합니다. 많은 경우 소스코드들은 폴더를 사용해 조직화되어 있으므로, 모든 프로젝트들이 재귀적으로 폴더 폴더 내에 CmakeLists.txt를 두고 있으면 폴더 == 프로젝트로 생각할 수 있게 됩니다. 이것이 무엇을 의미하는지는 아래에서 설명합니다. 먼저 폴더의 상태를 직관적으로 확인하기 위해서 tree 명령어를 사용해야합니다.

```bash
$ sudo apt install tree
```

원하는 폴더를 확인하면 그래프를 그려줍니다.  

```bash
$ tree ./gst-docs
```
결과는 아래와 같은 모습이 됩니다.  
```bash
├── scripts
│   ├── RELEASE_README.md
│   ├── generate_sitemap.py
│   └── release.py
├── sitemap.txt
└── theme
    └── extra
        ├── css
        │   └── extra_frontend.css
        ├── images
        │   ├── api-reference.svg
```

재귀적으로 CmakeLists.txt를 위치시키는 모습은 아래와 같을 것입니다.

$ tree ./small-project/
./small-project         # Project root folder
├── CMakeLists.txt      # <--- Root CMake
├── include             # header files
│   └── ... 
├── module1             # sub-project
│   ├── CMakeLists.txt
│   └── ... 
├── module2             # sub-project
│   ├── CMakeLists.txt
│   └── ... 
└── test                # sub-project
    ├── CMakeLists.txt
    └── ... 

이처럼 CmakeLists.txt를 재귀적으로 위치시키는 이유는 프로젝트가 커질 때 쉽게 관리하기 위해서입니다. 몇 천 줄, 몇 만 줄되는 프로젝트의 모든 libraries 정보를 확인해서 하나의 CmakeLists.txt로 만드는 것은 어렵기도 하고 나중에 수정하기도 불편할 것입니다.

Cmake가 알아서 Configure를 해준다고 했습니다. CmakeGUI 버전으로 설치했을 때를 기준으로 설명해보겠습니다. 한 번이라도 Cmake 프로그램을 본 적이 있는 사람이라면 다음의 설명이 이해가 될 것입니다. CMake를 사용해서 CmakeLists.txt를 생성하기 위해서는 source files가 들어있는 디렉토리와 build디렉토리를 지정해야합니다. 그리고 두 개의 버튼을 클릭합니다. 첫 번째는 Configure, 두 번째는 generate입니다. configure 버튼을 누르면 소스 코드로부터 컴파일하기 직전에 사용자 컴퓨터의 라이브러리의 존재 여부를 확인하고 연결시켜줍니다.

CmakeLists.txt란?

CmakeLists.txt가 필요한지는 이해하셨을테니 어떻게 작성하는지만 보면 됩니다. 아래의 코드는 제가 ROS 프로젝트를 했을 때 사용했던 코드 중 일부를 가져왔습니다.

## 인클루드 디렉터리를 설정한다.
include_directories(${catkin_INCLUDE_DIRS})

## path_publisher 노드에 대한 빌드 옵션이다.
## 실행 파일, 타깃 링크 라이브러리, 추가 의존성 등을 설정한다.
add_executable(path_publisher src/path_publisher.cpp)
add_dependencies(path_publisher ${${PROJECT_NAME}_EXPORTED_TARGETS}
${catkin_EXPORTED_TARGETS})
target_link_libraries(path_publisher ${catkin_LIBRARIES})

## path_subscriber 노드에 대한 빌드 옵션이다.
add_executable(path_subscriber src/path_subscriber.cpp)
add_dependencies(path_subscriber ${${PROJECT_NAME}_EXPORTED_TARGETS}
${catkin_EXPORTED_TARGETS})
target_link_libraries(path_subscriber ${catkin_LIBRARIES})

add_executable을 통해서 어떤 native file들을 object file로 만들 것인지 정해줍니다. 그리고 add_dependencies를 통해서 어떤 의존성을 추가할 것인지 결정합니다.

dependency란?

여기에 굉장히 잘 설명되어 있습니다. 요약하자면,

  1. PACKAGE A가 PACKAGE B를 recommends한다. = A를 사용하는 많은 사람들이 B없이는 A를 사용하지 않을 것이다.
  2. PACKAGE A가 PACKAGE B를 suggests한다. = B는 A의 기능과 관련된 파일들을 가지고 있으므로 B를 설치하는 것이 좋을 것이다.

dependencyinclude 이후에는 안쓰입니다. text segment에 들어가는 데이터의 크기는 커져도 의존성에 구애받지 않게됩니다. 즉, 빌드할 때 필요한 static library를 말합니다.

run-time dependency란?

위에서 설명한 dependencybuild dependecy에 대한 설명이었습니다. run-time dependencyDLL(Dynamic Linking Livrary)의 super-set입니다. DLLrun-time dependency 집합의 원소라고 생각할 수 있으니, DLL을 설명하면 run-time dependency가 뭔지 조금 감을 잡으실 수 있으실 것 같습니다.

DLLbuild dependecy처럼 “$include$한 곳에 해당하는 코드를 복-붙해서 옮겨 적으세요.”라는 의미가 아닙니다. “나중에 사용할 때 그 때 해당하는 코드를 가져와서 읽을게요.”라는 의미입니다. 멘토님이 구멍을 뚫어놓는다고 하신 부분이 이를 말씀하신 것 같습니다.

Cmake로 빌드하는 방법

예를 들어서 프로젝트 디렉토리가 다음과 같다고 해보겠습니다.

MyCMakeProject
├── CMakeLists.txt
├── include
│   └── CMakeLists.txt
└── src
    └── CMakeLists.txt

빌드 하는 방법은 MyCMakeProject 폴더, 즉 소스 코드 폴더에서 터미널에 다음과 같이 치면됩니다.

$ cd {source code directory}
$ mkdir build   //디렉토리 생성
$ cd build      //디렉토리로 이동
$ cmake ..      //빌드에 필요한 각종 코드들이 생성
$ make          //빌드

What are differeces between makefile and CMakeLists.txt?

makefile를 사용해서 make할 수 있습니다. 또 CMakeLists.txt를 사용해서 빌드를 할 수 있습니다. 뭐가 다를까요?

  • make는 빌드 시스템입니다. 이것은 우리의 소스코드를 빌드하기 위해서 컴파일러과 다른 build tools를 drive합니다.
  • Cmake는 a generator of build system입니다. 같은 CMakeLists.txt를 사용해서 makefiles를 만들 수도 있고 Ninja build files를 만들 수도 있습니다. 혹은 visual studio solution을 만들 수도 있습니다. 이와 같은 이유로 위에서 $cmake ..$를 사용해서 빌드에 필요한 각종 코드(makefile)을 만들고 $make$를 했던 것입니다.

정리

제가 이전에 작성했던 포스팅에서 gcc의 정의를 설명했었습니다.
- gcc : The GNU Compiler Collection (GCC) is a collection of compilers and libraries for C, C++, Java, … etc. Many open source projects including the GNU tools and the Linux kernel are compiled with GCC. 이제는 멘토님께서 makegcc의 확장이라고 설명해주신 부분이 조금 이해가 되네요.

처음 들어본 terminology

pbuild란? chroot란?

pbuild는 우분투 패키지 빌딩을 위한 chroot 환경을 제공합니다. chroot란 change-root의 약자로 현재 실행 중인 프로세스와 child 프로세스 그룹에서 Root directory를 변경하는 작업을 말합니다. 이렇게 수정된 환경에서 실행되는 프로그램은 지정된 디렉터리 트리 밖의 파일들의 이름을 지정할 수 없으므로 chroot 감옥이라고 부르기도 합니다. clean chroot 환경은 어떤 dependency가 정말로 필요하고 missing되었는지 확인해주는 역할을 합니다.

debuild란?

debuildDebian package를 빌드하는 것을 의미합니다. 이는 debian package를 upload하기 위한 모든 파일을 생성합니다. debian은 데비안 프로젝트가 개발하는 O/S로, 우분투가 데비안에서 나온 운영체제입니다. 데비안과 우분투는 둘 다 APT를 이용하여 패키지를 관리하며, 인터넷에서 .deb 파일을 다운로드하여 직접 설치할 수 있습니다. 다만, 우분투에서는 우분투용으로 빌드하거나 데비안용으로 빌드한 .deb 파일을 둘 다 사용할 수 있습니다. 그러나 데비안에서는 우분투용으로 빌드한 .deb 파일은 사용할 수 없는 경우가 많습니다.

debuild가 어떻게 동작하는지는 여기를 보면 이해할 수 있습니다.

pdebuild란?

pdebuilddebuild을 진행하는 pbuilder의 방법을 말합니다. 이는 pbuilder package와 함께 쓰입니다.

meson이란?

meson에 대해서는 여기에서 따로 포스팅하였습니다.

아카이빙Archiving이란?

아카이브(archive)는 역사적 가치 혹은 장기 보존의 가치를 가진 기록이나 문서들의 컬렉션을 의미하며, 동시에 이러한 기록이나 문서들을 보관하는 장소, 시설, 기관 등을 의미합니다.

export란?

멘토님께서 아래 명령어를 치라고 하셨는데 이것이 무엇인지 왜 하는지 알아보겠습니다. 저는 이 명령어를 ~/.bashrc의 가장 아래에 적어두면 terminal을 열 때마다 빌드를 하기 위한 환경이 잘 잡힌다고만 알고 있습니다.

$ export GST_PLUGIN_PATH=$(pwd)/gst/nnstreamer
$ export NNSTREAMER_CONF=$(pwd)/nnstreamer-test.ini
$ export NNSTREAMER_FILTERS=$(pwd)/ext/nnstreamer/tensor_filter
$ export NNSTREAMER_DECODERS=$(pwd)/ext/nnstreamer/tensor_decoder

export는 bash shell의 BUILTIN commmands입니다. 일반적으로 export command는 새로 fork된 child 프로세스들이 어디에서 환경 변수를 찾을 수 있는지 mark하는 기능을 합니다. 즉, 이 명령을 사용하면 child process에서도 환경 변수를 사용할 수 있는 것입니다. terminal을 열 때마다 export를 해주어야 하는 것은 terminal의 chile process를 새롭게 create 했기 때문이라고 생각이 됩니다.
환경 변수는 프로그램 밖에서 값이 설정된 변수를 의미합니다. 그리고 일반적으로 운영 체제 또는 마이크로 서비스에 내장 된 기능을 통해 제공됩니다.

Agile 방법론 & 지라Jira

Agile을 한 마디로 표현하면 “협력과 피드백을 자주! 일찍! 더 잘하는 것!”입니다. 이를 위해서 Github에는 Issue 기능을 제공합니다. 이렇나 Issue들을 더 잘 관리할 수 있도록 하는 tool이 Jira입니다. 사실 github의 Issue도 jira를 밴치마킹한 것이라고 합니다.

GST_DEBUG-3??

디버깅 레벨을 지정해서 자세하게 디버그 보는 방법을 의미합니다. gstreamer pipeline을 .sh 파일로 저장하고 아래와 같이 실행하면 특정 레벨까지의 출력들을 보여줍니다.

$ GST_DEBUG=4 ./gst-image-segmentation.sh

*.a file

static libraries는 Unix-like systems에서 Windows systems보다 더 흔하게 사용됩니다. Windows에서는 static libraies를 일반적으로 .a 대신에 .lib를 사용합니다.
*.a파일은 C/C++ source file에 의해서 참조될 수 있는 a library of functions 와 headers를 포함합니다. 몇몇 함수를 포함할 수도 있고 3D engine과 같은 대규모 라이브러리를 포함할 수도 있습니다.

NOTE: Using static libraries may result in larger program files, but enables faster loading times compared to dynamic libraries.

참조

Plugin V.S. Library

이 둘의 주된 차이점은 다음과 같습니다.

  • plugin : 어플리케이션의 사용성을 증가시키는 확장을 말합니다.
  • library : class와 function의 집합으로서 S/W 개발을 돕는 역할을 합니다.

따라서 NNstreamer는 plug-in인 동시에 library입니다.

Signed off

git commit –amend -s : Signed-oFF 내가 나임을 증명하고 의정서? 보험가입할 때 무슨 일이 생겨도 나는 책임지지 않겠다. 라고 말하는 것.

commit이 겹치면 –force 옵션을 붙혀서 git push origin my_porked_repo

gst indent

nnstreamer/tools/deveoper의 gst-indent 사용해서 코드 컨벤션 체크하기

android 부분의 기여거리

자바 nnstreamer Api를 만들었는데 이게 앱으로 돌아가는지 확인하고 싶다.

terminal command

  • apt : Advanced Packaging Tool의 ab.
  • apt update : 모든 패키지의 최신 버전을 얻는다.
  • rm -rf [file_name] : remove –recursive –force와 같은 뜻. 재귀적으로 삭제를 강제(하위 디렉토리에 파일이 있어도 삭제)한다.
  • nm : object 파일들의 symbols들을 list로 보여준다.
  • pwd : print working directory의 약어. 현재 디렉토리를 출력한다.
  • man : command의 명령어를 보여준다. 이제 터미널 명령어 물어봐서 욕먹지 않도록 하자..
    설치:
      $ sudo apt install man-db
    

    사용 :

      $ man nm
    

    결과 :

      NM(1)         GNU Development Tools             NM(1)
    
      NAME
          nm - list symbols from object files
    
      SYNOPSIS
          nm [-A|-o|--print-file-name] [-a|--debug-syms]
              [-B|--format=bsd] [-C|--demangle[=style]]
              [-D|--dynamic] [-fformat|--format=format]
      //생략
    

Reference

  • native code v.s. managed code 설명