자료 출처 korone.net programming 사이트를 운영하고 계시는 조병완님의 글의 네 번째 글,
"[04] Embedded C++ GUI Programming with Qt - Qt포팅" 입니다.

    3. Qt포팅

Qt를 포팅한다고 하는것은 Embedded기기 위에서 Qt로 작성된 프로그램을 실행하기 위한 과정입니다. Embedded를 한다고 하면 가장 흔히 접할 수 있는 단어중 하나가 포팅이라는 단어라 생각합니다. 이것은 주로 Embedded기기 자체가 x86이 아닌 ARM, PPC, MIPS 등을 사용하기 때문에 CPU마다 명령어의 차이로 인해 해당 CPU에 맞는 바이너리를 생성하기 위해서 필요한 작업이며, Qt역시 마찬가지로 해당 CPU에 맞게 컴파일을 해야합니다.

많은 Embedded 관련 서적과 사이트등에서 이러한 포팅에 대한 howto가 제공되고 있습니다. 저 역시 그러한 자료를 참조해서 Embedded Linux환경에서 속도가 그다지 만족스럽지는 못하지만 cross compiler를 통해 Embedded 기기보다 훨씬빠른 x86 기기에서 build를 해서 Embedded기기에 이식을 했습니다.

많은 시행과 착오를 겪었고 모든 포팅을 완수했으나 이러한 일련의 일에 대해서 필자 개인적인 생각은 아주 짜증스럽다고 밖에 표현할 수 밖에 없습니다. 반드시 해야할 일임에는 분명하나 작업하고 나서 남는것은 하나도 없고(물론 정리되면 howto문서 몇개는 남긴하지만 별 의미가 없는것 같습니다.) 정작 본인이 해야할 일은 프로그램 개발인데 대체 뭐하는 일인가 싶기도 했습니다.

이와 비슷한 맥락으로 디바이스 드라이버 개발을 빼놓을 수 없는데, 처음 임베디드 개발에 입문하는 사람들을 보면 대부분이 커널, 드라이버작업을 선호한다는 사실입니다. 필자 역시 처음 임베디드 개발에 입문하고서 가장 하고 싶었고 특별한 기술처럼 느껴졌던것이 사실이었으나 시간이 지날수록 필자와 같은 S/W 엔지니어가 할 분야는 아니라고 판단을 했습니다.

드라이버를 작성하는데 있어서 수십에서 수백페이지에 달하는 Datasheet를 펼쳐놓고서 이 레지스트러 한번 읽어보고 저 레지스트터 한번 값을 써보고의 반복은 S/W엔지니어 입장에서 알수없는 하드웨어의 특성은 여간 답답하고 짜증나는 작업이 아닐 수 가 없었습니다. 욕심을 버리고 H/W 분야에서 일하시는 분을 신뢰하고 그 분들의 영역을 근접치 아니하는것이 맞는거라고 생각합니다.
필자와 같은 S/W 엔지니어는 드라이브로 부터 올라오는 데이타를 효과적으로 처리해서 가공하는 Bussiness Logic 작성이 더 잘 할 수 있는 일일 것입니다.

포팅에 관련되어서, 드라이버 작성에 관련되어서 S/W엔지니어는 전체적인 흐름을 파악하고 이와 유사한 상황에 대비할 수 있는 훈련을 하는것이 맞을 것으로 생각됩니다.

이러한 맥락에 비추어 Qt포팅역시 어떻게 옵션을 주고 어떻게 컴파일 하며 또 에러가 나면 어떻게 대처하는지에 대한 howto 보다는 좀 더 근원적인 방법론이 필요할 것이라고 생각합니다. 아래에서는 qt를 컴파일 할때 어떤 옵션을 주고서 컴파일 해야하는지에 대한 내용보다는 인터넷 사이트등에서 쉽게 구할 수 있는 howto문서들의 과정이 왜 필요한것인지에 대한 내용위주로 설명할 생각입니다.

    3.1 시작하기

Qt는 여타 프로그램을 포팅하는 것과 마찬가지로 cross compiler에 맞게 환경설정을 한후 bulid를 해서 embedded기기에 이식을 하는 과정을 거칩니다. 일반적인 리눅스 프로그램 컴파일과 크게 다르지 않으며 단지 cross compiler를 이용한다는 차이점 하나만 존재합니다.

보통의 컴파일 방법은 아래와 같습니다.

./configure --> make --> make install

./configure는 시스템내의 gcc, g++등의 위치와 컴파일 하려면 프로그램에서 요구하는 각종 헤더파일과 라이브러리를 자동으로 찾아 Makefile을 생성합니다. 가령 프로그램에서 curses라이브러리를 참조한다면 curses 라이브러리 헤더파일과 라이브러리 파일을 찾고 없다면 에러를 발생시킵니다.

cross compile도 마찬가지입니다. 차이점은 요구하는 라이브러리가 반드시 cross compile되어 있는 라이브러리여야 한다는 사실입니다. (일치하지 않는경우 컴파일 최종 과정에서 링크할때 binary type이 일치하지 않는다는 에러 메세지가 표시됩니다.) 따라서 해당 프로그램이 무엇을 요구하는지 부터 정확하게 인지하고 있어야 합니다.

Qt의 경우 libjpeg, libz, libpng등등(이외에 더 있습니다만 생략합니다...)을 요구합니다. libjpeg, libpng는 jpeg, png이미지 포맷에 대한 압축을 해제하기 위한 라이브러리 이며, libz는 libjpeg, libpng등에서 사용하고 있는 압축 알고리즘에 대한 라이브러리 입니다. 필자가 운영하는 질문게시판에는 jpeg을 지원하기 위한 질문을 볼 수 있는데, 이 역시 이러한 부분을 이해하고 있다면 쉽게 해결 할 수 있습니다. 보통 프로그램이 어떠한 라이브러리를 참조하는지는 REAME파일등에 기술되어 있거나, ./configure등을 수행하면 출력되는 log에 표시가 되기도 하므로 이를 참조하시면 됩니다.

cross compile 하기위해서는 이와 더불어 컴파일러 프로그램을 cross 컴파일러로 지정해 주어야 합니다. 보통 --host --cross 옵션을 붙이거나 CC, GCC, CXX등의 환경변수를 직접 바꾸어 줍니다. 아마도 이러한 원칙만 아신다면 어떠한 프로그램도 쉽게 포팅하실 수 있을 것이라고 생각합니다. 그런데, qt소스코드를 받아서 빌드해보신분들은 아시겠지만 qt는 위와같은 방법과 약간 다르게 빌드를 하도록 되어 있습니다.

./configure & qmake --> make --> make install

./configure와 qmake라는것을 같이 이용하도록 되어 있는데, qmake는 trolltech에서 제공하는 Makefile생성 유틸리티입니다.
qmake는 qt자체가 다중 플랫폼 지원을 위해 개발된 것인만큼, 다중 플랫폼 지원을 위한 Makefile 생성과, Makefile의 복잡한 문법을 모르고 있어도 쉽게 Makefile을 생서해주기 위한 유틸리티입니다.

qmake의 동작방식을 살펴보면 QMAKESPEC이라는 환경변수를 참조하여 Makefile을 생성하는데, 이 환경변수가 가르키고 있는 곳은, qt 소스코드내의 mkspec 디렉토리내를 가르키도록 되어 있습니다.(만약 QMAKESPEC 환경변수가 없다면 기본적으로 x86, g++로 되어 있습니다.) mkspec디렉토리를 살펴보면 각종 컴파일러와 cpu들이 명시가 된 디렉토리가 있는데, 이 디렉토리내의 파일에 해당 컴파일러 cpu별로 컴파일러 명령과 옵션들이 정의되어 있고, 이를 추가/수정하면 쉽게 다른 기기로 이식할 수 있는 상태를 만들 수 있습니다.

보통 qt를 configure할때에 ./configure --embedded arm .... 이라고 옵션을 주는데, 여기서 arm이라는 옵션자체가 qmake로 전달되어 mkspec/qws/linux-arm-g++/qmake.conf 파일을 참조해서 Makefile을 생성합니다. 속도를 위해 컴파일러 최적화 옵션을 변경할때에 위의 해당 디렉토리를 직접 열어서 컴파일 옵션을 바꾸어 줄 수 도 있고, mips나 powerpc등으로 컴파일 할때에도 linux-ppc-g++ 이나 linux-mips-g++등을 참조하면 됩니다.

 

    3.2 Customize library

Qt/E는 Embedded를 목적에 두고 만들어진 라이브러리로서 Embedded 특성에 맞게 최대한 작은 사이즈의 라이브러리를 만들기 위한 build시스템을 갖추고 있습니다. 가령, Qt내의 network 기능을 사용하지 않고 라이브러리내에서 제거하려면 ./configure할때나 qconfig.h 파일등을 편집에서 소스코드레벨에서 제외시킬 수가 있습니다.

먼저 ./configure옵션으로 제외시킬 수 있는것들은 ./configure --help 명령을 통해 확인한후에 ./configure -no-zlib ... 와 같이 기술하여 zlib를 빼내버릴 수 있습니다. 이와 마찬가지로 ./configure --help를 통해 자신이 기기에서 필요없다고 판단되는 부분들은 제거하시면 속도나 메모리 사용측면에서의 이점을 살릴 수가 있습니다.

이외 또 하나의 방법으로는 ./configure -qconfig minimal 옵션을 통해 qt소스코드내의 qconfig-minimal.h 파일을 참조해서 필요없는 라이브러리를 제거시킬 수가 있습니다. qconfig 방법은 앞서 설명한 방법보다 좀더 세밀하게 여러가지 기능을 뺄 수가 있습니다.

qconfig에 관련된 파일은 qconfig-small.h, qconfig-medium.h, qconfig-large.h ... 등등의 파일이 있는데 이파일들은 직접 수정을 가해도 되고 새롭게 작성해서 저장한다음(qconfig-hello.h로 저장했다고 예를 든경우) ./configure -qconfig hello 라는 형식으로 qconfig-hello.h 파일을 지정할 수 도 있습니다.

qconfig-xxx.h 파일을 열어보면 각종 #define 등이 정의됨을 볼 수 있는데, 이곳에 정의가 된 모듈을 빼겠다는 의미로 사용됩니다.
만약 특정한 루틴을 사용하고자 한다면 주석처리하면 됩니다.

단, 한가지 문제점이 이러한 방식이 용량을 줄일수 있는 가장 좋은 방법이나 모듈간의 dependancy로 말이암아 컴파일 도중에 에러가 나거나 , 컴파일 완료후 runtime실행시 그냥 멈추는 증상이 생길 수 있습니다. 따라서 이 부분을 하기 위해서는 많은 인내심이 필요한데(필요없는 기능을 빼고 컴파일 해서 실행해보고의 반복), 이 단순반복 방법을 약간 줄이기 위해서 필자는 qt/e기반으로 작성된 qtopia의 qconfig-qpe.h 파일을 가지고와서 qte에 넣고 컴파일 하는 방법을 이용합니다.

이미 trolltech에서 qt/e를 이용해서 qtopia라는것을 만들었으므로 충분히 검증되었다라고 할 수 있을 것 이기 때문입니다. 하지만 qtopia는 현재 qte 2.x혹은 qte 4.x밖에 없으므로 qte 3.x에서는 사용할 수 없음을 유의해야 합니다.

 

    3.3 Customize device

qt는 x-windows없이 독립적으로 운영되는 시스템으로서, framebuffer, mouse(touch screen), keyboard등에 대한 드라이버코드가 있습니다.

여기서 말하는 드라이버란 kernel에서 운영되는 일반적인 드라이버가 아닌 qt상의 드라이버를 말합니다. framebuffer의 경우 qt내에서 그려지는 작업들이 모두 framebuffer를 통해서 그려지는데, 통상 해당 embedded 기기에서 linux framebuffer용 driver 를 제공 한다면 특별한 작업없이 수행이 가능하나, framebuffer의 특성상 graphic chipset이 가진 많은 성능좋은 기능들이 제대로 사용되지 못하게 됩니다.

이에 대해서 추가적으로 기본 framebuffer에 없는 가속 연산에 대해서 직접 해당 드라이버에 접근해서 연결해 주면 좋은 성능을 기대할 수 있습니다. 이에 대해서는 기존에 qt내에 구현된 다른 그래픽 카드 드라이버를 참조하면 됩니다.

mouse의 경우 일반적인 USB마우스나 touch screen등을 지원하도록 처리되어 있는데, USB마우스나 PS2마우스등은 특별히 고려안해도 모두 잘 동작하며 다만 qt프로그램을 실행할때 QWS_MOUSEPROTO 환경변수를 참조해서 이를 처리하도록 되어 있습니다. 터치스크린과 관련된 자세한 사항은 필자의 홈페이지에 이미 정리된 강좌가 있어서 이를 통해 대신합니다.

http://www.korone.net/bbs/board.php?bo_table=qt_lecture&wr_id=294&sca=&sfl=wr_content&stx=touch&sop=and

keyboard 는 흔히 일반 PC에서 사용하는 키보드와 일반 PDA나 특정 장치에서 사용되는 특수한 버튼들을 모두 처리할 수 있도록 되어 있습니다. 일반 PC에서 사용하는 키보드들은 앞전에 설명한 mouse의 경우와 비슷하게 QWS_KEYBOARD 환경변수를 참조하도록 되어 있으며 특수한 키 역시 touch screen과 마찬가지의 경우로 처리되게 되어 있어서 쉽게 처리할 수 있습니다.

가령 제품에 remocon이 있는 경우 serial등을 통해서 수신된 remocon 키 값을 parsing 해서 qt key로 변환해 주면 실제 qt application내에서는 remocon과 관계없이 일반적은 프로그래밍처럼 동작시킬 수 있습니다.

 

조병완
korone @ gmail . com
http://www.korone.net

 

태그: *qt *그래픽 *조병완 *korone